/* -*- 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 <limits.h>
#include <hintids.hxx>
#include <sfx2/bindings.hxx>
#include <sfx2/viewfrm.hxx>
#include <svl/eitem.hxx>
#include <svl/macitem.hxx>
#include <unotools/charclass.hxx>
#include <sfx2/event.hxx>
#include <osl/diagnose.h>
#include <cmdid.h>
#include <view.hxx>
#include <basesh.hxx>
#include <wrtsh.hxx>
#include <frmatr.hxx>
#include <mdiexp.hxx>
#include <fmtcol.hxx>
#include <frmfmt.hxx>
#include <swdtflvr.hxx>
#include <doc.hxx>
#include <wordcountdialog.hxx>
#include <memory>
#include <vcl/uitest/logger.hxx>
#include <vcl/uitest/eventdescription.hxx>
 
#include <vcl/weld.hxx>
#include <vcl/builder.hxx>
#include <officecfg/Office/Common.hxx>
#include <unotools/configmgr.hxx>
#include <bitmaps.hlst>
 
#include <svx/svdview.hxx>
 
namespace com::sun::star::util {
    struct SearchOptions2;
}
 
using namespace ::com::sun::star::util;
 
static tools::Long nStartDragX = 0, nStartDragY = 0;
static bool  bStartDrag = false;
 
void SwWrtShell::Invalidate()
{
    // to avoid making the slot volatile, invalidate it every time if something could have been changed
    // this is still much cheaper than asking for the state every 200 ms (and avoid background processing)
    GetView().GetViewFrame().GetBindings().Invalidate( FN_STAT_SELMODE );
    GetView().GetViewFrame().GetBindings().Update(FN_STAT_SELMODE); // make selection mode control icon update immediately
    SwWordCountWrapper *pWrdCnt = static_cast<SwWordCountWrapper*>(GetView().GetViewFrame().GetChildWindow(SwWordCountWrapper::GetChildWindowId()));
    if (pWrdCnt)
        pWrdCnt->UpdateCounts();
}
 
bool SwWrtShell::SelNearestWrd()
{
    SwMvContext aMvContext(this);
    if( !IsInWord() && !IsEndWrd() && !IsStartWord() )
        PrvWrd();
    if( IsEndWrd() )
        Left(SwCursorSkipMode::Cells, false, 1, false );
    return SelWrd();
}
 
bool SwWrtShell::SelWrd(const Point *pPt, sal_Int16 nWordType )
{
    bool bRet;
    {
        SwMvContext aMvContext(this);
        SttSelect();
        bRet = SwCursorShell::SelectWordWT( pPt, nWordType );
    }
    EndSelect();
    if( bRet )
    {
        m_bSelWrd = true;
        if(pPt)
            m_aStart = *pPt;
    }
    return bRet;
}
 
void SwWrtShell::SelSentence(const Point *pPt )
{
    {
        SwMvContext aMvContext(this);
        ClearMark();
        SwCursorShell::GoStartSentence();
        SttSelect();
        SwCursorShell::GoEndSentence();
    }
    EndSelect();
    if(pPt)
        m_aStart = *pPt;
    m_bSelLn = true;
    m_bSelWrd = false;  // disable SelWord, otherwise no SelLine goes on
}
 
void SwWrtShell::SelPara(const Point *pPt )
{
    {
        SwMvContext aMvContext(this);
        ClearMark();
        SwCursorShell::MovePara( GoCurrPara, fnParaStart );
        SttSelect();
        SwCursorShell::MovePara( GoCurrPara, fnParaEnd );
    }
    EndSelect();
    if(pPt)
        m_aStart = *pPt;
    m_bSelLn = false;
    m_bSelWrd = false;  // disable SelWord, otherwise no SelLine goes on
}
 
void SwWrtShell::SelAll()
{
    const bool bLockedView = IsViewLocked();
    LockView( true );
    {
        if(m_bBlockMode)
            LeaveBlockMode();
        SwMvContext aMvContext(this);
        bool bMoveTable = false;
        std::optional<SwPosition> oStartPos;
        std::optional<SwPosition> oEndPos;
        SwShellCursor* pTmpCursor = nullptr;
 
        // Query these early, before we move the cursor.
        bool bHasWholeTabSelection = HasWholeTabSelection();
        bool bIsCursorInTable = IsCursorInTable();
 
        if (!bHasWholeTabSelection
            && (   !bIsCursorInTable
                || getShellCursor(false)->GetMarkNode().FindTableNode() == nullptr
                || !ExtendedSelectedAll())) // ESA inside table -> else branch
        {
            if ( IsSelection() && IsCursorPtAtEnd() )
                SwapPam();
            pTmpCursor = getShellCursor( false );
            if( pTmpCursor )
            {
                oStartPos.emplace( *pTmpCursor->GetPoint() );
                oEndPos.emplace( *pTmpCursor->GetMark() );
            }
            Push();
            bool bIsFullSel = !MoveSection( GoCurrSection, fnSectionStart);
            SwapPam();
            bIsFullSel &= !MoveSection( GoCurrSection, fnSectionEnd);
            Pop(SwCursorShell::PopMode::DeleteCurrent);
            GoStart(true, &bMoveTable, false, !bIsFullSel);
            SttSelect();
            GoEnd(true, &bMoveTable);
        }
        else
        {
            if (MoveOutOfTable())
            {   // select outer text
                EnterStdMode(); // delete m_pTableCursor
//                GoStart(true, &bMoveTable, false, true);
                MoveSection(GoCurrSection, fnSectionStart); // don't move into prev table
                SttSelect();
                MoveSection(GoCurrSection, fnSectionEnd); // don't move to different cell
            }
            else
            {
                TrySelectOuterTable();
            }
        }
 
        bool bNeedsExtendedSelectAll = StartsWith_() != StartsWith::None;
 
        // the GoEnd() could have created a table selection, if so avoid ESA.
        if (bNeedsExtendedSelectAll && bIsCursorInTable)
        {
            bNeedsExtendedSelectAll = !HasWholeTabSelection();
        }
 
        if (bNeedsExtendedSelectAll)
        {
            ExtendedSelectAll(/*bFootnotes =*/ false);
        }
 
        SwDoc *pDoc = GetDoc();
        if ( pDoc )
        {
            pDoc->SetPrepareSelAll();
        }
 
        if( oStartPos )
        {
            pTmpCursor = getShellCursor( false );
            if( pTmpCursor )
            {
                // Some special handling for sections (e.g. TOC) at the beginning of the document body
                // to avoid the selection of the first section
                // if the last selection was behind the first section or
                // if the last selection was already the first section
                // In this both cases we select to the end of document
                if( ( *pTmpCursor->GetPoint() < *oEndPos ||
                    ( *oStartPos == *pTmpCursor->GetMark() &&
                      *oEndPos == *pTmpCursor->GetPoint() ) ) && !bNeedsExtendedSelectAll)
                    SwCursorShell::SttEndDoc(false);
            }
        }
    }
    EndSelect();
    LockView( bLockedView );
}
 
// Description: Text search
 
sal_Int32 SwWrtShell::SearchPattern( const i18nutil::SearchOptions2& rSearchOpt, bool bSearchInNotes,
                                SwDocPositions eStt, SwDocPositions eEnd,
                                FindRanges eFlags, bool bReplace )
{
        // no enhancement of existing selections
    if(!(eFlags & FindRanges::InSel))
        ClearMark();
    bool bCancel = false;
    sal_Int32 nRet = Find_Text(rSearchOpt, bSearchInNotes, eStt, eEnd, bCancel, eFlags, bReplace);
    if(bCancel)
    {
        Undo();
        nRet = SAL_MAX_INT32;
    }
    return nRet;
}
 
// Description: search for templates
 
sal_Int32 SwWrtShell::SearchTempl( const OUString &rTempl,
                               SwDocPositions eStt, SwDocPositions eEnd,
                               FindRanges eFlags, const OUString* pReplTempl )
{
        // no enhancement of existing selections
    if(!(eFlags & FindRanges::InSel))
        ClearMark();
    SwTextFormatColl *pColl = GetParaStyle(rTempl, SwWrtShell::GETSTYLE_CREATESOME);
    SwTextFormatColl *pReplaceColl = nullptr;
    if( pReplTempl )
        pReplaceColl = GetParaStyle(*pReplTempl, SwWrtShell::GETSTYLE_CREATESOME );
 
    bool bCancel = false;
    sal_Int32 nRet = FindFormat(pColl ? *pColl : GetDfltTextFormatColl(),
                               eStt,eEnd, bCancel, eFlags, pReplaceColl);
    if(bCancel)
    {
        Undo();
        nRet = SAL_MAX_INT32;
    }
    return nRet;
}
 
// search for attributes
 
sal_Int32 SwWrtShell::SearchAttr( const SfxItemSet& rFindSet, bool bNoColls,
                                SwDocPositions eStart, SwDocPositions eEnd,
                                FindRanges eFlags, const i18nutil::SearchOptions2* pSearchOpt,
                                const SfxItemSet* pReplaceSet )
{
    // no enhancement of existing selections
    if (!(eFlags & FindRanges::InSel))
        ClearMark();
 
    // Searching
    bool bCancel = false;
    sal_Int32 nRet = FindAttrs(rFindSet, bNoColls, eStart, eEnd, bCancel, eFlags, pSearchOpt, pReplaceSet);
 
    if(bCancel)
    {
        Undo();
        nRet = SAL_MAX_INT32;
    }
    return nRet;
}
 
// Selection modes
 
void SwWrtShell::PushMode()
{
    m_pModeStack = new ModeStack( m_pModeStack, m_bIns, m_bExtMode, m_bAddMode, m_bBlockMode );
}
 
void SwWrtShell::PopMode()
{
    if ( nullptr == m_pModeStack )
        return;
 
    if ( m_bExtMode && !m_pModeStack->bExt )
        LeaveExtMode();
    if ( m_bAddMode && !m_pModeStack->bAdd )
        LeaveAddMode();
    if ( m_bBlockMode && !m_pModeStack->bBlock )
        LeaveBlockMode();
    m_bIns = m_pModeStack->bIns;
 
    m_pModeStack = std::move(m_pModeStack->pNext);
}
 
// Two methods for setting cursors: the first maps at the
// eponymous methods in the CursorShell, the second removes
// all selections at first.
 
tools::Long SwWrtShell::SetCursor(const Point *pPt, bool bTextOnly)
{
        // Remove a possibly present selection at the position
        // of the mouseclick
 
    if(!IsInSelect() && TestCurrPam(*pPt)) {
        ClearMark();
    }
 
    return SwCursorShell::SetCursor(*pPt, bTextOnly);
}
 
tools::Long SwWrtShell::SetCursorKillSel(const Point *pPt, bool bTextOnly )
{
    SwActContext aActContext(this);
    ResetSelect(pPt,false);
    return SwCursorShell::SetCursor(*pPt, bTextOnly);
}
 
void SwWrtShell::UnSelectFrame()
{
    // Remove Frame selection with guaranteed invalid position
    Point aPt(LONG_MIN, LONG_MIN);
    SelectObj(aPt);
    SwTransferable::ClearSelection( *this );
}
 
// Remove of all selections
 
tools::Long SwWrtShell::ResetSelect(const Point *,bool)
{
    if(IsSelFrameMode())
    {
        UnSelectFrame();
        LeaveSelFrameMode();
    }
    else
    {
        //  SwActContext opens an Action -
        //  to avoid problems in the basic process with the
        //  shell switching, GetChgLnk().Call() may be called
        //  after EndAction().
        {
            SwActContext aActContext(this);
            m_bSelWrd = m_bSelLn = false;
            KillPams();
            ClearMark();
            m_fnKillSel = &SwWrtShell::Ignore;
            m_fnSetCursor = &SwWrtShell::SetCursor;
        }
 
        // After canceling of all selections an update of Attr-Controls
        // could be necessary.
        GetChgLnk().Call(nullptr);
 
        if ( GetEnhancedTableSelection() != SwTable::SEARCH_NONE )
            UnsetEnhancedTableSelection();
    }
    Invalidate();
    SwTransferable::ClearSelection( *this );
    return 1;
}
 
bool SwWrtShell::IsSplitVerticalByDefault() const
{
    return GetDoc()->IsSplitVerticalByDefault();
}
 
void SwWrtShell::SetSplitVerticalByDefault(bool value)
{
    GetDoc()->SetSplitVerticalByDefault(value);
}
 
// Do nothing
 
tools::Long SwWrtShell::Ignore(const Point *, bool ) {
    return 1;
}
 
// Begin of a selection process.
 
void SwWrtShell::SttSelect()
{
    if(m_bInSelect)
        return;
    if(!HasMark())
        SetMark();
    if( m_bBlockMode )
    {
        SwShellCursor* pTmp = getShellCursor( true );
        if( !pTmp->HasMark() )
            pTmp->SetMark();
    }
    m_fnKillSel = &SwWrtShell::Ignore;
    m_fnSetCursor = &SwWrtShell::SetCursor;
    m_bInSelect = true;
    Invalidate();
    SwTransferable::CreateSelection( *this );
}
 
namespace {
 
void collectUIInformation(SwShellCursor* pCursor)
{
    EventDescription aDescription;
    OUString aSelStart = OUString::number(pCursor->Start()->GetContentIndex());
    OUString aSelEnd = OUString::number(pCursor->End()->GetContentIndex());
 
    aDescription.aParameters = {{"START_POS", aSelStart}, {"END_POS", aSelEnd}};
    aDescription.aAction = "SELECT";
    aDescription.aID = "writer_edit";
    aDescription.aKeyWord = "SwEditWinUIObject";
    aDescription.aParent = "MainWindow";
 
    UITestLogger::getInstance().logEvent(aDescription);
}
 
}
 
// End of a selection process.
 
void SwWrtShell::EndSelect()
{
    if(m_bInSelect && !m_bExtMode)
    {
        m_bInSelect = false;
        if (m_bAddMode)
        {
            AddLeaveSelect();
        }
        else
        {
            SttLeaveSelect();
            m_fnSetCursor = &SwWrtShell::SetCursorKillSel;
            m_fnKillSel = &SwWrtShell::ResetSelect;
        }
    }
    SwWordCountWrapper *pWrdCnt = static_cast<SwWordCountWrapper*>(GetView().GetViewFrame().GetChildWindow(SwWordCountWrapper::GetChildWindowId()));
    if (pWrdCnt)
        pWrdCnt->UpdateCounts();
 
    collectUIInformation(GetCursor_());
}
 
void SwWrtShell::ExtSelWrd(const Point *pPt, bool )
{
    SwMvContext aMvContext(this);
    if( IsTableMode() )
        return;
 
    // Bug 66823: actual crsr has in additional mode no selection?
    // Then destroy the actual and go to prev, this will be expand
    if( !HasMark() && GoPrevCursor() )
    {
        bool bHasMark = HasMark(); // that's wrong!
        GoNextCursor();
        if( bHasMark )
        {
            DestroyCursor();
            GoPrevCursor();
        }
    }
 
    // check the direction of the selection with the new point
    bool bMoveCursor = true, bToTop = false;
    SwCursorShell::SelectWord( &m_aStart );     // select the startword
    SwCursorShell::Push();                    // save the cursor
    SwCursorShell::SetCursor( *pPt );           // and check the direction
 
    switch( SwCursorShell::CompareCursorStackMkCurrPt())
    {
    case -1:    bToTop = false;     break;
    case 1:     bToTop = true;      break;
    default:    bMoveCursor = false;  break;
    }
 
    SwCursorShell::Pop(SwCursorShell::PopMode::DeleteCurrent); // restore the saved cursor
 
    if( !bMoveCursor )
        return;
 
    // select to Top but cursor select to Bottom? or
    // select to Bottom but cursor select to Top?       --> swap the cursor
    if( bToTop )
        SwapPam();
 
    SwCursorShell::Push();                // save cur cursor
    if( SwCursorShell::SelectWord( pPt )) // select the current word
    {
        if( bToTop )
            SwapPam();
        Combine();
    }
    else
    {
        SwCursorShell::Pop(SwCursorShell::PopMode::DeleteCurrent);
        if( bToTop )
            SwapPam();
    }
}
 
void SwWrtShell::ExtSelLn(const Point *pPt, bool )
{
    SwMvContext aMvContext(this);
    SwCursorShell::SetCursor(*pPt);
    if( IsTableMode() )
        return;
 
    // Bug 66823: actual crsr has in additional mode no selection?
    // Then destroy the actual and go to prev, this will be expand
    if( !HasMark() && GoPrevCursor() )
    {
        bool bHasMark = HasMark(); // that's wrong!
        GoNextCursor();
        if( bHasMark )
        {
            DestroyCursor();
            GoPrevCursor();
        }
    }
 
    // if applicable fit the selection to the "Mark"
    bool bToTop = !IsCursorPtAtEnd();
    SwapPam();
 
    // The "Mark" has to be at the end or the beginning of the line.
    if( bToTop ? !IsEndSentence() : !IsStartSentence() )
    {
        if( bToTop )
        {
            if( !IsEndPara() )
                SwCursorShell::Right(1,SwCursorSkipMode::Chars);
            SwCursorShell::GoEndSentence();
        }
        else
            SwCursorShell::GoStartSentence();
    }
    SwapPam();
 
    if (bToTop)
        SwCursorShell::GoStartSentence();
    else
        SwCursorShell::GoEndSentence();
}
 
// Back into the standard mode: no mode, no selections.
 
void SwWrtShell::EnterStdMode()
{
    if(m_bAddMode)
        LeaveAddMode();
    if(m_bBlockMode)
        LeaveBlockMode();
    m_bBlockMode = false;
    m_bExtMode = false;
    m_bInSelect = false;
    if(IsSelFrameMode())
    {
        UnSelectFrame();
        LeaveSelFrameMode();
    }
    else
    {
        // SwActContext opens and action which has to be
        // closed prior to the call of
        // GetChgLnk().Call()
        SwActContext aActContext(this);
        m_bSelWrd = m_bSelLn = false;
        if( !IsRetainSelection() )
            KillPams();
        ClearMark();
        m_fnSetCursor = &SwWrtShell::SetCursorKillSel;
        m_fnKillSel = &SwWrtShell::ResetSelect;
    }
    Invalidate();
    SwTransferable::ClearSelection( *this );
}
 
void SwWrtShell::AssureStdMode()
{
    // deselect any drawing or frame and leave editing mode
    if (SdrView* pSdrView = GetDrawView())
    {
        if (pSdrView->IsTextEdit())
        {
            bool bLockView = IsViewLocked();
            LockView(true);
            EndTextEdit();
            LockView(bLockView);
        }
        // go out of the frame
        Point aPt(LONG_MIN, LONG_MIN);
        SelectObj(aPt, SW_LEAVE_FRAME);
    }
    if (IsSelFrameMode() || IsObjSelected())
    {
        UnSelectFrame();
        LeaveSelFrameMode();
        GetView().LeaveDrawCreate();
        EnterStdMode();
        DrawSelChanged();
        GetView().StopShellTimer();
    }
    else
        EnterStdMode();
}
 
// Extended Mode
 
void SwWrtShell::EnterExtMode()
{
    if(m_bBlockMode)
    {
        LeaveBlockMode();
        KillPams();
        ClearMark();
    }
    m_bExtMode = true;
    m_bAddMode = false;
    m_bBlockMode = false;
    SttSelect();
}
 
void SwWrtShell::LeaveExtMode()
{
    m_bExtMode = false;
    EndSelect();
}
 
// End of a selection; if the selection is empty,
// ClearMark().
 
void SwWrtShell::SttLeaveSelect()
{
    if(SwCursorShell::HasSelection() && !IsSelTableCells() && m_bClearMark) {
        return;
    }
    ClearMark();
}
 
// Leaving of the selection mode in additional mode
 
void SwWrtShell::AddLeaveSelect()
{
    if(IsTableMode()) LeaveAddMode();
    else if(SwCursorShell::HasSelection())
        CreateCursor();
}
 
// Additional Mode
 
void SwWrtShell::EnterAddMode()
{
    if(IsTableMode()) return;
    if(m_bBlockMode)
        LeaveBlockMode();
    m_fnKillSel = &SwWrtShell::Ignore;
    m_fnSetCursor = &SwWrtShell::SetCursor;
    m_bAddMode = true;
    m_bBlockMode = false;
    m_bExtMode = false;
    if(SwCursorShell::HasSelection())
        CreateCursor();
    Invalidate();
}
 
void SwWrtShell::LeaveAddMode()
{
    m_fnKillSel = &SwWrtShell::ResetSelect;
    m_fnSetCursor = &SwWrtShell::SetCursorKillSel;
    m_bAddMode = false;
    Invalidate();
}
 
// Block Mode
 
void SwWrtShell::EnterBlockMode()
{
    m_bBlockMode = false;
    EnterStdMode();
    m_bBlockMode = true;
    CursorToBlockCursor();
    Invalidate();
}
 
void SwWrtShell::LeaveBlockMode()
{
    m_bBlockMode = false;
    BlockCursorToCursor();
    EndSelect();
    Invalidate();
}
 
// Insert mode
 
void SwWrtShell::ImplSetInsMode(bool bOn)
{
    m_bIns = bOn;
    SwCursorShell::SetOverwriteCursor( !m_bIns );
    const SfxBoolItem aTmp( SID_ATTR_INSERT, m_bIns );
    GetView().GetViewFrame().GetBindings().SetState( aTmp );
    StartAction();
    EndAction();
    Invalidate();
}
 
namespace
{
    class QuerySetInsModeDialog : public weld::GenericDialogController
    {
        std::unique_ptr<weld::Image> m_xImage;
        std::unique_ptr<weld::CheckButton> m_xCheckBox;
    public:
        QuerySetInsModeDialog(weld::Window* pParent)
            : GenericDialogController(pParent, u"cui/ui/querysetinsmodedialog.ui"_ustr, u"SetInsModeDialog"_ustr)
            , m_xImage(m_xBuilder->weld_image(u"imSetInsMode"_ustr))
            , m_xCheckBox(m_xBuilder->weld_check_button(u"cbDontShowAgain"_ustr))
        {
            m_xImage->set_from_icon_name(RID_BMP_QUERYINSMODE);
        }
        bool GetDoNotShowAgain() const
        {
            return m_xCheckBox->get_active();
        }
    };
}
 
void SwWrtShell::SetInsMode( bool bOn )
{
    const bool bDoAsk = officecfg::Office::Common::Misc::QuerySetInsMode::get();
    if (!bOn && bDoAsk)
    {
        auto xDialog = std::make_shared<QuerySetInsModeDialog>(GetView().GetFrameWeld());
        weld::DialogController::runAsync(xDialog, [this, bOn, xDialog](sal_Int32 nResult){
 
            std::shared_ptr<comphelper::ConfigurationChanges> xChanges(
                comphelper::ConfigurationChanges::create());
            officecfg::Office::Common::Misc::QuerySetInsMode::set(!xDialog->GetDoNotShowAgain(), xChanges);
            xChanges->commit();
 
            if ( nResult == static_cast<int>(RET_NO) )
                return;
 
            ImplSetInsMode(bOn);
        });
        return;
    }
    ImplSetInsMode(bOn);
}
 
//Overwrite mode is incompatible with red-lining
void SwWrtShell::SetRedlineFlagsAndCheckInsMode( RedlineFlags eMode )
{
   SetRedlineFlags( eMode );
   if (IsRedlineOn())
       SetInsMode();
}
 
// Edit frame
 
void SwWrtShell::BeginFrameDrag(const Point *pPt, bool bIsShift)
{
    m_fnDrag = &SwFEShell::Drag;
    if(bStartDrag)
    {
        Point aTmp( nStartDragX, nStartDragY );
        SwFEShell::BeginDrag( &aTmp, bIsShift );
    }
    else
        SwFEShell::BeginDrag( pPt, bIsShift );
}
 
void SwWrtShell::EnterSelFrameMode(const Point *pPos)
{
    if(pPos)
    {
        nStartDragX = pPos->X();
        nStartDragY = pPos->Y();
        bStartDrag = true;
    }
    m_bLayoutMode = true;
    HideCursor();
 
        // equal call of BeginDrag in the SwFEShell
    m_fnDrag          = &SwWrtShell::BeginFrameDrag;
    m_fnEndDrag       = &SwWrtShell::UpdateLayoutFrame;
    SwBaseShell::SetFrameMode( FLY_DRAG_START, this );
    Invalidate();
}
 
void SwWrtShell::LeaveSelFrameMode()
{
    m_fnDrag          = &SwWrtShell::BeginDrag;
    m_fnEndDrag       = &SwWrtShell::DefaultEndDrag;
    m_bLayoutMode = false;
    bStartDrag = false;
    Edit();
    SwBaseShell::SetFrameMode( FLY_DRAG_END, this );
    Invalidate();
}
 
// Description: execute framebound macro
 
IMPL_LINK( SwWrtShell, ExecFlyMac, const SwFlyFrameFormat*, pFlyFormat, void )
{
    const SwFrameFormat *pFormat = pFlyFormat ? static_cast<const SwFrameFormat*>(pFlyFormat) : GetFlyFrameFormat();
    assert(pFormat && "no frame format");
    const SvxMacroItem &rFormatMac = pFormat->GetMacro();
 
    if(rFormatMac.HasMacro(SvMacroItemId::SwObjectSelect))
    {
        const SvxMacro &rMac = rFormatMac.GetMacro(SvMacroItemId::SwObjectSelect);
        if( IsFrameSelected() )
            m_bLayoutMode = true;
        CallChgLnk();
        ExecMacro( rMac );
    }
}
 
void SwWrtShell::UpdateLayoutFrame(const Point *, bool )
{
        // still a dummy
    SwFEShell::EndDrag();
    m_fnDrag = &SwWrtShell::BeginFrameDrag;
}
 
// Handler for toggling the modes. Returns back the old mode.
 
void SwWrtShell::ToggleAddMode()
{
    m_bAddMode ? LeaveAddMode(): EnterAddMode();
    Invalidate();
}
 
void SwWrtShell::ToggleBlockMode()
{
    m_bBlockMode ? LeaveBlockMode(): EnterBlockMode();
    Invalidate();
}
 
void SwWrtShell::ToggleExtMode()
{
    m_bExtMode ? LeaveExtMode() : EnterExtMode();
    Invalidate();
}
 
// Dragging in standard mode (Selecting of content)
 
void SwWrtShell::BeginDrag(const Point * /*pPt*/, bool )
{
    if(m_bSelWrd)
    {
        m_bInSelect = true;
        if( !IsCursorPtAtEnd() )
            SwapPam();
 
        m_fnDrag = &SwWrtShell::ExtSelWrd;
        m_fnSetCursor = &SwWrtShell::Ignore;
    }
    else if(m_bSelLn)
    {
        m_bInSelect = true;
        m_fnDrag = &SwWrtShell::ExtSelLn;
        m_fnSetCursor = &SwWrtShell::Ignore;
    }
    else
    {
        m_fnDrag = &SwWrtShell::DefaultDrag;
        SttSelect();
    }
}
 
void SwWrtShell::DefaultDrag(const Point *, bool )
{
    if( IsSelTableCells() )
        m_aSelTableLink.Call(*this);
}
 
void SwWrtShell::DefaultEndDrag(const Point * /*pPt*/, bool )
{
    m_fnDrag = &SwWrtShell::BeginDrag;
    if( IsExtSel() )
        LeaveExtSel();
 
    if( IsSelTableCells() )
        m_aSelTableLink.Call(*this);
    EndSelect();
}
 
// #i32329# Enhanced table selection
bool SwWrtShell::SelectTableRowCol( const Point& rPt, const Point* pEnd, bool bRowDrag )
{
    SwMvContext aMvContext(this);
    SttSelect();
    if(SelTableRowCol( rPt, pEnd, bRowDrag ))
    {
        m_fnSetCursor = &SwWrtShell::SetCursorKillSel;
        m_fnKillSel = &SwWrtShell::ResetSelect;
        return true;
    }
    return false;
}
 
// Description: Selection of a table line or column
 
void SwWrtShell::SelectTableRow()
{
    if ( SelTableRow() )
    {
        m_fnSetCursor = &SwWrtShell::SetCursorKillSel;
        m_fnKillSel = &SwWrtShell::ResetSelect;
    }
}
 
void SwWrtShell::SelectTableCol()
{
    if ( SelTableCol() )
    {
        m_fnSetCursor = &SwWrtShell::SetCursorKillSel;
        m_fnKillSel = &SwWrtShell::ResetSelect;
    }
}
 
void SwWrtShell::SelectTableCell()
{
    if ( SelTableBox() )
    {
        m_fnSetCursor = &SwWrtShell::SetCursorKillSel;
        m_fnKillSel = &SwWrtShell::ResetSelect;
    }
}
 
// Description: Check if a word selection is present.
//              According to the rules for intelligent cut / paste
//              surrounding spaces are cut out.
// Return:      Delivers the type of the word selection.
 
int SwWrtShell::IntelligentCut(SelectionType nSelection, bool bCut)
{
        // On multiple selection no intelligent drag and drop
        // there are multiple cursors, since a second was placed
        // already at the target position.
    if( IsAddMode() || !(nSelection & SelectionType::Text) )
        return NO_WORD;
 
    OUString sText;
    CharClass& rCC = GetAppCharClass();
 
        // If the first character is no word character,
        // no word selected.
    sal_Unicode cPrev = GetChar(false);
    sal_Unicode cNext = GetChar(true, -1);
    if( !cPrev || !cNext ||
        !rCC.isLetterNumeric( ( sText = OUString(cPrev) ), 0 ) ||
        !rCC.isLetterNumeric( ( sText = OUString(cNext) ), 0 ) )
        return NO_WORD;
 
    cPrev = GetChar(false, -1);
    cNext = GetChar();
 
    int cWord = NO_WORD;
        // is a word selected?
    if (cPrev && cNext &&
        CH_TXTATR_BREAKWORD != cPrev && CH_TXTATR_INWORD != cPrev &&
        CH_TXTATR_BREAKWORD != cNext && CH_TXTATR_INWORD != cNext &&
        !rCC.isLetterNumeric( ( sText = OUString(cPrev) ), 0 ) &&
        !rCC.isLetterNumeric( ( sText = OUString(cNext) ), 0 ) )
       cWord = WORD_NO_SPACE;
 
    if(cWord == WORD_NO_SPACE && ' ' == cPrev )
    {
        cWord = WORD_SPACE_BEFORE;
            // delete the space before
        if(bCut)
        {
            Push();
            if(IsCursorPtAtEnd())
                SwapPam();
            ClearMark();
            SetMark();
            SwCursorShell::Left(1,SwCursorSkipMode::Chars);
            SwFEShell::Delete(true);
            Pop(SwCursorShell::PopMode::DeleteCurrent);
        }
    }
    else if(cWord == WORD_NO_SPACE && cNext == ' ')
    {
        cWord = WORD_SPACE_AFTER;
            // delete the space behind
        if(bCut) {
            Push();
            if(!IsCursorPtAtEnd()) SwapPam();
            ClearMark();
            SetMark();
            SwCursorShell::Right(1,SwCursorSkipMode::Chars);
            SwFEShell::Delete(true);
            Pop(SwCursorShell::PopMode::DeleteCurrent);
        }
    }
    return cWord;
}
 
    // jump to the next / previous hyperlink - inside text and also
    // on graphics
void SwWrtShell::SelectNextPrevHyperlink( bool bNext )
{
    StartAction();
    bool bRet = SwCursorShell::SelectNxtPrvHyperlink( bNext );
    if( !bRet ) // didn't find? wrap and check again
    {
        SwShellCursor* pCursor = GetCursor_();
        SwCursorSaveState aSaveState(*pCursor);
        EnterStdMode();
        if( bNext )
            SttEndDoc(true);
        else
            SttEndDoc(false);
        bRet = SwCursorShell::SelectNxtPrvHyperlink(bNext);
        if (!bRet) // didn't find again? restore cursor position and bail
        {
            pCursor->RestoreSavePos();
            EndAction(true); // don't scroll to restored cursor position
            return;
        }
    }
    EndAction();
 
    bool bCreateXSelection = false;
    const bool bFrameSelected = IsFrameSelected() || IsObjSelected();
    if( IsSelection() )
    {
        if ( bFrameSelected )
            UnSelectFrame();
 
        // Set the function pointer for the canceling of the selection
        // set at cursor
        m_fnKillSel = &SwWrtShell::ResetSelect;
        m_fnSetCursor = &SwWrtShell::SetCursorKillSel;
        bCreateXSelection = true;
    }
    else if( bFrameSelected )
    {
        EnterSelFrameMode();
        bCreateXSelection = true;
    }
    else if( (CNT_GRF | CNT_OLE ) & GetCntType() )
    {
        SelectObj( GetCharRect().Pos() );
        EnterSelFrameMode();
        bCreateXSelection = true;
    }
 
    if( bCreateXSelection )
        SwTransferable::CreateSelection( *this );
}
 
// For the preservation of the selection the cursor will be moved left
// after SetMark(), so that the cursor is not moved by inserting text.
// Because a present selection at the CORE page is cleared at the
// current cursor position, the cursor will be pushed on the stack.
// After moving, they will again resummarized.
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V788 The variable 'bOn', captured in a lambda expression, has a constant value.

V1007 The value from the potentially uninitialized optional 'oEndPos' is used. Probably it is a mistake.

V1007 The value from the potentially uninitialized optional 'oEndPos' is used. Probably it is a mistake.