1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sc.hxx"
26
27 //----------------------------------------------------------------------------
28
29 #include "rangelst.hxx"
30 #include "scitems.hxx"
31 #include <sfx2/bindings.hxx>
32 #include <sfx2/imagemgr.hxx>
33 #include <svl/zforlist.hxx>
34 #include <vcl/msgbox.hxx>
35 #include <vcl/svapp.hxx>
36
37 #include "uiitems.hxx"
38 #include "reffact.hxx"
39 #include "docsh.hxx"
40 #include "docfunc.hxx"
41 #include "cell.hxx"
42 #include "rangeutl.hxx"
43 #include "scresid.hxx"
44 #include "convuno.hxx"
45 #include "unonames.hxx"
46 #include "solveroptions.hxx"
47 #include "solverutil.hxx"
48 #include "optsolver.hrc"
49
50 #include "optsolver.hxx"
51
52 #include <com/sun/star/sheet/Solver.hpp>
53 #include <com/sun/star/sheet/XSolverDescription.hpp>
54
55 using namespace com::sun::star;
56
57 //----------------------------------------------------------------------------
58
ScSolverProgressDialog(Window * pParent)59 ScSolverProgressDialog::ScSolverProgressDialog( Window* pParent )
60 : ModelessDialog( pParent, ScResId( RID_SCDLG_SOLVER_PROGRESS ) ),
61 maFtProgress ( this, ScResId( FT_PROGRESS ) ),
62 maFtTime ( this, ScResId( FT_TIMELIMIT ) ),
63 maFlButtons ( this, ScResId( FL_BUTTONS ) ),
64 maBtnOk ( this, ScResId( BTN_OK ) )
65 {
66 maBtnOk.Enable(sal_False);
67 FreeResource();
68 }
69
~ScSolverProgressDialog()70 ScSolverProgressDialog::~ScSolverProgressDialog()
71 {
72 }
73
HideTimeLimit()74 void ScSolverProgressDialog::HideTimeLimit()
75 {
76 maFtTime.Hide();
77 }
78
SetTimeLimit(sal_Int32 nSeconds)79 void ScSolverProgressDialog::SetTimeLimit( sal_Int32 nSeconds )
80 {
81 String aOld = maFtTime.GetText();
82 String aNew = aOld.GetToken(0,'#');
83 aNew += String::CreateFromInt32( nSeconds );
84 aNew += aOld.GetToken(1,'#');
85 maFtTime.SetText( aNew );
86 }
87
88 //----------------------------------------------------------------------------
89
ScSolverNoSolutionDialog(Window * pParent,const String & rErrorText)90 ScSolverNoSolutionDialog::ScSolverNoSolutionDialog( Window* pParent, const String& rErrorText )
91 : ModalDialog( pParent, ScResId( RID_SCDLG_SOLVER_NOSOLUTION ) ),
92 maFtNoSolution ( this, ScResId( FT_NOSOLUTION ) ),
93 maFtErrorText ( this, ScResId( FT_ERRORTEXT ) ),
94 maFlButtons ( this, ScResId( FL_BUTTONS ) ),
95 maBtnOk ( this, ScResId( BTN_OK ) )
96 {
97 maFtErrorText.SetText( rErrorText );
98 FreeResource();
99 }
100
~ScSolverNoSolutionDialog()101 ScSolverNoSolutionDialog::~ScSolverNoSolutionDialog()
102 {
103 }
104
105 //----------------------------------------------------------------------------
106
ScSolverSuccessDialog(Window * pParent,const String & rSolution)107 ScSolverSuccessDialog::ScSolverSuccessDialog( Window* pParent, const String& rSolution )
108 : ModalDialog( pParent, ScResId( RID_SCDLG_SOLVER_SUCCESS ) ),
109 maFtSuccess ( this, ScResId( FT_SUCCESS ) ),
110 maFtResult ( this, ScResId( FT_RESULT ) ),
111 maFtQuestion ( this, ScResId( FT_QUESTION ) ),
112 maFlButtons ( this, ScResId( FL_BUTTONS ) ),
113 maBtnOk ( this, ScResId( BTN_OK ) ),
114 maBtnCancel ( this, ScResId( BTN_CANCEL ) )
115 {
116 String aMessage = maFtResult.GetText();
117 aMessage.Append( (sal_Char) ' ' );
118 aMessage.Append( rSolution );
119 maFtResult.SetText( aMessage );
120 FreeResource();
121 }
122
~ScSolverSuccessDialog()123 ScSolverSuccessDialog::~ScSolverSuccessDialog()
124 {
125 }
126
127 //----------------------------------------------------------------------------
128
ScCursorRefEdit(ScAnyRefDlg * pParent,const ResId & rResId)129 ScCursorRefEdit::ScCursorRefEdit( ScAnyRefDlg* pParent, const ResId& rResId ) :
130 formula::RefEdit( pParent, pParent, rResId )
131 {
132 }
133
SetCursorLinks(const Link & rUp,const Link & rDown)134 void ScCursorRefEdit::SetCursorLinks( const Link& rUp, const Link& rDown )
135 {
136 maCursorUpLink = rUp;
137 maCursorDownLink = rDown;
138 }
139
KeyInput(const KeyEvent & rKEvt)140 void ScCursorRefEdit::KeyInput( const KeyEvent& rKEvt )
141 {
142 KeyCode aCode = rKEvt.GetKeyCode();
143 bool bUp = (aCode.GetCode() == KEY_UP);
144 bool bDown = (aCode.GetCode() == KEY_DOWN);
145 if ( !aCode.IsShift() && !aCode.IsMod1() && !aCode.IsMod2() && ( bUp || bDown ) )
146 {
147 if ( bUp )
148 maCursorUpLink.Call( this );
149 else
150 maCursorDownLink.Call( this );
151 }
152 else
153 formula::RefEdit::KeyInput( rKEvt );
154 }
155
156 //----------------------------------------------------------------------------
157
ScOptSolverSave(const String & rObjective,sal_Bool bMax,sal_Bool bMin,sal_Bool bValue,const String & rTarget,const String & rVariable,const std::vector<ScOptConditionRow> & rConditions,const String & rEngine,const uno::Sequence<beans::PropertyValue> & rProperties)158 ScOptSolverSave::ScOptSolverSave( const String& rObjective, sal_Bool bMax, sal_Bool bMin, sal_Bool bValue,
159 const String& rTarget, const String& rVariable,
160 const std::vector<ScOptConditionRow>& rConditions,
161 const String& rEngine,
162 const uno::Sequence<beans::PropertyValue>& rProperties ) :
163 maObjective( rObjective ),
164 mbMax( bMax ),
165 mbMin( bMin ),
166 mbValue( bValue ),
167 maTarget( rTarget ),
168 maVariable( rVariable ),
169 maConditions( rConditions ),
170 maEngine( rEngine ),
171 maProperties( rProperties )
172 {
173 }
174
175 //============================================================================
176 // class ScOptSolverDlg
177 //----------------------------------------------------------------------------
178
ScOptSolverDlg(SfxBindings * pB,SfxChildWindow * pCW,Window * pParent,ScDocShell * pDocSh,ScAddress aCursorPos)179 ScOptSolverDlg::ScOptSolverDlg( SfxBindings* pB, SfxChildWindow* pCW, Window* pParent,
180 ScDocShell* pDocSh, ScAddress aCursorPos )
181
182 : ScAnyRefDlg ( pB, pCW, pParent, RID_SCDLG_OPTSOLVER ),
183 //
184 maFtObjectiveCell ( this, ScResId( FT_OBJECTIVECELL ) ),
185 maEdObjectiveCell ( this, this, ScResId( ED_OBJECTIVECELL ) ),
186 maRBObjectiveCell ( this, ScResId( IB_OBJECTIVECELL ), &maEdObjectiveCell, this ),
187 maFtDirection ( this, ScResId( FT_DIRECTION ) ),
188 maRbMax ( this, ScResId( RB_MAX ) ),
189 maRbMin ( this, ScResId( RB_MIN ) ),
190 maRbValue ( this, ScResId( RB_VALUE ) ),
191 maEdTargetValue ( this, this, ScResId( ED_TARGET ) ),
192 maRBTargetValue ( this, ScResId( IB_TARGET ), &maEdTargetValue, this ),
193 maFtVariableCells ( this, ScResId( FT_VARIABLECELLS ) ),
194 maEdVariableCells ( this, this, ScResId( ED_VARIABLECELLS ) ),
195 maRBVariableCells ( this, ScResId( IB_VARIABLECELLS ), &maEdVariableCells, this),
196 maFlConditions ( this, ScResId( FL_CONDITIONS ) ),
197 maFtCellRef ( this, ScResId( FT_CELLREF ) ),
198 maEdLeft1 ( this, ScResId( ED_LEFT1 ) ),
199 maRBLeft1 ( this, ScResId( IB_LEFT1 ), &maEdLeft1, this ),
200 maFtOperator ( this, ScResId( FT_OPERATOR ) ),
201 maLbOp1 ( this, ScResId( LB_OP1 ) ),
202 maFtConstraint ( this, ScResId( FT_CONSTRAINT ) ),
203 maEdRight1 ( this, ScResId( ED_RIGHT1 ) ),
204 maRBRight1 ( this, ScResId( IB_RIGHT1 ), &maEdRight1, this ),
205 maBtnDel1 ( this, ScResId( IB_DELETE1 ) ),
206 maEdLeft2 ( this, ScResId( ED_LEFT2 ) ),
207 maRBLeft2 ( this, ScResId( IB_LEFT2 ), &maEdLeft2, this ),
208 maLbOp2 ( this, ScResId( LB_OP2 ) ),
209 maEdRight2 ( this, ScResId( ED_RIGHT2 ) ),
210 maRBRight2 ( this, ScResId( IB_RIGHT2 ), &maEdRight2, this ),
211 maBtnDel2 ( this, ScResId( IB_DELETE2 ) ),
212 maEdLeft3 ( this, ScResId( ED_LEFT3 ) ),
213 maRBLeft3 ( this, ScResId( IB_LEFT3 ), &maEdLeft3, this ),
214 maLbOp3 ( this, ScResId( LB_OP3 ) ),
215 maEdRight3 ( this, ScResId( ED_RIGHT3 ) ),
216 maRBRight3 ( this, ScResId( IB_RIGHT3 ), &maEdRight3, this ),
217 maBtnDel3 ( this, ScResId( IB_DELETE3 ) ),
218 maEdLeft4 ( this, ScResId( ED_LEFT4 ) ),
219 maRBLeft4 ( this, ScResId( IB_LEFT4 ), &maEdLeft4, this ),
220 maLbOp4 ( this, ScResId( LB_OP4 ) ),
221 maEdRight4 ( this, ScResId( ED_RIGHT4 ) ),
222 maRBRight4 ( this, ScResId( IB_RIGHT4 ), &maEdRight4, this ),
223 maBtnDel4 ( this, ScResId( IB_DELETE4 ) ),
224 maScrollBar ( this, ScResId( SB_SCROLL ) ),
225 maFlButtons ( this, ScResId( FL_BUTTONS ) ),
226 maBtnOpt ( this, ScResId( BTN_OPTIONS ) ),
227 maBtnHelp ( this, ScResId( BTN_HELP ) ),
228 maBtnCancel ( this, ScResId( BTN_CLOSE ) ),
229 maBtnSolve ( this, ScResId( BTN_SOLVE ) ),
230 maInputError ( ScResId( STR_INVALIDINPUT ) ),
231 maConditionError ( ScResId( STR_INVALIDCONDITION ) ),
232 //
233 mpDocShell ( pDocSh ),
234 mpDoc ( pDocSh->GetDocument() ),
235 mnCurTab ( aCursorPos.Tab() ),
236 mpEdActive ( NULL ),
237 mbDlgLostFocus ( false ),
238 nScrollPos ( 0 )
239 {
240 mpLeftEdit[0] = &maEdLeft1;
241 mpLeftButton[0] = &maRBLeft1;
242 mpRightEdit[0] = &maEdRight1;
243 mpRightButton[0] = &maRBRight1;
244 mpOperator[0] = &maLbOp1;
245 mpDelButton[0] = &maBtnDel1;
246
247 mpLeftEdit[1] = &maEdLeft2;
248 mpLeftButton[1] = &maRBLeft2;
249 mpRightEdit[1] = &maEdRight2;
250 mpRightButton[1] = &maRBRight2;
251 mpOperator[1] = &maLbOp2;
252 mpDelButton[1] = &maBtnDel2;
253
254 mpLeftEdit[2] = &maEdLeft3;
255 mpLeftButton[2] = &maRBLeft3;
256 mpRightEdit[2] = &maEdRight3;
257 mpRightButton[2] = &maRBRight3;
258 mpOperator[2] = &maLbOp3;
259 mpDelButton[2] = &maBtnDel3;
260
261 mpLeftEdit[3] = &maEdLeft4;
262 mpLeftButton[3] = &maRBLeft4;
263 mpRightEdit[3] = &maEdRight4;
264 mpRightButton[3] = &maRBRight4;
265 mpOperator[3] = &maLbOp4;
266 mpDelButton[3] = &maBtnDel4;
267
268 maRbMax.SetAccessibleRelationMemberOf(&maFtDirection);
269 maRbMin.SetAccessibleRelationMemberOf(&maFtDirection);
270 maRbValue.SetAccessibleRelationMemberOf(&maFtDirection);
271 maEdLeft2.SetAccessibleName(maFtCellRef.GetText());
272 maLbOp2.SetAccessibleName(maFtOperator.GetText());
273 maEdRight2.SetAccessibleName(maFtConstraint.GetText());
274 maEdLeft3.SetAccessibleName(maFtCellRef.GetText());
275 maLbOp3.SetAccessibleName(maFtOperator.GetText());
276 maEdRight3.SetAccessibleName(maFtConstraint.GetText());
277 maEdLeft4.SetAccessibleName(maFtCellRef.GetText());
278 maLbOp4.SetAccessibleName(maFtOperator.GetText());
279 maEdRight4.SetAccessibleName(maFtConstraint.GetText());
280
281 Init( aCursorPos );
282 FreeResource();
283 }
284
285 //----------------------------------------------------------------------------
286
~ScOptSolverDlg()287 ScOptSolverDlg::~ScOptSolverDlg()
288 {
289 }
290
291 //----------------------------------------------------------------------------
292
Init(const ScAddress & rCursorPos)293 void ScOptSolverDlg::Init(const ScAddress& rCursorPos)
294 {
295 // Get the "Delete Rows" commandimagelist images from sfx instead of
296 // adding a second copy to sc (see ScTbxInsertCtrl::StateChanged)
297
298 rtl::OUString aSlotURL( RTL_CONSTASCII_USTRINGPARAM( "slot:" ));
299 aSlotURL += rtl::OUString::valueOf( sal_Int32( SID_DEL_ROWS ) );
300 uno::Reference<frame::XFrame> xFrame = GetBindings().GetActiveFrame();
301 Image aDelNm = ::GetImage( xFrame, aSlotURL, sal_False, sal_False );
302 Image aDelHC = ::GetImage( xFrame, aSlotURL, sal_False, sal_True ); // high contrast
303
304 for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow )
305 {
306 mpDelButton[nRow]->SetModeImage( aDelNm, BMP_COLOR_NORMAL );
307 mpDelButton[nRow]->SetModeImage( aDelHC, BMP_COLOR_HIGHCONTRAST );
308 }
309
310 maBtnOpt.SetClickHdl( LINK( this, ScOptSolverDlg, BtnHdl ) );
311 maBtnCancel.SetClickHdl( LINK( this, ScOptSolverDlg, BtnHdl ) );
312 maBtnSolve.SetClickHdl( LINK( this, ScOptSolverDlg, BtnHdl ) );
313
314 Link aLink = LINK( this, ScOptSolverDlg, GetFocusHdl );
315 maEdObjectiveCell.SetGetFocusHdl( aLink );
316 maRBObjectiveCell.SetGetFocusHdl( aLink );
317 maEdTargetValue.SetGetFocusHdl( aLink );
318 maRBTargetValue.SetGetFocusHdl( aLink );
319 maEdVariableCells.SetGetFocusHdl( aLink );
320 maRBVariableCells.SetGetFocusHdl( aLink );
321 maRbValue.SetGetFocusHdl( aLink );
322 for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow )
323 {
324 mpLeftEdit[nRow]->SetGetFocusHdl( aLink );
325 mpLeftButton[nRow]->SetGetFocusHdl( aLink );
326 mpRightEdit[nRow]->SetGetFocusHdl( aLink );
327 mpRightButton[nRow]->SetGetFocusHdl( aLink );
328 mpOperator[nRow]->SetGetFocusHdl( aLink );
329 }
330
331 aLink = LINK( this, ScOptSolverDlg, LoseFocusHdl );
332 maEdObjectiveCell.SetLoseFocusHdl( aLink );
333 maRBObjectiveCell.SetLoseFocusHdl( aLink );
334 maEdTargetValue. SetLoseFocusHdl( aLink );
335 maRBTargetValue. SetLoseFocusHdl( aLink );
336 maEdVariableCells.SetLoseFocusHdl( aLink );
337 maRBVariableCells.SetLoseFocusHdl( aLink );
338 for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow )
339 {
340 mpLeftEdit[nRow]->SetLoseFocusHdl( aLink );
341 mpLeftButton[nRow]->SetLoseFocusHdl( aLink );
342 mpRightEdit[nRow]->SetLoseFocusHdl( aLink );
343 mpRightButton[nRow]->SetLoseFocusHdl( aLink );
344 }
345
346 Link aCursorUp = LINK( this, ScOptSolverDlg, CursorUpHdl );
347 Link aCursorDown = LINK( this, ScOptSolverDlg, CursorDownHdl );
348 Link aCondModify = LINK( this, ScOptSolverDlg, CondModifyHdl );
349 for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow )
350 {
351 mpLeftEdit[nRow]->SetCursorLinks( aCursorUp, aCursorDown );
352 mpRightEdit[nRow]->SetCursorLinks( aCursorUp, aCursorDown );
353 mpLeftEdit[nRow]->SetModifyHdl( aCondModify );
354 mpRightEdit[nRow]->SetModifyHdl( aCondModify );
355 mpDelButton[nRow]->SetClickHdl( LINK( this, ScOptSolverDlg, DelBtnHdl ) );
356 mpOperator[nRow]->SetSelectHdl( LINK( this, ScOptSolverDlg, SelectHdl ) );
357 }
358 maEdTargetValue.SetModifyHdl( LINK( this, ScOptSolverDlg, TargetModifyHdl ) );
359
360 maScrollBar.SetEndScrollHdl( LINK( this, ScOptSolverDlg, ScrollHdl ) );
361 maScrollBar.SetScrollHdl( LINK( this, ScOptSolverDlg, ScrollHdl ) );
362
363 maScrollBar.SetPageSize( EDIT_ROW_COUNT );
364 maScrollBar.SetVisibleSize( EDIT_ROW_COUNT );
365 maScrollBar.SetLineSize( 1 );
366 // Range is set in ShowConditions
367
368 // get available solver implementations
369 //! sort by descriptions?
370 ScSolverUtil::GetImplementations( maImplNames, maDescriptions );
371 sal_Int32 nImplCount = maImplNames.getLength();
372
373 const ScOptSolverSave* pOldData = mpDocShell->GetSolverSaveData();
374 if ( pOldData )
375 {
376 maEdObjectiveCell.SetRefString( pOldData->GetObjective() );
377 maRbMax.Check( pOldData->GetMax() );
378 maRbMin.Check( pOldData->GetMin() );
379 maRbValue.Check( pOldData->GetValue() );
380 maEdTargetValue.SetRefString( pOldData->GetTarget() );
381 maEdVariableCells.SetRefString( pOldData->GetVariable() );
382 maConditions = pOldData->GetConditions();
383 maEngine = pOldData->GetEngine();
384 maProperties = pOldData->GetProperties();
385 }
386 else
387 {
388 maRbMax.Check();
389 String aCursorStr;
390 if ( !mpDoc->GetRangeAtBlock( ScRange(rCursorPos), &aCursorStr ) )
391 rCursorPos.Format( aCursorStr, SCA_ABS, NULL, mpDoc->GetAddressConvention() );
392 maEdObjectiveCell.SetRefString( aCursorStr );
393 if ( nImplCount > 0 )
394 maEngine = maImplNames[0]; // use first implementation
395 }
396 ShowConditions();
397
398 maEdObjectiveCell.GrabFocus();
399 mpEdActive = &maEdObjectiveCell;
400 }
401
402 //----------------------------------------------------------------------------
403
ReadConditions()404 void ScOptSolverDlg::ReadConditions()
405 {
406 for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow )
407 {
408 ScOptConditionRow aRowEntry;
409 aRowEntry.aLeftStr = mpLeftEdit[nRow]->GetText();
410 aRowEntry.aRightStr = mpRightEdit[nRow]->GetText();
411 aRowEntry.nOperator = mpOperator[nRow]->GetSelectEntryPos();
412
413 long nVecPos = nScrollPos + nRow;
414 if ( nVecPos >= (long)maConditions.size() && !aRowEntry.IsDefault() )
415 maConditions.resize( nVecPos + 1 );
416
417 if ( nVecPos < (long)maConditions.size() )
418 maConditions[nVecPos] = aRowEntry;
419
420 // remove default entries at the end
421 size_t nSize = maConditions.size();
422 while ( nSize > 0 && maConditions[ nSize-1 ].IsDefault() )
423 --nSize;
424 maConditions.resize( nSize );
425 }
426 }
427
ShowConditions()428 void ScOptSolverDlg::ShowConditions()
429 {
430 for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow )
431 {
432 ScOptConditionRow aRowEntry;
433
434 long nVecPos = nScrollPos + nRow;
435 if ( nVecPos < (long)maConditions.size() )
436 aRowEntry = maConditions[nVecPos];
437
438 mpLeftEdit[nRow]->SetRefString( aRowEntry.aLeftStr );
439 mpRightEdit[nRow]->SetRefString( aRowEntry.aRightStr );
440 mpOperator[nRow]->SelectEntryPos( aRowEntry.nOperator );
441 }
442
443 // allow to scroll one page behind the visible or stored rows
444 long nVisible = nScrollPos + EDIT_ROW_COUNT;
445 long nMax = std::max( nVisible, (long) maConditions.size() );
446 maScrollBar.SetRange( Range( 0, nMax + EDIT_ROW_COUNT ) );
447 maScrollBar.SetThumbPos( nScrollPos );
448
449 EnableButtons();
450 }
451
EnableButtons()452 void ScOptSolverDlg::EnableButtons()
453 {
454 for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow )
455 {
456 long nVecPos = nScrollPos + nRow;
457 mpDelButton[nRow]->Enable( nVecPos < (long)maConditions.size() );
458 }
459 }
460
461 //----------------------------------------------------------------------------
462
Close()463 sal_Bool ScOptSolverDlg::Close()
464 {
465 return DoClose( ScOptSolverDlgWrapper::GetChildWindowId() );
466 }
467
468 //----------------------------------------------------------------------------
469
SetActive()470 void ScOptSolverDlg::SetActive()
471 {
472 if ( mbDlgLostFocus )
473 {
474 mbDlgLostFocus = false;
475 if( mpEdActive )
476 mpEdActive->GrabFocus();
477 }
478 else
479 {
480 GrabFocus();
481 }
482 RefInputDone();
483 }
484
485 //----------------------------------------------------------------------------
486
SetReference(const ScRange & rRef,ScDocument * pDocP)487 void ScOptSolverDlg::SetReference( const ScRange& rRef, ScDocument* pDocP )
488 {
489 if( mpEdActive )
490 {
491 if ( rRef.aStart != rRef.aEnd )
492 RefInputStart(mpEdActive);
493
494 // "target"/"value": single cell
495 bool bSingle = ( mpEdActive == &maEdObjectiveCell || mpEdActive == &maEdTargetValue );
496
497 String aStr;
498 ScAddress aAdr = rRef.aStart;
499 ScRange aNewRef( rRef );
500 if ( bSingle )
501 aNewRef.aEnd = aAdr;
502
503 String aName;
504 if ( pDocP->GetRangeAtBlock( aNewRef, &aName ) ) // named range: show name
505 aStr = aName;
506 else // format cell/range reference
507 {
508 sal_uInt16 nFmt = ( aAdr.Tab() == mnCurTab ) ? SCA_ABS : SCA_ABS_3D;
509 if ( bSingle )
510 aAdr.Format( aStr, nFmt, pDocP, pDocP->GetAddressConvention() );
511 else
512 rRef.Format( aStr, nFmt | SCR_ABS, pDocP, pDocP->GetAddressConvention() );
513 }
514
515 // variable cells can be several ranges, so only the selection is replaced
516 if ( mpEdActive == &maEdVariableCells )
517 {
518 String aVal = mpEdActive->GetText();
519 Selection aSel = mpEdActive->GetSelection();
520 aSel.Justify();
521 aVal.Erase( (xub_StrLen)aSel.Min(), (xub_StrLen)aSel.Len() );
522 aVal.Insert( aStr, (xub_StrLen)aSel.Min() );
523 Selection aNewSel( aSel.Min(), aSel.Min()+aStr.Len() );
524 mpEdActive->SetRefString( aVal );
525 mpEdActive->SetSelection( aNewSel );
526 }
527 else
528 mpEdActive->SetRefString( aStr );
529
530 ReadConditions();
531 EnableButtons();
532
533 // select "Value of" if a ref is input into "target" edit
534 if ( mpEdActive == &maEdTargetValue )
535 maRbValue.Check();
536 }
537 }
538
539 //----------------------------------------------------------------------------
540
IsRefInputMode() const541 sal_Bool ScOptSolverDlg::IsRefInputMode() const
542 {
543 return mpEdActive != NULL;
544 }
545
546 //----------------------------------------------------------------------------
547 // Handler:
548
IMPL_LINK(ScOptSolverDlg,BtnHdl,PushButton *,pBtn)549 IMPL_LINK( ScOptSolverDlg, BtnHdl, PushButton*, pBtn )
550 {
551 if ( pBtn == &maBtnSolve || pBtn == &maBtnCancel )
552 {
553 bool bSolve = ( pBtn == &maBtnSolve );
554
555 SetDispatcherLock( sal_False );
556 SwitchToDocument();
557
558 bool bClose = true;
559 if ( bSolve )
560 bClose = CallSolver();
561
562 if ( bClose )
563 {
564 // Close: write dialog settings to DocShell for subsequent calls
565 ReadConditions();
566 ScOptSolverSave aSave(
567 maEdObjectiveCell.GetText(), maRbMax.IsChecked(), maRbMin.IsChecked(), maRbValue.IsChecked(),
568 maEdTargetValue.GetText(), maEdVariableCells.GetText(), maConditions, maEngine, maProperties );
569 mpDocShell->SetSolverSaveData( aSave );
570 Close();
571 }
572 else
573 {
574 // no solution -> dialog is kept open
575 SetDispatcherLock( sal_True );
576 }
577 }
578 else if ( pBtn == &maBtnOpt )
579 {
580 //! move options dialog to UI lib?
581 ScSolverOptionsDialog* pOptDlg =
582 new ScSolverOptionsDialog( this, maImplNames, maDescriptions, maEngine, maProperties );
583 if ( pOptDlg->Execute() == RET_OK )
584 {
585 maEngine = pOptDlg->GetEngine();
586 maProperties = pOptDlg->GetProperties();
587 }
588 delete pOptDlg;
589 }
590
591 return 0;
592 }
593
594 //----------------------------------------------------------------------------
595
IMPL_LINK(ScOptSolverDlg,GetFocusHdl,Control *,pCtrl)596 IMPL_LINK( ScOptSolverDlg, GetFocusHdl, Control*, pCtrl )
597 {
598 Edit* pEdit = NULL;
599 mpEdActive = NULL;
600
601 if( pCtrl == &maEdObjectiveCell || pCtrl == &maRBObjectiveCell )
602 pEdit = mpEdActive = &maEdObjectiveCell;
603 else if( pCtrl == &maEdTargetValue || pCtrl == &maRBTargetValue )
604 pEdit = mpEdActive = &maEdTargetValue;
605 else if( pCtrl == &maEdVariableCells || pCtrl == &maRBVariableCells )
606 pEdit = mpEdActive = &maEdVariableCells;
607 for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow )
608 {
609 if( pCtrl == mpLeftEdit[nRow] || pCtrl == mpLeftButton[nRow] )
610 pEdit = mpEdActive = mpLeftEdit[nRow];
611 else if( pCtrl == mpRightEdit[nRow] || pCtrl == mpRightButton[nRow] )
612 pEdit = mpEdActive = mpRightEdit[nRow];
613 else if( pCtrl == mpOperator[nRow] ) // focus on "operator" list box
614 mpEdActive = mpRightEdit[nRow]; // use right edit for ref input, but don't change selection
615 }
616 if( pCtrl == &maRbValue ) // focus on "Value of" radio button
617 mpEdActive = &maEdTargetValue; // use value edit for ref input, but don't change selection
618
619 if( pEdit )
620 pEdit->SetSelection( Selection( 0, SELECTION_MAX ) );
621
622 return 0;
623 }
624
625 //----------------------------------------------------------------------------
626
IMPL_LINK(ScOptSolverDlg,LoseFocusHdl,Control *,EMPTYARG)627 IMPL_LINK( ScOptSolverDlg, LoseFocusHdl, Control*, EMPTYARG )
628 {
629 mbDlgLostFocus = !IsActive();
630 return 0;
631 }
632
633 //----------------------------------------------------------------------------
634
IMPL_LINK(ScOptSolverDlg,DelBtnHdl,PushButton *,pBtn)635 IMPL_LINK( ScOptSolverDlg, DelBtnHdl, PushButton*, pBtn )
636 {
637 for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow )
638 if( pBtn == mpDelButton[nRow] )
639 {
640 sal_Bool bHadFocus = pBtn->HasFocus();
641
642 ReadConditions();
643 long nVecPos = nScrollPos + nRow;
644 if ( nVecPos < (long)maConditions.size() )
645 {
646 maConditions.erase( maConditions.begin() + nVecPos );
647 ShowConditions();
648
649 if ( bHadFocus && !pBtn->IsEnabled() )
650 {
651 // If the button is disabled, focus would normally move to the next control,
652 // (left edit of the next row). Move it to left edit of this row instead.
653
654 mpEdActive = mpLeftEdit[nRow];
655 mpEdActive->GrabFocus();
656 }
657 }
658 }
659
660 return 0;
661 }
662
663 //----------------------------------------------------------------------------
664
IMPL_LINK(ScOptSolverDlg,TargetModifyHdl,Edit *,EMPTYARG)665 IMPL_LINK( ScOptSolverDlg, TargetModifyHdl, Edit*, EMPTYARG )
666 {
667 // modify handler for the target edit:
668 // select "Value of" if something is input into the edit
669 if ( maEdTargetValue.GetText().Len() )
670 maRbValue.Check();
671 return 0;
672 }
673
IMPL_LINK(ScOptSolverDlg,CondModifyHdl,Edit *,EMPTYARG)674 IMPL_LINK( ScOptSolverDlg, CondModifyHdl, Edit*, EMPTYARG )
675 {
676 // modify handler for the condition edits, just to enable/disable "delete" buttons
677 ReadConditions();
678 EnableButtons();
679 return 0;
680 }
681
IMPL_LINK(ScOptSolverDlg,SelectHdl,ListBox *,EMPTYARG)682 IMPL_LINK( ScOptSolverDlg, SelectHdl, ListBox*, EMPTYARG )
683 {
684 // select handler for operator list boxes, just to enable/disable "delete" buttons
685 ReadConditions();
686 EnableButtons();
687 return 0;
688 }
689
IMPL_LINK(ScOptSolverDlg,ScrollHdl,ScrollBar *,EMPTYARG)690 IMPL_LINK( ScOptSolverDlg, ScrollHdl, ScrollBar*, EMPTYARG )
691 {
692 ReadConditions();
693 nScrollPos = maScrollBar.GetThumbPos();
694 ShowConditions();
695 if( mpEdActive )
696 mpEdActive->SetSelection( Selection( 0, SELECTION_MAX ) );
697 return 0;
698 }
699
IMPL_LINK(ScOptSolverDlg,CursorUpHdl,ScCursorRefEdit *,pEdit)700 IMPL_LINK( ScOptSolverDlg, CursorUpHdl, ScCursorRefEdit*, pEdit )
701 {
702 if ( pEdit == mpLeftEdit[0] || pEdit == mpRightEdit[0] )
703 {
704 if ( nScrollPos > 0 )
705 {
706 ReadConditions();
707 --nScrollPos;
708 ShowConditions();
709 if( mpEdActive )
710 mpEdActive->SetSelection( Selection( 0, SELECTION_MAX ) );
711 }
712 }
713 else
714 {
715 formula::RefEdit* pFocus = NULL;
716 for ( sal_uInt16 nRow = 1; nRow < EDIT_ROW_COUNT; ++nRow ) // second row or below: move focus
717 {
718 if ( pEdit == mpLeftEdit[nRow] )
719 pFocus = mpLeftEdit[nRow-1];
720 else if ( pEdit == mpRightEdit[nRow] )
721 pFocus = mpRightEdit[nRow-1];
722 }
723 if (pFocus)
724 {
725 mpEdActive = pFocus;
726 pFocus->GrabFocus();
727 }
728 }
729
730 return 0;
731 }
732
IMPL_LINK(ScOptSolverDlg,CursorDownHdl,ScCursorRefEdit *,pEdit)733 IMPL_LINK( ScOptSolverDlg, CursorDownHdl, ScCursorRefEdit*, pEdit )
734 {
735 if ( pEdit == mpLeftEdit[EDIT_ROW_COUNT-1] || pEdit == mpRightEdit[EDIT_ROW_COUNT-1] )
736 {
737 //! limit scroll position?
738 ReadConditions();
739 ++nScrollPos;
740 ShowConditions();
741 if( mpEdActive )
742 mpEdActive->SetSelection( Selection( 0, SELECTION_MAX ) );
743 }
744 else
745 {
746 formula::RefEdit* pFocus = NULL;
747 for ( sal_uInt16 nRow = 0; nRow+1 < EDIT_ROW_COUNT; ++nRow ) // before last row: move focus
748 {
749 if ( pEdit == mpLeftEdit[nRow] )
750 pFocus = mpLeftEdit[nRow+1];
751 else if ( pEdit == mpRightEdit[nRow] )
752 pFocus = mpRightEdit[nRow+1];
753 }
754 if (pFocus)
755 {
756 mpEdActive = pFocus;
757 pFocus->GrabFocus();
758 }
759 }
760
761 return 0;
762 }
763
764 //----------------------------------------------------------------------------
765
ShowError(bool bCondition,formula::RefEdit * pFocus)766 void ScOptSolverDlg::ShowError( bool bCondition, formula::RefEdit* pFocus )
767 {
768 String aMessage = bCondition ? maConditionError : maInputError;
769 ErrorBox( this, WinBits( WB_OK | WB_DEF_OK ), aMessage ).Execute();
770 if (pFocus)
771 {
772 mpEdActive = pFocus;
773 pFocus->GrabFocus();
774 }
775 }
776
777 //----------------------------------------------------------------------------
778
ParseRef(ScRange & rRange,const String & rInput,bool bAllowRange)779 bool ScOptSolverDlg::ParseRef( ScRange& rRange, const String& rInput, bool bAllowRange )
780 {
781 ScRangeUtil aRangeUtil;
782 ScAddress::Details aDetails(mpDoc->GetAddressConvention(), 0, 0);
783 sal_uInt16 nFlags = rRange.ParseAny( rInput, mpDoc, aDetails );
784 if ( nFlags & SCA_VALID )
785 {
786 if ( (nFlags & SCA_TAB_3D) == 0 )
787 rRange.aStart.SetTab( mnCurTab );
788 if ( (nFlags & SCA_TAB2_3D) == 0 )
789 rRange.aEnd.SetTab( rRange.aStart.Tab() );
790 return ( bAllowRange || rRange.aStart == rRange.aEnd );
791 }
792 else if ( aRangeUtil.MakeRangeFromName( rInput, mpDoc, mnCurTab, rRange, RUTL_NAMES, aDetails ) )
793 return ( bAllowRange || rRange.aStart == rRange.aEnd );
794
795 return false; // not recognized
796 }
797
FindTimeout(sal_Int32 & rTimeout)798 bool ScOptSolverDlg::FindTimeout( sal_Int32& rTimeout )
799 {
800 bool bFound = false;
801
802 if ( !maProperties.getLength() )
803 maProperties = ScSolverUtil::GetDefaults( maEngine ); // get property defaults from component
804
805 sal_Int32 nPropCount = maProperties.getLength();
806 for (sal_Int32 nProp=0; nProp<nPropCount && !bFound; ++nProp)
807 {
808 const beans::PropertyValue& rValue = maProperties[nProp];
809 if ( rValue.Name.equalsAscii( SC_UNONAME_TIMEOUT ) )
810 bFound = ( rValue.Value >>= rTimeout );
811 }
812 return bFound;
813 }
814
CallSolver()815 bool ScOptSolverDlg::CallSolver() // return true -> close dialog after calling
816 {
817 // show progress dialog
818
819 ScSolverProgressDialog aProgress( this );
820 sal_Int32 nTimeout = 0;
821 if ( FindTimeout( nTimeout ) )
822 aProgress.SetTimeLimit( nTimeout );
823 else
824 aProgress.HideTimeLimit();
825 aProgress.Show();
826 aProgress.Update();
827 aProgress.Sync();
828 // try to make sure the progress dialog is painted before continuing
829 Application::Reschedule(true);
830
831 // collect solver parameters
832
833 ReadConditions();
834
835 uno::Reference<sheet::XSpreadsheetDocument> xDocument( mpDocShell->GetModel(), uno::UNO_QUERY );
836
837 ScRange aObjRange;
838 if ( !ParseRef( aObjRange, maEdObjectiveCell.GetText(), false ) )
839 {
840 ShowError( false, &maEdObjectiveCell );
841 return false;
842 }
843 table::CellAddress aObjective( aObjRange.aStart.Tab(), aObjRange.aStart.Col(), aObjRange.aStart.Row() );
844
845 // "changing cells" can be several ranges
846 ScRangeList aVarRanges;
847 if ( !ParseWithNames( aVarRanges, maEdVariableCells.GetText(), mpDoc ) )
848 {
849 ShowError( false, &maEdVariableCells );
850 return false;
851 }
852 uno::Sequence<table::CellAddress> aVariables;
853 sal_Int32 nVarPos = 0;
854 sal_uLong nRangeCount = aVarRanges.Count();
855 for (sal_uLong nRangePos=0; nRangePos<nRangeCount; ++nRangePos)
856 {
857 ScRange aRange(*aVarRanges.GetObject(nRangePos));
858 aRange.Justify();
859 SCTAB nTab = aRange.aStart.Tab();
860
861 // resolve into single cells
862
863 sal_Int32 nAdd = ( aRange.aEnd.Col() - aRange.aStart.Col() + 1 ) *
864 ( aRange.aEnd.Row() - aRange.aStart.Row() + 1 );
865 aVariables.realloc( nVarPos + nAdd );
866
867 for (SCROW nRow = aRange.aStart.Row(); nRow <= aRange.aEnd.Row(); ++nRow)
868 for (SCCOL nCol = aRange.aStart.Col(); nCol <= aRange.aEnd.Col(); ++nCol)
869 aVariables[nVarPos++] = table::CellAddress( nTab, nCol, nRow );
870 }
871
872 uno::Sequence<sheet::SolverConstraint> aConstraints;
873 sal_Int32 nConstrPos = 0;
874 for ( std::vector<ScOptConditionRow>::const_iterator aConstrIter = maConditions.begin();
875 aConstrIter != maConditions.end(); ++aConstrIter )
876 {
877 if ( aConstrIter->aLeftStr.Len() )
878 {
879 sheet::SolverConstraint aConstraint;
880 // order of list box entries must match enum values
881 aConstraint.Operator = static_cast<sheet::SolverConstraintOperator>(aConstrIter->nOperator);
882
883 ScRange aLeftRange;
884 if ( !ParseRef( aLeftRange, aConstrIter->aLeftStr, true ) )
885 {
886 ShowError( true, NULL );
887 return false;
888 }
889
890 bool bIsRange = false;
891 ScRange aRightRange;
892 if ( ParseRef( aRightRange, aConstrIter->aRightStr, true ) )
893 {
894 if ( aRightRange.aStart == aRightRange.aEnd )
895 aConstraint.Right <<= table::CellAddress( aRightRange.aStart.Tab(),
896 aRightRange.aStart.Col(), aRightRange.aStart.Row() );
897 else if ( aRightRange.aEnd.Col()-aRightRange.aStart.Col() == aLeftRange.aEnd.Col()-aLeftRange.aStart.Col() &&
898 aRightRange.aEnd.Row()-aRightRange.aStart.Row() == aLeftRange.aEnd.Row()-aLeftRange.aStart.Row() )
899 bIsRange = true; // same size as "left" range, resolve into single cells
900 else
901 {
902 ShowError( true, NULL );
903 return false;
904 }
905 }
906 else
907 {
908 sal_uInt32 nFormat = 0; //! explicit language?
909 double fValue = 0.0;
910 if ( mpDoc->GetFormatTable()->IsNumberFormat( aConstrIter->aRightStr, nFormat, fValue ) )
911 aConstraint.Right <<= fValue;
912 else if ( aConstraint.Operator != sheet::SolverConstraintOperator_INTEGER &&
913 aConstraint.Operator != sheet::SolverConstraintOperator_BINARY )
914 {
915 ShowError( true, NULL );
916 return false;
917 }
918 }
919
920 // resolve into single cells
921
922 sal_Int32 nAdd = ( aLeftRange.aEnd.Col() - aLeftRange.aStart.Col() + 1 ) *
923 ( aLeftRange.aEnd.Row() - aLeftRange.aStart.Row() + 1 );
924 aConstraints.realloc( nConstrPos + nAdd );
925
926 for (SCROW nRow = aLeftRange.aStart.Row(); nRow <= aLeftRange.aEnd.Row(); ++nRow)
927 for (SCCOL nCol = aLeftRange.aStart.Col(); nCol <= aLeftRange.aEnd.Col(); ++nCol)
928 {
929 aConstraint.Left = table::CellAddress( aLeftRange.aStart.Tab(), nCol, nRow );
930 if ( bIsRange )
931 aConstraint.Right <<= table::CellAddress( aRightRange.aStart.Tab(),
932 aRightRange.aStart.Col() + ( nCol - aLeftRange.aStart.Col() ),
933 aRightRange.aStart.Row() + ( nRow - aLeftRange.aStart.Row() ) );
934
935 aConstraints[nConstrPos++] = aConstraint;
936 }
937 }
938 }
939
940 sal_Bool bMaximize = maRbMax.IsChecked();
941 if ( maRbValue.IsChecked() )
942 {
943 // handle "value of" with an additional constraint (and then minimize)
944
945 sheet::SolverConstraint aConstraint;
946 aConstraint.Left = aObjective;
947 aConstraint.Operator = sheet::SolverConstraintOperator_EQUAL;
948
949 String aValStr = maEdTargetValue.GetText();
950 ScRange aRightRange;
951 if ( ParseRef( aRightRange, aValStr, false ) )
952 aConstraint.Right <<= table::CellAddress( aRightRange.aStart.Tab(),
953 aRightRange.aStart.Col(), aRightRange.aStart.Row() );
954 else
955 {
956 sal_uInt32 nFormat = 0; //! explicit language?
957 double fValue = 0.0;
958 if ( mpDoc->GetFormatTable()->IsNumberFormat( aValStr, nFormat, fValue ) )
959 aConstraint.Right <<= fValue;
960 else
961 {
962 ShowError( false, &maEdTargetValue );
963 return false;
964 }
965 }
966
967 aConstraints.realloc( nConstrPos + 1 );
968 aConstraints[nConstrPos++] = aConstraint;
969 }
970
971 // copy old document values
972
973 sal_Int32 nVarCount = aVariables.getLength();
974 uno::Sequence<double> aOldValues;
975 aOldValues.realloc( nVarCount );
976 for (nVarPos=0; nVarPos<nVarCount; ++nVarPos)
977 {
978 ScAddress aCellPos;
979 ScUnoConversion::FillScAddress( aCellPos, aVariables[nVarPos] );
980 aOldValues[nVarPos] = mpDoc->GetValue( aCellPos );
981 }
982
983 // create and initialize solver
984
985 uno::Reference<sheet::XSolver> xSolver = ScSolverUtil::GetSolver( maEngine );
986 DBG_ASSERT( xSolver.is(), "can't get solver component" );
987 if ( !xSolver.is() )
988 return false;
989
990 xSolver->setDocument( xDocument );
991 xSolver->setObjective( aObjective );
992 xSolver->setVariables( aVariables );
993 xSolver->setConstraints( aConstraints );
994 xSolver->setMaximize( bMaximize );
995
996 // set options
997 uno::Reference<beans::XPropertySet> xOptProp(xSolver, uno::UNO_QUERY);
998 if ( xOptProp.is() )
999 {
1000 sal_Int32 nPropCount = maProperties.getLength();
1001 for (sal_Int32 nProp=0; nProp<nPropCount; ++nProp)
1002 {
1003 const beans::PropertyValue& rValue = maProperties[nProp];
1004 try
1005 {
1006 xOptProp->setPropertyValue( rValue.Name, rValue.Value );
1007 }
1008 catch ( uno::Exception & )
1009 {
1010 DBG_ERRORFILE("Exception in solver option property");
1011 }
1012 }
1013 }
1014
1015 xSolver->solve();
1016 sal_Bool bSuccess = xSolver->getSuccess();
1017
1018 aProgress.Hide();
1019 bool bClose = false;
1020 bool bRestore = true; // restore old values unless a solution is accepted
1021 if ( bSuccess )
1022 {
1023 // put solution into document so it is visible when asking
1024 uno::Sequence<double> aSolution = xSolver->getSolution();
1025 if ( aSolution.getLength() == nVarCount )
1026 {
1027 mpDocShell->LockPaint();
1028 ScDocFunc aFunc(*mpDocShell);
1029 for (nVarPos=0; nVarPos<nVarCount; ++nVarPos)
1030 {
1031 ScAddress aCellPos;
1032 ScUnoConversion::FillScAddress( aCellPos, aVariables[nVarPos] );
1033 aFunc.PutCell( aCellPos, new ScValueCell( aSolution[nVarPos] ), sal_True );
1034 }
1035 mpDocShell->UnlockPaint();
1036 }
1037 //! else error?
1038
1039 // take formatted result from document (result value from component is ignored)
1040 String aResultStr;
1041 mpDoc->GetString( (SCCOL)aObjective.Column, (SCROW)aObjective.Row, (SCTAB)aObjective.Sheet, aResultStr );
1042 ScSolverSuccessDialog aDialog( this, aResultStr );
1043 if ( aDialog.Execute() == RET_OK )
1044 {
1045 // keep results and close dialog
1046 bRestore = false;
1047 bClose = true;
1048 }
1049 }
1050 else
1051 {
1052 rtl::OUString aError;
1053 uno::Reference<sheet::XSolverDescription> xDesc( xSolver, uno::UNO_QUERY );
1054 if ( xDesc.is() )
1055 aError = xDesc->getStatusDescription(); // error description from component
1056 ScSolverNoSolutionDialog aDialog( this, aError );
1057 aDialog.Execute();
1058 }
1059
1060 if ( bRestore ) // restore old values
1061 {
1062 mpDocShell->LockPaint();
1063 ScDocFunc aFunc(*mpDocShell);
1064 for (nVarPos=0; nVarPos<nVarCount; ++nVarPos)
1065 {
1066 ScAddress aCellPos;
1067 ScUnoConversion::FillScAddress( aCellPos, aVariables[nVarPos] );
1068 aFunc.PutCell( aCellPos, new ScValueCell( aOldValues[nVarPos] ), sal_True );
1069 }
1070 mpDocShell->UnlockPaint();
1071 }
1072
1073 return bClose;
1074 }
1075
1076