/* -*- 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 <column.hxx>

#include <hintids.hxx>
#include <svx/dialmgr.hxx>
#include <svx/strings.hrc>
#include <sfx2/htmlmode.hxx>
#include <svx/colorbox.hxx>
#include <editeng/borderline.hxx>
#include <editeng/boxitem.hxx>
#include <editeng/lrspitem.hxx>
#include <editeng/sizeitem.hxx>
#include <editeng/frmdiritem.hxx>
#include <editeng/ulspitem.hxx>
#include <svl/ctloptions.hxx>
#include <svl/itemset.hxx>
#include <sfx2/dispatch.hxx>
#include <sfx2/viewfrm.hxx>
#include <vcl/event.hxx>
#include <vcl/fieldvalues.hxx>
#include <vcl/settings.hxx>
#include <vcl/svapp.hxx>

#include <swmodule.hxx>

#include <swtypes.hxx>
#include <wrtsh.hxx>
#include <view.hxx>
#include <docsh.hxx>
#include <uitool.hxx>
#include <cmdid.h>
#include <viewopt.hxx>
#include <fmtclbl.hxx>
#include <fmtfsize.hxx>
#include <frmatr.hxx>
#include <colmgr.hxx>
#include <prcntfld.hxx>
#include <strings.hrc>
#include <section.hxx>
#include <pagedesc.hxx>

//to match associated data in ColumnPage.ui
#define LISTBOX_SELECTION       0
#define LISTBOX_SECTION         1
#define LISTBOX_SECTIONS        2
#define LISTBOX_PAGE            3
#define LISTBOX_FRAME           4

using namespace ::com::sun::star;

#define FRAME_FORMAT_WIDTH 1000

// static data
const sal_uInt16 nVisCols = 3;

static bool IsMarkInSameSection( SwWrtShell& rWrtSh, const SwSection* pSect )
{
    rWrtSh.SwapPam();
    bool bRet = pSect == rWrtSh.GetCurrSection();
    rWrtSh.SwapPam();
    return bRet;
}

SwColumnDlg::SwColumnDlg(weld::Window* pParent, SwWrtShell& rSh)
    : SfxDialogController(pParent, u"modules/swriter/ui/columndialog.ui"_ustr, u"ColumnDialog"_ustr)
    , m_rWrtShell(rSh)
    , m_pFrameSet(nullptr)
    , m_nOldSelection(0)
    , m_nSelectionWidth(0)
    , m_bPageChanged(false)
    , m_bSectionChanged(false)
    , m_bSelSectionChanged(false)
    , m_bFrameChanged(false)
    , m_xContentArea(m_xBuilder->weld_container(u"content"_ustr))
    , m_xOkButton(m_xBuilder->weld_button(u"ok"_ustr))
{
    SwRect aRect;
    m_rWrtShell.CalcBoundRect(aRect, RndStdIds::FLY_AS_CHAR);

    m_nSelectionWidth = aRect.Width();

    SfxItemSet* pColPgSet = nullptr;
    static const auto aSectIds = svl::Items<RES_FRM_SIZE, RES_FRM_SIZE,
                      RES_COL, RES_COL,
                      RES_COLUMNBALANCE, RES_FRAMEDIR>;

    const SwSection* pCurrSection = m_rWrtShell.GetCurrSection();
    const sal_uInt16 nFullSectCnt = m_rWrtShell.GetFullSelectedSectionCount();
    if( pCurrSection && ( !m_rWrtShell.HasSelection() || 0 != nFullSectCnt ))
    {
        m_nSelectionWidth = rSh.GetSectionWidth(*pCurrSection->GetFormat());
        if ( !m_nSelectionWidth )
            m_nSelectionWidth = USHRT_MAX;
        m_pSectionSet.reset( new SfxItemSet( m_rWrtShell.GetAttrPool(), aSectIds ) );
        m_pSectionSet->Put( pCurrSection->GetFormat()->GetAttrSet() );
        pColPgSet = m_pSectionSet.get();
    }

    if( m_rWrtShell.HasSelection() && m_rWrtShell.IsInsRegionAvailable() &&
        ( !pCurrSection || ( 1 != nFullSectCnt &&
            IsMarkInSameSection( m_rWrtShell, pCurrSection ) )))
    {
        m_pSelectionSet.reset( new SfxItemSet( m_rWrtShell.GetAttrPool(), aSectIds ) );
        pColPgSet = m_pSelectionSet.get();
    }

    if( m_rWrtShell.GetFlyFrameFormat() )
    {
        const SwFrameFormat* pFormat = rSh.GetFlyFrameFormat() ;
        m_pFrameSet = new SfxItemSet(m_rWrtShell.GetAttrPool(), aSectIds );
        m_pFrameSet->Put(pFormat->GetFrameSize());
        m_pFrameSet->Put(pFormat->GetCol());
        pColPgSet = m_pFrameSet;
    }

    const SwPageDesc* pPageDesc = m_rWrtShell.GetSelectedPageDescs();
    if( pPageDesc )
    {
        m_pPageSet = std::make_unique<SfxItemSetFixed<
                RES_FRM_SIZE, RES_FRM_SIZE,
                RES_LR_SPACE, RES_LR_SPACE,
                RES_COL, RES_COL>>(m_rWrtShell.GetAttrPool());

        const SwFrameFormat &rFormat = pPageDesc->GetMaster();
        m_nPageWidth = rFormat.GetFrameSize().GetSize().Width();

        const SvxLRSpaceItem& rLRSpace = rFormat.GetLRSpace();
        const SvxBoxItem& rBox = rFormat.GetBox();
        m_nPageWidth
            -= rLRSpace.ResolveLeft({}) + rLRSpace.ResolveRight({}) + rBox.GetSmallestDistance();

        m_pPageSet->Put(rFormat.GetCol());
        m_pPageSet->Put(rFormat.GetLRSpace());
        pColPgSet = m_pPageSet.get();
    }

    assert(pColPgSet);

    // create TabPage
    m_xTabPage = std::make_unique<SwColumnPage>(m_xContentArea.get(), this, *pColPgSet);
    m_xTabPage->GetApplyLabel()->show();
    weld::ComboBox* pApplyToLB = m_xTabPage->GetApplyComboBox();
    pApplyToLB->show();

    if (pCurrSection && (!m_rWrtShell.HasSelection() || 0 != nFullSectCnt))
    {
        pApplyToLB->remove_id(1 >= nFullSectCnt ? OUString::number(LISTBOX_SECTIONS) : OUString::number(LISTBOX_SECTION));
    }
    else
    {
        pApplyToLB->remove_id(OUString::number(LISTBOX_SECTION));
        pApplyToLB->remove_id(OUString::number(LISTBOX_SECTIONS));
    }

    if (!( m_rWrtShell.HasSelection() && m_rWrtShell.IsInsRegionAvailable() &&
        ( !pCurrSection || ( 1 != nFullSectCnt &&
            IsMarkInSameSection( m_rWrtShell, pCurrSection ) ))))
        pApplyToLB->remove_id(OUString::number(LISTBOX_SELECTION));

    if (!m_rWrtShell.GetFlyFrameFormat())
        pApplyToLB->remove_id(OUString::number(LISTBOX_FRAME));

    const int nPagePos = pApplyToLB->find_id(OUString::number(LISTBOX_PAGE));
    if (m_pPageSet && pPageDesc)
    {
        const OUString sPageStr = pApplyToLB->get_text(nPagePos) + pPageDesc->GetName().toString();
        pApplyToLB->remove(nPagePos);
        OUString sId(OUString::number(LISTBOX_PAGE));
        pApplyToLB->insert(nPagePos, sPageStr, &sId, nullptr, nullptr);
    }
    else
        pApplyToLB->remove( nPagePos );

    pApplyToLB->set_active(0);
    ObjectHdl(nullptr);

    pApplyToLB->connect_changed(LINK(this, SwColumnDlg, ObjectListBoxHdl));
    m_xOkButton->connect_clicked(LINK(this, SwColumnDlg, OkHdl));
    //#i80458# if no columns can be set then disable OK
    if (!pApplyToLB->get_count())
        m_xOkButton->set_sensitive(false);
    //#i97810# set focus to the TabPage
    m_xTabPage->ActivateColumnControl();
}

SwColumnDlg::~SwColumnDlg()
{
    m_xTabPage.reset();
}

IMPL_LINK(SwColumnDlg, ObjectListBoxHdl, weld::ComboBox&, rBox, void)
{
    ObjectHdl(&rBox);
}

void SwColumnDlg::ObjectHdl(const weld::ComboBox* pBox)
{
    SfxItemSet* pSet = EvalCurrentSelection();

    if (pBox)
    {
        m_xTabPage->FillItemSet(pSet);
    }
    weld::ComboBox* pApplyToLB = m_xTabPage->GetApplyComboBox();
    m_nOldSelection = pApplyToLB->get_active_id().toInt32();
    tools::Long nWidth = m_nSelectionWidth;
    switch(m_nOldSelection)
    {
        case LISTBOX_SELECTION  :
            pSet = m_pSelectionSet.get();
            if( m_pSelectionSet )
                pSet->Put(SwFormatFrameSize(SwFrameSize::Variable, nWidth, nWidth));
        break;
        case LISTBOX_SECTION    :
        case LISTBOX_SECTIONS   :
            pSet = m_pSectionSet.get();
            pSet->Put(SwFormatFrameSize(SwFrameSize::Variable, nWidth, nWidth));
        break;
        case LISTBOX_PAGE       :
            nWidth = m_nPageWidth;
            pSet = m_pPageSet.get();
            pSet->Put(SwFormatFrameSize(SwFrameSize::Variable, nWidth, nWidth));
        break;
        case LISTBOX_FRAME:
            pSet = m_pFrameSet;
        break;
    }

    bool bIsSection = pSet == m_pSectionSet.get() || pSet == m_pSelectionSet.get();
    m_xTabPage->ShowBalance(bIsSection);
    m_xTabPage->SetInSection(bIsSection);
    m_xTabPage->SetFrameMode(true);
    m_xTabPage->SetPageWidth(nWidth);
    if( pSet )
        m_xTabPage->Reset(pSet);
}

IMPL_LINK_NOARG(SwColumnDlg, OkHdl, weld::Button&, void)
{
    // evaluate current selection
    SfxItemSet* pSet = EvalCurrentSelection();
    m_xTabPage->FillItemSet(pSet);

    if(m_pSelectionSet && SfxItemState::SET == m_pSelectionSet->GetItemState(RES_COL))
    {
        //insert region with columns
        const SwFormatCol& rColItem = m_pSelectionSet->Get(RES_COL);
        //only if there actually are columns!
        if(rColItem.GetNumCols() > 1)
            m_rWrtShell.GetView().GetViewFrame().GetDispatcher()->Execute(
                FN_INSERT_REGION, SfxCallMode::ASYNCHRON, *m_pSelectionSet );
    }

    if(m_pSectionSet && m_pSectionSet->Count() && m_bSectionChanged )
    {
        const SwSection* pCurrSection = m_rWrtShell.GetCurrSection();
        const SwSectionFormat* pFormat = pCurrSection->GetFormat();
        const size_t nNewPos = m_rWrtShell.GetSectionFormatPos( *pFormat );
        SwSectionData aData(*pCurrSection);
        m_rWrtShell.UpdateSection( nNewPos, aData, m_pSectionSet.get() );
    }

    if(m_pSectionSet && m_pSectionSet->Count() && m_bSelSectionChanged )
    {
        m_rWrtShell.SetSectionAttr( *m_pSectionSet );
    }

    if(m_pPageSet && SfxItemState::SET == m_pPageSet->GetItemState(RES_COL) && m_bPageChanged)
    {
        // determine current PageDescriptor and fill the Set with it
        const size_t nCurIdx = m_rWrtShell.GetCurPageDesc();
        SwPageDesc aPageDesc(m_rWrtShell.GetPageDesc(nCurIdx));
        SwFrameFormat &rFormat = aPageDesc.GetMaster();
        rFormat.SetFormatAttr(m_pPageSet->Get(RES_COL));
        m_rWrtShell.ChgPageDesc(nCurIdx, aPageDesc);
    }
    if(m_pFrameSet && SfxItemState::SET == m_pFrameSet->GetItemState(RES_COL) && m_bFrameChanged)
    {
        SfxItemSetFixed<RES_COL, RES_COL> aTmp(*m_pFrameSet->GetPool());
        aTmp.Put(*m_pFrameSet);
        m_rWrtShell.StartAction();
        m_rWrtShell.Push();
        m_rWrtShell.SetFlyFrameAttr( aTmp );
        // undo the frame selection again
        if(m_rWrtShell.IsFrameSelected())
        {
            m_rWrtShell.UnSelectFrame();
            m_rWrtShell.LeaveSelFrameMode();
        }
        m_rWrtShell.Pop();
        m_rWrtShell.EndAction();
    }
    m_xDialog->response(RET_OK);
}

SfxItemSet* SwColumnDlg::EvalCurrentSelection()
{
    SfxItemSet* pSet = nullptr;

    switch(m_nOldSelection)
    {
        case LISTBOX_SELECTION  :
            pSet = m_pSelectionSet.get();
        break;
        case LISTBOX_SECTION    :
            pSet = m_pSectionSet.get();
            m_bSectionChanged = true;
        break;
        case LISTBOX_SECTIONS   :
            pSet = m_pSectionSet.get();
            m_bSelSectionChanged = true;
        break;
        case LISTBOX_PAGE       :
            pSet = m_pPageSet.get();
            m_bPageChanged = true;
        break;
        case LISTBOX_FRAME:
            pSet = m_pFrameSet;
            m_bFrameChanged = true;
        break;
    }

    return pSet;
}

static
sal_uInt16 GetMaxWidth( SwColMgr const * pColMgr, sal_uInt16 nCols )
{
    sal_uInt16 nMax = pColMgr->GetActualSize();
    if( --nCols )
        nMax -= pColMgr->GetGutterWidth() * nCols;
    return nMax;
}

const WhichRangesContainer SwColumnPage::s_aPageRg(svl::Items<RES_COL, RES_COL>);

void SwColumnPage::ResetColWidth()
{
    if( m_nCols )
    {
        const sal_uInt16 nWidth = GetMaxWidth( m_xColMgr.get(), m_nCols ) / m_nCols;

        for(sal_uInt16 i = 0; i < m_nCols; ++i)
            m_nColWidth[i] = static_cast<tools::Long>(nWidth);
    }

}

constexpr sal_uInt16 g_nMinWidth(MINLAY);

// Now as TabPage
SwColumnPage::SwColumnPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rSet)
    : SfxTabPage(pPage, pController, u"modules/swriter/ui/columnpage.ui"_ustr, u"ColumnPage"_ustr, &rSet)
    , m_nFirstVis(0)
    , m_nCols(0)
    , m_pModifiedField(nullptr)
    , m_bFormat(false)
    , m_bFrame(false)
    , m_bHtmlMode(false)
    , m_bLockUpdate(false)
    , m_xCLNrEdt(m_xBuilder->weld_spin_button(u"colsnf"_ustr))
    , m_xBalanceColsCB(m_xBuilder->weld_check_button(u"balance"_ustr))
    , m_xBtnBack(m_xBuilder->weld_button(u"back"_ustr))
    , m_xLbl1(m_xBuilder->weld_label(u"1"_ustr))
    , m_xLbl2(m_xBuilder->weld_label(u"2"_ustr))
    , m_xLbl3(m_xBuilder->weld_label(u"3"_ustr))
    , m_xBtnNext(m_xBuilder->weld_button(u"next"_ustr))
    , m_xAutoWidthBox(m_xBuilder->weld_check_button(u"autowidth"_ustr))
    , m_xLineTypeLbl(m_xBuilder->weld_label(u"linestyleft"_ustr))
    , m_xLineWidthLbl(m_xBuilder->weld_label(u"linewidthft"_ustr))
    , m_xLineWidthEdit(m_xBuilder->weld_metric_spin_button(u"linewidthmf"_ustr, FieldUnit::POINT))
    , m_xLineColorLbl(m_xBuilder->weld_label(u"linecolorft"_ustr))
    , m_xLineHeightLbl(m_xBuilder->weld_label(u"lineheightft"_ustr))
    , m_xLineHeightEdit(m_xBuilder->weld_metric_spin_button(u"lineheightmf"_ustr, FieldUnit::PERCENT))
    , m_xLinePosLbl(m_xBuilder->weld_label(u"lineposft"_ustr))
    , m_xLinePosDLB(m_xBuilder->weld_combo_box(u"lineposlb"_ustr))
    , m_xTextDirectionFT(m_xBuilder->weld_label(u"textdirectionft"_ustr))
    , m_xTextDirectionLB(new svx::FrameDirectionListBox(m_xBuilder->weld_combo_box(u"textdirectionlb"_ustr)))
    , m_xLineColorDLB(new ColorListBox(m_xBuilder->weld_menu_button(u"colorlb"_ustr),
                [this]{ return GetFrameWeld(); }))
    , m_xLineTypeDLB(new SvtLineListBox(m_xBuilder->weld_menu_button(u"linestylelb"_ustr)))
    , m_xEd1(new SwPercentField(m_xBuilder->weld_metric_spin_button(u"width1mf"_ustr, FieldUnit::CM)))
    , m_xEd2(new SwPercentField(m_xBuilder->weld_metric_spin_button(u"width2mf"_ustr, FieldUnit::CM)))
    , m_xEd3(new SwPercentField(m_xBuilder->weld_metric_spin_button(u"width3mf"_ustr, FieldUnit::CM)))
    , m_xDistEd1(new SwPercentField(m_xBuilder->weld_metric_spin_button(u"spacing1mf"_ustr, FieldUnit::CM)))
    , m_xDistEd2(new SwPercentField(m_xBuilder->weld_metric_spin_button(u"spacing2mf"_ustr, FieldUnit::CM)))
    , m_xDefaultVS(new weld::CustomWeld(*m_xBuilder, u"valueset"_ustr, m_aDefaultVS))
    , m_xPgeExampleWN(new weld::CustomWeld(*m_xBuilder, u"pageexample"_ustr, m_aPgeExampleWN))
    , m_xFrameExampleWN(new weld::CustomWeld(*m_xBuilder, u"frameexample"_ustr, m_aFrameExampleWN))
    , m_xApplyToFT(m_xBuilder->weld_label(u"applytoft"_ustr))
    , m_xApplyToLB(m_xBuilder->weld_combo_box(u"applytolb"_ustr))
{
    connectPercentField(*m_xEd1);
    connectPercentField(*m_xEd2);
    connectPercentField(*m_xEd3);
    connectPercentField(*m_xDistEd1);
    connectPercentField(*m_xDistEd2);

    m_xTextDirectionLB->append(SvxFrameDirection::Horizontal_LR_TB, SvxResId(RID_SVXSTR_FRAMEDIR_LTR));
    m_xTextDirectionLB->append(SvxFrameDirection::Horizontal_RL_TB, SvxResId(RID_SVXSTR_FRAMEDIR_RTL));
    m_xTextDirectionLB->append(SvxFrameDirection::Environment, SvxResId(RID_SVXSTR_FRAMEDIR_SUPER));

    SetExchangeSupport();

    m_aDefaultVS.SetColCount(5);

    for (int i = 0; i < 5; ++i)
    //Set accessible name one by one
    {
        OUString aItemText;
        switch( i )
        {
            case 0:
                aItemText =  SwResId( STR_COLUMN_VALUESET_ITEM0 ) ;
                break;
            case 1:
                aItemText =  SwResId( STR_COLUMN_VALUESET_ITEM1 ) ;
                break;
            case 2:
                aItemText =  SwResId( STR_COLUMN_VALUESET_ITEM2 ) ;
                break;
            case 3:
                aItemText =  SwResId( STR_COLUMN_VALUESET_ITEM3 );
                break;
            default:
                aItemText =  SwResId( STR_COLUMN_VALUESET_ITEM4 );
                break;
        }
        m_aDefaultVS.InsertItem( i + 1, aItemText, i );
    }

    m_aDefaultVS.SetSelectHdl(LINK(this, SwColumnPage, SetDefaultsHdl));

    Link<weld::SpinButton&,void> aCLNrLk = LINK(this, SwColumnPage, ColModify);
    m_xCLNrEdt->connect_value_changed(aCLNrLk);
    Link<weld::MetricSpinButton&,void> aLk = LINK(this, SwColumnPage, GapModify);
    m_xDistEd1->connect_value_changed(aLk);
    m_xDistEd2->connect_value_changed(aLk);

    aLk = LINK(this, SwColumnPage, EdModify);

    m_xEd1->connect_value_changed(aLk);
    m_xEd2->connect_value_changed(aLk);
    m_xEd3->connect_value_changed(aLk);

    m_xBtnBack->connect_clicked(LINK(this, SwColumnPage, Up));
    m_xBtnNext->connect_clicked(LINK(this, SwColumnPage, Down));
    m_xAutoWidthBox->connect_toggled(LINK(this, SwColumnPage, AutoWidthHdl));

    Link<weld::MetricSpinButton&,void> aLk2 = LINK( this, SwColumnPage, UpdateColMgr );
    m_xLineTypeDLB->SetSelectHdl(LINK(this, SwColumnPage, UpdateColMgrLineBox));
    m_xLineWidthEdit->connect_value_changed(aLk2);
    m_xLineColorDLB->SetSelectHdl(LINK( this, SwColumnPage, UpdateColMgrColorBox));
    m_xLineHeightEdit->connect_value_changed(aLk2);
    m_xLinePosDLB->connect_changed(LINK(this, SwColumnPage, UpdateColMgrListBox));

    // Separator line
    m_xLineTypeDLB->SetSourceUnit( FieldUnit::TWIP );

    // Fill the line styles listbox
    m_xLineTypeDLB->InsertEntry(
        ::editeng::SvxBorderLine::getWidthImpl(SvxBorderLineStyle::SOLID),
        SvxBorderLineStyle::SOLID );
    m_xLineTypeDLB->InsertEntry(
        ::editeng::SvxBorderLine::getWidthImpl(SvxBorderLineStyle::DOTTED),
        SvxBorderLineStyle::DOTTED );
    m_xLineTypeDLB->InsertEntry(
        ::editeng::SvxBorderLine::getWidthImpl(SvxBorderLineStyle::DASHED),
        SvxBorderLineStyle::DASHED );

    sal_Int64 nLineWidth = m_xLineWidthEdit->get_value(FieldUnit::POINT);
    nLineWidth = vcl::ConvertAndUnscaleValue(
            nLineWidth,
            m_xLineWidthEdit->get_digits(),
            FieldUnit::POINT, FieldUnit::TWIP );
    m_xLineTypeDLB->SetWidth(nLineWidth);
    m_xLineColorDLB->SelectEntry(COL_BLACK);
}

SwColumnPage::~SwColumnPage()
{
    m_xFrameExampleWN.reset();
    m_xPgeExampleWN.reset();
    m_xDefaultVS.reset();
    m_xDistEd2.reset();
    m_xDistEd1.reset();
    m_xEd3.reset();
    m_xEd2.reset();
    m_xEd1.reset();
    m_xLineTypeDLB.reset();
    m_xLineColorDLB.reset();
    m_xTextDirectionLB.reset();
}

void SwColumnPage::SetPageWidth(tools::Long nPageWidth)
{
    tools::Long nNewMaxWidth = static_cast< tools::Long >(m_xEd1->NormalizePercent(nPageWidth));

    m_xDistEd1->set_max(nNewMaxWidth, FieldUnit::TWIP);
    m_xDistEd2->set_max(nNewMaxWidth, FieldUnit::TWIP);
    m_xEd1->set_max(nNewMaxWidth, FieldUnit::TWIP);
    m_xEd2->set_max(nNewMaxWidth, FieldUnit::TWIP);
    m_xEd3->set_max(nNewMaxWidth, FieldUnit::TWIP);
}

void SwColumnPage::connectPercentField(SwPercentField &rWrap)
{
    weld::MetricSpinButton *pField = rWrap.get();
    assert(pField);
    m_aPercentFieldsMap[pField] = &rWrap;
}

void SwColumnPage::Reset(const SfxItemSet *rSet)
{
    const sal_uInt16 nHtmlMode =
        ::GetHtmlMode(static_cast<const SwDocShell*>(SfxObjectShell::Current()));
    if(nHtmlMode & HTMLMODE_ON)
    {
        m_bHtmlMode = true;
        m_xAutoWidthBox->set_sensitive(false);
    }
    FieldUnit aMetric = ::GetDfltMetric(m_bHtmlMode);
    m_xEd1->SetMetric(aMetric);
    m_xEd2->SetMetric(aMetric);
    m_xEd3->SetMetric(aMetric);
    m_xDistEd1->SetMetric(aMetric);
    m_xDistEd2->SetMetric(aMetric);
    //default spacing between cols = 0.5cm
    m_xDistEd1->set_value(50, FieldUnit::CM);
    m_xDistEd2->set_value(50, FieldUnit::CM);

    m_xColMgr.reset(new SwColMgr(*rSet));
    m_nCols = m_xColMgr->GetCount() ;
    m_xCLNrEdt->set_max(std::max(o3tl::narrowing<sal_uInt16>(m_xCLNrEdt->get_max()), m_nCols));

    if(m_bFrame)
    {
        if(m_bFormat)                     // there is no size here
            m_xColMgr->SetActualWidth(FRAME_FORMAT_WIDTH);
        else
        {
            const SwFormatFrameSize& rSize = rSet->Get(RES_FRM_SIZE);
            const SvxBoxItem& rBox = rSet->Get(RES_BOX);
            m_xColMgr->SetActualWidth(o3tl::narrowing<sal_uInt16>(rSize.GetSize().Width()) - rBox.GetSmallestDistance());
        }
    }
    if (m_xBalanceColsCB->get_visible())
    {
        if( const SwFormatNoBalancedColumns* pItem = rSet->GetItemIfSet( RES_COLUMNBALANCE, false ) )
            m_xBalanceColsCB->set_active(!pItem->GetValue());
        else
            m_xBalanceColsCB->set_active(true);
    }

    //text direction
    if( SfxItemState::DEFAULT <= rSet->GetItemState( RES_FRAMEDIR ) )
    {
        const SvxFrameDirectionItem& rItem = rSet->Get(RES_FRAMEDIR);
        SvxFrameDirection nVal  = rItem.GetValue();
        m_xTextDirectionLB->set_active_id(nVal);
        m_xTextDirectionLB->save_value();
    }

    Init();
    ActivatePage( *rSet );
}

// create TabPage
std::unique_ptr<SfxTabPage> SwColumnPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *rSet)
{
    return std::make_unique<SwColumnPage>(pPage, pController, *rSet);
}

// stuff attributes into the Set when OK
bool SwColumnPage::FillItemSet(SfxItemSet *rSet)
{
    // set in ItemSet;
    // the current settings are already present

    const SfxPoolItem* pOldItem;
    const SwFormatCol& rCol = m_xColMgr->GetColumns();
    if(nullptr == (pOldItem = GetOldItem( *rSet, RES_COL )) ||
                rCol != *pOldItem )
        rSet->Put(rCol);

    if (m_xBalanceColsCB->get_visible())
    {
        rSet->Put(SwFormatNoBalancedColumns(!m_xBalanceColsCB->get_active()));
    }
    if (m_xTextDirectionLB->get_visible())
    {
        if (m_xTextDirectionLB->get_value_changed_from_saved())
        {
            rSet->Put(SvxFrameDirectionItem(m_xTextDirectionLB->get_active_id(), RES_FRAMEDIR) );
        }
    }
    return true;
}

// update ColumnManager
IMPL_LINK_NOARG( SwColumnPage, UpdateColMgrListBox, weld::ComboBox&, void )
{
    UpdateColMgr(*m_xLineWidthEdit);
}

IMPL_LINK_NOARG( SwColumnPage, UpdateColMgrLineBox, SvtLineListBox&, void )
{
    UpdateColMgr(*m_xLineWidthEdit);
}

IMPL_LINK_NOARG( SwColumnPage, UpdateColMgrColorBox, ColorListBox&, void )
{
    UpdateColMgr(*m_xLineWidthEdit);
}

IMPL_LINK_NOARG( SwColumnPage, UpdateColMgr, weld::MetricSpinButton&, void )
{
    if (!m_xColMgr)
        return;
    tools::Long nGutterWidth = m_xColMgr->GetGutterWidth();
    if (m_nCols > 1)
    {
            // Determine whether the most narrow column is too narrow
            // for the adjusted column gap
        tools::Long nMin = m_nColWidth[0];

        for( sal_uInt16 i = 1; i < m_nCols; ++i )
            nMin = std::min(nMin, m_nColWidth[i]);

        bool bAutoWidth = m_xAutoWidthBox->get_active();
        if(!bAutoWidth)
        {
            m_xColMgr->SetAutoWidth(false);
                // when the user didn't allocate the whole width,
                // add the missing amount to the last column.
            tools::Long nSum = 0;
            for(sal_uInt16 i = 0; i < m_nCols; ++i)
                nSum += m_nColWidth[i];
            nGutterWidth = 0;
            for(sal_uInt16 i = 0; i < m_nCols - 1; ++i)
                nGutterWidth += m_nColDist[i];
            nSum += nGutterWidth;

            tools::Long nMaxW = m_xColMgr->GetActualSize();

            if( nSum < nMaxW  )
                m_nColWidth[m_nCols - 1] += nMaxW - nSum;

            m_xColMgr->SetColWidth( 0, static_cast< sal_uInt16 >(m_nColWidth[0] + m_nColDist[0]/2) );
            for( sal_uInt16 i = 1; i < m_nCols-1; ++i )
            {
                tools::Long nActDist = (m_nColDist[i] + m_nColDist[i - 1]) / 2;
                m_xColMgr->SetColWidth( i, static_cast< sal_uInt16 >(m_nColWidth[i] + nActDist ));
            }
            m_xColMgr->SetColWidth( m_nCols-1, static_cast< sal_uInt16 >(m_nColWidth[m_nCols-1] + m_nColDist[m_nCols -2]/2) );

        }

        bool bEnable = isLineNotNone();
        m_xLineHeightEdit->set_sensitive(bEnable);
        m_xLineHeightLbl->set_sensitive(bEnable);
        m_xLineWidthLbl->set_sensitive(bEnable);
        m_xLineWidthEdit->set_sensitive(bEnable);
        m_xLineColorDLB->set_sensitive(bEnable);
        m_xLineColorLbl->set_sensitive(bEnable);

        sal_Int64 nLineWidth = m_xLineWidthEdit->get_value(FieldUnit::PERCENT);
        nLineWidth = vcl::ConvertAndUnscaleValue(
                nLineWidth,
                m_xLineWidthEdit->get_digits(),
                m_xLineWidthEdit->get_unit(), FieldUnit::TWIP );
        if( !bEnable )
            m_xColMgr->SetNoLine();
        else
        {
            m_xColMgr->SetLineWidthAndColor(
                    m_xLineTypeDLB->GetSelectEntryStyle(),
                    nLineWidth,
                    m_xLineColorDLB->GetSelectEntryColor() );
            m_xColMgr->SetAdjust(SwColLineAdj(m_xLinePosDLB->get_active() + 1));
            m_xColMgr->SetLineHeightPercent(static_cast<short>(m_xLineHeightEdit->get_value(FieldUnit::PERCENT)));
            bEnable = m_xColMgr->GetLineHeightPercent() != 100;
        }
        m_xLinePosLbl->set_sensitive(bEnable);
        m_xLinePosDLB->set_sensitive(bEnable);

        //fdo#66815 if the values are going to be the same, don't update
        //them to avoid the listbox selection resetting
        if (nLineWidth != m_xLineTypeDLB->GetWidth())
            m_xLineTypeDLB->SetWidth(nLineWidth);
        const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
        m_xLineTypeDLB->SetColor(rStyleSettings.GetDialogTextColor());
    }
    else
    {
        m_xColMgr->NoCols();
        m_nCols = 0;
    }

    //set maximum values
    m_xCLNrEdt->set_max(std::max(tools::Long(1),
        std::min(tools::Long(nMaxCols), tools::Long(m_xColMgr->GetActualSize() / (nGutterWidth + MINLAY)) )));

    //prompt example window
    if(!m_bLockUpdate)
    {
        if(m_bFrame)
        {
            m_aFrameExampleWN.SetColumns(m_xColMgr->GetColumns());
            m_aFrameExampleWN.SetLineColor(m_xColMgr->GetLineColor());
            m_aFrameExampleWN.Invalidate();
        }
        else
            m_aPgeExampleWN.Invalidate();
    }
}

void SwColumnPage::Init()
{
    m_xCLNrEdt->set_value(m_nCols);

    bool bAutoWidth = m_xColMgr->IsAutoWidth() || m_bHtmlMode;
    m_xAutoWidthBox->set_active(bAutoWidth);

    sal_Int32 nColumnWidthSum = 0;
    // set the widths
    for(sal_uInt16 i = 0; i < m_nCols; ++i)
    {
        m_nColWidth[i] = m_xColMgr->GetColWidth(i);
        nColumnWidthSum += m_nColWidth[i];
        if(i < m_nCols - 1)
            m_nColDist[i] = m_xColMgr->GetGutterWidth(i);
    }

    if( 1 < m_nCols )
    {
        // #97495# make sure that the automatic column width's are always equal
        if(bAutoWidth)
        {
            nColumnWidthSum /= m_nCols;
            for(sal_uInt16 i = 0; i < m_nCols; ++i)
                m_nColWidth[i] = nColumnWidthSum;
        }
        SwColLineAdj eAdj = m_xColMgr->GetAdjust();
        if( COLADJ_NONE == eAdj )       // the dialog doesn't know a NONE!
        {
            eAdj = COLADJ_TOP;
            //without Adjust no line type
            m_xLineTypeDLB->SelectEntry(SvxBorderLineStyle::NONE);
            m_xLineHeightEdit->set_value(100, FieldUnit::PERCENT);
        }
        else
        {
            // Need to multiply by 100 because of the 2 decimals
            m_xLineWidthEdit->set_value( m_xColMgr->GetLineWidth() * 100, FieldUnit::TWIP);
            m_xLineColorDLB->SelectEntry( m_xColMgr->GetLineColor() );
            m_xLineTypeDLB->SelectEntry( m_xColMgr->GetLineStyle() );
            m_xLineTypeDLB->SetWidth( m_xColMgr->GetLineWidth( ) );
            m_xLineHeightEdit->set_value(m_xColMgr->GetLineHeightPercent(), FieldUnit::PERCENT);

        }
        m_xLinePosDLB->set_active( static_cast< sal_Int32 >(eAdj - 1) );
    }
    else
    {
        m_xLinePosDLB->set_active(0);
        m_xLineTypeDLB->SelectEntry(SvxBorderLineStyle::NONE);
        m_xLineHeightEdit->set_value(100, FieldUnit::PERCENT);
    }

    UpdateCols();
    Update(nullptr);

        // set maximum number of columns
        // values below 1 are not allowed
    m_xCLNrEdt->set_max(std::max(tools::Long(1),
        std::min(tools::Long(nMaxCols), tools::Long(m_xColMgr->GetActualSize() / g_nMinWidth) )));
}

bool SwColumnPage::isLineNotNone() const
{
    // nothing is turned off
    return m_xLineTypeDLB->GetSelectEntryStyle() != SvxBorderLineStyle::NONE;
}

/*
 * The number of columns has changed -- here the controls for editing of the
 * columns are en- or disabled according to the column number. In case there are
 * more than nVisCols (=3) all Edit are being enabled and the buttons for
 * scrolling too. Otherwise Edits are being enabled according to the column
 * numbers; one column can not be edited.
 */
void SwColumnPage::UpdateCols()
{
    bool bEnableBtns= false;
    bool bEnable12  = false;
    bool bEnable3   = false;
    const bool bEdit = !m_xAutoWidthBox->get_active();
    if ( m_nCols > nVisCols )
    {
        bEnableBtns = !m_bHtmlMode;
        bEnable12 = bEnable3 = bEdit;
    }
    else if( bEdit )
    {
        // here are purposely hardly any breaks
        switch(m_nCols)
        {
            case 3: bEnable3 = true;
                [[fallthrough]];
            case 2: bEnable12= true; break;
            default: /* do nothing */;
        }
    }
    m_xEd1->set_sensitive(bEnable12);
    bool bEnable = m_nCols > 1;
    m_xDistEd1->set_sensitive(bEnable);
    m_xAutoWidthBox->set_sensitive(bEnable && !m_bHtmlMode);
    m_xEd2->set_sensitive(bEnable12);
    m_xDistEd2->set_sensitive(bEnable3);
    m_xEd3->set_sensitive(bEnable3);
    m_xLbl1->set_sensitive(bEnable12);
    m_xLbl2->set_sensitive(bEnable12);
    m_xLbl3->set_sensitive(bEnable3);
    m_xBtnBack->set_sensitive(bEnableBtns);
    m_xBtnNext->set_sensitive(bEnableBtns);

    m_xLineTypeDLB->set_sensitive( bEnable );
    m_xLineTypeLbl->set_sensitive( bEnable );

    if (bEnable)
    {
        bEnable = isLineNotNone();
    }

    //all these depend on > 1 column and line style != none
    m_xLineHeightEdit->set_sensitive(bEnable);
    m_xLineHeightLbl->set_sensitive(bEnable);
    m_xLineWidthLbl->set_sensitive(bEnable);
    m_xLineWidthEdit->set_sensitive(bEnable);
    m_xLineColorDLB->set_sensitive(bEnable);
    m_xLineColorLbl->set_sensitive(bEnable);

    if (bEnable)
        bEnable = m_xColMgr->GetLineHeightPercent() != 100;

    //and these additionally depend on line height != 100%
    m_xLinePosDLB->set_sensitive(bEnable);
    m_xLinePosLbl->set_sensitive(bEnable);
}

void SwColumnPage::SetLabels( sal_uInt16 nVis )
{
    //insert ~ before the last character, e.g. 1 -> ~1, 10 -> 1~0
    const OUString sLbl( '~' );

    const OUString sLbl1(OUString::number( nVis + 1 ));
    m_xLbl1->set_label(sLbl1.replaceAt(sLbl1.getLength()-1, 0, sLbl));

    const OUString sLbl2(OUString::number( nVis + 2 ));
    m_xLbl2->set_label(sLbl2.replaceAt(sLbl2.getLength()-1, 0, sLbl));

    const OUString sLbl3(OUString::number( nVis + 3 ));
    m_xLbl3->set_label(sLbl3.replaceAt(sLbl3.getLength()-1, 0, sLbl));

    const OUString sColumnWidth = SwResId( STR_ACCESS_COLUMN_WIDTH ) ;
    m_xEd1->set_accessible_name(sColumnWidth.replaceFirst("%1", sLbl1));
    m_xEd2->set_accessible_name(sColumnWidth.replaceFirst("%1", sLbl2));
    m_xEd3->set_accessible_name(sColumnWidth.replaceFirst("%1", sLbl3));

    const OUString sDist = SwResId( STR_ACCESS_PAGESETUP_SPACING ) ;
    m_xDistEd1->set_accessible_name(
        sDist.replaceFirst("%1", sLbl1).replaceFirst("%2", sLbl2));

    m_xDistEd2->set_accessible_name(
        sDist.replaceFirst("%1", sLbl2).replaceFirst("%2", sLbl3));
}

/*
 * Handler that is called at alteration of the column number. An alteration of
 * the column number overwrites potential user's width settings; all columns
 * are equally wide.
 */
IMPL_LINK_NOARG(SwColumnPage, ColModify, weld::SpinButton&, void)
{
    ColModify(/*bForceColReset=*/false);
}

void SwColumnPage::ColModify(bool bForceColReset)
{
    m_nCols = o3tl::narrowing<sal_uInt16>(m_xCLNrEdt->get_value());
    //#107890# the handler is also called from LoseFocus()
    //then no change has been made and thus no action should be taken
    // #i17816# changing the displayed types within the ValueSet
    //from two columns to two columns with different settings doesn't invalidate the
    // example windows in ::ColModify()
    if (!bForceColReset && m_xColMgr->GetCount() == m_nCols)
        return;

    if (!bForceColReset)
        m_aDefaultVS.SetNoSelection();
    tools::Long nDist = static_cast< tools::Long >(m_xDistEd1->DenormalizePercent(m_xDistEd1->get_value(FieldUnit::TWIP)));
    m_xColMgr->SetCount(m_nCols, o3tl::narrowing<sal_uInt16>(nDist));
    for(sal_uInt16 i = 0; i < m_nCols; i++)
        m_nColDist[i] = nDist;
    m_nFirstVis = 0;
    SetLabels( m_nFirstVis );
    UpdateCols();
    ResetColWidth();
    Update(nullptr);
}

/*
 * Modify handler for an alteration of the column width or the column gap.
 * These changes take effect time-displaced. With an alteration of the column
 * width the automatic calculation of the column width is overruled; only an
 * alteration of the column number leads back to that default.
 */
IMPL_LINK(SwColumnPage, GapModify, weld::MetricSpinButton&, rMetricField, void)
{
    if (m_nCols < 2)
        return;
    SwPercentField *pField = m_aPercentFieldsMap[&rMetricField];
    assert(pField);
    tools::Long nActValue = static_cast< tools::Long >(pField->DenormalizePercent(pField->get_value(FieldUnit::TWIP)));
    if (m_xAutoWidthBox->get_active())
    {
        const tools::Long nMaxGap = static_cast< tools::Long >
            ((m_xColMgr->GetActualSize() - m_nCols * MINLAY)/(m_nCols - 1));
        if(nActValue > nMaxGap)
        {
            nActValue = nMaxGap;
            m_xDistEd1->set_value(m_xDistEd1->NormalizePercent(nMaxGap), FieldUnit::TWIP);
        }
        m_xColMgr->SetGutterWidth(o3tl::narrowing<sal_uInt16>(nActValue));
        for(sal_uInt16 i = 0; i < m_nCols; i++)
            m_nColDist[i] = nActValue;

        ResetColWidth();
        UpdateCols();
    }
    else
    {
        const sal_uInt16 nVis = m_nFirstVis + ((pField == m_xDistEd2.get()) ? 1 : 0);
        tools::Long nDiff = nActValue - m_nColDist[nVis];
        if(nDiff)
        {
            tools::Long nLeft = m_nColWidth[nVis];
            tools::Long nRight = m_nColWidth[nVis + 1];
            if(nLeft + nRight + 2 * MINLAY < nDiff)
                nDiff = nLeft + nRight - 2 * MINLAY;
            if(nDiff < nRight - MINLAY)
            {
                nRight -= nDiff;
            }
            else
            {
                tools::Long nTemp = nDiff - nRight + MINLAY;
                nRight = MINLAY;
                if(nLeft > nTemp - MINLAY)
                {
                    nLeft -= nTemp;
                    nTemp = 0;
                }
                else
                {
                    nTemp -= nLeft + MINLAY;
                    nLeft = MINLAY;
                }
                nDiff = nTemp;
            }
            m_nColWidth[nVis] = nLeft;
            m_nColWidth[nVis + 1] = nRight;
            m_nColDist[nVis] += nDiff;

            m_xColMgr->SetColWidth( nVis, sal_uInt16(nLeft) );
            m_xColMgr->SetColWidth( nVis + 1, sal_uInt16(nRight) );
            m_xColMgr->SetGutterWidth( sal_uInt16(m_nColDist[nVis]), nVis );
        }

    }
    Update(&rMetricField);
}

IMPL_LINK(SwColumnPage, EdModify, weld::MetricSpinButton&, rEdit, void)
{
    SwPercentField *pField = m_aPercentFieldsMap[&rEdit];
    assert(pField);
    m_pModifiedField = pField;
    Timeout();
}

// Handler behind the Checkbox for automatic width. When the box is checked
// no explicit values for the column width can be entered.
IMPL_LINK(SwColumnPage, AutoWidthHdl, weld::Toggleable&, rBox, void)
{
    tools::Long nDist = static_cast< tools::Long >(m_xDistEd1->DenormalizePercent(m_xDistEd1->get_value(FieldUnit::TWIP)));
    m_xColMgr->SetCount(m_nCols, o3tl::narrowing<sal_uInt16>(nDist));
    for(sal_uInt16 i = 0; i < m_nCols; i++)
        m_nColDist[i] = nDist;
    if (rBox.get_active())
    {
        m_xColMgr->SetGutterWidth(sal_uInt16(nDist));
        ResetColWidth();
    }
    m_xColMgr->SetAutoWidth(rBox.get_active(), sal_uInt16(nDist));
    UpdateCols();
    Update(nullptr);
}

// scroll up the contents of the edits
IMPL_LINK_NOARG(SwColumnPage, Up, weld::Button&, void)
{
    if( m_nFirstVis )
    {
        --m_nFirstVis;
        SetLabels( m_nFirstVis );
        Update(nullptr);
    }
}

// scroll down the contents of the edits.
IMPL_LINK_NOARG(SwColumnPage, Down, weld::Button&, void)
{
    if( m_nFirstVis + nVisCols < m_nCols )
    {
        ++m_nFirstVis;
        SetLabels( m_nFirstVis );
        Update(nullptr);
    }
}

// relict from ancient times - now directly without time handler; triggered by
// an alteration of the column width or the column gap.
void SwColumnPage::Timeout()
{
    SwPercentField *pField = m_pModifiedField;
    if (m_pModifiedField)
    {
            // find the changed column
        sal_uInt16 nChanged = m_nFirstVis;
        if (m_pModifiedField == m_xEd2.get())
            ++nChanged;
        else if (m_pModifiedField == m_xEd3.get())
            nChanged += 2;

        tools::Long nNewWidth = static_cast< tools::Long >
            (m_pModifiedField->DenormalizePercent(m_pModifiedField->get_value(FieldUnit::TWIP)));
        tools::Long nDiff = nNewWidth - m_nColWidth[nChanged];

        // when it's the last column
        if(nChanged == m_nCols - 1)
        {
            m_nColWidth[0] -= nDiff;
            if(m_nColWidth[0] < static_cast<tools::Long>(g_nMinWidth))
            {
                nNewWidth -= g_nMinWidth - m_nColWidth[0];
                m_nColWidth[0] = g_nMinWidth;
            }

        }
        else if(nDiff)
        {
            m_nColWidth[nChanged + 1] -= nDiff;
            if(m_nColWidth[nChanged + 1] < static_cast<tools::Long>(g_nMinWidth))
            {
                nNewWidth -= g_nMinWidth - m_nColWidth[nChanged + 1];
                m_nColWidth[nChanged + 1] = g_nMinWidth;
            }
        }
        m_nColWidth[nChanged] = nNewWidth;
        m_pModifiedField = nullptr;
    }

    Update(pField ? pField->get() : nullptr);
}

// Update the view
void SwColumnPage::Update(const weld::MetricSpinButton* pInteractiveField)
{
    m_xBalanceColsCB->set_sensitive(m_nCols > 1);
    if(m_nCols >= 2)
    {
        sal_Int64 nCurrentValue, nNewValue;

        nCurrentValue = m_xEd1->NormalizePercent(m_xEd1->DenormalizePercent(m_xEd1->get_value(FieldUnit::TWIP)));
        nNewValue = m_xEd1->NormalizePercent(m_nColWidth[m_nFirstVis]);

        //fdo#87612 if we're interacting with this widget and the value will be the same
        //then leave it alone (i.e. don't change equivalent values of e.g. .8 -> 0.8)
        if (nNewValue != nCurrentValue || pInteractiveField != m_xEd1->get())
            m_xEd1->set_value(nNewValue, FieldUnit::TWIP);

        nCurrentValue = m_xDistEd1->NormalizePercent(m_xDistEd1->DenormalizePercent(m_xDistEd1->get_value(FieldUnit::TWIP)));
        nNewValue = m_xDistEd1->NormalizePercent(m_nColDist[m_nFirstVis]);
        if (nNewValue != nCurrentValue || pInteractiveField != m_xDistEd1->get())
            m_xDistEd1->set_value(nNewValue, FieldUnit::TWIP);

        nCurrentValue = m_xEd2->NormalizePercent(m_xEd2->DenormalizePercent(m_xEd2->get_value(FieldUnit::TWIP)));
        nNewValue = m_xEd2->NormalizePercent(m_nColWidth[m_nFirstVis+1]);
        if (nNewValue != nCurrentValue || pInteractiveField != m_xEd2->get())
            m_xEd2->set_value(nNewValue, FieldUnit::TWIP);

        if(m_nCols >= 3)
        {
            nCurrentValue = m_xDistEd2->NormalizePercent(m_xDistEd2->DenormalizePercent(m_xDistEd2->get_value(FieldUnit::TWIP)));
            nNewValue = m_xDistEd2->NormalizePercent(m_nColDist[m_nFirstVis+1]);
            if (nNewValue != nCurrentValue || pInteractiveField != m_xDistEd2->get())
                m_xDistEd2->set_value(nNewValue, FieldUnit::TWIP);

            nCurrentValue = m_xEd3->NormalizePercent(m_xEd3->DenormalizePercent(m_xEd3->get_value(FieldUnit::TWIP)));
            nNewValue = m_xEd3->NormalizePercent(m_nColWidth[m_nFirstVis+2]);
            if (nNewValue != nCurrentValue || pInteractiveField != m_xEd3->get())
                m_xEd3->set_value(nNewValue, FieldUnit::TWIP);
        }
        else
        {
            m_xEd3->set_text(OUString());
            m_xDistEd2->set_text(OUString());
        }
    }
    else
    {
        m_xEd1->set_text(OUString());
        m_xEd2->set_text(OUString());
        m_xEd3->set_text(OUString());
        m_xDistEd1->set_text(OUString());
        m_xDistEd2->set_text(OUString());
    }
    UpdateColMgr(*m_xLineWidthEdit);
}

// Update Bsp
void SwColumnPage::ActivatePage(const SfxItemSet& rSet)
{
    bool bVertical = false;
    if (SfxItemState::DEFAULT <= rSet.GetItemState(RES_FRAMEDIR))
    {
        const SvxFrameDirectionItem& rDirItem =
                    rSet.Get(RES_FRAMEDIR);
        bVertical = rDirItem.GetValue() == SvxFrameDirection::Vertical_RL_TB||
                    rDirItem.GetValue() == SvxFrameDirection::Vertical_LR_TB;
    }

    if (!m_bFrame)
    {
        if( SfxItemState::SET == rSet.GetItemState( SID_ATTR_PAGE_SIZE ))
        {
            const SvxSizeItem& rSize = rSet.Get(SID_ATTR_PAGE_SIZE);

            sal_uInt16 nActWidth;

            if (!bVertical)
            {
                const SvxLRSpaceItem& rLRSpace = rSet.Get(RES_LR_SPACE);
                const SvxBoxItem& rBox = rSet.Get(RES_BOX);
                nActWidth = rSize.GetSize().Width() - rLRSpace.ResolveLeft({})
                            - rLRSpace.ResolveRight({}) - rBox.GetSmallestDistance();
            }
            else
            {
                const SvxULSpaceItem& rULSpace = rSet.Get( RES_UL_SPACE );
                const SvxBoxItem& rBox = rSet.Get(RES_BOX);
                nActWidth = rSize.GetSize().Height()
                                - rULSpace.GetUpper() - rULSpace.GetLower() - rBox.GetSmallestDistance();

            }

            if( m_xColMgr->GetActualSize() != nActWidth)
            {
                m_xColMgr->SetActualWidth(nActWidth);
                ColModify(/*bForceColReset=*/false);
                UpdateColMgr( *m_xLineWidthEdit );
            }
        }
        m_xFrameExampleWN->hide();
        m_aPgeExampleWN.UpdateExample(rSet, m_xColMgr.get());
        m_xPgeExampleWN->show();

    }
    else
    {
        m_xPgeExampleWN->hide();
        m_xFrameExampleWN->show();

        // Size
        const SwFormatFrameSize& rSize = rSet.Get(RES_FRM_SIZE);
        const SvxBoxItem& rBox = rSet.Get(RES_BOX);

        sal_uInt16 nTotalWish;
        if (m_bFormat)
            nTotalWish = FRAME_FORMAT_WIDTH;
        else
        {
            tools::Long const nDistance = rBox.GetSmallestDistance();
            nTotalWish = (!bVertical ? rSize.GetWidth() : rSize.GetHeight()) - 2 * nDistance;
        }

        // set maximum values of column width
        SetPageWidth(nTotalWish);

        if(m_xColMgr->GetActualSize() != nTotalWish)
        {
            m_xColMgr->SetActualWidth(nTotalWish);
            Init();
        }
        bool bPercent;
        // only relative data in frame format
        if ( m_bFormat || (rSize.GetWidthPercent() && rSize.GetWidthPercent() != SwFormatFrameSize::SYNCED) )
        {
            // set value for 100%
            m_xEd1->SetRefValue(nTotalWish);
            m_xEd2->SetRefValue(nTotalWish);
            m_xEd3->SetRefValue(nTotalWish);
            m_xDistEd1->SetRefValue(nTotalWish);
            m_xDistEd2->SetRefValue(nTotalWish);

            // switch to %-view
            bPercent = true;
        }
        else
            bPercent = false;

        m_xEd1->ShowPercent(bPercent);
        m_xEd2->ShowPercent(bPercent);
        m_xEd3->ShowPercent(bPercent);
        m_xDistEd1->ShowPercent(bPercent);
        m_xDistEd2->ShowPercent(bPercent);
        m_xDistEd1->SetMetricFieldMin(0);
        m_xDistEd2->SetMetricFieldMin(0);
    }
    Update(nullptr);
}

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

    return DeactivateRC::LeavePage;
}

IMPL_LINK(SwColumnPage, SetDefaultsHdl, ValueSet *, pVS, void)
{
    const sal_uInt16 nItem = pVS->GetSelectedItemId();
    if( nItem < 4 )
    {
        m_xCLNrEdt->set_value(nItem);
        m_xAutoWidthBox->set_active(true);
        m_xDistEd1->set_value(50, FieldUnit::CM);
        ColModify(/*bForceColReset=*/true);
    }
    else
    {
        m_bLockUpdate = true;
        m_xCLNrEdt->set_value(2);
        m_xAutoWidthBox->set_active(false);
        m_xDistEd1->set_value(50, FieldUnit::CM);
        ColModify(/*bForceColReset=*/true);
        // now set the width ratio to 2 : 1 or 1 : 2 respectively
        const tools::Long nSmall = static_cast< tools::Long >(m_xColMgr->GetActualSize() / 3);
        if(nItem == 4)
        {
            m_xEd2->set_value(m_xEd2->NormalizePercent(nSmall), FieldUnit::TWIP);
            m_pModifiedField = m_xEd2.get();
        }
        else
        {
            m_xEd1->set_value(m_xEd1->NormalizePercent(nSmall), FieldUnit::TWIP);
            m_pModifiedField = m_xEd1.get();
        }
        m_bLockUpdate = false;
        Timeout();

    }
}

void SwColumnPage::SetFrameMode(bool bMod)
{
    m_bFrame = bMod;
}

void SwColumnPage::SetInSection(bool bSet)
{
    if(!SvtCTLOptions::IsCTLFontEnabled())
        return;

    m_xTextDirectionFT->set_visible(bSet);
    m_xTextDirectionLB->set_visible(bSet);
}

void ColumnValueSet::UserDraw(const UserDrawEvent& rUDEvt)
{
    vcl::RenderContext* pDev = rUDEvt.GetRenderContext();
    const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();

    tools::Rectangle aRect = rUDEvt.GetRect();
    const sal_uInt16 nItemId = rUDEvt.GetItemId();
    tools::Long nRectWidth = aRect.GetWidth();
    tools::Long nRectHeight = aRect.GetHeight();

    Point aBLPos = aRect.TopLeft();
    auto popIt = pDev->ScopedPush(vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR);
    pDev->SetFillColor(rStyleSettings.GetFieldColor());
    pDev->SetLineColor(rStyleSettings.GetFieldTextColor());

    tools::Long nStep = std::abs(std::abs(nRectHeight * 95 /100) / 11);
    tools::Long nTop = (nRectHeight - 11 * nStep ) / 2;
    sal_uInt16 nCols = 0;
    tools::Long nStarts[3];
    tools::Long nEnds[3];
    nStarts[0] = nRectWidth * 10 / 100;
    switch( nItemId )
    {
        case 1:
            nEnds[0] = nRectWidth * 9 / 10;
            nCols = 1;
        break;
        case 2: nCols = 2;
            nEnds[0] = nRectWidth * 45 / 100;
            nStarts[1] = nEnds[0] + nStep;
            nEnds[1] = nRectWidth * 9 / 10;
        break;
        case 3: nCols = 3;
            nEnds[0]    = nRectWidth * 30 / 100;
            nStarts[1]  = nEnds[0] + nStep;
            nEnds[1]    = nRectWidth * 63 / 100;
            nStarts[2]  = nEnds[1] + nStep;
            nEnds[2]    = nRectWidth * 9 / 10;
        break;
        case 4: nCols = 2;
            nEnds[0] = nRectWidth * 63 / 100;
            nStarts[1] = nEnds[0] + nStep;
            nEnds[1] = nRectWidth * 9 / 10;
        break;
        case 5: nCols = 2;
            nEnds[0] = nRectWidth * 30 / 100;
            nStarts[1] = nEnds[0] + nStep;
            nEnds[1] = nRectWidth * 9 / 10;
        break;
    }
    for(sal_uInt16 j = 0; j < nCols; j++ )
    {
        Point aStart(aBLPos.X() + nStarts[j], 0);
        Point aEnd(aBLPos.X() + nEnds[j], 0);
        for( sal_uInt16 i = 0; i < 12; i ++)
        {
            aStart.setY( aBLPos.Y() + nTop + i * nStep);
            aEnd.setY( aStart.Y() );
            pDev->DrawLine(aStart, aEnd);
        }
    }
}

void ColumnValueSet::StyleUpdated()
{
    SetFormat();
    Invalidate();
    ValueSet::StyleUpdated();
}

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