/* -*- 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/.
 *
 */
 
#include <ThemeColorChanger.hxx>
 
#include <sal/config.h>
 
#include <docmodel/theme/Theme.hxx>
#include <editeng/colritem.hxx>
#include <editeng/brushitem.hxx>
#include <editeng/boxitem.hxx>
#include <editeng/borderline.hxx>
#include <svx/svditer.hxx>
 
#include <undodraw.hxx>
#include <stlpool.hxx>
#include <stlsheet.hxx>
#include <scitems.hxx>
#include <scresid.hxx>
#include <globstr.hrc>
#include <document.hxx>
#include <address.hxx>
#include <dociter.hxx>
#include <docfunc.hxx>
#include <tabvwsh.hxx>
#include <undostyl.hxx>
#include <undoblk.hxx>
#include <SparklineGroup.hxx>
#include <SparklineList.hxx>
 
#include <undo/UndoThemeChange.hxx>
 
namespace sc
{
ThemeColorChanger::ThemeColorChanger(ScDocShell& rDocShell)
    : m_rDocShell(rDocShell)
{
}
 
ThemeColorChanger::~ThemeColorChanger() = default;
 
namespace
{
bool changeBorderLine(editeng::SvxBorderLine* pBorderLine, model::ColorSet const& rColorSet)
{
    if (!pBorderLine)
        return false;
 
    model::ComplexColor const& rComplexColor = pBorderLine->getComplexColor();
    if (rComplexColor.isValidThemeType())
    {
        Color aColor = rColorSet.resolveColor(rComplexColor);
        pBorderLine->SetColor(aColor);
        return true;
    }
    return false;
}
 
bool changeCellItems(SfxItemSet& rItemSet, model::ColorSet const& rColorSet)
{
    const SfxPoolItem* pItem = nullptr;
    bool bChanged = false;
    if (rItemSet.HasItem(ATTR_FONT_COLOR, &pItem))
    {
        auto const* pColorItem = static_cast<const SvxColorItem*>(pItem);
        model::ComplexColor const& rComplexColor = pColorItem->getComplexColor();
        if (rComplexColor.isValidThemeType())
        {
            Color aColor = rColorSet.resolveColor(rComplexColor);
 
            SvxColorItem aColorItem(*pColorItem);
            aColorItem.setColor(aColor);
            rItemSet.Put(aColorItem);
            bChanged = true;
        }
    }
    if (rItemSet.HasItem(ATTR_BACKGROUND, &pItem))
    {
        auto const* pBrushItem = static_cast<const SvxBrushItem*>(pItem);
        model::ComplexColor const& rComplexColor = pBrushItem->getComplexColor();
        if (rComplexColor.isValidThemeType())
        {
            Color aColor = rColorSet.resolveColor(rComplexColor);
 
            SvxBrushItem aNewBrushItem(*pBrushItem);
            aNewBrushItem.SetColor(aColor);
            rItemSet.Put(aNewBrushItem);
            bChanged = true;
        }
    }
    if (rItemSet.HasItem(ATTR_BORDER, &pItem))
    {
        auto const* pBoxItem = static_cast<const SvxBoxItem*>(pItem);
        SvxBoxItem rNewItem(*pBoxItem);
        bool bLineChanged = false;
 
        bLineChanged = changeBorderLine(rNewItem.GetBottom(), rColorSet) || bChanged;
        bLineChanged = changeBorderLine(rNewItem.GetTop(), rColorSet) || bChanged;
        bLineChanged = changeBorderLine(rNewItem.GetLeft(), rColorSet) || bChanged;
        bLineChanged = changeBorderLine(rNewItem.GetRight(), rColorSet) || bChanged;
 
        if (bLineChanged)
        {
            rItemSet.Put(rNewItem);
            bChanged = true;
        }
    }
    return bChanged;
}
 
bool changeStyles(ScDocShell& rDocShell, model::ColorSet const& rColorSet)
{
    ScDocument& rDocument = rDocShell.GetDocument();
    ScStyleSheetPool* pPool = rDocument.GetStyleSheetPool();
    ScStyleSheet* pStyle = nullptr;
    bool bChanged = false;
 
    // Paragraph style color change
    pStyle = static_cast<ScStyleSheet*>(pPool->First(SfxStyleFamily::Para));
    while (pStyle)
    {
        ScStyleSaveData aOldData;
        aOldData.InitFromStyle(pStyle);
 
        auto rItemSet = pStyle->GetItemSet();
        if (changeCellItems(rItemSet, rColorSet))
        {
            if (rDocument.IsUndoEnabled())
            {
                ScStyleSaveData aNewData;
                aNewData.InitFromStyle(pStyle);
                rDocShell.GetUndoManager()->AddUndoAction(std::make_unique<ScUndoModifyStyle>(
                    &rDocShell, SfxStyleFamily::Para, aOldData, aNewData));
            }
            static_cast<SfxStyleSheet*>(pStyle)->Broadcast(SfxHint(SfxHintId::DataChanged));
            bChanged = true;
        }
 
        pStyle = static_cast<ScStyleSheet*>(pPool->Next());
    }
 
    return bChanged;
}
 
bool changeSheets(ScDocShell& rDocShell, ScTabViewShell* pViewShell, ScDrawLayer* pModel,
                  model::ColorSet const& rColorSet)
{
    ScDocument& rDocument = rDocShell.GetDocument();
    bool bChanged = false;
 
    for (SCTAB nTab = 0; nTab < rDocument.GetTableCount(); nTab++)
    {
        // Change Cell / Text attributes
        {
            ScDocAttrIterator aAttributeIterator(rDocument, nTab, 0, 0, rDocument.MaxCol(),
                                                 rDocument.MaxRow());
            SCCOL nCol = 0;
            SCROW nRow1 = 0;
            SCROW nRow2 = 0;
 
            while (const ScPatternAttr* pPattern = aAttributeIterator.GetNext(nCol, nRow1, nRow2))
            {
                if (!pPattern || !pPattern->IsVisible())
                    continue;
 
                ScPatternAttr aNewPattern(*pPattern);
                auto& rItemSet = aNewPattern.GetItemSet();
                bool bItemChanged = changeCellItems(rItemSet, rColorSet);
                bChanged = bChanged || bItemChanged;
 
                if (bItemChanged && rDocument.IsUndoEnabled())
                {
                    ScRange aRange(nCol, nRow1, nTab, nCol, nRow2, nTab);
                    ScMarkData aMark(rDocument.GetSheetLimits());
                    aMark.SetMarkArea(aRange);
 
                    ScDocumentUniquePtr pUndoDoc(new ScDocument(SCDOCMODE_UNDO));
                    pUndoDoc->InitUndo(rDocument, nTab, nTab);
                    pUndoDoc->AddUndoTab(nTab, nTab);
 
                    aMark.MarkToMulti();
 
                    rDocument.CopyToDocument(aRange, InsertDeleteFlags::ATTRIB, true, *pUndoDoc,
                                             &aMark);
 
                    auto pUndo = std::make_unique<ScUndoSelectionAttr>(
                        &rDocShell, aMark, aRange.aStart.Col(), aRange.aStart.Row(),
                        aRange.aStart.Tab(), aRange.aEnd.Col(), aRange.aEnd.Row(),
                        aRange.aEnd.Tab(), std::move(pUndoDoc), true, &aNewPattern);
 
                    ScEditDataArray* pDataArray = pUndo->GetDataArray();
 
                    rDocument.ApplySelectionPattern(aNewPattern, aMark, pDataArray);
 
                    rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndo));
                }
            }
        }
 
        // Change all SdrObjects
        {
            pModel->BeginCalcUndo(true);
            SdrView* pView = nullptr;
            if (pViewShell)
                pView = pViewShell->GetScDrawView();
 
            SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
            SdrObjListIter aIter(pPage, SdrIterMode::DeepNoGroups);
            for (SdrObject* pObject = aIter.Next(); pObject; pObject = aIter.Next())
            {
                svx::theme::updateSdrObject(rColorSet, pObject, pView, rDocShell.GetUndoManager());
            }
 
            std::unique_ptr<SdrUndoGroup> pUndo = pModel->GetCalcUndo();
 
            if (pUndo)
            {
                bChanged = true;
                auto pUndoDraw = std::make_unique<ScUndoDraw>(std::move(pUndo), &rDocShell);
                rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndoDraw));
            }
        }
    }
 
    return bChanged;
}
 
model::ComplexColor modifyComplexColor(model::ComplexColor const& rComplexColor,
                                       model::ColorSet const& rColorSet)
{
    model::ComplexColor aComplexColor(rComplexColor);
 
    if (aComplexColor.isValidThemeType())
    {
        Color aColor = rColorSet.resolveColor(aComplexColor);
        aComplexColor.setFinalColor(aColor);
    }
    return aComplexColor;
}
 
void changeSparklines(ScDocShell& rDocShell, model::ColorSet const& rColorSet)
{
    ScDocument& rDocument = rDocShell.GetDocument();
    auto& rDocFunc = rDocShell.GetDocFunc();
    for (SCTAB nTab = 0; nTab < rDocument.GetTableCount(); ++nTab)
    {
        auto* pSparklineList = rDocument.GetSparklineList(nTab);
        if (pSparklineList && !pSparklineList->getSparklineGroups().empty())
        {
            auto const aSparklineGroups = pSparklineList->getSparklineGroups();
            for (auto const& rSparklineGroup : aSparklineGroups)
            {
                auto aAttributes = rSparklineGroup->getAttributes();
 
                aAttributes.setColorAxis(modifyComplexColor(aAttributes.getColorAxis(), rColorSet));
                aAttributes.setColorSeries(
                    modifyComplexColor(aAttributes.getColorSeries(), rColorSet));
                aAttributes.setColorNegative(
                    modifyComplexColor(aAttributes.getColorNegative(), rColorSet));
                aAttributes.setColorMarkers(
                    modifyComplexColor(aAttributes.getColorMarkers(), rColorSet));
                aAttributes.setColorHigh(modifyComplexColor(aAttributes.getColorHigh(), rColorSet));
                aAttributes.setColorLow(modifyComplexColor(aAttributes.getColorLow(), rColorSet));
                aAttributes.setColorFirst(
                    modifyComplexColor(aAttributes.getColorFirst(), rColorSet));
                aAttributes.setColorLast(modifyComplexColor(aAttributes.getColorLast(), rColorSet));
                rDocFunc.ChangeSparklineGroupAttributes(rSparklineGroup, aAttributes);
            }
        }
    }
}
 
std::shared_ptr<model::Theme> getTheme(ScDocShell& rDocShell)
{
    ScDrawLayer* pModel = rDocShell.GetDocument().GetDrawLayer();
 
    auto pTheme = pModel->getTheme();
    if (!pTheme)
    {
        pTheme = std::make_shared<model::Theme>("Office");
        pModel->setTheme(pTheme);
    }
    return pTheme;
}
 
void changeThemeColorInTheDocModel(ScDocShell& rDocShell,
                                   std::shared_ptr<model::ColorSet> const& pColorSet)
{
    auto pTheme = getTheme(rDocShell);
    std::shared_ptr<model::ColorSet> pNewColorSet = pColorSet;
    std::shared_ptr<model::ColorSet> pOldColorSet = pTheme->getColorSet();
    pTheme->setColorSet(pNewColorSet);
 
    ScDocument& rDocument = rDocShell.GetDocument();
    if (rDocument.IsUndoEnabled())
    {
        auto pUndoThemeChange
            = std::make_unique<sc::UndoThemeChange>(rDocShell, pOldColorSet, pNewColorSet);
        rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndoThemeChange));
    }
}
 
} // end anonymous ns
 
void ThemeColorChanger::doApply(std::shared_ptr<model::ColorSet> const& pColorSet)
{
    // Can't change to an empty color set
    if (!pColorSet)
        return;
 
    m_rDocShell.MakeDrawLayer();
 
    ScDocShellModificator aModificator(m_rDocShell);
    ScDocument& rDocument = m_rDocShell.GetDocument();
    auto pUndoManager = m_rDocShell.GetUndoManager();
 
    const bool bUndo(rDocument.IsUndoEnabled());
 
    ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
 
    ViewShellId nViewShellId(-1);
    if (pViewShell)
        nViewShellId = pViewShell->GetViewShellId();
 
    if (bUndo)
    {
        OUString aUndo = ScResId(STR_UNDO_THEME_COLOR_CHANGE);
        pUndoManager->EnterListAction(aUndo, aUndo, 0, nViewShellId);
    }
 
    bool bChanged = false;
    bChanged = changeStyles(m_rDocShell, *pColorSet) || bChanged;
    bChanged
        = changeSheets(m_rDocShell, pViewShell, rDocument.GetDrawLayer(), *pColorSet) || bChanged;
    changeSparklines(m_rDocShell, *pColorSet);
 
    changeThemeColorInTheDocModel(m_rDocShell, pColorSet);
 
    if (bUndo)
    {
        pUndoManager->LeaveListAction();
    }
 
    m_rDocShell.SetDrawModified();
    aModificator.SetDocumentModified();
}
 
} // end sc namespace
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V560 A part of conditional expression is always false: !pPattern.