diff --git a/src/activities/menu/BackgroundMusicList.qml b/src/activities/menu/BackgroundMusicList.qml index d23e5b5b5..476d44643 100644 --- a/src/activities/menu/BackgroundMusicList.qml +++ b/src/activities/menu/BackgroundMusicList.qml @@ -1,183 +1,236 @@ /* GCompris - BackgroundMusicList.qml * * Copyright (C) 2019 Aman Kumar Gupta * * Authors: * Aman Kumar Gupta (Qt Quick) * * 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 . */ import QtQuick 2.6 import QtQuick.Controls 1.5 import GCompris 1.0 import "../../core" import "qrc:/gcompris/src/core/core.js" as Core Rectangle { id: dialogBackground color: "#696da3" border.color: "black" border.width: 1 z: 10000 anchors.fill: parent visible: false Keys.onEscapePressed: close() signal close property bool horizontalLayout: dialogBackground.width >= dialogBackground.height + property int margin30: Math.round(30 * ApplicationInfo.ratio) Row { spacing: 2 Item { width: 10; height: 1 } Column { spacing: 10 anchors.top: parent.top Item { width: 1; height: 10 } Rectangle { id: titleRectangle color: "#e6e6e6" radius: 6.0 width: dialogBackground.width - 30 height: title.height * 1.2 border.color: "black" border.width: 2 GCText { id: title - text: qsTr("Pieces of background music") + text: qsTr("Background music") width: dialogBackground.width - 30 horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter color: "black" fontSize: 20 font.weight: Font.DemiBold wrapMode: Text.WordWrap } } Rectangle { color: "#e6e6e6" radius: 6.0 width: dialogBackground.width - 30 height: dialogBackground.height - 100 border.color: "black" border.width: 2 anchors.margins: 100 Flickable { id: flickableList anchors.fill: parent - anchors.topMargin: 10 - anchors.leftMargin: 20 - contentWidth: parent.width - contentHeight: musicGrid.height + anchors.margins: 10 * ApplicationInfo.ratio + contentHeight: musicGrid.height + musicInfo.height + margin30 flickableDirection: Flickable.VerticalFlick clip: true Flow { id: musicGrid width: parent.width - spacing: 40 + spacing: 10 * ApplicationInfo.ratio anchors.horizontalCenter: parent.horizontalCenter Repeater { model: dialogActivityConfig.configItem ? dialogActivityConfig.configItem.allBackgroundMusic : 0 Item { - width: dialogBackground.horizontalLayout ? dialogBackground.width / 5 : dialogBackground.width / 4 - height: dialogBackground.height / 5 + width: (musicGrid.width - margin30) * 0.33 + height: title.height * 2 Button { text: modelData.slice(0, modelData.lastIndexOf('.')) onClicked: { if(dialogActivityConfig.configItem.filteredBackgroundMusic.indexOf(modelData) == -1) { // Keep the filtered playlist sorted w.r.t to their positions in "allBackgroundMusic" to maintain their playing order var musicOriginalPosition = dialogActivityConfig.configItem.allBackgroundMusic.indexOf(modelData) var i = 0 while(i < dialogActivityConfig.configItem.filteredBackgroundMusic.length) { var filteredMusicName = dialogActivityConfig.configItem.filteredBackgroundMusic[i] if(dialogActivityConfig.configItem.allBackgroundMusic.indexOf(filteredMusicName) > musicOriginalPosition) break i++ } dialogActivityConfig.configItem.filteredBackgroundMusic.splice(i, 0, modelData) } else { dialogActivityConfig.configItem.filteredBackgroundMusic.splice(dialogActivityConfig.configItem.filteredBackgroundMusic.indexOf(modelData), 1) if(dialogActivityConfig.configItem.filteredBackgroundMusic == 0) { dialogActivityConfig.configItem.filteredBackgroundMusic.push(modelData) selectedIcon.visible = false Core.showMessageDialog(dialogBackground, qsTr("Disable the background music if you don't want to play them."), "", null, "", null, null ); } } selectedIcon.visible = !selectedIcon.visible } width: parent.width height: parent.height * 0.8 style: GCButtonStyle { theme: "dark" } Image { id: selectedIcon source: "qrc:/gcompris/src/core/resource/apply.svg" sourceSize.width: height sourceSize.height: height width: height height: parent.height / 4 anchors.bottom: parent.bottom anchors.right: parent.right anchors.margins: 2 visible: dialogActivityConfig.configItem.filteredBackgroundMusic ? dialogActivityConfig.configItem.filteredBackgroundMusic.indexOf(modelData) != -1 : false } } } } } + + Column { + id: musicInfo + spacing: 10 * ApplicationInfo.ratio + width: parent.width + anchors.top: musicGrid.bottom + anchors.leftMargin: 20 + + GCText { + //: Current background music playing + text: qsTr("Now Playing:") + width: dialogBackground.width - 30 + horizontalAlignment: Text.AlignHCenter + color: "black" + fontSize: mediumSize + wrapMode: Text.WordWrap + } + GCText { + //: Title of the current background music playing + text: qsTr("Title: %1").arg(backgroundMusic.metaDataMusic[0]) + width: dialogBackground.width - 30 + horizontalAlignment: Text.AlignLeft + color: "black" + fontSize: smallSize + wrapMode: Text.WordWrap + } + GCText { + //: Artist of the current background music playing + text: qsTr("Artist: %1").arg(backgroundMusic.metaDataMusic[1]) + width: dialogBackground.width - 30 + horizontalAlignment: Text.AlignLeft + color: "black" + fontSize: smallSize + wrapMode: Text.WordWrap + } + GCText { + //: Date of the current background music playing + text: qsTr("Date: %1").arg(backgroundMusic.metaDataMusic[2]) + width: dialogBackground.width - 30 + horizontalAlignment: Text.AlignLeft + color: "black" + fontSize: smallSize + wrapMode: Text.WordWrap + } + GCText { + //: Copyright of the current background music playing + text: qsTr("Copyright: %1").arg(backgroundMusic.metaDataMusic[3]) + width: dialogBackground.width - 30 + horizontalAlignment: Text.AlignLeft + color: "black" + fontSize: smallSize + wrapMode: Text.WordWrap + } + } } // The scroll buttons GCButtonScroll { anchors.right: parent.right anchors.rightMargin: 5 * ApplicationInfo.ratio anchors.bottom: flickableList.bottom anchors.bottomMargin: 30 * ApplicationInfo.ratio width: parent.width / 20 height: width * heightRatio onUp: flickableList.flick(0, 1400) onDown: flickableList.flick(0, -1400) upVisible: (flickableList.visibleArea.yPosition <= 0) ? false : true downVisible: ((flickableList.visibleArea.yPosition + flickableList.visibleArea.heightRatio) >= 1) ? false : true } } Item { width: 1; height: 10 } } } GCButtonCancel { onClose: { parent.close() } } } diff --git a/src/activities/menu/ConfigurationItem.qml b/src/activities/menu/ConfigurationItem.qml index 8951305e6..57fbfe278 100644 --- a/src/activities/menu/ConfigurationItem.qml +++ b/src/activities/menu/ConfigurationItem.qml @@ -1,894 +1,884 @@ /* GCompris - ConfigurationItem.qml * * Copyright (C) 2014-2016 Johnny Jazeix * * Authors: * Johnny Jazeix * * 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 . */ import QtQuick 2.6 import QtQuick.Controls 1.5 import QtQuick.Controls.Styles 1.4 import GCompris 1.0 import QtMultimedia 5.0 import "../../core" import "qrc:/gcompris/src/core/core.js" as Core Item { id: dialogConfig property var languages: allLangs.languages height: column.height LanguageList { id: allLangs } Column { id: column - spacing: 10 + spacing: 10 * ApplicationInfo.ratio width: parent.width move: Transition { NumberAnimation { properties: "x,y"; duration: 120 } } // Put configuration here Row { id: demoModeBox width: parent.width - spacing: 10 + spacing: 10 * ApplicationInfo.ratio property bool checked: !ApplicationSettings.isDemoMode Image { sourceSize.height: 50 * ApplicationInfo.ratio source: demoModeBox.checked ? "qrc:/gcompris/src/core/resource/apply.svg" : "qrc:/gcompris/src/core/resource/cancel.svg" MouseArea { anchors.fill: parent onClicked: { if(ApplicationSettings.isDemoMode) ApplicationSettings.isDemoMode = false } } } Button { width: parent.parent.width - 50 * ApplicationInfo.ratio - 10 * 2 height: parent.height enabled: ApplicationSettings.isDemoMode anchors.leftMargin: 10 anchors.verticalCenter: parent.verticalCenter text: demoModeBox.checked ? qsTr("You have the full version") : qsTr("Buy the full version").toUpperCase() style: ButtonStyle { background: Rectangle { implicitWidth: 100 implicitHeight: 25 border.width: control.activeFocus ? 4 : 2 border.color: "black" radius: 10 gradient: Gradient { GradientStop { position: 0 ; color: control.pressed ? "#87ff5c" : ApplicationSettings.isDemoMode ? "#ffe85c" : "#EEEEEE"} GradientStop { position: 1 ; color: control.pressed ? "#44ff00" : ApplicationSettings.isDemoMode ? "#f8d600" : "#AAAAAA"} } } label: GCText { text: control.text horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter wrapMode: Text.WordWrap } } onClicked: { if(ApplicationSettings.activationMode == 1) { if(ApplicationSettings.isDemoMode) ApplicationSettings.isDemoMode = false } else if(ApplicationSettings.activationMode == 2) { activationCodeEntry.visible = !activationCodeEntry.visible } } } } Column { id: activationCodeEntry width: parent.width spacing: 10 visible: false opacity: 0 Behavior on opacity { NumberAnimation { duration: 200 } } onVisibleChanged: { if(visible) { activationInput.forceActiveFocus() activationInput.cursorPosition = 0 opacity = 1 } else { activationInput.focus = false opacity = 0 } } GCText { id: activationInstruction fontSize: regularSize color: "black" style: Text.Outline styleColor: "white" horizontalAlignment: Text.AlignHCenter width: parent.width wrapMode: TextEdit.WordWrap text: qsTr("On https://gcompris.net " + "you will find the instructions to obtain an activation code.") Component.onCompleted: ApplicationInfo.isDownloadAllowed ? linkActivated.connect(Qt.openUrlExternally) : null } TextInput { id: activationInput width: parent.width focus: true font.weight: Font.DemiBold font.pointSize: ApplicationSettings.baseFontSize + 14 * ApplicationInfo.fontRatio color: 'black' horizontalAlignment: Text.AlignHCenter inputMask: '>HHHH-HHHH-HHHH;#' text: ApplicationSettings.codeKey onTextChanged: { var code = text.replace(/-/g,'') var codeValidity = ApplicationSettings.checkActivationCode(code); switch (codeValidity) { case 0: activationMsg.text = qsTr('Enter your activation code'); break; case 1: activationMsg.text = qsTr('Sorry, your code is too old for this version of GCompris'); break; case 2: activationMsg.text = qsTr('Your code is valid, thanks a lot for your support'); activationCodeEntry.visible = false ApplicationSettings.codeKey = code break; } } } GCText { id: activationMsg width: parent.width color: "black" fontSize: regularSize horizontalAlignment: TextInput.AlignHCenter wrapMode: TextEdit.WordWrap } } GCDialogCheckBox { id: displayLockedActivitiesBox text: qsTr("Show locked activities") visible: ApplicationSettings.isDemoMode checked: showLockedActivities onCheckedChanged: { showLockedActivities = checked; } } GCDialogCheckBox { id: enableAudioVoicesBox text: qsTr("Enable audio voices") checked: isAudioVoicesEnabled onCheckedChanged: { isAudioVoicesEnabled = checked; } } GCDialogCheckBox { id: enableAudioEffectsBox text: qsTr("Enable audio effects") checked: isAudioEffectsEnabled onCheckedChanged: { isAudioEffectsEnabled = checked; } } Flow { - spacing: 5 + spacing: 5 * ApplicationInfo.ratio + width: parent.width + GCText { + id: audioEffectsVolumeText + text: qsTr("Audio effects volume") + fontSize: mediumSize + wrapMode: Text.WordWrap + } + } + + Flow { + spacing: 5 * ApplicationInfo.ratio width: parent.width GCSlider { id: audioEffectsVolumeSlider width: 250 * ApplicationInfo.ratio maximumValue: 10 minimumValue: 0 value: audioEffectsVolume * 10 onValueChanged: ApplicationSettings.audioEffectsVolume = value / 10; scrollEnabled: false } - GCText { - id: audioEffectsVolumeText - text: qsTr("Audio effects volume") - fontSize: mediumSize - wrapMode: Text.WordWrap - } } GCDialogCheckBox { id: enableBackgroundMusicBox text: qsTr("Enable background music") checked: isBackgroundMusicEnabled onCheckedChanged: { isBackgroundMusicEnabled = checked; } } - + Flow { - width: parent.width spacing: 5 * ApplicationInfo.ratio - + width: parent.width GCText { text: qsTr("Background Music") fontSize: mediumSize height: 50 * ApplicationInfo.ratio } + Image { + source: "qrc:/gcompris/src/core/resource/bar_next.svg" + height: Math.min(50 * ApplicationInfo.ratio, parent.width / 8) + sourceSize.width: height - // Padding - Item { - height: 1 - width: 10 * ApplicationInfo.ratio + MouseArea { + anchors.fill: parent + enabled: (backgroundMusic.playbackState == Audio.PlayingState && !backgroundMusic.muted) + onClicked: backgroundMusic.nextAudio() + } } - + } + + Flow { + width: parent.width + spacing: 5 * ApplicationInfo.ratio + Button { id: backgroundMusicName height: 30 * ApplicationInfo.ratio + width: background.width * 0.8 text: { if(backgroundMusic.playbackState != Audio.PlayingState) return qsTr("Not playing") - return configItem.extractMusicNameFromPath(backgroundMusic.source) + else if (backgroundMusic.metaDataMusic[0] != undefined) + return (qsTr("Title: %1 Artist: %2").arg(backgroundMusic.metaDataMusic[0]).arg(backgroundMusic.metaDataMusic[1])) + else if (String(backgroundMusic.source).slice(0, 37) === "qrc:/gcompris/src/core/resource/intro") + return qsTr("Introduction music") + return "" } style: GCButtonStyle {} onClicked: { dialogConfig.visible = false backgroundMusicList.visible = true } } - - // Padding - Item { - height: 1 - width: 10 * ApplicationInfo.ratio - } - - Image { - source: "qrc:/gcompris/src/core/resource/bar_next.svg" - sourceSize.height: Math.min(50 * ApplicationInfo.ratio, parent.width / 8) - - MouseArea { - anchors.fill: parent - enabled: (backgroundMusic.playbackState == Audio.PlayingState && !backgroundMusic.muted) - onClicked: backgroundMusic.nextAudio() - } - } } Flow { spacing: 5 width: parent.width - GCSlider { - id: backgroundMusicVolumeSlider - width: 250 * ApplicationInfo.ratio - maximumValue: 10 - minimumValue: 0 - value: backgroundMusicVolume * 10 - onValueChanged: ApplicationSettings.backgroundMusicVolume = value / 10; - scrollEnabled: false - } GCText { id: backgroundMusicVolumeText text: qsTr("Background music volume") fontSize: mediumSize wrapMode: Text.WordWrap } } + GCSlider { + id: backgroundMusicVolumeSlider + width: 250 * ApplicationInfo.ratio + maximumValue: 10 + minimumValue: 0 + value: backgroundMusicVolume * 10 + onValueChanged: ApplicationSettings.backgroundMusicVolume = value / 10; + scrollEnabled: false + } + GCDialogCheckBox { id: enableFullscreenBox text: qsTr("Fullscreen") checked: isFullscreen onCheckedChanged: { isFullscreen = checked; } visible: !ApplicationInfo.isMobile } GCDialogCheckBox { id: enableVirtualKeyboardBox text: qsTr("Virtual Keyboard") checked: isVirtualKeyboard onCheckedChanged: { isVirtualKeyboard = checked; } } GCDialogCheckBox { id: enableAutomaticDownloadsBox checked: isAutomaticDownloadsEnabled text: qsTr("Enable automatic downloads/updates of sound files") visible: ApplicationInfo.isDownloadAllowed onCheckedChanged: { isAutomaticDownloadsEnabled = checked; } } /* Technically wordset config is a string that holds the wordset name or '' for the * internal wordset. But as we support only internal and words its best to show the * user a boolean choice. */ GCDialogCheckBox { id: wordsetBox checked: DownloadManager.isDataRegistered("words") text: enabled ? qsTr("Use full word image set") : qsTr("Download full word image set") visible: ApplicationInfo.isDownloadAllowed enabled: !DownloadManager.isDataRegistered("words") onCheckedChanged: { wordset = checked ? 'data2/words/words.rcc' : ''; } } GCDialogCheckBox { id: sectionVisibleBox checked: sectionVisible text: qsTr("The activity section menu is visible") onCheckedChanged: { sectionVisible = checked; } } Flow { spacing: 5 width: parent.width GCComboBox { id: fontBox model: fonts background: dialogActivityConfig label: qsTr("Font selector") } } + GCText { + id: baseFontSizeText + text: qsTr("Font size") + fontSize: mediumSize + wrapMode: Text.WordWrap + } Flow { spacing: 5 width: parent.width GCSlider { id: baseFontSizeSlider width: 250 * ApplicationInfo.ratio maximumValue: ApplicationSettings.baseFontSizeMax minimumValue: ApplicationSettings.baseFontSizeMin value: baseFontSize onValueChanged: ApplicationSettings.baseFontSize = value; scrollEnabled: false } - GCText { - id: baseFontSizeText - text: qsTr("Font size") - fontSize: mediumSize - wrapMode: Text.WordWrap - } Button { height: 30 * ApplicationInfo.ratio text: qsTr("Default"); style: GCButtonStyle {} onClicked: baseFontSizeSlider.value = 0.0 } } Flow { spacing: 5 width: parent.width GCComboBox { id: fontCapitalizationBox model: fontCapitalizationModel background: dialogActivityConfig label: qsTr("Font Capitalization") } } + GCText { + id: fontLetterSpacingText + text: qsTr("Font letter spacing") + fontSize: mediumSize + wrapMode: Text.WordWrap + } Flow { spacing: 5 width: parent.width GCSlider { id: fontLetterSpacingSlider width: 250 * ApplicationInfo.ratio maximumValue: ApplicationSettings.fontLetterSpacingMax minimumValue: ApplicationSettings.fontLetterSpacingMin value: fontLetterSpacing onValueChanged: ApplicationSettings.fontLetterSpacing = value; scrollEnabled: false } - GCText { - id: fontLetterSpacingText - text: qsTr("Font letter spacing") - fontSize: mediumSize - wrapMode: Text.WordWrap - } Button { height: 30 * ApplicationInfo.ratio text: qsTr("Default"); style: GCButtonStyle {} onClicked: fontLetterSpacingSlider.value = ApplicationSettings.fontLetterSpacingMin } } Flow { spacing: 5 width: parent.width GCComboBox { id: languageBox model: dialogConfig.languages background: dialogActivityConfig onCurrentIndexChanged: voicesRow.localeChanged(); label: qsTr("Language selector") } } Flow { id: voicesRow width: parent.width spacing: 5 * ApplicationInfo.ratio property bool haveLocalResource: false function localeChanged() { var language = dialogConfig.languages[languageBox.currentIndex].text; voicesRow.haveLocalResource = DownloadManager.isDataRegistered( "voices-" + ApplicationInfo.CompressedAudio + "/" + ApplicationInfo.getVoicesLocale(dialogConfig.languages[languageBox.currentIndex].locale) ) } Connections { target: DownloadManager onDownloadFinished: voicesRow.localeChanged() } GCText { id: voicesText text: qsTr("Localized voices") fontSize: mediumSize wrapMode: Text.WordWrap } Image { id: voicesImage sourceSize.height: 30 * ApplicationInfo.ratio source: voicesRow.haveLocalResource ? "qrc:/gcompris/src/core/resource/apply.svg" : "qrc:/gcompris/src/core/resource/cancel.svg" } Button { id: voicesButton height: 30 * ApplicationInfo.ratio visible: ApplicationInfo.isDownloadAllowed text: voicesRow.haveLocalResource ? qsTr("Check for updates") : qsTr("Download") style: GCButtonStyle {} onClicked: { if (DownloadManager.downloadResource( DownloadManager.getVoicesResourceForLocale(dialogConfig.languages[languageBox.currentIndex].locale))) { var downloadDialog = Core.showDownloadDialog(dialogConfig.parent.rootItem, {}); } } } } Flow { width: parent.width spacing: 5 * ApplicationInfo.ratio GCText { text: qsTr("Difficulty filter:") fontSize: mediumSize height: 50 * ApplicationInfo.ratio } + } - // Padding - Item { - height: 1 - width: 10 * ApplicationInfo.ratio - } + Flow { + width: parent.width + spacing: 5 * ApplicationInfo.ratio Image { source: "qrc:/gcompris/src/core/resource/bar_next.svg" sourceSize.height: Math.min(50 * ApplicationInfo.ratio, parent.width / 8) MouseArea { anchors.fill: parent onClicked: { filterRepeater.setMin(filterRepeater.min + 1) } } } - // Padding - Item { - height: 1 - width: 5 * ApplicationInfo.ratio - } - // Level filtering Repeater { id: filterRepeater model: 6 property int min: ApplicationSettings.filterLevelMin property int max: ApplicationSettings.filterLevelMax function setMin(value) { var newMin if(min < 1) newMin = 1 else if(min > 6) newMin = 6 else if(max >= value) newMin = value if(newMin) ApplicationSettings.filterLevelMin = newMin } function setMax(value) { var newMax if(max < 1) newMax = 1 else if(max > 6) newMax = 6 else if(min <= value) newMax = value if(newMax) ApplicationSettings.filterLevelMax = newMax } Image { source: "qrc:/gcompris/src/core/resource/difficulty" + (modelData + 1) + ".svg"; sourceSize.width: Math.min(50 * ApplicationInfo.ratio, parent.width / 8) opacity: modelData + 1 >= filterRepeater.min && modelData + 1 <= filterRepeater.max ? 1 : 0.4 property int value: modelData + 1 MouseArea { anchors.fill: parent onClicked: { if(parent.value < filterRepeater.max) { if(parent.opacity == 1) filterRepeater.setMin(parent.value + 1) else filterRepeater.setMin(parent.value) } else if(parent.value > filterRepeater.min) { if(parent.opacity == 1) filterRepeater.setMax(parent.value - 1) else filterRepeater.setMax(parent.value) } } } } } - // Padding - Item { - height: 1 - width: 5 * ApplicationInfo.ratio - } - Image { source: "qrc:/gcompris/src/core/resource/bar_previous.svg" sourceSize.height: Math.min(50 * ApplicationInfo.ratio, parent.width / 8) MouseArea { anchors.fill: parent onClicked: { filterRepeater.setMax(filterRepeater.max - 1) } } } } } property bool showLockedActivities: ApplicationSettings.showLockedActivities property bool isAudioVoicesEnabled: ApplicationSettings.isAudioVoicesEnabled property bool isAudioEffectsEnabled: ApplicationSettings.isAudioEffectsEnabled property bool isBackgroundMusicEnabled: ApplicationSettings.isBackgroundMusicEnabled property bool isFullscreen: ApplicationSettings.isFullscreen property bool isVirtualKeyboard: ApplicationSettings.isVirtualKeyboard property bool isAutomaticDownloadsEnabled: ApplicationSettings.isAutomaticDownloadsEnabled property bool sectionVisible: ApplicationSettings.sectionVisible property string wordset: ApplicationSettings.wordset property var filteredBackgroundMusic: ApplicationSettings.filteredBackgroundMusic property var allBackgroundMusic: ApplicationInfo.getBackgroundMusicFromRcc() property int baseFontSize // don't bind to ApplicationSettings.baseFontSize property real fontLetterSpacing // don't bind to ApplicationSettings.fontLetterSpacing // or we get a binding loop warning property real backgroundMusicVolume property real audioEffectsVolume function extractMusicNameFromPath(musicPath) { var musicDirectoryPath = ApplicationInfo.getAudioFilePath("backgroundMusic/") var musicName = String(musicPath) musicName = musicName.slice(musicDirectoryPath.length, musicName.length) return musicName.slice(0, musicName.lastIndexOf('.')) } function loadFromConfig() { // Synchronize settings with data showLockedActivities = ApplicationSettings.showLockedActivities isAudioVoicesEnabled = ApplicationSettings.isAudioVoicesEnabled enableAudioVoicesBox.checked = isAudioVoicesEnabled isAudioEffectsEnabled = ApplicationSettings.isAudioEffectsEnabled enableAudioEffectsBox.checked = isAudioEffectsEnabled isBackgroundMusicEnabled = ApplicationSettings.isBackgroundMusicEnabled enableBackgroundMusicBox.checked = isBackgroundMusicEnabled isFullscreen = ApplicationSettings.isFullscreen enableFullscreenBox.checked = isFullscreen isVirtualKeyboard = ApplicationSettings.isVirtualKeyboard enableVirtualKeyboardBox.checked = isVirtualKeyboard isAutomaticDownloadsEnabled = ApplicationSettings.isAutomaticDownloadsEnabled enableAutomaticDownloadsBox.checked = isAutomaticDownloadsEnabled sectionVisible = ApplicationSettings.sectionVisible sectionVisibleBox.checked = sectionVisible wordset = ApplicationSettings.wordset wordsetBox.checked = DownloadManager.isDataRegistered("words") || ApplicationSettings.wordset == 'data2/words/words.rcc' wordsetBox.enabled = !DownloadManager.isDataRegistered("words") baseFontSize = ApplicationSettings.baseFontSize fontLetterSpacing = ApplicationSettings.fontLetterSpacing backgroundMusicVolume = ApplicationSettings.backgroundMusicVolume audioEffectsVolume = ApplicationSettings.audioEffectsVolume filteredBackgroundMusic = ApplicationSettings.filteredBackgroundMusic allBackgroundMusic = ApplicationInfo.getBackgroundMusicFromRcc() if(filteredBackgroundMusic.length === 0) filteredBackgroundMusic = allBackgroundMusic // Set locale for(var i = 0 ; i < dialogConfig.languages.length ; i ++) { if(dialogConfig.languages[i].locale === ApplicationSettings.locale) { languageBox.currentIndex = i; break; } } // Set font for(var i = 0 ; i < fonts.count ; i ++) { if(fonts.get(i).text == ApplicationSettings.font) { fontBox.currentIndex = i; break; } } // Set font capitalization for(var i = 0 ; i < fontCapitalizationModel.length ; i ++) { if(fontCapitalizationModel[i].value == ApplicationSettings.fontCapitalization) { fontCapitalizationBox.currentIndex = i; break; } } } function save() { ApplicationSettings.showLockedActivities = showLockedActivities ApplicationSettings.isAudioVoicesEnabled = isAudioVoicesEnabled ApplicationSettings.isAudioEffectsEnabled = isAudioEffectsEnabled ApplicationSettings.isBackgroundMusicEnabled = isBackgroundMusicEnabled ApplicationSettings.filteredBackgroundMusic = filteredBackgroundMusic ApplicationSettings.isFullscreen = isFullscreen ApplicationSettings.isVirtualKeyboard = isVirtualKeyboard ApplicationSettings.isAutomaticDownloadsEnabled = isAutomaticDownloadsEnabled ApplicationSettings.sectionVisible = sectionVisible ApplicationSettings.wordset = wordset ApplicationSettings.isEmbeddedFont = fonts.get(fontBox.currentIndex).isLocalResource; ApplicationSettings.font = fonts.get(fontBox.currentIndex).text ApplicationSettings.fontCapitalization = fontCapitalizationModel[fontCapitalizationBox.currentIndex].value ApplicationSettings.saveBaseFontSize(); ApplicationSettings.notifyFontLetterSpacingChanged(); if (ApplicationSettings.locale != dialogConfig.languages[languageBox.currentIndex].locale) { ApplicationSettings.locale = dialogConfig.languages[languageBox.currentIndex].locale if(ApplicationInfo.isDownloadAllowed && !DownloadManager.isDataRegistered( "voices-" + ApplicationInfo.CompressedAudio + "/" + ApplicationInfo.getVoicesLocale(dialogConfig.languages[languageBox.currentIndex].locale) )) { // ask for downloading new voices Core.showMessageDialog(main, qsTr("You selected a new locale. You need to restart GCompris to play in your new locale.
Do you want to download the corresponding sound files now?"), qsTr("Yes"), function() { // yes -> start download if (DownloadManager.downloadResource( DownloadManager.getVoicesResourceForLocale(ApplicationSettings.locale))) var downloadDialog = Core.showDownloadDialog(main, {}); }, qsTr("No"), null, null ); } else { // check for updates or/and register new voices DownloadManager.updateResource( DownloadManager.getVoicesResourceForLocale(ApplicationSettings.locale)) } } // download words.rcc if needed if(ApplicationSettings.wordset != "") { // we want to use the external dataset, it is either in // words/words.rcc or full-${CA}.rcc if(DownloadManager.isDataRegistered("words")) { // we either have it, we try to update in the background // or we are downloading it if(DownloadManager.haveLocalResource(wordset)) DownloadManager.updateResource(wordset) } else { // download automatically if automatic download else ask for download if(isAutomaticDownloadsEnabled) { var prevAutomaticDownload = ApplicationSettings.isAutomaticDownloadsEnabled ApplicationSettings.isAutomaticDownloadsEnabled = true; DownloadManager.updateResource(wordset); ApplicationSettings.isAutomaticDownloadsEnabled = prevAutomaticDownload } else { Core.showMessageDialog(main, qsTr("The images for several activities are not yet installed. ") + qsTr("Do you want to download them now?"), qsTr("Yes"), function() { if (DownloadManager.downloadResource(wordset)) var downloadDialog = Core.showDownloadDialog(pageView.currentItem, {}); }, qsTr("No"), function() { ApplicationSettings.wordset = '' }, null ); } } } // download backgroundMusic.rcc if needed if(DownloadManager.isDataRegistered("backgroundMusic")) { // we either have it, we try to update in the background // or we are downloading it if(DownloadManager.haveLocalResource(DownloadManager.getBackgroundMusicResources())) DownloadManager.updateResource(DownloadManager.getBackgroundMusicResources()) } else { // download automatically if automatic download else ask for download if(isAutomaticDownloadsEnabled) { var prevAutomaticDownload = ApplicationSettings.isAutomaticDownloadsEnabled ApplicationSettings.isAutomaticDownloadsEnabled = true; DownloadManager.updateResource(DownloadManager.getBackgroundMusicResources()); ApplicationSettings.isAutomaticDownloadsEnabled = prevAutomaticDownload } else { Core.showMessageDialog(main, qsTr("The background music is not yet installed. ") + qsTr("Do you want to download it now?"), qsTr("Yes"), function() { if (DownloadManager.downloadResource(DownloadManager.getBackgroundMusicResources())) var downloadDialog = Core.showDownloadDialog(pageView.currentItem, {}); }, qsTr("No"),null ); } } } ListModel { id: fonts Component.onCompleted: { var systemFonts = Qt.fontFamilies(); var rccFonts = ApplicationInfo.getFontsFromRcc(); // Remove explicitly all *symbol* and *ding* fonts var excludedFonts = ApplicationInfo.getSystemExcludedFonts(); excludedFonts.push("ding"); excludedFonts.push("symbol"); // first display fonts from rcc for(var i = 0 ; i < rccFonts.length ; ++ i) { // Append fonts from resources fonts.append({ "text": rccFonts[i], "isLocalResource": true }); } for(var i = 0 ; i < systemFonts.length ; ++ i) { var isExcluded = false; var systemFont = systemFonts[i].toLowerCase(); // Remove symbol fonts for(var j = 0 ; j < excludedFonts.length ; ++ j) { if(systemFont.indexOf(excludedFonts[j].toLowerCase()) != -1) { isExcluded = true; break; } } // Remove fonts from rcc (if you have a default font from rcc, Qt will add it to systemFonts) for(var j = 0 ; j < rccFonts.length ; ++ j) { if(rccFonts[j].toLowerCase().indexOf(systemFont) != -1) { isExcluded = true; break; } } // Finally, we know if we add this font or not if(!isExcluded) { fonts.append({ "text": systemFonts[i], "isLocalResource": false }); } } } } property var fontCapitalizationModel: [ { text: qsTr("Mixed case (default)"), value: Font.MixedCase }, { text: qsTr("All uppercase"), value: Font.AllUppercase }, { text: qsTr("All lowercase"), value: Font.AllLowercase } ] function isFilteredBackgroundMusicChanged() { initialFilteredMusic = ApplicationSettings.filteredBackgroundMusic if(initialFilteredMusic.length != filteredBackgroundMusic.length) return true for(var i = 0; i < initialFilteredMusic.length; i++) if(filteredBackgroundMusic.indexOf(initialFilteredMusic[i]) == -1) return true return false } function hasConfigChanged() { return (ApplicationSettings.locale !== dialogConfig.languages[languageBox.currentIndex].locale || (ApplicationSettings.sectionVisible != sectionVisible) || (ApplicationSettings.wordset != wordset) || (ApplicationSettings.font != fonts.get(fontBox.currentIndex).text) || (ApplicationSettings.isEmbeddedFont != fonts.get(fontBox.currentIndex).isLocalResource) || (ApplicationSettings.isEmbeddedFont != fonts.get(fontBox.currentIndex).isLocalResource) || (ApplicationSettings.fontCapitalization != fontCapitalizationModel[(fontcapitalizationBox.currentIndex)].value) || (ApplicationSettings.fontLetterSpacing != fontLetterSpacing) || (ApplicationSettings.isAudioVoicesEnabled != isAudioVoicesEnabled) || (ApplicationSettings.isAudioEffectsEnabled != isAudioEffectsEnabled) || (ApplicationSettings.isBackgroundMusicEnabled != isBackgroundMusicEnabled) || (ApplicationSettings.isFullscreen != isFullscreen) || (ApplicationSettings.isVirtualKeyboard != isVirtualKeyboard) || (ApplicationSettings.isAutomaticDownloadsEnabled != isAutomaticDownloadsEnabled) || (ApplicationSettings.baseFontSize != baseFontSize) || (ApplicationSettings.showLockedActivities != showLockedActivities) || isFilteredBackgroundMusicChanged() ); } } diff --git a/src/core/ApplicationSettings.cpp b/src/core/ApplicationSettings.cpp index 2e04e7b9e..9b48f6cb6 100644 --- a/src/core/ApplicationSettings.cpp +++ b/src/core/ApplicationSettings.cpp @@ -1,548 +1,548 @@ /* GCompris - ApplicationSettings.cpp * * Copyright (C) 2014-2016 Johnny Jazeix * * Authors: * Johnny Jazeix * * 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 . */ #include "ApplicationSettings.h" #include "ApplicationInfo.h" #include "DownloadManager.h" #include #include #include #include #include #include #include #include #define GC_DEFAULT_FONT QLatin1String("Andika-R.otf") #define GC_DEFAULT_FONT_CAPITALIZATION 0 // Font.MixedCase #define GC_DEFAULT_FONT_LETTER_SPACING 0 static const char *GENERAL_GROUP_KEY = "General"; static const char *ADMIN_GROUP_KEY = "Admin"; static const char *INTERNAL_GROUP_KEY = "Internal"; static const char *FAVORITE_GROUP_KEY = "Favorite"; static const char *FULLSCREEN_KEY = "fullscreen"; static const char *PREVIOUS_HEIGHT_KEY = "previousHeight"; static const char *PREVIOUS_WIDTH_KEY = "previousWidth"; static const char *SHOW_LOCKED_ACTIVITIES_KEY = "showLockedActivities"; static const char *ENABLE_AUDIO_VOICES_KEY = "enableAudioVoices"; static const char *ENABLE_AUDIO_EFFECTS_KEY = "enableAudioEffects"; static const char *ENABLE_BACKGROUND_MUSIC_KEY = "enableBackgroundMusic"; static const char *VIRTUALKEYBOARD_KEY = "virtualKeyboard"; static const char *LOCALE_KEY = "locale"; static const char *FONT_KEY = "font"; static const char *IS_CURRENT_FONT_EMBEDDED = "isCurrentFontEmbedded"; static const char *ENABLE_AUTOMATIC_DOWNLOADS = "enableAutomaticDownloads"; static const char *FILTERED_BACKGROUND_MUSIC_KEY = "filteredBackgroundMusic"; static const char *BACKGROUND_MUSIC_VOLUME_KEY = "backgroundMusicVolume"; static const char *AUDIO_EFFECTS_VOLUME_KEY = "audioEffectsVolume"; static const char *FILTERED_BACKGROUND_MUSIC_KEY = "filteredBackgroundMusic"; static const char *BACKGROUND_MUSIC_VOLUME_KEY = "backgroundMusicVolume"; static const char *AUDIO_EFFECTS_VOLUME_KEY = "audioEffectsVolume"; static const char *DOWNLOAD_SERVER_URL_KEY = "downloadServerUrl"; static const char *CACHE_PATH_KEY = "cachePath"; static const char *USERDATA_PATH_KEY = "userDataPath"; static const char *RENDERER_KEY = "renderer"; static const char *EXE_COUNT_KEY = "exeCount"; static const char *LAST_GC_VERSION_RAN = "lastGCVersionRan"; static const char *FILTER_LEVEL_MIN = "filterLevelMin"; static const char *FILTER_LEVEL_MAX = "filterLevelMax"; static const char *BASE_FONT_SIZE_KEY = "baseFontSize"; static const char *FONT_CAPITALIZATION = "fontCapitalization"; static const char *FONT_LETTER_SPACING = "fontLetterSpacing"; static const char *DEFAULT_CURSOR = "defaultCursor"; static const char *NO_CURSOR = "noCursor"; static const char *DEMO_KEY = "demo"; static const char *CODE_KEY = "key"; static const char *KIOSK_KEY = "kiosk"; static const char *SECTION_VISIBLE = "sectionVisible"; static const char *WORDSET = "wordset"; static const char *PROGRESS_KEY = "progress"; static const char *DEFAULT_DOWNLOAD_SERVER = "https://cdn.kde.org/gcompris"; ApplicationSettings *ApplicationSettings::m_instance = nullptr; ApplicationSettings::ApplicationSettings(const QString &configPath, QObject *parent): QObject(parent), m_baseFontSizeMin(-7), m_baseFontSizeMax(7), m_fontLetterSpacingMin(0.0), m_fontLetterSpacingMax(8.0), m_config(configPath, QSettings::IniFormat) { const QRect &screenSize = QGuiApplication::screens().at(0)->availableGeometry(); // initialize from settings file or default // general group m_config.beginGroup(GENERAL_GROUP_KEY); m_isAudioEffectsEnabled = m_config.value(ENABLE_AUDIO_EFFECTS_KEY, true).toBool(); m_isBackgroundMusicEnabled = m_config.value(ENABLE_BACKGROUND_MUSIC_KEY, true).toBool(); m_isFullscreen = m_config.value(FULLSCREEN_KEY, true).toBool(); m_previousHeight = m_config.value(PREVIOUS_HEIGHT_KEY, screenSize.height()).toUInt(); m_previousWidth = m_config.value(PREVIOUS_WIDTH_KEY, screenSize.width()).toUInt(); m_isAudioVoicesEnabled = m_config.value(ENABLE_AUDIO_VOICES_KEY, true).toBool(); m_isVirtualKeyboard = m_config.value(VIRTUALKEYBOARD_KEY, ApplicationInfo::getInstance()->isMobile()).toBool(); m_locale = m_config.value(LOCALE_KEY, GC_DEFAULT_LOCALE).toString(); m_font = m_config.value(FONT_KEY, GC_DEFAULT_FONT).toString(); if(m_font == QLatin1String("Andika-R.ttf")) m_font = "Andika-R.otf"; m_fontCapitalization = m_config.value(FONT_CAPITALIZATION, GC_DEFAULT_FONT_CAPITALIZATION).toUInt(); m_fontLetterSpacing = m_config.value(FONT_LETTER_SPACING, GC_DEFAULT_FONT_LETTER_SPACING).toReal(); m_isEmbeddedFont = m_config.value(IS_CURRENT_FONT_EMBEDDED, true).toBool(); m_filteredBackgroundMusic = m_config.value(FILTERED_BACKGROUND_MUSIC_KEY, ApplicationInfo::getInstance()->getBackgroundMusicFromRcc()).toStringList(); - m_backgroundMusicVolume = m_config.value(BACKGROUND_MUSIC_VOLUME_KEY, 1).toReal(); - m_audioEffectsVolume = m_config.value(AUDIO_EFFECTS_VOLUME_KEY, 1).toReal(); + m_backgroundMusicVolume = m_config.value(BACKGROUND_MUSIC_VOLUME_KEY, 0.4).toReal(); + m_audioEffectsVolume = m_config.value(AUDIO_EFFECTS_VOLUME_KEY, 0.7).toReal(); // Init the activation mode if(QLatin1String(ACTIVATION_MODE) == "no") m_activationMode = 0; else if(QLatin1String(ACTIVATION_MODE) == "inapp") m_activationMode = 1; else if(QLatin1String(ACTIVATION_MODE) == "internal") m_activationMode = 2; else qFatal("Unknown activation mode"); // Set the demo mode if(QLatin1String(ACTIVATION_MODE) != "no") m_isDemoMode = m_config.value(DEMO_KEY, true).toBool(); else m_isDemoMode = false; m_codeKey = m_config.value(CODE_KEY, "").toString(); #if defined(WITH_KIOSK_MODE) m_isKioskMode = m_config.value(KIOSK_KEY, true).toBool(); #else m_isKioskMode = m_config.value(KIOSK_KEY, false).toBool(); #endif // Option only useful if we are in demo mode (else all the activities are available and unlocked) // By default, all the activities are displayed (even locked ones) m_showLockedActivities = m_config.value(SHOW_LOCKED_ACTIVITIES_KEY, m_isDemoMode).toBool(); m_sectionVisible = m_config.value(SECTION_VISIBLE, true).toBool(); m_wordset = m_config.value(WORDSET, "").toString(); m_isAutomaticDownloadsEnabled = m_config.value(ENABLE_AUTOMATIC_DOWNLOADS, !ApplicationInfo::getInstance()->isMobile() && ApplicationInfo::isDownloadAllowed()).toBool(); m_filterLevelMin = m_config.value(FILTER_LEVEL_MIN, 1).toUInt(); m_filterLevelMax = m_config.value(FILTER_LEVEL_MAX, 6).toUInt(); m_defaultCursor = m_config.value(DEFAULT_CURSOR, false).toBool(); m_noCursor = m_config.value(NO_CURSOR, false).toBool(); m_baseFontSize = m_config.value(BASE_FONT_SIZE_KEY, 0).toInt(); m_config.sync(); // make sure all defaults are written back m_config.endGroup(); // admin group m_config.beginGroup(ADMIN_GROUP_KEY); m_downloadServerUrl = m_config.value(DOWNLOAD_SERVER_URL_KEY, QLatin1String(DEFAULT_DOWNLOAD_SERVER)).toString(); if(m_downloadServerUrl == "http://gcompris.net") { setDownloadServerUrl(DEFAULT_DOWNLOAD_SERVER); } m_cachePath = m_config.value(CACHE_PATH_KEY, QStandardPaths::writableLocation(QStandardPaths::CacheLocation)).toString(); m_userDataPath = m_config.value(USERDATA_PATH_KEY, QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/GCompris")).toString(); m_renderer = m_config.value(RENDERER_KEY, GRAPHICAL_RENDERER).toString(); m_config.endGroup(); // internal group m_config.beginGroup(INTERNAL_GROUP_KEY); m_exeCount = m_config.value(EXE_COUNT_KEY, 0).toUInt(); m_lastGCVersionRan = m_config.value(LAST_GC_VERSION_RAN, 0).toUInt(); m_config.endGroup(); // no group m_isBarHidden = false; connect(this, &ApplicationSettings::showLockedActivitiesChanged, this, &ApplicationSettings::notifyShowLockedActivitiesChanged); connect(this, &ApplicationSettings::audioVoicesEnabledChanged, this, &ApplicationSettings::notifyAudioVoicesEnabledChanged); connect(this, &ApplicationSettings::audioEffectsEnabledChanged, this, &ApplicationSettings::notifyAudioEffectsEnabledChanged); connect(this, &ApplicationSettings::backgroundMusicEnabledChanged, this, &ApplicationSettings::notifyBackgroundMusicEnabledChanged); connect(this, &ApplicationSettings::filteredBackgroundMusicChanged, this, &ApplicationSettings::notifyFilteredBackgroundMusicChanged); connect(this, &ApplicationSettings::fullscreenChanged, this, &ApplicationSettings::notifyFullscreenChanged); connect(this, &ApplicationSettings::previousHeightChanged, this, &ApplicationSettings::notifyPreviousHeightChanged); connect(this, &ApplicationSettings::previousWidthChanged, this, &ApplicationSettings::notifyPreviousWidthChanged); connect(this, &ApplicationSettings::localeChanged, this, &ApplicationSettings::notifyLocaleChanged); connect(this, &ApplicationSettings::fontChanged, this, &ApplicationSettings::notifyFontChanged); connect(this, &ApplicationSettings::virtualKeyboardChanged, this, &ApplicationSettings::notifyVirtualKeyboardChanged); connect(this, &ApplicationSettings::automaticDownloadsEnabledChanged, this, &ApplicationSettings::notifyAutomaticDownloadsEnabledChanged); connect(this, &ApplicationSettings::filterLevelMinChanged, this, &ApplicationSettings::notifyFilterLevelMinChanged); connect(this, &ApplicationSettings::filterLevelMaxChanged, this, &ApplicationSettings::notifyFilterLevelMaxChanged); connect(this, &ApplicationSettings::sectionVisibleChanged, this, &ApplicationSettings::notifySectionVisibleChanged); connect(this, &ApplicationSettings::wordsetChanged, this, &ApplicationSettings::notifyWordsetChanged); connect(this, &ApplicationSettings::demoModeChanged, this, &ApplicationSettings::notifyDemoModeChanged); connect(this, &ApplicationSettings::codeKeyChanged, this, &ApplicationSettings::notifyCodeKeyChanged); connect(this, &ApplicationSettings::kioskModeChanged, this, &ApplicationSettings::notifyKioskModeChanged); connect(this, &ApplicationSettings::downloadServerUrlChanged, this, &ApplicationSettings::notifyDownloadServerUrlChanged); connect(this, &ApplicationSettings::cachePathChanged, this, &ApplicationSettings::notifyCachePathChanged); connect(this, &ApplicationSettings::userDataPathChanged, this, &ApplicationSettings::notifyUserDataPathChanged); connect(this, &ApplicationSettings::rendererChanged, this, &ApplicationSettings::notifyRendererChanged); connect(this, &ApplicationSettings::exeCountChanged, this, &ApplicationSettings::notifyExeCountChanged); connect(this, &ApplicationSettings::barHiddenChanged, this, &ApplicationSettings::notifyBarHiddenChanged); connect(this, &ApplicationSettings::lastGCVersionRanChanged, this, &ApplicationSettings::notifyLastGCVersionRanChanged); connect(this, &ApplicationSettings::backgroundMusicVolumeChanged, this, &ApplicationSettings::notifyBackgroundMusicVolumeChanged); connect(this, &ApplicationSettings::audioEffectsVolumeChanged, this, &ApplicationSettings::notifyAudioEffectsVolumeChanged); } ApplicationSettings::~ApplicationSettings() { // make sure settings file is up2date: // general group m_config.beginGroup(GENERAL_GROUP_KEY); m_config.setValue(SHOW_LOCKED_ACTIVITIES_KEY, m_showLockedActivities); m_config.setValue(ENABLE_AUDIO_VOICES_KEY, m_isAudioVoicesEnabled); m_config.setValue(ENABLE_BACKGROUND_MUSIC_KEY, m_isBackgroundMusicEnabled); m_config.setValue(FILTERED_BACKGROUND_MUSIC_KEY, m_filteredBackgroundMusic); m_config.setValue(BACKGROUND_MUSIC_VOLUME_KEY, m_backgroundMusicVolume); m_config.setValue(AUDIO_EFFECTS_VOLUME_KEY, m_audioEffectsVolume); m_config.setValue(LOCALE_KEY, m_locale); m_config.setValue(FONT_KEY, m_font); m_config.setValue(IS_CURRENT_FONT_EMBEDDED, m_isEmbeddedFont); m_config.setValue(FULLSCREEN_KEY, m_isFullscreen); m_config.setValue(PREVIOUS_HEIGHT_KEY, m_previousHeight); m_config.setValue(PREVIOUS_WIDTH_KEY, m_previousWidth); m_config.setValue(VIRTUALKEYBOARD_KEY, m_isVirtualKeyboard); m_config.setValue(ENABLE_AUTOMATIC_DOWNLOADS, m_isAutomaticDownloadsEnabled); m_config.setValue(FILTER_LEVEL_MIN, m_filterLevelMin); m_config.setValue(FILTER_LEVEL_MAX, m_filterLevelMax); m_config.setValue(DEMO_KEY, m_isDemoMode); m_config.setValue(CODE_KEY, m_codeKey); m_config.setValue(KIOSK_KEY, m_isKioskMode); m_config.setValue(SECTION_VISIBLE, m_sectionVisible); m_config.setValue(WORDSET, m_wordset); m_config.setValue(DEFAULT_CURSOR, m_defaultCursor); m_config.setValue(NO_CURSOR, m_noCursor); m_config.setValue(BASE_FONT_SIZE_KEY, m_baseFontSize); m_config.setValue(FONT_CAPITALIZATION, m_fontCapitalization); m_config.setValue(FONT_LETTER_SPACING, m_fontLetterSpacing); m_config.endGroup(); // admin group m_config.beginGroup(ADMIN_GROUP_KEY); m_config.setValue(DOWNLOAD_SERVER_URL_KEY, m_downloadServerUrl); m_config.setValue(CACHE_PATH_KEY, m_cachePath); m_config.setValue(USERDATA_PATH_KEY, m_userDataPath); m_config.setValue(RENDERER_KEY, m_renderer); m_config.endGroup(); // internal group m_config.beginGroup(INTERNAL_GROUP_KEY); m_config.setValue(EXE_COUNT_KEY, m_exeCount); m_config.setValue(LAST_GC_VERSION_RAN, m_lastGCVersionRan); m_config.endGroup(); m_config.sync(); m_instance = nullptr; } void ApplicationSettings::notifyShowLockedActivitiesChanged() { updateValueInConfig(GENERAL_GROUP_KEY, SHOW_LOCKED_ACTIVITIES_KEY, m_showLockedActivities); qDebug() << "notifyShowLockedActivitiesChanged: " << m_showLockedActivities; } void ApplicationSettings::notifyAudioVoicesEnabledChanged() { updateValueInConfig(GENERAL_GROUP_KEY, ENABLE_AUDIO_VOICES_KEY, m_isAudioVoicesEnabled); qDebug() << "notifyAudioVoices: " << m_isAudioVoicesEnabled; } void ApplicationSettings::notifyAudioEffectsEnabledChanged() { updateValueInConfig(GENERAL_GROUP_KEY, ENABLE_AUDIO_EFFECTS_KEY, m_isAudioEffectsEnabled); qDebug() << "notifyAudioEffects: " << m_isAudioEffectsEnabled; } void ApplicationSettings::notifyBackgroundMusicEnabledChanged() { updateValueInConfig(GENERAL_GROUP_KEY, ENABLE_BACKGROUND_MUSIC_KEY, m_isBackgroundMusicEnabled); qDebug() << "notifyBackgroundMusic: " << m_isBackgroundMusicEnabled; } void ApplicationSettings::notifyFilteredBackgroundMusicChanged() { updateValueInConfig(GENERAL_GROUP_KEY, FILTERED_BACKGROUND_MUSIC_KEY, m_filteredBackgroundMusic); qDebug()<<"filteredBackgroundMusic: " << m_filteredBackgroundMusic; } void ApplicationSettings::notifyBackgroundMusicVolumeChanged() { updateValueInConfig(GENERAL_GROUP_KEY, BACKGROUND_MUSIC_VOLUME_KEY, m_backgroundMusicVolume); qDebug()<<"backgroundMusicVolume: " << m_backgroundMusicVolume; } void ApplicationSettings::notifyAudioEffectsVolumeChanged() { updateValueInConfig(GENERAL_GROUP_KEY, AUDIO_EFFECTS_VOLUME_KEY, m_audioEffectsVolume); qDebug()<<"audioEffectsVolume: " << m_audioEffectsVolume; } void ApplicationSettings::notifyLocaleChanged() { updateValueInConfig(GENERAL_GROUP_KEY, LOCALE_KEY, m_locale); qDebug() << "new locale: " << m_locale; } void ApplicationSettings::notifyFontChanged() { updateValueInConfig(GENERAL_GROUP_KEY, FONT_KEY, m_font); qDebug() << "new font: " << m_font; } void ApplicationSettings::notifyEmbeddedFontChanged() { updateValueInConfig(GENERAL_GROUP_KEY, IS_CURRENT_FONT_EMBEDDED, m_isEmbeddedFont); qDebug() << "new font is embedded: " << m_isEmbeddedFont; } void ApplicationSettings::notifyFontCapitalizationChanged() { updateValueInConfig(GENERAL_GROUP_KEY, FONT_CAPITALIZATION, m_fontCapitalization); qDebug() << "new fontCapitalization: " << m_fontCapitalization; } void ApplicationSettings::notifyFontLetterSpacingChanged() { updateValueInConfig(GENERAL_GROUP_KEY, FONT_LETTER_SPACING, m_fontLetterSpacing); qDebug() << "new fontLetterSpacing: " << m_fontLetterSpacing; } void ApplicationSettings::notifyFullscreenChanged() { updateValueInConfig(GENERAL_GROUP_KEY, FULLSCREEN_KEY, m_isFullscreen); qDebug() << "fullscreen set to: " << m_isFullscreen; } void ApplicationSettings::notifyPreviousHeightChanged() { updateValueInConfig(GENERAL_GROUP_KEY, PREVIOUS_HEIGHT_KEY, m_previousHeight); qDebug() << "previous height set to: " << m_previousHeight; } void ApplicationSettings::notifyPreviousWidthChanged() { updateValueInConfig(GENERAL_GROUP_KEY, PREVIOUS_WIDTH_KEY, m_previousWidth); qDebug() << "previous width set to: " << m_previousWidth; } void ApplicationSettings::notifyVirtualKeyboardChanged() { updateValueInConfig(GENERAL_GROUP_KEY, VIRTUALKEYBOARD_KEY, m_isVirtualKeyboard); qDebug() << "virtualkeyboard set to: " << m_isVirtualKeyboard; } bool ApplicationSettings::isAutomaticDownloadsEnabled() const { return m_isAutomaticDownloadsEnabled && ApplicationInfo::isDownloadAllowed(); } void ApplicationSettings::setIsAutomaticDownloadsEnabled(const bool newIsAutomaticDownloadsEnabled) { if(ApplicationInfo::isDownloadAllowed()) { m_isAutomaticDownloadsEnabled = newIsAutomaticDownloadsEnabled; emit automaticDownloadsEnabledChanged(); } } void ApplicationSettings::notifyAutomaticDownloadsEnabledChanged() { updateValueInConfig(GENERAL_GROUP_KEY, ENABLE_AUTOMATIC_DOWNLOADS, m_isAutomaticDownloadsEnabled); qDebug() << "enableAutomaticDownloads set to: " << m_isAutomaticDownloadsEnabled; } void ApplicationSettings::notifyFilterLevelMinChanged() { updateValueInConfig(GENERAL_GROUP_KEY, FILTER_LEVEL_MIN, m_filterLevelMin); qDebug() << "filterLevelMin set to: " << m_filterLevelMin; } void ApplicationSettings::notifyFilterLevelMaxChanged() { updateValueInConfig(GENERAL_GROUP_KEY, FILTER_LEVEL_MAX, m_filterLevelMax); qDebug() << "filterLevelMax set to: " << m_filterLevelMax; } void ApplicationSettings::notifyDemoModeChanged() { updateValueInConfig(GENERAL_GROUP_KEY, DEMO_KEY, m_isDemoMode); qDebug() << "notifyDemoMode: " << m_isDemoMode; } void ApplicationSettings::notifyCodeKeyChanged() { checkPayment(); if(!m_isDemoMode) updateValueInConfig(GENERAL_GROUP_KEY, CODE_KEY, m_codeKey); qDebug() << "notifyCodeKey: " << m_codeKey; } void ApplicationSettings::notifyKioskModeChanged() { updateValueInConfig(GENERAL_GROUP_KEY, KIOSK_KEY, m_isKioskMode); qDebug() << "notifyKioskMode: " << m_isKioskMode; } void ApplicationSettings::notifySectionVisibleChanged() { updateValueInConfig(GENERAL_GROUP_KEY, SECTION_VISIBLE, m_sectionVisible); qDebug() << "notifySectionVisible: " << m_sectionVisible; } void ApplicationSettings::notifyWordsetChanged() { if(!m_wordset.isEmpty() && DownloadManager::getInstance()->haveLocalResource(m_wordset) && !DownloadManager::getInstance()->isDataRegistered("words")) { // words.rcc is there -> register old file first // then try to update in the background DownloadManager::getInstance()->updateResource(m_wordset); } updateValueInConfig(GENERAL_GROUP_KEY, WORDSET, m_wordset); qDebug() << "notifyWordset: " << m_wordset; } void ApplicationSettings::notifyDownloadServerUrlChanged() { updateValueInConfig(ADMIN_GROUP_KEY, DOWNLOAD_SERVER_URL_KEY, m_downloadServerUrl); qDebug() << "downloadServerUrl set to: " << m_downloadServerUrl; } void ApplicationSettings::notifyCachePathChanged() { updateValueInConfig(ADMIN_GROUP_KEY, CACHE_PATH_KEY, m_cachePath); qDebug() << "cachePath set to: " << m_cachePath; } void ApplicationSettings::notifyUserDataPathChanged() { updateValueInConfig(ADMIN_GROUP_KEY, USERDATA_PATH_KEY, m_userDataPath); qDebug() << "userDataPath set to: " << m_userDataPath; } void ApplicationSettings::notifyRendererChanged() { updateValueInConfig(ADMIN_GROUP_KEY, RENDERER_KEY, m_renderer); qDebug() << "renderer set to: " << m_renderer; } void ApplicationSettings::notifyExeCountChanged() { updateValueInConfig(INTERNAL_GROUP_KEY, EXE_COUNT_KEY, m_exeCount); qDebug() << "exeCount set to: " << m_exeCount; } void ApplicationSettings::notifyLastGCVersionRanChanged() { updateValueInConfig(INTERNAL_GROUP_KEY, LAST_GC_VERSION_RAN, m_lastGCVersionRan); qDebug() << "lastVersionRan set to: " << m_lastGCVersionRan; } void ApplicationSettings::notifyBarHiddenChanged() { qDebug() << "is bar hidden: " << m_isBarHidden; } void ApplicationSettings::saveBaseFontSize() { updateValueInConfig(GENERAL_GROUP_KEY, BASE_FONT_SIZE_KEY, m_baseFontSize); } void ApplicationSettings::saveActivityConfiguration(const QString &activity, const QVariantMap &data) { qDebug() << "save configuration for:" << activity; QMapIterator i(data); while (i.hasNext()) { i.next(); updateValueInConfig(activity, i.key(), i.value()); } } QVariantMap ApplicationSettings::loadActivityConfiguration(const QString &activity) { qDebug() << "load configuration for:" << activity; m_config.beginGroup(activity); QStringList keys = m_config.childKeys(); QVariantMap data; for(const QString &key : keys) { data[key] = m_config.value(key); } m_config.endGroup(); return data; } void ApplicationSettings::setFavorite(const QString &activity, bool favorite) { updateValueInConfig(FAVORITE_GROUP_KEY, activity, favorite); } bool ApplicationSettings::isFavorite(const QString &activity) { m_config.beginGroup(FAVORITE_GROUP_KEY); bool favorite = m_config.value(activity, false).toBool(); m_config.endGroup(); return favorite; } template void ApplicationSettings::updateValueInConfig(const QString& group, const QString& key, const T& value) { m_config.beginGroup(group); m_config.setValue(key, value); m_config.endGroup(); m_config.sync(); } int ApplicationSettings::loadActivityProgress(const QString &activity) { int progress = 0; m_config.beginGroup(activity); progress = m_config.value(PROGRESS_KEY, 0).toInt(); m_config.endGroup(); qDebug() << "loaded progress for activity" << activity << ":" << progress; return progress; } void ApplicationSettings::saveActivityProgress(const QString &activity, int progress) { updateValueInConfig(activity, PROGRESS_KEY, progress); } bool ApplicationSettings::useExternalWordset() { return !m_wordset.isEmpty() && DownloadManager::getInstance()->isDataRegistered("words"); } QObject *ApplicationSettings::applicationSettingsProvider(QQmlEngine *engine, QJSEngine *scriptEngine) { Q_UNUSED(engine) Q_UNUSED(scriptEngine) ApplicationSettings* appSettings = getInstance(); return appSettings; } diff --git a/src/core/GCAudio.qml b/src/core/GCAudio.qml index efad26b5b..87ce996fc 100644 --- a/src/core/GCAudio.qml +++ b/src/core/GCAudio.qml @@ -1,252 +1,270 @@ /* GCompris - GCAudio.qml * * Copyright (C) 2014 Johnny Jazeix * * Authors: * Johnny Jazeix * * 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 . */ import QtQuick 2.6 import QtMultimedia 5.0 import GCompris 1.0 /** * A QML component for audio playback. * @ingroup components * * Wrapper component around QtQuick's Audio element, handling all audio * playback in GCompris uniformly. * * It maintains a queue of audio-sources (@ref files) that are played back * sequentially, to which the user can enqueue files that should be scheduled * for playback. * * To make sure an audio voice will be localized, replace the locale part * of the file by '$LOCALE'. * * To makes sure that all audio sources are normalized with respect to * ApplicationInfo.CompressedAudio replace the 'ogg' part of the file by * '$CA'. * * @inherit QtQuick.Item */ Item { id: gcaudio /** * type:bool * Whether audio should be muted. */ property bool muted /** * type:url * URL to the audio source to be played back. */ property alias source: audio.source /** * type: positive real number less than 1 * Determines intensity of the audio. */ property alias volume: audio.volume /** * type:bool * Whether the audio element contains audio. */ property bool hasAudio: audio.hasAudio /** * type:string * Detailed error message in case of playback errors. */ property alias errorString: audio.errorString + /** + * type:bool + * check if the player is for background music + */ + property bool isBackgroundMusic: false + + /** + * type:array + * background music metadata + */ + property var metaDataMusic: ["", "", "", ""] + /** * Trigger this signal externally to play the next audio in the "files". This, in turn, stops the currently playing audio and check the necessary * conditions (see onStopped signal in "audio" element) and decides what needs to be done for the next audio. */ signal nextAudio() onNextAudio: stop() /** * type:var * Current playback state. * * Possible values taken from Audio.status */ property var playbackState: (audio.error == Audio.NoError) ? audio.playbackState : Audio.StoppedState; /** * type:list * Playback queue. */ property var files: [] /** * Emitted in case of error. */ signal error /** * Emitted when playback of all scheduled audio sources has finished. */ signal done //Pauses the currently playing audio function pause() { if(playbackState === Audio.PlayingState) audio.pause() } //Resumes the current audio if it had been paused function resume() { if(playbackState === Audio.PausedState || playbackState === Audio.StoppedState) audio.play() } /** * Plays back the audio resource @p file. * * @param type:string file [optional] URL to an audio source. * @returns @c true if playback has been started, @c false if file does not * exist or audio is muted */ function play(file) { if(!fileId.exists(file) || muted) return false if(file) { // Setting the source to "" on Linux fix a case where the sound is no more played if you play twice the same sound in a row source = "" source = file } if(!muted) { audio.play() } return true } /** * Stops audio playback. */ function stop() { if(audio.playbackState != Audio.StoppedState) audio.stop() } /** * Schedules a @p file for audio playback. * * If there is no playback currently running, the new source will be * played back immediately. Otherwise it is appended to the file queue of * sources. * * @param type:string file File to the audio file to be played back. * @returns @c true upon success, or @c false if @p file does not exist or * audio is muted */ function append(file) { if(!fileId.exists(file) || muted) return false if(audio.playbackState !== Audio.PlayingState || audio.status === Audio.EndOfMedia || audio.status === Audio.NoMedia || audio.status === Audio.InvalidMedia) { // Setting the source to "" on Linux fix a case where the sound is no more played source = "" source = file files.push(file) - _playNextFile() + silenceTimer.start() } else { files.push(file) } return true } /** * Adds a pause of the given duration in ms before playing of the next file. * * @param type:int duration_ms Pause in milliseconds. */ function silence(duration_ms) { silenceTimer.interval = duration_ms } /** * Flushes the list of scheduled files. * @sa files */ function clearQueue() { while(files.length > 0) { files.pop(); } } /// @cond INTERNAL_DOCS function _playNextFile() { if(files.length == 0) return + var nextFile = files.shift() if(nextFile === '') { - audio.source = "" + source = "" gcaudio.done() } else { - audio.source = "" - audio.source = nextFile + source = "" + source = nextFile if(!muted) audio.play() } } Audio { id: audio muted: gcaudio.muted onError: { // This file cannot be played, remove it from the source asap source = "" if(files.length) silenceTimer.start() else gcaudio.error() } onStopped: { if(files.length) silenceTimer.start() else gcaudio.done() } + metaData.onMetaDataChanged: { + if(isBackgroundMusic) { + metaDataMusic = [metaData.title, metaData.contributingArtist, metaData.year, metaData.copyright] + } + } } Timer { id: silenceTimer repeat: false onTriggered: { interval = 0 _playNextFile() } } File { id: fileId } /// @endcond } diff --git a/src/core/main.qml b/src/core/main.qml index db29520ab..50e9e7886 100644 --- a/src/core/main.qml +++ b/src/core/main.qml @@ -1,462 +1,451 @@ /* GCompris - main.qml * * Copyright (C) 2014 Bruno Coudoin * * Authors: * Bruno Coudoin * * 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 . */ import QtQuick 2.6 import QtQuick.Controls 1.5 import QtQuick.Window 2.2 import QtQml 2.2 import GCompris 1.0 import "qrc:/gcompris/src/core/core.js" as Core /** * 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 */ Window { id: main // Start in window mode at full screen size width: ApplicationSettings.previousWidth height: ApplicationSettings.previousHeight minimumWidth: 400 * ApplicationInfo.ratio minimumHeight: 400 * ApplicationInfo.ratio title: "GCompris" /// @cond INTERNAL_DOCS property var applicationState: Qt.application.state property var rccBackgroundMusic: ApplicationInfo.getBackgroundMusicFromRcc() property var filteredBackgroundMusic: ApplicationSettings.filteredBackgroundMusic property alias backgroundMusic: backgroundMusic /** * type: bool * It tells whether the background music is enabled for an activity. * * It changes to false if the started activity is a musical activity and back to true when the activity is closed. */ property bool isBackgroundMusicEnabledInActivity: true /** * When a musical activity is started, isBackgroundMusicEnabledInActivity changes to false and the backgroundMusic pauses. * * When returning back from the musical activity to menu, isBackgroundMusicEnabledInActivity changes to true and backgroundMusic resumes. */ onIsBackgroundMusicEnabledInActivityChanged: { if(!isBackgroundMusicEnabledInActivity) { backgroundMusic.pause() } else { backgroundMusic.resume() } } onApplicationStateChanged: { if (ApplicationInfo.isMobile && applicationState != Qt.ApplicationActive) { audioVoices.stop(); audioEffects.stop(); } } onClosing: Core.quit(main) GCAudio { id: audioVoices muted: !ApplicationSettings.isAudioVoicesEnabled 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: { DownloadManager.voicesRegistered.disconnect(playWelcome); } function playWelcome() { audioVoices.append(ApplicationInfo.getAudioFilePath("voices-$CA/$LOCALE/misc/welcome.$CA")); } } Component.onCompleted: { - if(ApplicationSettings.isAudioEffectsEnabled) - append(ApplicationInfo.getAudioFilePath("qrc:/gcompris/src/core/resource/intro.$CA")) - if (DownloadManager.areVoicesRegistered()) delayedWelcomeTimer.playWelcome(); else { DownloadManager.voicesRegistered.connect( delayedWelcomeTimer.playWelcome); delayedWelcomeTimer.start(); } } } GCSfx { id: audioEffects muted: !ApplicationSettings.isAudioEffectsEnabled && !main.isMusicalActivityRunning volume: ApplicationSettings.audioEffectsVolume } GCAudio { id: backgroundMusic + + isBackgroundMusic: true + muted: !ApplicationSettings.isBackgroundMusicEnabled volume: ApplicationSettings.backgroundMusicVolume onMutedChanged: { - if(!hasAudio && !delayedbackgroundMusic.running && !files.length) { - delayedbackgroundMusic.playBackgroundMusic() + if(!hasAudio && !files.length) { + backgroundMusic.playBackgroundMusic() } } - onDone: delayedbackgroundMusic.playBackgroundMusic() - - Timer { - id: delayedbackgroundMusic - interval: (ApplicationSettings.isAudioVoicesEnabled && !ApplicationSettings.isAudioEffectsEnabled) ? 2000 : 20000 - repeat: false - - onTriggered: { - delayedbackgroundMusic.playBackgroundMusic(); - } + onDone: backgroundMusic.playBackgroundMusic() - function playBackgroundMusic() { - rccBackgroundMusic = ApplicationInfo.getBackgroundMusicFromRcc() - for(var i = 0; i < filteredBackgroundMusic.length; i++) { - backgroundMusic.append(ApplicationInfo.getAudioFilePath("backgroundMusic/" + filteredBackgroundMusic[i])) - } - - if(main.isMusicalActivityRunning) - backgroundMusic.pause() + function playBackgroundMusic() { + rccBackgroundMusic = ApplicationInfo.getBackgroundMusicFromRcc() + + for(var i = 0; i < filteredBackgroundMusic.length; i++) { + backgroundMusic.append(ApplicationInfo.getAudioFilePath("backgroundMusic/" + filteredBackgroundMusic[i])) } + + if(main.isMusicalActivityRunning) + backgroundMusic.pause() } + Component.onCompleted: { + if(ApplicationSettings.isBackgroundMusicEnabled) + backgroundMusic.append(ApplicationInfo.getAudioFilePath("qrc:/gcompris/src/core/resource/intro.$CA")) if(ApplicationSettings.isBackgroundMusicEnabled && DownloadManager.haveLocalResource(DownloadManager.getBackgroundMusicResources())) { - if(!ApplicationSettings.isAudioEffectsEnabled && !ApplicationSettings.isAudioVoicesEnabled) { - delayedbackgroundMusic.playBackgroundMusic() - } - else { - delayedbackgroundMusic.start() - } + backgroundMusic.playBackgroundMusic() } else { - DownloadManager.backgroundMusicRegistered.connect(delayedbackgroundMusic.playBackgroundMusic) + DownloadManager.backgroundMusicRegistered.connect(backgroundMusic.playBackgroundMusic) } } } function playIntroVoice(name) { name = name.split("/")[0] audioVoices.play(ApplicationInfo.getAudioFilePath("voices-$CA/$LOCALE/intro/" + name + ".$CA")) } function checkWordset() { var wordset = ApplicationSettings.wordset if(wordset == '') // Maybe the wordset has been bundled or copied manually // we have to register it if we find it. wordset = 'data2/words/words.rcc' // check for words.rcc: if (DownloadManager.isDataRegistered("words")) { // words.rcc is already registered -> nothing to do } else if(DownloadManager.haveLocalResource(wordset)) { // words.rcc is there -> register old file first // then try to update in the background if(DownloadManager.updateResource(wordset)) { ApplicationSettings.wordset = wordset } } else if(ApplicationSettings.wordset) { // Only if wordset specified // words.rcc has not been downloaded yet -> ask for download Core.showMessageDialog( main, qsTr("The images for several activities are not yet installed. " + "Do you want to download them now?"), qsTr("Yes"), function() { if (DownloadManager.downloadResource(wordset)) var downloadDialog = Core.showDownloadDialog(pageView.currentItem, {}); }, qsTr("No"), null, function() { pageView.currentItem.focus = true } ); } } function checkBackgroundMusic() { var music = DownloadManager.getBackgroundMusicResources() if(rccBackgroundMusic == '') { rccBackgroundMusic = ApplicationInfo.getBackgroundMusicFromRcc() } if(music == '') { music = DownloadManager.getBackgroundMusicResources() } // We have local music but it is not yet registered else if(!DownloadManager.isDataRegistered("backgroundMusic") && DownloadManager.haveLocalResource(music)) { // We have music and automatic download is enabled. Download the music and register it if(DownloadManager.updateResource(music) && DownloadManager.downloadIsRunning()) { DownloadManager.registerResource(music) rccBackgroundMusic = Core.shuffle(ApplicationInfo.getBackgroundMusicFromRcc()) } else { rccBackgroundMusic = ApplicationInfo.getBackgroundMusicFromRcc() } } else if(!DownloadManager.haveLocalResource(music)) { Core.showMessageDialog( main, qsTr("The background music is not yet downloaded. ") + qsTr("Do you want to download it now?"), qsTr("Yes"), function() { if(DownloadManager.downloadResource(DownloadManager.getBackgroundMusicResources())) { var downloadDialog = Core.showDownloadDialog(pageView.currentItem, {}); } }, qsTr("No"), null, function() { pageView.currentItem.focus = true } ); } } ChangeLog { id: changelog } Component.onCompleted: { console.log("enter main.qml (run #" + ApplicationSettings.exeCount + ", ratio=" + ApplicationInfo.ratio + ", fontRatio=" + ApplicationInfo.fontRatio + ", dpi=" + Math.round(Screen.pixelDensity*25.4) + ", userDataPath=" + ApplicationSettings.userDataPath + ")"); if (ApplicationSettings.exeCount === 1 && !ApplicationSettings.isKioskMode && ApplicationInfo.isDownloadAllowed) { // first run var dialog; 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).") .arg(Qt.locale(ApplicationInfo.getVoicesLocale(ApplicationSettings.locale)).nativeLanguageName) .arg(ApplicationInfo.getVoicesLocale(ApplicationSettings.locale)) + "\n" + qsTr("Do you want to download the corresponding sound files now?"), qsTr("Yes"), function() { if (DownloadManager.downloadResource( DownloadManager.getVoicesResourceForLocale(ApplicationSettings.locale))) var downloadDialog = Core.showDownloadDialog(pageView.currentItem, {}); }, qsTr("No"), null, function() { pageView.currentItem.focus = true checkWordset() checkBackgroundMusic() } ); } else { // Register voices-resources for current locale, updates/downloads only if // not prohibited by the settings if (!DownloadManager.areVoicesRegistered()) { DownloadManager.updateResource( DownloadManager.getVoicesResourceForLocale(ApplicationSettings.locale)); } checkWordset() checkBackgroundMusic() if(changelog.isNewerVersion(ApplicationSettings.lastGCVersionRan, ApplicationInfo.GCVersionCode)) { // display log between ApplicationSettings.lastGCVersionRan and ApplicationInfo.GCVersionCode Core.showMessageDialog( main, qsTr("GCompris has been updated! Here are the new changes:
") + changelog.getLogBetween(ApplicationSettings.lastGCVersionRan, ApplicationInfo.GCVersionCode), "", null, "", null, function() { pageView.currentItem.focus = true } ); // Store new version ApplicationSettings.lastGCVersionRan = ApplicationInfo.GCVersionCode; } } } Loading { id: loading } StackView { id: pageView anchors.fill: parent initialItem: { "item": "qrc:/gcompris/src/activities/" + ActivityInfoTree.rootMenu.name, "properties": { 'audioVoices': audioVoices, 'audioEffects': audioEffects, 'loading': loading, 'backgroundMusic': backgroundMusic } } focus: true delegate: StackViewDelegate { id: root function getTransition(properties) { audioVoices.clearQueue() audioVoices.stop() 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) properties.enterItem.start(); if(properties.name === "pushTransition") { if(properties.enterItem.isDialog) { return pushVTransition } else { if(properties.enterItem.isMusicalActivity) main.isMusicalActivityRunning = true return pushHTransition } } else { if(properties.exitItem.isDialog) { return popVTransition } else { main.isMusicalActivityRunning = false return popHTransition } } } function transitionFinished(properties) { properties.exitItem.opacity = 1 if(!properties.enterItem.isDialog) { properties.exitItem.stop() } } property Component pushHTransition: StackViewTransition { 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 } } property Component popHTransition: StackViewTransition { 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 } } 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 } } property Component replaceTransition: pushHTransition } } /// @endcond }