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