View Issue Details

IDProjectCategoryView StatusLast Update
0017954ScribusGuidespublic2026-08-29 13:59
Reporterqirat Assigned To 
PrioritynormalSeverityminorReproducibilityalways
Status newResolutionopen 
PlatformLinuxOSFedora WorkstationOS Version44
Product Version1.7.4.svn 
Summary0017954: [patch] Fix Master Guide State Across Modes
Description#Follow-up
  * This patch is a follow-up to the `master-page-guides-inheritance-v1.3.patch` (0015624) and is intended to be applied on top of that patch/base.
  * It relies on the separate inherited-guide storage introduced there (`m_horizontalMasterG` / `m_verticalMasterG`).

#Issue
* After switching between normal and Master Page mode, Guide Manager could keep showing rows from the previously active page.
* Those stale rows could remain editable, so edits/deletes could target the wrong page/master until the palette was refreshed by another interaction.
* Inherited master guides shown on normal pages had no ownership indication and could appear to behave like ordinary local guides.
* Local and inherited guides at the same coordinate could be represented as a single row, losing their separate ownership.

#Cause
  * `GuideManager` could keep a stale `currentPage` across Master Page mode transitions.
  * The model treated guide position as identity, so a local guide and inherited master guide at the same coordinate were conflated.

#Fix
  * Rebind the palette with `setupPage(false)` when entering/leaving Master Page mode.
  * Expose inherited guide lists separately through `GuideManagerCore`.
  * Track read-only state per `GuidesModel` row rather than by coordinate.
  * Keep coincident local/master guides as separate rows.
  * `setData()` / `flags()` reject editing inherited rows; removal paths skip them.
  * Add a tooltip identifying inherited rows and directing edits to Master Page mode.

#Result
  * Inherited master guides are visible but read-only on normal pages.
  * Local guides remain editable/deletable.
  * Master guides are editable normally in Master Page mode.
  * Coincident local/master guides preserve correct ownership.
  * Guide Manager no longer retains stale guide data across mode switches.
Tags#please_test
Attached Files
master-page-guides2-guide-manager-readonly-v1.1.patch (11,463 bytes)   
Index: scribus/guidemanagercore.h
===================================================================
--- scribus/guidemanagercore.h	(after master-page-guides-inheritance-v1.3)
+++ scribus/guidemanagercore.h	(working copy)
@@ -56,6 +56,8 @@
 	Guides verticals(GuideType type) const;
 	Guides horizontalsWithMaster() const;
 	Guides verticalsWithMaster() const;
+	Guides masterPageHorizontals() const;
+	Guides masterPageVerticals() const;
 	double horizontal(uint ix, GuideType type) const;
 	double vertical(uint ix, GuideType type) const;
 
Index: scribus/guidemanagercore.cpp
===================================================================
--- scribus/guidemanagercore.cpp	(after master-page-guides-inheritance-v1.3)
+++ scribus/guidemanagercore.cpp	(working copy)
@@ -213,6 +213,16 @@
 			guides.append(guidePos);
 	}
 	return guides;
+}
+
+Guides GuideManagerCore::masterPageHorizontals() const
+{
+	return m_horizontalMasterG;
+}
+
+Guides GuideManagerCore::masterPageVerticals() const
+{
+	return m_verticalMasterG;
 }
 
 double GuideManagerCore::horizontal(uint ix, GuideType type) const
Index: scribus/guidesmodel.h
===================================================================
--- scribus/guidesmodel.h	(after master-page-guides-inheritance-v1.3)
+++ scribus/guidesmodel.h	(working copy)
@@ -12,8 +12,7 @@
 
 
 /*! \brief A model for guides lists.
-It holds guides as a double QList (it will be expanded later because of RFEs)
-and it handles its values editing too. The editor from GuidesDelegate
+It holds guide values and per-row state, and handles value editing too. The editor from GuidesDelegate
 is used.
 See Qt4 documentation for more info about its methods.
 FIXME: unit conversions for dipslay vs. internal
@@ -37,17 +36,18 @@
 
 		QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
 
-		void removeValues(const Guides & guides);
-		// qt4 api is "wrokarounded" with insertRow() and removeValues()
+		bool removeIndexes(const QModelIndexList& indexes);
+		// qt4 api is "wrokarounded" with insertRow() and removeIndexes()
 		// these removeRows() and insertRows() does not handle margin items correctly
 // 		bool removeRows(int row, int count, const QModelIndex & parent = QModelIndex());
 // 		bool insertRows( int row, int count, const QModelIndex & parent = QModelIndex());
 		void insertRow();
 
 		//! \brief Set new values into the model.
-		void setValues(const Guides& values);
+		void setValues(const Guides& editableValues, const Guides& readOnlyValues = Guides());
 		//! \brief Get values back to the app.
 		const Guides& values() const;
+		Guides editableValues() const;
 
 		void unitChange(int docUnitIndex, int docUnitDecimals, double offset);
 #if 0
@@ -61,7 +61,10 @@
 		void valueChanged();
 
 	private:
+		void sortValues();
+
 		Guides m_values;
+		QList<bool> m_readOnlyRows;
 		int m_docUnitIndex {0};
 		int m_docUnitDecimals {0};
 		double m_rule {0.0};
Index: scribus/guidesmodel.cpp
===================================================================
--- scribus/guidesmodel.cpp	(after master-page-guides-inheritance-v1.3)
+++ scribus/guidesmodel.cpp	(working copy)
@@ -7,6 +7,7 @@
 
 #include <algorithm>
 #include <QLocale>
+#include <QPair>
 
 #include "guidesmodel.h"
 #include "units.h"
@@ -49,6 +50,8 @@
 		return pts2value(m_values.at(index.row()) - m_rule, m_docUnitIndex);
 	if (role == Qt::UserRole)
 		return m_values.at(index.row());
+	if (role == Qt::ToolTipRole && m_readOnlyRows.at(index.row()))
+		return tr("Inherited from master page. Edit this guide in Master Page mode.");
 
 	if (role == Qt::BackgroundRole && m_values.at(index.row()) == 0.0)
 		return QVariant(QColor(Qt::red));
@@ -57,22 +60,31 @@
 
 bool GuidesModel::setData(const QModelIndex & index, const QVariant & value, int role)
 {
-	if (role != Qt::EditRole || !index.isValid())
+	if (role != Qt::EditRole || !index.isValid() || m_readOnlyRows.at(index.row()))
 		return false;
 	bool ok;
 	double newVal = value.toDouble(&ok);
 	if (!ok)
 		return false;
-	m_values[index.row()] = value2pts(newVal, m_docUnitIndex) + m_rule;
-	std::sort(m_values.begin(), m_values.end());
+	double newValue = value2pts(newVal, m_docUnitIndex) + m_rule;
+	for (int i = 0; i < m_values.count(); ++i)
+	{
+		if (m_readOnlyRows.at(i) && m_values.at(i) == newValue)
+			return false;
+	}
+	m_values[index.row()] = newValue;
+	sortValues();
 	emit dataChanged(index, index);
 	emit valueChanged();
 	return true;
 }
 
-Qt::ItemFlags GuidesModel::flags(const QModelIndex & /*index*/) const
-{
-	return Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable;
+Qt::ItemFlags GuidesModel::flags(const QModelIndex & index) const
+{
+	Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
+	if (index.isValid() && !m_readOnlyRows.at(index.row()))
+		flags |= Qt::ItemIsEditable;
+	return flags;
 }
 
 QVariant GuidesModel::headerData(int /*section*/, Qt::Orientation orientation, int /*role*/) const
@@ -82,12 +94,26 @@
 	return "";
 }
 
-void GuidesModel::removeValues(const Guides & guides)
-{
-	beginResetModel();
-	for (double d : guides)
-		m_values.removeAll(d);
-	endResetModel();
+bool GuidesModel::removeIndexes(const QModelIndexList& indexes)
+{
+	QList<int> rows;
+	for (const QModelIndex& index : indexes)
+	{
+		if (index.isValid() && !m_readOnlyRows.at(index.row()) && !rows.contains(index.row()))
+			rows.append(index.row());
+	}
+	if (rows.isEmpty())
+		return false;
+
+	std::sort(rows.begin(), rows.end(), [](int a, int b) { return a > b; });
+	beginResetModel();
+	for (int row : rows)
+	{
+		m_values.removeAt(row);
+		m_readOnlyRows.removeAt(row);
+	}
+	endResetModel();
+	return true;
 }
 
 // bool GuidesModel::removeRows(int row, int count, const QModelIndex & parent)
@@ -114,25 +140,64 @@
 void GuidesModel::insertRow()
 {
 // 	insertRows(rowCount(), 1);
-	beginResetModel();
 	if (m_values.contains(0.0))
 		return;
+	beginResetModel();
 	m_values.append(0.0);
-	std::sort(m_values.begin(), m_values.end());
-	endResetModel();
-}
-
-void GuidesModel::setValues(const Guides& values)
-{
-	beginResetModel();
-	m_values = values;
-	std::sort(m_values.begin(), m_values.end());
+	m_readOnlyRows.append(false);
+	sortValues();
+	endResetModel();
+}
+
+void GuidesModel::setValues(const Guides& editableValues, const Guides& readOnlyValues)
+{
+	beginResetModel();
+	m_values = editableValues;
+	m_readOnlyRows.clear();
+	for (int i = 0; i < editableValues.count(); ++i)
+		m_readOnlyRows.append(false);
+	for (double value : readOnlyValues)
+	{
+		m_values.append(value);
+		m_readOnlyRows.append(true);
+	}
+	sortValues();
 	endResetModel();
 }
 
 const Guides& GuidesModel::values() const
 {
 	return m_values;
+}
+
+Guides GuidesModel::editableValues() const
+{
+	Guides values;
+	for (int i = 0; i < m_values.count(); ++i)
+	{
+		if (!m_readOnlyRows.at(i))
+			values.append(m_values.at(i));
+	}
+	return values;
+}
+
+void GuidesModel::sortValues()
+{
+	QList<QPair<double, bool>> entries;
+	for (int i = 0; i < m_values.count(); ++i)
+		entries.append(qMakePair(m_values.at(i), m_readOnlyRows.at(i)));
+
+	std::sort(entries.begin(), entries.end(), [](const QPair<double, bool>& a, const QPair<double, bool>& b)
+	{
+		if (a.first == b.first)
+			return a.second < b.second;
+		return a.first < b.first;
+	});
+	for (int i = 0; i < entries.count(); ++i)
+	{
+		m_values[i] = entries.at(i).first;
+		m_readOnlyRows[i] = entries.at(i).second;
+	}
 }
 
 void GuidesModel::unitChange(int docUnitIndex, int docUnitDecimals,double offset)
Index: scribus/ui/guidemanager.cpp
===================================================================
--- scribus/ui/guidemanager.cpp	(after master-page-guides-inheritance-v1.3)
+++ scribus/ui/guidemanager.cpp	(working copy)
@@ -267,36 +267,24 @@
 void GuideManager::delHorButton_clicked()
 {
 	const QModelIndexList indexes = horizontalView->selectionModel()->selectedRows(0);
-	Guides v;
-
-	for (const QModelIndex& ix : indexes)
-		// here *must* go EditRole due truncations in DisplayRole
-		// see GuidesModel::data()
-		v.append(horizontalModel->data(ix, Qt::UserRole).toDouble());
-
-	horizontalModel->removeValues(v);
+	if (!horizontalModel->removeIndexes(indexes))
+		return;
 
 	currentPage->guides.clearHorizontals(GuideManagerCore::Standard);
-	currentPage->guides.addHorizontals(horizontalModel->values(), GuideManagerCore::Standard);
-	drawGuides();
+	currentPage->guides.addHorizontals(horizontalModel->editableValues(), GuideManagerCore::Standard);
+	clearRestoreHorizontalList();
 	m_doc->changed();
 }
 
 void GuideManager::delVerButton_clicked()
 {
 	const QModelIndexList indexes = verticalView->selectionModel()->selectedRows(0);
-	Guides v;
-
-	for (const QModelIndex& ix : indexes)
-		// here *must* go EditRole due truncations in DisplayRole
-		// see GuidesModel::data()
-		v.append(verticalModel->data(ix, Qt::UserRole).toDouble());
-
-	verticalModel->removeValues(v);
+	if (!verticalModel->removeIndexes(indexes))
+		return;
 
 	currentPage->guides.clearVerticals(GuideManagerCore::Standard);
-	currentPage->guides.addVerticals(verticalModel->values(), GuideManagerCore::Standard);
-	drawGuides();
+	currentPage->guides.addVerticals(verticalModel->editableValues(), GuideManagerCore::Standard);
+	clearRestoreVerticalList();
 	m_doc->changed();
 }
 
@@ -474,13 +462,15 @@
 
 void GuideManager::clearRestoreHorizontalList()
 {
-	horizontalModel->setValues(currentPage->guides.horizontals(GuideManagerCore::Standard));
+	horizontalModel->setValues(currentPage->guides.horizontals(GuideManagerCore::Standard),
+		currentPage->guides.masterPageHorizontals());
 	drawGuides();
 }
 
 void GuideManager::clearRestoreVerticalList()
 {
-	verticalModel->setValues(currentPage->guides.verticals(GuideManagerCore::Standard));
+	verticalModel->setValues(currentPage->guides.verticals(GuideManagerCore::Standard),
+		currentPage->guides.masterPageVerticals());
 	drawGuides();
 }
 
@@ -595,16 +585,16 @@
 void GuideManager::verticalModel_valueChanged()
 {
 	currentPage->guides.clearVerticals(GuideManagerCore::Standard);
-	currentPage->guides.addVerticals(verticalModel->values(), GuideManagerCore::Standard);
-	drawGuides();
+	currentPage->guides.addVerticals(verticalModel->editableValues(), GuideManagerCore::Standard);
+	clearRestoreVerticalList();
 	m_doc->changed();
 }
 
 void GuideManager::horizontalModel_valueChanged()
 {
 	currentPage->guides.clearHorizontals(GuideManagerCore::Standard);
-	currentPage->guides.addHorizontals(horizontalModel->values(), GuideManagerCore::Standard);
-	drawGuides();
+	currentPage->guides.addHorizontals(horizontalModel->editableValues(), GuideManagerCore::Standard);
+	clearRestoreHorizontalList();
 	m_doc->changed();
 }
 
Index: scribus/scribus.cpp
===================================================================
--- scribus/scribus.cpp	(after master-page-guides-inheritance-v1.3)
+++ scribus/scribus.cpp	(working copy)
@@ -7728,12 +7728,14 @@
 	if (doc->masterPageMode())
 	{
 		pagePalette->startMasterPageMode(mpName);
+		guidePalette->setupPage(false);
 		return;
 	}
 
 	view->saveViewState();
 
 	pagePalette->startMasterPageMode(mpName);
+	guidePalette->setupPage(false);
 	if (pagePalette->isClosed())
 	{
 		auto* area = pagePalette->dockAreaWidget();
@@ -7770,6 +7772,7 @@
 	view->reformPages(false);
 	view->setContentsPos(viewState.contentX, viewState.contentY);
 	doc->setLoading(false);
+	guidePalette->setupPage(false);
 	view->DrawNew();
 }
 
PatchYes

Activities

Issue History

Date Modified Username Field Change
2026-08-29 13:58 qirat New Issue
2026-08-29 13:58 qirat File Added: master-page-guides2-guide-manager-readonly-v1.1.patch
2026-08-29 13:59 qirat Tag Attached: #please_test