/* -*- 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 <scitems.hxx>
#include <editeng/eeitem.hxx>

#include <memory>
#include <AccessibleText.hxx>
#include <AccessibleCell.hxx>
#include <attrib.hxx>
#include <tabvwsh.hxx>
#include <editutil.hxx>
#include <document.hxx>
#include <scmod.hxx>
#include <prevwsh.hxx>
#include <docsh.hxx>
#include <prevloc.hxx>
#include <patattr.hxx>
#include <inputwin.hxx>
#include <editeng/unofored.hxx>
#include <editeng/editview.hxx>
#include <editeng/unoedhlp.hxx>
#include <editeng/fhgtitem.hxx>
#include <editeng/adjustitem.hxx>
#include <editeng/justifyitem.hxx>
#include <svx/svdmodel.hxx>
#include <svx/algitem.hxx>
#include <utility>
#include <vcl/svapp.hxx>

class ScViewForwarder : public SvxViewForwarder
{
    ScTabViewShell*     mpViewShell;
    ScSplitPos          meSplitPos;
public:
                        ScViewForwarder(ScTabViewShell* pViewShell, ScSplitPos eSplitPos);

    virtual bool        IsValid() const override;
    virtual Point       LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const override;
    virtual Point       PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const override;

    void                SetInvalid();
};

ScViewForwarder::ScViewForwarder(ScTabViewShell* pViewShell, ScSplitPos eSplitPos)
    :
    mpViewShell(pViewShell),
    meSplitPos(eSplitPos)
{
}

bool ScViewForwarder::IsValid() const
{
    return mpViewShell != nullptr;
}

Point ScViewForwarder::LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const
{
    if (mpViewShell)
    {
        vcl::Window* pWindow = mpViewShell->GetWindowByPos(meSplitPos);
        if (pWindow)
            return pWindow->LogicToPixel( rPoint, rMapMode );
    }
    else
    {
        OSL_FAIL("this ViewForwarder is not valid");
    }
    return Point();
}

Point ScViewForwarder::PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const
{
    if (mpViewShell)
    {
        vcl::Window* pWindow = mpViewShell->GetWindowByPos(meSplitPos);
        if (pWindow)
            return pWindow->PixelToLogic( rPoint, rMapMode );
    }
    else
    {
        OSL_FAIL("this ViewForwarder is not valid");
    }
    return Point();
}

void ScViewForwarder::SetInvalid()
{
    mpViewShell = nullptr;
}

class ScEditObjectViewForwarder : public SvxViewForwarder
{
    VclPtr<OutputDevice> mpWindow;
    // #i49561# EditView needed for access to its visible area.
    const EditView*     mpEditView;
public:
                        ScEditObjectViewForwarder( OutputDevice* pWindow,
                                                   const EditView* _pEditView);

    virtual bool        IsValid() const override;
    virtual Point       LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const override;
    virtual Point       PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const override;

    void                SetInvalid();
};

ScEditObjectViewForwarder::ScEditObjectViewForwarder( OutputDevice* pWindow,
                                                      const EditView* _pEditView )
    : mpWindow(pWindow)
    , mpEditView( _pEditView )
{
}

bool ScEditObjectViewForwarder::IsValid() const
{
    return (mpWindow != nullptr);
}

Point ScEditObjectViewForwarder::LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const
{
    if (mpWindow)
    {
        // #i49561# - consider offset of the visible area
        // of the EditView before converting point to pixel.
        Point aPoint( rPoint );
        if ( mpEditView )
        {
            tools::Rectangle aEditViewVisArea( mpEditView->GetVisArea() );
            aPoint += aEditViewVisArea.TopLeft();
        }
        return mpWindow->LogicToPixel( aPoint, rMapMode );
    }
    else
    {
        OSL_FAIL("this ViewForwarder is not valid");
    }
    return Point();
}

Point ScEditObjectViewForwarder::PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const
{
    if (mpWindow)
    {
        // #i49561# - consider offset of the visible area
        // of the EditView after converting point to logic.
        Point aPoint( mpWindow->PixelToLogic( rPoint, rMapMode ) );
        if ( mpEditView )
        {
            tools::Rectangle aEditViewVisArea( mpEditView->GetVisArea() );
            aPoint -= aEditViewVisArea.TopLeft();
        }
        return aPoint;
    }
    else
    {
        OSL_FAIL("this ViewForwarder is not valid");
    }
    return Point();
}

void ScEditObjectViewForwarder::SetInvalid()
{
    mpWindow = nullptr;
}

class ScPreviewViewForwarder : public SvxViewForwarder
{
protected:
    ScPreviewShell*     mpViewShell;
public:
    explicit            ScPreviewViewForwarder(ScPreviewShell* pViewShell);

    virtual bool        IsValid() const override;
    virtual Point       LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const override;
    virtual Point       PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const override;

    void                SetInvalid();
};

ScPreviewViewForwarder::ScPreviewViewForwarder(ScPreviewShell* pViewShell)
    : mpViewShell(pViewShell)
{
}

bool ScPreviewViewForwarder::IsValid() const
{
    return mpViewShell != nullptr;
}

Point ScPreviewViewForwarder::LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const
{
    if (mpViewShell)
    {
        vcl::Window* pWindow = mpViewShell->GetWindow();
        if (pWindow)
        {
            MapMode aMapMode(pWindow->GetMapMode().GetMapUnit());
            Point aPoint2( OutputDevice::LogicToLogic( rPoint, rMapMode, aMapMode) );
            return pWindow->LogicToPixel(aPoint2);
        }
    }
    else
    {
        OSL_FAIL("this ViewForwarder is not valid");
    }
    return Point();
}

Point ScPreviewViewForwarder::PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const
{
    if (mpViewShell)
    {
        vcl::Window* pWindow = mpViewShell->GetWindow();
        if (pWindow)
        {
            MapMode aMapMode(pWindow->GetMapMode());
            aMapMode.SetOrigin(Point());
            Point aPoint1( pWindow->PixelToLogic( rPoint ) );
            Point aPoint2( OutputDevice::LogicToLogic( aPoint1,
                                                       MapMode(aMapMode.GetMapUnit()),
                                                       rMapMode ) );
            return aPoint2;
        }
    }
    else
    {
        OSL_FAIL("this ViewForwarder is not valid");
    }
    return Point();
}

void ScPreviewViewForwarder::SetInvalid()
{
    mpViewShell = nullptr;
}

namespace {

class ScPreviewHeaderFooterViewForwarder : public ScPreviewViewForwarder
{
public:
                        ScPreviewHeaderFooterViewForwarder(ScPreviewShell* pViewShell);
};

}

ScPreviewHeaderFooterViewForwarder::ScPreviewHeaderFooterViewForwarder(ScPreviewShell* pViewShell)
    :
    ScPreviewViewForwarder(pViewShell)
{
}

namespace {

class ScPreviewCellViewForwarder : public ScPreviewViewForwarder
{
public:
                        ScPreviewCellViewForwarder(ScPreviewShell* pViewShell);
};

}

ScPreviewCellViewForwarder::ScPreviewCellViewForwarder(ScPreviewShell* pViewShell)
    :
    ScPreviewViewForwarder(pViewShell)
{
}

namespace {

class ScPreviewHeaderCellViewForwarder : public ScPreviewViewForwarder
{
public:
                        ScPreviewHeaderCellViewForwarder(ScPreviewShell* pViewShell);
};

}

ScPreviewHeaderCellViewForwarder::ScPreviewHeaderCellViewForwarder(ScPreviewShell* pViewShell)
    :
    ScPreviewViewForwarder(pViewShell)
{
}

namespace {

class ScPreviewNoteViewForwarder : public ScPreviewViewForwarder
{
public:
                        ScPreviewNoteViewForwarder(ScPreviewShell* pViewShell);
};

}

ScPreviewNoteViewForwarder::ScPreviewNoteViewForwarder(ScPreviewShell* pViewShell)
    :
    ScPreviewViewForwarder(pViewShell)
{
}

class ScEditViewForwarder : public SvxEditViewForwarder
{
    EditView*           mpEditView;
    VclPtr<OutputDevice> mpWindow;
public:
                        ScEditViewForwarder(EditView* pEditView, OutputDevice* pWin);

    virtual bool        IsValid() const override;
    virtual Point       LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const override;
    virtual Point       PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const override;
    virtual bool        GetSelection( ESelection& rSelection ) const override;
    virtual bool        SetSelection( const ESelection& rSelection ) override;
    virtual bool        Copy() override;
    virtual bool        Cut() override;
    virtual bool        Paste() override;

    void                SetInvalid();
};

ScEditViewForwarder::ScEditViewForwarder(EditView* pEditView, OutputDevice* pWin)
    : mpEditView(pEditView)
    , mpWindow(pWin)
{
}

bool ScEditViewForwarder::IsValid() const
{
    return mpWindow && mpEditView;
}

Point ScEditViewForwarder::LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const
{
    if (mpWindow)
        return mpWindow->LogicToPixel( rPoint, rMapMode );
    else
    {
        OSL_FAIL("this ViewForwarder is not valid");
    }
    return Point();
}

Point ScEditViewForwarder::PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const
{
    if (mpWindow)
        return mpWindow->PixelToLogic( rPoint, rMapMode );
    else
    {
        OSL_FAIL("this ViewForwarder is not valid");
    }
    return Point();
}

bool ScEditViewForwarder::GetSelection( ESelection& rSelection ) const
{
    bool bResult(false);
    if (IsValid())
    {
        rSelection = mpEditView->GetSelection();
        bResult = true;
    }
    else
    {
        OSL_FAIL("this ViewForwarder is not valid");
    }
    return bResult;
}

bool ScEditViewForwarder::SetSelection( const ESelection& rSelection )
{
    bool bResult(false);
    if (IsValid())
    {
        mpEditView->SetSelection(rSelection);
        bResult = true;
    }
    else
    {
        OSL_FAIL("this ViewForwarder is not valid");
    }
    return bResult;
}

bool ScEditViewForwarder::Copy()
{
    bool bResult(false);
    if (IsValid())
    {
        mpEditView->Copy();
        bResult = true;
    }
    else
    {
        OSL_FAIL("this ViewForwarder is not valid");
    }
    return bResult;
}

bool ScEditViewForwarder::Cut()
{
    bool bResult(false);
    if (IsValid())
    {
        mpEditView->Cut();
        bResult = true;
    }
    else
    {
        OSL_FAIL("this ViewForwarder is not valid");
    }
    return bResult;
}

bool ScEditViewForwarder::Paste()
{
    bool bResult(false);
    if (IsValid())
    {
        mpEditView->Paste();
        bResult = true;
    }
    else
    {
        OSL_FAIL("this ViewForwarder is not valid");
    }
    return bResult;
}

void ScEditViewForwarder::SetInvalid()
{
    mpWindow = nullptr;
    mpEditView = nullptr;
}

//  ScAccessibleCellTextData: shared data between sub objects of an accessible cell text object

ScAccessibleCellTextData::ScAccessibleCellTextData(ScTabViewShell* pViewShell,
        const ScAddress& rP, ScSplitPos eSplitPos, ScAccessibleCell* pAccCell)
    : ScAccessibleCellBaseTextData(GetDocShell(pViewShell), rP),
    mpViewShell(pViewShell),
    meSplitPos(eSplitPos),
    mpAccessibleCell( pAccCell )
{
}

ScAccessibleCellTextData::~ScAccessibleCellTextData()
{
    if (pEditEngine)
        pEditEngine->SetNotifyHdl(Link<EENotify&,void>());
    mpViewForwarder.reset();
}

void ScAccessibleCellTextData::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
{
    if ( rHint.GetId() == SfxHintId::Dying )
    {
        mpViewShell = nullptr;                     // invalid now
        if (mpViewForwarder)
            mpViewForwarder->SetInvalid();
    }
    ScAccessibleCellBaseTextData::Notify(rBC, rHint);
}

ScAccessibleTextData* ScAccessibleCellTextData::Clone() const
{
    return new ScAccessibleCellTextData( mpViewShell, aCellPos, meSplitPos, mpAccessibleCell );
}

SvxTextForwarder* ScAccessibleCellTextData::GetTextForwarder()
{
    ScCellTextData::GetTextForwarder(); // creates Forwarder and EditEngine

    if ( pDocShell && pEditEngine && mpViewShell )
    {
        ScDocument& rDoc = pDocShell->GetDocument();
        tools::Long nSizeX, nSizeY;
        mpViewShell->GetViewData().GetMergeSizePixel(
            aCellPos.Col(), aCellPos.Row(), nSizeX, nSizeY);

        Size aSize(nSizeX, nSizeY);

        // #i92143# text getRangeExtents reports incorrect 'x' values for spreadsheet cells
        tools::Long nIndent = 0;
        const SvxHorJustifyItem& rHorJustifyItem = rDoc.GetAttr( aCellPos, ATTR_HOR_JUSTIFY );
        SvxCellHorJustify eHorJust = rHorJustifyItem.GetValue();
        if ( eHorJust == SvxCellHorJustify::Left )
        {
            const ScIndentItem& rIndentItem = rDoc.GetAttr( aCellPos, ATTR_INDENT );
            nIndent = static_cast< tools::Long >( rIndentItem.GetValue() );
        }

        const SvxMarginItem& rMarginItem = rDoc.GetAttr( aCellPos, ATTR_MARGIN );
        ScViewData& rViewData = mpViewShell->GetViewData();
        double nPPTX = rViewData.GetPPTX();
        double nPPTY = rViewData.GetPPTY();
        tools::Long nLeftM = static_cast< tools::Long >( ( rMarginItem.GetLeftMargin() + nIndent ) * nPPTX );
        tools::Long nTopM = static_cast< tools::Long >( rMarginItem.GetTopMargin() * nPPTY );
        tools::Long nRightM = static_cast< tools::Long >( rMarginItem.GetRightMargin() * nPPTX );
        tools::Long nBottomM = static_cast< tools::Long >( rMarginItem.GetBottomMargin() * nPPTY );
        tools::Long nWidth = aSize.getWidth() - nLeftM - nRightM;
        aSize.setWidth( nWidth );
        aSize.setHeight( aSize.getHeight() - nTopM - nBottomM );

        vcl::Window* pWin = mpViewShell->GetWindowByPos( meSplitPos );
        if ( pWin )
        {
            aSize = pWin->PixelToLogic( aSize, pEditEngine->GetRefMapMode() );
        }

        /*  #i19430# Gnopernicus reads text partly if it sticks out of the cell
            boundaries. This leads to wrong results in cases where the cell text
            is rotated, because rotation is not taken into account when calcu-
            lating the visible part of the text. In these cases we will expand
            the cell size passed as paper size to the edit engine. The function
            accessibility::AccessibleStaticTextBase::GetParagraphBoundingBox()
            (see svx/source/accessibility/AccessibleStaticTextBase.cxx) will
            return the size of the complete text then, which is used to expand
            the cell bounding box in ScAccessibleCell::GetBoundingBox()
            (see sc/source/ui/Accessibility/AccessibleCell.cxx). */
        const ScRotateValueItem& rItem = rDoc.GetAttr( aCellPos, ATTR_ROTATE_VALUE );
        if (rItem.GetValue() != 0_deg100)
        {
            pEditEngine->SetPaperSize( Size( LONG_MAX, aSize.getHeight() ) );
            tools::Long nTxtWidth = static_cast< tools::Long >( pEditEngine->CalcTextWidth() );
            aSize.setWidth( std::max( aSize.getWidth(), nTxtWidth + 2 ) );
        }
        else
        {
            // #i92143# text getRangeExtents reports incorrect 'x' values for spreadsheet cells
            const ScLineBreakCell& rLineBreakItem = rDoc.GetAttr( aCellPos, ATTR_LINEBREAK );
            bool bLineBreak = rLineBreakItem.GetValue();
            if ( !bLineBreak )
            {
                tools::Long nTxtWidth = static_cast< tools::Long >( pEditEngine->CalcTextWidth() );
                aSize.setWidth( ::std::max( aSize.getWidth(), nTxtWidth ) );
            }
        }

        pEditEngine->SetPaperSize( aSize );

        // #i92143# text getRangeExtents reports incorrect 'x' values for spreadsheet cells
        if ( eHorJust == SvxCellHorJustify::Standard && rDoc.HasValueData( aCellPos.Col(), aCellPos.Row(), aCellPos.Tab() ) )
        {
            pEditEngine->SetDefaultItem( SvxAdjustItem( SvxAdjust::Right, EE_PARA_JUST ) );
        }

        Size aTextSize;
        if ( pWin )
        {
            aTextSize = pWin->LogicToPixel( Size( pEditEngine->CalcTextWidth(), pEditEngine->GetTextHeight() ), pEditEngine->GetRefMapMode() );
        }
        tools::Long nTextWidth = aTextSize.Width();
        tools::Long nTextHeight = aTextSize.Height();

        tools::Long nOffsetX = nLeftM;
        tools::Long nDiffX = nTextWidth - nWidth;
        if ( nDiffX > 0 )
        {
            switch ( eHorJust )
            {
                case SvxCellHorJustify::Right:
                    {
                        nOffsetX -= nDiffX;
                    }
                    break;
                case SvxCellHorJustify::Center:
                    {
                        nOffsetX -= nDiffX / 2;
                    }
                    break;
                default:
                    {
                    }
                    break;
            }
        }

        tools::Long nOffsetY = 0;
        const SvxVerJustifyItem& rVerJustifyItem = rDoc.GetAttr( aCellPos, ATTR_VER_JUSTIFY );
        switch (rVerJustifyItem.GetValue())
        {
            case SvxCellVerJustify::Standard:
            case SvxCellVerJustify::Bottom:
                {
                    nOffsetY = nSizeY - nBottomM - nTextHeight;
                }
                break;
            case SvxCellVerJustify::Center:
                {
                    nOffsetY = ( nSizeY - nTopM - nBottomM - nTextHeight ) / 2 + nTopM;
                }
                break;
            default:
                {
                    nOffsetY = nTopM;
                }
                break;
        }

        if ( mpAccessibleCell )
        {
            mpAccessibleCell->SetOffset( Point( nOffsetX, nOffsetY ) );
        }

        pEditEngine->SetNotifyHdl( LINK(this, ScAccessibleCellTextData, NotifyHdl) );
    }

    return pForwarder.get();
}

SvxViewForwarder* ScAccessibleCellTextData::GetViewForwarder()
{
    if (!mpViewForwarder)
        mpViewForwarder.reset(new ScViewForwarder(mpViewShell, meSplitPos));
    return mpViewForwarder.get();
}

SvxEditViewForwarder* ScAccessibleCellTextData::GetEditViewForwarder( bool /* bCreate */ )
{
    //#102219#; there should no EditViewForwarder be, because the cell is now readonly in this interface
    return nullptr;
}

IMPL_LINK(ScAccessibleTextData, NotifyHdl, EENotify&, aNotify, void)
{
    ::std::unique_ptr< SfxHint > aHint = SvxEditSourceHelper::EENotification2Hint( &aNotify );

    if (aHint)
        GetBroadcaster().Broadcast(*aHint);
}

ScDocShell* ScAccessibleCellTextData::GetDocShell(ScTabViewShell* pViewShell)
{
    ScDocShell* pDocSh = nullptr;
    if (pViewShell)
        pDocSh = pViewShell->GetViewData().GetDocShell();
    return pDocSh;
}

ScAccessibleEditObjectTextData::ScAccessibleEditObjectTextData(EditView* pEditView, OutputDevice* pWin, bool isClone)
    : mpEditView(pEditView)
    , mpEditEngine(pEditView ? &pEditView->getEditEngine() : nullptr)
    , mpWindow(pWin)
{
    // If the object is cloned, do NOT add notify hdl.
    mbIsCloned = isClone;
    if (mpEditEngine && !mbIsCloned)
        mpEditEngine->SetNotifyHdl( LINK(this, ScAccessibleEditObjectTextData, NotifyHdl) );
}

ScAccessibleEditObjectTextData::~ScAccessibleEditObjectTextData()
{
    // If the object is cloned, do NOT set notify hdl.
    if (mpEditEngine && !mbIsCloned)
        mpEditEngine->SetNotifyHdl(Link<EENotify&,void>());
    mpViewForwarder.reset();
    mpEditViewForwarder.reset();
    mpForwarder.reset();
}

void ScAccessibleEditObjectTextData::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
{
    if ( rHint.GetId() == SfxHintId::Dying )
    {
        mpWindow = nullptr;
        mpEditView = nullptr;
        mpEditEngine = nullptr;
        mpForwarder.reset();
        if (mpViewForwarder)
            mpViewForwarder->SetInvalid();
        if (mpEditViewForwarder)
            mpEditViewForwarder->SetInvalid();
    }
    ScAccessibleTextData::Notify(rBC, rHint);
}

ScAccessibleTextData* ScAccessibleEditObjectTextData::Clone() const
{
    // Add para to indicate the object is cloned
    return new ScAccessibleEditObjectTextData(mpEditView, mpWindow, true);
}

SvxTextForwarder* ScAccessibleEditObjectTextData::GetTextForwarder()
{
    if ((!mpForwarder && mpEditView) || (mpEditEngine && !mpEditEngine->GetNotifyHdl().IsSet()))
    {
        if (!mpEditEngine)
            mpEditEngine = &mpEditView->getEditEngine();
        // If the object is cloned, do NOT add notify hdl.
        if (mpEditEngine && !mpEditEngine->GetNotifyHdl().IsSet()&&!mbIsCloned)
            mpEditEngine->SetNotifyHdl( LINK(this, ScAccessibleEditObjectTextData, NotifyHdl) );
        if(!mpForwarder)
            mpForwarder.reset(new SvxEditEngineForwarder(*mpEditEngine));
    }
    return mpForwarder.get();
}

SvxViewForwarder* ScAccessibleEditObjectTextData::GetViewForwarder()
{
    if (!mpViewForwarder)
    {
        // i#49561 Get right-aligned cell content to be read by screenreader.
        mpViewForwarder.reset(new ScEditObjectViewForwarder( mpWindow, mpEditView ));
    }
    return mpViewForwarder.get();
}

SvxEditViewForwarder* ScAccessibleEditObjectTextData::GetEditViewForwarder( bool bCreate )
{
    if (!mpEditViewForwarder && mpEditView)
        mpEditViewForwarder.reset(new ScEditViewForwarder(mpEditView, mpWindow));
    if (bCreate)
    {
        if (!mpEditView && mpEditViewForwarder)
        {
            mpEditViewForwarder.reset();
        }
    }
    return mpEditViewForwarder.get();
}

IMPL_LINK(ScAccessibleEditObjectTextData, NotifyHdl, EENotify&, rNotify, void)
{
    ::std::unique_ptr< SfxHint > aHint = SvxEditSourceHelper::EENotification2Hint( &rNotify );

    if (aHint)
        GetBroadcaster().Broadcast(*aHint);
}

ScAccessibleEditLineTextData::ScAccessibleEditLineTextData(EditView* pEditView,
                                                           OutputDevice* pWin,
                                                           ScTextWnd* pTxtWnd)
    : ScAccessibleEditObjectTextData(pEditView, pWin)
    , mpTxtWnd(pTxtWnd)
    , mbEditEngineCreated(false)
{
    if (mpTxtWnd)
        mpTxtWnd->InsertAccessibleTextData( *this );
}

ScAccessibleEditLineTextData::~ScAccessibleEditLineTextData()
{
    if (mpTxtWnd)
        mpTxtWnd->RemoveAccessibleTextData( *this );

    if (mbEditEngineCreated && mpEditEngine)
    {
        delete mpEditEngine;
        mpEditEngine = nullptr;    // don't access in ScAccessibleEditObjectTextData dtor!
    }
    else if (mpTxtWnd && mpTxtWnd->HasEditView())
    {
        //  the NotifyHdl also has to be removed from the ScTextWnd's EditEngine
        //  (it's set in ScAccessibleEditLineTextData::GetTextForwarder, and mpEditEngine
        //  is reset there)
        mpTxtWnd->GetEditView()->getEditEngine().SetNotifyHdl(Link<EENotify&,void>());
    }
}

void ScAccessibleEditLineTextData::Dispose()
{
    if (mpTxtWnd)
        mpTxtWnd->RemoveAccessibleTextData( *this );

    ResetEditMode();
    mpWindow = nullptr;
    mpTxtWnd = nullptr;
}

ScAccessibleTextData* ScAccessibleEditLineTextData::Clone() const
{
    return new ScAccessibleEditLineTextData(mpEditView, mpWindow, mpTxtWnd);
}

SvxTextForwarder* ScAccessibleEditLineTextData::GetTextForwarder()
{
    if (mpTxtWnd)
    {
        if (mpTxtWnd->HasEditView())
        {
            mpEditView = mpTxtWnd->GetEditView();

            if (mbEditEngineCreated && mpEditEngine)
                ResetEditMode();
            mbEditEngineCreated = false;

            mpEditView = mpTxtWnd->GetEditView();
            ScAccessibleEditObjectTextData::GetTextForwarder(); // fill the mpForwarder
            mpEditEngine = nullptr;
        }
        else
        {
            mpEditView = nullptr;

            if (mpEditEngine && !mbEditEngineCreated)
                ResetEditMode();
            if (!mpEditEngine)
            {
                rtl::Reference<SfxItemPool> pEnginePool = EditEngine::CreatePool();
                mpEditEngine = new ScFieldEditEngine(nullptr, pEnginePool.get(), true);
                mbEditEngineCreated = true;
                mpEditEngine->EnableUndo( false );
                mpEditEngine->SetRefMapMode(MapMode(MapUnit::Map100thMM));
                mpForwarder.reset(new SvxEditEngineForwarder(*mpEditEngine));

                mpEditEngine->SetText(mpTxtWnd->GetTextString());

#if 0
                Size aSize(pTxtWnd->GetSizePixel());
                aSize = pTxtWnd->PixelToLogic(aSize, mpEditEngine->GetRefMapMode());
                mpEditEngine->SetPaperSize(aSize);
#else
                OutputDevice& rDevice = mpTxtWnd->GetDrawingArea()->get_ref_device();
                Size aSize(rDevice.GetOutputSizePixel());
                aSize = rDevice.PixelToLogic(aSize, mpEditEngine->GetRefMapMode());
                mpEditEngine->SetPaperSize(aSize);
#endif

                mpEditEngine->SetNotifyHdl( LINK(this, ScAccessibleEditObjectTextData, NotifyHdl) );
            }
        }
    }
    return mpForwarder.get();
}

SvxEditViewForwarder* ScAccessibleEditLineTextData::GetEditViewForwarder( bool bCreate )
{
    if (mpTxtWnd)
    {
        if (!mpTxtWnd->HasEditView() && bCreate)
        {
            if ( !mpTxtWnd->IsInputActive() )
            {
                mpTxtWnd->StartEditEngine(ScInputHandler::ErrorMessage);
                mpTxtWnd->GrabFocus();

                mpEditView = mpTxtWnd->GetEditView();
            }
        }
    }

    return ScAccessibleEditObjectTextData::GetEditViewForwarder(bCreate);
}

void ScAccessibleEditLineTextData::ResetEditMode()
{
    if (mbEditEngineCreated && mpEditEngine)
        delete mpEditEngine;
    else if (mpTxtWnd && mpTxtWnd->HasEditView())
        mpTxtWnd->GetEditView()->getEditEngine().SetNotifyHdl(Link<EENotify&,void>());
    mpEditEngine = nullptr;

    mpForwarder.reset();
    mpEditViewForwarder.reset();
    mpViewForwarder.reset();
    mbEditEngineCreated = false;
}

void ScAccessibleEditLineTextData::TextChanged()
{
    if (mbEditEngineCreated && mpEditEngine)
    {
        if (mpTxtWnd)
            mpEditEngine->SetText(mpTxtWnd->GetTextString());
    }
}

void ScAccessibleEditLineTextData::StartEdit()
{
    ResetEditMode();
    mpEditView = nullptr;

    // send SdrHintKind::BeginEdit
    SdrHint aHint(SdrHintKind::BeginEdit);
    GetBroadcaster().Broadcast( aHint );
}

void ScAccessibleEditLineTextData::EndEdit()
{
    // send SdrHintKind::EndEdit
    SdrHint aHint(SdrHintKind::EndEdit);
    GetBroadcaster().Broadcast( aHint );

    ResetEditMode();
    mpEditView = nullptr;
}

//  ScAccessiblePreviewCellTextData: shared data between sub objects of an accessible cell text object

ScAccessiblePreviewCellTextData::ScAccessiblePreviewCellTextData(ScPreviewShell* pViewShell,
                            const ScAddress& rP)
    : ScAccessibleCellBaseTextData(GetDocShell(pViewShell), rP),
    mpViewShell(pViewShell)
{
}

ScAccessiblePreviewCellTextData::~ScAccessiblePreviewCellTextData()
{
    if (pEditEngine)
        pEditEngine->SetNotifyHdl(Link<EENotify&,void>());
    mpViewForwarder.reset();
}

void ScAccessiblePreviewCellTextData::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
{
    if ( rHint.GetId() == SfxHintId::Dying )
    {
        mpViewShell = nullptr;                     // invalid now
        if (mpViewForwarder)
            mpViewForwarder->SetInvalid();
    }
    ScAccessibleCellBaseTextData::Notify(rBC, rHint);
}

ScAccessibleTextData* ScAccessiblePreviewCellTextData::Clone() const
{
    return new ScAccessiblePreviewCellTextData(mpViewShell, aCellPos);
}

SvxTextForwarder* ScAccessiblePreviewCellTextData::GetTextForwarder()
{
    bool bEditEngineBefore(pEditEngine != nullptr);

    ScCellTextData::GetTextForwarder(); // creates Forwarder and EditEngine

    if (!bEditEngineBefore && pEditEngine)
    {
        Size aSize(mpViewShell->GetLocationData().GetCellOutputRect(aCellPos).GetSize());
        vcl::Window* pWin = mpViewShell->GetWindow();
        if (pWin)
            aSize = pWin->PixelToLogic(aSize, pEditEngine->GetRefMapMode());
        pEditEngine->SetPaperSize(aSize);
    }

    if (pEditEngine)
        pEditEngine->SetNotifyHdl( LINK(this, ScAccessiblePreviewCellTextData, NotifyHdl) );

    return pForwarder.get();
}

SvxViewForwarder* ScAccessiblePreviewCellTextData::GetViewForwarder()
{
    if (!mpViewForwarder)
        mpViewForwarder.reset(new ScPreviewCellViewForwarder(mpViewShell));
    return mpViewForwarder.get();
}

ScDocShell* ScAccessiblePreviewCellTextData::GetDocShell(ScPreviewShell* pViewShell)
{
    ScDocShell* pDocSh = nullptr;
    if (pViewShell)
        pDocSh = pViewShell->GetDocument().GetDocumentShell();
    return pDocSh;
}

//  ScAccessiblePreviewHeaderCellTextData: shared data between sub objects of an accessible cell text object

ScAccessiblePreviewHeaderCellTextData::ScAccessiblePreviewHeaderCellTextData(ScPreviewShell* pViewShell,
            OUString aText, const ScAddress& rP, bool bColHeader, bool bRowHeader)
    : ScAccessibleCellBaseTextData(GetDocShell(pViewShell), rP),
    mpViewShell(pViewShell),
    maText(std::move(aText)),
    mbColHeader(bColHeader),
    mbRowHeader(bRowHeader)
{
}

ScAccessiblePreviewHeaderCellTextData::~ScAccessiblePreviewHeaderCellTextData()
{
    if (pEditEngine)
        pEditEngine->SetNotifyHdl(Link<EENotify&,void>());
    mpViewForwarder.reset();
}

void ScAccessiblePreviewHeaderCellTextData::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
{
    if ( rHint.GetId() == SfxHintId::Dying )
    {
        mpViewShell = nullptr;                     // invalid now
        if (mpViewForwarder)
            mpViewForwarder->SetInvalid();
    }
    ScAccessibleCellBaseTextData::Notify(rBC, rHint);
}

ScAccessibleTextData* ScAccessiblePreviewHeaderCellTextData::Clone() const
{
    return new ScAccessiblePreviewHeaderCellTextData(mpViewShell, maText, aCellPos, mbColHeader, mbRowHeader);
}

SvxTextForwarder* ScAccessiblePreviewHeaderCellTextData::GetTextForwarder()
{
    if (!pEditEngine)
    {
        if ( pDocShell )
        {
            ScDocument& rDoc = pDocShell->GetDocument();
            pEditEngine = rDoc.CreateFieldEditEngine();
        }
        else
        {
            rtl::Reference<SfxItemPool> pEnginePool = EditEngine::CreatePool();
            pEditEngine.reset( new ScFieldEditEngine(nullptr, pEnginePool.get(), true) );
        }
        pEditEngine->EnableUndo( false );
        if (pDocShell)
            pEditEngine->SetRefDevice(pDocShell->GetRefDevice());
        else
            pEditEngine->SetRefMapMode(MapMode(MapUnit::Map100thMM));
        pForwarder.reset( new SvxEditEngineForwarder(*pEditEngine) );
    }

    if (bDataValid)
        return pForwarder.get();

    if (!maText.isEmpty())
    {
        if ( mpViewShell  )
        {
            Size aOutputSize;
            vcl::Window* pWindow = mpViewShell->GetWindow();
            if ( pWindow )
                aOutputSize = pWindow->GetOutputSizePixel();
            tools::Rectangle aVisRect( Point(), aOutputSize );
            Size aSize(mpViewShell->GetLocationData().GetHeaderCellOutputRect(aVisRect, aCellPos, mbColHeader).GetSize());
            if (pWindow)
                aSize = pWindow->PixelToLogic(aSize, pEditEngine->GetRefMapMode());
            pEditEngine->SetPaperSize(aSize);
        }
        pEditEngine->SetTextCurrentDefaults( maText );
    }

    bDataValid = true;

    pEditEngine->SetNotifyHdl( LINK(this, ScAccessiblePreviewHeaderCellTextData, NotifyHdl) );

    return pForwarder.get();
}

SvxViewForwarder* ScAccessiblePreviewHeaderCellTextData::GetViewForwarder()
{
    if (!mpViewForwarder)
        mpViewForwarder.reset(new ScPreviewHeaderCellViewForwarder(mpViewShell));
    return mpViewForwarder.get();
}

ScDocShell* ScAccessiblePreviewHeaderCellTextData::GetDocShell(ScPreviewShell* pViewShell)
{
    ScDocShell* pDocSh = nullptr;
    if (pViewShell)
        pDocSh = pViewShell->GetDocument().GetDocumentShell();
    return pDocSh;
}

ScAccessibleHeaderTextData::ScAccessibleHeaderTextData(ScPreviewShell* pViewShell,
                            const EditTextObject* pEditObj, SvxAdjust eAdjust)
    :
    mpViewShell(pViewShell),
    mpDocSh(nullptr),
    mpEditObj(pEditObj),
    mbDataValid(false),
    meAdjust(eAdjust)
{
    if (pViewShell)
        mpDocSh = pViewShell->GetDocument().GetDocumentShell();
    if (mpDocSh)
        mpDocSh->GetDocument().AddUnoObject(*this);
}

ScAccessibleHeaderTextData::~ScAccessibleHeaderTextData()
{
    SolarMutexGuard aGuard;     //  needed for EditEngine dtor

    if (mpDocSh)
        mpDocSh->GetDocument().RemoveUnoObject(*this);
    if (mpEditEngine)
        mpEditEngine->SetNotifyHdl(Link<EENotify&,void>());
    mpEditEngine.reset();
    mpForwarder.reset();
}

ScAccessibleTextData* ScAccessibleHeaderTextData::Clone() const
{
    return new ScAccessibleHeaderTextData(mpViewShell, mpEditObj, meAdjust);
}

void ScAccessibleHeaderTextData::Notify( SfxBroadcaster&, const SfxHint& rHint )
{
    if ( rHint.GetId() == SfxHintId::Dying )
    {
        mpViewShell = nullptr;// invalid now
        mpDocSh = nullptr;
        if (mxViewForwarder)
            mxViewForwarder->SetInvalid();
    }
}

SvxTextForwarder* ScAccessibleHeaderTextData::GetTextForwarder()
{
    if (!mpEditEngine)
    {
        rtl::Reference<SfxItemPool> pEnginePool = EditEngine::CreatePool();
        std::unique_ptr<ScHeaderEditEngine> pHdrEngine(new ScHeaderEditEngine( pEnginePool.get() ));

        pHdrEngine->EnableUndo( false );
        pHdrEngine->SetRefMapMode(MapMode(MapUnit::MapTwip));

        //  default font must be set, independently of document
        //  -> use global pool from module
        std::unique_ptr<CellAttributeHelper> pTmp;
        const ScPatternAttr* pCellAttributeDefault(nullptr);

        if (nullptr != mpDocSh)
        {
            // we can use default CellAttribute from ScDocument
            pCellAttributeDefault = &mpDocSh->GetDocument().getCellAttributeHelper().getDefaultCellAttribute();
        }
        else
        {
            // no access to ScDocument, use temporary CellAttributeHelper.
            // also see ScHeaderFooterTextData::GetTextForwarder for more comments
            pTmp.reset(new CellAttributeHelper(ScModule::get()->GetPool()));
            pCellAttributeDefault = &pTmp->getDefaultCellAttribute();
        }

        SfxItemSet aDefaults(pHdrEngine->GetEmptyItemSet());
        pCellAttributeDefault->FillEditItemSet(&aDefaults);
        //  FillEditItemSet adjusts font height to 1/100th mm,
        //  but for header/footer twips is needed, as in the PatternAttr:
        aDefaults.Put( pCellAttributeDefault->GetItem(ATTR_FONT_HEIGHT).CloneSetWhich(EE_CHAR_FONTHEIGHT) );
        aDefaults.Put( pCellAttributeDefault->GetItem(ATTR_CJK_FONT_HEIGHT).CloneSetWhich(EE_CHAR_FONTHEIGHT_CJK) );
        aDefaults.Put( pCellAttributeDefault->GetItem(ATTR_CTL_FONT_HEIGHT).CloneSetWhich(EE_CHAR_FONTHEIGHT_CTL) );
        aDefaults.Put( SvxAdjustItem( meAdjust, EE_PARA_JUST ) );
        pHdrEngine->SetDefaults(std::move(aDefaults));

        ScHeaderFieldData aData;
        if (mpViewShell)
            mpViewShell->FillFieldData(aData);
        else
            ScHeaderFooterTextObj::FillDummyFieldData( aData );
        pHdrEngine->SetData( aData );

        mpEditEngine = std::move(pHdrEngine);
        mpForwarder.reset(new SvxEditEngineForwarder(*mpEditEngine));
    }

    if (mbDataValid)
        return mpForwarder.get();

    if ( mpViewShell  )
    {
        tools::Rectangle aVisRect;
        mpViewShell->GetLocationData().GetHeaderPosition(aVisRect);
        Size aSize(aVisRect.GetSize());
        vcl::Window* pWin = mpViewShell->GetWindow();
        if (pWin)
            aSize = pWin->PixelToLogic(aSize, mpEditEngine->GetRefMapMode());
        mpEditEngine->SetPaperSize(aSize);
    }
    if (mpEditObj)
        mpEditEngine->SetTextCurrentDefaults(*mpEditObj);

    mbDataValid = true;
    return mpForwarder.get();
}

SvxViewForwarder* ScAccessibleHeaderTextData::GetViewForwarder()
{
    if (!mxViewForwarder)
        mxViewForwarder = std::make_unique<ScPreviewHeaderFooterViewForwarder>(mpViewShell);
    return mxViewForwarder.get();
}

ScAccessibleNoteTextData::ScAccessibleNoteTextData(ScPreviewShell* pViewShell,
                            OUString  sText, const ScAddress& aCellPos, bool bMarkNote)
    :
    mpViewShell(pViewShell),
    mpDocSh(nullptr),
    msText(std::move(sText)),
    maCellPos(aCellPos),
    mbMarkNote(bMarkNote),
    mbDataValid(false)
{
    if (pViewShell)
        mpDocSh = pViewShell->GetDocument().GetDocumentShell();
    if (mpDocSh)
        mpDocSh->GetDocument().AddUnoObject(*this);
}

ScAccessibleNoteTextData::~ScAccessibleNoteTextData()
{
    SolarMutexGuard aGuard;     //  needed for EditEngine dtor

    if (mpDocSh)
        mpDocSh->GetDocument().RemoveUnoObject(*this);
    if (mpEditEngine)
        mpEditEngine->SetNotifyHdl(Link<EENotify&,void>());
    mpEditEngine.reset();
    mpForwarder.reset();
}

ScAccessibleTextData* ScAccessibleNoteTextData::Clone() const
{
    return new ScAccessibleNoteTextData(mpViewShell, msText, maCellPos, mbMarkNote);
}

void ScAccessibleNoteTextData::Notify( SfxBroadcaster&, const SfxHint& rHint )
{
    if ( rHint.GetId() == SfxHintId::Dying )
    {
        mpViewShell = nullptr;// invalid now
        mpDocSh = nullptr;
        if (mxViewForwarder)
            mxViewForwarder->SetInvalid();
    }
}

SvxTextForwarder* ScAccessibleNoteTextData::GetTextForwarder()
{
    if (!mpEditEngine)
    {
        if ( mpDocSh )
        {
            ScDocument& rDoc = mpDocSh->GetDocument();
            mpEditEngine = rDoc.CreateFieldEditEngine();
        }
        else
        {
            rtl::Reference<SfxItemPool> pEnginePool = EditEngine::CreatePool();
            mpEditEngine.reset( new ScFieldEditEngine(nullptr, pEnginePool.get(), true) );
        }
        mpEditEngine->EnableUndo( false );
        if (mpDocSh)
            mpEditEngine->SetRefDevice(mpDocSh->GetRefDevice());
        else
            mpEditEngine->SetRefMapMode(MapMode(MapUnit::Map100thMM));
        mpForwarder.reset( new SvxEditEngineForwarder(*mpEditEngine) );
    }

    if (mbDataValid)
        return mpForwarder.get();

    if (!msText.isEmpty())
    {

        if ( mpViewShell  )
        {
            Size aOutputSize;
            vcl::Window* pWindow = mpViewShell->GetWindow();
            if ( pWindow )
                aOutputSize = pWindow->GetOutputSizePixel();
            tools::Rectangle aVisRect( Point(), aOutputSize );
            Size aSize(mpViewShell->GetLocationData().GetNoteInRangeOutputRect(aVisRect, mbMarkNote, maCellPos).GetSize());
            if (pWindow)
                aSize = pWindow->PixelToLogic(aSize, mpEditEngine->GetRefMapMode());
            mpEditEngine->SetPaperSize(aSize);
        }
        mpEditEngine->SetTextCurrentDefaults( msText );
    }

    mbDataValid = true;

    mpEditEngine->SetNotifyHdl( LINK(this, ScAccessibleNoteTextData, NotifyHdl) );

    return mpForwarder.get();
}

SvxViewForwarder* ScAccessibleNoteTextData::GetViewForwarder()
{
    if (!mxViewForwarder)
        mxViewForwarder = std::make_unique<ScPreviewNoteViewForwarder>(mpViewShell);
    return mxViewForwarder.get();
}

// CSV import =================================================================

class ScCsvViewForwarder : public SvxViewForwarder
{
    VclPtr<OutputDevice>        mpWindow;

public:
    explicit                    ScCsvViewForwarder( OutputDevice* pWindow );

    virtual bool                IsValid() const override;
    virtual Point               LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const override;
    virtual Point               PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const override;

    void                        SetInvalid();
};

ScCsvViewForwarder::ScCsvViewForwarder( OutputDevice* pWindow ) :
    mpWindow( pWindow )
{
}

bool ScCsvViewForwarder::IsValid() const
{
    return mpWindow != nullptr;
}

Point ScCsvViewForwarder::LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const
{
    if( !mpWindow ) return Point();
    return mpWindow->LogicToPixel( rPoint, rMapMode );
}

Point ScCsvViewForwarder::PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const
{
    if( !mpWindow ) return Point();
    return mpWindow->PixelToLogic( rPoint, rMapMode );
}

void ScCsvViewForwarder::SetInvalid()
{
    mpWindow = nullptr;
}

ScAccessibleCsvTextData::ScAccessibleCsvTextData(
        OutputDevice* pWindow, EditEngine* pEditEngine,
        OUString aCellText, const Size& rCellSize ) :
    mpWindow( pWindow ),
    mpEditEngine( pEditEngine ),
    maCellText(std::move( aCellText )),
    maCellSize( rCellSize )
{
}

ScAccessibleCsvTextData::~ScAccessibleCsvTextData()
{
}

void ScAccessibleCsvTextData::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
{
    if ( rHint.GetId() == SfxHintId::Dying )
    {
        mpWindow = nullptr;
        mpEditEngine = nullptr;
        if (mpViewForwarder)
            mpViewForwarder->SetInvalid();
    }
    ScAccessibleTextData::Notify( rBC, rHint );
}

ScAccessibleTextData* ScAccessibleCsvTextData::Clone() const
{
    return new ScAccessibleCsvTextData( mpWindow, mpEditEngine, maCellText, maCellSize );
}

SvxTextForwarder* ScAccessibleCsvTextData::GetTextForwarder()
{
    if( mpEditEngine )
    {
        mpEditEngine->SetPaperSize( maCellSize );
        mpEditEngine->SetText( maCellText );
        if( !mpTextForwarder )
            mpTextForwarder.reset( new SvxEditEngineForwarder( *mpEditEngine ) );
    }
    else
        mpTextForwarder.reset();
    return mpTextForwarder.get();
}

SvxViewForwarder* ScAccessibleCsvTextData::GetViewForwarder()
{
    if( !mpViewForwarder )
        mpViewForwarder.reset( new ScCsvViewForwarder( mpWindow ) );
    return mpViewForwarder.get();
}

SvxEditViewForwarder* ScAccessibleCsvTextData::GetEditViewForwarder( bool /* bCreate */ )
{
    return nullptr;
}

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