View Issue Details

IDProjectCategoryView StatusLast Update
0012734ScribusCanvaspublic2016-04-04 18:19
Reportermecirt Assigned To 
PrioritynormalSeverityfeatureReproducibilityhave not tried
Status newResolutionopen 
Product Version1.5.0svn 
Summary0012734: [PATCH] improved Duplicate feature
DescriptionThe attached patch improves the Multiple Duplicate feature as follows:
- add ability to duplicate on different pages - this creates copies of the object on the next N pages, creating these pages if needed
- add ability to link created text frames - if duplicating a text frame, this option automatically links the new ones with the original one, flowing in the overset text
- some code cleanup

The autolinking is mainly meant for book typesetting, but obviously can be used in other situations as well.

See what you think!
Tagsdiscussion
PatchYes

Relationships

related to 0012463 closedfschmid Multiple Duplicate does not place items on the correct page in Windows > Outline 
related to 0010557 new New "page dimension" buttons for multiple copy dialog 

Activities

JLuc

2014-09-28 21:03

developer   ~0033843

That proposal reads great.

It could be an answer to the needs inspiring following bugs :
0012463: Multiple Duplicate does not place items on the correct page in Windows > Outline
0010557: New "page dimension" buttons for multiple copy dialog

ale

2014-09-29 08:08

manager   ~0033844

being able to duplicate on other pages is fore sure a very good thing.

the automatic linking can be interesting, but it should be optional (sorry, the patch is too long for my eyes and i cannot test it right now).

not sure if automatically creating new pages is a good idea.
most of the time you already know from the beginning how many pages you have. and for the cases you don't, i'd prefer to have a very good "add pages", easy to use and to trigger, rather than having pages creation at multiple places in the code.

at the end of the day, i stay with my opinion that we rather need a single multiple insert / multiple duplicate dialog.

mecirt

2014-09-29 15:40

reporter   ~0033846

Auto-linking is optional, yes. The page creation is 3 lines of code, I can remove it if it's a concern, as it's not a crucial part of the patch, but it does add some ease-of-use.

JLuc

2014-09-29 18:07

developer   ~0033847

Last edited: 2014-09-29 18:07

I've applied the patch and tested it a bit.
It works fine.

Nice is :
- When "Each item is on a separate page", the measures inputs disappear because they are of no use with this option.
- "Each item is on a separate page" only creates pages when required

A couple of points should be improved :

- Undo undoes the duplicate action, but doesnt undo the page creation.

- With facing pages layout, this patch duplicates the object on all pages, both right and left.
IMO, it is not the best choice. I think improved behaviour should stick with actual scribus duplicate behaviour without the patch, that is with the "vertical gap" or "vertical move" options : some left page object, when duplicated with a vertical move or gap, only moves towart left pages.
That's because in a facing pages layout, left and right page layout is very likely to be different.
So the dialog should provide a couple of radiobutton when document is facing layout or 3 fold brochure so as to enable the "All pages" vs "Only same side pages" choice. Or a checkbox to choose "Only same side page".

PS : I have not the power to merge your patch in svn trunk. So better ask feedback of a core dev to know how they envision a future merge.

ale

2014-09-29 19:10

manager   ~0033848

as said on irc:

- i'd prefer to avoid creating pages in the multiple duplication dialog
- duplicate only duplicates on the same type of pages (left, right, ...)

let's keep it simple...
those setting cover imo the normal cases and for the other cases there are good workflow that use the existing dialog and functionality...

JLuc

2014-10-03 10:49

developer   ~0033881

Last edited: 2014-10-03 11:18

mecirt, ale, have you had a look at the UI proposed for 0010557

I mean http://minus.com/i/SOsBbM0whjGd

mecirt

2014-10-05 19:08

reporter  

improved-duplicate.patch (32,227 bytes)   
Index: scribus/canvasmode_drawcalligraphic.cpp
===================================================================
--- scribus/canvasmode_drawcalligraphic.cpp	(revision 19541)
+++ scribus/canvasmode_drawcalligraphic.cpp	(working copy)
@@ -331,28 +331,4 @@
 	}
 }
 
-void CalligraphicMode::selectPage(QMouseEvent *m)
-{
-	m_MouseButtonPressed = true;
-	FPoint mousePointDoc = m_canvas->globalToCanvas(m->globalPos());
-	Mxp = mousePointDoc.x(); //static_cast<int>(m->x()/m_canvas->scale());
-	Myp = mousePointDoc.y(); //static_cast<int>(m->y()/m_canvas->scale());
-	QRect mpo(m->x()-m_doc->guidesPrefs().grabRadius, m->y()-m_doc->guidesPrefs().grabRadius, m_doc->guidesPrefs().grabRadius*2, m_doc->guidesPrefs().grabRadius*2);
-	m_doc->nodeEdit.deselect();
-	m_view->Deselect(false);
-	if (!m_doc->masterPageMode())
-	{
-		int i = m_doc->OnPage(Mxp, Myp);
-		if (i!=-1)
-		{
-			uint docCurrPageNo=m_doc->currentPageNumber();
-			uint j=static_cast<uint>(i);
-			if (docCurrPageNo != j)
-			{
-				m_doc->setCurrentPage(m_doc->Pages->at(j));
-				m_view->m_ScMW->slotSetCurrentPage(j);
-				m_view->DrawNew();
-			}
-		}
-	}
-}
+
Index: scribus/canvasmode_drawcalligraphic.h
===================================================================
--- scribus/canvasmode_drawcalligraphic.h	(revision 19541)
+++ scribus/canvasmode_drawcalligraphic.h	(working copy)
@@ -48,7 +48,6 @@
 	
 private:
 	inline bool GetItem(PageItem** pi); 
-	void selectPage(QMouseEvent *m);
 
 	bool   m_MouseButtonPressed;
 	double Mxp, Myp, Dxp, Dyp;
Index: scribus/canvasmode_drawfreehand.cpp
===================================================================
--- scribus/canvasmode_drawfreehand.cpp	(revision 19541)
+++ scribus/canvasmode_drawfreehand.cpp	(working copy)
@@ -318,28 +318,3 @@
 	}
 }
 
-void FreehandMode::selectPage(QMouseEvent *m)
-{
-	m_MouseButtonPressed = true;
-	FPoint mousePointDoc = m_canvas->globalToCanvas(m->globalPos());
-	Mxp = mousePointDoc.x(); //static_cast<int>(m->x()/m_canvas->scale());
-	Myp = mousePointDoc.y(); //static_cast<int>(m->y()/m_canvas->scale());
-	QRect mpo(m->x()-m_doc->guidesPrefs().grabRadius, m->y()-m_doc->guidesPrefs().grabRadius, m_doc->guidesPrefs().grabRadius*2, m_doc->guidesPrefs().grabRadius*2);
-	m_doc->nodeEdit.deselect();
-	m_view->Deselect(false);
-	if (!m_doc->masterPageMode())
-	{
-		int i = m_doc->OnPage(Mxp, Myp);
-		if (i!=-1)
-		{
-			uint docCurrPageNo=m_doc->currentPageNumber();
-			uint j=static_cast<uint>(i);
-			if (docCurrPageNo != j)
-			{
-				m_doc->setCurrentPage(m_doc->Pages->at(j));
-				m_view->m_ScMW->slotSetCurrentPage(j);
-				m_view->DrawNew();
-			}
-		}
-	}
-}
Index: scribus/canvasmode_drawfreehand.h
===================================================================
--- scribus/canvasmode_drawfreehand.h	(revision 19541)
+++ scribus/canvasmode_drawfreehand.h	(working copy)
@@ -48,7 +48,6 @@
 	
 private:
 	inline bool GetItem(PageItem** pi); 
-	void selectPage(QMouseEvent *m);
 
 	bool   m_MouseButtonPressed;
 	double Mxp, Myp, Dxp, Dyp;
Index: scribus/canvasmode_framelinks.cpp
===================================================================
--- scribus/canvasmode_framelinks.cpp	(revision 19541)
+++ scribus/canvasmode_framelinks.cpp	(working copy)
@@ -178,12 +178,19 @@
 			if (currItem==NULL)
 				break;
 			SeleItem(m);
-			if (GetItem(&bb) && (bb->asTextFrame()))
+			bb = m_doc->m_Selection->itemAt(0);
+			if (!bb) {
+				// clicked outside anything, make a new one with a matching size
+				int page = m_doc->OnPage(Mxp, Myp);
+				int frame = -1;
+				if (page != -1) frame = m_doc->addFullTextFrame(page, Mxp, Myp, currItem->width(), currItem->height());
+				if (frame != -1)
+					bb = m_doc->Items->at(frame);
+			}
+			if (bb && bb->asTextFrame())
 			{
-				PageItem* bblast = bb;
-				while (bblast->nextInChain())
-					bblast = bblast->nextInChain();
-				
+				PageItem* bblast = bb->lastInChain();
+
 				if (currItem->nextInChain() == NULL && currItem != bblast) //possibility to insert empty frames into chain
 				{
 					if (bb->prevInChain() != NULL)
@@ -316,32 +323,6 @@
 	m_view->m_ScMW->pageSelector->clearFocus();
 }
 
-void CanvasMode_FrameLinks::selectPage(QMouseEvent *m)
-{
-	m_canvas->m_viewMode.m_MouseButtonPressed = true;
-	FPoint mousePointDoc = m_canvas->globalToCanvas(m->globalPos());
-	Mxp = mousePointDoc.x(); //static_cast<int>(m->x()/m_canvas->scale());
-	Myp = mousePointDoc.y(); //static_cast<int>(m->y()/m_canvas->scale());
-	m_doc->nodeEdit.deselect();
-	m_view->Deselect(false);
-	if (!m_doc->masterPageMode())
-	{
-		int i = m_doc->OnPage(Mxp, Myp);
-		if (i!=-1)
-		{
-			uint docCurrPageNo=m_doc->currentPageNumber();
-			uint j=static_cast<uint>(i);
-			if (docCurrPageNo != j)
-			{
-				m_doc->setCurrentPage(m_doc->Pages->at(j));
-				m_view->m_ScMW->slotSetCurrentPage(j);
-				m_view->DrawNew();
-			}
-		}
-		m_view->setRulerPos(m_view->contentsX(), m_view->contentsY());
-	}
-}
-
 //CB-->Doc/Fix
 bool CanvasMode_FrameLinks::SeleItem(QMouseEvent *m)
 {
Index: scribus/canvasmode_framelinks.h
===================================================================
--- scribus/canvasmode_framelinks.h	(revision 19541)
+++ scribus/canvasmode_framelinks.h	(working copy)
@@ -57,7 +57,6 @@
 
 private:
 	inline bool GetItem(PageItem** pi);
-	void selectPage(QMouseEvent *m);
 	bool SeleItem(QMouseEvent *m);
 	void createContextMenu(PageItem *currItem, double mx, double my);
 
Index: scribus/scribusdoc.cpp
===================================================================
--- scribus/scribusdoc.cpp	(revision 19541)
+++ scribus/scribusdoc.cpp	(working copy)
@@ -2899,29 +2899,37 @@
 {
 	if (!automaticTextFrames)
 		return -1;
+	if (masterPageMode())
+		return -1;
 	ScPage *addToPage=DocPages.at(pageNumber);
-	if ((!masterPageMode()) && (usesAutomaticTextFrames()))// && (!isLoading()))
-	{
-		int z = itemAdd(PageItem::TextFrame, PageItem::Unspecified,
-		                     addToPage->Margins.Left+addToPage->xOffset(),
-							 addToPage->Margins.Top+addToPage->yOffset(), docPrefsData.docSetupPrefs.pageWidth-addToPage->Margins.Right-addToPage->Margins.Left,
-							 docPrefsData.docSetupPrefs.pageHeight-addToPage->Margins.Bottom-addToPage->Margins.Top,
-							 1, CommonStrings::None, docPrefsData.itemToolPrefs.shapeLineColor, true);
+	if (!addToPage) return -1;
+	const MarginStruct mg = addToPage->Margins;
+	int z = addFullTextFrame(pageNumber, mg.Left + addToPage->xOffset(), mg.Top + addToPage->yOffset(),
+			docPrefsData.docSetupPrefs.pageWidth - mg.Right - mg.Left,
+			docPrefsData.docSetupPrefs.pageHeight - mg.Bottom - mg.Top);
+	if (z != -1) {
 		Items->at(z)->isAutoText = true;
-		Items->at(z)->Cols = qRound(PageSp);
-		Items->at(z)->ColGap = PageSpa;
 		if (LastAuto != 0)
 			LastAuto->link(Items->at(z));
 		else
 			FirstAuto = Items->at(z);
 		LastAuto = Items->at(z);
-		Items->at(z)->setRedrawBounding();
 		updateEndnotesFrames();
-		return z;
 	}
-	return -1;
+	return z;
 }
 
+int ScribusDoc::addFullTextFrame(const int pageNumber, double x, double y, double w, double h)
+{
+	if (masterPageMode()) return -1;
+	int z = itemAdd(PageItem::TextFrame, PageItem::Unspecified,
+		           x, y, w, h, 
+							 1, CommonStrings::None, docPrefsData.itemToolPrefs.shapeLineColor, true);
+	Items->at(z)->Cols = qRound(PageSp);
+	Items->at(z)->ColGap = PageSpa;
+	Items->at(z)->setRedrawBounding();
+	return z;
+}
 
 int ScribusDoc::addLayer(const QString& layerName, const bool activate)
 {
@@ -13753,7 +13761,90 @@
 	}
 	DoDrawing = false;
 	view()->updatesOn(false);
-	if (mdData.type==0) // Copy and offset or set a gap
+
+	// should we link frames? Only valid if we have a single item, which is a text frame
+	PageItem *frameToLink = 0;
+	if ((m_Selection->count() == 1) && m_Selection->itemAt(0)->isTextFrame())
+		frameToLink = m_Selection->itemAt(0);
+	bool linkFrames = frameToLink ? mdData.linkFrames : false;
+
+	// if we'll be linking frames, clear the text first - this majorly speeds up the operation (adding text is very slow)
+	StoryText stext;
+	if (linkFrames) {
+		stext = frameToLink->itemText;
+		frameToLink->itemText = StoryText();
+	}
+
+	ScriXmlDoc ss;
+	QString BufferS = ss.WriteElem(this, m_Selection);
+
+	if (linkFrames) frameToLink->itemText = stext;
+
+	// First create all the items, then move them as desired
+	int startIdx = Items->count();
+	int copyDiff = 0;
+	int copyCount = (mdData.type == 0) ? mdData.copyCount : mdData.gridRows * mdData.gridCols - 1;
+	double pageX = currentPage()->xOffset();
+	double pageY = currentPage()->yOffset();
+	int page = currentPage()->pageNr();
+
+	for (int i = 0; i < copyCount; ++i) {
+
+		if (mdData.copyNewPage) {
+			// Each item is on a new page, so grab the next one
+			bool correct = false;
+			int newPage = page;
+      do {
+				newPage++;
+				if (newPage >= DocPages.size()) break;
+			  // if we want same-side pages only, keep going until the correct side is found
+			  if (mdData.newPageSameSide && (locationOfPage(newPage) != locationOfPage(page))) continue;
+
+				correct = true;
+			} while (!correct);
+
+			// If the page doesn't exist, we bail out and only copy what we can.
+			if (!correct) {
+				copyCount = i;  // set copyCount so that this one is not included (its page doesn't exist)
+				break;
+			}
+
+			page = newPage;
+			ScPage *addToPage = DocPages.at(page);
+			pageX = addToPage->xOffset();
+			pageY = addToPage->yOffset();
+		}
+
+		ss.ReadElem(BufferS, appPrefsData.fontPrefs.AvailFonts, this, pageX, pageY, false, true, appPrefsData.fontPrefs.GFontSub);
+		if (i == 0) copyDiff = Items->count() - startIdx;
+		// link the frames if requested
+		// we do that right away so that we don't retain multiple copies of the text needlessly
+		if (linkFrames) {
+		  PageItem* bItem = Items->at(Items->count() - 1);
+			bItem->itemText = StoryText();  // clear the existing text, as link() would otherwise append the two, which we do not want
+			frameToLink->link(bItem);
+			frameToLink = bItem;
+		}
+	}
+	for (int i = startIdx; i < startIdx + copyCount * copyDiff; ++i) {
+		PageItem* bItem = Items->at(i);
+		bItem->setLocked(false);
+	}
+
+	if ((mdData.type == 0) && mdData.copyNewPage) {
+		// duplicating each item on a new page - nothing to do here
+		for (int diff = 0; diff < copyCount * copyDiff; ++diff)
+		{
+			// is this actually needed?
+			PageItem* bItem = Items->at(startIdx + diff);
+			bItem->connectToGUI();
+			bItem->emitAllToGUI();
+		}
+
+		tooltip = tr("Number of copies: %1").arg(copyCount);
+	}
+
+	else if (mdData.type==0) // Copy and offset or set a gap
 	{
 		double dH = mdData.copyShiftGapH / docUnitRatio;
 		double dV = mdData.copyShiftGapV / docUnitRatio;
@@ -13761,7 +13852,7 @@
 		double dV2 = dV;
 		double dR = mdData.copyRotation;
 		double dR2 = dR;
-		if (mdData.copyShiftOrGap==1)
+		if (mdData.copyShiftOrGap)
 		{
 			if (dH != 0.0)
 				dH2 += m_Selection->width();
@@ -13768,20 +13859,17 @@
 			if (dV != 0.0)
 				dV2 += m_Selection->height();
 		}
-		ScriXmlDoc ss;
-		QString BufferS = ss.WriteElem(this, m_Selection);
+
 		//FIXME: stop using m_View
 		m_View->Deselect(true);
-		for (int i=0; i<mdData.copyCount; ++i)
+		for (int i = 0; i < copyCount; ++i)
 		{
-			uint ac = Items->count();
-			ss.ReadElem(BufferS, appPrefsData.fontPrefs.AvailFonts, this, currentPage()->xOffset(), currentPage()->yOffset(), false, true, appPrefsData.fontPrefs.GFontSub);
+			uint ac = startIdx + copyDiff * i;
 			m_Selection->delaySignalsOn();
-			for (int as = ac; as < Items->count(); ++as)
+			for (int diff = 0; diff < copyDiff; ++diff)
 			{
-				PageItem* bItem = Items->at(as);
-				bItem->setLocked(false);
-				bItem->moveBy(dH2, dV2, true);
+				PageItem* bItem = Items->at(ac + diff);
+				bItem->moveBy(dH2 * (i + 1), dV2 * (i + 1), true);
 				m_Selection->addItem(bItem);
 			}
 			m_Selection->delaySignalsOff();
@@ -13789,19 +13877,10 @@
 			if (dR != 0.0)
 			{
 				if (m_Selection->count() > 1)
-					rotateGroup(dR2, FPoint(0,0)); //FIXME:av
+					rotateGroup(dR2 * (i + 1), FPoint(0,0)); //FIXME:av
 				else
-					RotateItem(dR2, m_Selection->itemAt(0));
+					RotateItem(dR2 * (i + 1), m_Selection->itemAt(0));
 			}
-			dH2 += dH;
-			dV2 += dV;
-			if (mdData.copyShiftOrGap==1)
-			{
-				if (dH != 0.0)
-					dH2 += m_Selection->width();
-				if (dV != 0.0)
-					dV2 += m_Selection->height();
-			}
 			dR2 += dR;
 			if (m_Selection->count() > 0)
 			{
@@ -13815,39 +13894,34 @@
 		QString hString = QString::number(dH * docUnitRatio, 'f', unitPrecision) + " " + unitSuffix;
 		QString vString = QString::number(dV * docUnitRatio, 'f', unitPrecision) + " " + unitSuffix;
 		QString dString = QString::number(dR) + " " + unitGetStrFromIndex(SC_DEGREES);
-		tooltip = tr("Number of copies: %1\nHorizontal shift: %2\nVertical shift: %3\nRotation: %4").arg(mdData.copyCount).arg(hString).arg(vString).arg(dString);
+		tooltip = tr("Number of copies: %1\nHorizontal shift: %2\nVertical shift: %3\nRotation: %4").arg(copyCount).arg(hString).arg(vString).arg(dString);
 	}
+
 	else if (mdData.type==1) // Create a grid of duplicated items
 	{
-		int copyCount = mdData.gridRows * mdData.gridCols;
 		double dX = mdData.gridGapH/docUnitRatio + m_Selection->width();
 		double dY = mdData.gridGapV/docUnitRatio + m_Selection->height();
-		ScriXmlDoc ss;
-		QString BufferS = ss.WriteElem(this, m_Selection);
-		for (int i = 0; i < mdData.gridRows; ++i) //skip 0, the item is the one we are copying
+
+		for (int i = 0; i < copyCount; ++i)
 		{
-			for (int j = 0; j < mdData.gridCols; ++j) //skip 0, the item is the one we are copying
+			// compute coords; 0,0 is the original item, which we skip
+			int row = (i + 1) / mdData.gridCols;   // gridCols non-zero, or we wouldn't be inside this loop
+			int col = (i + 1) % mdData.gridCols;
+			uint ac = startIdx + copyDiff * i;
+			for (int diff = 0; diff < copyDiff; ++diff)
 			{
-				// We can comment out this conditional jump if we use slotEditCut(), would not be cool? ;-)
-				if (i==0 && j==0)
-					continue;
-				uint ac = Items->count();
-				ss.ReadElem(BufferS, appPrefsData.fontPrefs.AvailFonts, this, currentPage()->xOffset(), currentPage()->yOffset(), false, true, appPrefsData.fontPrefs.GFontSub);
-				for (int as = ac; as < Items->count(); ++as)
-				{
-					PageItem* bItem = Items->at(as);
-					bItem->setLocked(false);
-					bItem->moveBy(j*dX, i*dY, true);
-					bItem->connectToGUI();
-					bItem->emitAllToGUI();
-				}
+				PageItem* bItem = Items->at(ac + diff);
+				bItem->moveBy(col * dX, row * dY, true);
+				bItem->connectToGUI();
+				bItem->emitAllToGUI();
 			}
 		}
+
 		QString unitSuffix = unitGetStrFromIndex(this->unitIndex());
 		int unitPrecision = unitGetPrecisionFromIndex(this->unitIndex());
 		QString hString = QString::number(mdData.gridGapH, 'f', unitPrecision) + " " + unitSuffix;
 		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);
+		tooltip = tr("Number of copies: %1\nHorizontal gap: %2\nVertical gap: %3").arg(copyCount).arg(hString).arg(vString).arg(unitSuffix);
 	}
 	if (activeTransaction)
 	{
Index: scribus/scribusdoc.h
===================================================================
--- scribus/scribusdoc.h	(revision 19541)
+++ scribus/scribusdoc.h	(working copy)
@@ -264,6 +264,18 @@
 	 */
 	int addAutomaticTextFrame(const int pageNumber);
 	/**
+	 * @brief Add a full-size text frame to the page. Similar to addAutomaticTextFrame,
+	 * but works regardless on whether automaticTextFrames is set, and it also
+	 * doesn't connect the frame with other automatically created frames
+	 * @param pageNumber page number
+	 * @param x x position
+	 * @param y y position
+	 * @param w width
+	 * @param h height
+	 * @return number of frame
+	 */
+	int addFullTextFrame(const int pageNumber, double x, double y, double w, double h);
+	/**
 	 * Set the left and right margins based on the location of the page
 	 * @param pageIndex 
 	 */
Index: scribus/scribusview.cpp
===================================================================
--- scribus/scribusview.cpp	(revision 19541)
+++ scribus/scribusview.cpp	(working copy)
@@ -158,7 +158,6 @@
 	m_groupTransactions(0),
 	m_groupTransaction(),
 	_isGlobalMode(true),
-	linkAfterDraw(false),
 	ImageAfterDraw(false),
 	m_vhRulerHW(17)
 {
@@ -4101,17 +4100,6 @@
 		QMouseEvent* m = static_cast<QMouseEvent*> (event);
 		m_canvasMode->mouseReleaseEvent(m);
 		m_canvas->m_viewMode.m_MouseButtonPressed = false;
-		if (linkAfterDraw)
-		{
-			//user creates new frame using linking tool
-			PageItem * secondFrame = Doc->m_Selection->itemAt(0);
-			if (secondFrame && firstFrame)
-			{
-				firstFrame->link(secondFrame);
-				firstFrame = NULL;
-			}
-			linkAfterDraw = false;
-		}
 		if (ImageAfterDraw)
 		{
 			//user creates new frame using linking tool
@@ -4129,20 +4117,7 @@
 	else if (obj == widget() && event->type() == QEvent::MouseButtonPress)
 	{
 		QMouseEvent* m = static_cast<QMouseEvent*> (event);
-		bool linkmode = (Doc->appMode == modeLinkFrames);
-		firstFrame = Doc->m_Selection->itemAt(0);
 		m_canvasMode->mousePressEvent(m);
-		//if user dont click any frame he want to draw new frame and link it
-		bool requestDrawMode = (Doc->ElemToLink == NULL);
-		if (linkmode && requestDrawMode)
-		{
-			//switch to drawing new text frame
-			linkAfterDraw = true;
-			requestMode(modeDrawText);
-			m_canvasMode->mousePressEvent(m);
-		}
-		else
-			firstFrame = NULL;
 		if(Doc->appMode == modeImportImage && ImageAfterDraw)
 		{
 			//switch to drawing new text frame
Index: scribus/scribusview.h
===================================================================
--- scribus/scribusview.h	(revision 19541)
+++ scribus/scribusview.h	(working copy)
@@ -262,10 +262,6 @@
 	void PasteToPage();
 	void TextToPath();
 
-//for linking frame after draw new frame
-private:
-	PageItem * firstFrame;
-
 private: // Private attributes
 	int m_previousMode;
 	QMenu *pmen3;
@@ -282,7 +278,6 @@
 	int m_oldCanvasWidth;
 	UndoTransaction m_groupTransaction;
 	bool _isGlobalMode;
-	bool linkAfterDraw;
 	bool ImageAfterDraw;
 	bool m_oldSnapToElem;
 
Index: scribus/ui/contextmenu.cpp
===================================================================
--- scribus/ui/contextmenu.cpp	(revision 19541)
+++ scribus/ui/contextmenu.cpp	(working copy)
@@ -97,6 +97,7 @@
 	QMenu *menuPDF = new QMenu(this);
 	QMenu *menuMark = new QMenu(this);
 	QMenu *menuResolution = new QMenu(this);
+	QMenu *menuDuplicate = new QMenu(this);
 	QMenu *menuLocking = new QMenu(this);
 	QMenu *menuSendTo = new QMenu(this);
 	QMenu *menuScrapbook = new QMenu(this);
@@ -362,6 +363,14 @@
 	
 	if (selectedItemCount>0)
 	{
+		// <-- Duplicate
+		menuDuplicate->addAction(m_ScMW->scrActions["itemDuplicate"]);
+		menuDuplicate->addAction(m_ScMW->scrActions["itemMulDuplicate"]);
+		menuDuplicate->addAction(m_ScMW->scrActions["itemTransform"]);
+		QAction *actDT = addMenu(menuDuplicate);
+		actDT->setText( tr("Duplicate/Transform"));
+		//-->
+
 		//<-- Item Locking
 		menuLocking->addAction(m_ScMW->scrActions["itemLock"]);
 		menuLocking->addAction(m_ScMW->scrActions["itemLockSize"]);
Index: scribus/ui/multipleduplicate.cpp
===================================================================
--- scribus/ui/multipleduplicate.cpp	(revision 19541)
+++ scribus/ui/multipleduplicate.cpp	(working copy)
@@ -45,11 +45,9 @@
 	rotationSpinBox->setDecimals(1);
 	rotationSpinBox->setNewUnit(6);
 	
+	// signals and slots connections
+	connect(shiftButtonGroup, SIGNAL(buttonToggled(QAbstractButton *, bool)), this, SLOT(shiftRadioToggled(QAbstractButton *, bool)));
 	createGapRadioButton->setChecked(true);
-	setCopiesGap();
-	// signals and slots connections
-	connect(createGapRadioButton, SIGNAL(clicked()), this, SLOT(setCopiesGap()));
-	connect(shiftCreatedItemsRadioButton, SIGNAL(clicked()), this, SLOT(setCopiesShift()));
 }
 
 MultipleDuplicate::~MultipleDuplicate()
@@ -72,7 +70,10 @@
 {
 	mdData.type = tabWidget->currentIndex();
 	mdData.copyCount = numberOfCopiesSpinBox->value();
-	mdData.copyShiftOrGap = createGapRadioButton->isChecked() ? 1 : 0;
+	mdData.copyShiftOrGap = createGapRadioButton->isChecked();
+	mdData.copyNewPage = eachItemOnNewPageRadioButton->isChecked();
+	mdData.newPageSameSide = newPageSameSide->isChecked();
+	mdData.linkFrames = linkFrames->isChecked();
 	mdData.copyShiftGapH = horizShiftSpinBox->value();
 	mdData.copyShiftGapV = vertShiftSpinBox->value();
 	mdData.copyRotation = rotationSpinBox->value();
@@ -81,3 +82,20 @@
 	mdData.gridGapH = horizRCGapSpinBox->value();
 	mdData.gridGapV = vertRCGapSpinBox->value();
 }
+
+void MultipleDuplicate::shiftRadioToggled(QAbstractButton *button, bool checked)
+{
+	if (!checked) return;
+	if (button == eachItemOnNewPageRadioButton) {
+	  duplicateOptions->hide();
+		newPageSameSide->show();
+		return;
+	}
+	newPageSameSide->hide();
+	duplicateOptions->show();
+	if (button == createGapRadioButton)
+		setCopiesGap();
+	else
+		setCopiesShift();
+}
+
Index: scribus/ui/multipleduplicate.h
===================================================================
--- scribus/ui/multipleduplicate.h	(revision 19541)
+++ scribus/ui/multipleduplicate.h	(working copy)
@@ -20,9 +20,11 @@
 	protected:
 		int m_unitIndex;
 		double m_unitRatio;
-	protected slots:
+
 		void setCopiesShift();
 		void setCopiesGap();
+	protected slots:
+		void shiftRadioToggled(QAbstractButton *button, bool checked);
 };
 
 #endif
Index: scribus/ui/multipleduplicate.ui
===================================================================
--- scribus/ui/multipleduplicate.ui	(revision 19541)
+++ scribus/ui/multipleduplicate.ui	(working copy)
@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>303</width>
-    <height>350</height>
+    <width>363</width>
+    <height>394</height>
    </rect>
   </property>
   <property name="windowTitle">
@@ -45,9 +45,18 @@
              <property name="spacing">
               <number>5</number>
              </property>
-             <property name="margin">
+             <property name="leftMargin">
               <number>5</number>
              </property>
+             <property name="topMargin">
+              <number>5</number>
+             </property>
+             <property name="rightMargin">
+              <number>5</number>
+             </property>
+             <property name="bottomMargin">
+              <number>5</number>
+             </property>
              <item>
               <widget class="QLabel" name="numberOfCopiesLabel">
                <property name="text">
@@ -74,6 +83,13 @@
             </layout>
            </item>
            <item>
+            <widget class="QCheckBox" name="linkFrames">
+             <property name="text">
+              <string>&amp;Link Text Frames</string>
+             </property>
+            </widget>
+           </item>
+           <item>
             <widget class="QGroupBox" name="copiesCreateButtonGroup">
              <property name="title">
               <string/>
@@ -87,6 +103,9 @@
                 <property name="shortcut">
                  <string>Alt+G</string>
                 </property>
+                <attribute name="buttonGroup">
+                 <string notr="true">shiftButtonGroup</string>
+                </attribute>
                </widget>
               </item>
               <item>
@@ -97,86 +116,111 @@
                 <property name="shortcut">
                  <string>Alt+S</string>
                 </property>
+                <attribute name="buttonGroup">
+                 <string notr="true">shiftButtonGroup</string>
+                </attribute>
                </widget>
               </item>
+              <item>
+               <widget class="QRadioButton" name="eachItemOnNewPageRadioButton">
+                <property name="text">
+                 <string>Each Item on a Separate &amp;Page</string>
+                </property>
+                <attribute name="buttonGroup">
+                 <string notr="true">shiftButtonGroup</string>
+                </attribute>
+               </widget>
+              </item>
              </layout>
             </widget>
            </item>
            <item>
-            <layout class="QGridLayout">
-             <property name="margin">
-              <number>5</number>
+            <widget class="QFrame" name="duplicateOptions">
+             <layout class="QGridLayout">
+              <property name="leftMargin">
+               <number>5</number>
+              </property>
+              <property name="topMargin">
+               <number>5</number>
+              </property>
+              <property name="rightMargin">
+               <number>5</number>
+              </property>
+              <property name="bottomMargin">
+               <number>5</number>
+              </property>
+              <property name="spacing">
+               <number>5</number>
+              </property>
+              <item row="0" column="0">
+               <widget class="QLabel" name="horizShiftLabel">
+                <property name="text">
+                 <string>&amp;Horizontal Shift:</string>
+                </property>
+                <property name="wordWrap">
+                 <bool>false</bool>
+                </property>
+               </widget>
+              </item>
+              <item row="2" column="1">
+               <widget class="ScrSpinBox" name="rotationSpinBox">
+                <property name="minimumSize">
+                 <size>
+                  <width>50</width>
+                  <height>24</height>
+                 </size>
+                </property>
+               </widget>
+              </item>
+              <item row="1" column="1">
+               <widget class="ScrSpinBox" name="vertShiftSpinBox">
+                <property name="minimumSize">
+                 <size>
+                  <width>50</width>
+                  <height>24</height>
+                 </size>
+                </property>
+               </widget>
+              </item>
+              <item row="2" column="0">
+               <widget class="QLabel" name="rotationLabel">
+                <property name="text">
+                 <string>Rotation:</string>
+                </property>
+                <property name="wordWrap">
+                 <bool>false</bool>
+                </property>
+               </widget>
+              </item>
+              <item row="0" column="1">
+               <widget class="ScrSpinBox" name="horizShiftSpinBox">
+                <property name="minimumSize">
+                 <size>
+                  <width>50</width>
+                  <height>24</height>
+                 </size>
+                </property>
+               </widget>
+              </item>
+              <item row="1" column="0">
+               <widget class="QLabel" name="vertShiftLabel">
+                <property name="text">
+                 <string>&amp;Vertical Shift:</string>
+                </property>
+                <property name="wordWrap">
+                 <bool>false</bool>
+                </property>
+               </widget>
+              </item>
+             </layout>
+            </widget>
+           </item>
+           <item>
+            <widget class="QCheckBox" name="newPageSameSide">
+             <property name="text">
+              <string>Duplicate to &amp;Same Side Pages Only</string>
              </property>
-             <property name="spacing">
-              <number>5</number>
-             </property>
-             <item row="0" column="0">
-              <widget class="QLabel" name="horizShiftLabel">
-               <property name="text">
-                <string>&amp;Horizontal Shift:</string>
-               </property>
-               <property name="wordWrap">
-                <bool>false</bool>
-               </property>
-               <property name="buddy">
-                <cstring>horizShiftSpinBox</cstring>
-               </property>
-              </widget>
-             </item>
-             <item row="2" column="1">
-              <widget class="ScrSpinBox" name="rotationSpinBox">
-               <property name="minimumSize">
-                <size>
-                 <width>50</width>
-                 <height>24</height>
-                </size>
-               </property>
-              </widget>
-             </item>
-             <item row="1" column="1">
-              <widget class="ScrSpinBox" name="vertShiftSpinBox">
-               <property name="minimumSize">
-                <size>
-                 <width>50</width>
-                 <height>24</height>
-                </size>
-               </property>
-              </widget>
-             </item>
-             <item row="2" column="0">
-              <widget class="QLabel" name="rotationLabel">
-               <property name="text">
-                <string>Rotation:</string>
-               </property>
-               <property name="wordWrap">
-                <bool>false</bool>
-               </property>
-              </widget>
-             </item>
-             <item row="0" column="1">
-              <widget class="ScrSpinBox" name="horizShiftSpinBox">
-               <property name="minimumSize">
-                <size>
-                 <width>50</width>
-                 <height>24</height>
-                </size>
-               </property>
-              </widget>
-             </item>
-             <item row="1" column="0">
-              <widget class="QLabel" name="vertShiftLabel">
-               <property name="text">
-                <string>&amp;Vertical Shift:</string>
-               </property>
-               <property name="wordWrap">
-                <bool>false</bool>
-               </property>
-               <property name="buddy">
-                <cstring>vertShiftSpinBox</cstring>
-               </property>
-              </widget>
-             </item>
-            </layout>
+            </widget>
            </item>
           </layout>
          </item>
@@ -221,16 +265,34 @@
        <string>By &amp;Rows &amp;&amp; Columns</string>
       </attribute>
       <layout class="QVBoxLayout">
-       <property name="margin">
+       <property name="leftMargin">
         <number>0</number>
        </property>
+       <property name="topMargin">
+        <number>0</number>
+       </property>
+       <property name="rightMargin">
+        <number>0</number>
+       </property>
+       <property name="bottomMargin">
+        <number>0</number>
+       </property>
        <item>
         <layout class="QHBoxLayout">
          <item>
           <layout class="QGridLayout">
-           <property name="margin">
+           <property name="leftMargin">
             <number>5</number>
            </property>
+           <property name="topMargin">
+            <number>5</number>
+           </property>
+           <property name="rightMargin">
+            <number>5</number>
+           </property>
+           <property name="bottomMargin">
+            <number>5</number>
+           </property>
            <property name="spacing">
             <number>5</number>
            </property>
@@ -410,7 +472,6 @@
  <tabstops>
   <tabstop>tabWidget</tabstop>
   <tabstop>numberOfCopiesSpinBox</tabstop>
-  <tabstop>createGapRadioButton</tabstop>
   <tabstop>shiftCreatedItemsRadioButton</tabstop>
   <tabstop>horizShiftSpinBox</tabstop>
   <tabstop>vertShiftSpinBox</tabstop>
@@ -464,4 +525,7 @@
    </hints>
   </connection>
  </connections>
+ <buttongroups>
+  <buttongroup name="shiftButtonGroup"/>
+ </buttongroups>
 </ui>
Index: scribus/usertaskstructs.h
===================================================================
--- scribus/usertaskstructs.h	(revision 19541)
+++ scribus/usertaskstructs.h	(working copy)
@@ -36,7 +36,10 @@
 {
 	int type;
 	int copyCount;
-	int copyShiftOrGap;
+	bool copyShiftOrGap;
+	bool copyNewPage;
+	bool linkFrames;
+	bool newPageSameSide;
 	double copyShiftGapH;
 	double copyShiftGapV;
 	double copyRotation;
improved-duplicate.patch (32,227 bytes)   

mecirt

2014-10-05 19:09

reporter   ~0033897

Attached an updated patch based on the feedback. Changes:
- pages are no longer auto-created, the instead the duplication stops if it reaches the end
- added a checkbox to duplicate on same-side pages only

JLuc

2014-10-06 06:27

developer   ~0033909

to be tested

ale

2014-10-06 08:49

manager   ~0033911

jluc: yep, i did see the proposal and didn't really like it.

the icons are nice and manage, with very little graphical elements, to explain what it does! well done.

but what is harder, is to make clear to the user why the button is there. the relationship to duplicating on other pages is hard to explain.

personally, i really prefer to have the feature being cleanly implemented rather than adding two buttons for a (clever) hack.

Kunda

2014-10-08 18:22

updater   ~0033935

ale, do you mind restating in a clear way what 'cleanly implemented' looks like?

mecirt

2014-10-26 16:28

reporter   ~0034154

Mkay so, do you need any further changes from me on this?

Kunda

2015-11-05 12:33

updater   ~0037257

Hi mecirt, sorry it's taken so long to get back to you on this.
One of this issues is that the patch is big and introduces a lot at once. It's easier if it's broken down in to smaller patches that address each issue individually.

The community is still discussing if all the features are necessary. Thank you again for you patch.

Kunda

2016-04-02 12:49

updater   ~0039698

hey mecirt
are you still around ?

ale

2016-04-02 15:19

manager   ~0039713

@kunda, it's a long time ago... but i guess that by "cleanly implemented" i used to mean that feature are easy to trigger and are in places where they are easy to find.

spreading part of the compound features all over the places, with complicated options to achieve special cases is imo not the way to go.

as written above:

- it should be easy to create new pages
- but the duplicate dialog should not create pages (as mecirt retained in the second patch)
- we should have only one duplicate dialog

... and more...

mecirt

2016-04-04 08:55

reporter   ~0039758

Yes, I am still around. I don't know if the targetted use case is that much of a speciality, but the ability to create pages on-the-fly is a fairly useful one in book DTP (a lot of text, not really much else), so I must say that I am a bit confused as of why there is opposition to the addition.

At any rate, if the page creation is a blocker for you, but the feature is acceptable otherwise, I can alter the patch to not include this part.

JLuc

2016-04-04 12:07

developer   ~0039762

mercirt : none of the core scribus devs has commented your patch yet. Dont imagine other comments are core dev's views.
Would you come on irc in the evening so as to remind MrB or jghali of this patch ?

mecirt

2016-04-04 18:19

reporter   ~0039794

Sure, I'm on the irc channel quite a lot, just ping me.

Issue History

Date Modified Username Field Change
2014-09-27 19:04 mecirt New Issue
2014-09-27 19:04 mecirt File Added: improved-duplicate.patch
2014-09-27 19:08 FirasH Summary Patch: improved Duplicate feature => [PATCH] improved Duplicate feature
2014-09-28 21:03 JLuc Note Added: 0033843
2014-09-28 21:03 JLuc Relationship added related to 0012463
2014-09-28 21:03 JLuc Relationship added related to 0010557
2014-09-29 08:08 ale Note Added: 0033844
2014-09-29 15:40 mecirt Note Added: 0033846
2014-09-29 18:07 JLuc Note Added: 0033847
2014-09-29 18:07 JLuc Note Edited: 0033847
2014-09-29 19:10 ale Note Added: 0033848
2014-10-03 10:49 JLuc Note Added: 0033881
2014-10-03 11:16 JLuc Note Edited: 0033881
2014-10-03 11:18 JLuc Note Edited: 0033881
2014-10-05 19:08 mecirt File Deleted: improved-duplicate.patch
2014-10-05 19:08 mecirt File Added: improved-duplicate.patch
2014-10-05 19:09 mecirt Note Added: 0033897
2014-10-06 06:27 JLuc Note Added: 0033909
2014-10-06 06:27 JLuc Status new => feedback
2014-10-06 08:49 ale Note Added: 0033911
2014-10-08 18:22 Kunda Note Added: 0033935
2014-10-24 22:57 Kunda Patch => Yes
2014-10-26 16:28 mecirt Note Added: 0034154
2014-10-26 16:28 mecirt Status feedback => new
2015-11-05 12:33 Kunda Note Added: 0037257
2016-04-02 12:49 Kunda Tag Attached: discussion
2016-04-02 12:49 Kunda Note Added: 0039698
2016-04-02 15:19 ale Note Added: 0039713
2016-04-04 08:55 mecirt Note Added: 0039758
2016-04-04 12:07 JLuc Note Added: 0039762
2016-04-04 18:19 mecirt Note Added: 0039794