| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_sc.hxx" |
| |
| //---------------------------------------------------------------------------- |
| |
| #include "rangelst.hxx" |
| #include "scitems.hxx" |
| #include <sfx2/bindings.hxx> |
| #include <sfx2/imagemgr.hxx> |
| #include <svl/zforlist.hxx> |
| #include <vcl/msgbox.hxx> |
| #include <vcl/svapp.hxx> |
| |
| #include "uiitems.hxx" |
| #include "reffact.hxx" |
| #include "docsh.hxx" |
| #include "docfunc.hxx" |
| #include "cell.hxx" |
| #include "rangeutl.hxx" |
| #include "scresid.hxx" |
| #include "convuno.hxx" |
| #include "unonames.hxx" |
| #include "solveroptions.hxx" |
| #include "solverutil.hxx" |
| #include "optsolver.hrc" |
| |
| #include "optsolver.hxx" |
| |
| #include <com/sun/star/sheet/Solver.hpp> |
| #include <com/sun/star/sheet/XSolverDescription.hpp> |
| |
| using namespace com::sun::star; |
| |
| //---------------------------------------------------------------------------- |
| |
| ScSolverProgressDialog::ScSolverProgressDialog( Window* pParent ) |
| : ModelessDialog( pParent, ScResId( RID_SCDLG_SOLVER_PROGRESS ) ), |
| maFtProgress ( this, ScResId( FT_PROGRESS ) ), |
| maFtTime ( this, ScResId( FT_TIMELIMIT ) ), |
| maFlButtons ( this, ScResId( FL_BUTTONS ) ), |
| maBtnOk ( this, ScResId( BTN_OK ) ) |
| { |
| maBtnOk.Enable(sal_False); |
| FreeResource(); |
| } |
| |
| ScSolverProgressDialog::~ScSolverProgressDialog() |
| { |
| } |
| |
| void ScSolverProgressDialog::HideTimeLimit() |
| { |
| maFtTime.Hide(); |
| } |
| |
| void ScSolverProgressDialog::SetTimeLimit( sal_Int32 nSeconds ) |
| { |
| String aOld = maFtTime.GetText(); |
| String aNew = aOld.GetToken(0,'#'); |
| aNew += String::CreateFromInt32( nSeconds ); |
| aNew += aOld.GetToken(1,'#'); |
| maFtTime.SetText( aNew ); |
| } |
| |
| //---------------------------------------------------------------------------- |
| |
| ScSolverNoSolutionDialog::ScSolverNoSolutionDialog( Window* pParent, const String& rErrorText ) |
| : ModalDialog( pParent, ScResId( RID_SCDLG_SOLVER_NOSOLUTION ) ), |
| maFtNoSolution ( this, ScResId( FT_NOSOLUTION ) ), |
| maFtErrorText ( this, ScResId( FT_ERRORTEXT ) ), |
| maFlButtons ( this, ScResId( FL_BUTTONS ) ), |
| maBtnOk ( this, ScResId( BTN_OK ) ) |
| { |
| maFtErrorText.SetText( rErrorText ); |
| FreeResource(); |
| } |
| |
| ScSolverNoSolutionDialog::~ScSolverNoSolutionDialog() |
| { |
| } |
| |
| //---------------------------------------------------------------------------- |
| |
| ScSolverSuccessDialog::ScSolverSuccessDialog( Window* pParent, const String& rSolution ) |
| : ModalDialog( pParent, ScResId( RID_SCDLG_SOLVER_SUCCESS ) ), |
| maFtSuccess ( this, ScResId( FT_SUCCESS ) ), |
| maFtResult ( this, ScResId( FT_RESULT ) ), |
| maFtQuestion ( this, ScResId( FT_QUESTION ) ), |
| maFlButtons ( this, ScResId( FL_BUTTONS ) ), |
| maBtnOk ( this, ScResId( BTN_OK ) ), |
| maBtnCancel ( this, ScResId( BTN_CANCEL ) ) |
| { |
| String aMessage = maFtResult.GetText(); |
| aMessage.Append( (sal_Char) ' ' ); |
| aMessage.Append( rSolution ); |
| maFtResult.SetText( aMessage ); |
| FreeResource(); |
| } |
| |
| ScSolverSuccessDialog::~ScSolverSuccessDialog() |
| { |
| } |
| |
| //---------------------------------------------------------------------------- |
| |
| ScCursorRefEdit::ScCursorRefEdit( ScAnyRefDlg* pParent, const ResId& rResId ) : |
| formula::RefEdit( pParent, pParent, rResId ) |
| { |
| } |
| |
| void ScCursorRefEdit::SetCursorLinks( const Link& rUp, const Link& rDown ) |
| { |
| maCursorUpLink = rUp; |
| maCursorDownLink = rDown; |
| } |
| |
| void ScCursorRefEdit::KeyInput( const KeyEvent& rKEvt ) |
| { |
| KeyCode aCode = rKEvt.GetKeyCode(); |
| bool bUp = (aCode.GetCode() == KEY_UP); |
| bool bDown = (aCode.GetCode() == KEY_DOWN); |
| if ( !aCode.IsShift() && !aCode.IsMod1() && !aCode.IsMod2() && ( bUp || bDown ) ) |
| { |
| if ( bUp ) |
| maCursorUpLink.Call( this ); |
| else |
| maCursorDownLink.Call( this ); |
| } |
| else |
| formula::RefEdit::KeyInput( rKEvt ); |
| } |
| |
| //---------------------------------------------------------------------------- |
| |
| ScOptSolverSave::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 ) : |
| maObjective( rObjective ), |
| mbMax( bMax ), |
| mbMin( bMin ), |
| mbValue( bValue ), |
| maTarget( rTarget ), |
| maVariable( rVariable ), |
| maConditions( rConditions ), |
| maEngine( rEngine ), |
| maProperties( rProperties ) |
| { |
| } |
| |
| //============================================================================ |
| // class ScOptSolverDlg |
| //---------------------------------------------------------------------------- |
| |
| ScOptSolverDlg::ScOptSolverDlg( SfxBindings* pB, SfxChildWindow* pCW, Window* pParent, |
| ScDocShell* pDocSh, ScAddress aCursorPos ) |
| |
| : ScAnyRefDlg ( pB, pCW, pParent, RID_SCDLG_OPTSOLVER ), |
| // |
| maFtObjectiveCell ( this, ScResId( FT_OBJECTIVECELL ) ), |
| maEdObjectiveCell ( this, this, ScResId( ED_OBJECTIVECELL ) ), |
| maRBObjectiveCell ( this, ScResId( IB_OBJECTIVECELL ), &maEdObjectiveCell, this ), |
| maFtDirection ( this, ScResId( FT_DIRECTION ) ), |
| maRbMax ( this, ScResId( RB_MAX ) ), |
| maRbMin ( this, ScResId( RB_MIN ) ), |
| maRbValue ( this, ScResId( RB_VALUE ) ), |
| maEdTargetValue ( this, this, ScResId( ED_TARGET ) ), |
| maRBTargetValue ( this, ScResId( IB_TARGET ), &maEdTargetValue, this ), |
| maFtVariableCells ( this, ScResId( FT_VARIABLECELLS ) ), |
| maEdVariableCells ( this, this, ScResId( ED_VARIABLECELLS ) ), |
| maRBVariableCells ( this, ScResId( IB_VARIABLECELLS ), &maEdVariableCells, this), |
| maFlConditions ( this, ScResId( FL_CONDITIONS ) ), |
| maFtCellRef ( this, ScResId( FT_CELLREF ) ), |
| maEdLeft1 ( this, ScResId( ED_LEFT1 ) ), |
| maRBLeft1 ( this, ScResId( IB_LEFT1 ), &maEdLeft1, this ), |
| maFtOperator ( this, ScResId( FT_OPERATOR ) ), |
| maLbOp1 ( this, ScResId( LB_OP1 ) ), |
| maFtConstraint ( this, ScResId( FT_CONSTRAINT ) ), |
| maEdRight1 ( this, ScResId( ED_RIGHT1 ) ), |
| maRBRight1 ( this, ScResId( IB_RIGHT1 ), &maEdRight1, this ), |
| maBtnDel1 ( this, ScResId( IB_DELETE1 ) ), |
| maEdLeft2 ( this, ScResId( ED_LEFT2 ) ), |
| maRBLeft2 ( this, ScResId( IB_LEFT2 ), &maEdLeft2, this ), |
| maLbOp2 ( this, ScResId( LB_OP2 ) ), |
| maEdRight2 ( this, ScResId( ED_RIGHT2 ) ), |
| maRBRight2 ( this, ScResId( IB_RIGHT2 ), &maEdRight2, this ), |
| maBtnDel2 ( this, ScResId( IB_DELETE2 ) ), |
| maEdLeft3 ( this, ScResId( ED_LEFT3 ) ), |
| maRBLeft3 ( this, ScResId( IB_LEFT3 ), &maEdLeft3, this ), |
| maLbOp3 ( this, ScResId( LB_OP3 ) ), |
| maEdRight3 ( this, ScResId( ED_RIGHT3 ) ), |
| maRBRight3 ( this, ScResId( IB_RIGHT3 ), &maEdRight3, this ), |
| maBtnDel3 ( this, ScResId( IB_DELETE3 ) ), |
| maEdLeft4 ( this, ScResId( ED_LEFT4 ) ), |
| maRBLeft4 ( this, ScResId( IB_LEFT4 ), &maEdLeft4, this ), |
| maLbOp4 ( this, ScResId( LB_OP4 ) ), |
| maEdRight4 ( this, ScResId( ED_RIGHT4 ) ), |
| maRBRight4 ( this, ScResId( IB_RIGHT4 ), &maEdRight4, this ), |
| maBtnDel4 ( this, ScResId( IB_DELETE4 ) ), |
| maScrollBar ( this, ScResId( SB_SCROLL ) ), |
| maFlButtons ( this, ScResId( FL_BUTTONS ) ), |
| maBtnOpt ( this, ScResId( BTN_OPTIONS ) ), |
| maBtnHelp ( this, ScResId( BTN_HELP ) ), |
| maBtnCancel ( this, ScResId( BTN_CLOSE ) ), |
| maBtnSolve ( this, ScResId( BTN_SOLVE ) ), |
| maInputError ( ScResId( STR_INVALIDINPUT ) ), |
| maConditionError ( ScResId( STR_INVALIDCONDITION ) ), |
| // |
| mpDocShell ( pDocSh ), |
| mpDoc ( pDocSh->GetDocument() ), |
| mnCurTab ( aCursorPos.Tab() ), |
| mpEdActive ( NULL ), |
| mbDlgLostFocus ( false ), |
| nScrollPos ( 0 ) |
| { |
| mpLeftEdit[0] = &maEdLeft1; |
| mpLeftButton[0] = &maRBLeft1; |
| mpRightEdit[0] = &maEdRight1; |
| mpRightButton[0] = &maRBRight1; |
| mpOperator[0] = &maLbOp1; |
| mpDelButton[0] = &maBtnDel1; |
| |
| mpLeftEdit[1] = &maEdLeft2; |
| mpLeftButton[1] = &maRBLeft2; |
| mpRightEdit[1] = &maEdRight2; |
| mpRightButton[1] = &maRBRight2; |
| mpOperator[1] = &maLbOp2; |
| mpDelButton[1] = &maBtnDel2; |
| |
| mpLeftEdit[2] = &maEdLeft3; |
| mpLeftButton[2] = &maRBLeft3; |
| mpRightEdit[2] = &maEdRight3; |
| mpRightButton[2] = &maRBRight3; |
| mpOperator[2] = &maLbOp3; |
| mpDelButton[2] = &maBtnDel3; |
| |
| mpLeftEdit[3] = &maEdLeft4; |
| mpLeftButton[3] = &maRBLeft4; |
| mpRightEdit[3] = &maEdRight4; |
| mpRightButton[3] = &maRBRight4; |
| mpOperator[3] = &maLbOp4; |
| mpDelButton[3] = &maBtnDel4; |
| |
| maRbMax.SetAccessibleRelationMemberOf(&maFtDirection); |
| maRbMin.SetAccessibleRelationMemberOf(&maFtDirection); |
| maRbValue.SetAccessibleRelationMemberOf(&maFtDirection); |
| maEdLeft2.SetAccessibleName(maFtCellRef.GetText()); |
| maLbOp2.SetAccessibleName(maFtOperator.GetText()); |
| maEdRight2.SetAccessibleName(maFtConstraint.GetText()); |
| maEdLeft3.SetAccessibleName(maFtCellRef.GetText()); |
| maLbOp3.SetAccessibleName(maFtOperator.GetText()); |
| maEdRight3.SetAccessibleName(maFtConstraint.GetText()); |
| maEdLeft4.SetAccessibleName(maFtCellRef.GetText()); |
| maLbOp4.SetAccessibleName(maFtOperator.GetText()); |
| maEdRight4.SetAccessibleName(maFtConstraint.GetText()); |
| |
| Init( aCursorPos ); |
| FreeResource(); |
| } |
| |
| //---------------------------------------------------------------------------- |
| |
| ScOptSolverDlg::~ScOptSolverDlg() |
| { |
| } |
| |
| //---------------------------------------------------------------------------- |
| |
| void ScOptSolverDlg::Init(const ScAddress& rCursorPos) |
| { |
| // Get the "Delete Rows" commandimagelist images from sfx instead of |
| // adding a second copy to sc (see ScTbxInsertCtrl::StateChanged) |
| |
| rtl::OUString aSlotURL( RTL_CONSTASCII_USTRINGPARAM( "slot:" )); |
| aSlotURL += rtl::OUString::valueOf( sal_Int32( SID_DEL_ROWS ) ); |
| uno::Reference<frame::XFrame> xFrame = GetBindings().GetActiveFrame(); |
| Image aDelNm = ::GetImage( xFrame, aSlotURL, sal_False, sal_False ); |
| Image aDelHC = ::GetImage( xFrame, aSlotURL, sal_False, sal_True ); // high contrast |
| |
| for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow ) |
| { |
| mpDelButton[nRow]->SetModeImage( aDelNm, BMP_COLOR_NORMAL ); |
| mpDelButton[nRow]->SetModeImage( aDelHC, BMP_COLOR_HIGHCONTRAST ); |
| } |
| |
| maBtnOpt.SetClickHdl( LINK( this, ScOptSolverDlg, BtnHdl ) ); |
| maBtnCancel.SetClickHdl( LINK( this, ScOptSolverDlg, BtnHdl ) ); |
| maBtnSolve.SetClickHdl( LINK( this, ScOptSolverDlg, BtnHdl ) ); |
| |
| Link aLink = LINK( this, ScOptSolverDlg, GetFocusHdl ); |
| maEdObjectiveCell.SetGetFocusHdl( aLink ); |
| maRBObjectiveCell.SetGetFocusHdl( aLink ); |
| maEdTargetValue.SetGetFocusHdl( aLink ); |
| maRBTargetValue.SetGetFocusHdl( aLink ); |
| maEdVariableCells.SetGetFocusHdl( aLink ); |
| maRBVariableCells.SetGetFocusHdl( aLink ); |
| maRbValue.SetGetFocusHdl( aLink ); |
| for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow ) |
| { |
| mpLeftEdit[nRow]->SetGetFocusHdl( aLink ); |
| mpLeftButton[nRow]->SetGetFocusHdl( aLink ); |
| mpRightEdit[nRow]->SetGetFocusHdl( aLink ); |
| mpRightButton[nRow]->SetGetFocusHdl( aLink ); |
| mpOperator[nRow]->SetGetFocusHdl( aLink ); |
| } |
| |
| aLink = LINK( this, ScOptSolverDlg, LoseFocusHdl ); |
| maEdObjectiveCell.SetLoseFocusHdl( aLink ); |
| maRBObjectiveCell.SetLoseFocusHdl( aLink ); |
| maEdTargetValue. SetLoseFocusHdl( aLink ); |
| maRBTargetValue. SetLoseFocusHdl( aLink ); |
| maEdVariableCells.SetLoseFocusHdl( aLink ); |
| maRBVariableCells.SetLoseFocusHdl( aLink ); |
| for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow ) |
| { |
| mpLeftEdit[nRow]->SetLoseFocusHdl( aLink ); |
| mpLeftButton[nRow]->SetLoseFocusHdl( aLink ); |
| mpRightEdit[nRow]->SetLoseFocusHdl( aLink ); |
| mpRightButton[nRow]->SetLoseFocusHdl( aLink ); |
| } |
| |
| Link aCursorUp = LINK( this, ScOptSolverDlg, CursorUpHdl ); |
| Link aCursorDown = LINK( this, ScOptSolverDlg, CursorDownHdl ); |
| Link aCondModify = LINK( this, ScOptSolverDlg, CondModifyHdl ); |
| for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow ) |
| { |
| mpLeftEdit[nRow]->SetCursorLinks( aCursorUp, aCursorDown ); |
| mpRightEdit[nRow]->SetCursorLinks( aCursorUp, aCursorDown ); |
| mpLeftEdit[nRow]->SetModifyHdl( aCondModify ); |
| mpRightEdit[nRow]->SetModifyHdl( aCondModify ); |
| mpDelButton[nRow]->SetClickHdl( LINK( this, ScOptSolverDlg, DelBtnHdl ) ); |
| mpOperator[nRow]->SetSelectHdl( LINK( this, ScOptSolverDlg, SelectHdl ) ); |
| } |
| maEdTargetValue.SetModifyHdl( LINK( this, ScOptSolverDlg, TargetModifyHdl ) ); |
| |
| maScrollBar.SetEndScrollHdl( LINK( this, ScOptSolverDlg, ScrollHdl ) ); |
| maScrollBar.SetScrollHdl( LINK( this, ScOptSolverDlg, ScrollHdl ) ); |
| |
| maScrollBar.SetPageSize( EDIT_ROW_COUNT ); |
| maScrollBar.SetVisibleSize( EDIT_ROW_COUNT ); |
| maScrollBar.SetLineSize( 1 ); |
| // Range is set in ShowConditions |
| |
| // get available solver implementations |
| //! sort by descriptions? |
| ScSolverUtil::GetImplementations( maImplNames, maDescriptions ); |
| sal_Int32 nImplCount = maImplNames.getLength(); |
| |
| const ScOptSolverSave* pOldData = mpDocShell->GetSolverSaveData(); |
| if ( pOldData ) |
| { |
| maEdObjectiveCell.SetRefString( pOldData->GetObjective() ); |
| maRbMax.Check( pOldData->GetMax() ); |
| maRbMin.Check( pOldData->GetMin() ); |
| maRbValue.Check( pOldData->GetValue() ); |
| maEdTargetValue.SetRefString( pOldData->GetTarget() ); |
| maEdVariableCells.SetRefString( pOldData->GetVariable() ); |
| maConditions = pOldData->GetConditions(); |
| maEngine = pOldData->GetEngine(); |
| maProperties = pOldData->GetProperties(); |
| } |
| else |
| { |
| maRbMax.Check(); |
| String aCursorStr; |
| if ( !mpDoc->GetRangeAtBlock( ScRange(rCursorPos), &aCursorStr ) ) |
| rCursorPos.Format( aCursorStr, SCA_ABS, NULL, mpDoc->GetAddressConvention() ); |
| maEdObjectiveCell.SetRefString( aCursorStr ); |
| if ( nImplCount > 0 ) |
| maEngine = maImplNames[0]; // use first implementation |
| } |
| ShowConditions(); |
| |
| maEdObjectiveCell.GrabFocus(); |
| mpEdActive = &maEdObjectiveCell; |
| } |
| |
| //---------------------------------------------------------------------------- |
| |
| void ScOptSolverDlg::ReadConditions() |
| { |
| for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow ) |
| { |
| ScOptConditionRow aRowEntry; |
| aRowEntry.aLeftStr = mpLeftEdit[nRow]->GetText(); |
| aRowEntry.aRightStr = mpRightEdit[nRow]->GetText(); |
| aRowEntry.nOperator = mpOperator[nRow]->GetSelectEntryPos(); |
| |
| long nVecPos = nScrollPos + nRow; |
| if ( nVecPos >= (long)maConditions.size() && !aRowEntry.IsDefault() ) |
| maConditions.resize( nVecPos + 1 ); |
| |
| if ( nVecPos < (long)maConditions.size() ) |
| maConditions[nVecPos] = aRowEntry; |
| |
| // remove default entries at the end |
| size_t nSize = maConditions.size(); |
| while ( nSize > 0 && maConditions[ nSize-1 ].IsDefault() ) |
| --nSize; |
| maConditions.resize( nSize ); |
| } |
| } |
| |
| void ScOptSolverDlg::ShowConditions() |
| { |
| for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow ) |
| { |
| ScOptConditionRow aRowEntry; |
| |
| long nVecPos = nScrollPos + nRow; |
| if ( nVecPos < (long)maConditions.size() ) |
| aRowEntry = maConditions[nVecPos]; |
| |
| mpLeftEdit[nRow]->SetRefString( aRowEntry.aLeftStr ); |
| mpRightEdit[nRow]->SetRefString( aRowEntry.aRightStr ); |
| mpOperator[nRow]->SelectEntryPos( aRowEntry.nOperator ); |
| } |
| |
| // allow to scroll one page behind the visible or stored rows |
| long nVisible = nScrollPos + EDIT_ROW_COUNT; |
| long nMax = std::max( nVisible, (long) maConditions.size() ); |
| maScrollBar.SetRange( Range( 0, nMax + EDIT_ROW_COUNT ) ); |
| maScrollBar.SetThumbPos( nScrollPos ); |
| |
| EnableButtons(); |
| } |
| |
| void ScOptSolverDlg::EnableButtons() |
| { |
| for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow ) |
| { |
| long nVecPos = nScrollPos + nRow; |
| mpDelButton[nRow]->Enable( nVecPos < (long)maConditions.size() ); |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| |
| sal_Bool ScOptSolverDlg::Close() |
| { |
| return DoClose( ScOptSolverDlgWrapper::GetChildWindowId() ); |
| } |
| |
| //---------------------------------------------------------------------------- |
| |
| void ScOptSolverDlg::SetActive() |
| { |
| if ( mbDlgLostFocus ) |
| { |
| mbDlgLostFocus = false; |
| if( mpEdActive ) |
| mpEdActive->GrabFocus(); |
| } |
| else |
| { |
| GrabFocus(); |
| } |
| RefInputDone(); |
| } |
| |
| //---------------------------------------------------------------------------- |
| |
| void ScOptSolverDlg::SetReference( const ScRange& rRef, ScDocument* pDocP ) |
| { |
| if( mpEdActive ) |
| { |
| if ( rRef.aStart != rRef.aEnd ) |
| RefInputStart(mpEdActive); |
| |
| // "target"/"value": single cell |
| bool bSingle = ( mpEdActive == &maEdObjectiveCell || mpEdActive == &maEdTargetValue ); |
| |
| String aStr; |
| ScAddress aAdr = rRef.aStart; |
| ScRange aNewRef( rRef ); |
| if ( bSingle ) |
| aNewRef.aEnd = aAdr; |
| |
| String aName; |
| if ( pDocP->GetRangeAtBlock( aNewRef, &aName ) ) // named range: show name |
| aStr = aName; |
| else // format cell/range reference |
| { |
| sal_uInt16 nFmt = ( aAdr.Tab() == mnCurTab ) ? SCA_ABS : SCA_ABS_3D; |
| if ( bSingle ) |
| aAdr.Format( aStr, nFmt, pDocP, pDocP->GetAddressConvention() ); |
| else |
| rRef.Format( aStr, nFmt | SCR_ABS, pDocP, pDocP->GetAddressConvention() ); |
| } |
| |
| // variable cells can be several ranges, so only the selection is replaced |
| if ( mpEdActive == &maEdVariableCells ) |
| { |
| String aVal = mpEdActive->GetText(); |
| Selection aSel = mpEdActive->GetSelection(); |
| aSel.Justify(); |
| aVal.Erase( (xub_StrLen)aSel.Min(), (xub_StrLen)aSel.Len() ); |
| aVal.Insert( aStr, (xub_StrLen)aSel.Min() ); |
| Selection aNewSel( aSel.Min(), aSel.Min()+aStr.Len() ); |
| mpEdActive->SetRefString( aVal ); |
| mpEdActive->SetSelection( aNewSel ); |
| } |
| else |
| mpEdActive->SetRefString( aStr ); |
| |
| ReadConditions(); |
| EnableButtons(); |
| |
| // select "Value of" if a ref is input into "target" edit |
| if ( mpEdActive == &maEdTargetValue ) |
| maRbValue.Check(); |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| |
| sal_Bool ScOptSolverDlg::IsRefInputMode() const |
| { |
| return mpEdActive != NULL; |
| } |
| |
| //---------------------------------------------------------------------------- |
| // Handler: |
| |
| IMPL_LINK( ScOptSolverDlg, BtnHdl, PushButton*, pBtn ) |
| { |
| if ( pBtn == &maBtnSolve || pBtn == &maBtnCancel ) |
| { |
| bool bSolve = ( pBtn == &maBtnSolve ); |
| |
| SetDispatcherLock( sal_False ); |
| SwitchToDocument(); |
| |
| bool bClose = true; |
| if ( bSolve ) |
| bClose = CallSolver(); |
| |
| if ( bClose ) |
| { |
| // Close: write dialog settings to DocShell for subsequent calls |
| ReadConditions(); |
| ScOptSolverSave aSave( |
| maEdObjectiveCell.GetText(), maRbMax.IsChecked(), maRbMin.IsChecked(), maRbValue.IsChecked(), |
| maEdTargetValue.GetText(), maEdVariableCells.GetText(), maConditions, maEngine, maProperties ); |
| mpDocShell->SetSolverSaveData( aSave ); |
| Close(); |
| } |
| else |
| { |
| // no solution -> dialog is kept open |
| SetDispatcherLock( sal_True ); |
| } |
| } |
| else if ( pBtn == &maBtnOpt ) |
| { |
| //! move options dialog to UI lib? |
| ScSolverOptionsDialog* pOptDlg = |
| new ScSolverOptionsDialog( this, maImplNames, maDescriptions, maEngine, maProperties ); |
| if ( pOptDlg->Execute() == RET_OK ) |
| { |
| maEngine = pOptDlg->GetEngine(); |
| maProperties = pOptDlg->GetProperties(); |
| } |
| delete pOptDlg; |
| } |
| |
| return 0; |
| } |
| |
| //---------------------------------------------------------------------------- |
| |
| IMPL_LINK( ScOptSolverDlg, GetFocusHdl, Control*, pCtrl ) |
| { |
| Edit* pEdit = NULL; |
| mpEdActive = NULL; |
| |
| if( pCtrl == &maEdObjectiveCell || pCtrl == &maRBObjectiveCell ) |
| pEdit = mpEdActive = &maEdObjectiveCell; |
| else if( pCtrl == &maEdTargetValue || pCtrl == &maRBTargetValue ) |
| pEdit = mpEdActive = &maEdTargetValue; |
| else if( pCtrl == &maEdVariableCells || pCtrl == &maRBVariableCells ) |
| pEdit = mpEdActive = &maEdVariableCells; |
| for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow ) |
| { |
| if( pCtrl == mpLeftEdit[nRow] || pCtrl == mpLeftButton[nRow] ) |
| pEdit = mpEdActive = mpLeftEdit[nRow]; |
| else if( pCtrl == mpRightEdit[nRow] || pCtrl == mpRightButton[nRow] ) |
| pEdit = mpEdActive = mpRightEdit[nRow]; |
| else if( pCtrl == mpOperator[nRow] ) // focus on "operator" list box |
| mpEdActive = mpRightEdit[nRow]; // use right edit for ref input, but don't change selection |
| } |
| if( pCtrl == &maRbValue ) // focus on "Value of" radio button |
| mpEdActive = &maEdTargetValue; // use value edit for ref input, but don't change selection |
| |
| if( pEdit ) |
| pEdit->SetSelection( Selection( 0, SELECTION_MAX ) ); |
| |
| return 0; |
| } |
| |
| //---------------------------------------------------------------------------- |
| |
| IMPL_LINK( ScOptSolverDlg, LoseFocusHdl, Control*, EMPTYARG ) |
| { |
| mbDlgLostFocus = !IsActive(); |
| return 0; |
| } |
| |
| //---------------------------------------------------------------------------- |
| |
| IMPL_LINK( ScOptSolverDlg, DelBtnHdl, PushButton*, pBtn ) |
| { |
| for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow ) |
| if( pBtn == mpDelButton[nRow] ) |
| { |
| sal_Bool bHadFocus = pBtn->HasFocus(); |
| |
| ReadConditions(); |
| long nVecPos = nScrollPos + nRow; |
| if ( nVecPos < (long)maConditions.size() ) |
| { |
| maConditions.erase( maConditions.begin() + nVecPos ); |
| ShowConditions(); |
| |
| if ( bHadFocus && !pBtn->IsEnabled() ) |
| { |
| // If the button is disabled, focus would normally move to the next control, |
| // (left edit of the next row). Move it to left edit of this row instead. |
| |
| mpEdActive = mpLeftEdit[nRow]; |
| mpEdActive->GrabFocus(); |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| //---------------------------------------------------------------------------- |
| |
| IMPL_LINK( ScOptSolverDlg, TargetModifyHdl, Edit*, EMPTYARG ) |
| { |
| // modify handler for the target edit: |
| // select "Value of" if something is input into the edit |
| if ( maEdTargetValue.GetText().Len() ) |
| maRbValue.Check(); |
| return 0; |
| } |
| |
| IMPL_LINK( ScOptSolverDlg, CondModifyHdl, Edit*, EMPTYARG ) |
| { |
| // modify handler for the condition edits, just to enable/disable "delete" buttons |
| ReadConditions(); |
| EnableButtons(); |
| return 0; |
| } |
| |
| IMPL_LINK( ScOptSolverDlg, SelectHdl, ListBox*, EMPTYARG ) |
| { |
| // select handler for operator list boxes, just to enable/disable "delete" buttons |
| ReadConditions(); |
| EnableButtons(); |
| return 0; |
| } |
| |
| IMPL_LINK( ScOptSolverDlg, ScrollHdl, ScrollBar*, EMPTYARG ) |
| { |
| ReadConditions(); |
| nScrollPos = maScrollBar.GetThumbPos(); |
| ShowConditions(); |
| if( mpEdActive ) |
| mpEdActive->SetSelection( Selection( 0, SELECTION_MAX ) ); |
| return 0; |
| } |
| |
| IMPL_LINK( ScOptSolverDlg, CursorUpHdl, ScCursorRefEdit*, pEdit ) |
| { |
| if ( pEdit == mpLeftEdit[0] || pEdit == mpRightEdit[0] ) |
| { |
| if ( nScrollPos > 0 ) |
| { |
| ReadConditions(); |
| --nScrollPos; |
| ShowConditions(); |
| if( mpEdActive ) |
| mpEdActive->SetSelection( Selection( 0, SELECTION_MAX ) ); |
| } |
| } |
| else |
| { |
| formula::RefEdit* pFocus = NULL; |
| for ( sal_uInt16 nRow = 1; nRow < EDIT_ROW_COUNT; ++nRow ) // second row or below: move focus |
| { |
| if ( pEdit == mpLeftEdit[nRow] ) |
| pFocus = mpLeftEdit[nRow-1]; |
| else if ( pEdit == mpRightEdit[nRow] ) |
| pFocus = mpRightEdit[nRow-1]; |
| } |
| if (pFocus) |
| { |
| mpEdActive = pFocus; |
| pFocus->GrabFocus(); |
| } |
| } |
| |
| return 0; |
| } |
| |
| IMPL_LINK( ScOptSolverDlg, CursorDownHdl, ScCursorRefEdit*, pEdit ) |
| { |
| if ( pEdit == mpLeftEdit[EDIT_ROW_COUNT-1] || pEdit == mpRightEdit[EDIT_ROW_COUNT-1] ) |
| { |
| //! limit scroll position? |
| ReadConditions(); |
| ++nScrollPos; |
| ShowConditions(); |
| if( mpEdActive ) |
| mpEdActive->SetSelection( Selection( 0, SELECTION_MAX ) ); |
| } |
| else |
| { |
| formula::RefEdit* pFocus = NULL; |
| for ( sal_uInt16 nRow = 0; nRow+1 < EDIT_ROW_COUNT; ++nRow ) // before last row: move focus |
| { |
| if ( pEdit == mpLeftEdit[nRow] ) |
| pFocus = mpLeftEdit[nRow+1]; |
| else if ( pEdit == mpRightEdit[nRow] ) |
| pFocus = mpRightEdit[nRow+1]; |
| } |
| if (pFocus) |
| { |
| mpEdActive = pFocus; |
| pFocus->GrabFocus(); |
| } |
| } |
| |
| return 0; |
| } |
| |
| //---------------------------------------------------------------------------- |
| |
| void ScOptSolverDlg::ShowError( bool bCondition, formula::RefEdit* pFocus ) |
| { |
| String aMessage = bCondition ? maConditionError : maInputError; |
| ErrorBox( this, WinBits( WB_OK | WB_DEF_OK ), aMessage ).Execute(); |
| if (pFocus) |
| { |
| mpEdActive = pFocus; |
| pFocus->GrabFocus(); |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| |
| bool ScOptSolverDlg::ParseRef( ScRange& rRange, const String& rInput, bool bAllowRange ) |
| { |
| ScRangeUtil aRangeUtil; |
| ScAddress::Details aDetails(mpDoc->GetAddressConvention(), 0, 0); |
| sal_uInt16 nFlags = rRange.ParseAny( rInput, mpDoc, aDetails ); |
| if ( nFlags & SCA_VALID ) |
| { |
| if ( (nFlags & SCA_TAB_3D) == 0 ) |
| rRange.aStart.SetTab( mnCurTab ); |
| if ( (nFlags & SCA_TAB2_3D) == 0 ) |
| rRange.aEnd.SetTab( rRange.aStart.Tab() ); |
| return ( bAllowRange || rRange.aStart == rRange.aEnd ); |
| } |
| else if ( aRangeUtil.MakeRangeFromName( rInput, mpDoc, mnCurTab, rRange, RUTL_NAMES, aDetails ) ) |
| return ( bAllowRange || rRange.aStart == rRange.aEnd ); |
| |
| return false; // not recognized |
| } |
| |
| bool ScOptSolverDlg::FindTimeout( sal_Int32& rTimeout ) |
| { |
| bool bFound = false; |
| |
| if ( !maProperties.getLength() ) |
| maProperties = ScSolverUtil::GetDefaults( maEngine ); // get property defaults from component |
| |
| sal_Int32 nPropCount = maProperties.getLength(); |
| for (sal_Int32 nProp=0; nProp<nPropCount && !bFound; ++nProp) |
| { |
| const beans::PropertyValue& rValue = maProperties[nProp]; |
| if ( rValue.Name.equalsAscii( SC_UNONAME_TIMEOUT ) ) |
| bFound = ( rValue.Value >>= rTimeout ); |
| } |
| return bFound; |
| } |
| |
| bool ScOptSolverDlg::CallSolver() // return true -> close dialog after calling |
| { |
| // show progress dialog |
| |
| ScSolverProgressDialog aProgress( this ); |
| sal_Int32 nTimeout = 0; |
| if ( FindTimeout( nTimeout ) ) |
| aProgress.SetTimeLimit( nTimeout ); |
| else |
| aProgress.HideTimeLimit(); |
| aProgress.Show(); |
| aProgress.Update(); |
| aProgress.Sync(); |
| // try to make sure the progress dialog is painted before continuing |
| Application::Reschedule(true); |
| |
| // collect solver parameters |
| |
| ReadConditions(); |
| |
| uno::Reference<sheet::XSpreadsheetDocument> xDocument( mpDocShell->GetModel(), uno::UNO_QUERY ); |
| |
| ScRange aObjRange; |
| if ( !ParseRef( aObjRange, maEdObjectiveCell.GetText(), false ) ) |
| { |
| ShowError( false, &maEdObjectiveCell ); |
| return false; |
| } |
| table::CellAddress aObjective( aObjRange.aStart.Tab(), aObjRange.aStart.Col(), aObjRange.aStart.Row() ); |
| |
| // "changing cells" can be several ranges |
| ScRangeList aVarRanges; |
| if ( !ParseWithNames( aVarRanges, maEdVariableCells.GetText(), mpDoc ) ) |
| { |
| ShowError( false, &maEdVariableCells ); |
| return false; |
| } |
| uno::Sequence<table::CellAddress> aVariables; |
| sal_Int32 nVarPos = 0; |
| sal_uLong nRangeCount = aVarRanges.Count(); |
| for (sal_uLong nRangePos=0; nRangePos<nRangeCount; ++nRangePos) |
| { |
| ScRange aRange(*aVarRanges.GetObject(nRangePos)); |
| aRange.Justify(); |
| SCTAB nTab = aRange.aStart.Tab(); |
| |
| // resolve into single cells |
| |
| sal_Int32 nAdd = ( aRange.aEnd.Col() - aRange.aStart.Col() + 1 ) * |
| ( aRange.aEnd.Row() - aRange.aStart.Row() + 1 ); |
| aVariables.realloc( nVarPos + nAdd ); |
| |
| for (SCROW nRow = aRange.aStart.Row(); nRow <= aRange.aEnd.Row(); ++nRow) |
| for (SCCOL nCol = aRange.aStart.Col(); nCol <= aRange.aEnd.Col(); ++nCol) |
| aVariables[nVarPos++] = table::CellAddress( nTab, nCol, nRow ); |
| } |
| |
| uno::Sequence<sheet::SolverConstraint> aConstraints; |
| sal_Int32 nConstrPos = 0; |
| for ( std::vector<ScOptConditionRow>::const_iterator aConstrIter = maConditions.begin(); |
| aConstrIter != maConditions.end(); ++aConstrIter ) |
| { |
| if ( aConstrIter->aLeftStr.Len() ) |
| { |
| sheet::SolverConstraint aConstraint; |
| // order of list box entries must match enum values |
| aConstraint.Operator = static_cast<sheet::SolverConstraintOperator>(aConstrIter->nOperator); |
| |
| ScRange aLeftRange; |
| if ( !ParseRef( aLeftRange, aConstrIter->aLeftStr, true ) ) |
| { |
| ShowError( true, NULL ); |
| return false; |
| } |
| |
| bool bIsRange = false; |
| ScRange aRightRange; |
| if ( ParseRef( aRightRange, aConstrIter->aRightStr, true ) ) |
| { |
| if ( aRightRange.aStart == aRightRange.aEnd ) |
| aConstraint.Right <<= table::CellAddress( aRightRange.aStart.Tab(), |
| aRightRange.aStart.Col(), aRightRange.aStart.Row() ); |
| else if ( aRightRange.aEnd.Col()-aRightRange.aStart.Col() == aLeftRange.aEnd.Col()-aLeftRange.aStart.Col() && |
| aRightRange.aEnd.Row()-aRightRange.aStart.Row() == aLeftRange.aEnd.Row()-aLeftRange.aStart.Row() ) |
| bIsRange = true; // same size as "left" range, resolve into single cells |
| else |
| { |
| ShowError( true, NULL ); |
| return false; |
| } |
| } |
| else |
| { |
| sal_uInt32 nFormat = 0; //! explicit language? |
| double fValue = 0.0; |
| if ( mpDoc->GetFormatTable()->IsNumberFormat( aConstrIter->aRightStr, nFormat, fValue ) ) |
| aConstraint.Right <<= fValue; |
| else if ( aConstraint.Operator != sheet::SolverConstraintOperator_INTEGER && |
| aConstraint.Operator != sheet::SolverConstraintOperator_BINARY ) |
| { |
| ShowError( true, NULL ); |
| return false; |
| } |
| } |
| |
| // resolve into single cells |
| |
| sal_Int32 nAdd = ( aLeftRange.aEnd.Col() - aLeftRange.aStart.Col() + 1 ) * |
| ( aLeftRange.aEnd.Row() - aLeftRange.aStart.Row() + 1 ); |
| aConstraints.realloc( nConstrPos + nAdd ); |
| |
| for (SCROW nRow = aLeftRange.aStart.Row(); nRow <= aLeftRange.aEnd.Row(); ++nRow) |
| for (SCCOL nCol = aLeftRange.aStart.Col(); nCol <= aLeftRange.aEnd.Col(); ++nCol) |
| { |
| aConstraint.Left = table::CellAddress( aLeftRange.aStart.Tab(), nCol, nRow ); |
| if ( bIsRange ) |
| aConstraint.Right <<= table::CellAddress( aRightRange.aStart.Tab(), |
| aRightRange.aStart.Col() + ( nCol - aLeftRange.aStart.Col() ), |
| aRightRange.aStart.Row() + ( nRow - aLeftRange.aStart.Row() ) ); |
| |
| aConstraints[nConstrPos++] = aConstraint; |
| } |
| } |
| } |
| |
| sal_Bool bMaximize = maRbMax.IsChecked(); |
| if ( maRbValue.IsChecked() ) |
| { |
| // handle "value of" with an additional constraint (and then minimize) |
| |
| sheet::SolverConstraint aConstraint; |
| aConstraint.Left = aObjective; |
| aConstraint.Operator = sheet::SolverConstraintOperator_EQUAL; |
| |
| String aValStr = maEdTargetValue.GetText(); |
| ScRange aRightRange; |
| if ( ParseRef( aRightRange, aValStr, false ) ) |
| aConstraint.Right <<= table::CellAddress( aRightRange.aStart.Tab(), |
| aRightRange.aStart.Col(), aRightRange.aStart.Row() ); |
| else |
| { |
| sal_uInt32 nFormat = 0; //! explicit language? |
| double fValue = 0.0; |
| if ( mpDoc->GetFormatTable()->IsNumberFormat( aValStr, nFormat, fValue ) ) |
| aConstraint.Right <<= fValue; |
| else |
| { |
| ShowError( false, &maEdTargetValue ); |
| return false; |
| } |
| } |
| |
| aConstraints.realloc( nConstrPos + 1 ); |
| aConstraints[nConstrPos++] = aConstraint; |
| } |
| |
| // copy old document values |
| |
| sal_Int32 nVarCount = aVariables.getLength(); |
| uno::Sequence<double> aOldValues; |
| aOldValues.realloc( nVarCount ); |
| for (nVarPos=0; nVarPos<nVarCount; ++nVarPos) |
| { |
| ScAddress aCellPos; |
| ScUnoConversion::FillScAddress( aCellPos, aVariables[nVarPos] ); |
| aOldValues[nVarPos] = mpDoc->GetValue( aCellPos ); |
| } |
| |
| // create and initialize solver |
| |
| uno::Reference<sheet::XSolver> xSolver = ScSolverUtil::GetSolver( maEngine ); |
| DBG_ASSERT( xSolver.is(), "can't get solver component" ); |
| if ( !xSolver.is() ) |
| return false; |
| |
| xSolver->setDocument( xDocument ); |
| xSolver->setObjective( aObjective ); |
| xSolver->setVariables( aVariables ); |
| xSolver->setConstraints( aConstraints ); |
| xSolver->setMaximize( bMaximize ); |
| |
| // set options |
| uno::Reference<beans::XPropertySet> xOptProp(xSolver, uno::UNO_QUERY); |
| if ( xOptProp.is() ) |
| { |
| sal_Int32 nPropCount = maProperties.getLength(); |
| for (sal_Int32 nProp=0; nProp<nPropCount; ++nProp) |
| { |
| const beans::PropertyValue& rValue = maProperties[nProp]; |
| try |
| { |
| xOptProp->setPropertyValue( rValue.Name, rValue.Value ); |
| } |
| catch ( uno::Exception & ) |
| { |
| DBG_ERRORFILE("Exception in solver option property"); |
| } |
| } |
| } |
| |
| xSolver->solve(); |
| sal_Bool bSuccess = xSolver->getSuccess(); |
| |
| aProgress.Hide(); |
| bool bClose = false; |
| bool bRestore = true; // restore old values unless a solution is accepted |
| if ( bSuccess ) |
| { |
| // put solution into document so it is visible when asking |
| uno::Sequence<double> aSolution = xSolver->getSolution(); |
| if ( aSolution.getLength() == nVarCount ) |
| { |
| mpDocShell->LockPaint(); |
| ScDocFunc aFunc(*mpDocShell); |
| for (nVarPos=0; nVarPos<nVarCount; ++nVarPos) |
| { |
| ScAddress aCellPos; |
| ScUnoConversion::FillScAddress( aCellPos, aVariables[nVarPos] ); |
| aFunc.PutCell( aCellPos, new ScValueCell( aSolution[nVarPos] ), sal_True ); |
| } |
| mpDocShell->UnlockPaint(); |
| } |
| //! else error? |
| |
| // take formatted result from document (result value from component is ignored) |
| String aResultStr; |
| mpDoc->GetString( (SCCOL)aObjective.Column, (SCROW)aObjective.Row, (SCTAB)aObjective.Sheet, aResultStr ); |
| ScSolverSuccessDialog aDialog( this, aResultStr ); |
| if ( aDialog.Execute() == RET_OK ) |
| { |
| // keep results and close dialog |
| bRestore = false; |
| bClose = true; |
| } |
| } |
| else |
| { |
| rtl::OUString aError; |
| uno::Reference<sheet::XSolverDescription> xDesc( xSolver, uno::UNO_QUERY ); |
| if ( xDesc.is() ) |
| aError = xDesc->getStatusDescription(); // error description from component |
| ScSolverNoSolutionDialog aDialog( this, aError ); |
| aDialog.Execute(); |
| } |
| |
| if ( bRestore ) // restore old values |
| { |
| mpDocShell->LockPaint(); |
| ScDocFunc aFunc(*mpDocShell); |
| for (nVarPos=0; nVarPos<nVarCount; ++nVarPos) |
| { |
| ScAddress aCellPos; |
| ScUnoConversion::FillScAddress( aCellPos, aVariables[nVarPos] ); |
| aFunc.PutCell( aCellPos, new ScValueCell( aOldValues[nVarPos] ), sal_True ); |
| } |
| mpDocShell->UnlockPaint(); |
| } |
| |
| return bClose; |
| } |
| |