diff options
Diffstat (limited to 'sw/source/uibase/wrtsh')
-rw-r--r-- | sw/source/uibase/wrtsh/delete.cxx | 665 | ||||
-rw-r--r-- | sw/source/uibase/wrtsh/move.cxx | 773 | ||||
-rw-r--r-- | sw/source/uibase/wrtsh/navmgr.cxx | 221 | ||||
-rw-r--r-- | sw/source/uibase/wrtsh/select.cxx | 1057 | ||||
-rw-r--r-- | sw/source/uibase/wrtsh/wrtsh1.cxx | 2731 | ||||
-rw-r--r-- | sw/source/uibase/wrtsh/wrtsh2.cxx | 686 | ||||
-rw-r--r-- | sw/source/uibase/wrtsh/wrtsh3.cxx | 392 | ||||
-rw-r--r-- | sw/source/uibase/wrtsh/wrtsh4.cxx | 253 | ||||
-rw-r--r-- | sw/source/uibase/wrtsh/wrtundo.cxx | 151 |
9 files changed, 6929 insertions, 0 deletions
diff --git a/sw/source/uibase/wrtsh/delete.cxx b/sw/source/uibase/wrtsh/delete.cxx new file mode 100644 index 0000000000..e7a09d0165 --- /dev/null +++ b/sw/source/uibase/wrtsh/delete.cxx @@ -0,0 +1,665 @@ +/* -*- 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 <hintids.hxx> +#include <wrtsh.hxx> +#include <swcrsr.hxx> +#include <editeng/lrspitem.hxx> +#include <view.hxx> +#include <drawbase.hxx> +#include <unobaseclass.hxx> +#include <fmtanchr.hxx> +#include <flyfrm.hxx> +#include <ndtxt.hxx> +#include <txtfld.hxx> +#include <docufld.hxx> +#include <IDocumentUndoRedo.hxx> +#include <i18nutil/unicode.hxx> +#include <o3tl/temporary.hxx> +#include <rtl/character.hxx> +#include <osl/diagnose.h> +#include <doc.hxx> +#include <IDocumentRedlineAccess.hxx> + +inline void SwWrtShell::OpenMark() +{ + StartAllAction(); + ResetCursorStack(); + KillPams(); + SetMark(); +} + +inline void SwWrtShell::CloseMark( bool bOkFlag ) +{ + if( bOkFlag ) + UpdateAttr(); + else + SwapPam(); + + ClearMark(); + EndAllAction(); +} + + + +// #i23725# +bool SwWrtShell::TryRemoveIndent() +{ + bool bResult = false; + + SfxItemSetFixed<RES_MARGIN_FIRSTLINE, RES_MARGIN_FIRSTLINE> aAttrSet(GetAttrPool()); + GetCurAttr(aAttrSet); + + SvxFirstLineIndentItem firstLine(aAttrSet.Get(RES_MARGIN_FIRSTLINE)); + SvxTextLeftMarginItem leftMargin(aAttrSet.Get(RES_MARGIN_TEXTLEFT)); + short aOldFirstLineOfst = firstLine.GetTextFirstLineOffset(); + + if (aOldFirstLineOfst > 0) + { + firstLine.SetTextFirstLineOffset(0); + bResult = true; + } + else if (aOldFirstLineOfst < 0) + { + // this used to call SetLeft() but this should be the same result + firstLine.SetTextFirstLineOffset(0); + leftMargin.SetTextLeft(leftMargin.GetTextLeft() + aOldFirstLineOfst); + bResult = true; + } + else if (leftMargin.GetTextLeft() != 0) + { + leftMargin.SetTextLeft(0); + bResult = true; + } + + if (bResult) + { + aAttrSet.Put(firstLine); + aAttrSet.Put(leftMargin); + SetAttrSet(aAttrSet); + } + + return bResult; +} + +/** Description: Erase the line. */ + +void SwWrtShell::DelLine() +{ + SwActContext aActContext(this); + ResetCursorStack(); + // remember the old cursor + Push(); + ClearMark(); + SwCursorShell::LeftMargin(); + SetMark(); + SwCursorShell::RightMargin(); + + bool bRet = Delete(false); + Pop(SwCursorShell::PopMode::DeleteCurrent); + if( bRet ) + UpdateAttr(); +} + +void SwWrtShell::DelToStartOfLine() +{ + OpenMark(); + SwCursorShell::LeftMargin(); + bool bRet = Delete(false); + CloseMark( bRet ); +} + +void SwWrtShell::DelToEndOfLine() +{ + OpenMark(); + SwCursorShell::RightMargin(); + bool bRet = Delete(false); + CloseMark( bRet ); +} + +bool SwWrtShell::DelLeft() +{ + // If it's a Fly, throw it away + SelectionType nSelType = GetSelectionType(); + const SelectionType nCmp = SelectionType::Frame | SelectionType::Graphic | SelectionType::Ole | SelectionType::DrawObject; + if( nCmp & nSelType ) + { + // #108205# Remember object's position. + Point aTmpPt = GetObjRect().TopLeft(); + + DelSelectedObj(); + + // #108205# Set cursor to remembered position. + SetCursor(&aTmpPt); + + LeaveSelFrameMode(); + UnSelectFrame(); + + nSelType = GetSelectionType(); + if ( nCmp & nSelType ) + { + EnterSelFrameMode(); + GotoNextFly(); + } + + return true; + } + + // If a selection exists, erase this + if ( IsSelection() ) + { + if( !IsBlockMode() || HasSelection() ) + { + //OS: Once again Basic: SwActContext must be leaved + //before EnterStdMode! + { + SwActContext aActContext(this); + ResetCursorStack(); + Delete(false); + UpdateAttr(); + } + if( IsBlockMode() ) + { + NormalizePam(); + ClearMark(); + EnterBlockMode(); + } + else + EnterStdMode(); + return true; + } + else + EnterStdMode(); + } + + // JP 29.06.95: never erase a table standing in front of it. + bool bSwap = false; + const SwTableNode * pWasInTableNd = SwCursorShell::IsCursorInTable(); + + if( SwCursorShell::IsSttPara()) + { + // Start/EndAllAction to avoid cursor flickering + UnoActionContext c(GetDoc()); + SwCursorShell::Push(); + + // #i4032# Don't actually call a 'delete' if we + // changed the table cell, compare DelRight(). + const SwStartNode * pSNdOld = pWasInTableNd ? + GetCursor()->GetPointNode().FindTableBoxStartNode() : + nullptr; + + // If the cursor is at the beginning of a paragraph, try to step + // backwards. On failure we are done. + bool bDoSomething = SwCursorShell::Left(1,SwCursorSkipMode::Chars); + + if (bDoSomething) + { + // If the cursor entered or left a table (or both) we are done. + const SwTableNode* pIsInTableNd = SwCursorShell::IsCursorInTable(); + bDoSomething = pIsInTableNd == pWasInTableNd; + + if (bDoSomething) + { + const SwStartNode* pSNdNew = pIsInTableNd ? + GetCursor()->GetPointNode().FindTableBoxStartNode() : + nullptr; + + // #i4032# Don't actually call a 'delete' if we + // changed the table cell, compare DelRight(). + bDoSomething = pSNdOld == pSNdNew; + } + } + + if (!bDoSomething) + { + // tdf#115132 Restore previous position and we are done + SwCursorShell::Pop(SwCursorShell::PopMode::DeleteCurrent); + return false; + } + + SwCursorShell::Pop(SwCursorShell::PopMode::DeleteStack); + + OpenMark(); + SwCursorShell::Right(1,SwCursorSkipMode::Chars); + SwCursorShell::SwapPam(); + bSwap = true; + } + else + { + // If we are just to the right to a fieldmark, then remove it completely + const SwPosition* pCurPos = GetCursor()->GetPoint(); + SwPosition aPrevChar(*pCurPos->GetContentNode(), pCurPos->GetContentIndex() - 1); + sw::mark::IFieldmark* pFm = getIDocumentMarkAccess()->getFieldmarkAt(aPrevChar); + if (pFm && pFm->GetMarkEnd() == *pCurPos) + { + mxDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr); + IDocumentMarkAccess::DeleteFieldmarkCommand(*pFm); + getIDocumentMarkAccess()->deleteMark(pFm); + mxDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr); + return true; + } + + OpenMark(); + SwCursorShell::Left(1, SwCursorSkipMode::Chars); + + // If we are deleting a variation selector, we want to delete the + // whole sequence. + sal_uInt32 nCode = GetChar(false); + if ( rtl::isSurrogate( nCode ) ) + { + OUString sStr = GetSelText(); + nCode = sStr.iterateCodePoints( &o3tl::temporary(sal_Int32(0)) ); + } + + if ( unicode::isVariationSelector( nCode ) ) + { + SwCursorShell::Push(); + SwCursorShell::Left(1, SwCursorSkipMode::Chars); + SwCursorShell::Pop( SwCursorShell::PopMode::DeleteStack ); + } + } + bool bRet = Delete(true); + if( !bRet && bSwap ) + SwCursorShell::SwapPam(); + CloseMark( bRet ); + + return bRet; +} + +bool SwWrtShell::DelRight(bool const isReplaceHeuristic) +{ + // Will be or'ed, if a tableselection exists; + // will here be implemented on SelectionType::Table + bool bRet = false; + SelectionType nSelection = GetSelectionType(); + if(nSelection & SelectionType::TableCell) + nSelection = SelectionType::Table; + if(nSelection & SelectionType::Text) + nSelection = SelectionType::Text; + + switch( nSelection & ~SelectionType::Ornament & ~SelectionType::Media ) + { + case SelectionType::PostIt: + case SelectionType::Text: + case SelectionType::Table: + case SelectionType::NumberList: + // If a selection exists, erase it. + if( IsSelection() ) + { + if( !IsBlockMode() || HasSelection() ) + { + //OS: And once again Basic: SwActContext must be + //leaved before EnterStdMode ! + { + SwActContext aActContext(this); + ResetCursorStack(); + Delete(isReplaceHeuristic); + UpdateAttr(); + } + if( IsBlockMode() ) + { + NormalizePam(); + ClearMark(); + EnterBlockMode(); + } + else + EnterStdMode(); + bRet = true; + break; + } + else + EnterStdMode(); + } + + if (SwCursorShell::IsEndPara()) + { + // Start/EndAllAction to avoid cursor flickering + UnoActionContext c(GetDoc()); + + const SwTableNode* pWasInTableNd = IsCursorInTable(); + // #108049# Save the startnode of the current cell + const SwStartNode* pSNdOld = pWasInTableNd ? + GetCursor()->GetPointNode().FindTableBoxStartNode() : nullptr; + bool bCheckDelFull = SelectionType::Text & nSelection && SwCursorShell::IsSttPara(); + bool bDelFull = false; + bool bDoNothing = false; + + // #i41424# Introduced a couple of + // Push()-Pop() pairs here. The reason for this is that a + // Right()-Left() combination does not make sure, that + // the cursor will be in its initial state, because there + // may be a numbering in front of the next paragraph. + SwCursorShell::Push(); + + if (SwCursorShell::Right(1, SwCursorSkipMode::Chars)) + { + const SwTableNode* pCurrTableNd = IsCursorInTable(); + bDelFull = bCheckDelFull && pCurrTableNd && pCurrTableNd != pWasInTableNd; + if (!bDelFull && (IsCursorInTable() || (pCurrTableNd != pWasInTableNd))) + { + // #108049# Save the startnode of the current cell. + // May be different to pSNdOld as we have moved. + const SwStartNode* pSNdNew = pCurrTableNd ? + GetCursor()->GetPointNode().FindTableBoxStartNode() : nullptr; + + // tdf#115132 Only keep cursor position instead of deleting + // if we have moved to a different cell + bDoNothing = pSNdOld != pSNdNew; + } + } + + // restore cursor + SwCursorShell::Pop(SwCursorShell::PopMode::DeleteCurrent); + + if (bDelFull) + { + DelFullPara(); + UpdateAttr(); + } + if (bDelFull || bDoNothing) + break; + } + + { + // If we are just ahead of a fieldmark, then remove it completely + sw::mark::IFieldmark *const pFm = getIDocumentMarkAccess()->getFieldmarkAt(*GetCursor()->GetPoint()); + if (pFm && pFm->GetMarkStart() == *GetCursor()->GetPoint()) + { + mxDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr); + IDocumentMarkAccess::DeleteFieldmarkCommand(*pFm); + getIDocumentMarkAccess()->deleteMark(pFm); + mxDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr); + bRet = true; + break; + } + } + + OpenMark(); + SwCursorShell::Right(1, SwCursorSkipMode::Cells); + bRet = Delete(true); + CloseMark( bRet ); + break; + + case SelectionType::Frame: + case SelectionType::Graphic: + case SelectionType::Ole: + case SelectionType::DrawObject: + case SelectionType::DrawObjectEditMode: + case SelectionType::DbForm: + { + // Group deletion of the object and its comment together + // (also as-character anchor conversion at track changes) + mxDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr); + + // #108205# Remember object's position. + Point aTmpPt = GetObjRect().TopLeft(); + + // Remember the anchor of the selected object before deletion. + std::optional<SwPosition> oAnchor; + RndStdIds eAnchorId = RndStdIds::FLY_AT_PARA; + SwFlyFrame* pFly = GetSelectedFlyFrame(); + SwFrameFormat* pFormat = nullptr; + if (pFly) + { + pFormat = pFly->GetFormat(); + if (pFormat) + { + eAnchorId = pFormat->GetAnchor().GetAnchorId(); + // to-character anchor conversion at track changes + if ( IsRedlineOn() && (eAnchorId != RndStdIds::FLY_AS_CHAR && + eAnchorId != RndStdIds::FLY_AT_CHAR) ) + { + SfxItemSetFixed<RES_ANCHOR, RES_ANCHOR> aSet(GetAttrPool()); + GetFlyFrameAttr(aSet); + SwFormatAnchor aAnch(RndStdIds::FLY_AT_CHAR); + aSet.Put(aAnch); + SetFlyFrameAttr(aSet); + eAnchorId = pFormat->GetAnchor().GetAnchorId(); + } + if ((eAnchorId == RndStdIds::FLY_AS_CHAR || eAnchorId == RndStdIds::FLY_AT_CHAR) + && pFormat->GetAnchor().GetAnchorNode()) + { + oAnchor.emplace(*pFormat->GetAnchor().GetContentAnchor()); + // set cursor before the anchor point + if ( IsRedlineOn() ) + *GetCurrentShellCursor().GetPoint() = *oAnchor; + } + } + } + + // track changes: create redline at anchor point of the image to record the deletion + if ( IsRedlineOn() && oAnchor && SelectionType::Graphic & nSelection && pFormat && + ( eAnchorId == RndStdIds::FLY_AT_CHAR || eAnchorId == RndStdIds::FLY_AS_CHAR ) ) + { + sal_Int32 nRedlineLength = 1; + // create a double CH_TXT_TRACKED_DUMMY_CHAR anchor point of the image to record the + // deletion, if needed (otherwise use the existing anchor point of the image anchored + // *as* character) + if ( eAnchorId == RndStdIds::FLY_AT_CHAR ) + { + nRedlineLength = 2; + LeaveSelFrameMode(); + UnSelectFrame(); + RedlineFlags eOld = GetRedlineFlags(); + SetRedlineFlags( eOld | RedlineFlags::Ignore ); + Insert( OUStringChar(CH_TXT_TRACKED_DUMMY_CHAR) + + OUStringChar(CH_TXT_TRACKED_DUMMY_CHAR) ); + SwFormatAnchor anchor(RndStdIds::FLY_AT_CHAR); + SwCursorShell::Left(1, SwCursorSkipMode::Chars); + anchor.SetAnchor(GetCursor()->GetPoint()); + GetDoc()->SetAttr(anchor, *pFormat); + SetRedlineFlags( eOld ); + SwCursorShell::Left(1, SwCursorSkipMode::Chars); + } + OpenMark(); + SwCursorShell::Right(nRedlineLength, SwCursorSkipMode::Chars); + bRet = Delete(false); + CloseMark( bRet ); + } + else + DelSelectedObj(); + + if (oAnchor) + { + SwTextNode* pTextNode = oAnchor->GetNode().GetTextNode(); + if (pTextNode) + { + const SwTextField* pField( + pTextNode->GetFieldTextAttrAt(oAnchor->GetContentIndex(), ::sw::GetTextAttrMode::Default)); + if (pField + && dynamic_cast<const SwPostItField*>(pField->GetFormatField().GetField())) + { + // Remove the comment of the deleted object. + *GetCurrentShellCursor().GetPoint() = *oAnchor; + DelRight(); + } + } + } + + mxDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr); + + // #108205# Set cursor to remembered position. + SetCursor(&aTmpPt); + + LeaveSelFrameMode(); + UnSelectFrame(); + OSL_ENSURE( !IsFrameSelected(), + "<SwWrtShell::DelRight(..)> - <SwWrtShell::UnSelectFrame()> should unmark all objects" ); + // leave draw mode, if necessary. + { + if (GetView().GetDrawFuncPtr()) + { + GetView().GetDrawFuncPtr()->Deactivate(); + GetView().SetDrawFuncPtr(nullptr); + } + if ( GetView().IsDrawMode() ) + { + GetView().LeaveDrawCreate(); + } + } + } + + // <IsFrameSelected()> can't be true - see above. + { + nSelection = GetSelectionType(); + if ( SelectionType::Frame & nSelection || + SelectionType::Graphic & nSelection || + SelectionType::Ole & nSelection || + SelectionType::DrawObject & nSelection ) + { + EnterSelFrameMode(); + GotoNextFly(); + } + } + bRet = true; + break; + default: break; + } + return bRet; +} + +void SwWrtShell::DelToEndOfPara() +{ + SwActContext aActContext(this); + ResetCursorStack(); + Push(); + SetMark(); + if( !MovePara(GoCurrPara,fnParaEnd)) + { + Pop(SwCursorShell::PopMode::DeleteCurrent); + return; + } + bool bRet = Delete(false); + Pop(SwCursorShell::PopMode::DeleteCurrent); + if( bRet ) + UpdateAttr(); +} + +void SwWrtShell::DelToStartOfPara() +{ + SwActContext aActContext(this); + ResetCursorStack(); + Push(); + SetMark(); + if( !MovePara(GoCurrPara,fnParaStart)) + { + Pop(SwCursorShell::PopMode::DeleteCurrent); + return; + } + bool bRet = Delete(false); + Pop(SwCursorShell::PopMode::DeleteCurrent); + if( bRet ) + UpdateAttr(); +} + +// All erase operations should work with Find instead with +// Nxt-/PrvDelim, because the latter works with Wrap Around +// -- that's probably not wished. + +void SwWrtShell::DelToStartOfSentence() +{ + if(IsStartOfDoc()) + return; + OpenMark(); + bool bRet = BwdSentence_() && Delete(false); + CloseMark( bRet ); +} + +bool SwWrtShell::DelToEndOfSentence() +{ + if(IsEndOfDoc()) + return false; + OpenMark(); + bool bRet(false); + // fdo#60967: special case that is documented in help: delete + // paragraph following table if cursor is at end of last cell in table + if (IsEndOfTable()) + { + Push(); + ClearMark(); + if (SwCursorShell::Right(1,SwCursorSkipMode::Chars)) + { + SetMark(); + if (!IsEndPara()) // can only be at the end if it's empty + { // for an empty paragraph this would actually select the _next_ + SwCursorShell::MovePara(GoCurrPara, fnParaEnd); + } + if (!IsEndOfDoc()) // do not delete last paragraph in body text + { + bRet = DelFullPara(); + } + } + Pop(SwCursorShell::PopMode::DeleteCurrent); + } + else + { + bRet = FwdSentence_() && Delete(false); + } + CloseMark( bRet ); + return bRet; +} + +void SwWrtShell::DelNxtWord() +{ + if(IsEndOfDoc()) + return; + SwActContext aActContext(this); + ResetCursorStack(); + EnterStdMode(); + SetMark(); + if(IsEndWrd() && !IsStartWord()) + NxtWrdForDelete(); // #i92468# + if(IsStartWord() || IsEndPara()) + NxtWrdForDelete(); // #i92468# + else + EndWrd(); + + bool bRet = Delete(false); + if( bRet ) + UpdateAttr(); + else + SwapPam(); + ClearMark(); +} + +void SwWrtShell::DelPrvWord() +{ + if(IsStartOfDoc()) + return; + SwActContext aActContext(this); + ResetCursorStack(); + EnterStdMode(); + SetMark(); + if ( !IsStartWord() || + !PrvWrdForDelete() ) // #i92468# + { + if (IsEndWrd() || IsSttPara()) + PrvWrdForDelete(); // #i92468# + else + SttWrd(); + } + bool bRet = Delete(false); + if( bRet ) + UpdateAttr(); + else + SwapPam(); + ClearMark(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/wrtsh/move.cxx b/sw/source/uibase/wrtsh/move.cxx new file mode 100644 index 0000000000..cda2b32112 --- /dev/null +++ b/sw/source/uibase/wrtsh/move.cxx @@ -0,0 +1,773 @@ +/* -*- 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 <sfx2/bindings.hxx> +#include <sfx2/viewfrm.hxx> +#include <wrtsh.hxx> +#include <view.hxx> +#include <viewopt.hxx> +#include <drawbase.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <drawdoc.hxx> +#include <svx/svdpage.hxx> +#include <svx/svdview.hxx> + +/** + Always: + - Reset of the cursor stack + - retrigger timer + - if applicable: GCAttr + + on selection + - SttSelect() + + else + - EndSelect() + */ + +const tools::Long nReadOnlyScrollOfst = 10; + +namespace { + +class ShellMoveCursor +{ + SwWrtShell* pSh; + bool bAct; +public: + ShellMoveCursor( SwWrtShell* pWrtSh, bool bSel ) + { + bAct = !pWrtSh->ActionPend() && (pWrtSh->GetFrameType(nullptr,false) & FrameTypeFlags::FLY_ANY); + pSh = pWrtSh; + pSh->MoveCursor( bSel ); + pWrtSh->GetView().GetViewFrame().GetBindings().Invalidate(SID_HYPERLINK_GETLINK); + } + ~ShellMoveCursor() COVERITY_NOEXCEPT_FALSE + { + if( bAct ) + { + // The action is used for scrolling in "single paragraph" + // frames with fixed height. + pSh->StartAllAction(); + pSh->EndAllAction(); + } + } +}; + +} + +void SwWrtShell::MoveCursor( bool bWithSelect ) +{ + ResetCursorStack(); + if ( IsGCAttr() ) + { + GCAttr(); + ClearGCAttr(); + } + if ( bWithSelect ) + SttSelect(); + else + { + EndSelect(); + (this->*m_fnKillSel)( nullptr, false ); + } +} + +bool SwWrtShell::SimpleMove( FNSimpleMove FnSimpleMove, bool bSelect ) +{ + bool bRet; + if( bSelect ) + { + SttCursorMove(); + MoveCursor( true ); + bRet = (this->*FnSimpleMove)(); + EndCursorMove(); + } + else + { + bRet = (this->*FnSimpleMove)(); + if( bRet ) + MoveCursor(); + } + return bRet; +} + +bool SwWrtShell::Left( SwCursorSkipMode nMode, bool bSelect, + sal_uInt16 nCount, bool bBasicCall, bool bVisual ) +{ + if ( !bSelect && !bBasicCall && IsCursorReadonly() && !GetViewOptions()->IsSelectionInReadonly()) + { + Point aTmp( VisArea().Pos() ); + aTmp.AdjustX( -(VisArea().Width() * nReadOnlyScrollOfst / 100) ); + m_rView.SetVisArea( aTmp ); + return true; + } + else + { + ShellMoveCursor aTmp( this, bSelect ); + return SwCursorShell::Left( nCount, nMode, bVisual ); + } +} + +bool SwWrtShell::Right( SwCursorSkipMode nMode, bool bSelect, + sal_uInt16 nCount, bool bBasicCall, bool bVisual ) +{ + if ( !bSelect && !bBasicCall && IsCursorReadonly() && !GetViewOptions()->IsSelectionInReadonly() ) + { + Point aTmp( VisArea().Pos() ); + aTmp.AdjustX(VisArea().Width() * nReadOnlyScrollOfst / 100 ); + aTmp.setX( m_rView.SetHScrollMax( aTmp.X() ) ); + m_rView.SetVisArea( aTmp ); + return true; + } + else + { + ShellMoveCursor aTmp( this, bSelect ); + return SwCursorShell::Right( nCount, nMode, bVisual ); + } +} + +bool SwWrtShell::Up( bool bSelect, sal_uInt16 nCount, bool bBasicCall ) +{ + if ( !bSelect && !bBasicCall && IsCursorReadonly() && !GetViewOptions()->IsSelectionInReadonly()) + { + Point aTmp( VisArea().Pos() ); + aTmp.AdjustY( -(VisArea().Height() * nReadOnlyScrollOfst / 100) ); + m_rView.SetVisArea( aTmp ); + return true; + } + + ShellMoveCursor aTmp( this, bSelect ); + return SwCursorShell::Up(nCount); +} + +bool SwWrtShell::Down( bool bSelect, sal_uInt16 nCount, bool bBasicCall ) +{ + if ( !bSelect && !bBasicCall && IsCursorReadonly() && !GetViewOptions()->IsSelectionInReadonly()) + { + Point aTmp( VisArea().Pos() ); + aTmp.AdjustY(VisArea().Height() * nReadOnlyScrollOfst / 100 ); + aTmp.setY( m_rView.SetVScrollMax( aTmp.Y() ) ); + m_rView.SetVisArea( aTmp ); + return true; + } + + ShellMoveCursor aTmp( this, bSelect ); + return SwCursorShell::Down(nCount); +} + +bool SwWrtShell::LeftMargin( bool bSelect, bool bBasicCall ) +{ + if ( !bSelect && !bBasicCall && IsCursorReadonly() ) + { + Point aTmp( VisArea().Pos() ); + aTmp.setX( DOCUMENTBORDER ); + m_rView.SetVisArea( aTmp ); + return true; + } + else + { + ShellMoveCursor aTmp( this, bSelect ); + return SwCursorShell::LeftMargin(); + } +} + +bool SwWrtShell::RightMargin( bool bSelect, bool bBasicCall ) +{ + if ( !bSelect && !bBasicCall && IsCursorReadonly() ) + { + Point aTmp( VisArea().Pos() ); + aTmp.setX( GetDocSize().Width() - VisArea().Width() + DOCUMENTBORDER ); + if( DOCUMENTBORDER > aTmp.X() ) + aTmp.setX( DOCUMENTBORDER ); + m_rView.SetVisArea( aTmp ); + return true; + } + else + { + ShellMoveCursor aTmp( this, bSelect ); + return SwCursorShell::RightMargin(bBasicCall); + } +} + +bool SwWrtShell::GoStart( bool bKeepArea, bool *pMoveTable, + bool bSelect, bool bDontMoveRegion ) +{ + if ( IsCursorInTable() ) + { + const bool bBoxSelection = HasBoxSelection(); + if( !m_bBlockMode ) + { + if ( !bSelect ) + EnterStdMode(); + else + SttSelect(); + } + // Table cell ? + if ( !bBoxSelection && (MoveSection( GoCurrSection, fnSectionStart) + || bDontMoveRegion)) + { + if ( pMoveTable ) + *pMoveTable = false; + return true; + } + SwTableNode const*const pTable(getShellCursor(false)->GetPoint()->GetNode().FindTableNode()); + assert(pTable); + if( MoveTable( GotoCurrTable, fnTableStart ) || bDontMoveRegion ) + { + if ( pMoveTable ) + *pMoveTable = true; + return true; + } + else if (SwCursor const*const pCursor = getShellCursor(false); + pTable->GetNodes()[pTable->GetIndex()+1]->EndOfSectionIndex() + < pCursor->GetPoint()->GetNode().GetIndex() + && pMoveTable != nullptr // only set by SelAll() + // problem: cursor isn't inside 1st cell, and didn't move there + // workaround: try to move cursor outside of table for SelAll() + && MoveOutOfTable()) + { + assert(!*pMoveTable); + return true; + } + else if( bBoxSelection && pMoveTable ) + { + // JP 09.01.96: We have a box selection (or an empty cell) + // and we want select (pMoveTable will be + // set in SelAll). Then the table must not + // be left, otherwise there is no selection + // of the entire table possible! + *pMoveTable = true; + return true; + } + } + + if( !m_bBlockMode ) + { + if ( !bSelect ) + EnterStdMode(); + else + SttSelect(); + } + const FrameTypeFlags nFrameType = GetFrameType(nullptr,false); + if ( FrameTypeFlags::FLY_ANY & nFrameType ) + { + if( MoveSection( GoCurrSection, fnSectionStart ) ) + return true; + else if ( FrameTypeFlags::FLY_FREE & nFrameType || bDontMoveRegion ) + return false; + } + if(( FrameTypeFlags::HEADER | FrameTypeFlags::FOOTER | FrameTypeFlags::FOOTNOTE ) & nFrameType ) + { + if ( MoveSection( GoCurrSection, fnSectionStart ) ) + return true; + else if ( bKeepArea ) + return true; + } + + // first try to move to the start of the current SwSection + return SwCursorShell::MoveRegion( GotoCurrRegionAndSkip, fnRegionStart ) || + (pMoveTable != nullptr + // move to start of text - if in different table, move out + ? MoveStartText() + // TODO who needs SttEndDoc for other case? + : SwCursorShell::SttEndDoc(true)); +} + +bool SwWrtShell::GoEnd(bool bKeepArea, const bool *pMoveTable) +{ + if (pMoveTable && *pMoveTable) // only in SelAll() + { + SwTableNode const*const pTable(getShellCursor(false)->GetPoint()->GetNode().FindTableNode()); + assert(pTable); + if (MoveTable(GotoCurrTable, fnTableEnd)) + { + return true; + } + else if (SwCursor const*const pCursor = getShellCursor(false); + pCursor->GetPoint()->GetNode().GetIndex() + < pTable->GetNodes()[pTable->EndOfSectionIndex()-1]->StartOfSectionIndex() + // problem: cursor isn't inside 1st cell, and didn't move there + // workaround: try to move cursor outside of table for SelAll() + && MoveOutOfTable()) + { + return true; + } + else + { + return false; + } + } + + if ( IsCursorInTable() ) + { + if ( MoveSection( GoCurrSection, fnSectionEnd ) || + MoveTable( GotoCurrTable, fnTableEnd ) ) + return true; + } + else + { + const FrameTypeFlags nFrameType = GetFrameType(nullptr,false); + if ( FrameTypeFlags::FLY_ANY & nFrameType ) + { + if ( MoveSection( GoCurrSection, fnSectionEnd ) ) + return true; + else if ( FrameTypeFlags::FLY_FREE & nFrameType ) + return false; + } + if(( FrameTypeFlags::HEADER | FrameTypeFlags::FOOTER | FrameTypeFlags::FOOTNOTE ) & nFrameType ) + { + if ( MoveSection( GoCurrSection, fnSectionEnd) ) + return true; + else if ( bKeepArea ) + return true; + } + } + // Regions ??? + return SwCursorShell::MoveRegion( GotoCurrRegionAndSkip, fnRegionEnd ) || + SwCursorShell::SttEndDoc(false); +} + +bool SwWrtShell::StartOfSection(bool const bSelect) +{ + ShellMoveCursor aTmp( this, bSelect ); + return GoStart(false, nullptr, bSelect ); +} + +bool SwWrtShell::EndOfSection(bool const bSelect) +{ + ShellMoveCursor aTmp( this, bSelect ); + return GoEnd(); +} + +bool SwWrtShell::SttNxtPg( bool bSelect ) +{ + ShellMoveCursor aTmp( this, bSelect ); + return MovePage( GetNextFrame, GetFirstSub ); +} + +void SwWrtShell::SttPrvPg( bool bSelect ) +{ + ShellMoveCursor aTmp( this, bSelect ); + MovePage( GetPrevFrame, GetFirstSub ); +} + +void SwWrtShell::EndNxtPg( bool bSelect ) +{ + ShellMoveCursor aTmp( this, bSelect ); + MovePage( GetNextFrame, GetLastSub ); +} + +bool SwWrtShell::EndPrvPg( bool bSelect ) +{ + ShellMoveCursor aTmp( this, bSelect ); + return MovePage( GetPrevFrame, GetLastSub ); +} + +bool SwWrtShell::SttPg( bool bSelect ) +{ + ShellMoveCursor aTmp( this, bSelect ); + return MovePage( GetThisFrame, GetFirstSub ); +} + +bool SwWrtShell::EndPg( bool bSelect ) +{ + ShellMoveCursor aTmp( this, bSelect ); + return MovePage( GetThisFrame, GetLastSub ); +} + +bool SwWrtShell::SttPara( bool bSelect ) +{ + ShellMoveCursor aTmp( this, bSelect ); + return MovePara( GoCurrPara, fnParaStart ); +} + +void SwWrtShell::EndPara( bool bSelect ) +{ + ShellMoveCursor aTmp( this, bSelect ); + MovePara(GoCurrPara,fnParaEnd); +} + +// Column-by-jumping. +// SSelection with or without +// returns success or failure + +void SwWrtShell::StartOfColumn() +{ + ShellMoveCursor aTmp( this, false/*bSelect*/); + MoveColumn(GetCurrColumn, GetColumnStt); +} + +void SwWrtShell::EndOfColumn() +{ + ShellMoveCursor aTmp( this, false/*bSelect*/); + MoveColumn(GetCurrColumn, GetColumnEnd); +} + +void SwWrtShell::StartOfNextColumn() +{ + ShellMoveCursor aTmp( this, false/*bSelect*/); + MoveColumn( GetNextColumn, GetColumnStt); +} + +void SwWrtShell::EndOfNextColumn() +{ + ShellMoveCursor aTmp( this, false/*bSelect*/); + MoveColumn(GetNextColumn, GetColumnEnd); +} + +void SwWrtShell::StartOfPrevColumn() +{ + ShellMoveCursor aTmp( this, false/*bSelect*/); + MoveColumn(GetPrevColumn, GetColumnStt); +} + +void SwWrtShell::EndOfPrevColumn() +{ + ShellMoveCursor aTmp( this, false/*bSelect*/); + MoveColumn(GetPrevColumn, GetColumnEnd); +} + +bool SwWrtShell::PushCursor(SwTwips lOffset, bool bSelect) +{ + bool bDiff = false; + SwRect aOldRect( GetCharRect() ), aTmpArea( VisArea() ); + + // m_bDestOnStack indicates if I could not set the coursor at the current + // position, because in this region is no content. + if( !m_bDestOnStack ) + { + Point aPt( aOldRect.Center() ); + + if( !IsCursorVisible() ) + // set CursorPos to top-/bottom left pos. So the pagescroll is not + // be dependent on the current cursor, but on the visarea. + aPt.setY( aTmpArea.Top() + aTmpArea.Height() / 2 ); + + aPt.AdjustY(lOffset ); + m_aDest = GetContentPos(aPt,lOffset > 0); + m_aDest.setX( aPt.X() ); + m_bDestOnStack = true; + } + + // If we had a frame selection, it must be removed after the m_fnSetCursor + // and we have to remember the position on the stack to return to it later. + bool bIsFrameSel = false; + + //Target position is now within the viewable region --> + //Place the cursor at the target position; remember that no target + //position is longer on the stack. + //The new visible region is to be determined beforehand. + aTmpArea.Pos().AdjustY(lOffset ); + if( aTmpArea.Contains(m_aDest) ) + { + if( bSelect ) + SttSelect(); + else + EndSelect(); + + bIsFrameSel = IsFrameSelected(); + bool bIsObjSel = 0 != IsObjSelected(); + + // unselect frame + if( bIsFrameSel || bIsObjSel ) + { + UnSelectFrame(); + LeaveSelFrameMode(); + if ( bIsObjSel ) + { + GetView().SetDrawFuncPtr( nullptr ); + GetView().LeaveDrawCreate(); + } + + CallChgLnk(); + } + + (this->*m_fnSetCursor)( &m_aDest, true ); + + bDiff = aOldRect != GetCharRect(); + + if( bIsFrameSel ) + { + // In frames take only the upper corner + // so that it can be re-selected. + aOldRect.SSize( 5, 5 ); + } + + // reset Dest. SPoint Flags + m_bDestOnStack = false; + } + + // Position into the stack; bDiff indicates if there is a + // difference between the old and the new cursor position. + m_pCursorStack.reset( new CursorStack( bDiff, bIsFrameSel, aOldRect.Center(), + lOffset, std::move(m_pCursorStack) ) ); + return !m_bDestOnStack && bDiff; +} + +bool SwWrtShell::PopCursor(bool bUpdate, bool bSelect) +{ + if( nullptr == m_pCursorStack) + return false; + + const bool bValidPos = m_pCursorStack->bValidCurPos; + if( bUpdate && bValidPos ) + { + // If a predecessor is on the stack, + // use the flag for a valid position. + SwRect aTmpArea(VisArea()); + aTmpArea.Pos().AdjustY( -(m_pCursorStack->lOffset) ); + if( aTmpArea.Contains( m_pCursorStack->aDocPos ) ) + { + if( bSelect ) + SttSelect(); + else + EndSelect(); + + (this->*m_fnSetCursor)(&m_pCursorStack->aDocPos, !m_pCursorStack->bIsFrameSel); + if( m_pCursorStack->bIsFrameSel && IsObjSelectable(m_pCursorStack->aDocPos)) + { + HideCursor(); + SelectObj( m_pCursorStack->aDocPos ); + EnterSelFrameMode( &m_pCursorStack->aDocPos ); + } + } + // If a discrepancy between the visible range and the + // remembered cursor position occurs, all of the remembered + // positions are thrown away. + else + { + ResetCursorStack_(); + return false; + } + } + m_pCursorStack = std::move(m_pCursorStack->pNext); + if( nullptr == m_pCursorStack ) + { + m_ePageMove = MV_NO; + m_bDestOnStack = false; + } + return bValidPos; +} + +// Reset of all pushed cursor positions; these will +// not be displayed ( --> No Start-/EndAction!!) + +void SwWrtShell::ResetCursorStack_() +{ + while(m_pCursorStack) + m_pCursorStack = std::move(m_pCursorStack->pNext); + m_ePageMove = MV_NO; + m_bDestOnStack = false; +} +/** + if no stack exists --> cancel selection + if stack && change of direction + --> pop cursor and return + else + --> push cursor + transpose cursor +*/ + +bool SwWrtShell::PageCursor(SwTwips lOffset, bool bSelect) +{ + // Do nothing if an offset of 0 was indicated + if(!lOffset) return false; + // Was once used to force a reformat of the layout. + // This has not work that way, because the cursor was not set + // because this does not happen within a + // Start-/EndActionParentheses. + // Because only SwViewShell::EndAction() is called at the end, + // no updating of the display of the cursor position takes place. + // The CursorShell-Actionparentheses cannot be used, because it + // always leads to displaying the cursor, thus also, + // if after the scroll scrolled in a region without a valid position. + // SwViewShell::StartAction(); + PageMove eDir = lOffset > 0? MV_PAGE_DOWN: MV_PAGE_UP; + // Change of direction and stack present + if( eDir != m_ePageMove && m_ePageMove != MV_NO && PopCursor( true, bSelect )) + return true; + + const bool bRet = PushCursor(lOffset, bSelect); + m_ePageMove = eDir; + return bRet; +} + +bool SwWrtShell::GotoPage(sal_uInt16 nPage, bool bRecord) +{ + addCurrentPosition(); + ShellMoveCursor aTmp( this, false); + if( SwCursorShell::GotoPage(nPage) && bRecord) + { + if(IsSelFrameMode()) + { + UnSelectFrame(); + LeaveSelFrameMode(); + } + return true; + } + return false; +} + +bool SwWrtShell::GotoMark( const ::sw::mark::IMark* const pMark, bool bSelect ) +{ + ShellMoveCursor aTmp( this, bSelect ); + SwPosition aPos = *GetCursor()->GetPoint(); + bool bRet = SwCursorShell::GotoMark( pMark, true/*bStart*/ ); + if (bRet) + m_aNavigationMgr.addEntry(aPos); + return bRet; +} + +bool SwWrtShell::GotoFly( const OUString& rName, FlyCntType eType, bool bSelFrame ) +{ + SwPosition aPos = *GetCursor()->GetPoint(); + bool bRet = SwFEShell::GotoFly(rName, eType, bSelFrame); + if (bRet) + m_aNavigationMgr.addEntry(aPos); + return bRet; +} + +bool SwWrtShell::GotoINetAttr( const SwTextINetFormat& rAttr ) +{ + SwPosition aPos = *GetCursor()->GetPoint(); + bool bRet = SwCursorShell::GotoINetAttr(rAttr); + if (bRet) + m_aNavigationMgr.addEntry(aPos); + return bRet; +} + +void SwWrtShell::GotoOutline( SwOutlineNodes::size_type nIdx ) +{ + addCurrentPosition(); + SwCursorShell::GotoOutline (nIdx); +} + +bool SwWrtShell::GotoOutline( const OUString& rName ) +{ + SwPosition aPos = *GetCursor()->GetPoint(); + bool bRet = SwCursorShell::GotoOutline (rName); + if (bRet) + m_aNavigationMgr.addEntry(aPos); + return bRet; +} + +bool SwWrtShell::GotoDrawingObject(std::u16string_view rName) +{ + SwPosition aPos = *GetCursor()->GetPoint(); + bool bRet = false; + SdrView* pDrawView = GetDrawView(); + if (pDrawView) + { + pDrawView->SdrEndTextEdit(); + pDrawView->UnmarkAll(); + SdrPage* pPage = getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0); + for (const rtl::Reference<SdrObject>& pObj : *pPage) + { + if (pObj->GetName() == rName) + { + SdrPageView* pPageView = pDrawView->GetSdrPageView(); + if(pPageView) + { + pDrawView->MarkObj(pObj.get(), pPageView); + m_aNavigationMgr.addEntry(aPos); + EnterStdMode(); + HideCursor(); + EnterSelFrameMode(); + bRet = true; + } + break; + } + } + } + return bRet; +} + +bool SwWrtShell::GotoRegion( std::u16string_view rName ) +{ + SwPosition aPos = *GetCursor()->GetPoint(); + bool bRet = SwCursorShell::GotoRegion (rName); + if (bRet) + m_aNavigationMgr.addEntry(aPos); + return bRet; + } + +bool SwWrtShell::GotoRefMark( const OUString& rRefMark, sal_uInt16 nSubType, + sal_uInt16 nSeqNo, sal_uInt16 nFlags ) +{ + SwPosition aPos = *GetCursor()->GetPoint(); + bool bRet = SwCursorShell::GotoRefMark(rRefMark, nSubType, nSeqNo, nFlags); + if (bRet) + m_aNavigationMgr.addEntry(aPos); + return bRet; +} + +bool SwWrtShell::GotoNextTOXBase( const OUString* pName ) +{ + SwPosition aPos = *GetCursor()->GetPoint(); + bool bRet = SwCursorShell::GotoNextTOXBase(pName); + if (bRet) + m_aNavigationMgr.addEntry(aPos); + return bRet; +} + +bool SwWrtShell::GotoTable( const OUString& rName ) +{ + SwPosition aPos = *GetCursor()->GetPoint(); + bool bRet = SwCursorShell::GotoTable(rName); + if (bRet) + m_aNavigationMgr.addEntry(aPos); + return bRet; +} + +void SwWrtShell::GotoFormatField( const SwFormatField& rField ) { + SwPosition aPos = *GetCursor()->GetPoint(); + bool bRet = SwCursorShell::GotoFormatField(rField); + if (bRet) + m_aNavigationMgr.addEntry(aPos); +} + +void SwWrtShell::GotoFootnoteAnchor(const SwTextFootnote& rTextFootnote) +{ + SwPosition aPos = *GetCursor()->GetPoint(); + bool bRet = SwCursorShell::GotoFootnoteAnchor(rTextFootnote); + if (bRet) + m_aNavigationMgr.addEntry(aPos); +} + +const SwRangeRedline* SwWrtShell::GotoRedline( SwRedlineTable::size_type nArrPos, bool bSelect ) { + SwPosition aPos = *GetCursor()->GetPoint(); + const SwRangeRedline *pRedline = SwCursorShell::GotoRedline(nArrPos, bSelect); + if (pRedline) + m_aNavigationMgr.addEntry(aPos); + return pRedline; +} + +bool SwWrtShell::SelectTextAttr( sal_uInt16 nWhich, const SwTextAttr* pAttr ) +{ + bool bRet; + { + SwMvContext aMvContext(this); + SttSelect(); + bRet = SwCursorShell::SelectTextAttr( nWhich, false, pAttr ); + } + EndSelect(); + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/wrtsh/navmgr.cxx b/sw/source/uibase/wrtsh/navmgr.cxx new file mode 100644 index 0000000000..eea6cda7dc --- /dev/null +++ b/sw/source/uibase/wrtsh/navmgr.cxx @@ -0,0 +1,221 @@ +/* -*- 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/. + */ + +#include <navmgr.hxx> +#include <wrtsh.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/viewfrm.hxx> +#include <vcl/svapp.hxx> +#include <cmdid.h> +#include <view.hxx> +#include <doc.hxx> +#include <unocrsr.hxx> + +// This method positions the cursor to the position rPos. + +void SwNavigationMgr::GotoSwPosition(const SwPosition &rPos) { + + // EnterStdMode() prevents the cursor to 'block' the current + // shell when it should move from the image back to the normal shell + m_rMyShell.EnterStdMode(); + m_rMyShell.StartAllAction(); + // cursor consists of two SwPositions: Point and Mark. + // Such a pair is called a PaM. SwPaM is derived from SwRing. + // The Ring contains the single regions of a multi-selection. + SwPaM* pPaM = m_rMyShell.GetCursor(); + + if(pPaM->HasMark()) + pPaM->DeleteMark(); // If there was a selection, get rid of it + *pPaM->GetPoint() = rPos; // Position Cursor + + m_rMyShell.EndAllAction(); +} + +// Ctor for the SwNavigationMgr class +// Sets the shell to the current shell +// and the index of the current position to 0 + +SwNavigationMgr::SwNavigationMgr(SwWrtShell & rShell) + : m_nCurrent(0), m_rMyShell(rShell) +{ +} + +SwNavigationMgr::~SwNavigationMgr() +{ + SolarMutexGuard g; + for (auto & it : m_entries) + { + EndListening(it->m_aNotifier); + } + m_entries.clear(); +} + +void SwNavigationMgr::Notify(SfxBroadcaster& rBC, const SfxHint& rHint) +{ + // our cursors may now spontaneously self-destruct: remove from + // m_entries if that happens + if (typeid(rHint) == typeid(sw::UnoCursorHint)) + { + auto it = std::find_if(m_entries.begin(), m_entries.end(), + [&rBC](const sw::UnoCursorPointer& rItem) { return !rItem || &rBC == &rItem->m_aNotifier; }); + if (it != m_entries.end()) + { + EndListening(rBC); + m_entries.erase(it); + } + } +} + +// This method is used by the navigation shell - defined in sw/source/uibase/inc/navsh.hxx +// and implemented in sw/source/uibase/shells/navsh.cxx +// It is called when we want to check if the back button should be enabled or not. +// The back button should be enabled only if there are some entries in the navigation history + +bool SwNavigationMgr::backEnabled() { + return (m_nCurrent > 0); +} + +// Similar to backEnabled() method. +// The forward button should be enabled if we ever clicked back +// Due to the implementation of the navigation class, this is when the +// current position within the navigation history entries in not the last one +// i.e. when the m_nCurrent index is not at the end of the m_entries vector + +bool SwNavigationMgr::forwardEnabled() { + return m_nCurrent+1 < m_entries.size(); +} + +// The goBack() method positions the cursor to the previous entry in the navigation history +// If there was no history to go forward to, it adds the current position of the cursor +// to the history so we could go forward to where we came from + +void SwNavigationMgr::goBack() { + + // Although the button should be disabled whenever the backEnabled() returns false, + // the UI is sometimes not as responsive as we would like it to be :) + // this check prevents segmentation faults and in this way the class is not relying on the UI + + if (!backEnabled()) return; + + /* Trying to get the current cursor */ + SwPaM* pPaM = m_rMyShell.GetCursor(); + if (!pPaM) { + return; + } + // This flag will be used to manually refresh the buttons + + bool bForwardWasDisabled = !forwardEnabled(); + + // If we're going backwards in our history, but the current location is not + // in the history then we need to add *here* to it so that we can "go + // forward" to here again. + + if (bForwardWasDisabled) { + + // the cursor consists of two SwPositions: Point and Mark. + // We are adding the current Point to the navigation history + // so we could later navigate forward to it + + // The addEntry() method returns true iff we should decrement + // the index before navigating back + + if (addEntry(*pPaM->GetPoint()) ) { + m_nCurrent--; + } + } + m_nCurrent--; + // Position cursor to appropriate navigation history entry + GotoSwPosition(*m_entries[m_nCurrent]->GetPoint()); + // Refresh the buttons + if (bForwardWasDisabled) + m_rMyShell.GetView().GetViewFrame().GetBindings().Invalidate(FN_NAVIGATION_FORWARD); + if (!backEnabled()) + m_rMyShell.GetView().GetViewFrame().GetBindings().Invalidate(FN_NAVIGATION_BACK); +} + +// The goForward() method positions the cursor to the next entry in the navigation history + +void SwNavigationMgr::goForward() { + + // Although the button should be disabled whenever the backForward() returns false, + // the UI is sometimes not as responsive as we would like it to be :) + // this check prevents segmentation faults and in this way the class is not relying on the UI + + if (!forwardEnabled()) return; + + // This flag will be used to manually refresh the buttons + bool bBackWasDisabled = !backEnabled(); + // The current index is positioned at the current entry in the navigation history + // We have to increment it to go to the next entry + m_nCurrent++; + GotoSwPosition(*m_entries[m_nCurrent]->GetPoint()); + // Refresh the buttons + if (bBackWasDisabled) + m_rMyShell.GetView().GetViewFrame().GetBindings().Invalidate(FN_NAVIGATION_BACK); + if (!forwardEnabled()) + m_rMyShell.GetView().GetViewFrame().GetBindings().Invalidate(FN_NAVIGATION_FORWARD); +} + +// This method adds the SwPosition rPos to the navigation history +// rPos is usually the current position of the cursor in the document + +bool SwNavigationMgr::addEntry(const SwPosition& rPos) { + + // Flags that will be used for refreshing the buttons + bool bBackWasDisabled = !backEnabled(); + bool bForwardWasEnabled = forwardEnabled(); + + bool bRet = false; // return value of the function. + // Indicates whether the index should be decremented before + // jumping back or not + // The navigation history has recency with temporal ordering enhancement, + // as described on http://zing.ncsl.nist.gov/hfweb/proceedings/greenberg/ + // If any forward history exists, twist the tail of the + // list from the current position to the end + if (bForwardWasEnabled) { + + size_t number_ofm_entries = m_entries.size(); // To avoid calling m_entries.size() multiple times + int curr = m_nCurrent; // Index from which we'll twist the tail. + int n = (number_ofm_entries - curr) / 2; // Number of entries that will swap places + for (int i = 0; i < n; i++) { + std::swap(m_entries[curr + i], m_entries[number_ofm_entries -1 - i]); + } + + if (*m_entries.back()->GetPoint() != rPos) + { + sw::UnoCursorPointer pCursor(m_rMyShell.GetDoc()->CreateUnoCursor(rPos)); + StartListening(pCursor->m_aNotifier); + m_entries.push_back(pCursor); + } + bRet = true; + } + else { + if ( (!m_entries.empty() && *m_entries.back()->GetPoint() != rPos) || m_entries.empty() ) { + sw::UnoCursorPointer pCursor(m_rMyShell.GetDoc()->CreateUnoCursor(rPos)); + StartListening(pCursor->m_aNotifier); + m_entries.push_back(pCursor); + bRet = true; + } + if (m_entries.size() > 1 && *m_entries.back()->GetPoint() == rPos) + bRet = true; + if (m_entries.size() == 1 && *m_entries.back()->GetPoint() == rPos) + bRet = false; + } + m_nCurrent = m_entries.size(); + + // Refresh buttons + if (bBackWasDisabled) + m_rMyShell.GetView().GetViewFrame().GetBindings().Invalidate(FN_NAVIGATION_BACK); + if (bForwardWasEnabled) + m_rMyShell.GetView().GetViewFrame().GetBindings().Invalidate(FN_NAVIGATION_FORWARD); + + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/wrtsh/select.cxx b/sw/source/uibase/wrtsh/select.cxx new file mode 100644 index 0000000000..3ae5842f61 --- /dev/null +++ b/sw/source/uibase/wrtsh/select.cxx @@ -0,0 +1,1057 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <limits.h> +#include <hintids.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/viewfrm.hxx> +#include <svl/eitem.hxx> +#include <svl/macitem.hxx> +#include <unotools/charclass.hxx> +#include <sfx2/event.hxx> +#include <osl/diagnose.h> +#include <cmdid.h> +#include <view.hxx> +#include <basesh.hxx> +#include <wrtsh.hxx> +#include <frmatr.hxx> +#include <mdiexp.hxx> +#include <fmtcol.hxx> +#include <frmfmt.hxx> +#include <swdtflvr.hxx> +#include <doc.hxx> +#include <wordcountdialog.hxx> +#include <memory> +#include <vcl/uitest/logger.hxx> +#include <vcl/uitest/eventdescription.hxx> + +#include <vcl/weld.hxx> +#include <vcl/builder.hxx> +#include <officecfg/Office/Common.hxx> +#include <unotools/configmgr.hxx> +#include <bitmaps.hlst> + +#include <svx/svdview.hxx> + +namespace com::sun::star::util { + struct SearchOptions2; +} + +using namespace ::com::sun::star::util; + +static tools::Long nStartDragX = 0, nStartDragY = 0; +static bool bStartDrag = false; + +void SwWrtShell::Invalidate() +{ + // to avoid making the slot volatile, invalidate it every time if something could have been changed + // this is still much cheaper than asking for the state every 200 ms (and avoid background processing) + GetView().GetViewFrame().GetBindings().Invalidate( FN_STAT_SELMODE ); + GetView().GetViewFrame().GetBindings().Update(FN_STAT_SELMODE); // make selection mode control icon update immediately + SwWordCountWrapper *pWrdCnt = static_cast<SwWordCountWrapper*>(GetView().GetViewFrame().GetChildWindow(SwWordCountWrapper::GetChildWindowId())); + if (pWrdCnt) + pWrdCnt->UpdateCounts(); +} + +bool SwWrtShell::SelNearestWrd() +{ + SwMvContext aMvContext(this); + if( !IsInWord() && !IsEndWrd() && !IsStartWord() ) + PrvWrd(); + if( IsEndWrd() ) + Left(SwCursorSkipMode::Cells, false, 1, false ); + return SelWrd(); +} + +bool SwWrtShell::SelWrd(const Point *pPt ) +{ + bool bRet; + { + SwMvContext aMvContext(this); + SttSelect(); + bRet = SwCursorShell::SelectWord( pPt ); + } + EndSelect(); + if( bRet ) + { + m_bSelWrd = true; + if(pPt) + m_aStart = *pPt; + } + return bRet; +} + +void SwWrtShell::SelSentence(const Point *pPt ) +{ + { + SwMvContext aMvContext(this); + ClearMark(); + SwCursorShell::GoStartSentence(); + SttSelect(); + SwCursorShell::GoEndSentence(); + } + EndSelect(); + if(pPt) + m_aStart = *pPt; + m_bSelLn = true; + m_bSelWrd = false; // disable SelWord, otherwise no SelLine goes on +} + +void SwWrtShell::SelPara(const Point *pPt ) +{ + { + SwMvContext aMvContext(this); + ClearMark(); + SwCursorShell::MovePara( GoCurrPara, fnParaStart ); + SttSelect(); + SwCursorShell::MovePara( GoCurrPara, fnParaEnd ); + } + EndSelect(); + if(pPt) + m_aStart = *pPt; + m_bSelLn = false; + m_bSelWrd = false; // disable SelWord, otherwise no SelLine goes on +} + +void SwWrtShell::SelAll() +{ + const bool bLockedView = IsViewLocked(); + LockView( true ); + { + if(m_bBlockMode) + LeaveBlockMode(); + SwMvContext aMvContext(this); + bool bMoveTable = false; + std::optional<SwPosition> oStartPos; + std::optional<SwPosition> oEndPos; + SwShellCursor* pTmpCursor = nullptr; + + // Query these early, before we move the cursor. + bool bHasWholeTabSelection = HasWholeTabSelection(); + bool bIsCursorInTable = IsCursorInTable(); + + if (!bHasWholeTabSelection + && ( !bIsCursorInTable + || getShellCursor(false)->GetMarkNode().FindTableNode() == nullptr + || !ExtendedSelectedAll())) // ESA inside table -> else branch + { + if ( IsSelection() && IsCursorPtAtEnd() ) + SwapPam(); + pTmpCursor = getShellCursor( false ); + if( pTmpCursor ) + { + oStartPos.emplace( *pTmpCursor->GetPoint() ); + oEndPos.emplace( *pTmpCursor->GetMark() ); + } + Push(); + bool bIsFullSel = !MoveSection( GoCurrSection, fnSectionStart); + SwapPam(); + bIsFullSel &= !MoveSection( GoCurrSection, fnSectionEnd); + Pop(SwCursorShell::PopMode::DeleteCurrent); + GoStart(true, &bMoveTable, false, !bIsFullSel); + SttSelect(); + GoEnd(true, &bMoveTable); + } + else + { + if (MoveOutOfTable()) + { // select outer text + EnterStdMode(); // delete m_pTableCursor +// GoStart(true, &bMoveTable, false, true); + MoveSection(GoCurrSection, fnSectionStart); // don't move into prev table + SttSelect(); + MoveSection(GoCurrSection, fnSectionEnd); // don't move to different cell + } + else + { + TrySelectOuterTable(); + } + } + + bool bNeedsExtendedSelectAll = StartsWith_() != StartsWith::None; + + // the GoEnd() could have created a table selection, if so avoid ESA. + if (bNeedsExtendedSelectAll && bIsCursorInTable) + { + bNeedsExtendedSelectAll = !HasWholeTabSelection(); + } + + if (bNeedsExtendedSelectAll) + { + ExtendedSelectAll(/*bFootnotes =*/ false); + } + + SwDoc *pDoc = GetDoc(); + if ( pDoc ) + { + pDoc->SetPrepareSelAll(); + } + + if( oStartPos ) + { + pTmpCursor = getShellCursor( false ); + if( pTmpCursor ) + { + // Some special handling for sections (e.g. TOC) at the beginning of the document body + // to avoid the selection of the first section + // if the last selection was behind the first section or + // if the last selection was already the first section + // In this both cases we select to the end of document + if( ( *pTmpCursor->GetPoint() < *oEndPos || + ( *oStartPos == *pTmpCursor->GetMark() && + *oEndPos == *pTmpCursor->GetPoint() ) ) && !bNeedsExtendedSelectAll) + SwCursorShell::SttEndDoc(false); + } + } + } + EndSelect(); + LockView( bLockedView ); +} + +// Description: Text search + +sal_Int32 SwWrtShell::SearchPattern( const i18nutil::SearchOptions2& rSearchOpt, bool bSearchInNotes, + SwDocPositions eStt, SwDocPositions eEnd, + FindRanges eFlags, bool bReplace ) +{ + // no enhancement of existing selections + if(!(eFlags & FindRanges::InSel)) + ClearMark(); + bool bCancel = false; + sal_Int32 nRet = Find_Text(rSearchOpt, bSearchInNotes, eStt, eEnd, bCancel, eFlags, bReplace); + if(bCancel) + { + Undo(); + nRet = SAL_MAX_INT32; + } + return nRet; +} + +// Description: search for templates + +sal_Int32 SwWrtShell::SearchTempl( const OUString &rTempl, + SwDocPositions eStt, SwDocPositions eEnd, + FindRanges eFlags, const OUString* pReplTempl ) +{ + // no enhancement of existing selections + if(!(eFlags & FindRanges::InSel)) + ClearMark(); + SwTextFormatColl *pColl = GetParaStyle(rTempl, SwWrtShell::GETSTYLE_CREATESOME); + SwTextFormatColl *pReplaceColl = nullptr; + if( pReplTempl ) + pReplaceColl = GetParaStyle(*pReplTempl, SwWrtShell::GETSTYLE_CREATESOME ); + + bool bCancel = false; + sal_Int32 nRet = FindFormat(pColl ? *pColl : GetDfltTextFormatColl(), + eStt,eEnd, bCancel, eFlags, pReplaceColl); + if(bCancel) + { + Undo(); + nRet = SAL_MAX_INT32; + } + return nRet; +} + +// search for attributes + +sal_Int32 SwWrtShell::SearchAttr( const SfxItemSet& rFindSet, bool bNoColls, + SwDocPositions eStart, SwDocPositions eEnd, + FindRanges eFlags, const i18nutil::SearchOptions2* pSearchOpt, + const SfxItemSet* pReplaceSet ) +{ + // no enhancement of existing selections + if (!(eFlags & FindRanges::InSel)) + ClearMark(); + + // Searching + bool bCancel = false; + sal_Int32 nRet = FindAttrs(rFindSet, bNoColls, eStart, eEnd, bCancel, eFlags, pSearchOpt, pReplaceSet); + + if(bCancel) + { + Undo(); + nRet = SAL_MAX_INT32; + } + return nRet; +} + +// Selection modes + +void SwWrtShell::PushMode() +{ + m_pModeStack = new ModeStack( m_pModeStack, m_bIns, m_bExtMode, m_bAddMode, m_bBlockMode ); +} + +void SwWrtShell::PopMode() +{ + if ( nullptr == m_pModeStack ) + return; + + if ( m_bExtMode && !m_pModeStack->bExt ) + LeaveExtMode(); + if ( m_bAddMode && !m_pModeStack->bAdd ) + LeaveAddMode(); + if ( m_bBlockMode && !m_pModeStack->bBlock ) + LeaveBlockMode(); + m_bIns = m_pModeStack->bIns; + + m_pModeStack = std::move(m_pModeStack->pNext); +} + +// Two methods for setting cursors: the first maps at the +// eponymous methods in the CursorShell, the second removes +// all selections at first. + +tools::Long SwWrtShell::SetCursor(const Point *pPt, bool bTextOnly) +{ + // Remove a possibly present selection at the position + // of the mouseclick + + if(!IsInSelect() && TestCurrPam(*pPt)) { + ClearMark(); + } + + return SwCursorShell::SetCursor(*pPt, bTextOnly); +} + +tools::Long SwWrtShell::SetCursorKillSel(const Point *pPt, bool bTextOnly ) +{ + SwActContext aActContext(this); + ResetSelect(pPt,false); + return SwCursorShell::SetCursor(*pPt, bTextOnly); +} + +void SwWrtShell::UnSelectFrame() +{ + // Remove Frame selection with guaranteed invalid position + Point aPt(LONG_MIN, LONG_MIN); + SelectObj(aPt); + SwTransferable::ClearSelection( *this ); +} + +// Remove of all selections + +tools::Long SwWrtShell::ResetSelect(const Point *,bool) +{ + if(IsSelFrameMode()) + { + UnSelectFrame(); + LeaveSelFrameMode(); + } + else + { + // SwActContext opens an Action - + // to avoid problems in the basic process with the + // shell switching, GetChgLnk().Call() may be called + // after EndAction(). + { + SwActContext aActContext(this); + m_bSelWrd = m_bSelLn = false; + KillPams(); + ClearMark(); + m_fnKillSel = &SwWrtShell::Ignore; + m_fnSetCursor = &SwWrtShell::SetCursor; + } + + // After canceling of all selections an update of Attr-Controls + // could be necessary. + GetChgLnk().Call(nullptr); + + if ( GetEnhancedTableSelection() != SwTable::SEARCH_NONE ) + UnsetEnhancedTableSelection(); + } + Invalidate(); + SwTransferable::ClearSelection( *this ); + return 1; +} + +bool SwWrtShell::IsSplitVerticalByDefault() const +{ + return GetDoc()->IsSplitVerticalByDefault(); +} + +void SwWrtShell::SetSplitVerticalByDefault(bool value) +{ + GetDoc()->SetSplitVerticalByDefault(value); +} + +// Do nothing + +tools::Long SwWrtShell::Ignore(const Point *, bool ) { + return 1; +} + +// Begin of a selection process. + +void SwWrtShell::SttSelect() +{ + if(m_bInSelect) + return; + if(!HasMark()) + SetMark(); + if( m_bBlockMode ) + { + SwShellCursor* pTmp = getShellCursor( true ); + if( !pTmp->HasMark() ) + pTmp->SetMark(); + } + m_fnKillSel = &SwWrtShell::Ignore; + m_fnSetCursor = &SwWrtShell::SetCursor; + m_bInSelect = true; + Invalidate(); + SwTransferable::CreateSelection( *this ); +} + +namespace { + +void collectUIInformation(SwShellCursor* pCursor) +{ + EventDescription aDescription; + OUString aSelStart = OUString::number(pCursor->Start()->GetContentIndex()); + OUString aSelEnd = OUString::number(pCursor->End()->GetContentIndex()); + + aDescription.aParameters = {{"START_POS", aSelStart}, {"END_POS", aSelEnd}}; + aDescription.aAction = "SELECT"; + aDescription.aID = "writer_edit"; + aDescription.aKeyWord = "SwEditWinUIObject"; + aDescription.aParent = "MainWindow"; + + UITestLogger::getInstance().logEvent(aDescription); +} + +} + +// End of a selection process. + +void SwWrtShell::EndSelect() +{ + if(m_bInSelect && !m_bExtMode) + { + m_bInSelect = false; + if (m_bAddMode) + { + AddLeaveSelect(); + } + else + { + SttLeaveSelect(); + m_fnSetCursor = &SwWrtShell::SetCursorKillSel; + m_fnKillSel = &SwWrtShell::ResetSelect; + } + } + SwWordCountWrapper *pWrdCnt = static_cast<SwWordCountWrapper*>(GetView().GetViewFrame().GetChildWindow(SwWordCountWrapper::GetChildWindowId())); + if (pWrdCnt) + pWrdCnt->UpdateCounts(); + + collectUIInformation(GetCursor_()); +} + +void SwWrtShell::ExtSelWrd(const Point *pPt, bool ) +{ + SwMvContext aMvContext(this); + if( IsTableMode() ) + return; + + // Bug 66823: actual crsr has in additional mode no selection? + // Then destroy the actual and go to prev, this will be expand + if( !HasMark() && GoPrevCursor() ) + { + bool bHasMark = HasMark(); // that's wrong! + GoNextCursor(); + if( bHasMark ) + { + DestroyCursor(); + GoPrevCursor(); + } + } + + // check the direction of the selection with the new point + bool bMoveCursor = true, bToTop = false; + SwCursorShell::SelectWord( &m_aStart ); // select the startword + SwCursorShell::Push(); // save the cursor + SwCursorShell::SetCursor( *pPt ); // and check the direction + + switch( SwCursorShell::CompareCursorStackMkCurrPt()) + { + case -1: bToTop = false; break; + case 1: bToTop = true; break; + default: bMoveCursor = false; break; + } + + SwCursorShell::Pop(SwCursorShell::PopMode::DeleteCurrent); // restore the saved cursor + + if( !bMoveCursor ) + return; + + // select to Top but cursor select to Bottom? or + // select to Bottom but cursor select to Top? --> swap the cursor + if( bToTop ) + SwapPam(); + + SwCursorShell::Push(); // save cur cursor + if( SwCursorShell::SelectWord( pPt )) // select the current word + { + if( bToTop ) + SwapPam(); + Combine(); + } + else + { + SwCursorShell::Pop(SwCursorShell::PopMode::DeleteCurrent); + if( bToTop ) + SwapPam(); + } +} + +void SwWrtShell::ExtSelLn(const Point *pPt, bool ) +{ + SwMvContext aMvContext(this); + SwCursorShell::SetCursor(*pPt); + if( IsTableMode() ) + return; + + // Bug 66823: actual crsr has in additional mode no selection? + // Then destroy the actual and go to prev, this will be expand + if( !HasMark() && GoPrevCursor() ) + { + bool bHasMark = HasMark(); // that's wrong! + GoNextCursor(); + if( bHasMark ) + { + DestroyCursor(); + GoPrevCursor(); + } + } + + // if applicable fit the selection to the "Mark" + bool bToTop = !IsCursorPtAtEnd(); + SwapPam(); + + // The "Mark" has to be at the end or the beginning of the line. + if( bToTop ? !IsEndSentence() : !IsStartSentence() ) + { + if( bToTop ) + { + if( !IsEndPara() ) + SwCursorShell::Right(1,SwCursorSkipMode::Chars); + SwCursorShell::GoEndSentence(); + } + else + SwCursorShell::GoStartSentence(); + } + SwapPam(); + + if (bToTop) + SwCursorShell::GoStartSentence(); + else + SwCursorShell::GoEndSentence(); +} + +// Back into the standard mode: no mode, no selections. + +void SwWrtShell::EnterStdMode() +{ + if(m_bAddMode) + LeaveAddMode(); + if(m_bBlockMode) + LeaveBlockMode(); + m_bBlockMode = false; + m_bExtMode = false; + m_bInSelect = false; + if(IsSelFrameMode()) + { + UnSelectFrame(); + LeaveSelFrameMode(); + } + else + { + // SwActContext opens and action which has to be + // closed prior to the call of + // GetChgLnk().Call() + SwActContext aActContext(this); + m_bSelWrd = m_bSelLn = false; + if( !IsRetainSelection() ) + KillPams(); + ClearMark(); + m_fnSetCursor = &SwWrtShell::SetCursorKillSel; + m_fnKillSel = &SwWrtShell::ResetSelect; + } + Invalidate(); + SwTransferable::ClearSelection( *this ); +} + +void SwWrtShell::AssureStdMode() +{ + // deselect any drawing or frame and leave editing mode + if (SdrView* pSdrView = GetDrawView()) + { + if (pSdrView->IsTextEdit()) + { + bool bLockView = IsViewLocked(); + LockView(true); + EndTextEdit(); + LockView(bLockView); + } + // go out of the frame + Point aPt(LONG_MIN, LONG_MIN); + SelectObj(aPt, SW_LEAVE_FRAME); + } + if (IsSelFrameMode() || IsObjSelected()) + { + UnSelectFrame(); + LeaveSelFrameMode(); + GetView().LeaveDrawCreate(); + EnterStdMode(); + DrawSelChanged(); + GetView().StopShellTimer(); + } + else + EnterStdMode(); +} + +// Extended Mode + +void SwWrtShell::EnterExtMode() +{ + if(m_bBlockMode) + { + LeaveBlockMode(); + KillPams(); + ClearMark(); + } + m_bExtMode = true; + m_bAddMode = false; + m_bBlockMode = false; + SttSelect(); +} + +void SwWrtShell::LeaveExtMode() +{ + m_bExtMode = false; + EndSelect(); +} + +// End of a selection; if the selection is empty, +// ClearMark(). + +void SwWrtShell::SttLeaveSelect() +{ + if(SwCursorShell::HasSelection() && !IsSelTableCells() && m_bClearMark) { + return; + } + ClearMark(); +} + +// Leaving of the selection mode in additional mode + +void SwWrtShell::AddLeaveSelect() +{ + if(IsTableMode()) LeaveAddMode(); + else if(SwCursorShell::HasSelection()) + CreateCursor(); +} + +// Additional Mode + +void SwWrtShell::EnterAddMode() +{ + if(IsTableMode()) return; + if(m_bBlockMode) + LeaveBlockMode(); + m_fnKillSel = &SwWrtShell::Ignore; + m_fnSetCursor = &SwWrtShell::SetCursor; + m_bAddMode = true; + m_bBlockMode = false; + m_bExtMode = false; + if(SwCursorShell::HasSelection()) + CreateCursor(); + Invalidate(); +} + +void SwWrtShell::LeaveAddMode() +{ + m_fnKillSel = &SwWrtShell::ResetSelect; + m_fnSetCursor = &SwWrtShell::SetCursorKillSel; + m_bAddMode = false; + Invalidate(); +} + +// Block Mode + +void SwWrtShell::EnterBlockMode() +{ + m_bBlockMode = false; + EnterStdMode(); + m_bBlockMode = true; + CursorToBlockCursor(); + Invalidate(); +} + +void SwWrtShell::LeaveBlockMode() +{ + m_bBlockMode = false; + BlockCursorToCursor(); + EndSelect(); + Invalidate(); +} + +// Insert mode + +void SwWrtShell::SetInsMode( bool bOn ) +{ + const bool bDoAsk = officecfg::Office::Common::Misc::QuerySetInsMode::get(); + if (!bOn && bDoAsk) { + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(GetView().GetFrameWeld(), "cui/ui/querysetinsmodedialog.ui")); + std::unique_ptr<weld::Dialog> xQuery(xBuilder->weld_dialog("SetInsModeDialog")); + std::unique_ptr<weld::Image> xImage(xBuilder->weld_image("imSetInsMode")); + std::unique_ptr<weld::CheckButton> xCheckBox(xBuilder->weld_check_button("cbDontShowAgain")); + + xImage->set_from_icon_name(RID_BMP_QUERYINSMODE); + + const int nResult = xQuery->run(); + + std::shared_ptr<comphelper::ConfigurationChanges> xChanges( + comphelper::ConfigurationChanges::create()); + officecfg::Office::Common::Misc::QuerySetInsMode::set(!xCheckBox->get_active(), xChanges); + xChanges->commit(); + + if ( nResult == static_cast<int>(RET_NO) ) + return; + } + m_bIns = bOn; + SwCursorShell::SetOverwriteCursor( !m_bIns ); + const SfxBoolItem aTmp( SID_ATTR_INSERT, m_bIns ); + GetView().GetViewFrame().GetBindings().SetState( aTmp ); + StartAction(); + EndAction(); + Invalidate(); +} +//Overwrite mode is incompatible with red-lining +void SwWrtShell::SetRedlineFlagsAndCheckInsMode( RedlineFlags eMode ) +{ + SetRedlineFlags( eMode ); + if (IsRedlineOn()) + SetInsMode(); +} + +// Edit frame + +void SwWrtShell::BeginFrameDrag(const Point *pPt, bool bIsShift) +{ + m_fnDrag = &SwFEShell::Drag; + if(bStartDrag) + { + Point aTmp( nStartDragX, nStartDragY ); + SwFEShell::BeginDrag( &aTmp, bIsShift ); + } + else + SwFEShell::BeginDrag( pPt, bIsShift ); +} + +void SwWrtShell::EnterSelFrameMode(const Point *pPos) +{ + if(pPos) + { + nStartDragX = pPos->X(); + nStartDragY = pPos->Y(); + bStartDrag = true; + } + m_bLayoutMode = true; + HideCursor(); + + // equal call of BeginDrag in the SwFEShell + m_fnDrag = &SwWrtShell::BeginFrameDrag; + m_fnEndDrag = &SwWrtShell::UpdateLayoutFrame; + SwBaseShell::SetFrameMode( FLY_DRAG_START, this ); + Invalidate(); +} + +void SwWrtShell::LeaveSelFrameMode() +{ + m_fnDrag = &SwWrtShell::BeginDrag; + m_fnEndDrag = &SwWrtShell::DefaultEndDrag; + m_bLayoutMode = false; + bStartDrag = false; + Edit(); + SwBaseShell::SetFrameMode( FLY_DRAG_END, this ); + Invalidate(); +} + +// Description: execute framebound macro + +IMPL_LINK( SwWrtShell, ExecFlyMac, const SwFlyFrameFormat*, pFlyFormat, void ) +{ + const SwFrameFormat *pFormat = pFlyFormat ? static_cast<const SwFrameFormat*>(pFlyFormat) : GetFlyFrameFormat(); + OSL_ENSURE(pFormat, "no frame format"); + const SvxMacroItem &rFormatMac = pFormat->GetMacro(); + + if(rFormatMac.HasMacro(SvMacroItemId::SwObjectSelect)) + { + const SvxMacro &rMac = rFormatMac.GetMacro(SvMacroItemId::SwObjectSelect); + if( IsFrameSelected() ) + m_bLayoutMode = true; + CallChgLnk(); + ExecMacro( rMac ); + } +} + +void SwWrtShell::UpdateLayoutFrame(const Point *, bool ) +{ + // still a dummy + SwFEShell::EndDrag(); + m_fnDrag = &SwWrtShell::BeginFrameDrag; +} + +// Handler for toggling the modes. Returns back the old mode. + +void SwWrtShell::ToggleAddMode() +{ + m_bAddMode ? LeaveAddMode(): EnterAddMode(); + Invalidate(); +} + +void SwWrtShell::ToggleBlockMode() +{ + m_bBlockMode ? LeaveBlockMode(): EnterBlockMode(); + Invalidate(); +} + +void SwWrtShell::ToggleExtMode() +{ + m_bExtMode ? LeaveExtMode() : EnterExtMode(); + Invalidate(); +} + +// Dragging in standard mode (Selecting of content) + +void SwWrtShell::BeginDrag(const Point * /*pPt*/, bool ) +{ + if(m_bSelWrd) + { + m_bInSelect = true; + if( !IsCursorPtAtEnd() ) + SwapPam(); + + m_fnDrag = &SwWrtShell::ExtSelWrd; + m_fnSetCursor = &SwWrtShell::Ignore; + } + else if(m_bSelLn) + { + m_bInSelect = true; + m_fnDrag = &SwWrtShell::ExtSelLn; + m_fnSetCursor = &SwWrtShell::Ignore; + } + else + { + m_fnDrag = &SwWrtShell::DefaultDrag; + SttSelect(); + } +} + +void SwWrtShell::DefaultDrag(const Point *, bool ) +{ + if( IsSelTableCells() ) + m_aSelTableLink.Call(*this); +} + +void SwWrtShell::DefaultEndDrag(const Point * /*pPt*/, bool ) +{ + m_fnDrag = &SwWrtShell::BeginDrag; + if( IsExtSel() ) + LeaveExtSel(); + + if( IsSelTableCells() ) + m_aSelTableLink.Call(*this); + EndSelect(); +} + +// #i32329# Enhanced table selection +bool SwWrtShell::SelectTableRowCol( const Point& rPt, const Point* pEnd, bool bRowDrag ) +{ + SwMvContext aMvContext(this); + SttSelect(); + if(SelTableRowCol( rPt, pEnd, bRowDrag )) + { + m_fnSetCursor = &SwWrtShell::SetCursorKillSel; + m_fnKillSel = &SwWrtShell::ResetSelect; + return true; + } + return false; +} + +// Description: Selection of a table line or column + +void SwWrtShell::SelectTableRow() +{ + if ( SelTableRow() ) + { + m_fnSetCursor = &SwWrtShell::SetCursorKillSel; + m_fnKillSel = &SwWrtShell::ResetSelect; + } +} + +void SwWrtShell::SelectTableCol() +{ + if ( SelTableCol() ) + { + m_fnSetCursor = &SwWrtShell::SetCursorKillSel; + m_fnKillSel = &SwWrtShell::ResetSelect; + } +} + +void SwWrtShell::SelectTableCell() +{ + if ( SelTableBox() ) + { + m_fnSetCursor = &SwWrtShell::SetCursorKillSel; + m_fnKillSel = &SwWrtShell::ResetSelect; + } +} + +// Description: Check if a word selection is present. +// According to the rules for intelligent cut / paste +// surrounding spaces are cut out. +// Return: Delivers the type of the word selection. + +int SwWrtShell::IntelligentCut(SelectionType nSelection, bool bCut) +{ + // On multiple selection no intelligent drag and drop + // there are multiple cursors, since a second was placed + // already at the target position. + if( IsAddMode() || !(nSelection & SelectionType::Text) ) + return NO_WORD; + + OUString sText; + CharClass& rCC = GetAppCharClass(); + + // If the first character is no word character, + // no word selected. + sal_Unicode cPrev = GetChar(false); + sal_Unicode cNext = GetChar(true, -1); + if( !cPrev || !cNext || + !rCC.isLetterNumeric( ( sText = OUString(cPrev) ), 0 ) || + !rCC.isLetterNumeric( ( sText = OUString(cNext) ), 0 ) ) + return NO_WORD; + + cPrev = GetChar(false, -1); + cNext = GetChar(); + + int cWord = NO_WORD; + // is a word selected? + if (cPrev && cNext && + CH_TXTATR_BREAKWORD != cPrev && CH_TXTATR_INWORD != cPrev && + CH_TXTATR_BREAKWORD != cNext && CH_TXTATR_INWORD != cNext && + !rCC.isLetterNumeric( ( sText = OUString(cPrev) ), 0 ) && + !rCC.isLetterNumeric( ( sText = OUString(cNext) ), 0 ) ) + cWord = WORD_NO_SPACE; + + if(cWord == WORD_NO_SPACE && ' ' == cPrev ) + { + cWord = WORD_SPACE_BEFORE; + // delete the space before + if(bCut) + { + Push(); + if(IsCursorPtAtEnd()) + SwapPam(); + ClearMark(); + SetMark(); + SwCursorShell::Left(1,SwCursorSkipMode::Chars); + SwFEShell::Delete(true); + Pop(SwCursorShell::PopMode::DeleteCurrent); + } + } + else if(cWord == WORD_NO_SPACE && cNext == ' ') + { + cWord = WORD_SPACE_AFTER; + // delete the space behind + if(bCut) { + Push(); + if(!IsCursorPtAtEnd()) SwapPam(); + ClearMark(); + SetMark(); + SwCursorShell::Right(1,SwCursorSkipMode::Chars); + SwFEShell::Delete(true); + Pop(SwCursorShell::PopMode::DeleteCurrent); + } + } + return cWord; +} + + // jump to the next / previous hyperlink - inside text and also + // on graphics +void SwWrtShell::SelectNextPrevHyperlink( bool bNext ) +{ + StartAction(); + bool bRet = SwCursorShell::SelectNxtPrvHyperlink( bNext ); + if( !bRet ) // didn't find? wrap and check again + { + SwShellCursor* pCursor = GetCursor_(); + SwCursorSaveState aSaveState(*pCursor); + EnterStdMode(); + if( bNext ) + SttEndDoc(true); + else + SttEndDoc(false); + bRet = SwCursorShell::SelectNxtPrvHyperlink(bNext); + if (!bRet) // didn't find again? restore cursor position and bail + { + pCursor->RestoreSavePos(); + EndAction(true); // don't scroll to restored cursor position + return; + } + } + EndAction(); + + bool bCreateXSelection = false; + const bool bFrameSelected = IsFrameSelected() || IsObjSelected(); + if( IsSelection() ) + { + if ( bFrameSelected ) + UnSelectFrame(); + + // Set the function pointer for the canceling of the selection + // set at cursor + m_fnKillSel = &SwWrtShell::ResetSelect; + m_fnSetCursor = &SwWrtShell::SetCursorKillSel; + bCreateXSelection = true; + } + else if( bFrameSelected ) + { + EnterSelFrameMode(); + bCreateXSelection = true; + } + else if( (CNT_GRF | CNT_OLE ) & GetCntType() ) + { + SelectObj( GetCharRect().Pos() ); + EnterSelFrameMode(); + bCreateXSelection = true; + } + + if( bCreateXSelection ) + SwTransferable::CreateSelection( *this ); +} + +// For the preservation of the selection the cursor will be moved left +// after SetMark(), so that the cursor is not moved by inserting text. +// Because a present selection at the CORE page is cleared at the +// current cursor position, the cursor will be pushed on the stack. +// After moving, they will again resummarized. + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/wrtsh/wrtsh1.cxx b/sw/source/uibase/wrtsh/wrtsh1.cxx new file mode 100644 index 0000000000..4b2402fc93 --- /dev/null +++ b/sw/source/uibase/wrtsh/wrtsh1.cxx @@ -0,0 +1,2731 @@ +/* -*- 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 <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/embed/EmbedMisc.hpp> +#include <com/sun/star/embed/EmbedStates.hpp> +#include <com/sun/star/embed/EmbedVerbs.hpp> +#include <com/sun/star/embed/NoVisualAreaSizeException.hpp> +#include <com/sun/star/chart2/XChartDocument.hpp> +#include <com/sun/star/util/XModifiable.hpp> +#include <com/sun/star/lang/XInitialization.hpp> + +#include <hintids.hxx> +#include <sot/exchange.hxx> +#include <svx/xfillit0.hxx> +#include <svx/hdft.hxx> +#include <svx/svdview.hxx> +#include <svl/itemiter.hxx> +#include <tools/bigint.hxx> +#include <svtools/insdlg.hxx> +#include <sfx2/ipclient.hxx> +#include <editeng/formatbreakitem.hxx> +#include <editeng/svxacorr.hxx> +#include <editeng/ulspitem.hxx> +#include <vcl/graph.hxx> +#include <unotools/charclass.hxx> +#include <comphelper/storagehelper.hxx> +#include <svx/svxdlg.hxx> +#include <svx/extrusionbar.hxx> +#include <svx/fontworkbar.hxx> +#include <dialoghelp.hxx> +#include <frmfmt.hxx> +#include <fmtftn.hxx> +#include <fmthdft.hxx> +#include <fmtpdsc.hxx> +#include <txtfrm.hxx> +#include <wdocsh.hxx> +#include <swmodule.hxx> +#include <wrtsh.hxx> +#include <view.hxx> +#include <cmdid.h> +#include <pagedesc.hxx> +#include <frmmgr.hxx> +#include <swundo.hxx> +#include <swcli.hxx> +#include <poolfmt.hxx> +#include <edtwin.hxx> +#include <fmtcol.hxx> +#include <swtable.hxx> +#include <viscrs.hxx> +#include <swdtflvr.hxx> +#include <doc.hxx> +#include <IDocumentSettingAccess.hxx> +#include <SwCapObjType.hxx> +#include <SwStyleNameMapper.hxx> +#include <sfx2/request.hxx> +#include <paratr.hxx> +#include <ndtxt.hxx> +#include <editeng/acorrcfg.hxx> +#include <IMark.hxx> +#include <sfx2/bindings.hxx> +#include <flyfrm.hxx> + +// -> #111827# +#include <SwRewriter.hxx> +#include <strings.hrc> +// <- #111827# + +#include <toolkit/helper/vclunohelper.hxx> +#include <sfx2/viewfrm.hxx> +#include <vcl/uitest/logger.hxx> +#include <vcl/uitest/eventdescription.hxx> +#include <osl/diagnose.h> +#include <o3tl/unit_conversion.hxx> +#include <officecfg/Office/Common.hxx> + +#include <PostItMgr.hxx> +#include <FrameControlsManager.hxx> +#include <fldmgr.hxx> +#include <docufld.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <fmtfld.hxx> + +#include <sfx2/msgpool.hxx> +#include <sfx2/msg.hxx> +#include <svtools/embedhlp.hxx> +#include <svtools/strings.hrc> +#include <svtools/svtresid.hxx> +#include <svx/postattr.hxx> +#include <comphelper/lok.hxx> +#include <comphelper/propertyvalue.hxx> +#include <svtools/optionsdrawinglayer.hxx> +#include <svl/numformat.hxx> +#include <svl/zformat.hxx> +#include <memory> + +#include "../../core/crsr/callnk.hxx" +#include <frmtool.hxx> +#include <viewopt.hxx> + +#include <IDocumentUndoRedo.hxx> +#include <UndoInsert.hxx> +#include <UndoCore.hxx> +#include <formatlinebreak.hxx> +#include <formatcontentcontrol.hxx> +#include <textcontentcontrol.hxx> + +using namespace sw::mark; +using namespace com::sun::star; +namespace { + +void collectUIInformation(const OUString& rAction, const OUString& aParameters) +{ + EventDescription aDescription; + aDescription.aAction = rAction; + aDescription.aParameters = {{"parameters", aParameters}}; + aDescription.aID = "writer_edit"; + aDescription.aKeyWord = "SwEditWinUIObject"; + aDescription.aParent = "MainWindow"; + UITestLogger::getInstance().logEvent(aDescription); +} + +} + +sal_uInt32 MakeAllOutlineContentTemporarilyVisible::nLock = 0; + +static bool lcl_IsAllowed(const SwWrtShell* rSh) +{ + if (rSh->GetViewOptions()->IsShowOutlineContentVisibilityButton() && rSh->IsEndPara()) + { + SwTextNode* pTextNode = rSh->GetCursor()->GetPointNode().GetTextNode(); + if (pTextNode && pTextNode->IsOutline()) + { + // disallow if this is an outline node having folded content + bool bVisible = true; + pTextNode->GetAttrOutlineContentVisible(bVisible); + if (!bVisible) + return false; + } + } + return true; +} + +#define BITFLD_INI_LIST \ + m_bClearMark = \ + m_bIns = true;\ + m_bAddMode = \ + m_bBlockMode = \ + m_bExtMode = \ + m_bInSelect = \ + m_bLayoutMode = \ + m_bSelWrd = \ + m_bSelLn = \ + m_bRetainSelection = false; \ + m_bIsInClickToEdit = false; + +static SvxAutoCorrect* lcl_IsAutoCorr() +{ + SvxAutoCorrect* pACorr = SvxAutoCorrCfg::Get().GetAutoCorrect(); + if( pACorr && !pACorr->IsAutoCorrFlag( ACFlags::CapitalStartSentence | ACFlags::CapitalStartWord | + ACFlags::AddNonBrkSpace | ACFlags::ChgOrdinalNumber | ACFlags::TransliterateRTL | + ACFlags::ChgToEnEmDash | ACFlags::SetINetAttr | ACFlags::Autocorrect | + ACFlags::SetDOIAttr )) + pACorr = nullptr; + return pACorr; +} + +void SwWrtShell::NoEdit(bool bHideCursor) +{ + if(bHideCursor) + HideCursor(); +} + +void SwWrtShell::Edit() +{ + if (CanInsert()) + { + ShowCursor(); + } +} + +bool SwWrtShell::IsEndWrd() +{ + SwMvContext aMvContext(this); + if(IsEndPara() && !IsSttPara()) + return true; + + return IsEndWord(); +} + +// Insert string +void SwWrtShell::InsertByWord( const OUString & rStr) +{ + if( rStr.isEmpty() ) + return; + + bool bDelim = GetAppCharClass().isLetterNumeric( rStr, 0 ); + sal_Int32 nPos = 0, nStt = 0; + for( ; nPos < rStr.getLength(); nPos++ ) + { + bool bTmpDelim = GetAppCharClass().isLetterNumeric( rStr, nPos ); + if( bTmpDelim != bDelim ) + { + Insert( rStr.copy( nStt, nPos - nStt )); + nStt = nPos; + } + } + if( nStt != nPos ) + Insert( rStr.copy( nStt, nPos - nStt )); +} + +void SwWrtShell::Insert( const OUString &rStr ) +{ + ResetCursorStack(); + if( !CanInsert() ) + return; + + bool bStarted = false; + bool bHasSel = HasSelection(), + bCallIns = m_bIns /*|| bHasSel*/; + bool bDeleted = false; + + if( bHasSel || ( !m_bIns && IsInHiddenRange(/*bSelect=*/true) ) ) + { + // Only here parenthesizing, because the normal + // insert is already in parentheses at Editshell. + StartAllAction(); + + SwRewriter aRewriter; + + aRewriter.AddRule(UndoArg1, GetCursorDescr()); + aRewriter.AddRule(UndoArg2, SwResId(STR_YIELDS)); + { + OUString aTmpStr = SwResId(STR_START_QUOTE) + + rStr + SwResId(STR_END_QUOTE); + + aRewriter.AddRule(UndoArg3, aTmpStr); + } + + StartUndo(SwUndoId::REPLACE, &aRewriter); + bStarted = true; + Push(); + // let's interpret a selection within the same node as "replace" + bDeleted = DelRight(GetCursor()->GetPoint()->GetNode() == GetCursor()->GetMark()->GetNode()); + Pop(SwCursorShell::PopMode::DeleteCurrent); // Restore selection (if tracking changes) + NormalizePam(false); // tdf#127635 put point at the end of deletion + ClearMark(); + } + + bCallIns ? + SwEditShell::Insert2( rStr, bDeleted ) : SwEditShell::Overwrite( rStr ); + + // Check whether node is content control + SwTextContentControl* pTextContentControl = CursorInsideContentControl(); + if (pTextContentControl) + { + std::shared_ptr<SwContentControl> pContentControl = + pTextContentControl->GetContentControl().GetContentControl(); + if (pContentControl) + { + // Set showingPlcHdr to false as node has been edited + pContentControl->SetShowingPlaceHolder(false); + } + } + + if( bStarted ) + { + EndUndo(); + EndAllAction(); + } +} + +// Maximum height limit not possible, because the maximum height +// of the current frame can not be obtained. + +void SwWrtShell::InsertGraphic( const OUString &rPath, const OUString &rFilter, + const Graphic &rGrf, SwFlyFrameAttrMgr *pFrameMgr, + RndStdIds nAnchorType ) +{ + ResetCursorStack(); + if ( !CanInsert() ) + return; + + StartAllAction(); + + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, SwResId(STR_GRAPHIC)); + + StartUndo(SwUndoId::INSERT, &aRewriter); + + if ( HasSelection() ) + DelRight(); + // Inserted graphics in its own paragraph, + // if at the end of a non-empty paragraph. + //For i120928,avoid to split node + + EnterSelFrameMode(); + + bool bSetGrfSize = true; + bool bOwnMgr = false; + + if ( !pFrameMgr ) + { + bOwnMgr = true; + pFrameMgr = new SwFlyFrameAttrMgr( true, this, Frmmgr_Type::GRF, nullptr ); + + // CAUTION + // GetAttrSet makes an adjustment + // While pasting is a SwFrameSize present + // because of the DEF-Framesize + // These must be removed explicitly for the optimal size. + pFrameMgr->DelAttr(RES_FRM_SIZE); + + if (nAnchorType != RndStdIds::FLY_AT_PARA) + // Something other than at-para was requested. + pFrameMgr->SetAnchor(nAnchorType); + } + else + { + Size aSz( pFrameMgr->GetSize() ); + if ( !aSz.Width() || !aSz.Height() ) + { + aSz.setWidth(o3tl::toTwips(1, o3tl::Length::cm)); + aSz.setHeight(o3tl::toTwips(1, o3tl::Length::cm)); + pFrameMgr->SetSize( aSz ); + } + else if ( aSz.Width() != DFLT_WIDTH && aSz.Height() != DFLT_HEIGHT ) + bSetGrfSize = false; + + pFrameMgr->SetHeightSizeType(SwFrameSize::Fixed); + } + + // during change tracking, insert the image anchored as character + // (to create an SwRangeRedline on its anchor point) + if ( IsRedlineOn() && nAnchorType != RndStdIds::FLY_AS_CHAR ) + pFrameMgr->SetAnchor( RndStdIds::FLY_AS_CHAR ); + + // Insert the graphic + SwFEShell::Insert(rPath, rFilter, &rGrf, &pFrameMgr->GetAttrSet()); + if ( bOwnMgr ) + pFrameMgr->UpdateAttrMgr(); + + if( bSetGrfSize ) + { + Size aSizePixel = rGrf.GetSizePixel(); + Size aBound = GetGraphicDefaultSize(); + + sal_Int32 nPreferredDPI = mxDoc->getIDocumentSettingAccess().getImagePreferredDPI(); + Size aGrfSize; + + if (nPreferredDPI > 0) + { + auto nWidth = o3tl::toTwips(aSizePixel.Width() / double(nPreferredDPI), o3tl::Length::in); + auto nHeight = o3tl::toTwips(aSizePixel.Height() / double(nPreferredDPI), o3tl::Length::in); + aGrfSize = Size(nWidth, nHeight); + } + else + { + GetGrfSize(aGrfSize); + } + + // Add the margin attributes to GrfSize, + // because these counts at the margin additionally + aGrfSize.AdjustWidth(pFrameMgr->CalcWidthBorder() ); + aGrfSize.AdjustHeight(pFrameMgr->CalcHeightBorder() ); + + const BigInt aTempWidth( aGrfSize.Width() ); + const BigInt aTempHeight( aGrfSize.Height()); + + // Fit width if necessary, scale down the height proportional thereafter. + if( aGrfSize.Width() > aBound.Width() ) + { + aGrfSize.setWidth( aBound.Width() ); + aGrfSize.setHeight( BigInt(aBound.Width()) * aTempHeight / aTempWidth ); + } + // Fit height if necessary, scale down the width proportional thereafter. + if( aGrfSize.Height() > aBound.Height() ) + { + aGrfSize.setHeight( aBound.Height() ); + aGrfSize.setWidth( BigInt(aBound.Height()) * aTempWidth / aTempHeight ); + } + pFrameMgr->SetSize( aGrfSize ); + pFrameMgr->UpdateFlyFrame(); + } + if ( bOwnMgr ) + delete pFrameMgr; + + EndUndo(); + EndAllAction(); +} + +// Insert an OLE-Object into the CORE. +// if no object is transferred, then one will be created. + +void SwWrtShell::InsertObject( const svt::EmbeddedObjectRef& xRef, SvGlobalName const *pName, + sal_uInt16 nSlotId ) +{ + ResetCursorStack(); + if( !CanInsert() ) + return; + + if( !xRef.is() ) + { + // temporary storage + svt::EmbeddedObjectRef xObj; + uno::Reference < embed::XStorage > xStor = comphelper::OStorageHelper::GetTemporaryStorage(); + bool bDoVerb = true; + if ( pName ) + { + comphelper::EmbeddedObjectContainer aCnt( xStor ); + OUString aName; + // TODO/LATER: get aspect? + xObj.Assign( aCnt.CreateEmbeddedObject( pName->GetByteSequence(), aName ), embed::Aspects::MSOLE_CONTENT ); + } + else + { + SvObjectServerList aServerList; + switch (nSlotId) + { + case SID_INSERT_OBJECT: + { + if (officecfg::Office::Common::Security::Scripting::DisableActiveContent::get()) + { + std::unique_ptr<weld::MessageDialog> xError( + Application::CreateMessageDialog( + nullptr, VclMessageType::Warning, VclButtonsType::Ok, + SvtResId(STR_WARNING_ACTIVE_CONTENT_DISABLED))); + xError->run(); + break; + } + aServerList.FillInsertObjects(); + aServerList.Remove( SwDocShell::Factory().GetClassId() ); + [[fallthrough]]; + } + + // TODO/LATER: recording! Convert properties to items + case SID_INSERT_FLOATINGFRAME: + { + SfxSlotPool* pSlotPool = SW_MOD()->GetSlotPool(); + const SfxSlot* pSlot = pSlotPool->GetSlot(nSlotId); + OUString aCmd = pSlot->GetCommand(); + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractInsertObjectDialog> pDlg(pFact->CreateInsertObjectDialog(GetFrameWeld(mxDoc->GetDocShell()), + aCmd, xStor, &aServerList)); + if (pDlg) + { + pDlg->Execute(); + bDoVerb = pDlg->IsCreateNew(); + OUString aIconMediaType; + uno::Reference< io::XInputStream > xIconMetaFile = pDlg->GetIconIfIconified( &aIconMediaType ); + xObj.Assign( pDlg->GetObject(), + xIconMetaFile.is() ? embed::Aspects::MSOLE_ICON : embed::Aspects::MSOLE_CONTENT ); + if ( xIconMetaFile.is() ) + xObj.SetGraphicStream( xIconMetaFile, aIconMediaType ); + } + + break; + } + + default: + break; + } + } + + if ( xObj.is() ) + { + if( InsertOleObject( xObj ) && bDoVerb ) + { + SfxInPlaceClient* pClient = GetView().FindIPClient( xObj.GetObject(), &GetView().GetEditWin() ); + if ( !pClient ) + { + pClient = new SwOleClient( &GetView(), &GetView().GetEditWin(), xObj ); + SetCheckForOLEInCaption( true ); + } + + if ( xObj.GetViewAspect() == embed::Aspects::MSOLE_ICON ) + { + SwRect aArea = GetAnyCurRect( CurRectType::FlyEmbeddedPrt, nullptr, xObj.GetObject() ); + aArea.Pos() += GetAnyCurRect( CurRectType::FlyEmbedded, nullptr, xObj.GetObject() ).Pos(); + MapMode aMapMode( MapUnit::MapTwip ); + Size aSize = xObj.GetSize( &aMapMode ); + aArea.Width( aSize.Width() ); + aArea.Height( aSize.Height() ); + RequestObjectResize( aArea, xObj.GetObject() ); + } + else + CalcAndSetScale( xObj ); + + //#50270# We don't need to handle error, this is handled by the + //DoVerb in the SfxViewShell + pClient->DoVerb(embed::EmbedVerbs::MS_OLEVERB_SHOW); + + // TODO/LATER: set document name - should be done in Client + } + } + } + else + { + if( HasSelection() ) + DelRight(); + InsertOleObject( xRef ); + } +} + +// Insert object into the Core. +// From ClipBoard or Insert + +bool SwWrtShell::InsertOleObject( const svt::EmbeddedObjectRef& xRef, SwFlyFrameFormat **pFlyFrameFormat ) +{ + //tdf#125100 Ensure that ole object is initially shown as pictogram + comphelper::EmbeddedObjectContainer& rEmbeddedObjectContainer = mxDoc->GetDocShell()->getEmbeddedObjectContainer(); + bool bSaveUserAllowsLinkUpdate = rEmbeddedObjectContainer.getUserAllowsLinkUpdate(); + rEmbeddedObjectContainer.setUserAllowsLinkUpdate(true); + + ResetCursorStack(); + StartAllAction(); + + StartUndo(SwUndoId::INSERT); + + //Some differences between StarMath and any other objects: + //1. Selections should be deleted. For StarMath the Text should be + // passed to the Object + //2. If the cursor is at the end of a non empty paragraph a paragraph + // break should be inserted. StarMath objects are character bound and + // no break should be inserted. + //3. If an selection is passed to a StarMath object, this object should + // not be activated. false should be returned then. + bool bStarMath = true; + bool bActivate = true; + + // set parent to get correct VisArea(in case of object needing parent printer) + uno::Reference < container::XChild > xChild( xRef.GetObject(), uno::UNO_QUERY ); + if ( xChild.is() ) + xChild->setParent( mxDoc->GetDocShell()->GetModel() ); + + SvGlobalName aCLSID( xRef->getClassID() ); + bStarMath = ( SotExchange::IsMath( aCLSID ) != 0 ); + if( IsSelection() ) + { + if( bStarMath ) + { + OUString aMathData; + GetSelectedText( aMathData, ParaBreakType::ToOnlyCR ); + + if( !aMathData.isEmpty() && svt::EmbeddedObjectRef::TryRunningState( xRef.GetObject() ) ) + { + uno::Reference < beans::XPropertySet > xSet( xRef->getComponent(), uno::UNO_QUERY ); + if ( xSet.is() ) + { + try + { + xSet->setPropertyValue("Formula", uno::Any( aMathData ) ); + bActivate = false; + } + catch (const uno::Exception&) + { + } + } + } + } + DelRight(); + } + + if ( !bStarMath ) + SwFEShell::SplitNode( false, false ); + + EnterSelFrameMode(); + + const SvGlobalName* pName = nullptr; + SvGlobalName aObjClsId; + if (xRef.is()) + { + aObjClsId = SvGlobalName(xRef.GetObject()->getClassID()); + pName = &aObjClsId; + } + SwFlyFrameAttrMgr aFrameMgr( true, this, Frmmgr_Type::OLE, pName ); + aFrameMgr.SetHeightSizeType(SwFrameSize::Fixed); + + SwRect aBound; + CalcBoundRect( aBound, aFrameMgr.GetAnchor() ); + + //The Size should be suggested by the OLE server + MapMode aMapMode( MapUnit::MapTwip ); + Size aSz = xRef.GetSize( &aMapMode ); + + //Object size can be limited + if ( aSz.Width() > aBound.Width() ) + { + //Always limit proportional. + aSz.setHeight( aSz.Height() * aBound.Width() / aSz.Width() ); + aSz.setWidth( aBound.Width() ); + } + aFrameMgr.SetSize( aSz ); + SwFlyFrameFormat *pFormat = SwFEShell::InsertObject( xRef, &aFrameMgr.GetAttrSet() ); + + // --> #i972# + if ( bStarMath && mxDoc->getIDocumentSettingAccess().get( DocumentSettingId::MATH_BASELINE_ALIGNMENT ) ) + AlignFormulaToBaseline( xRef.GetObject() ); + + if (pFlyFrameFormat) + *pFlyFrameFormat = pFormat; + + if ( SotExchange::IsChart( aCLSID ) ) + { + uno::Reference< embed::XEmbeddedObject > xEmbeddedObj = xRef.GetObject(); + if ( xEmbeddedObj.is() ) + { + bool bDisableDataTableDialog = false; + svt::EmbeddedObjectRef::TryRunningState( xEmbeddedObj ); + uno::Reference< beans::XPropertySet > xProps( xEmbeddedObj->getComponent(), uno::UNO_QUERY ); + if ( xProps.is() && + ( xProps->getPropertyValue("DisableDataTableDialog") >>= bDisableDataTableDialog ) && + bDisableDataTableDialog ) + { + xProps->setPropertyValue("DisableDataTableDialog", + uno::Any( false ) ); + xProps->setPropertyValue("DisableComplexChartTypes", + uno::Any( false ) ); + uno::Reference< util::XModifiable > xModifiable( xProps, uno::UNO_QUERY ); + if ( xModifiable.is() ) + { + xModifiable->setModified( true ); + } + } + } + } + + EndAllAction(); + GetView().AutoCaption(OLE_CAP, &aCLSID); + + SwRewriter aRewriter; + + if ( bStarMath ) + aRewriter.AddRule(UndoArg1, SwResId(STR_MATH_FORMULA)); + else if ( SotExchange::IsChart( aCLSID ) ) + aRewriter.AddRule(UndoArg1, SwResId(STR_CHART)); + else + aRewriter.AddRule(UndoArg1, SwResId(STR_OLE)); + + EndUndo(SwUndoId::INSERT, &aRewriter); + + rEmbeddedObjectContainer.setUserAllowsLinkUpdate(bSaveUserAllowsLinkUpdate); + + return bActivate; +} + +// The current selected OLE object will be loaded with the +// verb into the server. +void SwWrtShell::LaunchOLEObj(sal_Int32 nVerb) +{ + if ( GetCntType() != CNT_OLE || + GetView().GetViewFrame().GetFrame().IsInPlace() ) + return; + + svt::EmbeddedObjectRef& xRef = GetOLEObject(); + OSL_ENSURE( xRef.is(), "OLE not found" ); + + // LOK: we don't want to handle any other embedded objects than + // charts, there are too many problems with eg. embedded spreadsheets + // (like it creates a separate view for the calc sheet) + if (comphelper::LibreOfficeKit::isActive()) + { + const auto classId = xRef->getClassID(); + if (!SotExchange::IsChart(classId) && !SotExchange::IsMath(classId)) + return; + } + + SfxInPlaceClient* pCli = GetView().FindIPClient( xRef.GetObject(), &GetView().GetEditWin() ); + if ( !pCli ) + pCli = new SwOleClient( &GetView(), &GetView().GetEditWin(), xRef ); + + uno::Reference<lang::XInitialization> xOLEInit(xRef.GetObject(), uno::UNO_QUERY); + if (xOLEInit.is()) + { + uno::Sequence<beans::PropertyValue> aArguments + = { comphelper::makePropertyValue("ReadOnly", pCli->IsProtected()) }; + xOLEInit->initialize({ uno::Any(aArguments) }); + } + + static_cast<SwOleClient*>(pCli)->SetInDoVerb( true ); + + CalcAndSetScale( xRef ); + pCli->DoVerb( nVerb ); + + static_cast<SwOleClient*>(pCli)->SetInDoVerb( false ); + CalcAndSetScale( xRef ); +} + +void SwWrtShell::MoveObjectIfActive( svt::EmbeddedObjectRef& xObj, const Point& rOffset ) +{ + try + { + sal_Int32 nState = xObj->getCurrentState(); + if ( nState == css::embed::EmbedStates::INPLACE_ACTIVE + || nState == css::embed::EmbedStates::UI_ACTIVE ) + { + SfxInPlaceClient* pCli = + GetView().FindIPClient( xObj.GetObject(), &(GetView().GetEditWin()) ); + if ( pCli ) + { + tools::Rectangle aArea = pCli->GetObjArea(); + aArea += rOffset; + pCli->SetObjArea( aArea ); + } + } + } + catch (const uno::Exception&) + { + } +} + +void SwWrtShell::CalcAndSetScale( svt::EmbeddedObjectRef& xObj, + const SwRect *pFlyPrtRect, + const SwRect *pFlyFrameRect, + const bool bNoTextFramePrtAreaChanged ) +{ + // Setting the scale of the client. This arises from the difference + // between the VisArea of the object and the ObjArea. + OSL_ENSURE( xObj.is(), "ObjectRef not valid" ); + + sal_Int64 nAspect = xObj.GetViewAspect(); + if ( nAspect == embed::Aspects::MSOLE_ICON ) + return; // the replacement image is completely controlled by container in this case + + sal_Int64 nMisc = 0; + bool bLinkingChart = false; + + try + { + nMisc = xObj->getStatus( nAspect ); + + // This can surely only be a non-active object, if desired they + // get the new size set as VisArea (StarChart). + if( embed::EmbedMisc::MS_EMBED_RECOMPOSEONRESIZE & nMisc ) + { + // TODO/MBA: testing + SwRect aRect( pFlyPrtRect ? *pFlyPrtRect + : GetAnyCurRect( CurRectType::FlyEmbeddedPrt, nullptr, xObj.GetObject() )); + if( !aRect.IsEmpty() ) + { + // TODO/LEAN: getMapUnit can switch object to running state + // xObj.TryRunningState(); + + MapUnit aUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( nAspect ) ); + + // TODO/LATER: needs complete VisArea?! + Size aSize( OutputDevice::LogicToLogic(aRect.SVRect(), MapMode(MapUnit::MapTwip), MapMode(aUnit)).GetSize() ); + awt::Size aSz; + aSz.Width = aSize.Width(); + aSz.Height = aSize.Height(); + + // Action 'setVisualAreaSize' doesn't have to turn on the + // modified state of the document, either. + bool bModified = false; + uno::Reference<util::XModifiable> xModifiable(xObj->getComponent(), uno::UNO_QUERY); + if (xModifiable.is()) + bModified = xModifiable->isModified(); + xObj->setVisualAreaSize( nAspect, aSz ); + xModifiable.set(xObj->getComponent(), uno::UNO_QUERY); + if (xModifiable.is() && xModifiable->isModified() && !bModified) + xModifiable->setModified(bModified); + + // #i48419# - action 'UpdateReplacement' doesn't + // have to change the modified state of the document. + // This is only a workaround for the defect, that this action + // modifies a document after load, because unnecessarily the + // replacement graphic is updated, in spite of the fact that + // nothing has been changed. + // If the replacement graphic changes by this action, the document + // will be already modified via other mechanisms. + { + bool bResetEnableSetModified(false); + if ( GetDoc()->GetDocShell()->IsEnableSetModified() ) + { + GetDoc()->GetDocShell()->EnableSetModified( false ); + bResetEnableSetModified = true; + } + + //#i79576# don't destroy chart replacement images on load + //#i79578# don't request a new replacement image for charts to often + //a chart sends a modified call to the framework if it was changed + //thus the replacement update is already handled elsewhere + if ( !SotExchange::IsChart( xObj->getClassID() ) ) + xObj.UpdateReplacement(); + + if ( bResetEnableSetModified ) + { + GetDoc()->GetDocShell()->EnableSetModified(); + } + } + } + + // TODO/LATER: this is only a workaround, + uno::Reference< chart2::XChartDocument > xChartDocument( xObj->getComponent(), uno::UNO_QUERY ); + bLinkingChart = ( xChartDocument.is() && !xChartDocument->hasInternalDataProvider() ); + } + } + catch (const uno::Exception&) + { + // TODO/LATER: handle the error + return; + } + + SfxInPlaceClient* pCli = GetView().FindIPClient( xObj.GetObject(), &GetView().GetEditWin() ); + if ( !pCli ) + { + if ( (embed::EmbedMisc::EMBED_ACTIVATEIMMEDIATELY & nMisc) + || bLinkingChart + // --> OD #i117189# - refine condition for non-resizable objects + // non-resizable objects need to be set the size back by this method + || ( bNoTextFramePrtAreaChanged && nMisc & embed::EmbedMisc::EMBED_NEVERRESIZE ) ) + { + pCli = new SwOleClient( &GetView(), &GetView().GetEditWin(), xObj ); + } + else + return; + } + + // TODO/LEAN: getMapUnit can switch object to running state + // xObj.TryRunningState(); + + awt::Size aSize; + try + { + aSize = xObj->getVisualAreaSize( nAspect ); + } + catch (const embed::NoVisualAreaSizeException&) + { + OSL_FAIL("Can't get visual area size!" ); + // the scaling will not be done + } + catch (const uno::Exception&) + { + // TODO/LATER: handle the error + OSL_FAIL("Can't get visual area size!" ); + return; + } + + Size _aVisArea( aSize.Width, aSize.Height ); + + Fraction aScaleWidth( 1, 1 ); + Fraction aScaleHeight( 1, 1 ); + + bool bUseObjectSize = false; + + // As long as there comes no reasonable size from the object, + // nothing can be scaled. + if( _aVisArea.Width() && _aVisArea.Height() ) + { + const MapMode aTmp( MapUnit::MapTwip ); + MapUnit aUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( nAspect ) ); + _aVisArea = OutputDevice::LogicToLogic(_aVisArea, MapMode(aUnit), aTmp); + Size aObjArea; + if ( pFlyPrtRect ) + aObjArea = pFlyPrtRect->SSize(); + else + aObjArea = GetAnyCurRect( CurRectType::FlyEmbeddedPrt, nullptr, xObj.GetObject() ).SSize(); + + // differ the aObjArea and _aVisArea by 1 Pixel then set new VisArea + tools::Long nX, nY; + SwSelPaintRects::Get1PixelInLogic( *this, &nX, &nY ); + if( !( _aVisArea.Width() - nX <= aObjArea.Width() && + _aVisArea.Width() + nX >= aObjArea.Width() && + _aVisArea.Height()- nY <= aObjArea.Height()&& + _aVisArea.Height()+ nY >= aObjArea.Height() )) + { + if ( nMisc & embed::EmbedMisc::EMBED_NEVERRESIZE ) + { + // the object must not be scaled, + // the size stored in object must be used for restoring + bUseObjectSize = true; + } + else + { + aScaleWidth = Fraction( aObjArea.Width(), _aVisArea.Width() ); + aScaleHeight = Fraction( aObjArea.Height(), _aVisArea.Height()); + } + } + } + + // Now is the favorable time to set the ObjArea. + // The Scaling must be considered. + SwRect aArea; + if ( pFlyPrtRect ) + { + aArea = *pFlyPrtRect; + aArea += pFlyFrameRect->Pos(); + } + else + { + aArea = GetAnyCurRect( CurRectType::FlyEmbeddedPrt, nullptr, xObj.GetObject() ); + aArea.Pos() += GetAnyCurRect( CurRectType::FlyEmbedded, nullptr, xObj.GetObject() ).Pos(); + } + + if ( bUseObjectSize ) + { + // --> this moves non-resizable object so that when adding borders the baseline remains the same + const SwFlyFrameFormat *pFlyFrameFormat = dynamic_cast< const SwFlyFrameFormat * >( GetFlyFrameFormat() ); + OSL_ENSURE( pFlyFrameFormat, "Could not find fly frame." ); + if ( pFlyFrameFormat ) + { + const Point &rPoint = pFlyFrameFormat->GetLastFlyFramePrtRectPos(); + SwRect aRect( pFlyPrtRect ? *pFlyPrtRect + : GetAnyCurRect( CurRectType::FlyEmbeddedPrt, nullptr, xObj.GetObject() )); + aArea += rPoint - aRect.Pos(); // adjust area by diff of printing area position in order to keep baseline alignment correct. + } + aArea.Width ( _aVisArea.Width() ); + aArea.Height( _aVisArea.Height() ); + RequestObjectResize( aArea, xObj.GetObject() ); + } + else + { + double nWidth(pCli->GetScaleWidth()); + double nHeight(pCli->GetScaleHeight()); + if (nWidth && nHeight) + { + aArea.Width ( aArea.Width() / nWidth ); + aArea.Height( aArea.Height() / nHeight ); + } + } + + pCli->SetObjAreaAndScale( aArea.SVRect(), aScaleWidth, aScaleHeight ); +} + +void SwWrtShell::ConnectObj( svt::EmbeddedObjectRef& xObj, const SwRect &rPrt, + const SwRect &rFrame ) +{ + SfxInPlaceClient* pCli = GetView().FindIPClient( xObj.GetObject(), &GetView().GetEditWin()); + if ( !pCli ) + new SwOleClient( &GetView(), &GetView().GetEditWin(), xObj ); + CalcAndSetScale( xObj, &rPrt, &rFrame ); +} + +// Insert hard page break; +// Selections will be overwritten +void SwWrtShell::InsertPageBreak(const OUString *pPageDesc, const ::std::optional<sal_uInt16>& oPgNum ) +{ + if (!lcl_IsAllowed(this)) + return; + + ResetCursorStack(); + if( CanInsert() ) + { + SwActContext aActContext(this); + StartUndo(SwUndoId::UI_INSERT_PAGE_BREAK); + + if ( !IsCursorInTable() ) + { + if(HasSelection()) + DelRight(); + SwFEShell::SplitNode(); + // delete the numbered attribute of the last line if the last line is empty + GetDoc()->ClearLineNumAttrs( *GetCursor()->GetPoint() ); + } + + const SwPageDesc *pDesc = pPageDesc + ? FindPageDescByName( *pPageDesc, true ) : nullptr; + if( pDesc ) + { + SwFormatPageDesc aDesc( pDesc ); + aDesc.SetNumOffset( oPgNum ); + SetAttrItem( aDesc ); + } + else + SetAttrItem( SvxFormatBreakItem(SvxBreak::PageBefore, RES_BREAK) ); + EndUndo(SwUndoId::UI_INSERT_PAGE_BREAK); + } + collectUIInformation("BREAK_PAGE", "parameter"); +} + +// Insert hard page break; +// Selections will be overwritten + +void SwWrtShell::InsertLineBreak(std::optional<SwLineBreakClear> oClear) +{ + if (!lcl_IsAllowed(this)) + return; + + ResetCursorStack(); + if( CanInsert() ) + { + if(HasSelection()) + DelRight(); + + const sal_Unicode cIns = 0x0A; + SwLineBreakClear eClear = SwLineBreakClear::NONE; + if (oClear.has_value()) + { + eClear = *oClear; + } + SvxAutoCorrect* pACorr = lcl_IsAutoCorr(); + if (pACorr && eClear == SwLineBreakClear::NONE) + AutoCorrect( *pACorr, cIns ); + else + { + if (eClear == SwLineBreakClear::NONE) + { + SwWrtShell::Insert(OUString(cIns)); + } + else + { + SwFormatLineBreak aLineBreak(eClear); + SetAttrItem(aLineBreak); + } + } + } +} + +// Insert hard column break; +// Selections will be overwritten + +void SwWrtShell::InsertColumnBreak() +{ + if (!lcl_IsAllowed(this)) + return; + + SwActContext aActContext(this); + ResetCursorStack(); + if( !CanInsert() ) + return; + + StartUndo(SwUndoId::UI_INSERT_COLUMN_BREAK); + + if ( !IsCursorInTable() ) + { + if(HasSelection()) + DelRight(); + SwFEShell::SplitNode( false, false ); + } + SetAttrItem(SvxFormatBreakItem(SvxBreak::ColumnBefore, RES_BREAK)); + + EndUndo(SwUndoId::UI_INSERT_COLUMN_BREAK); +} + +void SwWrtShell::InsertContentControl(SwContentControlType eType) +{ + if (!lcl_IsAllowed(this)) + { + return; + } + + ResetCursorStack(); + if (!CanInsert()) + { + return; + } + + auto pContentControl = std::make_shared<SwContentControl>(nullptr); + OUString aPlaceholder; + switch (eType) + { + case SwContentControlType::RICH_TEXT: + case SwContentControlType::PLAIN_TEXT: + { + pContentControl->SetShowingPlaceHolder(true); + if (eType == SwContentControlType::PLAIN_TEXT) + { + pContentControl->SetPlainText(true); + } + if (!HasSelection()) + { + aPlaceholder = SwResId(STR_CONTENT_CONTROL_PLACEHOLDER); + } + break; + } + case SwContentControlType::CHECKBOX: + { + pContentControl->SetCheckbox(true); + // Ballot Box with X + pContentControl->SetCheckedState(u"\u2612"_ustr); + // Ballot Box + pContentControl->SetUncheckedState(u"\u2610"_ustr); + aPlaceholder = u"\u2610"_ustr; + break; + } + case SwContentControlType::COMBO_BOX: + case SwContentControlType::DROP_DOWN_LIST: + { + if (eType == SwContentControlType::COMBO_BOX) + { + pContentControl->SetComboBox(true); + } + else if (eType == SwContentControlType::DROP_DOWN_LIST) + { + pContentControl->SetDropDown(true); + } + + pContentControl->SetShowingPlaceHolder(true); + if (!HasSelection()) + { + aPlaceholder = SwResId(STR_DROPDOWN_CONTENT_CONTROL_PLACEHOLDER); + } + SwContentControlListItem aListItem; + aListItem.m_aValue = aPlaceholder; + pContentControl->SetListItems({ aListItem }); + break; + } + case SwContentControlType::PICTURE: + { + // Set up the picture content control. + pContentControl->SetShowingPlaceHolder(true); + pContentControl->SetPicture(true); + + // Create the placeholder bitmap. + BitmapEx aBitmap(Size(1, 1), vcl::PixelFormat::N24_BPP); + Color aColor = SvtOptionsDrawinglayer::getHilightColor(); + aColor.IncreaseLuminance(255 * 0.75); + aBitmap.Erase(aColor); + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, SwResId(STR_GRAPHIC_DEFNAME)); + StartUndo(SwUndoId::INSERT, &aRewriter); + LockPaint(LockPaintReason::InsertGraphic); + StartAction(); + InsertGraphic(OUString(), OUString(), aBitmap, nullptr, RndStdIds::FLY_AS_CHAR); + + // Set properties on the bitmap. + SfxItemSetFixed<RES_FRM_SIZE, RES_FRM_SIZE> aSet(GetDoc()->GetAttrPool()); + GetFlyFrameAttr(aSet); + SwFormatFrameSize aSize(SwFrameSize::Fixed, 3000, 3000); + aSet.Put(aSize); + SetFlyFrameAttr(aSet); + SwFrameFormat* pFrameFormat = GetFlyFrameFormat(); + EndAction(); + UnlockPaint(); + EndUndo(); + + // Go after the anchor position. + UnSelectFrame(); + LeaveSelFrameMode(); + { + SwCursor* pCursor = getShellCursor(true); + pCursor->DeleteMark(); + const SwFormatAnchor& rFormatAnchor = pFrameFormat->GetAnchor(); + pCursor->GetPoint()->Assign( *rFormatAnchor.GetAnchorContentNode(), rFormatAnchor.GetAnchorContentOffset() + 1); + } + + // Select before the anchor position. + Left(SwCursorSkipMode::Chars, /*bSelect=*/true, 1, /*bBasicCall=*/false); + break; + } + case SwContentControlType::DATE: + { + pContentControl->SetShowingPlaceHolder(true); + pContentControl->SetDate(true); + SvNumberFormatter* pFormatter = GetDoc()->GetNumberFormatter(); + sal_uInt32 nStandardFormat = pFormatter->GetStandardFormat(SvNumFormatType::DATE); + const SvNumberformat* pFormat = pFormatter->GetEntry(nStandardFormat); + pContentControl->SetDateFormat(pFormat->GetFormatstring()); + pContentControl->SetDateLanguage(LanguageTag(pFormat->GetLanguage()).getBcp47()); + if (!HasSelection()) + { + aPlaceholder = SwResId(STR_DATE_CONTENT_CONTROL_PLACEHOLDER); + } + break; + } + } + if (aPlaceholder.getLength()) + { + Insert(aPlaceholder); + Left(SwCursorSkipMode::Chars, /*bSelect=*/true, aPlaceholder.getLength(), + /*bBasicCall=*/false); + } + SwFormatContentControl aContentControl(pContentControl, RES_TXTATR_CONTENTCONTROL); + SetAttrItem(aContentControl); +} + +// Insert footnote +// rStr - optional footnote mark + +void SwWrtShell::InsertFootnote(const OUString &rStr, bool bEndNote, bool bEdit ) +{ + ResetCursorStack(); + if( !CanInsert() ) + return; + + if(HasSelection()) + { + //collapse cursor to the end + if(!IsCursorPtAtEnd()) + SwapPam(); + ClearMark(); + } + SwPosition aPos = *GetCursor()->GetPoint(); + SwFormatFootnote aFootNote( bEndNote ); + if(!rStr.isEmpty()) + aFootNote.SetNumStr( rStr ); + + SetAttrItem(aFootNote); + + if( bEdit ) + { + // For editing the footnote text. + Left(SwCursorSkipMode::Chars, false, 1, false ); + GotoFootnoteText(); + } + m_aNavigationMgr.addEntry(aPos); +} + +// tdf#141634 +static bool lcl_FoldedOutlineNodeEndOfParaSplit(SwWrtShell *pThis) +{ + SwTextNode* pTextNode = pThis->GetCursor()->GetPointNode().GetTextNode(); + if (pTextNode && pTextNode->IsOutline()) + { + bool bVisible = true; + pTextNode->GetAttrOutlineContentVisible(bVisible); + if (!bVisible) + { + const SwNodes& rNodes = pThis->GetNodes(); + const SwOutlineNodes& rOutlineNodes = rNodes.GetOutLineNds(); + SwOutlineNodes::size_type nPos; + (void) rOutlineNodes.Seek_Entry(pTextNode, &nPos); + + SwNode* pSttNd = rOutlineNodes[nPos]; + + // determine end node of folded outline content + SwNode* pEndNd = &rNodes.GetEndOfContent(); + if (rOutlineNodes.size() > nPos + 1) + pEndNd = rOutlineNodes[nPos + 1]; + + if (pThis->GetViewOptions()->IsTreatSubOutlineLevelsAsContent()) + { + // get the next outline node after the folded outline content (iPos) + // it is the next outline node with the same level or less + int nLevel = pSttNd->GetTextNode()->GetAttrOutlineLevel(); + SwOutlineNodes::size_type iPos = nPos; + while (++iPos < rOutlineNodes.size() && + rOutlineNodes[iPos]->GetTextNode()->GetAttrOutlineLevel() > nLevel); + + // get the correct end node + // the outline node may be in frames, headers, footers special section of doc model + SwNode* pStartOfSectionNodeSttNd = pSttNd->StartOfSectionNode(); + while (pStartOfSectionNodeSttNd->StartOfSectionNode() + != pStartOfSectionNodeSttNd->StartOfSectionNode()->StartOfSectionNode()) + { + pStartOfSectionNodeSttNd = pStartOfSectionNodeSttNd->StartOfSectionNode(); + } + pEndNd = pStartOfSectionNodeSttNd->EndOfSectionNode(); + + if (iPos < rOutlineNodes.size()) + { + SwNode* pStartOfSectionNode = rOutlineNodes[iPos]->StartOfSectionNode(); + while (pStartOfSectionNode->StartOfSectionNode() + != pStartOfSectionNode->StartOfSectionNode()->StartOfSectionNode()) + { + pStartOfSectionNode = pStartOfSectionNode->StartOfSectionNode(); + } + if (pStartOfSectionNodeSttNd == pStartOfSectionNode) + pEndNd = rOutlineNodes[iPos]; + } + } + + // table, text box, header, footer + if (pSttNd->GetTableBox() || pSttNd->GetIndex() < rNodes.GetEndOfExtras().GetIndex()) + { + // insert before section end node + if (pSttNd->EndOfSectionIndex() < pEndNd->GetIndex()) + { + SwNodeIndex aIdx(*pSttNd->EndOfSectionNode()); + while (aIdx.GetNode().IsEndNode()) + --aIdx; + ++aIdx; + pEndNd = &aIdx.GetNode(); + } + } + // if pSttNd isn't in table but pEndNd is then insert after table + else if (pEndNd->GetTableBox()) + { + pEndNd = pEndNd->FindTableNode(); + SwNodeIndex aIdx(*pEndNd, -1); + // account for nested tables + while (aIdx.GetNode().GetTableBox()) + { + pEndNd = aIdx.GetNode().FindTableNode(); + aIdx.Assign(*pEndNd, -1); + } + aIdx.Assign(*pEndNd->EndOfSectionNode(), +1); + pEndNd = &aIdx.GetNode(); + } + // end node determined + + // now insert the new outline node + SwDoc* pDoc = pThis->GetDoc(); + + // insert at end of tablebox doesn't work correct without + MakeAllOutlineContentTemporarilyVisible a(pDoc); + + SwTextNode* pNd = pDoc->GetNodes().MakeTextNode(*pEndNd, pTextNode->GetTextColl(), true); + + (void) rOutlineNodes.Seek_Entry(pNd, &nPos); + pThis->GotoOutline(nPos); + + if (pDoc->GetIDocumentUndoRedo().DoesUndo()) + { + pDoc->GetIDocumentUndoRedo().ClearRedo(); + pDoc->GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoInsert>(*pNd)); + pDoc->GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoFormatColl> + (SwPaM(*pNd), pNd->GetTextColl(), true, true)); + } + + pThis->SetModified(); + return true; + } + } + return false; +} + +// SplitNode; also, because +// - of deleting selected content; +// - of reset of the Cursorstack if necessary. + +void SwWrtShell::SplitNode( bool bAutoFormat ) +{ + ResetCursorStack(); + if( !CanInsert() ) + return; + + SwActContext aActContext(this); + + m_rView.GetEditWin().FlushInBuffer(); + StartUndo(SwUndoId::SPLITNODE); + + bool bHasSel = HasSelection(); + if (bHasSel) + DelRight(); + + bool bHandled = false; + if (GetViewOptions()->IsShowOutlineContentVisibilityButton() && IsEndPara()) + bHandled = lcl_FoldedOutlineNodeEndOfParaSplit(this); + + if (!bHandled) + SwFEShell::SplitNode( bAutoFormat ); + + EndUndo(SwUndoId::SPLITNODE); +} + +// Turn on numbering +// Parameter: Optional specification of a name for the named list; +// this indicates a position if it is possible to convert them +// into a number and less than nMaxRules. + +// To test the CharFormats at the numbering +// external void SetNumChrFormat( SwWrtShell*, SwNumRules& ); + +// -> #i40041# +// Preconditions (as far as OD has figured out): +// - <SwEditShell::HasNumber()> is false, if <bNum> is true +// - <SwEditShell::HasBullet()> is false, if <bNum> is false +// Behavior of method is determined by the current situation at the current +// cursor position in the document. +void SwWrtShell::NumOrBulletOn(bool bNum) +{ + StartUndo(SwUndoId::NUMORNONUM); + + const SwNumRule* pNumRule = GetNumRuleAtCurrCursorPos(); + + // - activate outline rule respectively turning on outline rule for + // current text node. But, only for turning on a numbering (<bNum> == true). + // - overwrite found numbering rule at current cursor position, if + // no numbering rule can be retrieved from the paragraph style. + bool bContinueFoundNumRule( false ); + bool bActivateOutlineRule( false ); + int nActivateOutlineLvl( MAXLEVEL ); // only relevant, if <bActivateOutlineRule> == true + SwTextFormatColl * pColl = GetCurTextFormatColl(); + if ( pColl ) + { + // retrieve numbering rule at paragraph + // style, which is found at current cursor position in the document. + SwNumRule* pCollRule = mxDoc->FindNumRulePtr(pColl->GetNumRule().GetValue()); + // #125993# - The outline numbering rule isn't allowed + // to be derived from a parent paragraph style to a derived one. + // Thus check, if the found outline numbering rule is directly + // set at the paragraph style <pColl>. If not, set <pCollRule> to NULL + if ( pCollRule && pCollRule == GetDoc()->GetOutlineNumRule() ) + { + const SwNumRule* pDirectCollRule = + mxDoc->FindNumRulePtr(pColl->GetNumRule( false ).GetValue()); + if ( !pDirectCollRule ) + { + pCollRule = nullptr; + } + } + + if ( !pCollRule ) + { + pNumRule = pCollRule; + } + // no activation or continuation of outline numbering in Writer/Web document + else if ( bNum && + !dynamic_cast<SwWebDocShell*>(GetDoc()->GetDocShell()) && + pCollRule == GetDoc()->GetOutlineNumRule() ) + { + if ( pNumRule == pCollRule ) + { + // check, if text node at current cursor positioned is counted. + // If not, let it been counted. Then it has to be checked, + // of the outline numbering has to be activated or continued. + SwTextNode const*const pTextNode = sw::GetParaPropsNode( + *GetLayout(), GetCursor()->GetPoint()->GetNode()); + if ( pTextNode && !pTextNode->IsCountedInList() ) + { + // check, if numbering of the outline level of the paragraph + // style is active. If not, activate this outline level. + nActivateOutlineLvl = pColl->GetAssignedOutlineStyleLevel(); + OSL_ENSURE( pColl->IsAssignedToListLevelOfOutlineStyle(), + "<SwWrtShell::NumOrBulletOn(..)> - paragraph style with outline rule, but no outline level" ); + if ( pColl->IsAssignedToListLevelOfOutlineStyle() && + pCollRule->Get( o3tl::narrowing<sal_uInt16>(nActivateOutlineLvl) ).GetNumberingType() + == SVX_NUM_NUMBER_NONE ) + { + // activate outline numbering + bActivateOutlineRule = true; + } + else + { + // turning on outline numbering at current cursor position + bContinueFoundNumRule = true; + } + } + else + { + // #i101234# + // activate outline numbering, because from the precondition + // it's known, that <SwEdit::HasNumber()> == false + bActivateOutlineRule = true; + nActivateOutlineLvl = pColl->GetAssignedOutlineStyleLevel(); + } + } + else if ( !pNumRule ) + { + // #i101234# + // Check, if corresponding list level of the outline numbering + // has already a numbering format set. + nActivateOutlineLvl = pColl->GetAssignedOutlineStyleLevel(); + if ( pCollRule->Get( o3tl::narrowing<sal_uInt16>(nActivateOutlineLvl) ).GetNumberingType() + == SVX_NUM_NUMBER_NONE ) + { + // activate outline numbering, because from the precondition + // it's known, that <SwEdit::HasNumber()> == false + bActivateOutlineRule = true; + } + else + { + // turning on outline numbering at current cursor position + bContinueFoundNumRule = true; + } + } + else + { + // check, if numbering of the outline level of the paragraph + // style is active. If not, activate this outline level. + nActivateOutlineLvl = pColl->GetAssignedOutlineStyleLevel(); + OSL_ENSURE( pColl->IsAssignedToListLevelOfOutlineStyle(), + "<SwWrtShell::NumOrBulletOn(..)> - paragraph style with outline rule, but no outline level" ); + if ( pColl->IsAssignedToListLevelOfOutlineStyle() && + pCollRule->Get( o3tl::narrowing<sal_uInt16>(nActivateOutlineLvl) ).GetNumberingType() + == SVX_NUM_NUMBER_NONE ) + { + // activate outline numbering + bActivateOutlineRule = true; + } + else + { + // turning on outline numbering at current cursor position + bContinueFoundNumRule = true; + } + } + pNumRule = pCollRule; + } + } + + // Only automatic numbering/bullet rules should be changed. + // Note: The outline numbering rule is also an automatic one. It's only + // changed, if it has to be activated. + if ( pNumRule ) + { + if ( !pNumRule->IsAutoRule() ) + { + pNumRule = nullptr; + } + else if ( pNumRule == GetDoc()->GetOutlineNumRule() && + !bActivateOutlineRule && !bContinueFoundNumRule ) + { + pNumRule = nullptr; + } + } + + // Search for a previous numbering/bullet rule to continue it. + OUString sContinuedListId; + if ( !pNumRule ) + { + pNumRule = GetDoc()->SearchNumRule( *GetCursor()->GetPoint(), + false, bNum, false, 0, + sContinuedListId, GetLayout() ); + bContinueFoundNumRule = pNumRule != nullptr; + } + + if (pNumRule) + { + SwNumRule aNumRule(*pNumRule); + + // do not change found numbering/bullet rule, if it should only be continued. + if ( !bContinueFoundNumRule ) + { + SwTextNode const*const pTextNode = sw::GetParaPropsNode( + *GetLayout(), GetCursor()->GetPoint()->GetNode()); + + if (pTextNode) + { + // use above retrieve outline level, if outline numbering has to be activated. + int nLevel = bActivateOutlineRule + ? nActivateOutlineLvl + : pTextNode->GetActualListLevel(); + + if (nLevel < 0) + nLevel = 0; + + if (nLevel >= MAXLEVEL) + nLevel = MAXLEVEL - 1; + + SwNumFormat aFormat(aNumRule.Get(o3tl::narrowing<sal_uInt16>(nLevel))); + + if (bNum) + aFormat.SetNumberingType(SVX_NUM_ARABIC); + else + { + // #i63395# Only apply user defined default bullet font + if ( numfunc::IsDefBulletFontUserDefined() ) + { + const vcl::Font* pFnt = &numfunc::GetDefBulletFont(); + aFormat.SetBulletFont( pFnt ); + } + aFormat.SetBulletChar( numfunc::GetBulletChar(static_cast<sal_uInt8>(nLevel))); + aFormat.SetNumberingType(SVX_NUM_CHAR_SPECIAL); + // #i93908# clear suffix for bullet lists + aFormat.SetListFormat("", "", nLevel); + } + aNumRule.Set(o3tl::narrowing<sal_uInt16>(nLevel), aFormat); + } + } + + // reset indent attribute on applying list style + SetCurNumRule( aNumRule, false, sContinuedListId, true ); + } + else + { + // #i95907# + const SvxNumberFormat::SvxNumPositionAndSpaceMode ePosAndSpaceMode( + numfunc::GetDefaultPositionAndSpaceMode() ); + SwNumRule aNumRule( GetUniqueNumRuleName(), ePosAndSpaceMode ); + // Append the character template at the numbering. + SwCharFormat* pChrFormat; + SwDocShell* pDocSh = GetView().GetDocShell(); + // #i63395# + // Only apply user defined default bullet font + const vcl::Font* pFnt = numfunc::IsDefBulletFontUserDefined() + ? &numfunc::GetDefBulletFont() + : nullptr; + + if (bNum) + { + pChrFormat = GetCharFormatFromPool( RES_POOLCHR_NUM_LEVEL ); + } + else + { + pChrFormat = GetCharFormatFromPool( RES_POOLCHR_BULLET_LEVEL ); + } + + const SwTextNode *const pTextNode = sw::GetParaPropsNode(*GetLayout(), + GetCursor()->GetPoint()->GetNode()); + const SwTwips nWidthOfTabs = pTextNode + ? pTextNode->GetWidthOfLeadingTabs() + : 0; + GetDoc()->getIDocumentContentOperations().RemoveLeadingWhiteSpace(*GetCursor()); + + const bool bHtml = dynamic_cast<SwWebDocShell*>( pDocSh ) != nullptr; + const bool bRightToLeft = IsInRightToLeftText(); + for( sal_uInt8 nLvl = 0; nLvl < MAXLEVEL; ++nLvl ) + { + SwNumFormat aFormat( aNumRule.Get( nLvl ) ); + aFormat.SetCharFormat( pChrFormat ); + + if (! bNum) + { + // #i63395# + // Only apply user defined default bullet font + if ( pFnt ) + { + aFormat.SetBulletFont( pFnt ); + } + aFormat.SetBulletChar( numfunc::GetBulletChar(nLvl) ); + aFormat.SetNumberingType(SVX_NUM_CHAR_SPECIAL); + // #i93908# clear suffix for bullet lists + aFormat.SetListFormat("", "", nLvl); + } + + // #i95907# + if ( ePosAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION ) + { + if(bHtml && nLvl) + { + // 1/2" for HTML + aFormat.SetAbsLSpace(nLvl * 720); + } + else if ( nWidthOfTabs > 0 ) + { + aFormat.SetAbsLSpace(nWidthOfTabs + nLvl * 720); + } + } + + // #i38904# Default alignment for + // numbering/bullet should be rtl in rtl paragraph: + if ( bRightToLeft ) + { + aFormat.SetNumAdjust( SvxAdjust::Right ); + } + + aNumRule.Set( nLvl, aFormat ); + } + + // #i95907# + if ( pTextNode && + ePosAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT ) + { + + const SwTwips nTextNodeIndent = pTextNode->GetAdditionalIndentForStartingNewList(); + if ( ( nTextNodeIndent + nWidthOfTabs ) != 0 ) + { + // #i111172#/fdo#85666 + // If text node is already inside a list, assure that the indents + // are the same. Thus, adjust the indent change value by subtracting + // indents of to be applied list style. + SwTwips nIndentChange = nTextNodeIndent + nWidthOfTabs; + if ( pTextNode->GetNumRule() ) + { + int nLevel = pTextNode->GetActualListLevel(); + + if (nLevel < 0) + nLevel = 0; + + if (nLevel >= MAXLEVEL) + nLevel = MAXLEVEL - 1; + + const SwNumFormat& aFormat( aNumRule.Get( nLevel ) ); + if ( aFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT ) + { + nIndentChange -= aFormat.GetIndentAt() + aFormat.GetFirstLineIndent(); + } + } + aNumRule.ChangeIndent( nIndentChange ); + } + } + // reset indent attribute on applying list style + // start new list + SetCurNumRule( aNumRule, true, OUString(), true ); + } + + EndUndo(SwUndoId::NUMORNONUM); +} +// <- #i40041# + +void SwWrtShell::NumOn() +{ + NumOrBulletOn(true); +} + +void SwWrtShell::NumOrBulletOff() +{ + const SwNumRule * pCurNumRule = GetNumRuleAtCurrCursorPos(); + + if (!pCurNumRule) + return; + + if (pCurNumRule->IsOutlineRule()) + { + SwNumRule aNumRule(*pCurNumRule); + + SwTextNode * pTextNode = + sw::GetParaPropsNode(*GetLayout(), GetCursor()->GetPoint()->GetNode()); + + if (pTextNode) + { + int nLevel = pTextNode->GetActualListLevel(); + + if (nLevel < 0) + nLevel = 0; + + if (nLevel >= MAXLEVEL) + nLevel = MAXLEVEL - 1; + + SwNumFormat aFormat(aNumRule.Get(o3tl::narrowing<sal_uInt16>(nLevel))); + + aFormat.SetNumberingType(SVX_NUM_NUMBER_NONE); + aNumRule.Set(nLevel, aFormat); + + // no start or continuation of a list - the outline style is only changed. + SetCurNumRule( aNumRule, false ); + } + } + else + { + DelNumRules(); + } + + // #126346# - Cursor can not be anymore in front of + // a label, because numbering/bullet is switched off. + SetInFrontOfLabel( false ); +} +// <- #i29560# + +// Request Default-Bulletlist + +void SwWrtShell::BulletOn() +{ + NumOrBulletOn(false); +} + +SelectionType SwWrtShell::GetSelectionType() const +{ + // ContentType cannot be determined within a Start-/EndAction. + // Because there is no invalid value TEXT will be returned. + // The value does not matter, it may be updated in EndAction anyway. + + if (ActionPend()) + return IsSelFrameMode() ? SelectionType::Frame : SelectionType::Text; + + SwView &_rView = const_cast<SwView&>(GetView()); + if (_rView.GetPostItMgr() && _rView.GetPostItMgr()->HasActiveSidebarWin() ) + return SelectionType::PostIt; + + // Inserting a frame is not a DrawMode + SelectionType nCnt; + if ( !_rView.GetEditWin().IsFrameAction() && + (IsObjSelected() || (_rView.IsDrawMode() && !IsFrameSelected()) )) + { + if (GetDrawView()->IsTextEdit()) + nCnt = SelectionType::DrawObjectEditMode; + else + { + if (GetView().IsFormMode()) // Only Form selected + nCnt = SelectionType::DbForm; + else + nCnt = SelectionType::DrawObject; // Any draw object + + if (_rView.IsBezierEditMode()) + nCnt |= SelectionType::Ornament; + else if( GetDrawView()->GetContext() == SdrViewContext::Media ) + nCnt |= SelectionType::Media; + + if (svx::checkForSelectedCustomShapes( GetDrawView(), true /* bOnlyExtruded */ )) + { + nCnt |= SelectionType::ExtrudedCustomShape; + } + + if (svx::checkForSelectedFontWork( GetDrawView() )) + { + nCnt |= SelectionType::FontWork; + } + } + + return nCnt; + } + + nCnt = static_cast<SelectionType>(GetCntType()); + + if ( IsFrameSelected() ) + { + if (_rView.IsDrawMode()) + _rView.LeaveDrawCreate(); // clean up (Bug #45639) + if ( !(nCnt & (SelectionType::Graphic | SelectionType::Ole)) ) + return SelectionType::Frame; + } + + if ( IsCursorInTable() ) + nCnt |= SelectionType::Table; + + if ( IsTableMode() ) + { + nCnt |= SelectionType::Table | SelectionType::TableCell; + SwTable::SearchType eTableSel = GetEnhancedTableSelection(); + if ( eTableSel == SwTable::SEARCH_ROW ) + nCnt |= SelectionType::TableRow; + else if ( eTableSel == SwTable::SEARCH_COL ) + nCnt |= SelectionType::TableCol; + } + + // Do not pop up numbering toolbar, if the text node has a numbering of type SVX_NUM_NUMBER_NONE. + const SwNumRule* pNumRule = GetNumRuleAtCurrCursorPos(); + if ( pNumRule ) + { + const SwTextNode* pTextNd = + sw::GetParaPropsNode(*GetLayout(), GetCursor()->GetPoint()->GetNode()); + + if ( pTextNd && pTextNd->IsInList() ) + { + int nLevel = pTextNd->GetActualListLevel(); + + if (nLevel < 0) + nLevel = 0; + + if (nLevel >= MAXLEVEL) + nLevel = MAXLEVEL - 1; + + const SwNumFormat& rFormat = pNumRule->Get(nLevel); + if ( SVX_NUM_NUMBER_NONE != rFormat.GetNumberingType() ) + nCnt |= SelectionType::NumberList; + } + } + + return nCnt; +} + +// Find the text collection with the name rCollname +// Returns: Pointer at the collection or 0, if no +// text collection with this name exists, or +// this is a default template. + +SwTextFormatColl *SwWrtShell::GetParaStyle(const OUString &rCollName, GetStyle eCreate ) +{ + SwTextFormatColl* pColl = FindTextFormatCollByName( rCollName ); + if( !pColl && GETSTYLE_NOCREATE != eCreate ) + { + sal_uInt16 nId = SwStyleNameMapper::GetPoolIdFromUIName( rCollName, SwGetPoolIdFromName::TxtColl ); + if( USHRT_MAX != nId || GETSTYLE_CREATEANY == eCreate ) + pColl = GetTextCollFromPool( nId ); + } + return pColl; +} + +// Find the text collection with the name rCollname +// Returns: Pointer at the collection or 0, if no +// character template with this name exists, or +// this is a default template or template is automatic. + +SwCharFormat *SwWrtShell::GetCharStyle(const OUString &rFormatName, GetStyle eCreate ) +{ + SwCharFormat* pFormat = FindCharFormatByName( rFormatName ); + if( !pFormat && GETSTYLE_NOCREATE != eCreate ) + { + sal_uInt16 nId = SwStyleNameMapper::GetPoolIdFromUIName( rFormatName, SwGetPoolIdFromName::ChrFmt ); + if( USHRT_MAX != nId || GETSTYLE_CREATEANY == eCreate ) + pFormat = static_cast<SwCharFormat*>(GetFormatFromPool( nId )); + } + return pFormat; +} + +// Find the table format with the name rFormatname +// Returns: Pointer at the collection or 0, if no +// frame format with this name exists or +// this is a default format or the format is automatic. + +SwFrameFormat *SwWrtShell::GetTableStyle(std::u16string_view rFormatName) +{ + for( size_t i = GetTableFrameFormatCount(); i; ) + { + SwFrameFormat *pFormat = &GetTableFrameFormat( --i ); + if( !pFormat->IsDefault() && + pFormat->GetName() == rFormatName && IsUsed( *pFormat ) ) + return pFormat; + } + return nullptr; +} + +void SwWrtShell::addCurrentPosition() { + SwPaM* pPaM = GetCursor(); + m_aNavigationMgr.addEntry(*pPaM->GetPoint()); +} + +// Applying templates + +void SwWrtShell::SetPageStyle(const OUString &rCollName) +{ + if( !SwCursorShell::HasSelection() && !IsSelFrameMode() && !IsObjSelected() ) + { + SwPageDesc* pDesc = FindPageDescByName( rCollName, true ); + if( pDesc ) + ChgCurPageDesc( *pDesc ); + } +} + +// Access templates + +OUString const & SwWrtShell::GetCurPageStyle() const +{ + return GetPageDesc(GetCurPageDesc( false/*bCalcFrame*/ )).GetName(); +} + +// Change the current template referring to the existing change. + +void SwWrtShell::QuickUpdateStyle() +{ + SwTextFormatColl *pColl = GetCurTextFormatColl(); + + // Default cannot be changed + if(pColl && !pColl->IsDefault()) + { + FillByEx(pColl); + // Also apply the template to remove hard attribute assignment. + SetTextFormatColl(pColl); + } +} + +void SwWrtShell::AutoUpdatePara(SwTextFormatColl* pColl, const SfxItemSet& rStyleSet, SwPaM* pPaM ) +{ + SwPaM* pCursor = pPaM ? pPaM : GetCursor( ); + SfxItemSetFixed< + RES_CHRATR_BEGIN, RES_CHRATR_END - 1, + RES_PARATR_BEGIN, RES_PARATR_END - 1, + RES_FRMATR_BEGIN, RES_FRMATR_END - 1, + SID_ATTR_TABSTOP_DEFAULTS,SID_ATTR_TABSTOP_OFFSET, + SID_ATTR_BORDER_INNER, SID_ATTR_BORDER_INNER, + SID_ATTR_PARA_MODEL, SID_ATTR_PARA_KEEP, + SID_ATTR_PARA_PAGENUM, SID_ATTR_PARA_PAGENUM> aCoreSet( GetAttrPool() ); + GetPaMAttr( pCursor, aCoreSet ); + bool bReset = false; + SfxItemIter aParaIter( aCoreSet ); + for (auto pParaItem = aParaIter.GetCurItem(); pParaItem; pParaItem = aParaIter.NextItem()) + { + if(!IsInvalidItem(pParaItem)) + { + sal_uInt16 nWhich = pParaItem->Which(); + if(SfxItemState::SET == aParaIter.GetItemState() && + SfxItemState::SET == rStyleSet.GetItemState(nWhich)) + { + aParaIter.ClearItem(); + bReset = true; + } + } + } + StartAction(); + if(bReset) + { + ResetAttr({}, pCursor); + SetAttrSet(aCoreSet, SetAttrMode::DEFAULT, pCursor); + } + mxDoc->ChgFormat(*pColl, rStyleSet ); + EndAction(); +} + +void SwWrtShell::AutoUpdateFrame( SwFrameFormat* pFormat, const SfxItemSet& rStyleSet ) +{ + StartAction(); + + ResetFlyFrameAttr( &rStyleSet ); + pFormat->SetFormatAttr( rStyleSet ); + + EndAction(); +} + +void SwWrtShell::AutoCorrect( SvxAutoCorrect& rACorr, sal_Unicode cChar ) +{ + ResetCursorStack(); + if(!CanInsert()) + return; + + bool bStarted = false; + SwRewriter aRewriter; + + if(HasSelection()) + { + // Only parentheses here, because the regular insert + // is already clipped to the editshell + StartAllAction(); + + OUString aTmpStr1 = SwResId(STR_START_QUOTE) + + GetSelText() + + SwResId(STR_END_QUOTE); + OUString aTmpStr3 = SwResId(STR_START_QUOTE) + + OUStringChar(cChar) + + SwResId(STR_END_QUOTE); + aRewriter.AddRule( UndoArg1, aTmpStr1 ); + aRewriter.AddRule( UndoArg2, SwResId(STR_YIELDS) ); + aRewriter.AddRule( UndoArg3, aTmpStr3 ); + + StartUndo( SwUndoId::REPLACE, &aRewriter ); + bStarted = true; + DelRight(true); + } + SwEditShell::AutoCorrect( rACorr, IsInsMode(), cChar ); + + if(bStarted) + { + EndAllAction(); + EndUndo( SwUndoId::REPLACE, &aRewriter ); + } +} + +// Some kind of controlled copy ctor + +SwWrtShell::SwWrtShell( SwWrtShell& rSh, vcl::Window *_pWin, SwView &rShell ) + : SwFEShell(rSh, _pWin) + , m_rView(rShell) + , m_aNavigationMgr(*this) +{ + BITFLD_INI_LIST + CurrShell aCurr( this ); + + SetSfxViewShell( static_cast<SfxViewShell *>(&rShell) ); + SetFlyMacroLnk( LINK(this, SwWrtShell, ExecFlyMac) ); + + // place the cursor on the first field... + IFieldmark *pBM = nullptr; + if (IsFormProtected() && (pBM = GetFieldmarkAfter(/*bLoop=*/true)) !=nullptr) { + GotoFieldmark(pBM); + } +} + +SwWrtShell::SwWrtShell( SwDoc& rDoc, vcl::Window *_pWin, SwView &rShell, + const SwViewOption *pViewOpt ) + : SwFEShell(rDoc, _pWin, pViewOpt) + , m_rView(rShell) + , m_aNavigationMgr(*this) +{ + BITFLD_INI_LIST + CurrShell aCurr( this ); + SetSfxViewShell( static_cast<SfxViewShell *>(&rShell) ); + SetFlyMacroLnk( LINK(this, SwWrtShell, ExecFlyMac) ); + + // place the cursor on the first field... + IFieldmark *pBM = nullptr; + if (IsFormProtected() && (pBM = GetFieldmarkAfter(/*bLoop=*/true)) !=nullptr) { + GotoFieldmark(pBM); + } +} + +SwWrtShell::~SwWrtShell() +{ + CurrShell aCurr( this ); + while(IsModePushed()) + PopMode(); + while(PopCursor(false)) + ; + SwTransferable::ClearSelection( *this ); +} + +bool SwWrtShell::Pop(SwCursorShell::PopMode const eDelete) +{ + ::std::optional<SwCallLink> aLink(std::in_place, *this); + return Pop(eDelete, aLink); +} + +bool SwWrtShell::Pop(SwCursorShell::PopMode const eDelete, ::std::optional<SwCallLink>& roLink) +{ + bool bRet = SwCursorShell::Pop(eDelete, roLink); + if( bRet && IsSelection() ) + { + m_fnSetCursor = &SwWrtShell::SetCursorKillSel; + m_fnKillSel = &SwWrtShell::ResetSelect; + } + return bRet; +} + +bool SwWrtShell::CanInsert() +{ + if(IsSelFrameMode()) + { + return false; + } + + if(IsObjSelected()) + { + return false; + } + + if(GetView().GetDrawFuncPtr()) + { + return false; + } + + if(GetView().GetPostItMgr()->GetActiveSidebarWin()) + { + return false; + } + + return true; +} + +void SwWrtShell::ChgDBData(const SwDBData& aDBData) +{ + SwEditShell::ChgDBData(aDBData); + //notify the db-beamer if available + GetView().NotifyDBChanged(); +} + +OUString SwWrtShell::GetSelDescr() const +{ + OUString aResult; + + SelectionType nSelType = GetSelectionType(); + switch (nSelType) + { + case SelectionType::Graphic: + aResult = SwResId(STR_GRAPHIC); + + break; + case SelectionType::Frame: + { + const SwFrameFormat * pFrameFormat = GetSelectedFrameFormat(); + + if (pFrameFormat) + aResult = pFrameFormat->GetDescription(); + } + break; + case SelectionType::DrawObject: + { + aResult = SwResId(STR_DRAWING_OBJECTS); + } + break; + default: + if (mxDoc) + aResult = GetCursorDescr(); + } + + return aResult; +} + +void SwWrtShell::ApplyViewOptions( const SwViewOption &rOpt ) +{ + SwFEShell::ApplyViewOptions( rOpt ); + //#i115062# invalidate meta character slot + GetView().GetViewFrame().GetBindings().Invalidate( FN_VIEW_META_CHARS ); +} + +void SwWrtShell::SetReadonlyOption(bool bSet) +{ + GetView().GetEditWin().GetFrameControlsManager().SetReadonlyControls( bSet ); + SwViewShell::SetReadonlyOption( bSet ); +} + +// Switch on/off header or footer of a page style - if an empty name is +// given all styles are changed + +void SwWrtShell::ChangeHeaderOrFooter( + std::u16string_view rStyleName, bool bHeader, bool bOn, bool bShowWarning) +{ + SdrView *const pSdrView = GetDrawView(); + if (pSdrView && pSdrView->IsTextEdit()) + { // tdf#107474 deleting header may delete active drawing object + pSdrView->SdrEndTextEdit(true); + } + addCurrentPosition(); + StartAllAction(); + StartUndo( SwUndoId::HEADER_FOOTER ); // #i7983# + bool bExecute = true; + bool bCursorSet = false; + for( size_t nFrom = 0, nTo = GetPageDescCnt(); + nFrom < nTo; ++nFrom ) + { + SwPageDesc aDesc( GetPageDesc( nFrom )); + OUString sTmp(aDesc.GetName()); + if( rStyleName.empty() || rStyleName == sTmp ) + { + bool bChgd = false; + + if( bShowWarning && !bOn && GetActiveView() && GetActiveView() == &GetView() && + ( (bHeader && aDesc.GetMaster().GetHeader().IsActive()) || + (!bHeader && aDesc.GetMaster().GetFooter().IsActive()) ) ) + { + bShowWarning = false; + //Actions have to be closed while the dialog is showing + EndAllAction(); + + weld::Window* pParent = GetView().GetFrameWeld(); + short nResult; + if (bHeader) { + nResult = DeleteHeaderDialog(pParent).run(); + } else { + nResult = DeleteFooterDialog(pParent).run(); + } + + bExecute = nResult == RET_YES; + StartAllAction(); + if (nResult == RET_YES) + ToggleHeaderFooterEdit(); + } + if( bExecute ) + { + bChgd = true; + SwFrameFormat &rMaster = aDesc.GetMaster(); + if(bHeader) + rMaster.SetFormatAttr( SwFormatHeader( bOn )); + else + rMaster.SetFormatAttr( SwFormatFooter( bOn )); + if( bOn ) + { + // keep in sync with FN_PGNUMBER_WIZARD + constexpr tools::Long constTwips_5mm = o3tl::toTwips(5, o3tl::Length::mm); + SvxULSpaceItem aUL(bHeader ? 0 : constTwips_5mm, bHeader ? constTwips_5mm : 0, RES_UL_SPACE ); + SwFrameFormat* pFormat = bHeader ? + const_cast<SwFrameFormat*>(rMaster.GetHeader().GetHeaderFormat()) : + const_cast<SwFrameFormat*>(rMaster.GetFooter().GetFooterFormat()); + pFormat->SetFormatAttr( aUL ); + XFillStyleItem aFill(drawing::FillStyle_NONE); + pFormat->SetFormatAttr(aFill); + } + } + if( bChgd ) + { + ChgPageDesc( nFrom, aDesc ); + + if( !bCursorSet && bOn ) + { + if ( !IsHeaderFooterEdit() ) + ToggleHeaderFooterEdit(); + bCursorSet = SetCursorInHdFt( + rStyleName.empty() ? SIZE_MAX : nFrom, + bHeader ); + } + } + } + } + EndUndo( SwUndoId::HEADER_FOOTER ); // #i7983# + EndAllAction(); +} + +void SwWrtShell::SetShowHeaderFooterSeparator( FrameControlType eControl, bool bShow ) +{ + SwViewShell::SetShowHeaderFooterSeparator( eControl, bShow ); + if ( !bShow ) + GetView().GetEditWin().GetFrameControlsManager().HideControls( eControl ); +} + +void SwWrtShell::InsertPostIt(SwFieldMgr& rFieldMgr, const SfxRequest& rReq) +{ + SwPostItField* pPostIt = dynamic_cast<SwPostItField*>(rFieldMgr.GetCurField()); + bool bNew = !(pPostIt && pPostIt->GetTyp()->Which() == SwFieldIds::Postit); + if (bNew || GetView().GetPostItMgr()->IsAnswer()) + { + const SvxPostItAuthorItem* pAuthorItem = rReq.GetArg<SvxPostItAuthorItem>(SID_ATTR_POSTIT_AUTHOR); + OUString sAuthor; + if ( pAuthorItem ) + sAuthor = pAuthorItem->GetValue(); + else + { + std::size_t nAuthor = SW_MOD()->GetRedlineAuthor(); + sAuthor = SW_MOD()->GetRedlineAuthor(nAuthor); + } + + const SvxPostItTextItem* pTextItem = rReq.GetArg<SvxPostItTextItem>(SID_ATTR_POSTIT_TEXT); + OUString sText; + if ( pTextItem ) + sText = pTextItem->GetValue(); + + // If we have a text already registered for answer, use that + if (GetView().GetPostItMgr()->IsAnswer() && !GetView().GetPostItMgr()->GetAnswerText().isEmpty()) + { + sText = GetView().GetPostItMgr()->GetAnswerText(); + GetView().GetPostItMgr()->RegisterAnswerText(OUString()); + } + + if ( HasSelection() && !IsTableMode() ) + { + KillPams(); + } + + // #i120513# Inserting a comment into an autocompletion crashes + // --> suggestion has to be removed before + GetView().GetEditWin().StopQuickHelp(); + + SwInsertField_Data aData(SwFieldTypesEnum::Postit, 0, sAuthor, sText, 0); + + if (IsSelFrameMode()) + { + SwFlyFrame* pFly = GetSelectedFlyFrame(); + + // Remember the anchor of the selected object before deletion. + std::optional<SwPosition> oAnchor; + if (pFly) + { + SwFrameFormat* pFormat = pFly->GetFormat(); + if (pFormat) + { + RndStdIds eAnchorId = pFormat->GetAnchor().GetAnchorId(); + if ((eAnchorId == RndStdIds::FLY_AS_CHAR || eAnchorId == RndStdIds::FLY_AT_CHAR) && pFormat->GetAnchor().GetAnchorNode()) + { + oAnchor.emplace(*pFormat->GetAnchor().GetContentAnchor()); + } + } + } + + // A frame is selected, end frame selection. + EnterStdMode(); + GetView().AttrChangedNotify(nullptr); + + // Set up text selection, so the anchor of the frame will be the anchor of the + // comment. + if (pFly) + { + if (oAnchor) + *GetCurrentShellCursor().GetPoint() = *oAnchor; + SwFrameFormat* pFormat = pFly->GetFormat(); + if (pFormat && pFormat->GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR) + { + Right(SwCursorSkipMode::Cells, /*bSelect=*/true, 1, /*bBasicCall=*/false, /*bVisual=*/true); + } + else if (pFormat && pFormat->GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_CHAR) + { + aData.m_oAnnotationRange.emplace(*GetCurrentShellCursor().Start(), + *GetCurrentShellCursor().End()); + } + } + } + + rFieldMgr.InsertField( aData ); + + Push(); + SwCursorShell::Left(1, SwCursorSkipMode::Chars); + pPostIt = static_cast<SwPostItField*>(rFieldMgr.GetCurField()); + Pop(SwCursorShell::PopMode::DeleteCurrent); // Restore cursor position + } + + // Client has disabled annotations rendering, no need to + // focus the postit field + if (comphelper::LibreOfficeKit::isActive() && !comphelper::LibreOfficeKit::isTiledAnnotations()) + return; + + if (pPostIt) + { + SwFieldType* pType = GetDoc()->getIDocumentFieldsAccess().GetFieldType(SwFieldIds::Postit, OUString(), false); + if(auto pFormat = pType->FindFormatForField(pPostIt)) + pFormat->Broadcast( SwFormatFieldHint( nullptr, SwFormatFieldHintWhich::FOCUS, &GetView() ) ); + } +} + +bool SwWrtShell::IsOutlineContentVisible(const size_t nPos) +{ + const SwOutlineNodes& rOutlineNodes = GetDoc()->GetNodes().GetOutLineNds(); + const SwNode* pOutlineNode = rOutlineNodes[nPos]; + + // no layout frame means outline folding is set to include sub levels and the outline node has + // a parent outline node with outline content visible attribute false (folded outline content) + if (!pOutlineNode->GetTextNode()->getLayoutFrame(GetLayout())) + return false; + + // try the next node to determine if this outline node has visible content + SwNodeIndex aIdx(*pOutlineNode, +1); + if (aIdx.GetNode() == aIdx.GetNodes().GetEndOfContent()) // end of regular content + return false; + + if (aIdx.GetNode().IsTextNode() || aIdx.GetNode().IsTableNode() || + aIdx.GetNode().IsSectionNode()) + { + // * sublevels treated as outline content + // If next node (aIdx) doesn't have a layout frame + // then this outline node does not have visible outline content. + // * sublevels NOT treated as outline content + // If the next node (aIdx) is the next outline node + // then return the outline content visible attribute value. + if (!GetViewOptions()->IsTreatSubOutlineLevelsAsContent() && + nPos + 1 < rOutlineNodes.size() && + rOutlineNodes[nPos + 1] == &aIdx.GetNode()) + return GetAttrOutlineContentVisible(nPos); + + if (aIdx.GetNode().IsTextNode()) + return aIdx.GetNode().GetTextNode()->getLayoutFrame(GetLayout()); + if (aIdx.GetNode().IsTableNode()) + { + SwTable& rTable = aIdx.GetNode().GetTableNode()->GetTable(); + return rTable.HasLayout(); + } + if (aIdx.GetNode().IsSectionNode()) + { + const auto pFormat = aIdx.GetNode().GetSectionNode()->GetSection().GetFormat(); + return pFormat && pFormat->IsVisible(); + } + } + + return true; +} + +void SwWrtShell::MakeOutlineLevelsVisible(const int nLevel) +{ + MakeAllOutlineContentTemporarilyVisible a(GetDoc()); + + m_rView.SetMaxOutlineLevelShown(nLevel); + + bool bDocChanged = false; + + const SwOutlineNodes& rOutlineNodes = GetNodes().GetOutLineNds(); + + // Make all missing frames. + for (SwOutlineNodes::size_type nPos = 0; nPos < rOutlineNodes.size(); ++nPos) + { + SwNode* pNode = rOutlineNodes[nPos]; + if (!pNode->GetTextNode()->getLayoutFrame(GetLayout())) + { + SwNodeIndex aIdx(*pNode, +1); + // Make the outline paragraph frame + MakeFrames(GetDoc(), *pNode, aIdx.GetNode()); + // Make the outline content visible but don't set the outline visible attribute and + // don't make outline content made visible not visible that have outline visible + // attribute false. Visibility will be taken care of when + // MakeAllOutlineContentTemporarilyVisible goes out of scope. + MakeOutlineContentVisible(nPos, true, false); + bDocChanged = true; + } + } + // Remove outline paragraph frame and outline content frames above given level. + for (SwOutlineNodes::size_type nPos = 0; nPos < rOutlineNodes.size(); ++nPos) + { + SwNode* pNode = rOutlineNodes[nPos]; + auto nOutlineLevel = pNode->GetTextNode()->GetAttrOutlineLevel(); + if (nOutlineLevel > nLevel) + { + // Remove the outline content but don't set the outline visible attribute. Visibility + // will be taken care of when MakeAllOutlineContentTemporarilyVisible goes out of scope. + MakeOutlineContentVisible(nPos, false, false); + // Remove the outline paragraph frame. + pNode->GetTextNode()->DelFrames(GetLayout()); + bDocChanged = true; + } + } + + // Broadcast DocChanged if document layout has changed so the Navigator will be updated. + if (bDocChanged) + GetDoc()->GetDocShell()->Broadcast(SfxHint(SfxHintId::DocChanged)); +} + +void SwWrtShell::MakeOutlineContentVisible(const size_t nPos, bool bMakeVisible, bool bSetAttrOutlineVisibility) +{ + const SwNodes& rNodes = GetNodes(); + const SwOutlineNodes& rOutlineNodes = rNodes.GetOutLineNds(); + + SwNode* pSttNd = rOutlineNodes[nPos]; + + // determine end node + SwNode* pEndNd = &rNodes.GetEndOfContent(); + if (rOutlineNodes.size() > nPos + 1) + pEndNd = rOutlineNodes[nPos + 1]; + + if (GetViewOptions()->IsTreatSubOutlineLevelsAsContent()) + { + // get the last outline node to include (iPos) + int nLevel = pSttNd->GetTextNode()->GetAttrOutlineLevel(); + int nMaxOutlineLevelShown = m_rView.GetMaxOutlineLevelShown(); + SwOutlineNodes::size_type iPos = nPos; + while (++iPos < rOutlineNodes.size() && + rOutlineNodes[iPos]->GetTextNode()->GetAttrOutlineLevel() > nLevel && + rOutlineNodes[iPos]->GetTextNode()->GetAttrOutlineLevel() <= nMaxOutlineLevelShown); + + // get the correct end node + // the outline node may be in frames, headers, footers special section of doc model + SwNode* pStartOfSectionNodeSttNd = pSttNd->StartOfSectionNode(); + while (pStartOfSectionNodeSttNd->StartOfSectionNode() + != pStartOfSectionNodeSttNd->StartOfSectionNode()->StartOfSectionNode()) + { + pStartOfSectionNodeSttNd = pStartOfSectionNodeSttNd->StartOfSectionNode(); + } + pEndNd = pStartOfSectionNodeSttNd->EndOfSectionNode(); + + if (iPos < rOutlineNodes.size()) + { + SwNode* pStartOfSectionNode = rOutlineNodes[iPos]->StartOfSectionNode(); + while (pStartOfSectionNode->StartOfSectionNode() + != pStartOfSectionNode->StartOfSectionNode()->StartOfSectionNode()) + { + pStartOfSectionNode = pStartOfSectionNode->StartOfSectionNode(); + } + if (pStartOfSectionNodeSttNd == pStartOfSectionNode) + pEndNd = rOutlineNodes[iPos]; + } + } + + // table, text box, header, footer + if (pSttNd->GetTableBox() || pSttNd->GetIndex() < rNodes.GetEndOfExtras().GetIndex()) + { + // limit to within section + if (pSttNd->EndOfSectionIndex() < pEndNd->GetIndex()) + pEndNd = pSttNd->EndOfSectionNode(); + } + // if pSttNd isn't in table but pEndNd is, skip over all outline nodes in table + else if (pEndNd->GetTableBox()) + { + pEndNd = &rNodes.GetEndOfContent(); + for (size_t nOutlinePos = nPos + 2; nOutlinePos < rOutlineNodes.size(); nOutlinePos++) + { + if (!(rOutlineNodes[nOutlinePos]->GetTableBox())) + { + pEndNd = rOutlineNodes[nOutlinePos]; + break; + } + } + } + // end node determined + + // Remove content frames from the next node after the starting outline node to + // the determined ending node. Always do this to prevent the chance of duplicate + // frames being made. They will be remade below if needed. + SwNodeIndex aIdx(*pSttNd, +1); + while (aIdx != *pEndNd) + { + SwNode* pNd = &aIdx.GetNode(); + if (pNd->IsContentNode()) + pNd->GetContentNode()->DelFrames(nullptr); + else if (pNd->IsTableNode()) + pNd->GetTableNode()->DelFrames(nullptr); + ++aIdx; + } + + if (bMakeVisible) // make outline nodes outline content visible + { + // reset the index marker and make frames + aIdx.Assign(*pSttNd, +1); + MakeFrames(GetDoc(), aIdx.GetNode(), *pEndNd); + + if (bSetAttrOutlineVisibility) + { + pSttNd->GetTextNode()->SetAttrOutlineContentVisible(true); + + // make outline content made visible that have outline visible attribute false not visible + while (aIdx != *pEndNd) + { + SwNode* pNd = &aIdx.GetNode(); + if (pNd->IsTextNode() && pNd->GetTextNode()->IsOutline()) + { + SwTextNode* pTextNd = pNd->GetTextNode(); + bool bOutlineContentVisibleAttr = true; + pTextNd->GetAttrOutlineContentVisible(bOutlineContentVisibleAttr); + if (!bOutlineContentVisibleAttr) + { + SwOutlineNodes::size_type iPos; + if (rOutlineNodes.Seek_Entry(pTextNd, &iPos)) + { + if (pTextNd->getLayoutFrame(nullptr)) + MakeOutlineContentVisible(iPos, false); + } + } + } + ++aIdx; + } + } + } + else if (bSetAttrOutlineVisibility) + pSttNd->GetTextNode()->SetAttrOutlineContentVisible(false); +} + +// make content visible or not visible only if needed +void SwWrtShell::InvalidateOutlineContentVisibility() +{ + GetView().GetEditWin().GetFrameControlsManager().HideControls(FrameControlType::Outline); + + const SwOutlineNodes& rOutlineNds = GetNodes().GetOutLineNds(); + for (SwOutlineNodes::size_type nPos = 0; nPos < rOutlineNds.size(); ++nPos) + { + bool bIsOutlineContentVisible = IsOutlineContentVisible(nPos); + bool bOutlineContentVisibleAttr = true; + rOutlineNds[nPos]->GetTextNode()->GetAttrOutlineContentVisible(bOutlineContentVisibleAttr); + if (!bIsOutlineContentVisible && bOutlineContentVisibleAttr) + MakeOutlineContentVisible(nPos); + else if (bIsOutlineContentVisible && !bOutlineContentVisibleAttr) + MakeOutlineContentVisible(nPos, false); + } +} + +void SwWrtShell::MakeAllFoldedOutlineContentVisible(bool bMakeVisible) +{ + if (bMakeVisible) + { + // make all content visible + + // When shortcut is assigned to the show outline content visibility button and used to + // toggle the feature and the mouse pointer is on an outline frame the button will not + // be removed. An easy way to make sure the button does not remain shown is to use the + // HideControls function. + GetView().GetEditWin().GetFrameControlsManager().HideControls(FrameControlType::Outline); + + // temporarily set outline content visible attribute true for folded outline nodes + std::vector<SwNode*> aFoldedOutlineNodeArray; + for (SwNode* pNd: GetNodes().GetOutLineNds()) + { + bool bOutlineContentVisibleAttr = true; + pNd->GetTextNode()->GetAttrOutlineContentVisible(bOutlineContentVisibleAttr); + if (!bOutlineContentVisibleAttr) + { + aFoldedOutlineNodeArray.push_back(pNd); + pNd->GetTextNode()->SetAttrOutlineContentVisible(true); + } + } + + StartAction(); + InvalidateOutlineContentVisibility(); + EndAction(); + + // restore outline content visible attribute for folded outline nodes + for (SwNode* pNd: aFoldedOutlineNodeArray) + pNd->GetTextNode()->SetAttrOutlineContentVisible(false); + } + else + { + AssureStdMode(); + + // Get the outline position of the cursor so the cursor can be place at a visible outline + // node if it is not visible after InvalidateOutlineContentVisiblity below. + SwOutlineNodes::size_type nPos = GetOutlinePos(); + + StartAction(); + InvalidateOutlineContentVisibility(); + EndAction(); + + // If needed, find a visible outline node to place the cursor. + if (nPos != SwOutlineNodes::npos && !IsOutlineContentVisible(nPos)) + { + while (nPos != SwOutlineNodes::npos && + !GetNodes().GetOutLineNds()[nPos]->GetTextNode()->getLayoutFrame(GetLayout())) + --nPos; + if (nPos != SwOutlineNodes::npos) + GotoOutline(nPos); + } + } + GetView().GetDocShell()->Broadcast(SfxHint(SfxHintId::DocChanged)); +} + +bool SwWrtShell::GetAttrOutlineContentVisible(const size_t nPos) const +{ + bool bVisibleAttr = true; + GetNodes().GetOutLineNds()[nPos]->GetTextNode()->GetAttrOutlineContentVisible(bVisibleAttr); + return bVisibleAttr; +} + +bool SwWrtShell::HasFoldedOutlineContentSelected() const +{ + // No need to check for selection over folded outline content when there are no outline nodes. + if (GetDoc()->GetNodes().GetOutLineNds().empty()) + return false; + for(const SwPaM& rPaM : GetCursor()->GetRingContainer()) + { + SwPaM aPaM(*rPaM.GetMark(), *rPaM.GetPoint()); + aPaM.Normalize(); + SwNodeIndex aPointIdx(aPaM.GetPoint()->GetNode()); + SwNodeIndex aMarkIdx(aPaM.GetMark()->GetNode()); + // Prevent crash in the for loop below by adjusting the mark if it is set to the end of + // content node. + if (aMarkIdx.GetNode() == GetDoc()->GetNodes().GetEndOfContent()) + --aMarkIdx; + if (aPointIdx == aMarkIdx) + continue; + // Return true if any nodes in PaM are folded outline content nodes. + SwOutlineNodes::size_type nPos; + for (SwNodeIndex aIdx = aPointIdx; aIdx <= aMarkIdx; ++aIdx) + { + // To allow delete when the start of the selection is at the start of a + // paragraph and the end of the selection is at the start of a paragraph and there + // are no folded outline content nodes in between. + if (aIdx == aMarkIdx && aPaM.GetPoint()->GetContentIndex() == 0 && + aPaM.GetMark()->GetContentIndex() == 0) + return false; + + if (GetDoc()->GetNodes().GetOutLineNds().Seek_Entry(&(aIdx.GetNode()), &nPos) && + !GetAttrOutlineContentVisible(nPos)) + return true; + } + } + return false; +} + +void SwWrtShell::InfoReadOnlyDialog(bool bAsync) const +{ + if (bAsync) + { + auto xInfo = std::make_shared<weld::MessageDialogController>( + GetView().GetFrameWeld(), "modules/swriter/ui/inforeadonlydialog.ui", "InfoReadonlyDialog"); + if (GetViewOptions()->IsShowOutlineContentVisibilityButton() && + HasFoldedOutlineContentSelected()) + { + xInfo->set_primary_text(SwResId(STR_INFORODLG_FOLDED_PRIMARY)); + xInfo->set_secondary_text(SwResId(STR_INFORODLG_FOLDED_SECONDARY)); + } + weld::DialogController::runAsync(xInfo, [](int) {}); + } + else + { + std::unique_ptr<weld::Builder> + xBuilder(Application::CreateBuilder(GetView().GetFrameWeld(), + "modules/swriter/ui/inforeadonlydialog.ui")); + std::unique_ptr<weld::MessageDialog> + xInfo(xBuilder->weld_message_dialog("InfoReadonlyDialog")); + if (GetViewOptions()->IsShowOutlineContentVisibilityButton() && + HasFoldedOutlineContentSelected()) + { + xInfo->set_primary_text(SwResId(STR_INFORODLG_FOLDED_PRIMARY)); + xInfo->set_secondary_text(SwResId(STR_INFORODLG_FOLDED_SECONDARY)); + } + xInfo->run(); + } +} + +bool SwWrtShell::WarnHiddenSectionDialog() const +{ + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder( + GetView().GetFrameWeld(), "modules/swriter/ui/warnhiddensectiondialog.ui")); + std::unique_ptr<weld::MessageDialog> xQuery( + xBuilder->weld_message_dialog("WarnHiddenSectionDialog")); + if (GetViewOptions()->IsShowOutlineContentVisibilityButton() + && HasFoldedOutlineContentSelected()) + { + xQuery->set_primary_text(SwResId(STR_INFORODLG_FOLDED_PRIMARY)); + xQuery->set_secondary_text(SwResId(STR_INFORODLG_FOLDED_SECONDARY)); + } + + return (RET_YES == xQuery->run()); +} + +bool SwWrtShell::WarnSwitchToDesignModeDialog() const +{ + std::unique_ptr<weld::MessageDialog> xQuery(Application::CreateMessageDialog(nullptr, + VclMessageType::Question, VclButtonsType::YesNo, SwResId(STR_A11Y_DESIGN_MODE_PRIMARY))); + xQuery->set_default_response(RET_YES); + xQuery->set_title(SwResId(STR_A11Y_DESIGN_MODE_TITLE)); + xQuery->set_secondary_text(SwResId(STR_A11Y_DESIGN_MODE_SECONDARY)); + + return (RET_YES == xQuery->run()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/wrtsh/wrtsh2.cxx b/sw/source/uibase/wrtsh/wrtsh2.cxx new file mode 100644 index 0000000000..3a9e51136f --- /dev/null +++ b/sw/source/uibase/wrtsh/wrtsh2.cxx @@ -0,0 +1,686 @@ +/* -*- 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 <svl/macitem.hxx> +#include <sfx2/frame.hxx> +#include <svl/eitem.hxx> +#include <svl/listener.hxx> +#include <svl/stritem.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/linkmgr.hxx> +#include <sfx2/viewfrm.hxx> +#include <sot/exchange.hxx> +#include <osl/diagnose.h> +#include <o3tl/string_view.hxx> +#include <fmtinfmt.hxx> +#include <wrtsh.hxx> +#include <docsh.hxx> +#include <fldbas.hxx> +#include <expfld.hxx> +#include <docufld.hxx> +#include <reffld.hxx> +#include <swundo.hxx> +#include <doc.hxx> +#include <frmfmt.hxx> +#include <fmtfld.hxx> +#include <view.hxx> +#include <swevent.hxx> +#include <section.hxx> +#include <navicont.hxx> +#include <txtinet.hxx> +#include <cmdid.h> +#include <swabstdlg.hxx> +#include <SwRewriter.hxx> +#include <authfld.hxx> +#include <ndtxt.hxx> + +#include <com/sun/star/document/XDocumentProperties.hpp> +#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> + +#include <memory> + +#include <LibreOfficeKit/LibreOfficeKitEnums.h> +#include <comphelper/lok.hxx> +#include <sfx2/event.hxx> +#include <sal/log.hxx> + +bool SwWrtShell::InsertField2(SwField const& rField, SwPaM* pAnnotationRange) +{ + ResetCursorStack(); + if(!CanInsert()) + return false; + StartAllAction(); + + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, rField.GetDescription()); + + StartUndo(SwUndoId::INSERT, &aRewriter); + + bool bDeleted = false; + std::optional<SwPaM> pAnnotationTextRange; + if (pAnnotationRange) + { + pAnnotationTextRange.emplace(*pAnnotationRange->Start(), *pAnnotationRange->End()); + } + + if ( HasSelection() ) + { + if ( rField.GetTyp()->Which() == SwFieldIds::Postit ) + { + // for annotation fields: + // - keep the current selection in order to create a corresponding annotation mark + // - collapse cursor to its end + if ( IsTableMode() ) + { + GetTableCrs()->Normalize( false ); + const SwPosition rStartPos( *(GetTableCrs()->GetMark()->GetNode().GetContentNode()), 0 ); + KillPams(); + if ( !IsEndOfPara() ) + { + EndPara(); + } + const SwPosition rEndPos( *GetCurrentShellCursor().GetPoint() ); + pAnnotationTextRange.emplace( rStartPos, rEndPos ); + } + else + { + NormalizePam( false ); + const SwPaM& rCurrPaM = GetCurrentShellCursor(); + pAnnotationTextRange.emplace( *rCurrPaM.GetPoint(), *rCurrPaM.GetMark() ); + ClearMark(); + } + } + else + { + bDeleted = DelRight(); + } + } + + bool const isSuccess = SwEditShell::InsertField(rField, bDeleted); + + if ( pAnnotationTextRange ) + { + if ( GetDoc() != nullptr ) + { + const SwPaM& rCurrPaM = GetCurrentShellCursor(); + if (*rCurrPaM.Start() == *pAnnotationTextRange->Start() + && *rCurrPaM.End() == *pAnnotationTextRange->End()) + { + // Annotation range was passed in externally, and inserting the postit field shifted + // its start/end positions right by one. Restore the original position for the range + // start. This allows commenting on the placeholder character of the field. + if (pAnnotationTextRange->Start()->GetContentIndex() > 0) + pAnnotationTextRange->Start()->AdjustContent(-1); + } + IDocumentMarkAccess* pMarksAccess = GetDoc()->getIDocumentMarkAccess(); + pMarksAccess->makeAnnotationMark( *pAnnotationTextRange, OUString() ); + } + pAnnotationTextRange.reset(); + } + + EndUndo(); + EndAllAction(); + + return isSuccess; +} + +// Start the field update + +void SwWrtShell::UpdateInputFields( SwInputFieldList* pLst ) +{ + // Go through the list of fields and updating + std::unique_ptr<SwInputFieldList> pTmp; + if (!pLst) + { + pTmp.reset(new SwInputFieldList( this )); + pLst = pTmp.get(); + } + + const size_t nCnt = pLst->Count(); + if(!nCnt) + return; + + pLst->PushCursor(); + + bool bCancel = false; + + size_t nIndex = 0; + FieldDialogPressedButton ePressedButton = FieldDialogPressedButton::NONE; + + SwField* pField = GetCurField(); + if (pField) + { + for (size_t i = 0; i < nCnt; i++) + { + if (pField == pLst->GetField(i)) + { + nIndex = i; + break; + } + } + } + + while (!bCancel) + { + bool bPrev = nIndex > 0; + bool bNext = nIndex < nCnt - 1; + pLst->GotoFieldPos(nIndex); + pField = pLst->GetField(nIndex); + if (pField->GetTyp()->Which() == SwFieldIds::Dropdown) + { + bCancel = StartDropDownFieldDlg(pField, bPrev, bNext, GetView().GetFrameWeld(), &ePressedButton); + } + else + bCancel = StartInputFieldDlg(pField, bPrev, bNext, GetView().GetFrameWeld(), &ePressedButton); + + if (!bCancel) + { + // Otherwise update error at multi-selection: + pLst->GetField(nIndex)->GetTyp()->UpdateFields(); + + if (ePressedButton == FieldDialogPressedButton::Previous && nIndex > 0) + nIndex--; + else if (ePressedButton == FieldDialogPressedButton::Next && nIndex < nCnt - 1) + nIndex++; + else + bCancel = true; + } + } + + pLst->PopCursor(); +} + +namespace { + +// Listener class: will close InputField dialog if input field(s) +// is(are) deleted (for instance, by an extension) after the dialog shows up. +// Otherwise, the for loop in SwWrtShell::UpdateInputFields will crash when doing: +// 'pTmp->GetField( i )->GetTyp()->UpdateFields();' +// on a deleted field. +class FieldDeletionListener : public SvtListener +{ + public: + FieldDeletionListener(AbstractFieldInputDlg* pInputFieldDlg, SwField* pField) + : mpInputFieldDlg(pInputFieldDlg) + , mpFormatField(nullptr) + { + SwInputField *const pInputField(dynamic_cast<SwInputField*>(pField)); + SwSetExpField *const pSetExpField(dynamic_cast<SwSetExpField*>(pField)); + + if (pInputField && pInputField->GetFormatField()) + { + mpFormatField = pInputField->GetFormatField(); + } + else if (pSetExpField && pSetExpField->GetFormatField()) + { + mpFormatField = pSetExpField->GetFormatField(); + } + + // Register for possible field deletion while dialog is open + if (mpFormatField) + StartListening(mpFormatField->GetNotifier()); + } + + virtual ~FieldDeletionListener() override + { + // Dialog closed, remove modification listener + EndListeningAll(); + } + + virtual void Notify(const SfxHint& rHint) override + { + // Input field has been deleted: better to close the dialog + if(rHint.GetId() == SfxHintId::Dying) + { + mpFormatField = nullptr; + mpInputFieldDlg->EndDialog(RET_CANCEL); + } + } + private: + VclPtr<AbstractFieldInputDlg> mpInputFieldDlg; + SwFormatField* mpFormatField; +}; + +} + +// Start input dialog for a specific field +bool SwWrtShell::StartInputFieldDlg(SwField* pField, bool bPrevButton, bool bNextButton, + weld::Widget* pParentWin, SwWrtShell::FieldDialogPressedButton* pPressedButton) +{ + + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractFieldInputDlg> pDlg(pFact->CreateFieldInputDlg(pParentWin, *this, pField, bPrevButton, bNextButton)); + + bool bRet; + + { + FieldDeletionListener aModify(pDlg.get(), pField); + bRet = RET_CANCEL == pDlg->Execute(); + } + + if (pPressedButton) + { + if (pDlg->PrevButtonPressed()) + *pPressedButton = FieldDialogPressedButton::Previous; + else if (pDlg->NextButtonPressed()) + *pPressedButton = FieldDialogPressedButton::Next; + } + + pDlg.disposeAndClear(); + GetWin()->PaintImmediately(); + return bRet; +} + +bool SwWrtShell::StartDropDownFieldDlg(SwField* pField, bool bPrevButton, bool bNextButton, + weld::Widget* pParentWin, SwWrtShell::FieldDialogPressedButton* pPressedButton) +{ + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractDropDownFieldDialog> pDlg(pFact->CreateDropDownFieldDialog(pParentWin, *this, pField, bPrevButton, bNextButton)); + const short nRet = pDlg->Execute(); + + if (pPressedButton) + { + if (pDlg->PrevButtonPressed()) + *pPressedButton = FieldDialogPressedButton::Previous; + else if (pDlg->NextButtonPressed()) + *pPressedButton = FieldDialogPressedButton::Next; + } + + pDlg.disposeAndClear(); + bool bRet = RET_CANCEL == nRet; + GetWin()->PaintImmediately(); + if(RET_YES == nRet) + { + GetView().GetViewFrame().GetDispatcher()->Execute(FN_EDIT_FIELD, SfxCallMode::SYNCHRON); + } + return bRet; +} + +// Insert directory - remove selection + +void SwWrtShell::InsertTableOf(const SwTOXBase& rTOX, const SfxItemSet* pSet) +{ + if(!CanInsert()) + return; + + if(HasSelection()) + DelRight(); + + SwEditShell::InsertTableOf(rTOX, pSet); +} + +// Update directory - remove selection + +void SwWrtShell::UpdateTableOf(const SwTOXBase& rTOX, const SfxItemSet* pSet) +{ + if(CanInsert()) + { + SwEditShell::UpdateTableOf(rTOX, pSet); + } +} + +// handler for click on the field given as parameter. +// the cursor is positioned on the field. + +void SwWrtShell::ClickToField(const SwField& rField, bool bExecHyperlinks) +{ + addCurrentPosition(); + + // Since the cross reference and bibliography mark move the cursor, + // only select the field if it's not a Ctrl+Click + if (!bExecHyperlinks + || (SwFieldIds::GetRef != rField.GetTyp()->Which() + && SwFieldIds::TableOfAuthorities != rField.GetTyp()->Which())) + { + StartAllAction(); + Right( SwCursorSkipMode::Chars, true, 1, false ); // Select the field. + NormalizePam(); + EndAllAction(); + } + + m_bIsInClickToEdit = true; + switch( rField.GetTyp()->Which() ) + { + case SwFieldIds::JumpEdit: + { + sal_uInt16 nSlotId = 0; + switch( rField.GetFormat() ) + { + case JE_FMT_TABLE: + nSlotId = FN_INSERT_TABLE; + break; + + case JE_FMT_FRAME: + nSlotId = FN_INSERT_FRAME; + break; + + case JE_FMT_GRAPHIC: nSlotId = SID_INSERT_GRAPHIC; break; + case JE_FMT_OLE: nSlotId = SID_INSERT_OBJECT; break; + + } + + if( nSlotId ) + { + StartUndo( SwUndoId::START ); + //#97295# immediately select the right shell + GetView().StopShellTimer(); + GetView().GetViewFrame().GetDispatcher()->Execute( nSlotId, + SfxCallMode::SYNCHRON|SfxCallMode::RECORD ); + EndUndo( SwUndoId::END ); + } + } + break; + + case SwFieldIds::Macro: + { + const SwMacroField *pField = static_cast<const SwMacroField*>(&rField); + const OUString sText( rField.GetPar2() ); + OUString sRet( sText ); + ExecMacro( pField->GetSvxMacro(), &sRet ); + + // return value changed? + if( sRet != sText ) + { + StartAllAction(); + const_cast<SwField&>(rField).SetPar2( sRet ); + rField.GetTyp()->UpdateFields(); + EndAllAction(); + } + } + break; + + case SwFieldIds::TableOfAuthorities: + { + if (!bExecHyperlinks) + break; // Since it's not a Ctrl+Click, do not jump anywhere + + Point vStartPoint = GetCursor_()->GetPtPos(); + const SwAuthorityField* pField = static_cast<const SwAuthorityField*>(&rField); + + if (auto targetType = pField->GetTargetType(); + targetType == SwAuthorityField::TargetType::UseDisplayURL + || targetType == SwAuthorityField::TargetType::UseTargetURL) + { + // Since the user selected target type with URL, try to use it if not empty + if (const OUString& rURL = pField->GetAbsoluteURL(); + rURL.getLength() > 0) + ::LoadURL(*this, rURL, LoadUrlFlags::NewView, /*rTargetFrameName=*/OUString()); + } + else if (targetType == SwAuthorityField::TargetType::BibliographyTableRow) + { + // Since the user selected to target Bibliography Table Row, + // try finding matching bibliography table line + + const bool bWasViewLocked = IsViewLocked(); + LockView(true); + + // Note: This way of iterating doesn't seem to take into account TOXes + // that are in a frame, probably in some other cases too + GotoPage(1); + while (GotoNextTOXBase()) + { + const SwTOXBase* pIteratedTOX = nullptr; + const SwTOXBase* pPreviousTOX = nullptr; + OUString vFieldText; + while ((pIteratedTOX = GetCurTOX()) != nullptr + && pIteratedTOX->GetType() == TOX_AUTHORITIES) + { + if (pIteratedTOX != pPreviousTOX) + vFieldText = pField->GetAuthority(GetLayout(), &pIteratedTOX->GetTOXForm()); + + if (const SwNode& rCurrentNode = GetCursor()->GetPoint()->GetNode(); + rCurrentNode.GetNodeType() == SwNodeType::Text + && (GetCursor()->GetPoint()->GetNode().FindSectionNode()->GetSection().GetType() + == SectionType::ToxContent) // this checks it's not a heading + && static_cast<const SwTextNode*>(&rCurrentNode)->GetText() == vFieldText) + { + // Since a node has been found that is a text node, isn't a heading, + // and has text matching to text generated by the field, jump to it + LockView(bWasViewLocked); + ShowCursor(); + return; + } + pPreviousTOX = pIteratedTOX; + FwdPara(); + } + } + // Since a matching node has not been found, return to original position + SetCursor(&vStartPoint); + LockView(bWasViewLocked); + } + } + break; + + case SwFieldIds::GetRef: + if (!bExecHyperlinks) + break; + + StartAllAction(); + SwCursorShell::GotoRefMark( static_cast<const SwGetRefField&>(rField).GetSetRefName(), + static_cast<const SwGetRefField&>(rField).GetSubType(), + static_cast<const SwGetRefField&>(rField).GetSeqNo(), + static_cast<const SwGetRefField&>(rField).GetFlags() ); + EndAllAction(); + break; + + case SwFieldIds::Input: + { + const SwInputField* pInputField = dynamic_cast<const SwInputField*>(&rField); + if ( pInputField == nullptr ) + { + StartInputFieldDlg(const_cast<SwField*>(&rField), false, false, GetView().GetFrameWeld()); + } + } + break; + + case SwFieldIds::SetExp: + if( static_cast<const SwSetExpField&>(rField).GetInputFlag() ) + StartInputFieldDlg(const_cast<SwField*>(&rField), false, false, GetView().GetFrameWeld()); + break; + case SwFieldIds::Dropdown : + StartDropDownFieldDlg(const_cast<SwField*>(&rField), false, false, GetView().GetFrameWeld()); + break; + default: + SAL_WARN_IF(rField.IsClickable(), "sw", "unhandled clickable field!"); + } + + m_bIsInClickToEdit = false; +} + +void SwWrtShell::ClickToINetAttr( const SwFormatINetFormat& rItem, LoadUrlFlags nFilter ) +{ + addCurrentPosition(); + + if( rItem.GetValue().isEmpty() ) + return ; + + m_bIsInClickToEdit = true; + + // At first run the possibly set ObjectSelect Macro + const SvxMacro* pMac = rItem.GetMacro( SvMacroItemId::OnClick ); + if( pMac ) + { + SwCallMouseEvent aCallEvent; + aCallEvent.Set( &rItem ); + GetDoc()->CallEvent( SvMacroItemId::OnClick, aCallEvent ); + } + + // So that the implementation of templates is displayed immediately + ::LoadURL( *this, rItem.GetValue(), nFilter, rItem.GetTargetFrame() ); + const SwTextINetFormat* pTextAttr = rItem.GetTextINetFormat(); + if( pTextAttr ) + { + const_cast<SwTextINetFormat*>(pTextAttr)->SetVisited( true ); + const_cast<SwTextINetFormat*>(pTextAttr)->SetVisitedValid( true ); + } + + m_bIsInClickToEdit = false; +} + +bool SwWrtShell::ClickToINetGrf( const Point& rDocPt, LoadUrlFlags nFilter ) +{ + bool bRet = false; + OUString sURL; + OUString sTargetFrameName; + const SwFrameFormat* pFnd = IsURLGrfAtPos( rDocPt, &sURL, &sTargetFrameName ); + if( pFnd && !sURL.isEmpty() ) + { + bRet = true; + // At first run the possibly set ObjectSelect Macro + SwCallMouseEvent aCallEvent; + aCallEvent.Set(EVENT_OBJECT_URLITEM, pFnd); + GetDoc()->CallEvent(SvMacroItemId::OnClick, aCallEvent); + + ::LoadURL(*this, sURL, nFilter, sTargetFrameName); + } + return bRet; +} + +static void LoadURL(SwView& rView, const OUString& rURL, LoadUrlFlags nFilter, + const OUString& rTargetFrameName) +{ + SwDocShell* pDShell = rView.GetDocShell(); + OSL_ENSURE( pDShell, "No DocShell?!"); + SfxViewFrame& rViewFrame = rView.GetViewFrame(); + + if (!SfxObjectShell::AllowedLinkProtocolFromDocument(rURL, pDShell, rViewFrame.GetFrameWeld())) + return; + + // We are doing tiledRendering, let the client handles the URL loading, + // unless we are jumping to a TOC mark. + if (comphelper::LibreOfficeKit::isActive() && !rURL.startsWith("#")) + { + rView.libreOfficeKitViewCallback(LOK_CALLBACK_HYPERLINK_CLICKED, rURL.toUtf8()); + return; + } + + OUString sTargetFrame(rTargetFrameName); + if (sTargetFrame.isEmpty() && pDShell) + { + using namespace ::com::sun::star; + uno::Reference<document::XDocumentPropertiesSupplier> xDPS( + pDShell->GetModel(), uno::UNO_QUERY_THROW); + uno::Reference<document::XDocumentProperties> xDocProps + = xDPS->getDocumentProperties(); + sTargetFrame = xDocProps->getDefaultTarget(); + } + + OUString sReferer; + if( pDShell && pDShell->GetMedium() ) + sReferer = pDShell->GetMedium()->GetName(); + SfxFrameItem aView( SID_DOCFRAME, &rViewFrame ); + SfxStringItem aName( SID_FILE_NAME, rURL ); + SfxStringItem aTargetFrameName( SID_TARGETNAME, sTargetFrame ); + SfxStringItem aReferer( SID_REFERER, sReferer ); + + SfxBoolItem aNewView( SID_OPEN_NEW_VIEW, false ); + //#39076# Silent can be removed accordingly to SFX. + SfxBoolItem aBrowse( SID_BROWSE, true ); + + if ((nFilter & LoadUrlFlags::NewView) && !comphelper::LibreOfficeKit::isActive()) + aTargetFrameName.SetValue( "_blank" ); + + rViewFrame.GetDispatcher()->ExecuteList(SID_OPENDOC, + SfxCallMode::ASYNCHRON|SfxCallMode::RECORD, + { + &aName, + &aNewView, /*&aSilent,*/ + &aReferer, + &aView, &aTargetFrameName, + &aBrowse + }); +} + +void LoadURL( SwViewShell& rVSh, const OUString& rURL, LoadUrlFlags nFilter, + const OUString& rTargetFrameName ) +{ + OSL_ENSURE( !rURL.isEmpty(), "what should be loaded here?" ); + if( rURL.isEmpty() ) + return ; + + // The shell could be 0 also!!!!! + if (auto pSh = dynamic_cast<SwWrtShell*>(&rVSh)) + ::LoadURL(pSh->GetView(), rURL, nFilter, rTargetFrameName); +} + +void SwWrtShell::NavigatorPaste( const NaviContentBookmark& rBkmk, + const sal_uInt16 nAction ) +{ + if( EXCHG_IN_ACTION_COPY == nAction ) + { + // Insert + OUString sURL = rBkmk.GetURL(); + // Is this is a jump within the current Doc? + const SwDocShell* pDocShell = GetView().GetDocShell(); + if(pDocShell->HasName()) + { + const OUString rName = pDocShell->GetMedium()->GetURLObject().GetURLNoMark(); + + if (sURL.startsWith(rName)) + { + if (sURL.getLength()>rName.getLength()) + { + sURL = sURL.copy(rName.getLength()); + } + else + { + sURL.clear(); + } + } + } + SwFormatINetFormat aFormat( sURL, OUString() ); + InsertURL( aFormat, rBkmk.GetDescription() ); + } + else + { + SwSectionData aSection( SectionType::FileLink, GetUniqueSectionName() ); + OUString aLinkFile = o3tl::getToken(rBkmk.GetURL(), 0, '#') + + OUStringChar(sfx2::cTokenSeparator) + + OUStringChar(sfx2::cTokenSeparator) + + o3tl::getToken(rBkmk.GetURL(), 1, '#'); + aSection.SetLinkFileName( aLinkFile ); + aSection.SetProtectFlag( true ); + const SwSection* pIns = InsertSection( aSection ); + if( EXCHG_IN_ACTION_MOVE == nAction && pIns ) + { + aSection = SwSectionData(*pIns); + aSection.SetLinkFileName( OUString() ); + aSection.SetType( SectionType::Content ); + aSection.SetProtectFlag( false ); + + // the update of content from linked section at time delete + // the undostack. Then the change of the section don't create + // any undoobject. - BUG 69145 + bool bDoesUndo = DoesUndo(); + SwUndoId nLastUndoId(SwUndoId::EMPTY); + if (GetLastUndoInfo(nullptr, & nLastUndoId)) + { + if (SwUndoId::INSSECTION != nLastUndoId) + { + DoUndo(false); + } + } + UpdateSection( GetSectionFormatPos( *pIns->GetFormat() ), aSection ); + DoUndo( bDoesUndo ); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/wrtsh/wrtsh3.cxx b/sw/source/uibase/wrtsh/wrtsh3.cxx new file mode 100644 index 0000000000..0907794d7c --- /dev/null +++ b/sw/source/uibase/wrtsh/wrtsh3.cxx @@ -0,0 +1,392 @@ +/* -*- 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 <svx/svxids.hrc> +#include <sfx2/app.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/viewfrm.hxx> +#include <svx/svdmark.hxx> +#include <svx/svdview.hxx> +#include <svx/svdouno.hxx> +#include <svx/srchdlg.hxx> +#include <com/sun/star/form/FormButtonType.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <osl/diagnose.h> +#include <sfx2/dispatch.hxx> +#include <comphelper/lok.hxx> +#include <tools/json_writer.hxx> +#include <LibreOfficeKit/LibreOfficeKitEnums.h> + +#include <swmodule.hxx> +#include <wrtsh.hxx> +#include <view.hxx> +#include <IMark.hxx> +#include <doc.hxx> +#include <formatcontentcontrol.hxx> +#include <IDocumentUndoRedo.hxx> +#include <SwRewriter.hxx> +#include <strings.hrc> +#include <textcontentcontrol.hxx> + +using namespace ::com::sun::star; + +bool SwWrtShell::MoveBookMark( BookMarkMove eFuncId, const ::sw::mark::IMark* const pMark) +{ + addCurrentPosition(); + (this->*m_fnKillSel)( nullptr, false ); + + bool bRet = true; + switch(eFuncId) + { + case BOOKMARK_INDEX:bRet = SwCursorShell::GotoMark( pMark );break; + case BOOKMARK_NEXT: bRet = SwCursorShell::GoNextBookmark();break; + case BOOKMARK_PREV: bRet = SwCursorShell::GoPrevBookmark();break; + default:;//prevent warning + } + + if( bRet && IsSelFrameMode() ) + { + UnSelectFrame(); + LeaveSelFrameMode(); + } + if( IsSelection() ) + { + m_fnKillSel = &SwWrtShell::ResetSelect; + m_fnSetCursor = &SwWrtShell::SetCursorKillSel; + } + return bRet; +} + +bool SwWrtShell::GotoField( const SwFormatField& rField ) +{ + (this->*m_fnKillSel)( nullptr, false ); + + bool bRet = SwCursorShell::GotoFormatField( rField ); + if( bRet && IsSelFrameMode() ) + { + UnSelectFrame(); + LeaveSelFrameMode(); + } + + if( IsSelection() ) + { + m_fnKillSel = &SwWrtShell::ResetSelect; + m_fnSetCursor = &SwWrtShell::SetCursorKillSel; + } + + return bRet; +} + +bool SwWrtShell::GotoContentControl(const SwFormatContentControl& rContentControl, + bool bOnlyRefresh) +{ + std::shared_ptr<SwContentControl> pContentControl = rContentControl.GetContentControl(); + if (IsFrameSelected() && pContentControl && pContentControl->GetPicture()) + { + // A frame is already selected, and its anchor is inside a picture content control. + if (pContentControl->GetShowingPlaceHolder()) + { + // Replace the placeholder image with a real one. + GetView().StopShellTimer(); + if (comphelper::LibreOfficeKit::isActive()) + { + tools::JsonWriter aJson; + aJson.put("action", "change-picture"); + OString pJson(aJson.finishAndGetAsOString()); + GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_CONTENT_CONTROL, + pJson); + } + else + { + GetView().GetViewFrame().GetDispatcher()->Execute(SID_CHANGE_PICTURE, + SfxCallMode::SYNCHRON); + } + pContentControl->SetShowingPlaceHolder(false); + } + return true; + } + + (this->*m_fnKillSel)(nullptr, false); + + bool bRet = SwCursorShell::GotoFormatContentControl(rContentControl); + + if (bRet && pContentControl && pContentControl->GetCheckbox()) + { + // Checkbox: GotoFormatContentControl() selected the old state. + LockView(/*bViewLocked=*/true); + OUString aOldState = GetCursorDescr(); + OUString aNewState; + if (pContentControl->GetChecked()) + aNewState = bOnlyRefresh ? pContentControl->GetCheckedState() + : pContentControl->GetUncheckedState(); + else + aNewState = bOnlyRefresh ? pContentControl->GetUncheckedState() + : pContentControl->GetCheckedState(); + + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, aOldState); + aRewriter.AddRule(UndoArg2, SwResId(STR_YIELDS)); + aRewriter.AddRule(UndoArg3, aNewState); + GetIDocumentUndoRedo().StartUndo(SwUndoId::REPLACE, &aRewriter); + + // Toggle the state. + pContentControl->SetReadWrite(true); + DelLeft(); + if (!bOnlyRefresh) + pContentControl->SetChecked(!pContentControl->GetChecked()); + Insert(aNewState); + pContentControl->SetReadWrite(false); + + GetIDocumentUndoRedo().EndUndo(SwUndoId::REPLACE, &aRewriter); + LockView(/*bViewLocked=*/false); + ShowCursor(); + } + else if (bRet && pContentControl && pContentControl->GetSelectedListItem()) + { + // Dropdown: GotoFormatContentControl() selected the old content. + size_t nSelectedListItem = *pContentControl->GetSelectedListItem(); + LockView(/*bViewLocked=*/true); + OUString aOldState = GetCursorDescr(); + OUString aNewState = pContentControl->GetListItems()[nSelectedListItem].ToString(); + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, aOldState); + aRewriter.AddRule(UndoArg2, SwResId(STR_YIELDS)); + aRewriter.AddRule(UndoArg3, SwResId(STR_START_QUOTE) + aNewState + SwResId(STR_END_QUOTE)); + GetIDocumentUndoRedo().StartUndo(SwUndoId::REPLACE, &aRewriter); + + // Update the content. + pContentControl->SetReadWrite(true); + DelLeft(); + pContentControl->SetSelectedListItem(std::nullopt); + Insert(aNewState); + pContentControl->SetReadWrite(false); + + GetIDocumentUndoRedo().EndUndo(SwUndoId::REPLACE, &aRewriter); + LockView(/*bViewLocked=*/false); + ShowCursor(); + } + else if (bRet && pContentControl && pContentControl->GetSelectedDate()) + { + // Date: GotoFormatContentControl() selected the old content. + LockView(/*bViewLocked=*/true); + OUString aOldState = GetCursorDescr(); + OUString aNewState = pContentControl->GetDateString(); + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, aOldState); + aRewriter.AddRule(UndoArg2, SwResId(STR_YIELDS)); + aRewriter.AddRule(UndoArg3, SwResId(STR_START_QUOTE) + aNewState + SwResId(STR_END_QUOTE)); + GetIDocumentUndoRedo().StartUndo(SwUndoId::REPLACE, &aRewriter); + + // Write the doc model. + pContentControl->SetCurrentDateValue(*pContentControl->GetSelectedDate()); + pContentControl->SetSelectedDate(std::nullopt); + + // Update the content. + DelLeft(); + Insert(aNewState); + + GetIDocumentUndoRedo().EndUndo(SwUndoId::REPLACE, &aRewriter); + LockView(/*bViewLocked=*/false); + ShowCursor(); + } + + if (bRet && IsSelFrameMode()) + { + UnSelectFrame(); + LeaveSelFrameMode(); + } + + if (IsSelection()) + { + m_fnKillSel = &SwWrtShell::ResetSelect; + m_fnSetCursor = &SwWrtShell::SetCursorKillSel; + } + + return bRet; +} + +bool SwWrtShell::GotoFieldmark(::sw::mark::IFieldmark const * const pMark) +{ + (this->*m_fnKillSel)( nullptr, false ); + bool bRet = SwCursorShell::GotoFieldmark(pMark); + if( bRet && IsSelFrameMode() ) + { + UnSelectFrame(); + LeaveSelFrameMode(); + } + if( IsSelection() ) + { + m_fnKillSel = &SwWrtShell::ResetSelect; + m_fnSetCursor = &SwWrtShell::SetCursorKillSel; + } + return bRet; +} + +// Invalidate FontWork-Slots + +void SwWrtShell::DrawSelChanged( ) +{ + static sal_uInt16 const aInval[] = + { + SID_ATTR_FILL_STYLE, SID_ATTR_FILL_COLOR, SID_ATTR_LINE_STYLE, + SID_ATTR_LINE_WIDTH, SID_ATTR_LINE_COLOR, + /*AF: these may be needed for the sidebar. + SID_SVX_AREA_TRANSPARENCY, SID_SVX_AREA_TRANSP_GRADIENT, + SID_SVX_AREA_TRANS_TYPE, + */ + 0 + }; + + GetView().GetViewFrame().GetBindings().Invalidate(aInval); + + bool bOldVal = g_bNoInterrupt; + g_bNoInterrupt = true; // Trick to run AttrChangedNotify by timer. + GetView().AttrChangedNotify(nullptr); + g_bNoInterrupt = bOldVal; +} + +void SwWrtShell::GotoMark( const OUString& rName ) +{ + IDocumentMarkAccess::const_iterator_t ppMark = getIDocumentMarkAccess()->findMark( rName ); + if (ppMark == getIDocumentMarkAccess()->getAllMarksEnd()) + return; + MoveBookMark( BOOKMARK_INDEX, *ppMark ); +} + +void SwWrtShell::GotoMark( const ::sw::mark::IMark* const pMark ) +{ + MoveBookMark( BOOKMARK_INDEX, pMark ); +} + +bool SwWrtShell::GoNextBookmark() +{ + if ( !getIDocumentMarkAccess()->getBookmarksCount() ) + { + SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound ); + return false; + } + LockView( true ); + bool bRet = MoveBookMark( BOOKMARK_NEXT ); + if ( !bRet ) + { + MoveBookMark( BOOKMARK_INDEX, *getIDocumentMarkAccess()->getBookmarksBegin() ); + SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::EndWrapped ); + } + else + SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty ); + LockView( false ); + ShowCursor(); + return true; +} + +bool SwWrtShell::GoPrevBookmark() +{ + if ( !getIDocumentMarkAccess()->getBookmarksCount() ) + { + SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound ); + return false; + } + LockView( true ); + bool bRet = MoveBookMark( BOOKMARK_PREV ); + if ( !bRet ) + { + MoveBookMark( BOOKMARK_INDEX, *( getIDocumentMarkAccess()->getBookmarksEnd() - 1 ) ); + SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::StartWrapped ); + } + else + SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty ); + LockView( false ); + ShowCursor(); + return true; +} + +void SwWrtShell::ExecMacro( const SvxMacro& rMacro, OUString* pRet, SbxArray* pArgs ) +{ + // execute macro, if it is allowed. + if ( IsMacroExecAllowed() ) + { + GetDoc()->ExecMacro( rMacro, pRet, pArgs ); + } +} + +sal_uInt16 SwWrtShell::CallEvent( SvMacroItemId nEvent, const SwCallMouseEvent& rCallEvent, + bool bChkPtr) +{ + return GetDoc()->CallEvent( nEvent, rCallEvent, bChkPtr ); +} + + // If a util::URL-Button is selected, return its util::URL + // otherwise an empty string. +bool SwWrtShell::GetURLFromButton( OUString& rURL, OUString& rDescr ) const +{ + bool bRet = false; + const SdrView *pDView = GetDrawView(); + if( pDView ) + { + // A fly is precisely achievable if it is selected. + const SdrMarkList &rMarkList = pDView->GetMarkedObjectList(); + + if (rMarkList.GetMark(0)) + { + SdrUnoObj* pUnoCtrl = dynamic_cast<SdrUnoObj*>( rMarkList.GetMark(0)->GetMarkedSdrObj() ); + if (pUnoCtrl && SdrInventor::FmForm == pUnoCtrl->GetObjInventor()) + { + const uno::Reference< awt::XControlModel >& xControlModel = pUnoCtrl->GetUnoControlModel(); + + OSL_ENSURE( xControlModel.is(), "UNO-Control without Model" ); + if( !xControlModel.is() ) + return bRet; + + uno::Reference< beans::XPropertySet > xPropSet(xControlModel, uno::UNO_QUERY); + + uno::Any aTmp; + + uno::Reference< beans::XPropertySetInfo > xInfo = xPropSet->getPropertySetInfo(); + if(xInfo->hasPropertyByName( "ButtonType" )) + { + aTmp = xPropSet->getPropertyValue( "ButtonType" ); + form::FormButtonType eTmpButtonType; + aTmp >>= eTmpButtonType; + if( form::FormButtonType_URL == eTmpButtonType) + { + // Label + aTmp = xPropSet->getPropertyValue( "Label" ); + OUString uTmp; + if( (aTmp >>= uTmp) && !uTmp.isEmpty()) + { + rDescr = uTmp; + } + + // util::URL + aTmp = xPropSet->getPropertyValue( "TargetURL" ); + if( (aTmp >>= uTmp) && !uTmp.isEmpty()) + { + rURL = uTmp; + } + bRet = true; + } + } + } + } + } + + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/wrtsh/wrtsh4.cxx b/sw/source/uibase/wrtsh/wrtsh4.cxx new file mode 100644 index 0000000000..0442e3152b --- /dev/null +++ b/sw/source/uibase/wrtsh/wrtsh4.cxx @@ -0,0 +1,253 @@ +/* -*- 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 <wrtsh.hxx> + +#include <LibreOfficeKit/LibreOfficeKitEnums.h> + +// Private methods, which move the cursor over search. +// The removal of the selection must be made on the level above. + +// The beginning of a word is the follow of a +// non-delimiter to delimiter. Furthermore, the follow of +// non-sentence separators on sentence separator. +// The begin of paragraph is also the word beginning. + +void SwWrtShell::SttWrd() +{ + if ( IsSttPara() ) + return; + // Create temporary cursor without selection. + Push(); + ClearMark(); + if( !GoStartWord() ) + // not found --> go to the beginning of the paragraph. + SwCursorShell::MovePara( GoCurrPara, fnParaStart ); + ClearMark(); + // If Mark was previously set, summarize. + Combine(); +} + +// The end of a word is the follow of separator to nonseparator. +// The end of a word is also the sequence of word separators to +// punctuation marks. +// The end of a paragraph is also the end of a word. + +void SwWrtShell::EndWrd() +{ + if ( IsEndWrd() ) + return; + // Create temporary cursor without selection. + Push(); + ClearMark(); + if( !GoEndWord() ) + // not found --> go to the end of the paragraph. + SwCursorShell::MovePara(GoCurrPara, fnParaEnd); + ClearMark(); + // If Mark was previously set, summarize. + Combine(); +} + +bool SwWrtShell::NxtWrd_() +{ + bool bRet = false; + while( IsEndPara() ) // If already at the end, then the next??? + { + if(!SwCursorShell::Right(1,SwCursorSkipMode::Chars)) // Document - end ?? + { + Pop(SwCursorShell::PopMode::DeleteCurrent); + return bRet; + } + bRet = IsStartWord(); + } + Push(); + ClearMark(); + while( !bRet ) + { + if( !GoNextWord() ) + { + if( (!IsEndPara() && !SwCursorShell::MovePara( GoCurrPara, fnParaEnd ) ) + || !SwCursorShell::Right(1,SwCursorSkipMode::Chars) ) + break; + bRet = IsStartWord(); + } + else + bRet = true; + } + ClearMark(); + Combine(); + return bRet; +} + +bool SwWrtShell::PrvWrd_() +{ + bool bRet = false; + while( IsSttPara() ) + { // if already at the beginning, then the next??? + if(!SwCursorShell::Left(1,SwCursorSkipMode::Chars)) + { // Document - beginning ?? + Pop(SwCursorShell::PopMode::DeleteCurrent); + return bRet; + } + bRet = IsStartWord() || IsEndPara(); + } + Push(); + ClearMark(); + while( !bRet ) + { + if( !GoPrevWord() ) + { + if( (!IsSttPara() && !SwCursorShell::MovePara( GoCurrPara, fnParaStart ) ) + || !SwCursorShell::Left(1,SwCursorSkipMode::Chars) ) + break; + bRet = IsStartWord(); + } + else + bRet = true; + } + ClearMark(); + Combine(); + return bRet; +} + +// #i92468# +// method code of <SwWrtShell::NxtWrd_()> before fix for issue i72162 +bool SwWrtShell::NxtWrdForDelete() +{ + if ( IsEndPara() ) + { + if ( !SwCursorShell::Right(1,SwCursorSkipMode::Chars) ) + { + Pop(SwCursorShell::PopMode::DeleteCurrent); + return false; + } + return true; + } + Push(); + ClearMark(); + if ( !GoNextWord() ) + { + SwCursorShell::MovePara( GoCurrPara, fnParaEnd ); + } + ClearMark(); + Combine(); + return true; +} + +// method code of <SwWrtShell::PrvWrd_()> before fix for issue i72162 +bool SwWrtShell::PrvWrdForDelete() +{ + if ( IsSttPara() ) + { + if ( !SwCursorShell::Left(1,SwCursorSkipMode::Chars) ) + { + Pop(SwCursorShell::PopMode::DeleteCurrent); + return false; + } + return true; + } + Push(); + ClearMark(); + if( !GoPrevWord() ) + { + SwCursorShell::MovePara( GoCurrPara, fnParaStart ); + } + ClearMark(); + Combine(); + return true; +} + +bool SwWrtShell::FwdSentence_() +{ + Push(); + ClearMark(); + if(!SwCursorShell::Right(1,SwCursorSkipMode::Chars)) + { + Pop(SwCursorShell::PopMode::DeleteCurrent); + return false; + } + if( !GoNextSentence() && !IsEndPara() ) + SwCursorShell::MovePara(GoCurrPara, fnParaEnd); + + ClearMark(); + Combine(); + return true; +} + +bool SwWrtShell::BwdSentence_() +{ + Push(); + ClearMark(); + if(!SwCursorShell::Left(1,SwCursorSkipMode::Chars)) + { + Pop(SwCursorShell::PopMode::DeleteCurrent); + return false; + } + if( !GoStartSentence() && !IsSttPara() ) + // not found --> go to the beginning of the paragraph + SwCursorShell::MovePara( GoCurrPara, fnParaStart ); + ClearMark(); + Combine(); + return true; +} + +bool SwWrtShell::FwdPara_() +{ + Push(); + ClearMark(); + bool bRet = SwCursorShell::MovePara(GoNextPara, fnParaStart); + + ClearMark(); + Combine(); + return bRet; +} + +bool SwWrtShell::BwdPara_() +{ + Push(); + ClearMark(); + + bool bRet = SwCursorShell::MovePara(GoPrevPara, fnParaStart); + if ( !bRet && !IsSttOfPara() ) + { + SttPara(); + } + + ClearMark(); + Combine(); + return bRet; +} + +std::optional<OString> SwWrtShell::getLOKPayload(int nType, int nViewId) const +{ + switch(nType) + { + case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR: + case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR: + return GetVisibleCursor()->getLOKPayload(nType, nViewId); + case LOK_CALLBACK_TEXT_SELECTION: + case LOK_CALLBACK_TEXT_SELECTION_START: + case LOK_CALLBACK_TEXT_SELECTION_END: + case LOK_CALLBACK_TEXT_VIEW_SELECTION: + return GetCursor_()->getLOKPayload(nType, nViewId); + } + abort(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/wrtsh/wrtundo.cxx b/sw/source/uibase/wrtsh/wrtundo.cxx new file mode 100644 index 0000000000..770c59367a --- /dev/null +++ b/sw/source/uibase/wrtsh/wrtundo.cxx @@ -0,0 +1,151 @@ +/* -*- 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 <svl/slstitm.hxx> +#include <rtl/ustrbuf.hxx> +#include <wrtsh.hxx> +#include <swundo.hxx> +#include <IDocumentUndoRedo.hxx> +#include <swdtflvr.hxx> +#include <svtools/svtresid.hxx> +#include <svtools/strings.hrc> +#include <osl/diagnose.h> + +// Undo ends all modes. If a selection is emerged by the Undo, +// this must be considered for further action. + +void SwWrtShell::Do(DoType eDoType, sal_uInt16 nCnt, sal_uInt16 nOffset) +{ + // #105332# save current state of DoesUndo() + bool bSaveDoesUndo = DoesUndo(); + + StartAllAction(); + switch (eDoType) + { + case UNDO: + DoUndo(false); // #i21739# + // Reset modes + EnterStdMode(); + SwEditShell::Undo(nCnt, nOffset); + break; + case REDO: + DoUndo(false); // #i21739# + // Reset modes + EnterStdMode(); + SwEditShell::Redo( nCnt ); + break; + case REPEAT: + // #i21739# do not touch undo flag here !!! + SwEditShell::Repeat( nCnt ); + break; + } + EndAllAction(); + // #105332# restore undo state + DoUndo(bSaveDoesUndo); + + bool bCreateXSelection = false; + const bool bFrameSelected = IsFrameSelected() || IsObjSelected(); + if ( IsSelection() ) + { + if ( bFrameSelected ) + UnSelectFrame(); + + // Set the function pointer for canceling the selection at the + // cursor position. + m_fnKillSel = &SwWrtShell::ResetSelect; + m_fnSetCursor = &SwWrtShell::SetCursorKillSel; + bCreateXSelection = true; + } + else if ( bFrameSelected ) + { + EnterSelFrameMode(); + bCreateXSelection = true; + } + else if( (CNT_GRF | CNT_OLE ) & GetCntType() ) + { + SelectObj( GetCharRect().Pos() ); + EnterSelFrameMode(); + bCreateXSelection = true; + } + + if( bCreateXSelection ) + SwTransferable::CreateSelection( *this ); + + // Bug 32918: After deleting of the numbering the object panel remains. + // Why is not here always a CallChgLink called? + CallChgLnk(); +} + +OUString SwWrtShell::GetDoString( DoType eDoType ) const +{ + OUString aUndoStr; + TranslateId pResStr = STR_UNDO; + switch( eDoType ) + { + case UNDO: + pResStr = STR_UNDO; + (void)GetLastUndoInfo(&aUndoStr, nullptr, &m_rView); + break; + case REDO: + pResStr = STR_REDO; + (void)GetFirstRedoInfo(&aUndoStr, nullptr, &m_rView); + break; + default:;//prevent warning + } + + return SvtResId(pResStr) + aUndoStr; +} + +void SwWrtShell::GetDoStrings( DoType eDoType, SfxStringListItem& rStrs ) const +{ + SwUndoComments_t comments; + switch( eDoType ) + { + case UNDO: + comments = GetIDocumentUndoRedo().GetUndoComments(); + break; + case REDO: + comments = GetIDocumentUndoRedo().GetRedoComments(); + break; + default:;//prevent warning + } + + OUStringBuffer buf; + for (const OUString & comment : comments) + { + OSL_ENSURE(!comment.isEmpty(), "no Undo/Redo Text set"); + buf.append(comment + "\n"); + } + rStrs.SetString(buf.makeStringAndClear()); +} + +OUString SwWrtShell::GetRepeatString() const +{ + OUString str; + GetRepeatInfo(& str); + + if (str.isEmpty()) + { + return str; + } + + return SvtResId(STR_REPEAT) + str; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |