View Issue Details

IDProjectCategoryView StatusLast Update
0002646ScribusGeneralpublic2026-08-27 23:30
Reporterchristoph_s Assigned To 
PrioritynormalSeverityfeatureReproducibilityalways
Status assignedResolutionopen 
Product Version1.3.2cvs 
Summary0002646: Mirror master pages
DescriptionIt would be very useful to have a feature that allows for "mirroring" a master page. Currently, I have to create master pages with guides for left and right pages seperately, always using a calculator to find the correct ratios for guides etc. The ability to mirror a left page to a right page would save a lot of time.
Tags#patch_to_be_reviewed, #please_test
Patch

Relationships

related to 0008732 closed Add two more buttons in the Guides Manager (Apply to Even Pages" and "Apply to Odd Pages") 
child of 0003837 acknowledged Metabug: Master pages/Page templates 

Activities

qirat

2026-08-26 04:32

reporter   ~0054304

Test this please. Two patches.

mp1 — Soft refactor / preparation
mp2 — Mirror master-page layout

More details in the pdf report.

qirat

2026-08-26 07:25

reporter   ~0054305

Order: mp1, mp2
mp2-mirror-master-page-layout-v1.03.patch (11,608 bytes)   
Index: scribus/masterpageoperations.h
===================================================================
--- scribus/masterpageoperations.h	(revision 27785)
+++ scribus/masterpageoperations.h	(working copy)
@@ -17,7 +17,7 @@
 bool isReservedName(const QString& name);
 QString nonReservedName(const QString& name);
 ScPage* duplicateMasterPage(ScribusDoc* doc, const QString& sourceName, const QString& destinationName,
-                            int pagePosition = -1);
+                            int pagePosition = -1, bool mirrorLayout = false);
 }
 
 #endif
Index: scribus/masterpageoperations.cpp
===================================================================
--- scribus/masterpageoperations.cpp	(revision 27785)
+++ scribus/masterpageoperations.cpp	(working copy)
@@ -6,9 +6,14 @@
 */
 #include "masterpageoperations.h"
 
+#include <algorithm>
+
+#include <QList>
+#include <QRectF>
 #include <QStringList>
 
 #include "commonstrings.h"
+#include "guidemanagercore.h"
 #include "pageitem.h"
 #include "pagestructs.h"
 #include "scpage.h"
@@ -48,7 +53,8 @@
 	}
 }
 
-void copyMasterItems(ScribusDoc* doc, int sourcePageIndex, ScPage* destination, const QString& destinationName)
+QList<PageItem*> copyMasterItems(ScribusDoc* doc, int sourcePageIndex, ScPage* destination,
+                                 const QString& destinationName)
 {
 	const int firstNewItem = doc->MasterItems.count();
 	Selection selection(doc, false);
@@ -70,10 +76,87 @@
 		}
 	}
 
+	QList<PageItem*> copiedItems;
+	copiedItems.reserve(doc->MasterItems.count() - firstNewItem);
 	const int destinationPageIndex = doc->MasterNames.value(destinationName);
 	for (int i = firstNewItem; i < doc->MasterItems.count(); ++i)
-		doc->MasterItems.at(i)->setMasterPage(destinationPageIndex, destinationName);
-}
+	{
+		PageItem* item = doc->MasterItems.at(i);
+		item->setMasterPage(destinationPageIndex, destinationName);
+		copiedItems.append(item);
+	}
+	return copiedItems;
+}
+
+void copyMirroredInitialMargins(const ScPage* source, ScPage* destination)
+{
+	const bool sourceIsLeftPage = source->LeftPg == 1;
+	const bool destinationIsLeftPage = destination->LeftPg == 1;
+	const double sourcePhysicalLeft = sourceIsLeftPage ?
+		source->initialMargins.right() : source->initialMargins.left();
+	const double sourcePhysicalRight = sourceIsLeftPage ?
+		source->initialMargins.left() : source->initialMargins.right();
+	const double destinationPhysicalLeft = sourcePhysicalRight;
+	const double destinationPhysicalRight = sourcePhysicalLeft;
+
+	destination->initialMargins.setTop(source->initialMargins.top());
+	destination->initialMargins.setBottom(source->initialMargins.bottom());
+	destination->initialMargins.setLeft(destinationIsLeftPage ? destinationPhysicalRight : destinationPhysicalLeft);
+	destination->initialMargins.setRight(destinationIsLeftPage ? destinationPhysicalLeft : destinationPhysicalRight);
+}
+
+Guides mirroredVerticalGuides(const Guides& guides, double pageWidth)
+{
+	Guides mirrored;
+	mirrored.reserve(guides.size());
+	for (double guide : guides)
+		mirrored.append(pageWidth - guide);
+	std::sort(mirrored.begin(), mirrored.end());
+	return mirrored;
+}
+
+void copyGuides(const ScPage* source, ScPage* destination, bool mirrorLayout)
+{
+	if (!mirrorLayout)
+	{
+		source->guides.copy(&destination->guides);
+		return;
+	}
+
+	const double pageWidth = destination->width();
+	destination->guides.addHorizontals(source->guides.horizontals(GuideManagerCore::Standard),
+	                                   GuideManagerCore::Standard);
+	destination->guides.addVerticals(mirroredVerticalGuides(
+		source->guides.verticals(GuideManagerCore::Standard), pageWidth), GuideManagerCore::Standard);
+
+	destination->guides.setHorizontalAutoCount(source->guides.horizontalAutoCount());
+	destination->guides.setVerticalAutoCount(source->guides.verticalAutoCount());
+	destination->guides.setHorizontalAutoGap(source->guides.horizontalAutoGap());
+	destination->guides.setVerticalAutoGap(source->guides.verticalAutoGap());
+	destination->guides.setHorizontalAutoRefer(source->guides.horizontalAutoRefer());
+	destination->guides.setVerticalAutoRefer(source->guides.verticalAutoRefer());
+	destination->guides.addHorizontals(source->guides.horizontals(GuideManagerCore::Auto),
+	                                   GuideManagerCore::Auto);
+	destination->guides.addVerticals(mirroredVerticalGuides(
+		source->guides.verticals(GuideManagerCore::Auto), pageWidth), GuideManagerCore::Auto);
+
+	destination->guides.gy = source->guides.gy;
+	destination->guides.gw = source->guides.gw;
+	destination->guides.gh = source->guides.gh;
+	if (source->guides.gx != 0.0 || source->guides.gw != 0.0)
+		destination->guides.gx = pageWidth - source->guides.gx - source->guides.gw;
+}
+
+void mirrorItemPositions(const QList<PageItem*>& items, const ScPage* destination)
+{
+	const double pageCenter = destination->xOffset() + destination->width() / 2.0;
+	for (PageItem* item : items)
+	{
+		const QRectF bounds = item->getBoundingRect();
+		item->moveBy(2.0 * (pageCenter - bounds.center().x()), 0.0, true);
+	}
+}
+
 }
 
 bool MasterPageOperations::isReservedName(const QString& name)
@@ -101,7 +184,8 @@
 }
 
 ScPage* MasterPageOperations::duplicateMasterPage(ScribusDoc* doc, const QString& sourceName,
-                                                    const QString& destinationName, int pagePosition)
+                                                    const QString& destinationName, int pagePosition,
+                                                    bool mirrorLayout)
 {
 	if (!doc || !doc->MasterNames.contains(sourceName) || destinationName.isEmpty() ||
 	    doc->MasterNames.contains(destinationName))
@@ -117,9 +201,15 @@
 	if (pagePosition >= 0)
 		destination->LeftPg = pagePosition;
 
-	copyInitialMargins(doc, source, destination);
-	copyMasterItems(doc, sourcePageIndex, destination, destinationName);
-	source->guides.copy(&destination->guides);
+	if (mirrorLayout)
+		copyMirroredInitialMargins(source, destination);
+	else
+		copyInitialMargins(doc, source, destination);
+
+	const auto copiedItems = copyMasterItems(doc, sourcePageIndex, destination, destinationName);
+	if (mirrorLayout)
+		mirrorItemPositions(copiedItems, destination);
+	copyGuides(source, destination, mirrorLayout);
 	doc->GroupCounter = groupCounter + 1;
 	return destination;
 }
Index: scribus/ui/newtemp.h
===================================================================
--- scribus/ui/newtemp.h	(revision 27785)
+++ scribus/ui/newtemp.h	(working copy)
@@ -8,6 +8,7 @@
 #define QUERY2_H
 
 #include <QDialog>
+class QCheckBox;
 class QHBoxLayout;
 class QVBoxLayout;
 class QLineEdit;
@@ -23,8 +24,11 @@
 	Q_OBJECT
 
 public:
-	NewTm(QWidget* parent, const QString& text, const QString& title, ScribusDoc* doc, const QString& answerText = QString());
-	~NewTm() {};
+	NewTm(QWidget* parent, const QString& text, const QString& title, ScribusDoc* doc,
+	      const QString& answerText = QString(), bool showMirrorLayout = false);
+	~NewTm() override = default;
+
+	bool mirrorLayout() const;
 
 	QPushButton* PushButton1;
 	QPushButton* PushButton2;
@@ -37,6 +41,9 @@
 	QHBoxLayout* Layout3;
 	QHBoxLayout* Layout2;
 	QHBoxLayout* Layout1;
+
+private:
+	QCheckBox* m_mirrorLayoutCheckBox { nullptr };
 };
 
 #endif // QUERY_H
Index: scribus/ui/newtemp.cpp
===================================================================
--- scribus/ui/newtemp.cpp	(revision 27785)
+++ scribus/ui/newtemp.cpp	(working copy)
@@ -6,7 +6,7 @@
 */
 #include "newtemp.h"
 
-#include <QPixmap>
+#include <QCheckBox>
 #include <QSpacerItem>
 #include <QHBoxLayout>
 #include <QVBoxLayout>
@@ -21,8 +21,9 @@
 #include "scpage.h"
 #include "scribusdoc.h"
 
-NewTm::NewTm( QWidget* parent, const QString& text, const QString& title, ScribusDoc *doc, const QString& answerText)
-		: QDialog( parent )
+NewTm::NewTm(QWidget* parent, const QString& text, const QString& title, ScribusDoc* doc,
+             const QString& answerText, bool showMirrorLayout)
+	: QDialog(parent)
 {
 	setModal(true);
 	setWindowTitle(title);
@@ -49,22 +50,25 @@
 		Layout3->setSpacing(6);
 		Layout3->setContentsMargins(0, 0, 0, 0);
 		Links = new QComboBox( this );
-		QStringList::Iterator pNames;
-		QList<PageSet> pageSet(doc->pageSets());
-		for (pNames = pageSet[doc->pagePositioning()].pageNames.begin(); pNames != pageSet[doc->pagePositioning()].pageNames.end(); ++pNames )
-		{
-			//Links->insertItem((*pNames));
-			Links->addItem(CommonStrings::translatePageSetLocString((*pNames)));
-		}
+		const PageSet& pageSet = doc->pageSets().at(doc->pagePositioning());
+		for (const QString& pageName : pageSet.pageNames)
+			Links->addItem(CommonStrings::translatePageSetLocString(pageName));
 		if (doc->currentPage()->LeftPg == 1)
 			Links->setCurrentIndex(0);
 		else if (doc->currentPage()->LeftPg == 0)
-			Links->setCurrentIndex(Links->count()-1);
+			Links->setCurrentIndex(Links->count() - 1);
 		else
-			Links->setCurrentIndex(doc->currentPage()->LeftPg-1);
+			Links->setCurrentIndex(doc->currentPage()->LeftPg - 1);
 		Links->setEditable(false);
 		Layout3->addWidget( Links );
 		QueryLayout->addLayout( Layout3 );
+	}
+	if (showMirrorLayout)
+	{
+		m_mirrorLayoutCheckBox = new QCheckBox(tr("&Mirror layout horizontally"), this);
+		m_mirrorLayoutCheckBox->setToolTip(
+			tr("Mirror margins, guides, and item positions on the duplicated master page."));
+		QueryLayout->addWidget(m_mirrorLayoutCheckBox);
 	}
 	Layout1 = new QHBoxLayout;
 	Layout1->setSpacing(6);
@@ -86,7 +90,11 @@
 	setMinimumSize(sizeHint());
 	Answer->setFocus();
 
-	// signals and slots connections
-	connect( PushButton1, SIGNAL( clicked() ), this, SLOT( accept() ) );
-	connect( PushButton2, SIGNAL( clicked() ), this, SLOT( reject() ) );
+	connect(PushButton1, &QPushButton::clicked, this, &QDialog::accept);
+	connect(PushButton2, &QPushButton::clicked, this, &QDialog::reject);
 }
+
+bool NewTm::mirrorLayout() const
+{
+	return m_mirrorLayoutCheckBox && m_mirrorLayoutCheckBox->isChecked();
+}
Index: scribus/ui/pagepalette_masterpages.cpp
===================================================================
--- scribus/ui/pagepalette_masterpages.cpp	(revision 27785)
+++ scribus/ui/pagepalette_masterpages.cpp	(working copy)
@@ -204,14 +204,15 @@
 void PagePalette_MasterPages::duplicateMasterPage()
 {
 	QString name = getUniqueName(m_currentPage, m_doc->MasterNames);
-	QScopedPointer<NewTm> dia(new NewTm(this, tr("&Name:"), tr("New Master Page"), m_doc, name));
-	if (!dia->exec())
+	NewTm dia(this, tr("&Name:"), tr("Duplicate Master Page"), m_doc, name,
+	          m_doc->pagePositioning() == doublePage);
+	if (!dia.exec())
 		return;
 
 	if (m_doc->appMode == modeEditClip)
 		m_view->requestMode(submodeEndNodeEdit);
 
-	name = dia->Answer->text();
+	name = dia.Answer->text();
 	if (name.isEmpty())
 		return;
 
@@ -221,16 +222,17 @@
 	int pagePosition = -1;
 	if (m_doc->pagePositioning() != singlePage)
 	{
-		pagePosition = dia->Links->currentIndex();
+		pagePosition = dia.Links->currentIndex();
 		if (pagePosition == 0)
 			pagePosition = 1;
-		else if (pagePosition == static_cast<int>(dia->Links->count() - 1))
+		else if (pagePosition == static_cast<int>(dia.Links->count() - 1))
 			pagePosition = 0;
 		else
 			++pagePosition;
 	}
 
-	if (!MasterPageOperations::duplicateMasterPage(m_doc, m_currentPage, name, pagePosition))
+	if (!MasterPageOperations::duplicateMasterPage(
+		m_doc, m_currentPage, name, pagePosition, dia.mirrorLayout()))
 		return;
 
 	updateMasterPageList(name);
mp1-extract-master-page-duplication-v1.03.patch (14,879 bytes)   
Index: scribus/CMakeLists_Sources.txt
===================================================================
--- scribus/CMakeLists_Sources.txt	(revision 27785)
+++ scribus/CMakeLists_Sources.txt	(working copy)
@@ -118,6 +118,7 @@
 	loadsaveplugin.cpp
 	localemgr.cpp
 	marks.cpp
+	masterpageoperations.cpp
 	menumanager.cpp
 	mesh.cpp
 	navigator.cpp
Index: scribus/masterpageoperations.h
===================================================================
--- scribus/masterpageoperations.h	(nonexistent)
+++ scribus/masterpageoperations.h	(working copy)
@@ -0,0 +1,23 @@
+/*
+For general Scribus (>=1.3.2) copyright and licensing information please refer
+to the COPYING file provided with the program. Following this notice may exist
+a copyright and/or license notice that predates the release of Scribus 1.3.2
+for which a new license (GPL+exception) is in place.
+*/
+#ifndef MASTERPAGEOPERATIONS_H
+#define MASTERPAGEOPERATIONS_H
+
+#include <QString>
+
+class ScPage;
+class ScribusDoc;
+
+namespace MasterPageOperations
+{
+bool isReservedName(const QString& name);
+QString nonReservedName(const QString& name);
+ScPage* duplicateMasterPage(ScribusDoc* doc, const QString& sourceName, const QString& destinationName,
+                            int pagePosition = -1);
+}
+
+#endif
Index: scribus/masterpageoperations.cpp
===================================================================
--- scribus/masterpageoperations.cpp	(nonexistent)
+++ scribus/masterpageoperations.cpp	(working copy)
@@ -0,0 +1,125 @@
+/*
+For general Scribus (>=1.3.2) copyright and licensing information please refer
+to the COPYING file provided with the program. Following this notice may exist
+a copyright and/or license notice that predates the release of Scribus 1.3.2
+for which a new license (GPL+exception) is in place.
+*/
+#include "masterpageoperations.h"
+
+#include <QStringList>
+
+#include "commonstrings.h"
+#include "pageitem.h"
+#include "pagestructs.h"
+#include "scpage.h"
+#include "scribusdoc.h"
+#include "scribusXml.h"
+#include "selection.h"
+#include "util.h"
+
+namespace
+{
+void copyInitialMargins(ScribusDoc* doc, const ScPage* source, ScPage* destination)
+{
+	destination->initialMargins.setTop(source->initialMargins.top());
+	destination->initialMargins.setBottom(source->initialMargins.bottom());
+	if (doc->pageSets()[doc->pagePositioning()].Columns == 1)
+	{
+		destination->initialMargins.setLeft(source->initialMargins.left());
+		destination->initialMargins.setRight(source->initialMargins.right());
+	}
+	else if (destination->LeftPg != source->LeftPg)
+	{
+		if (destination->LeftPg > 1)
+		{
+			destination->initialMargins.setRight(source->initialMargins.left());
+			destination->initialMargins.setLeft(source->initialMargins.left());
+		}
+		else
+		{
+			destination->initialMargins.setLeft(source->initialMargins.left());
+			destination->initialMargins.setRight(source->initialMargins.right());
+		}
+	}
+	else
+	{
+		destination->initialMargins.setLeft(source->initialMargins.left());
+		destination->initialMargins.setRight(source->initialMargins.right());
+	}
+}
+
+void copyMasterItems(ScribusDoc* doc, int sourcePageIndex, ScPage* destination, const QString& destinationName)
+{
+	const int firstNewItem = doc->MasterItems.count();
+	Selection selection(doc, false);
+
+	for (auto layer = doc->Layers.begin(); layer != doc->Layers.end(); ++layer)
+	{
+		for (int i = 0; i < firstNewItem; ++i)
+		{
+			PageItem* item = doc->MasterItems.at(i);
+			if (item->OwnPage == sourcePageIndex && item->m_layerID == layer->ID)
+				selection.addItem(item);
+		}
+		if (!selection.isEmpty())
+		{
+			ScriXmlDoc xml;
+			const QString buffer = xml.writeElem(doc, &selection);
+			xml.readElemToLayer(buffer, doc, destination->xOffset(), destination->yOffset(), false, true, layer->ID);
+			selection.clear();
+		}
+	}
+
+	const int destinationPageIndex = doc->MasterNames.value(destinationName);
+	for (int i = firstNewItem; i < doc->MasterItems.count(); ++i)
+		doc->MasterItems.at(i)->setMasterPage(destinationPageIndex, destinationName);
+}
+}
+
+bool MasterPageOperations::isReservedName(const QString& name)
+{
+	return name == CommonStrings::masterPageNormal ||
+	       name == CommonStrings::trMasterPageNormal ||
+	       name == CommonStrings::masterPageNormalLeft ||
+	       name == CommonStrings::trMasterPageNormalLeft ||
+	       name == CommonStrings::masterPageNormalMiddle ||
+	       name == CommonStrings::trMasterPageNormalMiddle ||
+	       name == CommonStrings::masterPageNormalRight ||
+	       name == CommonStrings::trMasterPageNormalRight;
+}
+
+QString MasterPageOperations::nonReservedName(const QString& name)
+{
+	const QStringList reservedNames = {
+		CommonStrings::masterPageNormal,
+		CommonStrings::trMasterPageNormal,
+		CommonStrings::trMasterPageNormalLeft,
+		CommonStrings::trMasterPageNormalMiddle,
+		CommonStrings::trMasterPageNormalRight
+	};
+	return getUniqueName(name, reservedNames);
+}
+
+ScPage* MasterPageOperations::duplicateMasterPage(ScribusDoc* doc, const QString& sourceName,
+                                                    const QString& destinationName, int pagePosition)
+{
+	if (!doc || !doc->MasterNames.contains(sourceName) || destinationName.isEmpty() ||
+	    doc->MasterNames.contains(destinationName))
+		return nullptr;
+
+	const int sourcePageIndex = doc->MasterNames.value(sourceName);
+	const ScPage* source = doc->MasterPages.at(sourcePageIndex);
+	const int groupCounter = doc->GroupCounter;
+	ScPage* destination = doc->addMasterPage(doc->MasterPages.count(), destinationName);
+	if (!destination)
+		return nullptr;
+
+	if (pagePosition >= 0)
+		destination->LeftPg = pagePosition;
+
+	copyInitialMargins(doc, source, destination);
+	copyMasterItems(doc, sourcePageIndex, destination, destinationName);
+	source->guides.copy(&destination->guides);
+	doc->GroupCounter = groupCounter + 1;
+	return destination;
+}
Index: scribus/ui/pagepalette_masterpages.h
===================================================================
--- scribus/ui/pagepalette_masterpages.h	(revision 27785)
+++ scribus/ui/pagepalette_masterpages.h	(working copy)
@@ -30,7 +30,7 @@
 
 public:
 	PagePalette_MasterPages( QWidget* parent, ScribusView *pCurrentView, const QString& masterPageName);
-	~PagePalette_MasterPages() {};
+	~PagePalette_MasterPages() override = default;
 
 	void setView(ScribusView* view, const QString& masterPageName = "");
 	
@@ -54,13 +54,6 @@
 
 	void changeEvent(QEvent *e) override;
 
-	bool isReservedName(const QString& name) const;
-	QString getNonReservedName(const QString& name) const;
-	/*!
-	 * \retval the current name or, if it's a standard one, the translated version
-	*/
-	QString getTranslatedReservedName(const QString& name) const;
-
 private slots:
 	void duplicateMasterPage();
 	void deleteMasterPage();
Index: scribus/ui/pagepalette_masterpages.cpp
===================================================================
--- scribus/ui/pagepalette_masterpages.cpp	(revision 27785)
+++ scribus/ui/pagepalette_masterpages.cpp	(working copy)
@@ -17,7 +17,6 @@
 #include <QScopedPointer>
 #include <QSpacerItem>
 #include <QString>
-#include <QStringList>
 #include <QToolButton>
 #include <QToolTip>
 #include <QVBoxLayout>
@@ -26,17 +25,15 @@
 #include "canvasmode.h"
 #include "commonstrings.h"
 #include "iconmanager.h"
+#include "masterpageoperations.h"
 #include "mergedoc.h"
 #include "newtemp.h"
 #include "pagestructs.h"
 #include "prefsmanager.h"
-#include "scpage.h"
 #include "scribus.h"
 #include "scribusapp.h"
 #include "scribusdoc.h"
 #include "scribusview.h"
-#include "scribusXml.h"
-#include "selection.h"
 #include "util.h"
 #include "util_os.h"
 
@@ -160,7 +157,7 @@
 	{
 		m_currentPage = delItem->text();
 
-		if (isReservedName(m_currentPage))
+		if (MasterPageOperations::isReservedName(m_currentPage))
 			continue;
 
 		QString extraWarn;
@@ -215,97 +212,29 @@
 		m_view->requestMode(submodeEndNodeEdit);
 
 	name = dia->Answer->text();
-
 	if (name.isEmpty())
 		return;
 
-	name = getNonReservedName(name);
+	name = MasterPageOperations::nonReservedName(name);
 	name = getUniqueName(name, m_doc->MasterNames);
 
-//	PrefsManager& prefsManager = PrefsManager::instance();
-	int inde = m_doc->MasterNames.value(m_currentPage);
-	int nr = m_doc->Pages->count();
-	ScPage* from = m_doc->Pages->at(inde);
-	ScPage* destination = m_doc->addMasterPage(nr, name);
+	int pagePosition = -1;
 	if (m_doc->pagePositioning() != singlePage)
 	{
-		int lp = dia->Links->currentIndex();
-		if (lp == 0)
-			lp = 1;
-		else if (lp == static_cast<int>(dia->Links->count()-1))
-			lp = 0;
+		pagePosition = dia->Links->currentIndex();
+		if (pagePosition == 0)
+			pagePosition = 1;
+		else if (pagePosition == static_cast<int>(dia->Links->count() - 1))
+			pagePosition = 0;
 		else
-			lp++;
-		destination->LeftPg = lp;
-	}
-	destination->initialMargins.setTop(from->initialMargins.top());
-	destination->initialMargins.setBottom(from->initialMargins.bottom());
-	if (m_doc->pageSets()[m_doc->pagePositioning()].Columns == 1)
-	{
-		destination->initialMargins.setLeft(from->initialMargins.left());
-		destination->initialMargins.setRight(from->initialMargins.right());
-	}
-	else
-	{
-		if (destination->LeftPg != from->LeftPg)
-		{
-			if (destination->LeftPg > 1)
-			{
-				destination->initialMargins.setRight(from->initialMargins.left());
-				destination->initialMargins.setLeft(from->initialMargins.left());
-			}
-			else
-			{
-				destination->initialMargins.setLeft(from->initialMargins.left());
-				destination->initialMargins.setRight(from->initialMargins.right());
-			}
-		}
-		else
-		{
-			destination->initialMargins.setLeft(from->initialMargins.left());
-			destination->initialMargins.setRight(from->initialMargins.right());
-		}
-	}
-	//#8321 : incorrect selection of master page on new mp creation/duplication
-	//m_doc->setCurrentPage(destination);
+			++pagePosition;
+	}
+
+	if (!MasterPageOperations::duplicateMasterPage(m_doc, m_currentPage, name, pagePosition))
+		return;
+
 	updateMasterPageList(name);
 	selectMasterPage(name);
-	uint oldItems = m_doc->Items->count();
-	uint end2 = m_doc->MasterItems.count();
-	int GrMax = m_doc->GroupCounter;
-	m_doc->m_Selection->clear();
-	if (oldItems>0)
-	{
-		ScLayers::iterator it;
-		m_doc->m_Selection->delaySignalsOn();
-		for (it = m_doc->Layers.begin(); it != m_doc->Layers.end(); ++it)
-		{
-			for (uint ite = 0; ite < oldItems; ++ite)
-			{
-				PageItem *itemToCopy = m_doc->Items->at(ite);
-				if (itemToCopy->OwnPage == inde && (it->ID == itemToCopy->m_layerID))
-					m_doc->m_Selection->addItem(itemToCopy);
-			}
-			if (m_doc->m_Selection->count() != 0)
-			{
-				ScriXmlDoc ss;
-				QString buffer = ss.writeElem(m_doc, m_doc->m_Selection);
-				ss.readElemToLayer(buffer, m_doc, destination->xOffset(), destination->yOffset(), false, true, it->ID);
-				m_doc->m_Selection->clear();
-			}
-		}
-		m_doc->m_Selection->clear();
-		m_doc->m_Selection->delaySignalsOff();
-	}
-	uint end3 = m_doc->MasterItems.count();
-	for (uint a = end2; a < end3; ++a)
-	{
-		PageItem *newItem = m_doc->MasterItems.at(a);
-		int masterPageIndex = m_doc->MasterNames.value(name);
-		newItem->setMasterPage(masterPageIndex, name);
-	}
-	from->guides.copy(&destination->guides);
-	m_doc->GroupCounter = GrMax + 1;
 	m_view->deselectItems(true);
 	m_doc->setLoading(false);
 	m_view->reformPages();
@@ -328,7 +257,7 @@
 	if (name.isEmpty())
 		return;
 
-	name = getNonReservedName(name);
+	name = MasterPageOperations::nonReservedName(name);
 	name = getUniqueName(name, m_doc->MasterNames);
 
 	m_doc->setCurrentPage(m_doc->addMasterPage(nr, name));
@@ -463,7 +392,7 @@
 			mpName = CommonStrings::trMasterPageNormal;
 		QListWidgetItem* mpItem = new QListWidgetItem(mpName);
 
-		if (!isReservedName(mpName))
+		if (!MasterPageOperations::isReservedName(mpName))
 			mpItem->setFlags(mpItem->flags() |= Qt::ItemIsEditable);
 		else
 			mpItem->setFlags(mpItem->flags() &= ~Qt::ItemIsEditable);
@@ -490,27 +419,7 @@
 	if (oldName == newName)
 		return;
 
-	// FIXME: the checks in the old version of this function
-	// and the ones in m_doc->renameMasterPage() did not match.
-	// we should move all MasterPage related code to a new masterpage.h
-	// file and remove code from scribusdoc.cpp and from here.
-	// at the end, the code below should be:
-	//
-	// if (m_doc->m_masterPage->rename(oldName, newName))
-	// {
-	//		bool sigBlocked = masterPageListBox->blockSignals(true);
-	//		item->setData(Qt::UserRole, newName);
-	//		masterPageListBox->blockSignals(sigBlocked);
-	//		updateMasterPageList(newName);
-	// }
-	// else
-	// {
-	//		bool sigBlocked = masterPageListBox->blockSignals(true);
-	//		item->setText(getTranslatedReservedName(oldName));
-	//		masterPageListBox->blockSignals(sigBlocked);
-	// }
-
-	if (newName.isEmpty() || isReservedName(newName) ||
+	if (newName.isEmpty() || MasterPageOperations::isReservedName(newName) ||
 		!m_doc->MasterNames.contains(oldName) ||
 		m_doc->MasterNames.contains(newName))
 	{
@@ -544,44 +453,3 @@
 
 	QWidget::changeEvent(e);
 }
-
-// FIXME: move to a new masterpages.h
-bool PagePalette_MasterPages::isReservedName(const QString& name) const
-{
-	bool retval = name == CommonStrings::masterPageNormal ||
-		name == CommonStrings::trMasterPageNormal ||
-		name == CommonStrings::masterPageNormalLeft ||
-		name == CommonStrings::trMasterPageNormalLeft ||
-		name == CommonStrings::masterPageNormalMiddle ||
-		name == CommonStrings::trMasterPageNormalMiddle ||
-		name == CommonStrings::masterPageNormalRight ||
-		name == CommonStrings::trMasterPageNormalRight;
-	return retval;
-}
-
-// FIXME: move to a new masterpages.h
-QString PagePalette_MasterPages::getNonReservedName(const QString& name) const
-{
-	const QStringList reservedNames = {
-		CommonStrings::masterPageNormal,
-		CommonStrings::trMasterPageNormal,
-		CommonStrings::trMasterPageNormalLeft,
-		CommonStrings::trMasterPageNormalMiddle,
-		CommonStrings::trMasterPageNormalRight
-	};
-	return getUniqueName(name, reservedNames);
-}
-
-// FIXME: move to a new masterpages.h
-QString PagePalette_MasterPages::getTranslatedReservedName(const QString& name) const
-{
-	if (name == CommonStrings::masterPageNormal)
-		return CommonStrings::trMasterPageNormal;
-	if (name == CommonStrings::masterPageNormalLeft)
-		return CommonStrings::trMasterPageNormalLeft;
-	if (name == CommonStrings::masterPageNormalMiddle)
-		return CommonStrings::trMasterPageNormalMiddle;
-	if (name == CommonStrings::masterPageNormalRight)
-		return CommonStrings::trMasterPageNormalRight;
-	return name;
-}

qirat

2026-08-26 07:35

reporter   ~0054306

Details on the patches.

ale

2026-08-27 09:16

manager   ~0054332

Checked the behavior... Seems to be working as described.

It's IMO a bit too cumbersome (but this probably due to other Scribus issues) and hidden, but it might be OK for now.

qirat

2026-08-27 23:30

reporter   ~0054349

Hidden? Where would you rather have it, @ale? I can give it another go.

Issue History

Date Modified Username Field Change
2005-10-02 14:35 christoph_s New Issue
2005-10-04 07:11 fschmid Status new => assigned
2005-10-04 07:11 fschmid Assigned To => fschmid
2006-05-18 18:44 christoph_s Relationship added child of 0003837
2014-07-21 02:36 Kunda Relationship added related to 0008732
2014-07-21 05:20 christoph_s Assigned To fschmid =>
2026-08-26 04:32 qirat Note Added: 0054304
2026-08-26 04:32 qirat File Added: mirror-master-page-layout-patch-series.pdf
2026-08-26 04:32 qirat File Added: mp2-mirror-master-page-layout-v1.02.patch
2026-08-26 04:32 qirat File Added: mp1-extract-master-page-duplication-v1.02.patch
2026-08-26 04:32 qirat Tag Attached: #please_test
2026-08-26 04:34 qirat File Deleted: mp2-mirror-master-page-layout-v1.02.patch
2026-08-26 04:35 qirat File Deleted: mp1-extract-master-page-duplication-v1.02.patch
2026-08-26 07:25 qirat Note Added: 0054305
2026-08-26 07:25 qirat File Added: mp2-mirror-master-page-layout-v1.03.patch
2026-08-26 07:25 qirat File Added: mp1-extract-master-page-duplication-v1.03.patch
2026-08-26 07:34 qirat File Deleted: mirror-master-page-layout-patch-series.pdf
2026-08-26 07:35 qirat Note Added: 0054306
2026-08-26 07:35 qirat File Added: mirror-master-page-layout-patch-series-v1.03.pdf
2026-08-27 09:16 ale Note Added: 0054332
2026-08-27 11:46 ale Tag Attached: #patch_to_be_reviewed
2026-08-27 23:30 qirat Note Added: 0054349