officecfg/registry/data/org/openoffice/Office/UI/ImpressWindowState.xcu |   10 
+++
 sd/UIConfig_simpress.mk                                                 |    2 
 sd/inc/drawdoc.hxx                                                      |    2 
 sd/inc/sdpage.hxx                                                       |    4 
+
 sd/source/core/drawdoc2.cxx                                             |   17 
++++++
 sd/source/core/sdpage.cxx                                               |    2 
 sd/source/ui/slidesorter/controller/SlideSorterController.cxx           |   14 
++++-
 sd/source/ui/slidesorter/shell/SlideSorterViewShell.cxx                 |   27 
+++++++++-
 sd/source/ui/view/ViewClipboard.cxx                                     |    5 
+
 sd/source/ui/view/drviews7.cxx                                          |   11 
++++
 sd/source/ui/view/viewshe3.cxx                                          |    2 
 sd/uiconfig/simpress/popupmenu/pagepanecanvas.xml                       |   13 
++++
 sd/uiconfig/simpress/popupmenu/pagepanecanvasmaster.xml                 |   13 
++++
 13 files changed, 118 insertions(+), 4 deletions(-)

New commits:
commit ddf570af86d6399f03cbd397dd60186f0a8e85ed
Author:     Mohit Marathe <[email protected]>
AuthorDate: Tue Oct 21 10:55:32 2025 +0530
Commit:     Andras Timar <[email protected]>
CommitDate: Thu Nov 13 09:59:51 2025 +0100

    sd: make sure that canvas page remains as the last page
    
    This commit restricts the following operations:
    - duplicating canvas page
    - moving canvas page up
    - inserting a page after the canvas page
    - including canvas page in the slideshow
    
    Also, prevent deleting the only non-canvas page.
    
    Signed-off-by: Mohit Marathe <[email protected]>
    Change-Id: I8050dfc9836719f345e0f5349f25b62cd164344d
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/192769
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Michael Stahl <[email protected]>
    (cherry picked from commit b058526f16724d72f87810c438ac0cccb2ca16ff)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/193883
    Tested-by: Andras Timar <[email protected]>
    Reviewed-by: Andras Timar <[email protected]>

diff --git 
a/officecfg/registry/data/org/openoffice/Office/UI/ImpressWindowState.xcu 
b/officecfg/registry/data/org/openoffice/Office/UI/ImpressWindowState.xcu
index 8475ac8c9847..2532a49ea2e8 100644
--- a/officecfg/registry/data/org/openoffice/Office/UI/ImpressWindowState.xcu
+++ b/officecfg/registry/data/org/openoffice/Office/UI/ImpressWindowState.xcu
@@ -125,6 +125,11 @@
           <value xml:lang="en-US">Slide Sorter/Pane</value>
         </prop>
       </node>
+      <node oor:name="private:resource/popupmenu/pagepanecanvas" 
oor:op="replace">
+        <prop oor:name="UIName" oor:type="xs:string">
+          <value xml:lang="en-US">Slide Sorter/Pane (Canvas)</value>
+        </prop>
+      </node>
       <node oor:name="private:resource/popupmenu/pagepanenosel" 
oor:op="replace">
         <prop oor:name="UIName" oor:type="xs:string">
           <value xml:lang="en-US">Slide Sorter/Pane (no selection)</value>
@@ -135,6 +140,11 @@
           <value xml:lang="en-US">Master Slide Sorter/Pane</value>
         </prop>
       </node>
+      <node oor:name="private:resource/popupmenu/pagepanecanvasmaster" 
oor:op="replace">
+        <prop oor:name="UIName" oor:type="xs:string">
+          <value xml:lang="en-US">Master Slide Sorter/Pane (Canvas)</value>
+        </prop>
+      </node>
       <node oor:name="private:resource/popupmenu/pagepanenoselmaster" 
oor:op="replace">
         <prop oor:name="UIName" oor:type="xs:string">
           <value xml:lang="en-US">Master Slide Sorter/Pane (no 
selection)</value>
diff --git a/sd/UIConfig_simpress.mk b/sd/UIConfig_simpress.mk
index c70fc3bc9cb3..ad21ed0e48fb 100644
--- a/sd/UIConfig_simpress.mk
+++ b/sd/UIConfig_simpress.mk
@@ -37,9 +37,11 @@ $(eval $(call 
gb_UIConfig_add_popupmenufiles,modules/simpress,\
        sd/uiconfig/simpress/popupmenu/oleobject \
        sd/uiconfig/simpress/popupmenu/outline \
        sd/uiconfig/simpress/popupmenu/pagepanemaster \
+       sd/uiconfig/simpress/popupmenu/pagepanecanvasmaster \
        sd/uiconfig/simpress/popupmenu/pagepanenoselmaster \
        sd/uiconfig/simpress/popupmenu/pagepanenosel \
        sd/uiconfig/simpress/popupmenu/pagepane \
+       sd/uiconfig/simpress/popupmenu/pagepanecanvas \
        sd/uiconfig/simpress/popupmenu/pagetab \
        sd/uiconfig/simpress/popupmenu/page \
        sd/uiconfig/simpress/popupmenu/table \
diff --git a/sd/inc/drawdoc.hxx b/sd/inc/drawdoc.hxx
index b13749c6fe6d..f08c9cdcabbe 100644
--- a/sd/inc/drawdoc.hxx
+++ b/sd/inc/drawdoc.hxx
@@ -1032,7 +1032,7 @@ public:
         bool bIsPageObj,
         const sal_Int32 nInsertPosition);
 
-    SAL_DLLPRIVATE bool HasCanvasPage() const { return mpCanvasPage != 
nullptr; }
+    SAL_DLLPRIVATE bool HasCanvasPage() const { return mpCanvasPage.is(); }
 
     SAL_DLLPRIVATE sal_uInt16 GetOrInsertCanvasPage ();
 
diff --git a/sd/inc/sdpage.hxx b/sd/inc/sdpage.hxx
index f873efc819e8..a338a880ad2b 100644
--- a/sd/inc/sdpage.hxx
+++ b/sd/inc/sdpage.hxx
@@ -123,6 +123,7 @@ friend class sd::UndoAttrObject;
     sal_uInt16  mnPaperBin;               ///< PaperBin
     SdPageLink* mpPageLink;               ///< Page link (at left sides only)
     bool    mbIsCanvasPage;               ///< whether the page is a canvas 
page
+    bool    mbIsCanvasMasterPage;         ///< whether it is the master page 
of canvas page
 
     // PDF link annotations for read-only pdfium
     std::vector<std::pair<basegfx::B2DRectangle, OUString>> maLinkAnnotations;
@@ -405,6 +406,9 @@ public:
     bool IsCanvasPage() const { return mbIsCanvasPage; }
     void SetCanvasPage() { mbIsCanvasPage = true; }
 
+    bool IsCanvasMasterPage() const { return mbIsCanvasMasterPage; }
+    void SetCanvasMasterPage() { mbIsCanvasMasterPage = true; }
+
 private:
     bool mbIsPrecious;
 
diff --git a/sd/source/core/drawdoc2.cxx b/sd/source/core/drawdoc2.cxx
index a59e651094c3..a32499255b55 100644
--- a/sd/source/core/drawdoc2.cxx
+++ b/sd/source/core/drawdoc2.cxx
@@ -486,11 +486,16 @@ void SdDrawDocument::DeletePage(sal_uInt16 nPgNum)
 // Remove page
 rtl::Reference<SdrPage> SdDrawDocument::RemovePage(sal_uInt16 nPgNum)
 {
+    // Do not remove the only non-canvas page
+    if (HasCanvasPage() && GetSdPageCount(PageKind::Standard) == 2)
+        return nullptr;
     rtl::Reference<SdrPage> pPage = FmFormModel::RemovePage(nPgNum);
 
     bool bLast = ((nPgNum+1)/2 == (GetPageCount()+1)/2);
 
     auto pSdPage = static_cast<SdPage*>(pPage.get());
+    if (pSdPage->IsCanvasPage())
+        mpCanvasPage = nullptr;
     pSdPage->DisconnectLink();
     ReplacePageInCustomShows( pSdPage, nullptr );
     UpdatePageObjectsInNotes(nPgNum);
@@ -1221,6 +1226,8 @@ sal_uInt16 SdDrawDocument::CreatePage (
     bool bIsPageObj,
     const sal_Int32 nInsertPosition)
 {
+    if (pActualPage->IsCanvasPage())
+        return 0xffff;
     SdPage* pPreviousStandardPage;
     SdPage* pPreviousNotesPage;
     rtl::Reference<SdPage> pStandardPage;
@@ -1304,6 +1311,9 @@ sal_uInt16 SdDrawDocument::DuplicatePage (sal_uInt16 
nPageNum)
     // Get current page
     SdPage* pActualPage = GetSdPage(nPageNum, ePageKind);
 
+    if (pActualPage->IsCanvasPage())
+        return 0xffff;
+
     // Get background flags
     SdrLayerAdmin& rLayerAdmin = GetLayerAdmin();
     SdrLayerID aBckgrnd = rLayerAdmin.GetLayerID(sUNO_LayerName_background);
@@ -1327,6 +1337,8 @@ sal_uInt16 SdDrawDocument::DuplicatePage (
     bool bIsPageObj,
     const sal_Int32 nInsertPosition)
 {
+    if (pActualPage->IsCanvasPage())
+        return 0xffff;
     SdPage* pPreviousStandardPage;
     SdPage* pPreviousNotesPage;
     rtl::Reference<SdPage> pStandardPage;
@@ -1470,6 +1482,8 @@ sal_uInt16 SdDrawDocument::GetOrInsertCanvasPage()
     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)
+        return 0xffff;
 
     const Size aCanvasSize(500000, 500000);
 
@@ -1477,6 +1491,9 @@ sal_uInt16 SdDrawDocument::GetOrInsertCanvasPage()
     pCanvasPage->SetCanvasPage();
     mpCanvasPage = pCanvasPage;
 
+    SdPage* pMasterCanvas = 
static_cast<SdPage*>(&pCanvasPage->TRG_GetMasterPage());
+    pMasterCanvas->SetCanvasMasterPage();
+
     populatePagePreviewsGrid();
 
     return pCanvasPage->GetPageNum() / 2;
diff --git a/sd/source/core/sdpage.cxx b/sd/source/core/sdpage.cxx
index 386fc9bf930d..9df568ffb7fe 100644
--- a/sd/source/core/sdpage.cxx
+++ b/sd/source/core/sdpage.cxx
@@ -121,6 +121,8 @@ SdPage::SdPage(SdDrawDocument& rNewDoc, bool bMasterPage)
 ,   meCharSet(osl_getThreadTextEncoding())
 ,   mnPaperBin(PAPERBIN_PRINTER_SETTINGS)
 ,   mpPageLink(nullptr)
+,   mbIsCanvasPage(false)
+,   mbIsCanvasMasterPage(false)
 ,   mnTransitionType(0)
 ,   mnTransitionSubtype(0)
 ,   mbTransitionDirection(true)
diff --git a/sd/source/ui/slidesorter/controller/SlideSorterController.cxx 
b/sd/source/ui/slidesorter/controller/SlideSorterController.cxx
index 1589488dd6f8..9675ed1077ba 100644
--- a/sd/source/ui/slidesorter/controller/SlideSorterController.cxx
+++ b/sd/source/ui/slidesorter/controller/SlideSorterController.cxx
@@ -302,12 +302,22 @@ bool SlideSorterController::Command (
             if (mrModel.GetEditMode() == EditMode::Page)
             {
                 if (pPage != nullptr)
-                    aPopupId = "pagepane";
+                {
+                    if (pPage->IsCanvasPage())
+                        aPopupId = "pagepanecanvas";
+                    else
+                        aPopupId = "pagepane";
+                }
                 else
                     aPopupId = "pagepanenosel";
             }
             else if (pPage != nullptr)
-                aPopupId = "pagepanemaster";
+            {
+                if (pPage->IsCanvasMasterPage())
+                    aPopupId = "pagepanecanvasmaster";
+                else
+                    aPopupId = "pagepanemaster";
+            }
             else
                 aPopupId = "pagepanenoselmaster";
 
diff --git a/sd/source/ui/slidesorter/shell/SlideSorterViewShell.cxx 
b/sd/source/ui/slidesorter/shell/SlideSorterViewShell.cxx
index e4f7a34fd05d..ef62841fd98e 100644
--- a/sd/source/ui/slidesorter/shell/SlideSorterViewShell.cxx
+++ b/sd/source/ui/slidesorter/shell/SlideSorterViewShell.cxx
@@ -797,6 +797,13 @@ void SlideSorterViewShell::GetStateMovePageFirst 
(SfxItemSet& rSet)
         rSet.DisableItem( SID_MOVE_PAGE_FIRST );
         rSet.DisableItem( SID_MOVE_PAGE_UP );
     }
+
+    if (GetDoc()->HasCanvasPage())
+        if (firstSelectedPageNo == 
GetDoc()->GetSdPageCount(PageKind::Standard) - 1)
+        {
+            rSet.DisableItem( SID_MOVE_PAGE_FIRST );
+            rSet.DisableItem( SID_MOVE_PAGE_UP );
+        }
 }
 
 void SlideSorterViewShell::ExecMovePageUp (SfxRequest& /*rReq*/)
@@ -819,6 +826,10 @@ void SlideSorterViewShell::ExecMovePageUp (SfxRequest& 
/*rReq*/)
     if (firstSelectedPageNo == 0)
         return;
 
+    if (GetDoc()->HasCanvasPage())
+        if (firstSelectedPageNo == 
GetDoc()->GetSdPageCount(PageKind::Standard) - 1)
+            return;
+
     // Move pages before firstSelectedPageNo - 1 (so after firstSelectedPageNo 
- 2),
     // remembering that -1 means at first, which is good.
     GetDoc()->MoveSelectedPages( firstSelectedPageNo - 2 );
@@ -848,6 +859,9 @@ void SlideSorterViewShell::ExecMovePageDown (SfxRequest& 
/*rReq*/)
     lastSelectedPageNo = (lastSelectedPageNo - 1) / 2;
     if (lastSelectedPageNo == nNoOfPages - 1)
         return;
+    if (GetDoc()->HasCanvasPage())
+        if (lastSelectedPageNo == nNoOfPages - 2)
+            return;
 
     // Move to position after lastSelectedPageNo
     GetDoc()->MoveSelectedPages( lastSelectedPageNo + 1 );
@@ -874,7 +888,10 @@ void SlideSorterViewShell::ExecMovePageLast (SfxRequest& 
/*rReq*/)
     sal_uInt16 nNoOfPages = GetDoc()->GetSdPageCount(PageKind::Standard);
 
     // Move to position after last page No (=Number of pages - 1)
-    GetDoc()->MoveSelectedPages( nNoOfPages - 1 );
+    if (!GetDoc()->HasCanvasPage())
+        GetDoc()->MoveSelectedPages( nNoOfPages - 1 );
+    else
+        GetDoc()->MoveSelectedPages( nNoOfPages - 2 );
 
     PostMoveSlidesActions(xSelection);
 }
@@ -906,6 +923,14 @@ void SlideSorterViewShell::GetStateMovePageLast 
(SfxItemSet& rSet)
         rSet.DisableItem( SID_MOVE_PAGE_LAST );
         rSet.DisableItem( SID_MOVE_PAGE_DOWN );
     }
+    if (GetDoc()->HasCanvasPage())
+    {
+        if (lastSelectedPageNo == nNoOfPages - 2)
+        {
+            rSet.DisableItem( SID_MOVE_PAGE_LAST );
+            rSet.DisableItem( SID_MOVE_PAGE_DOWN );
+        }
+    }
 }
 
 void SlideSorterViewShell::PostMoveSlidesActions(const 
std::shared_ptr<SlideSorterViewShell::PageSelection> &rpSelection)
diff --git a/sd/source/ui/view/ViewClipboard.cxx 
b/sd/source/ui/view/ViewClipboard.cxx
index 66af08ba2bb8..14adb70ca32e 100644
--- a/sd/source/ui/view/ViewClipboard.cxx
+++ b/sd/source/ui/view/ViewClipboard.cxx
@@ -172,6 +172,11 @@ sal_uInt16 ViewClipboard::DetermineInsertPosition  ()
         if( pPage->IsSelected() )
             nInsertPos = nPage * 2 + 3;
     }
+    if (rDoc.HasCanvasPage())
+    {
+        if (nInsertPos == rDoc.GetPageCount())
+            nInsertPos = rDoc.GetPageCount() - 2;
+    }
 
     return nInsertPos;
 }
diff --git a/sd/source/ui/view/drviews7.cxx b/sd/source/ui/view/drviews7.cxx
index 8857bab914b2..be89ec0e5c5e 100644
--- a/sd/source/ui/view/drviews7.cxx
+++ b/sd/source/ui/view/drviews7.cxx
@@ -850,6 +850,17 @@ void DrawViewShell::GetMenuState( SfxItemSet &rSet )
         rSet.DisableItem (SID_DELETE_MASTER_PAGE);
         rSet.DisableItem (SID_RENAME_MASTER_PAGE);
         rSet.DisableItem (SID_CLOSE_MASTER_VIEW);
+
+        if (mpActualPage->IsCanvasPage())
+        {
+            rSet.DisableItem(SID_INSERTPAGE);
+            rSet.DisableItem(SID_INSERTPAGE_QUICK);
+            rSet.DisableItem(SID_INSERTFILE);
+            rSet.DisableItem(SID_DUPLICATE_PAGE);
+            rSet.DisableItem(SID_PRESENTATION_LAYOUT);
+            rSet.DisableItem(SID_HIDE_SLIDE);
+            rSet.DisableItem(SID_SHOW_SLIDE);
+        }
     }
     else
     {
diff --git a/sd/source/ui/view/viewshe3.cxx b/sd/source/ui/view/viewshe3.cxx
index a722aef10fe3..c60a894b25e0 100644
--- a/sd/source/ui/view/viewshe3.cxx
+++ b/sd/source/ui/view/viewshe3.cxx
@@ -164,6 +164,8 @@ SdPage* ViewShell::CreateOrDuplicatePage (
     SdPage* pPage,
     const sal_Int32 nInsertPosition)
 {
+    if (pPage->IsCanvasPage())
+        return nullptr;
     sal_uInt16 nSId = rRequest.GetSlot();
     SdDrawDocument* pDocument = GetDoc();
     SdrLayerAdmin& rLayerAdmin = pDocument->GetLayerAdmin();
diff --git a/sd/uiconfig/simpress/popupmenu/pagepanecanvas.xml 
b/sd/uiconfig/simpress/popupmenu/pagepanecanvas.xml
new file mode 100644
index 000000000000..0cff10845b42
--- /dev/null
+++ b/sd/uiconfig/simpress/popupmenu/pagepanecanvas.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+-->
+<menu:menupopup xmlns:menu="http://openoffice.org/2001/menu";>
+  <menu:menuitem menu:id=".uno:RenameSlide"/>
+  <menu:menuitem menu:id=".uno:DeleteSlide"/>
+</menu:menupopup>
diff --git a/sd/uiconfig/simpress/popupmenu/pagepanecanvasmaster.xml 
b/sd/uiconfig/simpress/popupmenu/pagepanecanvasmaster.xml
new file mode 100644
index 000000000000..c6d0366c333f
--- /dev/null
+++ b/sd/uiconfig/simpress/popupmenu/pagepanecanvasmaster.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+-->
+<menu:menupopup xmlns:menu="http://openoffice.org/2001/menu";>
+  <menu:menuitem menu:id=".uno:DeleteMasterPage"/>
+  <menu:menuitem menu:id=".uno:RenameMasterPage"/>
+</menu:menupopup>

Reply via email to