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 "extimageprops.h"
8 #include <QHBoxLayout>
9 #include <QVBoxLayout>
10 #include <QListWidget>
11 #include <QListWidgetItem>
12 #include <QPixmap>
13 #include <QTabWidget>
14 #include <QLabel>
15 #include <QCheckBox>
16 #include <QComboBox>
17 #include <QPushButton>
18 #include <QToolTip>
19 #include <QPainter>
20 #include <QWidget>
21 #include <QHeaderView>
22 #include <QTableWidget>
23 #include <QTableWidgetItem>
24 #include <QTimer>
25
26 #include "commonstrings.h"
27 #include "pageitem.h"
28 #include "scpainter.h"
29 #include "scribusdoc.h"
30 #include "scribusview.h"
31 #include "iconmanager.h"
32 #include "util_math.h"
33 #include "util.h"
34 #include "ui/scrspinbox.h"
35
ExtImageProps(QWidget * parent,ImageInfoRecord * info,PageItem * item,ScribusView * view)36 ExtImageProps::ExtImageProps( QWidget* parent, ImageInfoRecord *info, PageItem *item, ScribusView *view )
37 : QDialog( parent )
38 {
39 setModal(true);
40 setWindowTitle( tr( "Extended Image Properties" ) );
41 setWindowIcon(IconManager::instance().loadIcon("AppIcon.png"));
42 ExtImagePropsLayout = new QVBoxLayout( this );
43 ExtImagePropsLayout->setContentsMargins(9, 9, 9, 9);
44 ExtImagePropsLayout->setSpacing(6);
45 m_view = view;
46 m_timer = nullptr;
47 if (info->layerInfo.count() != 0)
48 {
49 m_timer = new QTimer(this);
50 m_timer->setSingleShot(true);
51 m_timer->setInterval(350);
52 }
53 m_item = item;
54 currentLayer = 0;
55 originalInfo = *info;
56 originalImageClip = item->imageClip.copy();
57 blendModes.clear();
58 blendModes.insert("norm", tr("Normal"));
59 blendModes.insert("dark", tr("Darken"));
60 blendModes.insert("lite", tr("Lighten"));
61 blendModes.insert("hue ", tr("Hue"));
62 blendModes.insert("sat ", tr("Saturation"));
63 blendModes.insert("colr", tr("Color"));
64 blendModes.insert("lum ", tr("Luminosity"));
65 blendModes.insert("mul ", tr("Multiply"));
66 blendModes.insert("scrn", tr("Screen"));
67 blendModes.insert("diss", tr("Dissolve"));
68 blendModes.insert("over", tr("Overlay"));
69 blendModes.insert("hLit", tr("Hard Light"));
70 blendModes.insert("sLit", tr("Soft Light"));
71 blendModes.insert("diff", tr("Difference"));
72 blendModes.insert("smud", tr("Exclusion"));
73 blendModes.insert("div ", tr("Color Dodge"));
74 blendModes.insert("idiv", tr("Color Burn"));
75 blendModes.insert("plus", tr("Plus"));
76 blendModes.insert("dsti", tr("Destination In"));
77 blendModes.insert("dsto", tr("Destination Out"));
78 blendModesRev.clear();
79 blendModesRev.insert( tr("Normal"), "norm");
80 blendModesRev.insert( tr("Darken"), "dark");
81 blendModesRev.insert( tr("Lighten"), "lite");
82 blendModesRev.insert( tr("Hue"), "hue ");
83 blendModesRev.insert( tr("Saturation"), "sat ");
84 blendModesRev.insert( tr("Color"), "colr");
85 blendModesRev.insert( tr("Luminosity"), "lum ");
86 blendModesRev.insert( tr("Multiply"), "mul ");
87 blendModesRev.insert( tr("Screen"), "scrn");
88 blendModesRev.insert( tr("Dissolve"), "diss");
89 blendModesRev.insert( tr("Overlay"), "over");
90 blendModesRev.insert( tr("Hard Light"), "hLit");
91 blendModesRev.insert( tr("Soft Light"), "sLit");
92 blendModesRev.insert( tr("Difference"), "diff");
93 blendModesRev.insert( tr("Exclusion"), "smud");
94 blendModesRev.insert( tr("Color Dodge"), "div ");
95 blendModesRev.insert( tr("Color Burn"), "idiv");
96 blendModesRev.insert( tr("Plus"), "plus");
97 blendModesRev.insert( tr("Destination In"), "dsti");
98 blendModesRev.insert( tr("Destination Out"), "dsto");
99 propsTab = new QTabWidget( this );
100 QPalette palette;
101 palette.setColor(backgroundRole(), Qt::white);
102 if (info->layerInfo.count() != 0)
103 {
104 tab = new QWidget( propsTab );
105 tabLayout = new QVBoxLayout( tab );
106 tabLayout->setContentsMargins(9, 9, 9, 9);
107 tabLayout->setSpacing(6);
108 layout1 = new QHBoxLayout;
109 layout1->setContentsMargins(0, 0, 0, 0);
110 layout1->setSpacing(6);
111 textLabel1 = new QLabel( tab );
112 textLabel1->setText( tr( "Blend Mode:" ) );
113 layout1->addWidget( textLabel1 );
114 blendMode = new QComboBox( tab );
115 blendMode->clear();
116 blendMode->addItem( tr("Normal"));
117 blendMode->addItem( tr("Darken"));
118 blendMode->addItem( tr("Lighten"));
119 blendMode->addItem( tr("Hue"));
120 blendMode->addItem( tr("Saturation"));
121 blendMode->addItem( tr("Color"));
122 blendMode->addItem( tr("Luminosity"));
123 blendMode->addItem( tr("Multiply"));
124 blendMode->addItem( tr("Screen"));
125 blendMode->addItem( tr("Dissolve"));
126 blendMode->addItem( tr("Overlay"));
127 blendMode->addItem( tr("Hard Light"));
128 blendMode->addItem( tr("Soft Light"));
129 blendMode->addItem( tr("Difference"));
130 blendMode->addItem( tr("Exclusion"));
131 blendMode->addItem( tr("Color Dodge"));
132 blendMode->addItem( tr("Color Burn"));
133 blendMode->addItem( tr("Plus"));
134 blendMode->addItem( tr("Destination In"));
135 blendMode->addItem( tr("Destination Out"));
136 layout1->addWidget( blendMode );
137 textLabel2 = new QLabel( tab );
138 textLabel2->setText( tr( "Opacity:" ) );
139 layout1->addWidget( textLabel2 );
140 opacitySpinBox = new ScrSpinBox( tab );
141 opacitySpinBox->setMinimum(0);
142 opacitySpinBox->setDecimals(0);
143 opacitySpinBox->setMaximum(100);
144 opacitySpinBox->setSingleStep(10);
145 opacitySpinBox->setSuffix( tr(" %"));
146 layout1->addWidget( opacitySpinBox );
147 tabLayout->addLayout( layout1 );
148 layerTable = new QTableWidget(info->layerInfo.count(), 3, tab );
149 layerTable->setHorizontalHeaderItem(0, new QTableWidgetItem(IconManager::instance().loadIcon("16/show-object.png"), ""));
150 layerTable->setHorizontalHeaderItem(1, new QTableWidgetItem(""));
151 layerTable->setHorizontalHeaderItem(2, new QTableWidgetItem( tr("Name")));
152 QHeaderView* headerH = layerTable->horizontalHeader();
153 headerH->setStretchLastSection(true);
154 headerH->setSectionsClickable(false );
155 headerH->setSectionsMovable( false );
156 if (info->layerInfo.count() == 1)
157 {
158 layerTable->setColumnWidth(1, 40);
159 layerTable->setColumnWidth(0, 24);
160 }
161 layerTable->setSortingEnabled(false);
162 layerTable->setSelectionBehavior(QTableWidget::SelectRows);
163 QHeaderView *Header = layerTable->verticalHeader();
164 Header->setSectionsMovable( false );
165 Header->setSectionResizeMode(QHeaderView::Fixed);
166 Header->hide();
167 FlagsSicht.clear();
168 int col2Width = 0;
169 int col1Width = 0;
170 if ((info->isRequest) && (info->RequestProps.contains(0)))
171 {
172 opacitySpinBox->setValue(qRound(info->RequestProps[0].opacity / 255.0 * 100));
173 setCurrentComboItem(blendMode, blendModes[info->RequestProps[0].blend]);
174 }
175 else
176 {
177 opacitySpinBox->setValue(qRound(info->layerInfo[0].opacity / 255.0 * 100));
178 setCurrentComboItem(blendMode, blendModes[info->layerInfo[0].blend]);
179 }
180 opacitySpinBox->setEnabled(true);
181 blendMode->setEnabled(true);
182
183 QList<PSDLayer>::iterator it2;
184 uint counter = 0;
185 for (it2 = info->layerInfo.begin(); it2 != info->layerInfo.end(); ++it2)
186 {
187 QCheckBox *cp = new QCheckBox(it2->layerName, this);
188 cp->setPalette(palette);
189 QPixmap pm;
190 pm=QPixmap::fromImage(it2->thumb);
191 col1Width = qMax(col1Width, pm.width());
192 cp->setIcon(pm);
193 FlagsSicht.append(cp);
194 connect(cp, SIGNAL(clicked()), this, SLOT(changedLayer()));
195 layerTable->setCellWidget(info->layerInfo.count()-counter-1, 0, cp);
196 if ((info->isRequest) && (info->RequestProps.contains(counter)))
197 cp->setChecked(info->RequestProps[counter].visible);
198 else
199 cp->setChecked(!(it2->flags & 2));
200 if (!it2->thumb_mask.isNull())
201 {
202 QCheckBox *cp2 = new QCheckBox(it2->layerName, this);
203 cp2->setPalette(palette);
204 QPixmap pm2;
205 pm2=QPixmap::fromImage(it2->thumb_mask);
206 col2Width = qMax(col2Width, pm2.width());
207 cp2->setIcon(pm2);
208 connect(cp2, SIGNAL(clicked()), this, SLOT(changedLayer()));
209 layerTable->setCellWidget(info->layerInfo.count()-counter-1, 1, cp2);
210 if ((info->isRequest) && (info->RequestProps.contains(counter)))
211 cp2->setChecked(info->RequestProps[counter].useMask);
212 else
213 cp2->setChecked(true);
214 FlagsMask.append(cp2);
215 }
216 else
217 FlagsMask.append(0);
218 QTableWidgetItem *tW = new QTableWidgetItem(it2->layerName);
219 tW->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
220 layerTable->setItem(info->layerInfo.count()-counter-1, 2, tW);
221 layerTable->setRowHeight(info->layerInfo.count()-counter-1, 40);
222 counter++;
223 }
224 tabLayout->addWidget( layerTable );
225 layerTable->setColumnWidth(1, 24 + col2Width);
226 layerTable->setColumnWidth(0, 24 + col1Width);
227 blendMode->setCurrentIndex(0);
228 // headerH->setResizeMode(QHeaderView::Fixed);
229 propsTab->addTab( tab, tr( "Layers" ) );
230 }
231 tab_2 = new QWidget( propsTab );
232 tabLayout_2 = new QVBoxLayout( tab_2 );
233 tabLayout_2->setContentsMargins(9, 9, 9, 9);
234 tabLayout_2->setSpacing(6);
235 pathList = new QListWidget( tab_2 );
236 pathList->clear();
237 pathList->setIconSize(QSize(40, 40));
238 QMap<QString, FPointArray>::Iterator it;
239 if (info->PDSpathData.count() != 0)
240 {
241 for (it = info->PDSpathData.begin(); it != info->PDSpathData.end(); ++it)
242 {
243 QImage pixm(40, 40, QImage::Format_ARGB32_Premultiplied);
244 ScPainter *p = new ScPainter(&pixm, 40, 40);
245 p->clear();
246 p->translate(3.0, 3.0);
247 if (it.key() == info->clipPath)
248 {
249 pixm.fill(Qt::green);
250 p->clear(Qt::green);
251 }
252 else
253 pixm.fill(Qt::white);
254 FPointArray Path;
255 Path.resize(0);
256 Path = info->PDSpathData[it.key()].copy();
257 FPoint min = getMinClipF(&Path);
258 Path.translate(-min.x(), -min.y());
259 FPoint max = Path.widthHeight();
260 QTransform mm;
261 mm.scale(34.0 / qMax(max.x(), max.y()), 34.0 / qMax(max.x(), max.y()));
262 Path.map(mm);
263 p->setupPolygon(&Path);
264 p->setPen(Qt::black, 1, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
265 p->setBrush(Qt::white);
266 p->setFillMode(ScPainter::None);
267 p->setStrokeMode(ScPainter::Solid);
268 p->strokePath();
269 p->end();
270 delete p;
271 QPixmap pm;
272 pm=QPixmap::fromImage(pixm);
273 new QListWidgetItem(QIcon(pm), it.key(), pathList);
274 if (it.key() == info->usedPath)
275 {
276 pathList->setCurrentRow(pathList->count()-1);
277 pathList->currentItem()->setSelected(true);
278 }
279 }
280 }
281 tabLayout_2->addWidget( pathList );
282 resetPath = new QPushButton( tr("Don't use any Path"), tab_2);
283 tabLayout_2->addWidget( resetPath );
284 propsTab->addTab( tab_2, tr( "Paths" ) );
285 ExtImagePropsLayout->addWidget( propsTab );
286
287 layoutBottom = new QHBoxLayout;
288 layoutBottom->setContentsMargins(0, 0, 0, 0);
289 layoutBottom->setSpacing(6);
290 livePreview = new QCheckBox( this );
291 livePreview->setText( tr( "Live Preview" ) );
292 livePreview->setChecked(true);
293 doPreview = true;
294 layoutBottom->addWidget( livePreview );
295 QSpacerItem* spacer = new QSpacerItem( 2, 2, QSizePolicy::Expanding, QSizePolicy::Minimum );
296 layoutBottom->addItem( spacer );
297 okButton = new QPushButton( CommonStrings::tr_OK, this );
298 layoutBottom->addWidget( okButton );
299 cancelButton = new QPushButton( CommonStrings::tr_Cancel, this );
300 cancelButton->setDefault( true );
301 layoutBottom->addWidget( cancelButton );
302 ExtImagePropsLayout->addLayout( layoutBottom );
303 resize(330, 320);
304
305 connect(pathList, SIGNAL( itemClicked(QListWidgetItem*) ), this, SLOT( selPath(QListWidgetItem*) ) );
306 connect(resetPath, SIGNAL(clicked()), this, SLOT(noPath()));
307 connect(livePreview, SIGNAL(clicked()), this, SLOT(changePreview()));
308 connect(okButton, SIGNAL(clicked()), this, SLOT(leaveOK()));
309 connect(cancelButton, SIGNAL(clicked()), this, SLOT(leaveCancel()));
310 if (info->layerInfo.count() != 0)
311 {
312 layerTable->selectionModel()->clearSelection();
313 opacitySpinBox->setEnabled(false);
314 blendMode->setEnabled(false);
315 connect(m_timer, SIGNAL(timeout()), this, SLOT(changedLayer()));
316 connect(layerTable, SIGNAL(itemSelectionChanged()), this, SLOT(selLayer()));
317 connect(opacitySpinBox, SIGNAL(valueChanged(double)), this, SLOT(delayedLayerChange()));
318 connect(blendMode, SIGNAL(activated(int)), this, SLOT(changedLayer()));
319 }
320 }
321
leaveOK()322 void ExtImageProps::leaveOK()
323 {
324 doPreview = false;
325 if (originalInfo.layerInfo.count() != 0)
326 changedLayer();
327 m_view->m_doc->loadPict(m_item->Pfile, m_item, true);
328 if (pathList->count() != 0)
329 {
330 QList<QListWidgetItem *>sel = pathList->selectedItems();
331 if (sel.count() != 0)
332 {
333 m_item->imageClip = m_item->pixm.imgInfo.PDSpathData[sel[0]->text()].copy();
334 m_item->pixm.imgInfo.usedPath = sel[0]->text();
335 QTransform cl;
336 cl.translate(m_item->imageXOffset()*m_item->imageXScale(), m_item->imageYOffset()*m_item->imageYScale());
337 cl.rotate(m_item->imageRotation());
338 cl.scale(m_item->imageXScale(), m_item->imageYScale());
339 m_item->imageClip.map(cl);
340 }
341 else
342 {
343 m_item->imageClip.resize(0);
344 m_item->pixm.imgInfo.usedPath = "";
345 }
346 }
347 m_item->update();
348 accept();
349 }
350
leaveCancel()351 void ExtImageProps::leaveCancel()
352 {
353 m_item->pixm.imgInfo = originalInfo;
354 m_view->m_doc->loadPict(m_item->Pfile, m_item, true);
355 m_item->imageClip = originalImageClip.copy();
356 m_item->update();
357 reject();
358 }
359
changePreview()360 void ExtImageProps::changePreview()
361 {
362 doPreview = livePreview->isChecked();
363 if (doPreview)
364 {
365 if (originalInfo.layerInfo.count() != 0)
366 changedLayer();
367 m_view->m_doc->loadPict(m_item->Pfile, m_item, true);
368 if (pathList->count() != 0)
369 {
370 QList<QListWidgetItem *>sel = pathList->selectedItems();
371 if (sel.count() != 0)
372 {
373 m_item->imageClip = m_item->pixm.imgInfo.PDSpathData[sel[0]->text()].copy();
374 m_item->pixm.imgInfo.usedPath = sel[0]->text();
375 QTransform cl;
376 cl.translate(m_item->imageXOffset()*m_item->imageXScale(), m_item->imageYOffset()*m_item->imageYScale());
377 cl.rotate(m_item->imageRotation());
378 cl.scale(m_item->imageXScale(), m_item->imageYScale());
379 m_item->imageClip.map(cl);
380 }
381 else
382 {
383 m_item->imageClip.resize(0);
384 m_item->pixm.imgInfo.usedPath = "";
385 }
386 }
387 m_item->update();
388 }
389 else
390 {
391 m_item->pixm.imgInfo = originalInfo;
392 m_view->m_doc->loadPict(m_item->Pfile, m_item, true);
393 m_item->imageClip = originalImageClip.copy();
394 m_item->update();
395 }
396 }
397
changedLayer()398 void ExtImageProps::changedLayer()
399 {
400 updateLayerInfo();
401 if (doPreview)
402 {
403 m_view->m_doc->loadPict(m_item->Pfile, m_item, true);
404 m_item->update();
405 }
406 }
407
delayedLayerChange()408 void ExtImageProps::delayedLayerChange()
409 {
410 if (m_timer->isActive())
411 m_timer->stop();
412 updateLayerInfo();
413 m_timer->start();
414 }
415
selLayer()416 void ExtImageProps::selLayer()
417 {
418 QModelIndexList selectedRows = layerTable->selectionModel()->selectedRows();
419 if (selectedRows.count() <= 0)
420 {
421 currentLayer = -1;
422 opacitySpinBox->setEnabled(false);
423 blendMode->setEnabled(false);
424 return;
425 }
426
427 int selectedRow = selectedRows.at(0).row();
428 currentLayer = layerTable->rowCount() - selectedRow - 1;
429
430 disconnect(opacitySpinBox, SIGNAL(valueChanged(double)), this, SLOT(delayedLayerChange()));
431 disconnect(blendMode, SIGNAL(activated(int)), this, SLOT(changedLayer()));
432 if ((m_item->pixm.imgInfo.isRequest) && (m_item->pixm.imgInfo.RequestProps.contains(currentLayer)))
433 {
434 opacitySpinBox->setValue(qRound(m_item->pixm.imgInfo.RequestProps[currentLayer].opacity / 255.0 * 100));
435 setCurrentComboItem(blendMode, blendModes[m_item->pixm.imgInfo.RequestProps[currentLayer].blend]);
436 }
437 else
438 {
439 opacitySpinBox->setValue(qRound(m_item->pixm.imgInfo.layerInfo[currentLayer].opacity / 255.0 * 100));
440 setCurrentComboItem(blendMode, blendModes[m_item->pixm.imgInfo.layerInfo[currentLayer].blend]);
441 }
442 opacitySpinBox->setEnabled(true);
443 blendMode->setEnabled(true);
444 connect(opacitySpinBox, SIGNAL(valueChanged(double)), this, SLOT(delayedLayerChange()));
445 connect(blendMode, SIGNAL(activated(int)), this, SLOT(changedLayer()));
446 }
447
updateLayerInfo()448 void ExtImageProps::updateLayerInfo()
449 {
450 struct ImageLoadRequest loadingInfo;
451 bool isRequest = m_item->pixm.imgInfo.isRequest;
452 for (int r = 0; r < layerTable->rowCount(); ++r)
453 {
454 int layerIndex = layerTable->rowCount() - r - 1;
455 if (currentLayer == layerIndex)
456 {
457 loadingInfo.blend = blendModesRev[blendMode->currentText()];
458 loadingInfo.opacity = qRound(opacitySpinBox->value() / 100.0 * 255);
459 }
460 else if ((isRequest) && (m_item->pixm.imgInfo.RequestProps.contains(layerIndex)))
461 {
462 loadingInfo.blend = m_item->pixm.imgInfo.RequestProps[layerIndex].blend;
463 loadingInfo.opacity = m_item->pixm.imgInfo.RequestProps[layerIndex].opacity;
464 }
465 else
466 {
467 loadingInfo.blend = m_item->pixm.imgInfo.layerInfo[layerIndex].blend;
468 loadingInfo.opacity = m_item->pixm.imgInfo.layerInfo[layerIndex].opacity;
469 }
470 loadingInfo.visible = FlagsSicht.at(layerIndex)->isChecked();
471 if (FlagsMask.at(layerIndex))
472 loadingInfo.useMask = FlagsMask.at(layerIndex)->isChecked();
473 else
474 loadingInfo.useMask = true;
475 m_item->pixm.imgInfo.RequestProps.insert(layerIndex, loadingInfo);
476 }
477 m_item->pixm.imgInfo.isRequest = true;
478 }
479
noPath()480 void ExtImageProps::noPath()
481 {
482 disconnect(pathList, SIGNAL( itemClicked(QListWidgetItem*) ), this, SLOT( selPath(QListWidgetItem*) ) );
483 pathList->clearSelection();
484 if (doPreview)
485 {
486 m_item->imageClip.resize(0);
487 m_item->pixm.imgInfo.usedPath = "";
488 m_item->update();
489 }
490 connect(pathList, SIGNAL( itemClicked(QListWidgetItem*) ), this, SLOT( selPath(QListWidgetItem*) ) );
491 }
492
selPath(QListWidgetItem * c)493 void ExtImageProps::selPath(QListWidgetItem *c)
494 {
495 if ((c != nullptr) && (doPreview))
496 {
497 m_item->imageClip = m_item->pixm.imgInfo.PDSpathData[c->text()].copy();
498 m_item->pixm.imgInfo.usedPath = c->text();
499 QTransform cl;
500 cl.translate(m_item->imageXOffset()*m_item->imageXScale(), m_item->imageYOffset()*m_item->imageYScale());
501 cl.rotate(m_item->imageRotation());
502 cl.scale(m_item->imageXScale(), m_item->imageYScale());
503 m_item->imageClip.map(cl);
504 m_item->update();
505 }
506 }
507
508