File indexing completed on 2024-04-21 04:41:01

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 }