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