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 <comphelper/string.hxx>
21 #include <editeng/editview.hxx>
22 #include <sfx2/viewsh.hxx>
23 #include <formula/funcvarargs.h>
24 #include <unotools/charclass.hxx>
25 #include <unotools/textsearch.hxx>
26 #include <vcl/svapp.hxx>
27 #include <vcl/help.hxx>
31 #include <inputhdl.hxx>
32 #include <tabvwsh.hxx>
33 #include <funcdesc.hxx>
34 #include <compiler.hxx>
36 #include <dwfunctr.hxx>
38 /*************************************************************************
39 #* Member: ScFunctionWin
40 #*------------------------------------------------------------------------
42 #* Class: ScFunctionWin
44 #* Function: Constructor of ScFunctionWin Class
46 #* Input: Sfx - links, window, resource
50 #************************************************************************/
52 ScFunctionWin::ScFunctionWin(weld::Widget
* pParent
)
53 : PanelLayout(pParent
, u
"FunctionPanel"_ustr
, u
"modules/scalc/ui/functionpanel.ui"_ustr
)
54 , xCatBox(m_xBuilder
->weld_combo_box(u
"category"_ustr
))
55 , xFuncList(m_xBuilder
->weld_tree_view(u
"funclist"_ustr
))
56 , xScratchIter(xFuncList
->make_iterator())
57 , xInsertButton(m_xBuilder
->weld_button(u
"insert"_ustr
))
58 , xHelpButton(m_xBuilder
->weld_button(u
"help"_ustr
))
59 , xSimilaritySearch(m_xBuilder
->weld_check_button(u
"similaritysearch"_ustr
))
60 , xFiFuncDesc(m_xBuilder
->weld_text_view(u
"funcdesc"_ustr
))
61 , m_xSearchString(m_xBuilder
->weld_entry(u
"search"_ustr
))
62 , xConfigListener(new comphelper::ConfigurationListener(u
"/org.openoffice.Office.Calc/Formula/Syntax"_ustr
))
63 , xConfigChange(std::make_unique
<EnglishFunctionNameChange
>(xConfigListener
, this))
69 m_aListHelpId
= xFuncList
->get_help_id();
70 m_aSearchHelpId
= m_xSearchString
->get_help_id();
72 // Description box has a height of 8 lines of text
73 xFiFuncDesc
->set_size_request(-1, 8 * xFiFuncDesc
->get_text_height());
75 m_xSearchString
->connect_changed(LINK(this, ScFunctionWin
, ModifyHdl
));
76 m_xSearchString
->connect_key_press(LINK(this, ScFunctionWin
, KeyInputHdl
));
78 xCatBox
->connect_changed(LINK( this, ScFunctionWin
, SelComboHdl
));
79 xFuncList
->connect_selection_changed(LINK(this, ScFunctionWin
, SelTreeHdl
));
81 xFuncList
->connect_row_activated(LINK( this, ScFunctionWin
, SetRowActivatedHdl
));
82 xInsertButton
->connect_clicked(LINK( this, ScFunctionWin
, SetSelectionClickHdl
));
83 xHelpButton
->connect_clicked(LINK( this, ScFunctionWin
, SetHelpClickHdl
));
84 xSimilaritySearch
->connect_toggled(LINK(this, ScFunctionWin
, SetSimilarityToggleHdl
));
86 xCatBox
->set_active(0);
88 SelComboHdl(*xCatBox
);
91 /*************************************************************************
92 #* Member: ScFunctionWin
93 #*------------------------------------------------------------------------
95 #* Class: ScFunctionWin
97 #* Function: Destructor of ScFunctionWin Class
103 #************************************************************************/
105 ScFunctionWin::~ScFunctionWin()
107 xConfigChange
.reset();
108 xConfigListener
->dispose();
109 xConfigListener
.clear();
113 xInsertButton
.reset();
115 xSimilaritySearch
.reset();
119 /*************************************************************************
120 #* Member: InitLRUList
121 #*------------------------------------------------------------------------
123 #* Class: ScFunctionWin
125 #* Function: Updates the list of functions depending on the set category
131 #************************************************************************/
133 void ScFunctionWin::InitLRUList()
135 ScFunctionMgr
* pFuncMgr
= ScGlobal::GetStarCalcFunctionMgr();
136 pFuncMgr
->fillLastRecentlyUsedFunctions(aLRUList
);
138 sal_Int32 nSelPos
= xCatBox
->get_active();
141 UpdateFunctionList(u
""_ustr
);
145 /*************************************************************************
146 #* Member: FillCategoriesMap
147 #*------------------------------------------------------------------------
149 #* Class: ScFunctionWin
151 #* Function: Fills the categories map.
157 #************************************************************************/
159 weld::TreeIter
* ScFunctionWin::FillCategoriesMap(const OUString
& aCategory
, bool bFill
)
164 if (mCategories
.find(aCategory
) == mCategories
.end())
166 mCategories
[aCategory
] = xFuncList
->make_iterator();
167 xFuncList
->insert(nullptr, -1, &aCategory
, nullptr, nullptr, nullptr, false,
168 mCategories
[aCategory
].get());
170 return mCategories
[aCategory
].get();
173 /*************************************************************************
174 #* Member: UpdateLRUList
175 #*------------------------------------------------------------------------
177 #* Class: ScFunctionWin
179 #* Function: Updates the list of last used functions.
185 #************************************************************************/
187 void ScFunctionWin::UpdateLRUList()
189 if (pFuncDesc
&& pFuncDesc
->nFIndex
!=0)
191 ScModule::get()->InsertEntryToLRUList(pFuncDesc
->nFIndex
);
195 void ScFunctionWin::SearchFunction(const OUString
& rFuncName
, const OUString
& rSearchString
,
196 const ScFuncDesc
* pDesc
, const bool bSimilaritySearch
)
198 std::pair
<sal_Int32
, sal_Int32
> score
= std::make_pair(0, 0);
199 if (bSimilaritySearch
&& !utl::TextSearch::SimilaritySearch(rFuncName
, rSearchString
, score
))
201 if (!bSimilaritySearch
&& rFuncName
.indexOf(rSearchString
) < 0
202 && rSearchString
.indexOf(rFuncName
) < 0)
205 sFuncScores
.insert(std::make_pair(score
, std::make_pair(rFuncName
, pDesc
)));
208 /*************************************************************************
209 #* Member: SetDescription
210 #*------------------------------------------------------------------------
212 #* Class: ScFunctionWin
220 #************************************************************************/
222 void ScFunctionWin::SetDescription()
224 xFiFuncDesc
->set_text(OUString());
225 const ScFuncDesc
* pDesc
=
226 weld::fromId
<const ScFuncDesc
*>(xFuncList
->get_selected_id());
229 pDesc
->initArgumentInfo(); // full argument info is needed
231 OUString aBuf
= xFuncList
->get_selected_text() +
233 pDesc
->GetParamList() +
237 xFiFuncDesc
->set_text(aBuf
);
239 // Update help ID for the selected entry
240 const OUString sHelpId
= pDesc
->getHelpId();
241 if (!sHelpId
.isEmpty())
242 xFuncList
->set_help_id(pDesc
->getHelpId());
244 xFuncList
->set_help_id(m_aListHelpId
);
248 /*************************************************************************
249 #* Member: UpdateFunctionList
250 #*------------------------------------------------------------------------
252 #* Class: ScFunctionWin
254 #* Function: Updates the list of functions depending on the set category
256 #* Input: Search string used to filter the list of functions
260 #************************************************************************/
262 void ScFunctionWin::UpdateFunctionList(const OUString
& rSearchString
)
264 sal_Int32 nSelPos
= xCatBox
->get_active();
265 sal_Int32 nCategory
= ( -1 != nSelPos
)
273 bool bCollapse
= nCategory
== 0;
274 bool bFilter
= !rSearchString
.isEmpty();
277 ScFunctionMgr
* pFuncMgr
= ScGlobal::GetStarCalcFunctionMgr();
279 // Use the corresponding CharClass for uppercase() depending on whether
280 // English function names are used, or localized names.
281 const CharClass
* pCharClass
= (ScGlobal::GetStarCalcFunctionList()->IsEnglishFunctionNames()
282 ? ScCompiler::GetCharClassEnglish()
283 : ScCompiler::GetCharClassLocalized());
285 const OUString
aSearchStr(pCharClass
->uppercase(rSearchString
));
287 const ScFuncDesc
* pDesc
= pFuncMgr
->First(nCategory
);
290 const OUString
aCategory(pDesc
->getCategory()->getName());
291 const OUString
aFunction(pCharClass
->uppercase(pDesc
->getFunctionName()));
292 const OUString
aFuncDescId(weld::toId(pDesc
));
295 SearchFunction(aFunction
, aSearchStr
, pDesc
, xSimilaritySearch
->get_active());
298 weld::TreeIter
* pCategory
= FillCategoriesMap(aCategory
, bCollapse
);
299 xFuncList
->insert(pCategory
, -1, &aFunction
, &aFuncDescId
, nullptr, nullptr,
300 false, xScratchIter
.get());
302 pDesc
= pFuncMgr
->Next();
305 for (const auto& func
: sFuncScores
)
307 pDesc
= func
.second
.second
;
308 const OUString
aCategory(pDesc
->getCategory()->getName());
309 const OUString
aFunction(func
.second
.first
);
310 const OUString
aFuncDescId(weld::toId(pDesc
));
311 weld::TreeIter
* pCategory
= FillCategoriesMap(aCategory
, bCollapse
);
313 xFuncList
->insert(pCategory
, -1, &aFunction
, &aFuncDescId
, nullptr, nullptr, false,
319 for (const formula::IFunctionDescription
* pDesc
: aLRUList
)
323 OUString aFunction
= pDesc
->getFunctionName();
324 OUString aFuncDescId
= weld::toId(pDesc
);
326 xFuncList
->insert(nullptr, -1, &aFunction
, &aFuncDescId
, nullptr, nullptr,
327 false, xScratchIter
.get());
334 if (bCollapse
&& bFilter
)
336 for (const auto& category
: mCategories
)
337 xFuncList
->expand_row(*category
.second
);
340 if (xFuncList
->n_children() > 0)
342 xFuncList
->set_sensitive(true);
343 xFuncList
->select(0);
347 xFuncList
->set_sensitive(false);
351 /*************************************************************************
353 #*------------------------------------------------------------------------
355 #* Class: ScFunctionWin
357 #* Function: Save input into document. Is called after clicking the
358 #* Apply button or a double-click on the function list.
360 #* Input: Boolean to know if I double-clicked/press-enter or not
364 #************************************************************************/
366 void ScFunctionWin::DoEnter(bool bDoubleOrEnter
)
368 OUString aString
=xFuncList
->get_selected_text();
369 const bool isCategory
= mCategories
.find(aString
) != mCategories
.end();
370 if (isCategory
&& !bDoubleOrEnter
)
375 const auto& categoryRow
= *(mCategories
[aString
]);
376 if (xFuncList
->get_row_expanded(categoryRow
))
377 xFuncList
->collapse_row(categoryRow
);
379 xFuncList
->expand_row(categoryRow
);
383 OUStringBuffer aArgStr
;
384 SfxViewShell
* pCurSh
= SfxViewShell::Current();
387 if(!aString
.isEmpty())
389 OUString aFirstArgStr
;
390 ScModule
* pScMod
= ScModule::get();
391 ScTabViewShell
* pViewSh
= dynamic_cast<ScTabViewShell
*>( pCurSh
);
392 ScInputHandler
* pHdl
= pScMod
->GetInputHdl( pViewSh
);
393 if(!pScMod
->IsEditMode())
395 rtl::Reference
<comphelper::ConfigurationListener
> xDetectDisposed(xConfigListener
);
396 pScMod
->SetInputMode(SC_INPUT_TABLE
);
397 // the above call can result in us being disposed
398 if (xDetectDisposed
->isDisposed())
400 aString
= "=" + xFuncList
->get_selected_text();
404 const ScFuncDesc
* pDesc
=
405 weld::fromId
<const ScFuncDesc
*>(xFuncList
->get_selected_id());
410 nArgs
= pDesc
->nArgCount
;
413 // NOTE: Theoretically the first parameter could have the
414 // suppress flag as well, but practically it doesn't.
415 aFirstArgStr
= pDesc
->maDefArgNames
[0];
416 aFirstArgStr
= comphelper::string::strip(aFirstArgStr
, ' ');
417 aFirstArgStr
= aFirstArgStr
.replaceAll(" ", "_");
418 aArgStr
= aFirstArgStr
;
419 if ( nArgs
!= VAR_ARGS
&& nArgs
!= PAIRED_VAR_ARGS
)
420 { // no VarArgs or Fix plus VarArgs, but not VarArgs only
422 if (nArgs
>= PAIRED_VAR_ARGS
)
423 nFix
= nArgs
- PAIRED_VAR_ARGS
+ 2;
424 else if (nArgs
>= VAR_ARGS
)
425 nFix
= nArgs
- VAR_ARGS
+ 1;
428 for ( sal_uInt16 nArg
= 1;
429 nArg
< nFix
&& !pDesc
->pDefArgFlags
[nArg
].bOptional
; nArg
++ )
431 aArgStr
.append("; ");
432 OUString sTmp
= pDesc
->maDefArgNames
[nArg
];
433 sTmp
= comphelper::string::strip(sTmp
, ' ');
434 sTmp
= sTmp
.replaceAll(" ", "_");
435 aArgStr
.append(sTmp
);
442 if (pHdl
->GetEditString().isEmpty())
444 aString
= "=" + xFuncList
->get_selected_text();
446 EditView
*pEdView
=pHdl
->GetActiveView();
447 if(pEdView
!=nullptr) // @ needed because of crash during setting a name
451 pHdl
->InsertFunction(aString
);
452 pEdView
->InsertText(aArgStr
.makeStringAndClear(),true);
453 ESelection aESel
=pEdView
->GetSelection();
454 aESel
.end
.nIndex
= aESel
.start
.nIndex
+ aFirstArgStr
.getLength();
455 pEdView
->SetSelection(aESel
);
461 pEdView
->InsertText(aString
);
470 vcl::Window
* pShellWnd
= pCurSh
->GetWindow();
473 pShellWnd
->GrabFocus();
478 /*************************************************************************
480 #*------------------------------------------------------------------------
482 #* Class: ScFunctionWin
484 #* Function: Handles changes in the search text
486 #************************************************************************/
488 IMPL_LINK_NOARG(ScFunctionWin
, ModifyHdl
, weld::Entry
&, void)
490 if (xCatBox
->get_active() == 0)
492 xCatBox
->set_active(1);
493 xHelpButton
->set_sensitive(false);
495 OUString searchStr
= m_xSearchString
->get_text();
496 UpdateFunctionList(searchStr
);
500 /*************************************************************************
501 #* Handle: KeyInputHdl
502 #*------------------------------------------------------------------------
504 #* Class: ScFunctionWin
506 #* Function: Processes key inputs when the search entry has focus
508 #************************************************************************/
510 IMPL_LINK(ScFunctionWin
, KeyInputHdl
, const KeyEvent
&, rEvent
, bool)
512 bool bHandled
= false;
514 switch (rEvent
.GetKeyCode().GetCode())
524 int nNewIndex
= std::min(xFuncList
->get_selected_index() + 1, xFuncList
->n_children() - 1);
525 xFuncList
->select(nNewIndex
);
532 int nNewIndex
= std::max(xFuncList
->get_selected_index() - 1, 0);
533 xFuncList
->select(nNewIndex
);
540 // Escape in an empty search field should move focus to the document,
541 // adhering to Sidebar guidelines
542 if (m_xSearchString
->get_text().isEmpty())
544 if (SfxViewShell
* pCurSh
= SfxViewShell::Current())
546 vcl::Window
* pShellWnd
= pCurSh
->GetWindow();
549 pShellWnd
->GrabFocusToDocument();
554 m_xSearchString
->set_text(u
""_ustr
);
555 UpdateFunctionList(u
""_ustr
);
561 const ScFuncDesc
* pDesc
= weld::fromId
<const ScFuncDesc
*>(xFuncList
->get_selected_id());
564 sHelpId
= pDesc
->getHelpId();
566 if (!sHelpId
.isEmpty())
567 m_xSearchString
->set_help_id(sHelpId
);
569 m_xSearchString
->set_help_id(m_aSearchHelpId
);
578 /*************************************************************************
579 #* Handle: SelComboHdl
580 #*------------------------------------------------------------------------
582 #* Class: ScFunctionWin
584 #* Function: A change of the category will update the list of functions.
590 #************************************************************************/
592 IMPL_LINK_NOARG(ScFunctionWin
, SelComboHdl
, weld::ComboBox
&, void)
594 if (xCatBox
->get_active() == 0)
595 m_xSearchString
->set_text(u
""_ustr
);
596 xHelpButton
->set_sensitive(xCatBox
->get_active() != 1);
597 OUString searchStr
= m_xSearchString
->get_text();
598 UpdateFunctionList(searchStr
);
602 IMPL_LINK_NOARG(ScFunctionWin
, SelTreeHdl
, weld::TreeView
&, void)
604 bool bSensitivity
= weld::fromId
<const ScFuncDesc
*>(xFuncList
->get_selected_id());
605 xHelpButton
->set_sensitive(bSensitivity
);
609 /*************************************************************************
610 #* Handle: SetSelectionClickHdl
611 #*------------------------------------------------------------------------
613 #* Class: ScFunctionWin
615 #* Function: A change of the category will update the list of functions.
621 #************************************************************************/
623 IMPL_LINK_NOARG( ScFunctionWin
, SetSelectionClickHdl
, weld::Button
&, void )
625 DoEnter(); // saves the input
628 /*************************************************************************
629 #* Handle: SetHelpClickHdl
630 #*------------------------------------------------------------------------
632 #* Class: ScFunctionWin
634 #* Function: Get selected function's official help.
640 #************************************************************************/
642 IMPL_LINK_NOARG( ScFunctionWin
, SetHelpClickHdl
, weld::Button
&, void )
644 if (const auto pDesc
= weld::fromId
<const ScFuncDesc
*>(xFuncList
->get_selected_id()))
646 if (Help
* pHelp
= Application::GetHelp())
648 const OUString sHelpId
= pDesc
->getHelpId();
649 if (!sHelpId
.isEmpty())
651 pHelp
->Start(sHelpId
);
657 IMPL_LINK_NOARG(ScFunctionWin
, SetSimilarityToggleHdl
, weld::Toggleable
&, void)
659 OUString searchStr
= m_xSearchString
->get_text();
660 UpdateFunctionList(searchStr
);
664 IMPL_LINK_NOARG( ScFunctionWin
, SetRowActivatedHdl
, weld::TreeView
&, bool )
666 DoEnter(true); // saves the input
670 void EnglishFunctionNameChange::setProperty(const css::uno::Any
&rProperty
)
672 ConfigurationListenerProperty::setProperty(rProperty
);
673 m_pFunctionWin
->InitLRUList();
674 m_pFunctionWin
->UpdateFunctionList(u
""_ustr
);
677 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */