tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sc / source / ui / miscdlgs / optsolver.cxx
blob5f75a610def87728dee80e7880b50adfd81a96bd
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
23 #include <utility>
24 #include <vcl/commandinfoprovider.hxx>
25 #include <vcl/weld.hxx>
26 #include <vcl/svapp.hxx>
28 #include <reffact.hxx>
29 #include <docsh.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>
40 #include <table.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()
65 m_xFtTime->hide();
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);
106 else
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 ) )
130 if ( bUp )
131 maCursorUpLink.Call( *this );
132 else
133 maCursorDownLink.Call( *this );
134 return true;
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))
145 , mpDocShell(pDocSh)
146 , mrDoc(pDocSh->GetDocument())
147 , mnCurTab(aCursorPos.Tab())
148 , mbDlgLostFocus(false)
149 , nScrollPos(0)
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();
248 Init( aCursorPos );
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();
330 ShowConditions();
332 // If no objective cell has been loaded, then use the selected cell
333 if (m_xEdObjectiveCell->GetText().isEmpty())
335 OUString aCursorStr;
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() )
364 --nSize;
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);
390 EnableButtons();
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()
404 if (m_xOptDlg)
405 m_xOptDlg->response(RET_CANCEL);
406 assert(!m_xOptDlg);
407 DoClose( ScOptSolverDlgWrapper::GetChildWindowId() );
410 void ScOptSolverDlg::SetActive()
412 if ( mbDlgLostFocus )
414 mbDlgLostFocus = false;
415 if( mpEdActive )
416 mpEdActive->GrabFocus();
418 else
420 m_xDialog->grab_focus();
422 RefInputDone();
425 void ScOptSolverDlg::SetReference( const ScRange& rRef, ScDocument& rDocP )
427 if( !mpEdActive )
428 return;
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() );
436 OUString aStr;
437 ScAddress aAdr = rRef.aStart;
438 ScRange aNewRef( rRef );
439 if ( bSingle )
440 aNewRef.aEnd = aAdr;
442 OUString aName;
443 if ( rDocP.GetRangeAtBlock( aNewRef, aName ) ) // named range: show name
444 aStr = aName;
445 else // format cell/range reference
447 ScRefFlags nFmt = ( aAdr.Tab() == mnCurTab ) ? ScRefFlags::ADDR_ABS : ScRefFlags::ADDR_ABS_3D;
448 if ( bSingle )
449 aStr = aAdr.Format(nFmt, &rDocP, rDocP.GetAddressConvention());
450 else
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();
459 aSel.Normalize();
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 );
465 else
466 mpEdActive->SetRefString( aStr );
468 ReadConditions();
469 EnableButtons();
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));
488 // Objective type
489 sc::ObjectiveType eType = m_pSolverSettings->GetObjectiveType();
490 switch (eType)
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;
497 // Model constraints
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)
524 return;
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())
533 bModified = true;
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)
543 bModified = true;
545 // Check if model constraints changed
546 std::vector<sc::ModelConstraint> vCurConditions = m_pSolverSettings->GetConstraints();
547 if (!bModified && vCurConditions.size() != m_aConditions.size())
548 bModified = true;
549 else
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)
558 bModified = true;
560 if (bModified)
561 break;
565 // Check if the solver engine name and its options have changed
566 if (m_pSolverSettings->GetParameter(sc::SP_LO_ENGINE) != maEngine)
568 bModified = true;
570 else
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)
582 bModified = true;
583 break;
588 // Effectively save settings to file if modifications were made
589 if (bModified)
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);
605 return nIndex != -1;
608 // Handler:
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 );
618 SwitchToDocument();
620 bool bClose = true;
621 if ( bSolve )
622 bClose = CallSolver();
624 if ( bClose )
626 // Close: write dialog settings to DocShell for subsequent calls
627 ReadConditions();
628 SaveSolverSettings();
629 response(RET_CLOSE);
631 else
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();
647 m_xOptDlg.reset();
650 else if (&rBtn == m_xBtnResetAll.get())
652 OUString sEmpty;
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();
663 ShowConditions();
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];
690 if( pEdit )
691 pEdit->SelectAll();
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];
713 if( pEdit )
714 pEdit->SelectAll();
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
722 else
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();
749 ReadConditions();
750 tools::Long nVecPos = nScrollPos + nRow;
751 if ( nVecPos < static_cast<tools::Long>(m_aConditions.size()) )
753 m_aConditions.erase( m_aConditions.begin() + nVecPos );
754 ShowConditions();
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
779 ReadConditions();
780 EnableButtons();
783 IMPL_LINK_NOARG(ScOptSolverDlg, SelectHdl, weld::ComboBox&, void)
785 // select handler for operator list boxes, just to enable/disable "delete" buttons
786 ReadConditions();
787 EnableButtons();
790 IMPL_LINK_NOARG(ScOptSolverDlg, ScrollHdl, weld::ScrolledWindow&, void)
792 ReadConditions();
793 nScrollPos = m_xScrollBar->vadjustment_get_value();
794 ShowConditions();
795 if( mpEdActive )
796 mpEdActive->SelectAll();
799 IMPL_LINK( ScOptSolverDlg, CursorUpHdl, ScCursorRefEdit&, rEdit, void )
801 if ( &rEdit == mpLeftEdit[0] || &rEdit == mpRightEdit[0] )
803 if ( nScrollPos > 0 )
805 ReadConditions();
806 --nScrollPos;
807 ShowConditions();
808 if( mpEdActive )
809 mpEdActive->SelectAll();
812 else
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];
822 if (pFocus)
824 mpEdActive = pFocus;
825 pFocus->GrabFocus();
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?
835 ReadConditions();
836 ++nScrollPos;
837 ShowConditions();
838 if( mpEdActive )
839 mpEdActive->SelectAll();
841 else
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];
851 if (pFocus)
853 mpEdActive = pFocus;
854 pFocus->GrabFocus();
859 // Converts the position of the operator in the dropdown menu to a ConstraintOperator type
860 sc::ConstraintOperator ScOptSolverDlg::OperatorIndexToConstraintOperator(sal_Int32 nIndex)
862 switch(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,
878 aMessage));
879 xBox->run();
880 if (pFocus)
882 mpEdActive = pFocus;
883 pFocus->GrabFocus();
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 )
907 bool bFound = false;
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 );
919 return bFound;
922 OUString ScOptSolverDlg::GetCellStrAddress(css::table::CellAddress aUnoAddress)
924 ScAddress aScAddr;
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 );
938 else
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
948 ReadConditions();
950 rtl::Reference<ScModelObj> xDocument( mpDocShell->GetModel() );
952 ScRange aObjRange;
953 if ( !ParseRef( aObjRange, m_xEdObjectiveCell->GetText(), false ) )
955 ShowError( false, m_xEdObjectiveCell.get() );
956 return false;
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() );
965 return false;
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 ] );
973 aRange.PutInOrder();
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);
1000 ScRange aLeftRange;
1001 if ( !ParseRef( aLeftRange, rConstr.aLeftStr, true ) )
1003 ShowError( true, nullptr );
1004 return false;
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
1017 else
1019 ShowError( true, nullptr );
1020 return false;
1023 else
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 );
1033 return false;
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 );
1048 if ( bIsRange )
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() );
1072 else
1074 sal_uInt32 nFormat = 0; //! explicit language?
1075 double fValue = 0.0;
1076 if ( mrDoc.GetFormatTable()->IsNumberFormat( aValStr, nFormat, fValue ) )
1077 aConstraint.Right <<= fValue;
1078 else
1080 ShowError( false, m_xEdTargetValue.get() );
1081 return false;
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 {
1095 ScAddress aCellPos;
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() )
1105 return false;
1107 xSolver->setDocument( xDocument );
1108 xSolver->setObjective( aObjective );
1109 xSolver->setVariables( aVariables );
1110 xSolver->setConstraints( aConstraints );
1111 xSolver->setMaximize( bMaximize );
1113 // set options
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);
1134 xSolver->solve();
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)));
1142 xBox->run();
1145 xProgress->response(RET_CLOSE);
1147 bool bClose = false;
1148 bool bRestore = true; // restore old values unless a solution is accepted
1149 if ( bSuccess )
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)
1159 ScAddress aCellPos;
1160 ScUnoConversion::FillScAddress(aCellPos, aVariables[nVarPos]);
1161 rFunc.SetValueCell(aCellPos, aSolution[nVarPos], false);
1163 mpDocShell->UnlockPaint();
1165 //! else error?
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
1176 bRestore = false;
1177 bClose = true;
1180 else
1182 OUString aError;
1183 uno::Reference<sheet::XSolverDescription> xDesc( xSolver, uno::UNO_QUERY );
1184 if ( xDesc.is() )
1185 aError = xDesc->getStatusDescription(); // error description from component
1186 ScSolverNoSolutionDialog aDialog(m_xDialog.get(), aError);
1187 aDialog.run();
1190 if ( bRestore ) // restore old values
1192 mpDocShell->LockPaint();
1193 ScDocFunc &rFunc = mpDocShell->GetDocFunc();
1194 for (nVarPos=0; nVarPos<nVarCount; ++nVarPos)
1196 ScAddress aCellPos;
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;
1219 SCTAB nNewTab;
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
1226 SCTAB i = 1;
1227 OUString aName;
1230 i++;
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);
1240 SCTAB nReportTab;
1241 if (!mrDoc.GetTable(sNewTabName, nReportTab))
1243 SAL_WARN("sc", "Could not get the just inserted table!");
1244 return false;
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));
1253 aOutput.newLine();
1254 aOutput.writeString(ScResId(STR_SOLVER_ENGINE) + " " + maEngine);
1255 aOutput.newLine();
1256 aOutput.newLine();
1258 // Objective cell section
1259 aOutput.writeBoldString(ScResId(STR_SENSITIVITY_OBJCELL));
1260 aOutput.newLine();
1261 aOutput.writeString(ScResId(STR_SENSITIVITY_CELL));
1262 aOutput.nextColumn();
1263 aOutput.writeString(ScResId(STR_SENSITIVITY_FINALVALUE));
1264 aOutput.newLine();
1265 aOutput.writeString(GetCellStrAddress(xSolver->getObjective()));
1266 aOutput.nextColumn();
1267 aOutput.writeValue(xSolver->getResultValue());
1268 aOutput.newLine();
1269 aOutput.newLine();
1271 // Variable cell section
1272 aOutput.writeBoldString(ScResId(STR_SENSITIVITY_VARCELLS));
1273 aOutput.newLine();
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));
1285 aOutput.newLine();
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]);
1305 aOutput.newLine();
1307 aOutput.newLine();
1309 // Constraints section
1310 aOutput.writeBoldString(ScResId(STR_SENSITIVITY_CONSTRAINTS));
1311 aOutput.newLine();
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));
1323 aOutput.newLine();
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]);
1343 aOutput.newLine();
1348 return bClose;
1351 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */