/* -*- 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 <editeng/editobj.hxx>
#include <editeng/editstat.hxx>
#include <editeng/editview.hxx>
#include <editeng/flditem.hxx>
#include <editeng/adjustitem.hxx>
#include <editeng/fhgtitem.hxx>
#include <sfx2/basedlgs.hxx>
#include <sfx2/objsh.hxx>
#include <sfx2/sfxdlg.hxx>
#include <vcl/svapp.hxx>
#include <vcl/settings.hxx>
#include <vcl/builderfactory.hxx>

#include <tphfedit.hxx>
#include <editutil.hxx>
#include <global.hxx>
#include <attrib.hxx>
#include <patattr.hxx>
#include <scresid.hxx>
#include <sc.hrc>
#include <globstr.hrc>
#include <strings.hrc>
#include <tabvwsh.hxx>
#include <prevwsh.hxx>
#include <AccessibleEditObject.hxx>

#include <scabstdlg.hxx>
#include <memory>


static void lcl_GetFieldData( ScHeaderFieldData& rData )
{
    SfxViewShell* pShell = SfxViewShell::Current();
    if (pShell)
    {
        if (dynamic_cast<const ScTabViewShell*>( pShell) !=  nullptr)
            static_cast<ScTabViewShell*>(pShell)->FillFieldData(rData);
        else if (dynamic_cast<const ScPreviewShell*>( pShell) !=  nullptr)
            static_cast<ScPreviewShell*>(pShell)->FillFieldData(rData);
    }
}

// class ScEditWindow

ScEditWindow::ScEditWindow( vcl::Window* pParent, WinBits nBits, ScEditWindowLocation eLoc )
    :   Control( pParent, nBits ),
    eLocation(eLoc),
    pAcc(nullptr)
{
    EnableRTL(false);

    const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
    Color aBgColor = rStyleSettings.GetWindowColor();

    SetMapMode(MapMode(MapUnit::MapTwip));
    SetPointer( PointerStyle::Text );
    SetBackground( aBgColor );

    Size aSize( GetOutputSize() );
    aSize.setHeight( aSize.Height() * 4 );

    pEdEngine.reset( new ScHeaderEditEngine( EditEngine::CreatePool() ) );
    pEdEngine->SetPaperSize( aSize );
    pEdEngine->SetRefDevice( this );

    ScHeaderFieldData aData;
    lcl_GetFieldData( aData );

    // fields
    pEdEngine->SetData( aData );
    pEdEngine->SetControlWord( pEdEngine->GetControlWord() | EEControlBits::MARKFIELDS );
    mbRTL = ScGlobal::IsSystemRTL();
    if (mbRTL)
        pEdEngine->SetDefaultHorizontalTextDirection(EEHorizontalTextDirection::R2L);

    pEdView.reset( new EditView( pEdEngine.get(), this ) );
    pEdView->SetOutputArea( tools::Rectangle( Point(0,0), GetOutputSize() ) );

    pEdView->SetBackgroundColor( aBgColor );
    pEdEngine->InsertView( pEdView.get() );
}

void ScEditWindow::Resize()
{
    Size aOutputSize(GetOutputSize());
    Size aSize(aOutputSize);
    aSize.setHeight( aSize.Height() * 4 );
    pEdEngine->SetPaperSize(aSize);
    pEdView->SetOutputArea(tools::Rectangle(Point(0,0), aOutputSize));
    Control::Resize();
}

ScEditWindow::~ScEditWindow()
{
    disposeOnce();
}

void ScEditWindow::dispose()
{
    // delete Accessible object before deleting EditEngine and EditView
    if (pAcc)
    {
        css::uno::Reference< css::accessibility::XAccessible > xTemp = xAcc;
        if (xTemp.is())
            pAcc->dispose();
    }
    pEdEngine.reset();
    pEdView.reset();
    Control::dispose();
}

extern "C" SAL_DLLPUBLIC_EXPORT void makeScEditWindow(VclPtr<vcl::Window> & rRet, VclPtr<vcl::Window> & pParent, VclBuilder::stringmap &)
{
    rRet = VclPtr<ScEditWindow>::Create(pParent, WB_BORDER|WB_TABSTOP, Left);
}

void ScEditWindow::SetNumType(SvxNumType eNumType)
{
    pEdEngine->SetNumType(eNumType);
    pEdEngine->UpdateFields();
}

std::unique_ptr<EditTextObject> ScEditWindow::CreateTextObject()
{
    //  reset paragraph attributes
    //  (GetAttribs at creation of format dialog always returns the set items)

    const SfxItemSet& rEmpty = pEdEngine->GetEmptyItemSet();
    sal_Int32 nParCnt = pEdEngine->GetParagraphCount();
    for (sal_Int32 i=0; i<nParCnt; i++)
        pEdEngine->SetParaAttribs( i, rEmpty );

    return pEdEngine->CreateTextObject();
}

void ScEditWindow::SetFont( const ScPatternAttr& rPattern )
{
    SfxItemSet* pSet = new SfxItemSet( pEdEngine->GetEmptyItemSet() );
    rPattern.FillEditItemSet( pSet );
    //  FillEditItemSet adjusts font height to 1/100th mm,
    //  but for header/footer twips is needed, as in the PatternAttr:
    std::unique_ptr<SfxPoolItem> pNewItem(rPattern.GetItem(ATTR_FONT_HEIGHT).CloneSetWhich(EE_CHAR_FONTHEIGHT));
    pSet->Put( *pNewItem );
    pNewItem = rPattern.GetItem(ATTR_CJK_FONT_HEIGHT).CloneSetWhich(EE_CHAR_FONTHEIGHT_CJK);
    pSet->Put( *pNewItem );
    pNewItem = rPattern.GetItem(ATTR_CTL_FONT_HEIGHT).CloneSetWhich(EE_CHAR_FONTHEIGHT_CTL);
    pSet->Put( *pNewItem );
    if (mbRTL)
        pSet->Put( SvxAdjustItem( SvxAdjust::Right, EE_PARA_JUST ) );
    pEdEngine->SetDefaults( pSet );
}

void ScEditWindow::SetText( const EditTextObject& rTextObject )
{
    pEdEngine->SetText( rTextObject );
}

void ScEditWindow::InsertField( const SvxFieldItem& rFld )
{
    pEdView->InsertField( rFld );
}

void ScEditWindow::SetCharAttributes()
{
    SfxObjectShell* pDocSh  = SfxObjectShell::Current();

    SfxViewShell*       pViewSh = SfxViewShell::Current();

    ScTabViewShell* pTabViewSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() );

    OSL_ENSURE( pDocSh,  "Current DocShell not found" );
    OSL_ENSURE( pViewSh, "Current ViewShell not found" );

    if ( pDocSh && pViewSh )
    {
        if(pTabViewSh!=nullptr) pTabViewSh->SetInFormatDialog(true);

        SfxItemSet aSet( pEdView->GetAttribs() );

        ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();

        vcl::Window* pWin = GetParent();
        ScopedVclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateScCharDlg(
            pWin ? pWin->GetFrameWeld() : nullptr,  &aSet, pDocSh));
        pDlg->SetText( ScResId( STR_TEXTATTRS ) );
        if ( pDlg->Execute() == RET_OK )
        {
            aSet.ClearItem();
            aSet.Put( *pDlg->GetOutputItemSet() );
            pEdView->SetAttribs( aSet );
        }

        if(pTabViewSh!=nullptr) pTabViewSh->SetInFormatDialog(false);
    }
}

void ScEditWindow::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect )
{
    const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
    Color aBgColor = rStyleSettings.GetWindowColor();

    pEdView->SetBackgroundColor( aBgColor );

    SetBackground( aBgColor );

    Control::Paint(rRenderContext, rRect);

    pEdView->Paint(rRect);

    if( HasFocus() )
        pEdView->ShowCursor();
}

void ScEditWindow::MouseMove( const MouseEvent& rMEvt )
{
    pEdView->MouseMove( rMEvt );
}

void ScEditWindow::MouseButtonDown( const MouseEvent& rMEvt )
{
    if ( !HasFocus() )
        GrabFocus();

    pEdView->MouseButtonDown( rMEvt );
}

void ScEditWindow::MouseButtonUp( const MouseEvent& rMEvt )
{
    pEdView->MouseButtonUp( rMEvt );
}

void ScEditWindow::KeyInput( const KeyEvent& rKEvt )
{
    sal_uInt16 nKey =  rKEvt.GetKeyCode().GetModifier()
                 + rKEvt.GetKeyCode().GetCode();

    if ( nKey == KEY_TAB || nKey == KEY_TAB + KEY_SHIFT )
    {
        Control::KeyInput( rKEvt );
    }
    else if ( !pEdView->PostKeyEvent( rKEvt ) )
    {
        Control::KeyInput( rKEvt );
    }
    else if ( !rKEvt.GetKeyCode().IsMod1() && !rKEvt.GetKeyCode().IsShift() &&
                rKEvt.GetKeyCode().IsMod2() && rKEvt.GetKeyCode().GetCode() == KEY_DOWN )
    {
        aObjectSelectLink.Call(*this);
    }
}

void ScEditWindow::Command( const CommandEvent& rCEvt )
{
    pEdView->Command( rCEvt );
}

void ScEditWindow::GetFocus()
{
    pEdView->ShowCursor();

    assert(m_GetFocusLink);
    m_GetFocusLink(*this);

    css::uno::Reference< css::accessibility::XAccessible > xTemp = xAcc;
    if (xTemp.is() && pAcc)
    {
        pAcc->GotFocus();
    }
    else
        pAcc = nullptr;

    Control::GetFocus();
}

void ScEditWindow::LoseFocus()
{
    css::uno::Reference< css::accessibility::XAccessible > xTemp = xAcc;
    if (xTemp.is() && pAcc)
    {
        pAcc->LostFocus();
    }
    else
        pAcc = nullptr;
    Control::LoseFocus();
}

css::uno::Reference< css::accessibility::XAccessible > ScEditWindow::CreateAccessible()
{
    OUString sName;
    OUString sDescription(GetHelpText());
    switch (eLocation)
    {
    case Left:
        {
            sName = ScResId(STR_ACC_LEFTAREA_NAME);
        }
        break;
    case Center:
        {
            sName = ScResId(STR_ACC_CENTERAREA_NAME);
        }
        break;
    case Right:
        {
            sName = ScResId(STR_ACC_RIGHTAREA_NAME);
        }
        break;
    }
    pAcc = new ScAccessibleEditObject(GetAccessibleParentWindow()->GetAccessible(), pEdView.get(), this,
        sName, sDescription, ScAccessibleEditObject::EditControl);
    css::uno::Reference< css::accessibility::XAccessible > xAccessible = pAcc;
    xAcc = xAccessible;
    return pAcc;
}

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