/* -*- 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 <memory>
#include <editeng/sizeitem.hxx>
#include <osl/file.hxx>
#include <tools/urlobj.hxx>

#include <strings.hrc>
#include <svx/colorbox.hxx>
#include <svx/dlgctrl.hxx>
#include <svx/xlineit0.hxx>
#include <svx/xlinjoit.hxx>
#include <svx/xlncapit.hxx>
#include <svx/xlndsit.hxx>
#include <svx/xlnwtit.hxx>
#include <svx/xlnstwit.hxx>
#include <svx/xlnedwit.hxx>
#include <svx/xlnclit.hxx>
#include <svx/xlnstit.hxx>
#include <svx/xlnedit.hxx>
#include <svx/xlnstcit.hxx>
#include <svx/xlnedcit.hxx>


#include <svx/tabline.hxx>
#include <svx/xtable.hxx>
#include <svx/drawitem.hxx>
#include <cuitabline.hxx>
#include <dialmgr.hxx>
#include <svx/dlgutil.hxx>
#include <svx/svxgraphicitem.hxx>
#include <svx/ofaitem.hxx>
#include <svx/svdobj.hxx>
#include <svx/svdpage.hxx>
#include <svx/svdview.hxx>
#include <svx/svdmodel.hxx>
#include <svx/xlntrit.hxx>
#include <svx/xfltrit.hxx>
#include <editeng/numitem.hxx>
#include <editeng/brushitem.hxx>
#include <svx/gallery.hxx>
#include <sfx2/opengrf.hxx>
#include <svx/dialmgr.hxx>
#include <svx/svxids.hrc>
#include <svx/strings.hrc>
#include <cuitabarea.hxx>
#include <svtools/unitconv.hxx>
#include <comphelper/lok.hxx>
#include <o3tl/string_view.hxx>

#define MAX_BMP_WIDTH   16
#define MAX_BMP_HEIGHT  16

using namespace com::sun::star;

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

const WhichRangesContainer SvxLineTabPage::pLineRanges(svl::Items<
    XATTR_LINETRANSPARENCE, XATTR_LINETRANSPARENCE,
    SID_ATTR_LINE_STYLE, SID_ATTR_LINE_ENDCENTER
>);

SvxLineTabPage::SvxLineTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rInAttrs)
    : SfxTabPage(pPage, pController, u"cui/ui/linetabpage.ui"_ustr, u"LineTabPage"_ustr, &rInAttrs)
    , m_pSymbolList(nullptr)
    , m_bNewSize(false)
    , m_nSymbolType(SVX_SYMBOLTYPE_UNKNOWN) // unknown respectively unchanged
    , m_bLastWidthModified(false)
    , m_aSymbolLastSize(Size(0,0))
    , m_bSymbols(false)
    , m_rOutAttrs(rInAttrs)
    , m_bObjSelected(false)
    , m_aXLineAttr(rInAttrs.GetPool())
    , m_rXLSet(m_aXLineAttr.GetItemSet())
    , m_pnLineEndListState(nullptr)
    , m_pnDashListState(nullptr)
    , m_pnColorListState(nullptr)
    , m_nPageType(PageType::Area)
    , m_nDlgType(0)
    , m_pPosDashLb(nullptr)
    , m_pPosLineEndLb(nullptr)
    , m_xBoxColor(m_xBuilder->weld_widget(u"boxCOLOR"_ustr))
    , m_xLbLineStyle(new SvxLineLB(m_xBuilder->weld_combo_box(u"LB_LINE_STYLE"_ustr)))
    , m_xLbColor(new ColorListBox(m_xBuilder->weld_menu_button(u"LB_COLOR"_ustr),
                [this]{ return GetDialogController()->getDialog(); }))
    , m_xBoxWidth(m_xBuilder->weld_widget(u"boxWIDTH"_ustr))
    , m_xMtrLineWidth(m_xBuilder->weld_metric_spin_button(u"MTR_FLD_LINE_WIDTH"_ustr, FieldUnit::CM))
    , m_xBoxTransparency(m_xBuilder->weld_widget(u"boxTRANSPARENCY"_ustr))
    , m_xMtrTransparent(m_xBuilder->weld_metric_spin_button(u"MTR_LINE_TRANSPARENT"_ustr, FieldUnit::PERCENT))
    , m_xFlLineEnds(m_xBuilder->weld_widget(u"FL_LINE_ENDS"_ustr))
    , m_xBoxArrowStyles(m_xBuilder->weld_widget(u"boxARROW_STYLES"_ustr))
    , m_xLbStartStyle(new SvxLineEndLB(m_xBuilder->weld_combo_box(u"LB_START_STYLE"_ustr)))
    , m_xBoxStart(m_xBuilder->weld_widget(u"boxSTART"_ustr))
    , m_xMtrStartWidth(m_xBuilder->weld_metric_spin_button(u"MTR_FLD_START_WIDTH"_ustr, FieldUnit::CM))
    , m_xTsbCenterStart(m_xBuilder->weld_check_button(u"TSB_CENTER_START"_ustr))
    , m_xBoxEnd(m_xBuilder->weld_widget(u"boxEND"_ustr))
    , m_xLbEndStyle(new SvxLineEndLB(m_xBuilder->weld_combo_box(u"LB_END_STYLE"_ustr)))
    , m_xMtrEndWidth(m_xBuilder->weld_metric_spin_button(u"MTR_FLD_END_WIDTH"_ustr, FieldUnit::CM))
    , m_xTsbCenterEnd(m_xBuilder->weld_check_button(u"TSB_CENTER_END"_ustr))
    , m_xCbxSynchronize(m_xBuilder->weld_check_button(u"CBX_SYNCHRONIZE"_ustr))
    , m_xCtlPreview(new weld::CustomWeld(*m_xBuilder, u"CTL_PREVIEW"_ustr, m_aCtlPreview))
    , m_xFLEdgeStyle(m_xBuilder->weld_widget(u"FL_EDGE_STYLE"_ustr))
    , m_xGridEdgeCaps(m_xBuilder->weld_widget(u"gridEDGE_CAPS"_ustr))
    , m_xLBEdgeStyle(m_xBuilder->weld_combo_box(u"LB_EDGE_STYLE"_ustr))
    , m_xLBCapStyle(m_xBuilder->weld_combo_box(u"LB_CAP_STYLE"_ustr)) // LineCaps
    , m_xFlSymbol(m_xBuilder->weld_widget(u"FL_SYMBOL_FORMAT"_ustr)) //#58425# Symbols on a line (e.g. StarChart)
    , m_xGridIconSize(m_xBuilder->weld_widget(u"gridICON_SIZE"_ustr))
    , m_xSymbolMB(m_xBuilder->weld_menu_button(u"MB_SYMBOL_BITMAP"_ustr))
    , m_xSymbolWidthMF(m_xBuilder->weld_metric_spin_button(u"MF_SYMBOL_WIDTH"_ustr, FieldUnit::CM))
    , m_xSymbolHeightMF(m_xBuilder->weld_metric_spin_button(u"MF_SYMBOL_HEIGHT"_ustr, FieldUnit::CM))
    , m_xSymbolRatioCB(m_xBuilder->weld_check_button(u"CB_SYMBOL_RATIO"_ustr))
{
    // This Page requires ExchangeSupport
    SetExchangeSupport();

    // Metric set
    FieldUnit eFUnit = GetModuleFieldUnit( rInAttrs );

    switch ( eFUnit )
    {
        case FieldUnit::M:
        case FieldUnit::KM:
            eFUnit = FieldUnit::MM;
            [[fallthrough]]; // we now have mm
        case FieldUnit::MM:
            m_xMtrLineWidth->set_increments(50, 500, FieldUnit::NONE);
            m_xMtrStartWidth->set_increments(50, 500, FieldUnit::NONE);
            m_xMtrEndWidth->set_increments(50, 500, FieldUnit::NONE);
            break;

            case FieldUnit::INCH:
            m_xMtrLineWidth->set_increments(2, 20, FieldUnit::NONE);
            m_xMtrStartWidth->set_increments(2, 20, FieldUnit::NONE);
            m_xMtrEndWidth->set_increments(2, 20, FieldUnit::NONE);
            break;
            default: ;// prevent warning
    }
    SetFieldUnit( *m_xMtrLineWidth, eFUnit );
    SetFieldUnit( *m_xMtrStartWidth, eFUnit );
    SetFieldUnit( *m_xMtrEndWidth, eFUnit );

    // determine PoolUnit
    SfxItemPool* pPool = m_rOutAttrs.GetPool();
    assert(pPool && "Where is the pool?");
    m_ePoolUnit = pPool->GetMetric( SID_ATTR_LINE_WIDTH );

    m_xLbLineStyle->connect_changed(LINK(this, SvxLineTabPage, ClickInvisibleHdl_Impl));
    m_xLbColor->SetSelectHdl( LINK( this, SvxLineTabPage, ChangePreviewListBoxHdl_Impl ) );
    m_xMtrLineWidth->connect_value_changed(LINK(this, SvxLineTabPage, ChangePreviewModifyHdl_Impl));
    m_xMtrTransparent->connect_value_changed(LINK( this, SvxLineTabPage, ChangeTransparentHdl_Impl));

    m_xLbStartStyle->connect_changed(LINK(this, SvxLineTabPage, ChangeStartListBoxHdl_Impl));
    m_xLbEndStyle->connect_changed(LINK(this, SvxLineTabPage, ChangeEndListBoxHdl_Impl));
    m_xMtrStartWidth->connect_value_changed(LINK(this, SvxLineTabPage, ChangeStartModifyHdl_Impl));
    m_xMtrEndWidth->connect_value_changed(LINK( this, SvxLineTabPage, ChangeEndModifyHdl_Impl));
    m_xTsbCenterStart->connect_toggled(LINK(this, SvxLineTabPage, ChangeStartClickHdl_Impl));
    m_xTsbCenterEnd->connect_toggled(LINK(this, SvxLineTabPage, ChangeEndClickHdl_Impl));

    Link<weld::ComboBox&,void> aEdgeStyle = LINK(this, SvxLineTabPage, ChangeEdgeStyleHdl_Impl);
    m_xLBEdgeStyle->connect_changed(aEdgeStyle);

    // LineCaps
    Link<weld::ComboBox&,void> aCapStyle = LINK(this, SvxLineTabPage, ChangeCapStyleHdl_Impl);
    m_xLBCapStyle->connect_changed(aCapStyle);

    // Symbols on a line (eg star charts), MB-handler set
    m_xSymbolMB->connect_selected(LINK(this, SvxLineTabPage, GraphicHdl_Impl));
    m_xSymbolWidthMF->connect_value_changed(LINK(this, SvxLineTabPage, SizeHdl_Impl));
    m_xSymbolHeightMF->connect_value_changed(LINK(this, SvxLineTabPage, SizeHdl_Impl));
    m_xSymbolRatioCB->connect_toggled(LINK(this, SvxLineTabPage, RatioHdl_Impl));

    m_xSymbolRatioCB->set_active(true);
    ShowSymbolControls(false);

    m_nActLineWidth = -1;
}

void SvxLineTabPage::ShowSymbolControls(bool bOn)
{
    // Symbols on a line (e.g. StarCharts), symbol-enable controls

    m_bSymbols=bOn;
    m_xFlSymbol->set_visible(bOn);
    m_aCtlPreview.ShowSymbol(bOn);
}

SvxLineTabPage::~SvxLineTabPage()
{
    m_xCtlPreview.reset();
    m_xLbEndStyle.reset();
    m_xLbStartStyle.reset();
    m_xLbColor.reset();
    m_xLbLineStyle.reset();
    m_aGalleryBrushItems.clear();
    m_aSymbolBrushItems.clear();
}

void SvxLineTabPage::Construct()
{
    FillListboxes();
    PopulateMenus();
}

void SvxLineTabPage::FillListboxes()
{
    // Line styles
    auto nOldSelect = m_xLbLineStyle->get_active();
    // aLbLineStyle.FillStyles();
    m_xLbLineStyle->Fill( m_pDashList );
    m_xLbLineStyle->set_active( nOldSelect );

    // Line end style
    OUString sNone( comphelper::LibreOfficeKit::isActive() ? SvxResId( RID_SVXSTR_INVISIBLE )
        : SvxResId( RID_SVXSTR_NONE ) );
    nOldSelect = m_xLbStartStyle->get_active();
    m_xLbStartStyle->clear();
    m_xLbStartStyle->append_text(sNone);
    m_xLbStartStyle->Fill(m_pLineEndList);
    m_xLbStartStyle->set_active(nOldSelect);
    nOldSelect = m_xLbEndStyle->get_active();
    m_xLbEndStyle->clear();
    m_xLbEndStyle->append_text(sNone);
    m_xLbEndStyle->Fill(m_pLineEndList, false);
    m_xLbEndStyle->set_active(nOldSelect);
}

void SvxLineTabPage::ActivatePage( const SfxItemSet& rSet )
{
    const SfxUInt16Item* pPageTypeItem = rSet.GetItem<SfxUInt16Item>(SID_PAGE_TYPE, false);
    if (pPageTypeItem)
        SetPageType(static_cast<PageType>(pPageTypeItem->GetValue()));
    if( m_nDlgType == 0 && m_pDashList.is() )
    {
        sal_Int32 nPos;
        sal_Int32 nCount;

        // Dash list
        if( ( *m_pnDashListState & ChangeType::MODIFIED ) ||
            ( *m_pnDashListState & ChangeType::CHANGED ) )
        {
            if( *m_pnDashListState & ChangeType::CHANGED )
                m_pDashList = static_cast<SvxLineTabDialog*>(GetDialogController() )->GetNewDashList();

            *m_pnDashListState = ChangeType::NONE;

            // Style list
            nPos = m_xLbLineStyle->get_active();

            m_xLbLineStyle->clear();
            m_xLbLineStyle->append_text(SvxResId(RID_SVXSTR_INVISIBLE));
            m_xLbLineStyle->append_text(SvxResId(RID_SVXSTR_SOLID));
            m_xLbLineStyle->Fill(m_pDashList);
            nCount = m_xLbLineStyle->get_count();

            if ( nCount == 0 )
                ; // This case should never occur
            else if( nCount <= nPos )
                m_xLbLineStyle->set_active(0);
            else
                m_xLbLineStyle->set_active(nPos);
        }

        INetURLObject   aDashURL( m_pDashList->GetPath() );

        aDashURL.Append( m_pDashList->GetName() );
        DBG_ASSERT( aDashURL.GetProtocol() != INetProtocol::NotValid, "invalid URL" );
        // LineEnd list
        if( ( *m_pnLineEndListState & ChangeType::MODIFIED ) || ( *m_pnLineEndListState & ChangeType::CHANGED ) )
        {
            if( *m_pnLineEndListState & ChangeType::CHANGED )
                m_pLineEndList = static_cast<SvxLineTabDialog*>(GetDialogController())->GetNewLineEndList();

            *m_pnLineEndListState = ChangeType::NONE;

            nPos = m_xLbLineStyle->get_active();
            OUString sNone( comphelper::LibreOfficeKit::isActive() ? SvxResId( RID_SVXSTR_INVISIBLE )
                : SvxResId( RID_SVXSTR_NONE ) );

            m_xLbStartStyle->clear();
            m_xLbStartStyle->append_text(sNone);

            m_xLbStartStyle->Fill( m_pLineEndList );
            nCount = m_xLbStartStyle->get_count();
            if( nCount == 0 )
                ; // This case should never occur
            else if( nCount <= nPos )
                m_xLbStartStyle->set_active(0);
            else
                m_xLbStartStyle->set_active(nPos);

            m_xLbEndStyle->clear();
            m_xLbEndStyle->append_text(sNone);

            m_xLbEndStyle->Fill( m_pLineEndList, false );
            nCount = m_xLbEndStyle->get_count();

            if( nCount == 0 )
                ; // This case should never occur
            else if( nCount <= nPos )
                m_xLbEndStyle->set_active(0);
            else
                m_xLbEndStyle->set_active(nPos);
        }
        INetURLObject aLineURL( m_pLineEndList->GetPath() );

        aLineURL.Append( m_pLineEndList->GetName() );
        DBG_ASSERT( aLineURL.GetProtocol() != INetProtocol::NotValid, "invalid URL" );
        // Evaluate if another TabPage set another fill type
        if( m_xLbLineStyle->get_active() != 0 )
        {
            if( m_nPageType == PageType::Hatch ) // 1
            {
                m_xLbLineStyle->set_active(*m_pPosDashLb + 2); // +2 due to SOLID and INVISIBLE
                ChangePreviewHdl_Impl( nullptr );
            }
            if( m_nPageType == PageType::Bitmap )
            {
                m_xLbStartStyle->set_active(*m_pPosLineEndLb + 1);// +1 due to SOLID
                m_xLbEndStyle->set_active(*m_pPosLineEndLb + 1);// +1 due to SOLID
                ChangePreviewHdl_Impl( nullptr );
            }
        }

            // ColorList
        if( *m_pnColorListState != ChangeType::NONE )
        {
            ChangePreviewHdl_Impl( nullptr );
        }

        m_nPageType = PageType::Area;
    }
    // Page does not yet exist in the ctor, that's why we do it here!

    else if (m_nDlgType == 1101) // nNoArrowNoShadowDlg from chart2/source/controller/dialogs/dlg_ObjectProperties.cxx
    {
        m_xFlLineEnds->hide();
        m_xFLEdgeStyle->hide();
    }
}


DeactivateRC SvxLineTabPage::DeactivatePage( SfxItemSet* _pSet )
{
    if( m_nDlgType == 0 ) // Line dialog
    {
        m_nPageType = PageType::Gradient; // possibly for extensions
        *m_pPosDashLb = m_xLbLineStyle->get_active() - 2;// First entry SOLID!!!
        sal_Int32 nPos = m_xLbStartStyle->get_active();
        if (nPos != -1)
            nPos--;
        *m_pPosLineEndLb = nPos;
    }

    if( _pSet )
        FillItemSet( _pSet );

    return DeactivateRC::LeavePage;
}


bool SvxLineTabPage::FillItemSet( SfxItemSet* rAttrs )
{
    const SfxPoolItem* pOld = nullptr;
    sal_Int32  nPos;
    bool    bModified = false;

    // To prevent modifications to the list, we do not set other page's items.
    if( m_nDlgType != 0 || m_nPageType != PageType::Hatch )
    {
        nPos = m_xLbLineStyle->get_active();
        if( nPos != -1 &&
            m_xLbLineStyle->get_value_changed_from_saved() )
        {
            std::unique_ptr<XLineStyleItem> pStyleItem;

            if( nPos == 0 )
                pStyleItem.reset(new XLineStyleItem( drawing::LineStyle_NONE ));
            else if( nPos == 1 )
                pStyleItem.reset(new XLineStyleItem( drawing::LineStyle_SOLID ));
            else
            {
                pStyleItem.reset(new XLineStyleItem( drawing::LineStyle_DASH ));

                // For added security
                if( m_pDashList->Count() > static_cast<tools::Long>( nPos - 2 ) )
                {
                    XLineDashItem aDashItem( m_xLbLineStyle->get_active_text(),
                                        m_pDashList->GetDash( nPos - 2 )->GetDash() );
                    pOld = GetOldItem( *rAttrs, XATTR_LINEDASH );
                    if ( !pOld || !( *static_cast<const XLineDashItem*>(pOld) == aDashItem ) )
                    {
                        rAttrs->Put( aDashItem );
                        bModified = true;
                    }
                }
            }
            pOld = GetOldItem( *rAttrs, XATTR_LINESTYLE );
            if ( !pOld || !( *static_cast<const XLineStyleItem*>(pOld) == *pStyleItem ) )
            {
                rAttrs->Put( std::move(pStyleItem) );
                bModified = true;
            }
        }
    }
    // Line width
    // GetSavedValue() returns OUString!
    if( m_xMtrLineWidth->get_value_changed_from_saved() )
    {
        XLineWidthItem aItem( GetCoreValue( *m_xMtrLineWidth, m_ePoolUnit ) );
        pOld = GetOldItem( *rAttrs, XATTR_LINEWIDTH );
        if ( !pOld || !( *static_cast<const XLineWidthItem*>(pOld) == aItem ) )
        {
            rAttrs->Put( aItem );
            bModified = true;
        }
    }
    // Width line start
    if( m_xMtrStartWidth->get_value_changed_from_saved() )
    {
        XLineStartWidthItem aItem( GetCoreValue( *m_xMtrStartWidth, m_ePoolUnit ) );
        pOld = GetOldItem( *rAttrs, XATTR_LINESTARTWIDTH );
        if ( !pOld || !( *static_cast<const XLineStartWidthItem*>(pOld) == aItem ) )
        {
            rAttrs->Put( aItem );
            bModified = true;
        }
    }
    // Width line end
    if( m_xMtrEndWidth->get_value_changed_from_saved() )
    {
        XLineEndWidthItem aItem( GetCoreValue( *m_xMtrEndWidth, m_ePoolUnit ) );
        pOld = GetOldItem( *rAttrs, XATTR_LINEENDWIDTH );
        if ( !pOld || !( *static_cast<const XLineEndWidthItem*>(pOld) == aItem ) )
        {
            rAttrs->Put( aItem );
            bModified = true;
        }
    }

    // Line color
    if (m_xLbColor->IsValueChangedFromSaved())
    {
        NamedColor aNamedColor = m_xLbColor->GetSelectedEntry();
        XLineColorItem aItem(aNamedColor.m_aName, aNamedColor.m_aColor);
        aItem.setComplexColor(aNamedColor.getComplexColor());

        pOld = GetOldItem( *rAttrs, XATTR_LINECOLOR );
        if ( !pOld || !( *static_cast<const XLineColorItem*>(pOld) == aItem ) )
        {
            rAttrs->Put( aItem );
            bModified = true;
        }
    }

    if( m_nDlgType != 0 || m_nPageType != PageType::Bitmap )
    {
        // Line start
        nPos = m_xLbStartStyle->get_active();
        if( nPos != -1 && m_xLbStartStyle->get_value_changed_from_saved() )
        {
            std::unique_ptr<XLineStartItem> pItem;
            if( nPos == 0 )
                pItem.reset(new XLineStartItem());
            else if( m_pLineEndList->Count() > static_cast<tools::Long>( nPos - 1 ) )
                pItem.reset(new XLineStartItem( m_xLbStartStyle->get_active_text(), m_pLineEndList->GetLineEnd( nPos - 1 )->GetLineEnd() ));
            pOld = GetOldItem( *rAttrs, XATTR_LINESTART );
            if( pItem && ( !pOld || *pOld != *pItem ) )
            {
                rAttrs->Put( std::move(pItem) );
                bModified = true;
            }
        }
        // Line end
        nPos = m_xLbEndStyle->get_active();
        if( nPos != -1 && m_xLbEndStyle->get_value_changed_from_saved() )
        {
            std::unique_ptr<XLineEndItem> pItem;
            if( nPos == 0 )
                pItem.reset(new XLineEndItem());
            else if( m_pLineEndList->Count() > static_cast<tools::Long>( nPos - 1 ) )
                pItem.reset(new XLineEndItem( m_xLbEndStyle->get_active_text(), m_pLineEndList->GetLineEnd( nPos - 1 )->GetLineEnd() ));
            pOld = GetOldItem( *rAttrs, XATTR_LINEEND );
            if( pItem &&
                ( !pOld || !( *static_cast<const XLineEndItem*>(pOld) == *pItem ) ) )
            {
                rAttrs->Put( std::move(pItem) );
                bModified = true;
            }
        }
    }

    // Centered line end
    TriState eState = m_xTsbCenterStart->get_state();
    if( m_xTsbCenterStart->get_state_changed_from_saved() )
    {
        XLineStartCenterItem aItem( eState != TRISTATE_FALSE );
        pOld = GetOldItem( *rAttrs, XATTR_LINESTARTCENTER );
        if ( !pOld || !( *static_cast<const XLineStartCenterItem*>(pOld) == aItem ) )
        {
            rAttrs->Put( aItem );
            bModified = true;
        }
    }
    eState = m_xTsbCenterEnd->get_state();
    if( m_xTsbCenterEnd->get_state_changed_from_saved() )
    {
        XLineEndCenterItem aItem( eState != TRISTATE_FALSE );
        pOld = GetOldItem( *rAttrs, XATTR_LINEENDCENTER );
        if ( !pOld || !( *static_cast<const XLineEndCenterItem*>(pOld) == aItem ) )
        {
            rAttrs->Put( aItem );
            bModified = true;
        }
    }

    // Transparency
    sal_uInt16 nVal = m_xMtrTransparent->get_value(FieldUnit::PERCENT);
    if( m_xMtrTransparent->get_value_changed_from_saved() )
    {
        XLineTransparenceItem aItem( nVal );
        pOld = GetOldItem( *rAttrs, XATTR_LINETRANSPARENCE );
        if ( !pOld || !( *static_cast<const XLineTransparenceItem*>(pOld) == aItem ) )
        {
            rAttrs->Put( aItem );
            bModified = true;
        }
    }

    nPos = m_xLBEdgeStyle->get_active();
    if (nPos != -1 && m_xLBEdgeStyle->get_value_changed_from_saved())
    {
        std::unique_ptr<XLineJointItem> pNew;

        switch(nPos)
        {
            case 0: // Rounded, default
            {
                pNew.reset(new XLineJointItem(css::drawing::LineJoint_ROUND));
                break;
            }
            case 1: // - none -
            {
                pNew.reset(new XLineJointItem(css::drawing::LineJoint_NONE));
                break;
            }
            case 2: // Miter
            {
                pNew.reset(new XLineJointItem(css::drawing::LineJoint_MITER));
                break;
            }
            case 3: // Bevel
            {
                pNew.reset(new XLineJointItem(css::drawing::LineJoint_BEVEL));
                break;
            }
        }

        if(pNew)
        {
            pOld = GetOldItem( *rAttrs, XATTR_LINEJOINT );

            if(!pOld || !(*static_cast<const XLineJointItem*>(pOld) == *pNew))
            {
                rAttrs->Put( std::move(pNew) );
                bModified = true;
            }
        }
    }

    // LineCaps
    nPos = m_xLBCapStyle->get_active();
    if (nPos != -1 && m_xLBCapStyle->get_value_changed_from_saved())
    {
        std::unique_ptr<XLineCapItem> pNew;

        switch(nPos)
        {
            case 0: // Butt (=Flat), default
            {
                pNew.reset(new XLineCapItem(css::drawing::LineCap_BUTT));
                break;
            }
            case 1: // Round
            {
                pNew.reset(new XLineCapItem(css::drawing::LineCap_ROUND));
                break;
            }
            case 2: // Square
            {
                pNew.reset(new XLineCapItem(css::drawing::LineCap_SQUARE));
                break;
            }
        }

        if(pNew)
        {
            pOld = GetOldItem( *rAttrs, XATTR_LINECAP );

            if(!pOld || !(*static_cast<const XLineCapItem*>(pOld) == *pNew))
            {
                rAttrs->Put( std::move(pNew) );
                bModified = true;
            }
        }
    }

    if(m_nSymbolType!=SVX_SYMBOLTYPE_UNKNOWN || m_bNewSize)
    {
        // Was set by selection or the size is different
        SvxSizeItem  aSItem(rAttrs->GetPool()->GetWhichIDFromSlotID(SID_ATTR_SYMBOLSIZE),m_aSymbolSize);
        const SfxPoolItem* pSOld = GetOldItem( *rAttrs, rAttrs->GetPool()->GetWhichIDFromSlotID(SID_ATTR_SYMBOLSIZE) );
        m_bNewSize  = pSOld ? *static_cast<const SvxSizeItem *>(pSOld) != aSItem : m_bNewSize ;
        if(m_bNewSize)
        {
            rAttrs->Put(aSItem);
            bModified=true;
        }

        SfxInt32Item aTItem(rAttrs->GetPool()->GetWhichIDFromSlotID(SID_ATTR_SYMBOLTYPE),m_nSymbolType);
        const SfxPoolItem* pTOld = GetOldItem( *rAttrs, rAttrs->GetPool()->GetWhichIDFromSlotID(SID_ATTR_SYMBOLTYPE) );
        bool bNewType = pTOld == nullptr || *static_cast<const SfxInt32Item*>(pTOld) != aTItem;
        if(bNewType && m_nSymbolType==SVX_SYMBOLTYPE_UNKNOWN)
            bNewType=false; // a small fix, type wasn't set -> don't create a type item after all!
        if(bNewType)
        {
            rAttrs->Put(aTItem);
            bModified=true;
        }

        if(m_nSymbolType!=SVX_SYMBOLTYPE_NONE)
        {
            SvxBrushItem aBItem(m_aSymbolGraphic,GPOS_MM,rAttrs->GetPool()->GetWhichIDFromSlotID(SID_ATTR_BRUSH));
            const SfxPoolItem* pBOld = GetOldItem( *rAttrs, rAttrs->GetPool()->GetWhichIDFromSlotID(SID_ATTR_BRUSH) );
            bool bNewBrush =
                pBOld == nullptr || *static_cast<const SvxBrushItem*>(pBOld) != aBItem;
            if(bNewBrush)
            {
                rAttrs->Put(aBItem);
                bModified=true;
            }
        }
    }
    rAttrs->Put (CntUInt16Item(SID_PAGE_TYPE, static_cast<sal_uInt16>(m_nPageType)));
    return bModified;
}


void SvxLineTabPage::FillXLSet_Impl()
{
    sal_Int32 nPos;

    if (m_xLbLineStyle->get_active() == -1)
    {
        m_rXLSet.Put( XLineStyleItem( drawing::LineStyle_NONE ) );
    }
    else if (m_xLbLineStyle->get_active() == 0)
        m_rXLSet.Put( XLineStyleItem( drawing::LineStyle_NONE ) );
    else if (m_xLbLineStyle->get_active() == 1)
        m_rXLSet.Put( XLineStyleItem( drawing::LineStyle_SOLID ) );
    else
    {
        m_rXLSet.Put( XLineStyleItem( drawing::LineStyle_DASH ) );

        nPos = m_xLbLineStyle->get_active();
        if (nPos != -1)
        {
            m_rXLSet.Put( XLineDashItem( m_xLbLineStyle->get_active_text(),
                            m_pDashList->GetDash( nPos - 2 )->GetDash() ) );
        }
    }

    nPos = m_xLbStartStyle->get_active();
    if (nPos != -1)
    {
        if( nPos == 0 )
            m_rXLSet.Put( XLineStartItem() );
        else
            m_rXLSet.Put( XLineStartItem( m_xLbStartStyle->get_active_text(),
                        m_pLineEndList->GetLineEnd( nPos - 1 )->GetLineEnd() ) );
    }
    nPos = m_xLbEndStyle->get_active();
    if (nPos != -1)
    {
        if( nPos == 0 )
            m_rXLSet.Put( XLineEndItem() );
        else
            m_rXLSet.Put( XLineEndItem( m_xLbEndStyle->get_active_text(),
                        m_pLineEndList->GetLineEnd( nPos - 1 )->GetLineEnd() ) );
    }

    nPos = m_xLBEdgeStyle->get_active();
    if (nPos != -1)
    {
        switch(nPos)
        {
            case 0: // Rounded, default
            {
                m_rXLSet.Put(XLineJointItem(css::drawing::LineJoint_ROUND));
                break;
            }
            case 1: // - none -
            {
                m_rXLSet.Put(XLineJointItem(css::drawing::LineJoint_NONE));
                break;
            }
            case 2: // Miter
            {
                m_rXLSet.Put(XLineJointItem(css::drawing::LineJoint_MITER));
                break;
            }
            case 3: // Bevel
            {
                m_rXLSet.Put(XLineJointItem(css::drawing::LineJoint_BEVEL));
                break;
            }
        }
    }

    // LineCaps
    nPos = m_xLBCapStyle->get_active();
    if (nPos != -1)
    {
        switch(nPos)
        {
            case 0: // Butt (=Flat), default
            {
                m_rXLSet.Put(XLineCapItem(css::drawing::LineCap_BUTT));
                break;
            }
            case 1: // Round
            {
                m_rXLSet.Put(XLineCapItem(css::drawing::LineCap_ROUND));
                break;
            }
            case 2: // Square
            {
                m_rXLSet.Put(XLineCapItem(css::drawing::LineCap_SQUARE));
                break;
            }
        }
    }

    m_rXLSet.Put( XLineStartWidthItem( GetCoreValue( *m_xMtrStartWidth, m_ePoolUnit ) ) );
    m_rXLSet.Put( XLineEndWidthItem( GetCoreValue( *m_xMtrEndWidth, m_ePoolUnit ) ) );

    m_rXLSet.Put( XLineWidthItem( GetCoreValue( *m_xMtrLineWidth, m_ePoolUnit ) ) );
    NamedColor aNamedColor = m_xLbColor->GetSelectedEntry();
    XLineColorItem aLineColor(aNamedColor.m_aName, aNamedColor.m_aColor);
    aLineColor.setComplexColor(aNamedColor.getComplexColor());
    m_rXLSet.Put(aLineColor);

    // Centered line end
    if( m_xTsbCenterStart->get_state() == TRISTATE_TRUE )
        m_rXLSet.Put( XLineStartCenterItem( true ) );
    else if( m_xTsbCenterStart->get_state() == TRISTATE_FALSE )
        m_rXLSet.Put( XLineStartCenterItem( false ) );

    if( m_xTsbCenterEnd->get_state() == TRISTATE_TRUE )
        m_rXLSet.Put( XLineEndCenterItem( true ) );
    else if( m_xTsbCenterEnd->get_state() == TRISTATE_FALSE )
        m_rXLSet.Put( XLineEndCenterItem( false ) );

    // Transparency
    sal_uInt16 nVal = m_xMtrTransparent->get_value(FieldUnit::PERCENT);
    m_rXLSet.Put( XLineTransparenceItem( nVal ) );

    m_aCtlPreview.SetLineAttributes(m_aXLineAttr.GetItemSet());
}


void SvxLineTabPage::Reset( const SfxItemSet* rAttrs )
{
    drawing::LineStyle  eXLS; // drawing::LineStyle_NONE, drawing::LineStyle_SOLID, drawing::LineStyle_DASH

    // Line style
    tools::Long nSymType=SVX_SYMBOLTYPE_UNKNOWN;
    bool bPrevSym=false;
    bool bEnable=true;
    bool bIgnoreGraphic=false;
    bool bIgnoreSize=false;
    if(const SfxInt32Item* pSymbolTypeItem = rAttrs->GetItemIfSet(rAttrs->GetPool()->GetWhichIDFromSlotID(SID_ATTR_SYMBOLTYPE)))
    {
        nSymType = pSymbolTypeItem->GetValue();
    }

    if(nSymType == SVX_SYMBOLTYPE_AUTO)
    {
        m_aSymbolGraphic=m_aAutoSymbolGraphic;
        m_aSymbolSize=m_aSymbolLastSize=m_aAutoSymbolGraphic.GetPrefSize();
        bPrevSym=true;
    }
    else if(nSymType == SVX_SYMBOLTYPE_NONE)
    {
        bEnable=false;
        bIgnoreGraphic=true;
        bIgnoreSize=true;
    }
    else if(nSymType >= 0)
    {
        ScopedVclPtrInstance< VirtualDevice > pVDev;
        pVDev->SetMapMode(MapMode(MapUnit::Map100thMM));

        std::unique_ptr<SdrModel> pModel(
            new SdrModel(nullptr, nullptr, true));
        rtl::Reference<SdrPage> pPage = new SdrPage( *pModel, false );
        pPage->SetSize(Size(1000,1000));
        pModel->InsertPage( pPage.get(), 0 );
        {
        SdrView aView( *pModel, pVDev );
        aView.hideMarkHandles();
        aView.ShowSdrPage(pPage.get());
        size_t nSymTmp = static_cast<size_t>(nSymType);
        if(m_pSymbolList)
        {
            if(m_pSymbolList->GetObjCount())
            {
                nSymTmp %= m_pSymbolList->GetObjCount(); // Treat list as cyclic!
                rtl::Reference<SdrObject> pObj=m_pSymbolList->GetObj(nSymTmp);
                if(pObj)
                {
                    // directly clone to target SdrModel
                    pObj = pObj->CloneSdrObject(*pModel);

                    if(m_xSymbolAttr)
                    {
                        pObj->SetMergedItemSet(*m_xSymbolAttr);
                    }
                    else
                    {
                        pObj->SetMergedItemSet(m_rOutAttrs);
                    }

                    pPage->NbcInsertObject(pObj.get());

                    // Generate invisible square to give all symbol types a
                    // bitmap size, which is independent from specific glyph
                    rtl::Reference<SdrObject> pInvisibleSquare(m_pSymbolList->GetObj(0));

                    // directly clone to target SdrModel
                    pInvisibleSquare = pInvisibleSquare->CloneSdrObject(*pModel);

                    pPage->NbcInsertObject(pInvisibleSquare.get());
                    pInvisibleSquare->SetMergedItem(XFillTransparenceItem(100));
                    pInvisibleSquare->SetMergedItem(XLineTransparenceItem(100));

                    aView.MarkAll();
                    GDIMetaFile aMeta(aView.GetMarkedObjMetaFile());

                    m_aSymbolGraphic=Graphic(aMeta);
                    m_aSymbolSize=pObj->GetSnapRect().GetSize();
                    m_aSymbolGraphic.SetPrefSize(pInvisibleSquare->GetSnapRect().GetSize());
                    m_aSymbolGraphic.SetPrefMapMode(MapMode(MapUnit::Map100thMM));
                    bPrevSym=true;
                    bEnable=true;
                    bIgnoreGraphic=true;

                    aView.UnmarkAll();
                    pPage->RemoveObject(1);
                    pInvisibleSquare.clear();
                    pPage->RemoveObject(0);
                    pObj.clear();
                }
            }
        }
        }
    }
    if(const SvxBrushItem* pBrushItem = rAttrs->GetItemIfSet(rAttrs->GetPool()->GetWhichIDFromSlotID(SID_ATTR_BRUSH)))
    {
        const Graphic* pGraphic = pBrushItem->GetGraphic();
        if( pGraphic )
        {
            if(!bIgnoreGraphic)
            {
                m_aSymbolGraphic=*pGraphic;
            }
            if(!bIgnoreSize)
            {
                m_aSymbolSize=OutputDevice::LogicToLogic( pGraphic->GetPrefSize(),
                                                        pGraphic->GetPrefMapMode(),
                                                        MapMode(MapUnit::Map100thMM));
            }
            bPrevSym=true;
        }
    }

    if(const SvxSizeItem* pSymbolSizeItem = rAttrs->GetItemIfSet(rAttrs->GetPool()->GetWhichIDFromSlotID(SID_ATTR_SYMBOLSIZE)))
    {
        m_aSymbolSize = pSymbolSizeItem->GetSize();
    }

    m_xGridIconSize->set_sensitive(bEnable);

    if(bPrevSym)
    {
        SetMetricValue(*m_xSymbolWidthMF,  m_aSymbolSize.Width(), m_ePoolUnit);
        SetMetricValue(*m_xSymbolHeightMF, m_aSymbolSize.Height(),m_ePoolUnit);
        m_aCtlPreview.SetSymbol(&m_aSymbolGraphic,m_aSymbolSize);
        m_aSymbolLastSize=m_aSymbolSize;
    }

    if( rAttrs->GetItemState( XATTR_LINESTYLE ) != SfxItemState::INVALID )
    {
        eXLS = rAttrs->Get( XATTR_LINESTYLE ).GetValue();

        switch( eXLS )
        {
            case drawing::LineStyle_NONE:
                m_xLbLineStyle->set_active(0);
                break;
            case drawing::LineStyle_SOLID:
                m_xLbLineStyle->set_active(1);
                break;

            case drawing::LineStyle_DASH:
                m_xLbLineStyle->set_active(-1);
                m_xLbLineStyle->set_active_text(rAttrs->Get( XATTR_LINEDASH ).GetName());
                break;

            default:
                break;
        }
    }
    else
    {
        m_xLbLineStyle->set_active(-1);
    }

    // Line strength
    if( rAttrs->GetItemState( XATTR_LINEWIDTH ) != SfxItemState::INVALID )
    {
        SetMetricValue( *m_xMtrLineWidth, rAttrs->Get( XATTR_LINEWIDTH ).GetValue(), m_ePoolUnit );
    }
    else
        m_xMtrLineWidth->set_text(u""_ustr);

    // Line color
    m_xLbColor->SetNoSelection();

    if ( rAttrs->GetItemState( XATTR_LINECOLOR ) != SfxItemState::INVALID )
    {
        Color aCol = rAttrs->Get( XATTR_LINECOLOR ).GetColorValue();
        m_xLbColor->SelectEntry( aCol );
    }

    // Line start
    if( m_bObjSelected && rAttrs->GetItemState( XATTR_LINESTART ) == SfxItemState::DEFAULT )
    {
        m_xLbStartStyle->set_sensitive(false);
    }
    else if( rAttrs->GetItemState( XATTR_LINESTART ) != SfxItemState::INVALID )
    {
        // #86265# select entry using list and polygon, not string
        bool bSelected(false);
        const basegfx::B2DPolyPolygon& rItemPolygon = rAttrs->Get(XATTR_LINESTART).GetLineStartValue();

        for(tools::Long a(0);!bSelected &&  a < m_pLineEndList->Count(); a++)
        {
            const XLineEndEntry* pEntry = m_pLineEndList->GetLineEnd(a);
            const basegfx::B2DPolyPolygon& rEntryPolygon = pEntry->GetLineEnd();

            if(rItemPolygon == rEntryPolygon)
            {
                // select this entry
                m_xLbStartStyle->set_active(a + 1);
                bSelected = true;
            }
        }

        if(!bSelected)
            m_xLbStartStyle->set_active(0);
    }
    else
    {
        m_xLbStartStyle->set_active(-1);
    }

    // Line end
    if( m_bObjSelected && rAttrs->GetItemState( XATTR_LINEEND ) == SfxItemState::DEFAULT )
    {
        m_xLbEndStyle->set_sensitive(false);
    }
    else if( rAttrs->GetItemState( XATTR_LINEEND ) != SfxItemState::INVALID )
    {
        // #86265# select entry using list and polygon, not string
        bool bSelected(false);
        const basegfx::B2DPolyPolygon& rItemPolygon = rAttrs->Get(XATTR_LINEEND).GetLineEndValue();

        for(tools::Long a(0);!bSelected &&  a < m_pLineEndList->Count(); a++)
        {
            const XLineEndEntry* pEntry = m_pLineEndList->GetLineEnd(a);
            const basegfx::B2DPolyPolygon& rEntryPolygon = pEntry->GetLineEnd();

            if(rItemPolygon == rEntryPolygon)
            {
                // select this entry
                m_xLbEndStyle->set_active(a + 1);
                bSelected = true;
            }
        }

        if(!bSelected)
            m_xLbEndStyle->set_active(0);
    }
    else
    {
        m_xLbEndStyle->set_active(-1);
    }

    // Line start strength
    if( m_bObjSelected &&  rAttrs->GetItemState( XATTR_LINESTARTWIDTH ) == SfxItemState::DEFAULT )
    {
        m_xMtrStartWidth->set_sensitive(false);
    }
    else if( rAttrs->GetItemState( XATTR_LINESTARTWIDTH ) != SfxItemState::INVALID )
    {
        SetMetricValue( *m_xMtrStartWidth,
                        rAttrs->Get( XATTR_LINESTARTWIDTH ).GetValue(),
                        m_ePoolUnit );
    }
    else
        m_xMtrStartWidth->set_text( u""_ustr );

    // Line end strength
    if( m_bObjSelected && rAttrs->GetItemState( XATTR_LINEENDWIDTH ) == SfxItemState::DEFAULT )
    {
        m_xMtrEndWidth->set_sensitive(false);
    }
    else if( rAttrs->GetItemState( XATTR_LINEENDWIDTH ) != SfxItemState::INVALID )
    {
        SetMetricValue( *m_xMtrEndWidth,
                        rAttrs->Get( XATTR_LINEENDWIDTH ).GetValue(),
                        m_ePoolUnit );
    }
    else
        m_xMtrEndWidth->set_text(u""_ustr);

    // Centered line end (start)
    if( m_bObjSelected && rAttrs->GetItemState( XATTR_LINESTARTCENTER ) == SfxItemState::DEFAULT )
    {
        m_xTsbCenterStart->set_sensitive(false);
    }
    else if( rAttrs->GetItemState( XATTR_LINESTARTCENTER ) != SfxItemState::INVALID )
    {
        if( rAttrs->Get( XATTR_LINESTARTCENTER ).GetValue() )
            m_xTsbCenterStart->set_state(TRISTATE_TRUE);
        else
            m_xTsbCenterStart->set_state(TRISTATE_FALSE);
    }
    else
    {
        m_xTsbCenterStart->set_state(TRISTATE_INDET);
    }

    // Centered line end (end)
    if( m_bObjSelected && rAttrs->GetItemState( XATTR_LINEENDCENTER ) == SfxItemState::DEFAULT )
    {
        m_xTsbCenterEnd->set_sensitive(false);
    }
    else if( rAttrs->GetItemState( XATTR_LINEENDCENTER ) != SfxItemState::INVALID )
    {
        if( rAttrs->Get( XATTR_LINEENDCENTER ).GetValue() )
            m_xTsbCenterEnd->set_state(TRISTATE_TRUE);
        else
            m_xTsbCenterEnd->set_state(TRISTATE_FALSE);
    }
    else
    {
        m_xTsbCenterEnd->set_state(TRISTATE_INDET);
    }

    // Transparency
    if( rAttrs->GetItemState( XATTR_LINETRANSPARENCE ) != SfxItemState::INVALID )
    {
        sal_uInt16 nTransp = rAttrs->Get( XATTR_LINETRANSPARENCE ).GetValue();
        m_xMtrTransparent->set_value(nTransp, FieldUnit::PERCENT);
        ChangeTransparentHdl_Impl(*m_xMtrTransparent);
    }
    else
        m_xMtrTransparent->set_text( u""_ustr );

    if( !m_xLbStartStyle->get_sensitive()  &&
        !m_xLbEndStyle->get_sensitive()    &&
        !m_xMtrStartWidth->get_sensitive() &&
        !m_xMtrEndWidth->get_sensitive()   &&
        !m_xTsbCenterStart->get_sensitive()&&
        !m_xTsbCenterEnd->get_sensitive() )
    {
        m_xCbxSynchronize->set_sensitive(false);
        m_xFlLineEnds->set_sensitive(false);
    }

    // Synchronize
    // We get the value from the INI file now
    OUString aStr = GetUserData();
    m_xCbxSynchronize->set_active(aStr.toInt32() != 0);

    if(m_bObjSelected && SfxItemState::DEFAULT == rAttrs->GetItemState(XATTR_LINEJOINT))
    {
//         maFTEdgeStyle.set_sensitive(false);
        m_xLBEdgeStyle->set_sensitive(false);
    }
    else if(SfxItemState::INVALID != rAttrs->GetItemState(XATTR_LINEJOINT))
    {
        const css::drawing::LineJoint eLineJoint = rAttrs->Get(XATTR_LINEJOINT).GetValue();

        switch(eLineJoint)
        {
            case css::drawing::LineJoint::LineJoint_MAKE_FIXED_SIZE: // fallback to round, unused value
            case css::drawing::LineJoint_ROUND : m_xLBEdgeStyle->set_active(0); break;
            case css::drawing::LineJoint_NONE : m_xLBEdgeStyle->set_active(1); break;
            case css::drawing::LineJoint_MIDDLE : // fallback to mitre, unused value
            case css::drawing::LineJoint_MITER : m_xLBEdgeStyle->set_active(2); break;
            case css::drawing::LineJoint_BEVEL : m_xLBEdgeStyle->set_active(3); break;
        }
    }
    else
    {
        m_xLBEdgeStyle->set_active(-1);
    }

    // fdo#43209
    if(m_bObjSelected && SfxItemState::DEFAULT == rAttrs->GetItemState(XATTR_LINECAP))
    {
        m_xLBCapStyle->set_sensitive(false);
    }
    else if(SfxItemState::INVALID != rAttrs->GetItemState(XATTR_LINECAP))
    {
        const css::drawing::LineCap eLineCap(rAttrs->Get(XATTR_LINECAP).GetValue());

        switch(eLineCap)
        {
            case css::drawing::LineCap_ROUND: m_xLBCapStyle->set_active(1); break;
            case css::drawing::LineCap_SQUARE : m_xLBCapStyle->set_active(2); break;
            default /*css::drawing::LineCap_BUTT*/: m_xLBCapStyle->set_active(0); break;
        }
    }
    else
    {
        m_xLBCapStyle->set_active(-1);
    }

    // Save values
    m_xLbLineStyle->save_value();
    m_xMtrLineWidth->save_value();
    m_xLbColor->SaveValue();
    m_xLbStartStyle->save_value();
    m_xLbEndStyle->save_value();
    m_xMtrStartWidth->save_value();
    m_xMtrEndWidth->save_value();
    m_xTsbCenterStart->save_state();
    m_xTsbCenterEnd->save_state();
    m_xMtrTransparent->save_value();

    m_xLBEdgeStyle->save_value();

    // LineCaps
    m_xLBCapStyle->save_value();

    ClickInvisibleHdl_Impl();

    ChangePreviewHdl_Impl( nullptr );
}

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

IMPL_LINK_NOARG(SvxLineTabPage, ChangePreviewListBoxHdl_Impl, ColorListBox&, void)
{
    ChangePreviewHdl_Impl(nullptr);
}

IMPL_LINK(SvxLineTabPage, ChangePreviewModifyHdl_Impl, weld::MetricSpinButton&, rEdit, void)
{
    ChangePreviewHdl_Impl(&rEdit);
}

void SvxLineTabPage::ChangePreviewHdl_Impl(const weld::MetricSpinButton* pCntrl)
{
    if (pCntrl == m_xMtrLineWidth.get())
    {
        // Line width and start end width
        sal_Int32 nNewLineWidth = GetCoreValue( *m_xMtrLineWidth, m_ePoolUnit );
        if(m_nActLineWidth == -1)
        {
            // Don't initialize yet, get the start value
            const SfxPoolItem* pOld = GetOldItem( m_rXLSet, XATTR_LINEWIDTH );
            sal_Int32 nStartLineWidth = 0;
            if(pOld)
                nStartLineWidth = static_cast<const XLineWidthItem *>(pOld)->GetValue();
            m_nActLineWidth = nStartLineWidth;
        }

        if(m_nActLineWidth != nNewLineWidth)
        {
            // Adapt start/end width
            sal_Int32 nValAct = GetCoreValue( *m_xMtrStartWidth, m_ePoolUnit );
            sal_Int32 nValNew = nValAct + (((nNewLineWidth - m_nActLineWidth) * 15) / 10);
            if(nValNew < 0)
                nValNew = 0;
            SetMetricValue( *m_xMtrStartWidth, nValNew, m_ePoolUnit );

            nValAct = GetCoreValue( *m_xMtrEndWidth, m_ePoolUnit );
            nValNew = nValAct + (((nNewLineWidth - m_nActLineWidth) * 15) / 10);
            if(nValNew < 0)
                nValNew = 0;
            SetMetricValue( *m_xMtrEndWidth, nValNew, m_ePoolUnit );
        }

        // Remember current value
        m_nActLineWidth = nNewLineWidth;
    }

    FillXLSet_Impl();
    m_aCtlPreview.Invalidate();

    // Make transparency accessible accordingly
    if( m_xLbLineStyle->get_active() == 0 ) // invisible
    {
        m_xBoxTransparency->set_sensitive(false);
    }
    else
    {
        m_xBoxTransparency->set_sensitive(true);
    }

    const bool bHasLineStyle = m_xLbLineStyle->get_active() !=0;
    const bool bHasLineStart = m_xLbStartStyle->get_active() != 0;

    m_xBoxStart->set_sensitive(bHasLineStart && bHasLineStyle);

    const bool bHasLineEnd = m_xLbEndStyle->get_active() != 0;

    m_xBoxEnd->set_sensitive(bHasLineEnd && bHasLineStyle);
}

IMPL_LINK_NOARG(SvxLineTabPage, ChangeStartClickHdl_Impl, weld::Toggleable&, void)
{
    if (m_xCbxSynchronize->get_active())
        m_xTsbCenterEnd->set_state(m_xTsbCenterStart->get_state());
    ChangePreviewHdl_Impl(nullptr);
}

IMPL_LINK_NOARG(SvxLineTabPage, ChangeStartListBoxHdl_Impl, weld::ComboBox&, void)
{
    if (m_xCbxSynchronize->get_active())
        m_xLbEndStyle->set_active(m_xLbStartStyle->get_active());

    ChangePreviewHdl_Impl(nullptr);
}

IMPL_LINK_NOARG(SvxLineTabPage, ChangeStartModifyHdl_Impl, weld::MetricSpinButton&, void)
{
    if (m_xCbxSynchronize->get_active())
        m_xMtrEndWidth->set_value(m_xMtrStartWidth->get_value(FieldUnit::NONE), FieldUnit::NONE);

    ChangePreviewHdl_Impl(nullptr);
}

IMPL_LINK_NOARG(SvxLineTabPage, ChangeEdgeStyleHdl_Impl, weld::ComboBox&, void)
{
    ChangePreviewHdl_Impl( nullptr );
}

// fdo#43209
IMPL_LINK_NOARG(SvxLineTabPage, ChangeCapStyleHdl_Impl, weld::ComboBox&, void)
{
    ChangePreviewHdl_Impl( nullptr );
}

IMPL_LINK_NOARG(SvxLineTabPage, ClickInvisibleHdl_Impl, weld::ComboBox&, void)
{
    ClickInvisibleHdl_Impl();
}

void SvxLineTabPage::ClickInvisibleHdl_Impl()
{
    if( m_xLbLineStyle->get_active() == 0 ) // invisible
    {
        if(!m_bSymbols)
            m_xBoxColor->set_sensitive(false);

        m_xBoxWidth->set_sensitive(false);

        if( m_xFlLineEnds->get_sensitive() )
        {
            m_xBoxStart->set_sensitive(false);
            m_xBoxArrowStyles->set_sensitive(false);
            m_xGridEdgeCaps->set_sensitive(false);
        }
    }
    else
    {
        // set cap style associated to the line style
        sal_Int32 nPos = m_xLbLineStyle->get_active();
        if( nPos > 1 && m_pDashList->Count() > static_cast<tools::Long>( nPos - 2 ) )
        {
            css::drawing::DashStyle eStyle =
                m_pDashList->GetDash( nPos - 2 )->GetDash().GetDashStyle();
            if ( eStyle == drawing::DashStyle_RECT || eStyle == drawing::DashStyle_RECTRELATIVE)
                m_xLBCapStyle->set_active(0);
            else
                m_xLBCapStyle->set_active(1);
        }

        m_xBoxColor->set_sensitive(true);
        m_xBoxWidth->set_sensitive(true);

        if (m_xFlLineEnds->get_sensitive())
        {
            m_xBoxArrowStyles->set_sensitive(true);
            m_xGridEdgeCaps->set_sensitive(true);
        }
    }
    ChangePreviewHdl_Impl( nullptr );
}

IMPL_LINK_NOARG(SvxLineTabPage, ChangeEndClickHdl_Impl, weld::Toggleable&, void)
{
    if (m_xCbxSynchronize->get_active())
        m_xTsbCenterStart->set_state(m_xTsbCenterEnd->get_state());

    ChangePreviewHdl_Impl(nullptr);
}

IMPL_LINK_NOARG(SvxLineTabPage, ChangeEndListBoxHdl_Impl, weld::ComboBox&, void)
{
    if (m_xCbxSynchronize->get_active())
        m_xLbStartStyle->set_active(m_xLbEndStyle->get_active());

    ChangePreviewHdl_Impl(nullptr);
}

IMPL_LINK_NOARG(SvxLineTabPage, ChangeEndModifyHdl_Impl, weld::MetricSpinButton&, void)
{
    if (m_xCbxSynchronize->get_active())
        m_xMtrStartWidth->set_value(m_xMtrEndWidth->get_value(FieldUnit::NONE), FieldUnit::NONE);

    ChangePreviewHdl_Impl(nullptr);
}

IMPL_LINK_NOARG(SvxLineTabPage, ChangeTransparentHdl_Impl, weld::MetricSpinButton&, void)
{
    sal_uInt16 nVal = m_xMtrTransparent->get_value(FieldUnit::PERCENT);

    m_rXLSet.Put(XLineTransparenceItem(nVal));

    FillXLSet_Impl();

    m_aCtlPreview.Invalidate();
}

void SvxLineTabPage::FillUserData()
{
    // Write the synched value to the INI file
    OUString aStrUserData = OUString::boolean(m_xCbxSynchronize->get_active());
    SetUserData( aStrUserData );
}

// #58425# Symbols on a list (e.g. StarChart)
void SvxLineTabPage::PopulateMenus()
{
    ScopedVclPtrInstance< VirtualDevice > pVD;

    // Initialize popup
    if (!m_xGalleryMenu)
    {
        m_xGalleryMenu = m_xBuilder->weld_menu(u"gallerysubmenu"_ustr);
        weld::WaitObject aWait(GetFrameWeld());
        // Get gallery entries
        GalleryExplorer::FillObjList(GALLERY_THEME_BULLETS, m_aGrfNames);

        sal_uInt32 i = 0;
        for (auto const& grfName : m_aGrfNames)
        {
            const OUString *pUIName = &grfName;

            // Convert URL encodings to UI characters (e.g. %20 for spaces)
            OUString aPhysicalName;
            if (osl::FileBase::getSystemPathFromFileURL(grfName, aPhysicalName)
                == osl::FileBase::E_None)
            {
                pUIName = &aPhysicalName;
            }

            SvxBmpItemInfo* pInfo = new SvxBmpItemInfo;
            pInfo->pBrushItem.reset(new SvxBrushItem(grfName, u""_ustr, GPOS_AREA, SID_ATTR_BRUSH));
            pInfo->sItemId = "gallery" + OUString::number(i);
            m_aGalleryBrushItems.emplace_back(pInfo);
            const Graphic* pGraphic = pInfo->pBrushItem->GetGraphic();

            if(pGraphic)
            {
                BitmapEx aBitmap(pGraphic->GetBitmapEx());
                Size aSize(aBitmap.GetSizePixel());
                if(aSize.Width()  > MAX_BMP_WIDTH || aSize.Height() > MAX_BMP_HEIGHT)
                {
                    bool bWidth = aSize.Width() > aSize.Height();
                    double nScale = bWidth ?
                                        double(MAX_BMP_WIDTH) / static_cast<double>(aSize.Width()):
                                        double(MAX_BMP_HEIGHT) / static_cast<double>(aSize.Height());
                    aBitmap.Scale(nScale, nScale);

                }
                pVD->SetOutputSizePixel(aBitmap.GetSizePixel());
                pVD->DrawBitmapEx(Point(), aBitmap);
                m_xGalleryMenu->append(pInfo->sItemId, *pUIName, *pVD);
            }
            else
            {
                m_xGalleryMenu->append(pInfo->sItemId, *pUIName);
            }
            ++i;
        }

        if (m_aGrfNames.empty())
            m_xSymbolMB->set_item_sensitive(u"gallery"_ustr, false);
    }

    if (m_xSymbolsMenu || !m_pSymbolList)
        return;

    m_xSymbolsMenu = m_xBuilder->weld_menu(u"symbolssubmenu"_ustr);
    ScopedVclPtrInstance< VirtualDevice > pVDev;
    pVDev->SetMapMode(MapMode(MapUnit::Map100thMM));
    std::unique_ptr<SdrModel> pModel(
        new SdrModel(nullptr, nullptr, true));
    // Page
    rtl::Reference<SdrPage> pPage = new SdrPage( *pModel, false );
    pPage->SetSize(Size(1000,1000));
    pModel->InsertPage( pPage.get(), 0 );
    {
        // 3D View
        SdrView aView( *pModel, pVDev );
        aView.hideMarkHandles();
        aView.ShowSdrPage(pPage.get());

        // Generate invisible square to give all symbols a
        // bitmap size, which is independent from specific glyph
        rtl::Reference<SdrObject> pInvisibleSquare=m_pSymbolList->GetObj(0);

        // directly clone to target SdrModel
        pInvisibleSquare = pInvisibleSquare->CloneSdrObject(*pModel);

        pPage->NbcInsertObject(pInvisibleSquare.get());
        pInvisibleSquare->SetMergedItem(XFillTransparenceItem(100));
        pInvisibleSquare->SetMergedItem(XLineTransparenceItem(100));

        for(size_t i=0; i < m_pSymbolList->GetObjCount(); ++i)
        {
            rtl::Reference<SdrObject> pObj=m_pSymbolList->GetObj(i);
            assert(pObj);

            // directly clone to target SdrModel
            pObj = pObj->CloneSdrObject(*pModel);

            m_aGrfNames.emplace_back("");
            pPage->NbcInsertObject(pObj.get());
            if(m_xSymbolAttr)
            {
                pObj->SetMergedItemSet(*m_xSymbolAttr);
            }
            else
            {
                pObj->SetMergedItemSet(m_rOutAttrs);
            }
            aView.MarkAll();
            BitmapEx aBitmapEx(aView.GetMarkedObjBitmapEx());
            GDIMetaFile aMeta(aView.GetMarkedObjMetaFile());
            aView.UnmarkAll();
            pPage->RemoveObject(1);
            pObj.clear();

            SvxBmpItemInfo* pInfo = new SvxBmpItemInfo;
            pInfo->pBrushItem.reset(new SvxBrushItem(Graphic(aMeta), GPOS_AREA, SID_ATTR_BRUSH));
            pInfo->sItemId = "symbol" + OUString::number(i);
            m_aSymbolBrushItems.emplace_back(pInfo);

            Size aSize(aBitmapEx.GetSizePixel());
            if(aSize.Width() > MAX_BMP_WIDTH || aSize.Height() > MAX_BMP_HEIGHT)
            {
                bool bWidth = aSize.Width() > aSize.Height();
                double nScale = bWidth ?
                                    double(MAX_BMP_WIDTH) / static_cast<double>(aSize.Width()):
                                    double(MAX_BMP_HEIGHT) / static_cast<double>(aSize.Height());
                aBitmapEx.Scale(nScale, nScale);
            }
            pVD->SetOutputSizePixel(aBitmapEx.GetSizePixel());
            pVD->DrawBitmapEx(Point(), aBitmapEx);
            m_xSymbolsMenu->append(pInfo->sItemId, u""_ustr, *pVD);
        }
        pPage->RemoveObject(0);
        pInvisibleSquare.clear();

        if (m_aGrfNames.empty())
            m_xSymbolMB->set_item_sensitive(u"symbols"_ustr, false);
    }
}

// #58425# Symbols on a list (e.g. StarChart)
// Handler for menu button
IMPL_LINK(SvxLineTabPage, GraphicHdl_Impl, const OUString&, rIdent, void)
{
    const Graphic* pGraphic = nullptr;
    Graphic aGraphic;
    bool bResetSize = false;
    bool bEnable = true;
    tools::Long nPreviousSymbolType = m_nSymbolType;

    std::u16string_view sNumber;
    if (rIdent.startsWith("gallery", &sNumber))
    {
        SvxBmpItemInfo* pInfo = m_aGalleryBrushItems[o3tl::toUInt32(sNumber)].get();
        pGraphic = pInfo->pBrushItem->GetGraphic();
        m_nSymbolType = SVX_SYMBOLTYPE_BRUSHITEM;
    }
    else if (rIdent.startsWith("symbol", &sNumber))
    {
        m_nSymbolType = o3tl::toUInt32(sNumber);
        SvxBmpItemInfo* pInfo = m_aSymbolBrushItems[m_nSymbolType].get();
        pGraphic = pInfo->pBrushItem->GetGraphic();
    }
    else if (rIdent == "automatic")
    {
        pGraphic=&m_aAutoSymbolGraphic;
        m_aAutoSymbolGraphic.SetPrefSize( Size(253,253) );
        m_nSymbolType=SVX_SYMBOLTYPE_AUTO;
    }
    else if (rIdent == "none")
    {
        m_nSymbolType=SVX_SYMBOLTYPE_NONE;
        pGraphic=nullptr;
        bEnable = false;
    }
    else if (rIdent == "file")
    {
        SvxOpenGraphicDialog aGrfDlg(CuiResId(RID_CUISTR_EDIT_GRAPHIC), GetFrameWeld());
        aGrfDlg.EnableLink(false);
        aGrfDlg.AsLink(false);
        if( !aGrfDlg.Execute() )
        {
            // Remember selected filters
            if( !aGrfDlg.GetGraphic(aGraphic) )
            {
                m_nSymbolType=SVX_SYMBOLTYPE_BRUSHITEM;
                pGraphic = &aGraphic;
                bResetSize = true;
            }
        }
        if( !pGraphic )
            return;
    }

    if (pGraphic)
    {
        Size aSize = SvxNumberFormat::GetGraphicSizeMM100(pGraphic);
        aSize = OutputDevice::LogicToLogic(aSize, MapMode(MapUnit::Map100thMM), MapMode(m_ePoolUnit));
        m_aSymbolGraphic=*pGraphic;
        if( bResetSize )
        {
            m_aSymbolSize=aSize;
        }
        else if( nPreviousSymbolType == SVX_SYMBOLTYPE_BRUSHITEM )
        {   //#i31097# Data Point Symbol size changes when a different symbol is chosen(maoyg)
            if( m_aSymbolSize.Width() != m_aSymbolSize.Height() )
            {
                aSize.setWidth( static_cast<tools::Long>( m_aSymbolSize.Width() + m_aSymbolSize.Height() )/2 );
                aSize.setHeight( static_cast<tools::Long>( m_aSymbolSize.Width() + m_aSymbolSize.Height() )/2 );
                m_aSymbolSize = aSize;
            }
        }
        m_aCtlPreview.SetSymbol(&m_aSymbolGraphic,m_aSymbolSize);
    }
    else
    {
        m_aSymbolGraphic=Graphic();
        m_aCtlPreview.SetSymbol(nullptr,m_aSymbolSize);
        bEnable = false;
    }
    m_aSymbolLastSize=m_aSymbolSize;
    SetMetricValue(*m_xSymbolWidthMF,  m_aSymbolSize.Width(), m_ePoolUnit);
    SetMetricValue(*m_xSymbolHeightMF, m_aSymbolSize.Height(), m_ePoolUnit);

    m_xGridIconSize->set_sensitive(bEnable);
    m_aCtlPreview.Invalidate();
}

IMPL_LINK( SvxLineTabPage, SizeHdl_Impl, weld::MetricSpinButton&, rField, void)
{
    m_bNewSize = true;
    bool bWidth = &rField == m_xSymbolWidthMF.get();
    m_bLastWidthModified = bWidth;
    bool bRatio = m_xSymbolRatioCB->get_active();
    tools::Long nWidthVal = static_cast<tools::Long>(m_xSymbolWidthMF->denormalize(m_xSymbolWidthMF->get_value(FieldUnit::MM_100TH)));
    tools::Long nHeightVal= static_cast<tools::Long>(m_xSymbolHeightMF->denormalize(m_xSymbolHeightMF->get_value(FieldUnit::MM_100TH)));
    nWidthVal = OutputDevice::LogicToLogic(nWidthVal,MapUnit::Map100thMM, m_ePoolUnit );
    nHeightVal = OutputDevice::LogicToLogic(nHeightVal,MapUnit::Map100thMM, m_ePoolUnit);
    m_aSymbolSize = Size(nWidthVal,nHeightVal);
    double fSizeRatio = double(1);

    if(bRatio)
    {
        if (m_aSymbolLastSize.Height() && m_aSymbolLastSize.Width())
            fSizeRatio = static_cast<double>(m_aSymbolLastSize.Width()) / m_aSymbolLastSize.Height();
    }

    if (bWidth)
    {
        tools::Long nDelta = nWidthVal - m_aSymbolLastSize.Width();
        m_aSymbolSize.setWidth( nWidthVal );
        if (bRatio)
        {
            m_aSymbolSize.setHeight( m_aSymbolLastSize.Height() + static_cast<tools::Long>(static_cast<double>(nDelta) / fSizeRatio) );
            m_aSymbolSize.setHeight( OutputDevice::LogicToLogic( m_aSymbolSize.Height(), m_ePoolUnit, MapUnit::Map100thMM ) );
//TODO            m_xSymbolHeightMF->SetUserValue(m_xSymbolHeightMF->normalize(m_aSymbolSize.Height()), FieldUnit::MM_100TH);
            m_xSymbolHeightMF->set_value(m_xSymbolHeightMF->normalize(m_aSymbolSize.Height()), FieldUnit::MM_100TH);
        }
    }
    else
    {
        tools::Long nDelta = nHeightVal - m_aSymbolLastSize.Height();
        m_aSymbolSize.setHeight( nHeightVal );
        if (bRatio)
        {
            m_aSymbolSize.setWidth( m_aSymbolLastSize.Width() + static_cast<tools::Long>(static_cast<double>(nDelta) * fSizeRatio) );
            m_aSymbolSize.setWidth( OutputDevice::LogicToLogic( m_aSymbolSize.Width(), m_ePoolUnit, MapUnit::Map100thMM ) );
//TODO            m_xSymbolWidthMF->SetUserValue(m_xSymbolWidthMF->normalize(m_aSymbolSize.Width()), FieldUnit::MM_100TH);
            m_xSymbolWidthMF->set_value(m_xSymbolWidthMF->normalize(m_aSymbolSize.Width()), FieldUnit::MM_100TH);
        }
    }
    m_aCtlPreview.ResizeSymbol(m_aSymbolSize);
    m_aSymbolLastSize=m_aSymbolSize;
}

IMPL_LINK(SvxLineTabPage, RatioHdl_Impl, weld::Toggleable&, rBox, void)
{
    if (rBox.get_active())
    {
        if (m_bLastWidthModified)
            SizeHdl_Impl(*m_xSymbolWidthMF);
        else
            SizeHdl_Impl(*m_xSymbolHeightMF);
    }
}

void SvxLineTabPage::PageCreated(const SfxAllItemSet& aSet)
{
    const SvxDashListItem* pDashListItem = aSet.GetItem<SvxDashListItem>(SID_DASH_LIST, false);
    const SvxLineEndListItem* pLineEndListItem = aSet.GetItem<SvxLineEndListItem>(SID_LINEEND_LIST, false);
    const SfxUInt16Item* pPageTypeItem = aSet.GetItem<SfxUInt16Item>(SID_PAGE_TYPE, false);
    const SfxUInt16Item* pDlgTypeItem = aSet.GetItem<SfxUInt16Item>(SID_DLG_TYPE, false);
    const OfaPtrItem* pSdrObjListItem = aSet.GetItem<OfaPtrItem>(SID_OBJECT_LIST, false);
    const SfxTabDialogItem* pSymbolAttrItem = aSet.GetItem<SfxTabDialogItem>(SID_ATTR_SET, false);
    const SvxGraphicItem* pGraphicItem = aSet.GetItem<SvxGraphicItem>(SID_GRAPHIC, false);

    if (pDashListItem)
        SetDashList(pDashListItem->GetDashList());
    if (pLineEndListItem)
        SetLineEndList(pLineEndListItem->GetLineEndList());
    if (pPageTypeItem)
        SetPageType(static_cast<PageType>(pPageTypeItem->GetValue()));
    if (pDlgTypeItem)
        SetDlgType(pDlgTypeItem->GetValue());

    if(pSdrObjListItem) //symbols
    {
        ShowSymbolControls(true);
        m_pSymbolList = static_cast<SdrObjList*>(pSdrObjListItem->GetValue());
        if (pSymbolAttrItem)
            m_xSymbolAttr.reset(new SfxItemSet(pSymbolAttrItem->GetItemSet()));
        if(pGraphicItem)
            m_aAutoSymbolGraphic = pGraphicItem->GetGraphic();
    }

    Construct();
}

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