1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #undef SC_DLLIMPLEMENTATION
21 
22 #include <global.hxx>
23 #include <tpformula.hxx>
24 #include <formulaopt.hxx>
25 #include <sc.hrc>
26 #include <strings.hrc>
27 #include <scresid.hxx>
28 #include <formula/grammar.hxx>
29 #include <officecfg/Office/Calc.hxx>
30 #include "calcoptionsdlg.hxx"
31 
32 #include <unotools/localedatawrapper.hxx>
33 
ScTpFormulaOptions(weld::Container * pPage,weld::DialogController * pController,const SfxItemSet & rCoreAttrs)34 ScTpFormulaOptions::ScTpFormulaOptions(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rCoreAttrs)
35     : SfxTabPage(pPage, pController, "modules/scalc/ui/optformula.ui", "OptFormula", &rCoreAttrs)
36     , mnDecSep(0)
37     , mxLbFormulaSyntax(m_xBuilder->weld_combo_box("formulasyntax"))
38     , mxCbEnglishFuncName(m_xBuilder->weld_check_button("englishfuncname"))
39     , mxBtnCustomCalcDefault(m_xBuilder->weld_radio_button("calcdefault"))
40     , mxBtnCustomCalcCustom(m_xBuilder->weld_radio_button("calccustom"))
41     , mxBtnCustomCalcDetails(m_xBuilder->weld_button("details"))
42     , mxEdSepFuncArg(m_xBuilder->weld_entry("function"))
43     , mxEdSepArrayCol(m_xBuilder->weld_entry("arraycolumn"))
44     , mxEdSepArrayRow(m_xBuilder->weld_entry("arrayrow"))
45     , mxBtnSepReset(m_xBuilder->weld_button("reset"))
46     , mxLbOOXMLRecalcOptions(m_xBuilder->weld_combo_box("ooxmlrecalc"))
47     , mxLbODFRecalcOptions(m_xBuilder->weld_combo_box("odfrecalc"))
48 {
49     mxLbFormulaSyntax->append_text(ScResId(SCSTR_FORMULA_SYNTAX_CALC_A1));
50     mxLbFormulaSyntax->append_text(ScResId(SCSTR_FORMULA_SYNTAX_XL_A1));
51     mxLbFormulaSyntax->append_text(ScResId(SCSTR_FORMULA_SYNTAX_XL_R1C1));
52 
53     Link<weld::Button&,void> aLink2 = LINK( this, ScTpFormulaOptions, ButtonHdl );
54     mxBtnSepReset->connect_clicked(aLink2);
55     mxBtnCustomCalcDetails->connect_clicked(aLink2);
56 
57     Link<weld::Toggleable&,void> aToggleLink = LINK( this, ScTpFormulaOptions, ToggleHdl );
58     mxBtnCustomCalcDefault->connect_toggled(aToggleLink);
59     mxBtnCustomCalcCustom->connect_toggled(aToggleLink);
60 
61     mxEdSepFuncArg->connect_insert_text(LINK( this, ScTpFormulaOptions, SepInsertTextHdl ));
62     mxEdSepArrayCol->connect_insert_text(LINK( this, ScTpFormulaOptions, ColSepInsertTextHdl ));
63     mxEdSepArrayRow->connect_insert_text(LINK( this, ScTpFormulaOptions, RowSepInsertTextHdl ));
64 
65     Link<weld::Entry&,void> aLink = LINK( this, ScTpFormulaOptions, SepModifyHdl );
66     mxEdSepFuncArg->connect_changed(aLink);
67     mxEdSepArrayCol->connect_changed(aLink);
68     mxEdSepArrayRow->connect_changed(aLink);
69 
70     Link<weld::Widget&,void> aLink3 = LINK( this, ScTpFormulaOptions, SepEditOnFocusHdl );
71     mxEdSepFuncArg->connect_focus_in(aLink3);
72     mxEdSepArrayCol->connect_focus_in(aLink3);
73     mxEdSepArrayRow->connect_focus_in(aLink3);
74 
75     // Get the decimal separator for current locale.
76     OUString aSep = ScGlobal::getLocaleDataPtr()->getNumDecimalSep();
77     mnDecSep = aSep.isEmpty() ? u'.' : aSep[0];
78 
79     maSavedDocOptions = static_cast<const ScTpCalcItem&>(rCoreAttrs.Get(
80             GetWhich(SID_SCDOCOPTIONS))).GetDocOptions();
81 }
82 
~ScTpFormulaOptions()83 ScTpFormulaOptions::~ScTpFormulaOptions()
84 {
85 }
86 
ResetSeparators()87 void ScTpFormulaOptions::ResetSeparators()
88 {
89     OUString aFuncArg, aArrayCol, aArrayRow;
90     ScFormulaOptions::GetDefaultFormulaSeparators(aFuncArg, aArrayCol, aArrayRow);
91     mxEdSepFuncArg->set_text(aFuncArg);
92     mxEdSepArrayCol->set_text(aArrayCol);
93     mxEdSepArrayRow->set_text(aArrayRow);
94 }
95 
OnFocusSeparatorInput(weld::Entry * pEdit)96 void ScTpFormulaOptions::OnFocusSeparatorInput(weld::Entry* pEdit)
97 {
98     if (!pEdit)
99         return;
100 
101     // Make sure the entire text is selected.
102     pEdit->select_region(0, -1);
103     OUString sSepValue = pEdit->get_text();
104     if (!sSepValue.isEmpty())
105         maOldSepValue = sSepValue;
106 }
107 
UpdateCustomCalcRadioButtons(bool bDefault)108 void ScTpFormulaOptions::UpdateCustomCalcRadioButtons(bool bDefault)
109 {
110     if (bDefault)
111     {
112         mxBtnCustomCalcDefault->set_active(true);
113         mxBtnCustomCalcCustom->set_active(false);
114         mxBtnCustomCalcDetails->set_sensitive(false);
115     }
116     else
117     {
118         mxBtnCustomCalcDefault->set_active(false);
119         mxBtnCustomCalcCustom->set_active(true);
120         mxBtnCustomCalcDetails->set_sensitive(true);
121     }
122 }
123 
LaunchCustomCalcSettings()124 void ScTpFormulaOptions::LaunchCustomCalcSettings()
125 {
126     ScCalcOptionsDialog aDlg(GetFrameWeld(), maCurrentConfig, maCurrentDocOptions.IsWriteCalcConfig());
127     if (aDlg.run() == RET_OK)
128     {
129         maCurrentConfig = aDlg.GetConfig();
130         maCurrentDocOptions.SetWriteCalcConfig(aDlg.GetWriteCalcConfig());
131     }
132 }
133 
IsValidSeparator(const OUString & rSep,bool bArray) const134 bool ScTpFormulaOptions::IsValidSeparator(const OUString& rSep, bool bArray) const
135 {
136     if (rSep.getLength() != 1)
137         // Must be one-character long.
138         return false;
139 
140     const sal_Unicode c = rSep[0];
141 
142     if (c == mnDecSep)
143         // decimal separator is not allowed.
144         return false;
145 
146     if (c <= 0x20 || c == 0x7f)
147         // Disallow non-printables including space and DEL.
148         return false;
149 
150     if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9'))
151         // Disallow alphanumeric.
152         return false;
153 
154     if (bArray)
155     {
156         switch (c)
157         {
158             case '+':
159             case '-':
160             case '{':
161             case '}':
162             case '"':
163                 // All following just to prevent confusion, they are not
164                 // evaluated in inline arrays and theoretically would be
165                 // possible.
166             case '%':
167             case '/':
168             case '*':
169             case '=':
170             case '<':
171             case '>':
172             case '[':
173             case ']':
174             case '(':
175             case ')':
176             case '\'':
177                 // Disallowed characters.  Anything else we want to disallow ?
178                 return false;
179         }
180     }
181     else if (c <= 0x7f)
182     {
183         switch (c)
184         {
185             default:
186                 // Anything bad except the knowns.
187                 return false;
188             case ';':
189             case ',':
190                 ; // nothing
191         }
192     }
193     else
194     {
195         // Any Unicode character, would have to ask the compiler's localized
196         // symbol map whether it's a known symbol but not a separator
197         // (ocSep,ocArrayRowSep,ocArrayColSep), which we're about to set here.
198         // But really..
199         return false;
200     }
201 
202     return true;
203 }
204 
IMPL_LINK(ScTpFormulaOptions,ButtonHdl,weld::Button &,rBtn,void)205 IMPL_LINK( ScTpFormulaOptions, ButtonHdl, weld::Button&, rBtn, void )
206 {
207     if (&rBtn == mxBtnSepReset.get())
208         ResetSeparators();
209     else if (&rBtn == mxBtnCustomCalcDetails.get())
210         LaunchCustomCalcSettings();
211 }
212 
IMPL_LINK(ScTpFormulaOptions,ToggleHdl,weld::Toggleable &,rBtn,void)213 IMPL_LINK( ScTpFormulaOptions, ToggleHdl, weld::Toggleable&, rBtn, void )
214 {
215     if (!rBtn.get_active())
216         return;
217     if (mxBtnCustomCalcDefault->get_active())
218         UpdateCustomCalcRadioButtons(true);
219     else if (mxBtnCustomCalcCustom->get_active())
220         UpdateCustomCalcRadioButtons(false);
221 }
222 
IMPL_LINK(ScTpFormulaOptions,SepInsertTextHdl,OUString &,rTest,bool)223 IMPL_LINK(ScTpFormulaOptions, SepInsertTextHdl, OUString&, rTest, bool)
224 {
225     if (!IsValidSeparator(rTest, false) && !maOldSepValue.isEmpty())
226         // Invalid separator.  Restore the old value.
227         rTest = maOldSepValue;
228     return true;
229 }
230 
IMPL_LINK(ScTpFormulaOptions,RowSepInsertTextHdl,OUString &,rTest,bool)231 IMPL_LINK(ScTpFormulaOptions, RowSepInsertTextHdl, OUString&, rTest, bool)
232 {
233     // Invalid separator or same as ColStr - Restore the old value.
234     if ((!IsValidSeparator(rTest, true) || rTest == mxEdSepArrayCol->get_text()) && !maOldSepValue.isEmpty())
235         rTest = maOldSepValue;
236     return true;
237 }
238 
IMPL_LINK(ScTpFormulaOptions,ColSepInsertTextHdl,OUString &,rTest,bool)239 IMPL_LINK(ScTpFormulaOptions, ColSepInsertTextHdl, OUString&, rTest, bool)
240 {
241     // Invalid separator or same as RowStr - Restore the old value.
242     if ((!IsValidSeparator(rTest, true) || rTest == mxEdSepArrayRow->get_text()) && !maOldSepValue.isEmpty())
243         rTest = maOldSepValue;
244     return true;
245 }
246 
IMPL_LINK(ScTpFormulaOptions,SepModifyHdl,weld::Entry &,rEdit,void)247 IMPL_LINK( ScTpFormulaOptions, SepModifyHdl, weld::Entry&, rEdit, void )
248 {
249     OnFocusSeparatorInput(&rEdit);
250 }
251 
IMPL_LINK(ScTpFormulaOptions,SepEditOnFocusHdl,weld::Widget &,rControl,void)252 IMPL_LINK( ScTpFormulaOptions, SepEditOnFocusHdl, weld::Widget&, rControl, void )
253 {
254     OnFocusSeparatorInput(dynamic_cast<weld::Entry*>(&rControl));
255 }
256 
Create(weld::Container * pPage,weld::DialogController * pController,const SfxItemSet * rCoreSet)257 std::unique_ptr<SfxTabPage> ScTpFormulaOptions::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rCoreSet)
258 {
259     return std::make_unique<ScTpFormulaOptions>(pPage, pController, *rCoreSet);
260 }
261 
FillItemSet(SfxItemSet * rCoreSet)262 bool ScTpFormulaOptions::FillItemSet(SfxItemSet* rCoreSet)
263 {
264     bool bRet = false;
265     ScFormulaOptions aOpt;
266     bool bEnglishFuncName = mxCbEnglishFuncName->get_active();
267     sal_Int16 aSyntaxPos      = mxLbFormulaSyntax->get_active();
268     OUString aSep             = mxEdSepFuncArg->get_text();
269     OUString aSepArrayCol     = mxEdSepArrayCol->get_text();
270     OUString aSepArrayRow     = mxEdSepArrayRow->get_text();
271     sal_Int16 nOOXMLRecalcMode = mxLbOOXMLRecalcOptions->get_active();
272     sal_Int16 nODFRecalcMode = mxLbODFRecalcOptions->get_active();
273 
274     if (mxBtnCustomCalcDefault->get_active())
275     {
276         // When Default is selected, reset all the calc config settings to default.
277         maCurrentConfig.reset();
278     }
279 
280     if ( mxLbFormulaSyntax->get_saved_value() != mxLbFormulaSyntax->get_text(aSyntaxPos)
281          || mxCbEnglishFuncName->get_saved_state() != (bEnglishFuncName ? 1 : 0)
282          || mxEdSepFuncArg->get_saved_value() != aSep
283          || mxEdSepArrayCol->get_saved_value() != aSepArrayCol
284          || mxEdSepArrayRow->get_saved_value() != aSepArrayRow
285          || mxLbOOXMLRecalcOptions->get_saved_value() != mxLbOOXMLRecalcOptions->get_text(nOOXMLRecalcMode)
286          || mxLbODFRecalcOptions->get_saved_value() != mxLbODFRecalcOptions->get_text(nODFRecalcMode)
287          || maSavedConfig != maCurrentConfig
288          || maSavedDocOptions != maCurrentDocOptions )
289     {
290         ::formula::FormulaGrammar::Grammar eGram = ::formula::FormulaGrammar::GRAM_DEFAULT;
291 
292         switch (aSyntaxPos)
293         {
294         case 0:
295             eGram = ::formula::FormulaGrammar::GRAM_NATIVE;
296         break;
297         case 1:
298             eGram = ::formula::FormulaGrammar::GRAM_NATIVE_XL_A1;
299         break;
300         case 2:
301             eGram = ::formula::FormulaGrammar::GRAM_NATIVE_XL_R1C1;
302         break;
303         }
304 
305         ScRecalcOptions eOOXMLRecalc = static_cast<ScRecalcOptions>(nOOXMLRecalcMode);
306         ScRecalcOptions eODFRecalc = static_cast<ScRecalcOptions>(nODFRecalcMode);
307 
308         aOpt.SetFormulaSyntax(eGram);
309         aOpt.SetUseEnglishFuncName(bEnglishFuncName);
310         aOpt.SetFormulaSepArg(aSep);
311         aOpt.SetFormulaSepArrayCol(aSepArrayCol);
312         aOpt.SetFormulaSepArrayRow(aSepArrayRow);
313         aOpt.SetCalcConfig(maCurrentConfig);
314         aOpt.SetOOXMLRecalcOptions(eOOXMLRecalc);
315         aOpt.SetODFRecalcOptions(eODFRecalc);
316         aOpt.SetWriteCalcConfig( maCurrentDocOptions.IsWriteCalcConfig());
317 
318         rCoreSet->Put( ScTpFormulaItem( aOpt ) );
319         rCoreSet->Put( ScTpCalcItem( SID_SCDOCOPTIONS, maCurrentDocOptions ) );
320 
321         bRet = true;
322     }
323     return bRet;
324 }
325 
Reset(const SfxItemSet * rCoreSet)326 void ScTpFormulaOptions::Reset(const SfxItemSet* rCoreSet)
327 {
328     ScFormulaOptions aOpt;
329     const SfxPoolItem* pItem = nullptr;
330 
331     if(SfxItemState::SET == rCoreSet->GetItemState(SID_SCFORMULAOPTIONS, false , &pItem))
332         aOpt = static_cast<const ScTpFormulaItem*>(pItem)->GetFormulaOptions();
333 
334     // formula grammar.
335     ::formula::FormulaGrammar::Grammar eGram = aOpt.GetFormulaSyntax();
336 
337     switch (eGram)
338     {
339     case ::formula::FormulaGrammar::GRAM_NATIVE:
340         mxLbFormulaSyntax->set_active(0);
341         break;
342     case ::formula::FormulaGrammar::GRAM_NATIVE_XL_A1:
343         mxLbFormulaSyntax->set_active(1);
344         break;
345     case ::formula::FormulaGrammar::GRAM_NATIVE_XL_R1C1:
346         mxLbFormulaSyntax->set_active(2);
347         break;
348     default:
349         mxLbFormulaSyntax->set_active(0);
350     }
351 
352     mxLbFormulaSyntax->save_value();
353     mxLbFormulaSyntax->set_sensitive( !officecfg::Office::Calc::Formula::Syntax::Grammar::isReadOnly() );
354 
355     ScRecalcOptions eOOXMLRecalc = aOpt.GetOOXMLRecalcOptions();
356     mxLbOOXMLRecalcOptions->set_active(static_cast<sal_uInt16>(eOOXMLRecalc));
357     mxLbOOXMLRecalcOptions->save_value();
358     mxLbOOXMLRecalcOptions->set_sensitive( !officecfg::Office::Calc::Formula::Load::OOXMLRecalcMode::isReadOnly() );
359 
360     ScRecalcOptions eODFRecalc = aOpt.GetODFRecalcOptions();
361     mxLbODFRecalcOptions->set_active(static_cast<sal_uInt16>(eODFRecalc));
362     mxLbODFRecalcOptions->save_value();
363     mxLbODFRecalcOptions->set_sensitive( !officecfg::Office::Calc::Formula::Load::ODFRecalcMode::isReadOnly() );
364 
365     // english function name.
366     mxCbEnglishFuncName->set_active( aOpt.GetUseEnglishFuncName() );
367     mxCbEnglishFuncName->save_state();
368     mxCbEnglishFuncName->set_sensitive( !officecfg::Office::Calc::Formula::Syntax::EnglishFunctionName::isReadOnly() );
369 
370     // Separators
371     OUString aSep = aOpt.GetFormulaSepArg();
372     OUString aSepArrayRow = aOpt.GetFormulaSepArrayRow();
373     OUString aSepArrayCol = aOpt.GetFormulaSepArrayCol();
374 
375     if (IsValidSeparator(aSep, false) && IsValidSeparator(aSepArrayRow, true) && IsValidSeparator(aSepArrayCol, true))
376     {
377         // Each and all separators must be valid.
378         mxEdSepFuncArg->set_text(aSep);
379         mxEdSepArrayCol->set_text(aSepArrayCol);
380         mxEdSepArrayRow->set_text(aSepArrayRow);
381 
382         mxEdSepFuncArg->save_value();
383         mxEdSepArrayCol->save_value();
384         mxEdSepArrayRow->save_value();
385     }
386     else
387         ResetSeparators();
388 
389     mxEdSepFuncArg->set_sensitive( !officecfg::Office::Calc::Formula::Syntax::SeparatorArg::isReadOnly() );
390     mxEdSepArrayCol->set_sensitive( !officecfg::Office::Calc::Formula::Syntax::SeparatorArrayCol::isReadOnly() );
391     mxEdSepArrayRow->set_sensitive( !officecfg::Office::Calc::Formula::Syntax::SeparatorArrayRow::isReadOnly() );
392     mxBtnSepReset->set_sensitive ( !officecfg::Office::Calc::Formula::Syntax::SeparatorArg::isReadOnly()  &&
393                             !officecfg::Office::Calc::Formula::Syntax::SeparatorArrayCol::isReadOnly() &&
394                             !officecfg::Office::Calc::Formula::Syntax::SeparatorArrayRow::isReadOnly() );
395 
396     // detailed calc settings.
397     ScFormulaOptions aDefaults;
398 
399     maSavedConfig = aOpt.GetCalcConfig();
400     bool bDefault = aDefaults.GetCalcConfig() == maSavedConfig;
401     UpdateCustomCalcRadioButtons(bDefault);
402 
403     maCurrentConfig = maSavedConfig;
404 
405     maCurrentDocOptions = maSavedDocOptions;
406 }
407 
DeactivatePage(SfxItemSet *)408 DeactivateRC ScTpFormulaOptions::DeactivatePage(SfxItemSet* /*pSet*/)
409 {
410     // What's this method for ?
411     return DeactivateRC::KeepPage;
412 }
413 
414 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
415