sw/inc/crsrsh.hxx                    |   14 -
 sw/qa/extras/odfimport/odfimport.cxx |    4 
 sw/qa/extras/uiwriter/uiwriter3.cxx  |    8 
 sw/source/core/crsr/crsrsh.cxx       |  281 ++++++++++++++++++++++++++++++++---
 sw/source/core/edit/eddel.cxx        |   20 --
 sw/source/core/edit/edglss.cxx       |   20 --
 sw/source/uibase/wrtsh/move.cxx      |   46 +++++
 sw/source/uibase/wrtsh/select.cxx    |   36 ++--
 8 files changed, 348 insertions(+), 81 deletions(-)

New commits:
commit d81379db730a163c5ff75d4f3a3cddbd7b5eddda
Author:     Michael Stahl <michael.st...@allotropia.de>
AuthorDate: Mon May 8 16:38:03 2023 +0200
Commit:     Michael Stahl <michael.st...@allotropia.de>
CommitDate: Tue May 9 10:34:40 2023 +0200

    tdf#154877 sw: generalise ExtendedSelectAll()
    
    This used to work only in the body text; make it work in any text, so
    also text frame, header/footer, footnote, table cell.
    
    (fixes regression from commit 0590cd2857f68f48b8847071a9c1a7dbef135721)
    
    This is made much more difficult by table cells, which may contain
    nested tables; there is already some code to switch between text
    selection (via m_pCurrentCursor) and table cell selection (via
    m_pTableCursor).
    
    There were also a few things that looked kinda wrong, but i forgot
    where.
    
    One tricky case that can't be handled well is if there are multiple
    tables in a text but no paragraph (this is impossible in the body but
    possible in other texts by removing the paragraph via Ctrl+Shift+Delete)
    ... here the most we get is one table fully selected, because the
    SwTableCursor can't select cells from multiple tables.
    
    Change-Id: I0964baacecb8f10636792a27efa9ea6b18da4709
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/151544
    Tested-by: Jenkins
    Reviewed-by: Michael Stahl <michael.st...@allotropia.de>

diff --git a/sw/inc/crsrsh.hxx b/sw/inc/crsrsh.hxx
index 5f99deff126b..57bb90a11a5d 100644
--- a/sw/inc/crsrsh.hxx
+++ b/sw/inc/crsrsh.hxx
@@ -331,7 +331,7 @@ public:
     // only for usage in special cases allowed!
     void ExtendedSelectAll(bool bFootnotes = true);
     /// If ExtendedSelectAll() was called and selection didn't change since 
then.
-    bool ExtendedSelectedAll();
+    SwNode const* ExtendedSelectedAll() const;
     enum class StartsWith { None, Table, HiddenPara };
     /// If document body starts with a table or starts/ends with hidden 
paragraph.
     StartsWith StartsWith_();
@@ -593,8 +593,11 @@ public:
     // fields etc.
     OUString GetSelText() const;
 
-    // Check of SPoint or Mark of current cursor are placed within a table.
-    inline const SwTableNode* IsCursorInTable() const;
+    /// Check if Point of current cursor is placed within a table.
+    const SwTableNode* IsCursorInTable() const;
+    bool MoveOutOfTable();
+    bool TrySelectOuterTable();
+    bool MoveStartText();
 
     bool IsCursorInFootnote() const;
 
@@ -912,11 +915,6 @@ inline bool SwCursorShell::IsMultiSelection() const
     return m_pCurrentCursor->GetNext() != m_pCurrentCursor;
 }
 
-inline const SwTableNode* SwCursorShell::IsCursorInTable() const
-{
-    return m_pCurrentCursor->GetPointNode().FindTableNode();
-}
-
 inline bool SwCursorShell::IsCursorPtAtEnd() const
 {
     return m_pCurrentCursor->End() == m_pCurrentCursor->GetPoint();
diff --git a/sw/qa/extras/odfimport/odfimport.cxx 
b/sw/qa/extras/odfimport/odfimport.cxx
index add0d98789b6..7225be2d4b8e 100644
--- a/sw/qa/extras/odfimport/odfimport.cxx
+++ b/sw/qa/extras/odfimport/odfimport.cxx
@@ -732,6 +732,8 @@ CPPUNIT_TEST_FIXTURE(Test, testFdo37606)
 
         pWrtShell->SelAll(); // Selects the whole table.
         pWrtShell->SelAll(); // Selects the whole document.
+        pShellCursor = pWrtShell->getShellCursor(false);
+
         SwTextNode& rStart = 
dynamic_cast<SwTextNode&>(pShellCursor->Start()->GetNode());
         CPPUNIT_ASSERT_EQUAL(OUString("A1"), rStart.GetText());
 
@@ -798,11 +800,11 @@ CPPUNIT_TEST_FIXTURE(Test, testFdo69862)
     SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument 
*>(mxComponent.get());
     CPPUNIT_ASSERT(pTextDoc);
     SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell();
-    SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
 
     pWrtShell->SelAll(); // Selects A1.
     pWrtShell->SelAll(); // Selects the whole table.
     pWrtShell->SelAll(); // Selects the whole document.
+    SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
     SwTextNode& rStart = 
dynamic_cast<SwTextNode&>(pShellCursor->Start()->GetNode());
     // This was "Footnote.", as Ctrl-A also selected footnotes, but it should 
not.
     CPPUNIT_ASSERT_EQUAL(OUString("A1"), rStart.GetText());
diff --git a/sw/qa/extras/uiwriter/uiwriter3.cxx 
b/sw/qa/extras/uiwriter/uiwriter3.cxx
index 9580cd93c67f..c1a8faef373a 100644
--- a/sw/qa/extras/uiwriter/uiwriter3.cxx
+++ b/sw/qa/extras/uiwriter/uiwriter3.cxx
@@ -246,10 +246,12 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, testTdf114973)
 {
     createSwDoc("tdf114973.fodt");
 
-    dispatchCommand(mxComponent, ".uno:SelectAll", {});
-
     SwDoc* const pDoc = getSwDoc();
     SwWrtShell* const pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+    pWrtShell->SttEndDoc(true);
+
+    dispatchCommand(mxComponent, ".uno:SelectAll", {});
+
     // bug: cursor jumped into header
     CPPUNIT_ASSERT(!pWrtShell->IsInHeaderFooter());
 
@@ -1702,6 +1704,7 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, testTdf126504)
     //Use selectAll 2 times in a row
     dispatchCommand(mxComponent, ".uno:SelectAll", {});
     dispatchCommand(mxComponent, ".uno:SelectAll", {});
+    dispatchCommand(mxComponent, ".uno:SelectAll", {});
 
     dispatchCommand(mxComponent, ".uno:Copy", {});
 
@@ -1742,6 +1745,7 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, testTdf133982)
     dispatchCommand(mxComponent, ".uno:SelectAll", {});
     dispatchCommand(mxComponent, ".uno:SelectAll", {});
     dispatchCommand(mxComponent, ".uno:SelectAll", {});
+    dispatchCommand(mxComponent, ".uno:SelectAll", {});
 
     //Without the fix in place, it would have crashed here
     dispatchCommand(mxComponent, ".uno:Cut", {});
diff --git a/sw/source/core/crsr/crsrsh.cxx b/sw/source/core/crsr/crsrsh.cxx
index ef87acc0d454..dd78a45cbacb 100644
--- a/sw/source/core/crsr/crsrsh.cxx
+++ b/sw/source/core/crsr/crsrsh.cxx
@@ -591,56 +591,290 @@ bool SwCursorShell::SttEndDoc( bool bStt )
     return bRet;
 }
 
+const SwTableNode* SwCursorShell::IsCursorInTable() const
+{
+    if (m_pTableCursor)
+    {   // find the table that has the selected boxes
+        return 
m_pTableCursor->GetSelectedBoxes()[0]->GetSttNd()->FindTableNode();
+    }
+    return m_pCurrentCursor->GetPointNode().FindTableNode();
+}
+
+// fun cases to consider:
+// * outermost table
+//   - into para => SA/ESA
+//   - into prev/next table => continue...
+//   - no prev/next => done
+// * inner table
+//   - into containing cell => SA/ESA
+//   - into prev/next of containing cell
+//      + into para
+//      + into table nested in prev/next cell
+//   - out of table -> as above
+// => iterate in one direction until a node is reached that is a parent or a 
sibling of a parent of the current table
+// - parent reached => SA/ESA depending
+// - not in parent but in *prev/next* sibling of outer cell => 
TrySelectOuterTable
+// - not in parent but in *prev/next* sibling of outer table => 
TrySelectOuterTable
+//   => select-all cannot select a sequence of table with no para at same 
level; only 1 table
+// - no parent, no prev/next => TrySelectOuterTable
+
+bool SwCursorShell::MoveOutOfTable()
+{
+    SwPosition const point(*getShellCursor(false)->GetPoint());
+    SwPosition const mark(*getShellCursor(false)->GetMark());
+
+    for (auto const fnMove : {&fnMoveBackward, &fnMoveForward})
+    {
+        Push();
+        SwCursor *const pCursor(getShellCursor(false));
+
+        pCursor->Normalize(fnMove == &fnMoveBackward);
+        pCursor->DeleteMark();
+        SwTableNode const*const 
pTable(pCursor->GetPoint()->GetNode().FindTableNode());
+        assert(pTable);
+        while (MovePara(GoInContent, *fnMove))
+        {
+            SwStartNode const*const 
pBox(pCursor->GetPoint()->GetNode().FindTableBoxStartNode());
+            if (!pBox)
+            {
+                Pop(SwCursorShell::PopMode::DeleteStack);
+                return true; // moved to paragraph at top-level of text
+            }
+            if (pBox->GetIndex() < pTable->GetIndex()
+                && pTable->EndOfSectionIndex() < pBox->EndOfSectionIndex())
+            {
+                Pop(SwCursorShell::PopMode::DeleteStack);
+                return true; // pBox contains start position (pTable)
+            }
+        }
+
+        Pop(SwCursorShell::PopMode::DeleteCurrent);
+        // FIXME: Pop doesn't restore original cursor if nested tables
+        *getShellCursor(false)->GetPoint() = point;
+        getShellCursor(false)->SetMark();
+        *getShellCursor(false)->GetMark() = mark;
+    }
+    return false;
+}
+
+bool SwCursorShell::TrySelectOuterTable()
+{
+    assert(m_pTableCursor);
+    SwTableNode const& 
rInnerTable(*m_pTableCursor->GetPoint()->GetNode().FindTableNode());
+    SwNodes const& rNodes(rInnerTable.GetNodes());
+    SwTableNode const*const 
pOuterTable(rInnerTable.GetNodes()[rInnerTable.GetIndex()-1]->FindTableNode());
+    if (!pOuterTable)
+    {
+        return false;
+    }
+
+    // manually select boxes of pOuterTable
+    SwNodeIndex firstCell(*pOuterTable, +1);
+    SwNodeIndex 
lastCell(*rNodes[pOuterTable->EndOfSectionIndex()-1]->StartOfSectionNode());
+    SwSelBoxes aNew;
+    pOuterTable->GetTable().CreateSelection(&firstCell.GetNode(), 
&lastCell.GetNode(),
+            aNew, SwTable::SEARCH_NONE, false);
+    // set table cursor to 1st / last content which may be in inner table
+    SwContentNode *const pStart = rNodes.GoNext(&firstCell);
+    assert(pStart); // must at least find the previous point node
+    lastCell = *lastCell.GetNode().EndOfSectionNode();
+    SwContentNode *const pEnd = SwNodes::GoPrevious(&lastCell);
+    assert(pEnd); // must at least find the previous point node
+    delete m_pTableCursor;
+    m_pTableCursor = new SwShellTableCursor(*this, SwPosition(*pStart, 0), 
Point(),
+            SwPosition(*pEnd, 0), Point());
+    m_pTableCursor->ActualizeSelection( aNew );
+    m_pTableCursor->IsCursorMovedUpdate(); // clear this so GetCursor() 
doesn't recreate our SwSelBoxes
+
+    // this will update m_pCurrentCursor based on m_pTableCursor
+    
UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
+
+    return true;
+}
+
+/// find XText start node
+static SwStartNode const* FindTextStart(SwPosition const& rPos)
+{
+    SwStartNode const* pStartNode(rPos.GetNode().StartOfSectionNode());
+    while (pStartNode && (pStartNode->IsSectionNode() || 
pStartNode->IsTableNode()))
+    {
+        pStartNode = pStartNode->StartOfSectionNode();
+    }
+    return pStartNode;
+}
+
+static SwStartNode const* FindParentText(SwShellCursor const& rCursor)
+{
+    // find closest section containing both start and end - ignore Sections
+    SwStartNode const* pStartNode(FindTextStart(*rCursor.Start()));
+    SwEndNode const* 
pEndNode(FindTextStart(*rCursor.End())->EndOfSectionNode());
+    while (pStartNode->EndOfSectionNode()->GetIndex() < pEndNode->GetIndex())
+    {
+        pStartNode = pStartNode->StartOfSectionNode();
+    }
+    while (pStartNode->GetIndex() < pEndNode->StartOfSectionNode()->GetIndex())
+    {
+        pEndNode = 
pEndNode->StartOfSectionNode()->StartOfSectionNode()->EndOfSectionNode();
+    }
+    assert(pStartNode->EndOfSectionNode() == pEndNode);
+
+    return (pStartNode->IsSectionNode() || pStartNode->IsTableNode())
+        ? FindTextStart(SwPosition(*pStartNode))
+        : pStartNode;
+}
+
+bool SwCursorShell::MoveStartText()
+{
+    SwPosition const old(*m_pCurrentCursor->GetPoint());
+    SwStartNode const*const pStartNode(FindParentText(*getShellCursor(false)));
+    assert(pStartNode);
+    SwTableNode const*const pTable(pStartNode->FindTableNode());
+    m_pCurrentCursor->GetPoint()->Assign(*pStartNode);
+    GetDoc()->GetNodes().GoNext(m_pCurrentCursor->GetPoint());
+    while (m_pCurrentCursor->GetPoint()->GetNode().FindTableNode() != pTable
+        && (!pTable || pTable->GetIndex() < 
m_pCurrentCursor->GetPoint()->GetNode().FindTableNode()->GetIndex())
+        && MoveOutOfTable());
+    
UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
+    return old != *m_pCurrentCursor->GetPoint();
+}
+
+// select all inside the current XText, with table or hidden para at start/end
 void SwCursorShell::ExtendedSelectAll(bool bFootnotes)
 {
+    // find common ancestor node of both ends of cursor
+    SwStartNode const*const pStartNode(FindParentText(*getShellCursor(false)));
+    assert(pStartNode);
+    if (IsTableMode())
+    {   // convert m_pTableCursor to m_pCurrentCursor after determining 
pStartNode
+        TableCursorToCursor();
+    }
     SwNodes& rNodes = GetDoc()->GetNodes();
+    m_pCurrentCursor->Normalize(true);
     SwPosition* pPos = m_pCurrentCursor->GetPoint();
-    pPos->Assign( bFootnotes ? rNodes.GetEndOfPostIts() : 
rNodes.GetEndOfAutotext() );
+    pPos->Assign(bFootnotes ? rNodes.GetEndOfPostIts() : static_cast<SwNode 
const&>(*pStartNode));
     rNodes.GoNext( pPos );
     pPos = m_pCurrentCursor->GetMark();
-    pPos->Assign(rNodes.GetEndOfContent());
+    pPos->Assign(bFootnotes ? rNodes.GetEndOfContent() : static_cast<SwNode 
const&>(*pStartNode->EndOfSectionNode()));
     SwContentNode* pCNd = SwNodes::GoPrevious( pPos );
     if (pCNd)
         pPos->AssignEndIndex(*pCNd);
 }
 
-bool SwCursorShell::ExtendedSelectedAll()
+static typename SwCursorShell::StartsWith StartsWith(SwStartNode const& rStart)
 {
+    for (auto i = rStart.GetIndex() + 1; i < rStart.EndOfSectionIndex(); ++i)
+    {
+        SwNode const& rNode(*rStart.GetNodes()[i]);
+        switch (rNode.GetNodeType())
+        {
+            case SwNodeType::Section:
+                continue;
+            case SwNodeType::Table:
+                return SwCursorShell::StartsWith::Table;
+            case SwNodeType::Text:
+                if (rNode.GetTextNode()->IsHidden())
+                {
+                    return SwCursorShell::StartsWith::HiddenPara;
+                }
+                return SwCursorShell::StartsWith::None;
+            default:
+                return SwCursorShell::StartsWith::None;
+        }
+    }
+    return SwCursorShell::StartsWith::None;
+}
+
+static typename SwCursorShell::StartsWith EndsWith(SwStartNode const& rStart)
+{
+    for (auto i = rStart.EndOfSectionIndex() - 1; rStart.GetIndex() < i; --i)
+    {
+        SwNode const& rNode(*rStart.GetNodes()[i]);
+        switch (rNode.GetNodeType())
+        {
+            case SwNodeType::End:
+                if (rNode.StartOfSectionNode()->IsTableNode())
+                {
+                    return SwCursorShell::StartsWith::Table;
+                }
+//TODO buggy SwUndoRedline in testTdf137503?                
assert(rNode.StartOfSectionNode()->IsSectionNode());
+            break;
+            case SwNodeType::Text:
+                if (rNode.GetTextNode()->IsHidden())
+                {
+                    return SwCursorShell::StartsWith::HiddenPara;
+                }
+                return SwCursorShell::StartsWith::None;
+            default:
+                return SwCursorShell::StartsWith::None;
+        }
+    }
+    return SwCursorShell::StartsWith::None;
+}
+
+// return the node that is the start of the extended selection (to include 
table
+// or section start nodes; looks like extending for end nodes is not required)
+SwNode const* SwCursorShell::ExtendedSelectedAll() const
+{
+    if (m_pTableCursor)
+    {
+        return nullptr;
+    }
+
     SwNodes& rNodes = GetDoc()->GetNodes();
-    SwNodeIndex nNode(rNodes.GetEndOfAutotext());
+    SwShellCursor const*const pShellCursor = getShellCursor(false);
+    SwStartNode const* pStartNode(FindParentText(*pShellCursor));
+
+    SwNodeIndex nNode(*pStartNode);
     SwContentNode* pStart = rNodes.GoNext(&nNode);
     if (!pStart)
-        return false;
+    {
+        return nullptr;
+    }
 
-    nNode = rNodes.GetEndOfContent();
+    nNode = *pStartNode->EndOfSectionNode();
     SwContentNode* pEnd = SwNodes::GoPrevious(&nNode);
     if (!pEnd)
-        return false;
+    {
+        return nullptr;
+    }
 
     SwPosition aStart(*pStart, 0);
     SwPosition aEnd(*pEnd, pEnd->Len());
-    SwShellCursor* pShellCursor = getShellCursor(false);
-    return aStart == *pShellCursor->Start() && aEnd == *pShellCursor->End();
+    if (!(aStart == *pShellCursor->Start() && aEnd == *pShellCursor->End()))
+    {
+        return nullptr;
+    }
+
+    if (::StartsWith(*pStartNode) == StartsWith::None
+        && ::EndsWith(*pStartNode) == StartsWith::None)
+    {
+        return nullptr; // "ordinary" selection will work
+    }
+
+    // tdf#133990 ensure directly containing section is included in 
SwUndoDelete
+    while (pStartNode->IsSectionNode()
+        && pStartNode->GetIndex() == 
pStartNode->StartOfSectionNode()->GetIndex() + 1
+        && pStartNode->EndOfSectionNode()->GetIndex() + 1 == 
pStartNode->StartOfSectionNode()->EndOfSectionNode()->GetIndex())
+    {
+        pStartNode = pStartNode->StartOfSectionNode();
+    }
+
+    // pStartNode is the node that fully contains the selection - the first
+    // node of the selection is the first node inside pStartNode
+    return pStartNode->GetNodes()[pStartNode->GetIndex() + 1];
 }
 
 typename SwCursorShell::StartsWith SwCursorShell::StartsWith_()
 {
-    SwNodes& rNodes = GetDoc()->GetNodes();
-    SwNodeIndex nNode(rNodes.GetEndOfExtras());
-    SwContentNode* pContentNode = rNodes.GoNext(&nNode);
-    if (pContentNode->FindTableNode())
+    SwShellCursor const*const pShellCursor = getShellCursor(false);
+    SwStartNode const*const pStartNode(FindParentText(*pShellCursor));
+    if (auto const ret = ::StartsWith(*pStartNode); ret != StartsWith::None)
     {
-        return StartsWith::Table;
+        return ret;
     }
-    if (pContentNode->GetTextNode()->IsHidden())
+    if (auto const ret = ::EndsWith(*pStartNode); ret != StartsWith::None)
     {
-        return StartsWith::HiddenPara;
-    }
-    nNode = rNodes.GetEndOfContent();
-    pContentNode = SwNodes::GoPrevious(&nNode);
-    if (pContentNode && pContentNode->GetTextNode()->IsHidden())
-    {
-        return StartsWith::HiddenPara;
+        return ret;
     }
     return StartsWith::None;
 }
@@ -2329,7 +2563,7 @@ bool SwCursorShell::Pop(PopMode const eDelete,
 
     if (PopMode::DeleteCurrent == eDelete)
     {
-        SwCursorSaveState aSaveState( *m_pCurrentCursor );
+        ::std::optional<SwCursorSaveState> oSaveState( *m_pCurrentCursor );
 
         // If the visible SSelection was not changed
         const Point& rPoint = pOldStack->GetPtPos();
@@ -2357,6 +2591,7 @@ bool SwCursorShell::Pop(PopMode const eDelete,
             !m_pCurrentCursor->IsSelOvr( SwCursorSelOverFlags::Toggle |
                                  SwCursorSelOverFlags::ChangePos ) )
         {
+            oSaveState.reset(); // prevent UAF
             UpdateCursor(); // update current cursor
             if (m_pTableCursor)
             { // tdf#106929 ensure m_pCurrentCursor ring is recreated from 
table
diff --git a/sw/source/core/edit/eddel.cxx b/sw/source/core/edit/eddel.cxx
index e11bc3be4165..83855ff2d99a 100644
--- a/sw/source/core/edit/eddel.cxx
+++ b/sw/source/core/edit/eddel.cxx
@@ -35,7 +35,9 @@
 
 void SwEditShell::DeleteSel(SwPaM& rPam, bool const isArtificialSelection, 
bool *const pUndo)
 {
-    bool bSelectAll = StartsWith_() != SwCursorShell::StartsWith::None && 
ExtendedSelectedAll();
+    SwNode const*const pSelectAllStart(StartsWith_() != 
SwCursorShell::StartsWith::None
+        ? ExtendedSelectedAll()
+        : nullptr);
     // only for selections
     if (!rPam.HasMark()
         || (*rPam.GetPoint() == *rPam.GetMark()
@@ -51,7 +53,7 @@ void SwEditShell::DeleteSel(SwPaM& rPam, bool const 
isArtificialSelection, bool
     // 3. Point and Mark are at the document start and end, Point is in a 
table: delete selection as usual
     if( rPam.GetPointNode().FindTableNode() &&
         rPam.GetPointNode().StartOfSectionNode() !=
-        rPam.GetMarkNode().StartOfSectionNode() && !bSelectAll )
+        rPam.GetMarkNode().StartOfSectionNode() && pSelectAllStart == nullptr)
     {
         // group the Undo in the table
         if( pUndo && !*pUndo )
@@ -95,23 +97,13 @@ void SwEditShell::DeleteSel(SwPaM& rPam, bool const 
isArtificialSelection, bool
     {
         std::optional<SwPaM> pNewPam;
         SwPaM * pPam = &rPam;
-        if (bSelectAll)
+        if (pSelectAllStart)
         {
             assert(dynamic_cast<SwShellCursor*>(&rPam)); // must be corrected 
pam
             pNewPam.emplace(*rPam.GetMark(), *rPam.GetPoint());
             // Selection starts at the first para of the first cell, but we
             // want to delete the table node before the first cell as well.
-            while (SwTableNode const* pTableNode =
-                
pNewPam->Start()->GetNode().StartOfSectionNode()->FindTableNode())
-            {
-                pNewPam->Start()->Assign(*pTableNode);
-            }
-            // tdf#133990 ensure section is included in SwUndoDelete
-            while (SwSectionNode const* pSectionNode =
-                
pNewPam->Start()->GetNode().StartOfSectionNode()->FindSectionNode())
-            {
-                pNewPam->Start()->Assign(*pSectionNode);
-            }
+            pNewPam->Start()->Assign(*pSelectAllStart);
             pPam = &*pNewPam;
         }
         // delete everything
diff --git a/sw/source/core/edit/edglss.cxx b/sw/source/core/edit/edglss.cxx
index 667ae4690602..18e5d4ec823b 100644
--- a/sw/source/core/edit/edglss.cxx
+++ b/sw/source/core/edit/edglss.cxx
@@ -197,7 +197,9 @@ bool SwEditShell::CopySelToDoc( SwDoc& rInsDoc )
         bool bColSel = GetCursor_()->IsColumnSelection();
         if( bColSel && rInsDoc.IsClipBoard() )
             rInsDoc.SetColumnSelection( true );
-        bool bSelectAll = StartsWith_() != SwCursorShell::StartsWith::None && 
ExtendedSelectedAll();
+        SwNode const*const pSelectAllStart(StartsWith_() != 
SwCursorShell::StartsWith::None
+            ? ExtendedSelectedAll()
+            : nullptr);
         {
             for(SwPaM& rPaM : GetCursor()->GetRingContainer())
             {
@@ -221,24 +223,12 @@ bool SwEditShell::CopySelToDoc( SwDoc& rInsDoc )
                     // for the purpose of copying, our shell cursor is not 
touched.
                     // (Otherwise we would have to restore it.)
                     SwPaM aPaM(*rPaM.GetMark(), *rPaM.GetPoint());
-                    if (bSelectAll)
+                    if (pSelectAllStart)
                     {
                         // Selection starts at the first para of the first 
cell,
                         // but we want to copy the table and the start node 
before
                         // the first cell as well.
-                        // tdf#133982 tables can be nested
-                        const SwNode* pNode = &aPaM.Start()->GetNode();
-                        while (SwTableNode const* pTableNode =
-                            pNode->StartOfSectionNode()->FindTableNode())
-                        {
-                            pNode = pTableNode;
-                        }
-                        while (SwSectionNode const* pSectionNode =
-                            pNode->StartOfSectionNode()->FindSectionNode())
-                        {
-                            pNode = pSectionNode;
-                        }
-                        aPaM.Start()->Assign(*pNode);
+                        aPaM.Start()->Assign(*pSelectAllStart);
                     }
                     bRet = 
GetDoc()->getIDocumentContentOperations().CopyRange( aPaM, aPos, 
SwCopyFlags::CheckPosInFly)
                         || bRet;
diff --git a/sw/source/uibase/wrtsh/move.cxx b/sw/source/uibase/wrtsh/move.cxx
index b167003b1722..c29752db4392 100644
--- a/sw/source/uibase/wrtsh/move.cxx
+++ b/sw/source/uibase/wrtsh/move.cxx
@@ -226,12 +226,25 @@ bool SwWrtShell::GoStart( bool bKeepArea, bool 
*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)
@@ -266,15 +279,40 @@ bool SwWrtShell::GoStart( bool bKeepArea, bool 
*pMoveTable,
         else if ( bKeepArea )
             return true;
     }
-    // Regions ???
+
+    // first try to move to the start of the current SwSection
     return SwCursorShell::MoveRegion( GotoCurrRegionAndSkip, fnRegionStart ) ||
-           SwCursorShell::SttEndDoc(true);
+           (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 )
-        return MoveTable( GotoCurrTable, fnTableEnd );
+    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() )
     {
diff --git a/sw/source/uibase/wrtsh/select.cxx 
b/sw/source/uibase/wrtsh/select.cxx
index 5b7358c230ce..a23c1ec6a830 100644
--- a/sw/source/uibase/wrtsh/select.cxx
+++ b/sw/source/uibase/wrtsh/select.cxx
@@ -138,7 +138,10 @@ void SwWrtShell::SelAll()
         bool bHasWholeTabSelection = HasWholeTabSelection();
         bool bIsCursorInTable = IsCursorInTable();
 
-        if (!bHasWholeTabSelection)
+        if (!bHasWholeTabSelection
+            && (   !bIsCursorInTable
+                || getShellCursor(false)->GetMarkNode().FindTableNode() == 
nullptr
+                || !ExtendedSelectedAll())) // ESA inside table -> else branch
         {
             if ( IsSelection() && IsCursorPtAtEnd() )
                 SwapPam();
@@ -154,30 +157,35 @@ void SwWrtShell::SelAll()
             bIsFullSel &= !MoveSection( GoCurrSection, fnSectionEnd);
             Pop(SwCursorShell::PopMode::DeleteCurrent);
             GoStart(true, &bMoveTable, false, !bIsFullSel);
+            SttSelect();
+            GoEnd(true, &bMoveTable);
         }
         else
         {
-            EnterStdMode();
-            SttEndDoc(true);
+            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();
+            }
         }
-        SttSelect();
-        GoEnd(true, &bMoveTable);
 
         bool bNeedsExtendedSelectAll = StartsWith_() != StartsWith::None;
 
-        // If the cursor was in a table, then we only need the extended select
-        // all if the whole table is already selected, to still allow selecting
-        // only a single cell or a single table before selecting the whole
-        // document.
+        // the GoEnd() could have created a table selection, if so avoid ESA.
         if (bNeedsExtendedSelectAll && bIsCursorInTable)
-            bNeedsExtendedSelectAll = bHasWholeTabSelection;
+        {
+            bNeedsExtendedSelectAll = !HasWholeTabSelection();
+        }
 
         if (bNeedsExtendedSelectAll)
         {
-            // Disable table cursor to make sure getShellCursor() returns 
m_pCurrentCursor, not m_pTableCursor.
-            if (IsTableMode())
-                TableCursorToCursor();
-            // Do the extended select all on m_pCurrentCursor.
             ExtendedSelectAll(/*bFootnotes =*/ false);
         }
 

Reply via email to