LibreOffice Module sc (master) 1
solveroptions.cxx
Go to the documentation of this file.
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#include <memory>
21#include <solveroptions.hxx>
22#include <global.hxx>
23#include <miscuno.hxx>
24#include <solverutil.hxx>
25
26#include <rtl/math.hxx>
29#include <osl/diagnose.h>
30
31#include <algorithm>
32
33#include <com/sun/star/sheet/XSolver.hpp>
34#include <com/sun/star/sheet/XSolverDescription.hpp>
35#include <com/sun/star/beans/PropertyValue.hpp>
36#include <utility>
37
38using namespace com::sun::star;
39
40namespace {
41
43struct ScSolverOptionsEntry
44{
45 sal_Int32 nPosition;
46 OUString aDescription;
47
48 ScSolverOptionsEntry() : nPosition(0) {}
49
50 bool operator< (const ScSolverOptionsEntry& rOther) const
51 {
52 return (ScGlobal::GetCollator().compareString( aDescription, rOther.aDescription ) < 0);
53 }
54};
55
56}
57
59 const uno::Sequence<OUString>& rImplNames,
60 const uno::Sequence<OUString>& rDescriptions,
61 OUString aEngine,
62 const uno::Sequence<beans::PropertyValue>& rProperties )
63 : GenericDialogController(pParent, "modules/scalc/ui/solveroptionsdialog.ui", "SolverOptionsDialog")
64 , maImplNames(rImplNames)
65 , maEngine(std::move(aEngine))
66 , maProperties(rProperties)
67 , m_xLbEngine(m_xBuilder->weld_combo_box("engine"))
68 , m_xLbSettings(m_xBuilder->weld_tree_view("settings"))
69 , m_xBtnEdit(m_xBuilder->weld_button("edit"))
70{
71 m_xLbSettings->set_size_request(m_xLbSettings->get_approximate_digit_width() * 32,
72 m_xLbSettings->get_height_rows(6));
73
74 m_xLbSettings->enable_toggle_buttons(weld::ColumnToggleType::Check);
75
76 m_xLbEngine->connect_changed( LINK( this, ScSolverOptionsDialog, EngineSelectHdl ) );
77
78 m_xBtnEdit->connect_clicked( LINK( this, ScSolverOptionsDialog, ButtonHdl ) );
79
80 m_xLbSettings->connect_changed( LINK( this, ScSolverOptionsDialog, SettingsSelHdl ) );
81 m_xLbSettings->connect_row_activated( LINK( this, ScSolverOptionsDialog, SettingsDoubleClickHdl ) );
82
83 sal_Int32 nSelect = -1;
84 sal_Int32 nImplCount = maImplNames.getLength();
85 for (sal_Int32 nImpl=0; nImpl<nImplCount; ++nImpl)
86 {
87 OUString aImplName( maImplNames[nImpl] );
88 OUString aDescription( rDescriptions[nImpl] ); // user-visible descriptions in list box
89 m_xLbEngine->append_text(aDescription);
90 if ( aImplName == maEngine )
91 nSelect = nImpl;
92 }
93 if ( nSelect < 0 ) // no (valid) engine given
94 {
95 if ( nImplCount > 0 )
96 {
97 maEngine = maImplNames[0]; // use first implementation
98 nSelect = 0;
99 }
100 else
101 maEngine.clear();
102 maProperties.realloc(0); // don't use options from different engine
103 }
104 if ( nSelect >= 0 ) // select in list box
105 m_xLbEngine->set_active(nSelect);
106
107 if ( !maProperties.hasElements() )
108 ReadFromComponent(); // fill maProperties from component (using maEngine)
109 FillListBox(); // using maProperties
110}
111
113{
114 if (m_xIntDialog)
115 m_xIntDialog->response(RET_CANCEL);
116 assert(!m_xIntDialog);
117 if (m_xValDialog)
118 m_xValDialog->response(RET_CANCEL);
119 assert(!m_xValDialog);
120}
121
122const uno::Sequence<beans::PropertyValue>& ScSolverOptionsDialog::GetProperties()
123{
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())
128 {
129 auto maPropertiesRange = asNonConstRange(maProperties);
130 for (sal_Int32 nEntryPos=0; nEntryPos<nEntryCount; ++nEntryPos)
131 {
132 uno::Any& rValue = maPropertiesRange[nEntryPos].Value;
133 if (ScSolverOptionsString* pStringItem = weld::fromId<ScSolverOptionsString*>(m_xLbSettings->get_id(nEntryPos)))
134 {
135 if (pStringItem->IsDouble())
136 rValue <<= pStringItem->GetDoubleValue();
137 else
138 rValue <<= pStringItem->GetIntValue();
139 }
140 else
141 rValue <<= m_xLbSettings->get_toggle(nEntryPos) == TRISTATE_TRUE;
142 }
143 }
144 else
145 {
146 OSL_FAIL( "wrong count" );
147 }
148
149 return maProperties;
150}
151
153{
154 // get property descriptions, sort by them
155
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++)
160 {
161 OUString aPropName( maProperties[nPos].Name );
162 OUString aVisName;
163 if ( xDesc.is() )
164 aVisName = xDesc->getPropertyDescription( aPropName );
165 if ( aVisName.isEmpty() )
166 aVisName = aPropName;
167 aDescriptions[nPos].nPosition = nPos;
168 aDescriptions[nPos].aDescription = aVisName;
169 }
170 std::sort( aDescriptions.begin(), aDescriptions.end() );
171
172 // also update maProperties to the order of descriptions
173
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 = aNewSeq;
179
180 // fill the list box
181
182 m_xLbSettings->freeze();
183 m_xLbSettings->clear();
184
185 for (sal_Int32 nPos=0; nPos<nCount; nPos++)
186 {
187 OUString aVisName = aDescriptions[nPos].aDescription;
188
189 uno::Any aValue = maProperties[nPos].Value;
190 uno::TypeClass eClass = aValue.getValueTypeClass();
191
192 m_xLbSettings->append();
193
194 if ( eClass == uno::TypeClass_BOOLEAN )
195 {
196 // check box entry
198 m_xLbSettings->set_text(nPos, aVisName, 0);
199 }
200 else
201 {
202 // value entry
203 m_xLbSettings->set_text(nPos, aVisName, 0);
204 m_aOptions.emplace_back(new ScSolverOptionsString(aVisName));
205 if (eClass == uno::TypeClass_DOUBLE)
206 {
207 double fDoubleValue = 0.0;
208 if (aValue >>= fDoubleValue)
209 m_aOptions.back()->SetDoubleValue(fDoubleValue);
210
211 OUString sTxt = aVisName + ": " +
212 rtl::math::doubleToUString(fDoubleValue,
213 rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
214 ScGlobal::getLocaleData().getNumDecimalSep()[0], true );
215
216 m_xLbSettings->set_text(nPos, sTxt, 0);
217 }
218 else
219 {
220 sal_Int32 nIntValue = 0;
221 if (aValue >>= nIntValue)
222 m_aOptions.back()->SetIntValue(nIntValue);
223
224 OUString sTxt = aVisName + ": " + OUString::number(nIntValue);
225
226 m_xLbSettings->set_text(nPos, sTxt, 0);
227 }
228 m_xLbSettings->set_id(nPos, weld::toId(m_aOptions.back().get()));
229 }
230 }
231
232 m_xLbSettings->thaw();
233}
234
236{
238}
239
241{
242 int nEntry = m_xLbSettings->get_selected_index();
243 if (nEntry == -1)
244 return;
245 ScSolverOptionsString* pStringItem = weld::fromId<ScSolverOptionsString*>(m_xLbSettings->get_id(nEntry));
246 if (!pStringItem)
247 return;
248
249 if (pStringItem->IsDouble())
250 {
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)
264 {
265 pStringItem->SetDoubleValue(m_xValDialog->GetValue());
266
267 OUString sTxt(pStringItem->GetText() + ": " +
268 rtl::math::doubleToUString(pStringItem->GetDoubleValue(),
269 rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
271
272 m_xLbSettings->set_text(nEntry, sTxt, 0);
273 }
274 m_xValDialog.reset();
275 });
276 }
277 else
278 {
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)
288 {
289 pStringItem->SetIntValue(m_xIntDialog->GetValue());
290
291 OUString sTxt(
292 pStringItem->GetText() + ": " + OUString::number(pStringItem->GetIntValue()));
293
294 m_xLbSettings->set_text(nEntry, sTxt, 0);
295 }
296 m_xIntDialog.reset();
297 });
298 }
299}
300
302{
303 if (&rBtn == m_xBtnEdit.get())
304 EditOption();
305}
306
308{
309 EditOption();
310 return true;
311}
312
314{
315 const sal_Int32 nSelectPos = m_xLbEngine->get_active();
316 if ( nSelectPos < maImplNames.getLength() )
317 {
318 OUString aNewEngine( maImplNames[nSelectPos] );
319 if ( aNewEngine != maEngine )
320 {
321 maEngine = aNewEngine;
322 ReadFromComponent(); // fill maProperties from component (using maEngine)
323 FillListBox(); // using maProperties
324 }
325 }
326}
327
329{
330 bool bCheckbox = false;
331
332 int nEntry = m_xLbSettings->get_selected_index();
333 if (nEntry != -1)
334 {
335 ScSolverOptionsString* pStringItem = weld::fromId<ScSolverOptionsString*>(m_xLbSettings->get_id(nEntry));
336 if (!pStringItem)
337 bCheckbox = true;
338 }
339
340 m_xBtnEdit->set_sensitive(!bCheckbox);
341}
342
344 : GenericDialogController(pParent, "modules/scalc/ui/integerdialog.ui", "IntegerDialog")
345 , m_xFrame(m_xBuilder->weld_frame("frame"))
346 , m_xNfValue(m_xBuilder->weld_spin_button("value"))
347{
348}
349
351{
352}
353
354void ScSolverIntegerDialog::SetOptionName( const OUString& rName )
355{
356 m_xFrame->set_label(rName);
357}
358
359void ScSolverIntegerDialog::SetValue( sal_Int32 nValue )
360{
361 m_xNfValue->set_value( nValue );
362}
363
364void ScSolverIntegerDialog::SetMax( sal_Int32 nMax )
365{
366 m_xNfValue->set_range(0, nMax);
367}
368
370{
371 return m_xNfValue->get_value();
372}
373
375 : GenericDialogController(pParent, "modules/scalc/ui/doubledialog.ui", "DoubleDialog")
376 , m_xFrame(m_xBuilder->weld_frame("frame"))
377 , m_xEdValue(m_xBuilder->weld_entry("value"))
378 , m_fMaxValue(std::numeric_limits<double>::quiet_NaN())
379{
380}
381
383{
384}
385
386void ScSolverValueDialog::SetOptionName( const OUString& rName )
387{
388 m_xFrame->set_label(rName);
389}
390
391void ScSolverValueDialog::SetValue( double fValue )
392{
393 m_xEdValue->set_text( rtl::math::doubleToUString( fValue,
394 rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
395 ScGlobal::getLocaleData().getNumDecimalSep()[0], true ) );
396}
397
399{
400 m_fMaxValue = fMax;
401}
402
404{
405 OUString aInput = m_xEdValue->get_text();
406
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;
413 return fValue;
414}
415
416/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
css::uno::Reference< css::lang::XComponent > m_xFrame
double stringToDouble(std::u16string_view aString, bool bUseGroupSep, rtl_math_ConversionStatus *pStatus, sal_Int32 *pParseEnd) const
const OUString & getNumDecimalSep() const
static SC_DLLPUBLIC CollatorWrapper & GetCollator()
case-insensitive collator
Definition: global.cxx:1095
static SC_DLLPUBLIC const LocaleDataWrapper & getLocaleData()
Definition: global.cxx:1055
sal_Int32 GetValue() const
std::unique_ptr< weld::SpinButton > m_xNfValue
void SetValue(sal_Int32 nValue)
virtual ~ScSolverIntegerDialog() override
void SetOptionName(const OUString &rName)
void SetMax(sal_Int32 nValue)
std::unique_ptr< weld::Frame > m_xFrame
ScSolverIntegerDialog(weld::Window *pParent)
css::uno::Sequence< OUString > maImplNames
std::shared_ptr< ScSolverIntegerDialog > m_xIntDialog
std::shared_ptr< ScSolverValueDialog > m_xValDialog
virtual ~ScSolverOptionsDialog() override
std::unique_ptr< weld::Button > m_xBtnEdit
std::unique_ptr< weld::ComboBox > m_xLbEngine
std::unique_ptr< weld::TreeView > m_xLbSettings
const css::uno::Sequence< css::beans::PropertyValue > & GetProperties()
ScSolverOptionsDialog(weld::Window *pParent, const css::uno::Sequence< OUString > &rImplNames, const css::uno::Sequence< OUString > &rDescriptions, OUString aEngine, const css::uno::Sequence< css::beans::PropertyValue > &rProperties)
css::uno::Sequence< css::beans::PropertyValue > maProperties
std::vector< std::unique_ptr< ScSolverOptionsString > > m_aOptions
const OUString & GetText() const
double GetDoubleValue() const
sal_Int32 GetIntValue() const
void SetIntValue(sal_Int32 nNew)
void SetDoubleValue(double fNew)
static css::uno::Sequence< css::beans::PropertyValue > GetDefaults(std::u16string_view rImplName)
Definition: solverutil.cxx:136
static css::uno::Reference< css::sheet::XSolver > GetSolver(std::u16string_view rImplName)
Definition: solverutil.cxx:98
void SetOptionName(const OUString &rName)
virtual ~ScSolverValueDialog() override
std::unique_ptr< weld::Frame > m_xFrame
double GetValue() const
std::unique_ptr< weld::Entry > m_xEdValue
void SetMax(double fValue)
ScSolverValueDialog(weld::Window *pParent)
void SetValue(double fValue)
static bool GetBoolFromAny(const css::uno::Any &aAny)
Definition: miscuno.cxx:139
static bool runAsync(const std::shared_ptr< DialogController > &rController, const std::function< void(sal_Int32)> &)
std::shared_ptr< weld::Dialog > m_xDialog
int nCount
bool operator<(const ScDPCollection::DBType &left, const ScDPCollection::DBType &right)
Definition: dpobject.cxx:3969
sal_Int16 nValue
TRISTATE_FALSE
TRISTATE_TRUE
sal_uInt16 nPos
SvGenericNameContainerMapImpl maProperties
OUString aPropName
OUString toId(const void *pValue)
IMPL_LINK(ScSolverOptionsDialog, ButtonHdl, weld::Button &, rBtn, void)
IMPL_LINK_NOARG(ScSolverOptionsDialog, SettingsDoubleClickHdl, weld::TreeView &, bool)
Object Value
OUString Name
RET_OK
RET_CANCEL