main.qml 13.5 KB
Newer Older
1 2
/* GCompris - main.qml
 *
3
 * Copyright (C) 2014 Bruno Coudoin <bruno.coudoin@gcompris.net>
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 * Authors:
 *   Bruno Coudoin <bruno.coudoin@gcompris.net>
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program 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 General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, see <http://www.gnu.org/licenses/>.
 */
Bruno Coudoin's avatar
Bruno Coudoin committed
21
import QtQuick 2.2
22
import QtQuick.Controls 1.0
23
import QtQuick.Window 2.1
24
import QtQml 2.2
25

26
import GCompris 1.0
27
import "qrc:/gcompris/src/core/core.js" as Core
28

29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
/**
 * GCompris' main QML file defining the top level window.
 * @ingroup infrastructure
 *
 * Handles application start (Component.onCompleted) and shutdown (onClosing)
 * on the QML layer.
 *
 * Contains the central GCAudio objects audio effects and audio voices.
 *
 * Contains the top level StackView presenting and animating GCompris'
 * full screen views.
 *
 * @sa BarButton, BarEnumContent
 * @inherit QtQuick.Window
 */
44
Window {
45
    id: main
46
    // Start in window mode at full screen size
47 48
    width: ApplicationSettings.previousWidth
    height: ApplicationSettings.previousHeight
49 50
    minimumWidth: 400 * ApplicationInfo.ratio
    minimumHeight: 400 * ApplicationInfo.ratio
51
    title: "GCompris"
52

53 54
    /// @cond INTERNAL_DOCS

55 56 57 58 59 60 61 62 63
    property var applicationState: Qt.application.state

    onApplicationStateChanged: {
        if (ApplicationInfo.isMobile && applicationState != Qt.ApplicationActive) {
            audioVoices.stop();
            audioEffects.stop();
        }
    }

64
    onClosing: Core.quit(main)
65

66
    GCAudio {
Bruno Coudoin's avatar
Bruno Coudoin committed
67 68
        id: audioVoices
        muted: !ApplicationSettings.isAudioVoicesEnabled
69

70 71 72 73 74 75 76 77
        Timer {
            id: delayedWelcomeTimer
            interval: 10000 /* Make sure, that playing welcome.ogg if delayed
                             * because of not yet registered voices, will only
                             * happen max 10sec after startup */
            repeat: false

            onTriggered: {
78
                DownloadManager.voicesRegistered.disconnect(playWelcome);
79 80 81
            }

            function playWelcome() {
82
                audioVoices.append(ApplicationInfo.getAudioFilePath("voices-$CA/$LOCALE/misc/welcome.$CA"));
83 84 85
            }
        }

86
        Component.onCompleted: {
87
            if(ApplicationSettings.isAudioEffectsEnabled)
88
                append(ApplicationInfo.getAudioFilePath("qrc:/gcompris/src/core/resource/intro.$CA"))
89 90 91 92 93 94 95 96

            if (DownloadManager.areVoicesRegistered())
                delayedWelcomeTimer.playWelcome();
            else {
                DownloadManager.voicesRegistered.connect(
                        delayedWelcomeTimer.playWelcome);
                delayedWelcomeTimer.start();
            }
97
        }
Bruno Coudoin's avatar
Bruno Coudoin committed
98 99 100 101 102
    }

    GCAudio {
        id: audioEffects
        muted: !ApplicationSettings.isAudioEffectsEnabled
103
    }
104 105 106

    function playIntroVoice(name) {
        name = name.split("/")[0]
107
        audioVoices.append(ApplicationInfo.getAudioFilePath("voices-$CA/$LOCALE/intro/" + name + ".$CA"))
108 109
    }

110 111 112 113 114 115 116
    function checkWordset() {
        if(ApplicationSettings.wordset == '')
            return

        // check for words.rcc:
        if (DownloadManager.isDataRegistered("words")) {
            // words.rcc is already registered -> nothing to do
117
        } else if(DownloadManager.haveLocalResource(wordset)) {
118 119
            // words.rcc is there -> register old file first
            // then try to update in the background
120 121 122 123 124 125 126 127 128 129 130 131 132
            if(DownloadManager.updateResource(wordset)) {
                DownloadManager.resourceRegistered.connect(function() {
                    // not sure if needed, we check if the resource registered is the one we updated here?
                    if(wordset === arguments[0]) {
                        DownloadManager.resourceRegistered.disconnect(arguments.callee);
                        // force configuration to use the local wordset
                        ApplicationSettings.wordset = 'data2/words/words.rcc'
                    }
                })
            }


        } else if(ApplicationSettings.wordset) { // Only if wordset specified
133 134 135 136 137 138 139
            // words.rcc has not been downloaded yet -> ask for download
            Core.showMessageDialog(
                        main,
                        qsTr("The images for several activities are not yet installed.")
                        + qsTr("Do you want to download them now?"),
                        qsTr("Yes"),
                        function() {
140
                            if (DownloadManager.downloadResource(wordset))
141 142 143 144 145 146 147
                                var downloadDialog = Core.showDownloadDialog(pageView.currentItem, {});
                        },
                        qsTr("No"), null,
                        function() { pageView.currentItem.focus = true }
            );
        }
    }
148 149 150
    ChangeLog {
       id: changelog
    }
151

152
    Component.onCompleted: {
153
        console.log("enter main.qml (run #" + ApplicationSettings.exeCount
154 155 156 157 158
                    + ", ratio=" + ApplicationInfo.ratio
                    + ", fontRatio=" + ApplicationInfo.fontRatio
                    + ", dpi=" + Math.round(Screen.pixelDensity*25.4)
                    + ", sharedWritablePath=" + ApplicationInfo.getSharedWritablePath()
                    + ")");
159
        if (ApplicationSettings.exeCount === 1 && !ApplicationSettings.isKioskMode) {
160 161
            // first run
            var dialog;
162 163 164 165 166 167 168 169 170
            dialog = Core.showMessageDialog(
                        main,
                        qsTr("Welcome to GCompris!") + '\n'
                        + qsTr("You are running GCompris for the first time.") + '\n'
                        + qsTr("You should verify that your application settings especially your language is set correctly, and that all language specific sound files are installed. You can do this in the Preferences Dialog.")
                        + "\n"
                        + qsTr("Have Fun!")
                        + "\n"
                        + qsTr("Your current language is %1 (%2).")
171 172
                          .arg(Qt.locale(ApplicationInfo.getVoicesLocale(ApplicationSettings.locale)).nativeLanguageName)
                          .arg(ApplicationInfo.getVoicesLocale(ApplicationSettings.locale))
173 174
                        + "\n"
                        + qsTr("Do you want to download the corresponding sound files now?"),
175
                        qsTr("Yes"),
176 177
                        function() {
                            if (DownloadManager.downloadResource(
178
                                        DownloadManager.getVoicesResourceForLocale(ApplicationSettings.locale)))
179 180
                                var downloadDialog = Core.showDownloadDialog(pageView.currentItem, {});
                        },
181
                        qsTr("No"), null,
182 183 184 185
                        function() {
                            pageView.currentItem.focus = true
                            checkWordset()
                        }
186
            );
187 188
        }
        else {
189 190
            // Register voices-resources for current locale, updates/downloads only if
            // not prohibited by the settings
191
            if (!DownloadManager.areVoicesRegistered()) {
192
                if (DownloadManager.updateResource(
193
                            DownloadManager.getVoicesResourceForLocale(ApplicationSettings.locale))) {
194 195 196 197 198 199 200 201
                    DownloadManager.resourceRegistered.connect(function() {
                        // not sure if needed, we check if the resource registered is the one we updated here?
                        if(DownloadManager.getVoicesResourceForLocale(ApplicationSettings.locale) === arguments[0]) {
                            DownloadManager.resourceRegistered.disconnect(arguments.callee);
                            checkWordset();
                        }
                    });
                }
202
                else {
203
                    checkWordset()
204
                }
205
            }
206
            else {
207
                checkWordset()
208
            }
209 210 211 212 213 214

            if(changelog.isNewerVersion(ApplicationSettings.lastGCVersionRan, ApplicationInfo.GCVersionCode)) {
                // display log between ApplicationSettings.lastGCVersionRan and ApplicationInfo.GCVersionCode
                var dialog;
                dialog = Core.showMessageDialog(
                main,
Johnny Jazeix's avatar
Johnny Jazeix committed
215
                qsTr("GCompris has been updated! Here are the new changes:<br/>") + changelog.getLogBetween(ApplicationSettings.lastGCVersionRan, ApplicationInfo.GCVersionCode),
216 217 218 219 220 221 222
                "", null,
                "", null,
                function() { pageView.currentItem.focus = true }
                );
                // Store new version
                ApplicationSettings.lastGCVersionRan = ApplicationInfo.GCVersionCode;
            }
223 224
        }
    }
225

226 227 228 229
    Loading {
        id: loading
    }

230 231 232
    StackView {
        id: pageView
        anchors.fill: parent
233 234 235 236
        initialItem: {
            "item": "qrc:/gcompris/src/activities/" + ActivityInfoTree.rootMenu.name,
            "properties": {
                'audioVoices': audioVoices,
237 238
                'audioEffects': audioEffects,
                'loading': loading
239 240 241
            }
        }

Johnny Jazeix's avatar
Johnny Jazeix committed
242 243
        focus: ApplicationInfo.QTVersion >= "5.4.0"

244
        delegate: StackViewDelegate {
245 246 247
            id: root
            function getTransition(properties)
            {
248
                audioVoices.clearQueue()
249 250 251 252 253 254
                if(!properties.exitItem.isDialog &&        // if coming from menu and
                        !properties.enterItem.isDialog)    // going into an activity then
                    playIntroVoice(properties.enterItem.activityInfo.name);    // play intro

                if (!properties.exitItem.isDialog ||       // if coming from menu or
                        properties.enterItem.alwaysStart)  // start signal enforced (for special case like transition from config-dialog to editor)
255
                    properties.enterItem.start();
256

257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272
                if(properties.name === "pushTransition") {
                    if(properties.enterItem.isDialog) {
                        return pushVTransition
                    } else {
                        return pushHTransition
                    }
                } else {
                    if(properties.exitItem.isDialog) {
                        return popVTransition
                    } else {
                        return popHTransition
                    }

                }
            }

273 274 275 276 277
            function transitionFinished(properties)
            {
                properties.exitItem.opacity = 1
                if(!properties.enterItem.isDialog) {
                    properties.exitItem.stop()
278
                }
279 280 281
            }

            property Component pushHTransition: StackViewTransition {
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
                PropertyAnimation {
                    target: enterItem
                    property: "x"
                    from: target.width
                    to: 0
                    duration: 500
                    easing.type: Easing.OutSine
                }
                PropertyAnimation {
                    target: exitItem
                    property: "x"
                    from: 0
                    to: -target.width
                    duration: 500
                    easing.type: Easing.OutSine
                }
            }
299 300

            property Component popHTransition: StackViewTransition {
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
                PropertyAnimation {
                    target: enterItem
                    property: "x"
                    from: -target.width
                    to: 0
                    duration: 500
                    easing.type: Easing.OutSine
                }
                PropertyAnimation {
                    target: exitItem
                    property: "x"
                    from: 0
                    to: target.width
                    duration: 500
                    easing.type: Easing.OutSine
316 317
                }
            }
318

319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353
            property Component pushVTransition: StackViewTransition {
                PropertyAnimation {
                    target: enterItem
                    property: "y"
                    from: -target.height
                    to: 0
                    duration: 500
                    easing.type: Easing.OutSine
                }
                PropertyAnimation {
                    target: exitItem
                    property: "y"
                    from: 0
                    to: target.height
                    duration: 500
                    easing.type: Easing.OutSine
                }
            }

            property Component popVTransition: StackViewTransition {
                PropertyAnimation {
                    target: enterItem
                    property: "y"
                    from: target.height
                    to: 0
                    duration: 500
                    easing.type: Easing.OutSine
                }
                PropertyAnimation {
                    target: exitItem
                    property: "y"
                    from: 0
                    to: -target.height
                    duration: 500
                    easing.type: Easing.OutSine
354 355
                }
            }
356 357

            property Component replaceTransition: pushHTransition
358 359
        }
    }
360 361

    /// @endcond
362
}