1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25 
26 #include "contextpanewidgetimage.h"
27 
28 #include "ui_contextpanewidgetimage.h"
29 #include "ui_contextpanewidgetborderimage.h"
30 #include <qmljs/qmljspropertyreader.h>
31 #include <QFile>
32 #include <QFileInfo>
33 #include <QDir>
34 #include <QPixmap>
35 #include <QPainter>
36 #include <QGraphicsEffect>
37 #include <QMouseEvent>
38 #include <QScrollArea>
39 #include <QSlider>
40 #include <QStyle>
41 #include <QToolButton>
42 #include <QDebug>
43 
44 namespace QmlEditorWidgets {
45 
eventFilter(QObject * obj,QEvent * event)46 bool LabelFilter::eventFilter(QObject *obj, QEvent *event)
47 {
48     if (event->type() == QEvent::MouseButtonDblClick) {
49         emit doubleClicked();
50         event->accept();
51         return true;
52     }
53     return QObject::eventFilter(obj, event);
54 }
55 
eventFilter(QObject * obj,QEvent * event)56 bool WheelFilter::eventFilter(QObject *obj, QEvent *event)
57 {
58     if (event->type() == QEvent::Wheel) {
59         if (obj
60             && obj->isWidgetType()
61             && obj != m_target) {
62             QApplication::sendEvent(m_target, event);
63             return true;
64         }
65     }
66     return QObject::eventFilter(obj, event);
67 }
68 
ContextPaneWidgetImage(QWidget * parent,bool borderImage)69 ContextPaneWidgetImage::ContextPaneWidgetImage(QWidget *parent, bool borderImage) :
70     QWidget(parent),
71     ui(nullptr), uiBorderImage(nullptr), previewWasVisible(false)
72 {
73     LabelFilter *labelFilter = new LabelFilter(this);
74 
75     m_borderImage = borderImage;
76 
77     if (m_borderImage) {
78         uiBorderImage = new Ui::ContextPaneWidgetBorderImage;
79         uiBorderImage->setupUi(this);
80         m_fileWidget = uiBorderImage->fileWidget;
81         m_sizeLabel = uiBorderImage->sizeLabel;
82         uiBorderImage->label->setToolTip(tr("Double click for preview."));
83         uiBorderImage->label->installEventFilter(labelFilter);
84 
85 
86         connect(uiBorderImage->verticalTileRadioButton, &QRadioButton::toggled,
87                 this, &ContextPaneWidgetImage::onVerticalStretchChanged);
88         connect(uiBorderImage->verticalStretchRadioButton, &QRadioButton::toggled,
89                 this, &ContextPaneWidgetImage::onVerticalStretchChanged);
90         connect(uiBorderImage->verticalTileRadioButtonNoCrop, &QRadioButton::toggled,
91                 this, &ContextPaneWidgetImage::onVerticalStretchChanged);
92 
93         connect(uiBorderImage->horizontalTileRadioButton, &QRadioButton::toggled,
94                 this, &ContextPaneWidgetImage::onHorizontalStretchChanged);
95         connect(uiBorderImage->horizontalStretchRadioButton, &QRadioButton::toggled,
96                 this, &ContextPaneWidgetImage::onHorizontalStretchChanged);
97         connect(uiBorderImage->horizontalTileRadioButtonNoCrop, &QRadioButton::toggled,
98                 this, &ContextPaneWidgetImage::onHorizontalStretchChanged);
99         PreviewLabel *previewLabel = previewDialog()->previewLabel();
100         connect(previewLabel, &PreviewLabel::leftMarginChanged,
101                 this, &ContextPaneWidgetImage::onLeftMarginsChanged);
102         connect(previewLabel, &PreviewLabel::rightMarginChanged,
103                 this, &ContextPaneWidgetImage::onRightMarginsChanged);
104         connect(previewLabel, &PreviewLabel::topMarginChanged,
105                 this, &ContextPaneWidgetImage::onTopMarginsChanged);
106         connect(previewLabel, &PreviewLabel::bottomMarginChanged,
107                 this, &ContextPaneWidgetImage::onBottomMarginsChanged);
108 
109     } else {
110         ui = new Ui::ContextPaneWidgetImage;
111         ui->setupUi(this);
112         ui->label->setToolTip(tr("Double click for preview."));
113         ui->label->installEventFilter(labelFilter);
114         m_fileWidget = ui->fileWidget;
115         m_sizeLabel = ui->sizeLabel;
116 
117         connect(ui->stretchRadioButton, &QRadioButton::toggled,
118                 this, &ContextPaneWidgetImage::onStretchChanged);
119         connect(ui->tileRadioButton, &QRadioButton::toggled,
120                 this, &ContextPaneWidgetImage::onStretchChanged);
121         connect(ui->horizontalStretchRadioButton, &QRadioButton::toggled,
122                 this, &ContextPaneWidgetImage::onStretchChanged);
123         connect(ui->verticalStretchRadioButton, &QRadioButton::toggled,
124                 this, &ContextPaneWidgetImage::onStretchChanged);
125         connect(ui->preserveAspectFitRadioButton, &QRadioButton::toggled,
126                 this, &ContextPaneWidgetImage::onStretchChanged);
127         connect(ui->cropAspectFitRadioButton, &QRadioButton::toggled,
128                 this, &ContextPaneWidgetImage::onStretchChanged);
129     }
130     previewDialog();
131     m_fileWidget->setShowComboBox(true);
132     m_fileWidget->setFilter(QLatin1String("*.png *.gif *.jpg"));
133 
134     connect(m_fileWidget, &FileWidget::fileNameChanged,
135             this, &ContextPaneWidgetImage::onFileNameChanged);
136     connect(labelFilter, &LabelFilter::doubleClicked,
137             this, &ContextPaneWidgetImage::onPixmapDoubleClicked);
138 
139 }
140 
~ContextPaneWidgetImage()141 ContextPaneWidgetImage::~ContextPaneWidgetImage()
142 {
143     if (ui)
144         delete ui;
145     if (uiBorderImage)
146         delete uiBorderImage;
147 }
148 
setProperties(QmlJS::PropertyReader * propertyReader)149 void ContextPaneWidgetImage::setProperties(QmlJS::PropertyReader *propertyReader)
150 {
151     if (m_borderImage) {
152 
153         int leftBorder = 0;
154         int rightBorder = 0;
155         int topBorder = 0;
156         int bottomBorder = 0;
157 
158         if (propertyReader->hasProperty(QLatin1String("border.left")))
159             leftBorder =propertyReader->readProperty(QLatin1String("border.left")).toInt();
160         if (propertyReader->hasProperty(QLatin1String("border.right")))
161             rightBorder =propertyReader->readProperty(QLatin1String("border.right")).toInt();
162         if (propertyReader->hasProperty(QLatin1String("border.top")))
163             topBorder =propertyReader->readProperty(QLatin1String("border.top")).toInt();
164         if (propertyReader->hasProperty(QLatin1String("border.bottom")))
165             bottomBorder =propertyReader->readProperty(QLatin1String("border.bottom")).toInt();
166         previewDialog()->previewLabel()->setMargins(leftBorder, topBorder, rightBorder, bottomBorder);
167 
168         if (propertyReader->hasProperty(QLatin1String("horizontalTileMode"))) {
169             QString fillMode = propertyReader->readProperty(QLatin1String("horizontalTileMode")).toString();
170             if (fillMode.contains(QLatin1String("BorderImage.")))
171                 fillMode.remove(QLatin1String("BorderImage."));
172 
173             uiBorderImage->horizontalStretchRadioButton->setChecked(true);
174 
175             if (fillMode == QLatin1String("Stretch"))
176                 uiBorderImage->horizontalStretchRadioButton->setChecked(true);
177             if (fillMode == QLatin1String("Repeat"))
178                 uiBorderImage->horizontalTileRadioButton->setChecked(true);
179             if (fillMode == QLatin1String("Round"))
180                 uiBorderImage->horizontalTileRadioButtonNoCrop->setChecked(true);
181         } else {
182             //uiBorderImage
183             uiBorderImage->horizontalStretchRadioButton->setChecked(true);
184         }
185         if (propertyReader->hasProperty(QLatin1String("verticalTileMode"))) {
186             QString fillMode = propertyReader->readProperty(QLatin1String("verticalTileMode")).toString();
187             if (fillMode.contains(QLatin1String("BorderImage.")))
188                 fillMode.remove(QLatin1String("BorderImage."));
189 
190             uiBorderImage->verticalStretchRadioButton->setChecked(true);
191 
192             if (fillMode == QLatin1String("Stretch"))
193                 uiBorderImage->verticalStretchRadioButton->setChecked(true);
194             if (fillMode == QLatin1String("Repeat"))
195                 uiBorderImage->verticalTileRadioButton->setChecked(true);
196             if (fillMode == QLatin1String("Round"))
197                 uiBorderImage->verticalTileRadioButtonNoCrop->setChecked(true);
198         } else {
199             //uiBorderImage
200             uiBorderImage->verticalStretchRadioButton->setChecked(true);
201         }
202     } else {
203         if (propertyReader->hasProperty(QLatin1String("fillMode"))) {
204             QString fillMode = propertyReader->readProperty(QLatin1String("fillMode")).toString();
205             if (fillMode.contains(QLatin1String("Image.")))
206                 fillMode.remove(QLatin1String("Image."));
207 
208             ui->stretchRadioButton->setChecked(true);
209 
210             if (fillMode == QLatin1String("Image.Tile") || fillMode == QLatin1String("Tile"))
211                 ui->tileRadioButton->setChecked(true);
212             if (fillMode == QLatin1String("Image.TileVertically") || fillMode == QLatin1String("TileVertically"))
213                 ui->horizontalStretchRadioButton->setChecked(true);
214             if (fillMode == QLatin1String("Image.TileHorizontally") || fillMode == QLatin1String("TileHorizontally"))
215                 ui->verticalStretchRadioButton->setChecked(true);
216             if (fillMode == QLatin1String("Image.PreserveAspectFit") || fillMode == QLatin1String("PreserveAspectFit"))
217                 ui->preserveAspectFitRadioButton->setChecked(true);
218             if (fillMode == QLatin1String("Image.PreserveAspectCrop") || fillMode == QLatin1String("PreserveAspectCrop"))
219                 ui->cropAspectFitRadioButton->setChecked(true);
220         } else {
221             ui->stretchRadioButton->setChecked(true);
222         }
223     }
224     if (propertyReader->hasProperty(QLatin1String("source"))) {
225         QString source = propertyReader->readProperty(QLatin1String("source")).toString();
226         m_fileWidget->setFileName(source);
227         if (QFile::exists(m_path + QLatin1Char('/') + source))
228             setPixmap(m_path + QLatin1Char('/') + source);
229         else
230             setPixmap(source);
231     } else {
232         m_sizeLabel->clear();
233         m_fileWidget->setFileName(QUrl());
234         setPixmap(QString());
235     }
236 }
237 
setPath(const QString & path)238 void ContextPaneWidgetImage::setPath(const QString& path)
239 {
240     m_path = path;
241     m_fileWidget->setPath(QUrl::fromLocalFile(m_path));
242 }
243 
setZoom(int z)244 void PreviewDialog::setZoom(int z)
245 {
246     m_zoom = z;
247     m_label->setZoom(z);
248     switch (m_zoom) {
249     case 1:
250         m_zoomLabel->setText(QLatin1String("100%"));
251         m_slider->setValue(1);
252         break;
253     case 2:
254         m_zoomLabel->setText(QLatin1String("200%"));
255         m_slider->setValue(2);
256         break;
257     case 4:
258         m_zoomLabel->setText(QLatin1String("400%"));
259         m_slider->setValue(3);
260         break;
261     case 6:
262         m_zoomLabel->setText(QLatin1String("600%"));
263         m_slider->setValue(4);
264         break;
265     case 8:
266         m_zoomLabel->setText(QLatin1String("800%"));
267         m_slider->setValue(5);
268         break;
269     case 10:
270         m_zoomLabel->setText(QLatin1String("1000%"));
271         m_slider->setValue(6);
272         break;
273     default: break;
274     }
275     setPixmap(m_pixmap, m_zoom);
276 }
277 
setIsBorderImage(bool b)278 void PreviewDialog::setIsBorderImage(bool b)
279 {
280     m_borderImage = b;
281     m_label->setIsBorderImage(b);
282 }
283 
previewLabel() const284 PreviewLabel *PreviewDialog::previewLabel() const
285 {
286     return m_label;
287 }
288 
onStretchChanged()289 void ContextPaneWidgetImage::onStretchChanged()
290 {
291     QString stretch;
292     if (ui->stretchRadioButton->isChecked())
293         stretch = QLatin1String("Image.Stretch");
294     else if (ui->tileRadioButton->isChecked())
295         stretch = QLatin1String("Image.Tile");
296     else if (ui->horizontalStretchRadioButton->isChecked())
297         stretch = QLatin1String("Image.TileVertically");
298     else if (ui->verticalStretchRadioButton->isChecked())
299         stretch = QLatin1String("Image.TileHorizontally");
300     else if (ui->preserveAspectFitRadioButton->isChecked())
301         stretch = QLatin1String("Image.PreserveAspectFit");
302     else if (ui->cropAspectFitRadioButton->isChecked())
303         stretch = QLatin1String("Image.PreserveAspectCrop");
304 
305     if (stretch == QLatin1String("Image.Stretch"))
306         emit removeProperty(QLatin1String("fillMode"));
307     else
308         emit propertyChanged(QLatin1String("fillMode"), stretch);
309 }
310 
onHorizontalStretchChanged()311 void ContextPaneWidgetImage::onHorizontalStretchChanged()
312 {
313     QString stretch;
314     if (uiBorderImage->horizontalStretchRadioButton->isChecked())
315         stretch = QLatin1String("BorderImage.Stretch");
316     if (uiBorderImage->horizontalTileRadioButton->isChecked())
317         stretch = QLatin1String("BorderImage.Repeat");
318     if (uiBorderImage->horizontalTileRadioButtonNoCrop->isChecked())
319         stretch = QLatin1String("BorderImage.Round");
320 
321     if (stretch == QLatin1String("BorderImage.Stretch"))
322         emit removeProperty(QLatin1String("horizontalTileMode"));
323     else
324         emit propertyChanged(QLatin1String("horizontalTileMode"), stretch);
325 }
326 
onVerticalStretchChanged()327 void ContextPaneWidgetImage::onVerticalStretchChanged()
328 {
329     QString stretch;
330     if (uiBorderImage->verticalStretchRadioButton->isChecked())
331         stretch = QLatin1String("BorderImage.Stretch");
332     if (uiBorderImage->verticalTileRadioButton->isChecked())
333         stretch = QLatin1String("BorderImage.Repeat");
334     if (uiBorderImage->verticalTileRadioButtonNoCrop->isChecked())
335         stretch = QLatin1String("BorderImage.Round");
336 
337     if (stretch == QLatin1String("BorderImage.Stretch"))
338         emit removeProperty(QLatin1String("verticalTileMode"));
339     else
340         emit propertyChanged(QLatin1String("verticalTileMode"), stretch);
341 
342 }
343 
onFileNameChanged()344 void ContextPaneWidgetImage::onFileNameChanged()
345 {
346     if (m_fileWidget->fileName().isNull())
347         emit removeProperty(QLatin1String("source"));
348     else
349         emit propertyChanged(QLatin1String("source"), QString(QLatin1Char('\"') + m_fileWidget->fileName() + QLatin1Char('\"')));
350 }
351 
onPixmapDoubleClicked()352 void ContextPaneWidgetImage::onPixmapDoubleClicked()
353 {
354     previewDialog()->setParent(parentWidget()->parentWidget());
355     previewDialog()->setMaximumSize(previewDialog()->parentWidget()->size() - QSize(150, 100));
356     if (m_borderImage)
357         previewDialog()->setZoom(4);
358     previewDialog()->setIsBorderImage(m_borderImage);
359 
360     QPoint p = parentWidget()->pos();
361     p = p + QPoint(-2, -2);
362     previewDialog()->show();
363     previewDialog()->update();
364     previewDialog()->move(p);
365     //previewDialog()->adjustSize();
366     if ((previewDialog()->pos().x() + previewDialog()->width()) > previewDialog()->parentWidget()->width())
367         previewDialog()->move(previewDialog()->parentWidget()->width() - (previewDialog()->width()) - 40, p.y());
368 
369     if ((previewDialog()->pos().y() + previewDialog()->height()) > previewDialog()->parentWidget()->height())
370         previewDialog()->move(previewDialog()->pos().x(), previewDialog()->parentWidget()->height() - (previewDialog()->height()) - 40);
371 
372     if (previewDialog()->pos().x() < 0)
373         previewDialog()->move(0, previewDialog()->pos().y());
374     if (previewDialog()->pos().y() < 0)
375         previewDialog()->move(previewDialog()->pos().x(), 0);
376 
377     previewDialog()->raise();
378 }
379 
onLeftMarginsChanged()380 void ContextPaneWidgetImage::onLeftMarginsChanged()
381 {
382     if (previewDialog()->previewLabel()->leftMarging())
383         emit propertyChanged("border.left", previewDialog()->previewLabel()->leftMarging());
384     else
385         emit removeProperty(QLatin1String("border.left"));
386 }
387 
onRightMarginsChanged()388 void ContextPaneWidgetImage::onRightMarginsChanged()
389 {
390     if (previewDialog()->previewLabel()->rightMarging())
391         emit propertyChanged("border.right", previewDialog()->previewLabel()->rightMarging());
392     else
393         emit removeProperty(QLatin1String("border.right"));
394 
395 
396 }
397 
onTopMarginsChanged()398 void ContextPaneWidgetImage::onTopMarginsChanged()
399 {
400     if (previewDialog()->previewLabel()->topMarging())
401         emit propertyChanged("border.top", previewDialog()->previewLabel()->topMarging());
402     else
403         emit removeProperty(QLatin1String("border.top"));
404 }
405 
onBottomMarginsChanged()406 void ContextPaneWidgetImage::onBottomMarginsChanged()
407 {
408     if (previewDialog()->previewLabel()->bottomMarging())
409         emit propertyChanged("border.bottom", previewDialog()->previewLabel()->bottomMarging());
410     else
411         emit removeProperty(QLatin1String("border.bottom"));
412 
413 }
414 
stringToRule(const QString & s)415 static inline Qt::TileRule stringToRule(const QString &s)
416 {
417     if (s == QLatin1String("Stretch"))
418         return  Qt::StretchTile;
419     if (s == QLatin1String("Repeat"))
420         return  Qt::RepeatTile;
421     if (s == QLatin1String("Round"))
422         return Qt::RoundTile;
423 
424     qWarning("QDeclarativeGridScaledImage: Invalid tile rule specified. Using Stretch.");
425     return  Qt::StretchTile;
426 }
427 
parseSciFile(const QString & fileName,QString & pixmapFileName,int & left,int & right,int & top,int & bottom,Qt::TileRule & horizontalTileRule,Qt::TileRule & verticalTileRule)428 static inline bool parseSciFile(const QString &fileName, QString &pixmapFileName, int &left, int &right, int &top, int &bottom, Qt::TileRule &horizontalTileRule, Qt::TileRule &verticalTileRule)
429 {
430     int l = -1;
431     int r = -1;
432     int t = -1;
433     int b = -1;
434     QString imgFile;
435 
436     QFile data(fileName);
437     if (!data.open(QIODevice::ReadOnly))
438         return false;
439 
440     QByteArray raw;
441     while (raw = data.readLine(), !raw.isEmpty()) {
442         QString line = QString::fromUtf8(raw.trimmed());
443         if (line.isEmpty() || line.startsWith(QLatin1Char('#')))
444             continue;
445 
446         QStringList list = line.split(QLatin1Char(':'));
447         if (list.count() != 2)
448             return false;
449 
450         list[0] = list[0].trimmed();
451         list[1] = list[1].trimmed();
452 
453         if (list[0] == QLatin1String("border.left"))
454             l = list[1].toInt();
455         else if (list[0] == QLatin1String("border.right"))
456             r = list[1].toInt();
457         else if (list[0] == QLatin1String("border.top"))
458             t = list[1].toInt();
459         else if (list[0] == QLatin1String("border.bottom"))
460             b = list[1].toInt();
461         else if (list[0] == QLatin1String("source"))
462             imgFile = list[1];
463         else if (list[0] == QLatin1String("horizontalTileRule"))
464             horizontalTileRule = stringToRule(list[1]);
465         else if (list[0] == QLatin1String("verticalTileRule"))
466            verticalTileRule = stringToRule(list[1]);
467     }
468 
469     if (l < 0 || r < 0 || t < 0 || b < 0 || imgFile.isEmpty())
470         return false;
471 
472     left = l; right = r; top = t; bottom = b;
473 
474     pixmapFileName = imgFile;
475 
476     return true;
477 }
478 
setPixmap(const QString & fileName)479 void ContextPaneWidgetImage::setPixmap(const QString &fileName)
480 {
481     QPixmap pix(76,76);
482     pix.fill(Qt::black);
483 
484     if (m_borderImage) {
485         QString localFileName(fileName);
486         if (QFileInfo::exists(fileName)) {
487             if (fileName.endsWith(QLatin1String("sci"))) {
488                 QString pixmapFileName;
489                 int left = 0;
490                 int right = 0;
491                 int top = 0;
492                 int bottom = 0;
493 
494                 Qt::TileRule horizontalTileRule;
495                 Qt::TileRule verticalTileRule;
496                 if (parseSciFile(fileName, pixmapFileName, left, right, top, bottom, horizontalTileRule, verticalTileRule)) {
497                     localFileName = QFileInfo(fileName).absoluteDir().absolutePath() + QLatin1Char('/') + pixmapFileName;
498                     previewDialog()->previewLabel()->setMargins(left, top, right, bottom);
499                 } else { // sci file not parsed correctly
500                     uiBorderImage->sizeLabel->clear();
501                     return;
502                 }
503             }
504             QPixmap source(localFileName);
505             if (source.isNull())
506                 source = pix;
507             previewDialog()->setPixmap(source, previewDialog()->zoom());
508             uiBorderImage->sizeLabel->setText(QString::number(source.width()) + QLatin1Char('x')
509                                               + QString::number(source.height()));
510             QPainter p(&pix);
511             Qt::TileRule horizontalTileMode = Qt::StretchTile;
512             Qt::TileRule verticalTileMode = Qt::StretchTile;
513             if (uiBorderImage->horizontalTileRadioButton->isChecked())
514                 horizontalTileMode =Qt::RepeatTile;
515             if (uiBorderImage->horizontalTileRadioButtonNoCrop->isChecked())
516                 horizontalTileMode =Qt::RoundTile;
517             if (uiBorderImage->verticalTileRadioButton->isChecked())
518                 verticalTileMode =Qt::RepeatTile;
519             if (uiBorderImage->verticalTileRadioButtonNoCrop->isChecked())
520                 verticalTileMode =Qt::RoundTile;
521             QTileRules rules(horizontalTileMode, verticalTileMode);
522             PreviewLabel *previewLabel = previewDialog()->previewLabel();
523             QMargins margins(previewLabel->leftMarging() ,previewLabel->topMarging() ,previewLabel->rightMarging(), previewLabel->bottomMarging());
524             qDrawBorderPixmap(&p, QRect(0, 0, 76, 76), margins, source, source.rect(), margins, rules);
525             //p.drawPixmap(0,0,76,76, source);
526         } else {
527             uiBorderImage->sizeLabel->clear();
528         }
529         uiBorderImage->label->setPixmap(pix);
530     } else {
531         if (QFileInfo::exists(fileName)) {
532             QPixmap source(fileName);
533             previewDialog()->setPixmap(source, 1);
534             ui->sizeLabel->setText(QString::number(source.width()) + QLatin1Char('x') + QString::number(source.height()));
535             QPainter p(&pix);
536             if (ui->stretchRadioButton->isChecked()) {
537                 p.drawPixmap(0,0,76,76, source);
538             } else if (ui->tileRadioButton->isChecked()) {
539                 QPixmap small = source.scaled(38,38);
540                 p.drawTiledPixmap(0,0,76,76, small);
541             } else if (ui->horizontalStretchRadioButton->isChecked()) {
542                 QPixmap small = source.scaled(38,38);
543                 QPixmap half = pix.scaled(38, 76);
544                 QPainter p2(&half);
545                 p2.drawTiledPixmap(0,0,38,76, small);
546                 p.drawPixmap(0,0,76,76, half);
547             } else if (ui->verticalStretchRadioButton->isChecked()) {
548                 QPixmap small = source.scaled(38,38);
549                 QPixmap half = pix.scaled(76, 38);
550                 QPainter p2(&half);
551                 p2.drawTiledPixmap(0,0,76,38, small);
552                 p.drawPixmap(0,0,76,76, half);
553             } else if (ui->preserveAspectFitRadioButton->isChecked()) {
554                 QPixmap preserved = source.scaledToWidth(76);
555                 int offset = (76 - preserved.height()) / 2;
556                 p.drawPixmap(0, offset, 76, preserved.height(), source);
557             } else if (ui->cropAspectFitRadioButton->isChecked()) {
558                 QPixmap cropped = source.scaledToHeight(76);
559                 int offset = (76 - cropped.width()) / 2;
560                 p.drawPixmap(offset, 0, cropped.width(), 76, source);
561             }
562         } else {
563             ui->sizeLabel->clear();
564         }
565 
566         ui->label->setPixmap(pix);
567 
568     }
569 }
570 
previewDialog()571 PreviewDialog* ContextPaneWidgetImage::previewDialog()
572 {
573     if (!m_previewDialog) {
574         m_previewDialog = new PreviewDialog(this);
575         m_previewDialog->hide();
576     }
577 
578     return m_previewDialog.data();
579 }
580 
changeEvent(QEvent * e)581 void ContextPaneWidgetImage::changeEvent(QEvent *e)
582 {
583     QWidget::changeEvent(e);
584     switch (e->type()) {
585     case QEvent::LanguageChange:
586         if (ui)
587             ui->retranslateUi(this);
588         if (uiBorderImage)
589             uiBorderImage->retranslateUi(this);
590         break;
591     default:
592         break;
593     }
594 }
595 
596 
hideEvent(QHideEvent * event)597 void ContextPaneWidgetImage::hideEvent(QHideEvent * event)
598 {
599     previewWasVisible = previewDialog()->isVisible();
600     previewDialog()->hide();
601     QWidget::hideEvent(event);
602 }
603 
showEvent(QShowEvent * event)604 void ContextPaneWidgetImage::showEvent(QShowEvent* event)
605 {
606     if (previewWasVisible)
607         previewDialog()->show();
608     QWidget::showEvent(event);
609 }
610 
PreviewLabel(QWidget * parent)611 PreviewLabel::PreviewLabel(QWidget *parent)
612     : QLabel(parent),
613       m_dragging_left(false), m_dragging_right(false),
614       m_dragging_top(false), m_dragging_bottom(false)
615 
616 {
617     m_zoom = 1;
618     m_showBorders = false;
619     m_left = 2;
620     m_right = 2;
621     m_top = 2;
622     m_bottom = 2;
623     setMouseTracking(true);
624     setCursor(QCursor(Qt::ArrowCursor));
625     m_hooverInfo = new QLabel(parentWidget());
626     m_hooverInfo->hide();
627 
628     m_hooverInfo->setFrameShape(QFrame::StyledPanel);
629     m_hooverInfo->setFrameShadow(QFrame::Sunken);
630     QGraphicsDropShadowEffect *dropShadowEffect = new QGraphicsDropShadowEffect;
631     dropShadowEffect->setBlurRadius(4);
632     dropShadowEffect->setOffset(2, 2);
633     m_hooverInfo->setGraphicsEffect(dropShadowEffect);
634     m_hooverInfo->setAutoFillBackground(true);
635     m_hooverInfo->raise();
636 }
637 
setZoom(int z)638 void PreviewLabel::setZoom(int z)
639 {
640     m_zoom = z;
641 }
642 
setIsBorderImage(bool b)643 void PreviewLabel::setIsBorderImage(bool b)
644 {
645     m_borderImage = b;
646 }
647 
setMargins(int left,int top,int right,int bottom)648 void PreviewLabel::setMargins(int left, int top, int right, int bottom)
649 {
650     m_left = left;
651     m_top = top;
652     m_right = right;
653     m_bottom = bottom;
654 }
655 
paintEvent(QPaintEvent * event)656 void PreviewLabel::paintEvent(QPaintEvent *event)
657 {
658     QLabel::paintEvent(event);
659     if (m_borderImage) {
660 
661         QPainter p(this);
662 
663         p.setOpacity(0.5);
664 
665         p.setBackgroundMode(Qt::TransparentMode);
666         {
667             QPen pen(Qt::SolidLine);
668             pen.setColor(QColor(0xf0, 0xf0, 0xf0));
669             p.setPen(pen);
670 
671             if (m_left >= 0)
672                 p.drawLine(m_left * m_zoom, 4, m_left * m_zoom, height() - 4);
673             if (m_right >= 0)
674                 p.drawLine(width() - m_right * m_zoom - 1, 4, width() - m_right * m_zoom - 1, height() - 4);
675             if (m_top >= 0)
676                 p.drawLine(4, m_top * m_zoom, width() - 4, m_top * m_zoom);
677             if (m_bottom >= 0)
678                 p.drawLine(4, height() - m_bottom * m_zoom - 1, width() - 4, height() - m_bottom * m_zoom - 1);
679         }
680 
681         {
682             QBrush brush(Qt::Dense4Pattern);
683             brush.setColor(QColor(0x10, 0x10, 0x10));
684             QPen pen(brush, 1, Qt::DotLine);
685             pen.setColor(QColor(0x10, 0x10, 0x10));
686             p.setPen(pen);
687 
688             if (m_left >= 0)
689                 p.drawLine(m_left * m_zoom, 4, m_left * m_zoom, height() - 4);
690             if (m_right >= 0)
691                 p.drawLine(width() - m_right * m_zoom - 1, 4, width() - m_right * m_zoom - 1, height() - 4);
692             if (m_top >= 0)
693                 p.drawLine(4, m_top * m_zoom, width() - 4, m_top * m_zoom);
694             if (m_bottom >= 0)
695                 p.drawLine(4, height() - m_bottom * m_zoom - 1, width() - 4, height() - m_bottom * m_zoom - 1);
696         }
697     }
698 }
699 
rangeCheck(int target,int pos)700 static inline bool rangeCheck(int target, int pos)
701 {
702     return (target - 10 < pos) && (target + 10 > pos);
703 }
704 
mousePressEvent(QMouseEvent * event)705 void PreviewLabel::mousePressEvent(QMouseEvent * event)
706 {
707     if (!m_borderImage) {
708         QLabel::mouseMoveEvent(event);
709         return;
710     }
711 
712     bool bottom = false;
713 
714     if (event->button() == Qt::LeftButton) {
715         if (QApplication::overrideCursor())
716             QApplication::restoreOverrideCursor();
717         if (rangeCheck(m_left * m_zoom, event->pos().x())) {
718             QApplication::setOverrideCursor(QCursor(Qt::SizeHorCursor));
719             m_dragging_left = true;
720             event->accept();
721             m_hooverInfo->setText(QLatin1String("Left ") + QString::number(m_left));
722             m_hooverInfo->show();
723         } else if (rangeCheck(m_top * m_zoom, event->pos().y())) {
724             QApplication::setOverrideCursor(QCursor(Qt::SizeVerCursor));
725             m_dragging_top = true;
726             event->accept();
727             m_hooverInfo->setText(QLatin1String("Top ") + QString::number(m_top));
728             m_hooverInfo->show();
729         } else if (rangeCheck(m_right * m_zoom, width() - event->pos().x())) {
730             QApplication::setOverrideCursor(QCursor(Qt::SizeHorCursor));
731             m_dragging_right = true;
732             event->accept();
733             m_hooverInfo->setText(QLatin1String("Right ") + QString::number(m_right));
734             m_hooverInfo->show();
735         } else if (rangeCheck(m_bottom * m_zoom, height() - event->pos().y())) {
736             QApplication::setOverrideCursor(QCursor(Qt::SizeVerCursor));
737             m_dragging_bottom = true;
738             event->accept();
739             m_hooverInfo->setText(QLatin1String("Bottom ") + QString::number(m_bottom));
740             m_hooverInfo->show();
741             bottom = true;
742         } else {
743             QLabel::mousePressEvent(event);
744         }
745         m_startPos = event->pos();
746         if (bottom)
747             m_hooverInfo->move(mapToParent(m_startPos) + QPoint(0, -10));
748         else
749             m_hooverInfo->move(mapToParent(m_startPos) + QPoint(0, 40));
750         m_hooverInfo->resize(m_hooverInfo->sizeHint());
751         m_hooverInfo->raise();
752     }
753 }
754 
mouseReleaseEvent(QMouseEvent * event)755 void PreviewLabel::mouseReleaseEvent(QMouseEvent * event)
756 {
757     if (!m_borderImage)
758         return QLabel::mouseMoveEvent(event);
759 
760     if (m_dragging_left || m_dragging_top || m_dragging_right|| m_dragging_bottom) {
761         m_hooverInfo->hide();
762         if (m_dragging_left)
763             emit leftMarginChanged();
764 
765         if (m_dragging_top)
766             emit topMarginChanged();
767 
768         if (m_dragging_bottom)
769             emit bottomMarginChanged();
770 
771         if (m_dragging_right)
772             emit rightMarginChanged();
773 
774         m_dragging_left = false;
775         m_dragging_top = false;
776         m_dragging_right = false;
777         m_dragging_bottom = false;
778         QApplication::restoreOverrideCursor();
779         event->accept();
780 
781     } else {
782         QLabel::mouseReleaseEvent(event);
783     }
784 }
785 
786 
limitPositive(int i)787 static inline int limitPositive(int i)
788 {
789     if (i >= 0)
790         return i;
791     return 0;
792 }
793 
mouseMoveEvent(QMouseEvent * event)794 void PreviewLabel::mouseMoveEvent(QMouseEvent * event)
795 {
796     if (!m_borderImage) {
797         QLabel::mouseMoveEvent(event);
798         return;
799     }
800 
801     QPoint p = event->pos();
802     bool bottom = false;
803     if (m_dragging_left) {
804         m_left = p.x() / m_zoom;
805         m_left = limitPositive(m_left);
806         event->accept();
807         m_hooverInfo->setText(QLatin1String("Left ") + QString::number(m_left));
808         update();
809     } else if (m_dragging_top) {
810         m_top = p.y() / m_zoom;
811         m_top = limitPositive(m_top);
812         event->accept();
813         m_hooverInfo->setText(QLatin1String("Top ") + QString::number(m_top));
814         update();
815     }  else if (m_dragging_right) {
816         m_right = (width() - p.x()) / m_zoom;
817         m_right = limitPositive(m_right);
818         event->accept();
819         m_hooverInfo->setText(QLatin1String("Right ") + QString::number(m_right));
820         update();
821     } else if (m_dragging_bottom) {
822         m_bottom = (height() - p.y()) / m_zoom;
823         m_bottom = limitPositive(m_bottom);
824         event->accept();
825         m_hooverInfo->setText(QLatin1String("Bottom ") + QString::number(m_bottom));
826         bottom = true;
827         update();
828     } else if (rangeCheck(m_left * m_zoom, p.x())) {
829         QApplication::setOverrideCursor(QCursor(Qt::SizeHorCursor));
830         event->accept();
831         m_hooverInfo->setText(QLatin1String("Left ") + QString::number(m_left));
832         m_hooverInfo->show();
833     } else if (rangeCheck(m_top * m_zoom, p.y())) {
834         QApplication::setOverrideCursor(QCursor(Qt::SizeVerCursor));
835         event->accept();
836         m_hooverInfo->setText(QLatin1String("Top ") + QString::number(m_top));
837         m_hooverInfo->show();
838     } else if (rangeCheck(m_right * m_zoom, width() - p.x())) {
839         QApplication::setOverrideCursor(QCursor(Qt::SizeHorCursor));
840         event->accept();
841         m_hooverInfo->setText(QLatin1String("Right ") + QString::number(m_right));
842         m_hooverInfo->show();
843     } else if (rangeCheck(m_bottom * m_zoom, height() - p.y())) {
844         QApplication::setOverrideCursor(QCursor(Qt::SizeVerCursor));
845         event->accept();
846         m_hooverInfo->setText(QLatin1String("Bottom ") + QString::number(m_bottom));
847         m_hooverInfo->show();
848         bottom = true;
849     } else {
850         if (QApplication::overrideCursor())
851             QApplication::restoreOverrideCursor();
852         QLabel::mouseMoveEvent(event);
853         m_hooverInfo->hide();
854     }
855     m_startPos = p;
856     if (bottom)
857         m_hooverInfo->move(mapToParent(p) + QPoint(0, -10));
858     else
859         m_hooverInfo->move(mapToParent(p) + QPoint(0, 40));
860     m_hooverInfo->resize(m_hooverInfo->sizeHint());
861     m_hooverInfo->raise();
862 }
863 
leaveEvent(QEvent * event)864 void PreviewLabel::leaveEvent(QEvent* event )
865 {
866     while (QApplication::overrideCursor())
867         QApplication::restoreOverrideCursor();
868     m_hooverInfo->hide();
869     QLabel::leaveEvent(event);
870 }
871 
PreviewDialog(QWidget * parent)872 PreviewDialog::PreviewDialog(QWidget *parent) : DragWidget(parent)
873 {
874     m_borderImage = false;
875     setAutoFillBackground(true);
876 
877     m_label = new PreviewLabel(this);
878     m_slider = new QSlider(this);
879 
880     m_zoomLabel = new QLabel(this);
881 
882     setZoom(1);
883 
884     QVBoxLayout *layout = new QVBoxLayout(this);
885     QHBoxLayout *horizontalLayout = new QHBoxLayout();
886     QHBoxLayout *horizontalLayout2 = new QHBoxLayout();
887     layout->setContentsMargins(2, 2, 2, 16);
888     layout->setSpacing(4);
889     QToolButton *toolButton = new QToolButton(this);
890     QIcon icon(style()->standardIcon(QStyle::SP_DockWidgetCloseButton));
891     toolButton->setIcon(icon);
892     toolButton->setToolButtonStyle(Qt::ToolButtonIconOnly);
893     toolButton->setFixedSize(icon.availableSizes().value(0) + QSize(4, 4));
894     connect(toolButton, &QToolButton::clicked, this, &PreviewDialog::onTogglePane);
895 
896     QScrollArea *scrollArea = new QScrollArea(this);
897     WheelFilter *wheelFilter = new WheelFilter(scrollArea);
898     scrollArea->setWidget(m_label);
899     scrollArea->setFrameStyle(QFrame::NoFrame);
900     m_slider->setOrientation(Qt::Horizontal);
901     m_slider->setRange(1, 6);
902     m_slider->setFixedWidth(80);
903     m_zoomLabel->setFixedWidth(50);
904 
905     horizontalLayout->addWidget(toolButton);
906     horizontalLayout->addSpacing(6);
907     horizontalLayout->addWidget(m_slider);
908     horizontalLayout->addSpacing(6);
909     horizontalLayout->addWidget(m_zoomLabel);
910     horizontalLayout->addStretch(1);
911 
912     layout->addLayout(horizontalLayout);
913     horizontalLayout2->addSpacing(24);
914     horizontalLayout2->addWidget(scrollArea);
915     layout->addLayout(horizontalLayout2);
916 
917     wheelFilter->setTarget(this);
918 
919     connect(m_slider, &QSlider::valueChanged, this, &PreviewDialog::onSliderMoved);
920 
921     foreach (QWidget *childWidget, findChildren<QWidget*>()) {
922         childWidget->installEventFilter(wheelFilter);
923     }
924 }
925 
setPixmap(const QPixmap & p,int zoom)926 void PreviewDialog::setPixmap(const QPixmap &p, int zoom)
927 {
928     m_pixmap = p;
929     if (!p.isNull())
930         m_label->setPixmap(p.scaled(p.width() * zoom, p.height() * zoom));
931     else
932         m_label->setPixmap(QPixmap());
933     m_label->adjustSize();
934     m_zoom = zoom;
935     m_label->setZoom(m_zoom);
936 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
937     QSize size = m_label->pixmap().size() + QSize(54, 44);
938 #else
939     QSize size = m_label->pixmap()->size() + QSize(54, 44);
940 #endif
941     if (size.width() < 180)
942         size.setWidth(180);
943     resize(size);
944 }
945 
wheelEvent(QWheelEvent * event)946 void PreviewDialog::wheelEvent(QWheelEvent* event)
947 {
948     const auto delta = event->angleDelta().y();
949     event->accept();
950     if (delta >  0) {
951         if (m_zoom == 1)
952             setZoom(2);
953         else if (m_zoom == 2)
954             setZoom(4);
955         else if (m_zoom == 4)
956             setZoom(6);
957         else if (m_zoom == 6)
958             setZoom(8);
959         else if (m_zoom == 8)
960             setZoom(10);
961     } else {
962         if (m_zoom == 10)
963             setZoom(8);
964         else if (m_zoom == 8)
965             setZoom(6);
966         else if (m_zoom == 6)
967             setZoom(4);
968         else if (m_zoom == 4)
969             setZoom(2);
970         else if (m_zoom == 2)
971             setZoom(1);
972     }
973     setPixmap(m_pixmap, m_zoom);
974 }
975 
onTogglePane()976 void PreviewDialog::onTogglePane()
977 {
978     hide();
979 }
980 
onSliderMoved(int value)981 void PreviewDialog::onSliderMoved(int value)
982 {
983     switch (value) {
984     case 1: setZoom(1); break;
985     case 2: setZoom(2); break;
986     case 3: setZoom(4); break;
987     case 4: setZoom(6); break;
988     case 5: setZoom(8); break;
989     case 6: setZoom(10); break;
990     default: break;
991     }
992 }
993 
994 } //QmlDesigner
995