summaryrefslogtreecommitdiffstats
path: root/sw/source/uibase/docvw/edtwin.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sw/source/uibase/docvw/edtwin.cxx')
-rw-r--r--sw/source/uibase/docvw/edtwin.cxx6897
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: */