/* -*- 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 <sal/config.h>

#include <sfx2/objsh.hxx>
#include <svx/strings.hrc>
#include <svx/svxids.hrc>

#include <strings.hrc>
#include <bitmaps.hlst>

#include <editeng/boxitem.hxx>
#include <editeng/lineitem.hxx>
#include <border.hxx>
#include <svx/dlgutil.hxx>
#include <dialmgr.hxx>
#include <sfx2/htmlmode.hxx>
#include <vcl/fieldvalues.hxx>
#include <vcl/settings.hxx>
#include <vcl/svapp.hxx>
#include <svx/dialmgr.hxx>
#include <svx/flagsdef.hxx>
#include <svl/grabbagitem.hxx>
#include <svl/intitem.hxx>
#include <svl/ilstitem.hxx>
#include <svl/int64item.hxx>
#include <sal/macros.h>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <comphelper/lok.hxx>
#include <svtools/unitconv.hxx>

using namespace ::editeng;
using ::com::sun::star::uno::Reference;
using ::com::sun::star::lang::XServiceInfo;
using ::com::sun::star::uno::UNO_QUERY;


/*
 * [Description:]
 * TabPage for setting the border attributes.
 * Needs
 *      a SvxShadowItem: shadow
 *      a SvxBoxItem:    lines left, right, top, bottom,
 *      a SvxBoxInfo:    lines vertical, horizontal, distance, flags
 *
 * Lines can have three conditions:
 *      1. Show     ( -> valid values )
 *      2. Hide     ( -> NULL-Pointer )
 *      3. DontCare ( -> special Valid-Flags in the InfoItem )
 */

// static ----------------------------------------------------------------

const WhichRangesContainer SvxBorderTabPage::pRanges(
    svl::Items<
        SID_ATTR_BORDER_INNER,      SID_ATTR_BORDER_SHADOW,
        SID_ATTR_ALIGN_MARGIN,      SID_ATTR_ALIGN_MARGIN,
        SID_ATTR_BORDER_CONNECT,    SID_ATTR_BORDER_CONNECT,
        SID_SW_COLLAPSING_BORDERS,  SID_SW_COLLAPSING_BORDERS,
        SID_ATTR_BORDER_DIAG_TLBR,  SID_ATTR_BORDER_DIAG_BLTR>);

namespace
{
constexpr int twipsToPt100(sal_Int64 nTwips)
{
    return o3tl::convert(nTwips * 100, o3tl::Length::twip, o3tl::Length::pt);
}
constexpr int s_LineWidths[] = { twipsToPt100(SvxBorderLineWidth::Hairline),
                                 twipsToPt100(SvxBorderLineWidth::VeryThin),
                                 twipsToPt100(SvxBorderLineWidth::Thin),
                                 twipsToPt100(SvxBorderLineWidth::Medium),
                                 twipsToPt100(SvxBorderLineWidth::Thick),
                                 twipsToPt100(SvxBorderLineWidth::ExtraThick),
                                 -1 };
}

static void lcl_SetDecimalDigitsTo1(weld::MetricSpinButton& rField)
{
    auto nMin = rField.denormalize(rField.get_min(FieldUnit::TWIP));
    rField.set_digits(1);
    rField.set_min(rField.normalize(nMin), FieldUnit::TWIP);
}

// returns in pt
static sal_Int64 lcl_GetMinLineWidth(SvxBorderLineStyle aStyle)
{
    switch (aStyle)
    {
    case SvxBorderLineStyle::NONE:
        return 0;

    case SvxBorderLineStyle::SOLID:
    case SvxBorderLineStyle::DOTTED:
    case SvxBorderLineStyle::DASHED:
    case SvxBorderLineStyle::FINE_DASHED:
    case SvxBorderLineStyle::DASH_DOT:
    case SvxBorderLineStyle::DASH_DOT_DOT:
        return 15;

        // Double lines
    case SvxBorderLineStyle::DOUBLE: return 15;
    case SvxBorderLineStyle::DOUBLE_THIN: return 15;
    case SvxBorderLineStyle::THINTHICK_SMALLGAP: return 20;
    case SvxBorderLineStyle::THINTHICK_MEDIUMGAP: return 15;
    case SvxBorderLineStyle::THINTHICK_LARGEGAP: return 15;
    case SvxBorderLineStyle::THICKTHIN_SMALLGAP: return 20;
    case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP: return 15;
    case SvxBorderLineStyle::THICKTHIN_LARGEGAP: return 15;

    case SvxBorderLineStyle::EMBOSSED: return 15;
    case SvxBorderLineStyle::ENGRAVED: return 15;

    case SvxBorderLineStyle::OUTSET: return 10;
    case SvxBorderLineStyle::INSET: return 10;

    default:
        return 15;
    }
}

// number of preset images to show
const sal_uInt16 BORDER_PRESET_COUNT = 5;

// number of shadow images to show
const sal_uInt16 BORDER_SHADOW_COUNT = 5;

ShadowControlsWrapper::ShadowControlsWrapper(ValueSet& rVsPos, weld::MetricSpinButton& rMfSize, ColorListBox& rLbColor)
    : mrVsPos(rVsPos)
    , mrMfSize(rMfSize)
    , mrLbColor(rLbColor)
{
}

SvxShadowItem ShadowControlsWrapper::GetControlValue(const SvxShadowItem& rItem) const
{
    SvxShadowItem aItem(rItem);
    if (!mrVsPos.IsNoSelection())
    {
        switch (mrVsPos.GetSelectedItemId())
        {
            case 1:
                aItem.SetLocation(SvxShadowLocation::NONE);
                break;
            case 2:
                aItem.SetLocation(SvxShadowLocation::BottomRight);
                break;
            case 3:
                aItem.SetLocation(SvxShadowLocation::TopRight);
                break;
            case 4:
                aItem.SetLocation(SvxShadowLocation::BottomLeft);
                break;
            case 5:
                aItem.SetLocation(SvxShadowLocation::TopLeft);
                break;
            default:
                aItem.SetLocation(SvxShadowLocation::NONE);
                break;
        }
    }
    // Default value was saved; so don't change the aItem's width if the control
    // has not changed its value, to avoid round-trip errors (like twip->cm->twip)
    // E.g., initial 100 twip will become 0.18 cm, which will return as 102 twip
    if (mrMfSize.get_value_changed_from_saved())
        aItem.SetWidth(mrMfSize.denormalize(mrMfSize.get_value(FieldUnit::TWIP)));
    if (!mrLbColor.IsNoSelection())
        aItem.SetColor(mrLbColor.GetSelectEntryColor());
    return aItem;
}

void ShadowControlsWrapper::SetControlValue(const SvxShadowItem& rItem)
{
    switch (rItem.GetLocation())
    {
        case SvxShadowLocation::NONE:
            mrVsPos.SelectItem(1);
            break;
        case SvxShadowLocation::BottomRight:
            mrVsPos.SelectItem(2);
            break;
        case SvxShadowLocation::TopRight:
            mrVsPos.SelectItem(3);
            break;
        case SvxShadowLocation::BottomLeft:
            mrVsPos.SelectItem(4);
            break;
        case SvxShadowLocation::TopLeft:
            mrVsPos.SelectItem(5);
            break;
        default:
            mrVsPos.SetNoSelection();
            break;
    }
    mrVsPos.SaveValue();
    mrMfSize.set_value(mrMfSize.normalize(rItem.GetWidth()), FieldUnit::TWIP);
    mrMfSize.save_value();
    mrLbColor.SelectEntry(rItem.GetColor());
    mrLbColor.SaveValue();
}

bool ShadowControlsWrapper::get_value_changed_from_saved() const
{
    return mrVsPos.IsValueChangedFromSaved() ||
           mrMfSize.get_value_changed_from_saved() ||
           mrLbColor.IsValueChangedFromSaved();
}

void ShadowControlsWrapper::SetControlDontKnow()
{
    mrVsPos.SetNoSelection();
    mrMfSize.set_text(u""_ustr);
    mrLbColor.SetNoSelection();
}

MarginControlsWrapper::MarginControlsWrapper(weld::MetricSpinButton& rMfLeft, weld::MetricSpinButton& rMfRight,
                                             weld::MetricSpinButton& rMfTop, weld::MetricSpinButton& rMfBottom)
    : mrLeftWrp(rMfLeft)
    , mrRightWrp(rMfRight)
    , mrTopWrp(rMfTop)
    , mrBottomWrp(rMfBottom)
{
}

SvxMarginItem MarginControlsWrapper::GetControlValue(const SvxMarginItem &rItem) const
{
    SvxMarginItem aItem(rItem);
    if (mrLeftWrp.get_sensitive())
        aItem.SetLeftMargin(mrLeftWrp.denormalize(mrLeftWrp.get_value(FieldUnit::TWIP)));
    if (mrRightWrp.get_sensitive())
        aItem.SetRightMargin(mrRightWrp.denormalize(mrRightWrp.get_value(FieldUnit::TWIP)));
    if (mrTopWrp.get_sensitive())
        aItem.SetTopMargin(mrTopWrp.denormalize(mrTopWrp.get_value(FieldUnit::TWIP)));
    if (mrBottomWrp.get_sensitive())
        aItem.SetBottomMargin(mrBottomWrp.denormalize(mrBottomWrp.get_value(FieldUnit::TWIP)));
    return aItem;
}

bool MarginControlsWrapper::get_value_changed_from_saved() const
{
    return mrLeftWrp.get_value_changed_from_saved() ||
           mrRightWrp.get_value_changed_from_saved() ||
           mrTopWrp.get_value_changed_from_saved() ||
           mrBottomWrp.get_value_changed_from_saved();
}

void MarginControlsWrapper::SetControlValue(const SvxMarginItem& rItem)
{
    mrLeftWrp.set_value(mrLeftWrp.normalize(rItem.GetLeftMargin()), FieldUnit::TWIP);
    mrRightWrp.set_value(mrRightWrp.normalize(rItem.GetRightMargin()), FieldUnit::TWIP);
    mrTopWrp.set_value(mrTopWrp.normalize(rItem.GetTopMargin()), FieldUnit::TWIP);
    mrBottomWrp.set_value(mrBottomWrp.normalize(rItem.GetBottomMargin()), FieldUnit::TWIP);
    mrLeftWrp.save_value();
    mrRightWrp.save_value();
    mrTopWrp.save_value();
    mrBottomWrp.save_value();
}

void MarginControlsWrapper::SetControlDontKnow()
{
    const OUString sEmpty;
    mrLeftWrp.set_text(sEmpty);
    mrRightWrp.set_text(sEmpty);
    mrTopWrp.set_text(sEmpty);
    mrBottomWrp.set_text(sEmpty);
}

SvxBorderTabPage::SvxBorderTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rCoreAttrs)
    : SfxTabPage(pPage, pController, u"cui/ui/borderpage.ui"_ustr, u"BorderPage"_ustr, &rCoreAttrs)
    , nMinValue(0)
    , nSWMode(SwBorderModes::NONE)
    , mnBoxSlot(SID_ATTR_BORDER_OUTER)
    , mnShadowSlot(SID_ATTR_BORDER_SHADOW)
    , mbHorEnabled(false)
    , mbVerEnabled(false)
    , mbTLBREnabled(false)
    , mbBLTREnabled(false)
    , mbUseMarginItem(false)
    , mbLeftModified(false)
    , mbRightModified(false)
    , mbTopModified(false)
    , mbBottomModified(false)
    , mbSync(true)
    , mbRemoveAdjacentCellBorders(false)
    , bIsCalcDoc(false)
    , m_xWndPresets(new ValueSet(nullptr))
    , m_xWndPresetsWin(new weld::CustomWeld(*m_xBuilder, u"presets"_ustr, *m_xWndPresets))
    , m_xUserDefFT(m_xBuilder->weld_label(u"userdefft"_ustr))
    , m_xFrameSelWin(new weld::CustomWeld(*m_xBuilder, u"framesel"_ustr, m_aFrameSel))
    , m_xLbLineStyle(new SvtLineListBox(m_xBuilder->weld_menu_button(u"linestylelb"_ustr)))
    , m_xLbLineColor(new ColorListBox(m_xBuilder->weld_menu_button(u"linecolorlb"_ustr),
                [this]{ return GetDialogController()->getDialog(); }))
    , m_xLineWidthLB(m_xBuilder->weld_combo_box(u"linewidthlb"_ustr))
    , m_xLineWidthMF(m_xBuilder->weld_metric_spin_button(u"linewidthmf"_ustr, FieldUnit::POINT))
    , m_xSpacingFrame(m_xBuilder->weld_container(u"spacing"_ustr))
    , m_xLeftFT(m_xBuilder->weld_label(u"leftft"_ustr))
    , m_xLeftMF(m_xBuilder->weld_metric_spin_button(u"leftmf"_ustr, FieldUnit::MM))
    , m_xRightFT(m_xBuilder->weld_label(u"rightft"_ustr))
    , m_xRightMF(m_xBuilder->weld_metric_spin_button(u"rightmf"_ustr, FieldUnit::MM))
    , m_xTopFT(m_xBuilder->weld_label(u"topft"_ustr))
    , m_xTopMF(m_xBuilder->weld_metric_spin_button(u"topmf"_ustr, FieldUnit::MM))
    , m_xBottomFT(m_xBuilder->weld_label(u"bottomft"_ustr))
    , m_xBottomMF(m_xBuilder->weld_metric_spin_button(u"bottommf"_ustr, FieldUnit::MM))
    , m_xSynchronizeCB(m_xBuilder->weld_check_button(u"sync"_ustr))
    , m_xShadowFrame(m_xBuilder->weld_container(u"shadow"_ustr))
    , m_xWndShadows(new ValueSet(nullptr))
    , m_xWndShadowsWin(new weld::CustomWeld(*m_xBuilder, u"shadows"_ustr, *m_xWndShadows))
    , m_xFtShadowSize(m_xBuilder->weld_label(u"distanceft"_ustr))
    , m_xEdShadowSize(m_xBuilder->weld_metric_spin_button(u"distancemf"_ustr, FieldUnit::MM))
    , m_xFtShadowColor(m_xBuilder->weld_label(u"shadowcolorft"_ustr))
    , m_xLbShadowColor(new ColorListBox(m_xBuilder->weld_menu_button(u"shadowcolorlb"_ustr),
                [this]{ return GetDialogController()->getDialog(); }))
    , m_xPropertiesFrame(m_xBuilder->weld_container(u"properties"_ustr))
    , m_xMergeWithNextCB(m_xBuilder->weld_check_button(u"mergewithnext"_ustr))
    , m_xMergeAdjacentBordersCB(m_xBuilder->weld_check_button(u"mergeadjacent"_ustr))
    , m_xRemoveAdjacentCellBordersCB(m_xBuilder->weld_check_button(u"rmadjcellborders"_ustr))
    , m_xRemoveAdjacentCellBordersFT(m_xBuilder->weld_label(u"rmadjcellbordersft"_ustr))
{
    static std::vector<OUString> aBorderImageIds;

    if (aBorderImageIds.empty())
    {
        if (comphelper::LibreOfficeKit::isActive())
        {
            aBorderImageIds.insert(aBorderImageIds.end(), {
                RID_SVXBMP_CELL_NONE_32,
                RID_SVXBMP_CELL_ALL_32,
                RID_SVXBMP_CELL_LR_32,
                RID_SVXBMP_CELL_TB_32,
                RID_SVXBMP_CELL_L_32,
                RID_SVXBMP_CELL_DIAG_32
            });
        }
        else
        {
            aBorderImageIds.insert(aBorderImageIds.end(), {
                RID_SVXBMP_CELL_NONE,
                RID_SVXBMP_CELL_ALL,
                RID_SVXBMP_CELL_LR,
                RID_SVXBMP_CELL_TB,
                RID_SVXBMP_CELL_L,
                RID_SVXBMP_CELL_DIAG
            });
        }
        aBorderImageIds.insert(aBorderImageIds.end(), {
            RID_SVXBMP_HOR_NONE,
            RID_SVXBMP_HOR_OUTER,
            RID_SVXBMP_HOR_HOR,
            RID_SVXBMP_HOR_ALL,
            RID_SVXBMP_HOR_OUTER2,
            RID_SVXBMP_VER_NONE,
            RID_SVXBMP_VER_OUTER,
            RID_SVXBMP_VER_VER,
            RID_SVXBMP_VER_ALL,
            RID_SVXBMP_VER_OUTER2,
            RID_SVXBMP_TABLE_NONE,
            RID_SVXBMP_TABLE_OUTER,
            RID_SVXBMP_TABLE_OUTERH,
            RID_SVXBMP_TABLE_ALL,
            RID_SVXBMP_TABLE_OUTER2
        });
    }

    for (auto const & rImageId : aBorderImageIds)
        m_aBorderImgVec.emplace_back(StockImage::Yes, rImageId);

    static std::vector<OUString> aShadowImageIds;
    if (aShadowImageIds.empty())
    {
        if (comphelper::LibreOfficeKit::isActive())
        {
            aShadowImageIds.insert(aShadowImageIds.end(), {
                RID_SVXBMP_SHADOWNONE_32,
                RID_SVXBMP_SHADOW_BOT_RIGHT_32,
                RID_SVXBMP_SHADOW_TOP_RIGHT_32,
                RID_SVXBMP_SHADOW_BOT_LEFT_32,
                RID_SVXBMP_SHADOW_TOP_LEFT_32
            });
        }
        else
        {
            aShadowImageIds.insert(aShadowImageIds.end(), {
                RID_SVXBMP_SHADOWNONE,
                RID_SVXBMP_SHADOW_BOT_RIGHT,
                RID_SVXBMP_SHADOW_TOP_RIGHT,
                RID_SVXBMP_SHADOW_BOT_LEFT,
                RID_SVXBMP_SHADOW_TOP_LEFT
            });
        }
    }

    for (auto const & rImageId : aShadowImageIds)
        m_aShadowImgVec.emplace_back(StockImage::Yes, rImageId);

    assert(m_aShadowImgVec.size() == BORDER_SHADOW_COUNT);

    // this page needs ExchangeSupport
    SetExchangeSupport();

    /*  Use SvxMarginItem instead of margins from SvxBoxItem, if present.
        ->  Remember this state in mbUseMarginItem, because other special handling
            is needed across various functions... */
    mbUseMarginItem = rCoreAttrs.GetItemState(GetWhich(SID_ATTR_ALIGN_MARGIN)) != SfxItemState::UNKNOWN;

    if (const SfxIntegerListItem* p = rCoreAttrs.GetItemIfSet(SID_ATTR_BORDER_STYLES))
    {
        std::vector<sal_Int32> aUsedStyles = p->GetList();
        for (int aUsedStyle : aUsedStyles)
            maUsedBorderStyles.insert(static_cast<SvxBorderLineStyle>(aUsedStyle));
    }

    if (const SfxInt64Item* p = rCoreAttrs.GetItemIfSet(SID_ATTR_BORDER_DEFAULT_WIDTH))
    {
        // The caller specifies default line width.  Honor it.
        SetLineWidth(p->GetValue());
    }

    // set metric
    FieldUnit eFUnit = GetModuleFieldUnit( rCoreAttrs );

    if( mbUseMarginItem )
    {
        // copied from SvxAlignmentTabPage
        switch ( eFUnit )
        {
            //  #103396# the default value (1pt) can't be accurately represented in
            //  inches or pica with two decimals, so point is used instead.
            case FieldUnit::PICA:
            case FieldUnit::INCH:
            case FieldUnit::FOOT:
            case FieldUnit::MILE:
                eFUnit = FieldUnit::POINT;
                break;

            case FieldUnit::CM:
            case FieldUnit::M:
            case FieldUnit::KM:
                eFUnit = FieldUnit::MM;
                break;
            default: ;//prevent warning
        }
    }
    else
    {
        switch ( eFUnit )
        {
            case FieldUnit::M:
            case FieldUnit::KM:
                eFUnit = FieldUnit::MM;
                break;
            default: ; //prevent warning
        }
    }

    SetFieldUnit(*m_xEdShadowSize, eFUnit);

    sal_uInt16 nWhich = GetWhich( SID_ATTR_BORDER_INNER, false );
    bool bIsDontCare = true;

    if ( rCoreAttrs.GetItemState( nWhich ) >= SfxItemState::DEFAULT )
    {
        // paragraph or table
        const SvxBoxInfoItem* pBoxInfo =
            static_cast<const SvxBoxInfoItem*>(&( rCoreAttrs.Get( nWhich ) ));

        mbHorEnabled = pBoxInfo->IsHorEnabled();
        mbVerEnabled = pBoxInfo->IsVerEnabled();
        mbTLBREnabled = rCoreAttrs.GetItemState(GetWhich(SID_ATTR_BORDER_DIAG_TLBR)) != SfxItemState::UNKNOWN;
        mbBLTREnabled = rCoreAttrs.GetItemState(GetWhich(SID_ATTR_BORDER_DIAG_BLTR)) != SfxItemState::UNKNOWN;

        if(pBoxInfo->IsDist())
        {
            SetFieldUnit(*m_xLeftMF, eFUnit);
            SetFieldUnit(*m_xRightMF, eFUnit);
            SetFieldUnit(*m_xTopMF, eFUnit);
            SetFieldUnit(*m_xBottomMF, eFUnit);
            m_xSynchronizeCB->connect_toggled(LINK(this, SvxBorderTabPage, SyncHdl_Impl));
            m_xLeftMF->connect_value_changed(LINK(this, SvxBorderTabPage, ModifyDistanceHdl_Impl));
            m_xRightMF->connect_value_changed(LINK(this, SvxBorderTabPage, ModifyDistanceHdl_Impl));
            m_xTopMF->connect_value_changed(LINK(this, SvxBorderTabPage, ModifyDistanceHdl_Impl));
            m_xBottomMF->connect_value_changed(LINK(this, SvxBorderTabPage, ModifyDistanceHdl_Impl));
        }
        else
        {
            m_xSpacingFrame->hide();
        }
        bIsDontCare = !pBoxInfo->IsValid( SvxBoxInfoItemValidFlags::DISABLE );
    }
    if(!mbUseMarginItem && eFUnit == FieldUnit::MM && MapUnit::MapTwip == rCoreAttrs.GetPool()->GetMetric( GetWhich( SID_ATTR_BORDER_INNER ) ))
    {
        //#i91548# changing the number of decimal digits changes the minimum values, too
        lcl_SetDecimalDigitsTo1(*m_xLeftMF);
        lcl_SetDecimalDigitsTo1(*m_xRightMF);
        lcl_SetDecimalDigitsTo1(*m_xTopMF);
        lcl_SetDecimalDigitsTo1(*m_xBottomMF);
        lcl_SetDecimalDigitsTo1(*m_xEdShadowSize);
    }

    FrameSelFlags nFlags = FrameSelFlags::Outer;
    if( mbHorEnabled )
        nFlags |= FrameSelFlags::InnerHorizontal;
    if( mbVerEnabled )
        nFlags |= FrameSelFlags::InnerVertical;
    if( mbTLBREnabled )
        nFlags |= FrameSelFlags::DiagonalTLBR;
    if( mbBLTREnabled )
        nFlags |= FrameSelFlags::DiagonalBLTR;
    if( bIsDontCare )
        nFlags |= FrameSelFlags::DontCare;
    m_aFrameSel.Initialize( nFlags );

    m_aFrameSel.SetSelectHdl(LINK(this, SvxBorderTabPage, LinesChanged_Impl));
    m_xLbLineStyle->SetSelectHdl( LINK( this, SvxBorderTabPage, SelStyleHdl_Impl ) );
    m_xLbLineColor->SetSelectHdl( LINK( this, SvxBorderTabPage, SelColHdl_Impl ) );
    m_xLineWidthLB->connect_changed(LINK(this, SvxBorderTabPage, ModifyWidthLBHdl_Impl));
    m_xLineWidthMF->connect_value_changed(LINK(this, SvxBorderTabPage, ModifyWidthMFHdl_Impl));
    m_xWndPresets->SetSelectHdl( LINK( this, SvxBorderTabPage, SelPreHdl_Impl ) );
    m_xWndShadows->SetSelectHdl( LINK( this, SvxBorderTabPage, SelSdwHdl_Impl ) );

    FillValueSets();
    FillLineListBox_Impl();

    // Reapply line width: probably one of predefined values should be selected
    SetLineWidth(m_xLineWidthMF->get_value(FieldUnit::NONE));

    // connections
    const SfxPoolItem* pItem = nullptr;
    if (rCoreAttrs.HasItem(GetWhich(SID_ATTR_PARA_GRABBAG), &pItem))
    {
        const SfxGrabBagItem* pGrabBag = static_cast<const SfxGrabBagItem*>(pItem);
        auto it = pGrabBag->GetGrabBag().find(u"DialogUseCharAttr"_ustr);
        if (it != pGrabBag->GetGrabBag().end())
        {
            bool bDialogUseCharAttr = false;
            it->second >>= bDialogUseCharAttr;
            if (bDialogUseCharAttr)
            {
                mnShadowSlot = SID_ATTR_CHAR_SHADOW;
                mnBoxSlot = SID_ATTR_CHAR_BOX;
            }
        }
    }

    bool bSupportsShadow = !SfxItemPool::IsSlot(GetWhich(mnShadowSlot));
    if( bSupportsShadow )
        m_xShadowControls.reset(new ShadowControlsWrapper(*m_xWndShadows, *m_xEdShadowSize, *m_xLbShadowColor));
    else
        HideShadowControls();

    if (mbUseMarginItem)
        m_xMarginControls.reset(new MarginControlsWrapper(*m_xLeftMF, *m_xRightMF, *m_xTopMF, *m_xBottomMF));

    // checkbox "Merge with next paragraph" only visible for Writer dialog format.paragraph
    m_xMergeWithNextCB->hide();
    // checkbox "Merge adjacent line styles" only visible for Writer dialog format.table
    m_xMergeAdjacentBordersCB->hide();

    if (SfxObjectShell* pDocSh = SfxObjectShell::Current())
    {
        Reference< XServiceInfo > xSI( pDocSh->GetModel(), UNO_QUERY );
        if ( xSI.is() )
            bIsCalcDoc = xSI->supportsService(u"com.sun.star.sheet.SpreadsheetDocument"_ustr);
    }
    if( bIsCalcDoc )
    {
        m_xRemoveAdjacentCellBordersCB->connect_toggled(LINK(this, SvxBorderTabPage, RemoveAdjacentCellBorderHdl_Impl));
        m_xRemoveAdjacentCellBordersCB->show();
        m_xRemoveAdjacentCellBordersCB->set_sensitive(false);
    }
    else
    {
        m_xRemoveAdjacentCellBordersCB->hide();
        m_xRemoveAdjacentCellBordersFT->hide();
    }
}

SvxBorderTabPage::~SvxBorderTabPage()
{
    m_xLbShadowColor.reset();
    m_xWndShadowsWin.reset();
    m_xWndShadows.reset();
    m_xLbLineColor.reset();
    m_xLbLineStyle.reset();
    m_xFrameSelWin.reset();
    m_xWndPresetsWin.reset();
    m_xWndPresets.reset();
}

std::unique_ptr<SfxTabPage> SvxBorderTabPage::Create( weld::Container* pPage, weld::DialogController* pController,
                                             const SfxItemSet* rAttrSet )
{
    return std::make_unique<SvxBorderTabPage>(pPage, pController, *rAttrSet);
}

void SvxBorderTabPage::ResetFrameLine_Impl( svx::FrameBorderType eBorder, const SvxBorderLine* pCoreLine, bool bValid )
{
    if( m_aFrameSel.IsBorderEnabled( eBorder ) )
    {
        if( bValid )
            m_aFrameSel.ShowBorder( eBorder, pCoreLine );
        else
            m_aFrameSel.SetBorderDontCare( eBorder );
    }
}

bool SvxBorderTabPage::IsBorderLineStyleAllowed( SvxBorderLineStyle nStyle ) const
{
    if (maUsedBorderStyles.empty())
        // All border styles are allowed.
        return true;

    return maUsedBorderStyles.count(nStyle) > 0;
}

void SvxBorderTabPage::Reset( const SfxItemSet* rSet )
{
    SfxItemPool* pPool = rSet->GetPool();

    if (m_aFrameSel.IsBorderEnabled(svx::FrameBorderType::TLBR))
    {
        sal_uInt16 nBorderDiagId = pPool->GetWhichIDFromSlotID(SID_ATTR_BORDER_DIAG_TLBR);
        if (const SvxLineItem* pLineItem = static_cast<const SvxLineItem*>(rSet->GetItem(nBorderDiagId)))
            m_aFrameSel.ShowBorder(svx::FrameBorderType::TLBR, pLineItem->GetLine());
        else
            m_aFrameSel.SetBorderDontCare(svx::FrameBorderType::TLBR);
    }

    if (m_aFrameSel.IsBorderEnabled(svx::FrameBorderType::BLTR))
    {
        sal_uInt16 nBorderDiagId = pPool->GetWhichIDFromSlotID(SID_ATTR_BORDER_DIAG_BLTR);
        if (const SvxLineItem* pLineItem = static_cast<const SvxLineItem*>(rSet->GetItem(nBorderDiagId)))
            m_aFrameSel.ShowBorder(svx::FrameBorderType::BLTR, pLineItem->GetLine());
        else
            m_aFrameSel.SetBorderDontCare(svx::FrameBorderType::BLTR);
    }

    if (m_xShadowControls)
    {
        sal_uInt16 nShadowId = pPool->GetWhichIDFromSlotID(mnShadowSlot);
        const SfxPoolItem* pItem = rSet->GetItem(nShadowId);
        if (pItem)
            m_xShadowControls->SetControlValue(*static_cast<const SvxShadowItem*>(pItem));
        else
            m_xShadowControls->SetControlDontKnow();
    }

    if (m_xMarginControls)
    {
        sal_uInt16 nAlignMarginId = pPool->GetWhichIDFromSlotID(SID_ATTR_ALIGN_MARGIN);
        const SfxPoolItem* pItem = rSet->GetItem(nAlignMarginId);
        if (pItem)
            m_xMarginControls->SetControlValue(*static_cast<const SvxMarginItem*>(pItem));
        else
            m_xMarginControls->SetControlDontKnow();
    }

    sal_uInt16 nMergeAdjacentBordersId = pPool->GetWhichIDFromSlotID(SID_SW_COLLAPSING_BORDERS);
    const SfxBoolItem *pMergeAdjacentBorders = static_cast<const SfxBoolItem*>(rSet->GetItem(nMergeAdjacentBordersId));
    if (!pMergeAdjacentBorders)
        m_xMergeAdjacentBordersCB->set_state(TRISTATE_INDET);
    else
        m_xMergeAdjacentBordersCB->set_active(pMergeAdjacentBorders->GetValue());
    m_xMergeAdjacentBordersCB->save_state();

    sal_uInt16 nMergeWithNextId = pPool->GetWhichIDFromSlotID(SID_ATTR_BORDER_CONNECT);
    const SfxBoolItem *pMergeWithNext = static_cast<const SfxBoolItem*>(rSet->GetItem(nMergeWithNextId));
    if (!pMergeWithNext)
        m_xMergeWithNextCB->set_state(TRISTATE_INDET);
    else
        m_xMergeWithNextCB->set_active(pMergeWithNext->GetValue());
    m_xMergeWithNextCB->save_state();

    const SvxBoxItem*       pBoxItem;
    const SvxBoxInfoItem*   pBoxInfoItem;
    sal_uInt16              nWhichBox       = GetWhich(mnBoxSlot);
    MapUnit                 eCoreUnit;

    pBoxItem  = static_cast<const SvxBoxItem*>(GetItem( *rSet, mnBoxSlot ));

    pBoxInfoItem = GetItem( *rSet, SID_ATTR_BORDER_INNER, false );

    eCoreUnit = pPool->GetMetric( nWhichBox );

    if ( pBoxItem && pBoxInfoItem ) // -> Don't Care
    {
        ResetFrameLine_Impl( svx::FrameBorderType::Left,   pBoxItem->GetLeft(),     pBoxInfoItem->IsValid( SvxBoxInfoItemValidFlags::LEFT ) );
        ResetFrameLine_Impl( svx::FrameBorderType::Right,  pBoxItem->GetRight(),    pBoxInfoItem->IsValid( SvxBoxInfoItemValidFlags::RIGHT ) );
        ResetFrameLine_Impl( svx::FrameBorderType::Top,    pBoxItem->GetTop(),      pBoxInfoItem->IsValid( SvxBoxInfoItemValidFlags::TOP ) );
        ResetFrameLine_Impl( svx::FrameBorderType::Bottom, pBoxItem->GetBottom(),   pBoxInfoItem->IsValid( SvxBoxInfoItemValidFlags::BOTTOM ) );
        ResetFrameLine_Impl( svx::FrameBorderType::Vertical,    pBoxInfoItem->GetVert(), pBoxInfoItem->IsValid( SvxBoxInfoItemValidFlags::VERT ) );
        ResetFrameLine_Impl( svx::FrameBorderType::Horizontal,    pBoxInfoItem->GetHori(), pBoxInfoItem->IsValid( SvxBoxInfoItemValidFlags::HORI ) );


        // distance inside

        if( !mbUseMarginItem )
        {
            if (m_xLeftMF->get_visible())
            {
                SetMetricValue(*m_xLeftMF,    pBoxInfoItem->GetDefDist(), eCoreUnit);
                SetMetricValue(*m_xRightMF,   pBoxInfoItem->GetDefDist(), eCoreUnit);
                SetMetricValue(*m_xTopMF,     pBoxInfoItem->GetDefDist(), eCoreUnit);
                SetMetricValue(*m_xBottomMF,  pBoxInfoItem->GetDefDist(), eCoreUnit);

                nMinValue = m_xLeftMF->get_value(FieldUnit::NONE);

                if ( pBoxInfoItem->IsDist() )
                {
                    if( rSet->GetItemState( nWhichBox ) >= SfxItemState::DEFAULT )
                    {
                        bool bIsAnyBorderVisible = m_aFrameSel.IsAnyBorderVisible();
                        if( !bIsAnyBorderVisible || !pBoxInfoItem->IsMinDist() )
                        {
                            m_xLeftMF->set_min(0, FieldUnit::NONE);
                            m_xRightMF->set_min(0, FieldUnit::NONE);
                            m_xTopMF->set_min(0, FieldUnit::NONE);
                            m_xBottomMF->set_min(0, FieldUnit::NONE);
                        }
                        tools::Long nLeftDist = pBoxItem->GetDistance( SvxBoxItemLine::LEFT);
                        SetMetricValue(*m_xLeftMF, nLeftDist, eCoreUnit);
                        tools::Long nRightDist = pBoxItem->GetDistance( SvxBoxItemLine::RIGHT);
                        SetMetricValue(*m_xRightMF, nRightDist, eCoreUnit);
                        tools::Long nTopDist = pBoxItem->GetDistance( SvxBoxItemLine::TOP);
                        SetMetricValue( *m_xTopMF, nTopDist, eCoreUnit );
                        tools::Long nBottomDist = pBoxItem->GetDistance( SvxBoxItemLine::BOTTOM);
                        SetMetricValue( *m_xBottomMF, nBottomDist, eCoreUnit );

                        // if the distance is set with no active border line
                        // or it is null with an active border line
                        // no automatic changes should be made
                        const tools::Long nDefDist = bIsAnyBorderVisible ? pBoxInfoItem->GetDefDist() : 0;
                        bool bDiffDist = (nDefDist != nLeftDist ||
                                    nDefDist != nRightDist ||
                                    nDefDist != nTopDist   ||
                                    nDefDist != nBottomDist);
                        if ((pBoxItem->GetSmallestDistance() || bIsAnyBorderVisible) && bDiffDist )
                        {
                            mbLeftModified = true;
                            mbRightModified = true;
                            mbTopModified = true;
                            mbBottomModified = true;
                        }
                    }
                    else
                    {
                        // #106224# different margins -> do not fill the edits
                        m_xLeftMF->set_text( OUString() );
                        m_xRightMF->set_text( OUString() );
                        m_xTopMF->set_text( OUString() );
                        m_xBottomMF->set_text( OUString() );
                    }
                }
                m_xLeftMF->save_value();
                m_xRightMF->save_value();
                m_xTopMF->save_value();
                m_xBottomMF->save_value();
            }
        }
    }
    else
    {
        // avoid ResetFrameLine-calls:
        m_aFrameSel.HideAllBorders();
    }

    if( !m_aFrameSel.IsAnyBorderVisible() )
        m_aFrameSel.DeselectAllBorders();

    // depict line (color) in controllers if unambiguous:

    {
        // Do all visible lines show the same line widths?
        tools::Long nWidth;
        SvxBorderLineStyle nStyle;
        bool bWidthEq = m_aFrameSel.GetVisibleWidth( nWidth, nStyle );
        if( bWidthEq )
        {
            // Determine the width first as some styles can be missing depending on it
            sal_Int64 nWidthPt =  static_cast<sal_Int64>(vcl::ConvertDoubleValue(
                        sal_Int64( nWidth ), m_xLineWidthMF->get_digits(),
                        MapUnit::MapTwip, FieldUnit::POINT ));
            SetLineWidth(nWidthPt);
            m_xLbLineStyle->SetWidth(nWidth);

            // then set the style
            m_xLbLineStyle->SelectEntry( nStyle );
        }
        else
            m_xLbLineStyle->SelectEntry(SvxBorderLineStyle::SOLID);

        // Do all visible lines show the same line color?
        Color aColor;
        bool bColorEq = m_aFrameSel.GetVisibleColor( aColor );
        if( !bColorEq )
            aColor = COL_BLACK;

        m_xLbLineColor->SelectEntry(aColor);
        auto nTextColor = Application::GetSettings().GetStyleSettings().GetWindowTextColor();
        m_xLbLineStyle->SetColor(nTextColor);

        // Select all visible lines, if they are all equal.
        if( bWidthEq && bColorEq )
            m_aFrameSel.SelectAllVisibleBorders();

        // set the current style and color (caches style in control even if nothing is selected)
        SelStyleHdl_Impl(*m_xLbLineStyle);
        SelColHdl_Impl(*m_xLbLineColor);
    }

    bool bEnable = m_xWndShadows->GetSelectedItemId() > 1 ;
    m_xFtShadowSize->set_sensitive(bEnable);
    m_xEdShadowSize->set_sensitive(bEnable);
    m_xFtShadowColor->set_sensitive(bEnable);
    m_xLbShadowColor->set_sensitive(bEnable);

    m_xWndPresets->SetNoSelection();

    // - no line - should not be selected

    if (m_xLbLineStyle->GetSelectEntryStyle() == SvxBorderLineStyle::NONE)
    {
        m_xLbLineStyle->SelectEntry(SvxBorderLineStyle::SOLID);
        SelStyleHdl_Impl(*m_xLbLineStyle);
    }

    const SfxUInt16Item* pHtmlModeItem = rSet->GetItemIfSet(SID_HTML_MODE, false);
    if(!pHtmlModeItem)
    {
        if (SfxObjectShell* pShell = SfxObjectShell::Current())
            pHtmlModeItem = pShell->GetItem(SID_HTML_MODE);
    }
    if(pHtmlModeItem)
    {
        sal_uInt16 nHtmlMode = pHtmlModeItem->GetValue();
        if(nHtmlMode & HTMLMODE_ON)
        {
            // there are no shadows in Html-mode and only complete borders
            m_xShadowFrame->set_sensitive(false);

            if( !(nSWMode & SwBorderModes::TABLE) )
            {
                m_xUserDefFT->set_sensitive(false);
                m_xFrameSelWin->set_sensitive(false);
                m_xWndPresets->RemoveItem(3);
                m_xWndPresets->RemoveItem(4);
                m_xWndPresets->RemoveItem(5);
            }
        }
    }

    LinesChanged_Impl( nullptr );
    if (m_xLeftMF->get_value(FieldUnit::NONE) == m_xRightMF->get_value(FieldUnit::NONE) &&
        m_xTopMF->get_value(FieldUnit::NONE) == m_xBottomMF->get_value(FieldUnit::NONE) &&
        m_xTopMF->get_value(FieldUnit::NONE) == m_xLeftMF->get_value(FieldUnit::NONE))
    {
        mbSync = true;
    }
    else
        mbSync = false;
    m_xSynchronizeCB->set_active(mbSync);

    mbRemoveAdjacentCellBorders = false;
    m_xRemoveAdjacentCellBordersCB->set_active(false);
    m_xRemoveAdjacentCellBordersCB->set_sensitive(false);
}

void SvxBorderTabPage::ChangesApplied()
{
    m_xLeftMF->save_value();
    m_xRightMF->save_value();
    m_xTopMF->save_value();
    m_xBottomMF->save_value();
    m_xMergeWithNextCB->save_state();
    m_xMergeAdjacentBordersCB->save_state();
}

DeactivateRC SvxBorderTabPage::DeactivatePage( SfxItemSet* _pSet )
{
    if ( _pSet )
        FillItemSet( _pSet );

    return DeactivateRC::LeavePage;
}

bool SvxBorderTabPage::FillItemSet( SfxItemSet* rCoreAttrs )
{
    bool bAttrsChanged = false;

    SfxItemPool* pPool = rCoreAttrs->GetPool();

    if (m_aFrameSel.IsBorderEnabled(svx::FrameBorderType::TLBR) &&
        m_aFrameSel.GetFrameBorderState(svx::FrameBorderType::TLBR) != svx::FrameBorderState::DontCare)
    {
        if (const SfxPoolItem* pOldItem = GetOldItem(*rCoreAttrs, SID_ATTR_BORDER_DIAG_TLBR))
        {
            SvxLineItem aLineItem(*static_cast<const SvxLineItem*>(pOldItem));
            aLineItem.SetLine(m_aFrameSel.GetFrameBorderStyle(svx::FrameBorderType::TLBR));
            rCoreAttrs->Put(aLineItem);
            bAttrsChanged = true;
        }
    }

    if (m_aFrameSel.IsBorderEnabled(svx::FrameBorderType::BLTR) &&
        m_aFrameSel.GetFrameBorderState(svx::FrameBorderType::BLTR) != svx::FrameBorderState::DontCare)
    {
        if (const SfxPoolItem* pOldItem = GetOldItem(*rCoreAttrs, SID_ATTR_BORDER_DIAG_BLTR))
        {
            SvxLineItem aLineItem(*static_cast<const SvxLineItem*>(pOldItem));
            aLineItem.SetLine(m_aFrameSel.GetFrameBorderStyle(svx::FrameBorderType::BLTR));
            rCoreAttrs->Put(aLineItem);
            bAttrsChanged = true;
        }
    }

    if (m_xShadowControls && m_xShadowControls->get_value_changed_from_saved())
    {
        if (const SfxPoolItem* pOldItem = GetOldItem(*rCoreAttrs, mnShadowSlot))
        {
            const SvxShadowItem& rOldShadowItem = *static_cast<const SvxShadowItem*>(pOldItem);
            rCoreAttrs->Put(m_xShadowControls->GetControlValue(rOldShadowItem));
            bAttrsChanged = true;
        }
    }

    if (m_xMarginControls && m_xMarginControls->get_value_changed_from_saved())
    {
        if (const SfxPoolItem* pOldItem = GetOldItem(*rCoreAttrs, SID_ATTR_ALIGN_MARGIN))
        {
            const SvxMarginItem& rOldMarginItem = *static_cast<const SvxMarginItem*>(pOldItem);
            rCoreAttrs->Put(m_xMarginControls->GetControlValue(rOldMarginItem));
            bAttrsChanged = true;
        }
    }

    if (m_xMergeAdjacentBordersCB->get_state_changed_from_saved())
    {
        auto nState = m_xMergeAdjacentBordersCB->get_state();
        if (nState == TRISTATE_INDET)
        {
            sal_uInt16 nMergeAdjacentBordersId = pPool->GetWhichIDFromSlotID(SID_SW_COLLAPSING_BORDERS);
            rCoreAttrs->ClearItem(nMergeAdjacentBordersId);
        }
        else
        {
            if (const SfxPoolItem* pOldItem = GetOldItem(*rCoreAttrs, SID_SW_COLLAPSING_BORDERS))
            {
                std::unique_ptr<SfxBoolItem> xNewItem(static_cast<SfxBoolItem*>(pOldItem->Clone()));
                xNewItem->SetValue(static_cast<bool>(nState));
                rCoreAttrs->Put(std::move(xNewItem));
            }
        }
        bAttrsChanged = true;
    }

    if (m_xMergeWithNextCB->get_state_changed_from_saved())
    {
        auto nState = m_xMergeWithNextCB->get_state();
        if (nState == TRISTATE_INDET)
        {
            sal_uInt16 nMergeWithNextId = pPool->GetWhichIDFromSlotID(SID_ATTR_BORDER_CONNECT);
            rCoreAttrs->ClearItem(nMergeWithNextId);
        }
        else
        {
            if (const SfxPoolItem* pOldItem = GetOldItem(*rCoreAttrs, SID_ATTR_BORDER_CONNECT))
            {
                std::unique_ptr<SfxBoolItem> xNewItem(static_cast<SfxBoolItem*>(pOldItem->Clone()));
                xNewItem->SetValue(static_cast<bool>(nState));
                rCoreAttrs->Put(std::move(xNewItem));
            }
        }
        bAttrsChanged = true;
    }

    bool                  bPut          = true;
    sal_uInt16            nBoxWhich     = GetWhich( mnBoxSlot );
    sal_uInt16            nBoxInfoWhich = pPool->GetWhichIDFromSlotID( SID_ATTR_BORDER_INNER, false );
    const SfxItemSet&     rOldSet       = GetItemSet();
    SvxBoxItem            aBoxItem      ( nBoxWhich );
    SvxBoxInfoItem        aBoxInfoItem  ( nBoxInfoWhich );
    const SvxBoxItem*     pOldBoxItem = static_cast<const SvxBoxItem*>(GetOldItem( *rCoreAttrs, mnBoxSlot ));

    MapUnit eCoreUnit = rOldSet.GetPool()->GetMetric( nBoxWhich );


    // outer border:

    std::pair<svx::FrameBorderType,SvxBoxItemLine> eTypes1[] = {
                                { svx::FrameBorderType::Top,SvxBoxItemLine::TOP },
                                { svx::FrameBorderType::Bottom,SvxBoxItemLine::BOTTOM },
                                { svx::FrameBorderType::Left,SvxBoxItemLine::LEFT },
                                { svx::FrameBorderType::Right,SvxBoxItemLine::RIGHT },
                            };

    for (std::pair<svx::FrameBorderType,SvxBoxItemLine> const & i : eTypes1)
        aBoxItem.SetLine( m_aFrameSel.GetFrameBorderStyle( i.first ), i.second );


    aBoxItem.SetRemoveAdjacentCellBorder( mbRemoveAdjacentCellBorders );
    // border hor/ver and TableFlag

    std::pair<svx::FrameBorderType,SvxBoxInfoItemLine> eTypes2[] = {
                                { svx::FrameBorderType::Horizontal,SvxBoxInfoItemLine::HORI },
                                { svx::FrameBorderType::Vertical,SvxBoxInfoItemLine::VERT }
                            };
    for (std::pair<svx::FrameBorderType,SvxBoxInfoItemLine> const & j : eTypes2)
        aBoxInfoItem.SetLine( m_aFrameSel.GetFrameBorderStyle( j.first ), j.second );

    aBoxInfoItem.EnableHor( mbHorEnabled );
    aBoxInfoItem.EnableVer( mbVerEnabled );


    // inner distance

    if (m_xLeftMF->get_visible())
    {
        // #i40405# enable distance controls for next dialog call
        aBoxInfoItem.SetDist( true );

        if( !mbUseMarginItem )
        {
            // #106224# all edits empty: do nothing
            if( !m_xLeftMF->get_text().isEmpty() || !m_xRightMF->get_text().isEmpty() ||
                !m_xTopMF->get_text().isEmpty() || !m_xBottomMF->get_text().isEmpty() )
            {
                const SvxBoxInfoItem* pOldBoxInfoItem = GetOldItem( *rCoreAttrs, SID_ATTR_BORDER_INNER );
                if (
                    !pOldBoxItem ||
                    m_xLeftMF->get_value_changed_from_saved() ||
                    m_xRightMF->get_value_changed_from_saved() ||
                    m_xTopMF->get_value_changed_from_saved() ||
                    m_xBottomMF->get_value_changed_from_saved() ||
                    nMinValue == m_xLeftMF->get_value(FieldUnit::NONE) ||
                    nMinValue == m_xRightMF->get_value(FieldUnit::NONE) ||
                    nMinValue == m_xTopMF->get_value(FieldUnit::NONE) ||
                    nMinValue == m_xBottomMF->get_value(FieldUnit::NONE) ||
                    (pOldBoxInfoItem && !pOldBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::DISTANCE))
                   )
                {
                    aBoxItem.SetDistance( static_cast<sal_uInt16>(GetCoreValue(*m_xLeftMF, eCoreUnit )), SvxBoxItemLine::LEFT  );
                    aBoxItem.SetDistance( static_cast<sal_uInt16>(GetCoreValue(*m_xRightMF, eCoreUnit )), SvxBoxItemLine::RIGHT );
                    aBoxItem.SetDistance( static_cast<sal_uInt16>(GetCoreValue(*m_xTopMF, eCoreUnit )), SvxBoxItemLine::TOP   );
                    aBoxItem.SetDistance( static_cast<sal_uInt16>(GetCoreValue(*m_xBottomMF, eCoreUnit )), SvxBoxItemLine::BOTTOM);
                }
                else
                {
                    aBoxItem.SetDistance(pOldBoxItem->GetDistance(SvxBoxItemLine::LEFT ), SvxBoxItemLine::LEFT);
                    aBoxItem.SetDistance(pOldBoxItem->GetDistance(SvxBoxItemLine::RIGHT),  SvxBoxItemLine::RIGHT);
                    aBoxItem.SetDistance(pOldBoxItem->GetDistance(SvxBoxItemLine::TOP  ), SvxBoxItemLine::TOP);
                    aBoxItem.SetDistance(pOldBoxItem->GetDistance(SvxBoxItemLine::BOTTOM), SvxBoxItemLine::BOTTOM);
                }
                aBoxInfoItem.SetValid( SvxBoxInfoItemValidFlags::DISTANCE );
            }
        }
    }


    // note Don't Care Status in the Info-Item:

    aBoxInfoItem.SetValid( SvxBoxInfoItemValidFlags::TOP,    m_aFrameSel.GetFrameBorderState( svx::FrameBorderType::Top )    != svx::FrameBorderState::DontCare );
    aBoxInfoItem.SetValid( SvxBoxInfoItemValidFlags::BOTTOM, m_aFrameSel.GetFrameBorderState( svx::FrameBorderType::Bottom ) != svx::FrameBorderState::DontCare );
    aBoxInfoItem.SetValid( SvxBoxInfoItemValidFlags::LEFT,   m_aFrameSel.GetFrameBorderState( svx::FrameBorderType::Left )   != svx::FrameBorderState::DontCare );
    aBoxInfoItem.SetValid( SvxBoxInfoItemValidFlags::RIGHT,  m_aFrameSel.GetFrameBorderState( svx::FrameBorderType::Right )  != svx::FrameBorderState::DontCare );
    aBoxInfoItem.SetValid( SvxBoxInfoItemValidFlags::HORI,   m_aFrameSel.GetFrameBorderState( svx::FrameBorderType::Horizontal )    != svx::FrameBorderState::DontCare );
    aBoxInfoItem.SetValid( SvxBoxInfoItemValidFlags::VERT,   m_aFrameSel.GetFrameBorderState( svx::FrameBorderType::Vertical )    != svx::FrameBorderState::DontCare );


    // Put or Clear of the border?

    bPut = true;

    if (   SfxItemState::DEFAULT == rOldSet.GetItemState( nBoxWhich,     false ))
    {
        bPut = aBoxItem != static_cast<const SvxBoxItem&>(rOldSet.Get(nBoxWhich));
    }
    if(  SfxItemState::DEFAULT == rOldSet.GetItemState( nBoxInfoWhich, false ) )
    {
        const SvxBoxInfoItem& rOldBoxInfo = static_cast<const SvxBoxInfoItem&>(
                                rOldSet.Get(nBoxInfoWhich));

        aBoxInfoItem.SetMinDist( rOldBoxInfo.IsMinDist() );
        aBoxInfoItem.SetDefDist( rOldBoxInfo.GetDefDist() );
        bPut |= (aBoxInfoItem != rOldBoxInfo );
    }

    if ( bPut )
    {
        if ( !pOldBoxItem || *pOldBoxItem != aBoxItem )
        {
            rCoreAttrs->Put( aBoxItem );
            bAttrsChanged = true;
        }
        const SfxPoolItem* pOld = GetOldItem( *rCoreAttrs, SID_ATTR_BORDER_INNER, false );

        if ( !pOld || *static_cast<const SvxBoxInfoItem*>(pOld) != aBoxInfoItem )
        {
            rCoreAttrs->Put( aBoxInfoItem );
            bAttrsChanged = true;
        }
    }
    else
    {
        rCoreAttrs->ClearItem( nBoxWhich );
        rCoreAttrs->ClearItem( nBoxInfoWhich );
    }

    return bAttrsChanged;
}

void SvxBorderTabPage::HideShadowControls()
{
    m_xShadowFrame->hide();
}

#define IID_PRE_CELL_NONE       1
#define IID_PRE_CELL_ALL        2
#define IID_PRE_CELL_LR         3
#define IID_PRE_CELL_TB         4
#define IID_PRE_CELL_L          5
#define IID_PRE_CELL_DIAG       6
#define IID_PRE_HOR_NONE        7
#define IID_PRE_HOR_OUTER       8
#define IID_PRE_HOR_HOR         9
#define IID_PRE_HOR_ALL         10
#define IID_PRE_HOR_OUTER2      11
#define IID_PRE_VER_NONE        12
#define IID_PRE_VER_OUTER       13
#define IID_PRE_VER_VER         14
#define IID_PRE_VER_ALL         15
#define IID_PRE_VER_OUTER2      16
#define IID_PRE_TABLE_NONE      17
#define IID_PRE_TABLE_OUTER     18
#define IID_PRE_TABLE_OUTERH    19
#define IID_PRE_TABLE_ALL       20
#define IID_PRE_TABLE_OUTER2    21

IMPL_LINK_NOARG(SvxBorderTabPage, SelPreHdl_Impl, ValueSet*, void)
{
    const svx::FrameBorderState SHOW = svx::FrameBorderState::Show;
    const svx::FrameBorderState HIDE = svx::FrameBorderState::Hide;
    const svx::FrameBorderState DONT = svx::FrameBorderState::DontCare;

    static const svx::FrameBorderState ppeStates[][ svx::FRAMEBORDERTYPE_COUNT ] =
    {                   /*    Left  Right Top   Bot   Hor   Ver   TLBR  BLTR */
/* ---------------------+--------------------------------------------------- */
/* IID_PRE_CELL_NONE    */  { HIDE, HIDE, HIDE, HIDE, HIDE, HIDE, HIDE, HIDE },
/* IID_PRE_CELL_ALL     */  { SHOW, SHOW, SHOW, SHOW, HIDE, HIDE, HIDE, HIDE },
/* IID_PRE_CELL_LR      */  { SHOW, SHOW, HIDE, HIDE, HIDE, HIDE, HIDE, HIDE },
/* IID_PRE_CELL_TB      */  { HIDE, HIDE, SHOW, SHOW, HIDE, HIDE, HIDE, HIDE },
/* IID_PRE_CELL_L       */  { SHOW, HIDE, HIDE, HIDE, HIDE, HIDE, HIDE, HIDE },
/* IID_PRE_CELL_DIAG    */  { HIDE, HIDE, HIDE, HIDE, HIDE, HIDE, SHOW, SHOW },
/* IID_PRE_HOR_NONE     */  { HIDE, HIDE, HIDE, HIDE, HIDE, HIDE, HIDE, HIDE },
/* IID_PRE_HOR_OUTER    */  { SHOW, SHOW, SHOW, SHOW, HIDE, HIDE, HIDE, HIDE },
/* IID_PRE_HOR_HOR      */  { HIDE, HIDE, SHOW, SHOW, SHOW, HIDE, HIDE, HIDE },
/* IID_PRE_HOR_ALL      */  { SHOW, SHOW, SHOW, SHOW, SHOW, HIDE, HIDE, HIDE },
/* IID_PRE_HOR_OUTER2   */  { SHOW, SHOW, SHOW, SHOW, DONT, HIDE, HIDE, HIDE },
/* IID_PRE_VER_NONE     */  { HIDE, HIDE, HIDE, HIDE, HIDE, HIDE, HIDE, HIDE },
/* IID_PRE_VER_OUTER    */  { SHOW, SHOW, SHOW, SHOW, HIDE, HIDE, HIDE, HIDE },
/* IID_PRE_VER_VER      */  { SHOW, SHOW, HIDE, HIDE, HIDE, SHOW, HIDE, HIDE },
/* IID_PRE_VER_ALL      */  { SHOW, SHOW, SHOW, SHOW, HIDE, SHOW, HIDE, HIDE },
/* IID_PRE_VER_OUTER2   */  { SHOW, SHOW, SHOW, SHOW, HIDE, DONT, HIDE, HIDE },
/* IID_PRE_TABLE_NONE   */  { HIDE, HIDE, HIDE, HIDE, HIDE, HIDE, HIDE, HIDE },
/* IID_PRE_TABLE_OUTER  */  { SHOW, SHOW, SHOW, SHOW, HIDE, HIDE, HIDE, HIDE },
/* IID_PRE_TABLE_OUTERH */  { SHOW, SHOW, SHOW, SHOW, SHOW, HIDE, HIDE, HIDE },
/* IID_PRE_TABLE_ALL    */  { SHOW, SHOW, SHOW, SHOW, SHOW, SHOW, HIDE, HIDE },
/* IID_PRE_TABLE_OUTER2 */  { SHOW, SHOW, SHOW, SHOW, DONT, DONT, HIDE, HIDE }
    };

    // first hide and deselect all frame borders
    m_aFrameSel.HideAllBorders();
    m_aFrameSel.DeselectAllBorders();

    // Using image ID to find correct line in table above.
    sal_uInt16 nLine = GetPresetImageId( m_xWndPresets->GetSelectedItemId() ) - 1;

    // Apply all styles from the table
    for( int nBorder = 0; nBorder < svx::FRAMEBORDERTYPE_COUNT; ++nBorder )
    {
        svx::FrameBorderType eBorder = svx::GetFrameBorderTypeFromIndex( nBorder );
        switch( ppeStates[ nLine ][ nBorder ] )
        {
            case SHOW:  m_aFrameSel.SelectBorder( eBorder );      break;
            case HIDE:  /* nothing to do */                     break;
            case DONT:  m_aFrameSel.SetBorderDontCare( eBorder ); break;
        }
    }

    // Show all lines that have been selected above
    if( m_aFrameSel.IsAnyBorderSelected() )
    {
        // any visible style, but "no-line" in line list box? -> use hair-line
        if (m_xLbLineStyle->GetSelectEntryStyle() == SvxBorderLineStyle::NONE)
            m_xLbLineStyle->SelectEntry(SvxBorderLineStyle::SOLID);

        // set current style to all previously selected lines
        SelStyleHdl_Impl(*m_xLbLineStyle);
        SelColHdl_Impl(*m_xLbLineColor);
    }

    // Presets ValueSet does not show a selection (used as push buttons).
    m_xWndPresets->SetNoSelection();

    LinesChanged_Impl( nullptr );
    UpdateRemoveAdjCellBorderCB( nLine + 1 );
}

IMPL_LINK_NOARG(SvxBorderTabPage, SelSdwHdl_Impl, ValueSet*, void)
{
    bool bEnable = m_xWndShadows->GetSelectedItemId() > 1;
    m_xFtShadowSize->set_sensitive(bEnable);
    m_xEdShadowSize->set_sensitive(bEnable);
    m_xFtShadowColor->set_sensitive(bEnable);
    m_xLbShadowColor->set_sensitive(bEnable);
}

IMPL_LINK(SvxBorderTabPage, SelColHdl_Impl, ColorListBox&, rColorBox, void)
{
    const NamedColor& aNamedColor = rColorBox.GetSelectedEntry();
    m_aFrameSel.SetColorToSelection(aNamedColor.m_aColor, aNamedColor.getComplexColor());
}

IMPL_LINK_NOARG(SvxBorderTabPage, ModifyWidthLBHdl_Impl, weld::ComboBox&, void)
{
    sal_Int32 nPos = m_xLineWidthLB->get_active();
    sal_Int32 nRemovedType = 0;
    if (m_xLineWidthLB->get_values_changed_from_saved()) {
        nRemovedType = std::size(s_LineWidths) - m_xLineWidthLB->get_count();
    }

    SetLineWidth(s_LineWidths[nPos + nRemovedType], nRemovedType);

    // Call the spinner handler to trigger all related modifications
    ModifyWidthMFHdl_Impl(*m_xLineWidthMF);
}

IMPL_LINK_NOARG(SvxBorderTabPage, ModifyWidthMFHdl_Impl, weld::MetricSpinButton&, void)
{
    sal_Int64 nVal = m_xLineWidthMF->get_value(FieldUnit::NONE);

    // for DOUBLE_THIN line style we cannot allow thinner line width then 1.10pt
    if (m_xLbLineStyle->GetSelectEntryStyle() == SvxBorderLineStyle::DOUBLE_THIN)
        m_xLineWidthMF->set_min(110, FieldUnit::NONE);
    else
        m_xLineWidthMF->set_min(5, FieldUnit::NONE);

    nVal = static_cast<sal_Int64>(vcl::ConvertDoubleValue(
                nVal,
                m_xLineWidthMF->get_digits(),
                FieldUnit::POINT, MapUnit::MapTwip ));
    m_xLbLineStyle->SetWidth( nVal );

    m_aFrameSel.SetStyleToSelection( nVal,
        m_xLbLineStyle->GetSelectEntryStyle() );
}

IMPL_LINK_NOARG(SvxBorderTabPage, SelStyleHdl_Impl, SvtLineListBox&, void)
{
    sal_Int64 nOldWidth = m_xLineWidthMF->get_value(FieldUnit::NONE);

    // for DOUBLE_THIN line style we cannot allow thinner line width then 1.10pt
    if (m_xLbLineStyle->GetSelectEntryStyle() == SvxBorderLineStyle::DOUBLE_THIN)
        m_xLineWidthMF->set_min(110, FieldUnit::NONE);
    else
        m_xLineWidthMF->set_min(5, FieldUnit::NONE);

    nOldWidth = static_cast<sal_Int64>(vcl::ConvertDoubleValue(
        nOldWidth,
        m_xLineWidthMF->get_digits(),
        FieldUnit::POINT,
        MapUnit::MapTwip));

    const sal_Int64 nOldMinWidth = lcl_GetMinLineWidth(m_aFrameSel.getCurrentStyleLineStyle());
    const sal_Int64 nNewMinWidth = lcl_GetMinLineWidth(m_xLbLineStyle->GetSelectEntryStyle());

    // auto change line-width if it doesn't correspond to minimal value
    // let's change only in case when user has not changed the line-width into some custom value
    sal_Int64 nNewWidth = (nOldMinWidth == nOldWidth) ? nNewMinWidth : nOldWidth;

    // if we had selected a predefined border width under SvxBorderLineWidth::Medium set the Medium as default
    // otherwise if we had a custom border width under 1.10pt then set the spinner to the maximum allowed value for double border styles
    bool bNewDoubleHairline = m_xLbLineStyle->GetSelectEntryStyle() == SvxBorderLineStyle::DOUBLE_THIN && !m_xLineWidthMF->get_visible() &&
        (nOldWidth == SvxBorderLineWidth::Hairline || nOldWidth == SvxBorderLineWidth::VeryThin || nOldWidth == SvxBorderLineWidth::Thin);
    if (bNewDoubleHairline && nNewWidth < SvxBorderLineWidth::Medium)
        nNewWidth = SvxBorderLineWidth::Medium;

    // set value inside edit box
    if (nOldWidth != nNewWidth)
    {
        const sal_Int64 nNewWidthPt = static_cast<sal_Int64>(vcl::ConvertDoubleValue(
            nNewWidth,
            m_xLineWidthMF->get_digits(),
            MapUnit::MapTwip,
            FieldUnit::POINT));
        SetLineWidth(nNewWidthPt);
    }

    if (m_xLbLineStyle->GetSelectEntryStyle() == SvxBorderLineStyle::DOUBLE_THIN)
    {
        for (size_t i = 0; i < 3; i++)
        {
            m_xLineWidthLB->save_values_by_id(OUString::number(i));
            m_xLineWidthLB->remove_id(OUString::number(i));
        }
        if (m_xLineWidthLB->get_active_id().isEmpty())
            m_xLineWidthLB->set_active_id(u"3"_ustr);
    }
    else
    {
        if (m_xLineWidthLB->get_values_changed_from_saved())
        {
            for (size_t i = 0; i < 3; i++)
                m_xLineWidthLB->append(i, OUString::number(i), m_xLineWidthLB->get_saved_values(i));
            m_xLineWidthLB->removeSavedValues();
        }
    }

    // set value inside style box
    m_aFrameSel.SetStyleToSelection( nNewWidth,
        m_xLbLineStyle->GetSelectEntryStyle() );
}


// ValueSet handling
sal_uInt16 SvxBorderTabPage::GetPresetImageId( sal_uInt16 nValueSetIdx ) const
{
    // table with all sets of predefined border styles
    static const sal_uInt16 ppnImgIds[][ BORDER_PRESET_COUNT ] =
    {
        // simple cell without diagonal frame borders
        {   IID_PRE_CELL_NONE,  IID_PRE_CELL_ALL,       IID_PRE_CELL_LR,        IID_PRE_CELL_TB,    IID_PRE_CELL_L          },
        // simple cell with diagonal frame borders
        {   IID_PRE_CELL_NONE,  IID_PRE_CELL_ALL,       IID_PRE_CELL_LR,        IID_PRE_CELL_TB,    IID_PRE_CELL_DIAG       },
        // with horizontal inner frame border
        {   IID_PRE_HOR_NONE,   IID_PRE_HOR_OUTER,      IID_PRE_HOR_HOR,        IID_PRE_HOR_ALL,    IID_PRE_HOR_OUTER2      },
        // with vertical inner frame border
        {   IID_PRE_VER_NONE,   IID_PRE_VER_OUTER,      IID_PRE_VER_VER,        IID_PRE_VER_ALL,    IID_PRE_VER_OUTER2      },
        // with horizontal and vertical inner frame borders
        {   IID_PRE_TABLE_NONE, IID_PRE_TABLE_OUTER,    IID_PRE_TABLE_OUTERH,   IID_PRE_TABLE_ALL,  IID_PRE_TABLE_OUTER2    }
    };

    // find correct set of presets
    int nLine = 0;
    if( !mbHorEnabled && !mbVerEnabled )
        nLine = (mbTLBREnabled || mbBLTREnabled) ? 1 : 0;
    else if( mbHorEnabled && !mbVerEnabled )
        nLine = 2;
    else if( !mbHorEnabled && mbVerEnabled )
        nLine = 3;
    else
        nLine = 4;

    DBG_ASSERT( (1 <= nValueSetIdx) && (nValueSetIdx <= BORDER_PRESET_COUNT),
        "SvxBorderTabPage::GetPresetImageId - wrong index" );
    return ppnImgIds[ nLine ][ nValueSetIdx - 1 ];
}

TranslateId SvxBorderTabPage::GetPresetStringId( sal_uInt16 nValueSetIdx ) const
{
    // string resource IDs for each image (in order of the IID_PRE_* image IDs)
    static const TranslateId pnStrIds[] =
    {
        RID_SVXSTR_TABLE_PRESET_NONE,
        RID_SVXSTR_PARA_PRESET_ALL,
        RID_SVXSTR_PARA_PRESET_ONLYLEFTRIGHT,
        RID_SVXSTR_PARA_PRESET_ONLYTOPBOTTOM,
        RID_SVXSTR_PARA_PRESET_ONLYLEFT,
        RID_SVXSTR_PARA_PRESET_DIAGONAL,

        RID_SVXSTR_TABLE_PRESET_NONE,
        RID_SVXSTR_TABLE_PRESET_ONLYOUTER,
        RID_SVXSTR_HOR_PRESET_ONLYHOR,
        RID_SVXSTR_TABLE_PRESET_OUTERALL,
        RID_SVXSTR_TABLE_PRESET_OUTERINNER,

        RID_SVXSTR_TABLE_PRESET_NONE,
        RID_SVXSTR_TABLE_PRESET_ONLYOUTER,
        RID_SVXSTR_VER_PRESET_ONLYVER,
        RID_SVXSTR_TABLE_PRESET_OUTERALL,
        RID_SVXSTR_TABLE_PRESET_OUTERINNER,

        RID_SVXSTR_TABLE_PRESET_NONE,
        RID_SVXSTR_TABLE_PRESET_ONLYOUTER,
        RID_SVXSTR_TABLE_PRESET_OUTERHORI,
        RID_SVXSTR_TABLE_PRESET_OUTERALL,
        RID_SVXSTR_TABLE_PRESET_OUTERINNER
    };
    return pnStrIds[ GetPresetImageId( nValueSetIdx ) - 1 ];
}

void SvxBorderTabPage::FillPresetVS()
{
    // basic initialization of the ValueSet
    m_xWndPresets->SetStyle( m_xWndPresets->GetStyle() | WB_ITEMBORDER | WB_DOUBLEBORDER );
    m_xWndPresets->SetColCount( BORDER_PRESET_COUNT );

    // insert images and help texts
    for( sal_uInt16 nVSIdx = 1; nVSIdx <= BORDER_PRESET_COUNT; ++nVSIdx )
    {
        m_xWndPresets->InsertItem( nVSIdx );
        m_xWndPresets->SetItemImage(nVSIdx, m_aBorderImgVec[GetPresetImageId(nVSIdx) - 1]);
        m_xWndPresets->SetItemText( nVSIdx, SvxResId( GetPresetStringId( nVSIdx ) ) );
    }

    // show the control
    m_xWndPresets->SetNoSelection();
    m_xWndPresets->SetOptimalSize();
    m_xWndPresets->Show();
}

void SvxBorderTabPage::FillShadowVS()
{
    // basic initialization of the ValueSet
    m_xWndShadows->SetStyle( m_xWndShadows->GetStyle() | WB_ITEMBORDER | WB_DOUBLEBORDER );
    m_xWndShadows->SetColCount( BORDER_SHADOW_COUNT );

    // string resource IDs for each image
    static const TranslateId pnStrIds[ BORDER_SHADOW_COUNT ] =
        { RID_CUISTR_SHADOW_STYLE_NONE, RID_CUISTR_SHADOW_STYLE_BOTTOMRIGHT, RID_CUISTR_SHADOW_STYLE_TOPRIGHT, RID_CUISTR_SHADOW_STYLE_BOTTOMLEFT, RID_CUISTR_SHADOW_STYLE_TOPLEFT };

    // insert images and help texts
    for( sal_uInt16 nVSIdx = 1; nVSIdx <= BORDER_SHADOW_COUNT; ++nVSIdx )
    {
        m_xWndShadows->InsertItem( nVSIdx );
        m_xWndShadows->SetItemImage(nVSIdx, m_aShadowImgVec[nVSIdx-1]);
        m_xWndShadows->SetItemText( nVSIdx, CuiResId( pnStrIds[ nVSIdx - 1 ] ) );
    }

    // show the control
    m_xWndShadows->SelectItem( 1 );
    m_xWndShadows->SetOptimalSize();
    m_xWndShadows->Show();
}


void SvxBorderTabPage::FillValueSets()
{
    FillPresetVS();
    FillShadowVS();
}

void SvxBorderTabPage::SetLineWidth( sal_Int64 nWidth, sal_Int32 nRemovedType )
{
    if ( nWidth >= 0 )
        m_xLineWidthMF->set_value( nWidth, FieldUnit::POINT );

    auto it = std::find( std::begin(s_LineWidths), std::end(s_LineWidths), nWidth );

    if ( it != std::end(s_LineWidths) && *it >= 0 )
    {
        // Select predefined value in combobox
        m_xLineWidthMF->hide();
        m_xLineWidthLB->set_active(std::distance(std::begin(s_LineWidths), it) - nRemovedType);
    }
    else
    {
        // This is not one of predefined values. Show spinner
        m_xLineWidthLB->set_active(std::size(s_LineWidths) - nRemovedType -1);
        m_xLineWidthMF->show();
    }
}

static Color lcl_mediumColor( Color aMain, Color /*aDefault*/ )
{
    return SvxBorderLine::threeDMediumColor( aMain );
}

void SvxBorderTabPage::FillLineListBox_Impl()
{
    using namespace ::com::sun::star::table::BorderLineStyle;

    static struct {
        SvxBorderLineStyle mnStyle;
        SvtLineListBox::ColorFunc mpColor1Fn;
        SvtLineListBox::ColorFunc mpColor2Fn;
        SvtLineListBox::ColorDistFunc mpColorDistFn;
    } const aLines[] = {
        // Simple lines
        { SvxBorderLineStyle::SOLID,        &sameColor, &sameColor, &sameDistColor },
        { SvxBorderLineStyle::DOTTED,       &sameColor, &sameColor, &sameDistColor },
        { SvxBorderLineStyle::DASHED,       &sameColor, &sameColor, &sameDistColor },
        { SvxBorderLineStyle::FINE_DASHED,  &sameColor, &sameColor, &sameDistColor },
        { SvxBorderLineStyle::DASH_DOT,     &sameColor, &sameColor, &sameDistColor },
        { SvxBorderLineStyle::DASH_DOT_DOT, &sameColor, &sameColor, &sameDistColor },

        // Double lines
        { SvxBorderLineStyle::DOUBLE,              &sameColor, &sameColor, &sameDistColor },
        { SvxBorderLineStyle::DOUBLE_THIN,         &sameColor, &sameColor, &sameDistColor },
        { SvxBorderLineStyle::THINTHICK_SMALLGAP,  &sameColor, &sameColor, &sameDistColor },
        { SvxBorderLineStyle::THINTHICK_MEDIUMGAP, &sameColor, &sameColor, &sameDistColor },
        { SvxBorderLineStyle::THINTHICK_LARGEGAP,  &sameColor, &sameColor, &sameDistColor },
        { SvxBorderLineStyle::THICKTHIN_SMALLGAP,  &sameColor, &sameColor, &sameDistColor },
        { SvxBorderLineStyle::THICKTHIN_MEDIUMGAP, &sameColor, &sameColor, &sameDistColor },
        { SvxBorderLineStyle::THICKTHIN_LARGEGAP,  &sameColor, &sameColor, &sameDistColor },

        { SvxBorderLineStyle::EMBOSSED, &SvxBorderLine::threeDLightColor, &SvxBorderLine::threeDDarkColor, &lcl_mediumColor },
        { SvxBorderLineStyle::ENGRAVED, &SvxBorderLine::threeDDarkColor, &SvxBorderLine::threeDLightColor, &lcl_mediumColor },

        { SvxBorderLineStyle::OUTSET, &SvxBorderLine::lightColor, &SvxBorderLine::darkColor, &sameDistColor },
        { SvxBorderLineStyle::INSET,  &SvxBorderLine::darkColor, &SvxBorderLine::lightColor, &sameDistColor }
    };

    m_xLbLineStyle->SetSourceUnit( FieldUnit::TWIP );

    for (size_t i = 0; i < std::size(aLines); ++i)
    {
        if (!IsBorderLineStyleAllowed(aLines[i].mnStyle))
            continue;

        m_xLbLineStyle->InsertEntry(
            SvxBorderLine::getWidthImpl(aLines[i].mnStyle),
            aLines[i].mnStyle,
            lcl_GetMinLineWidth(aLines[i].mnStyle),
            aLines[i].mpColor1Fn,
            aLines[i].mpColor2Fn,
            aLines[i].mpColorDistFn);
    }

    sal_Int64 nVal = m_xLineWidthMF->get_value(FieldUnit::NONE);
    nVal = static_cast<sal_Int64>(vcl::ConvertDoubleValue(nVal, m_xLineWidthMF->get_digits(),
                                                                  m_xLineWidthMF->get_unit(), MapUnit::MapTwip));
    m_xLbLineStyle->SetWidth( nVal );
}


IMPL_LINK_NOARG(SvxBorderTabPage, LinesChanged_Impl, LinkParamNone*, void)
{
    if (!mbUseMarginItem && m_xLeftMF->get_visible())
    {
        bool bLineSet = m_aFrameSel.IsAnyBorderVisible();
        bool bSpaceModified =   mbLeftModified ||
                                mbRightModified ||
                                mbTopModified ||
                                mbBottomModified;

        if(bLineSet)
        {
            if(!bSpaceModified)
            {
                m_xLeftMF->set_value(nMinValue, FieldUnit::NONE);
                m_xRightMF->set_value(nMinValue, FieldUnit::NONE);
                m_xTopMF->set_value(nMinValue, FieldUnit::NONE);
                m_xBottomMF->set_value(nMinValue, FieldUnit::NONE);
            }
        }
        else
        {
            m_xLeftMF->set_min(0, FieldUnit::NONE);
            m_xRightMF->set_min(0, FieldUnit::NONE);
            m_xTopMF->set_min(0, FieldUnit::NONE);
            m_xBottomMF->set_min(0, FieldUnit::NONE);
        }
        // for tables everything is allowed
        SvxBoxInfoItemValidFlags nValid = SvxBoxInfoItemValidFlags::TOP|SvxBoxInfoItemValidFlags::BOTTOM|SvxBoxInfoItemValidFlags::LEFT|SvxBoxInfoItemValidFlags::RIGHT;

        m_xLeftFT->set_sensitive( bool(nValid & SvxBoxInfoItemValidFlags::LEFT) );
        m_xRightFT->set_sensitive( bool(nValid & SvxBoxInfoItemValidFlags::RIGHT) );
        m_xTopFT->set_sensitive( bool(nValid & SvxBoxInfoItemValidFlags::TOP) );
        m_xBottomFT->set_sensitive( bool(nValid & SvxBoxInfoItemValidFlags::BOTTOM) );
        m_xLeftMF->set_sensitive( bool(nValid & SvxBoxInfoItemValidFlags::LEFT) );
        m_xRightMF->set_sensitive( bool(nValid & SvxBoxInfoItemValidFlags::RIGHT) );
        m_xTopMF->set_sensitive( bool(nValid & SvxBoxInfoItemValidFlags::TOP) );
        m_xBottomMF->set_sensitive( bool(nValid & SvxBoxInfoItemValidFlags::BOTTOM) );
        m_xSynchronizeCB->set_sensitive(m_xRightMF->get_sensitive() || m_xTopMF->get_sensitive() ||
                                        m_xBottomMF->get_sensitive() || m_xLeftMF->get_sensitive());
    }
    UpdateRemoveAdjCellBorderCB( SAL_MAX_UINT16 );
}


IMPL_LINK( SvxBorderTabPage, ModifyDistanceHdl_Impl, weld::MetricSpinButton&, rField, void)
{
    if (&rField == m_xLeftMF.get())
        mbLeftModified = true;
    else if (&rField == m_xRightMF.get())
        mbRightModified = true;
    else if (&rField == m_xTopMF.get())
        mbTopModified = true;
    else if (&rField == m_xBottomMF.get())
        mbBottomModified = true;

    if (mbSync)
    {
        const auto nVal = rField.get_value(FieldUnit::NONE);
        if (&rField != m_xLeftMF.get())
            m_xLeftMF->set_value(nVal, FieldUnit::NONE);
        if (&rField != m_xRightMF.get())
            m_xRightMF->set_value(nVal, FieldUnit::NONE);
        if (&rField != m_xTopMF.get())
            m_xTopMF->set_value(nVal, FieldUnit::NONE);
        if (&rField != m_xBottomMF.get())
            m_xBottomMF->set_value(nVal, FieldUnit::NONE);
    }
}

IMPL_LINK( SvxBorderTabPage, SyncHdl_Impl, weld::Toggleable&, rBox, void)
{
    mbSync = rBox.get_active();
}

IMPL_LINK( SvxBorderTabPage, RemoveAdjacentCellBorderHdl_Impl, weld::Toggleable&, rBox, void)
{
    mbRemoveAdjacentCellBorders = rBox.get_active();
}

void SvxBorderTabPage::UpdateRemoveAdjCellBorderCB( sal_uInt16 nPreset )
{
    if( !bIsCalcDoc )
        return;
    const SfxItemSet&     rOldSet         = GetItemSet();
    const SvxBoxInfoItem* pOldBoxInfoItem = GetOldItem( rOldSet, SID_ATTR_BORDER_INNER );
    const SvxBoxItem*     pOldBoxItem     = static_cast<const SvxBoxItem*>(GetOldItem( rOldSet, mnBoxSlot ));
    if( !pOldBoxInfoItem || !pOldBoxItem )
        return;
    std::pair<svx::FrameBorderType, SvxBoxInfoItemValidFlags> eTypes1[] = {
        { svx::FrameBorderType::Top,SvxBoxInfoItemValidFlags::TOP },
        { svx::FrameBorderType::Bottom,SvxBoxInfoItemValidFlags::BOTTOM },
        { svx::FrameBorderType::Left,SvxBoxInfoItemValidFlags::LEFT },
        { svx::FrameBorderType::Right,SvxBoxInfoItemValidFlags::RIGHT },
    };
    SvxBoxItemLine const eTypes2[] = {
        SvxBoxItemLine::TOP,
        SvxBoxItemLine::BOTTOM,
        SvxBoxItemLine::LEFT,
        SvxBoxItemLine::RIGHT,
    };

    // Check if current selection involves deletion of at least one border
    bool bBorderDeletionReq = false;
    for ( size_t i=0; i < std::size( eTypes1 ); ++i )
    {
        if( pOldBoxItem->GetLine( eTypes2[i] ) || !( pOldBoxInfoItem->IsValid( eTypes1[i].second ) ) )
        {
            if( m_aFrameSel.GetFrameBorderState( eTypes1[i].first ) == svx::FrameBorderState::Hide )
            {
                bBorderDeletionReq = true;
                break;
            }
        }
    }

    if( !bBorderDeletionReq && ( nPreset == IID_PRE_CELL_NONE || nPreset == IID_PRE_TABLE_NONE ) )
        bBorderDeletionReq = true;

    m_xRemoveAdjacentCellBordersCB->set_sensitive(bBorderDeletionReq);

    if( !bBorderDeletionReq )
    {
        mbRemoveAdjacentCellBorders = false;
        m_xRemoveAdjacentCellBordersCB->set_active(false);
    }
}

void SvxBorderTabPage::PageCreated(const SfxAllItemSet& aSet)
{
    const SfxUInt16Item* pSWModeItem = aSet.GetItem<SfxUInt16Item>(SID_SWMODE_TYPE, false);
    const SfxUInt32Item* pFlagItem = aSet.GetItem<SfxUInt32Item>(SID_FLAG_TYPE, false);
    if (pSWModeItem)
    {
        nSWMode = static_cast<SwBorderModes>(pSWModeItem->GetValue());
        // #i43593#
        // show checkbox <m_xMergeWithNextCB> for format.paragraph
        if ( nSWMode == SwBorderModes::PARA )
        {
            m_xMergeWithNextCB->show();
            m_xPropertiesFrame->show();
        }
        // show checkbox <m_xMergeAdjacentBordersCB> for format.paragraph
        else if ( nSWMode == SwBorderModes::TABLE )
        {
            m_xMergeAdjacentBordersCB->show();
            m_xPropertiesFrame->show();
        }
    }
    if (pFlagItem)
        if ( ( pFlagItem->GetValue() & SVX_HIDESHADOWCTL ) == SVX_HIDESHADOWCTL )
            HideShadowControls();
}

void SvxBorderTabPage::SetTableMode()
{
    nSWMode = SwBorderModes::TABLE;
}

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