/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "romenu.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 at evaluated in Up. static bool g_bHoldSelection = false; bool g_bFrameDrag = false; static bool g_bValidCursorPos = false; static bool g_bModePushed = false; bool g_bDDTimerStarted = false; bool g_bFlushCharBuffer = false; bool g_bDDINetAttr = false; static SdrHdlKind g_eSdrMoveHdl = SdrHdlKind::User; QuickHelpData* SwEditWin::m_pQuickHlpData = nullptr; long SwEditWin::m_nDDStartPosY = 0; long SwEditWin::m_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* pHdl; Point const aHdlPos; Point aLastPos; bool bTopRightHandle; public: explicit SwAnchorMarker( SdrHdl* pH ) : pHdl( pH ) , aHdlPos( pH->GetPos() ) , aLastPos( pH->GetPos() ) , bTopRightHandle( pH->GetKind() == SdrHdlKind::Anchor_TR ) {} const Point& GetLastPos() const { return aLastPos; } void SetLastPos( const Point& rNew ) { aLastPos = rNew; } void SetPos( const Point& rNew ) { pHdl->SetPos( rNew ); } const Point& GetHdlPos() { return aHdlPos; } SdrHdl* GetHdl() const { return pHdl; } void ChgHdl( SdrHdl* pNew ) { pHdl = pNew; if ( pHdl ) { bTopRightHandle = (pHdl->GetKind() == SdrHdlKind::Anchor_TR); } } const Point GetPosForHitTest( const OutputDevice& rOut ) { Point aHitTestPos( pHdl->GetPos() ); aHitTestPos = rOut.LogicToPixel( aHitTestPos ); if ( 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> m_aHelpStrings; /// Index of the current help string. sal_uInt16 nCurArrPos; static constexpr sal_uInt16 nNoPos = std::numeric_limits::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 * an 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 ) { 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( 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; SET_CURR_SHELL(&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 ) { SvtSecurityOptions aSecOpts; const bool bSecureOption = aSecOpts.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; } } } } // 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; } 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().IsInside( 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 long coMinLen = 100; #else const long coMinLen = 50; #endif 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( OBJ_NONE, false ); m_bInsFrame = true; m_nInsFrameColCount = nCols; } void SwEditWin::StdDrawMode( SdrObjKind eSdrObjectKind, bool bObjSelect ) { SetSdrDrawMode( eSdrObjectKind ); if (bObjSelect) m_rView.SetDrawFuncPtr(o3tl::make_unique( &m_rView.GetWrtShell(), this, &m_rView )); else m_rView.SetDrawFuncPtr(o3tl::make_unique( &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 ) { const SvtCTLOptions& rCTLOptions = SW_MOD()->GetCTLOptions(); if ( !rCTLOptions.IsCTLFontEnabled() || !rCTLOptions.IsCTLSequenceChecking() ) return false; if ( 0 == rCursor.Start()->nContent.GetIndex() ) /* first char needs not to be checked */ return false; SwBreakIt *pBreakIter = SwBreakIt::Get(); uno::Reference < i18n::XBreakIterator > xBI = pBreakIter->GetBreakIter(); assert(xBI.is()); 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(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_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()->nContent = 0; const OUString aOldText( rSh.GetCursor()->GetText() ); const sal_Int32 nOldLen = aOldText.getLength(); SvtCTLOptions& rCTLOptions = SW_MOD()->GetCTLOptions(); sal_Int32 nExpandSelection = 0; if (nOldLen > 0) { sal_Int32 nTmpPos = nOldLen; sal_Int16 nCheckMode = rCTLOptions.IsCTLSequenceCheckingRestricted() ? i18n::InputSequenceCheckMode::STRICT : i18n::InputSequenceCheckMode::BASIC; OUString aNewText( aOldText ); if (rCTLOptions.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 += OUStringLiteral1( 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()->nContent.GetIndex(); OSL_ENSURE( nCursorStartPos >= nExpandSelection, "cannot expand selection as specified!!" ); if (nExpandSelection && nCursorStartPos >= nExpandSelection) { if (!rCursor.HasMark()) rCursor.SetMark(); rCursor.Start()->nContent -= 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(); g_bFlushCharBuffer = false; } #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; SfxItemSet aSet( rSh.GetAttrPool(), svl::Items< 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>{}); 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(aSnap.Width()) / nDiv ) ); nDiv = rSh.GetViewOptions()->GetDivisionY(); if ( nDiv > 0 ) aSnap.setHeight( std::max( sal_uLong(1), static_cast(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 SwPosition* pToCharContentPos = aSet.Get(RES_ANCHOR).GetContentAnchor(); rSh.CalcBoundRect( aBoundRect, eAnchorId, text::RelOrientation::FRAME, aVert.GetRelationOrient(), pToCharContentPos, bFollowTextFlow, false, &aRefPoint ); } long nLeft = std::min( aTmp.Left() - aBoundRect.Left(), aSnap.Width() ); long nRight = std::min( aBoundRect.Right() - aTmp.Right(), aSnap.Width() ); long nUp = std::min( aTmp.Top() - aBoundRect.Top(), aSnap.Height() ); 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(); SAL_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(); SAL_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 )) { 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(); long nX = 0; 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(aSnap.Width()) / nDiv ) ); nDiv = rSh.GetViewOptions()->GetDivisionY(); if ( nDiv > 0 ) aSnap.setHeight( std::max( sal_uLong(1), static_cast(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(pHdl && (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(rDragStat).SetNoSnap(); if(bWasSnapEnabled) pSdrView->SetSnapEnabled(false); pSdrView->MovAction(aEndPoint); pSdrView->EndDragObj(); rSh.SetModified(); // restore snap if(!bWasNoSnap) const_cast(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; } } if( rKEvt.GetKeyCode().GetCode() == KEY_ESCAPE && m_pApplyTempl && m_pApplyTempl->m_pFormatClipboard ) { m_pApplyTempl->m_pFormatClipboard->Erase(); SetApplyTemplate(SwApplyTemplate()); m_rView.GetViewFrame()->GetBindings().Invalidate(SID_FORMATPAINTBRUSH); } else if ( rKEvt.GetKeyCode().GetCode() == KEY_ESCAPE && 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(); 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( m_pQuickHlpData->m_bIsDisplayed ) { aTmpQHD.Move( *m_pQuickHlpData ); m_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 = m_pQuickHlpData->m_bAppendSpace; m_pQuickHlpData->m_bAppendSpace = false; if ( getenv("SW_DEBUG") && rKEvt.GetKeyCode().GetCode() == KEY_F12 ) { if( rKEvt.GetKeyCode().IsShift()) { GetView().GetDocShell()->GetDoc()->dumpAsXml(); return; } else { SwRootFrame* pLayout = GetView().GetDocShell()->GetWrtShell()->GetLayout(); pLayout->dumpAsXml( ); return; } } KeyEvent aKeyEvent( rKEvt ); // look for vertical mappings if( !bIsDocReadOnly && !rSh.IsSelFrameMode() && !rSh.IsObjSelected() ) { // must changed from switch to if, because the Linux // compiler has problem with the code. Has to remove if the new general // handler exist. sal_uInt16 nKey = rKEvt.GetKeyCode().GetCode(); 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; } const SwFrameFormat* pFlyFormat = rSh.GetFlyFrameFormat(); 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(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, ColTopBig, ColBottomBig, ColTopSmall, 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 OSL_DEBUG_LEVEL > 1 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // for switching cursor behaviour in ReadOnly regions if( 0x7210 == rKeyCode.GetFullCode() ) rSh.SetReadOnlyAvailable( !rSh.IsReadOnlyAvailable() ); else //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! #endif 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: 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; 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) { 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()) { std::unique_ptr xBuilder(Application::CreateBuilder(GetFrameWeld(), "modules/swriter/ui/inforeadonlydialog.ui")); std::unique_ptr xInfo(xBuilder->weld_message_dialog("InfoReadonlyDialog")); xInfo->run(); eKeyState = SwKeyState::End; } break; case KEY_RETURN: { if ( !rSh.HasReadonlySel() && !rSh.CursorInsideInputField() ) { 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 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 . // Otherwise method // should only change the state of // the current paragraph depending of the key. // On it is set to , // on it is set to . // Thus, assure that method // 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()) { std::unique_ptr xBuilder(Application::CreateBuilder(GetFrameWeld(), "modules/swriter/ui/inforeadonlydialog.ui")); std::unique_ptr xInfo(xBuilder->weld_message_dialog("InfoReadonlyDialog")); xInfo->run(); eKeyState = SwKeyState::End; } break; case KEY_RIGHT: { eFlyState = SwKeyState::Fly_Change; nDir = MOVE_RIGHT_BIG; goto KEYINPUT_CHECKTABLE_INSDEL; } case KEY_TAB: { if (rSh.IsFormProtected() || 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 ( !rSh.IsMultiSelection() && rSh.IsFirstOfNumRuleAtCursorPos() && numfunc::ChangeIndentOnTabAtFirstPosOfFirstListItem() ) eKeyState = SwKeyState::NumIndentInc; else eKeyState = SwKeyState::NumDown; } else if ( rSh.GetTableFormat() ) { if( rSh.HasSelection() || rSh.HasReadonlySel() ) eKeyState = SwKeyState::NextCell; else { eKeyState = SwKeyState::CheckAutoCorrect; eNextKeyState = SwKeyState::NextCell; } } else if ( rSh.GetSelectionType() & (SelectionType::Graphic | SelectionType::Frame | SelectionType::Ole | SelectionType::DrawObject | SelectionType::DbForm)) eKeyState = SwKeyState::NextObject; 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: { if (rSh.IsFormProtected() || 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() ) { if ( !rSh.IsMultiSelection() && rSh.IsFirstOfNumRuleAtCursorPos() && numfunc::ChangeIndentOnTabAtFirstPosOfFirstListItem() ) eKeyState = SwKeyState::NumIndentDec; else eKeyState = SwKeyState::NumUp; } else if ( rSh.GetTableFormat() ) { if( rSh.HasSelection() || rSh.HasReadonlySel() ) eKeyState = SwKeyState::PrevCell; else { eKeyState = SwKeyState::CheckAutoCorrect; eNextKeyState = SwKeyState::PrevCell; } } else if ( rSh.GetSelectionType() & (SelectionType::Graphic | SelectionType::Frame | SelectionType::Ole | SelectionType::DrawObject | SelectionType::DbForm)) eKeyState = SwKeyState::PrevObject; 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 { eKeyState = SwKeyState::InsTab; } } 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; } } 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 { SfxItemSet aSet(rSh.GetAttrPool(), svl::Items{}); 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(rItem)); } } } break; } } break; case SwKeyState::EnterCharCell: { eKeyState = SwKeyState::KeyToView; switch ( rKeyCode.GetModifier() | rKeyCode.GetCode() ) { case KEY_RIGHT | KEY_MOD2: rSh.Right( CRSR_SKIP_CHARS, false, 1, false ); eKeyState = SwKeyState::End; FlushInBuffer(); break; case KEY_LEFT | KEY_MOD2: rSh.Left( CRSR_SKIP_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) { auto xInfo(std::make_shared(GetFrameWeld(), "modules/swriter/ui/inforeadonlydialog.ui", "InfoReadonlyDialog")); weld::DialogController::runAsync(xInfo, [](int) {}); 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 ( dynamic_cast< const SwDrawTextShell *>( m_rView.GetCurShell() ) != nullptr ) static_cast(m_rView.GetCurShell())->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 (dynamic_cast< const SwDrawTextShell *>( m_rView.GetCurShell() ) != nullptr ) static_cast(m_rView.GetCurShell())->Init(); } } eKeyState = SwKeyState::End; } break; case SwKeyState::EnterDrawHandleMode: { const SdrHdlList& rHdlList = rSh.GetDrawView()->GetHdlList(); bool bForward(!aKeyEvent.GetKeyCode().IsShift()); const_cast(rHdlList).TravelFocusHdl(bForward); eKeyState = SwKeyState::End; } break; case SwKeyState::InsTab: if( dynamic_cast( &m_rView) != nullptr) // no Tab for WebView { // then it should be passed along Window::KeyInput( aKeyEvent ); eKeyState = SwKeyState::End; break; } aCh = '\t'; SAL_FALLTHROUGH; case SwKeyState::InsChar: 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 ) && '\"' != 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(); g_bFlushCharBuffer = Application::AnyInput( VclInputFlags::KEYBOARD ); bFlushBuffer = !g_bFlushCharBuffer; if( g_bFlushCharBuffer ) m_aKeyInputFlushTimer.Start(); } eKeyState = SwKeyState::End; } else { auto xInfo(std::make_shared(GetFrameWeld(), "modules/swriter/ui/inforeadonlydialog.ui", "InfoReadonlyDialog")); weld::DialogController::runAsync(xInfo, [](int) {}); eKeyState = SwKeyState::End; } break; case SwKeyState::CheckAutoCorrect: { if( pACorr && pACfg->IsAutoFormatByInput() && pACorr->IsAutoCorrFlag( ACFlags::CapitalStartSentence | ACFlags::CapitalStartWord | ACFlags::ChgOrdinalNumber | ACFlags::ChgToEnEmDash | ACFlags::SetINetAttr | ACFlags::Autocorrect ) && !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: { ::sw::mark::IFieldmark const * const pFieldmark = rSh.GetFieldmarkAfter(); if(pFieldmark) rSh.GotoFieldmark(pFieldmark); } break; case SwKeyState::GotoPrevFieldMark: { ::sw::mark::IFieldmark const * const pFieldmark = rSh.GetFieldmarkBefore(); if( pFieldmark ) rSh.GotoFieldmark(pFieldmark); } 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); m_pQuickHlpData->m_bAppendSpace = true; } } else { sFnd = sFnd.copy(aTmpQHD.CurLen()); rSh.Insert( sFnd ); m_pQuickHlpData->m_bAppendSpace = !pACorr || pACorr->GetSwFlags().bAutoCmpltAppendBlanc; } rSh.EndUndo( SwUndoId::END ); } break; case SwKeyState::NextPrevGlossary: m_pQuickHlpData->Move( aTmpQHD ); m_pQuickHlpData->Start(rSh, false); break; case SwKeyState::EditFormula: { const sal_uInt16 nId = SwInputChild::GetChildWindowId(); SfxViewFrame* pVFrame = GetView().GetViewFrame(); pVFrame->ToggleChildWindow( nId ); SwInputChild* pChildWin = static_cast(pVFrame-> 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( &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; } } } // in case the buffered characters are inserted if( bFlushBuffer && !m_aInBuffer.isEmpty() ) { // bFlushCharBuffer was not resetted here // why not? bool bSave = g_bFlushCharBuffer; FlushInBuffer(); g_bFlushCharBuffer = bSave; // 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(GetView().GetViewFrame()->GetChildWindow(SwWordCountWrapper::GetChildWindowId())); if( pWrdCnt ) pWrdCnt->UpdateCounts(); } /** * MouseEvents */ void SwEditWin::RstMBDownFlags() { // 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 contex 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 && PopupMenu::IsInExecute() ) return; MouseEvent rMEvt(_rMEvt); if (m_rView.GetPostItMgr()->IsHit(rMEvt.GetPosPixel())) return; if (comphelper::LibreOfficeKit::isActive()) { if (vcl::Window* pWindow = m_rView.GetPostItMgr()->IsHitSidebarWindow(rMEvt.GetPosPixel())) { pWindow->MouseButtonDown(rMEvt); return; } } m_rView.GetPostItMgr()->SetActiveSidebarWin(nullptr); GrabFocus(); //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() ); } m_bWasShdwCursor = nullptr != m_pShadCursor; m_pShadCursor.reset(); const Point aDocPos( PixelToLogic( rMEvt.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 ); // 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 == Header ) bActive = pFormat->GetHeader().IsActive(); else bActive = pFormat->GetFooter().IsActive(); } if ( !bActive ) { // When in Hide-Whitespace mode, we don't want header // and footer controls. if (!rSh.GetViewOptions()->IsHideWhitespaceMode()) { SwPaM aPam(*rSh.GetCurrentShellCursor().GetPoint()); const bool bWasInHeader = aPam.GetPoint()->nNode.GetNode().FindHeaderStartNode() != nullptr; const bool bWasInFooter = aPam.GetPoint()->nNode.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 == Header) && !(bWasInFooter && eControl == Footer)) { rSh.SetShowHeaderFooterSeparator(eControl, !rSh.IsShowHeaderFooterSeparator(eControl)); // Repaint everything Invalidate(); } } } 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( Header, eControl == Header ); rSh.SetShowHeaderFooterSeparator( Footer, eControl == Footer ); if ( !rSh.IsHeaderFooterEdit() ) { rSh.ToggleHeaderFooterEdit(); // Repaint everything rSh.GetWin()->Invalidate(); } } } else { if ( rSh.IsHeaderFooterEdit( ) ) rSh.ToggleHeaderFooterEdit( ); else { // Make sure that the separators are hidden rSh.SetShowHeaderFooterSeparator( Header, false ); rSh.SetShowHeaderFooterSeparator( Footer, false ); // Repaint everything // FIXME fdo#67358 for unknown reasons this causes painting // problems when resizing table columns, so disable it // rSh.GetWin()->Invalidate(); } // 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(rSh.GetFlyFrameFormat()); if ( rSh.Chainable( aDummy, *pFormat, aDocPos ) == SwChainRet::OK ) rSh.Chain( *pFormat, aDocPos ); UpdatePointer( aDocPos, rMEvt.GetModifier() ); return; } // After GrabFocus a shell should be pushed. That should actually // work but in practice ... m_rView.SelectShellForDrop(); bool bCallBase = true; if( m_pQuickHlpData->m_bIsDisplayed ) m_pQuickHlpData->Stop( rSh ); m_pQuickHlpData->m_bAppendSpace = false; if( rSh.FinishOLEObj() ) return; // end InPlace and the click doesn't count anymore SET_CURR_SHELL( &rSh ); SdrView *pSdrView = rSh.GetDrawView(); if ( pSdrView ) { if (pSdrView->MouseButtonDown( rMEvt, this ) ) { rSh.GetView().GetViewFrame()->GetBindings().InvalidateAll(false); return; // SdrView's event evaluated } } m_bIsInMove = false; m_aStartPos = rMEvt.GetPosPixel(); m_aRszMvHdlPt.setX( 0 ); m_aRszMvHdlPt.setY( 0 ); SwTab nMouseTabCol = SwTab::COL_NONE; const bool bTmp = !rSh.IsDrawCreate() && !m_pApplyTempl && !rSh.IsInSelect() && rMEvt.GetClicks() == 1 && MOUSE_LEFT == rMEvt.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( rMEvt, (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( rMEvt, 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 == rMEvt.GetButtons() ) { bool bOnlyText = false; m_bMBPressed = true; g_bNoInterrupt = true; m_nKS_NUMDOWN_Count = 0; CaptureMouse(); // reset cursor position if applicable rSh.ResetCursorStack(); switch ( rMEvt.GetModifier() + rMEvt.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, rMEvt.GetModifier() ); return; } } if ( EnterDrawMode( rMEvt, 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 (rMEvt.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) && !(rMEvt.GetModifier() == KEY_SHIFT && !bHitHandle)) { rSh.EnterSelFrameMode( &aDocPos ); if ( !m_pApplyTempl ) { // only if no position to size was hit. if (!bHitHandle) { StartDDTimer(); SwEditWin::m_nDDStartPosY = aDocPos.Y(); SwEditWin::m_nDDStartPosX = aDocPos.X(); } g_bFrameDrag = true; } g_bNoInterrupt = false; return; } } } } bool bExecHyperlinks = m_rView.GetDocShell()->IsReadOnly(); if ( !bExecHyperlinks ) { SvtSecurityOptions aSecOpts; const bool bSecureOption = aSecOpts.IsOptionSet( SvtSecurityOptions::EOption::CtrlClickHyperlink ); if ( ( bSecureOption && rMEvt.GetModifier() == KEY_MOD1 ) || ( !bSecureOption && rMEvt.GetModifier() != KEY_MOD1 ) ) bExecHyperlinks = true; } // Enhanced selection sal_uInt8 nNumberOfClicks = static_cast< sal_uInt8 >(rMEvt.GetClicks() % 4); if ( 0 == nNumberOfClicks && 0 < rMEvt.GetClicks() ) nNumberOfClicks = 4; bool bExecDrawTextLink = false; switch ( rMEvt.GetModifier() + rMEvt.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, rMEvt.GetModifier() ); SwEditWin::m_nDDStartPosY = aDocPos.Y(); SwEditWin::m_nDDStartPosX = aDocPos.X(); // hit an URL in DrawText object? if (bExecHyperlinks && pSdrView) { SdrViewEvent aVEvt; pSdrView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); if (aVEvt.eEvent == 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() && !bExecDrawTextLink) { // 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, rMEvt.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, rMEvt.GetModifier() ); } return; } else bOnlyText = rSh.IsObjSelectable( aDocPos ); if (!m_rView.GetDrawFuncPtr()) rSh.ShowCursor(); } else bOnlyText = KEY_MOD1 != rMEvt.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, rMEvt.GetModifier() ); return; } else { bool bUnLockView = !rSh.IsViewLocked(); rSh.LockView( true ); sal_uInt8 nFlag = rMEvt.IsShift() ? SW_ADD_SELECT :0; if( rMEvt.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, rMEvt.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 ) ) { /* 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: RstMBDownFlags(); 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: if (rSh.IsSelObjProtected(FlyProtectFlags::Content) == FlyProtectFlags::NONE) { RstMBDownFlags(); rSh.LaunchOLEObj(); } return; case SelectionType::Frame: RstMBDownFlags(); if (!comphelper::LibreOfficeKit::isActive()) { GetView().GetViewFrame()->GetBindings().Execute( FN_FORMAT_FRAME_DLG, nullptr, SfxCallMode::RECORD|SfxCallMode::SLOT); } return; case SelectionType::DrawObject: RstMBDownFlags(); EnterDrawTextMode(aDocPos); if ( dynamic_cast< const SwDrawTextShell *>( m_rView.GetCurShell() ) != nullptr ) static_cast(m_rView.GetCurShell())->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() ) || ( bFootnote = rSh.GetCurFootnote() ) ) ) { RstMBDownFlags(); if( bFootnote ) GetView().GetViewFrame()->GetBindings().Execute( FN_EDIT_FOOTNOTE ); else { sal_uInt16 nTypeId = pField->GetTypeId(); SfxViewFrame* pVFrame = GetView().GetViewFrame(); switch( nTypeId ) { case TYP_POSTITFLD: case TYP_SCRIPTFLD: { // if it's a Readonly region, status has to be enabled sal_uInt16 nSlot = TYP_POSTITFLD == nTypeId ? FN_POSTIT : FN_JAVAEDIT; SfxBoolItem aItem(nSlot, true); pVFrame->GetBindings().SetState(aItem); pVFrame->GetBindings().Execute(nSlot); break; } case TYP_AUTHORITY : pVFrame->GetBindings().Execute(FN_EDIT_AUTH_ENTRY_DLG); break; case TYP_INPUTFLD: case TYP_DROPDOWN: pVFrame->GetBindings().Execute(FN_UPDATE_INPUTFIELDS); break; default: pVFrame->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 == rMEvt.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 ) { RstMBDownFlags(); rSh.getIDocumentMarkAccess()->ClearFieldActivation(); GetView().GetViewFrame()->GetBindings().Execute(SID_FM_CTL_PROPERTIES); 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 == rMEvt.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; } SAL_FALLTHROUGH; } case MOUSE_LEFT + KEY_SHIFT: case MOUSE_LEFT + KEY_SHIFT + KEY_MOD1: { bool bLockView = m_bWasShdwCursor; switch ( rMEvt.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::m_nDDStartPosY = aDocPos.Y(); SwEditWin::m_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 ( !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::m_nDDStartPosY = aDocPos.Y(); SwEditWin::m_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 ( !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::m_nDDStartPosY = aDocPos.Y(); SwEditWin::m_nDDStartPosX = aDocPos.X(); g_bFrameDrag = true; return; } } } else { if ( rSh.IsSelFrameMode() && rSh.IsInsideSelectedObj( aDocPos ) ) { rSh.EnterSelFrameMode( &aDocPos ); SwEditWin::m_nDDStartPosY = aDocPos.Y(); SwEditWin::m_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() || Pointer(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 (rSh.GetCurrentShellCursor().GetPoint()->nContent .GetIndex() != pTextField->GetStart()) { assert(rSh.GetCurrentShellCursor().GetPoint()->nContent .GetIndex() == (pTextField->GetStart() + 1)); rSh.Left( CRSR_SKIP_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 ) { 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.SelectText( 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 == rMEvt.GetButtons() && !rMEvt.GetModifier() && static_cast< sal_uInt8 >(rMEvt.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.SelectText( aFieldAtPos.pFndTextAttr->GetStart() + 1, *(aFieldAtPos.pFndTextAttr->End()) - 1 ); } } if (bCallBase) Window::MouseButtonDown(rMEvt); } 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( m_pShadCursor && 0 != (rMEvt.GetModifier() + rMEvt.GetButtons() ) ) { m_pShadCursor.reset(); } bool bIsDocReadOnly = m_rView.GetDocShell()->IsReadOnly(); SET_CURR_SHELL( &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,this) ) { SetPointer( PointerStyle::Text ); return; // evaluate SdrView's event } const Point aOldPt( rSh.VisArea().Pos() ); const bool bInsWin = rSh.VisArea().IsInside( aDocPt ) || comphelper::LibreOfficeKit::isActive(); 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::m_nDDStartPosX, SwEditWin::m_nDDStartPosY ); aDD = LogicToPixel( aDD ); tools::Rectangle aRect( aDD.X()-3, aDD.Y()-3, aDD.X()+3, aDD.Y()+3 ); if ( !aRect.IsInside( 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( &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 SfxStringItem aCell( SID_TABLE_CELL, OUString() ); rBnd.SetState( aCell ); } } SwTab nMouseTabCol; if( !bIsDocReadOnly && bInsWin && !m_pApplyTempl && !rSh.IsInSelect() ) { if ( SwTab::COL_NONE != (nMouseTabCol = rSh.WhichMouseTabCol( aDocPt ) ) && !rSh.IsObjSelectable( aDocPt ) ) { 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 || !rSh.IsTableMode() ) && !comphelper::LibreOfficeKit::isActive() ) { SetPointer( nPointer ); } return; } else if (rSh.IsNumLabel(aDocPt, RULER_MOUSE_MARGINWIDTH)) { // i#42921 - consider vertical mode SwTextNode* pNodeAtPos = rSh.GetNumRuleNodeAtPos( aDocPt ); const PointerStyle nPointer = SwFEShell::IsVerticalModeAtNdAndPos( *pNodeAtPos, aDocPt ) ? PointerStyle::VSizeBar : PointerStyle::HSizeBar; SetPointer( nPointer ); 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(GetView().GetViewFrame()->GetChildWindow(SwWordCountWrapper::GetChildWindowId())); if (pWrdCnt) pWrdCnt->UpdateCounts(); } SAL_FALLTHROUGH; case MOUSE_LEFT + KEY_SHIFT: case MOUSE_LEFT + KEY_SHIFT + KEY_MOD1: if ( !m_bMBPressed ) break; SAL_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_uInt16 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 + KEY_MOD1 == rMEvt.GetModifier() + rMEvt.GetButtons() ) && 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(pField),false); } else m_rView.GetPostItMgr()->SetShadowState(nullptr,false); } else m_rView.GetPostItMgr()->SetShadowState(nullptr,false); SAL_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(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 a 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() && !GetConnectMetaFile() ) { SwRect aRect; sal_Int16 eOrient; SwFillMode eMode = static_cast(rSh.GetViewOptions()->GetShdwCursorFillMode()); if( rSh.GetShadowCursorPos( aDocPt, eMode, aRect, eOrient )) { if( !m_pShadCursor ) m_pShadCursor.reset( new SwShadowCursor( *this, SwViewOption::GetDirectCursorColor() ) ); if( text::HoriOrientation::RIGHT != eOrient && text::HoriOrientation::CENTER != eOrient ) eOrient = text::HoriOrientation::LEFT; m_pShadCursor->SetPos( aRect.Pos(), aRect.Height(), static_cast< sal_uInt16 >(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(); SET_CURR_SHELL( &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,this ) ) { 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( OBJ_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; } SAL_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_uInt16 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(); } } rSh.EndDrag( &aDocPt, false ); } g_bFrameDrag = false; bCallBase = false; break; } bPopMode = true; SAL_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 ) { SvtSecurityOptions aSecOpts; const bool bSecureOption = aSecOpts.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); 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 ); // 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 ( 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(*fieldBM); rCheckboxFm.SetChecked(!rCheckboxFm.IsChecked()); rCheckboxFm.Invalidate(); rSh.InvalidateWindows( m_rView.GetVisArea() ); } } } else if ( IsAttrAtPos::InetAttr == aContentAtPos.eContentAtPos ) { if (comphelper::LibreOfficeKit::isActive()) { OUString val((*static_cast(aContentAtPos.aFnd.pAttr)).GetValue()); if (val.startsWith("#")) bExecHyperlinks = true; } if ( bExecHyperlinks && aContentAtPos.aFnd.pAttr ) rSh.ClickToINetAttr( *static_cast(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.eEvent == SdrEventKind::ExecuteUrl) { // hit URL field const SvxURLField *pField = aVEvt.pURLField; 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() && !GetConnectMetaFile() && rSh.VisArea().IsInside( aDocPt )) { SwUndoId nLastUndoId(SwUndoId::EMPTY); if (rSh.GetLastUndoInfo(nullptr, & nLastUndoId)) { if (SwUndoId::INS_FROM_SHADOWCRSR == nLastUndoId) { rSh.Undo(); } } SwFillMode eMode = static_cast(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; bool bNoParagraphFormats = true; { 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 = false; } //execute paste pFormatClipboard->Paste( rWrtShell, pPool, bNoCharacterFormats, bNoParagraphFormats ); //if the clipboard is empty after paste remove the ApplyTemplate if(!pFormatClipboard->HasContent()) SetApplyTemplate(SwApplyTemplate()); } 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_COLOR_BACKGROUND_EXT: 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, 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( 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(m_pApplyTempl->eType) ) ); aReq.Done(); } } } } ReleaseMouse(); // Only processed MouseEvents arrive here; only at these this mode can // be resetted. 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()) { // 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 ( dynamic_cast< const SwDrawTextShell *>( m_rView.GetCurShell() ) != nullptr ) static_cast(m_rView.GetCurShell())->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): Window(pParent, WinBits(WB_CLIPCHILDREN | WB_DIALOGCONTROL)), DropTargetHelper( this ), DragSourceHelper( this ), m_eBufferLanguage(LANGUAGE_DONTKNOW), 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(OBJ_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( 200 ); m_aKeyInputFlushTimer.SetInvokeHandler(LINK(this, SwEditWin, KeyInputFlushHandler)); // TemplatePointer for colors should be resetted 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( m_pQuickHlpData->m_bIsDisplayed && m_rView.GetWrtShellPtr() ) m_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::m_nDDStartPosY = aDocPos.Y(); SwEditWin::m_nDDStartPosX = aDocPos.X(); g_bFrameDrag = true; } if( bUnLockView ) rSh.LockView( false ); m_rView.AttrChangedNotify(nullptr); return true; } return false; } bool SwEditWin::IsDrawSelMode() { return IsObjectSelect(); } void SwEditWin::GetFocus() { if ( m_rView.GetPostItMgr()->HasActiveSidebarWin() ) { m_rView.GetPostItMgr()->GrabFocusOnActiveSidebarWin(); } else { m_rView.GotFocus(); Window::GetFocus(); m_rView.GetWrtShell().InvalidateAccessibleFocus(); } } void SwEditWin::LoseFocus() { if (m_rView.GetWrtShellPtr()) m_rView.GetWrtShell().InvalidateAccessibleFocus(); Window::LoseFocus(); if( m_pQuickHlpData && m_pQuickHlpData->m_bIsDisplayed ) m_pQuickHlpData->Stop( m_rView.GetWrtShell() ); } void SwEditWin::Command( const CommandEvent& rCEvt ) { SwWrtShell &rSh = m_rView.GetWrtShell(); if ( !m_rView.GetViewFrame() ) { // If ViewFrame dies shortly, no popup anymore! Window::Command(rCEvt); return; } // 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(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()) { SET_CURR_SHELL( &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(); ScopedVclPtr pMenu; if (GetView().TryContextMenuInterception(aROPopup.GetMenu(), "private:resource/ReadonlyContextMenu", pMenu, aEvent)) { if ( pMenu ) { sal_uInt16 nExecId = static_cast(pMenu.get())->Execute(this, aPixPos); if( !::ExecuteMenuCommand( *static_cast(pMenu.get()), *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: m_pShadCursor.reset(); bCallBase = !m_rView.HandleWheelCommands( rCEvt ); break; case CommandEventId::LongPress: case CommandEventId::Swipe: //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 escpecially 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( m_pQuickHlpData->m_bIsDisplayed ) m_pQuickHlpData->Stop( rSh ); OUString sWord; if( rSh.HasDrawView() && rSh.GetDrawView()->IsTextEdit() ) { bCallBase = false; rSh.GetDrawView()->GetTextEditOutlinerView()->Command( rCEvt ); } else { const CommandExtTextInputData* pData = rCEvt.GetExtTextInputData(); if( pData ) { sWord = pData->GetText(); 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. auto xInfo = std::make_shared( GetFrameWeld(), "modules/swriter/ui/inforeadonlydialog.ui", "InfoReadonlyDialog"); weld::DialogController::runAsync(xInfo, [](sal_Int32 /*nResult*/) {}); } } break; case CommandEventId::CursorPos: // will be handled by the base class break; case CommandEventId::PasteSelection: if( !m_rView.GetDocShell()->IsReadOnly() ) { TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSelection( this )); if( !aDataHelper.GetXTransferable().is() ) break; SotExchangeDest nDropDestination = GetDropDestination( rCEvt.GetMousePosPixel() ); if( !bool(nDropDestination) ) 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 && SW_MOD()->GetCTLOptions().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()->nContent += sal::static_int_cast(pData->GetStart()); rSh.SetMark(); rSh.GetCursor()->GetMark()->nContent += sal::static_int_cast(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 ) { sal_uLong nPosNodeIdx = pCursor->GetPoint()->nNode.GetIndex(); const sal_Int32 nPosIdx = pCursor->GetPoint()->nContent.GetIndex(); sal_uLong nMarkNodeIdx = pCursor->GetMark()->nNode.GetIndex(); const sal_Int32 nMarkIdx = pCursor->GetMark()->nContent.GetIndex(); if( !rSh.GetCursor()->HasMark() ) rSh.GetCursor()->SetMark(); rSh.SttCursorMove(); if( nPosNodeIdx < nMarkNodeIdx ) { rSh.GetCursor()->GetPoint()->nNode = nPosNodeIdx; rSh.GetCursor()->GetPoint()->nContent = nPosIdx; rSh.GetCursor()->GetMark()->nNode = nPosNodeIdx; rSh.GetCursor()->GetMark()->nContent = rSh.GetCursor()->GetContentNode()->Len(); } else if( nPosNodeIdx == nMarkNodeIdx ) { rSh.GetCursor()->GetPoint()->nNode = nPosNodeIdx; rSh.GetCursor()->GetPoint()->nContent = nPosIdx; rSh.GetCursor()->GetMark()->nNode = nMarkNodeIdx; rSh.GetCursor()->GetMark()->nContent = nMarkIdx; } else { rSh.GetCursor()->GetMark()->nNode = nMarkNodeIdx; rSh.GetCursor()->GetMark()->nContent = nMarkIdx; rSh.GetCursor()->GetPoint()->nNode = nMarkNodeIdx; rSh.GetCursor()->GetPoint()->nContent = rSh.GetCursor()->GetContentNode( false )->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.nNode.GetNode(), rPos.nContent.GetIndex() ); if ( pInput ) { const SwPosition& rStart = *pInput->Start(); const SwPosition& rEnd = *pInput->End(); int nSize = 0; for ( SwIndex nIndex = rStart.nContent; nIndex < rEnd.nContent; ++nIndex ) { ++nSize; } 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 aRects(new tools::Rectangle[ nSize ]); int nRectIndex = 0; for ( SwIndex nIndex = rStart.nContent; nIndex < rEnd.nContent; ++nIndex ) { const SwPosition aPos( rStart.nNode, 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: #if OSL_DEBUG_LEVEL > 0 OSL_ENSURE( false, "unknown command." ); #endif 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 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, this ) ) { pSdrView->MouseButtonUp( aMEvt, this ); 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() { m_pQuickHlpData = new QuickHelpData(); } void SwEditWin::FinitStaticData() { delete m_pQuickHlpData; } /* i#3370 - remove quick help to prevent saving * of autocorrection suggestions */ void SwEditWin::StopQuickHelp() { if( HasFocus() && m_pQuickHlpData && m_pQuickHlpData->m_bIsDisplayed ) m_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() { 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; } 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 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; sWordSentence = sWordSentence.replaceAt( 0, 1, OUString(rWord[0]) ); if ( rWord == sWordSentence ) aWordCase = CASE_SENTENCE; else { if ( rWord == rCC.uppercase( rWord ) ) aWordCase = CASE_UPPER; } } } salhelper::SingletonRef* pCalendar = s_getCalendarWrapper(); (*pCalendar)->LoadDefaultCalendar( rSh.GetCurLang() ); // Add matching calendar month and day names uno::Sequence< i18n::CalendarItem2 > aNames( (*pCalendar)->getMonths() ); for ( sal_uInt16 i = 0; i < 2; ++i ) { for ( long n = 0; n < aNames.getLength(); ++n ) { const OUString& rStr( aNames[n].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, OUString(rStr[0])); else if ( aWordCase == CASE_UPPER ) sStr = rCC.uppercase(rStr); if (!sStr.isEmpty()) m_aHelpStrings.emplace_back(sStr, rWord.getLength()); } } // Data for second loop iteration if ( i == 0 ) aNames = (*pCalendar)->getDays(); } // Add matching current date in ISO 8601 format, for example 2016-01-30 OUString rStrToday; if (rWord[0] == '2') { OUStringBuffer rStr(""); rStr.append(sal::static_int_cast< sal_Int32 >((*pCalendar)->getValue(i18n::CalendarFieldIndex::YEAR))).append("-"); sal_Int32 nMonth = sal::static_int_cast< sal_Int32 >((*pCalendar)->getValue(i18n::CalendarFieldIndex::MONTH)+1); sal_Int32 nDay = sal::static_int_cast< sal_Int32 > ((*pCalendar)->getValue(i18n::CalendarFieldIndex::DAY_OF_MONTH)); if (nMonth < 10) rStr.append("0"); rStr.append(nMonth).append("-"); if (nDay < 10) rStr.append("0"); rStrToday = rStr.append(nDay).toString(); // 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.getLength() != 4 && rStrToday.startsWith(rWord)) m_aHelpStrings.emplace_back(rStrToday, rWord.getLength()); } // Add matching words from AutoCompleteWord list const SwAutoCompleteWord& rACList = SwEditShell::GetAutoCompleteWords(); std::vector strings; if ( rACList.GetWordsMatching( rWord, strings ) ) { 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, OUString(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& s1, const std::pair& 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& s1, const std::pair& 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 alhpabetically 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& rChunkCandidates) { m_pQuickHlpData->ClearContent(); if (!rChunkCandidates.empty()) { SwGlossaryList* pList = ::GetGlossaryList(); pList->HasLongName(rChunkCandidates, m_pQuickHlpData->m_aHelpStrings); } if (!m_pQuickHlpData->m_aHelpStrings.empty()) { m_pQuickHlpData->Start(m_rView.GetWrtShell(), true); } return !m_pQuickHlpData->m_aHelpStrings.empty(); } void SwEditWin::ShowAutoCorrectQuickHelp( const OUString& rWord, SvxAutoCorrect& rACorr, bool bFromIME ) { if (rWord.isEmpty()) return; SwWrtShell& rSh = m_rView.GetWrtShell(); m_pQuickHlpData->ClearContent(); if( m_pQuickHlpData->m_aHelpStrings.empty() && rACorr.GetSwFlags().bAutoCompleteWords ) { m_pQuickHlpData->m_bIsAutoText = false; m_pQuickHlpData->m_bIsTip = bFromIME || rACorr.GetSwFlags().bAutoCmpltShowAsTip; // Get the necessary data to show help text. m_pQuickHlpData->FillStrArr( rSh, rWord ); } if( !m_pQuickHlpData->m_aHelpStrings.empty() ) { m_pQuickHlpData->SortAndFilter(rWord); m_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( Header ) || rSh.IsShowHeaderFooterSeparator( Footer ) ) { SwFrameControlsManager &rMgr = rSh.GetView().GetEditWin().GetFrameControlsManager(); Point aPoint( LogicToPixel( rDocPt ) ); if ( rSh.IsShowHeaderFooterSeparator( Header ) ) { SwFrameControlPtr pControl = rMgr.GetControl( Header, pPageFrame ); if ( pControl.get() && pControl->Contains( aPoint ) ) { rControl = Header; return true; } } if ( rSh.IsShowHeaderFooterSeparator( Footer ) ) { SwFrameControlPtr pControl = rMgr.GetControl( Footer, pPageFrame ); if ( pControl.get() && pControl->Contains( aPoint ) ) { rControl = 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()->GetCursorOfst( aPam.GetPoint(), aPt, nullptr, true ); const SwStartNode* pStartFly = aPam.GetPoint()->nNode.GetNode().FindFlyStartNode(); if ( pStartFly ) { bOverFly = true; SwFrameFormat* pFlyFormat = pStartFly->GetFlyFormat( ); if ( pFlyFormat ) { const SwPosition* pAnchor = pFlyFormat->GetAnchor( ).GetContentAnchor( ); if ( pAnchor ) { bool bInHeader = pAnchor->nNode.GetNode( ).FindHeaderStartNode( ) != nullptr; bool bInFooter = pAnchor->nNode.GetNode( ).FindFooterStartNode( ) != nullptr; bRet = bInHeader || bInFooter; if ( bInHeader ) rControl = Header; else if ( bInFooter ) rControl = 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 { OUString sReturn; SwWrtShell& rSh = m_rView.GetWrtShell(); if( rSh.HasSelection() && !rSh.IsMultiSelection() && rSh.IsSelOnePara() ) rSh.GetSelectedText( sReturn, ParaBreakType::ToOnlyCR ); else if( !rSh.HasSelection() ) { SwPosition *pPos = rSh.GetCursor()->GetPoint(); const sal_Int32 nPos = pPos->nContent.GetIndex(); // get the sentence around the cursor rSh.HideCursor(); rSh.GoStartSentence(); rSh.SetMark(); rSh.GoEndSentence(); rSh.GetSelectedText( sReturn, ParaBreakType::ToOnlyCR ); pPos->nContent = nPos; rSh.ClearMark(); rSh.HideCursor(); } return sReturn; } Selection SwEditWin::GetSurroundingTextSelection() const { SwWrtShell& rSh = m_rView.GetWrtShell(); if( rSh.HasSelection() ) { OUString sReturn; rSh.GetSelectedText( sReturn, ParaBreakType::ToOnlyCR ); return Selection( 0, sReturn.getLength() ); } else { // Return the position of the visible cursor in the sentence // around the visible cursor. SwPosition *pPos = rSh.GetCursor()->GetPoint(); const sal_Int32 nPos = pPos->nContent.GetIndex(); rSh.HideCursor(); rSh.GoStartSentence(); const sal_Int32 nStartPos = rSh.GetCursor()->GetPoint()->nContent.GetIndex(); pPos->nContent = nPos; rSh.ClearMark(); rSh.ShowCursor(); return Selection( nPos - nStartPos, nPos - nStartPos ); } } void SwEditWin::LogicInvalidate(const tools::Rectangle* pRectangle) { OString sRectangle; if (!pRectangle) sRectangle = "EMPTY"; else sRectangle = pRectangle->toString(); SfxLokHelper::notifyInvalidation(&m_rView, sRectangle); } void SwEditWin::LogicMouseButtonDown(const MouseEvent& rMouseEvent) { // When we're not doing tiled rendering, then positions must be passed as pixels. assert(comphelper::LibreOfficeKit::isActive()); Point aPoint = GetPointerPosPixel(); SetLastMousePos(rMouseEvent.GetPosPixel()); MouseButtonDown(rMouseEvent); SetPointerPosPixel(aPoint); } void SwEditWin::LogicMouseButtonUp(const MouseEvent& rMouseEvent) { // When we're not doing tiled rendering, then positions must be passed as pixels. assert(comphelper::LibreOfficeKit::isActive()); Point aPoint = GetPointerPosPixel(); SetLastMousePos(rMouseEvent.GetPosPixel()); MouseButtonUp(rMouseEvent); SetPointerPosPixel(aPoint); } void SwEditWin::LogicMouseMove(const MouseEvent& rMouseEvent) { // When we're not doing tiled rendering, then positions must be passed as pixels. assert(comphelper::LibreOfficeKit::isActive()); Point aPoint = GetPointerPosPixel(); SetLastMousePos(rMouseEvent.GetPosPixel()); MouseMove(rMouseEvent); SetPointerPosPixel(aPoint); } 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; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */