4085bac49c8533fe5c01b5e4ecdc7b02c4dd3f43
[harmattan/cameraplus] / qml / main.qml
1 // -*- qml -*-
2
3 /*!
4  * This file is part of CameraPlus.
5  *
6  * Copyright (C) 2012-2013 Mohammed Sameer <msameer@foolab.org>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21  */
22
23 import QtQuick 1.1
24 import com.nokia.meego 1.1
25 import com.nokia.extras 1.1
26 import QtCamera 1.0
27 import CameraPlus 1.0
28 import QtMobility.location 1.2
29
30 // TODO: flash not ready (battery low or flash not ready message)
31
32 Window {
33         id: root
34
35         property alias dimmer: camDimmer
36
37         PageStack {
38                 id: pageStack
39                 anchors.fill: parent
40         }
41
42         MouseArea {
43                 anchors.fill: parent
44                 enabled: pageStack.busy
45         }
46
47         PlatformSettings {
48                 id: platformSettings
49         }
50
51         Component.onCompleted: {
52                 platformSettings.init();
53                 theme.inverted = true;
54                 // TODO: hardcoding device id
55                 root.resetCamera(0, settings.mode);
56         }
57
58         function showError(msg) {
59                 error.text = msg;
60                 error.show();
61         }
62
63         function resetCamera(deviceId, mode) {
64                 if (!cam.reset(deviceId, mode)) {
65                         showError(qsTr("Failed to set camera device and mode. Please restart the application."));
66                 }
67         }
68
69         PositionSource {
70                 // NOTE: The source will not reset the position when we lose the signal.
71                 // This shouldn't be a big problem as we are course enough.
72                 // If we ever need street level updates then this will be an issue.
73                 id: positionSource
74                 active: settings.useGps
75                 // TODO: we cannot bind to cam.running because camera will stop
76                 // when the connection dialog pops up and we end up with an infinite loop
77                 // active: cam.running && settings.useGps
78                 onPositionChanged: geocode.search(position.coordinate.longitude, position.coordinate.latitude);
79         }
80
81         MetaData {
82                 id: metaData
83                 camera: cam
84                 manufacturer: deviceInfo.manufacturer
85                 model: deviceInfo.model
86                 country: geocode.country
87                 city: geocode.city
88                 suburb: geocode.suburb
89                 longitude: positionSource.position.coordinate.longitude
90                 longitudeValid: positionSource.position.longitudeValid && settings.useGps
91                 latitude: positionSource.position.coordinate.latitude
92                 latitudeValid: positionSource.position.latitudeValid && settings.useGps
93                 elevation: positionSource.position.coordinate.altitude
94                 elevationValid: positionSource.position.altitudeValid && settings.useGps
95                 orientation: orientation.orientation
96                 artist: settings.creatorName
97                 captureDirection: compass.direction
98                 captureDirectionValid: compass.directionValid
99                 horizontalError: positionSource.position.horizontalAccuracy
100                 horizontalErrorValid: positionSource.position.horizontalAccuracyValid && settings.useGps
101                 dateTimeEnabled: true
102         }
103
104         Orientation {
105                 id: orientation
106                 active: cam.running
107         }
108
109         Compass {
110                 id: compass
111                 active: cam.running
112         }
113
114         ReverseGeocode {
115                 id: geocode
116                 active: cam.running && settings.useGps && settings.useGeotags
117         }
118
119         PipelineManager {
120                 id: pipelineManager
121                 camera: cam
122         }
123
124         DeviceInfo {
125                 id: deviceInfo
126         }
127
128         FSMonitor {
129                 id: fileSystem
130         }
131
132         InfoBanner {
133                 id: error
134         }
135
136         Settings {
137                 id: settings
138         }
139
140         FileNaming {
141                 id: fileNaming
142                 imageSuffix: cam.imageSuffix
143                 videoSuffix: cam.videoSuffix
144         }
145
146         MountProtector {
147                 id: mountProtector
148                 path: fileNaming.path
149         }
150
151         BatteryInfo {
152                 id: batteryMonitor
153                 active: cam.running
154
155                 function check() {
156                         if (!checkBattery()) {
157                                 pageStack.currentPage.batteryLow();
158                         }
159                 }
160
161                 onChargingChanged: {
162                         batteryMonitor.check();
163                 }
164
165                 onCriticalChanged: {
166                         batteryMonitor.check();
167                 }
168         }
169
170         function replacePage(file) {
171                 pageStack.replace(Qt.resolvedUrl(file), {cam: cam, dimmer: root.dimmer}, true);
172         }
173
174         function openFile(file) {
175                 pageStack.push(Qt.resolvedUrl(file), {cam: cam, dimmer: root.dimmer});
176         }
177
178         function openFileNow(file) {
179                 pageStack.push(Qt.resolvedUrl(file), {cam: cam, dimmer: root.dimmer}, true);
180         }
181
182         function checkBattery() {
183                 // We are fine if we are connected to the charger:
184                 if (batteryMonitor.charging) {
185                         return true;
186                 }
187
188                 // If we have enough battery then we are fine:
189                 if (!batteryMonitor.critical) {
190                         return true;
191                 }
192
193                 return false;
194         }
195
196         ImageSettings {
197                 id: imageSettings
198                 camera: cam
199                 function setImageResolution() {
200                         if (!imageSettings.setResolution(settings.imageAspectRatio, settings.imageResolution)) {
201                                 showError(qsTr("Failed to set required resolution"));
202                         }
203                 }
204
205                 onReadyChanged: {
206                         if (ready) {
207                                 imageSettings.setImageResolution();
208                         }
209                 }
210         }
211
212         VideoSettings {
213                 id: videoSettings
214                 camera: cam
215
216                 function setVideoResolution() {
217                         if (!videoSettings.setResolution(settings.videoAspectRatio, settings.videoResolution)) {
218                                 showError(qsTr("Failed to set required resolution"));
219                         }
220                 }
221
222                 onReadyChanged: {
223                         if (ready) {
224                                 videoSettings.setVideoResolution();
225                         }
226                 }
227         }
228
229         Connections {
230                 target: settings
231
232                 onImageAspectRatioChanged: {
233                         imageSettings.setImageResolution();
234                 }
235
236                 onImageResolutionChanged: {
237                         imageSettings.setImageResolution();
238                 }
239
240                 onVideoResolutionChanged: {
241                         videoSettings.setVideoResolution();
242                 }
243         }
244
245         Camera {
246                 id: cam
247                 anchors.fill: parent
248
249                 onRoiChanged: roi.normalize = false;
250
251                 GridLines {
252                         x: cam.renderArea.x
253                         y: cam.renderArea.y
254                         width: cam.renderArea.width
255                         height: cam.renderArea.height
256                         visible: settings.gridEnabled
257                 }
258
259                 FocusReticle {
260                         id: focusReticle
261                         cam: cam
262                         visible: pageStack.currentPage && pageStack.currentPage.controlsVisible && pageStack.currentPage.focusReticleVisible && cam && cam.autoFocus.canFocus(cam.scene.value);
263                         cafStatus: cam ? cam.autoFocus.cafStatus : -1
264                         status: cam ? cam.autoFocus.status : -1
265                 }
266
267                 onError: {
268                         if (pipelineManager.error) {
269                                 // Ignore any subsequent errors.
270                                 // Killing pulseaudio while recording will lead to an
271                                 // infinite supply of errors which will break the UI
272                                 // if we show a banner for each.
273                                 return;
274                         }
275
276                         pipelineManager.error = true;
277                         pageStack.currentPage.cameraError();
278                         console.log("Camera error (" + code + "): " + message + " " + debug);
279                         showError(qsTr("Camera error. Please restart the application."));
280
281                         // We cannot stop camera here. Seems there is a race condition somewhere
282                         // which leads to a freeze if we do so.
283                 }
284
285                 onRunningChanged: {
286                         if (!cam.running) {
287                                 mountProtector.unlock();
288                         }
289                 }
290
291                 Component.onDestruction: cam.stop();
292
293                 // We need to show viewfinder below pages.
294                 z: -1
295
296                 Rectangle {
297                         property bool dimmed: false
298                         id: camDimmer
299                         z: 1
300                         anchors.fill: parent
301                         opacity: dimmed ? 1.0 : 0.0
302                         color: "black"
303                         Behavior on opacity {
304                                 PropertyAnimation { duration: 150 }
305                         }
306                 }
307
308                 notifications: Sounds {
309                         id: sounds
310                         mute: !settings.soundEnabled
311                 }
312
313         }
314
315         Binding {
316                 target: cam.flash
317                 property: "value"
318                 when: cam.mode == Camera.ImageMode
319                 value: settings.imageFlashMode
320         }
321
322         Binding {
323                 target: settings
324                 property: "imageFlashMode"
325                 when: cam.mode == Camera.ImageMode
326                 value: cam.flash.value
327         }
328
329         Binding {
330                 target: cam.scene
331                 property: "value"
332                 when: cam.mode == Camera.VideoMode
333                 value: settings.videoSceneMode
334         }
335
336         Binding {
337                 target: cam.scene
338                 property: "value"
339                 when: cam.mode == Camera.ImageMode
340                 value: settings.imageSceneMode
341         }
342
343         Binding {
344                 target: cam.evComp
345                 property: "value"
346                 when: cam.mode == Camera.ImageMode
347                 value: settings.imageEvComp
348         }
349
350         Binding {
351                 target: cam.evComp
352                 property: "value"
353                 when: cam.mode == Camera.VideoMode
354                 value: settings.videoEvComp
355         }
356
357         Binding {
358                 target: settings
359                 property: "imageEvComp"
360                 when: cam.mode == Camera.ImageMode
361                 value: cam.evComp.value
362         }
363
364         Binding {
365                 target: settings
366                 property: "videoEvComp"
367                 when: cam.mode == Camera.VideoMode
368                 value: cam.evComp.value
369         }
370
371         Binding {
372                 target: cam.whiteBalance
373                 property: "value"
374                 when: cam.mode == Camera.ImageMode
375                 value: settings.imageWhiteBalance
376         }
377
378         Binding {
379                 target: cam.whiteBalance
380                 property: "value"
381                 when: cam.mode == Camera.VideoMode
382                 value: settings.videoWhiteBalance
383         }
384
385         Binding {
386                 target: cam.colorTone
387                 property: "value"
388                 when: cam.mode == Camera.ImageMode
389                 value: settings.imageColorFilter
390         }
391
392         Binding {
393                 target: cam.colorTone
394                 property: "value"
395                 when: cam.mode == Camera.VideoMode
396                 value: settings.videoColorFilter
397         }
398
399         Binding {
400                 target: cam.iso
401                 property: "value"
402                 when: cam.mode == Camera.ImageMode
403                 value: settings.imageIso
404         }
405
406         Binding {
407                 target: settings
408                 property: "imageIso"
409                 when: cam.mode == Camera.ImageMode
410                 value: cam.iso.value
411         }
412
413         Binding {
414                 target: cam.videoMute
415                 property: "enabled"
416                 value: settings.videoMuted
417         }
418
419         Binding {
420                 target: cam.roi
421                 property: "enabled"
422                 value: settings.faceDetectionEnabled && !focusReticle.pressed && !focusReticle.touchMode && cam.mode == Camera.ImageMode
423         }
424
425
426         TrackerStore {
427                 id: trackerStore
428                 active: cam.running
429                 manufacturer: deviceInfo.manufacturer
430                 model: deviceInfo.model
431         }
432
433         ModeController {
434                 id: cameraMode
435                 cam: cam
436                 dimmer: root.dimmer
437         }
438
439         Connections {
440                 target: cam
441                 onModeChanged: {
442                         if (cam.mode == Camera.VideoMode) {
443                                 replacePage("VideoPage.qml");
444                         }
445                         else {
446                                 replacePage("ImagePage.qml");
447                         }
448                 }
449         }
450
451         Standby {
452                 policyLost: pipelineManager.state == "policyLost"
453                 show: !pageStack.currentPage || (pageStack.currentPage.activationData.standbyVisible && pageStack.currentPage.status == PageStatus.Active && pipelineManager.showStandBy)
454         }
455
456         DeviceKeys {
457                 id: keys
458                 active: Qt.application.active && pipelineManager.scaleAcquired
459                 repeat: !settings.zoomAsShutter
460         }
461 }