diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..e0e21e4a3 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "external/qml-box2d"] + path = external/qml-box2d + url = https://github.com/qml-box2d/qml-box2d diff --git a/CMakeLists.txt b/CMakeLists.txt index 5ebba8ce7..21ae4bafe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,262 +1,284 @@ if(SAILFISHOS) cmake_minimum_required(VERSION 2.8.11) else(SAILfISHOS) cmake_minimum_required(VERSION 2.8.12) endif() project(gcompris C CXX) # Set c++11 support include(CheckCXXCompilerFlag) CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) if(COMPILER_SUPPORTS_CXX11) set(my_cxx_flags "-std=c++11") elseif(COMPILER_SUPPORTS_CXX0X) set(my_cxx_flags "-std=c++0x") else() message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.") endif() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${my_cxx_flags}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${my_cxx_flags}") set(GCOMPRIS_MAJOR_VERSION 0) set(GCOMPRIS_MINOR_VERSION 40) set(GCOMPRIS_PATCH_VERSION 0) # Set executable filename if(ANDROID) set(GCOMPRIS_EXECUTABLE_NAME GCompris) if("${ANDROID_ABI}" STREQUAL "x86") # We always want x86 to be a release above to arm one because the play # store want x86 to be pushed after arm. MATH(EXPR GCOMPRIS_MINOR_VERSION "${GCOMPRIS_MINOR_VERSION}+1") endif("${ANDROID_ABI}" STREQUAL "x86") elseif(SAILFISHOS) set(GCOMPRIS_EXECUTABLE_NAME harbour-gcompris-qt) else() set(GCOMPRIS_EXECUTABLE_NAME gcompris-qt) endif() set(GCOMPRIS_VERSION ${GCOMPRIS_MAJOR_VERSION}.${GCOMPRIS_MINOR_VERSION}) # An integer value that represents the version of the application # Increase it at each release set(GCOMPRIS_VERSION_CODE ${GCOMPRIS_MINOR_VERSION}) # cmake modules setup find_package(ECM 1.4.0 QUIET NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ${CMAKE_SOURCE_DIR}/cmake/) set(CMAKE_PREFIX_PATH "${Qt5_DIR}/lib/cmake/Qt5") # KDE po to qm tools if(ECM_FOUND) include(ECMPoQmTools) if (IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/po") ecm_install_po_files_as_qm(po) endif() endif(ECM_FOUND) # prevent build in source directory if("${CMAKE_BINARY_DIR}" STREQUAL "${CMAKE_SOURCE_DIR}") message(SEND_ERROR "Building in the source directory is not supported.") message(FATAL_ERROR "Please remove the created \"CMakeCache.txt\" file, the \"CMakeFiles\" directory and create a build directory and call \"${CMAKE_COMMAND} \".") endif("${CMAKE_BINARY_DIR}" STREQUAL "${CMAKE_SOURCE_DIR}") # Sailfish does not provide a recent Qt version if(SAILFISHOS) set(QT_REQUIRED_VERSION 5.2.2) else() set(QT_REQUIRED_VERSION 5.3.0) endif() find_package(Qt5 ${QT_REQUIRED_VERSION} REQUIRED Qml Quick Gui Multimedia Core Svg Xml XmlPatterns LinguistTools Sensors) find_package (KF5 QUIET COMPONENTS DocTools ) if(KF5_FOUND) include(KDEInstallDirs) endif(KF5_FOUND) FIND_PROGRAM(LCONVERT_EXECUTABLE lconvert PATHS ${Qt5_DIR}/../../../bin/ /usr/local/bin /usr/bin/ NO_DEFAULT_PATH ) FIND_PROGRAM(LRELEASE_EXECUTABLE lrelease PATHS ${Qt5_DIR}/../../../bin/ /usr/local/bin /usr/bin/ NO_DEFAULT_PATH ) #get_cmake_property(_variableNames VARIABLES) #foreach (_variableName ${_variableNames}) # message("${_variableName}=${${_variableName}}") #endforeach() option(WITH_ACTIVATION_CODE "Include the activation system" OFF) option(WITH_DEMO_ONLY "Include only demo activities" OFF) option(WITH_DOWNLOAD "Internal download" ON) if(WITH_DOWNLOAD) set(ANDROID_INTERNET_PERMISSION "") set(ANDROID_ACCESS_NETWORK_STATE_PERMISSION "") endif(WITH_DOWNLOAD) # Set output directory if(CMAKE_HOST_APPLE) set(_bundle_bin gcompris-qt.app/Contents/MacOS) set(_data_dest_dir bin/${_bundle_bin}) elseif(ANDROID) set(_data_dest_dir android/assets) else() set(_data_dest_dir share/${GCOMPRIS_EXECUTABLE_NAME}) endif() if(ANDROID) # Android .so output set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/android/libs/${ARM_TARGET}/) set(GCOMPRIS_TRANSLATIONS_DIR ${CMAKE_BINARY_DIR}/${_data_dest_dir} CACHE INTERNAL "" FORCE) set(GCOMPRIS_RCC_DIR ${CMAKE_BINARY_DIR}/${_data_dest_dir} CACHE INTERNAL "" FORCE) if(WITH_ACTIVATION_CODE) set(ANDROID_BILLING_PERMISSION "") set(ANDROID_PACKAGE "net.gcompris") else(WITH_ACTIVATION_CODE) set(ANDROID_PACKAGE "net.gcompris.full") endif() add_subdirectory(android) else(ANDROID) # Desktop build set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(GCOMPRIS_TRANSLATIONS_DIR ${CMAKE_BINARY_DIR}/${_data_dest_dir}/translations CACHE INTERNAL "" FORCE) set(GCOMPRIS_RCC_DIR ${CMAKE_BINARY_DIR}/${_data_dest_dir}/rcc CACHE INTERNAL "" FORCE) endif(ANDROID) # Always create these folders add_custom_command( OUTPUT shareFolders COMMAND cmake -E make_directory ${GCOMPRIS_TRANSLATIONS_DIR} COMMAND cmake -E make_directory ${GCOMPRIS_RCC_DIR} ) add_custom_target( createShareFolders ALL DEPENDS shareFolders ) include(cmake/rcc.cmake) # Translations handling # Simple command calling the python script add_custom_command( OUTPUT retrievePoFilesFromSvn COMMAND python2 tools/l10n-fetch-po-files.py WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} ) # Install translations add_custom_target(getSvnTranslations DEPENDS retrievePoFilesFromSvn COMMENT "Re-run cmake after this to be able to run BuildTranslations with the latest files" ) # Get all po files in po/. You can get them doing : python2 tools/l10n-fetch-po-files.py file(GLOB TRANSLATIONS_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "po/*.po") # Set the output dir for the translation files to /bin foreach(PoSource ${TRANSLATIONS_FILES}) # Changes the .po extension to .ts string(REPLACE ".po" ".ts" TsSource ${PoSource}) # Removes the po/ folder string(REPLACE "po/" "" TsSource ${TsSource}) # qm filename string(REPLACE ".ts" ".qm" QmOutput ${TsSource}) set(OutTsFile ${CMAKE_BINARY_DIR}/tmp/${TsSource}) add_custom_command( OUTPUT ${QmOutput} COMMAND cmake -E make_directory ${GCOMPRIS_TRANSLATIONS_DIR} COMMAND cmake -E make_directory ${CMAKE_BINARY_DIR}/tmp # Remove the obsolete translations and set po in the ts output file COMMAND msgattrib --no-obsolete ${CMAKE_CURRENT_SOURCE_DIR}/${PoSource} -o ${OutTsFile} # Convert the po into ts COMMAND ${LCONVERT_EXECUTABLE} -if po -of ts -i ${OutTsFile} -o ${OutTsFile} # Convert the ts in qm removing non finished translations COMMAND ${LRELEASE_EXECUTABLE} -compress -nounfinished ${OutTsFile} -qm ${GCOMPRIS_TRANSLATIONS_DIR}/${QmOutput} ) list(APPEND QM_FILES ${QmOutput}) endforeach() # Install translations add_custom_target(BuildTranslations DEPENDS ${QM_FILES} COMMENT "If you don't have the .po, you need to run make getSvnTranslations first then re-run cmake" ) if(CMAKE_HOST_APPLE) install(DIRECTORY ${GCOMPRIS_TRANSLATIONS_DIR} DESTINATION ${_bundle_bin}) else() install(DIRECTORY ${GCOMPRIS_TRANSLATIONS_DIR} DESTINATION ${_data_dest_dir}) endif() # Build standalone package option -> if ON, we will copy the required Qt files in the build package. # If OFF, "make install" will not copy Qt files so only GCompris files will be packaged. # By default, it is true on Windows (as we deliver NSIS package), macOS (bundled), android (apk) and false on linux (to do make install) # If you want to create a STGZ package for linux (auto-extractible), override this variable by typing : cmake -DBUILD_STANDALONE=ON if(UNIX AND NOT ANDROID AND NOT APPLE) option(BUILD_STANDALONE "Build a standalone package when typing 'make package'" OFF) else() option(BUILD_STANDALONE "Build a standalone package when typing 'make package'" ON) endif() option(WITH_KIOSK_MODE "Set the kiosk mode by default" OFF) if(WIN32) set(COMPRESSED_AUDIO "ac3" CACHE STRING "Compressed Audio format [ogg|aac|ac3]") elseif(APPLE) set(COMPRESSED_AUDIO "aac" CACHE STRING "Compressed Audio format [ogg|aac|ac3]") else() set(COMPRESSED_AUDIO "ogg" CACHE STRING "Compressed Audio format [ogg|aac|ac3]") endif() file(GLOB_RECURSE OGG_FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/ "*.ogg") foreach(OGG_FILE ${OGG_FILES}) # This should only replace the extension string(REGEX REPLACE "ogg$" "aac" AAC_FILE ${OGG_FILE}) add_custom_command( OUTPUT ${AAC_FILE} # Put the good line depending on your installation COMMAND avconv -v warning -i ${OGG_FILE} -acodec libvo_aacenc ${AAC_FILE} #COMMAND ffmpeg -v warning -i ${OGG_FILE} -acodec aac -strict -2 ${AAC_FILE} ) list(APPEND AAC_FILES ${AAC_FILE}) endforeach() add_custom_target( createAacFromOgg DEPENDS ${AAC_FILES} ) set(ARCHIVE_NAME ${CMAKE_PROJECT_NAME}-${GCOMPRIS_VERSION}) add_custom_target(dist COMMAND git archive --prefix=${ARCHIVE_NAME}/ HEAD | bzip2 > ${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}.tar.bz2 WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) add_subdirectory(src) if(KF5_FOUND) add_subdirectory(docs/docbook) endif(KF5_FOUND) if(SAILFISHOS) # Need to be done at the end, after src add_subdirectory(platforms/sailfishOS) endif() +# +# external: qml-box2d +# + +include(ExternalProject) + +set (_box2d_install_dir ${CMAKE_CURRENT_BINARY_DIR}/lib/qml/Box2D.2.0) +set (_box2d_source_dir ${CMAKE_CURRENT_SOURCE_DIR}/external/qml-box2d) + +ExternalProject_Add(qml_box2d + DOWNLOAD_COMMAND "" + SOURCE_DIR ${_box2d_source_dir} + CONFIGURE_COMMAND qmake ${_box2d_source_dir}/box2d.pro + BUILD_COMMAND make + INSTALL_DIR ${_box2d_install_dir} + INSTALL_COMMAND cp libBox2D.so ${_box2d_source_dir}/qmldir ${_box2d_install_dir} + ) + +add_library(qml-box2d SHARED IMPORTED) +set_target_properties(qml-box2d PROPERTIES IMPORTED_LOCATION ${_box2d_install_dir}/libBox2D.so) + +install(DIRECTORY ${_box2d_install_dir} DESTINATION lib/qml) diff --git a/android/configAndroid.json.cmake b/android/configAndroid.json.cmake index a00dcc5ea..e5294e3d5 100644 --- a/android/configAndroid.json.cmake +++ b/android/configAndroid.json.cmake @@ -1,14 +1,15 @@ { "description": "This file is to be read by androiddeployqt", "qt": "@_qt5Core_install_prefix@", "sdk": "@ANDROID_SDK@", "ndk": "@ANDROID_NDK@", "toolchain-prefix": "@ANDROID_TOOLCHAIN_MACHINE_NAME@", "tool-prefix": "@ANDROID_TOOLCHAIN_MACHINE_NAME@", "toolchain-version": "@ANDROID_COMPILER_VERSION@", "ndk-host": "@ANDROID_NDK_HOST_SYSTEM_NAME@", "target-architecture": "@ARM_TARGET@", "application-binary": "@CMAKE_LIBRARY_OUTPUT_DIRECTORY@libGCompris.so", "android-package-source-directory": "@PACKAGE_SOURCE_ANDROID@/", - "android-package": "net.gcompris" + "android-package": "net.gcompris", + "android-extra-plugins": "@CMAKE_BINARY_DIR@/lib/qml" } diff --git a/external/qml-box2d b/external/qml-box2d new file mode 160000 index 000000000..130885547 --- /dev/null +++ b/external/qml-box2d @@ -0,0 +1 @@ +Subproject commit 1308855477d5c7fdae0de3d50168a6765af1ad78 diff --git a/src/core/main.cpp b/src/core/main.cpp index 6f425921d..a09d4207b 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -1,259 +1,262 @@ /* GCompris - main.cpp * * 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 . */ #include #include #include #include #include #include #include #include #include #include #include #include "ApplicationInfo.h" #include "ActivityInfoTree.h" #include "File.h" #include "DownloadManager.h" bool loadAndroidTranslation(QTranslator &translator, const QString &locale) { QFile file("assets:/gcompris_" + locale + ".qm"); file.open(QIODevice::ReadOnly); QDataStream in(&file); uchar *data = (uchar*)malloc(file.size()); if(!file.exists()) qDebug() << "file assets:/" << locale << ".qm exists"; in.readRawData((char*)data, file.size()); if(!translator.load(data, file.size())) { qDebug() << "Unable to load translation for locale " << locale << ", use en_US by default"; free(data); return false; } // Do not free data, it is still needed by translator return true; } // Return the locale QString loadTranslation(QSettings &config, QTranslator &translator) { QString locale; // Get locale if(config.contains("General/locale")) { locale = config.value("General/locale").toString(); } else { locale = GC_DEFAULT_LOCALE; } if(locale == GC_DEFAULT_LOCALE) locale = QString(QLocale::system().name() + ".UTF-8"); if(locale == "C.UTF-8") locale = "en_US.UTF-8"; // Load translation // Remove .UTF8 locale.remove(".UTF-8"); #if defined(Q_OS_ANDROID) if(!loadAndroidTranslation(translator, locale)) loadAndroidTranslation(translator, ApplicationInfo::localeShort(locale)); #else #if (defined(Q_OS_LINUX) || defined(Q_OS_UNIX)) // only useful for translators: load from $application_dir/../share/... if exists as it is where kde scripts install translations if(translator.load("gcompris_qt.qm", QString("%1/../share/locale/%2/LC_MESSAGES").arg(QCoreApplication::applicationDirPath(), locale))) { qDebug() << "load translation for locale " << locale << " in " << QString("%1/../share/locale/%2/LC_MESSAGES").arg(QCoreApplication::applicationDirPath(), locale); } else if(translator.load("gcompris_qt.qm", QString("%1/../share/locale/%2/LC_MESSAGES").arg(QCoreApplication::applicationDirPath(), locale.split('_')[0]))) { qDebug() << "load translation for locale " << locale << " in " << QString("%1/../share/locale/%2/LC_MESSAGES").arg(QCoreApplication::applicationDirPath(), locale.split('_')[0]); } else #endif if(!translator.load("gcompris_" + locale, QString("%1/%2/translations").arg(QCoreApplication::applicationDirPath(), GCOMPRIS_DATA_FOLDER))) { qDebug() << "Unable to load translation for locale " << locale << ", use en_US by default"; } #endif return locale; } int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); app.setOrganizationName("KDE"); app.setApplicationName(GCOMPRIS_APPLICATION_NAME); app.setOrganizationDomain("kde.org"); app.setApplicationVersion(ApplicationInfo::GCVersion()); // Local scope for config QSettings config(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + "/gcompris/" + GCOMPRIS_APPLICATION_NAME + ".conf", QSettings::IniFormat); // Load translations QTranslator translator; QString locale = loadTranslation(config, translator); // Apply translation app.installTranslator(&translator); QCommandLineParser parser; parser.setApplicationDescription("GCompris is an educational software for children 2 to 10"); parser.addHelpOption(); parser.addVersionOption(); QCommandLineOption exportActivitiesAsSQL("export-activities-as-sql", "Export activities as SQL"); parser.addOption(exportActivitiesAsSQL); QCommandLineOption clDefaultCursor(QStringList() << "c" << "cursor", QObject::tr("Run GCompris with the default system cursor.")); parser.addOption(clDefaultCursor); QCommandLineOption clNoCursor(QStringList() << "C" << "nocursor", QObject::tr("Run GCompris without cursor (touch screen mode).")); parser.addOption(clNoCursor); QCommandLineOption clFullscreen(QStringList() << "f" << "fullscreen", QObject::tr("Run GCompris in fullscreen mode.")); parser.addOption(clFullscreen); QCommandLineOption clWindow(QStringList() << "w" << "window", QObject::tr("Run GCompris in window mode.")); parser.addOption(clWindow); QCommandLineOption clSound(QStringList() << "s" << "sound", QObject::tr("Run GCompris with sound enabled.")); parser.addOption(clSound); QCommandLineOption clMute(QStringList() << "m" << "mute", QObject::tr("Run GCompris without sound.")); parser.addOption(clMute); QCommandLineOption clWithoutConfig(QStringList() << "disable-config", QObject::tr("Disable the configuration button.")); parser.addOption(clWithoutConfig); QCommandLineOption clWithConfig(QStringList() << "enable-config", QObject::tr("Enable the configuration button (default).")); parser.addOption(clWithConfig); parser.process(app); ApplicationInfo::init(); ActivityInfoTree::init(); ApplicationSettings::init(); File::init(); DownloadManager::init(); // Tell media players to stop playing, it's GCompris time ApplicationInfo::getInstance()->requestAudioFocus(); // Must be done after ApplicationSettings is constructed because we get an // async callback from the payment system ApplicationSettings::getInstance()->checkPayment(); // Getting fullscreen mode from config if exist, else true is default value bool isFullscreen = true; { if(config.contains("General/fullscreen")) { isFullscreen = config.value("General/fullscreen").toBool(); } // Set the cursor image bool defaultCursor = false; if(config.contains("General/defaultCursor")) { defaultCursor = config.value("General/defaultCursor").toBool(); } if(!defaultCursor && !parser.isSet(clDefaultCursor)) QGuiApplication::setOverrideCursor( QCursor(QPixmap(":/gcompris/src/core/resource/cursor.svg"), 0, 0)); // Hide the cursor bool noCursor = false; if(config.contains("General/noCursor")) { noCursor = config.value("General/noCursor").toBool(); } if(noCursor || parser.isSet(clNoCursor)) QGuiApplication::setOverrideCursor(QCursor(Qt::BlankCursor)); } // Update execution counter ApplicationSettings::getInstance()->setExeCount(ApplicationSettings::getInstance()->exeCount() + 1); // Register voices-resources for current locale, updates/downloads only if // not prohibited by the settings if(!DownloadManager::getInstance()->areVoicesRegistered()) DownloadManager::getInstance()->updateResource(DownloadManager::getInstance() ->getVoicesResourceForLocale(locale)); if(parser.isSet(clFullscreen)) { isFullscreen = true; } if(parser.isSet(clWindow)) { isFullscreen = false; } if(parser.isSet(clMute)) { ApplicationSettings::getInstance()->setIsAudioEffectsEnabled(false); ApplicationSettings::getInstance()->setIsAudioVoicesEnabled(false); } if(parser.isSet(clSound)) { ApplicationSettings::getInstance()->setIsAudioEffectsEnabled(true); ApplicationSettings::getInstance()->setIsAudioVoicesEnabled(true); } if(parser.isSet(clWithConfig)) { ApplicationSettings::getInstance()->setKioskMode(false); } if(parser.isSet(clWithoutConfig)) { ApplicationSettings::getInstance()->setKioskMode(true); } QQmlApplicationEngine engine(QUrl("qrc:/gcompris/src/core/main.qml")); QObject::connect(&engine, SIGNAL(quit()), DownloadManager::getInstance(), SLOT(shutdown())); + // add import path for shipped qml modules: + engine.addImportPath(QStringLiteral("%1/../lib/qml") + .arg(QCoreApplication::applicationDirPath())); if(parser.isSet(exportActivitiesAsSQL)) { ActivityInfoTree *menuTree(qobject_cast(ActivityInfoTree::menuTreeProvider(&engine, NULL))); menuTree->exportAsSQL(); exit(0); } QObject *topLevel = engine.rootObjects().value(0); QQuickWindow *window = qobject_cast(topLevel); if (!window) { qWarning("Error: Your root item has to be a Window."); return -1; } ApplicationInfo::setWindow(window); window->setIcon(QIcon(QPixmap(QString::fromUtf8(":/gcompris/src/core/resource/gcompris-icon.png")))); if(isFullscreen) { window->showFullScreen(); } else { window->show(); } return app.exec(); }