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 <rangelst.hxx>
21 #include <sfx2/bindings.hxx>
22 #include <svl/numformat.hxx>
24 #include <vcl/commandinfoprovider.hxx>
25 #include <vcl/weld.hxx>
26 #include <vcl/svapp.hxx>
28 #include <reffact.hxx>
30 #include <docfunc.hxx>
31 #include <rangeutl.hxx>
32 #include <convuno.hxx>
33 #include <unonames.hxx>
34 #include <solveroptions.hxx>
35 #include <solverutil.hxx>
36 #include <globstr.hrc>
37 #include <scresid.hxx>
38 #include <comphelper/sequence.hxx>
39 #include <optsolver.hxx>
41 #include <TableFillingAndNavigationTools.hxx>
43 #include <com/sun/star/beans/XPropertySetInfo.hpp>
44 #include <com/sun/star/sheet/SolverConstraint.hpp>
45 #include <com/sun/star/sheet/SolverConstraintOperator.hpp>
46 #include <com/sun/star/sheet/XSolverDescription.hpp>
47 #include <com/sun/star/sheet/XSolver.hpp>
48 #include <com/sun/star/sheet/SensitivityReport.hpp>
50 using namespace com::sun::star
;
52 ScSolverProgressDialog::ScSolverProgressDialog(weld::Window
* pParent
)
53 : GenericDialogController(pParent
, u
"modules/scalc/ui/solverprogressdialog.ui"_ustr
,
54 u
"SolverProgressDialog"_ustr
)
55 , m_xFtTime(m_xBuilder
->weld_label(u
"progress"_ustr
))
59 ScSolverProgressDialog::~ScSolverProgressDialog()
63 void ScSolverProgressDialog::HideTimeLimit()
68 void ScSolverProgressDialog::SetTimeLimit( sal_Int32 nSeconds
)
70 OUString aOld
= m_xFtTime
->get_label();
71 OUString aNew
= aOld
.replaceFirst("#", OUString::number(nSeconds
));
72 m_xFtTime
->set_label(aNew
);
75 ScSolverNoSolutionDialog::ScSolverNoSolutionDialog(weld::Window
* pParent
, const OUString
& rErrorText
)
76 : GenericDialogController(pParent
, u
"modules/scalc/ui/nosolutiondialog.ui"_ustr
, u
"NoSolutionDialog"_ustr
)
77 , m_xFtErrorText(m_xBuilder
->weld_label(u
"error"_ustr
))
79 m_xFtErrorText
->set_label(rErrorText
);
82 ScSolverNoSolutionDialog::~ScSolverNoSolutionDialog()
86 ScSolverSuccessDialog::ScSolverSuccessDialog(weld::Window
* pParent
, std::u16string_view rSolution
)
87 : GenericDialogController(pParent
, u
"modules/scalc/ui/solversuccessdialog.ui"_ustr
, u
"SolverSuccessDialog"_ustr
)
88 , m_xFtResult(m_xBuilder
->weld_label(u
"result"_ustr
))
89 , m_xBtnOk(m_xBuilder
->weld_button(u
"ok"_ustr
))
90 , m_xBtnCancel(m_xBuilder
->weld_button(u
"cancel"_ustr
))
92 m_xBtnOk
->connect_clicked(LINK(this, ScSolverSuccessDialog
, ClickHdl
));
93 m_xBtnCancel
->connect_clicked(LINK(this, ScSolverSuccessDialog
, ClickHdl
));
94 OUString aMessage
= m_xFtResult
->get_label() + " " + rSolution
;
95 m_xFtResult
->set_label(aMessage
);
98 ScSolverSuccessDialog::~ScSolverSuccessDialog()
102 IMPL_LINK(ScSolverSuccessDialog
, ClickHdl
, weld::Button
&, rBtn
, void)
104 if (&rBtn
== m_xBtnOk
.get())
105 m_xDialog
->response(RET_OK
);
107 m_xDialog
->response(RET_CANCEL
);
110 ScCursorRefEdit::ScCursorRefEdit(std::unique_ptr
<weld::Entry
> xControl
)
111 : formula::RefEdit(std::move(xControl
))
113 xEntry
->connect_key_press(Link
<const KeyEvent
&, bool>()); //acknowledge we first remove the old one
114 xEntry
->connect_key_press(LINK(this, ScCursorRefEdit
, KeyInputHdl
));
117 void ScCursorRefEdit::SetCursorLinks( const Link
<ScCursorRefEdit
&,void>& rUp
, const Link
<ScCursorRefEdit
&,void>& rDown
)
119 maCursorUpLink
= rUp
;
120 maCursorDownLink
= rDown
;
123 IMPL_LINK(ScCursorRefEdit
, KeyInputHdl
, const KeyEvent
&, rKEvt
, bool)
125 vcl::KeyCode aCode
= rKEvt
.GetKeyCode();
126 bool bUp
= (aCode
.GetCode() == KEY_UP
);
127 bool bDown
= (aCode
.GetCode() == KEY_DOWN
);
128 if ( !aCode
.IsShift() && !aCode
.IsMod1() && !aCode
.IsMod2() && ( bUp
|| bDown
) )
131 maCursorUpLink
.Call( *this );
133 maCursorDownLink
.Call( *this );
136 return formula::RefEdit::KeyInput(rKEvt
);
139 ScOptSolverDlg::ScOptSolverDlg(SfxBindings
* pB
, SfxChildWindow
* pCW
, weld::Window
* pParent
,
140 ScDocShell
* pDocSh
, const ScAddress
& aCursorPos
)
141 : ScAnyRefDlgController(pB
, pCW
, pParent
, u
"modules/scalc/ui/solverdlg.ui"_ustr
, u
"SolverDialog"_ustr
)
142 , maInputError(ScResId(STR_INVALIDINPUT
))
143 , maConditionError(ScResId(STR_INVALIDCONDITION
))
146 , mrDoc(pDocSh
->GetDocument())
147 , mnCurTab(aCursorPos
.Tab())
148 , mbDlgLostFocus(false)
150 , mpEdActive(nullptr)
151 , m_xFtObjectiveCell(m_xBuilder
->weld_label(u
"targetlabel"_ustr
))
152 , m_xEdObjectiveCell(new formula::RefEdit(m_xBuilder
->weld_entry(u
"targetedit"_ustr
)))
153 , m_xRBObjectiveCell(new formula::RefButton(m_xBuilder
->weld_button(u
"targetbutton"_ustr
)))
154 , m_xRbMax(m_xBuilder
->weld_radio_button(u
"max"_ustr
))
155 , m_xRbMin(m_xBuilder
->weld_radio_button(u
"min"_ustr
))
156 , m_xRbValue(m_xBuilder
->weld_radio_button(u
"value"_ustr
))
157 , m_xEdTargetValue(new formula::RefEdit(m_xBuilder
->weld_entry(u
"valueedit"_ustr
)))
158 , m_xRBTargetValue(new formula::RefButton(m_xBuilder
->weld_button(u
"valuebutton"_ustr
)))
159 , m_xFtVariableCells(m_xBuilder
->weld_label(u
"changelabel"_ustr
))
160 , m_xEdVariableCells(new formula::RefEdit(m_xBuilder
->weld_entry(u
"changeedit"_ustr
)))
161 , m_xRBVariableCells(new formula::RefButton(m_xBuilder
->weld_button(u
"changebutton"_ustr
)))
162 , m_xFtCellRef(m_xBuilder
->weld_label(u
"cellreflabel"_ustr
))
163 , m_xEdLeft1(new ScCursorRefEdit(m_xBuilder
->weld_entry(u
"ref1edit"_ustr
)))
164 , m_xRBLeft1(new formula::RefButton(m_xBuilder
->weld_button(u
"ref1button"_ustr
)))
165 , m_xLbOp1(m_xBuilder
->weld_combo_box(u
"op1list"_ustr
))
166 , m_xFtConstraint(m_xBuilder
->weld_label(u
"constraintlabel"_ustr
))
167 , m_xEdRight1(new ScCursorRefEdit(m_xBuilder
->weld_entry(u
"val1edit"_ustr
)))
168 , m_xRBRight1(new formula::RefButton(m_xBuilder
->weld_button(u
"val1button"_ustr
)))
169 , m_xBtnDel1(m_xBuilder
->weld_button(u
"del1"_ustr
))
170 , m_xEdLeft2(new ScCursorRefEdit(m_xBuilder
->weld_entry(u
"ref2edit"_ustr
)))
171 , m_xRBLeft2(new formula::RefButton(m_xBuilder
->weld_button(u
"ref2button"_ustr
)))
172 , m_xLbOp2(m_xBuilder
->weld_combo_box(u
"op2list"_ustr
))
173 , m_xEdRight2(new ScCursorRefEdit(m_xBuilder
->weld_entry(u
"val2edit"_ustr
)))
174 , m_xRBRight2(new formula::RefButton(m_xBuilder
->weld_button(u
"val2button"_ustr
)))
175 , m_xBtnDel2(m_xBuilder
->weld_button(u
"del2"_ustr
))
176 , m_xEdLeft3(new ScCursorRefEdit(m_xBuilder
->weld_entry(u
"ref3edit"_ustr
)))
177 , m_xRBLeft3(new formula::RefButton(m_xBuilder
->weld_button(u
"ref3button"_ustr
)))
178 , m_xLbOp3(m_xBuilder
->weld_combo_box(u
"op3list"_ustr
))
179 , m_xEdRight3(new ScCursorRefEdit(m_xBuilder
->weld_entry(u
"val3edit"_ustr
)))
180 , m_xRBRight3(new formula::RefButton(m_xBuilder
->weld_button(u
"val3button"_ustr
)))
181 , m_xBtnDel3(m_xBuilder
->weld_button(u
"del3"_ustr
))
182 , m_xEdLeft4(new ScCursorRefEdit(m_xBuilder
->weld_entry(u
"ref4edit"_ustr
)))
183 , m_xRBLeft4(new formula::RefButton(m_xBuilder
->weld_button(u
"ref4button"_ustr
)))
184 , m_xLbOp4(m_xBuilder
->weld_combo_box(u
"op4list"_ustr
))
185 , m_xEdRight4(new ScCursorRefEdit(m_xBuilder
->weld_entry(u
"val4edit"_ustr
)))
186 , m_xRBRight4(new formula::RefButton(m_xBuilder
->weld_button(u
"val4button"_ustr
)))
187 , m_xBtnDel4(m_xBuilder
->weld_button(u
"del4"_ustr
))
188 , m_xScrollBar(m_xBuilder
->weld_scrolled_window(u
"scrollbar"_ustr
, true))
189 , m_xBtnOpt(m_xBuilder
->weld_button(u
"options"_ustr
))
190 , m_xBtnClose(m_xBuilder
->weld_button(u
"close"_ustr
))
191 , m_xBtnSolve(m_xBuilder
->weld_button(u
"ok"_ustr
))
192 , m_xBtnResetAll(m_xBuilder
->weld_button(u
"resetall"_ustr
))
193 , m_xResultFT(m_xBuilder
->weld_label(u
"result"_ustr
))
194 , m_xContents(m_xBuilder
->weld_widget(u
"grid"_ustr
))
195 , m_pSolverSettings(mrDoc
.FetchTable(mnCurTab
)->GetSolverSettings())
197 m_xEdObjectiveCell
->SetReferences(this, m_xFtObjectiveCell
.get());
198 m_xRBObjectiveCell
->SetReferences(this, m_xEdObjectiveCell
.get());
199 m_xEdTargetValue
->SetReferences(this, m_xResultFT
.get());
200 m_xRBTargetValue
->SetReferences(this, m_xEdTargetValue
.get());
201 m_xEdVariableCells
->SetReferences(this, m_xFtVariableCells
.get());
202 m_xRBVariableCells
->SetReferences(this, m_xEdVariableCells
.get());
203 m_xEdLeft1
->SetReferences(this, m_xFtCellRef
.get());
204 m_xRBLeft1
->SetReferences(this, m_xEdLeft1
.get());
205 m_xEdRight1
->SetReferences(this, m_xFtConstraint
.get());
206 m_xRBRight1
->SetReferences(this, m_xEdRight1
.get());
207 m_xEdLeft2
->SetReferences(this, m_xFtCellRef
.get());
208 m_xRBLeft2
->SetReferences(this, m_xEdLeft2
.get());
209 m_xEdRight2
->SetReferences(this, m_xFtConstraint
.get());
210 m_xRBRight2
->SetReferences(this, m_xEdRight2
.get());
211 m_xEdLeft3
->SetReferences(this, m_xFtCellRef
.get());
212 m_xRBLeft3
->SetReferences(this, m_xEdLeft3
.get());
213 m_xEdRight3
->SetReferences(this, m_xFtConstraint
.get());
214 m_xRBRight3
->SetReferences(this, m_xEdRight3
.get());
215 m_xEdLeft4
->SetReferences(this, m_xFtCellRef
.get());
216 m_xRBLeft4
->SetReferences(this, m_xEdLeft4
.get());
217 m_xEdRight4
->SetReferences(this, m_xFtConstraint
.get());
218 m_xRBRight4
->SetReferences(this, m_xEdRight4
.get());
220 mpLeftEdit
[0] = m_xEdLeft1
.get();
221 mpLeftButton
[0] = m_xRBLeft1
.get();
222 mpRightEdit
[0] = m_xEdRight1
.get();
223 mpRightButton
[0] = m_xRBRight1
.get();
224 mpOperator
[0] = m_xLbOp1
.get();
225 mpDelButton
[0] = m_xBtnDel1
.get();
227 mpLeftEdit
[1] = m_xEdLeft2
.get();
228 mpLeftButton
[1] = m_xRBLeft2
.get();
229 mpRightEdit
[1] = m_xEdRight2
.get();
230 mpRightButton
[1] = m_xRBRight2
.get();
231 mpOperator
[1] = m_xLbOp2
.get();
232 mpDelButton
[1] = m_xBtnDel2
.get();
234 mpLeftEdit
[2] = m_xEdLeft3
.get();
235 mpLeftButton
[2] = m_xRBLeft3
.get();
236 mpRightEdit
[2] = m_xEdRight3
.get();
237 mpRightButton
[2] = m_xRBRight3
.get();
238 mpOperator
[2] = m_xLbOp3
.get();
239 mpDelButton
[2] = m_xBtnDel3
.get();
241 mpLeftEdit
[3] = m_xEdLeft4
.get();
242 mpLeftButton
[3] = m_xRBLeft4
.get();
243 mpRightEdit
[3] = m_xEdRight4
.get();
244 mpRightButton
[3] = m_xRBRight4
.get();
245 mpOperator
[3] = m_xLbOp4
.get();
246 mpDelButton
[3] = m_xBtnDel4
.get();
251 ScOptSolverDlg::~ScOptSolverDlg()
255 void ScOptSolverDlg::Init(const ScAddress
& rCursorPos
)
257 uno::Reference
<frame::XFrame
> xFrame
= GetBindings().GetActiveFrame();
258 auto xDelNm
= vcl::CommandInfoProvider::GetXGraphicForCommand(u
".uno:DeleteRows"_ustr
, xFrame
);
259 for (weld::Button
* pButton
: mpDelButton
)
260 pButton
->set_image(xDelNm
);
262 m_xBtnOpt
->connect_clicked( LINK( this, ScOptSolverDlg
, BtnHdl
) );
263 m_xBtnClose
->connect_clicked( LINK( this, ScOptSolverDlg
, BtnHdl
) );
264 m_xBtnSolve
->connect_clicked( LINK( this, ScOptSolverDlg
, BtnHdl
) );
265 m_xBtnResetAll
->connect_clicked( LINK( this, ScOptSolverDlg
, BtnHdl
) );
267 Link
<formula::RefEdit
&,void> aEditLink
= LINK( this, ScOptSolverDlg
, GetEditFocusHdl
);
268 Link
<formula::RefButton
&,void> aButtonLink
= LINK( this, ScOptSolverDlg
, GetButtonFocusHdl
);
269 m_xEdObjectiveCell
->SetGetFocusHdl( aEditLink
);
270 m_xRBObjectiveCell
->SetGetFocusHdl( aButtonLink
);
271 m_xEdTargetValue
->SetGetFocusHdl( aEditLink
);
272 m_xRBTargetValue
->SetGetFocusHdl( aButtonLink
);
273 m_xEdVariableCells
->SetGetFocusHdl( aEditLink
);
274 m_xRBVariableCells
->SetGetFocusHdl( aButtonLink
);
275 Link
<weld::Widget
&,void> aLink
= LINK(this, ScOptSolverDlg
, GetFocusHdl
);
276 m_xRbValue
->connect_focus_in(aLink
);
277 for ( sal_uInt16 nRow
= 0; nRow
< EDIT_ROW_COUNT
; ++nRow
)
279 mpLeftEdit
[nRow
]->SetGetFocusHdl( aEditLink
);
280 mpLeftButton
[nRow
]->SetGetFocusHdl( aButtonLink
);
281 mpRightEdit
[nRow
]->SetGetFocusHdl( aEditLink
);
282 mpRightButton
[nRow
]->SetGetFocusHdl( aButtonLink
);
283 mpOperator
[nRow
]->connect_focus_in(aLink
);
286 aEditLink
= LINK( this, ScOptSolverDlg
, LoseEditFocusHdl
);
287 aButtonLink
= LINK( this, ScOptSolverDlg
, LoseButtonFocusHdl
);
288 m_xEdObjectiveCell
->SetLoseFocusHdl( aEditLink
);
289 m_xRBObjectiveCell
->SetLoseFocusHdl( aButtonLink
);
290 m_xEdTargetValue
->SetLoseFocusHdl( aEditLink
);
291 m_xRBTargetValue
-> SetLoseFocusHdl( aButtonLink
);
292 m_xEdVariableCells
->SetLoseFocusHdl( aEditLink
);
293 m_xRBVariableCells
->SetLoseFocusHdl( aButtonLink
);
294 for ( sal_uInt16 nRow
= 0; nRow
< EDIT_ROW_COUNT
; ++nRow
)
296 mpLeftEdit
[nRow
]->SetLoseFocusHdl( aEditLink
);
297 mpLeftButton
[nRow
]->SetLoseFocusHdl( aButtonLink
);
298 mpRightEdit
[nRow
]->SetLoseFocusHdl( aEditLink
);
299 mpRightButton
[nRow
]->SetLoseFocusHdl( aButtonLink
);
302 Link
<ScCursorRefEdit
&,void> aCursorUp
= LINK( this, ScOptSolverDlg
, CursorUpHdl
);
303 Link
<ScCursorRefEdit
&,void> aCursorDown
= LINK( this, ScOptSolverDlg
, CursorDownHdl
);
304 Link
<formula::RefEdit
&,void> aCondModify
= LINK( this, ScOptSolverDlg
, CondModifyHdl
);
305 for ( sal_uInt16 nRow
= 0; nRow
< EDIT_ROW_COUNT
; ++nRow
)
307 mpLeftEdit
[nRow
]->SetCursorLinks( aCursorUp
, aCursorDown
);
308 mpRightEdit
[nRow
]->SetCursorLinks( aCursorUp
, aCursorDown
);
309 mpLeftEdit
[nRow
]->SetModifyHdl( aCondModify
);
310 mpRightEdit
[nRow
]->SetModifyHdl( aCondModify
);
311 mpDelButton
[nRow
]->connect_clicked( LINK( this, ScOptSolverDlg
, DelBtnHdl
) );
312 mpOperator
[nRow
]->connect_changed( LINK( this, ScOptSolverDlg
, SelectHdl
) );
314 m_xEdTargetValue
->SetModifyHdl( LINK( this, ScOptSolverDlg
, TargetModifyHdl
) );
316 Size
aSize(m_xContents
->get_preferred_size());
317 m_xContents
->set_size_request(aSize
.Width(), aSize
.Height());
318 m_xScrollBar
->connect_vadjustment_changed( LINK( this, ScOptSolverDlg
, ScrollHdl
) );
320 m_xScrollBar
->vadjustment_set_page_increment( EDIT_ROW_COUNT
);
321 m_xScrollBar
->vadjustment_set_page_size( EDIT_ROW_COUNT
);
322 // Range is set in ShowConditions
324 // get available solver implementations
325 //! sort by descriptions?
326 ScSolverUtil::GetImplementations( maImplNames
, maDescriptions
);
328 // Load existing settings stored in the tab
329 LoadSolverSettings();
332 // If no objective cell has been loaded, then use the selected cell
333 if (m_xEdObjectiveCell
->GetText().isEmpty())
336 if (!mrDoc
.GetRangeAtBlock(ScRange(rCursorPos
), aCursorStr
))
337 aCursorStr
= rCursorPos
.Format(ScRefFlags::ADDR_ABS
, nullptr, mrDoc
.GetAddressConvention());
338 m_xEdObjectiveCell
->SetRefString(aCursorStr
);
341 m_xEdObjectiveCell
->GrabFocus();
342 mpEdActive
= m_xEdObjectiveCell
.get();
345 void ScOptSolverDlg::ReadConditions()
347 for ( sal_uInt16 nRow
= 0; nRow
< EDIT_ROW_COUNT
; ++nRow
)
349 sc::ModelConstraint aRowEntry
;
350 aRowEntry
.aLeftStr
= mpLeftEdit
[nRow
]->GetText();
351 aRowEntry
.aRightStr
= mpRightEdit
[nRow
]->GetText();
352 aRowEntry
.nOperator
= OperatorIndexToConstraintOperator(mpOperator
[nRow
]->get_active());
354 tools::Long nVecPos
= nScrollPos
+ nRow
;
355 if ( nVecPos
>= static_cast<tools::Long
>(m_aConditions
.size()) && !aRowEntry
.IsDefault() )
356 m_aConditions
.resize( nVecPos
+ 1 );
358 if ( nVecPos
< static_cast<tools::Long
>(m_aConditions
.size()) )
359 m_aConditions
[nVecPos
] = std::move(aRowEntry
);
361 // remove default entries at the end
362 size_t nSize
= m_aConditions
.size();
363 while ( nSize
> 0 && m_aConditions
[ nSize
-1 ].IsDefault() )
365 m_aConditions
.resize( nSize
);
369 void ScOptSolverDlg::ShowConditions()
371 for ( sal_uInt16 nRow
= 0; nRow
< EDIT_ROW_COUNT
; ++nRow
)
373 sc::ModelConstraint aRowEntry
;
375 tools::Long nVecPos
= nScrollPos
+ nRow
;
376 if ( nVecPos
< static_cast<tools::Long
>(m_aConditions
.size()) )
377 aRowEntry
= m_aConditions
[nVecPos
];
379 mpLeftEdit
[nRow
]->SetRefString( aRowEntry
.aLeftStr
);
380 mpRightEdit
[nRow
]->SetRefString( aRowEntry
.aRightStr
);
381 mpOperator
[nRow
]->set_active( aRowEntry
.nOperator
- 1);
384 // allow to scroll one page behind the visible or stored rows
385 tools::Long nVisible
= nScrollPos
+ EDIT_ROW_COUNT
;
386 tools::Long nMax
= std::max( nVisible
, static_cast<tools::Long
>(m_aConditions
.size()) );
387 m_xScrollBar
->vadjustment_configure(nScrollPos
, 0, nMax
+ EDIT_ROW_COUNT
, 1,
388 EDIT_ROW_COUNT
- 1, EDIT_ROW_COUNT
);
393 void ScOptSolverDlg::EnableButtons()
395 for ( sal_uInt16 nRow
= 0; nRow
< EDIT_ROW_COUNT
; ++nRow
)
397 tools::Long nVecPos
= nScrollPos
+ nRow
;
398 mpDelButton
[nRow
]->set_sensitive(nVecPos
< static_cast<tools::Long
>(m_aConditions
.size()));
402 void ScOptSolverDlg::Close()
405 m_xOptDlg
->response(RET_CANCEL
);
407 DoClose( ScOptSolverDlgWrapper::GetChildWindowId() );
410 void ScOptSolverDlg::SetActive()
412 if ( mbDlgLostFocus
)
414 mbDlgLostFocus
= false;
416 mpEdActive
->GrabFocus();
420 m_xDialog
->grab_focus();
425 void ScOptSolverDlg::SetReference( const ScRange
& rRef
, ScDocument
& rDocP
)
430 if ( rRef
.aStart
!= rRef
.aEnd
)
431 RefInputStart(mpEdActive
);
433 // "target"/"value": single cell
434 bool bSingle
= ( mpEdActive
== m_xEdObjectiveCell
.get() || mpEdActive
== m_xEdTargetValue
.get() );
437 ScAddress aAdr
= rRef
.aStart
;
438 ScRange
aNewRef( rRef
);
443 if ( rDocP
.GetRangeAtBlock( aNewRef
, aName
) ) // named range: show name
445 else // format cell/range reference
447 ScRefFlags nFmt
= ( aAdr
.Tab() == mnCurTab
) ? ScRefFlags::ADDR_ABS
: ScRefFlags::ADDR_ABS_3D
;
449 aStr
= aAdr
.Format(nFmt
, &rDocP
, rDocP
.GetAddressConvention());
451 aStr
= rRef
.Format(rDocP
, nFmt
| ScRefFlags::RANGE_ABS
, rDocP
.GetAddressConvention());
454 // variable cells can be several ranges, so only the selection is replaced
455 if ( mpEdActive
== m_xEdVariableCells
.get() )
457 OUString aVal
= mpEdActive
->GetText();
458 Selection aSel
= mpEdActive
->GetSelection();
460 aVal
= aVal
.replaceAt( aSel
.Min(), aSel
.Len(), aStr
);
461 Selection
aNewSel( aSel
.Min(), aSel
.Min()+aStr
.getLength() );
462 mpEdActive
->SetRefString( aVal
);
463 mpEdActive
->SetSelection( aNewSel
);
466 mpEdActive
->SetRefString( aStr
);
471 // select "Value of" if a ref is input into "target" edit
472 if ( mpEdActive
== m_xEdTargetValue
.get() )
473 m_xRbValue
->set_active(true);
476 bool ScOptSolverDlg::IsRefInputMode() const
478 return mpEdActive
!= nullptr;
481 // Loads solver settings into the dialog
482 void ScOptSolverDlg::LoadSolverSettings()
484 m_xEdObjectiveCell
->SetRefString(m_pSolverSettings
->GetParameter(sc::SP_OBJ_CELL
));
485 m_xEdTargetValue
->SetRefString(m_pSolverSettings
->GetParameter(sc::SP_OBJ_VAL
));
486 m_xEdVariableCells
->SetRefString(m_pSolverSettings
->GetParameter(sc::SP_VAR_CELLS
));
489 sc::ObjectiveType eType
= m_pSolverSettings
->GetObjectiveType();
492 case sc::OT_MAXIMIZE
: m_xRbMax
->set_active(true); break;
493 case sc::OT_MINIMIZE
: m_xRbMin
->set_active(true); break;
494 case sc::OT_VALUE
: m_xRbValue
->set_active(true); break;
498 m_aConditions
= m_pSolverSettings
->GetConstraints();
500 // Loads solver engine name
501 // If the solver engine in the current settings are not supported, use the first available
502 maEngine
= m_pSolverSettings
->GetParameter(sc::SP_LO_ENGINE
);
503 if (!IsEngineAvailable(maEngine
))
505 maEngine
= maImplNames
[0];
506 m_pSolverSettings
->SetParameter(sc::SP_LO_ENGINE
, maEngine
);
509 // Query current engine options
510 maProperties
= ScSolverUtil::GetDefaults(maEngine
);
511 m_pSolverSettings
->GetEngineOptions(maProperties
);
514 // Set solver settings and save them to the file
515 // But first, checks if the settings have changed
516 void ScOptSolverDlg::SaveSolverSettings()
518 // tdf#160104 If file does not have a solver model and the Solver dialog is set to its
519 // default initial values (maximize is selected; no variable cells; no target value
520 // and no constraints defined) then nothing needs to be saved
521 if (!m_pSolverSettings
->TabHasSolverModel() && m_xRbMax
->get_active()
522 && m_xEdTargetValue
->GetText().isEmpty() && m_xEdVariableCells
->GetText().isEmpty()
523 && m_aConditions
.size() == 0)
526 // The current tab has a model; now we need to determined if it has been modified
527 bool bModified
= false;
529 // Check objective cell, objective value and variable cells
530 if (m_pSolverSettings
->GetParameter(sc::SP_OBJ_CELL
) != m_xEdObjectiveCell
->GetText()
531 || m_pSolverSettings
->GetParameter(sc::SP_OBJ_VAL
) != m_xEdTargetValue
->GetText()
532 || m_pSolverSettings
->GetParameter(sc::SP_VAR_CELLS
) != m_xEdVariableCells
->GetText())
535 // Check selected objective type and save it if changed
536 sc::ObjectiveType aType
= sc::OT_MAXIMIZE
;
537 if (m_xRbMin
->get_active())
538 aType
= sc::OT_MINIMIZE
;
539 else if (m_xRbValue
->get_active())
540 aType
= sc::OT_VALUE
;
542 if (m_pSolverSettings
->GetObjectiveType() != aType
)
545 // Check if model constraints changed
546 std::vector
<sc::ModelConstraint
> vCurConditions
= m_pSolverSettings
->GetConstraints();
547 if (!bModified
&& vCurConditions
.size() != m_aConditions
.size())
551 // Here the size of both vectors is the same
552 // Now it needs to check the contents of the constraints
553 for (size_t i
= 0; i
< vCurConditions
.size(); i
++)
555 if (vCurConditions
[i
].aLeftStr
!= m_aConditions
[i
].aLeftStr
556 || vCurConditions
[i
].nOperator
!= m_aConditions
[i
].nOperator
557 || vCurConditions
[i
].aRightStr
!= m_aConditions
[i
].aRightStr
)
565 // Check if the solver engine name and its options have changed
566 if (m_pSolverSettings
->GetParameter(sc::SP_LO_ENGINE
) != maEngine
)
572 // The solver engine hasn't changed, so we need to check if engine options changed
573 // Query current engine options; here we start by creating a copy of maProperties
574 // to ensure the order is the same
575 css::uno::Sequence
<css::beans::PropertyValue
> vCurOptions(maProperties
);
576 m_pSolverSettings
->GetEngineOptions(vCurOptions
);
578 for (sal_Int32 i
= 0; i
< vCurOptions
.getLength(); i
++)
580 if (vCurOptions
[i
].Value
!= maProperties
[i
].Value
)
588 // Effectively save settings to file if modifications were made
591 m_pSolverSettings
->SetParameter(sc::SP_OBJ_CELL
, m_xEdObjectiveCell
->GetText());
592 m_pSolverSettings
->SetParameter(sc::SP_OBJ_VAL
, m_xEdTargetValue
->GetText());
593 m_pSolverSettings
->SetParameter(sc::SP_VAR_CELLS
, m_xEdVariableCells
->GetText());
594 m_pSolverSettings
->SetObjectiveType(aType
);
595 m_pSolverSettings
->SetConstraints(m_aConditions
);
596 m_pSolverSettings
->SetParameter(sc::SP_LO_ENGINE
, maEngine
);
597 m_pSolverSettings
->SetEngineOptions(maProperties
);
598 m_pSolverSettings
->SaveSolverSettings();
601 // Test if a LO engine implementation exists
602 bool ScOptSolverDlg::IsEngineAvailable(std::u16string_view sEngineName
)
604 auto nIndex
= comphelper::findValue(maImplNames
, sEngineName
);
610 IMPL_LINK(ScOptSolverDlg
, BtnHdl
, weld::Button
&, rBtn
, void)
612 auto xKeepAlive
= shared_from_this();
613 if (&rBtn
== m_xBtnSolve
.get() || &rBtn
== m_xBtnClose
.get())
615 bool bSolve
= ( &rBtn
== m_xBtnSolve
.get() );
617 SetDispatcherLock( false );
622 bClose
= CallSolver();
626 // Close: write dialog settings to DocShell for subsequent calls
628 SaveSolverSettings();
633 // no solution -> dialog is kept open
634 SetDispatcherLock( true );
637 else if (&rBtn
== m_xBtnOpt
.get())
639 //! move options dialog to UI lib?
640 m_xOptDlg
= std::make_shared
<ScSolverOptionsDialog
>(m_xDialog
.get(), maImplNames
, maDescriptions
, maEngine
, maProperties
);
641 weld::DialogController::runAsync(m_xOptDlg
, [this](sal_Int32 nResult
){
642 if (nResult
== RET_OK
)
644 maEngine
= m_xOptDlg
->GetEngine();
645 maProperties
= m_xOptDlg
->GetProperties();
650 else if (&rBtn
== m_xBtnResetAll
.get())
653 m_xEdObjectiveCell
->SetText(sEmpty
);
654 m_xEdTargetValue
->SetText(sEmpty
);
655 m_xEdVariableCells
->SetText(sEmpty
);
657 // Get default property values of solver implementations
658 maEngine
= maImplNames
[0];
659 maProperties
= ScSolverUtil::GetDefaults( maEngine
);
661 // Clear all conditions (Constraints)
662 m_aConditions
.clear();
665 m_xRbMax
->set_active(true);
666 m_xEdObjectiveCell
->GrabFocus();
667 mpEdActive
= m_xEdObjectiveCell
.get();
671 IMPL_LINK( ScOptSolverDlg
, GetEditFocusHdl
, formula::RefEdit
&, rCtrl
, void )
673 formula::RefEdit
* pEdit
= nullptr;
674 mpEdActive
= nullptr;
676 if( &rCtrl
== m_xEdObjectiveCell
.get() )
677 pEdit
= mpEdActive
= m_xEdObjectiveCell
.get();
678 else if( &rCtrl
== m_xEdTargetValue
.get() )
679 pEdit
= mpEdActive
= m_xEdTargetValue
.get();
680 else if( &rCtrl
== m_xEdVariableCells
.get() )
681 pEdit
= mpEdActive
= m_xEdVariableCells
.get();
682 for ( sal_uInt16 nRow
= 0; nRow
< EDIT_ROW_COUNT
; ++nRow
)
684 if( &rCtrl
== mpLeftEdit
[nRow
] )
685 pEdit
= mpEdActive
= mpLeftEdit
[nRow
];
686 else if( &rCtrl
== mpRightEdit
[nRow
] )
687 pEdit
= mpEdActive
= mpRightEdit
[nRow
];
694 IMPL_LINK( ScOptSolverDlg
, GetButtonFocusHdl
, formula::RefButton
&, rCtrl
, void )
696 formula::RefEdit
* pEdit
= nullptr;
697 mpEdActive
= nullptr;
699 if( &rCtrl
== m_xRBObjectiveCell
.get() )
700 pEdit
= mpEdActive
= m_xEdObjectiveCell
.get();
701 else if( &rCtrl
== m_xRBTargetValue
.get() )
702 pEdit
= mpEdActive
= m_xEdTargetValue
.get();
703 else if( &rCtrl
== m_xRBVariableCells
.get() )
704 pEdit
= mpEdActive
= m_xEdVariableCells
.get();
705 for ( sal_uInt16 nRow
= 0; nRow
< EDIT_ROW_COUNT
; ++nRow
)
707 if( &rCtrl
== mpLeftButton
[nRow
] )
708 pEdit
= mpEdActive
= mpLeftEdit
[nRow
];
709 else if( &rCtrl
== mpRightButton
[nRow
] )
710 pEdit
= mpEdActive
= mpRightEdit
[nRow
];
718 IMPL_LINK(ScOptSolverDlg
, GetFocusHdl
, weld::Widget
&, rCtrl
, void)
720 if( &rCtrl
== m_xRbValue
.get() ) // focus on "Value of" radio button
721 mpEdActive
= m_xEdTargetValue
.get(); // use value edit for ref input, but don't change selection
724 for ( sal_uInt16 nRow
= 0; nRow
< EDIT_ROW_COUNT
; ++nRow
)
726 if( &rCtrl
== mpOperator
[nRow
] ) // focus on "operator" list box
727 mpEdActive
= mpRightEdit
[nRow
]; // use right edit for ref input, but don't change selection
732 IMPL_LINK_NOARG(ScOptSolverDlg
, LoseEditFocusHdl
, formula::RefEdit
&, void)
734 mbDlgLostFocus
= !m_xDialog
->has_toplevel_focus();
737 IMPL_LINK_NOARG(ScOptSolverDlg
, LoseButtonFocusHdl
, formula::RefButton
&, void)
739 mbDlgLostFocus
= !m_xDialog
->has_toplevel_focus();
742 IMPL_LINK(ScOptSolverDlg
, DelBtnHdl
, weld::Button
&, rBtn
, void)
744 for ( sal_uInt16 nRow
= 0; nRow
< EDIT_ROW_COUNT
; ++nRow
)
745 if (&rBtn
== mpDelButton
[nRow
])
747 bool bHadFocus
= rBtn
.has_focus();
750 tools::Long nVecPos
= nScrollPos
+ nRow
;
751 if ( nVecPos
< static_cast<tools::Long
>(m_aConditions
.size()) )
753 m_aConditions
.erase( m_aConditions
.begin() + nVecPos
);
756 if ( bHadFocus
&& !rBtn
.get_sensitive() )
758 // If the button is disabled, focus would normally move to the next control,
759 // (left edit of the next row). Move it to left edit of this row instead.
761 mpEdActive
= mpLeftEdit
[nRow
];
762 mpEdActive
->GrabFocus();
768 IMPL_LINK_NOARG(ScOptSolverDlg
, TargetModifyHdl
, formula::RefEdit
&, void)
770 // modify handler for the target edit:
771 // select "Value of" if something is input into the edit
772 if ( !m_xEdTargetValue
->GetText().isEmpty() )
773 m_xRbValue
->set_active(true);
776 IMPL_LINK_NOARG(ScOptSolverDlg
, CondModifyHdl
, formula::RefEdit
&, void)
778 // modify handler for the condition edits, just to enable/disable "delete" buttons
783 IMPL_LINK_NOARG(ScOptSolverDlg
, SelectHdl
, weld::ComboBox
&, void)
785 // select handler for operator list boxes, just to enable/disable "delete" buttons
790 IMPL_LINK_NOARG(ScOptSolverDlg
, ScrollHdl
, weld::ScrolledWindow
&, void)
793 nScrollPos
= m_xScrollBar
->vadjustment_get_value();
796 mpEdActive
->SelectAll();
799 IMPL_LINK( ScOptSolverDlg
, CursorUpHdl
, ScCursorRefEdit
&, rEdit
, void )
801 if ( &rEdit
== mpLeftEdit
[0] || &rEdit
== mpRightEdit
[0] )
803 if ( nScrollPos
> 0 )
809 mpEdActive
->SelectAll();
814 formula::RefEdit
* pFocus
= nullptr;
815 for ( sal_uInt16 nRow
= 1; nRow
< EDIT_ROW_COUNT
; ++nRow
) // second row or below: move focus
817 if ( &rEdit
== mpLeftEdit
[nRow
] )
818 pFocus
= mpLeftEdit
[nRow
-1];
819 else if ( &rEdit
== mpRightEdit
[nRow
] )
820 pFocus
= mpRightEdit
[nRow
-1];
830 IMPL_LINK( ScOptSolverDlg
, CursorDownHdl
, ScCursorRefEdit
&, rEdit
, void )
832 if ( &rEdit
== mpLeftEdit
[EDIT_ROW_COUNT
-1] || &rEdit
== mpRightEdit
[EDIT_ROW_COUNT
-1] )
834 //! limit scroll position?
839 mpEdActive
->SelectAll();
843 formula::RefEdit
* pFocus
= nullptr;
844 for ( sal_uInt16 nRow
= 0; nRow
+1 < EDIT_ROW_COUNT
; ++nRow
) // before last row: move focus
846 if ( &rEdit
== mpLeftEdit
[nRow
] )
847 pFocus
= mpLeftEdit
[nRow
+1];
848 else if ( &rEdit
== mpRightEdit
[nRow
] )
849 pFocus
= mpRightEdit
[nRow
+1];
859 // Converts the position of the operator in the dropdown menu to a ConstraintOperator type
860 sc::ConstraintOperator
ScOptSolverDlg::OperatorIndexToConstraintOperator(sal_Int32 nIndex
)
864 case 0 : return sc::CO_LESS_EQUAL
; break;
865 case 1 : return sc::CO_EQUAL
; break;
866 case 2 : return sc::CO_GREATER_EQUAL
; break;
867 case 3 : return sc::CO_INTEGER
; break;
868 case 4 : return sc::CO_BINARY
; break;
869 default : return sc::CO_LESS_EQUAL
; break;
873 void ScOptSolverDlg::ShowError( bool bCondition
, formula::RefEdit
* pFocus
)
875 OUString aMessage
= bCondition
? maConditionError
: maInputError
;
876 std::unique_ptr
<weld::MessageDialog
> xBox(Application::CreateMessageDialog(m_xDialog
.get(),
877 VclMessageType::Warning
, VclButtonsType::Ok
,
887 bool ScOptSolverDlg::ParseRef( ScRange
& rRange
, const OUString
& rInput
, bool bAllowRange
)
889 ScAddress::Details
aDetails(mrDoc
.GetAddressConvention(), 0, 0);
890 ScRefFlags nFlags
= rRange
.ParseAny( rInput
, mrDoc
, aDetails
);
891 if ( nFlags
& ScRefFlags::VALID
)
893 if ( (nFlags
& ScRefFlags::TAB_3D
) == ScRefFlags::ZERO
)
894 rRange
.aStart
.SetTab( mnCurTab
);
895 if ( (nFlags
& ScRefFlags::TAB2_3D
) == ScRefFlags::ZERO
)
896 rRange
.aEnd
.SetTab( rRange
.aStart
.Tab() );
897 return ( bAllowRange
|| rRange
.aStart
== rRange
.aEnd
);
899 else if ( ScRangeUtil::MakeRangeFromName( rInput
, mrDoc
, mnCurTab
, rRange
, RUTL_NAMES
, aDetails
) )
900 return ( bAllowRange
|| rRange
.aStart
== rRange
.aEnd
);
902 return false; // not recognized
905 bool ScOptSolverDlg::FindTimeout( sal_Int32
& rTimeout
)
909 if ( !maProperties
.hasElements() )
910 maProperties
= ScSolverUtil::GetDefaults( maEngine
); // get property defaults from component
912 sal_Int32 nPropCount
= maProperties
.getLength();
913 for (sal_Int32 nProp
=0; nProp
<nPropCount
&& !bFound
; ++nProp
)
915 const beans::PropertyValue
& rValue
= maProperties
[nProp
];
916 if ( rValue
.Name
== SC_UNONAME_TIMEOUT
)
917 bFound
= ( rValue
.Value
>>= rTimeout
);
922 OUString
ScOptSolverDlg::GetCellStrAddress(css::table::CellAddress aUnoAddress
)
925 ScUnoConversion::FillScAddress(aScAddr
, aUnoAddress
);
926 ScRange
aRange(aScAddr
);
927 return aRange
.Format(mrDoc
, ScRefFlags::RANGE_ABS
);
930 bool ScOptSolverDlg::CallSolver() // return true -> close dialog after calling
932 // show progress dialog
934 auto xProgress
= std::make_shared
<ScSolverProgressDialog
>(m_xDialog
.get());
935 sal_Int32 nTimeout
= 0;
936 if ( FindTimeout( nTimeout
) )
937 xProgress
->SetTimeLimit( nTimeout
);
939 xProgress
->HideTimeLimit();
941 weld::DialogController::runAsync(xProgress
, [](sal_Int32
/*nResult*/){});
943 // try to make sure the progress dialog is painted before continuing
944 Application::Reschedule(true);
946 // collect solver parameters
950 rtl::Reference
<ScModelObj
> xDocument( mpDocShell
->GetModel() );
953 if ( !ParseRef( aObjRange
, m_xEdObjectiveCell
->GetText(), false ) )
955 ShowError( false, m_xEdObjectiveCell
.get() );
958 table::CellAddress
aObjective( aObjRange
.aStart
.Tab(), aObjRange
.aStart
.Col(), aObjRange
.aStart
.Row() );
960 // "changing cells" can be several ranges
961 ScRangeList aVarRanges
;
962 if ( !ParseWithNames( aVarRanges
, m_xEdVariableCells
->GetText(), mrDoc
) )
964 ShowError( false, m_xEdVariableCells
.get() );
967 uno::Sequence
<table::CellAddress
> aVariables
;
968 sal_Int32 nVarPos
= 0;
970 for ( size_t nRangePos
=0, nRange
= aVarRanges
.size(); nRangePos
< nRange
; ++nRangePos
)
972 ScRange
aRange( aVarRanges
[ nRangePos
] );
974 SCTAB nTab
= aRange
.aStart
.Tab();
976 // resolve into single cells
978 sal_Int32 nAdd
= ( aRange
.aEnd
.Col() - aRange
.aStart
.Col() + 1 ) *
979 ( aRange
.aEnd
.Row() - aRange
.aStart
.Row() + 1 );
980 aVariables
.realloc( nVarPos
+ nAdd
);
981 auto pVariables
= aVariables
.getArray();
983 for (SCROW nRow
= aRange
.aStart
.Row(); nRow
<= aRange
.aEnd
.Row(); ++nRow
)
984 for (SCCOL nCol
= aRange
.aStart
.Col(); nCol
<= aRange
.aEnd
.Col(); ++nCol
)
985 pVariables
[nVarPos
++] = table::CellAddress( nTab
, nCol
, nRow
);
988 uno::Sequence
<sheet::SolverConstraint
> aConstraints
;
989 sal_Int32 nConstrPos
= 0;
990 for ( const auto& rConstr
: m_aConditions
)
992 if ( !rConstr
.aLeftStr
.isEmpty() )
994 sheet::SolverConstraint aConstraint
;
995 // Order of list box entries must match enum values.
996 // The enum SolverConstraintOperator starts at zero, whereas ConstraintOperator starts at 1
997 // hence we need to subtract -1 here
998 aConstraint
.Operator
= static_cast<sheet::SolverConstraintOperator
>(rConstr
.nOperator
- 1);
1001 if ( !ParseRef( aLeftRange
, rConstr
.aLeftStr
, true ) )
1003 ShowError( true, nullptr );
1007 bool bIsRange
= false;
1008 ScRange aRightRange
;
1009 if ( ParseRef( aRightRange
, rConstr
.aRightStr
, true ) )
1011 if ( aRightRange
.aStart
== aRightRange
.aEnd
)
1012 aConstraint
.Right
<<= table::CellAddress( aRightRange
.aStart
.Tab(),
1013 aRightRange
.aStart
.Col(), aRightRange
.aStart
.Row() );
1014 else if ( aRightRange
.aEnd
.Col()-aRightRange
.aStart
.Col() == aLeftRange
.aEnd
.Col()-aLeftRange
.aStart
.Col() &&
1015 aRightRange
.aEnd
.Row()-aRightRange
.aStart
.Row() == aLeftRange
.aEnd
.Row()-aLeftRange
.aStart
.Row() )
1016 bIsRange
= true; // same size as "left" range, resolve into single cells
1019 ShowError( true, nullptr );
1025 sal_uInt32 nFormat
= 0; //! explicit language?
1026 double fValue
= 0.0;
1027 if ( mrDoc
.GetFormatTable()->IsNumberFormat( rConstr
.aRightStr
, nFormat
, fValue
) )
1028 aConstraint
.Right
<<= fValue
;
1029 else if ( aConstraint
.Operator
!= sheet::SolverConstraintOperator_INTEGER
&&
1030 aConstraint
.Operator
!= sheet::SolverConstraintOperator_BINARY
)
1032 ShowError( true, nullptr );
1037 // resolve into single cells
1039 sal_Int32 nAdd
= ( aLeftRange
.aEnd
.Col() - aLeftRange
.aStart
.Col() + 1 ) *
1040 ( aLeftRange
.aEnd
.Row() - aLeftRange
.aStart
.Row() + 1 );
1041 aConstraints
.realloc( nConstrPos
+ nAdd
);
1042 auto pConstraints
= aConstraints
.getArray();
1044 for (SCROW nRow
= aLeftRange
.aStart
.Row(); nRow
<= aLeftRange
.aEnd
.Row(); ++nRow
)
1045 for (SCCOL nCol
= aLeftRange
.aStart
.Col(); nCol
<= aLeftRange
.aEnd
.Col(); ++nCol
)
1047 aConstraint
.Left
= table::CellAddress( aLeftRange
.aStart
.Tab(), nCol
, nRow
);
1049 aConstraint
.Right
<<= table::CellAddress( aRightRange
.aStart
.Tab(),
1050 aRightRange
.aStart
.Col() + ( nCol
- aLeftRange
.aStart
.Col() ),
1051 aRightRange
.aStart
.Row() + ( nRow
- aLeftRange
.aStart
.Row() ) );
1053 pConstraints
[nConstrPos
++] = aConstraint
;
1058 bool bMaximize
= m_xRbMax
->get_active();
1059 if ( m_xRbValue
->get_active() )
1061 // handle "value of" with an additional constraint (and then minimize)
1063 sheet::SolverConstraint aConstraint
;
1064 aConstraint
.Left
= aObjective
;
1065 aConstraint
.Operator
= sheet::SolverConstraintOperator_EQUAL
;
1067 OUString aValStr
= m_xEdTargetValue
->GetText();
1068 ScRange aRightRange
;
1069 if ( ParseRef( aRightRange
, aValStr
, false ) )
1070 aConstraint
.Right
<<= table::CellAddress( aRightRange
.aStart
.Tab(),
1071 aRightRange
.aStart
.Col(), aRightRange
.aStart
.Row() );
1074 sal_uInt32 nFormat
= 0; //! explicit language?
1075 double fValue
= 0.0;
1076 if ( mrDoc
.GetFormatTable()->IsNumberFormat( aValStr
, nFormat
, fValue
) )
1077 aConstraint
.Right
<<= fValue
;
1080 ShowError( false, m_xEdTargetValue
.get() );
1085 aConstraints
.realloc( nConstrPos
+ 1 );
1086 aConstraints
.getArray()[nConstrPos
++] = std::move(aConstraint
);
1089 // copy old document values
1091 sal_Int32 nVarCount
= aVariables
.getLength();
1092 uno::Sequence
<double> aOldValues( nVarCount
);
1093 std::transform(std::cbegin(aVariables
), std::cend(aVariables
), aOldValues
.getArray(),
1094 [this](const table::CellAddress
& rVariable
) -> double {
1096 ScUnoConversion::FillScAddress( aCellPos
, rVariable
);
1097 return mrDoc
.GetValue( aCellPos
);
1100 // create and initialize solver
1102 uno::Reference
<sheet::XSolver
> xSolver
= ScSolverUtil::GetSolver( maEngine
);
1103 OSL_ENSURE( xSolver
.is(), "can't get solver component" );
1104 if ( !xSolver
.is() )
1107 xSolver
->setDocument( xDocument
);
1108 xSolver
->setObjective( aObjective
);
1109 xSolver
->setVariables( aVariables
);
1110 xSolver
->setConstraints( aConstraints
);
1111 xSolver
->setMaximize( bMaximize
);
1114 uno::Reference
<beans::XPropertySet
> xOptProp(xSolver
, uno::UNO_QUERY
);
1115 if ( xOptProp
.is() )
1117 for (const beans::PropertyValue
& rValue
: maProperties
)
1121 xOptProp
->setPropertyValue( rValue
.Name
, rValue
.Value
);
1123 catch ( uno::Exception
& )
1125 OSL_FAIL("Exception in solver option property");
1130 // tdf#162760 The solver engine may crash unexpectedly, so we need a try...catch here
1131 bool bSuccess(false);
1135 bSuccess
= xSolver
->getSuccess();
1137 catch (const uno::RuntimeException
&)
1139 std::unique_ptr
<weld::MessageDialog
> xBox(Application::CreateMessageDialog(m_xDialog
.get(),
1140 VclMessageType::Error
, VclButtonsType::Ok
,
1141 ScResId(STR_SOLVER_ENGINE_ERROR
)));
1145 xProgress
->response(RET_CLOSE
);
1147 bool bClose
= false;
1148 bool bRestore
= true; // restore old values unless a solution is accepted
1151 // put solution into document so it is visible when asking
1152 uno::Sequence
<double> aSolution
= xSolver
->getSolution();
1153 if ( aSolution
.getLength() == nVarCount
)
1155 mpDocShell
->LockPaint();
1156 ScDocFunc
&rFunc
= mpDocShell
->GetDocFunc();
1157 for (nVarPos
=0; nVarPos
<nVarCount
; ++nVarPos
)
1160 ScUnoConversion::FillScAddress(aCellPos
, aVariables
[nVarPos
]);
1161 rFunc
.SetValueCell(aCellPos
, aSolution
[nVarPos
], false);
1163 mpDocShell
->UnlockPaint();
1167 // take formatted result from document (result value from component is ignored)
1168 OUString aResultStr
= mrDoc
.GetString(
1169 static_cast<SCCOL
>(aObjective
.Column
), static_cast<SCROW
>(aObjective
.Row
),
1170 static_cast<SCTAB
>(aObjective
.Sheet
));
1172 ScSolverSuccessDialog
aDialog(m_xDialog
.get(), aResultStr
);
1173 if (aDialog
.run() == RET_OK
)
1175 // keep results and close dialog
1183 uno::Reference
<sheet::XSolverDescription
> xDesc( xSolver
, uno::UNO_QUERY
);
1185 aError
= xDesc
->getStatusDescription(); // error description from component
1186 ScSolverNoSolutionDialog
aDialog(m_xDialog
.get(), aError
);
1190 if ( bRestore
) // restore old values
1192 mpDocShell
->LockPaint();
1193 ScDocFunc
&rFunc
= mpDocShell
->GetDocFunc();
1194 for (nVarPos
=0; nVarPos
<nVarCount
; ++nVarPos
)
1197 ScUnoConversion::FillScAddress( aCellPos
, aVariables
[nVarPos
] );
1198 rFunc
.SetValueCell(aCellPos
, aOldValues
[nVarPos
], false);
1200 mpDocShell
->UnlockPaint();
1203 // Generate sensitivity report if user wants it
1204 uno::Reference
<css::beans::XPropertySetInfo
> xInfo
= xOptProp
->getPropertySetInfo();
1205 bool bUserWantsReport
= false;
1206 if (xInfo
->hasPropertyByName("GenSensitivityReport"))
1207 xOptProp
->getPropertyValue("GenSensitivityReport") >>= bUserWantsReport
;
1209 if (bSuccess
&& bUserWantsReport
)
1211 // Retrieve the sensitivity analysis report
1212 css::sheet::SensitivityReport aSensitivity
;
1213 bool bHasReportObj
= xOptProp
->getPropertyValue("SensitivityReport") >>= aSensitivity
;
1215 if (bHasReportObj
&& aSensitivity
.HasReport
)
1217 // Define the Tab name where the sensitivity analysis will be written to
1218 OUString sNewTabName
;
1220 mrDoc
.GetName(mnCurTab
, sNewTabName
);
1221 sNewTabName
+= "_" + ScResId(STR_SENSITIVITY
);
1222 // Check if the new Tab name exists
1223 if (mrDoc
.GetTable(sNewTabName
, nNewTab
))
1225 // Add numbers to the end of the Tab name to make it unique
1231 aName
= sNewTabName
+ "_" + OUString::number(static_cast<sal_Int32
>(i
));
1233 while(mrDoc
.GetTable(aName
, nNewTab
));
1234 sNewTabName
= aName
;
1237 // Insert new sheet to the document and start writing the report
1238 ScDocFunc
&rFunc
= mpDocShell
->GetDocFunc();
1239 rFunc
.InsertTable(mnCurTab
+ 1, sNewTabName
, false, false);
1241 if (!mrDoc
.GetTable(sNewTabName
, nReportTab
))
1243 SAL_WARN("sc", "Could not get the just inserted table!");
1247 // Used to input data in the new sheet
1248 ScAddress
aOutputAddress(0, 0, nReportTab
);
1249 ScAddress::Details
mAddressDetails(mrDoc
, aOutputAddress
);
1250 AddressWalkerWriter
aOutput(aOutputAddress
, mpDocShell
, mrDoc
,
1251 formula::FormulaGrammar::mergeToGrammar(formula::FormulaGrammar::GRAM_ENGLISH
, mAddressDetails
.eConv
));
1252 aOutput
.writeBoldString(ScResId(STR_SENSITIVITY_TITLE
));
1254 aOutput
.writeString(ScResId(STR_SOLVER_ENGINE
) + " " + maEngine
);
1258 // Objective cell section
1259 aOutput
.writeBoldString(ScResId(STR_SENSITIVITY_OBJCELL
));
1261 aOutput
.writeString(ScResId(STR_SENSITIVITY_CELL
));
1262 aOutput
.nextColumn();
1263 aOutput
.writeString(ScResId(STR_SENSITIVITY_FINALVALUE
));
1265 aOutput
.writeString(GetCellStrAddress(xSolver
->getObjective()));
1266 aOutput
.nextColumn();
1267 aOutput
.writeValue(xSolver
->getResultValue());
1271 // Variable cell section
1272 aOutput
.writeBoldString(ScResId(STR_SENSITIVITY_VARCELLS
));
1274 aOutput
.writeString(ScResId(STR_SENSITIVITY_CELL
));
1275 aOutput
.nextColumn();
1276 aOutput
.writeString(ScResId(STR_SENSITIVITY_FINALVALUE
));
1277 aOutput
.nextColumn();
1278 aOutput
.writeString(ScResId(STR_SENSITIVITY_REDUCED
));
1279 aOutput
.nextColumn();
1280 aOutput
.writeString(ScResId(STR_SENSITIVITY_OBJCOEFF
));
1281 aOutput
.nextColumn();
1282 aOutput
.writeString(ScResId(STR_SENSITIVITY_DECREASE
));
1283 aOutput
.nextColumn();
1284 aOutput
.writeString(ScResId(STR_SENSITIVITY_INCREASE
));
1287 uno::Sequence
<double> aSolution
= xSolver
->getSolution();
1288 uno::Sequence
<double> aObjCoefficients
= aSensitivity
.ObjCoefficients
;
1289 uno::Sequence
<double> aObjReducedCosts
= aSensitivity
.ObjReducedCosts
;
1290 uno::Sequence
<double> aObjAllowableDecreases
= aSensitivity
.ObjAllowableDecreases
;
1291 uno::Sequence
<double> aObjAllowableIncreases
= aSensitivity
.ObjAllowableIncreases
;
1292 for (sal_Int32 i
= 0; i
< aVariables
.getLength(); i
++)
1294 aOutput
.writeString(GetCellStrAddress(aVariables
[i
]));
1295 aOutput
.nextColumn();
1296 aOutput
.writeValue(aSolution
[i
]);
1297 aOutput
.nextColumn();
1298 aOutput
.writeValue(aObjReducedCosts
[i
]);
1299 aOutput
.nextColumn();
1300 aOutput
.writeValue(aObjCoefficients
[i
]);
1301 aOutput
.nextColumn();
1302 aOutput
.writeValue(aObjAllowableDecreases
[i
]);
1303 aOutput
.nextColumn();
1304 aOutput
.writeValue(aObjAllowableIncreases
[i
]);
1309 // Constraints section
1310 aOutput
.writeBoldString(ScResId(STR_SENSITIVITY_CONSTRAINTS
));
1312 aOutput
.writeString(ScResId(STR_SENSITIVITY_CELL
));
1313 aOutput
.nextColumn();
1314 aOutput
.writeString(ScResId(STR_SENSITIVITY_FINALVALUE
));
1315 aOutput
.nextColumn();
1316 aOutput
.writeString(ScResId(STR_SENSITIVITY_SHADOWPRICE
));
1317 aOutput
.nextColumn();
1318 aOutput
.writeString(ScResId(STR_SENSITIVITY_RHS
));
1319 aOutput
.nextColumn();
1320 aOutput
.writeString(ScResId(STR_SENSITIVITY_DECREASE
));
1321 aOutput
.nextColumn();
1322 aOutput
.writeString(ScResId(STR_SENSITIVITY_INCREASE
));
1325 uno::Sequence
<double> aConstrValues
= aSensitivity
.ConstrValues
;
1326 uno::Sequence
<double> aConstrRHS
= aSensitivity
.ConstrRHS
;
1327 uno::Sequence
<double> aConstrShadowPrices
= aSensitivity
.ConstrShadowPrices
;
1328 uno::Sequence
<double> aConstrAllowableDecreases
= aSensitivity
.ConstrAllowableDecreases
;
1329 uno::Sequence
<double> aConstrAllowableIncreases
= aSensitivity
.ConstrAllowableIncreases
;
1330 for (sal_Int32 i
= 0; i
< aConstraints
.getLength(); i
++)
1332 aOutput
.writeString(GetCellStrAddress(aConstraints
[i
].Left
));
1333 aOutput
.nextColumn();
1334 aOutput
.writeValue(aConstrValues
[i
]);
1335 aOutput
.nextColumn();
1336 aOutput
.writeValue(aConstrShadowPrices
[i
]);
1337 aOutput
.nextColumn();
1338 aOutput
.writeValue(aConstrRHS
[i
]);
1339 aOutput
.nextColumn();
1340 aOutput
.writeValue(aConstrAllowableDecreases
[i
]);
1341 aOutput
.nextColumn();
1342 aOutput
.writeValue(aConstrAllowableIncreases
[i
]);
1351 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */