officecfg/registry/data/org/openoffice/Office/UI/DrawImpressCommands.xcu | 8 + sd/inc/app.hrc | 1 sd/inc/drawdoc.hxx | 4 sd/sdi/_drvwsh.sdi | 5 sd/sdi/sdraw.sdi | 17 ++ sd/source/core/drawdoc2.cxx | 79 +++++++++- sd/source/ui/view/drviews2.cxx | 10 + 7 files changed, 123 insertions(+), 1 deletion(-)
New commits: commit b7992d7a8f0a6ea747ff76e25582de259a4e2868 Author: Mohit Marathe <[email protected]> AuthorDate: Fri Oct 24 12:34:55 2025 +0530 Commit: Michael Stahl <[email protected]> CommitDate: Thu Nov 6 19:00:56 2025 +0100 sd: add support for shuffling slides from canvas page added .uno:ReshufflePages which when executed will re-order the pages based on the position of their previews on the canvas page. Sorting logic: 1. Sort the previews based on their y-center 2. Form a row if the y-centers are within a tolerance limit 3. Sort the row based on their x-center (also considering RTL locale) 4. Repeat for each rows Signed-off-by: Mohit Marathe <[email protected]> Change-Id: If654e0cc14686aed2ad6cd69f2dc81249924ee2c Reviewed-on: https://gerrit.libreoffice.org/c/core/+/192940 Reviewed-by: Michael Stahl <[email protected]> Tested-by: Jenkins CollaboraOffice <[email protected]> diff --git a/officecfg/registry/data/org/openoffice/Office/UI/DrawImpressCommands.xcu b/officecfg/registry/data/org/openoffice/Office/UI/DrawImpressCommands.xcu index 3b53ffb9e7e1..65dafd620765 100644 --- a/officecfg/registry/data/org/openoffice/Office/UI/DrawImpressCommands.xcu +++ b/officecfg/registry/data/org/openoffice/Office/UI/DrawImpressCommands.xcu @@ -2447,6 +2447,14 @@ <value>1</value> </prop> </node> + <node oor:name=".uno:ReshufflePages" oor:op="replace"> + <prop oor:name="Label" oor:type="xs:string"> + <value xml:lang="en-US">Re-order pages</value> + </prop> + <prop oor:name="Properties" oor:type="xs:int"> + <value>1</value> + </prop> + </node> <node oor:name=".uno:AssignLayout?WhatLayout:long=20" oor:op="replace"> <prop oor:name="Label" oor:type="xs:string"> <value xml:lang="en-US">Blank Slide</value> diff --git a/sd/inc/app.hrc b/sd/inc/app.hrc index 16433cd14ec8..1b63519f326e 100644 --- a/sd/inc/app.hrc +++ b/sd/inc/app.hrc @@ -377,6 +377,7 @@ // FREE #define SID_INSERTPAGE_QUICK (SID_SD_START+352) #define SID_INSERT_CANVAS_SLIDE (SID_SD_START+353) +#define SID_SHUFFLE_PAGES (SID_SD_START+354) // FREE #define SID_INSERT_FLD_PAGE_TITLE (SID_SD_START+356) #define SID_INSERT_FLD_DATE_VAR (SID_SD_START+357) diff --git a/sd/inc/drawdoc.hxx b/sd/inc/drawdoc.hxx index f08c9cdcabbe..46cf1007113c 100644 --- a/sd/inc/drawdoc.hxx +++ b/sd/inc/drawdoc.hxx @@ -1100,6 +1100,10 @@ public: bool bUndo = true, const OUString& sNewName = OUString()); + /** Re-order the pages based on the position of their previews on canvas page. + */ + void ReshufflePages(); + private: void UpdatePageRelativeURLsImpl(const std::function<void(const SvxFieldItem & rFieldItem, editeng::SvxFieldItemUpdater& rFieldItemUpdater)>& rItemCallback); diff --git a/sd/sdi/_drvwsh.sdi b/sd/sdi/_drvwsh.sdi index d934a9f12b10..d5e9fb31cfd9 100644 --- a/sd/sdi/_drvwsh.sdi +++ b/sd/sdi/_drvwsh.sdi @@ -165,6 +165,11 @@ interface DrawView ExecMethod = FuTemporary ; StateMethod = GetMenuState ; ] + SID_SHUFFLE_PAGES + [ + ExecMethod = FuTemporary ; + StateMethod = GetMenuState ; + ] SID_HYPERLINK_SETLINK // ole : no, status : no [ ExecMethod = FuTemporary ; diff --git a/sd/sdi/sdraw.sdi b/sd/sdi/sdraw.sdi index 819ff5f31cd0..aa3dafb09349 100644 --- a/sd/sdi/sdraw.sdi +++ b/sd/sdi/sdraw.sdi @@ -2246,6 +2246,23 @@ SfxVoidItem InsertCanvasSlide SID_INSERT_CANVAS_SLIDE GroupId = SfxGroupId::Insert; ] +SfxVoidItem ReshufflePages SID_SHUFFLE_PAGES +() +[ + AutoUpdate = FALSE, + FastCall = FALSE, + ReadOnlyDoc = FALSE, + Toggle = FALSE, + Container = FALSE, + RecordAbsolute = FALSE, + RecordPerSet; + + AccelConfig = TRUE, + MenuConfig = TRUE, + ToolBoxConfig = TRUE, + GroupId = SfxGroupId::Modify; +] + SfxVoidItem InsertPageField SID_INSERT_FLD_PAGE () [ diff --git a/sd/source/core/drawdoc2.cxx b/sd/source/core/drawdoc2.cxx index a32499255b55..786932b8bf04 100644 --- a/sd/source/core/drawdoc2.cxx +++ b/sd/source/core/drawdoc2.cxx @@ -17,6 +17,7 @@ * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ +#include <algorithm> #include <vcl/settings.hxx> #include <sal/log.hxx> @@ -1471,6 +1472,79 @@ void SdDrawDocument::SetupNewPage ( } } +void SdDrawDocument::ReshufflePages() +{ + SdrObjList* pObjList = mpCanvasPage.get(); + std::vector<SdrPageObj*> aPageOrder; + + SdrObjListIter aIter(pObjList, SdrIterMode::Flat); + for (SdrObject* pObj = aIter.Next(); pObj; pObj = aIter.Next()) + { + if (pObj->GetObjIdentifier() == SdrObjKind::Page) + { + SdrPageObj* pPageObj = static_cast<SdrPageObj*>(pObj); + aPageOrder.push_back(pPageObj); + } + } + + // Sort the previews by comparing their y-center (compare x-center as a tie-breaker) + std::sort(aPageOrder.begin(), aPageOrder.end(), + [](const SdrPageObj* pObj1, const SdrPageObj* pObj2) { + const ::tools::Rectangle& rect1 = pObj1->GetSnapRect(); + const ::tools::Rectangle& rect2 = pObj2->GetSnapRect(); + const ::tools::Long yCenter1 = (rect1.Top() + rect1.Bottom()) / 2; + const ::tools::Long yCenter2 = (rect2.Top() + rect2.Bottom()) / 2; + + if (yCenter1 != yCenter2) + return yCenter1 < yCenter2; + + const ::tools::Long xCenter1 = (rect1.Left() + rect1.Right()) / 2; + const ::tools::Long xCenter2 = (rect2.Left() + rect2.Right()) / 2; + return xCenter1 < xCenter2; + } + ); + + const bool bIsRTL = AllSettings::GetLayoutRTL(); + + // Form a row, and sort them based on their x-center + for (size_t rowStart = 0; rowStart < aPageOrder.size();) + { + const ::tools::Rectangle& rect0 = aPageOrder[rowStart]->GetSnapRect(); + const ::tools::Long top = rect0.Top(); + const ::tools::Long bottom = rect0.Bottom(); + + size_t rowEnd = rowStart; + while (rowEnd < aPageOrder.size()) + { + const ::tools::Rectangle& rect = aPageOrder[rowEnd]->GetSnapRect(); + const ::tools::Long yCenter = (rect.Top() + rect.Bottom()) / 2; + + if (yCenter < top || yCenter > bottom) + break; + rowEnd++; + } + + std::stable_sort(aPageOrder.begin() + rowStart, aPageOrder.begin() + rowEnd, + [&bIsRTL](const SdrPageObj* pObj1, const SdrPageObj* pObj2) { + const ::tools::Rectangle& rect1 = pObj1->GetSnapRect(); + const ::tools::Rectangle& rect2 = pObj2->GetSnapRect(); + const ::tools::Long xCenter1 = (rect1.Left() + rect1.Right()) / 2; + const ::tools::Long xCenter2 = (rect2.Left() + rect2.Right()) / 2; + + return bIsRTL ? (xCenter1 > xCenter2) : (xCenter1 < xCenter2); + }); + } + + for (size_t i = 0; i < aPageOrder.size(); i++) + { + SdPage* pPage = static_cast<SdPage*>(aPageOrder[i]->GetReferencedPage()); + sal_uInt16 nCurrentPageNum = pPage->GetPageNum(); + sal_uInt16 nTargetPageNum = 2 * i + 1; + MovePage(nCurrentPageNum, nTargetPageNum); // Standard page + MovePage(nCurrentPageNum + 1, nTargetPageNum + 1); // Notes page + } +} + sal_uInt16 SdDrawDocument::GetOrInsertCanvasPage() { if (HasCanvasPage()) @@ -1479,7 +1553,10 @@ sal_uInt16 SdDrawDocument::GetOrInsertCanvasPage() sal_uInt16 nLastPageNum = GetSdPageCount(PageKind::Standard); SdPage* pLastStandardPage = GetSdPage(nLastPageNum - 1, PageKind::Standard); - sal_uInt16 nCanvasPageNum = CreatePage(pLastStandardPage, PageKind::Standard, u"Canvas Page"_ustr, u"Canvas notes page"_ustr, AutoLayout::AUTOLAYOUT_NONE, AutoLayout::AUTOLAYOUT_NONE, false, true, pLastStandardPage->GetPageNum() + 2); + sal_uInt16 nCanvasPageNum = CreatePage(pLastStandardPage, PageKind::Standard, + u"Canvas Page"_ustr, u"Canvas notes page"_ustr, + AutoLayout::AUTOLAYOUT_NONE, AutoLayout::AUTOLAYOUT_NONE, + false, true, pLastStandardPage->GetPageNum() + 2); SdPage* pCanvasPage = GetSdPage(nCanvasPageNum, PageKind::Standard); if (!pCanvasPage) diff --git a/sd/source/ui/view/drviews2.cxx b/sd/source/ui/view/drviews2.cxx index 47264a5f0b20..4e4b2bb3dba7 100644 --- a/sd/source/ui/view/drviews2.cxx +++ b/sd/source/ui/view/drviews2.cxx @@ -1402,6 +1402,16 @@ void DrawViewShell::FuTemporary(SfxRequest& rReq) } break; + case SID_SHUFFLE_PAGES: + { + if (!GetDoc()->HasCanvasPage()) + break; + GetDoc()->ReshufflePages(); + Cancel(); + rReq.Done(); + } + break; + case SID_DUPLICATE_PAGE: { auto slideSorter = sd::slidesorter::SlideSorterViewShell::GetSlideSorter(GetViewShellBase());
