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