diff --git a/src/activities/mosaic/resource/2/Data.qml b/src/activities/mosaic/resource/2/Data.qml new file mode 100644 index 000000000..dc5c142fb --- /dev/null +++ b/src/activities/mosaic/resource/2/Data.qml @@ -0,0 +1,172 @@ +/* GCompris - Data.qml + * + * Copyright (C) 2020 Deepak Kumar + * + * Authors: + * Deepak Kumar + * + * 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 "../../../../core" + +Dataset { + objective: qsTr("Rebuid the mosaic when items are not on a single line.") + difficulty: 3 + data: [ + { + "nbItems": 8, + "layout": [ + [4,2], + [8,1] + ], + "multipleDatasetType": "multipleDataset2" + + }, + { + "nbItems": 8, + "layout": [ + [4,2], + [8,1] + ], + "multipleDatasetType": "multipleDataset2" + + }, + { + "nbItems": 8, + "layout": [ + [4,2], + [8,1] + ], + "multipleDatasetType": "multipleDataset2" + + }, + { + "nbItems": 8, + "layout": [ + [4,2], + [8,1] + ], + "multipleDatasetType": "multipleDataset2" + + }, + { + "nbItems": 16, + "layout": [ + [4,4], + [8,2] + ], + "multipleDatasetType": "multipleDataset2" + + }, + { + "nbItems": 16, + "layout": [ + [4,4], + [8,2] + ], + "multipleDatasetType": "multipleDataset2" + + }, + { + "nbItems": 16, + "layout": [ + [4,4], + [8,2] + ], + "multipleDatasetType": "multipleDataset2" + + }, + { + "nbItems": 24, + "layout": [ + [6,4], + [12,2] + ], + "multipleDatasetType": "multipleDataset2" + + }, + { + "nbItems": 24, + "layout": [ + [6,4], + [12,2] + ], + "multipleDatasetType": "multipleDataset2" + + }, + { + "nbItems": 24, + "layout": [ + [6,4], + [12,2] + ], + "multipleDatasetType": "multipleDataset2" + + }, + { + "nbItems": 24, + "layout": [ + [6,4], + [12,2] + ], + "multipleDatasetType": "multipleDataset2" + + }, + { + "nbItems": 24, + "layout": [ + [6,4], + [12,2] + ], + "multipleDatasetType": "multipleDataset2" + + }, + { + "nbItems": 24, + "layout": [ + [6,4], + [12,2] + ], + "multipleDatasetType": "multipleDataset2" + + }, + { + "nbItems": 24, + "layout": [ + [6,4], + [12,2] + ], + "multipleDatasetType": "multipleDataset2" + + }, + { + "nbItems": 24, + "layout": [ + [6,4], + [12,2] + ], + "multipleDatasetType": "multipleDataset2" + + }, + { + "nbItems": 24, + "layout": [ + [6,4], + [12,2] + ], + "multipleDatasetType": "multipleDataset2" + + } + ] +} diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 36abc31a7..5b4de82b3 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,390 +1,392 @@ include(qt_helper) configure_file(config.h.in "${CMAKE_CURRENT_BINARY_DIR}/config.h" @ONLY) if(WITH_KIOSK_MODE) add_definitions(-DWITH_KIOSK_MODE) endif() if(SAILFISHOS) add_definitions(-DSAILFISHOS) endif() if(ANDROID) # needed since ECM 5.45 (https://bugs.kde.org/show_bug.cgi?id=394042) include_directories(SYSTEM "${CMAKE_SYSROOT}/usr/include" ) endif() include_directories(${CMAKE_CURRENT_SOURCE_DIR}/serverMasterController/) set(gcompris_SRCS ActivityInfo.cpp ActivityInfo.h ActivityInfoTree.cpp ActivityInfoTree.h ApplicationInfo.cpp ApplicationInfo.h ApplicationSettings.cpp ApplicationSettings.h File.cpp File.h Directory.cpp Directory.h DownloadManager.cpp DownloadManager.h GComprisPlugin.cpp GComprisPlugin.h main.cpp config.h.in synth/ADSRenvelope.cpp synth/ADSRenvelope.h synth/GSynth.cpp synth/GSynth.h synth/linearSynthesis.cpp synth/linearSynthesis.h synth/modulation.cpp synth/modulation.h synth/generator.cpp synth/generator.h synth/preset.h synth/preset.cpp synth/waveform.cpp synth/waveform.h serverMasterController/controllers/cm-lib_global.h serverMasterController/controllers/master-controller.cpp serverMasterController/controllers/master-controller.h serverMasterController/controllers/navigation-controller.h serverMasterController/controllers/command-controller.h serverMasterController/controllers/command-controller.cpp + serverMasterController/controllers/database-controller.h + serverMasterController/controllers/database-controller.cpp serverMasterController/models/client.cpp serverMasterController/models/client.h serverMasterController/models/address.cpp serverMasterController/models/address.h serverMasterController/models/client-search.cpp serverMasterController/models/client-search.h serverMasterController/models/contact.cpp serverMasterController/models/contact.h serverMasterController/models/appointment.cpp serverMasterController/models/appointment.h serverMasterController/framework/command.cpp serverMasterController/framework/command.h serverMasterController/data/data-decorator.cpp serverMasterController/data/data-decorator.h serverMasterController/data/datetime-decorator.cpp serverMasterController/data/datetime-decorator.h serverMasterController/data/entity-collection.h serverMasterController/data/entity.cpp serverMasterController/data/entity.h serverMasterController/data/enumerator-decorator.cpp serverMasterController/data/enumerator-decorator.h serverMasterController/data/int-decorator.cpp serverMasterController/data/int-decorator.h serverMasterController/data/string-decorator.cpp serverMasterController/data/string-decorator.h ) if(ANDROID) list(APPEND gcompris_SRCS ApplicationAndroid.cpp) else() list(APPEND gcompris_SRCS ApplicationSettingsDefault.cpp ApplicationInfoDefault.cpp) endif() # Resources set(GCOMPRIS_RESOURCES "${PROJECT_SOURCE_DIR}/installer") if(CMAKE_HOST_WIN32) set(gcompris_icon GCompris.ico) set(gcompris_RES ${GCOMPRIS_RESOURCES}/${gcompris_icon} GCompris.rc ) elseif(CMAKE_HOST_APPLE) set(gcompris_icon GCompris.icns) set(gcompris_RES ${GCOMPRIS_RESOURCES}/${gcompris_icon}) set_source_files_properties(${gcompris_RES} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") endif() -set(used_qt_modules Qt5::Qml Qt5::Quick Qt5::Gui Qt5::Multimedia Qt5::Core Qt5::Svg Qt5::Xml Qt5::XmlPatterns Qt5::Sensors Qt5::QuickControls2) +set(used_qt_modules Qt5::Qml Qt5::Quick Qt5::Gui Qt5::Multimedia Qt5::Core Qt5::Svg Qt5::Xml Qt5::XmlPatterns Qt5::Sensors Qt5::QuickControls2 Qt5::Sql) if(ANDROID) add_library(${GCOMPRIS_EXECUTABLE_NAME} SHARED ${gcompris_SRCS}) set(used_qt_modules ${used_qt_modules} Qt5::AndroidExtras) elseif(CMAKE_HOST_APPLE) add_executable(${GCOMPRIS_EXECUTABLE_NAME} MACOSX_BUNDLE ${gcompris_SRCS} ${gcompris_RES}) elseif(CMAKE_HOST_WIN32) add_executable(${GCOMPRIS_EXECUTABLE_NAME} WIN32 ${gcompris_SRCS} ${gcompris_RES}) elseif(SAILFISHOS) add_executable(${GCOMPRIS_EXECUTABLE_NAME} ${gcompris_SRCS} ${gcompris_RES}) set(used_qt_modules ${used_qt_modules} Qt5::Widgets) else() add_executable(${GCOMPRIS_EXECUTABLE_NAME} ${gcompris_SRCS} ${gcompris_RES}) endif() # only build the lib for testing purpose if(BUILD_TESTING) add_library(gcompris_core SHARED ${gcompris_SRCS}) target_link_libraries(gcompris_core ${used_qt_modules}) endif() target_link_libraries(${GCOMPRIS_EXECUTABLE_NAME} ${used_qt_modules}) GCOMPRIS_ADD_RCC(core *.qml *.js resource/*.${COMPRESSED_AUDIO} resource/*.gif resource/*.png resource/*.svg resource/bonus/* resource/sounds/* resource/fonts/* qmldir COPYING) # Installation # ============ install(TARGETS ${GCOMPRIS_EXECUTABLE_NAME} ARCHIVE DESTINATION bin RUNTIME DESTINATION bin LIBRARY DESTINATION lib BUNDLE DESTINATION .) if(BUILD_STANDALONE) # Qt plugins to install set(_qt_plugins "") if(NOT SAILFISHOS) list(APPEND _qt_plugins Qt5::QJpegPlugin) endif() if(APPLE) list(APPEND _qt_plugins Qt5::QTgaPlugin Qt5::QTiffPlugin Qt5::QCocoaIntegrationPlugin) elseif(WIN32) list(APPEND _qt_plugins Qt5::QWindowsIntegrationPlugin Qt5::QWindowsAudioPlugin Qt5::AudioCaptureServicePlugin Qt5::DSServicePlugin) elseif(UNIX AND NOT ANDROID AND NOT SAILFISHOS) list(APPEND _qt_plugins Qt5::QXcbIntegrationPlugin Qt5::QXcbEglIntegrationPlugin Qt5::QXcbGlxIntegrationPlugin Qt5::QAlsaPlugin Qt5::QPulseAudioPlugin) endif() list(APPEND _qt_plugins Qt5::genericSensorPlugin Qt5::QtSensorGesturePlugin Qt5::QShakeSensorGesturePlugin) # Qml plugins to install if(WIN32) set(_lib_prefix "") else() set(_lib_prefix "lib") endif() set(_qt_plugins2 imageformats/${_lib_prefix}qsvg) if(UNIX AND NOT ANDROID AND NOT APPLE AND NOT SAILFISHOS) list(APPEND _qt_plugins2 mediaservice/${_lib_prefix}gstaudiodecoder mediaservice/${_lib_prefix}gstcamerabin mediaservice/${_lib_prefix}gstmediacapture mediaservice/${_lib_prefix}gstmediaplayer) elseif(APPLE) list(APPEND _qt_plugins2 audio/${_lib_prefix}qtaudio_coreaudio mediaservice/${_lib_prefix}qavfmediaplayer mediaservice/${_lib_prefix}qtmedia_audioengine mediaservice/${_lib_prefix}qavfcamera) endif() set(_qml_plugins QtGraphicalEffects/${_lib_prefix}qtgraphicaleffectsplugin QtGraphicalEffects/private/${_lib_prefix}qtgraphicaleffectsprivate QtQuick/Window.2/${_lib_prefix}windowplugin QtQuick/Particles.2/${_lib_prefix}particlesplugin QtQuick.2/${_lib_prefix}qtquick2plugin QtMultimedia/${_lib_prefix}declarative_multimedia QtSensors/${_lib_prefix}declarative_sensors) if(NOT SAILFISHOS) list(APPEND _qml_plugins QtQuick/Controls/${_lib_prefix}qtquickcontrolsplugin QtQuick/Layouts/${_lib_prefix}qquicklayoutsplugin) endif() set(GCOMPRIS_OTHER_LIBS) if(APPLE) set(_app gcompris-qt.app) set(_qtconf_destdir ${_app}/Contents/Resources) set(_qt_plugins_destdir ${_app}/Contents/plugins) set(_qt_qml_destdir ${_app}/Contents/qml) set(GCOMPRIS_BUNDLE "\${CMAKE_INSTALL_PREFIX}/${_app}") set_target_properties(gcompris-qt PROPERTIES MACOSX_BUNDLE_INFO_STRING "GCompris, Educational game for children 2 to 10" MACOSX_BUNDLE_ICON_FILE "${gcompris_icon}" MACOSX_BUNDLE_GUI_IDENTIFIER "net.gcompris" MACOSX_BUNDLE_LONG_VERSION_STRING "${GCOMPRIS_MAJOR_VERSION}.${GCOMPRIS_MINOR_VERSION}.${GCOMPRIS_PATCH_VERSION}" MACOSX_BUNDLE_BUNDLE_NAME "gcompris-qt" MACOSX_BUNDLE_SHORT_VERSION_STRING "${GCOMPRIS_VERSION}" MACOSX_BUNDLE_BUNDLE_VERSION "${GCOMPRIS_VERSION}" MACOSX_BUNDLE_COPYRIGHT "GPL License, Copyright 2000-2019 Timothee Giet and Others.") set_source_files_properties(${GCOMPRIS_RESOURCES}/${gcompris_icon} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") else() set(_qtconf_destdir bin) set(_qt_plugins_destdir bin/plugins) set(_qt_qml_destdir bin/qml) if(CMAKE_HOST_WIN32) set(GCOMPRIS_BUNDLE "\${CMAKE_INSTALL_PREFIX}/bin/${GCOMPRIS_EXECUTABLE_NAME}.exe") else() set(GCOMPRIS_BUNDLE "\${CMAKE_INSTALL_PREFIX}/bin/${GCOMPRIS_EXECUTABLE_NAME}") endif() endif() # install qt.conf file install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/qt.conf DESTINATION ${_qtconf_destdir}) # install qt plugins foreach(_plugin ${_qt_plugins}) installQtPlugin(${_plugin} ${_qt_plugins_destdir} _lib) list(APPEND GCOMPRIS_OTHER_LIBS ${_lib}) endforeach() foreach(_plugin ${_qt_plugins2}) if(APPLE) installQtPlugin2(${_plugin} ${_qt_plugins_destdir}/../Plugins _lib) else() installQtPlugin2(${_plugin} ${_qt_plugins_destdir} _lib) endif() list(APPEND GCOMPRIS_OTHER_LIBS ${_lib}) endforeach() # install qml plugins foreach(_plugin ${_qml_plugins}) installQmlPlugin(${_plugin} ${_qt_qml_destdir} _lib) list(APPEND GCOMPRIS_OTHER_LIBS ${_lib}) endforeach() ## install QtGraphicalEffects (which is not a lib but only qml files) # BUT, actually there are some libs in it, and this does not work on APPLE. Moved to _qml_plugins instead # #set(_qml_subdir QtGraphicalEffects) #getQtQmlPath(_qt_qml_path) #install(DIRECTORY ${_qt_qml_path}/QtGraphicalEffects DESTINATION ${_qt_qml_destdir}) # Fix for Linux 'make package' that fails to link with libicu; also package OpenSSL libs from system if(UNIX AND NOT APPLE AND NOT SAILFISHOS AND NOT ANDROID) add_library( libicudata SHARED IMPORTED ) FILE(GLOB LIBICUDATA_SO "${Qt5_DIR}/../../libicudata.so.[0-9][0-9]") if ("${LIBICUDATA_SO}" STREQUAL "") FILE(GLOB LIBICUDATA_SO "/usr/lib/*/libicudata.so.[0-9][0-9]") endif() set_target_properties( libicudata PROPERTIES IMPORTED_LOCATION ${LIBICUDATA_SO} ) add_library( libicui18n SHARED IMPORTED ) FILE(GLOB LIBICUI18N_SO "${Qt5_DIR}/../../libicui18n.so.[0-9][0-9]") if ("${LIBICUI18N_SO}" STREQUAL "") FILE(GLOB LIBICUI18N_SO "/usr/lib/*/libicui18n.so.[0-9][0-9]") endif() set_target_properties( libicui18n PROPERTIES IMPORTED_LOCATION ${LIBICUI18N_SO} ) add_library( libicuuc SHARED IMPORTED ) FILE(GLOB LIBICUUC_SO "${Qt5_DIR}/../../libicuuc.so.[0-9][0-9]") if ("${LIBICUUC_SO}" STREQUAL "") FILE(GLOB LIBICUUC_SO "/usr/lib/*/libicuuc.so.[0-9][0-9]") endif() set_target_properties( libicuuc PROPERTIES IMPORTED_LOCATION ${LIBICUUC_SO} ) TARGET_LINK_LIBRARIES(${GCOMPRIS_EXECUTABLE_NAME} libicudata libicui18n libicuuc) # package installed OpenSSL libraries install(FILES "${OPENSSL_SSL_LIBRARY}" DESTINATION bin) install(FILES "${OPENSSL_CRYPTO_LIBRARY}" DESTINATION bin) install(FILES "${OPENSSL_SSL_LIBRARY}.10" DESTINATION bin) install(FILES "${OPENSSL_CRYPTO_LIBRARY}.10" DESTINATION bin) install(FILES "${OPENSSL_SSL_LIBRARY}.1.0.2k" DESTINATION bin) install(FILES "${OPENSSL_CRYPTO_LIBRARY}.1.0.2k" DESTINATION bin) TARGET_LINK_LIBRARIES(${GCOMPRIS_EXECUTABLE_NAME} OpenSSL::SSL OpenSSL::Crypto) endif() #Add OpenSSL support on Windows builds if(WIN32) if(MINGW) if(CMAKE_SIZEOF_VOID_P EQUAL 8) #64bit set(OPENSSL_DLL_SUFFIX "-x64") endif() install(FILES "${Qt5_DIR}/../../../bin/libcrypto-1_1${OPENSSL_DLL_SUFFIX}.dll" DESTINATION bin) install(FILES "${Qt5_DIR}/../../../bin/libssl-1_1${OPENSSL_DLL_SUFFIX}.dll" DESTINATION bin) else(MINGW) # appveyor install(FILES "${OPENSSL_INCLUDE_DIR}/../libeay32.dll" DESTINATION bin) install(FILES "${OPENSSL_INCLUDE_DIR}/../libssl32.dll" DESTINATION bin) install(FILES "${OPENSSL_INCLUDE_DIR}/../ssleay32.dll" DESTINATION bin) endif() target_link_libraries(${GCOMPRIS_EXECUTABLE_NAME} OpenSSL::SSL OpenSSL::Crypto) endif() endif(BUILD_STANDALONE) # Hack: do not fixup Qt and Qml plugins on Windows because fixup_bundle takes ages (cmake bug ?) -> Johnny : we need this even if it takes time because some required dependencies are pulled here #if(WIN32) set(GCOMPRIS_OTHER_LIBS "") endif() # install fixup_bundle script to resolve and fixup runtime dependencies if(BUILD_STANDALONE AND NOT ANDROID) configure_file(${CMAKE_SOURCE_DIR}/cmake/FixBundle.cmake.in FixBundle.cmake) install(SCRIPT ${CMAKE_CURRENT_BINARY_DIR}/FixBundle.cmake) # install a startup script for linux bundle if(UNIX AND NOT APPLE AND NOT SAILFISHOS) install(PROGRAMS ../../tools/gcompris-qt.sh DESTINATION bin) endif() endif() if(WIN32 AND NOT MINGW) # install libEGL.dll, libGLESv2.dll, d3dcompiler_47.dll from Qt installation. Not sure if there is a clean way to get them... # Qt5_Dir is like C:/Qt/Qt5.5.1/5.5/mingw492_32/lib/cmake/Qt5 install(FILES ${Qt5_DIR}/../../../bin/libEGL.dll DESTINATION bin) install(FILES ${Qt5_DIR}/../../../bin/libGLESv2.dll DESTINATION bin) install(FILES ${Qt5_DIR}/../../../bin/d3dcompiler_47.dll DESTINATION bin) target_link_libraries(${GCOMPRIS_EXECUTABLE_NAME} ${Qt5_DIR}/../../libEGL.lib) target_link_libraries(${GCOMPRIS_EXECUTABLE_NAME} ${Qt5_DIR}/../../libGLESv2.lib) install(FILES ${Qt5_DIR}/../../../bin/opengl32sw.dll DESTINATION bin) # CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS contains visual c++ libraries install(FILES ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS} DESTINATION bin) endif() # Packaging # ========= set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "GCompris is a high quality educational software suite, including a large number of activities for children aged 2 to 10.") set(CPACK_PACKAGE_VERSION_MAJOR ${GCOMPRIS_MAJOR_VERSION}) set(CPACK_PACKAGE_VERSION_MINOR ${GCOMPRIS_MINOR_VERSION}) set(CPACK_PACKAGE_VERSION_PATCH ${GCOMPRIS_PATCH_VERSION}) set(CPACK_PACKAGE_VERSION ${GCOMPRIS_VERSION}) set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/COPYING") set(CPACK_RESOURCE_FILE_README "${PROJECT_SOURCE_DIR}/README") if(WIN32) set(CPACK_PACKAGE_INSTALL_DIRECTORY "${GCOMPRIS_EXECUTABLE_NAME}-Qt") set(CPACK_PACKAGE_INSTALL_REGISTRY_KEY "${GCOMPRIS_EXECUTABLE_NAME}-Qt") set(CPACK_PACKAGE_ICON "${PROJECT_SOURCE_DIR}/installer\\\\gcompris-header.bmp") set(CPACK_NSIS_MUI_ICON "${PROJECT_SOURCE_DIR}/installer\\\\GCompris-install.ico") set(CPACK_NSIS_MUI_UNIICON "${PROJECT_SOURCE_DIR}/installer\\\\GCompris-uninstall.ico") set(CPACK_NSIS_EXECUTABLES_DIRECTORY "bin") set(CPACK_PACKAGE_EXECUTABLES "${GCOMPRIS_EXECUTABLE_NAME};GCompris") set(CPACK_CREATE_DESKTOP_LINKS "${GCOMPRIS_EXECUTABLE_NAME};GCompris") set(CPACK_NSIS_URL_INFO_ABOUT "https:\\\\\\\\gcompris.net") set(CPACK_NSIS_DISPLAY_NAME "GCompris Educational Software") set(CPACK_NSIS_MUI_FINISHPAGE_RUN "${GCOMPRIS_EXECUTABLE_NAME}") set(CPACK_PACKAGE_VENDOR "GCompris team") # Create shortcuts in menu to be able to launch in software or opengl mode list(APPEND CPACK_NSIS_CREATE_ICONS_EXTRA " CreateShortCut '$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\GCompris (Safe Mode).lnk' '$INSTDIR\\\\bin\\\\${GCOMPRIS_EXECUTABLE_NAME}.exe' '--software-renderer'") list(APPEND CPACK_NSIS_CREATE_ICONS_EXTRA " CreateShortCut '$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\GCompris.lnk' '$INSTDIR\\\\bin\\\\${GCOMPRIS_EXECUTABLE_NAME}.exe' '--opengl-renderer'") string (REPLACE ";" "\n" CPACK_NSIS_CREATE_ICONS_EXTRA "${CPACK_NSIS_CREATE_ICONS_EXTRA}") else(WIN32) set(CPACK_PACKAGE_ICON "${PROJECT_SOURCE_DIR}/GCompris64.png") endif() if(APPLE) set(CPACK_GENERATOR "DragNDrop") set(CPACK_DMG_DS_STORE "${GCOMPRIS_RESOURCES}/dmg_DS_Store") set(CPACK_DMG_BACKGROUND_IMAGE "${GCOMPRIS_RESOURCES}/dmg_background.png") elseif(WIN32) set(CPACK_GENERATOR "NSIS") elseif(SAILFISHOS) configure_file(${PROJECT_SOURCE_DIR}/platforms/sailfishOS/harbour-gcompris-qt.spec.cmake ${CMAKE_BINARY_DIR}/harbour-gcompris-qt.spec @ONLY) install(FILES ${PROJECT_SOURCE_DIR}/platforms/sailfishOS/harbour-gcompris-qt.desktop DESTINATION share/applications) install(FILES ${PROJECT_SOURCE_DIR}/platforms/sailfishOS/harbour-gcompris-qt.png DESTINATION share/icons/hicolor/86x86/apps) set(CPACK_RPM_PACKAGE_SUMMARY "gcompris-qt") # BUILD_ARCH is either armv7hl or i486 set(CPACK_RPM_PACKAGE_ARCHITECTURE "${BUILD_ARCH}") set(CPACK_RPM_PACKAGE_NAME "${GCOMPRIS_EXECUTABLE_NAME}") set(CPACK_RPM_PACKAGE_VERSION "${GCOMPRIS_VERSION}") set(CPACK_RPM_PACKAGE_LICENSED "GPLv3") set(CPACK_RPM_PACKAGE_URL "https://www.gcompris.org") set(CPACK_RPM_PACKAGE_DESCRIPTION "GCompris is a high quality educational software suite comprising of numerous activities for children aged 2 to 10.") set(CPACK_RPM_USER_BINARY_SPECFILE "${CMAKE_BINARY_DIR}/harbour-gcompris-qt.spec") set(CMAKE_INSTALL_PREFIX "/usr") set(CPACK_PACKAGING_INSTALL_PREFIX "/usr") set(CPACK_GENERATOR "RPM") else() set(CPACK_GENERATOR "STGZ") endif() include(CPack) diff --git a/src/core/serverMasterController/controllers/command-controller.cpp b/src/core/serverMasterController/controllers/command-controller.cpp index 40bb801ad..eaf3ad927 100644 --- a/src/core/serverMasterController/controllers/command-controller.cpp +++ b/src/core/serverMasterController/controllers/command-controller.cpp @@ -1,47 +1,123 @@ #include "command-controller.h" #include #include using namespace cm::framework; +using namespace cm::models; namespace cm { namespace controllers { class CommandController::Implementation { public: - Implementation(CommandController* _commandController) + Implementation(CommandController* _commandController, IDatabaseController* _databaseController, NavigationController* _navigationController, Client* _newClient, ClientSearch* _clientSearch) : commandController(_commandController) + , databaseController(_databaseController) + , navigationController(_navigationController) + , newClient(_newClient) + , clientSearch(_clientSearch) { Command* createClientSaveCommand = new Command( commandController, QChar( 0xf0c7 ), "Save" ); QObject::connect( createClientSaveCommand, &Command::executed, commandController, &CommandController::onCreateClientSaveExecuted ); createClientViewContextCommands.append( createClientSaveCommand ); + + Command* findClientSearchCommand = new Command( commandController, QChar( 0xf002 ), "Search" ); + QObject::connect( findClientSearchCommand, &Command::executed, commandController, &CommandController::onFindClientSearchExecuted ); + findClientViewContextCommands.append( findClientSearchCommand ); + + Command* editClientDeleteCommand = new Command( commandController, QChar( 0xf235 ), "Delete" ); + QObject::connect( editClientDeleteCommand, &Command::executed, commandController, &CommandController::onEditClientDeleteExecuted ); + editClientViewContextCommands.append( editClientDeleteCommand ); + + Command* editClientSaveCommand = new Command( commandController, QChar( 0xf0c7 ), "Save" ); + QObject::connect( editClientSaveCommand, &Command::executed, commandController, &CommandController::onEditClientSaveExecuted ); + editClientViewContextCommands.append( editClientSaveCommand ); } CommandController* commandController{nullptr}; + IDatabaseController* databaseController{nullptr}; + NavigationController* navigationController{nullptr}; + Client* newClient{nullptr}; + ClientSearch* clientSearch{nullptr}; + Client* selectedClient{nullptr}; QList createClientViewContextCommands{}; + QList findClientViewContextCommands{}; + QList editClientViewContextCommands{}; }; -CommandController::CommandController(QObject* parent) +CommandController::CommandController(QObject* parent, IDatabaseController* databaseController, NavigationController* navigationController, Client* newClient, ClientSearch* clientSearch) : QObject(parent) { - implementation.reset(new Implementation(this)); + implementation.reset(new Implementation(this, databaseController, navigationController, newClient, clientSearch)); } CommandController::~CommandController() { } QQmlListProperty CommandController::ui_createClientViewContextCommands() { return QQmlListProperty(this, implementation->createClientViewContextCommands); } +QQmlListProperty CommandController::ui_findClientViewContextCommands() +{ + return QQmlListProperty(this, implementation->findClientViewContextCommands); +} + +QQmlListProperty CommandController::ui_editClientViewContextCommands() +{ + return QQmlListProperty(this, implementation->editClientViewContextCommands); +} + +void CommandController::setSelectedClient(Client* client) +{ + implementation->selectedClient = client; +} + void CommandController::onCreateClientSaveExecuted() { qDebug() << "You executed the Save command!"; + + implementation->databaseController->createRow(implementation->newClient->key(), implementation->newClient->id(), implementation->newClient->toJson()); + + qDebug() << "New client saved."; + + implementation->clientSearch->searchText()->setValue(implementation->newClient->id()); + implementation->clientSearch->search(); + implementation->navigationController->goFindClientView(); +} + +void CommandController::onFindClientSearchExecuted() +{ + qDebug() << "You executed the Search command!"; + + implementation->clientSearch->search(); +} + +void CommandController::onEditClientSaveExecuted() +{ + qDebug() << "You executed the Save command!"; + + implementation->databaseController->updateRow(implementation->selectedClient->key(), implementation->selectedClient->id(), implementation->selectedClient->toJson()); + + qDebug() << "Updated client saved."; +} + +void CommandController::onEditClientDeleteExecuted() +{ + qDebug() << "You executed the Delete command!"; + + implementation->databaseController->deleteRow(implementation->selectedClient->key(), implementation->selectedClient->id()); + implementation->selectedClient = nullptr; + + qDebug() << "Client deleted."; + + implementation->clientSearch->search(); + implementation->navigationController->goDashboardView(); } }} diff --git a/src/core/serverMasterController/controllers/command-controller.h b/src/core/serverMasterController/controllers/command-controller.h index 3574e202b..ef7957c76 100644 --- a/src/core/serverMasterController/controllers/command-controller.h +++ b/src/core/serverMasterController/controllers/command-controller.h @@ -1,33 +1,46 @@ #ifndef COMMANDCONTROLLER_H #define COMMANDCONTROLLER_H #include #include + #include #include +#include +#include +#include +#include namespace cm { namespace controllers { class CMLIBSHARED_EXPORT CommandController : public QObject { Q_OBJECT Q_PROPERTY(QQmlListProperty ui_createClientViewContextCommands READ ui_createClientViewContextCommands CONSTANT) + Q_PROPERTY(QQmlListProperty ui_findClientViewContextCommands READ ui_findClientViewContextCommands CONSTANT) + Q_PROPERTY(QQmlListProperty ui_editClientViewContextCommands READ ui_editClientViewContextCommands CONSTANT) public: - explicit CommandController(QObject* _parent = nullptr); + explicit CommandController(QObject* _parent = nullptr, IDatabaseController* databaseController = nullptr, controllers::NavigationController* navigationController = nullptr, models::Client* newClient = nullptr, models::ClientSearch* clientSearch = nullptr); ~CommandController(); QQmlListProperty ui_createClientViewContextCommands(); + QQmlListProperty ui_findClientViewContextCommands(); + QQmlListProperty ui_editClientViewContextCommands(); public slots: + void setSelectedClient(cm::models::Client* client); void onCreateClientSaveExecuted(); + void onFindClientSearchExecuted(); + void onEditClientSaveExecuted(); + void onEditClientDeleteExecuted(); private: class Implementation; QScopedPointer implementation; }; }} #endif diff --git a/src/core/serverMasterController/controllers/database-controller.cpp b/src/core/serverMasterController/controllers/database-controller.cpp new file mode 100644 index 000000000..8c3f26638 --- /dev/null +++ b/src/core/serverMasterController/controllers/database-controller.cpp @@ -0,0 +1,191 @@ +#include "database-controller.h" + +#include +#include +#include +#include + +namespace cm { +namespace controllers { + +class DatabaseController::Implementation +{ +public: + Implementation(DatabaseController* _databaseController) + : databaseController(_databaseController) + { + if (initialise()) { + qDebug() << "Database created using Sqlite version: " + sqliteVersion(); + if (createTables()) { + qDebug() << "Database tables created"; + } else { + qDebug() << "ERROR: Unable to create database tables"; + } + } else { + qDebug() << "ERROR: Unable to open database"; + } + } + + DatabaseController* databaseController{nullptr}; + QSqlDatabase database; + +private: + bool initialise() + { + database = QSqlDatabase::addDatabase("QSQLITE", "cm"); + database.setDatabaseName( "cm.sqlite" ); + return database.open(); + } + + bool createTables() + { + return createJsonTable( "client" ); + } + + bool createJsonTable(const QString& tableName) const + { + QSqlQuery query(database); + QString sqlStatement = "CREATE TABLE IF NOT EXISTS " + tableName + " (id text primary key, json text not null)"; + + if (!query.prepare(sqlStatement)) return false; + + return query.exec(); + } + + QString sqliteVersion() const + { + QSqlQuery query(database); + + query.exec("SELECT sqlite_version()"); + + if (query.next()) return query.value(0).toString(); + + return QString::number(-1); + } +}; +} + +namespace controllers { + +DatabaseController::DatabaseController(QObject* parent) + : IDatabaseController(parent) +{ + implementation.reset(new Implementation(this)); +} + +DatabaseController::~DatabaseController() +{ +} + +bool DatabaseController::createRow(const QString& tableName, const QString& id, const QJsonObject& jsonObject) const +{ + if (tableName.isEmpty()) return false; + if (id.isEmpty()) return false; + if (jsonObject.isEmpty()) return false; + + QSqlQuery query(implementation->database); + + QString sqlStatement = "INSERT OR REPLACE INTO " + tableName + " (id, json) VALUES (:id, :json)"; + + if (!query.prepare(sqlStatement)) return false; + + query.bindValue(":id", QVariant(id)); + query.bindValue(":json", QVariant(QJsonDocument(jsonObject).toJson(QJsonDocument::Compact))); + + if(!query.exec()) return false; + + return query.numRowsAffected() > 0; +} + +bool DatabaseController::deleteRow(const QString& tableName, const QString& id) const +{ + if (tableName.isEmpty()) return false; + if (id.isEmpty()) return false; + + QSqlQuery query(implementation->database); + + QString sqlStatement = "DELETE FROM " + tableName + " WHERE id=:id"; + + if (!query.prepare(sqlStatement)) return false; + + query.bindValue(":id", QVariant(id)); + + if(!query.exec()) return false; + + return query.numRowsAffected() > 0; +} + +QJsonArray DatabaseController::find(const QString& tableName, const QString& searchText) const +{ + if (tableName.isEmpty()) return {}; + if (searchText.isEmpty()) return {}; + + QSqlQuery query(implementation->database); + + QString sqlStatement = "SELECT json FROM " + tableName + " where lower(json) like :searchText"; + + if (!query.prepare(sqlStatement)) return {}; + + query.bindValue(":searchText", QVariant("%" + searchText.toLower() + "%")); + + if (!query.exec()) return {}; + + QJsonArray returnValue; + + while ( query.next() ) { + auto json = query.value(0).toByteArray(); + auto jsonDocument = QJsonDocument::fromJson(json); + if (jsonDocument.isObject()) { + returnValue.append(jsonDocument.object()); + } + } + + return returnValue; +} + +QJsonObject DatabaseController::readRow(const QString& tableName, const QString& id) const +{ + if (tableName.isEmpty()) return {}; + if (id.isEmpty()) return {}; + + QSqlQuery query(implementation->database); + + QString sqlStatement = "SELECT json FROM " + tableName + " WHERE id=:id"; + + if (!query.prepare(sqlStatement)) return {}; + + query.bindValue(":id", QVariant(id)); + + if (!query.exec()) return {}; + + if (!query.first()) return {}; + + auto json = query.value(0).toByteArray(); + auto jsonDocument = QJsonDocument::fromJson(json); + + if (!jsonDocument.isObject()) return {}; + + return jsonDocument.object(); +} + +bool DatabaseController::updateRow(const QString& tableName, const QString& id, const QJsonObject& jsonObject) const +{ + if (tableName.isEmpty()) return false; + if (id.isEmpty()) return false; + if (jsonObject.isEmpty()) return false; + + QSqlQuery query(implementation->database); + + QString sqlStatement = "UPDATE " + tableName + " SET json=:json WHERE id=:id"; + + if (!query.prepare(sqlStatement)) return false; + + query.bindValue(":id", QVariant(id)); + query.bindValue(":json", QVariant(QJsonDocument(jsonObject).toJson(QJsonDocument::Compact))); + + if(!query.exec()) return false; + + return query.numRowsAffected() > 0; +} + +}} diff --git a/src/core/serverMasterController/controllers/database-controller.h b/src/core/serverMasterController/controllers/database-controller.h new file mode 100644 index 000000000..5031b4ac3 --- /dev/null +++ b/src/core/serverMasterController/controllers/database-controller.h @@ -0,0 +1,35 @@ +#ifndef DATABASECONTROLLER_H +#define DATABASECONTROLLER_H + +#include +#include + +#include + +#include + +namespace cm { +namespace controllers { + +class CMLIBSHARED_EXPORT DatabaseController : public IDatabaseController +{ + Q_OBJECT + +public: + explicit DatabaseController(QObject* parent = nullptr); + ~DatabaseController(); + + bool createRow(const QString& tableName, const QString& id, const QJsonObject& jsonObject) const override; + bool deleteRow(const QString& tableName, const QString& id) const override; + QJsonArray find(const QString& tableName, const QString& searchText) const override; + QJsonObject readRow(const QString& tableName, const QString& id) const override; + bool updateRow(const QString& tableName, const QString& id, const QJsonObject& jsonObject) const override; + +private: + class Implementation; + QScopedPointer implementation; +}; + +}} + +#endif diff --git a/src/core/serverMasterController/controllers/i-database-controller.h b/src/core/serverMasterController/controllers/i-database-controller.h new file mode 100644 index 000000000..c1eee6b3f --- /dev/null +++ b/src/core/serverMasterController/controllers/i-database-controller.h @@ -0,0 +1,32 @@ +#ifndef IDATABASECONTROLLER_H +#define IDATABASECONTROLLER_H + +#include +#include +#include +#include +#include + +#include + +namespace cm { +namespace controllers { + +class CMLIBSHARED_EXPORT IDatabaseController : public QObject +{ + Q_OBJECT + +public: + IDatabaseController(QObject* parent) : QObject(parent){} + virtual ~IDatabaseController(){} + + virtual bool createRow(const QString& tableName, const QString& id, const QJsonObject& jsonObject) const = 0; + virtual bool deleteRow(const QString& tableName, const QString& id) const = 0; + virtual QJsonArray find(const QString& tableName, const QString& searchText) const = 0; + virtual QJsonObject readRow(const QString& tableName, const QString& id) const = 0; + virtual bool updateRow(const QString& tableName, const QString& id, const QJsonObject& jsonObject) const = 0; +}; + +}} + +#endif diff --git a/src/core/serverMasterController/controllers/master-controller.cpp b/src/core/serverMasterController/controllers/master-controller.cpp index 49c4e9922..b329a8286 100644 --- a/src/core/serverMasterController/controllers/master-controller.cpp +++ b/src/core/serverMasterController/controllers/master-controller.cpp @@ -1,56 +1,75 @@ #include "master-controller.h" using namespace cm::models; namespace cm { namespace controllers { class MasterController::Implementation { public: Implementation(MasterController* _masterController) : masterController(_masterController) { - commandController = new CommandController(masterController); + databaseController = new DatabaseController(masterController); navigationController = new NavigationController(masterController); - newClient = new Client(masterController); + newClient = new Client(masterController); + clientSearch = new ClientSearch(masterController, databaseController); + commandController = new CommandController(masterController, databaseController, navigationController, newClient, clientSearch); } MasterController* masterController{nullptr}; CommandController* commandController{nullptr}; + DatabaseController* databaseController{nullptr}; NavigationController* navigationController{nullptr}; - Client* newClient{nullptr}; - QString welcomeMessage = "This is MasterController to Major Tom"; + Client* newClient{nullptr}; + ClientSearch* clientSearch{nullptr}; + QString welcomeMessage = "Welcome to the Client Management system!"; }; MasterController::MasterController(QObject* parent) : QObject(parent) { implementation.reset(new Implementation(this)); } MasterController::~MasterController() { } CommandController* MasterController::commandController() { return implementation->commandController; } +DatabaseController* MasterController::databaseController() +{ + return implementation->databaseController; +} + NavigationController* MasterController::navigationController() { return implementation->navigationController; } Client* MasterController::newClient() { - return implementation->newClient; + return implementation->newClient; +} + +ClientSearch* MasterController::clientSearch() +{ + return implementation->clientSearch; } const QString& MasterController::welcomeMessage() const { return implementation->welcomeMessage; } +void MasterController::selectClient(Client* client) +{ + implementation->navigationController->goEditClientView(client); +} + }} diff --git a/src/core/serverMasterController/controllers/master-controller.h b/src/core/serverMasterController/controllers/master-controller.h index 29b44da56..b813684ea 100644 --- a/src/core/serverMasterController/controllers/master-controller.h +++ b/src/core/serverMasterController/controllers/master-controller.h @@ -1,40 +1,50 @@ #ifndef MASTERCONTROLLER_H #define MASTERCONTROLLER_H #include #include #include #include #include +#include #include +#include +#include namespace cm { namespace controllers { class CMLIBSHARED_EXPORT MasterController : public QObject { Q_OBJECT Q_PROPERTY( QString ui_welcomeMessage READ welcomeMessage CONSTANT ) Q_PROPERTY( cm::controllers::NavigationController* ui_navigationController READ navigationController CONSTANT ) Q_PROPERTY( cm::controllers::CommandController* ui_commandController READ commandController CONSTANT ) - Q_PROPERTY( cm::models::Client* ui_newClient READ newClient CONSTANT ) + Q_PROPERTY( cm::controllers::DatabaseController* ui_databaseController READ databaseController CONSTANT ) + Q_PROPERTY( cm::models::Client* ui_newClient READ newClient CONSTANT ) + Q_PROPERTY( cm::models::ClientSearch* ui_clientSearch READ clientSearch CONSTANT ) public: explicit MasterController(QObject* parent = nullptr); ~MasterController(); CommandController* commandController(); + DatabaseController* databaseController(); NavigationController* navigationController(); + models::Client* newClient(); + models::ClientSearch* clientSearch(); const QString& welcomeMessage() const; - models::Client* newClient(); + +public slots: + void selectClient(cm::models::Client* client); private: class Implementation; QScopedPointer implementation; }; }} #endif diff --git a/src/core/serverMasterController/data/entity.cpp b/src/core/serverMasterController/data/entity.cpp index 5acf1f059..aa4751e55 100644 --- a/src/core/serverMasterController/data/entity.cpp +++ b/src/core/serverMasterController/data/entity.cpp @@ -1,117 +1,141 @@ #include "entity.h" #include +#include namespace cm { namespace data { class Entity::Implementation { public: Implementation(Entity* _entity, const QString& _key) : entity(_entity) , key(_key) + , id(QUuid::createUuid().toString()) { } Entity* entity{nullptr}; QString key; + QString id; + StringDecorator* primaryKey{nullptr}; std::map childCollections; std::map childEntities; std::map dataDecorators; }; Entity::Entity(QObject* parent, const QString& key) : QObject(parent) { implementation.reset(new Implementation(this, key)); } Entity::Entity(QObject* parent, const QString& key, const QJsonObject& jsonObject) : Entity(parent, key) { update(jsonObject); } Entity::~Entity() { } + +const QString& Entity::id() const +{ + if(implementation->primaryKey != nullptr && !implementation->primaryKey->value().isEmpty()) { + return implementation->primaryKey->value(); + } + + return implementation->id; +} + const QString& Entity::key() const { return implementation->key; } +void Entity::setPrimaryKey(StringDecorator* primaryKey) +{ + implementation->primaryKey = primaryKey; +} + Entity* Entity::addChild(Entity* entity, const QString& key) { if(implementation->childEntities.find(key) == std::end(implementation->childEntities)) { implementation->childEntities[key] = entity; emit childEntitiesChanged(); } return entity; } EntityCollectionBase* Entity::addChildCollection(EntityCollectionBase* entityCollection) { if(implementation->childCollections.find(entityCollection->getKey()) == std::end(implementation->childCollections)) { implementation->childCollections[entityCollection->getKey()] = entityCollection; emit childCollectionsChanged(entityCollection->getKey()); } return entityCollection; } DataDecorator* Entity::addDataItem(DataDecorator* dataDecorator) { if(implementation->dataDecorators.find(dataDecorator->key()) == std::end(implementation->dataDecorators)) { implementation->dataDecorators[dataDecorator->key()] = dataDecorator; emit dataDecoratorsChanged(); } return dataDecorator; } void Entity::update(const QJsonObject& jsonObject) { + if (jsonObject.contains("id")) { + implementation->id = jsonObject.value("id").toString(); + } + // Update data decorators for (std::pair dataDecoratorPair : implementation->dataDecorators) { dataDecoratorPair.second->update(jsonObject); } // Update child entities for (std::pair childEntityPair : implementation->childEntities) { childEntityPair.second->update(jsonObject.value(childEntityPair.first).toObject()); } // Update child collections for (std::pair childCollectionPair : implementation->childCollections) { childCollectionPair.second->update(jsonObject.value(childCollectionPair.first).toArray()); } } QJsonObject Entity::toJson() const { QJsonObject returnValue; + returnValue.insert("id", implementation->id); // Add data decorators for (std::pair dataDecoratorPair : implementation->dataDecorators) { returnValue.insert( dataDecoratorPair.first, dataDecoratorPair.second->jsonValue() ); } // Add child entities for (std::pair childEntityPair : implementation->childEntities) { returnValue.insert( childEntityPair.first, childEntityPair.second->toJson() ); } // Add child collections for (std::pair childCollectionPair : implementation->childCollections) { QJsonArray entityArray; for (Entity* entity : childCollectionPair.second->baseEntities()) { entityArray.append( entity->toJson() ); } returnValue.insert( childCollectionPair.first, entityArray ); } return returnValue; } }} diff --git a/src/core/serverMasterController/data/entity.h b/src/core/serverMasterController/data/entity.h index 5b674f24f..ebfdfcfe7 100644 --- a/src/core/serverMasterController/data/entity.h +++ b/src/core/serverMasterController/data/entity.h @@ -1,47 +1,50 @@ #ifndef ENTITY_H #define ENTITY_H #include #include #include #include #include +#include #include namespace cm { namespace data { class CMLIBSHARED_EXPORT Entity : public QObject { Q_OBJECT public: Entity(QObject* parent = nullptr, const QString& key = "SomeEntityKey"); Entity(QObject* parent, const QString& key, const QJsonObject& jsonObject); virtual ~Entity(); public: + const QString& id() const; const QString& key() const; + void setPrimaryKey(StringDecorator* primaryKey); void update(const QJsonObject& jsonObject); QJsonObject toJson() const; signals: void childCollectionsChanged(const QString& collectionKey); void childEntitiesChanged(); void dataDecoratorsChanged(); protected: Entity* addChild(Entity* entity, const QString& key); EntityCollectionBase* addChildCollection(EntityCollectionBase* entityCollection); DataDecorator* addDataItem(DataDecorator* dataDecorator); protected: class Implementation; QScopedPointer implementation; }; }} #endif diff --git a/src/core/serverMasterController/models/client-search.h b/src/core/serverMasterController/models/client-search.h index 1b4fbe6fc..c34d41809 100644 --- a/src/core/serverMasterController/models/client-search.h +++ b/src/core/serverMasterController/models/client-search.h @@ -1,40 +1,40 @@ #ifndef CLIENTSEARCH_H #define CLIENTSEARCH_H #include #include -//#include +#include #include #include #include #include namespace cm { namespace models { class CMLIBSHARED_EXPORT ClientSearch : public data::Entity { Q_OBJECT Q_PROPERTY( cm::data::StringDecorator* ui_searchText READ searchText CONSTANT ) Q_PROPERTY( QQmlListProperty ui_searchResults READ ui_searchResults NOTIFY searchResultsChanged ) public: -// ClientSearch(QObject* parent = nullptr, controllers::IDatabaseController* databaseController = nullptr); + ClientSearch(QObject* parent = nullptr, controllers::IDatabaseController* databaseController = nullptr); ~ClientSearch(); data::StringDecorator* searchText(); QQmlListProperty ui_searchResults(); void search(); signals: void searchResultsChanged(); private: class Implementation; QScopedPointer implementation; }; }} #endif