Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  optsolver.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   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 .
 */


#include <rangelst.hxx>
#include <sfx2/bindings.hxx>
#include <svl/numformat.hxx>
#include <utility>
#include <vcl/commandinfoprovider.hxx>
#include <vcl/weld.hxx>
#include <vcl/svapp.hxx>

#include <reffact.hxx>
#include <docsh.hxx>
#include <docfunc.hxx>
#include <rangeutl.hxx>
#include <convuno.hxx>
#include <unonames.hxx>
#include <solveroptions.hxx>
#include <solverutil.hxx>
#include <globstr.hrc>
#include <scresid.hxx>
#include <comphelper/sequence.hxx>
#include <optsolver.hxx>
#include <table.hxx>
#include <TableFillingAndNavigationTools.hxx>
#include <tabvwsh.hxx>

#include <com/sun/star/beans/XPropertySetInfo.hpp>
#include <com/sun/star/sheet/SolverConstraint.hpp>
#include <com/sun/star/sheet/SolverConstraintOperator.hpp>
#include <com/sun/star/sheet/XSolverDescription.hpp>
#include <com/sun/star/sheet/XSolver.hpp>
#include <com/sun/star/sheet/SensitivityReport.hpp>

using namespace com::sun::star;

ScSolverProgressDialog::ScSolverProgressDialog(weld::Window* pParent)
    : GenericDialogController(pParent, u"modules/scalc/ui/solverprogressdialog.ui"_ustr,
                              u"SolverProgressDialog"_ustr)
    , m_xFtTime(m_xBuilder->weld_label(u"progress"_ustr))
{
}

ScSolverProgressDialog::~ScSolverProgressDialog()
{
}

void ScSolverProgressDialog::HideTimeLimit()
{
    m_xFtTime->hide();
}

void ScSolverProgressDialog::SetTimeLimit( sal_Int32 nSeconds )
{
    OUString aOld = m_xFtTime->get_label();
    OUString aNew = aOld.replaceFirst("#", OUString::number(nSeconds));
    m_xFtTime->set_label(aNew);
}

ScSolverNoSolutionDialog::ScSolverNoSolutionDialog(weld::Window* pParent, const OUString& rErrorText)
    : GenericDialogController(pParent, u"modules/scalc/ui/nosolutiondialog.ui"_ustr, u"NoSolutionDialog"_ustr)
    , m_xFtErrorText(m_xBuilder->weld_label(u"error"_ustr))
{
    m_xFtErrorText->set_label(rErrorText);
}

ScSolverNoSolutionDialog::~ScSolverNoSolutionDialog()
{
}

ScSolverSuccessDialog::ScSolverSuccessDialog(weld::Window* pParent, std::u16string_view rSolution)
    : GenericDialogController(pParent, u"modules/scalc/ui/solversuccessdialog.ui"_ustr, u"SolverSuccessDialog"_ustr)
    , m_xFtResult(m_xBuilder->weld_label(u"result"_ustr))
    , m_xBtnOk(m_xBuilder->weld_button(u"ok"_ustr))
    , m_xBtnCancel(m_xBuilder->weld_button(u"cancel"_ustr))
{
    m_xBtnOk->connect_clicked(LINK(this, ScSolverSuccessDialog, ClickHdl));
    m_xBtnCancel->connect_clicked(LINK(this, ScSolverSuccessDialog, ClickHdl));
    OUString aMessage = m_xFtResult->get_label() + " " + rSolution;
    m_xFtResult->set_label(aMessage);
}

ScSolverSuccessDialog::~ScSolverSuccessDialog()
{
}

IMPL_LINK(ScSolverSuccessDialog, ClickHdl, weld::Button&, rBtn, void)
{
    if (&rBtn == m_xBtnOk.get())
        m_xDialog->response(RET_OK);
    else
        m_xDialog->response(RET_CANCEL);
}

ScCursorRefEdit::ScCursorRefEdit(std::unique_ptr<weld::Entry> xControl)
    : formula::RefEdit(std::move(xControl))
{
    xEntry->connect_key_press(Link<const KeyEvent&, bool>()); //acknowledge we first remove the old one
    xEntry->connect_key_press(LINK(this, ScCursorRefEdit, KeyInputHdl));
}

void ScCursorRefEdit::SetCursorLinks( const Link<ScCursorRefEdit&,void>& rUp, const Link<ScCursorRefEdit&,void>& rDown )
{
    maCursorUpLink = rUp;
    maCursorDownLink = rDown;
}

IMPL_LINK(ScCursorRefEdit, KeyInputHdl, const KeyEvent&, rKEvt, bool)
{
    vcl::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 );
        return true;
    }
    return formula::RefEdit::KeyInput(rKEvt);
}

ScOptSolverDlg::ScOptSolverDlg(SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent,
                               ScDocShell& rDocSh, const ScAddress& aCursorPos)
    : ScAnyRefDlgController(pB, pCW, pParent, u"modules/scalc/ui/solverdlg.ui"_ustr, u"SolverDialog"_ustr)
    , maInputError(ScResId(STR_INVALIDINPUT))
    , maConditionError(ScResId(STR_INVALIDCONDITION))

    , mrDocShell(rDocSh)
    , mrDoc(rDocSh.GetDocument())
    , mnCurTab(aCursorPos.Tab())
    , mbDlgLostFocus(false)
    , nScrollPos(0)
    , mpEdActive(nullptr)
    , m_xFtObjectiveCell(m_xBuilder->weld_label(u"targetlabel"_ustr))
    , m_xEdObjectiveCell(new formula::RefEdit(m_xBuilder->weld_entry(u"targetedit"_ustr)))
    , m_xRBObjectiveCell(new formula::RefButton(m_xBuilder->weld_button(u"targetbutton"_ustr)))
    , m_xRbMax(m_xBuilder->weld_radio_button(u"max"_ustr))
    , m_xRbMin(m_xBuilder->weld_radio_button(u"min"_ustr))
    , m_xRbValue(m_xBuilder->weld_radio_button(u"value"_ustr))
    , m_xEdTargetValue(new formula::RefEdit(m_xBuilder->weld_entry(u"valueedit"_ustr)))
    , m_xRBTargetValue(new formula::RefButton(m_xBuilder->weld_button(u"valuebutton"_ustr)))
    , m_xFtVariableCells(m_xBuilder->weld_label(u"changelabel"_ustr))
    , m_xEdVariableCells(new formula::RefEdit(m_xBuilder->weld_entry(u"changeedit"_ustr)))
    , m_xRBVariableCells(new formula::RefButton(m_xBuilder->weld_button(u"changebutton"_ustr)))
    , m_xFtCellRef(m_xBuilder->weld_label(u"cellreflabel"_ustr))
    , m_xEdLeft1(new ScCursorRefEdit(m_xBuilder->weld_entry(u"ref1edit"_ustr)))
    , m_xRBLeft1(new formula::RefButton(m_xBuilder->weld_button(u"ref1button"_ustr)))
    , m_xLbOp1(m_xBuilder->weld_combo_box(u"op1list"_ustr))
    , m_xFtConstraint(m_xBuilder->weld_label(u"constraintlabel"_ustr))
    , m_xEdRight1(new ScCursorRefEdit(m_xBuilder->weld_entry(u"val1edit"_ustr)))
    , m_xRBRight1(new formula::RefButton(m_xBuilder->weld_button(u"val1button"_ustr)))
    , m_xBtnDel1(m_xBuilder->weld_button(u"del1"_ustr))
    , m_xEdLeft2(new ScCursorRefEdit(m_xBuilder->weld_entry(u"ref2edit"_ustr)))
    , m_xRBLeft2(new formula::RefButton(m_xBuilder->weld_button(u"ref2button"_ustr)))
    , m_xLbOp2(m_xBuilder->weld_combo_box(u"op2list"_ustr))
    , m_xEdRight2(new ScCursorRefEdit(m_xBuilder->weld_entry(u"val2edit"_ustr)))
    , m_xRBRight2(new formula::RefButton(m_xBuilder->weld_button(u"val2button"_ustr)))
    , m_xBtnDel2(m_xBuilder->weld_button(u"del2"_ustr))
    , m_xEdLeft3(new ScCursorRefEdit(m_xBuilder->weld_entry(u"ref3edit"_ustr)))
    , m_xRBLeft3(new formula::RefButton(m_xBuilder->weld_button(u"ref3button"_ustr)))
    , m_xLbOp3(m_xBuilder->weld_combo_box(u"op3list"_ustr))
    , m_xEdRight3(new ScCursorRefEdit(m_xBuilder->weld_entry(u"val3edit"_ustr)))
    , m_xRBRight3(new formula::RefButton(m_xBuilder->weld_button(u"val3button"_ustr)))
    , m_xBtnDel3(m_xBuilder->weld_button(u"del3"_ustr))
    , m_xEdLeft4(new ScCursorRefEdit(m_xBuilder->weld_entry(u"ref4edit"_ustr)))
    , m_xRBLeft4(new formula::RefButton(m_xBuilder->weld_button(u"ref4button"_ustr)))
    , m_xLbOp4(m_xBuilder->weld_combo_box(u"op4list"_ustr))
    , m_xEdRight4(new ScCursorRefEdit(m_xBuilder->weld_entry(u"val4edit"_ustr)))
    , m_xRBRight4(new formula::RefButton(m_xBuilder->weld_button(u"val4button"_ustr)))
    , m_xBtnDel4(m_xBuilder->weld_button(u"del4"_ustr))
    , m_xScrollBar(m_xBuilder->weld_scrolled_window(u"scrollbar"_ustr, true))
    , m_xBtnOpt(m_xBuilder->weld_button(u"options"_ustr))
    , m_xBtnClose(m_xBuilder->weld_button(u"close"_ustr))
    , m_xBtnSolve(m_xBuilder->weld_button(u"ok"_ustr))
    , m_xBtnResetAll(m_xBuilder->weld_button(u"resetall"_ustr))
    , m_xResultFT(m_xBuilder->weld_label(u"result"_ustr))
    , m_xContents(m_xBuilder->weld_widget(u"grid"_ustr))
    , m_pSolverSettings(mrDoc.FetchTable(mnCurTab)->GetSolverSettings())
{
    m_xEdObjectiveCell->SetReferences(this, m_xFtObjectiveCell.get());
    m_xRBObjectiveCell->SetReferences(this, m_xEdObjectiveCell.get());
    m_xEdTargetValue->SetReferences(this, m_xResultFT.get());
    m_xRBTargetValue->SetReferences(this, m_xEdTargetValue.get());
    m_xEdVariableCells->SetReferences(this, m_xFtVariableCells.get());
    m_xRBVariableCells->SetReferences(this, m_xEdVariableCells.get());
    m_xEdLeft1->SetReferences(this, m_xFtCellRef.get());
    m_xRBLeft1->SetReferences(this, m_xEdLeft1.get());
    m_xEdRight1->SetReferences(this, m_xFtConstraint.get());
    m_xRBRight1->SetReferences(this, m_xEdRight1.get());
    m_xEdLeft2->SetReferences(this, m_xFtCellRef.get());
    m_xRBLeft2->SetReferences(this, m_xEdLeft2.get());
    m_xEdRight2->SetReferences(this, m_xFtConstraint.get());
    m_xRBRight2->SetReferences(this, m_xEdRight2.get());
    m_xEdLeft3->SetReferences(this, m_xFtCellRef.get());
    m_xRBLeft3->SetReferences(this, m_xEdLeft3.get());
    m_xEdRight3->SetReferences(this, m_xFtConstraint.get());
    m_xRBRight3->SetReferences(this, m_xEdRight3.get());
    m_xEdLeft4->SetReferences(this, m_xFtCellRef.get());
    m_xRBLeft4->SetReferences(this, m_xEdLeft4.get());
    m_xEdRight4->SetReferences(this, m_xFtConstraint.get());
    m_xRBRight4->SetReferences(this, m_xEdRight4.get());

    mpLeftEdit[0]    = m_xEdLeft1.get();
    mpLeftButton[0]  = m_xRBLeft1.get();
    mpRightEdit[0]   = m_xEdRight1.get();
    mpRightButton[0] = m_xRBRight1.get();
    mpOperator[0]    = m_xLbOp1.get();
    mpDelButton[0]   = m_xBtnDel1.get();

    mpLeftEdit[1]    = m_xEdLeft2.get();
    mpLeftButton[1]  = m_xRBLeft2.get();
    mpRightEdit[1]   = m_xEdRight2.get();
    mpRightButton[1] = m_xRBRight2.get();
    mpOperator[1]    = m_xLbOp2.get();
    mpDelButton[1]   = m_xBtnDel2.get();

    mpLeftEdit[2]    = m_xEdLeft3.get();
    mpLeftButton[2]  = m_xRBLeft3.get();
    mpRightEdit[2]   = m_xEdRight3.get();
    mpRightButton[2] = m_xRBRight3.get();
    mpOperator[2]    = m_xLbOp3.get();
    mpDelButton[2]   = m_xBtnDel3.get();

    mpLeftEdit[3]    = m_xEdLeft4.get();
    mpLeftButton[3]  = m_xRBLeft4.get();
    mpRightEdit[3]   = m_xEdRight4.get();
    mpRightButton[3] = m_xRBRight4.get();
    mpOperator[3]    = m_xLbOp4.get();
    mpDelButton[3]   = m_xBtnDel4.get();

    Init( aCursorPos );
}

ScOptSolverDlg::~ScOptSolverDlg()
{
}

void ScOptSolverDlg::Init(const ScAddress& rCursorPos)
{
    uno::Reference<frame::XFrame> xFrame = GetBindings().GetActiveFrame();
    auto xDelNm = vcl::CommandInfoProvider::GetXGraphicForCommand(u".uno:DeleteRows"_ustr, xFrame);
    for (weld::Button* pButton : mpDelButton)
        pButton->set_image(xDelNm);

    m_xBtnOpt->connect_clicked( LINK( this, ScOptSolverDlg, BtnHdl ) );
    m_xBtnClose->connect_clicked( LINK( this, ScOptSolverDlg, BtnHdl ) );
    m_xBtnSolve->connect_clicked( LINK( this, ScOptSolverDlg, BtnHdl ) );
    m_xBtnResetAll->connect_clicked( LINK( this, ScOptSolverDlg, BtnHdl ) );

    Link<formula::RefEdit&,void> aEditLink = LINK( this, ScOptSolverDlg, GetEditFocusHdl );
    Link<formula::RefButton&,void> aButtonLink = LINK( this, ScOptSolverDlg, GetButtonFocusHdl );
    m_xEdObjectiveCell->SetGetFocusHdl( aEditLink );
    m_xRBObjectiveCell->SetGetFocusHdl( aButtonLink );
    m_xEdTargetValue->SetGetFocusHdl( aEditLink );
    m_xRBTargetValue->SetGetFocusHdl( aButtonLink );
    m_xEdVariableCells->SetGetFocusHdl( aEditLink );
    m_xRBVariableCells->SetGetFocusHdl( aButtonLink );
    Link<weld::Widget&,void> aLink = LINK(this, ScOptSolverDlg, GetFocusHdl);
    m_xRbValue->connect_focus_in(aLink);
    for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow )
    {
        mpLeftEdit[nRow]->SetGetFocusHdl( aEditLink );
        mpLeftButton[nRow]->SetGetFocusHdl( aButtonLink );
        mpRightEdit[nRow]->SetGetFocusHdl( aEditLink );
        mpRightButton[nRow]->SetGetFocusHdl( aButtonLink );
        mpOperator[nRow]->connect_focus_in(aLink);
    }

    aEditLink = LINK( this, ScOptSolverDlg, LoseEditFocusHdl );
    aButtonLink = LINK( this, ScOptSolverDlg, LoseButtonFocusHdl );
    m_xEdObjectiveCell->SetLoseFocusHdl( aEditLink );
    m_xRBObjectiveCell->SetLoseFocusHdl( aButtonLink );
    m_xEdTargetValue->SetLoseFocusHdl( aEditLink );
    m_xRBTargetValue-> SetLoseFocusHdl( aButtonLink );
    m_xEdVariableCells->SetLoseFocusHdl( aEditLink );
    m_xRBVariableCells->SetLoseFocusHdl( aButtonLink );
    for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow )
    {
        mpLeftEdit[nRow]->SetLoseFocusHdl( aEditLink );
        mpLeftButton[nRow]->SetLoseFocusHdl( aButtonLink );
        mpRightEdit[nRow]->SetLoseFocusHdl( aEditLink );
        mpRightButton[nRow]->SetLoseFocusHdl( aButtonLink );
    }

    Link<ScCursorRefEdit&,void> aCursorUp = LINK( this, ScOptSolverDlg, CursorUpHdl );
    Link<ScCursorRefEdit&,void> aCursorDown = LINK( this, ScOptSolverDlg, CursorDownHdl );
    Link<formula::RefEdit&,void> 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]->connect_clicked( LINK( this, ScOptSolverDlg, DelBtnHdl ) );
        mpOperator[nRow]->connect_changed( LINK( this, ScOptSolverDlg, SelectHdl ) );
    }
    m_xEdTargetValue->SetModifyHdl( LINK( this, ScOptSolverDlg, TargetModifyHdl ) );

    Size aSize(m_xContents->get_preferred_size());
    m_xContents->set_size_request(aSize.Width(), aSize.Height());
    m_xScrollBar->connect_vadjustment_changed( LINK( this, ScOptSolverDlg, ScrollHdl ) );

    m_xScrollBar->vadjustment_set_page_increment( EDIT_ROW_COUNT );
    m_xScrollBar->vadjustment_set_page_size( EDIT_ROW_COUNT );
    // Range is set in ShowConditions

    // get available solver implementations
    //! sort by descriptions?
    ScSolverUtil::GetImplementations( maImplNames, maDescriptions );

    // Load existing settings stored in the tab
    LoadSolverSettings();
    ShowConditions();

    // If no objective cell has been loaded, then use the selected cell
    if (m_xEdObjectiveCell->GetText().isEmpty())
    {
        OUString aCursorStr;
        if (!mrDoc.GetRangeAtBlock(ScRange(rCursorPos), aCursorStr))
            aCursorStr = rCursorPos.Format(ScRefFlags::ADDR_ABS, nullptr, mrDoc.GetAddressConvention());
        m_xEdObjectiveCell->SetRefString(aCursorStr);
    }

    m_xEdObjectiveCell->GrabFocus();
    mpEdActive = m_xEdObjectiveCell.get();
}

void ScOptSolverDlg::ReadConditions()
{
    for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow )
    {
        sc::ModelConstraint aRowEntry;
        aRowEntry.aLeftStr = mpLeftEdit[nRow]->GetText();
        aRowEntry.aRightStr = mpRightEdit[nRow]->GetText();
        aRowEntry.nOperator = OperatorIndexToConstraintOperator(mpOperator[nRow]->get_active());

        tools::Long nVecPos = nScrollPos + nRow;
        if ( nVecPos >= static_cast<tools::Long>(m_aConditions.size()) && !aRowEntry.IsDefault() )
            m_aConditions.resize( nVecPos + 1 );

        if ( nVecPos < static_cast<tools::Long>(m_aConditions.size()) )
            m_aConditions[nVecPos] = std::move(aRowEntry);

        // remove default entries at the end
        size_t nSize = m_aConditions.size();
        while ( nSize > 0 && m_aConditions[ nSize-1 ].IsDefault() )
            --nSize;
        m_aConditions.resize( nSize );
    }
}

void ScOptSolverDlg::ShowConditions()
{
    for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow )
    {
        sc::ModelConstraint aRowEntry;

        tools::Long nVecPos = nScrollPos + nRow;
        if ( nVecPos < static_cast<tools::Long>(m_aConditions.size()) )
            aRowEntry = m_aConditions[nVecPos];

        mpLeftEdit[nRow]->SetRefString( aRowEntry.aLeftStr );
        mpRightEdit[nRow]->SetRefString( aRowEntry.aRightStr );
        mpOperator[nRow]->set_active( aRowEntry.nOperator - 1);
    }

    // allow to scroll one page behind the visible or stored rows
    tools::Long nVisible = nScrollPos + EDIT_ROW_COUNT;
    tools::Long nMax = std::max( nVisible, static_cast<tools::Long>(m_aConditions.size()) );
    m_xScrollBar->vadjustment_configure(nScrollPos, 0, nMax + EDIT_ROW_COUNT, 1,
                                        EDIT_ROW_COUNT - 1, EDIT_ROW_COUNT);

    EnableButtons();
}

void ScOptSolverDlg::EnableButtons()
{
    for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow )
    {
        tools::Long nVecPos = nScrollPos + nRow;
        mpDelButton[nRow]->set_sensitive(nVecPos < static_cast<tools::Long>(m_aConditions.size()));
    }
}

void ScOptSolverDlg::Close()
{
    if (m_xOptDlg)
        m_xOptDlg->response(RET_CANCEL);
    assert(!m_xOptDlg);
    DoClose( ScOptSolverDlgWrapper::GetChildWindowId() );
}

void ScOptSolverDlg::SetActive()
{
    if ( mbDlgLostFocus )
    {
        mbDlgLostFocus = false;
        if( mpEdActive )
            mpEdActive->GrabFocus();
    }
    else
    {
        m_xDialog->grab_focus();
    }
    RefInputDone();
}

void ScOptSolverDlg::SetReference( const ScRange& rRef, ScDocument& rDocP )
{
    if( !mpEdActive )
        return;

    if ( rRef.aStart != rRef.aEnd )
        RefInputStart(mpEdActive);

    // "target"/"value": single cell
    bool bSingle = ( mpEdActive == m_xEdObjectiveCell.get() || mpEdActive == m_xEdTargetValue.get() );

    OUString aStr;
    ScAddress aAdr = rRef.aStart;
    ScRange aNewRef( rRef );
    if ( bSingle )
        aNewRef.aEnd = aAdr;

    OUString aName;
    if ( rDocP.GetRangeAtBlock( aNewRef, aName ) )            // named range: show name
        aStr = aName;
    else                                                        // format cell/range reference
    {
        ScRefFlags nFmt = ( aAdr.Tab() == mnCurTab ) ? ScRefFlags::ADDR_ABS : ScRefFlags::ADDR_ABS_3D;
        if ( bSingle )
            aStr = aAdr.Format(nFmt, &rDocP, rDocP.GetAddressConvention());
        else
            aStr = rRef.Format(rDocP, nFmt | ScRefFlags::RANGE_ABS, rDocP.GetAddressConvention());
    }

    // variable cells can be several ranges, so only the selection is replaced
    if ( mpEdActive == m_xEdVariableCells.get() )
    {
        OUString aVal = mpEdActive->GetText();
        Selection aSel = mpEdActive->GetSelection();
        aSel.Normalize();
        aVal = aVal.replaceAt( aSel.Min(), aSel.Len(), aStr );
        Selection aNewSel( aSel.Min(), aSel.Min()+aStr.getLength() );
        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 == m_xEdTargetValue.get() )
        m_xRbValue->set_active(true);
}

bool ScOptSolverDlg::IsRefInputMode() const
{
    return mpEdActive != nullptr;
}

// Loads solver settings into the dialog
void ScOptSolverDlg::LoadSolverSettings()
{
    m_xEdObjectiveCell->SetRefString(m_pSolverSettings->GetParameter(sc::SP_OBJ_CELL));
    m_xEdTargetValue->SetRefString(m_pSolverSettings->GetParameter(sc::SP_OBJ_VAL));
    m_xEdVariableCells->SetRefString(m_pSolverSettings->GetParameter(sc::SP_VAR_CELLS));

    // Objective type
    sc::ObjectiveType eType = m_pSolverSettings->GetObjectiveType();
    switch (eType)
    {
        case sc::OT_MAXIMIZE : m_xRbMax->set_active(true); break;
        case sc::OT_MINIMIZE : m_xRbMin->set_active(true); break;
        case sc::OT_VALUE    : m_xRbValue->set_active(true); break;
    }

    // Model constraints
    m_aConditions = m_pSolverSettings->GetConstraints();

    // Loads solver engine name
    // If the solver engine in the current settings are not supported, use the first available
    maEngine = m_pSolverSettings->GetParameter(sc::SP_LO_ENGINE);
    if (!IsEngineAvailable(maEngine))
    {
        maEngine = maImplNames[0];
        m_pSolverSettings->SetParameter(sc::SP_LO_ENGINE, maEngine);
    }

    // Query current engine options
    maProperties = ScSolverUtil::GetDefaults(maEngine);
    m_pSolverSettings->GetEngineOptions(maProperties);
}

// Set solver settings and save them to the file
// But first, checks if the settings have changed
void ScOptSolverDlg::SaveSolverSettings()
{
    // tdf#160104 If file does not have a solver model and the Solver dialog is set to its
    // default initial values (maximize is selected; no variable cells; no target value
    // and no constraints defined) then nothing needs to be saved
    if (!m_pSolverSettings->TabHasSolverModel() && m_xRbMax->get_active()
        && m_xEdTargetValue->GetText().isEmpty() && m_xEdVariableCells->GetText().isEmpty()
        && m_aConditions.size() == 0)
        return;

    // The current tab has a model; now we need to determined if it has been modified
    bool bModified = false;

    // Check objective cell, objective value and variable cells
    if (m_pSolverSettings->GetParameter(sc::SP_OBJ_CELL) != m_xEdObjectiveCell->GetText()
        || m_pSolverSettings->GetParameter(sc::SP_OBJ_VAL) != m_xEdTargetValue->GetText()
        || m_pSolverSettings->GetParameter(sc::SP_VAR_CELLS) != m_xEdVariableCells->GetText())
        bModified = true;

    // Check selected objective type and save it if changed
    sc::ObjectiveType aType = sc::OT_MAXIMIZE;
    if (m_xRbMin->get_active())
        aType = sc::OT_MINIMIZE;
    else if (m_xRbValue->get_active())
        aType = sc::OT_VALUE;

    if (m_pSolverSettings->GetObjectiveType() != aType)
        bModified = true;

    // Check if model constraints changed
    std::vector<sc::ModelConstraint> vCurConditions = m_pSolverSettings->GetConstraints();
    if (!bModified && vCurConditions.size() != m_aConditions.size())
        bModified = true;
    else
    {
        // Here the size of both vectors is the same
        // Now it needs to check the contents of the constraints
        for (size_t i = 0; i < vCurConditions.size(); i++)
        {
            if (vCurConditions[i].aLeftStr != m_aConditions[i].aLeftStr
                || vCurConditions[i].nOperator != m_aConditions[i].nOperator
                || vCurConditions[i].aRightStr != m_aConditions[i].aRightStr)
                bModified = true;

            if (bModified)
                break;
        }
    }

    // Check if the solver engine name and its options have changed
    if (m_pSolverSettings->GetParameter(sc::SP_LO_ENGINE) != maEngine)
    {
        bModified = true;
    }
    else
    {
        // The solver engine hasn't changed, so we need to check if engine options changed
        // Query current engine options; here we start by creating a copy of maProperties
        // to ensure the order is the same
        css::uno::Sequence<css::beans::PropertyValue> vCurOptions(maProperties);
        m_pSolverSettings->GetEngineOptions(vCurOptions);

        for (sal_Int32 i = 0; i < vCurOptions.getLength(); i++)
        {
            if (vCurOptions[i].Value != maProperties[i].Value)
            {
                bModified = true;
                break;
            }
        }
    }

    // Effectively save settings to file if modifications were made
    if (bModified)
    {
        m_pSolverSettings->SetParameter(sc::SP_OBJ_CELL, m_xEdObjectiveCell->GetText());
        m_pSolverSettings->SetParameter(sc::SP_OBJ_VAL, m_xEdTargetValue->GetText());
        m_pSolverSettings->SetParameter(sc::SP_VAR_CELLS, m_xEdVariableCells->GetText());
        m_pSolverSettings->SetObjectiveType(aType);
        m_pSolverSettings->SetConstraints(m_aConditions);
        m_pSolverSettings->SetParameter(sc::SP_LO_ENGINE, maEngine);
        m_pSolverSettings->SetEngineOptions(maProperties);
        m_pSolverSettings->SaveSolverSettings();
    }
}
// Test if a LO engine implementation exists
bool ScOptSolverDlg::IsEngineAvailable(std::u16string_view sEngineName)
{
    auto nIndex = comphelper::findValue(maImplNames, sEngineName);
    return nIndex != -1;
}

// Handler:

IMPL_LINK(ScOptSolverDlg, BtnHdl, weld::Button&, rBtn, void)
{
    auto xKeepAlive = shared_from_this();
    if (&rBtn == m_xBtnSolve.get() || &rBtn == m_xBtnClose.get())
    {
        bool bSolve = ( &rBtn == m_xBtnSolve.get() );

        SetDispatcherLock( false );
        SwitchToDocument();

        bool bClose = true;
        if ( bSolve )
            bClose = CallSolver();

        if ( bClose )
        {
            // Close: write dialog settings to DocShell for subsequent calls
            ReadConditions();
            SaveSolverSettings();
            response(RET_CLOSE);
        }
        else
        {
            // no solution -> dialog is kept open
            SetDispatcherLock( true );
        }
    }
    else if (&rBtn == m_xBtnOpt.get())
    {
        //! move options dialog to UI lib?
        m_xOptDlg = std::make_shared<ScSolverOptionsDialog>(m_xDialog.get(), maImplNames, maDescriptions, maEngine, maProperties);
        weld::DialogController::runAsync(m_xOptDlg, [this](sal_Int32 nResult){
            if (nResult == RET_OK)
            {
                maEngine = m_xOptDlg->GetEngine();
                maProperties = m_xOptDlg->GetProperties();
            }
            m_xOptDlg.reset();
        });
    }
    else if (&rBtn == m_xBtnResetAll.get())
    {
        OUString sEmpty;
        m_xEdObjectiveCell->SetText(sEmpty);
        m_xEdTargetValue->SetText(sEmpty);
        m_xEdVariableCells->SetText(sEmpty);

        // Get default property values of solver implementations
        maEngine = maImplNames[0];
        maProperties = ScSolverUtil::GetDefaults( maEngine );

        // Clear all conditions (Constraints)
        m_aConditions.clear();
        ShowConditions();

        m_xRbMax->set_active(true);
        m_xEdObjectiveCell->GrabFocus();
        mpEdActive = m_xEdObjectiveCell.get();
    }
}

IMPL_LINK( ScOptSolverDlg, GetEditFocusHdl, formula::RefEdit&, rCtrl, void )
{
    formula::RefEdit* pEdit = nullptr;
    mpEdActive = nullptr;

    if( &rCtrl == m_xEdObjectiveCell.get() )
        pEdit = mpEdActive = m_xEdObjectiveCell.get();
    else if( &rCtrl == m_xEdTargetValue.get() )
        pEdit = mpEdActive = m_xEdTargetValue.get();
    else if( &rCtrl == m_xEdVariableCells.get() )
        pEdit = mpEdActive = m_xEdVariableCells.get();
    for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow )
    {
        if( &rCtrl == mpLeftEdit[nRow]  )
            pEdit = mpEdActive = mpLeftEdit[nRow];
        else if( &rCtrl == mpRightEdit[nRow]  )
            pEdit = mpEdActive = mpRightEdit[nRow];
    }

    if( pEdit )
        pEdit->SelectAll();
}

IMPL_LINK( ScOptSolverDlg, GetButtonFocusHdl, formula::RefButton&, rCtrl, void )
{
    formula::RefEdit* pEdit = nullptr;
    mpEdActive = nullptr;

    if( &rCtrl == m_xRBObjectiveCell.get() )
        pEdit = mpEdActive = m_xEdObjectiveCell.get();
    else if( &rCtrl == m_xRBTargetValue.get() )
        pEdit = mpEdActive = m_xEdTargetValue.get();
    else if( &rCtrl == m_xRBVariableCells.get() )
        pEdit = mpEdActive = m_xEdVariableCells.get();
    for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow )
    {
        if( &rCtrl == mpLeftButton[nRow] )
            pEdit = mpEdActive = mpLeftEdit[nRow];
        else if( &rCtrl == mpRightButton[nRow] )
            pEdit = mpEdActive = mpRightEdit[nRow];
    }

    if( pEdit )
        pEdit->SelectAll();
}


IMPL_LINK(ScOptSolverDlg, GetFocusHdl, weld::Widget&, rCtrl, void)
{
    if( &rCtrl == m_xRbValue.get() )                   // focus on "Value of" radio button
        mpEdActive = m_xEdTargetValue.get();          // use value edit for ref input, but don't change selection
    else
    {
        for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow )
        {
            if( &rCtrl == mpOperator[nRow] )    // focus on "operator" list box
                mpEdActive = mpRightEdit[nRow];     // use right edit for ref input, but don't change selection
        }
    }
}

IMPL_LINK_NOARG(ScOptSolverDlg, LoseEditFocusHdl, formula::RefEdit&, void)
{
    mbDlgLostFocus = !m_xDialog->has_toplevel_focus();
}

IMPL_LINK_NOARG(ScOptSolverDlg, LoseButtonFocusHdl, formula::RefButton&, void)
{
    mbDlgLostFocus = !m_xDialog->has_toplevel_focus();
}

IMPL_LINK(ScOptSolverDlg, DelBtnHdl, weld::Button&, rBtn, void)
{
    for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow )
        if (&rBtn == mpDelButton[nRow])
        {
            bool bHadFocus = rBtn.has_focus();

            ReadConditions();
            tools::Long nVecPos = nScrollPos + nRow;
            if ( nVecPos < static_cast<tools::Long>(m_aConditions.size()) )
            {
                m_aConditions.erase( m_aConditions.begin() + nVecPos );
                ShowConditions();

                if ( bHadFocus && !rBtn.get_sensitive() )
                {
                    // 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();
                }
            }
        }
}

IMPL_LINK_NOARG(ScOptSolverDlg, TargetModifyHdl, formula::RefEdit&, void)
{
    // modify handler for the target edit:
    //  select "Value of" if something is input into the edit
    if ( !m_xEdTargetValue->GetText().isEmpty() )
        m_xRbValue->set_active(true);
}

IMPL_LINK_NOARG(ScOptSolverDlg, CondModifyHdl, formula::RefEdit&, void)
{
    // modify handler for the condition edits, just to enable/disable "delete" buttons
    ReadConditions();
    EnableButtons();
}

IMPL_LINK_NOARG(ScOptSolverDlg, SelectHdl, weld::ComboBox&, void)
{
    // select handler for operator list boxes, just to enable/disable "delete" buttons
    ReadConditions();
    EnableButtons();
}

IMPL_LINK_NOARG(ScOptSolverDlg, ScrollHdl, weld::ScrolledWindow&, void)
{
    ReadConditions();
    nScrollPos = m_xScrollBar->vadjustment_get_value();
    ShowConditions();
    if( mpEdActive )
        mpEdActive->SelectAll();
}

IMPL_LINK( ScOptSolverDlg, CursorUpHdl, ScCursorRefEdit&, rEdit, void )
{
    if ( &rEdit == mpLeftEdit[0] || &rEdit == mpRightEdit[0] )
    {
        if ( nScrollPos > 0 )
        {
            ReadConditions();
            --nScrollPos;
            ShowConditions();
            if( mpEdActive )
                mpEdActive->SelectAll();
        }
    }
    else
    {
        formula::RefEdit* pFocus = nullptr;
        for ( sal_uInt16 nRow = 1; nRow < EDIT_ROW_COUNT; ++nRow )      // second row or below: move focus
        {
            if ( &rEdit == mpLeftEdit[nRow] )
                pFocus = mpLeftEdit[nRow-1];
            else if ( &rEdit == mpRightEdit[nRow] )
                pFocus = mpRightEdit[nRow-1];
        }
        if (pFocus)
        {
            mpEdActive = pFocus;
            pFocus->GrabFocus();
        }
    }
}

IMPL_LINK( ScOptSolverDlg, CursorDownHdl, ScCursorRefEdit&, rEdit, void )
{
    if ( &rEdit == mpLeftEdit[EDIT_ROW_COUNT-1] || &rEdit == mpRightEdit[EDIT_ROW_COUNT-1] )
    {
        //! limit scroll position?
        ReadConditions();
        ++nScrollPos;
        ShowConditions();
        if( mpEdActive )
            mpEdActive->SelectAll();
    }
    else
    {
        formula::RefEdit* pFocus = nullptr;
        for ( sal_uInt16 nRow = 0; nRow+1 < EDIT_ROW_COUNT; ++nRow )      // before last row: move focus
        {
            if ( &rEdit == mpLeftEdit[nRow] )
                pFocus = mpLeftEdit[nRow+1];
            else if ( &rEdit == mpRightEdit[nRow] )
                pFocus = mpRightEdit[nRow+1];
        }
        if (pFocus)
        {
            mpEdActive = pFocus;
            pFocus->GrabFocus();
        }
    }
}

// Converts the position of the operator in the dropdown menu to a ConstraintOperator type
sc::ConstraintOperator ScOptSolverDlg::OperatorIndexToConstraintOperator(sal_Int32 nIndex)
{
    switch(nIndex)
    {
        case 0  : return sc::CO_LESS_EQUAL; break;
        case 1  : return sc::CO_EQUAL; break;
        case 2  : return sc::CO_GREATER_EQUAL; break;
        case 3  : return sc::CO_INTEGER; break;
        case 4  : return sc::CO_BINARY; break;
        default : return sc::CO_LESS_EQUAL; break;
    }
}

void ScOptSolverDlg::ShowError( bool bCondition, formula::RefEdit* pFocus )
{
    OUString aMessage = bCondition ? maConditionError : maInputError;
    std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
                                              VclMessageType::Warning, VclButtonsType::Ok,
                                              aMessage));
    xBox->run();
    if (pFocus)
    {
        mpEdActive = pFocus;
        pFocus->GrabFocus();
    }
}

bool ScOptSolverDlg::ParseRef( ScRange& rRange, const OUString& rInput, bool bAllowRange )
{
    ScAddress::Details aDetails(mrDoc.GetAddressConvention(), 0, 0);
    ScRefFlags nFlags = rRange.ParseAny( rInput, mrDoc, aDetails );
    if ( nFlags & ScRefFlags::VALID )
    {
        if ( (nFlags & ScRefFlags::TAB_3D) == ScRefFlags::ZERO)
            rRange.aStart.SetTab( mnCurTab );
        if ( (nFlags & ScRefFlags::TAB2_3D) == ScRefFlags::ZERO)
            rRange.aEnd.SetTab( rRange.aStart.Tab() );
        return ( bAllowRange || rRange.aStart == rRange.aEnd );
    }
    else if ( ScRangeUtil::MakeRangeFromName( rInput, mrDoc, 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.hasElements() )
        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 == SC_UNONAME_TIMEOUT )
            bFound = ( rValue.Value >>= rTimeout );
    }
    return bFound;
}

OUString ScOptSolverDlg::GetCellStrAddress(css::table::CellAddress aUnoAddress)
{
    ScAddress aScAddr;
    ScUnoConversion::FillScAddress(aScAddr, aUnoAddress);
    ScRange aRange(aScAddr);
    return aRange.Format(mrDoc, ScRefFlags::RANGE_ABS);
}

bool ScOptSolverDlg::CallSolver()       // return true -> close dialog after calling
{
    // show progress dialog

    auto xProgress = std::make_shared<ScSolverProgressDialog>(m_xDialog.get());
    sal_Int32 nTimeout = 0;
    if ( FindTimeout( nTimeout ) )
        xProgress->SetTimeLimit( nTimeout );
    else
        xProgress->HideTimeLimit();

    weld::DialogController::runAsync(xProgress, [](sal_Int32 /*nResult*/){});

    // try to make sure the progress dialog is painted before continuing
    Application::Reschedule(true);

    // collect solver parameters

    ReadConditions();

    rtl::Reference<ScModelObj> xDocument( mrDocShell.GetModel() );

    ScRange aObjRange;
    if ( !ParseRef( aObjRange, m_xEdObjectiveCell->GetText(), false ) )
    {
        ShowError( false, m_xEdObjectiveCell.get() );
        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, m_xEdVariableCells->GetText(), mrDoc ) )
    {
        ShowError( false, m_xEdVariableCells.get() );
        return false;
    }
    uno::Sequence<table::CellAddress> aVariables;
    sal_Int32 nVarPos = 0;

    for ( size_t nRangePos=0, nRange = aVarRanges.size(); nRangePos < nRange; ++nRangePos )
    {
        ScRange aRange( aVarRanges[ nRangePos ] );
        aRange.PutInOrder();
        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 );
        auto pVariables = aVariables.getArray();

        for (SCROW nRow = aRange.aStart.Row(); nRow <= aRange.aEnd.Row(); ++nRow)
            for (SCCOL nCol = aRange.aStart.Col(); nCol <= aRange.aEnd.Col(); ++nCol)
                pVariables[nVarPos++] = table::CellAddress( nTab, nCol, nRow );
    }

    uno::Sequence<sheet::SolverConstraint> aConstraints;
    sal_Int32 nConstrPos = 0;
    for ( const auto& rConstr : m_aConditions )
    {
        if ( !rConstr.aLeftStr.isEmpty() )
        {
            sheet::SolverConstraint aConstraint;
            // Order of list box entries must match enum values.
            // The enum SolverConstraintOperator starts at zero, whereas ConstraintOperator starts at 1
            // hence we need to subtract -1 here
            aConstraint.Operator = static_cast<sheet::SolverConstraintOperator>(rConstr.nOperator - 1);

            ScRange aLeftRange;
            if ( !ParseRef( aLeftRange, rConstr.aLeftStr, true ) )
            {
                ShowError( true, nullptr );
                return false;
            }

            bool bIsRange = false;
            ScRange aRightRange;
            if ( ParseRef( aRightRange, rConstr.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, nullptr );
                    return false;
                }
            }
            else
            {
                sal_uInt32 nFormat = 0;     //! explicit language?
                double fValue = 0.0;
                if ( mrDoc.GetFormatTable()->IsNumberFormat( rConstr.aRightStr, nFormat, fValue ) )
                    aConstraint.Right <<= fValue;
                else if ( aConstraint.Operator != sheet::SolverConstraintOperator_INTEGER &&
                          aConstraint.Operator != sheet::SolverConstraintOperator_BINARY )
                {
                    ShowError( true, nullptr );
                    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 );
            auto pConstraints = aConstraints.getArray();

            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() ) );

                    pConstraints[nConstrPos++] = aConstraint;
                }
        }
    }

    bool bMaximize = m_xRbMax->get_active();
    if ( m_xRbValue->get_active() )
    {
        // handle "value of" with an additional constraint (and then minimize)

        sheet::SolverConstraint aConstraint;
        aConstraint.Left     = aObjective;
        aConstraint.Operator = sheet::SolverConstraintOperator_EQUAL;

        OUString aValStr = m_xEdTargetValue->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 ( mrDoc.GetFormatTable()->IsNumberFormat( aValStr, nFormat, fValue ) )
                aConstraint.Right <<= fValue;
            else
            {
                ShowError( false, m_xEdTargetValue.get() );
                return false;
            }
        }

        aConstraints.realloc( nConstrPos + 1 );
        aConstraints.getArray()[nConstrPos++] = std::move(aConstraint);
    }

    // copy old document values

    sal_Int32 nVarCount = aVariables.getLength();
    uno::Sequence<double> aOldValues( nVarCount );
    std::transform(std::cbegin(aVariables), std::cend(aVariables), aOldValues.getArray(),
        [this](const table::CellAddress& rVariable) -> double {
            ScAddress aCellPos;
            ScUnoConversion::FillScAddress( aCellPos, rVariable );
            return mrDoc.GetValue( aCellPos );
        });

    // create and initialize solver

    uno::Reference<sheet::XSolver> xSolver = ScSolverUtil::GetSolver( maEngine );
    OSL_ENSURE( 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() )
    {
        for (const beans::PropertyValue& rValue : maProperties)
        {
            try
            {
                xOptProp->setPropertyValue( rValue.Name, rValue.Value );
            }
            catch ( uno::Exception & )
            {
                OSL_FAIL("Exception in solver option property");
            }
        }
    }

    // tdf#162760 The solver engine may crash unexpectedly, so we need a try...catch here
    bool bSuccess(false);
    try
    {
        xSolver->solve();
        bSuccess = xSolver->getSuccess();
    }
    catch (const uno::RuntimeException&)
    {
        std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
                                                  VclMessageType::Error, VclButtonsType::Ok,
                                                  ScResId(STR_SOLVER_ENGINE_ERROR)));
        xBox->run();
    }

    xProgress->response(RET_CLOSE);

    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 )
        {
            mrDocShell.LockPaint();
            ScDocFunc &rFunc = mrDocShell.GetDocFunc();
            for (nVarPos=0; nVarPos<nVarCount; ++nVarPos)
            {
                ScAddress aCellPos;
                ScUnoConversion::FillScAddress(aCellPos, aVariables[nVarPos]);
                rFunc.SetValueCell(aCellPos, aSolution[nVarPos], false);
            }
            mrDocShell.UnlockPaint();
        }
        //! else error?

        // take formatted result from document (result value from component is ignored)
        OUString aResultStr = mrDoc.GetString(
            static_cast<SCCOL>(aObjective.Column), static_cast<SCROW>(aObjective.Row),
            static_cast<SCTAB>(aObjective.Sheet));

        ScSolverSuccessDialog aDialog(m_xDialog.get(), aResultStr);
        if (aDialog.run() == RET_OK)
        {
            // keep results and close dialog
            bRestore = false;
            bClose = true;
        }
    }
    else
    {
        OUString aError;
        uno::Reference<sheet::XSolverDescription> xDesc( xSolver, uno::UNO_QUERY );
        if ( xDesc.is() )
            aError = xDesc->getStatusDescription();         // error description from component
        ScSolverNoSolutionDialog aDialog(m_xDialog.get(), aError);
        aDialog.run();
    }

    if ( bRestore )         // restore old values
    {
        mrDocShell.LockPaint();
        ScDocFunc &rFunc = mrDocShell.GetDocFunc();
        for (nVarPos=0; nVarPos<nVarCount; ++nVarPos)
        {
            ScAddress aCellPos;
            ScUnoConversion::FillScAddress( aCellPos, aVariables[nVarPos] );
            rFunc.SetValueCell(aCellPos, aOldValues[nVarPos], false);
        }
        mrDocShell.UnlockPaint();
    }

    // Generate sensitivity report if user wants it
    uno::Reference<css::beans::XPropertySetInfo> xInfo = xOptProp->getPropertySetInfo();
    bool bUserWantsReport = false;
    if (xInfo->hasPropertyByName("GenSensitivityReport"))
        xOptProp->getPropertyValue("GenSensitivityReport") >>= bUserWantsReport;

    if (bSuccess && bUserWantsReport)
    {
        // Retrieve the sensitivity analysis report
        css::sheet::SensitivityReport aSensitivity;
        bool bHasReportObj = xOptProp->getPropertyValue("SensitivityReport") >>= aSensitivity;

        if (bHasReportObj && aSensitivity.HasReport)
        {
            // Define the Tab name where the sensitivity analysis will be written to
            OUString sNewTabName;
            SCTAB nNewTab;
            mrDoc.GetName(mnCurTab, sNewTabName);
            sNewTabName += "_" + ScResId(STR_SENSITIVITY);
            // Check if the new Tab name exists
            if (mrDoc.GetTable(sNewTabName, nNewTab))
            {
                // Add numbers to the end of the Tab name to make it unique
                SCTAB i = 1;
                OUString aName;
                do
                {
                    i++;
                    aName = sNewTabName + "_" + OUString::number(static_cast<sal_Int32>(i));
                }
                while(mrDoc.GetTable(aName, nNewTab));
                sNewTabName = aName;
            }

            // Insert new sheet to the document and start writing the report
            ScDocFunc &rFunc = mrDocShell.GetDocFunc();
            rFunc.InsertTable(mnCurTab + 1, sNewTabName, falsefalse);
            SCTAB nReportTab;
            if (!mrDoc.GetTable(sNewTabName, nReportTab))
            {
                SAL_WARN("sc""Could not get the just inserted table!");
                return false;
            }

            // Used to input data in the new sheet
            ScAddress aOutputAddress(0, 0, nReportTab);
            ScAddress::Details mAddressDetails(mrDoc, aOutputAddress);
            AddressWalkerWriter aOutput(aOutputAddress, mrDocShell, mrDoc,
                                        formula::FormulaGrammar::mergeToGrammar(formula::FormulaGrammar::GRAM_ENGLISH, mAddressDetails.eConv));
            aOutput.writeBoldString(ScResId(STR_SENSITIVITY_TITLE));
            aOutput.newLine();
            aOutput.writeString(ScResId(STR_SOLVER_ENGINE) + " " + maEngine);
            aOutput.newLine();
            aOutput.newLine();

            // Objective cell section
            aOutput.writeBoldString(ScResId(STR_SENSITIVITY_OBJCELL));
            aOutput.newLine();
            aOutput.formatAsColumnHeader(2);
            aOutput.writeString(ScResId(STR_SENSITIVITY_CELL));
            aOutput.nextColumn();
            aOutput.writeString(ScResId(STR_SENSITIVITY_FINALVALUE));
            aOutput.newLine();
            aOutput.formatTableBottom(2);
            aOutput.writeString(GetCellStrAddress(xSolver->getObjective()));
            aOutput.nextColumn();
            aOutput.writeValue(xSolver->getResultValue());
            aOutput.newLine();
            aOutput.newLine();

            // Variable cell section
            aOutput.writeBoldString(ScResId(STR_SENSITIVITY_VARCELLS));
            aOutput.newLine();
            aOutput.formatAsColumnHeader(6);
            aOutput.writeString(ScResId(STR_SENSITIVITY_CELL));
            aOutput.nextColumn();
            aOutput.writeString(ScResId(STR_SENSITIVITY_FINALVALUE));
            aOutput.nextColumn();
            aOutput.writeString(ScResId(STR_SENSITIVITY_REDUCED));
            aOutput.nextColumn();
            aOutput.writeString(ScResId(STR_SENSITIVITY_OBJCOEFF));
            aOutput.nextColumn();
            aOutput.writeString(ScResId(STR_SENSITIVITY_DECREASE));
            aOutput.nextColumn();
            aOutput.writeString(ScResId(STR_SENSITIVITY_INCREASE));
            aOutput.newLine();

            uno::Sequence<double> aSolution = xSolver->getSolution();
            uno::Sequence<double> aObjCoefficients = aSensitivity.ObjCoefficients;
            uno::Sequence<double> aObjReducedCosts = aSensitivity.ObjReducedCosts;
            uno::Sequence<double> aObjAllowableDecreases = aSensitivity.ObjAllowableDecreases;
            uno::Sequence<double> aObjAllowableIncreases = aSensitivity.ObjAllowableIncreases;
            sal_Int32 nRows = aVariables.getLength();
            for (sal_Int32 i = 0; i < nRows; i++)
            {
                if (i == nRows - 1)
                    aOutput.formatTableBottom(6);
                aOutput.writeString(GetCellStrAddress(aVariables[i]));
                aOutput.nextColumn();
                aOutput.writeValue(aSolution[i]);
                aOutput.nextColumn();
                aOutput.writeValue(aObjReducedCosts[i]);
                aOutput.nextColumn();
                aOutput.writeValue(aObjCoefficients[i]);
                aOutput.nextColumn();
                aOutput.writeValue(aObjAllowableDecreases[i]);
                aOutput.nextColumn();
                aOutput.writeValue(aObjAllowableIncreases[i]);
                aOutput.newLine();
            }
            aOutput.newLine();

            // Constraints section
            aOutput.writeBoldString(ScResId(STR_SENSITIVITY_CONSTRAINTS));
            aOutput.newLine();
            aOutput.formatAsColumnHeader(6);
            aOutput.writeString(ScResId(STR_SENSITIVITY_CELL));
            aOutput.nextColumn();
            aOutput.writeString(ScResId(STR_SENSITIVITY_FINALVALUE));
            aOutput.nextColumn();
            aOutput.writeString(ScResId(STR_SENSITIVITY_SHADOWPRICE));
            aOutput.nextColumn();
            aOutput.writeString(ScResId(STR_SENSITIVITY_RHS));
            aOutput.nextColumn();
            aOutput.writeString(ScResId(STR_SENSITIVITY_DECREASE));
            aOutput.nextColumn();
            aOutput.writeString(ScResId(STR_SENSITIVITY_INCREASE));
            aOutput.newLine();

            uno::Sequence<double> aConstrValues = aSensitivity.ConstrValues;
            uno::Sequence<double> aConstrRHS = aSensitivity.ConstrRHS;
            uno::Sequence<double> aConstrShadowPrices = aSensitivity.ConstrShadowPrices;
            uno::Sequence<double> aConstrAllowableDecreases = aSensitivity.ConstrAllowableDecreases;
            uno::Sequence<double> aConstrAllowableIncreases = aSensitivity.ConstrAllowableIncreases;
            nRows = aConstraints.getLength();
            for (sal_Int32 i = 0; i < nRows; i++)
            {
                if (i == nRows - 1)
                    aOutput.formatTableBottom(6);
                aOutput.writeString(GetCellStrAddress(aConstraints[i].Left));
                aOutput.nextColumn();
                aOutput.writeValue(aConstrValues[i]);
                aOutput.nextColumn();
                aOutput.writeValue(aConstrShadowPrices[i]);
                aOutput.nextColumn();
                aOutput.writeValue(aConstrRHS[i]);
                aOutput.nextColumn();
                aOutput.writeValue(aConstrAllowableDecreases[i]);
                aOutput.nextColumn();
                aOutput.writeValue(aConstrAllowableIncreases[i]);
                aOutput.newLine();
            }

            // Disable grid lines in the sensitivity report
            if (ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell())
            {
                ScViewData& rData = pViewSh->GetViewData();
                rData.SetTabNo(nReportTab);
                rData.SetShowGrid(false);
                rData.SetTabNo(mnCurTab);
            }
        }
    }

    return bClose;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Messung V0.5
C=89 H=85 G=86

¤ Dauer der Verarbeitung: 0.19 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge