diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
commit | 267c6f2ac71f92999e969232431ba04678e7437e (patch) | |
tree | 358c9467650e1d0a1d7227a21dac2e3d08b622b2 /sw/source/uibase/docvw/edtwin.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip |
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sw/source/uibase/docvw/edtwin.cxx')
-rw-r--r-- | sw/source/uibase/docvw/edtwin.cxx | 6897 |
1 files changed, 6897 insertions, 0 deletions
diff --git a/sw/source/uibase/docvw/edtwin.cxx b/sw/source/uibase/docvw/edtwin.cxx new file mode 100644 index 0000000000..322928eb4e --- /dev/null +++ b/sw/source/uibase/docvw/edtwin.cxx @@ -0,0 +1,6897 @@ +/* -*- 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 <config_wasm_strip.h> + +#include <swtypes.hxx> +#include <hintids.hxx> + +#include <com/sun/star/accessibility/XAccessible.hpp> +#include <com/sun/star/awt/PopupMenuDirection.hpp> +#include <com/sun/star/awt/XPopupMenu.hpp> +#include <com/sun/star/i18n/XBreakIterator.hpp> +#include <com/sun/star/i18n/ScriptType.hpp> +#include <com/sun/star/i18n/InputSequenceCheckMode.hpp> +#include <com/sun/star/i18n/UnicodeScript.hpp> +#include <com/sun/star/i18n/XExtendedInputSequenceChecker.hpp> +#include <com/sun/star/ui/ContextMenuExecuteEvent.hpp> + +#include <comphelper/scopeguard.hxx> +#include <comphelper/string.hxx> + +#include <vcl/dialoghelper.hxx> +#include <vcl/inputctx.hxx> +#include <vcl/help.hxx> +#include <vcl/weld.hxx> +#include <vcl/ptrstyle.hxx> +#include <svl/macitem.hxx> +#include <unotools/securityoptions.hxx> +#include <basic/sbxvar.hxx> +#include <svl/ctloptions.hxx> +#include <basic/sbx.hxx> +#include <svl/eitem.hxx> +#include <svl/stritem.hxx> +#include <sfx2/ipclient.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/request.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/dispatch.hxx> +#include <svl/ptitem.hxx> +#include <editeng/sizeitem.hxx> +#include <editeng/langitem.hxx> +#include <svx/statusitem.hxx> +#include <svx/svdview.hxx> +#include <svx/svdhdl.hxx> +#include <svx/svdoutl.hxx> +#include <editeng/editeng.hxx> +#include <editeng/editview.hxx> +#include <editeng/svxacorr.hxx> +#include <editeng/flditem.hxx> +#include <editeng/colritem.hxx> +#include <unotools/charclass.hxx> +#include <unotools/datetime.hxx> + +#include <comphelper/lok.hxx> +#include <sfx2/lokhelper.hxx> + +#include <editeng/acorrcfg.hxx> +#include <bookmark.hxx> +#include <SwSmartTagMgr.hxx> +#include <edtdd.hxx> +#include <edtwin.hxx> +#include <view.hxx> +#include <wrtsh.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <IDocumentUndoRedo.hxx> +#include <textboxhelper.hxx> +#include <dcontact.hxx> +#include <fldbas.hxx> +#include <swmodule.hxx> +#include <docsh.hxx> +#include <viewopt.hxx> +#include <drawbase.hxx> +#include <dselect.hxx> +#include <textsh.hxx> +#include <shdwcrsr.hxx> +#include <txatbase.hxx> +#include <fmtanchr.hxx> +#include <fmtornt.hxx> +#include <fmthdft.hxx> +#include <frmfmt.hxx> +#include <modcfg.hxx> +#include <fmtcol.hxx> +#include <wview.hxx> +#include <gloslst.hxx> +#include <inputwin.hxx> +#include <gloshdl.hxx> +#include <swundo.hxx> +#include <drwtxtsh.hxx> +#include <fchrfmt.hxx> +#include "romenu.hxx" +#include <initui.hxx> +#include <frmatr.hxx> +#include <extinput.hxx> +#include <acmplwrd.hxx> +#include <swcalwrp.hxx> +#include <swdtflvr.hxx> +#include <breakit.hxx> +#include <checkit.hxx> +#include <pagefrm.hxx> + +#include <helpids.h> +#include <cmdid.h> +#include <uitool.hxx> +#include <fmtfollowtextflow.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <charfmt.hxx> +#include <numrule.hxx> +#include <pagedesc.hxx> +#include <svtools/ruler.hxx> +#include <formatclipboard.hxx> +#include <vcl/svapp.hxx> +#include <wordcountdialog.hxx> +#include <fmtfld.hxx> + +#include <IMark.hxx> +#include <doc.hxx> +#include <xmloff/odffields.hxx> + +#include <PostItMgr.hxx> +#include <FrameControlsManager.hxx> +#include <AnnotationWin.hxx> + +#include <algorithm> +#include <vector> + +#include <rootfrm.hxx> + +#include <unotools/syslocaleoptions.hxx> +#include <i18nlangtag/mslangid.hxx> +#include <salhelper/singletonref.hxx> +#include <sfx2/event.hxx> +#include <memory> + +#include "../../core/crsr/callnk.hxx" +#include <IDocumentOutlineNodes.hxx> +#include <ndtxt.hxx> +#include <cntfrm.hxx> +#include <txtfrm.hxx> +#include <strings.hrc> +#include <textcontentcontrol.hxx> +#include <contentcontrolbutton.hxx> + +using namespace sw::mark; +using namespace ::com::sun::star; + +/** + * Globals + */ +static bool g_bInputLanguageSwitched = false; + +// Usually in MouseButtonUp a selection is revoked when the selection is +// not currently being pulled open. Unfortunately in MouseButtonDown there +// is being selected at double/triple click. That selection is completely +// finished in the Handler and thus can't be distinguished in the Up. +// To resolve this g_bHoldSelection is set in Down and evaluated in Up. +static bool g_bHoldSelection = false; + +bool g_bFrameDrag = false; +static bool g_bValidCursorPos = false; +bool g_bModePushed = false; +bool g_bDDTimerStarted = false; +bool g_bDDINetAttr = false; +static SdrHdlKind g_eSdrMoveHdl = SdrHdlKind::User; + +QuickHelpData* SwEditWin::s_pQuickHlpData = nullptr; + +tools::Long SwEditWin::s_nDDStartPosY = 0; +tools::Long SwEditWin::s_nDDStartPosX = 0; + +static SfxShell* lcl_GetTextShellFromDispatcher( SwView const & rView ); + +/// Check if the selected shape has a TextBox: if so, go into that instead. +static bool lcl_goIntoTextBox(SwEditWin& rEditWin, SwWrtShell& rSh) +{ + SdrMark* pMark = rSh.GetDrawView()->GetMarkedObjectList().GetMark(0); + if (!pMark) + return false; + + SdrObject* pSdrObject = pMark->GetMarkedSdrObj(); + SwFrameFormat* pObjectFormat = ::FindFrameFormat(pSdrObject); + if (SwFrameFormat* pTextBoxFormat = SwTextBoxHelper::getOtherTextBoxFormat(pObjectFormat, RES_DRAWFRMFMT)) + { + SdrObject* pTextBox = pTextBoxFormat->FindRealSdrObject(); + SdrView* pSdrView = rSh.GetDrawView(); + // Unmark the shape. + pSdrView->UnmarkAllObj(); + // Mark the textbox. + rSh.SelectObj(Point(), SW_ALLOW_TEXTBOX, pTextBox); + // Clear the DrawFuncPtr. + rEditWin.StopInsFrame(); + return true; + } + return false; +} + +class SwAnchorMarker +{ + SdrHdl* m_pHdl; + Point m_aHdlPos; + Point m_aLastPos; + bool m_bTopRightHandle; +public: + explicit SwAnchorMarker( SdrHdl* pH ) + : m_pHdl( pH ) + , m_aHdlPos( pH->GetPos() ) + , m_aLastPos( pH->GetPos() ) + , m_bTopRightHandle( pH->GetKind() == SdrHdlKind::Anchor_TR ) + {} + const Point& GetLastPos() const { return m_aLastPos; } + void SetLastPos( const Point& rNew ) { m_aLastPos = rNew; } + void SetPos( const Point& rNew ) { m_pHdl->SetPos( rNew ); } + const Point& GetHdlPos() const { return m_aHdlPos; } + SdrHdl* GetHdl() const { return m_pHdl; } + void ChgHdl( SdrHdl* pNew ) + { + m_pHdl = pNew; + if ( m_pHdl ) + { + m_bTopRightHandle = (m_pHdl->GetKind() == SdrHdlKind::Anchor_TR); + } + } + Point GetPosForHitTest( const OutputDevice& rOut ) + { + Point aHitTestPos( m_pHdl->GetPos() ); + aHitTestPos = rOut.LogicToPixel( aHitTestPos ); + if ( m_bTopRightHandle ) + { + aHitTestPos += Point( -1, 1 ); + } + else + { + aHitTestPos += Point( 1, 1 ); + } + aHitTestPos = rOut.PixelToLogic( aHitTestPos ); + + return aHitTestPos; + } +}; + +/// Assists with auto-completion of AutoComplete words and AutoText names. +struct QuickHelpData +{ + /// Strings that at least partially match an input word, and match length. + std::vector<std::pair<OUString, sal_uInt16>> m_aHelpStrings; + /// Index of the current help string. + sal_uInt16 nCurArrPos; + static constexpr sal_uInt16 nNoPos = std::numeric_limits<sal_uInt16>::max(); + + /// Help data stores AutoText names rather than AutoComplete words. + bool m_bIsAutoText; + /// Display help string as a tip rather than inline. + bool m_bIsTip; + /// Tip ID when a help string is displayed as a tip. + void* nTipId; + /// Append a space character to the displayed help string (if appropriate). + bool m_bAppendSpace; + + /// Help string is currently displayed. + bool m_bIsDisplayed; + + QuickHelpData() { ClearContent(); } + + void Move( QuickHelpData& rCpy ); + void ClearContent(); + void Start(SwWrtShell& rSh, bool bRestart); + void Stop( SwWrtShell& rSh ); + + bool HasContent() const { return !m_aHelpStrings.empty() && nCurArrPos != nNoPos; } + const OUString& CurStr() const { return m_aHelpStrings[nCurArrPos].first; } + sal_uInt16 CurLen() const { return m_aHelpStrings[nCurArrPos].second; } + + /// Next help string. + void Next( bool bEndLess ) + { + if( ++nCurArrPos >= m_aHelpStrings.size() ) + nCurArrPos = (bEndLess && !m_bIsAutoText ) ? 0 : nCurArrPos-1; + } + /// Previous help string. + void Previous( bool bEndLess ) + { + if( 0 == nCurArrPos-- ) + nCurArrPos = (bEndLess && !m_bIsAutoText ) ? m_aHelpStrings.size()-1 : 0; + } + + // Fills internal structures with hopefully helpful information. + void FillStrArr( SwWrtShell const & rSh, const OUString& rWord ); + void SortAndFilter(const OUString &rOrigWord); +}; + +/** + * Avoid minimal movement shiver + */ +#define HIT_PIX 2 /* hit tolerance in pixel */ +#define MIN_MOVE 4 + +static bool IsMinMove(const Point &rStartPos, const Point &rLPt) +{ + return std::abs(rStartPos.X() - rLPt.X()) > MIN_MOVE || + std::abs(rStartPos.Y() - rLPt.Y()) > MIN_MOVE; +} + +/** + * For MouseButtonDown - determine whether a DrawObject + * a NO SwgFrame was hit! Shift/Ctrl should only result + * in selecting, with DrawObjects; at SwgFlys to trigger + * hyperlinks if applicable (Download/NewWindow!) + */ +static bool IsDrawObjSelectable( const SwWrtShell& rSh, const Point& rPt ) +{ + bool bRet = true; + SdrObject* pObj; + switch( rSh.GetObjCntType( rPt, pObj )) + { + case OBJCNT_NONE: + case OBJCNT_FLY: + case OBJCNT_GRF: + case OBJCNT_OLE: + bRet = false; + break; + default:; //prevent warning + } + return bRet; +} + +/* + * Switch pointer + */ +void SwEditWin::UpdatePointer(const Point &rLPt, sal_uInt16 nModifier ) +{ + SetQuickHelpText(OUString()); + SwWrtShell &rSh = m_rView.GetWrtShell(); + if( m_pApplyTempl ) + { + PointerStyle eStyle = PointerStyle::Fill; + if ( rSh.IsOverReadOnlyPos( rLPt ) ) + { + m_pUserMarker.reset(); + + eStyle = PointerStyle::NotAllowed; + } + else + { + SwRect aRect; + SwRect* pRect = &aRect; + const SwFrameFormat* pFormat = nullptr; + + bool bFrameIsValidTarget = false; + if( m_pApplyTempl->m_pFormatClipboard ) + bFrameIsValidTarget = m_pApplyTempl->m_pFormatClipboard->HasContentForThisType( SelectionType::Frame ); + else if( !m_pApplyTempl->nColor ) + bFrameIsValidTarget = ( m_pApplyTempl->eType == SfxStyleFamily::Frame ); + + if( bFrameIsValidTarget && + nullptr !=(pFormat = rSh.GetFormatFromObj( rLPt, &pRect )) && + dynamic_cast<const SwFlyFrameFormat*>( pFormat) ) + { + //turn on highlight for frame + tools::Rectangle aTmp( pRect->SVRect() ); + + if ( !m_pUserMarker ) + { + m_pUserMarker.reset(new SdrDropMarkerOverlay( *rSh.GetDrawView(), aTmp )); + } + } + else + { + m_pUserMarker.reset(); + } + + rSh.SwCursorShell::SetVisibleCursor( rLPt ); + } + SetPointer( eStyle ); + return; + } + + if( !rSh.VisArea().Width() ) + return; + + CurrShell aCurr(&rSh); + + if ( IsChainMode() ) + { + SwRect aRect; + SwChainRet nChainable = rSh.Chainable( aRect, *rSh.GetFlyFrameFormat(), rLPt ); + PointerStyle eStyle = nChainable != SwChainRet::OK + ? PointerStyle::ChainNotAllowed : PointerStyle::Chain; + if ( nChainable == SwChainRet::OK ) + { + tools::Rectangle aTmp( aRect.SVRect() ); + + if ( !m_pUserMarker ) + { + m_pUserMarker.reset(new SdrDropMarkerOverlay( *rSh.GetDrawView(), aTmp )); + } + } + else + { + m_pUserMarker.reset(); + } + + SetPointer( eStyle ); + return; + } + + bool bExecHyperlinks = m_rView.GetDocShell()->IsReadOnly(); + if ( !bExecHyperlinks ) + { + const bool bSecureOption = SvtSecurityOptions::IsOptionSet( SvtSecurityOptions::EOption::CtrlClickHyperlink ); + if ( ( bSecureOption && nModifier == KEY_MOD1 ) || + ( !bSecureOption && nModifier != KEY_MOD1 ) ) + bExecHyperlinks = true; + } + + const bool bExecSmarttags = nModifier == KEY_MOD1; + + SdrView *pSdrView = rSh.GetDrawView(); + bool bPrefSdrPointer = false; + bool bHitHandle = false; + bool bCntAtPos = false; + bool bIsDocReadOnly = m_rView.GetDocShell()->IsReadOnly() && + rSh.IsCursorReadonly(); + m_aActHitType = SdrHitKind::NONE; + PointerStyle eStyle = PointerStyle::Text; + if ( !pSdrView ) + bCntAtPos = true; + else if ( (bHitHandle = (pSdrView->PickHandle(rLPt) != nullptr)) ) + { + m_aActHitType = SdrHitKind::Object; + bPrefSdrPointer = true; + } + else + { + const bool bNotInSelObj = !rSh.IsInsideSelectedObj( rLPt ); + if ( m_rView.GetDrawFuncPtr() && !m_bInsDraw && bNotInSelObj ) + { + m_aActHitType = SdrHitKind::Object; + if (IsObjectSelect()) + eStyle = PointerStyle::Arrow; + else + bPrefSdrPointer = true; + } + else + { + SdrPageView* pPV = nullptr; + pSdrView->SetHitTolerancePixel( HIT_PIX ); + SdrObject* pObj = (bNotInSelObj && bExecHyperlinks) ? + pSdrView->PickObj(rLPt, pSdrView->getHitTolLog(), pPV, SdrSearchOptions::PICKMACRO) : + nullptr; + if (pObj) + { + SdrObjMacroHitRec aTmp; + aTmp.aPos = rLPt; + aTmp.pPageView = pPV; + SetPointer( pObj->GetMacroPointer( aTmp ) ); + return; + } + else + { + // dvo: IsObjSelectable() eventually calls SdrView::PickObj, so + // apparently this is used to determine whether this is a + // drawling layer object or not. + if ( rSh.IsObjSelectable( rLPt ) ) + { + if (pSdrView->IsTextEdit()) + { + m_aActHitType = SdrHitKind::NONE; + bPrefSdrPointer = true; + } + else + { + SdrViewEvent aVEvt; + SdrHitKind eHit = pSdrView->PickAnything(rLPt, aVEvt); + + if (eHit == SdrHitKind::UrlField && bExecHyperlinks) + { + m_aActHitType = SdrHitKind::Object; + bPrefSdrPointer = true; + } + else + { + // if we're over a selected object, we show an + // ARROW by default. We only show a MOVE if 1) the + // object is selected, and 2) it may be moved + // (i.e., position is not protected). + bool bMovable = + (!bNotInSelObj) && + (rSh.IsObjSelected() || rSh.IsFrameSelected()) && + (rSh.IsSelObjProtected(FlyProtectFlags::Pos) == FlyProtectFlags::NONE); + + SdrObject* pSelectableObj = rSh.GetObjAt(rLPt); + // Don't update pointer if this is a background image only. + if (pSelectableObj->GetLayer() != rSh.GetDoc()->getIDocumentDrawModelAccess().GetHellId()) + eStyle = bMovable ? PointerStyle::Move : PointerStyle::Arrow; + m_aActHitType = SdrHitKind::Object; + } + } + } + else + { + if ( rSh.IsFrameSelected() && !bNotInSelObj ) + { + // dvo: this branch appears to be dead and should be + // removed in a future version. Reason: The condition + // !bNotInSelObj means that this branch will only be + // executed in the cursor points inside a selected + // object. However, if this is the case, the previous + // if( rSh.IsObjSelectable(rLPt) ) must always be true: + // rLPt is inside a selected object, then obviously + // rLPt is over a selectable object. + if (rSh.IsSelObjProtected(FlyProtectFlags::Size) != FlyProtectFlags::NONE) + eStyle = PointerStyle::NotAllowed; + else + eStyle = PointerStyle::Move; + m_aActHitType = SdrHitKind::Object; + } + else + { + if ( m_rView.GetDrawFuncPtr() ) + bPrefSdrPointer = true; + else + bCntAtPos = true; + } + } + } + } + } + if ( bPrefSdrPointer ) + { + if (bIsDocReadOnly || (rSh.IsObjSelected() && rSh.IsSelObjProtected(FlyProtectFlags::Content) != FlyProtectFlags::NONE)) + SetPointer( PointerStyle::NotAllowed ); + else + { + if (m_rView.GetDrawFuncPtr() && m_rView.GetDrawFuncPtr()->IsInsertForm() && !bHitHandle) + SetPointer( PointerStyle::DrawRect ); + else + SetPointer( pSdrView->GetPreferredPointer( rLPt, rSh.GetOut() ) ); + } + } + else + { + if( !rSh.IsPageAtPos( rLPt ) || m_pAnchorMarker ) + eStyle = PointerStyle::Arrow; + else + { + // Even if we already have something, prefer URLs if possible. + SwContentAtPos aUrlPos(IsAttrAtPos::InetAttr); + if (bCntAtPos || rSh.GetContentAtPos(rLPt, aUrlPos)) + { + SwContentAtPos aSwContentAtPos( + IsAttrAtPos::Field | + IsAttrAtPos::ClickField | + IsAttrAtPos::InetAttr | + IsAttrAtPos::Ftn | + IsAttrAtPos::SmartTag); + if( rSh.GetContentAtPos( rLPt, aSwContentAtPos) ) + { + // Is edit inline input field + if (IsAttrAtPos::Field == aSwContentAtPos.eContentAtPos + && aSwContentAtPos.pFndTextAttr != nullptr + && aSwContentAtPos.pFndTextAttr->Which() == RES_TXTATR_INPUTFIELD) + { + const SwField *pCursorField = rSh.CursorInsideInputField() ? rSh.GetCurField( true ) : nullptr; + if (!(pCursorField && pCursorField == aSwContentAtPos.pFndTextAttr->GetFormatField().GetField())) + eStyle = PointerStyle::RefHand; + } + else + { + const bool bClickToFollow = IsAttrAtPos::InetAttr == aSwContentAtPos.eContentAtPos || + IsAttrAtPos::SmartTag == aSwContentAtPos.eContentAtPos; + if( !bClickToFollow || + (IsAttrAtPos::InetAttr == aSwContentAtPos.eContentAtPos && bExecHyperlinks) || + (IsAttrAtPos::SmartTag == aSwContentAtPos.eContentAtPos && bExecSmarttags) ) + eStyle = PointerStyle::RefHand; + } + } + else if (GetView().GetWrtShell().GetViewOptions()->IsShowOutlineContentVisibilityButton()) + { + aSwContentAtPos.eContentAtPos = IsAttrAtPos::Outline; + if (rSh.GetContentAtPos(rLPt, aSwContentAtPos)) + { + if (IsAttrAtPos::Outline == aSwContentAtPos.eContentAtPos) + { + if (nModifier == KEY_MOD1) + { + eStyle = PointerStyle::RefHand; + // set quick help + if(aSwContentAtPos.aFnd.pNode && aSwContentAtPos.aFnd.pNode->IsTextNode()) + { + const SwNodes& rNds = GetView().GetWrtShell().GetDoc()->GetNodes(); + SwOutlineNodes::size_type nPos; + rNds.GetOutLineNds().Seek_Entry(aSwContentAtPos.aFnd.pNode->GetTextNode(), &nPos); + SwOutlineNodes::size_type nOutlineNodesCount + = rSh.getIDocumentOutlineNodesAccess()->getOutlineNodesCount(); + int nLevel = rSh.getIDocumentOutlineNodesAccess()->getOutlineLevel(nPos); + OUString sQuickHelp(SwResId(STR_CLICK_OUTLINE_CONTENT_TOGGLE_VISIBILITY)); + if (!rSh.GetViewOptions()->IsTreatSubOutlineLevelsAsContent() + && nPos + 1 < nOutlineNodesCount + && rSh.getIDocumentOutlineNodesAccess()->getOutlineLevel(nPos + 1) > nLevel) + sQuickHelp += " (" + SwResId(STR_CLICK_OUTLINE_CONTENT_TOGGLE_VISIBILITY_EXT) + ")"; + SetQuickHelpText(sQuickHelp); + } + } + } + } + } + } + } + + // which kind of text pointer have we to show - horz / vert - ? + if( PointerStyle::Text == eStyle && rSh.IsInVerticalText( &rLPt )) + eStyle = PointerStyle::TextVertical; + else if (rSh.GetViewOptions()->CanHideWhitespace() && + rSh.GetLayout()->IsBetweenPages(rLPt)) + { + if (rSh.GetViewOptions()->IsHideWhitespaceMode()) + eStyle = PointerStyle::ShowWhitespace; + else + eStyle = PointerStyle::HideWhitespace; + } + + if( m_pShadCursor ) + { + if( text::HoriOrientation::LEFT == m_eOrient ) // Arrow to the right + eStyle = PointerStyle::AutoScrollE; + else // Arrow to the left + eStyle = PointerStyle::AutoScrollW; + } + + SetPointer( eStyle ); + } +} + +/** + * Increase timer for selection + */ +IMPL_LINK_NOARG(SwEditWin, TimerHandler, Timer *, void) +{ + SwWrtShell &rSh = m_rView.GetWrtShell(); + Point aModPt( m_aMovePos ); + const SwRect aOldVis( rSh.VisArea() ); + bool bDone = false; + + if ( !rSh.VisArea().Contains( aModPt ) ) + { + if ( m_bInsDraw ) + { + const int nMaxScroll = 40; + m_rView.Scroll( tools::Rectangle(aModPt,Size(1,1)), nMaxScroll, nMaxScroll); + bDone = true; + } + else if ( g_bFrameDrag ) + { + rSh.Drag(&aModPt, false); + bDone = true; + } + if ( !bDone ) + aModPt = rSh.GetContentPos( aModPt,aModPt.Y() > rSh.VisArea().Bottom() ); + } + if ( !bDone && !(g_bFrameDrag || m_bInsDraw) ) + { + if ( m_xRowColumnSelectionStart ) + { + Point aPos( aModPt ); + rSh.SelectTableRowCol( *m_xRowColumnSelectionStart, &aPos, m_bIsRowDrag ); + } + else + rSh.CallSetCursor( &aModPt, false ); + + // It can be that a "jump" over a table cannot be accomplished like + // that. So we jump over the table by Up/Down here. + const SwRect& rVisArea = rSh.VisArea(); + if( aOldVis == rVisArea && !rSh.IsStartOfDoc() && !rSh.IsEndOfDoc() ) + { + // take the center point of VisArea to + // decide in which direction the user want. + if( aModPt.Y() < ( rVisArea.Top() + rVisArea.Height() / 2 ) ) + rSh.Up( true ); + else + rSh.Down( true ); + } + } + + m_aMovePos += rSh.VisArea().Pos() - aOldVis.Pos(); + JustifyAreaTimer(); +} + +void SwEditWin::JustifyAreaTimer() +{ + const tools::Rectangle &rVisArea = GetView().GetVisArea(); +#ifdef UNX + const tools::Long coMinLen = 100; +#else + const tools::Long coMinLen = 50; +#endif + tools::Long const nTimeout = 800, + nDiff = std::max( + std::max( m_aMovePos.Y() - rVisArea.Bottom(), rVisArea.Top() - m_aMovePos.Y() ), + std::max( m_aMovePos.X() - rVisArea.Right(), rVisArea.Left() - m_aMovePos.X())); + m_aTimer.SetTimeout( std::max( coMinLen, nTimeout - nDiff*2L) ); +} + +void SwEditWin::LeaveArea(const Point &rPos) +{ + m_aMovePos = rPos; + JustifyAreaTimer(); + if( !m_aTimer.IsActive() ) + m_aTimer.Start(); + m_pShadCursor.reset(); +} + +inline void SwEditWin::EnterArea() +{ + m_aTimer.Stop(); +} + +/** + * Insert mode for frames + */ +void SwEditWin::InsFrame(sal_uInt16 nCols) +{ + StdDrawMode(SdrObjKind::NewFrame, false); + m_bInsFrame = true; + m_nInsFrameColCount = nCols; +} + +void SwEditWin::StdDrawMode( SdrObjKind eSdrObjectKind, bool bObjSelect ) +{ + SetSdrDrawMode( eSdrObjectKind ); + + if (bObjSelect) + m_rView.SetDrawFuncPtr(std::make_unique<DrawSelection>( &m_rView.GetWrtShell(), this, &m_rView )); + else + m_rView.SetDrawFuncPtr(std::make_unique<SwDrawBase>( &m_rView.GetWrtShell(), this, &m_rView )); + + m_rView.SetSelDrawSlot(); + SetSdrDrawMode( eSdrObjectKind ); + if (bObjSelect) + m_rView.GetDrawFuncPtr()->Activate( SID_OBJECT_SELECT ); + else + m_rView.GetDrawFuncPtr()->Activate( sal::static_int_cast< sal_uInt16 >(eSdrObjectKind) ); + m_bInsFrame = false; + m_nInsFrameColCount = 1; +} + +void SwEditWin::StopInsFrame() +{ + if (m_rView.GetDrawFuncPtr()) + { + m_rView.GetDrawFuncPtr()->Deactivate(); + m_rView.SetDrawFuncPtr(nullptr); + } + m_rView.LeaveDrawCreate(); // leave construction mode + m_bInsFrame = false; + m_nInsFrameColCount = 1; +} + +bool SwEditWin::IsInputSequenceCheckingRequired( const OUString &rText, const SwPaM& rCursor ) +{ + if ( !SvtCTLOptions::IsCTLFontEnabled() || + !SvtCTLOptions::IsCTLSequenceChecking() ) + return false; + + if ( 0 == rCursor.Start()->GetContentIndex() ) /* first char needs not to be checked */ + return false; + + SwBreakIt *pBreakIter = SwBreakIt::Get(); + uno::Reference < i18n::XBreakIterator > xBI = pBreakIter->GetBreakIter(); + assert(xBI.is()); + tools::Long nCTLScriptPos = -1; + + if (xBI->getScriptType( rText, 0 ) == i18n::ScriptType::COMPLEX) + nCTLScriptPos = 0; + else + nCTLScriptPos = xBI->nextScript( rText, 0, i18n::ScriptType::COMPLEX ); + + return (0 <= nCTLScriptPos && nCTLScriptPos <= rText.getLength()); +} + +//return INVALID_HINT if language should not be explicitly overridden, the correct +//HintId to use for the eBufferLanguage otherwise +static sal_uInt16 lcl_isNonDefaultLanguage(LanguageType eBufferLanguage, SwView const & rView, + const OUString &rInBuffer) +{ + sal_uInt16 nWhich = INVALID_HINT; + + //If the option to IgnoreLanguageChange is set, short-circuit this method + //which results in the document/paragraph language remaining the same + //despite a change to the keyboard/input language + SvtSysLocaleOptions aSysLocaleOptions; + if(aSysLocaleOptions.IsIgnoreLanguageChange()) + { + return INVALID_HINT; + } + + bool bLang = true; + if(eBufferLanguage != LANGUAGE_DONTKNOW) + { + switch( SvtLanguageOptions::GetI18NScriptTypeOfLanguage( eBufferLanguage )) + { + case i18n::ScriptType::ASIAN: nWhich = RES_CHRATR_CJK_LANGUAGE; break; + case i18n::ScriptType::COMPLEX: nWhich = RES_CHRATR_CTL_LANGUAGE; break; + case i18n::ScriptType::LATIN: nWhich = RES_CHRATR_LANGUAGE; break; + default: bLang = false; + } + if(bLang) + { + SfxItemSet aLangSet(rView.GetPool(), nWhich, nWhich); + SwWrtShell& rSh = rView.GetWrtShell(); + rSh.GetCurAttr(aLangSet); + if(SfxItemState::DEFAULT <= aLangSet.GetItemState(nWhich)) + { + LanguageType eLang = static_cast<const SvxLanguageItem&>(aLangSet.Get(nWhich)).GetLanguage(); + if ( eLang == eBufferLanguage ) + { + // current language attribute equal to language reported from system + bLang = false; + } + else if ( !g_bInputLanguageSwitched && RES_CHRATR_LANGUAGE == nWhich ) + { + // special case: switching between two "LATIN" languages + // In case the current keyboard setting might be suitable + // for both languages we can't safely assume that the user + // wants to use the language reported from the system, + // except if we knew that it was explicitly switched (thus + // the check for "bInputLangeSwitched"). + + // The language reported by the system could be just the + // system default language that the user is not even aware + // of, because no language selection tool is installed at + // all. In this case the OOo language should get preference + // as it might have been selected by the user explicitly. + + // Usually this case happens if the OOo language is + // different to the system language but the system keyboard + // is still suitable for the OOo language (e.g. writing + // English texts with a German keyboard). + + // For non-latin keyboards overwriting the attribute is + // still valid. We do this for cyrillic and greek ATM. In + // future versions of OOo this should be replaced by a + // configuration switch that allows to give the preference + // to the OOo setting or the system setting explicitly + // and/or a better handling of the script type. + i18n::UnicodeScript eType = !rInBuffer.isEmpty() ? + GetAppCharClass().getScript( rInBuffer, 0 ) : + i18n::UnicodeScript_kScriptCount; + + bool bSystemIsNonLatin = false; + switch ( eType ) + { + case i18n::UnicodeScript_kGreek: + case i18n::UnicodeScript_kCyrillic: + // in case other UnicodeScripts require special + // keyboards they can be added here + bSystemIsNonLatin = true; + break; + default: + break; + } + + bool bOOoLangIsNonLatin = MsLangId::isNonLatinWestern( eLang); + + bLang = (bSystemIsNonLatin != bOOoLangIsNonLatin); + } + } + } + } + return bLang ? nWhich : INVALID_HINT; +} + +/** + * Character buffer is inserted into the document + */ +void SwEditWin::FlushInBuffer() +{ + if ( m_aKeyInputFlushTimer.IsActive()) + m_aKeyInputFlushTimer.Stop(); + + if ( m_aInBuffer.isEmpty() ) + return; + + SwWrtShell& rSh = m_rView.GetWrtShell(); + + // generate new sequence input checker if not already done + if ( !pCheckIt ) + pCheckIt = new SwCheckIt; + + uno::Reference < i18n::XExtendedInputSequenceChecker > xISC = pCheckIt->xCheck; + if ( xISC.is() && IsInputSequenceCheckingRequired( m_aInBuffer, *rSh.GetCursor() ) ) + { + + // apply (Thai) input sequence checking/correction + + rSh.Push(); // push current cursor to stack + + // get text from the beginning (i.e left side) of current selection + // to the start of the paragraph + rSh.NormalizePam(); // make point be the first (left) one + if (!rSh.GetCursor()->HasMark()) + rSh.GetCursor()->SetMark(); + rSh.GetCursor()->GetMark()->SetContent(0); + + const OUString aOldText( rSh.GetCursor()->GetText() ); + const sal_Int32 nOldLen = aOldText.getLength(); + + sal_Int32 nExpandSelection = 0; + if (nOldLen > 0) + { + sal_Int32 nTmpPos = nOldLen; + sal_Int16 nCheckMode = SvtCTLOptions::IsCTLSequenceCheckingRestricted() ? + i18n::InputSequenceCheckMode::STRICT : i18n::InputSequenceCheckMode::BASIC; + + OUString aNewText( aOldText ); + if (SvtCTLOptions::IsCTLSequenceCheckingTypeAndReplace()) + { + for( sal_Int32 k = 0; k < m_aInBuffer.getLength(); ++k) + { + const sal_Unicode cChar = m_aInBuffer[k]; + const sal_Int32 nPrevPos =xISC->correctInputSequence( aNewText, nTmpPos - 1, cChar, nCheckMode ); + + // valid sequence or sequence could be corrected: + if (nPrevPos != aNewText.getLength()) + nTmpPos = nPrevPos + 1; + } + + // find position of first character that has changed + sal_Int32 nNewLen = aNewText.getLength(); + const sal_Unicode *pOldText = aOldText.getStr(); + const sal_Unicode *pNewText = aNewText.getStr(); + sal_Int32 nChgPos = 0; + while ( nChgPos < nOldLen && nChgPos < nNewLen && + pOldText[nChgPos] == pNewText[nChgPos] ) + ++nChgPos; + + const sal_Int32 nChgLen = nNewLen - nChgPos; + if (nChgLen) + { + m_aInBuffer = aNewText.copy( nChgPos, nChgLen ); + nExpandSelection = nOldLen - nChgPos; + } + else + m_aInBuffer.clear(); + } + else + { + for( sal_Int32 k = 0; k < m_aInBuffer.getLength(); ++k ) + { + const sal_Unicode cChar = m_aInBuffer[k]; + if (xISC->checkInputSequence( aNewText, nTmpPos - 1, cChar, nCheckMode )) + { + // character can be inserted: + aNewText += OUStringChar( cChar ); + ++nTmpPos; + } + } + m_aInBuffer = aNewText.copy( aOldText.getLength() ); // copy new text to be inserted to buffer + } + } + + // at this point now we will insert the buffer text 'normally' some lines below... + + rSh.Pop(SwCursorShell::PopMode::DeleteCurrent); + + if (m_aInBuffer.isEmpty()) + return; + + // if text prior to the original selection needs to be changed + // as well, we now expand the selection accordingly. + SwPaM &rCursor = *rSh.GetCursor(); + const sal_Int32 nCursorStartPos = rCursor.Start()->GetContentIndex(); + OSL_ENSURE( nCursorStartPos >= nExpandSelection, "cannot expand selection as specified!!" ); + if (nExpandSelection && nCursorStartPos >= nExpandSelection) + { + if (!rCursor.HasMark()) + rCursor.SetMark(); + rCursor.Start()->AdjustContent( -nExpandSelection ); + } + } + + uno::Reference< frame::XDispatchRecorder > xRecorder = + m_rView.GetViewFrame().GetBindings().GetRecorder(); + if ( xRecorder.is() ) + { + // determine shell + SfxShell *pSfxShell = lcl_GetTextShellFromDispatcher( m_rView ); + // generate request and record + if (pSfxShell) + { + SfxRequest aReq(m_rView.GetViewFrame(), FN_INSERT_STRING); + aReq.AppendItem( SfxStringItem( FN_INSERT_STRING, m_aInBuffer ) ); + aReq.Done(); + } + } + + sal_uInt16 nWhich = lcl_isNonDefaultLanguage(m_eBufferLanguage, m_rView, m_aInBuffer); + if (nWhich != INVALID_HINT ) + { + SvxLanguageItem aLangItem( m_eBufferLanguage, nWhich ); + rSh.SetAttrItem( aLangItem ); + } + + rSh.Insert( m_aInBuffer ); + m_eBufferLanguage = LANGUAGE_DONTKNOW; + m_aInBuffer.clear(); +} + +#define MOVE_LEFT_SMALL 0 +#define MOVE_UP_SMALL 1 +#define MOVE_RIGHT_BIG 2 +#define MOVE_DOWN_BIG 3 +#define MOVE_LEFT_BIG 4 +#define MOVE_UP_BIG 5 +#define MOVE_RIGHT_SMALL 6 +#define MOVE_DOWN_SMALL 7 + +// #i121236# Support for shift key in writer +#define MOVE_LEFT_HUGE 8 +#define MOVE_UP_HUGE 9 +#define MOVE_RIGHT_HUGE 10 +#define MOVE_DOWN_HUGE 11 + +void SwEditWin::ChangeFly( sal_uInt8 nDir, bool bWeb ) +{ + SwWrtShell &rSh = m_rView.GetWrtShell(); + SwRect aTmp = rSh.GetFlyRect(); + if( !aTmp.HasArea() || + rSh.IsSelObjProtected( FlyProtectFlags::Pos ) != FlyProtectFlags::NONE ) + return; + + SfxItemSetFixed< + RES_FRM_SIZE, RES_FRM_SIZE, + RES_PROTECT, RES_PROTECT, + RES_VERT_ORIENT, RES_ANCHOR, + RES_COL, RES_COL, + RES_FOLLOW_TEXT_FLOW, RES_FOLLOW_TEXT_FLOW> + aSet( rSh.GetAttrPool() ); + rSh.GetFlyFrameAttr( aSet ); + RndStdIds eAnchorId = aSet.Get(RES_ANCHOR).GetAnchorId(); + Size aSnap; + bool bHuge(MOVE_LEFT_HUGE == nDir || + MOVE_UP_HUGE == nDir || + MOVE_RIGHT_HUGE == nDir || + MOVE_DOWN_HUGE == nDir); + + if(MOVE_LEFT_SMALL == nDir || + MOVE_UP_SMALL == nDir || + MOVE_RIGHT_SMALL == nDir || + MOVE_DOWN_SMALL == nDir ) + { + aSnap = PixelToLogic(Size(1,1)); + } + else + { + aSnap = rSh.GetViewOptions()->GetSnapSize(); + short nDiv = rSh.GetViewOptions()->GetDivisionX(); + if ( nDiv > 0 ) + aSnap.setWidth( std::max( sal_uLong(1), static_cast<sal_uLong>(aSnap.Width()) / nDiv ) ); + nDiv = rSh.GetViewOptions()->GetDivisionY(); + if ( nDiv > 0 ) + aSnap.setHeight( std::max( sal_uLong(1), static_cast<sal_uLong>(aSnap.Height()) / nDiv ) ); + } + + if(bHuge) + { + // #i121236# 567twips == 1cm, but just take three times the normal snap + aSnap = Size(aSnap.Width() * 3, aSnap.Height() * 3); + } + + SwRect aBoundRect; + Point aRefPoint; + // adjustment for allowing vertical position + // aligned to page for fly frame anchored to paragraph or to character. + { + const SwFormatVertOrient& aVert( aSet.Get(RES_VERT_ORIENT) ); + const bool bFollowTextFlow = + aSet.Get(RES_FOLLOW_TEXT_FLOW).GetValue(); + const SwFormatAnchor& rFormatAnchor = aSet.Get(RES_ANCHOR); + rSh.CalcBoundRect( aBoundRect, eAnchorId, + text::RelOrientation::FRAME, aVert.GetRelationOrient(), + &rFormatAnchor, bFollowTextFlow, + false, &aRefPoint ); + } + tools::Long nLeft = std::min( aTmp.Left() - aBoundRect.Left(), aSnap.Width() ); + tools::Long nRight = std::min( aBoundRect.Right() - aTmp.Right(), aSnap.Width() ); + tools::Long nUp = std::min( aTmp.Top() - aBoundRect.Top(), aSnap.Height() ); + tools::Long nDown = std::min( aBoundRect.Bottom() - aTmp.Bottom(), aSnap.Height() ); + + switch ( nDir ) + { + case MOVE_LEFT_BIG: + case MOVE_LEFT_HUGE: + case MOVE_LEFT_SMALL: aTmp.Left( aTmp.Left() - nLeft ); + break; + + case MOVE_UP_BIG: + case MOVE_UP_HUGE: + case MOVE_UP_SMALL: aTmp.Top( aTmp.Top() - nUp ); + break; + + case MOVE_RIGHT_SMALL: + if( aTmp.Width() < aSnap.Width() + MINFLY ) + break; + nRight = aSnap.Width(); + [[fallthrough]]; + case MOVE_RIGHT_HUGE: + case MOVE_RIGHT_BIG: aTmp.Left( aTmp.Left() + nRight ); + break; + + case MOVE_DOWN_SMALL: + if( aTmp.Height() < aSnap.Height() + MINFLY ) + break; + nDown = aSnap.Height(); + [[fallthrough]]; + case MOVE_DOWN_HUGE: + case MOVE_DOWN_BIG: aTmp.Top( aTmp.Top() + nDown ); + break; + + default: OSL_ENSURE(true, "ChangeFly: Unknown direction." ); + } + bool bSet = false; + if ((RndStdIds::FLY_AS_CHAR == eAnchorId) && ( nDir % 2 )) + { + tools::Long aDiff = aTmp.Top() - aRefPoint.Y(); + if( aDiff > 0 ) + aDiff = 0; + else if ( aDiff < -aTmp.Height() ) + aDiff = -aTmp.Height(); + SwFormatVertOrient aVert( aSet.Get(RES_VERT_ORIENT) ); + sal_Int16 eNew; + if( bWeb ) + { + eNew = aVert.GetVertOrient(); + bool bDown = 0 != ( nDir & 0x02 ); + switch( eNew ) + { + case text::VertOrientation::CHAR_TOP: + if( bDown ) eNew = text::VertOrientation::CENTER; + break; + case text::VertOrientation::CENTER: + eNew = bDown ? text::VertOrientation::TOP : text::VertOrientation::CHAR_TOP; + break; + case text::VertOrientation::TOP: + if( !bDown ) eNew = text::VertOrientation::CENTER; + break; + case text::VertOrientation::LINE_TOP: + if( bDown ) eNew = text::VertOrientation::LINE_CENTER; + break; + case text::VertOrientation::LINE_CENTER: + eNew = bDown ? text::VertOrientation::LINE_BOTTOM : text::VertOrientation::LINE_TOP; + break; + case text::VertOrientation::LINE_BOTTOM: + if( !bDown ) eNew = text::VertOrientation::LINE_CENTER; + break; + default:; //prevent warning + } + } + else + { + aVert.SetPos( aDiff ); + eNew = text::VertOrientation::NONE; + } + aVert.SetVertOrient( eNew ); + aSet.Put( aVert ); + bSet = true; + } + if (bWeb && (RndStdIds::FLY_AT_PARA == eAnchorId) + && ( nDir==MOVE_LEFT_SMALL || nDir==MOVE_RIGHT_BIG )) + { + SwFormatHoriOrient aHori( aSet.Get(RES_HORI_ORIENT) ); + sal_Int16 eNew; + eNew = aHori.GetHoriOrient(); + switch( eNew ) + { + case text::HoriOrientation::RIGHT: + if( nDir==MOVE_LEFT_SMALL ) + eNew = text::HoriOrientation::LEFT; + break; + case text::HoriOrientation::LEFT: + if( nDir==MOVE_RIGHT_BIG ) + eNew = text::HoriOrientation::RIGHT; + break; + default:; //prevent warning + } + if( eNew != aHori.GetHoriOrient() ) + { + aHori.SetHoriOrient( eNew ); + aSet.Put( aHori ); + bSet = true; + } + } + rSh.StartAllAction(); + if( bSet ) + rSh.SetFlyFrameAttr( aSet ); + bool bSetPos = (RndStdIds::FLY_AS_CHAR != eAnchorId); + if(bSetPos && bWeb) + { + bSetPos = RndStdIds::FLY_AT_PAGE == eAnchorId; + } + if( bSetPos ) + rSh.SetFlyPos( aTmp.Pos() ); + rSh.EndAllAction(); + +} + +void SwEditWin::ChangeDrawing( sal_uInt8 nDir ) +{ + // start undo action in order to get only one + // undo action for this change. + SwWrtShell &rSh = m_rView.GetWrtShell(); + rSh.StartUndo(); + + tools::Long nX = 0; + tools::Long nY = 0; + const bool bOnePixel( + MOVE_LEFT_SMALL == nDir || + MOVE_UP_SMALL == nDir || + MOVE_RIGHT_SMALL == nDir || + MOVE_DOWN_SMALL == nDir); + const bool bHuge( + MOVE_LEFT_HUGE == nDir || + MOVE_UP_HUGE == nDir || + MOVE_RIGHT_HUGE == nDir || + MOVE_DOWN_HUGE == nDir); + SwMove nAnchorDir = SwMove::UP; + switch(nDir) + { + case MOVE_LEFT_SMALL: + case MOVE_LEFT_HUGE: + case MOVE_LEFT_BIG: + nX = -1; + nAnchorDir = SwMove::LEFT; + break; + case MOVE_UP_SMALL: + case MOVE_UP_HUGE: + case MOVE_UP_BIG: + nY = -1; + break; + case MOVE_RIGHT_SMALL: + case MOVE_RIGHT_HUGE: + case MOVE_RIGHT_BIG: + nX = +1; + nAnchorDir = SwMove::RIGHT; + break; + case MOVE_DOWN_SMALL: + case MOVE_DOWN_HUGE: + case MOVE_DOWN_BIG: + nY = +1; + nAnchorDir = SwMove::DOWN; + break; + } + + if(0 != nX || 0 != nY) + { + FlyProtectFlags nProtect = rSh.IsSelObjProtected( FlyProtectFlags::Pos|FlyProtectFlags::Size ); + Size aSnap( rSh.GetViewOptions()->GetSnapSize() ); + short nDiv = rSh.GetViewOptions()->GetDivisionX(); + if ( nDiv > 0 ) + aSnap.setWidth( std::max( sal_uLong(1), static_cast<sal_uLong>(aSnap.Width()) / nDiv ) ); + nDiv = rSh.GetViewOptions()->GetDivisionY(); + if ( nDiv > 0 ) + aSnap.setHeight( std::max( sal_uLong(1), static_cast<sal_uLong>(aSnap.Height()) / nDiv ) ); + + if(bOnePixel) + { + aSnap = PixelToLogic(Size(1,1)); + } + else if(bHuge) + { + // #i121236# 567twips == 1cm, but just take three times the normal snap + aSnap = Size(aSnap.Width() * 3, aSnap.Height() * 3); + } + + nX *= aSnap.Width(); + nY *= aSnap.Height(); + + SdrView *pSdrView = rSh.GetDrawView(); + const SdrHdlList& rHdlList = pSdrView->GetHdlList(); + SdrHdl* pHdl = rHdlList.GetFocusHdl(); + rSh.StartAllAction(); + if(nullptr == pHdl) + { + // now move the selected draw objects + // if the object's position is not protected + if(!(nProtect&FlyProtectFlags::Pos)) + { + // Check if object is anchored as character and move direction + bool bDummy1, bDummy2; + const bool bVertAnchor = rSh.IsFrameVertical( true, bDummy1, bDummy2 ); + bool bHoriMove = !bVertAnchor == !( nDir % 2 ); + bool bMoveAllowed = + !bHoriMove || (rSh.GetAnchorId() != RndStdIds::FLY_AS_CHAR); + if ( bMoveAllowed ) + { + pSdrView->MoveAllMarked(Size(nX, nY)); + rSh.SetModified(); + } + } + } + else + { + // move handle with index nHandleIndex + if (nX || nY) + { + if( SdrHdlKind::Anchor == pHdl->GetKind() || + SdrHdlKind::Anchor_TR == pHdl->GetKind() ) + { + // anchor move cannot be allowed when position is protected + if(!(nProtect&FlyProtectFlags::Pos)) + rSh.MoveAnchor( nAnchorDir ); + } + //now resize if size is protected + else if(!(nProtect&FlyProtectFlags::Size)) + { + // now move the Handle (nX, nY) + Point aStartPoint(pHdl->GetPos()); + Point aEndPoint(pHdl->GetPos() + Point(nX, nY)); + const SdrDragStat& rDragStat = pSdrView->GetDragStat(); + + // start dragging + pSdrView->BegDragObj(aStartPoint, nullptr, pHdl, 0); + + if(pSdrView->IsDragObj()) + { + bool bWasNoSnap = rDragStat.IsNoSnap(); + bool bWasSnapEnabled = pSdrView->IsSnapEnabled(); + + // switch snapping off + if(!bWasNoSnap) + const_cast<SdrDragStat&>(rDragStat).SetNoSnap(); + if(bWasSnapEnabled) + pSdrView->SetSnapEnabled(false); + + pSdrView->MovAction(aEndPoint); + pSdrView->EndDragObj(); + rSh.SetModified(); + + // restore snap + if(!bWasNoSnap) + const_cast<SdrDragStat&>(rDragStat).SetNoSnap(bWasNoSnap); + if(bWasSnapEnabled) + pSdrView->SetSnapEnabled(bWasSnapEnabled); + } + } + } + } + rSh.EndAllAction(); + } + + rSh.EndUndo(); +} + +/** + * KeyEvents + */ +void SwEditWin::KeyInput(const KeyEvent &rKEvt) +{ + SwWrtShell &rSh = m_rView.GetWrtShell(); + + if (comphelper::LibreOfficeKit::isActive() && m_rView.GetPostItMgr()) + { + if (vcl::Window* pWindow = m_rView.GetPostItMgr()->GetActiveSidebarWin()) + { + pWindow->KeyInput(rKEvt); + return; + } + } + + sal_uInt16 nKey = rKEvt.GetKeyCode().GetCode(); + + if (nKey == KEY_ESCAPE) + { + if (m_pApplyTempl && m_pApplyTempl->m_pFormatClipboard) + { + m_pApplyTempl->m_pFormatClipboard->Erase(); + SetApplyTemplate(SwApplyTemplate()); + m_rView.GetViewFrame().GetBindings().Invalidate(SID_FORMATPAINTBRUSH); + } + else if (rSh.IsHeaderFooterEdit()) + { + bool bHeader = bool(FrameTypeFlags::HEADER & rSh.GetFrameType(nullptr, false)); + if (bHeader) + rSh.SttPg(); + else + rSh.EndPg(); + rSh.ToggleHeaderFooterEdit(); + } + } + + SfxObjectShell *pObjSh = m_rView.GetViewFrame().GetObjectShell(); + if ( m_bLockInput || (pObjSh && pObjSh->GetProgress()) ) + // When the progress bar is active or a progress is + // running on a document, no order is being taken + return; + + m_pShadCursor.reset(); + // Do not reset the timer here, otherwise when flooded with events it would never time out + // if every key event stopped and started it again. + comphelper::ScopeGuard keyInputFlushTimerStop([this]() { m_aKeyInputFlushTimer.Stop(); }); + + bool bIsDocReadOnly = m_rView.GetDocShell()->IsReadOnly() && + rSh.IsCursorReadonly(); + + //if the language changes the buffer must be flushed + LanguageType eNewLanguage = GetInputLanguage(); + if(!bIsDocReadOnly && m_eBufferLanguage != eNewLanguage && !m_aInBuffer.isEmpty()) + { + FlushInBuffer(); + } + m_eBufferLanguage = eNewLanguage; + + QuickHelpData aTmpQHD; + if( s_pQuickHlpData->m_bIsDisplayed ) + { + aTmpQHD.Move( *s_pQuickHlpData ); + s_pQuickHlpData->Stop( rSh ); + } + + // OS:the DrawView also needs a readonly-Flag as well + if ( !bIsDocReadOnly && rSh.GetDrawView() && rSh.GetDrawView()->KeyInput( rKEvt, this ) ) + { + rSh.GetView().GetViewFrame().GetBindings().InvalidateAll( false ); + rSh.SetModified(); + return; // Event evaluated by SdrView + } + + if ( m_rView.GetDrawFuncPtr() && m_bInsFrame ) + { + StopInsFrame(); + rSh.Edit(); + } + + bool bFlushBuffer = false; + bool bNormalChar = false; + bool bAppendSpace = s_pQuickHlpData->m_bAppendSpace; + s_pQuickHlpData->m_bAppendSpace = false; + + if (nKey == KEY_F12 && getenv("SW_DEBUG")) + { + if( rKEvt.GetKeyCode().IsShift()) + { + GetView().GetDocShell()->GetDoc()->dumpAsXml(); + } + else + { + SwRootFrame* pLayout = GetView().GetDocShell()->GetWrtShell()->GetLayout(); + pLayout->dumpAsXml( ); + } + return; + } + + KeyEvent aKeyEvent( rKEvt ); + // look for vertical mappings + if( !bIsDocReadOnly && !rSh.IsSelFrameMode() && !rSh.IsObjSelected() ) + { + if( KEY_UP == nKey || KEY_DOWN == nKey || + KEY_LEFT == nKey || KEY_RIGHT == nKey ) + { + // In general, we want to map the direction keys if we are inside + // some vertical formatted text. + // 1. Exception: For a table cursor in a horizontal table, the + // directions should never be mapped. + // 2. Exception: For a table cursor in a vertical table, the + // directions should always be mapped. + const bool bVertText = rSh.IsInVerticalText(); + const bool bTableCursor = rSh.GetTableCursor(); + const bool bVertTable = rSh.IsTableVertical(); + if( ( bVertText && ( !bTableCursor || bVertTable ) ) || + ( bTableCursor && bVertTable ) ) + { + SvxFrameDirection eDirection = rSh.GetTextDirection(); + if (eDirection == SvxFrameDirection::Vertical_LR_BT) + { + // Map from physical to logical, so rotate clockwise. + if (KEY_UP == nKey) + nKey = KEY_RIGHT; + else if (KEY_DOWN == nKey) + nKey = KEY_LEFT; + else if (KEY_LEFT == nKey) + nKey = KEY_UP; + else /* KEY_RIGHT == nKey */ + nKey = KEY_DOWN; + } + else + { + // Attempt to integrate cursor travelling for mongolian layout does not work. + // Thus, back to previous mapping of cursor keys to direction keys. + if( KEY_UP == nKey ) nKey = KEY_LEFT; + else if( KEY_DOWN == nKey ) nKey = KEY_RIGHT; + else if( KEY_LEFT == nKey ) nKey = KEY_DOWN; + else /* KEY_RIGHT == nKey */ nKey = KEY_UP; + } + } + + if ( rSh.IsInRightToLeftText() ) + { + if( KEY_LEFT == nKey ) nKey = KEY_RIGHT; + else if( KEY_RIGHT == nKey ) nKey = KEY_LEFT; + } + + aKeyEvent = KeyEvent( rKEvt.GetCharCode(), + vcl::KeyCode( nKey, rKEvt.GetKeyCode().GetModifier() ), + rKEvt.GetRepeat() ); + } + } + + const vcl::KeyCode& rKeyCode = aKeyEvent.GetKeyCode(); + sal_Unicode aCh = aKeyEvent.GetCharCode(); + + // enable switching to notes anchor with Ctrl - Alt - Page Up/Down + // pressing this inside a note will switch to next/previous note + if ((rKeyCode.IsMod1() && rKeyCode.IsMod2()) && ((rKeyCode.GetCode() == KEY_PAGEUP) || (rKeyCode.GetCode() == KEY_PAGEDOWN))) + { + const bool bNext = rKeyCode.GetCode()==KEY_PAGEDOWN; + const SwFieldType* pFieldType = rSh.GetFieldType( 0, SwFieldIds::Postit ); + rSh.MoveFieldType( pFieldType, bNext ); + return; + } + + if (SwTextContentControl* pTextContentControl = rSh.CursorInsideContentControl()) + { + // Check if this combination of rKeyCode and pTextContentControl should open a popup. + const SwFormatContentControl& rFormatContentControl = pTextContentControl->GetContentControl(); + std::shared_ptr<SwContentControl> pContentControl = rFormatContentControl.GetContentControl(); + if (pContentControl->ShouldOpenPopup(rKeyCode)) + { + SwShellCursor* pCursor = rSh.GetCursor_(); + if (pCursor) + { + VclPtr<SwContentControlButton> pContentControlButton = pCursor->GetContentControlButton(); + if (pContentControlButton) + { + pContentControlButton->StartPopup(); + return; + } + } + } + } + + const SwFrameFormat* pFlyFormat = rSh.GetFlyFrameFormat(); + + if (pFlyFormat) + { + // See if the fly frame's anchor is in a content control. If so, + // try to interact with it. + const SwFormatAnchor& rFormatAnchor = pFlyFormat->GetAnchor(); + SwNode* pAnchorNode = rFormatAnchor.GetAnchorNode(); + if (pAnchorNode) + { + SwTextNode* pTextNode = pAnchorNode->GetTextNode(); + if (pTextNode) + { + sal_Int32 nContentIdx = rFormatAnchor.GetAnchorContentOffset(); + SwTextAttr* pAttr = pTextNode->GetTextAttrAt( + nContentIdx, RES_TXTATR_CONTENTCONTROL, ::sw::GetTextAttrMode::Parent); + if (pAttr) + { + SwTextContentControl* pTextContentControl + = static_txtattr_cast<SwTextContentControl*>(pAttr); + const SwFormatContentControl& rFormatContentControl + = pTextContentControl->GetContentControl(); + std::shared_ptr<SwContentControl> pContentControl + = rFormatContentControl.GetContentControl(); + if (pContentControl->IsInteractingCharacter(aCh)) + { + rSh.GotoContentControl(rFormatContentControl); + return; + } + } + } + } + } + + if( pFlyFormat ) + { + SvMacroItemId nEvent; + + if( 32 <= aCh && + 0 == (( KEY_MOD1 | KEY_MOD2 ) & rKeyCode.GetModifier() )) + nEvent = SvMacroItemId::SwFrmKeyInputAlpha; + else + nEvent = SvMacroItemId::SwFrmKeyInputNoAlpha; + + const SvxMacro* pMacro = pFlyFormat->GetMacro().GetMacroTable().Get( nEvent ); + if( pMacro ) + { + SbxArrayRef xArgs = new SbxArray; + SbxVariableRef xVar = new SbxVariable; + xVar->PutString( pFlyFormat->GetName() ); + xArgs->Put(xVar.get(), 1); + + xVar = new SbxVariable; + if( SvMacroItemId::SwFrmKeyInputAlpha == nEvent ) + xVar->PutChar( aCh ); + else + xVar->PutUShort( rKeyCode.GetModifier() | rKeyCode.GetCode() ); + xArgs->Put(xVar.get(), 2); + + OUString sRet; + rSh.ExecMacro( *pMacro, &sRet, xArgs.get() ); + if( !sRet.isEmpty() && sRet.toInt32()!=0 ) + return ; + } + } + SelectionType nLclSelectionType; + //A is converted to 1 + if( rKeyCode.GetFullCode() == (KEY_A | KEY_MOD1 |KEY_SHIFT) + && rSh.HasDrawView() && + (bool(nLclSelectionType = rSh.GetSelectionType()) && + ((nLclSelectionType & (SelectionType::Frame|SelectionType::Graphic)) || + ((nLclSelectionType & (SelectionType::DrawObject|SelectionType::DbForm)) && + rSh.GetDrawView()->GetMarkedObjectList().GetMarkCount() == 1)))) + { + SdrHdlList& rHdlList = const_cast<SdrHdlList&>(rSh.GetDrawView()->GetHdlList()); + SdrHdl* pAnchor = rHdlList.GetHdl(SdrHdlKind::Anchor); + if ( ! pAnchor ) + pAnchor = rHdlList.GetHdl(SdrHdlKind::Anchor_TR); + if(pAnchor) + rHdlList.SetFocusHdl(pAnchor); + return; + } + + SvxAutoCorrCfg* pACfg = nullptr; + SvxAutoCorrect* pACorr = nullptr; + + uno::Reference< frame::XDispatchRecorder > xRecorder = + m_rView.GetViewFrame().GetBindings().GetRecorder(); + if ( !xRecorder.is() ) + { + pACfg = &SvxAutoCorrCfg::Get(); + pACorr = pACfg->GetAutoCorrect(); + } + + SwModuleOptions* pModOpt = SW_MOD()->GetModuleConfig(); + + OUString sFormulaEntry; + + enum class SwKeyState { CheckKey, InsChar, InsTab, + NoNum, NumOff, NumOrNoNum, NumDown, NumUp, + NumIndentInc, NumIndentDec, + + OutlineLvOff, + NextCell, PrevCell, OutlineUp, OutlineDown, + GlossaryExpand, NextPrevGlossary, + AutoFormatByInput, + NextObject, PrevObject, + KeyToView, + LaunchOLEObject, GoIntoFly, GoIntoDrawing, + EnterDrawHandleMode, + CheckDocReadOnlyKeys, + CheckAutoCorrect, EditFormula, + ColLeftBig, ColRightBig, + ColLeftSmall, ColRightSmall, + ColBottomBig, + ColBottomSmall, + CellLeftBig, CellRightBig, + CellLeftSmall, CellRightSmall, + CellTopBig, CellBottomBig, + CellTopSmall, CellBottomSmall, + + Fly_Change, Draw_Change, + SpecialInsert, + EnterCharCell, + GotoNextFieldMark, + GotoPrevFieldMark, + End }; + + SwKeyState eKeyState = bIsDocReadOnly ? SwKeyState::CheckDocReadOnlyKeys : SwKeyState::CheckKey; + SwKeyState eNextKeyState = SwKeyState::End; + sal_uInt8 nDir = 0; + + if (m_nKS_NUMDOWN_Count > 0) + m_nKS_NUMDOWN_Count--; + + if (m_nKS_NUMINDENTINC_Count > 0) + m_nKS_NUMINDENTINC_Count--; + + while( SwKeyState::End != eKeyState ) + { + SwKeyState eFlyState = SwKeyState::KeyToView; + + switch( eKeyState ) + { + case SwKeyState::CheckKey: + eKeyState = SwKeyState::KeyToView; // default forward to View + + if (!comphelper::LibreOfficeKit::isActive() && + !rKeyCode.IsMod2() && '=' == aCh && + !rSh.IsTableMode() && rSh.GetTableFormat() && + rSh.IsSttPara() && + !rSh.HasReadonlySel()) + { + // at the beginning of the table's cell a '=' -> + // call EditRow (F2-functionality) + // [Avoid this for LibreOfficeKit, as the separate input window + // steals the focus & things go wrong - the user never gets + // the focus back.] + rSh.Push(); + if( !rSh.MoveSection( GoCurrSection, fnSectionStart) && + !rSh.IsTableBoxTextFormat() ) + { + // is at the beginning of the box + eKeyState = SwKeyState::EditFormula; + if( rSh.HasMark() ) + rSh.SwapPam(); + else + rSh.SttSelect(); + rSh.MoveSection( GoCurrSection, fnSectionEnd ); + rSh.Pop(); + rSh.EndSelect(); + sFormulaEntry = "="; + } + else + rSh.Pop(SwCursorShell::PopMode::DeleteCurrent); + } + else + { + if( pACorr && aTmpQHD.HasContent() && !rSh.HasSelection() && + !rSh.HasReadonlySel() && !aTmpQHD.m_bIsAutoText && + pACorr->GetSwFlags().nAutoCmpltExpandKey == + (rKeyCode.GetModifier() | rKeyCode.GetCode()) ) + { + eKeyState = SwKeyState::GlossaryExpand; + break; + } + + switch( rKeyCode.GetModifier() | rKeyCode.GetCode() ) + { + case KEY_RIGHT | KEY_MOD2: + eKeyState = SwKeyState::ColRightBig; + eFlyState = SwKeyState::Fly_Change; + nDir = MOVE_RIGHT_SMALL; + goto KEYINPUT_CHECKTABLE; + + case KEY_LEFT | KEY_MOD2: + eKeyState = SwKeyState::ColRightSmall; + eFlyState = SwKeyState::Fly_Change; + nDir = MOVE_LEFT_SMALL; + goto KEYINPUT_CHECKTABLE; + + case KEY_RIGHT | KEY_MOD2 | KEY_SHIFT: + eKeyState = SwKeyState::ColLeftSmall; + goto KEYINPUT_CHECKTABLE; + + case KEY_LEFT | KEY_MOD2 | KEY_SHIFT: + eKeyState = SwKeyState::ColLeftBig; + goto KEYINPUT_CHECKTABLE; + + case KEY_RIGHT | KEY_MOD2 | KEY_MOD1: + eKeyState = SwKeyState::CellRightBig; + goto KEYINPUT_CHECKTABLE; + + case KEY_LEFT | KEY_MOD2 | KEY_MOD1: + eKeyState = SwKeyState::CellRightSmall; + goto KEYINPUT_CHECKTABLE; + + case KEY_RIGHT | KEY_MOD2 | KEY_SHIFT | KEY_MOD1: + eKeyState = SwKeyState::CellLeftSmall; + goto KEYINPUT_CHECKTABLE; + + case KEY_LEFT | KEY_MOD2 | KEY_SHIFT | KEY_MOD1: + eKeyState = SwKeyState::CellLeftBig; + goto KEYINPUT_CHECKTABLE; + + case KEY_UP | KEY_MOD2: + eKeyState = SwKeyState::ColBottomSmall; + eFlyState = SwKeyState::Fly_Change; + nDir = MOVE_UP_SMALL; + goto KEYINPUT_CHECKTABLE; + + case KEY_DOWN | KEY_MOD2: + eKeyState = SwKeyState::ColBottomBig; + eFlyState = SwKeyState::Fly_Change; + nDir = MOVE_DOWN_SMALL; + goto KEYINPUT_CHECKTABLE; + + case KEY_UP | KEY_MOD2 | KEY_MOD1: + eKeyState = SwKeyState::CellBottomSmall; + goto KEYINPUT_CHECKTABLE; + + case KEY_DOWN | KEY_MOD2 | KEY_MOD1: + eKeyState = SwKeyState::CellBottomBig; + goto KEYINPUT_CHECKTABLE; + + case KEY_UP | KEY_MOD2 | KEY_SHIFT | KEY_MOD1: + eKeyState = SwKeyState::CellTopBig; + goto KEYINPUT_CHECKTABLE; + + case KEY_DOWN | KEY_MOD2 | KEY_SHIFT | KEY_MOD1: + eKeyState = SwKeyState::CellTopSmall; + goto KEYINPUT_CHECKTABLE; + +KEYINPUT_CHECKTABLE: + // Resolve bugs 49091, 53190, 93402 and + // https://bz.apache.org/ooo/show_bug.cgi?id=113502 + // but provide an option for restoring interactive + // table sizing functionality when needed. + if ( + ! (Window::GetIndicatorState() & KeyIndicatorState::CAPSLOCK) + && m_rView.KeyInput( aKeyEvent ) // Keystroke is customized + ) + { + bFlushBuffer = true; + bNormalChar = false; + eKeyState = SwKeyState::End; + break ; + } + + if( rSh.IsTableMode() || !rSh.GetTableFormat() ) + { + if(!pFlyFormat && SwKeyState::KeyToView != eFlyState && + (rSh.GetSelectionType() & (SelectionType::DrawObject|SelectionType::DbForm)) && + rSh.GetDrawView()->AreObjectsMarked()) + eKeyState = SwKeyState::Draw_Change; + + if( pFlyFormat ) + eKeyState = eFlyState; + else if( SwKeyState::Draw_Change != eKeyState) + eKeyState = SwKeyState::EnterCharCell; + } + break; + + // huge object move + case KEY_RIGHT | KEY_SHIFT: + case KEY_LEFT | KEY_SHIFT: + case KEY_UP | KEY_SHIFT: + case KEY_DOWN | KEY_SHIFT: + { + const SelectionType nSelectionType = rSh.GetSelectionType(); + if ( ( pFlyFormat + && ( nSelectionType & (SelectionType::Frame|SelectionType::Ole|SelectionType::Graphic) ) ) + || ( ( nSelectionType & (SelectionType::DrawObject|SelectionType::DbForm) ) + && rSh.GetDrawView()->AreObjectsMarked() ) ) + { + eKeyState = pFlyFormat ? SwKeyState::Fly_Change : SwKeyState::Draw_Change; + if (nSelectionType & SelectionType::DrawObject) + { + // tdf#137964: always move the DrawObject if one is selected + eKeyState = SwKeyState::Draw_Change; + } + switch ( rKeyCode.GetCode() ) + { + case KEY_RIGHT: nDir = MOVE_RIGHT_HUGE; break; + case KEY_LEFT: nDir = MOVE_LEFT_HUGE; break; + case KEY_UP: nDir = MOVE_UP_HUGE; break; + case KEY_DOWN: nDir = MOVE_DOWN_HUGE; break; + } + } + break; + } + + case KEY_LEFT: + case KEY_LEFT | KEY_MOD1: + { + bool bMod1 = 0 != (rKeyCode.GetModifier() & KEY_MOD1); + if(!bMod1) + { + eFlyState = SwKeyState::Fly_Change; + nDir = MOVE_LEFT_BIG; + } + goto KEYINPUT_CHECKTABLE_INSDEL; + } + case KEY_RIGHT | KEY_MOD1: + { + goto KEYINPUT_CHECKTABLE_INSDEL; + } + case KEY_UP: + case KEY_UP | KEY_MOD1: + { + bool bMod1 = 0 != (rKeyCode.GetModifier() & KEY_MOD1); + if(!bMod1) + { + eFlyState = SwKeyState::Fly_Change; + nDir = MOVE_UP_BIG; + } + goto KEYINPUT_CHECKTABLE_INSDEL; + } + case KEY_DOWN: + case KEY_DOWN | KEY_MOD1: + { + bool bMod1 = 0 != (rKeyCode.GetModifier() & KEY_MOD1); + if(!bMod1) + { + ::sw::mark::IFieldmark* pMark = rSh.GetCurrentFieldmark(); + if (auto pDropDown = dynamic_cast<FieldmarkWithDropDownButton*>(pMark)) + { + pDropDown->LaunchPopup(); + eKeyState = SwKeyState::End; + break; + } + eFlyState = SwKeyState::Fly_Change; + nDir = MOVE_DOWN_BIG; + } + goto KEYINPUT_CHECKTABLE_INSDEL; + } + +KEYINPUT_CHECKTABLE_INSDEL: + if( rSh.IsTableMode() || !rSh.GetTableFormat() ) + { + const SelectionType nSelectionType = rSh.GetSelectionType(); + + eKeyState = SwKeyState::KeyToView; + if(SwKeyState::KeyToView != eFlyState) + { + if((nSelectionType & (SelectionType::DrawObject|SelectionType::DbForm)) && + rSh.GetDrawView()->AreObjectsMarked()) + eKeyState = SwKeyState::Draw_Change; + else if(nSelectionType & (SelectionType::Frame|SelectionType::Ole|SelectionType::Graphic)) + eKeyState = SwKeyState::Fly_Change; + } + } + break; + + + case KEY_DELETE: + if ( !rSh.HasReadonlySel() || rSh.CursorInsideInputField()) + { + if (rSh.IsInFrontOfLabel() && rSh.NumOrNoNum()) + eKeyState = SwKeyState::NumOrNoNum; + } + else if (!rSh.IsCursorInParagraphMetadataField()) + { + rSh.InfoReadOnlyDialog(false); + eKeyState = SwKeyState::End; + } + break; + + case KEY_RETURN: + { + if ( !rSh.HasReadonlySel() + && !rSh.CursorInsideInputField() + && !rSh.CursorInsideContentControl() ) + { + const SelectionType nSelectionType = rSh.GetSelectionType(); + if(nSelectionType & SelectionType::Ole) + eKeyState = SwKeyState::LaunchOLEObject; + else if(nSelectionType & SelectionType::Frame) + eKeyState = SwKeyState::GoIntoFly; + else if((nSelectionType & SelectionType::DrawObject) && + !(nSelectionType & SelectionType::DrawObjectEditMode) && + rSh.GetDrawView()->GetMarkedObjectList().GetMarkCount() == 1) + { + eKeyState = SwKeyState::GoIntoDrawing; + if (lcl_goIntoTextBox(*this, rSh)) + eKeyState = SwKeyState::GoIntoFly; + } + else if( aTmpQHD.HasContent() && !rSh.HasSelection() && + aTmpQHD.m_bIsAutoText ) + eKeyState = SwKeyState::GlossaryExpand; + + //RETURN and empty paragraph in numbering -> end numbering + else if( m_aInBuffer.isEmpty() && + rSh.GetNumRuleAtCurrCursorPos() && + !rSh.GetNumRuleAtCurrCursorPos()->IsOutlineRule() && + !rSh.HasSelection() && + rSh.IsSttPara() && rSh.IsEndPara() ) + { + eKeyState = SwKeyState::NumOff; + eNextKeyState = SwKeyState::OutlineLvOff; + } + //RETURN for new paragraph with AutoFormatting + else if( pACfg && pACfg->IsAutoFormatByInput() && + !(nSelectionType & (SelectionType::Graphic | + SelectionType::Ole | SelectionType::Frame | + SelectionType::TableCell | SelectionType::DrawObject | + SelectionType::DrawObjectEditMode)) ) + { + eKeyState = SwKeyState::AutoFormatByInput; + } + else + { + eNextKeyState = eKeyState; + eKeyState = SwKeyState::CheckAutoCorrect; + } + } + } + break; + case KEY_RETURN | KEY_MOD2: + { + if ( !rSh.HasReadonlySel() + && !rSh.IsSttPara() + && rSh.GetNumRuleAtCurrCursorPos() + && !rSh.CursorInsideInputField() ) + { + eKeyState = SwKeyState::NoNum; + } + else if( rSh.CanSpecialInsert() ) + eKeyState = SwKeyState::SpecialInsert; + } + break; + case KEY_BACKSPACE: + case KEY_BACKSPACE | KEY_SHIFT: + if ( !rSh.HasReadonlySel() || rSh.CursorInsideInputField()) + { + bool bDone = false; + // try to add comment for code snip: + // Remove the paragraph indent, if the cursor is at the + // beginning of a paragraph, there is no selection + // and no numbering rule found at the current paragraph + // Also try to remove indent, if current paragraph + // has numbering rule, but isn't counted and only + // key <backspace> is hit. + const bool bOnlyBackspaceKey( KEY_BACKSPACE == rKeyCode.GetFullCode() ); + if ( rSh.IsSttPara() + && !rSh.HasSelection() + && ( rSh.GetNumRuleAtCurrCursorPos() == nullptr + || ( rSh.IsNoNum() && bOnlyBackspaceKey ) ) ) + { + bDone = rSh.TryRemoveIndent(); + } + + if (bDone) + eKeyState = SwKeyState::End; + else + { + if ( rSh.IsSttPara() && !rSh.IsNoNum() ) + { + if (m_nKS_NUMDOWN_Count > 0 && + 0 < rSh.GetNumLevel()) + { + eKeyState = SwKeyState::NumUp; + m_nKS_NUMDOWN_Count = 2; + bDone = true; + } + else if (m_nKS_NUMINDENTINC_Count > 0) + { + eKeyState = SwKeyState::NumIndentDec; + m_nKS_NUMINDENTINC_Count = 2; + bDone = true; + } + } + + // If the cursor is in an empty paragraph, which has + // a numbering, but not the outline numbering, and + // there is no selection, the numbering has to be + // deleted on key <Backspace>. + // Otherwise method <SwEditShell::NumOrNoNum(..)> + // should only change the <IsCounted()> state of + // the current paragraph depending of the key. + // On <backspace> it is set to <false>, + // on <shift-backspace> it is set to <true>. + // Thus, assure that method <SwEditShell::NumOrNum(..)> + // is only called for the intended purpose. + if ( !bDone && rSh.IsSttPara() ) + { + bool bCallNumOrNoNum( false ); + if ( bOnlyBackspaceKey && !rSh.IsNoNum() ) + { + bCallNumOrNoNum = true; + } + else if ( !bOnlyBackspaceKey && rSh.IsNoNum() ) + { + bCallNumOrNoNum = true; + } + else if ( bOnlyBackspaceKey + && rSh.IsSttPara() + && rSh.IsEndPara() + && !rSh.HasSelection() ) + { + const SwNumRule* pCurrNumRule( rSh.GetNumRuleAtCurrCursorPos() ); + if ( pCurrNumRule != nullptr + && pCurrNumRule != rSh.GetOutlineNumRule() ) + { + bCallNumOrNoNum = true; + } + } + if ( bCallNumOrNoNum + && rSh.NumOrNoNum( !bOnlyBackspaceKey ) ) + { + eKeyState = SwKeyState::NumOrNoNum; + } + } + } + } + else if (!rSh.IsCursorInParagraphMetadataField()) + { + rSh.InfoReadOnlyDialog(false); + eKeyState = SwKeyState::End; + } + break; + + case KEY_RIGHT: + { + eFlyState = SwKeyState::Fly_Change; + nDir = MOVE_RIGHT_BIG; + goto KEYINPUT_CHECKTABLE_INSDEL; + } + case KEY_TAB: + { + // Rich text contentControls accept tabs and fieldmarks and other rich text, + // so first act on cases that are not a content control + SwTextContentControl* pTextContentControl = rSh.CursorInsideContentControl(); + if ((rSh.IsFormProtected() && !pTextContentControl) || + rSh.GetCurrentFieldmark() || rSh.GetChar(false)==CH_TXT_ATR_FORMELEMENT) + { + eKeyState = SwKeyState::GotoNextFieldMark; + } + else if ( !rSh.IsMultiSelection() && rSh.CursorInsideInputField() ) + { + GetView().GetViewFrame().GetDispatcher()->Execute( FN_GOTO_NEXT_INPUTFLD ); + eKeyState = SwKeyState::End; + } + else if( rSh.GetNumRuleAtCurrCursorPos() + && rSh.IsSttOfPara() + && !rSh.HasReadonlySel() ) + { + if (numfunc::NumDownChangesIndent(rSh)) + { + eKeyState = SwKeyState::NumDown; + } + else + { + eKeyState = SwKeyState::InsTab; + } + } + else if (rSh.GetSelectionType() & + (SelectionType::Graphic | + SelectionType::Frame | + SelectionType::Ole | + SelectionType::DrawObject | + SelectionType::DbForm)) + { + eKeyState = SwKeyState::NextObject; + } + else if ( rSh.GetTableFormat() ) + { + if( rSh.HasSelection() || rSh.HasReadonlySel() ) + eKeyState = SwKeyState::NextCell; + else + { + eKeyState = SwKeyState::CheckAutoCorrect; + eNextKeyState = SwKeyState::NextCell; + } + } + else if (pTextContentControl) + { + auto pCC = pTextContentControl->GetContentControl().GetContentControl(); + if (pCC) + { + switch (pCC->GetType()) + { + case SwContentControlType::RICH_TEXT: + eKeyState = SwKeyState::InsTab; + break; + default: + eKeyState = SwKeyState::GotoNextFieldMark; + } + } + } + else + { + eKeyState = SwKeyState::InsTab; + if( rSh.IsSttOfPara() && !rSh.HasReadonlySel() ) + { + SwTextFormatColl* pColl = rSh.GetCurTextFormatColl(); + if( pColl && + + pColl->IsAssignedToListLevelOfOutlineStyle() + && MAXLEVEL-1 > pColl->GetAssignedOutlineStyleLevel() ) + eKeyState = SwKeyState::OutlineDown; + } + } + } + break; + case KEY_TAB | KEY_SHIFT: + { + SwTextContentControl* pTextContentControl = rSh.CursorInsideContentControl(); + if ((rSh.IsFormProtected() && !pTextContentControl) || + rSh.GetCurrentFieldmark()|| rSh.GetChar(false)==CH_TXT_ATR_FORMELEMENT) + { + eKeyState = SwKeyState::GotoPrevFieldMark; + } + else if ( !rSh.IsMultiSelection() && rSh.CursorInsideInputField() ) + { + GetView().GetViewFrame().GetDispatcher()->Execute( FN_GOTO_PREV_INPUTFLD ); + eKeyState = SwKeyState::End; + } + else if( rSh.GetNumRuleAtCurrCursorPos() + && rSh.IsSttOfPara() + && !rSh.HasReadonlySel() ) + { + eKeyState = SwKeyState::NumUp; + } + else if (rSh.GetSelectionType() & + (SelectionType::Graphic | + SelectionType::Frame | + SelectionType::Ole | + SelectionType::DrawObject | + SelectionType::DbForm)) + { + eKeyState = SwKeyState::PrevObject; + } + else if ( rSh.GetTableFormat() ) + { + if( rSh.HasSelection() || rSh.HasReadonlySel() ) + eKeyState = SwKeyState::PrevCell; + else + { + eKeyState = SwKeyState::CheckAutoCorrect; + eNextKeyState = SwKeyState::PrevCell; + } + } + else if (pTextContentControl) + { + eKeyState = SwKeyState::GotoPrevFieldMark; + } + else + { + eKeyState = SwKeyState::End; + if( rSh.IsSttOfPara() && !rSh.HasReadonlySel() ) + { + SwTextFormatColl* pColl = rSh.GetCurTextFormatColl(); + if( pColl && + pColl->IsAssignedToListLevelOfOutlineStyle() && + 0 < pColl->GetAssignedOutlineStyleLevel()) + eKeyState = SwKeyState::OutlineUp; + } + } + } + break; + case KEY_TAB | KEY_MOD1: + case KEY_TAB | KEY_MOD2: + if( !rSh.HasReadonlySel() ) + { + if( aTmpQHD.HasContent() && !rSh.HasSelection() ) + { + // Next auto-complete suggestion + aTmpQHD.Next( pACorr && + pACorr->GetSwFlags().bAutoCmpltEndless ); + eKeyState = SwKeyState::NextPrevGlossary; + } + else if( rSh.GetTableFormat() ) + eKeyState = SwKeyState::InsTab; + else if((rSh.GetSelectionType() & + (SelectionType::DrawObject|SelectionType::DbForm| + SelectionType::Frame|SelectionType::Ole|SelectionType::Graphic)) && + rSh.GetDrawView()->AreObjectsMarked()) + eKeyState = SwKeyState::EnterDrawHandleMode; + else + { + if ( !rSh.IsMultiSelection() + && numfunc::ChangeIndentOnTabAtFirstPosOfFirstListItem() ) + eKeyState = SwKeyState::NumIndentInc; + } + } + break; + + case KEY_TAB | KEY_MOD1 | KEY_SHIFT: + { + if( aTmpQHD.HasContent() && !rSh.HasSelection() && + !rSh.HasReadonlySel() ) + { + // Previous auto-complete suggestion. + aTmpQHD.Previous( pACorr && + pACorr->GetSwFlags().bAutoCmpltEndless ); + eKeyState = SwKeyState::NextPrevGlossary; + } + else if((rSh.GetSelectionType() & (SelectionType::DrawObject|SelectionType::DbForm| + SelectionType::Frame|SelectionType::Ole|SelectionType::Graphic)) && + rSh.GetDrawView()->AreObjectsMarked()) + { + eKeyState = SwKeyState::EnterDrawHandleMode; + } + else + { + if ( !rSh.IsMultiSelection() + && numfunc::ChangeIndentOnTabAtFirstPosOfFirstListItem() ) + eKeyState = SwKeyState::NumIndentDec; + } + } + break; + case KEY_F2 : + if( !rSh.HasReadonlySel() ) + { + const SelectionType nSelectionType = rSh.GetSelectionType(); + if(nSelectionType & SelectionType::Frame) + eKeyState = SwKeyState::GoIntoFly; + else if(nSelectionType & SelectionType::DrawObject) + { + eKeyState = SwKeyState::GoIntoDrawing; + if (lcl_goIntoTextBox(*this, rSh)) + eKeyState = SwKeyState::GoIntoFly; + } + } + break; + } + } + break; + case SwKeyState::CheckDocReadOnlyKeys: + { + eKeyState = SwKeyState::KeyToView; + switch( rKeyCode.GetModifier() | rKeyCode.GetCode() ) + { + case KEY_TAB: + case KEY_TAB | KEY_SHIFT: + bNormalChar = false; + eKeyState = SwKeyState::End; + if ( rSh.GetSelectionType() & + (SelectionType::Graphic | + SelectionType::Frame | + SelectionType::Ole | + SelectionType::DrawObject | + SelectionType::DbForm)) + + { + eKeyState = (rKeyCode.GetModifier() & KEY_SHIFT) ? + SwKeyState::PrevObject : SwKeyState::NextObject; + } + else if ( !rSh.IsMultiSelection() && rSh.CursorInsideInputField() ) + { + GetView().GetViewFrame().GetDispatcher()->Execute( + KEY_SHIFT != rKeyCode.GetModifier() ? FN_GOTO_NEXT_INPUTFLD : FN_GOTO_PREV_INPUTFLD ); + } + else + { + rSh.SelectNextPrevHyperlink( KEY_SHIFT != rKeyCode.GetModifier() ); + } + break; + case KEY_RETURN: + { + const SelectionType nSelectionType = rSh.GetSelectionType(); + if(nSelectionType & SelectionType::Frame) + eKeyState = SwKeyState::GoIntoFly; + else + { + SfxItemSetFixed<RES_TXTATR_INETFMT, RES_TXTATR_INETFMT> aSet(rSh.GetAttrPool()); + rSh.GetCurAttr(aSet); + if(SfxItemState::SET == aSet.GetItemState(RES_TXTATR_INETFMT, false)) + { + const SfxPoolItem& rItem = aSet.Get(RES_TXTATR_INETFMT); + bNormalChar = false; + eKeyState = SwKeyState::End; + rSh.ClickToINetAttr(static_cast<const SwFormatINetFormat&>(rItem)); + } + } + } + break; + } + } + break; + + case SwKeyState::EnterCharCell: + { + eKeyState = SwKeyState::KeyToView; + switch ( rKeyCode.GetModifier() | rKeyCode.GetCode() ) + { + case KEY_RIGHT | KEY_MOD2: + rSh.Right( SwCursorSkipMode::Chars, false, 1, false ); + eKeyState = SwKeyState::End; + FlushInBuffer(); + break; + case KEY_LEFT | KEY_MOD2: + rSh.Left( SwCursorSkipMode::Chars, false, 1, false ); + eKeyState = SwKeyState::End; + FlushInBuffer(); + break; + } + } + break; + + case SwKeyState::KeyToView: + { + eKeyState = SwKeyState::End; + bNormalChar = + !rKeyCode.IsMod2() && + rKeyCode.GetModifier() != KEY_MOD1 && + rKeyCode.GetModifier() != (KEY_MOD1|KEY_SHIFT) && + SW_ISPRINTABLE( aCh ); + + if( bNormalChar && rSh.IsInFrontOfLabel() ) + { + rSh.NumOrNoNum(); + } + + if( !m_aInBuffer.isEmpty() && ( !bNormalChar || bIsDocReadOnly )) + FlushInBuffer(); + + if (rSh.HasReadonlySel() + && ( rKeyCode.GetFunction() == KeyFuncType::PASTE + || rKeyCode.GetFunction() == KeyFuncType::CUT)) + { + rSh.InfoReadOnlyDialog(true); + eKeyState = SwKeyState::End; + } + else if( m_rView.KeyInput( aKeyEvent ) ) + { + bFlushBuffer = true; + bNormalChar = false; + } + else + { + // Because Sfx accelerators are only called when they were + // enabled at the last status update, copy has to called + // 'forcefully' by us if necessary. + if( rKeyCode.GetFunction() == KeyFuncType::COPY ) + GetView().GetViewFrame().GetBindings().Execute(SID_COPY); + + if( !bIsDocReadOnly && bNormalChar ) + { + const SelectionType nSelectionType = rSh.GetSelectionType(); + const bool bDrawObject = (nSelectionType & SelectionType::DrawObject) && + !(nSelectionType & SelectionType::DrawObjectEditMode) && + rSh.GetDrawView()->GetMarkedObjectList().GetMarkCount() == 1; + + bool bTextBox = false; + if (bDrawObject && lcl_goIntoTextBox(*this, rSh)) + // A draw shape was selected, but it has a TextBox, + // start editing that instead when the normal + // character is pressed. + bTextBox = true; + + if (bDrawObject && !bTextBox) + { + SdrObject* pObj = rSh.GetDrawView()->GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj(); + if(pObj) + { + EnterDrawTextMode(pObj->GetLogicRect().Center()); + if ( auto pSwDrawTextShell = dynamic_cast< SwDrawTextShell *>( m_rView.GetCurShell() ) ) + pSwDrawTextShell->Init(); + rSh.GetDrawView()->KeyInput( rKEvt, this ); + } + } + else if (nSelectionType & SelectionType::Frame || bTextBox) + { + rSh.UnSelectFrame(); + rSh.LeaveSelFrameMode(); + m_rView.AttrChangedNotify(nullptr); + rSh.MoveSection( GoCurrSection, fnSectionEnd ); + } + eKeyState = SwKeyState::InsChar; + } + else + { + bNormalChar = false; + Window::KeyInput( aKeyEvent ); + } + } + } + break; + case SwKeyState::LaunchOLEObject: + { + rSh.LaunchOLEObj(); + eKeyState = SwKeyState::End; + } + break; + case SwKeyState::GoIntoFly: + { + rSh.UnSelectFrame(); + rSh.LeaveSelFrameMode(); + m_rView.AttrChangedNotify(nullptr); + rSh.MoveSection( GoCurrSection, fnSectionEnd ); + eKeyState = SwKeyState::End; + } + break; + case SwKeyState::GoIntoDrawing: + { + if (SdrMark* pMark = rSh.GetDrawView()->GetMarkedObjectList().GetMark(0)) + { + SdrObject* pObj = pMark->GetMarkedSdrObj(); + if(pObj) + { + EnterDrawTextMode(pObj->GetLogicRect().Center()); + if (auto pSwDrawTextShell = dynamic_cast< SwDrawTextShell *>( m_rView.GetCurShell() ) ) + pSwDrawTextShell->Init(); + } + } + eKeyState = SwKeyState::End; + } + break; + case SwKeyState::EnterDrawHandleMode: + { + const SdrHdlList& rHdlList = rSh.GetDrawView()->GetHdlList(); + bool bForward(!aKeyEvent.GetKeyCode().IsShift()); + + const_cast<SdrHdlList&>(rHdlList).TravelFocusHdl(bForward); + eKeyState = SwKeyState::End; + } + break; + case SwKeyState::InsTab: + if( dynamic_cast<const SwWebView*>( &m_rView) != nullptr) // no Tab for WebView + { + // then it should be passed along + Window::KeyInput( aKeyEvent ); + eKeyState = SwKeyState::End; + break; + } + aCh = '\t'; + [[fallthrough]]; + case SwKeyState::InsChar: + { + if (rSh.CursorInsideContentControl()) + { + const SwPosition* pStart = rSh.GetCursor()->Start(); + SwTextNode* pTextNode = pStart->GetNode().GetTextNode(); + if (pTextNode) + { + sal_Int32 nIndex = pStart->GetContentIndex(); + SwTextAttr* pAttr = pTextNode->GetTextAttrAt(nIndex, RES_TXTATR_CONTENTCONTROL, ::sw::GetTextAttrMode::Parent); + if (pAttr) + { + auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr); + const SwFormatContentControl& rFormatContentControl = pTextContentControl->GetContentControl(); + std::shared_ptr<SwContentControl> pContentControl = rFormatContentControl.GetContentControl(); + if (pContentControl->IsInteractingCharacter(aCh)) + { + rSh.GotoContentControl(rFormatContentControl); + eKeyState = SwKeyState::End; + break; + } + } + } + } + + if (rSh.GetChar(false)==CH_TXT_ATR_FORMELEMENT) + { + ::sw::mark::ICheckboxFieldmark* pFieldmark = + dynamic_cast< ::sw::mark::ICheckboxFieldmark* > + (rSh.GetCurrentFieldmark()); + OSL_ENSURE(pFieldmark, + "Where is my FieldMark??"); + if(pFieldmark) + { + pFieldmark->SetChecked(!pFieldmark->IsChecked()); + OSL_ENSURE(pFieldmark->IsExpanded(), + "where is the otherpos?"); + if (pFieldmark->IsExpanded()) + { + rSh.CalcLayout(); + } + } + eKeyState = SwKeyState::End; + } + else if ( !rSh.HasReadonlySel() + || rSh.CursorInsideInputField() ) + { + const bool bIsNormalChar = + GetAppCharClass().isLetterNumeric( OUString( aCh ), 0 ); + if( bAppendSpace && bIsNormalChar && + (!m_aInBuffer.isEmpty() || !rSh.IsSttPara() || !rSh.IsEndPara() )) + { + // insert a blank ahead of the character. this ends up + // between the expanded text and the new "non-word-separator". + m_aInBuffer += " "; + } + + const bool bIsAutoCorrectChar = SvxAutoCorrect::IsAutoCorrectChar( aCh ); + if( !aKeyEvent.GetRepeat() && pACorr && ( bIsAutoCorrectChar || rSh.IsNbspRunNext() ) && + pACfg->IsAutoFormatByInput() && + (( pACorr->IsAutoCorrFlag( ACFlags::ChgWeightUnderl ) && + ( '*' == aCh || '_' == aCh ) ) || + ( pACorr->IsAutoCorrFlag( ACFlags::ChgQuotes ) && ('\"' == aCh ))|| + ( pACorr->IsAutoCorrFlag( ACFlags::ChgSglQuotes ) && ( '\'' == aCh)))) + { + FlushInBuffer(); + rSh.AutoCorrect( *pACorr, aCh ); + if( '\"' != aCh && '\'' != aCh ) // only call when "*_"! + rSh.UpdateAttr(); + } + else if( !aKeyEvent.GetRepeat() && pACorr && ( bIsAutoCorrectChar || rSh.IsNbspRunNext() ) && + pACfg->IsAutoFormatByInput() && + pACorr->IsAutoCorrFlag( ACFlags::CapitalStartSentence | ACFlags::CapitalStartWord | + ACFlags::ChgOrdinalNumber | ACFlags::AddNonBrkSpace | + ACFlags::ChgToEnEmDash | ACFlags::SetINetAttr | + ACFlags::Autocorrect | ACFlags::TransliterateRTL | + ACFlags::SetDOIAttr ) && + '\"' != aCh && '\'' != aCh && '*' != aCh && '_' != aCh + ) + { + FlushInBuffer(); + rSh.AutoCorrect( *pACorr, aCh ); + } + else + { + OUStringBuffer aBuf(m_aInBuffer); + comphelper::string::padToLength(aBuf, + m_aInBuffer.getLength() + aKeyEvent.GetRepeat() + 1, aCh); + m_aInBuffer = aBuf.makeStringAndClear(); + bool delayFlush = Application::AnyInput( VclInputFlags::KEYBOARD ); + bFlushBuffer = !delayFlush; + if( delayFlush ) + { + // Start the timer, make sure to not restart it. + keyInputFlushTimerStop.dismiss(); + if( !m_aKeyInputFlushTimer.IsActive()) + m_aKeyInputFlushTimer.Start(); + } + } + eKeyState = SwKeyState::End; + } + else + { + rSh.InfoReadOnlyDialog(true); + eKeyState = SwKeyState::End; + } + + bool bIsSpace = (aCh == ' '); + if (bIsSpace && pACorr && pACfg) + { + // do the formatting only for few starting characters (for "* " or "- " conversion) + SwPosition aPos(*rSh.GetCursor()->GetPoint()); + if (aPos.nContent < 3) + { + SvxSwAutoFormatFlags& rFlags = pACorr->GetSwFlags(); + if(pACfg->IsAutoFormatByInput() && rFlags.bSetNumRule && rFlags.bSetNumRuleAfterSpace) + rSh.AutoFormat(&rFlags, true); + } + } + } + break; + + case SwKeyState::CheckAutoCorrect: + { + if( pACorr && pACfg->IsAutoFormatByInput() && + pACorr->IsAutoCorrFlag( ACFlags::CapitalStartSentence | ACFlags::CapitalStartWord | + ACFlags::ChgOrdinalNumber | ACFlags::TransliterateRTL | + ACFlags::ChgToEnEmDash | ACFlags::SetINetAttr | + ACFlags::Autocorrect | ACFlags::SetDOIAttr ) && + !rSh.HasReadonlySel() ) + { + FlushInBuffer(); + rSh.AutoCorrect( *pACorr, u'\0' ); + } + eKeyState = eNextKeyState; + } + break; + + default: + { + sal_uInt16 nSlotId = 0; + FlushInBuffer(); + switch( eKeyState ) + { + case SwKeyState::SpecialInsert: + rSh.DoSpecialInsert(); + break; + + case SwKeyState::NoNum: + rSh.NoNum(); + break; + + case SwKeyState::NumOff: + // shell change - so record in advance + rSh.DelNumRules(); + break; + case SwKeyState::OutlineLvOff: // delete autofmt outlinelevel later + break; + + case SwKeyState::NumDown: + rSh.NumUpDown(); + m_nKS_NUMDOWN_Count = 2; + break; + case SwKeyState::NumUp: + rSh.NumUpDown( false ); + break; + + case SwKeyState::NumIndentInc: + rSh.ChangeIndentOfAllListLevels(360); + m_nKS_NUMINDENTINC_Count = 2; + break; + + case SwKeyState::GotoNextFieldMark: + { + rSh.GotoFormControl(/*bNext=*/true); + } + break; + + case SwKeyState::GotoPrevFieldMark: + { + rSh.GotoFormControl(/*bNext=*/false); + } + break; + + case SwKeyState::NumIndentDec: + rSh.ChangeIndentOfAllListLevels(-360); + break; + + case SwKeyState::OutlineDown: + rSh.OutlineUpDown(); + break; + case SwKeyState::OutlineUp: + rSh.OutlineUpDown( -1 ); + break; + + case SwKeyState::NextCell: + // always 'flush' in tables + rSh.GoNextCell(!rSh.HasReadonlySel()); + nSlotId = FN_GOTO_NEXT_CELL; + break; + case SwKeyState::PrevCell: + rSh.GoPrevCell(); + nSlotId = FN_GOTO_PREV_CELL; + break; + case SwKeyState::AutoFormatByInput: + rSh.SplitNode( true ); + break; + + case SwKeyState::NextObject: + case SwKeyState::PrevObject: + if(rSh.GotoObj( SwKeyState::NextObject == eKeyState, GotoObjFlags::Any)) + { + if( rSh.IsFrameSelected() && + m_rView.GetDrawFuncPtr() ) + { + m_rView.GetDrawFuncPtr()->Deactivate(); + m_rView.SetDrawFuncPtr(nullptr); + m_rView.LeaveDrawCreate(); + m_rView.AttrChangedNotify(nullptr); + } + rSh.HideCursor(); + rSh.EnterSelFrameMode(); + } + break; + case SwKeyState::GlossaryExpand: + { + // replace the word or abbreviation with the auto text + rSh.StartUndo( SwUndoId::START ); + + OUString sFnd(aTmpQHD.CurStr()); + if( aTmpQHD.m_bIsAutoText ) + { + SwGlossaryList* pList = ::GetGlossaryList(); + OUString sShrtNm; + OUString sGroup; + if(pList->GetShortName( sFnd, sShrtNm, sGroup)) + { + rSh.SttSelect(); + rSh.ExtendSelection(false, aTmpQHD.CurLen()); + SwGlossaryHdl* pGlosHdl = GetView().GetGlosHdl(); + pGlosHdl->SetCurGroup(sGroup, true); + pGlosHdl->InsertGlossary( sShrtNm); + s_pQuickHlpData->m_bAppendSpace = true; + } + } + else + { + sFnd = sFnd.copy(aTmpQHD.CurLen()); + rSh.Insert( sFnd ); + s_pQuickHlpData->m_bAppendSpace = !pACorr || + pACorr->GetSwFlags().bAutoCmpltAppendBlank; + } + rSh.EndUndo( SwUndoId::END ); + } + break; + + case SwKeyState::NextPrevGlossary: + s_pQuickHlpData->Move( aTmpQHD ); + s_pQuickHlpData->Start(rSh, false); + break; + + case SwKeyState::EditFormula: + { + const sal_uInt16 nId = SwInputChild::GetChildWindowId(); + + SfxViewFrame& rVFrame = GetView().GetViewFrame(); + rVFrame.ToggleChildWindow( nId ); + SwInputChild* pChildWin = static_cast<SwInputChild*>(rVFrame. + GetChildWindow( nId )); + if( pChildWin ) + pChildWin->SetFormula( sFormulaEntry ); + } + break; + + case SwKeyState::ColLeftBig: rSh.SetColRowWidthHeight( TableChgWidthHeightType::ColLeft|TableChgWidthHeightType::BiggerMode, pModOpt->GetTableHMove() ); break; + case SwKeyState::ColRightBig: rSh.SetColRowWidthHeight( TableChgWidthHeightType::ColRight|TableChgWidthHeightType::BiggerMode, pModOpt->GetTableHMove() ); break; + case SwKeyState::ColLeftSmall: rSh.SetColRowWidthHeight( TableChgWidthHeightType::ColLeft, pModOpt->GetTableHMove() ); break; + case SwKeyState::ColRightSmall: rSh.SetColRowWidthHeight( TableChgWidthHeightType::ColRight, pModOpt->GetTableHMove() ); break; + case SwKeyState::ColBottomBig: rSh.SetColRowWidthHeight( TableChgWidthHeightType::RowBottom|TableChgWidthHeightType::BiggerMode, pModOpt->GetTableVMove() ); break; + case SwKeyState::ColBottomSmall: rSh.SetColRowWidthHeight( TableChgWidthHeightType::RowBottom, pModOpt->GetTableVMove() ); break; + case SwKeyState::CellLeftBig: rSh.SetColRowWidthHeight( TableChgWidthHeightType::CellLeft|TableChgWidthHeightType::BiggerMode, pModOpt->GetTableHMove() ); break; + case SwKeyState::CellRightBig: rSh.SetColRowWidthHeight( TableChgWidthHeightType::CellRight|TableChgWidthHeightType::BiggerMode, pModOpt->GetTableHMove() ); break; + case SwKeyState::CellLeftSmall: rSh.SetColRowWidthHeight( TableChgWidthHeightType::CellLeft, pModOpt->GetTableHMove() ); break; + case SwKeyState::CellRightSmall: rSh.SetColRowWidthHeight( TableChgWidthHeightType::CellRight, pModOpt->GetTableHMove() ); break; + case SwKeyState::CellTopBig: rSh.SetColRowWidthHeight( TableChgWidthHeightType::CellTop|TableChgWidthHeightType::BiggerMode, pModOpt->GetTableVMove() ); break; + case SwKeyState::CellBottomBig: rSh.SetColRowWidthHeight( TableChgWidthHeightType::CellBottom|TableChgWidthHeightType::BiggerMode, pModOpt->GetTableVMove() ); break; + case SwKeyState::CellTopSmall: rSh.SetColRowWidthHeight( TableChgWidthHeightType::CellTop, pModOpt->GetTableVMove() ); break; + case SwKeyState::CellBottomSmall: rSh.SetColRowWidthHeight( TableChgWidthHeightType::CellBottom, pModOpt->GetTableVMove() ); break; + + case SwKeyState::Fly_Change: + { + SdrView *pSdrView = rSh.GetDrawView(); + const SdrHdlList& rHdlList = pSdrView->GetHdlList(); + if(rHdlList.GetFocusHdl()) + ChangeDrawing( nDir ); + else + ChangeFly( nDir, dynamic_cast<const SwWebView*>( &m_rView) != nullptr ); + } + break; + case SwKeyState::Draw_Change : + ChangeDrawing( nDir ); + break; + default: + break; + } + if( nSlotId && m_rView.GetViewFrame().GetBindings().GetRecorder().is() ) + { + SfxRequest aReq(m_rView.GetViewFrame(), nSlotId); + aReq.Done(); + } + eKeyState = SwKeyState::End; + } + } + } + + // update the page number in the statusbar + nKey = rKEvt.GetKeyCode().GetCode(); + if( KEY_UP == nKey || KEY_DOWN == nKey || KEY_PAGEUP == nKey || KEY_PAGEDOWN == nKey ) + GetView().GetViewFrame().GetBindings().Update( FN_STAT_PAGE ); + + // in case the buffered characters are inserted + if( bFlushBuffer && !m_aInBuffer.isEmpty() ) + { + FlushInBuffer(); + + // maybe show Tip-Help + if (bNormalChar) + { + const bool bAutoTextShown + = pACfg && pACfg->IsAutoTextTip() && ShowAutoText(rSh.GetChunkForAutoText()); + if (!bAutoTextShown && pACorr && pACorr->GetSwFlags().bAutoCompleteWords) + ShowAutoCorrectQuickHelp(rSh.GetPrevAutoCorrWord(*pACorr), *pACorr); + } + } + + // get the word count dialog to update itself + SwWordCountWrapper *pWrdCnt = static_cast<SwWordCountWrapper*>(GetView().GetViewFrame().GetChildWindow(SwWordCountWrapper::GetChildWindowId())); + if( pWrdCnt ) + pWrdCnt->UpdateCounts(); + +} + +/** + * MouseEvents + */ +void SwEditWin::ResetMouseButtonDownFlags() +{ + // Not on all systems a MouseButtonUp is used ahead + // of the modal dialog (like on WINDOWS). + // So reset the statuses here and release the mouse + // for the dialog. + m_bMBPressed = false; + g_bNoInterrupt = false; + EnterArea(); + ReleaseMouse(); +} + +/** + * Determines if the current position has a clickable url over a background + * frame. In that case, ctrl-click should select the url, not the frame. + */ +static bool lcl_urlOverBackground(SwWrtShell& rSh, const Point& rDocPos) +{ + SwContentAtPos aSwContentAtPos(IsAttrAtPos::InetAttr); + SdrObject* pSelectableObj = rSh.GetObjAt(rDocPos); + + return rSh.GetContentAtPos(rDocPos, aSwContentAtPos) && pSelectableObj->GetLayer() == rSh.GetDoc()->getIDocumentDrawModelAccess().GetHellId(); +} + +void SwEditWin::MoveCursor( SwWrtShell &rSh, const Point& rDocPos, + const bool bOnlyText, bool bLockView ) +{ + const bool bTmpNoInterrupt = g_bNoInterrupt; + g_bNoInterrupt = false; + + int nTmpSetCursor = 0; + + if( !rSh.IsViewLocked() && bLockView ) + rSh.LockView( true ); + else + bLockView = false; + + { + // only temporary generate move context because otherwise + // the query to the content form doesn't work!!! + SwMvContext aMvContext( &rSh ); + nTmpSetCursor = rSh.CallSetCursor(&rDocPos, bOnlyText); + g_bValidCursorPos = !(CRSR_POSCHG & nTmpSetCursor); + } + + // notify the edit window that from now on we do not use the input language + if ( !(CRSR_POSOLD & nTmpSetCursor) ) + SetUseInputLanguage( false ); + + if( bLockView ) + rSh.LockView( false ); + + g_bNoInterrupt = bTmpNoInterrupt; +} + +void SwEditWin::MouseButtonDown(const MouseEvent& _rMEvt) +{ + SwWrtShell &rSh = m_rView.GetWrtShell(); + const SwField *pCursorField = rSh.CursorInsideInputField() ? rSh.GetCurField( true ) : nullptr; + + // We have to check if a context menu is shown and we have an UI + // active inplace client. In that case we have to ignore the mouse + // button down event. Otherwise we would crash (context menu has been + // opened by inplace client and we would deactivate the inplace client, + // the context menu is closed by VCL asynchronously which in the end + // would work on deleted objects or the context menu has no parent anymore) + SfxInPlaceClient* pIPClient = rSh.GetSfxViewShell()->GetIPClient(); + bool bIsOleActive = ( pIPClient && pIPClient->IsObjectInPlaceActive() ); + + if (bIsOleActive && vcl::IsInPopupMenuExecute()) + return; + + MouseEvent aMEvt(_rMEvt); + + if (m_rView.GetPostItMgr()->IsHit(aMEvt.GetPosPixel())) + return; + + if (comphelper::LibreOfficeKit::isActive()) + { + if (vcl::Window* pWindow = m_rView.GetPostItMgr()->IsHitSidebarWindow(aMEvt.GetPosPixel())) + { + pWindow->MouseButtonDown(aMEvt); + return; + } + } + + m_rView.GetPostItMgr()->SetActiveSidebarWin(nullptr); + + GrabFocus(); + rSh.addCurrentPosition(); + + //ignore key modifiers for format paintbrush + { + bool bExecFormatPaintbrush = m_pApplyTempl && m_pApplyTempl->m_pFormatClipboard + && m_pApplyTempl->m_pFormatClipboard->HasContent(); + if( bExecFormatPaintbrush ) + aMEvt = MouseEvent(_rMEvt.GetPosPixel(), _rMEvt.GetClicks(), _rMEvt.GetMode(), + _rMEvt.GetButtons()); + } + + m_bWasShdwCursor = nullptr != m_pShadCursor; + m_pShadCursor.reset(); + + const Point aDocPos(PixelToLogic(aMEvt.GetPosPixel())); + + FrameControlType eControl; + bool bOverFly = false; + bool bPageAnchored = false; + bool bOverHeaderFooterFly = IsOverHeaderFooterFly( aDocPos, eControl, bOverFly, bPageAnchored ); + + bool bIsDocReadOnly = m_rView.GetDocShell()->IsReadOnly(); + if (bOverHeaderFooterFly && (!bIsDocReadOnly && rSh.GetCurField())) + // We have a field here, that should have priority over header/footer fly. + bOverHeaderFooterFly = false; + + // Are we clicking on a blank header/footer area? + if ( IsInHeaderFooter( aDocPos, eControl ) || bOverHeaderFooterFly ) + { + const SwPageFrame* pPageFrame = rSh.GetLayout()->GetPageAtPos( aDocPos ); + + if ( pPageFrame ) + { + // Is it active? + bool bActive = true; + const SwPageDesc* pDesc = pPageFrame->GetPageDesc(); + + const SwFrameFormat* pFormat = pDesc->GetLeftFormat(); + if ( pPageFrame->OnRightPage() ) + pFormat = pDesc->GetRightFormat(); + + if ( pFormat ) + { + if ( eControl == FrameControlType::Header ) + bActive = pFormat->GetHeader().IsActive(); + else + bActive = pFormat->GetFooter().IsActive(); + } + + if ( !bActive ) + { + // HeaderFooter menu implies header/footer controls, so only do this with IsUseHeaderFooterMenu enabled. + // But, additionally, when in Hide-Whitespace mode, we don't want those controls. + if (rSh.GetViewOptions()->IsUseHeaderFooterMenu() && !rSh.GetViewOptions()->IsHideWhitespaceMode()) + { + SwPaM aPam(*rSh.GetCurrentShellCursor().GetPoint()); + const bool bWasInHeader = aPam.GetPoint()->GetNode().FindHeaderStartNode() != nullptr; + const bool bWasInFooter = aPam.GetPoint()->GetNode().FindFooterStartNode() != nullptr; + + // Is the cursor in a part like similar to the one we clicked on? For example, + // if the cursor is in a header and we click on an empty header... don't change anything to + // keep consistent behaviour due to header edit mode (and the same for the footer as well). + + // Otherwise, we hide the header/footer control if a separator is shown, and vice versa. + if (!(bWasInHeader && eControl == FrameControlType::Header) && + !(bWasInFooter && eControl == FrameControlType::Footer)) + { + const bool bSeparatorWasVisible = rSh.IsShowHeaderFooterSeparator(eControl); + rSh.SetShowHeaderFooterSeparator(eControl, !bSeparatorWasVisible); + + // Repaint everything + Invalidate(); + + // tdf#84929. If the footer control had not been showing, do not change the cursor position, + // because the user may have scrolled to turn on the separator control and + // if the cursor cannot be positioned on-screen, then the user would need to scroll back again to use the control. + // This should only be done for the footer. The cursor can always be re-positioned near the header. tdf#134023. + if ( eControl == FrameControlType::Footer && !bSeparatorWasVisible + && !Application::IsHeadlessModeEnabled() ) + return; + } + } + } + else + { + // Make sure we have the proper Header/Footer separators shown + // as these may be changed if clicking on an empty Header/Footer + rSh.SetShowHeaderFooterSeparator( FrameControlType::Header, eControl == FrameControlType::Header ); + rSh.SetShowHeaderFooterSeparator( FrameControlType::Footer, eControl == FrameControlType::Footer ); + + if ( !rSh.IsHeaderFooterEdit() ) + rSh.ToggleHeaderFooterEdit(); + } + } + } + else + { + if ( rSh.IsHeaderFooterEdit( ) ) + rSh.ToggleHeaderFooterEdit( ); + else + { + // Make sure that the separators are hidden + rSh.SetShowHeaderFooterSeparator( FrameControlType::Header, false ); + rSh.SetShowHeaderFooterSeparator( FrameControlType::Footer, false ); + } + + // Toggle Hide-Whitespace if between pages. + if (rSh.GetViewOptions()->CanHideWhitespace() && + rSh.GetLayout()->IsBetweenPages(aDocPos)) + { + if (_rMEvt.GetClicks() >= 2) + { + SwViewOption aOpt(*rSh.GetViewOptions()); + aOpt.SetHideWhitespaceMode(!aOpt.IsHideWhitespaceMode()); + rSh.ApplyViewOptions(aOpt); + } + + return; + } + } + + if ( IsChainMode() ) + { + SetChainMode( false ); + SwRect aDummy; + SwFlyFrameFormat *pFormat = static_cast<SwFlyFrameFormat*>(rSh.GetFlyFrameFormat()); + if ( rSh.Chainable( aDummy, *pFormat, aDocPos ) == SwChainRet::OK ) + rSh.Chain( *pFormat, aDocPos ); + UpdatePointer(aDocPos, aMEvt.GetModifier()); + return; + } + + // After GrabFocus a shell should be pushed. That should actually + // work but in practice ... + m_rView.SelectShellForDrop(); + + bool bCallBase = true; + + if( s_pQuickHlpData->m_bIsDisplayed ) + s_pQuickHlpData->Stop( rSh ); + s_pQuickHlpData->m_bAppendSpace = false; + + if( rSh.FinishOLEObj() ) + return; // end InPlace and the click doesn't count anymore + + CurrShell aCurr( &rSh ); + + SdrView *pSdrView = rSh.GetDrawView(); + if ( pSdrView ) + { + if (pSdrView->MouseButtonDown(aMEvt, GetOutDev())) + { + rSh.GetView().GetViewFrame().GetBindings().InvalidateAll(false); + return; // SdrView's event evaluated + } + } + + m_bIsInMove = false; + m_aStartPos = aMEvt.GetPosPixel(); + m_aRszMvHdlPt.setX( 0 ); + m_aRszMvHdlPt.setY( 0 ); + + SwTab nMouseTabCol = SwTab::COL_NONE; + const bool bTmp = !rSh.IsDrawCreate() && !m_pApplyTempl && !rSh.IsInSelect() + && aMEvt.GetClicks() == 1 && MOUSE_LEFT == aMEvt.GetButtons(); + if ( bTmp && + SwTab::COL_NONE != (nMouseTabCol = rSh.WhichMouseTabCol( aDocPos ) ) && + !rSh.IsObjSelectable( aDocPos ) ) + { + // Enhanced table selection + if ( SwTab::SEL_HORI <= nMouseTabCol && SwTab::COLSEL_VERT >= nMouseTabCol ) + { + rSh.EnterStdMode(); + rSh.SelectTableRowCol( aDocPos ); + if( SwTab::SEL_HORI != nMouseTabCol && SwTab::SEL_HORI_RTL != nMouseTabCol) + { + m_xRowColumnSelectionStart = aDocPos; + m_bIsRowDrag = SwTab::ROWSEL_HORI == nMouseTabCol|| + SwTab::ROWSEL_HORI_RTL == nMouseTabCol || + SwTab::COLSEL_VERT == nMouseTabCol; + m_bMBPressed = true; + CaptureMouse(); + } + return; + } + + if ( !rSh.IsTableMode() ) + { + // comes from table columns out of the document. + if(SwTab::COL_VERT == nMouseTabCol || SwTab::COL_HORI == nMouseTabCol) + m_rView.SetTabColFromDoc( true ); + else + m_rView.SetTabRowFromDoc( true ); + + m_rView.SetTabColFromDocPos( aDocPos ); + m_rView.InvalidateRulerPos(); + SfxBindings& rBind = m_rView.GetViewFrame().GetBindings(); + rBind.Update(); + if (RulerColumnDrag( + aMEvt, (SwTab::COL_VERT == nMouseTabCol || SwTab::ROW_HORI == nMouseTabCol))) + { + m_rView.SetTabColFromDoc( false ); + m_rView.SetTabRowFromDoc( false ); + m_rView.InvalidateRulerPos(); + rBind.Update(); + bCallBase = false; + } + else + { + return; + } + } + } + else if (bTmp && + rSh.IsNumLabel(aDocPos)) + { + SwTextNode* pNodeAtPos = rSh.GetNumRuleNodeAtPos( aDocPos ); + m_rView.SetNumRuleNodeFromDoc( pNodeAtPos ); + m_rView.InvalidateRulerPos(); + SfxBindings& rBind = m_rView.GetViewFrame().GetBindings(); + rBind.Update(); + + if (RulerMarginDrag(aMEvt, SwFEShell::IsVerticalModeAtNdAndPos(*pNodeAtPos, aDocPos))) + { + m_rView.SetNumRuleNodeFromDoc( nullptr ); + m_rView.InvalidateRulerPos(); + rBind.Update(); + bCallBase = false; + } + else + { + // Make sure the pointer is set to 0, otherwise it may point to + // nowhere after deleting the corresponding text node. + m_rView.SetNumRuleNodeFromDoc( nullptr ); + return; + } + } + + if ( rSh.IsInSelect() ) + rSh.EndSelect(); + + // query against LEFT because otherwise for example also a right + // click releases the selection. + if (MOUSE_LEFT == aMEvt.GetButtons()) + { + bool bOnlyText = false; + m_bMBPressed = true; + g_bNoInterrupt = true; + m_nKS_NUMDOWN_Count = 0; + + CaptureMouse(); + + // reset cursor position if applicable + rSh.ResetCursorStack(); + + switch (aMEvt.GetModifier() + aMEvt.GetButtons()) + { + case MOUSE_LEFT: + case MOUSE_LEFT + KEY_SHIFT: + case MOUSE_LEFT + KEY_MOD2: + if( rSh.IsObjSelected() ) + { + SdrHdl* pHdl; + if( !bIsDocReadOnly && + !m_pAnchorMarker && + pSdrView && + nullptr != ( pHdl = pSdrView->PickHandle(aDocPos) ) && + ( pHdl->GetKind() == SdrHdlKind::Anchor || + pHdl->GetKind() == SdrHdlKind::Anchor_TR ) ) + { + // #i121463# Set selected during drag + pHdl->SetSelected(); + m_pAnchorMarker.reset( new SwAnchorMarker( pHdl ) ); + UpdatePointer(aDocPos, aMEvt.GetModifier()); + return; + } + } + if (EnterDrawMode(aMEvt, aDocPos)) + { + g_bNoInterrupt = false; + return; + } + else if ( m_rView.GetDrawFuncPtr() && m_bInsFrame ) + { + StopInsFrame(); + rSh.Edit(); + } + + // Without SHIFT because otherwise Toggle doesn't work at selection + if (aMEvt.GetClicks() == 1) + { + if ( rSh.IsSelFrameMode()) + { + SdrHdl* pHdl = rSh.GetDrawView()->PickHandle(aDocPos); + bool bHitHandle = pHdl && pHdl->GetKind() != SdrHdlKind::Anchor && + pHdl->GetKind() != SdrHdlKind::Anchor_TR; + + if ((rSh.IsInsideSelectedObj(aDocPos) || bHitHandle) + && (aMEvt.GetModifier() != KEY_SHIFT || bHitHandle)) + { + rSh.EnterSelFrameMode( &aDocPos ); + if ( !m_pApplyTempl ) + { + // only if no position to size was hit. + if (!bHitHandle) + { + StartDDTimer(); + SwEditWin::s_nDDStartPosY = aDocPos.Y(); + SwEditWin::s_nDDStartPosX = aDocPos.X(); + } + g_bFrameDrag = true; + } + g_bNoInterrupt = false; + return; + } + } + } + } + + bool bExecHyperlinks = m_rView.GetDocShell()->IsReadOnly(); + if ( !bExecHyperlinks ) + { + const bool bSecureOption = SvtSecurityOptions::IsOptionSet( SvtSecurityOptions::EOption::CtrlClickHyperlink ); + if ((bSecureOption && aMEvt.GetModifier() == KEY_MOD1) + || (!bSecureOption && aMEvt.GetModifier() != KEY_MOD1)) + bExecHyperlinks = true; + } + + // Enhanced selection + sal_uInt8 nNumberOfClicks = static_cast<sal_uInt8>(aMEvt.GetClicks() % 4); + if (0 == nNumberOfClicks && 0 < aMEvt.GetClicks()) + nNumberOfClicks = 4; + + bool bExecDrawTextLink = false; + + switch (aMEvt.GetModifier() + aMEvt.GetButtons()) + { + case MOUSE_LEFT: + case MOUSE_LEFT + KEY_MOD1: + case MOUSE_LEFT + KEY_MOD2: + { + + // fdo#79604: first, check if a link has been clicked - do not + // select fly in this case! + if (1 == nNumberOfClicks) + { + UpdatePointer(aDocPos, aMEvt.GetModifier()); + SwEditWin::s_nDDStartPosY = aDocPos.Y(); + SwEditWin::s_nDDStartPosX = aDocPos.X(); + + // hit a URL in DrawText object? + if (bExecHyperlinks && pSdrView) + { + SdrViewEvent aVEvt; + pSdrView->PickAnything(aMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + + if (aVEvt.meEvent == SdrEventKind::ExecuteUrl) + bExecDrawTextLink = true; + } + } + + if (1 == nNumberOfClicks && !bExecDrawTextLink) + { + // only try to select frame, if pointer already was + // switched accordingly + if ( m_aActHitType != SdrHitKind::NONE && !rSh.IsSelFrameMode() && + !GetView().GetViewFrame().GetDispatcher()->IsLocked()) + { + // Test if there is a draw object at that position and if it should be selected. + bool bShould = rSh.ShouldObjectBeSelected(aDocPos); + + if(bShould) + { + m_rView.NoRotate(); + rSh.HideCursor(); + + bool bUnLockView = !rSh.IsViewLocked(); + rSh.LockView( true ); + bool bSelObj + = rSh.SelectObj(aDocPos, aMEvt.IsMod1() ? SW_ENTER_GROUP : 0); + if( bUnLockView ) + rSh.LockView( false ); + + if( bSelObj ) + { + // if the frame was deselected in the macro + // the cursor just has to be displayed again + if( FrameTypeFlags::NONE == rSh.GetSelFrameType() ) + rSh.ShowCursor(); + else + { + if (rSh.IsFrameSelected() && m_rView.GetDrawFuncPtr()) + { + m_rView.GetDrawFuncPtr()->Deactivate(); + m_rView.SetDrawFuncPtr(nullptr); + m_rView.LeaveDrawCreate(); + m_rView.AttrChangedNotify(nullptr); + } + + rSh.EnterSelFrameMode( &aDocPos ); + g_bFrameDrag = true; + UpdatePointer(aDocPos, aMEvt.GetModifier()); + } + return; + } + else + bOnlyText = rSh.IsObjSelectable( aDocPos ); + + if (!m_rView.GetDrawFuncPtr()) + rSh.ShowCursor(); + } + else + bOnlyText = KEY_MOD1 != aMEvt.GetModifier(); + } + else if ( rSh.IsSelFrameMode() && + (m_aActHitType == SdrHitKind::NONE || + !rSh.IsInsideSelectedObj( aDocPos ))) + { + m_rView.NoRotate(); + SdrHdl *pHdl; + if( !bIsDocReadOnly && !m_pAnchorMarker && nullptr != + ( pHdl = pSdrView->PickHandle(aDocPos) ) && + ( pHdl->GetKind() == SdrHdlKind::Anchor || + pHdl->GetKind() == SdrHdlKind::Anchor_TR ) ) + { + m_pAnchorMarker.reset( new SwAnchorMarker( pHdl ) ); + UpdatePointer(aDocPos, aMEvt.GetModifier()); + return; + } + else + { + bool bUnLockView = !rSh.IsViewLocked(); + rSh.LockView( true ); + sal_uInt8 nFlag = aMEvt.IsShift() ? SW_ADD_SELECT : 0; + if (aMEvt.IsMod1()) + nFlag = nFlag | SW_ENTER_GROUP; + + if ( rSh.IsSelFrameMode() ) + { + rSh.UnSelectFrame(); + rSh.LeaveSelFrameMode(); + m_rView.AttrChangedNotify(nullptr); + } + + bool bSelObj = rSh.SelectObj( aDocPos, nFlag ); + if( bUnLockView ) + rSh.LockView( false ); + + if( !bSelObj ) + { + // move cursor here so that it is not drawn in the + // frame first; ShowCursor() happens in LeaveSelFrameMode() + g_bValidCursorPos = !(CRSR_POSCHG & rSh.CallSetCursor(&aDocPos, false)); + rSh.LeaveSelFrameMode(); + m_rView.AttrChangedNotify(nullptr); + bCallBase = false; + } + else + { + rSh.HideCursor(); + rSh.EnterSelFrameMode( &aDocPos ); + rSh.SelFlyGrabCursor(); + rSh.MakeSelVisible(); + g_bFrameDrag = true; + if( rSh.IsFrameSelected() && + m_rView.GetDrawFuncPtr() ) + { + m_rView.GetDrawFuncPtr()->Deactivate(); + m_rView.SetDrawFuncPtr(nullptr); + m_rView.LeaveDrawCreate(); + m_rView.AttrChangedNotify(nullptr); + } + UpdatePointer(aDocPos, aMEvt.GetModifier()); + return; + } + } + } + } + + switch ( nNumberOfClicks ) + { + case 1: + break; + case 2: + { + g_bFrameDrag = false; + if (!bIsDocReadOnly && rSh.IsInsideSelectedObj(aDocPos) + && (FlyProtectFlags::NONE + == rSh.IsSelObjProtected(FlyProtectFlags::Content + | FlyProtectFlags::Parent) + || rSh.GetSelectionType() == SelectionType::Ole)) + { + /* This is no good: on the one hand GetSelectionType is used as flag field + * (take a look into the GetSelectionType method) and on the other hand the + * return value is used in a switch without proper masking (very nice), this must lead to trouble + */ + switch ( rSh.GetSelectionType() & ~SelectionType( SelectionType::FontWork | SelectionType::ExtrudedCustomShape ) ) + { + case SelectionType::Graphic: + ResetMouseButtonDownFlags(); + if (!comphelper::LibreOfficeKit::isActive()) + { + GetView().GetViewFrame().GetBindings().Execute( + FN_FORMAT_GRAFIC_DLG, nullptr, + SfxCallMode::RECORD|SfxCallMode::SLOT); + } + return; + + // double click on OLE object --> OLE-InPlace + case SelectionType::Ole: + ResetMouseButtonDownFlags(); + rSh.LaunchOLEObj(); + return; + + case SelectionType::Frame: + ResetMouseButtonDownFlags(); + if (!comphelper::LibreOfficeKit::isActive()) + { + GetView().GetViewFrame().GetBindings().Execute( + FN_FORMAT_FRAME_DLG, nullptr, + SfxCallMode::RECORD|SfxCallMode::SLOT); + } + return; + + case SelectionType::DrawObject: + ResetMouseButtonDownFlags(); + EnterDrawTextMode(aDocPos); + if ( auto pSwDrawTextShell = dynamic_cast< SwDrawTextShell *>( m_rView.GetCurShell() ) ) + pSwDrawTextShell->Init(); + return; + + default: break; + } + } + + // if the cursor position was corrected or if a Fly + // was selected in ReadOnlyMode, no word selection, except when tiled rendering. + if ((!g_bValidCursorPos || rSh.IsFrameSelected()) && !comphelper::LibreOfficeKit::isActive()) + return; + + SwField *pField; + bool bFootnote = false; + + if( !bIsDocReadOnly && + (nullptr != (pField = rSh.GetCurField(true)) || + ( bFootnote = rSh.GetCurFootnote() ) ) ) + { + ResetMouseButtonDownFlags(); + if( bFootnote ) + GetView().GetViewFrame().GetBindings().Execute( FN_EDIT_FOOTNOTE ); + else + { + SwFieldTypesEnum nTypeId = pField->GetTypeId(); + SfxViewFrame& rVFrame = GetView().GetViewFrame(); + switch( nTypeId ) + { + case SwFieldTypesEnum::Postit: + case SwFieldTypesEnum::Script: + { + // if it's a Readonly region, status has to be enabled + sal_uInt16 nSlot = SwFieldTypesEnum::Postit == nTypeId ? FN_POSTIT : FN_JAVAEDIT; + SfxBoolItem aItem(nSlot, true); + rVFrame.GetBindings().SetState(aItem); + rVFrame.GetBindings().Execute(nSlot); + break; + } + case SwFieldTypesEnum::Authority : + rVFrame.GetBindings().Execute(FN_EDIT_AUTH_ENTRY_DLG); + break; + case SwFieldTypesEnum::Input: + case SwFieldTypesEnum::Dropdown: + case SwFieldTypesEnum::SetInput: + rVFrame.GetBindings().Execute(FN_UPDATE_INPUTFIELDS); + break; + default: + rVFrame.GetBindings().Execute(FN_EDIT_FIELD); + } + } + return; + } + // in extended mode double and triple + // click has no effect. + if ( rSh.IsExtMode() || rSh.IsBlockMode() ) + return; + + // select word, AdditionalMode if applicable + if (KEY_MOD1 == aMEvt.GetModifier() && !rSh.IsAddMode()) + { + rSh.EnterAddMode(); + rSh.SelWrd( &aDocPos ); + rSh.LeaveAddMode(); + } + else + { + if (!rSh.SelWrd(&aDocPos) && comphelper::LibreOfficeKit::isActive()) + // Double click did not select any word: try to + // select the current cell in case we are in a + // table. + rSh.SelTableBox(); + } + + SwContentAtPos aContentAtPos(IsAttrAtPos::FormControl); + if( rSh.GetContentAtPos( aDocPos, aContentAtPos ) && + aContentAtPos.aFnd.pFieldmark != nullptr) + { + IFieldmark *pFieldBM = const_cast< IFieldmark* > ( aContentAtPos.aFnd.pFieldmark ); + if ( pFieldBM->GetFieldname( ) == ODF_FORMDROPDOWN || pFieldBM->GetFieldname( ) == ODF_FORMDATE ) + { + ResetMouseButtonDownFlags(); + rSh.getIDocumentMarkAccess()->ClearFieldActivation(); + GetView().GetViewFrame().GetBindings().Execute(SID_FM_CTL_PROPERTIES); + return; + } + } + + // tdf#143158 - handle alphabetical index entries + SwContentAtPos aToxContentAtPos(IsAttrAtPos::ToxMark); + if (rSh.GetContentAtPos(aDocPos, aToxContentAtPos)) + { + const OUString sToxText = aToxContentAtPos.sStr; + if (!sToxText.isEmpty() && aToxContentAtPos.pFndTextAttr) + { + const SwTOXType* pTType + = aToxContentAtPos.pFndTextAttr->GetTOXMark().GetTOXType(); + if (pTType && pTType->GetType() == TOXTypes::TOX_INDEX) + { + ResetMouseButtonDownFlags(); + GetView().GetViewFrame().GetBindings().Execute( + FN_EDIT_IDX_ENTRY_DLG); + return; + } + } + } + + g_bHoldSelection = true; + return; + } + case 3: + case 4: + { + g_bFrameDrag = false; + // in extended mode double and triple + // click has no effect. + if ( rSh.IsExtMode() ) + return; + + // if the cursor position was corrected or if a Fly + // was selected in ReadOnlyMode, no word selection. + if ( !g_bValidCursorPos || rSh.IsFrameSelected() ) + return; + + // select line, AdditionalMode if applicable + const bool bMod = KEY_MOD1 == aMEvt.GetModifier() && !rSh.IsAddMode(); + + if ( bMod ) + rSh.EnterAddMode(); + + // Enhanced selection + if ( 3 == nNumberOfClicks ) + rSh.SelSentence( &aDocPos ); + else + rSh.SelPara( &aDocPos ); + + if ( bMod ) + rSh.LeaveAddMode(); + + g_bHoldSelection = true; + return; + } + + default: + return; + } + + [[fallthrough]]; + } + case MOUSE_LEFT + KEY_SHIFT: + case MOUSE_LEFT + KEY_SHIFT + KEY_MOD1: + { + bool bLockView = m_bWasShdwCursor; + + switch (aMEvt.GetModifier()) + { + case KEY_MOD1 + KEY_SHIFT: + { + if ( !m_bInsDraw && IsDrawObjSelectable( rSh, aDocPos ) ) + { + m_rView.NoRotate(); + rSh.HideCursor(); + if ( rSh.IsSelFrameMode() ) + rSh.SelectObj(aDocPos, SW_ADD_SELECT | SW_ENTER_GROUP); + else + { if ( rSh.SelectObj( aDocPos, SW_ADD_SELECT | SW_ENTER_GROUP ) ) + { + rSh.EnterSelFrameMode( &aDocPos ); + SwEditWin::s_nDDStartPosY = aDocPos.Y(); + SwEditWin::s_nDDStartPosX = aDocPos.X(); + g_bFrameDrag = true; + return; + } + } + } + else if( rSh.IsSelFrameMode() && + rSh.GetDrawView()->PickHandle( aDocPos )) + { + g_bFrameDrag = true; + g_bNoInterrupt = false; + return; + } + } + break; + case KEY_MOD1: + if ( !bExecDrawTextLink ) + { + if (rSh.GetViewOptions()->IsShowOutlineContentVisibilityButton()) + { + // ctrl+left-click on outline node frame + SwContentAtPos aContentAtPos(IsAttrAtPos::Outline); + if(rSh.GetContentAtPos(aDocPos, aContentAtPos)) + { + SwOutlineNodes::size_type nPos; + if (rSh.GetNodes().GetOutLineNds().Seek_Entry(aContentAtPos.aFnd.pNode, &nPos)) + { + ToggleOutlineContentVisibility(nPos, false); + return; + } + } + } + if ( !m_bInsDraw && IsDrawObjSelectable( rSh, aDocPos ) && !lcl_urlOverBackground( rSh, aDocPos ) ) + { + m_rView.NoRotate(); + rSh.HideCursor(); + if ( rSh.IsSelFrameMode() ) + rSh.SelectObj(aDocPos, SW_ENTER_GROUP); + else + { if ( rSh.SelectObj( aDocPos, SW_ENTER_GROUP ) ) + { + rSh.EnterSelFrameMode( &aDocPos ); + SwEditWin::s_nDDStartPosY = aDocPos.Y(); + SwEditWin::s_nDDStartPosX = aDocPos.X(); + g_bFrameDrag = true; + return; + } + } + } + else if( rSh.IsSelFrameMode() && + rSh.GetDrawView()->PickHandle( aDocPos )) + { + g_bFrameDrag = true; + g_bNoInterrupt = false; + return; + } + else + { + if ( !rSh.IsAddMode() && !rSh.IsExtMode() && !rSh.IsBlockMode() ) + { + rSh.PushMode(); + g_bModePushed = true; + + bool bUnLockView = !rSh.IsViewLocked(); + rSh.LockView( true ); + rSh.EnterAddMode(); + if( bUnLockView ) + rSh.LockView( false ); + } + bCallBase = false; + } + } + break; + case KEY_MOD2: + { + if ( !rSh.IsAddMode() && !rSh.IsExtMode() && !rSh.IsBlockMode() ) + { + rSh.PushMode(); + g_bModePushed = true; + bool bUnLockView = !rSh.IsViewLocked(); + rSh.LockView( true ); + rSh.EnterBlockMode(); + if( bUnLockView ) + rSh.LockView( false ); + } + bCallBase = false; + } + break; + case KEY_SHIFT: + { + if (nNumberOfClicks == 2) + { + // Left mouse button, shift, double-click: see if we have a graphic and + // dispatch its dialog in this case. + if (rSh.GetSelectionType() == SelectionType::Graphic) + { + GetView().GetViewFrame().GetBindings().Execute( + FN_FORMAT_GRAFIC_DLG, nullptr, + SfxCallMode::RECORD | SfxCallMode::SLOT); + return; + } + } + + if ( !m_bInsDraw && IsDrawObjSelectable( rSh, aDocPos ) ) + { + m_rView.NoRotate(); + rSh.HideCursor(); + if ( rSh.IsSelFrameMode() ) + { + rSh.SelectObj(aDocPos, SW_ADD_SELECT); + + const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList(); + if (rMarkList.GetMark(0) == nullptr) + { + rSh.LeaveSelFrameMode(); + m_rView.AttrChangedNotify(nullptr); + g_bFrameDrag = false; + } + } + else + { if ( rSh.SelectObj( aDocPos ) ) + { + rSh.EnterSelFrameMode( &aDocPos ); + SwEditWin::s_nDDStartPosY = aDocPos.Y(); + SwEditWin::s_nDDStartPosX = aDocPos.X(); + g_bFrameDrag = true; + return; + } + } + } + else + { + bool bShould = rSh.ShouldObjectBeSelected(aDocPos); + if (bShould) + { + // Left mouse button, shift, non-double-click, not a draw object and + // have an object to select: select it. + rSh.HideCursor(); + bool bSelObj = rSh.SelectObj(aDocPos); + if (bSelObj) + { + rSh.EnterSelFrameMode(&aDocPos); + } + return; + } + + if ( rSh.IsSelFrameMode() && + rSh.IsInsideSelectedObj( aDocPos ) ) + { + rSh.EnterSelFrameMode( &aDocPos ); + SwEditWin::s_nDDStartPosY = aDocPos.Y(); + SwEditWin::s_nDDStartPosX = aDocPos.X(); + g_bFrameDrag = true; + return; + } + if ( rSh.IsSelFrameMode() ) + { + rSh.UnSelectFrame(); + rSh.LeaveSelFrameMode(); + m_rView.AttrChangedNotify(nullptr); + g_bFrameDrag = false; + } + if ( !rSh.IsExtMode() ) + { + // don't start a selection when an + // URL field or a graphic is clicked + bool bSttSelect = rSh.HasSelection() || + PointerStyle::RefHand != GetPointer(); + + if( !bSttSelect ) + { + bSttSelect = true; + if( bExecHyperlinks ) + { + SwContentAtPos aContentAtPos( + IsAttrAtPos::Ftn | + IsAttrAtPos::InetAttr ); + + if( rSh.GetContentAtPos( aDocPos, aContentAtPos ) ) + { + if( !rSh.IsViewLocked() && + !rSh.IsReadOnlyAvailable() && + aContentAtPos.IsInProtectSect() ) + bLockView = true; + + bSttSelect = false; + } + else if( rSh.IsURLGrfAtPos( aDocPos )) + bSttSelect = false; + } + } + + if( bSttSelect ) + rSh.SttSelect(); + } + } + bCallBase = false; + break; + } + default: + if( !rSh.IsViewLocked() ) + { + SwContentAtPos aContentAtPos( IsAttrAtPos::ClickField | + IsAttrAtPos::InetAttr ); + if( rSh.GetContentAtPos( aDocPos, aContentAtPos ) && + !rSh.IsReadOnlyAvailable() && + aContentAtPos.IsInProtectSect() ) + bLockView = true; + } + } + + if ( rSh.IsGCAttr() ) + { + rSh.GCAttr(); + rSh.ClearGCAttr(); + } + + SwContentAtPos aFieldAtPos(IsAttrAtPos::Field); + bool bEditableFieldClicked = false; + + // Are we clicking on a field? + if (rSh.GetContentAtPos(aDocPos, aFieldAtPos)) + { + bool bEditableField = (aFieldAtPos.pFndTextAttr != nullptr + && aFieldAtPos.pFndTextAttr->Which() == RES_TXTATR_INPUTFIELD); + + if (!bEditableField) + { + rSh.CallSetCursor(&aDocPos, bOnlyText); + // Unfortunately the cursor may be on field + // position or on position after field depending on which + // half of the field was clicked on. + SwTextAttr const*const pTextField(aFieldAtPos.pFndTextAttr); + if (pTextField && + rSh.GetCurrentShellCursor().GetPoint()->GetContentIndex() != pTextField->GetStart()) + { + assert(rSh.GetCurrentShellCursor().GetPoint()->GetContentIndex() == (pTextField->GetStart() + 1)); + rSh.Left( SwCursorSkipMode::Chars, false, 1, false ); + } + // don't go into the !bOverSelect block below - it moves + // the cursor + break; + } + else + { + bEditableFieldClicked = true; + } + } + + bool bOverSelect = rSh.TestCurrPam( aDocPos ); + bool bOverURLGrf = false; + if( !bOverSelect ) + bOverURLGrf = bOverSelect = nullptr != rSh.IsURLGrfAtPos( aDocPos ); + + if ( !bOverSelect || rSh.IsInSelect() ) + { + MoveCursor( rSh, aDocPos, bOnlyText, bLockView ); + bCallBase = false; + } + if (!bOverURLGrf && !bExecDrawTextLink && !bOnlyText) + { + const SelectionType nSelType = rSh.GetSelectionType(); + // Check in general, if an object is selectable at given position. + // Thus, also text fly frames in background become selectable via Ctrl-Click. + if ( ( nSelType & SelectionType::Ole || + nSelType & SelectionType::Graphic || + rSh.IsObjSelectable( aDocPos ) ) && !lcl_urlOverBackground( rSh, aDocPos ) ) + { + SwMvContext aMvContext( &rSh ); + rSh.EnterSelFrameMode(); + bCallBase = false; + } + } + if ( !bOverSelect && bEditableFieldClicked && (!pCursorField || + pCursorField != aFieldAtPos.pFndTextAttr->GetFormatField().GetField())) + { + // select content of Input Field, but exclude CH_TXT_ATR_INPUTFIELDSTART + // and CH_TXT_ATR_INPUTFIELDEND + rSh.SttSelect(); + rSh.SelectTextModel( aFieldAtPos.pFndTextAttr->GetStart() + 1, + *(aFieldAtPos.pFndTextAttr->End()) - 1 ); + } + // don't reset here any longer so that, in case through MouseMove + // with pressed Ctrl key a multiple-selection should happen, + // the previous selection is not released in Drag. + break; + } + } + } + else if (MOUSE_RIGHT == aMEvt.GetButtons()) + { + if (rSh.GetViewOptions()->IsShowOutlineContentVisibilityButton() + && aMEvt.GetModifier() == KEY_MOD1) + { + // ctrl+right-click on outline node frame + SwContentAtPos aContentAtPos(IsAttrAtPos::Outline); + if(rSh.GetContentAtPos(aDocPos, aContentAtPos)) + { + SwOutlineNodes::size_type nPos; + if (rSh.GetNodes().GetOutLineNds().Seek_Entry(aContentAtPos.aFnd.pNode, &nPos)) + { + ToggleOutlineContentVisibility(nPos, !rSh.GetViewOptions()->IsTreatSubOutlineLevelsAsContent()); + return; + } + } + } + else if (!aMEvt.GetModifier() && static_cast<sal_uInt8>(aMEvt.GetClicks() % 4) == 1 + && !rSh.TestCurrPam(aDocPos)) + { + SwContentAtPos aFieldAtPos(IsAttrAtPos::Field); + + // Are we clicking on a field? + if (g_bValidCursorPos + && rSh.GetContentAtPos(aDocPos, aFieldAtPos) + && aFieldAtPos.pFndTextAttr != nullptr + && aFieldAtPos.pFndTextAttr->Which() == RES_TXTATR_INPUTFIELD + && (!pCursorField || pCursorField != aFieldAtPos.pFndTextAttr->GetFormatField().GetField())) + { + // Move the cursor + MoveCursor( rSh, aDocPos, rSh.IsObjSelectable( aDocPos ), m_bWasShdwCursor ); + bCallBase = false; + + // select content of Input Field, but exclude CH_TXT_ATR_INPUTFIELDSTART + // and CH_TXT_ATR_INPUTFIELDEND + rSh.SttSelect(); + rSh.SelectTextModel( aFieldAtPos.pFndTextAttr->GetStart() + 1, + *(aFieldAtPos.pFndTextAttr->End()) - 1 ); + } + } + } + + if (bCallBase) + Window::MouseButtonDown(aMEvt); +} + +bool SwEditWin::changeMousePointer(Point const & rDocPoint) +{ + SwWrtShell & rShell = m_rView.GetWrtShell(); + + SwTab nMouseTabCol; + if ( SwTab::COL_NONE != (nMouseTabCol = rShell.WhichMouseTabCol( rDocPoint ) ) && + !rShell.IsObjSelectable( rDocPoint ) ) + { + PointerStyle nPointer = PointerStyle::Null; + bool bChkTableSel = false; + + switch ( nMouseTabCol ) + { + case SwTab::COL_VERT : + case SwTab::ROW_HORI : + nPointer = PointerStyle::VSizeBar; + bChkTableSel = true; + break; + case SwTab::ROW_VERT : + case SwTab::COL_HORI : + nPointer = PointerStyle::HSizeBar; + bChkTableSel = true; + break; + // Enhanced table selection + case SwTab::SEL_HORI : + nPointer = PointerStyle::TabSelectSE; + break; + case SwTab::SEL_HORI_RTL : + case SwTab::SEL_VERT : + nPointer = PointerStyle::TabSelectSW; + break; + case SwTab::COLSEL_HORI : + case SwTab::ROWSEL_VERT : + nPointer = PointerStyle::TabSelectS; + break; + case SwTab::ROWSEL_HORI : + nPointer = PointerStyle::TabSelectE; + break; + case SwTab::ROWSEL_HORI_RTL : + case SwTab::COLSEL_VERT : + nPointer = PointerStyle::TabSelectW; + break; + default: break; // prevent compiler warning + } + + if ( PointerStyle::Null != nPointer && + // i#35543 - Enhanced table selection is explicitly allowed in table mode + ( !bChkTableSel || !rShell.IsTableMode() ) && + !comphelper::LibreOfficeKit::isActive() ) + { + SetPointer( nPointer ); + } + + return true; + } + else if (rShell.IsNumLabel(rDocPoint, RULER_MOUSE_MARGINWIDTH)) + { + // i#42921 - consider vertical mode + SwTextNode* pNodeAtPos = rShell.GetNumRuleNodeAtPos( rDocPoint ); + const PointerStyle nPointer = + SwFEShell::IsVerticalModeAtNdAndPos( *pNodeAtPos, rDocPoint ) + ? PointerStyle::VSizeBar + : PointerStyle::HSizeBar; + SetPointer( nPointer ); + + return true; + } + return false; +} + +void SwEditWin::MouseMove(const MouseEvent& _rMEvt) +{ + MouseEvent rMEvt(_rMEvt); + + if (comphelper::LibreOfficeKit::isActive()) + { + if (vcl::Window* pWindow = m_rView.GetPostItMgr()->IsHitSidebarWindow(rMEvt.GetPosPixel())) + { + pWindow->MouseMove(rMEvt); + return; + } + } + + //ignore key modifiers for format paintbrush + { + bool bExecFormatPaintbrush = m_pApplyTempl && m_pApplyTempl->m_pFormatClipboard + && m_pApplyTempl->m_pFormatClipboard->HasContent(); + if( bExecFormatPaintbrush ) + rMEvt = MouseEvent( _rMEvt.GetPosPixel(), _rMEvt.GetClicks(), + _rMEvt.GetMode(), _rMEvt.GetButtons() ); + } + + // as long as an action is running the MouseMove should be disconnected + // otherwise bug 40102 occurs + SwWrtShell &rSh = m_rView.GetWrtShell(); + if( rSh.ActionPend() ) + return ; + + if (rSh.GetViewOptions()->IsShowOutlineContentVisibilityButton()) + { + // add/remove outline content hide button + const SwNodes& rNds = rSh.GetDoc()->GetNodes(); + SwOutlineNodes::size_type nPos; + SwContentAtPos aSwContentAtPos(IsAttrAtPos::Outline); + if (rSh.GetContentAtPos(PixelToLogic(rMEvt.GetPosPixel()), aSwContentAtPos)) + { + // mouse pointer is on an outline paragraph node + if(aSwContentAtPos.aFnd.pNode && aSwContentAtPos.aFnd.pNode->IsTextNode()) + { + // Get the outline paragraph frame and compare it to the saved outline frame. If they + // are not the same, remove the fold button from the saved outline frame, if not + // already removed, and then add a fold button to the mouse over outline frame if + // the content is not folded. + SwContentFrame* pContentFrame = + aSwContentAtPos.aFnd.pNode->GetTextNode()->getLayoutFrame(rSh.GetLayout()); + if (pContentFrame != m_pSavedOutlineFrame) + { + if (m_pSavedOutlineFrame) + { + if (m_pSavedOutlineFrame->isFrameAreaDefinitionValid()) + { + SwTextNode* pTextNode = m_pSavedOutlineFrame->GetTextNodeFirst(); + if (pTextNode && rNds.GetOutLineNds().Seek_Entry(pTextNode, &nPos) && + rSh.GetAttrOutlineContentVisible(nPos)) + { + GetFrameControlsManager().RemoveControlsByType( + FrameControlType::Outline, m_pSavedOutlineFrame); + } + } + } + m_pSavedOutlineFrame = static_cast<SwTextFrame*>(pContentFrame); + } + // show fold button if outline content is visible + if (rNds.GetOutLineNds().Seek_Entry(aSwContentAtPos.aFnd.pNode->GetTextNode(), &nPos) && + rSh.GetAttrOutlineContentVisible(nPos)) + GetFrameControlsManager().SetOutlineContentVisibilityButton(pContentFrame); + } + } + else if (m_pSavedOutlineFrame) + { + // The saved frame may not still be in the document, e.g., when an outline paragraph + // is deleted. This causes the call to GetTextNodeFirst to behave badly. Use + // isFrameAreaDefinitionValid to check if the frame is still in the document. + if (m_pSavedOutlineFrame->isFrameAreaDefinitionValid()) + { + // current pointer pos is not over an outline frame + // previous pointer pos was over an outline frame + // remove outline content visibility button if showing + SwTextNode* pTextNode = m_pSavedOutlineFrame->GetTextNodeFirst(); + if (pTextNode && rNds.GetOutLineNds().Seek_Entry(pTextNode, &nPos) && + rSh.GetAttrOutlineContentVisible(nPos)) + { + GetFrameControlsManager().RemoveControlsByType( + FrameControlType::Outline, m_pSavedOutlineFrame); + } + } + m_pSavedOutlineFrame = nullptr; + } + } + + if( m_pShadCursor && 0 != (rMEvt.GetModifier() + rMEvt.GetButtons() ) ) + { + m_pShadCursor.reset(); + } + + bool bIsDocReadOnly = m_rView.GetDocShell()->IsReadOnly(); + + CurrShell aCurr( &rSh ); + + //aPixPt == Point in Pixel, relative to ChildWin + //aDocPt == Point in Twips, document coordinates + const Point aPixPt( rMEvt.GetPosPixel() ); + const Point aDocPt( PixelToLogic( aPixPt ) ); + + if ( IsChainMode() ) + { + UpdatePointer( aDocPt, rMEvt.GetModifier() ); + return; + } + + SdrView *pSdrView = rSh.GetDrawView(); + + const SwCallMouseEvent aLastCallEvent( m_aSaveCallEvent ); + m_aSaveCallEvent.Clear(); + + if ( !bIsDocReadOnly && pSdrView && pSdrView->MouseMove(rMEvt,GetOutDev()) ) + { + SetPointer( PointerStyle::Text ); + return; // evaluate SdrView's event + } + + const Point aOldPt( rSh.VisArea().Pos() ); + const bool bInsWin = rSh.VisArea().Contains( aDocPt ) || comphelper::LibreOfficeKit::isActive(); + + if (rSh.GetViewOptions()->IsShowOutlineContentVisibilityButton()) + { + if (m_pSavedOutlineFrame && !bInsWin) + { + // the mouse pointer has left the building (edit window) + // remove the outline content visibility button if showing + if (m_pSavedOutlineFrame->isFrameAreaDefinitionValid()) + { + const SwNodes& rNds = rSh.GetDoc()->GetNodes(); + SwOutlineNodes::size_type nPos; + SwTextNode* pTextNode = m_pSavedOutlineFrame->GetTextNodeFirst(); + if (pTextNode && rNds.GetOutLineNds().Seek_Entry(pTextNode, &nPos) && + rSh.GetAttrOutlineContentVisible(nPos)) + { + GetFrameControlsManager().RemoveControlsByType(FrameControlType::Outline, + m_pSavedOutlineFrame); + } + } + m_pSavedOutlineFrame = nullptr; + } + } + + if( m_pShadCursor && !bInsWin ) + { + m_pShadCursor.reset(); + } + + if( bInsWin && m_xRowColumnSelectionStart ) + { + EnterArea(); + Point aPos( aDocPt ); + if( rSh.SelectTableRowCol( *m_xRowColumnSelectionStart, &aPos, m_bIsRowDrag )) + return; + } + + // position is necessary for OS/2 because obviously after a MB-Down + // a MB-Move is called immediately. + if( g_bDDTimerStarted ) + { + Point aDD( SwEditWin::s_nDDStartPosX, SwEditWin::s_nDDStartPosY ); + aDD = LogicToPixel( aDD ); + tools::Rectangle aRect( aDD.X()-3, aDD.Y()-3, aDD.X()+3, aDD.Y()+3 ); + if ( !aRect.Contains( aPixPt ) ) + StopDDTimer( &rSh, aDocPt ); + } + + if(m_rView.GetDrawFuncPtr()) + { + if( m_bInsDraw ) + { + m_rView.GetDrawFuncPtr()->MouseMove( rMEvt ); + if ( !bInsWin ) + { + Point aTmp( aDocPt ); + aTmp += rSh.VisArea().Pos() - aOldPt; + LeaveArea( aTmp ); + } + else + EnterArea(); + return; + } + else if(!rSh.IsFrameSelected() && !rSh.IsObjSelected()) + { + SfxBindings &rBnd = rSh.GetView().GetViewFrame().GetBindings(); + Point aRelPos = rSh.GetRelativePagePosition(aDocPt); + if(aRelPos.X() >= 0) + { + FieldUnit eMetric = ::GetDfltMetric(dynamic_cast<SwWebView*>( &GetView()) != nullptr ); + SW_MOD()->PutItem(SfxUInt16Item(SID_ATTR_METRIC, static_cast< sal_uInt16 >(eMetric))); + const SfxPointItem aTmp1( SID_ATTR_POSITION, aRelPos ); + rBnd.SetState( aTmp1 ); + } + else + { + rBnd.Invalidate(SID_ATTR_POSITION); + } + rBnd.Invalidate(SID_ATTR_SIZE); + const SvxStatusItem aCell( SID_TABLE_CELL, OUString(), StatusCategory::NONE ); + rBnd.SetState( aCell ); + } + } + + // determine if we only change the mouse pointer and return + if (!bIsDocReadOnly && bInsWin && !m_pApplyTempl && !rSh.IsInSelect() && changeMousePointer(aDocPt)) + { + return; + } + + bool bDelShadCursor = true; + + switch ( rMEvt.GetModifier() + rMEvt.GetButtons() ) + { + case MOUSE_LEFT: + if( m_pAnchorMarker ) + { + // Now we need to refresh the SdrHdl pointer of m_pAnchorMarker. + // This looks a little bit tricky, but it solves the following + // problem: the m_pAnchorMarker contains a pointer to an SdrHdl, + // if the FindAnchorPos-call cause a scrolling of the visible + // area, it's possible that the SdrHdl will be destroyed and a + // new one will initialized at the original position(GetHdlPos). + // So the m_pAnchorMarker has to find the right SdrHdl, if it's + // the old one, it will find it with position aOld, if this one + // is destroyed, it will find a new one at position GetHdlPos(). + + const Point aOld = m_pAnchorMarker->GetPosForHitTest( *(rSh.GetOut()) ); + Point aNew = rSh.FindAnchorPos( aDocPt ); + SdrHdl* pHdl; + if( pSdrView && (nullptr!=( pHdl = pSdrView->PickHandle( aOld ) )|| + nullptr !=(pHdl = pSdrView->PickHandle( m_pAnchorMarker->GetHdlPos()) ) ) && + ( pHdl->GetKind() == SdrHdlKind::Anchor || + pHdl->GetKind() == SdrHdlKind::Anchor_TR ) ) + { + m_pAnchorMarker->ChgHdl( pHdl ); + if( aNew.X() || aNew.Y() ) + { + m_pAnchorMarker->SetPos( aNew ); + m_pAnchorMarker->SetLastPos( aDocPt ); + } + } + else + { + m_pAnchorMarker.reset(); + } + } + if ( m_bInsDraw ) + { + if ( !m_bMBPressed ) + break; + if ( m_bIsInMove || IsMinMove( m_aStartPos, aPixPt ) ) + { + if ( !bInsWin ) + LeaveArea( aDocPt ); + else + EnterArea(); + if ( m_rView.GetDrawFuncPtr() ) + { + pSdrView->SetOrtho(false); + m_rView.GetDrawFuncPtr()->MouseMove( rMEvt ); + } + m_bIsInMove = true; + } + return; + } + + { + SwWordCountWrapper *pWrdCnt = static_cast<SwWordCountWrapper*>(GetView().GetViewFrame().GetChildWindow(SwWordCountWrapper::GetChildWindowId())); + if (pWrdCnt) + pWrdCnt->UpdateCounts(); + } + [[fallthrough]]; + + case MOUSE_LEFT + KEY_SHIFT: + case MOUSE_LEFT + KEY_SHIFT + KEY_MOD1: + if ( !m_bMBPressed ) + break; + [[fallthrough]]; + case MOUSE_LEFT + KEY_MOD1: + if ( g_bFrameDrag && rSh.IsSelFrameMode() ) + { + if( !m_bMBPressed ) + break; + + if ( m_bIsInMove || IsMinMove( m_aStartPos, aPixPt ) ) + { + // event processing for resizing + if (pSdrView && pSdrView->AreObjectsMarked()) + { + const Point aSttPt( PixelToLogic( m_aStartPos ) ); + + // can we start? + if( SdrHdlKind::User == g_eSdrMoveHdl ) + { + SdrHdl* pHdl = pSdrView->PickHandle( aSttPt ); + g_eSdrMoveHdl = pHdl ? pHdl->GetKind() : SdrHdlKind::Move; + } + + const SwFrameFormat *const pFlyFormat(rSh.GetFlyFrameFormat()); + const SvxMacro* pMacro = nullptr; + + SvMacroItemId nEvent = SdrHdlKind::Move == g_eSdrMoveHdl + ? SvMacroItemId::SwFrmMove + : SvMacroItemId::SwFrmResize; + + if (nullptr != pFlyFormat) + pMacro = pFlyFormat->GetMacro().GetMacroTable().Get(nEvent); + if (nullptr != pMacro && + // or notify only e.g. every 20 Twip? + m_aRszMvHdlPt != aDocPt ) + { + m_aRszMvHdlPt = aDocPt; + sal_uInt32 nPos = 0; + SbxArrayRef xArgs = new SbxArray; + SbxVariableRef xVar = new SbxVariable; + xVar->PutString( pFlyFormat->GetName() ); + xArgs->Put(xVar.get(), ++nPos); + + if( SvMacroItemId::SwFrmResize == nEvent ) + { + xVar = new SbxVariable; + xVar->PutUShort( static_cast< sal_uInt16 >(g_eSdrMoveHdl) ); + xArgs->Put(xVar.get(), ++nPos); + } + + xVar = new SbxVariable; + xVar->PutLong( aDocPt.X() - aSttPt.X() ); + xArgs->Put(xVar.get(), ++nPos); + xVar = new SbxVariable; + xVar->PutLong( aDocPt.Y() - aSttPt.Y() ); + xArgs->Put(xVar.get(), ++nPos); + + OUString sRet; + + ReleaseMouse(); + + rSh.ExecMacro( *pMacro, &sRet, xArgs.get() ); + + CaptureMouse(); + + if( !sRet.isEmpty() && sRet.toInt32()!=0 ) + return ; + } + } + // event processing for resizing + + if( bIsDocReadOnly ) + break; + + bool bResizeKeepRatio = rSh.GetSelectionType() & SelectionType::Graphic || + rSh.GetSelectionType() & SelectionType::Media || + rSh.GetSelectionType() & SelectionType::Ole; + bool bisResize = g_eSdrMoveHdl != SdrHdlKind::Move; + + if (pSdrView) + { + // Resize proportionally when media is selected and the user drags on a corner + const Point aSttPt(PixelToLogic(m_aStartPos)); + SdrHdl* pHdl = pSdrView->PickHandle(aSttPt); + if (pHdl) + bResizeKeepRatio = bResizeKeepRatio && pHdl->IsCornerHdl(); + + if (pSdrView->GetDragMode() == SdrDragMode::Crop) + bisResize = false; + if (rMEvt.IsShift()) + { + pSdrView->SetAngleSnapEnabled(!bResizeKeepRatio); + if (bisResize) + pSdrView->SetOrtho(!bResizeKeepRatio); + else + pSdrView->SetOrtho(true); + } + else + { + pSdrView->SetAngleSnapEnabled(bResizeKeepRatio); + if (bisResize) + pSdrView->SetOrtho(bResizeKeepRatio); + else + pSdrView->SetOrtho(false); + } + } + + rSh.Drag( &aDocPt, rMEvt.IsShift() ); + m_bIsInMove = true; + } + else if( bIsDocReadOnly ) + break; + + if ( !bInsWin ) + { + Point aTmp( aDocPt ); + aTmp += rSh.VisArea().Pos() - aOldPt; + LeaveArea( aTmp ); + } + else if(m_bIsInMove) + EnterArea(); + return; + } + if ( !rSh.IsSelFrameMode() && !g_bDDINetAttr && + (IsMinMove( m_aStartPos,aPixPt ) || m_bIsInMove) && + (rSh.IsInSelect() || !rSh.TestCurrPam( aDocPt )) ) + { + if ( pSdrView ) + { + if ( rMEvt.IsShift() ) + pSdrView->SetOrtho(true); + else + pSdrView->SetOrtho(false); + } + if ( !bInsWin ) + { + Point aTmp( aDocPt ); + aTmp += rSh.VisArea().Pos() - aOldPt; + LeaveArea( aTmp ); + } + else + { + if( !rMEvt.IsSynthetic() && + ( MOUSE_LEFT != rMEvt.GetButtons() || + KEY_MOD1 != rMEvt.GetModifier() || + !rSh.Is_FnDragEQBeginDrag() || + rSh.IsAddMode() ) ) + { + rSh.Drag( &aDocPt, false ); + + g_bValidCursorPos = !(CRSR_POSCHG & rSh.CallSetCursor(&aDocPt, false)); + EnterArea(); + } + } + } + g_bDDINetAttr = false; + break; + case 0: + { + if ( m_pApplyTempl ) + { + UpdatePointer(aDocPt); // maybe a frame has to be marked here + break; + } + // change ui if mouse is over SwPostItField + // TODO: do the same thing for redlines IsAttrAtPos::Redline + SwContentAtPos aContentAtPos( IsAttrAtPos::Field); + if (rSh.GetContentAtPos(aDocPt, aContentAtPos, false)) + { + const SwField* pField = aContentAtPos.aFnd.pField; + if (pField->Which()== SwFieldIds::Postit) + { + m_rView.GetPostItMgr()->SetShadowState(reinterpret_cast<const SwPostItField*>(pField),false); + } + else + m_rView.GetPostItMgr()->SetShadowState(nullptr,false); + } + else + m_rView.GetPostItMgr()->SetShadowState(nullptr,false); + [[fallthrough]]; + } + case KEY_SHIFT: + case KEY_MOD2: + case KEY_MOD1: + if ( !m_bInsDraw ) + { + bool bTstShdwCursor = true; + + UpdatePointer( aDocPt, rMEvt.GetModifier() ); + + const SwFrameFormat* pFormat = nullptr; + const SwFormatINetFormat* pINet = nullptr; + SwContentAtPos aContentAtPos( IsAttrAtPos::InetAttr ); + if( rSh.GetContentAtPos( aDocPt, aContentAtPos ) ) + pINet = static_cast<const SwFormatINetFormat*>(aContentAtPos.aFnd.pAttr); + + const void* pTmp = pINet; + + if( pINet || + nullptr != ( pTmp = pFormat = rSh.GetFormatFromAnyObj( aDocPt ))) + { + bTstShdwCursor = false; + if( pTmp == pINet ) + m_aSaveCallEvent.Set( pINet ); + else + { + IMapObject* pIMapObj = pFormat->GetIMapObject( aDocPt ); + if( pIMapObj ) + m_aSaveCallEvent.Set( pFormat, pIMapObj ); + else + m_aSaveCallEvent.Set( EVENT_OBJECT_URLITEM, pFormat ); + } + + // should be over an InternetField with an + // embedded macro? + if( m_aSaveCallEvent != aLastCallEvent ) + { + if( aLastCallEvent.HasEvent() ) + rSh.CallEvent( SvMacroItemId::OnMouseOut, + aLastCallEvent, true ); + // 0 says that the object doesn't have any table + if( !rSh.CallEvent( SvMacroItemId::OnMouseOver, + m_aSaveCallEvent )) + m_aSaveCallEvent.Clear(); + } + } + else if( aLastCallEvent.HasEvent() ) + { + // cursor was on an object + rSh.CallEvent( SvMacroItemId::OnMouseOut, + aLastCallEvent, true ); + } + + if( bTstShdwCursor && bInsWin && !bIsDocReadOnly && + !m_bInsFrame && + !rSh.GetViewOptions()->getBrowseMode() && + rSh.GetViewOptions()->IsShadowCursor() && + !(rMEvt.GetModifier() + rMEvt.GetButtons()) && + !rSh.HasSelection() && !GetOutDev()->GetConnectMetaFile() ) + { + SwRect aRect; + + SwFillMode eMode = rSh.GetViewOptions()->GetShdwCursorFillMode(); + if( rSh.GetShadowCursorPos( aDocPt, eMode, aRect, m_eOrient )) + { + if( !m_pShadCursor ) + m_pShadCursor.reset( new SwShadowCursor( *this ) ); + if( text::HoriOrientation::RIGHT != m_eOrient && text::HoriOrientation::CENTER != m_eOrient ) + m_eOrient = text::HoriOrientation::LEFT; + m_pShadCursor->SetPos( aRect.Pos(), aRect.Height(), static_cast< sal_uInt16 >(m_eOrient) ); + bDelShadCursor = false; + } + } + } + break; + case MOUSE_LEFT + KEY_MOD2: + if( rSh.IsBlockMode() && !rMEvt.IsSynthetic() ) + { + rSh.Drag( &aDocPt, false ); + g_bValidCursorPos = !(CRSR_POSCHG & rSh.CallSetCursor(&aDocPt, false)); + EnterArea(); + } + break; + } + + if( bDelShadCursor && m_pShadCursor ) + { + m_pShadCursor.reset(); + } + m_bWasShdwCursor = false; +} + +/** + * Button Up + */ +void SwEditWin::MouseButtonUp(const MouseEvent& rMEvt) +{ + if (comphelper::LibreOfficeKit::isActive()) + { + if (vcl::Window* pWindow = m_rView.GetPostItMgr()->IsHitSidebarWindow(rMEvt.GetPosPixel())) + { + pWindow->MouseButtonUp(rMEvt); + return; + } + } + + bool bCallBase = true; + + bool bCallShadowCursor = m_bWasShdwCursor; + m_bWasShdwCursor = false; + if( m_pShadCursor ) + { + m_pShadCursor.reset(); + } + + m_xRowColumnSelectionStart.reset(); + + SdrHdlKind eOldSdrMoveHdl = g_eSdrMoveHdl; + g_eSdrMoveHdl = SdrHdlKind::User; // for MoveEvents - reset again + + // preventively reset + m_rView.SetTabColFromDoc( false ); + m_rView.SetNumRuleNodeFromDoc(nullptr); + + SwWrtShell &rSh = m_rView.GetWrtShell(); + CurrShell aCurr( &rSh ); + SdrView *pSdrView = rSh.GetDrawView(); + if ( pSdrView ) + { + // tdf34555: ortho was always reset before being used in EndSdrDrag + // Now, it is reset only if not in Crop mode. + if (pSdrView->GetDragMode() != SdrDragMode::Crop && !rMEvt.IsShift()) + pSdrView->SetOrtho(false); + + if ( pSdrView->MouseButtonUp( rMEvt,GetOutDev() ) ) + { + rSh.GetView().GetViewFrame().GetBindings().InvalidateAll(false); + return; // SdrView's event evaluated + } + } + // only process MouseButtonUp when the Down went to that windows as well. + if ( !m_bMBPressed ) + { + // Undo for the watering can is already in CommandHdl + // that's the way it should be! + + return; + } + + Point aDocPt( PixelToLogic( rMEvt.GetPosPixel() ) ); + + if ( g_bDDTimerStarted ) + { + StopDDTimer( &rSh, aDocPt ); + m_bMBPressed = false; + if ( rSh.IsSelFrameMode() ) + { + rSh.EndDrag( &aDocPt, false ); + g_bFrameDrag = false; + } + g_bNoInterrupt = false; + const Point aDocPos( PixelToLogic( rMEvt.GetPosPixel() ) ); + if ((PixelToLogic(m_aStartPos).Y() == (aDocPos.Y())) && (PixelToLogic(m_aStartPos).X() == (aDocPos.X())))//To make sure it was not moved + { + SdrPageView* pPV = nullptr; + SdrObject* pObj = pSdrView ? pSdrView->PickObj(aDocPos, pSdrView->getHitTolLog(), pPV, SdrSearchOptions::ALSOONMASTER) : nullptr; + if (pObj) + { + SwFrameFormat* pFormat = GetUserCall(pObj)->GetFormat(); + SwFrameFormat* pShapeFormat = SwTextBoxHelper::getOtherTextBoxFormat(pFormat, RES_FLYFRMFMT); + if (!pShapeFormat) + { + pSdrView->UnmarkAllObj(); + pSdrView->MarkObj(pObj,pPV); + } + else + { + // If the fly frame is a textbox of a shape, then select the shape instead. + SdrObject* pShape = pShapeFormat->FindSdrObject(); + pSdrView->UnmarkAllObj(); + pSdrView->MarkObj(pShape, pPV); + } + } + } + ReleaseMouse(); + return; + } + + if( m_pAnchorMarker ) + { + if(m_pAnchorMarker->GetHdl()) + { + // #i121463# delete selected after drag + m_pAnchorMarker->GetHdl()->SetSelected(false); + } + + Point aPnt( m_pAnchorMarker->GetLastPos() ); + m_pAnchorMarker.reset(); + if( aPnt.X() || aPnt.Y() ) + rSh.FindAnchorPos( aPnt, true ); + } + if ( m_bInsDraw && m_rView.GetDrawFuncPtr() ) + { + if ( m_rView.GetDrawFuncPtr()->MouseButtonUp( rMEvt ) ) + { + if (m_rView.GetDrawFuncPtr()) // could have been destroyed in MouseButtonUp + { + m_rView.GetDrawFuncPtr()->Deactivate(); + + if (!m_rView.IsDrawMode()) + { + m_rView.SetDrawFuncPtr(nullptr); + SfxBindings& rBind = m_rView.GetViewFrame().GetBindings(); + rBind.Invalidate( SID_ATTR_SIZE ); + rBind.Invalidate( SID_TABLE_CELL ); + } + } + + if ( rSh.IsObjSelected() ) + { + rSh.EnterSelFrameMode(); + if (!m_rView.GetDrawFuncPtr()) + StdDrawMode( SdrObjKind::NONE, true ); + } + else if ( rSh.IsFrameSelected() ) + { + rSh.EnterSelFrameMode(); + StopInsFrame(); + } + else + { + const Point aDocPos( PixelToLogic( m_aStartPos ) ); + g_bValidCursorPos = !(CRSR_POSCHG & rSh.CallSetCursor(&aDocPos, false)); + rSh.Edit(); + } + + m_rView.AttrChangedNotify(nullptr); + } + else if (rMEvt.GetButtons() == MOUSE_RIGHT && rSh.IsDrawCreate()) + m_rView.GetDrawFuncPtr()->BreakCreate(); // abort drawing + + g_bNoInterrupt = false; + if (IsMouseCaptured()) + ReleaseMouse(); + return; + } + bool bPopMode = false; + switch ( rMEvt.GetModifier() + rMEvt.GetButtons() ) + { + case MOUSE_LEFT: + if ( m_bInsDraw && rSh.IsDrawCreate() ) + { + if ( m_rView.GetDrawFuncPtr() && m_rView.GetDrawFuncPtr()->MouseButtonUp(rMEvt) ) + { + m_rView.GetDrawFuncPtr()->Deactivate(); + m_rView.AttrChangedNotify(nullptr); + if ( rSh.IsObjSelected() ) + rSh.EnterSelFrameMode(); + if ( m_rView.GetDrawFuncPtr() && m_bInsFrame ) + StopInsFrame(); + } + bCallBase = false; + break; + } + [[fallthrough]]; + case MOUSE_LEFT + KEY_MOD1: + case MOUSE_LEFT + KEY_MOD2: + case MOUSE_LEFT + KEY_SHIFT + KEY_MOD1: + if ( g_bFrameDrag && rSh.IsSelFrameMode() ) + { + if ( rMEvt.IsMod1() ) // copy and don't move. + { + // abort drag, use internal Copy instead + tools::Rectangle aRect; + rSh.GetDrawView()->TakeActionRect( aRect ); + if (!aRect.IsEmpty()) + { + rSh.BreakDrag(); + Point aEndPt, aSttPt; + if ( rSh.GetSelFrameType() & FrameTypeFlags::FLY_ATCNT ) + { + aEndPt = aRect.TopLeft(); + aSttPt = rSh.GetDrawView()->GetAllMarkedRect().TopLeft(); + } + else + { + aEndPt = aRect.Center(); + aSttPt = rSh.GetDrawView()->GetAllMarkedRect().Center(); + } + if ( aSttPt != aEndPt ) + { + rSh.StartUndo( SwUndoId::UI_DRAG_AND_COPY ); + rSh.Copy(rSh, aSttPt, aEndPt); + rSh.EndUndo( SwUndoId::UI_DRAG_AND_COPY ); + } + } + else { + rSh.EndDrag( &aDocPt, false ); + } + } + else + { + { + const SwFrameFormat *const pFlyFormat(rSh.GetFlyFrameFormat()); + const SvxMacro* pMacro = nullptr; + + SvMacroItemId nEvent = SdrHdlKind::Move == eOldSdrMoveHdl + ? SvMacroItemId::SwFrmMove + : SvMacroItemId::SwFrmResize; + + if (nullptr != pFlyFormat) + pMacro = pFlyFormat->GetMacro().GetMacroTable().Get(nEvent); + if (nullptr != pMacro) + { + const Point aSttPt( PixelToLogic( m_aStartPos ) ); + m_aRszMvHdlPt = aDocPt; + sal_uInt32 nPos = 0; + SbxArrayRef xArgs = new SbxArray; + SbxVariableRef xVar = new SbxVariable; + xVar->PutString( pFlyFormat->GetName() ); + xArgs->Put(xVar.get(), ++nPos); + + if( SvMacroItemId::SwFrmResize == nEvent ) + { + xVar = new SbxVariable; + xVar->PutUShort( static_cast< sal_uInt16 >(eOldSdrMoveHdl) ); + xArgs->Put(xVar.get(), ++nPos); + } + + xVar = new SbxVariable; + xVar->PutLong( aDocPt.X() - aSttPt.X() ); + xArgs->Put(xVar.get(), ++nPos); + xVar = new SbxVariable; + xVar->PutLong( aDocPt.Y() - aSttPt.Y() ); + xArgs->Put(xVar.get(), ++nPos); + + xVar = new SbxVariable; + xVar->PutUShort( 1 ); + xArgs->Put(xVar.get(), ++nPos); + + ReleaseMouse(); + + rSh.ExecMacro( *pMacro, nullptr, xArgs.get() ); + + CaptureMouse(); + } + + if (pFlyFormat) + { + // See if the fly frame's anchor is in a content control. If so, + // interact with it. + const SwFormatAnchor& rFormatAnchor = pFlyFormat->GetAnchor(); + SwNode* pAnchorNode = rFormatAnchor.GetAnchorNode(); + if (pAnchorNode) + { + SwTextNode* pTextNode = pAnchorNode->GetTextNode(); + if (pTextNode) + { + SwTextAttr* pAttr = pTextNode->GetTextAttrAt( + rFormatAnchor.GetAnchorContentOffset(), RES_TXTATR_CONTENTCONTROL, + ::sw::GetTextAttrMode::Parent); + if (pAttr) + { + SwTextContentControl* pTextContentControl + = static_txtattr_cast<SwTextContentControl*>(pAttr); + const SwFormatContentControl& rFormatContentControl + = pTextContentControl->GetContentControl(); + rSh.GotoContentControl(rFormatContentControl); + } + } + } + } + } + rSh.EndDrag( &aDocPt, false ); + } + g_bFrameDrag = false; + bCallBase = false; + break; + } + bPopMode = true; + [[fallthrough]]; + case MOUSE_LEFT + KEY_SHIFT: + if (rSh.IsSelFrameMode()) + { + + rSh.EndDrag( &aDocPt, false ); + g_bFrameDrag = false; + bCallBase = false; + break; + } + + if( g_bHoldSelection ) + { + // the EndDrag should be called in any case + g_bHoldSelection = false; + rSh.EndDrag( &aDocPt, false ); + } + else + { + SwContentAtPos aFieldAtPos ( IsAttrAtPos::Field ); + if ( !rSh.IsInSelect() && rSh.TestCurrPam( aDocPt ) && + !rSh.GetContentAtPos( aDocPt, aFieldAtPos ) ) + { + const bool bTmpNoInterrupt = g_bNoInterrupt; + g_bNoInterrupt = false; + { // create only temporary move context because otherwise + // the query to the content form doesn't work!!! + SwMvContext aMvContext( &rSh ); + const Point aDocPos( PixelToLogic( m_aStartPos ) ); + g_bValidCursorPos = !(CRSR_POSCHG & rSh.CallSetCursor(&aDocPos, false)); + } + g_bNoInterrupt = bTmpNoInterrupt; + + } + else + { + bool bInSel = rSh.IsInSelect(); + rSh.EndDrag( &aDocPt, false ); + + // Internetfield? --> call link (load doc!!) + if( !bInSel ) + { + LoadUrlFlags nFilter = LoadUrlFlags::NONE; + if( KEY_MOD1 == rMEvt.GetModifier() ) + nFilter |= LoadUrlFlags::NewView; + + bool bExecHyperlinks = m_rView.GetDocShell()->IsReadOnly(); + if ( !bExecHyperlinks ) + { + const bool bSecureOption = SvtSecurityOptions::IsOptionSet( SvtSecurityOptions::EOption::CtrlClickHyperlink ); + if ( ( bSecureOption && rMEvt.GetModifier() == KEY_MOD1 ) || + ( !bSecureOption && rMEvt.GetModifier() != KEY_MOD1 ) ) + bExecHyperlinks = true; + } + + const bool bExecSmarttags = rMEvt.GetModifier() == KEY_MOD1; + + if(m_pApplyTempl) + bExecHyperlinks = false; + + SwContentAtPos aContentAtPos( IsAttrAtPos::Field | + IsAttrAtPos::InetAttr | + IsAttrAtPos::SmartTag | IsAttrAtPos::FormControl | + IsAttrAtPos::ContentControl); + + if( rSh.GetContentAtPos( aDocPt, aContentAtPos ) ) + { + // Do it again if we're not on a field/hyperlink to update the cursor accordingly + if ( IsAttrAtPos::Field != aContentAtPos.eContentAtPos + && IsAttrAtPos::InetAttr != aContentAtPos.eContentAtPos ) + rSh.GetContentAtPos( aDocPt, aContentAtPos, true ); + + bool bViewLocked = rSh.IsViewLocked(); + if( !bViewLocked && !rSh.IsReadOnlyAvailable() && + aContentAtPos.IsInProtectSect() ) + rSh.LockView( true ); + + ReleaseMouse(); + + if( IsAttrAtPos::Field == aContentAtPos.eContentAtPos ) + { + bool bAddMode(false); + // AdditionalMode if applicable + if (KEY_MOD1 == rMEvt.GetModifier() + && !rSh.IsAddMode()) + { + bAddMode = true; + rSh.EnterAddMode(); + } + if ( aContentAtPos.pFndTextAttr != nullptr + && aContentAtPos.pFndTextAttr->Which() == RES_TXTATR_INPUTFIELD ) + { + if (!rSh.IsInSelect()) + { + // create only temporary move context because otherwise + // the query to the content form doesn't work!!! + SwMvContext aMvContext( &rSh ); + const Point aDocPos( PixelToLogic( m_aStartPos ) ); + g_bValidCursorPos = !(CRSR_POSCHG & rSh.CallSetCursor(&aDocPos, false)); + } + else + { + g_bValidCursorPos = true; + } + } + else + { + rSh.ClickToField(*aContentAtPos.aFnd.pField, bExecHyperlinks); + // a bit of a mystery what this is good for? + // in this case we assume it's valid since we + // just selected a field + g_bValidCursorPos = true; + } + if (bAddMode) + { + rSh.LeaveAddMode(); + } + } + else if (aContentAtPos.eContentAtPos == IsAttrAtPos::ContentControl) + { + auto pTextContentControl + = static_txtattr_cast<const SwTextContentControl*>( + aContentAtPos.pFndTextAttr); + const SwFormatContentControl& rFormatContentControl + = pTextContentControl->GetContentControl(); + rSh.GotoContentControl(rFormatContentControl); + } + else if ( IsAttrAtPos::SmartTag == aContentAtPos.eContentAtPos ) + { + // execute smarttag menu + if ( bExecSmarttags && SwSmartTagMgr::Get().IsSmartTagsEnabled() ) + m_rView.ExecSmartTagPopup( aDocPt ); + } + else if ( IsAttrAtPos::FormControl == aContentAtPos.eContentAtPos ) + { + OSL_ENSURE( aContentAtPos.aFnd.pFieldmark != nullptr, "where is my field ptr???"); + if ( aContentAtPos.aFnd.pFieldmark != nullptr) + { + IFieldmark *fieldBM = const_cast< IFieldmark* > ( aContentAtPos.aFnd.pFieldmark ); + if ( fieldBM->GetFieldname( ) == ODF_FORMCHECKBOX ) + { + ICheckboxFieldmark& rCheckboxFm = dynamic_cast<ICheckboxFieldmark&>(*fieldBM); + rCheckboxFm.SetChecked(!rCheckboxFm.IsChecked()); + rCheckboxFm.Invalidate(); + rSh.InvalidateWindows( SwRect(m_rView.GetVisArea()) ); + } + } + } + else if ( IsAttrAtPos::InetAttr == aContentAtPos.eContentAtPos ) + { + if (comphelper::LibreOfficeKit::isActive()) + { + OUString val((*static_cast<const SwFormatINetFormat*>(aContentAtPos.aFnd.pAttr)).GetValue()); + if (val.startsWith("#")) + bExecHyperlinks = true; + } + if ( bExecHyperlinks && aContentAtPos.aFnd.pAttr ) + rSh.ClickToINetAttr( *static_cast<const SwFormatINetFormat*>(aContentAtPos.aFnd.pAttr), nFilter ); + } + + rSh.LockView( bViewLocked ); + bCallShadowCursor = false; + } + else + { + aContentAtPos = SwContentAtPos( IsAttrAtPos::Ftn ); + if( !rSh.GetContentAtPos( aDocPt, aContentAtPos, true ) && bExecHyperlinks ) + { + SdrViewEvent aVEvt; + + if (pSdrView) + pSdrView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + + if (pSdrView && aVEvt.meEvent == SdrEventKind::ExecuteUrl) + { + // hit URL field + const SvxURLField *pField = aVEvt.mpURLField; + if (pField) + { + const OUString& sURL(pField->GetURL()); + const OUString& sTarget(pField->GetTargetFrame()); + ::LoadURL(rSh, sURL, nFilter, sTarget); + } + bCallShadowCursor = false; + } + else + { + // hit graphic + ReleaseMouse(); + if( rSh.ClickToINetGrf( aDocPt, nFilter )) + bCallShadowCursor = false; + } + } + } + + if( bCallShadowCursor && + rSh.GetViewOptions()->IsShadowCursor() && + MOUSE_LEFT == (rMEvt.GetModifier() + rMEvt.GetButtons()) && + !rSh.HasSelection() && + !GetOutDev()->GetConnectMetaFile() && + rSh.VisArea().Contains( aDocPt )) + { + SwUndoId nLastUndoId(SwUndoId::EMPTY); + if (rSh.GetLastUndoInfo(nullptr, & nLastUndoId)) + { + if (SwUndoId::INS_FROM_SHADOWCRSR == nLastUndoId) + { + rSh.Undo(); + } + } + SwFillMode eMode = rSh.GetViewOptions()->GetShdwCursorFillMode(); + rSh.SetShadowCursorPos( aDocPt, eMode ); + } + } + } + bCallBase = false; + + } + + // reset pushed mode in Down again if applicable + if ( bPopMode && g_bModePushed ) + { + rSh.PopMode(); + g_bModePushed = false; + bCallBase = false; + } + break; + + default: + ReleaseMouse(); + return; + } + + if( m_pApplyTempl ) + { + SelectionType eSelection = rSh.GetSelectionType(); + SwFormatClipboard* pFormatClipboard = m_pApplyTempl->m_pFormatClipboard; + if( pFormatClipboard )//apply format paintbrush + { + //get some parameters + SwWrtShell& rWrtShell = m_rView.GetWrtShell(); + SfxStyleSheetBasePool* pPool=nullptr; + bool bNoCharacterFormats = false; + // Paste paragraph properties if the selection contains a whole paragraph or + // there was no selection at all (i.e. just a left click) + bool bNoParagraphFormats = rSh.HasSelection() && rSh.IsSelOnePara() && !rSh.IsSelFullPara(); + + { + SwDocShell* pDocSh = m_rView.GetDocShell(); + if(pDocSh) + pPool = pDocSh->GetStyleSheetPool(); + if( (rMEvt.GetModifier()&KEY_MOD1) && (rMEvt.GetModifier()&KEY_SHIFT) ) + { + bNoCharacterFormats = true; + bNoParagraphFormats = false; + } + else if( rMEvt.GetModifier() & KEY_MOD1 ) + bNoParagraphFormats = true; + } + //execute paste + pFormatClipboard->Paste( rWrtShell, pPool, bNoCharacterFormats, bNoParagraphFormats ); + + //if the clipboard is empty after paste remove the ApplyTemplate + if(!pFormatClipboard->HasContent()) + SetApplyTemplate(SwApplyTemplate()); + + //tdf#38101 remove temporary highlighting + m_pUserMarker.reset(); + } + else if( m_pApplyTempl->nColor ) + { + sal_uInt16 nId = 0; + switch( m_pApplyTempl->nColor ) + { + case SID_ATTR_CHAR_COLOR_EXT: + nId = RES_CHRATR_COLOR; + break; + case SID_ATTR_CHAR_BACK_COLOR: + case SID_ATTR_CHAR_COLOR_BACKGROUND: + nId = RES_CHRATR_BACKGROUND; + break; + } + if( nId && (SelectionType::Text|SelectionType::Table) & eSelection) + { + if( rSh.IsSelection() && !rSh.HasReadonlySel() ) + { + m_pApplyTempl->nUndo = + std::min(m_pApplyTempl->nUndo, rSh.GetDoc()->GetIDocumentUndoRedo().GetUndoActionCount()); + if (nId == RES_CHRATR_BACKGROUND) + ApplyCharBackground(m_aWaterCanTextBackColor, model::ComplexColor(), rSh); + else + rSh.SetAttrItem( SvxColorItem( m_aWaterCanTextColor, nId ) ); + rSh.UnSetVisibleCursor(); + rSh.EnterStdMode(); + rSh.SetVisibleCursor(aDocPt); + bCallBase = false; + m_aTemplateTimer.Stop(); + } + else if(rMEvt.GetClicks() == 1) + { + // no selection -> so turn off watering can + m_aTemplateTimer.Start(); + } + } + } + else + { + OUString aStyleName; + switch ( m_pApplyTempl->eType ) + { + case SfxStyleFamily::Para: + if( (( SelectionType::Text | SelectionType::Table ) + & eSelection ) && !rSh.HasReadonlySel() ) + { + rSh.SetTextFormatColl( m_pApplyTempl->aColl.pTextColl ); + m_pApplyTempl->nUndo = + std::min(m_pApplyTempl->nUndo, rSh.GetDoc()->GetIDocumentUndoRedo().GetUndoActionCount()); + bCallBase = false; + if ( m_pApplyTempl->aColl.pTextColl ) + aStyleName = m_pApplyTempl->aColl.pTextColl->GetName(); + } + break; + case SfxStyleFamily::Char: + if( (( SelectionType::Text | SelectionType::Table ) + & eSelection ) && !rSh.HasReadonlySel() ) + { + rSh.SetAttrItem( SwFormatCharFormat(m_pApplyTempl->aColl.pCharFormat) ); + rSh.UnSetVisibleCursor(); + rSh.EnterStdMode(); + rSh.SetVisibleCursor(aDocPt); + m_pApplyTempl->nUndo = + std::min(m_pApplyTempl->nUndo, rSh.GetDoc()->GetIDocumentUndoRedo().GetUndoActionCount()); + bCallBase = false; + if ( m_pApplyTempl->aColl.pCharFormat ) + aStyleName = m_pApplyTempl->aColl.pCharFormat->GetName(); + } + break; + case SfxStyleFamily::Frame : + { + const SwFrameFormat* pFormat = rSh.GetFormatFromObj( aDocPt ); + if(dynamic_cast<const SwFlyFrameFormat*>( pFormat) ) + { + rSh.SetFrameFormat( m_pApplyTempl->aColl.pFrameFormat, false, &aDocPt ); + m_pApplyTempl->nUndo = + std::min(m_pApplyTempl->nUndo, rSh.GetDoc()->GetIDocumentUndoRedo().GetUndoActionCount()); + bCallBase = false; + if( m_pApplyTempl->aColl.pFrameFormat ) + aStyleName = m_pApplyTempl->aColl.pFrameFormat->GetName(); + } + break; + } + case SfxStyleFamily::Page: + // no Undo with page templates + rSh.ChgCurPageDesc( *m_pApplyTempl->aColl.pPageDesc ); + if ( m_pApplyTempl->aColl.pPageDesc ) + aStyleName = m_pApplyTempl->aColl.pPageDesc->GetName(); + m_pApplyTempl->nUndo = + std::min(m_pApplyTempl->nUndo, rSh.GetDoc()->GetIDocumentUndoRedo().GetUndoActionCount()); + bCallBase = false; + break; + case SfxStyleFamily::Pseudo: + if( !rSh.HasReadonlySel() ) + { + rSh.SetCurNumRule( *m_pApplyTempl->aColl.pNumRule, + false, + m_pApplyTempl->aColl.pNumRule->GetDefaultListId() ); + bCallBase = false; + m_pApplyTempl->nUndo = + std::min(m_pApplyTempl->nUndo, rSh.GetDoc()->GetIDocumentUndoRedo().GetUndoActionCount()); + if( m_pApplyTempl->aColl.pNumRule ) + aStyleName = m_pApplyTempl->aColl.pNumRule->GetName(); + } + break; + default: break; + } + + uno::Reference< frame::XDispatchRecorder > xRecorder = + m_rView.GetViewFrame().GetBindings().GetRecorder(); + if ( !aStyleName.isEmpty() && xRecorder.is() ) + { + SfxShell *pSfxShell = lcl_GetTextShellFromDispatcher( m_rView ); + if ( pSfxShell ) + { + SfxRequest aReq(m_rView.GetViewFrame(), SID_STYLE_APPLY); + aReq.AppendItem( SfxStringItem( SID_STYLE_APPLY, aStyleName ) ); + aReq.AppendItem( SfxUInt16Item( SID_STYLE_FAMILY, static_cast<sal_uInt16>(m_pApplyTempl->eType) ) ); + aReq.Done(); + } + } + } + + } + ReleaseMouse(); + // Only processed MouseEvents arrive here; only at these this mode can + // be reset. + m_bMBPressed = false; + + // Make this call just to be sure. Selecting has finished surely by now. + // Otherwise the timeout's timer could give problems. + EnterArea(); + g_bNoInterrupt = false; + + if (bCallBase) + Window::MouseButtonUp(rMEvt); + + if (!(pSdrView && rMEvt.GetClicks() == 1 && comphelper::LibreOfficeKit::isActive())) + return; + + // When tiled rendering, single click on a shape text starts editing already. + SdrViewEvent aViewEvent; + SdrHitKind eHit = pSdrView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONUP, aViewEvent); + const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList(); + if (eHit == SdrHitKind::TextEditObj && rMarkList.GetMarkCount() == 1) + { + if (SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj()) + { + EnterDrawTextMode(pObj->GetLogicRect().Center()); + if ( auto pSwDrawTextShell = dynamic_cast< SwDrawTextShell *>( m_rView.GetCurShell() ) ) + pSwDrawTextShell->Init(); + } + } +} + +/** + * Apply template + */ +void SwEditWin::SetApplyTemplate(const SwApplyTemplate &rTempl) +{ + static bool bIdle = false; + m_pApplyTempl.reset(); + SwWrtShell &rSh = m_rView.GetWrtShell(); + + if(rTempl.m_pFormatClipboard) + { + m_pApplyTempl.reset(new SwApplyTemplate( rTempl )); + m_pApplyTempl->nUndo = rSh.GetDoc()->GetIDocumentUndoRedo().GetUndoActionCount(); + SetPointer( PointerStyle::Fill );//@todo #i20119# maybe better a new brush pointer here in future + rSh.NoEdit( false ); + bIdle = rSh.GetViewOptions()->IsIdle(); + rSh.GetViewOptions()->SetIdle( false ); + } + else if(rTempl.nColor) + { + m_pApplyTempl.reset(new SwApplyTemplate( rTempl )); + m_pApplyTempl->nUndo = rSh.GetDoc()->GetIDocumentUndoRedo().GetUndoActionCount(); + SetPointer( PointerStyle::Fill ); + rSh.NoEdit( false ); + bIdle = rSh.GetViewOptions()->IsIdle(); + rSh.GetViewOptions()->SetIdle( false ); + } + else if( rTempl.eType != SfxStyleFamily::None ) + { + m_pApplyTempl.reset(new SwApplyTemplate( rTempl )); + m_pApplyTempl->nUndo = rSh.GetDoc()->GetIDocumentUndoRedo().GetUndoActionCount(); + SetPointer( PointerStyle::Fill ); + rSh.NoEdit( false ); + bIdle = rSh.GetViewOptions()->IsIdle(); + rSh.GetViewOptions()->SetIdle( false ); + } + else + { + SetPointer( PointerStyle::Text ); + rSh.UnSetVisibleCursor(); + + rSh.GetViewOptions()->SetIdle( bIdle ); + if ( !rSh.IsSelFrameMode() ) + rSh.Edit(); + } + + static sal_uInt16 aInva[] = + { + SID_STYLE_WATERCAN, + SID_ATTR_CHAR_COLOR_EXT, + SID_ATTR_CHAR_COLOR_BACKGROUND_EXT, + 0 + }; + m_rView.GetViewFrame().GetBindings().Invalidate(aInva); +} + +/** + * Ctor + */ +SwEditWin::SwEditWin(vcl::Window *pParent, SwView &rMyView): + DocWindow(pParent, WinBits(WB_CLIPCHILDREN | WB_DIALOGCONTROL)), + DropTargetHelper( this ), + DragSourceHelper( this ), + + m_aTimer("SwEditWin"), + m_aKeyInputFlushTimer("SwEditWin m_aKeyInputFlushTimer"), + m_eBufferLanguage(LANGUAGE_DONTKNOW), + m_aTemplateTimer("SwEditWin m_aTemplateTimer"), + m_pUserMarkerObj( nullptr ), + + m_rView( rMyView ), + + m_aActHitType(SdrHitKind::NONE), + m_nDropFormat( SotClipboardFormatId::NONE ), + m_nDropAction( 0 ), + m_nDropDestination( SotExchangeDest::NONE ), + + m_eBezierMode(SID_BEZIER_INSERT), + m_nInsFrameColCount( 1 ), + m_eDrawMode(SdrObjKind::NONE), + + m_bMBPressed(false), + m_bInsDraw(false), + m_bInsFrame(false), + m_bIsInMove(false), + m_bIsInDrag(false), + m_bOldIdle(false), + m_bOldIdleSet(false), + m_bChainMode(false), + m_bWasShdwCursor(false), + m_bLockInput(false), + m_bIsRowDrag(false), + m_bUseInputLanguage(false), + m_bObjectSelect(false), + m_nKS_NUMDOWN_Count(0), + m_nKS_NUMINDENTINC_Count(0), + m_pFrameControlsManager(new SwFrameControlsManager(this)) +{ + set_id("writer_edit"); + SetHelpId(HID_EDIT_WIN); + EnableChildTransparentMode(); + SetDialogControlFlags( DialogControlFlags::Return | DialogControlFlags::WantFocus ); + + m_bMBPressed = m_bInsDraw = m_bInsFrame = + m_bIsInDrag = m_bOldIdle = m_bOldIdleSet = m_bChainMode = m_bWasShdwCursor = false; + // initially use the input language + m_bUseInputLanguage = true; + + SetMapMode(MapMode(MapUnit::MapTwip)); + + SetPointer( PointerStyle::Text ); + m_aTimer.SetInvokeHandler(LINK(this, SwEditWin, TimerHandler)); + + m_aKeyInputFlushTimer.SetTimeout( 20 ); + m_aKeyInputFlushTimer.SetInvokeHandler(LINK(this, SwEditWin, KeyInputFlushHandler)); + + // TemplatePointer for colors should be reset without + // selection after single click, but not after double-click (tdf#122442) + m_aTemplateTimer.SetTimeout(GetSettings().GetMouseSettings().GetDoubleClickTime()); + m_aTemplateTimer.SetInvokeHandler(LINK(this, SwEditWin, TemplateTimerHdl)); + + // temporary solution!!! Should set the font of the current + // insert position at every cursor movement! + if( !rMyView.GetDocShell()->IsReadOnly() ) + { + vcl::Font aFont; + SetInputContext( InputContext( aFont, InputContextFlags::Text | + InputContextFlags::ExtText ) ); + } +} + +SwEditWin::~SwEditWin() +{ + disposeOnce(); +} + +void SwEditWin::dispose() +{ + m_pShadCursor.reset(); + + if( s_pQuickHlpData->m_bIsDisplayed && m_rView.GetWrtShellPtr() ) + s_pQuickHlpData->Stop( m_rView.GetWrtShell() ); + g_bExecuteDrag = false; + m_pApplyTempl.reset(); + + m_rView.SetDrawFuncPtr(nullptr); + + m_pUserMarker.reset(); + + m_pAnchorMarker.reset(); + + m_pFrameControlsManager->dispose(); + m_pFrameControlsManager.reset(); + + DragSourceHelper::dispose(); + DropTargetHelper::dispose(); + vcl::Window::dispose(); +} + +/** + * Turn on DrawTextEditMode + */ +void SwEditWin::EnterDrawTextMode( const Point& aDocPos ) +{ + if ( m_rView.EnterDrawTextMode(aDocPos) ) + { + if (m_rView.GetDrawFuncPtr()) + { + m_rView.GetDrawFuncPtr()->Deactivate(); + m_rView.SetDrawFuncPtr(nullptr); + m_rView.LeaveDrawCreate(); + } + m_rView.NoRotate(); + m_rView.AttrChangedNotify(nullptr); + } +} + +/** + * Turn on DrawMode + */ +bool SwEditWin::EnterDrawMode(const MouseEvent& rMEvt, const Point& aDocPos) +{ + SwWrtShell &rSh = m_rView.GetWrtShell(); + SdrView *pSdrView = rSh.GetDrawView(); + + if ( m_rView.GetDrawFuncPtr() ) + { + if (rSh.IsDrawCreate()) + return true; + + bool bRet = m_rView.GetDrawFuncPtr()->MouseButtonDown( rMEvt ); + m_rView.AttrChangedNotify(nullptr); + return bRet; + } + + if ( pSdrView && pSdrView->IsTextEdit() ) + { + bool bUnLockView = !rSh.IsViewLocked(); + rSh.LockView( true ); + + rSh.EndTextEdit(); // clicked aside, end Edit + rSh.SelectObj( aDocPos ); + if ( !rSh.IsObjSelected() && !rSh.IsFrameSelected() ) + rSh.LeaveSelFrameMode(); + else + { + SwEditWin::s_nDDStartPosY = aDocPos.Y(); + SwEditWin::s_nDDStartPosX = aDocPos.X(); + g_bFrameDrag = true; + } + if( bUnLockView ) + rSh.LockView( false ); + m_rView.AttrChangedNotify(nullptr); + return true; + } + return false; +} + +bool SwEditWin::IsDrawSelMode() const +{ + return IsObjectSelect(); +} + +void SwEditWin::GetFocus() +{ + if ( m_rView.GetPostItMgr()->HasActiveSidebarWin() ) + { + m_rView.GetPostItMgr()->GrabFocusOnActiveSidebarWin(); + } + else + { + m_rView.GotFocus(); + Window::GetFocus(); +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + m_rView.GetWrtShell().InvalidateAccessibleFocus(); +#endif + } +} + +void SwEditWin::LoseFocus() +{ +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + if (m_rView.GetWrtShellPtr()) + m_rView.GetWrtShell().InvalidateAccessibleFocus(); +#endif + Window::LoseFocus(); + if( s_pQuickHlpData && s_pQuickHlpData->m_bIsDisplayed ) + s_pQuickHlpData->Stop( m_rView.GetWrtShell() ); +} + +void SwEditWin::Command( const CommandEvent& rCEvt ) +{ + if (isDisposed()) + { + // If ViewFrame dies shortly, no popup anymore! + Window::Command(rCEvt); + return; + } + + SwWrtShell &rSh = m_rView.GetWrtShell(); + + // The command event is send to the window after a possible context + // menu from an inplace client has been closed. Now we have the chance + // to deactivate the inplace client without any problem regarding parent + // windows and code on the stack. + SfxInPlaceClient* pIPClient = rSh.GetSfxViewShell()->GetIPClient(); + bool bIsOleActive = ( pIPClient && pIPClient->IsObjectInPlaceActive() ); + if ( bIsOleActive && ( rCEvt.GetCommand() == CommandEventId::ContextMenu )) + { + rSh.FinishOLEObj(); + return; + } + + bool bCallBase = true; + + switch ( rCEvt.GetCommand() ) + { + case CommandEventId::ContextMenu: + { + const sal_uInt16 nId = SwInputChild::GetChildWindowId(); + SwInputChild* pChildWin = static_cast<SwInputChild*>(GetView().GetViewFrame(). + GetChildWindow( nId )); + + if (m_rView.GetPostItMgr()->IsHit(rCEvt.GetMousePosPixel())) + return; + + Point aDocPos( PixelToLogic( rCEvt.GetMousePosPixel() ) ); + if ( !rCEvt.IsMouseEvent() ) + aDocPos = rSh.GetCharRect().Center(); + + // Don't trigger the command on a frame anchored to header/footer is not editing it + FrameControlType eControl; + bool bOverFly = false; + bool bPageAnchored = false; + bool bOverHeaderFooterFly = IsOverHeaderFooterFly( aDocPos, eControl, bOverFly, bPageAnchored ); + // !bOverHeaderFooterFly doesn't mean we have a frame to select + if ( !bPageAnchored && rCEvt.IsMouseEvent( ) && + ( ( rSh.IsHeaderFooterEdit( ) && !bOverHeaderFooterFly && bOverFly ) || + ( !rSh.IsHeaderFooterEdit( ) && bOverHeaderFooterFly ) ) ) + { + return; + } + + if((!pChildWin || pChildWin->GetView() != &m_rView) && + !rSh.IsDrawCreate() && !IsDrawAction()) + { + CurrShell aCurr( &rSh ); + if (!m_pApplyTempl) + { + if (g_bNoInterrupt) + { + ReleaseMouse(); + g_bNoInterrupt = false; + m_bMBPressed = false; + } + if ( rCEvt.IsMouseEvent() ) + { + SelectMenuPosition(rSh, rCEvt.GetMousePosPixel()); + m_rView.StopShellTimer(); + } + const Point aPixPos = LogicToPixel( aDocPos ); + + if ( m_rView.GetDocShell()->IsReadOnly() ) + { + SwReadOnlyPopup aROPopup(aDocPos, m_rView); + + ui::ContextMenuExecuteEvent aEvent; + aEvent.SourceWindow = VCLUnoHelper::GetInterface( this ); + aEvent.ExecutePosition.X = aPixPos.X(); + aEvent.ExecutePosition.Y = aPixPos.Y(); + rtl::Reference<VCLXPopupMenu> xMenu; + rtl::Reference<VCLXPopupMenu> xMenuInterface = aROPopup.CreateMenuInterface(); + if (GetView().TryContextMenuInterception(xMenuInterface, "private:resource/ReadonlyContextMenu", xMenu, aEvent)) + { + if (xMenu.is()) + { + css::uno::Reference<css::awt::XWindowPeer> xParent(aEvent.SourceWindow, css::uno::UNO_QUERY); + sal_uInt16 nExecId = xMenu->execute(xParent, css::awt::Rectangle(aPixPos.X(), aPixPos.Y(), 1, 1), + css::awt::PopupMenuDirection::EXECUTE_DOWN); + if (!::ExecuteMenuCommand(xMenu, m_rView.GetViewFrame(), nExecId)) + aROPopup.Execute(this, nExecId); + } + else + aROPopup.Execute(this, aPixPos); + } + } + else if ( !m_rView.ExecSpellPopup( aDocPos ) ) + SfxDispatcher::ExecutePopup(this, &aPixPos); + } + else if (m_pApplyTempl->nUndo < rSh.GetDoc()->GetIDocumentUndoRedo().GetUndoActionCount()) + { + // Undo until we reach the point when we entered this context. + rSh.Do(SwWrtShell::UNDO); + } + bCallBase = false; + } + } + break; + + case CommandEventId::Wheel: + case CommandEventId::StartAutoScroll: + case CommandEventId::AutoScroll: + if (m_pSavedOutlineFrame && rSh.GetViewOptions()->IsShowOutlineContentVisibilityButton()) + { + GetFrameControlsManager().RemoveControlsByType(FrameControlType::Outline, m_pSavedOutlineFrame); + m_pSavedOutlineFrame = nullptr; + } + m_pShadCursor.reset(); + bCallBase = !m_rView.HandleWheelCommands( rCEvt ); + break; + + case CommandEventId::GestureZoom: + { + if (m_pSavedOutlineFrame && rSh.GetViewOptions()->IsShowOutlineContentVisibilityButton()) + { + GetFrameControlsManager().RemoveControlsByType(FrameControlType::Outline, m_pSavedOutlineFrame); + m_pSavedOutlineFrame = nullptr; + } + m_pShadCursor.reset(); + bCallBase = !m_rView.HandleGestureZoomCommand(rCEvt); + break; + } + + case CommandEventId::GestureLongPress: + case CommandEventId::GestureSwipe: //nothing yet + break; + + case CommandEventId::StartExtTextInput: + { + bool bIsDocReadOnly = m_rView.GetDocShell()->IsReadOnly() && + rSh.IsCursorReadonly(); + if(!bIsDocReadOnly) + { + if( rSh.HasDrawView() && rSh.GetDrawView()->IsTextEdit() ) + { + bCallBase = false; + rSh.GetDrawView()->GetTextEditOutlinerView()->Command( rCEvt ); + } + else + { + if( rSh.HasSelection() ) + rSh.DelRight(); + + bCallBase = false; + LanguageType eInputLanguage = GetInputLanguage(); + rSh.CreateExtTextInput(eInputLanguage); + } + } + break; + } + case CommandEventId::EndExtTextInput: + { + bool bIsDocReadOnly = m_rView.GetDocShell()->IsReadOnly() && + rSh.IsCursorReadonly(); + if(!bIsDocReadOnly) + { + if( rSh.HasDrawView() && rSh.GetDrawView()->IsTextEdit() ) + { + bCallBase = false; + rSh.GetDrawView()->GetTextEditOutlinerView()->Command( rCEvt ); + } + else + { + bCallBase = false; + OUString sRecord = rSh.DeleteExtTextInput(); + uno::Reference< frame::XDispatchRecorder > xRecorder = + m_rView.GetViewFrame().GetBindings().GetRecorder(); + + if ( !sRecord.isEmpty() ) + { + // convert quotes in IME text + // works on the last input character, this is especially in Korean text often done + // quotes that are inside of the string are not replaced! + const sal_Unicode aCh = sRecord[sRecord.getLength() - 1]; + SvxAutoCorrCfg& rACfg = SvxAutoCorrCfg::Get(); + SvxAutoCorrect* pACorr = rACfg.GetAutoCorrect(); + if(pACorr && + (( pACorr->IsAutoCorrFlag( ACFlags::ChgQuotes ) && ('\"' == aCh ))|| + ( pACorr->IsAutoCorrFlag( ACFlags::ChgSglQuotes ) && ( '\'' == aCh)))) + { + rSh.DelLeft(); + rSh.AutoCorrect( *pACorr, aCh ); + } + + if ( xRecorder.is() ) + { + // determine Shell + SfxShell *pSfxShell = lcl_GetTextShellFromDispatcher( m_rView ); + // generate request and record + if (pSfxShell) + { + SfxRequest aReq(m_rView.GetViewFrame(), FN_INSERT_STRING); + aReq.AppendItem( SfxStringItem( FN_INSERT_STRING, sRecord ) ); + aReq.Done(); + } + } + } + } + } + } + break; + case CommandEventId::ExtTextInput: + { + bool bIsDocReadOnly = m_rView.GetDocShell()->IsReadOnly() && + rSh.IsCursorReadonly(); + if (!bIsDocReadOnly && !rSh.HasReadonlySel()) + { + if( s_pQuickHlpData->m_bIsDisplayed ) + s_pQuickHlpData->Stop( rSh ); + + if( rSh.HasDrawView() && rSh.GetDrawView()->IsTextEdit() ) + { + bCallBase = false; + rSh.GetDrawView()->GetTextEditOutlinerView()->Command( rCEvt ); + } + else + { + const CommandExtTextInputData* pData = rCEvt.GetExtTextInputData(); + if( pData ) + { + bCallBase = false; + rSh.SetExtTextInputData( *pData ); + } + } + uno::Reference< frame::XDispatchRecorder > xRecorder = + m_rView.GetViewFrame().GetBindings().GetRecorder(); + if(!xRecorder.is()) + { + SvxAutoCorrCfg& rACfg = SvxAutoCorrCfg::Get(); + if (!rACfg.IsAutoTextTip() || !ShowAutoText(rSh.GetChunkForAutoText())) + { + SvxAutoCorrect* pACorr = rACfg.GetAutoCorrect(); + if (pACorr && pACorr->GetSwFlags().bAutoCompleteWords) + ShowAutoCorrectQuickHelp(rSh.GetPrevAutoCorrWord(*pACorr), *pACorr); + } + } + } + + if (rSh.HasReadonlySel()) + { + // Inform the user that the request has been ignored. + rSh.InfoReadOnlyDialog(true); + } + } + break; + case CommandEventId::CursorPos: + // will be handled by the base class + break; + + case CommandEventId::PasteSelection: + if( !m_rView.GetDocShell()->IsReadOnly() ) + { + TransferableDataHelper aDataHelper( + TransferableDataHelper::CreateFromPrimarySelection()); + if( !aDataHelper.GetXTransferable().is() ) + break; + + SotExchangeDest nDropDestination = GetDropDestination( rCEvt.GetMousePosPixel() ); + if( nDropDestination == SotExchangeDest::NONE ) + break; + SotClipboardFormatId nDropFormat; + sal_uInt8 nEventAction, nDropAction; + SotExchangeActionFlags nActionFlags; + nDropAction = SotExchange::GetExchangeAction( + aDataHelper.GetDataFlavorExVector(), + nDropDestination, EXCHG_IN_ACTION_COPY, + EXCHG_IN_ACTION_COPY, nDropFormat, + nEventAction, + SotClipboardFormatId::NONE, nullptr, + &nActionFlags ); + if( EXCHG_INOUT_ACTION_NONE != nDropAction ) + { + const Point aDocPt( PixelToLogic( rCEvt.GetMousePosPixel() ) ); + SwTransferable::PasteData( aDataHelper, rSh, nDropAction, nActionFlags, + nDropFormat, nDropDestination, false, + false, &aDocPt, EXCHG_IN_ACTION_COPY, + true ); + } + } + break; + case CommandEventId::ModKeyChange : + { + const CommandModKeyData* pCommandData = rCEvt.GetModKeyData(); + if (!pCommandData->IsDown() && pCommandData->IsMod1() && !pCommandData->IsMod2()) + { + sal_uInt16 nSlot = 0; + if(pCommandData->IsLeftShift() && !pCommandData->IsRightShift()) + nSlot = SID_ATTR_PARA_LEFT_TO_RIGHT; + else if(!pCommandData->IsLeftShift() && pCommandData->IsRightShift()) + nSlot = SID_ATTR_PARA_RIGHT_TO_LEFT; + if(nSlot && SvtCTLOptions::IsCTLFontEnabled()) + GetView().GetViewFrame().GetDispatcher()->Execute(nSlot); + } + } + break; + case CommandEventId::InputLanguageChange : + // i#42732 - update state of fontname if input language changes + g_bInputLanguageSwitched = true; + SetUseInputLanguage( true ); + break; + case CommandEventId::SelectionChange: + { + const CommandSelectionChangeData *pData = rCEvt.GetSelectionChangeData(); + rSh.SttCursorMove(); + rSh.GoStartSentence(); + rSh.GetCursor()->GetPoint()->AdjustContent(sal::static_int_cast<sal_uInt16, sal_uLong>(pData->GetStart())); + rSh.SetMark(); + rSh.GetCursor()->GetMark()->AdjustContent(sal::static_int_cast<sal_uInt16, sal_uLong>(pData->GetEnd() - pData->GetStart())); + rSh.EndCursorMove( true ); + } + break; + case CommandEventId::PrepareReconversion: + if( rSh.HasSelection() ) + { + SwPaM *pCursor = rSh.GetCursor(); + + if( rSh.IsMultiSelection() ) + { + if (pCursor && !pCursor->HasMark() && + pCursor->GetPoint() == pCursor->GetMark()) + { + rSh.GoPrevCursor(); + pCursor = rSh.GetCursor(); + } + + // Cancel all selections other than the last selected one. + while( rSh.GetCursor()->GetNext() != rSh.GetCursor() ) + delete rSh.GetCursor()->GetNext(); + } + + if( pCursor ) + { + SwNodeOffset nPosNodeIdx = pCursor->GetPoint()->GetNodeIndex(); + const sal_Int32 nPosIdx = pCursor->GetPoint()->GetContentIndex(); + SwNodeOffset nMarkNodeIdx = pCursor->GetMark()->GetNodeIndex(); + const sal_Int32 nMarkIdx = pCursor->GetMark()->GetContentIndex(); + + if( !rSh.GetCursor()->HasMark() ) + rSh.GetCursor()->SetMark(); + + rSh.SttCursorMove(); + + if( nPosNodeIdx < nMarkNodeIdx ) + { + rSh.GetCursor()->GetPoint()->Assign(nPosNodeIdx, nPosIdx); + rSh.GetCursor()->GetMark()->Assign(nPosNodeIdx, + rSh.GetCursor()->GetPointContentNode()->Len()); + } + else if( nPosNodeIdx == nMarkNodeIdx ) + { + rSh.GetCursor()->GetPoint()->Assign(nPosNodeIdx, nPosIdx); + rSh.GetCursor()->GetMark()->Assign(nMarkNodeIdx, nMarkIdx); + } + else + { + rSh.GetCursor()->GetMark()->Assign(nMarkNodeIdx, nMarkIdx); + rSh.GetCursor()->GetPoint()->Assign(nMarkNodeIdx, + rSh.GetCursor()->GetMarkContentNode()->Len()); + } + + rSh.EndCursorMove( true ); + } + } + break; + case CommandEventId::QueryCharPosition: + { + bool bVertical = rSh.IsInVerticalText(); + const SwPosition& rPos = *rSh.GetCursor()->GetPoint(); + SwDocShell* pDocSh = m_rView.GetDocShell(); + SwDoc *pDoc = pDocSh->GetDoc(); + SwExtTextInput* pInput = pDoc->GetExtTextInput( rPos.GetNode(), rPos.GetContentIndex() ); + if ( pInput ) + { + const SwPosition& rStart = *pInput->Start(); + const SwPosition& rEnd = *pInput->End(); + sal_Int32 nSize = rEnd.GetContentIndex() - rStart.GetContentIndex(); + vcl::Window& rWin = rSh.GetView().GetEditWin(); + if ( nSize == 0 ) + { + // When the composition does not exist, use Caret rect instead. + const SwRect& aCaretRect ( rSh.GetCharRect() ); + tools::Rectangle aRect( aCaretRect.Left(), aCaretRect.Top(), aCaretRect.Right(), aCaretRect.Bottom() ); + rWin.SetCompositionCharRect( &aRect, 1, bVertical ); + } + else + { + std::unique_ptr<tools::Rectangle[]> aRects(new tools::Rectangle[ nSize ]); + int nRectIndex = 0; + for ( sal_Int32 nIndex = rStart.GetContentIndex(); nIndex < rEnd.GetContentIndex(); ++nIndex ) + { + const SwPosition aPos( rStart.GetNode(), rStart.GetNode().GetContentNode(), nIndex ); + SwRect aRect ( rSh.GetCharRect() ); + rSh.GetCharRectAt( aRect, &aPos ); + aRects[ nRectIndex ] = tools::Rectangle( aRect.Left(), aRect.Top(), aRect.Right(), aRect.Bottom() ); + ++nRectIndex; + } + rWin.SetCompositionCharRect( aRects.get(), nSize, bVertical ); + } + } + bCallBase = false; + } + break; + default: + SAL_WARN("sw.ui", "unknown command."); + break; + } + if (bCallBase) + Window::Command(rCEvt); +} + +/* i#18686 select the object/cursor at the mouse + position of the context menu request */ +void SwEditWin::SelectMenuPosition(SwWrtShell& rSh, const Point& rMousePos ) +{ + const Point aDocPos( PixelToLogic( rMousePos ) ); + const bool bIsInsideSelectedObj( rSh.IsInsideSelectedObj( aDocPos ) ); + //create a synthetic mouse event out of the coordinates + MouseEvent aMEvt(rMousePos); + SdrView *pSdrView = rSh.GetDrawView(); + if ( pSdrView ) + { + // no close of insert_draw and reset of + // draw mode, if context menu position is inside a selected object. + if ( !bIsInsideSelectedObj && m_rView.GetDrawFuncPtr() ) + { + + m_rView.GetDrawFuncPtr()->Deactivate(); + m_rView.SetDrawFuncPtr(nullptr); + m_rView.LeaveDrawCreate(); + SfxBindings& rBind = m_rView.GetViewFrame().GetBindings(); + rBind.Invalidate( SID_ATTR_SIZE ); + rBind.Invalidate( SID_TABLE_CELL ); + } + + // if draw text is active and there's a text selection + // at the mouse position then do nothing + if(rSh.GetSelectionType() & SelectionType::DrawObjectEditMode) + { + OutlinerView* pOLV = pSdrView->GetTextEditOutlinerView(); + ESelection aSelection = pOLV->GetSelection(); + if(!aSelection.IsZero()) + { + SdrOutliner* pOutliner = pSdrView->GetTextEditOutliner(); + bool bVertical = pOutliner->IsVertical(); + const EditEngine& rEditEng = pOutliner->GetEditEngine(); + Point aEEPos(aDocPos); + const tools::Rectangle& rOutputArea = pOLV->GetOutputArea(); + // regard vertical mode + if(bVertical) + { + aEEPos -= rOutputArea.TopRight(); + //invert the horizontal direction and exchange X and Y + tools::Long nTemp = -aEEPos.X(); + aEEPos.setX( aEEPos.Y() ); + aEEPos.setY( nTemp ); + } + else + aEEPos -= rOutputArea.TopLeft(); + + EPosition aDocPosition = rEditEng.FindDocPosition(aEEPos); + ESelection aCompare(aDocPosition.nPara, aDocPosition.nIndex); + // make it a forward selection - otherwise the IsLess/IsGreater do not work :-( + aSelection.Adjust(); + if(!(aCompare < aSelection) && !(aCompare > aSelection)) + { + return; + } + } + + } + + if (pSdrView->MouseButtonDown( aMEvt, GetOutDev() ) ) + { + pSdrView->MouseButtonUp( aMEvt, GetOutDev() ); + rSh.GetView().GetViewFrame().GetBindings().InvalidateAll(false); + return; + } + } + rSh.ResetCursorStack(); + + if ( EnterDrawMode( aMEvt, aDocPos ) ) + { + return; + } + if ( m_rView.GetDrawFuncPtr() && m_bInsFrame ) + { + StopInsFrame(); + rSh.Edit(); + } + + UpdatePointer( aDocPos ); + + if( !rSh.IsSelFrameMode() && + !GetView().GetViewFrame().GetDispatcher()->IsLocked() ) + { + // Test if there is a draw object at that position and if it should be selected. + bool bShould = rSh.ShouldObjectBeSelected(aDocPos); + + if(bShould) + { + m_rView.NoRotate(); + rSh.HideCursor(); + + bool bUnLockView = !rSh.IsViewLocked(); + rSh.LockView( true ); + bool bSelObj = rSh.SelectObj( aDocPos ); + if( bUnLockView ) + rSh.LockView( false ); + + if( bSelObj ) + { + // in case the frame was deselected in the macro + // just the cursor has to be displayed again. + if( FrameTypeFlags::NONE == rSh.GetSelFrameType() ) + rSh.ShowCursor(); + else + { + if (rSh.IsFrameSelected() && m_rView.GetDrawFuncPtr()) + { + m_rView.GetDrawFuncPtr()->Deactivate(); + m_rView.SetDrawFuncPtr(nullptr); + m_rView.LeaveDrawCreate(); + m_rView.AttrChangedNotify(nullptr); + } + + rSh.EnterSelFrameMode( &aDocPos ); + g_bFrameDrag = true; + UpdatePointer( aDocPos ); + return; + } + } + + if (!m_rView.GetDrawFuncPtr()) + rSh.ShowCursor(); + } + } + else if ( rSh.IsSelFrameMode() && + (m_aActHitType == SdrHitKind::NONE || + !bIsInsideSelectedObj)) + { + m_rView.NoRotate(); + bool bUnLockView = !rSh.IsViewLocked(); + rSh.LockView( true ); + + if ( rSh.IsSelFrameMode() ) + { + rSh.UnSelectFrame(); + rSh.LeaveSelFrameMode(); + m_rView.AttrChangedNotify(nullptr); + } + + bool bSelObj = rSh.SelectObj( aDocPos, 0/*nFlag*/ ); + if( bUnLockView ) + rSh.LockView( false ); + + if( !bSelObj ) + { + // move cursor here so that it is not drawn in the + // frame at first; ShowCursor() happens in LeaveSelFrameMode() + g_bValidCursorPos = !(CRSR_POSCHG & rSh.CallSetCursor(&aDocPos, false)); + rSh.LeaveSelFrameMode(); + m_rView.LeaveDrawCreate(); + m_rView.AttrChangedNotify(nullptr); + } + else + { + rSh.HideCursor(); + rSh.EnterSelFrameMode( &aDocPos ); + rSh.SelFlyGrabCursor(); + rSh.MakeSelVisible(); + g_bFrameDrag = true; + if( rSh.IsFrameSelected() && + m_rView.GetDrawFuncPtr() ) + { + m_rView.GetDrawFuncPtr()->Deactivate(); + m_rView.SetDrawFuncPtr(nullptr); + m_rView.LeaveDrawCreate(); + m_rView.AttrChangedNotify(nullptr); + } + UpdatePointer( aDocPos ); + } + } + else if ( rSh.IsSelFrameMode() && bIsInsideSelectedObj ) + { + // Object at the mouse cursor is already selected - do nothing + return; + } + + if ( rSh.IsGCAttr() ) + { + rSh.GCAttr(); + rSh.ClearGCAttr(); + } + + bool bOverSelect = rSh.TestCurrPam( aDocPos ); + bool bOverURLGrf = false; + if( !bOverSelect ) + bOverURLGrf = bOverSelect = nullptr != rSh.IsURLGrfAtPos( aDocPos ); + + if ( !bOverSelect ) + { + // create only temporary move context because otherwise + // the query against the content form doesn't work!!! + SwMvContext aMvContext( &rSh ); + rSh.CallSetCursor(&aDocPos, false); + } + if( !bOverURLGrf ) + { + const SelectionType nSelType = rSh.GetSelectionType(); + if( nSelType == SelectionType::Ole || + nSelType == SelectionType::Graphic ) + { + SwMvContext aMvContext( &rSh ); + if( !rSh.IsFrameSelected() ) + rSh.GotoNextFly(); + rSh.EnterSelFrameMode(); + } + } +} + +static SfxShell* lcl_GetTextShellFromDispatcher( SwView const & rView ) +{ + // determine Shell + SfxShell* pShell; + SfxDispatcher* pDispatcher = rView.GetViewFrame().GetDispatcher(); + for(sal_uInt16 i = 0; true; ++i ) + { + pShell = pDispatcher->GetShell( i ); + if( !pShell || dynamic_cast< const SwTextShell *>( pShell ) != nullptr ) + break; + } + return pShell; +} + +IMPL_LINK_NOARG(SwEditWin, KeyInputFlushHandler, Timer *, void) +{ + FlushInBuffer(); +} + +void SwEditWin::InitStaticData() +{ + s_pQuickHlpData = new QuickHelpData(); +} + +void SwEditWin::FinitStaticData() +{ + delete s_pQuickHlpData; +} +/* i#3370 - remove quick help to prevent saving + * of autocorrection suggestions */ +void SwEditWin::StopQuickHelp() +{ + if( HasFocus() && s_pQuickHlpData && s_pQuickHlpData->m_bIsDisplayed ) + s_pQuickHlpData->Stop( m_rView.GetWrtShell() ); +} + +IMPL_LINK_NOARG(SwEditWin, TemplateTimerHdl, Timer *, void) +{ + SetApplyTemplate(SwApplyTemplate()); +} + +void SwEditWin::SetChainMode( bool bOn ) +{ + if ( !m_bChainMode ) + StopInsFrame(); + + m_pUserMarker.reset(); + + m_bChainMode = bOn; + + static sal_uInt16 aInva[] = + { + FN_FRAME_CHAIN, FN_FRAME_UNCHAIN, 0 + }; + m_rView.GetViewFrame().GetBindings().Invalidate(aInva); +} + +uno::Reference< css::accessibility::XAccessible > SwEditWin::CreateAccessible() +{ +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + SolarMutexGuard aGuard; // this should have happened already!!! + SwWrtShell *pSh = m_rView.GetWrtShellPtr(); + OSL_ENSURE( pSh, "no writer shell, no accessible object" ); + uno::Reference< + css::accessibility::XAccessible > xAcc; + if( pSh ) + xAcc = pSh->CreateAccessible(); + + return xAcc; +#else + return nullptr; +#endif +} + +void QuickHelpData::Move( QuickHelpData& rCpy ) +{ + m_aHelpStrings.clear(); + m_aHelpStrings.swap( rCpy.m_aHelpStrings ); + + m_bIsDisplayed = rCpy.m_bIsDisplayed; + nCurArrPos = rCpy.nCurArrPos; + m_bAppendSpace = rCpy.m_bAppendSpace; + m_bIsTip = rCpy.m_bIsTip; + m_bIsAutoText = rCpy.m_bIsAutoText; +} + +void QuickHelpData::ClearContent() +{ + nCurArrPos = nNoPos; + m_bIsDisplayed = m_bAppendSpace = false; + nTipId = nullptr; + m_aHelpStrings.clear(); + m_bIsTip = true; + m_bIsAutoText = true; +} + +void QuickHelpData::Start(SwWrtShell& rSh, const bool bRestart) +{ + if (bRestart) + { + nCurArrPos = 0; + } + m_bIsDisplayed = true; + + vcl::Window& rWin = rSh.GetView().GetEditWin(); + if( m_bIsTip ) + { + Point aPt( rWin.OutputToScreenPixel( rWin.LogicToPixel( + rSh.GetCharRect().Pos() ))); + aPt.AdjustY( -3 ); + nTipId = Help::ShowPopover(&rWin, tools::Rectangle( aPt, Size( 1, 1 )), + CurStr(), + QuickHelpFlags::Left | QuickHelpFlags::Bottom); + } + else + { + OUString sStr(CurStr()); + sStr = sStr.copy(CurLen()); + sal_uInt16 nL = sStr.getLength(); + const ExtTextInputAttr nVal = ExtTextInputAttr::DottedUnderline | + ExtTextInputAttr::Highlight; + const std::vector<ExtTextInputAttr> aAttrs( nL, nVal ); + CommandExtTextInputData aCETID( sStr, aAttrs.data(), nL, + 0, false ); + + //fdo#33092. If the current input language is the default + //language that text would appear in if typed, then don't + //force a language on for the ExtTextInput. + LanguageType eInputLanguage = rWin.GetInputLanguage(); + if (lcl_isNonDefaultLanguage(eInputLanguage, + rSh.GetView(), sStr) == INVALID_HINT) + { + eInputLanguage = LANGUAGE_DONTKNOW; + } + + rSh.CreateExtTextInput(eInputLanguage); + rSh.SetExtTextInputData( aCETID ); + } +} + +void QuickHelpData::Stop( SwWrtShell& rSh ) +{ + if( !m_bIsTip ) + rSh.DeleteExtTextInput( false ); + else if( nTipId ) + { + vcl::Window& rWin = rSh.GetView().GetEditWin(); + Help::HidePopover(&rWin, nTipId); + } + ClearContent(); +} + +void QuickHelpData::FillStrArr( SwWrtShell const & rSh, const OUString& rWord ) +{ + enum Capitalization { CASE_LOWER, CASE_UPPER, CASE_SENTENCE, CASE_OTHER }; + + // Determine word capitalization + const CharClass& rCC = GetAppCharClass(); + const OUString sWordLower = rCC.lowercase( rWord ); + Capitalization aWordCase = CASE_OTHER; + if ( !rWord.isEmpty() ) + { + if ( rWord[0] == sWordLower[0] ) + { + if ( rWord == sWordLower ) + aWordCase = CASE_LOWER; + } + else + { + // First character is not lower case i.e. assume upper or title case + OUString sWordSentence = sWordLower.replaceAt( 0, 1, rtl::OUStringChar(rWord[0]) ); + if ( rWord == sWordSentence ) + aWordCase = CASE_SENTENCE; + else + { + if ( rWord == rCC.uppercase( rWord ) ) + aWordCase = CASE_UPPER; + } + } + } + + SwCalendarWrapper& rCalendar = s_getCalendarWrapper(); + rCalendar.LoadDefaultCalendar( rSh.GetCurLang() ); + + // Add matching calendar month and day names + for ( const auto& aNames : { rCalendar.getMonths(), rCalendar.getDays() } ) + { + for ( const auto& rName : aNames ) + { + const OUString& rStr( rName.FullName ); + // Check string longer than word and case insensitive match + if( rStr.getLength() > rWord.getLength() && + rCC.lowercase( rStr, 0, rWord.getLength() ) == sWordLower ) + { + OUString sStr; + + //fdo#61251 if it's an exact match, ensure unchanged replacement + //exists as a candidate + if (rStr.startsWith(rWord)) + m_aHelpStrings.emplace_back(rStr, rWord.getLength()); + else + sStr = rStr; // to be added if no case conversion is performed below + + if ( aWordCase == CASE_LOWER ) + sStr = rCC.lowercase(rStr); + else if ( aWordCase == CASE_SENTENCE ) + sStr = rCC.lowercase(rStr).replaceAt(0, 1, rtl::OUStringChar(rStr[0])); + else if ( aWordCase == CASE_UPPER ) + sStr = rCC.uppercase(rStr); + + if (!sStr.isEmpty()) + m_aHelpStrings.emplace_back(sStr, rWord.getLength()); + } + } + } + + // Add matching current date in ISO 8601 format, for example 2016-01-30 + OUString rStrToday; + + // do not suggest for single years, for example for "2016", + // only for "201" or "2016-..." (to avoid unintentional text + // insertion at line ending, for example typing "30 January 2016") + if (!rWord.isEmpty() && rWord.getLength() != 4 && rWord[0] == '2') + { + rStrToday = utl::toISO8601(DateTime(Date(Date::SYSTEM)).GetUNODateTime()); + if (rStrToday.startsWith(rWord)) + m_aHelpStrings.emplace_back(rStrToday, rWord.getLength()); + } + + // Add matching words from AutoCompleteWord list + const SwAutoCompleteWord& rACList = SwEditShell::GetAutoCompleteWords(); + std::vector<OUString> strings; + + if ( !rACList.GetWordsMatching( rWord, strings ) ) + return; + + for (const OUString & aCompletedString : strings) + { + // when we have a matching current date, avoid to suggest + // other words with the same matching starting characters, + // for example 2016-01-3 instead of 2016-01-30 + if (!rStrToday.isEmpty() && aCompletedString.startsWith(rWord)) + continue; + + OUString sStr; + + //fdo#61251 if it's an exact match, ensure unchanged replacement + //exists as a candidate + if (aCompletedString.startsWith(rWord)) + m_aHelpStrings.emplace_back(aCompletedString, rWord.getLength()); + else + sStr = aCompletedString; // to be added if no case conversion is performed below + + if (aWordCase == CASE_LOWER) + sStr = rCC.lowercase(aCompletedString); + else if (aWordCase == CASE_SENTENCE) + sStr = rCC.lowercase(aCompletedString) + .replaceAt(0, 1, rtl::OUStringChar(aCompletedString[0])); + else if (aWordCase == CASE_UPPER) + sStr = rCC.uppercase(aCompletedString); + + if (!sStr.isEmpty()) + m_aHelpStrings.emplace_back(aCompletedString, rWord.getLength()); + } +} + +namespace { + +class CompareIgnoreCaseAsciiFavorExact +{ + const OUString &m_rOrigWord; +public: + explicit CompareIgnoreCaseAsciiFavorExact(const OUString& rOrigWord) + : m_rOrigWord(rOrigWord) + { + } + + bool operator()(const std::pair<OUString, sal_uInt16>& s1, + const std::pair<OUString, sal_uInt16>& s2) const + { + int nRet = s1.first.compareToIgnoreAsciiCase(s2.first); + if (nRet == 0) + { + //fdo#61251 sort stuff that starts with the exact rOrigWord before + //another ignore-case candidate + int n1StartsWithOrig = s1.first.startsWith(m_rOrigWord) ? 0 : 1; + int n2StartsWithOrig = s2.first.startsWith(m_rOrigWord) ? 0 : 1; + return n1StartsWithOrig < n2StartsWithOrig; + } + return nRet < 0; + } +}; + +struct EqualIgnoreCaseAscii +{ + bool operator()(const std::pair<OUString, sal_uInt16>& s1, + const std::pair<OUString, sal_uInt16>& s2) const + { + return s1.first.equalsIgnoreAsciiCase(s2.first); + } +}; + +} // anonymous namespace + +// TODO Implement an i18n aware sort +void QuickHelpData::SortAndFilter(const OUString &rOrigWord) +{ + std::sort( m_aHelpStrings.begin(), + m_aHelpStrings.end(), + CompareIgnoreCaseAsciiFavorExact(rOrigWord) ); + + const auto& it + = std::unique(m_aHelpStrings.begin(), m_aHelpStrings.end(), EqualIgnoreCaseAscii()); + m_aHelpStrings.erase( it, m_aHelpStrings.end() ); + + nCurArrPos = 0; +} + +// For a given chunk of typed text between 3 and 9 characters long that may start at a word boundary +// or in a whitespace and may include whitespaces, SwEditShell::GetChunkForAutoTextcreates a list of +// possible candidates for long AutoText names. Let's say, we have typed text "lorem ipsum dr f"; +// and the cursor is right after the "f". SwEditShell::GetChunkForAutoText would take " dr f", +// since it's the longest chunk to the left of the cursor no longer than 9 characters, not starting +// in the middle of a word. Then it would create this list from it (in this order, longest first): +// " dr f" +// " dr f" +// "dr f" +// It cannot add "r f", because it starts in the middle of the word "dr"; also it cannot give " f", +// because it's only 2 characters long. +// Now the result of SwEditShell::GetChunkForAutoText is passed here to SwEditWin::ShowAutoText, and +// then to SwGlossaryList::HasLongName, where all existing autotext entries' long names are tested +// if they start with one of the list elements. The matches are sorted according the position of the +// candidate that matched first, then alphabetically inside the group of suggestions for a given +// candidate. Say, if we have these AutoText entry long names: +// "Dr Frodo" +// "Dr Credo" +// "Or Bilbo" +// "dr foo" +// " Dr Fuzz" +// " dr Faust" +// the resulting list would be: +// " Dr Fuzz" -> matches the first (longest) item in the candidates list +// " dr Faust" -> matches the second candidate item +// "Dr Foo" -> first item of the two matching the third candidate; alphabetically sorted +// "Dr Frodo" -> second item of the two matching the third candidate; alphabetically sorted +// Each of the resulting suggestions knows the length of the candidate it replaces, so accepting the +// first suggestion would replace 6 characters before cursor, while tabbing to and accepting the +// last suggestion would replace only 4 characters to the left of cursor. +bool SwEditWin::ShowAutoText(const std::vector<OUString>& rChunkCandidates) +{ + s_pQuickHlpData->ClearContent(); + if (!rChunkCandidates.empty()) + { + SwGlossaryList* pList = ::GetGlossaryList(); + pList->HasLongName(rChunkCandidates, s_pQuickHlpData->m_aHelpStrings); + } + + if (!s_pQuickHlpData->m_aHelpStrings.empty()) + { + s_pQuickHlpData->Start(m_rView.GetWrtShell(), true); + } + return !s_pQuickHlpData->m_aHelpStrings.empty(); +} + +void SwEditWin::ShowAutoCorrectQuickHelp( + const OUString& rWord, SvxAutoCorrect& rACorr ) +{ + if (rWord.isEmpty()) + return; + SwWrtShell& rSh = m_rView.GetWrtShell(); + s_pQuickHlpData->ClearContent(); + + if( s_pQuickHlpData->m_aHelpStrings.empty() && + rACorr.GetSwFlags().bAutoCompleteWords ) + { + s_pQuickHlpData->m_bIsAutoText = false; + s_pQuickHlpData->m_bIsTip = rACorr.GetSwFlags().bAutoCmpltShowAsTip; + + // Get the necessary data to show help text. + s_pQuickHlpData->FillStrArr( rSh, rWord ); + } + + if( !s_pQuickHlpData->m_aHelpStrings.empty() ) + { + s_pQuickHlpData->SortAndFilter(rWord); + s_pQuickHlpData->Start(rSh, true); + } +} + +bool SwEditWin::IsInHeaderFooter( const Point &rDocPt, FrameControlType &rControl ) const +{ + SwWrtShell &rSh = m_rView.GetWrtShell(); + const SwPageFrame* pPageFrame = rSh.GetLayout()->GetPageAtPos( rDocPt ); + + if ( pPageFrame && pPageFrame->IsOverHeaderFooterArea( rDocPt, rControl ) ) + return true; + + if ( rSh.IsShowHeaderFooterSeparator( FrameControlType::Header ) || rSh.IsShowHeaderFooterSeparator( FrameControlType::Footer ) ) + { + SwFrameControlsManager &rMgr = rSh.GetView().GetEditWin().GetFrameControlsManager(); + Point aPoint( LogicToPixel( rDocPt ) ); + + if ( rSh.IsShowHeaderFooterSeparator( FrameControlType::Header ) ) + { + SwFrameControlPtr pControl = rMgr.GetControl( FrameControlType::Header, pPageFrame ); + if ( pControl && pControl->Contains( aPoint ) ) + { + rControl = FrameControlType::Header; + return true; + } + } + + if ( rSh.IsShowHeaderFooterSeparator( FrameControlType::Footer ) ) + { + SwFrameControlPtr pControl = rMgr.GetControl( FrameControlType::Footer, pPageFrame ); + if ( pControl && pControl->Contains( aPoint ) ) + { + rControl = FrameControlType::Footer; + return true; + } + } + } + + return false; +} + +bool SwEditWin::IsOverHeaderFooterFly( const Point& rDocPos, FrameControlType& rControl, bool& bOverFly, bool& bPageAnchored ) const +{ + bool bRet = false; + Point aPt( rDocPos ); + SwWrtShell &rSh = m_rView.GetWrtShell(); + SwPaM aPam( *rSh.GetCurrentShellCursor().GetPoint() ); + rSh.GetLayout()->GetModelPositionForViewPoint( aPam.GetPoint(), aPt, nullptr, true ); + + const SwStartNode* pStartFly = aPam.GetPoint()->GetNode().FindFlyStartNode(); + if ( pStartFly ) + { + bOverFly = true; + SwFrameFormat* pFlyFormat = pStartFly->GetFlyFormat( ); + if ( pFlyFormat ) + { + const SwNode* pAnchorNode = pFlyFormat->GetAnchor( ).GetAnchorNode( ); + if ( pAnchorNode ) + { + bool bInHeader = pAnchorNode->FindHeaderStartNode( ) != nullptr; + bool bInFooter = pAnchorNode->FindFooterStartNode( ) != nullptr; + + bRet = bInHeader || bInFooter; + if ( bInHeader ) + rControl = FrameControlType::Header; + else if ( bInFooter ) + rControl = FrameControlType::Footer; + } + else + bPageAnchored = pFlyFormat->GetAnchor( ).GetAnchorId( ) == RndStdIds::FLY_AT_PAGE; + } + } + else + bOverFly = false; + return bRet; +} + +void SwEditWin::SetUseInputLanguage( bool bNew ) +{ + if ( bNew || m_bUseInputLanguage ) + { + SfxBindings& rBind = GetView().GetViewFrame().GetBindings(); + rBind.Invalidate( SID_ATTR_CHAR_FONT ); + rBind.Invalidate( SID_ATTR_CHAR_FONTHEIGHT ); + } + m_bUseInputLanguage = bNew; +} + +OUString SwEditWin::GetSurroundingText() const +{ + SwWrtShell& rSh = m_rView.GetWrtShell(); + + if (rSh.HasDrawView() && rSh.GetDrawView()->IsTextEdit()) + return rSh.GetDrawView()->GetTextEditOutlinerView()->GetSurroundingText(); + + OUString sReturn; + if( rSh.HasSelection() && !rSh.IsMultiSelection() && rSh.IsSelOnePara() ) + rSh.GetSelectedText( sReturn, ParaBreakType::ToOnlyCR ); + else if( !rSh.HasSelection() ) + { + bool bUnLockView = !rSh.IsViewLocked(); + rSh.LockView(true); + + // store shell state *before* Push + ::std::optional<SwCallLink> aLink(std::in_place, rSh); + rSh.Push(); + + // disable accessible events for internal-only helper cursor + const bool bSendAccessibleEventOld = rSh.IsSendAccessibleCursorEvents(); + rSh.SetSendAccessibleCursorEvents(false); + + // get the sentence around the cursor + rSh.HideCursor(); + rSh.GoStartSentence(); + rSh.SetMark(); + rSh.GoEndSentence(); + rSh.GetSelectedText( sReturn, ParaBreakType::ToOnlyCR ); + + rSh.Pop(SwCursorShell::PopMode::DeleteCurrent, aLink); + rSh.SetSendAccessibleCursorEvents(bSendAccessibleEventOld); + rSh.HideCursor(); + + if (bUnLockView) + rSh.LockView(false); + } + + return sReturn; +} + +Selection SwEditWin::GetSurroundingTextSelection() const +{ + SwWrtShell& rSh = m_rView.GetWrtShell(); + + if (rSh.HasDrawView() && rSh.GetDrawView()->IsTextEdit()) + return rSh.GetDrawView()->GetTextEditOutlinerView()->GetSurroundingTextSelection(); + + Selection aSel(0, 0); + if( rSh.HasSelection() ) + { + OUString sReturn; + rSh.GetSelectedText( sReturn, ParaBreakType::ToOnlyCR ); + aSel = Selection( 0, sReturn.getLength() ); + } + else if (rSh.GetCursor()->GetPoint()->GetNode().GetTextNode()) + { + bool bUnLockView = !rSh.IsViewLocked(); + rSh.LockView(true); + + // Return the position of the visible cursor in the sentence + // around the visible cursor. + TextFrameIndex const nPos(rSh.GetCursorPointAsViewIndex()); + + // store shell state *before* Push + ::std::optional<SwCallLink> aLink(std::in_place, rSh); + rSh.Push(); + + // disable accessible events for internal-only helper cursor + const bool bSendAccessibleEventOld = rSh.IsSendAccessibleCursorEvents(); + rSh.SetSendAccessibleCursorEvents(false); + + rSh.HideCursor(); + rSh.GoStartSentence(); + TextFrameIndex const nStartPos(rSh.GetCursorPointAsViewIndex()); + + rSh.Pop(SwCursorShell::PopMode::DeleteCurrent, aLink); + rSh.SetSendAccessibleCursorEvents(bSendAccessibleEventOld); + rSh.ShowCursor(); + + if (bUnLockView) + rSh.LockView(false); + + aSel = Selection(sal_Int32(nPos - nStartPos), sal_Int32(nPos - nStartPos)); + } + + return aSel; +} + +bool SwEditWin::DeleteSurroundingText(const Selection& rSelection) +{ + SwWrtShell& rSh = m_rView.GetWrtShell(); + + if (rSh.HasDrawView() && rSh.GetDrawView()->IsTextEdit()) + return rSh.GetDrawView()->GetTextEditOutlinerView()->DeleteSurroundingText(rSelection); + + if (rSh.HasSelection()) + return false; + + // rSelection is relative to the start of the sentence, so find that and + // adjust the range by it + rSh.Push(); + + // disable accessible events for internal-only helper cursor + const bool bSendAccessibleEventOld = rSh.IsSendAccessibleCursorEvents(); + rSh.SetSendAccessibleCursorEvents(false); + + rSh.HideCursor(); + rSh.GoStartSentence(); + TextFrameIndex const nStartPos(rSh.GetCursorPointAsViewIndex()); + + rSh.Pop(SwCursorShell::PopMode::DeleteCurrent); + rSh.SetSendAccessibleCursorEvents(bSendAccessibleEventOld); + rSh.ShowCursor(); + + if (rSh.SelectTextView(nStartPos + TextFrameIndex(rSelection.Min()), nStartPos + TextFrameIndex(rSelection.Max()))) + { + rSh.Delete(false); + return true; + } + + return false; +} + +void SwEditWin::LogicInvalidate(const tools::Rectangle* pRectangle) +{ + SfxLokHelper::notifyInvalidation(&m_rView, pRectangle); +} + +void SwEditWin::SetCursorTwipPosition(const Point& rPosition, bool bPoint, bool bClearMark) +{ + if (SdrView* pSdrView = m_rView.GetWrtShell().GetDrawView()) + { + // Editing shape text, then route the call to editeng. + if (pSdrView->GetTextEditObject()) + { + EditView& rEditView = pSdrView->GetTextEditOutlinerView()->GetEditView(); + rEditView.SetCursorLogicPosition(rPosition, bPoint, bClearMark); + return; + } + } + + if (m_rView.GetPostItMgr()) + { + if (sw::annotation::SwAnnotationWin* pWin = m_rView.GetPostItMgr()->GetActiveSidebarWin()) + { + // Editing postit text. + pWin->SetCursorLogicPosition(rPosition, bPoint, bClearMark); + return; + } + } + + // Not an SwWrtShell, as that would make SwCursorShell::GetCursor() inaccessible. + SwEditShell& rShell = m_rView.GetWrtShell(); + + bool bCreateSelection = false; + { + SwMvContext aMvContext(&rShell); + if (bClearMark) + rShell.ClearMark(); + else + bCreateSelection = !rShell.HasMark(); + + if (bCreateSelection) + m_rView.GetWrtShell().SttSelect(); + + // If the mark is to be updated, then exchange the point and mark before + // and after, as we can't easily set the mark. + if (!bPoint) + rShell.getShellCursor(/*bBlock=*/false)->Exchange(); + rShell.SetCursor(rPosition); + if (!bPoint) + rShell.getShellCursor(/*bBlock=*/false)->Exchange(); + } + + if (bCreateSelection) + m_rView.GetWrtShell().EndSelect(); +} + +void SwEditWin::SetGraphicTwipPosition(bool bStart, const Point& rPosition) +{ + if (bStart) + { + MouseEvent aClickEvent(rPosition, 1, MouseEventModifiers::SIMPLECLICK, MOUSE_LEFT); + MouseButtonDown(aClickEvent); + MouseEvent aMoveEvent(Point(rPosition.getX() + MIN_MOVE + 1, rPosition.getY()), 0, MouseEventModifiers::SIMPLEMOVE, MOUSE_LEFT); + MouseMove(aMoveEvent); + } + else + { + MouseEvent aMoveEvent(Point(rPosition.getX() - MIN_MOVE - 1, rPosition.getY()), 0, MouseEventModifiers::SIMPLEMOVE, MOUSE_LEFT); + MouseMove(aMoveEvent); + MouseEvent aClickEvent(rPosition, 1, MouseEventModifiers::SIMPLECLICK, MOUSE_LEFT); + MouseButtonUp(aClickEvent); + } +} + +SwFrameControlsManager& SwEditWin::GetFrameControlsManager() +{ + return *m_pFrameControlsManager; +} + +void SwEditWin::ToggleOutlineContentVisibility(const size_t nOutlinePos, const bool bSubs) +{ + // bSubs purpose is to set all sub level outline content to the same visibility as + // nOutlinePos outline content visibility is toggled. It is only applicable when not treating + // sub outline levels as content. + SwWrtShell& rSh = GetView().GetWrtShell(); + + if (GetView().GetDrawView()->IsTextEdit()) + rSh.EndTextEdit(); + if (GetView().IsDrawMode()) + GetView().LeaveDrawCreate(); + rSh.EnterStdMode(); + + if (!bSubs || rSh.GetViewOptions()->IsTreatSubOutlineLevelsAsContent()) + { + SwNode* pNode = rSh.GetNodes().GetOutLineNds()[nOutlinePos]; + bool bVisible = true; + pNode->GetTextNode()->GetAttrOutlineContentVisible(bVisible); + pNode->GetTextNode()->SetAttrOutlineContentVisible(!bVisible); + } + else if (bSubs) + { + // also toggle sub levels to the same content visibility + SwOutlineNodes::size_type nPos = nOutlinePos; + SwOutlineNodes::size_type nOutlineNodesCount + = rSh.getIDocumentOutlineNodesAccess()->getOutlineNodesCount(); + int nLevel = rSh.getIDocumentOutlineNodesAccess()->getOutlineLevel(nOutlinePos); + bool bVisible = rSh.IsOutlineContentVisible(nOutlinePos); + do + { + if (rSh.IsOutlineContentVisible(nPos) == bVisible) + rSh.GetNodes().GetOutLineNds()[nPos]->GetTextNode()->SetAttrOutlineContentVisible(!bVisible); + } while (++nPos < nOutlineNodesCount + && rSh.getIDocumentOutlineNodesAccess()->getOutlineLevel(nPos) > nLevel); + } + + rSh.InvalidateOutlineContentVisibility(); + rSh.GotoOutline(nOutlinePos); + rSh.SetModified(); + GetView().GetDocShell()->Broadcast(SfxHint(SfxHintId::DocChanged)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |