main.qml 12.7 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
    function checkWordset() {
111
        var wordset = ApplicationSettings.wordset
112 113 114
        if(wordset == '')
            // Maybe the wordset has been bundled or copied manually
            // we have to register it if we find it.
115
            wordset = 'data2/words/words.rcc'
116 117 118 119

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

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

192 193
            checkWordset()

194 195 196 197 198
            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
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
            }
        }

Johnny Jazeix's avatar
Johnny Jazeix committed
226 227
        focus: ApplicationInfo.QTVersion >= "5.4.0"

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

235 236 237 238 239 240
                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)
241
                    properties.enterItem.start();
242

243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
                if(properties.name === "pushTransition") {
                    if(properties.enterItem.isDialog) {
                        return pushVTransition
                    } else {
                        return pushHTransition
                    }
                } else {
                    if(properties.exitItem.isDialog) {
                        return popVTransition
                    } else {
                        return popHTransition
                    }

                }
            }

259 260 261 262 263
            function transitionFinished(properties)
            {
                properties.exitItem.opacity = 1
                if(!properties.enterItem.isDialog) {
                    properties.exitItem.stop()
264
                }
265 266 267
            }

            property Component pushHTransition: StackViewTransition {
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
                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
                }
            }
285 286

            property Component popHTransition: StackViewTransition {
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
                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
302 303
                }
            }
304

305 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
            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
340 341
                }
            }
342 343

            property Component replaceTransition: pushHTransition
344 345
        }
    }
346 347

    /// @endcond
348
}