/* -*- 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 <condformatmgr.hxx>
#include <condformateasydlg.hxx>
#include <condformathelper.hxx>
#include <condformatdlg.hxx>
#include <document.hxx>
#include <conditio.hxx>
#include <sc.hrc>
#include <o3tl/safeint.hxx>
#include <sfx2/dispatch.hxx>
#include <sfx2/sfxsids.hrc>
#include <sfx2/viewsh.hxx>
#include <svl/eitem.hxx>
#include <svl/intitem.hxx>
#include <svl/stritem.hxx>
#include <unotools/viewoptions.hxx>
#include <o3tl/string_view.hxx>
 
namespace
{
OUString generateEntryId(sal_uInt32 key, sal_uInt32 index)
{
    return OUString::number(key) + "_" + OUString::number(index);
}
 
sal_Int32 getKeyFromId(std::u16string_view id) { return o3tl::toInt32(o3tl::getToken(id, 0, '_')); }
 
sal_Int32 getEntryIndexFromId(std::u16string_view id) { return o3tl::toInt32(o3tl::getToken(id, 1, '_')); }
}
 
ScCondFormatManagerWindow::ScCondFormatManagerWindow(weld::TreeView& rTreeView,
    ScDocument& rDoc, ScConditionalFormatList* pFormatList)
    : mrTreeView(rTreeView)
    , mrDoc(rDoc)
    , mpFormatList(pFormatList)
{
    mrTreeView.set_size_request(mrTreeView.get_approximate_digit_width() * 70,
                                mrTreeView.get_height_rows(20));
    setColSizes();
 
    Init();
    mrTreeView.set_selection_mode(SelectionMode::Multiple);
    mrTreeView.make_sorted();
}
 
void ScCondFormatManagerWindow::Init()
{
    mrTreeView.freeze();
 
    if (mpFormatList)
    {
        int nRow = 0;
        OUString sRangeStr;
        for(const auto& rItem : *mpFormatList)
        {
            const ScRangeList& aRange = rItem->GetRange();
            aRange.Format(sRangeStr, ScRefFlags::VALID, mrDoc, mrDoc.GetAddressConvention());
            for (size_t i = 0; i < rItem->size(); i++)
            {
                mrTreeView.append(generateEntryId(rItem->GetKey(), i), sRangeStr);
                mrTreeView.set_text(nRow++,
                                    ScCondFormatHelper::GetExpression(rItem->GetEntry(i),
                                                                      aRange.GetTopLeftCorner()),
                                    1);
            }
        }
    }
 
    mrTreeView.thaw();
 
    if (mpFormatList && !mpFormatList->empty())
        mrTreeView.select(0);
}
 
void ScCondFormatManagerWindow::DeleteSelection()
{
    auto aSelectedRows = mrTreeView.get_selected_rows();
    std::sort(aSelectedRows.begin(), aSelectedRows.end());
    for (auto it = aSelectedRows.rbegin(); it != aSelectedRows.rend(); ++it)
    {
        sal_Int32 nIndex = mrTreeView.get_id(*it).toInt32();
        mpFormatList->erase(nIndex);
        mrTreeView.remove(*it);
    }
}
 
ScConditionalFormat* ScCondFormatManagerWindow::GetSelection()
{
    int nEntry = mrTreeView.get_selected_index();
    if (nEntry == -1)
        return nullptr;
 
    sal_Int32 nKey = getKeyFromId(mrTreeView.get_id(nEntry));
    return mpFormatList->GetFormat(nKey);
}
 
const ScFormatEntry* ScCondFormatManagerWindow::GetSelectedEntry() const
{
    sal_Int32 nKey = GetSelectedFormatKey();
    sal_Int32 nEntryIndex = GetSelectedEntryIndex();
 
    if (nKey == -1 || nEntryIndex == -1)
        return nullptr;
    return mpFormatList->GetFormat(nKey)->GetEntry(nEntryIndex);
}
 
sal_Int32 ScCondFormatManagerWindow::GetSelectedFormatKey() const
{
    OUString id = mrTreeView.get_selected_id();
    if (id.isEmpty())
        return -1;
    return getKeyFromId(id);
}
 
sal_Int32 ScCondFormatManagerWindow::GetSelectedEntryIndex() const
{
    OUString id = mrTreeView.get_selected_id();
    if (id.isEmpty())
        return -1;
    return getEntryIndexFromId(id);
}
 
void ScCondFormatManagerWindow::setColSizes()
{
    std::vector<int> aWidths
    {
        o3tl::narrowing<int>(mrTreeView.get_size_request().Width() / 2)
    };
    mrTreeView.set_column_fixed_widths(aWidths);
}
 
ScCondFormatManagerDlg::ScCondFormatManagerDlg(weld::Window* pParent, ScDocument& rDoc, const ScConditionalFormatList* pFormatList)
    : GenericDialogController(pParent, u"modules/scalc/ui/condformatmanager.ui"_ustr, u"CondFormatManager"_ustr)
    , m_bModified(false)
    , m_xFormatList( pFormatList ? new ScConditionalFormatList(*pFormatList) : nullptr)
    , m_xConditionalType(m_xBuilder->weld_combo_box("type"))
    , m_xConditionalCellValue(m_xBuilder->weld_combo_box("typeis"))
    , m_xConditionalFormula(new formula::RefEdit(m_xBuilder->weld_entry("formula")))
    , m_xConditionalDate(m_xBuilder->weld_combo_box("datetype"))
    , m_xBtnAdd(m_xBuilder->weld_button(u"add"_ustr))
    , m_xBtnRemove(m_xBuilder->weld_button(u"remove"_ustr))
    , m_xBtnEdit(m_xBuilder->weld_button(u"edit"_ustr))
    , m_xTreeView(m_xBuilder->weld_tree_view(u"CONTAINER"_ustr))
    , m_xCtrlManager(new ScCondFormatManagerWindow(*m_xTreeView, rDoc, m_xFormatList.get()))
{
    m_xBtnRemove->connect_clicked(LINK(this, ScCondFormatManagerDlg, RemoveBtnHdl));
    m_xBtnEdit->connect_clicked(LINK(this, ScCondFormatManagerDlg, EditBtnClickHdl));
    m_xBtnAdd->connect_clicked(LINK(this, ScCondFormatManagerDlg, AddBtnHdl));
    m_xTreeView->connect_row_activated(LINK(this, ScCondFormatManagerDlg, EditBtnHdl));
    m_xTreeView->connect_changed(LINK(this, ScCondFormatManagerDlg, EntryFocus));
    m_xConditionalType->connect_changed(LINK(this, ScCondFormatManagerDlg, ComboHdl));
 
    SvtViewOptions aDlgOpt(EViewType::Dialog, u"CondFormatDialog"_ustr);
    if (aDlgOpt.Exists())
        m_xDialog->set_window_state(aDlgOpt.GetWindowState());
 
    UpdateButtonSensitivity();
    EntryFocus(*m_xTreeView);
}
 
ScCondFormatManagerDlg::~ScCondFormatManagerDlg()
{
   // tdf#101285 - Remember position of dialog
    SvtViewOptions aDlgOpt(EViewType::Dialog, u"CondFormatDialog"_ustr);
    aDlgOpt.SetWindowState(m_xDialog->get_window_state(vcl::WindowDataMask::Pos));
}
 
std::unique_ptr<ScConditionalFormatList> ScCondFormatManagerDlg::GetConditionalFormatList()
{
    return std::move(m_xFormatList);
}
 
void ScCondFormatManagerDlg::UpdateButtonSensitivity()
{
    bool bNewSensitivity = !m_xFormatList->empty();
    m_xBtnRemove->set_sensitive(bNewSensitivity);
    m_xBtnEdit->set_sensitive(bNewSensitivity);
}
 
// Get the current conditional format selected.
//
ScConditionalFormat* ScCondFormatManagerDlg::GetCondFormatSelected()
{
    return m_xCtrlManager->GetSelection();
}
 
void ScCondFormatManagerDlg::ShowEasyConditionalDialog(bool isEdit)
{
    SfxViewShell* pViewShell = SfxViewShell::Current();
    if (!pViewShell)
        return;
 
    auto id = m_xConditionalType->get_active();
    SfxBoolItem IsManaged(FN_PARAM_2, true);
    SfxInt32Item FormatKey(FN_PARAM_3, isEdit ? m_xCtrlManager->GetSelectedFormatKey() : -1);
    SfxInt32Item EntryIndex(FN_PARAM_4, isEdit ? m_xCtrlManager->GetSelectedEntryIndex() : -1);
    switch (id)
    {
        case 0: // Cell value
        {
            SfxInt16Item FormatRule(FN_PARAM_1,
                                    m_xConditionalCellValue->get_active_id().toUInt32());
            pViewShell->GetDispatcher()->ExecuteList(
                SID_EASY_CONDITIONAL_FORMAT_DIALOG, SfxCallMode::ASYNCHRON,
                { &FormatRule, &IsManaged, &FormatKey, &EntryIndex });
        }
        break;
        case 1: // Formula
        {
            SfxInt16Item FormatRule(FN_PARAM_1, static_cast<sal_Int16>(ScConditionMode::Formula));
            SfxStringItem Formula(FN_PARAM_5, m_xConditionalFormula->GetText());
            pViewShell->GetDispatcher()->ExecuteList(
                SID_EASY_CONDITIONAL_FORMAT_DIALOG, SfxCallMode::ASYNCHRON,
                { &FormatRule, &IsManaged, &FormatKey, &EntryIndex, &Formula });
        }
        break;
        case 2: // Date
        {
            SfxInt16Item FormatRule(FN_PARAM_1, m_xConditionalDate->get_active_id().toUInt32());
            pViewShell->GetDispatcher()->ExecuteList(
                SID_EASY_CONDITIONAL_FORMAT_DIALOG, SfxCallMode::ASYNCHRON,
                { &FormatRule, &IsManaged, &FormatKey, &EntryIndex });
        }
        break;
        default:
            break;
    }
}
 
IMPL_LINK_NOARG(ScCondFormatManagerDlg, RemoveBtnHdl, weld::Button&, void)
{
    m_xCtrlManager->DeleteSelection();
    m_bModified = true;
    UpdateButtonSensitivity();
}
 
IMPL_LINK_NOARG(ScCondFormatManagerDlg, EditBtnClickHdl, weld::Button&, void)
{
    EditBtnHdl(*m_xTreeView);
}
 
IMPL_LINK_NOARG(ScCondFormatManagerDlg, EditBtnHdl, weld::TreeView&, bool)
{
    ScConditionalFormat* pFormat = m_xCtrlManager->GetSelection();
 
    if (!pFormat)
        return true;
 
    m_bModified = true;
    m_xDialog->response( DLG_RET_EDIT );
 
    return true;
}
 
IMPL_LINK_NOARG(ScCondFormatManagerDlg, AddBtnHdl, weld::Button&, void)
{
    m_bModified = true;
    m_xDialog->response( DLG_RET_ADD );
}
 
IMPL_LINK_NOARG(ScCondFormatManagerDlg, ComboHdl, weld::ComboBox&, void)
{
    auto id = m_xConditionalType->get_active();
    switch (id)
    {
        case 0:
        {
            m_xConditionalCellValue->set_visible(true);
            m_xConditionalFormula->GetWidget()->set_visible(false);
            m_xConditionalDate->set_visible(false);
        }
        break;
        case 1:
        {
            m_xConditionalCellValue->set_visible(false);
            m_xConditionalFormula->GetWidget()->set_visible(true);
            m_xConditionalDate->set_visible(false);
        }
        break;
        case 2:
        {
            m_xConditionalCellValue->set_visible(false);
            m_xConditionalFormula->GetWidget()->set_visible(false);
            m_xConditionalDate->set_visible(true);
        }
        break;
        default:
            break;
    }
}
 
IMPL_LINK_NOARG(ScCondFormatManagerDlg, EntryFocus, weld::TreeView&, void)
{
    const ScFormatEntry* entry = m_xCtrlManager->GetSelectedEntry();
    if (!entry)
        return;
    auto type = entry->GetType();
 
    if (type == ScFormatEntry::Type::Condition)
    {
        const ScCondFormatEntry* conditionEntry = dynamic_cast<const ScCondFormatEntry*>(entry);
        auto conditionType = conditionEntry->GetOperation();
 
        if (conditionType == ScConditionMode::Direct) // Formula conditions
        {
            m_xConditionalType->set_active(1);
            ComboHdl(*m_xConditionalType);
            m_xConditionalFormula->SetText(
                conditionEntry->GetExpression(conditionEntry->GetSrcPos(), 0));
        }
        else
        {
            m_xConditionalType->set_active(0);
            ComboHdl(*m_xConditionalType);
            m_xConditionalCellValue->set_active(static_cast<int>(conditionType));
        }
    }
    else if (type == ScFormatEntry::Type::Date)
    {
        const ScCondDateFormatEntry* dateEntry = dynamic_cast<const ScCondDateFormatEntry*>(entry);
        auto dateType = dateEntry->GetDateType();
        m_xConditionalType->set_active(2);
        ComboHdl(*m_xConditionalType);
        m_xConditionalDate->set_active(dateType);
    }
}
 
void ScCondFormatManagerDlg::SetModified()
{
    m_bModified = true;
    UpdateButtonSensitivity();
}
 
bool ScCondFormatManagerDlg::CondFormatsChanged() const
{
    return m_bModified;
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V530 The return value of function 'ExecuteList' is required to be utilized.

V530 The return value of function 'ExecuteList' is required to be utilized.

V530 The return value of function 'ExecuteList' is required to be utilized.

V522 There might be dereferencing of a potential null pointer 'conditionEntry'.

V522 There might be dereferencing of a potential null pointer 'dateEntry'.