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 "smcellstyle.h"
15 #include "smcellstylewidget.h"
16 #include "ui/scmessagebox.h"
17
SMCellStyle()18 SMCellStyle::SMCellStyle()
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 SMCellStyleWidget();
25 Q_CHECK_PTR(m_page);
26
27 m_widget->addTab(m_page, tr("Properties"));
28 }
29
~SMCellStyle()30 SMCellStyle::~SMCellStyle()
31 {
32 delete m_page;
33 delete m_widget;
34 m_page = nullptr;
35 m_widget = nullptr;
36 }
37
widget()38 QTabWidget* SMCellStyle::widget()
39 {
40 return m_widget;
41 }
42
typeNamePlural()43 QString SMCellStyle::typeNamePlural()
44 {
45 return tr("Cell Styles");
46 }
47
typeNameSingular()48 QString SMCellStyle::typeNameSingular()
49 {
50 return tr("Cell Style");
51 }
52
setCurrentDoc(ScribusDoc * doc)53 void SMCellStyle::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> SMCellStyle::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 SMCellStyle::reload()
101 {
102 updateStylesCache();
103 }
104
selected(const QStringList & styleNames)105 void SMCellStyle::selected(const QStringList &styleNames)
106 {
107 m_selection.clear();
108 m_selectionIsDirty = false;
109 removeConnections();
110 QList<CellStyle> cellStyles;
111
112 m_cachedStyles.invalidate();
113
114 for (int i = 0; i < m_cachedStyles.count(); ++i)
115 cellStyles << 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::trDefaultCellStyle)
122 index = m_cachedStyles.find(CommonStrings::DefaultCellStyle);
123 if (index > -1)
124 m_selection.append(&m_cachedStyles[index]);
125 }
126 m_page->show(m_selection, cellStyles, PrefsManager::instance().appPrefs.docSetupPrefs.language, m_doc->unitIndex());
127 setupConnections();
128 }
129
fromSelection() const130 QString SMCellStyle::fromSelection() const
131 {
132 // TODO: Implement this once we have cell items.
133 return QString();
134 }
135
toSelection(const QString & styleName) const136 void SMCellStyle::toSelection(const QString &styleName) const
137 {
138 // TODO: Implement this once we have cell items.
139 }
140
newStyle()141 QString SMCellStyle::newStyle()
142 {
143 Q_ASSERT(m_doc);
144
145 QString name = getUniqueName(tr("New Style"));
146 CellStyle 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 SMCellStyle::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::trDefaultCellStyle)
159 fromStyleName = CommonStrings::DefaultCellStyle;
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 CellStyle cellStyle(m_cachedStyles.get(fromStyleName));
168 cellStyle.setDefaultStyle(false);
169 cellStyle.setName(styleName);
170 cellStyle.setShortcut(QString());
171 m_cachedStyles.create(cellStyle);
172
173 return styleName;
174 }
175
apply()176 void SMCellStyle::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->redefineCellStyles(m_cachedStyles, false);
191 m_doc->replaceCellStyles(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 SMCellStyle::editMode(bool isOn)
202 {
203 if (isOn)
204 updateStylesCache();
205 }
206
isDefaultStyle(const QString & styleName) const207 bool SMCellStyle::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::trDefaultCellStyle == styleName)
216 {
217 index = m_cachedStyles.find(CommonStrings::DefaultCellStyle);
218 if (index > -1)
219 result = m_cachedStyles[index].isDefaultStyle();
220 }
221 }
222 return result;
223 }
224
setDefaultStyle(bool isDefaultStyle)225 void SMCellStyle::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 SMCellStyle::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::trDefaultCellStyle == styleName)
250 {
251 index = m_cachedStyles.find(CommonStrings::DefaultCellStyle);
252 if (index > -1)
253 result = m_cachedStyles[index].shortcut();
254 }
255 }
256 return result;
257 }
258
setShortcut(const QString & shortcut)259 void SMCellStyle::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 SMCellStyle::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 CellStyle& cellStyle = m_cachedStyles[i];
297 QString parentName = cellStyle.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::trDefaultCellStyle)
314 replacementName = CommonStrings::DefaultCellStyle;
315 if (!cellStyle.canInherit(replacementName))
316 replacementName = QString();
317 if (!replacementName.isEmpty() && (m_cachedStyles.find(replacementName) < 0))
318 replacementName = QString();
319 cellStyle.setParent(replacementName);
320 }
321 }
322
nameChanged(const QString & newName)323 void SMCellStyle::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 CellStyle 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 SMCellStyle::getUniqueName(const QString &name)
377 {
378 return m_cachedStyles.getUniqueCopyName(name);
379 }
380
languageChange()381 void SMCellStyle::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 SMCellStyle::unitChange()
391 {
392 // Unimplemented.
393 }
394
updateStylesCache()395 void SMCellStyle::updateStylesCache()
396 {
397 if (!m_doc)
398 return; // No document available.
399 m_selection.clear();
400 m_cachedStyles.clear();
401 m_deleted.clear();
402 m_cachedStyles.redefine(m_doc->cellStyles(), true);
403 }
404
setupConnections()405 void SMCellStyle::setupConnections()
406 {
407 if (!m_page)
408 return;
409 connect(m_page->fillColor, SIGNAL(activated(const QString&)), this, SLOT(slotFillColor()));
410 connect(m_page->fillShade, SIGNAL(clicked()), this, SLOT(slotFillShade()));
411 connect(m_page->parentCombo, SIGNAL(activated(const QString&)), this, SLOT(slotParentChanged(const QString&)));
412 }
413
removeConnections()414 void SMCellStyle::removeConnections()
415 {
416 if (!m_page)
417 return;
418 disconnect(m_page->fillColor, SIGNAL(activated(const QString&)), this, SLOT(slotFillColor()));
419 disconnect(m_page->fillShade, SIGNAL(clicked()), this, SLOT(slotFillShade()));
420 disconnect(m_page->parentCombo, SIGNAL(activated(const QString&)), this, SLOT(slotParentChanged(const QString&)));
421 }
422
slotFillColor()423 void SMCellStyle::slotFillColor()
424 {
425 if (m_page->fillColor->useParentValue())
426 {
427 for (int i = 0; i < m_selection.count(); ++i)
428 m_selection[i]->resetFillColor();
429 }
430 else
431 {
432 QString col(m_page->fillColor->currentText());
433 for (int i = 0; i < m_selection.count(); ++i)
434 {
435 m_selection[i]->setFillColor(col);
436 }
437 }
438 if (!m_selectionIsDirty)
439 {
440 m_selectionIsDirty = true;
441 emit selectionDirty();
442 }
443 }
444
slotFillShade()445 void SMCellStyle::slotFillShade()
446 {
447 if (m_page->fillShade->useParentValue())
448 {
449 for (int i = 0; i < m_selection.count(); ++i)
450 {
451 m_selection[i]->resetFillShade();
452 }
453 }
454 else
455 {
456 int fs = m_page->fillShade->getValue();
457 for (int i = 0; i < m_selection.count(); ++i)
458 {
459 m_selection[i]->setFillShade(fs);
460 }
461 }
462 if (!m_selectionIsDirty)
463 {
464 m_selectionIsDirty = true;
465 emit selectionDirty();
466 }
467 }
468
slotParentChanged(const QString & parent)469 void SMCellStyle::slotParentChanged(const QString &parent)
470 {
471 Q_ASSERT(!parent.isNull());
472
473 bool loop = false, parentLoop = false;
474 const BaseStyle* parentStyle = (!parent.isEmpty()) ? m_cachedStyles.resolve(parent) : nullptr;
475 QStringList sel;
476
477 for (int i = 0; i < m_selection.count(); ++i)
478 {
479 loop = false;
480 // Check if setting parent won't create a loop
481 const BaseStyle* pStyle = parentStyle;
482 while (pStyle)
483 {
484 if (pStyle->hasParent() && (pStyle->parent() == m_selection[i]->name()))
485 {
486 loop = parentLoop = true;
487 break;
488 }
489 pStyle = pStyle->hasParent() ? pStyle->parentStyle() : nullptr;
490 }
491 if (!loop)
492 {
493 m_selection[i]->erase();
494 m_selection[i]->setParent(parent);
495 }
496 sel << m_selection[i]->name();
497 }
498
499 if (parentLoop)
500 ScMessageBox::warning(this->widget(), CommonStrings::trWarning, tr("Setting that style as parent would create an infinite loop."));
501
502 selected(sel);
503
504 if (!m_selectionIsDirty)
505 {
506 m_selectionIsDirty = true;
507 emit selectionDirty();
508 }
509 }
510