summaryrefslogtreecommitdiff
path: root/sw
diff options
context:
space:
mode:
authorMiklos Vajna <vmiklos@collabora.com>2023-03-31 08:13:15 +0200
committerMiklos Vajna <vmiklos@collabora.com>2023-03-31 07:04:05 +0000
commitf6fbd9d5ff5b049112e6ca7a8943c156b3e4f411 (patch)
treeb47e21f95a3f4eb8ac4bd2be9788eebb994c88d3 /sw
parentad811b4441f6f9f8f42114640fea39cf0f3156a5 (diff)
sw floattable: remove empty follow flys on follow table removal
- add a SwRootFrame::DeleteEmptyFlys_(), which can delete empty flys and invalidate the anchors, so the necessary text frame joins and page frame deletions happen - add a SwRootFrame::InsertEmptyFly(), which can build a to-delete list for DeleteEmptyFlys_() - add a SwFlyAtContentFrame::DelEmpty(), which can call InsertEmptyFly() for one empty fly frame - in SwTabFrame::Cut(), call DelEmpty() on the fly parent, similar to how we do it for sections - in SwLayAction::InternalAction(), call DeleteEmptyFlys() to actually delete the unnecessary fly frames Change-Id: I8d3b4ee2c07b60d6187059bb177c56a129810750 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/149815 Reviewed-by: Miklos Vajna <vmiklos@collabora.com> Tested-by: Jenkins
Diffstat (limited to 'sw')
-rw-r--r--sw/qa/core/layout/flycnt.cxx138
-rw-r--r--sw/source/core/inc/flyfrms.hxx2
-rw-r--r--sw/source/core/inc/rootfrm.hxx9
-rw-r--r--sw/source/core/layout/flowfrm.cxx1
-rw-r--r--sw/source/core/layout/flycnt.cxx50
-rw-r--r--sw/source/core/layout/layact.cxx1
-rw-r--r--sw/source/core/layout/tabfrm.cxx11
7 files changed, 147 insertions, 65 deletions
diff --git a/sw/qa/core/layout/flycnt.cxx b/sw/qa/core/layout/flycnt.cxx
index daa9764325f5..09d08ff14285 100644
--- a/sw/qa/core/layout/flycnt.cxx
+++ b/sw/qa/core/layout/flycnt.cxx
@@ -37,8 +37,55 @@ public:
: SwModelTestBase("/sw/qa/core/layout/data/")
{
}
+
+ /// Creates a document with a multi-page floating table: 1 columns and 2 rows.
+ void Create1x2SplitFly();
};
+void Test::Create1x2SplitFly()
+{
+ createSwDoc();
+ SwDoc* pDoc = getSwDoc();
+ SwPageDesc aStandard(pDoc->GetPageDesc(0));
+ SwFormatFrameSize aPageSize(aStandard.GetMaster().GetFrameSize());
+ // 5cm for the page height, 2cm are the top and bottom margins, so 1cm remains for the body
+ // frame:
+ aPageSize.SetHeight(2834);
+ aStandard.GetMaster().SetFormatAttr(aPageSize);
+ pDoc->ChgPageDesc(0, aStandard);
+ // Insert a table:
+ SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
+ SwInsertTableOptions aTableOptions(SwInsertTableFlags::DefaultBorder, 0);
+ pWrtShell->InsertTable(aTableOptions, /*nRows=*/2, /*nCols=*/1);
+ pWrtShell->MoveTable(GotoPrevTable, fnTableStart);
+ pWrtShell->GoPrevCell();
+ pWrtShell->Insert("A1");
+ pWrtShell->GoNextCell();
+ pWrtShell->Insert("A2");
+ // Select cell:
+ pWrtShell->SelAll();
+ // Select table:
+ pWrtShell->SelAll();
+ // Wrap the table in a text frame:
+ SwFlyFrameAttrMgr aMgr(true, pWrtShell, Frmmgr_Type::TEXT, nullptr);
+ pWrtShell->StartAllAction();
+ aMgr.InsertFlyFrame(RndStdIds::FLY_AT_PARA, aMgr.GetPos(), aMgr.GetSize());
+ pWrtShell->EndAllAction();
+ // Allow the text frame to split:
+ pWrtShell->StartAllAction();
+ SwFrameFormats& rFlys = *pDoc->GetSpzFrameFormats();
+ SwFrameFormat* pFly = rFlys[0];
+ SwAttrSet aSet(pFly->GetAttrSet());
+ aSet.Put(SwFormatFlySplit(true));
+ pDoc->SetAttr(aSet, *pFly);
+ pWrtShell->EndAllAction();
+ SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
+ auto pPage1 = dynamic_cast<SwPageFrame*>(pLayout->Lower());
+ CPPUNIT_ASSERT(pPage1);
+ // We have 2 pages:
+ CPPUNIT_ASSERT(pPage1->GetNext());
+}
+
CPPUNIT_TEST_FIXTURE(Test, testSplitFlyWithTable)
{
// Given a document with a multi-page floating table:
@@ -235,38 +282,10 @@ CPPUNIT_TEST_FIXTURE(Test, testSplitFlyRow)
CPPUNIT_TEST_FIXTURE(Test, testSplitFlyEnable)
{
// Given a document with a table in a textframe:
- createSwDoc();
- SwDocShell* pDocShell = getSwDocShell();
- SwDoc* pDoc = getSwDoc();
- SwPageDesc aStandard(pDoc->GetPageDesc(0));
- SwFormatFrameSize aPageSize(aStandard.GetMaster().GetFrameSize());
- // 5cm for the page height, 2cm are the top and bottom margins, so 1cm remains for the body
- // frame:
- aPageSize.SetHeight(2834);
- aStandard.GetMaster().SetFormatAttr(aPageSize);
- pDoc->ChgPageDesc(0, aStandard);
- // Insert a table:
- SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
- SwInsertTableOptions aTableOptions(SwInsertTableFlags::DefaultBorder, 0);
- pWrtShell->InsertTable(aTableOptions, /*nRows=*/2, /*nCols=*/1);
- pWrtShell->MoveTable(GotoPrevTable, fnTableStart);
- pWrtShell->SelAll();
- // Wrap it in a text frame:
- SwFlyFrameAttrMgr aMgr(true, pWrtShell, Frmmgr_Type::TEXT, nullptr);
- pWrtShell->StartAllAction();
- aMgr.InsertFlyFrame(RndStdIds::FLY_AT_PARA, aMgr.GetPos(), aMgr.GetSize());
- pWrtShell->EndAllAction();
-
- // When allowing the text frame to split:
- pWrtShell->StartAllAction();
- SwFrameFormats& rFlys = *pDoc->GetSpzFrameFormats();
- SwFrameFormat* pFly = rFlys[0];
- SwAttrSet aSet(pFly->GetAttrSet());
- aSet.Put(SwFormatFlySplit(true));
- pDoc->SetAttr(aSet, *pFly);
- pWrtShell->EndAllAction();
+ Create1x2SplitFly();
// Then make sure that the layout is updated and we have 2 pages:
+ SwDoc* pDoc = getSwDoc();
SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
auto pPage1 = dynamic_cast<SwPageFrame*>(pLayout->Lower());
CPPUNIT_ASSERT(pPage1);
@@ -548,43 +567,10 @@ CPPUNIT_TEST_FIXTURE(Test, testSplitFlyFollowHorizontalPosition)
CPPUNIT_TEST_FIXTURE(Test, testCursorTraversal)
{
// Given a document with a multi-page floating table:
- createSwDoc();
- SwDoc* pDoc = getSwDoc();
- SwPageDesc aStandard(pDoc->GetPageDesc(0));
- SwFormatFrameSize aPageSize(aStandard.GetMaster().GetFrameSize());
- // 5cm for the page height, 2cm are the top and bottom margins, so 1cm remains for the body
- // frame:
- aPageSize.SetHeight(2834);
- aStandard.GetMaster().SetFormatAttr(aPageSize);
- pDoc->ChgPageDesc(0, aStandard);
- // Insert a table:
- SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
- SwInsertTableOptions aTableOptions(SwInsertTableFlags::DefaultBorder, 0);
- pWrtShell->InsertTable(aTableOptions, /*nRows=*/2, /*nCols=*/1);
- pWrtShell->MoveTable(GotoPrevTable, fnTableStart);
- pWrtShell->GoPrevCell();
- pWrtShell->Insert("A1");
- pWrtShell->GoNextCell();
- pWrtShell->Insert("A2");
- // Select cell:
- pWrtShell->SelAll();
- // Select table:
- pWrtShell->SelAll();
- // Wrap the table in a text frame:
- SwFlyFrameAttrMgr aMgr(true, pWrtShell, Frmmgr_Type::TEXT, nullptr);
- pWrtShell->StartAllAction();
- aMgr.InsertFlyFrame(RndStdIds::FLY_AT_PARA, aMgr.GetPos(), aMgr.GetSize());
- pWrtShell->EndAllAction();
- // Allow the text frame to split:
- pWrtShell->StartAllAction();
- SwFrameFormats& rFlys = *pDoc->GetSpzFrameFormats();
- SwFrameFormat* pFly = rFlys[0];
- SwAttrSet aSet(pFly->GetAttrSet());
- aSet.Put(SwFormatFlySplit(true));
- pDoc->SetAttr(aSet, *pFly);
- pWrtShell->EndAllAction();
+ Create1x2SplitFly();
// When going from A1 to A2:
+ SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
pWrtShell->GotoTable("Table1");
SwTextNode* pTextNode = pWrtShell->GetCursor()->GetPointNode().GetTextNode();
CPPUNIT_ASSERT_EQUAL(OUString("A1"), pTextNode->GetText());
@@ -598,6 +584,28 @@ CPPUNIT_TEST_FIXTURE(Test, testCursorTraversal)
// i.e. the cursor didn't get from A1 to A2.
CPPUNIT_ASSERT_EQUAL(OUString("A2"), pTextNode->GetText());
}
+
+CPPUNIT_TEST_FIXTURE(Test, testSplitFlyRowDelete)
+{
+ // Given a document with a multi-page floating table:
+ Create1x2SplitFly();
+
+ // When deleting the row of A2:
+ SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
+ pWrtShell->GotoTable("Table1");
+ pWrtShell->Down(/*bSelect=*/false);
+ SwTextNode* pTextNode = pWrtShell->GetCursor()->GetPointNode().GetTextNode();
+ // We delete the right row:
+ CPPUNIT_ASSERT_EQUAL(OUString("A2"), pTextNode->GetText());
+ pWrtShell->DeleteRow();
+
+ // Then make sure we only have 1 page:
+ SwDoc* pDoc = getSwDoc();
+ SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
+ auto pPage1 = dynamic_cast<SwPageFrame*>(pLayout->Lower());
+ CPPUNIT_ASSERT(pPage1);
+ CPPUNIT_ASSERT(!pPage1->GetNext());
+}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/flyfrms.hxx b/sw/source/core/inc/flyfrms.hxx
index 8d3bd95589bc..74b9c44c6c25 100644
--- a/sw/source/core/inc/flyfrms.hxx
+++ b/sw/source/core/inc/flyfrms.hxx
@@ -200,6 +200,8 @@ public:
SwFlyAtContentFrame* GetFollow();
const SwFlyAtContentFrame* GetPrecede() const;
SwFlyAtContentFrame* GetPrecede();
+ /// Like Cut(), except that follow chaining is maintained.
+ void DelEmpty();
void dumpAsXmlAttributes(xmlTextWriterPtr pWriter) const override;
};
diff --git a/sw/source/core/inc/rootfrm.hxx b/sw/source/core/inc/rootfrm.hxx
index 9cf2a4f13143..0caf4012862d 100644
--- a/sw/source/core/inc/rootfrm.hxx
+++ b/sw/source/core/inc/rootfrm.hxx
@@ -76,6 +76,8 @@ using SwCurrShells = std::set<CurrShell*>;
class SwSectionFrame;
using SwDestroyList = o3tl::sorted_vector<SwSectionFrame*>;
+class SwFlyFrame;
+using SwFlyDestroyList = o3tl::sorted_vector<SwFlyFrame*>;
/// The root element of a Writer document layout. Lower frames are expected to
/// be SwPageFrame instances.
@@ -172,6 +174,7 @@ class SW_DLLPUBLIC SwRootFrame final : public SwLayoutFrame
SdrPage *mpDrawPage;
std::unique_ptr<SwDestroyList> mpDestroy;
+ std::unique_ptr<SwFlyDestroyList> mpFlyDestroy;
sal_uInt16 mnPhyPageNums; /// Page count
sal_uInt16 mnAccessibleShells; // Number of accessible shells
@@ -180,6 +183,8 @@ class SW_DLLPUBLIC SwRootFrame final : public SwLayoutFrame
void ImplInvalidateBrowseWidth();
void DeleteEmptySct_(); // Destroys the registered SectionFrames
+ /// Destroys the registered FlyFrames.
+ void DeleteEmptyFlys_();
void RemoveFromList_( SwSectionFrame* pSct ); // Removes SectionFrames from the Delete List
virtual void DestroyImpl() override;
@@ -381,7 +386,11 @@ public:
* destroyed later on or deregistered.
*/
void InsertEmptySct( SwSectionFrame* pDel );
+ /// Empty SwFlyFrames are registered here for deletion and destroyed later if they are not
+ /// de-registered in the meantime.
+ void InsertEmptyFly(SwFlyFrame* pDel);
void DeleteEmptySct() { if( mpDestroy ) DeleteEmptySct_(); }
+ void DeleteEmptyFlys() { if( mpFlyDestroy ) DeleteEmptyFlys_(); }
void RemoveFromList( SwSectionFrame* pSct ) { if( mpDestroy ) RemoveFromList_( pSct ); }
#ifdef DBG_UTIL
bool IsInDelList( SwSectionFrame* pSct ) const;
diff --git a/sw/source/core/layout/flowfrm.cxx b/sw/source/core/layout/flowfrm.cxx
index 0aa9c6f14534..444e597eaa3b 100644
--- a/sw/source/core/layout/flowfrm.cxx
+++ b/sw/source/core/layout/flowfrm.cxx
@@ -64,6 +64,7 @@
#include <IDocumentDrawModelAccess.hxx>
#include <pam.hxx>
#include <ndtxt.hxx>
+#include <flyfrms.hxx>
bool SwFlowFrame::s_bMoveBwdJump = false;
diff --git a/sw/source/core/layout/flycnt.cxx b/sw/source/core/layout/flycnt.cxx
index 52eaeb6c1cd4..a0abd0bf15d3 100644
--- a/sw/source/core/layout/flycnt.cxx
+++ b/sw/source/core/layout/flycnt.cxx
@@ -1624,6 +1624,25 @@ SwLayoutFrame *SwFrame::GetNextFlyLeaf( MakePageType eMakePage )
return pLayLeaf;
}
+void SwRootFrame::DeleteEmptyFlys_()
+{
+ assert(mpFlyDestroy);
+
+ while (!mpFlyDestroy->empty())
+ {
+ SwFlyFrame* pFly = *mpFlyDestroy->begin();
+ mpFlyDestroy->erase( mpFlyDestroy->begin() );
+ if (!pFly->getFrameArea().HasArea() && !pFly->ContainsContent()
+ && !pFly->IsDeleteForbidden())
+ {
+ SwTextFrame* pFlyAnchor = pFly->FindAnchorCharFrame();
+ SwFrame::DestroyFrame(pFly);
+ // So that JoinFrame() is called on the precede of the anchor if it has any.
+ pFlyAnchor->InvalidateSize();
+ }
+ }
+}
+
const SwFlyAtContentFrame* SwFlyAtContentFrame::GetPrecede() const
{
return static_cast<const SwFlyAtContentFrame*>(SwFlowFrame::GetPrecede());
@@ -1634,6 +1653,37 @@ SwFlyAtContentFrame* SwFlyAtContentFrame::GetPrecede()
return static_cast<SwFlyAtContentFrame*>(SwFlowFrame::GetPrecede());
}
+void SwFlyAtContentFrame::DelEmpty()
+{
+ SwFlyAtContentFrame* pMaster = IsFollow() ? GetPrecede() : nullptr;
+ if (pMaster)
+ {
+ pMaster->SetFollow(GetFollow());
+ }
+ SetFollow(nullptr);
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Height(0);
+ }
+ InvalidateObjRectWithSpaces();
+
+ if(getRootFrame())
+ {
+ getRootFrame()->InsertEmptyFly(this);
+ }
+}
+
+void SwRootFrame::InsertEmptyFly(SwFlyFrame* pDel)
+{
+ if (!mpFlyDestroy)
+ {
+ mpFlyDestroy.reset(new SwFlyDestroyList);
+ }
+
+ mpFlyDestroy->insert(pDel);
+}
+
SwLayoutFrame* SwFrame::GetPrevFlyLeaf()
{
auto pFly = dynamic_cast<SwFlyAtContentFrame*>(FindFlyFrame());
diff --git a/sw/source/core/layout/layact.cxx b/sw/source/core/layout/layact.cxx
index 971d2761639e..583ca4a3b4db 100644
--- a/sw/source/core/layout/layact.cxx
+++ b/sw/source/core/layout/layact.cxx
@@ -549,6 +549,7 @@ void SwLayAction::InternalAction(OutputDevice* pRenderContext)
const bool bTakeShortcut = !IsIdle() && !IsComplete() && IsShortCut(pPage);
m_pRoot->DeleteEmptySct();
+ m_pRoot->DeleteEmptyFlys();
if (lcl_isLayoutLooping()) return;
if (!bTakeShortcut)
diff --git a/sw/source/core/layout/tabfrm.cxx b/sw/source/core/layout/tabfrm.cxx
index ef169a209212..9bb630f181a0 100644
--- a/sw/source/core/layout/tabfrm.cxx
+++ b/sw/source/core/layout/tabfrm.cxx
@@ -3826,6 +3826,7 @@ void SwTabFrame::Cut()
{
OSL_ENSURE( !pUp->IsFootnoteFrame(), "Table in Footnote." );
SwSectionFrame *pSct = nullptr;
+ SwFlyFrame *pFly = nullptr;
// #126020# - adjust check for empty section
// #130797# - correct fix #126020#
if ( !pUp->Lower() && pUp->IsInSct() &&
@@ -3838,6 +3839,16 @@ void SwTabFrame::Cut()
pSct->InvalidateSize_();
}
}
+ else if (!pUp->Lower() && pUp->IsInFly() &&
+ !(pFly = pUp->FindFlyFrame())->ContainsContent() &&
+ !pFly->ContainsAny())
+ {
+ if (pUp == pFly && pFly->IsFlySplitAllowed())
+ {
+ auto pFlyAtContent = static_cast<SwFlyAtContentFrame*>(pFly);
+ pFlyAtContent->DelEmpty();
+ }
+ }
// table-in-footnote: delete empty footnote frames (like SwContentFrame::Cut)
else if (!pUp->Lower() && pUp->IsFootnoteFrame() && !pUp->IsColLocked())
{