KProperty

pixmapedit.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
3 Copyright (C) 2004 Alexander Dymo <cloudtemple@mskat.net>
4 Copyright (C) 2005-2015 Jarosław Staniek <staniek@kde.org>
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
43static const int POPUP_MARGIN = 2;
44
45class Q_DECL_HIDDEN KPropertyPixmapEditor::Private
46{
47public:
48 Private()
49 {
50 }
51 ~Private()
52 {
53 delete popup;
54 }
55
56 QLabel *edit;
57 QLabel *popup;
58 QPushButton *button;
59 KProperty *property;
60 //! @todo QVariant recentlyPainted;
61 QPixmap pixmap;
62 //! @todo QPixmap scaledPixmap
63 QPixmap previewPixmap;
64};
65
66KPropertyPixmapEditor::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
104KPropertyPixmapEditor::~KPropertyPixmapEditor()
105{
106 delete d;
107}
108
109QVariant KPropertyPixmapEditor::value() const
110{
111 return d->pixmap;
112}
113
114void 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
132QString 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
154 //! @todo download the file if remote, then set fileName properly
155//#endif
156 return fileName;
157}
158
159void KPropertyPixmapEditor::selectPixmap()
160{
161 const QString fileName(selectPixmapFileName());
162 if (fileName.isEmpty())
163 return;
164
165 QPixmap pm;
166 if (!pm.load(fileName)) {
167//! @todo err msg
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
182static 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
243bool
244KPropertyPixmapEditor::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/*
274void
275PixmapEdit::setReadOnlyInternal(bool readOnly)
276{
277 d->button->setEnabled(!readOnly);
278}*/
279
280//-----------------------
281
282KPropertyPixmapDelegate::KPropertyPixmapDelegate()
283{
284}
285
286QWidget* 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
300void 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 }
311//! @todo
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
329QString 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}
The base class representing a single property.
Definition KProperty.h:96
QString captionOrName() const
Definition KProperty.h:202
void animateClick(int msec)
QDesktopWidget * desktop()
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
const QRect availableGeometry(const QWidget *widget) const const
MouseButtonPress
QEvent::Type type() const const
QUrl getOpenFileUrl(QWidget *parent, const QString &caption, const QUrl &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options, const QStringList &supportedSchemes)
int key() const const
void setPixmap(const QPixmap &)
void setContentsMargins(int left, int top, int right, int bottom)
QLocale::Language language() const const
QString toString(qlonglong i) const const
QVariant data(int role) const const
Qt::MouseButton button() const const
virtual bool eventFilter(QObject *watched, QEvent *event)
QVariant property(const char *name) const const
QString tr(const char *sourceText, const char *disambiguation, int n)
void drawPixmap(const QRectF &target, const QPixmap &pixmap, const QRectF &source)
void drawText(const QPointF &position, const QString &text)
QPixmap fromImage(const QImage &image, Qt::ImageConversionFlags flags)
int height() const const
bool isNull() const const
bool load(const QString &fileName, const char *format, Qt::ImageConversionFlags flags)
QRect rect() const const
QSize size() const const
QImage toImage() const const
int width() const const
int bottom() const const
int left() const const
int right() const const
int top() const const
int y() const const
int height() const const
int width() const const
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
QString fromLatin1(const char *str, int size)
bool isEmpty() const const
AlignLeft
KeepAspectRatio
EditRole
Key_Enter
LeftButton
SmoothTransformation
T value() const const
void hide()
QPoint mapToGlobal(const QPoint &pos) const const
void setMaximumWidth(int maxw)
void setGeometry(int x, int y, int w, int h)
void show()
bool isVisible() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sun Feb 25 2024 18:41:55 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.