1 /*
2 Copyright (C) 2011 Elvis Stansvik <elvstone@gmail.com>
3 
4 For general Scribus (>=1.3.2) copyright and licensing information please refer
5 to the COPYING file provided with the program. Following this notice may exist
6 a copyright and/or license notice that predates the release of Scribus 1.3.2
7 for which a new license (GPL+exception) is in place.
8 */
9 
10 #include <QTabWidget>
11 #include <QMessageBox>
12 
13 #include "prefsmanager.h"
14 #include "smtablestyle.h"
15 #include "smtablestylewidget.h"
16 #include "ui/scmessagebox.h"
17 
SMTableStyle()18 SMTableStyle::SMTableStyle()
19 {
20 	m_widget = new QTabWidget();
21 	Q_CHECK_PTR(m_widget);
22 	m_widget->setContentsMargins(5, 5, 5, 5);
23 
24 	m_page = new SMTableStyleWidget();
25 	Q_CHECK_PTR(m_page);
26 
27 	m_widget->addTab(m_page, tr("Properties"));
28 }
29 
~SMTableStyle()30 SMTableStyle::~SMTableStyle()
31 {
32 	delete m_page;
33 	delete m_widget;
34 	m_page = nullptr;
35 	m_widget = nullptr;
36 }
37 
widget()38 QTabWidget* SMTableStyle::widget()
39 {
40 	return m_widget;
41 }
42 
typeNamePlural()43 QString SMTableStyle::typeNamePlural()
44 {
45 	return tr("Table Styles");
46 }
47 
typeNameSingular()48 QString SMTableStyle::typeNameSingular()
49 {
50 	return tr("Table Style");
51 }
52 
setCurrentDoc(ScribusDoc * doc)53 void SMTableStyle::setCurrentDoc(ScribusDoc *doc)
54 {
55 	m_doc = doc;
56 
57 	if (m_page)
58 		m_page->setDoc(doc);
59 
60 	if (!m_doc)
61 	{
62 		removeConnections();
63 		m_selection.clear();
64 		m_cachedStyles.clear();
65 	}
66 }
67 
styles(bool reloadFromDoc)68 QList<StyleName> SMTableStyle::styles(bool reloadFromDoc)
69 {
70 	QList<StyleName> stylesList;
71 
72 	if (!m_doc)
73 		return stylesList; // No document available.
74 
75 	if (reloadFromDoc)
76 		updateStylesCache(); // Update cache.
77 
78 	// Return a list of names of cached styles.
79 	for (int i = 0; i < m_cachedStyles.count(); ++i)
80 	{
81 		if (m_cachedStyles[i].hasName())
82 		{
83 			QString styleName(m_cachedStyles[i].displayName());
84 			QString parentName;
85 
86 			if (m_cachedStyles[i].hasParent())
87 			{
88 				const BaseStyle* parentStyle = m_cachedStyles[i].parentStyle();
89 				if (parentStyle)
90 					parentName = parentStyle->displayName();
91 			}
92 
93 			stylesList << StyleName(styleName, parentName);
94 		}
95 	}
96 
97 	return stylesList;
98 }
99 
reload()100 void SMTableStyle::reload()
101 {
102 	updateStylesCache();
103 }
104 
selected(const QStringList & styleNames)105 void SMTableStyle::selected(const QStringList &styleNames)
106 {
107 	m_selection.clear();
108 	m_selectionIsDirty = false;
109 	removeConnections();
110 	QList<TableStyle> tableStyles;
111 
112 	m_cachedStyles.invalidate();
113 
114 	for (int i = 0; i < m_cachedStyles.count(); ++i)
115 		tableStyles << m_cachedStyles[i];
116 
117 	for (const QString& styleName : styleNames)
118 	{
119 		int index = m_cachedStyles.find(styleName);
120 		// FIXME: #7133: Use .isDefaultStyle() instead here rather than relying on tr text comparison
121 		if (index < 0 && styleName == CommonStrings::trDefaultTableStyle)
122 			index = m_cachedStyles.find(CommonStrings::DefaultTableStyle);
123 		if (index > -1)
124 			m_selection.append(&m_cachedStyles[index]);
125 	}
126 	m_page->show(m_selection, tableStyles, PrefsManager::instance().appPrefs.docSetupPrefs.language, m_doc->unitIndex());
127 	setupConnections();
128 }
129 
fromSelection() const130 QString SMTableStyle::fromSelection() const
131 {
132 	// TODO: Implement this once we have table items.
133 	return QString();
134 }
135 
toSelection(const QString & styleName) const136 void SMTableStyle::toSelection(const QString &styleName) const
137 {
138 	// TODO: Implement this once we have table items.
139 }
140 
newStyle()141 QString SMTableStyle::newStyle()
142 {
143 	Q_ASSERT(m_doc);
144 
145 	QString name = getUniqueName(tr("New Style"));
146 	TableStyle style;
147 	style.setDefaultStyle(false);
148 	style.setName(name);
149 	m_cachedStyles.create(style);
150 	return name;
151 }
152 
newStyle(const QString & fromStyle)153 QString SMTableStyle::newStyle(const QString &fromStyle)
154 {
155 	// #7179, do our name switch yet again to handle this properly for default styles
156 	// FIXME: use isDefaultStyle somehow
157 	QString fromStyleName(fromStyle);
158 	if (fromStyle == CommonStrings::trDefaultTableStyle)
159 		fromStyleName = CommonStrings::DefaultTableStyle;
160 
161 	Q_ASSERT(m_cachedStyles.resolve(fromStyleName));
162 	if (!m_cachedStyles.resolve(fromStyleName))
163 		return QString();
164 
165 	// Copy the style with name constructed from the original name.
166 	QString styleName(getUniqueName(fromStyle));
167 	TableStyle tableStyle(m_cachedStyles.get(fromStyleName));
168 	tableStyle.setDefaultStyle(false);
169 	tableStyle.setName(styleName);
170 	tableStyle.setShortcut(QString());
171 	m_cachedStyles.create(tableStyle);
172 
173 	return styleName;
174 }
175 
apply()176 void SMTableStyle::apply()
177 {
178 	if (!m_doc)
179 		return; // No document available.
180 
181 	// Handle replacement of deleted styles.
182 	QMap<QString, QString> replacement;
183 	for (int i = 0; i < m_deleted.count(); ++i)
184 	{
185 		if (m_deleted[i].first == m_deleted[i].second)
186 			continue; // Nothing to do.
187 		replacement[m_deleted[i].first] = m_deleted[i].second;
188 	}
189 
190 	m_doc->redefineTableStyles(m_cachedStyles, false);
191 	m_doc->replaceTableStyles(replacement);
192 
193 	m_deleted.clear(); // Deletion done at this point.
194 
195 	// TODO: We should probably have something similar to this for tables/cells.
196 	//m_doc->scMW()->requestUpdate(reqTextStylesUpdate);
197 
198 	m_doc->changed();
199 }
200 
editMode(bool isOn)201 void SMTableStyle::editMode(bool isOn)
202 {
203 	if (isOn)
204 		updateStylesCache();
205 }
206 
isDefaultStyle(const QString & styleName) const207 bool SMTableStyle::isDefaultStyle(const QString &styleName) const
208 {
209 	int index = m_cachedStyles.find(styleName);
210 	bool result = false;
211 	if (index > -1)
212 		result = m_cachedStyles[index].isDefaultStyle();
213 	else
214 	{
215 		if (CommonStrings::trDefaultTableStyle == styleName)
216 		{
217 			index = m_cachedStyles.find(CommonStrings::DefaultTableStyle);
218 			if (index > -1)
219 				result = m_cachedStyles[index].isDefaultStyle();
220 		}
221 	}
222 	return result;
223 }
224 
setDefaultStyle(bool isDefaultStyle)225 void SMTableStyle::setDefaultStyle(bool isDefaultStyle)
226 {
227 	Q_ASSERT(m_selection.count() == 1);
228 	if (m_selection.count() != 1)
229 		return;
230 
231 	m_selection[0]->setDefaultStyle(isDefaultStyle);
232 
233 	if (!m_selectionIsDirty)
234 	{
235 		m_selectionIsDirty = true;
236 		emit selectionDirty();
237 	}
238 }
239 
shortcut(const QString & styleName) const240 QString SMTableStyle::shortcut(const QString &styleName) const
241 {
242 	QString result;
243 	int index = m_cachedStyles.find(styleName);
244 	if (index > -1)
245 		result = m_cachedStyles[index].shortcut();
246 	else
247 	{
248 		// FIXME: Use isDefaultStyle somehow.
249 		if (CommonStrings::trDefaultTableStyle == styleName)
250 		{
251 			index = m_cachedStyles.find(CommonStrings::DefaultTableStyle);
252 			if (index > -1)
253 				result = m_cachedStyles[index].shortcut();
254 		}
255 	}
256 	return result;
257 }
258 
setShortcut(const QString & shortcut)259 void SMTableStyle::setShortcut(const QString &shortcut)
260 {
261 	Q_ASSERT(m_selection.count() == 1);
262 	if (m_selection.count() != 1)
263 		return;
264 
265 	m_selection[0]->setShortcut(shortcut);
266 
267 	if (!m_selectionIsDirty)
268 	{
269 		m_selectionIsDirty = true;
270 		emit selectionDirty();
271 	}
272 }
273 
deleteStyles(const QList<RemoveItem> & removeList)274 void SMTableStyle::deleteStyles(const QList<RemoveItem> &removeList)
275 {
276 	for (const RemoveItem& removeItem : removeList)
277 	{
278 		for (int i = 0; i < m_selection.count(); ++i)
279 		{
280 			if (m_selection[i]->name() == removeItem.first)
281 			{
282 				m_selection.removeAt(i);
283 				break;
284 			}
285 		}
286 
287 		int index = m_cachedStyles.find(removeItem.first);
288 		if (index > -1)
289 			m_cachedStyles.remove(index);
290 		m_deleted << removeItem;
291 	}
292 
293 	// Check other styles and replace inherited styles if necessary
294 	for (int i = 0; i < m_cachedStyles.count(); ++i)
295 	{
296 		TableStyle& tableStyle = m_cachedStyles[i];
297 		QString parentName = tableStyle.parent();
298 		if (parentName.isEmpty())
299 			continue;
300 
301 		QString replacementName = parentName;
302 		for (int j = 0; j < removeList.count(); ++j)
303 		{
304 			if (removeList.at(j).first == parentName)
305 			{
306 				replacementName = removeList.at(j).second;
307 				break;
308 			}
309 		}
310 
311 		if (replacementName == parentName)
312 			continue;
313 		if (replacementName == CommonStrings::trDefaultTableStyle)
314 			replacementName = CommonStrings::DefaultTableStyle;
315 		if (!tableStyle.canInherit(replacementName))
316 			replacementName = QString();
317 		if (!replacementName.isEmpty() && (m_cachedStyles.find(replacementName) < 0))
318 			replacementName = QString();
319 		tableStyle.setParent(replacementName);
320 	}
321 }
322 
nameChanged(const QString & newName)323 void SMTableStyle::nameChanged(const QString &newName)
324 {
325 	// Save the old name.
326 	QString oldName(m_selection[0]->name());
327 
328 	// Make a copy of the old style but with new name.
329 	TableStyle newStyle(*m_selection[0]);
330 	newStyle.setName(newName);
331 	m_cachedStyles.create(newStyle);
332 
333 	// Select the new style.
334 	m_selection.clear();
335 	m_selection.append(&m_cachedStyles[m_cachedStyles.find(newName)]);
336 
337 	// Remove old style from cache.
338 	for (int j = 0; j < m_cachedStyles.count(); ++j)
339 	{
340 		int index = m_cachedStyles.find(oldName);
341 		if (index > -1)
342 		{
343 			m_cachedStyles.remove(index);
344 			break;
345 		}
346 	}
347 
348 	// Set parent references to old style to new style.
349 	for (int j = 0; j < m_cachedStyles.count(); ++j)
350 	{
351 		if (m_cachedStyles[j].parent() == oldName)
352 			m_cachedStyles[j].setParent(newName);
353 	}
354 
355 	// Update the deleted list to reflect the name change.
356 	QList<RemoveItem>::iterator it;
357 	for (it = m_deleted.begin(); it != m_deleted.end(); ++it)
358 	{
359 		if ((*it).second == oldName)
360 		{
361 			oldName = (*it).first;
362 			m_deleted.erase(it);
363 			break;
364 		}
365 	}
366 	m_deleted.append(RemoveItem(oldName, newName));
367 
368 	// Mark selection as dirty.
369 	if (!m_selectionIsDirty)
370 	{
371 		m_selectionIsDirty = true;
372 		emit selectionDirty();
373 	}
374 }
375 
getUniqueName(const QString & name)376 QString SMTableStyle::getUniqueName(const QString &name)
377 {
378 	return m_cachedStyles.getUniqueCopyName(name);
379 }
380 
languageChange()381 void SMTableStyle::languageChange()
382 {
383 	if (m_widget && m_page)
384 	{
385 		m_widget->setTabText(m_widget->indexOf(m_page), tr("Properties"));
386 		m_page->languageChange();
387 	}
388 }
389 
unitChange()390 void SMTableStyle::unitChange()
391 {
392 	// Unimplemented.
393 }
394 
updateStylesCache()395 void SMTableStyle::updateStylesCache()
396 {
397 	if (!m_doc)
398 		return; // No document available.
399 
400 	m_selection.clear();
401 	m_cachedStyles.clear();
402 	m_deleted.clear();
403 
404 	m_cachedStyles.redefine(m_doc->tableStyles(), true);
405 }
406 
setupConnections()407 void SMTableStyle::setupConnections()
408 {
409 	if (!m_page)
410 		return;
411 	connect(m_page->fillColor, SIGNAL(activated(const QString&)), this, SLOT(slotFillColor()));
412 	connect(m_page->fillShade, SIGNAL(clicked()), this, SLOT(slotFillShade()));
413 	connect(m_page->parentCombo, SIGNAL(activated(const QString&)), this, SLOT(slotParentChanged(const QString&)));
414 }
415 
removeConnections()416 void SMTableStyle::removeConnections()
417 {
418 	if (!m_page)
419 		return;
420 	disconnect(m_page->fillColor, SIGNAL(activated(const QString&)), this, SLOT(slotFillColor()));
421 	disconnect(m_page->fillShade, SIGNAL(clicked()), this, SLOT(slotFillShade()));
422 	disconnect(m_page->parentCombo, SIGNAL(activated(const QString&)), this, SLOT(slotParentChanged(const QString&)));
423 }
424 
slotFillColor()425 void SMTableStyle::slotFillColor()
426 {
427 	if (m_page->fillColor->useParentValue())
428 	{
429 		for (int i = 0; i < m_selection.count(); ++i)
430 			m_selection[i]->resetFillColor();
431 	}
432 	else
433 	{
434 		QString col(m_page->fillColor->currentText());
435 		for (int i = 0; i < m_selection.count(); ++i)
436 		{
437 			m_selection[i]->setFillColor(col);
438 		}
439 	}
440 	if (!m_selectionIsDirty)
441 	{
442 		m_selectionIsDirty = true;
443 		emit selectionDirty();
444 	}
445 }
446 
slotFillShade()447 void SMTableStyle::slotFillShade()
448 {
449 	if (m_page->fillShade->useParentValue())
450 	{
451 		for (int i = 0; i < m_selection.count(); ++i)
452 		{
453 			m_selection[i]->resetFillShade();
454 		}
455 	}
456 	else
457 	{
458 		int fs = m_page->fillShade->getValue();
459 		for (int i = 0; i < m_selection.count(); ++i)
460 		{
461 			m_selection[i]->setFillShade(fs);
462 		}
463 	}
464 	if (!m_selectionIsDirty)
465 	{
466 		m_selectionIsDirty = true;
467 		emit selectionDirty();
468 	}
469 }
470 
slotParentChanged(const QString & parent)471 void SMTableStyle::slotParentChanged(const QString &parent)
472 {
473 	Q_ASSERT(!parent.isNull());
474 
475 	bool  loop = false, parentLoop = false;
476 	const BaseStyle* parentStyle = (!parent.isEmpty()) ? m_cachedStyles.resolve(parent) : nullptr;
477 	QStringList  sel;
478 
479 	for (int i = 0; i < m_selection.count(); ++i)
480 	{
481 		loop = false;
482 		// Check if setting parent won't create a loop
483 		const BaseStyle* pStyle = parentStyle;
484 		while (pStyle)
485 		{
486 			if (pStyle->hasParent() && (pStyle->parent() == m_selection[i]->name()))
487 			{
488 				loop = parentLoop = true;
489 				break;
490 			}
491 			pStyle = pStyle->hasParent() ? pStyle->parentStyle() : nullptr;
492 		}
493 		if (!loop)
494 		{
495 			m_selection[i]->erase();
496 			m_selection[i]->setParent(parent);
497 		}
498 		sel << m_selection[i]->name();
499 	}
500 
501 	if (parentLoop)
502 		ScMessageBox::warning(this->widget(), CommonStrings::trWarning, tr("Setting that style as parent would create an infinite loop."));
503 
504 	selected(sel);
505 
506 	if (!m_selectionIsDirty)
507 	{
508 		m_selectionIsDirty = true;
509 		emit selectionDirty();
510 	}
511 }
512