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

#include <cmdid.h>
#include <docsh.hxx>
#include <swmodule.hxx>
#include <view.hxx>
#include <wrtsh.hxx>
#include <strings.hrc>

#include <vcl/metric.hxx>
#include <vcl/settings.hxx>

#include <rtl/ustrbuf.hxx>
#include <svl/stritem.hxx>
#include <editeng/fontitem.hxx>
#include <sfx2/dialoghelper.hxx>
#include <sfx2/htmlmode.hxx>
#include <sfx2/objsh.hxx>
#include <sfx2/printer.hxx>
#include <svtools/unitconv.hxx>
#include <vcl/print.hxx>
#include <vcl/svapp.hxx>
#include <com/sun/star/i18n/BreakIterator.hpp>
#include <com/sun/star/i18n/ScriptType.hpp>
#include <comphelper/processfactory.hxx>
#include <osl/diagnose.h>

#include <charatr.hxx>
#include <viewopt.hxx>
#include <drpcps.hxx>
#include <paratr.hxx>
#include <uitool.hxx>
#include <charfmt.hxx>

using namespace css;
using namespace css::uno;

const WhichRangesContainer SwDropCapsPage::s_aPageRg(svl::Items<RES_PARATR_DROP, RES_PARATR_DROP>);

void SwDropCapsPict::SetText( const OUString& rT )
{
    maText = rT;
    UpdatePaintSettings();
}

void SwDropCapsPict::SetDrawingArea(weld::DrawingArea* pDrawingArea)
{
    CustomWidgetController::SetDrawingArea(pDrawingArea);
    Size aPrefSize(getParagraphPreviewOptimalSize(pDrawingArea->get_ref_device()));
    pDrawingArea->set_size_request(aPrefSize.Width(), aPrefSize.Height());
}

void SwDropCapsPict::Resize()
{
    CustomWidgetController::Resize();
    UpdatePaintSettings();
}

void SwDropCapsPict::SetLines( sal_uInt8 nL )
{
    mnLines = nL;
    UpdatePaintSettings();
}

void SwDropCapsPict::SetDistance( sal_uInt16 nD )
{
    mnDistance = nD;
    UpdatePaintSettings();
}

void SwDropCapsPict::SetValues( const OUString& rText, sal_uInt8 nLines, sal_uInt16 nDistance )
{
    maText = rText;
    mnLines = nLines;
    mnDistance = nDistance;

    UpdatePaintSettings();
}

void SwDropCapsPict::InitPrinter()
{
    if( !mpPrinter )
        InitPrinter_();
}

// Create Default-String from character-count (A, AB, ABC, ...)
static OUString GetDefaultString(sal_Int32 nChars)
{
    OUStringBuffer aStr(nChars);
    for (sal_Int32 i = 0; i < nChars; i++)
        aStr.append(static_cast<sal_Unicode>(i + 65));
    return aStr.makeStringAndClear();
}

static void calcFontHeightAnyAscent(vcl::RenderContext& rWin, vcl::Font const & _rFont, tools::Long& _nHeight, tools::Long& _nAscent)
{
    if ( !_nHeight )
    {
        auto popIt = rWin.ScopedPush(vcl::PushFlags::FONT);
        rWin.SetFont(_rFont);
        FontMetric aMetric(rWin.GetFontMetric());
        _nHeight = aMetric.GetLineHeight();
        _nAscent = aMetric.GetAscent();
    }
}

SwDropCapsPict::~SwDropCapsPict()
{
     if (mbDelPrinter)
         mpPrinter.disposeAndClear();
}

/// Get the details of the first script change.
/// @param[out] start      The character position of the start of the segment.
/// @param[out] end        The character position of the end of the segment.
/// @param[out] scriptType The script type (Latin, Asian, Complex etc.)
void SwDropCapsPict::GetFirstScriptSegment(sal_Int32 &start, sal_Int32 &end, sal_uInt16 &scriptType)
{
    start = 0;
    if( maScriptChanges.empty() )
    {
        end = maText.getLength();
        scriptType = css::i18n::ScriptType::LATIN;
    }
    else
    {
        end = maScriptChanges[ 0 ].changePos;
        scriptType = maScriptChanges[ 0 ].scriptType;
    }
}

/// Get the details of the first script change.
/// @param[in,out] nIdx       Index of the current script change.
/// @param[out]    start      The character position of the start of the segment.
/// @param[in,out] end        The character position of the end of the segment.
/// @param[out]    scriptType The script type (Latin, Asian, Complex etc.)
/// @returns True if there was a next segment, false if not.
bool SwDropCapsPict::GetNextScriptSegment(size_t &nIdx, sal_Int32 &start, sal_Int32 &end, sal_uInt16 &scriptType)
{
    if (maScriptChanges.empty() || nIdx >= maScriptChanges.size() - 1 || end >= maText.getLength())
        return false;
    start = maScriptChanges[nIdx++].changePos;
    end = maScriptChanges[ nIdx ].changePos;
    scriptType = maScriptChanges[ nIdx ].scriptType;
    return true;
}

#define LINES  10
#define BORDER  2

void SwDropCapsPict::GetFontSettings( vcl::Font& _rFont, sal_uInt16 _nWhich )
{
    SwView* pView = GetActiveView();
    if (!pView)
        return;
    SwWrtShell& rWrtShell = pView->GetWrtShell();

    SfxItemSet aSet( rWrtShell.GetAttrPool(), _nWhich, _nWhich);
    rWrtShell.GetCurAttr(aSet);
    SvxFontItem aFormatFont(static_cast<const SvxFontItem &>( aSet.Get(_nWhich)));

    _rFont.SetFamily(aFormatFont.GetFamily());
    _rFont.SetFamilyName(aFormatFont.GetFamilyName());
    _rFont.SetPitch(aFormatFont.GetPitch());
    _rFont.SetCharSet(aFormatFont.GetCharSet());
}

void SwDropCapsPict::UpdatePaintSettings()
{
    SwView* pView = GetActiveView();
    if (!pView)
        return;
    SwWrtShell& rWrtShell = pView->GetWrtShell();

    maBackColor = Application::GetSettings().GetStyleSettings().GetWindowColor();
    maTextLineColor = COL_LIGHTGRAY;

    // gray lines
    mnTotLineH = (GetOutputSizePixel().Height() - 2 * BORDER) / LINES;
    mnLineH = mnTotLineH - 2;

    vcl::Font aFont;
    if (mpPage)
    {
        // tdf#135244: preview generation should not jump document view
        auto aLock(rWrtShell.GetView().GetDocShell()->LockAllViews());

        if (!mpPage->m_xTemplateBox->get_active())
        {
            // query the Font at paragraph's beginning
            rWrtShell.Push();
            rWrtShell.SttCursorMove();
            rWrtShell.ClearMark();
            SwWhichPara pSwuifnParaCurr = GoCurrPara;
            SwMoveFnCollection const & pSwuifnParaStart = fnParaStart;
            rWrtShell.MovePara(pSwuifnParaCurr,pSwuifnParaStart);
            // normal
            GetFontSettings( aFont, RES_CHRATR_FONT );

            // CJK
            GetFontSettings( maCJKFont, RES_CHRATR_CJK_FONT );

            // CTL
            GetFontSettings( maCTLFont, RES_CHRATR_CTL_FONT );

            rWrtShell.EndCursorMove();
            rWrtShell.Pop(SwCursorShell::PopMode::DeleteCurrent);
        }
        else
        {
            // query Font at character template
            SwCharFormat *pFormat = rWrtShell.GetCharStyle(
                                    UIName(mpPage->m_xTemplateBox->get_active_text()),
                                    SwWrtShell::GETSTYLE_CREATEANY );
            assert(pFormat && "character style doesn't exist!");
            const SvxFontItem &rFormatFont = pFormat->GetFont();

            aFont.SetFamily(rFormatFont.GetFamily());
            aFont.SetFamilyName(rFormatFont.GetFamilyName());
            aFont.SetPitch(rFormatFont.GetPitch());
            aFont.SetCharSet(rFormatFont.GetCharSet());
        }

        const Color& rFontColor = rWrtShell.GetViewOptions()->GetFontColor();
        aFont.SetColor( rFontColor );
        maCJKFont.SetColor( rFontColor );
        maCTLFont.SetColor( rFontColor );
    }

    mnTextH = mnLines * mnTotLineH;
    aFont.SetFontSize(Size(0, mnTextH));
    maCJKFont.SetFontSize(Size(0, mnTextH));
    maCTLFont.SetFontSize(Size(0, mnTextH));

    aFont.SetTransparent(true);
    maCJKFont.SetTransparent(true);
    maCTLFont.SetTransparent(true);

    aFont.SetFillColor(Application::GetSettings().GetStyleSettings().GetWindowColor());
    maCJKFont.SetFillColor(Application::GetSettings().GetStyleSettings().GetWindowColor());
    maCTLFont.SetFillColor(Application::GetSettings().GetStyleSettings().GetWindowColor());

    maCJKFont.SetFontSize(Size(0, maCJKFont.GetFontSize().Height()));
    maCTLFont.SetFontSize(Size(0, maCTLFont.GetFontSize().Height()));

    aFont.SetFontSize(Size(0, aFont.GetFontSize().Height()));
    maFont = aFont;

    CheckScript();

    maTextSize = CalcTextSize();

    Invalidate();
}

void SwDropCapsPict::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& /*rRect*/)
{
    if (!IsVisible())
        return;

    rRenderContext.SetMapMode(MapMode(MapUnit::MapPixel));
    rRenderContext.SetLineColor();

    rRenderContext.SetFillColor(maBackColor);

    Size aOutputSizePixel(GetOutputSizePixel());

    rRenderContext.DrawRect(tools::Rectangle(Point(0, 0), aOutputSizePixel));
    rRenderContext.SetClipRegion(vcl::Region(tools::Rectangle(Point(BORDER, BORDER),
                                                       Size(aOutputSizePixel.Width () - 2 * BORDER,
                                                            aOutputSizePixel.Height() - 2 * BORDER))));

    OSL_ENSURE(mnLineH > 0, "We cannot make it that small");
    tools::Long nY0 = (aOutputSizePixel.Height() - (LINES * mnTotLineH)) / 2;

    rRenderContext.SetFillColor(maTextLineColor);

    for (int i = 0; i < LINES; ++i)
    {
        rRenderContext.DrawRect(tools::Rectangle(Point(BORDER, nY0 + i * mnTotLineH),
                                Size(aOutputSizePixel.Width() - 2 * BORDER, mnLineH)));
    }

    // Text background with gap (240 twips ~ 1 line height)
    const tools::Long nDistW = (((static_cast<tools::Long>(mnDistance) * 100) / 240) * mnTotLineH) / 100;
    rRenderContext.SetFillColor(maBackColor);
    if (mpPage && mpPage->m_xDropCapsBox->get_active())
    {
        const Size aTextSize(maTextSize.Width() + nDistW, maTextSize.Height());
        rRenderContext.DrawRect(tools::Rectangle(Point(BORDER, nY0), aTextSize));

        // draw Text
        DrawPrev(rRenderContext, Point(BORDER, nY0));
    }
    rRenderContext.SetClipRegion();
}

void SwDropCapsPict::DrawPrev(vcl::RenderContext& rRenderContext, const Point& rPt)
{
    Point aPt(rPt);
    InitPrinter();

    vcl::Font aOldFont = mpPrinter->GetFont();
    sal_uInt16 nScript;
    size_t nIdx = 0;
    sal_Int32 nStart;
    sal_Int32 nEnd;

    GetFirstScriptSegment(nStart, nEnd, nScript);

    do
    {
        SvxFont& rFnt = (nScript == css::i18n::ScriptType::ASIAN)
                            ? maCJKFont
                            : ((nScript == css::i18n::ScriptType::COMPLEX)
                                    ? maCTLFont
                                    : maFont);
        mpPrinter->SetFont(rFnt);

        rFnt.DrawPrev(&rRenderContext, mpPrinter, aPt, maText, nStart, nEnd - nStart);

        if (!maScriptChanges.empty())
            aPt.AdjustX(maScriptChanges[nIdx].textWidth );

        if (!GetNextScriptSegment(nIdx, nStart, nEnd, nScript))
            break;
    }
    while(true);

    mpPrinter->SetFont(aOldFont);
}

void SwDropCapsPict::CheckScript()
{
    if( maScriptText == maText )
        return;

    maScriptText = maText;
    maScriptChanges.clear();
    if( !m_xBreak.is() )
    {
        const Reference< XComponentContext >& xContext = ::comphelper::getProcessComponentContext();
        m_xBreak = css::i18n::BreakIterator::create(xContext);
    }
    sal_Int16 nScript = m_xBreak->getScriptType( maText, 0 );
    sal_Int32 nChg = 0;
    if( css::i18n::ScriptType::WEAK == nScript )
    {
        nChg = m_xBreak->endOfScript( maText, nChg, nScript );
        if( nChg < maText.getLength() )
            nScript = m_xBreak->getScriptType( maText, nChg );
        else
            nScript = css::i18n::ScriptType::LATIN;
    }

    for(;;)
    {
        nChg = m_xBreak->endOfScript( maText, nChg, nScript );
        maScriptChanges.emplace_back(nScript, nChg );
        if( nChg >= maText.getLength() || nChg < 0 )
            break;
        nScript = m_xBreak->getScriptType( maText, nChg );
    }
}

Size SwDropCapsPict::CalcTextSize()
{
    InitPrinter();

    sal_uInt16 nScript;
    size_t nIdx = 0;
    sal_Int32 nStart;
    sal_Int32 nEnd;
    GetFirstScriptSegment(nStart, nEnd, nScript);
    tools::Long nTextWidth = 0;
    tools::Long nCJKHeight = 0;
    tools::Long nCTLHeight = 0;
    tools::Long nHeight = 0;
    tools::Long nAscent = 0;
    tools::Long nCJKAscent = 0;
    tools::Long nCTLAscent = 0;
    do
    {
        SvxFont& rFnt = (nScript == css::i18n::ScriptType::ASIAN)
                            ? maCJKFont
                            : ((nScript == css::i18n::ScriptType::COMPLEX)
                                    ? maCTLFont
                                    : maFont);

        sal_uLong nWidth = rFnt.GetTextSize(*mpPrinter, maText, nStart, nEnd-nStart ).Width();

        if (nIdx < maScriptChanges.size())
            maScriptChanges[nIdx].textWidth = nWidth;
        nTextWidth += nWidth;
        switch(nScript)
        {
            case css::i18n::ScriptType::ASIAN:
                calcFontHeightAnyAscent(GetDrawingArea()->get_ref_device(), maCJKFont, nCJKHeight, nCJKAscent);
                break;
            case css::i18n::ScriptType::COMPLEX:
                calcFontHeightAnyAscent(GetDrawingArea()->get_ref_device(), maCTLFont, nCTLHeight, nCTLAscent);
                break;
            default:
                calcFontHeightAnyAscent(GetDrawingArea()->get_ref_device(), maFont, nHeight, nAscent);
        }

        if (!GetNextScriptSegment(nIdx, nStart, nEnd, nScript))
            break;
    }
    while(true);

    nHeight -= nAscent;
    nCJKHeight -= nCJKAscent;
    nCTLHeight -= nCTLAscent;
    if (nHeight < nCJKHeight)
        nHeight = nCJKHeight;
    if (nAscent < nCJKAscent)
        nAscent = nCJKAscent;
    if (nHeight < nCTLHeight)
        nHeight = nCTLHeight;
    if (nAscent < nCTLAscent)
        nAscent = nCTLAscent;
    nHeight += nAscent;

    Size aTextSize(nTextWidth, nHeight);
    return aTextSize;
}

void SwDropCapsPict::InitPrinter_()
{
    SfxViewShell* pSh = SfxViewShell::Current();

    if (pSh)
        mpPrinter = pSh->GetPrinter();

    if (!mpPrinter)
    {
        mpPrinter = VclPtr<Printer>::Create();
        mbDelPrinter = true;
    }
}

SwDropCapsDlg::SwDropCapsDlg(weld::Window *pParent, const SfxItemSet &rSet)
    : SfxSingleTabDialogController(pParent, &rSet)
{
    auto xNewPage(SwDropCapsPage::Create(get_content_area(), this, &rSet));
    static_cast<SwDropCapsPage*>(xNewPage.get())->SetFormat(false);
    SetTabPage(std::move(xNewPage));
}

SwDropCapsPage::SwDropCapsPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rSet)
    : SfxTabPage(pPage, pController, u"modules/swriter/ui/dropcapspage.ui"_ustr, u"DropCapPage"_ustr, &rSet)
    , m_bModified(false)
    , m_bFormat(true)
    , m_xDropCapsBox(m_xBuilder->weld_check_button(u"checkCB_SWITCH"_ustr))
    , m_xWholeWordCB(m_xBuilder->weld_check_button(u"checkCB_WORD"_ustr))
    , m_xSwitchText(m_xBuilder->weld_label(u"labelFT_DROPCAPS"_ustr))
    , m_xDropCapsField(m_xBuilder->weld_spin_button(u"spinFLD_DROPCAPS"_ustr))
    , m_xLinesText(m_xBuilder->weld_label(u"labelTXT_LINES"_ustr))
    , m_xLinesField(m_xBuilder->weld_spin_button(u"spinFLD_LINES"_ustr))
    , m_xDistanceText(m_xBuilder->weld_label(u"labelTXT_DISTANCE"_ustr))
    , m_xDistanceField(m_xBuilder->weld_metric_spin_button(u"spinFLD_DISTANCE"_ustr, FieldUnit::CM))
    , m_xTextText(m_xBuilder->weld_label(u"labelTXT_TEXT"_ustr))
    , m_xTextEdit(m_xBuilder->weld_entry(u"entryEDT_TEXT"_ustr))
    , m_xTemplateText(m_xBuilder->weld_label(u"labelTXT_TEMPLATE"_ustr))
    , m_xTemplateBox(m_xBuilder->weld_combo_box(u"comboBOX_TEMPLATE"_ustr))
    , m_xPict(new weld::CustomWeld(*m_xBuilder, u"drawingareaWN_EXAMPLE"_ustr, m_aPict))
{
    m_aPict.SetDropCapsPage(this);

    SetExchangeSupport();

    const sal_uInt16 nHtmlMode = ::GetHtmlMode(static_cast<const SwDocShell*>(SfxObjectShell::Current()));
    m_bHtmlMode = (nHtmlMode & HTMLMODE_ON) != 0;

    // tdf#92154 limit comboBOX_TEMPLATE length
    const int nMaxWidth(m_xTemplateBox->get_approximate_digit_width() * 50);
    m_xTemplateBox->set_size_request(nMaxWidth , -1);

    // In the template dialog the text is not influenceable
    m_xTextText->set_sensitive(!m_bFormat);
    m_xTextEdit->set_sensitive(!m_bFormat);

    // Metrics
    SetFieldUnit(*m_xDistanceField, GetDfltMetric(m_bHtmlMode));

    // Install handler
    Link<weld::SpinButton&,void> aValueChangedLk = LINK(this, SwDropCapsPage, ValueChangedHdl);
    m_xDropCapsField->connect_value_changed(aValueChangedLk);
    m_xLinesField->connect_value_changed(aValueChangedLk);
    Link<weld::MetricSpinButton&,void> aMetricValueChangedLk = LINK(this, SwDropCapsPage, MetricValueChangedHdl);
    m_xDistanceField->connect_value_changed(aMetricValueChangedLk);
    m_xTextEdit->connect_changed(LINK(this, SwDropCapsPage, ModifyHdl));
    m_xDropCapsBox->connect_toggled(LINK(this, SwDropCapsPage, ClickHdl));
    m_xTemplateBox->connect_changed(LINK(this, SwDropCapsPage, SelectHdl));
    m_xWholeWordCB->connect_toggled(LINK(this, SwDropCapsPage, WholeWordHdl));
}

SwDropCapsPage::~SwDropCapsPage()
{
}

DeactivateRC SwDropCapsPage::DeactivatePage(SfxItemSet * _pSet)
{
    if (_pSet)
        FillSet(*_pSet);

    return DeactivateRC::LeavePage;
}

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

bool  SwDropCapsPage::FillItemSet(SfxItemSet *rSet)
{
    if (m_bModified)
        FillSet(*rSet);
    return m_bModified;
}

void  SwDropCapsPage::Reset(const SfxItemSet *rSet)
{
    // Characters, lines, gap and text
    const SwFormatDrop& aFormatDrop( rSet->Get(RES_PARATR_DROP) );
    if (aFormatDrop.GetLines() > 1)
    {
        m_xDropCapsField->set_value(aFormatDrop.GetChars());
        m_xLinesField->set_value(aFormatDrop.GetLines());
        m_xDistanceField->set_value(m_xDistanceField->normalize(aFormatDrop.GetDistance()), FieldUnit::TWIP);
        m_xWholeWordCB->set_active(aFormatDrop.GetWholeWord());
    }
    else
    {
        m_xDropCapsField->set_value(1);
        m_xLinesField->set_value(3);
        m_xDistanceField->set_value(0, FieldUnit::TWIP);
    }

    SwView* pView = GetActiveView();
    if (pView)
        ::FillCharStyleListBox(*m_xTemplateBox, pView->GetWrtShell().GetView().GetDocShell(), true);

    m_xTemplateBox->insert_text(0, SwResId(SW_STR_NONE));

    // Reset format
    int nSelect = 0;
    if (aFormatDrop.GetCharFormat())
    {
        int nPos = m_xTemplateBox->find_text(aFormatDrop.GetCharFormat()->GetName().toString());
        if (nPos != -1)
            nSelect = nPos;
    }
    m_xTemplateBox->set_active(nSelect);

    // Enable controls
    m_xDropCapsBox->set_active(aFormatDrop.GetLines() > 1);
    const sal_Int32 nVal = m_xDropCapsField->get_value();
    if (m_bFormat)
        m_xTextEdit->set_text(GetDefaultString(nVal));
    else
    {
        if (pView)
            m_xTextEdit->set_text(pView->GetWrtShell().GetDropText(nVal));
        m_xTextEdit->set_sensitive(true);
        m_xTextText->set_sensitive(true);
    }

    // Preview
    m_aPict.SetValues(m_xTextEdit->get_text(),
                      sal_uInt8(m_xLinesField->get_value()),
                      sal_uInt16(m_xDistanceField->denormalize(m_xDistanceField->get_value(FieldUnit::TWIP))));

    ClickHdl(*m_xDropCapsBox);
    m_bModified = false;
}

IMPL_LINK_NOARG(SwDropCapsPage, ClickHdl, weld::Toggleable&, void)
{
    bool bChecked = m_xDropCapsBox->get_active();

    m_xWholeWordCB->set_sensitive(bChecked && !m_bHtmlMode);

    m_xSwitchText->set_sensitive(bChecked && !m_xWholeWordCB->get_active());
    m_xDropCapsField->set_sensitive(bChecked && !m_xWholeWordCB->get_active());
    m_xLinesText->set_sensitive( bChecked );
    m_xLinesField->set_sensitive( bChecked );
    m_xDistanceText->set_sensitive( bChecked );
    m_xDistanceField->set_sensitive( bChecked );
    m_xTemplateText->set_sensitive( bChecked );
    m_xTemplateBox->set_sensitive( bChecked );
    m_xTextEdit->set_sensitive( bChecked && !m_bFormat );
    m_xTextText->set_sensitive( bChecked && !m_bFormat );

    if ( bChecked )
    {
        ValueChangedHdl(*m_xDropCapsField);
        m_xDropCapsField->grab_focus();
    }
    else
        m_aPict.SetText(u""_ustr);

    m_bModified = true;
}

IMPL_LINK_NOARG(SwDropCapsPage, WholeWordHdl, weld::Toggleable&, void)
{
    m_xDropCapsField->set_sensitive(!m_xWholeWordCB->get_active());
    m_xSwitchText->set_sensitive(!m_xWholeWordCB->get_active());

    ValueChangedHdl(*m_xDropCapsField);

    m_bModified = true;
}

void SwDropCapsPage::ModifyEntry(const weld::Entry& rEdit)
{
    OUString sPreview;

    // set text if applicable
    if (&rEdit == m_xDropCapsField.get())
    {
        const sal_Int32 nVal = !m_xWholeWordCB->get_active()
            ? static_cast<sal_Int32>(m_xDropCapsField->get_value())
            : 0;
        bool bSetText = false;

        if (SwView* pView = GetActiveView())
        {
            if (m_bFormat || pView->GetWrtShell().GetDropText(1).isEmpty())
                sPreview = GetDefaultString(nVal);
            else
            {
                bSetText = true;
                sPreview = pView->GetWrtShell().GetDropText(nVal);
            }
        }

        OUString sEdit(m_xTextEdit->get_text());

        if (!sEdit.isEmpty() && !sPreview.startsWith(sEdit))
        {
            sPreview = sEdit.copy(0, std::min(sEdit.getLength(), sPreview.getLength()));
            bSetText = false;
        }

        if (bSetText)
            m_xTextEdit->set_text(sPreview);
    }
    else if (&rEdit == m_xTextEdit.get())   // set quantity if applicable
    {
        const sal_Int32 nTmp = m_xTextEdit->get_text().getLength();
        m_xDropCapsField->set_value(std::max<sal_Int32>(1, nTmp));
        sPreview = m_xTextEdit->get_text();
    }

    // adjust image
    if (&rEdit == m_xDropCapsField.get() || &rEdit == m_xTextEdit.get())
        m_aPict.SetText(sPreview);
    else if (&rEdit == m_xLinesField.get())
        m_aPict.SetLines(static_cast<sal_uInt8>(m_xLinesField->get_value()));
    else
        m_aPict.SetDistance(o3tl::narrowing<sal_uInt16>(m_xDistanceField->denormalize(m_xDistanceField->get_value(FieldUnit::TWIP))));

    m_bModified = true;
}

IMPL_LINK(SwDropCapsPage, ModifyHdl, weld::Entry&, rEdit, void)
{
    ModifyEntry(rEdit);
}

IMPL_LINK(SwDropCapsPage, ValueChangedHdl, weld::SpinButton&, rEdit, void)
{
    ModifyEntry(rEdit);
}

IMPL_LINK(SwDropCapsPage, MetricValueChangedHdl, weld::MetricSpinButton&, rEdit, void)
{
    ModifyEntry(rEdit.get_widget());
}

IMPL_LINK_NOARG(SwDropCapsPage, SelectHdl, weld::ComboBox&, void)
{
    m_aPict.UpdatePaintSettings();
    m_bModified = true;
}

void SwDropCapsPage::FillSet( SfxItemSet &rSet )
{
    if(!m_bModified)
        return;

    SwFormatDrop aFormat;

    bool bOn = m_xDropCapsBox->get_active();
    if (bOn)
    {
        // quantity, lines, gap
        aFormat.SetChars(     static_cast<sal_uInt8>(m_xDropCapsField->get_value()) );
        aFormat.SetLines(     static_cast<sal_uInt8>(m_xLinesField->get_value())  );
        aFormat.SetDistance(  o3tl::narrowing<sal_uInt16>(m_xDistanceField->denormalize(m_xDistanceField->get_value(FieldUnit::TWIP)))  );
        aFormat.SetWholeWord( m_xWholeWordCB->get_active() );

        // template
        if (SwView* pView = GetActiveView())
            if (m_xTemplateBox->get_active())
                aFormat.SetCharFormat(pView->GetWrtShell().GetCharStyle(UIName(m_xTemplateBox->get_active_text())));
    }
    else
    {
        aFormat.SetChars(1);
        aFormat.SetLines(1);
        aFormat.SetDistance(0);
    }

    // set attributes
    const SfxPoolItem* pOldItem;
    if (nullptr == (pOldItem = GetOldItem(rSet, FN_FORMAT_DROPCAPS)) || aFormat != *pOldItem)
        rSet.Put(aFormat);

    // hard text formatting
    // Bug 24974: in designer/template catalog this doesn't make sense!!
    if (!m_bFormat && m_xDropCapsBox->get_active())
    {
        OUString sText(m_xTextEdit->get_text());

        if (!m_xWholeWordCB->get_active())
        {
            sText = sText.copy(0, std::min<sal_Int32>(sText.getLength(), m_xDropCapsField->get_value()));
        }

        SfxStringItem aStr(FN_PARAM_1, sText);
        rSet.Put(aStr);
    }
}

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