/src/libreoffice/sc/source/ui/miscdlgs/optsolver.cxx
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* |
3 | | * This file is part of the LibreOffice project. |
4 | | * |
5 | | * This Source Code Form is subject to the terms of the Mozilla Public |
6 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
7 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
8 | | * |
9 | | * This file incorporates work covered by the following license notice: |
10 | | * |
11 | | * Licensed to the Apache Software Foundation (ASF) under one or more |
12 | | * contributor license agreements. See the NOTICE file distributed |
13 | | * with this work for additional information regarding copyright |
14 | | * ownership. The ASF licenses this file to you under the Apache |
15 | | * License, Version 2.0 (the "License"); you may not use this file |
16 | | * except in compliance with the License. You may obtain a copy of |
17 | | * the License at http://www.apache.org/licenses/LICENSE-2.0 . |
18 | | */ |
19 | | |
20 | | #include <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> |
27 | | |
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> |
42 | | #include <tabvwsh.hxx> |
43 | | |
44 | | #include <com/sun/star/beans/XPropertySetInfo.hpp> |
45 | | #include <com/sun/star/sheet/SolverConstraint.hpp> |
46 | | #include <com/sun/star/sheet/SolverConstraintOperator.hpp> |
47 | | #include <com/sun/star/sheet/XSolverDescription.hpp> |
48 | | #include <com/sun/star/sheet/XSolver.hpp> |
49 | | #include <com/sun/star/sheet/SensitivityReport.hpp> |
50 | | |
51 | | using namespace com::sun::star; |
52 | | |
53 | | ScSolverProgressDialog::ScSolverProgressDialog(weld::Window* pParent) |
54 | 0 | : GenericDialogController(pParent, u"modules/scalc/ui/solverprogressdialog.ui"_ustr, |
55 | 0 | u"SolverProgressDialog"_ustr) |
56 | 0 | , m_xFtTime(m_xBuilder->weld_label(u"progress"_ustr)) |
57 | 0 | { |
58 | 0 | } |
59 | | |
60 | | ScSolverProgressDialog::~ScSolverProgressDialog() |
61 | 0 | { |
62 | 0 | } |
63 | | |
64 | | void ScSolverProgressDialog::HideTimeLimit() |
65 | 0 | { |
66 | 0 | m_xFtTime->hide(); |
67 | 0 | } |
68 | | |
69 | | void ScSolverProgressDialog::SetTimeLimit( sal_Int32 nSeconds ) |
70 | 0 | { |
71 | 0 | OUString aOld = m_xFtTime->get_label(); |
72 | 0 | OUString aNew = aOld.replaceFirst("#", OUString::number(nSeconds)); |
73 | 0 | m_xFtTime->set_label(aNew); |
74 | 0 | } |
75 | | |
76 | | ScSolverNoSolutionDialog::ScSolverNoSolutionDialog(weld::Window* pParent, const OUString& rErrorText) |
77 | 0 | : GenericDialogController(pParent, u"modules/scalc/ui/nosolutiondialog.ui"_ustr, u"NoSolutionDialog"_ustr) |
78 | 0 | , m_xFtErrorText(m_xBuilder->weld_label(u"error"_ustr)) |
79 | 0 | { |
80 | 0 | m_xFtErrorText->set_label(rErrorText); |
81 | 0 | } |
82 | | |
83 | | ScSolverNoSolutionDialog::~ScSolverNoSolutionDialog() |
84 | 0 | { |
85 | 0 | } |
86 | | |
87 | | ScSolverSuccessDialog::ScSolverSuccessDialog(weld::Window* pParent, std::u16string_view rSolution) |
88 | 0 | : GenericDialogController(pParent, u"modules/scalc/ui/solversuccessdialog.ui"_ustr, u"SolverSuccessDialog"_ustr) |
89 | 0 | , m_xFtResult(m_xBuilder->weld_label(u"result"_ustr)) |
90 | 0 | , m_xBtnOk(m_xBuilder->weld_button(u"ok"_ustr)) |
91 | 0 | , m_xBtnCancel(m_xBuilder->weld_button(u"cancel"_ustr)) |
92 | 0 | { |
93 | 0 | m_xBtnOk->connect_clicked(LINK(this, ScSolverSuccessDialog, ClickHdl)); |
94 | 0 | m_xBtnCancel->connect_clicked(LINK(this, ScSolverSuccessDialog, ClickHdl)); |
95 | 0 | OUString aMessage = m_xFtResult->get_label() + " " + rSolution; |
96 | 0 | m_xFtResult->set_label(aMessage); |
97 | 0 | } |
98 | | |
99 | | ScSolverSuccessDialog::~ScSolverSuccessDialog() |
100 | 0 | { |
101 | 0 | } |
102 | | |
103 | | IMPL_LINK(ScSolverSuccessDialog, ClickHdl, weld::Button&, rBtn, void) |
104 | 0 | { |
105 | 0 | if (&rBtn == m_xBtnOk.get()) |
106 | 0 | m_xDialog->response(RET_OK); |
107 | 0 | else |
108 | 0 | m_xDialog->response(RET_CANCEL); |
109 | 0 | } |
110 | | |
111 | | ScCursorRefEdit::ScCursorRefEdit(std::unique_ptr<weld::Entry> xControl) |
112 | 0 | : formula::RefEdit(std::move(xControl)) |
113 | 0 | { |
114 | 0 | xEntry->connect_key_press(Link<const KeyEvent&, bool>()); //acknowledge we first remove the old one |
115 | 0 | xEntry->connect_key_press(LINK(this, ScCursorRefEdit, KeyInputHdl)); |
116 | 0 | } |
117 | | |
118 | | void ScCursorRefEdit::SetCursorLinks( const Link<ScCursorRefEdit&,void>& rUp, const Link<ScCursorRefEdit&,void>& rDown ) |
119 | 0 | { |
120 | 0 | maCursorUpLink = rUp; |
121 | 0 | maCursorDownLink = rDown; |
122 | 0 | } |
123 | | |
124 | | IMPL_LINK(ScCursorRefEdit, KeyInputHdl, const KeyEvent&, rKEvt, bool) |
125 | 0 | { |
126 | 0 | vcl::KeyCode aCode = rKEvt.GetKeyCode(); |
127 | 0 | bool bUp = (aCode.GetCode() == KEY_UP); |
128 | 0 | bool bDown = (aCode.GetCode() == KEY_DOWN); |
129 | 0 | if ( !aCode.IsShift() && !aCode.IsMod1() && !aCode.IsMod2() && ( bUp || bDown ) ) |
130 | 0 | { |
131 | 0 | if ( bUp ) |
132 | 0 | maCursorUpLink.Call( *this ); |
133 | 0 | else |
134 | 0 | maCursorDownLink.Call( *this ); |
135 | 0 | return true; |
136 | 0 | } |
137 | 0 | return formula::RefEdit::KeyInput(rKEvt); |
138 | 0 | } |
139 | | |
140 | | ScOptSolverDlg::ScOptSolverDlg(SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent, |
141 | | ScDocShell& rDocSh, const ScAddress& aCursorPos) |
142 | 0 | : ScAnyRefDlgController(pB, pCW, pParent, u"modules/scalc/ui/solverdlg.ui"_ustr, u"SolverDialog"_ustr) |
143 | 0 | , maInputError(ScResId(STR_INVALIDINPUT)) |
144 | 0 | , maConditionError(ScResId(STR_INVALIDCONDITION)) |
145 | | |
146 | 0 | , mrDocShell(rDocSh) |
147 | 0 | , mrDoc(rDocSh.GetDocument()) |
148 | 0 | , mnCurTab(aCursorPos.Tab()) |
149 | 0 | , mbDlgLostFocus(false) |
150 | 0 | , nScrollPos(0) |
151 | 0 | , mpEdActive(nullptr) |
152 | 0 | , m_xFtObjectiveCell(m_xBuilder->weld_label(u"targetlabel"_ustr)) |
153 | 0 | , m_xEdObjectiveCell(new formula::RefEdit(m_xBuilder->weld_entry(u"targetedit"_ustr))) |
154 | 0 | , m_xRBObjectiveCell(new formula::RefButton(m_xBuilder->weld_button(u"targetbutton"_ustr))) |
155 | 0 | , m_xRbMax(m_xBuilder->weld_radio_button(u"max"_ustr)) |
156 | 0 | , m_xRbMin(m_xBuilder->weld_radio_button(u"min"_ustr)) |
157 | 0 | , m_xRbValue(m_xBuilder->weld_radio_button(u"value"_ustr)) |
158 | 0 | , m_xEdTargetValue(new formula::RefEdit(m_xBuilder->weld_entry(u"valueedit"_ustr))) |
159 | 0 | , m_xRBTargetValue(new formula::RefButton(m_xBuilder->weld_button(u"valuebutton"_ustr))) |
160 | 0 | , m_xFtVariableCells(m_xBuilder->weld_label(u"changelabel"_ustr)) |
161 | 0 | , m_xEdVariableCells(new formula::RefEdit(m_xBuilder->weld_entry(u"changeedit"_ustr))) |
162 | 0 | , m_xRBVariableCells(new formula::RefButton(m_xBuilder->weld_button(u"changebutton"_ustr))) |
163 | 0 | , m_xFtCellRef(m_xBuilder->weld_label(u"cellreflabel"_ustr)) |
164 | 0 | , m_xEdLeft1(new ScCursorRefEdit(m_xBuilder->weld_entry(u"ref1edit"_ustr))) |
165 | 0 | , m_xRBLeft1(new formula::RefButton(m_xBuilder->weld_button(u"ref1button"_ustr))) |
166 | 0 | , m_xLbOp1(m_xBuilder->weld_combo_box(u"op1list"_ustr)) |
167 | 0 | , m_xFtConstraint(m_xBuilder->weld_label(u"constraintlabel"_ustr)) |
168 | 0 | , m_xEdRight1(new ScCursorRefEdit(m_xBuilder->weld_entry(u"val1edit"_ustr))) |
169 | 0 | , m_xRBRight1(new formula::RefButton(m_xBuilder->weld_button(u"val1button"_ustr))) |
170 | 0 | , m_xBtnDel1(m_xBuilder->weld_button(u"del1"_ustr)) |
171 | 0 | , m_xEdLeft2(new ScCursorRefEdit(m_xBuilder->weld_entry(u"ref2edit"_ustr))) |
172 | 0 | , m_xRBLeft2(new formula::RefButton(m_xBuilder->weld_button(u"ref2button"_ustr))) |
173 | 0 | , m_xLbOp2(m_xBuilder->weld_combo_box(u"op2list"_ustr)) |
174 | 0 | , m_xEdRight2(new ScCursorRefEdit(m_xBuilder->weld_entry(u"val2edit"_ustr))) |
175 | 0 | , m_xRBRight2(new formula::RefButton(m_xBuilder->weld_button(u"val2button"_ustr))) |
176 | 0 | , m_xBtnDel2(m_xBuilder->weld_button(u"del2"_ustr)) |
177 | 0 | , m_xEdLeft3(new ScCursorRefEdit(m_xBuilder->weld_entry(u"ref3edit"_ustr))) |
178 | 0 | , m_xRBLeft3(new formula::RefButton(m_xBuilder->weld_button(u"ref3button"_ustr))) |
179 | 0 | , m_xLbOp3(m_xBuilder->weld_combo_box(u"op3list"_ustr)) |
180 | 0 | , m_xEdRight3(new ScCursorRefEdit(m_xBuilder->weld_entry(u"val3edit"_ustr))) |
181 | 0 | , m_xRBRight3(new formula::RefButton(m_xBuilder->weld_button(u"val3button"_ustr))) |
182 | 0 | , m_xBtnDel3(m_xBuilder->weld_button(u"del3"_ustr)) |
183 | 0 | , m_xEdLeft4(new ScCursorRefEdit(m_xBuilder->weld_entry(u"ref4edit"_ustr))) |
184 | 0 | , m_xRBLeft4(new formula::RefButton(m_xBuilder->weld_button(u"ref4button"_ustr))) |
185 | 0 | , m_xLbOp4(m_xBuilder->weld_combo_box(u"op4list"_ustr)) |
186 | 0 | , m_xEdRight4(new ScCursorRefEdit(m_xBuilder->weld_entry(u"val4edit"_ustr))) |
187 | 0 | , m_xRBRight4(new formula::RefButton(m_xBuilder->weld_button(u"val4button"_ustr))) |
188 | 0 | , m_xBtnDel4(m_xBuilder->weld_button(u"del4"_ustr)) |
189 | 0 | , m_xScrollBar(m_xBuilder->weld_scrolled_window(u"scrollbar"_ustr, true)) |
190 | 0 | , m_xBtnOpt(m_xBuilder->weld_button(u"options"_ustr)) |
191 | 0 | , m_xBtnClose(m_xBuilder->weld_button(u"close"_ustr)) |
192 | 0 | , m_xBtnSolve(m_xBuilder->weld_button(u"ok"_ustr)) |
193 | 0 | , m_xBtnResetAll(m_xBuilder->weld_button(u"resetall"_ustr)) |
194 | 0 | , m_xResultFT(m_xBuilder->weld_label(u"result"_ustr)) |
195 | 0 | , m_xContents(m_xBuilder->weld_widget(u"grid"_ustr)) |
196 | 0 | , m_pSolverSettings(mrDoc.FetchTable(mnCurTab)->GetSolverSettings()) |
197 | 0 | { |
198 | 0 | m_xEdObjectiveCell->SetReferences(this, m_xFtObjectiveCell.get()); |
199 | 0 | m_xRBObjectiveCell->SetReferences(this, m_xEdObjectiveCell.get()); |
200 | 0 | m_xEdTargetValue->SetReferences(this, m_xResultFT.get()); |
201 | 0 | m_xRBTargetValue->SetReferences(this, m_xEdTargetValue.get()); |
202 | 0 | m_xEdVariableCells->SetReferences(this, m_xFtVariableCells.get()); |
203 | 0 | m_xRBVariableCells->SetReferences(this, m_xEdVariableCells.get()); |
204 | 0 | m_xEdLeft1->SetReferences(this, m_xFtCellRef.get()); |
205 | 0 | m_xRBLeft1->SetReferences(this, m_xEdLeft1.get()); |
206 | 0 | m_xEdRight1->SetReferences(this, m_xFtConstraint.get()); |
207 | 0 | m_xRBRight1->SetReferences(this, m_xEdRight1.get()); |
208 | 0 | m_xEdLeft2->SetReferences(this, m_xFtCellRef.get()); |
209 | 0 | m_xRBLeft2->SetReferences(this, m_xEdLeft2.get()); |
210 | 0 | m_xEdRight2->SetReferences(this, m_xFtConstraint.get()); |
211 | 0 | m_xRBRight2->SetReferences(this, m_xEdRight2.get()); |
212 | 0 | m_xEdLeft3->SetReferences(this, m_xFtCellRef.get()); |
213 | 0 | m_xRBLeft3->SetReferences(this, m_xEdLeft3.get()); |
214 | 0 | m_xEdRight3->SetReferences(this, m_xFtConstraint.get()); |
215 | 0 | m_xRBRight3->SetReferences(this, m_xEdRight3.get()); |
216 | 0 | m_xEdLeft4->SetReferences(this, m_xFtCellRef.get()); |
217 | 0 | m_xRBLeft4->SetReferences(this, m_xEdLeft4.get()); |
218 | 0 | m_xEdRight4->SetReferences(this, m_xFtConstraint.get()); |
219 | 0 | m_xRBRight4->SetReferences(this, m_xEdRight4.get()); |
220 | |
|
221 | 0 | mpLeftEdit[0] = m_xEdLeft1.get(); |
222 | 0 | mpLeftButton[0] = m_xRBLeft1.get(); |
223 | 0 | mpRightEdit[0] = m_xEdRight1.get(); |
224 | 0 | mpRightButton[0] = m_xRBRight1.get(); |
225 | 0 | mpOperator[0] = m_xLbOp1.get(); |
226 | 0 | mpDelButton[0] = m_xBtnDel1.get(); |
227 | |
|
228 | 0 | mpLeftEdit[1] = m_xEdLeft2.get(); |
229 | 0 | mpLeftButton[1] = m_xRBLeft2.get(); |
230 | 0 | mpRightEdit[1] = m_xEdRight2.get(); |
231 | 0 | mpRightButton[1] = m_xRBRight2.get(); |
232 | 0 | mpOperator[1] = m_xLbOp2.get(); |
233 | 0 | mpDelButton[1] = m_xBtnDel2.get(); |
234 | |
|
235 | 0 | mpLeftEdit[2] = m_xEdLeft3.get(); |
236 | 0 | mpLeftButton[2] = m_xRBLeft3.get(); |
237 | 0 | mpRightEdit[2] = m_xEdRight3.get(); |
238 | 0 | mpRightButton[2] = m_xRBRight3.get(); |
239 | 0 | mpOperator[2] = m_xLbOp3.get(); |
240 | 0 | mpDelButton[2] = m_xBtnDel3.get(); |
241 | |
|
242 | 0 | mpLeftEdit[3] = m_xEdLeft4.get(); |
243 | 0 | mpLeftButton[3] = m_xRBLeft4.get(); |
244 | 0 | mpRightEdit[3] = m_xEdRight4.get(); |
245 | 0 | mpRightButton[3] = m_xRBRight4.get(); |
246 | 0 | mpOperator[3] = m_xLbOp4.get(); |
247 | 0 | mpDelButton[3] = m_xBtnDel4.get(); |
248 | |
|
249 | 0 | Init( aCursorPos ); |
250 | 0 | } |
251 | | |
252 | | ScOptSolverDlg::~ScOptSolverDlg() |
253 | 0 | { |
254 | 0 | } |
255 | | |
256 | | void ScOptSolverDlg::Init(const ScAddress& rCursorPos) |
257 | 0 | { |
258 | 0 | uno::Reference<frame::XFrame> xFrame = GetBindings().GetActiveFrame(); |
259 | 0 | auto xDelNm = vcl::CommandInfoProvider::GetXGraphicForCommand(u".uno:DeleteRows"_ustr, xFrame); |
260 | 0 | for (weld::Button* pButton : mpDelButton) |
261 | 0 | pButton->set_image(xDelNm); |
262 | |
|
263 | 0 | m_xBtnOpt->connect_clicked( LINK( this, ScOptSolverDlg, BtnHdl ) ); |
264 | 0 | m_xBtnClose->connect_clicked( LINK( this, ScOptSolverDlg, BtnHdl ) ); |
265 | 0 | m_xBtnSolve->connect_clicked( LINK( this, ScOptSolverDlg, BtnHdl ) ); |
266 | 0 | m_xBtnResetAll->connect_clicked( LINK( this, ScOptSolverDlg, BtnHdl ) ); |
267 | |
|
268 | 0 | Link<formula::RefEdit&,void> aEditLink = LINK( this, ScOptSolverDlg, GetEditFocusHdl ); |
269 | 0 | Link<formula::RefButton&,void> aButtonLink = LINK( this, ScOptSolverDlg, GetButtonFocusHdl ); |
270 | 0 | m_xEdObjectiveCell->SetGetFocusHdl( aEditLink ); |
271 | 0 | m_xRBObjectiveCell->SetGetFocusHdl( aButtonLink ); |
272 | 0 | m_xEdTargetValue->SetGetFocusHdl( aEditLink ); |
273 | 0 | m_xRBTargetValue->SetGetFocusHdl( aButtonLink ); |
274 | 0 | m_xEdVariableCells->SetGetFocusHdl( aEditLink ); |
275 | 0 | m_xRBVariableCells->SetGetFocusHdl( aButtonLink ); |
276 | 0 | Link<weld::Widget&,void> aLink = LINK(this, ScOptSolverDlg, GetFocusHdl); |
277 | 0 | m_xRbValue->connect_focus_in(aLink); |
278 | 0 | for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow ) |
279 | 0 | { |
280 | 0 | mpLeftEdit[nRow]->SetGetFocusHdl( aEditLink ); |
281 | 0 | mpLeftButton[nRow]->SetGetFocusHdl( aButtonLink ); |
282 | 0 | mpRightEdit[nRow]->SetGetFocusHdl( aEditLink ); |
283 | 0 | mpRightButton[nRow]->SetGetFocusHdl( aButtonLink ); |
284 | 0 | mpOperator[nRow]->connect_focus_in(aLink); |
285 | 0 | } |
286 | |
|
287 | 0 | aEditLink = LINK( this, ScOptSolverDlg, LoseEditFocusHdl ); |
288 | 0 | aButtonLink = LINK( this, ScOptSolverDlg, LoseButtonFocusHdl ); |
289 | 0 | m_xEdObjectiveCell->SetLoseFocusHdl( aEditLink ); |
290 | 0 | m_xRBObjectiveCell->SetLoseFocusHdl( aButtonLink ); |
291 | 0 | m_xEdTargetValue->SetLoseFocusHdl( aEditLink ); |
292 | 0 | m_xRBTargetValue-> SetLoseFocusHdl( aButtonLink ); |
293 | 0 | m_xEdVariableCells->SetLoseFocusHdl( aEditLink ); |
294 | 0 | m_xRBVariableCells->SetLoseFocusHdl( aButtonLink ); |
295 | 0 | for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow ) |
296 | 0 | { |
297 | 0 | mpLeftEdit[nRow]->SetLoseFocusHdl( aEditLink ); |
298 | 0 | mpLeftButton[nRow]->SetLoseFocusHdl( aButtonLink ); |
299 | 0 | mpRightEdit[nRow]->SetLoseFocusHdl( aEditLink ); |
300 | 0 | mpRightButton[nRow]->SetLoseFocusHdl( aButtonLink ); |
301 | 0 | } |
302 | |
|
303 | 0 | Link<ScCursorRefEdit&,void> aCursorUp = LINK( this, ScOptSolverDlg, CursorUpHdl ); |
304 | 0 | Link<ScCursorRefEdit&,void> aCursorDown = LINK( this, ScOptSolverDlg, CursorDownHdl ); |
305 | 0 | Link<formula::RefEdit&,void> aCondModify = LINK( this, ScOptSolverDlg, CondModifyHdl ); |
306 | 0 | for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow ) |
307 | 0 | { |
308 | 0 | mpLeftEdit[nRow]->SetCursorLinks( aCursorUp, aCursorDown ); |
309 | 0 | mpRightEdit[nRow]->SetCursorLinks( aCursorUp, aCursorDown ); |
310 | 0 | mpLeftEdit[nRow]->SetModifyHdl( aCondModify ); |
311 | 0 | mpRightEdit[nRow]->SetModifyHdl( aCondModify ); |
312 | 0 | mpDelButton[nRow]->connect_clicked( LINK( this, ScOptSolverDlg, DelBtnHdl ) ); |
313 | 0 | mpOperator[nRow]->connect_changed( LINK( this, ScOptSolverDlg, SelectHdl ) ); |
314 | 0 | } |
315 | 0 | m_xEdTargetValue->SetModifyHdl( LINK( this, ScOptSolverDlg, TargetModifyHdl ) ); |
316 | |
|
317 | 0 | Size aSize(m_xContents->get_preferred_size()); |
318 | 0 | m_xContents->set_size_request(aSize.Width(), aSize.Height()); |
319 | 0 | m_xScrollBar->connect_vadjustment_changed( LINK( this, ScOptSolverDlg, ScrollHdl ) ); |
320 | |
|
321 | 0 | m_xScrollBar->vadjustment_set_page_increment( EDIT_ROW_COUNT ); |
322 | 0 | m_xScrollBar->vadjustment_set_page_size( EDIT_ROW_COUNT ); |
323 | | // Range is set in ShowConditions |
324 | | |
325 | | // get available solver implementations |
326 | | //! sort by descriptions? |
327 | 0 | ScSolverUtil::GetImplementations( maImplNames, maDescriptions ); |
328 | | |
329 | | // Load existing settings stored in the tab |
330 | 0 | LoadSolverSettings(); |
331 | 0 | ShowConditions(); |
332 | | |
333 | | // If no objective cell has been loaded, then use the selected cell |
334 | 0 | if (m_xEdObjectiveCell->GetText().isEmpty()) |
335 | 0 | { |
336 | 0 | OUString aCursorStr; |
337 | 0 | if (!mrDoc.GetRangeAtBlock(ScRange(rCursorPos), aCursorStr)) |
338 | 0 | aCursorStr = rCursorPos.Format(ScRefFlags::ADDR_ABS, nullptr, mrDoc.GetAddressConvention()); |
339 | 0 | m_xEdObjectiveCell->SetRefString(aCursorStr); |
340 | 0 | } |
341 | |
|
342 | 0 | m_xEdObjectiveCell->GrabFocus(); |
343 | 0 | mpEdActive = m_xEdObjectiveCell.get(); |
344 | 0 | } |
345 | | |
346 | | void ScOptSolverDlg::ReadConditions() |
347 | 0 | { |
348 | 0 | for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow ) |
349 | 0 | { |
350 | 0 | sc::ModelConstraint aRowEntry; |
351 | 0 | aRowEntry.aLeftStr = mpLeftEdit[nRow]->GetText(); |
352 | 0 | aRowEntry.aRightStr = mpRightEdit[nRow]->GetText(); |
353 | 0 | aRowEntry.nOperator = OperatorIndexToConstraintOperator(mpOperator[nRow]->get_active()); |
354 | |
|
355 | 0 | tools::Long nVecPos = nScrollPos + nRow; |
356 | 0 | if ( nVecPos >= static_cast<tools::Long>(m_aConditions.size()) && !aRowEntry.IsDefault() ) |
357 | 0 | m_aConditions.resize( nVecPos + 1 ); |
358 | |
|
359 | 0 | if ( nVecPos < static_cast<tools::Long>(m_aConditions.size()) ) |
360 | 0 | m_aConditions[nVecPos] = std::move(aRowEntry); |
361 | | |
362 | | // remove default entries at the end |
363 | 0 | size_t nSize = m_aConditions.size(); |
364 | 0 | while ( nSize > 0 && m_aConditions[ nSize-1 ].IsDefault() ) |
365 | 0 | --nSize; |
366 | 0 | m_aConditions.resize( nSize ); |
367 | 0 | } |
368 | 0 | } |
369 | | |
370 | | void ScOptSolverDlg::ShowConditions() |
371 | 0 | { |
372 | 0 | for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow ) |
373 | 0 | { |
374 | 0 | sc::ModelConstraint aRowEntry; |
375 | |
|
376 | 0 | tools::Long nVecPos = nScrollPos + nRow; |
377 | 0 | if ( nVecPos < static_cast<tools::Long>(m_aConditions.size()) ) |
378 | 0 | aRowEntry = m_aConditions[nVecPos]; |
379 | |
|
380 | 0 | mpLeftEdit[nRow]->SetRefString( aRowEntry.aLeftStr ); |
381 | 0 | mpRightEdit[nRow]->SetRefString( aRowEntry.aRightStr ); |
382 | 0 | mpOperator[nRow]->set_active( aRowEntry.nOperator - 1); |
383 | 0 | } |
384 | | |
385 | | // allow to scroll one page behind the visible or stored rows |
386 | 0 | tools::Long nVisible = nScrollPos + EDIT_ROW_COUNT; |
387 | 0 | tools::Long nMax = std::max( nVisible, static_cast<tools::Long>(m_aConditions.size()) ); |
388 | 0 | m_xScrollBar->vadjustment_configure(nScrollPos, 0, nMax + EDIT_ROW_COUNT, 1, |
389 | 0 | EDIT_ROW_COUNT - 1, EDIT_ROW_COUNT); |
390 | |
|
391 | 0 | EnableButtons(); |
392 | 0 | } |
393 | | |
394 | | void ScOptSolverDlg::EnableButtons() |
395 | 0 | { |
396 | 0 | for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow ) |
397 | 0 | { |
398 | 0 | tools::Long nVecPos = nScrollPos + nRow; |
399 | 0 | mpDelButton[nRow]->set_sensitive(nVecPos < static_cast<tools::Long>(m_aConditions.size())); |
400 | 0 | } |
401 | 0 | } |
402 | | |
403 | | void ScOptSolverDlg::Close() |
404 | 0 | { |
405 | 0 | if (m_xOptDlg) |
406 | 0 | m_xOptDlg->response(RET_CANCEL); |
407 | 0 | assert(!m_xOptDlg); |
408 | 0 | DoClose( ScOptSolverDlgWrapper::GetChildWindowId() ); |
409 | 0 | } |
410 | | |
411 | | void ScOptSolverDlg::SetActive() |
412 | 0 | { |
413 | 0 | if ( mbDlgLostFocus ) |
414 | 0 | { |
415 | 0 | mbDlgLostFocus = false; |
416 | 0 | if( mpEdActive ) |
417 | 0 | mpEdActive->GrabFocus(); |
418 | 0 | } |
419 | 0 | else |
420 | 0 | { |
421 | 0 | m_xDialog->grab_focus(); |
422 | 0 | } |
423 | 0 | RefInputDone(); |
424 | 0 | } |
425 | | |
426 | | void ScOptSolverDlg::SetReference( const ScRange& rRef, ScDocument& rDocP ) |
427 | 0 | { |
428 | 0 | if( !mpEdActive ) |
429 | 0 | return; |
430 | | |
431 | 0 | if ( rRef.aStart != rRef.aEnd ) |
432 | 0 | RefInputStart(mpEdActive); |
433 | | |
434 | | // "target"/"value": single cell |
435 | 0 | bool bSingle = ( mpEdActive == m_xEdObjectiveCell.get() || mpEdActive == m_xEdTargetValue.get() ); |
436 | |
|
437 | 0 | OUString aStr; |
438 | 0 | ScAddress aAdr = rRef.aStart; |
439 | 0 | ScRange aNewRef( rRef ); |
440 | 0 | if ( bSingle ) |
441 | 0 | aNewRef.aEnd = aAdr; |
442 | |
|
443 | 0 | OUString aName; |
444 | 0 | if ( rDocP.GetRangeAtBlock( aNewRef, aName ) ) // named range: show name |
445 | 0 | aStr = aName; |
446 | 0 | else // format cell/range reference |
447 | 0 | { |
448 | 0 | ScRefFlags nFmt = ( aAdr.Tab() == mnCurTab ) ? ScRefFlags::ADDR_ABS : ScRefFlags::ADDR_ABS_3D; |
449 | 0 | if ( bSingle ) |
450 | 0 | aStr = aAdr.Format(nFmt, &rDocP, rDocP.GetAddressConvention()); |
451 | 0 | else |
452 | 0 | aStr = rRef.Format(rDocP, nFmt | ScRefFlags::RANGE_ABS, rDocP.GetAddressConvention()); |
453 | 0 | } |
454 | | |
455 | | // variable cells can be several ranges, so only the selection is replaced |
456 | 0 | if ( mpEdActive == m_xEdVariableCells.get() ) |
457 | 0 | { |
458 | 0 | OUString aVal = mpEdActive->GetText(); |
459 | 0 | Selection aSel = mpEdActive->GetSelection(); |
460 | 0 | aSel.Normalize(); |
461 | 0 | aVal = aVal.replaceAt( aSel.Min(), aSel.Len(), aStr ); |
462 | 0 | Selection aNewSel( aSel.Min(), aSel.Min()+aStr.getLength() ); |
463 | 0 | mpEdActive->SetRefString( aVal ); |
464 | 0 | mpEdActive->SetSelection( aNewSel ); |
465 | 0 | } |
466 | 0 | else |
467 | 0 | mpEdActive->SetRefString( aStr ); |
468 | |
|
469 | 0 | ReadConditions(); |
470 | 0 | EnableButtons(); |
471 | | |
472 | | // select "Value of" if a ref is input into "target" edit |
473 | 0 | if ( mpEdActive == m_xEdTargetValue.get() ) |
474 | 0 | m_xRbValue->set_active(true); |
475 | 0 | } |
476 | | |
477 | | bool ScOptSolverDlg::IsRefInputMode() const |
478 | 0 | { |
479 | 0 | return mpEdActive != nullptr; |
480 | 0 | } |
481 | | |
482 | | // Loads solver settings into the dialog |
483 | | void ScOptSolverDlg::LoadSolverSettings() |
484 | 0 | { |
485 | 0 | m_xEdObjectiveCell->SetRefString(m_pSolverSettings->GetParameter(sc::SP_OBJ_CELL)); |
486 | 0 | m_xEdTargetValue->SetRefString(m_pSolverSettings->GetParameter(sc::SP_OBJ_VAL)); |
487 | 0 | m_xEdVariableCells->SetRefString(m_pSolverSettings->GetParameter(sc::SP_VAR_CELLS)); |
488 | | |
489 | | // Objective type |
490 | 0 | sc::ObjectiveType eType = m_pSolverSettings->GetObjectiveType(); |
491 | 0 | switch (eType) |
492 | 0 | { |
493 | 0 | case sc::OT_MAXIMIZE : m_xRbMax->set_active(true); break; |
494 | 0 | case sc::OT_MINIMIZE : m_xRbMin->set_active(true); break; |
495 | 0 | case sc::OT_VALUE : m_xRbValue->set_active(true); break; |
496 | 0 | } |
497 | | |
498 | | // Model constraints |
499 | 0 | m_aConditions = m_pSolverSettings->GetConstraints(); |
500 | | |
501 | | // Loads solver engine name |
502 | | // If the solver engine in the current settings are not supported, use the first available |
503 | 0 | maEngine = m_pSolverSettings->GetParameter(sc::SP_LO_ENGINE); |
504 | 0 | if (!IsEngineAvailable(maEngine)) |
505 | 0 | { |
506 | 0 | maEngine = maImplNames[0]; |
507 | 0 | m_pSolverSettings->SetParameter(sc::SP_LO_ENGINE, maEngine); |
508 | 0 | } |
509 | | |
510 | | // Query current engine options |
511 | 0 | maProperties = ScSolverUtil::GetDefaults(maEngine); |
512 | 0 | m_pSolverSettings->GetEngineOptions(maProperties); |
513 | 0 | } |
514 | | |
515 | | // Set solver settings and save them to the file |
516 | | // But first, checks if the settings have changed |
517 | | void ScOptSolverDlg::SaveSolverSettings() |
518 | 0 | { |
519 | | // tdf#160104 If file does not have a solver model and the Solver dialog is set to its |
520 | | // default initial values (maximize is selected; no variable cells; no target value |
521 | | // and no constraints defined) then nothing needs to be saved |
522 | 0 | if (!m_pSolverSettings->TabHasSolverModel() && m_xRbMax->get_active() |
523 | 0 | && m_xEdTargetValue->GetText().isEmpty() && m_xEdVariableCells->GetText().isEmpty() |
524 | 0 | && m_aConditions.size() == 0) |
525 | 0 | return; |
526 | | |
527 | | // The current tab has a model; now we need to determined if it has been modified |
528 | 0 | bool bModified = false; |
529 | | |
530 | | // Check objective cell, objective value and variable cells |
531 | 0 | if (m_pSolverSettings->GetParameter(sc::SP_OBJ_CELL) != m_xEdObjectiveCell->GetText() |
532 | 0 | || m_pSolverSettings->GetParameter(sc::SP_OBJ_VAL) != m_xEdTargetValue->GetText() |
533 | 0 | || m_pSolverSettings->GetParameter(sc::SP_VAR_CELLS) != m_xEdVariableCells->GetText()) |
534 | 0 | bModified = true; |
535 | | |
536 | | // Check selected objective type and save it if changed |
537 | 0 | sc::ObjectiveType aType = sc::OT_MAXIMIZE; |
538 | 0 | if (m_xRbMin->get_active()) |
539 | 0 | aType = sc::OT_MINIMIZE; |
540 | 0 | else if (m_xRbValue->get_active()) |
541 | 0 | aType = sc::OT_VALUE; |
542 | |
|
543 | 0 | if (m_pSolverSettings->GetObjectiveType() != aType) |
544 | 0 | bModified = true; |
545 | | |
546 | | // Check if model constraints changed |
547 | 0 | std::vector<sc::ModelConstraint> vCurConditions = m_pSolverSettings->GetConstraints(); |
548 | 0 | if (!bModified && vCurConditions.size() != m_aConditions.size()) |
549 | 0 | bModified = true; |
550 | 0 | else |
551 | 0 | { |
552 | | // Here the size of both vectors is the same |
553 | | // Now it needs to check the contents of the constraints |
554 | 0 | for (size_t i = 0; i < vCurConditions.size(); i++) |
555 | 0 | { |
556 | 0 | if (vCurConditions[i].aLeftStr != m_aConditions[i].aLeftStr |
557 | 0 | || vCurConditions[i].nOperator != m_aConditions[i].nOperator |
558 | 0 | || vCurConditions[i].aRightStr != m_aConditions[i].aRightStr) |
559 | 0 | bModified = true; |
560 | |
|
561 | 0 | if (bModified) |
562 | 0 | break; |
563 | 0 | } |
564 | 0 | } |
565 | | |
566 | | // Check if the solver engine name and its options have changed |
567 | 0 | if (m_pSolverSettings->GetParameter(sc::SP_LO_ENGINE) != maEngine) |
568 | 0 | { |
569 | 0 | bModified = true; |
570 | 0 | } |
571 | 0 | else |
572 | 0 | { |
573 | | // The solver engine hasn't changed, so we need to check if engine options changed |
574 | | // Query current engine options; here we start by creating a copy of maProperties |
575 | | // to ensure the order is the same |
576 | 0 | css::uno::Sequence<css::beans::PropertyValue> vCurOptions(maProperties); |
577 | 0 | m_pSolverSettings->GetEngineOptions(vCurOptions); |
578 | |
|
579 | 0 | for (sal_Int32 i = 0; i < vCurOptions.getLength(); i++) |
580 | 0 | { |
581 | 0 | if (vCurOptions[i].Value != maProperties[i].Value) |
582 | 0 | { |
583 | 0 | bModified = true; |
584 | 0 | break; |
585 | 0 | } |
586 | 0 | } |
587 | 0 | } |
588 | | |
589 | | // Effectively save settings to file if modifications were made |
590 | 0 | if (bModified) |
591 | 0 | { |
592 | 0 | m_pSolverSettings->SetParameter(sc::SP_OBJ_CELL, m_xEdObjectiveCell->GetText()); |
593 | 0 | m_pSolverSettings->SetParameter(sc::SP_OBJ_VAL, m_xEdTargetValue->GetText()); |
594 | 0 | m_pSolverSettings->SetParameter(sc::SP_VAR_CELLS, m_xEdVariableCells->GetText()); |
595 | 0 | m_pSolverSettings->SetObjectiveType(aType); |
596 | 0 | m_pSolverSettings->SetConstraints(m_aConditions); |
597 | 0 | m_pSolverSettings->SetParameter(sc::SP_LO_ENGINE, maEngine); |
598 | 0 | m_pSolverSettings->SetEngineOptions(maProperties); |
599 | 0 | m_pSolverSettings->SaveSolverSettings(); |
600 | 0 | } |
601 | 0 | } |
602 | | // Test if a LO engine implementation exists |
603 | | bool ScOptSolverDlg::IsEngineAvailable(std::u16string_view sEngineName) |
604 | 0 | { |
605 | 0 | auto nIndex = comphelper::findValue(maImplNames, sEngineName); |
606 | 0 | return nIndex != -1; |
607 | 0 | } |
608 | | |
609 | | // Handler: |
610 | | |
611 | | IMPL_LINK(ScOptSolverDlg, BtnHdl, weld::Button&, rBtn, void) |
612 | 0 | { |
613 | 0 | auto xKeepAlive = shared_from_this(); |
614 | 0 | if (&rBtn == m_xBtnSolve.get() || &rBtn == m_xBtnClose.get()) |
615 | 0 | { |
616 | 0 | bool bSolve = ( &rBtn == m_xBtnSolve.get() ); |
617 | |
|
618 | 0 | SetDispatcherLock( false ); |
619 | 0 | SwitchToDocument(); |
620 | |
|
621 | 0 | bool bClose = true; |
622 | 0 | if ( bSolve ) |
623 | 0 | bClose = CallSolver(); |
624 | |
|
625 | 0 | if ( bClose ) |
626 | 0 | { |
627 | | // Close: write dialog settings to DocShell for subsequent calls |
628 | 0 | ReadConditions(); |
629 | 0 | SaveSolverSettings(); |
630 | 0 | response(RET_CLOSE); |
631 | 0 | } |
632 | 0 | else |
633 | 0 | { |
634 | | // no solution -> dialog is kept open |
635 | 0 | SetDispatcherLock( true ); |
636 | 0 | } |
637 | 0 | } |
638 | 0 | else if (&rBtn == m_xBtnOpt.get()) |
639 | 0 | { |
640 | | //! move options dialog to UI lib? |
641 | 0 | m_xOptDlg = std::make_shared<ScSolverOptionsDialog>(m_xDialog.get(), maImplNames, maDescriptions, maEngine, maProperties); |
642 | 0 | weld::DialogController::runAsync(m_xOptDlg, [this](sal_Int32 nResult){ |
643 | 0 | if (nResult == RET_OK) |
644 | 0 | { |
645 | 0 | maEngine = m_xOptDlg->GetEngine(); |
646 | 0 | maProperties = m_xOptDlg->GetProperties(); |
647 | 0 | } |
648 | 0 | m_xOptDlg.reset(); |
649 | 0 | }); |
650 | 0 | } |
651 | 0 | else if (&rBtn == m_xBtnResetAll.get()) |
652 | 0 | { |
653 | 0 | OUString sEmpty; |
654 | 0 | m_xEdObjectiveCell->SetText(sEmpty); |
655 | 0 | m_xEdTargetValue->SetText(sEmpty); |
656 | 0 | m_xEdVariableCells->SetText(sEmpty); |
657 | | |
658 | | // Get default property values of solver implementations |
659 | 0 | maEngine = maImplNames[0]; |
660 | 0 | maProperties = ScSolverUtil::GetDefaults( maEngine ); |
661 | | |
662 | | // Clear all conditions (Constraints) |
663 | 0 | m_aConditions.clear(); |
664 | 0 | ShowConditions(); |
665 | |
|
666 | 0 | m_xRbMax->set_active(true); |
667 | 0 | m_xEdObjectiveCell->GrabFocus(); |
668 | 0 | mpEdActive = m_xEdObjectiveCell.get(); |
669 | 0 | } |
670 | 0 | } |
671 | | |
672 | | IMPL_LINK( ScOptSolverDlg, GetEditFocusHdl, formula::RefEdit&, rCtrl, void ) |
673 | 0 | { |
674 | 0 | formula::RefEdit* pEdit = nullptr; |
675 | 0 | mpEdActive = nullptr; |
676 | |
|
677 | 0 | if( &rCtrl == m_xEdObjectiveCell.get() ) |
678 | 0 | pEdit = mpEdActive = m_xEdObjectiveCell.get(); |
679 | 0 | else if( &rCtrl == m_xEdTargetValue.get() ) |
680 | 0 | pEdit = mpEdActive = m_xEdTargetValue.get(); |
681 | 0 | else if( &rCtrl == m_xEdVariableCells.get() ) |
682 | 0 | pEdit = mpEdActive = m_xEdVariableCells.get(); |
683 | 0 | for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow ) |
684 | 0 | { |
685 | 0 | if( &rCtrl == mpLeftEdit[nRow] ) |
686 | 0 | pEdit = mpEdActive = mpLeftEdit[nRow]; |
687 | 0 | else if( &rCtrl == mpRightEdit[nRow] ) |
688 | 0 | pEdit = mpEdActive = mpRightEdit[nRow]; |
689 | 0 | } |
690 | |
|
691 | 0 | if( pEdit ) |
692 | 0 | pEdit->SelectAll(); |
693 | 0 | } |
694 | | |
695 | | IMPL_LINK( ScOptSolverDlg, GetButtonFocusHdl, formula::RefButton&, rCtrl, void ) |
696 | 0 | { |
697 | 0 | formula::RefEdit* pEdit = nullptr; |
698 | 0 | mpEdActive = nullptr; |
699 | |
|
700 | 0 | if( &rCtrl == m_xRBObjectiveCell.get() ) |
701 | 0 | pEdit = mpEdActive = m_xEdObjectiveCell.get(); |
702 | 0 | else if( &rCtrl == m_xRBTargetValue.get() ) |
703 | 0 | pEdit = mpEdActive = m_xEdTargetValue.get(); |
704 | 0 | else if( &rCtrl == m_xRBVariableCells.get() ) |
705 | 0 | pEdit = mpEdActive = m_xEdVariableCells.get(); |
706 | 0 | for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow ) |
707 | 0 | { |
708 | 0 | if( &rCtrl == mpLeftButton[nRow] ) |
709 | 0 | pEdit = mpEdActive = mpLeftEdit[nRow]; |
710 | 0 | else if( &rCtrl == mpRightButton[nRow] ) |
711 | 0 | pEdit = mpEdActive = mpRightEdit[nRow]; |
712 | 0 | } |
713 | |
|
714 | 0 | if( pEdit ) |
715 | 0 | pEdit->SelectAll(); |
716 | 0 | } |
717 | | |
718 | | |
719 | | IMPL_LINK(ScOptSolverDlg, GetFocusHdl, weld::Widget&, rCtrl, void) |
720 | 0 | { |
721 | 0 | if( &rCtrl == m_xRbValue.get() ) // focus on "Value of" radio button |
722 | 0 | mpEdActive = m_xEdTargetValue.get(); // use value edit for ref input, but don't change selection |
723 | 0 | else |
724 | 0 | { |
725 | 0 | for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow ) |
726 | 0 | { |
727 | 0 | if( &rCtrl == mpOperator[nRow] ) // focus on "operator" list box |
728 | 0 | mpEdActive = mpRightEdit[nRow]; // use right edit for ref input, but don't change selection |
729 | 0 | } |
730 | 0 | } |
731 | 0 | } |
732 | | |
733 | | IMPL_LINK_NOARG(ScOptSolverDlg, LoseEditFocusHdl, formula::RefEdit&, void) |
734 | 0 | { |
735 | 0 | mbDlgLostFocus = !m_xDialog->has_toplevel_focus(); |
736 | 0 | } |
737 | | |
738 | | IMPL_LINK_NOARG(ScOptSolverDlg, LoseButtonFocusHdl, formula::RefButton&, void) |
739 | 0 | { |
740 | 0 | mbDlgLostFocus = !m_xDialog->has_toplevel_focus(); |
741 | 0 | } |
742 | | |
743 | | IMPL_LINK(ScOptSolverDlg, DelBtnHdl, weld::Button&, rBtn, void) |
744 | 0 | { |
745 | 0 | for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow ) |
746 | 0 | if (&rBtn == mpDelButton[nRow]) |
747 | 0 | { |
748 | 0 | bool bHadFocus = rBtn.has_focus(); |
749 | |
|
750 | 0 | ReadConditions(); |
751 | 0 | tools::Long nVecPos = nScrollPos + nRow; |
752 | 0 | if ( nVecPos < static_cast<tools::Long>(m_aConditions.size()) ) |
753 | 0 | { |
754 | 0 | m_aConditions.erase( m_aConditions.begin() + nVecPos ); |
755 | 0 | ShowConditions(); |
756 | |
|
757 | 0 | if ( bHadFocus && !rBtn.get_sensitive() ) |
758 | 0 | { |
759 | | // If the button is disabled, focus would normally move to the next control, |
760 | | // (left edit of the next row). Move it to left edit of this row instead. |
761 | |
|
762 | 0 | mpEdActive = mpLeftEdit[nRow]; |
763 | 0 | mpEdActive->GrabFocus(); |
764 | 0 | } |
765 | 0 | } |
766 | 0 | } |
767 | 0 | } |
768 | | |
769 | | IMPL_LINK_NOARG(ScOptSolverDlg, TargetModifyHdl, formula::RefEdit&, void) |
770 | 0 | { |
771 | | // modify handler for the target edit: |
772 | | // select "Value of" if something is input into the edit |
773 | 0 | if ( !m_xEdTargetValue->GetText().isEmpty() ) |
774 | 0 | m_xRbValue->set_active(true); |
775 | 0 | } |
776 | | |
777 | | IMPL_LINK_NOARG(ScOptSolverDlg, CondModifyHdl, formula::RefEdit&, void) |
778 | 0 | { |
779 | | // modify handler for the condition edits, just to enable/disable "delete" buttons |
780 | 0 | ReadConditions(); |
781 | 0 | EnableButtons(); |
782 | 0 | } |
783 | | |
784 | | IMPL_LINK_NOARG(ScOptSolverDlg, SelectHdl, weld::ComboBox&, void) |
785 | 0 | { |
786 | | // select handler for operator list boxes, just to enable/disable "delete" buttons |
787 | 0 | ReadConditions(); |
788 | 0 | EnableButtons(); |
789 | 0 | } |
790 | | |
791 | | IMPL_LINK_NOARG(ScOptSolverDlg, ScrollHdl, weld::ScrolledWindow&, void) |
792 | 0 | { |
793 | 0 | ReadConditions(); |
794 | 0 | nScrollPos = m_xScrollBar->vadjustment_get_value(); |
795 | 0 | ShowConditions(); |
796 | 0 | if( mpEdActive ) |
797 | 0 | mpEdActive->SelectAll(); |
798 | 0 | } |
799 | | |
800 | | IMPL_LINK( ScOptSolverDlg, CursorUpHdl, ScCursorRefEdit&, rEdit, void ) |
801 | 0 | { |
802 | 0 | if ( &rEdit == mpLeftEdit[0] || &rEdit == mpRightEdit[0] ) |
803 | 0 | { |
804 | 0 | if ( nScrollPos > 0 ) |
805 | 0 | { |
806 | 0 | ReadConditions(); |
807 | 0 | --nScrollPos; |
808 | 0 | ShowConditions(); |
809 | 0 | if( mpEdActive ) |
810 | 0 | mpEdActive->SelectAll(); |
811 | 0 | } |
812 | 0 | } |
813 | 0 | else |
814 | 0 | { |
815 | 0 | formula::RefEdit* pFocus = nullptr; |
816 | 0 | for ( sal_uInt16 nRow = 1; nRow < EDIT_ROW_COUNT; ++nRow ) // second row or below: move focus |
817 | 0 | { |
818 | 0 | if ( &rEdit == mpLeftEdit[nRow] ) |
819 | 0 | pFocus = mpLeftEdit[nRow-1]; |
820 | 0 | else if ( &rEdit == mpRightEdit[nRow] ) |
821 | 0 | pFocus = mpRightEdit[nRow-1]; |
822 | 0 | } |
823 | 0 | if (pFocus) |
824 | 0 | { |
825 | 0 | mpEdActive = pFocus; |
826 | 0 | pFocus->GrabFocus(); |
827 | 0 | } |
828 | 0 | } |
829 | 0 | } |
830 | | |
831 | | IMPL_LINK( ScOptSolverDlg, CursorDownHdl, ScCursorRefEdit&, rEdit, void ) |
832 | 0 | { |
833 | 0 | if ( &rEdit == mpLeftEdit[EDIT_ROW_COUNT-1] || &rEdit == mpRightEdit[EDIT_ROW_COUNT-1] ) |
834 | 0 | { |
835 | | //! limit scroll position? |
836 | 0 | ReadConditions(); |
837 | 0 | ++nScrollPos; |
838 | 0 | ShowConditions(); |
839 | 0 | if( mpEdActive ) |
840 | 0 | mpEdActive->SelectAll(); |
841 | 0 | } |
842 | 0 | else |
843 | 0 | { |
844 | 0 | formula::RefEdit* pFocus = nullptr; |
845 | 0 | for ( sal_uInt16 nRow = 0; nRow+1 < EDIT_ROW_COUNT; ++nRow ) // before last row: move focus |
846 | 0 | { |
847 | 0 | if ( &rEdit == mpLeftEdit[nRow] ) |
848 | 0 | pFocus = mpLeftEdit[nRow+1]; |
849 | 0 | else if ( &rEdit == mpRightEdit[nRow] ) |
850 | 0 | pFocus = mpRightEdit[nRow+1]; |
851 | 0 | } |
852 | 0 | if (pFocus) |
853 | 0 | { |
854 | 0 | mpEdActive = pFocus; |
855 | 0 | pFocus->GrabFocus(); |
856 | 0 | } |
857 | 0 | } |
858 | 0 | } |
859 | | |
860 | | // Converts the position of the operator in the dropdown menu to a ConstraintOperator type |
861 | | sc::ConstraintOperator ScOptSolverDlg::OperatorIndexToConstraintOperator(sal_Int32 nIndex) |
862 | 0 | { |
863 | 0 | switch(nIndex) |
864 | 0 | { |
865 | 0 | case 0 : return sc::CO_LESS_EQUAL; break; |
866 | 0 | case 1 : return sc::CO_EQUAL; break; |
867 | 0 | case 2 : return sc::CO_GREATER_EQUAL; break; |
868 | 0 | case 3 : return sc::CO_INTEGER; break; |
869 | 0 | case 4 : return sc::CO_BINARY; break; |
870 | 0 | default : return sc::CO_LESS_EQUAL; break; |
871 | 0 | } |
872 | 0 | } |
873 | | |
874 | | void ScOptSolverDlg::ShowError( bool bCondition, formula::RefEdit* pFocus ) |
875 | 0 | { |
876 | 0 | OUString aMessage = bCondition ? maConditionError : maInputError; |
877 | 0 | std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(), |
878 | 0 | VclMessageType::Warning, VclButtonsType::Ok, |
879 | 0 | aMessage)); |
880 | 0 | xBox->run(); |
881 | 0 | if (pFocus) |
882 | 0 | { |
883 | 0 | mpEdActive = pFocus; |
884 | 0 | pFocus->GrabFocus(); |
885 | 0 | } |
886 | 0 | } |
887 | | |
888 | | bool ScOptSolverDlg::ParseRef( ScRange& rRange, const OUString& rInput, bool bAllowRange ) |
889 | 0 | { |
890 | 0 | ScAddress::Details aDetails(mrDoc.GetAddressConvention(), 0, 0); |
891 | 0 | ScRefFlags nFlags = rRange.ParseAny( rInput, mrDoc, aDetails ); |
892 | 0 | if ( nFlags & ScRefFlags::VALID ) |
893 | 0 | { |
894 | 0 | if ( (nFlags & ScRefFlags::TAB_3D) == ScRefFlags::ZERO) |
895 | 0 | rRange.aStart.SetTab( mnCurTab ); |
896 | 0 | if ( (nFlags & ScRefFlags::TAB2_3D) == ScRefFlags::ZERO) |
897 | 0 | rRange.aEnd.SetTab( rRange.aStart.Tab() ); |
898 | 0 | return ( bAllowRange || rRange.aStart == rRange.aEnd ); |
899 | 0 | } |
900 | 0 | else if ( ScRangeUtil::MakeRangeFromName( rInput, mrDoc, mnCurTab, rRange, RUTL_NAMES, aDetails ) ) |
901 | 0 | return ( bAllowRange || rRange.aStart == rRange.aEnd ); |
902 | | |
903 | 0 | return false; // not recognized |
904 | 0 | } |
905 | | |
906 | | bool ScOptSolverDlg::FindTimeout( sal_Int32& rTimeout ) |
907 | 0 | { |
908 | 0 | bool bFound = false; |
909 | |
|
910 | 0 | if ( !maProperties.hasElements() ) |
911 | 0 | maProperties = ScSolverUtil::GetDefaults( maEngine ); // get property defaults from component |
912 | |
|
913 | 0 | sal_Int32 nPropCount = maProperties.getLength(); |
914 | 0 | for (sal_Int32 nProp=0; nProp<nPropCount && !bFound; ++nProp) |
915 | 0 | { |
916 | 0 | const beans::PropertyValue& rValue = maProperties[nProp]; |
917 | 0 | if ( rValue.Name == SC_UNONAME_TIMEOUT ) |
918 | 0 | bFound = ( rValue.Value >>= rTimeout ); |
919 | 0 | } |
920 | 0 | return bFound; |
921 | 0 | } |
922 | | |
923 | | OUString ScOptSolverDlg::GetCellStrAddress(css::table::CellAddress aUnoAddress) |
924 | 0 | { |
925 | 0 | ScAddress aScAddr; |
926 | 0 | ScUnoConversion::FillScAddress(aScAddr, aUnoAddress); |
927 | 0 | ScRange aRange(aScAddr); |
928 | 0 | return aRange.Format(mrDoc, ScRefFlags::RANGE_ABS); |
929 | 0 | } |
930 | | |
931 | | bool ScOptSolverDlg::CallSolver() // return true -> close dialog after calling |
932 | 0 | { |
933 | | // show progress dialog |
934 | |
|
935 | 0 | auto xProgress = std::make_shared<ScSolverProgressDialog>(m_xDialog.get()); |
936 | 0 | sal_Int32 nTimeout = 0; |
937 | 0 | if ( FindTimeout( nTimeout ) ) |
938 | 0 | xProgress->SetTimeLimit( nTimeout ); |
939 | 0 | else |
940 | 0 | xProgress->HideTimeLimit(); |
941 | |
|
942 | 0 | weld::DialogController::runAsync(xProgress, [](sal_Int32 /*nResult*/){}); |
943 | | |
944 | | // try to make sure the progress dialog is painted before continuing |
945 | 0 | Application::Reschedule(true); |
946 | | |
947 | | // collect solver parameters |
948 | |
|
949 | 0 | ReadConditions(); |
950 | |
|
951 | 0 | rtl::Reference<ScModelObj> xDocument( mrDocShell.GetModel() ); |
952 | |
|
953 | 0 | ScRange aObjRange; |
954 | 0 | if ( !ParseRef( aObjRange, m_xEdObjectiveCell->GetText(), false ) ) |
955 | 0 | { |
956 | 0 | ShowError( false, m_xEdObjectiveCell.get() ); |
957 | 0 | return false; |
958 | 0 | } |
959 | 0 | table::CellAddress aObjective( aObjRange.aStart.Tab(), aObjRange.aStart.Col(), aObjRange.aStart.Row() ); |
960 | | |
961 | | // "changing cells" can be several ranges |
962 | 0 | ScRangeList aVarRanges; |
963 | 0 | if ( !ParseWithNames( aVarRanges, m_xEdVariableCells->GetText(), mrDoc ) ) |
964 | 0 | { |
965 | 0 | ShowError( false, m_xEdVariableCells.get() ); |
966 | 0 | return false; |
967 | 0 | } |
968 | 0 | uno::Sequence<table::CellAddress> aVariables; |
969 | 0 | sal_Int32 nVarPos = 0; |
970 | |
|
971 | 0 | for ( size_t nRangePos=0, nRange = aVarRanges.size(); nRangePos < nRange; ++nRangePos ) |
972 | 0 | { |
973 | 0 | ScRange aRange( aVarRanges[ nRangePos ] ); |
974 | 0 | aRange.PutInOrder(); |
975 | 0 | SCTAB nTab = aRange.aStart.Tab(); |
976 | | |
977 | | // resolve into single cells |
978 | |
|
979 | 0 | sal_Int32 nAdd = ( aRange.aEnd.Col() - aRange.aStart.Col() + 1 ) * |
980 | 0 | ( aRange.aEnd.Row() - aRange.aStart.Row() + 1 ); |
981 | 0 | aVariables.realloc( nVarPos + nAdd ); |
982 | 0 | auto pVariables = aVariables.getArray(); |
983 | |
|
984 | 0 | for (SCROW nRow = aRange.aStart.Row(); nRow <= aRange.aEnd.Row(); ++nRow) |
985 | 0 | for (SCCOL nCol = aRange.aStart.Col(); nCol <= aRange.aEnd.Col(); ++nCol) |
986 | 0 | pVariables[nVarPos++] = table::CellAddress( nTab, nCol, nRow ); |
987 | 0 | } |
988 | |
|
989 | 0 | uno::Sequence<sheet::SolverConstraint> aConstraints; |
990 | 0 | sal_Int32 nConstrPos = 0; |
991 | 0 | for ( const auto& rConstr : m_aConditions ) |
992 | 0 | { |
993 | 0 | if ( !rConstr.aLeftStr.isEmpty() ) |
994 | 0 | { |
995 | 0 | sheet::SolverConstraint aConstraint; |
996 | | // Order of list box entries must match enum values. |
997 | | // The enum SolverConstraintOperator starts at zero, whereas ConstraintOperator starts at 1 |
998 | | // hence we need to subtract -1 here |
999 | 0 | aConstraint.Operator = static_cast<sheet::SolverConstraintOperator>(rConstr.nOperator - 1); |
1000 | |
|
1001 | 0 | ScRange aLeftRange; |
1002 | 0 | if ( !ParseRef( aLeftRange, rConstr.aLeftStr, true ) ) |
1003 | 0 | { |
1004 | 0 | ShowError( true, nullptr ); |
1005 | 0 | return false; |
1006 | 0 | } |
1007 | | |
1008 | 0 | bool bIsRange = false; |
1009 | 0 | ScRange aRightRange; |
1010 | 0 | if ( ParseRef( aRightRange, rConstr.aRightStr, true ) ) |
1011 | 0 | { |
1012 | 0 | if ( aRightRange.aStart == aRightRange.aEnd ) |
1013 | 0 | aConstraint.Right <<= table::CellAddress( aRightRange.aStart.Tab(), |
1014 | 0 | aRightRange.aStart.Col(), aRightRange.aStart.Row() ); |
1015 | 0 | else if ( aRightRange.aEnd.Col()-aRightRange.aStart.Col() == aLeftRange.aEnd.Col()-aLeftRange.aStart.Col() && |
1016 | 0 | aRightRange.aEnd.Row()-aRightRange.aStart.Row() == aLeftRange.aEnd.Row()-aLeftRange.aStart.Row() ) |
1017 | 0 | bIsRange = true; // same size as "left" range, resolve into single cells |
1018 | 0 | else |
1019 | 0 | { |
1020 | 0 | ShowError( true, nullptr ); |
1021 | 0 | return false; |
1022 | 0 | } |
1023 | 0 | } |
1024 | 0 | else |
1025 | 0 | { |
1026 | 0 | sal_uInt32 nFormat = 0; //! explicit language? |
1027 | 0 | double fValue = 0.0; |
1028 | 0 | if ( mrDoc.GetFormatTable()->IsNumberFormat( rConstr.aRightStr, nFormat, fValue ) ) |
1029 | 0 | aConstraint.Right <<= fValue; |
1030 | 0 | else if ( aConstraint.Operator != sheet::SolverConstraintOperator_INTEGER && |
1031 | 0 | aConstraint.Operator != sheet::SolverConstraintOperator_BINARY ) |
1032 | 0 | { |
1033 | 0 | ShowError( true, nullptr ); |
1034 | 0 | return false; |
1035 | 0 | } |
1036 | 0 | } |
1037 | | |
1038 | | // resolve into single cells |
1039 | | |
1040 | 0 | sal_Int32 nAdd = ( aLeftRange.aEnd.Col() - aLeftRange.aStart.Col() + 1 ) * |
1041 | 0 | ( aLeftRange.aEnd.Row() - aLeftRange.aStart.Row() + 1 ); |
1042 | 0 | aConstraints.realloc( nConstrPos + nAdd ); |
1043 | 0 | auto pConstraints = aConstraints.getArray(); |
1044 | |
|
1045 | 0 | for (SCROW nRow = aLeftRange.aStart.Row(); nRow <= aLeftRange.aEnd.Row(); ++nRow) |
1046 | 0 | for (SCCOL nCol = aLeftRange.aStart.Col(); nCol <= aLeftRange.aEnd.Col(); ++nCol) |
1047 | 0 | { |
1048 | 0 | aConstraint.Left = table::CellAddress( aLeftRange.aStart.Tab(), nCol, nRow ); |
1049 | 0 | if ( bIsRange ) |
1050 | 0 | aConstraint.Right <<= table::CellAddress( aRightRange.aStart.Tab(), |
1051 | 0 | aRightRange.aStart.Col() + ( nCol - aLeftRange.aStart.Col() ), |
1052 | 0 | aRightRange.aStart.Row() + ( nRow - aLeftRange.aStart.Row() ) ); |
1053 | |
|
1054 | 0 | pConstraints[nConstrPos++] = aConstraint; |
1055 | 0 | } |
1056 | 0 | } |
1057 | 0 | } |
1058 | | |
1059 | 0 | bool bMaximize = m_xRbMax->get_active(); |
1060 | 0 | if ( m_xRbValue->get_active() ) |
1061 | 0 | { |
1062 | | // handle "value of" with an additional constraint (and then minimize) |
1063 | |
|
1064 | 0 | sheet::SolverConstraint aConstraint; |
1065 | 0 | aConstraint.Left = aObjective; |
1066 | 0 | aConstraint.Operator = sheet::SolverConstraintOperator_EQUAL; |
1067 | |
|
1068 | 0 | OUString aValStr = m_xEdTargetValue->GetText(); |
1069 | 0 | ScRange aRightRange; |
1070 | 0 | if ( ParseRef( aRightRange, aValStr, false ) ) |
1071 | 0 | aConstraint.Right <<= table::CellAddress( aRightRange.aStart.Tab(), |
1072 | 0 | aRightRange.aStart.Col(), aRightRange.aStart.Row() ); |
1073 | 0 | else |
1074 | 0 | { |
1075 | 0 | sal_uInt32 nFormat = 0; //! explicit language? |
1076 | 0 | double fValue = 0.0; |
1077 | 0 | if ( mrDoc.GetFormatTable()->IsNumberFormat( aValStr, nFormat, fValue ) ) |
1078 | 0 | aConstraint.Right <<= fValue; |
1079 | 0 | else |
1080 | 0 | { |
1081 | 0 | ShowError( false, m_xEdTargetValue.get() ); |
1082 | 0 | return false; |
1083 | 0 | } |
1084 | 0 | } |
1085 | | |
1086 | 0 | aConstraints.realloc( nConstrPos + 1 ); |
1087 | 0 | aConstraints.getArray()[nConstrPos++] = std::move(aConstraint); |
1088 | 0 | } |
1089 | | |
1090 | | // copy old document values |
1091 | | |
1092 | 0 | sal_Int32 nVarCount = aVariables.getLength(); |
1093 | 0 | uno::Sequence<double> aOldValues( nVarCount ); |
1094 | 0 | std::transform(std::cbegin(aVariables), std::cend(aVariables), aOldValues.getArray(), |
1095 | 0 | [this](const table::CellAddress& rVariable) -> double { |
1096 | 0 | ScAddress aCellPos; |
1097 | 0 | ScUnoConversion::FillScAddress( aCellPos, rVariable ); |
1098 | 0 | return mrDoc.GetValue( aCellPos ); |
1099 | 0 | }); |
1100 | | |
1101 | | // create and initialize solver |
1102 | |
|
1103 | 0 | uno::Reference<sheet::XSolver> xSolver = ScSolverUtil::GetSolver( maEngine ); |
1104 | 0 | OSL_ENSURE( xSolver.is(), "can't get solver component" ); |
1105 | 0 | if ( !xSolver.is() ) |
1106 | 0 | return false; |
1107 | | |
1108 | 0 | xSolver->setDocument( xDocument ); |
1109 | 0 | xSolver->setObjective( aObjective ); |
1110 | 0 | xSolver->setVariables( aVariables ); |
1111 | 0 | xSolver->setConstraints( aConstraints ); |
1112 | 0 | xSolver->setMaximize( bMaximize ); |
1113 | | |
1114 | | // set options |
1115 | 0 | uno::Reference<beans::XPropertySet> xOptProp(xSolver, uno::UNO_QUERY); |
1116 | 0 | if ( xOptProp.is() ) |
1117 | 0 | { |
1118 | 0 | for (const beans::PropertyValue& rValue : maProperties) |
1119 | 0 | { |
1120 | 0 | try |
1121 | 0 | { |
1122 | 0 | xOptProp->setPropertyValue( rValue.Name, rValue.Value ); |
1123 | 0 | } |
1124 | 0 | catch ( uno::Exception & ) |
1125 | 0 | { |
1126 | 0 | OSL_FAIL("Exception in solver option property"); |
1127 | 0 | } |
1128 | 0 | } |
1129 | 0 | } |
1130 | | |
1131 | | // tdf#162760 The solver engine may crash unexpectedly, so we need a try...catch here |
1132 | 0 | bool bSuccess(false); |
1133 | 0 | try |
1134 | 0 | { |
1135 | 0 | xSolver->solve(); |
1136 | 0 | bSuccess = xSolver->getSuccess(); |
1137 | 0 | } |
1138 | 0 | catch (const uno::RuntimeException&) |
1139 | 0 | { |
1140 | 0 | std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(), |
1141 | 0 | VclMessageType::Error, VclButtonsType::Ok, |
1142 | 0 | ScResId(STR_SOLVER_ENGINE_ERROR))); |
1143 | 0 | xBox->run(); |
1144 | 0 | } |
1145 | |
|
1146 | 0 | xProgress->response(RET_CLOSE); |
1147 | |
|
1148 | 0 | bool bClose = false; |
1149 | 0 | bool bRestore = true; // restore old values unless a solution is accepted |
1150 | 0 | if ( bSuccess ) |
1151 | 0 | { |
1152 | | // put solution into document so it is visible when asking |
1153 | 0 | uno::Sequence<double> aSolution = xSolver->getSolution(); |
1154 | 0 | if ( aSolution.getLength() == nVarCount ) |
1155 | 0 | { |
1156 | 0 | mrDocShell.LockPaint(); |
1157 | 0 | ScDocFunc &rFunc = mrDocShell.GetDocFunc(); |
1158 | 0 | for (nVarPos=0; nVarPos<nVarCount; ++nVarPos) |
1159 | 0 | { |
1160 | 0 | ScAddress aCellPos; |
1161 | 0 | ScUnoConversion::FillScAddress(aCellPos, aVariables[nVarPos]); |
1162 | 0 | rFunc.SetValueCell(aCellPos, aSolution[nVarPos], false); |
1163 | 0 | } |
1164 | 0 | mrDocShell.UnlockPaint(); |
1165 | 0 | } |
1166 | | //! else error? |
1167 | | |
1168 | | // take formatted result from document (result value from component is ignored) |
1169 | 0 | OUString aResultStr = mrDoc.GetString( |
1170 | 0 | static_cast<SCCOL>(aObjective.Column), static_cast<SCROW>(aObjective.Row), |
1171 | 0 | static_cast<SCTAB>(aObjective.Sheet)); |
1172 | |
|
1173 | 0 | ScSolverSuccessDialog aDialog(m_xDialog.get(), aResultStr); |
1174 | 0 | if (aDialog.run() == RET_OK) |
1175 | 0 | { |
1176 | | // keep results and close dialog |
1177 | 0 | bRestore = false; |
1178 | 0 | bClose = true; |
1179 | 0 | } |
1180 | 0 | } |
1181 | 0 | else |
1182 | 0 | { |
1183 | 0 | OUString aError; |
1184 | 0 | uno::Reference<sheet::XSolverDescription> xDesc( xSolver, uno::UNO_QUERY ); |
1185 | 0 | if ( xDesc.is() ) |
1186 | 0 | aError = xDesc->getStatusDescription(); // error description from component |
1187 | 0 | ScSolverNoSolutionDialog aDialog(m_xDialog.get(), aError); |
1188 | 0 | aDialog.run(); |
1189 | 0 | } |
1190 | |
|
1191 | 0 | if ( bRestore ) // restore old values |
1192 | 0 | { |
1193 | 0 | mrDocShell.LockPaint(); |
1194 | 0 | ScDocFunc &rFunc = mrDocShell.GetDocFunc(); |
1195 | 0 | for (nVarPos=0; nVarPos<nVarCount; ++nVarPos) |
1196 | 0 | { |
1197 | 0 | ScAddress aCellPos; |
1198 | 0 | ScUnoConversion::FillScAddress( aCellPos, aVariables[nVarPos] ); |
1199 | 0 | rFunc.SetValueCell(aCellPos, aOldValues[nVarPos], false); |
1200 | 0 | } |
1201 | 0 | mrDocShell.UnlockPaint(); |
1202 | 0 | } |
1203 | | |
1204 | | // Generate sensitivity report if user wants it |
1205 | 0 | uno::Reference<css::beans::XPropertySetInfo> xInfo = xOptProp->getPropertySetInfo(); |
1206 | 0 | bool bUserWantsReport = false; |
1207 | 0 | if (xInfo->hasPropertyByName("GenSensitivityReport")) |
1208 | 0 | xOptProp->getPropertyValue("GenSensitivityReport") >>= bUserWantsReport; |
1209 | |
|
1210 | 0 | if (bSuccess && bUserWantsReport) |
1211 | 0 | { |
1212 | | // Retrieve the sensitivity analysis report |
1213 | 0 | css::sheet::SensitivityReport aSensitivity; |
1214 | 0 | bool bHasReportObj = xOptProp->getPropertyValue("SensitivityReport") >>= aSensitivity; |
1215 | |
|
1216 | 0 | if (bHasReportObj && aSensitivity.HasReport) |
1217 | 0 | { |
1218 | | // Define the Tab name where the sensitivity analysis will be written to |
1219 | 0 | OUString sNewTabName; |
1220 | 0 | SCTAB nNewTab; |
1221 | 0 | mrDoc.GetName(mnCurTab, sNewTabName); |
1222 | 0 | sNewTabName += "_" + ScResId(STR_SENSITIVITY); |
1223 | | // Check if the new Tab name exists |
1224 | 0 | if (mrDoc.GetTable(sNewTabName, nNewTab)) |
1225 | 0 | { |
1226 | | // Add numbers to the end of the Tab name to make it unique |
1227 | 0 | SCTAB i = 1; |
1228 | 0 | OUString aName; |
1229 | 0 | do |
1230 | 0 | { |
1231 | 0 | i++; |
1232 | 0 | aName = sNewTabName + "_" + OUString::number(static_cast<sal_Int32>(i)); |
1233 | 0 | } |
1234 | 0 | while(mrDoc.GetTable(aName, nNewTab)); |
1235 | 0 | sNewTabName = aName; |
1236 | 0 | } |
1237 | | |
1238 | | // Insert new sheet to the document and start writing the report |
1239 | 0 | ScDocFunc &rFunc = mrDocShell.GetDocFunc(); |
1240 | 0 | rFunc.InsertTable(mnCurTab + 1, sNewTabName, false, false); |
1241 | 0 | SCTAB nReportTab; |
1242 | 0 | if (!mrDoc.GetTable(sNewTabName, nReportTab)) |
1243 | 0 | { |
1244 | 0 | SAL_WARN("sc", "Could not get the just inserted table!"); |
1245 | 0 | return false; |
1246 | 0 | } |
1247 | | |
1248 | | // Used to input data in the new sheet |
1249 | 0 | ScAddress aOutputAddress(0, 0, nReportTab); |
1250 | 0 | ScAddress::Details mAddressDetails(mrDoc, aOutputAddress); |
1251 | 0 | AddressWalkerWriter aOutput(aOutputAddress, mrDocShell, mrDoc, |
1252 | 0 | formula::FormulaGrammar::mergeToGrammar(formula::FormulaGrammar::GRAM_ENGLISH, mAddressDetails.eConv)); |
1253 | 0 | aOutput.writeBoldString(ScResId(STR_SENSITIVITY_TITLE)); |
1254 | 0 | aOutput.newLine(); |
1255 | 0 | aOutput.writeString(ScResId(STR_SOLVER_ENGINE) + " " + maEngine); |
1256 | 0 | aOutput.newLine(); |
1257 | 0 | aOutput.newLine(); |
1258 | | |
1259 | | // Objective cell section |
1260 | 0 | aOutput.writeBoldString(ScResId(STR_SENSITIVITY_OBJCELL)); |
1261 | 0 | aOutput.newLine(); |
1262 | 0 | aOutput.formatAsColumnHeader(2); |
1263 | 0 | aOutput.writeString(ScResId(STR_SENSITIVITY_CELL)); |
1264 | 0 | aOutput.nextColumn(); |
1265 | 0 | aOutput.writeString(ScResId(STR_SENSITIVITY_FINALVALUE)); |
1266 | 0 | aOutput.newLine(); |
1267 | 0 | aOutput.formatTableBottom(2); |
1268 | 0 | aOutput.writeString(GetCellStrAddress(xSolver->getObjective())); |
1269 | 0 | aOutput.nextColumn(); |
1270 | 0 | aOutput.writeValue(xSolver->getResultValue()); |
1271 | 0 | aOutput.newLine(); |
1272 | 0 | aOutput.newLine(); |
1273 | | |
1274 | | // Variable cell section |
1275 | 0 | aOutput.writeBoldString(ScResId(STR_SENSITIVITY_VARCELLS)); |
1276 | 0 | aOutput.newLine(); |
1277 | 0 | aOutput.formatAsColumnHeader(6); |
1278 | 0 | aOutput.writeString(ScResId(STR_SENSITIVITY_CELL)); |
1279 | 0 | aOutput.nextColumn(); |
1280 | 0 | aOutput.writeString(ScResId(STR_SENSITIVITY_FINALVALUE)); |
1281 | 0 | aOutput.nextColumn(); |
1282 | 0 | aOutput.writeString(ScResId(STR_SENSITIVITY_REDUCED)); |
1283 | 0 | aOutput.nextColumn(); |
1284 | 0 | aOutput.writeString(ScResId(STR_SENSITIVITY_OBJCOEFF)); |
1285 | 0 | aOutput.nextColumn(); |
1286 | 0 | aOutput.writeString(ScResId(STR_SENSITIVITY_DECREASE)); |
1287 | 0 | aOutput.nextColumn(); |
1288 | 0 | aOutput.writeString(ScResId(STR_SENSITIVITY_INCREASE)); |
1289 | 0 | aOutput.newLine(); |
1290 | |
|
1291 | 0 | uno::Sequence<double> aSolution = xSolver->getSolution(); |
1292 | 0 | uno::Sequence<double> aObjCoefficients = aSensitivity.ObjCoefficients; |
1293 | 0 | uno::Sequence<double> aObjReducedCosts = aSensitivity.ObjReducedCosts; |
1294 | 0 | uno::Sequence<double> aObjAllowableDecreases = aSensitivity.ObjAllowableDecreases; |
1295 | 0 | uno::Sequence<double> aObjAllowableIncreases = aSensitivity.ObjAllowableIncreases; |
1296 | 0 | sal_Int32 nRows = aVariables.getLength(); |
1297 | 0 | for (sal_Int32 i = 0; i < nRows; i++) |
1298 | 0 | { |
1299 | 0 | if (i == nRows - 1) |
1300 | 0 | aOutput.formatTableBottom(6); |
1301 | 0 | aOutput.writeString(GetCellStrAddress(aVariables[i])); |
1302 | 0 | aOutput.nextColumn(); |
1303 | 0 | aOutput.writeValue(aSolution[i]); |
1304 | 0 | aOutput.nextColumn(); |
1305 | 0 | aOutput.writeValue(aObjReducedCosts[i]); |
1306 | 0 | aOutput.nextColumn(); |
1307 | 0 | aOutput.writeValue(aObjCoefficients[i]); |
1308 | 0 | aOutput.nextColumn(); |
1309 | 0 | aOutput.writeValue(aObjAllowableDecreases[i]); |
1310 | 0 | aOutput.nextColumn(); |
1311 | 0 | aOutput.writeValue(aObjAllowableIncreases[i]); |
1312 | 0 | aOutput.newLine(); |
1313 | 0 | } |
1314 | 0 | aOutput.newLine(); |
1315 | | |
1316 | | // Constraints section |
1317 | 0 | aOutput.writeBoldString(ScResId(STR_SENSITIVITY_CONSTRAINTS)); |
1318 | 0 | aOutput.newLine(); |
1319 | 0 | aOutput.formatAsColumnHeader(6); |
1320 | 0 | aOutput.writeString(ScResId(STR_SENSITIVITY_CELL)); |
1321 | 0 | aOutput.nextColumn(); |
1322 | 0 | aOutput.writeString(ScResId(STR_SENSITIVITY_FINALVALUE)); |
1323 | 0 | aOutput.nextColumn(); |
1324 | 0 | aOutput.writeString(ScResId(STR_SENSITIVITY_SHADOWPRICE)); |
1325 | 0 | aOutput.nextColumn(); |
1326 | 0 | aOutput.writeString(ScResId(STR_SENSITIVITY_RHS)); |
1327 | 0 | aOutput.nextColumn(); |
1328 | 0 | aOutput.writeString(ScResId(STR_SENSITIVITY_DECREASE)); |
1329 | 0 | aOutput.nextColumn(); |
1330 | 0 | aOutput.writeString(ScResId(STR_SENSITIVITY_INCREASE)); |
1331 | 0 | aOutput.newLine(); |
1332 | |
|
1333 | 0 | uno::Sequence<double> aConstrValues = aSensitivity.ConstrValues; |
1334 | 0 | uno::Sequence<double> aConstrRHS = aSensitivity.ConstrRHS; |
1335 | 0 | uno::Sequence<double> aConstrShadowPrices = aSensitivity.ConstrShadowPrices; |
1336 | 0 | uno::Sequence<double> aConstrAllowableDecreases = aSensitivity.ConstrAllowableDecreases; |
1337 | 0 | uno::Sequence<double> aConstrAllowableIncreases = aSensitivity.ConstrAllowableIncreases; |
1338 | 0 | nRows = aConstraints.getLength(); |
1339 | 0 | for (sal_Int32 i = 0; i < nRows; i++) |
1340 | 0 | { |
1341 | 0 | if (i == nRows - 1) |
1342 | 0 | aOutput.formatTableBottom(6); |
1343 | 0 | aOutput.writeString(GetCellStrAddress(aConstraints[i].Left)); |
1344 | 0 | aOutput.nextColumn(); |
1345 | 0 | aOutput.writeValue(aConstrValues[i]); |
1346 | 0 | aOutput.nextColumn(); |
1347 | 0 | aOutput.writeValue(aConstrShadowPrices[i]); |
1348 | 0 | aOutput.nextColumn(); |
1349 | 0 | aOutput.writeValue(aConstrRHS[i]); |
1350 | 0 | aOutput.nextColumn(); |
1351 | 0 | aOutput.writeValue(aConstrAllowableDecreases[i]); |
1352 | 0 | aOutput.nextColumn(); |
1353 | 0 | aOutput.writeValue(aConstrAllowableIncreases[i]); |
1354 | 0 | aOutput.newLine(); |
1355 | 0 | } |
1356 | | |
1357 | | // Disable grid lines in the sensitivity report |
1358 | 0 | if (ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell()) |
1359 | 0 | { |
1360 | 0 | ScViewData& rData = pViewSh->GetViewData(); |
1361 | 0 | rData.SetTabNo(nReportTab); |
1362 | 0 | rData.SetShowGrid(false); |
1363 | 0 | rData.SetTabNo(mnCurTab); |
1364 | 0 | } |
1365 | 0 | } |
1366 | 0 | } |
1367 | | |
1368 | 0 | return bClose; |
1369 | 0 | } |
1370 | | |
1371 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |