/* -*- 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 <sfx2/sidebar/ControlFactory.hxx>
 
#include <com/sun/star/chart/ChartAxisLabelPosition.hpp>
#include <com/sun/star/chart/ErrorBarStyle.hpp>
#include <com/sun/star/util/XModifyBroadcaster.hpp>
 
#include "ChartErrorBarPanel.hxx"
#include <ChartController.hxx>
#include <sfx2/bindings.hxx>
#include <sfx2/dispatch.hxx>
#include <vcl/fixed.hxx>
#include <vcl/lstbox.hxx>
#include <vcl/field.hxx>
#include <vcl/toolbox.hxx>
#include <svl/intitem.hxx>
#include <svl/stritem.hxx>
 
using namespace css;
using namespace css::uno;
 
namespace chart { namespace sidebar {
 
namespace {
 
enum class ErrorBarDirection
{
    POSITIVE,
    NEGATIVE
};
 
css::uno::Reference<css::beans::XPropertySet> getErrorBarPropSet(
        const css::uno::Reference<css::frame::XModel>& xModel, const OUString& rCID)
{
    return ObjectIdentifier::getObjectPropertySet(rCID, xModel);
}
 
bool showPositiveError(const css::uno::Reference<css::frame::XModel>& xModel,
        const OUString& rCID)
{
    css::uno::Reference<css::beans::XPropertySet> xPropSet =
        getErrorBarPropSet(xModel, rCID);
 
    if (!xPropSet.is())
        return false;
 
    css::uno::Any aAny = xPropSet->getPropertyValue("ShowPositiveError");
 
    if (!aAny.hasValue())
        return false;
 
    bool bShow = false;
    aAny >>= bShow;
    return bShow;
}
 
bool showNegativeError(const css::uno::Reference<css::frame::XModel>& xModel,
        const OUString& rCID)
{
    css::uno::Reference<css::beans::XPropertySet> xPropSet =
        getErrorBarPropSet(xModel, rCID);
 
    if (!xPropSet.is())
        return false;
 
    css::uno::Any aAny = xPropSet->getPropertyValue("ShowNegativeError");
 
    if (!aAny.hasValue())
        return false;
 
    bool bShow = false;
    aAny >>= bShow;
    return bShow;
}
 
void setShowPositiveError(const css::uno::Reference<css::frame::XModel>& xModel,
        const OUString& rCID, bool bShow)
{
    css::uno::Reference<css::beans::XPropertySet> xPropSet =
        getErrorBarPropSet(xModel, rCID);
 
    if (!xPropSet.is())
        return;
 
    xPropSet->setPropertyValue("ShowPositiveError", css::uno::Any(bShow));
}
 
void setShowNegativeError(const css::uno::Reference<css::frame::XModel>& xModel,
        const OUString& rCID, bool bShow)
{
    css::uno::Reference<css::beans::XPropertySet> xPropSet =
        getErrorBarPropSet(xModel, rCID);
 
    if (!xPropSet.is())
        return;
 
    xPropSet->setPropertyValue("ShowNegativeError", css::uno::Any(bShow));
}
 
struct ErrorBarTypeMap
{
    sal_Int32 nPos;
    sal_Int32 nApi;
};
 
ErrorBarTypeMap aErrorBarType[] = {
    { 0, css::chart::ErrorBarStyle::ABSOLUTE },
    { 1, css::chart::ErrorBarStyle::RELATIVE },
    { 2, css::chart::ErrorBarStyle::FROM_DATA },
    { 3, css::chart::ErrorBarStyle::STANDARD_DEVIATION },
    { 4, css::chart::ErrorBarStyle::STANDARD_ERROR },
    { 5, css::chart::ErrorBarStyle::VARIANCE},
    { 6, css::chart::ErrorBarStyle::ERROR_MARGIN },
};
 
sal_Int32 getTypePos(const css::uno::Reference<css::frame::XModel>& xModel,
        const OUString& rCID)
{
    css::uno::Reference<css::beans::XPropertySet> xPropSet =
        getErrorBarPropSet(xModel, rCID);
 
    if (!xPropSet.is())
        return 0;
 
    css::uno::Any aAny = xPropSet->getPropertyValue("ErrorBarStyle");
 
    if (!aAny.hasValue())
        return 0;
 
    sal_Int32 nApi = 0;
    aAny >>= nApi;
 
    for (ErrorBarTypeMap & i : aErrorBarType)
    {
        if (i.nApi == nApi)
            return i.nPos;
    }
 
    return 0;
}
 
void setTypePos(const css::uno::Reference<css::frame::XModel>& xModel,
        const OUString& rCID, sal_Int32 nPos)
{
    css::uno::Reference<css::beans::XPropertySet> xPropSet =
        getErrorBarPropSet(xModel, rCID);
 
    if (!xPropSet.is())
        return;
 
    sal_Int32 nApi = 0;
    for (ErrorBarTypeMap & i : aErrorBarType)
    {
        if (i.nPos == nPos)
            nApi = i.nApi;
    }
 
    xPropSet->setPropertyValue("ErrorBarStyle", css::uno::Any(nApi));
}
 
double getValue(const css::uno::Reference<css::frame::XModel>& xModel,
        const OUString& rCID, ErrorBarDirection eDir)
{
    css::uno::Reference<css::beans::XPropertySet> xPropSet =
        getErrorBarPropSet(xModel, rCID);
 
    if (!xPropSet.is())
        return 0;
 
    OUString aName = "PositiveError";
    if (eDir == ErrorBarDirection::NEGATIVE)
        aName = "NegativeError";
 
    css::uno::Any aAny = xPropSet->getPropertyValue(aName);
 
    if (!aAny.hasValue())
        return 0;
 
    double nVal = 0;
    aAny >>= nVal;
 
    return nVal;
}
 
void setValue(const css::uno::Reference<css::frame::XModel>& xModel,
        const OUString& rCID, double nVal, ErrorBarDirection eDir)
{
    css::uno::Reference<css::beans::XPropertySet> xPropSet =
        getErrorBarPropSet(xModel, rCID);
 
    if (!xPropSet.is())
        return;
 
    OUString aName = "PositiveError";
    if (eDir == ErrorBarDirection::NEGATIVE)
        aName = "NegativeError";
 
    xPropSet->setPropertyValue(aName, css::uno::Any(nVal));
}
 
OUString getCID(const css::uno::Reference<css::frame::XModel>& xModel)
{
    css::uno::Reference<css::frame::XController> xController(xModel->getCurrentController());
    css::uno::Reference<css::view::XSelectionSupplier> xSelectionSupplier(xController, css::uno::UNO_QUERY);
    if (!xSelectionSupplier.is())
        return OUString();
 
    uno::Any aAny = xSelectionSupplier->getSelection();
    assert(aAny.hasValue());
    OUString aCID;
    aAny >>= aCID;
#ifdef DBG_UTIL
    ObjectType eType = ObjectIdentifier::getObjectType(aCID);
    assert(eType == OBJECTTYPE_DATA_ERRORS_X ||
            eType == OBJECTTYPE_DATA_ERRORS_Y ||
            eType == OBJECTTYPE_DATA_ERRORS_Z);
#endif
 
    return aCID;
}
 
}
 
ChartErrorBarPanel::ChartErrorBarPanel(
    vcl::Window* pParent,
    const css::uno::Reference<css::frame::XFrame>& rxFrame,
    ChartController* pController)
  : PanelLayout(pParent, "ChartErrorBarPanel", "modules/schart/ui/sidebarerrorbar.ui", rxFrame),
    mxModel(pController->getModel()),
    mxListener(new ChartSidebarModifyListener(this)),
    mbModelValid(true)
{
 
    get(mpRBPosAndNeg, "radiobutton_positive_negative");
    get(mpRBPos, "radiobutton_positive");
    get(mpRBNeg, "radiobutton_negative");
 
    get(mpLBType, "comboboxtext_type");
 
    get(mpMFPos, "spinbutton_pos");
    get(mpMFNeg, "spinbutton_neg");
 
    Initialize();
}
 
ChartErrorBarPanel::~ChartErrorBarPanel()
{
    disposeOnce();
}
 
void ChartErrorBarPanel::dispose()
{
    css::uno::Reference<css::util::XModifyBroadcaster> xBroadcaster(mxModel, css::uno::UNO_QUERY_THROW);
    xBroadcaster->removeModifyListener(mxListener);
 
    mpRBPosAndNeg.clear();
    mpRBPos.clear();
    mpRBNeg.clear();
 
    mpLBType.clear();
 
    mpMFPos.clear();
    mpMFNeg.clear();
 
    PanelLayout::dispose();
}
 
void ChartErrorBarPanel::Initialize()
{
    css::uno::Reference<css::util::XModifyBroadcaster> xBroadcaster(mxModel, css::uno::UNO_QUERY_THROW);
    xBroadcaster->addModifyListener(mxListener);
    mpRBNeg->Check(false);
    mpRBPos->Check(false);
    mpRBPosAndNeg->Check(false);
 
    updateData();
 
    Link<RadioButton&,void> aLink = LINK(this, ChartErrorBarPanel, RadioBtnHdl);
    mpRBPosAndNeg->SetToggleHdl(aLink);
    mpRBPos->SetToggleHdl(aLink);
    mpRBNeg->SetToggleHdl(aLink);
 
    mpLBType->SetSelectHdl(LINK(this, ChartErrorBarPanel, ListBoxHdl));
 
    Link<Edit&,void> aLink2 = LINK(this, ChartErrorBarPanel, NumericFieldHdl);
    mpMFPos->SetModifyHdl(aLink2);
    mpMFNeg->SetModifyHdl(aLink2);
}
 
void ChartErrorBarPanel::updateData()
{
    if (!mbModelValid)
        return;
 
    OUString aCID = getCID(mxModel);
    bool bPos = showPositiveError(mxModel, aCID);
    bool bNeg = showNegativeError(mxModel, aCID);
 
    SolarMutexGuard aGuard;
 
    if (bPos && bNeg)
        mpRBPosAndNeg->Check();
    else if (bPos)
        mpRBPos->Check();
    else if (bNeg)
        mpRBNeg->Check();
 
    sal_Int32 nTypePos = getTypePos(mxModel, aCID);
    mpLBType->SelectEntryPos(nTypePos);
 
    if (nTypePos <= 1)
    {
        if (bPos)
            mpMFPos->Enable();
        else
            mpMFPos->Disable();
 
        if (bNeg)
            mpMFNeg->Enable();
        else
            mpMFNeg->Disable();
 
        double nValPos = getValue(mxModel, aCID, ErrorBarDirection::POSITIVE);
        double nValNeg = getValue(mxModel, aCID, ErrorBarDirection::NEGATIVE);
 
        mpMFPos->SetValue(nValPos);
        mpMFNeg->SetValue(nValNeg);
    }
    else
    {
        mpMFPos->Disable();
        mpMFNeg->Disable();
    }
}
 
VclPtr<vcl::Window> ChartErrorBarPanel::Create (
    vcl::Window* pParent,
    const css::uno::Reference<css::frame::XFrame>& rxFrame,
    ChartController* pController)
{
    if (pParent == nullptr)
        throw lang::IllegalArgumentException("no parent Window given to ChartErrorBarPanel::Create", nullptr, 0);
    if ( ! rxFrame.is())
        throw lang::IllegalArgumentException("no XFrame given to ChartErrorBarPanel::Create", nullptr, 1);
 
    return  VclPtr<ChartErrorBarPanel>::Create(
                        pParent, rxFrame, pController);
}
 
void ChartErrorBarPanel::DataChanged(
    const DataChangedEvent& )
{
    updateData();
}
 
void ChartErrorBarPanel::HandleContextChange(
    const vcl::EnumContext& )
{
    updateData();
}
 
void ChartErrorBarPanel::NotifyItemUpdate(
    sal_uInt16 /*nSID*/,
    SfxItemState /*eState*/,
    const SfxPoolItem* /*pState*/,
    const bool )
{
}
 
void ChartErrorBarPanel::modelInvalid()
{
    mbModelValid = false;
}
 
void ChartErrorBarPanel::updateModel(
        css::uno::Reference<css::frame::XModel> xModel)
{
    if (mbModelValid)
    {
        css::uno::Reference<css::util::XModifyBroadcaster> xBroadcaster(mxModel, css::uno::UNO_QUERY_THROW);
        xBroadcaster->removeModifyListener(mxListener);
    }
 
    mxModel = xModel;
    mbModelValid = true;
 
    css::uno::Reference<css::util::XModifyBroadcaster> xBroadcasterNew(mxModel, css::uno::UNO_QUERY_THROW);
    xBroadcasterNew->addModifyListener(mxListener);
}
 
IMPL_LINK_NOARG(ChartErrorBarPanel, RadioBtnHdl, RadioButton&, void)
{
    OUString aCID = getCID(mxModel);
    bool bPos = mpRBPosAndNeg->IsChecked() || mpRBPos->IsChecked();
    bool bNeg = mpRBPosAndNeg->IsChecked() || mpRBNeg->IsChecked();
 
    setShowPositiveError(mxModel, aCID, bPos);
    setShowNegativeError(mxModel, aCID, bNeg);
}
 
IMPL_LINK_NOARG(ChartErrorBarPanel, ListBoxHdl, ListBox&, void)
{
    OUString aCID = getCID(mxModel);
    sal_Int32 nPos = mpLBType->GetSelectedEntryPos();
 
    setTypePos(mxModel, aCID, nPos);
}
 
IMPL_LINK(ChartErrorBarPanel, NumericFieldHdl, Edit&, rMetricField, void)
{
    OUString aCID = getCID(mxModel);
    double nVal = static_cast<NumericField&>(rMetricField).GetValue();
    if (&rMetricField == mpMFPos.get())
        setValue(mxModel, aCID, nVal, ErrorBarDirection::POSITIVE);
    else if (&rMetricField == mpMFNeg.get())
        setValue(mxModel, aCID, nVal, ErrorBarDirection::NEGATIVE);
}
 
}} // end of namespace ::chart::sidebar
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V1001 The 'aAny' variable is assigned but is not used by the end of the function.

V1001 The 'aAny' variable is assigned but is not used by the end of the function.

V1001 The 'aAny' variable is assigned but is not used by the end of the function.

V1001 The 'aAny' variable is assigned but is not used by the end of the function.