diff options
Diffstat (limited to 'sc/source/ui/miscdlgs/optsolver.cxx')
-rw-r--r-- | sc/source/ui/miscdlgs/optsolver.cxx | 1068 |
1 files changed, 1068 insertions, 0 deletions
diff --git a/sc/source/ui/miscdlgs/optsolver.cxx b/sc/source/ui/miscdlgs/optsolver.cxx new file mode 100644 index 000000000000..9dd2e84550ce --- /dev/null +++ b/sc/source/ui/miscdlgs/optsolver.cxx @@ -0,0 +1,1068 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 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(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, BOOL bMax, BOOL bMin, 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; + + 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, FALSE, FALSE ); + Image aDelHC = ::GetImage( xFrame, aSlotURL, FALSE, 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() ); + } +} + +//---------------------------------------------------------------------------- + +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 + { + USHORT 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(); + } +} + +//---------------------------------------------------------------------------- + +BOOL ScOptSolverDlg::IsRefInputMode() const +{ + return mpEdActive != NULL; +} + +//---------------------------------------------------------------------------- +// Handler: + +IMPL_LINK( ScOptSolverDlg, BtnHdl, PushButton*, pBtn ) +{ + if ( pBtn == &maBtnSolve || pBtn == &maBtnCancel ) + { + bool bSolve = ( pBtn == &maBtnSolve ); + + SetDispatcherLock( 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( 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] ) + { + 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); + USHORT 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; + ULONG nRangeCount = aVarRanges.Count(); + for (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] ), 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] ), TRUE ); + } + mpDocShell->UnlockPaint(); + } + + return bClose; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |