android: Update app-specific/MIME type icons
[LibreOffice.git] / sw / source / uibase / wrtsh / delete.cxx
blobc4c89df82a67987a0a7eea4001dbc19d6b09cc60
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
21 #include <wrtsh.hxx>
22 #include <swcrsr.hxx>
23 #include <editeng/lrspitem.hxx>
24 #include <view.hxx>
25 #include <drawbase.hxx>
26 #include <unobaseclass.hxx>
27 #include <fmtanchr.hxx>
28 #include <flyfrm.hxx>
29 #include <ndtxt.hxx>
30 #include <txtfld.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>
37 #include <doc.hxx>
38 #include <IDocumentRedlineAccess.hxx>
40 inline void SwWrtShell::OpenMark()
42 StartAllAction();
43 ResetCursorStack();
44 KillPams();
45 SetMark();
48 inline void SwWrtShell::CloseMark( bool bOkFlag )
50 if( bOkFlag )
51 UpdateAttr();
52 else
53 SwapPam();
55 ClearMark();
56 EndAllAction();
61 // #i23725#
62 bool SwWrtShell::TryRemoveIndent()
64 bool bResult = false;
66 SfxItemSetFixed<RES_MARGIN_FIRSTLINE, RES_MARGIN_FIRSTLINE> aAttrSet(GetAttrPool());
67 GetCurAttr(aAttrSet);
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);
76 bResult = true;
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);
83 bResult = true;
85 else if (leftMargin.GetTextLeft() != 0)
87 leftMargin.SetTextLeft(0);
88 bResult = true;
91 if (bResult)
93 aAttrSet.Put(firstLine);
94 aAttrSet.Put(leftMargin);
95 SetAttrSet(aAttrSet);
98 return bResult;
101 /** Description: Erase the line. */
103 void SwWrtShell::DelLine()
105 SwActContext aActContext(this);
106 ResetCursorStack();
107 // remember the old cursor
108 Push();
109 ClearMark();
110 SwCursorShell::LeftMargin();
111 SetMark();
112 SwCursorShell::RightMargin();
114 bool bRet = Delete(false);
115 Pop(SwCursorShell::PopMode::DeleteCurrent);
116 if( bRet )
117 UpdateAttr();
120 void SwWrtShell::DelToStartOfLine()
122 OpenMark();
123 SwCursorShell::LeftMargin();
124 bool bRet = Delete(false);
125 CloseMark( bRet );
128 void SwWrtShell::DelToEndOfLine()
130 OpenMark();
131 SwCursorShell::RightMargin();
132 bool bRet = Delete(false);
133 CloseMark( bRet );
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();
146 DelSelectedObj();
148 // #108205# Set cursor to remembered position.
149 SetCursor(&aTmpPt);
151 LeaveSelFrameMode();
152 UnSelectFrame();
154 nSelType = GetSelectionType();
155 if ( nCmp & nSelType )
157 EnterSelFrameMode();
158 GotoNextFly();
161 return true;
164 // If a selection exists, erase this
165 if ( IsSelection() )
167 if( !IsBlockMode() || HasSelection() )
169 //OS: Once again Basic: SwActContext must be leaved
170 //before EnterStdMode!
172 SwActContext aActContext(this);
173 ResetCursorStack();
174 Delete(false);
175 UpdateAttr();
177 if( IsBlockMode() )
179 NormalizePam();
180 ClearMark();
181 EnterBlockMode();
183 else
184 EnterStdMode();
185 return true;
187 else
188 EnterStdMode();
191 // JP 29.06.95: never erase a table standing in front of it.
192 bool bSwap = false;
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() :
205 nullptr;
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);
211 if (bDoSomething)
213 // If the cursor entered or left a table (or both) we are done.
214 const SwTableNode* pIsInTableNd = SwCursorShell::IsCursorInTable();
215 bDoSomething = pIsInTableNd == pWasInTableNd;
217 if (bDoSomething)
219 const SwStartNode* pSNdNew = pIsInTableNd ?
220 GetCursor()->GetPointNode().FindTableBoxStartNode() :
221 nullptr;
223 // #i4032# Don't actually call a 'delete' if we
224 // changed the table cell, compare DelRight().
225 bDoSomething = pSNdOld == pSNdNew;
229 if (!bDoSomething)
231 // tdf#115132 Restore previous position and we are done
232 SwCursorShell::Pop(SwCursorShell::PopMode::DeleteCurrent);
233 return false;
236 SwCursorShell::Pop(SwCursorShell::PopMode::DeleteStack);
238 OpenMark();
239 SwCursorShell::Right(1,SwCursorSkipMode::Chars);
240 SwCursorShell::SwapPam();
241 bSwap = true;
243 else
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);
255 return true;
258 OpenMark();
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 );
277 else
278 SwCursorShell::Pop( SwCursorShell::PopMode::DeleteCurrent ); // For the weak script.
282 bool bRet = Delete(true);
283 if( !bRet && bSwap )
284 SwCursorShell::SwapPam();
285 CloseMark( bRet );
287 return bRet;
290 bool SwWrtShell::DelRight(bool const isReplaceHeuristic)
292 // Will be or'ed, if a tableselection exists;
293 // will here be implemented on SelectionType::Table
294 bool bRet = false;
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.
308 if( IsSelection() )
310 if( !IsBlockMode() || HasSelection() )
312 //OS: And once again Basic: SwActContext must be
313 //leaved before EnterStdMode !
315 SwActContext aActContext(this);
316 ResetCursorStack();
317 Delete(isReplaceHeuristic);
318 UpdateAttr();
320 if( IsBlockMode() )
322 NormalizePam();
323 ClearMark();
324 EnterBlockMode();
326 else
327 EnterStdMode();
328 bRet = true;
329 break;
331 else
332 EnterStdMode();
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;
372 // restore cursor
373 SwCursorShell::Pop(SwCursorShell::PopMode::DeleteCurrent);
375 if (bDelFull)
377 DelFullPara();
378 UpdateAttr();
380 if (bDelFull || bDoNothing)
381 break;
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);
393 bRet = true;
394 break;
398 OpenMark();
399 SwCursorShell::Right(1, SwCursorSkipMode::Cells);
400 bRet = Delete(true);
401 CloseMark( bRet );
402 break;
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;
423 if (pFly)
425 pFormat = pFly->GetFormat();
426 if (pFormat)
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);
436 aSet.Put(aAnch);
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
445 if ( IsRedlineOn() )
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
458 // *as* character)
459 if ( eAnchorId == RndStdIds::FLY_AT_CHAR )
461 nRedlineLength = 2;
462 LeaveSelFrameMode();
463 UnSelectFrame();
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);
475 OpenMark();
476 SwCursorShell::Right(nRedlineLength, SwCursorSkipMode::Chars);
477 bRet = Delete(false);
478 CloseMark( bRet );
480 else
481 DelSelectedObj();
483 if (oAnchor)
485 SwTextNode* pTextNode = oAnchor->GetNode().GetTextNode();
486 if (pTextNode)
488 const SwTextField* pField(
489 pTextNode->GetFieldTextAttrAt(oAnchor->GetContentIndex(), ::sw::GetTextAttrMode::Default));
490 if (pField
491 && dynamic_cast<const SwPostItField*>(pField->GetFormatField().GetField()))
493 // Remove the comment of the deleted object.
494 *GetCurrentShellCursor().GetPoint() = *oAnchor;
495 DelRight();
500 mxDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr);
502 // #108205# Set cursor to remembered position.
503 SetCursor(&aTmpPt);
505 LeaveSelFrameMode();
506 UnSelectFrame();
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 )
531 EnterSelFrameMode();
532 GotoNextFly();
535 bRet = true;
536 break;
537 default: break;
539 return bRet;
542 void SwWrtShell::DelToEndOfPara()
544 SwActContext aActContext(this);
545 ResetCursorStack();
546 Push();
547 SetMark();
548 if( !MovePara(GoCurrPara,fnParaEnd))
550 Pop(SwCursorShell::PopMode::DeleteCurrent);
551 return;
553 bool bRet = Delete(false);
554 Pop(SwCursorShell::PopMode::DeleteCurrent);
555 if( bRet )
556 UpdateAttr();
559 void SwWrtShell::DelToStartOfPara()
561 SwActContext aActContext(this);
562 ResetCursorStack();
563 Push();
564 SetMark();
565 if( !MovePara(GoCurrPara,fnParaStart))
567 Pop(SwCursorShell::PopMode::DeleteCurrent);
568 return;
570 bool bRet = Delete(false);
571 Pop(SwCursorShell::PopMode::DeleteCurrent);
572 if( bRet )
573 UpdateAttr();
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()
582 if(IsStartOfDoc())
583 return;
584 OpenMark();
585 bool bRet = BwdSentence_() && Delete(false);
586 CloseMark( bRet );
589 bool SwWrtShell::DelToEndOfSentence()
591 if(IsEndOfDoc())
592 return false;
593 OpenMark();
594 bool bRet(false);
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
597 if (IsEndOfTable())
599 Push();
600 ClearMark();
601 if (SwCursorShell::Right(1,SwCursorSkipMode::Chars))
603 SetMark();
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);
615 else
617 bRet = FwdSentence_() && Delete(false);
619 CloseMark( bRet );
620 return bRet;
623 void SwWrtShell::DelNxtWord()
625 if(IsEndOfDoc())
626 return;
627 SwActContext aActContext(this);
628 ResetCursorStack();
629 EnterStdMode();
630 SetMark();
631 if(IsEndWrd() && !IsStartWord())
632 NxtWrdForDelete(); // #i92468#
633 if(IsStartWord() || IsEndPara())
634 NxtWrdForDelete(); // #i92468#
635 else
636 EndWrd();
638 bool bRet = Delete(false);
639 if( bRet )
640 UpdateAttr();
641 else
642 SwapPam();
643 ClearMark();
646 void SwWrtShell::DelPrvWord()
648 if(IsStartOfDoc())
649 return;
650 SwActContext aActContext(this);
651 ResetCursorStack();
652 EnterStdMode();
653 SetMark();
654 if ( !IsStartWord() ||
655 !PrvWrdForDelete() ) // #i92468#
657 if (IsEndWrd() || IsSttPara())
658 PrvWrdForDelete(); // #i92468#
659 else
660 SttWrd();
662 bool bRet = Delete(false);
663 if( bRet )
664 UpdateAttr();
665 else
666 SwapPam();
667 ClearMark();
670 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */