File indexing completed on 2024-05-05 13:07:59
0001 /* This file is part of the KDE project 0002 Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> 0003 Copyright (C) 2004 Alexander Dymo <cloudtemple@mskat.net> 0004 Copyright (C) 2005-2015 Jarosław Staniek <staniek@kde.org> 0005 0006 This library is free software; you can redistribute it and/or 0007 modify it under the terms of the GNU Library General Public 0008 License as published by the Free Software Foundation; either 0009 version 2 of the License, or (at your option) any later version. 0010 0011 This library is distributed in the hope that it will be useful, 0012 but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0014 Library General Public License for more details. 0015 0016 You should have received a copy of the GNU Library General Public License 0017 along with this library; see the file COPYING.LIB. If not, write to 0018 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0019 * Boston, MA 02110-1301, USA. 0020 */ 0021 0022 #include "pixmapedit.h" 0023 #include "utils.h" 0024 #include "KProperty.h" 0025 #include "KPropertyUtils.h" 0026 #include "KPropertyUtils_p.h" 0027 0028 #include <QApplication> 0029 #include <QLabel> 0030 #include <QCursor> 0031 #include <QImage> 0032 #include <QDesktopWidget> 0033 #include <QPixmap> 0034 #include <QEvent> 0035 #include <QKeyEvent> 0036 #include <QFrame> 0037 #include <QResizeEvent> 0038 #include <QMouseEvent> 0039 #include <QPushButton> 0040 #include <QFileDialog> 0041 #include <QHBoxLayout> 0042 0043 static const int POPUP_MARGIN = 2; 0044 0045 class Q_DECL_HIDDEN KPropertyPixmapEditor::Private 0046 { 0047 public: 0048 Private() 0049 { 0050 } 0051 ~Private() 0052 { 0053 delete popup; 0054 } 0055 0056 QLabel *edit; 0057 QLabel *popup; 0058 QPushButton *button; 0059 KProperty *property; 0060 //! @todo QVariant recentlyPainted; 0061 QPixmap pixmap; 0062 //! @todo QPixmap scaledPixmap 0063 QPixmap previewPixmap; 0064 }; 0065 0066 KPropertyPixmapEditor::KPropertyPixmapEditor(KProperty *prop, QWidget *parent) 0067 : QWidget(parent), d(new Private) 0068 { 0069 d->property = prop; 0070 setBackgroundRole(QPalette::Base); 0071 0072 QHBoxLayout *lyr = new QHBoxLayout(this); 0073 lyr->setContentsMargins(0,0,0,0); 0074 0075 d->edit = new QLabel(this); 0076 lyr->addWidget(d->edit); 0077 d->edit->setContentsMargins(0, 1, 0, 0); 0078 d->edit->setToolTip(tr("Click to show image preview")); 0079 d->edit->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); 0080 d->edit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); 0081 d->edit->setBackgroundRole(QPalette::Base); 0082 d->edit->setMouseTracking(true); 0083 0084 d->button = new QPushButton(this); 0085 lyr->addWidget(d->button); 0086 KPropertyUtils::setupDotDotDotButton(d->button, tr("Insert image from file"), 0087 tr("Inserts image from file")); 0088 0089 d->popup = new QLabel(nullptr, Qt::ToolTip); 0090 d->popup->setBackgroundRole(QPalette::ToolTipBase); 0091 d->popup->setForegroundRole(QPalette::ToolTipText); 0092 d->popup->setFrameStyle(QFrame::Plain | QFrame::Box); 0093 d->popup->setMargin(POPUP_MARGIN); 0094 d->popup->setLineWidth(1); 0095 d->popup->hide(); 0096 0097 setFocusProxy(d->edit); 0098 connect(d->button, SIGNAL(clicked()), this, SLOT(selectPixmap())); 0099 0100 d->edit->installEventFilter(this); 0101 installEventFilter(this); 0102 } 0103 0104 KPropertyPixmapEditor::~KPropertyPixmapEditor() 0105 { 0106 delete d; 0107 } 0108 0109 QVariant KPropertyPixmapEditor::value() const 0110 { 0111 return d->pixmap; 0112 } 0113 0114 void KPropertyPixmapEditor::setValue(const QVariant &value) 0115 { 0116 d->pixmap = value.value<QPixmap>(); 0117 if (d->pixmap.isNull() || (d->pixmap.height() <= height())) { 0118 d->previewPixmap = d->pixmap; 0119 } else { 0120 QImage img(d->pixmap.toImage()); 0121 const QSize sz(size() - QSize(0,1)); 0122 if (!QRect(QPoint(0, 0), sz).contains(d->pixmap.rect())) { 0123 img = img.scaled(sz, Qt::KeepAspectRatio, Qt::SmoothTransformation); 0124 d->previewPixmap = QPixmap::fromImage(img);//preview pixmap is a bit larger 0125 } else { 0126 d->previewPixmap = d->pixmap; 0127 } 0128 } 0129 emit commitData(this); 0130 } 0131 0132 QString KPropertyPixmapEditor::selectPixmapFileName() 0133 { 0134 /*#ifdef PURE_QT 0135 QString url = QFileDialog::getOpenFileName(); 0136 if (!url.isEmpty()) { 0137 d->edit->setPixmap(QPixmap(url)); 0138 emit valueChanged(this); 0139 } 0140 #endif*/ 0141 const QString caption( 0142 tr("Insert Image From File (for \"%1\" property)").arg(d->property->captionOrName())); 0143 /*KDE4: 0144 #ifdef Q_OS_WIN 0145 QString recentDir; 0146 QString fileName = Q3FileDialog::getOpenFileName( 0147 KFileDialog::getStartURL(":lastVisitedImagePath", recentDir).path(), 0148 convertKFileDialogFilterToQFileDialogFilter(KImageIO::pattern(KImageIO::Reading)), 0149 this, 0, caption); 0150 #else*/ 0151 const QUrl url(QFileDialog::getOpenFileUrl(this, caption)); 0152 QString fileName = url.isLocalFile() ? url.toLocalFile() : url.toString(); 0153 0154 //! @todo download the file if remote, then set fileName properly 0155 //#endif 0156 return fileName; 0157 } 0158 0159 void KPropertyPixmapEditor::selectPixmap() 0160 { 0161 const QString fileName(selectPixmapFileName()); 0162 if (fileName.isEmpty()) 0163 return; 0164 0165 QPixmap pm; 0166 if (!pm.load(fileName)) { 0167 //! @todo err msg 0168 return; 0169 } 0170 setValue(pm); 0171 0172 /* KDE4: 0173 #ifdef Q_OS_WIN 0174 //save last visited path 0175 QUrl url(fileName); 0176 if (url.isLocalFile()) 0177 KRecentDirs::add(":lastVisitedImagePath", url.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).path()); 0178 #endif 0179 */ 0180 } 0181 0182 static QRect popupGeometry(QWidget *editor, const QSize &pixmapSize) 0183 { 0184 const QRect screenRect = QApplication::desktop()->availableGeometry(editor); 0185 const QRect globalRect = QRect(editor->mapToGlobal(QPoint(0, 0)), editor->size()) & screenRect; 0186 int aboveSpace = std::min(pixmapSize.height(), globalRect.y() - screenRect.top()); 0187 int belowSpace = std::min(pixmapSize.height(), screenRect.bottom() - globalRect.bottom()); 0188 // find max area 0189 // area3 | area1 0190 // area2 | area0 0191 const QVector<int> widths{ 0192 std::min(pixmapSize.width(), screenRect.right() - globalRect.left()), 0193 std::min(pixmapSize.width(), screenRect.right() - globalRect.left()), 0194 std::min(pixmapSize.width(), globalRect.right() - screenRect.left()), 0195 std::min(pixmapSize.width(), globalRect.right() - screenRect.left()) 0196 }; 0197 const std::vector<int> areas{ widths[0] * belowSpace, widths[1] * aboveSpace, 0198 widths[2] * belowSpace, widths[3] * aboveSpace }; 0199 const int areaNumber = std::distance(areas.begin(), std::max_element(areas.begin(), areas.end())); 0200 QRect rect; 0201 switch (areaNumber) { 0202 case 0: { 0203 int width = double(belowSpace) / pixmapSize.height() * pixmapSize.width(); 0204 if (width > widths[areaNumber]) { 0205 width = widths[areaNumber]; 0206 belowSpace = double(width) / pixmapSize.width() * pixmapSize.height(); 0207 } 0208 rect = QRect(globalRect.left(), globalRect.bottom(), width, belowSpace); 0209 break; 0210 } 0211 case 1: { 0212 int width = double(aboveSpace) / pixmapSize.height() * pixmapSize.width(); 0213 if (width > widths[areaNumber]) { 0214 width = widths[areaNumber]; 0215 aboveSpace = double(width) / pixmapSize.width() * pixmapSize.height(); 0216 } 0217 rect = QRect(globalRect.left(), globalRect.top() - aboveSpace, width, aboveSpace); 0218 break; 0219 } 0220 case 2: { 0221 int width = double(belowSpace) / pixmapSize.height() * pixmapSize.width(); 0222 if (width > widths[areaNumber]) { 0223 width = widths[areaNumber]; 0224 belowSpace = double(width) / pixmapSize.width() * pixmapSize.height(); 0225 } 0226 rect = QRect(globalRect.right() - width, globalRect.bottom(), width, belowSpace); 0227 break; 0228 } 0229 case 3: { 0230 int width = double(aboveSpace) / pixmapSize.height() * pixmapSize.width(); 0231 if (width > widths[areaNumber]) { 0232 width = widths[areaNumber]; 0233 aboveSpace = double(width) / pixmapSize.width() * pixmapSize.height(); 0234 } 0235 rect = QRect(globalRect.right() - width, globalRect.top() - aboveSpace, width, 0236 aboveSpace); 0237 break; 0238 } 0239 } 0240 return rect; 0241 } 0242 0243 bool 0244 KPropertyPixmapEditor::eventFilter(QObject *o, QEvent *ev) 0245 { 0246 if (o == d->edit) { 0247 if (ev->type() == QEvent::MouseButtonPress && static_cast<QMouseEvent*>(ev)->button() == Qt::LeftButton) { 0248 if (d->pixmap.height() <= d->edit->height() && d->pixmap.width() <= d->edit->width()) { 0249 return false; // nothing to preview 0250 } 0251 d->popup->setGeometry(popupGeometry(this, d->pixmap.size())); 0252 d->popup->setPixmap(d->pixmap); 0253 d->popup->show(); 0254 } else if (ev->type() == QEvent::MouseButtonRelease || ev->type() == QEvent::Hide) { 0255 if (d->popup->isVisible()) { 0256 d->popup->hide(); 0257 } 0258 } else if (ev->type() == QEvent::KeyPress) { 0259 QKeyEvent* e = static_cast<QKeyEvent*>(ev); 0260 if ((e->key() == Qt::Key_Enter) || (e->key() == Qt::Key_Space) || (e->key() == Qt::Key_Return)) { 0261 d->button->animateClick(); 0262 return true; 0263 } 0264 } 0265 } else if (o == this) { 0266 if (ev->type() == QEvent::Resize) { 0267 d->button->setMaximumWidth(height()); 0268 } 0269 } 0270 return QWidget::eventFilter(o, ev); 0271 } 0272 0273 /* 0274 void 0275 PixmapEdit::setReadOnlyInternal(bool readOnly) 0276 { 0277 d->button->setEnabled(!readOnly); 0278 }*/ 0279 0280 //----------------------- 0281 0282 KPropertyPixmapDelegate::KPropertyPixmapDelegate() 0283 { 0284 } 0285 0286 QWidget* KPropertyPixmapDelegate::createEditor( int type, QWidget *parent, 0287 const QStyleOptionViewItem & option, const QModelIndex & index ) const 0288 { 0289 Q_UNUSED(type); 0290 Q_UNUSED(option); 0291 0292 KProperty *property = KPropertyUtils::propertyForIndex(index); 0293 if (!property) { 0294 return nullptr; 0295 } 0296 KPropertyPixmapEditor *pe = new KPropertyPixmapEditor(property, parent); 0297 return pe; 0298 } 0299 0300 void KPropertyPixmapDelegate::paint( QPainter * painter, 0301 const QStyleOptionViewItem & option, const QModelIndex & index ) const 0302 { 0303 const KPropertyUtilsPrivate::PainterSaver saver(painter); 0304 QPixmap pm( index.data(Qt::EditRole).value<QPixmap>() ); 0305 if (!pm.isNull()) { 0306 if (pm.height() > option.rect.height() || pm.width() > option.rect.width()) { //scale down 0307 QImage img(pm.toImage()); 0308 img = img.scaled(option.rect.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation); 0309 pm = QPixmap::fromImage(img); 0310 } 0311 //! @todo 0312 /* if (d->recentlyPainted != value) { 0313 d->recentlyPainted = value; 0314 d->scaledPixmap = value.value<QPixmap>(); 0315 if (d->scaledPixmap.height() > r2.height() || d->scaledPixmap.width() > r2.width()) { //scale down 0316 QImage img(d->scaledPixmap.toImage()); 0317 img = img.scaled(r2.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation); 0318 d->scaledPixmap = QPixmap::fromImage(img); 0319 } 0320 }*/ 0321 painter->drawPixmap(option.rect.topLeft().x(), 0322 option.rect.topLeft().y() + (option.rect.height() - pm.height()) / 2, pm); 0323 } 0324 QRect r(option.rect); 0325 r.setLeft(r.left() + pm.width() + 2); 0326 painter->drawText(r, valueToString(index.data(Qt::EditRole), QLocale())); 0327 } 0328 0329 QString KPropertyPixmapDelegate::valueToString(const QVariant& value, const QLocale &locale) const 0330 { 0331 const QPixmap pm(value.value<QPixmap>()); 0332 if (pm.isNull()) { 0333 if (locale.language() == QLocale::C) { 0334 return QString(); 0335 } 0336 return QObject::tr("None", "No pixmap"); 0337 } 0338 if (locale.language() == QLocale::C) { 0339 return QString::fromLatin1("%1x%2px").arg(pm.width()).arg(pm.height()); 0340 } 0341 return QObject::tr("%1x%2px").arg(locale.toString(pm.width())).arg(locale.toString(pm.height())); 0342 }