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 "colorlistbox.h"
8 
9 #include <cstdlib>
10 #include <QBitmap>
11 #include <QCursor>
12 #include <QEvent>
13 #include <QHelpEvent>
14 #include <QMenu>
15 #include <QPainter>
16 #include <QPersistentModelIndex>
17 #include <QPixmap>
18 #include <QSignalBlocker>
19 #include <QToolTip>
20 
21 #include "colorlistmodel.h"
22 #include "commonstrings.h"
23 #include "iconmanager.h"
24 #include "sccolorengine.h"
25 #include "scconfig.h"
26 #include "scribusapp.h"
27 #include "scribusdoc.h"
28 #include "util_color.h"
29 
30 class SCRIBUS_API ColorSmallItemDelegate : public ScListBoxPixmap<15, 15>
31 {
32 public:
ColorSmallItemDelegate()33 	ColorSmallItemDelegate(): ScListBoxPixmap<15, 15>() {};
~ColorSmallItemDelegate()34 	~ColorSmallItemDelegate() {};
35 
36 	void redraw(const QVariant&) const override;
37 	QString text(const QVariant&) const override;
38 };
39 
40 class SCRIBUS_API ColorWideItemDelegate : public ScListBoxPixmap<30, 15>
41 {
42 public:
ColorWideItemDelegate()43 	ColorWideItemDelegate(): ScListBoxPixmap<30, 15>() {};
~ColorWideItemDelegate()44 	~ColorWideItemDelegate() {};
45 
46 	void redraw(const QVariant&) const override;
47 	QString text(const QVariant&) const override;
48 };
49 
50 class SCRIBUS_API ColorFancyItemDelegate : public ScListBoxPixmap<60, 15>
51 {
52 public:
53 	ColorFancyItemDelegate();
~ColorFancyItemDelegate()54 	~ColorFancyItemDelegate() {};
55 
56 	void iconSetChange();
57 	void redraw(const QVariant&) const override;
58 	QString text(const QVariant&) const override;
59 
60 private:
61 	QPixmap alertIcon;
62 	QPixmap cmykIcon;
63 	QPixmap rgbIcon;
64 	QPixmap labIcon;
65 	QPixmap spotIcon;
66 	QPixmap regIcon;
67 };
68 
ColorFancyItemDelegate()69 ColorFancyItemDelegate::ColorFancyItemDelegate() : ScListBoxPixmap<60, 15>()
70 {
71 	iconSetChange();
72 }
73 
redraw(const QVariant & data) const74 void ColorSmallItemDelegate::redraw(const QVariant& data) const
75 {
76 	QPixmap* pPixmap = ScListBoxPixmap<15, 15>::pmap.data();
77 	pPixmap->fill(Qt::transparent);
78 	if (data.canConvert<ColorPixmapValue>())
79 	{
80 		ColorPixmapValue item(data.value<ColorPixmapValue>());
81 		QColor rgb = ScColorEngine::getDisplayColor(item.m_color, item.m_doc);
82 		pPixmap->fill(rgb);
83 		QPainter painter(pPixmap);
84 		painter.setBrush(Qt::NoBrush);
85 		QPen b(Qt::black, 1);
86 		painter.setPen(b);
87 		painter.drawRect(0, 0, 15, 15);
88 		painter.end();
89 	}
90 }
91 
redraw(const QVariant & data) const92 void ColorWideItemDelegate::redraw(const QVariant& data) const
93 {
94 	QPixmap* pPixmap = ScListBoxPixmap<30, 15>::pmap.data();
95 	pPixmap->fill(Qt::transparent);
96 	if (data.canConvert<ColorPixmapValue>())
97 	{
98 		ColorPixmapValue item(data.value<ColorPixmapValue>());
99 		QColor rgb = ScColorEngine::getDisplayColor(item.m_color, item.m_doc);
100 		pPixmap->fill(rgb);
101 	}
102 }
103 
redraw(const QVariant & data) const104 void ColorFancyItemDelegate::redraw(const QVariant& data) const
105 {
106 	static QPixmap smallPix(15, 15);
107 
108 	QPixmap* pPixmap = ScListBoxPixmap<60, 15>::pmap.data();
109 	pPixmap->fill(Qt::transparent);
110 
111 	if (data.canConvert<ColorPixmapValue>())
112 	{
113 		ColorPixmapValue item(data.value<ColorPixmapValue>());
114 
115 		QColor rgb = ScColorEngine::getDisplayColor(item.m_color, item.m_doc);
116 		smallPix.fill(rgb);
117 		QPainter painter(&smallPix);
118 		painter.setBrush(Qt::NoBrush);
119 		QPen b(Qt::black, 1);
120 		painter.setPen(b);
121 		painter.drawRect(0, 0, 15, 15);
122 		painter.end();
123 
124 		paintAlert(smallPix, *pPixmap, 0, 0);
125 		bool isOutOfGamut = ScColorEngine::isOutOfGamut(item.m_color, item.m_doc);
126 		if (isOutOfGamut)
127 			paintAlert(alertIcon, *pPixmap, 15, 0);
128 		if (item.m_color.getColorModel() == colorModelCMYK)
129 			paintAlert(cmykIcon, *pPixmap, 30, 0);
130 		else if (item.m_color.getColorModel() == colorModelRGB)
131 			paintAlert(rgbIcon, *pPixmap, 30, 0);
132 		else if (item.m_color.getColorModel() == colorModelLab)
133 			paintAlert(labIcon, *pPixmap, 30, 0);
134 		if (item.m_color.isSpotColor())
135 			paintAlert(spotIcon, *pPixmap, 45, 0);
136 		if (item.m_color.isRegistrationColor())
137 			paintAlert(regIcon, *pPixmap, 46, 0);
138 	}
139 }
140 
141 
text(const QVariant & data) const142 QString ColorSmallItemDelegate::text(const QVariant& data) const
143 {
144 	if (data.canConvert<ColorPixmapValue>())
145 		return data.value<ColorPixmapValue>().m_name;
146 	return data.toString();
147 }
148 
text(const QVariant & data) const149 QString ColorWideItemDelegate::text(const QVariant& data) const
150 {
151 	if (data.canConvert<ColorPixmapValue>())
152 		return data.value<ColorPixmapValue>().m_name;
153 	return data.toString();
154 }
155 
text(const QVariant & data) const156 QString ColorFancyItemDelegate::text(const QVariant& data) const
157 {
158 	if (data.canConvert<ColorPixmapValue>())
159 		return data.value<ColorPixmapValue>().m_name;
160 	return data.toString();
161 }
162 
iconSetChange()163 void ColorFancyItemDelegate::iconSetChange()
164 {
165 	IconManager& iconManager = IconManager::instance();
166 
167 	alertIcon = iconManager.loadPixmap("alert.png", true);
168 	cmykIcon = iconManager.loadPixmap("cmyk.png", true);
169 	rgbIcon = iconManager.loadPixmap("rgb.png", true);
170 	labIcon = iconManager.loadPixmap("lab.png", true);
171 	spotIcon = iconManager.loadPixmap("spot.png", true);
172 	regIcon = iconManager.loadPixmap("register.png", true);
173 }
174 
175 int ColorListBox::initialized;
176 int ColorListBox::sortRule;
177 
ColorListBox(QWidget * parent)178 ColorListBox::ColorListBox(QWidget * parent)
179 	: QListView(parent)
180 {
181 	cList = nullptr;
182 	if (initialized != 12345)
183 		sortRule = 0;
184 	initialized = 12345;
185 	QListView::setModel(new ColorListModel(this));
186 	setPixmapType(ColorListBox::widePixmap);
187 
188 	connect(this, SIGNAL(clicked(QModelIndex)),       this, SLOT(emitItemClicked(QModelIndex)));
189 	connect(this, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(emitItemDoubleClicked(QModelIndex)));
190 	connect(this->selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)),
191             this, SLOT(emitCurrentChanged(QModelIndex, QModelIndex)));
192 	connect(this->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
193             this, SIGNAL(itemSelectionChanged()));
194 	connect(this, SIGNAL(contextMenuRequested()), this, SLOT(slotRightClick()));
195 }
196 
ColorListBox(ColorListBox::PixmapType type,QWidget * parent)197 ColorListBox::ColorListBox(ColorListBox::PixmapType type, QWidget * parent)
198 	: QListView(parent)
199 {
200 	cList = nullptr;
201 	if (initialized != 12345)
202 		sortRule = 0;
203 	initialized = 12345;
204 	QListView::setModel(new ColorListModel(this));
205 	setPixmapType(type);
206 
207 	connect(ScQApp, SIGNAL(iconSetChanged()), this, SLOT(iconSetChange()));
208 
209 	connect(this, SIGNAL(clicked(QModelIndex)),       this, SLOT(emitItemClicked(QModelIndex)));
210 	connect(this, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(emitItemDoubleClicked(QModelIndex)));
211 	connect(this->selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)),
212             this, SLOT(emitCurrentChanged(QModelIndex, QModelIndex)));
213 	connect(this->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
214             this, SIGNAL(itemSelectionChanged()));
215 	connect(this, SIGNAL(contextMenuRequested()), this, SLOT(slotRightClick()));
216 }
217 
~ColorListBox()218 ColorListBox::~ColorListBox()
219 {
220 	if (itemDelegate())
221 		delete itemDelegate();
222 	clear();
223 }
224 
clear()225 void ColorListBox::clear()
226 {
227 	QAbstractItemModel* itemModel = model();
228 	itemModel->removeRows(0, itemModel->rowCount());
229 }
230 
count() const231 int ColorListBox::count() const
232 {
233 	return this->model()->rowCount();
234 }
235 
changeEvent(QEvent * e)236 void ColorListBox::changeEvent(QEvent *e)
237 {
238 	if (e->type() == QEvent::LanguageChange)
239 	{
240 		languageChange();
241 		return;
242 	}
243 	QListView::changeEvent(e);
244 }
245 
emitCurrentChanged(const QModelIndex & current,const QModelIndex & previous)246 void ColorListBox::emitCurrentChanged(const QModelIndex &current, const QModelIndex &previous)
247 {
248 	QPersistentModelIndex persistentCurrent = current;
249 
250 	QString text = model()->data(current, Qt::DisplayRole).toString();
251 	emit currentTextChanged(text);
252 	emit currentRowChanged(persistentCurrent.row());
253 }
254 
emitItemClicked(const QModelIndex & current)255 void ColorListBox::emitItemClicked(const QModelIndex &current)
256 {
257 	QPersistentModelIndex persistentCurrent = current;
258 	emit itemClicked(persistentCurrent.row());
259 }
260 
emitItemDoubleClicked(const QModelIndex & current)261 void ColorListBox::emitItemDoubleClicked(const QModelIndex &current)
262 {
263 	QPersistentModelIndex persistentCurrent = current;
264 	emit itemDoubleClicked(persistentCurrent.row());
265 }
266 
iconSetChange()267 void ColorListBox::iconSetChange()
268 {
269 	if (m_type == ColorListBox::fancyPixmap)
270 	{
271 		QAbstractItemDelegate* curDelegate = itemDelegate();
272 		ColorFancyItemDelegate* colorDelegate = dynamic_cast<ColorFancyItemDelegate*>(curDelegate);
273 		if (colorDelegate)
274 		{
275 			colorDelegate->iconSetChange();
276 			this->update();
277 		}
278 	}
279 }
280 
languageChange()281 void ColorListBox::languageChange()
282 {
283 	// Not needed anymore normally: on language change a paintEvent is sent to widget
284 	// and model will return the new translated string for None color
285 	/*if (this->count() > 0)
286 	{
287 		QModelIndexList result;
288 		QModelIndex start = model()->index(0, 0, this->rootIndex());
289 		result =  model()->match(start, Qt::UserRole, CommonStrings::None, 1, Qt::MatchExactly | Qt::MatchCaseSensitive);
290 		if (result.isEmpty())
291 			return;
292 		int index = result.first().row();
293 		QListWidgetItem* item = this->item(index);
294 		item->setText(CommonStrings::tr_NoneColor);
295 	}*/
296 }
297 
currentColor() const298 QString ColorListBox::currentColor() const
299 {
300 	if (currentRow() >= 0)
301 	{
302 		QAbstractItemModel* itemModel = model();
303 		return itemModel->data(currentIndex(), Qt::DisplayRole).toString();
304 	}
305 	return CommonStrings::tr_NoneColor;
306 }
307 
currentRow() const308 int ColorListBox::currentRow() const
309 {
310 	return currentIndex().row();
311 }
312 
data(int row,int role) const313 QVariant ColorListBox::data(int row, int role) const
314 {
315 	QModelIndex index = model()->index(row, 0);
316 	return model()->data(index, role);
317 }
318 
findColors(const QString & name,Qt::MatchFlags flags) const319 QStringList ColorListBox::findColors(const QString &name, Qt::MatchFlags flags) const
320 {
321 	QStringList foundColors;
322 	QAbstractItemModel* currentModel = model();
323 
324 	QModelIndex firstIndex = currentModel->index(0, 0, QModelIndex());
325 	QModelIndexList indexes = currentModel->match(firstIndex, Qt::DisplayRole, name, -1, flags);
326 	for (int i = 0; i < indexes.count(); ++i)
327 	{
328 		QModelIndex modelIndex = indexes.at(i);
329 		QVariant modelData = currentModel->data(modelIndex, Qt::DisplayRole);
330 		foundColors.append(modelData.toString());
331 	}
332 
333 	return foundColors;
334 }
335 
hasSelection() const336 bool ColorListBox::hasSelection() const
337 {
338 	return this->selectionModel()->hasSelection();
339 }
340 
insertItem(int row,const ScColor & color,const QString & colorName)341 void ColorListBox::insertItem(int row, const ScColor& color, const QString& colorName)
342 {
343 	ColorListModel* colorListModel = qobject_cast<ColorListModel*>(model());
344 	if (!colorListModel)
345 		return;
346 
347 	ScribusDoc* doc = nullptr;
348 	if (cList)
349 		doc = cList->document();
350 
351 	ColorPixmapValue value(color, doc, colorName);
352 	colorListModel->insert(row, value);
353 }
354 
isNoneColorShown() const355 bool ColorListBox::isNoneColorShown() const
356 {
357 	ColorListModel* colorListModel = qobject_cast<ColorListModel*>(model());
358 	if (colorListModel)
359 		return colorListModel->isNoneColorShown();
360 	return false;
361 }
362 
removeItem(int i)363 void ColorListBox::removeItem(int i)
364 {
365 	// None color item cannot be removed
366 	if (isNoneColorShown() && (i == 0))
367 		return;
368 
369 	model()->removeRow(i);
370 }
371 
row(const QString & colorName)372 int ColorListBox::row(const QString& colorName)
373 {
374 	QAbstractItemModel* currentModel = model();
375 
376 	QModelIndex firstIndex = currentModel->index(0, 0, QModelIndex());
377 	QModelIndexList indexes = currentModel->match(firstIndex, Qt::DisplayRole, colorName, -1, Qt::MatchExactly);
378 	if (indexes.count() > 0)
379 	{
380 		const QModelIndex& first = indexes.at(0);
381 		return first.row();
382 	}
383 	return -1;
384 }
385 
setCurrentColor(QString colorName)386 void ColorListBox::setCurrentColor(QString colorName)
387 {
388 	if (colorName == CommonStrings::None)
389 		colorName = CommonStrings::tr_NoneColor;
390 
391 	QModelIndex firstIndex = model()->index(0, 0, QModelIndex());
392 	QModelIndexList indexes = this->model()->match(firstIndex, Qt::DisplayRole, colorName, -1, Qt::MatchExactly);
393 	if (indexes.count() > 0)
394 		this->selectionModel()->setCurrentIndex(indexes[0], QItemSelectionModel::ClearAndSelect);
395 }
396 
setColors(ColorList & list,bool insertNone)397 void ColorListBox::setColors(ColorList& list, bool insertNone)
398 {
399 	ColorList::Iterator it;
400 
401 	ColorListModel* colorModel = qobject_cast<ColorListModel*>(this->model());
402 	if (!colorModel)
403 		return;
404 
405 	cList = &list;
406 
407 	colorModel->setColorList(list, insertNone);
408 }
409 
setCurrentRow(int row)410 void ColorListBox::setCurrentRow(int row)
411 {
412 	QModelIndex index = this->model()->index(row, 0);
413 	selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect);
414 }
415 
setPixmapType(ColorListBox::PixmapType type)416 void ColorListBox::setPixmapType(ColorListBox::PixmapType type)
417 {
418 	if (type == ColorListBox::fancyPixmap)
419 	{
420 		QAbstractItemDelegate* oldDelegate = itemDelegate();
421 		ColorFancyItemDelegate* colorDelegate = dynamic_cast<ColorFancyItemDelegate*>(oldDelegate);
422 		if (!colorDelegate)
423 		{
424 			setItemDelegate(new ColorFancyItemDelegate());
425 			delete oldDelegate;
426 			m_type = type;
427 		}
428 	}
429 	else if (type == ColorListBox::widePixmap)
430 	{
431 		QAbstractItemDelegate* oldDelegate = itemDelegate();
432 		ColorWideItemDelegate* colorDelegate = dynamic_cast<ColorWideItemDelegate*>(oldDelegate);
433 		if (!colorDelegate)
434 		{
435 			setItemDelegate(new ColorWideItemDelegate());
436 			delete oldDelegate;
437 			m_type = type;
438 		}
439 	}
440 	else if (type == ColorListBox::smallPixmap)
441 	{
442 		QAbstractItemDelegate* oldDelegate = itemDelegate();
443 		ColorSmallItemDelegate* colorDelegate = dynamic_cast<ColorSmallItemDelegate*>(oldDelegate);
444 		if (!colorDelegate)
445 		{
446 			setItemDelegate(new ColorSmallItemDelegate());
447 			delete oldDelegate;
448 			m_type = type;
449 		}
450 	}
451 }
452 
setShowNoneColor(bool showNone)453 void ColorListBox::setShowNoneColor(bool showNone)
454 {
455 	ColorListModel* colorListModel = qobject_cast<ColorListModel*>(model());
456 	if (colorListModel)
457 		colorListModel->setShowNoneColor(showNone);
458 }
459 
slotRightClick()460 void ColorListBox::slotRightClick()
461 {
462 	QSignalBlocker sigBlocker(this);
463 	QString currentSel = currentColor();
464 	if (currentSel.isEmpty())
465 		return;
466 
467 	QMenu *pmen = new QMenu();
468 	pmen->addAction( tr("Sort by Name"));
469 	pmen->addAction( tr("Sort by Color"));
470 	pmen->addAction( tr("Sort by Type"));
471 	sortRule = pmen->actions().indexOf(pmen->exec(QCursor::pos()));
472 	delete pmen;
473 
474 	ColorListModel* colorListModel = qobject_cast<ColorListModel*>(model());
475 	if (!colorListModel)
476 		return;
477 
478 	if (sortRule == 0)
479 		colorListModel->setSortRule(ColorListModel::SortByName);
480 	else if (sortRule == 1)
481 		colorListModel->setSortRule(ColorListModel::SortByValues);
482 	else if (sortRule == 2)
483 		colorListModel->setSortRule(ColorListModel::SortByType);
484 
485 	if (!currentSel.isEmpty())
486 		setCurrentColor(currentSel);
487 }
488 
text(int row)489 QString ColorListBox::text(int row)
490 {
491 	QVariant varText = data(row, Qt::DisplayRole);
492 	return varText.toString();
493 }
494 
updateBox(ColorList & list)495 void ColorListBox::updateBox(ColorList& list)
496 {
497 	bool showNoneColor = false;
498 
499 	clear();
500 	reset();
501 
502 	ColorListModel* colorModel = qobject_cast<ColorListModel*>(this->model());
503 	if (colorModel)
504 		showNoneColor = colorModel->isNoneColorShown();
505 	setColors(list, showNoneColor);
506 }
507 
viewportEvent(QEvent * event)508 bool ColorListBox::viewportEvent(QEvent *event)
509 {
510 	if (event != nullptr)
511 	{
512 		if (event->type() == QEvent::MouseButtonPress)
513 		{
514 			QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
515 			if (mouseEvent->button() == Qt::RightButton)
516 				return true;
517 		}
518 		else if (event->type() == QEvent::MouseButtonRelease)
519 		{
520 			QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
521 			if (mouseEvent->button() == Qt::RightButton)
522 			{
523 				emit contextMenuRequested();
524 				return true;
525 			}
526 		}
527 	}
528 	return QListView::viewportEvent(event);
529 }
530