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); }