Use Qtm namespace for QtMobility.systeminfo to resolve the BatteryInfo conflict
[harmattan/cameraplus] / qml / main.qml
index 28ca75a..d30139a 100644 (file)
@@ -1,20 +1,40 @@
 // -*- qml -*-
+
+/*!
+ * This file is part of CameraPlus.
+ *
+ * Copyright (C) 2012 Mohammed Sameer <msameer@foolab.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
 import QtQuick 1.1
 import com.nokia.meego 1.1
+import com.nokia.extras 1.1
 import QtCamera 1.0
 import CameraPlus 1.0
+import QtMobility.systeminfo 1.2 as Qtm
+import QtMobility.location 1.2
 
-// TODO: metadata creator name, gps, geotags
-// TODO: resolutions and aspect ratios
-// TODO: postcapture
-// TODO: file naming
-// TODO: battery low state
-// TODO: disk space
-// TODO: flash not ready
-// TODO: focus, caf, ...
-// TODO: indicators
+// TODO: flash not ready (battery low or flash not ready message)
 // TODO: portrait/landscape
-// TODO: record in a hidden directory and then copy the video to avoid tracker indexing it.
+// TODO: grid lines, face tracking
+// TODO: select primary/secondary camera.
+// TODO: disable debug builds.
+// TODO: a way to get buffers to the application
+// TODO: fcam like functionality (precise control over capture parameters).
 
 PageStackWindow {
         id: root
@@ -25,100 +45,380 @@ PageStackWindow {
 
         Component.onCompleted: {
                 theme.inverted = true;
-                if (settings.mode == 0) {
-                        openFile("ImagePage.qml");
-                }
-                else {
-                        openFile("VideoPage.qml");
+                // TODO: hardcoding device id
+                root.resetCamera(0, settings.mode);
+        }
+
+        function showError(msg) {
+                error.text = msg;
+                error.show();
+        }
+
+        function resetCamera(deviceId, mode) {
+                if (!cam.reset(deviceId, mode)) {
+                        showError(qsTr("Failed to set camera device and mode. Please restart the application."));
                 }
         }
 
+        PositionSource {
+                // NOTE: The source will not reset the position when we lose the signal.
+                // This shouldn't be a big problem as we are course enough.
+                // If we ever need street level updates then this will be an issue.
+                id: positionSource
+                active: settings.useGps
+                // TODO: we cannot bind to cam.running because camera will stop
+                // when the connection dialog pops up and we end up with an infinite loop
+                // active: cam.running && settings.useGps
+                onPositionChanged: geocode.search(position.coordinate.longitude, position.coordinate.latitude);
+        }
+
+        MetaData {
+                id: metaData
+                camera: cam
+                manufacturer: deviceInfo.manufacturer
+                model: deviceInfo.model
+                country: geocode.country
+                city: geocode.city
+                suburb: geocode.suburb
+                longitude: positionSource.position.coordinate.longitude
+                longitudeValid: positionSource.position.longitudeValid && settings.useGps
+                latitude: positionSource.position.coordinate.latitude
+                latitudeValid: positionSource.position.latitudeValid && settings.useGps
+                elevation: positionSource.position.coordinate.altitude
+                elevationValid: positionSource.position.altitudeValid && settings.useGps
+                orientation: orientation.orientation
+                artist: settings.creatorName
+                captureDirection: compass.direction
+                captureDirectionValid: compass.directionValid
+                horizontalError: positionSource.position.horizontalAccuracy
+                horizontalErrorValid: positionSource.position.horizontalAccuracyValid && settings.useGps
+                dateTimeEnabled: true
+        }
+
+        Orientation {
+                id: orientation
+                active: cam.running
+        }
+
+        Compass {
+                id: compass
+                active: cam.running
+        }
+
+        ReverseGeocode {
+                id: geocode
+                active: cam.running && settings.useGps && settings.useGeotags
+        }
+
+        PipelineManager {
+                id: pipelineManager
+                camera: cam
+        }
+
+        Qtm.DeviceInfo {
+                id: deviceInfo
+        }
+
+        FSMonitor {
+                id: fileSystem
+        }
+
+        InfoBanner {
+                id: error
+        }
+
         Settings {
                 id: settings
         }
 
-        // Stolen from https://qt.gitorious.org/qt-components/qt-components/blobs/master/examples/meego/QmlComponentGallery/qml/ListPage.qml
-        function replacePage(file) {
-                var component = Qt.createComponent(file)
+        FileNaming {
+                id: fileNaming
+                imageSuffix: cam.imageSuffix
+                videoSuffix: cam.videoSuffix
+        }
 
-                if (component.status == Component.Ready) {
-                        pageStack.replace(component, {cam: cam}, true);
+        MountProtector {
+                id: mountProtector
+                path: fileNaming.path
+        }
+
+        BatteryInfo {
+                id: batteryMonitor
+                active: cam.running
+
+                function check() {
+                        if (!checkBattery()) {
+                                pageStack.currentPage.batteryLow();
+                        }
                 }
-                else {
-                        console.log("Error loading component:", component.errorString());
+
+                onChargingChanged: {
+                        batteryMonitor.check();
+                }
+
+                onCriticalChanged: {
+                        batteryMonitor.check();
                 }
         }
 
+        function replacePage(file) {
+                pageStack.replace(Qt.resolvedUrl(file), {cam: cam, dimmer: root.dimmer}, true);
+        }
+
         function openFile(file) {
-                var component = Qt.createComponent(file)
+                pageStack.push(Qt.resolvedUrl(file), {cam: cam, dimmer: root.dimmer});
+        }
 
-                if (component.status == Component.Ready) {
-                        pageStack.push(component, {cam: cam});
+        function openFileNow(file) {
+                pageStack.push(Qt.resolvedUrl(file), {cam: cam, dimmer: root.dimmer}, true);
+        }
+
+        function checkBattery() {
+                // We are fine if we are connected to the charger:
+                if (batteryMonitor.charging) {
+                        return true;
                 }
-                else {
-                        console.log("Error loading component:", component.errorString());
+
+                // If we have enough battery then we are fine:
+                if (!batteryMonitor.critical) {
+                        return true;
                 }
+
+                return false;
         }
 
         platformStyle: PageStackWindowStyle {
-                // TODO: Hack
-                background: " "
+                cornersVisible: false
+                background: ""
+                backgroundColor: "transparent"
         }
 
-        Connections {
-                target: platformWindow
-                onActiveChanged: {
-                        if (platformWindow.active) {
-                                cam.start();
+        ImageSettings {
+                id: imageSettings
+                camera: cam
+                function setImageResolution() {
+                        if (!imageSettings.setResolution(settings.imageAspectRatio, settings.imageResolution)) {
+                                showError(qsTr("Failed to set required resolution"));
                         }
-                        else {
-                                // TODO: only stop if idle.
-                                cam.stop();
+                }
+
+                onReadyChanged: {
+                        if (ready) {
+                                imageSettings.setImageResolution();
                         }
                 }
         }
 
+        VideoSettings {
+                id: videoSettings
+                camera: cam
+
+                function setVideoResolution() {
+                        if (!videoSettings.setResolution(settings.videoAspectRatio, settings.videoResolution)) {
+                                showError(qsTr("Failed to set required resolution"));
+                        }
+                }
+
+                onReadyChanged: {
+                        if (ready) {
+                                videoSettings.setVideoResolution();
+                        }
+                }
+        }
+
+        Connections {
+                target: settings
+
+                onImageAspectRatioChanged: {
+                        imageSettings.setImageResolution();
+                }
+
+                onImageResolutionChanged: {
+                        imageSettings.setImageResolution();
+                }
+
+                onVideoResolutionChanged: {
+                        videoSettings.setVideoResolution();
+                }
+        }
+
         Camera {
+                id: cam
+                anchors.fill: parent
+
+                FocusReticle {
+                        id: focusReticle
+                        cam: cam
+                        visible: pageStack.currentPage && pageStack.currentPage.controlsVisible && pageStack.currentPage.focusReticleVisible && cam && cam.autoFocus.canFocus(cam.scene.value);
+                        cafStatus: cam ? cam.autoFocus.cafStatus : -1
+                        status: cam ? cam.autoFocus.status : -1
+                }
+
+/*
                 onDeviceIdChanged: {
+                        // TODO: is this needed ?
                         if (platformWindow.active) {
                                 cam.start();
                         }
                 }
+*/
+                onError: {
+                        if (pipelineManager.error) {
+                                // Ignore any subsequent errors.
+                                // Killing pulseaudio while recording will lead to an
+                                // infinite supply of errors which will break the UI
+                                // if we show a banner for each.
+                                return;
+                        }
 
-                id: cam
-                anchors.fill: parent
+                        pipelineManager.error = true;
+                        pageStack.currentPage.cameraError();
+                        console.log("Camera error (" + code + "): " + message + " " + debug);
+                        showError(qsTr("Camera error. Please restart the application."));
 
-                // TODO: hardcoding device id
-                Component.onCompleted: { cam.deviceId = 0; mode = settings.mode; }
+                        // We cannot stop camera here. Seems there is a race condition somewhere
+                        // which leads to a freeze if we do so.
+                }
+
+                onRunningChanged: {
+                        if (!cam.running) {
+                                mountProtector.unlock();
+                        }
+                }
 
-                // TODO: Hack
+                Component.onDestruction: cam.stop();
+
+                // We need to show viewfinder below pages.
                 z: -1
 
                 Rectangle {
+                        property bool dimmed: false
                         id: camDimmer
                         z: 1
                         anchors.fill: parent
-                        opacity: 0
+                        opacity: dimmed ? 1.0 : 0.0
                         color: "black"
+                        Behavior on opacity {
+                                PropertyAnimation { duration: 150 }
+                        }
+                }
+
+                notifications: Sounds {
+                        id: sounds
+                        mute: !settings.soundEnabled
                 }
+
         }
 
-        Scene {
-                id: sceneController
-                camera: cam
-                value: ready ? camera.mode == Camera.VideoMode ? settings.videoSceneMode : settings.imageSceneMode : 0
+        Binding {
+                target: cam.flash
+                property: "value"
+                when: cam.mode == Camera.ImageMode
+                value: settings.imageFlashMode
         }
 
-        ColorTone {
-                id: colorToneController
-                camera: cam
-                value: ready ? camera.mode == Camera.VideoMode ? settings.videoColorFilter : settings.imageColorFilter : 0
+        Binding {
+                target: settings
+                property: "imageFlashMode"
+                when: cam.mode == Camera.ImageMode
+                value: cam.flash.value
         }
 
-        WhiteBalance {
-                id: whiteBalanceController
-                camera: cam
-                value: ready ? camera.mode == Camera.VideoMode ? settings.videoWhiteBalance : settings.imageWhiteBalance : 0
+        Binding {
+                target: cam.scene
+                property: "value"
+                when: cam.mode == Camera.VideoMode
+                value: settings.videoSceneMode
+        }
+
+        Binding {
+                target: cam.scene
+                property: "value"
+                when: cam.mode == Camera.ImageMode
+                value: settings.imageSceneMode
+        }
+
+        Binding {
+                target: cam.evComp
+                property: "value"
+                when: cam.mode == Camera.ImageMode
+                value: settings.imageEvComp
+        }
+
+        Binding {
+                target: cam.evComp
+                property: "value"
+                when: cam.mode == Camera.VideoMode
+                value: settings.videoEvComp
+        }
+
+        Binding {
+                target: settings
+                property: "imageEvComp"
+                when: cam.mode == Camera.ImageMode
+                value: cam.evComp.value
+        }
+
+        Binding {
+                target: settings
+                property: "videoEvComp"
+                when: cam.mode == Camera.VideoMode
+                value: cam.evComp.value
+        }
+
+        Binding {
+                target: cam.whiteBalance
+                property: "value"
+                when: cam.mode == Camera.ImageMode
+                value: settings.imageWhiteBalance
+        }
+
+        Binding {
+                target: cam.whiteBalance
+                property: "value"
+                when: cam.mode == Camera.VideoMode
+                value: settings.videoWhiteBalance
+        }
+
+        Binding {
+                target: cam.colorTone
+                property: "value"
+                when: cam.mode == Camera.ImageMode
+                value: settings.imageColorFilter
+        }
+
+        Binding {
+                target: cam.colorTone
+                property: "value"
+                when: cam.mode == Camera.VideoMode
+                value: settings.videoColorFilter
+        }
+
+        Binding {
+                target: cam.iso
+                property: "value"
+                when: cam.mode == Camera.ImageMode
+                value: settings.imageIso
+        }
+
+        Binding {
+                target: settings
+                property: "imageIso"
+                when: cam.mode == Camera.ImageMode
+                value: cam.iso.value
+        }
+
+        Binding {
+                target: cam.videoMute
+                property: "enabled"
+                value: settings.videoMuted
+        }
+
+        TrackerStore {
+                id: trackerStore
+                active: cam.running
+                manufacturer: deviceInfo.manufacturer
+                model: deviceInfo.model
         }
 
         ModeController {
@@ -138,4 +438,9 @@ PageStackWindow {
                         }
                 }
         }
+
+        Standby {
+                policyLost: pipelineManager.state == "policyLost"
+                show: !pageStack.currentPage || (pageStack.currentPage.standbyVisible && pageStack.currentPage.status == PageStatus.Active && pipelineManager.showStandBy)
+        }
 }