KProperty

pixmapedit.cpp
1 /* This file is part of the KDE project
2  Copyright (C) 2004 Cedric Pasteur <[email protected]>
3  Copyright (C) 2004 Alexander Dymo <[email protected]>
4  Copyright (C) 2005-2015 Jarosław Staniek <[email protected]>
5 
6  This library is free software; you can redistribute it and/or
7  modify it under the terms of the GNU Library General Public
8  License as published by the Free Software Foundation; either
9  version 2 of the License, or (at your option) any later version.
10 
11  This library is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  Library General Public License for more details.
15 
16  You should have received a copy of the GNU Library General Public License
17  along with this library; see the file COPYING.LIB. If not, write to
18  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20 */
21 
22 #include "pixmapedit.h"
23 #include "utils.h"
24 #include "KProperty.h"
25 #include "KPropertyUtils.h"
26 #include "KPropertyUtils_p.h"
27 
28 #include <QApplication>
29 #include <QLabel>
30 #include <QCursor>
31 #include <QImage>
32 #include <QDesktopWidget>
33 #include <QPixmap>
34 #include <QEvent>
35 #include <QKeyEvent>
36 #include <QFrame>
37 #include <QResizeEvent>
38 #include <QMouseEvent>
39 #include <QPushButton>
40 #include <QFileDialog>
41 #include <QHBoxLayout>
42 
43 static const int POPUP_MARGIN = 2;
44 
45 class Q_DECL_HIDDEN KPropertyPixmapEditor::Private
46 {
47 public:
48  Private()
49  {
50  }
51  ~Private()
52  {
53  delete popup;
54  }
55 
56  QLabel *edit;
57  QLabel *popup;
58  QPushButton *button;
59  KProperty *property;
61  QPixmap pixmap;
63  QPixmap previewPixmap;
64 };
65 
66 KPropertyPixmapEditor::KPropertyPixmapEditor(KProperty *prop, QWidget *parent)
67  : QWidget(parent), d(new Private)
68 {
69  d->property = prop;
70  setBackgroundRole(QPalette::Base);
71 
72  QHBoxLayout *lyr = new QHBoxLayout(this);
73  lyr->setContentsMargins(0,0,0,0);
74 
75  d->edit = new QLabel(this);
76  lyr->addWidget(d->edit);
77  d->edit->setContentsMargins(0, 1, 0, 0);
78  d->edit->setToolTip(tr("Click to show image preview"));
79  d->edit->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
80  d->edit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
81  d->edit->setBackgroundRole(QPalette::Base);
82  d->edit->setMouseTracking(true);
83 
84  d->button = new QPushButton(this);
85  lyr->addWidget(d->button);
86  KPropertyUtils::setupDotDotDotButton(d->button, tr("Insert image from file"),
87  tr("Inserts image from file"));
88 
89  d->popup = new QLabel(nullptr, Qt::ToolTip);
90  d->popup->setBackgroundRole(QPalette::ToolTipBase);
91  d->popup->setForegroundRole(QPalette::ToolTipText);
92  d->popup->setFrameStyle(QFrame::Plain | QFrame::Box);
93  d->popup->setMargin(POPUP_MARGIN);
94  d->popup->setLineWidth(1);
95  d->popup->hide();
96 
97  setFocusProxy(d->edit);
98  connect(d->button, SIGNAL(clicked()), this, SLOT(selectPixmap()));
99 
100  d->edit->installEventFilter(this);
101  installEventFilter(this);
102 }
103 
104 KPropertyPixmapEditor::~KPropertyPixmapEditor()
105 {
106  delete d;
107 }
108 
109 QVariant KPropertyPixmapEditor::value() const
110 {
111  return d->pixmap;
112 }
113 
114 void KPropertyPixmapEditor::setValue(const QVariant &value)
115 {
116  d->pixmap = value.value<QPixmap>();
117  if (d->pixmap.isNull() || (d->pixmap.height() <= height())) {
118  d->previewPixmap = d->pixmap;
119  } else {
120  QImage img(d->pixmap.toImage());
121  const QSize sz(size() - QSize(0,1));
122  if (!QRect(QPoint(0, 0), sz).contains(d->pixmap.rect())) {
123  img = img.scaled(sz, Qt::KeepAspectRatio, Qt::SmoothTransformation);
124  d->previewPixmap = QPixmap::fromImage(img);//preview pixmap is a bit larger
125  } else {
126  d->previewPixmap = d->pixmap;
127  }
128  }
129  emit commitData(this);
130 }
131 
132 QString KPropertyPixmapEditor::selectPixmapFileName()
133 {
134  /*#ifdef PURE_QT
135  QString url = QFileDialog::getOpenFileName();
136  if (!url.isEmpty()) {
137  d->edit->setPixmap(QPixmap(url));
138  emit valueChanged(this);
139  }
140  #endif*/
141  const QString caption(
142  tr("Insert Image From File (for \"%1\" property)").arg(d->property->captionOrName()));
143  /*KDE4:
144  #ifdef Q_OS_WIN
145  QString recentDir;
146  QString fileName = Q3FileDialog::getOpenFileName(
147  KFileDialog::getStartURL(":lastVisitedImagePath", recentDir).path(),
148  convertKFileDialogFilterToQFileDialogFilter(KImageIO::pattern(KImageIO::Reading)),
149  this, 0, caption);
150  #else*/
151  const QUrl url(QFileDialog::getOpenFileUrl(this, caption));
152  QString fileName = url.isLocalFile() ? url.toLocalFile() : url.toString();
153 
155 //#endif
156  return fileName;
157 }
158 
159 void KPropertyPixmapEditor::selectPixmap()
160 {
161  const QString fileName(selectPixmapFileName());
162  if (fileName.isEmpty())
163  return;
164 
165  QPixmap pm;
166  if (!pm.load(fileName)) {
168  return;
169  }
170  setValue(pm);
171 
172  /* KDE4:
173  #ifdef Q_OS_WIN
174  //save last visited path
175  QUrl url(fileName);
176  if (url.isLocalFile())
177  KRecentDirs::add(":lastVisitedImagePath", url.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).path());
178  #endif
179  */
180 }
181 
182 static QRect popupGeometry(QWidget *editor, const QSize &pixmapSize)
183 {
184  const QRect screenRect = QApplication::desktop()->availableGeometry(editor);
185  const QRect globalRect = QRect(editor->mapToGlobal(QPoint(0, 0)), editor->size()) & screenRect;
186  int aboveSpace = std::min(pixmapSize.height(), globalRect.y() - screenRect.top());
187  int belowSpace = std::min(pixmapSize.height(), screenRect.bottom() - globalRect.bottom());
188  // find max area
189  // area3 | area1
190  // area2 | area0
191  const QVector<int> widths{
192  std::min(pixmapSize.width(), screenRect.right() - globalRect.left()),
193  std::min(pixmapSize.width(), screenRect.right() - globalRect.left()),
194  std::min(pixmapSize.width(), globalRect.right() - screenRect.left()),
195  std::min(pixmapSize.width(), globalRect.right() - screenRect.left())
196  };
197  const std::vector<int> areas{ widths[0] * belowSpace, widths[1] * aboveSpace,
198  widths[2] * belowSpace, widths[3] * aboveSpace };
199  const int areaNumber = std::distance(areas.begin(), std::max_element(areas.begin(), areas.end()));
200  QRect rect;
201  switch (areaNumber) {
202  case 0: {
203  int width = double(belowSpace) / pixmapSize.height() * pixmapSize.width();
204  if (width > widths[areaNumber]) {
205  width = widths[areaNumber];
206  belowSpace = double(width) / pixmapSize.width() * pixmapSize.height();
207  }
208  rect = QRect(globalRect.left(), globalRect.bottom(), width, belowSpace);
209  break;
210  }
211  case 1: {
212  int width = double(aboveSpace) / pixmapSize.height() * pixmapSize.width();
213  if (width > widths[areaNumber]) {
214  width = widths[areaNumber];
215  aboveSpace = double(width) / pixmapSize.width() * pixmapSize.height();
216  }
217  rect = QRect(globalRect.left(), globalRect.top() - aboveSpace, width, aboveSpace);
218  break;
219  }
220  case 2: {
221  int width = double(belowSpace) / pixmapSize.height() * pixmapSize.width();
222  if (width > widths[areaNumber]) {
223  width = widths[areaNumber];
224  belowSpace = double(width) / pixmapSize.width() * pixmapSize.height();
225  }
226  rect = QRect(globalRect.right() - width, globalRect.bottom(), width, belowSpace);
227  break;
228  }
229  case 3: {
230  int width = double(aboveSpace) / pixmapSize.height() * pixmapSize.width();
231  if (width > widths[areaNumber]) {
232  width = widths[areaNumber];
233  aboveSpace = double(width) / pixmapSize.width() * pixmapSize.height();
234  }
235  rect = QRect(globalRect.right() - width, globalRect.top() - aboveSpace, width,
236  aboveSpace);
237  break;
238  }
239  }
240  return rect;
241 }
242 
243 bool
244 KPropertyPixmapEditor::eventFilter(QObject *o, QEvent *ev)
245 {
246  if (o == d->edit) {
247  if (ev->type() == QEvent::MouseButtonPress && static_cast<QMouseEvent*>(ev)->button() == Qt::LeftButton) {
248  if (d->pixmap.height() <= d->edit->height() && d->pixmap.width() <= d->edit->width()) {
249  return false; // nothing to preview
250  }
251  d->popup->setGeometry(popupGeometry(this, d->pixmap.size()));
252  d->popup->setPixmap(d->pixmap);
253  d->popup->show();
254  } else if (ev->type() == QEvent::MouseButtonRelease || ev->type() == QEvent::Hide) {
255  if (d->popup->isVisible()) {
256  d->popup->hide();
257  }
258  } else if (ev->type() == QEvent::KeyPress) {
259  QKeyEvent* e = static_cast<QKeyEvent*>(ev);
260  if ((e->key() == Qt::Key_Enter) || (e->key() == Qt::Key_Space) || (e->key() == Qt::Key_Return)) {
261  d->button->animateClick();
262  return true;
263  }
264  }
265  } else if (o == this) {
266  if (ev->type() == QEvent::Resize) {
267  d->button->setMaximumWidth(height());
268  }
269  }
270  return QWidget::eventFilter(o, ev);
271 }
272 
273 /*
274 void
275 PixmapEdit::setReadOnlyInternal(bool readOnly)
276 {
277  d->button->setEnabled(!readOnly);
278 }*/
279 
280 //-----------------------
281 
282 KPropertyPixmapDelegate::KPropertyPixmapDelegate()
283 {
284 }
285 
286 QWidget* KPropertyPixmapDelegate::createEditor( int type, QWidget *parent,
287  const QStyleOptionViewItem & option, const QModelIndex & index ) const
288 {
289  Q_UNUSED(type);
290  Q_UNUSED(option);
291 
292  KProperty *property = KPropertyUtils::propertyForIndex(index);
293  if (!property) {
294  return nullptr;
295  }
296  KPropertyPixmapEditor *pe = new KPropertyPixmapEditor(property, parent);
297  return pe;
298 }
299 
300 void KPropertyPixmapDelegate::paint( QPainter * painter,
301  const QStyleOptionViewItem & option, const QModelIndex & index ) const
302 {
303  const KPropertyUtilsPrivate::PainterSaver saver(painter);
304  QPixmap pm( index.data(Qt::EditRole).value<QPixmap>() );
305  if (!pm.isNull()) {
306  if (pm.height() > option.rect.height() || pm.width() > option.rect.width()) { //scale down
307  QImage img(pm.toImage());
308  img = img.scaled(option.rect.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
309  pm = QPixmap::fromImage(img);
310  }
312 /* if (d->recentlyPainted != value) {
313  d->recentlyPainted = value;
314  d->scaledPixmap = value.value<QPixmap>();
315  if (d->scaledPixmap.height() > r2.height() || d->scaledPixmap.width() > r2.width()) { //scale down
316  QImage img(d->scaledPixmap.toImage());
317  img = img.scaled(r2.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
318  d->scaledPixmap = QPixmap::fromImage(img);
319  }
320  }*/
321  painter->drawPixmap(option.rect.topLeft().x(),
322  option.rect.topLeft().y() + (option.rect.height() - pm.height()) / 2, pm);
323  }
324  QRect r(option.rect);
325  r.setLeft(r.left() + pm.width() + 2);
326  painter->drawText(r, valueToString(index.data(Qt::EditRole), QLocale()));
327 }
328 
329 QString KPropertyPixmapDelegate::valueToString(const QVariant& value, const QLocale &locale) const
330 {
331  const QPixmap pm(value.value<QPixmap>());
332  if (pm.isNull()) {
333  if (locale.language() == QLocale::C) {
334  return QString();
335  }
336  return QObject::tr("None", "No pixmap");
337  }
338  if (locale.language() == QLocale::C) {
339  return QString::fromLatin1("%1x%2px").arg(pm.width()).arg(pm.height());
340  }
341  return QObject::tr("%1x%2px").arg(locale.toString(pm.width())).arg(locale.toString(pm.height()));
342 }
QString caption()
MouseButtonPress
QEvent::Type type() const const
void setContentsMargins(int left, int top, int right, int bottom)
int width() const const
int width() const const
QString toString(qlonglong i) const const
int right() const const
QPoint mapToGlobal(const QPoint &pos) const const
LeftButton
T value() const const
QPixmap fromImage(const QImage &image, Qt::ImageConversionFlags flags)
int y() const const
QString tr(const char *sourceText, const char *disambiguation, int n)
AlignLeft
QUrl getOpenFileUrl(QWidget *parent, const QString &caption, const QUrl &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options, const QStringList &supportedSchemes)
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
int top() const const
int left() const const
void drawPixmap(const QRectF &target, const QPixmap &pixmap, const QRectF &source)
EditRole
bool isEmpty() const const
void drawText(const QPointF &position, const QString &text)
QLocale::Language language() const const
virtual bool eventFilter(QObject *watched, QEvent *event)
bool load(const QString &fileName, const char *format, Qt::ImageConversionFlags flags)
int key() const const
bool isNull() const const
int height() const const
The base class representing a single property.
Definition: KProperty.h:95
QVariant data(int role) const const
QDesktopWidget * desktop()
const QRect availableGeometry(const QWidget *widget) const const
KeepAspectRatio
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
int height() const const
int bottom() const const
QString fromLatin1(const char *str, int size)
QImage toImage() const const
SmoothTransformation
Key_Enter
QImage scaled(int width, int height, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Sun Aug 9 2020 23:10:57 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.