diff --git a/.github/workflows/build_on_pull_request.yml b/.github/workflows/build_on_pull_request.yml new file mode 100644 index 00000000..047c9458 --- /dev/null +++ b/.github/workflows/build_on_pull_request.yml @@ -0,0 +1,64 @@ +# This workflow will run on pull requests +name: Build on Pull Requests +on: + workflow_dispatch: + branches: [main, dev] + + pull_request: + branches: [dev, main] + +jobs: + build: + runs-on: windows-2019 + steps: + + - name: Install Qt + uses: jurplel/install-qt-action@v3.3.0 + with: + version: '6.5.3' + host: 'windows' + target: 'desktop' + arch: 'win64_msvc2019_64' + dir: 'C:\' + install-deps: 'true' + modules: 'all' + #archives: 'qtbase qtsvg' + cache: 'false' + cache-key-prefix: 'install-qt-action' + setup-python: 'false' + tools: 'tools_ninja' + set-env: 'true' + tools-only: 'false' + aqtversion: '==3.1.*' + py7zrversion: '==0.20.*' + extra: '--external 7z' + + - name: Checkout files + uses: actions/checkout@v3 + with: + submodules: recursive + + - name: List current files + run: | + dir + + - name: Create build directory + run: mkdir build + + - name: List Qt directory + working-directory: ${{env.Qt6_DIR}} + run: | + dir + + - uses: TheMrMilchmann/setup-msvc-dev@v3 + with: + arch: x64 + + - name: Build + env: + CMAKE_MODULE_PATH : ${{env.Qt6_Dir}}/lib/cmake/Qt6 + working-directory: build + run: | + cmake ../ -DCMAKE_BUILD_TYPE=Release -DQT6_DIR=${{env.Qt6_Dir}} -G CodeBlocks + cmake --build . + diff --git a/.github/workflows/docs_deploy.yml b/.github/workflows/docs_deploy.yml index 3283c134..e6959164 100644 --- a/.github/workflows/docs_deploy.yml +++ b/.github/workflows/docs_deploy.yml @@ -8,18 +8,6 @@ on: workflow_dispatch: branches: ["main", "dev"] -# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages -permissions: - contents: read - pages: write - id-token: write - -# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. -# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. -concurrency: - group: "pages" - cancel-in-progress: false - jobs: # Build job build: diff --git a/.gitignore b/.gitignore index a6c78627..8f3b334a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,9 @@ /client/resources/translations/*.qm deploy +/build-* +*.user + +html +venv +doctrees +CMakeLists.txt.user diff --git a/CMakeLists.txt b/CMakeLists.txt index c6a696bb..3556fa78 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,23 +1,23 @@ if (APPLE) - set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13" CACHE STRING "Minimum OS X deployment version") + set(CMAKE_OSX_DEPLOYMENT_TARGET "11.0" CACHE STRING "Minimum OS X deployment version") endif(APPLE) project(OpenTeraPlus) -include(CTest) -enable_testing() +#include(CTest) +#enable_testing() #include(CheckFunctionExists) #Look for minimum cmake version -cmake_minimum_required(VERSION 3.0.2) +cmake_minimum_required(VERSION 3.21) #DEFINITIONS POLICY, NEW SINCE 3.0.2 cmake_policy(SET CMP0043 NEW) # Software version SET(CPACK_PACKAGE_VERSION_MAJOR "1") -SET(CPACK_PACKAGE_VERSION_MINOR "1") -SET(CPACK_PACKAGE_VERSION_PATCH "3") +SET(CPACK_PACKAGE_VERSION_MINOR "2") +SET(CPACK_PACKAGE_VERSION_PATCH "0") SET(CPACK_PACKAGE_VERSION ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}) add_definitions(-DOPENTERAPLUS_VERSION="${CPACK_PACKAGE_VERSION}" ) add_definitions(-DOPENTERAPLUS_VERSION_MAJOR="${CPACK_PACKAGE_VERSION_MAJOR}" ) @@ -48,7 +48,7 @@ endif(UNIX AND NOT APPLE) set(CMAKE_VERBOSE_MAKEFILE ON) -set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) message(STATUS "Using QMAKE: ${QT_QMAKE_EXECUTABLE}") @@ -67,7 +67,7 @@ add_subdirectory(shared) add_subdirectory(client) # Tests -add_subdirectory(tests) +#add_subdirectory(tests) # Readme and other files add_custom_target(readme SOURCES README.md LICENSE.TXT) diff --git a/README.md b/README.md index e2c1f00f..a098ff6f 100755 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ OpenTeraPlus is a client that works with [OpenTera Server](https://github.com/in ## Publication(s) +* [![DOI](https://joss.theoj.org/papers/10.21105/joss.05497/status.svg)](https://doi.org/10.21105/joss.05497) Létourneau, D., Brière , S., et al., [OpenTera: A Framework for Telehealth Applications](https://doi.org/10.21105/joss.05497), Journal of Open Source Software, vol. 8, no 91, p. 5497 (2023) * Panchea, A.M., Létourneau, D., Brière, S. et al., [OpenTera: A microservice architecture solution for rapid prototyping of robotic solutions to COVID-19 challenges in care facilities](https://rdcu.be/cHzmf), Health Technol. 12, 583–596 (2022) ## Current Features diff --git a/README_fr.md b/README_fr.md new file mode 100644 index 00000000..8bc4230e --- /dev/null +++ b/README_fr.md @@ -0,0 +1,66 @@ + + +# OpenTeraPlus +OpenTeraPlus est un client pour le [Serveur OpenTera](https://github.com/introlab/opentera). Son rôle est d'agir comme un outil général pour gérer une instance d'OpenTera et de fournir certaines fonctionnalités spécifiques à certains services, telles que la Téléréadaptation. + +## Auteurs + +* Simon Brière, ing. M.Sc.A., Centre de recherche sur le Vieillissement (CDRV), CIUSSS de l'Estrie-CHUS (@sbriere) +* Dominic Létourneau, ing. M.Sc.A., IntRoLab, Université de Sherbrooke (@doumdi) +* François Michaud, ing. Ph.D., IntRoLab, Université de Sherbrooke +* Michel Tousignant, pht, Ph.D., CDRV, Université de Sherbrooke + +## Publication(s) + +* [![DOI](https://joss.theoj.org/papers/10.21105/joss.05497/status.svg)](https://doi.org/10.21105/joss.05497) Létourneau, D., Brière , S., et al., [OpenTera: A Framework for Telehealth Applications](https://doi.org/10.21105/joss.05497), Journal of Open Source Software, vol. 8, no 91, p. 5497 (2023) +* Panchea, A.M., Létourneau, D., Brière, S. et al., [OpenTera: A microservice architecture solution for rapid prototyping of robotic solutions to COVID-19 challenges in care facilities](https://rdcu.be/cHzmf), Health Technol. 12, 583–596 (2022) + +## Fonctionnalités actuelles + +### Aperçu des fonctionnalités +* Gestion globale du système, incluant les accès utilisateurs - rôles pour sites, projets, appareils, types de séances et services +* Gestion des participants (patients), incluant l'activation/désactivation, regroupements et tableaux de bord des séances +* Intégration spécifique pour les services de téléréadaptation et toutes les activités cliniques demandant une séance audio-vidéo adaptée au contexte +* Gestion des séances, incluant fichiers / données attachées et tests / questionnaires + +### Fonctionnalités détaillées +* Connexion en tant qu'utilisateur + * Implémentation de l'accès selon les groupes utilisateurs +* Gestion des projets et sites + * Accès des groupes utilisateurs + * Appareils, types de séances et évaluations liées + * Services associés +* Gestion des participants (patients) + * Regroupement des participants + * Activation / désactivation des participants, liens webs et connexion traditionnelle + * Ajout / suppression +* Gestion des séances + * Création de nouvelle séance et édition de séances existantes + * Vue calendrier des séances réalisées et planifiées + * Affichage des événements de séance + * Téléchargement et gestion des fichiers attachés aux séances + * Affichage des évaluations attachées aux séances +* Gestion des appareils + * Association d'appareils aux participants + * Activation / désactivation d'appareils + * Configuration des appareils +* Gestion des services OpenTera + * Édition de la configuration +* Séances de téléréadaptation + * Création et gestion de séances vidéos axées sur la télésanté +* ... et plus! + +# Captures d'écran + + + + + + +
Login ScreenProject NavigatorParticipant ViewerSession LobbyAdmin configuration
+ +# Vidéo +[![OpenTera+ Logiciel Clinique de Télésanté](https://img.youtube.com/vi/FathjoDGlZ0/maxresdefault.jpg)](https://youtu.be/FathjoDGlZ0) + +# Licence +OpenTeraPlus est licencé sous [GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.en.html) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index be3b1957..d1697fd5 100755 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -1,4 +1,4 @@ -find_package(Qt5Core REQUIRED) +find_package(Qt6Core REQUIRED) add_subdirectory(resources) add_subdirectory(src) diff --git a/client/resources/TeraClient.qrc b/client/resources/TeraClient.qrc index eb9767a4..21e19d1f 100755 --- a/client/resources/TeraClient.qrc +++ b/client/resources/TeraClient.qrc @@ -148,5 +148,6 @@ icons/logs/os_mac.png icons/logs/os_windows.png icons/pause.png + icons/project_disabled.png diff --git a/client/resources/icons.txt b/client/resources/icons.txt index 3d8fa63b..b55b0b3c 100644 --- a/client/resources/icons.txt +++ b/client/resources/icons.txt @@ -1,3 +1,13 @@ Logos icons created by Pixel perfect - Flaticon Logotype icons created by Freepik - Flaticon -Chrome icons created by Pixel perfect - Flaticon \ No newline at end of file +Chrome icons created by Pixel perfect - Flaticon +Book icons created by max.icons - Flaticon +Search icons created by Vector Stall - Flaticon +Filter icons created by designhub - Flaticon +Loading arrow icons created by Laisa Islam Ani - Flaticon +Files and folders icons created by riajulislam - Flaticon +Ui icons created by berkahicon - Flaticon +Output icons created by Laisa Islam Ani - Flaticon +Company icons created by Dragon Icons - Flaticon +Work icons created by Pixel perfect - Flaticon +Folder icons created by Freepik - Flaticon \ No newline at end of file diff --git a/client/resources/icons/config.png b/client/resources/icons/config.png index df031b75..609880f7 100644 Binary files a/client/resources/icons/config.png and b/client/resources/icons/config.png differ diff --git a/client/resources/icons/filter.png b/client/resources/icons/filter.png index 4cd8db61..7f294da9 100755 Binary files a/client/resources/icons/filter.png and b/client/resources/icons/filter.png differ diff --git a/client/resources/icons/group.png b/client/resources/icons/group.png index f986d0ad..e0f553c7 100644 Binary files a/client/resources/icons/group.png and b/client/resources/icons/group.png differ diff --git a/client/resources/icons/group_new.png b/client/resources/icons/group_new.png index 486195b6..2614dddf 100644 Binary files a/client/resources/icons/group_new.png and b/client/resources/icons/group_new.png differ diff --git a/client/resources/icons/navtree.png b/client/resources/icons/navtree.png index 9c7b0522..12df4c87 100644 Binary files a/client/resources/icons/navtree.png and b/client/resources/icons/navtree.png differ diff --git a/client/resources/icons/password.png b/client/resources/icons/password.png index 5f807115..55520ba1 100644 Binary files a/client/resources/icons/password.png and b/client/resources/icons/password.png differ diff --git a/client/resources/icons/project-icon.png b/client/resources/icons/project-icon.png index af119d79..c61c4be1 100644 Binary files a/client/resources/icons/project-icon.png and b/client/resources/icons/project-icon.png differ diff --git a/client/resources/icons/project.png b/client/resources/icons/project.png index b9dc7879..5303a70f 100644 Binary files a/client/resources/icons/project.png and b/client/resources/icons/project.png differ diff --git a/client/resources/icons/project_disabled.png b/client/resources/icons/project_disabled.png new file mode 100644 index 00000000..5488707d Binary files /dev/null and b/client/resources/icons/project_disabled.png differ diff --git a/client/resources/icons/project_new.png b/client/resources/icons/project_new.png index 75a4a3ff..b2f76bc6 100644 Binary files a/client/resources/icons/project_new.png and b/client/resources/icons/project_new.png differ diff --git a/client/resources/icons/refresh.png b/client/resources/icons/refresh.png index 5001f7de..bfaf2fe6 100755 Binary files a/client/resources/icons/refresh.png and b/client/resources/icons/refresh.png differ diff --git a/client/resources/icons/search.png b/client/resources/icons/search.png index 0e988c4b..ba937bc9 100755 Binary files a/client/resources/icons/search.png and b/client/resources/icons/search.png differ diff --git a/client/resources/icons/server.png b/client/resources/icons/server.png index 0f5785a6..407379ec 100644 Binary files a/client/resources/icons/server.png and b/client/resources/icons/server.png differ diff --git a/client/resources/icons/site-icon.png b/client/resources/icons/site-icon.png index 31968fc1..37668af0 100644 Binary files a/client/resources/icons/site-icon.png and b/client/resources/icons/site-icon.png differ diff --git a/client/resources/icons/site.png b/client/resources/icons/site.png index 2b33e4f2..8a290b22 100644 Binary files a/client/resources/icons/site.png and b/client/resources/icons/site.png differ diff --git a/client/resources/icons/unlock.png b/client/resources/icons/unlock.png index 8c804541..99b0f1d5 100755 Binary files a/client/resources/icons/unlock.png and b/client/resources/icons/unlock.png differ diff --git a/client/resources/stylesheet.qss b/client/resources/stylesheet.qss index 41cc5939..51cbfc93 100644 --- a/client/resources/stylesheet.qss +++ b/client/resources/stylesheet.qss @@ -10,35 +10,65 @@ QWidget{ /* QToolBar */ -QToolBar {background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 rgb(125,125,125), stop: 1.0 rgb(80,80,80)); spacing: 5px;} +QToolBar { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 rgb(125,125,125), stop: 1.0 rgb(80,80,80)); + spacing: 5px; +} /* QDockWidget */ -QDockWidget{titlebar-close-icon: url(:/pictures/close-red.png);font:bold large Arial; color:rgb(200,200,198);} -QDockWidget::title{border: darkgray solid 2px;text-align:left;background:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 rgb(64,64,64), stop: 1.0 rgb(40,40,40));padding-left:5px} -QDockWidget::close-button {icon-size:14px;background: rgba(0,0,0,0);} +QDockWidget{ + titlebar-close-icon: url(:/pictures/close-red.png); + font:bold large Arial; + color:rgb(200,200,198); +} +QDockWidget::title{ + text-align:left; + background:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 rgb(64,64,64), stop: 1.0 rgb(40,40,40)); + padding-left:5px +} +QDockWidget::close-button { + icon-size:14px; + background: rgba(0,0,0,0); +} +QWidget#dockWidgetContents{ + border: 0px solid transparent; +} /* QStatusBar */ -QStatusBar::item{border: 1px transparent black;} +QStatusBar::item{ + border: 1px transparent black; +} /* QMainWindow */ -QMainWindow::separator { background: rgb(29,29,29);width: 5px;height: 5px;} -QMainWindow::separator:hover {background: black;} -QMainWindow{background: #2c3338; } +QMainWindow::separator { + background: rgb(60,60,60); + width: 2px; + height: 5px; +} + +QMainWindow::separator:hover { + background: rgb(120,120,120); +} + +QMainWindow{ + background: #2c3338; +} /* QFrame */ -QFrame{background-color:rgba(29,29,29,50%);} - +QFrame{ + background-color:rgba(0,0,0,50%); +} /* TabWidget @@ -54,12 +84,19 @@ QTabWidget::tab-bar { } QTableWidget::tab, QHeaderView::section { - background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgba(128,128,140,50%), stop: 1.0 rgba(100,100,140,25%)); - border: 2px solid rgba(128,128,140,50%); + background: rgba(128,128,140,50%); /*qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgba(128,128,140,50%), stop: 1.0 rgba(100,100,140,25%));*/ + border: 1px solid rgba(128,128,140,50%); padding: 2px; min-height:25px; } +QTableWidget { + gridline-color: rgba(128,128,140,50%); +} + +QHeaderView{ + background-color: transparent; +} QTableWidget QTableCornerButton::section { background-color: rgb(64, 65, 74); @@ -77,30 +114,55 @@ QHeaderView::up-arrow { width: 12px; } +QTabBar{ + qproperty-drawBase: 0; +} + QTabBar::tab{ - background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgba(128,128,140,50%), stop: 1.0 rgba(100,100,140,25%)); - border: 2px solid rgba(128,128,140,50%); - padding: 2px; - min-height:25px; + border: 1px solid rgba(10, 100, 174, 50%); + background-color: #FF17365D; + padding: 5px; + min-height: 25px; color: white; + margin: 1px; +} + +QMainWindow QTabBar::tab:left{ + min-height: 120px; +} + +TeraForm QTabBar::tab{ + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgba(128,128,140,50%), stop: 1.0 rgba(100,100,140,25%)); + min-height: 15px; + border: 1px solid rgba(128,128,140,50%); +} + +TeraForm QTabBar::tab:top{ + border-top-left-radius: 5px; + border-top-right-radius: 5px; +} + +TeraForm QTabBar::tab:selected{ + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgba(220,220,220,70%), stop: 1.0 rgba(255,255,255,50%)); + color: black; } QTabBar::tab:top{ - border-top-left-radius: 4px; - border-top-right-radius: 4px; + border-top-left-radius: 10px; + border-top-right-radius: 10px; border-bottom: 0px; } QTabBar::tab:left{ - min-height:120px; - border-bottom-left-radius: 4px; - border-top-left-radius: 4px; + border-top-left-radius: 10px; + border-bottom-left-radius: 10px; + /*min-height:120px;*/ border-right: 0px; } QTabBar::tab:selected, QTabBar::tab:hover { - background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgba(220,220,220,70%), stop: 1.0 rgba(255,255,255,50%)); - color: black; + background-color: rgba(10, 100, 174, 100%); + color: white; } QTabBar::tab:selected { @@ -108,9 +170,9 @@ QTabBar::tab:selected { border-bottom-color:rgba(128,128,140,60%); } -QTabBar::tab:!selected { +/*QTabBar::tab:!selected { margin-top: 2px; -} +}*/ /* QGroupBox @@ -147,9 +209,6 @@ QToolBox{ } QToolBox::tab { - /*background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 rgba(0,5,45,50%), stop: 0.2 rgba(0,64,128,50%), stop:1 rgba(0,5,45,50%)); - border-radius: 5px; - color: darkgray;*/ border-top-left-radius: 15px; border-top-right-radius: 15px; padding: 5px; @@ -201,8 +260,12 @@ QLabel { QTreeWidget */ QTreeWidget, QTableWidget{ - selection-background-color: rgba(255,255,255,50%);/*rgba(0,5,45,50%)*/; - selection-color: black/*lightblue*/; + selection-background-color: rgba(23,54,93,70%); + selection-color: white; +} + +QTreeWidget::item::selected{ + background-color: rgba(23,54,93,70%); } QTreeWidget#treeNavigator { @@ -234,7 +297,6 @@ QLineEdit:!enabled, QSpinBox:!enabled, QTextEdit:!enabled, QDateTimeEdit:!enable background-color: rgba(0,0,0,0%); color:rgba(255,255,255,80%); padding-left: 0px; - color: white; } @@ -254,20 +316,29 @@ QPlainTextEdit { /* QListWidget */ -QListWidget { + +QListView { color: white; - selection-background-color: rgba(0,5,45,50%); - selection-color: cyan; + outline: 0; +} + +QListView::item{ + /*min-height: 24px;*/ +} + +QListView::item:selected { + color: cyan; + background-color: rgba(0,5,145,50%); } +QListView::item:hover { + color: white; + background-color: rgba(23,54,93,50%); +} /* QTextEdit, See QLineEdit */ -/*QTextEdit { - background-color: rgba(255,255,255,50%); - color: white; -}*/ /* QComboBox @@ -310,12 +381,11 @@ QComboBox::item{ color: white; } - /* QPushButton */ QPushButton{ - color:white; + color: white; background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #173448, stop: 1 #295d80);/*qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 black, stop: 0.2 #173448, stop:1 black);*/ border: 2px solid #15679e; border-radius: 5px; @@ -354,9 +424,7 @@ QPushButton[checkable=true]:!checked{ */ QToolButton{ color:white; - /* background-color: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 black, stop: 0.2 rgb(200,200,200), stop:1 black); */ background-color: transparent; - /*border: 2px solid rgb(96, 96, 96);*/ border: 0px transparent; border-radius: 5px; min-height: 25px; @@ -372,9 +440,7 @@ QToolButton:hover{ QToolButton[checkable=true]:!checked{ color:red; - /*background-color:rgba(180,0,0,50%);*/ background-color:rgba(100,100,100,50%); - /*border: 2px solid rgb(180,0,0);*/ border: 2px solid rgb(100,100,100); } @@ -400,7 +466,7 @@ QCheckBox::indicator { height: 32px; } -QListWidget::indicator, QTreeWidget::indicator, QTableWidget::indicator { +QListView::indicator, QTreeWidget::indicator, QTableWidget::indicator { width: 20px; height: 20px; } @@ -408,14 +474,14 @@ QListWidget::indicator, QTreeWidget::indicator, QTableWidget::indicator { QCheckBox::indicator:unchecked:!enabled { image: url(://controls/check_off_disabled.png); } -QListWidget::indicator:unchecked:!enabled, QTreeWidget::indicator:unchecked:!enabled, QTableWidget::indicator:unchecked:!enabled{ +QListView::indicator:unchecked:!enabled, QTreeWidget::indicator:unchecked:!enabled, QTableWidget::indicator:unchecked:!enabled{ image: url(://controls/check2_off_disabled.png); } QCheckBox::indicator:checked:!enabled{ image: url(://controls/check_on_disabled.png); } -QListWidget::indicator:checked:!enabled, QTreeWidget::indicator:checked:!enabled, QTableWidget::indicator:checked:!enabled { +QListView::indicator:checked:!enabled, QTreeWidget::indicator:checked:!enabled, QTableWidget::indicator:checked:!enabled { image: url(://controls/check2_on_disabled.png); } @@ -429,7 +495,7 @@ QListWidget::indicator:unchecked, QTreeWidget::indicator:unchecked, QTableWidget QCheckBox::indicator:checked{ image: url(://controls/check_on.png); } -QListWidget::indicator:checked, QTreeWidget::indicator:checked, QTableWidget::indicator:checked{ +QListView::indicator:checked, QTreeWidget::indicator:checked, QTableWidget::indicator:checked{ image: url(://controls/check2_on.png); } @@ -462,10 +528,14 @@ QRadioButton:!checked{color:red;background-color:rgba(0,0,0,0%);} QMenu { icon-size: 22px; font-size: 10pt; + background-color: #8817365D; + border: 1px solid gray; + border-radius: 2px; + padding: 4px; } QMenu::item{ - background: black; + } QMenu::item:selected{ @@ -476,48 +546,65 @@ QMenu::item:selected{ QCalendarWidget */ -QCalendarWidget QWidget#qt_calendar_navigationbar { background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop: 0 #cccccc, stop: 1 #333333); } - QCalendarWidget QToolButton { +/*QCalendarWidget QToolButton { height: 20px; color: white; font-size: 12px; - font-weight: bold; - icon-size: 20px, 20px; + font-weight: bold; background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop: 0 #cccccc, stop: 1 #333333); - } +} + QCalendarWidget QMenu { -left: 20px; -color: white; -font-size: 12px; -background-color: rgb(100, 100, 100); + left: 20px; + color: white; + font-size: 12px; + background-color: rgb(100, 100, 100); } QCalendarWidget QSpinBox { -font-size:12px; -color: white; -background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop: 0 #cccccc, stop: 1 #333333); -selection-background-color: rgb(136, 136, 136); -selection-color: rgb(255, 255, 255); + font-size:12px; + color: white; + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop: 0 #cccccc, stop: 1 #333333); + selection-background-color: rgb(136, 136, 136); + selection-color: rgb(255, 255, 255); +} + +QCalendarWidget QSpinBox::up-button { + subcontrol-origin: border; + subcontrol-position: top right; } -QCalendarWidget QSpinBox::up-button { subcontrol-origin: border; subcontrol-position: top right; } -QCalendarWidget QSpinBox::down-button {subcontrol-origin: border; subcontrol-position: bottom right; } -QCalendarWidget QSpinBox::up-arrow { width:16px; height:16px; } -QCalendarWidget QSpinBox::down-arrow { width:16px; height:16px; } +QCalendarWidget QSpinBox::down-button { + subcontrol-origin: border; + subcontrol-position: bottom right; +} + +QCalendarWidget QSpinBox::up-arrow { + width:16px; + height:16px; +} -QCalendarWidget QWidget { alternate-background-color: #0d5ca6; } +QCalendarWidget QSpinBox::down-arrow { + width:16px; + height:16px; +} +*/ +QCalendarWidget QWidget { + alternate-background-color: #0d5ca6; +} QCalendarWidget QAbstractItemView:enabled { -font-size:12px; -color: rgb(180, 180, 180); -background-color: black; -selection-background-color: rgb(0,0,0); -selection-color: rgb(0, 255, 0); + font-size:12px; + color: rgb(180, 180, 180); + background-color: black; + selection-background-color: rgb(0,0,0); + selection-color: rgb(0, 255, 0); } - -QCalendarWidget QAbstractItemView:disabled { color: rgb(64, 64, 64); } +QCalendarWidget QAbstractItemView:disabled { + color: rgb(64, 64, 64); +} /* QProgressBar */ QProgressBar{ @@ -566,7 +653,7 @@ QLabel#lblTitle{ color: cyan; } -QLabel#lblWarning{ +QLabel#lblWarning, QLabel#lblWarning2{ font-weight: bold; font-size: 14px; color: orange; @@ -577,15 +664,24 @@ QLabel#lblWarning{ QFrame#frameLogos{background-color:rgba(200,200,200,100%);} QLabel#lblMessage{color:white;} QFrame#frameMessage{background-color: transparent;} +QFrame#frameButtons{background-color:rgba(29,29,29,50%);} /* Customizations for MainWindow */ QMainWindow{background-image: url(://TeRA_Background.png); background-color: #2c3338;} QWidget#toolsWidget,QWidget#InToolsWidget{background-color:rgba(0,128,200,20%);} -QWidget#docketTopWidget{background-color: #2c3338;} -QFrame#frameMenu,QFrame#frameVideo{border: 1px solid rgba(150,150,150,50%);} +QWidget#dockerTopWidget{background-color: #2c3338;} + +QFrame#frameMenu, QFrame#frameVideo{ + border: 1px solid rgba(150,150,150,50%); +} +QWidget#tabProjectNavigator, QWidget#tabOnline, QWidget#tabSearch{ + border: 1px solid rgba(150,150,150,50%); + border-radius: 1px; +} QFrame#frameMessages{background-color:rgba(255,0,0,50%); border-radius:5px;} QToolButton#btnVideoSwitch, QToolButton#btnLog {color: white; min-height: 35px; background-color: qlineargradient(spread:reflect, x1:0.220183, y1:0.745192, x2:1, y2:0, stop:0.201835 rgba(0, 0, 0, 255), stop:0.880734 rgba(255, 255, 255, 255), stop:1 rgba(189, 189, 189, 255)); border-radius: 5px;} + /* Red buttons */ QPushButton#btnLogout { background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #8f1010, stop: 1 #cc1616);/*qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 rgba(140,10,10,100%), stop: 0.2 rgba(45,5,0,100%), stop:1 rgba(140,10,10,100%));*/ @@ -621,18 +717,20 @@ QPushButton#btnNewSession:disabled, QPushButton#btnStartSession:disabled { } QPushButton#btnEditUser,QPushButton#btnConfig { - background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgba(100,100,100,100%), stop: 1 rgba(148,148,148,100%)); /*rgba(100,100,100,50%); */ + background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgba(100,100,100,100%), stop: 1 rgba(148,148,148,100%)); border: 1px solid rgba(148,148,148,100%); min-width: 0px; } -QPushButton#btnEditUser::hover, QPushButton#btnConfig::hover{ - color:black; + +QPushButton#btnEditUser:hover,QPushButton#btnConfig:hover{ + color: black; } -QToolButton#btnVideo:!checked, QToolButton#btnLog:!checked, QToolButton#btnFilterActive:!checked, QToolButton#btnSearch:!checked{ +QToolButton#btnVideo:!checked, QToolButton#btnLog:!checked, QToolButton#btnFilterActive:!checked, QToolButton#btnSearch:!checked, QToolButton#btnAdvanced:!checked{ color:white; background-color: transparent; - border: 1px solid rgb(120,120,120); + /*border: 1px solid rgb(120,120,120);*/ + border: 0px solid transparent; border-radius: 5px; } @@ -642,7 +740,7 @@ QToolButton#btnLog:!checked, QToolButton#btnInSessionInfos:!checked, QToolButton border: 0px; } -QToolButton#btnVideo:checked, QToolButton#btnLog:checked, QToolButton#btnInSessionInfos:checked, QToolButton#btnSearch:checked, QToolButton#btnFilterActive:checked, QToolButton#btnPause:checked{ +QToolButton#btnVideo:checked, QToolButton#btnLog:checked, QToolButton#btnInSessionInfos:checked, QToolButton#btnSearch:checked, QToolButton#btnFilterActive:checked, QToolButton#btnPause:checked, QToolButton#btnAdvanced:checked{ color:black; background-color: gray; /*border: 2px solid white;*/ @@ -698,7 +796,7 @@ QToolButton#btnRandomPass{ } /* Customizations for SiteWidget */ -QLabel#lblInherited{ +QLabel#lblInherited, QLabel#lblAdminTestTypes, QLabel#lblAdminSessionTypes{ font-weight: bold; font-size: 12px; color: orange; @@ -740,7 +838,7 @@ QPushButton#btnFilterSessionsTypes[checkable=true]:!checked{ QPushButton#btnManageInvitees:checked, QPushButton#btnUpcomingSessions:checked, QPushButton#btnRecentParticipants:checked, QPushButton#btnAttention:checked, QPushButton#btnAdvancedConfig:checked{ color:white; - background-color: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 black, stop: 0.2 rgb(100,100,100), stop:1 black); + background-color: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 black, stop: 0.2 rgb(10,100,174), stop:1 black); border: 1px solid rgb(96, 96, 96); border-radius: 5px; text-align: left; @@ -766,7 +864,7 @@ QPushButton#btnUpcomingSessions[checkable=true]:!checked, QPushButton#btnRecentParticipants[checkable=true]:!checked, QPushButton#btnAttention[checkable=true]:!checked, QPushButton#btnAdvancedConfig[checkable=true]:!checked{ color:white; - background-color: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 rgb(100,100,100), stop: 0.2 black, stop:1 rgb(100,100,100)); + background-color: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 rgb(23,54,93), stop: 0.2 black, stop:1 rgb(23,54,93)); border: 2px solid transparent/*rgb(145, 145, 145)*/; border-radius: 5px; text-align: left; @@ -791,7 +889,7 @@ QTreeWidget#treeOnline{ /* Customizations for DashboardWidget */ QFrame#frameAttention, QFrame#frameRecent, QFrame#frameUpcomingSessions{ - background-color: rgba(100,100,100, 50%); + /*background-color: rgba(100,100,100, 50%);*/ } QLabel#lblNoUpcomingSessions, QLabel#lblNoRecentParticipants, QLabel#lblAttention{ @@ -799,6 +897,7 @@ QLabel#lblNoUpcomingSessions, QLabel#lblNoRecentParticipants, QLabel#lblAttentio font-size: 12px; color: orange; background-color: black; + border-radius: 15px; } /* Customizations for ResultMessageWidget */ @@ -817,6 +916,10 @@ QTreeWidget#treeAssets { /* Customizations for InSessionWidget */ QPushButton#btnDefaultPath, QPushButton#btnBrowseSavePath{ - max-width: 80px; - min-width: 80px; + max-width: 80px; + min-width: 80px; +} + +QWidget#line{ + background-color: #808080; } diff --git a/client/src/CMakeLists.txt b/client/src/CMakeLists.txt index 16d32106..70dac8be 100755 --- a/client/src/CMakeLists.txt +++ b/client/src/CMakeLists.txt @@ -1,10 +1,20 @@ -find_package(Qt5Core REQUIRED) -find_package(Qt5WebEngine REQUIRED) -find_package(Qt5WebEngineWidgets REQUIRED) -find_package(Qt5Multimedia REQUIRED) -find_package(Qt5WebSockets REQUIRED) -find_package(Qt5Network REQUIRED) -find_package(Qt5LinguistTools REQUIRED) +find_package(Qt6Core REQUIRED) + +if(NOT DEFINED OPENTERA_WEBASSEMBLY) + find_package(Qt6 REQUIRED COMPONENTS WebEngineCore WebEngineWidgets) +endif() + +if (MSVC) + add_definitions( + /wd4910 # Ignore export warnings (C4910) with MSVC + /wd4661 # Ignore no suitable definition for explicit template (C4661) + /wd4251 # Ignore needs to have dll-interface + /wd4146 # Ignore unary minus operator applied to unsigned type, result still unsigned + /wd4267 # Ignore size_t to uint32_t conversion warnings + ) +endif (MSVC) + +find_package(Qt6 REQUIRED COMPONENTS LinguistTools Multimedia WebSockets Network) # Drivers first add_subdirectory(drivers) @@ -21,9 +31,10 @@ set(headers data/TransferringFile.h data/UploadingFile.h data/IconMenuDelegate.h + # Librairies + libs/AudioVideoUtils.h # Main Windows main/MainWindow.h - main/MainKitWindow.h # Managers managers/ConfigManagerClient.h managers/ComManager.h @@ -33,12 +44,7 @@ set(headers managers/AssetComManager.h # Dialogs dialogs/LoginDialog.h - dialogs/StartSessionDialog.h - dialogs/SessionLobbyDialog.h - dialogs/JoinSessionDialog.h dialogs/EmailInviteDialog.h - dialogs/AboutDialog.h - dialogs/AboutDialogPage.h dialogs/GeneratePasswordDialog.h dialogs/PasswordStrengthDialog.h dialogs/TransferProgressDialog.h @@ -74,21 +80,10 @@ set(headers services/BaseServiceWidget.h services/BaseServiceToolsWidget.h services/BaseServiceSetupWidget.h - services/VideoRehabService/ScreenController.h - services/VideoRehabService/VideoRehabWidget.h - services/VideoRehabService/VideoRehabWebPage.h - services/VideoRehabService/VideoRehabSetupWidget.h - services/VideoRehabService/VideoRehabToolsWidget.h - services/VideoRehabService/VideoRehabVirtualCamSetupDialog.h - services/VideoRehabService/VideoRehabPTZDialog.h - services/VideoRehabService/WebSocket/SharedObject.h - services/VideoRehabService/WebSocket/WebSocketClientWrapper.h - services/VideoRehabService/WebSocket/WebSocketTransport.h services/DanceService/DanceConfigWidget.h services/DanceService/DanceWebAPI.h services/DanceService/DanceComManager.h # Widgets - widgets/InSessionWidget.h widgets/SessionInviteWidget.h widgets/OnlineManagerWidget.h widgets/ClickableLabel.h @@ -106,13 +101,40 @@ set(headers widgets/QRWidget.h widgets/LogViewWidget.h widgets/SessionsListWidget.h - # Kits - kit/KitConfigDialog.h - kit/KitConfigManager.h - kit/KitInSessionDialog.h - kit/KitVideoRehabWidget.h + ) +#Additional headers not for WebAssembly +if(NOT DEFINED OPENTERA_WEBASSEMBLY) + set(headers ${headers} + # Dialogs + dialogs/StartSessionDialog.h + dialogs/SessionLobbyDialog.h + dialogs/JoinSessionDialog.h + dialogs/AboutDialog.h + dialogs/AboutDialogPage.h + # Widgets + widgets/InSessionWidget.h + # Services + services/VideoRehabService/ScreenController.h + services/VideoRehabService/VideoRehabWidget.h + services/VideoRehabService/VideoRehabWebPage.h + services/VideoRehabService/VideoRehabSetupWidget.h + services/VideoRehabService/VideoRehabToolsWidget.h + services/VideoRehabService/VideoRehabVirtualCamSetupDialog.h + services/VideoRehabService/VideoRehabPTZDialog.h + services/VideoRehabService/WebSocket/SharedObject.h + services/VideoRehabService/WebSocket/WebSocketClientWrapper.h + services/VideoRehabService/WebSocket/WebSocketTransport.h + # Kits + kit/KitConfigDialog.h + kit/KitConfigManager.h + kit/KitInSessionDialog.h + kit/KitVideoRehabWidget.h + main/MainKitWindow.h + ) +endif() + set(srcs main.cpp ClientApp.cpp @@ -125,9 +147,10 @@ set(srcs data/TransferringFile.cpp data/UploadingFile.cpp data/IconMenuDelegate.cpp + # Librairies + libs/AudioVideoUtils.cpp # Main Windows main/MainWindow.cpp - main/MainKitWindow.cpp # Managers managers/ConfigManagerClient.cpp managers/ComManager.cpp @@ -140,17 +163,12 @@ set(srcs dialogs/DeviceAssignDialog.cpp dialogs/TransferProgressDialog.cpp dialogs/BaseDialog.cpp - dialogs/StartSessionDialog.cpp - dialogs/SessionLobbyDialog.cpp - dialogs/JoinSessionDialog.cpp dialogs/EmailInviteDialog.cpp - dialogs/AboutDialog.cpp - dialogs/AboutDialogPage.cpp dialogs/GeneratePasswordDialog.cpp dialogs/PasswordStrengthDialog.cpp dialogs/CleanUpDialog.cpp dialogs/FileUploaderDialog.cpp - dialogs/QRCodeDialog.cpp + dialogs/QRCodeDialog.cpp # Editors editors/DataEditorWidget.cpp editors/DataListWidget.cpp @@ -171,7 +189,6 @@ set(srcs editors/UserGroupWidget.cpp editors/UserSummaryWidget.cpp editors/UserWidget.cpp - # Wizards wizards/UserWizard.cpp wizards/BaseWizard.cpp @@ -179,16 +196,6 @@ set(srcs services/BaseServiceWidget.cpp services/BaseServiceToolsWidget.cpp services/BaseServiceSetupWidget.cpp - services/VideoRehabService/ScreenController.cpp - services/VideoRehabService/VideoRehabWidget.cpp - services/VideoRehabService/VideoRehabWebPage.cpp - services/VideoRehabService/VideoRehabSetupWidget.cpp - services/VideoRehabService/VideoRehabToolsWidget.cpp - services/VideoRehabService/VideoRehabVirtualCamSetupDialog.cpp - services/VideoRehabService/VideoRehabPTZDialog.cpp - services/VideoRehabService/WebSocket/SharedObject.cpp - services/VideoRehabService/WebSocket/WebSocketClientWrapper.cpp - services/VideoRehabService/WebSocket/WebSocketTransport.cpp services/DanceService/DanceConfigWidget.cpp services/DanceService/DanceComManager.cpp # Widgets @@ -197,7 +204,6 @@ set(srcs widgets/NotificationWindow.cpp widgets/ConfigWidget.cpp widgets/HistoryCalendarWidget.cpp - widgets/InSessionWidget.cpp widgets/SessionInviteWidget.cpp widgets/OnlineManagerWidget.cpp widgets/ClickableLabel.cpp @@ -210,13 +216,40 @@ set(srcs widgets/QRWidget.cpp widgets/LogViewWidget.cpp widgets/SessionsListWidget.cpp - # Kits - kit/KitConfigDialog.cpp - kit/KitConfigManager.cpp - kit/KitInSessionDialog.cpp - kit/KitVideoRehabWidget.cpp ) +#Additional srcs not for WebAssembly +if(NOT DEFINED OPENTERA_WEBASSEMBLY) + set(srcs ${srcs} + # Dialogs + dialogs/StartSessionDialog.cpp + dialogs/SessionLobbyDialog.cpp + dialogs/JoinSessionDialog.cpp + dialogs/AboutDialog.cpp + dialogs/AboutDialogPage.cpp + # Widgets + widgets/InSessionWidget.cpp + # Services + services/VideoRehabService/ScreenController.cpp + services/VideoRehabService/VideoRehabWidget.cpp + services/VideoRehabService/VideoRehabWebPage.cpp + services/VideoRehabService/VideoRehabSetupWidget.cpp + services/VideoRehabService/VideoRehabToolsWidget.cpp + services/VideoRehabService/VideoRehabVirtualCamSetupDialog.cpp + services/VideoRehabService/VideoRehabPTZDialog.cpp + services/VideoRehabService/WebSocket/SharedObject.cpp + services/VideoRehabService/WebSocket/WebSocketClientWrapper.cpp + services/VideoRehabService/WebSocket/WebSocketTransport.cpp + # Kits + kit/KitConfigDialog.cpp + kit/KitConfigManager.cpp + kit/KitInSessionDialog.cpp + kit/KitVideoRehabWidget.cpp + main/MainKitWindow.cpp + ) +endif() + + SET(uis # Main Windows main/MainWindow.ui @@ -287,31 +320,34 @@ SET(qrcs ) #Generate .h files from the .ui files -QT5_WRAP_UI(moc_uis ${uis}) +QT6_WRAP_UI(moc_uis ${uis}) #This will generate moc_* for Qt -QT5_WRAP_CPP(moc_srcs ${headers}) +QT6_WRAP_CPP(moc_srcs ${headers}) # generate rules for building source files from the resources -QT5_ADD_RESOURCES(client_qrc ${qrcs}) +QT6_ADD_RESOURCES(client_qrc ${qrcs}) + + set(translation_files_srcs - ${TERACLIENT_RES_INCLUDES}/translations/openteraplus_fr.ts - ${TERACLIENT_RES_INCLUDES}/translations/openteraplus_en.ts + ${TERACLIENT_RES_INCLUDES}/translations/openteraplus_fr.ts + ${TERACLIENT_RES_INCLUDES}/translations/openteraplus_en.ts ) + #Translation files, this is done manually to avoid removing the file when doing make clean # Add -noobsolete parameter to remove obsoleted translations add_custom_target(openteraplus_en_ts - COMMAND ${Qt5_LUPDATE_EXECUTABLE} -target-language en ${srcs} ${OPENTERA_SHARED_INCLUDES} ${headers} ${moc_uis} -ts ${TERACLIENT_RES_INCLUDES}/translations/openteraplus_en.ts - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - DEPENDS ${moc_uis} + COMMAND ${Qt6_LUPDATE_EXECUTABLE} -target-language en ${srcs} ${OPENTERA_SHARED_INCLUDES} ${headers} ${moc_uis} -ts ${TERACLIENT_RES_INCLUDES}/translations/openteraplus_en.ts + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + DEPENDS ${moc_uis} ) add_custom_target(openteraplus_fr_ts - COMMAND ${Qt5_LUPDATE_EXECUTABLE} -target-language fr ${srcs} ${OPENTERA_SHARED_INCLUDES} ${headers} ${moc_uis} -ts ${TERACLIENT_RES_INCLUDES}/translations/openteraplus_fr.ts - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - DEPENDS ${moc_uis} + COMMAND ${Qt6_LUPDATE_EXECUTABLE} -target-language fr ${srcs} ${OPENTERA_SHARED_INCLUDES} ${headers} ${moc_uis} -ts ${TERACLIENT_RES_INCLUDES}/translations/openteraplus_fr.ts + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + DEPENDS ${moc_uis} ) #set qm files output directory @@ -321,9 +357,9 @@ set_source_files_properties(${translation_files_srcs} PROPERTIES OUTPUT_LOCATION add_custom_target(translations DEPENDS openteraplus_en_ts openteraplus_fr_ts) add_custom_target(translation_files SOURCES ${translation_files_srcs}) - #Generate qm files from .ts files -qt5_add_translation(qm_files ${TERACLIENT_RES_INCLUDES}/translations/openteraplus_en.ts ${TERACLIENT_RES_INCLUDES}/translations/openteraplus_fr.ts) +qt6_add_translation(qm_files ${TERACLIENT_RES_INCLUDES}/translations/openteraplus_en.ts ${TERACLIENT_RES_INCLUDES}/translations/openteraplus_fr.ts) + message(STATUS "qm_files : ${qm_files}") @@ -334,11 +370,22 @@ include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/editors ${CMAKE_CURRENT_BINARY_DIR} - # DL Temporary test for virtual cameras ${VIRTUAL_CAMERA_INCLUDES} ${PTZ_DRIVERS_INCLUDES} ${AVKYS_INCLUDES} ) +if(WIN32) + set (OPENTERA_OS_LIBS + strmiids + uuid + ole32 + oleaut32 + shell32 + ) +else() + set (OPENTERA_OS_LIBS + ) +endif() # Application icon SET(icon openteraico.rc) @@ -357,13 +404,22 @@ set(ICON_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${MACOSX_BUNDLE_ICON_FILE}") # create an executable file named "OpenTeraPlus" from the source files # Put qrc files after qm files -add_executable(OpenTeraPlus MACOSX_BUNDLE WIN32 ${icon} ${srcs} ${headers} ${moc_srcs} ${moc_uis} ${ICON_PATH} ${qm_files} ${client_qrc}) -# qt5_use_modules(TeraClient Core Network Multimedia WebSockets WebEngine WebEngineWidgets WebSockets) +if(NOT DEFINED OPENTERA_WEBASSEMBLY) + add_executable(OpenTeraPlus MACOSX_BUNDLE WIN32 ${icon} ${srcs} ${headers} ${moc_srcs} ${moc_uis} ${ICON_PATH} ${qm_files} ${client_qrc}) +else() + qt_add_executable(OpenTeraPlus ${icon} ${srcs} ${headers} ${moc_srcs} ${moc_uis} ${ICON_PATH} ${qm_files} ${client_qrc}) +endif() + # Linking with Qt libraries and others -target_link_libraries(OpenTeraPlus ${OPENTERA_SHARED_LIBS} ${OPENTERA_MESSAGES_LIBS} Qt5::Core Qt5::Network Qt5::Multimedia Qt5::WebSockets Qt5::WebEngine Qt5::WebEngineWidgets) -target_link_libraries(OpenTeraPlus ${VIRTUAL_CAMERA_LIBS}) -target_link_libraries(OpenTeraPlus ${PTZ_DRIVERS_LIBS}) +target_link_libraries(OpenTeraPlus PRIVATE ${OPENTERA_SHARED_LIBS} ${OPENTERA_MESSAGES_LIBS} ${OPENTERA_OS_LIBS} Qt6::Core Qt6::Network Qt6::Multimedia Qt6::WebSockets) + +if(NOT DEFINED OPENTERA_WEBASSEMBLY) + target_link_libraries(OpenTeraPlus PRIVATE Qt6::WebEngineCore Qt6::WebEngineWidgets) +endif() + +target_link_libraries(OpenTeraPlus PRIVATE ${VIRTUAL_CAMERA_LIBS}) +target_link_libraries(OpenTeraPlus PRIVATE ${PTZ_DRIVERS_LIBS}) set_target_properties(OpenTeraPlus PROPERTIES @@ -385,15 +441,14 @@ set_source_files_properties(${ICON_PATH} PROPERTIES MACOSX_PACKAGE_LOCATION "Res if (APPLE) # Mac bundles... # Install targets - install(TARGETS OpenTeraPlus DESTINATION .) + install(TARGETS OpenTeraPlus RUNTIME DESTINATION .) # install(TARGETS opentera_messages DESTINATION .) else(APPLE) # Every other systems # Install target to bin - install(TARGETS OpenTeraPlus DESTINATION bin) - install(TARGETS opentera_messages DESTINATION bin) + install(TARGETS OpenTeraPlus RUNTIME DESTINATION bin) + install(TARGETS opentera_messages RUNTIME DESTINATION bin) endif(APPLE) - diff --git a/client/src/ClientApp.cpp b/client/src/ClientApp.cpp index 186c7e02..a7fbd16e 100755 --- a/client/src/ClientApp.cpp +++ b/client/src/ClientApp.cpp @@ -8,12 +8,14 @@ #include "data/GlobalEvent.h" ClientApp::ClientApp(int &argc, char **argv) - : QApplication(argc, argv) + : QApplication(argc, argv) { m_comMan = nullptr; m_loginDiag = nullptr; m_mainWindow = nullptr; +#ifndef OPENTERA_WEBASSEMBLY m_mainKitWindow = nullptr; +#endif m_translator = new QTranslator(); m_qt_translator = new QTranslator(); @@ -43,8 +45,10 @@ ClientApp::ClientApp(int &argc, char **argv) // Show login dialog showLogin(); }else{ +#ifndef OPENTERA_WEBASSEMBLY // Show main participant UI showMainKitWindow(); +#endif } } @@ -55,8 +59,8 @@ ClientApp::~ClientApp() delete m_loginDiag; if (m_comMan){ - m_comMan->disconnectFromServer(); - m_comMan->deleteLater(); + m_comMan->disconnectFromServer(); + m_comMan->deleteLater(); } delete m_translator; @@ -146,6 +150,7 @@ void ClientApp::showMainWindow() // Delete login window, if present if (m_loginDiag){ + m_loginDiag->hide(); m_loginDiag->deleteLater(); m_loginDiag = nullptr; } @@ -162,7 +167,7 @@ void ClientApp::showMainWindow() if (m_comMan->getCurrentUser().hasNameField()) processQueuedEvents(); } - +#ifndef OPENTERA_WEBASSEMBLY void ClientApp::showMainKitWindow() { if (m_mainKitWindow != nullptr){ @@ -171,7 +176,7 @@ void ClientApp::showMainKitWindow() m_mainKitWindow = new MainKitWindow(&m_config); } - +#endif void ClientApp::setupLogger() { @@ -206,46 +211,48 @@ void ClientApp::processQueuedEvents() void ClientApp::setTranslation(QString language) { - bool lang_changed = false; - QStringList supported_languages = {"fr", "en"}; - - if (language.isEmpty() || !supported_languages.contains(language.toLower())){ - //Set French as default - //m_currentLocale = QLocale(QLocale::French); - m_currentLocale = QLocale(); // Use system locale by default - lang_changed = true; - } - if (language.toLower() == "en" && m_currentLocale != QLocale::English){ - m_currentLocale = QLocale(QLocale::English); - lang_changed = true; - } - - if (language.toLower() == "fr" && m_currentLocale != QLocale::French){ - m_currentLocale = QLocale(QLocale::French); - lang_changed = true; - } - - //QLocale english = QLocale(QLocale::English); - //QLocale french = QLocale(QLocale::French); - //QLocale::setDefault(english); - //qDebug() << QLocale(); - - if (lang_changed){ - QLocale::setDefault(m_currentLocale); - - // Install Qt translator for default widgets - m_qt_translator->load("qt_" + m_currentLocale.name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath)); - this->installTranslator(m_qt_translator); - - // Install app specific translator - if (m_translator->load(m_currentLocale, QLatin1String("openteraplus"), QLatin1String("_"), QLatin1String(":/translations"))) { - this->installTranslator(m_translator); - //qDebug() << "Installed translator"; - } - - // Save last used language - TeraSettings::setGlobalSetting(SETTINGS_LASTLANGUAGE, language.toLower()); - } + bool lang_changed = false; + QStringList supported_languages = {"fr", "en"}; + + if (language.isEmpty() || !supported_languages.contains(language.toLower())){ + //Set French as default + //m_currentLocale = QLocale(QLocale::French); + m_currentLocale = QLocale(); // Use system locale by default + lang_changed = true; + } + if (language.toLower() == "en" && m_currentLocale != QLocale::English){ + m_currentLocale = QLocale(QLocale::English); + lang_changed = true; + } + + if (language.toLower() == "fr" && m_currentLocale != QLocale::French){ + m_currentLocale = QLocale(QLocale::French); + lang_changed = true; + } + + //QLocale english = QLocale(QLocale::English); + //QLocale french = QLocale(QLocale::French); + //QLocale::setDefault(english); + //qDebug() << QLocale(); + + if (lang_changed){ + QLocale::setDefault(m_currentLocale); + + // Install Qt translator for default widgets + if (m_qt_translator->load("qt_" + m_currentLocale.name(), QLibraryInfo::path(QLibraryInfo::TranslationsPath))) + { + this->installTranslator(m_qt_translator); + } + + // Install app specific translator + if (m_translator->load(m_currentLocale, QLatin1String("openteraplus"), QLatin1String("_"), QLatin1String(":/translations"))) { + this->installTranslator(m_translator); + //qDebug() << "Installed translator"; + } + + // Save last used language + TeraSettings::setGlobalSetting(SETTINGS_LASTLANGUAGE, language.toLower()); + } } @@ -327,21 +334,21 @@ void ClientApp::on_networkError(QNetworkReply::NetworkError error, QString error if (m_loginDiag){ switch(error){ - case QNetworkReply::ConnectionRefusedError: - error_str = tr("La connexion a été refusée par le serveur."); + case QNetworkReply::ConnectionRefusedError: + error_str = tr("La connexion a été refusée par le serveur."); break; - case QNetworkReply::AuthenticationRequiredError: - //error_str = tr("Impossible de négocier l'authentification avec le serveur"); - return; + case QNetworkReply::AuthenticationRequiredError: + //error_str = tr("Impossible de négocier l'authentification avec le serveur"); + return; break; - case QNetworkReply::TimeoutError: - error_str = tr("Impossible de rejoindre le serveur."); + case QNetworkReply::TimeoutError: + error_str = tr("Impossible de rejoindre le serveur."); break; - case QNetworkReply::HostNotFoundError: - error_str = tr("Le serveur est introuvable."); + case QNetworkReply::HostNotFoundError: + error_str = tr("Le serveur est introuvable."); break; - default: - error_str = tr("Impossible de se connecter (Code erreur: ") + QString::number(status_code) + " " + error_str + ")"; + default: + error_str = tr("Impossible de se connecter (Code erreur: ") + QString::number(status_code) + " " + error_str + ")"; } //Remove \n from error_str diff --git a/client/src/ClientApp.h b/client/src/ClientApp.h index 92132023..ae14b5ec 100755 --- a/client/src/ClientApp.h +++ b/client/src/ClientApp.h @@ -11,7 +11,9 @@ #include #include "main/MainWindow.h" +#ifndef OPENTERA_WEBASSEMBLY #include "main/MainKitWindow.h" +#endif #include "dialogs/LoginDialog.h" #include "GlobalMessageBox.h" @@ -24,8 +26,7 @@ class ClientApp : public QApplication Q_OBJECT public: ClientApp(int &argc, char** argv); - ~ClientApp(); - + virtual ~ClientApp() override; ComManager *getComManager(); protected: @@ -33,7 +34,9 @@ class ClientApp : public QApplication void connectSignals(); void showLogin(); void showMainWindow(); +#ifndef OPENTERA_WEBASSEMBLY void showMainKitWindow(); +#endif void setupLogger(); void processQueuedEvents(); @@ -43,7 +46,9 @@ class ClientApp : public QApplication ConfigManagerClient m_config; LoginDialog* m_loginDiag; MainWindow* m_mainWindow; +#ifndef OPENTERA_WEBASSEMBLY MainKitWindow* m_mainKitWindow; +#endif QList m_eventQueue; // Queue to stack missed events when just connected, but no MainWindow yet. diff --git a/client/src/GlobalMessageBox.cpp b/client/src/GlobalMessageBox.cpp index 1587e4e0..fccedb4b 100644 --- a/client/src/GlobalMessageBox.cpp +++ b/client/src/GlobalMessageBox.cpp @@ -17,6 +17,7 @@ GlobalMessageBox::GlobalMessageBox(QWidget *parent) : "QPushButton:hover{background-color: rgba(255,255,255,75%);color:black;}");*/ m_xPressed=false; + setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); } GlobalMessageBox::~GlobalMessageBox() @@ -25,23 +26,24 @@ GlobalMessageBox::~GlobalMessageBox() } QMessageBox::StandardButton GlobalMessageBox::showYesNo(const QString &title, const QString &text){ - - /* QPixmap image; - image.load(":/pictures/UnknownSystem.png"); - - setIconPixmap(image.scaled(48,48));*/ setIcon(QMessageBox::Question); setWindowTitle(title); setText(text); setModal(true); + setStandardButtons(QMessageBox::Yes | QMessageBox::No); - setButtonText(QMessageBox::Yes,tr("Oui")); - setButtonText(QMessageBox::No,tr("Non")); + //setButtonText(QMessageBox::Yes,tr("Oui")); + //setButtonText(QMessageBox::No,tr("Non")); button(QMessageBox::Yes)->setIcon(QIcon("://icons/ok.png")); button(QMessageBox::Yes)->setCursor(Qt::PointingHandCursor); + button(QMessageBox::Yes)->setText(tr("Oui")); + button(QMessageBox::No)->setIcon(QIcon("://icons/error.png")); button(QMessageBox::No)->setCursor(Qt::PointingHandCursor); + button(QMessageBox::No)->setText(tr("Non")); + + setDefaultButton(QMessageBox::No); exec(); diff --git a/client/src/data/DownloadingFile.cpp b/client/src/data/DownloadingFile.cpp index bf294013..d5f7913b 100644 --- a/client/src/data/DownloadingFile.cpp +++ b/client/src/data/DownloadingFile.cpp @@ -69,7 +69,7 @@ void DownloadingFile::onDownloadDataReceived() abortTransfer(); return; }*/ - for (const QString &info: qAsConst(header_info_parts)){ + for (const QString &info: std::as_const(header_info_parts)){ if (info.trimmed().startsWith("filename=")){ QStringList file_parts = info.split("="); if (file_parts.count() == 2) diff --git a/client/src/dialogs/AboutDialog.cpp b/client/src/dialogs/AboutDialog.cpp index b9c6495e..f35497ae 100644 --- a/client/src/dialogs/AboutDialog.cpp +++ b/client/src/dialogs/AboutDialog.cpp @@ -25,6 +25,7 @@ AboutDialog::AboutDialog(QUrl server_url, QWidget *parent) : QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); m_webEngine->setSizePolicy(sizePolicy); ui->wdgWebView->layout()->addWidget(m_webEngine); + ui->wdgWebView->setFocus(); } AboutDialog::~AboutDialog() @@ -42,6 +43,8 @@ void AboutDialog::on_btnOk_clicked() void AboutDialog::on_lblAbout_clicked() { if (m_webEngine){ - m_webEngine->setUrl(QUrl("chrome://dino")); + m_webEngine->setUrl(QUrl("chrome://dino")); + ui->wdgWebView->setFocus(); + } } diff --git a/client/src/dialogs/AboutDialog.ui b/client/src/dialogs/AboutDialog.ui index 864e98a2..95b77983 100644 --- a/client/src/dialogs/AboutDialog.ui +++ b/client/src/dialogs/AboutDialog.ui @@ -33,6 +33,9 @@ 0 + + Qt::StrongFocus + @@ -74,6 +77,9 @@ 24 + + false + @@ -86,6 +92,7 @@ QLabel
widgets/ClickableLabel.h
+ clicked() clicked() diff --git a/client/src/dialogs/AboutDialogPage.cpp b/client/src/dialogs/AboutDialogPage.cpp index 6c374ed4..d473e1ac 100644 --- a/client/src/dialogs/AboutDialogPage.cpp +++ b/client/src/dialogs/AboutDialogPage.cpp @@ -1,13 +1,17 @@ #include "AboutDialogPage.h" +#include AboutDialogPage::AboutDialogPage() { - + //TODO Do something about certificate errors. + connect(this, &QWebEnginePage::certificateError, this, &AboutDialogPage::onCertificateError); } -bool AboutDialogPage::certificateError(const QWebEngineCertificateError &certificateError) +void AboutDialogPage::onCertificateError(const QWebEngineCertificateError &certificateError) { - Q_UNUSED(certificateError) - - return true; // Accept all certificates + //TODO do Something about certificates + qDebug() << "Certificate error: " << certificateError.description(); + //TODO Do not accept certificates in production ? + auto mutableError = const_cast(certificateError); + mutableError.acceptCertificate(); } diff --git a/client/src/dialogs/AboutDialogPage.h b/client/src/dialogs/AboutDialogPage.h index 59d0910f..c8bb73c8 100644 --- a/client/src/dialogs/AboutDialogPage.h +++ b/client/src/dialogs/AboutDialogPage.h @@ -10,8 +10,9 @@ class AboutDialogPage : public QWebEnginePage public: AboutDialogPage(); -protected: - virtual bool certificateError(const QWebEngineCertificateError &certificateError) override; +protected slots: + + void onCertificateError(const QWebEngineCertificateError &certificateError); }; #endif // ABOUTDIALOGPAGE_H diff --git a/client/src/dialogs/BaseDialog.cpp b/client/src/dialogs/BaseDialog.cpp index bab5b60f..5b739582 100644 --- a/client/src/dialogs/BaseDialog.cpp +++ b/client/src/dialogs/BaseDialog.cpp @@ -8,7 +8,7 @@ BaseDialog::BaseDialog(QWidget *parent) m_ui.setupUi(this); m_centralWidgetLayout = new QVBoxLayout(m_ui.centralWidget); - m_centralWidgetLayout->setMargin(0); + //m_centralWidgetLayout->setMargin(0); m_centralWidgetLayout->setContentsMargins(0,0,0,0); // setWindowFlags(Qt::Dialog | Qt::WindowTitleHint | Qt::CustomizeWindowHint | Qt::FramelessWindowHint); diff --git a/client/src/dialogs/CleanUpDialog.cpp b/client/src/dialogs/CleanUpDialog.cpp index 1c55c81d..bbc15974 100644 --- a/client/src/dialogs/CleanUpDialog.cpp +++ b/client/src/dialogs/CleanUpDialog.cpp @@ -133,11 +133,11 @@ void CleanUpDialog::refreshData() ui->tableItems->clearContents(); m_itemsMap.clear(); - for(const QVariant &data:qAsConst(m_data)){ + for(const QVariant &data:std::as_const(m_data)){ QVariantMap data_map = data.toMap(); int current_row = ui->tableItems->rowCount(); ui->tableItems->setRowCount(ui->tableItems->rowCount()+1); - for (const QVariant &data_value: qAsConst(data_map)){ + for (const QVariant &data_value: std::as_const(data_map)){ QString data_key = data_map.key(data_value); if (m_columnsMap.contains(data_key)){ QTableWidgetItem* item; @@ -163,7 +163,7 @@ void CleanUpDialog::refreshData() QFrame* action_frame = new QFrame(); QHBoxLayout* layout = new QHBoxLayout(); layout->setContentsMargins(0,0,0,0); - layout->setAlignment(Qt::AlignLeft); + layout->setAlignment(Qt::AlignHCenter); action_frame->setLayout(layout); // Delete button @@ -172,7 +172,7 @@ void CleanUpDialog::refreshData() btnDisable->setIcon(QIcon("://controls/check_off.png")); btnDisable->setProperty("id", m_itemsMap[ui->tableItems->item(current_row,0)]); btnDisable->setCursor(Qt::PointingHandCursor); - btnDisable->setMaximumWidth(32); + btnDisable->setMaximumWidth(48); btnDisable->setToolTip(tr("Désactiver")); connect(btnDisable, &QToolButton::clicked, this, &CleanUpDialog::btnDisable_clicked); layout->addWidget(btnDisable); @@ -184,7 +184,7 @@ void CleanUpDialog::refreshData() btnDelete->setIcon(QIcon(":/icons/delete_old.png")); btnDelete->setProperty("id", m_itemsMap[ui->tableItems->item(current_row,0)]); btnDelete->setCursor(Qt::PointingHandCursor); - btnDelete->setMaximumWidth(32); + btnDelete->setMaximumWidth(48); btnDelete->setToolTip(tr("Supprimer")); connect(btnDelete, &QToolButton::clicked, this, &CleanUpDialog::btnDelete_clicked); layout->addWidget(btnDelete); @@ -289,7 +289,7 @@ void CleanUpDialog::on_btnDisableAll_clicked() answer = diag.showYesNo(tr("Désactivation?"), tr("Êtes-vous sûrs de vouloir tout désactiver?")); if (answer == QMessageBox::Yes){ - for(int id:qAsConst(m_itemsMap)){ + for(int id:std::as_const(m_itemsMap)){ m_idsToManage.append(id); } disableNextItem(); @@ -304,7 +304,7 @@ void CleanUpDialog::on_btnDeleteAll_clicked() answer = diag.showYesNo(tr("Suppression?"), tr("Êtes-vous sûrs de vouloir tout supprimer?")); if (answer == QMessageBox::Yes){ - for(int id:qAsConst(m_itemsMap)){ + for(int id:std::as_const(m_itemsMap)){ m_idsToManage.append(id); } deleteNextItem(); diff --git a/client/src/dialogs/FileUploaderDialog.cpp b/client/src/dialogs/FileUploaderDialog.cpp index 5f12d7bf..342b6013 100644 --- a/client/src/dialogs/FileUploaderDialog.cpp +++ b/client/src/dialogs/FileUploaderDialog.cpp @@ -54,7 +54,7 @@ void FileUploaderDialog::on_btnAddFile_clicked() QStringList files_to_upload = dlg.getOpenFileNames(this, tr("Choisir le(s) fichier(s) à envoyer"), m_current_base_path, m_file_pattern); - for (const QString &file:qAsConst(files_to_upload)){ + for (const QString &file:std::as_const(files_to_upload)){ QFileInfo file_info(file); QString filename = file_info.fileName(); QString default_label = filename.left(filename.length() - filename.split(".").last().length() - 1); diff --git a/client/src/dialogs/JoinSessionDialog.cpp b/client/src/dialogs/JoinSessionDialog.cpp index f2e79317..86eec8ad 100644 --- a/client/src/dialogs/JoinSessionDialog.cpp +++ b/client/src/dialogs/JoinSessionDialog.cpp @@ -1,5 +1,6 @@ #include "JoinSessionDialog.h" #include "ui_JoinSessionDialog.h" +#include JoinSessionDialog::JoinSessionDialog(ComManager *comMan, opentera::protobuf::JoinSessionEvent event, QWidget *parent) : QDialog(parent), @@ -65,7 +66,9 @@ void JoinSessionDialog::initUi() ui->lblInviteMsg->setText(tr("L'invitation comporte le message suivant:
") + QString::fromStdString(m_event.join_msg()) + ""); } - QSound::play("://sounds/notify_invite.wav"); + m_soundPlayer.setSource(QUrl::fromLocalFile("://sounds/notify_invite.wav")); + m_soundPlayer.setVolume(0.25f); + m_soundPlayer.play(); } void JoinSessionDialog::connectSignals() diff --git a/client/src/dialogs/JoinSessionDialog.h b/client/src/dialogs/JoinSessionDialog.h index 7a260a7b..71713891 100644 --- a/client/src/dialogs/JoinSessionDialog.h +++ b/client/src/dialogs/JoinSessionDialog.h @@ -2,8 +2,7 @@ #define JOINSESSIONDIALOG_H #include -#include - +#include #include "managers/ComManager.h" // Protobuf @@ -30,7 +29,7 @@ class JoinSessionDialog : public QDialog int getSessionId(); - ~JoinSessionDialog(); + virtual ~JoinSessionDialog() override; private slots: void on_btnJoinSession_clicked(); @@ -51,6 +50,7 @@ private slots: TeraData m_session; TeraData m_sessionType; + QSoundEffect m_soundPlayer; void initUi(); void connectSignals(); diff --git a/client/src/dialogs/LoginDialog.ui b/client/src/dialogs/LoginDialog.ui index 397cb6f3..b76fe565 100644 --- a/client/src/dialogs/LoginDialog.ui +++ b/client/src/dialogs/LoginDialog.ui @@ -439,7 +439,7 @@ QGroupBox#grpLogos{background-color:rgba(200,200,200,200);} - :/controls/branch_closed.png:/controls/branch_closed.png + :/icons/play.png:/icons/play.png diff --git a/client/src/dialogs/SessionLobbyDialog.cpp b/client/src/dialogs/SessionLobbyDialog.cpp index 9dafc5a2..281c2951 100644 --- a/client/src/dialogs/SessionLobbyDialog.cpp +++ b/client/src/dialogs/SessionLobbyDialog.cpp @@ -12,6 +12,8 @@ SessionLobbyDialog::SessionLobbyDialog(ComManager* comManager, TeraData &session ui->setupUi(this); m_setupWdg = nullptr; + setModal(true); + ui->lblTitle->setText(m_sessionType.getFieldValue("session_type_name").toString()); connectSignals(); @@ -43,37 +45,16 @@ void SessionLobbyDialog::addDevicesToSession(const QList &devices, con QStringList SessionLobbyDialog::getSessionParticipantsUuids() { - /*QStringList uuids; - QList participants = ui->wdgSessionInvite->getParticipantsInSession(); - - foreach(TeraData part, participants){ - uuids.append(part.getUuid()); - } - return uuids;*/ return ui->wdgSessionInvite->getParticipantsUuidsInSession(); } QStringList SessionLobbyDialog::getSessionUsersUuids() { - /*QStringList uuids; - QList users = ui->wdgSessionInvite->getUsersInSession(); - - foreach(TeraData user, users){ - uuids.append(user.getUuid()); - } - return uuids;*/ return ui->wdgSessionInvite->getUsersUuidsInSession(); } QStringList SessionLobbyDialog::getSessionDevicesUuids() { - /*QStringList uuids; - QList devices = ui->wdgSessionInvite->getDevicesInSession(); - - foreach(TeraData device, devices){ - uuids.append(device.getUuid()); - } - return uuids;*/ return ui->wdgSessionInvite->getDevicesUuidsInSession(); } @@ -266,7 +247,7 @@ void SessionLobbyDialog::processSessionsReply(QList sessions) if (session.hasFieldName("session_participants")){ item_list = session.getFieldValue("session_participants").toList(); - for(const QVariant &session_part:qAsConst(item_list)){ + for(const QVariant &session_part:std::as_const(item_list)){ QVariantMap part_info = session_part.toMap(); ui->wdgSessionInvite->addRequiredParticipant(part_info["id_participant"].toInt()); } @@ -275,7 +256,7 @@ void SessionLobbyDialog::processSessionsReply(QList sessions) if (session.hasFieldName("session_users")){ item_list = session.getFieldValue("session_users").toList(); - for(const QVariant &session_user:qAsConst(item_list)){ + for(const QVariant &session_user:std::as_const(item_list)){ QVariantMap user_info = session_user.toMap(); ui->wdgSessionInvite->addRequiredUser(user_info["id_user"].toInt()); } @@ -284,7 +265,7 @@ void SessionLobbyDialog::processSessionsReply(QList sessions) if (session.hasFieldName("session_devices")){ item_list = session.getFieldValue("session_devices").toList(); - for(const QVariant &session_device:qAsConst(item_list)){ + for(const QVariant &session_device:std::as_const(item_list)){ QVariantMap device_info = session_device.toMap(); ui->wdgSessionInvite->addRequiredDevice(device_info["id_device"].toInt()); } diff --git a/client/src/dialogs/TransferProgressDialog.cpp b/client/src/dialogs/TransferProgressDialog.cpp index fb1d6187..8cf9aba0 100644 --- a/client/src/dialogs/TransferProgressDialog.cpp +++ b/client/src/dialogs/TransferProgressDialog.cpp @@ -177,10 +177,10 @@ void TransferProgressDialog::on_btnCancel_clicked() // Set aborting flag to prevent further updates m_aborting = true; - /*for(QTableWidgetItem* item:qAsConst(m_files)){ + /*for(QTableWidgetItem* item:std::as_const(m_files)){ m_files.key(item)->abortTransfer(); } - for(TransferringFile* file:qAsConst(m_waitingFiles)){ + for(TransferringFile* file:std::as_const(m_waitingFiles)){ file->abortTransfer(); }*/ emit transferAbortRequested(); diff --git a/client/src/drivers/PTZ/CMakeLists.txt b/client/src/drivers/PTZ/CMakeLists.txt index 87c78e0f..2e122805 100644 --- a/client/src/drivers/PTZ/CMakeLists.txt +++ b/client/src/drivers/PTZ/CMakeLists.txt @@ -1,8 +1,7 @@ -find_package(Qt5Core REQUIRED) -find_package(Qt5Widgets REQUIRED) -find_package(Qt5LinguistTools REQUIRED) -find_package(Qt5Xml REQUIRED) -find_package(Qt5Network REQUIRED) +find_package(Qt6Core REQUIRED) +find_package(Qt6Widgets REQUIRED) +find_package(Qt6Xml REQUIRED) +find_package(Qt6Network REQUIRED) set(PTZDrivers_headers ICameraDriver.h @@ -34,18 +33,18 @@ include_directories( ) #Generate .h files from the .ui files -QT5_WRAP_UI(moc_uis ${PTZDrivers_uis}) +QT6_WRAP_UI(moc_uis ${PTZDrivers_uis}) # generate rules for building source files from the resources -QT5_ADD_RESOURCES(ptz_drivers_qrc ${PTZDrivers_qrcs}) +QT6_ADD_RESOURCES(ptz_drivers_qrc ${PTZDrivers_qrcs}) #This will generate moc_* for Qt -qt5_wrap_cpp(ptz_drivers_moc_srcs ${PTZDrivers_headers}) +qt6_wrap_cpp(ptz_drivers_moc_srcs ${PTZDrivers_headers}) add_library(PTZDrivers STATIC ${PTZDrivers_headers} ${PTZDrivers_srcs} ${ptz_drivers_qrc} ${ptz_drivers_moc_srcs} ${moc_uis}) -target_link_libraries(PTZDrivers Qt5::Core Qt5::Widgets Qt5::Xml Qt5::Network) +target_link_libraries(PTZDrivers Qt6::Core Qt6::Widgets Qt6::Xml Qt6::Network) set(PTZ_DRIVERS_LIBS PTZDrivers CACHE INTERNAL "doc string") set(PTZ_DRIVERS_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR} CACHE INTERNAL "doc string") diff --git a/client/src/drivers/VirtualCamera/CMakeLists.txt b/client/src/drivers/VirtualCamera/CMakeLists.txt index 10e7c507..576d88c7 100644 --- a/client/src/drivers/VirtualCamera/CMakeLists.txt +++ b/client/src/drivers/VirtualCamera/CMakeLists.txt @@ -1,8 +1,8 @@ -find_package(Qt5Core REQUIRED) -find_package(Qt5LinguistTools REQUIRED) -find_package(Qt5Qml REQUIRED) -find_package(Qt5Concurrent REQUIRED) -find_package(Qt5Quick REQUIRED) +find_package(Qt6Core REQUIRED) +find_package(Qt6LinguistTools REQUIRED) +find_package(Qt6Qml REQUIRED) +find_package(Qt6Concurrent REQUIRED) +find_package(Qt6Quick REQUIRED) set(VirtualCamera_headers VirtualCamera.h @@ -25,17 +25,18 @@ include_directories( ${CMAKE_CURRENT_BINARY_DIR} ) -message(STATUS "PLUGIN_DIRECTORY: ${AVKYS_PLUGIN_DIRECTORY}") +message(STATUS "Virtual Camera AVKYS PLUGIN_DIRECTORY: ${AVKYS_PLUGIN_DIRECTORY}") +message(STATUS "Virtual Camera AVKYS INCLUDE_DIRECTORY: ${AVKYS_INCLUDES}") # generate rules for building source files from the resources -QT5_ADD_RESOURCES(virtual_camera_qrc ${VirtualCamera_qrcs}) +QT6_ADD_RESOURCES(virtual_camera_qrc ${VirtualCamera_qrcs}) #This will generate moc_* for Qt -qt5_wrap_cpp(virtual_camera_moc_srcs ${VirtualCamera_headers}) +qt_wrap_cpp(virtual_camera_moc_srcs ${VirtualCamera_headers}) add_definitions(-DAVKYS_PLUGIN_DIRECTORY="${AVKYS_PLUGIN_DIRECTORY}") add_library(VirtualCameraDriver STATIC ${VirtualCamera_headers} ${VirtualCamera_srcs} ${virtual_camera_qrc} ${virtual_camera_moc_srcs}) -target_link_libraries(VirtualCameraDriver Qt5::Core Qt5::Qml Qt5::Concurrent Qt5::Quick ${AVKYS_LIBS}) +target_link_libraries(VirtualCameraDriver Qt6::Core Qt6::Qml Qt6::Concurrent Qt6::Quick ${AVKYS_LIBS}) set(VIRTUAL_CAMERA_LIBS VirtualCameraDriver CACHE INTERNAL "doc string") set(VIRTUAL_CAMERA_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR} CACHE INTERNAL "doc string") diff --git a/client/src/editors/DataEditorWidget.cpp b/client/src/editors/DataEditorWidget.cpp index b1d34631..1ecbd20e 100644 --- a/client/src/editors/DataEditorWidget.cpp +++ b/client/src/editors/DataEditorWidget.cpp @@ -256,6 +256,10 @@ QString DataEditorWidget::getRoleName(const QString &role) return tr("Administrateur"); if (role == "user") return tr("Utilisateur"); + if (role == "manager") + return tr("Gestionnaire"); + if (role == "editor") + return tr("Éditeur"); if (role == "") return tr("Aucun rôle"); return role; @@ -331,11 +335,15 @@ void DataEditorWidget::deleteDataReplyOK(const QString &path, const int &id) setReady(); } -void DataEditorWidget::comDataError(QNetworkReply::NetworkError error, QString error_str) +void DataEditorWidget::comDataError(QNetworkReply::NetworkError error, QString error_str, QNetworkAccessManager::Operation op, int status_code, QString path, QUrlQuery url_query) { Q_UNUSED(error) Q_UNUSED(error_str) + QString query_name = getQueryDataName(path, url_query); + m_requests.removeOne(query_name); setReady(); + if (!hasPendingDataRequests()) + updateFieldsValue(); } void DataEditorWidget::editToggleClicked() @@ -358,7 +366,7 @@ void DataEditorWidget::saveButtonClicked() if (!invalids.isEmpty()){ QString msg = tr("Les champs suivants doivent être complétés:") +"
    "; - for (const QString &field:qAsConst(invalids)){ + for (const QString &field:std::as_const(invalids)){ msg += "
  • " + field + "
  • "; } msg += "
"; diff --git a/client/src/editors/DataEditorWidget.h b/client/src/editors/DataEditorWidget.h index 077c6f35..31769765 100644 --- a/client/src/editors/DataEditorWidget.h +++ b/client/src/editors/DataEditorWidget.h @@ -16,7 +16,7 @@ class DataEditorWidget : public QWidget Q_OBJECT public: explicit DataEditorWidget(ComManager *comMan, const TeraData* data = nullptr, QWidget *parent = nullptr); - ~DataEditorWidget(); + ~DataEditorWidget() override; enum EditorState{ STATE_READY=0, @@ -110,7 +110,7 @@ private slots: void queryDataReplyOK(const QString &path, const QUrlQuery &query_args); void postDataReplyOK(const QString &path); void deleteDataReplyOK(const QString &path, const int &id); - void comDataError(QNetworkReply::NetworkError error, QString error_str); + void comDataError(QNetworkReply::NetworkError error, QString error_str, QNetworkAccessManager::Operation op, int status_code, QString path, QUrlQuery url_query); protected slots: virtual void editToggleClicked(); diff --git a/client/src/editors/DataListWidget.cpp b/client/src/editors/DataListWidget.cpp index 0d6089f8..56d9ffd4 100644 --- a/client/src/editors/DataListWidget.cpp +++ b/client/src/editors/DataListWidget.cpp @@ -109,7 +109,7 @@ void DataListWidget::updateDataInList(TeraData* data, bool select_item){ // If we have extra fields to display, append them QString extra_field = ""; if (!m_extraDisplayFields.isEmpty() && !m_extraInfos.contains(data->getId())){ - for (QString field:qAsConst(m_extraDisplayFields)){ + for (QString field:std::as_const(m_extraDisplayFields)){ QStringList subfields = field.split("."); QString subfield; if (subfields.count() > 1){ @@ -121,22 +121,26 @@ void DataListWidget::updateDataInList(TeraData* data, bool select_item){ extra_field += ", "; QVariant field_value = data->getFieldValue(field); - if (field_value.canConvert(QMetaType::QVariantList)){ - QVariantList field_values = field_value.toList(); - QString merged_list; - for (QVariant field_value:qAsConst(field_values)){ - if (!merged_list.isEmpty()) - merged_list += ", "; - // Search for subfield? - if (field_value.canConvert(QMetaType::QVariantMap)){ - QVariantMap field_map = field_value.toMap(); - field_value = field_map[subfield]; + if (field_value.typeId() == QMetaType::QString){ + extra_field += field_value.toString(); + }else{ + if (field_value.canConvert()){ + QVariantList field_values = field_value.toList(); + QString merged_list; + for (QVariant field_value:std::as_const(field_values)){ + if (!merged_list.isEmpty()) + merged_list += ", "; + // Search for subfield? + if (field_value.canConvert()){ + QVariantMap field_map = field_value.toMap(); + field_value = field_map[subfield]; + } + merged_list += field_value.toString(); } - merged_list += field_value.toString(); + extra_field += merged_list; + }else{ + extra_field += field_value.toString(); } - extra_field += merged_list; - }else{ - extra_field += data->getFieldValue(field).toString(); } } } @@ -224,7 +228,7 @@ void DataListWidget::showEditor(TeraData *data) m_editor = new UserWidget(m_comManager, data); break; case TERADATA_SITE: - m_editor = new SiteWidget(m_comManager, data); + m_editor = new SiteWidget(m_comManager, data, true); break; case TERADATA_DEVICE: m_editor = new DeviceWidget(m_comManager, data); @@ -341,7 +345,7 @@ QListWidgetItem *DataListWidget::getItemForData(TeraData *data) // Less simple case - the pointers are not the same, but we might be referencing an object already present. if (!data->isNew()){ - for (QListWidgetItem* item: qAsConst(m_datamap)){ + for (QListWidgetItem* item: std::as_const(m_datamap)){ TeraData* current_data = m_datamap.key(item); /*} for (TeraData* current_data:m_datamap.keys()){*/ @@ -352,7 +356,7 @@ QListWidgetItem *DataListWidget::getItemForData(TeraData *data) // Not found - try to find an item which is new but with the same name //for (TeraData* current_data:m_datamap.keys()){ - for (QListWidgetItem* item: qAsConst(m_datamap)){ + for (QListWidgetItem* item: std::as_const(m_datamap)){ TeraData* current_data = m_datamap.key(item); if (current_data->isNew() && current_data->getName() == data->getName()){ m_newdata=false; @@ -362,7 +366,7 @@ QListWidgetItem *DataListWidget::getItemForData(TeraData *data) }else{ // We have a new item - try and match. //for (TeraData* current_data:m_datamap.keys()){ - for (QListWidgetItem* item: qAsConst(m_datamap)){ + for (QListWidgetItem* item: std::as_const(m_datamap)){ TeraData* current_data = m_datamap.key(item); if (current_data->isNew()){ return m_datamap[current_data]; @@ -379,7 +383,7 @@ void DataListWidget::clearDataList(){ ui->lstData->clear(); //for (TeraData* data:m_datamap.keys()){ - for (QListWidgetItem* item: qAsConst(m_datamap)){ + for (QListWidgetItem* item: std::as_const(m_datamap)){ TeraData* data = m_datamap.key(item); delete data; } @@ -407,7 +411,7 @@ void DataListWidget::deleteDataReply(QString path, int id) if (path == TeraData::getPathForDataType(m_dataType)){ // An item that we are managing got deleted - for (QListWidgetItem* item: qAsConst(m_datamap)){ + for (QListWidgetItem* item: std::as_const(m_datamap)){ TeraData* data = m_datamap.key(item); //for (TeraData* data:m_datamap.keys()){ if (data->getId() == id){ @@ -464,7 +468,7 @@ void DataListWidget::editor_dataChanged() void DataListWidget::searchChanged(QString new_search){ Q_UNUSED(new_search) // Check if search field is empty - if (ui->txtSearch->text().count()==0){ + if (ui->txtSearch->text().size()==0){ setSearching(false); // Display back all items for (int i=0; ilstData->count();i++){ diff --git a/client/src/editors/DataListWidget.ui b/client/src/editors/DataListWidget.ui index 10d85c31..3a0ed15e 100644 --- a/client/src/editors/DataListWidget.ui +++ b/client/src/editors/DataListWidget.ui @@ -92,7 +92,7 @@ QTreeWidget::branch:closed:has-children:has-siblings {
- 3 + 6 QLayout::SetMaximumSize @@ -165,6 +165,9 @@ QTreeWidget::branch:closed:has-children:has-siblings { Seuls les ... ayant un lien avec ce ... sont présentement affichés. + + Qt::AlignCenter + true @@ -172,8 +175,26 @@ QTreeWidget::branch:closed:has-children:has-siblings { + + 5 + + + 5 + + + 5 + + + 5 + + + + 0 + 0 + + PointingHandCursor @@ -187,6 +208,12 @@ QTreeWidget::branch:closed:has-children:has-siblings { + + + 0 + 0 + + PointingHandCursor @@ -222,7 +249,7 @@ QTreeWidget::branch:closed:has-children:has-siblings { - 0 + 6 0 diff --git a/client/src/editors/DeviceSummaryWidget.cpp b/client/src/editors/DeviceSummaryWidget.cpp index f577826a..bdb97173 100644 --- a/client/src/editors/DeviceSummaryWidget.cpp +++ b/client/src/editors/DeviceSummaryWidget.cpp @@ -9,7 +9,9 @@ DeviceSummaryWidget::DeviceSummaryWidget(ComManager *comMan, const TeraData *dat { m_diag_editor = nullptr; +#ifndef OPENTERA_WEBASSEMBLY m_sessionLobby = nullptr; +#endif m_idProject = id_project; ui->setupUi(this); @@ -47,9 +49,10 @@ DeviceSummaryWidget::DeviceSummaryWidget(ComManager *comMan, const TeraData *dat DeviceSummaryWidget::~DeviceSummaryWidget() { delete ui; - +#ifndef OPENTERA_WEBASSEMBLY if (m_sessionLobby) m_sessionLobby->deleteLater(); +#endif } void DeviceSummaryWidget::connectSignals() @@ -232,7 +235,7 @@ void DeviceSummaryWidget::ws_deviceEvent(DeviceEvent event) } updateFieldsValue(); } - +#ifndef OPENTERA_WEBASSEMBLY void DeviceSummaryWidget::sessionLobbyStartSessionRequested() { int id_session_type = ui->cmbSessionType->currentData().toInt(); @@ -281,7 +284,7 @@ void DeviceSummaryWidget::on_btnNewSession_clicked() // Show Session Lobby m_sessionLobby->exec(); } - +#endif void DeviceSummaryWidget::on_tabNav_currentChanged(int index) { Q_UNUSED(index) diff --git a/client/src/editors/DeviceSummaryWidget.h b/client/src/editors/DeviceSummaryWidget.h index 8dec8686..b335cf87 100644 --- a/client/src/editors/DeviceSummaryWidget.h +++ b/client/src/editors/DeviceSummaryWidget.h @@ -10,7 +10,10 @@ #include "TeraSessionStatus.h" #include "Utils.h" #include "ServiceConfigWidget.h" + +#ifndef OPENTERA_WEBASSEMBLY #include "dialogs/SessionLobbyDialog.h" +#endif namespace Ui { class DeviceSummaryWidget; @@ -22,9 +25,9 @@ class DeviceSummaryWidget : public DataEditorWidget public: explicit DeviceSummaryWidget(ComManager* comMan, const TeraData* data = nullptr, const int &id_project = -1, QWidget *parent = nullptr); - ~DeviceSummaryWidget(); + ~DeviceSummaryWidget() override; - void saveData(bool signal=true); + void saveData(bool signal=true) override; void connectSignals(); private: @@ -33,15 +36,17 @@ class DeviceSummaryWidget : public DataEditorWidget QMap m_ids_session_types; BaseDialog* m_diag_editor; +#ifndef OPENTERA_WEBASSEMBLY SessionLobbyDialog* m_sessionLobby; +#endif int m_idProject; - void updateControlsState(); - void updateFieldsValue(); + void updateControlsState() override; + void updateFieldsValue() override; void initUI(); - bool validateData(); + bool validateData() override; private slots: void processFormsReply(QString form_type, QString data); @@ -51,11 +56,11 @@ private slots: void processStatsReply(TeraData stats, QUrlQuery reply_query); void ws_deviceEvent(opentera::protobuf::DeviceEvent event); - +#ifndef OPENTERA_WEBASSEMBLY void sessionLobbyStartSessionRequested(); void sessionLobbyStartSessionCancelled(); - void on_btnNewSession_clicked(); +#endif void on_tabNav_currentChanged(int index); void sessionTotalCountUpdated(int new_count); diff --git a/client/src/editors/DeviceWidget.cpp b/client/src/editors/DeviceWidget.cpp index 58dbfdd2..64afef17 100644 --- a/client/src/editors/DeviceWidget.cpp +++ b/client/src/editors/DeviceWidget.cpp @@ -112,7 +112,7 @@ void DeviceWidget::updateControlsState() bool has_project_admin_access = m_comManager->isCurrentUserSuperAdmin(); if (!has_project_admin_access){ // Check if we are admin in a list one site - for(int id_project:qAsConst(m_devicesProjects)){ + for(int id_project:std::as_const(m_devicesProjects)){ if (m_comManager->isCurrentUserProjectAdmin(id_project)){ has_project_admin_access = true; break; @@ -153,7 +153,7 @@ bool DeviceWidget::validateSitesProjects() if (!m_comManager->isCurrentUserSuperAdmin()){ bool at_least_one_selected = false; // Sites - for (QTreeWidgetItem* item: qAsConst(m_treeSites_items)){ + for (QTreeWidgetItem* item: std::as_const(m_treeSites_items)){ if (item->checkState(0) == Qt::Checked){ at_least_one_selected = true; break; @@ -161,7 +161,7 @@ bool DeviceWidget::validateSitesProjects() } // Projects - for (QTreeWidgetItem* item: qAsConst(m_treeProjects_items)){ + for (QTreeWidgetItem* item: std::as_const(m_treeProjects_items)){ if (item->checkState(0) == Qt::Checked){ at_least_one_selected = true; break; @@ -344,7 +344,7 @@ void DeviceWidget::postDeviceSites() QJsonObject base_obj; QJsonArray sites; - for(QTreeWidgetItem* item: qAsConst(m_treeSites_items)){ + for(QTreeWidgetItem* item: std::as_const(m_treeSites_items)){ int site_id = m_treeSites_items.key(item); if (item->checkState(0) == Qt::Checked){ QJsonObject data_obj; @@ -367,7 +367,7 @@ void DeviceWidget::postDeviceProjects() QJsonObject base_obj; QJsonArray projects; - for(QTreeWidgetItem* item: qAsConst(m_treeProjects_items)){ + for(QTreeWidgetItem* item: std::as_const(m_treeProjects_items)){ int project_id = m_treeProjects_items.key(item); if (item->checkState(0) == Qt::Checked){ QJsonObject data_obj; @@ -387,7 +387,7 @@ void DeviceWidget::postDeviceProjects() QJsonArray DeviceWidget::getSelectedProjectsAsJsonArray() { QJsonArray projects; - for(QTreeWidgetItem* item: qAsConst(m_treeProjects_items)){ + for(QTreeWidgetItem* item: std::as_const(m_treeProjects_items)){ //for (int i=0; icheckState(0) == Qt::Checked){ @@ -402,7 +402,7 @@ QJsonArray DeviceWidget::getSelectedProjectsAsJsonArray() QJsonArray DeviceWidget::getSelectedSitesAsJsonArray() { QJsonArray sites; - for(QTreeWidgetItem* item: qAsConst(m_treeSites_items)){ + for(QTreeWidgetItem* item: std::as_const(m_treeSites_items)){ int site_id = m_treeSites_items.key(item); if (item->checkState(0) == Qt::Checked){ QJsonObject data_obj; @@ -499,7 +499,7 @@ void DeviceWidget::btnSaveSites_clicked() bool has_removed = false; - for(QTreeWidgetItem* item: qAsConst(m_treeSites_items)){ + for(QTreeWidgetItem* item: std::as_const(m_treeSites_items)){ if (item->checkState(0) == Qt::Unchecked && item->data(0, Qt::UserRole) == Qt::Checked){ has_removed = true; break; @@ -507,7 +507,7 @@ void DeviceWidget::btnSaveSites_clicked() } if (!has_removed){ - for(QTreeWidgetItem* item: qAsConst(m_treeProjects_items)){ + for(QTreeWidgetItem* item: std::as_const(m_treeProjects_items)){ if (item->checkState(0) == Qt::Unchecked && item->data(0, Qt::UserRole) == Qt::Checked){ has_removed = true; break; @@ -598,14 +598,14 @@ void DeviceWidget::lstSites_itemChanged(QTreeWidgetItem *item, int column) updating = false; - for(QTreeWidgetItem* site: qAsConst(m_treeSites_items)){ + for(QTreeWidgetItem* site: std::as_const(m_treeSites_items)){ if (site->checkState(0) != site->data(0, Qt::UserRole) && site->data(0, Qt::UserRole) != Qt::PartiallyChecked){ has_changes = true; break; } } if (!has_changes){ - for(QTreeWidgetItem* proj: qAsConst(m_treeProjects_items)){ + for(QTreeWidgetItem* proj: std::as_const(m_treeProjects_items)){ if (proj->checkState(0) != proj->data(0, Qt::UserRole)){ has_changes = true; break; @@ -644,7 +644,7 @@ void DeviceWidget::on_tabNav_currentChanged(int index) // Service configuration if (!ui->wdgServiceConfig->layout()){ QHBoxLayout* layout = new QHBoxLayout(); - layout->setMargin(0); + layout->setContentsMargins(0,0,0,0); ui->wdgServiceConfig->setLayout(layout); } if (ui->wdgServiceConfig->layout()->count() == 0){ diff --git a/client/src/editors/DeviceWidget.ui b/client/src/editors/DeviceWidget.ui index 6f3735ba..e11222d3 100644 --- a/client/src/editors/DeviceWidget.ui +++ b/client/src/editors/DeviceWidget.ui @@ -338,6 +338,9 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, 0 + + Qt::NoFocus + QAbstractItemView::NoEditTriggers @@ -402,6 +405,9 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, true + + QAbstractItemView::NoEditTriggers + 24 diff --git a/client/src/editors/GroupWidget.cpp b/client/src/editors/GroupWidget.cpp index 5cabded7..c3fcafca 100644 --- a/client/src/editors/GroupWidget.cpp +++ b/client/src/editors/GroupWidget.cpp @@ -15,9 +15,9 @@ GroupWidget::GroupWidget(ComManager *comMan, const TeraData *data, QWidget *pare setAttribute(Qt::WA_StyledBackground); //Required to set a background image setLimited(false); - +#ifndef OPENTERA_WEBASSEMBLY m_sessionLobby = nullptr; - +#endif // Use base class to manage editing setEditorControls(ui->wdgGroup, ui->btnEdit, ui->frameButtons, ui->btnSave, ui->btnUndo); @@ -60,13 +60,16 @@ GroupWidget::GroupWidget(ComManager *comMan, const TeraData *data, QWidget *pare GroupWidget::~GroupWidget() { - if (ui) + if (ui) { delete ui; + } qDeleteAll(m_ids_session_types); - - if (m_sessionLobby) +#ifndef OPENTERA_WEBASSEMBLY + if (m_sessionLobby) { m_sessionLobby->deleteLater(); + } +#endif } void GroupWidget::saveData(bool signal){ @@ -178,7 +181,7 @@ void GroupWidget::processStatsReply(TeraData stats, QUrlQuery reply_query) QVariantList parts_list = stats.getFieldValue("participants").toList(); - for(const QVariant &part:qAsConst(parts_list)){ + for(const QVariant &part:std::as_const(parts_list)){ QVariantMap part_info = part.toMap(); int part_id = part_info["id_participant"].toInt(); @@ -196,9 +199,12 @@ void GroupWidget::processStatsReply(TeraData stats, QUrlQuery reply_query) item->setForeground(Qt::green); if (!m_activeParticipants.contains(part_id)) m_activeParticipants.append(part_id); + ui->tableSummary->showRow(current_row); }else{ status = tr("Inactif"); item->setForeground(Qt::red); + if (!ui->chkShowInactive->isChecked()) + ui->tableSummary->hideRow(current_row); } item->setText(status); item->setTextAlignment(Qt::AlignCenter); @@ -325,7 +331,7 @@ void GroupWidget::deleteDataReply(QString path, int del_id) } } } - +#ifndef OPENTERA_WEBASSEMBLY void GroupWidget::showSessionLobby(const int &id_session_type, const int &id_session) { if (m_sessionLobby) @@ -335,7 +341,7 @@ void GroupWidget::showSessionLobby(const int &id_session_type, const int &id_ses // Add current participants to session //m_sessionLobby->addParticipantsToSession(QList() << *m_data, QList() << m_data->getId()); - for(int id_part:qAsConst(m_activeParticipants)){ + for(int id_part:std::as_const(m_activeParticipants)){ TeraData part_data; part_data.setDataType(TeraDataTypes::TERADATA_PARTICIPANT); part_data.setId(id_part); @@ -385,6 +391,7 @@ void GroupWidget::sessionLobbyStartSessionCancelled() } } +#endif void GroupWidget::connectSignals() { connect(m_comManager, &ComManager::formReceived, this, &GroupWidget::processFormsReply); @@ -429,7 +436,7 @@ void GroupWidget::on_tableSummary_itemSelectionChanged() ui->btnDelete->setEnabled(!ui->tableSummary->selectedItems().isEmpty()); } - +#ifndef OPENTERA_WEBASSEMBLY void GroupWidget::on_btnNewSession_clicked() { if (ui->cmbSessionType->currentIndex() < 0) @@ -443,4 +450,22 @@ void GroupWidget::on_btnNewSession_clicked() showSessionLobby(id_session_type, id_session); } +#endif + +void GroupWidget::on_chkShowInactive_stateChanged(int checked) +{ + for(QTableWidgetItem* item: m_tableParticipants_items){ + int row = item->row(); + if (ui->chkShowInactive->isChecked()){ + if (ui->tableSummary->isRowHidden(row)) + ui->tableSummary->showRow(row); + }else{ + bool active = ui->tableSummary->item(row, 1)->foreground() != Qt::red; + if (!active){ + ui->tableSummary->hideRow(row); + } + } + } + ui->tableSummary->resizeColumnsToContents(); +} diff --git a/client/src/editors/GroupWidget.h b/client/src/editors/GroupWidget.h index 601a358d..c93de90b 100644 --- a/client/src/editors/GroupWidget.h +++ b/client/src/editors/GroupWidget.h @@ -11,7 +11,9 @@ #include "widgets/TableDateWidgetItem.h" #include "widgets/TableNumberWidgetItem.h" +#ifndef OPENTERA_WEBASSEMBLY #include "dialogs/SessionLobbyDialog.h" +#endif namespace Ui { class GroupWidget; @@ -23,9 +25,9 @@ class GroupWidget : public DataEditorWidget public: explicit GroupWidget(ComManager* comMan, const TeraData* data = nullptr, QWidget *parent = nullptr); - ~GroupWidget(); + ~GroupWidget() override; - void saveData(bool signal=true); + void saveData(bool signal=true) override; void connectSignals(); @@ -36,17 +38,17 @@ class GroupWidget : public DataEditorWidget QList m_activeParticipants; QMap m_ids_session_types; - +#ifndef OPENTERA_WEBASSEMBLY SessionLobbyDialog* m_sessionLobby; +#endif + void updateControlsState() override; + void updateFieldsValue() override; - void updateControlsState(); - void updateFieldsValue(); - - void setData(const TeraData* data); + void setData(const TeraData* data) override; bool canStartSession(); - bool validateData(); + bool validateData() override; private slots: void processFormsReply(QString form_type, QString data); @@ -55,15 +57,17 @@ private slots: void postResultReply(QString path); void deleteDataReply(QString path, int del_id); - +#ifndef OPENTERA_WEBASSEMBLY void showSessionLobby(const int& id_session_type, const int& id_session); void sessionLobbyStartSessionRequested(); void sessionLobbyStartSessionCancelled(); - + void on_btnNewSession_clicked(); +#endif void on_btnNewParticipant_clicked(); void on_btnDelete_clicked(); void on_tableSummary_itemSelectionChanged(); - void on_btnNewSession_clicked(); + + void on_chkShowInactive_stateChanged(int checked); }; #endif // GROUPWIDGET_H diff --git a/client/src/editors/GroupWidget.ui b/client/src/editors/GroupWidget.ui index 1ce75076..66278299 100644 --- a/client/src/editors/GroupWidget.ui +++ b/client/src/editors/GroupWidget.ui @@ -160,126 +160,126 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - - - - - - - 0 - 0 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - - true - - - - 0 - 0 - - - - - 0 - 40 - - - - - 400 - 16777215 - - - - - - - - true - - - - 0 - 0 - - - - - 125 - 40 - - - - - 16777215 - 40 - - - - PointingHandCursor - - - Démarrer Séance + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 - - - :/icons/play.png:/icons/play.png + + 0 - - - 24 - 24 - + + 0 - - false + + 0 - - - - + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + true + + + + 0 + 0 + + + + + 0 + 40 + + + + + 400 + 16777215 + + + + + + + + true + + + + 0 + 0 + + + + + 125 + 40 + + + + + 16777215 + 40 + + + + PointingHandCursor + + + Démarrer Séance + + + + :/icons/play.png:/icons/play.png + + + + 24 + 24 + + + + false + + + + + + + @@ -411,6 +411,9 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, Résumé + + 15 + @@ -528,6 +531,13 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + + + + Qt::Horizontal + + + @@ -595,6 +605,13 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + + + + Afficher les participants inactifs + + + diff --git a/client/src/editors/ParticipantWidget.cpp b/client/src/editors/ParticipantWidget.cpp index 5959b820..40339748 100644 --- a/client/src/editors/ParticipantWidget.cpp +++ b/client/src/editors/ParticipantWidget.cpp @@ -15,9 +15,9 @@ ParticipantWidget::ParticipantWidget(ComManager *comMan, const TeraData *data, Q DataEditorWidget(comMan, data, parent), ui(new Ui::ParticipantWidget) { - +#ifndef OPENTERA_WEBASSEMBLY m_sessionLobby = nullptr; - +#endif m_allowFileTransfers = false; ui->setupUi(this); @@ -76,11 +76,15 @@ ParticipantWidget::~ParticipantWidget() qDeleteAll(m_ids_session_types); delete ui; - if (m_sessionLobby) +#ifndef OPENTERA_WEBASSEMBLY + if (m_sessionLobby) { m_sessionLobby->deleteLater(); + } +#endif - if (m_diag_qr) + if (m_diag_qr) { m_diag_qr->deleteLater(); + } } @@ -132,8 +136,9 @@ void ParticipantWidget::connectSignals() connect(ui->lstAvailDevices, &QListWidget::currentItemChanged, this, &ParticipantWidget::currentAvailDeviceChanged); connect(ui->lstDevices, &QListWidget::currentItemChanged, this, &ParticipantWidget::currentDeviceChanged); - +#ifndef OPENTERA_WEBASSEMBLY connect(ui->wdgSessions, &SessionsListWidget::startSessionRequested, this, &ParticipantWidget::showSessionLobby); +#endif connect(ui->wdgSessions, &SessionsListWidget::sessionsCountUpdated, this, &ParticipantWidget::sessionTotalCountUpdated); } @@ -169,6 +174,9 @@ void ParticipantWidget::updateFieldsValue() if (!dataIsNew()){ ui->lblTitle->setText(m_data->getName()); ui->chkEnabled->setChecked(m_data->getFieldValue("participant_enabled").toBool()); + if (m_data->hasFieldName("participant_project_enabled")){ + ui->chkEnabled->setEnabled(m_data->getFieldValue("participant_project_enabled").toBool()); + } ui->chkLogin->setChecked(m_data->getFieldValue("participant_login_enabled").toBool()); if (ui->chkLogin->isChecked()){ ui->btnSaveLogin->setEnabled(false); // Disable save login infos on already enabled participants @@ -186,7 +194,7 @@ void ParticipantWidget::updateFieldsValue() on_txtPassword_textEdited(""); // Status - ui->icoOnline->setVisible(m_data->isEnabled()); + //ui->icoOnline->setVisible(m_data->isEnabled()); ui->icoTitle->setPixmap(QPixmap(m_data->getIconStateFilename())); if (m_data->isBusy()){ ui->icoOnline->setPixmap(QPixmap("://status/status_busy.png")); @@ -219,6 +227,7 @@ void ParticipantWidget::initUI() ui->frameActive->hide(); ui->frameWeb->hide(); ui->txtWeb->hide(); + ui->icoOnline->hide(); // Disable random password button, handled in the PasswordStrengthDialog now! ui->btnRandomPass->hide(); @@ -364,7 +373,7 @@ void ParticipantWidget::updateServiceTabs() QList ids_service; - for(const TeraData &service: qAsConst(m_services)){ + for(const TeraData &service: std::as_const(m_services)){ ids_service.append(service.getId()); // Create specific tabs for services @@ -372,7 +381,7 @@ void ParticipantWidget::updateServiceTabs() } // Remove tabs not anymore present - /*for(QWidget* tab: qAsConst(m_services_tabs)){ + /*for(QWidget* tab: std::as_const(m_services_tabs)){ if (!ids_service.contains(m_services_tabs.key(tab))){ ui->tabNav->removeTab(ui->tabNav->indexOf(tab)); tab->deleteLater(); @@ -598,7 +607,7 @@ void ParticipantWidget::deleteDataReply(QString path, int id) if (path == WEB_DEVICEPARTICIPANTINFO_PATH){ // A participant device association was deleted - for (QListWidgetItem* item: qAsConst(m_listDevices_items)){ + for (QListWidgetItem* item: std::as_const(m_listDevices_items)){ // Check for id_device_participant, which is stored in "data" of the item if (item->data(Qt::UserRole).toInt() == id){ // We found it - remove it and request update @@ -665,7 +674,7 @@ void ParticipantWidget::btnAddDevice_clicked() if (diag->result() == DeviceAssignDialog::DEVICEASSIGN_DEASSIGN){ // Delete all associated participants QList ids = diag->getDeviceParticipantsIds(); - for (int id:qAsConst(ids)){ + for (int id:std::as_const(ids)){ deleteDataRequest(WEB_DEVICEPARTICIPANTINFO_PATH, id); } } @@ -714,7 +723,7 @@ bool ParticipantWidget::isProjectAdmin() return m_comManager->getCurrentUserProjectRole(m_data->getFieldValue("id_project").toInt()) == "admin"; } - +#ifndef OPENTERA_WEBASSEMBLY void ParticipantWidget::showSessionLobby(const int &id_session_type, const int &id_session) { if (!canStartNewSession(id_session_type)){ @@ -738,6 +747,10 @@ void ParticipantWidget::showSessionLobby(const int &id_session_type, const int & connect(m_sessionLobby, &QDialog::rejected, this, &ParticipantWidget::sessionLobbyStartSessionCancelled); if (height()<800) m_sessionLobby->showMaximized(); + else{ + m_sessionLobby->setMinimumSize(3*QGuiApplication::primaryScreen()->availableGeometry().width() / 4, + 2*QGuiApplication::primaryScreen()->availableGeometry().height() / 3); + } // Show Session Lobby m_sessionLobby->exec(); @@ -773,7 +786,7 @@ void ParticipantWidget::sessionLobbyStartSessionCancelled() m_sessionLobby = nullptr; } } - +#endif void ParticipantWidget::currentAvailDeviceChanged(QListWidgetItem *current, QListWidgetItem *previous) { Q_UNUSED(previous) @@ -876,7 +889,7 @@ void ParticipantWidget::on_chkLogin_stateChanged(int checkState) ui->btnSaveLogin->setEnabled(false); }else{ ui->txtPassword->setStyleSheet("background-color: #ffaaaa;"); - ui->btnSaveLogin->setEnabled(true); + ui->btnSaveLogin->setEnabled(false); } } } @@ -1019,7 +1032,9 @@ void ParticipantWidget::on_btnNewSession_clicked() } // If id_session == 0, will start a new session. Otherwise, will resume that session with id_session +#ifndef OPENTERA_WEBASSEMBLY showSessionLobby(id_session_type, id_session); +#endif } void ParticipantWidget::on_btnViewLink_clicked() @@ -1118,7 +1133,8 @@ void ParticipantWidget::on_tabNav_currentChanged(int index) if (current_tab == ui->tabServices){ // Services if (!ui->wdgServiceConfig->layout()){ QHBoxLayout* layout = new QHBoxLayout(); - layout->setMargin(0); + + //layout->setMargin(0); ui->wdgServiceConfig->setLayout(layout); } if (ui->wdgServiceConfig->layout()->count() == 0){ @@ -1131,12 +1147,14 @@ void ParticipantWidget::on_tabNav_currentChanged(int index) void ParticipantWidget::on_lstAvailDevices_itemDoubleClicked(QListWidgetItem *item) { + Q_UNUSED(item) btnAddDevice_clicked(); } void ParticipantWidget::on_lstDevices_itemDoubleClicked(QListWidgetItem *item) { + Q_UNUSED(item) btnDelDevice_clicked(); } diff --git a/client/src/editors/ParticipantWidget.h b/client/src/editors/ParticipantWidget.h index c34a3f4d..87eaf023 100644 --- a/client/src/editors/ParticipantWidget.h +++ b/client/src/editors/ParticipantWidget.h @@ -17,7 +17,10 @@ #include "TeraSettings.h" #include "ServiceConfigWidget.h" +#ifndef OPENTERA_WEBASSEMBLY #include "dialogs/SessionLobbyDialog.h" +#endif + #include "dialogs/EmailInviteDialog.h" #include "dialogs/DeviceAssignDialog.h" #include "dialogs/QRCodeDialog.h" @@ -56,7 +59,9 @@ class ParticipantWidget : public DataEditorWidget bool m_allowFileTransfers; // Allow to attach files to a session? QRCodeDialog* m_diag_qr = nullptr; +#ifndef OPENTERA_WEBASSEMBLY SessionLobbyDialog* m_sessionLobby; +#endif QHash m_ids_session_types; @@ -95,10 +100,11 @@ private slots: void btnAddDevice_clicked(); void btnDelDevice_clicked(); - +#ifndef OPENTERA_WEBASSEMBLY void showSessionLobby(const int& id_session_type, const int& id_session); void sessionLobbyStartSessionRequested(); void sessionLobbyStartSessionCancelled(); +#endif void currentAvailDeviceChanged(QListWidgetItem* current, QListWidgetItem* previous); void currentDeviceChanged(QListWidgetItem* current, QListWidgetItem* previous); diff --git a/client/src/editors/ParticipantWidget.ui b/client/src/editors/ParticipantWidget.ui index 1e8922ad..a28900d7 100644 --- a/client/src/editors/ParticipantWidget.ui +++ b/client/src/editors/ParticipantWidget.ui @@ -212,146 +212,146 @@ QCalendarWidget QSpinBox::down-arrow { width:16px; height:16px; } - - - - 0 - 0 - - - - - 32 - 32 - - - - - - - :/status/status_unknown.png - - - true - - - - - - - - - - - 0 - 0 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - - true - + - + 0 0 - - - 0 - 40 - + + QFrame::StyledPanel - - - 400 - 16777215 - + + QFrame::Raised + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + true + + + + 0 + 0 + + + + + 0 + 40 + + + + + 400 + 16777215 + + + + + + + + true + + + + 0 + 0 + + + + + 125 + 40 + + + + + 16777215 + 40 + + + + PointingHandCursor + + + Démarrer Séance + + + + :/icons/play.png:/icons/play.png + + + + 24 + 24 + + + + false + + + + - - - true - + - + 0 0 - - - 125 - 40 - - - 16777215 - 40 + 32 + 32 - - PointingHandCursor - - Démarrer Séance - - - - :/icons/play.png:/icons/play.png + - - - 24 - 24 - + + :/status/status_unknown.png - - false + + true diff --git a/client/src/editors/ProjectWidget.cpp b/client/src/editors/ProjectWidget.cpp index a860d581..97b8b9aa 100644 --- a/client/src/editors/ProjectWidget.cpp +++ b/client/src/editors/ProjectWidget.cpp @@ -2,13 +2,13 @@ #include "ui_ProjectWidget.h" #include "editors/DataListWidget.h" -#include "Logger.h" ProjectWidget::ProjectWidget(ComManager *comMan, const TeraData *data, QWidget *parent) : DataEditorWidget(comMan, data, parent), ui(new Ui::ProjectWidget) { m_diag_editor = nullptr; + m_refreshProjectParticipants = false; ui->setupUi(this); @@ -29,10 +29,12 @@ ProjectWidget::ProjectWidget(ComManager *comMan, const TeraData *data, QWidget * queryDataRequest(WEB_FORMS_PATH, QUrlQuery(WEB_FORMS_QUERY_PROJECT)); ui->wdgProject->setComManager(m_comManager); + ui->wdgProject->setSectionsPosition(QTabWidget::North); if (data->isNew()){ // Connect session-type site for new project connect(m_comManager, &ComManager::sessionTypesSitesReceived, this, &ProjectWidget::processSessionTypeSiteReply); + connect(m_comManager, &ComManager::testTypesSitesReceived, this, &ProjectWidget::processTestTypeSiteReply); } ProjectWidget::setData(data); @@ -59,7 +61,7 @@ void ProjectWidget::saveData(bool signal) QJsonObject base_st = base_obj["project"].toObject(); QJsonArray session_types; - for(QListWidgetItem* item:qAsConst(m_listSessionTypes_items)){ + for(QListWidgetItem* item:std::as_const(m_listSessionTypes_items)){ if (item->checkState() == Qt::Checked){ int session_type_id = m_listSessionTypes_items.key(item); QJsonObject data_obj; @@ -80,6 +82,10 @@ void ProjectWidget::saveData(bool signal) if (signal){ TeraData* new_data = ui->wdgProject->getFormDataObject(TERADATA_PROJECT); + if (m_data->isEnabled() && !new_data->isEnabled()){ + // Project was disabled. Also forces participants from that project refresh when we get the "Post" reply + m_refreshProjectParticipants = true; + } *m_data = *new_data; delete new_data; emit dataWasChanged(); @@ -96,7 +102,6 @@ void ProjectWidget::setData(const TeraData *data) args.addQueryItem(WEB_QUERY_ID_PROJECT, QString::number(m_data->getId())); args.addQueryItem(WEB_QUERY_WITH_PARTICIPANTS, "1"); queryDataRequest(WEB_STATS_PATH, args); - } } @@ -107,6 +112,7 @@ void ProjectWidget::connectSignals() connect(m_comManager, &ComManager::postResultsOK, this, &ProjectWidget::processPostOKReply); connect(m_comManager, &ComManager::servicesProjectsReceived, this, &ProjectWidget::processServiceProjectsReply); connect(m_comManager, &ComManager::sessionTypesProjectsReceived, this, &ProjectWidget::processSessionTypeProjectReply); + connect(m_comManager, &ComManager::testTypesProjectsReceived, this, &ProjectWidget::processTestTypeProjectReply); connect(m_comManager, &ComManager::statsReceived, this, &ProjectWidget::processStatsReply); connect(m_comManager, &ComManager::deleteResultsOK, this, &ProjectWidget::deleteDataReply); @@ -206,7 +212,7 @@ void ProjectWidget::updateDevice(const TeraData *device) // Add participants to devices if (device->hasFieldName("device_participants")){ QVariantList participants = device->getFieldValue("device_participants").toList(); - for(const QVariant &participant: qAsConst(participants)){ + for(const QVariant &participant: std::as_const(participants)){ QVariantHash part_info = participant.toHash(); if (part_info.contains("id_project")){ if (part_info["id_project"].toInt() != m_data->getId()) @@ -386,6 +392,157 @@ void ProjectWidget::updateSessionTypeSite(const TeraData *sts) item->setText(st_name); } +void ProjectWidget::updateTestTypeProject(const TeraData *ttp) +{ + int id_tt_project = ttp->getId(); + int id_test_type = ttp->getFieldValue("id_test_type").toInt(); + QString tt_name; + if (ttp->hasFieldName("test_type_name")) + tt_name = ttp->getFieldValue("test_type_name").toString(); + + QListWidgetItem* item; + if (m_listTestTypes_items.contains(id_test_type)){ + item = m_listTestTypes_items[id_test_type]; + }else{ + // Must create a new item + item = new QListWidgetItem(tt_name); + item->setIcon(QIcon(TeraData::getIconFilenameForDataType(TERADATA_TESTTYPE))); + ui->lstTestTypes->addItem(item); + + m_listTestTypes_items[id_test_type] = item; + } + + if (!tt_name.isEmpty()) + item->setText(tt_name); + + if (ttp->getFieldValue("id_project").toInt() == m_data->getId()){ + item->setCheckState(Qt::Checked); + if (!m_listTestTypesProjects_items.contains(id_tt_project)){ + m_listTestTypesProjects_items[id_tt_project] = item; + } + }else{ + item->setCheckState(Qt::Unchecked); + if (m_listTestTypesProjects_items.contains(id_tt_project)){ + m_listTestTypesProjects_items.remove(id_tt_project); + } + } +} + +void ProjectWidget::updateTestTypeSite(const TeraData *tts) +{ + int id_test_type = tts->getFieldValue("id_test_type").toInt(); + QString tt_name; + if (tts->hasFieldName("test_type_name")) + tt_name = tts->getFieldValue("test_type_name").toString(); + + QListWidgetItem* item; + if (m_listTestTypes_items.contains(id_test_type)){ + item = m_listTestTypes_items[id_test_type]; + }else{ + // Must create a new item + item = new QListWidgetItem(tt_name); + item->setIcon(QIcon(TeraData::getIconFilenameForDataType(TERADATA_TESTTYPE))); + item->setCheckState(Qt::Unchecked); + ui->lstTestTypes->addItem(item); + + m_listTestTypes_items[id_test_type] = item; + } + + if (!tt_name.isEmpty()) + item->setText(tt_name); +} + +void ProjectWidget::updateParticipant(const TeraData *participant) +{ + QTableWidgetItem* item_name; + QTableWidgetItem* item_state; + TableNumberWidgetItem* item_sessions; + TableDateWidgetItem* item_first_session; + TableDateWidgetItem* item_last_session; + TableDateWidgetItem* item_last_online; + + if (m_tableParticipants_items.contains(participant->getId())){ + item_name = m_tableParticipants_items[participant->getId()]; + int row = item_name->row(); + item_state = ui->tableSummary->item(row, 1); + item_sessions = dynamic_cast(ui->tableSummary->item(row, 2)); + item_first_session = dynamic_cast(ui->tableSummary->item(row, 3)); + item_last_session = dynamic_cast(ui->tableSummary->item(row, 4)); + item_last_online = dynamic_cast(ui->tableSummary->item(row, 5)); + }else{ + // Create new widget items + item_name = new QTableWidgetItem(QIcon(TeraData::getIconFilenameForDataType(TERADATA_PARTICIPANT)), participant->getName()); + m_tableParticipants_items[participant->getId()] = item_name; + + ui->tableSummary->setRowCount(ui->tableSummary->rowCount()+1); + int current_row = ui->tableSummary->rowCount()-1; + + ui->tableSummary->setItem(current_row, 0, item_name); + + item_state = new QTableWidgetItem(); + item_state->setTextAlignment(Qt::AlignCenter); + ui->tableSummary->setItem(current_row, 1, item_state); + + item_sessions = new TableNumberWidgetItem(); + item_sessions->setTextAlignment(Qt::AlignCenter); + ui->tableSummary->setItem(current_row, 2, item_sessions); + + item_first_session = new TableDateWidgetItem(); + item_first_session->setTextAlignment(Qt::AlignCenter); + ui->tableSummary->setItem(current_row, 3, item_first_session); + item_last_session = new TableDateWidgetItem(); + item_last_session->setTextAlignment(Qt::AlignCenter); + ui->tableSummary->setItem(current_row, 4, item_last_session); + + item_last_online = new TableDateWidgetItem(); + item_last_online->setTextAlignment(Qt::AlignCenter); + ui->tableSummary->setItem(current_row, 5, item_last_online); + } + + // Set current values + item_name->setText(participant->getName()); + QString status; + if (participant->isEnabled()){ + status = tr("Actif"); + item_state->setForeground(Qt::green); + ui->tableSummary->showRow(item_state->row()); + }else{ + status = tr("Inactif"); + item_state->setForeground(Qt::red); + if (!ui->chkShowInactive->isChecked()) + ui->tableSummary->hideRow(item_state->row()); + } + item_state->setText(status); + if (participant->hasFieldName("participant_sessions_count")){ + item_sessions->setText(participant->getFieldValue("participant_sessions_count").toString()); + } + if (participant->hasFieldName("participant_first_session")){ + item_first_session->setDate(participant->getFieldValue("participant_first_session").toDateTime().toLocalTime()); + } + if (participant->hasFieldName("participant_last_session")){ + QDateTime last_session_datetime = participant->getFieldValue("participant_last_session").toDateTime().toLocalTime(); + item_last_session->setDate(last_session_datetime); + if (participant->isEnabled() && last_session_datetime.isValid()){ + // Set background color + QColor back_color = TeraForm::getGradientColor(0, 7, 14, static_cast(last_session_datetime.daysTo(QDateTime::currentDateTime()))); + back_color.setAlphaF(0.5); + item_last_session->setBackground(back_color); + } + } + if (participant->hasFieldName("participant_last_online")){ + QDateTime last_connect_datetime = participant->getFieldValue("participant_last_online").toDateTime().toLocalTime(); + item_last_online->setDate(last_connect_datetime); + if (participant->isEnabled() && last_connect_datetime.isValid()){ + // Set background color + QColor back_color = TeraForm::getGradientColor(0, 7, 14, static_cast(last_connect_datetime.daysTo(QDateTime::currentDateTime()))); + back_color.setAlphaF(0.5); + item_last_online->setBackground(back_color); + } + } + + +} + void ProjectWidget::queryServicesProject() { // Query services for this project @@ -403,6 +560,14 @@ void ProjectWidget::querySessionTypesProject() queryDataRequest(WEB_SESSIONTYPEPROJECT_PATH, args); } +void ProjectWidget::queryTestTypesProject() +{ + QUrlQuery args; + args.addQueryItem(WEB_QUERY_ID_PROJECT, QString::number(m_data->getId())); + args.addQueryItem(WEB_QUERY_WITH_TESTTYPES, "1"); + queryDataRequest(WEB_TESTTYPEPROJECT_PATH, args); +} + void ProjectWidget::updateControlsState() { if (dataIsNew() && ui->tabNav->count() > 1){ @@ -413,6 +578,7 @@ void ProjectWidget::updateControlsState() // Move projects list to first tab ui->tabSessionTypes->layout()->removeWidget(ui->lstSessionTypes); + ui->tabTestTypes->layout()->removeWidget(ui->lstTestTypes); ui->tabDashboard->layout()->removeWidget(ui->frameButtons); QLabel* lbl = new QLabel(tr("Types de séances associés")); @@ -423,6 +589,10 @@ void ProjectWidget::updateControlsState() ui->tabDashboard->layout()->addWidget(lbl); ui->tabDashboard->layout()->addWidget(ui->lstSessionTypes); + lbl = new QLabel(tr("Types de tests associés")); + lbl->setFont(labelFont); + ui->tabDashboard->layout()->addWidget(lbl); + ui->tabDashboard->layout()->addWidget(ui->lstTestTypes); ui->tabDashboard->layout()->addWidget(ui->frameButtons); ui->frameActions->hide(); // Can't add participant when creating new project @@ -434,6 +604,13 @@ void ProjectWidget::updateControlsState() queryDataRequest(WEB_SESSIONTYPESITE_PATH, args); } + // Query test types + if (m_listTestTypes_items.isEmpty()){ + QUrlQuery args; + args.addQueryItem(WEB_QUERY_ID_SITE, m_data->getFieldValue("id_site").toString()); + queryDataRequest(WEB_TESTTYPESITE_PATH, args); + } + }else{ bool is_project_admin = m_comManager->isCurrentUserProjectAdmin(m_data->getId()); @@ -444,6 +621,7 @@ void ProjectWidget::updateControlsState() ui->tabNav->setTabVisible(ui->tabNav->indexOf(ui->tabUsersDetails), is_project_admin); ui->tabNav->setTabVisible(ui->tabNav->indexOf(ui->tabDevices), is_project_admin); ui->tabNav->setTabVisible(ui->tabNav->indexOf(ui->tabSessionTypes), is_project_admin); + ui->tabNav->setTabVisible(ui->tabNav->indexOf(ui->tabTestTypes), is_project_admin); ui->tabNav->setTabVisible(ui->tabNav->indexOf(ui->tabServicesDetails), is_site_admin || !m_services_tabs.isEmpty()); ui->btnNewGroup->setEnabled(is_project_admin); @@ -467,6 +645,7 @@ void ProjectWidget::updateFieldsValue() if (m_data){ ui->wdgProject->fillFormFromData(m_data->toJson()); ui->lblTitle->setText(m_data->getName()); + ui->icoTitle->setPixmap(QPixmap(m_data->getIconStateFilename())); if (dataIsNew() && m_data->hasFieldName("id_site")){ // New project - locked to that site @@ -477,6 +656,19 @@ void ProjectWidget::updateFieldsValue() bool ProjectWidget::validateData() { + bool editedProjectEnabled = ui->wdgProject->getFieldValue("project_enabled").toBool(); + + if (!editedProjectEnabled && m_data->getFieldValue("project_enabled").toBool()){ + // We changed from "enabled" to "disabled". User confirmation required before proceeding. + GlobalMessageBox msg; + GlobalMessageBox::StandardButton rval = msg.showYesNo(tr("Confirmation - désactivation"), tr("Le project sera désactivé.") + "\n\r" + + tr("Tous les participants seront aussi désactivés et les appareils associés à ceux-ci seront désassociés.") + "\n\r" + + tr("Êtes-vous sûrs de vouloir continuer?")); + if (rval != GlobalMessageBox::Yes){ + undoButtonClicked(); + return false; + } + } return ui->wdgProject->validateFormData(); } @@ -540,6 +732,25 @@ void ProjectWidget::processProjectAccessReply(QList access, QUrlQuery } } +void ProjectWidget::processParticipantsReply(QList participants, QUrlQuery reply_query) +{ + if (!m_data) + return; + + // Check if that reply is for us or not. + if (!reply_query.hasQueryItem(WEB_QUERY_ID_PROJECT)) + return; + + if (reply_query.queryItemValue(WEB_QUERY_ID_PROJECT).toInt() != m_data->getId()) + return; + + // Only update state for now... + for (int i=0; i devices) { if (!m_data) @@ -607,40 +818,12 @@ void ProjectWidget::processServiceProjectsReply(QList services_project for (int i=0; ilstServices->count(); i++){ QListWidgetItem* item = ui->lstServices->item(i); if (item->checkState() == Qt::Unchecked){ - if (std::find(m_listServicesProjects_items.cbegin(), m_listServicesProjects_items.cend(), item) != m_listServicesProjects_items.cend()){ - m_listServicesProjects_items.remove(m_listServicesProjects_items.key(item)); + int item_key = m_listServicesProjects_items.key(item, -1); + if (item_key > 0){ + m_listServicesProjects_items.remove(item_key); } } } - - // Remove service tabs not present anymore - /*if (!dataIsNew()){ - QList tabs_to_del; - for(QWidget* tab: qAsConst(m_services_tabs)){ - if (!ids_service.contains(m_services_tabs.key(tab))){ - tabs_to_del.append(tab); - } - } - for(QWidget *tab: tabs_to_del){ - ui->tabNav->removeTab(ui->tabNav->indexOf(tab)); - tab->deleteLater(); - m_services_tabs.remove(m_services_tabs.key(tab)); - } - }*/ - - /*bool services_empty = m_services.isEmpty(); - foreach(TeraData service, services){ - if(service.getFieldValue("id_project").toInt() == m_data->getId()){ - updateService(&service); - } - } - - if (services_empty){ - QUrlQuery args; - args.addQueryItem(WEB_QUERY_ID_PROJECT, QString::number(m_data->getId())); - args.addQueryItem(WEB_QUERY_WITH_USERGROUPS, "1"); // Includes user groups without any access - queryDataRequest(WEB_SERVICEACCESSINFO_PATH, args); - }*/ } void ProjectWidget::processSessionTypeProjectReply(QList stp_list, QUrlQuery reply_query) @@ -656,8 +839,9 @@ void ProjectWidget::processSessionTypeProjectReply(QList stp_list, QUr for (int i=0; ilstSessionTypes->count(); i++){ QListWidgetItem* item = ui->lstSessionTypes->item(i); if (item->checkState() == Qt::Unchecked){ - if (std::find(m_listSessionTypesProjects_items.cbegin(), m_listSessionTypesProjects_items.cend(), item) != m_listSessionTypesProjects_items.cend()){ - m_listSessionTypesProjects_items.remove(m_listSessionTypesProjects_items.key(item)); + int item_key = m_listSessionTypesProjects_items.key(item, -1); + if (item_key > 0){ + m_listSessionTypesProjects_items.remove(item_key); } } } @@ -670,6 +854,34 @@ void ProjectWidget::processSessionTypeSiteReply(QList sts_list, QUrlQu } } +void ProjectWidget::processTestTypeProjectReply(QList ttp_list, QUrlQuery reply_query) +{ + foreach(TeraData tt_project, ttp_list){ + updateTestTypeProject(&tt_project); + } + + // New list received - disable save button + ui->btnUpdateTestTypes->setEnabled(false); + + // Update used list from what is checked right now + for (int i=0; ilstTestTypes->count(); i++){ + QListWidgetItem* item = ui->lstTestTypes->item(i); + if (item->checkState() == Qt::Unchecked){ + int item_key = m_listTestTypesProjects_items.key(item, -1); + if (item_key > 0){ + m_listTestTypesProjects_items.remove(item_key); + } + } + } +} + +void ProjectWidget::processTestTypeSiteReply(QList tts_list, QUrlQuery reply_query) +{ + foreach(TeraData tt_site, tts_list){ + updateTestTypeSite(&tt_site); + } +} + void ProjectWidget::processStatsReply(TeraData stats, QUrlQuery reply_query) { // Check if stats are for us @@ -694,70 +906,20 @@ void ProjectWidget::processStatsReply(TeraData stats, QUrlQuery reply_query) QVariantList parts_list = stats.getFieldValue("participants").toList(); - for(const QVariant &part:qAsConst(parts_list)){ + /*ui->tableSummary->setRowCount(parts_list.count()); + int current_row = 0;*/ + for(const QVariant &part:std::as_const(parts_list)){ QVariantMap part_info = part.toMap(); - int part_id = part_info["id_participant"].toInt(); - - ui->tableSummary->setRowCount(ui->tableSummary->rowCount()+1); - int current_row = ui->tableSummary->rowCount()-1; - QTableWidgetItem* item = new QTableWidgetItem(QIcon(TeraData::getIconFilenameForDataType(TERADATA_PARTICIPANT)), - part_info["participant_name"].toString()); - m_tableParticipants_items[part_id] = item; - ui->tableSummary->setItem(current_row, 0, item); - - item = new QTableWidgetItem(); - QString status; - if (part_info["participant_enabled"].toBool() == true){ - status = tr("Actif"); - item->setForeground(Qt::green); - }else{ - status = tr("Inactif"); - item->setForeground(Qt::red); - } - item->setText(status); - item->setTextAlignment(Qt::AlignCenter); - ui->tableSummary->setItem(current_row, 1, item); - - item = new TableNumberWidgetItem(part_info["participant_sessions_count"].toString()); - item->setTextAlignment(Qt::AlignCenter); - ui->tableSummary->setItem(current_row, 2, item); - - item = new TableDateWidgetItem(part_info["participant_first_session"].toDateTime().toLocalTime().toString("dd-MM-yyyy hh:mm:ss")); - item->setTextAlignment(Qt::AlignCenter); - ui->tableSummary->setItem(current_row, 3, item); - - QDateTime last_session_datetime = part_info["participant_last_session"].toDateTime().toLocalTime(); - item = new TableDateWidgetItem(last_session_datetime.toString("dd-MM-yyyy hh:mm:ss")); - if (part_info["participant_enabled"].toBool() == true && last_session_datetime.isValid()){ - // Set background color - QColor back_color = TeraForm::getGradientColor(0, 7, 14, static_cast(last_session_datetime.daysTo(QDateTime::currentDateTime()))); - back_color.setAlphaF(0.5); - item->setBackground(back_color); - } - item->setTextAlignment(Qt::AlignCenter); - ui->tableSummary->setItem(current_row, 4, item); - - QString last_connect; - QDateTime last_connect_datetime; - if (part_info.contains("participant_last_online")){ - last_connect_datetime = part_info["participant_last_online"].toDateTime().toLocalTime(); - if (last_connect_datetime.isValid()) - last_connect = last_connect_datetime.toString("dd-MM-yyyy hh:mm:ss"); - } - item = new TableDateWidgetItem(last_connect); - item->setTextAlignment(Qt::AlignCenter); - - if (part_info["participant_enabled"].toBool() == true && last_connect_datetime.isValid()){ - // Set background color - QColor back_color = TeraForm::getGradientColor(0, 5, 10, static_cast(last_connect_datetime.daysTo(QDateTime::currentDateTime()))); - back_color.setAlphaF(0.5); - item->setBackground(back_color); - } - ui->tableSummary->setItem(current_row, 5, item); + TeraData data(TeraDataTypes::TERADATA_PARTICIPANT); + data.fromMap(part_info); + updateParticipant(&data); } - ui->tableSummary->resizeColumnsToContents(); ui->tableSummary->sortByColumn(4, Qt::DescendingOrder); // Sort by last session date + ui->tableSummary->resizeColumnsToContents(); } + + // Connect signal to receive participants updates from now on + connect(m_comManager, &ComManager::participantsReceived, this, &ProjectWidget::processParticipantsReply); } void ProjectWidget::processPostOKReply(QString path) @@ -765,6 +927,13 @@ void ProjectWidget::processPostOKReply(QString path) if (path == WEB_PROJECTINFO_PATH){ // Update current user access list for the newly created project m_comManager->doUpdateCurrentUser(); + if (m_refreshProjectParticipants){ + // Also refresh participants list to show disabled participants + m_refreshProjectParticipants = false; + QUrlQuery args; + args.addQueryItem(WEB_QUERY_ID_PROJECT, QString::number(m_data->getId())); + queryDataRequest(WEB_PARTICIPANTINFO_PATH, args); + } } if (path == WEB_SESSIONTYPEPROJECT_PATH || path == WEB_TESTTYPEPROJECT_PATH){ // Also update associated services @@ -812,7 +981,7 @@ void ProjectWidget::btnUpdateAccess_clicked() QMap::iterator i; //for (int i=0; irow(); QComboBox* combo_roles = dynamic_cast(ui->tableUserGroups->cellWidget(row,1)); @@ -1033,25 +1202,18 @@ void ProjectWidget::on_tabNav_currentChanged(int index) if (current_tab == ui->tabSessionTypes){ // Session types - /*if (!ui->wdgSessionTypes->layout()){ - QHBoxLayout* layout = new QHBoxLayout(); - layout->setMargin(0); - ui->wdgSessionTypes->setLayout(layout); - } - if (ui->wdgSessionTypes->layout()->count() == 0){ - args.addQueryItem(WEB_QUERY_ID_PROJECT, QString::number(m_data->getId())); - DataListWidget* stlist_editor = new DataListWidget(m_comManager, TERADATA_SESSIONTYPE, WEB_SESSIONTYPEPROJECT_PATH, args, QStringList(), ui->wdgSessionTypes); - // m_limited = true = user only, not project admin - stlist_editor->setPermissions(!m_limited, !m_limited); - stlist_editor->setFilterText(tr("Seuls les types de séance associés au projet sont affichés.")); - ui->wdgSessionTypes->layout()->addWidget(stlist_editor); - }*/ - if (m_listSessionTypesProjects_items.isEmpty()){ querySessionTypesProject(); } } + if (current_tab == ui->tabTestTypes){ + // Test types + if (m_listTestTypesProjects_items.isEmpty()){ + queryTestTypesProject(); + } + } + if (current_tab == ui->tabServicesDetails){ ui->tabManageServices->setCurrentIndex(0); } @@ -1108,22 +1270,8 @@ void ProjectWidget::on_btnUpdateSessionTypes_clicked() item_obj.insert("id_session_type", session_type_id); st_projects.append(item_obj); } - /*if (item->checkState() == Qt::Unchecked){ - // Unselected - if (std::find(m_listSessionTypesProjects_items.cbegin(), m_listSessionTypesProjects_items.cend(), item) != m_listSessionTypesProjects_items.cend()){ - removed_sts = true; - } - }*/ } - /*if (removed_sts){ - GlobalMessageBox msgbox; - int rval = msgbox.showYesNo(tr("Suppression de type de séance associé"), tr("Au moins un type de séance a été retiré de ce project. S'il y a des types de séances qui utilisent ce service, elles ne seront plus accessibles.\nSouhaitez-vous continuer?")); - if (rval != GlobalMessageBox::Yes){ - return; - } - }*/ - project_obj.insert("sessiontypes", st_projects); base_obj.insert("project", project_obj); document.setObject(base_obj); @@ -1271,3 +1419,65 @@ void ProjectWidget::on_btnManageUsers_clicked() m_diag_editor->open(); } + +void ProjectWidget::on_chkShowInactive_stateChanged(int state) +{ + for(QTableWidgetItem* item: m_tableParticipants_items){ + int row = item->row(); + if (ui->chkShowInactive->isChecked()){ + if (ui->tableSummary->isRowHidden(row)) + ui->tableSummary->showRow(row); + }else{ + bool active = ui->tableSummary->item(row, 1)->foreground() != Qt::red; + if (!active){ + ui->tableSummary->hideRow(row); + } + } + } + ui->tableSummary->resizeColumnsToContents(); +} + + +void ProjectWidget::on_lstTestTypes_itemChanged(QListWidgetItem *item) +{ + // Check for changed items + bool has_changes = false; + if (m_listTestTypesProjects_items.key(item) > 0 && item->checkState() == Qt::Unchecked){ + // Item deselected + has_changes = true; + }else{ + if (m_listTestTypesProjects_items.key(item, -1) <= 0 && item->checkState() == Qt::Checked){ + // Item selected + has_changes = true; + } + } + + ui->btnUpdateTestTypes->setEnabled(has_changes); +} + + +void ProjectWidget::on_btnUpdateTestTypes_clicked() +{ + QJsonDocument document; + QJsonObject base_obj; + QJsonObject project_obj; + QJsonArray tt_projects; + + project_obj.insert("id_project", m_data->getId()); + for (int i=0; ilstTestTypes->count(); i++){ + QListWidgetItem* item = ui->lstTestTypes->item(i); + int test_type_id = m_listTestTypes_items.key(item, 0); + if (item->checkState() == Qt::Checked){ + // New item selected + QJsonObject item_obj; + item_obj.insert("id_test_type", test_type_id); + tt_projects.append(item_obj); + } + } + + project_obj.insert("testtypes", tt_projects); + base_obj.insert("project", project_obj); + document.setObject(base_obj); + postDataRequest(WEB_TESTTYPEPROJECT_PATH, document.toJson()); +} + diff --git a/client/src/editors/ProjectWidget.h b/client/src/editors/ProjectWidget.h index 93fb78fc..2b6ddbaf 100644 --- a/client/src/editors/ProjectWidget.h +++ b/client/src/editors/ProjectWidget.h @@ -34,15 +34,16 @@ class ProjectWidget : public DataEditorWidget private slots: void processFormsReply(QString form_type, QString data); void processProjectAccessReply(QList access, QUrlQuery reply_query); + void processParticipantsReply(QList participants, QUrlQuery reply_query); void processDevicesReply(QList devices); void processDevicesProjectsReply(QList devices_projects); void processServiceProjectsReply(QList services); void processSessionTypeProjectReply(QList stp_list, QUrlQuery reply_query); void processSessionTypeSiteReply(QList sts_list, QUrlQuery reply_query); + void processTestTypeProjectReply(QList ttp_list, QUrlQuery reply_query); + void processTestTypeSiteReply(QList tts_list, QUrlQuery reply_query); void processStatsReply(TeraData stats, QUrlQuery reply_query); - void processPostOKReply(QString path); - void deleteDataReply(QString path, int del_id); void btnUpdateAccess_clicked(); @@ -51,56 +52,58 @@ private slots: void on_icoUsers_clicked(); void on_icoSessions_clicked(); void on_btnUserGroups_clicked(); - void userGroupsEditor_finished(); + void userGroupsEditor_finished(); void usersEditor_finished(); void on_tableSummary_itemDoubleClicked(QTableWidgetItem *item); - void on_lstServices_itemChanged(QListWidgetItem *item); - void on_tabNav_currentChanged(int index); void on_tabManageUsers_currentChanged(int index); void on_tabManageServices_currentChanged(int index); void on_btnUpdateSessionTypes_clicked(); - void on_lstSessionTypes_itemChanged(QListWidgetItem *item); - - void on_treeDevices_itemChanged(QTreeWidgetItem *item, int column); - void on_btnUpdateDevices_clicked(); - void on_btnNewParticipant_clicked(); void on_btnNewGroup_clicked(); void on_btnDelete_clicked(); - void on_tableSummary_itemSelectionChanged(); - void on_btnManageUsers_clicked(); + void on_chkShowInactive_stateChanged(int state); + + void on_lstTestTypes_itemChanged(QListWidgetItem *item); + + void on_btnUpdateTestTypes_clicked(); + private: - Ui::ProjectWidget *ui; + Ui::ProjectWidget *ui; + + QHash m_tableUsers_items; + QMap m_tableUserGroups_items; + QHash m_tableParticipants_items; + QHash m_listGroups_items; + + QHash m_treeDevices_items; + QHash m_treeDevicesProjects_items; - QMap m_tableUsers_items; - QMap m_tableUserGroups_items; - QMap m_tableParticipants_items; - QMap m_listGroups_items; + QHash m_listServicesProjects_items; + QHash m_listServices_items; + QHash m_services_keys; - QMap m_treeDevices_items; - QMap m_treeDevicesProjects_items; + QHash m_listSessionTypesProjects_items; + QHash m_listSessionTypes_items; - QMap m_listServicesProjects_items; - QMap m_listServices_items; - QMap m_services_keys; + QHash m_listTestTypesProjects_items; + QHash m_listTestTypes_items; - QMap m_listSessionTypesProjects_items; - QMap m_listSessionTypes_items; + QHash m_services_tabs; - QMap m_services_tabs; + BaseDialog* m_diag_editor; - BaseDialog* m_diag_editor; + bool m_refreshProjectParticipants; void connectSignals(); void initUI(); @@ -112,9 +115,13 @@ private slots: void updateServiceProject(const TeraData* sp); void updateSessionTypeProject(const TeraData* stp); void updateSessionTypeSite(const TeraData* sts); + void updateTestTypeProject(const TeraData* ttp); + void updateTestTypeSite(const TeraData* tts); + void updateParticipant(const TeraData* participant); void queryServicesProject(); void querySessionTypesProject(); + void queryTestTypesProject(); void queryUserGroupsProjectAccess(); void queryUsers(); diff --git a/client/src/editors/ProjectWidget.ui b/client/src/editors/ProjectWidget.ui index 12af75a5..4f6e36aa 100644 --- a/client/src/editors/ProjectWidget.ui +++ b/client/src/editors/ProjectWidget.ui @@ -162,7 +162,7 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - 0 + 3 @@ -195,6 +195,12 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, 0 + + + 16777215 + 150 + + @@ -227,6 +233,12 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + + + 0 + 0 + + @@ -285,10 +297,19 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + + + 0 + 0 + + Résumé + + 15 + @@ -491,6 +512,13 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + + + + Qt::Horizontal + + + @@ -562,6 +590,13 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + + + + Afficher les participants inactifs + + + @@ -709,7 +744,7 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, QTabWidget::West - 0 + 1 @@ -815,6 +850,9 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, true + + false + Utilisateur @@ -955,6 +993,9 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, true + + false + Groupe Utilisateur @@ -1010,6 +1051,9 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + + QAbstractItemView::NoEditTriggers + 24 @@ -1046,6 +1090,56 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + + + + :/icons/test_type.png:/icons/test_type.png + + + Types de tests + + + + + + QAbstractItemView::NoEditTriggers + + + + 24 + 24 + + + + + + + + + 0 + 32 + + + + PointingHandCursor + + + Mettre à jour les types de tests associés + + + + :/icons/save.png:/icons/save.png + + + + 24 + 24 + + + + + + @@ -1061,14 +1155,17 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, QAbstractItemView::NoEditTriggers - QAbstractItemView::NoSelection + QAbstractItemView::SingleSelection - 20 - 20 + 24 + 24 + + 0 + false @@ -1141,6 +1238,9 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + + QAbstractItemView::NoEditTriggers + 24 @@ -1184,14 +1284,19 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + + + + 0 + 0 + 100 + 30 + + + + - - TeraForm - QWidget -
TeraForm.h
- 1 -
ClickableLabel QLabel @@ -1201,6 +1306,12 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, clicked() + + TeraForm + QWidget +
TeraForm.h
+ 1 +
diff --git a/client/src/editors/ServiceConfigWidget.cpp b/client/src/editors/ServiceConfigWidget.cpp index dcea5a1c..38879a1f 100644 --- a/client/src/editors/ServiceConfigWidget.cpp +++ b/client/src/editors/ServiceConfigWidget.cpp @@ -7,7 +7,6 @@ ServiceConfigWidget::ServiceConfigWidget(ComManager *comMan, const QString id_fi { ui->setupUi(this); - setAttribute(Qt::WA_StyledBackground); //Required to set a background image setLimited(false); @@ -119,7 +118,7 @@ void ServiceConfigWidget::updateFieldsValue(){ QVariantList specifics = m_data->getFieldValue("service_config_specifics").toList(); if (!specifics.isEmpty()){ ui->cmbSpecific->addItem(tr("Globale")); - for (const QVariant &specific_id:qAsConst(specifics)){ + for (const QVariant &specific_id:std::as_const(specifics)){ ui->cmbSpecific->addItem(specific_id.toString()); } ui->frameSpecific->show(); @@ -146,7 +145,7 @@ void ServiceConfigWidget::updateService(const TeraData *service) // New service with a config item = new QListWidgetItem(QIcon(TeraData::getIconFilenameForDataType(TERADATA_SERVICE_CONFIG)), service_name); item->setToolTip(service_name); - item->setSizeHint(QSize(ui->lstServiceConfig->width(), 64)); + //item->setSizeHint(QSize(ui->lstServiceConfig->width(), 64)); ui->lstServiceConfig->addItem(item); m_listServices_items[id_service] = item; } @@ -156,8 +155,7 @@ void ServiceConfigWidget::updateService(const TeraData *service) bool ServiceConfigWidget::validateData(){ bool valid = false; - valid = ui->wdgServiceConfig->validateFormData() & ui->wdgServiceConfigConfig->validateFormData() ; - + valid = ui->wdgServiceConfig->validateFormData() && ui->wdgServiceConfigConfig->validateFormData() ; return valid; } @@ -290,7 +288,7 @@ void ServiceConfigWidget::on_btnSave_clicked() if (!invalids.isEmpty()){ QString msg = tr("Les champs suivants doivent être complétés:") +"
    "; - for (const QString &field:qAsConst(invalids)){ + for (const QString &field:std::as_const(invalids)){ msg += "
  • " + field + "
  • "; } msg += "
"; @@ -307,22 +305,21 @@ void ServiceConfigWidget::on_btnUndo_clicked() undoData(); } -void ServiceConfigWidget::on_cmbSpecific_currentIndexChanged(const QString ¤t_id) +void ServiceConfigWidget::on_cmbSpecific_currentIndexChanged(const int ¤t_index) { if (isLoading()) return; - // Reload data for requested config int id_service = m_listServices_items.key(ui->lstServiceConfig->currentItem()); QUrlQuery args; args.addQueryItem(m_idFieldName, QString::number(m_idFieldValue)); args.addQueryItem(WEB_QUERY_ID_SERVICE, QString::number(id_service)); if (ui->cmbSpecific->currentIndex()>0){ - args.addQueryItem(WEB_QUERY_ID_SPECIFIC, current_id); - m_specificId = current_id; + args.addQueryItem(WEB_QUERY_ID_SPECIFIC, ui->cmbSpecific->currentText()); + m_specificId = ui->cmbSpecific->currentText(); }else{ m_specificId.clear(); } queryDataRequest(WEB_SERVICECONFIGINFO_PATH, args); - } + diff --git a/client/src/editors/ServiceConfigWidget.h b/client/src/editors/ServiceConfigWidget.h index ab78edae..ccef6dde 100644 --- a/client/src/editors/ServiceConfigWidget.h +++ b/client/src/editors/ServiceConfigWidget.h @@ -57,7 +57,7 @@ private slots: void on_lstServiceConfig_itemClicked(QListWidgetItem *item); void on_btnSave_clicked(); void on_btnUndo_clicked(); - void on_cmbSpecific_currentIndexChanged(const QString ¤t_id); + void on_cmbSpecific_currentIndexChanged(const int ¤t_index); }; #endif // SERVICECONFIGWIDGET_H diff --git a/client/src/editors/ServiceConfigWidget.ui b/client/src/editors/ServiceConfigWidget.ui index 988ca834..000a8702 100644 --- a/client/src/editors/ServiceConfigWidget.ui +++ b/client/src/editors/ServiceConfigWidget.ui @@ -16,20 +16,8 @@ - - - 0 - - - 0 - - - 0 - - - 0 - - + + @@ -39,31 +27,46 @@ - 115 + 150 16777215 QAbstractItemView::NoEditTriggers + + false + + + Qt::IgnoreAction + + + QAbstractItemView::SelectRows + - 32 - 32 + 48 + 48 + + Qt::ElideRight + QListView::Static - QListView::TopToBottom + QListView::LeftToRight + + + true - - QListView::Fixed + + QListView::Batched - 100 + 150 90 @@ -76,6 +79,9 @@ true + + false + Qt::AlignCenter @@ -135,8 +141,8 @@ 0 0 - 609 - 389 + 556 + 371 diff --git a/client/src/editors/ServiceWidget.cpp b/client/src/editors/ServiceWidget.cpp index 143cb34d..11b37f61 100644 --- a/client/src/editors/ServiceWidget.cpp +++ b/client/src/editors/ServiceWidget.cpp @@ -150,11 +150,8 @@ void ServiceWidget::postServiceRoles() QJsonObject base_obj; QJsonArray roles; - for (const QString &role_name:qAsConst(m_serviceRoles)){ + for (const QString &role_name:std::as_const(m_serviceRoles)){ int role_id = m_serviceRoles.key(role_name); -/* for (int i=0; icheckState(0) == Qt::Checked){ QJsonObject data_obj; @@ -198,7 +195,7 @@ void ServiceWidget::postServiceProjects() QJsonObject base_obj; QJsonArray projects; - for(QTreeWidgetItem* item: qAsConst(m_treeProjects_items)){ + for(QTreeWidgetItem* item: std::as_const(m_treeProjects_items)){ int project_id = m_treeProjects_items.key(item); if (item->checkState(0) == Qt::Checked){ QJsonObject data_obj; diff --git a/client/src/editors/ServiceWidget.ui b/client/src/editors/ServiceWidget.ui index 1e191e97..927bc751 100644 --- a/client/src/editors/ServiceWidget.ui +++ b/client/src/editors/ServiceWidget.ui @@ -246,6 +246,9 @@ + + Qt::NoFocus + QAbstractItemView::NoEditTriggers diff --git a/client/src/editors/SessionTypeWidget.cpp b/client/src/editors/SessionTypeWidget.cpp index 2858c5a7..3a7c1983 100644 --- a/client/src/editors/SessionTypeWidget.cpp +++ b/client/src/editors/SessionTypeWidget.cpp @@ -20,6 +20,7 @@ SessionTypeWidget::SessionTypeWidget(ComManager *comMan, const TeraData *data, Q connectSignals(); // Query form definition + ui->wdgSessionType->setComManager(m_comManager); queryDataRequest(WEB_FORMS_PATH, QUrlQuery(WEB_FORMS_QUERY_SESSION_TYPE)); if (!data->isNew()){ QUrlQuery args(WEB_FORMS_QUERY_SESSION_TYPE_CONFIG); @@ -27,7 +28,6 @@ SessionTypeWidget::SessionTypeWidget(ComManager *comMan, const TeraData *data, Q queryDataRequest(WEB_FORMS_PATH, args); } - ui->wdgSessionType->setComManager(m_comManager); setData(data); } @@ -51,7 +51,7 @@ void SessionTypeWidget::saveData(bool signal){ QJsonArray sites; // Sites - for(QTreeWidgetItem* item:qAsConst(m_treeSites_items)){ + for(QTreeWidgetItem* item:std::as_const(m_treeSites_items)){ if (item->checkState(0) == Qt::Checked){ int site_id = m_treeSites_items.key(item); QJsonObject data_obj; @@ -67,7 +67,7 @@ void SessionTypeWidget::saveData(bool signal){ } // Projects - for(QTreeWidgetItem* item:qAsConst(m_treeProjects_items)){ + for(QTreeWidgetItem* item:std::as_const(m_treeProjects_items)){ if (item->checkState(0) == Qt::Checked){ int project_id = m_treeProjects_items.key(item); QJsonObject data_obj; @@ -238,7 +238,7 @@ void SessionTypeWidget::postSessionTypeSites() QJsonObject base_obj; QJsonArray sites; - for(QTreeWidgetItem* item: qAsConst(m_treeSites_items)){ + for(QTreeWidgetItem* item: std::as_const(m_treeSites_items)){ int site_id = m_treeSites_items.key(item); if (item->checkState(0) == Qt::Checked){ QJsonObject data_obj; @@ -261,7 +261,7 @@ void SessionTypeWidget::postSessionTypeProjects() QJsonObject base_obj; QJsonArray projects; - for(QTreeWidgetItem* item: qAsConst(m_treeProjects_items)){ + for(QTreeWidgetItem* item: std::as_const(m_treeProjects_items)){ int proj_id = m_treeProjects_items.key(item); if (item->checkState(0) == Qt::Checked){ QJsonObject data_obj; @@ -293,7 +293,7 @@ bool SessionTypeWidget::validateProjects() { if (!m_comManager->isCurrentUserSuperAdmin()){ bool at_least_one_selected = false; - for(QTreeWidgetItem* site_item:qAsConst(m_treeSites_items)){ + for(QTreeWidgetItem* site_item:std::as_const(m_treeSites_items)){ if (site_item->checkState(0) == Qt::Checked){ at_least_one_selected = true; break; diff --git a/client/src/editors/SessionTypeWidget.ui b/client/src/editors/SessionTypeWidget.ui index 00cd1026..d51f586e 100644 --- a/client/src/editors/SessionTypeWidget.ui +++ b/client/src/editors/SessionTypeWidget.ui @@ -135,7 +135,7 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
- :/icons/session_type.png + :/icons/session_type.png true @@ -163,7 +163,7 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - + :/icons/info.png:/icons/info.png @@ -227,7 +227,7 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, Éditer
- + :/icons/edit.png:/icons/edit.png @@ -272,7 +272,7 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, Sauvegarder - + :/icons/save.png:/icons/save.png @@ -298,7 +298,7 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, Annuler - + :/icons/undo.png:/icons/undo.png @@ -316,7 +316,7 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - + :/icons/site-icon.png:/icons/site-icon.png @@ -331,6 +331,9 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, 0 + + Qt::NoFocus + QAbstractItemView::NoEditTriggers @@ -368,7 +371,7 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, Mettre à jour les sites / projets associés - + :/icons/save.png:/icons/save.png @@ -383,7 +386,7 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - + :/icons/config.png:/icons/config.png @@ -418,7 +421,7 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, Mettre à jour la configuration - + :/icons/save.png:/icons/save.png @@ -443,8 +446,6 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, 1 - - - + diff --git a/client/src/editors/SessionWidget.cpp b/client/src/editors/SessionWidget.cpp index 74a9a7b1..7628d9b0 100644 --- a/client/src/editors/SessionWidget.cpp +++ b/client/src/editors/SessionWidget.cpp @@ -98,7 +98,7 @@ void SessionWidget::saveData(bool signal){ if (ui->wdgSessionInvitees->getDevicesInSessionCount() > 0){ QJsonArray data_list; QList ids = ui->wdgSessionInvitees->getDevicesIdsInSession(); - for(const int &data_id:qAsConst(ids)){ + for(const int &data_id:std::as_const(ids)){ data_list.append(QJsonValue(data_id)); } session_data.insert("session_devices_ids", data_list); @@ -107,7 +107,7 @@ void SessionWidget::saveData(bool signal){ if (ui->wdgSessionInvitees->getParticipantsInSessionCount() > 0){ QJsonArray data_list; QList ids = ui->wdgSessionInvitees->getParticipantsIdsInSession(); - for(const int &data_id:qAsConst(ids)){ + for(const int &data_id:std::as_const(ids)){ data_list.append(QJsonValue(data_id)); } session_data.insert("session_participants_ids", data_list); @@ -116,7 +116,7 @@ void SessionWidget::saveData(bool signal){ if (ui->wdgSessionInvitees->getUsersInSessionCount() > 0){ QJsonArray data_list; QList ids = ui->wdgSessionInvitees->getUsersIdsInSession(); - for(const int &data_id:qAsConst(ids)){ + for(const int &data_id:std::as_const(ids)){ data_list.append(QJsonValue(data_id)); } session_data.insert("session_users_ids", data_list); @@ -248,7 +248,7 @@ void SessionWidget::updateSessionParticipants() if (m_data->hasFieldName("session_participants")){ QVariantList session_parts_list = m_data->getFieldValue("session_participants").toList(); QList participants; - for(const QVariant &session_part:qAsConst(session_parts_list)){ + for(const QVariant &session_part:std::as_const(session_parts_list)){ QVariantMap part_info = session_part.toMap(); TeraData part(TERADATA_PARTICIPANT); part.fromMap(part_info); @@ -291,7 +291,7 @@ void SessionWidget::updateSessionUsers() QVariantList session_users_list = m_data->getFieldValue("session_users").toList(); QList users; - for(const QVariant &session_user:qAsConst(session_users_list)){ + for(const QVariant &session_user:std::as_const(session_users_list)){ QVariantMap user_info = session_user.toMap(); TeraData user(TERADATA_USER); user.fromMap(user_info); @@ -330,7 +330,7 @@ void SessionWidget::updateSessionDevices() QVariantList session_devices_list = m_data->getFieldValue("session_devices").toList(); QList devices; - for(const QVariant &session_device:qAsConst(session_devices_list)){ + for(const QVariant &session_device:std::as_const(session_devices_list)){ QVariantMap device_info = session_device.toMap(); TeraData device(TERADATA_DEVICE); device.fromMap(device_info); @@ -560,7 +560,7 @@ void SessionWidget::sessionInviteesChanged(QStringList user_uuids, QStringList p if (ui->wdgSessionInvitees->getDevicesInSessionCount() > 0){ QList ids = ui->wdgSessionInvitees->getDevicesIdsInSession(); - for(const int &data_id:qAsConst(ids)){ + for(const int &data_id:std::as_const(ids)){ data_list.append(QJsonValue(data_id)); } } @@ -573,7 +573,7 @@ void SessionWidget::sessionInviteesChanged(QStringList user_uuids, QStringList p if (ui->wdgSessionInvitees->getParticipantsInSessionCount() > 0){ QList ids = ui->wdgSessionInvitees->getParticipantsIdsInSession(); - for(const int &data_id:qAsConst(ids)){ + for(const int &data_id:std::as_const(ids)){ data_list.append(QJsonValue(data_id)); } } @@ -584,7 +584,7 @@ void SessionWidget::sessionInviteesChanged(QStringList user_uuids, QStringList p } if (ui->wdgSessionInvitees->getUsersInSessionCount() > 0){ QList ids = ui->wdgSessionInvitees->getUsersIdsInSession(); - for(const int &data_id:qAsConst(ids)){ + for(const int &data_id:std::as_const(ids)){ data_list.append(QJsonValue(data_id)); } } diff --git a/client/src/editors/SessionWidget.ui b/client/src/editors/SessionWidget.ui index ebb89dad..0fa2119b 100644 --- a/client/src/editors/SessionWidget.ui +++ b/client/src/editors/SessionWidget.ui @@ -165,36 +165,36 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + + + + + 0 + 0 + + + + + 200 + 30 + + + + + 10 + true + + + + État de la séance + + + Qt::AlignCenter + + + - - - - - 0 - 0 - - - - - 0 - 30 - - - - - 10 - true - - - - État de la séance - - - Qt::AlignCenter - - - @@ -774,12 +774,6 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - - TeraForm - QWidget -
TeraForm.h
- 1 -
ClickableLabel QLabel @@ -789,6 +783,12 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, clicked() + + TeraForm + QWidget +
TeraForm.h
+ 1 +
SessionInviteWidget QWidget diff --git a/client/src/editors/SiteWidget.cpp b/client/src/editors/SiteWidget.cpp index 06706f4a..4f5c1f00 100644 --- a/client/src/editors/SiteWidget.cpp +++ b/client/src/editors/SiteWidget.cpp @@ -3,12 +3,13 @@ #include "editors/DataListWidget.h" -SiteWidget::SiteWidget(ComManager *comMan, const TeraData *data, QWidget *parent) : +SiteWidget::SiteWidget(ComManager *comMan, const TeraData *data, const bool configMode, QWidget *parent) : DataEditorWidget(comMan, data, parent), ui(new Ui::SiteWidget) { m_diag_editor = nullptr; m_devicesCount = 0; + m_configMode = configMode; ui->setupUi(this); @@ -79,6 +80,7 @@ void SiteWidget::connectSignals() connect(m_comManager, &ComManager::deviceSitesReceived, this, &SiteWidget::processDeviceSiteAccessReply); connect(m_comManager, &ComManager::servicesSitesReceived, this, &SiteWidget::processServiceSiteAccessReply); connect(m_comManager, &ComManager::sessionTypesSitesReceived, this, &SiteWidget::processSessionTypeSiteAccessReply); + connect(m_comManager, &ComManager::testTypesSitesReceived, this, &SiteWidget::processTestTypeSiteAccessReply); connect(m_comManager, &ComManager::statsReceived, this, &SiteWidget::processStatsReply); connect(ui->btnUpdateRoles, &QPushButton::clicked, this, &SiteWidget::btnUpdateAccess_clicked); @@ -150,12 +152,19 @@ void SiteWidget::updateControlsState() ui->tabNav->setTabVisible(ui->tabNav->indexOf(ui->tabDevices), is_site_admin /*(is_site_admin && m_devicesCount>0) || is_super_admin*/); ui->tabNav->setTabVisible(ui->tabNav->indexOf(ui->tabServices), is_site_admin); ui->tabNav->setTabVisible(ui->tabNav->indexOf(ui->tabSessionTypes), is_site_admin); + ui->tabNav->setTabVisible(ui->tabNav->indexOf(ui->tabTestTypes), is_site_admin); ui->btnUpdateServices->setVisible(is_super_admin); ui->btnUpdateDevices->setVisible(is_super_admin); - ui->btnEditDevices->setVisible(is_site_admin); - /*ui->btnUpdateRoles->setVisible(is_site_admin); - ui->btnUserGroups->setVisible(is_site_admin);*/ + ui->btnUpdateSessionTypes->setVisible(is_super_admin); + ui->btnUpdateTestTypes->setVisible(is_super_admin); + ui->btnEditDevices->setVisible(is_site_admin && !m_configMode); + + ui->lblAdminSessionTypes->setVisible(!is_super_admin); + ui->lblAdminTestTypes->setVisible(!is_super_admin); + + ui->btnEditSessionTypes->setVisible(!m_configMode && is_super_admin); + ui->btnUserGroups->setVisible(!m_configMode); ui->grpSummary->setVisible(!dataIsNew()); if (dataIsNew()){ @@ -225,10 +234,20 @@ void SiteWidget::querySessionTypeSiteAccess() queryDataRequest(WEB_SESSIONTYPESITE_PATH, args); } +void SiteWidget::queryTestTypeSiteAccess() +{ + // Query session types for this site + QUrlQuery args; + args.addQueryItem(WEB_QUERY_ID_SITE, QString::number(m_data->getId())); + args.addQueryItem(WEB_QUERY_WITH_TESTTYPES, "1"); + queryDataRequest(WEB_TESTTYPESITE_PATH, args); +} + void SiteWidget::processFormsReply(QString form_type, QString data) { if (form_type == WEB_FORMS_QUERY_SITE){ - ui->wdgSite->buildUiFromStructure(data); + if (!ui->wdgSite->formHasStructure()) + ui->wdgSite->buildUiFromStructure(data); return; } } @@ -264,8 +283,9 @@ void SiteWidget::processServiceSiteAccessReply(QList service_sites, QU for (int i=0; ilstServices->count(); i++){ QListWidgetItem* item = ui->lstServices->item(i); if (item->checkState() == Qt::Unchecked){ - if (std::find(m_listServicesSites_items.cbegin(), m_listServicesSites_items.cend(), item) != m_listServicesSites_items.cend()){ - m_listServicesSites_items.remove(m_listServicesSites_items.key(item)); + int item_key = m_listServicesSites_items.key(item, -1); + if (item_key > 0){ + m_listServicesSites_items.remove(item_key); } } } @@ -284,8 +304,9 @@ void SiteWidget::processDeviceSiteAccessReply(QList device_sites, QUrl for (int i=0; ilstDevices->count(); i++){ QListWidgetItem* item = ui->lstDevices->item(i); if (item->checkState() == Qt::Unchecked){ - if (std::find(m_listDevicesSites_items.cbegin(), m_listDevicesSites_items.cend(), item) != m_listDevicesSites_items.cend()){ - m_listDevicesSites_items.remove(m_listDevicesSites_items.key(item)); + int item_key = m_listDevicesSites_items.key(item, -1); + if (item_key > 0){ + m_listDevicesSites_items.remove(item_key); } } } @@ -304,8 +325,9 @@ void SiteWidget::processSessionTypeSiteAccessReply(QList st_sites, QUr for (int i=0; ilstSessionTypes->count(); i++){ QListWidgetItem* item = ui->lstSessionTypes->item(i); if (item->checkState() == Qt::Unchecked){ - if (std::find(m_listSessionTypeSites_items.cbegin(), m_listSessionTypeSites_items.cend(), item) != m_listSessionTypeSites_items.cend()){ - m_listSessionTypeSites_items.remove(m_listSessionTypeSites_items.key(item)); + int item_key = m_listSessionTypeSites_items.key(item, -1); + if (item_key > 0){ + m_listSessionTypeSites_items.remove(item_key); } } } @@ -314,6 +336,27 @@ void SiteWidget::processSessionTypeSiteAccessReply(QList st_sites, QUr ui->btnUpdateSessionTypes->setEnabled(false); } +void SiteWidget::processTestTypeSiteAccessReply(QList tt_sites, QUrlQuery reply_query) +{ + for(const TeraData &tt_site: tt_sites){ + updateTestTypeSite(&tt_site); + } + + // Update used list from what is checked right now + for (int i=0; ilstTestTypes->count(); i++){ + QListWidgetItem* item = ui->lstTestTypes->item(i); + if (item->checkState() == Qt::Unchecked){ + int item_key = m_listTestTypeSites_items.key(item, -1); + if (item_key > 0){ + m_listTestTypeSites_items.remove(item_key); + } + } + } + + // New list received - disable save button + ui->btnUpdateTestTypes->setEnabled(false); +} + void SiteWidget::processStatsReply(TeraData stats, QUrlQuery reply_query) { @@ -456,6 +499,44 @@ void SiteWidget::updateSessionTypeSite(const TeraData *st_site) } } +void SiteWidget::updateTestTypeSite(const TeraData *tt_site) +{ + int id_site = tt_site->getFieldValue("id_site").toInt(); + + if (id_site != m_data->getId() && id_site>0) + return; // Not for us + + int id_test_type = tt_site->getFieldValue("id_test_type").toInt(); + QString tt_name = tt_site->getFieldValue("test_type_name").toString(); + QListWidgetItem* item; + + if (m_listTestTypes_items.contains(id_test_type)){ + item = m_listTestTypes_items[id_test_type]; + }else{ + item = new QListWidgetItem(QIcon(TeraData::getIconFilenameForDataType(TeraDataTypes::TERADATA_TESTTYPE)), tt_name); + ui->lstTestTypes->addItem(item); + m_listTestTypes_items[id_test_type] = item; + } + + if (!tt_name.isEmpty()) + item->setText(tt_name); + + int id_test_type_site = tt_site->getId(); + if (id_test_type_site > 0){ + if (m_comManager->isCurrentUserSuperAdmin()) + item->setCheckState(Qt::Checked); + if (!m_listTestTypeSites_items.contains(id_test_type_site)){ + m_listTestTypeSites_items[id_test_type_site] = item; + } + }else{ + if (m_comManager->isCurrentUserSuperAdmin()) + item->setCheckState(Qt::Unchecked); + if (m_listTestTypeSites_items.contains(id_test_type_site)){ + m_listTestTypeSites_items.remove(id_test_type_site); + } + } +} + void SiteWidget::processPostOKReply(QString path) { if (path == WEB_SITEINFO_PATH){ @@ -479,7 +560,7 @@ void SiteWidget::btnUpdateAccess_clicked() QJsonObject base_obj; QJsonArray roles; - for (QTableWidgetItem* item: qAsConst(m_tableUserGroups_items)){ + for (QTableWidgetItem* item: std::as_const(m_tableUserGroups_items)){ int user_group_id = m_tableUserGroups_items.key(item); int row = item->row(); // } @@ -598,27 +679,13 @@ void SiteWidget::on_tabNav_currentChanged(int index) if (m_listDevices_items.isEmpty()){ queryDeviceSiteAccess(); } - // Devices - /*if (!ui->wdgDevices->layout()){ - QHBoxLayout* layout = new QHBoxLayout(); - layout->setMargin(0); - ui->wdgDevices->setLayout(layout); - } - if (ui->wdgDevices->layout()->count() == 0){ - args.addQueryItem(WEB_QUERY_WITH_PARTICIPANTS, ""); - args.addQueryItem(WEB_QUERY_WITH_SITES, ""); - DataListWidget* deviceslist_editor = new DataListWidget(m_comManager, TERADATA_DEVICE, args, QStringList("device_participants.participant_name"), ui->wdgUsers); - deviceslist_editor->setPermissions(isSiteAdmin(), m_comManager->isCurrentUserSuperAdmin()); - deviceslist_editor->setFilterText(tr("Seuls les appareils associés au site sont affichés.")); - ui->wdgDevices->layout()->addWidget(deviceslist_editor); - }*/ } if (current_tab == ui->tabUsersDetails){ // Users if (!ui->wdgUsers->layout()){ QHBoxLayout* layout = new QHBoxLayout(); - layout->setMargin(0); + layout->setContentsMargins(0,0,0,0); ui->wdgUsers->setLayout(layout); } if (ui->wdgUsers->layout()->count() == 0){ @@ -639,21 +706,13 @@ void SiteWidget::on_tabNav_currentChanged(int index) if (current_tab == ui->tabSessionTypes){ // Session types - /*if (!ui->wdgSessionTypes->layout()){ - QHBoxLayout* layout = new QHBoxLayout(); - layout->setMargin(0); - ui->wdgSessionTypes->setLayout(layout); - } - if (ui->wdgSessionTypes->layout()->count() == 0){ - DataListWidget* stlist_editor = new DataListWidget(m_comManager, TERADATA_SESSIONTYPE, WEB_SESSIONTYPESITE_PATH, args, QStringList(), ui->wdgSessionTypes); - // m_limited = true = user only, not project admin - stlist_editor->setPermissions(!m_limited, !m_limited); - stlist_editor->setFilterText(tr("Seuls les types de séances associés au site sont affichés.")); - ui->wdgSessionTypes->layout()->addWidget(stlist_editor); - }*/ querySessionTypeSiteAccess(); } + if (current_tab == ui->tabTestTypes){ + queryTestTypeSiteAccess(); + } + } @@ -690,8 +749,10 @@ void SiteWidget::on_btnUpdateServices_clicked() item_obj.insert("id_service", service_id); services_sites.append(item_obj); }else{ - if (std::find(m_listServicesSites_items.cbegin(), m_listServicesSites_items.cend(), item) != m_listServicesSites_items.cend()){ - removed_services = true; + if (!removed_services){ + if (m_listServicesSites_items.key(item, 0) > 0){ + removed_services = true; + } } } } @@ -783,8 +844,10 @@ void SiteWidget::on_btnUpdateDevices_clicked() item_obj.insert("id_device", device_id); devices_sites.append(item_obj); }else{ - if (std::find(m_listDevicesSites_items.cbegin(), m_listDevicesSites_items.cend(), item) != m_listDevicesSites_items.cend()){ - removed_devices = true; + if (!removed_devices){ + if (m_listDevicesSites_items.key(item, 0) > 0){ + removed_devices = true; + } } } } @@ -806,7 +869,7 @@ void SiteWidget::on_btnUpdateDevices_clicked() void SiteWidget::on_txtSearchDevices_textChanged(const QString &search_text) { - for(QListWidgetItem* item: qAsConst(m_listDevices_items)){ + for(QListWidgetItem* item: std::as_const(m_listDevices_items)){ item->setHidden(!item->text().contains(search_text, Qt::CaseInsensitive)); } } @@ -843,7 +906,7 @@ void SiteWidget::on_btnEditDevices_clicked() void SiteWidget::on_lstSessionTypes_itemChanged(QListWidgetItem *item) { - if (!isSiteAdmin()) + if (!m_comManager->isCurrentUserSuperAdmin()) return; // Check for changed items @@ -886,8 +949,10 @@ void SiteWidget::on_btnUpdateSessionTypes_clicked() item_obj.insert("id_session_type", st_id); st_sites.append(item_obj); }else{ - if (std::find(m_listSessionTypes_items.cbegin(), m_listSessionTypes_items.cend(), item) != m_listSessionTypes_items.cend()){ - removed_sts = true; + if (!removed_sts){ + if (m_listSessionTypes_items.key(item, 0) > 0){ + removed_sts = true; + } } } } @@ -928,3 +993,71 @@ void SiteWidget::on_btnEditSessionTypes_clicked() m_diag_editor->open(); } + +void SiteWidget::on_lstTestTypes_itemChanged(QListWidgetItem *item) +{ + if (!m_comManager->isCurrentUserSuperAdmin()) + return; + + // Check for changed items + bool has_changes = false; + if (m_listTestTypeSites_items.key(item) > 0 && item->checkState() == Qt::Unchecked){ + // Item deselected + has_changes = true; + }else{ + if (m_listTestTypeSites_items.key(item) <= 0 && item->checkState() == Qt::Checked){ + // Item selected + has_changes = true; + } + } + + if (item->checkState() == Qt::Checked){ + item->setForeground(Qt::green); + }else{ + item->setForeground(Qt::red); + } + + ui->btnUpdateTestTypes->setEnabled(has_changes); +} + + +void SiteWidget::on_btnUpdateTestTypes_clicked() +{ + QJsonDocument document; + QJsonObject base_obj; + QJsonObject site_obj; + QJsonArray tt_sites; + bool removed_tts = false; + + site_obj.insert("id_site", m_data->getId()); + for (int i=0; ilstTestTypes->count(); i++){ + QListWidgetItem* item = ui->lstTestTypes->item(i); + int tt_id = m_listTestTypes_items.key(item, 0); + if (item->checkState() == Qt::Checked){ + // New item selected + QJsonObject item_obj; + item_obj.insert("id_test_type", tt_id); + tt_sites.append(item_obj); + }else{ + if (!removed_tts){ + if (m_listTestTypeSites_items.key(item, 0) > 0){ + removed_tts = true; + } + } + } + } + + if (removed_tts){ + GlobalMessageBox msgbox; + int rval = msgbox.showYesNo(tr("Suppression de types de tests associés"), tr("Au moins un type de test a été retiré de ce site. S'il y a des projets qui utilisent ce type, ils ne pourront plus l'utiliser.\nSouhaitez-vous continuer?")); + if (rval != GlobalMessageBox::Yes){ + return; + } + } + + site_obj.insert("testtypes", tt_sites); + base_obj.insert("site", site_obj); + document.setObject(base_obj); + postDataRequest(WEB_TESTTYPESITE_PATH, document.toJson()); +} + diff --git a/client/src/editors/SiteWidget.h b/client/src/editors/SiteWidget.h index 5f6d08c6..795c1588 100644 --- a/client/src/editors/SiteWidget.h +++ b/client/src/editors/SiteWidget.h @@ -18,11 +18,10 @@ class SiteWidget : public DataEditorWidget Q_OBJECT public: - explicit SiteWidget(ComManager* comMan, const TeraData* data = nullptr, QWidget *parent = nullptr); + explicit SiteWidget(ComManager* comMan, const TeraData* data = nullptr, const bool configMode = false, QWidget *parent = nullptr); ~SiteWidget(); void saveData(bool signal=true); - void setData(const TeraData* data); private slots: @@ -31,6 +30,7 @@ private slots: void processServiceSiteAccessReply(QList service_sites, QUrlQuery reply_query); void processDeviceSiteAccessReply(QListdevice_sites, QUrlQuery reply_query); void processSessionTypeSiteAccessReply(QListst_sites, QUrlQuery reply_query); + void processTestTypeSiteAccessReply(QListtt_sites, QUrlQuery reply_query); void processStatsReply(TeraData stats, QUrlQuery reply_query); void processPostOKReply(QString path); @@ -55,11 +55,13 @@ private slots: void on_btnEditDevices_clicked(); void on_lstSessionTypes_itemChanged(QListWidgetItem *item); - void on_btnUpdateSessionTypes_clicked(); - void on_btnEditSessionTypes_clicked(); + void on_lstTestTypes_itemChanged(QListWidgetItem *item); + + void on_btnUpdateTestTypes_clicked(); + private: Ui::SiteWidget *ui; @@ -74,9 +76,13 @@ private slots: QMap m_listSessionTypeSites_items; QMap m_listSessionTypes_items; + QMap m_listTestTypeSites_items; + QMap m_listTestTypes_items; + BaseDialog* m_diag_editor; int m_devicesCount; + bool m_configMode; void connectSignals(); @@ -84,6 +90,7 @@ private slots: void updateServiceSite(const TeraData* service_site); void updateDeviceSite(const TeraData* device_site); void updateSessionTypeSite(const TeraData* st_site); + void updateTestTypeSite(const TeraData* tt_site); void updateControlsState(); void updateFieldsValue(); @@ -95,6 +102,7 @@ private slots: void queryServiceSiteAccess(); void queryDeviceSiteAccess(); void querySessionTypeSiteAccess(); + void queryTestTypeSiteAccess(); }; #endif // SITEWIDGET_H diff --git a/client/src/editors/SiteWidget.ui b/client/src/editors/SiteWidget.ui index f03ea2ec..eb6a3340 100644 --- a/client/src/editors/SiteWidget.ui +++ b/client/src/editors/SiteWidget.ui @@ -20,106 +20,14 @@ Form - /* -QPushButton#btnConnect{background-color:rgb(80,180,80)} -QWidget{background-color: rgba(0,0,0,0);color:white;border-radius:5px} -QWidget#mainWidget{background-color:rgba(128,128,128,25%);} -QWidget#frmProfile,QWidget#pageProfile{background-color:rgba(108,8,156,25%);} -QPlainTextEdit,QLineEdit,QListWidget,QComboBox,QTextEdit,QSpinBox{background-color: rgba(255,255,255,50%); color: black;} - -QTableWidget{ -background-color: rgba(0,0,0,50%); -color: white; -border: 1px solid rgba(255,255,255,50%); -} - -QLabel#lblLastOnline{color:lightblue;} -QLabel#lblError,QLabel#lblCamMissing,QLabel#lblAudioMissing{color:red; background-color:rgba(255,0,0,30%);} - -QLineEdit:!enabled, QListWidget:!enabled,QComboBox:!enabled,QTextEdit:!enabled,QPlainTextEdit:!enabled{background-color: rgba(0,0,0,30%);color:white;border:0} -QDateEdit::down-arrow:!enabled,QTimeEdit::down-arrow:!enabled,QDateEdit::up-arrow:!enabled,QTimeEdit::up-arrow:!enabled,QComboBox::drop-down:!enabled{background-color:rgba(0,0,0,0%);} - -QTextEdit#txtProfile{background-color: rgba(255,255,255,80%);color:black;border:0} -QTextEdit:!enabled#txtProfile{background-color: rgba(255,255,255,60%);color:black;border:0} - -QFrame#frmSelUser{background-color:rgba(100,100,200,50%);} -QFrame#frmAudio,QFrame#frmCam1,QFrame#frmCam2,QFrame#frmSensors,QFrame#frmWebRTC,QFrame#frmVirtualCam,QFrame#frmExternalPrograms{background-color:rgba(100,100,200,35%);} - -QCheckBox::indicator:unchecked:!enabled { background-color:rgba(128,0,0,100%);} -QCheckBox::indicator:checked:!enabled { background-color:green;} -QCheckBox::indicator:unchecked{ background-color:red;border-radius:3px;} -QCheckBox::indicator:checked {background-color:rgb(0,255,0);border-radius:3px;} -QCheckBox:checked{color:lightgreen;background-color:rgba(0,0,0,0%);} -QCheckBox:!checked{color:red;background-color:rgba(0,0,0,0%);} - -QPushButton{background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #2198c0, stop: 1 #0d5ca6);} -QPushButton:checked{background-color:rgb(128,128,128); border:1px solid rgba(255,255,255,50%);} -QPushButton:hover,QPushButton:checked{background-color: rgba(255,255,255,75%);color:black;} - -QToolButton:hover{background-color: rgba(255,255,255,50);} - -QTreeWidget::branch:closed:has-children:has-siblings { - border-image: none; - image: url(:/pictures/controls/branch_closed.png); - } - - QTreeView::branch:open:has-children:!has-siblings, - QTreeView::branch:open:has-children:has-siblings { - border-image: none; - image:url(:/pictures/controls/branch_opened.png); - } - -QWidget#lineOnline{background-color:blue;} -QWidget#lineOffline{background-color:red;} -QWidget#lineBoth{background-color:rgb(255,0,255);} - - -QTabWidget::pane { - background-color: rgba(128,128,140,25%); - border-radius: 5px; - border: 2px solid rgba(128,128,140,50%); - } - - QTabWidget::tab-bar { - left: 5px; - } - - - QTabBar::tab,QTableWidget::tab,QHeaderView::section { - background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 rgba(128,128,140,50%), stop: 1.0 rgba(128,128,140,25%)); - border: 2px solid rgba(128,128,140,50%); - border-bottom: 0px; - border-top-left-radius: 4px; - border-top-right-radius: 4px; - padding: 2px; -min-height:25px; - } - - QTabBar::tab:selected, QTabBar::tab:hover { - -background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 rgba(128,128,140,70%), stop: 1.0 rgba(128,128,140,50%)); - } - - QTabBar::tab:selected { - border-color:rgba(128,128,140,60%); - border-bottom-color:rgba(128,128,140,60%); - } - - QTabBar::tab:!selected { - margin-top: 2px; - } -*/ - - + 10 - 1 + 9 @@ -171,7 +79,7 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - 0 + 3 @@ -314,7 +222,7 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - 10 + 15 @@ -776,6 +684,9 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, 3 + + true + 200 @@ -785,6 +696,9 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, true + + false + Groupe utilisateur @@ -845,6 +759,19 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + + + + + 0 + 32 + + + + Pour modifier les types de séances associés à ce site, veuillez contacter votre administrateur + + + @@ -888,12 +815,27 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + + Qt::NoFocus + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::NoSelection + + + QAbstractItemView::SelectRows + 24 24 + + false + @@ -924,6 +866,85 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + + + + :/icons/test_type.png:/icons/test_type.png + + + Types de tests + + + + + + + + + 0 + 32 + + + + Pour modifier les types de tests associés à ce site, veuillez contacter votre administrateur + + + + + + + + + Qt::NoFocus + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::NoSelection + + + QAbstractItemView::SelectRows + + + + 24 + 24 + + + + false + + + + + + + + 0 + 32 + + + + PointingHandCursor + + + Mettre à jour les types de tests associés + + + + :/icons/save.png:/icons/save.png + + + + 24 + 24 + + + + + + @@ -1008,6 +1029,9 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + + Qt::NoFocus + QAbstractItemView::NoEditTriggers @@ -1070,6 +1094,15 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + + Qt::NoFocus + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::NoSelection + 24 @@ -1111,12 +1144,6 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - - TeraForm - QWidget -
TeraForm.h
- 1 -
ClickableLabel QLabel @@ -1126,6 +1153,12 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, clicked() + + TeraForm + QWidget +
TeraForm.h
+ 1 +
diff --git a/client/src/editors/TeraForm.cpp b/client/src/editors/TeraForm.cpp index 8d299745..aa0958d0 100644 --- a/client/src/editors/TeraForm.cpp +++ b/client/src/editors/TeraForm.cpp @@ -2,7 +2,9 @@ #include "ui_TeraForm.h" #include "Logger.h" -#include "DataEditorWidget.h" +#include "GlobalMessageBox.h" +#include "libs/AudioVideoUtils.h" + #include #include @@ -13,17 +15,9 @@ TeraForm::TeraForm(QWidget *parent, ComManager *com_man) : ui->setupUi(this); m_mainWidget = ui->toolboxMain; - m_highlightConditionals = true; - - // TODO: Find out why the global stylesheet isn't correctly used by TeraForm - /*QFile file(":/stylesheet.qss"); - file.open(QFile::ReadOnly); - QString stylesheet = QLatin1String(file.readAll()); - setStyleSheet(stylesheet);*/ + m_highlightConditionals = false; // Automatically sets comManager - /*DataEditorWidget* parent_editor = dynamic_cast(parent); - if (parent_editor){*/ setComManager(com_man); //} } @@ -48,8 +42,10 @@ void TeraForm::buildUiFromStructure(const QString &structure) while (ui->toolboxMain->count() > 0){ ui->toolboxMain->widget(0)->deleteLater(); - ui->toolboxMain->removeItem(0); + //ui->toolboxMain->removeItem(0); + ui->toolboxMain->removeTab(0); } + ui->toolboxMain->show(); QJsonObject struct_object = struct_info.object(); @@ -63,18 +59,19 @@ void TeraForm::buildUiFromStructure(const QString &structure) if (struct_data.count() > 1){ int page_index = 0; m_mainWidget = ui->toolboxMain; - for (const QVariant §ion:qAsConst(struct_data)){ - if (section.canConvert(QMetaType::QVariantHash)){ + for (const QVariant §ion:std::as_const(struct_data)){ + if (section.canConvert()){ QVariantHash section_data = section.toHash(); //if (page_index>0){ QWidget* new_page = new QWidget(ui->toolboxMain); new_page->setObjectName("pageSection" + QString::number(page_index+1)); new_page->setStyleSheet("QWidget#" + new_page->objectName() + "{border: 1px solid white; border-radius: 5px;}"); - ui->toolboxMain->addItem(new_page,""); + //ui->toolboxMain->addItem(new_page,""); + ui->toolboxMain->addTab(new_page, section_data["label"].toString()); //} - ui->toolboxMain->setItemText(page_index, section_data["label"].toString()); + //ui->toolboxMain->setItemText(page_index, section_data["label"].toString()); if (section_data.contains("items")){ - if (section_data["items"].canConvert(QMetaType::QVariantList)){ + if (section_data["items"].canConvert()){ buildFormFromStructure(ui->toolboxMain->widget(page_index), section_data["items"].toList()); } } @@ -85,11 +82,12 @@ void TeraForm::buildUiFromStructure(const QString &structure) if (struct_data.count() == 1){ // Only one section - hides the header and don't use QToolBox ui->mainLayout->removeWidget(ui->toolboxMain); + ui->toolboxMain->hide(); m_mainWidget = new QFrame(this); ui->mainLayout->addWidget(m_mainWidget); QVariantHash section_data = struct_data.first().toHash(); if (section_data.contains("items")){ - if (section_data["items"].canConvert(QMetaType::QVariantList)){ + if (section_data["items"].canConvert()){ buildFormFromStructure(m_mainWidget, section_data["items"].toList()); } } @@ -116,7 +114,7 @@ void TeraForm::fillFormFromData(const QJsonObject &data) resetFormValues(); // Set initial values for missing fields - for (QWidget* widget:qAsConst(m_widgets)){ + for (QWidget* widget:std::as_const(m_widgets)){ QString field = m_widgets.key(widget); if (!m_initialValues.contains(field)){ QVariant value; @@ -150,10 +148,17 @@ void TeraForm::fillFormFromData(const QString &structure) } +void TeraForm::setSectionsPosition(const QTabWidget::TabPosition &position) +{ + if (dynamic_cast(ui->toolboxMain)){ + ui->toolboxMain->setTabPosition(position); + } +} + bool TeraForm::validateFormData(bool include_hidden) { bool rval = true; - for (QWidget* item:qAsConst(m_widgets)){ + for (QWidget* item:std::as_const(m_widgets)){ rval &= validateWidget(item, include_hidden); } return rval; @@ -162,7 +167,7 @@ bool TeraForm::validateFormData(bool include_hidden) QStringList TeraForm::getInvalidFormDataLabels(bool include_hidden) { QStringList rval; - for (QWidget* item:qAsConst(m_widgets)){ + for (QWidget* item:std::as_const(m_widgets)){ if (!validateWidget(item, include_hidden)){ rval.append(item->property("label").toString()); } @@ -226,7 +231,7 @@ bool TeraForm::getFieldDirty(QWidget *widget) } QVariant value, id; getWidgetValues(widget, &id, &value); - if (!id.isNull()) + if (!id.isNull() && id.isValid()) value = id; if (dynamic_cast(widget)){ @@ -255,7 +260,7 @@ void TeraForm::hideField(const QString &field) // Disable condition if (widget->property("condition").isValid() && !hasHookCondition(widget)){ widget->setProperty("_condition", widget->property("condition")); - widget->setProperty("condition", QVariant::Invalid); + widget->setProperty("condition", QVariant()); } } } @@ -268,7 +273,7 @@ void TeraForm::showField(const QString &field) // Enable condition if (widget->property("_condition").isValid()){ widget->setProperty("condition", widget->property("_condition")); - widget->setProperty("_condition", QVariant::Invalid); + widget->setProperty("_condition", QVariant()); } checkConditions(widget); } @@ -313,7 +318,7 @@ void TeraForm::setFieldsEnabled(const QStringList &fields, const bool &enabled) bool TeraForm::isDirty() { bool dirty = false; - for(QWidget* wdg: qAsConst(m_widgets)){ + for(QWidget* wdg: std::as_const(m_widgets)){ if (getFieldDirty(wdg)){ dirty = true; break; @@ -336,11 +341,11 @@ QJsonDocument TeraForm::getFormDataJson(bool include_unmodified_data) QJsonObject data_obj; QJsonObject base_obj; - for(QWidget* wdg:qAsConst(m_widgets)){ + for(QWidget* wdg:std::as_const(m_widgets)){ QString field = m_widgets.key(wdg); QVariant value, id; getWidgetValues(wdg, &id, &value); - if (!id.isNull()) + if (!id.isNull() && id.isValid()) value = id; // Include only modified fields or ids if ((!include_unmodified_data && getFieldDirty(field)) @@ -365,11 +370,11 @@ QJsonDocument TeraForm::getFormDataJson(bool include_unmodified_data) TeraData* TeraForm::getFormDataObject(const TeraDataTypes data_type) { TeraData* rval = new TeraData(data_type); - for(QWidget* wdg:qAsConst(m_widgets)){ + for(QWidget* wdg:std::as_const(m_widgets)){ QString field = m_widgets.key(wdg); QVariant value, id; if (getWidgetValues(wdg, &id, &value)){ - if (!id.isNull()) + if (!id.isNull() && id.isValid()) value = id; rval->setFieldValue(field, value); } @@ -435,12 +440,12 @@ bool TeraForm::formHasStructure() void TeraForm::resetFormValues() { QStringList keys = m_initialValues.keys(); - for (const QString& field:qAsConst(keys)){ + for (const QString& field:std::as_const(keys)){ if (m_widgets.contains(field)){ setWidgetValue(m_widgets[field], m_initialValues[field]); checkConditions(m_widgets[field]); } else{ - LOG_WARNING("No widget for field: " + field, "TeraForm::resetFormValues"); + //LOG_WARNING("No widget for field: " + field, "TeraForm::resetFormValues"); } } @@ -458,27 +463,24 @@ void TeraForm::buildFormFromStructure(QWidget *page, const QVariantList &structu layout = new QFormLayout(page); //layout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); layout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow); + //layout->setFieldGrowthPolicy(QFormLayout::FieldsStayAtSizeHint); + layout->setFormAlignment(Qt::AlignLeft | Qt::AlignTop); + layout->setAlignment(Qt::AlignLeft | Qt::AlignTop); layout->setVerticalSpacing(3); + }else{ layout = static_cast(page->layout()); } for (const QVariant &item:structure){ - if (item.canConvert(QMetaType::QVariantHash)){ + if (item.canConvert()){ QVariantHash item_data = item.toHash(); QString item_id = item_data["id"].toString(); QWidget* item_widget = nullptr; QLabel* item_label = new QLabel(item_data["label"].toString()); item_label->setAlignment(Qt::AlignVCenter); - item_label->setStyleSheet("QLabel{min-height: 25px;}"); - - /*QFrame* item_frame = new QFrame(); - QHBoxLayout* item_frame_layout = new QHBoxLayout(); - item_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding); - item_frame->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding); - item_frame_layout->addWidget(item_label); - item_frame->setLayout(item_frame_layout);*/ - + item_label->setStyleSheet("QLabel{color: #b6ddf8;}"); + //item_label->setStyleSheet("QLabel{min-height: 25px;}"); // Build widget according to item type QString item_type = item_data["type"].toString().toLower(); @@ -515,6 +517,9 @@ void TeraForm::buildFormFromStructure(QWidget *page, const QVariantList &structu else if (item_type == "label"){ item_widget = createLabelWidget(item_data); } + else if (item_type == "longlabel"){ + item_widget = createLongLabelWidget(item_data); + } else if (item_type == "color"){ item_widget = createColorWidget(item_data); } @@ -580,6 +585,7 @@ void TeraForm::buildFormFromStructure(QWidget *page, const QVariantList &structu // Set layout alignement layout->setAlignment(Qt::AlignLeft | Qt::AlignTop); + layout->update(); page->setLayout(layout); // Set default values @@ -588,12 +594,13 @@ void TeraForm::buildFormFromStructure(QWidget *page, const QVariantList &structu validateFormData(true); page->setDisabled(m_disabled); + updateRequiredWidgetsLabel(); } void TeraForm::setDefaultValues() { - for (QWidget* item:qAsConst(m_widgets)){ + for (QWidget* item:std::as_const(m_widgets)){ if (QComboBox* combo = dynamic_cast(item)){ combo->setCurrentIndex(0); } @@ -616,7 +623,7 @@ QWidget *TeraForm::createVideoInputsWidget(const QVariantHash &structure) //item_combo->addItem(camera.description(), camera.deviceName()); item_combo->addItem(camera.description(), camera.description()); }*/ - for (const QString &camera:qAsConst(m_videoInputs)){ + for (const QString &camera:std::as_const(m_videoInputs)){ item_combo->addItem(camera, camera); } @@ -641,7 +648,7 @@ QWidget *TeraForm::createAudioInputsWidget(const QVariantHash &structure) /*for (QAudioDeviceInfo input:m_audioInputs){ item_combo->addItem(input.deviceName(), input.deviceName()); }*/ - for (const QString &input:qAsConst(m_audioInputs)){ + for (const QString &input:std::as_const(m_audioInputs)){ item_combo->addItem(input, input); } @@ -660,10 +667,10 @@ QWidget *TeraForm::createArrayWidget(const QVariantHash &structure) item_combo->addItem("", ""); if (structure.contains("values")){ - if (structure["values"].canConvert(QMetaType::QVariantList)){ + if (structure["values"].canConvert()){ QVariantList list = structure["values"].toList(); - for (const QVariant &value:qAsConst(list)){ - if (value.canConvert(QMetaType::QVariantHash)){ + for (const QVariant &value:std::as_const(list)){ + if (value.canConvert()){ QVariantHash item_data = value.toHash(); item_combo->addItem(item_data["value"].toString(), item_data["id"].toString()); } @@ -745,12 +752,27 @@ QWidget *TeraForm::createLabelWidget(const QVariantHash &structure) QLabel* item_label = new QLabel(); item_label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); + //item_label->setWordWrap(true); //item_label->setHidden(is_hidden); return item_label; } +QWidget *TeraForm::createLongLabelWidget(const QVariantHash &structure) +{ + QPushButton* item_button = new QPushButton(); + item_button->setText(tr("Afficher")); + item_button->setIcon(QIcon(":/icons/view_on.png")); + item_button->setCursor(Qt::PointingHandCursor); + + // Connect signal + connect(item_button, &QPushButton::clicked, this, &TeraForm::longLabelButtonClicked); + + return item_button; + +} + QWidget *TeraForm::createListWidget(const QVariantHash &structure) { QListWidget* item_list = new QListWidget(); @@ -767,6 +789,10 @@ QWidget *TeraForm::createLongTextWidget(const QVariantHash &structure) Q_UNUSED(structure) QTextEdit* item_text = new QTextEdit(); + /*QFontMetrics metrics(item_text->font()); + int row_height = metrics.lineSpacing(); + item_text->setFixedHeight(4.5 * row_height);*/ + //item_text->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); connect(item_text, &QTextEdit::textChanged, this, &TeraForm::widgetValueChanged); @@ -823,17 +849,17 @@ QWidget *TeraForm::createDurationWidget(const QVariantHash &structure) void TeraForm::loadAudioInputs() { - m_audioInputs = Utils::getAudioDeviceNames();// QAudioDeviceInfo::availableDevices(QAudio::AudioInput); + m_audioInputs = AudioVideoUtils::getAudioDeviceNames();// QAudioDeviceInfo::availableDevices(QAudio::AudioInput); } void TeraForm::loadVideoInputs() { - m_videoInputs = Utils::getVideoDeviceNames(); //QCameraInfo::availableCameras(); + m_videoInputs = AudioVideoUtils::getVideoDeviceNames(); //QCameraInfo::availableCameras(); } void TeraForm::checkConditions(QWidget *item_triggering) { - for (QWidget* item:qAsConst(m_widgets)){ + for (QWidget* item:std::as_const(m_widgets)){ if (!item) continue; if (item == item_triggering) @@ -846,7 +872,7 @@ void TeraForm::checkConditionsForItem(QWidget *item, QWidget *item_triggering) { if (item->property("condition").isValid()){ // Item has a condition - if (item->property("condition").canConvert(QMetaType::QVariantHash)){ + if (item->property("condition").canConvert()){ QVariantHash condition = item->property("condition").toHash(); QString check_id = condition["item"].toString(); if (!check_id.isNull()){ @@ -885,8 +911,9 @@ void TeraForm::checkConditionsForItem(QWidget *item, QWidget *item_triggering) } } if (op.toUpper() == "NOT NULL"){ - if (!sender_index.isNull() || !sender_value.isNull()) + if ((!sender_index.isValid() && !sender_value.isValid()) || (!sender_value.isValid() || (sender_value.typeId() == QMetaType::QString && !sender_value.toString().isEmpty()))){ condition_met = true; + } } if (op.toUpper() == "CONTAINS"){ if (sender_index == value || sender_value.toString().contains(value.toString())){ @@ -898,7 +925,7 @@ void TeraForm::checkConditionsForItem(QWidget *item, QWidget *item_triggering) setWidgetVisibility(item, check_item, condition_met); // We have a hook requesting data for that specific widget... - if (!hook.isNull()){ + if (!hook.isNull() || hook.isValid()){ if (item_triggering == check_item){ if (sender_index.toString() != ""){ if (m_comManager){ @@ -931,7 +958,7 @@ bool TeraForm::hasHookCondition(QWidget *item) // Check if any item a hook condition on it if (item->property("condition").isValid()){ // Item has a condition - if (item->property("condition").canConvert(QMetaType::QVariantHash)){ + if (item->property("condition").canConvert()){ QVariantHash condition = item->property("condition").toHash(); if (condition.contains("hook")) return true; @@ -1036,6 +1063,9 @@ bool TeraForm::getWidgetValues(QWidget* widget, QVariant *id, QVariant *value) if (btn->property("color").isValid()){ *value = btn->property("color").toString(); } + if (btn->property("display_text").isValid()){ + *value = btn->property("display_text").toString(); + } } if (QTimeEdit* te = dynamic_cast(widget)){ @@ -1057,7 +1087,7 @@ bool TeraForm::getWidgetValues(QWidget* widget, QVariant *id, QVariant *value) } - if (value->canConvert(QMetaType::QString)){ + if (value->canConvert()){ bool ok; QString string_val = value->toString(); string_val.toInt(&ok); @@ -1087,7 +1117,7 @@ QVariant TeraForm::getWidgetValue(QWidget *widget) getWidgetValues(widget, &id, &value); - if (!id.isNull()) + if (!id.isNull() || id.isValid()) value = id; return value; @@ -1108,7 +1138,7 @@ void TeraForm::setWidgetValue(QWidget *widget, const QVariant &value) combo->setCurrentIndex(index); }else{ // Check if we have a number, if so, suppose it is the index - if (value.canConvert(QMetaType::Int)){ + if (value.canConvert()){ index = value.toInt(); if (index>=0 && index+1count()){ combo->setCurrentIndex(index+1); @@ -1127,7 +1157,7 @@ void TeraForm::setWidgetValue(QWidget *widget, const QVariant &value) } if (QTextEdit* text = dynamic_cast(widget)){ - if (value.canConvert(QMetaType::QVariantMap) || value.canConvert(QMetaType::QVariantHash)){ + if (value.canConvert() || value.canConvert()){ QVariantHash data = value.toHash(); QJsonDocument doc; doc.setObject(QJsonObject::fromVariantHash(data)); @@ -1158,6 +1188,9 @@ void TeraForm::setWidgetValue(QWidget *widget, const QVariant &value) btn->setProperty("color", value.toString()); btn->setStyleSheet(QString("background-color: " + value.toString() + ";min-width: 32px;")); return; + }else{ + btn->setProperty("display_text", value.toString()); + return; } } @@ -1174,7 +1207,7 @@ void TeraForm::setWidgetValue(QWidget *widget, const QVariant &value) } if (QDateTimeEdit* dt = dynamic_cast(widget)){ - if (value.canConvert(QVariant::String)){ + if (value.canConvert()){ if (value.toString().isEmpty()){ widget->hide(); return; @@ -1182,7 +1215,6 @@ void TeraForm::setWidgetValue(QWidget *widget, const QVariant &value) } QDateTime time_value = value.toDateTime().toLocalTime(); - if (!time_value.isValid()){ unsigned int time_s = value.toUInt(); // Consider we have a UNIX timestamp @@ -1209,12 +1241,13 @@ void TeraForm::setWidgetRequired(QWidget *item_widget, QLabel *item_label, const return; item_widget->setProperty("required", required); - if (required){ + item_label->setProperty("required", required); + item_label->setProperty("label", item_label->text()); + /*if (required){ item_label->setText("* " + item_label->text()); }else{ - item_label->setText(" " + item_label->text()); - } + }*/ } void TeraForm::updateWidgetChoices(QWidget *widget, const QList values) @@ -1258,7 +1291,7 @@ bool TeraForm::validateWidget(QWidget *widget, bool include_hidden) QVariant id, value; getWidgetValues(widget, &id, &value); //qDebug() << widget->metaObject()->className() << " - " << m_widgets.key(widget) << " - " << value; - if (value.isNull() || value.toInt()==-1 || value.toString().isEmpty()){ + if (value.isNull() || !value.isValid() || value.toInt()==-1 || value.toString().isEmpty()){ rval = false; } } @@ -1283,6 +1316,25 @@ qreal TeraForm::doLinearInterpolation(const qreal &p1, const qreal &p2, const qr return (p1 + (p2-p1)*value); } +void TeraForm::updateRequiredWidgetsLabel() +{ + for (QLabel* label: m_widgetsLabels){ + if (m_disabled){ + if (label->property("label").isValid()){ + label->setText(label->property("label").toString()); + } + }else{ + for (QLabel* label: m_widgetsLabels){ + if (label->property("required").toBool()){ + label->setText("* " + label->property("label").toString()); + }else{ + label->setText(" " + label->property("label").toString()); + } + } + } + } +} + bool TeraForm::eventFilter(QObject *object, QEvent *event) { if(event->type() == QEvent::FocusIn) { @@ -1344,6 +1396,21 @@ void TeraForm::colorWidgetClicked() } } +void TeraForm::longLabelButtonClicked() +{ + QObject* sender = QObject::sender(); + if (!sender) + return; + + QPushButton* sender_widget = dynamic_cast(sender); + QString text = sender_widget->property("display_text").toString(); + + if (!text.isEmpty()){ + GlobalMessageBox msgbox(this); + msgbox.showInfo(m_widgetsLabels.value(sender_widget)->text(), text); + } +} + void TeraForm::hookReplyReceived(TeraDataTypes data_type, QList datas) { if (m_widgetsHookRequests.isEmpty()) @@ -1376,8 +1443,12 @@ void TeraForm::setDisabled(bool disable) m_mainWidget->setDisabled(disable); } m_disabled = disable; - //QWidget::setDisabled(disable); + // Hide "required" labels indicators + updateRequiredWidgetsLabel(); + + //QLabel* widget_label = m_widgetsLabels[widget]; + //QWidget::setDisabled(disable); } @@ -1394,4 +1465,6 @@ void TeraForm::setEnabled(bool enable) } m_disabled = !enable; //QWidget::setEnabled(enable); + // Show "required" labels indicators + updateRequiredWidgetsLabel(); } diff --git a/client/src/editors/TeraForm.h b/client/src/editors/TeraForm.h index c5b6eb85..10c217a0 100644 --- a/client/src/editors/TeraForm.h +++ b/client/src/editors/TeraForm.h @@ -25,13 +25,11 @@ #include #include - -#include -#include +#include +#include #include "TeraData.h" #include "managers/ComManager.h" -#include "Utils.h" namespace Ui { class TeraForm; @@ -45,9 +43,13 @@ class TeraForm : public QWidget explicit TeraForm(QWidget *parent = nullptr, ComManager* com_man = nullptr); ~TeraForm(); + void buildUiFromStructure(const QString& structure); void fillFormFromData(const QJsonObject& data); void fillFormFromData(const QString& structure); + + void setSectionsPosition(const QTabWidget::TabPosition &position); + bool formHasData(); bool formHasStructure(); void resetFormValues(); @@ -113,6 +115,7 @@ class TeraForm : public QWidget QWidget* createBooleanWidget(const QVariantHash& structure); QWidget* createNumericWidget(const QVariantHash& structure); QWidget* createLabelWidget(const QVariantHash& structure); + QWidget* createLongLabelWidget(const QVariantHash& structure); QWidget* createListWidget(const QVariantHash& structure); QWidget* createLongTextWidget(const QVariantHash& structure); QWidget* createColorWidget(const QVariantHash& structure); @@ -129,26 +132,25 @@ class TeraForm : public QWidget bool getWidgetValues(QWidget *widget, QVariant *id, QVariant* value); QVariant getWidgetValue(QWidget* widget); void setWidgetValue(QWidget* widget, const QVariant& value); - void setWidgetRequired(QWidget* item_widget, QLabel* item_label, const bool& required); - void updateWidgetChoices(QWidget* widget, const QList values); - bool validateWidget(QWidget* widget, bool include_hidden=false); static qreal doLinearInterpolation(const qreal &p1, const qreal &p2, const qreal &value); + void updateRequiredWidgetsLabel(); + bool eventFilter(QObject* object, QEvent* event); private slots: void widgetValueChanged(); void colorWidgetClicked(); + void longLabelButtonClicked(); // Hooks void hookReplyReceived(TeraDataTypes data_type, QList datas); public slots: - void setDisabled(bool disable); void setEnabled(bool enable); diff --git a/client/src/editors/TeraForm.ui b/client/src/editors/TeraForm.ui index 4452a21e..ec390bb9 100644 --- a/client/src/editors/TeraForm.ui +++ b/client/src/editors/TeraForm.ui @@ -30,30 +30,16 @@ 0 - + - - QFrame::Box + + QTabWidget::West - - QFrame::Raised - - - 0 - - - - - 0 - 0 - 396 - 266 - - - - Ce formulaire ne contient aucune information. + + + diff --git a/client/src/editors/TestTypeWidget.cpp b/client/src/editors/TestTypeWidget.cpp index e271393d..5cc4716b 100644 --- a/client/src/editors/TestTypeWidget.cpp +++ b/client/src/editors/TestTypeWidget.cpp @@ -46,7 +46,7 @@ void TestTypeWidget::saveData(bool signal){ QJsonArray sites; // Sites - for(QTreeWidgetItem* item:qAsConst(m_treeSites_items)){ + for(QTreeWidgetItem* item:std::as_const(m_treeSites_items)){ if (item->checkState(0) == Qt::Checked){ int site_id = m_treeSites_items.key(item); QJsonObject data_obj; @@ -62,7 +62,7 @@ void TestTypeWidget::saveData(bool signal){ } // Projects - for(QTreeWidgetItem* item:qAsConst(m_treeProjects_items)){ + for(QTreeWidgetItem* item:std::as_const(m_treeProjects_items)){ if (item->checkState(0) == Qt::Checked){ int project_id = m_treeProjects_items.key(item); QJsonObject data_obj; @@ -234,7 +234,7 @@ void TestTypeWidget::postTestTypeSites() QJsonObject base_obj; QJsonArray sites; - for(QTreeWidgetItem* item: qAsConst(m_treeSites_items)){ + for(QTreeWidgetItem* item: std::as_const(m_treeSites_items)){ int site_id = m_treeSites_items.key(item); if (item->checkState(0) == Qt::Checked){ QJsonObject data_obj; @@ -257,7 +257,7 @@ void TestTypeWidget::postTestTypeProjects() QJsonObject base_obj; QJsonArray projects; - for(QTreeWidgetItem* item: qAsConst(m_treeProjects_items)){ + for(QTreeWidgetItem* item: std::as_const(m_treeProjects_items)){ int proj_id = m_treeProjects_items.key(item); if (item->checkState(0) == Qt::Checked){ QJsonObject data_obj; @@ -289,7 +289,7 @@ bool TestTypeWidget::validateProjects() { if (!m_comManager->isCurrentUserSuperAdmin()){ bool at_least_one_selected = false; - for(QTreeWidgetItem* site_item:qAsConst(m_treeSites_items)){ + for(QTreeWidgetItem* site_item:std::as_const(m_treeSites_items)){ if (site_item->checkState(0) == Qt::Checked){ at_least_one_selected = true; break; diff --git a/client/src/editors/TestTypeWidget.ui b/client/src/editors/TestTypeWidget.ui index 71766835..a80c0061 100644 --- a/client/src/editors/TestTypeWidget.ui +++ b/client/src/editors/TestTypeWidget.ui @@ -331,6 +331,9 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, 0 + + Qt::NoFocus + QAbstractItemView::NoEditTriggers diff --git a/client/src/editors/UserGroupWidget.cpp b/client/src/editors/UserGroupWidget.cpp index bc80b3e5..87d7c9e8 100644 --- a/client/src/editors/UserGroupWidget.cpp +++ b/client/src/editors/UserGroupWidget.cpp @@ -1,14 +1,12 @@ #include "UserGroupWidget.h" #include "ui_UserGroupWidget.h" -#include "editors/DataListWidget.h" - UserGroupWidget::UserGroupWidget(ComManager *comMan, const TeraData *data, QWidget *parent) : DataEditorWidget(comMan, data, parent), ui(new Ui::UserGroupWidget) { - ui->setupUi(this); + ui->setupUi(this); setAttribute(Qt::WA_StyledBackground); //Required to set a background image // Use base class to manage editing @@ -45,7 +43,7 @@ void UserGroupWidget::saveData(bool signal) // Sites QJsonArray sites; - for(QTableWidgetItem* site: qAsConst(m_tableSites_items)){ + for(QTableWidgetItem* site: std::as_const(m_tableSites_items)){ //for (int i=0; irow(); @@ -62,7 +60,7 @@ void UserGroupWidget::saveData(bool signal) // Projects QJsonArray projects; //for (int i=0; irow(); QComboBox* combo_roles = dynamic_cast(ui->tableProjects->cellWidget(row,2)); @@ -106,8 +104,10 @@ void UserGroupWidget::connectSignals() connect(m_comManager, &ComManager::formReceived, this, &UserGroupWidget::processFormsReply); connect(m_comManager, &ComManager::siteAccessReceived, this, &UserGroupWidget::processSiteAccessReply); connect(m_comManager, &ComManager::projectAccessReceived, this, &UserGroupWidget::processProjectAccessReply); + connect(m_comManager, &ComManager::servicesAccessReceived, this, &UserGroupWidget::processServiceAccessReply); connect(m_comManager, &ComManager::sitesReceived, this, &UserGroupWidget::processSitesReply); connect(m_comManager, &ComManager::projectsReceived, this, &UserGroupWidget::processProjectsReply); + connect(m_comManager, &ComManager::servicesRolesReceived, this, &UserGroupWidget::processServiceRolesReply); connect(m_comManager, &ComManager::userUserGroupsReceived, this, &UserGroupWidget::processUserUserGroupsReply); connect(m_comManager, &ComManager::postResultsOK, this, &UserGroupWidget::processPostOKReply); connect(m_comManager, &ComManager::deleteResultsOK, this, &UserGroupWidget::processDeleteReply); @@ -188,11 +188,13 @@ void UserGroupWidget::updateProjectAccess(const TeraData *access) item = new QTableWidgetItem(QIcon(access->getIconFilenameForDataType(TERADATA_SITE)), access->getFieldValue("site_name").toString()); - ui->tableProjects->setItem(current_row, 0, item); + ui->tableProjects->setItem(current_row, 1, item); item = new QTableWidgetItem(QIcon(access->getIconFilenameForDataType(TERADATA_PROJECT)), access->getFieldValue("project_name").toString()); - ui->tableProjects->setItem(current_row, 1, item); + ui->tableProjects->setItem(current_row, 0, item); combo_roles = buildRolesComboBox(); + connect(combo_roles, static_cast(&QComboBox::currentIndexChanged), + this, &UserGroupWidget::comboProjectRole_changed); ui->tableProjects->setCellWidget(current_row, 2, combo_roles); m_tableProjects_items.insert(id_project, item); m_tableProjectSite_items.insert(id_site, item); @@ -223,6 +225,47 @@ void UserGroupWidget::updateProjectAccess(const TeraData *access) } +void UserGroupWidget::updateServiceRole(const TeraData *access) +{ + QTableWidgetItem* item; + int id_service = access->getFieldValue("id_service").toInt(); + QString service_role; + if (access->hasFieldName("service_role_name")) + service_role = access->getFieldValue("service_role_name").toString(); + QComboBox* combo_roles; + if (m_tableServices_items.contains(id_service)){ + item = m_tableServices_items[id_service]; + combo_roles = dynamic_cast(ui->tableServices->cellWidget(item->row(), 1)); + }else{ + // Not there - must add the service and role + int current_row = ui->tableServices->rowCount(); + ui->tableServices->setRowCount(ui->tableServices->rowCount()+1); + item = new QTableWidgetItem(QIcon(access->getIconFilenameForDataType(TERADATA_SERVICE)), + access->getFieldValue("service_name").toString()); + + ui->tableServices->setItem(current_row, 0, item); + QMap current_role; + current_role.insert(access->getId(), access->getName()); + combo_roles = buildRolesComboBox(current_role); + connect(combo_roles, static_cast(&QComboBox::currentIndexChanged), + this, &UserGroupWidget::comboServiceRole_changed); + combo_roles->setProperty("id_service_access", 0); + ui->tableServices->setCellWidget(current_row, 1, combo_roles); + m_tableServices_items.insert(id_service, item); + } + + if (combo_roles){ + // Check if the role is already in the list + int index = combo_roles->findData(access->getId()); + if (index == -1){ + // Insert value in combo + combo_roles->addItem(getRoleName(access->getName()), access->getId()); + } + /*combo_roles->setProperty("original_index", index); + combo_roles->setDisabled(false);*/ + } +} + void UserGroupWidget::updateUserUserGroup(const TeraData *uug) { int id_user_user_group = uug->getId(); @@ -305,6 +348,12 @@ void UserGroupWidget::updateFieldsValue() ui->lblTitle->setText(m_data->getName()); } + if (m_tableServices_items.isEmpty()){ + QUrlQuery args; + args.addQueryItem(WEB_QUERY_GLOBALS, "1"); + queryDataRequest(WEB_SERVICEROLEINFO_PATH, args); + } + } @@ -337,6 +386,8 @@ void UserGroupWidget::processSiteAccessReply(QList access, QUrlQuery r updateSiteAccess(&access.at(i)); } + ui->btnUpdateSitesRoles->setEnabled(false); + } void UserGroupWidget::processProjectAccessReply(QList access, QUrlQuery reply_query) @@ -355,6 +406,8 @@ void UserGroupWidget::processProjectAccessReply(QList access, QUrlQuer updateProjectAccess(&access.at(i)); } + ui->btnUpdateProjectsRoles->setEnabled(false); + } void UserGroupWidget::processSitesReply(QList sites) @@ -371,6 +424,38 @@ void UserGroupWidget::processProjectsReply(QList projects) } } +void UserGroupWidget::processServiceRolesReply(QList roles, QUrlQuery reply_query) +{ + for (int i=0; i access, QUrlQuery reply_query) +{ + for (int i=0; ihasFieldName("id_service")){ + // Find service item + QTableWidgetItem* service_row = m_tableServices_items.value(current_access->getFieldValue("id_service").toInt(), nullptr); + if (service_row){ + // Get related combo box and check if id_service_role present + QComboBox* combo = dynamic_cast(ui->tableServices->cellWidget(service_row->row(), 1)); + if (combo){ + int role_index = combo->findData(current_access->getFieldValue("id_service_role").toInt()); + if (role_index != -1){ // Found + combo->setCurrentIndex(role_index); + } + combo->setProperty("original_index", role_index); + combo->setProperty("id_service_access", current_access->getId()); + } + } + } + } + + ui->btnUpdateServicesRoles->setEnabled(false); +} + void UserGroupWidget::processUserUserGroupsReply(QList users_user_groups) { for (int i=0; irow(); QComboBox* combo_roles = dynamic_cast(ui->tableSites->cellWidget(row,1)); @@ -391,12 +476,13 @@ void UserGroupWidget::processPostOKReply(QString path) combo_roles->setProperty("original_index", combo_roles->currentIndex()); } } + ui->btnUpdateSitesRoles->setEnabled(false); } if (path == TeraData::getPathForDataType(TERADATA_PROJECTACCESS)){ // Reset "dirty" flag on each combo box role //for (int i=0; irow(); QComboBox* combo_roles = dynamic_cast(ui->tableProjects->cellWidget(row,2)); @@ -404,6 +490,22 @@ void UserGroupWidget::processPostOKReply(QString path) combo_roles->setProperty("original_index", combo_roles->currentIndex()); } } + ui->btnUpdateProjectsRoles->setEnabled(false); + } + + if (path == TeraData::getPathForDataType(TERADATA_SERVICE_ACCESS)){ + for(QTableWidgetItem* service: std::as_const(m_tableServices_items)){ + int service_id = m_tableServices_items.key(service); + int row = m_tableServices_items[service_id]->row(); + QComboBox* combo_roles = dynamic_cast(ui->tableServices->cellWidget(row,1)); + if (combo_roles){ + combo_roles->setProperty("original_index", combo_roles->currentIndex()); + if (combo_roles->currentIndex() == 0){ + combo_roles->setProperty("id_service_access", 0); + } + } + } + ui->btnUpdateServicesRoles->setEnabled(false); } } @@ -418,13 +520,12 @@ void UserGroupWidget::processDeleteReply(QString path, int id) void UserGroupWidget::btnUpdateSiteAccess_clicked() { - QJsonDocument document; QJsonObject base_obj; QJsonArray roles; //for (int i=0; irow(); QComboBox* combo_roles = dynamic_cast(ui->tableSites->cellWidget(row,1)); @@ -449,13 +550,12 @@ void UserGroupWidget::btnUpdateSiteAccess_clicked() void UserGroupWidget::btnUpdateProjectAccess_clicked() { - QJsonDocument document; QJsonObject base_obj; QJsonArray roles; //for (int i=0; irow(); QComboBox* combo_roles = dynamic_cast(ui->tableProjects->cellWidget(row,2)); @@ -500,7 +600,7 @@ void UserGroupWidget::comboSiteRole_changed(int index) // Find all related project items QList project_items = m_tableProjectSite_items.values(id_site); bool is_site_admin = combo->currentData() == "admin"; - for(QTableWidgetItem* project_item: qAsConst(project_items)){ + for(QTableWidgetItem* project_item: std::as_const(project_items)){ // Get combo box QComboBox* combo_role = dynamic_cast(ui->tableProjects->cellWidget(project_item->row(), 2)); if (combo_role){ @@ -520,6 +620,55 @@ void UserGroupWidget::comboSiteRole_changed(int index) } } } + }else{ + // Enable save button if changes + bool has_changes = false; + for (QTableWidgetItem* item: m_tableSites_items){ + QComboBox* combo = dynamic_cast(ui->tableSites->cellWidget(item->row(), 1)); + if (combo){ + if (combo->currentIndex() != combo->property("original_index").toInt()){ + has_changes = true; + break; + } + } + } + ui->btnUpdateSitesRoles->setEnabled(has_changes); + } +} + +void UserGroupWidget::comboProjectRole_changed(int index) +{ + if (!dataIsNew()){ + // Enable save button if changes + bool has_changes = false; + for (QTableWidgetItem* item: m_tableProjects_items){ + QComboBox* combo = dynamic_cast(ui->tableProjects->cellWidget(item->row(), 2)); + if (combo){ + if (combo->currentIndex() != combo->property("original_index").toInt()){ + has_changes = true; + break; + } + } + } + ui->btnUpdateProjectsRoles->setEnabled(has_changes); + } +} + +void UserGroupWidget::comboServiceRole_changed(int index) +{ + if (!dataIsNew()){ + // Enable save button if changes + bool has_changes = false; + for (QTableWidgetItem* item: m_tableServices_items){ + QComboBox* combo = dynamic_cast(ui->tableServices->cellWidget(item->row(), 1)); + if (combo){ + if (combo->currentIndex() != combo->property("original_index").toInt()){ + has_changes = true; + break; + } + } + } + ui->btnUpdateServicesRoles->setEnabled(has_changes); } } @@ -538,6 +687,7 @@ void UserGroupWidget::on_tabNav_currentChanged(int index) } if (current_tab == ui->tabSites){ + // Sites args.addQueryItem(WEB_QUERY_WITH_EMPTY, "1"); queryDataRequest(WEB_SITEACCESS_PATH, args); } @@ -549,6 +699,11 @@ void UserGroupWidget::on_tabNav_currentChanged(int index) queryDataRequest(WEB_USERUSERGROUPINFO_PATH, args); } } + + if (current_tab == ui->tabServices){ + // Global service access + queryDataRequest(WEB_SERVICEACCESSINFO_PATH, args); + } } void UserGroupWidget::on_btnUpdateUsers_clicked() @@ -590,3 +745,35 @@ void UserGroupWidget::on_btnUpdateUsers_clicked() } } +void UserGroupWidget::on_btnUpdateServicesRoles_clicked() +{ + QJsonDocument document; + QJsonObject base_obj; + QJsonArray roles; + + for(QTableWidgetItem* service: std::as_const(m_tableServices_items)){ + int service_id = m_tableServices_items.key(service); + int row = m_tableServices_items[service_id]->row(); + QComboBox* combo_roles = dynamic_cast(ui->tableServices->cellWidget(row,1)); + if (combo_roles->property("original_index").toInt() != combo_roles->currentIndex()){ + int id_service_access = combo_roles->property("id_service_access").toInt(); + QJsonObject data_obj; + // Ok, value was modified - must add! + QJsonValue role = combo_roles->currentData().toString(); + data_obj.insert("id_user_group", m_data->getId()); + if ((id_service_access > 0 && combo_roles->currentIndex() != 0) || (id_service_access == 0 && combo_roles->currentIndex()>0)){ + // Only add "id_service_role" if the item is selected. When no role is selected, don't include any id_service_role + data_obj.insert("id_service_role", role); + } + data_obj.insert("id_service_access", id_service_access); + roles.append(data_obj); + } + } + + if (!roles.isEmpty()){ + base_obj.insert("service_access", roles); + document.setObject(base_obj); + postDataRequest(WEB_SERVICEACCESSINFO_PATH, document.toJson()); + } +} + diff --git a/client/src/editors/UserGroupWidget.h b/client/src/editors/UserGroupWidget.h index eb556814..2e909a0a 100644 --- a/client/src/editors/UserGroupWidget.h +++ b/client/src/editors/UserGroupWidget.h @@ -5,9 +5,8 @@ #include #include +#include "editors/DataListWidget.h" #include "DataEditorWidget.h" -#include "GlobalMessageBox.h" -#include "dialogs/BaseDialog.h" namespace Ui { class UserGroupWidget; @@ -30,6 +29,8 @@ private slots: void processProjectAccessReply(QList access, QUrlQuery reply_query); void processSitesReply(QList sites); void processProjectsReply(QList projects); + void processServiceRolesReply(QList roles, QUrlQuery reply_query); + void processServiceAccessReply(QList access, QUrlQuery reply_query); void processUserUserGroupsReply(QList users_user_groups); void processPostOKReply(QString path); void processDeleteReply(QString path, int id); @@ -38,23 +39,30 @@ private slots: void btnUpdateProjectAccess_clicked(); void comboSiteRole_changed(int index); + void comboProjectRole_changed(int index); + void comboServiceRole_changed(int index); + void on_tabNav_currentChanged(int index); void on_btnUpdateUsers_clicked(); + void on_btnUpdateServicesRoles_clicked(); + private: Ui::UserGroupWidget *ui; - QMap m_tableProjects_items; - QMultiMap m_tableProjectSite_items; // Map: id_site, project item - QMap m_tableSites_items; + QMap m_tableProjects_items; + QMultiMap m_tableProjectSite_items; // Map: id_site, project item + QMap m_tableSites_items; + QMap m_tableServices_items; - QMap m_listUsersUserGroups_items; - QMap m_listUsers_items; + QMap m_listUsersUserGroups_items; + QMap m_listUsers_items; void connectSignals(); void updateSiteAccess(const TeraData* access); void updateProjectAccess(const TeraData* access); + void updateServiceRole(const TeraData* access); void updateUserUserGroup(const TeraData* uug); void updateControlsState(); diff --git a/client/src/editors/UserGroupWidget.ui b/client/src/editors/UserGroupWidget.ui index 92cd9693..c5cd9cc5 100644 --- a/client/src/editors/UserGroupWidget.ui +++ b/client/src/editors/UserGroupWidget.ui @@ -269,6 +269,9 @@ + + Qt::NoFocus + QAbstractItemView::NoEditTriggers @@ -299,6 +302,9 @@ true + + false + Site @@ -313,6 +319,9 @@ + + false + 0 @@ -374,6 +383,9 @@ + + Qt::NoFocus + QAbstractItemView::NoEditTriggers @@ -404,14 +416,17 @@ true + + false + - Site + Projet - Projet + Site @@ -423,6 +438,9 @@ + + false + 0 @@ -449,6 +467,96 @@
+ + + + :/icons/service.png:/icons/service.png + + + Accès - Services + + + + + + Qt::NoFocus + + + QAbstractItemView::NoEditTriggers + + + false + + + false + + + QAbstractItemView::NoSelection + + + QAbstractItemView::SelectRows + + + true + + + 2 + + + 250 + + + true + + + true + + + false + + + + Service + + + + + Rôle + + + + + + + + false + + + + 0 + 32 + + + + PointingHandCursor + + + Mettre à jour les rôles des services + + + + :/icons/save.png:/icons/save.png + + + + 24 + 24 + + + + + + @@ -460,6 +568,9 @@ + + Qt::NoFocus + 24 diff --git a/client/src/editors/UserSummaryWidget.cpp b/client/src/editors/UserSummaryWidget.cpp index 98b857a0..45fa08b5 100644 --- a/client/src/editors/UserSummaryWidget.cpp +++ b/client/src/editors/UserSummaryWidget.cpp @@ -11,7 +11,9 @@ UserSummaryWidget::UserSummaryWidget(ComManager *comMan, const TeraData *data, c { m_diag_editor = nullptr; +#ifndef OPENTERA_WEBASSEMBLY m_sessionLobby = nullptr; +#endif m_passwordJustGenerated = false; m_idProject = id_project; @@ -50,9 +52,11 @@ UserSummaryWidget::UserSummaryWidget(ComManager *comMan, const TeraData *data, c UserSummaryWidget::~UserSummaryWidget() { delete ui; - - if (m_sessionLobby) +#ifndef OPENTERA_WEBASSEMBLY + if (m_sessionLobby) { m_sessionLobby->deleteLater(); + } +#endif } void UserSummaryWidget::connectSignals() @@ -309,6 +313,7 @@ void UserSummaryWidget::ws_userEvent(UserEvent event) updateFieldsValue(); } +#ifndef OPENTERA_WEBASSEMBLY void UserSummaryWidget::sessionLobbyStartSessionRequested() { int id_session_type = ui->cmbSessionType->currentData().toInt(); @@ -356,6 +361,7 @@ void UserSummaryWidget::on_btnNewSession_clicked() // Show Session Lobby m_sessionLobby->exec(); } +#endif void UserSummaryWidget::on_tabNav_currentChanged(int index) { diff --git a/client/src/editors/UserSummaryWidget.h b/client/src/editors/UserSummaryWidget.h index 3799621c..5448597d 100644 --- a/client/src/editors/UserSummaryWidget.h +++ b/client/src/editors/UserSummaryWidget.h @@ -10,7 +10,9 @@ #include "TeraSessionStatus.h" #include "Utils.h" #include "ServiceConfigWidget.h" +#ifndef OPENTERA_WEBASSEMBLY #include "dialogs/SessionLobbyDialog.h" +#endif namespace Ui { class UserSummaryWidget; @@ -22,9 +24,9 @@ class UserSummaryWidget : public DataEditorWidget public: explicit UserSummaryWidget(ComManager* comMan, const TeraData* data = nullptr, const int &id_project = -1, QWidget *parent = nullptr); - ~UserSummaryWidget(); + ~UserSummaryWidget() override; - void saveData(bool signal=true); + void saveData(bool signal=true) override; void connectSignals(); @@ -34,18 +36,20 @@ class UserSummaryWidget : public DataEditorWidget QMap m_ids_session_types; BaseDialog* m_diag_editor; +#ifndef OPENTERA_WEBASSEMBLY SessionLobbyDialog* m_sessionLobby; +#endif int m_idProject; bool m_passwordJustGenerated; bool m_currentUserPasswordChanged; - void updateControlsState(); - void updateFieldsValue(); + void updateControlsState() override; + void updateFieldsValue() override; void initUI(); - bool validateData(); + bool validateData() override; private slots: void processFormsReply(QString form_type, QString data); @@ -58,11 +62,11 @@ private slots: void userFormValueHasFocus(QWidget* widget); void ws_userEvent(opentera::protobuf::UserEvent event); - +#ifndef OPENTERA_WEBASSEMBLY void sessionLobbyStartSessionRequested(); void sessionLobbyStartSessionCancelled(); - void on_btnNewSession_clicked(); +#endif void on_tabNav_currentChanged(int index); diff --git a/client/src/editors/UserWidget.cpp b/client/src/editors/UserWidget.cpp index 49af9c35..389f1abc 100644 --- a/client/src/editors/UserWidget.cpp +++ b/client/src/editors/UserWidget.cpp @@ -1,12 +1,11 @@ #include "UserWidget.h" #include "ui_UserWidget.h" -#include -#include +#include #include #include -#include +#include #include "dialogs/PasswordStrengthDialog.h" @@ -160,7 +159,7 @@ void UserWidget::updateControlsState(){ if (!has_site_admin_access){ // Check if we are admin in a list one site QList id_sites = m_userSitesRole.keys(); - for(int id_site:qAsConst(id_sites)){ + for(int id_site:std::as_const(id_sites)){ if (m_comManager->isCurrentUserSiteAdmin(id_site)){ has_site_admin_access = true; break; @@ -207,7 +206,7 @@ bool UserWidget::validateData(){ void UserWidget::refreshUsersUserGroups() { - for(QListWidgetItem* item: qAsConst(m_listUserGroups_items)){ + for(QListWidgetItem* item: std::as_const(m_listUserGroups_items)){ if (std::find(m_listUserUserGroups_items.cbegin(), m_listUserUserGroups_items.cend(), item) != m_listUserUserGroups_items.cend()){ //if (m_listUserUserGroups_items.contains(item)){ item->setCheckState(Qt::Checked); @@ -229,7 +228,7 @@ void UserWidget::refreshUsersUserGroups() QJsonArray UserWidget::getSelectedGroupsAsJsonArray() { QJsonArray user_groups; - for(QListWidgetItem* item: qAsConst(m_listUserGroups_items)){ + for(QListWidgetItem* item: std::as_const(m_listUserGroups_items)){ int user_group_id = m_listUserGroups_items.key(item); if (item->checkState() == Qt::Checked){ QJsonObject data_obj; @@ -334,6 +333,19 @@ void UserWidget::updateProjectAccess(const TeraData *project_access) ui->tableProjectsRoles->setItem(current_row,2,item); } +void UserWidget::updateServiceAccess(const TeraData *service_access) +{ + // We assume the table is cleared beforehand and that item isn't already present. + int current_row = ui->tableServicesRoles->rowCount(); + ui->tableServicesRoles->setRowCount(ui->tableServicesRoles->rowCount()+1); + QTableWidgetItem* item = new QTableWidgetItem(service_access->getFieldValue("service_name").toString()); + item->setIcon(QIcon(TeraData::getIconFilenameForDataType(TERADATA_SERVICE))); + ui->tableServicesRoles->setItem(current_row,0,item); + item = new QTableWidgetItem(getRoleName(service_access->getFieldValue("service_access_role_name").toString())); + ui->tableServicesRoles->setItem(current_row,1,item); + +} + void UserWidget::queryUserAccess() { // Roles @@ -344,6 +356,9 @@ void UserWidget::queryUserAccess() ui->tableSitesRoles->clearContents(); ui->tableSitesRoles->setRowCount(0); ui->tableSitesRoles->sortItems(-1); + ui->tableServicesRoles->clearContents(); + ui->tableServicesRoles->setRowCount(0); + ui->tableServicesRoles->sortItems(-1); // Query sites and projects roles if (!m_data->isNew()){ @@ -351,6 +366,9 @@ void UserWidget::queryUserAccess() args.addQueryItem(WEB_QUERY_ID_USER, QString::number(m_data->getId())); queryDataRequest(WEB_SITEACCESS_PATH, args); + + queryDataRequest(WEB_SERVICEACCESSINFO_PATH, args); + args.addQueryItem(WEB_QUERY_WITH_SITES, "1"); queryDataRequest(WEB_PROJECTACCESS_PATH, args); @@ -362,7 +380,7 @@ bool UserWidget::validateUserGroups() //if (!m_comManager->isCurrentUserSuperAdmin()){ bool at_least_one_selected = false; //for (int i=0; icheckState() == Qt::Checked){ at_least_one_selected = true; break; @@ -405,7 +423,7 @@ void UserWidget::processSitesAccessReply(QList sites) foreach (TeraData site, sites){ updateSiteAccess(&site); } - ui->tableSitesRoles->resizeColumnsToContents(); + //ui->tableSitesRoles->resizeColumnsToContents(); } void UserWidget::processProjectsAccessReply(QList projects) @@ -413,7 +431,17 @@ void UserWidget::processProjectsAccessReply(QList projects) foreach (TeraData project, projects){ updateProjectAccess(&project); } - ui->tableProjectsRoles->resizeColumnsToContents(); + //ui->tableProjectsRoles->resizeColumnsToContents(); +} + +void UserWidget::processServicesAccessReply(QList services_access) +{ + foreach (TeraData access, services_access){ + // Ignore "OpenTera" service + if (access.getFieldValue("service_key").toString() == "OpenTeraServer") + continue; + updateServiceAccess(&access); + } } void UserWidget::processUserGroupsReply(QList user_groups, QUrlQuery query) @@ -502,6 +530,7 @@ void UserWidget::connectSignals() connect(m_comManager, &ComManager::usersReceived, this, &UserWidget::processUsersReply); connect(m_comManager, &ComManager::siteAccessReceived, this, &UserWidget::processSitesAccessReply); connect(m_comManager, &ComManager::projectAccessReceived, this, &UserWidget::processProjectsAccessReply); + connect(m_comManager, &ComManager::servicesAccessReceived, this, &UserWidget::processServicesAccessReply); connect(m_comManager, &ComManager::formReceived, this, &UserWidget::processFormsReply); connect(m_comManager, &ComManager::userGroupsReceived, this, &UserWidget::processUserGroupsReply); connect(m_comManager, &ComManager::userUserGroupsReceived, this, &UserWidget::processUserUsersGroupsReply); @@ -527,6 +556,7 @@ void UserWidget::initUI() void UserWidget::on_tabMain_currentChanged(int index) { QUrlQuery args; + bool super_admin = m_data->getFieldValue("user_superadmin").toBool(); if (!ui->tabMain->currentWidget()->isEnabled()) return; @@ -543,19 +573,21 @@ void UserWidget::on_tabMain_currentChanged(int index) //} // If user is super admin, disable groups - bool super_admin = m_data->getFieldValue("user_superadmin").toBool(); ui->lblWarning->setVisible(super_admin); ui->frameGroups->setVisible(!super_admin); } if (current_tab == ui->tabRoles){ queryUserAccess(); + // If user is super admin, disable services + ui->lblWarning2->setVisible(super_admin); + ui->tableServicesRoles->setVisible(!super_admin); } if (current_tab == ui->tabConfig){ // Service config if (!ui->wdgServiceConfig->layout()){ QHBoxLayout* layout = new QHBoxLayout(); - layout->setMargin(0); + layout->setContentsMargins(0,0,0,0); ui->wdgServiceConfig->setLayout(layout); } if (ui->wdgServiceConfig->layout()->count() == 0){ @@ -605,7 +637,7 @@ void UserWidget::on_btnUpdateGroups_clicked() QList user_user_group_to_delete; //for (int i=0; icheckState()==Qt::Checked){ QJsonObject item_obj; diff --git a/client/src/editors/UserWidget.h b/client/src/editors/UserWidget.h index 35d66955..cea5644d 100644 --- a/client/src/editors/UserWidget.h +++ b/client/src/editors/UserWidget.h @@ -64,6 +64,7 @@ class UserWidget : public DataEditorWidget void updateUserGroup(const TeraData* group); void updateSiteAccess(const TeraData* site_access); void updateProjectAccess(const TeraData* project_access); + void updateServiceAccess(const TeraData* service_access); void queryUserAccess(); @@ -76,6 +77,7 @@ private slots: void processUsersReply(QList users); void processSitesAccessReply(QList sites); void processProjectsAccessReply(QList projects); + void processServicesAccessReply(QList services_access); void processUserGroupsReply(QList user_groups, QUrlQuery query); void processUserUsersGroupsReply(QList user_users_groups, QUrlQuery query); void processUserPrefsReply(QList user_prefs, QUrlQuery query); diff --git a/client/src/editors/UserWidget.ui b/client/src/editors/UserWidget.ui index 199f7b91..855d1681 100644 --- a/client/src/editors/UserWidget.ui +++ b/client/src/editors/UserWidget.ui @@ -70,7 +70,7 @@ - 5 + 0 @@ -297,6 +297,9 @@ 0 + + Qt::NoFocus + QAbstractItemView::NoEditTriggers @@ -306,6 +309,12 @@ QAbstractItemView::NoSelection + + + 24 + 24 + + true @@ -408,12 +417,21 @@ + + QAbstractItemView::NoEditTriggers + QAbstractItemView::NoSelection QAbstractItemView::SelectRows + + + 24 + 24 + + true @@ -486,12 +504,21 @@ + + QAbstractItemView::NoEditTriggers + QAbstractItemView::NoSelection QAbstractItemView::SelectRows + + + 24 + 24 + + true @@ -524,6 +551,94 @@ + + + + + + + 32 + 32 + + + + + 32 + 32 + + + + + + + :/icons/service.png + + + true + + + + + + + + 10 + true + true + + + + Services + + + + + + + + + Cet utilisateur est un super-administrateur. + + + + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::NoSelection + + + + 24 + 24 + + + + 175 + + + true + + + true + + + false + + + + Service + + + + + Rôle + + + + diff --git a/client/src/kit/KitConfigDialog.h b/client/src/kit/KitConfigDialog.h index 96d38e8f..b1bbd434 100644 --- a/client/src/kit/KitConfigDialog.h +++ b/client/src/kit/KitConfigDialog.h @@ -25,7 +25,7 @@ class KitConfigDialog : public QDialog public: explicit KitConfigDialog(ComManager* comMan, KitConfigManager* kitConfig, QWidget *parent = nullptr); - ~KitConfigDialog(); + ~KitConfigDialog() override; private slots: void on_btnClose_clicked(); diff --git a/client/src/kit/KitVideoRehabWidget.cpp b/client/src/kit/KitVideoRehabWidget.cpp index 4e1b0ebb..95d58e0e 100644 --- a/client/src/kit/KitVideoRehabWidget.cpp +++ b/client/src/kit/KitVideoRehabWidget.cpp @@ -53,13 +53,11 @@ void KitVideoRehabWidget::initUi() ui->icoLoading->setMovie(m_loadingIcon); m_loadingIcon->start(); - QWebEngineSettings::defaultSettings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, true); - QWebEngineSettings::defaultSettings()->setAttribute(QWebEngineSettings::PlaybackRequiresUserGesture, false); - m_webEngine = new QWebEngineView(ui->wdgWebEngine); connect(m_webEngine, &QWebEngineView::loadFinished, this, &KitVideoRehabWidget::webPageLoaded); // Create a new page + // DL 21/04/2023 settings are now in VideoRehabWebPage constructor m_webPage = new VideoRehabWebPage(m_webEngine); connect(m_webPage->getSharedObject(), &SharedObject::pageIsReady, this, &KitVideoRehabWidget::webPageReady); connect(m_webPage->getSharedObject(), &SharedObject::videoErrorOccured, this, &KitVideoRehabWidget::webPageVideoError); @@ -294,11 +292,11 @@ void KitVideoRehabWidget::startVirtualCamera(const QString &src) m_virtualCamThread = new VirtualCameraThread(src); connect(m_virtualCamThread, &VirtualCameraThread::virtualCamDisconnected, this, &KitVideoRehabWidget::virtualCameraDisconnected); m_virtualCamThread->start(); + } void KitVideoRehabWidget::stopVirtualCamera() { - qDebug() << "KitVideoRehabWidget::stopVirtualCamera"; if (m_virtualCamThread){ m_virtualCamThread->quit(); m_virtualCamThread->wait(); diff --git a/client/src/libs/AudioVideoUtils.cpp b/client/src/libs/AudioVideoUtils.cpp new file mode 100644 index 00000000..7bc29c11 --- /dev/null +++ b/client/src/libs/AudioVideoUtils.cpp @@ -0,0 +1,194 @@ +#include "AudioVideoUtils.h" +#include +#include +#include + +#include + +AudioVideoUtils::AudioVideoUtils(QObject *parent) : QObject(parent) { + +} + +QStringList AudioVideoUtils::getAudioDeviceNames() +{ + QStringList names; + + + auto audio_devices = QMediaDevices::audioInputs(); + //QList audio_devices = QAudioDeviceInfo::availableDevices(QAudio::AudioInput); + foreach(const QAudioDevice &input, audio_devices){ +//TODO FIX LINUX +#if 0 +//#ifdef Q_OS_LINUX + // On linux, since Qt use ALSA API, we must filter the returned device list... + if (input.deviceName().startsWith("alsa_input") || input.deviceName() == "default"){ + QString filtered_name = input.deviceName(); + if (input.deviceName() != "default"){ + QStringList name_parts = filtered_name.split("."); + // Removes first part - usually is "alsa_input" + if (name_parts.count()>1) + filtered_name = name_parts[1]; + + // Split using "_" to remove first and last part, and replace others with spaces + name_parts = filtered_name.split("_"); + if (name_parts.count()>2){ + filtered_name = ""; + for (int i=1; i1) + filtered_name += " "; + filtered_name += name_parts[i]; + } + }else{ + // No audio name... + name_parts = filtered_name.split("-"); + if (name_parts.count()>2){ + filtered_name = name_parts[1].replace("_", ":"); + } + } + + } + names.append(filtered_name); + } +#else \ + //TODO Not sure! + names.append(input.description()); +#endif + } + return names; +} + +QStringList AudioVideoUtils::getVideoDeviceNames() +{ + QStringList names; + + auto inputs = QMediaDevices::videoInputs(); + foreach (const QCameraDevice &info, inputs) + { + QString name = info.description(); + if (!name.contains(" IR ")) // Remove infrared cameras. + names.append(info.description()); + } +#ifdef Q_OS_WINDOWS + // Also query virtual cameras, since they are not detected anymore by QMediaDevices + names.append(AudioVideoUtils::getVirtualCameras()); + + //names.append("OpenTeraCam"); +#endif + return names; +} + +#ifdef Q_OS_WINDOWS +QStringList AudioVideoUtils::getVirtualCameras() +{ + //qDebug() << "AudioVideoUtils::getVirtualCameras()"; + + IEnumMoniker *pEnum = nullptr; + HRESULT hr = AudioVideoUtils::enumerateCameras(&pEnum); + + if (FAILED(hr)) + { + //qDebug("enumerate Fails"); + return QStringList(); + } + + IMoniker *pMoniker = nullptr; + QString devicePath; + + for (int i = 0; pEnum->Next(1, &pMoniker, nullptr) == S_OK; i++) { + IBaseFilter *filter = nullptr; + hr = pMoniker->BindToObject(nullptr, + nullptr, + IID_IBaseFilter, + reinterpret_cast(&filter)); + + if (FAILED(hr)) { + pMoniker->Release(); + pMoniker = nullptr; + + continue; + } + + CLSID clsid; + + if (FAILED(filter->GetClassID(&clsid))) { + filter->Release(); + pMoniker->Release(); + pMoniker = nullptr; + + continue; + } + + filter->Release(); + + if (clsid != CLSID_VirtualCameraSource) { + pMoniker->Release(); + pMoniker = nullptr; + + continue; + } + + IPropertyBag *pPropBag = nullptr; + hr = pMoniker->BindToStorage(nullptr, + nullptr, + IID_IPropertyBag, + reinterpret_cast(&pPropBag)); + + if (SUCCEEDED(hr)) { + VARIANT var; + VariantInit(&var); + hr = pPropBag->Read(L"FriendlyName", &var, 0); + + if (SUCCEEDED(hr)) + devicePath = QString::fromWCharArray(var.bstrVal); + else + devicePath = QString("OpenTeraCam").arg(i); + + pPropBag->Release(); + } + + pMoniker->Release(); + pMoniker = nullptr; + + break; + } + + pEnum->Release(); + + QStringList webcams; + + if (!devicePath.isEmpty()) + webcams << devicePath; + + + //qDebug() << "AudioVideoUtils: " << webcams; + return webcams; +} + +HRESULT AudioVideoUtils::enumerateCameras(IEnumMoniker **ppEnum) +{ + // Create the System Device Enumerator. + ICreateDevEnum *pDevEnum = nullptr; + HRESULT hr = CoInitialize(nullptr); + + if (SUCCEEDED(hr)) { + + hr = CoCreateInstance(CLSID_SystemDeviceEnum, + nullptr, + CLSCTX_INPROC_SERVER, + IID_ICreateDevEnum, + reinterpret_cast(&pDevEnum)); + + if (SUCCEEDED(hr)) { + // Create an enumerator for the category. + hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, ppEnum, 0); + + if (hr == S_FALSE) + hr = VFW_E_NOT_FOUND; + + pDevEnum->Release(); + } + } + + return hr; +} +#endif diff --git a/client/src/libs/AudioVideoUtils.h b/client/src/libs/AudioVideoUtils.h new file mode 100644 index 00000000..454e4486 --- /dev/null +++ b/client/src/libs/AudioVideoUtils.h @@ -0,0 +1,31 @@ +#ifndef AUDIOVIDEOUTILS_H +#define AUDIOVIDEOUTILS_H + +#include + +#ifdef Q_OS_WINDOWS + #include + #include + #include + #include + DEFINE_GUID(CLSID_VirtualCameraSource, 0x41764b79, 0x7320, 0x5669, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x43, 0x61, 0x6d); +#endif + +class AudioVideoUtils: public QObject +{ + Q_OBJECT + +public: + explicit AudioVideoUtils(QObject *parent = nullptr); + + static QStringList getAudioDeviceNames(); + static QStringList getVideoDeviceNames(); + +private: +#ifdef Q_OS_WINDOWS + static QStringList getVirtualCameras(); + static HRESULT enumerateCameras(IEnumMoniker **ppEnum); +#endif +}; + +#endif // AUDIOVIDEOUTILS_H diff --git a/client/src/main.cpp b/client/src/main.cpp index 779519c4..3bf1a8a3 100755 --- a/client/src/main.cpp +++ b/client/src/main.cpp @@ -3,7 +3,7 @@ * */ #include "ClientApp.h" -#include +//#include #include int main(int argc, char* argv[]) @@ -11,17 +11,6 @@ int main(int argc, char* argv[]) // Global settings QCoreApplication::setOrganizationName("INTER"); QCoreApplication::setApplicationName("OpenTeraPlus"); - -#ifdef WIN32 - //QApplication::setAttribute(Qt::AA_UseOpenGLES, true); // Force use of ANGLE instead of Desktop OpenGL - some memory leak may happen with WebRTC on Intel Graphics Cards otherwise. - QApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true); - QApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton, true); -#endif - -#ifdef __APPLE__ - QApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true); -#endif - #ifdef QT_DEBUG qputenv("QTWEBENGINE_CHROMIUM_FLAGS", "--autoplay-policy=no-user-gesture-required --remote-debugging-port=22222"); // Allow auto-play feature in webengine without any user interaction to test, for example, the microphone and play sounds #else @@ -34,10 +23,11 @@ int main(int argc, char* argv[]) #ifndef WIN32 // Don't set style on Windows - it creates some issues with combobox look. //app->setStyle("windows"); #endif + //WebEngine default Settings - /*QWebEngineSettings::defaultSettings()->setAttribute(QWebEngineSettings::PluginsEnabled, true); - QWebEngineSettings::defaultSettings()->setAttribute(QWebEngineSettings::JavascriptEnabled, true); - QWebEngineSettings::defaultSettings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, true);*/ + //QWebEngineSettings::defaultSettings()->setAttribute(QWebEngineSettings::PluginsEnabled, true); + //QWebEngineSettings::defaultSettings()->setAttribute(QWebEngineSettings::JavascriptEnabled, true); + //QWebEngineSettings::defaultSettings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, true); return app.exec(); } diff --git a/client/src/main/MainWindow.cpp b/client/src/main/MainWindow.cpp index ff39ca4e..af8657f1 100644 --- a/client/src/main/MainWindow.cpp +++ b/client/src/main/MainWindow.cpp @@ -1,8 +1,7 @@ #include "MainWindow.h" #include -#include +//#include #include -#include #include "ui_MainWindow.h" @@ -25,7 +24,9 @@ MainWindow::MainWindow(ComManager *com_manager, const QString ¤t_server, Q m_diag_editor = nullptr; m_data_editor = nullptr; m_dashboard = nullptr; +#ifndef OPENTERA_WEBASSEMBLY m_inSessionWidget = nullptr; +#endif m_download_dialog = nullptr; m_joinSession_dialog = nullptr; m_currentLanguage = m_comManager->getCurrentPreferences().getLanguage(); @@ -64,7 +65,11 @@ MainWindow::~MainWindow() bool MainWindow::isInSession() { +#ifndef OPENTERA_WEBASSEMBLY return m_inSessionWidget != nullptr; +#else + return false; +#endif } void MainWindow::connectSignals() @@ -92,6 +97,7 @@ void MainWindow::connectSignals() connect(m_comManager->getWebSocketManager(), &WebSocketManager::userEventReceived, this, &MainWindow::ws_userEvent); connect(m_comManager->getWebSocketManager(), &WebSocketManager::participantEventReceived, this, &MainWindow::ws_participantEvent); + connect(m_comManager->getWebSocketManager(), &WebSocketManager::deviceEventReceived, this, &MainWindow::ws_deviceEvent); connect(m_comManager->getWebSocketManager(), &WebSocketManager::joinSessionEventReceived, this, &MainWindow::ws_joinSessionEvent); connect(ui->projNavigator, &ProjectNavigator::dataDisplayRequest, this, &MainWindow::dataDisplayRequested); @@ -110,6 +116,7 @@ void MainWindow::connectSignals() void MainWindow::initUi() { ui->btnConfig->hide(); + ui->lblVersion->hide(); // Setup messages ui->wdgMessages->hide(); @@ -159,6 +166,7 @@ void MainWindow::showDataEditor(const TeraDataTypes &data_type, const TeraData*d showDashboard(false); + // Save values to display them again if needed m_currentDataType = data_type; m_currentDataId = data->getId(); @@ -253,7 +261,7 @@ void MainWindow::showDashboard(const bool &show) } } - +#ifndef OPENTERA_WEBASSEMBLY void MainWindow::setInSession(bool in_session, const TeraData *session_type, const int &id_session, int id_project) { if (!in_session && !isInSession()) @@ -288,16 +296,21 @@ void MainWindow::setInSession(bool in_session, const TeraData *session_type, con if (in_session){ if (id_project == 0) id_project = ui->projNavigator->getCurrentProjectId(); - m_inSessionWidget = new InSessionWidget(m_comManager, session_type, id_session, id_project); + m_inSessionWidget = new InSessionWidget(m_comManager, session_type, id_session, id_project, nullptr, this); connect(m_inSessionWidget, &InSessionWidget::sessionEndedWithError, this, &MainWindow::inSession_sessionEndedWithError); connect(m_inSessionWidget, &InSessionWidget::requestShowNotification, this, &MainWindow::addNotification); ui->wdgMainTop->layout()->addWidget(m_inSessionWidget); + ui->btnEditUser->hide(); }else{ // Loads back the "previous" data type dataDisplayRequested(m_currentDataType, m_currentDataId); + ui->btnEditUser->show(); } -} + // Refresh user information - hiding config button at the same time + updateCurrentUser(); +} +#endif QIcon MainWindow::getGlobalEventIcon(GlobalEvent &global_event) { switch(global_event.getEventType()){ @@ -413,6 +426,7 @@ void MainWindow::editorDialogFinished() ui->projNavigator->setOnHold(false); } +#ifndef OPENTERA_WEBASSEMBLY void MainWindow::joinSessionDialogFinished() { if (m_joinSession_dialog->result() == JoinSessionDialog::Accepted){ @@ -443,6 +457,7 @@ void MainWindow::joinSessionDialogFinished() m_joinSession_dialog->deleteLater(); m_joinSession_dialog = nullptr; } +#endif void MainWindow::dataDisplayRequested(TeraDataTypes data_type, int data_id) { @@ -499,8 +514,12 @@ void MainWindow::dataDisplayRequested(TeraDataTypes data_type, int data_id) void MainWindow::dataDisplayRequestedByUuid(TeraDataTypes data_type, QString data_uuid) { // Try to select in project navigator - if (data_type == TERADATA_PROJECT || data_type == TERADATA_PARTICIPANT || data_type == TERADATA_GROUP) - ui->projNavigator->selectItemByUuid(data_type, data_uuid); + if (data_type == TERADATA_PROJECT || data_type == TERADATA_PARTICIPANT || data_type == TERADATA_GROUP){ + if (ui->projNavigator->selectItemByUuid(data_type, data_uuid)){ + // Selected in the project navigator - will do its query from there! + return; + } + } // Request to display a specific item by uuid. QUrlQuery query; @@ -543,7 +562,7 @@ void MainWindow::updateCurrentUser() if (m_comManager->getCurrentUser().hasFieldName("user_name")){ // Ok, we have a user to update. ui->lblUser->setText(m_comManager->getCurrentUser().getName()); - ui->btnConfig->setVisible(m_comManager->getCurrentUser().getFieldValue("user_superadmin").toBool()); + ui->btnConfig->setVisible(m_comManager->getCurrentUser().getFieldValue("user_superadmin").toBool() && !isInSession()); } } @@ -722,6 +741,7 @@ void MainWindow::com_preferencesUpdated() void MainWindow::com_sessionStarted(TeraData session_type, int id_session) { +#ifndef OPENTERA_WEBASSEMBLY if (!m_inSessionWidget){ // Loads the in-session widget since none loaded yet! setInSession(true, &session_type, id_session); @@ -729,29 +749,35 @@ void MainWindow::com_sessionStarted(TeraData session_type, int id_session) // Update session id in InSessionWidget m_inSessionWidget->setSessionId(id_session); } +#endif } void MainWindow::com_sessionStartRequested(TeraData session_type) { +#ifndef OPENTERA_WEBASSEMBLY // Loads the in-session widget setInSession(true, &session_type, -1); +#endif } void MainWindow::com_sessionStopped(int id_session) { +#ifndef OPENTERA_WEBASSEMBLY setInSession(false, nullptr, id_session); +#endif } void MainWindow::com_sessionError(QString error) { +#ifndef OPENTERA_WEBASSEMBLY setInSession(false, nullptr, -1); +#endif GlobalMessageBox msg; msg.showError(tr("Erreur de séance"), tr("Une erreur est survenue:\n") + error + tr("\n\nLa séance ne peut pas continuer.")); } void MainWindow::ws_userEvent(UserEvent event) { - if (event.type() == UserEvent_EventType_USER_CONNECTED){ // Don't do anything for current user! if (event.user_uuid() == m_comManager->getCurrentUser().getUuid().toStdString()) @@ -762,9 +788,6 @@ void MainWindow::ws_userEvent(UserEvent event) // Add a trace in events also GlobalEvent g_event(EVENT_LOGIN, msg_text); addGlobalEvent(g_event); - - // Update online users list - //updateOnlineUser(QString::fromStdString(event.user_uuid()), true, QString::fromStdString(event.user_fullname())); } if (event.type() == UserEvent_EventType_USER_DISCONNECTED){ @@ -773,9 +796,6 @@ void MainWindow::ws_userEvent(UserEvent event) GlobalEvent g_event(EVENT_LOGOUT, msg_text); addGlobalEvent(g_event); - - // Update online users list - //updateOnlineUser(QString::fromStdString(event.user_uuid()), false); } } @@ -790,9 +810,6 @@ void MainWindow::ws_participantEvent(ParticipantEvent event) // Add a trace in events also GlobalEvent g_event(EVENT_LOGIN, msg_text); addGlobalEvent(g_event); - - // Update online users list - //updateOnlineParticipant(QString::fromStdString(event.participant_uuid()), true, QString::fromStdString(event.participant_name())); } } @@ -805,15 +822,33 @@ void MainWindow::ws_participantEvent(ParticipantEvent event) // Add a trace in events also GlobalEvent g_event(EVENT_LOGOUT, msg_text); addGlobalEvent(g_event); - - // Update online participants list - //updateOnlineParticipant(QString::fromStdString(event.participant_uuid()), false); } } } +void MainWindow::ws_deviceEvent(DeviceEvent event) +{ + if (event.type() == opentera::protobuf::DeviceEvent_EventType_DEVICE_CONNECTED){ + QString msg_text = "" + QString::fromStdString(event.device_name()) + "" + tr(" est en ligne."); + addNotification(NotificationWindow::TYPE_MESSAGE, msg_text, "://icons/device_online.png", "://sounds/notify_online.wav"); + + // Add a trace in events also + GlobalEvent g_event(EVENT_LOGIN, msg_text); + addGlobalEvent(g_event); + } + + if (event.type() == opentera::protobuf::DeviceEvent_EventType_DEVICE_DISCONNECTED){ + QString msg_text = "" + QString::fromStdString(event.device_name()) + "" + tr(" est hors-ligne."); + addNotification(NotificationWindow::TYPE_MESSAGE, msg_text, "://icons/device_offline.png", "://sounds/notify_offline.wav"); + + GlobalEvent g_event(EVENT_LOGOUT, msg_text); + addGlobalEvent(g_event); + } +} + void MainWindow::ws_joinSessionEvent(JoinSessionEvent event) { +#ifndef OPENTERA_WEBASSEMBLY if (isInSession()){ // If we are in a session, the InSession Widget will handle that event for us. return; @@ -837,6 +872,7 @@ void MainWindow::ws_joinSessionEvent(JoinSessionEvent event) return; } } +#endif } void MainWindow::inSession_sessionEndedWithError() @@ -897,8 +933,11 @@ void MainWindow::addNotification(const NotificationWindow::NotificationType noti connect(notify, &NotificationWindow::notificationClosed, this, &MainWindow::notificationCompleted); if (m_comManager->getCurrentPreferences().isNotifySounds() && !soundPath.isEmpty()){ - if (!m_inSessionWidget) // Don't play sounds when in session! - QSound::play(soundPath); + if (!isInSession()) {// Don't play sounds when in session! + m_soundPlayer.setSource(QUrl::fromLocalFile(soundPath)); + m_soundPlayer.setVolume(0.25f); + m_soundPlayer.play(); + } } } @@ -1008,6 +1047,7 @@ void MainWindow::changeEvent(QEvent* event) void MainWindow::closeEvent(QCloseEvent *event) { // About to close... check if we have something in progress that prevents it +#ifndef OPENTERA_WEBASSEMBLY if (m_inSessionWidget){ if (!m_inSessionWidget->sessionCanBeEnded()){ GlobalMessageBox msg; @@ -1016,16 +1056,17 @@ void MainWindow::closeEvent(QCloseEvent *event) return; } } +#endif event->accept(); } void MainWindow::on_lblLogo_clicked() { - AboutDialog about(m_comManager->getServerUrl(), this); - +#ifndef OPENTERA_WEBASSEMBLY + AboutDialog about(m_comManager->getServerUrl()); about.setFixedSize(size().width()-50, size().height()-150); - about.move(this->x()+25, this->y()+75); - + //about.move(this->x()+25, this->y()+75); about.exec(); +#endif } diff --git a/client/src/main/MainWindow.h b/client/src/main/MainWindow.h index 60c26913..f6e3543d 100644 --- a/client/src/main/MainWindow.h +++ b/client/src/main/MainWindow.h @@ -5,6 +5,7 @@ #include #include #include +#include #include "editors/UserWidget.h" #include "dialogs/BaseDialog.h" @@ -20,10 +21,16 @@ #include "data/GlobalEvent.h" #include "data/DownloadingFile.h" #include "TeraSessionCategory.h" + +#ifndef OPENTERA_WEBASSEMBLY #include "widgets/InSessionWidget.h" +#endif + #include "dialogs/JoinSessionDialog.h" +#ifndef OPENTERA_WEBASSEMBLY #include "dialogs/AboutDialog.h" +#endif // Protobuf #include "UserEvent.pb.h" @@ -50,6 +57,7 @@ class MainWindow : public QMainWindow public slots: void ws_userEvent(opentera::protobuf::UserEvent event); void ws_participantEvent(opentera::protobuf::ParticipantEvent event); + void ws_deviceEvent(opentera::protobuf::DeviceEvent event); void ws_joinSessionEvent(opentera::protobuf::JoinSessionEvent event); private slots: @@ -88,7 +96,10 @@ private slots: void addGlobalEvent(GlobalEvent event); void editorDialogFinished(); +#ifndef OPENTERA_WEBASSEMBLY void joinSessionDialogFinished(); +#endif + void dataDisplayRequested(TeraDataTypes data_type, int data_id); void dataDisplayRequestedByUuid(TeraDataTypes data_type, QString data_uuid); void dataDeleteRequested(TeraDataTypes data_type, int data_id); @@ -99,9 +110,7 @@ private slots: void on_btnConfig_clicked(); void on_btnLog_toggled(bool checked); - void on_tableHistory_itemDoubleClicked(QTableWidgetItem *item); - void on_lblLogo_clicked(); private: @@ -109,8 +118,9 @@ private slots: void initUi(); void showDataEditor(const TeraDataTypes &data_type, const TeraData *data); void showDashboard(const bool &show); +#ifndef OPENTERA_WEBASSEMBLY void setInSession(bool in_session, const TeraData *session_type, const int& id_session, int id_project=0); - +#endif // Messages and notifications void addMessage(Message::MessageType msg_type, QString msg); void addMessage(Message &msg); @@ -126,7 +136,9 @@ private slots: BaseDialog* m_diag_editor; DataEditorWidget* m_data_editor; DashboardWidget* m_dashboard; +#ifndef OPENTERA_WEBASSEMBLY InSessionWidget* m_inSessionWidget; +#endif TransferProgressDialog* m_download_dialog; JoinSessionDialog* m_joinSession_dialog; TeraDataTypes m_waiting_for_data_type; @@ -135,6 +147,7 @@ private slots: // Message & notification system QList m_notifications; + QSoundEffect m_soundPlayer; // UI items QMovie* m_loadingIcon; diff --git a/client/src/main/MainWindow.ui b/client/src/main/MainWindow.ui index 347c8468..088328da 100644 --- a/client/src/main/MainWindow.ui +++ b/client/src/main/MainWindow.ui @@ -6,8 +6,8 @@ 0 0 - 1689 - 897 + 950 + 514 @@ -40,8 +40,8 @@ QLabel#lblLogo, QLabel#lblVersion, QLabel#lblUser{background-color:transparent;} 0 0 - 1069 - 759 + 330 + 376 @@ -139,7 +139,7 @@ QLabel#lblLogo, QLabel#lblVersion, QLabel#lblUser{background-color:transparent;} - 409 + 595 50 @@ -210,18 +210,78 @@ QLabel#lblLogo, QLabel#lblVersion, QLabel#lblUser{background-color:transparent;}
- + + + + 0 + 0 + + 0 32 + + + 100 + 16777215 + + + + PointingHandCursor + - 0.1 + Profil - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + :/icons/software_user.png:/icons/software_user.png + + + + 20 + 20 + + + + + + + + + 0 + 0 + + + + + 0 + 32 + + + + + 100 + 16777215 + + + + PointingHandCursor + + + Admin + + + + :/icons/config.png:/icons/config.png + + + + 20 + 20 + @@ -362,6 +422,22 @@ QLabel#lblLogo, QLabel#lblVersion, QLabel#lblUser{background-color:transparent;}
+ + + + + 0 + 32 + + + + 0.1 + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + @@ -377,99 +453,6 @@ QLabel#lblLogo, QLabel#lblVersion, QLabel#lblUser{background-color:transparent;} - - - - - - - 0 - 0 - - - - - 0 - 32 - - - - - 100 - 16777215 - - - - PointingHandCursor - - - Profil - - - - :/icons/software_user.png:/icons/software_user.png - - - - 20 - 20 - - - - - - - - - 0 - 0 - - - - - 0 - 32 - - - - - 100 - 16777215 - - - - PointingHandCursor - - - Admin - - - - :/icons/config.png:/icons/config.png - - - - 20 - 20 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - @@ -509,14 +492,14 @@ QLabel#lblLogo, QLabel#lblVersion, QLabel#lblUser{background-color:transparent;} - :/icons/project.png:/icons/project.png + :/icons/details.png:/icons/details.png Navigateur - 4 + 1 4 @@ -546,6 +529,12 @@ QLabel#lblLogo, QLabel#lblVersion, QLabel#lblUser{background-color:transparent;} 0 + + + 0 + 200 + + :/status/status_ok.png:/status/status_ok.png @@ -558,7 +547,7 @@ QLabel#lblLogo, QLabel#lblVersion, QLabel#lblUser{background-color:transparent;} QLayout::SetMinAndMaxSize - 4 + 0 4 @@ -758,7 +747,7 @@ QLabel#lblLogo, QLabel#lblVersion, QLabel#lblUser{background-color:transparent;} 0 0 - 1689 + 950 21 diff --git a/client/src/managers/AssetComManager.cpp b/client/src/managers/AssetComManager.cpp index 982b7356..46f2ccbe 100644 --- a/client/src/managers/AssetComManager.cpp +++ b/client/src/managers/AssetComManager.cpp @@ -86,7 +86,7 @@ bool AssetComManager::handleDataReply(const QString &reply_path, const QString & QList items; if (data_list.isArray()){ QJsonArray data_list_array = data_list.array(); - for (const QJsonValue &data:qAsConst(data_list_array)){ + for (const QJsonValue &data:std::as_const(data_list_array)){ items.append(data.toObject()); } }else{ diff --git a/client/src/managers/BaseComManager.cpp b/client/src/managers/BaseComManager.cpp index c6176f8a..38f34582 100644 --- a/client/src/managers/BaseComManager.cpp +++ b/client/src/managers/BaseComManager.cpp @@ -23,8 +23,10 @@ BaseComManager::BaseComManager(QUrl serverUrl, QObject *parent) // Connect base signals connect(m_netManager, &QNetworkAccessManager::finished, this, &BaseComManager::onNetworkFinished); +#ifndef OPENTERA_WEBASSEMBLY connect(m_netManager, &QNetworkAccessManager::sslErrors, this, &BaseComManager::onNetworkSslErrors); connect(m_netManager, &QNetworkAccessManager::encrypted, this, &BaseComManager::onNetworkEncrypted); +#endif connect(m_netManager, &QNetworkAccessManager::authenticationRequired, this, &BaseComManager::onNetworkAuthenticationRequired); } @@ -256,7 +258,7 @@ bool BaseComManager::hasDownloadsWaiting() void BaseComManager::updateWaitingDownloadsQueryParameter(const QString ¶meter, const QString &new_value) { - for(DownloadingFile* file: qAsConst(m_waitingDownloads)){ + for(DownloadingFile* file: std::as_const(m_waitingDownloads)){ QNetworkRequest* request = m_waitingDownloads.key(file); QUrlQuery query(request->url()); if (query.hasQueryItem(parameter)){ @@ -271,7 +273,7 @@ void BaseComManager::updateWaitingDownloadsQueryParameter(const QString ¶met void BaseComManager::updateWaitingDownloadsQueryParameter(const QString &download_uuid, const QString ¶meter, const QString &new_value) { - for(DownloadingFile* file: qAsConst(m_waitingDownloads)){ + for(DownloadingFile* file: std::as_const(m_waitingDownloads)){ if (file->getAssociatedUuid() == download_uuid){ QNetworkRequest* request = m_waitingDownloads.key(file); QUrlQuery query(request->url()); @@ -288,28 +290,28 @@ void BaseComManager::updateWaitingDownloadsQueryParameter(const QString &downloa void BaseComManager::abortDownloads() { - for(DownloadingFile* file:qAsConst(m_waitingDownloads)){ + for(DownloadingFile* file:std::as_const(m_waitingDownloads)){ delete m_waitingDownloads.key(file); file->deleteLater(); } m_waitingDownloads.clear(); QList files = m_currentDownloads.values(); - for(DownloadingFile* file:qAsConst(files)){ + for(DownloadingFile* file:std::as_const(files)){ file->abortTransfer(); // Should cascade and be deleted on_Transfer_abort } } void BaseComManager::abortUploads() { - for(UploadingFile* file:qAsConst(m_waitingUploads)){ + for(UploadingFile* file:std::as_const(m_waitingUploads)){ delete m_waitingUploads.key(file); file->deleteLater(); } m_waitingUploads.clear(); QList files = m_currentUploads.values(); - for(UploadingFile* file:qAsConst(files)){ + for(UploadingFile* file:std::as_const(files)){ file->abortTransfer(); // Should cascade and be deleted on_Transfer_abort } } @@ -367,13 +369,14 @@ void BaseComManager::onNetworkFinished(QNetworkReply *reply){ m_currentDownloads[reply]->abortTransfer(); } - emit networkError(reply->error(), reply_msg, reply->operation(), status_code); + emit networkError(reply->error(), reply_msg, reply->operation(), status_code, reply->url().path(), QUrlQuery(reply->url().query())); } reply->deleteLater(); } +#ifndef OPENTERA_WEBASSEMBLY void BaseComManager::onNetworkSslErrors(QNetworkReply *reply, const QList &errors){ Q_UNUSED(reply) Q_UNUSED(errors) @@ -389,6 +392,8 @@ void BaseComManager::onNetworkEncrypted(QNetworkReply *reply){ Q_UNUSED(reply) //qDebug() << QString(this->metaObject()->className()) + "::onNetworkEncrypted"; } +#endif + void BaseComManager::onNetworkAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator) { diff --git a/client/src/managers/BaseComManager.h b/client/src/managers/BaseComManager.h index 4a834521..568bfa7f 100644 --- a/client/src/managers/BaseComManager.h +++ b/client/src/managers/BaseComManager.h @@ -91,8 +91,12 @@ class BaseComManager : public QObject protected slots: virtual void onNetworkFinished(QNetworkReply *reply); + +#ifndef OPENTERA_WEBASSEMBLY virtual void onNetworkSslErrors(QNetworkReply *reply, const QList &errors); void onNetworkEncrypted(QNetworkReply *reply); +#endif + void onNetworkAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator); void onTransferProgress(TransferringFile* file); @@ -101,7 +105,7 @@ protected slots: signals: void waitingForReply(bool waiting); - void networkError(QNetworkReply::NetworkError, QString, QNetworkAccessManager::Operation op, int status_code); + void networkError(QNetworkReply::NetworkError error, QString error_str, QNetworkAccessManager::Operation op, int status_code, QString path, QUrlQuery url_query); void networkAuthFailed(); diff --git a/client/src/managers/ComManager.cpp b/client/src/managers/ComManager.cpp index 9f6df7be..8bf996f6 100644 --- a/client/src/managers/ComManager.cpp +++ b/client/src/managers/ComManager.cpp @@ -574,7 +574,7 @@ bool ComManager::handleDataReply(const QString& reply_path, const QString &reply TeraDataTypes items_type = TeraData::getDataTypeFromPath(reply_path); if (data_list.isArray()){ const QJsonArray data_array = data_list.array(); - for (const QJsonValue &data:data_array){ + for (const QJsonValue data:data_array){ TeraData item_data(items_type, data); // Check if the currently connected user was updated and not requesting a list (limited information) @@ -697,6 +697,9 @@ bool ComManager::handleDataReply(const QString& reply_path, const QString &reply case TERADATA_SERVICE_CONFIG: emit servicesConfigReceived(items, reply_query); break; + case TERADATA_SERVICE_ROLE: + emit servicesRolesReceived(items, reply_query); + break; case TERADATA_STATS: if (items.count() > 0) emit statsReceived(items.first(), reply_query); diff --git a/client/src/managers/ComManager.h b/client/src/managers/ComManager.h index 8ed42270..d7184f67 100644 --- a/client/src/managers/ComManager.h +++ b/client/src/managers/ComManager.h @@ -7,7 +7,11 @@ #include #include #include + +#ifndef OPENTERA_WEBASSEMBLY #include +#endif + #include #include @@ -143,6 +147,7 @@ class ComManager : public BaseComManager void servicesSitesReceived(QList sites_list, QUrlQuery reply_query); void servicesAccessReceived(QList access_list, QUrlQuery reply_query); void servicesConfigReceived(QList config_list, QUrlQuery reply_query); + void servicesRolesReceived(QList roles_list, QUrlQuery reply_query); void statsReceived(TeraData stats, QUrlQuery reply_query); void onlineUsersReceived(QList users_list, QUrlQuery reply_query); void onlineParticipantsReceived(QList participants_list, QUrlQuery reply_query); diff --git a/client/src/managers/ParticipantComManager.h b/client/src/managers/ParticipantComManager.h index ee1f05b6..59f8da51 100644 --- a/client/src/managers/ParticipantComManager.h +++ b/client/src/managers/ParticipantComManager.h @@ -7,7 +7,9 @@ #include #include #include -#include +#ifndef OPENTERA_WEBASSEMBLY + #include +#endif #include #include diff --git a/client/src/managers/WebSocketManager.cpp b/client/src/managers/WebSocketManager.cpp index c4c1305f..5b4839c9 100644 --- a/client/src/managers/WebSocketManager.cpp +++ b/client/src/managers/WebSocketManager.cpp @@ -18,7 +18,9 @@ WebSocketManager::WebSocketManager(QObject *parent) : QObject(parent) connect(m_webSocket, &QWebSocket::connected, this, &WebSocketManager::onSocketConnected); connect(m_webSocket, &QWebSocket::disconnected, this, &WebSocketManager::onSocketDisconnected); connect(m_webSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onSocketError(QAbstractSocket::SocketError))); +#ifndef OPENTERA_WEBASSEMBLY connect(m_webSocket, &QWebSocket::sslErrors, this, &WebSocketManager::onSocketSslErrors); +#endif connect(m_webSocket, &QWebSocket::textMessageReceived, this, &WebSocketManager::onSocketTextMessageReceived); connect(m_webSocket, &QWebSocket::binaryMessageReceived, this, &WebSocketManager::onSocketBinaryMessageReceived); @@ -136,6 +138,7 @@ void WebSocketManager::onSocketDisconnected() emit serverDisconnected(); } +#ifndef OPENTERA_WEBASSEMBLY void WebSocketManager::onSocketSslErrors(const QList &errors) { Q_UNUSED(errors) @@ -146,6 +149,7 @@ void WebSocketManager::onSocketSslErrors(const QList &errors) qDebug() << "WebSocketManager::SSlErrors " << errors; m_webSocket->ignoreSslErrors(); } +#endif void WebSocketManager::onSocketTextMessageReceived(const QString &message) { @@ -253,7 +257,6 @@ void WebSocketManager::onSocketTextMessageReceived(const QString &message) }else{ LOG_ERROR("Unable to decode received protobuf message", "WebSocketManager::onSocketTextMessageReceived"); } - } void WebSocketManager::onSocketBinaryMessageReceived(const QByteArray &message) diff --git a/client/src/managers/WebSocketManager.h b/client/src/managers/WebSocketManager.h index 94a616b0..5bd5d0a2 100644 --- a/client/src/managers/WebSocketManager.h +++ b/client/src/managers/WebSocketManager.h @@ -8,6 +8,7 @@ #include "Logger.h" // Protobuf includes + #include "UserRegisterToEvent.pb.h" #include "UserEvent.pb.h" #include "DeviceEvent.pb.h" @@ -20,18 +21,18 @@ #include "TeraMessage.pb.h" #include "TeraModuleMessage.pb.h" #include "DatabaseEvent.pb.h" - #include "google/protobuf/any.h" #include "google/protobuf/util/json_util.h" using namespace opentera::protobuf; + class WebSocketManager : public QObject { Q_OBJECT public: explicit WebSocketManager(QObject *parent = nullptr); - ~WebSocketManager(); + ~WebSocketManager() override; void connectWebSocket(QString &socketUrl, QString &user_uuid); void disconnectWebSocket(); @@ -57,7 +58,6 @@ class WebSocketManager : public QObject void serverDisconnected(); void websocketError(QAbstractSocket::SocketError, QString); void loginResult(bool logged_in); - void userEventReceived(UserEvent event); void participantEventReceived(ParticipantEvent event); void deviceEventReceived(DeviceEvent event); @@ -66,7 +66,6 @@ class WebSocketManager : public QObject void stopSessionEventReceived(StopSessionEvent event); void databaseEventReceived(DatabaseEvent event); void joinSessionReplyEventReceived(JoinSessionReplyEvent event); - void genericEventReceived(TeraEvent event); @@ -74,7 +73,10 @@ private slots: void onSocketConnected(); void onSocketDisconnected(); void onSocketError(QAbstractSocket::SocketError error); + +#ifndef OPENTERA_WEBASSEMBLY void onSocketSslErrors(const QList &errors); +#endif void onSocketTextMessageReceived(const QString &message); void onSocketBinaryMessageReceived(const QByteArray &message); diff --git a/client/src/services/DanceService/DanceComManager.cpp b/client/src/services/DanceService/DanceComManager.cpp index 56fe07e8..53075c29 100644 --- a/client/src/services/DanceService/DanceComManager.cpp +++ b/client/src/services/DanceService/DanceComManager.cpp @@ -77,7 +77,7 @@ bool DanceComManager::handleDataReply(const QString &reply_path, const QString & QList items; if (data_list.isArray()){ QJsonArray data_list_array = data_list.array(); - for (const QJsonValue &data:qAsConst(data_list_array)){ + for (const QJsonValue &data:std::as_const(data_list_array)){ items.append(data.toObject()); } }else{ diff --git a/client/src/services/DanceService/DanceConfigWidget.cpp b/client/src/services/DanceService/DanceConfigWidget.cpp index 5df89186..d20869e6 100644 --- a/client/src/services/DanceService/DanceConfigWidget.cpp +++ b/client/src/services/DanceService/DanceConfigWidget.cpp @@ -75,7 +75,7 @@ void DanceConfigWidget::on_btnUpload_clicked() void DanceConfigWidget::processVideosReply(QList videos) { - for (const QJsonObject &video:qAsConst(videos)){ + for (const QJsonObject &video:std::as_const(videos)){ updateVideoInLibrary(video); updateVideoInPlaylist(video); } @@ -107,7 +107,7 @@ void DanceConfigWidget::processPlaylistReply(QList playlists) m_playlistIds.clear(); checkDirty(); - for (const QJsonObject &playlist:qAsConst(playlists)){ + for (const QJsonObject &playlist:std::as_const(playlists)){ int id_video = playlist["id_video"].toInt(); m_playlistIds.append(id_video); // Find video in available list @@ -661,7 +661,7 @@ void DanceConfigWidget::on_btnCancel_clicked() ui->lstPlaylist->clearContents(); ui->lstPlaylist->setRowCount(0); - for (const int& id:qAsConst(m_playlistIds)){ + for (const int& id:std::as_const(m_playlistIds)){ // Find video in available for(int i=0; ilstAvailVideos->count(); i++){ if (ui->lstAvailVideos->item(i)->data(Qt::UserRole).toInt() == id){ diff --git a/client/src/services/VideoRehabService/VideoRehabSetupWidget.cpp b/client/src/services/VideoRehabService/VideoRehabSetupWidget.cpp index cc9fd6c9..b380e992 100644 --- a/client/src/services/VideoRehabService/VideoRehabSetupWidget.cpp +++ b/client/src/services/VideoRehabService/VideoRehabSetupWidget.cpp @@ -36,6 +36,7 @@ VideoRehabSetupWidget::~VideoRehabSetupWidget() m_virtualCamThread->wait(); m_virtualCamThread->deleteLater(); } + } QJsonDocument VideoRehabSetupWidget::getSetupConfig() @@ -46,6 +47,7 @@ QJsonDocument VideoRehabSetupWidget::getSetupConfig() void VideoRehabSetupWidget::initUI() { ui->frameError->hide(); + ui->widgetSetup->setComManager(m_comManager); //// Web engine setup m_webEngine = new QWebEngineView(ui->wdgWebEngine); @@ -75,8 +77,8 @@ void VideoRehabSetupWidget::initUI() m_webEngine->setSizePolicy(sizePolicy); ui->wdgWebEngine->layout()->addWidget(m_webEngine); - // Wait for service configuration before setting url - //m_webEngine->setUrl(QUrl("qrc:/VideoRehabService/html/index.html")); + // Load url - will set correct camera when service config form will be loaded + m_webEngine->setUrl(QUrl("qrc:/VideoRehabService/html/index.html")); // Hide PTZ fields in setup widget //ui->widgetSetup->hideFields(QStringList() << "camera_ptz_type" << "camera_ptz_ip" << "camera_ptz_port" << "camera_ptz_username" << "camera_ptz_password"); @@ -186,7 +188,6 @@ void VideoRehabSetupWidget::startPTZCamera() return; } - showError(tr("Caméra PTZ"), "VideoRehabSetupWidget::startPTZCamera", tr("Type de caméra PTZ non-supporté"), true); } @@ -211,8 +212,9 @@ void VideoRehabSetupWidget::refreshWebpageSettings() bool ptz = ui->widgetSetup->getFieldValue("camera_ptz").toBool(); m_webPage->getSharedObject()->setPTZCapabilities(ptz, ptz, ptz); m_webPage->getSharedObject()->sendPTZCapabilities(); - if (ptz) + if (ptz) { startPTZCamera(); + } // Update video source QString video_src = ui->widgetSetup->getFieldValue("camera").toString(); @@ -306,7 +308,7 @@ void VideoRehabSetupWidget::processServiceConfigsReply(QList configs, m_webPage->getSharedObject()->setCurrentCameraName(ui->widgetSetup->getFieldValue("camera").toString()); // Load page - m_webEngine->setUrl(QUrl("qrc:/VideoRehabService/html/index.html")); + //m_webEngine->setUrl(QUrl("qrc:/VideoRehabService/html/index.html")); } } @@ -403,8 +405,12 @@ void VideoRehabSetupWidget::setupFormValueChanged(QWidget *wdg, QVariant value) startVirtualCamera(src); } }else{ + /* if (m_virtualCamThread) + { stopVirtualCamera(); + } + */ } } @@ -445,7 +451,6 @@ void VideoRehabSetupWidget::ptzCameraError(CameraInfo infos) break; case CameraInfo::CIE_NO_ERROR: // Shouldn't get here, but managed in case - return; break; } diff --git a/client/src/services/VideoRehabService/VideoRehabSetupWidget.h b/client/src/services/VideoRehabService/VideoRehabSetupWidget.h index ef6abb34..98c7910b 100644 --- a/client/src/services/VideoRehabService/VideoRehabSetupWidget.h +++ b/client/src/services/VideoRehabService/VideoRehabSetupWidget.h @@ -7,8 +7,9 @@ #include #include -#include -#include +#include +#include +#include #include "services/BaseServiceSetupWidget.h" diff --git a/client/src/services/VideoRehabService/VideoRehabWebPage.cpp b/client/src/services/VideoRehabService/VideoRehabWebPage.cpp index 9ede835d..9dc06859 100644 --- a/client/src/services/VideoRehabService/VideoRehabWebPage.cpp +++ b/client/src/services/VideoRehabService/VideoRehabWebPage.cpp @@ -1,8 +1,18 @@ #include "VideoRehabWebPage.h" #include "Logger.h" +#include VideoRehabWebPage::VideoRehabWebPage(QObject *parent): QWebEnginePage(parent) { + //Set page Settings + auto settings = this->settings(); + settings->setAttribute(QWebEngineSettings::PluginsEnabled, true); + settings->setAttribute(QWebEngineSettings::JavascriptEnabled, true); + settings->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, true); + settings->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, true); + settings->setAttribute(QWebEngineSettings::PlaybackRequiresUserGesture, false); + + // Create shared object for communication with webpage m_sharedObject = new SharedObject(this); @@ -19,6 +29,7 @@ VideoRehabWebPage::VideoRehabWebPage(QObject *parent): QWebEnginePage(parent) // Connect signals connect(m_clientWrapper, &WebSocketClientWrapper::clientConnected, m_webChannel, &QWebChannel::connectTo); // Transport will be automatically connected connect(this, &VideoRehabWebPage::featurePermissionRequested, this, &VideoRehabWebPage::featurePermissionHandler); + connect(this,&QWebEnginePage::certificateError, this, &VideoRehabWebPage::onCertificateError); m_webChannel->registerObject(QStringLiteral("SharedObject"), m_sharedObject); } @@ -34,26 +45,22 @@ SharedObject *VideoRehabWebPage::getSharedObject() const return m_sharedObject; } -bool VideoRehabWebPage::certificateError(const QWebEngineCertificateError &certificateError) +void VideoRehabWebPage::onCertificateError(const QWebEngineCertificateError &certificateError) { -//#ifdef QT_DEBUG - - qDebug() << "Certificate error: " << certificateError.errorDescription(); - /* - The certificateError parameter contains information about the certificate and details of the error. - Return true to ignore the error and complete the request. Return false to stop loading the request. - */ - - return true; // Accept all certificates in debug -/*#else - // Refuse invalid certificates - return false; -#endif*/ + + //TODO do Something about certificates + qDebug() << "Certificate error: " << certificateError.description(); + + //TODO Do not accept certificates in production ? + auto mutableError = const_cast(certificateError); + mutableError.acceptCertificate(); + } void VideoRehabWebPage::featurePermissionHandler(const QUrl &securityOrigin, QWebEnginePage::Feature feature) { // TODO: Only allow specific features like webcam, micro, screenshare? + qDebug() << securityOrigin << " requesting: " << feature; //Grant all features! this->setFeaturePermission(securityOrigin,feature,QWebEnginePage::PermissionGrantedByUser); diff --git a/client/src/services/VideoRehabService/VideoRehabWebPage.h b/client/src/services/VideoRehabService/VideoRehabWebPage.h index 6454401f..d36c5224 100644 --- a/client/src/services/VideoRehabService/VideoRehabWebPage.h +++ b/client/src/services/VideoRehabService/VideoRehabWebPage.h @@ -16,15 +16,16 @@ class VideoRehabWebPage : public QWebEnginePage Q_OBJECT public: VideoRehabWebPage(QObject *parent = nullptr); - ~VideoRehabWebPage(); + ~VideoRehabWebPage() override; SharedObject* getSharedObject() const; protected: - virtual bool certificateError(const QWebEngineCertificateError &certificateError) override; + private slots: void featurePermissionHandler(const QUrl &securityOrigin, QWebEnginePage::Feature feature); + void onCertificateError(const QWebEngineCertificateError &certificateError); private: SharedObject *m_sharedObject; // Shared object for communication with page itself diff --git a/client/src/services/VideoRehabService/VideoRehabWidget.cpp b/client/src/services/VideoRehabService/VideoRehabWidget.cpp index 537532e5..94fe7767 100644 --- a/client/src/services/VideoRehabService/VideoRehabWidget.cpp +++ b/client/src/services/VideoRehabService/VideoRehabWidget.cpp @@ -19,8 +19,6 @@ VideoRehabWidget::VideoRehabWidget(ComManager *comMan, QWidget *parent) : connectSignals(); emit widgetIsReady(false); // We wait until webpage is fully loaded... - - } VideoRehabWidget::~VideoRehabWidget() @@ -29,6 +27,7 @@ VideoRehabWidget::~VideoRehabWidget() m_loadingIcon->deleteLater(); m_webPage->deleteLater(); m_webEngine->deleteLater(); + if (m_virtualCamThread){ m_virtualCamThread->quit(); m_virtualCamThread->wait(); @@ -47,20 +46,16 @@ void VideoRehabWidget::initUI() // Set and start loading ui->frameError->hide(); setLoading(true); - ui->wdgWebEngine->hide(); m_loadingIcon = new QMovie("://status/calling.gif"); ui->icoLoading->setMovie(m_loadingIcon); m_loadingIcon->start(); - QWebEngineSettings::defaultSettings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, true); - //QWebEngineSettings::defaultSettings()->setAttribute(QWebEngineSettings::PluginsEnabled, true); - QWebEngineSettings::defaultSettings()->setAttribute(QWebEngineSettings::PlaybackRequiresUserGesture, false); - - m_webEngine = new QWebEngineView(ui->wdgWebEngine); + m_webEngine = new QWebEngineView(/*ui->wdgWebEngine*/); connect(m_webEngine, &QWebEngineView::loadFinished, this, &VideoRehabWidget::webPageLoaded); // Create a new page + // DL new Settings are set in the page constructor m_webPage = new VideoRehabWebPage(m_webEngine); connect(m_webPage->getSharedObject(), &SharedObject::pageIsReady, this, &VideoRehabWidget::webPageReady); connect(m_webPage->getSharedObject(), &SharedObject::videoErrorOccured, this, &VideoRehabWidget::webPageVideoError); @@ -77,8 +72,8 @@ void VideoRehabWidget::initUI() //Set page to view m_webEngine->setPage(m_webPage); - QWebEngineProfile::defaultProfile()->setPersistentCookiesPolicy(/*QWebEngineProfile::AllowPersistentCookies*/QWebEngineProfile::NoPersistentCookies); - QWebEngineProfile::defaultProfile()->setHttpCacheType(/*QWebEngineProfile::DiskHttpCache*/QWebEngineProfile::NoCache); + QWebEngineProfile::defaultProfile()->setPersistentCookiesPolicy(QWebEngineProfile::NoPersistentCookies); + QWebEngineProfile::defaultProfile()->setHttpCacheType(QWebEngineProfile::NoCache); // Set download path setDataSavePath(); @@ -90,9 +85,10 @@ void VideoRehabWidget::initUI() layout->setContentsMargins(0,0,0,0); ui->wdgWebEngine->setLayout(layout); } - QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); m_webEngine->setSizePolicy(sizePolicy); ui->wdgWebEngine->layout()->addWidget(m_webEngine); + } bool VideoRehabWidget::handleJoinSessionEvent(const JoinSessionEvent &event) @@ -177,7 +173,7 @@ void VideoRehabWidget::webEngineURLChanged(QUrl url) ui->txtURL->setText(url.toString()); } -void VideoRehabWidget::webEngineDownloadRequested(QWebEngineDownloadItem *item) +void VideoRehabWidget::webEngineDownloadRequested(QWebEngineDownloadRequest *item) { //qDebug() << "WebEngine: about to download " << item->suggestedFileName(); emit fileDownloading(true); @@ -210,7 +206,7 @@ void VideoRehabWidget::webEngineDownloadRequested(QWebEngineDownloadItem *item) item->setDownloadFileName(file_name); } - connect(item, &QWebEngineDownloadItem::finished, this, &VideoRehabWidget::webEngineDownloadCompleted); + connect(item, &QWebEngineDownloadRequest::isFinishedChanged, this, &VideoRehabWidget::webEngineDownloadCompleted); item->accept(); } @@ -220,7 +216,7 @@ void VideoRehabWidget::webEngineDownloadCompleted() // Enable buttons emit fileDownloading(false); - QWebEngineDownloadItem* item = dynamic_cast(sender()); + QWebEngineDownloadRequest* item = dynamic_cast(sender()); if (item){ if (item->receivedBytes() == 0) return; @@ -357,7 +353,13 @@ void VideoRehabWidget::setLoading(const bool &loading) { ui->frameLoading->setVisible(loading); if (!ui->frameError->isVisible()){ - ui->wdgWebEngine->setVisible(!loading); + // SB Qt6 WebEngine seems to force refresh of the whole window when showing / hiding once in a while. + // So instead of fully hiding, we just set its maximum height. + if (loading) + ui->wdgWebEngine->setMaximumHeight(0); + else + ui->wdgWebEngine->setMaximumHeight(65535); + //ui->wdgWebEngine->setVisible(!loading); } } @@ -383,11 +385,12 @@ void VideoRehabWidget::startVirtualCamera(const QString &src) ui->frameError->hide(); m_virtualCamThread = new VirtualCameraThread(src); connect(m_virtualCamThread, &VirtualCameraThread::virtualCamDisconnected, this, &VideoRehabWidget::virtualCameraDisconnected); - m_virtualCamThread->start(); + m_virtualCamThread->start(); } void VideoRehabWidget::stopVirtualCamera() { + qDebug() << "VideoRehabWidget::stopVirtualCamera"; if (m_virtualCamThread){ m_virtualCamThread->quit(); @@ -397,6 +400,16 @@ void VideoRehabWidget::stopVirtualCamera() } } +void VideoRehabWidget::resizeEvent(QResizeEvent *event) +{ + // Webpage will set its content size to its initial size, thus webengine will stay at that size. + // This resizes the view to force a resize of the underlying page. + // Not perfect as multiple calls could be performed, but working for now. + ui->wdgWebEngine->setMaximumWidth(event->size().width()-50); + + //BaseServiceWidget::resizeEvent(event); +} + void VideoRehabWidget::on_btnRefresh_clicked() { reload(); diff --git a/client/src/services/VideoRehabService/VideoRehabWidget.h b/client/src/services/VideoRehabService/VideoRehabWidget.h index 18fd83fe..a7e86aa2 100644 --- a/client/src/services/VideoRehabService/VideoRehabWidget.h +++ b/client/src/services/VideoRehabService/VideoRehabWidget.h @@ -41,7 +41,7 @@ class VideoRehabWidget : public BaseServiceWidget private slots: void on_txtURL_returnPressed(); void webEngineURLChanged(QUrl url); - void webEngineDownloadRequested(QWebEngineDownloadItem* item); + void webEngineDownloadRequested(QWebEngineDownloadRequest* item); void webEngineDownloadCompleted(); void webPageLoaded(bool ok); @@ -75,6 +75,10 @@ private slots: signals: void fileDownloading(bool downloading); + + // QWidget interface + protected: + void resizeEvent(QResizeEvent *event) override; }; #endif // VIDEOREHABWIDGET_H diff --git a/client/src/services/VideoRehabService/VideoRehabWidget.ui b/client/src/services/VideoRehabService/VideoRehabWidget.ui index 79ac5ea3..4b4d58ac 100644 --- a/client/src/services/VideoRehabService/VideoRehabWidget.ui +++ b/client/src/services/VideoRehabService/VideoRehabWidget.ui @@ -10,6 +10,12 @@ 427 + + + 0 + 0 + + Form diff --git a/client/src/widgets/AssetsWidget.cpp b/client/src/widgets/AssetsWidget.cpp index 0ffe2350..df0c5f68 100644 --- a/client/src/widgets/AssetsWidget.cpp +++ b/client/src/widgets/AssetsWidget.cpp @@ -214,7 +214,7 @@ void AssetsWidget::queryAssetsInfos() QMultiMap services_assets_tokens; // Group each assets by access url - for(TeraData* asset: qAsConst(m_assets)){ + for(TeraData* asset: std::as_const(m_assets)){ if (asset->hasFieldName("asset_infos_url")){ QString asset_info_str = asset->getFieldValue("asset_infos_url").toString(); if (!asset_info_str.isEmpty()){ @@ -227,7 +227,7 @@ void AssetsWidget::queryAssetsInfos() // Query each service for their assets QStringList service_urls = services_assets.uniqueKeys(); - for(const QString &service_url: qAsConst(service_urls)){ + for(const QString &service_url: std::as_const(service_urls)){ QStringList asset_uuids = services_assets.values(service_url); QStringList asset_tokens = services_assets_tokens.values(service_url); QJsonDocument document; @@ -677,7 +677,7 @@ void AssetsWidget::processServicesReply(QList services, QUrlQuery repl // Check if project is in service_fields if (service.hasFieldName("service_projects")){ QVariantList projects = service.getFieldValue("service_projects").toList(); - for (const QVariant &project:qAsConst(projects)){ + for (const QVariant &project:std::as_const(projects)){ QVariantMap project_info = project.toMap(); if (project_info["id_project"].toInt() == m_idProject){ // Ok, we are allowed to use file transfer service @@ -837,7 +837,7 @@ void AssetsWidget::assetComPostOK(QString path) void AssetsWidget::processAssetsInfos(QList infos, QUrlQuery reply_query, QString reply_path) { - for (const QJsonObject &asset_info:qAsConst(infos)){ + for (const QJsonObject &asset_info:std::as_const(infos)){ QString asset_uuid = asset_info["asset_uuid"].toString(); if (!m_treeAssets.contains(asset_uuid)){ @@ -1011,7 +1011,7 @@ void AssetsWidget::on_btnDownloadAll_clicked() QStringList assets_to_download; // Download all assets - for(TeraData* asset:qAsConst(m_assets)){ + for(TeraData* asset:std::as_const(m_assets)){ assets_to_download.append(asset->getUuid()); } @@ -1029,7 +1029,7 @@ void AssetsWidget::on_treeAssets_itemExpanded(QTreeWidgetItem *item) setLoading(true, tr("Affichage des données en cours...")); QCoreApplication::processEvents(); QStringList assets_uuids = m_assetsSessions.values(id_session); - for (const QString &asset_uuid: qAsConst(assets_uuids)){ + for (const QString &asset_uuid: std::as_const(assets_uuids)){ if (m_assets.contains(asset_uuid)){ updateAsset(*m_assets[asset_uuid]); } @@ -1049,7 +1049,7 @@ void AssetsWidget::on_treeAssets_itemCollapsed(QTreeWidgetItem *item) if (id_session != -1){ // Remove all assets from display QList assets_items = item->takeChildren(); - for(QTreeWidgetItem* asset_item: qAsConst(assets_items)){ + for(QTreeWidgetItem* asset_item: std::as_const(assets_items)){ QString asset_uuid = m_treeAssets.key(asset_item); m_treeAssets.remove(asset_uuid); delete asset_item; diff --git a/client/src/widgets/ConfigWidget.cpp b/client/src/widgets/ConfigWidget.cpp index 5f696245..7f081775 100644 --- a/client/src/widgets/ConfigWidget.cpp +++ b/client/src/widgets/ConfigWidget.cpp @@ -46,13 +46,14 @@ void ConfigWidget::addSection(const QString &name, const QIcon &icon, const int tmp->setText(name); tmp->setTextAlignment(Qt::AlignCenter); tmp->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - //tmp->setSizeHint(QSize(ui->lstSections->width(), 64)); tmp->setToolTip(name); + //tmp->setSizeHint(ui->lstSections->gridSize()); tmp->setData(Qt::UserRole, id); } void ConfigWidget::setupSections() { + ui->lstSections->setGridSize(QSize(ui->lstSections->width()-20, ui->lstSections->gridSize().height())); addSection(tr("Utilisateurs"), QIcon(TeraData::getIconFilenameForDataType(TERADATA_USER)), TERADATA_USER); addSection(tr("Groupes utilisateurs"), QIcon(TeraData::getIconFilenameForDataType(TERADATA_USERGROUP)), TERADATA_USERGROUP); addSection(tr("Sites"), QIcon("://icons/site-icon.png"), TERADATA_SITE); diff --git a/client/src/widgets/ConfigWidget.ui b/client/src/widgets/ConfigWidget.ui index da237cff..9a22a708 100644 --- a/client/src/widgets/ConfigWidget.ui +++ b/client/src/widgets/ConfigWidget.ui @@ -14,104 +14,7 @@ Form - /* -QPushButton#btnConnect{background-color:rgb(80,180,80)} -QWidget{background-color: rgba(0,0,0,0);color:white;border-radius:5px} -QWidget#mainWidget{background-color:rgba(128,128,128,25%);} -QWidget#frmProfile,QWidget#pageProfile{background-color:rgba(108,8,156,25%);} -QPlainTextEdit,QLineEdit,QListWidget,QComboBox,QTextEdit,QSpinBox{background-color: rgba(255,255,255,50%); color: black;} - -QTableWidget{ -background-color: rgba(0,0,0,50%); -color: white; -border: 1px solid rgba(255,255,255,50%); -} - -QLabel#lblLastOnline{color:lightblue;} -QLabel#lblError,QLabel#lblCamMissing,QLabel#lblAudioMissing{color:red; background-color:rgba(255,0,0,30%);} - -QLineEdit:!enabled, QListWidget:!enabled,QComboBox:!enabled,QTextEdit:!enabled,QPlainTextEdit:!enabled{background-color: rgba(0,0,0,30%);color:white;border:0} -QDateEdit::down-arrow:!enabled,QTimeEdit::down-arrow:!enabled,QDateEdit::up-arrow:!enabled,QTimeEdit::up-arrow:!enabled,QComboBox::drop-down:!enabled{background-color:rgba(0,0,0,0%);} - -QTextEdit#txtProfile{background-color: rgba(255,255,255,80%);color:black;border:0} -QTextEdit:!enabled#txtProfile{background-color: rgba(255,255,255,60%);color:black;border:0} - -QFrame#frmSelUser{background-color:rgba(100,100,200,50%);} -QFrame#frmAudio,QFrame#frmCam1,QFrame#frmCam2,QFrame#frmSensors,QFrame#frmWebRTC,QFrame#frmVirtualCam,QFrame#frmExternalPrograms{background-color:rgba(100,100,200,35%);} - -QCheckBox::indicator:unchecked:!enabled { background-color:rgba(128,0,0,100%);} -QCheckBox::indicator:checked:!enabled { background-color:green;} -QCheckBox::indicator:unchecked{ background-color:red;border-radius:3px;} -QCheckBox::indicator:checked {background-color:rgb(0,255,0);border-radius:3px;} -QCheckBox:checked{color:lightgreen;background-color:rgba(0,0,0,0%);} -QCheckBox:!checked{color:red;background-color:rgba(0,0,0,0%);} - -QPushButton{background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #2198c0, stop: 1 #0d5ca6);} -QPushButton:checked{background-color:rgb(128,128,128); border:1px solid rgba(255,255,255,50%);} -QPushButton:hover,QPushButton:checked{background-color: rgba(255,255,255,75%);color:black;} -QPushButton#btnEditUser:hover{background-color: rgba(255,255,255,75%);color:black;} -QPushButton#btnEdit,QPushButton#btnDelete,QPushButton#btnExisting{background-color: rgba(255,255,255,0%);border:0 px;} -QPushButton#btnEdit:hover,QPushButton#btnDelete:hover,QPushButton#btnExisting:hover{background-color: rgba(255,255,255,25%);border:0 px;} -QPushButton#btnExisting:checked{background-color:rgba(0,100,200,50%);} -QPushButton#btnUpgrade{background-color:rgba(200,0,0,90%);} - -QToolButton:hover{background-color: rgba(255,255,255,50);} - -QTreeWidget::branch:closed:has-children:has-siblings { - border-image: none; - image: url(:/pictures/controls/branch_closed.png); - } - - QTreeView::branch:open:has-children:!has-siblings, - QTreeView::branch:open:has-children:has-siblings { - border-image: none; - image:url(:/pictures/controls/branch_opened.png); - } - -QWidget#lineOnline{background-color:blue;} -QWidget#lineOffline{background-color:red;} -QWidget#lineBoth{background-color:rgb(255,0,255);} - - -QTabWidget::pane { - background-color: rgba(128,128,140,25%); - border-radius: 5px; - border: 2px solid rgba(128,128,140,50%); - } - - QTabWidget::tab-bar { - left: 5px; / - } - - - QTabBar::tab,QTableWidget::tab,QHeaderView::section { - background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 rgba(128,128,140,50%), stop: 1.0 rgba(128,128,140,25%)); - border: 2px solid rgba(128,128,140,50%); - border-bottom: 0px; - border-top-left-radius: 4px; - border-top-right-radius: 4px; - padding: 2px; -min-height:25px; - } - - QTabBar::tab:selected, QTabBar::tab:hover { - -background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 rgba(128,128,140,70%), stop: 1.0 rgba(128,128,140,50%)); - } - - QTabBar::tab:selected { - border-color:rgba(128,128,140,60%); - border-bottom-color:rgba(128,128,140,60%); - } - - QTabBar::tab:!selected { - margin-top: 2px; - } -*/ - - + @@ -195,6 +98,9 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, true + + QListView::Batched + 100 @@ -205,13 +111,13 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, QListView::IconMode - false + true true - true + false Qt::AlignCenter diff --git a/client/src/widgets/DashboardWidget.cpp b/client/src/widgets/DashboardWidget.cpp index dd367121..737fcf4f 100644 --- a/client/src/widgets/DashboardWidget.cpp +++ b/client/src/widgets/DashboardWidget.cpp @@ -7,8 +7,9 @@ DashboardWidget::DashboardWidget(ComManager *comMan, int id_site, QWidget *paren m_comManager(comMan) { ui->setupUi(this); - +#ifndef OPENTERA_WEBASSEMBLY m_sessionLobby = nullptr; +#endif m_cleanupDiag = nullptr; ui->tableUpcomingSessions->hide(); ui->tableRecentParticipants->hide(); @@ -28,10 +29,15 @@ DashboardWidget::~DashboardWidget() qDeleteAll(m_sessions); qDeleteAll(m_session_types); - if (m_cleanupDiag) + if (m_cleanupDiag) { m_cleanupDiag->deleteLater(); - if (m_sessionLobby) + } + +#ifndef OPENTERA_WEBASSEMBLY + if (m_sessionLobby) { m_sessionLobby->deleteLater(); + } +#endif delete ui; } @@ -152,7 +158,7 @@ void DashboardWidget::processSessionsReply(const QList sessions) // Update values name_item->setText(session.getName()); QDateTime session_date = session.getFieldValue("session_start_datetime").toDateTime().toLocalTime(); - date_item->setText(session_date.toString("dd-MM-yyyy hh:mm:ss")); + date_item->setDate(session_date); // Type int id_session_type = session.getFieldValue("id_session_type").toInt(); @@ -180,7 +186,7 @@ void DashboardWidget::processSessionsReply(const QList sessions) QString project_name; if (session.hasFieldName("session_participants")){ QVariantList participants = session.getFieldValue("session_participants").toList(); - for(const QVariant &participant: qAsConst(participants)){ + for(const QVariant &participant: std::as_const(participants)){ QVariantHash part_data = participant.toHash(); if (project_name.isEmpty() && part_data.contains("project_name")){ @@ -195,14 +201,14 @@ void DashboardWidget::processSessionsReply(const QList sessions) } if (session.hasFieldName("session_users")){ QVariantList users = session.getFieldValue("session_users").toList(); - for(const QVariant &user: qAsConst(users)){ + for(const QVariant &user: std::as_const(users)){ QVariantHash user_data = user.toHash(); invitees.append(user_data["user_name"].toString()); } } if (session.hasFieldName("session_devices")){ QVariantList devices = session.getFieldValue("session_devices").toList(); - for(const QVariant &device: qAsConst(devices)){ + for(const QVariant &device: std::as_const(devices)){ QVariantHash device_data = device.toHash(); invitees.append(device_data["device_name"].toString()); } @@ -253,6 +259,9 @@ void DashboardWidget::processSessionTypesReply(const QList session_typ // Refresh data refreshData(); + + // Disconnect signal since we don't need it anymore + disconnect(m_comManager, &ComManager::sessionTypesReceived, this, &DashboardWidget::processSessionTypesReply); } void DashboardWidget::processParticipantsReply(const QList participants, const QUrlQuery reply_query) @@ -346,6 +355,7 @@ void DashboardWidget::processStatsReply(const TeraData stats, const QUrlQuery re ui->treeWarnings->clear(); int warnings = 0; int total_warnings = 0; + ui->treeWarnings->setColumnWidth(0, 48); if (stats.hasFieldName("warning_participants_count")){ warnings = stats.getFieldValue("warning_participants_count").toInt(); if (warnings > 0){ @@ -357,11 +367,11 @@ void DashboardWidget::processStatsReply(const TeraData stats, const QUrlQuery re QToolButton* manage_btn = createManageWarningButton(); manage_btn->setProperty("data", stats.getFieldValue("warning_participants")); manage_btn->setProperty("data_type", TERADATA_PARTICIPANT); - ui->treeWarnings->setItemWidget(item, 0, manage_btn); + ui->treeWarnings->setItemWidget(item, 0, manage_btn); // Participants details QVariantList participants = stats.getFieldValue("warning_participants").toList(); - for (const QVariant &part: qAsConst(participants)){ + for (const QVariant &part: std::as_const(participants)){ QVariantHash part_data = part.toHash(); QString data_str; data_str = part_data["participant_name"].toString(); @@ -393,7 +403,7 @@ void DashboardWidget::processStatsReply(const TeraData stats, const QUrlQuery re // Participants details QVariantList participants = stats.getFieldValue("warning_nosession_participants").toList(); - for (const QVariant &part: qAsConst(participants)){ + for (const QVariant &part: std::as_const(participants)){ QVariantHash part_data = part.toHash(); QString data_str; data_str = part_data["participant_name"].toString(); @@ -426,7 +436,7 @@ void DashboardWidget::processStatsReply(const TeraData stats, const QUrlQuery re // Users details QVariantList users = stats.getFieldValue("warning_users").toList(); - for (const QVariant &user: qAsConst(users)){ + for (const QVariant &user: std::as_const(users)){ QVariantHash user_data = user.toHash(); QString data_str; data_str = user_data["user_fullname"].toString(); @@ -455,7 +465,7 @@ void DashboardWidget::processStatsReply(const TeraData stats, const QUrlQuery re // Users details QVariantList users = stats.getFieldValue("warning_neverlogged_users").toList(); - for (const QVariant &user: qAsConst(users)){ + for (const QVariant &user: std::as_const(users)){ QVariantHash user_data = user.toHash(); QString data_str; data_str = user_data["user_fullname"].toString(); @@ -486,7 +496,7 @@ void DashboardWidget::processStatsReply(const TeraData stats, const QUrlQuery re ui->btnAttention->setText(tr("Attention requise") + " (" + QString::number(total_warnings) + ")"); } - +#ifndef OPENTERA_WEBASSEMBLY void DashboardWidget::sessionLobbyStartSessionCancelled() { if (m_sessionLobby){ @@ -509,6 +519,7 @@ void DashboardWidget::sessionLobbyStartSessionRequested() m_sessionLobby->deleteLater(); m_sessionLobby = nullptr; } +#endif void DashboardWidget::cleanupDialogCompleted() { @@ -567,6 +578,7 @@ void DashboardWidget::updateUiSpacing() } } +#ifndef OPENTERA_WEBASSEMBLY void DashboardWidget::showSessionLobby(const int &id_session_type, const int &id_session) { if (m_sessionLobby) @@ -598,7 +610,7 @@ void DashboardWidget::showSessionLobby(const int &id_session_type, const int &id QList ids; if (session->hasFieldName("session_participants")){ QVariantList session_participants = session->getFieldValue("session_participants").toList(); - for(const QVariant &participant: qAsConst(session_participants)){ + for(const QVariant &participant: std::as_const(session_participants)){ TeraData part_data(TERADATA_PARTICIPANT, participant.toJsonValue()); participants.append(part_data); ids.append(part_data.getId()); @@ -612,7 +624,7 @@ void DashboardWidget::showSessionLobby(const int &id_session_type, const int &id QList users; if (session->hasFieldName("session_users")){ QVariantList session_users = session->getFieldValue("session_users").toList(); - for(const QVariant &user: qAsConst(session_users)){ + for(const QVariant &user: std::as_const(session_users)){ TeraData user_data(TERADATA_USER, user.toJsonValue()); users.append(user_data); ids.append(user_data.getId()); @@ -626,7 +638,7 @@ void DashboardWidget::showSessionLobby(const int &id_session_type, const int &id QList devices; if (session->hasFieldName("session_devices")){ QVariantList session_devices = session->getFieldValue("session_devices").toList(); - for(const QVariant &device: qAsConst(session_devices)){ + for(const QVariant &device: std::as_const(session_devices)){ TeraData device_data(TERADATA_DEVICE, device.toJsonValue()); devices.append(device_data); ids.append(device_data.getId()); @@ -644,13 +656,15 @@ void DashboardWidget::showSessionLobby(const int &id_session_type, const int &id // Show Session Lobby m_sessionLobby->exec(); } +#endif QToolButton *DashboardWidget::createManageWarningButton() { QToolButton* manage_btn = new QToolButton(); manage_btn->setIcon(QIcon("://icons/config.png")); manage_btn->setIconSize(QSize(20,20)); - manage_btn->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); + manage_btn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + manage_btn->setFixedSize(28, 28); manage_btn->setToolTip(tr("Gérer")); manage_btn->setCursor(Qt::PointingHandCursor); manage_btn->setStyleSheet("QToolButton::hover{background-color:transparent;}"); @@ -663,13 +677,14 @@ QToolButton *DashboardWidget::createManageWarningButton() void DashboardWidget::on_tableUpcomingSessions_itemDoubleClicked(QTableWidgetItem *item) { +#ifndef OPENTERA_WEBASSEMBLY int current_row = item->row(); QTableWidgetItem* base_item = ui->tableUpcomingSessions->item(current_row, 1); - if (m_listSessions_items.contains(base_item)){ int id_session = m_listSessions_items.value(base_item); showSessionLobby(m_sessions[id_session]->getFieldValue("id_session_type").toInt(), id_session); } +#endif } diff --git a/client/src/widgets/DashboardWidget.h b/client/src/widgets/DashboardWidget.h index 5d01c535..72f23fd4 100644 --- a/client/src/widgets/DashboardWidget.h +++ b/client/src/widgets/DashboardWidget.h @@ -11,7 +11,10 @@ #include "widgets/TableDateWidgetItem.h" +#ifndef OPENTERA_WEBASSEMBLY #include "dialogs/SessionLobbyDialog.h" +#endif + #include "dialogs/CleanUpDialog.h" #include "GlobalMessageBox.h" @@ -43,9 +46,10 @@ private slots: void processSessionTypesReply(const QList session_types); void processParticipantsReply(const QList participants, const QUrlQuery reply_query); void processStatsReply(const TeraData stats, const QUrlQuery reply_query); - +#ifndef OPENTERA_WEBASSEMBLY void sessionLobbyStartSessionCancelled(); void sessionLobbyStartSessionRequested(); +#endif void cleanupDialogCompleted(); void on_tableUpcomingSessions_itemDoubleClicked(QTableWidgetItem *item); @@ -62,7 +66,9 @@ private slots: ComManager* m_comManager; int m_siteId; +#ifndef OPENTERA_WEBASSEMBLY SessionLobbyDialog* m_sessionLobby; +#endif CleanUpDialog* m_cleanupDiag; QHash m_listSessions_items; @@ -76,9 +82,9 @@ private slots: void refreshData(); void updateUiSpacing(); - +#ifndef OPENTERA_WEBASSEMBLY void showSessionLobby(const int &id_session_type, const int &id_session); - +#endif QToolButton *createManageWarningButton(); }; diff --git a/client/src/widgets/DashboardWidget.ui b/client/src/widgets/DashboardWidget.ui index 2881725c..47cbc671 100644 --- a/client/src/widgets/DashboardWidget.ui +++ b/client/src/widgets/DashboardWidget.ui @@ -46,7 +46,7 @@ text-align:left; 0 - -14 + -528 704 948 @@ -120,7 +120,6 @@ text-align:left; 9 - 75 true @@ -195,7 +194,6 @@ text-align:left; 9 - 75 true @@ -212,6 +210,9 @@ text-align:left; + + Qt::NoFocus + QAbstractItemView::NoEditTriggers @@ -331,7 +332,6 @@ text-align:left; 9 - 75 true @@ -416,6 +416,9 @@ text-align:left; + + Qt::NoFocus + QAbstractItemView::NoEditTriggers @@ -514,7 +517,6 @@ text-align:left; 9 - 75 true @@ -587,6 +589,9 @@ text-align:left; + + Qt::NoFocus + QAbstractItemView::NoEditTriggers @@ -656,6 +661,10 @@ text-align:left; ClickableLabel QLabel
widgets/ClickableLabel.h
+ + clicked() + clicked() + diff --git a/client/src/widgets/HistoryCalendarWidget.cpp b/client/src/widgets/HistoryCalendarWidget.cpp index a3d61891..090c223e 100644 --- a/client/src/widgets/HistoryCalendarWidget.cpp +++ b/client/src/widgets/HistoryCalendarWidget.cpp @@ -14,7 +14,7 @@ HistoryCalendarWidget::~HistoryCalendarWidget(){ qDeleteAll(m_ids_session_types); } -void HistoryCalendarWidget::paintCell(QPainter *painter, const QRect &rect, const QDate &date) const{ +void HistoryCalendarWidget::paintCell(QPainter *painter, const QRect &rect, QDate date) const{ QPen pen; QBrush brush; brush.setColor(Qt::transparent); @@ -37,16 +37,27 @@ void HistoryCalendarWidget::paintCell(QPainter *painter, const QRect &rect, cons //return; }*/ - if (date==QDate::currentDate()){ - pen.setColor(Qt::cyan); + QFont painter_font = painter->font(); + if (date==QDate::currentDate() && date <= maximumDate()){ + pen.setColor(Qt::red); + painter_font.setBold(true); painter->setPen(pen); painter->drawText(rect, Qt::AlignCenter, QString::number(date.day())); + }else{ + painter_font.setBold(false); } + painter->setFont(painter_font); // Check for sessions on that date - if (!m_sessions.contains(date) || date.month() != monthShown()){ + if (!m_sessions.contains(date) || date.month() != monthShown() || date > maximumDate() || date < minimumDate()){ // Paint with default format - QCalendarWidget::paintCell (painter, rect, date); + if (date >= minimumDate() && date <= maximumDate()){ + pen.setColor(Qt::gray); + painter->setPen(pen); + painter->drawText(rect, Qt::AlignCenter, QString::number(date.day())); + }/*else{ + QCalendarWidget::paintCell(painter, rect, date); + }*/ return; } // Check to be sure that we don't have all filtered out sessions types for that date @@ -66,11 +77,13 @@ void HistoryCalendarWidget::paintCell(QPainter *painter, const QRect &rect, cons LOG_WARNING("No session type match - ignoring.", "HistoryCalendarWidget::paintCell"); } } - int count = 0; int total = display_colors.count(); //m_sessions->at(m_dates.value(date))->sessionsTypesCount(*m_displayTypes); if (total==0){ // All session types filtered for today, paint with default - QCalendarWidget::paintCell (painter, rect, date); + pen.setColor(Qt::gray); + painter->setPen(pen); + painter->drawText(rect, Qt::AlignCenter, QString::number(date.day())); + return; } @@ -105,7 +118,8 @@ void HistoryCalendarWidget::paintCell(QPainter *painter, const QRect &rect, cons //qDebug() << date.toString() << ": Count = " << QString::number(count) << ", Total = " << QString::number(total); //for (int i=0; idrawRect(rect.adjusted(2, static_cast(ratio*rect.height()+2), static_cast(-2*static_cast(rect.width())/3-2), - static_cast(rect.height()/static_cast(total)-2))); + -2 //static_cast(rect.height()/static_cast(total)-2) + ) + ); count++; } - // Check if we need to display any indicator warning for that session - /*for (int i=0; icount(); i++){ - if (sessions->at(i)->hasTechAlert()){ - painter->drawImage(rect.adjusted((float)rect.width()/2,2,-2,-(float)rect.height()/2),QImage(":/pictures/icons/warning.png")); - } - }*/ - pen.setColor(Qt::black); painter->setPen(pen); painter->drawText(rect, Qt::AlignCenter, QString::number(date.day())); diff --git a/client/src/widgets/HistoryCalendarWidget.h b/client/src/widgets/HistoryCalendarWidget.h index 50a318bb..f03b0dfa 100644 --- a/client/src/widgets/HistoryCalendarWidget.h +++ b/client/src/widgets/HistoryCalendarWidget.h @@ -15,7 +15,7 @@ class HistoryCalendarWidget : public QCalendarWidget explicit HistoryCalendarWidget(QWidget *parent=nullptr); ~HistoryCalendarWidget(); - void paintCell(QPainter *painter, const QRect &rect, const QDate &date) const; + void paintCell(QPainter *painter, const QRect &rect, QDate date) const override; //void setEvents1(QList* events); //void setEvents2(QList* events); diff --git a/client/src/widgets/InSessionWidget.cpp b/client/src/widgets/InSessionWidget.cpp index 485bca32..047e5179 100644 --- a/client/src/widgets/InSessionWidget.cpp +++ b/client/src/widgets/InSessionWidget.cpp @@ -260,8 +260,13 @@ void InSessionWidget::on_btnEndSession_clicked() void InSessionWidget::on_btnInSessionInfos_toggled(bool checked) { + int mainWidth = ui->widgetMain->width(); ui->tabInfos->setVisible(checked); - + if (checked){ + ui->widgetMain->setMaximumWidth(mainWidth - ui->tabInfos->width()); + }else{ + ui->widgetMain->setMaximumWidth(mainWidth + ui->tabInfos->width()); + } } void InSessionWidget::processSessionsReply(QList sessions) @@ -282,7 +287,7 @@ void InSessionWidget::processSessionsReply(QList sessions) /*if (session.hasFieldName("session_participants")){ item_list = session.getFieldValue("session_participants").toList(); - for(const QVariant &session_part:qAsConst(item_list)){ + for(const QVariant &session_part:std::as_const(item_list)){ QVariantMap part_info = session_part.toMap(); ui->wdgInvitees->addRequiredParticipant(part_info["id_participant"].toInt()); } @@ -291,7 +296,7 @@ void InSessionWidget::processSessionsReply(QList sessions) if (session.hasFieldName("session_users")){ item_list = session.getFieldValue("session_users").toList(); - for(const QVariant &session_user:qAsConst(item_list)){ + for(const QVariant &session_user:std::as_const(item_list)){ QVariantMap user_info = session_user.toMap(); int id_user = user_info["id_user"].toInt(); if (id_user == m_comManager->getCurrentUser().getId()) @@ -302,7 +307,7 @@ void InSessionWidget::processSessionsReply(QList sessions) /*if (session.hasFieldName("session_devices")){ item_list = session.getFieldValue("session_devices").toList(); - for(const QVariant &session_device:qAsConst(item_list)){ + for(const QVariant &session_device:std::as_const(item_list)){ QVariantMap device_info = session_device.toMap(); ui->wdgInvitees->addRequiredDevice(device_info["id_device"].toInt()); } @@ -367,6 +372,17 @@ void InSessionWidget::ws_JoinSessionEvent(JoinSessionEvent event) // Forward to widget if (m_serviceWidget){ + // SB Qt6 WebEngineView seems to cause issue when loading / displaying... Bug? + // This patch this behaviour and ensures that the session always starts in maximized mode + QWidget* parent = parentWidget(); + while(parent){ + parent = parent->parentWidget(); + if (QString::fromStdString(parent->metaObject()->className()) == "MainWindow"){ + parent->showNormal(); + parent->showMaximized(); + break; + } + } bool result = m_serviceWidget->handleJoinSessionEvent(event); if (result){ // If we have a result here, it's that the join was accepted for the first time. @@ -458,17 +474,17 @@ void InSessionWidget::connectSignals() void InSessionWidget::initUI() { + ui->btnEndSession->hide(); + ui->grpSavePath->hide(); ui->wdgInvitees->setConfirmOnRemove(true); ui->wdgInvitees->setComManager(m_comManager); //ui->wdgInvitees->showAvailableInvitees(true); - ui->btnInSessionInfos->setChecked(true); + ui->btnInSessionInfos->setChecked(false); + ui->tabInfos->hide(); ui->tabInfos->setCurrentIndex(0); - ui->btnEndSession->hide(); - ui->grpSavePath->hide(); - // Clean up, if needed if (m_serviceWidget){ m_serviceWidget->deleteLater(); @@ -485,7 +501,7 @@ void InSessionWidget::initUI() bool handled = false; if (service_key == "VideoRehabService"){ // Main widget = QWebEngine - m_serviceWidget = new VideoRehabWidget(m_comManager, this); + m_serviceWidget = new VideoRehabWidget(m_comManager); setMainWidget(m_serviceWidget); m_serviceToolsWidget = new VideoRehabToolsWidget(m_comManager, m_serviceWidget, this); setToolsWidget(m_serviceToolsWidget); diff --git a/client/src/widgets/InSessionWidget.ui b/client/src/widgets/InSessionWidget.ui index 9a17eb47..6d76b2c4 100644 --- a/client/src/widgets/InSessionWidget.ui +++ b/client/src/widgets/InSessionWidget.ui @@ -11,24 +11,12 @@ - Form + OpenTeraPlus - Séance - - 0 - - - 0 - - - 0 - - - 0 - @@ -184,195 +172,197 @@ - - - - 0 - 0 - - - - Qt::Horizontal - - - - - 0 - 0 - - - - - 300 - 16777215 - - - - QTabWidget::West - - - 1 - - - - 20 - 20 - - - - - - :/icons/patient.png:/icons/patient.png - - - Invités - - - - 5 - - - 5 - - - 5 - - - 5 - - - - - + + + + + + 0 + 0 + + + + + 400 + 16777215 + + + + QTabWidget::West + + + 0 + + + + 20 + 20 + + + + + + :/icons/patient.png:/icons/patient.png + + + Invités + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + + + + + + + :/icons/config.png:/icons/config.png + + + Paramètres + + + + + + + 0 + 0 + + + + Données + + + + + + + 0 + 0 + + + + Répertoire d'enregistrement + + + + + + + true + + + + + + + + + + 0 + 32 + + + + + 16777215 + 16777215 + + + + PointingHandCursor + + + Défaut + + + + :/icons/leave.png:/icons/leave.png + + + + 24 + 24 + + + + + + + + + 0 + 32 + + + + + 16777215 + 16777215 + + + + PointingHandCursor + + + Parcourir... + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + - - - - :/icons/config.png:/icons/config.png - - - Paramètres - - - - - - - 0 - 0 - - - - Données - - - - - - - 0 - 0 - - - - Répertoire d'enregistrement - - - - - - - true - - - - - - - - - - 0 - 32 - - - - - 16777215 - 16777215 - - - - PointingHandCursor - - - Défaut - - - - :/icons/leave.png:/icons/leave.png - - - - 24 - 24 - - - - - - - - - 0 - 32 - - - - - 16777215 - 16777215 - - - - PointingHandCursor - - - Parcourir... - - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - + + + + + + 0 + 0 + + + - - - - - 0 - 0 - - - - - + +
diff --git a/client/src/widgets/LogViewWidget.cpp b/client/src/widgets/LogViewWidget.cpp index c74fc14f..b8e74a03 100644 --- a/client/src/widgets/LogViewWidget.cpp +++ b/client/src/widgets/LogViewWidget.cpp @@ -18,7 +18,7 @@ LogViewWidget::LogViewWidget(QWidget *parent): // Init levels combo box int level_id = LogEvent::LogLevel_MIN; QStringList names = getLogLevelNames(); - for(const QString &name:qAsConst(names)){ + for(const QString &name:std::as_const(names)){ ui->cmbLevel->addItem(name, level_id++); /*if (level_id == LogEvent::LOGLEVEL_INFO){ ui->cmbLevel->setCurrentIndex(ui->cmbLevel->count()-1); @@ -384,6 +384,9 @@ QString LogViewWidget::getOSIcon(const QString &os) if (compare_os.contains("android")) return "://icons/logs/os_android.png"; + if (compare_os.contains("chrome")) + return "://icons/logs/browser_chrome.png"; + return ""; } diff --git a/client/src/widgets/NotificationWindow.cpp b/client/src/widgets/NotificationWindow.cpp index 93640c20..09ebba66 100644 --- a/client/src/widgets/NotificationWindow.cpp +++ b/client/src/widgets/NotificationWindow.cpp @@ -146,7 +146,8 @@ void NotificationWindow::paintEvent(QPaintEvent *) { //this is mandatory for the style sheet to take effect QStyleOption opt; - opt.init(this); + //TODO Verify + //opt.init(this); QPainter p(this); style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); } diff --git a/client/src/widgets/NotificationWindow.h b/client/src/widgets/NotificationWindow.h index d3fa5763..8bfc8f16 100644 --- a/client/src/widgets/NotificationWindow.h +++ b/client/src/widgets/NotificationWindow.h @@ -5,7 +5,7 @@ #include #include #include -#include +//#include #include #include "ui_notification.h" diff --git a/client/src/widgets/OnlineManagerWidget.cpp b/client/src/widgets/OnlineManagerWidget.cpp index eb7d2896..2403fc9f 100644 --- a/client/src/widgets/OnlineManagerWidget.cpp +++ b/client/src/widgets/OnlineManagerWidget.cpp @@ -124,9 +124,10 @@ void OnlineManagerWidget::refreshOnlines() QUrlQuery args; m_comManager->doGet(WEB_ONLINEDEVICEINFO_PATH, args); - m_comManager->doGet(WEB_ONLINEPARTICIPANTINFO_PATH, args); m_comManager->doGet(WEB_ONLINEUSERINFO_PATH, args); - + args.addQueryItem(WEB_QUERY_WITH_PROJECTS, "1"); + args.addQueryItem(WEB_QUERY_WITH_SITES, "1"); + m_comManager->doGet(WEB_ONLINEPARTICIPANTINFO_PATH, args); } void OnlineManagerWidget::updateCounts() @@ -190,9 +191,27 @@ void OnlineManagerWidget::updateOnlineParticipant(const TeraData *online_partici if (online_participant->isOnline()/* || online_participant.isBusy()*/){ // Not online and not busy = don't need to add! participant_item = new QTreeWidgetItem(m_baseParticipants); participant_item->setIcon(0, QIcon(online_participant->getIconStateFilename())); - participant_item->setText(0, online_participant->getName()); + QString name = online_participant->getName(); + QString project; + if (online_participant->hasFieldName("project_name")){ + project = online_participant->getFieldValue("project_name").toString(); + } + QString site; + if (online_participant->hasFieldName("site_name")){ + site = online_participant->getFieldValue("site_name").toString(); + } + if (!project.isEmpty()){ + name += " (" + project + ")"; + } + participant_item->setText(0, name); #ifdef QT_DEBUG - participant_item->setToolTip(0, uuid); + QString tooltip = uuid; + if (!site.isEmpty() && !project.isEmpty()) + tooltip += "\n" + site + "\n" + project; + participant_item->setToolTip(0, tooltip); +#else + if (!site.isEmpty() && !project.isEmpty()) + participant_item->setToolTip(0, site + "\n" + project); #endif m_onlineParticipants[uuid] = participant_item; } @@ -265,11 +284,13 @@ void OnlineManagerWidget::createOnlineUser(const QString &uuid, const QString &n m_onlineUsersData[uuid] = new_data; } -void OnlineManagerWidget::createOnlineParticipant(const QString& uuid, const QString& name) +void OnlineManagerWidget::createOnlineParticipant(const QString& uuid, const QString& name, const QString &site, const QString &project) { TeraData new_data(TERADATA_ONLINE_PARTICIPANT); new_data.setName(name); new_data.setUuid(uuid); + new_data.setFieldValue("site_name", site); + new_data.setFieldValue("project_name", project); m_onlineParticipantsData[uuid] = new_data; } @@ -320,7 +341,7 @@ void OnlineManagerWidget::ws_participantEvent(ParticipantEvent event) return;*/ if (!m_onlineParticipantsData.contains(uuid)){ - createOnlineParticipant(uuid, QString::fromStdString(event.participant_name())); + createOnlineParticipant(uuid, QString::fromStdString(event.participant_name()), QString::fromStdString(event.participant_site_name()), QString::fromStdString(event.participant_project_name())); } TeraData* participant_data = &m_onlineParticipantsData[uuid]; @@ -397,7 +418,13 @@ void OnlineManagerWidget::processOnlineParticipants(QList participants { foreach(TeraData online_participant, participants){ if (!m_onlineParticipantsData.contains(online_participant.getUuid())){ - createOnlineParticipant(online_participant.getUuid(), online_participant.getName()); + QString project; + if (online_participant.hasFieldName("project_name")) + project = online_participant.getFieldValue("project_name").toString(); + QString site; + if (online_participant.hasFieldName("site_name")) + site = online_participant.getFieldValue("site_name").toString(); + createOnlineParticipant(online_participant.getUuid(), online_participant.getName(), site, project); } TeraData* part_data = &m_onlineParticipantsData[online_participant.getUuid()]; part_data->setOnline(online_participant.isOnline()); diff --git a/client/src/widgets/OnlineManagerWidget.h b/client/src/widgets/OnlineManagerWidget.h index 2f18aa11..93ce5104 100644 --- a/client/src/widgets/OnlineManagerWidget.h +++ b/client/src/widgets/OnlineManagerWidget.h @@ -54,7 +54,7 @@ class OnlineManagerWidget : public QWidget void updateOnlineDevice(const TeraData *online_device); void createOnlineUser(const QString& uuid, const QString& name); - void createOnlineParticipant(const QString& uuid, const QString& name); + void createOnlineParticipant(const QString& uuid, const QString& name, const QString& site, const QString& project); void createOnlineDevice(const QString& uuid, const QString& name); private slots: diff --git a/client/src/widgets/OnlineManagerWidget.ui b/client/src/widgets/OnlineManagerWidget.ui index deb78aa2..58cfeb4c 100644 --- a/client/src/widgets/OnlineManagerWidget.ui +++ b/client/src/widgets/OnlineManagerWidget.ui @@ -229,6 +229,12 @@ + + QAbstractItemView::NoEditTriggers + + + false + QAbstractItemView::NoSelection diff --git a/client/src/widgets/ProjectNavigator.cpp b/client/src/widgets/ProjectNavigator.cpp index d2ccec0c..b2ec22e8 100644 --- a/client/src/widgets/ProjectNavigator.cpp +++ b/client/src/widgets/ProjectNavigator.cpp @@ -194,7 +194,7 @@ bool ProjectNavigator::selectItemByName(const TeraDataTypes &data_type, const QS if (data_type == TERADATA_GROUP){ foreach(QTreeWidgetItem* item, m_groups_items){ if (item->text(0) == name){ - ui->treeNavigator->setCurrentItem(item); + setCurrentItem(item); currentNavItemChanged(item, nullptr); return true; } @@ -204,7 +204,7 @@ bool ProjectNavigator::selectItemByName(const TeraDataTypes &data_type, const QS if (data_type == TERADATA_PROJECT){ foreach(QTreeWidgetItem* item, m_projects_items){ if (item->text(0) == name){ - ui->treeNavigator->setCurrentItem(item); + setCurrentItem(item); currentNavItemChanged(item, nullptr); return true; } @@ -214,7 +214,7 @@ bool ProjectNavigator::selectItemByName(const TeraDataTypes &data_type, const QS if (data_type == TERADATA_PARTICIPANT){ foreach(QTreeWidgetItem* item, m_participants_items){ if (item->text(0) == name){ - ui->treeNavigator->setCurrentItem(item); + setCurrentItem(item); currentNavItemChanged(item, nullptr); return true; } @@ -224,7 +224,7 @@ bool ProjectNavigator::selectItemByName(const TeraDataTypes &data_type, const QS if (data_type == TERADATA_USER){ foreach(QTreeWidgetItem* item, m_users_items){ if (item->text(0) == name){ - ui->treeNavigator->setCurrentItem(item); + setCurrentItem(item); currentNavItemChanged(item, nullptr); return true; } @@ -234,7 +234,7 @@ bool ProjectNavigator::selectItemByName(const TeraDataTypes &data_type, const QS if (data_type == TERADATA_DEVICE){ foreach(QTreeWidgetItem* item, m_devices_items){ if (item->text(0) == name){ - ui->treeNavigator->setCurrentItem(item); + setCurrentItem(item); currentNavItemChanged(item, nullptr); return true; } @@ -250,7 +250,7 @@ bool ProjectNavigator::selectItemByUuid(const TeraDataTypes &data_type, const QS if (m_participants.contains(uuid)){ QTreeWidgetItem* item = m_participants_items[m_participants[uuid].getId()]; if (item){ - ui->treeNavigator->setCurrentItem(item); + setCurrentItem(item); currentNavItemChanged(item, nullptr); return true; } @@ -353,21 +353,7 @@ TeraDataTypes ProjectNavigator::getCurrentItemType() { QTreeWidgetItem* item = getCurrentItem(); - if (item){ - if (m_devices_items.key(item, -1) >= 0){ - return TERADATA_DEVICE; - } - if (m_groups_items.key(item, -1) >= 0){ - return TERADATA_GROUP; - } - if (m_participants_items.key(item, -1) >= 0){ - return TERADATA_PARTICIPANT; - } - if (m_users_items.key(item, -1) >= 0){ - return TERADATA_USER; - } - } - return TERADATA_NONE; + return getItemType(item); } int ProjectNavigator::getCurrentItemId() @@ -386,6 +372,9 @@ int ProjectNavigator::getCurrentItemId() if (id == -1){ id = m_users_items.key(item, -1); } + if (id == -1){ + id = m_projects_items.key(item, -1); + } return id; } return -1; @@ -534,7 +523,14 @@ void ProjectNavigator::updateProject(const TeraData *project) } item->setText(0, project->getName()); - item->setIcon(0, QIcon(TeraData::getIconFilenameForDataType(TERADATA_PROJECT))); + item->setIcon(0, QIcon(project->getIconStateFilename())); + if (project->isEnabled() || !project->hasEnabledField()){ + item->setForeground(0, Qt::white); + item->setData(0, Qt::UserRole, true); // Enabled status + }else{ + item->setForeground(0, Qt::gray); + item->setData(0, Qt::UserRole, false); // Disabled status + } if (m_currentProjectId == id_project && m_currentProjectId >0 && !m_selectionHold){ // Load details @@ -696,6 +692,7 @@ void ProjectNavigator::updateGroup(const TeraData *group) } item->setText(0, group->getName()); + item->setForeground(0, m_projects_items[id_project]->foreground(0)); item->setIcon(0, QIcon(TeraData::getIconFilenameForDataType(TERADATA_GROUP))); if (m_currentGroupId == id_group && m_currentGroupId >0 && !item->isExpanded()){ @@ -900,6 +897,12 @@ void ProjectNavigator::updateParticipant(const TeraData *participant) } item->setText(0, participant->getName()); + if (participant->isEnabled()){ + item->setForeground(0, Qt::white); + }else{ + item->setForeground(0, Qt::gray); + } + if (participant->hasBusyStateField() || participant->hasOnlineStateField()) item->setIcon(0, QIcon(participant->getIconStateFilename())); m_participants[participant->getUuid()] = *participant; @@ -926,7 +929,7 @@ void ProjectNavigator::updateUser(const TeraData *user, const int& id_project) if (id_project >=0){ // Check if already exists for that project bool exists = false; - for (QTreeWidgetItem* item:qAsConst(items)){ + for (QTreeWidgetItem* item:std::as_const(items)){ QTreeWidgetItem* project = getProjectForItem(item); if (project){ if (m_projects_items.key(project) == id_project){ @@ -964,7 +967,7 @@ void ProjectNavigator::updateUser(const TeraData *user, const int& id_project) } } - for (QTreeWidgetItem* item:qAsConst(items)){ + for (QTreeWidgetItem* item:std::as_const(items)){ item->setText(0, user->getName()); item->setIcon(0, QIcon(user->getIconStateFilename())); } @@ -982,7 +985,7 @@ void ProjectNavigator::updateDevice(const TeraData *device, const int &id_projec if (id_project >=0){ // Check if already exists for that project bool exists = false; - for (QTreeWidgetItem* item:qAsConst(items)){ + for (QTreeWidgetItem* item:std::as_const(items)){ QTreeWidgetItem* project = getProjectForItem(item); if (project){ if (m_projects_items.key(project) == id_project){ @@ -1019,7 +1022,7 @@ void ProjectNavigator::updateDevice(const TeraData *device, const int &id_projec } } - for (QTreeWidgetItem* item:qAsConst(items)){ + for (QTreeWidgetItem* item:std::as_const(items)){ item->setText(0, device->getName()); item->setIcon(0, QIcon(device->getIconStateFilename())); } @@ -1133,6 +1136,18 @@ QTreeWidgetItem *ProjectNavigator::getProjectForItem(const QTreeWidgetItem *item } +QTreeWidgetItem *ProjectNavigator::getParticipantGroupForItem(const QTreeWidgetItem *item) +{ + QTreeWidgetItem* group = nullptr; + QTreeWidgetItem* item_parent = item->parent(); + if (item_parent){ + if (m_groups_items.key(item_parent,0) > 0){ + group = item_parent; + } + } + return group; +} + bool ProjectNavigator::isParticipantFiltered(const QString &part_uuid) { // No participant with that uuid! @@ -1148,7 +1163,6 @@ bool ProjectNavigator::isParticipantFiltered(const QString &part_uuid) } // Check for text filtering - if (ui->btnSearch->isChecked() && !filtered){ filtered = !m_participants[part_uuid].getName().contains(ui->txtNavSearch->text(), Qt::CaseInsensitive); } @@ -1164,6 +1178,21 @@ bool ProjectNavigator::isAdvancedView() void ProjectNavigator::setCurrentItem(QTreeWidgetItem *item) { ui->treeNavigator->setCurrentItem(item); + TeraDataTypes item_type = getItemType(item); + if (item_type == TERADATA_GROUP || item_type == TERADATA_PARTICIPANT){ + // Make sure current project id is correct + QTreeWidgetItem* project = getProjectForItem(item); + if (project){ + m_currentProjectId = m_projects_items.key(project); + } + } + if (item_type == TERADATA_PARTICIPANT){ + // Make sure group id is correct, if available + QTreeWidgetItem* group = getParticipantGroupForItem(item); + if (group){ + m_currentGroupId = m_groups_items.key(group); + } + } updateAvailableActions(item); } @@ -1186,6 +1215,16 @@ void ProjectNavigator::selectItem(QTreeWidgetItem *item) int id = m_participants_items.key(item); setCurrentItem(m_participants_items[id]); m_currentParticipantUuid = getParticipantUuid(id); + /*QTreeWidgetItem* project = getProjectForItem(ui->treeNavigator->currentItem()); + if (project){ + m_currentProjectId = m_projects_items.key(project); + } + + QTreeWidgetItem* group = getParticipantGroupForItem(ui->treeNavigator->currentItem()); + if (group){ + m_currentGroupId = m_groups_items.key(group); + }*/ + return; } } @@ -1258,24 +1297,20 @@ void ProjectNavigator::updateAvailableActions(QTreeWidgetItem* current_item) TeraDataTypes ProjectNavigator::getItemType(QTreeWidgetItem *item) { - if (std::find(m_projects_items.cbegin(), m_projects_items.cend(), item) != m_projects_items.cend()){ - return TERADATA_PROJECT; + if (m_devices_items.key(item, -1) >= 0){ + return TERADATA_DEVICE; } - - if (std::find(m_groups_items.cbegin(), m_groups_items.cend(), item) != m_groups_items.cend()){ + if (m_groups_items.key(item, -1) >= 0){ return TERADATA_GROUP; } - - if (std::find(m_participants_items.cbegin(), m_participants_items.cend(), item) != m_participants_items.cend()){ + if (m_participants_items.key(item, -1) >= 0){ return TERADATA_PARTICIPANT; } - - if (std::find(m_users_items.cbegin(), m_users_items.cend(), item) != m_users_items.cend()){ + if (m_users_items.key(item, -1) >= 0){ return TERADATA_USER; } - - if (std::find(m_devices_items.cbegin(), m_devices_items.cend(), item) != m_devices_items.cend()){ - return TERADATA_DEVICE; + if (m_projects_items.key(item, -1) >= 0){ + return TERADATA_PROJECT; } if (isAdvancedView() && item){ @@ -1370,8 +1405,8 @@ void ProjectNavigator::deleteItemRequested() tr("Êtes-vous sûrs de vouloir supprimer """) + ui->treeNavigator->currentItem()->text(0) + """?"); if (answer == QMessageBox::Yes){ // We must delete! - TeraDataTypes item_type = getItemType(ui->treeNavigator->currentItem()); - int id_todel = ui->treeNavigator->currentItem()->data(0, Qt::UserRole).toInt(); + TeraDataTypes item_type = getCurrentItemType(); //getItemType(ui->treeNavigator->currentItem()); + int id_todel = getCurrentItemId(); emit dataDeleteRequest(item_type, id_todel); } } @@ -1842,9 +1877,16 @@ void ProjectNavigator::on_btnFilterActive_toggled(bool checked) item->setHidden(filtered); } } + + // Filter disabled projects + foreach(QTreeWidgetItem* item, m_projects_items){ + bool enabled = item->data(0, Qt::UserRole).toBool(); + item->setHidden(!enabled && checked); + } + // Update counts for all projects /*if (isAdvancedView()){ - for(QTreeWidgetItem* item_project:qAsConst(m_projects_items)){ + for(QTreeWidgetItem* item_project:std::as_const(m_projects_items)){ if (item_project->isExpanded()){ updateCountsForProject(m_projects_items.key(item_project)); } diff --git a/client/src/widgets/ProjectNavigator.h b/client/src/widgets/ProjectNavigator.h index d25a9399..237e65f8 100644 --- a/client/src/widgets/ProjectNavigator.h +++ b/client/src/widgets/ProjectNavigator.h @@ -91,6 +91,7 @@ class ProjectNavigator : public QWidget QTreeWidgetItem* getProjectItem(const int& id_project, const TeraDataTypes& data_type = TERADATA_NONE); QTreeWidgetItem* getProjectForItem(const QTreeWidgetItem *item); + QTreeWidgetItem* getParticipantGroupForItem(const QTreeWidgetItem* item); bool isParticipantFiltered(const QString &part_uuid); bool isAdvancedView(); diff --git a/client/src/widgets/ProjectNavigator.ui b/client/src/widgets/ProjectNavigator.ui index c66c8b44..5f31b0c3 100644 --- a/client/src/widgets/ProjectNavigator.ui +++ b/client/src/widgets/ProjectNavigator.ui @@ -15,16 +15,16 @@ - 0 + 2 - 0 + 5 - 0 + 2 - 0 + 5 @@ -381,7 +381,7 @@ - :/icons/details.png:/icons/details.png + :/icons/navtree.png:/icons/navtree.png @@ -448,15 +448,14 @@ PointingHandCursor - Afficher / masquer les participants inactifs + Afficher / masquer les éléments inactifs ... - :/icons/patient.png - :/icons/patient_installed.png:/icons/patient.png + :/icons/filter.png:/icons/filter.png diff --git a/client/src/widgets/ProjectNavigatorTree.cpp b/client/src/widgets/ProjectNavigatorTree.cpp index f956d101..07c98f51 100644 --- a/client/src/widgets/ProjectNavigatorTree.cpp +++ b/client/src/widgets/ProjectNavigatorTree.cpp @@ -18,7 +18,7 @@ void ProjectNavigatorTree::dropEvent(QDropEvent *event) } QTreeWidgetItem* dropped_item = currentItem(); - QTreeWidgetItem* target_item = itemAt(event->pos()); + QTreeWidgetItem* target_item = itemAt(event->position().toPoint()); if (!target_item){ event->ignore(); @@ -53,7 +53,7 @@ void ProjectNavigatorTree::dragMoveEvent(QDragMoveEvent *event) } // Check allowed types - QTreeWidgetItem* target_item = itemAt(event->pos()); + QTreeWidgetItem* target_item = itemAt(event->position().toPoint()); if (target_item){ if (dragged_item->parent() == target_item){ event->ignore(); diff --git a/client/src/widgets/ResultMessageWidget.cpp b/client/src/widgets/ResultMessageWidget.cpp index 9ba9eed2..f157db4f 100644 --- a/client/src/widgets/ResultMessageWidget.cpp +++ b/client/src/widgets/ResultMessageWidget.cpp @@ -43,7 +43,7 @@ bool ResultMessageWidget::hasMessagesWaiting(const Message::MessageType &msg_typ return m_messages.isEmpty(); // Otherwise, we look for message of that type in the queue - for (Message msg:qAsConst(m_messages)){ + for (Message msg:std::as_const(m_messages)){ if (msg.getMessageType() == msg_type) return true; } diff --git a/client/src/widgets/SessionInviteWidget.cpp b/client/src/widgets/SessionInviteWidget.cpp index ed6e59df..58c4018f 100644 --- a/client/src/widgets/SessionInviteWidget.cpp +++ b/client/src/widgets/SessionInviteWidget.cpp @@ -1050,6 +1050,6 @@ void SessionInviteWidget::on_txtSearchInvitees_textChanged(const QString &arg1) { Q_UNUSED(arg1) // Check if search field is empty - setSearching(ui->txtSearchInvitees->text().count()>0); + setSearching(ui->txtSearchInvitees->text().size()>0); updateFilters(); } diff --git a/client/src/widgets/SessionInviteWidget.h b/client/src/widgets/SessionInviteWidget.h index c9f7385f..9a6dc6eb 100644 --- a/client/src/widgets/SessionInviteWidget.h +++ b/client/src/widgets/SessionInviteWidget.h @@ -9,7 +9,7 @@ #include "GlobalMessageBox.h" #include "managers/ComManager.h" -#define MAX_INVITEES_IN_SESSION 5 +#define MAX_INVITEES_IN_SESSION 6 namespace Ui { class SessionInviteWidget; diff --git a/client/src/widgets/SessionInviteWidget.ui b/client/src/widgets/SessionInviteWidget.ui index 2e5e66f5..e8790d3e 100644 --- a/client/src/widgets/SessionInviteWidget.ui +++ b/client/src/widgets/SessionInviteWidget.ui @@ -253,7 +253,7 @@ - 0 + 6 0 diff --git a/client/src/widgets/SessionsListWidget.cpp b/client/src/widgets/SessionsListWidget.cpp index d7bdc45d..0e0bf007 100644 --- a/client/src/widgets/SessionsListWidget.cpp +++ b/client/src/widgets/SessionsListWidget.cpp @@ -164,7 +164,7 @@ const TeraData *SessionsListWidget::hasResumableSession(const int &id_session_ty { TeraData *rval = nullptr; // Check if we have a session that can be resumed for that date - for(TeraData* session:qAsConst(m_ids_sessions)){ + for(TeraData* session:std::as_const(m_ids_sessions)){ if (id_session_type == session->getFieldValue("id_session_type").toInt()){ int session_status = session->getFieldValue("session_status").toInt(); if (session_status == TeraSessionStatus::STATUS_INPROGRESS || session_status == TeraSessionStatus::STATUS_NOTSTARTED){ @@ -387,14 +387,20 @@ void SessionsListWidget::on_btnNextCal_clicked() void SessionsListWidget::updateCalendars(QDate left_date) { ui->calMonth1->setCurrentPage(left_date.year(),left_date.month()); + ui->calMonth1->setMinimumDate(QDate(left_date.year(),left_date.month(), 1)); + ui->calMonth1->setMaximumDate(QDate(left_date.year(),left_date.month(), left_date.daysInMonth())); ui->lblMonth1->setText(Utils::toCamelCase(QLocale().monthName(left_date.month())) + " " + QString::number(left_date.year())); left_date = left_date.addMonths(1); ui->calMonth2->setCurrentPage(left_date.year(),left_date.month()); + ui->calMonth2->setMinimumDate(QDate(left_date.year(),left_date.month(), 1)); + ui->calMonth2->setMaximumDate(QDate(left_date.year(),left_date.month(), left_date.daysInMonth())); ui->lblMonth2->setText(Utils::toCamelCase(QLocale().monthName(left_date.month())) + " " + QString::number(left_date.year())); left_date = left_date.addMonths(1); ui->calMonth3->setCurrentPage(left_date.year(),left_date.month()); + ui->calMonth3->setMinimumDate(QDate(left_date.year(),left_date.month(), 1)); + ui->calMonth3->setMaximumDate(QDate(left_date.year(),left_date.month(), left_date.daysInMonth())); ui->lblMonth3->setText(Utils::toCamelCase(QLocale().monthName(left_date.month())) + " " + QString::number(left_date.year())); // Check if we must enable the previous month button @@ -409,7 +415,7 @@ void SessionsListWidget::updateCalendars(QDate left_date) QDate SessionsListWidget::getMinimumSessionDate() { QDate min_date = QDate::currentDate(); - for (TeraData* session:qAsConst(m_ids_sessions)){ + for (TeraData* session:std::as_const(m_ids_sessions)){ QDate session_date = session->getFieldValue("session_start_datetime").toDateTime().toLocalTime().date(); if (session_date < min_date) min_date = session_date; @@ -420,13 +426,12 @@ QDate SessionsListWidget::getMinimumSessionDate() QDate SessionsListWidget::getMaximumSessionDate() { - QDate max_date = QDate::currentDate(); - for (TeraData* session:qAsConst(m_ids_sessions)){ + QDate max_date; + for (TeraData* session:std::as_const(m_ids_sessions)){ QDate session_date = session->getFieldValue("session_start_datetime").toDateTime().toLocalTime().date(); if (session_date > max_date) max_date = session_date; } - return max_date; } @@ -456,6 +461,7 @@ void SessionsListWidget::newSessionRequest(const QDateTime &session_datetime) m_diag_editor->setWindowTitle(tr("Séance")); m_diag_editor->setMinimumSize(this->width(), this->height()); + connect(ses_widget, &SessionWidget::closeRequest, m_diag_editor, &QDialog::accept); connect(ses_widget, &SessionWidget::dataWasChanged, m_diag_editor, &QDialog::accept); connect(ses_widget, &SessionWidget::dataWasDeleted, m_diag_editor, &QDialog::accept); @@ -475,7 +481,7 @@ void SessionsListWidget::setSessionsLoading(const bool &loading) void SessionsListWidget::querySessions() { if (m_totalSessions == 0){ - qWarning() << "SessionsListWidget::querySessions(): No sessions to query - aborting!"; + //qWarning() << "SessionsListWidget::querySessions(): No sessions to query - aborting!"; return; } ui->tableSessions->setSortingEnabled(false); @@ -847,7 +853,7 @@ void SessionsListWidget::currentCalendarDateChanged(QDate current_date) // Select all the sessions in the list that fits with that date QTableWidgetItem* first_item = nullptr; //foreach(TeraData* session, m_ids_sessions){ - for(TeraData* session: qAsConst(m_ids_sessions)){ + for(TeraData* session: std::as_const(m_ids_sessions)){ if (session->getFieldValue("session_start_datetime").toDateTime().toLocalTime().date() == current_date){ QTableWidgetItem* session_item = m_listSessions_items.value(session->getId()); if (session_item){ diff --git a/client/src/widgets/SessionsListWidget.ui b/client/src/widgets/SessionsListWidget.ui index 9ec204ea..413f2ade 100644 --- a/client/src/widgets/SessionsListWidget.ui +++ b/client/src/widgets/SessionsListWidget.ui @@ -7,7 +7,7 @@ 0 0 852 - 453 + 524 @@ -64,7 +64,7 @@ 0 0 850 - 451 + 536 @@ -104,7 +104,7 @@ 16777215 - 200 + 225 @@ -384,7 +384,7 @@ - + 0 0 @@ -478,7 +478,7 @@ - + 0 0 @@ -486,7 +486,7 @@ 0 - 0 + 150 @@ -541,7 +541,7 @@ - + 0 0 diff --git a/client/src/widgets/TableDateWidgetItem.cpp b/client/src/widgets/TableDateWidgetItem.cpp index 6d0c2264..a2fba2d5 100644 --- a/client/src/widgets/TableDateWidgetItem.cpp +++ b/client/src/widgets/TableDateWidgetItem.cpp @@ -1,18 +1,36 @@ #include "TableDateWidgetItem.h" +TableDateWidgetItem::TableDateWidgetItem(const QDateTime &date) +{ + setDate(date); +} bool TableDateWidgetItem::operator<(const QTableWidgetItem &other) const { - QString current_value = this->text(); + const TableDateWidgetItem* other_item = dynamic_cast(&other); + QDateTime other_date; + if (!other_item->m_date.isValid()){ + other_date = QDateTime::fromString(other.text(), "dd-MM-yyyy hh:mm:ss"); + }else{ + other_date = other_item->m_date; + } + + /*QString current_value = this->text(); QString other_value = other.text(); // Convert to date and compate - return (QDateTime::fromString(current_value, "dd-MM-yyyy hh:mm:ss") < QDateTime::fromString(other_value, "dd-MM-yyyy hh:mm:ss")); + return (QDateTime::fromString(current_value, "dd-MM-yyyy hh:mm:ss") < QDateTime::fromString(other_value, "dd-MM-yyyy hh:mm:ss"));*/ + + + if (m_date.isValid()) + return (m_date < other_date); + else + return (QDateTime::fromString(text(), "dd-MM-yyyy hh:mm:ss") < other_date); } void TableDateWidgetItem::setDate(const QVariant &date_var) { - QDateTime date_value = date_var.toDateTime().toLocalTime(); - setText(date_value.toString("dd-MM-yyyy hh:mm:ss")); + m_date = date_var.toDateTime().toLocalTime(); + setText(m_date.toString("dd-MM-yyyy hh:mm:ss")); } diff --git a/client/src/widgets/TableDateWidgetItem.h b/client/src/widgets/TableDateWidgetItem.h index f803057c..4e044a42 100644 --- a/client/src/widgets/TableDateWidgetItem.h +++ b/client/src/widgets/TableDateWidgetItem.h @@ -4,16 +4,22 @@ #include #include #include +#include class TableDateWidgetItem : public QTableWidgetItem { -public: + public: + + using QTableWidgetItem::QTableWidgetItem; // Use base class constructors + explicit TableDateWidgetItem(const QDateTime& date); - using QTableWidgetItem::QTableWidgetItem; // Use base class constructors + bool operator<(const QTableWidgetItem &other) const override; - bool operator<(const QTableWidgetItem &other) const override; + void setDate(const QVariant &date_var); - void setDate(const QVariant &date_var); + private: + QDateTime m_date; }; + #endif // TABLEDATEWIDGETITEM_H diff --git a/client/src/widgets/TestsWidget.cpp b/client/src/widgets/TestsWidget.cpp index 15cb5974..dedeffa9 100644 --- a/client/src/widgets/TestsWidget.cpp +++ b/client/src/widgets/TestsWidget.cpp @@ -323,6 +323,149 @@ void TestsWidget::updateTestsCountLabel() ui->btnDownloadAll->setEnabled(m_tests.count()>0); } +void TestsWidget::downloadTests(const QList tests) +{ + + if (tests.isEmpty()) return; + + GlobalMessageBox msg(this); + + // Query for file to save + QString full_path; + QString filename = ""; // Generate default filename + if (tests.first()->hasFieldName("test_participant")){ + QString clean_name = Utils::removeNonAlphanumerics(tests.first()->getFieldValue("test_participant").toString()); + filename += clean_name; + if (!filename.endsWith("_")) + filename += "_"; + } + if (m_viewMode == VIEWMODE_SESSION){ + if (tests.first()->hasFieldName("test_session_name")){ + QString clean_name = Utils::removeNonAlphanumerics(tests.first()->getFieldValue("test_session_name").toString()); + filename += clean_name; + if (!filename.endsWith("_")) + filename += "_"; + } + } + filename += "tests.csv"; + + m_fileDialog.selectFile(filename); + m_fileDialog.setFileMode(QFileDialog::AnyFile); + m_fileDialog.setWindowTitle(tr("Fichier à enregistrer")); + m_fileDialog.setAcceptMode(QFileDialog::AcceptSave); + + if (m_fileDialog.exec()){ + QStringList selected_files = m_fileDialog.selectedFiles(); + if (selected_files.isEmpty()) + return; + full_path = selected_files.first(); + }else{ + return; + } + + // Get all variables in the json fields + QStringList columns; + QStringList variables; + for (TeraData* test:std::as_const(tests)){ + // Parse json result + if (test->hasFieldName("test_summary")){ + QJsonDocument test_summary_doc = QJsonDocument::fromJson(test->getFieldValue("test_summary").toString().toUtf8()); + if (!test_summary_doc.isNull()){ + QVariantMap test_summary = test_summary_doc.object().toVariantMap(); + QVariantMap::const_iterator test_result = test_summary.constBegin(); + while (test_result != test_summary.constEnd()) { + if (!variables.contains(test_result.key())) + variables << test_result.key(); + ++test_result; + } + } + } + + } + + columns << "Participant" << "User" << "Device" << "Service" << "Session" << "TestType" << "Status" << "Name" << "Date" << "Time"; // Basic columns + + // Format and save to csv + QFile csv_file(full_path); + + if (csv_file.open(QIODevice::WriteOnly| QIODevice::Text)){ + QTextStream csv_writer(&csv_file); + + for (const QString &col: std::as_const(columns)) + csv_writer << col << '\t'; + + for (const QString &var: std::as_const(variables)) + csv_writer << var << '\t'; + csv_writer << Qt::endl; + + for (TeraData* test:tests){ + + // Creator + if (test->hasFieldName("test_participant")){ + csv_writer << test->getFieldValue("test_participant").toString(); + } + csv_writer << '\t'; + if (test->hasFieldName("test_user")){ + csv_writer << test->getFieldValue("test_user").toString(); + } + csv_writer << '\t'; + if (test->hasFieldName("test_device")){ + csv_writer << test->getFieldValue("test_device").toString(); + } + csv_writer << '\t'; + if (test->hasFieldName("test_service")){ + csv_writer << test->getFieldValue("test_service").toString(); + } + csv_writer << '\t'; + + // Test infos + if (test->hasFieldName("test_session_name")){ + csv_writer << test->getFieldValue("test_session_name").toString(); + } + csv_writer << '\t'; + if (test->hasFieldName("id_test_type")){ + csv_writer << test->getFieldValue("id_test_type").toString(); + } + csv_writer << '\t'; + if (test->hasFieldName("test_status")){ + csv_writer << test->getFieldValue("test_status").toString(); + } + csv_writer << '\t'; + if (test->hasFieldName("test_name")){ + csv_writer << test->getFieldValue("test_name").toString(); + } + csv_writer << '\t'; + if (test->hasFieldName("test_datetime")){ + csv_writer << test->getFieldValue("test_datetime").toDateTime().toString("yyyy-MM-dd"); + csv_writer << '\t'; + csv_writer << test->getFieldValue("test_datetime").toDateTime().toString("hh:mm:ss"); + } + csv_writer << '\t'; + if (test->hasFieldName("test_summary")){ + QJsonDocument test_summary_doc = QJsonDocument::fromJson(test->getFieldValue("test_summary").toString().toUtf8()); + if (!test_summary_doc.isNull()){ + QVariantMap test_summary = test_summary_doc.object().toVariantMap(); + for (const QString &var: std::as_const(variables)){ + if (test_summary.contains(var)){ + csv_writer < tests, QUrlQuery reply_query) { if (reply_query.hasQueryItem(WEB_QUERY_WITH_ONLY_TOKEN)){ @@ -368,7 +511,7 @@ void TestsWidget::processServicesReply(QList services, QUrlQuery reply // Check if project is in service_fields if (service.hasFieldName("service_projects")){ QVariantList projects = service.getFieldValue("service_projects").toList(); - for (const QVariant &project:qAsConst(projects)){ + for (const QVariant &project:std::as_const(projects)){ QVariantMap project_info = project.toMap(); if (project_info["id_project"].toInt() == m_idProject){ // Ok, we are allowed to use file transfer service @@ -388,7 +531,7 @@ void TestsWidget::comDeleteDataReply(QString path, int id) { if (path == WEB_TESTINFO_PATH){ // Remove test from list - for(TeraData* test_info:qAsConst(m_tests)){ + for(TeraData* test_info:std::as_const(m_tests)){ if (test_info->getId() == id){ QString test_uuid = test_info->getUuid(); ui->tableTests->removeRow(m_tableTests[test_uuid]->row()); @@ -413,105 +556,6 @@ void TestsWidget::nextMessageWasShown(Message current_message) } } -/*void AssetsWidget::processAssetsInfos(QList infos, QUrlQuery reply_query, QString reply_path) -{ - for (const QJsonObject &asset_info:qAsConst(infos)){ - QString asset_uuid = asset_info["asset_uuid"].toString(); - - if (!m_treeAssets.contains(asset_uuid)){ - // Create a new asset, if possible (should happen if we posted a new asset) - TeraData asset(TERADATA_ASSET, asset_info); - updateAsset(asset); // TODO: manage participant association, if required by the view - } - - if (m_treeAssets.contains(asset_uuid)){ - QTreeWidgetItem* asset_item = m_treeAssets[asset_uuid]; - - if (asset_info.contains("asset_file_size")){ - - asset_item->setText(AssetColumn::ASSET_SIZE, Utils::formatFileSize(asset_info["asset_file_size"].toInt())); - ui->treeAssets->showColumn(AssetColumn::ASSET_SIZE); - } - - if (asset_info.contains("file_size")){ - asset_item->setText(AssetColumn::ASSET_SIZE, Utils::formatFileSize(asset_info["file_size"].toInt())); - ui->treeAssets->showColumn(AssetColumn::ASSET_SIZE); - } - - if (asset_info.contains("video_duration")){ - asset_item->setText(AssetColumn::ASSET_DURATION, Utils::formatDuration(asset_info["video_duration"].toString())); - ui->treeAssets->showColumn(AssetColumn::ASSET_DURATION); - } - } - - if (m_assets.contains(asset_uuid)){ - // Append received fields to current asset - TeraData* asset = m_assets[asset_uuid]; - asset->updateFrom(asset_info); - } - } - resizeAssetsColumnsToContent(); - - setLoading(false); -} - -void AssetsWidget::refreshAccessToken() -{ - if (!m_comManager) - return; - - QUrlQuery query = m_dataQuery; // Original data query - - query.addQueryItem(WEB_QUERY_WITH_ONLY_TOKEN, "1"); - m_comManager->doGet(WEB_ASSETINFO_PATH, query); -} - -void AssetsWidget::on_treeAssets_itemSelectionChanged() -{ - bool at_least_one_asset_selected = false; - - // Make sure we don't have only "labels" items (session, participant, ...) selected - const QList selected_items = ui->treeAssets->selectedItems(); - for(QTreeWidgetItem* item:selected_items){ - if (!m_treeAssets.key(item).isEmpty() ){ - at_least_one_asset_selected = true; - break; - } - } - ui->btnDelete->setEnabled(at_least_one_asset_selected); - ui->btnDownload->setEnabled(at_least_one_asset_selected); -} - - -void AssetsWidget::on_btnNew_clicked() -{ - // Upload a new asset to the FileTransfer service - if (!m_fileTransferServiceInfos || !m_comAssetManager || !m_comManager){ - return; // Should not happen at this point. - } - - if (m_uploadDialog){ - m_uploadDialog->deleteLater(); - } - - // Open upload dialog - m_uploadDialog = new FileUploaderDialog(tr("Tous (*.*)"), dynamic_cast(this)); - if (m_uploadDialog->result() == QDialog::Rejected){ - // No file selected - no need to upload anything! - m_uploadDialog->deleteLater(); - m_uploadDialog = nullptr; - return; - } - m_uploadDialog->setMinimumWidth(2*this->width()/3); - - connect(m_uploadDialog, &FileUploaderDialog::finished, this, &AssetsWidget::fileUploaderFinished); - - m_uploadDialog->setModal(true); - m_uploadDialog->show(); - - -}*/ - void TestsWidget::on_btnDelete_clicked() { // Check if the sender is a QToolButton (from the action column) @@ -551,7 +595,9 @@ void TestsWidget::on_btnDelete_clicked() void TestsWidget::on_tableTests_itemSelectionChanged() { - ui->btnDelete->setEnabled(!ui->tableTests->selectedItems().isEmpty()); + bool has_selection = !ui->tableTests->selectedItems().isEmpty(); + ui->btnDelete->setEnabled(has_selection); + ui->btnDownload->setEnabled(has_selection); // Display / hide summary ui->tableTestSummary->setVisible(ui->tableTests->selectionModel()->selectedRows().count() == 1); @@ -560,150 +606,37 @@ void TestsWidget::on_tableTests_itemSelectionChanged() QString test_uuid = m_tableTests.key(ui->tableTests->item(ui->tableTests->currentRow(), TEST_NAME_COLUMN)); showTestSummary(test_uuid); } + } void TestsWidget::on_btnDownloadAll_clicked() { if (m_tests.isEmpty()) return; // Should not happen - GlobalMessageBox msg(this); + QList tests; - // Query for file to save - QString full_path; - QString filename = ""; // Generate default filename - if (m_tests.first()->hasFieldName("test_participant")){ - QString clean_name = Utils::removeNonAlphanumerics(m_tests.first()->getFieldValue("test_participant").toString()); - filename += clean_name; - if (!filename.endsWith("_")) - filename += "_"; + // Browse table items to have sorted list (the order it will be exported) + for (int i=0; itableTests->rowCount(); i++){ + TeraData* test = m_tests[m_tableTests.key(ui->tableTests->item(i,0))]; + tests.append(test); } - if (m_viewMode == VIEWMODE_SESSION){ - if (m_tests.first()->hasFieldName("test_session_name")){ - QString clean_name = Utils::removeNonAlphanumerics(m_tests.first()->getFieldValue("test_session_name").toString()); - filename += clean_name; - if (!filename.endsWith("_")) - filename += "_"; - } - } - filename += "tests.csv"; - - m_fileDialog.selectFile(filename); - m_fileDialog.setFileMode(QFileDialog::AnyFile); - m_fileDialog.setWindowTitle(tr("Fichier à enregistrer")); - m_fileDialog.setAcceptMode(QFileDialog::AcceptSave); - - if (m_fileDialog.exec()){ - QStringList selected_files = m_fileDialog.selectedFiles(); - if (selected_files.isEmpty()) - return; - full_path = selected_files.first(); - }else{ - return; - } - - // Get all variables in the json fields - QStringList columns; - QStringList variables; - for (TeraData* test:qAsConst(m_tests)){ - // Parse json result - if (test->hasFieldName("test_summary")){ - QJsonDocument test_summary_doc = QJsonDocument::fromJson(test->getFieldValue("test_summary").toString().toUtf8()); - if (!test_summary_doc.isNull()){ - QVariantMap test_summary = test_summary_doc.object().toVariantMap(); - QVariantMap::const_iterator test_result = test_summary.constBegin(); - while (test_result != test_summary.constEnd()) { - if (!variables.contains(test_result.key())) - variables << test_result.key(); - ++test_result; - } - } - } - - } - - columns << "Participant" << "User" << "Device" << "Service" << "Session" << "TestType" << "Status" << "Name" << "Date" << "Time"; // Basic columns - - // Format and save to csv - QFile csv_file(full_path); - - if (csv_file.open(QIODevice::WriteOnly| QIODevice::Text)){ - QTextStream csv_writer(&csv_file); - - for (const QString &col: qAsConst(columns)) - csv_writer << col << '\t'; - - for (const QString &var: qAsConst(variables)) - csv_writer << var << '\t'; - csv_writer << Qt::endl; + downloadTests(tests); +} - for (int i=0; itableTests->rowCount(); i++){ - // Use the data in the table to keep received order of items - TeraData* test = m_tests[m_tableTests.key(ui->tableTests->item(i,0))]; - // Creator - if (test->hasFieldName("test_participant")){ - csv_writer << test->getFieldValue("test_participant").toString(); - } - csv_writer << '\t'; - if (test->hasFieldName("test_user")){ - csv_writer << test->getFieldValue("test_user").toString(); - } - csv_writer << '\t'; - if (test->hasFieldName("test_device")){ - csv_writer << test->getFieldValue("test_device").toString(); - } - csv_writer << '\t'; - if (test->hasFieldName("test_service")){ - csv_writer << test->getFieldValue("test_service").toString(); - } - csv_writer << '\t'; +void TestsWidget::on_btnDownload_clicked() +{ + if (m_tests.isEmpty() || ui->tableTests->selectedItems().isEmpty()) return; // Should not happen - // Test infos - if (test->hasFieldName("test_session_name")){ - csv_writer << test->getFieldValue("test_session_name").toString(); - } - csv_writer << '\t'; - if (test->hasFieldName("id_test_type")){ - csv_writer << test->getFieldValue("id_test_type").toString(); - } - csv_writer << '\t'; - if (test->hasFieldName("test_status")){ - csv_writer << test->getFieldValue("test_status").toString(); - } - csv_writer << '\t'; - if (test->hasFieldName("test_name")){ - csv_writer << test->getFieldValue("test_name").toString(); - } - csv_writer << '\t'; - if (test->hasFieldName("test_datetime")){ - csv_writer << test->getFieldValue("test_datetime").toDateTime().toString("yyyy-MM-dd"); - csv_writer << '\t'; - csv_writer << test->getFieldValue("test_datetime").toDateTime().toString("hh:mm:ss"); - } - csv_writer << '\t'; - if (test->hasFieldName("test_summary")){ - QJsonDocument test_summary_doc = QJsonDocument::fromJson(test->getFieldValue("test_summary").toString().toUtf8()); - if (!test_summary_doc.isNull()){ - QVariantMap test_summary = test_summary_doc.object().toVariantMap(); - for (const QString &var: qAsConst(variables)){ - if (test_summary.contains(var)){ - csv_writer < tests; + // Browse table items to have sorted list (the order it will be exported) + for (int i=0; itableTests->selectedItems().count(); i++){ + if (m_tableTests.key(ui->tableTests->selectedItems().at(i), nullptr) != nullptr){ + TeraData* test = m_tests[m_tableTests.key(ui->tableTests->selectedItems().at(i))]; + tests.append(test); } - csv_file.close(); - }else{ - msg.showError(tr("Erreur d'exportation"), tr("Impossible d'exporter les données dans le fichier.") + "\n\r" + tr("Le fichier est peut-être ouvert dans une autre application?")); - return; } - - msg.showInfo(tr("Exportation terminée"), tr("L'exportation des données a été complétée avec succès.")); - - + downloadTests(tests); } diff --git a/client/src/widgets/TestsWidget.h b/client/src/widgets/TestsWidget.h index 5ed5d561..12cc64ae 100644 --- a/client/src/widgets/TestsWidget.h +++ b/client/src/widgets/TestsWidget.h @@ -81,23 +81,22 @@ class TestsWidget : public QWidget void setLoading(const bool &loading, const QString &msg = QString(), const bool &hide_ui = false); void showTestSummary(const QString& test_uuid); - void updateTestsCountLabel(); + void downloadTests(const QList tests); + private slots: void processTestsReply(QList tests, QUrlQuery reply_query); void processServicesReply(QList services, QUrlQuery reply_query); void comDeleteDataReply(QString path, int id); - void nextMessageWasShown(Message current_message); - void on_btnDelete_clicked(); - void on_tableTests_itemSelectionChanged(); void on_btnDownloadAll_clicked(); + void on_btnDownload_clicked(); signals: void testCountChanged(int new_count); diff --git a/client/src/widgets/TestsWidget.ui b/client/src/widgets/TestsWidget.ui index 8f444253..f575c5f2 100644 --- a/client/src/widgets/TestsWidget.ui +++ b/client/src/widgets/TestsWidget.ui @@ -209,6 +209,38 @@ + + + + false + + + + 0 + 0 + + + + PointingHandCursor + + + Exporter la sélection + + + + :/icons/download.png:/icons/download.png + + + + 24 + 24 + + + + Qt::ToolButtonTextBesideIcon + + + @@ -218,7 +250,7 @@ PointingHandCursor - Exporter + Tout exporter diff --git a/docs/Home.rst b/docs/Home.rst new file mode 100644 index 00000000..6e034020 --- /dev/null +++ b/docs/Home.rst @@ -0,0 +1,3 @@ +.. include:: ../README_fr.md + :parser: myst_parser.sphinx_ + :start-line: 2 \ No newline at end of file diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..d4bb2cbb --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 00000000..c5efc315 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,41 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +project = 'OpenTeraPlus' +copyright = '2023, Simon Brière, Dominic Létourneau' +author = 'Simon Brière, Dominic Létourneau' +release = '1.1.3' +version = release + +html_logo = 'logo/LogoOpenTeraPlus.png' + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = ['myst_parser', 'sphinx_rtd_theme', 'sphinxcontrib.openapi', 'sphinx.ext.autosummary'] + +templates_path = ['_templates'] +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'venv'] + +locale_dirs = ['locale/'] +language = 'fr' # Default language + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_show_sourcelink = False +html_theme = 'sphinx_rtd_theme' +html_theme_options = { + 'navigation_depth': -1, + 'display_version': True +} + +source_suffix = { + '.rst': 'restructuredtext', + '.md': 'markdown', +} diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 00000000..9b02804a --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,18 @@ +.. toctree:: + :maxdepth: 2 + :hidden: + :caption: Introduction + +.. toctree:: + :maxdepth: 5 + :hidden: + :caption: Manuel d'utilisation + + users/Troubleshooting + +.. toctree:: + :maxdepth: 5 + :hidden: + :caption: Développeurs + +.. include:: Home.rst diff --git a/docs/locale/en/LC_MESSAGES/index.po b/docs/locale/en/LC_MESSAGES/index.po new file mode 100644 index 00000000..ef591f1a --- /dev/null +++ b/docs/locale/en/LC_MESSAGES/index.po @@ -0,0 +1,56 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2023, Simon Brière, Dominic Létourneau +# This file is distributed under the same license as the OpenTeraPlus +# package. +# FIRST AUTHOR , 2023. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: OpenTeraPlus 1.1.3\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-10-30 09:14-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.13.1\n" + +#: ../../index.rst:1 +msgid "Introduction" +msgstr "" + +#: ../../index.rst:6 +msgid "Utilisateurs" +msgstr "" + +#: ../../index.rst:13 +msgid "Développeurs" +msgstr "" + +#: ../../index.rst:19 +msgid "Indices and tables" +msgstr "" + +#: ../../index.rst:21 +msgid ":ref:`genindex`" +msgstr "" + +#: ../../index.rst:22 +msgid ":ref:`modindex`" +msgstr "" + +#: ../../index.rst:23 +msgid ":ref:`search`" +msgstr "" + +#~ msgid "Contents:" +#~ msgstr "" + +#~ msgid "Welcome to OpenTeraPlus's documentation!" +#~ msgstr "" + diff --git a/docs/locale/en/LC_MESSAGES/users.po b/docs/locale/en/LC_MESSAGES/users.po new file mode 100644 index 00000000..2786b59d --- /dev/null +++ b/docs/locale/en/LC_MESSAGES/users.po @@ -0,0 +1,502 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2023, Simon Brière, Dominic Létourneau +# This file is distributed under the same license as the OpenTeraPlus +# package. +# FIRST AUTHOR , 2023. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: OpenTeraPlus 1.1.3\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-10-30 09:14-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.13.1\n" + +#: ../../users/Troubleshooting.md:1 +msgid "Problèmes et solutions" +msgstr "" + +#: ../../users/Troubleshooting.md:3 +msgid "Démarrage du logiciel" +msgstr "" + +#: ../../users/Troubleshooting.md:4 +msgid "Connexion" +msgstr "" + +#: ../../users/Troubleshooting.md:5 +msgid "1. *Utilisateur ou mot de passe invalide*" +msgstr "" + +#: ../../users/Troubleshooting.md:6 +msgid "Vérifiez votre code utilisateur et votre mot de passe" +msgstr "" + +#: ../../users/Troubleshooting.md:7 +msgid "Validez auprès de votre administrateur si votre compte est toujours actif" +msgstr "" + +#: ../../users/Troubleshooting.md:9 +msgid "2. *Impossible de rejoindre le serveur*" +msgstr "" + +#: ../../users/Troubleshooting.md:10 +msgid "" +"Vérifiez que votre ordinateur est bien connecté à Internet (en ouvrant un" +" navigateur et en effectuant une recherche, par exemple)" +msgstr "" + +#: ../../users/Troubleshooting.md:11 +msgid "" +"Assurez-vous que votre ordinateur est bien connecté sur le bon réseau " +"Internet" +msgstr "" + +#: ../../users/Troubleshooting.md:12 +msgid "" +"Si nécessaire, assurez-vous que les politiques en place dans votre " +"établissement permettent bien l’utilisation du logiciel (i.e. aucun port " +"bloqué, aucun anti-virus qui empêche l'exécution adéquate du logiciel)" +msgstr "" + +#: ../../users/Troubleshooting.md:14 +msgid "3. *Message d’erreur : « Déjà connecté au logiciel »*" +msgstr "" + +#: ../../users/Troubleshooting.md:15 +msgid "Vérifiez que le logiciel n’est pas déjà ouvert sur votre poste de travail" +msgstr "" + +#: ../../users/Troubleshooting.md:16 +msgid "" +"Si vous utilisez plusieurs postes, assurez-vous que vous n’êtes pas " +"connecté sur un autre poste" +msgstr "" + +#: ../../users/Troubleshooting.md:17 +msgid "Assurez-vous que personne d’autre n’utilise votre compte utilisateur" +msgstr "" + +#: ../../users/Troubleshooting.md:18 +msgid "" +"Si vous ne savez pas où vous êtes connecté, contactez votre " +"administrateur qui pourra désactiver votre compte, changer le mot de " +"passe et le réactiver" +msgstr "" + +#: ../../users/Troubleshooting.md:20 +msgid "Séance de télésanté" +msgstr "" + +#: ../../users/Troubleshooting.md:21 +msgid "Téléréadaptation ou autre, basée sur le service de *VideoRehab*." +msgstr "" + +#: ../../users/Troubleshooting.md:23 +msgid "Démarrage de séance" +msgstr "" + +#: ../../users/Troubleshooting.md:25 +msgid "1. *Pas d’image vidéo dans le vestibule de la séance*" +msgstr "" + +#: ../../users/Troubleshooting.md:26 +msgid "Vérifiez qu’il n’y a pas de cache-caméra sur votre caméra" +msgstr "" + +#: ../../users/Troubleshooting.md:27 +msgid "" +"Vérifiez que votre caméra est bien branchée sur votre appareil s’il " +"s’agit d’une caméra externe" +msgstr "" + +#: ../../users/Troubleshooting.md:28 +msgid "" +"Assurez-vous qu’aucun autre logiciel présentement ouvert n’utilise votre " +"caméra (comme Microsoft Teams, Zoom ou autres)" +msgstr "" + +#: ../../users/Troubleshooting.md:29 +msgid "" +"Assurez-vous que la bonne source vidéo est sélectionnée (en bas de la " +"zone d’image)" +msgstr "" + +#: ../../users/Troubleshooting.md:31 +msgid "2. *Pas de son dans le vestibule de séance*" +msgstr "" + +#: ../../users/Troubleshooting.md:32 +msgid "La barre de son à gauche ne s’affiche ou ne bouge pas lorsque vous parlez." +msgstr "" + +#: ../../users/Troubleshooting.md:33 +msgid "" +"Si vous utilisez un microphone externe, assurez-vous qu’il est bien " +"branché" +msgstr "" + +#: ../../users/Troubleshooting.md:34 +msgid "" +"S’il y a lieu, assurez-vous que le bouton « mute » n’est pas activé sur " +"votre microphone" +msgstr "" + +#: ../../users/Troubleshooting.md:35 +msgid "" +"Assurez-vous que le bon microphone est sélectionné (en bas de la zone " +"d’image)" +msgstr "" + +#: ../../users/Troubleshooting.md:37 +msgid "3. *Mauvaise caméra*" +msgstr "" + +#: ../../users/Troubleshooting.md:38 +msgid "" +"Dans le vestibule, sous l’image de la caméra, sélectionnez la bonne " +"caméra. N’oubliez pas d’enregistrer les paramètres par défaut si vous " +"souhaitez toujours utiliser cette caméra !" +msgstr "" + +#: ../../users/Troubleshooting.md:40 +msgid "4. *Participant ne se joint jamais à la séance*" +msgstr "" + +#: ../../users/Troubleshooting.md:41 +msgid "" +"Assurez-vous que le participant était bien en ligne (icône verte) au " +"démarrage de la séance." +msgstr "" + +#: ../../users/Troubleshooting.md:42 +msgid "Contactez le participant et vérifiez :" +msgstr "" + +#: ../../users/Troubleshooting.md:43 +msgid "Qu’il a bien ouvert le lien fourni et qu’il se voit sur son écran" +msgstr "" + +#: ../../users/Troubleshooting.md:44 +msgid "" +"Qu’il ait bien accepté de partager son microphone et sa caméra lorsque la" +" question lui sera demandée" +msgstr "" + +#: ../../users/Troubleshooting.md:46 +msgid "Pendant la séance" +msgstr "" + +#: ../../users/Troubleshooting.md:48 +msgid "1. *Le participant n’entend pas ce que vous dites*" +msgstr "" + +#: ../../users/Troubleshooting.md:49 +msgid "" +"Assurez-vous que votre microphone est bien allumé et pas en « mute » " +"(voir « Pas de son dans le vestibule de séance ») plus haut" +msgstr "" + +#: ../../users/Troubleshooting.md:50 +msgid "" +"Assurez-vous que vous n’avez pas activé l’icône pour désactiver le " +"microphone dans le logiciel" +msgstr "" + +#: ../../users/Troubleshooting.md:51 +msgid "" +"Assurez-vous que la bonne source audio est sélectionnée dans les " +"paramètres (icône « engrenage » sous votre image)." +msgstr "" + +#: ../../users/Troubleshooting.md:52 ../../users/Troubleshooting.md:67 +#: ../../users/Troubleshooting.md:74 +msgid "Cliquez sur le bouton « Reconnecter » en haut" +msgstr "" + +#: ../../users/Troubleshooting.md:54 +msgid "2. *Vous n’entendez pas le participant*" +msgstr "" + +#: ../../users/Troubleshooting.md:55 +msgid "" +"Assurez-vous que le volume de votre poste est bien allumé et à une valeur" +" plus grande que 0" +msgstr "" + +#: ../../users/Troubleshooting.md:56 +msgid "" +"Assurez-vous que votre icône « haut-parleur » dans le logiciel est bien " +"active" +msgstr "" + +#: ../../users/Troubleshooting.md:57 +msgid "" +"Si le participant utilise un microphone externe (tel un casque d’écoute)," +" assurez-vous :" +msgstr "" + +#: ../../users/Troubleshooting.md:58 +msgid "Qu’il est bien allumé" +msgstr "" + +#: ../../users/Troubleshooting.md:59 +msgid "Qu’il ne soit pas à « mute »" +msgstr "" + +#: ../../users/Troubleshooting.md:60 +msgid "Bien connecté à l’appareil" +msgstr "" + +#: ../../users/Troubleshooting.md:61 +msgid "" +"Assurez-vous que le participant a bien la bonne source de microphone en " +"cliquant sur l’icône « engrenage » sous son image et en changeant sa " +"source audio principale" +msgstr "" + +#: ../../users/Troubleshooting.md:63 +msgid "3. *Le participant ne vous voit pas*" +msgstr "" + +#: ../../users/Troubleshooting.md:64 +msgid "Assurez-vous que vous voyez votre propre image" +msgstr "" + +#: ../../users/Troubleshooting.md:65 +msgid "" +"Si ce n’est pas le cas, validez les étapes identifiées plus haut sous « " +"Pas d’image vidéo dans le vestibule de la séance »)" +msgstr "" + +#: ../../users/Troubleshooting.md:66 +msgid "Assurez-vous que le bouton « caméra » est bien activé dans le logiciel" +msgstr "" + +#: ../../users/Troubleshooting.md:68 ../../users/Troubleshooting.md:75 +#: ../../users/Troubleshooting.md:91 ../../users/Troubleshooting.md:112 +msgid "" +"Demandez au participant de rafraichir sa page de son côté (la façon de " +"faire variera selon l’appareil et le navigateur utilisé) ou de cliquer " +"sur le bouton « Déconnecter » en haut à droite et de suivre à nouveau le " +"lien que vous lui avez envoyé" +msgstr "" + +#: ../../users/Troubleshooting.md:70 +msgid "4. *Vous ne voyez pas l’image du participant, mais vous l’entendez*" +msgstr "" + +#: ../../users/Troubleshooting.md:71 +msgid "Si le participant vous entend, validez qu’il se voit" +msgstr "" + +#: ../../users/Troubleshooting.md:72 +msgid "" +"S’il ne se voit pas, assurez-vous que l’icône « caméra » sous son image " +"est bien activée" +msgstr "" + +#: ../../users/Troubleshooting.md:73 +msgid "" +"Dans les paramètres du participant (icône « engrenage » sous son image), " +"assurez-vous que la bonne caméra est sélectionnée" +msgstr "" + +#: ../../users/Troubleshooting.md:77 +msgid "5. *Coupures de son / qualité vidéo dégradée*" +msgstr "" + +#: ../../users/Troubleshooting.md:78 +msgid "" +"Ces problèmes sont dus à une mauvaise qualité de connexion Internet, que " +"ce soit du côté du professionnel ou du participant. Quelques ajustements " +"et validations pouvant être faites :" +msgstr "" + +#: ../../users/Troubleshooting.md:79 +msgid "" +"Si vous êtes sur un réseau sans fil et qu’un réseau filaire est " +"disponible, tentez de brancher votre appareil sur ce réseau" +msgstr "" + +#: ../../users/Troubleshooting.md:80 +msgid "" +"Assurez-vous que personne d’autre au domicile du participant n’utilise la" +" connexion Internet en même temps que la séance pour y faire du " +"téléchargement, du visionnement en ligne (« streaming ») ou jouer à des " +"jeux vidéo en ligne" +msgstr "" + +#: ../../users/Troubleshooting.md:81 +msgid "" +"Si le participant se connecte à Internet via une connexion de type « " +"cellulaire », demandez-lui, si possible de se connecter avec une " +"connexion de type sans-fil (non-cellulaire) ou filaire" +msgstr "" + +#: ../../users/Troubleshooting.md:82 +msgid "" +"Si possible, demandez au participant de changer de pièce. Idéalement, " +"s’il sait où se trouve son point d’accès Internet (routeur et/ou modem), " +"demandez-lui de s’y rapprocher." +msgstr "" + +#: ../../users/Troubleshooting.md:83 +msgid "" +"En dernier recours, activez et désactivez les caméras dans le logiciel et" +" activez-les uniquement si nécessaire" +msgstr "" + +#: ../../users/Troubleshooting.md:84 +msgid "Si le problème persiste :" +msgstr "" + +#: ../../users/Troubleshooting.md:85 +msgid "" +"Si le problème revient constamment avec tous les participants, il faudra " +"peut-être vérifier la connexion Internet du poste clinicien" +msgstr "" + +#: ../../users/Troubleshooting.md:86 +msgid "" +"Si le problème revient constamment avec un participant donné, le problème" +" se trouve probablement de son côté et vérifier la connexion Internet de " +"celui-ci. Un redémarrage du modem / routeur peut parfois résoudre les " +"problèmes." +msgstr "" + +#: ../../users/Troubleshooting.md:88 +msgid "6. *Tout autre problème*" +msgstr "" + +#: ../../users/Troubleshooting.md:89 +msgid "" +"La plupart des autres problèmes pouvant survenir peuvent être réglés avec" +" ces étapes :" +msgstr "" + +#: ../../users/Troubleshooting.md:90 +msgid "Cliquez sur le bouton « Reconnecter » en haut à gauche" +msgstr "" + +#: ../../users/Troubleshooting.md:92 +msgid "Terminez la séance et démarrez une nouvelle séance" +msgstr "" + +#: ../../users/Troubleshooting.md:94 +msgid "Problèmes du côté du participant" +msgstr "" + +#: ../../users/Troubleshooting.md:96 +msgid "1. *Message d’erreur : « Navigateur non-supporté »*" +msgstr "" + +#: ../../users/Troubleshooting.md:97 +msgid "" +"Demandez au participant d'utiliser un autre navigateur (tels que Firefox," +" Chrome ou Safari)" +msgstr "" + +#: ../../users/Troubleshooting.md:98 +msgid "Si le participant est sur un appareil mobile:" +msgstr "" + +#: ../../users/Troubleshooting.md:99 +msgid "Demandez-lui de faire les mises à jour logicielles, s'il y a lieu" +msgstr "" + +#: ../../users/Troubleshooting.md:100 +msgid "" +"S'il est sur un appareil de type Apple, demandez-lui d'utiliser le " +"navigateur *Safari* pour ouvrir le lien. Sur ces appareils, aucun autre " +"navigateur n'est supporté." +msgstr "" + +#: ../../users/Troubleshooting.md:101 +msgid "" +"S'il n'y a pas de mise à jour disponible et que son appareil date de " +"plusieurs années, il est possible qu'il ne soit plus supporté. Demandez-" +"lui d'utilisateur un autre appareil si possible." +msgstr "" + +#: ../../users/Troubleshooting.md:103 +msgid "2. *Pas d’image vidéo en cliquant sur le lien*" +msgstr "" + +#: ../../users/Troubleshooting.md:104 +msgid "Assurez-vous qu'il n'a pas de cache-caméra devant sa caméra" +msgstr "" + +#: ../../users/Troubleshooting.md:105 +msgid "" +"Validez, avec le participant, qu'il a bien répondu \"Oui\" à la question " +"qui demandait l'accès à sa caméra" +msgstr "" + +#: ../../users/Troubleshooting.md:106 +msgid "S'il a répondu non:" +msgstr "" + +#: ../../users/Troubleshooting.md:107 +msgid "Tentez de rafraichir la page du navigateur" +msgstr "" + +#: ../../users/Troubleshooting.md:108 +msgid "" +"Si la question ne réapparait pas, la procédure variera selon les " +"navigateurs. Il faut généralement cliquer sur l'icône \"Caméra\" en haut " +"près de l'adresse et activer celle-ci." +msgstr "" + +#: ../../users/Troubleshooting.md:109 +msgid "Assurez-vous qu'il utilise un navigateur compatible (voir ci-dessus)" +msgstr "" + +#: ../../users/Troubleshooting.md:111 +msgid "" +"3. *Même si le professionnel démarre la séance, celle-ci ne démarre pas " +"pour le participant*" +msgstr "" + +#: ../../users/Troubleshooting.md:113 +msgid "" +"En dernier recours, demandez au participant de redémarrer son appareil et" +" de suivre à nouveau le lien." +msgstr "" + +#: ../../users/Troubleshooting.md:115 +msgid "4. *Le son est faible*" +msgstr "" + +#: ../../users/Troubleshooting.md:116 +msgid "Vérifiez le volume de l'appareil du participant" +msgstr "" + +#: ../../users/Troubleshooting.md:117 +msgid "" +"Même si le volume est au maximum, il se pourrait que le son soit faible. " +"Vérifiez ces éléments:" +msgstr "" + +#: ../../users/Troubleshooting.md:118 +msgid "" +"Assurez-vous de minimiser le bruit de fond de votre côté (personnes qui " +"parlent, musique, bruit constant)" +msgstr "" + +#: ../../users/Troubleshooting.md:119 +msgid "Assurez-vous de minimiser le bruit de font du participant" +msgstr "" + +#: ../../users/Troubleshooting.md:120 +msgid "Branchez un haut-parleur externe ou utilisez un casque d'écoute" +msgstr "" + diff --git a/docs/locale/fr/LC_MESSAGES/index.mo b/docs/locale/fr/LC_MESSAGES/index.mo new file mode 100644 index 00000000..226e2fd7 Binary files /dev/null and b/docs/locale/fr/LC_MESSAGES/index.mo differ diff --git a/docs/locale/fr/LC_MESSAGES/index.po b/docs/locale/fr/LC_MESSAGES/index.po new file mode 100644 index 00000000..4c68b318 --- /dev/null +++ b/docs/locale/fr/LC_MESSAGES/index.po @@ -0,0 +1,56 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2023, Simon Brière, Dominic Létourneau +# This file is distributed under the same license as the OpenTeraPlus +# package. +# FIRST AUTHOR , 2023. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: OpenTeraPlus 1.1.3\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-10-30 09:14-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: fr\n" +"Language-Team: fr \n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.13.1\n" + +#: ../../index.rst:1 +msgid "Introduction" +msgstr "" + +#: ../../index.rst:6 +msgid "Utilisateurs" +msgstr "" + +#: ../../index.rst:13 +msgid "Développeurs" +msgstr "" + +#: ../../index.rst:19 +msgid "Indices and tables" +msgstr "" + +#: ../../index.rst:21 +msgid ":ref:`genindex`" +msgstr "" + +#: ../../index.rst:22 +msgid ":ref:`modindex`" +msgstr "" + +#: ../../index.rst:23 +msgid ":ref:`search`" +msgstr "" + +#~ msgid "Contents:" +#~ msgstr "" + +#~ msgid "Welcome to OpenTeraPlus's documentation!" +#~ msgstr "" + diff --git a/docs/locale/fr/LC_MESSAGES/users.mo b/docs/locale/fr/LC_MESSAGES/users.mo new file mode 100644 index 00000000..226e2fd7 Binary files /dev/null and b/docs/locale/fr/LC_MESSAGES/users.mo differ diff --git a/docs/locale/fr/LC_MESSAGES/users.po b/docs/locale/fr/LC_MESSAGES/users.po new file mode 100644 index 00000000..30340a9b --- /dev/null +++ b/docs/locale/fr/LC_MESSAGES/users.po @@ -0,0 +1,502 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2023, Simon Brière, Dominic Létourneau +# This file is distributed under the same license as the OpenTeraPlus +# package. +# FIRST AUTHOR , 2023. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: OpenTeraPlus 1.1.3\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-10-30 09:14-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: fr\n" +"Language-Team: fr \n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.13.1\n" + +#: ../../users/Troubleshooting.md:1 +msgid "Problèmes et solutions" +msgstr "" + +#: ../../users/Troubleshooting.md:3 +msgid "Démarrage du logiciel" +msgstr "" + +#: ../../users/Troubleshooting.md:4 +msgid "Connexion" +msgstr "" + +#: ../../users/Troubleshooting.md:5 +msgid "1. *Utilisateur ou mot de passe invalide*" +msgstr "" + +#: ../../users/Troubleshooting.md:6 +msgid "Vérifiez votre code utilisateur et votre mot de passe" +msgstr "" + +#: ../../users/Troubleshooting.md:7 +msgid "Validez auprès de votre administrateur si votre compte est toujours actif" +msgstr "" + +#: ../../users/Troubleshooting.md:9 +msgid "2. *Impossible de rejoindre le serveur*" +msgstr "" + +#: ../../users/Troubleshooting.md:10 +msgid "" +"Vérifiez que votre ordinateur est bien connecté à Internet (en ouvrant un" +" navigateur et en effectuant une recherche, par exemple)" +msgstr "" + +#: ../../users/Troubleshooting.md:11 +msgid "" +"Assurez-vous que votre ordinateur est bien connecté sur le bon réseau " +"Internet" +msgstr "" + +#: ../../users/Troubleshooting.md:12 +msgid "" +"Si nécessaire, assurez-vous que les politiques en place dans votre " +"établissement permettent bien l’utilisation du logiciel (i.e. aucun port " +"bloqué, aucun anti-virus qui empêche l'exécution adéquate du logiciel)" +msgstr "" + +#: ../../users/Troubleshooting.md:14 +msgid "3. *Message d’erreur : « Déjà connecté au logiciel »*" +msgstr "" + +#: ../../users/Troubleshooting.md:15 +msgid "Vérifiez que le logiciel n’est pas déjà ouvert sur votre poste de travail" +msgstr "" + +#: ../../users/Troubleshooting.md:16 +msgid "" +"Si vous utilisez plusieurs postes, assurez-vous que vous n’êtes pas " +"connecté sur un autre poste" +msgstr "" + +#: ../../users/Troubleshooting.md:17 +msgid "Assurez-vous que personne d’autre n’utilise votre compte utilisateur" +msgstr "" + +#: ../../users/Troubleshooting.md:18 +msgid "" +"Si vous ne savez pas où vous êtes connecté, contactez votre " +"administrateur qui pourra désactiver votre compte, changer le mot de " +"passe et le réactiver" +msgstr "" + +#: ../../users/Troubleshooting.md:20 +msgid "Séance de télésanté" +msgstr "" + +#: ../../users/Troubleshooting.md:21 +msgid "Téléréadaptation ou autre, basée sur le service de *VideoRehab*." +msgstr "" + +#: ../../users/Troubleshooting.md:23 +msgid "Démarrage de séance" +msgstr "" + +#: ../../users/Troubleshooting.md:25 +msgid "1. *Pas d’image vidéo dans le vestibule de la séance*" +msgstr "" + +#: ../../users/Troubleshooting.md:26 +msgid "Vérifiez qu’il n’y a pas de cache-caméra sur votre caméra" +msgstr "" + +#: ../../users/Troubleshooting.md:27 +msgid "" +"Vérifiez que votre caméra est bien branchée sur votre appareil s’il " +"s’agit d’une caméra externe" +msgstr "" + +#: ../../users/Troubleshooting.md:28 +msgid "" +"Assurez-vous qu’aucun autre logiciel présentement ouvert n’utilise votre " +"caméra (comme Microsoft Teams, Zoom ou autres)" +msgstr "" + +#: ../../users/Troubleshooting.md:29 +msgid "" +"Assurez-vous que la bonne source vidéo est sélectionnée (en bas de la " +"zone d’image)" +msgstr "" + +#: ../../users/Troubleshooting.md:31 +msgid "2. *Pas de son dans le vestibule de séance*" +msgstr "" + +#: ../../users/Troubleshooting.md:32 +msgid "La barre de son à gauche ne s’affiche ou ne bouge pas lorsque vous parlez." +msgstr "" + +#: ../../users/Troubleshooting.md:33 +msgid "" +"Si vous utilisez un microphone externe, assurez-vous qu’il est bien " +"branché" +msgstr "" + +#: ../../users/Troubleshooting.md:34 +msgid "" +"S’il y a lieu, assurez-vous que le bouton « mute » n’est pas activé sur " +"votre microphone" +msgstr "" + +#: ../../users/Troubleshooting.md:35 +msgid "" +"Assurez-vous que le bon microphone est sélectionné (en bas de la zone " +"d’image)" +msgstr "" + +#: ../../users/Troubleshooting.md:37 +msgid "3. *Mauvaise caméra*" +msgstr "" + +#: ../../users/Troubleshooting.md:38 +msgid "" +"Dans le vestibule, sous l’image de la caméra, sélectionnez la bonne " +"caméra. N’oubliez pas d’enregistrer les paramètres par défaut si vous " +"souhaitez toujours utiliser cette caméra !" +msgstr "" + +#: ../../users/Troubleshooting.md:40 +msgid "4. *Participant ne se joint jamais à la séance*" +msgstr "" + +#: ../../users/Troubleshooting.md:41 +msgid "" +"Assurez-vous que le participant était bien en ligne (icône verte) au " +"démarrage de la séance." +msgstr "" + +#: ../../users/Troubleshooting.md:42 +msgid "Contactez le participant et vérifiez :" +msgstr "" + +#: ../../users/Troubleshooting.md:43 +msgid "Qu’il a bien ouvert le lien fourni et qu’il se voit sur son écran" +msgstr "" + +#: ../../users/Troubleshooting.md:44 +msgid "" +"Qu’il ait bien accepté de partager son microphone et sa caméra lorsque la" +" question lui sera demandée" +msgstr "" + +#: ../../users/Troubleshooting.md:46 +msgid "Pendant la séance" +msgstr "" + +#: ../../users/Troubleshooting.md:48 +msgid "1. *Le participant n’entend pas ce que vous dites*" +msgstr "" + +#: ../../users/Troubleshooting.md:49 +msgid "" +"Assurez-vous que votre microphone est bien allumé et pas en « mute » " +"(voir « Pas de son dans le vestibule de séance ») plus haut" +msgstr "" + +#: ../../users/Troubleshooting.md:50 +msgid "" +"Assurez-vous que vous n’avez pas activé l’icône pour désactiver le " +"microphone dans le logiciel" +msgstr "" + +#: ../../users/Troubleshooting.md:51 +msgid "" +"Assurez-vous que la bonne source audio est sélectionnée dans les " +"paramètres (icône « engrenage » sous votre image)." +msgstr "" + +#: ../../users/Troubleshooting.md:52 ../../users/Troubleshooting.md:67 +#: ../../users/Troubleshooting.md:74 +msgid "Cliquez sur le bouton « Reconnecter » en haut" +msgstr "" + +#: ../../users/Troubleshooting.md:54 +msgid "2. *Vous n’entendez pas le participant*" +msgstr "" + +#: ../../users/Troubleshooting.md:55 +msgid "" +"Assurez-vous que le volume de votre poste est bien allumé et à une valeur" +" plus grande que 0" +msgstr "" + +#: ../../users/Troubleshooting.md:56 +msgid "" +"Assurez-vous que votre icône « haut-parleur » dans le logiciel est bien " +"active" +msgstr "" + +#: ../../users/Troubleshooting.md:57 +msgid "" +"Si le participant utilise un microphone externe (tel un casque d’écoute)," +" assurez-vous :" +msgstr "" + +#: ../../users/Troubleshooting.md:58 +msgid "Qu’il est bien allumé" +msgstr "" + +#: ../../users/Troubleshooting.md:59 +msgid "Qu’il ne soit pas à « mute »" +msgstr "" + +#: ../../users/Troubleshooting.md:60 +msgid "Bien connecté à l’appareil" +msgstr "" + +#: ../../users/Troubleshooting.md:61 +msgid "" +"Assurez-vous que le participant a bien la bonne source de microphone en " +"cliquant sur l’icône « engrenage » sous son image et en changeant sa " +"source audio principale" +msgstr "" + +#: ../../users/Troubleshooting.md:63 +msgid "3. *Le participant ne vous voit pas*" +msgstr "" + +#: ../../users/Troubleshooting.md:64 +msgid "Assurez-vous que vous voyez votre propre image" +msgstr "" + +#: ../../users/Troubleshooting.md:65 +msgid "" +"Si ce n’est pas le cas, validez les étapes identifiées plus haut sous « " +"Pas d’image vidéo dans le vestibule de la séance »)" +msgstr "" + +#: ../../users/Troubleshooting.md:66 +msgid "Assurez-vous que le bouton « caméra » est bien activé dans le logiciel" +msgstr "" + +#: ../../users/Troubleshooting.md:68 ../../users/Troubleshooting.md:75 +#: ../../users/Troubleshooting.md:91 ../../users/Troubleshooting.md:112 +msgid "" +"Demandez au participant de rafraichir sa page de son côté (la façon de " +"faire variera selon l’appareil et le navigateur utilisé) ou de cliquer " +"sur le bouton « Déconnecter » en haut à droite et de suivre à nouveau le " +"lien que vous lui avez envoyé" +msgstr "" + +#: ../../users/Troubleshooting.md:70 +msgid "4. *Vous ne voyez pas l’image du participant, mais vous l’entendez*" +msgstr "" + +#: ../../users/Troubleshooting.md:71 +msgid "Si le participant vous entend, validez qu’il se voit" +msgstr "" + +#: ../../users/Troubleshooting.md:72 +msgid "" +"S’il ne se voit pas, assurez-vous que l’icône « caméra » sous son image " +"est bien activée" +msgstr "" + +#: ../../users/Troubleshooting.md:73 +msgid "" +"Dans les paramètres du participant (icône « engrenage » sous son image), " +"assurez-vous que la bonne caméra est sélectionnée" +msgstr "" + +#: ../../users/Troubleshooting.md:77 +msgid "5. *Coupures de son / qualité vidéo dégradée*" +msgstr "" + +#: ../../users/Troubleshooting.md:78 +msgid "" +"Ces problèmes sont dus à une mauvaise qualité de connexion Internet, que " +"ce soit du côté du professionnel ou du participant. Quelques ajustements " +"et validations pouvant être faites :" +msgstr "" + +#: ../../users/Troubleshooting.md:79 +msgid "" +"Si vous êtes sur un réseau sans fil et qu’un réseau filaire est " +"disponible, tentez de brancher votre appareil sur ce réseau" +msgstr "" + +#: ../../users/Troubleshooting.md:80 +msgid "" +"Assurez-vous que personne d’autre au domicile du participant n’utilise la" +" connexion Internet en même temps que la séance pour y faire du " +"téléchargement, du visionnement en ligne (« streaming ») ou jouer à des " +"jeux vidéo en ligne" +msgstr "" + +#: ../../users/Troubleshooting.md:81 +msgid "" +"Si le participant se connecte à Internet via une connexion de type « " +"cellulaire », demandez-lui, si possible de se connecter avec une " +"connexion de type sans-fil (non-cellulaire) ou filaire" +msgstr "" + +#: ../../users/Troubleshooting.md:82 +msgid "" +"Si possible, demandez au participant de changer de pièce. Idéalement, " +"s’il sait où se trouve son point d’accès Internet (routeur et/ou modem), " +"demandez-lui de s’y rapprocher." +msgstr "" + +#: ../../users/Troubleshooting.md:83 +msgid "" +"En dernier recours, activez et désactivez les caméras dans le logiciel et" +" activez-les uniquement si nécessaire" +msgstr "" + +#: ../../users/Troubleshooting.md:84 +msgid "Si le problème persiste :" +msgstr "" + +#: ../../users/Troubleshooting.md:85 +msgid "" +"Si le problème revient constamment avec tous les participants, il faudra " +"peut-être vérifier la connexion Internet du poste clinicien" +msgstr "" + +#: ../../users/Troubleshooting.md:86 +msgid "" +"Si le problème revient constamment avec un participant donné, le problème" +" se trouve probablement de son côté et vérifier la connexion Internet de " +"celui-ci. Un redémarrage du modem / routeur peut parfois résoudre les " +"problèmes." +msgstr "" + +#: ../../users/Troubleshooting.md:88 +msgid "6. *Tout autre problème*" +msgstr "" + +#: ../../users/Troubleshooting.md:89 +msgid "" +"La plupart des autres problèmes pouvant survenir peuvent être réglés avec" +" ces étapes :" +msgstr "" + +#: ../../users/Troubleshooting.md:90 +msgid "Cliquez sur le bouton « Reconnecter » en haut à gauche" +msgstr "" + +#: ../../users/Troubleshooting.md:92 +msgid "Terminez la séance et démarrez une nouvelle séance" +msgstr "" + +#: ../../users/Troubleshooting.md:94 +msgid "Problèmes du côté du participant" +msgstr "" + +#: ../../users/Troubleshooting.md:96 +msgid "1. *Message d’erreur : « Navigateur non-supporté »*" +msgstr "" + +#: ../../users/Troubleshooting.md:97 +msgid "" +"Demandez au participant d'utiliser un autre navigateur (tels que Firefox," +" Chrome ou Safari)" +msgstr "" + +#: ../../users/Troubleshooting.md:98 +msgid "Si le participant est sur un appareil mobile:" +msgstr "" + +#: ../../users/Troubleshooting.md:99 +msgid "Demandez-lui de faire les mises à jour logicielles, s'il y a lieu" +msgstr "" + +#: ../../users/Troubleshooting.md:100 +msgid "" +"S'il est sur un appareil de type Apple, demandez-lui d'utiliser le " +"navigateur *Safari* pour ouvrir le lien. Sur ces appareils, aucun autre " +"navigateur n'est supporté." +msgstr "" + +#: ../../users/Troubleshooting.md:101 +msgid "" +"S'il n'y a pas de mise à jour disponible et que son appareil date de " +"plusieurs années, il est possible qu'il ne soit plus supporté. Demandez-" +"lui d'utilisateur un autre appareil si possible." +msgstr "" + +#: ../../users/Troubleshooting.md:103 +msgid "2. *Pas d’image vidéo en cliquant sur le lien*" +msgstr "" + +#: ../../users/Troubleshooting.md:104 +msgid "Assurez-vous qu'il n'a pas de cache-caméra devant sa caméra" +msgstr "" + +#: ../../users/Troubleshooting.md:105 +msgid "" +"Validez, avec le participant, qu'il a bien répondu \"Oui\" à la question " +"qui demandait l'accès à sa caméra" +msgstr "" + +#: ../../users/Troubleshooting.md:106 +msgid "S'il a répondu non:" +msgstr "" + +#: ../../users/Troubleshooting.md:107 +msgid "Tentez de rafraichir la page du navigateur" +msgstr "" + +#: ../../users/Troubleshooting.md:108 +msgid "" +"Si la question ne réapparait pas, la procédure variera selon les " +"navigateurs. Il faut généralement cliquer sur l'icône \"Caméra\" en haut " +"près de l'adresse et activer celle-ci." +msgstr "" + +#: ../../users/Troubleshooting.md:109 +msgid "Assurez-vous qu'il utilise un navigateur compatible (voir ci-dessus)" +msgstr "" + +#: ../../users/Troubleshooting.md:111 +msgid "" +"3. *Même si le professionnel démarre la séance, celle-ci ne démarre pas " +"pour le participant*" +msgstr "" + +#: ../../users/Troubleshooting.md:113 +msgid "" +"En dernier recours, demandez au participant de redémarrer son appareil et" +" de suivre à nouveau le lien." +msgstr "" + +#: ../../users/Troubleshooting.md:115 +msgid "4. *Le son est faible*" +msgstr "" + +#: ../../users/Troubleshooting.md:116 +msgid "Vérifiez le volume de l'appareil du participant" +msgstr "" + +#: ../../users/Troubleshooting.md:117 +msgid "" +"Même si le volume est au maximum, il se pourrait que le son soit faible. " +"Vérifiez ces éléments:" +msgstr "" + +#: ../../users/Troubleshooting.md:118 +msgid "" +"Assurez-vous de minimiser le bruit de fond de votre côté (personnes qui " +"parlent, musique, bruit constant)" +msgstr "" + +#: ../../users/Troubleshooting.md:119 +msgid "Assurez-vous de minimiser le bruit de font du participant" +msgstr "" + +#: ../../users/Troubleshooting.md:120 +msgid "Branchez un haut-parleur externe ou utilisez un casque d'écoute" +msgstr "" + diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 00000000..954237b9 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 00000000..c16be0ca --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,5 @@ +sphinx +sphinx-intl +myst-parser +sphinx-rtd-theme +sphinxcontrib-openapi diff --git a/docs/users/Troubleshooting.md b/docs/users/Troubleshooting.md new file mode 100644 index 00000000..1ea687ee --- /dev/null +++ b/docs/users/Troubleshooting.md @@ -0,0 +1,120 @@ +# Problèmes et solutions + +## Démarrage du logiciel +### Connexion +#### 1. *Utilisateur ou mot de passe invalide* +- Vérifiez votre code utilisateur et votre mot de passe +- Validez auprès de votre administrateur si votre compte est toujours actif + +#### 2. *Impossible de rejoindre le serveur* +- Vérifiez que votre ordinateur est bien connecté à Internet (en ouvrant un navigateur et en effectuant une recherche, par exemple) +- Assurez-vous que votre ordinateur est bien connecté sur le bon réseau Internet +- Si nécessaire, assurez-vous que les politiques en place dans votre établissement permettent bien l’utilisation du logiciel (i.e. aucun port bloqué, aucun anti-virus qui empêche l'exécution adéquate du logiciel) + +#### 3. *Message d’erreur : « Déjà connecté au logiciel »* +- Vérifiez que le logiciel n’est pas déjà ouvert sur votre poste de travail +- Si vous utilisez plusieurs postes, assurez-vous que vous n’êtes pas connecté sur un autre poste +- Assurez-vous que personne d’autre n’utilise votre compte utilisateur +- Si vous ne savez pas où vous êtes connecté, contactez votre administrateur qui pourra désactiver votre compte, changer le mot de passe et le réactiver + +## Séance de télésanté +> Téléréadaptation ou autre, basée sur le service de *VideoRehab*. + +### Démarrage de séance + +#### 1. *Pas d’image vidéo dans le vestibule de la séance* +- Vérifiez qu’il n’y a pas de cache-caméra sur votre caméra +- Vérifiez que votre caméra est bien branchée sur votre appareil s’il s’agit d’une caméra externe +- Assurez-vous qu’aucun autre logiciel présentement ouvert n’utilise votre caméra (comme Microsoft Teams, Zoom ou autres) +- Assurez-vous que la bonne source vidéo est sélectionnée (en bas de la zone d’image) + +#### 2. *Pas de son dans le vestibule de séance* +> La barre de son à gauche ne s’affiche ou ne bouge pas lorsque vous parlez. +- Si vous utilisez un microphone externe, assurez-vous qu’il est bien branché +- S’il y a lieu, assurez-vous que le bouton « mute » n’est pas activé sur votre microphone +- Assurez-vous que le bon microphone est sélectionné (en bas de la zone d’image) + +#### 3. *Mauvaise caméra* +- Dans le vestibule, sous l’image de la caméra, sélectionnez la bonne caméra. N’oubliez pas d’enregistrer les paramètres par défaut si vous souhaitez toujours utiliser cette caméra ! + +#### 4. *Participant ne se joint jamais à la séance* +- Assurez-vous que le participant était bien en ligne (icône verte) au démarrage de la séance. +- Contactez le participant et vérifiez : + - Qu’il a bien ouvert le lien fourni et qu’il se voit sur son écran + - Qu’il ait bien accepté de partager son microphone et sa caméra lorsque la question lui sera demandée + +### Pendant la séance + +#### 1. *Le participant n’entend pas ce que vous dites* +- Assurez-vous que votre microphone est bien allumé et pas en « mute » (voir « Pas de son dans le vestibule de séance ») plus haut +- Assurez-vous que vous n’avez pas activé l’icône pour désactiver le microphone dans le logiciel +- Assurez-vous que la bonne source audio est sélectionnée dans les paramètres (icône « engrenage » sous votre image). +- Cliquez sur le bouton « Reconnecter » en haut + +#### 2. *Vous n’entendez pas le participant* +- Assurez-vous que le volume de votre poste est bien allumé et à une valeur plus grande que 0 +- Assurez-vous que votre icône « haut-parleur » dans le logiciel est bien active +- Si le participant utilise un microphone externe (tel un casque d’écoute), assurez-vous : + - Qu’il est bien allumé + - Qu’il ne soit pas à « mute » + - Bien connecté à l’appareil +- Assurez-vous que le participant a bien la bonne source de microphone en cliquant sur l’icône « engrenage » sous son image et en changeant sa source audio principale + +#### 3. *Le participant ne vous voit pas* +- Assurez-vous que vous voyez votre propre image + - Si ce n’est pas le cas, validez les étapes identifiées plus haut sous « Pas d’image vidéo dans le vestibule de la séance ») + - Assurez-vous que le bouton « caméra » est bien activé dans le logiciel +- Cliquez sur le bouton « Reconnecter » en haut +- Demandez au participant de rafraichir sa page de son côté (la façon de faire variera selon l’appareil et le navigateur utilisé) ou de cliquer sur le bouton « Déconnecter » en haut à droite et de suivre à nouveau le lien que vous lui avez envoyé + +#### 4. *Vous ne voyez pas l’image du participant, mais vous l’entendez* +- Si le participant vous entend, validez qu’il se voit + - S’il ne se voit pas, assurez-vous que l’icône « caméra » sous son image est bien activée + - Dans les paramètres du participant (icône « engrenage » sous son image), assurez-vous que la bonne caméra est sélectionnée +- Cliquez sur le bouton « Reconnecter » en haut +- Demandez au participant de rafraichir sa page de son côté (la façon de faire variera selon l’appareil et le navigateur utilisé) ou de cliquer sur le bouton « Déconnecter » en haut à droite et de suivre à nouveau le lien que vous lui avez envoyé + +#### 5. *Coupures de son / qualité vidéo dégradée* +Ces problèmes sont dus à une mauvaise qualité de connexion Internet, que ce soit du côté du professionnel ou du participant. Quelques ajustements et validations pouvant être faites : +- Si vous êtes sur un réseau sans fil et qu’un réseau filaire est disponible, tentez de brancher votre appareil sur ce réseau +- Assurez-vous que personne d’autre au domicile du participant n’utilise la connexion Internet en même temps que la séance pour y faire du téléchargement, du visionnement en ligne (« streaming ») ou jouer à des jeux vidéo en ligne +- Si le participant se connecte à Internet via une connexion de type « cellulaire », demandez-lui, si possible de se connecter avec une connexion de type sans-fil (non-cellulaire) ou filaire +- Si possible, demandez au participant de changer de pièce. Idéalement, s’il sait où se trouve son point d’accès Internet (routeur et/ou modem), demandez-lui de s’y rapprocher. +- En dernier recours, activez et désactivez les caméras dans le logiciel et activez-les uniquement si nécessaire +- Si le problème persiste : + - Si le problème revient constamment avec tous les participants, il faudra peut-être vérifier la connexion Internet du poste clinicien + - Si le problème revient constamment avec un participant donné, le problème se trouve probablement de son côté et vérifier la connexion Internet de celui-ci. Un redémarrage du modem / routeur peut parfois résoudre les problèmes. + +#### 6. *Tout autre problème* +La plupart des autres problèmes pouvant survenir peuvent être réglés avec ces étapes : +- Cliquez sur le bouton « Reconnecter » en haut à gauche +- Demandez au participant de rafraichir sa page de son côté (la façon de faire variera selon l’appareil et le navigateur utilisé) ou de cliquer sur le bouton « Déconnecter » en haut à droite et de suivre à nouveau le lien que vous lui avez envoyé +- Terminez la séance et démarrez une nouvelle séance + +### Problèmes du côté du participant + +#### 1. *Message d’erreur : « Navigateur non-supporté »* +- Demandez au participant d'utiliser un autre navigateur (tels que Firefox, Chrome ou Safari) +- Si le participant est sur un appareil mobile: + - Demandez-lui de faire les mises à jour logicielles, s'il y a lieu + - S'il est sur un appareil de type Apple, demandez-lui d'utiliser le navigateur *Safari* pour ouvrir le lien. Sur ces appareils, aucun autre navigateur n'est supporté. + - S'il n'y a pas de mise à jour disponible et que son appareil date de plusieurs années, il est possible qu'il ne soit plus supporté. Demandez-lui d'utilisateur un autre appareil si possible. + +#### 2. *Pas d’image vidéo en cliquant sur le lien* +- Assurez-vous qu'il n'a pas de cache-caméra devant sa caméra +- Validez, avec le participant, qu'il a bien répondu "Oui" à la question qui demandait l'accès à sa caméra + - S'il a répondu non: + - Tentez de rafraichir la page du navigateur + - Si la question ne réapparait pas, la procédure variera selon les navigateurs. Il faut généralement cliquer sur l'icône "Caméra" en haut près de l'adresse et activer celle-ci. +- Assurez-vous qu'il utilise un navigateur compatible (voir ci-dessus) + +#### 3. *Même si le professionnel démarre la séance, celle-ci ne démarre pas pour le participant* +- Demandez au participant de rafraichir sa page de son côté (la façon de faire variera selon l’appareil et le navigateur utilisé) ou de cliquer sur le bouton « Déconnecter » en haut à droite et de suivre à nouveau le lien que vous lui avez envoyé +- En dernier recours, demandez au participant de redémarrer son appareil et de suivre à nouveau le lien. + +#### 4. *Le son est faible* +- Vérifiez le volume de l'appareil du participant +- Même si le volume est au maximum, il se pourrait que le son soit faible. Vérifiez ces éléments: + - Assurez-vous de minimiser le bruit de fond de votre côté (personnes qui parlent, musique, bruit constant) + - Assurez-vous de minimiser le bruit de font du participant +- Branchez un haut-parleur externe ou utilisez un casque d'écoute diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 4a6957ee..7f46ad21 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -8,6 +8,8 @@ if (MSVC) /wd4910 # Ignore export warnings (C4910) with MSVC /wd4661 # Ignore no suitable definition for explicit template (C4661) /wd4251 # Ignore needs to have dll-interface + /wd4146 # Ignore unary minus operator applied to unsigned type, result still unsigned + /wd4267 # Ignore size_t to uint32_t conversion warnings ) endif (MSVC) @@ -22,13 +24,18 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON) #Disable tests for now... option(protobuf_BUILD_TESTS "" OFF) +set(ABSL_PROPAGATE_CXX_STD OFF) +set(protobuf_BUILD_EXAMPLES OFF) +set(protobuf_BUILD_LIBPROTOC OFF) +set(protobuf_BUILD_TESTS OFF) + #Use dynamic MSVC runtime if (MSVC) option(protobuf_MSVC_STATIC_RUNTIME "" OFF) endif(MSVC) -add_subdirectory(protobuf/cmake) +add_subdirectory(protobuf) #Generate messages add_subdirectory(messages/cpp) diff --git a/external/protobuf b/external/protobuf index 3d8943fe..2dca62f7 160000 --- a/external/protobuf +++ b/external/protobuf @@ -1 +1 @@ -Subproject commit 3d8943fea997ca18e022bbd6c074cb6aa6610fbe +Subproject commit 2dca62f7296e5b49d729f7384f975cecb38382a0 diff --git a/external/webcamoid/Driver/CMakeLists.txt b/external/webcamoid/Driver/CMakeLists.txt index a3d2785b..8364ddfc 100644 --- a/external/webcamoid/Driver/CMakeLists.txt +++ b/external/webcamoid/Driver/CMakeLists.txt @@ -1,22 +1,22 @@ MESSAGE ( STATUS "Buiding AkKysDriver...") -find_package(Qt5Core REQUIRED) -find_package(Qt5Concurrent REQUIRED) -find_package(Qt5Gui REQUIRED) -find_package(Qt5Qml REQUIRED) +find_package(Qt6Core REQUIRED) +find_package(Qt6Concurrent REQUIRED) +find_package(Qt6Gui REQUIRED) +find_package(Qt6Qml REQUIRED) include_directories(${AVKYS_INCLUDES} ./) add_executable(installDriver install.cpp) # qt5_use_modules(installDriver Core Concurrent Gui Qml) -target_link_libraries(installDriver Qt5::Core Qt5::Concurrent Qt5::Gui Qt5::Qml) +target_link_libraries(installDriver Qt6::Core Qt6::Concurrent Qt6::Gui Qt6::Qml) target_link_libraries(installDriver ${AVKYS_LIBS}) add_executable(uninstallDriver uninstall.cpp) # qt5_use_modules(uninstallDriver Core Concurrent Gui Qml) -target_link_libraries(uninstallDriver Qt5::Core Qt5::Concurrent Qt5::Gui Qt5::Qml) +target_link_libraries(uninstallDriver Qt6::Core Qt6::Concurrent Qt6::Gui Qt6::Qml) target_link_libraries(uninstallDriver ${AVKYS_LIBS}) diff --git a/external/webcamoid/Lib/CMakeLists.txt b/external/webcamoid/Lib/CMakeLists.txt index 5a920e82..648dfef8 100644 --- a/external/webcamoid/Lib/CMakeLists.txt +++ b/external/webcamoid/Lib/CMakeLists.txt @@ -1,9 +1,9 @@ MESSAGE ( STATUS "Buiding AkCommons...") -find_package(Qt5Core REQUIRED) -find_package(Qt5Concurrent REQUIRED) -find_package(Qt5Gui REQUIRED) -find_package(Qt5Qml REQUIRED) +find_package(Qt6Core REQUIRED) +find_package(Qt6Concurrent REQUIRED) +find_package(Qt6Gui REQUIRED) +find_package(Qt6Qml REQUIRED) add_definitions(-DQT_DEPRECATED_WARNINGS -DAKCOMMONS_LIBRARY) @@ -42,15 +42,15 @@ set (libavkys_srcs ) #This will generate moc_* for Qt -QT5_WRAP_CPP(libavkys_moc_srcs ${libavkys_headers}) +QT_WRAP_CPP(libavkys_moc_srcs ${libavkys_headers}) add_library(AvKys SHARED ${libavkys_srcs} ${libavkys_headers} ${libavkys_moc_srcs}) # qt5_use_modules(AvKys Core Concurrent Gui Qml) -target_link_libraries(AvKys Qt5::Core Qt5::Concurrent Qt5::Gui Qt5::Qml) +target_link_libraries(AvKys Qt6::Core Qt6::Concurrent Qt6::Gui Qt6::Qml) set(AVKYS_LIBS AvKys CACHE INTERNAL "doc string") set(AVKYS_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/src CACHE INTERNAL "doc string") -install(TARGETS AvKys DESTINATION ${CMAKE_INSTALL_BINDIR}) +install(TARGETS AvKys RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/external/webcamoid/Lib/src/ak.cpp b/external/webcamoid/Lib/src/ak.cpp index 880fdf4e..4090253b 100644 --- a/external/webcamoid/Lib/src/ak.cpp +++ b/external/webcamoid/Lib/src/ak.cpp @@ -40,14 +40,15 @@ class AkPrivate { this->m_globalEngine = nullptr; + // SB: qRegisterMetaTypeStreamOperators is now useless in Qt6: https://www.qt.io/blog/whats-new-in-qmetatype-qvariant qRegisterMetaType("QRgb"); qRegisterMetaType("QColor"); qRegisterMetaType("AkCaps"); - qRegisterMetaTypeStreamOperators("AkCaps"); + //qRegisterMetaTypeStreamOperators("AkCaps"); qRegisterMetaType("AkCaps::CapsType"); qRegisterMetaType("CapsType"); qRegisterMetaType("AkAudioCaps"); - qRegisterMetaTypeStreamOperators("AkAudioCaps"); + //qRegisterMetaTypeStreamOperators("AkAudioCaps"); qRegisterMetaType("AkAudioCaps::SampleFormat"); qRegisterMetaType("SampleFormat"); qRegisterMetaType("AkAudioCaps::SampleType"); @@ -55,14 +56,14 @@ class AkPrivate qRegisterMetaType("AkAudioCaps::ChannelLayout"); qRegisterMetaType("ChannelLayout"); qRegisterMetaType("AkVideoCaps"); - qRegisterMetaTypeStreamOperators("AkVideoCaps"); + //qRegisterMetaTypeStreamOperators("AkVideoCaps"); qRegisterMetaType("AkVideoCaps::PixelFormat"); qRegisterMetaType("PixelFormat"); qRegisterMetaType("AkElement::ElementState"); qRegisterMetaType("ElementState"); - qRegisterMetaTypeStreamOperators("AkElement::ElementState"); + //qRegisterMetaTypeStreamOperators("AkElement::ElementState"); qRegisterMetaType("AkFrac"); - qRegisterMetaTypeStreamOperators("AkFrac"); + //qRegisterMetaTypeStreamOperators("AkFrac"); qRegisterMetaType("AkPacket"); qRegisterMetaType("AkElementPtr"); diff --git a/external/webcamoid/Lib/src/akaudiopacket.h b/external/webcamoid/Lib/src/akaudiopacket.h index dc460584..4a9ed1fe 100644 --- a/external/webcamoid/Lib/src/akaudiopacket.h +++ b/external/webcamoid/Lib/src/akaudiopacket.h @@ -22,6 +22,7 @@ #include "akpacket.h" #include "akaudiocaps.h" +#include class AkAudioPacketPrivate; diff --git a/external/webcamoid/Lib/src/akcaps.cpp b/external/webcamoid/Lib/src/akcaps.cpp index ce68bec1..5ecaa41d 100644 --- a/external/webcamoid/Lib/src/akcaps.cpp +++ b/external/webcamoid/Lib/src/akcaps.cpp @@ -17,10 +17,8 @@ * Web-Site: http://webcamoid.github.io/ */ -#include +#include #include -#include - #include "akcaps.h" class AkCapsPrivate @@ -140,8 +138,8 @@ AkCaps &AkCaps::fromMap(const QVariantMap &caps) for (const QString &key: caps.keys()) if (key == "mimeType") { - this->d->m_isValid = QRegExp("\\s*[a-z]+/\\w+(?:(?:-|\\+|\\.)\\w+)*\\s*") - .exactMatch(caps[key].toString()); + this->d->m_isValid = QRegularExpression("\\s*[a-z]+/\\w+(?:(?:-|\\+|\\.)\\w+)*\\s*") + .match(caps[key].toString()).hasMatch(); this->d->m_mimeType = caps[key].toString().trimmed(); } else this->setProperty(key.trimmed().toStdString().c_str(), caps[key]); @@ -151,9 +149,9 @@ AkCaps &AkCaps::fromMap(const QVariantMap &caps) AkCaps &AkCaps::fromString(const QString &caps) { - this->d->m_isValid = QRegExp("\\s*[a-z]+/\\w+(?:(?:-|\\+|\\.)\\w+)*" + this->d->m_isValid = QRegularExpression("\\s*[a-z]+/\\w+(?:(?:-|\\+|\\.)\\w+)*" "(?:\\s*,\\s*[a-zA-Z_]\\w*\\s*=" - "\\s*[^,=]+)*\\s*").exactMatch(caps); + "\\s*[^,=]+)*\\s*").match(caps).hasMatch(); QList properties = this->dynamicPropertyNames(); @@ -163,11 +161,11 @@ AkCaps &AkCaps::fromString(const QString &caps) QStringList capsChunks; if (this->d->m_isValid) - capsChunks = caps.split(QRegExp("\\s*,\\s*"), + capsChunks = caps.split(QRegularExpression("\\s*,\\s*"), Qt::SkipEmptyParts); for (int i = 1; i < capsChunks.length(); i++) { - QStringList pair = capsChunks[i].split(QRegExp("\\s*=\\s*"), + QStringList pair = capsChunks[i].split(QRegularExpression("\\s*=\\s*"), Qt::SkipEmptyParts); this->setProperty(pair[0].trimmed().toStdString().c_str(), @@ -247,7 +245,7 @@ bool AkCaps::contains(const QString &property) const void AkCaps::setMimeType(const QString &mimeType) { - this->d->m_isValid = QRegExp("\\s*[a-z]+/\\w+(?:(?:-|\\+|\\.)\\w+)*\\s*").exactMatch(mimeType); + this->d->m_isValid = QRegularExpression("\\s*[a-z]+/\\w+(?:(?:-|\\+|\\.)\\w+)*\\s*").match(mimeType).hasMatch(); QString _mimeType = this->d->m_isValid? mimeType.trimmed(): QString(""); if (this->d->m_mimeType == _mimeType) diff --git a/external/webcamoid/Lib/src/akcaps.h b/external/webcamoid/Lib/src/akcaps.h index 77aa9dc2..8351805a 100644 --- a/external/webcamoid/Lib/src/akcaps.h +++ b/external/webcamoid/Lib/src/akcaps.h @@ -23,6 +23,7 @@ #include #include #include +#include #include "akcommons.h" diff --git a/external/webcamoid/Lib/src/akelement.cpp b/external/webcamoid/Lib/src/akelement.cpp index 0c109bf4..c0dbb848 100644 --- a/external/webcamoid/Lib/src/akelement.cpp +++ b/external/webcamoid/Lib/src/akelement.cpp @@ -17,7 +17,7 @@ * Web-Site: http://webcamoid.github.io/ */ -#include +#include #include #include #include @@ -105,8 +105,8 @@ class AkElementPrivate QMetaMethod method = object->metaObject()->method(i); QString signature(method.methodSignature()); - if (QRegExp(QString("\\s*%1\\s*\\(.*").arg(methodName)) - .exactMatch(signature)) + if (QRegularExpression(QString("\\s*%1\\s*\\(.*").arg(methodName)) + .match(signature).hasMatch()) if (!methodSignatures.contains(signature)) { methods << method; methodSignatures << signature; @@ -133,7 +133,7 @@ class AkElementPrivate return pluginId; ; #else - return pluginId.remove(QRegExp("^lib")); + return pluginId.remove(QRegularExpression("^lib")); #endif } @@ -158,7 +158,7 @@ class AkElementPrivate for (int i = sPath->length() - 1; i >= 0; i--) { QString searchDir(sPath->at(i)); - searchDir.replace(QRegExp("((\\\\/?)|(/\\\\?))+"), + searchDir.replace(QRegularExpression("((\\\\/?)|(/\\\\?))+"), QDir::separator()); while (searchDir.endsWith(QDir::separator())) @@ -208,9 +208,12 @@ class AkElementPrivate if (QFileInfo(path).isFile()) { QString fileName = QFileInfo(path).fileName(); - if (QRegExp(this->m_pluginFilePattern, - Qt::CaseSensitive, - QRegExp::Wildcard).exactMatch(fileName)) { + // if (QRegExp(this->m_pluginFilePattern, + // Qt::CaseSensitive, + // QRegExp::Wildcard).exactMatch(fileName)) { + if (QRegularExpression( + QRegularExpression::fromWildcard(this->m_pluginFilePattern, Qt::CaseSensitive) + ).match(fileName).hasMatch()) { QPluginLoader pluginLoader(path); if (pluginLoader.load()) { @@ -508,7 +511,7 @@ QStringList AkElement::listSubModules(const QStringList &types) if (this->d->m_pluginId.isEmpty()) { pluginId = this->metaObject()->className(); - pluginId.replace(QRegExp("Element$"), ""); + pluginId.replace(QRegularExpression("Element$"), ""); } else { pluginId = this->d->m_pluginId; } @@ -575,7 +578,7 @@ QStringList AkElement::listSubModulesPaths() if (this->d->m_pluginId.isEmpty()) { pluginId = this->metaObject()->className(); - pluginId.replace(QRegExp("Element$"), ""); + pluginId.replace(QRegularExpression("Element$"), ""); } else { pluginId = this->d->m_pluginId; } @@ -624,7 +627,7 @@ QObject *AkElement::loadSubModule(const QString &subModule) if (this->d->m_pluginId.isEmpty()) { pluginId = this->metaObject()->className(); - pluginId.replace(QRegExp("Element$"), ""); + pluginId.replace(QRegularExpression("Element$"), ""); } else { pluginId = this->d->m_pluginId; } @@ -717,7 +720,7 @@ QStringList AkElement::listPluginPaths(const QString &searchPath) QString searchDir(searchPath); - searchDir.replace(QRegExp("((\\\\/?)|(/\\\\?))+"), + searchDir.replace(QRegularExpression("((\\\\/?)|(/\\\\?))+"), QDir::separator()); QStringList files; diff --git a/external/webcamoid/Lib/src/akfrac.cpp b/external/webcamoid/Lib/src/akfrac.cpp index 7de14728..dba40fbb 100644 --- a/external/webcamoid/Lib/src/akfrac.cpp +++ b/external/webcamoid/Lib/src/akfrac.cpp @@ -17,7 +17,7 @@ * Web-Site: http://webcamoid.github.io/ */ -#include +#include #include #include @@ -235,8 +235,8 @@ void AkFrac::setNumDen(qint64 num, qint64 den) void AkFrac::setNumDen(const QString &fracString) { - bool match = QRegExp("(\\s*-)?\\s*\\d+\\s*/" - "\\s*\\d+\\s*").exactMatch(fracString); + bool match = QRegularExpression("(\\s*-)?\\s*\\d+\\s*/" + "\\s*\\d+\\s*").match(fracString).hasMatch(); if (!match) { this->setNumDen(0, 0); @@ -244,7 +244,7 @@ void AkFrac::setNumDen(const QString &fracString) return; } - QStringList fracChunks = fracString.split(QRegExp("\\s*/\\s*"), + QStringList fracChunks = fracString.split(QRegularExpression("\\s*/\\s*"), Qt::SkipEmptyParts); qint64 num = fracChunks[0].trimmed().toInt(); diff --git a/external/webcamoid/Lib/src/akpacket.h b/external/webcamoid/Lib/src/akpacket.h index 0a09f28b..7aa9c7e4 100644 --- a/external/webcamoid/Lib/src/akpacket.h +++ b/external/webcamoid/Lib/src/akpacket.h @@ -21,6 +21,7 @@ #define AKPACKET_H #include +#include #include "akcaps.h" #include "akfrac.h" diff --git a/external/webcamoid/Lib/src/akutils.cpp b/external/webcamoid/Lib/src/akutils.cpp index 4b47e3fe..c29898e5 100644 --- a/external/webcamoid/Lib/src/akutils.cpp +++ b/external/webcamoid/Lib/src/akutils.cpp @@ -18,6 +18,7 @@ */ #include "akutils.h" +#include typedef QMap ImageToPixelFormatMap; diff --git a/external/webcamoid/Lib/src/akvideopacket.cpp b/external/webcamoid/Lib/src/akvideopacket.cpp index d64e5ea0..1e02f44d 100644 --- a/external/webcamoid/Lib/src/akvideopacket.cpp +++ b/external/webcamoid/Lib/src/akvideopacket.cpp @@ -18,6 +18,7 @@ */ #include "akvideopacket.h" +#include class AkVideoPacketPrivate { diff --git a/external/webcamoid/Plugins/ACapsConvert/CMakeLists.txt b/external/webcamoid/Plugins/ACapsConvert/CMakeLists.txt index b86ee424..65498262 100644 --- a/external/webcamoid/Plugins/ACapsConvert/CMakeLists.txt +++ b/external/webcamoid/Plugins/ACapsConvert/CMakeLists.txt @@ -3,10 +3,10 @@ message(STATUS "Building ${PLUGIN_NAME} Plugin.") add_definitions(-DUNICODE) -find_package(Qt5Core REQUIRED) -find_package(Qt5Concurrent REQUIRED) -find_package(Qt5Gui REQUIRED) -find_package(Qt5Qml REQUIRED) +find_package(Qt6Core REQUIRED) +find_package(Qt6Concurrent REQUIRED) +find_package(Qt6Gui REQUIRED) +find_package(Qt6Qml REQUIRED) include_directories( ${AVKYS_PLUGIN_INCLUDES} @@ -34,15 +34,15 @@ set (plugin_qrc #This will generate moc_* for Qt -QT5_WRAP_CPP(plugin_moc_srcs ${plugin_headers}) +QT_WRAP_CPP(plugin_moc_srcs ${plugin_headers}) -QT5_ADD_RESOURCES(plugin_qrc_srcs ${plugin_qrc}) +QT6_ADD_RESOURCES(plugin_qrc_srcs ${plugin_qrc}) add_library(${PLUGIN_NAME} SHARED ${plugin_srcs} ${plugin_headers} ${plugin_moc_srcs} ${plugin_qrc_srcs}) # qt5_use_modules(${PLUGIN_NAME} Core Concurrent Gui Qml) target_link_libraries(${PLUGIN_NAME} ${AVKYS_PLUGIN_LIBRARIES}) -target_link_libraries(${PLUGIN_NAME} Qt5::Core Qt5::Concurrent Qt5::Gui Qt5::Qml) -install(TARGETS ${PLUGIN_NAME} DESTINATION ${AVKYS_LIBRARY_INSTALL_PATH}) +target_link_libraries(${PLUGIN_NAME} Qt6::Core Qt6::Concurrent Qt6::Gui Qt6::Qml) +install(TARGETS ${PLUGIN_NAME} RUNTIME DESTINATION ${AVKYS_LIBRARY_INSTALL_PATH}) #submodule(s) FILE(MAKE_DIRECTORY ${LIBRARY_OUTPUT_PATH}/submodules/${PLUGIN_NAME}) diff --git a/external/webcamoid/Plugins/ACapsConvert/src/ffmpegsw/CMakeLists.txt b/external/webcamoid/Plugins/ACapsConvert/src/ffmpegsw/CMakeLists.txt index 45d460e3..d636ab8c 100644 --- a/external/webcamoid/Plugins/ACapsConvert/src/ffmpegsw/CMakeLists.txt +++ b/external/webcamoid/Plugins/ACapsConvert/src/ffmpegsw/CMakeLists.txt @@ -13,11 +13,11 @@ add_definitions(-DHAVE_SAMPLEFORMAT64) SET(LIBRARY_OUTPUT_PATH ${LIBRARY_OUTPUT_PATH}/submodules/${PLUGIN_NAME}) -find_package(Qt5Core REQUIRED) -find_package(Qt5Concurrent REQUIRED) -find_package(Qt5Gui REQUIRED) -find_package(Qt5Qml REQUIRED) -find_package(Qt5Widgets REQUIRED) +find_package(Qt6Core REQUIRED) +find_package(Qt6Concurrent REQUIRED) +find_package(Qt6Gui REQUIRED) +find_package(Qt6Qml REQUIRED) +find_package(Qt6Widgets REQUIRED) include_directories( ${AVKYS_PLUGIN_INCLUDES} @@ -44,16 +44,16 @@ set (submodule_qrc #This will generate moc_* for Qt -QT5_WRAP_CPP(submodule_moc_srcs ${submodule_headers}) +QT_WRAP_CPP(submodule_moc_srcs ${submodule_headers}) -QT5_ADD_RESOURCES(submodule_qrc_srcs ${summodule_qrc}) +QT6_ADD_RESOURCES(submodule_qrc_srcs ${summodule_qrc}) add_library(${PLUGIN_NAME}_${SUBMMODULE_NAME} SHARED ${submodule_srcs} ${submodule_headers} ${submodule_moc_srcs} ${submodule_qrc_srcs}) set_target_properties(${PLUGIN_NAME}_${SUBMMODULE_NAME} PROPERTIES OUTPUT_NAME "ffmpegsw" ) #qt5_use_modules(${PLUGIN_NAME}_${SUBMMODULE_NAME} Core Concurrent Widgets Gui Qml) -target_link_libraries(${PLUGIN_NAME}_${SUBMMODULE_NAME} Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Gui Qt5::Qml) +target_link_libraries(${PLUGIN_NAME}_${SUBMMODULE_NAME} Qt6::Core Qt6::Concurrent Qt6::Widgets Qt6::Gui Qt6::Qml) target_link_libraries(${PLUGIN_NAME}_${SUBMMODULE_NAME} ${AVKYS_PLUGIN_LIBRARIES} ${FFMPEG_LIBS}) -install(TARGETS ${PLUGIN_NAME}_${SUBMMODULE_NAME} DESTINATION ${AVKYS_LIBRARY_INSTALL_PATH}/submodules/${PLUGIN_NAME}) +install(TARGETS ${PLUGIN_NAME}_${SUBMMODULE_NAME} RUNTIME DESTINATION ${AVKYS_LIBRARY_INSTALL_PATH}/submodules/${PLUGIN_NAME}) diff --git a/external/webcamoid/Plugins/CMakeLists.txt b/external/webcamoid/Plugins/CMakeLists.txt index 43ddf63c..aa483039 100644 --- a/external/webcamoid/Plugins/CMakeLists.txt +++ b/external/webcamoid/Plugins/CMakeLists.txt @@ -57,19 +57,50 @@ endif() if(WIN32) - #Hard coded path for now - set(FFMPEG_PATH "C:/Libs/ffmpeg") + # Oldest archive found working + set(ffmpeg_version "4.4") + + set(ffmpeg_archive_name "ffmpeg-${ffmpeg_version}-full_build-shared.zip") + set(ffmpeg_archive_url "https://github.com/GyanD/codexffmpeg/releases/download/${ffmpeg_version}/${ffmpeg_archive_name}") + + set(ffmpeg_archive_url_download_log "") + set(ffmpeg_archive_url_download_status "") + + # Download FFMPEG + if(NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/${ffmpeg_archive_name}) + message(STATUS "Downloading : ${ffmpeg_archive_url}") + file(DOWNLOAD + ${ffmpeg_archive_url} + ${CMAKE_CURRENT_BINARY_DIR}/${ffmpeg_archive_name} + TIMEOUT 300 #seconds + LOG ffmpeg_archive_url_download_log + STATUS ffmpeg_archive_url_download_status + TLS_VERIFY ON + SHOW_PROGRESS) + endif() + + MESSAGE ( STATUS "Download status = ${ffmpeg_archive_url_download_status} log = ${ffmpeg_archive_url_download_log}") + + + # Extract file + if (NOT EXISTS {CMAKE_CURRENT_BINARY_DIR}/ffmpeg-${ffmpeg_version}-full_build-shared/README.txt) + file(ARCHIVE_EXTRACT INPUT ${CMAKE_CURRENT_BINARY_DIR}/${ffmpeg_archive_name} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) + endif() + + + # Using downloaded archive + set(FFMPEG_PATH ${CMAKE_CURRENT_BINARY_DIR}/ffmpeg-${ffmpeg_version}-full_build-shared) FILE(GLOB FFMPEG_DLL "${FFMPEG_PATH}/bin/*.dll") MESSAGE ( STATUS "FFMpeg libs = ${FFMPEG_DLL}") - #FOR SOME REASON, DLLs ARE IN THE BIN DIRECTORY... + # DLLs ARE IN THE BIN DIRECTORY... INSTALL(FILES ${FFMPEG_DLL} DESTINATION bin) - #For IN-SOURCE debug + # For IN-SOURCE debug file(COPY ${FFMPEG_DLL} DESTINATION ${EXECUTABLE_OUTPUT_PATH}) - #FOR MSVC + # FOR MSVC set(ffmpeg_lib_names ${FFMPEG_PATH}/lib/avcodec.lib ${FFMPEG_PATH}/lib/avfilter.lib @@ -81,7 +112,7 @@ if(WIN32) ${FFMPEG_PATH}/lib/swscale.lib ) - #for linking and compiling + # For linking and compiling set(FFMPEG_INCLUDES "${FFMPEG_PATH}/include " CACHE INTERNAL "doc string") message ( STATUS "FFMpeg includes = ${FFMPEG_INCLUDES}") @@ -90,7 +121,7 @@ if(WIN32) endif() -#Will output in the right directory +# Will output in the right directory FILE(MAKE_DIRECTORY ${CMAKE_INSTALL_PREFIX}/bin/AvKysPlugins) SET(LIBRARY_OUTPUT_PATH ${CMAKE_INSTALL_PREFIX}/bin/AvKysPlugins) SET (AVKYS_LIBRARY_INSTALL_PATH ${CMAKE_INSTALL_BINDIR}/AvKysPlugins) diff --git a/external/webcamoid/Plugins/DesktopCapture/CMakeLists.txt b/external/webcamoid/Plugins/DesktopCapture/CMakeLists.txt index 1f7d65f8..33f00565 100644 --- a/external/webcamoid/Plugins/DesktopCapture/CMakeLists.txt +++ b/external/webcamoid/Plugins/DesktopCapture/CMakeLists.txt @@ -3,10 +3,10 @@ message(STATUS "Building ${PLUGIN_NAME} Plugin.") add_definitions(-DUNICODE) -find_package(Qt5Core REQUIRED) -find_package(Qt5Concurrent REQUIRED) -find_package(Qt5Gui REQUIRED) -find_package(Qt5Qml REQUIRED) +find_package(Qt6Core REQUIRED) +find_package(Qt6Concurrent REQUIRED) +find_package(Qt6Gui REQUIRED) +find_package(Qt6Qml REQUIRED) include_directories( ${AVKYS_PLUGIN_INCLUDES} @@ -34,17 +34,15 @@ set (plugin_qrc #This will generate moc_* for Qt -QT5_WRAP_CPP(plugin_moc_srcs ${plugin_headers}) +QT_WRAP_CPP(plugin_moc_srcs ${plugin_headers}) -QT5_ADD_RESOURCES(plugin_qrc_srcs ${plugin_qrc}) +QT6_ADD_RESOURCES(plugin_qrc_srcs ${plugin_qrc}) add_library(${PLUGIN_NAME} SHARED ${plugin_srcs} ${plugin_headers} ${plugin_moc_srcs} ${plugin_qrc_srcs}) # qt5_use_modules(${PLUGIN_NAME} Core Concurrent Gui Qml) -target_link_libraries(${PLUGIN_NAME} Qt5::Core Qt5::Concurrent Qt5::Gui Qt5::Qml) +target_link_libraries(${PLUGIN_NAME} Qt6::Core Qt6::Concurrent Qt6::Gui Qt6::Qml) target_link_libraries(${PLUGIN_NAME} ${AVKYS_PLUGIN_LIBRARIES}) -install(TARGETS ${PLUGIN_NAME} DESTINATION ${AVKYS_LIBRARY_INSTALL_PATH}) - - +install(TARGETS ${PLUGIN_NAME} RUNTIME DESTINATION ${AVKYS_LIBRARY_INSTALL_PATH}) #submodule(s) FILE(MAKE_DIRECTORY ${LIBRARY_OUTPUT_PATH}/submodules/${PLUGIN_NAME}) diff --git a/external/webcamoid/Plugins/DesktopCapture/src/qtscreen/CMakeLists.txt b/external/webcamoid/Plugins/DesktopCapture/src/qtscreen/CMakeLists.txt index b5ec2bb0..9310127a 100644 --- a/external/webcamoid/Plugins/DesktopCapture/src/qtscreen/CMakeLists.txt +++ b/external/webcamoid/Plugins/DesktopCapture/src/qtscreen/CMakeLists.txt @@ -5,11 +5,11 @@ message(STATUS "Building ${PLUGIN_NAME} : ${SUBMMODULE_NAME} submodule.") SET(LIBRARY_OUTPUT_PATH ${LIBRARY_OUTPUT_PATH}/submodules/${PLUGIN_NAME}) -find_package(Qt5Core REQUIRED) -find_package(Qt5Concurrent REQUIRED) -find_package(Qt5Gui REQUIRED) -find_package(Qt5Qml REQUIRED) -find_package(Qt5Widgets REQUIRED) +find_package(Qt6Core REQUIRED) +find_package(Qt6Concurrent REQUIRED) +find_package(Qt6Gui REQUIRED) +find_package(Qt6Qml REQUIRED) +find_package(Qt6Widgets REQUIRED) include_directories( ${AVKYS_PLUGIN_INCLUDES} @@ -35,16 +35,16 @@ set (submodule_qrc #This will generate moc_* for Qt -QT5_WRAP_CPP(submodule_moc_srcs ${submodule_headers}) +QT_WRAP_CPP(submodule_moc_srcs ${submodule_headers}) -QT5_ADD_RESOURCES(submodule_qrc_srcs ${summodule_qrc}) +QT6_ADD_RESOURCES(submodule_qrc_srcs ${summodule_qrc}) add_library(${PLUGIN_NAME}_${SUBMMODULE_NAME} SHARED ${submodule_srcs} ${submodule_headers} ${submodule_moc_srcs} ${submodule_qrc_srcs}) set_target_properties(${PLUGIN_NAME}_${SUBMMODULE_NAME} PROPERTIES OUTPUT_NAME "qtscreen") # qt5_use_modules(${PLUGIN_NAME}_${SUBMMODULE_NAME} Core Concurrent Widgets Gui Qml) -target_link_libraries(${PLUGIN_NAME}_${SUBMMODULE_NAME} Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Gui Qt5::Qml) +target_link_libraries(${PLUGIN_NAME}_${SUBMMODULE_NAME} Qt6::Core Qt6::Concurrent Qt6::Widgets Qt6::Gui Qt6::Qml) target_link_libraries(${PLUGIN_NAME}_${SUBMMODULE_NAME} ${AVKYS_PLUGIN_LIBRARIES}) -install(TARGETS ${PLUGIN_NAME}_${SUBMMODULE_NAME} DESTINATION ${AVKYS_LIBRARY_INSTALL_PATH}/submodules/${PLUGIN_NAME}) +install(TARGETS ${PLUGIN_NAME}_${SUBMMODULE_NAME} RUNTIME DESTINATION ${AVKYS_LIBRARY_INSTALL_PATH}/submodules/${PLUGIN_NAME}) diff --git a/external/webcamoid/Plugins/DesktopCapture/src/qtscreen/src/qtscreendev.cpp b/external/webcamoid/Plugins/DesktopCapture/src/qtscreen/src/qtscreendev.cpp index 6dce60be..33e26835 100644 --- a/external/webcamoid/Plugins/DesktopCapture/src/qtscreen/src/qtscreendev.cpp +++ b/external/webcamoid/Plugins/DesktopCapture/src/qtscreen/src/qtscreendev.cpp @@ -326,10 +326,10 @@ void QtScreenDev::readFrame() if (!this->m_threadStatus.isRunning()) { this->m_curPacket = packet; - this->m_threadStatus = QtConcurrent::run(&this->m_threadPool, + this->m_threadStatus = QtConcurrent::run(&this->m_threadPool,[=]{QtScreenDev::sendPacket(this->m_curPacket);}); + /*&QtScreenDev::sendPacket, this, - &QtScreenDev::sendPacket, - this->m_curPacket); + this->m_curPacket);*/ } } @@ -343,7 +343,7 @@ void QtScreenDev::screenCountChanged(QScreen *screen) void QtScreenDev::srceenResized(int screen) { QString media = QString("screen://%1").arg(screen); - QWidget *widget = QApplication::desktop()->screen(screen); + QScreen *widget = QGuiApplication::screens().at(screen);//QApplication::desktop()->screen(screen); emit this->sizeChanged(media, widget->size()); } diff --git a/external/webcamoid/Plugins/DesktopCapture/src/qtscreen/src/qtscreendev.h b/external/webcamoid/Plugins/DesktopCapture/src/qtscreen/src/qtscreendev.h index 147f3403..8d065585 100644 --- a/external/webcamoid/Plugins/DesktopCapture/src/qtscreen/src/qtscreendev.h +++ b/external/webcamoid/Plugins/DesktopCapture/src/qtscreen/src/qtscreendev.h @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include diff --git a/external/webcamoid/Plugins/MultiSrc/CMakeLists.txt b/external/webcamoid/Plugins/MultiSrc/CMakeLists.txt index 92e860bb..37e2c21c 100644 --- a/external/webcamoid/Plugins/MultiSrc/CMakeLists.txt +++ b/external/webcamoid/Plugins/MultiSrc/CMakeLists.txt @@ -2,10 +2,10 @@ set(PLUGIN_NAME MultiSrc) message(STATUS "Building ${PLUGIN_NAME} Plugin.") -find_package(Qt5Core REQUIRED) -find_package(Qt5Concurrent REQUIRED) -find_package(Qt5Gui REQUIRED) -find_package(Qt5Qml REQUIRED) +find_package(Qt6Core REQUIRED) +find_package(Qt6Concurrent REQUIRED) +find_package(Qt6Gui REQUIRED) +find_package(Qt6Qml REQUIRED) include_directories( ${AVKYS_PLUGIN_INCLUDES} @@ -33,15 +33,15 @@ set (plugin_qrc #This will generate moc_* for Qt -QT5_WRAP_CPP(plugin_moc_srcs ${plugin_headers}) +QT_WRAP_CPP(plugin_moc_srcs ${plugin_headers}) -QT5_ADD_RESOURCES(plugin_qrc_srcs ${plugin_qrc}) +QT6_ADD_RESOURCES(plugin_qrc_srcs ${plugin_qrc}) add_library(${PLUGIN_NAME} SHARED ${plugin_srcs} ${plugin_headers} ${plugin_moc_srcs} ${plugin_qrc_srcs}) # qt5_use_modules(${PLUGIN_NAME} Core Concurrent Gui Qml) -target_link_libraries(${PLUGIN_NAME} Qt5::Core Qt5::Concurrent Qt5::Gui Qt5::Qml) +target_link_libraries(${PLUGIN_NAME} Qt6::Core Qt6::Concurrent Qt6::Gui Qt6::Qml) target_link_libraries(${PLUGIN_NAME} ${AVKYS_PLUGIN_LIBRARIES}) -install(TARGETS ${PLUGIN_NAME} DESTINATION ${AVKYS_LIBRARY_INSTALL_PATH}) +install(TARGETS ${PLUGIN_NAME} RUNTIME DESTINATION ${AVKYS_LIBRARY_INSTALL_PATH}) #submodule(s) FILE(MAKE_DIRECTORY ${LIBRARY_OUTPUT_PATH}/submodules/${PLUGIN_NAME}) diff --git a/external/webcamoid/Plugins/MultiSrc/src/ffmpeg/CMakeLists.txt b/external/webcamoid/Plugins/MultiSrc/src/ffmpeg/CMakeLists.txt index 82c7b4b2..cc01ab32 100644 --- a/external/webcamoid/Plugins/MultiSrc/src/ffmpeg/CMakeLists.txt +++ b/external/webcamoid/Plugins/MultiSrc/src/ffmpeg/CMakeLists.txt @@ -13,11 +13,11 @@ add_definitions(-DHAVE_SAMPLEFORMAT64) SET(LIBRARY_OUTPUT_PATH ${LIBRARY_OUTPUT_PATH}/submodules/${PLUGIN_NAME}) -find_package(Qt5Core REQUIRED) -find_package(Qt5Concurrent REQUIRED) -find_package(Qt5Gui REQUIRED) -find_package(Qt5Qml REQUIRED) -find_package(Qt5Widgets REQUIRED) +find_package(Qt6Core REQUIRED) +find_package(Qt6Concurrent REQUIRED) +find_package(Qt6Gui REQUIRED) +find_package(Qt6Qml REQUIRED) +find_package(Qt6Widgets REQUIRED) include_directories( ${AVKYS_PLUGIN_INCLUDES} @@ -54,14 +54,14 @@ set (submodule_qrc #This will generate moc_* for Qt -QT5_WRAP_CPP(submodule_moc_srcs ${submodule_headers}) +QT_WRAP_CPP(submodule_moc_srcs ${submodule_headers}) -QT5_ADD_RESOURCES(submodule_qrc_srcs ${summodule_qrc}) +QT6_ADD_RESOURCES(submodule_qrc_srcs ${summodule_qrc}) add_library(${SUBMMODULE_NAME} SHARED ${submodule_srcs} ${submodule_headers} ${submodule_moc_srcs} ${submodule_qrc_srcs}) # qt5_use_modules(${SUBMMODULE_NAME} Core Concurrent Widgets Gui Qml) -target_link_libraries(${SUBMMODULE_NAME} Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Gui Qt5::Qml) +target_link_libraries(${SUBMMODULE_NAME} Qt6::Core Qt6::Concurrent Qt6::Widgets Qt6::Gui Qt6::Qml) target_link_libraries(${SUBMMODULE_NAME} ${AVKYS_PLUGIN_LIBRARIES} ${FFMPEG_LIBS}) -install(TARGETS ${SUBMMODULE_NAME} DESTINATION ${AVKYS_LIBRARY_INSTALL_PATH}/submodules/${PLUGIN_NAME}) +install(TARGETS ${SUBMMODULE_NAME} RUNTIME DESTINATION ${AVKYS_LIBRARY_INSTALL_PATH}/submodules/${PLUGIN_NAME}) diff --git a/external/webcamoid/Plugins/MultiSrc/src/ffmpeg/src/abstractstream.cpp b/external/webcamoid/Plugins/MultiSrc/src/ffmpeg/src/abstractstream.cpp index 1389fb5b..2d328f32 100644 --- a/external/webcamoid/Plugins/MultiSrc/src/ffmpeg/src/abstractstream.cpp +++ b/external/webcamoid/Plugins/MultiSrc/src/ffmpeg/src/abstractstream.cpp @@ -412,9 +412,7 @@ void AbstractStream::setPaused(bool paused) this->m_dataLoopResult.waitForFinished(); else this->m_dataLoopResult = - QtConcurrent::run(&this->m_threadPool, - this, - &AbstractStream::dataLoop); + QtConcurrent::run(&this->m_threadPool, [=]{AbstractStream::dataLoop();}); this->m_paused = paused; emit this->pausedChanged(paused); @@ -440,13 +438,9 @@ bool AbstractStream::init() this->m_runPacketLoop = true; this->m_runDataLoop = true; this->m_packetLoopResult = - QtConcurrent::run(&this->m_threadPool, - this, - &AbstractStream::packetLoop); + QtConcurrent::run(&m_threadPool,[=]{AbstractStream::packetLoop();}); this->m_dataLoopResult = - QtConcurrent::run(&this->m_threadPool, - this, - &AbstractStream::dataLoop); + QtConcurrent::run(&m_threadPool, [=]{AbstractStream::dataLoop();}); return true; } diff --git a/external/webcamoid/Plugins/MultiSrc/src/ffmpeg/src/audiostream.cpp b/external/webcamoid/Plugins/MultiSrc/src/ffmpeg/src/audiostream.cpp index e19233b4..3149b684 100644 --- a/external/webcamoid/Plugins/MultiSrc/src/ffmpeg/src/audiostream.cpp +++ b/external/webcamoid/Plugins/MultiSrc/src/ffmpeg/src/audiostream.cpp @@ -310,7 +310,8 @@ AkPacket AudioStream::convert(AVFrame *iFrame) AkAudioCaps caps(format, iChannels > 1? 2: 1, iFrame->sample_rate); - this->m_audioConvert->setProperty("caps", caps.toString()); + // SB: FIXME - call to setProperty is done from another thread, and thus crashing... For now, we don't care much, since we don't have audio in our stream + //this->m_audioConvert->setProperty("caps", caps.toString()); this->m_audioConvert->setState(AkElement::ElementStatePlaying); } diff --git a/external/webcamoid/Plugins/MultiSrc/src/ffmpeg/src/mediasourceffmpeg.cpp b/external/webcamoid/Plugins/MultiSrc/src/ffmpeg/src/mediasourceffmpeg.cpp index 6f56195e..7b404ca3 100644 --- a/external/webcamoid/Plugins/MultiSrc/src/ffmpeg/src/mediasourceffmpeg.cpp +++ b/external/webcamoid/Plugins/MultiSrc/src/ffmpeg/src/mediasourceffmpeg.cpp @@ -18,8 +18,8 @@ */ #include -#include #include +#include #include "mediasourceffmpeg.h" #include "videostream.h" @@ -509,9 +509,8 @@ bool MediaSourceFFmpeg::setState(AkElement::ElementState state) this->m_globalClock.setClock(0.); this->m_run = true; this->m_readPacketsLoopResult = - QtConcurrent::run(&this->m_threadPool, - this, - &MediaSourceFFmpeg::readPackets); + QtConcurrent::run(&this->m_threadPool,[=]{MediaSourceFFmpeg::readPackets();}); + // &MediaSourceFFmpeg::readPackets, this); this->m_curState = state; return true; @@ -631,9 +630,8 @@ void MediaSourceFFmpeg::reconnectStream(){ void MediaSourceFFmpeg::packetConsumed() { - QtConcurrent::run(&this->m_threadPool, - this, - &MediaSourceFFmpeg::unlockQueue); + QtConcurrent::run(&this->m_threadPool,[=]{MediaSourceFFmpeg::unlockQueue();}); + //&MediaSourceFFmpeg::unlockQueue, this); } bool MediaSourceFFmpeg::initContext() @@ -646,13 +644,13 @@ bool MediaSourceFFmpeg::initContext() AVInputFormat *inputFormat = nullptr; AVDictionary *inputOptions = nullptr; - if (QRegExp("/dev/video\\d*").exactMatch(uri)) + if (QRegularExpression("/dev/video\\d*").match(uri).hasMatch()) inputFormat = av_find_input_format("v4l2"); - else if (QRegExp(":\\d+\\.\\d+(?:\\+\\d+,\\d+)?").exactMatch(uri)) { + else if (QRegularExpression(":\\d+\\.\\d+(?:\\+\\d+,\\d+)?").match(uri).hasMatch()) { inputFormat = av_find_input_format("x11grab"); - int width = this->roundDown(QApplication::desktop()->width(), 4); - int height = this->roundDown(QApplication::desktop()->height(), 4); + int width = this->roundDown(QGuiApplication::primaryScreen()->size().width(), 4); + int height = this->roundDown(QGuiApplication::primaryScreen()->size().height(), 4); av_dict_set(&inputOptions, "video_size", @@ -664,7 +662,7 @@ bool MediaSourceFFmpeg::initContext() // draw_mouse (int) } else if (uri == "pulse" || - QRegExp("hw:\\d+").exactMatch(uri)) + QRegularExpression("hw:\\d+").match(uri).hasMatch()) inputFormat = av_find_input_format("alsa"); else if (uri == "/dev/dsp") inputFormat = av_find_input_format("oss"); @@ -687,7 +685,7 @@ bool MediaSourceFFmpeg::initContext() QString uriCopy = uri; for (const QString &schemer: mmsSchemes) - uriCopy.replace(QRegExp(QString("^%1").arg(schemer)), + uriCopy.replace(QRegularExpression(QString("^%1").arg(schemer)), scheme); inputContext = nullptr; diff --git a/external/webcamoid/Plugins/VirtualCamera/CMakeLists.txt b/external/webcamoid/Plugins/VirtualCamera/CMakeLists.txt index 16811f0d..f4cc25db 100644 --- a/external/webcamoid/Plugins/VirtualCamera/CMakeLists.txt +++ b/external/webcamoid/Plugins/VirtualCamera/CMakeLists.txt @@ -3,10 +3,10 @@ message(STATUS "Building ${PLUGIN_NAME} Plugin.") add_definitions(-DUNICODE) -find_package(Qt5Core REQUIRED) -find_package(Qt5Concurrent REQUIRED) -find_package(Qt5Gui REQUIRED) -find_package(Qt5Qml REQUIRED) +find_package(Qt6Core REQUIRED) +find_package(Qt6Concurrent REQUIRED) +find_package(Qt6Gui REQUIRED) +find_package(Qt6Qml REQUIRED) include_directories( ${AVKYS_PLUGIN_INCLUDES} @@ -36,15 +36,15 @@ set (plugin_qrc #This will generate moc_* for Qt -QT5_WRAP_CPP(plugin_moc_srcs ${plugin_headers}) +QT_WRAP_CPP(plugin_moc_srcs ${plugin_headers}) -QT5_ADD_RESOURCES(plugin_qrc_srcs ${plugin_qrc}) +QT6_ADD_RESOURCES(plugin_qrc_srcs ${plugin_qrc}) add_library(${PLUGIN_NAME} SHARED ${plugin_srcs} ${plugin_headers} ${plugin_moc_srcs} ${plugin_qrc_srcs}) # qt5_use_modules(${PLUGIN_NAME} Core Concurrent Gui Qml) -target_link_libraries(${PLUGIN_NAME} Qt5::Core Qt5::Concurrent Qt5::Gui Qt5::Qml) +target_link_libraries(${PLUGIN_NAME} Qt6::Core Qt6::Concurrent Qt6::Gui Qt6::Qml) target_link_libraries(${PLUGIN_NAME} ${AVKYS_PLUGIN_LIBRARIES}) -install(TARGETS ${PLUGIN_NAME} DESTINATION ${AVKYS_LIBRARY_INSTALL_PATH}) +install(TARGETS ${PLUGIN_NAME} RUNTIME DESTINATION ${AVKYS_LIBRARY_INSTALL_PATH}) #submodule(s) FILE(MAKE_DIRECTORY ${LIBRARY_OUTPUT_PATH}/submodules/${PLUGIN_NAME}) diff --git a/external/webcamoid/Plugins/VirtualCamera/src/dshow/CMakeLists.txt b/external/webcamoid/Plugins/VirtualCamera/src/dshow/CMakeLists.txt index 5ba54aa6..5c13a320 100644 --- a/external/webcamoid/Plugins/VirtualCamera/src/dshow/CMakeLists.txt +++ b/external/webcamoid/Plugins/VirtualCamera/src/dshow/CMakeLists.txt @@ -6,11 +6,11 @@ SET(LIBRARY_OUTPUT_PATH ${LIBRARY_OUTPUT_PATH}/submodules/${PLUGIN_NAME}) #Very important to get .lib files SET(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE) -find_package(Qt5Core REQUIRED) -find_package(Qt5Concurrent REQUIRED) -find_package(Qt5Gui REQUIRED) -find_package(Qt5Qml REQUIRED) -find_package(Qt5Widgets REQUIRED) +find_package(Qt6Core REQUIRED) +find_package(Qt6Concurrent REQUIRED) +find_package(Qt6Gui REQUIRED) +find_package(Qt6Qml REQUIRED) +find_package(Qt6Widgets REQUIRED) add_subdirectory(VirtualCameraFilter) @@ -45,9 +45,9 @@ set (submodule_qrc #This will generate moc_* for Qt -QT5_WRAP_CPP(submodule_moc_srcs ${submodule_headers}) +QT_WRAP_CPP(submodule_moc_srcs ${submodule_headers}) -QT5_ADD_RESOURCES(submodule_qrc_srcs ${summodule_qrc}) +QT6_ADD_RESOURCES(submodule_qrc_srcs ${summodule_qrc}) set (dshow_additional_libs @@ -60,6 +60,6 @@ set (dshow_additional_libs add_library(${SUBMMODULE_NAME} SHARED ${submodule_srcs} ${submodule_headers} ${submodule_moc_srcs} ${submodule_qrc_srcs}) # qt5_use_modules(${SUBMMODULE_NAME} Core Concurrent Widgets Gui Qml) -target_link_libraries(${SUBMMODULE_NAME} Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Gui Qt5::Qml) +target_link_libraries(${SUBMMODULE_NAME} Qt6::Core Qt6::Concurrent Qt6::Widgets Qt6::Gui Qt6::Qml) target_link_libraries(${SUBMMODULE_NAME} ${AVKYS_PLUGIN_LIBRARIES} ${dshow_additional_libs} ipc) -install(TARGETS ${SUBMMODULE_NAME} DESTINATION ${AVKYS_LIBRARY_INSTALL_PATH}/submodules/${PLUGIN_NAME}) +install(TARGETS ${SUBMMODULE_NAME} RUNTIME DESTINATION ${AVKYS_LIBRARY_INSTALL_PATH}/submodules/${PLUGIN_NAME}) diff --git a/external/webcamoid/Plugins/VirtualCamera/src/dshow/src/cameraoutdshow.cpp b/external/webcamoid/Plugins/VirtualCamera/src/dshow/src/cameraoutdshow.cpp index 3abccd81..0a3c788c 100644 --- a/external/webcamoid/Plugins/VirtualCamera/src/dshow/src/cameraoutdshow.cpp +++ b/external/webcamoid/Plugins/VirtualCamera/src/dshow/src/cameraoutdshow.cpp @@ -211,7 +211,7 @@ void CameraOutDShow::writeFrame(const AkPacket &frame) DWORD(videoFrame.caps().height()), reinterpret_cast(videoFrame.buffer().constData())) < 1) { - qDebug() << "Error writing frame " << videoFrame.caps(); + //qDebug() << "Error writing frame " << videoFrame.caps(); } } diff --git a/external/webcamoid/Plugins/VirtualCamera/src/ffmpeg/CMakeLists.txt b/external/webcamoid/Plugins/VirtualCamera/src/ffmpeg/CMakeLists.txt index ecc64cff..7d5c5450 100644 --- a/external/webcamoid/Plugins/VirtualCamera/src/ffmpeg/CMakeLists.txt +++ b/external/webcamoid/Plugins/VirtualCamera/src/ffmpeg/CMakeLists.txt @@ -13,11 +13,11 @@ add_definitions(-DHAVE_SAMPLEFORMAT64) SET(LIBRARY_OUTPUT_PATH ${LIBRARY_OUTPUT_PATH}/submodules/${PLUGIN_NAME}) -find_package(Qt5Core REQUIRED) -find_package(Qt5Concurrent REQUIRED) -find_package(Qt5Gui REQUIRED) -find_package(Qt5Qml REQUIRED) -find_package(Qt5Widgets REQUIRED) +find_package(Qt6Core REQUIRED) +find_package(Qt6Concurrent REQUIRED) +find_package(Qt6Gui REQUIRED) +find_package(Qt6Qml REQUIRED) +find_package(Qt6Widgets REQUIRED) include_directories( ${AVKYS_PLUGIN_INCLUDES} @@ -45,17 +45,17 @@ set (submodule_qrc #This will generate moc_* for Qt -QT5_WRAP_CPP(submodule_moc_srcs ${submodule_headers}) +QT_WRAP_CPP(submodule_moc_srcs ${submodule_headers}) -QT5_ADD_RESOURCES(submodule_qrc_srcs ${summodule_qrc}) +QT6_ADD_RESOURCES(submodule_qrc_srcs ${summodule_qrc}) add_library(${PLUGIN_NAME}_${SUBMMODULE_NAME} SHARED ${submodule_srcs} ${submodule_headers} ${submodule_moc_srcs} ${submodule_qrc_srcs}) set_target_properties(${PLUGIN_NAME}_${SUBMMODULE_NAME} PROPERTIES OUTPUT_NAME "ffmpeg") # qt5_use_modules(${PLUGIN_NAME}_${SUBMMODULE_NAME} Core Concurrent Widgets Gui Qml) -target_link_libraries(${PLUGIN_NAME}_${SUBMMODULE_NAME} Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Gui Qt5::Qml) +target_link_libraries(${PLUGIN_NAME}_${SUBMMODULE_NAME} Qt6::Core Qt6::Concurrent Qt6::Widgets Qt6::Gui Qt6::Qml) target_link_libraries(${PLUGIN_NAME}_${SUBMMODULE_NAME} ${AVKYS_PLUGIN_LIBRARIES} ${FFMPEG_LIBS}) -install(TARGETS ${PLUGIN_NAME}_${SUBMMODULE_NAME} DESTINATION ${AVKYS_LIBRARY_INSTALL_PATH}/submodules/${PLUGIN_NAME}) +install(TARGETS ${PLUGIN_NAME}_${SUBMMODULE_NAME} RUNTIME DESTINATION ${AVKYS_LIBRARY_INSTALL_PATH}/submodules/${PLUGIN_NAME}) diff --git a/package/CMakeLists.txt b/package/CMakeLists.txt index d35b8e07..cad8e749 100644 --- a/package/CMakeLists.txt +++ b/package/CMakeLists.txt @@ -1,17 +1,20 @@ -find_package(Qt5Core REQUIRED) +find_package(Qt6Core REQUIRED) message(STATUS "Entering package directory.") include(InstallRequiredSystemLibraries) -get_target_property(_qt5_qmake_location Qt5::qmake IMPORTED_LOCATION) +get_target_property(_qt6_qmake_location Qt6::qmake IMPORTED_LOCATION) execute_process( - COMMAND "${_qt5_qmake_location}" -query QT_INSTALL_PREFIX + COMMAND "${_qt6_qmake_location}" -query QT_INSTALL_PREFIX RESULT_VARIABLE return_code - OUTPUT_VARIABLE qt5_install_prefix + OUTPUT_VARIABLE qt6_install_prefix OUTPUT_STRIP_TRAILING_WHITESPACE ) -message(STATUS "Testing package ${_qt5_qmake_location} ${qt5_install_prefix}") +message(STATUS "Testing package ${_qt6_qmake_location} ${qt6_install_prefix}") + +# Pre-build script, used to clean up unneeded files for example +set(CPACK_PRE_BUILD_SCRIPTS ${CMAKE_CURRENT_SOURCE_DIR}/cpack-prebuild.cmake) if (APPLE) cmake_policy(SET CMP0087 NEW) # Enable generator expressions in INSTALL(CODE @@ -24,13 +27,13 @@ if (APPLE) set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) # add_custom_target(mac_deploy_qt -# COMMAND ${qt5_install_prefix}/bin/macdeployqt "$/../.." -always-overwrite -codesign="" +# COMMAND ${qt6_install_prefix}/bin/macdeployqt "$/../.." -always-overwrite -codesign="" # COMMENT "Running macdeployqt ..." # DEPENDS OpenTeraPlus # ) set(MACSIGNID "") - set(MACQTDEPLOY_COMMAND "${qt5_install_prefix}/bin/macdeployqt $/../.. -always-overwrite -verbose=1") + set(MACQTDEPLOY_COMMAND "${qt6_install_prefix}/bin/macdeployqt $/../.. -always-overwrite -verbose=1") # message(STATUS "********** ${MACQTDEPLOY_COMMAND}") @@ -67,8 +70,9 @@ if (WIN32) # Create files for installation add_custom_target(windows_deploy_qt # Collect and copy required dependencies - COMMAND ${qt5_install_prefix}/bin/windeployqt "$/OpenTeraPlus.exe" --release --dir "${CMAKE_INSTALL_PREFIX}/qtdeploy" - COMMENT "Running windeployqt ..." + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_INSTALL_PREFIX}/qtdeploy + COMMAND ${qt6_install_prefix}/bin/windeployqt6 "$/OpenTeraPlus.exe" --release --dir "${CMAKE_INSTALL_PREFIX}/qtdeploy" --no-quick-import --no-compiler-runtime + COMMENT "Running windeployqt6 ..." ) set(CPACK_PACKAGE_NAME "OpenTeraPlus") @@ -86,10 +90,10 @@ if (WIN32) set(CPACK_COMPONENT_OPENTERAPLUS_DEPENDS libprotobuf qt_dist_component openssl) set(CPACK_COMPONENT_OPENTERAPLUS_REQUIRED ON) - set(CPACK_COMPONENT_QT_DIST_COMPONENT_DISPLAY_NAME "Librairies Qt") + set(CPACK_COMPONENT_QT_DIST_COMPONENT_DISPLAY_NAME "Qt") set(CPACK_COMPONENT_QT_DIST_COMPONENT_REQUIRED ON) - set(CPACK_COMPONENT_LIBPROTOBUF_DISPLAY_NAME "Librairies Protobuf") + set(CPACK_COMPONENT_LIBPROTOBUF_DISPLAY_NAME "Protobuf") set(CPACK_COMPONENT_LIBPROTOBUF_REQUIRED ON) set(CPACK_COMPONENT_OPENSSL_DISPLAY_NAME "OpenSSL") @@ -99,7 +103,7 @@ if (WIN32) if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/config/OpenTeraPlus.json") # The components that will be packaged set(CPACK_COMPONENTS_ALL ${CPACK_COMPONENTS_ALL} special_configuration) - set(CPACK_COMPONENT_SPECIAL_CONFIGURATION_DISPLAY_NAME "Fichier de configuration") + set(CPACK_COMPONENT_SPECIAL_CONFIGURATION_DISPLAY_NAME "Configuration") install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/config/OpenTeraPlus.json DESTINATION bin/config COMPONENT special_configuration) endif() @@ -107,26 +111,31 @@ if (WIN32) # list(APPEND CPACK_NSIS_EXTRA_INSTALL_COMMANDS "ExecWait 'cmd /c $INSTDIR\\\\bin\\\\installDriver.exe'") # list(APPEND CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS "ExecWait 'cmd /c $INSTDIR\\\\bin\\\\uninstallDriver.exe'") - # add SSL librariesOpenSSL\Win_x64\bin - # file(GLOB OPENSSL_LIBRARIES "${qt5_install_prefix}/../../Tools/OpenSSL/Win_x64/bin/*.dll") + # file(GLOB OPENSSL_LIBRARIES "${qt6_install_prefix}/../../Tools/OpenSSL/Win_x64/bin/*.dll") file(GLOB OPENSSL_LIBRARIES "${CMAKE_CURRENT_SOURCE_DIR}/openssl/64bits/*.dll") - message(STATUS "******************* ${OPENSSL_LIBRARIES} ${qt5_install_prefix} ${CPACK_PACKAGING_INSTALL_PREFIX}") + message(STATUS "******************* ${OPENSSL_LIBRARIES} ${qt6_install_prefix} ${CPACK_PACKAGING_INSTALL_PREFIX}") install(FILES ${OPENSSL_LIBRARIES} DESTINATION bin COMPONENT openssl) - # Uses NSIS packaging system on Windows SET(CPACK_SOURCE_GENERATOR "NSIS") - - # SET(CPACK_NSIS_INSTALL_ROOT "C:\\\\INTER") SET(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES64") SET(CPACK_PACKAGE_INSTALL_DIRECTORY "OpenTeraPlus") SET(CPACK_NSIS_PACKAGE_NAME "OpenTeraPlus") SET(CPACK_NSIS_MODIFY_PATH OFF) SET(CPACK_NSIS_EXECUTABLES_DIRECTORY .) SET(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON) - SET(CPACK_NSIS_MUI_FINISHPAGE_RUN "bin\\\\OpenTeraPlus.exe") + + set(CPACK_NSIS_CREATE_ICONS_EXTRA + "CreateShortCut '$DESKTOP\\\\OpenTeraPlus.lnk' '$INSTDIR\\\\bin\\\\OpenTeraPlus.exe'" + ) + set(CPACK_NSIS_DELETE_ICONS_EXTRA + "Delete '$DESKTOP\\\\OpenTeraPlus.lnk'" + ) + + # Removed run after install option, as this caused issue with config file (will run as admin!) + # SET(CPACK_NSIS_MUI_FINISHPAGE_RUN "bin\\\\OpenTeraPlus.exe") SET(CPACK_NSIS_URL_INFO_ABOUT "https://github.com/introlab/openteraplus") SET(CPACK_NSIS_CONTACT "simon.briere@usherbrooke.ca") @@ -146,11 +155,16 @@ if (WIN32) VIAddVersionKey ProductVersion ${CPACK_PACKAGE_VERSION}") - include(CPack REQUIRED) + # Run VC++ redistributable + list(APPEND CPACK_NSIS_EXTRA_INSTALL_COMMANDS "ExecWait '\\\"$INSTDIR\\\\bin\\\\vc_redist.x64.exe\\\" /install /passive /norestart'") + + # The trick is to deploy Qt to a directory and then install the directory in the bin folder + install(DIRECTORY ${CMAKE_INSTALL_PREFIX}/qtdeploy/ DESTINATION bin COMPONENT qt_dist_component) + # Deploy before packaging + add_custom_target(nsis-package DEPENDS windows_deploy_qt package) - # Run VC++ redistributable - list(APPEND CPACK_NSIS_EXTRA_INSTALL_COMMANDS "ExecWait 'cmd /c $INSTDIR\\\\bin\\\\VC_redist.x64.exe /install /passive /norestart'") + include(CPack REQUIRED) # Set files to install - everything in deploy folder #install( @@ -162,11 +176,6 @@ if (WIN32) # install(CODE " ") - # The trick is to deploy Qt to a directory and then install the directory in the bin folder - install(DIRECTORY ${CMAKE_INSTALL_PREFIX}/qtdeploy/ DESTINATION bin COMPONENT qt_dist_component) - - # Deploy before packaging - add_custom_target(nsis-package DEPENDS windows_deploy_qt package) endif (WIN32) diff --git a/package/cpack-prebuild.cmake b/package/cpack-prebuild.cmake new file mode 100644 index 00000000..4193c426 --- /dev/null +++ b/package/cpack-prebuild.cmake @@ -0,0 +1,6 @@ +if(WIN32) + message("Cleaning unneeded files for package...") + file(REMOVE_RECURSE ${CPACK_TEMPORARY_INSTALL_DIRECTORY}/libprotobuf/lib) + file(REMOVE_RECURSE ${CPACK_TEMPORARY_INSTALL_DIRECTORY}/openteraplus/lib) + file(REMOVE_RECURSE ${CPACK_TEMPORARY_INSTALL_DIRECTORY}/openteraplus/include) +endif(WIN32) diff --git a/package/openssl/64bits/libcrypto-1_1-x64.dll b/package/openssl/64bits/libcrypto-1_1-x64.dll deleted file mode 100644 index d91fe73b..00000000 Binary files a/package/openssl/64bits/libcrypto-1_1-x64.dll and /dev/null differ diff --git a/package/openssl/64bits/libcrypto-3-x64.dll b/package/openssl/64bits/libcrypto-3-x64.dll new file mode 100644 index 00000000..370140ba Binary files /dev/null and b/package/openssl/64bits/libcrypto-3-x64.dll differ diff --git a/package/openssl/64bits/libssl-1_1-x64.dll b/package/openssl/64bits/libssl-1_1-x64.dll deleted file mode 100644 index 43baf830..00000000 Binary files a/package/openssl/64bits/libssl-1_1-x64.dll and /dev/null differ diff --git a/package/openssl/64bits/libssl-3-x64.dll b/package/openssl/64bits/libssl-3-x64.dll new file mode 100644 index 00000000..708d1ae6 Binary files /dev/null and b/package/openssl/64bits/libssl-3-x64.dll differ diff --git a/shared/src/CMakeLists.txt b/shared/src/CMakeLists.txt index 521c2466..70a05c47 100755 --- a/shared/src/CMakeLists.txt +++ b/shared/src/CMakeLists.txt @@ -1,5 +1,5 @@ -find_package(Qt5Core REQUIRED) -find_package(Qt5Multimedia REQUIRED) +find_package(Qt6Core REQUIRED) +find_package(Qt6Multimedia REQUIRED) add_definitions(-DSHAREDLIB_LIBRARY) if (MSVC) @@ -8,6 +8,7 @@ if (MSVC) /wd4910 # Ignore export warnings (C4910) with MSVC /wd4661 # Ignore no suitable definition for explicit template (C4661) /wd4251 # Ignore needs to have dll-interface + /wd4146 # Ignore unary minus operator applied to unsigned type, result still unsigned ) endif(MSVC) @@ -52,23 +53,27 @@ include_directories( ) #This will generate moc_* for Qt -qt5_wrap_cpp(sharedlib_moc_srcs ${sharedlib_headers}) +qt6_wrap_cpp(sharedlib_moc_srcs ${sharedlib_headers}) #compile library -add_library(opentera_shared SHARED ${sharedlib_headers} ${sharedlib_srcs} ${sharedlib_moc_srcs}) -# qt5_use_modules(opentera_shared Core Network) +if(NOT DEFINED OPENTERA_WEBASSEMBLY) + add_library(opentera_shared SHARED ${sharedlib_headers} ${sharedlib_srcs} ${sharedlib_moc_srcs}) +else() + add_library(opentera_shared STATIC ${sharedlib_headers} ${sharedlib_srcs} ${sharedlib_moc_srcs}) +endif() +# qt6_use_modules(opentera_shared Core Network) set_target_properties(opentera_shared PROPERTIES INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/bin INSTALL_RPATH_USE_LINK_PATH TRUE) -target_link_libraries(opentera_shared Qt5::Core Qt5::Multimedia) + target_link_libraries(opentera_shared Qt6::Core) if (MSVC) target_compile_definitions(opentera_shared PUBLIC OPENTERAMESSAGES_EXPORT=__declspec\(dllimport\)) endif(MSVC) # Install target -install(TARGETS opentera_shared DESTINATION ${CMAKE_INSTALL_BINDIR}) +install(TARGETS opentera_shared RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) set(OPENTERA_SHARED_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/data CACHE INTERNAL "doc string") set(OPENTERA_SHARED_LIBS "opentera_shared" CACHE INTERNAL "doc string") diff --git a/shared/src/Logger.cpp b/shared/src/Logger.cpp index eb18782d..b41e59f7 100755 --- a/shared/src/Logger.cpp +++ b/shared/src/Logger.cpp @@ -2,11 +2,11 @@ #include #include #include - +#include //Singleton Logger* Logger::m_instance = nullptr; -QMutex Logger::m_mutex(QMutex::Recursive); +QRecursiveMutex Logger::m_mutex; Logger::ActivationPriority Logger::m_active_priority = Logger::DISABLE_PRIORITY; //DISABLE_PRIORITY; //INFO_PRIORITY; // diff --git a/shared/src/Logger.h b/shared/src/Logger.h index 41ca4e07..84806018 100755 --- a/shared/src/Logger.h +++ b/shared/src/Logger.h @@ -165,7 +165,7 @@ class SHAREDLIB_EXPORT Logger : public QObject ///The file where to log QFile m_file; ///Mutex for thread safety - static QMutex m_mutex; + static QRecursiveMutex m_mutex; ///Msg Priority static ActivationPriority m_active_priority; diff --git a/shared/src/SimpleCrypt.cpp b/shared/src/SimpleCrypt.cpp index 278e6dde..a259ae67 100644 --- a/shared/src/SimpleCrypt.cpp +++ b/shared/src/SimpleCrypt.cpp @@ -26,11 +26,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "SimpleCrypt.h" #include +#include #include #include #include #include #include +#include SimpleCrypt::SimpleCrypt(): m_key(0), @@ -38,7 +40,7 @@ SimpleCrypt::SimpleCrypt(): m_protectionMode(ProtectionChecksum), m_lastError(ErrorNoError) { - qsrand(uint(QDateTime::currentMSecsSinceEpoch() & 0xFFFF)); + srand(uint(QDateTime::currentMSecsSinceEpoch() & 0xFFFF)); } SimpleCrypt::SimpleCrypt(quint64 key): @@ -47,7 +49,7 @@ SimpleCrypt::SimpleCrypt(quint64 key): m_protectionMode(ProtectionChecksum), m_lastError(ErrorNoError) { - qsrand(uint(QDateTime::currentMSecsSinceEpoch() & 0xFFFF)); + srand(uint(QDateTime::currentMSecsSinceEpoch() & 0xFFFF)); splitKey(); } @@ -93,7 +95,7 @@ QByteArray SimpleCrypt::encryptToByteArray(QByteArray plaintext) flags |= CryptoFlagCompression; } else if (m_compressionMode == CompressionAuto) { QByteArray compressed = qCompress(ba, 9); - if (compressed.count() < ba.count()) { + if (compressed.size() < ba.size()) { ba = compressed; flags |= CryptoFlagCompression; } @@ -103,7 +105,7 @@ QByteArray SimpleCrypt::encryptToByteArray(QByteArray plaintext) if (m_protectionMode == ProtectionChecksum) { flags |= CryptoFlagChecksum; QDataStream s(&integrityProtection, QIODevice::WriteOnly); - s << qChecksum(ba.constData(), ba.length()); + s << qChecksum(QByteArrayView(ba)); } else if (m_protectionMode == ProtectionHash) { flags |= CryptoFlagHash; QCryptographicHash hash(QCryptographicHash::Sha1); @@ -113,13 +115,13 @@ QByteArray SimpleCrypt::encryptToByteArray(QByteArray plaintext) } //prepend a random char to the string - char randomChar = char(qrand() & 0xFF); + char randomChar = char(rand() & 0xFF); ba = randomChar + integrityProtection + ba; int pos(0); char lastChar(0); - int cnt = ba.count(); + auto cnt = ba.size(); while (pos < cnt) { ba[pos] = ba.at(pos) ^ m_keyParts.at(pos % 8) ^ lastChar; @@ -188,7 +190,7 @@ QByteArray SimpleCrypt::decryptToByteArray(QByteArray cypher) QByteArray ba = cypher; - if( cypher.count() < 3 ) + if( cypher.size() < 3 ) return QByteArray(); char version = ba.at(0); @@ -203,7 +205,7 @@ QByteArray SimpleCrypt::decryptToByteArray(QByteArray cypher) ba = ba.mid(2); int pos(0); - int cnt(ba.count()); + auto cnt(ba.size()); char lastChar = 0; while (pos < cnt) { @@ -227,7 +229,7 @@ QByteArray SimpleCrypt::decryptToByteArray(QByteArray cypher) s >> storedChecksum; } ba = ba.mid(2); - quint16 checksum = qChecksum(ba.constData(), ba.length()); + quint16 checksum = qChecksum(QByteArrayView(ba)); integrityOk = (checksum == storedChecksum); } else if (flags.testFlag(CryptoFlagHash)) { if (ba.length() < 20) { diff --git a/shared/src/SimpleCrypt.h b/shared/src/SimpleCrypt.h index 7e9cf44b..20de635d 100644 --- a/shared/src/SimpleCrypt.h +++ b/shared/src/SimpleCrypt.h @@ -27,6 +27,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef SIMPLECRYPT_H #define SIMPLECRYPT_H + +#include "SharedLib.h" #include #include #include @@ -54,7 +56,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. SimpleCrypt is prepared for the case that the encryption and decryption algorithm is changed in a later version, by prepending a version identifier to the cypertext. */ -class SimpleCrypt +class SHAREDLIB_EXPORT SimpleCrypt { public: /** diff --git a/shared/src/Utils.cpp b/shared/src/Utils.cpp index 812247fd..bfa6447f 100644 --- a/shared/src/Utils.cpp +++ b/shared/src/Utils.cpp @@ -1,4 +1,5 @@ #include "Utils.h" +#include Utils::Utils(QObject *parent) : QObject(parent) { @@ -16,7 +17,7 @@ QString Utils::generatePassword(const int &len) password.clear(); for(int i=0; ibounded(possibleCharacters.length()); + auto index = QRandomGenerator::global()->bounded(possibleCharacters.length()); QChar nextChar = possibleCharacters.at(index); password.append(nextChar); } @@ -33,23 +34,24 @@ QList Utils::validatePassword(const QString &pa errors.append(PASSWORD_LENGTH); } - QRegExp validator("[A-Z]"); - if (validator.indexIn(password) == -1){ + QRegularExpression validator("[A-Z]"); + + if (!validator.match(password).hasMatch()){ errors.append(PASSWORD_NOCAPS); } validator.setPattern("[a-z]"); - if (validator.indexIn(password) == -1){ + if (!validator.match(password).hasMatch()){ errors.append(PASSWORD_NONOCAPS); } validator.setPattern("[0-9]"); - if (validator.indexIn(password) == -1){ + if (!validator.match(password).hasMatch()){ errors.append(PASSWORD_NODIGITS); } validator.setPattern("[^A-Za-z0-9]"); - if (validator.indexIn(password) == -1){ + if (!validator.match(password).hasMatch()){ errors.append(PASSWORD_NOCHAR); } @@ -79,66 +81,14 @@ QString Utils::getMachineUniqueId() return machine_id; } -QStringList Utils::getAudioDeviceNames() -{ - QStringList names; - QList audio_devices = QAudioDeviceInfo::availableDevices(QAudio::AudioInput); - foreach(QAudioDeviceInfo input, audio_devices){ -#ifdef Q_OS_LINUX - // On linux, since Qt use ALSA API, we must filter the returned device list... - if (input.deviceName().startsWith("alsa_input") || input.deviceName() == "default"){ - QString filtered_name = input.deviceName(); - if (input.deviceName() != "default"){ - QStringList name_parts = filtered_name.split("."); - // Removes first part - usually is "alsa_input" - if (name_parts.count()>1) - filtered_name = name_parts[1]; - - // Split using "_" to remove first and last part, and replace others with spaces - name_parts = filtered_name.split("_"); - if (name_parts.count()>2){ - filtered_name = ""; - for (int i=1; i1) - filtered_name += " "; - filtered_name += name_parts[i]; - } - }else{ - // No audio name... - name_parts = filtered_name.split("-"); - if (name_parts.count()>2){ - filtered_name = name_parts[1].replace("_", ":"); - } - } - - } - names.append(filtered_name); - } -#else - names.append(input.deviceName()); -#endif - } - return names; -} - -QStringList Utils::getVideoDeviceNames() -{ - QStringList names; - QList video_devices = QCameraInfo::availableCameras(); - for (const QCameraInfo &camera:qAsConst(video_devices)){ - names.append(camera.description()); - } - return names; -} - void Utils::inStringUnicodeConverter(QString *str) { if (str->contains("\\u")) { do { - int idx = str->indexOf("\\u"); + auto idx = str->indexOf("\\u"); QString strHex = str->mid(idx, 6); strHex = strHex.replace("\\u", QString()); - int nHex = strHex.toInt(0, 16); + auto nHex = strHex.toInt(nullptr, 16); str->replace(idx, 6, QChar(nHex)); } while (str->indexOf("\\u") != -1); } @@ -151,7 +101,7 @@ QString Utils::removeAccents(const QString &s) { QString output = ""; for (int i = 0; i < s.length(); i++) { QChar c = s[i]; - int dIndex = diacriticLetters_.indexOf(c); + auto dIndex = diacriticLetters_.indexOf(c); if (dIndex < 0) { output.append(c); } else { @@ -217,4 +167,3 @@ QString Utils::formatDuration(const QString &duration) } return video_duration.toString("hh:mm:ss"); } - diff --git a/shared/src/Utils.h b/shared/src/Utils.h index 4162296e..4e6be93c 100644 --- a/shared/src/Utils.h +++ b/shared/src/Utils.h @@ -1,17 +1,16 @@ #ifndef UTILS_H #define UTILS_H +#include "SharedLib.h" #include #include #include #include #include #include -#include -#include #include -class Utils : public QObject +class SHAREDLIB_EXPORT Utils : public QObject { Q_OBJECT public: @@ -30,9 +29,6 @@ class Utils : public QObject static QString getMachineUniqueId(); - static QStringList getAudioDeviceNames(); - static QStringList getVideoDeviceNames(); - static void inStringUnicodeConverter(QString* str); static QString removeAccents(const QString& s); diff --git a/shared/src/WebAPI.h b/shared/src/WebAPI.h index 42181339..a6b68037 100755 --- a/shared/src/WebAPI.h +++ b/shared/src/WebAPI.h @@ -33,6 +33,7 @@ #define WEB_SERVICESITEINFO_PATH "/api/user/services/sites" #define WEB_SERVICEACCESSINFO_PATH "/api/user/services/access" #define WEB_SERVICECONFIGINFO_PATH "/api/user/services/configs" +#define WEB_SERVICEROLEINFO_PATH "/api/user/services/roles" #define WEB_STATS_PATH "/api/user/stats" #define WEB_ASSETINFO_PATH "/api/user/assets" #define WEB_TESTTYPEINFO_PATH "/api/user/testtypes" @@ -112,6 +113,7 @@ #define WEB_QUERY_START_DATE "start_date" #define WEB_QUERY_END_DATE "end_date" #define WEB_QUERY_LOG_LEVEL "log_level" +#define WEB_QUERY_GLOBALS "globals" #define WEB_QUERY_WITH_USERGROUPS "with_usergroups" #define WEB_QUERY_WITH_SITES "with_sites" @@ -127,6 +129,7 @@ #define WEB_QUERY_WITH_URLS "with_urls" #define WEB_QUERY_WITH_ONLY_TOKEN "with_only_token" #define WEB_QUERY_WITH_SESSIONTYPE "with_session_type" +#define WEB_QUERY_WITH_TESTTYPES "with_test_types" #define WEB_QUERY_WITH_DEVICES "with_devices" #define WEB_QUERY_WITH_NAMES "with_names" diff --git a/shared/src/data/TeraAsset.h b/shared/src/data/TeraAsset.h index a328a450..fddf55b0 100644 --- a/shared/src/data/TeraAsset.h +++ b/shared/src/data/TeraAsset.h @@ -1,11 +1,12 @@ #ifndef TERAASSET_H #define TERAASSET_H +#include "SharedLib.h" #include // #include // #include -class TeraAsset : public QObject +class SHAREDLIB_EXPORT TeraAsset : public QObject { Q_OBJECT public: diff --git a/shared/src/data/TeraData.cpp b/shared/src/data/TeraData.cpp index 3f18cd03..a194b788 100644 --- a/shared/src/data/TeraData.cpp +++ b/shared/src/data/TeraData.cpp @@ -53,7 +53,7 @@ QString TeraData::getUuid() const { QVariant raw_uuid = getFieldValue(m_uuidField); - if (raw_uuid.isValid() && raw_uuid.canConvert(QMetaType::QString)){ + if (raw_uuid.isValid() && raw_uuid.canConvert()){ return raw_uuid.toString(); } return ""; @@ -170,7 +170,7 @@ void TeraData::updateFrom(const TeraData &other) void TeraData::updateFrom(const QJsonObject &object) { QVariantHash fields = object.toVariantHash(); - for(const QVariant &field_value:qAsConst(fields)){ + for(const QVariant &field_value:std::as_const(fields)){ m_fieldsValue[fields.key(field_value)] = field_value; } } @@ -293,6 +293,8 @@ QString TeraData::getDataTypeName(const TeraDataTypes &data_type) return "service_access"; case TERADATA_SERVICE_CONFIG: return "service_config"; + case TERADATA_SERVICE_ROLE: + return "service_role"; case TERADATA_STATS: return "stats"; case TERADATA_USERPREFERENCE: @@ -370,9 +372,11 @@ QString TeraData::getDataTypeNameText(const TeraDataTypes &data_type) case TERADATA_SERVICE_SITE: return tr("Service: site"); case TERADATA_SERVICE_ACCESS: - return tr("Service: Accès"); + return tr("Service: accès"); case TERADATA_SERVICE_CONFIG: - return tr("Service: Configuration"); + return tr("Service: configuration"); + case TERADATA_SERVICE_ROLE: + return tr("Service: rôle"); case TERADATA_STATS: return tr("Statistiques"); case TERADATA_ONLINE_DEVICE: @@ -420,6 +424,7 @@ TeraDataTypes TeraData::getDataTypeFromPath(const QString &path) if (path==WEB_SERVICESITEINFO_PATH) return TERADATA_SERVICE_SITE; if (path==WEB_SERVICEACCESSINFO_PATH) return TERADATA_SERVICE_ACCESS; if (path==WEB_SERVICECONFIGINFO_PATH) return TERADATA_SERVICE_CONFIG; + if (path==WEB_SERVICEROLEINFO_PATH) return TERADATA_SERVICE_ROLE; if (path==WEB_USERUSERGROUPINFO_PATH) return TERADATA_USERUSERGROUP; if (path==WEB_USERPREFSINFO_PATH) return TERADATA_USERPREFERENCE; if (path==WEB_STATS_PATH) return TERADATA_STATS; @@ -455,6 +460,7 @@ QString TeraData::getPathForDataType(const TeraDataTypes &data_type) if (data_type==TERADATA_DEVICESUBTYPE) return WEB_DEVICESUBTYPE_PATH; if (data_type==TERADATA_DEVICETYPE) return WEB_DEVICETYPE_PATH; if (data_type==TERADATA_SERVICE) return WEB_SERVICEINFO_PATH; + if (data_type==TERADATA_SERVICE_ACCESS) return WEB_SERVICEACCESSINFO_PATH; if (data_type==TERADATA_SITEACCESS) return WEB_SITEACCESS_PATH; if (data_type==TERADATA_SERVICE_CONFIG) return WEB_SERVICECONFIGINFO_PATH; if (data_type==TERADATA_ASSET) return WEB_ASSETINFO_PATH; @@ -478,7 +484,7 @@ QString TeraData::getIconFilenameForDataType(const TeraDataTypes &data_type) case TERADATA_SITE: case TERADATA_SERVICE_SITE: case TERADATA_SESSIONTYPESITE: - return "://icons/site.png"; + return "://icons/site-icon.png"; case TERADATA_SESSIONTYPE: return "://icons/session_type.png"; case TERADATA_SESSION: @@ -496,7 +502,7 @@ QString TeraData::getIconFilenameForDataType(const TeraDataTypes &data_type) case TERADATA_PROJECT: case TERADATA_SERVICE_PROJECT: case TERADATA_SESSIONTYPEPROJECT: - return "://icons/project.png"; + return "://icons/project-icon.png"; case TERADATA_DEVICESUBTYPE: case TERADATA_DEVICETYPE: return "://icons/kit.png"; @@ -550,6 +556,13 @@ QString TeraData::getIconStateFilename() const if (isEnabled()) return "://icons/patient_installed.png"; return "://icons/patient.png"; + case TERADATA_PROJECT: + if (isEnabled() || !hasEnabledField()){ + return "://icons/project.png"; + }else{ + return "://icons/project_disabled.png"; + } + break; default: return "://icons/error.png"; } @@ -634,14 +647,14 @@ QJsonObject TeraData::toJson(const QString specific_fieldName) } // Ignore "metaObject" properties QVariant fieldData = getFieldValue(fieldName); - if (fieldData.canConvert(QMetaType::QString)){ + if (fieldData.canConvert()){ QDateTime date_tester = QDateTime::fromString(fieldData.toString(), Qt::ISODateWithMs); if (date_tester.isValid()){ object[fieldName] = fieldData.toDateTime().toString(Qt::ISODateWithMs); }else{ object[fieldName] = fieldData.toString(); } - }else if (fieldData.canConvert(QMetaType::QJsonValue)){ + }else if (fieldData.canConvert()){ object[fieldName] = fieldData.toJsonValue(); }else{ LOG_WARNING("Field " + fieldName + " can't be 'jsonized'", "TeraData::toJson"); @@ -653,8 +666,10 @@ QJsonObject TeraData::toJson(const QString specific_fieldName) bool TeraData::fromMap(const QVariantMap &map) { - foreach(QVariant value, map){ - QString key = map.key(value); + QStringList keys = map.keys(); + + foreach(QString key, keys){ + QVariant value = map.value(key); setFieldValue(key, value); } diff --git a/shared/src/data/TeraData.h b/shared/src/data/TeraData.h index 0a9f6ea1..2144ce4a 100644 --- a/shared/src/data/TeraData.h +++ b/shared/src/data/TeraData.h @@ -11,6 +11,7 @@ #include "WebAPI.h" #include "ParticipantWebAPI.h" +#include "SharedLib.h" enum TeraDataTypes { TERADATA_NONE, @@ -42,6 +43,7 @@ enum TeraDataTypes { TERADATA_SERVICE_PROJECT, TERADATA_SERVICE_SITE, TERADATA_SERVICE_ACCESS, + TERADATA_SERVICE_ROLE, TERADATA_SERVICE_CONFIG, TERADATA_STATS, TERADATA_ONLINE_USER, @@ -55,7 +57,7 @@ enum TeraDataTypes { Q_DECLARE_METATYPE(TeraDataTypes) -class TeraData : public QObject +class SHAREDLIB_EXPORT TeraData : public QObject { Q_OBJECT diff --git a/shared/src/data/TeraPreferences.h b/shared/src/data/TeraPreferences.h index b86b6e30..c9dc00d6 100644 --- a/shared/src/data/TeraPreferences.h +++ b/shared/src/data/TeraPreferences.h @@ -4,8 +4,9 @@ #include #include "TeraData.h" #include +#include "SharedLib.h" -class TeraPreferences : public QObject +class SHAREDLIB_EXPORT TeraPreferences : public QObject { Q_OBJECT diff --git a/shared/src/data/TeraSessionCategory.h b/shared/src/data/TeraSessionCategory.h index e8c4e6b9..5d963b81 100644 --- a/shared/src/data/TeraSessionCategory.h +++ b/shared/src/data/TeraSessionCategory.h @@ -2,8 +2,9 @@ #define TERASESSIONCATEGORY_H #include +#include "SharedLib.h" -class TeraSessionCategory : public QObject +class SHAREDLIB_EXPORT TeraSessionCategory : public QObject { Q_OBJECT public: diff --git a/shared/src/data/TeraSessionEvent.h b/shared/src/data/TeraSessionEvent.h index 32a0cd0c..a44d24e0 100644 --- a/shared/src/data/TeraSessionEvent.h +++ b/shared/src/data/TeraSessionEvent.h @@ -2,8 +2,9 @@ #define TERASESSIONEVENT_H #include +#include "SharedLib.h" -class TeraSessionEvent : public QObject +class SHAREDLIB_EXPORT TeraSessionEvent : public QObject { Q_OBJECT public: diff --git a/shared/src/data/TeraSessionStatus.h b/shared/src/data/TeraSessionStatus.h index c1a7430f..930a62ad 100644 --- a/shared/src/data/TeraSessionStatus.h +++ b/shared/src/data/TeraSessionStatus.h @@ -2,8 +2,9 @@ #define TERASESSIONSTATUS_H #include +#include "SharedLib.h" -class TeraSessionStatus : public QObject +class SHAREDLIB_EXPORT TeraSessionStatus : public QObject { Q_OBJECT public: diff --git a/shared/src/data/TeraSettings.h b/shared/src/data/TeraSettings.h index 23672ab0..04cb3aa5 100644 --- a/shared/src/data/TeraSettings.h +++ b/shared/src/data/TeraSettings.h @@ -2,6 +2,7 @@ #define TERASETTINGS_H #include +#include "SharedLib.h" #define SETTINGS_UI_ADVANCEDVIEW "ui_advancedView" #define SETTINGS_UI_FILTERINACTIVE "ui_filterInactives" @@ -13,7 +14,7 @@ #define SETTINGS_LASTLANGUAGE "lastLanguage" #define SETTINGS_DATA_SAVEPATH "dataSavePath" -class TeraSettings +class SHAREDLIB_EXPORT TeraSettings { public: TeraSettings(); diff --git a/shared/src/data/TeraTest.h b/shared/src/data/TeraTest.h index 12c60c86..470a54cb 100644 --- a/shared/src/data/TeraTest.h +++ b/shared/src/data/TeraTest.h @@ -2,8 +2,9 @@ #define TERATEST_H #include +#include "SharedLib.h" -class TeraTest : public QObject +class SHAREDLIB_EXPORT TeraTest : public QObject { Q_OBJECT public: diff --git a/shared/src/qrcodegen.hpp b/shared/src/qrcodegen.hpp index 208a6c68..23bb6c79 100644 --- a/shared/src/qrcodegen.hpp +++ b/shared/src/qrcodegen.hpp @@ -23,6 +23,7 @@ #pragma once +#include "SharedLib.h" #include #include #include diff --git a/wasm/CMakeLists.txt b/wasm/CMakeLists.txt new file mode 100644 index 00000000..47c9163a --- /dev/null +++ b/wasm/CMakeLists.txt @@ -0,0 +1,143 @@ +project(OpenTeraPlusWebAssembly) + +#Look for minimum cmake version +cmake_minimum_required(VERSION 3.21) + +# Software version +SET(CPACK_PACKAGE_VERSION_MAJOR "1") +SET(CPACK_PACKAGE_VERSION_MINOR "2") +SET(CPACK_PACKAGE_VERSION_PATCH "0") +SET(CPACK_PACKAGE_VERSION ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}) + +add_definitions(-DOPENTERAPLUS_VERSION="${CPACK_PACKAGE_VERSION}" ) +add_definitions(-DOPENTERAPLUS_VERSION_MAJOR="${CPACK_PACKAGE_VERSION_MAJOR}" ) +add_definitions(-DOPENTERAPLUS_VERSION_MINOR="${CPACK_PACKAGE_VERSION_MINOR}" ) +add_definitions(-DOPENTERAPLUS_VERSION_PATCH="${CPACK_PACKAGE_VERSION_PATCH}" ) +add_definitions(-DOPENTERAPLUS_CLIENT_NAME="OpenTeraPlus") + +set(CMAKE_CXX_STANDARD 17) + +# Compile in debug or release mode +if(NOT CMAKE_BUILD_TYPE) + SET(CMAKE_BUILD_TYPE Release) + #set(CMAKE_BUILD_TYPE Debug) +endif(NOT CMAKE_BUILD_TYPE) + +# Building protobuf webasm +# https://github.com/protocolbuffers/protobuf/releases/download/v22.3/protoc-22.3-win64.zip + +if (APPLE) + set(protoc_archive_name protoc-22.3-osx-x86_64.zip) + set(protobuf_PROTOC_EXE ${CMAKE_CURRENT_BINARY_DIR}/protoc/bin/protoc) +elseif(WIN32) + set(protoc_archive_name protoc-22.3-win64.zip) + set(protobuf_PROTOC_EXE ${CMAKE_CURRENT_BINARY_DIR}/protoc/bin/protoc.exe) +else() + set(protoc_archive_name protoc-22.3-linux-x86_64.zip) + set(protobuf_PROTOC_EXE ${CMAKE_CURRENT_BINARY_DIR}/protoc/bin/protoc) +endif() + + +set(protc_binaries_url https://github.com/protocolbuffers/protobuf/releases/download/v22.3/${protoc_archive_name}) + +# Download Archive +if(NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/${protoc_archive_name}) + file(DOWNLOAD + ${protc_binaries_url} + ${CMAKE_CURRENT_BINARY_DIR}/${protoc_archive_name} + TIMEOUT 300 #seconds + TLS_VERIFY ON + SHOW_PROGRESS) +endif() + +# Extract Archive +# No protoc, will download native +file(ARCHIVE_EXTRACT INPUT ${CMAKE_CURRENT_BINARY_DIR}/${protoc_archive_name} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/protoc) + +set(ABSL_PROPAGATE_CXX_STD ON) +set(protobuf_BUILD_EXAMPLES OFF) +set(protobuf_BUILD_PROTOC_BINARIES OFF) +set(protobuf_BUILD_LIBPROTOC OFF) +set(protobuf_BUILD_SHARED_LIBS_DEFAULT OFF) +set(protobuf_BUILD_TESTS OFF) +set(protobuf_INSTALL OFF) + +add_compile_options(-pthread) +add_subdirectory(../external/protobuf protobuf) + +#Generate messages +#add_subdirectory(messages/cpp) +set (MESSAGE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../external/messages/proto) +FILE(GLOB_RECURSE messages RELATIVE "${MESSAGE_PATH}" "${MESSAGE_PATH}/*.proto") +FILE(GLOB_RECURSE messages_sources RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${MESSAGE_PATH}/*.proto") +add_custom_target(proto_files SOURCES ${messages_sources}) + +#Message files output directory, will create directory +set (output_directory ${CMAKE_CURRENT_BINARY_DIR}) +file(MAKE_DIRECTORY ${output_directory}) + +set (generated_headers "") +set (generated_srcs "") + +# Include path +set(GOOGLE_PROTO_PATH ${PROJECT_SOURCE_DIR}/../external/protobuf/src) + +message(STATUS "GOOGLE_PROTO_PATH: ${GOOGLE_PROTO_PATH}") + +#Generate code using protoc compiler +foreach (file ${messages}) + + message(STATUS "Processing : ${file}") + + get_filename_component (name_without_extension ${file} NAME_WE) + + message(STATUS "name_without_extension : ${name_without_extension}") + + set(output_header_file ${output_directory}/${name_without_extension}.pb.h) + set(output_src_file ${output_directory}/${name_without_extension}.pb.cc) + + #Complete files to be compiled... + list(APPEND generated_headers ${output_header_file}) + list(APPEND generated_srcs ${output_src_file}) + + #Should execute protoc on each .proto files + #Ex: protoc -I=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/addressbook.proto + add_custom_command(OUTPUT ${output_header_file} ${output_src_file} + COMMAND ${protobuf_PROTOC_EXE} -I=${MESSAGE_PATH} -I${GOOGLE_PROTO_PATH} --cpp_out=${output_directory} ${file} + DEPENDS ${MESSAGE_PATH}/${file} ${protobuf_PROTOC_EXE} + WORKING_DIRECTORY ${MESSAGE_PATH} + VERBATIM) + +endforeach() + +message (STATUS "Headers : ${generated_headers}") +message (STATUS "Sources : ${generated_srcs}") + +include_directories( + ${PROJECT_SOURCE_DIR}/../external/protobuf/src + ${output_directory} +) + +#compile library +add_library(opentera_messages STATIC ${generated_headers} ${generated_srcs}) +#Link with protobuf +target_link_libraries(opentera_messages libprotobuf) +target_compile_definitions(opentera_messages PUBLIC OPENTERA_PROTOCOL_VERSION=1) +target_compile_options(opentera_messages PUBLIC -pthread) +get_target_property(all_includes opentera_messages INCLUDE_DIRECTORIES) + +message (STATUS "includes : ${all_includes}") + +set(OPENTERA_MESSAGES_INCLUDES ${all_includes} CACHE INTERNAL "doc string") +set(OPENTERA_MESSAGES_LIBS "opentera_messages" CACHE INTERNAL "doc string") + +add_definitions(-DOPENTERA_WEBASSEMBLY=1) +set(OPENTERA_WEBASSEMBLY ON) + +# Shared data +add_subdirectory(../shared shared) + +# Client +add_subdirectory(../client client) + +qt6_finalize_project()