1 /*
2 For general Scribus (>=1.3.2) copyright and licensing information please refer
3 to the COPYING file provided with the program. Following this notice may exist
4 a copyright and/or license notice that predates the release of Scribus 1.3.2
5 for which a new license (GPL+exception) is in place.
6 */
7 #include "pagepalette_masterpages.h"
8 
9 #include <QApplication>
10 #include <QCloseEvent>
11 #include <QCursor>
12 #include <QHBoxLayout>
13 #include <QInputDialog>
14 #include <QListWidget>
15 #include <QListWidgetItem>
16 #include <QMessageBox>
17 #include <QScopedPointer>
18 #include <QSpacerItem>
19 #include <QString>
20 #include <QStringList>
21 #include <QToolButton>
22 #include <QToolTip>
23 #include <QVBoxLayout>
24 
25 #include "appmodes.h"
26 #include "canvasmode.h"
27 #include "commonstrings.h"
28 #include "iconmanager.h"
29 #include "mergedoc.h"
30 #include "newtemp.h"
31 #include "pagestructs.h"
32 #include "prefsmanager.h"
33 #include "scpage.h"
34 #include "scribus.h"
35 #include "scribusapp.h"
36 #include "scribusdoc.h"
37 #include "scribusview.h"
38 #include "scribusXml.h"
39 #include "selection.h"
40 #include "util.h"
41 #include "util_os.h"
42 
43 
PagePalette_MasterPages(QWidget * parent,ScribusView * pCurrentView,const QString & masterPageName)44 PagePalette_MasterPages::PagePalette_MasterPages( QWidget* parent, ScribusView *pCurrentView, const QString& masterPageName) : QWidget(parent)
45 {
46 	m_doc  = pCurrentView->m_doc;
47 	m_view = pCurrentView;
48 
49 	setupUi(this);
50 	iconSetChange();
51 
52 	masterPageListBox->setSelectionMode(QAbstractItemView::ExtendedSelection);
53 	styleChange();
54 	languageChange();
55 
56 	if (masterPageName.isEmpty())
57 		m_currentPage = m_doc->MasterNames.begin().key();
58 	else
59 		m_currentPage = masterPageName;
60 	updateMasterPageList(m_currentPage);
61 	m_view->showMasterPage(m_doc->MasterNames[m_currentPage]);
62 
63 	setMinimumSize(sizeHint());
64 
65 	// signals and slots connections
66 	connect(ScQApp, SIGNAL(iconSetChanged()), this, SLOT(iconSetChange()));
67 	connectSignals();
68 }
69 
setView(ScribusView * view,const QString & masterPageName)70 void PagePalette_MasterPages::setView(ScribusView* view, const QString& masterPageName)
71 {
72 //	ScribusView* oldView = m_view;
73 //	QString oldPage = m_currentPage;
74 
75 	disconnectSignals();
76 	m_view = view;
77 	m_doc = m_view ? m_view->m_doc : nullptr;
78 
79 	if (!view)
80 	{
81 		this->setEnabled(false);
82 		return;
83 	}
84 
85 	if (masterPageName.isEmpty() && m_doc->masterPageMode())
86 		m_currentPage = m_doc->currentPage()->pageName();
87 	else if (masterPageName.isEmpty())
88 		m_currentPage = m_doc->MasterNames.begin().key();
89 	else
90 		m_currentPage = masterPageName;
91 
92 	QString currentPageName;
93 	if (m_doc->masterPageMode())
94 		currentPageName = m_doc->currentPage()->pageName();
95 	if (m_doc->masterPageMode() && m_currentPage != currentPageName)
96 		m_view->showMasterPage(m_doc->MasterNames[m_currentPage]);
97 
98 	this->setEnabled(m_doc->masterPageMode());
99 	connectSignals();
100 }
101 
connectSignals()102 void PagePalette_MasterPages::connectSignals()
103 {
104 	connect(duplicateButton, SIGNAL(clicked()), this, SLOT(duplicateMasterPage()));
105 	connect(deleteButton   , SIGNAL(clicked()), this, SLOT(deleteMasterPage()));
106 	connect(newButton      , SIGNAL(clicked()), this, SLOT(newMasterPage()));
107 	connect(importButton   , SIGNAL(clicked()), this, SLOT(importPage()));
108 	connect(masterPageListBox, SIGNAL(itemClicked(QListWidgetItem*)),
109 			 this, SLOT(selectMasterPage(QListWidgetItem*)));
110 	connect(masterPageListBox, SIGNAL(itemChanged(QListWidgetItem*)),
111 			 this, SLOT(renameMasterPage( QListWidgetItem*)));
112 	connect(finishButton   , SIGNAL(released()), this, SIGNAL(finished()));
113 }
114 
disconnectSignals()115 void PagePalette_MasterPages::disconnectSignals()
116 {
117 	disconnect(duplicateButton, SIGNAL(clicked()), this, SLOT(duplicateMasterPage()));
118 	disconnect(deleteButton   , SIGNAL(clicked()), this, SLOT(deleteMasterPage()));
119 	disconnect(newButton      , SIGNAL(clicked()), this, SLOT(newMasterPage()));
120 	disconnect(importButton   , SIGNAL(clicked()), this, SLOT(importPage()));
121 	disconnect(masterPageListBox, SIGNAL(itemClicked(QListWidgetItem*)),
122 			   this, SLOT(selectMasterPage(QListWidgetItem*)));
123 	disconnect(masterPageListBox, SIGNAL(itemChanged(QListWidgetItem*)),
124 			   this, SLOT(renameMasterPage( QListWidgetItem*)));
125 	disconnect(finishButton   , SIGNAL(released()), this, SIGNAL(finished()));
126 }
127 
iconSetChange()128 void PagePalette_MasterPages::iconSetChange()
129 {
130 	IconManager& iconManager = IconManager::instance();
131 
132 	importButton->setIcon(iconManager.loadIcon("16/document-open.png"));
133 	newButton->setIcon(iconManager.loadIcon("16/document-new.png"));
134 	duplicateButton->setIcon(iconManager.loadIcon("16/edit-copy.png"));
135 	deleteButton->setIcon(iconManager.loadIcon("16/edit-delete.png"));
136 }
137 
languageChange()138 void PagePalette_MasterPages::languageChange()
139 {
140 	setWindowTitle( tr( "Edit Master Pages" ) );
141 	duplicateButton->setToolTip( tr( "Duplicate the selected master page" ) );
142 	deleteButton->setToolTip( tr( "Delete the selected master page" ) );
143 	newButton->setToolTip( tr( "Add a new master page" ) );
144 	importButton->setToolTip( tr( "Import master pages from another document" ) );
145 }
146 
styleChange()147 void PagePalette_MasterPages::styleChange()
148 {
149 	PrefsManager& prefsManager = PrefsManager::instance();
150 	if (prefsManager.appPrefs.uiPrefs.style=="Macintosh" || (os_is_osx() && prefsManager.appPrefs.uiPrefs.style==""))
151 		finishButton->setMinimumHeight(32);
152 	else
153 		finishButton->setMinimumHeight(0);
154 }
155 
deleteMasterPage()156 void PagePalette_MasterPages::deleteMasterPage()
157 {
158 	bool forceDelete = false;
159 
160 	// allow to delete multiple pages in one step
161 	foreach (QListWidgetItem * delItem, masterPageListBox->selectedItems())
162 	{
163 		m_currentPage = delItem->text();
164 
165 		if (isReservedName(m_currentPage))
166 			continue;
167 
168 		QString extraWarn = "";
169 
170 		if (!forceDelete)
171 		{
172 			for (int i=0; i < m_doc->DocPages.count(); ++i )
173 			{
174 				if (m_doc->DocPages[i]->masterPageName() == m_currentPage)
175 					extraWarn = tr("This master page is used at least once in the document.");
176 			}
177 			int exit = ScMessageBox::warning(this,
178 										CommonStrings::trWarning,
179 										tr("Do you really want to delete master page \"%1\"?").arg(m_currentPage)+"\n"+extraWarn,
180 										QMessageBox::Yes | QMessageBox::YesToAll | QMessageBox::No | QMessageBox::Cancel,
181 										QMessageBox::NoButton,	// GUI default
182 										QMessageBox::YesToAll);	// batch default
183 
184 			if (exit == QMessageBox::YesToAll)
185 				forceDelete = true;
186 			if (exit == QMessageBox::No)
187 				continue;
188 			if (exit == QMessageBox::Cancel)
189 				break;
190 		}
191 
192 		if (m_doc->appMode == modeEditClip)
193 			m_view->requestMode(submodeEndNodeEdit);
194 		m_doc->scMW()->deletePage2(m_doc->MasterNames[m_currentPage]);
195 		//<<CB TODO Move back into ScribusDoc::deleteMasterPage();
196 		//This must happen after the pages have been reformed (view/doc)
197 		m_doc->rebuildMasterNames();
198 		// Fix up any pages that refer to the deleted master page
199 		m_doc->replaceMasterPage(m_currentPage);
200 	}
201 
202 	// set the 1st MP for the other slots
203 	QMap<QString,int>::Iterator it = m_doc->MasterNames.begin();
204 	m_currentPage = it.key();
205 	updateMasterPageList(m_currentPage);
206 	selectMasterPage(m_currentPage);
207 }
208 
duplicateMasterPage()209 void PagePalette_MasterPages::duplicateMasterPage()
210 {
211 	QString name = getUniqueName(m_currentPage, m_doc->MasterNames);
212 	QScopedPointer<NewTm> dia(new NewTm(this, tr("&Name:"), tr("New Master Page"), m_doc, name));
213 	if (!dia->exec())
214 		return;
215 
216 	if (m_doc->appMode == modeEditClip)
217 		m_view->requestMode(submodeEndNodeEdit);
218 
219 	name = dia->Answer->text();
220 
221 	if (name.isEmpty())
222 		return;
223 
224 	name = getNonReservedName(name);
225 	name = getUniqueName(name, m_doc->MasterNames);
226 
227 //	PrefsManager& prefsManager = PrefsManager::instance();
228 	int inde = m_doc->MasterNames[m_currentPage];
229 	int nr = m_doc->Pages->count();
230 	ScPage* from = m_doc->Pages->at(inde);
231 	ScPage* destination = m_doc->addMasterPage(nr, name);
232 	if (m_doc->pagePositioning() != singlePage)
233 	{
234 		int lp = dia->Links->currentIndex();
235 		if (lp == 0)
236 			lp = 1;
237 		else if (lp == static_cast<int>(dia->Links->count()-1))
238 			lp = 0;
239 		else
240 			lp++;
241 		destination->LeftPg = lp;
242 	}
243 	destination->initialMargins.setTop(from->initialMargins.top());
244 	destination->initialMargins.setBottom(from->initialMargins.bottom());
245 	if (m_doc->pageSets()[m_doc->pagePositioning()].Columns == 1)
246 	{
247 		destination->initialMargins.setLeft(from->initialMargins.left());
248 		destination->initialMargins.setRight(from->initialMargins.right());
249 	}
250 	else
251 	{
252 		if (destination->LeftPg != from->LeftPg)
253 		{
254 			if (destination->LeftPg > 1)
255 			{
256 				destination->initialMargins.setRight(from->initialMargins.left());
257 				destination->initialMargins.setLeft(from->initialMargins.left());
258 			}
259 			else
260 			{
261 				destination->initialMargins.setLeft(from->initialMargins.left());
262 				destination->initialMargins.setRight(from->initialMargins.right());
263 			}
264 		}
265 		else
266 		{
267 			destination->initialMargins.setLeft(from->initialMargins.left());
268 			destination->initialMargins.setRight(from->initialMargins.right());
269 		}
270 	}
271 	//#8321 : incorrect selection of master page on new mp creation/duplication
272 	//m_doc->setCurrentPage(destination);
273 	updateMasterPageList(name);
274 	selectMasterPage(name);
275 	uint oldItems = m_doc->Items->count();
276 	uint end2 = m_doc->MasterItems.count();
277 	int GrMax = m_doc->GroupCounter;
278 	m_doc->m_Selection->clear();
279 	if (oldItems>0)
280 	{
281 		ScLayers::iterator it;
282 		m_doc->m_Selection->delaySignalsOn();
283 		for (it = m_doc->Layers.begin(); it != m_doc->Layers.end(); ++it)
284 		{
285 			for (uint ite = 0; ite < oldItems; ++ite)
286 			{
287 				PageItem *itemToCopy = m_doc->Items->at(ite);
288 				if (itemToCopy->OwnPage == inde && (it->ID == itemToCopy->m_layerID))
289 					m_doc->m_Selection->addItem(itemToCopy, true);
290 			}
291 			if (m_doc->m_Selection->count() != 0)
292 			{
293 				ScriXmlDoc ss;
294 				QString buffer = ss.writeElem(m_doc, m_doc->m_Selection);
295 				ss.readElemToLayer(buffer, m_doc, destination->xOffset(), destination->yOffset(), false, true, it->ID);
296 				m_doc->m_Selection->clear();
297 			}
298 		}
299 		m_doc->m_Selection->clear();
300 		m_doc->m_Selection->delaySignalsOff();
301 	}
302 	uint end3 = m_doc->MasterItems.count();
303 	for (uint a = end2; a < end3; ++a)
304 	{
305 		PageItem *newItem = m_doc->MasterItems.at(a);
306 		int masterPageIndex = m_doc->MasterNames[name];
307 		newItem->setMasterPage(masterPageIndex, name);
308 	}
309 	from->guides.copy(&destination->guides);
310 	m_doc->GroupCounter = GrMax + 1;
311 	m_view->deselectItems(true);
312 	m_doc->setLoading(false);
313 	m_view->reformPages();
314 	m_view->DrawNew();
315 }
316 
newMasterPage()317 void PagePalette_MasterPages::newMasterPage()
318 {
319 	int nr = m_doc->Pages->count();
320 
321 	QScopedPointer<NewTm> dia(new NewTm(this, tr("Name:"), tr("New MasterPage"), m_doc, tr("New Master Page %1").arg(nr)));
322 	if (!dia->exec())
323 		return;
324 
325 	if (m_doc->appMode == modeEditClip)
326 		m_view->requestMode(submodeEndNodeEdit);
327 
328 	auto name = dia->Answer->text();
329 
330 	if (name.isEmpty())
331 		return;
332 
333 	name = getNonReservedName(name);
334 	name = getUniqueName(name, m_doc->MasterNames);
335 
336 	m_doc->setCurrentPage(m_doc->addMasterPage(nr, name));
337 	if (m_doc->pagePositioning() != singlePage)
338 	{
339 		int lp = dia->Links->currentIndex();
340 		if (lp == 0)
341 			lp = 1;
342 		else if (lp == static_cast<int>(dia->Links->count()-1))
343 			lp = 0;
344 		else
345 			lp++;
346 		m_doc->Pages->at(nr)->LeftPg = lp;
347 	}
348 	updateMasterPageList(name);
349 	//#8321 : incorrect selection of master page on new mp creation/duplictation
350 	//m_view->showMasterPage(m_doc->MasterNames[name]);
351 	selectMasterPage(name);
352 	m_view->reformPages();
353 }
354 
importPage()355 void PagePalette_MasterPages::importPage()
356 {
357 	QScopedPointer<MergeDoc> dia(new MergeDoc(this, true));
358 	if (!dia->exec())
359 		return;
360 
361 	if (m_doc->appMode == modeEditClip)
362 		m_view->requestMode(submodeEndNodeEdit);
363 
364 	qApp->setOverrideCursor(QCursor(Qt::WaitCursor));
365 	int nr = m_doc->Pages->count();
366 
367 	auto indexes = dia->getMasterPageIndexes();
368 	QString lastName;
369 	for (auto name: dia->getMasterPageNames())
370 	{
371 		name = getUniqueName(name, m_doc->MasterNames);
372 		m_doc->setCurrentPage(m_doc->addMasterPage(nr, name));
373 
374 		nr++;
375 		qApp->processEvents();
376 
377 		//CB TODO: If we are loading to a new name, we rely on doc->onpage in
378 		//FileLoader::PasteItem as this call doesn't pass in the new destination page
379 		m_doc->scMW()->loadPage(dia->getFromDoc(), indexes.first(), true, name);
380 		qApp->processEvents();
381 
382 		indexes.removeFirst();
383 		lastName = name;
384 	}
385 
386 	updateMasterPageList(lastName);
387 	m_view->showMasterPage(m_doc->MasterNames[lastName]);
388 	qApp->restoreOverrideCursor();
389 }
390 
selectMasterPage(QListWidgetItem * item)391 void PagePalette_MasterPages::selectMasterPage(QListWidgetItem *item)
392 {
393 	m_currentPage = item->data(Qt::UserRole).toString();
394 
395 	bool deleteEnabled = true;
396 	deleteEnabled &= (m_doc->MasterNames.count() > 1);
397 	deleteEnabled &= (m_currentPage != CommonStrings::masterPageNormal);
398 	deleteEnabled &= (m_currentPage != CommonStrings::trMasterPageNormal);
399 	deleteButton->setEnabled(deleteEnabled);
400 
401 	if (m_doc->appMode == modeEditClip)
402 		m_view->requestMode(submodeEndNodeEdit);
403 	else if (m_doc->appMode == modeEdit || m_doc->appMode == modeEditGradientVectors)
404 		m_view->requestMode(modeNormal);
405 	m_view->showMasterPage(m_doc->MasterNames[m_currentPage]);
406 
407 	this->setEnabled(true);
408 }
409 
selectMasterPage(const QString & name)410 void PagePalette_MasterPages::selectMasterPage(const QString& name)
411 {
412 	if (!m_doc || !m_view)
413 		return;
414 
415 	m_currentPage.clear();
416 	for (int i = 0; i < masterPageListBox->count(); ++i)
417 	{
418 		QListWidgetItem* item = masterPageListBox->item(i);
419 		QString realPageName  = item->data(Qt::UserRole).toString();
420 		if (item->text() == name)
421 		{
422 			m_currentPage = realPageName;
423 			break;
424 		}
425 		if (realPageName == name)
426 		{
427 			m_currentPage = name;
428 			break;
429 		}
430 	}
431 
432 	if (m_currentPage.isEmpty())
433 		m_currentPage = m_doc->MasterPages.at(0)->pageName();
434 
435 	bool deleteEnabled = true;
436 	deleteEnabled &= (m_doc->MasterNames.count() > 1);
437 	deleteEnabled &= (m_currentPage != CommonStrings::masterPageNormal);
438 	deleteEnabled &= (m_currentPage != CommonStrings::trMasterPageNormal);
439 	deleteButton->setEnabled(deleteEnabled);
440 
441 	if (m_doc->appMode == modeEditClip)
442 		m_view->requestMode(submodeEndNodeEdit);
443 	m_view->showMasterPage(m_doc->MasterNames[m_currentPage]);
444 
445 	this->setEnabled(true);
446 }
447 
updateMasterPageList()448 void PagePalette_MasterPages::updateMasterPageList()
449 {
450 	QString masterPageName = (m_doc->MasterNames.contains(m_currentPage)) ? m_currentPage : CommonStrings::masterPageNormal;
451 	updateMasterPageList(masterPageName);
452 }
453 
updateMasterPageList(QString masterPageName)454 void PagePalette_MasterPages::updateMasterPageList(QString masterPageName)
455 {
456 	if (!m_doc || !m_view)
457 		return;
458 
459 	masterPageListBox->clear();
460 	for (QMap<QString,int>::Iterator it = m_doc->MasterNames.begin(); it != m_doc->MasterNames.end(); ++it)
461 	{
462 		QString mpName = it.key();
463 
464 		if (it.key() == CommonStrings::masterPageNormal)
465 			mpName = CommonStrings::trMasterPageNormal;
466 		QListWidgetItem* mpItem = new QListWidgetItem(mpName);
467 
468 		if (!isReservedName(mpName))
469 			mpItem->setFlags(mpItem->flags() |= Qt::ItemIsEditable);
470 		else
471 			mpItem->setFlags(mpItem->flags() &= ~Qt::ItemIsEditable);
472 
473 		mpItem->setData(Qt::UserRole, it.key());
474 		masterPageListBox->addItem(mpItem);
475 	}
476 	deleteButton->setEnabled(m_doc->MasterNames.count() != 1);
477 	if (masterPageName == CommonStrings::masterPageNormal)
478 	{
479 		masterPageName = CommonStrings::trMasterPageNormal;
480 		deleteButton->setEnabled(false);
481 	}
482 	QList<QListWidgetItem *> itL = masterPageListBox->findItems(masterPageName, Qt::MatchExactly);
483 	if (itL.count() != 0)
484 		itL.at(0)->setSelected(true);
485 }
486 
renameMasterPage(QListWidgetItem * item)487 void PagePalette_MasterPages::renameMasterPage(QListWidgetItem * item)
488 {
489 	QString oldName(item->data(Qt::UserRole).toString());
490 	QString newName(item->text());
491 
492 	if (oldName == newName)
493 		return;
494 
495 	// FIXME: the checks in the old version of this function
496 	// and the ones in m_doc->renameMasterPage() did not match.
497 	// we should move all MasterPage related code to a new masterpage.h
498 	// file and remove code from scribusdoc.cpp and from here.
499 	// at the end, the code below should be:
500 	//
501 	// if (m_doc->m_masterPage->rename(oldName, newName))
502 	// {
503 	//		bool sigBlocked = masterPageListBox->blockSignals(true);
504 	//		item->setData(Qt::UserRole, newName);
505 	//		masterPageListBox->blockSignals(sigBlocked);
506 	//		updateMasterPageList(newName);
507 	// }
508 	// else
509 	// {
510 	//		bool sigBlocked = masterPageListBox->blockSignals(true);
511 	//		item->setText(getTranslatedReservedName(oldName));
512 	//		masterPageListBox->blockSignals(sigBlocked);
513 	// }
514 
515 	if (newName.isEmpty() || isReservedName(newName) ||
516 		!m_doc->MasterNames.contains(oldName) ||
517 		m_doc->MasterNames.contains(newName))
518 	{
519 		bool sigBlocked = masterPageListBox->blockSignals(true);
520 		item->setText(oldName);
521 		masterPageListBox->blockSignals(sigBlocked);
522 	}
523 	else
524 	{
525 		bool sigBlocked = masterPageListBox->blockSignals(true);
526 		m_doc->renameMasterPage(oldName, newName);
527 		item->setData(Qt::UserRole, newName);
528 		masterPageListBox->blockSignals(sigBlocked);
529 		updateMasterPageList(newName);
530 	}
531 }
532 
533 
changeEvent(QEvent * e)534 void PagePalette_MasterPages::changeEvent(QEvent *e)
535 {
536 	if (e->type() == QEvent::LanguageChange)
537 	{
538 		languageChange();
539 		return;
540 	}
541 	if (e->type() == QEvent::StyleChange)
542 	{
543 		styleChange();
544 		return;
545 	}
546 
547 	QWidget::changeEvent(e);
548 }
549 
550 // FIXME: move to a new masterpages.h
isReservedName(const QString & name) const551 bool PagePalette_MasterPages::isReservedName(const QString& name) const
552 {
553 	return name == CommonStrings::masterPageNormal ||
554 		name == CommonStrings::trMasterPageNormal ||
555 		name == CommonStrings::masterPageNormalLeft ||
556 		name == CommonStrings::trMasterPageNormalLeft ||
557 		name == CommonStrings::masterPageNormalMiddle ||
558 		name == CommonStrings::trMasterPageNormalMiddle ||
559 		name == CommonStrings::masterPageNormalRight ||
560 		name == CommonStrings::trMasterPageNormalRight;
561 }
562 
563 // FIXME: move to a new masterpages.h
getNonReservedName(const QString & name) const564 QString PagePalette_MasterPages::getNonReservedName(const QString& name) const
565 {
566 	const QStringList reservedNames = {
567 		CommonStrings::masterPageNormal,
568 		CommonStrings::trMasterPageNormal,
569 		CommonStrings::trMasterPageNormalLeft,
570 		CommonStrings::trMasterPageNormalMiddle,
571 		CommonStrings::trMasterPageNormalRight
572 	};
573 	return getUniqueName(name, reservedNames);
574 }
575 
576 // FIXME: move to a new masterpages.h
getTranslatedReservedName(const QString & name) const577 QString PagePalette_MasterPages::getTranslatedReservedName(const QString& name) const
578 {
579 	if (name == CommonStrings::masterPageNormal)
580 		return CommonStrings::trMasterPageNormal;
581 	if (name == CommonStrings::masterPageNormalLeft)
582 		return CommonStrings::trMasterPageNormalLeft;
583 	if (name == CommonStrings::masterPageNormalMiddle)
584 		return CommonStrings::trMasterPageNormalMiddle;
585 	if (name == CommonStrings::masterPageNormalRight)
586 		return CommonStrings::trMasterPageNormalRight;
587 	return name;
588 }
589