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 .
21 #include <solveroptions.hxx>
23 #include <miscuno.hxx>
24 #include <solverutil.hxx>
26 #include <rtl/math.hxx>
27 #include <unotools/collatorwrapper.hxx>
28 #include <unotools/localedatawrapper.hxx>
29 #include <osl/diagnose.h>
33 #include <com/sun/star/sheet/XSolver.hpp>
34 #include <com/sun/star/sheet/XSolverDescription.hpp>
35 #include <com/sun/star/beans/PropertyValue.hpp>
38 using namespace com::sun::star
;
42 /// Helper for sorting properties
43 struct ScSolverOptionsEntry
46 OUString aDescription
;
48 ScSolverOptionsEntry() : nPosition(0) {}
50 bool operator< (const ScSolverOptionsEntry
& rOther
) const
52 return (ScGlobal::GetCollator().compareString( aDescription
, rOther
.aDescription
) < 0);
58 ScSolverOptionsDialog::ScSolverOptionsDialog(weld::Window
* pParent
,
59 const uno::Sequence
<OUString
>& rImplNames
,
60 const uno::Sequence
<OUString
>& rDescriptions
,
62 const uno::Sequence
<beans::PropertyValue
>& rProperties
)
63 : GenericDialogController(pParent
, u
"modules/scalc/ui/solveroptionsdialog.ui"_ustr
, u
"SolverOptionsDialog"_ustr
)
64 , maImplNames(rImplNames
)
65 , maEngine(std::move(aEngine
))
66 , maProperties(rProperties
)
67 , m_xLbEngine(m_xBuilder
->weld_combo_box(u
"engine"_ustr
))
68 , m_xLbSettings(m_xBuilder
->weld_tree_view(u
"settings"_ustr
))
69 , m_xBtnEdit(m_xBuilder
->weld_button(u
"edit"_ustr
))
71 m_xLbSettings
->set_size_request(m_xLbSettings
->get_approximate_digit_width() * 32,
72 m_xLbSettings
->get_height_rows(12));
74 m_xLbSettings
->enable_toggle_buttons(weld::ColumnToggleType::Check
);
76 m_xLbEngine
->connect_changed( LINK( this, ScSolverOptionsDialog
, EngineSelectHdl
) );
78 m_xBtnEdit
->connect_clicked( LINK( this, ScSolverOptionsDialog
, ButtonHdl
) );
80 m_xLbSettings
->connect_selection_changed(LINK(this, ScSolverOptionsDialog
, SettingsSelHdl
));
81 m_xLbSettings
->connect_row_activated( LINK( this, ScSolverOptionsDialog
, SettingsDoubleClickHdl
) );
83 sal_Int32 nSelect
= -1;
84 sal_Int32 nImplCount
= maImplNames
.getLength();
85 for (sal_Int32 nImpl
=0; nImpl
<nImplCount
; ++nImpl
)
87 OUString
aImplName( maImplNames
[nImpl
] );
88 const OUString
& aDescription( rDescriptions
[nImpl
] ); // user-visible descriptions in list box
89 m_xLbEngine
->append_text(aDescription
);
90 if ( aImplName
== maEngine
)
93 if ( nSelect
< 0 ) // no (valid) engine given
97 maEngine
= maImplNames
[0]; // use first implementation
102 maProperties
.realloc(0); // don't use options from different engine
104 if ( nSelect
>= 0 ) // select in list box
105 m_xLbEngine
->set_active(nSelect
);
107 if ( !maProperties
.hasElements() )
108 ReadFromComponent(); // fill maProperties from component (using maEngine)
109 FillListBox(); // using maProperties
112 ScSolverOptionsDialog::~ScSolverOptionsDialog()
115 m_xIntDialog
->response(RET_CANCEL
);
116 assert(!m_xIntDialog
);
118 m_xValDialog
->response(RET_CANCEL
);
119 assert(!m_xValDialog
);
122 const uno::Sequence
<beans::PropertyValue
>& ScSolverOptionsDialog::GetProperties()
124 // update maProperties from list box content
125 // order of entries in list box and maProperties is the same
126 sal_Int32 nEntryCount
= maProperties
.getLength();
127 if (nEntryCount
== m_xLbSettings
->n_children())
129 auto maPropertiesRange
= asNonConstRange(maProperties
);
130 for (sal_Int32 nEntryPos
=0; nEntryPos
<nEntryCount
; ++nEntryPos
)
132 uno::Any
& rValue
= maPropertiesRange
[nEntryPos
].Value
;
133 if (ScSolverOptionsString
* pStringItem
= weld::fromId
<ScSolverOptionsString
*>(m_xLbSettings
->get_id(nEntryPos
)))
135 if (pStringItem
->IsDouble())
136 rValue
<<= pStringItem
->GetDoubleValue();
138 rValue
<<= pStringItem
->GetIntValue();
141 rValue
<<= m_xLbSettings
->get_toggle(nEntryPos
) == TRISTATE_TRUE
;
146 OSL_FAIL( "wrong count" );
152 void ScSolverOptionsDialog::FillListBox()
154 // get property descriptions, sort by them
156 uno::Reference
<sheet::XSolverDescription
> xDesc( ScSolverUtil::GetSolver( maEngine
), uno::UNO_QUERY
);
157 sal_Int32 nCount
= maProperties
.getLength();
158 std::vector
<ScSolverOptionsEntry
> aDescriptions( nCount
);
159 for (sal_Int32 nPos
=0; nPos
<nCount
; nPos
++)
161 OUString
aPropName( maProperties
[nPos
].Name
);
164 aVisName
= xDesc
->getPropertyDescription( aPropName
);
165 if ( aVisName
.isEmpty() )
166 aVisName
= aPropName
;
167 aDescriptions
[nPos
].nPosition
= nPos
;
168 aDescriptions
[nPos
].aDescription
= aVisName
;
170 std::sort( aDescriptions
.begin(), aDescriptions
.end() );
172 // also update maProperties to the order of descriptions
174 uno::Sequence
<beans::PropertyValue
> aNewSeq
;
175 aNewSeq
.realloc( nCount
);
176 std::transform(aDescriptions
.begin(), aDescriptions
.end(), aNewSeq
.getArray(),
177 [this](const ScSolverOptionsEntry
& rDescr
) -> beans::PropertyValue
{ return maProperties
[ rDescr
.nPosition
]; });
178 maProperties
= std::move(aNewSeq
);
182 m_xLbSettings
->freeze();
183 m_xLbSettings
->clear();
185 for (sal_Int32 nPos
=0; nPos
<nCount
; nPos
++)
187 OUString aVisName
= aDescriptions
[nPos
].aDescription
;
189 uno::Any aValue
= maProperties
[nPos
].Value
;
190 uno::TypeClass eClass
= aValue
.getValueTypeClass();
192 m_xLbSettings
->append();
194 if ( eClass
== uno::TypeClass_BOOLEAN
)
197 m_xLbSettings
->set_toggle(nPos
, ScUnoHelpFunctions::GetBoolFromAny(aValue
) ? TRISTATE_TRUE
: TRISTATE_FALSE
);
198 m_xLbSettings
->set_text(nPos
, aVisName
, 0);
203 m_xLbSettings
->set_text(nPos
, aVisName
, 0);
204 m_aOptions
.emplace_back(new ScSolverOptionsString(aVisName
));
205 if (eClass
== uno::TypeClass_DOUBLE
)
207 double fDoubleValue
= 0.0;
208 if (aValue
>>= fDoubleValue
)
209 m_aOptions
.back()->SetDoubleValue(fDoubleValue
);
211 OUString sTxt
= aVisName
+ ": " +
212 rtl::math::doubleToUString(fDoubleValue
,
213 rtl_math_StringFormat_Automatic
, rtl_math_DecimalPlaces_Max
,
214 ScGlobal::getLocaleData().getNumDecimalSep()[0], true );
216 m_xLbSettings
->set_text(nPos
, sTxt
, 0);
220 sal_Int32 nIntValue
= 0;
221 if (aValue
>>= nIntValue
)
222 m_aOptions
.back()->SetIntValue(nIntValue
);
224 OUString sTxt
= aVisName
+ ": " + OUString::number(nIntValue
);
226 m_xLbSettings
->set_text(nPos
, sTxt
, 0);
228 m_xLbSettings
->set_id(nPos
, weld::toId(m_aOptions
.back().get()));
232 m_xLbSettings
->thaw();
235 void ScSolverOptionsDialog::ReadFromComponent()
237 maProperties
= ScSolverUtil::GetDefaults( maEngine
);
240 void ScSolverOptionsDialog::EditOption()
242 int nEntry
= m_xLbSettings
->get_selected_index();
245 ScSolverOptionsString
* pStringItem
= weld::fromId
<ScSolverOptionsString
*>(m_xLbSettings
->get_id(nEntry
));
249 if (pStringItem
->IsDouble())
251 m_xValDialog
= std::make_shared
<ScSolverValueDialog
>(m_xDialog
.get());
252 m_xValDialog
->SetOptionName(pStringItem
->GetText());
253 if (maProperties
[nEntry
].Name
== "DECR")
254 m_xValDialog
->SetMax(1.0);
255 else if (maProperties
[nEntry
].Name
== "DEFactorMax")
256 m_xValDialog
->SetMax(1.2);
257 else if (maProperties
[nEntry
].Name
== "DEFactorMin")
258 m_xValDialog
->SetMax(1.2);
259 else if (maProperties
[nEntry
].Name
== "PSCL")
260 m_xValDialog
->SetMax(0.005);
261 m_xValDialog
->SetValue(pStringItem
->GetDoubleValue());
262 weld::DialogController::runAsync(m_xValDialog
, [nEntry
, pStringItem
, this](sal_Int32 nResult
){
263 if (nResult
== RET_OK
)
265 pStringItem
->SetDoubleValue(m_xValDialog
->GetValue());
267 OUString
sTxt(pStringItem
->GetText() + ": " +
268 rtl::math::doubleToUString(pStringItem
->GetDoubleValue(),
269 rtl_math_StringFormat_Automatic
, rtl_math_DecimalPlaces_Max
,
270 ScGlobal::getLocaleData().getNumDecimalSep()[0], true ));
272 m_xLbSettings
->set_text(nEntry
, sTxt
, 0);
274 m_xValDialog
.reset();
279 m_xIntDialog
= std::make_shared
<ScSolverIntegerDialog
>(m_xDialog
.get());
280 m_xIntDialog
->SetOptionName( pStringItem
->GetText() );
281 if (maProperties
[nEntry
].Name
== "EpsilonLevel")
282 m_xIntDialog
->SetMax(3);
283 else if (maProperties
[nEntry
].Name
== "Algorithm")
284 m_xIntDialog
->SetMax(1);
285 m_xIntDialog
->SetValue( pStringItem
->GetIntValue() );
286 weld::DialogController::runAsync(m_xIntDialog
, [nEntry
, pStringItem
, this](sal_Int32 nResult
){
287 if (nResult
== RET_OK
)
289 pStringItem
->SetIntValue(m_xIntDialog
->GetValue());
292 pStringItem
->GetText() + ": " + OUString::number(pStringItem
->GetIntValue()));
294 m_xLbSettings
->set_text(nEntry
, sTxt
, 0);
296 m_xIntDialog
.reset();
301 IMPL_LINK( ScSolverOptionsDialog
, ButtonHdl
, weld::Button
&, rBtn
, void )
303 if (&rBtn
== m_xBtnEdit
.get())
307 IMPL_LINK_NOARG(ScSolverOptionsDialog
, SettingsDoubleClickHdl
, weld::TreeView
&, bool)
313 IMPL_LINK_NOARG(ScSolverOptionsDialog
, EngineSelectHdl
, weld::ComboBox
&, void)
315 const sal_Int32 nSelectPos
= m_xLbEngine
->get_active();
316 if ( nSelectPos
< maImplNames
.getLength() )
318 OUString
aNewEngine( maImplNames
[nSelectPos
] );
319 if ( aNewEngine
!= maEngine
)
321 maEngine
= aNewEngine
;
322 ReadFromComponent(); // fill maProperties from component (using maEngine)
323 FillListBox(); // using maProperties
328 IMPL_LINK_NOARG(ScSolverOptionsDialog
, SettingsSelHdl
, weld::TreeView
&, void)
330 bool bCheckbox
= false;
332 int nEntry
= m_xLbSettings
->get_selected_index();
335 ScSolverOptionsString
* pStringItem
= weld::fromId
<ScSolverOptionsString
*>(m_xLbSettings
->get_id(nEntry
));
340 m_xBtnEdit
->set_sensitive(!bCheckbox
);
343 ScSolverIntegerDialog::ScSolverIntegerDialog(weld::Window
* pParent
)
344 : GenericDialogController(pParent
, u
"modules/scalc/ui/integerdialog.ui"_ustr
, u
"IntegerDialog"_ustr
)
345 , m_xFrame(m_xBuilder
->weld_frame(u
"frame"_ustr
))
346 , m_xNfValue(m_xBuilder
->weld_spin_button(u
"value"_ustr
))
350 ScSolverIntegerDialog::~ScSolverIntegerDialog()
354 void ScSolverIntegerDialog::SetOptionName( const OUString
& rName
)
356 m_xFrame
->set_label(rName
);
359 void ScSolverIntegerDialog::SetValue( sal_Int32 nValue
)
361 m_xNfValue
->set_value( nValue
);
364 void ScSolverIntegerDialog::SetMax( sal_Int32 nMax
)
366 m_xNfValue
->set_range(0, nMax
);
369 sal_Int32
ScSolverIntegerDialog::GetValue() const
371 return m_xNfValue
->get_value();
374 ScSolverValueDialog::ScSolverValueDialog(weld::Window
* pParent
)
375 : GenericDialogController(pParent
, u
"modules/scalc/ui/doubledialog.ui"_ustr
, u
"DoubleDialog"_ustr
)
376 , m_xFrame(m_xBuilder
->weld_frame(u
"frame"_ustr
))
377 , m_xEdValue(m_xBuilder
->weld_entry(u
"value"_ustr
))
378 , m_fMaxValue(std::numeric_limits
<double>::quiet_NaN())
382 ScSolverValueDialog::~ScSolverValueDialog()
386 void ScSolverValueDialog::SetOptionName( const OUString
& rName
)
388 m_xFrame
->set_label(rName
);
391 void ScSolverValueDialog::SetValue( double fValue
)
393 m_xEdValue
->set_text( rtl::math::doubleToUString( fValue
,
394 rtl_math_StringFormat_Automatic
, rtl_math_DecimalPlaces_Max
,
395 ScGlobal::getLocaleData().getNumDecimalSep()[0], true ) );
398 void ScSolverValueDialog::SetMax(double fMax
)
403 double ScSolverValueDialog::GetValue() const
405 OUString aInput
= m_xEdValue
->get_text();
407 rtl_math_ConversionStatus eStatus
= rtl_math_ConversionStatus_Ok
;
408 sal_Int32 nParseEnd
= 0;
409 double fValue
= ScGlobal::getLocaleData().stringToDouble( aInput
, true, &eStatus
, &nParseEnd
);
410 /* TODO: shouldn't there be some error checking? */
411 if (!std::isnan(m_fMaxValue
) && fValue
> m_fMaxValue
)
412 fValue
= m_fMaxValue
;
416 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */