Reworked and fixed pipeline error handling.
[harmattan/cameraplus] / qml / main.qml
1 // -*- qml -*-
2
3 /*!
4  * This file is part of CameraPlus.
5  *
6  * Copyright (C) 2012 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.systeminfo 1.2
29 import QtMobility.location 1.2
30
31 // TODO: postcapture
32 // TODO: flash not ready (battery low or flash not ready message)
33 // TODO: portrait/landscape
34 // TODO: stop viewfinder in settings pages ?
35 // TODO: grid lines, face tracking
36 // TODO: select primary/secondary camera.
37 // TODO: disable debug builds.
38 // TODO: a way to get buffers to the application
39 // TODO: fcam like functionality (precise control over capture parameters).
40 // TODO: mute video sound
41
42 PageStackWindow {
43         id: root
44
45         property alias dimmer: camDimmer
46
47         showStatusBar: false
48
49         Component.onCompleted: {
50                 theme.inverted = true;
51                 // TODO: hardcoding device id
52                 root.resetCamera(0, settings.mode);
53         }
54
55         function showError(msg) {
56                 error.text = msg;
57                 error.show();
58         }
59
60         function resetCamera(deviceId, mode) {
61                 if (!cam.reset(deviceId, mode)) {
62                         showError(qsTr("Failed to set camera device and mode. Please restart the application."));
63                 }
64         }
65
66         PositionSource {
67                 // NOTE: The source will not reset the position when we lose the signal.
68                 // This shouldn't be a big problem as we are course enough.
69                 // If we ever need street level updates then this will be an issue.
70                 id: positionSource
71                 active: settings.useGps
72                 // TODO: we cannot bind to cam.running because camera will stop
73                 // when the connection dialog pops up and we end up with an infinite loop
74                 // active: cam.running && settings.useGps
75                 onPositionChanged: geocode.search(position.coordinate.longitude, position.coordinate.latitude);
76         }
77
78         MetaData {
79                 id: metaData
80                 camera: cam
81                 manufacturer: deviceInfo.manufacturer
82                 model: deviceInfo.model
83                 country: geocode.country
84                 city: geocode.city
85                 suburb: geocode.suburb
86                 longitude: positionSource.position.coordinate.longitude
87                 longitudeValid: positionSource.position.longitudeValid && settings.useGps
88                 latitude: positionSource.position.coordinate.latitude
89                 latitudeValid: positionSource.position.latitudeValid && settings.useGps
90                 elevation: positionSource.position.coordinate.altitude
91                 elevationValid: positionSource.position.altitudeValid && settings.useGps
92                 orientation: orientation.orientation
93                 artist: settings.creatorName
94                 captureDirection: compass.direction
95                 captureDirectionValid: compass.directionValid
96                 horizontalError: positionSource.position.horizontalAccuracy
97                 horizontalErrorValid: positionSource.position.horizontalAccuracyValid && settings.useGps
98                 dateTimeEnabled: true
99         }
100
101         Orientation {
102                 id: orientation
103                 active: cam.running
104         }
105
106         Compass {
107                 id: compass
108                 active: cam.running
109         }
110
111         ReverseGeocode {
112                 id: geocode
113                 active: cam.running && settings.useGps && settings.useGeotags
114         }
115
116         PipelineManager {
117                 id: pipelineManager
118                 camera: cam
119         }
120
121         DeviceInfo {
122                 id: deviceInfo
123         }
124
125         FSMonitor {
126                 id: fileSystem
127         }
128
129         InfoBanner {
130                 id: error
131         }
132
133         Settings {
134                 id: settings
135         }
136
137         FileNaming {
138                 id: fileNaming
139                 imageSuffix: cam.imageSuffix
140                 videoSuffix: cam.videoSuffix
141         }
142
143         MountProtector {
144                 id: mountProtector
145                 path: fileNaming.path
146         }
147
148         function replacePage(file) {
149                 pageStack.replace(Qt.resolvedUrl(file), {cam: cam}, true);
150         }
151
152         function openFile(file) {
153                 pageStack.push(Qt.resolvedUrl(file), {cam: cam});
154         }
155
156         function openFileNow(file) {
157                 pageStack.push(Qt.resolvedUrl(file), {cam: cam}, true);
158         }
159
160         platformStyle: PageStackWindowStyle {
161                 cornersVisible: false
162                 background: ""
163                 backgroundColor: "transparent"
164         }
165
166         ImageSettings {
167                 id: imageSettings
168                 camera: cam
169                 function setImageResolution() {
170                         if (!imageSettings.setResolution(settings.imageAspectRatio, settings.imageResolution)) {
171                                 showError(qsTr("Failed to set required resolution"));
172                         }
173                 }
174
175                 onReadyChanged: {
176                         if (ready) {
177                                 imageSettings.setImageResolution();
178                         }
179                 }
180         }
181
182         VideoSettings {
183                 id: videoSettings
184                 camera: cam
185
186                 function setVideoResolution() {
187                         if (!videoSettings.setResolution(settings.videoAspectRatio, settings.videoResolution)) {
188                                 showError(qsTr("Failed to set required resolution"));
189                         }
190                 }
191
192                 onReadyChanged: {
193                         if (ready) {
194                                 videoSettings.setVideoResolution();
195                         }
196                 }
197         }
198
199         Connections {
200                 target: settings
201
202                 onImageAspectRatioChanged: {
203                         imageSettings.setImageResolution();
204                 }
205
206                 onImageResolutionChanged: {
207                         imageSettings.setImageResolution();
208                 }
209
210                 onVideoResolutionChanged: {
211                         videoSettings.setVideoResolution();
212                 }
213         }
214
215         Camera {
216                 id: cam
217                 anchors.fill: parent
218
219                 FocusReticle {
220                         id: focusReticle
221                         cam: cam
222                         visible: pageStack.currentPage && pageStack.currentPage.controlsVisible && pageStack.currentPage.focusReticleVisible && cam && cam.autoFocus.canFocus(cam.scene.value);
223                         cafStatus: cam ? cam.autoFocus.cafStatus : -1
224                         status: cam ? cam.autoFocus.status : -1
225         }
226
227 /*
228                 onDeviceIdChanged: {
229                         // TODO: is this needed ?
230                         if (platformWindow.active) {
231                                 cam.start();
232                         }
233                 }
234 */
235                 onError: {
236                         if (pipelineManager.error) {
237                                 // Ignore any subsequent errors.
238                                 // Killing pulseaudio while recording will lead to an
239                                 // infinite supply of errors which will break the UI
240                                 // if we show a banner for each.
241                                 return;
242                         }
243
244                         pipelineManager.error = true;
245                         pageStack.currentPage.cameraError();
246                         console.log("Camera error (" + code + "): " + message + " " + debug);
247                         showError(qsTr("Camera error. Please restart the application."));
248
249                         // We cannot stop camera here. Seems there is a race condition somewhere
250                         // which leads to a freeze if we do so.
251                 }
252
253                 onRunningChanged: {
254                         if (!cam.running) {
255                                 mountProtector.unlock();
256                         }
257                 }
258
259                 Component.onDestruction: cam.stop();
260
261                 // We need to show viewfinder below pages.
262                 z: -1
263
264                 Rectangle {
265                         id: camDimmer
266                         z: 1
267                         anchors.fill: parent
268                         opacity: 0
269                         color: "black"
270                 }
271
272                 notifications: Sounds {
273                         id: sounds
274                         mute: !settings.soundEnabled
275                 }
276
277         }
278
279         Binding {
280                 target: cam.flash
281                 property: "value"
282                 when: cam.mode == Camera.ImageMode
283                 value: settings.imageFlashMode
284         }
285
286         Binding {
287                 target: settings
288                 property: "imageFlashMode"
289                 when: cam.mode == Camera.ImageMode
290                 value: cam.flash.value
291         }
292
293         Binding {
294                 target: cam.scene
295                 property: "value"
296                 when: cam.mode == Camera.VideoMode
297                 value: settings.videoSceneMode
298         }
299
300         Binding {
301                 target: cam.scene
302                 property: "value"
303                 when: cam.mode == Camera.ImageMode
304                 value: settings.imageSceneMode
305         }
306
307         Binding {
308                 target: cam.evComp
309                 property: "value"
310                 when: cam.mode == Camera.ImageMode
311                 value: settings.imageEvComp
312         }
313
314         Binding {
315                 target: cam.evComp
316                 property: "value"
317                 when: cam.mode == Camera.VideoMode
318                 value: settings.videoEvComp
319         }
320
321         Binding {
322                 target: settings
323                 property: "imageEvComp"
324                 when: cam.mode == Camera.ImageMode
325                 value: cam.evComp.value
326         }
327
328         Binding {
329                 target: settings
330                 property: "videoEvComp"
331                 when: cam.mode == Camera.VideoMode
332                 value: cam.evComp.value
333         }
334
335         Binding {
336                 target: cam.whiteBalance
337                 property: "value"
338                 when: cam.mode == Camera.ImageMode
339                 value: settings.imageWhiteBalance
340         }
341
342         Binding {
343                 target: cam.whiteBalance
344                 property: "value"
345                 when: cam.mode == Camera.VideoMode
346                 value: settings.videoWhiteBalance
347         }
348
349         Binding {
350                 target: cam.colorTone
351                 property: "value"
352                 when: cam.mode == Camera.ImageMode
353                 value: settings.imageColorFilter
354         }
355
356         Binding {
357                 target: cam.colorTone
358                 property: "value"
359                 when: cam.mode == Camera.VideoMode
360                 value: settings.videoColorFilter
361         }
362
363         Binding {
364                 target: cam.iso
365                 property: "value"
366                 when: cam.mode == Camera.ImageMode
367                 value: settings.imageIso
368         }
369
370         Binding {
371                 target: settings
372                 property: "imageIso"
373                 when: cam.mode == Camera.ImageMode
374                 value: cam.iso.value
375         }
376
377         TrackerStore {
378                 id: trackerStore
379                 active: cam.running
380                 manufacturer: deviceInfo.manufacturer
381                 model: deviceInfo.model
382         }
383
384         ModeController {
385                 id: cameraMode
386                 cam: cam
387                 dimmer: root.dimmer
388         }
389
390         Connections {
391                 target: cam
392                 onModeChanged: {
393                         if (cam.mode == Camera.VideoMode) {
394                                 replacePage("VideoPage.qml");
395                         }
396                         else {
397                                 replacePage("ImagePage.qml");
398                         }
399                 }
400         }
401 }