cid#1607171 Data race condition
[LibreOffice.git] / formula / source / ui / dlg / funcpage.cxx
blob60d1e5adf2a015424814d96d9a584ed7ee2e58e4
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
30 namespace formula
32 IMPL_LINK(FuncPage, KeyInputHdl, const KeyEvent&, rKEvt, bool)
34 if (rKEvt.GetCharCode() == ' ')
36 aDoubleClickLink.Call(*this);
37 return true;
39 return false;
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)
91 if (!bFill)
92 return nullptr;
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))
127 return;
128 if (!bSimilaritySearch && rFuncName.indexOf(rSearchString) < 0
129 && rSearchString.indexOf(rFuncName) < 0)
130 return;
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();
140 mCategories.clear();
141 sFuncScores.clear();
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));
154 if (nSelPos > 0)
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);
164 else
166 impl_addFunctions(pCategory, false);
169 else // LRU-List
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());
184 else
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);
197 sal_uInt32 nCatEnd;
198 if (nCatBeg < 0)
200 nCatBeg = 0;
201 nCatEnd = nCategoryCount;
203 else
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);
246 if (IsVisible())
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();
263 if (pDesc)
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);
282 else
283 m_xLbFunction->expand_row(categoryRow);
284 return true;
286 m_xLbFunctionSearchString->set_text(OUString());
287 aDoubleClickLink.Call(*this);
288 return true;
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)
310 if (const auto pDesc
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)
339 if (nFunc == -1)
340 m_xLbFunction->unselect_all();
341 else
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)
360 return nullptr;
361 // not pretty, but hopefully rare
362 return weld::fromId<const IFunctionDescription*>(m_xLbFunction->get_selected_id());
365 } // formula
367 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */