main.qml 13 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
 *
 * 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
19
 *   along with this program; if not, see <https://www.gnu.org/licenses/>.
20
 */
21 22
import QtQuick 2.6
import QtQuick.Controls 1.5
23
import QtQuick.Window 2.2
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
    property var applicationState: Qt.application.state
56
    property bool isMusicalActivityRunning: false
57 58 59 60 61 62 63 64

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

65
    onClosing: Core.quit(main)
66

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

71 72 73 74 75 76 77 78
        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: {
79
                DownloadManager.voicesRegistered.disconnect(playWelcome);
80 81 82
            }

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

87
        Component.onCompleted: {
88
            if(ApplicationSettings.isAudioEffectsEnabled)
Timothée Giet's avatar
Timothée Giet committed
89
                audioVoices.append(ApplicationInfo.getAudioFilePath("qrc:/gcompris/src/core/resource/intro.$CA"))
90 91 92 93 94 95 96 97

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

Timothée Giet's avatar
Timothée Giet committed
101
    GCSfx {
Bruno Coudoin's avatar
Bruno Coudoin committed
102
        id: audioEffects
103
        muted: !ApplicationSettings.isAudioEffectsEnabled && !main.isMusicalActivityRunning
104
    }
105 106 107

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

111
    function checkWordset() {
112
        var wordset = ApplicationSettings.wordset
113 114 115
        if(wordset == '')
            // Maybe the wordset has been bundled or copied manually
            // we have to register it if we find it.
116
            wordset = 'data2/words/words.rcc'
117 118 119 120

        // check for words.rcc:
        if (DownloadManager.isDataRegistered("words")) {
            // words.rcc is already registered -> nothing to do
121
        } else if(DownloadManager.haveLocalResource(wordset)) {
122 123
            // words.rcc is there -> register old file first
            // then try to update in the background
124
            if(DownloadManager.updateResource(wordset)) {
125
                ApplicationSettings.wordset = wordset
126 127
            }
        } else if(ApplicationSettings.wordset) { // Only if wordset specified
128 129 130
            // words.rcc has not been downloaded yet -> ask for download
            Core.showMessageDialog(
                        main,
131 132
                        qsTr("The images for several activities are not yet installed. " +
                        "Do you want to download them now?"),
133 134
                        qsTr("Yes"),
                        function() {
135
                            if (DownloadManager.downloadResource(wordset))
136 137 138 139 140 141 142
                                var downloadDialog = Core.showDownloadDialog(pageView.currentItem, {});
                        },
                        qsTr("No"), null,
                        function() { pageView.currentItem.focus = true }
            );
        }
    }
143 144 145
    ChangeLog {
       id: changelog
    }
146

147
    Component.onCompleted: {
148
        console.log("enter main.qml (run #" + ApplicationSettings.exeCount
149 150 151
                    + ", ratio=" + ApplicationInfo.ratio
                    + ", fontRatio=" + ApplicationInfo.fontRatio
                    + ", dpi=" + Math.round(Screen.pixelDensity*25.4)
152
                    + ", userDataPath=" + ApplicationSettings.userDataPath
153
                    + ")");
154 155 156
        if (ApplicationSettings.exeCount === 1 &&
                !ApplicationSettings.isKioskMode &&
                ApplicationInfo.isDownloadAllowed) {
157 158
            // first run
            var dialog;
159 160 161 162 163 164 165 166 167
            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).")
168 169
                          .arg(Qt.locale(ApplicationInfo.getVoicesLocale(ApplicationSettings.locale)).nativeLanguageName)
                          .arg(ApplicationInfo.getVoicesLocale(ApplicationSettings.locale))
170 171
                        + "\n"
                        + qsTr("Do you want to download the corresponding sound files now?"),
172
                        qsTr("Yes"),
173 174
                        function() {
                            if (DownloadManager.downloadResource(
175
                                        DownloadManager.getVoicesResourceForLocale(ApplicationSettings.locale)))
176 177
                                var downloadDialog = Core.showDownloadDialog(pageView.currentItem, {});
                        },
178
                        qsTr("No"), null,
179 180 181 182
                        function() {
                            pageView.currentItem.focus = true
                            checkWordset()
                        }
183
             );
184 185
        }
        else {
186 187
            // Register voices-resources for current locale, updates/downloads only if
            // not prohibited by the settings
188
            if (!DownloadManager.areVoicesRegistered()) {
189 190
                DownloadManager.updateResource(
                    DownloadManager.getVoicesResourceForLocale(ApplicationSettings.locale));
191
            }
192

193 194
            checkWordset()

195 196
            if(changelog.isNewerVersion(ApplicationSettings.lastGCVersionRan, ApplicationInfo.GCVersionCode)) {
                // display log between ApplicationSettings.lastGCVersionRan and ApplicationInfo.GCVersionCode
197
                Core.showMessageDialog(
198
                main,
Johnny Jazeix's avatar
Johnny Jazeix committed
199
                qsTr("GCompris has been updated! Here are the new changes:<br/>") + changelog.getLogBetween(ApplicationSettings.lastGCVersionRan, ApplicationInfo.GCVersionCode),
200 201 202 203 204 205 206
                "", null,
                "", null,
                function() { pageView.currentItem.focus = true }
                );
                // Store new version
                ApplicationSettings.lastGCVersionRan = ApplicationInfo.GCVersionCode;
            }
207 208
        }
    }
209

210 211 212 213
    Loading {
        id: loading
    }

214 215 216
    StackView {
        id: pageView
        anchors.fill: parent
217 218 219 220
        initialItem: {
            "item": "qrc:/gcompris/src/activities/" + ActivityInfoTree.rootMenu.name,
            "properties": {
                'audioVoices': audioVoices,
221 222
                'audioEffects': audioEffects,
                'loading': loading
223 224 225
            }
        }

226
        focus: true
Johnny Jazeix's avatar
Johnny Jazeix committed
227

228
        delegate: StackViewDelegate {
229 230 231
            id: root
            function getTransition(properties)
            {
232
                audioVoices.clearQueue()
233 234
                audioVoices.stop()

235 236 237 238
                if(!properties.exitItem.isDialog &&        // if coming from menu and
                        !properties.enterItem.isDialog)    // going into an activity then
                    playIntroVoice(properties.enterItem.activityInfo.name);    // play intro

239 240 241
                // is this if needed?
                //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)
242
                    properties.enterItem.start();
243

244 245 246 247
                if(properties.name === "pushTransition") {
                    if(properties.enterItem.isDialog) {
                        return pushVTransition
                    } else {
248 249
                        if(properties.enterItem.isMusicalActivity)
                            main.isMusicalActivityRunning = true
250 251 252 253 254 255
                        return pushHTransition
                    }
                } else {
                    if(properties.exitItem.isDialog) {
                        return popVTransition
                    } else {
256
                        main.isMusicalActivityRunning = false
257 258 259 260 261 262
                        return popHTransition
                    }

                }
            }

263 264 265 266 267
            function transitionFinished(properties)
            {
                properties.exitItem.opacity = 1
                if(!properties.enterItem.isDialog) {
                    properties.exitItem.stop()
268
                }
269 270 271
            }

            property Component pushHTransition: StackViewTransition {
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
                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
                }
            }
289 290

            property Component popHTransition: StackViewTransition {
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
                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
306 307
                }
            }
308

309 310 311 312 313 314 315 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
            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
344 345
                }
            }
346 347

            property Component replaceTransition: pushHTransition
348 349
        }
    }
350
    /// @endcond
351
}