diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..f59ffda --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "external/CatalogueAPI"] + path = external/CatalogueAPI + url = https://hephaistos.lpp.polytechnique.fr/rhodecode/HG_REPOSITORIES/LPP/INSTRUMENTATION/USERS/JEANDET/CatalogueAPI +[submodule "external/libcatalogs"] + path = external/libcatalogs + url = https://hephaistos.lpp.polytechnique.fr/rhodecode/HG_REPOSITORIES/LPP/SciQLOP_Repos/libcatalogs diff --git a/CMakeLists.txt b/CMakeLists.txt index 52bef36..6d3dddc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,18 +1,62 @@ - +cmake_minimum_required(VERSION 3.6) +project(SciQLOP CXX) -## Main CMakeLists for SCIQLOP -CMAKE_MINIMUM_REQUIRED (VERSION 3.6.1) -cmake_policy(VERSION 3.6) +include(GNUInstallDirs) -PROJECT(SCIQLOP) +SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_LIST_DIR}/cmake") +OPTION (CPPCHECK "Analyzes the source code with cppcheck" OFF) +OPTION (CLANG_TIDY "Analyzes the source code with Clang Tidy" OFF) +OPTION (IWYU "Analyzes the source code with Include What You Use" OFF) -# -# build the CatalogueAPI dependencu -# -INCLUDE("extern/CatalogueAPI.cmake") +set(CMAKE_CXX_STANDARD 14) -# -# build the project -# -INCLUDE("cmake/sciqlop.cmake") +set(CMAKE_AUTOMOC ON) +#https://gitlab.kitware.com/cmake/cmake/issues/15227 +#set(CMAKE_AUTOUIC ON) +if(POLICY CMP0071) + cmake_policy(SET CMP0071 OLD) +endif() +set(CMAKE_AUTORCC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +if(NOT DEFINED CMAKE_INSTALL_RPATH_USE_LINK_PATH) + set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) +endif() +if(NOT DEFINED CMAKE_MACOSX_RPATH) + set(CMAKE_MACOSX_RPATH TRUE) +endif() + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Release" CACHE STRING "" FORCE) +endif() + +find_package(Qt5 COMPONENTS Core Widgets Network PrintSupport Svg Test REQUIRED) + +IF(CPPCHECK) + set(CMAKE_CXX_CPPCHECK "cppcheck;--enable=warning,style") +ENDIF(CPPCHECK) + +IF(CLANG_TIDY) + set(CMAKE_CXX_CLANG_TIDY "clang-tidy;-style=file;-checks=*") +ENDIF(CLANG_TIDY) + +IF(IWYU) + set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE "include-what-you-use") +ENDIF(IWYU) + +enable_testing() + + +find_package(catalogs CONFIG QUIET) +if (NOT CatalogueAPI_FOUND) + execute_process(COMMAND git submodule init external/libcatalogs WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + execute_process(COMMAND git submodule update external/libcatalogs WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + add_subdirectory(external/libcatalogs) +endif() + +add_subdirectory(core) +add_subdirectory(gui) +add_subdirectory(app) +add_subdirectory(plugins) +add_subdirectory(docs) diff --git a/analyzer/cmake/FindClangAnalyzer.cmake b/analyzer/cmake/FindClangAnalyzer.cmake deleted file mode 100644 index b13c0a7..0000000 --- a/analyzer/cmake/FindClangAnalyzer.cmake +++ /dev/null @@ -1,42 +0,0 @@ -# - try to find scan-build tool -# -# Cache Variables: -# CLANGANALYZER_ROOT_DIR -# CLANGANALYZER_EXECUTABLE -# -# Non-cache variables you might use in your CMakeLists.txt: -# CLANGANALYZER_FOUND -# -# Requires these CMake modules: -# FindPackageHandleStandardArgs (known included with CMake >=2.6.2) - -file(TO_CMAKE_PATH "${CLANGANALYZER_ROOT_DIR}" CLANGANALYZER_ROOT_DIR) -set(CLANGANALYZER_ROOT_DIR - "${CLANGANALYZER_ROOT_DIR}" - CACHE - PATH - "Path to search for scan-build") - -if(CLANGANALYZER_EXECUTABLE AND NOT EXISTS "${CLANGANALYZER_EXECUTABLE}") - set(CLANGANALYZER_EXECUTABLE "notfound" CACHE PATH FORCE "") -endif() - -# If we have a custom path, look there first. -if(CLANGANALYZER_ROOT_DIR) - find_program(CLANGANALYZER_EXECUTABLE - NAMES - scan-build - PATHS - "${CLANGANALYZER_ROOT_DIR}" - PATH_SUFFIXES - bin - NO_DEFAULT_PATH) -endif() - -find_program(CLANGANALYZER_EXECUTABLE NAMES scan-build) - -IF(NOT("${CLANGANALYZER_EXECUTABLE}" STREQUAL "")) - set(CLANGANALYZER_FOUND TRUE) -endif() - -mark_as_advanced(CLANGANALYZER_EXECUTABLE) diff --git a/analyzer/cmake/Findcppcheck.cmake b/analyzer/cmake/Findcppcheck.cmake deleted file mode 100644 index 2dcf799..0000000 --- a/analyzer/cmake/Findcppcheck.cmake +++ /dev/null @@ -1,167 +0,0 @@ -# - try to find cppcheck tool -# -# Cache Variables: -# CPPCHECK_EXECUTABLE -# -# Non-cache variables you might use in your CMakeLists.txt: -# CPPCHECK_FOUND -# CPPCHECK_POSSIBLEERROR_ARG -# CPPCHECK_UNUSEDFUNC_ARG -# CPPCHECK_STYLE_ARG -# CPPCHECK_QUIET_ARG -# CPPCHECK_INCLUDEPATH_ARG -# CPPCHECK_FAIL_REGULAR_EXPRESSION -# CPPCHECK_WARN_REGULAR_EXPRESSION -# CPPCHECK_MARK_AS_ADVANCED - whether to mark our vars as advanced even -# if we don't find this program. -# -# Requires these CMake modules: -# FindPackageHandleStandardArgs (known included with CMake >=2.6.2) -# -# Original Author: -# 2009-2010 Ryan Pavlik -# http://academic.cleardefinition.com -# Iowa State University HCI Graduate Program/VRAC -# -# Copyright Iowa State University 2009-2010. -# Distributed under the Boost Software License, Version 1.0. -# (See accompanying file LICENSE_1_0.txt or copy at -# http://www.boost.org/LICENSE_1_0.txt) - -file(TO_CMAKE_PATH "${CPPCHECK_ROOT_DIR}" CPPCHECK_ROOT_DIR) -set(CPPCHECK_ROOT_DIR - "${CPPCHECK_ROOT_DIR}" - CACHE - PATH - "Path to search for cppcheck") - -# cppcheck app bundles on Mac OS X are GUI, we want command line only -set(_oldappbundlesetting ${CMAKE_FIND_APPBUNDLE}) -set(CMAKE_FIND_APPBUNDLE NEVER) - -if(CPPCHECK_EXECUTABLE AND NOT EXISTS "${CPPCHECK_EXECUTABLE}") - set(CPPCHECK_EXECUTABLE "notfound" CACHE PATH FORCE "") -endif() - -# If we have a custom path, look there first. -if(CPPCHECK_ROOT_DIR) - find_program(CPPCHECK_EXECUTABLE - NAMES - cppcheck - cli - PATHS - "${CPPCHECK_ROOT_DIR}" - PATH_SUFFIXES - cli - NO_DEFAULT_PATH) -endif() - -find_program(CPPCHECK_EXECUTABLE NAMES cppcheck) - -# Restore original setting for appbundle finding -set(CMAKE_FIND_APPBUNDLE ${_oldappbundlesetting}) - -# Find out where our test file is -get_filename_component(_cppcheckmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) -set(_cppcheckdummyfile "${_cppcheckmoddir}/Findcppcheck.cpp") -if(NOT EXISTS "${_cppcheckdummyfile}") - message(FATAL_ERROR - "Missing file ${_cppcheckdummyfile} - should be alongside Findcppcheck.cmake, can be found at https://github.com/rpavlik/cmake-modules") -endif() - -function(_cppcheck_test_arg _resultvar _arg) - if(NOT CPPCHECK_EXECUTABLE) - set(${_resultvar} NO) - return() - endif() - execute_process(COMMAND - "${CPPCHECK_EXECUTABLE}" - "${_arg}" - "--quiet" - "${_cppcheckdummyfile}" - RESULT_VARIABLE - _cppcheck_result - OUTPUT_QUIET - ERROR_QUIET) - if("${_cppcheck_result}" EQUAL 0) - set(${_resultvar} YES PARENT_SCOPE) - else() - set(${_resultvar} NO PARENT_SCOPE) - endif() -endfunction() - -function(_cppcheck_set_arg_var _argvar _arg) - if("${${_argvar}}" STREQUAL "") - _cppcheck_test_arg(_cppcheck_arg "${_arg}") - if(_cppcheck_arg) - set(${_argvar} "${_arg}" PARENT_SCOPE) - endif() - endif() -endfunction() - -if(CPPCHECK_EXECUTABLE) - - # Check for the two types of command line arguments by just trying them - _cppcheck_set_arg_var(CPPCHECK_STYLE_ARG "--enable=style") - _cppcheck_set_arg_var(CPPCHECK_STYLE_ARG "--style") - if("${CPPCHECK_STYLE_ARG}" STREQUAL "--enable=style") - - _cppcheck_set_arg_var(CPPCHECK_UNUSEDFUNC_ARG - "--enable=unusedFunction") - _cppcheck_set_arg_var(CPPCHECK_INFORMATION_ARG "--enable=information") - _cppcheck_set_arg_var(CPPCHECK_MISSINGINCLUDE_ARG - "--enable=missingInclude") - _cppcheck_set_arg_var(CPPCHECK_POSIX_ARG "--enable=posix") - _cppcheck_set_arg_var(CPPCHECK_POSSIBLEERROR_ARG - "--enable=possibleError") - _cppcheck_set_arg_var(CPPCHECK_POSSIBLEERROR_ARG "--enable=all") - - if(MSVC) - set(CPPCHECK_TEMPLATE_ARG --template vs) - set(CPPCHECK_FAIL_REGULAR_EXPRESSION "[(]error[)]") - set(CPPCHECK_WARN_REGULAR_EXPRESSION "[(]style[)]") - elseif(CMAKE_COMPILER_IS_GNUCXX) - set(CPPCHECK_TEMPLATE_ARG --template gcc) - set(CPPCHECK_FAIL_REGULAR_EXPRESSION " error: ") - set(CPPCHECK_WARN_REGULAR_EXPRESSION " style: ") - else() - set(CPPCHECK_TEMPLATE_ARG --template gcc) - set(CPPCHECK_FAIL_REGULAR_EXPRESSION " error: ") - set(CPPCHECK_WARN_REGULAR_EXPRESSION " style: ") - endif() - elseif("${CPPCHECK_STYLE_ARG}" STREQUAL "--style") - # Old arguments - _cppcheck_set_arg_var(CPPCHECK_UNUSEDFUNC_ARG "--unused-functions") - _cppcheck_set_arg_var(CPPCHECK_POSSIBLEERROR_ARG "--all") - set(CPPCHECK_FAIL_REGULAR_EXPRESSION "error:") - set(CPPCHECK_WARN_REGULAR_EXPRESSION "[(]style[)]") - else() - # No idea - some other issue must be getting in the way - message(STATUS - "WARNING: Can't detect whether CPPCHECK wants new or old-style arguments!") - endif() - - set(CPPCHECK_QUIET_ARG "--quiet") - set(CPPCHECK_INCLUDEPATH_ARG "-I") - -endif() - -set(CPPCHECK_ALL - "${CPPCHECK_EXECUTABLE} ${CPPCHECK_POSSIBLEERROR_ARG} ${CPPCHECK_UNUSEDFUNC_ARG} ${CPPCHECK_STYLE_ARG} ${CPPCHECK_QUIET_ARG} ${CPPCHECK_INCLUDEPATH_ARG} some/include/path") - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(cppcheck - DEFAULT_MSG - CPPCHECK_ALL - CPPCHECK_EXECUTABLE - CPPCHECK_POSSIBLEERROR_ARG - CPPCHECK_UNUSEDFUNC_ARG - CPPCHECK_STYLE_ARG - CPPCHECK_INCLUDEPATH_ARG - CPPCHECK_QUIET_ARG) - -if(CPPCHECK_FOUND OR CPPCHECK_MARK_AS_ADVANCED) - mark_as_advanced(CPPCHECK_ROOT_DIR) -endif() - -mark_as_advanced(CPPCHECK_EXECUTABLE) diff --git a/analyzer/cmake/Findcppcheck.cpp b/analyzer/cmake/Findcppcheck.cpp deleted file mode 100644 index 84350db..0000000 --- a/analyzer/cmake/Findcppcheck.cpp +++ /dev/null @@ -1,16 +0,0 @@ -/** - * \file Findcppcheck.cpp - * \brief Dummy C++ source file used by CMake module Findcppcheck.cmake - * - * \author - * Ryan Pavlik, 2009-2010 - * - * http://academic.cleardefinition.com/ - * - */ - - - -int main(int argc, char* argv[]) { - return 0; -} diff --git a/analyzer/launch-clang-analyzer-linux.sh b/analyzer/launch-clang-analyzer-linux.sh deleted file mode 100644 index 63ce517..0000000 --- a/analyzer/launch-clang-analyzer-linux.sh +++ /dev/null @@ -1,18 +0,0 @@ - -export CC=/usr/libexec/ccc-analyzer -export CXX=/usr/libexec/c++-analyzer -export CCC_CC=clang -export CCC_CXX=clang++ -export LD=clang++ -export CCC_ANALYZER_VERBOSE=1 - -LD_LIBRARY_PATH=/usr/local/lib64 -export LD_LIBRARY_PATH - -rm -rf build_clang-analyzer -mkdir build_clang-analyzer -cd build_clang-analyzer - -scan-build cmake -DCMAKE_CXX_COMPILER=clazy -DENABLE_ANALYSIS=false -DENABLE_CPPCHECK=false -DENABLE_FORMATTING=false -DENABLE_CHECKSTYLE=false -BUILD_DOCUMENTATION=false -BUILD_TESTS=false -DCMAKE_BUILD_TYPE=Debug ../../SCIQLOP-Initialisation/ - -scan-build -o clang-analyzer-output make -j2 diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 251e0a7..e381645 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -1,157 +1,34 @@ +include_directories(include) -## sciqlop - CMakeLists.txt -SET(EXECUTABLE_NAME "sciqlop") -SCIQLOP_SET_TO_PARENT_SCOPE(EXECUTABLE_NAME) -SET(SOURCES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src/) -SET(INCLUDES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include") -SET(UI_FOLDER ${CMAKE_CURRENT_SOURCE_DIR}/ui) -SET(RES_FOLDER ${CMAKE_CURRENT_SOURCE_DIR}/resources) +FILE (GLOB_RECURSE app_SRCS + include/*.h + src/*.cpp + resources/*.qrc + ) -# Include gui directory -include_directories("${INCLUDES_DIR}") - -# -# Find Qt modules -# -SCIQLOP_FIND_QT(Core Widgets) - - -# -# Find dependent libraries -# ======================== -find_package(sciqlop-gui) - -SET(LIBRARIES ${SCIQLOP-GUI_LIBRARIES}) -SET(EXTERN_SHARED_LIBRARIES) - -INCLUDE_DIRECTORIES(${SCIQLOP-GUI_INCLUDE_DIR}) - -# Add sqpcore to the list of libraries to use -list(APPEND LIBRARIES ${SQPCORE_LIBRARY_NAME}) - -# Include core directory -include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../core/include") - -# Add dependent shared libraries -list(APPEND SHARED_LIBRARIES ${SQPCORE_SHARED_LIBRARIES}) - -# Retrieve the location of the dynamic library to copy it to the output path -#get_property(sqpcoreLocation TARGET ${SQPCORE_LIBRARY_NAME} PROPERTY LOCATION) -list(APPEND SHARED_LIBRARIES_FROM_TARGETS ${sqpcoreLocation}) - -# Ui files -FILE (GLOB_RECURSE PROJECT_FORMS ${UI_FOLDER}/*.ui) - -# -# Compile the application -# -FILE (GLOB_RECURSE APPLICATION_SOURCES - ${INCLUDES_DIR}/*.h - ${SOURCES_DIR}/*.c - ${SOURCES_DIR}/*.cpp - ${SOURCES_DIR}/*.h - ${PROJECT_FORMS}) - -FILE (GLOB_RECURSE APPLICATION_RESOURCES ${RES_FOLDER}/*.qrc) - -QT5_ADD_RESOURCES(RCC_HDRS ${APPLICATION_RESOURCES} ) - -QT5_WRAP_UI(UIS_HDRS - ${PROJECT_FORMS} +QT5_WRAP_UI(UiGenerated_SRCS + ui/MainWindow.ui ) - -ADD_EXECUTABLE(${EXECUTABLE_NAME} ${APPLICATION_SOURCES} ${RCC_HDRS} ${UIS_HDRS}) -set_property(TARGET ${EXECUTABLE_NAME} PROPERTY CXX_STANDARD 14) -set_property(TARGET ${EXECUTABLE_NAME} PROPERTY CXX_STANDARD_REQUIRED ON) -target_link_libraries(${EXECUTABLE_NAME} - ${LIBRARIES}) - -INSTALL(TARGETS ${EXECUTABLE_NAME} - RUNTIME DESTINATION ${INSTALL_BINARY_DIR} - LIBRARY DESTINATION ${INSTALL_BINARY_DIR} - ARCHIVE DESTINATION ${INSTALL_BINARY_DIR} +add_executable(sciqlopapp ${app_SRCS} ${UiGenerated_SRCS}) +if(NOT BUILD_SHARED_LIBS) + add_definitions(-DQT_STATICPLUGIN) + target_link_libraries(sciqlopapp mockplugin) + target_link_libraries(sciqlopapp amdaplugin) +endif() + +target_link_libraries(sciqlopapp + Qt5::Core + Qt5::Widgets + Qt5::Network + Qt5::PrintSupport + Qt5::Svg + sciqlopgui + sciqlopcore ) -# Link with Qt5 modules -qt5_use_modules(${EXECUTABLE_NAME} Core Widgets) - - -add_dependencies(${EXECUTABLE_NAME} ${SQPGUI_LIBRARY_NAME} ${SQPCORE_LIBRARY_NAME}) - - - -# Add the files to the list of files to be analyzed -LIST(APPEND CHECKSTYLE_INPUT_FILES ${APPLICATION_SOURCES}) -SCIQLOP_SET_TO_PARENT_SCOPE(CHECKSTYLE_INPUT_FILES) -# Vera++ exclusion files -LIST(APPEND CHECKSTYLE_EXCLUSION_FILES ${CMAKE_CURRENT_SOURCE_DIR}/vera-exclusions/exclusions.txt) -SCIQLOP_SET_TO_PARENT_SCOPE(CHECKSTYLE_EXCLUSION_FILES) - -# -# Compile the tests -# -IF(BUILD_TESTS) - - INCLUDE_DIRECTORIES(${SOURCES_DIR}) - FILE (GLOB_RECURSE TESTS_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/Test*.cpp) - FILE (GLOB_RECURSE TESTS_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/Test*.h) - SET( TEST_LIBRARIES ${LIBRARIES}) - - FOREACH( testFile ${TESTS_SOURCES} ) - GET_FILENAME_COMPONENT( testDirectory ${testFile} DIRECTORY ) - GET_FILENAME_COMPONENT( testName ${testFile} NAME_WE ) - - # Add to the list of sources files all the sources in the same - # directory that aren't another test - FILE (GLOB currentTestSources - ${testDirectory}/*.c - ${testDirectory}/*.cpp - ${testDirectory}/*.h) - LIST (REMOVE_ITEM currentTestSources ${TESTS_SOURCES}) - LIST (REMOVE_ITEM currentTestSources ${TESTS_HEADERS}) - - ADD_EXECUTABLE(${testName} ${testFile} ${currentTestSources}) - TARGET_LINK_LIBRARIES( ${testName} ${TEST_LIBRARIES}) - qt5_use_modules(${testName} Test) - - ADD_TEST( NAME ${testName} COMMAND ${testName} ) - - SCIQLOP_COPY_TO_TARGET(RUNTIME ${testName}) - ENDFOREACH( testFile ) - - LIST(APPEND testFilesToFormat ${TESTS_SOURCES}) - LIST(APPEND testFilesToFormat ${TESTS_HEADERS}) - LIST(APPEND FORMATTING_INPUT_FILES ${testFilesToFormat}) - - SCIQLOP_SET_TO_PARENT_SCOPE(FORMATTING_INPUT_FILES) -ENDIF(BUILD_TESTS) -# -# Set the files that must be formatted by clang-format. -# -LIST (APPEND FORMATTING_INPUT_FILES ${APPLICATION_SOURCES}) -SCIQLOP_SET_TO_PARENT_SCOPE(FORMATTING_INPUT_FILES) +install(TARGETS sciqlopapp DESTINATION ${CMAKE_INSTALL_BINDIR}) +install(FILES resources/SciQLOP.desktop DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications/) +install(FILES resources/sciqlopLOGO.svg DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/SciQLOP/icons/) -# -# Set the directories that doxygen must browse to generate the -# documentation. -# -# Source directories: -LIST (APPEND DOXYGEN_INPUT_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/docs") -LIST (APPEND DOXYGEN_INPUT_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/src") -SCIQLOP_SET_TO_PARENT_SCOPE(DOXYGEN_INPUT_DIRS) -# Source directories to exclude from the documentation generation -#LIST (APPEND DOXYGEN_EXCLUDE_PATTERNS "${CMAKE_CURRENT_SOURCE_DIR}/path/to/subdir/*") -SCIQLOP_SET_TO_PARENT_SCOPE(DOXYGEN_EXCLUDE_PATTERNS) -# -# Set the directories with the sources to analyze and propagate the -# modification to the parent scope -# -# Source directories to analyze: -LIST (APPEND ANALYSIS_INPUT_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/src") -LIST (APPEND ANALYSIS_INPUT_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/tests") -SCIQLOP_SET_TO_PARENT_SCOPE(ANALYSIS_INPUT_DIRS) -# Source directories to exclude from the analysis -#LIST (APPEND ANALYSIS_EXCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/path/to/subdir") -SCIQLOP_SET_TO_PARENT_SCOPE(ANALYSIS_EXCLUDE_DIRS) diff --git a/app/resources/SciQLOP.desktop b/app/resources/SciQLOP.desktop new file mode 100644 index 0000000..d928f6c --- /dev/null +++ b/app/resources/SciQLOP.desktop @@ -0,0 +1,18 @@ +[Desktop Entry] +Version=1.0 +Name=QLop +Name[en_US]=SciQLOP + +Type=Application +GenericName=SciQLOP +Comment=SCIentific Qt application for Learning from Observations of Plasmas + +Exec=sciqlopapp +Icon=/usr/share/SciQLOP/icons/sciqlopLOGO.svg +Terminal=false + +Categories=Qt;Education;Science; + +#MimeType=text/x-python; + + diff --git a/app/src/Main.cpp b/app/src/Main.cpp index 8cc71ae..b3c8c10 100644 --- a/app/src/Main.cpp +++ b/app/src/Main.cpp @@ -25,7 +25,7 @@ #include #include -#include +#include #include #include @@ -53,6 +53,8 @@ int main(int argc, char *argv[]) SqpApplication::setOrganizationDomain("lpp.fr"); SqpApplication::setApplicationName("SciQLop"); + QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + SqpApplication a{argc, argv}; MainWindow w; diff --git a/app/src/MainWindow.cpp b/app/src/MainWindow.cpp index 02ff19c..d195733 100644 --- a/app/src/MainWindow.cpp +++ b/app/src/MainWindow.cpp @@ -133,7 +133,7 @@ MainWindow::MainWindow(QWidget *parent) auto openInspector = [this](bool checked, bool right, auto action) { - action->setIcon(QIcon{(checked xor right) ? ":/icones/next.png" : ":/icones/previous.png"}); + action->setIcon(QIcon{(checked ^ right) ? ":/icones/next.png" : ":/icones/previous.png"}); auto &lastInspectorSize = right ? impl->m_LastOpenRightInspectorSize : impl->m_LastOpenLeftInspectorSize; @@ -386,8 +386,8 @@ bool MainWindow::MainWindowPrivate::checkDataToSave(QWidget *parentWidget) if (hasChanges) { // There are some unsaved changes switch (QMessageBox::question( - parentWidget, "Save changes", - tr("The catalogue controller unsaved changes.\nDo you want to save them ?"), + parentWidget, tr("Save changes"), + tr("The catalogue controller has unsaved changes.\nDo you want to save them ?"), QMessageBox::SaveAll | QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::SaveAll)) { case QMessageBox::SaveAll: diff --git a/cmake/FindCatalogueAPI.cmake b/cmake/FindCatalogueAPI.cmake deleted file mode 100644 index e1ec0e8..0000000 --- a/cmake/FindCatalogueAPI.cmake +++ /dev/null @@ -1,29 +0,0 @@ -# - Try to find CatalogueAPI Module -# Once done this will define -# CATALOGUEAPI_FOUND - System has CatalogueAPI -# CATALOGUEAPI_INCLUDE_DIRS - The CatalogueAPI include directories -# CATALOGUEAPI_LIBRARIES - The libraries needed to use CatalogueAPI -# CATALOGUEAPI_SHARED_LIBRARIES - The shared libraries for CatalogueAPI - -set(CATALOGUEAPI_ROOT_DIR "${CATALOGUEAPI_EXTERN_FOLDER}" - CACHE PATHS - "Path to the installation of CatalogueAPI" - ${libRootDirForceValue}) - -find_path(CATALOGUEAPI_INCLUDE_DIR CatalogueDao.h - HINTS ${CATALOGUEAPI_ROOT_DIR} ${CATALOGUEAPI_EXTERN_FOLDER} - PATH_SUFFIXES src ) - -find_library(CATALOGUEAPI_LIBRARY NAMES CatalogueAPI - HINTS ${CATALOGUEAPI_ROOT_DIR} ${CATALOGUEAPI_EXTERN_FOLDER} - PATH_SUFFIXES lib) - -set(CATALOGUEAPI_LIBRARIES ${CATALOGUEAPI_LIBRARY} ) -set(CATALOGUEAPI_INCLUDE_DIRS ${CATALOGUEAPI_INCLUDE_DIR} ) - -include(FindPackageHandleStandardArgs) -# handle the QUIETLY and REQUIRED arguments and set CATALOGUEAPI_FOUND to TRUE -# if all listed variables are TRUE -find_package_handle_standard_args(CatalogueAPI FOUND_VAR CATALOGUEAPI_FOUND - REQUIRED_VARS CATALOGUEAPI_LIBRARY CATALOGUEAPI_INCLUDE_DIR) -mark_as_advanced(CATALOGUEAPI_INCLUDE_DIR CATALOGUEAPI_LIBRARY ) diff --git a/cmake/compiler/compiler.cmake b/cmake/compiler/compiler.cmake deleted file mode 100644 index 63c661c..0000000 --- a/cmake/compiler/compiler.cmake +++ /dev/null @@ -1,14 +0,0 @@ -# -# compiler.cmake : configure the compilation flags -# - -message("Compiler id: ${CMAKE_CXX_COMPILER_ID}") -IF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - MESSAGE("Compiler supported") -ELSEIF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - MESSAGE("Compiler supported") -ELSEIF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") - INCLUDE("cmake/compiler/compiler_msvc.cmake") -ELSE() - MESSAGE(FATAL_ERROR "Compiler not supported") -ENDIF() diff --git a/cmake/compiler/compiler_msvc.cmake b/cmake/compiler/compiler_msvc.cmake deleted file mode 100644 index 32d15fa..0000000 --- a/cmake/compiler/compiler_msvc.cmake +++ /dev/null @@ -1,7 +0,0 @@ -# -# compiler_msvc.cmake : specific configuration for MSVC compilers -# - -ADD_DEFINITIONS( /D _USE_MATH_DEFINES) -ADD_DEFINITIONS( /D _VARIADIC_MAX=10 ) -ADD_DEFINITIONS( /D _CRT_SECURE_NO_WARNINGS) diff --git a/cmake/find_libs.cmake b/cmake/find_libs.cmake deleted file mode 100644 index f797685..0000000 --- a/cmake/find_libs.cmake +++ /dev/null @@ -1,32 +0,0 @@ -# -# findslibs.cmake -# - -# -# Qt -# -# Find Qt here so that a message is displayed in the console when executing -# cmake, but each application must call SCIQLOP_FIND_QT() to load the Qt modules that -# it needs. -FIND_PACKAGE(Qt5Core REQUIRED) -FIND_PACKAGE(Qt5Test REQUIRED) -FIND_PACKAGE(Qt5Gui REQUIRED) - -# -# doxygen tools -# -FIND_PACKAGE(Doxygen) - -# -# Analyzer tools -# -LIST( APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/analyzer/cmake") -FIND_PACKAGE(cppcheck) -FIND_PACKAGE(ClangAnalyzer) - -# -# Formatting tools -# -LIST( APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/formatting/cmake") -FIND_PACKAGE(vera++) -FIND_PACKAGE(ClangFormat) diff --git a/cmake/sciqlop.cmake b/cmake/sciqlop.cmake deleted file mode 100644 index bf62108..0000000 --- a/cmake/sciqlop.cmake +++ /dev/null @@ -1,39 +0,0 @@ -# -# sciqlop.cmake -# - -# -# Update the CMAKE_MODULE_PATH to use custom FindXXX files -# -LIST( APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/CMakeModules/") - -# Include the sciqlop version file -INCLUDE("cmake/sciqlop_version.cmake") - -# Include the sciqlop cmake macros -INCLUDE("cmake/sciqlop_macros.cmake") - -# -# Define the project parameters -# -INCLUDE("cmake/sciqlop_params.cmake") - -# -# Configure the compiler -# -#INCLUDE("cmake/compiler/compiler.cmake") - -# -# Find all necessary dependencies -# -INCLUDE("cmake/find_libs.cmake") - -# -# Compile all applications -# -INCLUDE("cmake/sciqlop_applications.cmake") - -# -# Package creation using CPack -# -INCLUDE("cmake/sciqlop_package.cmake") diff --git a/cmake/sciqlop_applications.cmake b/cmake/sciqlop_applications.cmake deleted file mode 100644 index 3509844..0000000 --- a/cmake/sciqlop_applications.cmake +++ /dev/null @@ -1,74 +0,0 @@ - -if(BUILD_TESTS) - INCLUDE ("cmake/sciqlop_code_coverage.cmake") - APPEND_COVERAGE_COMPILER_FLAGS() -endif(BUILD_TESTS) - -# -# Compile the diffents modules -# -set(sciqlop-plugin_DIR "${CMAKE_SOURCE_DIR}/plugin/cmake") -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${sciqlop-plugin_DIR}") -ADD_SUBDIRECTORY("${CMAKE_SOURCE_DIR}/plugin") - -set(sciqlop-core_DIR "${CMAKE_SOURCE_DIR}/core/cmake") -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${sciqlop-core_DIR}") -ADD_SUBDIRECTORY("${CMAKE_SOURCE_DIR}/core") - -set(sciqlop-gui_DIR "${CMAKE_SOURCE_DIR}/gui/cmake") -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${sciqlop-gui_DIR}") -ADD_SUBDIRECTORY("${CMAKE_SOURCE_DIR}/gui") - -ADD_SUBDIRECTORY("${CMAKE_SOURCE_DIR}/app") - -OPTION (BUILD_PLUGINS "Build the plugins" OFF) -IF(BUILD_PLUGINS) - set(sciqlop-mockplugin_DIR "${CMAKE_SOURCE_DIR}/plugins/mockplugin/cmake") - set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${sciqlop-mockplugin_DIR}") - ADD_SUBDIRECTORY("${CMAKE_SOURCE_DIR}/plugins/mockplugin") - - # Sets AMDA server that will be used during execution. - # Available values are: - # - "default": default AMDA server - # - "amdatest": AMDA test server - # - "hybrid": use both the default server and the test server (the server used is relative to each product, according to its "server" property in the JSON file) - # - "localhost": use local AMDA server - # Any other value will lead to the use of the default server - ADD_DEFINITIONS(-DSCIQLOP_AMDA_SERVER="hybrid") - - set(sciqlop-amda_DIR "${CMAKE_SOURCE_DIR}/plugins/amda/cmake") - set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${sciqlop-amda_DIR}") - ADD_SUBDIRECTORY("${CMAKE_SOURCE_DIR}/plugins/amda") - - # Temporary target to copy to plugins dir - find_package(sciqlop-mockplugin) - find_package(sciqlop-amda) - ADD_CUSTOM_TARGET(plugins - COMMAND ${CMAKE_COMMAND} -E copy ${SCIQLOP-MOCKPLUGIN_LIBRARIES} "${LIBRARY_OUTPUT_PATH}/plugins/${SCIQLOP-MOCKPLUGIN_LIBRARIES_NAME}" - COMMAND ${CMAKE_COMMAND} -E copy ${SCIQLOP-AMDA_LIBRARIES} "${LIBRARY_OUTPUT_PATH}/plugins/${SCIQLOP-AMDA_LIBRARIES_NAME}" - ) -ENDIF(BUILD_PLUGINS) - -# LOGGER -set(QTLOGGING_INI_FILE "${CMAKE_SOURCE_DIR}/config/QtProject/qtlogging.ini") -FILE(COPY ${QTLOGGING_INI_FILE} DESTINATION ${CONFIG_OUTPUT_PATH}) - - -# -# Code formatting -# -# Vera++ exclusion files -LIST(APPEND CHECKSTYLE_EXCLUSION_FILES ${CMAKE_CURRENT_SOURCE_DIR}/formatting/vera-exclusions/exclusions.txt) -#SCIQLOP_SET_TO_PARENT_SCOPE(CHECKSTYLE_EXCLUSION_FILES) -INCLUDE ("cmake/sciqlop_formatting.cmake") - -# -# Documentation generation -# -INCLUDE ("cmake/sciqlop_doxygen.cmake") - -# -# Source code analysis -# -INCLUDE ("cmake/sciqlop_code_analysis.cmake") -INCLUDE ("cmake/sciqlop_code_cppcheck.cmake") diff --git a/cmake/sciqlop_code_analysis.cmake b/cmake/sciqlop_code_analysis.cmake deleted file mode 100644 index 45418dc..0000000 --- a/cmake/sciqlop_code_analysis.cmake +++ /dev/null @@ -1,44 +0,0 @@ -# -# sciqlop_code_analysis.cmake - -# Launch code source analysis with CLANGANALYZER. Can be activated with the -# ENABLE_ANALYSIS option. -# -# The following CACHE variables are available: -# * CLANGANALYZER_EXTRA_ARGS: extra arguments for CLANGANALYZER; -# * CLANGANALYZER_OUTPUT: path to the xml report of CLANGANALYZER. -# -# The following variables are used (must be set by the cmake file calling this -# one): -# * ANALYSIS_INPUT_DIRS: directories to analyze; -# * ANALYSIS_EXCLUDE_DIRS: directories to exclude from the analysis. -# - -# -# Analyze the source code with CLANGANALYZER -# -OPTION (ENABLE_ANALYSIS "Analyze the source code with clang_analyze" ON) -IF (ENABLE_ANALYSIS) - - # Make sure CLANGANALYZER has been found, otherwise the source code can't be - # analyzed - IF (CLANGANALYZER_FOUND) - - SET (CLANGANALYZER_OUTPUT "${CMAKE_BINARY_DIR}/clang-analyzer-ouput" - CACHE STRING "Output file for the CLANGANALYZER report") - MARK_AS_ADVANCED (CLANGANALYZER_OUTPUT) - - SET (CLANGANALYZER_EXTRA_ARGS -o ${CLANGANALYZER_OUTPUT} - CACHE STRING "Extra arguments for CLANGANALYZER") - MARK_AS_ADVANCED (CLANGANALYZER_EXTRA_ARGS) - - # Add the analyze target to launch CLANGANALYZER - ADD_CUSTOM_TARGET (analyze - COMMAND - sh ${CMAKE_CURRENT_SOURCE_DIR}/analyzer/launch-clang-analyzer-linux.sh - ) - - ELSE (CLANGANALYZER_FOUND) - MESSAGE (STATUS "The source code won't be analyzed - CLANGANALYZER not found") - ENDIF (CLANGANALYZER_FOUND) -ENDIF (ENABLE_ANALYSIS) diff --git a/cmake/sciqlop_code_coverage.cmake b/cmake/sciqlop_code_coverage.cmake deleted file mode 100644 index 5184084..0000000 --- a/cmake/sciqlop_code_coverage.cmake +++ /dev/null @@ -1,191 +0,0 @@ -# Copyright (c) 2012 - 2017, Lars Bilke -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, this -# list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# 3. Neither the name of the copyright holder nor the names of its contributors -# may be used to endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -# CHANGES: -# -# 2012-01-31, Lars Bilke -# - Enable Code Coverage -# -# 2013-09-17, Joakim Söderberg -# - Added support for Clang. -# - Some additional usage instructions. -# -# 2016-02-03, Lars Bilke -# - Refactored functions to use named parameters -# -# 2017-06-02, Lars Bilke -# - Merged with modified version from github.com/ufz/ogs -# -# -# USAGE: -# -# 1. Copy this file into your cmake modules path. -# -# 2. Add the following line to your CMakeLists.txt: -# include(CodeCoverage) -# -# 3. Append necessary compiler flags: -# APPEND_COVERAGE_COMPILER_FLAGS() -# -# 4. If you need to exclude additional directories from the report, specify them -# using the COVERAGE_EXCLUDES variable before calling SETUP_TARGET_FOR_COVERAGE. -# Example: -# set(COVERAGE_EXCLUDES 'dir1/*' 'dir2/*') -# -# 5. Use the function SETUP_TARGET_FOR_COVERAGE to create a custom make target -# which runs your test executable and produces a lcov code coverage report: -# Example: -# SETUP_TARGET_FOR_COVERAGE( -# my_coverage_target # Name for custom target. -# test_driver # Name of the test driver executable that runs the tests. -# # NOTE! This should always have a ZERO as exit code -# # otherwise the coverage generation will not complete. -# coverage # Name of output directory. -# ) -# -# 6. Build a Debug build: -# cmake -DCMAKE_BUILD_TYPE=Debug .. -# make -# make my_coverage_target -# - -include(CMakeParseArguments) - -# Check prereqs -find_program( GCOV_PATH gcov ) -find_program( LCOV_PATH lcov ) -find_program( GENHTML_PATH genhtml ) -find_program( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test) -find_program( SIMPLE_PYTHON_EXECUTABLE python ) - -if(NOT GCOV_PATH) - message(FATAL_ERROR "gcov not found! Aborting...") -endif() # NOT GCOV_PATH - -if("${CMAKE_CXX_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang") - if("${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS 3) - message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...") - endif() -elseif(NOT CMAKE_COMPILER_IS_GNUCXX) - message(FATAL_ERROR "Compiler is not GNU gcc! Aborting...") -endif() - -set(COVERAGE_COMPILER_FLAGS "-g -O0 --coverage -fprofile-arcs -ftest-coverage" - CACHE INTERNAL "") - -set(CMAKE_CXX_FLAGS_COVERAGE - ${COVERAGE_COMPILER_FLAGS} - CACHE STRING "Flags used by the C++ compiler during coverage builds." - FORCE ) -set(CMAKE_C_FLAGS_COVERAGE - ${COVERAGE_COMPILER_FLAGS} - CACHE STRING "Flags used by the C compiler during coverage builds." - FORCE ) -set(CMAKE_EXE_LINKER_FLAGS_COVERAGE - "" - CACHE STRING "Flags used for linking binaries during coverage builds." - FORCE ) -set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE - "" - CACHE STRING "Flags used by the shared libraries linker during coverage builds." - FORCE ) -mark_as_advanced( - CMAKE_CXX_FLAGS_COVERAGE - CMAKE_C_FLAGS_COVERAGE - CMAKE_EXE_LINKER_FLAGS_COVERAGE - CMAKE_SHARED_LINKER_FLAGS_COVERAGE ) - -if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") - message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading") -endif() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug" - -if(CMAKE_COMPILER_IS_GNUCXX) - link_libraries(gcov) -else() - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage") -endif() - -# Defines a target for running and collection code coverage information -# Builds dependencies, runs the given executable and outputs reports. -# NOTE! The executable should always have a ZERO as exit code otherwise -# the coverage generation will not complete. -# -# SETUP_TARGET_FOR_COVERAGE( -# NAME testrunner_coverage # New target name -# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR -# DEPENDENCIES testrunner # Dependencies to build first -# ) -function(SETUP_TARGET_FOR_COVERAGE) - - set(options NONE) - set(oneValueArgs NAME TARGET OUTPUT) - set(multiValueArgs EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES) - cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - if(NOT LCOV_PATH) - message(FATAL_ERROR "lcov not found! Aborting...") - endif() # NOT LCOV_PATH - - if(NOT GENHTML_PATH) - message(FATAL_ERROR "genhtml not found! Aborting...") - endif() # NOT GENHTML_PATH - - # Setup target - add_custom_target(${Coverage_TARGET} - - # Cleanup lcov - COMMAND ${LCOV_PATH} --directory . --zerocounters - - # Run tests - COMMAND ${Coverage_EXECUTABLE} - - # Capturing lcov counters and generating report - COMMAND ${LCOV_PATH} --directory . --capture --output-file ${Coverage_NAME}.info - COMMAND ${LCOV_PATH} --remove ${Coverage_NAME}.info ${COVERAGE_EXCLUDES} --output-file ${Coverage_NAME}.info.cleaned - COMMAND ${GENHTML_PATH} -o ${Coverage_OUTPUT} ${Coverage_NAME}.info.cleaned - COMMAND ${CMAKE_COMMAND} -E remove ${Coverage_NAME}.info ${Coverage_NAME}.info.cleaned - - WORKING_DIRECTORY ${PROJECT_BINARY_DIR} - DEPENDS ${Coverage_DEPENDENCIES} - COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report." - ) - - # Show info where to find the report -# message("Nome de la target ${Coverage_TARGET}") -# add_custom_command(TARGET ${Coverage_TARGET} POST_BUILD -# COMMAND ; -# COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report." -# ) - -endfunction() - -function(APPEND_COVERAGE_COMPILER_FLAGS) - set(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) - message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}") -endfunction() # APPEND_COVERAGE_COMPILER_FLAGS diff --git a/cmake/sciqlop_code_cppcheck.cmake b/cmake/sciqlop_code_cppcheck.cmake deleted file mode 100644 index 772d0eb..0000000 --- a/cmake/sciqlop_code_cppcheck.cmake +++ /dev/null @@ -1,52 +0,0 @@ -# -# sciqlop_code_analysis.cmake - -# Launch code source analysis with cppcheck. Can be activated with the -# ENABLE_CPPCHECK option. -# -# The following CACHE variables are available: -# * CPPCHECK_EXTRA_ARGS: extra arguments for cppcheck; -# * CPPCHECK_OUTPUT: path to the xml report of cppcheck. -# -# The following variables are used (must be set by the cmake file calling this -# one): -# * ANALYSIS_INPUT_DIRS: directories to analyze; -# * ANALYSIS_EXCLUDE_DIRS: directories to exclude from the analysis. -# - -# -# Analyze the source code with cppcheck -# -OPTION (ENABLE_CPPCHECK "Analyze the source code with cppcheck" ON) -IF (ENABLE_CPPCHECK) - - # Make sure cppcheck has been found, otherwise the source code can't be - # analyzed - IF (CPPCHECK_FOUND) - SET (CPPCHECK_EXTRA_ARGS --inline-suppr --xml --xml-version=2 --enable="warning,style" --force -v - CACHE STRING "Extra arguments for cppcheck") - MARK_AS_ADVANCED (CPPCHECK_EXTRA_ARGS) - - SET (CPPCHECK_OUTPUT "${CMAKE_BINARY_DIR}/cppcheck-report.xml" - CACHE STRING "Output file for the cppcheck report") - MARK_AS_ADVANCED (CPPCHECK_OUTPUT) - - SET (CPPCHECK_EXCLUDE_DIRS) - FOREACH (dir ${ANALYSIS_EXCLUDE_DIRS}) - LIST (APPEND CPPCHECK_EXCLUDE_DIRS "-i${dir}") - ENDFOREACH () - - # Add the analyze target to launch cppcheck - ADD_CUSTOM_TARGET (cppcheck - COMMAND - ${CPPCHECK_EXECUTABLE} - ${CPPCHECK_EXTRA_ARGS} - ${ANALYSIS_INPUT_DIRS} - ${CPPCHECK_EXCLUDE_DIRS} - 2> ${CPPCHECK_OUTPUT} - ) - - ELSE (CPPCHECK_FOUND) - MESSAGE (STATUS "The source code won't be analyzed - Cppcheck not found") - ENDIF (CPPCHECK_FOUND) -ENDIF (ENABLE_CPPCHECK) diff --git a/cmake/sciqlop_doxygen.cmake b/cmake/sciqlop_doxygen.cmake deleted file mode 100644 index e1fe072..0000000 --- a/cmake/sciqlop_doxygen.cmake +++ /dev/null @@ -1,110 +0,0 @@ -# -# sciqlop_doxygen.cmake -# -# Launch doxygen generation. Can be activated with the BUILD_DOCUMENTATION -# option. -# -# The following CACHE variables are available: -# * DOXYGEN_LANGUAGE: Documentation language; -# -# The following variables are used (must be set by the cmake file calling this -# one): -# * DOXYGEN_INPUT_DIRS: directories to document; -# * DOXYGEN_EXCLUDE_PATTERNS: directories to exclude from the documentation -# generation. -# - -# -# Compile the doxygen documentation -# -OPTION (BUILD_DOCUMENTATION "Build the doxygen-based documentation" ON) -IF (BUILD_DOCUMENTATION) - - # Make sure Doxygen is on the system, if not then the documentation can't be built - IF (DOXYGEN_FOUND) - - # Append the global docs directory to the list of input directories - LIST (APPEND DOXYGEN_INPUT_DIRS "${CMAKE_SOURCE_DIR}/docs") - LIST (APPEND DOXYGEN_INPUT_DIRS "${CMAKE_BINARY_DIR}/gendocs") - - # Exclude the "*_private.h" files by default - list(APPEND DOXYGEN_EXCLUDE_PATTERNS "*_private.h") - # Exclude cpp files - list(APPEND DOXYGEN_EXCLUDE_PATTERNS "*.cpp") - - # Set the variables used by the Doxyfile template - SET (PROJECT_NAME "${CMAKE_PROJECT_NAME}") - SET (INPUT_DIRECTORIES) - FOREACH (dir ${DOXYGEN_INPUT_DIRS}) - SET (INPUT_DIRECTORIES "${INPUT_DIRECTORIES}\\ \n \"${dir}\" ") - ENDFOREACH () - SET (EXCLUDE_PATTERNS) - FOREACH (pattern ${DOXYGEN_EXCLUDE_PATTERNS}) - SET (EXCLUDE_PATTERNS "${EXCLUDE_PATTERNS}\\ \n \"${pattern}\" ") - ENDFOREACH () - - SET (INDEX_LIST_MODULES "
    \n") - FOREACH(module ${ENABLED_MODULE_LIST}) - SET (INDEX_LIST_MODULES "${INDEX_LIST_MODULES}
  • [${module}](@ref ${module})
  • \n") - ENDFOREACH() - SET (INDEX_LIST_MODULES "${INDEX_LIST_MODULES}
\n") - - # This is the doxyfile that will be used to generate the documentation - # You can use programs like doxywizard to edit the settings - SET (doxygenConfigFileIn "${CMAKE_SOURCE_DIR}/docs/Doxyfile.dox.in") - SET (doxygenConfigFile "${CMAKE_BINARY_DIR}/Doxyfile.dox") - - SET (DOXYGEN_LANGUAGE "English" CACHE STRING "Documentation language") - MARK_AS_ADVANCED (DOXYGEN_LANGUAGE) - - SET (doxygenIndexFileIn "${CMAKE_SOURCE_DIR}/docs/index.md.in") - SET (doxygenIndexFile "${CMAKE_BINARY_DIR}/gendocs/index.md") - - # Using a .in file means we can use CMake to insert project settings - # into the doxyfile. For example, CMake will replace @PROJECT_NAME@ in - # a configured file with the CMake PROJECT_NAME variable's value. - - - CONFIGURE_FILE (${doxygenConfigFileIn} ${doxygenConfigFile} @ONLY) - CONFIGURE_FILE (${doxygenIndexFileIn} ${doxygenIndexFile} @ONLY) - - # Add the documentation target. This lets you run "make docs" from the - # generated CMake makefiles - ADD_CUSTOM_TARGET (docs - ${DOXYGEN_EXECUTABLE} ${doxygenConfigFile} - DEPENDS ${doxygenConfigFile} - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - VERBATIM) - - # You can add an "install" directive to install the resulting documentation - # if desired. - INSTALL(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/documentation/html DESTINATION ${INSTALL_DOCUMENTATION_DIR} OPTIONAL COMPONENT binaries) - - # Add a custom command to archive the current HTML documentation generated - # by doxygen - set(ARCHIVED_HTML_OUTPUT_FILE_NAME "${PROJECT_NAME}-${SCIQLOP_VERSION}-documentation-html.tar.bz2") - add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/documentation/${ARCHIVED_HTML_OUTPUT_FILE_NAME} - COMMAND sh -c "tar --bzip2 -cf ${ARCHIVED_HTML_OUTPUT_FILE_NAME} html" - DEPENDS docs - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/documentation) - # Add a custom target to execute the above command - add_custom_target(htmldocs DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/documentation/${ARCHIVED_HTML_OUTPUT_FILE_NAME}) - - # Add a custom command to execute pdflatex on the latex documentation - # generated by doxygen - set(LATEX_OUTPUT_FILE_NAME "${PROJECT_NAME}-${SCIQLOP_VERSION}-documentation.pdf") - add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/documentation/${LATEX_OUTPUT_FILE_NAME} - COMMAND make - COMMAND cp refman.pdf ../${LATEX_OUTPUT_FILE_NAME} - DEPENDS docs - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/documentation/latex) - # Add a custom target to execute the above command - add_custom_target(latexdocs DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/documentation/${LATEX_OUTPUT_FILE_NAME}) - - # Add a custom target to execute all the docs commands - add_custom_target(alldocs DEPENDS htmldocs latexdocs) - - ELSE (DOXYGEN_FOUND) - MESSAGE (STATUS "Documentation will not be built - Doxygen not found") - ENDIF (DOXYGEN_FOUND) -ENDIF (BUILD_DOCUMENTATION) diff --git a/cmake/sciqlop_formatting.cmake b/cmake/sciqlop_formatting.cmake deleted file mode 100644 index 98fa28e..0000000 --- a/cmake/sciqlop_formatting.cmake +++ /dev/null @@ -1,53 +0,0 @@ -# -# sciqlop_formatting.cmake -# -# Launch code formatting tools. Can be activated with ENABLE_FORMATTING and -# ENABLE_CHECKSTYLE options. -# -# The following variables are used (must be set by the cmake file calling this -# one): -# * FORMATTING_INPUT_FILES: list of files to format; -# * CHECKSTYLE_INPUT_FILES: list of files to check for style; -# * CHECKSTYLE_EXCLUSION_FILES: list of vera++ exclusion files. -# - -OPTION (ENABLE_FORMATTING "Format the source code while compiling" ON) -OPTION (ENABLE_CHECKSTYLE "Analyse the style of the code while compiling" ON) - -IF (ENABLE_FORMATTING) - IF (CLANGFORMAT_FOUND) - INCLUDE(${CLANGFORMAT_USE_FILE}) - - ADD_CLANGFORMAT_TARGETS(${FORMATTING_INPUT_FILES} - ADD_TO_ALL) - ELSE() - MESSAGE (STATUS "Source code will not be formatted - clang-format not found") - ENDIF() -ENDIF() - -IF (ENABLE_CHECKSTYLE) - IF (VERA++_FOUND) - INCLUDE(${VERA++_USE_FILE}) - - SET(EXCLUSIONS) - FOREACH (e ${CHECKSTYLE_EXCLUSION_FILES}) - LIST(APPEND EXCLUSIONS EXCLUSION ${e}) - ENDFOREACH() - - ADD_VERA_TARGETS(${CHECKSTYLE_INPUT_FILES} - ADD_TO_ALL - PROFILE "sciqlop" - ROOT "${CMAKE_SOURCE_DIR}/formatting/vera-root" - PARAMETER "project-name=${PROJECT_NAME}" - ${EXCLUSIONS}) - - ADD_VERA_CHECKSTYLE_TARGET(${CHECKSTYLE_INPUT_FILES} - PROFILE "sciqlop" - ROOT "${CMAKE_SOURCE_DIR}/formatting/vera-root" - PARAMETER "project-name=${PROJECT_NAME}" - ${EXCLUSIONS}) - - ELSE() - MESSAGE (STATUS "Source code will not be checkstyled - vera++ not found") - ENDIF() -ENDIF() diff --git a/cmake/sciqlop_macros.cmake b/cmake/sciqlop_macros.cmake deleted file mode 100644 index 2cf91d5..0000000 --- a/cmake/sciqlop_macros.cmake +++ /dev/null @@ -1,191 +0,0 @@ -# -# sciqlop_macros.cmake -# -# The following functions or macros are defined in this document: -# - SUBDIRLIST -# - SCIQLOP_SET_TO_PARENT_SCOPE -# - SCIQLOP_PROCESS_EXTERN_DEPENDENCIES -# - SCIQLOP_COPY_TO_TARGET -# - SCIQLOP_READ_FILE -# - SCIQLOP_FIND_QT -# - SCIQLOP_ADD_EXTERN_DEPENDENCY - -# -# Define a macro to retrieve all subdirectory names of a specific directory -# -MACRO(SUBDIRLIST result curdir) - FILE(GLOB children RELATIVE ${curdir} ${curdir}/*) - SET(dirlist "") - FOREACH(child ${children}) - IF(IS_DIRECTORY ${curdir}/${child}) - LIST(APPEND dirlist ${child}) - ENDIF() - ENDFOREACH() - SET(${result} ${dirlist}) -ENDMACRO() - -# SCIQLOP_SET_TO_PARENT_SCOPE(variable) -# -# Set the given variable to the parent scope. -# -MACRO (SCIQLOP_SET_TO_PARENT_SCOPE variable) - SET(${variable} ${${variable}} PARENT_SCOPE) -ENDMACRO (SCIQLOP_SET_TO_PARENT_SCOPE) - -MACRO(SCIQLOP_FIND_QT) - # Find includes in corresponding build directories - set(CMAKE_INCLUDE_CURRENT_DIR ON) - # Instruct CMake to run moc automatically when needed. - set(CMAKE_AUTOMOC ON) - - # Find Qt5 and the modules asked - FOREACH(component ${ARGN}) - FIND_PACKAGE(Qt5${component} QUIET REQUIRED) - INCLUDE_DIRECTORIES(${Qt5${component}_INCLUDE_DIRS}) - ENDFOREACH(component) -ENDMACRO(SCIQLOP_FIND_QT) - -# SCIQLOP_PROCESS_EXTERN_DEPENDENCIES(externFile externLibraries externSharedLibraries) -# -# Process the dependencies file for the modules. Each line of this file must -# contain the parameters to pass to the FIND_PACKAGE function. This function -# will append the libraries of the extern dependency found to the variable -# passed as a second parameter, and add the include directories of the extern -# dependency to the global include directories. Moreover, the extern shared -# libraries are stored in the variable passed as a third parameter. These shared -# libraries can then be copied to a target output path by using the -# SCIQLOP_COPY_TO_TARGET function. -# -# Examples: -# -# SCIQLOP_PROCESS_MODULE_DEPENDENCIES("path/to/extern.dependencies" -# EXTERN_LIBRARIES -# EXTERN_SHARED_LIBRARIES) -# -FUNCTION (SCIQLOP_PROCESS_EXTERN_DEPENDENCIES externFile librariesVar sharedLibrariesVar) - - SCIQLOP_READ_FILE(${externFile} externDependencies) - SET (externLibraries) - SET (externSharedLibraries) - FOREACH (externDependency ${externDependencies}) - # Check if the line is a comment (begins with #) - STRING(REGEX MATCH "^ *#.*$" matched "${externDependency}") - IF (NOT matched) - STRING(REGEX REPLACE " +" ";" externDependency "${externDependency}") - SCIQLOP_ADD_EXTERN_DEPENDENCY(externLibraries externSharedLibraries ${externDependency}) - ENDIF() - ENDFOREACH() - - LIST (APPEND ${librariesVar} ${externLibraries}) - SCIQLOP_SET_TO_PARENT_SCOPE(${librariesVar}) - - LIST (APPEND ${sharedLibrariesVar} ${externSharedLibraries}) - SCIQLOP_SET_TO_PARENT_SCOPE(${sharedLibrariesVar}) -ENDFUNCTION (SCIQLOP_PROCESS_EXTERN_DEPENDENCIES) - -# SCIQLOP_COPY_TO_TARGET copy the given files to the given target output path. -# -# The first parameter must be RUNTIME or LIBRARY, and it indicates the type of -# the target. -# -# The second parameter is the name of the target where the files must be copied. -# The RUNTIME_OUTPUT_DIRECTORY or LIBRARY_OUTPUT_DIRECTORY target properties -# will be used to find the output path of the copy. If these properties are -# empty, then the EXECUTABLE_OUTPUT_PATH or LIBRARY_OUTPUT_PATH variables will -# be used. -# -# The rest of the parameters are the files that must be copied. -FUNCTION (SCIQLOP_COPY_TO_TARGET runtimeOrLibrary targetName) - # Check RUNTIME or LIBRARY argument - IF (${runtimeOrLibrary} STREQUAL "RUNTIME") - SET (targetProperty "RUNTIME_OUTPUT_DIRECTORY") - SET (pathProperty ${EXECUTABLE_OUTPUT_PATH}) - ELSEIF (${runtimeOrLibrary} STREQUAL "LIBRARY") - SET (targetProperty "LIBRARY_OUTPUT_DIRECTORY") - SET (pathProperty ${LIBRARY_OUTPUT_PATH}) - ELSE () - MESSAGE (FATAL "The first parameter of COPY_TO_TARGET must be either RUNTIME or LIBRARY, not \"${runtimeOrLibrary}\"") - ENDIF () - - # Select the output directory - GET_TARGET_PROPERTY(OUTPUT_DIR ${targetName} ${targetProperty}) - IF (OUTPUT_DIR STREQUAL "OUTPUT_DIR-NOTFOUND") - SET (OUTPUT_DIR ${pathProperty}) - ENDIF () - - # Retrieve the list of files to copy by listing the rest of the macro - # arguments - FOREACH (arg ${ARGN}) - LIST(APPEND fileList ${arg}) - ENDFOREACH() - - # Only copy if the list isn't empty - IF (fileList) - FILE(COPY ${fileList} DESTINATION ${OUTPUT_DIR}) - ENDIF() -ENDFUNCTION (SCIQLOP_COPY_TO_TARGET) - -# SCIQLOP_READ_FILE(file contents) -# -# Read the given file line by line and store the resulting list inside the -# contents variable. -# -# /!\ If the file contains semicolons, the macro will escape them before -# returning the list. -# -# From -FUNCTION (SCIQLOP_READ_FILE file contentsVar) - FILE (READ ${file} contents) - - # Convert file contents into a CMake list (where each element in the list - # is one line of the file) - # - STRING(REGEX REPLACE ";" "\\\\;" contents "${contents}") - STRING(REGEX REPLACE "\n" ";" contents "${contents}") - - # Return file contents as a list - SET (${contentsVar} "${contents}" PARENT_SCOPE) -ENDFUNCTION (SCIQLOP_READ_FILE) - -# SCIQLOP_ADD_EXTERN_DEPENDENCY(externLibrariesVar externSharedLibrariesVar dependencyName [EXTRA FIND_PACKAGE ARGS]) -# -# SCIQLOP_ADD_EXTERN_DEPENDENCY can be used to add a dependency residing in the -# extern subdirectory to a module. -# -# The first parameter is the name of the variable where the found libraries will -# be added. -# -# The second parameter is the name of the variable where the found shared -# libraries will be added. -# -# The third parameter is the name of the dependency, and the rest of the -# arguments are the same than the FIND_PACKAGE command. In fact they are passed -# as-is to the command. -# -# If the dependency is found, then INCLUDE_DIRECTORIES is called for the -# dependency include directories, and the libraries are added to the -# externLibrariesVar variable. Moreover, if the dependency is a shared library, -# then the dynamic libraries are added to the externSharedLibrariesVar so that -# they can be copied and installed alongside the module. The libraries in this -# variable are ordered so that the real library is before the symlinks to the -# library, so that the copy and install works as expected. -FUNCTION (SCIQLOP_ADD_EXTERN_DEPENDENCY externLibrariesVar externSharedLibrariesVar dependencyName) - STRING (TOUPPER ${dependencyName} upperDependencyName) - - FIND_PACKAGE(${dependencyName} ${ARGN}) - IF (${upperDependencyName}_FOUND) - # Add the include directories of the dependency - INCLUDE_DIRECTORIES(${${upperDependencyName}_INCLUDE_DIRS}) - - # Add the libraries to the externLibrariesVar variable and export it to - # the parent scope - LIST(APPEND ${externLibrariesVar} ${${upperDependencyName}_LIBRARIES}) - SCIQLOP_SET_TO_PARENT_SCOPE(${externLibrariesVar}) - - # Find the shared libraries - LIST(APPEND ${externSharedLibrariesVar} ${${upperDependencyName}_SHARED_LIBRARIES}) - - # Export the externSharedLibrariesVar variable to the parent scope - SCIQLOP_SET_TO_PARENT_SCOPE(${externSharedLibrariesVar}) - ENDIF () -ENDFUNCTION (SCIQLOP_ADD_EXTERN_DEPENDENCY) diff --git a/cmake/sciqlop_package.cmake b/cmake/sciqlop_package.cmake deleted file mode 100644 index 3067f34..0000000 --- a/cmake/sciqlop_package.cmake +++ /dev/null @@ -1,59 +0,0 @@ -# -# Generate the source package of SciqLop. -# - -install(DIRECTORY - ${EXECUTABLE_OUTPUT_PATH} - DESTINATION "." - USE_SOURCE_PERMISSIONS - COMPONENT CORE - PATTERN "*.a" EXCLUDE -) - -set(EXECUTABLEDOTEXTENSION) -if(WIN32) - set(EXECUTABLEDOTEXTENSION ".exe") -endif(WIN32) -set (SCIQLOP_EXE_LOCATION ${EXECUTABLE_OUTPUT_PATH}/${EXECUTABLE_NAME}${EXECUTABLEDOTEXTENSION}) - -if(WIN32) - include ("cmake/sciqlop_package_qt.cmake") -endif(WIN32) - - -SET (CPACK_PACKAGE_VENDOR "CNRS") -SET (CPACK_PACKAGE_VERSION_MAJOR "${SCIQLOP_VERSION_MAJOR}") -SET (CPACK_PACKAGE_VERSION_MINOR "${SCIQLOP_VERSION_MINOR}") -SET (CPACK_PACKAGE_VERSION_PATCH "${SCIQLOP_VERSION_PATCH}${SCIQLOP_VERSION_SUFFIX}") -SET (CPACK_PACKAGE_VERSION "${SCIQLOP_VERSION}") -SET (CPACK_RESOURCE_FILE_LICENSE ${CMAKE_SOURCE_DIR}/COPYING) -SET (CPACK_PACKAGE_CONTACT "nicolas.aunai@lpp.polytechnique.fr") -SET(CPACK_PACKAGE_DESCRIPTION_FILE ${CMAKE_CURRENT_SOURCE_DIR}/README.md) -# SET(CPACK_RESOURCE_FILE_WELCOME ${CMAKE_CURRENT_SOURCE_DIR}/WARN.txt) -SET(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_CURRENT_SOURCE_DIR}/COPYING) -# SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY ${PROJECT_NAME}-${PROJECT_VERSION}) -SET(FULLBUILD ON) - -SET(CPACK_PACKAGE_NAME ${PROJECT_NAME}) -SET(CPACK_GENERATOR "NSIS") -SET(CPACK_MONOLITHIC_INSTALL 1) -#SET(CPACK_COMPONENTS_ALL sciqlop qt) -SET(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-Setup") -SET(CPACK_PACKAGE_EXECUTABLES ${CPACK_PACKAGE_NAME} ${CPACK_PACKAGE_NAME}) - -set(CPACK_PACKAGE_INSTALL_DIRECTORY ${CPACK_PACKAGE_NAME}) - -if (WIN32) - SET(CPACK_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}) - SET(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON) - SET(CPACK_NSIS_COMPONENT_INSTALL ON) - SET(CPACK_SYSTEM_NAME "MinGW32") - SET(CPACK_PACKAGING_INSTALL_PREFIX "") - #SET(CPACK_GENERATOR "NSIS") - SET(CPACK_NSIS_DISPLAY_NAME ${PROJECT_NAME}) - SET(CPACK_NSIS_MUI_FINISHPAGE_RUN ${SCIQLOP_EXECUTABLE_NAME}) - SET(CPACK_NSIS_MUI_ICON ${SCIQLOP_EXECUTABLE_ICON_LOCATION}) - SET(CPACK_NSIS_INSTALLED_ICON_NAME "bin\\\\${SCIQLOP_EXECUTABLE_NAME}.exe") -endif (WIN32) - -INCLUDE(CPack) diff --git a/cmake/sciqlop_package_qt.cmake b/cmake/sciqlop_package_qt.cmake deleted file mode 100644 index 4f6139b..0000000 --- a/cmake/sciqlop_package_qt.cmake +++ /dev/null @@ -1,36 +0,0 @@ -STRING(TOLOWER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_TO_LOWER) -SET(WINDEPLOYQT_ARGS --${CMAKE_BUILD_TYPE_TO_LOWER} --list mapping -network -sql --no-system-d3d-compiler --compiler-runtime --dry-run) - -#FOREACH(qtModule @_qt5_modules@) -# STRING(TOLOWER ${qtModule} qtLowerModule) -# SET(WINDEPLOYQT_ARGS ${WINDEPLOYQT_ARGS} -${qtLowerModule}) -#ENDFOREACH() - -EXECUTE_PROCESS( -COMMAND windeployqt ${WINDEPLOYQT_ARGS} -printsupport ${SCIQLOP_EXE_LOCATION} -OUTPUT_VARIABLE QT_FILES -) - -IF( QT_FILES ) - -STRING(REPLACE "\n" ";" QT_FILES ${QT_FILES}) -LIST(APPEND QT_FILES_LIST ${QT_FILES}) -FOREACH(QtFile ${QT_FILES_LIST}) - STRING(REPLACE "\"" "" QtFile ${QtFile}) - STRING(REPLACE "\\" "/" QtFile ${QtFile}) - STRING(REGEX MATCH "^(.*) " QtFileSource ${QtFile}) - STRING(REGEX MATCH " (.*)$" QtFileTarget ${QtFile}) - - STRING(STRIP ${QtFileSource} QtFileSource) - STRING(STRIP ${QtFileTarget} QtFileTarget) - GET_FILENAME_COMPONENT(QtFileTargetDir ${QtFileTarget} DIRECTORY) - - IF(NOT EXISTS "${CMAKE_INSTALL_PREFIX}/${EXECUTABLE_OUTPUT_PATH}/${QtFileTarget}") - GET_FILENAME_COMPONENT(QtFileTargetDir ${QtFileTarget} DIRECTORY) - FILE(INSTALL DESTINATION "${EXECUTABLE_OUTPUT_PATH}/${QtFileTargetDir}" FILES "${QtFileSource}") - ENDIF() -ENDFOREACH() - -ENDIF() - -MESSAGE( "Exec windeployqt done" ) diff --git a/cmake/sciqlop_params.cmake b/cmake/sciqlop_params.cmake deleted file mode 100644 index 82ec488..0000000 --- a/cmake/sciqlop_params.cmake +++ /dev/null @@ -1,128 +0,0 @@ -# -# sciqlop_params : Define compilation parameters -# -# Debug or release -# -# As the "NMake Makefiles" forces by default the CMAKE_BUILD_TYPE variable to Debug, SCIQLOP_BUILD_TYPE variable is used to be sure that the debug mode is a user choice -#SET(SCIQLOP_BUILD_TYPE "Release" CACHE STRING "Choose to compile in Debug or Release mode") - -IF(CMAKE_BUILD_TYPE MATCHES "Debug") - SET (DEBUG_SUFFIX "d") -ELSE() - MESSAGE (STATUS "Build in Release") - SET (DEBUG_SUFFIX "") -ENDIF() - -# -# Need to compile tests? -# -OPTION (BUILD_TESTS "Build the tests" OFF) -ENABLE_TESTING(${BUILD_TESTS}) - -# -# Path to the folder for sciqlop's extern libraries. -# -# When looking for an external library in sciqlop, we look to the following -# folders: -# - The specific folder for the library (generally of the form _ROOT_DIR -# - The global Sciqlop extern folder -# - The system folders. -# -# If a dependency is found in the global extern folder or a specific folder for -# the library, then it is installed with the sciqlop libraries. If it is found -# in the system folders, it is not. This behavior can be overriden with the -# _COPY_SHARED_LIBRARIES flag. -# -set(SCIQLOP_EXTERN_FOLDER "${CMAKE_CURRENT_SOURCE_DIR}/extern" - CACHE PATH "Path to the folder for sciqlop's extern libraries") -option(SCIQLOP_FORCE_UPDATE_EXT_ROOT_DIR "Force the _ROOT_DIR to be updated to the global sciqlop extern folder" - OFF) - -if (SCIQLOP_FORCE_UPDATE_EXT_ROOT_DIR) - set(libRootDirForceValue FORCE) -else() - set(libRootDirForceValue) -endif() - -# -# Sciqlop_modules.cmake -# -# Set ouptut directories -# -IF (UNIX) - # 32 or 64 bits compiler - IF( CMAKE_SIZEOF_VOID_P EQUAL 8 ) - SET(defaultLib "lib64/sciqlop") - ELSE() - SET(defaultLib "lib/sciqlop") - ENDIF() - SET (EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}/bin) - SET (CONFIG_OUTPUT_PATH $ENV{HOME}/.config/QtProject) - SET (LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}/${defaultLib}) -ELSEIF(WIN32) - SET (EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}/dist) - SET (LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}/dist) - SET (CONFIG_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}/dist/app/QtProject) -ELSE() - SET (EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}/dist) - SET (CONFIG_OUTPUT_PATH $ENV{HOME}/.config/QtProject) - SET (LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}/dist) -ENDIF() - - -# -# Static or shared libraries -# -OPTION (BUILD_SHARED_LIBS "Build the shared libraries" ON) - -# Generate position independant code (-fPIC) -SET(CMAKE_POSITION_INDEPENDENT_CODE TRUE) - - - -# Configuration for make install - -set(PROJECT_PLUGIN_PREFIX "SciQlop") - -IF (UNIX) - SET(CMAKE_INSTALL_PREFIX "/usr/local/${PROJECT_PLUGIN_PREFIX}") - SET(defaultBin "bin") - SET(defaultInc "include/sciqlop") - - # 32 or 64 bits compiler - IF( CMAKE_SIZEOF_VOID_P EQUAL 8 ) - SET(defaultLib "lib64") - SET(defaultPluginsLib "lib64/${PROJECT_PLUGIN_PREFIX}") - ELSE() - SET(defaultLib "lib/") - SET(defaultPluginsLib "lib/${PROJECT_PLUGIN_PREFIX}") - ENDIF() - - SET(defaultDoc "share/docs/${PROJECT_PLUGIN_PREFIX}") -ELSE() - SET(defaultBin "bin") - SET(defaultInc "include/${PROJECT_PLUGIN_PREFIX}") - SET(defaultLib "lib") - SET(defaultPluginsLib "lib/${PROJECT_PLUGIN_PREFIX}") - SET(defaultDoc "docs/${PROJECT_PLUGIN_PREFIX}") -ENDIF() - -SET(INSTALL_BINARY_DIR "${defaultBin}" CACHE STRING - "Installation directory for binaries") -SET(INSTALL_LIBRARY_DIR "${defaultLib}" CACHE STRING - "Installation directory for libraries") -SET(INSTALL_PLUGINS_LIBRARY_DIR "${defaultPluginsLib}" CACHE STRING - "Installation directory for libraries") -SET(INSTALL_INCLUDE_DIR "${defaultInc}" CACHE STRING - "Installation directory for headers") -SET(INSTALL_DOCUMENTATION_DIR "${defaultDoc}" CACHE STRING - "Installation directory for documentations") - - -# Set the rpath when installing -SET(CMAKE_SKIP_BUILD_RPATH FALSE) -SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) -SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${INSTALL_LIBRARY_DIR}") -SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) - -message("Install RPATH: ${CMAKE_INSTALL_PREFIX}/${INSTALL_LIBRARY_DIR}") diff --git a/cmake/sciqlop_tests.cmake b/cmake/sciqlop_tests.cmake new file mode 100644 index 0000000..2b09088 --- /dev/null +++ b/cmake/sciqlop_tests.cmake @@ -0,0 +1,5 @@ +macro(declare_test testname testexe sources libraries) + add_executable(${testexe} ${sources}) + target_link_libraries(${testexe} ${libraries}) + add_test(NAME ${testname} COMMAND ${testexe}) +endmacro(declare_test) diff --git a/cmake/sciqlop_version.cmake b/cmake/sciqlop_version.cmake deleted file mode 100644 index 6899550..0000000 --- a/cmake/sciqlop_version.cmake +++ /dev/null @@ -1,22 +0,0 @@ -# -# sciqlop_version.cmake -# -# Holds the version of sciqlop. -# -# These variables are used to generate the -# "Version.h" and "Version.cpp" files so that the version number is available -# inside of sciqlop source code. -# -# Moreover, they're used with CPack to display the version number in the setups. -# - -# Version number parts. These variables must be updated when the version change. -SET (SCIQLOP_VERSION_MAJOR 0) -SET (SCIQLOP_VERSION_MINOR 1) -SET (SCIQLOP_VERSION_PATCH 0) -SET (SCIQLOP_VERSION_SUFFIX "") - -# Version number as a string. This variable is automatically generated from the -# above variables to build a version number of the form: MAJOR.MINOR.PATCH. If -# SCIQLOP_VERSION_SUFFIX isn't empty, it is appended to the version number. -SET (SCIQLOP_VERSION "${SCIQLOP_VERSION_MAJOR}.${SCIQLOP_VERSION_MINOR}.${SCIQLOP_VERSION_PATCH}${SCIQLOP_VERSION_SUFFIX}") diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 809f326..df571fb 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -1,173 +1,31 @@ -## core - CMakeLists.txt -STRING(TOLOWER ${CMAKE_PROJECT_NAME} LIBRARY_PREFFIX) -SET(SQPCORE_LIBRARY_NAME "${LIBRARY_PREFFIX}_core${DEBUG_SUFFIX}") -SET(SOURCES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src/") -SET(INCLUDES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include/") -SET(TESTS_RESOURCES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tests-resources") +FILE (GLOB_RECURSE core_SRCS + include/*.h + src/*.cpp + ) -# Include core directory -include_directories("${INCLUDES_DIR}") +add_definitions(-DCORE_STATIC) -# Set a variable to display a warning in the version files. -SET(SCIQLOP_CMAKE_GENERATION_WARNING "DON'T CHANGE THIS FILE. AUTOGENERATED BY CMAKE.") -# Generate the version file from the cmake version variables. The version -# variables are defined in the cmake/sciqlop_version.cmake file. -CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/resources/Version.h.in" - "${INCLUDES_DIR}/Version.h") -CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/resources/Version.cpp.in" - "${SOURCES_DIR}/Version.cpp") +add_library(sciqlopcore ${core_SRCS}) +SET_TARGET_PROPERTIES(sciqlopcore PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE) -# Find dependent modules -find_package(sciqlop-plugin) -INCLUDE_DIRECTORIES(${SCIQLOP-PLUGIN_INCLUDE_DIR}) - -# -# Find Qt modules -# -SCIQLOP_FIND_QT(Core Network) - -# -# Compile the library library -# - -ADD_DEFINITIONS(-DCORE_LIB) - -FILE (GLOB_RECURSE MODULE_SOURCES - ${INCLUDES_DIR}/*.h - ${SOURCES_DIR}/*.c - ${SOURCES_DIR}/*.cpp - ${SOURCES_DIR}/*.h) - -ADD_LIBRARY(${SQPCORE_LIBRARY_NAME} ${MODULE_SOURCES}) -set_property(TARGET ${SQPCORE_LIBRARY_NAME} PROPERTY CXX_STANDARD 14) -set_property(TARGET ${SQPCORE_LIBRARY_NAME} PROPERTY CXX_STANDARD_REQUIRED ON) -TARGET_LINK_LIBRARIES(${SQPCORE_LIBRARY_NAME}) -qt5_use_modules(${SQPCORE_LIBRARY_NAME} Core Network) - -# Find CATALOGUE_API -include_directories("${CATALOGUEAPI_INCLUDE}") -TARGET_LINK_LIBRARIES(${SQPCORE_LIBRARY_NAME} ${CATALOGUEAPI_LIBRARIES}) -INSTALL(TARGETS ${SQPCORE_LIBRARY_NAME} - RUNTIME DESTINATION ${INSTALL_BINARY_DIR} - LIBRARY DESTINATION ${INSTALL_LIBRARY_DIR} - ARCHIVE DESTINATION ${INSTALL_LIBRARY_DIR} +target_include_directories(sciqlopcore PUBLIC + $ + $ ) -add_dependencies(${SQPCORE_LIBRARY_NAME} CatalogueAPI) - -# From cmake documentation: http://www.cmake.org/cmake/help/v3.0/manual/cmake-buildsystem.7.html -# Entries in the COMPILE_DEFINITIONS are prefixed with -D or /D and added to the compile line in an unspecified order. -# The DEFINE_SYMBOL target property is also added as a compile definition as a special convenience case for SHARED and MODULE library targets -IF(BUILD_SHARED_LIBS) - SET_TARGET_PROPERTIES(${SQPCORE_LIBRARY_NAME} PROPERTIES COMPILE_DEFINITIONS "SCIQLOP_EXPORT") -ELSE() - TARGET_COMPILE_DEFINITIONS(${SQPCORE_LIBRARY_NAME} PUBLIC "SCIQLOP_STATIC_LIBRARIES") -ENDIF() - -# Set the variable to parent scope so that the other projects can copy the -# dependent shared libraries -SCIQLOP_SET_TO_PARENT_SCOPE(SQPCORE_LIBRARY_NAME) - -## Copy extern shared libraries to the lib folder -LIST (APPEND ${EXTERN_SHARED_LIBRARIES} ${CATALOGUEAPI_LIBRARIES}) - - -SET (COPY_LIBS_DESTINATION LIBRARY) -if(APPLE) - SET (COPY_LIBS_DESTINATION RUNTIME) -endif() - -add_custom_command(TARGET ${SQPCORE_LIBRARY_NAME} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy ${CATALOGUEAPI_LIBRARIES} ${EXECUTABLE_OUTPUT_PATH} +target_link_libraries(sciqlopcore PUBLIC + Qt5::Core + Qt5::Network + catalogs ) -# Add the files to the list of files to be analyzed -LIST(APPEND CHECKSTYLE_INPUT_FILES ${MODULE_SOURCES}) -SCIQLOP_SET_TO_PARENT_SCOPE(CHECKSTYLE_INPUT_FILES) -# Vera++ exclusion files -LIST(APPEND CHECKSTYLE_EXCLUSION_FILES ${CMAKE_CURRENT_SOURCE_DIR}/vera-exclusions/exclusions.txt) -SCIQLOP_SET_TO_PARENT_SCOPE(CHECKSTYLE_EXCLUSION_FILES) - -# -# Compile the tests -# -IF(BUILD_TESTS) - INCLUDE_DIRECTORIES(${SOURCES_DIR}) - FILE (GLOB_RECURSE TESTS_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/Test*.cpp) - FILE (GLOB_RECURSE TESTS_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/Test*.h) - SET( TEST_LIBRARIES ${SQPCORE_LIBRARY_NAME}) - list(APPEND TEST_LIBRARIES ${CATALOGUEAPI_LIBRARIES}) - - SET(TARGETS_COV) - FOREACH( testFile ${TESTS_SOURCES} ) - GET_FILENAME_COMPONENT( testDirectory ${testFile} DIRECTORY ) - GET_FILENAME_COMPONENT( testName ${testFile} NAME_WE ) - - # Add to the list of sources files all the sources in the same - # directory that aren't another test - FILE (GLOB currentTestSources - ${testDirectory}/*.c - ${testDirectory}/*.cpp - ${testDirectory}/*.h) - LIST (REMOVE_ITEM currentTestSources ${TESTS_SOURCES}) - # LIST (REMOVE_ITEM currentTestSources ${TESTS_HEADERS}) - - ADD_EXECUTABLE(${testName} ${testFile} ${currentTestSources}) - set_property(TARGET ${testName} PROPERTY CXX_STANDARD 14) - set_property(TARGET ${testName} PROPERTY CXX_STANDARD_REQUIRED ON) - TARGET_LINK_LIBRARIES( ${testName} ${TEST_LIBRARIES}) - qt5_use_modules(${testName} Test) - - ADD_TEST( NAME ${testName} COMMAND ${testName} ) - - set(Coverage_NAME ${testName}) - if(UNIX) - SETUP_TARGET_FOR_COVERAGE(TARGET ${testName}_coverage OUTPUT ${testFile}-path NAME ${testFile} EXECUTABLE ${testName}) - LIST( APPEND TARGETS_COV ${testName}_coverage) - endif(UNIX) - - ENDFOREACH( testFile ) - - add_custom_target(coverage) - - FOREACH( target_cov ${TARGETS_COV} ) - add_custom_command(TARGET coverage PRE_BUILD COMMAND make ${target_cov}) - ENDFOREACH( target_cov ) - - LIST(APPEND testFilesToFormat ${TESTS_SOURCES}) - LIST(APPEND testFilesToFormat ${TESTS_HEADERS}) - LIST(APPEND FORMATTING_INPUT_FILES ${testFilesToFormat}) - SCIQLOP_SET_TO_PARENT_SCOPE(FORMATTING_INPUT_FILES) - - ADD_DEFINITIONS(-DCORE_TESTS_RESOURCES_DIR="${TESTS_RESOURCES_DIR}") -ENDIF(BUILD_TESTS) - -# -# Set the files that must be formatted by clang-format. -# -LIST (APPEND FORMATTING_INPUT_FILES ${MODULE_SOURCES}) -SCIQLOP_SET_TO_PARENT_SCOPE(FORMATTING_INPUT_FILES) +install(TARGETS sciqlopcore EXPORT SciQLOPCoreConfig + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) -# -# Set the directories that doxygen must browse to generate the -# documentation. -# -# Source directories: -LIST (APPEND DOXYGEN_INPUT_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/docs") -LIST (APPEND DOXYGEN_INPUT_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/src") -SCIQLOP_SET_TO_PARENT_SCOPE(DOXYGEN_INPUT_DIRS) -# Source directories to exclude from the documentation generation -#LIST (APPEND DOXYGEN_EXCLUDE_PATTERNS "${CMAKE_CURRENT_SOURCE_DIR}/path/to/subdir/*") -SCIQLOP_SET_TO_PARENT_SCOPE(DOXYGEN_EXCLUDE_PATTERNS) +install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/SciQLOP) +install(EXPORT SciQLOPCoreConfig DESTINATION share/SciQLOPCore/cmake) +export(TARGETS sciqlopcore FILE SciQLOPCoreConfig.cmake) -# -# Set the directories with the sources to analyze and propagate the -# modification to the parent scope -# -# Source directories to analyze: -LIST (APPEND ANALYSIS_INPUT_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/src") -LIST (APPEND ANALYSIS_INPUT_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/tests") -SCIQLOP_SET_TO_PARENT_SCOPE(ANALYSIS_INPUT_DIRS) -# Source directories to exclude from the analysis -#LIST (APPEND ANALYSIS_EXCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/path/to/subdir") -SCIQLOP_SET_TO_PARENT_SCOPE(ANALYSIS_EXCLUDE_DIRS) +add_subdirectory(tests) diff --git a/core/cmake/Findsciqlop-core.cmake b/core/cmake/Findsciqlop-core.cmake deleted file mode 100755 index d266456..0000000 --- a/core/cmake/Findsciqlop-core.cmake +++ /dev/null @@ -1,25 +0,0 @@ -# - Try to find sciqlop-core -# Once done this will define -# SCIQLOP-CORE_FOUND - System has sciqlop-core -# SCIQLOP-CORE_INCLUDE_DIR - The sciqlop-core include directories -# SCIQLOP-CORE_LIBRARIES - The libraries needed to use sciqlop-core - -if(SCIQLOP-CORE_FOUND) - return() -endif(SCIQLOP-CORE_FOUND) - -set(SCIQLOP-CORE_INCLUDE_DIR ${sciqlop-core_DIR}/../include) - -set (OS_LIB_EXTENSION "so") - -if(WIN32) - set (OS_LIB_EXTENSION "dll") -endif(WIN32) - -if (APPLE) - set (OS_LIB_EXTENSION "dylib") -endif(APPLE) - -set(SCIQLOP-CORE_LIBRARIES ${LIBRARY_OUTPUT_PATH}/libsciqlop_core${DEBUG_SUFFIX}.${OS_LIB_EXTENSION}) - -set(SCIQLOP-CORE_FOUND TRUE) diff --git a/core/include/Catalogue/CatalogueController.h b/core/include/Catalogue/CatalogueController.h index cf089e7..a385242 100644 --- a/core/include/Catalogue/CatalogueController.h +++ b/core/include/Catalogue/CatalogueController.h @@ -41,8 +41,7 @@ public: /// retrieveEvents with empty repository retrieve them from the default repository std::list > retrieveEvents(const QString &repository) const; std::list > retrieveAllEvents() const; - std::list > - retrieveEventsFromCatalogue(std::shared_ptr catalogue) const; + void addEvent(std::shared_ptr event); void updateEvent(std::shared_ptr event); void updateEventProduct(std::shared_ptr eventProduct); @@ -50,17 +49,21 @@ public: // void trashEvent(std::shared_ptr event); // void restore(std::shared_ptr event); void saveEvent(std::shared_ptr event); - void discardEvent(std::shared_ptr event); + void discardEvent(std::shared_ptr event, bool &removed); bool eventHasChanges(std::shared_ptr event) const; // Catalogue - // bool createCatalogue(const QString &name, QVector eventList); + std::list > + retrieveEventsFromCatalogue(std::shared_ptr catalogue) const; + /// retrieveEvents with empty repository retrieve them from the default repository std::list > retrieveCatalogues(const QString &repository = QString()) const; + void addCatalogue(std::shared_ptr catalogue); void updateCatalogue(std::shared_ptr catalogue); void removeCatalogue(std::shared_ptr catalogue); void saveCatalogue(std::shared_ptr catalogue); + void discardCatalogue(std::shared_ptr catalogue, bool &removed); void saveAll(); bool hasChanges() const; @@ -71,6 +74,13 @@ public: /// Returns the list of variables contained in a MIME data QVector > eventsForMimeData(const QByteArray &mimeData) const; + /// Returns the MIME data associated to a list of variables + QByteArray + mimeDataForCatalogues(const QVector > &catalogues) const; + + /// Returns the list of variables contained in a MIME data + QVector > cataloguesForMimeData(const QByteArray &mimeData) const; + public slots: /// Manage init/end of the controller void initialize(); diff --git a/core/include/Common/DateUtils.h b/core/include/Common/DateUtils.h index 7d80539..b185027 100644 --- a/core/include/Common/DateUtils.h +++ b/core/include/Common/DateUtils.h @@ -5,6 +5,10 @@ #include +/// Format for datetimes +const auto DATETIME_FORMAT = QStringLiteral("dd/MM/yyyy \nhh:mm:ss:zzz"); +const auto DATETIME_FORMAT_ONE_LINE = QStringLiteral("dd/MM/yyyy hh:mm:ss:zzz"); + /** * Utility class with methods for dates */ diff --git a/core/include/Common/MimeTypesDef.h b/core/include/Common/MimeTypesDef.h index cb8113a..00dcd3a 100644 --- a/core/include/Common/MimeTypesDef.h +++ b/core/include/Common/MimeTypesDef.h @@ -16,6 +16,7 @@ extern SCIQLOP_CORE_EXPORT const QString MIME_TYPE_PRODUCT_LIST; extern SCIQLOP_CORE_EXPORT const QString MIME_TYPE_TIME_RANGE; extern SCIQLOP_CORE_EXPORT const QString MIME_TYPE_SELECTION_ZONE; extern SCIQLOP_CORE_EXPORT const QString MIME_TYPE_EVENT_LIST; +extern SCIQLOP_CORE_EXPORT const QString MIME_TYPE_SOURCE_CATALOGUE_LIST; #endif // SCIQLOP_MIMETYPESDEF_H diff --git a/core/include/Data/DataSeriesType.h b/core/include/Data/DataSeriesType.h new file mode 100644 index 0000000..d019c83 --- /dev/null +++ b/core/include/Data/DataSeriesType.h @@ -0,0 +1,26 @@ +#ifndef SCIQLOP_DATASERIESTYPE_H +#define SCIQLOP_DATASERIESTYPE_H + +#include + +enum class DataSeriesType { SCALAR, SPECTROGRAM, VECTOR, UNKNOWN }; + +struct DataSeriesTypeUtils { + static DataSeriesType fromString(const QString &type) + { + if (type == QStringLiteral("scalar")) { + return DataSeriesType::SCALAR; + } + else if (type == QStringLiteral("spectrogram")) { + return DataSeriesType::SPECTROGRAM; + } + else if (type == QStringLiteral("vector")) { + return DataSeriesType::VECTOR; + } + else { + return DataSeriesType::UNKNOWN; + } + } +}; + +#endif // SCIQLOP_DATASERIESTYPE_H diff --git a/core/include/DataSource/DataSourceController.h b/core/include/DataSource/DataSourceController.h index cbf2d67..4e41b7e 100644 --- a/core/include/DataSource/DataSourceController.h +++ b/core/include/DataSource/DataSourceController.h @@ -75,6 +75,10 @@ public slots: void initialize(); void finalize(); + /// Request the creation of a variable from the ID_DATA_KEY of a product + void requestVariableFromProductIdKey(const QString &datasourceIdKey); + + /// Request the creation of a variable from metadata of a product void requestVariable(const QVariantHash &productData); signals: diff --git a/core/include/DataSource/DataSourceItem.h b/core/include/DataSource/DataSourceItem.h index b872ce6..a1edb5e 100644 --- a/core/include/DataSource/DataSourceItem.h +++ b/core/include/DataSource/DataSourceItem.h @@ -145,6 +145,14 @@ public: */ DataSourceItem *findItem(const QVariantHash &data, bool recursive); + /** + * @brief Searches the first child matching the specified \p ID_DATA_KEY in its metadata. + * @param id The id to search. + * @param recursive So the search recursively. + * @return the item matching the data or nullptr if it was not found. + */ + DataSourceItem *findItem(const QString &datasourceIdKey, bool recursive); + bool operator==(const DataSourceItem &other); bool operator!=(const DataSourceItem &other); diff --git a/plugin/include/Plugin/IPlugin.h b/core/include/Plugin/IPlugin.h similarity index 100% rename from plugin/include/Plugin/IPlugin.h rename to core/include/Plugin/IPlugin.h diff --git a/core/include/Plugin/PluginManager.h b/core/include/PluginManager/PluginManager.h similarity index 96% rename from core/include/Plugin/PluginManager.h rename to core/include/PluginManager/PluginManager.h index 025e42b..3f834f6 100644 --- a/core/include/Plugin/PluginManager.h +++ b/core/include/PluginManager/PluginManager.h @@ -14,7 +14,7 @@ Q_DECLARE_LOGGING_CATEGORY(LOG_PluginManager) /** * @brief The PluginManager class aims to handle the plugins loaded dynamically into SciQLop. */ -class SCIQLOP_CORE_EXPORT PluginManager { +class PluginManager { public: explicit PluginManager(); diff --git a/core/include/Variable/Variable.h b/core/include/Variable/Variable.h index 88c5682..ea92956 100644 --- a/core/include/Variable/Variable.h +++ b/core/include/Variable/Variable.h @@ -4,6 +4,7 @@ #include "CoreGlobal.h" #include +#include #include #include @@ -54,6 +55,9 @@ public: /// @return the data of the variable, nullptr if there is no data std::shared_ptr dataSeries() const noexcept; + /// @return the type of data that the variable holds + DataSeriesType type() const noexcept; + QVariantHash metadata() const noexcept; bool contains(const SqpRange &range) const noexcept; @@ -76,6 +80,8 @@ public: signals: void updated(); + /// Signal emitted when when the data series of the variable is loaded for the first time + void dataInitialized(); private: class VariablePrivate; diff --git a/core/include/Variable/VariableController.h b/core/include/Variable/VariableController.h index 9c02286..9238f8a 100644 --- a/core/include/Variable/VariableController.h +++ b/core/include/Variable/VariableController.h @@ -48,26 +48,6 @@ public: */ std::shared_ptr cloneVariable(std::shared_ptr variable) noexcept; - /** - * Deletes from the controller the variable passed in parameter. - * - * Delete a variable includes: - * - the deletion of the various references to the variable in SciQlop - * - the deletion of the model variable - * - the deletion of the provider associated with the variable - * - removing the cache associated with the variable - * - * @param variable the variable to delete from the controller. - */ - void deleteVariable(std::shared_ptr variable) noexcept; - - /** - * Deletes from the controller the variables passed in parameter. - * @param variables the variables to delete from the controller. - * @sa deleteVariable() - */ - void deleteVariables(const QVector > &variables) noexcept; - /// Returns the MIME data associated to a list of variables QByteArray mimeDataForVariables(const QList > &variables) const; @@ -89,7 +69,29 @@ signals: /// validated, canceled, or failed) void acquisitionFinished(); + void variableAdded(const std::shared_ptr &variable); + public slots: + /** + * Deletes from the controller the variable passed in parameter. + * + * Delete a variable includes: + * - the deletion of the various references to the variable in SciQlop + * - the deletion of the model variable + * - the deletion of the provider associated with the variable + * - removing the cache associated with the variable + * + * @param variable the variable to delete from the controller. + */ + void deleteVariable(std::shared_ptr variable) noexcept; + + /** + * Deletes from the controller the variables passed in parameter. + * @param variables the variables to delete from the controller. + * @sa deleteVariable() + */ + void deleteVariables(const QVector > &variables) noexcept; + /// Request the data loading of the variable whithin range void onRequestDataLoading(QVector > variables, const SqpRange &range, bool synchronise); diff --git a/core/meson.build b/core/meson.build index 8ce78de..92d55b1 100644 --- a/core/meson.build +++ b/core/meson.build @@ -1,6 +1,6 @@ qxorm_dep = dependency('QxOrm', required : true, fallback:['QxOrm','qxorm_dep']) -catalogueapi_dep = dependency('CatalogueAPI', required : true, fallback:['CatalogueAPI','CatalogueAPI_dep']) +libcatalogs_dep = dependency('libcatalogs', required : true, fallback:['libcatalogs','libcatalogs_dep']) core_moc_headers = [ @@ -41,7 +41,7 @@ core_sources = [ 'src/DataSource/DataSourceItemAction.cpp', 'src/DataSource/DataSourceItemMergeHelper.cpp', 'src/Network/NetworkController.cpp', - 'src/Plugin/PluginManager.cpp', + 'src/PluginManager/PluginManager.cpp', 'src/Settings/SqpSettingsDefs.cpp', 'src/Time/TimeController.cpp', 'src/Variable/Variable.cpp', @@ -53,21 +53,21 @@ core_sources = [ 'src/Visualization/VisualizationController.cpp' ] -core_inc = include_directories(['include', '../plugin/include']) +core_inc = include_directories(['include', 'include/Plugin']) sciqlop_core_lib = library('sciqlopcore', core_sources, core_moc_files, cpp_args : '-DCORE_LIB', include_directories : core_inc, - dependencies : [qt5core, qt5network, catalogueapi_dep], + dependencies : [qt5core, qt5network, libcatalogs_dep], install : true ) sciqlop_core = declare_dependency(link_with : sciqlop_core_lib, include_directories : core_inc, - dependencies : [qt5core, qt5network, catalogueapi_dep]) + dependencies : [qt5core, qt5network, libcatalogs_dep]) diff --git a/core/src/Catalogue/CatalogueController.cpp b/core/src/Catalogue/CatalogueController.cpp index 6ae79e0..b9e5743 100644 --- a/core/src/Catalogue/CatalogueController.cpp +++ b/core/src/Catalogue/CatalogueController.cpp @@ -27,6 +27,10 @@ static QString REPOSITORY_WORK_SUFFIX = QString{"_work"}; static QString REPOSITORY_TRASH_SUFFIX = QString{"_trash"}; } +/** + * Possible types of an repository + */ +enum class DBType { SYNC, WORK, TRASH }; class CatalogueController::CatalogueControllerPrivate { public: @@ -37,9 +41,10 @@ public: QStringList m_RepositoryList; CatalogueController *m_Q; - QSet m_EventKeysWithChanges; + QSet m_KeysWithChanges; QString eventUniqueKey(const std::shared_ptr &event) const; + QString catalogueUniqueKey(const std::shared_ptr &catalogue) const; void copyDBtoDB(const QString &dbFrom, const QString &dbTo); QString toWorkRepository(QString repository); @@ -48,6 +53,9 @@ public: void saveEvent(std::shared_ptr event, bool persist = true); void saveCatalogue(std::shared_ptr catalogue, bool persist = true); + + std::shared_ptr createFinder(const QUuid &uniqId, const QString &repository, + DBType type); }; CatalogueController::CatalogueController(QObject *parent) @@ -142,7 +150,7 @@ void CatalogueController::updateEvent(std::shared_ptr event) event->setRepository(impl->toWorkRepository(event->getRepository())); auto uniqueId = impl->eventUniqueKey(event); - impl->m_EventKeysWithChanges.insert(uniqueId); + impl->m_KeysWithChanges.insert(uniqueId); impl->m_CatalogueDao.updateEvent(*event); } @@ -183,48 +191,49 @@ void CatalogueController::addEvent(std::shared_ptr event) impl->m_CatalogueDao.updateEvent(eventTemp); } + + auto workPred = impl->createFinder(event->getUniqId(), event->getRepository(), DBType::WORK); + + auto workEvent = impl->m_CatalogueDao.getEvent(workPred); + *event = workEvent; + + + auto uniqueId = impl->eventUniqueKey(event); + impl->m_KeysWithChanges.insert(uniqueId); } void CatalogueController::saveEvent(std::shared_ptr event) { impl->saveEvent(event, true); - impl->m_EventKeysWithChanges.remove(impl->eventUniqueKey(event)); + impl->m_KeysWithChanges.remove(impl->eventUniqueKey(event)); } -void CatalogueController::discardEvent(std::shared_ptr event) +void CatalogueController::discardEvent(std::shared_ptr event, bool &removed) { - auto uniqIdPredicate = std::make_shared( - QString{"uniqId"}, event->getUniqId(), ComparaisonOperation::EQUALEQUAL); - - auto syncRepositoryPredicate = std::make_shared( - QString{"repository"}, impl->toSyncRepository(event->getRepository()), - ComparaisonOperation::EQUALEQUAL); - - auto syncPred = std::make_shared(CompoundOperation::AND); - syncPred->AddRequestPredicate(uniqIdPredicate); - syncPred->AddRequestPredicate(syncRepositoryPredicate); - - - auto workRepositoryPredicate = std::make_shared( - QString{"repository"}, impl->toWorkRepository(event->getRepository()), - ComparaisonOperation::EQUALEQUAL); - - auto workPred = std::make_shared(CompoundOperation::AND); - workPred->AddRequestPredicate(uniqIdPredicate); - workPred->AddRequestPredicate(workRepositoryPredicate); - + auto syncPred = impl->createFinder(event->getUniqId(), event->getRepository(), DBType::SYNC); + auto workPred = impl->createFinder(event->getUniqId(), event->getRepository(), DBType::WORK); auto syncEvent = impl->m_CatalogueDao.getEvent(syncPred); - impl->m_CatalogueDao.copyEvent(syncEvent, impl->toWorkRepository(event->getRepository()), true); - - auto workEvent = impl->m_CatalogueDao.getEvent(workPred); - *event = workEvent; - impl->m_EventKeysWithChanges.remove(impl->eventUniqueKey(event)); + if (!syncEvent.getUniqId().isNull()) { + removed = false; + impl->m_CatalogueDao.copyEvent(syncEvent, impl->toWorkRepository(event->getRepository()), + true); + + auto workEvent = impl->m_CatalogueDao.getEvent(workPred); + *event = workEvent; + impl->m_KeysWithChanges.remove(impl->eventUniqueKey(event)); + } + else { + removed = true; + // Since the element wasn't in sync repository. Discard it means remove it + event->setRepository(impl->toWorkRepository(event->getRepository())); + impl->m_CatalogueDao.removeEvent(*event); + } } bool CatalogueController::eventHasChanges(std::shared_ptr event) const { - return impl->m_EventKeysWithChanges.contains(impl->eventUniqueKey(event)); + return impl->m_KeysWithChanges.contains(impl->eventUniqueKey(event)); } std::list > @@ -240,10 +249,30 @@ CatalogueController::retrieveCatalogues(const QString &repository) const return cataloguesShared; } +void CatalogueController::addCatalogue(std::shared_ptr catalogue) +{ + catalogue->setRepository(impl->toWorkRepository(catalogue->getRepository())); + + auto catalogueTemp = *catalogue; + impl->m_CatalogueDao.addCatalogue(catalogueTemp); + + auto workPred + = impl->createFinder(catalogue->getUniqId(), catalogue->getRepository(), DBType::WORK); + + auto workCatalogue = impl->m_CatalogueDao.getCatalogue(workPred); + *catalogue = workCatalogue; + + auto uniqueId = impl->catalogueUniqueKey(catalogue); + impl->m_KeysWithChanges.insert(uniqueId); +} + void CatalogueController::updateCatalogue(std::shared_ptr catalogue) { catalogue->setRepository(impl->toWorkRepository(catalogue->getRepository())); + auto uniqueId = impl->catalogueUniqueKey(catalogue); + impl->m_KeysWithChanges.insert(uniqueId); + impl->m_CatalogueDao.updateCatalogue(*catalogue); } @@ -254,11 +283,46 @@ void CatalogueController::removeCatalogue(std::shared_ptr catalogue impl->m_CatalogueDao.removeCatalogue(*catalogue); catalogue->setRepository(impl->toSyncRepository(catalogue->getRepository())); impl->m_CatalogueDao.removeCatalogue(*catalogue); + impl->savAllDB(); } void CatalogueController::saveCatalogue(std::shared_ptr catalogue) { impl->saveCatalogue(catalogue, true); + impl->m_KeysWithChanges.remove(impl->catalogueUniqueKey(catalogue)); + + // remove key of events of the catalogue + if (catalogue->getType() == CatalogueType::STATIC) { + auto events = this->retrieveEventsFromCatalogue(catalogue); + for (auto event : events) { + impl->m_KeysWithChanges.remove(impl->eventUniqueKey(event)); + } + } +} + +void CatalogueController::discardCatalogue(std::shared_ptr catalogue, bool &removed) +{ + auto syncPred + = impl->createFinder(catalogue->getUniqId(), catalogue->getRepository(), DBType::SYNC); + auto workPred + = impl->createFinder(catalogue->getUniqId(), catalogue->getRepository(), DBType::WORK); + + auto syncCatalogue = impl->m_CatalogueDao.getCatalogue(syncPred); + if (!syncCatalogue.getUniqId().isNull()) { + removed = false; + impl->m_CatalogueDao.copyCatalogue( + syncCatalogue, impl->toWorkRepository(catalogue->getRepository()), true); + + auto workCatalogue = impl->m_CatalogueDao.getCatalogue(workPred); + *catalogue = workCatalogue; + impl->m_KeysWithChanges.remove(impl->catalogueUniqueKey(catalogue)); + } + else { + removed = true; + // Since the element wasn't in sync repository. Discard it means remove it + catalogue->setRepository(impl->toWorkRepository(catalogue->getRepository())); + impl->m_CatalogueDao.removeCatalogue(*catalogue); + } } void CatalogueController::saveAll() @@ -278,12 +342,12 @@ void CatalogueController::saveAll() } impl->savAllDB(); - impl->m_EventKeysWithChanges.clear(); + impl->m_KeysWithChanges.clear(); } bool CatalogueController::hasChanges() const { - return !impl->m_EventKeysWithChanges.isEmpty(); // TODO: catalogues + return !impl->m_KeysWithChanges.isEmpty(); } QByteArray @@ -326,6 +390,46 @@ CatalogueController::eventsForMimeData(const QByteArray &mimeData) const return events; } +QByteArray CatalogueController::mimeDataForCatalogues( + const QVector > &catalogues) const +{ + auto encodedData = QByteArray{}; + + QMap idsPerRepository; + for (auto catalogue : catalogues) { + idsPerRepository[catalogue->getRepository()] << catalogue->getUniqId(); + } + + QDataStream stream{&encodedData, QIODevice::WriteOnly}; + stream << idsPerRepository; + + return encodedData; +} + +QVector > +CatalogueController::cataloguesForMimeData(const QByteArray &mimeData) const +{ + auto catalogues = QVector >{}; + QDataStream stream{mimeData}; + + QMap idsPerRepository; + stream >> idsPerRepository; + + for (auto it = idsPerRepository.cbegin(); it != idsPerRepository.cend(); ++it) { + auto repository = it.key(); + auto allRepositoryCatalogues = retrieveCatalogues(repository); + for (auto uuid : it.value()) { + for (auto repositoryCatalogues : allRepositoryCatalogues) { + if (uuid.toUuid() == repositoryCatalogues->getUniqId()) { + catalogues << repositoryCatalogues; + } + } + } + } + + return catalogues; +} + void CatalogueController::initialize() { qCDebug(LOG_CatalogueController()) << tr("CatalogueController init") @@ -339,9 +443,23 @@ void CatalogueController::initialize() if (defaultRepositoryLocationDir.mkpath(defaultRepositoryLocation)) { defaultRepositoryLocationDir.cd(defaultRepositoryLocation); auto defaultRepository = defaultRepositoryLocationDir.absoluteFilePath(REPOSITORY_DEFAULT); + qCInfo(LOG_CatalogueController()) << tr("Persistant data loading from: ") << defaultRepository; - this->addDB(defaultRepository); + + QDir dbDir(defaultRepository); + impl->m_RepositoryList << REPOSITORY_DEFAULT; + if (dbDir.exists()) { + auto dirName = dbDir.dirName(); + + if (impl->m_CatalogueDao.addDB(defaultRepository, dirName)) { + impl->copyDBtoDB(dirName, impl->toWorkRepository(dirName)); + } + } + else { + qCInfo(LOG_CatalogueController()) << tr("Initialisation of Default repository detected") + << defaultRepository; + } } else { qCWarning(LOG_CatalogueController()) @@ -358,6 +476,12 @@ QString CatalogueController::CatalogueControllerPrivate::eventUniqueKey( return event->getUniqId().toString().append(event->getRepository()); } +QString CatalogueController::CatalogueControllerPrivate::catalogueUniqueKey( + const std::shared_ptr &catalogue) const +{ + return catalogue->getUniqId().toString().append(catalogue->getRepository()); +} + void CatalogueController::CatalogueControllerPrivate::copyDBtoDB(const QString &dbFrom, const QString &dbTo) { @@ -418,3 +542,33 @@ void CatalogueController::CatalogueControllerPrivate::saveCatalogue( savAllDB(); } } + +std::shared_ptr CatalogueController::CatalogueControllerPrivate::createFinder( + const QUuid &uniqId, const QString &repository, DBType type) +{ + // update catalogue parameter + auto uniqIdPredicate = std::make_shared(QString{"uniqId"}, uniqId, + ComparaisonOperation::EQUALEQUAL); + + auto repositoryType = repository; + switch (type) { + case DBType::SYNC: + repositoryType = toSyncRepository(repositoryType); + break; + case DBType::WORK: + repositoryType = toWorkRepository(repositoryType); + break; + case DBType::TRASH: + default: + break; + } + + auto repositoryPredicate = std::make_shared( + QString{"repository"}, repositoryType, ComparaisonOperation::EQUALEQUAL); + + auto finderPred = std::make_shared(CompoundOperation::AND); + finderPred->AddRequestPredicate(uniqIdPredicate); + finderPred->AddRequestPredicate(repositoryPredicate); + + return finderPred; +} diff --git a/core/src/Common/MimeTypesDef.cpp b/core/src/Common/MimeTypesDef.cpp index 85a5347..7fdc6f4 100644 --- a/core/src/Common/MimeTypesDef.cpp +++ b/core/src/Common/MimeTypesDef.cpp @@ -7,3 +7,4 @@ const QString MIME_TYPE_PRODUCT_LIST = QStringLiteral("sciqlop/product-list"); const QString MIME_TYPE_TIME_RANGE = QStringLiteral("sciqlop/time-range"); const QString MIME_TYPE_SELECTION_ZONE = QStringLiteral("sciqlop/selection-zone"); const QString MIME_TYPE_EVENT_LIST = QStringLiteral("sciqlop/event-list"); +const QString MIME_TYPE_SOURCE_CATALOGUE_LIST = QStringLiteral("sciqlop/source-catalogue-list"); diff --git a/core/src/DataSource/DataSourceController.cpp b/core/src/DataSource/DataSourceController.cpp index a7bc3d2..b59016a 100644 --- a/core/src/DataSource/DataSourceController.cpp +++ b/core/src/DataSource/DataSourceController.cpp @@ -37,6 +37,20 @@ public: return sourceItem; } + + // Search for the first datasource item matching the specified ID_DATA_KEY + DataSourceItem *findDataSourceItem(const QString &datasourceIdKey) + { + DataSourceItem *sourceItem = nullptr; + for (const auto &item : m_DataSourceItems) { + sourceItem = item.second->findItem(datasourceIdKey, true); + if (sourceItem) { + break; + } + } + + return sourceItem; + } }; DataSourceController::DataSourceController(QObject *parent) @@ -149,6 +163,20 @@ void DataSourceController::finalize() impl->m_WorkingMutex.unlock(); } +void DataSourceController::requestVariableFromProductIdKey(const QString &datasourceIdKey) +{ + auto sourceItem = impl->findDataSourceItem(datasourceIdKey); + + if (sourceItem) { + auto sourceName = sourceItem->rootItem().name(); + auto sourceId = impl->m_DataSources.key(sourceName); + loadProductItem(sourceId, *sourceItem); + } + else { + qCWarning(LOG_DataSourceController()) << tr("requestVariable, product data not found"); + } +} + void DataSourceController::requestVariable(const QVariantHash &productData) { auto sourceItem = impl->findDataSourceItem(productData); diff --git a/core/src/DataSource/DataSourceItem.cpp b/core/src/DataSource/DataSourceItem.cpp index 86730d3..3d59332 100644 --- a/core/src/DataSource/DataSourceItem.cpp +++ b/core/src/DataSource/DataSourceItem.cpp @@ -165,6 +165,24 @@ DataSourceItem *DataSourceItem::findItem(const QVariantHash &data, bool recursiv return nullptr; } +DataSourceItem *DataSourceItem::findItem(const QString &datasourceIdKey, bool recursive) +{ + for (const auto &child : impl->m_Children) { + auto childId = child->impl->m_Data.value(ID_DATA_KEY); + if (childId == datasourceIdKey) { + return child.get(); + } + + if (recursive) { + if (auto foundItem = child->findItem(datasourceIdKey, true)) { + return foundItem; + } + } + } + + return nullptr; +} + bool DataSourceItem::operator==(const DataSourceItem &other) { // Compares items' attributes diff --git a/core/src/Plugin/PluginManager.cpp b/core/src/PluginManager/PluginManager.cpp similarity index 99% rename from core/src/Plugin/PluginManager.cpp rename to core/src/PluginManager/PluginManager.cpp index 6579f63..1a116f5 100644 --- a/core/src/Plugin/PluginManager.cpp +++ b/core/src/PluginManager/PluginManager.cpp @@ -1,4 +1,4 @@ -#include +#include #include diff --git a/core/src/Variable/Variable.cpp b/core/src/Variable/Variable.cpp index 8d68fa0..d431fda 100644 --- a/core/src/Variable/Variable.cpp +++ b/core/src/Variable/Variable.cpp @@ -9,6 +9,29 @@ Q_LOGGING_CATEGORY(LOG_Variable, "Variable") +namespace { + +/** + * Searches in metadata for a value that can be converted to DataSeriesType + * @param metadata the metadata where to search + * @return the value converted to a DataSeriesType if it was found, UNKNOWN type otherwise + * @sa DataSeriesType + */ +DataSeriesType findDataSeriesType(const QVariantHash &metadata) +{ + auto dataSeriesType = DataSeriesType::UNKNOWN; + + // Go through the metadata and stop at the first value that could be converted to DataSeriesType + for (auto it = metadata.cbegin(), end = metadata.cend(); + it != end && dataSeriesType == DataSeriesType::UNKNOWN; ++it) { + dataSeriesType = DataSeriesTypeUtils::fromString(it.value().toString()); + } + + return dataSeriesType; +} + +} // namespace + struct Variable::VariablePrivate { explicit VariablePrivate(const QString &name, const QVariantHash &metadata) : m_Name{name}, @@ -17,7 +40,8 @@ struct Variable::VariablePrivate { m_Metadata{metadata}, m_DataSeries{nullptr}, m_RealRange{INVALID_RANGE}, - m_NbPoints{0} + m_NbPoints{0}, + m_Type{findDataSeriesType(m_Metadata)} { } @@ -28,7 +52,8 @@ struct Variable::VariablePrivate { m_Metadata{other.m_Metadata}, m_DataSeries{other.m_DataSeries != nullptr ? other.m_DataSeries->clone() : nullptr}, m_RealRange{other.m_RealRange}, - m_NbPoints{other.m_NbPoints} + m_NbPoints{other.m_NbPoints}, + m_Type{findDataSeriesType(m_Metadata)} { } @@ -75,6 +100,7 @@ struct Variable::VariablePrivate { std::shared_ptr m_DataSeries; SqpRange m_RealRange; int m_NbPoints; + DataSeriesType m_Type; QReadWriteLock m_Lock; }; @@ -161,16 +187,23 @@ void Variable::mergeDataSeries(std::shared_ptr dataSeries) noexcept return; } + auto dataInit = false; + // Add or merge the data impl->lockWrite(); if (!impl->m_DataSeries) { impl->m_DataSeries = dataSeries->clone(); + dataInit = true; } else { impl->m_DataSeries->merge(dataSeries.get()); } impl->purgeDataSeries(); impl->unlock(); + + if (dataInit) { + emit dataInitialized(); + } } @@ -183,6 +216,15 @@ std::shared_ptr Variable::dataSeries() const noexcept return dataSeries; } +DataSeriesType Variable::type() const noexcept +{ + impl->lockRead(); + auto type = impl->m_Type; + impl->unlock(); + + return type; +} + QVariantHash Variable::metadata() const noexcept { impl->lockRead(); diff --git a/core/src/Variable/VariableController.cpp b/core/src/Variable/VariableController.cpp index 7e1e4b0..916d0cb 100644 --- a/core/src/Variable/VariableController.cpp +++ b/core/src/Variable/VariableController.cpp @@ -136,6 +136,9 @@ struct VariableController::VariableControllerPrivate { void cancelVariableRequest(QUuid varRequestId); void executeVarRequest(std::shared_ptr var, VariableRequest &varRequest); + template + void desynchronize(VariableIterator variableIt, const QUuid &syncGroupId); + QMutex m_WorkingMutex; /// Variable model. The VariableController has the ownership VariableModel *m_VariableModel; @@ -258,8 +261,22 @@ void VariableController::deleteVariable(std::shared_ptr variable) noex // make some treatments before the deletion emit variableAboutToBeDeleted(variable); + auto variableIt = impl->m_VariableToIdentifierMap.find(variable); + Q_ASSERT(variableIt != impl->m_VariableToIdentifierMap.cend()); + + auto variableId = variableIt->second; + + // Removes variable's handler + impl->m_VarIdToVarRequestHandler.erase(variableId); + + // Desynchronizes variable (if the variable is in a sync group) + auto syncGroupIt = impl->m_VariableIdGroupIdMap.find(variableId); + if (syncGroupIt != impl->m_VariableIdGroupIdMap.cend()) { + impl->desynchronize(variableIt, syncGroupIt->second); + } + // Deletes identifier - impl->m_VariableToIdentifierMap.erase(variable); + impl->m_VariableToIdentifierMap.erase(variableIt); // Deletes provider auto nbProvidersDeleted = impl->m_VariableToProviderMap.erase(variable); @@ -356,6 +373,8 @@ VariableController::createVariable(const QString &name, const QVariantHash &meta // impl->processRequest(newVariable, range, varRequestId); // impl->updateVariableRequest(varRequestId); + emit variableAdded(newVariable); + return newVariable; } @@ -545,23 +564,7 @@ void VariableController::desynchronize(std::shared_ptr variable, return; } - // Gets synchronization group - auto groupIt = impl->m_GroupIdToVariableSynchronizationGroupMap.find(synchronizationGroupId); - if (groupIt == impl->m_GroupIdToVariableSynchronizationGroupMap.cend()) { - qCCritical(LOG_VariableController()) - << tr("Can't desynchronize variable %1: unknown synchronization group") - .arg(variable->name()); - return; - } - - auto variableId = variableIt->second; - - // Removes variable from synchronization group - auto synchronizationGroup = groupIt->second; - synchronizationGroup->removeVariableId(variableId); - - // Removes link between variable and synchronization group - impl->m_VariableIdGroupIdMap.erase(variableId); + impl->desynchronize(variableIt, synchronizationGroupId); } void VariableController::onRequestDataLoading(QVector > variables, @@ -933,6 +936,7 @@ void VariableController::VariableControllerPrivate::updateVariableRequest(QUuid varHandler->m_PendingVarRequest = VariableRequest{}; auto var = findVariable(itVarHandler->first); executeVarRequest(var, varHandler->m_RunningVarRequest); + updateVariables(varHandler->m_RunningVarRequest.m_VariableGroupId); break; } default: @@ -1032,7 +1036,14 @@ void VariableController::VariableControllerPrivate::executeVarRequest(std::share { qCDebug(LOG_VariableController()) << tr("TORM: executeVarRequest"); - auto varId = m_VariableToIdentifierMap.at(var); + auto varIdIt = m_VariableToIdentifierMap.find(var); + if (varIdIt == m_VariableToIdentifierMap.cend()) { + qCWarning(LOG_VariableController()) << tr( + "Can't execute request of a variable that is not registered (may has been deleted)"); + return; + } + + auto varId = varIdIt->second; auto varCacheRange = var->cacheRange(); auto varCacheRangeRequested = varRequest.m_CacheRangeRequested; @@ -1067,3 +1078,27 @@ void VariableController::VariableControllerPrivate::executeVarRequest(std::share var->dataSeries()->subDataSeries(varRequest.m_CacheRangeRequested)); } } + +template +void VariableController::VariableControllerPrivate::desynchronize(VariableIterator variableIt, + const QUuid &syncGroupId) +{ + const auto &variable = variableIt->first; + const auto &variableId = variableIt->second; + + // Gets synchronization group + auto groupIt = m_GroupIdToVariableSynchronizationGroupMap.find(syncGroupId); + if (groupIt == m_GroupIdToVariableSynchronizationGroupMap.cend()) { + qCCritical(LOG_VariableController()) + << tr("Can't desynchronize variable %1: unknown synchronization group") + .arg(variable->name()); + return; + } + + // Removes variable from synchronization group + auto synchronizationGroup = groupIt->second; + synchronizationGroup->removeVariableId(variableId); + + // Removes link between variable and synchronization group + m_VariableIdGroupIdMap.erase(variableId); +} diff --git a/core/src/Variable/VariableModel.cpp b/core/src/Variable/VariableModel.cpp index 490ebc8..be665ef 100644 --- a/core/src/Variable/VariableModel.cpp +++ b/core/src/Variable/VariableModel.cpp @@ -52,9 +52,6 @@ const auto COLUMN_PROPERTIES = QHash{ {UNIT_COLUMN, {QObject::tr("Unit")}}, {MISSION_COLUMN, {QObject::tr("Mission")}}, {PLUGIN_COLUMN, {QObject::tr("Plugin")}}}; -/// Format for datetimes -const auto DATETIME_FORMAT = QStringLiteral("dd/MM/yyyy \nhh:mm:ss:zzz"); - QString uniqueName(const QString &defaultName, const std::vector > &variables) { diff --git a/core/tests/CMakeLists.txt b/core/tests/CMakeLists.txt new file mode 100644 index 0000000..377fcae --- /dev/null +++ b/core/tests/CMakeLists.txt @@ -0,0 +1,33 @@ +include(sciqlop_tests) + +add_definitions(-DCORE_TESTS_RESOURCES_DIR="${CMAKE_CURRENT_LIST_DIR}/../tests-resources") + + +declare_test(TestStringUtils TestStringUtils Common/TestStringUtils.cpp "sciqlopcore;Qt5::Test") + +declare_test(TestDataSeriesUtils TestDataSeriesUtils Data/TestDataSeriesUtils.cpp "sciqlopcore;Qt5::Test") +declare_test(TestOptionalAxis TestOptionalAxis Data/TestOptionalAxis.cpp "sciqlopcore;Qt5::Test") +declare_test(TestSpectrogramSeries TestSpectrogramSeries + "Data/TestSpectrogramSeries.cpp;Data/DataSeriesBuilders.h;Data/DataSeriesBuilders.cpp;Data/DataSeriesTestsUtils.h;Data/DataSeriesTestsUtils.cpp" + "sciqlopcore;Qt5::Test") +declare_test(TestOneDimArrayData TestOneDimArrayData Data/TestOneDimArrayData.cpp "sciqlopcore;Qt5::Test") +declare_test(TestScalarSeries TestScalarSeries + "Data/TestScalarSeries.cpp;Data/DataSeriesBuilders.h;Data/DataSeriesBuilders.cpp;Data/DataSeriesTestsUtils.h;Data/DataSeriesTestsUtils.cpp" + "sciqlopcore;Qt5::Test") +declare_test(TestTwoDimArrayData TestTwoDimArrayData Data/TestTwoDimArrayData.cpp "sciqlopcore;Qt5::Test") +declare_test(TestVectorSeries TestVectorSeries + "Data/TestVectorSeries.cpp;Data/DataSeriesBuilders.h;Data/DataSeriesBuilders.cpp;Data/DataSeriesTestsUtils.h;Data/DataSeriesTestsUtils.cpp" + "sciqlopcore;Qt5::Test") + +declare_test(TestDataSourceController TestDataSourceController + "DataSource/TestDataSourceController.cpp;DataSource/DataSourceItemBuilder.cpp" + "sciqlopcore;Qt5::Test") +declare_test(TestDataSourceItem TestDataSourceItem + "DataSource/TestDataSourceItem.cpp;DataSource/DataSourceItemBuilder.cpp" + "sciqlopcore;Qt5::Test") + +declare_test(TestVariable TestVariable Variable/TestVariable.cpp "sciqlopcore;Qt5::Test") +declare_test(TestVariableCacheController TestVariableCacheController Variable/TestVariableCacheController.cpp "sciqlopcore;Qt5::Test") +declare_test(TestVariableController TestVariableController Variable/TestVariableController.cpp "sciqlopcore;Qt5::Test") +declare_test(TestVariableSync TestVariableSync Variable/TestVariableSync.cpp "sciqlopcore;Qt5::Test") + diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt new file mode 100644 index 0000000..a5bbc44 --- /dev/null +++ b/docs/CMakeLists.txt @@ -0,0 +1,13 @@ +find_package(Doxygen) +if(DOXYGEN_FOUND) + set(doxyfile_in ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.dox.in) + set(doxyfile ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) + configure_file(${doxyfile_in} ${doxyfile} @ONLY) + + add_custom_target(doc + COMMAND ${DOXYGEN_EXECUTABLE} ${doxyfile} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + COMMENT "Generating API documentation with Doxygen" + VERBATIM) + +endif() diff --git a/extern/CatalogueAPI.cmake b/extern/CatalogueAPI.cmake deleted file mode 100644 index c18078c..0000000 --- a/extern/CatalogueAPI.cmake +++ /dev/null @@ -1,73 +0,0 @@ -# - Clone and build CatalogueAPI Module -include(ExternalProject) - -find_package(Git REQUIRED) - -if(WIN32) - find_program(MesonExec meson PATHS C:/Appli/Meson) - if(NOT MesonExec) - Message("Error: Meson not found") - else() - message("Meson found: ${MesonExec}" ) - endif() - find_program(NinjaExec ninja PATHS C:/Appli/Meson) - if(NOT NinjaExec) - Message("Error: Ninja not found") - else() - message("Ninja found: ${NinjaExec}" ) - endif() -endif() -if(NOT MesonExec) - set (MesonExec meson) -endif() -if(NOT NinjaExec) - set (NinjaExec ninja) -endif() - -SET(CATALOGUEAPI_SOURCES_PATH ${CMAKE_SOURCE_DIR}/3rdparty/CatalogueAPI) -SET(CATALOGUEAPI_BUILD_PATH ${CATALOGUEAPI_SOURCES_PATH}/build) -SET(CATALOGUEAPI_QXORM_LIB_PATH ${CATALOGUEAPI_BUILD_PATH}/subprojects/QxOrm) -SET(CatalogueAPI_build_type plain) - -if(CMAKE_BUILD_TYPE STREQUAL "") - set(CMAKE_BUILD_TYPE Release) -endif() -string(TOLOWER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_TOLOWER) - -ExternalProject_Add( - CatalogueAPI - - GIT_REPOSITORY https://perrinel@hephaistos.lpp.polytechnique.fr/rhodecode/GIT_REPOSITORIES/LPP/Users/mperrinel/CatalogueAPI - GIT_TAG develop - - UPDATE_COMMAND ${GIT_EXECUTABLE} pull origin develop - PATCH_COMMAND "" - - SOURCE_DIR "${CATALOGUEAPI_SOURCES_PATH}" - CONFIGURE_COMMAND ${MesonExec} --prefix=${CATALOGUEAPI_SOURCES_PATH} --buildtype=${CMAKE_BUILD_TYPE_TOLOWER} "${CATALOGUEAPI_SOURCES_PATH}" "${CATALOGUEAPI_BUILD_PATH}" - - BUILD_COMMAND ${NinjaExec} -C "${CATALOGUEAPI_BUILD_PATH}" - INSTALL_COMMAND ${NinjaExec} -C "${CATALOGUEAPI_BUILD_PATH}" install - LOG_DOWNLOAD 1 - LOG_UPDATE 1 -) - -set(CATALOG_LIB_PATH lib) -if(WIN32) - set(CATALOG_LIB_PATH bin) -endif() - -ExternalProject_Add_Step( - CatalogueAPI CopyToBin - COMMAND ${CMAKE_COMMAND} -E copy_directory ${CATALOGUEAPI_SOURCES_PATH}/lib64 ${CATALOGUEAPI_SOURCES_PATH}/${CATALOG_LIB_PATH} - COMMAND ${CMAKE_COMMAND} -E copy_directory ${CATALOGUEAPI_QXORM_LIB_PATH} ${CATALOGUEAPI_SOURCES_PATH}/${CATALOG_LIB_PATH} - DEPENDEES install -) - - -set(CATALOGUEAPI_INCLUDE ${CATALOGUEAPI_SOURCES_PATH}/src) -set(CATALOGUEAPI_LIBRARIES ${CATALOGUEAPI_SOURCES_PATH}/${CATALOG_LIB_PATH}/${CMAKE_SHARED_LIBRARY_PREFIX}CatalogueAPI${CMAKE_SHARED_LIBRARY_SUFFIX}) -list(APPEND CATALOGUEAPI_LIBRARIES ${CATALOGUEAPI_SOURCES_PATH}/${CATALOG_LIB_PATH}/${CMAKE_SHARED_LIBRARY_PREFIX}QxOrm${CMAKE_SHARED_LIBRARY_SUFFIX}) - -mark_as_advanced(CATALOGUEAPI_INCLUDE) -mark_as_advanced(CATALOGUEAPI_LIBRARIES) diff --git a/external/CatalogueAPI b/external/CatalogueAPI new file mode 160000 index 0000000..232c863 --- /dev/null +++ b/external/CatalogueAPI @@ -0,0 +1 @@ +Subproject commit 232c863d6217298caf06deafb1dd821063f9efdc diff --git a/external/libcatalogs b/external/libcatalogs new file mode 160000 index 0000000..f2fa281 --- /dev/null +++ b/external/libcatalogs @@ -0,0 +1 @@ +Subproject commit f2fa281306099e7136a288dd70fd97d0e33afa15 diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt index 119546f..ecb3554 100644 --- a/gui/CMakeLists.txt +++ b/gui/CMakeLists.txt @@ -1,179 +1,51 @@ - -## gui - CMakeLists.txt -STRING(TOLOWER ${CMAKE_PROJECT_NAME} LIBRARY_PREFFIX) -SET(SQPGUI_LIBRARY_NAME "${LIBRARY_PREFFIX}_gui${DEBUG_SUFFIX}") -SET(SOURCES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src") -SET(INCLUDES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include") -SET(UI_FOLDER "${CMAKE_CURRENT_SOURCE_DIR}/ui") -SET(RES_FOLDER "${CMAKE_CURRENT_SOURCE_DIR}/resources") - -# Include gui directory -include_directories("${INCLUDES_DIR}") -include_directories("${CMAKE_CURRENT_BINARY_DIR}") - -# Set a variable to display a warning in the version files. -SET(SCIQLOP_CMAKE_GENERATION_WARNING "DON'T CHANGE THIS FILE. AUTOGENERATED BY CMAKE.") - -# -# Find Qt modules -# -SCIQLOP_FIND_QT(Core Widgets PrintSupport) - -# -# Find dependent libraries -# ======================== -find_package(sciqlop-core) - -SET(LIBRARIES ${SCIQLOP-CORE_LIBRARIES}) - -INCLUDE_DIRECTORIES(${SCIQLOP-CORE_INCLUDE_DIR}) - -# Add sqpcore to the list of libraries to use -list(APPEND LIBRARIES ${SQPCORE_LIBRARY_NAME}) - -# Add dependent shared libraries -list(APPEND SHARED_LIBRARIES ${SQPCORE_SHARED_LIBRARIES}) - - -# Ui files -FILE (GLOB_RECURSE PROJECT_FORMS ${UI_FOLDER}/*.ui) - -# Resources files -FILE (GLOB_RECURSE PROJECT_RESOURCES ${RES_FOLDER}/*.qrc) - -# -# Compile the library library -# -FILE (GLOB_RECURSE MODULE_SOURCES - ${INCLUDES_DIR}/*.h - ${SOURCES_DIR}/*.c - ${SOURCES_DIR}/*.cpp - ${SOURCES_DIR}/*.h - ${PROJECT_FORMS}) - -QT5_ADD_RESOURCES(RCC_HDRS - ${PROJECT_RESOURCES} -) - -QT5_WRAP_UI(UIS_HDRS - ${PROJECT_FORMS} +FILE (GLOB_RECURSE gui_SRCS + include/*.h + src/*.cpp + resources/*.qrc + ) + +QT5_WRAP_UI( + UiGenerated_SRCS + ui/DataSource/DataSourceWidget.ui + ui/Settings/SqpSettingsDialog.ui + ui/Settings/SqpSettingsGeneralWidget.ui + ui/SidePane/SqpSidePane.ui + ui/TimeWidget/TimeWidget.ui + ui/Variable/RenameVariableDialog.ui + ui/Variable/VariableInspectorWidget.ui + ui/Variable/VariableMenuHeaderWidget.ui + ui/Visualization/ColorScaleEditor.ui + ui/Visualization/VisualizationGraphWidget.ui + ui/Visualization/VisualizationTabWidget.ui + ui/Visualization/VisualizationWidget.ui + ui/Visualization/VisualizationZoneWidget.ui + ui/Visualization/VisualizationMultiZoneSelectionDialog.ui + ui/Catalogue/CatalogueEventsWidget.ui + ui/Catalogue/CatalogueExplorer.ui + ui/Catalogue/CatalogueInspectorWidget.ui + ui/Catalogue/CatalogueSideBarWidget.ui ) +add_library(sciqlopgui ${gui_SRCS} ${UiGenerated_SRCS}) +SET_TARGET_PROPERTIES(sciqlopgui PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE) -ADD_LIBRARY(${SQPGUI_LIBRARY_NAME} ${MODULE_SOURCES} ${UIS_HDRS} ${RCC_HDRS}) -set_property(TARGET ${SQPGUI_LIBRARY_NAME} PROPERTY CXX_STANDARD 14) -set_property(TARGET ${SQPGUI_LIBRARY_NAME} PROPERTY CXX_STANDARD_REQUIRED ON) - -TARGET_LINK_LIBRARIES(${SQPGUI_LIBRARY_NAME} ${LIBRARIES}) -qt5_use_modules(${SQPGUI_LIBRARY_NAME} Core Widgets PrintSupport) +target_include_directories(sciqlopgui PUBLIC + $ + $ + ) - -INSTALL(TARGETS ${SQPGUI_LIBRARY_NAME} - RUNTIME DESTINATION ${INSTALL_BINARY_DIR} - LIBRARY DESTINATION ${INSTALL_LIBRARY_DIR} - ARCHIVE DESTINATION ${INSTALL_LIBRARY_DIR} -) -add_dependencies(${SQPGUI_LIBRARY_NAME} ${SQPCORE_LIBRARY_NAME}) - -# Find CATALOGUE_API -include_directories("${CATALOGUEAPI_INCLUDE}") -TARGET_LINK_LIBRARIES(${SQPGUI_LIBRARY_NAME} ${CATALOGUEAPI_LIBRARIES}) -INSTALL(TARGETS ${SQPGUI_LIBRARY_NAME} - RUNTIME DESTINATION ${INSTALL_BINARY_DIR} - LIBRARY DESTINATION ${INSTALL_LIBRARY_DIR} - ARCHIVE DESTINATION ${INSTALL_LIBRARY_DIR} +target_link_libraries(sciqlopgui PUBLIC + Qt5::Widgets + Qt5::PrintSupport + Qt5::Svg + sciqlopcore ) -add_dependencies(${SQPGUI_LIBRARY_NAME} CatalogueAPI) - -# From cmake documentation: http://www.cmake.org/cmake/help/v3.0/manual/cmake-buildsystem.7.html -# Entries in the COMPILE_DEFINITIONS are prefixed with -D or /D and added to the compile line in an unspecified order. -# The DEFINE_SYMBOL target property is also added as a compile definition as a special convenience case for SHARED and MODULE library targets -IF(BUILD_SHARED_LIBS) - SET_TARGET_PROPERTIES(${SQPGUI_LIBRARY_NAME} PROPERTIES COMPILE_DEFINITIONS "SCIQLOP_EXPORT") -ELSE() - TARGET_COMPILE_DEFINITIONS(${SQPGUI_LIBRARY_NAME} PUBLIC "SCIQLOP_STATIC_LIBRARIES") -ENDIF() - -# Set the variable to parent scope so that the other projects can copy the -# dependent shared libraries -SCIQLOP_SET_TO_PARENT_SCOPE(SQPGUI_LIBRARY_NAME) - -# Copy extern shared libraries to the lib folder -SCIQLOP_COPY_TO_TARGET(LIBRARY ${SQPGUI_LIBRARY_NAME}) - -# Add the files to the list of files to be analyzed -LIST(APPEND CHECKSTYLE_INPUT_FILES ${MODULE_SOURCES}) -SCIQLOP_SET_TO_PARENT_SCOPE(CHECKSTYLE_INPUT_FILES) -# Vera++ exclusion files -LIST(APPEND CHECKSTYLE_EXCLUSION_FILES ${CMAKE_CURRENT_SOURCE_DIR}/vera-exclusions/exclusions.txt) -SCIQLOP_SET_TO_PARENT_SCOPE(CHECKSTYLE_EXCLUSION_FILES) - -# -# Compile the tests -# -IF(BUILD_TESTS) - INCLUDE_DIRECTORIES(${SOURCES_DIR}) - FILE (GLOB_RECURSE TESTS_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/Test*.cpp) - FILE (GLOB_RECURSE TESTS_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/Test*.h) - SET( TEST_LIBRARIES ${SQPGUI_LIBRARY_NAME}) - - FOREACH( testFile ${TESTS_SOURCES} ) - GET_FILENAME_COMPONENT( testDirectory ${testFile} DIRECTORY ) - GET_FILENAME_COMPONENT( testName ${testFile} NAME_WE ) - - # Add to the list of sources files all the sources in the same - # directory that aren't another test - FILE (GLOB currentTestSources - ${testDirectory}/*.c - ${testDirectory}/*.cpp - ${testDirectory}/*.h) - LIST (REMOVE_ITEM currentTestSources ${TESTS_SOURCES}) - # LIST (REMOVE_ITEM currentTestSources ${TESTS_HEADERS}) - - ADD_EXECUTABLE(${testName} ${testFile} ${currentTestSources}) - set_property(TARGET ${testName} PROPERTY CXX_STANDARD 14) - set_property(TARGET ${testName} PROPERTY CXX_STANDARD_REQUIRED ON) - TARGET_LINK_LIBRARIES( ${testName} ${TEST_LIBRARIES} ) - qt5_use_modules(${testName} Test) - - ADD_TEST( NAME ${testName} COMMAND ${testName} ) - - SCIQLOP_COPY_TO_TARGET(RUNTIME ${testName} ${EXTERN_SHARED_LIBRARIES}) - ENDFOREACH( testFile ) - - LIST(APPEND testFilesToFormat ${TESTS_SOURCES}) - LIST(APPEND testFilesToFormat ${TESTS_HEADERS}) - LIST(APPEND FORMATTING_INPUT_FILES ${testFilesToFormat}) - SCIQLOP_SET_TO_PARENT_SCOPE(FORMATTING_INPUT_FILES) -ENDIF(BUILD_TESTS) - -# -# Set the files that must be formatted by clang-format. -# -LIST (APPEND FORMATTING_INPUT_FILES ${MODULE_SOURCES}) -SCIQLOP_SET_TO_PARENT_SCOPE(FORMATTING_INPUT_FILES) - -# -# Set the directories that doxygen must browse to generate the -# documentation. -# -# Source directories: -LIST (APPEND DOXYGEN_INPUT_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/docs") -LIST (APPEND DOXYGEN_INPUT_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/src") -SCIQLOP_SET_TO_PARENT_SCOPE(DOXYGEN_INPUT_DIRS) -# Source directories to exclude from the documentation generation -#LIST (APPEND DOXYGEN_EXCLUDE_PATTERNS "${CMAKE_CURRENT_SOURCE_DIR}/path/to/subdir/*") -SCIQLOP_SET_TO_PARENT_SCOPE(DOXYGEN_EXCLUDE_PATTERNS) +install(TARGETS sciqlopgui EXPORT SciQLOPGuiConfig + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) -# -# Set the directories with the sources to analyze and propagate the -# modification to the parent scope -# -# Source directories to analyze: -LIST (APPEND ANALYSIS_INPUT_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/src") -LIST (APPEND ANALYSIS_INPUT_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/tests") -SCIQLOP_SET_TO_PARENT_SCOPE(ANALYSIS_INPUT_DIRS) -# Source directories to exclude from the analysis -#LIST (APPEND ANALYSIS_EXCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/path/to/subdir") -SCIQLOP_SET_TO_PARENT_SCOPE(ANALYSIS_EXCLUDE_DIRS) +install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/SciQLOP) +install(EXPORT SciQLOPGuiConfig DESTINATION share/SciQLOPGui/cmake) +export(TARGETS sciqlopgui FILE SciQLOPGuiConfig.cmake) diff --git a/gui/cmake/Findsciqlop-gui.cmake b/gui/cmake/Findsciqlop-gui.cmake deleted file mode 100755 index b0f47f7..0000000 --- a/gui/cmake/Findsciqlop-gui.cmake +++ /dev/null @@ -1,25 +0,0 @@ -# - Try to find sciqlop-gui -# Once done this will define -# SCIQLOP-GUI_FOUND - System has sciqlop-gui -# SCIQLOP-GUI_INCLUDE_DIR - The sciqlop-gui include directories -# SCIQLOP-GUI_LIBRARIES - The libraries needed to use sciqlop-gui - -if(SCIQLOP-GUI_FOUND) - return() -endif(SCIQLOP-GUI_FOUND) - -set(SCIQLOP-GUI_INCLUDE_DIR ${sciqlop-gui_DIR}/../include) - -set (OS_LIB_EXTENSION "so") - -if(WIN32) - set (OS_LIB_EXTENSION "dll") -endif(WIN32) - -if (APPLE) - set (OS_LIB_EXTENSION "dylib") -endif(APPLE) - -set(SCIQLOP-GUI_LIBRARIES ${LIBRARY_OUTPUT_PATH}/libsciqlop_gui${DEBUG_SUFFIX}.${OS_LIB_EXTENSION}) - -set(SCIQLOP-GUI_FOUND TRUE) diff --git a/gui/include/Actions/ActionsGuiController.h b/gui/include/Actions/ActionsGuiController.h index 7dab26c..16a2cf9 100644 --- a/gui/include/Actions/ActionsGuiController.h +++ b/gui/include/Actions/ActionsGuiController.h @@ -19,6 +19,14 @@ public: QVector > selectionZoneActions() const; + void removeAction(const std::shared_ptr &action); + + /// Sets a flag to say that the specified menu can be filtered, usually via a FilteringAction + void addFilterForMenu(const QStringList &menuPath); + + /// Returns true if the menu can be filtered + bool isMenuFiltered(const QStringList &menuPath) const; + private: class ActionsGuiControllerPrivate; spimpl::unique_impl_ptr impl; diff --git a/gui/include/Actions/FilteringAction.h b/gui/include/Actions/FilteringAction.h new file mode 100644 index 0000000..05eec06 --- /dev/null +++ b/gui/include/Actions/FilteringAction.h @@ -0,0 +1,19 @@ +#ifndef SCIQLOP_FILTERINGACTION_H +#define SCIQLOP_FILTERINGACTION_H + +#include +#include + +/// A LineEdit inside an action which is able to filter other actions +class FilteringAction : public QWidgetAction { +public: + FilteringAction(QWidget *parent); + + void addActionToFilter(QAction *action); + +private: + class FilteringActionPrivate; + spimpl::unique_impl_ptr impl; +}; + +#endif // SCIQLOP_FILTERINGACTION_H diff --git a/gui/include/Actions/SelectionZoneAction.h b/gui/include/Actions/SelectionZoneAction.h index 93f6b4b..22793a8 100644 --- a/gui/include/Actions/SelectionZoneAction.h +++ b/gui/include/Actions/SelectionZoneAction.h @@ -60,6 +60,13 @@ public: /// The path in the sub menus, if any QStringList subMenuList() const noexcept; + /// Sets if filtering the action is allowed via a FilteringAction + void setAllowedFiltering(bool value); + + /// Returns true if filtering the action is allowed via a FilteringAction. By default it is + /// allowed. + bool isFilteringAllowed() const; + public slots: /// Executes the action void execute(const QVector &item); diff --git a/gui/include/Catalogue/CatalogueActionManager.h b/gui/include/Catalogue/CatalogueActionManager.h index 07172db..75afa31 100644 --- a/gui/include/Catalogue/CatalogueActionManager.h +++ b/gui/include/Catalogue/CatalogueActionManager.h @@ -10,6 +10,7 @@ public: CatalogueActionManager(CatalogueExplorer *catalogueExplorer); void installSelectionZoneActions(); + void refreshCreateInCatalogueAction(); private: class CatalogueActionManagerPrivate; diff --git a/gui/include/Catalogue/CatalogueEventsModel.h b/gui/include/Catalogue/CatalogueEventsModel.h index 6251ac4..b0ba54c 100644 --- a/gui/include/Catalogue/CatalogueEventsModel.h +++ b/gui/include/Catalogue/CatalogueEventsModel.h @@ -6,6 +6,7 @@ #include #include +class DBCatalogue; class DBEvent; class DBEventProduct; @@ -22,6 +23,7 @@ public: enum class Column { Name, TStart, TEnd, Tags, Product, Validation, NbColumn }; + void setSourceCatalogues(const QVector > &catalogues); void setEvents(const QVector > &events); void addEvent(const std::shared_ptr &event); void removeEvent(const std::shared_ptr &event); diff --git a/gui/include/Catalogue/CatalogueEventsWidget.h b/gui/include/Catalogue/CatalogueEventsWidget.h index 0dee481..d9b6307 100644 --- a/gui/include/Catalogue/CatalogueEventsWidget.h +++ b/gui/include/Catalogue/CatalogueEventsWidget.h @@ -9,6 +9,7 @@ class DBCatalogue; class DBEvent; class DBEventProduct; class VisualizationWidget; +class VisualizationSelectionZoneItem; namespace Ui { class CatalogueEventsWidget; @@ -21,10 +22,15 @@ class CatalogueEventsWidget : public QWidget { signals: void eventsSelected(const QVector > &event); + void eventsRemoved(const QVector > &event); void eventProductsSelected( const QVector, std::shared_ptr > > &eventproducts); void selectionCleared(); + void selectionZoneAdded(const std::shared_ptr &event, const QString &productId, + VisualizationSelectionZoneItem *selectionZone); + + void eventCataloguesModified(const QVector > &catalogues); public: explicit CatalogueEventsWidget(QWidget *parent = 0); @@ -34,17 +40,25 @@ public: void addEvent(const std::shared_ptr &event); void setEventChanges(const std::shared_ptr &event, bool hasChanges); + void setEventsChanges(const std::shared_ptr &event, bool hasChanges); QVector > displayedCatalogues() const; bool isAllEventsDisplayed() const; bool isEventDisplayed(const std::shared_ptr &event) const; + void refreshEvent(const std::shared_ptr &event); + public slots: void populateWithCatalogues(const QVector > &catalogues); void populateWithAllEvents(); void clear(); void refresh(); + // QWidget interface +protected: + void keyPressEvent(QKeyEvent *event); + + private: Ui::CatalogueEventsWidget *ui; diff --git a/gui/include/Catalogue/CatalogueExplorer.h b/gui/include/Catalogue/CatalogueExplorer.h index 37d3ad9..079fd15 100644 --- a/gui/include/Catalogue/CatalogueExplorer.h +++ b/gui/include/Catalogue/CatalogueExplorer.h @@ -12,6 +12,10 @@ class CatalogueEventsWidget; class CatalogueSideBarWidget; class VisualizationWidget; +class VisualizationSelectionZoneItem; + +class DBEvent; + class CatalogueExplorer : public QDialog { Q_OBJECT @@ -25,6 +29,10 @@ public: CatalogueEventsWidget &eventsWidget() const; CatalogueSideBarWidget &sideBarWidget() const; + void clearSelectionZones(); + void addSelectionZoneItem(const std::shared_ptr &event, const QString &productId, + VisualizationSelectionZoneItem *selectionZone); + private: Ui::CatalogueExplorer *ui; diff --git a/gui/include/Catalogue/CatalogueInspectorWidget.h b/gui/include/Catalogue/CatalogueInspectorWidget.h index 51c42b9..1bef099 100644 --- a/gui/include/Catalogue/CatalogueInspectorWidget.h +++ b/gui/include/Catalogue/CatalogueInspectorWidget.h @@ -36,6 +36,8 @@ public: const std::shared_ptr &eventProduct); void setCatalogue(const std::shared_ptr &catalogue); + void refresh(); + public slots: void showPage(Page page); diff --git a/gui/include/Catalogue/CatalogueSideBarWidget.h b/gui/include/Catalogue/CatalogueSideBarWidget.h index 1d578a8..73acaf5 100644 --- a/gui/include/Catalogue/CatalogueSideBarWidget.h +++ b/gui/include/Catalogue/CatalogueSideBarWidget.h @@ -6,6 +6,7 @@ #include #include +class CatalogueAbstractTreeItem; class DBCatalogue; namespace Ui { @@ -23,16 +24,26 @@ signals: void allEventsSelected(); void trashSelected(); void selectionCleared(); + void catalogueSaved(const std::shared_ptr &catalogue); + void catalogueListChanged(); public: explicit CatalogueSideBarWidget(QWidget *parent = 0); virtual ~CatalogueSideBarWidget(); - void addCatalogue(const std::shared_ptr &catalogue, const QString &repository); + CatalogueAbstractTreeItem *addCatalogue(const std::shared_ptr &catalogue, + const QString &repository); void setCatalogueChanges(const std::shared_ptr &catalogue, bool hasChanges); QVector > getCatalogues(const QString &repository) const; + // QWidget interface +protected: + void keyPressEvent(QKeyEvent *event); + +private slots: + void emitSelection(); + private: Ui::CatalogueSideBarWidget *ui; diff --git a/gui/include/Catalogue/CatalogueTreeItems/CatalogueAbstractTreeItem.h b/gui/include/Catalogue/CatalogueTreeItems/CatalogueAbstractTreeItem.h index 7fc10fc..0135a5b 100644 --- a/gui/include/Catalogue/CatalogueTreeItems/CatalogueAbstractTreeItem.h +++ b/gui/include/Catalogue/CatalogueTreeItems/CatalogueAbstractTreeItem.h @@ -15,6 +15,7 @@ public: virtual ~CatalogueAbstractTreeItem(); void addChild(CatalogueAbstractTreeItem *child); + void removeChild(CatalogueAbstractTreeItem *child); QVector children() const; CatalogueAbstractTreeItem *parent() const; diff --git a/gui/include/Catalogue/CatalogueTreeItems/CatalogueTreeItem.h b/gui/include/Catalogue/CatalogueTreeItems/CatalogueTreeItem.h index 4aa26d0..d072aea 100644 --- a/gui/include/Catalogue/CatalogueTreeItems/CatalogueTreeItem.h +++ b/gui/include/Catalogue/CatalogueTreeItems/CatalogueTreeItem.h @@ -19,6 +19,7 @@ public: /// Returns the catalogue represented by the item std::shared_ptr catalogue() const; + void replaceCatalogue(const std::shared_ptr &catalogue); private: class CatalogueTreeItemPrivate; diff --git a/gui/include/Catalogue/CatalogueTreeModel.h b/gui/include/Catalogue/CatalogueTreeModel.h index ac14836..c526657 100644 --- a/gui/include/Catalogue/CatalogueTreeModel.h +++ b/gui/include/Catalogue/CatalogueTreeModel.h @@ -16,7 +16,7 @@ class CatalogueTreeModel : public QAbstractItemModel { signals: void itemRenamed(const QModelIndex &index); - void itemDropped(const QModelIndex &parentIndex); + void itemDropped(const QModelIndex &parentIndex, const QMimeData *data, Qt::DropAction action); public: CatalogueTreeModel(QObject *parent = nullptr); @@ -27,6 +27,9 @@ public: QVector topLevelItems() const; void addChildItem(CatalogueAbstractTreeItem *child, const QModelIndex &parentIndex); + void removeChildItem(CatalogueAbstractTreeItem *child, const QModelIndex &parentIndex); + /// Refresh the data for the specified index + void refresh(const QModelIndex &index); CatalogueAbstractTreeItem *item(const QModelIndex &index) const; QModelIndex indexOf(CatalogueAbstractTreeItem *item, int column = 0) const; diff --git a/gui/include/Catalogue/CreateEventDialog.h b/gui/include/Catalogue/CreateEventDialog.h deleted file mode 100644 index 9e85fa4..0000000 --- a/gui/include/Catalogue/CreateEventDialog.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef SCIQLOP_CREATEEVENTDIALOG_H -#define SCIQLOP_CREATEEVENTDIALOG_H - -#include -#include -#include - -namespace Ui { -class CreateEventDialog; -} - -class DBCatalogue; - -class CreateEventDialog : public QDialog { - Q_OBJECT - -public: - explicit CreateEventDialog(const QVector > &catalogues, - QWidget *parent = 0); - virtual ~CreateEventDialog(); - - void hideCatalogueChoice(); - - QString eventName() const; - - std::shared_ptr selectedCatalogue() const; - QString catalogueName() const; - -private: - Ui::CreateEventDialog *ui; - - class CreateEventDialogPrivate; - spimpl::unique_impl_ptr impl; -}; - -#endif // SCIQLOP_CREATEEVENTDIALOG_H diff --git a/gui/include/Visualization/AxisRenderingUtils.h b/gui/include/Visualization/AxisRenderingUtils.h index 29fc11e..dbf1709 100644 --- a/gui/include/Visualization/AxisRenderingUtils.h +++ b/gui/include/Visualization/AxisRenderingUtils.h @@ -12,6 +12,7 @@ class IDataSeries; class QCPAxis; class QCustomPlot; class SqpColorScale; +class Variable; /// Formats a data value according to the axis on which it is present QString formatValue(double value, const QCPAxis &axis); @@ -27,11 +28,17 @@ struct IAxisHelper { /// @param plot the plot for which to set axe properties /// @param colorScale the color scale for which to set properties virtual void setProperties(QCustomPlot &plot, SqpColorScale &colorScale) = 0; + + /// Set the units of the plot's axes and the color scale associated to plot passed as + /// parameters + /// @param plot the plot for which to set axe units + /// @param colorScale the color scale for which to set unit + virtual void setUnits(QCustomPlot &plot, SqpColorScale &colorScale) = 0; }; struct IAxisHelperFactory { - /// Creates IAxisHelper according to a data series - static std::unique_ptr create(std::shared_ptr dataSeries) noexcept; + /// Creates IPlottablesHelper according to the type of data series a variable holds + static std::unique_ptr create(const Variable &variable) noexcept; }; #endif // SCIQLOP_AXISRENDERINGUTILS_H diff --git a/gui/include/Visualization/PlottablesRenderingUtils.h b/gui/include/Visualization/PlottablesRenderingUtils.h index 055a490..6c958d3 100644 --- a/gui/include/Visualization/PlottablesRenderingUtils.h +++ b/gui/include/Visualization/PlottablesRenderingUtils.h @@ -1,6 +1,8 @@ #ifndef SCIQLOP_PLOTTABLESRENDERINGUTILS_H #define SCIQLOP_PLOTTABLESRENDERINGUTILS_H +#include + #include #include @@ -9,9 +11,9 @@ Q_DECLARE_LOGGING_CATEGORY(LOG_PlottablesRenderingUtils) -class IDataSeries; class QCPColorScale; class QCustomPlot; +class Variable; /** * Helper used to handle plottables rendering @@ -25,9 +27,8 @@ struct IPlottablesHelper { }; struct IPlottablesHelperFactory { - /// Creates IPlottablesHelper according to a data series - static std::unique_ptr - create(std::shared_ptr dataSeries) noexcept; + /// Creates IPlottablesHelper according to the type of data series a variable holds + static std::unique_ptr create(const Variable &variable) noexcept; }; #endif // SCIQLOP_PLOTTABLESRENDERINGUTILS_H diff --git a/gui/include/Visualization/VisualizationGraphHelper.h b/gui/include/Visualization/VisualizationGraphHelper.h index c0bf2cf..42eee9c 100644 --- a/gui/include/Visualization/VisualizationGraphHelper.h +++ b/gui/include/Visualization/VisualizationGraphHelper.h @@ -32,7 +32,7 @@ struct VisualizationGraphHelper { */ static PlottablesMap create(std::shared_ptr variable, QCustomPlot &plot) noexcept; - static void updateData(PlottablesMap &plottables, std::shared_ptr dataSeries, + static void updateData(PlottablesMap &plottables, std::shared_ptr variable, const SqpRange &dateTime); static void setYAxisRange(std::shared_ptr variable, QCustomPlot &plot) noexcept; diff --git a/gui/include/Visualization/VisualizationGraphRenderingDelegate.h b/gui/include/Visualization/VisualizationGraphRenderingDelegate.h index e59fd18..20fb5a4 100644 --- a/gui/include/Visualization/VisualizationGraphRenderingDelegate.h +++ b/gui/include/Visualization/VisualizationGraphRenderingDelegate.h @@ -9,6 +9,7 @@ class IDataSeries; class QCustomPlot; class QMouseEvent; class Unit; +class Variable; class VisualizationGraphWidget; class VisualizationGraphRenderingDelegate { @@ -23,13 +24,14 @@ public: /// Updates rendering when data of plot changed void onPlotUpdated() noexcept; - /// Sets properties of the plot's axes from the data series passed as parameter - void setAxesProperties(std::shared_ptr dataSeries) noexcept; + /// Sets units of the plot's axes according to the properties of the variable passed as + /// parameter + void setAxesUnits(const Variable &variable) noexcept; - /// Sets rendering properties of the plottables passed as parameter, from the data series that + /// Sets graph properties of the plottables passed as parameter, from the variable that /// generated these - void setPlottablesProperties(std::shared_ptr dataSeries, - PlottablesMap &plottables) noexcept; + void setGraphProperties(const Variable &variable, PlottablesMap &plottables) noexcept; + /// Shows or hides graph overlay (name, close button, etc.) void showGraphOverlay(bool show) noexcept; diff --git a/gui/include/Visualization/VisualizationGraphWidget.h b/gui/include/Visualization/VisualizationGraphWidget.h index 8f97ea9..c48e045 100644 --- a/gui/include/Visualization/VisualizationGraphWidget.h +++ b/gui/include/Visualization/VisualizationGraphWidget.h @@ -25,6 +25,20 @@ namespace Ui { class VisualizationGraphWidget; } // namespace Ui +/// Defines options that can be associated with the graph +enum GraphFlag { + DisableAll = 0x0, ///< Disables acquisition and synchronization + EnableAcquisition = 0x1, ///< When this flag is set, the change of the graph's range leads to + /// the acquisition of data + EnableSynchronization = 0x2, ///< When this flag is set, the change of the graph's range causes + /// the call to the synchronization of the graphs contained in the + /// same zone of this graph + EnableAll = ~DisableAll ///< Enables acquisition and synchronization +}; + +Q_DECLARE_FLAGS(GraphFlags, GraphFlag) +Q_DECLARE_OPERATORS_FOR_FLAGS(GraphFlags) + class VisualizationGraphWidget : public VisualizationDragWidget, public IVisualizationWidget { Q_OBJECT @@ -41,8 +55,8 @@ public: /// Returns the main VisualizationWidget which contains the graph or nullptr VisualizationWidget *parentVisualizationWidget() const; - /// If acquisition isn't enable, requestDataLoading signal cannot be emit - void enableAcquisition(bool enable); + /// Sets graph options + void setFlags(GraphFlags flags); void addVariable(std::shared_ptr variable, SqpRange range); @@ -55,13 +69,16 @@ public: /// Sets the y-axis range based on the data of a variable void setYRange(std::shared_ptr variable); SqpRange graphRange() const noexcept; - void setGraphRange(const SqpRange &range); + void setGraphRange(const SqpRange &range, bool calibration = false); + void setAutoRangeOnVariableInitialization(bool value); // Zones /// Returns the ranges of all the selection zones on the graph QVector selectionZoneRanges() const; /// Adds new selection zones in the graph void addSelectionZones(const QVector &ranges); + /// Adds a new selection zone in the graph + VisualizationSelectionZoneItem *addSelectionZone(const QString &name, const SqpRange &range); /// Removes the specified selection zone void removeSelectionZone(VisualizationSelectionZoneItem *selectionZone); diff --git a/gui/include/Visualization/VisualizationSelectionZoneItem.h b/gui/include/Visualization/VisualizationSelectionZoneItem.h index d7b6e58..675981e 100644 --- a/gui/include/Visualization/VisualizationSelectionZoneItem.h +++ b/gui/include/Visualization/VisualizationSelectionZoneItem.h @@ -8,6 +8,11 @@ class VisualizationGraphWidget; class VisualizationSelectionZoneItem : public QCPItemRect { + Q_OBJECT + +signals: + /// Signal emitted when the zone range is edited manually + void rangeEdited(const SqpRange &range); public: VisualizationSelectionZoneItem(QCustomPlot *plot); diff --git a/gui/include/Visualization/VisualizationZoneWidget.h b/gui/include/Visualization/VisualizationZoneWidget.h index daa2525..a54e1ef 100644 --- a/gui/include/Visualization/VisualizationZoneWidget.h +++ b/gui/include/Visualization/VisualizationZoneWidget.h @@ -68,6 +68,9 @@ public: /// Returns the first graph in the zone or nullptr if there is no graph inside VisualizationGraphWidget *firstGraph() const; + /// Closes all graphes inside the zone + void closeAllGraphs(); + // IVisualizationWidget interface void accept(IVisualizationWidgetVisitor *visitor) override; bool canDrop(const Variable &variable) const override; diff --git a/gui/meson.build b/gui/meson.build index add3dca..e1c2c62 100644 --- a/gui/meson.build +++ b/gui/meson.build @@ -1,5 +1,3 @@ -qxorm_dep = dependency('QxOrm', required : true, fallback:['QxOrm','qxorm_dep']) -catalogueapi_dep = dependency('CatalogueAPI', required : true, fallback:['CatalogueAPI','CatalogueAPI_dep']) gui_moc_headers = [ 'include/DataSource/DataSourceWidget.h', @@ -20,6 +18,7 @@ gui_moc_headers = [ 'include/Visualization/VisualizationDragDropContainer.h', 'include/Visualization/VisualizationDragWidget.h', 'include/Visualization/ColorScaleEditor.h', + 'include/Visualization/VisualizationSelectionZoneItem.h', 'include/Actions/SelectionZoneAction.h', 'include/Visualization/VisualizationMultiZoneSelectionDialog.h', 'include/Catalogue/CatalogueExplorer.h', @@ -27,8 +26,8 @@ gui_moc_headers = [ 'include/Catalogue/CatalogueSideBarWidget.h', 'include/Catalogue/CatalogueInspectorWidget.h', 'include/Catalogue/CatalogueEventsModel.h', - 'include/Catalogue/CreateEventDialog.h', - 'include/Catalogue/CatalogueTreeModel.h' + 'include/Catalogue/CatalogueTreeModel.h', + 'include/Actions/FilteringAction.h' ] gui_ui_files = [ @@ -49,8 +48,7 @@ gui_ui_files = [ 'ui/Catalogue/CatalogueExplorer.ui', 'ui/Catalogue/CatalogueEventsWidget.ui', 'ui/Catalogue/CatalogueSideBarWidget.ui', - 'ui/Catalogue/CatalogueInspectorWidget.ui', - 'ui/Catalogue/CreateEventDialog.ui' + 'ui/Catalogue/CatalogueInspectorWidget.ui' ] gui_qresources = ['resources/sqpguiresources.qrc'] @@ -113,6 +111,7 @@ gui_sources = [ 'src/Visualization/VisualizationSelectionZoneManager.cpp', 'src/Actions/SelectionZoneAction.cpp', 'src/Actions/ActionsGuiController.cpp', + 'src/Actions/FilteringAction.cpp', 'src/Visualization/VisualizationActionManager.cpp', 'src/Visualization/VisualizationMultiZoneSelectionDialog.cpp', 'src/Catalogue/CatalogueExplorer.cpp', @@ -125,7 +124,6 @@ gui_sources = [ 'src/Catalogue/CatalogueEventsModel.cpp', 'src/Catalogue/CatalogueExplorerHelper.cpp', 'src/Catalogue/CatalogueActionManager.cpp', - 'src/Catalogue/CreateEventDialog.cpp', 'src/Catalogue/CatalogueTreeModel.cpp' ] @@ -136,12 +134,12 @@ sciqlop_gui_lib = library('sciqlopgui', gui_moc_files, rcc_files, include_directories : [gui_inc], - dependencies : [ qt5printsupport, qt5gui, qt5widgets, qt5svg, sciqlop_core, catalogueapi_dep], + dependencies : [ qt5printsupport, qt5gui, qt5widgets, qt5svg, sciqlop_core], install : true ) sciqlop_gui = declare_dependency(link_with : sciqlop_gui_lib, include_directories : gui_inc, - dependencies : [qt5printsupport, qt5gui, qt5widgets, qt5svg, sciqlop_core, catalogueapi_dep]) + dependencies : [qt5printsupport, qt5gui, qt5widgets, qt5svg, sciqlop_core]) diff --git a/gui/resources/icones/discard.png b/gui/resources/icones/discard.png old mode 100644 new mode 100755 index bfc6b097e9fa0c1093924faeca285fd4cf9c262d..b6fc4f32cb0f7b07c0b391e8d912072f1a2b9099 GIT binary patch delta 1210 zc$@*c1V#JE4)O_*NPh&BNklS_S_Bsdy2JMLY`<5kbL&KQ09YtB4grTQz7^YLr;PlvI*N56^^b)68af zXP$YUoq50DOSZJ#KKndhI-h-JW)~z$k|arzBuSDaNs=TJKnL`OJsY57wuU_$ zphLC`dp1Bvl!ZMTpyhT7dp1Ce4HEWjfR-9G?AZVIi!_z?iBl?AZWgrtYw31B{UvVb2B_6S2ac4KM~`hCLe~f{7ZR zYd`P`a8ZrVIjFUsI@j~~4De4;%RP@z1HTuw*z zIE3}qcffD7@PC}D4KM?|1>9f5SaSCV@Hy~B&(FoABY#S)>9Ch${RkWe9s#DSP3L$? z5%zMgRp1El0&v^tQ=7loJ9}-Z&w!_aYe$>r{4}+**S1;(-Xk0)CWoH&Ofz+=>Vo@OA1y}W83xVrC()yS}yXPpG@?XzyRFzn@B>%ajb9TLWNXR1Rc zfR}(nM7-8wj&PAg>Tb9C*So;wC2LsOEN~)&7ismcqri0~YgpL~@KFXY(&}F)fa^=v zurfD-#-olA`E8=_4)U*0fh)=uunva9{Ody^pMPF99OhqdGvZtNLEcv`0dD|zllVl1 zO5H)YAL2;Nf5dQ@cdY{tmabln42SvF60oOy>FQ%R%(sp*L@2Csf?<|$wd<`Fo+Agp z0zVV(@>wL@0kjDGNjNuu7Fa2Mo-O{)R8f}{Kc@-1bG@~PFh^H5^g4PS2A&+`57u#O z*nd13UCvZu1^BwCF^Efl{O%LK3+xSbHx?gZ{BejVE!VOKZ0+`{29;f22l>?2He ziaVD3xdwQMFvc&5GY<>EWlg;xNEZ%&0p29sg>x5~USYd~n+SJTzYqLNeRK12lP?0& zgu}L(Cfq#o3DxK39AW>aQBCh0w%IJm6H zg;?QGAtO9X*aPV|SFfcS;D#9CP@8ThTsd4eS8HVO@il&7L|hZVUcxz!?dEGf-G9)| z=U2wWH4VJZh4ZyeMkny<2#4x#FX1|Y-Z?vzbVOTJ4Tsvey!7N4V(aB$=K$^~vN|Gc=k|arzBuSDaNs=T@~ delta 1813 zc$@(h2kQ9p3CIqRNPhS*TT29L9h9oI5p{re>BH6@_6`HmS{{`C1P> z1VIEr5J3=GmN^v#LJ>AkYDI-%^Q;g=5Slq;+9Zusq*;2cw3o7neHY$)Ji|WwthN4Y z`rv!;D%|he-_Lu`Is5E=Kvq`*&jDS+IV6+;^MJX+ImC1o@P99`2Utp65UBC@40x_+ z8UbAm{Kwt{5EBHo<15dR3}sW-uK+tIXXdt+j#^pnYpw;-nLvS6f%ZCa<(eagM z%fvLN>nUD7Gzi)qKR5_(p#AcpK#&3RDc|3F2yUc&dEX)EbbRGIYzNn+o9Mi}uMp@O z-&+V~ps&1d5P#?%UwM|z5}jx!`pf$QfvNGmfnXNq%DWDM>G7S1U=HTXy9$A=@s)44 zQ>&QDKFkf(*FVKnY(UaBY0$nI=msWx)LcK^1t$fzVPAgf_nNi~`;1BEM8iI;<1~p^je&f-c|%2SQ3g5c>E{A?OBP zbRe7*1b?xNuYB{M?sNmo90(-^L2Tm>fuK4t{^i7{iNFM45<;g`g>?zAs|)x}I*FjJ zkPaZM%c{T%U>Gn}I82gVtpui52mY{}1VKz%1xyEaRtJ4?IdCvAMI=&^Ev*7B1^OX> z5I9%~g0S^5gT{w_0e~EW;Uow`(`stQhxz~jynjc6ASA5@rU5%@|Dq89kRS*}pU_}@ zXbu465R3pOYYvrUM{8&@KC}h^5(K_#4f(5m+nfEPA^<=R!676Fe9>A8j1R>D0C<-K zfe%_o!STx|10F#BOjdGzuOSRCri&R+g1`;0M__!V z41d2a84Jyy0;5{4WLOD;h{spTfQOMkD6x104vnvrAh0+7=z^PASAxK-Kf~eil`13% zOn4)3Xd63-sscQM{6P!FUX8DmAkgDYcsRb2uXh)5#hZXJZ69^VlprYQ&k10Br3&yE z@(0xye*qj;>PS1R1VO~(D^-BUkw3_;`hQDcZ0V!xi4p`6kFVtW?dNp+D4V>-AzRLXgIH(^eozn!J>))w8L+^CP`1Zc ziVcFgx1!5{`y2>o+P?!Q)s`oR;AIE$nRe3~Qf0tG2SS=0|KvvH2_J&5DR4uoP(K6c z5AsL8t<*!95NrmHD9|4W?*`9B$5-kBUU49|E`I}zYokAqL-3>n;dP9EN`JfM2?v62 zf$=5!133hX9Vk)O+bK7`QV;N|13k+6d*H|p`UCl!37&PJOquad?X*1pLhu7{l!pF5 z4#D#dlxk=E({z=`KL~yVj@H;8fG%LE1LazI2YSa>>H%JJpkFKh1RP_aKaf99Ugki_ zM(zhrH&q_@A@~_M)>!)ib${dLqvL)CPHq@h5AeD`zgGSQIL=`2A#&{?^aGQPHXbV1 z`u_?{Fxt3|dV#k(46egFfhmT&)>AKTK(GZk-f-83>LWj&9^3dzy}%oFa@q1$V50Gs zEwmr70-5orTIknGQV{$GoM53}A?**mkG=AL0%uxjR!qIXo7n4L`+t50Cv*Vt0r8Em zWD9~;r?f?NAn+mP%KrtNWvxy7G7Ul56I)hw5bzOZ#y{I)kFqlL>`2$S4J#T3tVHkl z=U8pfnRG+21GBGi8qsiIHG0NB*K&3p>O&6%wqNAZsu93i%9sBeKgU<<1Ky^5f9cmb zv}h!-o^s=#Cy-aS%6~%O>a(3v8U<{iz46bN2x>@uz&o_}YtM%VylrVTu#wi~cM;b3 zN<|^?{nuVojUm4(rP=rwh@~^HRuF^={}7EOzdEJ1{BGbv!Cbb~=mc6U{ek=fh0P2t zf01Y+yQ+m?H*m3NA_0vfzhEKC^6Z7+A7GkPi2ng4RV#XAR1Va+00000NkvXXu0mjf D|E)hp diff --git a/gui/src/Actions/ActionsGuiController.cpp b/gui/src/Actions/ActionsGuiController.cpp index b69e6c3..1f9c465 100644 --- a/gui/src/Actions/ActionsGuiController.cpp +++ b/gui/src/Actions/ActionsGuiController.cpp @@ -3,6 +3,7 @@ struct ActionsGuiController::ActionsGuiControllerPrivate { QVector > m_SelectionZoneActions; + QSet m_FilteredMenu; }; ActionsGuiController::ActionsGuiController() @@ -34,3 +35,18 @@ QVector > ActionsGuiController::selectionZo { return impl->m_SelectionZoneActions; } + +void ActionsGuiController::removeAction(const std::shared_ptr &action) +{ + impl->m_SelectionZoneActions.removeAll(action); +} + +void ActionsGuiController::addFilterForMenu(const QStringList &menuPath) +{ + impl->m_FilteredMenu.insert(menuPath); +} + +bool ActionsGuiController::isMenuFiltered(const QStringList &menuPath) const +{ + return impl->m_FilteredMenu.contains(menuPath); +} diff --git a/gui/src/Actions/FilteringAction.cpp b/gui/src/Actions/FilteringAction.cpp new file mode 100644 index 0000000..e28b567 --- /dev/null +++ b/gui/src/Actions/FilteringAction.cpp @@ -0,0 +1,27 @@ +#include "Actions/FilteringAction.h" + +#include + +struct FilteringAction::FilteringActionPrivate { + QLineEdit *m_FilterLineEdit; + QVector m_FilteredActions; +}; + +FilteringAction::FilteringAction(QWidget *parent) + : QWidgetAction(parent), impl{spimpl::make_unique_impl()} +{ + impl->m_FilterLineEdit = new QLineEdit(parent); + setDefaultWidget(impl->m_FilterLineEdit); + + connect(impl->m_FilterLineEdit, &QLineEdit::textEdited, [this](auto text) { + for (auto action : impl->m_FilteredActions) { + auto match = action->text().contains(text, Qt::CaseInsensitive); + action->setVisible(match); + } + }); +} + +void FilteringAction::addActionToFilter(QAction *action) +{ + impl->m_FilteredActions << action; +} diff --git a/gui/src/Actions/SelectionZoneAction.cpp b/gui/src/Actions/SelectionZoneAction.cpp index 0fcae06..da183b7 100644 --- a/gui/src/Actions/SelectionZoneAction.cpp +++ b/gui/src/Actions/SelectionZoneAction.cpp @@ -15,6 +15,7 @@ struct SelectionZoneAction::SelectionZoneActionPrivate { QKeySequence m_DisplayedShortcut; SelectionZoneAction::ExecuteFunction m_Fun; SelectionZoneAction::EnableFunction m_EnableFun = [](auto zones) { return true; }; + bool m_FilteringAllowed = true; }; SelectionZoneAction::SelectionZoneAction(const QString &name, ExecuteFunction fun) @@ -55,6 +56,16 @@ QStringList SelectionZoneAction::subMenuList() const noexcept return impl->m_SubMenuList; } +void SelectionZoneAction::setAllowedFiltering(bool value) +{ + impl->m_FilteringAllowed = value; +} + +bool SelectionZoneAction::isFilteringAllowed() const +{ + return impl->m_FilteringAllowed; +} + void SelectionZoneAction::execute(const QVector &item) { impl->m_Fun(item); diff --git a/gui/src/Catalogue/CatalogueActionManager.cpp b/gui/src/Catalogue/CatalogueActionManager.cpp index 685b679..10f3c8b 100644 --- a/gui/src/Catalogue/CatalogueActionManager.cpp +++ b/gui/src/Catalogue/CatalogueActionManager.cpp @@ -11,7 +11,6 @@ #include #include #include -#include #include #include @@ -25,9 +24,16 @@ #include #include +const auto CATALOGUE_MENU_NAME = QObject::tr("Catalogues"); +const auto CATALOGUE_CREATE_EVENT_MENU_NAME = QObject::tr("New Event..."); + +const auto DEFAULT_EVENT_NAME = QObject::tr("Event"); +const auto DEFAULT_CATALOGUE_NAME = QObject::tr("Catalogue"); + struct CatalogueActionManager::CatalogueActionManagerPrivate { CatalogueExplorer *m_CatalogueExplorer = nullptr; + QVector > m_CreateInCatalogueActions; CatalogueActionManagerPrivate(CatalogueExplorer *catalogueExplorer) : m_CatalogueExplorer(catalogueExplorer) @@ -48,12 +54,14 @@ struct CatalogueActionManager::CatalogueActionManagerPrivate { auto eventProduct = std::make_shared(); eventProduct->setEvent(*event); + auto productId + = var->metadata().value(DataSourceItem::ID_DATA_KEY, "UnknownID").toString(); + auto zoneRange = zone->range(); eventProduct->setTStart(zoneRange.m_TStart); eventProduct->setTEnd(zoneRange.m_TEnd); - eventProduct->setProductId( - var->metadata().value(DataSourceItem::ID_DATA_KEY, "UnknownID").toString()); + eventProduct->setProductId(productId); productList.push_back(*eventProduct); } @@ -65,8 +73,8 @@ struct CatalogueActionManager::CatalogueActionManagerPrivate { if (catalogue) { - // TODO - // catalogue->addEvent(event); + catalogue->addEvent(event->getUniqId()); + sqpApp->catalogueController().updateCatalogue(catalogue); m_CatalogueExplorer->sideBarWidget().setCatalogueChanges(catalogue, true); if (m_CatalogueExplorer->eventsWidget().displayedCatalogues().contains(catalogue)) { m_CatalogueExplorer->eventsWidget().addEvent(event); @@ -78,6 +86,32 @@ struct CatalogueActionManager::CatalogueActionManagerPrivate { m_CatalogueExplorer->eventsWidget().setEventChanges(event, true); } } + + SelectionZoneAction::EnableFunction createEventEnableFuntion() const + { + return [](auto zones) { + + // Checks that all variables in the zones doesn't refer to the same product + QSet usedDatasource; + for (auto zone : zones) { + auto graph = zone->parentGraphWidget(); + auto variables = graph->variables(); + + for (auto var : variables) { + auto datasourceId + = var->metadata().value(DataSourceItem::ID_DATA_KEY).toString(); + if (!usedDatasource.contains(datasourceId)) { + usedDatasource.insert(datasourceId); + } + else { + return false; + } + } + } + + return true; + }; + } }; CatalogueActionManager::CatalogueActionManager(CatalogueExplorer *catalogueExplorer) @@ -89,48 +123,52 @@ void CatalogueActionManager::installSelectionZoneActions() { auto &actionController = sqpApp->actionsGuiController(); - auto createEventEnableFuntion = [](auto zones) { - QSet usedGraphs; - for (auto zone : zones) { - auto graph = zone->parentGraphWidget(); - if (!usedGraphs.contains(graph)) { - usedGraphs.insert(graph); - } - else { - return false; - } - } - - return true; - }; - auto createEventAction = actionController.addSectionZoneAction( - {QObject::tr("Catalogues")}, QObject::tr("New Event..."), [this](auto zones) { - CreateEventDialog dialog( - impl->m_CatalogueExplorer->sideBarWidget().getCatalogues(REPOSITORY_DEFAULT)); - dialog.hideCatalogueChoice(); - if (dialog.exec() == QDialog::Accepted) { - impl->createEventFromZones(dialog.eventName(), zones); - } + {CATALOGUE_MENU_NAME, CATALOGUE_CREATE_EVENT_MENU_NAME}, QObject::tr("Without Catalogue"), + [this](auto zones) { impl->createEventFromZones(DEFAULT_EVENT_NAME, zones); }); + createEventAction->setEnableFunction(impl->createEventEnableFuntion()); + createEventAction->setAllowedFiltering(false); + + auto createEventInNewCatalogueAction = actionController.addSectionZoneAction( + {CATALOGUE_MENU_NAME, CATALOGUE_CREATE_EVENT_MENU_NAME}, QObject::tr("In New Catalogue"), + [this](auto zones) { + + auto newCatalogue = std::make_shared(); + newCatalogue->setName(DEFAULT_CATALOGUE_NAME); + sqpApp->catalogueController().addCatalogue(newCatalogue); + impl->m_CatalogueExplorer->sideBarWidget().addCatalogue(newCatalogue, + REPOSITORY_DEFAULT); + + impl->createEventFromZones(DEFAULT_EVENT_NAME, zones, newCatalogue); }); - createEventAction->setEnableFunction(createEventEnableFuntion); - - auto createEventInCatalogueAction = actionController.addSectionZoneAction( - {QObject::tr("Catalogues")}, QObject::tr("New Event in Catalogue..."), [this](auto zones) { - CreateEventDialog dialog( - impl->m_CatalogueExplorer->sideBarWidget().getCatalogues(REPOSITORY_DEFAULT)); - if (dialog.exec() == QDialog::Accepted) { - auto selectedCatalogue = dialog.selectedCatalogue(); - if (!selectedCatalogue) { - selectedCatalogue = std::make_shared(); - selectedCatalogue->setName(dialog.catalogueName()); - // sqpApp->catalogueController().addCatalogue(selectedCatalogue); TODO - impl->m_CatalogueExplorer->sideBarWidget().addCatalogue(selectedCatalogue, - REPOSITORY_DEFAULT); - } + createEventInNewCatalogueAction->setEnableFunction(impl->createEventEnableFuntion()); + createEventInNewCatalogueAction->setAllowedFiltering(false); - impl->createEventFromZones(dialog.eventName(), zones, selectedCatalogue); - } - }); - createEventInCatalogueAction->setEnableFunction(createEventEnableFuntion); + refreshCreateInCatalogueAction(); + + actionController.addFilterForMenu({CATALOGUE_MENU_NAME, CATALOGUE_CREATE_EVENT_MENU_NAME}); +} + +void CatalogueActionManager::refreshCreateInCatalogueAction() +{ + auto &actionController = sqpApp->actionsGuiController(); + + for (auto action : impl->m_CreateInCatalogueActions) { + actionController.removeAction(action); + } + impl->m_CreateInCatalogueActions.clear(); + + auto allCatalogues + = impl->m_CatalogueExplorer->sideBarWidget().getCatalogues(REPOSITORY_DEFAULT); + + for (auto catalogue : allCatalogues) { + auto catalogueName = catalogue->getName(); + auto createEventInCatalogueAction = actionController.addSectionZoneAction( + {CATALOGUE_MENU_NAME, CATALOGUE_CREATE_EVENT_MENU_NAME}, + QObject::tr("In \"").append(catalogueName).append("\""), [this, catalogue](auto zones) { + impl->createEventFromZones(DEFAULT_EVENT_NAME, zones, catalogue); + }); + createEventInCatalogueAction->setEnableFunction(impl->createEventEnableFuntion()); + impl->m_CreateInCatalogueActions << createEventInCatalogueAction; + } } diff --git a/gui/src/Catalogue/CatalogueEventsModel.cpp b/gui/src/Catalogue/CatalogueEventsModel.cpp index 9a79328..baae51d 100644 --- a/gui/src/Catalogue/CatalogueEventsModel.cpp +++ b/gui/src/Catalogue/CatalogueEventsModel.cpp @@ -24,6 +24,7 @@ const auto EVENT_PRODUCT_ITEM_TYPE = 2; struct CatalogueEventsModel::CatalogueEventsModelPrivate { QVector > m_Events; std::unordered_map > > m_EventProducts; + QVector > m_SourceCatalogue; QStringList columnNames() { @@ -47,13 +48,23 @@ struct CatalogueEventsModel::CatalogueEventsModelPrivate { case CatalogueEventsModel::Column::Name: return event->getName(); case CatalogueEventsModel::Column::TStart: - return nbEventProducts(event) > 0 ? DateUtils::dateTime(event->getTStart()) - : QVariant{}; + return nbEventProducts(event) > 0 + ? DateUtils::dateTime(event->getTStart()) + .toString(DATETIME_FORMAT_ONE_LINE) + : QVariant{}; case CatalogueEventsModel::Column::TEnd: - return nbEventProducts(event) > 0 ? DateUtils::dateTime(event->getTEnd()) - : QVariant{}; - case CatalogueEventsModel::Column::Product: - return QString::number(nbEventProducts(event)) + " product(s)"; + return nbEventProducts(event) > 0 + ? DateUtils::dateTime(event->getTEnd()) + .toString(DATETIME_FORMAT_ONE_LINE) + : QVariant{}; + case CatalogueEventsModel::Column::Product: { + auto eventProducts = event->getEventProducts(); + QStringList eventProductList; + for (auto evtProduct : eventProducts) { + eventProductList << evtProduct.getProductId(); + } + return eventProductList.join(";"); + } case CatalogueEventsModel::Column::Tags: { QString tagList; auto tags = event->getTags(); @@ -98,9 +109,11 @@ struct CatalogueEventsModel::CatalogueEventsModelPrivate { case CatalogueEventsModel::Column::Name: return eventProduct->getProductId(); case CatalogueEventsModel::Column::TStart: - return DateUtils::dateTime(eventProduct->getTStart()); + return DateUtils::dateTime(eventProduct->getTStart()) + .toString(DATETIME_FORMAT_ONE_LINE); case CatalogueEventsModel::Column::TEnd: - return DateUtils::dateTime(eventProduct->getTEnd()); + return DateUtils::dateTime(eventProduct->getTEnd()) + .toString(DATETIME_FORMAT_ONE_LINE); case CatalogueEventsModel::Column::Product: return eventProduct->getProductId(); case CatalogueEventsModel::Column::Tags: @@ -129,6 +142,12 @@ CatalogueEventsModel::CatalogueEventsModel(QObject *parent) { } +void CatalogueEventsModel::setSourceCatalogues( + const QVector > &catalogues) +{ + impl->m_SourceCatalogue = catalogues; +} + void CatalogueEventsModel::setEvents(const QVector > &events) { beginResetModel(); @@ -388,12 +407,12 @@ void CatalogueEventsModel::sort(int column, Qt::SortOrder order) Qt::DropActions CatalogueEventsModel::supportedDragActions() const { - return Qt::CopyAction; + return Qt::CopyAction | Qt::MoveAction; } QStringList CatalogueEventsModel::mimeTypes() const { - return {MIME_TYPE_EVENT_LIST, MIME_TYPE_TIME_RANGE}; + return {MIME_TYPE_EVENT_LIST, MIME_TYPE_SOURCE_CATALOGUE_LIST, MIME_TYPE_TIME_RANGE}; } QMimeData *CatalogueEventsModel::mimeData(const QModelIndexList &indexes) const @@ -436,6 +455,10 @@ QMimeData *CatalogueEventsModel::mimeData(const QModelIndexList &indexes) const if (!eventList.isEmpty() && eventProductList.isEmpty()) { auto eventsEncodedData = sqpApp->catalogueController().mimeDataForEvents(eventList); mimeData->setData(MIME_TYPE_EVENT_LIST, eventsEncodedData); + + auto sourceCataloguesEncodedData + = sqpApp->catalogueController().mimeDataForCatalogues(impl->m_SourceCatalogue); + mimeData->setData(MIME_TYPE_SOURCE_CATALOGUE_LIST, sourceCataloguesEncodedData); } if (eventList.count() + eventProductList.count() == 1) { diff --git a/gui/src/Catalogue/CatalogueEventsWidget.cpp b/gui/src/Catalogue/CatalogueEventsWidget.cpp index 0493aa5..46c553d 100644 --- a/gui/src/Catalogue/CatalogueEventsWidget.cpp +++ b/gui/src/Catalogue/CatalogueEventsWidget.cpp @@ -6,20 +6,31 @@ #include #include #include +#include +#include +#include #include +#include +#include +#include #include #include #include +#include #include #include +#include #include +#include #include Q_LOGGING_CATEGORY(LOG_CatalogueEventsWidget, "CatalogueEventsWidget") -/// Fixed size of the validation column -const auto VALIDATION_COLUMN_SIZE = 35; +/// Percentage added to the range of a event when it is displayed +const auto EVENT_RANGE_MARGE = 30; // in % + +const QString NEW_ZONE_TEXT = QStringLiteral("New Zone"); struct CatalogueEventsWidget::CatalogueEventsWidgetPrivate { @@ -27,12 +38,15 @@ struct CatalogueEventsWidget::CatalogueEventsWidgetPrivate { QStringList m_ZonesForTimeMode; QString m_ZoneForGraphMode; QVector > m_DisplayedCatalogues; + bool m_AllEventDisplayed = false; + QVector m_CustomGraphs; VisualizationWidget *m_VisualizationWidget = nullptr; void setEvents(const QVector > &events, CatalogueEventsWidget *widget) { widget->ui->treeView->setSortingEnabled(false); + m_Model->setSourceCatalogues(m_DisplayedCatalogues); m_Model->setEvents(events); widget->ui->treeView->setSortingEnabled(true); @@ -70,72 +84,46 @@ struct CatalogueEventsWidget::CatalogueEventsWidgetPrivate { } QStringList selectZone(QWidget *parent, const QStringList &selectedZones, - bool allowMultiSelection, const QPoint &location) + bool allowMultiSelection, bool addNewZoneOption, const QPoint &location) { auto availableZones = getAvailableVisualizationZoneList(); - if (availableZones.isEmpty()) { + if (!addNewZoneOption && availableZones.isEmpty()) { return QStringList{}; } - QDialog d(parent, Qt::Tool); - d.setWindowTitle("Choose a zone"); - auto layout = new QVBoxLayout{&d}; - layout->setContentsMargins(0, 0, 0, 0); - auto listWidget = new QListWidget{&d}; - layout->addWidget(listWidget); + QActionGroup actionGroup{parent}; + actionGroup.setExclusive(!allowMultiSelection); - QSet checkedItems; - for (auto zone : availableZones) { - auto item = new QListWidgetItem{zone}; - item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable); - if (selectedZones.contains(zone)) { - item->setCheckState(Qt::Checked); - checkedItems << item; - } - else { - item->setCheckState(Qt::Unchecked); - } + QVector zoneActions; + + QMenu selectionMenu{parent}; - listWidget->addItem(item); + if (addNewZoneOption) { + availableZones.prepend(NEW_ZONE_TEXT); } - auto buttonBox = new QDialogButtonBox{QDialogButtonBox::Ok, &d}; - layout->addWidget(buttonBox); - - QObject::connect(buttonBox, &QDialogButtonBox::accepted, &d, &QDialog::accept); - QObject::connect(buttonBox, &QDialogButtonBox::rejected, &d, &QDialog::reject); - - QObject::connect(listWidget, &QListWidget::itemChanged, - [&checkedItems, allowMultiSelection, listWidget](auto item) { - if (item->checkState() == Qt::Checked) { - if (!allowMultiSelection) { - for (auto checkedItem : checkedItems) { - listWidget->blockSignals(true); - checkedItem->setCheckState(Qt::Unchecked); - listWidget->blockSignals(false); - } - - checkedItems.clear(); - } - checkedItems << item; - } - else { - checkedItems.remove(item); - } - }); + selectionMenu.addSeparator(); + for (auto zone : availableZones) { + auto zoneAction = selectionMenu.addAction(zone); + zoneAction->setCheckable(true); + zoneAction->setChecked(selectedZones.contains(zone)); + actionGroup.addAction(zoneAction); + zoneActions << zoneAction; + } + + auto resultAction = selectionMenu.exec(QCursor::pos()); QStringList result; - d.setMinimumWidth(120); - d.resize(d.minimumSizeHint()); - d.move(location); - if (d.exec() == QDialog::Accepted) { - for (auto item : checkedItems) { - result += item->text(); - } + if (resultAction == nullptr) { + result = selectedZones; } else { - result = selectedZones; + for (auto zoneAction : zoneActions) { + if (zoneAction->isChecked()) { + result << zoneAction->text(); + } + } } return result; @@ -177,31 +165,146 @@ struct CatalogueEventsWidget::CatalogueEventsWidgetPrivate { } } - void updateForGraphMode(QTreeView *treeView) + QVector getGraphRanges(const std::shared_ptr &event) { - auto selectedRows = treeView->selectionModel()->selectedRows(); - - if (selectedRows.count() == 1) { - auto event = m_Model->getEvent(selectedRows.first()); - if (m_VisualizationWidget) { - if (auto tab = m_VisualizationWidget->currentTabWidget()) { - if (auto zone = tab->getZoneWithName(m_ZoneForGraphMode)) { - // TODO - } - } - else { - qCWarning(LOG_CatalogueEventsWidget()) - << "updateGraphMode: no tab found in the visualization"; - } - } - else { - qCWarning(LOG_CatalogueEventsWidget()) - << "updateGraphMode: visualization widget not found"; + // Retrieves the range of each product and the maximum size + QVector graphRanges; + double maxDt = 0; + for (auto eventProduct : event->getEventProducts()) { + SqpRange eventRange; + eventRange.m_TStart = eventProduct.getTStart(); + eventRange.m_TEnd = eventProduct.getTEnd(); + graphRanges << eventRange; + + auto dt = eventRange.m_TEnd - eventRange.m_TStart; + if (dt > maxDt) { + maxDt = dt; } } - else { + + // Adds the marge + maxDt *= (100.0 + EVENT_RANGE_MARGE) / 100.0; + + // Corrects the graph ranges so that they all have the same size + QVector correctedGraphRanges; + for (auto range : graphRanges) { + auto dt = range.m_TEnd - range.m_TStart; + auto diff = qAbs((maxDt - dt) / 2.0); + + SqpRange correctedRange; + correctedRange.m_TStart = range.m_TStart - diff; + correctedRange.m_TEnd = range.m_TEnd + diff; + + correctedGraphRanges << correctedRange; + } + + return correctedGraphRanges; + } + + void updateForGraphMode(CatalogueEventsWidget *catalogueEventWidget) + { + auto selectedRows = catalogueEventWidget->ui->treeView->selectionModel()->selectedRows(); + if (selectedRows.count() != 1) { qCWarning(LOG_CatalogueEventsWidget()) << "updateGraphMode: not compatible with multiple events selected"; + return; + } + + if (!m_VisualizationWidget) { + qCWarning(LOG_CatalogueEventsWidget()) + << "updateGraphMode: visualization widget not found"; + return; + } + + auto event = m_Model->getEvent(selectedRows.first()); + if (!event) { + // A event product is probably selected + qCInfo(LOG_CatalogueEventsWidget()) << "updateGraphMode: no events are selected"; + return; + } + + auto tab = m_VisualizationWidget->currentTabWidget(); + if (!tab) { + qCWarning(LOG_CatalogueEventsWidget()) + << "updateGraphMode: no tab found in the visualization"; + return; + } + + auto isNewZone = m_ZoneForGraphMode == NEW_ZONE_TEXT; + auto zone = tab->getZoneWithName(m_ZoneForGraphMode); + if (!isNewZone && !zone) { + qCWarning(LOG_CatalogueEventsWidget()) << "updateGraphMode: zone not found"; + return; + } + + // Closes the previous graph and delete the asociated variables + for (auto graph : m_CustomGraphs) { + graph->close(); + auto variables = graph->variables().toVector(); + + QMetaObject::invokeMethod(&sqpApp->variableController(), "deleteVariables", + Qt::QueuedConnection, + Q_ARG(QVector >, variables)); + } + m_CustomGraphs.clear(); + + // Closes the remaining graphs inside the zone + if (zone) { + zone->closeAllGraphs(); + } + + // Creates the zone if needed + if (isNewZone) { + zone = tab->createEmptyZone(0); + m_ZoneForGraphMode = zone->name(); + } + + // Calculates the range of each graph which will be created + auto graphRange = getGraphRanges(event); + + // Loops through the event products and create the graph + auto itRange = graphRange.cbegin(); + for (auto eventProduct : event->getEventProducts()) { + auto productId = eventProduct.getProductId(); + + auto range = *itRange; + ++itRange; + + SqpRange productRange; + productRange.m_TStart = eventProduct.getTStart(); + productRange.m_TEnd = eventProduct.getTEnd(); + + auto context = new QObject{catalogueEventWidget}; + QObject::connect( + &sqpApp->variableController(), &VariableController::variableAdded, context, + [this, catalogueEventWidget, zone, context, event, range, productRange, + productId](auto variable) { + + if (variable->metadata().value(DataSourceItem::ID_DATA_KEY).toString() + == productId) { + auto graph = zone->createGraph(variable); + graph->setAutoRangeOnVariableInitialization(false); + + auto selectionZone + = graph->addSelectionZone(event->getName(), productRange); + emit catalogueEventWidget->selectionZoneAdded(event, productId, + selectionZone); + m_CustomGraphs << graph; + + graph->setGraphRange(range, true); + + // Removes the graph from the graph list if it is closed manually + QObject::connect(graph, &VisualizationGraphWidget::destroyed, + [this, graph]() { m_CustomGraphs.removeAll(graph); }); + + delete context; // removes the connection + } + }, + Qt::QueuedConnection); + + QMetaObject::invokeMethod(&sqpApp->dataSourceController(), + "requestVariableFromProductIdKey", Qt::QueuedConnection, + Q_ARG(QString, productId)); } } @@ -236,11 +339,12 @@ CatalogueEventsWidget::CatalogueEventsWidget(QWidget *parent) ui->treeView->setDragDropMode(QAbstractItemView::DragDrop); ui->treeView->setDragEnabled(true); + connect(ui->btnTime, &QToolButton::clicked, [this](auto checked) { if (checked) { ui->btnChart->setChecked(false); impl->m_ZonesForTimeMode - = impl->selectZone(this, impl->m_ZonesForTimeMode, true, + = impl->selectZone(this, impl->m_ZonesForTimeMode, true, false, this->mapToGlobal(ui->btnTime->frameGeometry().center())); impl->updateForTimeMode(ui->treeView); @@ -250,12 +354,13 @@ CatalogueEventsWidget::CatalogueEventsWidget(QWidget *parent) connect(ui->btnChart, &QToolButton::clicked, [this](auto checked) { if (checked) { ui->btnTime->setChecked(false); + impl->m_ZoneForGraphMode - = impl->selectZone(this, {impl->m_ZoneForGraphMode}, false, + = impl->selectZone(this, {impl->m_ZoneForGraphMode}, false, true, this->mapToGlobal(ui->btnChart->frameGeometry().center())) .value(0); - impl->updateForGraphMode(ui->treeView); + impl->updateForGraphMode(this); } }); @@ -266,16 +371,38 @@ CatalogueEventsWidget::CatalogueEventsWidget(QWidget *parent) if (!events.isEmpty() && eventProducts.isEmpty()) { - if (QMessageBox::warning(this, tr("Remove Event(s)"), - tr("The selected event(s) will be completly removed " - "from the repository!\nAre you sure you want to continue?"), - QMessageBox::Yes | QMessageBox::No, QMessageBox::No) - == QMessageBox::Yes) { + auto canRemoveEvent + = !this->isAllEventsDisplayed() + || (QMessageBox::warning( + this, tr("Remove Event(s)"), + tr("The selected event(s) will be permanently removed " + "from the repository!\nAre you sure you want to continue?"), + QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + == QMessageBox::Yes); + if (canRemoveEvent) { for (auto event : events) { - sqpApp->catalogueController().removeEvent(event); - impl->removeEvent(event, ui->treeView); + if (this->isAllEventsDisplayed()) { + sqpApp->catalogueController().removeEvent(event); + impl->removeEvent(event, ui->treeView); + } + else { + QVector > modifiedCatalogues; + for (auto catalogue : this->displayedCatalogues()) { + if (catalogue->removeEvent(event->getUniqId())) { + sqpApp->catalogueController().updateCatalogue(catalogue); + modifiedCatalogues << catalogue; + } + } + if (!modifiedCatalogues.empty()) { + emit eventCataloguesModified(modifiedCatalogues); + } + } + impl->m_Model->removeEvent(event); } + + + emit this->eventsRemoved(events); } } }); @@ -294,7 +421,7 @@ CatalogueEventsWidget::CatalogueEventsWidget(QWidget *parent) impl->updateForTimeMode(ui->treeView); } else if (isNotMultiSelection && ui->btnChart->isChecked()) { - impl->updateForGraphMode(ui->treeView); + impl->updateForGraphMode(this); } QVector > events; @@ -307,11 +434,13 @@ CatalogueEventsWidget::CatalogueEventsWidget(QWidget *parent) ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Tags, QHeaderView::Stretch); ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Validation, - QHeaderView::Fixed); + QHeaderView::ResizeToContents); ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Name, QHeaderView::Interactive); - ui->treeView->header()->resizeSection((int)CatalogueEventsModel::Column::Validation, - VALIDATION_COLUMN_SIZE); + ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::TStart, + QHeaderView::ResizeToContents); + ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::TEnd, + QHeaderView::ResizeToContents); ui->treeView->header()->setSortIndicatorShown(true); connect(impl->m_Model, &CatalogueEventsModel::modelSorted, [this]() { @@ -357,12 +486,20 @@ void CatalogueEventsWidget::setEventChanges(const std::shared_ptr &even setEventChanges(event, false); }, [this, event]() { - sqpApp->catalogueController().discardEvent(event); - setEventChanges(event, false); - impl->m_Model->refreshEvent(event, true); + bool removed = false; + sqpApp->catalogueController().discardEvent(event, removed); + if (removed) { + impl->m_Model->removeEvent(event); + } + else { + setEventChanges(event, false); + impl->m_Model->refreshEvent(event, true); + } emitSelection(); }); ui->treeView->setIndexWidget(validationIndex, widget); + ui->treeView->header()->resizeSection((int)CatalogueEventsModel::Column::Validation, + QHeaderView::ResizeToContents); } } else { @@ -383,7 +520,7 @@ QVector > CatalogueEventsWidget::displayedCatalogue bool CatalogueEventsWidget::isAllEventsDisplayed() const { - return impl->m_DisplayedCatalogues.isEmpty() && !impl->m_Model->events().isEmpty(); + return impl->m_AllEventDisplayed; } bool CatalogueEventsWidget::isEventDisplayed(const std::shared_ptr &event) const @@ -391,10 +528,16 @@ bool CatalogueEventsWidget::isEventDisplayed(const std::shared_ptr &eve return impl->m_Model->indexOf(event).isValid(); } +void CatalogueEventsWidget::refreshEvent(const std::shared_ptr &event) +{ + impl->m_Model->refreshEvent(event, true); +} + void CatalogueEventsWidget::populateWithCatalogues( const QVector > &catalogues) { impl->m_DisplayedCatalogues = catalogues; + impl->m_AllEventDisplayed = false; QSet eventIds; QVector > events; @@ -415,6 +558,7 @@ void CatalogueEventsWidget::populateWithCatalogues( void CatalogueEventsWidget::populateWithAllEvents() { impl->m_DisplayedCatalogues.clear(); + impl->m_AllEventDisplayed = true; auto allEvents = sqpApp->catalogueController().retrieveAllEvents(); @@ -429,15 +573,16 @@ void CatalogueEventsWidget::populateWithAllEvents() void CatalogueEventsWidget::clear() { impl->m_DisplayedCatalogues.clear(); + impl->m_AllEventDisplayed = false; impl->setEvents({}, this); } void CatalogueEventsWidget::refresh() { - if (impl->m_DisplayedCatalogues.isEmpty()) { + if (isAllEventsDisplayed()) { populateWithAllEvents(); } - else { + else if (!impl->m_DisplayedCatalogues.isEmpty()) { populateWithCatalogues(impl->m_DisplayedCatalogues); } } @@ -458,3 +603,15 @@ void CatalogueEventsWidget::emitSelection() emit selectionCleared(); } } + + +void CatalogueEventsWidget::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Delete: { + ui->btnRemove->click(); + } + default: + break; + } +} diff --git a/gui/src/Catalogue/CatalogueExplorer.cpp b/gui/src/Catalogue/CatalogueExplorer.cpp index e730041..414e6ca 100644 --- a/gui/src/Catalogue/CatalogueExplorer.cpp +++ b/gui/src/Catalogue/CatalogueExplorer.cpp @@ -4,13 +4,22 @@ #include #include #include +#include +#include #include #include #include +#include + +#include struct CatalogueExplorer::CatalogueExplorerPrivate { CatalogueActionManager m_ActionManager; + std::unordered_map, QVector > + m_SelectionZonesPerEvents; + + QMetaObject::Connection m_Conn; CatalogueExplorerPrivate(CatalogueExplorer *catalogueExplorer) : m_ActionManager(catalogueExplorer) @@ -27,6 +36,7 @@ CatalogueExplorer::CatalogueExplorer(QWidget *parent) impl->m_ActionManager.installSelectionZoneActions(); + // Updates events and inspector when something is selected in the catalogue widget connect(ui->catalogues, &CatalogueSideBarWidget::catalogueSelected, [this](auto catalogues) { if (catalogues.count() == 1) { ui->inspector->setCatalogue(catalogues.first()); @@ -66,6 +76,13 @@ CatalogueExplorer::CatalogueExplorer(QWidget *parent) ui->events->clear(); }); + connect(ui->catalogues, &CatalogueSideBarWidget::catalogueSaved, ui->events, + &CatalogueEventsWidget::refresh); + + connect(ui->catalogues, &CatalogueSideBarWidget::catalogueListChanged, + [this]() { impl->m_ActionManager.refreshCreateInCatalogueAction(); }); + + // Updates the inspectot when something is selected in the events connect(ui->events, &CatalogueEventsWidget::eventsSelected, [this](auto events) { if (events.count() == 1) { ui->inspector->setEvent(events.first()); @@ -88,9 +105,31 @@ CatalogueExplorer::CatalogueExplorer(QWidget *parent) connect(ui->events, &CatalogueEventsWidget::selectionCleared, [this]() { ui->inspector->showPage(CatalogueInspectorWidget::Page::Empty); }); + // Manage Selection Zones associated to events + connect(ui->events, &CatalogueEventsWidget::selectionZoneAdded, + [this](auto event, auto productId, auto zone) { + this->addSelectionZoneItem(event, productId, zone); + }); + + connect(ui->events, &CatalogueEventsWidget::eventsRemoved, [this](auto events) { + for (auto event : events) { + auto associatedSelectionZonesIt = impl->m_SelectionZonesPerEvents.find(event); + if (associatedSelectionZonesIt != impl->m_SelectionZonesPerEvents.cend()) { + for (auto selectionZone : associatedSelectionZonesIt->second) { + auto parentGraph = selectionZone->parentGraphWidget(); + parentGraph->removeSelectionZone(selectionZone); + } + + impl->m_SelectionZonesPerEvents.erase(event); + } + } + }); + + // Updates changes from the inspector connect(ui->inspector, &CatalogueInspectorWidget::catalogueUpdated, [this](auto catalogue) { sqpApp->catalogueController().updateCatalogue(catalogue); ui->catalogues->setCatalogueChanges(catalogue, true); + impl->m_ActionManager.refreshCreateInCatalogueAction(); }); connect(ui->inspector, &CatalogueInspectorWidget::eventUpdated, [this](auto event) { @@ -103,10 +142,18 @@ CatalogueExplorer::CatalogueExplorer(QWidget *parent) sqpApp->catalogueController().updateEventProduct(eventProduct); ui->events->setEventChanges(event, true); }); + + connect(ui->events, &CatalogueEventsWidget::eventCataloguesModified, + [this](const QVector > &catalogues) { + for (auto catalogue : catalogues) { + ui->catalogues->setCatalogueChanges(catalogue, true); + } + }); } CatalogueExplorer::~CatalogueExplorer() { + disconnect(impl->m_Conn); delete ui; } @@ -124,3 +171,37 @@ CatalogueSideBarWidget &CatalogueExplorer::sideBarWidget() const { return *ui->catalogues; } + +void CatalogueExplorer::clearSelectionZones() +{ + impl->m_SelectionZonesPerEvents.clear(); +} + +void CatalogueExplorer::addSelectionZoneItem(const std::shared_ptr &event, + const QString &productId, + VisualizationSelectionZoneItem *selectionZone) +{ + impl->m_SelectionZonesPerEvents[event] << selectionZone; + connect(selectionZone, &VisualizationSelectionZoneItem::rangeEdited, + [event, productId, this](auto range) { + auto productList = event->getEventProducts(); + for (auto &product : productList) { + if (product.getProductId() == productId) { + product.setTStart(range.m_TStart); + product.setTEnd(range.m_TEnd); + } + } + event->setEventProducts(productList); + sqpApp->catalogueController().updateEvent(event); + ui->events->refreshEvent(event); + ui->events->setEventChanges(event, true); + ui->inspector->refresh(); + }); + + impl->m_Conn = connect(selectionZone, &VisualizationSelectionZoneItem::destroyed, + [event, selectionZone, this]() { + if (!impl->m_SelectionZonesPerEvents.empty()) { + impl->m_SelectionZonesPerEvents[event].removeAll(selectionZone); + } + }); +} diff --git a/gui/src/Catalogue/CatalogueExplorerHelper.cpp b/gui/src/Catalogue/CatalogueExplorerHelper.cpp index 1fb7767..c08c38d 100644 --- a/gui/src/Catalogue/CatalogueExplorerHelper.cpp +++ b/gui/src/Catalogue/CatalogueExplorerHelper.cpp @@ -3,7 +3,6 @@ #include #include -const auto VALIDATION_BUTTON_ICON_SIZE = 12; QWidget *CatalogueExplorerHelper::buildValidationWidget(QWidget *parent, std::function save, std::function discard) @@ -11,22 +10,21 @@ QWidget *CatalogueExplorerHelper::buildValidationWidget(QWidget *parent, std::fu auto widget = new QWidget{parent}; auto layout = new QHBoxLayout{widget}; - layout->setContentsMargins(0, 0, 0, 0); - layout->setSpacing(0); auto btnValid = new QToolButton{widget}; btnValid->setIcon(QIcon{":/icones/save"}); - btnValid->setIconSize(QSize{VALIDATION_BUTTON_ICON_SIZE, VALIDATION_BUTTON_ICON_SIZE}); btnValid->setAutoRaise(true); QObject::connect(btnValid, &QToolButton::clicked, save); layout->addWidget(btnValid); auto btnDiscard = new QToolButton{widget}; btnDiscard->setIcon(QIcon{":/icones/discard"}); - btnDiscard->setIconSize(QSize{VALIDATION_BUTTON_ICON_SIZE, VALIDATION_BUTTON_ICON_SIZE}); btnDiscard->setAutoRaise(true); QObject::connect(btnDiscard, &QToolButton::clicked, discard); layout->addWidget(btnDiscard); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + return widget; } diff --git a/gui/src/Catalogue/CatalogueInspectorWidget.cpp b/gui/src/Catalogue/CatalogueInspectorWidget.cpp index 57fb08f..c24cbcc 100644 --- a/gui/src/Catalogue/CatalogueInspectorWidget.cpp +++ b/gui/src/Catalogue/CatalogueInspectorWidget.cpp @@ -28,6 +28,9 @@ CatalogueInspectorWidget::CatalogueInspectorWidget(QWidget *parent) impl->connectCatalogueUpdateSignals(this, ui); impl->connectEventUpdateSignals(this, ui); + + ui->dateTimeEventTStart->setDisplayFormat(DATETIME_FORMAT); + ui->dateTimeEventTEnd->setDisplayFormat(DATETIME_FORMAT); } CatalogueInspectorWidget::~CatalogueInspectorWidget() @@ -64,7 +67,7 @@ void CatalogueInspectorWidget::CatalogueInspectorWidgetPrivate::connectEventUpda }); connect(ui->leEventTags, &QLineEdit::editingFinished, [ui, inspector, this]() { - auto tags = ui->leEventTags->text().split(QRegExp("\\s+")); + auto tags = ui->leEventTags->text().split(QRegExp("\\s+"), QString::SkipEmptyParts); std::list tagNames; for (auto tag : tags) { tagNames.push_back(tag); @@ -148,8 +151,14 @@ void CatalogueInspectorWidget::setEvent(const std::shared_ptr &event) ui->leEventName->setEnabled(true); ui->leEventName->setText(event->getName()); ui->leEventProduct->setEnabled(false); - ui->leEventProduct->setText( - QString::number(event->getEventProducts().size()).append(" product(s)")); + + auto eventProducts = event->getEventProducts(); + QStringList eventProductList; + for (auto evtProduct : eventProducts) { + eventProductList << evtProduct.getProductId(); + } + + ui->leEventProduct->setText(eventProductList.join(";")); QString tagList; auto tags = event->getTagsNames(); @@ -209,3 +218,19 @@ void CatalogueInspectorWidget::setCatalogue(const std::shared_ptr & blockSignals(false); } + +void CatalogueInspectorWidget::refresh() +{ + switch (static_cast(ui->stackedWidget->currentIndex())) { + case Page::CatalogueProperties: + setCatalogue(impl->m_DisplayedCatalogue); + break; + case Page::EventProperties: { + auto isEventShowed = ui->leEventName->isEnabled(); + setEvent(impl->m_DisplayedEvent); + if (!isEventShowed && impl->m_DisplayedEvent) { + setEventProduct(impl->m_DisplayedEvent, impl->m_DisplayedEventProduct); + } + } + } +} diff --git a/gui/src/Catalogue/CatalogueSideBarWidget.cpp b/gui/src/Catalogue/CatalogueSideBarWidget.cpp index 06db478..8a2312d 100644 --- a/gui/src/Catalogue/CatalogueSideBarWidget.cpp +++ b/gui/src/Catalogue/CatalogueSideBarWidget.cpp @@ -8,10 +8,14 @@ #include #include #include +#include #include #include +#include #include +#include +#include Q_LOGGING_CATEGORY(LOG_CatalogueSideBarWidget, "CatalogueSideBarWidget") @@ -21,6 +25,8 @@ constexpr auto TRASH_ITEM_TYPE = CatalogueAbstractTreeItem::DEFAULT_TYPE + 2; constexpr auto CATALOGUE_ITEM_TYPE = CatalogueAbstractTreeItem::DEFAULT_TYPE + 3; constexpr auto DATABASE_ITEM_TYPE = CatalogueAbstractTreeItem::DEFAULT_TYPE + 4; +const auto DEFAULT_CATALOGUE_NAME = QObject::tr("Catalogue"); + struct CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate { @@ -29,11 +35,11 @@ struct CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate { void configureTreeWidget(QTreeView *treeView); QModelIndex addDatabaseItem(const QString &name); CatalogueAbstractTreeItem *getDatabaseItem(const QString &name); - void addCatalogueItem(const std::shared_ptr &catalogue, - const QModelIndex &databaseIndex); + CatalogueAbstractTreeItem *addCatalogueItem(const std::shared_ptr &catalogue, + const QModelIndex &databaseIndex); CatalogueTreeItem *getCatalogueItem(const std::shared_ptr &catalogue) const; - void setHasChanges(bool value, const QModelIndex &index, QTreeView *treeView); + void setHasChanges(bool value, const QModelIndex &index, CatalogueSideBarWidget *sideBarWidget); bool hasChanges(const QModelIndex &index, QTreeView *treeView); int selectionType(QTreeView *treeView) const @@ -104,44 +110,93 @@ CatalogueSideBarWidget::CatalogueSideBarWidget(QWidget *parent) ui->treeView->setModel(impl->m_TreeModel); impl->configureTreeWidget(ui->treeView); + emit catalogueListChanged(); ui->treeView->header()->setStretchLastSection(false); ui->treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); - ui->treeView->header()->setSectionResizeMode(0, QHeaderView::Stretch); - - auto emitSelection = [this]() { - - auto selectionType = impl->selectionType(ui->treeView); - - switch (selectionType) { - case CATALOGUE_ITEM_TYPE: - emit this->catalogueSelected(impl->selectedCatalogues(ui->treeView)); - break; - case DATABASE_ITEM_TYPE: - emit this->databaseSelected(impl->selectedRepositories(ui->treeView)); - break; - case ALL_EVENT_ITEM_TYPE: - emit this->allEventsSelected(); - break; - case TRASH_ITEM_TYPE: - emit this->trashSelected(); - break; - default: - emit this->selectionCleared(); - break; + ui->treeView->header()->setSectionResizeMode((int)CatalogueTreeModel::Column::Name, + QHeaderView::Stretch); + + connect(ui->treeView, &QTreeView::clicked, this, &CatalogueSideBarWidget::emitSelection); + connect(ui->treeView->selectionModel(), &QItemSelectionModel::currentChanged, this, + &CatalogueSideBarWidget::emitSelection); + + + connect(ui->btnAdd, &QToolButton::clicked, [this]() { + auto catalogue = std::make_shared(); + catalogue->setName(DEFAULT_CATALOGUE_NAME); + sqpApp->catalogueController().addCatalogue(catalogue); + auto item = this->addCatalogue(catalogue, REPOSITORY_DEFAULT); + this->setCatalogueChanges(catalogue, true); + ui->treeView->edit(impl->m_TreeModel->indexOf(item)); + + }); + + + connect(impl->m_TreeModel, &CatalogueTreeModel::itemDropped, + [this](auto index, auto mimeData, auto action) { + auto item = impl->m_TreeModel->item(index); + + if (item && item->type() == CATALOGUE_ITEM_TYPE) { + auto catalogue = static_cast(item)->catalogue(); + this->setCatalogueChanges(catalogue, true); + } + + if (action == Qt::MoveAction) { + /// Display a save button on source catalogues + auto sourceCatalogues = sqpApp->catalogueController().cataloguesForMimeData( + mimeData->data(MIME_TYPE_SOURCE_CATALOGUE_LIST)); + for (auto catalogue : sourceCatalogues) { + if (auto catalogueItem = impl->getCatalogueItem(catalogue)) { + catalogueItem->replaceCatalogue(catalogue); + this->setCatalogueChanges(catalogue, true); + } + } + + this->emitSelection(); + } + }); + + connect(ui->btnRemove, &QToolButton::clicked, [this]() { + QVector, CatalogueAbstractTreeItem *> > + cataloguesToItems; + auto selectedIndexes = ui->treeView->selectionModel()->selectedRows(); + + for (auto index : selectedIndexes) { + auto item = impl->m_TreeModel->item(index); + if (item && item->type() == CATALOGUE_ITEM_TYPE) { + auto catalogue = static_cast(item)->catalogue(); + cataloguesToItems << qMakePair(catalogue, item); + } } - }; - connect(ui->treeView, &QTreeView::clicked, emitSelection); - connect(ui->treeView->selectionModel(), &QItemSelectionModel::currentChanged, emitSelection); - connect(impl->m_TreeModel, &CatalogueTreeModel::itemRenamed, [emitSelection, this](auto index) { + if (!cataloguesToItems.isEmpty()) { + + if (QMessageBox::warning(this, tr("Remove Catalogue(s)"), + tr("The selected catalogues(s) will be completly removed " + "from the repository!\nAre you sure you want to continue?"), + QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + == QMessageBox::Yes) { + + for (auto catalogueToItem : cataloguesToItems) { + sqpApp->catalogueController().removeCatalogue(catalogueToItem.first); + impl->m_TreeModel->removeChildItem( + catalogueToItem.second, + impl->m_TreeModel->indexOf(catalogueToItem.second->parent())); + } + emitSelection(); + emit catalogueListChanged(); + } + } + }); + + connect(impl->m_TreeModel, &CatalogueTreeModel::itemRenamed, [this](auto index) { auto selectedIndexes = ui->treeView->selectionModel()->selectedRows(); if (selectedIndexes.contains(index)) { - emitSelection(); + this->emitSelection(); } - - auto item = impl->m_TreeModel->item(index); - impl->setHasChanges(true, index, ui->treeView); + impl->setHasChanges(true, index, this); + emit this->catalogueListChanged(); }); ui->treeView->setContextMenuPolicy(Qt::CustomContextMenu); @@ -154,11 +209,17 @@ CatalogueSideBarWidget::~CatalogueSideBarWidget() delete ui; } -void CatalogueSideBarWidget::addCatalogue(const std::shared_ptr &catalogue, - const QString &repository) +CatalogueAbstractTreeItem * +CatalogueSideBarWidget::addCatalogue(const std::shared_ptr &catalogue, + const QString &repository) { auto repositoryItem = impl->getDatabaseItem(repository); - impl->addCatalogueItem(catalogue, impl->m_TreeModel->indexOf(repositoryItem)); + auto catalogueItem + = impl->addCatalogueItem(catalogue, impl->m_TreeModel->indexOf(repositoryItem)); + + emit catalogueListChanged(); + + return catalogueItem; } void CatalogueSideBarWidget::setCatalogueChanges(const std::shared_ptr &catalogue, @@ -166,7 +227,7 @@ void CatalogueSideBarWidget::setCatalogueChanges(const std::shared_ptrgetCatalogueItem(catalogue)) { auto index = impl->m_TreeModel->indexOf(catalogueItem); - impl->setHasChanges(hasChanges, index, ui->treeView); + impl->setHasChanges(hasChanges, index, this); // catalogueItem->refresh(); } } @@ -189,6 +250,29 @@ CatalogueSideBarWidget::getCatalogues(const QString &repository) const return result; } +void CatalogueSideBarWidget::emitSelection() +{ + auto selectionType = impl->selectionType(ui->treeView); + + switch (selectionType) { + case CATALOGUE_ITEM_TYPE: + emit this->catalogueSelected(impl->selectedCatalogues(ui->treeView)); + break; + case DATABASE_ITEM_TYPE: + emit this->databaseSelected(impl->selectedRepositories(ui->treeView)); + break; + case ALL_EVENT_ITEM_TYPE: + emit this->allEventsSelected(); + break; + case TRASH_ITEM_TYPE: + emit this->trashSelected(); + break; + default: + emit this->selectionCleared(); + break; + } +} + void CatalogueSideBarWidget::onContextMenuRequested(const QPoint &pos) { QMenu menu{this}; @@ -274,12 +358,14 @@ CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::getDatabaseItem(const QSt return nullptr; } -void CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::addCatalogueItem( +CatalogueAbstractTreeItem *CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::addCatalogueItem( const std::shared_ptr &catalogue, const QModelIndex &databaseIndex) { auto catalogueItem = new CatalogueTreeItem{catalogue, QIcon{":/icones/catalogue.png"}, CATALOGUE_ITEM_TYPE}; m_TreeModel->addChildItem(catalogueItem, databaseIndex); + + return catalogueItem; } CatalogueTreeItem *CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::getCatalogueItem( @@ -290,7 +376,7 @@ CatalogueTreeItem *CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::getCat for (auto childItem : item->children()) { if (childItem->type() == CATALOGUE_ITEM_TYPE) { auto catalogueItem = static_cast(childItem); - if (catalogueItem->catalogue() == catalogue) { + if (catalogueItem->catalogue()->getUniqId() == catalogue->getUniqId()) { return catalogueItem; } } @@ -307,25 +393,51 @@ CatalogueTreeItem *CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::getCat return nullptr; } -void CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::setHasChanges(bool value, - const QModelIndex &index, - QTreeView *treeView) +void CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::setHasChanges( + bool value, const QModelIndex &index, CatalogueSideBarWidget *sideBarWidget) { + std::shared_ptr catalogue = nullptr; + auto item = m_TreeModel->item(index); + if (item && item->type() == CATALOGUE_ITEM_TYPE) { + catalogue = static_cast(item)->catalogue(); + } + auto validationIndex = index.sibling(index.row(), (int)CatalogueTreeModel::Column::Validation); if (value) { - if (!hasChanges(validationIndex, treeView)) { + if (!hasChanges(validationIndex, sideBarWidget->ui->treeView)) { auto widget = CatalogueExplorerHelper::buildValidationWidget( - treeView, [this, validationIndex, - treeView]() { setHasChanges(false, validationIndex, treeView); }, - [this, validationIndex, treeView]() { - setHasChanges(false, validationIndex, treeView); + sideBarWidget->ui->treeView, + [this, validationIndex, sideBarWidget, catalogue]() { + if (catalogue) { + sqpApp->catalogueController().saveCatalogue(catalogue); + emit sideBarWidget->catalogueSaved(catalogue); + } + setHasChanges(false, validationIndex, sideBarWidget); + }, + [this, validationIndex, sideBarWidget, catalogue, item]() { + if (catalogue) { + bool removed; + sqpApp->catalogueController().discardCatalogue(catalogue, removed); + + if (removed) { + m_TreeModel->removeChildItem(item, + m_TreeModel->indexOf(item->parent())); + } + else { + m_TreeModel->refresh(m_TreeModel->indexOf(item)); + setHasChanges(false, validationIndex, sideBarWidget); + } + sideBarWidget->emitSelection(); + } }); - treeView->setIndexWidget(validationIndex, widget); + sideBarWidget->ui->treeView->setIndexWidget(validationIndex, widget); + sideBarWidget->ui->treeView->header()->resizeSection( + (int)CatalogueTreeModel::Column::Validation, QHeaderView::ResizeToContents); } } else { // Note: the widget is destroyed - treeView->setIndexWidget(validationIndex, nullptr); + sideBarWidget->ui->treeView->setIndexWidget(validationIndex, nullptr); } } @@ -335,3 +447,15 @@ bool CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::hasChanges(const QMo auto validationIndex = index.sibling(index.row(), (int)CatalogueTreeModel::Column::Validation); return treeView->indexWidget(validationIndex) != nullptr; } + + +void CatalogueSideBarWidget::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Delete: { + ui->btnRemove->click(); + } + default: + break; + } +} diff --git a/gui/src/Catalogue/CatalogueTreeItems/CatalogueAbstractTreeItem.cpp b/gui/src/Catalogue/CatalogueTreeItems/CatalogueAbstractTreeItem.cpp index 3fb084c..60cd390 100644 --- a/gui/src/Catalogue/CatalogueTreeItems/CatalogueAbstractTreeItem.cpp +++ b/gui/src/Catalogue/CatalogueTreeItems/CatalogueAbstractTreeItem.cpp @@ -24,6 +24,12 @@ void CatalogueAbstractTreeItem::addChild(CatalogueAbstractTreeItem *child) child->impl->m_Parent = this; } +void CatalogueAbstractTreeItem::removeChild(CatalogueAbstractTreeItem *child) +{ + impl->m_Children.removeAll(child); + delete child; +} + QVector CatalogueAbstractTreeItem::children() const { return impl->m_Children; diff --git a/gui/src/Catalogue/CatalogueTreeItems/CatalogueTreeItem.cpp b/gui/src/Catalogue/CatalogueTreeItems/CatalogueTreeItem.cpp index 78bcd4b..4fb59dd 100644 --- a/gui/src/Catalogue/CatalogueTreeItems/CatalogueTreeItem.cpp +++ b/gui/src/Catalogue/CatalogueTreeItems/CatalogueTreeItem.cpp @@ -76,20 +76,56 @@ Qt::ItemFlags CatalogueTreeItem::flags(int column) const bool CatalogueTreeItem::canDropMimeData(const QMimeData *data, Qt::DropAction action) { - return data->hasFormat(MIME_TYPE_EVENT_LIST); + // Check that the event is not dropped on the same catalogue + auto sourceCatalogues = sqpApp->catalogueController().cataloguesForMimeData( + data->data(MIME_TYPE_SOURCE_CATALOGUE_LIST)); + for (auto catalogue : sourceCatalogues) { + if (catalogue->getUniqId() == impl->m_Catalogue->getUniqId()) { + return false; + } + } + + auto events = sqpApp->catalogueController().eventsForMimeData(data->data(MIME_TYPE_EVENT_LIST)); + auto canDrop = data->hasFormat(MIME_TYPE_EVENT_LIST); + + for (auto event : events) { + canDrop &= (event->getRepository() == impl->m_Catalogue->getRepository()); + } + + return canDrop; } bool CatalogueTreeItem::dropMimeData(const QMimeData *data, Qt::DropAction action) { Q_ASSERT(canDropMimeData(data, action)); + // Warning: Check that the events aren't already in the catalogue + // No need to check check for the repository: inter-repository drop is forbidden in + // canDropMimeData auto events = sqpApp->catalogueController().eventsForMimeData(data->data(MIME_TYPE_EVENT_LIST)); - // impl->m_Catalogue->addEvents(events); TODO: move events in the new catalogue - // Warning: Check that the events aren't already in the catalogue - // Also check for the repository !!! + auto sourceCatalogues = sqpApp->catalogueController().cataloguesForMimeData( + data->data(MIME_TYPE_SOURCE_CATALOGUE_LIST)); + + for (auto event : events) { + + if (action == Qt::MoveAction) { + for (auto catalogue : sourceCatalogues) { + catalogue->removeEvent(event->getUniqId()); + sqpApp->catalogueController().updateCatalogue(catalogue); + } + } + + impl->m_Catalogue->addEvent(event->getUniqId()); + sqpApp->catalogueController().updateCatalogue(impl->m_Catalogue); + } } std::shared_ptr CatalogueTreeItem::catalogue() const { return impl->m_Catalogue; } + +void CatalogueTreeItem::replaceCatalogue(const std::shared_ptr &catalogue) +{ + impl->m_Catalogue = catalogue; +} diff --git a/gui/src/Catalogue/CatalogueTreeModel.cpp b/gui/src/Catalogue/CatalogueTreeModel.cpp index 86ac456..bf72ba1 100644 --- a/gui/src/Catalogue/CatalogueTreeModel.cpp +++ b/gui/src/Catalogue/CatalogueTreeModel.cpp @@ -46,6 +46,23 @@ void CatalogueTreeModel::addChildItem(CatalogueAbstractTreeItem *child, emit dataChanged(parentIndex, parentIndex); } +void CatalogueTreeModel::removeChildItem(CatalogueAbstractTreeItem *child, + const QModelIndex &parentIndex) +{ + auto parentItem = item(parentIndex); + int i = parentItem->children().indexOf(child); + beginRemoveRows(parentIndex, i, i); + parentItem->removeChild(child); + endRemoveRows(); + + emit dataChanged(parentIndex, parentIndex); +} + +void CatalogueTreeModel::refresh(const QModelIndex &index) +{ + emit dataChanged(index, index); +} + CatalogueAbstractTreeItem *CatalogueTreeModel::item(const QModelIndex &index) const { return static_cast(index.internalPointer()); @@ -161,6 +178,7 @@ bool CatalogueTreeModel::setData(const QModelIndex &index, const QVariant &value return false; } + bool CatalogueTreeModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const { @@ -183,7 +201,7 @@ bool CatalogueTreeModel::dropMimeData(const QMimeData *data, Qt::DropAction acti if (draggedItem) { result = draggedItem->dropMimeData(data, action); if (result) { - emit itemDropped(draggedIndex); + emit itemDropped(draggedIndex, data, action); } } @@ -197,5 +215,5 @@ Qt::DropActions CatalogueTreeModel::supportedDropActions() const QStringList CatalogueTreeModel::mimeTypes() const { - return {MIME_TYPE_EVENT_LIST}; + return {MIME_TYPE_EVENT_LIST, MIME_TYPE_SOURCE_CATALOGUE_LIST}; } diff --git a/gui/src/Catalogue/CreateEventDialog.cpp b/gui/src/Catalogue/CreateEventDialog.cpp deleted file mode 100644 index 2dd6249..0000000 --- a/gui/src/Catalogue/CreateEventDialog.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include "Catalogue/CreateEventDialog.h" -#include "ui_CreateEventDialog.h" - -#include -#include - -#include - -struct CreateEventDialog::CreateEventDialogPrivate { - QVector > m_DisplayedCatalogues; -}; - -CreateEventDialog::CreateEventDialog(const QVector > &catalogues, - QWidget *parent) - : QDialog(parent), - ui(new Ui::CreateEventDialog), - impl{spimpl::make_unique_impl()} -{ - ui->setupUi(this); - - connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); - connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); - - impl->m_DisplayedCatalogues = catalogues; - for (auto cat : impl->m_DisplayedCatalogues) { - ui->cbCatalogue->addItem(cat->getName()); - } -} - -CreateEventDialog::~CreateEventDialog() -{ - delete ui; -} - -void CreateEventDialog::hideCatalogueChoice() -{ - ui->cbCatalogue->hide(); - ui->lblCatalogue->hide(); -} - -QString CreateEventDialog::eventName() const -{ - return ui->leEvent->text(); -} - -std::shared_ptr CreateEventDialog::selectedCatalogue() const -{ - auto catalogue = impl->m_DisplayedCatalogues.value(ui->cbCatalogue->currentIndex()); - if (!catalogue || catalogue->getName() != catalogueName()) { - return nullptr; - } - - return catalogue; -} - -QString CreateEventDialog::catalogueName() const -{ - return ui->cbCatalogue->currentText(); -} diff --git a/gui/src/DragAndDrop/DragDropGuiController.cpp b/gui/src/DragAndDrop/DragDropGuiController.cpp index 1546c22..22f058b 100644 --- a/gui/src/DragAndDrop/DragDropGuiController.cpp +++ b/gui/src/DragAndDrop/DragDropGuiController.cpp @@ -7,6 +7,7 @@ #include "Visualization/VisualizationWidget.h" #include "Visualization/operations/FindVariableOperation.h" +#include "DataSource/DataSourceController.h" #include "Variable/Variable.h" #include "Variable/VariableController.h" @@ -286,11 +287,22 @@ bool DragDropGuiController::checkMimeDataForVisualization( // result = false: cannot drop multiple variables in the visualisation } } + else if (mimeData->hasFormat(MIME_TYPE_PRODUCT_LIST)) { + auto productDataList = sqpApp->dataSourceController().productsDataForMimeData( + mimeData->data(MIME_TYPE_PRODUCT_LIST)); + if (productDataList.count() == 1) { + result = true; + } + else { + // result = false: cannot drop multiple products in the visualisation + } + } else { // Other MIME data // no special rules, accepted by default result = true; } + return result; } diff --git a/gui/src/SqpApplication.cpp b/gui/src/SqpApplication.cpp index b69af14..6ebcbe9 100644 --- a/gui/src/SqpApplication.cpp +++ b/gui/src/SqpApplication.cpp @@ -109,6 +109,8 @@ SqpApplication::SqpApplication(int &argc, char **argv) { qCDebug(LOG_SqpApplication()) << tr("SqpApplication construction") << QThread::currentThread(); + QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + connect(&impl->m_DataSourceControllerThread, &QThread::started, impl->m_DataSourceController.get(), &DataSourceController::initialize); connect(&impl->m_DataSourceControllerThread, &QThread::finished, diff --git a/gui/src/Visualization/AxisRenderingUtils.cpp b/gui/src/Visualization/AxisRenderingUtils.cpp index 33f47a6..f5ce4cb 100644 --- a/gui/src/Visualization/AxisRenderingUtils.cpp +++ b/gui/src/Visualization/AxisRenderingUtils.cpp @@ -4,6 +4,8 @@ #include #include +#include + #include #include @@ -11,8 +13,6 @@ Q_LOGGING_CATEGORY(LOG_AxisRenderingUtils, "AxisRenderingUtils") namespace { -const auto DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd hh:mm:ss:zzz"); - /// Format for datetimes on a axis const auto DATETIME_TICKER_FORMAT = QStringLiteral("yyyy/MM/dd \nhh:mm:ss"); @@ -68,11 +68,17 @@ void setAxisProperties(QCPAxis &axis, const Unit &unit, */ template struct AxisSetter { - static void setProperties(T &, QCustomPlot &, SqpColorScale &) + static void setProperties(QCustomPlot &, SqpColorScale &) { // Default implementation does nothing qCCritical(LOG_AxisRenderingUtils()) << "Can't set axis properties: unmanaged type of data"; } + + static void setUnits(T &, QCustomPlot &, SqpColorScale &) + { + // Default implementation does nothing + qCCritical(LOG_AxisRenderingUtils()) << "Can't set axis units: unmanaged type of data"; + } }; /** @@ -83,7 +89,12 @@ struct AxisSetter { template struct AxisSetter::value or std::is_base_of::value> > { - static void setProperties(T &dataSeries, QCustomPlot &plot, SqpColorScale &) + static void setProperties(QCustomPlot &, SqpColorScale &) + { + // Nothing to do + } + + static void setUnits(T &dataSeries, QCustomPlot &plot, SqpColorScale &) { dataSeries.lockRead(); auto xAxisUnit = dataSeries.xAxisUnit(); @@ -101,17 +112,8 @@ struct AxisSetter: */ template struct AxisSetter::value> > { - static void setProperties(T &dataSeries, QCustomPlot &plot, SqpColorScale &colorScale) + static void setProperties(QCustomPlot &plot, SqpColorScale &colorScale) { - dataSeries.lockRead(); - auto xAxisUnit = dataSeries.xAxisUnit(); - auto yAxisUnit = dataSeries.yAxisUnit(); - auto valuesUnit = dataSeries.valuesUnit(); - dataSeries.unlock(); - - setAxisProperties(*plot.xAxis, xAxisUnit); - setAxisProperties(*plot.yAxis, yAxisUnit, QCPAxis::stLogarithmic); - // Displays color scale in plot plot.plotLayout()->insertRow(0); plot.plotLayout()->addElement(0, 0, colorScale.m_Scale); @@ -125,9 +127,21 @@ struct AxisSetteraxis(), valuesUnit, QCPAxis::stLogarithmic); colorScale.m_AutomaticThreshold = true; } + + static void setUnits(T &dataSeries, QCustomPlot &plot, SqpColorScale &colorScale) + { + dataSeries.lockRead(); + auto xAxisUnit = dataSeries.xAxisUnit(); + auto yAxisUnit = dataSeries.yAxisUnit(); + auto valuesUnit = dataSeries.valuesUnit(); + dataSeries.unlock(); + + setAxisProperties(*plot.xAxis, xAxisUnit); + setAxisProperties(*plot.yAxis, yAxisUnit, QCPAxis::stLogarithmic); + setAxisProperties(*colorScale.m_Scale->axis(), valuesUnit, QCPAxis::stLogarithmic); + } }; /** @@ -136,14 +150,25 @@ struct AxisSetter struct AxisHelper : public IAxisHelper { - explicit AxisHelper(T &dataSeries) : m_DataSeries{dataSeries} {} + explicit AxisHelper(std::shared_ptr dataSeries) : m_DataSeries{dataSeries} {} void setProperties(QCustomPlot &plot, SqpColorScale &colorScale) override { - AxisSetter::setProperties(m_DataSeries, plot, colorScale); + AxisSetter::setProperties(plot, colorScale); } - T &m_DataSeries; + void setUnits(QCustomPlot &plot, SqpColorScale &colorScale) override + { + if (m_DataSeries) { + AxisSetter::setUnits(*m_DataSeries, plot, colorScale); + } + else { + qCCritical(LOG_AxisRenderingUtils()) << "Can't set units: inconsistency between the " + "type of data series and the type supposed"; + } + } + + std::shared_ptr m_DataSeries; }; } // namespace @@ -159,19 +184,22 @@ QString formatValue(double value, const QCPAxis &axis) } } -std::unique_ptr -IAxisHelperFactory::create(std::shared_ptr dataSeries) noexcept +std::unique_ptr IAxisHelperFactory::create(const Variable &variable) noexcept { - if (auto scalarSeries = std::dynamic_pointer_cast(dataSeries)) { - return std::make_unique >(*scalarSeries); - } - else if (auto spectrogramSeries = std::dynamic_pointer_cast(dataSeries)) { - return std::make_unique >(*spectrogramSeries); - } - else if (auto vectorSeries = std::dynamic_pointer_cast(dataSeries)) { - return std::make_unique >(*vectorSeries); - } - else { - return std::make_unique >(*dataSeries); + switch (variable.type()) { + case DataSeriesType::SCALAR: + return std::make_unique >( + std::dynamic_pointer_cast(variable.dataSeries())); + case DataSeriesType::SPECTROGRAM: + return std::make_unique >( + std::dynamic_pointer_cast(variable.dataSeries())); + case DataSeriesType::VECTOR: + return std::make_unique >( + std::dynamic_pointer_cast(variable.dataSeries())); + default: + // Creates default helper + break; } + + return std::make_unique >(nullptr); } diff --git a/gui/src/Visualization/PlottablesRenderingUtils.cpp b/gui/src/Visualization/PlottablesRenderingUtils.cpp index e326eb1..b73fa91 100644 --- a/gui/src/Visualization/PlottablesRenderingUtils.cpp +++ b/gui/src/Visualization/PlottablesRenderingUtils.cpp @@ -6,6 +6,8 @@ #include #include +#include + #include Q_LOGGING_CATEGORY(LOG_PlottablesRenderingUtils, "PlottablesRenderingUtils") @@ -17,7 +19,7 @@ namespace { */ template struct PlottablesSetter { - static void setProperties(T &, PlottablesMap &) + static void setProperties(PlottablesMap &) { // Default implementation does nothing qCCritical(LOG_PlottablesRenderingUtils()) @@ -33,20 +35,25 @@ struct PlottablesSetter { template struct PlottablesSetter::value or std::is_base_of::value> > { - static void setProperties(T &dataSeries, PlottablesMap &plottables) + static void setProperties(PlottablesMap &plottables) { - // Gets the number of components of the data series - dataSeries.lockRead(); - auto componentCount = dataSeries.valuesData()->componentCount(); - dataSeries.unlock(); + // Finds the plottable with the highest index to determine the number of colors to generate + auto end = plottables.cend(); + auto maxPlottableIndexIt + = std::max_element(plottables.cbegin(), end, [](const auto &it1, const auto &it2) { + return it1.first < it2.first; + }); + auto componentCount = maxPlottableIndexIt != end ? maxPlottableIndexIt->first + 1 : 0; // Generates colors for each component auto colors = ColorUtils::colors(Qt::blue, Qt::red, componentCount); // For each component of the data series, creates a QCPGraph to add to the plot for (auto i = 0; i < componentCount; ++i) { - auto graph = plottables.at(i); - graph->setPen(QPen{colors.at(i)}); + auto graphIt = plottables.find(i); + if (graphIt != end) { + graphIt->second->setPen(QPen{colors.at(i)}); + } } } }; @@ -58,7 +65,7 @@ struct PlottablesSetter struct PlottablesSetter::value> > { - static void setProperties(T &, PlottablesMap &plottables) + static void setProperties(PlottablesMap &plottables) { // Checks that for a spectrogram there is only one plottable, that is a colormap if (plottables.size() != 1) { @@ -92,31 +99,28 @@ struct PlottablesSetter struct PlottablesHelper : public IPlottablesHelper { - explicit PlottablesHelper(T &dataSeries) : m_DataSeries{dataSeries} {} - void setProperties(PlottablesMap &plottables) override { - PlottablesSetter::setProperties(m_DataSeries, plottables); + PlottablesSetter::setProperties(plottables); } - - T &m_DataSeries; }; } // namespace std::unique_ptr -IPlottablesHelperFactory::create(std::shared_ptr dataSeries) noexcept +IPlottablesHelperFactory::create(const Variable &variable) noexcept { - if (auto scalarSeries = std::dynamic_pointer_cast(dataSeries)) { - return std::make_unique >(*scalarSeries); - } - else if (auto spectrogramSeries = std::dynamic_pointer_cast(dataSeries)) { - return std::make_unique >(*spectrogramSeries); - } - else if (auto vectorSeries = std::dynamic_pointer_cast(dataSeries)) { - return std::make_unique >(*vectorSeries); - } - else { - return std::make_unique >(*dataSeries); + switch (variable.type()) { + case DataSeriesType::SCALAR: + return std::make_unique >(); + case DataSeriesType::SPECTROGRAM: + return std::make_unique >(); + case DataSeriesType::VECTOR: + return std::make_unique >(); + default: + // Returns default helper + break; } + + return std::make_unique >(); } diff --git a/gui/src/Visualization/VisualizationGraphHelper.cpp b/gui/src/Visualization/VisualizationGraphHelper.cpp index 363a7c0..bd0c047 100644 --- a/gui/src/Visualization/VisualizationGraphHelper.cpp +++ b/gui/src/Visualization/VisualizationGraphHelper.cpp @@ -25,7 +25,7 @@ public: */ template struct PlottablesCreator { - static PlottablesMap createPlottables(T &, QCustomPlot &) + static PlottablesMap createPlottables(QCustomPlot &) { qCCritical(LOG_DataSeries()) << QObject::tr("Can't create plottables: unmanaged data series type"); @@ -33,34 +33,37 @@ struct PlottablesCreator { } }; +PlottablesMap createGraphs(QCustomPlot &plot, int nbGraphs) +{ + PlottablesMap result{}; + + // Creates {nbGraphs} QCPGraph to add to the plot + for (auto i = 0; i < nbGraphs; ++i) { + auto graph = plot.addGraph(); + result.insert({i, graph}); + } + + plot.replot(); + + return result; +} + /** - * Specialization of PlottablesCreator for scalars and vectors + * Specialization of PlottablesCreator for scalars * @sa ScalarSeries - * @sa VectorSeries */ template -struct PlottablesCreator::value - or std::is_base_of::value> > { - static PlottablesMap createPlottables(T &dataSeries, QCustomPlot &plot) - { - PlottablesMap result{}; - - // Gets the number of components of the data series - dataSeries.lockRead(); - auto componentCount = dataSeries.valuesData()->componentCount(); - dataSeries.unlock(); - - // For each component of the data series, creates a QCPGraph to add to the plot - for (auto i = 0; i < componentCount; ++i) { - auto graph = plot.addGraph(); - result.insert({i, graph}); - } - - plot.replot(); +struct PlottablesCreator::value> > { + static PlottablesMap createPlottables(QCustomPlot &plot) { return createGraphs(plot, 1); } +}; - return result; - } +/** + * Specialization of PlottablesCreator for vectors + * @sa VectorSeries + */ +template +struct PlottablesCreator::value> > { + static PlottablesMap createPlottables(QCustomPlot &plot) { return createGraphs(plot, 3); } }; /** @@ -70,7 +73,7 @@ struct PlottablesCreator struct PlottablesCreator::value> > { - static PlottablesMap createPlottables(T &dataSeries, QCustomPlot &plot) + static PlottablesMap createPlottables(QCustomPlot &plot) { PlottablesMap result{}; result.insert({0, new QCPColorMap{plot.xAxis, plot.yAxis}}); @@ -264,41 +267,59 @@ struct IPlottablesHelper { */ template struct PlottablesHelper : public IPlottablesHelper { - explicit PlottablesHelper(T &dataSeries) : m_DataSeries{dataSeries} {} + explicit PlottablesHelper(std::shared_ptr dataSeries) : m_DataSeries{dataSeries} {} PlottablesMap create(QCustomPlot &plot) const override { - return PlottablesCreator::createPlottables(m_DataSeries, plot); + return PlottablesCreator::createPlottables(plot); } void update(PlottablesMap &plottables, const SqpRange &range, bool rescaleAxes) const override { - PlottablesUpdater::updatePlottables(m_DataSeries, plottables, range, rescaleAxes); + if (m_DataSeries) { + PlottablesUpdater::updatePlottables(*m_DataSeries, plottables, range, rescaleAxes); + } + else { + qCCritical(LOG_VisualizationGraphHelper()) << "Can't update plottables: inconsistency " + "between the type of data series and the " + "type supposed"; + } } void setYAxisRange(const SqpRange &xAxisRange, QCustomPlot &plot) const override { - return PlottablesUpdater::setPlotYAxisRange(m_DataSeries, xAxisRange, plot); + if (m_DataSeries) { + PlottablesUpdater::setPlotYAxisRange(*m_DataSeries, xAxisRange, plot); + } + else { + qCCritical(LOG_VisualizationGraphHelper()) << "Can't update plottables: inconsistency " + "between the type of data series and the " + "type supposed"; + } } - T &m_DataSeries; + std::shared_ptr m_DataSeries; }; -/// Creates IPlottablesHelper according to a data series -std::unique_ptr createHelper(std::shared_ptr dataSeries) noexcept +/// Creates IPlottablesHelper according to the type of data series a variable holds +std::unique_ptr createHelper(std::shared_ptr variable) noexcept { - if (auto scalarSeries = std::dynamic_pointer_cast(dataSeries)) { - return std::make_unique >(*scalarSeries); - } - else if (auto spectrogramSeries = std::dynamic_pointer_cast(dataSeries)) { - return std::make_unique >(*spectrogramSeries); - } - else if (auto vectorSeries = std::dynamic_pointer_cast(dataSeries)) { - return std::make_unique >(*vectorSeries); - } - else { - return std::make_unique >(*dataSeries); + switch (variable->type()) { + case DataSeriesType::SCALAR: + return std::make_unique >( + std::dynamic_pointer_cast(variable->dataSeries())); + case DataSeriesType::SPECTROGRAM: + return std::make_unique >( + std::dynamic_pointer_cast(variable->dataSeries())); + case DataSeriesType::VECTOR: + return std::make_unique >( + std::dynamic_pointer_cast(variable->dataSeries())); + default: + // Creates default helper + break; } + + return std::make_unique >(nullptr); } } // namespace @@ -307,7 +328,7 @@ PlottablesMap VisualizationGraphHelper::create(std::shared_ptr variabl QCustomPlot &plot) noexcept { if (variable) { - auto helper = createHelper(variable->dataSeries()); + auto helper = createHelper(variable); auto plottables = helper->create(plot); return plottables; } @@ -322,7 +343,7 @@ void VisualizationGraphHelper::setYAxisRange(std::shared_ptr variable, QCustomPlot &plot) noexcept { if (variable) { - auto helper = createHelper(variable->dataSeries()); + auto helper = createHelper(variable); helper->setYAxisRange(variable->range(), plot); } else { @@ -332,9 +353,9 @@ void VisualizationGraphHelper::setYAxisRange(std::shared_ptr variable, } void VisualizationGraphHelper::updateData(PlottablesMap &plottables, - std::shared_ptr dataSeries, + std::shared_ptr variable, const SqpRange &dateTime) { - auto helper = createHelper(dataSeries); + auto helper = createHelper(variable); helper->update(plottables, dateTime); } diff --git a/gui/src/Visualization/VisualizationGraphRenderingDelegate.cpp b/gui/src/Visualization/VisualizationGraphRenderingDelegate.cpp index 30747c3..7764aa2 100644 --- a/gui/src/Visualization/VisualizationGraphRenderingDelegate.cpp +++ b/gui/src/Visualization/VisualizationGraphRenderingDelegate.cpp @@ -9,6 +9,7 @@ #include #include +#include #include @@ -302,14 +303,14 @@ void VisualizationGraphRenderingDelegate::onPlotUpdated() noexcept impl->m_Plot.replot(); } -void VisualizationGraphRenderingDelegate::setAxesProperties( - std::shared_ptr dataSeries) noexcept +void VisualizationGraphRenderingDelegate::setAxesUnits(const Variable &variable) noexcept { - // Stores x-axis label to be able to retrieve it when x-axis pixmap is unselected - impl->m_XAxisLabel = dataSeries->xAxisUnit().m_Name; - auto axisHelper = IAxisHelperFactory::create(dataSeries); - axisHelper->setProperties(impl->m_Plot, impl->m_ColorScale); + auto axisHelper = IAxisHelperFactory::create(variable); + axisHelper->setUnits(impl->m_Plot, impl->m_ColorScale); + + // Stores x-axis label to be able to retrieve it when x-axis pixmap is unselected + impl->m_XAxisLabel = impl->m_Plot.xAxis->label(); // Updates x-axis state impl->updateXAxisState(); @@ -317,10 +318,15 @@ void VisualizationGraphRenderingDelegate::setAxesProperties( impl->m_Plot.layer(AXES_LAYER)->replot(); } -void VisualizationGraphRenderingDelegate::setPlottablesProperties( - std::shared_ptr dataSeries, PlottablesMap &plottables) noexcept +void VisualizationGraphRenderingDelegate::setGraphProperties(const Variable &variable, + PlottablesMap &plottables) noexcept { - auto plottablesHelper = IPlottablesHelperFactory::create(dataSeries); + // Axes' properties + auto axisHelper = IAxisHelperFactory::create(variable); + axisHelper->setProperties(impl->m_Plot, impl->m_ColorScale); + + // Plottables' properties + auto plottablesHelper = IPlottablesHelperFactory::create(variable); plottablesHelper->setProperties(plottables); } diff --git a/gui/src/Visualization/VisualizationGraphWidget.cpp b/gui/src/Visualization/VisualizationGraphWidget.cpp index b1e2048..6f5b3f9 100644 --- a/gui/src/Visualization/VisualizationGraphWidget.cpp +++ b/gui/src/Visualization/VisualizationGraphWidget.cpp @@ -12,6 +12,7 @@ #include "ui_VisualizationGraphWidget.h" #include +#include #include #include #include @@ -59,16 +60,16 @@ struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate { explicit VisualizationGraphWidgetPrivate(const QString &name) : m_Name{name}, - m_DoAcquisition{true}, + m_Flags{GraphFlag::EnableAll}, m_IsCalibration{false}, m_RenderingDelegate{nullptr} { } - void updateData(PlottablesMap &plottables, std::shared_ptr dataSeries, + void updateData(PlottablesMap &plottables, std::shared_ptr variable, const SqpRange &range) { - VisualizationGraphHelper::updateData(plottables, dataSeries, range); + VisualizationGraphHelper::updateData(plottables, variable, range); // Prevents that data has changed to update rendering m_RenderingDelegate->onPlotUpdated(); @@ -77,7 +78,7 @@ struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate { QString m_Name; // 1 variable -> n qcpplot std::map, PlottablesMap> m_VariableToPlotMultiMap; - bool m_DoAcquisition; + GraphFlags m_Flags; bool m_IsCalibration; /// Delegate used to attach rendering features to the plot std::unique_ptr m_RenderingDelegate; @@ -94,6 +95,8 @@ struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate { bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even + bool m_VariableAutoRangeOnInit = true; + void startDrawingRect(const QPoint &pos, QCustomPlot &plot) { removeDrawingRect(plot); @@ -243,7 +246,7 @@ VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget &VisualizationGraphWidget::onMouseDoubleClick); connect(ui->widget->xAxis, static_cast( &QCPAxis::rangeChanged), - this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection); + this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection); // Activates menu when right clicking on the graph ui->widget->setContextMenuPolicy(Qt::CustomContextMenu); @@ -256,9 +259,8 @@ VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this, &VisualizationGraphWidget::onUpdateVarDisplaying); -#ifdef Q_OS_MAC + // Necessary for all platform since Qt::AA_EnableHighDpiScaling is enable. plot().setPlottingHint(QCP::phFastPolylines, true); -#endif } @@ -287,35 +289,54 @@ VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const return qobject_cast(parent); } -void VisualizationGraphWidget::enableAcquisition(bool enable) +void VisualizationGraphWidget::setFlags(GraphFlags flags) { - impl->m_DoAcquisition = enable; + impl->m_Flags = std::move(flags); } void VisualizationGraphWidget::addVariable(std::shared_ptr variable, SqpRange range) { - // Uses delegate to create the qcpplot components according to the variable - auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget); + /// Lambda used to set graph's units and range according to the variable passed in parameter + auto loadRange = [this](std::shared_ptr variable, const SqpRange &range) { + impl->m_RenderingDelegate->setAxesUnits(*variable); + + this->setFlags(GraphFlag::DisableAll); + setGraphRange(range); + this->setFlags(GraphFlag::EnableAll); + emit requestDataLoading({variable}, range, false); + }; - if (auto dataSeries = variable->dataSeries()) { - // Set axes properties according to the units of the data series - impl->m_RenderingDelegate->setAxesProperties(dataSeries); + connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated())); - // Sets rendering properties for the new plottables - // Warning: this method must be called after setAxesProperties(), as it can access to some - // axes properties that have to be initialized - impl->m_RenderingDelegate->setPlottablesProperties(dataSeries, createdPlottables); - } + // Calls update of graph's range and units when the data of the variable have been initialized. + // Note: we use QueuedConnection here as the update event must be called in the UI thread + connect(variable.get(), &Variable::dataInitialized, this, + [ varW = std::weak_ptr{variable}, range, loadRange, this ]() { + if (auto var = varW.lock()) { + // If the variable is the first added in the graph, we load its range + auto firstVariableInGraph = range == INVALID_RANGE; + auto loadedRange = graphRange(); + if (impl->m_VariableAutoRangeOnInit) { + loadedRange = firstVariableInGraph ? var->range() : range; + } + loadRange(var, loadedRange); + setYRange(var); + } + }, + Qt::QueuedConnection); - impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)}); + // Uses delegate to create the qcpplot components according to the variable + auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget); - connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated())); + // Sets graph properties + impl->m_RenderingDelegate->setGraphProperties(*variable, createdPlottables); - this->enableAcquisition(false); - this->setGraphRange(range); - this->enableAcquisition(true); + impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)}); - emit requestDataLoading(QVector >() << variable, range, false); + // If the variable already has its data loaded, load its units and its range in the graph + if (variable->dataSeries() != nullptr) { + loadRange(variable, range); + } emit variableAdded(variable); } @@ -371,14 +392,29 @@ SqpRange VisualizationGraphWidget::graphRange() const noexcept return SqpRange{graphRange.lower, graphRange.upper}; } -void VisualizationGraphWidget::setGraphRange(const SqpRange &range) +void VisualizationGraphWidget::setGraphRange(const SqpRange &range, bool calibration) { qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START"); + + if (calibration) { + impl->m_IsCalibration = true; + } + ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd); ui->widget->replot(); + + if (calibration) { + impl->m_IsCalibration = false; + } + qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END"); } +void VisualizationGraphWidget::setAutoRangeOnVariableInitialization(bool value) +{ + impl->m_VariableAutoRangeOnInit = value; +} + QVector VisualizationGraphWidget::selectionZoneRanges() const { QVector ranges; @@ -401,6 +437,20 @@ void VisualizationGraphWidget::addSelectionZones(const QVector &ranges plot().replot(QCustomPlot::rpQueuedReplot); } +VisualizationSelectionZoneItem *VisualizationGraphWidget::addSelectionZone(const QString &name, + const SqpRange &range) +{ + // note: ownership is transfered to QCustomPlot + auto zone = new VisualizationSelectionZoneItem(&plot()); + zone->setName(name); + zone->setRange(range.m_TStart, range.m_TEnd); + impl->addSelectionZone(zone); + + plot().replot(QCustomPlot::rpQueuedReplot); + + return zone; +} + void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone) { parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false); @@ -584,6 +634,10 @@ void VisualizationGraphWidget::closeEvent(QCloseEvent *event) { Q_UNUSED(event); + for (auto i : impl->m_SelectionZones) { + parentVisualizationWidget()->selectionZoneManager().setSelected(i, false); + } + // Prevents that all variables will be removed from graph when it will be closed for (auto &variableEntry : impl->m_VariableToPlotMultiMap) { emit variableAboutToBeRemoved(variableEntry.first); @@ -654,32 +708,51 @@ void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept QHash subMenus; QHash subMenusEnabled; + QHash filteredMenu; for (auto zoneAction : zoneActions) { auto isEnabled = zoneAction->isEnabled(selectedItems); auto menu = &graphMenu; + QString menuPath; for (auto subMenuName : zoneAction->subMenuList()) { - if (!subMenus.contains(subMenuName)) { + menuPath += '/'; + menuPath += subMenuName; + + if (!subMenus.contains(menuPath)) { menu = menu->addMenu(subMenuName); - subMenus[subMenuName] = menu; - subMenusEnabled[subMenuName] = isEnabled; + subMenus[menuPath] = menu; + subMenusEnabled[menuPath] = isEnabled; } else { - menu = subMenus.value(subMenuName); + menu = subMenus.value(menuPath); if (isEnabled) { // The sub menu is enabled if at least one of its actions is enabled - subMenusEnabled[subMenuName] = true; + subMenusEnabled[menuPath] = true; } } } + FilteringAction *filterAction = nullptr; + if (sqpApp->actionsGuiController().isMenuFiltered(zoneAction->subMenuList())) { + filterAction = filteredMenu.value(menuPath); + if (!filterAction) { + filterAction = new FilteringAction{this}; + filteredMenu[menuPath] = filterAction; + menu->addAction(filterAction); + } + } + auto action = menu->addAction(zoneAction->name()); action->setEnabled(isEnabled); action->setShortcut(zoneAction->displayedShortcut()); QObject::connect(action, &QAction::triggered, [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); }); + + if (filterAction && zoneAction->isFilteringAllowed()) { + filterAction->addActionToFilter(action); + } } for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) { @@ -696,12 +769,12 @@ void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange { qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: VisualizationGraphWidget::onRangeChanged") << QThread::currentThread()->objectName() << "DoAcqui" - << impl->m_DoAcquisition; + << impl->m_Flags.testFlag(GraphFlag::EnableAcquisition); auto graphRange = SqpRange{t1.lower, t1.upper}; auto oldGraphRange = SqpRange{t2.lower, t2.upper}; - if (impl->m_DoAcquisition) { + if (impl->m_Flags.testFlag(GraphFlag::EnableAcquisition)) { QVector > variableUnderGraphVector; for (auto it = impl->m_VariableToPlotMultiMap.begin(), @@ -711,13 +784,13 @@ void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange } emit requestDataLoading(std::move(variableUnderGraphVector), graphRange, !impl->m_IsCalibration); + } - if (!impl->m_IsCalibration) { - qCDebug(LOG_VisualizationGraphWidget()) - << tr("TORM: VisualizationGraphWidget::Synchronize notify !!") - << QThread::currentThread()->objectName() << graphRange << oldGraphRange; - emit synchronize(graphRange, oldGraphRange); - } + if (impl->m_Flags.testFlag(GraphFlag::EnableSynchronization) && !impl->m_IsCalibration) { + qCDebug(LOG_VisualizationGraphWidget()) + << tr("TORM: VisualizationGraphWidget::Synchronize notify !!") + << QThread::currentThread()->objectName() << graphRange << oldGraphRange; + emit synchronize(graphRange, oldGraphRange); } auto pos = mapFromGlobal(QCursor::pos()); @@ -733,6 +806,9 @@ void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange else { qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget"; } + + // Quits calibration + impl->m_IsCalibration = false; } void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept @@ -803,6 +879,11 @@ void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept { + // Processes event only if the wheel occurs on axis rect + if (!dynamic_cast(ui->widget->layoutElementAt(event->posF()))) { + return; + } + auto value = event->angleDelta().x() + event->angleDelta().y(); if (value != 0) { @@ -917,8 +998,6 @@ void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept impl->endDrawingZone(this); - impl->m_IsCalibration = false; - // Selection / Deselection auto isSelectionZoneMode = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones; @@ -988,7 +1067,7 @@ void VisualizationGraphWidget::onDataCacheVariableUpdated() qCDebug(LOG_VisualizationGraphWidget()) << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime; if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) { - impl->updateData(variableEntry.second, variable->dataSeries(), variable->range()); + impl->updateData(variableEntry.second, variable, variable->range()); } } } @@ -998,6 +1077,6 @@ void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr v { auto it = impl->m_VariableToPlotMultiMap.find(variable); if (it != impl->m_VariableToPlotMultiMap.end()) { - impl->updateData(it->second, variable->dataSeries(), range); + impl->updateData(it->second, variable, range); } } diff --git a/gui/src/Visualization/VisualizationMultiZoneSelectionDialog.cpp b/gui/src/Visualization/VisualizationMultiZoneSelectionDialog.cpp index e625b52..aaf9161 100644 --- a/gui/src/Visualization/VisualizationMultiZoneSelectionDialog.cpp +++ b/gui/src/Visualization/VisualizationMultiZoneSelectionDialog.cpp @@ -4,7 +4,7 @@ #include "Common/DateUtils.h" #include "Visualization/VisualizationSelectionZoneItem.h" -const auto DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd hh:mm:ss"); +const auto DATETIME_FORMAT_S = QStringLiteral("yyyy/MM/dd hh:mm:ss"); struct VisualizationMultiZoneSelectionDialog::VisualizationMultiZoneSelectionDialogPrivate { QVector m_Zones; @@ -46,9 +46,9 @@ void VisualizationMultiZoneSelectionDialog::setZones( } auto range = zone->range(); - name += DateUtils::dateTime(range.m_TStart).toString(DATETIME_FORMAT); + name += DateUtils::dateTime(range.m_TStart).toString(DATETIME_FORMAT_S); name += " - "; - name += DateUtils::dateTime(range.m_TEnd).toString(DATETIME_FORMAT); + name += DateUtils::dateTime(range.m_TEnd).toString(DATETIME_FORMAT_S); auto item = new QListWidgetItem(name, ui->listWidget); item->setSelected(zone->selected()); diff --git a/gui/src/Visualization/VisualizationSelectionZoneItem.cpp b/gui/src/Visualization/VisualizationSelectionZoneItem.cpp index 2e5c5b2..4e51bd9 100644 --- a/gui/src/Visualization/VisualizationSelectionZoneItem.cpp +++ b/gui/src/Visualization/VisualizationSelectionZoneItem.cpp @@ -388,8 +388,11 @@ void VisualizationSelectionZoneItem::mouseMoveEvent(QMouseEvent *event, const QP break; } + emit rangeEdited(range()); + for (auto associatedZone : impl->m_AssociatedEditedZones) { associatedZone->parentPlot()->replot(); + emit associatedZone->rangeEdited(associatedZone->range()); } } else { diff --git a/gui/src/Visualization/VisualizationSelectionZoneManager.cpp b/gui/src/Visualization/VisualizationSelectionZoneManager.cpp index ee679ce..1dc6b0d 100644 --- a/gui/src/Visualization/VisualizationSelectionZoneManager.cpp +++ b/gui/src/Visualization/VisualizationSelectionZoneManager.cpp @@ -39,7 +39,10 @@ void VisualizationSelectionZoneManager::clearSelection() { for (auto item : impl->m_SelectedItems) { item->setSelected(false); - item->parentPlot()->replot(QCustomPlot::rpQueuedReplot); + auto parentPlot = item->parentPlot(); + if (parentPlot) { + parentPlot->replot(QCustomPlot::rpQueuedReplot); + } } impl->m_SelectedItems.clear(); diff --git a/gui/src/Visualization/VisualizationTabWidget.cpp b/gui/src/Visualization/VisualizationTabWidget.cpp index 957de0e..383f384 100644 --- a/gui/src/Visualization/VisualizationTabWidget.cpp +++ b/gui/src/Visualization/VisualizationTabWidget.cpp @@ -7,6 +7,7 @@ #include "Visualization/MacScrollBarStyle.h" +#include "DataSource/DataSourceController.h" #include "Variable/VariableController.h" #include "Common/MimeTypesDef.h" @@ -69,6 +70,8 @@ struct VisualizationTabWidget::VisualizationTabWidgetPrivate { void dropZone(int index, VisualizationTabWidget *tabWidget); void dropVariables(const QList > &variables, int index, VisualizationTabWidget *tabWidget); + void dropProducts(const QVariantList &productsMetaData, int index, + VisualizationTabWidget *tabWidget); }; VisualizationTabWidget::VisualizationTabWidget(const QString &name, QWidget *parent) @@ -91,6 +94,8 @@ VisualizationTabWidget::VisualizationTabWidget(const QString &name, QWidget *par VisualizationDragDropContainer::DropBehavior::Inserted); ui->dragDropContainer->setMimeType(MIME_TYPE_VARIABLE_LIST, VisualizationDragDropContainer::DropBehavior::Inserted); + ui->dragDropContainer->setMimeType(MIME_TYPE_PRODUCT_LIST, + VisualizationDragDropContainer::DropBehavior::Inserted); ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) { return sqpApp->dragDropGuiController().checkMimeDataForVisualization(mimeData, @@ -229,6 +234,11 @@ void VisualizationTabWidget::dropMimeData(int index, const QMimeData *mimeData) mimeData->data(MIME_TYPE_VARIABLE_LIST)); impl->dropVariables(variables, index, this); } + else if (mimeData->hasFormat(MIME_TYPE_PRODUCT_LIST)) { + auto productsData = sqpApp->dataSourceController().productsDataForMimeData( + mimeData->data(MIME_TYPE_PRODUCT_LIST)); + impl->dropProducts(productsData, index, this); + } else { qCWarning(LOG_VisualizationZoneWidget()) << tr("VisualizationTabWidget::dropMimeData, unknown MIME data received."); @@ -352,3 +362,28 @@ void VisualizationTabWidget::VisualizationTabWidgetPrivate::dropVariables( tabWidget->createZone(variables, index); } + +void VisualizationTabWidget::VisualizationTabWidgetPrivate::dropProducts( + const QVariantList &productsMetaData, int index, VisualizationTabWidget *tabWidget) +{ + // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and + // compatible variable here + if (productsMetaData.count() != 1) { + qCWarning(LOG_VisualizationZoneWidget()) + << tr("VisualizationTabWidget::dropProducts, dropping multiple products, operation " + "aborted."); + return; + } + + auto context = new QObject{tabWidget}; + connect(&sqpApp->variableController(), &VariableController::variableAdded, context, + [this, index, tabWidget, context](auto variable) { + tabWidget->createZone({variable}, index); + delete context; // removes the connection + }, + Qt::QueuedConnection); + + auto productData = productsMetaData.first().toHash(); + QMetaObject::invokeMethod(&sqpApp->dataSourceController(), "requestVariable", + Qt::QueuedConnection, Q_ARG(QVariantHash, productData)); +} diff --git a/gui/src/Visualization/VisualizationZoneWidget.cpp b/gui/src/Visualization/VisualizationZoneWidget.cpp index 5f967a9..fab2acc 100644 --- a/gui/src/Visualization/VisualizationZoneWidget.cpp +++ b/gui/src/Visualization/VisualizationZoneWidget.cpp @@ -10,6 +10,7 @@ #include "Common/VisualizationDef.h" #include +#include #include