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 
37 using namespace com::sun::star;
38 
39 namespace {
40 
42 struct ScSolverOptionsEntry
43 {
44  sal_Int32 nPosition;
45  OUString aDescription;
46 
47  ScSolverOptionsEntry() : nPosition(0) {}
48 
49  bool operator< (const ScSolverOptionsEntry& rOther) const
50  {
51  return (ScGlobal::GetCollator().compareString( aDescription, rOther.aDescription ) < 0);
52  }
53 };
54 
55 }
56 
58  const uno::Sequence<OUString>& rImplNames,
59  const uno::Sequence<OUString>& rDescriptions,
60  const OUString& rEngine,
61  const uno::Sequence<beans::PropertyValue>& rProperties )
62  : GenericDialogController(pParent, "modules/scalc/ui/solveroptionsdialog.ui", "SolverOptionsDialog")
63  , maImplNames(rImplNames)
64  , maEngine(rEngine)
65  , maProperties(rProperties)
66  , m_xLbEngine(m_xBuilder->weld_combo_box("engine"))
67  , m_xLbSettings(m_xBuilder->weld_tree_view("settings"))
68  , m_xBtnEdit(m_xBuilder->weld_button("edit"))
69 {
70  m_xLbSettings->set_size_request(m_xLbSettings->get_approximate_digit_width() * 32,
71  m_xLbSettings->get_height_rows(6));
72 
73  m_xLbSettings->enable_toggle_buttons(weld::ColumnToggleType::Check);
74 
75  m_xLbEngine->connect_changed( LINK( this, ScSolverOptionsDialog, EngineSelectHdl ) );
76 
77  m_xBtnEdit->connect_clicked( LINK( this, ScSolverOptionsDialog, ButtonHdl ) );
78 
79  m_xLbSettings->connect_changed( LINK( this, ScSolverOptionsDialog, SettingsSelHdl ) );
80  m_xLbSettings->connect_row_activated( LINK( this, ScSolverOptionsDialog, SettingsDoubleClickHdl ) );
81 
82  sal_Int32 nSelect = -1;
83  sal_Int32 nImplCount = maImplNames.getLength();
84  for (sal_Int32 nImpl=0; nImpl<nImplCount; ++nImpl)
85  {
86  OUString aImplName( maImplNames[nImpl] );
87  OUString aDescription( rDescriptions[nImpl] ); // user-visible descriptions in list box
88  m_xLbEngine->append_text(aDescription);
89  if ( aImplName == maEngine )
90  nSelect = nImpl;
91  }
92  if ( nSelect < 0 ) // no (valid) engine given
93  {
94  if ( nImplCount > 0 )
95  {
96  maEngine = maImplNames[0]; // use first implementation
97  nSelect = 0;
98  }
99  else
100  maEngine.clear();
101  maProperties.realloc(0); // don't use options from different engine
102  }
103  if ( nSelect >= 0 ) // select in list box
104  m_xLbEngine->set_active(nSelect);
105 
106  if ( !maProperties.hasElements() )
107  ReadFromComponent(); // fill maProperties from component (using maEngine)
108  FillListBox(); // using maProperties
109 }
110 
112 {
113  if (m_xIntDialog)
114  m_xIntDialog->response(RET_CANCEL);
115  assert(!m_xIntDialog);
116  if (m_xValDialog)
117  m_xValDialog->response(RET_CANCEL);
118  assert(!m_xValDialog);
119 }
120 
121 const uno::Sequence<beans::PropertyValue>& ScSolverOptionsDialog::GetProperties()
122 {
123  // update maProperties from list box content
124  // order of entries in list box and maProperties is the same
125  sal_Int32 nEntryCount = maProperties.getLength();
126  if (nEntryCount == m_xLbSettings->n_children())
127  {
128  auto maPropertiesRange = asNonConstRange(maProperties);
129  for (sal_Int32 nEntryPos=0; nEntryPos<nEntryCount; ++nEntryPos)
130  {
131  uno::Any& rValue = maPropertiesRange[nEntryPos].Value;
132  if (ScSolverOptionsString* pStringItem = reinterpret_cast<ScSolverOptionsString*>(m_xLbSettings->get_id(nEntryPos).toInt64()))
133  {
134  if (pStringItem->IsDouble())
135  rValue <<= pStringItem->GetDoubleValue();
136  else
137  rValue <<= pStringItem->GetIntValue();
138  }
139  else
140  rValue <<= m_xLbSettings->get_toggle(nEntryPos) == TRISTATE_TRUE;
141  }
142  }
143  else
144  {
145  OSL_FAIL( "wrong count" );
146  }
147 
148  return maProperties;
149 }
150 
152 {
153  // get property descriptions, sort by them
154 
155  uno::Reference<sheet::XSolverDescription> xDesc( ScSolverUtil::GetSolver( maEngine ), uno::UNO_QUERY );
156  sal_Int32 nCount = maProperties.getLength();
157  std::vector<ScSolverOptionsEntry> aDescriptions( nCount );
158  for (sal_Int32 nPos=0; nPos<nCount; nPos++)
159  {
160  OUString aPropName( maProperties[nPos].Name );
161  OUString aVisName;
162  if ( xDesc.is() )
163  aVisName = xDesc->getPropertyDescription( aPropName );
164  if ( aVisName.isEmpty() )
165  aVisName = aPropName;
166  aDescriptions[nPos].nPosition = nPos;
167  aDescriptions[nPos].aDescription = aVisName;
168  }
169  std::sort( aDescriptions.begin(), aDescriptions.end() );
170 
171  // also update maProperties to the order of descriptions
172 
173  uno::Sequence<beans::PropertyValue> aNewSeq;
174  aNewSeq.realloc( nCount );
175  std::transform(aDescriptions.begin(), aDescriptions.end(), aNewSeq.getArray(),
176  [this](const ScSolverOptionsEntry& rDescr) -> beans::PropertyValue { return maProperties[ rDescr.nPosition ]; });
177  maProperties = aNewSeq;
178 
179  // fill the list box
180 
181  m_xLbSettings->freeze();
182  m_xLbSettings->clear();
183 
184  for (sal_Int32 nPos=0; nPos<nCount; nPos++)
185  {
186  OUString aVisName = aDescriptions[nPos].aDescription;
187 
188  uno::Any aValue = maProperties[nPos].Value;
189  uno::TypeClass eClass = aValue.getValueTypeClass();
190 
191  m_xLbSettings->append();
192 
193  if ( eClass == uno::TypeClass_BOOLEAN )
194  {
195  // check box entry
197  m_xLbSettings->set_text(nPos, aVisName, 0);
198  }
199  else
200  {
201  // value entry
202  m_xLbSettings->set_text(nPos, aVisName, 0);
203  m_aOptions.emplace_back(new ScSolverOptionsString(aVisName));
204  if (eClass == uno::TypeClass_DOUBLE)
205  {
206  double fDoubleValue = 0.0;
207  if (aValue >>= fDoubleValue)
208  m_aOptions.back()->SetDoubleValue(fDoubleValue);
209 
210  OUString sTxt = aVisName + ": ";
211  sTxt += rtl::math::doubleToUString(fDoubleValue,
212  rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
213  ScGlobal::getLocaleData().getNumDecimalSep()[0], true );
214 
215  m_xLbSettings->set_text(nPos, sTxt, 0);
216  }
217  else
218  {
219  sal_Int32 nIntValue = 0;
220  if (aValue >>= nIntValue)
221  m_aOptions.back()->SetIntValue(nIntValue);
222 
223  OUString sTxt = aVisName + ": " + OUString::number(nIntValue);
224 
225  m_xLbSettings->set_text(nPos, sTxt, 0);
226  }
227  m_xLbSettings->set_id(nPos, OUString::number(reinterpret_cast<sal_Int64>(m_aOptions.back().get())));
228  }
229  }
230 
231  m_xLbSettings->thaw();
232 }
233 
235 {
237 }
238 
240 {
241  int nEntry = m_xLbSettings->get_selected_index();
242  if (nEntry == -1)
243  return;
244  ScSolverOptionsString* pStringItem = reinterpret_cast<ScSolverOptionsString*>(m_xLbSettings->get_id(nEntry).toInt64());
245  if (!pStringItem)
246  return;
247 
248  if (pStringItem->IsDouble())
249  {
250  m_xValDialog = std::make_shared<ScSolverValueDialog>(m_xDialog.get());
251  m_xValDialog->SetOptionName(pStringItem->GetText());
252  if (maProperties[nEntry].Name == "DECR")
253  m_xValDialog->SetMax(1.0);
254  else if (maProperties[nEntry].Name == "DEFactorMax")
255  m_xValDialog->SetMax(1.2);
256  else if (maProperties[nEntry].Name == "DEFactorMin")
257  m_xValDialog->SetMax(1.2);
258  else if (maProperties[nEntry].Name == "PSCL")
259  m_xValDialog->SetMax(0.005);
260  m_xValDialog->SetValue(pStringItem->GetDoubleValue());
261  weld::DialogController::runAsync(m_xValDialog, [nEntry, pStringItem, this](sal_Int32 nResult){
262  if (nResult == RET_OK)
263  {
264  pStringItem->SetDoubleValue(m_xValDialog->GetValue());
265 
266  OUString sTxt(pStringItem->GetText() + ": ");
267  sTxt += rtl::math::doubleToUString(pStringItem->GetDoubleValue(),
268  rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
270 
271  m_xLbSettings->set_text(nEntry, sTxt, 0);
272  }
273  m_xValDialog.reset();
274  });
275  }
276  else
277  {
278  m_xIntDialog = std::make_shared<ScSolverIntegerDialog>(m_xDialog.get());
279  m_xIntDialog->SetOptionName( pStringItem->GetText() );
280  if (maProperties[nEntry].Name == "EpsilonLevel")
281  m_xIntDialog->SetMax(3);
282  else if (maProperties[nEntry].Name == "Algorithm")
283  m_xIntDialog->SetMax(1);
284  m_xIntDialog->SetValue( pStringItem->GetIntValue() );
285  weld::DialogController::runAsync(m_xIntDialog, [nEntry, pStringItem, this](sal_Int32 nResult){
286  if (nResult == RET_OK)
287  {
288  pStringItem->SetIntValue(m_xIntDialog->GetValue());
289 
290  OUString sTxt(
291  pStringItem->GetText() + ": " + OUString::number(pStringItem->GetIntValue()));
292 
293  m_xLbSettings->set_text(nEntry, sTxt, 0);
294  }
295  m_xIntDialog.reset();
296  });
297  }
298 }
299 
300 IMPL_LINK( ScSolverOptionsDialog, ButtonHdl, weld::Button&, rBtn, void )
301 {
302  if (&rBtn == m_xBtnEdit.get())
303  EditOption();
304 }
305 
306 IMPL_LINK_NOARG(ScSolverOptionsDialog, SettingsDoubleClickHdl, weld::TreeView&, bool)
307 {
308  EditOption();
309  return true;
310 }
311 
313 {
314  const sal_Int32 nSelectPos = m_xLbEngine->get_active();
315  if ( nSelectPos < maImplNames.getLength() )
316  {
317  OUString aNewEngine( maImplNames[nSelectPos] );
318  if ( aNewEngine != maEngine )
319  {
320  maEngine = aNewEngine;
321  ReadFromComponent(); // fill maProperties from component (using maEngine)
322  FillListBox(); // using maProperties
323  }
324  }
325 }
326 
328 {
329  bool bCheckbox = false;
330 
331  int nEntry = m_xLbSettings->get_selected_index();
332  if (nEntry != -1)
333  {
334  ScSolverOptionsString* pStringItem = reinterpret_cast<ScSolverOptionsString*>(m_xLbSettings->get_id(nEntry).toInt64());
335  if (!pStringItem)
336  bCheckbox = true;
337  }
338 
339  m_xBtnEdit->set_sensitive(!bCheckbox);
340 }
341 
343  : GenericDialogController(pParent, "modules/scalc/ui/integerdialog.ui", "IntegerDialog")
344  , m_xFrame(m_xBuilder->weld_frame("frame"))
345  , m_xNfValue(m_xBuilder->weld_spin_button("value"))
346 {
347 }
348 
350 {
351 }
352 
353 void ScSolverIntegerDialog::SetOptionName( const OUString& rName )
354 {
355  m_xFrame->set_label(rName);
356 }
357 
358 void ScSolverIntegerDialog::SetValue( sal_Int32 nValue )
359 {
360  m_xNfValue->set_value( nValue );
361 }
362 
363 void ScSolverIntegerDialog::SetMax( sal_Int32 nMax )
364 {
365  m_xNfValue->set_range(0, nMax);
366 }
367 
369 {
370  return m_xNfValue->get_value();
371 }
372 
374  : GenericDialogController(pParent, "modules/scalc/ui/doubledialog.ui", "DoubleDialog")
375  , m_xFrame(m_xBuilder->weld_frame("frame"))
376  , m_xEdValue(m_xBuilder->weld_entry("value"))
377  , m_fMaxValue(std::numeric_limits<double>::quiet_NaN())
378 {
379 }
380 
382 {
383 }
384 
385 void ScSolverValueDialog::SetOptionName( const OUString& rName )
386 {
387  m_xFrame->set_label(rName);
388 }
389 
390 void ScSolverValueDialog::SetValue( double fValue )
391 {
392  m_xEdValue->set_text( rtl::math::doubleToUString( fValue,
393  rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
394  ScGlobal::getLocaleData().getNumDecimalSep()[0], true ) );
395 }
396 
398 {
399  m_fMaxValue = fMax;
400 }
401 
403 {
404  OUString aInput = m_xEdValue->get_text();
405 
406  rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok;
407  sal_Int32 nParseEnd = 0;
408  double fValue = ScGlobal::getLocaleData().stringToDouble( aInput, true, &eStatus, &nParseEnd);
409  /* TODO: shouldn't there be some error checking? */
410  if (!std::isnan(m_fMaxValue) && fValue > m_fMaxValue)
411  fValue = m_fMaxValue;
412  return fValue;
413 }
414 
415 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
virtual ~ScSolverValueDialog() override
std::shared_ptr< weld::Dialog > m_xDialog
ScSolverOptionsDialog(weld::Window *pParent, const css::uno::Sequence< OUString > &rImplNames, const css::uno::Sequence< OUString > &rDescriptions, const OUString &rEngine, const css::uno::Sequence< css::beans::PropertyValue > &rProperties)
void SetDoubleValue(double fNew)
void SetMax(sal_Int32 nValue)
const css::uno::Sequence< css::beans::PropertyValue > & GetProperties()
sal_Int32 GetIntValue() const
css::uno::Reference< css::lang::XComponent > m_xFrame
ScSolverIntegerDialog(weld::Window *pParent)
OUString Name
void SetIntValue(sal_Int32 nNew)
TRISTATE_TRUE
RET_CANCEL
std::unique_ptr< weld::Entry > m_xEdValue
double stringToDouble(const OUString &rString, bool bUseGroupSep, rtl_math_ConversionStatus *pStatus, sal_Int32 *pParseEnd) const
std::unique_ptr< weld::SpinButton > m_xNfValue
static bool runAsync(const std::shared_ptr< DialogController > &rController, const std::function< void(sal_Int32)> &)
static SC_DLLPUBLIC const LocaleDataWrapper & getLocaleData()
Definition: global.cxx:1004
int nCount
void SetValue(double fValue)
static css::uno::Sequence< css::beans::PropertyValue > GetDefaults(std::u16string_view rImplName)
Definition: solverutil.cxx:136
SvGenericNameContainerMapImpl maProperties
virtual ~ScSolverIntegerDialog() override
const OUString & getNumDecimalSep() const
css::uno::Sequence< OUString > maImplNames
ScSolverValueDialog(weld::Window *pParent)
void SetOptionName(const OUString &rName)
bool IsDouble() const
TRISTATE_FALSE
Object Value
OUString aPropName
std::shared_ptr< ScSolverValueDialog > m_xValDialog
double GetDoubleValue() const
bool operator<(const ScDPCollection::DBType &left, const ScDPCollection::DBType &right)
Definition: dpobject.cxx:3930
static SC_DLLPUBLIC CollatorWrapper & GetCollator()
case-insensitive collator
Definition: global.cxx:1044
void SetMax(double fValue)
std::unique_ptr< weld::Frame > m_xFrame
RET_OK
void SetValue(sal_Int32 nValue)
std::shared_ptr< ScSolverIntegerDialog > m_xIntDialog
virtual ~ScSolverOptionsDialog() override
std::unique_ptr< weld::Button > m_xBtnEdit
const OUString & GetText() const
css::uno::Sequence< css::beans::PropertyValue > maProperties
sal_Int32 GetValue() const
IMPL_LINK(ScSolverOptionsDialog, ButtonHdl, weld::Button &, rBtn, void)
std::unique_ptr< weld::Frame > m_xFrame
IMPL_LINK_NOARG(ScSolverOptionsDialog, SettingsDoubleClickHdl, weld::TreeView &, bool)
std::unique_ptr< weld::ComboBox > m_xLbEngine
static bool GetBoolFromAny(const css::uno::Any &aAny)
Definition: miscuno.cxx:138
double GetValue() const
void SetOptionName(const OUString &rName)
static css::uno::Reference< css::sheet::XSolver > GetSolver(std::u16string_view rImplName)
Definition: solverutil.cxx:98
std::vector< std::unique_ptr< ScSolverOptionsString > > m_aOptions
std::unique_ptr< weld::TreeView > m_xLbSettings
sal_uInt16 nPos