1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <hintids.hxx>
23 #include <editeng/lrspitem.hxx>
25 #include <drawbase.hxx>
26 #include <unobaseclass.hxx>
27 #include <fmtanchr.hxx>
31 #include <docufld.hxx>
32 #include <IDocumentUndoRedo.hxx>
33 #include <i18nutil/unicode.hxx>
34 #include <o3tl/temporary.hxx>
35 #include <rtl/character.hxx>
36 #include <osl/diagnose.h>
38 #include <IDocumentRedlineAccess.hxx>
40 inline void SwWrtShell::OpenMark()
48 inline void SwWrtShell::CloseMark( bool bOkFlag
)
62 bool SwWrtShell::TryRemoveIndent()
66 SfxItemSetFixed
<RES_MARGIN_FIRSTLINE
, RES_MARGIN_FIRSTLINE
> aAttrSet(GetAttrPool());
69 SvxFirstLineIndentItem
firstLine(aAttrSet
.Get(RES_MARGIN_FIRSTLINE
));
70 SvxTextLeftMarginItem
leftMargin(aAttrSet
.Get(RES_MARGIN_TEXTLEFT
));
71 short aOldFirstLineOfst
= firstLine
.GetTextFirstLineOffset();
73 if (aOldFirstLineOfst
> 0)
75 firstLine
.SetTextFirstLineOffset(0);
78 else if (aOldFirstLineOfst
< 0)
80 // this used to call SetLeft() but this should be the same result
81 firstLine
.SetTextFirstLineOffset(0);
82 leftMargin
.SetTextLeft(leftMargin
.GetTextLeft() + aOldFirstLineOfst
);
85 else if (leftMargin
.GetTextLeft() != 0)
87 leftMargin
.SetTextLeft(0);
93 aAttrSet
.Put(firstLine
);
94 aAttrSet
.Put(leftMargin
);
101 /** Description: Erase the line. */
103 void SwWrtShell::DelLine()
105 SwActContext
aActContext(this);
107 // remember the old cursor
110 SwCursorShell::LeftMargin();
112 SwCursorShell::RightMargin();
114 bool bRet
= Delete(false);
115 Pop(SwCursorShell::PopMode::DeleteCurrent
);
120 void SwWrtShell::DelToStartOfLine()
123 SwCursorShell::LeftMargin();
124 bool bRet
= Delete(false);
128 void SwWrtShell::DelToEndOfLine()
131 SwCursorShell::RightMargin();
132 bool bRet
= Delete(false);
136 bool SwWrtShell::DelLeft()
138 // If it's a Fly, throw it away
139 SelectionType nSelType
= GetSelectionType();
140 const SelectionType nCmp
= SelectionType::Frame
| SelectionType::Graphic
| SelectionType::Ole
| SelectionType::DrawObject
;
141 if( nCmp
& nSelType
)
143 // #108205# Remember object's position.
144 Point aTmpPt
= GetObjRect().TopLeft();
148 // #108205# Set cursor to remembered position.
154 nSelType
= GetSelectionType();
155 if ( nCmp
& nSelType
)
164 // If a selection exists, erase this
167 if( !IsBlockMode() || HasSelection() )
169 //OS: Once again Basic: SwActContext must be leaved
170 //before EnterStdMode!
172 SwActContext
aActContext(this);
191 // JP 29.06.95: never erase a table standing in front of it.
193 const SwTableNode
* pWasInTableNd
= SwCursorShell::IsCursorInTable();
195 if( SwCursorShell::IsSttPara())
197 // Start/EndAllAction to avoid cursor flickering
198 UnoActionContext
c(GetDoc());
199 SwCursorShell::Push();
201 // #i4032# Don't actually call a 'delete' if we
202 // changed the table cell, compare DelRight().
203 const SwStartNode
* pSNdOld
= pWasInTableNd
?
204 GetCursor()->GetPointNode().FindTableBoxStartNode() :
207 // If the cursor is at the beginning of a paragraph, try to step
208 // backwards. On failure we are done.
209 bool bDoSomething
= SwCursorShell::Left(1,SwCursorSkipMode::Chars
);
213 // If the cursor entered or left a table (or both) we are done.
214 const SwTableNode
* pIsInTableNd
= SwCursorShell::IsCursorInTable();
215 bDoSomething
= pIsInTableNd
== pWasInTableNd
;
219 const SwStartNode
* pSNdNew
= pIsInTableNd
?
220 GetCursor()->GetPointNode().FindTableBoxStartNode() :
223 // #i4032# Don't actually call a 'delete' if we
224 // changed the table cell, compare DelRight().
225 bDoSomething
= pSNdOld
== pSNdNew
;
231 // tdf#115132 Restore previous position and we are done
232 SwCursorShell::Pop(SwCursorShell::PopMode::DeleteCurrent
);
236 SwCursorShell::Pop(SwCursorShell::PopMode::DeleteStack
);
239 SwCursorShell::Right(1,SwCursorSkipMode::Chars
);
240 SwCursorShell::SwapPam();
245 // If we are just to the right to a fieldmark, then remove it completely
246 const SwPosition
* pCurPos
= GetCursor()->GetPoint();
247 SwPosition
aPrevChar(*pCurPos
->GetContentNode(), pCurPos
->GetContentIndex() - 1);
248 sw::mark::IFieldmark
* pFm
= getIDocumentMarkAccess()->getFieldmarkAt(aPrevChar
);
249 if (pFm
&& pFm
->GetMarkEnd() == *pCurPos
)
251 mxDoc
->GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY
, nullptr);
252 IDocumentMarkAccess::DeleteFieldmarkCommand(*pFm
);
253 getIDocumentMarkAccess()->deleteMark(pFm
);
254 mxDoc
->GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY
, nullptr);
259 SwCursorShell::Left(1, SwCursorSkipMode::Chars
);
260 if (SvtScriptType::ASIAN
== GetScriptType())
262 sal_uInt32 nCode
= GetChar(false);
263 if ( rtl::isSurrogate( nCode
) )
265 OUString sStr
= GetSelText();
266 nCode
= sStr
.iterateCodePoints( &o3tl::temporary(sal_Int32(0)) );
269 if ( unicode::isIVSSelector( nCode
) )
271 SwCursorShell::Push();
272 SwCursorShell::Left(1, SwCursorSkipMode::Chars
);
273 OUString sStr
= GetSelText();
274 nCode
= sStr
.iterateCodePoints( &o3tl::temporary(sal_Int32(0)) );
275 if ( unicode::isCJKIVSCharacter( nCode
) )
276 SwCursorShell::Pop( SwCursorShell::PopMode::DeleteStack
);
278 SwCursorShell::Pop( SwCursorShell::PopMode::DeleteCurrent
); // For the weak script.
282 bool bRet
= Delete(true);
284 SwCursorShell::SwapPam();
290 bool SwWrtShell::DelRight(bool const isReplaceHeuristic
)
292 // Will be or'ed, if a tableselection exists;
293 // will here be implemented on SelectionType::Table
295 SelectionType nSelection
= GetSelectionType();
296 if(nSelection
& SelectionType::TableCell
)
297 nSelection
= SelectionType::Table
;
298 if(nSelection
& SelectionType::Text
)
299 nSelection
= SelectionType::Text
;
301 switch( nSelection
& ~SelectionType::Ornament
& ~SelectionType::Media
)
303 case SelectionType::PostIt
:
304 case SelectionType::Text
:
305 case SelectionType::Table
:
306 case SelectionType::NumberList
:
307 // If a selection exists, erase it.
310 if( !IsBlockMode() || HasSelection() )
312 //OS: And once again Basic: SwActContext must be
313 //leaved before EnterStdMode !
315 SwActContext
aActContext(this);
317 Delete(isReplaceHeuristic
);
335 if (SwCursorShell::IsEndPara())
337 // Start/EndAllAction to avoid cursor flickering
338 UnoActionContext
c(GetDoc());
340 const SwTableNode
* pWasInTableNd
= IsCursorInTable();
341 // #108049# Save the startnode of the current cell
342 const SwStartNode
* pSNdOld
= pWasInTableNd
?
343 GetCursor()->GetPointNode().FindTableBoxStartNode() : nullptr;
344 bool bCheckDelFull
= SelectionType::Text
& nSelection
&& SwCursorShell::IsSttPara();
345 bool bDelFull
= false;
346 bool bDoNothing
= false;
348 // #i41424# Introduced a couple of
349 // Push()-Pop() pairs here. The reason for this is that a
350 // Right()-Left() combination does not make sure, that
351 // the cursor will be in its initial state, because there
352 // may be a numbering in front of the next paragraph.
353 SwCursorShell::Push();
355 if (SwCursorShell::Right(1, SwCursorSkipMode::Chars
))
357 const SwTableNode
* pCurrTableNd
= IsCursorInTable();
358 bDelFull
= bCheckDelFull
&& pCurrTableNd
&& pCurrTableNd
!= pWasInTableNd
;
359 if (!bDelFull
&& (IsCursorInTable() || (pCurrTableNd
!= pWasInTableNd
)))
361 // #108049# Save the startnode of the current cell.
362 // May be different to pSNdOld as we have moved.
363 const SwStartNode
* pSNdNew
= pCurrTableNd
?
364 GetCursor()->GetPointNode().FindTableBoxStartNode() : nullptr;
366 // tdf#115132 Only keep cursor position instead of deleting
367 // if we have moved to a different cell
368 bDoNothing
= pSNdOld
!= pSNdNew
;
373 SwCursorShell::Pop(SwCursorShell::PopMode::DeleteCurrent
);
380 if (bDelFull
|| bDoNothing
)
385 // If we are just ahead of a fieldmark, then remove it completely
386 sw::mark::IFieldmark
*const pFm
= getIDocumentMarkAccess()->getFieldmarkAt(*GetCursor()->GetPoint());
387 if (pFm
&& pFm
->GetMarkStart() == *GetCursor()->GetPoint())
389 mxDoc
->GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY
, nullptr);
390 IDocumentMarkAccess::DeleteFieldmarkCommand(*pFm
);
391 getIDocumentMarkAccess()->deleteMark(pFm
);
392 mxDoc
->GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY
, nullptr);
399 SwCursorShell::Right(1, SwCursorSkipMode::Cells
);
404 case SelectionType::Frame
:
405 case SelectionType::Graphic
:
406 case SelectionType::Ole
:
407 case SelectionType::DrawObject
:
408 case SelectionType::DrawObjectEditMode
:
409 case SelectionType::DbForm
:
411 // Group deletion of the object and its comment together
412 // (also as-character anchor conversion at track changes)
413 mxDoc
->GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY
, nullptr);
415 // #108205# Remember object's position.
416 Point aTmpPt
= GetObjRect().TopLeft();
418 // Remember the anchor of the selected object before deletion.
419 std::optional
<SwPosition
> oAnchor
;
420 RndStdIds eAnchorId
= RndStdIds::FLY_AT_PARA
;
421 SwFlyFrame
* pFly
= GetSelectedFlyFrame();
422 SwFrameFormat
* pFormat
= nullptr;
425 pFormat
= pFly
->GetFormat();
428 eAnchorId
= pFormat
->GetAnchor().GetAnchorId();
429 // to-character anchor conversion at track changes
430 if ( IsRedlineOn() && (eAnchorId
!= RndStdIds::FLY_AS_CHAR
&&
431 eAnchorId
!= RndStdIds::FLY_AT_CHAR
) )
433 SfxItemSetFixed
<RES_ANCHOR
, RES_ANCHOR
> aSet(GetAttrPool());
434 GetFlyFrameAttr(aSet
);
435 SwFormatAnchor
aAnch(RndStdIds::FLY_AT_CHAR
);
437 SetFlyFrameAttr(aSet
);
438 eAnchorId
= pFormat
->GetAnchor().GetAnchorId();
440 if ((eAnchorId
== RndStdIds::FLY_AS_CHAR
|| eAnchorId
== RndStdIds::FLY_AT_CHAR
)
441 && pFormat
->GetAnchor().GetAnchorNode())
443 oAnchor
.emplace(*pFormat
->GetAnchor().GetContentAnchor());
444 // set cursor before the anchor point
446 *GetCurrentShellCursor().GetPoint() = *oAnchor
;
451 // track changes: create redline at anchor point of the image to record the deletion
452 if ( IsRedlineOn() && oAnchor
&& SelectionType::Graphic
& nSelection
&& pFormat
&&
453 ( eAnchorId
== RndStdIds::FLY_AT_CHAR
|| eAnchorId
== RndStdIds::FLY_AS_CHAR
) )
455 sal_Int32 nRedlineLength
= 1;
456 // create a double CH_TXT_TRACKED_DUMMY_CHAR anchor point of the image to record the
457 // deletion, if needed (otherwise use the existing anchor point of the image anchored
459 if ( eAnchorId
== RndStdIds::FLY_AT_CHAR
)
464 RedlineFlags eOld
= GetRedlineFlags();
465 SetRedlineFlags( eOld
| RedlineFlags::Ignore
);
466 Insert( OUStringChar(CH_TXT_TRACKED_DUMMY_CHAR
) +
467 OUStringChar(CH_TXT_TRACKED_DUMMY_CHAR
) );
468 SwFormatAnchor
anchor(RndStdIds::FLY_AT_CHAR
);
469 SwCursorShell::Left(1, SwCursorSkipMode::Chars
);
470 anchor
.SetAnchor(GetCursor()->GetPoint());
471 GetDoc()->SetAttr(anchor
, *pFormat
);
472 SetRedlineFlags( eOld
);
473 SwCursorShell::Left(1, SwCursorSkipMode::Chars
);
476 SwCursorShell::Right(nRedlineLength
, SwCursorSkipMode::Chars
);
477 bRet
= Delete(false);
485 SwTextNode
* pTextNode
= oAnchor
->GetNode().GetTextNode();
488 const SwTextField
* pField(
489 pTextNode
->GetFieldTextAttrAt(oAnchor
->GetContentIndex(), ::sw::GetTextAttrMode::Default
));
491 && dynamic_cast<const SwPostItField
*>(pField
->GetFormatField().GetField()))
493 // Remove the comment of the deleted object.
494 *GetCurrentShellCursor().GetPoint() = *oAnchor
;
500 mxDoc
->GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY
, nullptr);
502 // #108205# Set cursor to remembered position.
507 OSL_ENSURE( !IsFrameSelected(),
508 "<SwWrtShell::DelRight(..)> - <SwWrtShell::UnSelectFrame()> should unmark all objects" );
509 // leave draw mode, if necessary.
511 if (GetView().GetDrawFuncPtr())
513 GetView().GetDrawFuncPtr()->Deactivate();
514 GetView().SetDrawFuncPtr(nullptr);
516 if ( GetView().IsDrawMode() )
518 GetView().LeaveDrawCreate();
523 // <IsFrameSelected()> can't be true - see above.
525 nSelection
= GetSelectionType();
526 if ( SelectionType::Frame
& nSelection
||
527 SelectionType::Graphic
& nSelection
||
528 SelectionType::Ole
& nSelection
||
529 SelectionType::DrawObject
& nSelection
)
542 void SwWrtShell::DelToEndOfPara()
544 SwActContext
aActContext(this);
548 if( !MovePara(GoCurrPara
,fnParaEnd
))
550 Pop(SwCursorShell::PopMode::DeleteCurrent
);
553 bool bRet
= Delete(false);
554 Pop(SwCursorShell::PopMode::DeleteCurrent
);
559 void SwWrtShell::DelToStartOfPara()
561 SwActContext
aActContext(this);
565 if( !MovePara(GoCurrPara
,fnParaStart
))
567 Pop(SwCursorShell::PopMode::DeleteCurrent
);
570 bool bRet
= Delete(false);
571 Pop(SwCursorShell::PopMode::DeleteCurrent
);
576 // All erase operations should work with Find instead with
577 // Nxt-/PrvDelim, because the latter works with Wrap Around
578 // -- that's probably not wished.
580 void SwWrtShell::DelToStartOfSentence()
585 bool bRet
= BwdSentence_() && Delete(false);
589 bool SwWrtShell::DelToEndOfSentence()
595 // fdo#60967: special case that is documented in help: delete
596 // paragraph following table if cursor is at end of last cell in table
601 if (SwCursorShell::Right(1,SwCursorSkipMode::Chars
))
604 if (!IsEndPara()) // can only be at the end if it's empty
605 { // for an empty paragraph this would actually select the _next_
606 SwCursorShell::MovePara(GoCurrPara
, fnParaEnd
);
608 if (!IsEndOfDoc()) // do not delete last paragraph in body text
610 bRet
= DelFullPara();
613 Pop(SwCursorShell::PopMode::DeleteCurrent
);
617 bRet
= FwdSentence_() && Delete(false);
623 void SwWrtShell::DelNxtWord()
627 SwActContext
aActContext(this);
631 if(IsEndWrd() && !IsStartWord())
632 NxtWrdForDelete(); // #i92468#
633 if(IsStartWord() || IsEndPara())
634 NxtWrdForDelete(); // #i92468#
638 bool bRet
= Delete(false);
646 void SwWrtShell::DelPrvWord()
650 SwActContext
aActContext(this);
654 if ( !IsStartWord() ||
655 !PrvWrdForDelete() ) // #i92468#
657 if (IsEndWrd() || IsSttPara())
658 PrvWrdForDelete(); // #i92468#
662 bool bRet
= Delete(false);
670 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */