[Libreoffice-commits] core.git: sw/inc sw/qa sw/source

Michael Stahl (via logerrit) logerrit at kemper.freedesktop.org
Fri Dec 6 12:28:06 UTC 2019


 sw/inc/undobj.hxx                                                             |    6 
 sw/qa/core/uwriter.cxx                                                        |    3 
 sw/qa/extras/uiwriter/data/redline_fly_at_para_one_paragraph.odt              |binary
 sw/qa/extras/uiwriter/data/redline_fly_duplication_at_para_2nd_paragraph.fodt |  222 ++++++++++
 sw/qa/extras/uiwriter/uiwriter.cxx                                            |   16 
 sw/qa/uitest/writer_tests4/tdf108124.py                                       |   15 
 sw/source/core/access/accframebase.cxx                                        |    3 
 sw/source/core/access/accmap.cxx                                              |   18 
 sw/source/core/doc/DocumentContentOperationsManager.cxx                       |   45 --
 sw/source/core/doc/docedt.cxx                                                 |   57 --
 sw/source/core/edit/edglbldc.cxx                                              |    2 
 sw/source/core/layout/trvlfrm.cxx                                             |   17 
 sw/source/core/undo/undobj.cxx                                                |   96 +++-
 sw/source/core/undo/untblk.cxx                                                |   17 
 14 files changed, 374 insertions(+), 143 deletions(-)

New commits:
commit 91b2325808a75174f284c48c8b8afc118fad74e4
Author:     Michael Stahl <Michael.Stahl at cib.de>
AuthorDate: Thu Dec 5 16:49:33 2019 +0100
Commit:     Michael Stahl <michael.stahl at cib.de>
CommitDate: Fri Dec 6 13:26:32 2019 +0100

    tdf#121300 sw: consistent fly at-pargraph selection
    
    As a follow-up to commit 28b77c89dfcafae82cf2a6d85731b643ff9290e5, add
    IsSelectFrameAnchoredAtPara() function and use it to harmonize at-para
    fly selection across all relevant operations:
    
    * CopyImpl:
      - it had a pre-existing bugs that would lose a fly anchored to the
        2nd (1st fully selected) node of a redline
      - remove a bunch of code for finding the last node of the body content,
        which doesn't matter for the remaining at-fly checks
      - flys that already existed at the insert position need to have their
        anchors corrected
    
    * DeleteRange:
      - get rid of the bDelFwrd checks
    
    * MoveRange:
      - the ALLFLYS flag would be obsoleted by the new selection, were it
        not for the writerfiler "special hack", see below
    
    * also adapt A11y and UI selection, SwRootFrame::CalcFrameRects()
    
    The selection behavior is changed:
    
    * the bDelFwrd case is quite odd, some code was checking whether a
      deletion was "forward" or "backward" and removing only the flys at the
      point node while retaining the flys at the mark node; this worked in a
      very non-obvious way relying on sw_GetJoinFlags actually calling
      Exchange() on the cursor, and then SwUndoDelete stored it in
      m_bJoinNext, but it turns out that only SwUndoMove also has a
      m_bJoinNext and it's dead, and no other Undo has such a flag, so this
      only worked for "delete". It's not obvious what the value of this is,
      let's just ignore the "direction".
    
    * Selections exclude the start and end position except if it's a fully
      selected node or at the start or end of a section (i.e. Ctrl+A should
      also select every at-para fly).
    
    * An exception is made in case the selection looks like it's a
      backspace/delete joining 2 paragraphs; flys are not deleted in that
      case because it seemed annoying (and as it happens, Word does not
      appear to delete flys in that case), particularly if both of the nodes
      are already empty. This is done with a heuristic, it's conceivable to
      pass down some flag from DelLeft()/DelRight() but probably this is
      good enough.
    
    A special hack is needed to keep writerfilter happy for now; it likes to
    anchor flys at nodes which it then deletes in RemoveLastParagraph(),
    which likely could be improved there.  The ALLFLYS usage in
    SwRangeRedline::MoveFromSection() could be removed (because the
    end-of-section check already handles the case) except for this, because
    it turns out that the ODF import runs SetRedlineFlags with a temporarily
    reset IsInXMLImport() flag because of its effect in thints.cxx, so
    during the move IsSelectFrameAnchoredAtPara() can't check it.
    
    tdf#108124 scenario works better, now everything that's selected is both
    copied and deleted.
    
    Fixes the problem where an at-para fly at the 2nd node of a redline
    where the 1st node is partially deleted was lost on ODF export.
    
    Change-Id: I168013665e70ff0a5f198e09f3fa3afc06ba0449
    Reviewed-on: https://gerrit.libreoffice.org/84576
    Tested-by: Jenkins
    Reviewed-by: Michael Stahl <michael.stahl at cib.de>

diff --git a/sw/inc/undobj.hxx b/sw/inc/undobj.hxx
index dc271d0cf94d..cde1e3a0ff35 100644
--- a/sw/inc/undobj.hxx
+++ b/sw/inc/undobj.hxx
@@ -135,7 +135,7 @@ enum class DelContentType : sal_uInt16
     Fly          = 0x02,
     Bkm          = 0x08,
     AllMask      = 0x0b,
-    ExcludeAtCharFlyAtStartEnd = 0x40,
+    ExcludeFlyAtStartEnd = 0x40,
     CheckNoCntnt = 0x80,
 };
 namespace o3tl {
@@ -146,6 +146,10 @@ namespace o3tl {
 bool IsDestroyFrameAnchoredAtChar(SwPosition const & rAnchorPos,
         SwPosition const & rStart, SwPosition const & rEnd,
         DelContentType const nDelContentType = DelContentType::AllMask);
+/// is a fly anchored at paragraph at rAnchorPos selected?
+bool IsSelectFrameAnchoredAtPara(SwPosition const & rAnchorPos,
+        SwPosition const & rStart, SwPosition const & rEnd,
+        DelContentType const nDelContentType = DelContentType::AllMask);
 
 // This class has to be inherited into an Undo-object if it saves content
 // for Redo/Undo...
diff --git a/sw/qa/core/uwriter.cxx b/sw/qa/core/uwriter.cxx
index 575baa3c32ee..22cd289c7f27 100644
--- a/sw/qa/core/uwriter.cxx
+++ b/sw/qa/core/uwriter.cxx
@@ -1165,8 +1165,7 @@ void SwDocTest::randomTest()
                 SwMoveFlags nFlags =
                          getRand(1) // FIXME: puterb this more ?
                          ? SwMoveFlags::DEFAULT
-                         : SwMoveFlags::ALLFLYS |
-                           SwMoveFlags::CREATEUNDOOBJ |
+                         : SwMoveFlags::CREATEUNDOOBJ |
                            SwMoveFlags::REDLINES |
                            SwMoveFlags::NO_DELFRMS;
                 SwPosition aTo(getRandomPosition(m_pDoc, i/10));
diff --git a/sw/qa/extras/uiwriter/data/redline_fly_at_para_one_paragraph.odt b/sw/qa/extras/uiwriter/data/redline_fly_at_para_one_paragraph.odt
new file mode 100644
index 000000000000..a4b41e4ec25d
Binary files /dev/null and b/sw/qa/extras/uiwriter/data/redline_fly_at_para_one_paragraph.odt differ
diff --git a/sw/qa/extras/uiwriter/data/redline_fly_duplication_at_para_2nd_paragraph.fodt b/sw/qa/extras/uiwriter/data/redline_fly_duplication_at_para_2nd_paragraph.fodt
new file mode 100644
index 000000000000..230159513ba0
--- /dev/null
+++ b/sw/qa/extras/uiwriter/data/redline_fly_duplication_at_para_2nd_paragraph.fodt
@@ -0,0 +1,222 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:config="urn:oas
 is:names:tc:opendocument:xmlns:config:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rpt="http://openoffice.org/2005/report" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:officeooo="http://openoffice.org/2009/office" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:formx="urn:openoffice:names:
 experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:css3t="http://www.w3.org/TR/css3-text/" office:version="1.2" office:mimetype="application/vnd.oasis.opendocument.text">
+ <office:font-face-decls>
+  <style:font-face style:name="Wingdings" svg:font-family="Wingdings" style:font-pitch="variable" style:font-charset="x-symbol"/>
+  <style:font-face style:name="Symbol" svg:font-family="Symbol" style:font-family-generic="roman" style:font-pitch="variable" style:font-charset="x-symbol"/>
+  <style:font-face style:name="Arial" svg:font-family="Arial"/>
+  <style:font-face style:name="Scala-Regular" svg:font-family="Scala-Regular"/>
+  <style:font-face style:name="ScalaPro-Regular" svg:font-family="ScalaPro-Regular"/>
+  <style:font-face style:name="StarSymbol" svg:font-family="StarSymbol"/>
+  <style:font-face style:name="Courier New" svg:font-family="'Courier New'" style:font-family-generic="modern"/>
+  <style:font-face style:name="ScalaPro-Bold" svg:font-family="ScalaPro-Bold" style:font-family-generic="roman"/>
+  <style:font-face style:name="Arial4" svg:font-family="Arial, Arial" style:font-family-generic="swiss"/>
+  <style:font-face style:name="Arial1" svg:font-family="Arial" style:font-family-generic="swiss" style:font-pitch="variable"/>
+  <style:font-face style:name="Arial2" svg:font-family="Arial" style:font-adornments="Fett" style:font-family-generic="swiss" style:font-pitch="variable"/>
+  <style:font-face style:name="Arial3" svg:font-family="Arial" style:font-adornments="Standard" style:font-family-generic="swiss" style:font-pitch="variable"/>
+  <style:font-face style:name="Arial Unicode MS" svg:font-family="'Arial Unicode MS'" style:font-family-generic="swiss" style:font-pitch="variable"/>
+  <style:font-face style:name="DejaVu Sans" svg:font-family="'DejaVu Sans'" style:font-family-generic="system" style:font-pitch="variable"/>
+ </office:font-face-decls>
+ <office:styles>
+  <style:default-style style:family="graphic">
+   <style:graphic-properties svg:stroke-color="#000000" draw:fill-color="#99ccff" fo:wrap-option="no-wrap" draw:shadow-offset-x="0.3cm" draw:shadow-offset-y="0.3cm" draw:start-line-spacing-horizontal="0.283cm" draw:start-line-spacing-vertical="0.283cm" draw:end-line-spacing-horizontal="0.283cm" draw:end-line-spacing-vertical="0.283cm" style:flow-with-text="false"/>
+   <style:paragraph-properties style:text-autospace="ideograph-alpha" style:line-break="strict" style:font-independent-line-spacing="false">
+    <style:tab-stops/>
+   </style:paragraph-properties>
+   <style:text-properties style:use-window-font-color="true" style:font-name="Arial" fo:font-size="11pt" fo:language="de" fo:country="DE" style:letter-kerning="true" style:font-name-asian="DejaVu Sans" style:font-size-asian="12pt" style:language-asian="zh" style:country-asian="CN" style:font-name-complex="DejaVu Sans" style:font-size-complex="12pt" style:language-complex="hi" style:country-complex="IN"/>
+  </style:default-style>
+  <style:default-style style:family="paragraph">
+   <style:paragraph-properties fo:hyphenation-ladder-count="no-limit" style:text-autospace="ideograph-alpha" style:punctuation-wrap="hanging" style:line-break="strict" style:tab-stop-distance="1.251cm" style:writing-mode="page"/>
+   <style:text-properties style:use-window-font-color="true" style:font-name="Arial" fo:font-size="11pt" fo:language="de" fo:country="DE" style:letter-kerning="true" style:font-name-asian="DejaVu Sans" style:font-size-asian="12pt" style:language-asian="zh" style:country-asian="CN" style:font-name-complex="DejaVu Sans" style:font-size-complex="12pt" style:language-complex="hi" style:country-complex="IN" fo:hyphenate="false" fo:hyphenation-remain-char-count="2" fo:hyphenation-push-char-count="2"/>
+  </style:default-style>
+  <style:default-style style:family="table">
+   <style:table-properties table:border-model="collapsing"/>
+  </style:default-style>
+  <style:default-style style:family="table-row">
+   <style:table-row-properties fo:keep-together="auto"/>
+  </style:default-style>
+  <style:style style:name="Standard" style:family="paragraph" style:class="text"/>
+  <style:style style:name="Text_20_body" style:display-name="Text body" style:family="paragraph" style:parent-style-name="Standard" style:class="text">
+   <style:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0.212cm" loext:contextual-spacing="false"/>
+  </style:style>
+  <style:style style:name="RGUgliederung" style:family="paragraph" style:parent-style-name="Standard" style:list-style-name="WW8Num6">
+   <style:paragraph-properties fo:margin-left="0cm" fo:margin-right="0cm" fo:margin-top="0cm" fo:margin-bottom="0.529cm" loext:contextual-spacing="false" style:line-height-at-least="0.529cm" fo:text-indent="0cm" style:auto-text-indent="false" style:text-autospace="none" style:punctuation-wrap="simple" style:vertical-align="baseline">
+    <style:tab-stops>
+     <style:tab-stop style:position="0.751cm"/>
+     <style:tab-stop style:position="16.277cm"/>
+    </style:tab-stops>
+   </style:paragraph-properties>
+   <style:text-properties style:font-size-complex="10pt"/>
+  </style:style>
+  <style:style style:name="Bullet_20_Symbols" style:display-name="Bullet Symbols" style:family="text">
+   <style:text-properties style:font-name="StarSymbol" fo:font-family="StarSymbol" fo:font-size="9pt" style:font-name-asian="StarSymbol" style:font-family-asian="StarSymbol" style:font-size-asian="9pt" style:font-name-complex="StarSymbol" style:font-family-complex="StarSymbol" style:font-size-complex="9pt"/>
+  </style:style>
+  <style:style style:name="WW8Num6z1" style:family="text">
+   <style:text-properties style:font-name="Courier New" fo:font-family="'Courier New'" style:font-family-generic="modern"/>
+  </style:style>
+  <style:style style:name="WW8Num6z0" style:family="text">
+   <style:text-properties style:font-name="Wingdings" fo:font-family="Wingdings" style:font-pitch="variable" style:font-charset="x-symbol"/>
+  </style:style>
+  <style:style style:name="WW8Num6z3" style:family="text">
+   <style:text-properties style:font-name="Symbol" fo:font-family="Symbol" style:font-family-generic="roman" style:font-pitch="variable" style:font-charset="x-symbol"/>
+  </style:style>
+  <style:style style:name="Graphics" style:family="graphic">
+   <style:graphic-properties text:anchor-type="paragraph" svg:x="0cm" svg:y="0cm" style:wrap="none" style:vertical-pos="top" style:vertical-rel="paragraph" style:horizontal-pos="center" style:horizontal-rel="paragraph"/>
+  </style:style>
+  <text:list-style style:name="WW8Num6" text:consecutive-numbering="true">
+   <text:list-level-style-bullet text:level="1" text:style-name="Bullet_20_Symbols" text:bullet-char="•">
+    <style:list-level-properties text:space-before="0.635cm" text:min-label-width="0.635cm"/>
+    <style:text-properties fo:font-family="StarSymbol" style:font-charset="x-symbol"/>
+   </text:list-level-style-bullet>
+   <text:list-level-style-bullet text:level="2" text:style-name="WW8Num6z1" text:bullet-char="o">
+    <style:list-level-properties text:space-before="1.905cm" text:min-label-width="0.635cm"/>
+    <style:text-properties fo:font-family="'Courier New'" style:font-family-generic="modern" style:font-pitch="fixed"/>
+   </text:list-level-style-bullet>
+   <text:list-level-style-bullet text:level="3" text:style-name="WW8Num6z0" text:bullet-char="">
+    <style:list-level-properties text:space-before="3.175cm" text:min-label-width="0.635cm"/>
+    <style:text-properties style:font-name="Wingdings"/>
+   </text:list-level-style-bullet>
+   <text:list-level-style-bullet text:level="4" text:style-name="WW8Num6z3" text:bullet-char="ï‚·">
+    <style:list-level-properties text:space-before="4.445cm" text:min-label-width="0.635cm"/>
+    <style:text-properties style:font-name="Symbol"/>
+   </text:list-level-style-bullet>
+   <text:list-level-style-bullet text:level="5" text:style-name="WW8Num6z1" text:bullet-char="o">
+    <style:list-level-properties text:space-before="5.715cm" text:min-label-width="0.635cm"/>
+    <style:text-properties fo:font-family="'Courier New'" style:font-family-generic="modern" style:font-pitch="fixed"/>
+   </text:list-level-style-bullet>
+   <text:list-level-style-bullet text:level="6" text:style-name="WW8Num6z0" text:bullet-char="">
+    <style:list-level-properties text:space-before="6.985cm" text:min-label-width="0.635cm"/>
+    <style:text-properties style:font-name="Wingdings"/>
+   </text:list-level-style-bullet>
+   <text:list-level-style-bullet text:level="7" text:style-name="WW8Num6z3" text:bullet-char="ï‚·">
+    <style:list-level-properties text:space-before="8.255cm" text:min-label-width="0.635cm"/>
+    <style:text-properties style:font-name="Symbol"/>
+   </text:list-level-style-bullet>
+   <text:list-level-style-bullet text:level="8" text:style-name="WW8Num6z1" text:bullet-char="o">
+    <style:list-level-properties text:space-before="9.525cm" text:min-label-width="0.635cm"/>
+    <style:text-properties fo:font-family="'Courier New'" style:font-family-generic="modern" style:font-pitch="fixed"/>
+   </text:list-level-style-bullet>
+   <text:list-level-style-bullet text:level="9" text:style-name="WW8Num6z0" text:bullet-char="">
+    <style:list-level-properties text:space-before="10.795cm" text:min-label-width="0.635cm"/>
+    <style:text-properties style:font-name="Wingdings"/>
+   </text:list-level-style-bullet>
+   <text:list-level-style-number text:level="10" style:num-suffix="." style:num-format="1">
+    <style:list-level-properties text:space-before="6.35cm" text:min-label-width="0.635cm"/>
+   </text:list-level-style-number>
+  </text:list-style>
+ </office:styles>
+ <office:automatic-styles>
+  <style:style style:name="P1" style:family="paragraph" style:parent-style-name="RGUgliederung" style:list-style-name="WW8Num6">
+   <style:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0.229cm" loext:contextual-spacing="false"/>
+   <style:text-properties fo:color="#000000"/>
+  </style:style>
+  <style:style style:name="P2" style:family="paragraph" style:parent-style-name="RGUgliederung" style:list-style-name="">
+   <style:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0.229cm" loext:contextual-spacing="false"/>
+   <style:text-properties fo:color="#000000"/>
+  </style:style>
+  <style:style style:name="P3" style:family="paragraph" style:parent-style-name="RGUgliederung" style:list-style-name="">
+   <style:paragraph-properties fo:margin-left="0cm" fo:margin-right="0cm" fo:text-indent="0cm" style:auto-text-indent="false"/>
+   <style:text-properties fo:color="#000000" style:font-name="Arial1" fo:font-size="11pt" fo:font-weight="normal" officeooo:rsid="0036e634" officeooo:paragraph-rsid="002d5bb4" style:font-name-asian="Scala-Regular" style:font-size-asian="11pt" style:font-weight-asian="normal" style:font-name-complex="Scala-Regular" style:font-size-complex="11pt" style:font-weight-complex="normal"/>
+  </style:style>
+  <style:style style:name="P5" style:family="paragraph" style:parent-style-name="RGUgliederung" style:list-style-name="">
+   <style:paragraph-properties fo:margin-left="0cm" fo:margin-right="0cm" fo:text-indent="0cm" style:auto-text-indent="false"/>
+   <style:text-properties fo:color="#000000" style:font-name="Arial2" fo:font-size="11pt" fo:font-weight="normal" officeooo:rsid="0036e634" officeooo:paragraph-rsid="002d5bb4" style:font-name-asian="Scala-Regular" style:font-size-asian="11pt" style:font-weight-asian="normal" style:font-name-complex="Scala-Regular" style:font-size-complex="11pt" style:font-weight-complex="normal"/>
+  </style:style>
+  <style:style style:name="P6" style:family="paragraph" style:parent-style-name="RGUgliederung" style:list-style-name="">
+   <style:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0.229cm" loext:contextual-spacing="false"/>
+   <style:text-properties fo:color="#000000"/>
+  </style:style>
+  <style:style style:name="T1" style:family="text">
+   <style:text-properties fo:color="#231f20" style:font-name="Arial3" fo:font-size="10pt" fo:font-style="normal" fo:font-weight="normal" style:font-name-asian="ScalaPro-Regular" style:font-size-asian="10pt" style:font-name-complex="ScalaPro-Regular" style:font-size-complex="10pt"/>
+  </style:style>
+  <style:style style:name="T2" style:family="text">
+   <style:text-properties fo:color="#231f20" style:font-name="Arial3" fo:font-size="10pt" fo:font-style="normal" fo:font-weight="normal" style:font-name-asian="ScalaPro-Bold" style:font-size-asian="10pt" style:font-name-complex="ScalaPro-Bold" style:font-size-complex="10pt"/>
+  </style:style>
+  <style:style style:name="T3" style:family="text">
+   <style:text-properties style:font-name="Arial1" fo:font-size="11pt" style:text-underline-style="none" fo:font-weight="bold" style:font-name-asian="ScalaPro-Regular" style:font-size-asian="11pt" style:font-weight-asian="bold" style:font-name-complex="ScalaPro-Regular" style:font-size-complex="11pt" style:font-weight-complex="bold"/>
+  </style:style>
+  <style:style style:name="T4" style:family="text">
+   <style:text-properties style:font-name="Arial1" fo:font-size="11pt" style:text-underline-style="none" fo:font-weight="bold" style:font-name-asian="Arial4" style:font-size-asian="11pt" style:font-weight-asian="bold" style:font-name-complex="Arial1" style:font-size-complex="11pt" style:font-weight-complex="bold"/>
+  </style:style>
+  <style:style style:name="T5" style:family="text">
+   <style:text-properties style:font-name="Arial2" style:font-name-asian="ScalaPro-Regular" style:font-name-complex="ScalaPro-Regular"/>
+  </style:style>
+  <style:style style:name="fr1" style:family="graphic" style:parent-style-name="Graphics">
+   <style:graphic-properties style:vertical-pos="top" style:vertical-rel="paragraph" style:horizontal-pos="center" style:horizontal-rel="paragraph" style:mirror="none" fo:clip="rect(0cm, 0cm, 0cm, 0cm)" draw:luminance="0%" draw:contrast="0%" draw:red="0%" draw:green="0%" draw:blue="0%" draw:gamma="100%" draw:color-inversion="false" draw:image-opacity="100%" draw:color-mode="standard"/>
+  </style:style>
+  <style:page-layout style:name="pm1">
+   <style:page-layout-properties fo:page-width="21.001cm" fo:page-height="29.7cm" style:num-format="1" style:print-orientation="portrait" fo:margin-top="2cm" fo:margin-bottom="2cm" fo:margin-left="2cm" fo:margin-right="2cm" style:writing-mode="lr-tb" style:layout-grid-color="#c0c0c0" style:layout-grid-lines="20" style:layout-grid-base-height="0.706cm" style:layout-grid-ruby-height="0.353cm" style:layout-grid-mode="none" style:layout-grid-ruby-below="false" style:layout-grid-print="false" style:layout-grid-display="false" style:footnote-max-height="0cm">
+    <style:footnote-sep style:width="0.018cm" style:distance-before-sep="0.101cm" style:distance-after-sep="0.101cm" style:line-style="none" style:adjustment="left" style:rel-width="25%" style:color="#000000"/>
+   </style:page-layout-properties>
+   <style:header-style/>
+   <style:footer-style/>
+  </style:page-layout>
+ </office:automatic-styles>
+ <office:master-styles>
+  <style:master-page style:name="Standard" style:page-layout-name="pm1"/>
+ </office:master-styles>
+ <office:body>
+  <office:text>
+   <text:tracked-changes>
+     <text:changed-region xml:id="ct121341168" text:id="ct121341168">
+       <text:deletion>
+         <office:change-info>
+           <dc:creator>Unknown Author</dc:creator>
+           <dc:date>2019-12-04T18:05:04</dc:date>
+         </office:change-info>
+         <text:p text:style-name="P1">x</text:p>
+         <text:p text:style-name="P1"><draw:frame draw:style-name="fr1" draw:name="Image1" text:anchor-type="paragraph" svg:width="0.67cm" svg:height="0.67cm" draw:z-index="0"><draw:image loext:mime-type="image/png">
+         <office:binary-data>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
+          SUVORK5CYII=
+         </office:binary-data>
+        </draw:image></draw:frame>x</text:p>
+        <text:p text:style-name="P1"/>
+       </text:deletion>
+     </text:changed-region>
+   </text:tracked-changes>
+   <text:sequence-decls>
+    <text:sequence-decl text:display-outline-level="0" text:name="Illustration"/>
+    <text:sequence-decl text:display-outline-level="0" text:name="Table"/>
+    <text:sequence-decl text:display-outline-level="0" text:name="Text"/>
+    <text:sequence-decl text:display-outline-level="0" text:name="Drawing"/>
+    <text:sequence-decl text:display-outline-level="0" text:name="Figure"/>
+   </text:sequence-decls>
+   <text:p text:style-name="P1">x<text:change text:change-id="ct121341168"/></text:p>
+   <text:p text:style-name="P1"></text:p>
+  </office:text>
+ </office:body>
+</office:document>
+
diff --git a/sw/qa/extras/uiwriter/uiwriter.cxx b/sw/qa/extras/uiwriter/uiwriter.cxx
index 1ede61bc5bad..f4ceaeb25839 100644
--- a/sw/qa/extras/uiwriter/uiwriter.cxx
+++ b/sw/qa/extras/uiwriter/uiwriter.cxx
@@ -168,6 +168,8 @@ public:
     void testRedlineFrameAtCharStartInside();
     void testRedlineFrameAtParaStartOutside();
     void testRedlineFrameAtParaEndInside();
+    void testRedlineFrameAtParaOneParagraph();
+    void testRedlineFrameAtPara2ndParagraph();
     void testThreadedException();
     void testBookmarkCopy();
     void testFdo69893();
@@ -368,6 +370,8 @@ public:
     CPPUNIT_TEST(testRedlineFrameAtCharStartInside);
     CPPUNIT_TEST(testRedlineFrameAtParaStartOutside);
     CPPUNIT_TEST(testRedlineFrameAtParaEndInside);
+    CPPUNIT_TEST(testRedlineFrameAtParaOneParagraph);
+    CPPUNIT_TEST(testRedlineFrameAtPara2ndParagraph);
     CPPUNIT_TEST(testThreadedException);
     CPPUNIT_TEST(testBookmarkCopy);
     CPPUNIT_TEST(testFdo69893);
@@ -684,6 +688,18 @@ void SwUiWriterTest::testRedlineFrameAtParaEndInside()
     testRedlineFrame("redline_fly_duplication_at_para_end_inside.fodt");
 }
 
+void SwUiWriterTest::testRedlineFrameAtParaOneParagraph()
+{
+    // test ALLFLYS flag: oddly enough it didn't fail as fodt but failed as odt...
+    testRedlineFrame("redline_fly_at_para_one_paragraph.odt");
+}
+
+void SwUiWriterTest::testRedlineFrameAtPara2ndParagraph()
+{
+    // lost via the buggy increment in Copy
+    testRedlineFrame("redline_fly_duplication_at_para_2nd_paragraph.fodt");
+}
+
 void SwUiWriterTest::testThreadedException()
 {
     SvFileStream aFileStream(m_directories.getURLFromSrc(DATA_DIRECTORY) + "threadedException.fodt", StreamMode::READ);
diff --git a/sw/qa/uitest/writer_tests4/tdf108124.py b/sw/qa/uitest/writer_tests4/tdf108124.py
index 168f51bff33a..929a3d7b498d 100644
--- a/sw/qa/uitest/writer_tests4/tdf108124.py
+++ b/sw/qa/uitest/writer_tests4/tdf108124.py
@@ -28,11 +28,22 @@ class tdf108124(UITestCase):
     xWriterEdit.executeAction("TYPE", mkPropertyValues({"KEYCODE":"CTRL+a"})) # select all
     xWriterEdit.executeAction("TYPE", mkPropertyValues({"KEYCODE":"CTRL+c"})) # copy
     xWriterEdit.executeAction("TYPE", mkPropertyValues({"KEYCODE":"CTRL+v"})) # paste
-    self.assertEqual(document.GraphicObjects.getCount(), 4)
+    self.assertEqual(document.GraphicObjects.getCount(), 2)
+    xObj1Old = document.GraphicObjects[0]
+    xObj2Old = document.GraphicObjects[1]
     self.xUITest.executeCommand(".uno:Undo")  #Undo
     self.assertEqual(document.GraphicObjects.getCount(), 2)
+    xObj1New = document.GraphicObjects[0]
+    xObj2New = document.GraphicObjects[1]
+    # there should be 2 different objects now but they have the same names,
+    # so rely on the object identity for testing...
+    self.assertNotEqual(xObj1Old, xObj1New)
+    self.assertNotEqual(xObj1Old, xObj2New)
+    self.assertNotEqual(xObj2Old, xObj1New)
+    self.assertNotEqual(xObj2Old, xObj2New)
     self.xUITest.executeCommand(".uno:Redo")  #Redo
-    self.assertEqual(document.GraphicObjects.getCount(), 4)
+    self.assertEqual(document.GraphicObjects.getCount(), 2)
 
     self.ui_test.close_doc()
+
 # vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/sw/source/core/access/accframebase.cxx b/sw/source/core/access/accframebase.cxx
index fba6ca85fcc6..cfbfe9ecf678 100644
--- a/sw/source/core/access/accframebase.cxx
+++ b/sw/source/core/access/accframebase.cxx
@@ -319,8 +319,7 @@ bool SwAccessibleFrameBase::GetSelectedState( )
                         }
                         else if( rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA )
                         {
-                            if( ((nHere > nStartIndex) || pStart->nContent.GetIndex() ==0 )
-                                && (nHere < nEndIndex ) )
+                            if (IsSelectFrameAnchoredAtPara(*pPos, *pStart, *pEnd))
                                 return true;
                         }
                         else if (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
diff --git a/sw/source/core/access/accmap.cxx b/sw/source/core/access/accmap.cxx
index df641292b4f0..0662ec7ddf26 100644
--- a/sw/source/core/access/accmap.cxx
+++ b/sw/source/core/access/accmap.cxx
@@ -1283,19 +1283,17 @@ void SwAccessibleMap::InvalidateShapeInParaSelection()
                                     }
                                     else if( rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA )
                                     {
-                                        if (((nStartIndex < nFirstNode) ||
-                                             (nFirstNode == nStartIndex && pStart->nContent.GetIndex() == 0))
-                                            && (nLastNode < nEndIndex))
+                                        uno::Reference<XAccessible> const xAcc((*aIter).second);
+                                        if (xAcc.is())
                                         {
-                                            uno::Reference < XAccessible > xAcc( (*aIter).second );
-                                            if( xAcc.is() )
+                                            if (IsSelectFrameAnchoredAtPara(*pPos, *pStart, *pEnd))
+                                            {
                                                 static_cast < ::accessibility::AccessibleShape* >(xAcc.get())->SetState( AccessibleStateType::SELECTED );
-                                        }
-                                        else
-                                        {
-                                            uno::Reference < XAccessible > xAcc( (*aIter).second );
-                                            if(xAcc.is())
+                                            }
+                                            else
+                                            {
                                                 static_cast < ::accessibility::AccessibleShape* >(xAcc.get())->ResetState( AccessibleStateType::SELECTED );
+                                            }
                                         }
                                     }
                                     else if (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
diff --git a/sw/source/core/doc/DocumentContentOperationsManager.cxx b/sw/source/core/doc/DocumentContentOperationsManager.cxx
index 731bac5b7b49..4b9ee39a1ed4 100644
--- a/sw/source/core/doc/DocumentContentOperationsManager.cxx
+++ b/sw/source/core/doc/DocumentContentOperationsManager.cxx
@@ -3466,9 +3466,11 @@ void DocumentContentOperationsManager::CopyFlyInFlyImpl(
                     ++nStart;
             break;
             case RndStdIds::FLY_AT_PARA:
-                // FIXME TODO why exclude start node, this seems very questionable and causes data loss on export
-                if(m_rDoc.getIDocumentRedlineAccess().IsRedlineMove())
-                    ++nStart;
+                {
+                    bAdd = IsSelectFrameAnchoredAtPara(*pAPos,
+                        pCopiedPaM ? *pCopiedPaM->Start() : SwPosition(rRg.aStart),
+                        pCopiedPaM ? *pCopiedPaM->End() : SwPosition(rRg.aEnd));
+                }
             break;
             case RndStdIds::FLY_AT_CHAR:
                 {
@@ -3480,7 +3482,7 @@ void DocumentContentOperationsManager::CopyFlyInFlyImpl(
             default:
                 continue;
         }
-        if (RndStdIds::FLY_AT_CHAR != pAnchor->GetAnchorId())
+        if (RndStdIds::FLY_AT_FLY == pAnchor->GetAnchorId())
         {
             if (nStart > nSkipAfter)
                 continue;
@@ -3495,31 +3497,6 @@ void DocumentContentOperationsManager::CopyFlyInFlyImpl(
                 bAdd = true;
             if (!bAdd && !m_rDoc.getIDocumentRedlineAccess().IsRedlineMove()) // fdo#40599: not for redline move
             {
-                bool bEmptyNode = false;
-                bool bLastNode = false;
-                // is the node empty?
-                const SwNodes& rNodes = pAPos->nNode.GetNodes();
-                SwTextNode *const pTextNode = pAPos->nNode.GetNode().GetTextNode();
-                if (nullptr != pTextNode)
-                {
-                    bEmptyNode = pTextNode->GetText().isEmpty();
-                    if (bEmptyNode)
-                    {
-                        //last node information is only necessary to know for the last TextNode
-                        SwNodeIndex aTmp( pAPos->nNode );
-                        ++aTmp;//goto next node
-                        while (aTmp.GetNode().IsEndNode())
-                        {
-                            if (aTmp == rNodes.GetEndOfContent().GetIndex())
-                            {
-                                bLastNode = true;
-                                break;
-                            }
-                            ++aTmp;
-                        }
-                    }
-                }
-                bAdd = bLastNode && bEmptyNode;
                 if (!bAdd)
                 {
                     // technically old code checked nContent of AT_FLY which is pointless
@@ -4857,6 +4834,8 @@ bool DocumentContentOperationsManager::CopyImplImpl(SwPaM& rPam, SwPosition& rPo
 
         // at-char anchors post SplitNode are on index 0 of 2nd node and will
         // remain there - move them back to the start (end would also work?)
+        // ... also for at-para anchors; here start is preferable because
+        // it's consistent with SplitNode from SwUndoInserts::RedoImpl()
         if (pFlysAtInsPos)
         {
             // init *again* - because CopyWithFlyInFly moved startPos
@@ -4869,6 +4848,8 @@ bool DocumentContentOperationsManager::CopyImplImpl(SwPaM& rPam, SwPosition& rPo
                 startPos = *temp.GetPoint();
             }
             assert(startPos.nNode.GetNode().IsContentNode());
+            SwPosition startPosAtPara(startPos);
+            startPosAtPara.nContent.Assign(nullptr, 0);
 
             for (SwFrameFormat * pFly : *pFlysAtInsPos)
             {
@@ -4879,6 +4860,12 @@ bool DocumentContentOperationsManager::CopyImplImpl(SwPaM& rPam, SwPosition& rPo
                     anchor.SetAnchor( &startPos );
                     pFly->SetFormatAttr(anchor);
                 }
+                if (pAnchor->GetAnchorId() == RndStdIds::FLY_AT_PARA)
+                {
+                    SwFormatAnchor anchor(*pAnchor);
+                    anchor.SetAnchor( &startPosAtPara );
+                    pFly->SetFormatAttr(anchor);
+                }
             }
         }
 
diff --git a/sw/source/core/doc/docedt.cxx b/sw/source/core/doc/docedt.cxx
index 3cc2e8b6144c..7edd87e12850 100644
--- a/sw/source/core/doc/docedt.cxx
+++ b/sw/source/core/doc/docedt.cxx
@@ -138,14 +138,14 @@ void SaveFlyInRange( const SwPaM& rPam, const SwPosition& rInsPos,
 
     const SwPosition* pPos = rPam.Start();
     const SwNodeIndex& rSttNdIdx = pPos->nNode;
-    short nSttOff = (!bMoveAllFlys && rSttNdIdx.GetNode().IsContentNode() &&
-                    pPos->nContent.GetIndex()) ? 1 : 0;
 
-    pPos = rPam.GetPoint() == pPos ? rPam.GetMark() : rPam.GetPoint();
-    const SwNodeIndex& rEndNdIdx = pPos->nNode;
-    short nOff = ( bMoveAllFlys || ( rEndNdIdx.GetNode().IsContentNode() &&
-                pPos->nContent == rEndNdIdx.GetNode().GetContentNode()->Len() ))
-                    ? 0 : 1;
+    SwPosition atParaEnd(*rPam.End());
+    if (bMoveAllFlys)
+    {
+        assert(rPam.End()->nContent.GetIndex() == rPam.End()->nNode.GetNode().GetTextNode()->Len());
+        ++atParaEnd.nNode;
+        atParaEnd.nContent.Assign(atParaEnd.nNode.GetNode().GetContentNode(), 0);
+    }
 
     for( SwFrameFormats::size_type n = 0; n < rFormats.size(); ++n )
     {
@@ -163,27 +163,13 @@ void SaveFlyInRange( const SwPaM& rPam, const SwPosition& rInsPos,
         {
             bool bInsPos = false;
 
-            if (!bMoveAllFlys
-                && RndStdIds::FLY_AT_CHAR != pAnchor->GetAnchorId()
-                && rEndNdIdx == pAPos->nNode)
-            {
-                // Do not touch Anchor, if only a part of the EndNode
-                // or the whole EndNode is identical with the SttNode
-                if( rSttNdIdx != pAPos->nNode )
-                {
-                    // Only attach an anchor to the beginning or end
-                    SwPosition aPos( rSttNdIdx );
-                    SwFormatAnchor aAnchor( *pAnchor );
-                    aAnchor.SetAnchor( &aPos );
-                    pFormat->SetFormatAttr( aAnchor );
-                }
-            }
-            else if (  (//bMoveAllFlys ... no do not check - all callers are actually from redline code, from the MoveToSection case; so check bMoveAllFlys only for AT_PARA!
-                           (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())
+            if (       (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId()
                         && IsDestroyFrameAnchoredAtChar(*pAPos, *rPam.Start(), *rPam.End()))
                     || (RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()
-                        && rSttNdIdx.GetIndex() + nSttOff <= pAPos->nNode.GetIndex()
-                        && pAPos->nNode.GetIndex() <= rEndNdIdx.GetIndex() - nOff)
+                        && IsSelectFrameAnchoredAtPara(*pAPos, *rPam.Start(), atParaEnd,
+                            bMoveAllFlys
+                                ? DelContentType::CheckNoCntnt|DelContentType::AllMask
+                                : DelContentType::AllMask))
                     || (RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()
                             && (bInsPos = (rInsPos.nNode == pAPos->nNode)))
                     || (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId()
@@ -224,7 +210,6 @@ void DelFlyInRange( const SwNodeIndex& rMkNdIdx,
                             : SwPosition(rMkNdIdx));
     SwPosition const& rStart = mark <= point ? mark : point;
     SwPosition const& rEnd   = mark <= point ? point : mark;
-    const bool bDelFwrd = rMkNdIdx.GetIndex() <= rPtNdIdx.GetIndex();
 
     SwDoc* pDoc = rMkNdIdx.GetNode().GetDoc();
     SwFrameFormats& rTable = *pDoc->GetSpzFrameFormats();
@@ -235,25 +220,14 @@ void DelFlyInRange( const SwNodeIndex& rMkNdIdx,
         SwPosition const*const pAPos = rAnch.GetContentAnchor();
         if (pAPos &&
             (((rAnch.GetAnchorId() == RndStdIds::FLY_AT_PARA)
-                && (bDelFwrd
-                    ? rMkNdIdx < pAPos->nNode && pAPos->nNode <= rPtNdIdx
-                    : rPtNdIdx <= pAPos->nNode && pAPos->nNode < rMkNdIdx))
+                && IsSelectFrameAnchoredAtPara(*pAPos, rStart, rEnd, pPtIdx
+                    ? DelContentType::AllMask
+                    : DelContentType::AllMask|DelContentType::CheckNoCntnt))
             || ((rAnch.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
                 && IsDestroyFrameAnchoredAtChar(*pAPos, rStart, rEnd, pPtIdx
                     ? DelContentType::AllMask
                     : DelContentType::AllMask|DelContentType::CheckNoCntnt))))
         {
-            // Only move the Anchor??
-            if ((rAnch.GetAnchorId() == RndStdIds::FLY_AT_PARA)
-                && rPtNdIdx == pAPos->nNode )
-            {
-                SwFormatAnchor aAnch( pFormat->GetAnchor() );
-                SwPosition aPos( rMkNdIdx );
-                aAnch.SetAnchor( &aPos );
-                pFormat->SetFormatAttr( aAnch );
-            }
-            else
-            {
                 // If the Fly is deleted, all Flys in its content have to be deleted too.
                 const SwFormatContent &rContent = pFormat->GetContent();
                 // But only fly formats own their content, not draw formats.
@@ -274,7 +248,6 @@ void DelFlyInRange( const SwNodeIndex& rMkNdIdx,
                 // DelLayoutFormat can also trigger the deletion of objects.
                 if( i > rTable.size() )
                     i = rTable.size();
-            }
         }
     }
 }
diff --git a/sw/source/core/edit/edglbldc.cxx b/sw/source/core/edit/edglbldc.cxx
index a49b1ca8c235..bbc10e095f80 100644
--- a/sw/source/core/edit/edglbldc.cxx
+++ b/sw/source/core/edit/edglbldc.cxx
@@ -325,7 +325,7 @@ bool SwEditShell::MoveGlobalDocContent( const SwGlblDocContents& rArr ,
         aInsPos  = pMyDoc->GetNodes().GetEndOfContent();
 
     bool bRet = pMyDoc->getIDocumentContentOperations().MoveNodeRange( aRg, aInsPos,
-              SwMoveFlags::ALLFLYS | SwMoveFlags::CREATEUNDOOBJ );
+              SwMoveFlags::CREATEUNDOOBJ );
 
     EndAllAction();
     return bRet;
diff --git a/sw/source/core/layout/trvlfrm.cxx b/sw/source/core/layout/trvlfrm.cxx
index fe131dbc51c1..b0a46504e66a 100644
--- a/sw/source/core/layout/trvlfrm.cxx
+++ b/sw/source/core/layout/trvlfrm.cxx
@@ -2584,22 +2584,7 @@ void SwRootFrame::CalcFrameRects(SwShellCursor &rCursor)
                         && (   (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR
                                 && IsDestroyFrameAnchoredAtChar(*anchoredAt, *pStartPos, *pEndPos))
                             || (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA
-                                && *pStartPos <= *anchoredAt
-                                && *anchoredAt < *pEndPos)));
-                if (anchoredAt != nullptr
-                    && rAnchor.GetAnchorId() != RndStdIds::FLY_AT_CHAR
-                    && *anchoredAt == *pEndPos)
-                {
-                    const SwNodes& nodes = anchoredAt->GetDoc()->GetNodes();
-                    if( *pEndPos == SwPosition( nodes.GetEndOfContent()))
-                        inSelection = true;
-                    else
-                    {
-                        SwNodeIndex idx( nodes.GetEndOfContent());
-                        if( SwContentNode* last = SwNodes::GoPrevious( &idx ))
-                            inSelection = *pEndPos == SwPosition( *last, last->Len());
-                    }
-                }
+                                && IsSelectFrameAnchoredAtPara(*anchoredAt, *pStartPos, *pEndPos))));
                 if( inSelection )
                         Add( aRegion, pFly->getFrameArea() );
                 else if ( !pFly->IsAnLower( pStartFrame ) &&
diff --git a/sw/source/core/undo/undobj.cxx b/sw/source/core/undo/undobj.cxx
index ef6dc1ae90c6..7ddeb7a15e3e 100644
--- a/sw/source/core/undo/undobj.cxx
+++ b/sw/source/core/undo/undobj.cxx
@@ -931,7 +931,6 @@ void SwUndoSaveContent::DelContentIndex( const SwPosition& rMark,
         const SwFrameFormats& rSpzArr = *pDoc->GetSpzFrameFormats();
         if( !rSpzArr.empty() )
         {
-            const bool bDelFwrd = rMark.nNode.GetIndex() <= rPoint.nNode.GetIndex();
             SwFrameFormat* pFormat;
             const SwFormatAnchor* pAnchor;
             size_t n = rSpzArr.size();
@@ -965,28 +964,25 @@ void SwUndoSaveContent::DelContentIndex( const SwPosition& rMark,
                 case RndStdIds::FLY_AT_PARA:
                     {
                         pAPos =  pAnchor->GetContentAnchor();
-                        if( pAPos )
+                        if (pAPos &&
+                            pStt->nNode <= pAPos->nNode && pAPos->nNode <= pEnd->nNode)
                         {
-                            bool bTmp;
-                            if( DelContentType::CheckNoCntnt & nDelContentType )
-                                bTmp = pStt->nNode <= pAPos->nNode && pAPos->nNode < pEnd->nNode;
-                            else
-                            {
-                                if (bDelFwrd)
-                                    bTmp = rMark.nNode < pAPos->nNode &&
-                                        pAPos->nNode <= rPoint.nNode;
-                                else
-                                    bTmp = rPoint.nNode <= pAPos->nNode &&
-                                        pAPos->nNode < rMark.nNode;
-                            }
-
-                            if (bTmp)
-                            {
                                 if( !m_pHistory )
                                     m_pHistory.reset( new SwHistory );
 
+                                if (IsSelectFrameAnchoredAtPara(*pAPos, *pStt, *pEnd, nDelContentType))
+                                {
+                                    m_pHistory->AddDeleteFly(*pFormat, nChainInsPos);
+                                    // reset n so that no Format is skipped
+                                    n = n >= rSpzArr.size()
+                                        ? rSpzArr.size() : n+1;
+                                }
                                 // Moving the anchor?
-                                if( !( DelContentType::CheckNoCntnt & nDelContentType ) &&
+                                else if (!((DelContentType::CheckNoCntnt|DelContentType::ExcludeFlyAtStartEnd)
+                                        & nDelContentType) &&
+                                    // at least for calls from SwUndoDelete,
+                                    // this should work - other Undos don't
+                                    // remember the order of the cursor
                                     (rPoint.nNode.GetIndex() == pAPos->nNode.GetIndex())
                                     // Do not try to move the anchor to a table!
                                     && rMark.nNode.GetNode().IsTextNode())
@@ -997,14 +993,6 @@ void SwUndoSaveContent::DelContentIndex( const SwPosition& rMark,
                                     aAnch.SetAnchor( &aPos );
                                     pFormat->SetFormatAttr( aAnch );
                                 }
-                                else
-                                {
-                                    m_pHistory->AddDeleteFly(*pFormat, nChainInsPos );
-                                    // reset n so that no Format is skipped
-                                    n = n >= rSpzArr.size() ?
-                                        rSpzArr.size() : n+1;
-                                }
-                            }
                         }
                     }
                     break;
@@ -1021,7 +1009,7 @@ void SwUndoSaveContent::DelContentIndex( const SwPosition& rMark,
                             n = n >= rSpzArr.size() ? rSpzArr.size() : n+1;
                         }
                         else if (!((DelContentType::CheckNoCntnt |
-                                    DelContentType::ExcludeAtCharFlyAtStartEnd)
+                                    DelContentType::ExcludeFlyAtStartEnd)
                                     & nDelContentType))
                         {
                             if( *pStt <= *pAPos && *pAPos < *pEnd )
@@ -1547,6 +1535,15 @@ static bool IsAtStartOfSection(SwPosition const& rAnchorPos)
     return node == rAnchorPos.nNode && rAnchorPos.nContent == 0;
 }
 
+static bool IsNotBackspaceHeuristic(
+        SwPosition const& rStart, SwPosition const& rEnd)
+{
+    // check if the selection is backspace/delete created by DelLeft/DelRight
+    return rStart.nNode.GetIndex() + 1 != rEnd.nNode.GetIndex()
+        || rEnd.nContent != 0
+        || rStart.nContent != rStart.nNode.GetNode().GetTextNode()->Len();
+}
+
 bool IsDestroyFrameAnchoredAtChar(SwPosition const & rAnchorPos,
         SwPosition const & rStart, SwPosition const & rEnd,
         DelContentType const nDelContentType)
@@ -1566,16 +1563,57 @@ bool IsDestroyFrameAnchoredAtChar(SwPosition const & rAnchorPos,
     // in general, exclude the start and end position
     return ((rStart < rAnchorPos)
             || (rStart == rAnchorPos
-                && !(nDelContentType & DelContentType::ExcludeAtCharFlyAtStartEnd)
+                && !(nDelContentType & DelContentType::ExcludeFlyAtStartEnd)
                 // special case: fully deleted node
                 && ((rStart.nNode != rEnd.nNode && rStart.nContent == 0)
                     || IsAtStartOfSection(rAnchorPos))))
         && ((rAnchorPos < rEnd)
             || (rAnchorPos == rEnd
-                && !(nDelContentType & DelContentType::ExcludeAtCharFlyAtStartEnd)
+                && !(nDelContentType & DelContentType::ExcludeFlyAtStartEnd)
                 // special case: fully deleted node
                 && ((rEnd.nNode != rStart.nNode && rEnd.nContent == rEnd.nNode.GetNode().GetTextNode()->Len())
                     || IsAtEndOfSection(rAnchorPos))));
 }
 
+bool IsSelectFrameAnchoredAtPara(SwPosition const & rAnchorPos,
+        SwPosition const & rStart, SwPosition const & rEnd,
+        DelContentType const nDelContentType)
+{
+    assert(rStart <= rEnd);
+
+    // CheckNoCntnt means DelFullPara which is obvious to handle
+    if (DelContentType::CheckNoCntnt & nDelContentType)
+    {   // exclude selection end node because it won't be deleted
+        return (rAnchorPos.nNode < rEnd.nNode)
+            && (rStart.nNode <= rAnchorPos.nNode);
+    }
+
+    if (rAnchorPos.GetDoc()->IsInReading())
+    {   // FIXME hack for writerfilter RemoveLastParagraph(); can't test file format more specific?
+        // but it MUST NOT be done during the SetRedlineFlags at the end of ODF
+        // import, where the IsInXMLImport() cannot be checked because the
+        // stupid code temp. overrides it - instead rely on setting the ALLFLYS
+        // flag in MoveFromSection() and converting that to CheckNoCntnt with
+        // adjusted cursor!
+        return (rStart.nNode < rAnchorPos.nNode) && (rAnchorPos.nNode < rEnd.nNode);
+    }
+
+    // in general, exclude the start and end position
+    return ((rStart.nNode < rAnchorPos.nNode)
+            || (rStart.nNode == rAnchorPos.nNode
+                && !(nDelContentType & DelContentType::ExcludeFlyAtStartEnd)
+                // special case: fully deleted node
+                && ((rStart.nNode != rEnd.nNode && rStart.nContent == 0
+                        // but not if the selection is backspace/delete!
+                        && IsNotBackspaceHeuristic(rStart, rEnd))
+                    || IsAtStartOfSection(rStart))))
+        && ((rAnchorPos.nNode < rEnd.nNode)
+            || (rAnchorPos.nNode == rEnd.nNode
+                && !(nDelContentType & DelContentType::ExcludeFlyAtStartEnd)
+                // special case: fully deleted node
+                && ((rEnd.nNode != rStart.nNode && rEnd.nContent == rEnd.nNode.GetNode().GetTextNode()->Len()
+                        && IsNotBackspaceHeuristic(rStart, rEnd))
+                    || IsAtEndOfSection(rEnd))));
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/untblk.cxx b/sw/source/core/undo/untblk.cxx
index 46f7b7a769c5..365736807059 100644
--- a/sw/source/core/undo/untblk.cxx
+++ b/sw/source/core/undo/untblk.cxx
@@ -200,15 +200,14 @@ SwUndoInserts::~SwUndoInserts()
 //  But storing absolute indices leads to crashes if some action in Undo fails to roll back some modifications.
 
 //  Has following main steps:
-//  1. DelContentIndex to delete footnotes, flys, bookmarks (see comment for this function)
+//  1. m_FlyUndos removes flys anchored to first and last paragraph in Undo range.
+//     This array may be empty.
+//  2. DelContentIndex to delete footnotes, flys, bookmarks (see comment for this function)
 //     Deleted flys are stored in pHistory array.
-//     First and last paragraphs flys are handled later in this function! They are not deleted by DelContentIndex!
-//     For flys anchored to last paragraph, DelContentIndex re-anchors them to the last paragraph that will remain after Undo.
-//     This is not fully correct, as everything between nSttNode and nEndNode should be deleted (these nodes marks range of inserted nodes).
-//     But due to bug in paste (probably there), during paste all flys are anchored to last paragraph (see https://bugs.documentfoundation.org/show_bug.cgi?id=94225#c38).
-//     So they should be re-anchored.
-//  2. MoveToUndoNds moves nodes to Undo nodes array and removes them from document.
-//  3. m_FlyUndos removes flys anchored to first and last paragraph in Undo range. This array may be empty.
+//     First and last paragraphs flys are not deleted by DelContentIndex!
+//     For flys anchored to last paragraph, DelContentIndex re-anchors them to
+//     the last paragraph that will remain after Undo.
+//  3. MoveToUndoNds moves nodes to Undo nodes array and removes them from document.
 //  4. Lastly (starting from if(pTextNode)), text from last paragraph is joined to last remaining paragraph and FormatColl for last paragraph is restored.
 //     Format coll for last paragraph is removed during execution of UndoImpl
 
@@ -266,7 +265,7 @@ void SwUndoInserts::UndoImpl(::sw::UndoRedoContext & rContext)
         m_nSetPos = m_pHistory->Count();
         sal_uLong nTmp = rPam.GetMark()->nNode.GetIndex();
         DelContentIndex(*rPam.GetMark(), *rPam.GetPoint(),
-            DelContentType::AllMask|DelContentType::ExcludeAtCharFlyAtStartEnd);
+            DelContentType::AllMask|DelContentType::ExcludeFlyAtStartEnd);
         m_nNodeDiff += nTmp - rPam.GetMark()->nNode.GetIndex();
         if( *rPam.GetPoint() != *rPam.GetMark() )
         {


More information about the Libreoffice-commits mailing list