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