View Issue Details

IDProjectCategoryView StatusLast Update
0015438ScribusGeneralpublic2019-12-08 21:24
Reporterale Assigned Toale  
PrioritynormalSeverityfeatureReproducibilityN/A
Status closedResolutionfixed 
Product Version1.5.5.svn 
Fixed in Version1.5.6.svn 
Summary0015438: Multiple duplicate on pages
Description- i've added a new tab to "item > duplicate > multiple duplicate" to
  duplicate an item on the following pages (all, even, odd, ranges;
  with text links).
- this tab should replace the "insert > frames" dialog.
- this tab should replace the "automatic text frames" in the "file >
  new" dialog.

in the mailing list, there are a few more details:
http://lists.scribus.net/pipermail/scribus/2018-September/055315.html
TagsNo tags attached.
PatchYes

Relationships

related to 0010557 new New "page dimension" buttons for multiple copy dialog 

Activities

ale

2018-09-27 07:30

manager   ~0045485

https://github.com/scribusproject/scribus/compare/master...aoloe:insert-frames.diff
insert-frames.diff (16,877 bytes)   
diff --git a/scribus/pageitem.cpp b/scribus/pageitem.cpp
index 22c5fcb3d3..6050021f8f 100644
--- a/scribus/pageitem.cpp
+++ b/scribus/pageitem.cpp
@@ -9881,6 +9881,29 @@ PageItem* PageItem::lastInChain()
 	return last;
 }
 
+bool PageItem::isAllNextInChainSamePage()
+{
+    PageItem* next = this;
+	while (next->nextInChain() != nullptr) {
+        next = next->nextInChain();
+        if (next->OwnPage != this->OwnPage) {
+            return false;
+        }
+    }
+	return true;
+}
+
+PageItem* PageItem::lastInChainSamePage()
+{
+	assert(isInChain());
+	auto last = lastInChain();
+	while (last->OwnPage != OwnPage && last->prevInChain() != nullptr) {
+		last = last->nextInChain();
+	}
+	return last;
+}
+
+
 QRect PageItem::getRedrawBounding(const double viewScale)
 {
 	int x = qRound(floor(BoundingX - Oldm_lineWidth / 2.0 - 5) * viewScale);
diff --git a/scribus/pageitem.h b/scribus/pageitem.h
index 31061f0aaa..162f476e31 100644
--- a/scribus/pageitem.h
+++ b/scribus/pageitem.h
@@ -465,6 +465,9 @@ class SCRIBUS_API PageItem : public QObject, public UndoObject, public SaxIO, pu
 	//you can change all code for search first or last item in chain
 	PageItem* firstInChain();
 	PageItem* lastInChain();
+	PageItem* lastInChainSamePage();
+	bool isAllNextInChainSamePage();
+
 	bool testLinkCandidate(PageItem* nextFrame);
 	void unlink(bool createUndo = true);
 	void link(PageItem* nextFrame, bool addPARSEP = true);
diff --git a/scribus/pageitem_textframe.cpp b/scribus/pageitem_textframe.cpp
index 9f5ec4b64a..dcd75df54c 100644
--- a/scribus/pageitem_textframe.cpp
+++ b/scribus/pageitem_textframe.cpp
@@ -1753,7 +1753,7 @@ void PageItem_TextFrame::layout()
 						realCharHeight = qMax(realCharHeight, gm.ascent + gm.descent);
 						gm = font.glyphBBox(gl.glyph, chsd / 10.0);
 						realAsce = qMax(realAsce, gm.ascent + gm.descent);
-						wide += gm.width;
+						wide += gm.width; // a.l.e: this sets the width of the indent
 					}
 					wide = (wide* scaleH) + (1 - scaleH);
 					realAsce = realAsce  * scaleV + offset;
diff --git a/scribus/scribus.cpp b/scribus/scribus.cpp
index 4330c79081..4eb0b3aab3 100644
--- a/scribus/scribus.cpp
+++ b/scribus/scribus.cpp
@@ -61,6 +61,8 @@ for which a new license (GPL+exception) is in place.
 //#include <QQmlProperty>
 //>>
 #include <QRegExp>
+#include <memory>
+#include <QScopedPointer>
 #include <QScopedPointer>
 #include <QStyleFactory>
 #include <QTableWidget>
@@ -259,6 +261,7 @@ for which a new license (GPL+exception) is in place.
 #include "undostate.h"
 #include "units.h"
 #include "urllauncher.h"
+#include "usertaskstructs.h"
 #include "util.h"
 #include "util_file.h"
 #include "util_formats.h"
@@ -6432,8 +6435,7 @@ void ScribusMainWindow::duplicateItem()
 	if (UndoManager::undoEnabled())
 		trans = m_undoManager->beginTransaction(Um::Selection, Um::IPolygon, Um::Duplicate, "", Um::IMultipleDuplicate);
 
-	ItemMultipleDuplicateData mdData;
-	memset(&mdData, 0, sizeof(mdData));
+	ItemMultipleDuplicateData mdData{};
 	mdData.type = 0;
 	mdData.copyCount = 1;
 	mdData.copyShiftOrGap = 0;
@@ -6469,7 +6471,7 @@ void ScribusMainWindow::duplicateItemMulti()
 	if (doc->appMode == modeEditClip)
 		view->requestMode(submodeEndNodeEdit);
 	internalCopy = true;
-	QScopedPointer<MultipleDuplicate> dia(new MultipleDuplicate(doc->unitIndex(), this));
+	QScopedPointer<MultipleDuplicate> dia(new MultipleDuplicate(this, doc));
 	if (dia->exec())
 	{
 		ItemMultipleDuplicateData mdData;
diff --git a/scribus/scribusdoc.cpp b/scribus/scribusdoc.cpp
index e2e5da6fca..1e37b004cc 100644
--- a/scribus/scribusdoc.cpp
+++ b/scribus/scribusdoc.cpp
@@ -38,6 +38,7 @@ for which a new license (GPL+exception) is in place.
 #include <QProgressBar>
 #include <QtAlgorithms>
 #include <QTime>
+#include <QStringList>
 //#include <qtconcurrentmap.h>
 
 #include "actionmanager.h"
@@ -13500,10 +13501,14 @@ QRectF ScribusDoc::ApplyGridF(const QRectF& in)
 	return nr;
 }
 
-void ScribusDoc::itemSelection_MultipleDuplicate(ItemMultipleDuplicateData& mdData)
+void ScribusDoc::itemSelection_MultipleDuplicate(const ItemMultipleDuplicateData& mdData)
 {
 	if ((mdData.type==0 && mdData.copyCount<1) || (mdData.type==1 && (mdData.gridRows==1 && mdData.gridCols==1)))
 		return;
+	if ((mdData.type == 2) && (mdData.pageSelection != 4) && (currentPageNumber() == Pages->count() - 1))
+		// cannot copy on following pages, while being on the last page
+		return;
+
 	QString tooltip;
 	UndoTransaction activeTransaction;
 
@@ -13631,6 +13636,10 @@ void ScribusDoc::itemSelection_MultipleDuplicate(ItemMultipleDuplicateData& mdDa
 		QString vString = QString::number(mdData.gridGapV, 'f', unitPrecision) + " " + unitSuffix;
 		tooltip = tr("Number of copies: %1\nHorizontal gap: %2\nVertical gap: %3").arg(copyCount-1).arg(hString).arg(vString).arg(unitSuffix);
 	}
+	else if (mdData.type==2)
+	{
+		multipleDuplicateByPage(mdData, selection, tooltip);
+	}
 	if (activeTransaction)
 	{
 		activeTransaction.commit("", nullptr, "", tooltip, nullptr);
@@ -13643,6 +13652,76 @@ void ScribusDoc::itemSelection_MultipleDuplicate(ItemMultipleDuplicateData& mdDa
 	changed();
 }
 
+void ScribusDoc::multipleDuplicateByPage(const ItemMultipleDuplicateData& dialogData, Selection& selection, QString& tooltip)
+{
+	std::vector<int> pages;
+	QString pageRange;
+	if (dialogData.pageSelection == 1) {
+		pageRange = QString("%1-%2").arg(currentPageNumber() + 2).arg(Pages->count());
+	} else if ((dialogData.pageSelection == 2) || dialogData.pageSelection == 3) {
+		int start = currentPageNumber() + 2;
+		// round to the next odd / even number
+		if (dialogData.pageSelection == 2) {
+			start += start % 2;
+		} else {
+			start += 1 - (start % 2);
+		}
+		QStringList pageList{};
+		for (int i = start; i <= Pages->count(); i += 2) {
+			pageList << QString::number(i);
+		}
+		pageRange = pageList.join(',');
+	} else if (dialogData.pageSelection == 4) {
+		pageRange = dialogData.pageRange;
+	}
+	parsePagesString(pageRange, &pages, Pages->count());
+
+	PageItem* lastInChain = nullptr;
+	if (dialogData.pageLinkText) {
+		for (auto item: selection.items()) {
+			// get the first text frame in the selection, the first frame in the chain
+			if (item->itemType() == PageItem::TextFrame) {
+				if (item->isInChain() && item->isAllNextInChainSamePage()) {
+					lastInChain = item->lastInChain();
+					assert( !lastInChain->nextInChain() );
+				} else {
+					lastInChain = item;
+				}
+				break;
+			}
+		}
+	}
+
+	ScPage* oldCurrentPage = currentPage();
+	ScriXmlDoc xmlStream;
+	QString buffer = xmlStream.WriteElem(this, &selection);
+	for (const auto page: pages) {
+		ScPage* targetPage = Pages->at(page - 1);
+		setCurrentPage(targetPage);
+		auto countBeforeInsert = Items->count();
+		xmlStream.ReadElem(buffer, m_appPrefsData.fontPrefs.AvailFonts, this, currentPage()->xOffset(), currentPage()->yOffset(), false, true, m_appPrefsData.fontPrefs.GFontSub);
+		if (lastInChain) {
+			for (auto i = countBeforeInsert; i < Items->count(); ++i) {
+				auto item = Items->at(i);
+				if (item->itemType() == PageItem::TextFrame) {
+					if (item->isInChain()) {
+						lastInChain->link(item->firstInChain());
+						lastInChain = item->lastInChain();
+					} else {
+						lastInChain->link(item);
+						lastInChain = item;
+					}
+					break;
+				}
+			}
+		}
+	}
+
+	setCurrentPage(oldCurrentPage);
+
+	tooltip = tr("Copied %1 items on %2 pages").arg(selection.count()).arg(pages.size());
+}
+
 
 void ScribusDoc::itemSelection_ApplyImageEffects(ScImageEffectList& newEffectList, Selection* customSelection)
 {
diff --git a/scribus/scribusdoc.h b/scribus/scribusdoc.h
index 8ddb018bea..0bd0c1ff44 100644
--- a/scribus/scribusdoc.h
+++ b/scribus/scribusdoc.h
@@ -1512,7 +1512,7 @@ public slots:
 	void itemSelection_DistributeTop();
 	void itemSelection_SwapLeft();
 	void itemSelection_SwapRight();
-	void itemSelection_MultipleDuplicate(ItemMultipleDuplicateData&);
+	void itemSelection_MultipleDuplicate(const ItemMultipleDuplicateData&);
 	void itemSelection_UniteItems(Selection* customSelection = nullptr);
 	void itemSelection_SplitItems(Selection* customSelection = nullptr);
 	/**
@@ -1693,6 +1693,8 @@ public slots:
 	//flags used for indicating needs of updates
 	bool m_flag_notesChanged;
 
+	void multipleDuplicateByPage(const ItemMultipleDuplicateData& mdData, Selection& selection, QString& tooltip);
+
 public:
 	const QList<Mark*> marksList() { return m_docMarksList; }
 	const QList<TextNote*> notesList() { return m_docNotesList; }
diff --git a/scribus/ui/multipleduplicate.cpp b/scribus/ui/multipleduplicate.cpp
index eb5a65aec7..1834486d6e 100644
--- a/scribus/ui/multipleduplicate.cpp
+++ b/scribus/ui/multipleduplicate.cpp
@@ -13,21 +13,24 @@ for which a new license (GPL+exception) is in place.
 #include <QSpinBox>
 #include <QTabWidget>
 
+#include "iconmanager.h"
+#include "scribusdoc.h"
 #include "scrspinbox.h"
 #include "units.h"
+#include "ui/createrange.h"
 #include "usertaskstructs.h"
 
-MultipleDuplicate::MultipleDuplicate( int unitIndex, QWidget* parent) : QDialog(parent), m_unitIndex(unitIndex)
+MultipleDuplicate::MultipleDuplicate(QWidget* parent, ScribusDoc *doc) : QDialog(parent), m_Doc(doc)
 {
 	setupUi(this);
 
-	m_unitRatio = unitGetRatioFromIndex(m_unitIndex);
+	m_unitRatio = unitGetRatioFromIndex(m_Doc->unitIndex());
 
 	//set up mspinboxes
-	horizShiftSpinBox->setNewUnit(unitIndex);
-	vertShiftSpinBox->setNewUnit(unitIndex);
-	horizRCGapSpinBox->setNewUnit(unitIndex);
-	vertRCGapSpinBox->setNewUnit(unitIndex);
+	horizShiftSpinBox->setNewUnit(m_Doc->unitIndex());
+	vertShiftSpinBox->setNewUnit(m_Doc->unitIndex());
+	horizRCGapSpinBox->setNewUnit(m_Doc->unitIndex());
+	vertRCGapSpinBox->setNewUnit(m_Doc->unitIndex());
 	horizShiftSpinBox->setMinimum(-1000);
 	vertShiftSpinBox->setMinimum(-1000);
 	horizRCGapSpinBox->setMinimum(-1000);
@@ -43,9 +46,16 @@ MultipleDuplicate::MultipleDuplicate( int unitIndex, QWidget* parent) : QDialog(
 	
 	createGapRadioButton->setChecked(true);
 	setCopiesGap();
+
+	toolButtonPageRange->setIcon(IconManager::instance()->loadIcon("ellipsis.png"));
+	radioButtonPageAll->setChecked(true);
+
 	// signals and slots connections
 	connect(createGapRadioButton, SIGNAL(clicked()), this, SLOT(setCopiesGap()));
 	connect(shiftCreatedItemsRadioButton, SIGNAL(clicked()), this, SLOT(setCopiesShift()));
+
+	connect(lineEditPageRange, &QLineEdit::textChanged, this, &MultipleDuplicate::selectRangeOfPages);
+	connect(toolButtonPageRange, &QToolButton::clicked, this, &MultipleDuplicate::createPageNumberRange);
 }
 
 MultipleDuplicate::~MultipleDuplicate()
@@ -76,4 +86,36 @@ void MultipleDuplicate::getMultiplyData(ItemMultipleDuplicateData& mdData)
 	mdData.gridCols = gridColsSpinBox->value();
 	mdData.gridGapH = horizRCGapSpinBox->value();
 	mdData.gridGapV = vertRCGapSpinBox->value();
+	if (radioButtonPageAll->isChecked()) {
+		mdData.pageSelection = 1;
+	} else if (radioButtonPageEven->isChecked()) {
+		mdData.pageSelection = 2;
+	} else if (radioButtonPageOdd->isChecked()) {
+		mdData.pageSelection = 3;
+	} else if (radioButtonPageRange->isChecked()) {
+		mdData.pageSelection = 4;
+	}
+	mdData.pageRange = lineEditPageRange->text();
+	mdData.pageLinkText = checkBoxPageLinkText->isChecked();
+}
+
+void MultipleDuplicate::selectRangeOfPages()
+{
+	radioButtonPageRange->setChecked(true);
+}
+
+void MultipleDuplicate::createPageNumberRange()
+{
+	if (m_Doc!=0)
+	{
+		CreateRange cr(lineEditPageRange->text(), m_Doc->Pages->count(), this);
+		if (cr.exec())
+		{
+			CreateRangeData crData;
+			cr.getCreateRangeData(crData);
+			lineEditPageRange->setText(crData.pageRange);
+			return;
+		}
+	}
+	lineEditPageRange->setText(QString::null);
 }
diff --git a/scribus/ui/multipleduplicate.h b/scribus/ui/multipleduplicate.h
index c8abb74b4e..36c1bd1580 100644
--- a/scribus/ui/multipleduplicate.h
+++ b/scribus/ui/multipleduplicate.h
@@ -9,20 +9,25 @@ for which a new license (GPL+exception) is in place.
 
 #include "ui_multipleduplicate.h"
 struct ItemMultipleDuplicateData;
+class ScribusDoc;
 
 class MultipleDuplicate : public QDialog, Ui::MultipleDuplicate
 {
 	Q_OBJECT
 	public:
-		MultipleDuplicate(int unitIndex, QWidget* parent);
+        // InsertAFrame(QWidget* parent, ScribusDoc *doc);
+		MultipleDuplicate(QWidget* parent, ScribusDoc *doc);
 		~MultipleDuplicate();
 		void getMultiplyData(ItemMultipleDuplicateData&);
 	protected:
-		int m_unitIndex;
+		// int m_unitIndex;
+		ScribusDoc *m_Doc;
 		double m_unitRatio;
 	protected slots:
 		void setCopiesShift();
 		void setCopiesGap();
+        void selectRangeOfPages();
+        void createPageNumberRange();
 };
 
 #endif
diff --git a/scribus/ui/multipleduplicate.ui b/scribus/ui/multipleduplicate.ui
index cb3524b2c3..dc12a729dd 100644
--- a/scribus/ui/multipleduplicate.ui
+++ b/scribus/ui/multipleduplicate.ui
@@ -350,6 +350,95 @@
        </item>
       </layout>
      </widget>
+     <widget class="QWidget" name="tab3">
+      <attribute name="title">
+       <string>By &amp;Page</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout_8">
+       <item>
+        <layout class="QVBoxLayout" name="verticalLayout_7">
+         <item>
+          <widget class="QRadioButton" name="radioButtonPageAll">
+           <property name="text">
+            <string>&amp;All Following Pages</string>
+           </property>
+           <attribute name="buttonGroup">
+            <string notr="true">buttonGroup</string>
+           </attribute>
+          </widget>
+         </item>
+         <item>
+          <widget class="QRadioButton" name="radioButtonPageEven">
+           <property name="text">
+            <string>All Follwing &amp;Even Pages</string>
+           </property>
+           <attribute name="buttonGroup">
+            <string notr="true">buttonGroup</string>
+           </attribute>
+          </widget>
+         </item>
+         <item>
+          <widget class="QRadioButton" name="radioButtonPageOdd">
+           <property name="text">
+            <string>All Following &amp;Odd Pages</string>
+           </property>
+           <attribute name="buttonGroup">
+            <string notr="true">buttonGroup</string>
+           </attribute>
+          </widget>
+         </item>
+         <item>
+          <widget class="QRadioButton" name="radioButtonPageRange">
+           <property name="text">
+            <string>Ran&amp;ge of Pages:</string>
+           </property>
+           <attribute name="buttonGroup">
+            <string notr="true">buttonGroup</string>
+           </attribute>
+          </widget>
+         </item>
+         <item>
+          <layout class="QHBoxLayout" name="horizontalLayout">
+           <item>
+            <widget class="QLineEdit" name="lineEditPageRange"/>
+           </item>
+           <item>
+            <widget class="QToolButton" name="toolButtonPageRange">
+             <property name="text">
+              <string>...</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QVBoxLayout" name="verticalLayout_9">
+         <item>
+          <widget class="QCheckBox" name="checkBoxPageLinkText">
+           <property name="text">
+            <string>&amp;Link Text Frames</string>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <spacer name="verticalSpacer_5">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>40</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </widget>
     </widget>
    </item>
    <item>
@@ -468,4 +557,7 @@
    </hints>
   </connection>
  </connections>
+ <buttongroups>
+  <buttongroup name="buttonGroup"/>
+ </buttongroups>
 </ui>
diff --git a/scribus/usertaskstructs.h b/scribus/usertaskstructs.h
index bffd02d241..1d5a0cd0f9 100644
--- a/scribus/usertaskstructs.h
+++ b/scribus/usertaskstructs.h
@@ -34,16 +34,19 @@ struct InsertAFrameData
 
 struct ItemMultipleDuplicateData
 {
-	int type;
-	int copyCount;
-	int copyShiftOrGap;
-	double copyShiftGapH;
-	double copyShiftGapV;
-	double copyRotation;
-	int gridRows;
-	int gridCols;
-	double gridGapH;
-	double gridGapV;
+	int type = 0;
+	int copyCount = 0;
+	int copyShiftOrGap = 0;
+	double copyShiftGapH = 0.0;
+	double copyShiftGapV = 0.0;
+	double copyRotation = 0.0;
+	int gridRows = 0;
+	int gridCols = 0;
+	double gridGapH = 0.0;
+	double gridGapV = 0.0;
+	int pageSelection = 0; // 1 = All, 2 = Even, 3 = odd, 4 = Range
+	QString pageRange;
+	bool pageLinkText = false;
 };
 
 struct CreateRangeData
insert-frames.diff (16,877 bytes)   

ale

2019-01-24 12:28

manager   ~0045852

ping

jghali

2019-08-28 14:22

administrator   ~0046559

Here is the patch updated to current code.
15438_multiple_duplicate_pages.diff (16,617 bytes)   
Index: scribus/pageitem.cpp
===================================================================
--- scribus/pageitem.cpp	(revision 23163)
+++ scribus/pageitem.cpp	(working copy)
@@ -10025,6 +10025,27 @@
 	return last;
 }
 
+PageItem* PageItem::lastInChainSamePage()
+{
+	//huh? Q_ASSERT(this != nullptr);
+	PageItem* last = this;
+	while (last->OwnPage != OwnPage && last->nextInChain() != nullptr)
+		last = last->nextInChain();
+	return last;
+}
+
+bool PageItem::isAllNextInChainOnSamePage() const
+{
+	const PageItem* next = this;
+	while (next->nextInChain() != nullptr)
+	{
+		next = next->nextInChain();
+		if (next->OwnPage != this->OwnPage)
+			return false;
+	}
+	return true;
+}
+
 QRect PageItem::getRedrawBounding(double viewScale) const
 {
 	int x = qRound(floor(BoundingX - m_oldLineWidth / 2.0 - 5) * viewScale);
Index: scribus/pageitem.h
===================================================================
--- scribus/pageitem.h	(revision 23163)
+++ scribus/pageitem.h	(working copy)
@@ -467,11 +467,13 @@
 	//you can change all code for search first or last item in chain
 	PageItem* firstInChain();
 	PageItem* lastInChain();
+	PageItem* lastInChainSamePage();
 	PageItem* prevInChain() { return BackBox; }
 	PageItem* nextInChain() { return NextBox; }
 	const PageItem* prevInChain() const { return BackBox; }
 	const PageItem* nextInChain() const { return NextBox; }
 	bool isInChain() const { return ((BackBox != nullptr) || (NextBox != nullptr)); }
+	bool isAllNextInChainOnSamePage() const;
 
 	bool canBeLinkedTo(const PageItem* nextFrame) const;
 	void unlink(bool createUndo = true);
Index: scribus/pageitem_textframe.cpp
===================================================================
--- scribus/pageitem_textframe.cpp	(revision 23163)
+++ scribus/pageitem_textframe.cpp	(working copy)
@@ -1669,7 +1669,7 @@
 						realCharAscent = qMax(realCharAscent, gm.ascent);
 						gm = font.glyphBBox(gl.glyph, chsd / 10.0);
 						realAsce = qMax(realAsce, gm.ascent + gm.descent);
-						wide += gm.width;
+						wide += gm.width; // a.l.e: this sets the width of the indent
 					}
 					wide = (wide* scaleH) + (1 - scaleH);
 					realAsce = realAsce  * scaleV + offset;
Index: scribus/scribus.cpp
===================================================================
--- scribus/scribus.cpp	(revision 23163)
+++ scribus/scribus.cpp	(working copy)
@@ -61,7 +61,9 @@
 //#include <QQmlProperty>
 //>>
 #include <QRegExp>
+#include <memory>
 #include <QScopedPointer>
+#include <QScopedPointer>
 #include <QStyleFactory>
 #include <QTableWidget>
 #include <QTextCodec>
@@ -261,6 +263,7 @@
 #include "undostate.h"
 #include "units.h"
 #include "urllauncher.h"
+#include "usertaskstructs.h"
 #include "util.h"
 #include "util_file.h"
 #include "util_formats.h"
@@ -6456,7 +6459,6 @@
 		trans = m_undoManager->beginTransaction(Um::Selection, Um::IPolygon, Um::Duplicate, "", Um::IMultipleDuplicate);
 
 	ItemMultipleDuplicateData mdData;
-	memset(&mdData, 0, sizeof(mdData));
 	mdData.type = 0;
 	mdData.copyCount = 1;
 	mdData.copyShiftOrGap = 0;
@@ -6492,7 +6494,7 @@
 	if (doc->appMode == modeEditClip)
 		view->requestMode(submodeEndNodeEdit);
 	internalCopy = true;
-	QScopedPointer<MultipleDuplicate> dia(new MultipleDuplicate(doc->unitIndex(), this));
+	QScopedPointer<MultipleDuplicate> dia(new MultipleDuplicate(this, doc));
 	if (dia->exec())
 	{
 		ItemMultipleDuplicateData mdData;
Index: scribus/scribusdoc.cpp
===================================================================
--- scribus/scribusdoc.cpp	(revision 23163)
+++ scribus/scribusdoc.cpp	(working copy)
@@ -38,6 +38,7 @@
 #include <QProgressBar>
 #include <QtAlgorithms>
 #include <QTime>
+#include <QStringList>
 //#include <qtconcurrentmap.h>
 
 #include "actionmanager.h"
@@ -13442,10 +13443,14 @@
 	return nr;
 }
 
-void ScribusDoc::itemSelection_MultipleDuplicate(ItemMultipleDuplicateData& mdData)
+void ScribusDoc::itemSelection_MultipleDuplicate(const ItemMultipleDuplicateData& mdData)
 {
 	if ((mdData.type==0 && mdData.copyCount<1) || (mdData.type==1 && (mdData.gridRows==1 && mdData.gridCols==1)))
 		return;
+	if ((mdData.type == 2) && (mdData.pageSelection != 4) && (currentPageNumber() == Pages->count() - 1))
+		// cannot copy on following pages, while being on the last page
+		return;
+
 	QString tooltip;
 	UndoTransaction activeTransaction;
 
@@ -13572,6 +13577,10 @@
 		QString vString = QString::number(mdData.gridGapV, 'f', unitPrecision) + " " + unitSuffix;
 		tooltip = tr("Number of rows: %1\nNumber of columns: %2\nHorizontal gap: %3\nVertical gap: %4").arg(mdData.gridRows).arg(mdData.gridCols).arg(hString).arg(vString).arg(unitSuffix);
 	}
+	else if (mdData.type==2)
+	{
+		multipleDuplicateByPage(mdData, selection, tooltip);
+	}
 	if (activeTransaction)
 	{
 		activeTransaction.commit("", nullptr, "", tooltip, nullptr);
@@ -13584,7 +13593,90 @@
 	changed();
 }
 
+void ScribusDoc::multipleDuplicateByPage(const ItemMultipleDuplicateData& dialogData, Selection& selection, QString& tooltip)
+{
+	std::vector<int> pages;
+	QString pageRange;
+	if (dialogData.pageSelection == 1)
+		pageRange = QString("%1-%2").arg(currentPageNumber() + 2).arg(Pages->count());
+	else if ((dialogData.pageSelection == 2) || dialogData.pageSelection == 3)
+	{
+		int start = currentPageNumber() + 2;
+		// round to the next odd / even number
+		if (dialogData.pageSelection == 2)
+			start += start % 2;
+		else
+			start += 1 - (start % 2);
 
+		QStringList pageList;
+		for (int i = start; i <= Pages->count(); i += 2) {
+			pageList << QString::number(i);
+		}
+		pageRange = pageList.join(',');
+	}
+	else if (dialogData.pageSelection == 4)
+	{
+		pageRange = dialogData.pageRange;
+	}
+	parsePagesString(pageRange, &pages, Pages->count());
+
+	PageItem* lastInChain = nullptr;
+	if (dialogData.pageLinkText)
+	{
+		for (auto item: selection.items())
+		{
+			// get the first text frame in the selection, the first frame in the chain
+			if (item->itemType() != PageItem::TextFrame)
+				continue;
+			if (item->isInChain() && item->isAllNextInChainOnSamePage())
+			{
+				lastInChain = item->lastInChain();
+				assert( !lastInChain->nextInChain() );
+			}
+			else
+			{
+				lastInChain = item;
+			}
+			break;
+		}
+	}
+
+	ScPage* oldCurrentPage = currentPage();
+	ScriXmlDoc xmlStream;
+	QString buffer = xmlStream.writeElem(this, &selection);
+	for (const auto page: pages)
+	{
+		ScPage* targetPage = Pages->at(page - 1);
+		setCurrentPage(targetPage);
+		auto countBeforeInsert = Items->count();
+		xmlStream.readElem(buffer, m_appPrefsData.fontPrefs.AvailFonts, this, currentPage()->xOffset(), currentPage()->yOffset(), false, true, m_appPrefsData.fontPrefs.GFontSub);
+		if (!lastInChain)
+			continue;
+		for (auto i = countBeforeInsert; i < Items->count(); ++i)
+		{
+			auto item = Items->at(i);
+			if (item->itemType() != PageItem::TextFrame)
+				continue;
+			if (item->isInChain())
+			{
+				lastInChain->link(item->firstInChain());
+				lastInChain = item->lastInChain();
+			}
+			else
+			{
+				lastInChain->link(item);
+				lastInChain = item;
+			}
+			break;
+		}
+	}
+
+	setCurrentPage(oldCurrentPage);
+
+	tooltip = tr("Copied %1 items on %2 pages").arg(selection.count()).arg(pages.size());
+}
+
+
 void ScribusDoc::itemSelection_ApplyImageEffects(ScImageEffectList& newEffectList, Selection* customSelection)
 {
 	Selection* itemSelection = (customSelection!=nullptr) ? customSelection : m_Selection;
Index: scribus/scribusdoc.h
===================================================================
--- scribus/scribusdoc.h	(revision 23163)
+++ scribus/scribusdoc.h	(working copy)
@@ -1527,7 +1527,7 @@
 	void itemSelection_DistributeTop();
 	void itemSelection_SwapLeft();
 	void itemSelection_SwapRight();
-	void itemSelection_MultipleDuplicate(ItemMultipleDuplicateData&);
+	void itemSelection_MultipleDuplicate(const ItemMultipleDuplicateData&);
 	void itemSelection_UniteItems(Selection* customSelection = nullptr);
 	void itemSelection_SplitItems(Selection* customSelection = nullptr);
 	/**
@@ -1708,6 +1708,8 @@
 	//flags used for indicating needs of updates
 	bool m_flag_notesChanged {false};
 
+	void multipleDuplicateByPage(const ItemMultipleDuplicateData& mdData, Selection& selection, QString& tooltip);
+
 public:
 	const QList<Mark*> marksList() { return m_docMarksList; }
 	const QList<TextNote*> notesList() { return m_docNotesList; }
Index: scribus/ui/multipleduplicate.cpp
===================================================================
--- scribus/ui/multipleduplicate.cpp	(revision 23163)
+++ scribus/ui/multipleduplicate.cpp	(working copy)
@@ -13,21 +13,24 @@
 #include <QSpinBox>
 #include <QTabWidget>
 
+#include "iconmanager.h"
+#include "scribusdoc.h"
 #include "scrspinbox.h"
 #include "units.h"
+#include "ui/createrange.h"
 #include "usertaskstructs.h"
 
-MultipleDuplicate::MultipleDuplicate( int unitIndex, QWidget* parent) : QDialog(parent), m_unitIndex(unitIndex)
+MultipleDuplicate::MultipleDuplicate(QWidget* parent, ScribusDoc *doc) : QDialog(parent), m_Doc(doc)
 {
 	setupUi(this);
 
-	m_unitRatio = unitGetRatioFromIndex(m_unitIndex);
+	m_unitRatio = unitGetRatioFromIndex(m_Doc->unitIndex());
 
 	//set up mspinboxes
-	horizShiftSpinBox->setNewUnit(unitIndex);
-	vertShiftSpinBox->setNewUnit(unitIndex);
-	horizRCGapSpinBox->setNewUnit(unitIndex);
-	vertRCGapSpinBox->setNewUnit(unitIndex);
+	horizShiftSpinBox->setNewUnit(m_Doc->unitIndex());
+	vertShiftSpinBox->setNewUnit(m_Doc->unitIndex());
+	horizRCGapSpinBox->setNewUnit(m_Doc->unitIndex());
+	vertRCGapSpinBox->setNewUnit(m_Doc->unitIndex());
 	horizShiftSpinBox->setMinimum(-1000);
 	vertShiftSpinBox->setMinimum(-1000);
 	horizRCGapSpinBox->setMinimum(-1000);
@@ -43,9 +46,16 @@
 	
 	createGapRadioButton->setChecked(true);
 	setCopiesGap();
+
+	toolButtonPageRange->setIcon(IconManager::instance().loadIcon("ellipsis.png"));
+	radioButtonPageAll->setChecked(true);
+
 	// signals and slots connections
 	connect(createGapRadioButton, SIGNAL(clicked()), this, SLOT(setCopiesGap()));
 	connect(shiftCreatedItemsRadioButton, SIGNAL(clicked()), this, SLOT(setCopiesShift()));
+
+	connect(lineEditPageRange, &QLineEdit::textChanged, this, &MultipleDuplicate::selectRangeOfPages);
+	connect(toolButtonPageRange, &QToolButton::clicked, this, &MultipleDuplicate::createPageNumberRange);
 }
 
 MultipleDuplicate::~MultipleDuplicate()
@@ -76,4 +86,36 @@
 	mdData.gridCols = gridColsSpinBox->value();
 	mdData.gridGapH = horizRCGapSpinBox->value();
 	mdData.gridGapV = vertRCGapSpinBox->value();
+	if (radioButtonPageAll->isChecked()) {
+		mdData.pageSelection = 1;
+	} else if (radioButtonPageEven->isChecked()) {
+		mdData.pageSelection = 2;
+	} else if (radioButtonPageOdd->isChecked()) {
+		mdData.pageSelection = 3;
+	} else if (radioButtonPageRange->isChecked()) {
+		mdData.pageSelection = 4;
+	}
+	mdData.pageRange = lineEditPageRange->text();
+	mdData.pageLinkText = checkBoxPageLinkText->isChecked();
 }
+
+void MultipleDuplicate::selectRangeOfPages()
+{
+	radioButtonPageRange->setChecked(true);
+}
+
+void MultipleDuplicate::createPageNumberRange()
+{
+	if (m_Doc!=0)
+	{
+		CreateRange cr(lineEditPageRange->text(), m_Doc->Pages->count(), this);
+		if (cr.exec())
+		{
+			CreateRangeData crData;
+			cr.getCreateRangeData(crData);
+			lineEditPageRange->setText(crData.pageRange);
+			return;
+		}
+	}
+	lineEditPageRange->setText(QString::null);
+}
Index: scribus/ui/multipleduplicate.h
===================================================================
--- scribus/ui/multipleduplicate.h	(revision 23163)
+++ scribus/ui/multipleduplicate.h	(working copy)
@@ -9,20 +9,25 @@
 
 #include "ui_multipleduplicate.h"
 struct ItemMultipleDuplicateData;
+class ScribusDoc;
 
 class MultipleDuplicate : public QDialog, Ui::MultipleDuplicate
 {
 	Q_OBJECT
 	public:
-		MultipleDuplicate(int unitIndex, QWidget* parent);
+        // InsertAFrame(QWidget* parent, ScribusDoc *doc);
+		MultipleDuplicate(QWidget* parent, ScribusDoc *doc);
 		~MultipleDuplicate();
 		void getMultiplyData(ItemMultipleDuplicateData&);
 	protected:
-		int m_unitIndex;
+		// int m_unitIndex;
+		ScribusDoc *m_Doc;
 		double m_unitRatio;
 	protected slots:
 		void setCopiesShift();
 		void setCopiesGap();
+        void selectRangeOfPages();
+        void createPageNumberRange();
 };
 
 #endif
Index: scribus/ui/multipleduplicate.ui
===================================================================
--- scribus/ui/multipleduplicate.ui	(revision 23163)
+++ scribus/ui/multipleduplicate.ui	(working copy)
@@ -350,6 +350,95 @@
        </item>
       </layout>
      </widget>
+     <widget class="QWidget" name="tab3">
+      <attribute name="title">
+       <string>By &amp;Page</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout_8">
+       <item>
+        <layout class="QVBoxLayout" name="verticalLayout_7">
+         <item>
+          <widget class="QRadioButton" name="radioButtonPageAll">
+           <property name="text">
+            <string>&amp;All Following Pages</string>
+           </property>
+           <attribute name="buttonGroup">
+            <string notr="true">buttonGroup</string>
+           </attribute>
+          </widget>
+         </item>
+         <item>
+          <widget class="QRadioButton" name="radioButtonPageEven">
+           <property name="text">
+            <string>All Following &amp;Even Pages</string>
+           </property>
+           <attribute name="buttonGroup">
+            <string notr="true">buttonGroup</string>
+           </attribute>
+          </widget>
+         </item>
+         <item>
+          <widget class="QRadioButton" name="radioButtonPageOdd">
+           <property name="text">
+            <string>All Following &amp;Odd Pages</string>
+           </property>
+           <attribute name="buttonGroup">
+            <string notr="true">buttonGroup</string>
+           </attribute>
+          </widget>
+         </item>
+         <item>
+          <widget class="QRadioButton" name="radioButtonPageRange">
+           <property name="text">
+            <string>Ran&amp;ge of Pages:</string>
+           </property>
+           <attribute name="buttonGroup">
+            <string notr="true">buttonGroup</string>
+           </attribute>
+          </widget>
+         </item>
+         <item>
+          <layout class="QHBoxLayout" name="horizontalLayout">
+           <item>
+            <widget class="QLineEdit" name="lineEditPageRange"/>
+           </item>
+           <item>
+            <widget class="QToolButton" name="toolButtonPageRange">
+             <property name="text">
+              <string>...</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QVBoxLayout" name="verticalLayout_9">
+         <item>
+          <widget class="QCheckBox" name="checkBoxPageLinkText">
+           <property name="text">
+            <string>&amp;Link Text Frames</string>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <spacer name="verticalSpacer_5">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>40</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </widget>
     </widget>
    </item>
    <item>
@@ -468,4 +557,7 @@
    </hints>
   </connection>
  </connections>
+ <buttongroups>
+  <buttongroup name="buttonGroup"/>
+ </buttongroups>
 </ui>
Index: scribus/usertaskstructs.h
===================================================================
--- scribus/usertaskstructs.h	(revision 23163)
+++ scribus/usertaskstructs.h	(working copy)
@@ -34,16 +34,19 @@
 
 struct ItemMultipleDuplicateData
 {
-	int type;
-	int copyCount;
-	int copyShiftOrGap;
-	double copyShiftGapH;
-	double copyShiftGapV;
-	double copyRotation;
-	int gridRows;
-	int gridCols;
-	double gridGapH;
-	double gridGapV;
+	int type = 0;
+	int copyCount = 0;
+	int copyShiftOrGap = 0;
+	double copyShiftGapH = 0.0;
+	double copyShiftGapV = 0.0;
+	double copyRotation = 0.0;
+	int gridRows = 0;
+	int gridCols = 0;
+	double gridGapH = 0.0;
+	double gridGapV = 0.0;
+	int pageSelection = 0; // 1 = All, 2 = Even, 3 = odd, 4 = Range
+	QString pageRange;
+	bool pageLinkText = false;
 };
 
 struct CreateRangeData

jghali

2019-08-28 14:23

administrator   ~0046560

Last edited: 2019-08-28 14:23

A basic test shows that frame linking is not correct if you try to duplicate a selection of two linked frames located on same page.

jghali

2019-08-28 14:29

administrator   ~0046561

The options should be also:
- All Pages
- Even Pages
- Odd Pages
- Range of Pages
No need to limit us to following pages. You can just skip the current page when performing the copy.

jghali

2019-08-28 14:58

administrator   ~0046563

Not sure in the end if we should provide that "Link text" option...

jghali

2019-08-28 15:21

administrator   ~0046564

Here is a patch where I have renamed the "All Following *" options to Even / Odd pages and did the appropriate modifications + some minor cleanups. I will have to get a closer look at how this "Link Text" option works.
15438_multiple_duplicate_pages_v2.diff (15,636 bytes)   
Index: scribus/pageitem.cpp
===================================================================
--- scribus/pageitem.cpp	(revision 23163)
+++ scribus/pageitem.cpp	(working copy)
@@ -10025,6 +10025,27 @@
 	return last;
 }
 
+PageItem* PageItem::lastInChainSamePage()
+{
+	//huh? Q_ASSERT(this != nullptr);
+	PageItem* last = this;
+	while (last->OwnPage != OwnPage && last->nextInChain() != nullptr)
+		last = last->nextInChain();
+	return last;
+}
+
+bool PageItem::isAllNextInChainOnSamePage() const
+{
+	const PageItem* next = this;
+	while (next->nextInChain() != nullptr)
+	{
+		next = next->nextInChain();
+		if (next->OwnPage != this->OwnPage)
+			return false;
+	}
+	return true;
+}
+
 QRect PageItem::getRedrawBounding(double viewScale) const
 {
 	int x = qRound(floor(BoundingX - m_oldLineWidth / 2.0 - 5) * viewScale);
Index: scribus/pageitem.h
===================================================================
--- scribus/pageitem.h	(revision 23163)
+++ scribus/pageitem.h	(working copy)
@@ -467,11 +467,13 @@
 	//you can change all code for search first or last item in chain
 	PageItem* firstInChain();
 	PageItem* lastInChain();
+	PageItem* lastInChainSamePage();
 	PageItem* prevInChain() { return BackBox; }
 	PageItem* nextInChain() { return NextBox; }
 	const PageItem* prevInChain() const { return BackBox; }
 	const PageItem* nextInChain() const { return NextBox; }
 	bool isInChain() const { return ((BackBox != nullptr) || (NextBox != nullptr)); }
+	bool isAllNextInChainOnSamePage() const;
 
 	bool canBeLinkedTo(const PageItem* nextFrame) const;
 	void unlink(bool createUndo = true);
Index: scribus/scribus.cpp
===================================================================
--- scribus/scribus.cpp	(revision 23163)
+++ scribus/scribus.cpp	(working copy)
@@ -93,7 +93,6 @@
 #include <csignal>
 #include <string>
 
-
 #include "actionmanager.h"
 #include "actionsearch.h"
 #include "appmodehelper.h"
@@ -261,6 +260,7 @@
 #include "undostate.h"
 #include "units.h"
 #include "urllauncher.h"
+#include "usertaskstructs.h"
 #include "util.h"
 #include "util_file.h"
 #include "util_formats.h"
@@ -6456,7 +6456,6 @@
 		trans = m_undoManager->beginTransaction(Um::Selection, Um::IPolygon, Um::Duplicate, "", Um::IMultipleDuplicate);
 
 	ItemMultipleDuplicateData mdData;
-	memset(&mdData, 0, sizeof(mdData));
 	mdData.type = 0;
 	mdData.copyCount = 1;
 	mdData.copyShiftOrGap = 0;
@@ -6492,7 +6491,7 @@
 	if (doc->appMode == modeEditClip)
 		view->requestMode(submodeEndNodeEdit);
 	internalCopy = true;
-	QScopedPointer<MultipleDuplicate> dia(new MultipleDuplicate(doc->unitIndex(), this));
+	QScopedPointer<MultipleDuplicate> dia(new MultipleDuplicate(this, doc));
 	if (dia->exec())
 	{
 		ItemMultipleDuplicateData mdData;
Index: scribus/scribusdoc.cpp
===================================================================
--- scribus/scribusdoc.cpp	(revision 23163)
+++ scribus/scribusdoc.cpp	(working copy)
@@ -13442,10 +13442,13 @@
 	return nr;
 }
 
-void ScribusDoc::itemSelection_MultipleDuplicate(ItemMultipleDuplicateData& mdData)
+void ScribusDoc::itemSelection_MultipleDuplicate(const ItemMultipleDuplicateData& mdData)
 {
 	if ((mdData.type==0 && mdData.copyCount<1) || (mdData.type==1 && (mdData.gridRows==1 && mdData.gridCols==1)))
 		return;
+	if ((mdData.type == 2) && (Pages->count() == 1))
+		return;
+
 	QString tooltip;
 	UndoTransaction activeTransaction;
 
@@ -13572,6 +13575,10 @@
 		QString vString = QString::number(mdData.gridGapV, 'f', unitPrecision) + " " + unitSuffix;
 		tooltip = tr("Number of rows: %1\nNumber of columns: %2\nHorizontal gap: %3\nVertical gap: %4").arg(mdData.gridRows).arg(mdData.gridCols).arg(hString).arg(vString).arg(unitSuffix);
 	}
+	else if (mdData.type == 2)
+	{
+		multipleDuplicateByPage(mdData, selection, tooltip);
+	}
 	if (activeTransaction)
 	{
 		activeTransaction.commit("", nullptr, "", tooltip, nullptr);
@@ -13584,7 +13591,88 @@
 	changed();
 }
 
+void ScribusDoc::multipleDuplicateByPage(const ItemMultipleDuplicateData& dialogData, Selection& selection, QString& tooltip)
+{
+	int currPageNumber = currentPageNumber();
+	std::vector<int> pages;
+	QString pageRange;
 
+	if (dialogData.pageSelection == 1)
+		pageRange = QString("%1-%2").arg(1).arg(Pages->count());
+	else if ((dialogData.pageSelection == 2) || dialogData.pageSelection == 3)
+	{
+		int start = (dialogData.pageSelection == 2) ? 0 : 1;
+
+		QStringList pageList;
+		for (int i = start; i <= Pages->count(); i += 2)
+			pageList << QString::number(i);
+		pageRange = pageList.join(',');
+	}
+	else if (dialogData.pageSelection == 4)
+	{
+		pageRange = dialogData.pageRange;
+	}
+	parsePagesString(pageRange, &pages, Pages->count());
+
+	PageItem* lastInChain = nullptr;
+	if (dialogData.pageLinkText)
+	{
+		for (auto item: selection.items())
+		{
+			// get the first text frame in the selection, the first frame in the chain
+			if (item->itemType() != PageItem::TextFrame)
+				continue;
+			if (item->isInChain() && item->isAllNextInChainOnSamePage())
+			{
+				lastInChain = item->lastInChain();
+				assert( !lastInChain->nextInChain() );
+			}
+			else
+			{
+				lastInChain = item;
+			}
+			break;
+		}
+	}
+
+	ScPage* oldCurrentPage = currentPage();
+	ScriXmlDoc xmlStream;
+	QString buffer = xmlStream.writeElem(this, &selection);
+	for (const auto page: pages)
+	{
+		if (currPageNumber == page - 1)
+			continue;
+		ScPage* targetPage = Pages->at(page - 1);
+		setCurrentPage(targetPage);
+		auto countBeforeInsert = Items->count();
+		xmlStream.readElem(buffer, m_appPrefsData.fontPrefs.AvailFonts, this, currentPage()->xOffset(), currentPage()->yOffset(), false, true, m_appPrefsData.fontPrefs.GFontSub);
+		if (!lastInChain)
+			continue;
+		for (auto i = countBeforeInsert; i < Items->count(); ++i)
+		{
+			auto item = Items->at(i);
+			if (item->itemType() != PageItem::TextFrame)
+				continue;
+			if (item->isInChain())
+			{
+				lastInChain->link(item->firstInChain());
+				lastInChain = item->lastInChain();
+			}
+			else
+			{
+				lastInChain->link(item);
+				lastInChain = item;
+			}
+			break;
+		}
+	}
+
+	setCurrentPage(oldCurrentPage);
+
+	tooltip = tr("Copied %1 items on %2 pages").arg(selection.count()).arg(pages.size());
+}
+
+
 void ScribusDoc::itemSelection_ApplyImageEffects(ScImageEffectList& newEffectList, Selection* customSelection)
 {
 	Selection* itemSelection = (customSelection!=nullptr) ? customSelection : m_Selection;
Index: scribus/scribusdoc.h
===================================================================
--- scribus/scribusdoc.h	(revision 23163)
+++ scribus/scribusdoc.h	(working copy)
@@ -1527,7 +1527,7 @@
 	void itemSelection_DistributeTop();
 	void itemSelection_SwapLeft();
 	void itemSelection_SwapRight();
-	void itemSelection_MultipleDuplicate(ItemMultipleDuplicateData&);
+	void itemSelection_MultipleDuplicate(const ItemMultipleDuplicateData&);
 	void itemSelection_UniteItems(Selection* customSelection = nullptr);
 	void itemSelection_SplitItems(Selection* customSelection = nullptr);
 	/**
@@ -1708,6 +1708,8 @@
 	//flags used for indicating needs of updates
 	bool m_flag_notesChanged {false};
 
+	void multipleDuplicateByPage(const ItemMultipleDuplicateData& mdData, Selection& selection, QString& tooltip);
+
 public:
 	const QList<Mark*> marksList() { return m_docMarksList; }
 	const QList<TextNote*> notesList() { return m_docNotesList; }
Index: scribus/ui/multipleduplicate.cpp
===================================================================
--- scribus/ui/multipleduplicate.cpp	(revision 23163)
+++ scribus/ui/multipleduplicate.cpp	(working copy)
@@ -13,21 +13,24 @@
 #include <QSpinBox>
 #include <QTabWidget>
 
+#include "iconmanager.h"
+#include "scribusdoc.h"
 #include "scrspinbox.h"
 #include "units.h"
+#include "ui/createrange.h"
 #include "usertaskstructs.h"
 
-MultipleDuplicate::MultipleDuplicate( int unitIndex, QWidget* parent) : QDialog(parent), m_unitIndex(unitIndex)
+MultipleDuplicate::MultipleDuplicate(QWidget* parent, ScribusDoc *doc) : QDialog(parent), m_Doc(doc)
 {
 	setupUi(this);
 
-	m_unitRatio = unitGetRatioFromIndex(m_unitIndex);
+	m_unitRatio = unitGetRatioFromIndex(m_Doc->unitIndex());
 
 	//set up mspinboxes
-	horizShiftSpinBox->setNewUnit(unitIndex);
-	vertShiftSpinBox->setNewUnit(unitIndex);
-	horizRCGapSpinBox->setNewUnit(unitIndex);
-	vertRCGapSpinBox->setNewUnit(unitIndex);
+	horizShiftSpinBox->setNewUnit(m_Doc->unitIndex());
+	vertShiftSpinBox->setNewUnit(m_Doc->unitIndex());
+	horizRCGapSpinBox->setNewUnit(m_Doc->unitIndex());
+	vertRCGapSpinBox->setNewUnit(m_Doc->unitIndex());
 	horizShiftSpinBox->setMinimum(-1000);
 	vertShiftSpinBox->setMinimum(-1000);
 	horizRCGapSpinBox->setMinimum(-1000);
@@ -43,9 +46,16 @@
 	
 	createGapRadioButton->setChecked(true);
 	setCopiesGap();
+
+	toolButtonPageRange->setIcon(IconManager::instance().loadIcon("ellipsis.png"));
+	radioButtonPageAll->setChecked(true);
+
 	// signals and slots connections
 	connect(createGapRadioButton, SIGNAL(clicked()), this, SLOT(setCopiesGap()));
 	connect(shiftCreatedItemsRadioButton, SIGNAL(clicked()), this, SLOT(setCopiesShift()));
+
+	connect(lineEditPageRange, &QLineEdit::textChanged, this, &MultipleDuplicate::selectRangeOfPages);
+	connect(toolButtonPageRange, &QToolButton::clicked, this, &MultipleDuplicate::createPageNumberRange);
 }
 
 MultipleDuplicate::~MultipleDuplicate()
@@ -76,4 +86,35 @@
 	mdData.gridCols = gridColsSpinBox->value();
 	mdData.gridGapH = horizRCGapSpinBox->value();
 	mdData.gridGapV = vertRCGapSpinBox->value();
+	if (radioButtonPageAll->isChecked())
+		mdData.pageSelection = 1;
+	else if (radioButtonPageEven->isChecked())
+		mdData.pageSelection = 2;
+	else if (radioButtonPageOdd->isChecked())
+		mdData.pageSelection = 3;
+	else if (radioButtonPageRange->isChecked())
+		mdData.pageSelection = 4;
+	mdData.pageRange = lineEditPageRange->text();
+	mdData.pageLinkText = checkBoxPageLinkText->isChecked();
 }
+
+void MultipleDuplicate::selectRangeOfPages()
+{
+	radioButtonPageRange->setChecked(true);
+}
+
+void MultipleDuplicate::createPageNumberRange()
+{
+	if (m_Doc!=0)
+	{
+		CreateRange cr(lineEditPageRange->text(), m_Doc->Pages->count(), this);
+		if (cr.exec())
+		{
+			CreateRangeData crData;
+			cr.getCreateRangeData(crData);
+			lineEditPageRange->setText(crData.pageRange);
+			return;
+		}
+	}
+	lineEditPageRange->setText(QString::null);
+}
Index: scribus/ui/multipleduplicate.h
===================================================================
--- scribus/ui/multipleduplicate.h	(revision 23163)
+++ scribus/ui/multipleduplicate.h	(working copy)
@@ -9,20 +9,24 @@
 
 #include "ui_multipleduplicate.h"
 struct ItemMultipleDuplicateData;
+class ScribusDoc;
 
 class MultipleDuplicate : public QDialog, Ui::MultipleDuplicate
 {
 	Q_OBJECT
 	public:
-		MultipleDuplicate(int unitIndex, QWidget* parent);
+		MultipleDuplicate(QWidget* parent, ScribusDoc *doc);
 		~MultipleDuplicate();
 		void getMultiplyData(ItemMultipleDuplicateData&);
 	protected:
-		int m_unitIndex;
+		// int m_unitIndex;
+		ScribusDoc *m_Doc;
 		double m_unitRatio;
 	protected slots:
 		void setCopiesShift();
 		void setCopiesGap();
+		void selectRangeOfPages();
+		void createPageNumberRange();
 };
 
 #endif
Index: scribus/ui/multipleduplicate.ui
===================================================================
--- scribus/ui/multipleduplicate.ui	(revision 23163)
+++ scribus/ui/multipleduplicate.ui	(working copy)
@@ -9,7 +9,7 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>300</width>
+    <width>329</width>
     <height>302</height>
    </rect>
   </property>
@@ -350,6 +350,95 @@
        </item>
       </layout>
      </widget>
+     <widget class="QWidget" name="tab3">
+      <attribute name="title">
+       <string>By &amp;Page</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout_8">
+       <item>
+        <layout class="QVBoxLayout" name="verticalLayout_7">
+         <item>
+          <widget class="QRadioButton" name="radioButtonPageAll">
+           <property name="text">
+            <string>&amp;All Pages</string>
+           </property>
+           <attribute name="buttonGroup">
+            <string notr="true">buttonGroup</string>
+           </attribute>
+          </widget>
+         </item>
+         <item>
+          <widget class="QRadioButton" name="radioButtonPageEven">
+           <property name="text">
+            <string>All &amp;Even Pages</string>
+           </property>
+           <attribute name="buttonGroup">
+            <string notr="true">buttonGroup</string>
+           </attribute>
+          </widget>
+         </item>
+         <item>
+          <widget class="QRadioButton" name="radioButtonPageOdd">
+           <property name="text">
+            <string>All &amp;Odd Pages</string>
+           </property>
+           <attribute name="buttonGroup">
+            <string notr="true">buttonGroup</string>
+           </attribute>
+          </widget>
+         </item>
+         <item>
+          <widget class="QRadioButton" name="radioButtonPageRange">
+           <property name="text">
+            <string>Ran&amp;ge of Pages:</string>
+           </property>
+           <attribute name="buttonGroup">
+            <string notr="true">buttonGroup</string>
+           </attribute>
+          </widget>
+         </item>
+         <item>
+          <layout class="QHBoxLayout" name="horizontalLayout">
+           <item>
+            <widget class="QLineEdit" name="lineEditPageRange"/>
+           </item>
+           <item>
+            <widget class="QToolButton" name="toolButtonPageRange">
+             <property name="text">
+              <string>...</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QVBoxLayout" name="verticalLayout_9">
+         <item>
+          <widget class="QCheckBox" name="checkBoxPageLinkText">
+           <property name="text">
+            <string>&amp;Link Text Frames</string>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <spacer name="verticalSpacer_5">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>40</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </widget>
     </widget>
    </item>
    <item>
@@ -468,4 +557,7 @@
    </hints>
   </connection>
  </connections>
+ <buttongroups>
+  <buttongroup name="buttonGroup"/>
+ </buttongroups>
 </ui>
Index: scribus/usertaskstructs.h
===================================================================
--- scribus/usertaskstructs.h	(revision 23163)
+++ scribus/usertaskstructs.h	(working copy)
@@ -34,16 +34,19 @@
 
 struct ItemMultipleDuplicateData
 {
-	int type;
-	int copyCount;
-	int copyShiftOrGap;
-	double copyShiftGapH;
-	double copyShiftGapV;
-	double copyRotation;
-	int gridRows;
-	int gridCols;
-	double gridGapH;
-	double gridGapV;
+	int type = 0;
+	int copyCount = 0;
+	int copyShiftOrGap = 0;
+	double copyShiftGapH = 0.0;
+	double copyShiftGapV = 0.0;
+	double copyRotation = 0.0;
+	int gridRows = 0;
+	int gridCols = 0;
+	double gridGapH = 0.0;
+	double gridGapV = 0.0;
+	int pageSelection = 0; // 1 = All, 2 = Even, 3 = Odd, 4 = Range
+	QString pageRange;
+	bool pageLinkText = false;
 };
 
 struct CreateRangeData

ale

2019-08-29 08:21

manager   ~0046567

as discussed in irc: please, consider reverting the changes and let the duplicate apply only to the following pages.

i really don't see a use case where somebody defines something on page 4 and wants it applied to the pages 1 to 8...
and if there are such cases, it's trivial to cut and paste the item on page 1 and then duplicate it.

i really think that the "normal" use case is to duplicate an item on the next n pages, and i would optimize the UI around that.
obviously, we should make sure that other uses are possible and easy.

jghali

2019-09-01 14:22

administrator   ~0046596

Done!

Issue History

Date Modified Username Field Change
2018-09-25 08:49 ale New Issue
2018-09-25 08:49 ale Assigned To => ale
2018-09-25 08:49 ale Status new => assigned
2018-09-25 09:22 jghali Category - => General
2018-09-25 11:24 ale Relationship added related to 0010557
2018-09-27 07:30 ale File Added: insert-frames.diff
2018-09-27 07:30 ale Note Added: 0045485
2018-09-27 07:30 ale Summary multiple duplicate on pages => [PATCH] multiple duplicate on pages
2018-09-27 07:30 ale Patch No => Yes
2019-01-24 12:28 ale Note Added: 0045852
2019-08-28 14:22 jghali File Added: 15438_multiple_duplicate_pages.diff
2019-08-28 14:22 jghali Note Added: 0046559
2019-08-28 14:23 jghali Note Added: 0046560
2019-08-28 14:23 jghali Note Edited: 0046560
2019-08-28 14:29 jghali Note Added: 0046561
2019-08-28 14:58 jghali Note Added: 0046563
2019-08-28 15:21 jghali File Added: 15438_multiple_duplicate_pages_v2.diff
2019-08-28 15:21 jghali Note Added: 0046564
2019-08-29 08:21 ale Note Added: 0046567
2019-09-01 14:22 jghali Summary [PATCH] multiple duplicate on pages => Multiple duplicate on pages
2019-09-01 14:22 jghali Status assigned => resolved
2019-09-01 14:22 jghali Resolution open => fixed
2019-09-01 14:22 jghali Fixed in Version => 1.5.6.svn
2019-09-01 14:22 jghali Note Added: 0046596
2019-12-08 21:24 cbradney Status resolved => closed