src/declarative/graphicsitems/qdeclarativeborderimage.cpp
changeset 30 5dc02b23752f
child 33 3e2da88830cd
equal deleted inserted replaced
29:b72c6db6890b 30:5dc02b23752f
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "private/qdeclarativeborderimage_p.h"
       
    43 #include "private/qdeclarativeborderimage_p_p.h"
       
    44 
       
    45 #include <qdeclarativeinfo.h>
       
    46 #include <private/qdeclarativeengine_p.h>
       
    47 
       
    48 #include <QNetworkRequest>
       
    49 #include <QNetworkReply>
       
    50 #include <QFile>
       
    51 
       
    52 QT_BEGIN_NAMESPACE
       
    53 
       
    54 /*!
       
    55     \qmlclass BorderImage QDeclarativeBorderImage
       
    56     \brief The BorderImage element provides an image that can be used as a border.
       
    57     \inherits Item
       
    58     \since 4.7
       
    59 
       
    60     A BorderImage breaks an image into 9 sections, as shown below:
       
    61 
       
    62     \image declarative-scalegrid.png
       
    63 
       
    64     When the image is scaled:
       
    65     \list
       
    66     \i the corners (sections 1, 3, 7, and 9) are not scaled at all
       
    67     \i sections 2 and 8 are scaled according to \l{BorderImage::horizontalTileMode}{horizontalTileMode}
       
    68     \i sections 4 and 6 are scaled according to \l{BorderImage::verticalTileMode}{verticalTileMode}
       
    69     \i the middle (section 5) is scaled according to both \l{BorderImage::horizontalTileMode}{horizontalTileMode} and \l{BorderImage::verticalTileMode}{verticalTileMode}
       
    70     \endlist
       
    71 
       
    72     Examples:
       
    73     \snippet snippets/declarative/borderimage.qml 0
       
    74 
       
    75     \image BorderImage.png
       
    76 
       
    77     The \l{declarative/imageelements/borderimage}{BorderImage example} shows how a BorderImage can be used to simulate a shadow effect on a
       
    78     rectangular item.
       
    79  */
       
    80 
       
    81 /*!
       
    82     \class QDeclarativeBorderImage BorderImage
       
    83     \internal
       
    84     \brief The QDeclarativeBorderImage class provides an image item that you can add to a QDeclarativeView.
       
    85 */
       
    86 
       
    87 QDeclarativeBorderImage::QDeclarativeBorderImage(QDeclarativeItem *parent)
       
    88   : QDeclarativeImageBase(*(new QDeclarativeBorderImagePrivate), parent)
       
    89 {
       
    90 }
       
    91 
       
    92 QDeclarativeBorderImage::~QDeclarativeBorderImage()
       
    93 {
       
    94     Q_D(QDeclarativeBorderImage);
       
    95     if (d->sciReply)
       
    96         d->sciReply->deleteLater();
       
    97     if (d->sciPendingPixmapCache)
       
    98         QDeclarativePixmapCache::cancel(d->sciurl, this);
       
    99 }
       
   100 /*!
       
   101     \qmlproperty enumeration BorderImage::status
       
   102 
       
   103     This property holds the status of image loading.  It can be one of:
       
   104     \list
       
   105     \o BorderImage.Null - no image has been set
       
   106     \o BorderImage.Ready - the image has been loaded
       
   107     \o BorderImage.Loading - the image is currently being loaded
       
   108     \o BorderImage.Error - an error occurred while loading the image
       
   109     \endlist
       
   110 
       
   111     \sa progress
       
   112 */
       
   113 
       
   114 /*!
       
   115     \qmlproperty real BorderImage::progress
       
   116 
       
   117     This property holds the progress of image loading, from 0.0 (nothing loaded)
       
   118     to 1.0 (finished).
       
   119 
       
   120     \sa status
       
   121 */
       
   122 
       
   123 /*!
       
   124     \qmlproperty bool BorderImage::smooth
       
   125 
       
   126     Set this property if you want the image to be smoothly filtered when scaled or
       
   127     transformed.  Smooth filtering gives better visual quality, but is slower.  If
       
   128     the image is displayed at its natural size, this property has no visual or
       
   129     performance effect.
       
   130 
       
   131     \note Generally scaling artifacts are only visible if the image is stationary on
       
   132     the screen.  A common pattern when animating an image is to disable smooth
       
   133     filtering at the beginning of the animation and reenable it at the conclusion.
       
   134 */
       
   135 
       
   136 /*!
       
   137     \qmlproperty url BorderImage::source
       
   138 
       
   139     BorderImage can handle any image format supported by Qt, loaded from any URL scheme supported by Qt.
       
   140 
       
   141     It can also handle .sci files, which are a Qml-specific format. A .sci file uses a simple text-based format that specifies
       
   142     the borders, the image file and the tile rules.
       
   143 
       
   144     The following .sci file sets the borders to 10 on each side for the image \c picture.png:
       
   145     \qml
       
   146     border.left: 10
       
   147     border.top: 10
       
   148     border.bottom: 10
       
   149     border.right: 10
       
   150     source: picture.png
       
   151     \endqml
       
   152 
       
   153     The URL may be absolute, or relative to the URL of the component.
       
   154 */
       
   155 void QDeclarativeBorderImage::setSource(const QUrl &url)
       
   156 {
       
   157     Q_D(QDeclarativeBorderImage);
       
   158     //equality is fairly expensive, so we bypass for simple, common case
       
   159     if ((d->url.isEmpty() == url.isEmpty()) && url == d->url)
       
   160         return;
       
   161 
       
   162     if (d->sciReply) {
       
   163         d->sciReply->deleteLater();
       
   164         d->sciReply = 0;
       
   165     }
       
   166 
       
   167     if (d->pendingPixmapCache) {
       
   168         QDeclarativePixmapCache::cancel(d->url, this);
       
   169         d->pendingPixmapCache = false;
       
   170     }
       
   171     if (d->sciPendingPixmapCache) {
       
   172         QDeclarativePixmapCache::cancel(d->sciurl, this);
       
   173         d->sciPendingPixmapCache = false;
       
   174     }
       
   175 
       
   176     d->url = url;
       
   177     d->sciurl = QUrl();
       
   178     emit sourceChanged(d->url);
       
   179 
       
   180     if (isComponentComplete())
       
   181         load();
       
   182 }
       
   183 
       
   184 void QDeclarativeBorderImage::load()
       
   185 {
       
   186     Q_D(QDeclarativeBorderImage);
       
   187     if (d->progress != 0.0) {
       
   188         d->progress = 0.0;
       
   189         emit progressChanged(d->progress);
       
   190     }
       
   191 
       
   192     if (d->url.isEmpty()) {
       
   193         d->pix = QPixmap();
       
   194         d->status = Null;
       
   195         setImplicitWidth(0);
       
   196         setImplicitHeight(0);
       
   197         emit statusChanged(d->status);
       
   198         update();
       
   199     } else {
       
   200         d->status = Loading;
       
   201         if (d->url.path().endsWith(QLatin1String("sci"))) {
       
   202 #ifndef QT_NO_LOCALFILE_OPTIMIZED_QML
       
   203             QString lf = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(d->url);
       
   204             if (!lf.isEmpty()) {
       
   205                 QFile file(lf);
       
   206                 file.open(QIODevice::ReadOnly);
       
   207                 setGridScaledImage(QDeclarativeGridScaledImage(&file));
       
   208             } else
       
   209 #endif
       
   210             {
       
   211                 QNetworkRequest req(d->url);
       
   212                 d->sciReply = qmlEngine(this)->networkAccessManager()->get(req);
       
   213 
       
   214                 static int sciReplyFinished = -1;
       
   215                 static int thisSciRequestFinished = -1;
       
   216                 if (sciReplyFinished == -1) {
       
   217                     sciReplyFinished =
       
   218                         QNetworkReply::staticMetaObject.indexOfSignal("finished()");
       
   219                     thisSciRequestFinished =
       
   220                         QDeclarativeBorderImage::staticMetaObject.indexOfSlot("sciRequestFinished()");
       
   221                 }
       
   222 
       
   223                 QMetaObject::connect(d->sciReply, sciReplyFinished, this,
       
   224                                      thisSciRequestFinished, Qt::DirectConnection);
       
   225             }
       
   226         } else {
       
   227             QSize impsize;
       
   228             QString errorString;
       
   229             QDeclarativePixmapReply::Status status = QDeclarativePixmapCache::get(d->url, &d->pix, &errorString, &impsize, d->async);
       
   230             if (status != QDeclarativePixmapReply::Ready && status != QDeclarativePixmapReply::Error) {
       
   231                 QDeclarativePixmapReply *reply = QDeclarativePixmapCache::request(qmlEngine(this), d->url);
       
   232                 d->pendingPixmapCache = true;
       
   233                 connect(reply, SIGNAL(finished()), this, SLOT(requestFinished()));
       
   234                 connect(reply, SIGNAL(downloadProgress(qint64,qint64)),
       
   235                         this, SLOT(requestProgress(qint64,qint64)));
       
   236             } else {
       
   237                 //### should be unified with requestFinished
       
   238                 setImplicitWidth(impsize.width());
       
   239                 setImplicitHeight(impsize.height());
       
   240 
       
   241                 if (d->pix.isNull()) {
       
   242                     d->status = Error;
       
   243                     qmlInfo(this) << errorString;
       
   244                 }
       
   245                 if (d->status == Loading)
       
   246                     d->status = Ready;
       
   247                 d->progress = 1.0;
       
   248                 emit statusChanged(d->status);
       
   249                 emit progressChanged(d->progress);
       
   250                 update();
       
   251             }
       
   252         }
       
   253     }
       
   254 
       
   255     emit statusChanged(d->status);
       
   256 }
       
   257 
       
   258 /*!
       
   259     \qmlproperty int BorderImage::border.left
       
   260     \qmlproperty int BorderImage::border.right
       
   261     \qmlproperty int BorderImage::border.top
       
   262     \qmlproperty int BorderImage::border.bottom
       
   263 
       
   264     The 4 border lines (2 horizontal and 2 vertical) break the image into 9 sections, as shown below:
       
   265 
       
   266     \image declarative-scalegrid.png
       
   267 
       
   268     Each border line (left, right, top, and bottom) specifies an offset in pixels from the respective side.
       
   269 
       
   270     For example:
       
   271     \qml
       
   272     border.bottom: 10
       
   273     \endqml
       
   274     sets the bottom line 10 pixels up from the bottom of the image.
       
   275 
       
   276     The border lines can also be specified using a
       
   277     \l {BorderImage::source}{.sci file}.
       
   278 */
       
   279 
       
   280 QDeclarativeScaleGrid *QDeclarativeBorderImage::border()
       
   281 {
       
   282     Q_D(QDeclarativeBorderImage);
       
   283     return d->getScaleGrid();
       
   284 }
       
   285 
       
   286 /*!
       
   287     \qmlproperty enumeration BorderImage::horizontalTileMode
       
   288     \qmlproperty enumeration BorderImage::verticalTileMode
       
   289 
       
   290     This property describes how to repeat or stretch the middle parts of the border image.
       
   291 
       
   292     \list
       
   293     \o BorderImage.Stretch - Scale the image to fit to the available area.
       
   294     \o BorderImage.Repeat - Tile the image until there is no more space. May crop the last image.
       
   295     \o BorderImage.Round - Like Repeat, but scales the images down to ensure that the last image is not cropped.
       
   296     \endlist
       
   297 */
       
   298 QDeclarativeBorderImage::TileMode QDeclarativeBorderImage::horizontalTileMode() const
       
   299 {
       
   300     Q_D(const QDeclarativeBorderImage);
       
   301     return d->horizontalTileMode;
       
   302 }
       
   303 
       
   304 void QDeclarativeBorderImage::setHorizontalTileMode(TileMode t)
       
   305 {
       
   306     Q_D(QDeclarativeBorderImage);
       
   307     if (t != d->horizontalTileMode) {
       
   308         d->horizontalTileMode = t;
       
   309         emit horizontalTileModeChanged();
       
   310         update();
       
   311     }
       
   312 }
       
   313 
       
   314 QDeclarativeBorderImage::TileMode QDeclarativeBorderImage::verticalTileMode() const
       
   315 {
       
   316     Q_D(const QDeclarativeBorderImage);
       
   317     return d->verticalTileMode;
       
   318 }
       
   319 
       
   320 void QDeclarativeBorderImage::setVerticalTileMode(TileMode t)
       
   321 {
       
   322     Q_D(QDeclarativeBorderImage);
       
   323     if (t != d->verticalTileMode) {
       
   324         d->verticalTileMode = t;
       
   325         emit verticalTileModeChanged();
       
   326         update();
       
   327     }
       
   328 }
       
   329 
       
   330 void QDeclarativeBorderImage::setGridScaledImage(const QDeclarativeGridScaledImage& sci)
       
   331 {
       
   332     Q_D(QDeclarativeBorderImage);
       
   333     if (!sci.isValid()) {
       
   334         d->status = Error;
       
   335         emit statusChanged(d->status);
       
   336     } else {
       
   337         QDeclarativeScaleGrid *sg = border();
       
   338         sg->setTop(sci.gridTop());
       
   339         sg->setBottom(sci.gridBottom());
       
   340         sg->setLeft(sci.gridLeft());
       
   341         sg->setRight(sci.gridRight());
       
   342         d->horizontalTileMode = sci.horizontalTileRule();
       
   343         d->verticalTileMode = sci.verticalTileRule();
       
   344 
       
   345         d->sciurl = d->url.resolved(QUrl(sci.pixmapUrl()));
       
   346         QSize impsize;
       
   347         QString errorString;
       
   348         QDeclarativePixmapReply::Status status = QDeclarativePixmapCache::get(d->sciurl, &d->pix, &errorString, &impsize, d->async);
       
   349         if (status != QDeclarativePixmapReply::Ready && status != QDeclarativePixmapReply::Error) {
       
   350             QDeclarativePixmapReply *reply = QDeclarativePixmapCache::request(qmlEngine(this), d->sciurl);
       
   351             d->sciPendingPixmapCache = true;
       
   352 
       
   353             static int replyDownloadProgress = -1;
       
   354             static int replyFinished = -1;
       
   355             static int thisRequestProgress = -1;
       
   356             static int thisRequestFinished = -1;
       
   357             if (replyDownloadProgress == -1) {
       
   358                 replyDownloadProgress =
       
   359                     QDeclarativePixmapReply::staticMetaObject.indexOfSignal("downloadProgress(qint64,qint64)");
       
   360                 replyFinished =
       
   361                     QDeclarativePixmapReply::staticMetaObject.indexOfSignal("finished()");
       
   362                 thisRequestProgress =
       
   363                     QDeclarativeBorderImage::staticMetaObject.indexOfSlot("requestProgress(qint64,qint64)");
       
   364                 thisRequestFinished =
       
   365                     QDeclarativeBorderImage::staticMetaObject.indexOfSlot("requestFinished()");
       
   366             }
       
   367 
       
   368             QMetaObject::connect(reply, replyFinished, this,
       
   369                                  thisRequestFinished, Qt::DirectConnection);
       
   370             QMetaObject::connect(reply, replyDownloadProgress, this,
       
   371                                  thisRequestProgress, Qt::DirectConnection);
       
   372         } else {
       
   373             //### should be unified with requestFinished
       
   374             setImplicitWidth(impsize.width());
       
   375             setImplicitHeight(impsize.height());
       
   376 
       
   377             if (d->pix.isNull()) {
       
   378                 d->status = Error;
       
   379                 qmlInfo(this) << errorString;
       
   380             }
       
   381             if (d->status == Loading)
       
   382                 d->status = Ready;
       
   383             d->progress = 1.0;
       
   384             emit statusChanged(d->status);
       
   385             emit progressChanged(1.0);
       
   386             update();
       
   387         }
       
   388     }
       
   389 }
       
   390 
       
   391 void QDeclarativeBorderImage::requestFinished()
       
   392 {
       
   393     Q_D(QDeclarativeBorderImage);
       
   394 
       
   395     QSize impsize;
       
   396     if (d->url.path().endsWith(QLatin1String(".sci"))) {
       
   397         d->sciPendingPixmapCache = false;
       
   398         QString errorString;
       
   399         if (QDeclarativePixmapCache::get(d->sciurl, &d->pix, &errorString, &impsize, d->async) != QDeclarativePixmapReply::Ready) {
       
   400             d->status = Error;
       
   401             qmlInfo(this) << errorString;
       
   402         }
       
   403     } else {
       
   404         d->pendingPixmapCache = false;
       
   405         QString errorString;
       
   406         if (QDeclarativePixmapCache::get(d->url, &d->pix, &errorString, &impsize, d->async) != QDeclarativePixmapReply::Ready) {
       
   407             d->status = Error;
       
   408             qmlInfo(this) << errorString;
       
   409         }
       
   410     }
       
   411     setImplicitWidth(impsize.width());
       
   412     setImplicitHeight(impsize.height());
       
   413 
       
   414     if (d->status == Loading)
       
   415         d->status = Ready;
       
   416     d->progress = 1.0;
       
   417     emit statusChanged(d->status);
       
   418     emit progressChanged(1.0);
       
   419     update();
       
   420 }
       
   421 
       
   422 #define BORDERIMAGE_MAX_REDIRECT 16
       
   423 
       
   424 void QDeclarativeBorderImage::sciRequestFinished()
       
   425 {
       
   426     Q_D(QDeclarativeBorderImage);
       
   427 
       
   428     d->redirectCount++;
       
   429     if (d->redirectCount < BORDERIMAGE_MAX_REDIRECT) {
       
   430         QVariant redirect = d->sciReply->attribute(QNetworkRequest::RedirectionTargetAttribute);
       
   431         if (redirect.isValid()) {
       
   432             QUrl url = d->sciReply->url().resolved(redirect.toUrl());
       
   433             setSource(url);
       
   434             return;
       
   435         }
       
   436     }
       
   437     d->redirectCount=0;
       
   438 
       
   439     if (d->sciReply->error() != QNetworkReply::NoError) {
       
   440         d->status = Error;
       
   441         d->sciReply->deleteLater();
       
   442         d->sciReply = 0;
       
   443         emit statusChanged(d->status);
       
   444     } else {
       
   445         QDeclarativeGridScaledImage sci(d->sciReply);
       
   446         d->sciReply->deleteLater();
       
   447         d->sciReply = 0;
       
   448         setGridScaledImage(sci);
       
   449     }
       
   450 }
       
   451 
       
   452 void QDeclarativeBorderImage::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *)
       
   453 {
       
   454     Q_D(QDeclarativeBorderImage);
       
   455     if (d->pix.isNull())
       
   456         return;
       
   457 
       
   458     bool oldAA = p->testRenderHint(QPainter::Antialiasing);
       
   459     bool oldSmooth = p->testRenderHint(QPainter::SmoothPixmapTransform);
       
   460     if (d->smooth)
       
   461         p->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, d->smooth);
       
   462 
       
   463     const QDeclarativeScaleGrid *border = d->getScaleGrid();
       
   464     QMargins margins(border->left(), border->top(), border->right(), border->bottom());
       
   465     QTileRules rules((Qt::TileRule)d->horizontalTileMode, (Qt::TileRule)d->verticalTileMode);
       
   466     qDrawBorderPixmap(p, QRect(0, 0, (int)d->width(), (int)d->height()), margins, d->pix, d->pix.rect(), margins, rules);
       
   467     if (d->smooth) {
       
   468         p->setRenderHint(QPainter::Antialiasing, oldAA);
       
   469         p->setRenderHint(QPainter::SmoothPixmapTransform, oldSmooth);
       
   470     }
       
   471 }
       
   472 
       
   473 QT_END_NAMESPACE