/* -*- 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 <comphelper/string.hxx>
#include <comphelper/lok.hxx>
#include <scitems.hxx>
#include <editeng/eeitem.hxx>
#include <i18nutil/unicode.hxx>
#include <i18nutil/transliteration.hxx>

#include <svx/clipfmtitem.hxx>
#include <svx/svxdlg.hxx>
#include <editeng/contouritem.hxx>
#include <editeng/outliner.hxx>
#include <editeng/crossedoutitem.hxx>
#include <editeng/editeng.hxx>
#include <editeng/editview.hxx>
#include <editeng/escapementitem.hxx>
#include <editeng/flditem.hxx>
#include <editeng/flstitem.hxx>
#include <editeng/fontitem.hxx>
#include <editeng/urlfieldhelper.hxx>
#include <editeng/editund2.hxx>
#include <svx/hlnkitem.hxx>
#include <vcl/EnumContext.hxx>
#include <editeng/postitem.hxx>
#include <editeng/scriptsetitem.hxx>
#include <editeng/shdditem.hxx>
#include <editeng/udlnitem.hxx>
#include <editeng/wghtitem.hxx>
#include <sfx2/bindings.hxx>
#include <sfx2/dispatch.hxx>
#include <sfx2/docfile.hxx>
#include <sfx2/docfilt.hxx>
#include <sfx2/namedcolor.hxx>
#include <sfx2/msg.hxx>
#include <sfx2/objface.hxx>
#include <sfx2/objsh.hxx>
#include <sfx2/request.hxx>
#include <sfx2/viewfrm.hxx>
#include <svtools/cliplistener.hxx>
#include <svl/whiter.hxx>
#include <sot/formats.hxx>
#include <vcl/transfer.hxx>
#include <vcl/unohelp2.hxx>
#include <svl/stritem.hxx>
#include <editeng/colritem.hxx>

#include <editsh.hxx>
#include <global.hxx>
#include <appoptio.hxx>
#include <scmod.hxx>
#include <sc.hrc>
#include <inputhdl.hxx>
#include <viewutil.hxx>
#include <viewdata.hxx>
#include <document.hxx>
#include <reffind.hxx>
#include <tabvwsh.hxx>
#include <editutil.hxx>
#include <globstr.hrc>
#include <scresid.hxx>
#include <gridwin.hxx>

#define ShellClass_ScEditShell
#include <scslots.hxx>

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

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


SFX_IMPL_INTERFACE(ScEditShell, SfxShell)

void ScEditShell::InitInterface_Impl()
{
    GetStaticInterface()->RegisterPopupMenu(u"celledit"_ustr);
}

ScEditShell::ScEditShell(EditView* pView, ScViewData& rData) :
    pEditView       (pView),
    rViewData       (rData),
    bPastePossible  (false),
    bIsInsertMode   (true)
{
    SetPool( pEditView->getEditEngine().GetEmptyItemSet().GetPool() );
    SetUndoManager( &pEditView->getEditEngine().GetUndoManager() );
    SetName(u"EditCell"_ustr);
    SfxShell::SetContextName(vcl::EnumContext::GetContextName(vcl::EnumContext::Context::EditCell));
}

ScEditShell::~ScEditShell()
{
    if ( mxClipEvtLstnr.is() )
    {
        mxClipEvtLstnr->RemoveListener( rViewData.GetActiveWin() );

        //  The listener may just now be waiting for the SolarMutex and call the link
        //  afterwards, in spite of RemoveListener. So the link has to be reset, too.
        mxClipEvtLstnr->ClearCallbackLink();
    }
}

ScInputHandler* ScEditShell::GetMyInputHdl()
{
    return ScModule::get()->GetInputHdl(rViewData.GetViewShell());
}

void ScEditShell::SetEditView(EditView* pView)
{
    pEditView = pView;
    pEditView->SetInsertMode( bIsInsertMode );
    SetPool( pEditView->getEditEngine().GetEmptyItemSet().GetPool() );
    SetUndoManager( &pEditView->getEditEngine().GetUndoManager() );
}

static void lcl_RemoveAttribs( EditView& rEditView )
{
    ScEditEngineDefaulter* pEngine = static_cast<ScEditEngineDefaulter*>(&rEditView.getEditEngine());

    bool bOld = pEngine->SetUpdateLayout(false);

    OUString aName = ScResId( STR_UNDO_DELETECONTENTS );
    ViewShellId nViewShellId(-1);
    if (ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell())
        nViewShellId = pViewSh->GetViewShellId();
    pEngine->GetUndoManager().EnterListAction( aName, aName, 0, nViewShellId );

    rEditView.RemoveAttribs(true);
    pEngine->RepeatDefaults();      // paragraph attributes from cell formats must be preserved

    pEngine->GetUndoManager().LeaveListAction();

    pEngine->SetUpdateLayout(bOld);
}

static void lclInsertCharacter( EditView* pTableView, EditView* pTopView, sal_Unicode cChar )
{
    OUString aString( cChar );
    if( pTableView )
        pTableView->InsertText( aString );
    if( pTopView )
        pTopView->InsertText( aString );
}

void ScEditShell::Execute( SfxRequest& rReq )
{
    const SfxItemSet*   pReqArgs    = rReq.GetArgs();
    sal_uInt16              nSlot   = rReq.GetSlot();
    SfxBindings&        rBindings   = rViewData.GetBindings();

    ScInputHandler* pHdl = GetMyInputHdl();
    OSL_ENSURE(pHdl,"no ScInputHandler");

    EditView* pTopView   = pHdl->GetTopView();      // Has thee input cell the focus?
    EditView* pTableView = pHdl->GetTableView();

    OSL_ENSURE(pTableView,"no EditView :-(");
    /* #i91683# No EditView if spell-check dialog is active and positioned on
     * an error and user immediately (without double click or F2) selected a
     * text portion of that cell with the mouse and wanted to modify it. */
    /* FIXME: Bailing out only cures the symptom and prevents a crash, no edit
     * action is possible. A real fix somehow would need to create a valid
     * EditView from the spell-check view. */
    if (!pTableView)
        return;

    EditEngine& rEngine = pTableView->getEditEngine();

    pHdl->DataChanging();
    bool bSetSelIsRef = false;
    bool bSetModified = true;

    switch ( nSlot )
    {
        case SID_ATTR_INSERT:
        case FID_INS_CELL_CONTENTS: // Insert taste, while defined as Acc
            bIsInsertMode = !pTableView->IsInsertMode();
            pTableView->SetInsertMode( bIsInsertMode );
            if (pTopView)
                pTopView->SetInsertMode( bIsInsertMode );
            rBindings.Invalidate( SID_ATTR_INSERT );
            break;

        case SID_THES:
            {
                OUString aReplaceText;
                const SfxStringItem* pItem2 = rReq.GetArg(FN_PARAM_THES_WORD_REPLACE);
                if (pItem2)
                    aReplaceText = pItem2->GetValue();
                if (!aReplaceText.isEmpty())
                    ReplaceTextWithSynonym( *pEditView, aReplaceText );
            }
            break;

        case SID_COPY:
            pTableView->Copy();
            bSetModified = false;
            break;

        case SID_CUT:
            pTableView->Cut();
            if (pTopView)
                pTopView->DeleteSelected();
            break;

        case SID_PASTE:
        {
            EVControlBits nControl = pTableView->GetControlWord();
            if (pTopView)
            {
                pTopView->Paste();
                pTableView->SetControlWord(nControl | EVControlBits::SINGLELINEPASTE);
            }

            pTableView->PasteSpecial();
            pTableView->SetControlWord(nControl);
        }
        break;

        case SID_DELETE:
            pTableView->DeleteSelected();
            if (pTopView)
                pTopView->DeleteSelected();
            break;

        case SID_CELL_FORMAT_RESET:                 // "Standard"
            lcl_RemoveAttribs( *pTableView );
            if ( pTopView )
                lcl_RemoveAttribs( *pTopView );
            break;

        case SID_CLIPBOARD_FORMAT_ITEMS:
            {
                SotClipboardFormatId nFormat = SotClipboardFormatId::NONE;
                const SfxPoolItem* pItem;
                if ( pReqArgs && pReqArgs->GetItemState(nSlot, true, &pItem) == SfxItemState::SET )
                    if (auto pIntItem = dynamic_cast<const SfxUInt32Item*>( pItem))
                        nFormat = static_cast<SotClipboardFormatId>(pIntItem->GetValue());

                if ( nFormat != SotClipboardFormatId::NONE )
                {
                    if (SotClipboardFormatId::STRING == nFormat)
                        pTableView->Paste();
                    else
                        pTableView->PasteSpecial();

                    if (pTopView)
                        pTopView->Paste();
                }
            }
            break;

        case SID_PASTE_SPECIAL:
            {
                SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
                ScopedVclPtr<SfxAbstractPasteDialog> pDlg(pFact->CreatePasteDialog(rViewData.GetDialogParent()));
                SotClipboardFormatId nFormat = SotClipboardFormatId::NONE;
                pDlg->Insert( SotClipboardFormatId::STRING, OUString() );
                pDlg->Insert( SotClipboardFormatId::RTF,    OUString() );
                pDlg->Insert( SotClipboardFormatId::RICHTEXT,    OUString() );
                // Do not offer SotClipboardFormatId::STRING_TSVC for
                // in-cell paste.

                TransferableDataHelper aDataHelper(
                    TransferableDataHelper::CreateFromSystemClipboard( rViewData.GetActiveWin() ) );

                nFormat = pDlg->GetFormat( aDataHelper.GetTransferable() );
                pDlg.disposeAndClear();

                // while the dialog was open, edit mode may have been stopped
                if (!ScModule::get()->IsInputMode())
                    return;

                if (nFormat != SotClipboardFormatId::NONE)
                {
                    if (SotClipboardFormatId::STRING == nFormat)
                        pTableView->Paste();
                    else
                        pTableView->PasteSpecial();

                    if (pTopView)
                        pTopView->Paste();
                }

                if (vcl::Window* pViewWindow = pTopView ? pTopView->GetWindow() : nullptr)
                    pViewWindow->GrabFocus();
            }
            break;

        case SID_PASTE_UNFORMATTED:
            {
                pTableView->Paste();

                if (pTopView)
                {
                    pTopView->Paste();
                    if (vcl::Window* pViewWindow = pTopView->GetWindow())
                        pViewWindow->GrabFocus();
                }
            }
            break;

        case SID_SELECTALL:
            {
                sal_Int32 nPar = rEngine.GetParagraphCount();
                if (nPar)
                {
                    sal_Int32 nLen = rEngine.GetTextLen(nPar-1);
                    pTableView->SetSelection(ESelection(0,0,nPar-1,nLen));
                    if (pTopView)
                        pTopView->SetSelection(ESelection(0,0,nPar-1,nLen));
                    rBindings.Invalidate( SID_ATTR_CHAR_FONT );
                    rBindings.Invalidate( SID_ATTR_CHAR_FONTHEIGHT );
                    rBindings.Invalidate( SID_ATTR_CHAR_WEIGHT );
                    rBindings.Invalidate( SID_ATTR_CHAR_POSTURE );
                    rBindings.Invalidate( SID_ATTR_CHAR_UNDERLINE );
                    rBindings.Invalidate( SID_ATTR_CHAR_STRIKEOUT );
                    rBindings.Invalidate( SID_ATTR_CHAR_SHADOWED );
                    rBindings.Invalidate( SID_ATTR_CHAR_KERNING );
                    rBindings.Invalidate( SID_ATTR_CHAR_COLOR );
                    rBindings.Invalidate( SID_SET_SUPER_SCRIPT );
                    rBindings.Invalidate( SID_SET_SUB_SCRIPT );
                }
            }
            return;
        case SID_UNICODE_NOTATION_TOGGLE:
            {
                EditView* pActiveView = pHdl->GetActiveView();
                if( pActiveView )
                {
                    ESelection aSel( pActiveView->GetSelection() );
                    OUString sInput(aSel.HasRange() ? pActiveView->GetSelected()
                                                    : rEngine.GetText());

                    if (aSel.start.nIndex > aSel.end.nIndex)
                        aSel.end.nIndex = aSel.start.nIndex;

                    //calculate a valid end-position by reading logical characters
                    sal_Int32 nUtf16Pos=0;
                    while ((nUtf16Pos < sInput.getLength()) && (nUtf16Pos < aSel.end.nIndex))
                    {
                        sInput.iterateCodePoints(&nUtf16Pos);
                        if (nUtf16Pos > aSel.end.nIndex)
                            aSel.end.nIndex = nUtf16Pos;
                    }

                    ToggleUnicodeCodepoint aToggle;
                    while( nUtf16Pos && aToggle.AllowMoreInput( sInput[nUtf16Pos-1]) )
                        --nUtf16Pos;
                    OUString sReplacement = aToggle.ReplacementString();
                    if( !sReplacement.isEmpty() )
                    {
                        aSel.start.nIndex = aSel.end.nIndex - aToggle.StringToReplace().getLength();
                        pTableView->SetSelection( aSel );
                        pTableView->InsertText(sReplacement, true);
                        if( pTopView )
                        {
                            pTopView->SetSelection( aSel );
                            pTopView->InsertText(sReplacement, true);
                        }
                    }
                }
            }
            break;

        case SID_CHARMAP:
            {
                SvtScriptType nScript = pTableView->GetSelectedScriptType();
                sal_uInt16 nFontWhich = ( nScript == SvtScriptType::ASIAN ) ? EE_CHAR_FONTINFO_CJK :
                                ( ( nScript == SvtScriptType::COMPLEX ) ? EE_CHAR_FONTINFO_CTL :
                                                                        EE_CHAR_FONTINFO );
                auto const attribs = pTableView->GetAttribs();
                const SvxFontItem& rItem = static_cast<const SvxFontItem&>(
                            attribs.Get(nFontWhich));

                OUString aString;
                std::shared_ptr<SvxFontItem> aNewItem(std::make_shared<SvxFontItem>(EE_CHAR_FONTINFO));

                const SfxItemSet *pArgs = rReq.GetArgs();
                const SfxPoolItem* pItem = nullptr;
                if( pArgs )
                    pArgs->GetItemState(SID_CHARMAP, false, &pItem);

                if ( pItem )
                {
                    aString = static_cast<const SfxStringItem*>(pItem)->GetValue();
                    const SfxStringItem* pFontItem = pArgs->GetItemIfSet( SID_ATTR_SPECIALCHAR, false);
                    if ( pFontItem )
                    {
                        const OUString& aFontName(pFontItem->GetValue());
                        vcl::Font aFont(aFontName, Size(1,1)); // Size just because CTOR
                        // tdf#125054 see comment in drtxob.cxx, same ID
                        aNewItem = std::make_shared<SvxFontItem>(
                            aFont.GetFamilyTypeMaybeAskConfig(), aFont.GetFamilyName(),
                            aFont.GetStyleName(), aFont.GetPitchMaybeAskConfig(),
                            aFont.GetCharSet(), ATTR_FONT);
                    }
                    else
                    {
                        aNewItem.reset(rItem.Clone());
                    }

                    // tdf#125054 force Item to correct intended ID
                    aNewItem->SetWhich(EE_CHAR_FONTINFO);
                }
                else
                {
                    ScViewUtil::ExecuteCharMap(rItem, *rViewData.GetViewShell());

                    // while the dialog was open, edit mode may have been stopped
                    if (!ScModule::get()->IsInputMode())
                        return;
                }

                if ( !aString.isEmpty() )
                {
                    //  if string contains WEAK characters, set all fonts
                    SvtScriptType nSetScript;
                    ScDocument& rDoc = rViewData.GetDocument();
                    if ( rDoc.HasStringWeakCharacters( aString ) )
                        nSetScript = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX;
                    else
                        nSetScript = rDoc.GetStringScriptType( aString );

                    SfxItemSet aSet( pTableView->GetEmptyItemSet() );
                    SvxScriptSetItem aSetItem( SID_ATTR_CHAR_FONT, GetPool() );
                    aSetItem.PutItemForScriptType( nSetScript, *aNewItem );
                    aSet.Put( aSetItem.GetItemSet(), false );

                    // SetAttribs on the View selects a word, when nothing is selected
                    pTableView->getEditEngine().QuickSetAttribs( aSet, pTableView->GetSelection() );
                    pTableView->InsertText(aString);
                    if (pTopView)
                        pTopView->InsertText(aString);

                    SfxStringItem aStringItem( SID_CHARMAP, aString );
                    SfxStringItem aFontItem( SID_ATTR_SPECIALCHAR, aNewItem->GetFamilyName() );
                    rReq.AppendItem( aFontItem );
                    rReq.AppendItem( aStringItem );
                    rReq.Done();

                }

                if (vcl::Window* pViewWindow = pTopView ? pTopView->GetWindow() : nullptr)
                    pViewWindow->GrabFocus();
            }
            break;

        case FID_INSERT_NAME:
            {
                ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();

                ScopedVclPtr<AbstractScNamePasteDlg> pDlg(pFact->CreateScNamePasteDlg(rViewData.GetDialogParent(), rViewData.GetDocShell()));
                short nRet = pDlg->Execute();
                // pDlg is needed below

                // while the dialog was open, edit mode may have been stopped
                if (!ScModule::get()->IsInputMode())
                    return;

                if ( nRet == BTN_PASTE_NAME )
                {
                    std::vector<OUString> aNames = pDlg->GetSelectedNames();
                    if (!aNames.empty())
                    {
                        OUStringBuffer aBuffer;
                        for (const auto& rName : aNames)
                        {
                            aBuffer.append(rName + " ");
                        }
                        const OUString s = aBuffer.makeStringAndClear();
                        pTableView->InsertText(s);
                        if (pTopView)
                            pTopView->InsertText(s);
                    }
                }
                pDlg.disposeAndClear();

                if (vcl::Window* pViewWindow = pTopView ? pTopView->GetWindow() : nullptr)
                    pViewWindow->GrabFocus();
            }
            break;

        case SID_CHAR_DLG_EFFECT:
        case SID_CHAR_DLG:
            {
                SfxItemSet aAttrs( pTableView->GetAttribs() );

                SfxObjectShell* pObjSh = rViewData.GetSfxDocShell();

                ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();

                ScopedVclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateScCharDlg(
                    rViewData.GetDialogParent(), &aAttrs, pObjSh, false));
                if (nSlot == SID_CHAR_DLG_EFFECT)
                {
                    pDlg->SetCurPageId(u"fonteffects"_ustr);
                }
                short nRet = pDlg->Execute();
                // pDlg is needed below

                // while the dialog was open, edit mode may have been stopped
                if (!ScModule::get()->IsInputMode())
                    return;

                if ( nRet == RET_OK )
                {
                    const SfxItemSet* pOut = pDlg->GetOutputItemSet();
                    pTableView->SetAttribs( *pOut );
                }
            }
            break;

        case SID_TOGGLE_REL:
            {
                auto PaMToStringIndex = [](const OUString& s, EPaM pam)
                {
                    sal_Int32 index = 0;
                    for (; pam.nPara > 0; --pam.nPara)
                    {
                        index = s.indexOf('\n', index) + 1; // becomes 0 on "not found"
                        if (index <= 0)
                            return s.getLength();
                    }
                    index += pam.nIndex;
                    return std::min(index, s.getLength());
                };
                auto StringIndexToPaM = [](const OUString& s, sal_Int32 index)
                {
                    EPaM pam;
                    for (sal_Int32 i = 0; i < index; ++i)
                    {
                        if (s[i] == '\n')
                        {
                            ++pam.nPara;
                            pam.nIndex = 0;
                        }
                        else
                        {
                            ++pam.nIndex;
                        }
                    }
                    return pam;
                };

                OUString aText = rEngine.GetText(LINEEND_LF);
                ESelection aSel = pEditView->GetSelection(); // current View
                sal_Int32 nStart = PaMToStringIndex(aText, aSel.start);
                sal_Int32 nEnd = PaMToStringIndex(aText, aSel.end);

                ScDocument& rDoc = rViewData.GetDocument();
                ScRefFinder aFinder(aText, rViewData.GetCurPos(), rDoc, rDoc.GetAddressConvention());
                aFinder.ToggleRel(nStart, nEnd);
                if (aFinder.GetFound())
                {
                    const OUString& aNew = aFinder.GetText();
                    ESelection aNewSel(StringIndexToPaM(aNew, aFinder.GetSelStart()));
                    aNewSel.end = StringIndexToPaM(aNew, aFinder.GetSelEnd());
                    rEngine.SetText( aNew );
                    pTableView->SetSelection( aNewSel );
                    if ( pTopView )
                    {
                        pTopView->getEditEngine().SetText( aNew );
                        pTopView->SetSelection( aNewSel );
                    }

                    // reference is being selected -> do not overwrite when typing
                    bSetSelIsRef = true;
                }
            }
            break;

        case SID_HYPERLINK_SETLINK:
            if( pReqArgs )
            {
                const SfxPoolItem* pItem;
                if ( pReqArgs->GetItemState( SID_HYPERLINK_SETLINK, true, &pItem ) == SfxItemState::SET )
                {
                    const SvxHyperlinkItem* pHyper = static_cast<const SvxHyperlinkItem*>(pItem);
                    const OUString& rName     = pHyper->GetName();
                    const OUString& rURL      = pHyper->GetURL();
                    const OUString& rTarget   = pHyper->GetTargetFrame();
                    SvxLinkInsertMode eMode = pHyper->GetInsertMode();

                    bool bCellLinksOnly
                        = (ScModule::get()->GetAppOptions().GetLinksInsertedLikeMSExcel()
                          && rViewData.GetSfxDocShell()->GetMedium()->GetFilter()->IsMSOFormat())
                          || comphelper::LibreOfficeKit::isActive();

                    bool bDone = false;
                    if ( (eMode == HLINK_DEFAULT || eMode == HLINK_FIELD) && !bCellLinksOnly )
                    {
                        std::unique_ptr<const SvxFieldData> aSvxFieldDataPtr(GetURLField());
                        const SvxURLField* pURLField(static_cast<const SvxURLField*>(aSvxFieldDataPtr.get()));
                        if ( pURLField )
                        {
                            // select old field

                            ESelection aSel = pTableView->GetSelection();
                            aSel.Adjust();
                            aSel.end.nPara = aSel.start.nPara;
                            aSel.end.nIndex = aSel.start.nIndex + 1;
                            pTableView->SetSelection( aSel );

                            // insert new field

                            SvxURLField aURLField( rURL, rName, SvxURLFormat::Repr );
                            aURLField.SetTargetFrame( rTarget );
                            SvxFieldItem aURLItem( aURLField, EE_FEATURE_FIELD );
                            pTableView->InsertField( aURLItem );
                            pTableView->SetSelection( aSel );       // select inserted field

                            // now also fields in the Top-View

                            if ( pTopView )
                            {
                                aSel = pTopView->GetSelection();
                                aSel.end.nPara = aSel.start.nPara;
                                aSel.end.nIndex = aSel.start.nIndex + 1;
                                pTopView->SetSelection( aSel );
                                pTopView->InsertField( aURLItem );
                                pTopView->SetSelection( aSel );     // select inserted field
                            }

                            bDone = true;
                        }
                    }

                    if (!bDone)
                    {
                        if (bCellLinksOnly)
                        {
                            sal_Int32 nPar = rEngine.GetParagraphCount();
                            if (nPar)
                            {
                                sal_Int32 nLen = rEngine.GetTextLen(nPar - 1);
                                pTableView->SetSelection(ESelection(0, 0, nPar - 1, nLen));
                                if (pTopView)
                                    pTopView->SetSelection(ESelection(0, 0, nPar - 1, nLen));
                            }
                        }
                        rViewData.GetViewShell()->
                            InsertURL( rName, rURL, rTarget, static_cast<sal_uInt16>(eMode) );

                        // when "Button", the InsertURL in ViewShell turns the EditShell  off
                        // thus the immediate return statement
                        return;
                    }
                }
            }
            break;
        case SID_INSERT_HYPERLINK:
            {
                rViewData.GetViewShell()->GetViewFrame().GetDispatcher()->Execute(
                    SID_HYPERLINK_DIALOG);
            }
        break;
        case SID_EDIT_HYPERLINK:
            {
                // Ensure the field is selected first
                pEditView->SelectFieldAtCursor();
                rViewData.GetViewShell()->GetViewFrame().GetDispatcher()->Execute(
                    SID_HYPERLINK_DIALOG);
            }
        break;
        case SID_COPY_HYPERLINK_LOCATION:
            {
                const SvxFieldItem* pFieldItem
                    = pEditView->GetFieldAtSelection(/*AlsoCheckBeforeCursor=*/true);
                const SvxFieldData* pField = pFieldItem ? pFieldItem->GetField() : nullptr;
                if (const SvxURLField* pURLField = dynamic_cast<const SvxURLField*>(pField))
                {
                    uno::Reference<datatransfer::clipboard::XClipboard> xClipboard
                        = pEditView->GetClipboard();
                    vcl::unohelper::TextDataObject::CopyStringTo(pURLField->GetURL(), xClipboard, SfxViewShell::Current());
                }
            }
        break;
        case SID_REMOVE_HYPERLINK:
            {
                URLFieldHelper::RemoveURLField(*pEditView);
            }
        break;

        case FN_INSERT_SOFT_HYPHEN:
            lclInsertCharacter( pTableView, pTopView, CHAR_SHY );
        break;
        case FN_INSERT_HARDHYPHEN:
            lclInsertCharacter( pTableView, pTopView, CHAR_NBHY );
        break;
        case FN_INSERT_HARD_SPACE:
            lclInsertCharacter( pTableView, pTopView, CHAR_NBSP );
        break;
        case FN_INSERT_NNBSP:
            lclInsertCharacter( pTableView, pTopView, CHAR_NNBSP );
        break;
        case SID_INSERT_RLM:
            lclInsertCharacter( pTableView, pTopView, CHAR_RLM );
        break;
        case SID_INSERT_LRM:
            lclInsertCharacter( pTableView, pTopView, CHAR_LRM );
        break;
        case SID_INSERT_ZWSP:
            lclInsertCharacter( pTableView, pTopView, CHAR_ZWSP );
        break;
        case SID_INSERT_WJ:
            lclInsertCharacter( pTableView, pTopView, CHAR_WJ );
        break;
        case SID_INSERT_FIELD_SHEET:
        {
            SvxTableField aField(rViewData.CurrentTabForData());
            SvxFieldItem aItem(aField, EE_FEATURE_FIELD);
            pTableView->InsertField(aItem);
        }
        break;
        case SID_INSERT_FIELD_TITLE:
        {
            SvxFileField aField;
            SvxFieldItem aItem(aField, EE_FEATURE_FIELD);
            pTableView->InsertField(aItem);
        }
        break;
        case SID_INSERT_FIELD_DATE_VAR:
        {
            SvxDateField aField;
            SvxFieldItem aItem(aField, EE_FEATURE_FIELD);
            pTableView->InsertField(aItem);
        }
        break;
    }

    pHdl->DataChanged(false, bSetModified);
    if (bSetSelIsRef)
        pHdl->SetSelIsRef(true);
}

static void lcl_DisableAll( SfxItemSet& rSet )    // disable all slots
{
    SfxWhichIter aIter( rSet );
    sal_uInt16 nWhich = aIter.FirstWhich();
    while (nWhich)
    {
        rSet.DisableItem( nWhich );
        nWhich = aIter.NextWhich();
    }
}

void ScEditShell::GetState( SfxItemSet& rSet )
{
    // When deactivating the view, edit mode is stopped, but the EditShell is left active
    // (a shell can't be removed from within Deactivate). In that state, the EditView isn't inserted
    // into the EditEngine, so it can have an invalid selection and must not be used.
    ScInputHandler* pHdl = GetMyInputHdl();
    if ( !rViewData.HasEditView( rViewData.GetActivePart() ) )
    {
        lcl_DisableAll( rSet );

        // Some items are actually useful and still applicable when in formula building mode: enable
        if (pHdl && pHdl->IsFormulaMode())
        {
            rSet.ClearItem(SID_TOGGLE_REL); //  F4 Cycle Cell Reference Types
            rSet.ClearItem(SID_CHARMAP); // Insert Special Characters
        }
        return;
    }

    EditView* pActiveView = pHdl ? pHdl->GetActiveView() : pEditView;

    SfxWhichIter aIter( rSet );
    sal_uInt16 nWhich = aIter.FirstWhich();
    while (nWhich)
    {
        switch (nWhich)
        {
            case SID_ATTR_INSERT:   // Status row
                {
                    if ( pActiveView )
                        rSet.Put( SfxBoolItem( nWhich, pActiveView->IsInsertMode() ) );
                    else
                    {
                        // Here the code used to pass the value 42 and it used
                        // to "work" without warnings because the SfxBoolItem
                        // was based on 'sal_Bool', which is actually 'unsigned
                        // char'. But now it uses actual 'bool', and passing 42
                        // for a 'bool' parameter causes a warning at least with
                        // MSVC.  So use 'true'. I really really hope there is
                        // not code somewhere that retrieves this "boolean" item
                        // and checks it value for the magic value 42...
                        rSet.Put( SfxBoolItem( nWhich,  true) );
                    }
                }
                break;

            case SID_HYPERLINK_GETLINK:
                {
                    SvxHyperlinkItem aHLinkItem;
                    aHLinkItem.SetShowName(false);
                    bool bCellLinksOnly
                        = (ScModule::get()->GetAppOptions().GetLinksInsertedLikeMSExcel()
                          && rViewData.GetSfxDocShell()->GetMedium()->GetFilter()->IsMSOFormat())
                          || comphelper::LibreOfficeKit::isActive();
                    std::unique_ptr<const SvxFieldData> aSvxFieldDataPtr(GetURLField());
                    const SvxURLField* pURLField(static_cast<const SvxURLField*>(aSvxFieldDataPtr.get()));
                    if (!bCellLinksOnly)
                    {
                        if (pURLField)
                        {
                            aHLinkItem.SetName(pURLField->GetRepresentation());
                            aHLinkItem.SetURL(pURLField->GetURL());
                            aHLinkItem.SetTargetFrame(pURLField->GetTargetFrame());
                        }
                        else if (pActiveView)
                        {
                            // use selected text as name for urls
                            OUString sReturn = pActiveView->GetSelected();
                            sReturn = sReturn.copy(
                                0, std::min(sReturn.getLength(), static_cast<sal_Int32>(255)));
                            aHLinkItem.SetName(comphelper::string::stripEnd(sReturn, ' '));
                        }
                    }
                    else
                    {
                        if (!pURLField)
                        {
                            aSvxFieldDataPtr = GetFirstURLFieldFromCell();
                            pURLField = static_cast<const SvxURLField*>(aSvxFieldDataPtr.get());
                        }
                        if (pURLField)
                        {
                            aHLinkItem.SetURL(pURLField->GetURL());
                            aHLinkItem.SetTargetFrame(pURLField->GetTargetFrame());
                        }
                        ScDocument& rDoc = rViewData.GetDocument();
                        SCCOL nPosX = rViewData.GetCurX();
                        SCROW nPosY = rViewData.GetCurY();
                        SCTAB nTab = rViewData.CurrentTabForData();
                        aHLinkItem.SetName(rDoc.GetString(nPosX, nPosY, nTab));
                    }
                    rSet.Put(aHLinkItem);
                }
                break;

            case SID_INSERT_HYPERLINK:
                {
                    // Disable insert hyperlink if no text is selected or cursor is at a URL field
                    ESelection aSel( pActiveView->GetSelection() );
                    if (!aSel.HasRange() || URLFieldHelper::IsCursorAtURLField(*pEditView,
                                                            /*AlsoCheckBeforeCursor=*/true))
                        rSet.DisableItem (nWhich);
                }
                break;

            case SID_EDIT_HYPERLINK:
            case SID_COPY_HYPERLINK_LOCATION:
            case SID_REMOVE_HYPERLINK:
                {
                    if (!URLFieldHelper::IsCursorAtURLField(*pEditView,
                                                            /*AlsoCheckBeforeCursor=*/true))
                        rSet.DisableItem (nWhich);
                }
                break;

            case SID_TRANSLITERATE_HALFWIDTH:
            case SID_TRANSLITERATE_FULLWIDTH:
            case SID_TRANSLITERATE_HIRAGANA:
            case SID_TRANSLITERATE_KATAKANA:
            case SID_INSERT_RLM:
            case SID_INSERT_LRM:
                ScViewUtil::HideDisabledSlot( rSet, rViewData.GetBindings(), nWhich );
            break;

            case SID_THES:
                {
                    OUString        aStatusVal;
                    LanguageType    nLang = LANGUAGE_NONE;
                    bool bIsLookUpWord = pActiveView &&
                        GetStatusValueForThesaurusFromContext(aStatusVal, nLang, *pActiveView);
                    rSet.Put( SfxStringItem( SID_THES, aStatusVal ) );

                    // disable thesaurus context menu entry if there is nothing to look up
                    bool bCanDoThesaurus = ScModule::HasThesaurusLanguage( nLang );
                    if (!bIsLookUpWord || !bCanDoThesaurus)
                        rSet.DisableItem( SID_THES );
                }
                break;
            case SID_INSERT_FIELD_SHEET:
            case SID_INSERT_FIELD_TITLE:
            case SID_INSERT_FIELD_DATE_VAR:
            break;
            case SID_COPY:
            case SID_CUT:
                if (GetObjectShell() && GetObjectShell()->isContentExtractionLocked())
                {
                    rSet.DisableItem(SID_COPY);
                    rSet.DisableItem(SID_CUT);
                }
                break;

        }
        nWhich = aIter.NextWhich();
    }
}

std::unique_ptr<const SvxFieldData> ScEditShell::GetURLField()
{
    ScInputHandler* pHdl = GetMyInputHdl();
    EditView* pActiveView = pHdl ? pHdl->GetActiveView() : pEditView;
    if (!pActiveView)
        return std::unique_ptr<const SvxFieldData>();

    const SvxFieldData* pField = pActiveView->GetFieldUnderMouseOrInSelectionOrAtCursor();
    if (auto pURLField = dynamic_cast<const SvxURLField*>(pField))
        return pURLField->Clone();

    return std::unique_ptr<const SvxFieldData>();
}

std::unique_ptr<const SvxFieldData> ScEditShell::GetFirstURLFieldFromCell()
{
    EditEngine& rEditEngine = GetEditView()->getEditEngine();
    sal_Int32 nParaCount = rEditEngine.GetParagraphCount();
    for (sal_Int32 nPara = 0; nPara < nParaCount; ++nPara)
    {
        ESelection aSel(nPara, 0);
        std::vector<sal_Int32> aPosList;
        rEditEngine.GetPortions(nPara, aPosList);
        for (const auto& rPos : aPosList)
        {
            aSel.end.nIndex = rPos;

            SfxItemSet aEditSet(rEditEngine.GetAttribs(aSel));
            if (aSel.start.nIndex + 1 == aSel.end.nIndex)
            {
                // test if the character is a text field
                if (const SvxFieldItem* pItem = aEditSet.GetItemIfSet(EE_FEATURE_FIELD, false))
                {
                    const SvxFieldData* pField = pItem->GetField();
                    if (const SvxURLField* pUrlField = dynamic_cast<const SvxURLField*>(pField))
                    {
                        return pUrlField->Clone();
                    }
                }
            }
            aSel.start.nIndex = aSel.end.nIndex;
        }
    }

    return std::unique_ptr<const SvxFieldData>();
}

IMPL_LINK( ScEditShell, ClipboardChanged, TransferableDataHelper*, pDataHelper, void )
{
    bPastePossible = ( pDataHelper->HasFormat( SotClipboardFormatId::STRING )
            || pDataHelper->HasFormat( SotClipboardFormatId::RTF )
            || pDataHelper->HasFormat( SotClipboardFormatId::RICHTEXT ));

    SfxBindings& rBindings = rViewData.GetBindings();
    rBindings.Invalidate( SID_PASTE );
    rBindings.Invalidate( SID_PASTE_SPECIAL );
    rBindings.Invalidate( SID_PASTE_UNFORMATTED );
    rBindings.Invalidate( SID_CLIPBOARD_FORMAT_ITEMS );
}

void ScEditShell::GetClipState( SfxItemSet& rSet )
{
    // Do not offer SotClipboardFormatId::STRING_TSVC for in-cell paste.

    if ( !mxClipEvtLstnr.is() )
    {
        // create listener
        mxClipEvtLstnr = new TransferableClipboardListener( LINK( this, ScEditShell, ClipboardChanged ) );
        vcl::Window* pWin = rViewData.GetActiveWin();
        mxClipEvtLstnr->AddListener( pWin );

        // get initial state
        TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( rViewData.GetActiveWin() ) );
        bPastePossible = ( aDataHelper.HasFormat( SotClipboardFormatId::STRING )
                || aDataHelper.HasFormat( SotClipboardFormatId::RTF )
                || aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) );
    }

    SfxWhichIter aIter( rSet );
    sal_uInt16 nWhich = aIter.FirstWhich();
    while (nWhich)
    {
        switch (nWhich)
        {
            case SID_PASTE:
            case SID_PASTE_SPECIAL:
            case SID_PASTE_UNFORMATTED:
                if( !bPastePossible )
                    rSet.DisableItem( nWhich );
                break;
            case SID_CLIPBOARD_FORMAT_ITEMS:
                if( bPastePossible )
                {
                    SvxClipboardFormatItem aFormats( SID_CLIPBOARD_FORMAT_ITEMS );
                    TransferableDataHelper aDataHelper(
                            TransferableDataHelper::CreateFromSystemClipboard( rViewData.GetActiveWin() ) );

                    if ( aDataHelper.HasFormat( SotClipboardFormatId::STRING ) )
                        aFormats.AddClipbrdFormat( SotClipboardFormatId::STRING );
                    if ( aDataHelper.HasFormat( SotClipboardFormatId::RTF ) )
                        aFormats.AddClipbrdFormat( SotClipboardFormatId::RTF );

                    rSet.Put( aFormats );
                }
                else
                    rSet.DisableItem( nWhich );
                break;
        }
        nWhich = aIter.NextWhich();
    }
}

static void lcl_InvalidateUnder( SfxBindings& rBindings )
{
    rBindings.Invalidate( SID_ATTR_CHAR_UNDERLINE );
    rBindings.Invalidate( SID_ULINE_VAL_NONE );
    rBindings.Invalidate( SID_ULINE_VAL_SINGLE );
    rBindings.Invalidate( SID_ULINE_VAL_DOUBLE );
    rBindings.Invalidate( SID_ULINE_VAL_DOTTED );
}

void ScEditShell::ExecuteAttr(SfxRequest& rReq)
{
    SfxItemSet          aSet( pEditView->GetEmptyItemSet() );
    SfxBindings&        rBindings   = rViewData.GetBindings();
    const SfxItemSet*   pArgs       = rReq.GetArgs();
    sal_uInt16              nSlot       = rReq.GetSlot();

    switch ( nSlot )
    {
        case SID_ATTR_CHAR_FONTHEIGHT:
        case SID_ATTR_CHAR_FONT:
            {
                if (pArgs)
                {
                    // #i78017 establish the same behaviour as in Writer
                    SvtScriptType nScript = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX;
                    if (nSlot == SID_ATTR_CHAR_FONT)
                    {
                        nScript = pEditView->GetSelectedScriptType();
                        if (nScript == SvtScriptType::NONE) nScript = ScGlobal::GetDefaultScriptType();
                    }

                    SfxItemPool& rPool = GetPool();
                    SvxScriptSetItem aSetItem( nSlot, rPool );
                    sal_uInt16 nWhich = rPool.GetWhichIDFromSlotID( nSlot );
                    aSetItem.PutItemForScriptType( nScript, pArgs->Get( nWhich ) );

                    aSet.Put( aSetItem.GetItemSet(), false );
                }
            }
            break;

        case SID_ATTR_CHAR_COLOR:
        case SID_ATTR_CHAR_BACK_COLOR:
            {
                if (pArgs)
                {
                    aSet.Put( pArgs->Get( pArgs->GetPool()->GetWhichIDFromSlotID( nSlot ) ) );
                    rBindings.Invalidate( nSlot );
                }
                else
                {
                    const sal_uInt16 nEEWhich = GetPool().GetWhichIDFromSlotID(nSlot);
                    const std::optional<NamedColor> oColor
                        = rViewData.GetDocShell()->GetRecentColor(nSlot);
                    if (oColor.has_value())
                    {
                        const model::ComplexColor aCol = (*oColor).getComplexColor();
                        aSet.Put(SvxColorItem(aCol.getFinalColor(), aCol, nEEWhich));
                    }
                }
            }
            break;

        //  Toggles

        case SID_ATTR_CHAR_WEIGHT:
            {
                // #i78017 establish the same behaviour as in Writer
                SvtScriptType nScript = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX;

                SfxItemPool& rPool = GetPool();

                bool bOld = false;
                SvxScriptSetItem aOldSetItem( nSlot, rPool );
                aOldSetItem.GetItemSet().Put( pEditView->GetAttribs(), false );
                const SfxPoolItem* pCore = aOldSetItem.GetItemOfScript( nScript );
                if ( pCore && static_cast<const SvxWeightItem*>(pCore)->GetWeight() > WEIGHT_NORMAL )
                    bOld = true;

                SvxScriptSetItem aSetItem( nSlot, rPool );
                aSetItem.PutItemForScriptType( nScript,
                            SvxWeightItem( bOld ? WEIGHT_NORMAL : WEIGHT_BOLD, EE_CHAR_WEIGHT ) );
                aSet.Put( aSetItem.GetItemSet(), false );

                rBindings.Invalidate( nSlot );
            }
            break;

        case SID_ATTR_CHAR_POSTURE:
            {
                // #i78017 establish the same behaviour as in Writer
                SvtScriptType nScript = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX;

                SfxItemPool& rPool = GetPool();

                bool bOld = false;
                SvxScriptSetItem aOldSetItem( nSlot, rPool );
                aOldSetItem.GetItemSet().Put( pEditView->GetAttribs(), false );
                const SfxPoolItem* pCore = aOldSetItem.GetItemOfScript( nScript );
                if ( pCore && static_cast<const SvxPostureItem*>(pCore)->GetValue() != ITALIC_NONE )
                    bOld = true;

                SvxScriptSetItem aSetItem( nSlot, rPool );
                aSetItem.PutItemForScriptType( nScript,
                            SvxPostureItem( bOld ? ITALIC_NONE : ITALIC_NORMAL, EE_CHAR_ITALIC ) );
                aSet.Put( aSetItem.GetItemSet(), false );

                rBindings.Invalidate( nSlot );
            }
            break;

        case SID_ULINE_VAL_NONE:
            aSet.Put( SvxUnderlineItem( LINESTYLE_NONE, EE_CHAR_UNDERLINE ) );
            lcl_InvalidateUnder( rBindings );
            break;

        case SID_ATTR_CHAR_UNDERLINE:
        case SID_ULINE_VAL_SINGLE:
        case SID_ULINE_VAL_DOUBLE:
        case SID_ULINE_VAL_DOTTED:
            {
                FontLineStyle eOld = pEditView->GetAttribs().Get(EE_CHAR_UNDERLINE).GetLineStyle();
                FontLineStyle eNew = eOld;
                switch (nSlot)
                {
                    case SID_ATTR_CHAR_UNDERLINE:
                        if ( pArgs )
                        {
                            const SvxTextLineItem& rTextLineItem = static_cast< const SvxTextLineItem& >( pArgs->Get( pArgs->GetPool()->GetWhichIDFromSlotID(nSlot) ) );
                            eNew = rTextLineItem.GetLineStyle();
                        }
                        else
                        {
                            eNew = ( eOld != LINESTYLE_NONE ) ? LINESTYLE_NONE : LINESTYLE_SINGLE;
                        }
                        break;
                    case SID_ULINE_VAL_SINGLE:
                        eNew = ( eOld == LINESTYLE_SINGLE ) ? LINESTYLE_NONE : LINESTYLE_SINGLE;
                        break;
                    case SID_ULINE_VAL_DOUBLE:
                        eNew = ( eOld == LINESTYLE_DOUBLE ) ? LINESTYLE_NONE : LINESTYLE_DOUBLE;
                        break;
                    case SID_ULINE_VAL_DOTTED:
                        eNew = ( eOld == LINESTYLE_DOTTED ) ? LINESTYLE_NONE : LINESTYLE_DOTTED;
                        break;
                }
                aSet.Put( SvxUnderlineItem( eNew, EE_CHAR_UNDERLINE ) );
                lcl_InvalidateUnder( rBindings );
            }
            break;

        case SID_ATTR_CHAR_OVERLINE:
            {
                FontLineStyle eOld = pEditView->GetAttribs().Get(EE_CHAR_OVERLINE).GetLineStyle();
                FontLineStyle eNew = ( eOld != LINESTYLE_NONE ) ? LINESTYLE_NONE : LINESTYLE_SINGLE;
                aSet.Put( SvxOverlineItem( eNew, EE_CHAR_OVERLINE ) );
                rBindings.Invalidate( nSlot );
            }
            break;

        case SID_ATTR_CHAR_STRIKEOUT:
            {
                bool bOld = pEditView->GetAttribs().Get(EE_CHAR_STRIKEOUT).GetValue() != STRIKEOUT_NONE;
                aSet.Put( SvxCrossedOutItem( bOld ? STRIKEOUT_NONE : STRIKEOUT_SINGLE, EE_CHAR_STRIKEOUT ) );
                rBindings.Invalidate( nSlot );
            }
            break;

        case SID_ATTR_CHAR_SHADOWED:
            {
                bool bOld = pEditView->GetAttribs().Get(EE_CHAR_SHADOW).GetValue();
                aSet.Put( SvxShadowedItem( !bOld, EE_CHAR_SHADOW ) );
                rBindings.Invalidate( nSlot );
            }
            break;

        case SID_ATTR_CHAR_CONTOUR:
            {
                bool bOld = pEditView->GetAttribs().Get(EE_CHAR_OUTLINE).GetValue();
                aSet.Put( SvxContourItem( !bOld, EE_CHAR_OUTLINE ) );
                rBindings.Invalidate( nSlot );
            }
            break;

        case SID_SET_SUPER_SCRIPT:
            {
                SvxEscapement eOld = pEditView->GetAttribs().Get(EE_CHAR_ESCAPEMENT).GetEscapement();
                SvxEscapement eNew = (eOld == SvxEscapement::Superscript) ?
                                        SvxEscapement::Off : SvxEscapement::Superscript;
                aSet.Put( SvxEscapementItem( eNew, EE_CHAR_ESCAPEMENT ) );
                rBindings.Invalidate( nSlot );
            }
            break;
        case SID_SET_SUB_SCRIPT:
            {
                SvxEscapement eOld = pEditView->GetAttribs().Get(EE_CHAR_ESCAPEMENT).GetEscapement();
                SvxEscapement eNew = (eOld == SvxEscapement::Subscript) ?
                                        SvxEscapement::Off : SvxEscapement::Subscript;
                aSet.Put( SvxEscapementItem( eNew, EE_CHAR_ESCAPEMENT ) );
                rBindings.Invalidate( nSlot );
            }
            break;
        case SID_ATTR_CHAR_KERNING:
            {
                if(pArgs)
                {
                    aSet.Put ( pArgs->Get(pArgs->GetPool()->GetWhichIDFromSlotID(nSlot)));
                    rBindings.Invalidate( nSlot );
                }
            }
            break;

        case SID_GROW_FONT_SIZE:
        case SID_SHRINK_FONT_SIZE:
            {
                SfxObjectShell* pObjSh = SfxObjectShell::Current();
                const SvxFontListItem* pFontListItem = static_cast<const SvxFontListItem*>
                        (pObjSh ? pObjSh->GetItem(SID_ATTR_CHAR_FONTLIST) : nullptr);
                const FontList* pFontList = pFontListItem ? pFontListItem->GetFontList() : nullptr;
                pEditView->ChangeFontSize( nSlot == SID_GROW_FONT_SIZE, pFontList );
                rBindings.Invalidate( SID_ATTR_CHAR_FONTHEIGHT );
            }
            break;
    }

    // apply

    EditEngine& rEngine = pEditView->getEditEngine();
    bool bOld = rEngine.SetUpdateLayout(false);

    pEditView->SetAttribs( aSet );

    rEngine.SetUpdateLayout(bOld);
    pEditView->Invalidate();

    ScInputHandler* pHdl = GetMyInputHdl();
    pHdl->SetModified();

    rReq.Done();
}

void ScEditShell::GetAttrState(SfxItemSet &rSet)
{
    if ( !rViewData.HasEditView( rViewData.GetActivePart() ) )
    {
        lcl_DisableAll( rSet );
        return;
    }

    SfxItemSet aAttribs = pEditView->GetAttribs();
    rSet.Put( aAttribs );

    //  choose font info according to selection script type

    SvtScriptType nScript = pEditView->GetSelectedScriptType();
    if (nScript == SvtScriptType::NONE) nScript = ScGlobal::GetDefaultScriptType();

    // #i55929# input-language-dependent script type (depends on input language if nothing selected)
    SvtScriptType nInputScript = nScript;
    if ( !pEditView->GetSelection().HasRange() )
    {
        LanguageType nInputLang = rViewData.GetActiveWin()->GetInputLanguage();
        if (nInputLang != LANGUAGE_DONTKNOW && nInputLang != LANGUAGE_SYSTEM)
            nInputScript = SvtLanguageOptions::GetScriptTypeOfLanguage( nInputLang );
    }

    // #i55929# according to spec, nInputScript is used for font and font height only
    if ( rSet.GetItemState( EE_CHAR_FONTINFO ) != SfxItemState::UNKNOWN )
        ScViewUtil::PutItemScript( rSet, aAttribs, EE_CHAR_FONTINFO, nInputScript );
    if ( rSet.GetItemState( EE_CHAR_FONTHEIGHT ) != SfxItemState::UNKNOWN )
        ScViewUtil::PutItemScript( rSet, aAttribs, EE_CHAR_FONTHEIGHT, nInputScript );
    if ( rSet.GetItemState( EE_CHAR_WEIGHT ) != SfxItemState::UNKNOWN )
        ScViewUtil::PutItemScript( rSet, aAttribs, EE_CHAR_WEIGHT, nScript );
    if ( rSet.GetItemState( EE_CHAR_ITALIC ) != SfxItemState::UNKNOWN )
        ScViewUtil::PutItemScript( rSet, aAttribs, EE_CHAR_ITALIC, nScript );

    // underline
    SfxItemState eState = aAttribs.GetItemState( EE_CHAR_UNDERLINE );
    if ( eState == SfxItemState::INVALID )
    {
        rSet.InvalidateItem( SID_ULINE_VAL_NONE );
        rSet.InvalidateItem( SID_ULINE_VAL_SINGLE );
        rSet.InvalidateItem( SID_ULINE_VAL_DOUBLE );
        rSet.InvalidateItem( SID_ULINE_VAL_DOTTED );
    }
    else
    {
        FontLineStyle eUnderline = aAttribs.Get(EE_CHAR_UNDERLINE).GetLineStyle();
        rSet.Put(SfxBoolItem(SID_ULINE_VAL_SINGLE, eUnderline == LINESTYLE_SINGLE));
        rSet.Put(SfxBoolItem(SID_ULINE_VAL_DOUBLE, eUnderline == LINESTYLE_DOUBLE));
        rSet.Put(SfxBoolItem(SID_ULINE_VAL_DOTTED, eUnderline == LINESTYLE_DOTTED));
        rSet.Put(SfxBoolItem(SID_ULINE_VAL_NONE, eUnderline == LINESTYLE_NONE));
    }

    //! Testing whether brace highlighting is active !!!!
    ScInputHandler* pHdl = GetMyInputHdl();
    if ( pHdl && pHdl->IsFormulaMode() )
        rSet.ClearItem( EE_CHAR_WEIGHT );   // Highlighted brace not here

    SvxEscapement eEsc = aAttribs.Get(EE_CHAR_ESCAPEMENT).GetEscapement();
    rSet.Put(SfxBoolItem(SID_SET_SUPER_SCRIPT, eEsc == SvxEscapement::Superscript));
    rSet.Put(SfxBoolItem(SID_SET_SUB_SCRIPT, eEsc == SvxEscapement::Subscript));
    rViewData.GetBindings().Invalidate( SID_SET_SUPER_SCRIPT );
    rViewData.GetBindings().Invalidate( SID_SET_SUB_SCRIPT );

    eState = aAttribs.GetItemState( EE_CHAR_KERNING );
    rViewData.GetBindings().Invalidate( SID_ATTR_CHAR_KERNING );
    if ( eState == SfxItemState::INVALID )
    {
        rSet.InvalidateItem(EE_CHAR_KERNING);
    }
}

OUString ScEditShell::GetSelectionText( bool bWholeWord )
{
    OUString aStrSelection;

    if ( rViewData.HasEditView( rViewData.GetActivePart() ) )
    {
        if ( bWholeWord )
        {
            EditEngine& rEngine = pEditView->getEditEngine();
            ESelection  aSel = pEditView->GetSelection();
            OUString    aStrCurrentDelimiters = rEngine.GetWordDelimiters();

            rEngine.SetWordDelimiters(u" .,;\"'"_ustr);
            aStrSelection = rEngine.GetWord(aSel.end);
            rEngine.SetWordDelimiters( aStrCurrentDelimiters );
        }
        else
        {
            aStrSelection = pEditView->GetSelected();
        }
    }

    return aStrSelection;
}

void ScEditShell::ExecuteUndo(const SfxRequest& rReq)
{
    //  Undo must be handled here because it's called for both EditViews

    ScInputHandler* pHdl = GetMyInputHdl();
    OSL_ENSURE(pHdl,"no ScInputHandler");
    EditView* pTopView   = pHdl->GetTopView();
    EditView* pTableView = pHdl->GetTableView();
    OSL_ENSURE(pTableView,"no EditView");

    pHdl->DataChanging();

    const SfxItemSet* pReqArgs = rReq.GetArgs();
    sal_uInt16 nSlot = rReq.GetSlot();
    switch ( nSlot )
    {
        case SID_UNDO:
        case SID_REDO:
            {
                bool bIsUndo = ( nSlot == SID_UNDO );

                sal_uInt16 nCount = 1;
                const SfxPoolItem* pItem;
                if ( pReqArgs && pReqArgs->GetItemState( nSlot, true, &pItem ) == SfxItemState::SET )
                    nCount = static_cast<const SfxUInt16Item*>(pItem)->GetValue();

                for (sal_uInt16 i=0; i<nCount; i++)
                {
                    if ( bIsUndo )
                    {
                        pTableView->Undo();
                        if (pTopView)
                            pTopView->Undo();
                    }
                    else
                    {
                        pTableView->Redo();
                        if (pTopView)
                            pTopView->Redo();
                    }
                }
            }
            break;
    }
    rViewData.GetBindings().InvalidateAll(false);

    pHdl->DataChanged();
}

void ScEditShell::GetUndoState(SfxItemSet &rSet)
{
    //  Undo state is taken from normal ViewFrame state function

    SfxViewFrame& rViewFrm = rViewData.GetViewShell()->GetViewFrame();
    if ( GetUndoManager() )
    {
        SfxWhichIter aIter(rSet);
        sal_uInt16 nWhich = aIter.FirstWhich();
        while( nWhich )
        {
            rViewFrm.GetSlotState( nWhich, nullptr, &rSet );
            nWhich = aIter.NextWhich();
        }
    }

    //  disable if no action in input line EditView

    ScInputHandler* pHdl = GetMyInputHdl();
    OSL_ENSURE(pHdl,"no ScInputHandler");
    EditView* pTopView = pHdl->GetTopView();
    if (pTopView)
    {
        SfxUndoManager& rTopMgr = pTopView->getEditEngine().GetUndoManager();
        if ( rTopMgr.GetUndoActionCount() == 0 )
            rSet.DisableItem( SID_UNDO );
        if ( rTopMgr.GetRedoActionCount() == 0 )
            rSet.DisableItem( SID_REDO );
    }
}

void ScEditShell::ExecuteTrans( const SfxRequest& rReq )
{
    TransliterationFlags nType = ScViewUtil::GetTransliterationType( rReq.GetSlot() );
    if ( nType == TransliterationFlags::NONE )
        return;

    ScInputHandler* pHdl = GetMyInputHdl();
    assert(pHdl && "no ScInputHandler");

    EditView* pTopView   = pHdl->GetTopView();
    EditView* pTableView = pHdl->GetTableView();
    assert(pTableView && "no EditView");

    pHdl->DataChanging();

    pTableView->TransliterateText( nType );
    if (pTopView)
        pTopView->TransliterateText( nType );

    pHdl->DataChanged();
}

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