I have made the following changes intended for :
  CE:Apps / qmlgallery

Please review and accept or decline.
BOSS has already run some checks on this request.
See the "Messages from BOSS" section below.

https://build.pub.meego.com//request/show/8290

Thank You,
Robin Burchell

[This message was auto-generated]

---

Request # 8290:

Messages from BOSS:

State: review at 2013-03-03T00:41:26 by bossbot

Reviews:
       accepted by bossbot : Prechecks succeeded.
       new for CE-maintainers : Please replace this text with a review and 
approve/reject the review (not the SR). BOSS will take care of the rest

Changes:
  submit: home:w00t:branches:CE:Apps / qmlgallery -> CE:Apps / qmlgallery
  
changes files:
--------------
--- qmlgallery.changes
+++ qmlgallery.changes
@@ -0,0 +1,17 @@
+* Sun Mar 03 2013 Robin Burchell <[email protected]> - 0.1.0
+- Fixes NEMO#498: Slideshow mode is greyed out if there are no elements (from 
Andrea)
+- Fixes NEMO#402: Exiting slideshow mode doesn't change the index of the 
displayed element (from Andrea)
+- Fixes NEMO#397: Fullscreen mode can now be enabled/disabled in all cases 
(from Andrea)
+- Fixes NEMO#196: gallery handles external images/videos (from Andrea)
+- Fixes NEMO#404: Added doubletap to zoom (from Andrea)
+- Fixes NEMO#218: Toolbar and statusbar now autohide (from Andrea)
+- Refactor pinch zoom logic (from Andrea)
+- Remove useless Item (from Andrea)
+- Restructure mouse handling (from Andrea)
+- Allow ImageContainer to work without the gallery model (from Andrea)
+- Don't resize the image when toggling fullscreen mode (from Andrea)
+- Make toolbar semi-transparent (from Andrea)
+- Add packaging to git repository (from Robin)
+- Use unified empty state view (from Robin)
+- Tidy up packaging a bit (from Robin)
+

old:
----
  qmlgallery-0.0.10.tar.bz2

new:
----
  qmlgallery-0.1.0.tar.bz2

spec files:
-----------
--- qmlgallery.spec
+++ qmlgallery.spec
@@ -9,13 +9,14 @@
 # << macros
 
 Summary:    Photo Gallery for Nemo
-Version:    0.0.10
+Version:    0.1.0
 Release:    1
 Group:      Applications/System
 License:    BSD
 URL:        https://github.com/nemomobile/qmlgallery
 Source0:    %{name}-%{version}.tar.bz2
 Source100:  qmlgallery.yaml
+Requires:   qt-components >= 1.4.8
 Requires:   libdeclarative-gallery
 Requires:   libdeclarative-multimedia
 Requires:   nemo-qml-plugins-thumbnailer
@@ -43,12 +44,7 @@
 # >> build pre
 # << build pre
 
-%qmake  \
-    MEEGO_VERSION_MAJOR=1 \
-    MEEGO_VERSION_MINOR=2 \
-    MEEGO_VERSION_PATCH=0 \
-    MEEGO_EDITION=harmattan \
-    DEFINES+=MEEGO_EDITION_HARMATTAN
+%qmake 
 
 make %{?jobs:-j%jobs}
 

other changes:
--------------

++++++ qmlgallery-0.0.10.tar.bz2 -> qmlgallery-0.1.0.tar.bz2
--- qml/ImageContainer.qml
+++ qml/ImageContainer.qml
@@ -36,219 +36,111 @@
 Item {
     id: imgContainer
     property int index: -1
-    property variant imgController: imageController
-    property bool isVideo: galleryModel.isVideo(index)
-
-    //used inside ImagePage's imgFlickable to get the bounding rectangle of 
the image
+    property variant pinchingController
+    property variant pageStack
+    property string imageSource: ""
+    property string videoSource: ""
+    property bool isVideo: false
+    property alias flickableArea: flickImg
+    property int doubleClickInterval: 350
     property alias image: img
-
-    width: imgController.imgContainerWidth
-    height: imgController.imgContainerHeight
-
-    function resetZoom() {
-        //resetting all variables related to pinch-to-zoom
-        img.scale = 1
-        flickImg.contentX = flickImg.contentY = 0
-        pinchImg.lastContentX = pinchImg.lastContentY = pinchImg.deltaX = 
pinchImg.deltaY = 0
-        pinchImg.lastScaleX = pinchImg.lastScaleY = 1
-        pinchImg.isZoomingOut = false
+    property int videoThumbnailSize: 480
+    //this is to trick the statusBar, so that when it shows the imageContainer 
isn't moved downwards
+    y: -(height - parent.height - appWindow.pageStack.toolBar.height)
+
+    //this long ternary conditional expression is to make so that the size is 
not changed before the screen rotates.
+    //i.e. if you just use screen.platformHeight/Width the container will 
resize BEFORE the orientation change
+    //animation is started, thus causing an unexpected behaviour
+    width: (parent.width > parent.height) ?
+               ((screen.platformWidth > screen.platformHeight) ? 
screen.platformWidth : screen.platformHeight) :
+               ((screen.platformWidth > screen.platformHeight) ? 
screen.platformHeight : screen.platformWidth)
+    height: (parent.width > parent.height) ?
+                ((screen.platformWidth > screen.platformHeight) ? 
screen.platformHeight : screen.platformWidth) :
+                ((screen.platformWidth > screen.platformHeight) ? 
screen.platformWidth : screen.platformHeight)
+
+    signal clickedWhileZoomed()
+    signal pressedWhileNotZoomed()
+
+    Timer {
+        id: doubleClickTimer
+        interval: doubleClickInterval
     }
 
-    PinchArea {
-        id: pinchImg
-        anchors.fill: imgContainer
-
-        //Disable the pincharea if the listview is scrolling, to avoid problems
-        enabled: (!imgController.moving && !isVideo)
-        pinch.target: img
-        pinch.maximumScale: 5
-        pinch.dragAxis: Pinch.NoDrag
-
-        property real lastContentX: 0
-        property real lastContentY: 0
-        property real lastScaleX: 1
-        property real lastScaleY: 1
-        property real deltaX: 0
-        property real deltaY: 0
-        property bool initializedX: false
-        property bool initializedY: false
-        property bool isZoomingOut: false
-
-
-        function updateContentX() {
-
-            //Only calculate the correct ContentX if the image is wider than 
the screen, otherwise keep it centered (contentX = 0 in the else branch)
-            if (rect.width == imgController.width) {
-
-                //Anchors the image to the left
-                if (flickImg.contentX < 0){
-                    deltaX = 0.0
-                    lastContentX = 0.0
-                    flickImg.contentX = 0.0
-                }
-                else {
-                    //if the right end of the image is inside the screen area, 
lock it to the right and zoom out using right edge as an anchor
-                    if ((flickImg.contentWidth - flickImg.contentX < 
parent.width) && isZoomingOut) {
-
-                        //align to the right
-                        flickImg.contentX -= parent.width - 
(flickImg.contentWidth - flickImg.contentX)
-
-                        //Algo: set variable as if a new pinch starting from 
right edge were triggered
-                        lastContentX = flickImg.contentX
-                        deltaX = flickImg.contentX + parent.width
-                        lastScaleX = img.scale
-                    }
-
-                    flickImg.contentX = (lastContentX + deltaX * ((img.scale / 
lastScaleX) - 1.0 ))
-                }
-            }
-            else {
-                flickImg.contentX = 0
-            }
-        }
-
-        function updateContentY() {
+    Flickable {
+        id: flickImg
 
-            //Only calculate the correct ContentY if the image is taller than 
the screen, otherwise keep it centered (contentY = 0 in the else branch)
-            if (rect.height == imgController.height) {
+        interactive: img.scale > 1
 
-                //Anchors the image to the top when zooming out
-                if (flickImg.contentY < 0) {
-                    deltaY = 0.0
-                    lastContentY = 0.0
-                    flickImg.contentY = 0.0
-                }
-                else {
-                    //if the bottom end of the image is inside the screen 
area, lock it to the bottom and zoom out using bottom edge as an anchor
-                    if ((flickImg.contentHeight - flickImg.contentY < 
parent.height) && isZoomingOut) {
-                        //align to the bottom
-                        flickImg.contentY -= parent.height - 
(flickImg.contentHeight - flickImg.contentY)
-
-                        //Algo: set variable as if a new pinch starting from 
bottom edge were triggered
-                        lastContentY = flickImg.contentY
-                        deltaY = flickImg.contentY + parent.height
-                        lastScaleY = img.scale
-                    }
-                    flickImg.contentY = (lastContentY + deltaY * ((img.scale / 
lastScaleY) - 1.0 ))
-                }
-            }
-            else {
-                flickImg.contentY = 0
-            }
-        }
-
-
-        onPinchUpdated: {
-            //Am I zooming in or out?
-            if (pinch.scale > pinch.previousScale) isZoomingOut = false
-            else isZoomingOut = true
-
-            //Get updated "zoom center point" values when the image is 
completely zoomed out
-            if(img.scale == 1) {
-                //This is so that everytime you zoom out, the new zoom is 
started with updated values
-                initializedX = false
-                initializedY = false
-            }
+        anchors.centerIn: parent
+        width: Math.min(img.width*img.scale, imgContainer.width)
+        height: Math.min(img.height*img.scale, imgContainer.height)
 
-            //i.e. everytime the image is wider than the screen, it should 
actually be
-            // img.width == imgController.width, but this condition is rarely 
met because of numeric error
-            if (rect.width == imgController.width) {
-                if (!initializedX ) {
-                    //If it has not already been set by the "if (height == 
parent.imgController.height)" branch, set the scale here
-                    lastScaleX = img.scale
-
-                    lastContentX = flickImg.contentX
-                    deltaX = flickImg.contentX + pinch.center.x
-                    initializedX = true;
-                }
+        transformOrigin: Item.TopLeft
 
-            }
+        contentWidth: img.width * img.scale
+        contentHeight: img.height * img.scale
 
-            if (rect.height == imgController.height) {
-                if (!initializedY) {
-                    //If it has not already been set by the "if (width == 
imgController.width)", set the scale here
-                    lastScaleY = img.scale
-
-                    lastContentY = flickImg.contentY
-                    deltaY = flickImg.contentY + pinch.center.y
-                    initializedY = true;
-                }
+        onContentWidthChanged: {
+            //this check is because the first time this slot is called, 
pinchingController isn't set yet
+            if (pinchingController && pinchingController.pinch.active) {
+                pinchingController.updateContentX()
+                pinchingController.updateContentY()
             }
-            // updateContentX and updateContentY are called after the scale on 
the target item updates bindings
         }
-
-        onPinchFinished: {
-            lastContentX = flickImg.contentX
-            lastContentY = flickImg.contentY
-
-            initializedX = false
-            initializedY = false
+        onContentHeightChanged: {
+            if (pinchingController && pinchingController.pinch.active) {
+                pinchingController.updateContentX()
+                pinchingController.updateContentY()
+            }
         }
 
-    }
-
-    Item {
-        id: rect
-        anchors.centerIn: parent
-
-        width: Math.min(img.width*img.scale, parent.width)
-        height: Math.min(img.height*img.scale, parent.height)
-
-        Flickable {
-            id: flickImg
+        Image {
+            id: img
+            width: (fitsVertically) ? (imgContainer.height * imgRatio) : 
imgContainer.width
+            height: (fitsVertically) ? (imgContainer.height) : 
(imgContainer.width / imgRatio)
+
+            //(isVideo --> imgRatio = 1.0) because we're using a square dummy 
thumbnail
+            property real imgRatio: isVideo ? 1.0 : implicitWidth / 
implicitHeight
+            property bool fitsVertically: imgRatio < (imgContainer.width / 
imgContainer.height)
 
-            anchors.fill: rect
             transformOrigin: Item.TopLeft
+            asynchronous: true
+            source: isVideo ? "qrc:/images/DefaultVideoThumbnail.jpg" : 
imageSource
+            sourceSize.width: 1200
+
+            MouseArea {
+                anchors.fill: parent
+
+                //this is only called when img.scale != 1
+                //----> WARNING!!!: this causes a problem! double-press will 
call the zoomIn/Out function, while only
+                //doubleCLICK will stop the toolbarTimer in ImagePage! So if 
you double-press (and keep it pressed)
+                //both the zoomIn/Out function will be called and the toolBar 
in ImagePage will show/hide!!
+                //TODO: look for a way to fix this
+                onClicked: {
+                    imgContainer.clickedWhileZoomed()
+                }
+
+                onPressed: {
+                    //setting mouse.accepted to false means we'll only receive 
the onPressed of this event,
+                    //the rest will be propagated to the area underneath
+                    //if img.scale == 1 send events underneath, otherwise emit 
the signal (to make it
+                    //propagate beyond the Flickable parent)
+                    if (img.scale == 1) {
+                        imgContainer.pressedWhileNotZoomed()
+                        mouse.accepted = false
+                    }
 
-            contentWidth: img.width * img.scale
-            contentHeight: img.height * img.scale
-
-            onContentWidthChanged: {
-                pinchImg.updateContentX()
-                pinchImg.updateContentY()
-            }
-            onContentHeightChanged: {
-                pinchImg.updateContentX()
-                pinchImg.updateContentY()
-            }
-
-            Image {
-                id: img
-                width: (fitsVertically) ? (imgController.height * imgRatio) : 
imgController.width
-                height: (fitsVertically) ? (imgController.height) : 
(imgController.width / imgRatio)
-
-                property int imgWidth: isVideo ? videoThumbnailSize : 
(info.available ? info.metaData.width : -1)
-                property int imgHeight: isVideo ? videoThumbnailSize : 
(info.available ? info.metaData.height : -1)
-                property real imgRatio: imgWidth / imgHeight
-                property bool fitsVertically: imgRatio < (imgContainer.width / 
imgContainer.height)
-
-                //DocumentGalleryItem automatically recognizes the rootType of 
the file
-                DocumentGalleryItem {
-                    id: info
-                    item: galleryModel.get(index).itemId
-                    autoUpdate: true
-                    properties: ["width", "height", "url"]
-                }
-
-                transformOrigin: Item.TopLeft
-                asynchronous: true
-                source: isVideo ? "qrc:/images/DefaultVideoThumbnail.jpg" : 
galleryModel.get(index).url
-                sourceSize.width: 1200
-
-                //Disable ListView scrolling if you're zooming
-                onScaleChanged: {
-                    if (scale != 1 && imgController.flickAreaEnabled == true) 
imgController.flickAreaEnabled = false
-                    else if (scale == 1 && imgController.flickAreaEnabled == 
false) imgController.flickAreaEnabled = true
-                }
-
-                MouseArea {
-                    anchors.fill: parent
-
-                    onClicked: {
-                        if (!isVideo) {
-                            if (imgController.doubleClickTimer.running) 
resetZoom()
-                            else imgController.doubleClickTimer.start()
+                    if (!isVideo) {
+                        if (doubleClickTimer.running) {
+                            if (img.scale != 1) {
+                                pinchingController.resetZoom()
+                            }
+                            else {
+                                var point = mapToItem(imgContainer, mouse.x, 
mouse.y)
+                                pinchingController.quickZoomIn(point.x, 
point.y, 2.5)
+                            }
                         }
+                        else doubleClickTimer.start()
                     }
                 }
             }
--- qml/ImagePage.qml
+++ qml/ImagePage.qml
@@ -37,15 +37,10 @@
     id: imageController
     anchors.fill: parent
 
-    clip: true
     tools: imgTools
+    clip: true
 
-    property int imgContainerWidth: width
-    property int imgContainerHeight: height
     property variant galleryModel
-    // XXX: This is not actually the visible index; it's the index shown when
-    // loading the page. Use middle.index instead. Should be refactored.
-    property int visibleIndex: 0
     property real firstPressX
     property real pressX
     property int flickToX: 0
@@ -56,14 +51,32 @@
     property variant middle: three
     property variant rightMiddle: four
     property variant rightMost: five
+    property bool videoPlayerRequested: false
+
+    //this is the index which has to be passed as a parameter when creating 
this page
+    //it will only be used for initialization
+    property int parameterIndex
+
+    property int visibleIndex
+
+    Component.onCompleted: {
+        //this is to make so that when visibleIndex is changed the image 
containers have already been
+        //initialized
+        visibleIndex = parameterIndex
+        updateImagesIndexes()
+    }
+
+    //this will make so that every time visibleIndex is changed, the image 
containers will all load
+    //the correct images.
+    //visibleIndex is the reliable source to know what index is currently 
being displayed on screen
+    onVisibleIndexChanged: {
+        updateImagesIndexes()
+    }
+
     property real swipeThreshold: 40
     property real leftMostOptimalX: -width*2
     //number of pixel you have to move before the Pinch Area is disabled
     property real pinchThreshold: 3
-    property alias flickAreaEnabled: imgFlickable.enabled
-    property variant doubleClickTimer: timer
-    property int doubleClickInterval: 350
-    property int videoThumbnailSize: 480
     //This property forces the middle item to be visible on screen by keeping 
the leftMost item at x = leftMostOptimalX
     //You have to set it to FALSE when you want to want to modify leftMost's x 
property, and set it back to true
     //to be sure that the middle item will be the one centered on screen.
@@ -72,13 +85,15 @@
 
     onWidthChanged: {
         if (!middle.isVideo)
-            middle.resetZoom()
+            pinchImg.resetZoom()
     }
 
-    function showVideoPlayer() {
-        appWindow.pageStack.push(Qt.resolvedUrl("VideoPlayer.qml"),
-                                 {videoSource: 
galleryModel.get(middle.index).url},
-                                 true)
+    function updateImagesIndexes() {
+        leftMost.index = modulus(visibleIndex - 2, galleryModel.count);
+        leftMiddle.index = modulus(visibleIndex - 1, galleryModel.count);
+        middle.index = modulus(visibleIndex, galleryModel.count);
+        rightMiddle.index = modulus(visibleIndex + 1, galleryModel.count);
+        rightMost.index = modulus(visibleIndex + 2, galleryModel.count);
     }
 
     function modulus(a, b) {
@@ -86,10 +101,10 @@
         else return a % b
     }
 
-    function isInside(x, y, rect) {
-        var rectAbsolute = rect.mapToItem(imageController, rect.x, rect.y)
-        return ((x > rectAbsolute.x) && (x < rectAbsolute.x + rect.width) &&
-                (y > rectAbsolute.y) && (y < rectAbsolute.y + rect.height))
+    function showVideoPlayer(fileName) {
+        pageStack.push(Qt.resolvedUrl("VideoPlayer.qml"),
+                       {videoSource: fileName},
+                       true)
     }
 
     function swapLeftMost() {
@@ -104,8 +119,8 @@
         middle = rightMiddle
         rightMiddle = rightMost
         rightMost = oldLeftMost
-        //set the index (relative to galleryModel) of the image which has to 
be loaded by the shifted image container
-        rightMost.index = modulus(middle.index + 2, galleryModel.count);
+
+        visibleIndex = middle.index;
     }
 
     function swapRightMost() {
@@ -119,7 +134,9 @@
         middle = leftMiddle
         leftMiddle = leftMost
         leftMost = oldRightMost
-        leftMost.index = modulus(middle.index - 2, galleryModel.count);
+
+        visibleIndex = middle.index;
+
     }
 
     NumberAnimation {
@@ -154,62 +171,58 @@
         when: keepMiddleItemAligned
     }
 
-    ImageContainer {
-        id: one;
-        index: modulus(visibleIndex - 2, galleryModel.count)
-        x: leftMostOptimalX
-    }
-
-    ImageContainer {
-        id: two
-        anchors.left: one.right
-        index: modulus(visibleIndex - 1, galleryModel.count)
-    }
+    ZoomController {
+        id: pinchImg
 
-    ImageContainer {
-        id: three
-        anchors.left: two.right
-        index: visibleIndex
-    }
+        //Disable the pincharea if the listview is scrolling, to avoid problems
+        enabled: (!imageController.moving && !middle.isVideo)
 
-    ImageContainer {
-        id: four
-        anchors.left: three.right
-        index: modulus (visibleIndex + 1, galleryModel.count)
+        pinchTarget: middle.image
+        connectedFlickable: middle.flickableArea
+        targetContainer: middle
     }
 
-    ImageContainer {
-        id: five
-        anchors.left: four.right
-        index: modulus (visibleIndex + 2, galleryModel.count)
+    Connections {
+        target: middle
+        onClickedWhileZoomed: listFlickable.handleClick()
+        onPressedWhileNotZoomed: if (middle.isVideo) videoPlayerRequested = 
true
     }
 
     Timer {
-        id: timer
-        interval: doubleClickInterval
+        id: toolbarAutohideTimer
+        interval: 2500
+        running: !appWindow.fullscreen && (pageMenu.status === 
DialogStatus.Closed)
+        onTriggered: appWindow.fullscreen = true
     }
 
     MouseArea {
-        id: imgFlickable
+        id: listFlickable
         anchors.fill: parent
 
         property bool pressedForClick: false
 
-        //HACK: this mousarea is disabled when the image is zoomed, and the 
mousearea inside the imageContainer's img is disabled when zoom factor is 1,
-        //so we have to get the doubleClick event here when the zoom factor is 
1, and inside imageContainer's img when the image is zoomed.
-        //This is due to the high number of mouse areas (pincharea, flickable, 
multiple mouseareas) available in the same view
-        //Adding another MouseArea to handle this only made things worse
-        //comment added by faenil
-        onClicked: {
-            if (!imageController.moving && isInside(mouse.x, mouse.y, 
middle.image)) {
-                if (!middle.isVideo) {
-                    if (doubleClickTimer.running) {}  //TODO: IMPLEMENT 
ZOOM-IN VIA DOUBLECLICK INSIDE THE CURLY BRACKETS
-                    else doubleClickTimer.start()
+        function handleClick() {
+            if (videoPlayerRequested) {
+                videoPlayerRequested = false
+                imageController.showVideoPlayer(middle.videoSource)
+            }
+            else {
+
+                if (toolbarTimer.running) {
+                    toolbarTimer.stop()
+                } else {
+                    toolbarTimer.start()
                 }
-                else showVideoPlayer()
             }
         }
 
+        //we use this to be able to not call singleclick handlers when the 
user is actually doubleclicking
+        Timer {
+            id: toolbarTimer
+            interval: 350
+            onTriggered: appWindow.fullscreen = !appWindow.fullscreen
+        }
+
         onPressed: {
             firstPressX = mouseX
             pressX = mouseX
@@ -226,7 +239,7 @@
                 pressedForClick = false
             }
 
-            //Only move the image if we're sure the user isn't trying to pinch
+            //moving == true means the user isn't trying to pinch
             if (moving) {
                 leftMost.x = leftMost.x - (pressX - mouseX)
                 pressX = mouseX
@@ -234,38 +247,87 @@
         }
 
         onReleased: {
+            if (pressedForClick) {
+                handleClick()
+                pressedForClick = false
+            }
+
             if (middle.x >= swipeThreshold) {
                 //move it left
-                flickToX = leftMostOptimalX + imgContainerWidth
+                flickToX = leftMostOptimalX + parent.width
             }
             else if (middle.x <= -swipeThreshold) {
                 //move it right
-                flickToX = leftMostOptimalX -imgContainerWidth
+                flickToX = leftMostOptimalX -parent.width
             }
             else {
                 //bring it back
                 flickToX = leftMostOptimalX
             }
 
-            if (pressedForClick) {
-                appWindow.fullscreen = !appWindow.fullscreen
-                pressedForClick = false
-            }
-
             flickFromX = leftMost.x
             flickTo.start()
         }
     }
 
+    ImageContainer {
+        id: one; x: leftMostOptimalX
+        pinchingController: pinchImg
+        pageStack: appWindow.pageStack
+        isVideo: galleryModel.isVideo(index)
+        imageSource: galleryModel.get(index).url
+        videoSource: isVideo ? galleryModel.get(index).url : ""
+        visible: (middle == one || moving)
+    }
+
+    ImageContainer {
+        id: two; anchors.left: one.right
+        pinchingController: pinchImg
+        pageStack: appWindow.pageStack
+        isVideo: galleryModel.isVideo(index)
+        imageSource: galleryModel.get(index).url
+        videoSource: isVideo ? galleryModel.get(index).url : ""
+    }
+
+    //this is the item which is in the middle by default
+    ImageContainer {
+        id: three; anchors.left: two.right
+        pinchingController: pinchImg
+        pageStack: appWindow.pageStack
+        isVideo: galleryModel.isVideo(index)
+        imageSource: galleryModel.get(index).url
+        videoSource: isVideo ? galleryModel.get(index).url : ""
+    }
+
+    ImageContainer {
+        id: four; anchors.left: three.right
+        pinchingController: pinchImg
+        pageStack: appWindow.pageStack
+        isVideo: galleryModel.isVideo(index)
+        imageSource: galleryModel.get(index).url
+        videoSource: isVideo ? galleryModel.get(index).url : ""
+    }
+
+    ImageContainer {
+        id: five; anchors.left: four.right
+        pinchingController: pinchImg
+        pageStack: appWindow.pageStack
+        isVideo: galleryModel.isVideo(index)
+        imageSource: galleryModel.get(index).url
+        videoSource: isVideo ? galleryModel.get(index).url : ""
+    }
+
     Menu {
         id: pageMenu
         MenuLayout {
             MenuItem {
                 text: "Slideshow"
                 onClicked: 
appWindow.pageStack.push(Qt.resolvedUrl("ImageSlideshowPage.qml"),
-                                                    {visibleIndex: 
imageController.middle.index,
-                                                    galleryModel: 
imageController.galleryModel},
+                                                    { visibleIndex: 
imageController.visibleIndex,
+                                                        controller: 
imageController,
+                                                        galleryModel: 
imageController.galleryModel },
                                                     true)
+                enabled: galleryModel.count > 0
             }
         }
     }
@@ -286,4 +348,26 @@
             onClicked: (pageMenu.status === DialogStatus.Closed) ? 
pageMenu.open() : pageMenu.close()
         }
     }
+
+    states: State {
+        name: "active"
+        when: status === PageStatus.Active || status === PageStatus.Activating
+
+        PropertyChanges {
+            target: appWindow.pageStack.toolBar
+            opacity: 0.8
+        }
+    }
+
+    transitions: Transition {
+        from: "active"
+        reversible: true
+
+        NumberAnimation {
+            target: appWindow.pageStack.toolBar
+            property: "opacity"
+            duration: 250
+        }
+    }
+
 }
--- qml/ImageSlideshowPage.qml
+++ qml/ImageSlideshowPage.qml
@@ -38,6 +38,12 @@
     anchors.fill: parent
 
     property int slideVisibleTime: 4000
+
+    //this is to make so that when the slideshow page is popped, the list view 
will be showing the
+    //last element displayed while in slideshow mode
+    //Related to: NEMO#402
+    property variant controller
+
     property int visibleIndex
     property int phase
     property variant galleryModel
@@ -52,6 +58,7 @@
         fillMode: Image.PreserveAspectFit
         Behavior on opacity { NumberAnimation { duration: 1000 } }
     }
+
     Image {
         id: image2
         asynchronous: true
@@ -62,6 +69,7 @@
         fillMode: Image.PreserveAspectFit
         Behavior on opacity { NumberAnimation { duration: 1000 } }
     }
+
     SequentialAnimation {
         id: mainLoop
         loops: Animation.Infinite
@@ -73,11 +81,20 @@
             }
         }
         PauseAnimation { duration: 1001 }
-        ScriptAction { script: loadNextImage() }
+        ScriptAction {
+            script: {
+                visibleIndex++
+                if (visibleIndex >= galleryModel.count)
+                    visibleIndex = 0
+                loadNextImage()
+            }
+        }
     }
+
     MouseArea {
         anchors.fill: parent
         onPressed: {
+            if (controller != undefined) controller.visibleIndex = visibleIndex
             mainLoop.stop()
             image1.source = ""
             image2.source = ""
@@ -85,24 +102,20 @@
             appWindow.pageStack.pop()
         }
     }
+
     Component.onCompleted: {
         appWindow.fullscreen = true
-        if (visibleIndex < galleryModel.count) {
-            phase = 0
-            image1.source = galleryModel.get(visibleIndex).url
-            loadNextImage()
-            mainLoop.start()
-        }
+        phase = 0
+        image1.source = galleryModel.get(visibleIndex).url
+        loadNextImage()
+        mainLoop.start()
     }
-    function loadNextImage() {
-        visibleIndex++
-        if (visibleIndex >= galleryModel.count)
-            visibleIndex = 0
 
+    function loadNextImage() {
         if (phase === 0)
-            image2.source = galleryModel.get(visibleIndex).url
+            image2.source = galleryModel.get((visibleIndex + 1) % 
galleryModel.count).url
         else
-            image1.source = galleryModel.get(visibleIndex).url
+            image1.source = galleryModel.get((visibleIndex + 1) % 
galleryModel.count).url
         phase = 1-phase
     }
 }
--- qml/MainPage.qml
+++ qml/MainPage.qml
@@ -46,7 +46,7 @@
         delegate: GalleryDelegate {
             MouseArea {
                 anchors.fill: parent
-                onClicked: 
appWindow.pageStack.push(Qt.resolvedUrl("ImagePage.qml"), {visibleIndex: index, 
galleryModel: gallery} )
+                onClicked: 
appWindow.pageStack.push(Qt.resolvedUrl("ImagePage.qml"), {parameterIndex: 
index, galleryModel: gallery} )
             }
         }
     }
@@ -102,6 +102,7 @@
             MenuItem {
                 text: "Slideshow"
                 onClicked: 
appWindow.pageStack.push(Qt.resolvedUrl("ImageSlideshowPage.qml"), { 
visibleIndex: 0, galleryModel: gallery })
+                enabled: gallery.count > 0
             }
         }
     }
--- qml/SingleImagePage.qml
+++ qml/SingleImagePage.qml
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2012 Andrea Bernabei <[email protected]>
+ *
+ * You may use this file under the terms of the BSD license as follows:
+ *
+ * "Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of Nemo Mobile nor the names of its contributors
+ *     may be used to endorse or promote products derived from this
+ *     software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+ */
+
+import QtQuick 1.1
+import com.nokia.meego 1.0
+import QtMobility.gallery 1.1
+
+Page {
+    id: singleImagePage
+    anchors.fill: parent
+
+    tools: imgTools
+
+    //filename of the element we're showing
+    property alias imageSource: singleImage.imageSource
+
+    property real swipeThreshold: 40
+    //number of pixel you have to move before the Pinch Area is disabled
+    property real pinchThreshold: 3
+
+    onWidthChanged: {
+        //we're assuming this is only used for images (videos are auto-played, 
so they won't use this page)
+        //if we decide to show this page with the video thumbnail before 
playing the video, a check
+        //has to be added here (for instance: if (!video) resetZoom())
+        pinchImg.resetZoom()
+    }
+
+    ZoomController {
+        id: pinchImg
+
+        //if we decide this page is also used to show video thumbnails
+        //this area will have to be disabled when showing video thumbnails
+
+        pinchTarget: singleImage.image
+        connectedFlickable: singleImage.flickableArea
+        targetContainer: singleImage
+    }
+
+    Connections {
+        target: singleImage
+        onClickedWhileZoomed: fullScreenModeArea.handleClick()
+    }
+
+    Timer {
+        id: toolbarAutohideTimer
+        interval: 2500
+        running: !appWindow.fullscreen
+        onTriggered: appWindow.fullscreen = true
+    }
+    
+    MouseArea {
+        id: fullScreenModeArea
+        anchors.fill: parent
+
+        function handleClick() {
+            if (toolbarTimer.running) {
+                toolbarTimer.stop()
+            } else {
+                toolbarTimer.start()
+            }
+        }
+
+        //we use this to be able to not call singleclick handlers when the 
user is actually doubleclicking
+        Timer {
+            id: toolbarTimer
+            interval: 350
+            onTriggered: appWindow.fullscreen = !appWindow.fullscreen
+        }
+
+        onClicked: handleClick()
+    }
+
+    ImageContainer {
+        id: singleImage
+        pinchingController: pinchImg
+    }
+
+    ToolBarLayout {
+        id: imgTools
+        ToolIcon {
+            platformIconId: "toolbar-back"
+            anchors.left: (parent === undefined) ? undefined : parent.left
+            onClicked: {
+                appWindow.fullscreen = false
+                appWindow.pageStack.pop()
+            }
+        }
+    }
+
+    states: State {
+        name: "active"
+        when: status === PageStatus.Active || status === PageStatus.Activating
+
+        PropertyChanges {
+            target: appWindow.pageStack.toolBar
+            opacity: 0.8
+        }
+    }
+
+    transitions: Transition {
+        from: "active"
+        reversible: true
+
+        NumberAnimation {
+            target: appWindow.pageStack.toolBar
+            property: "opacity"
+            duration: 250
+        }
+    }
+
+}
--- qml/ZoomController.qml
+++ qml/ZoomController.qml
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2012 Andrea Bernabei <[email protected]>
+ *
+ * You may use this file under the terms of the BSD license as follows:
+ *
+ * "Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of Nemo Mobile nor the names of its contributors
+ *     may be used to endorse or promote products derived from this
+ *     software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+ */
+
+import QtQuick 1.1
+
+PinchArea {
+    anchors.fill: parent
+
+    property variant pinchTarget
+    property variant connectedFlickable
+    property variant targetContainer
+
+    pinch.target: pinchTarget
+    pinch.maximumScale: 5
+    pinch.dragAxis: Pinch.NoDrag
+
+    property real lastContentX: 0
+    property real lastContentY: 0
+    property real lastScaleX: 1
+    property real lastScaleY: 1
+    property real deltaX: 0
+    property real deltaY: 0
+    property bool initializedX: false
+    property bool initializedY: false
+    property bool isZoomingOut: false
+    property real middleQuickZoomInContentX: 0
+    property real middleQuickZoomInContentY: 0
+    property real middleQuickZoomInScale: 1.0
+    property real finalQuickZoomInContentX: 0
+    property real finalQuickZoomInContentY: 0
+    property real finalQuickZoomInScale: 1.0
+    property bool needsCenteringAnimation: false
+
+    function resetZoom() {
+        //resetting all variables related to pinch-to-zoom
+        pinchTarget.scale = 1
+        connectedFlickable.contentX = connectedFlickable.contentY = 0
+        lastContentX = lastContentY = 0
+        deltaX = deltaY = 0
+        lastScaleX = lastScaleY = 1
+        isZoomingOut = false
+    }
+
+    //quickZoomIn works in 2 phases:
+    //1) zoom image (until it fills screen in case we're going to execute 
phase 2 too)
+    //2) (ONLY IF the requested scale factor exceeds the screen-filling scale 
factor) Zoom again centered on the finger position
+    function quickZoomIn(centerX, centerY, finalScale) {
+        //TODO: this needs to be updated when we decide how to get the 
original size of an image
+        var imgRatio = pinchTarget.implicitWidth / pinchTarget.implicitHeight
+        var fitsVertically = imgRatio < (targetContainer.width / 
targetContainer.height)
+
+        //the scaling factor needed to make the image fill the screen
+        middleQuickZoomInScale = (fitsVertically ? screen.platformWidth : 
screen.platformHeight) / (fitsVertically ? pinchTarget.width : 
pinchTarget.height)
+
+        //if we don't need the image to fill the screen
+        if (middleQuickZoomInScale > finalScale) middleQuickZoomInScale = 
finalScale
+
+        if (fitsVertically) {
+            middleQuickZoomInContentX = 0
+            middleQuickZoomInContentY = (centerY * middleQuickZoomInScale) - 
centerY
+            needsCenteringAnimation = (pinchTarget.width * finalScale) > 
screen.platformWidth
+        }
+        else {
+            middleQuickZoomInContentX = (centerX * middleQuickZoomInScale) - 
centerX
+            middleQuickZoomInContentY = 0
+            needsCenteringAnimation = (pinchTarget.height * finalScale) > 
screen.platformHeight
+        }
+
+        if (needsCenteringAnimation) {
+            finalQuickZoomInScale = finalScale
+            var remainingScaleFactorX = finalQuickZoomInScale / 
(screen.platformWidth / pinchTarget.width)
+            var remainingScaleFactorY = finalQuickZoomInScale / 
(screen.platformHeight / pinchTarget.height)
+            finalQuickZoomInContentX = (centerX * remainingScaleFactorX) - 
centerX
+            finalQuickZoomInContentY = (centerY * remainingScaleFactorY) - 
centerY
+        }
+        quickZoomInScalingAnimation.start()
+    }
+
+    //first scale the image so that it fills the screen
+    ParallelAnimation {
+        id: quickZoomInScalingAnimation
+        NumberAnimation {target: pinchTarget; property: "scale"; to: 
middleQuickZoomInScale; duration: 100}
+        NumberAnimation {target: connectedFlickable; property: "contentX"; to: 
middleQuickZoomInContentX; duration: 100}
+        NumberAnimation {target: connectedFlickable; property: "contentY"; to: 
middleQuickZoomInContentY; duration: 100}
+        onCompleted: if (needsCenteringAnimation) 
quickZoomInCenteringAnimation.start()
+    }
+
+    //then create zoom effect centered on the doubleclick coordinates (IF 
NEEDED)
+    ParallelAnimation {
+        id: quickZoomInCenteringAnimation
+        NumberAnimation {target: pinchTarget; property: "scale"; to: 
finalQuickZoomInScale; duration: 100}
+        NumberAnimation {target: connectedFlickable; property: "contentX"; to: 
finalQuickZoomInContentX; duration: 100}
+        NumberAnimation {target: connectedFlickable; property: "contentY"; to: 
finalQuickZoomInContentY; duration: 100}
+    }
+
+    function updateContentX() {
+        //Only calculate the correct ContentX if the image is wider than the 
screen, otherwise keep it centered (contentX = 0 in the else branch)
+        if (connectedFlickable.width == targetContainer.width) {
+
+            //Anchors the image to the left
+            if (connectedFlickable.contentX < 0){
+                deltaX = 0.0
+                lastContentX = 0.0
+                connectedFlickable.contentX = 0.0
+            }
+            else {
+                //if the right end of the image is inside the screen area, 
lock it to the right and zoom out using right edge as an anchor
+                if ((connectedFlickable.contentWidth - 
connectedFlickable.contentX < parent.width) && isZoomingOut) {
+
+                    //align to the right
+                    connectedFlickable.contentX -= parent.width - 
(connectedFlickable.contentWidth - connectedFlickable.contentX)
+
+                    //Algo: set variable as if a new pinch starting from right 
edge were triggered
+                    lastContentX = connectedFlickable.contentX
+                    deltaX = connectedFlickable.contentX + parent.width
+                    lastScaleX = pinchTarget.scale
+                }
+                connectedFlickable.contentX = (lastContentX + deltaX * 
((pinchTarget.scale / lastScaleX) - 1.0 ))
+            }
+        }
+        else {
+            connectedFlickable.contentX = 0
+        }
+    }
+
+    function updateContentY() {
+        //Only calculate the correct ContentY if the image is taller than the 
screen, otherwise keep it centered (contentY = 0 in the else branch)
+        if (connectedFlickable.height == targetContainer.height) {
+
+            //Anchors the image to the top when zooming out
+            if (connectedFlickable.contentY < 0) {
+                deltaY = 0.0
+                lastContentY = 0.0
+                connectedFlickable.contentY = 0.0
+            }
+            else {
+                //if the bottom end of the image is inside the screen area, 
lock it to the bottom and zoom out using bottom edge as an anchor
+                if ((connectedFlickable.contentHeight - 
connectedFlickable.contentY < parent.height) && isZoomingOut) {
+                    //align to the bottom
+                    connectedFlickable.contentY -= parent.height - 
(connectedFlickable.contentHeight - connectedFlickable.contentY)
+
+                    //Algo: set variable as if a new pinch starting from 
bottom edge were triggered
+                    lastContentY = connectedFlickable.contentY
+                    deltaY = connectedFlickable.contentY + parent.height
+                    lastScaleY = pinchTarget.scale
+                }
+                connectedFlickable.contentY = (lastContentY + deltaY * 
((pinchTarget.scale / lastScaleY) - 1.0 ))
+            }
+        }
+        else {
+            connectedFlickable.contentY = 0
+        }
+    }
+
+    onPinchUpdated: {
+        //Am I zooming in or out?
+        if (pinch.scale > pinch.previousScale) isZoomingOut = false
+        else isZoomingOut = true
+
+        //Get updated "zoom center point" values when the image is completely 
zoomed out
+        if(pinchTarget.scale == 1) {
+            //This is so that everytime you zoom out, the new zoom is started 
with updated values
+            initializedX = false
+            initializedY = false
+        }
+
+        //i.e. everytime the image is wider than the screen, it should 
actually be
+        // pinchTarget.width == targetContainer.width, but this condition is 
rarely met because of numeric error
+        if (connectedFlickable.width == targetContainer.width) {
+            if (!initializedX ) {
+                //If it has not already been set by the "if (height == 
parent.targetContainer.height)" branch, set the scale here
+                lastScaleX = pinchTarget.scale
+
+                lastContentX = connectedFlickable.contentX
+                deltaX = connectedFlickable.contentX + pinch.center.x
+                initializedX = true;
+            }
+
+        }
+
+        if (connectedFlickable.height == targetContainer.height) {
+            if (!initializedY) {
+                //If it has not already been set by the "if (width == 
targetContainer.width)", set the scale here
+                lastScaleY = pinchTarget.scale
+
+                lastContentY = connectedFlickable.contentY
+                deltaY = connectedFlickable.contentY + pinch.center.y
+                initializedY = true;
+            }
+        }
+        // updateContentX and updateContentY are called after the scale on the 
target item updates bindings
+    }
+
+    onPinchFinished: {
+        lastContentX = connectedFlickable.contentX
+        lastContentY = connectedFlickable.contentY
+
+        initializedX = false
+        initializedY = false
+    }
+
+}
--- qml/api/GalleryView.qml
+++ qml/api/GalleryView.qml
@@ -30,7 +30,7 @@
  */
 
 import QtQuick 1.1
-import com.nokia.meego 1.0
+import com.nokia.meego 1.2
 import org.nemomobile.thumbnailer 1.0
 
 GridView {
@@ -72,20 +72,8 @@
         onCurrentOrientationChanged: updateThumbnailSize()
     }
 
-    Text {
-        id: noElementsFoundText
-        anchors.verticalCenter: parent.verticalCenter
-        anchors.left: parent.left
-        anchors.right: parent.right
-        anchors.leftMargin: 20
-        anchors.rightMargin: 20
-
-        visible: parent.model.count == 0 && parent.model.progress == 1.0
-
+    ViewPlaceholder {
         text: "No elements found..."
-        color: "lightgrey"
-        font.pointSize: 26
-        horizontalAlignment: Text.AlignHCenter
-        wrapMode: Text.WordWrap
+        enabled: parent.model.count == 0 && parent.model.progress == 1.0
     }
 }
--- qml/main.qml
+++ qml/main.qml
@@ -35,8 +35,9 @@
 PageStackWindow {
     id: appWindow
 
-    initialPage: mainPage
     property bool fullscreen: false
+
+    initialPage: mainPage
     showStatusBar: !fullscreen
     showToolBar: !fullscreen
 
@@ -47,4 +48,21 @@
     Component.onCompleted: {
         theme.inverted = true
     }
+
+    function displayFile(filename) {
+        console.log("displayFile:" + filename)
+        appWindow.pageStack.pop(null) // Unwind to top of the stack, wherever 
we are
+
+        switch (gallery.isVideo(filename)) {
+        case 0:
+            appWindow.pageStack.push(Qt.resolvedUrl("SingleImagePage.qml"), { 
imageSource: filename })
+            break
+        case 1:
+            appWindow.pageStack.push(Qt.resolvedUrl("VideoPlayer.qml"), { 
videoSource: filename })
+            break
+        case -1:
+            console.log("displayFile: ERROR WHILE LOADING THE FILE, FILE NOT 
FOUND")
+
+        }
+    }
 }
--- qmlgallery.desktop
+++ qmlgallery.desktop
@@ -3,8 +3,9 @@
 Type=Application
 Terminal=false
 Name=Gallery
-Exec=invoker -s --type=d /usr/bin/qmlgallery -fullscreen
+Exec=invoker -s --type=d /usr/bin/qmlgallery -fullscreen %u
 Icon=icons-Applications-photos
 X-Window-Icon=
 X-HildonDesk-ShowInToolbar=true
 X-Osso-Type=application/x-executable
+MimeType=image/*;video/*;
--- res.qrc
+++ res.qrc
@@ -12,5 +12,7 @@
         <file>qml/SortDialog.qml</file>
         <file>images/down_arrow.png</file>
         <file>images/up_arrow.png</file>
+        <file>qml/ZoomController.qml</file>
+        <file>qml/SingleImagePage.qml</file>
     </qresource>
 </RCC>
--- rpm
+++ rpm
+(directory)
--- rpm/qmlgallery.spec
+++ rpm/qmlgallery.spec
@@ -0,0 +1,73 @@
+# 
+# Do NOT Edit the Auto-generated Part!
+# Generated by: spectacle version 0.25
+# 
+
+Name:       qmlgallery
+
+# >> macros
+# << macros
+
+Summary:    Photo Gallery for Nemo
+Version:    0.1.0
+Release:    1
+Group:      Applications/System
+License:    BSD
+URL:        https://github.com/nemomobile/qmlgallery
+Source0:    %{name}-%{version}.tar.bz2
+Source100:  qmlgallery.yaml
+Requires:   qt-components >= 1.4.8
+Requires:   libdeclarative-gallery
+Requires:   libdeclarative-multimedia
+Requires:   nemo-qml-plugins-thumbnailer
+BuildRequires:  pkgconfig(QtCore) >= 4.7.0
+BuildRequires:  pkgconfig(QtDeclarative)
+BuildRequires:  pkgconfig(QtGui)
+BuildRequires:  pkgconfig(qdeclarative-boostable)
+BuildRequires:  pkgconfig(QtOpenGL)
+BuildRequires:  pkgconfig(libresourceqt1)
+BuildRequires:  desktop-file-utils
+Provides:   meego-handset-video > 0.2.5
+Obsoletes:   meego-handset-video <= 0.2.5
+
+%description
+Photo Gallery application using Qt Quick for Nemo Mobile.
+
+
+%prep
+%setup -q -n %{name}-%{version}
+
+# >> setup
+# << setup
+
+%build
+# >> build pre
+# << build pre
+
+%qmake 
+
+make %{?jobs:-j%jobs}
+
+# >> build post
+# << build post
+
+%install
+rm -rf %{buildroot}
+# >> install pre
+# << install pre
+%qmake_install
+
+# >> install post
+# << install post
+
+desktop-file-install --delete-original       \
+  --dir %{buildroot}%{_datadir}/applications             \
+   %{buildroot}%{_datadir}/applications/*.desktop
+
+%files
+%defattr(-,root,root,-)
+%{_bindir}/qmlgallery
+%{_datadir}/applications/qmlgallery.desktop
+%{_libdir}/qt4/imports/org/nemomobile/qmlgallery/*
+# >> files
+# << files
--- rpm/qmlgallery.yaml
+++ rpm/qmlgallery.yaml
@@ -0,0 +1,35 @@
+Name:  qmlgallery
+Summary: Photo Gallery for Nemo
+Version: 0.1.0
+Release: 1
+Group: Applications/System
+License: BSD
+URL:        https://github.com/nemomobile/qmlgallery
+Sources:
+    - "%{name}-%{version}.tar.bz2"
+Description: Photo Gallery application using Qt Quick for Nemo Mobile.
+Configure: none
+Builder: qmake
+Obsoletes:
+    - meego-handset-video <= 0.2.5
+Provides:
+    - meego-handset-video > 0.2.5
+
+PkgConfigBR:
+    - QtCore >= 4.7.0
+    - QtDeclarative
+    - QtGui
+    - qdeclarative-boostable
+    - QtOpenGL
+    - libresourceqt1
+Requires:
+    - qt-components >= 1.4.8
+    - libdeclarative-gallery
+    - libdeclarative-multimedia
+    - nemo-qml-plugins-thumbnailer
+ 
+Files:
+    - "%{_bindir}/qmlgallery"
+    - "%{_datadir}/applications/qmlgallery.desktop"
+    - "%{_libdir}/qt4/imports/org/nemomobile/qmlgallery/*"
+
--- src/gallery.cpp
+++ src/gallery.cpp
@@ -36,10 +36,17 @@
 #include <QDir>
 #include <QGLWidget>
 #include <QDebug>
+#include <QFile>
+#include <QFileInfo>
+#include <QMetaObject>
+#include <QGraphicsObject>
+#include <QUrl>
+#include <QImageReader>
 
 Gallery::Gallery(QDeclarativeView *v, QObject *parent)
     : QObject(parent), view(v)
 {
+    QFile fileToOpen;
     bool isFullscreen = false;
     bool glwidget = true;
     foreach (QString parameter, qApp->arguments()) {
@@ -50,8 +57,11 @@
             qDebug() << "-fullscreen   - show QML fullscreen";
             qDebug() << "-no-glwidget  - Don't use QGLWidget viewport";
             exit(0);
-        } else if (parameter == "-no-glwidget")
+        } else if (parameter == "-no-glwidget") {
             glwidget = false;
+        } else if (parameter != qApp->arguments().first() && 
!fileToOpen.exists()) {
+            fileToOpen.setFileName(parameter);
+        }
     }
 
     // See NEMO#415 for an explanation of why this may be necessary.
@@ -84,6 +94,18 @@
         view->showFullScreen();
     else
         view->show();
+
+    if(!fileToOpen.fileName().isNull()) {
+        if (fileToOpen.exists()) {
+            QFileInfo fileInfo(fileToOpen);
+            QUrl fileUrl = QUrl::fromLocalFile(fileInfo.absoluteFilePath());
+            if (view->rootObject()) {
+                QMetaObject::invokeMethod(view->rootObject(), "displayFile", 
Q_ARG(QVariant, QVariant(fileUrl.toString())));
+            }
+        } else {
+            qDebug() << "File " << fileToOpen.fileName() << " does not exist.";
+        }
+    }
 }
 
 void Gallery::acquireVideoResources()
@@ -123,3 +145,21 @@
     qDebug() << Q_FUNC_INFO;
 }
 
+int Gallery::isVideo(QString fileUrl)
+{
+    //RETURN VALUES
+    //-1: ERROR, 0: IMAGE, 1: VIDEO
+    const QString fileName = QUrl(fileUrl).toLocalFile();
+    QFileInfo testFile(fileName);
+    if (testFile.exists())
+    {
+        QImageReader reader(fileName);
+        QByteArray format = reader.format();
+        if (format.isNull() && reader.error() == 
QImageReader::UnsupportedFormatError) {
+            //we assume it's a video
+            return 1;
+        }
+        else return 0;
+    }
+    return -1;
+}
--- src/gallery.h
+++ src/gallery.h
@@ -35,6 +35,7 @@
 #include <policy/resource-set.h>
 
 class QDeclarativeView;
+class QString;
 
 class Gallery : public QObject
 {
@@ -46,6 +47,7 @@
 public slots:
     void acquireVideoResources();
     void releaseVideoResources();
+    int isVideo(QString fileName);
 
 private slots:
     void resourcesGranted();

++++++ qmlgallery.yaml
--- qmlgallery.yaml
+++ qmlgallery.yaml
@@ -1,25 +1,16 @@
 Name:  qmlgallery
 Summary: Photo Gallery for Nemo
-Version: 0.0.10
+Version: 0.1.0
 Release: 1
 Group: Applications/System
 License: BSD
 URL:        https://github.com/nemomobile/qmlgallery
 Sources:
-    - "%{name}-%{version}.tar.bz2"
+- "%{name}-%{version}.tar.bz2"
+SetupOptions: -q -n %{name}
 Description: Photo Gallery application using Qt Quick for Nemo Mobile.
 Configure: none
 Builder: qmake
-
-# These are the flags QtSDK sets for Harmattan
-# we want to keep both QtSDK harmattan and Nemo build as close as possible
-# so we define the same flags
-QMakeOptions:
-    - "MEEGO_VERSION_MAJOR=1"
-    - "MEEGO_VERSION_MINOR=2"
-    - "MEEGO_VERSION_PATCH=0"
-    - "MEEGO_EDITION=harmattan"
-    - "DEFINES+=MEEGO_EDITION_HARMATTAN"
 Obsoletes:
     - meego-handset-video <= 0.2.5
 Provides:
@@ -33,6 +24,7 @@
     - QtOpenGL
     - libresourceqt1
 Requires:
+    - qt-components >= 1.4.8
     - libdeclarative-gallery
     - libdeclarative-multimedia
     - nemo-qml-plugins-thumbnailer
@@ -41,3 +33,4 @@
     - "%{_bindir}/qmlgallery"
     - "%{_datadir}/applications/qmlgallery.desktop"
     - "%{_libdir}/qt4/imports/org/nemomobile/qmlgallery/*"
+



Reply via email to