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