feat: add task manager and activity panel
This commit is contained in:
parent
2a8ed10034
commit
1c391335c6
23 changed files with 4569 additions and 25 deletions
790
docs/tickets/closed/TICKET-035.md
Normal file
790
docs/tickets/closed/TICKET-035.md
Normal file
|
|
@ -0,0 +1,790 @@
|
|||
# Ticket #035 — File de tâches et panneau d’activité
|
||||
|
||||
## Contexte
|
||||
|
||||
Le ticket #034 a introduit `BackgroundTask`, une primitive asynchrone capable de :
|
||||
|
||||
- lancer un worker dans un thread GLib ;
|
||||
- suivre son état ;
|
||||
- signaler sa progression ;
|
||||
- gérer l’annulation ;
|
||||
- conserver un résultat ou une erreur ;
|
||||
- revenir sur le contexte principal à la fin ;
|
||||
- rester vivante grâce au comptage de références.
|
||||
|
||||
Cette primitive ne gère cependant pas encore plusieurs tâches ni leur présentation dans l’interface.
|
||||
|
||||
## Objectif
|
||||
|
||||
Créer un gestionnaire opaque :
|
||||
|
||||
```c
|
||||
TaskManager
|
||||
```
|
||||
|
||||
chargé de suivre les tâches actives et terminées, puis ajouter un panneau GTK permettant de les consulter.
|
||||
|
||||
Le ticket doit permettre de voir :
|
||||
|
||||
```text
|
||||
Titre
|
||||
État
|
||||
Progression
|
||||
Message courant
|
||||
Bouton Annuler
|
||||
```
|
||||
|
||||
Le gestionnaire reste indépendant de GTK.
|
||||
|
||||
---
|
||||
|
||||
# Architecture attendue
|
||||
|
||||
Créer :
|
||||
|
||||
```text
|
||||
include/core/task_manager.h
|
||||
src/core/task_manager.c
|
||||
tests/test_task_manager.c
|
||||
|
||||
include/widgets/task_panel.h
|
||||
src/widgets/task_panel.c
|
||||
```
|
||||
|
||||
Relations :
|
||||
|
||||
```text
|
||||
Application
|
||||
├── possède TaskManager
|
||||
└── transmet TaskManager à MainWindow
|
||||
|
||||
MainWindow
|
||||
└── possède TaskPanel
|
||||
|
||||
TaskPanel
|
||||
└── observe TaskManager
|
||||
|
||||
TaskManager
|
||||
└── conserve des références vers BackgroundTask
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Phase A — `TaskManager`
|
||||
|
||||
## 1. Type opaque
|
||||
|
||||
Dans :
|
||||
|
||||
```text
|
||||
include/core/task_manager.h
|
||||
```
|
||||
|
||||
déclarer :
|
||||
|
||||
```c
|
||||
typedef struct TaskManager TaskManager;
|
||||
```
|
||||
|
||||
Le type doit rester indépendant de GTK, SQLite et des enquêtes.
|
||||
|
||||
---
|
||||
|
||||
## 2. Callback de changement
|
||||
|
||||
Définir :
|
||||
|
||||
```c
|
||||
typedef void (*TaskManagerChangedCallback)(
|
||||
TaskManager *task_manager,
|
||||
gpointer user_data
|
||||
);
|
||||
```
|
||||
|
||||
Le callback signale qu’un changement visible a eu lieu :
|
||||
|
||||
- tâche ajoutée ;
|
||||
- progression modifiée ;
|
||||
- tâche terminée ;
|
||||
- tâche retirée ;
|
||||
- annulation demandée.
|
||||
|
||||
Le callback est une notification globale. Il ne transmet pas directement une tâche particulière.
|
||||
|
||||
---
|
||||
|
||||
## 3. API publique
|
||||
|
||||
### Construction
|
||||
|
||||
```c
|
||||
TaskManager *task_manager_new(void);
|
||||
|
||||
void task_manager_free(
|
||||
TaskManager *task_manager
|
||||
);
|
||||
```
|
||||
|
||||
### Ajouter une tâche
|
||||
|
||||
```c
|
||||
gboolean task_manager_add(
|
||||
TaskManager *task_manager,
|
||||
BackgroundTask *task,
|
||||
GError **error
|
||||
);
|
||||
```
|
||||
|
||||
Règles :
|
||||
|
||||
- `task_manager` devient propriétaire d’une référence supplémentaire ;
|
||||
- l’appelant conserve sa propre référence ;
|
||||
- une même tâche ne peut pas être ajoutée deux fois ;
|
||||
- une tâche déjà terminée peut être ajoutée, mais elle est immédiatement visible comme terminée ;
|
||||
- `NULL` est refusé.
|
||||
|
||||
### Consulter les tâches
|
||||
|
||||
```c
|
||||
gsize task_manager_get_count(
|
||||
const TaskManager *task_manager
|
||||
);
|
||||
|
||||
BackgroundTask *task_manager_get_task(
|
||||
const TaskManager *task_manager,
|
||||
gsize index
|
||||
);
|
||||
```
|
||||
|
||||
`task_manager_get_task()` retourne une nouvelle référence que l’appelant doit libérer.
|
||||
|
||||
### Retirer une tâche
|
||||
|
||||
```c
|
||||
gboolean task_manager_remove(
|
||||
TaskManager *task_manager,
|
||||
BackgroundTask *task
|
||||
);
|
||||
```
|
||||
|
||||
La tâche n’est pas annulée automatiquement.
|
||||
|
||||
### Supprimer les tâches terminées
|
||||
|
||||
```c
|
||||
gsize task_manager_remove_finished(
|
||||
TaskManager *task_manager
|
||||
);
|
||||
```
|
||||
|
||||
États concernés :
|
||||
|
||||
```text
|
||||
COMPLETED
|
||||
FAILED
|
||||
CANCELLED
|
||||
```
|
||||
|
||||
### Annuler toutes les tâches actives
|
||||
|
||||
```c
|
||||
void task_manager_cancel_all(
|
||||
TaskManager *task_manager
|
||||
);
|
||||
```
|
||||
|
||||
### Callback de changement
|
||||
|
||||
```c
|
||||
void task_manager_set_changed_callback(
|
||||
TaskManager *task_manager,
|
||||
TaskManagerChangedCallback callback,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_data_destroy
|
||||
);
|
||||
```
|
||||
|
||||
Le manager possède `user_data` après l’appel.
|
||||
|
||||
Remplacer le callback existant doit détruire les anciennes données exactement une fois.
|
||||
|
||||
---
|
||||
|
||||
## 4. Domaine d’erreur
|
||||
|
||||
Définir :
|
||||
|
||||
```c
|
||||
typedef enum
|
||||
{
|
||||
TASK_MANAGER_ERROR_INVALID_ARGUMENT,
|
||||
TASK_MANAGER_ERROR_ALREADY_ADDED
|
||||
} TaskManagerError;
|
||||
```
|
||||
|
||||
Puis :
|
||||
|
||||
```c
|
||||
#define TASK_MANAGER_ERROR \
|
||||
task_manager_error_quark()
|
||||
|
||||
GQuark task_manager_error_quark(void);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Structure interne recommandée
|
||||
|
||||
```c
|
||||
struct TaskManager
|
||||
{
|
||||
GMutex mutex;
|
||||
GPtrArray *tasks;
|
||||
|
||||
TaskManagerChangedCallback changed_callback;
|
||||
gpointer changed_user_data;
|
||||
GDestroyNotify changed_user_data_destroy;
|
||||
};
|
||||
```
|
||||
|
||||
`tasks` doit contenir des références `BackgroundTask *`.
|
||||
|
||||
Configurer le `GPtrArray` avec :
|
||||
|
||||
```c
|
||||
background_task_unref
|
||||
```
|
||||
|
||||
comme fonction de destruction.
|
||||
|
||||
---
|
||||
|
||||
## 6. Notification périodique
|
||||
|
||||
`BackgroundTask` ne possède pas encore de callback de progression.
|
||||
|
||||
Pour ce ticket, `TaskPanel` peut rafraîchir périodiquement l’affichage avec :
|
||||
|
||||
```c
|
||||
g_timeout_add()
|
||||
```
|
||||
|
||||
fréquence recommandée :
|
||||
|
||||
```text
|
||||
200 à 300 ms
|
||||
```
|
||||
|
||||
`TaskManager` notifie immédiatement les changements structurels.
|
||||
|
||||
La progression sera relue par le panneau.
|
||||
|
||||
Une API d’observation plus fine pourra être ajoutée plus tard si nécessaire.
|
||||
|
||||
---
|
||||
|
||||
# Phase B — Tests de `TaskManager`
|
||||
|
||||
Créer :
|
||||
|
||||
```text
|
||||
tests/test_task_manager.c
|
||||
```
|
||||
|
||||
## Tests minimaux
|
||||
|
||||
### Construction
|
||||
|
||||
Vérifier :
|
||||
|
||||
```text
|
||||
manager non NULL
|
||||
compteur initial à zéro
|
||||
```
|
||||
|
||||
### Ajout
|
||||
|
||||
Ajouter une tâche et vérifier :
|
||||
|
||||
```text
|
||||
compteur à un
|
||||
callback déclenché
|
||||
tâche récupérable
|
||||
référence indépendante
|
||||
```
|
||||
|
||||
### Doublon
|
||||
|
||||
Ajouter deux fois la même tâche :
|
||||
|
||||
```text
|
||||
FALSE
|
||||
TASK_MANAGER_ERROR_ALREADY_ADDED
|
||||
compteur inchangé
|
||||
```
|
||||
|
||||
### Retrait
|
||||
|
||||
Retirer une tâche :
|
||||
|
||||
```text
|
||||
TRUE
|
||||
compteur décrémenté
|
||||
callback déclenché
|
||||
```
|
||||
|
||||
### Retrait inconnu
|
||||
|
||||
Retirer une tâche absente :
|
||||
|
||||
```text
|
||||
FALSE
|
||||
aucun crash
|
||||
```
|
||||
|
||||
### Nettoyage des tâches terminées
|
||||
|
||||
Ajouter :
|
||||
|
||||
- une tâche en attente ;
|
||||
- une tâche terminée ;
|
||||
- une tâche échouée ;
|
||||
- une tâche annulée.
|
||||
|
||||
Vérifier que seules les tâches terminées sont supprimées.
|
||||
|
||||
### Annulation globale
|
||||
|
||||
Ajouter plusieurs tâches actives et appeler :
|
||||
|
||||
```c
|
||||
task_manager_cancel_all()
|
||||
```
|
||||
|
||||
Vérifier que chaque `GCancellable` reçoit une demande d’annulation.
|
||||
|
||||
### Durée de vie
|
||||
|
||||
Vérifier que :
|
||||
|
||||
- le manager conserve ses références ;
|
||||
- la destruction du manager libère toutes les tâches ;
|
||||
- le remplacement du callback détruit les anciennes données une seule fois.
|
||||
|
||||
---
|
||||
|
||||
# Phase C — `TaskPanel`
|
||||
|
||||
## 7. Type opaque
|
||||
|
||||
Dans :
|
||||
|
||||
```text
|
||||
include/widgets/task_panel.h
|
||||
```
|
||||
|
||||
déclarer :
|
||||
|
||||
```c
|
||||
typedef struct TaskPanel TaskPanel;
|
||||
```
|
||||
|
||||
API :
|
||||
|
||||
```c
|
||||
TaskPanel *task_panel_new(
|
||||
TaskManager *task_manager
|
||||
);
|
||||
|
||||
GtkWidget *task_panel_get_widget(
|
||||
const TaskPanel *task_panel
|
||||
);
|
||||
|
||||
void task_panel_refresh(
|
||||
TaskPanel *task_panel
|
||||
);
|
||||
|
||||
void task_panel_free(
|
||||
TaskPanel *task_panel
|
||||
);
|
||||
```
|
||||
|
||||
`TaskPanel` ne devient pas propriétaire de `TaskManager`.
|
||||
|
||||
`TaskPanel` doit rester valide tant que le manager existe.
|
||||
|
||||
---
|
||||
|
||||
## 8. Interface recommandée
|
||||
|
||||
Premier rendu simple :
|
||||
|
||||
```text
|
||||
┌──────────────────────────────────────────────────────────┐
|
||||
│ Activité [ Nettoyer ] │
|
||||
├──────────────────────────────────────────────────────────┤
|
||||
│ Extraction des métadonnées │
|
||||
│ En cours — 45 % │
|
||||
│ [████████░░░░░░░░░░] [ Annuler ] │
|
||||
├──────────────────────────────────────────────────────────┤
|
||||
│ Calcul SHA-256 │
|
||||
│ Terminé │
|
||||
│ [████████████████████] │
|
||||
└──────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
Widgets GTK possibles :
|
||||
|
||||
- `GtkBox` ;
|
||||
- `GtkLabel` ;
|
||||
- `GtkProgressBar` ;
|
||||
- `GtkButton` ;
|
||||
- `GtkScrolledWindow`.
|
||||
|
||||
Ne pas utiliser encore de `GtkListView` si cela complexifie inutilement le ticket.
|
||||
|
||||
---
|
||||
|
||||
## 9. État visuel
|
||||
|
||||
Créer une fonction privée traduisant les états :
|
||||
|
||||
```text
|
||||
PENDING → En attente
|
||||
RUNNING → En cours
|
||||
COMPLETED → Terminée
|
||||
FAILED → Échouée
|
||||
CANCELLED → Annulée
|
||||
```
|
||||
|
||||
Une tâche en erreur doit afficher son message d’erreur sous forme courte.
|
||||
|
||||
Une tâche en cours doit afficher son message de progression lorsqu’il existe.
|
||||
|
||||
---
|
||||
|
||||
## 10. Bouton d’annulation
|
||||
|
||||
Le bouton `Annuler` doit être visible uniquement pour :
|
||||
|
||||
```text
|
||||
RUNNING
|
||||
```
|
||||
|
||||
Son callback appelle :
|
||||
|
||||
```c
|
||||
background_task_cancel(task);
|
||||
```
|
||||
|
||||
Le bouton ne doit pas retirer la tâche.
|
||||
|
||||
---
|
||||
|
||||
## 11. Bouton de nettoyage
|
||||
|
||||
Ajouter :
|
||||
|
||||
```text
|
||||
Nettoyer
|
||||
```
|
||||
|
||||
Il appelle :
|
||||
|
||||
```c
|
||||
task_manager_remove_finished()
|
||||
```
|
||||
|
||||
Les tâches actives restent visibles.
|
||||
|
||||
---
|
||||
|
||||
## 12. Rafraîchissement périodique
|
||||
|
||||
`TaskPanel` doit enregistrer une source GLib :
|
||||
|
||||
```c
|
||||
g_timeout_add()
|
||||
```
|
||||
|
||||
Elle appelle :
|
||||
|
||||
```c
|
||||
task_panel_refresh()
|
||||
```
|
||||
|
||||
Lors de `task_panel_free()` :
|
||||
|
||||
- retirer la source avec `g_source_remove()` ;
|
||||
- empêcher tout callback après destruction ;
|
||||
- ne pas détruire `TaskManager`.
|
||||
|
||||
---
|
||||
|
||||
# Phase D — Intégration GTK
|
||||
|
||||
## 13. Ajouter le panneau à `MainWindow`
|
||||
|
||||
Modifier :
|
||||
|
||||
```text
|
||||
include/views/main_window.h
|
||||
src/views/main_window.c
|
||||
```
|
||||
|
||||
Changer la construction :
|
||||
|
||||
```c
|
||||
MainWindow *main_window_new(
|
||||
GtkApplication *application,
|
||||
TaskManager *task_manager
|
||||
);
|
||||
```
|
||||
|
||||
`MainWindow` doit créer :
|
||||
|
||||
```c
|
||||
TaskPanel *task_panel;
|
||||
```
|
||||
|
||||
Le panneau peut être placé :
|
||||
|
||||
- sous la zone de travail ;
|
||||
- dans un volet inférieur ;
|
||||
- ou temporairement dans une colonne latérale secondaire.
|
||||
|
||||
Pour ce ticket, un volet inférieur sous `GtkPaned` est acceptable.
|
||||
|
||||
---
|
||||
|
||||
## 14. Ajouter `TaskManager` à `Application`
|
||||
|
||||
Dans la structure privée :
|
||||
|
||||
```c
|
||||
TaskManager *task_manager;
|
||||
```
|
||||
|
||||
Dans `application_new()` :
|
||||
|
||||
```c
|
||||
application->task_manager =
|
||||
task_manager_new();
|
||||
```
|
||||
|
||||
En cas d’échec, nettoyer l’application.
|
||||
|
||||
Dans `application_on_activate()` :
|
||||
|
||||
```c
|
||||
application->main_window = main_window_new(
|
||||
gtk_application,
|
||||
application->task_manager
|
||||
);
|
||||
```
|
||||
|
||||
Dans `application_free()` :
|
||||
|
||||
1. fermer/détruire `MainWindow` ;
|
||||
2. libérer `TaskManager` après le panneau ;
|
||||
3. poursuivre le nettoyage existant.
|
||||
|
||||
L’ordre doit éviter que `TaskPanel` lise un manager déjà détruit.
|
||||
|
||||
---
|
||||
|
||||
# Phase E — Tâche de démonstration
|
||||
|
||||
## 15. Ajouter temporairement une tâche de test
|
||||
|
||||
Pour valider l’interface, ajouter un bouton temporaire :
|
||||
|
||||
```text
|
||||
Tâche de test
|
||||
```
|
||||
|
||||
Il lance une `BackgroundTask` qui :
|
||||
|
||||
- dure environ deux secondes ;
|
||||
- progresse de 0 à 100 % ;
|
||||
- accepte l’annulation ;
|
||||
- retourne un résultat simple.
|
||||
|
||||
Cette tâche doit être ajoutée au `TaskManager`.
|
||||
|
||||
Le bouton pourra être supprimé lorsque le premier véritable traitement asynchrone sera disponible.
|
||||
|
||||
Le code de démonstration doit rester clairement identifié :
|
||||
|
||||
```c
|
||||
/* Temporary demonstration task for ticket #035. */
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Hors périmètre
|
||||
|
||||
Ne pas ajouter encore :
|
||||
|
||||
- de limite de concurrence ;
|
||||
- de priorité ;
|
||||
- de persistance SQLite ;
|
||||
- de reprise après redémarrage ;
|
||||
- d’historique permanent ;
|
||||
- d’exécution de commandes ;
|
||||
- de recherche DNS ;
|
||||
- d’ExifTool ;
|
||||
- de notifications système ;
|
||||
- de tri avancé ;
|
||||
- de pagination.
|
||||
|
||||
---
|
||||
|
||||
# Critères d’acceptation
|
||||
|
||||
- [ ] `TaskManager` est opaque.
|
||||
- [ ] `TaskManager` ne dépend pas de GTK.
|
||||
- [ ] `TaskManager` protège sa collection avec `GMutex`.
|
||||
- [ ] Le manager conserve une référence par tâche.
|
||||
- [ ] Une tâche ne peut pas être ajoutée deux fois.
|
||||
- [ ] Une tâche peut être retirée.
|
||||
- [ ] Les tâches terminées peuvent être nettoyées.
|
||||
- [ ] Toutes les tâches actives peuvent être annulées.
|
||||
- [ ] Le callback de changement fonctionne.
|
||||
- [ ] `TaskPanel` affiche toutes les tâches.
|
||||
- [ ] La progression est visible.
|
||||
- [ ] L’état est lisible.
|
||||
- [ ] Une tâche active peut être annulée.
|
||||
- [ ] Les tâches terminées peuvent être supprimées.
|
||||
- [ ] Le rafraîchissement périodique est correctement détruit.
|
||||
- [ ] `MainWindow` ne devient pas propriétaire du manager.
|
||||
- [ ] L’ordre de destruction ne provoque aucun crash.
|
||||
- [ ] La tâche de démonstration fonctionne.
|
||||
- [ ] `make` réussit.
|
||||
- [ ] `make test` réussit.
|
||||
- [ ] `git diff --check` ne retourne aucune erreur.
|
||||
|
||||
---
|
||||
|
||||
# Audit attendu
|
||||
|
||||
Vérifier l’indépendance du manager :
|
||||
|
||||
```bash
|
||||
rg -n \
|
||||
'#include <gtk|sqlite3_|Database|Investigation' \
|
||||
include/core/task_manager.h \
|
||||
src/core/task_manager.c
|
||||
```
|
||||
|
||||
Résultat attendu :
|
||||
|
||||
```text
|
||||
aucune sortie
|
||||
```
|
||||
|
||||
Vérifier les références :
|
||||
|
||||
```bash
|
||||
rg -n \
|
||||
'background_task_ref|background_task_unref' \
|
||||
src/core/task_manager.c
|
||||
```
|
||||
|
||||
Vérifier le rafraîchissement GTK :
|
||||
|
||||
```bash
|
||||
rg -n \
|
||||
'g_timeout_add|g_source_remove|task_panel_refresh' \
|
||||
src/widgets/task_panel.c
|
||||
```
|
||||
|
||||
Vérifier l’absence de threads bruts :
|
||||
|
||||
```bash
|
||||
rg -n \
|
||||
'pthread_|pthread.h' \
|
||||
include/core/task_manager.h \
|
||||
src/core/task_manager.c \
|
||||
src/widgets/task_panel.c
|
||||
```
|
||||
|
||||
Résultat attendu :
|
||||
|
||||
```text
|
||||
aucune sortie
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Fichiers concernés
|
||||
|
||||
```text
|
||||
include/core/task_manager.h
|
||||
src/core/task_manager.c
|
||||
tests/test_task_manager.c
|
||||
|
||||
include/widgets/task_panel.h
|
||||
src/widgets/task_panel.c
|
||||
|
||||
include/views/main_window.h
|
||||
src/views/main_window.c
|
||||
|
||||
src/core/application.c
|
||||
Makefile
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Commit attendu
|
||||
|
||||
```bash
|
||||
make clean
|
||||
make
|
||||
make test
|
||||
git diff --check
|
||||
git status --short
|
||||
```
|
||||
|
||||
```bash
|
||||
git add \
|
||||
include/core/task_manager.h \
|
||||
src/core/task_manager.c \
|
||||
tests/test_task_manager.c \
|
||||
include/widgets/task_panel.h \
|
||||
src/widgets/task_panel.c \
|
||||
include/views/main_window.h \
|
||||
src/views/main_window.c \
|
||||
src/core/application.c \
|
||||
Makefile
|
||||
```
|
||||
|
||||
```bash
|
||||
git diff --cached --stat
|
||||
git diff --cached
|
||||
```
|
||||
|
||||
```bash
|
||||
git commit -m "feat(ui): add task manager and activity panel"
|
||||
git push
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Résultat attendu
|
||||
|
||||
Après ce ticket, Labfy possédera une infrastructure visible pour tous les futurs traitements longs :
|
||||
|
||||
```text
|
||||
Utilisateur
|
||||
↓
|
||||
Action GTK
|
||||
↓
|
||||
BackgroundTask
|
||||
↓
|
||||
TaskManager
|
||||
↓
|
||||
TaskPanel
|
||||
```
|
||||
|
||||
Le ticket suivant pourra ajouter le registre des dépendances et lancer les premiers contrôles d’outils externes sans bloquer l’interface.
|
||||
198
include/core/task_manager.h
Normal file
198
include/core/task_manager.h
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
/******************************************************************************
|
||||
* @file task_manager.h
|
||||
* @brief Gestionnaire central des tâches exécutées en arrière-plan.
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef LABFY_INVESTIGATION_TASK_MANAGER_H
|
||||
#define LABFY_INVESTIGATION_TASK_MANAGER_H
|
||||
|
||||
#include "core/background_task.h"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
/**
|
||||
* @brief Représentation opaque du gestionnaire de tâches.
|
||||
*/
|
||||
typedef struct TaskManager TaskManager;
|
||||
|
||||
/**
|
||||
* @brief Codes d’erreur du gestionnaire de tâches.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
TASK_MANAGER_ERROR_INVALID_ARGUMENT,
|
||||
TASK_MANAGER_ERROR_ALREADY_ADDED
|
||||
} TaskManagerError;
|
||||
|
||||
/**
|
||||
* @brief Domaine d’erreur du gestionnaire de tâches.
|
||||
*/
|
||||
#define TASK_MANAGER_ERROR \
|
||||
task_manager_error_quark()
|
||||
|
||||
/**
|
||||
* @brief Callback appelé lorsque la collection de tâches change.
|
||||
*
|
||||
* Ce callback est utilisé lorsqu’une tâche est ajoutée, retirée,
|
||||
* nettoyée ou lorsqu’une demande globale d’annulation est effectuée.
|
||||
*
|
||||
* Il est appelé sans que le mutex interne du gestionnaire soit verrouillé.
|
||||
*
|
||||
* @param task_manager Gestionnaire ayant changé.
|
||||
* @param user_data Données associées au callback.
|
||||
*/
|
||||
typedef void (*TaskManagerChangedCallback)(
|
||||
TaskManager *task_manager,
|
||||
gpointer user_data
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Retourne le domaine d’erreur du module.
|
||||
*
|
||||
* @return Quark GLib du domaine d’erreur.
|
||||
*/
|
||||
GQuark task_manager_error_quark(void);
|
||||
|
||||
/**
|
||||
* @brief Crée un gestionnaire de tâches vide.
|
||||
*
|
||||
* @return Nouveau gestionnaire, ou NULL en cas d’échec.
|
||||
*/
|
||||
TaskManager *task_manager_new(void);
|
||||
|
||||
/**
|
||||
* @brief Libère le gestionnaire et ses références sur les tâches.
|
||||
*
|
||||
* Cette fonction accepte task_manager == NULL.
|
||||
*
|
||||
* Les tâches continuent d’exister si d’autres composants possèdent
|
||||
* encore leurs propres références.
|
||||
*
|
||||
* @param task_manager Gestionnaire à libérer.
|
||||
*/
|
||||
void task_manager_free(
|
||||
TaskManager *task_manager
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Ajoute une tâche au gestionnaire.
|
||||
*
|
||||
* Le gestionnaire prend une référence supplémentaire sur la tâche.
|
||||
* L’appelant conserve la propriété de sa propre référence.
|
||||
*
|
||||
* Une même instance de BackgroundTask ne peut pas être ajoutée deux fois.
|
||||
*
|
||||
* @param task_manager Gestionnaire concerné.
|
||||
* @param task Tâche à ajouter.
|
||||
* @param error Emplacement facultatif recevant une erreur.
|
||||
*
|
||||
* @return TRUE si la tâche est ajoutée, sinon FALSE.
|
||||
*/
|
||||
gboolean task_manager_add(
|
||||
TaskManager *task_manager,
|
||||
BackgroundTask *task,
|
||||
GError **error
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Retourne le nombre de tâches actuellement suivies.
|
||||
*
|
||||
* @param task_manager Gestionnaire concerné.
|
||||
*
|
||||
* @return Nombre de tâches.
|
||||
*/
|
||||
gsize task_manager_get_count(
|
||||
const TaskManager *task_manager
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Retourne une référence vers une tâche selon son index.
|
||||
*
|
||||
* L’appelant doit libérer la référence retournée avec :
|
||||
*
|
||||
* @code
|
||||
* background_task_unref(task);
|
||||
* @endcode
|
||||
*
|
||||
* @param task_manager Gestionnaire concerné.
|
||||
* @param index Index de la tâche.
|
||||
*
|
||||
* @return Nouvelle référence vers la tâche, ou NULL si l’index est invalide.
|
||||
*/
|
||||
BackgroundTask *task_manager_get_task(
|
||||
const TaskManager *task_manager,
|
||||
gsize index
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Retire une tâche du gestionnaire.
|
||||
*
|
||||
* La tâche n’est pas annulée automatiquement.
|
||||
*
|
||||
* La référence détenue par le gestionnaire est libérée.
|
||||
*
|
||||
* @param task_manager Gestionnaire concerné.
|
||||
* @param task Tâche à retirer.
|
||||
*
|
||||
* @return TRUE si la tâche était présente et a été retirée, sinon FALSE.
|
||||
*/
|
||||
gboolean task_manager_remove(
|
||||
TaskManager *task_manager,
|
||||
BackgroundTask *task
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Retire toutes les tâches terminées.
|
||||
*
|
||||
* États concernés :
|
||||
*
|
||||
* - BACKGROUND_TASK_STATE_COMPLETED ;
|
||||
* - BACKGROUND_TASK_STATE_FAILED ;
|
||||
* - BACKGROUND_TASK_STATE_CANCELLED.
|
||||
*
|
||||
* Les tâches en attente ou en cours sont conservées.
|
||||
*
|
||||
* @param task_manager Gestionnaire concerné.
|
||||
*
|
||||
* @return Nombre de tâches retirées.
|
||||
*/
|
||||
gsize task_manager_remove_finished(
|
||||
TaskManager *task_manager
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Demande l’annulation de toutes les tâches en cours.
|
||||
*
|
||||
* L’annulation reste coopérative.
|
||||
*
|
||||
* Les tâches ne sont pas retirées du gestionnaire.
|
||||
*
|
||||
* @param task_manager Gestionnaire concerné.
|
||||
*/
|
||||
void task_manager_cancel_all(
|
||||
TaskManager *task_manager
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Définit le callback signalant un changement du gestionnaire.
|
||||
*
|
||||
* Le gestionnaire devient propriétaire de user_data après cet appel.
|
||||
*
|
||||
* Lorsqu’un nouveau callback remplace l’ancien, les anciennes données
|
||||
* sont détruites avec leur fonction de destruction.
|
||||
*
|
||||
* Le callback peut être désactivé en fournissant callback == NULL.
|
||||
*
|
||||
* @param task_manager Gestionnaire concerné.
|
||||
* @param callback Nouveau callback, ou NULL.
|
||||
* @param user_data Données transmises au callback.
|
||||
* @param user_data_destroy Fonction de destruction associée.
|
||||
*/
|
||||
void task_manager_set_changed_callback(
|
||||
TaskManager *task_manager,
|
||||
TaskManagerChangedCallback callback,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_data_destroy
|
||||
);
|
||||
|
||||
#endif
|
||||
|
|
@ -9,6 +9,7 @@
|
|||
#include "core/investigation_tree_model.h"
|
||||
#include "widgets/investigation_tree_view.h"
|
||||
#include "core/investigation_node.h"
|
||||
#include "core/task_manager.h"
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
|
|
@ -45,11 +46,17 @@ void main_window_set_new_investigation_callback(
|
|||
/**
|
||||
* @brief Crée une nouvelle fenêtre principale.
|
||||
*
|
||||
* @param application Application GTK.
|
||||
* MainWindow ne devient pas propriétaire de task_manager.
|
||||
*
|
||||
* @return Une nouvelle fenêtre ou NULL en cas d'échec.
|
||||
* @param application Application GTK.
|
||||
* @param task_manager Gestionnaire de tâches de l'application.
|
||||
*
|
||||
* @return Nouvelle fenêtre, ou NULL en cas d'échec.
|
||||
*/
|
||||
MainWindow *main_window_new(GtkApplication *application);
|
||||
MainWindow *main_window_new(
|
||||
GtkApplication *application,
|
||||
TaskManager *task_manager
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Affiche la fenêtre principale.
|
||||
|
|
@ -156,6 +163,16 @@ typedef void (*MainWindowQuitCallback)(
|
|||
gpointer user_data
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Callback appelé lorsque l'utilisateur demande une tâche
|
||||
* de démonstration.
|
||||
*
|
||||
* @param user_data Données utilisateur associées au callback.
|
||||
*/
|
||||
typedef void (*MainWindowDemoTaskCallback)(
|
||||
gpointer user_data
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Définit le callback du bouton « Ouvrir une enquête ».
|
||||
*
|
||||
|
|
@ -186,6 +203,21 @@ void main_window_set_quit_callback(
|
|||
gpointer user_data
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Définit le callback du bouton « Tâche de test ».
|
||||
*
|
||||
* Ce bouton est temporaire et sert à valider le panneau d'activité.
|
||||
*
|
||||
* @param main_window Fenêtre principale.
|
||||
* @param callback Fonction appelée lors du clic.
|
||||
* @param user_data Données transmises au callback.
|
||||
*/
|
||||
void main_window_set_demo_task_callback(
|
||||
MainWindow *main_window,
|
||||
MainWindowDemoTaskCallback callback,
|
||||
gpointer user_data
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Libère les ressources de la fenêtre.
|
||||
*
|
||||
|
|
|
|||
66
include/widgets/task_panel.h
Normal file
66
include/widgets/task_panel.h
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/******************************************************************************
|
||||
* @file task_panel.h
|
||||
* @brief Panneau GTK affichant les tâches suivies par TaskManager.
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef LABFY_INVESTIGATION_TASK_PANEL_H
|
||||
#define LABFY_INVESTIGATION_TASK_PANEL_H
|
||||
|
||||
#include "core/task_manager.h"
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
/**
|
||||
* @brief Représentation opaque du panneau d’activité.
|
||||
*/
|
||||
typedef struct TaskPanel TaskPanel;
|
||||
|
||||
/**
|
||||
* @brief Crée un panneau affichant les tâches d’un gestionnaire.
|
||||
*
|
||||
* Le panneau ne devient pas propriétaire de task_manager.
|
||||
* Le gestionnaire doit rester valide pendant toute la durée de vie
|
||||
* du panneau.
|
||||
*
|
||||
* @param task_manager Gestionnaire de tâches observé.
|
||||
*
|
||||
* @return Nouveau panneau, ou NULL si le gestionnaire est invalide.
|
||||
*/
|
||||
TaskPanel *task_panel_new(
|
||||
TaskManager *task_manager
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Retourne le widget racine du panneau.
|
||||
*
|
||||
* Le pointeur retourné est emprunté.
|
||||
*
|
||||
* @param task_panel Panneau concerné.
|
||||
*
|
||||
* @return Widget racine, ou NULL.
|
||||
*/
|
||||
GtkWidget *task_panel_get_widget(
|
||||
const TaskPanel *task_panel
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Reconstruit l’affichage à partir du gestionnaire.
|
||||
*
|
||||
* @param task_panel Panneau à actualiser.
|
||||
*/
|
||||
void task_panel_refresh(
|
||||
TaskPanel *task_panel
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Libère le panneau et arrête son rafraîchissement périodique.
|
||||
*
|
||||
* Le gestionnaire associé n’est pas libéré.
|
||||
*
|
||||
* @param task_panel Panneau à libérer.
|
||||
*/
|
||||
void task_panel_free(
|
||||
TaskPanel *task_panel
|
||||
);
|
||||
|
||||
#endif
|
||||
Binary file not shown.
|
|
@ -15,6 +15,8 @@
|
|||
#include "views/folder_dialog.h"
|
||||
#include "views/main_window.h"
|
||||
#include "views/application_message_dialog.h"
|
||||
#include "core/task_manager.h"
|
||||
#include "core/background_task.h"
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
|
|
@ -64,6 +66,7 @@ struct Application
|
|||
MainWindow *main_window;
|
||||
InvestigationSession *session;
|
||||
InvestigationTreeModel *tree_model;
|
||||
TaskManager *task_manager;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -602,6 +605,269 @@ static void application_on_open_investigation_requested(
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Exécute une tâche temporaire servant à tester le panneau.
|
||||
*
|
||||
* Cette fonction s'exécute dans un thread secondaire.
|
||||
*
|
||||
* @param task Tâche en cours.
|
||||
* @param cancellable Objet d'annulation.
|
||||
* @param worker_data Données inutilisées.
|
||||
* @param result Emplacement recevant le résultat.
|
||||
* @param error Emplacement recevant une erreur.
|
||||
*
|
||||
* @return TRUE en cas de succès, sinon FALSE.
|
||||
*/
|
||||
static gboolean application_demo_task_worker(
|
||||
BackgroundTask *task,
|
||||
GCancellable *cancellable,
|
||||
gpointer worker_data,
|
||||
gpointer *result,
|
||||
GError **error
|
||||
)
|
||||
{
|
||||
guint step = 0;
|
||||
char status_message[64];
|
||||
|
||||
(void) worker_data;
|
||||
|
||||
if (task == NULL ||
|
||||
cancellable == NULL ||
|
||||
result == NULL ||
|
||||
error == NULL)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
for (step = 0; step <= 20; step++)
|
||||
{
|
||||
if (g_cancellable_set_error_if_cancelled(
|
||||
cancellable,
|
||||
error
|
||||
))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_snprintf(
|
||||
status_message,
|
||||
sizeof(status_message),
|
||||
"Étape %u sur 20",
|
||||
step
|
||||
);
|
||||
|
||||
background_task_report_progress(
|
||||
task,
|
||||
(double) step / 20.0,
|
||||
status_message
|
||||
);
|
||||
|
||||
if (step < 20)
|
||||
{
|
||||
g_usleep(
|
||||
500000
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
*result = g_strdup(
|
||||
"Tâche de démonstration terminée."
|
||||
);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Journalise la fin de la tâche de démonstration.
|
||||
*
|
||||
* @param task Tâche terminée.
|
||||
* @param user_data Données inutilisées.
|
||||
*/
|
||||
static void application_demo_task_completed(
|
||||
BackgroundTask *task,
|
||||
gpointer user_data
|
||||
)
|
||||
{
|
||||
BackgroundTaskState state;
|
||||
const char *result = NULL;
|
||||
GError *error = NULL;
|
||||
|
||||
(void) user_data;
|
||||
|
||||
if (task == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
state = background_task_get_state(
|
||||
task
|
||||
);
|
||||
|
||||
if (state == BACKGROUND_TASK_STATE_COMPLETED)
|
||||
{
|
||||
result = background_task_get_result(
|
||||
task
|
||||
);
|
||||
|
||||
g_print(
|
||||
"%s\n",
|
||||
result != NULL
|
||||
? result
|
||||
: "Tâche terminée."
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (state == BACKGROUND_TASK_STATE_CANCELLED)
|
||||
{
|
||||
g_print(
|
||||
"Tâche de démonstration annulée.\n"
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
error = background_task_dup_error(
|
||||
task
|
||||
);
|
||||
|
||||
g_warning(
|
||||
"La tâche de démonstration a échoué : %s",
|
||||
error != NULL
|
||||
? error->message
|
||||
: "erreur inconnue"
|
||||
);
|
||||
|
||||
g_clear_error(
|
||||
&error
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Crée et lance une tâche de démonstration.
|
||||
*
|
||||
* @param user_data Pointeur vers Application.
|
||||
*/
|
||||
static void application_on_demo_task_requested(
|
||||
gpointer user_data
|
||||
)
|
||||
{
|
||||
Application *application = user_data;
|
||||
BackgroundTask *task = NULL;
|
||||
GError *error = NULL;
|
||||
|
||||
if (application == NULL ||
|
||||
application->task_manager == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
task = background_task_new(
|
||||
"Tâche de démonstration"
|
||||
);
|
||||
|
||||
if (task == NULL)
|
||||
{
|
||||
application_present_error(
|
||||
application,
|
||||
"Tâche impossible",
|
||||
"La tâche de démonstration n'a pas pu être créée."
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Le manager prend une référence supplémentaire.
|
||||
*/
|
||||
if (!task_manager_add(
|
||||
application->task_manager,
|
||||
task,
|
||||
&error
|
||||
))
|
||||
{
|
||||
g_warning(
|
||||
"Impossible d'ajouter la tâche : %s",
|
||||
error != NULL
|
||||
? error->message
|
||||
: "erreur inconnue"
|
||||
);
|
||||
|
||||
application_present_error(
|
||||
application,
|
||||
"Tâche impossible",
|
||||
error != NULL
|
||||
? error->message
|
||||
: "La tâche n'a pas pu être ajoutée."
|
||||
);
|
||||
|
||||
g_clear_error(
|
||||
&error
|
||||
);
|
||||
|
||||
background_task_unref(
|
||||
task
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!background_task_start(
|
||||
task,
|
||||
application_demo_task_worker,
|
||||
NULL,
|
||||
NULL,
|
||||
g_free,
|
||||
application_demo_task_completed,
|
||||
NULL,
|
||||
NULL,
|
||||
&error
|
||||
))
|
||||
{
|
||||
g_warning(
|
||||
"Impossible de démarrer la tâche : %s",
|
||||
error != NULL
|
||||
? error->message
|
||||
: "erreur inconnue"
|
||||
);
|
||||
|
||||
application_present_error(
|
||||
application,
|
||||
"Démarrage impossible",
|
||||
error != NULL
|
||||
? error->message
|
||||
: "La tâche n'a pas pu être démarrée."
|
||||
);
|
||||
|
||||
g_clear_error(
|
||||
&error
|
||||
);
|
||||
|
||||
task_manager_remove(
|
||||
application->task_manager,
|
||||
task
|
||||
);
|
||||
|
||||
background_task_unref(
|
||||
task
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* La référence locale n'est plus nécessaire :
|
||||
*
|
||||
* - TaskManager conserve une référence ;
|
||||
* - BackgroundTask conserve une référence interne pendant
|
||||
* l'exécution.
|
||||
*/
|
||||
background_task_unref(
|
||||
task
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Traite la sélection d'un nœud dans l'arborescence.
|
||||
*
|
||||
|
|
@ -675,6 +941,10 @@ static void application_on_quit_requested(
|
|||
return;
|
||||
}
|
||||
|
||||
task_manager_cancel_all(
|
||||
application->task_manager
|
||||
);
|
||||
|
||||
g_application_quit(
|
||||
G_APPLICATION(
|
||||
application->gtk_application
|
||||
|
|
@ -713,7 +983,8 @@ static void application_on_activate(
|
|||
}
|
||||
|
||||
application->main_window = main_window_new(
|
||||
gtk_application
|
||||
gtk_application,
|
||||
application->task_manager
|
||||
);
|
||||
|
||||
if (application->main_window == NULL)
|
||||
|
|
@ -743,6 +1014,12 @@ static void application_on_activate(
|
|||
application
|
||||
);
|
||||
|
||||
main_window_set_demo_task_callback(
|
||||
application->main_window,
|
||||
application_on_demo_task_requested,
|
||||
application
|
||||
);
|
||||
|
||||
main_window_set_quit_callback(
|
||||
application->main_window,
|
||||
application_on_quit_requested,
|
||||
|
|
@ -763,6 +1040,18 @@ Application *application_new(void)
|
|||
1
|
||||
);
|
||||
|
||||
application->task_manager =
|
||||
task_manager_new();
|
||||
|
||||
if (application->task_manager == NULL)
|
||||
{
|
||||
g_free(
|
||||
application
|
||||
);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
application->gtk_application = gtk_application_new(
|
||||
APPLICATION_ID,
|
||||
G_APPLICATION_DEFAULT_FLAGS
|
||||
|
|
@ -770,7 +1059,14 @@ Application *application_new(void)
|
|||
|
||||
if (application->gtk_application == NULL)
|
||||
{
|
||||
g_free(application);
|
||||
task_manager_free(
|
||||
application->task_manager
|
||||
);
|
||||
|
||||
g_free(
|
||||
application
|
||||
);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -834,10 +1130,18 @@ void application_free(
|
|||
application->session
|
||||
);
|
||||
|
||||
/*
|
||||
* MainWindow contient TaskPanel.
|
||||
* TaskPanel doit être détruit avant TaskManager.
|
||||
*/
|
||||
main_window_free(
|
||||
application->main_window
|
||||
);
|
||||
|
||||
task_manager_free(
|
||||
application->task_manager
|
||||
);
|
||||
|
||||
if (application->gtk_application != NULL)
|
||||
{
|
||||
g_object_unref(
|
||||
|
|
@ -845,5 +1149,7 @@ void application_free(
|
|||
);
|
||||
}
|
||||
|
||||
g_free(application);
|
||||
g_free(
|
||||
application
|
||||
);
|
||||
}
|
||||
|
|
|
|||
736
src/core/task_manager.c
Normal file
736
src/core/task_manager.c
Normal file
|
|
@ -0,0 +1,736 @@
|
|||
/******************************************************************************
|
||||
* @file task_manager.c
|
||||
* @brief Implémentation du gestionnaire central des tâches.
|
||||
******************************************************************************/
|
||||
|
||||
#include "core/task_manager.h"
|
||||
|
||||
/**
|
||||
* @struct TaskManagerCallbackSlot
|
||||
* @brief Stockage référencé du callback de changement.
|
||||
*
|
||||
* Le comptage de références permet d'appeler le callback hors du mutex
|
||||
* sans risquer que ses données soient détruites pendant l'appel.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
gatomicrefcount reference_count;
|
||||
|
||||
TaskManagerChangedCallback callback;
|
||||
gpointer user_data;
|
||||
GDestroyNotify user_data_destroy;
|
||||
} TaskManagerCallbackSlot;
|
||||
|
||||
/**
|
||||
* @struct TaskManager
|
||||
* @brief État interne du gestionnaire de tâches.
|
||||
*/
|
||||
struct TaskManager
|
||||
{
|
||||
GMutex mutex;
|
||||
GPtrArray *tasks;
|
||||
|
||||
TaskManagerCallbackSlot *changed_slot;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Adapte background_task_unref() au type GDestroyNotify.
|
||||
*
|
||||
* @param user_data Pointeur vers BackgroundTask.
|
||||
*/
|
||||
static void task_manager_task_unref(
|
||||
gpointer user_data
|
||||
)
|
||||
{
|
||||
background_task_unref(
|
||||
user_data
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Crée un emplacement référencé pour un callback.
|
||||
*
|
||||
* @param callback Callback à conserver.
|
||||
* @param user_data Données du callback.
|
||||
* @param user_data_destroy Destructeur des données.
|
||||
*
|
||||
* @return Nouvel emplacement, ou NULL si aucune donnée n'est fournie.
|
||||
*/
|
||||
static TaskManagerCallbackSlot *
|
||||
task_manager_callback_slot_new(
|
||||
TaskManagerChangedCallback callback,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_data_destroy
|
||||
)
|
||||
{
|
||||
TaskManagerCallbackSlot *slot = NULL;
|
||||
|
||||
if (callback == NULL &&
|
||||
user_data == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
slot = g_new0(
|
||||
TaskManagerCallbackSlot,
|
||||
1
|
||||
);
|
||||
|
||||
g_atomic_ref_count_init(
|
||||
&slot->reference_count
|
||||
);
|
||||
|
||||
slot->callback = callback;
|
||||
slot->user_data = user_data;
|
||||
slot->user_data_destroy =
|
||||
user_data_destroy;
|
||||
|
||||
return slot;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Ajoute une référence à un emplacement de callback.
|
||||
*
|
||||
* @param slot Emplacement concerné.
|
||||
*
|
||||
* @return L'emplacement fourni, ou NULL.
|
||||
*/
|
||||
static TaskManagerCallbackSlot *
|
||||
task_manager_callback_slot_ref(
|
||||
TaskManagerCallbackSlot *slot
|
||||
)
|
||||
{
|
||||
if (slot == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g_atomic_ref_count_inc(
|
||||
&slot->reference_count
|
||||
);
|
||||
|
||||
return slot;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Libère une référence à un emplacement de callback.
|
||||
*
|
||||
* @param slot Emplacement concerné.
|
||||
*/
|
||||
static void task_manager_callback_slot_unref(
|
||||
TaskManagerCallbackSlot *slot
|
||||
)
|
||||
{
|
||||
gpointer user_data = NULL;
|
||||
GDestroyNotify user_data_destroy = NULL;
|
||||
|
||||
if (slot == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_atomic_ref_count_dec(
|
||||
&slot->reference_count
|
||||
))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
user_data = slot->user_data;
|
||||
user_data_destroy =
|
||||
slot->user_data_destroy;
|
||||
|
||||
slot->callback = NULL;
|
||||
slot->user_data = NULL;
|
||||
slot->user_data_destroy = NULL;
|
||||
|
||||
if (user_data != NULL &&
|
||||
user_data_destroy != NULL)
|
||||
{
|
||||
user_data_destroy(
|
||||
user_data
|
||||
);
|
||||
}
|
||||
|
||||
g_free(
|
||||
slot
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Signale un changement du gestionnaire.
|
||||
*
|
||||
* Le callback est appelé sans verrouiller le mutex du gestionnaire.
|
||||
*
|
||||
* @param task_manager Gestionnaire concerné.
|
||||
*/
|
||||
static void task_manager_notify_changed(
|
||||
TaskManager *task_manager
|
||||
)
|
||||
{
|
||||
TaskManagerCallbackSlot *slot = NULL;
|
||||
|
||||
if (task_manager == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g_mutex_lock(
|
||||
&task_manager->mutex
|
||||
);
|
||||
|
||||
slot = task_manager_callback_slot_ref(
|
||||
task_manager->changed_slot
|
||||
);
|
||||
|
||||
g_mutex_unlock(
|
||||
&task_manager->mutex
|
||||
);
|
||||
|
||||
if (slot != NULL &&
|
||||
slot->callback != NULL)
|
||||
{
|
||||
slot->callback(
|
||||
task_manager,
|
||||
slot->user_data
|
||||
);
|
||||
}
|
||||
|
||||
task_manager_callback_slot_unref(
|
||||
slot
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Indique si un état représente une tâche terminée.
|
||||
*
|
||||
* @param state État à examiner.
|
||||
*
|
||||
* @return TRUE si la tâche est terminée, sinon FALSE.
|
||||
*/
|
||||
static gboolean task_manager_state_is_finished(
|
||||
BackgroundTaskState state
|
||||
)
|
||||
{
|
||||
return
|
||||
state == BACKGROUND_TASK_STATE_COMPLETED ||
|
||||
state == BACKGROUND_TASK_STATE_FAILED ||
|
||||
state == BACKGROUND_TASK_STATE_CANCELLED;
|
||||
}
|
||||
|
||||
GQuark task_manager_error_quark(void)
|
||||
{
|
||||
return g_quark_from_static_string(
|
||||
"labfy-investigation-task-manager-error"
|
||||
);
|
||||
}
|
||||
|
||||
TaskManager *task_manager_new(void)
|
||||
{
|
||||
TaskManager *task_manager = NULL;
|
||||
|
||||
task_manager = g_new0(
|
||||
TaskManager,
|
||||
1
|
||||
);
|
||||
|
||||
g_mutex_init(
|
||||
&task_manager->mutex
|
||||
);
|
||||
|
||||
task_manager->tasks =
|
||||
g_ptr_array_new_with_free_func(
|
||||
task_manager_task_unref
|
||||
);
|
||||
|
||||
if (task_manager->tasks == NULL)
|
||||
{
|
||||
g_mutex_clear(
|
||||
&task_manager->mutex
|
||||
);
|
||||
|
||||
g_free(
|
||||
task_manager
|
||||
);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return task_manager;
|
||||
}
|
||||
|
||||
void task_manager_free(
|
||||
TaskManager *task_manager
|
||||
)
|
||||
{
|
||||
GPtrArray *tasks = NULL;
|
||||
|
||||
TaskManagerCallbackSlot *changed_slot =
|
||||
NULL;
|
||||
|
||||
if (task_manager == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g_mutex_lock(
|
||||
&task_manager->mutex
|
||||
);
|
||||
|
||||
tasks = task_manager->tasks;
|
||||
task_manager->tasks = NULL;
|
||||
|
||||
changed_slot =
|
||||
task_manager->changed_slot;
|
||||
|
||||
task_manager->changed_slot = NULL;
|
||||
|
||||
g_mutex_unlock(
|
||||
&task_manager->mutex
|
||||
);
|
||||
|
||||
/*
|
||||
* La libération des tâches et des données utilisateur se fait
|
||||
* hors du mutex.
|
||||
*/
|
||||
if (tasks != NULL)
|
||||
{
|
||||
g_ptr_array_unref(
|
||||
tasks
|
||||
);
|
||||
}
|
||||
|
||||
task_manager_callback_slot_unref(
|
||||
changed_slot
|
||||
);
|
||||
|
||||
g_mutex_clear(
|
||||
&task_manager->mutex
|
||||
);
|
||||
|
||||
g_free(
|
||||
task_manager
|
||||
);
|
||||
}
|
||||
|
||||
gboolean task_manager_add(
|
||||
TaskManager *task_manager,
|
||||
BackgroundTask *task,
|
||||
GError **error
|
||||
)
|
||||
{
|
||||
gsize index = 0;
|
||||
gboolean already_added = FALSE;
|
||||
|
||||
g_return_val_if_fail(
|
||||
error == NULL || *error == NULL,
|
||||
FALSE
|
||||
);
|
||||
|
||||
if (task_manager == NULL ||
|
||||
task == NULL)
|
||||
{
|
||||
g_set_error_literal(
|
||||
error,
|
||||
TASK_MANAGER_ERROR,
|
||||
TASK_MANAGER_ERROR_INVALID_ARGUMENT,
|
||||
"Le gestionnaire ou la tâche est invalide."
|
||||
);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_mutex_lock(
|
||||
&task_manager->mutex
|
||||
);
|
||||
|
||||
for (index = 0;
|
||||
index < task_manager->tasks->len;
|
||||
index++)
|
||||
{
|
||||
if (g_ptr_array_index(
|
||||
task_manager->tasks,
|
||||
index
|
||||
) == task)
|
||||
{
|
||||
already_added = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!already_added)
|
||||
{
|
||||
g_ptr_array_add(
|
||||
task_manager->tasks,
|
||||
background_task_ref(task)
|
||||
);
|
||||
}
|
||||
|
||||
g_mutex_unlock(
|
||||
&task_manager->mutex
|
||||
);
|
||||
|
||||
if (already_added)
|
||||
{
|
||||
g_set_error_literal(
|
||||
error,
|
||||
TASK_MANAGER_ERROR,
|
||||
TASK_MANAGER_ERROR_ALREADY_ADDED,
|
||||
"Cette tâche est déjà suivie par le gestionnaire."
|
||||
);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
task_manager_notify_changed(
|
||||
task_manager
|
||||
);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gsize task_manager_get_count(
|
||||
const TaskManager *task_manager
|
||||
)
|
||||
{
|
||||
TaskManager *mutable_task_manager = NULL;
|
||||
gsize count = 0;
|
||||
|
||||
if (task_manager == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
mutable_task_manager =
|
||||
(TaskManager *) task_manager;
|
||||
|
||||
g_mutex_lock(
|
||||
&mutable_task_manager->mutex
|
||||
);
|
||||
|
||||
if (mutable_task_manager->tasks != NULL)
|
||||
{
|
||||
count =
|
||||
mutable_task_manager->tasks->len;
|
||||
}
|
||||
|
||||
g_mutex_unlock(
|
||||
&mutable_task_manager->mutex
|
||||
);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
BackgroundTask *task_manager_get_task(
|
||||
const TaskManager *task_manager,
|
||||
gsize index
|
||||
)
|
||||
{
|
||||
TaskManager *mutable_task_manager = NULL;
|
||||
BackgroundTask *task = NULL;
|
||||
|
||||
if (task_manager == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mutable_task_manager =
|
||||
(TaskManager *) task_manager;
|
||||
|
||||
g_mutex_lock(
|
||||
&mutable_task_manager->mutex
|
||||
);
|
||||
|
||||
if (mutable_task_manager->tasks != NULL &&
|
||||
index < mutable_task_manager->tasks->len)
|
||||
{
|
||||
task = background_task_ref(
|
||||
g_ptr_array_index(
|
||||
mutable_task_manager->tasks,
|
||||
index
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
g_mutex_unlock(
|
||||
&mutable_task_manager->mutex
|
||||
);
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
gboolean task_manager_remove(
|
||||
TaskManager *task_manager,
|
||||
BackgroundTask *task
|
||||
)
|
||||
{
|
||||
BackgroundTask *removed_task = NULL;
|
||||
|
||||
gsize index = 0;
|
||||
|
||||
if (task_manager == NULL ||
|
||||
task == NULL)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_mutex_lock(
|
||||
&task_manager->mutex
|
||||
);
|
||||
|
||||
for (index = 0;
|
||||
index < task_manager->tasks->len;
|
||||
index++)
|
||||
{
|
||||
if (g_ptr_array_index(
|
||||
task_manager->tasks,
|
||||
index
|
||||
) == task)
|
||||
{
|
||||
removed_task = g_ptr_array_steal_index(
|
||||
task_manager->tasks,
|
||||
index
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_mutex_unlock(
|
||||
&task_manager->mutex
|
||||
);
|
||||
|
||||
if (removed_task == NULL)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* La référence du gestionnaire est libérée hors du mutex.
|
||||
*/
|
||||
background_task_unref(
|
||||
removed_task
|
||||
);
|
||||
|
||||
task_manager_notify_changed(
|
||||
task_manager
|
||||
);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gsize task_manager_remove_finished(
|
||||
TaskManager *task_manager
|
||||
)
|
||||
{
|
||||
GPtrArray *removed_tasks = NULL;
|
||||
|
||||
gsize index = 0;
|
||||
gsize removed_count = 0;
|
||||
|
||||
if (task_manager == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
removed_tasks =
|
||||
g_ptr_array_new_with_free_func(
|
||||
task_manager_task_unref
|
||||
);
|
||||
|
||||
g_mutex_lock(
|
||||
&task_manager->mutex
|
||||
);
|
||||
|
||||
/*
|
||||
* Le parcours se fait à l'envers afin que la suppression d'un
|
||||
* élément ne décale pas les index encore à examiner.
|
||||
*/
|
||||
index = task_manager->tasks->len;
|
||||
|
||||
while (index > 0)
|
||||
{
|
||||
BackgroundTask *task = NULL;
|
||||
BackgroundTaskState state;
|
||||
|
||||
index--;
|
||||
|
||||
task = g_ptr_array_index(
|
||||
task_manager->tasks,
|
||||
index
|
||||
);
|
||||
|
||||
state = background_task_get_state(
|
||||
task
|
||||
);
|
||||
|
||||
if (task_manager_state_is_finished(
|
||||
state
|
||||
))
|
||||
{
|
||||
BackgroundTask *removed_task = NULL;
|
||||
|
||||
removed_task = g_ptr_array_steal_index(
|
||||
task_manager->tasks,
|
||||
index
|
||||
);
|
||||
|
||||
g_ptr_array_add(
|
||||
removed_tasks,
|
||||
removed_task
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
removed_count =
|
||||
removed_tasks->len;
|
||||
|
||||
g_mutex_unlock(
|
||||
&task_manager->mutex
|
||||
);
|
||||
|
||||
/*
|
||||
* Les références retirées sont libérées hors du mutex.
|
||||
*/
|
||||
g_ptr_array_unref(
|
||||
removed_tasks
|
||||
);
|
||||
|
||||
if (removed_count > 0)
|
||||
{
|
||||
task_manager_notify_changed(
|
||||
task_manager
|
||||
);
|
||||
}
|
||||
|
||||
return removed_count;
|
||||
}
|
||||
|
||||
void task_manager_cancel_all(
|
||||
TaskManager *task_manager
|
||||
)
|
||||
{
|
||||
GPtrArray *task_snapshot = NULL;
|
||||
|
||||
gsize index = 0;
|
||||
gboolean cancellation_requested = FALSE;
|
||||
|
||||
if (task_manager == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
task_snapshot =
|
||||
g_ptr_array_new_with_free_func(
|
||||
task_manager_task_unref
|
||||
);
|
||||
|
||||
g_mutex_lock(
|
||||
&task_manager->mutex
|
||||
);
|
||||
|
||||
for (index = 0;
|
||||
index < task_manager->tasks->len;
|
||||
index++)
|
||||
{
|
||||
BackgroundTask *task = NULL;
|
||||
|
||||
task = g_ptr_array_index(
|
||||
task_manager->tasks,
|
||||
index
|
||||
);
|
||||
|
||||
g_ptr_array_add(
|
||||
task_snapshot,
|
||||
background_task_ref(task)
|
||||
);
|
||||
}
|
||||
|
||||
g_mutex_unlock(
|
||||
&task_manager->mutex
|
||||
);
|
||||
|
||||
/*
|
||||
* L'annulation et la lecture des états sont effectuées hors
|
||||
* du mutex du gestionnaire.
|
||||
*/
|
||||
for (index = 0;
|
||||
index < task_snapshot->len;
|
||||
index++)
|
||||
{
|
||||
BackgroundTask *task = NULL;
|
||||
|
||||
task = g_ptr_array_index(
|
||||
task_snapshot,
|
||||
index
|
||||
);
|
||||
|
||||
if (background_task_get_state(task) ==
|
||||
BACKGROUND_TASK_STATE_RUNNING)
|
||||
{
|
||||
background_task_cancel(
|
||||
task
|
||||
);
|
||||
|
||||
cancellation_requested = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
g_ptr_array_unref(
|
||||
task_snapshot
|
||||
);
|
||||
|
||||
if (cancellation_requested)
|
||||
{
|
||||
task_manager_notify_changed(
|
||||
task_manager
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void task_manager_set_changed_callback(
|
||||
TaskManager *task_manager,
|
||||
TaskManagerChangedCallback callback,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_data_destroy
|
||||
)
|
||||
{
|
||||
TaskManagerCallbackSlot *new_slot = NULL;
|
||||
TaskManagerCallbackSlot *old_slot = NULL;
|
||||
|
||||
if (task_manager == NULL)
|
||||
{
|
||||
/*
|
||||
* Aucun transfert de propriété n'a lieu lorsque le
|
||||
* gestionnaire est invalide.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
new_slot = task_manager_callback_slot_new(
|
||||
callback,
|
||||
user_data,
|
||||
user_data_destroy
|
||||
);
|
||||
|
||||
g_mutex_lock(
|
||||
&task_manager->mutex
|
||||
);
|
||||
|
||||
old_slot =
|
||||
task_manager->changed_slot;
|
||||
|
||||
task_manager->changed_slot =
|
||||
new_slot;
|
||||
|
||||
g_mutex_unlock(
|
||||
&task_manager->mutex
|
||||
);
|
||||
|
||||
/*
|
||||
* L'ancien destructeur utilisateur est appelé hors du mutex.
|
||||
*/
|
||||
task_manager_callback_slot_unref(
|
||||
old_slot
|
||||
);
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@
|
|||
#include "widgets/sidebar.h"
|
||||
#include "widgets/workspace.h"
|
||||
#include "widgets/investigation_tree_view.h"
|
||||
#include "widgets/task_panel.h"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
|
|
@ -25,6 +26,11 @@
|
|||
*/
|
||||
#define MAIN_WINDOW_SIDEBAR_POSITION 250
|
||||
|
||||
/**
|
||||
* @brief Position initiale de la séparation verticale.
|
||||
*/
|
||||
#define MAIN_WINDOW_TASK_PANEL_POSITION 430
|
||||
|
||||
/**
|
||||
* @brief Titre par défaut de la fenêtre.
|
||||
*/
|
||||
|
|
@ -52,12 +58,15 @@ struct MainWindow
|
|||
GtkWidget *action_bar;
|
||||
GtkWidget *new_investigation_button;
|
||||
GtkWidget *open_investigation_button;
|
||||
GtkWidget *demo_task_button;
|
||||
GtkWidget *content_paned;
|
||||
GtkWidget *main_paned;
|
||||
GtkWidget *status_label;
|
||||
GtkWidget *quit_button;
|
||||
|
||||
Sidebar *sidebar;
|
||||
Workspace *workspace;
|
||||
TaskPanel *task_panel;
|
||||
|
||||
MainWindowNewInvestigationCallback
|
||||
new_investigation_callback;
|
||||
|
|
@ -76,6 +85,12 @@ struct MainWindow
|
|||
|
||||
gpointer
|
||||
quit_user_data;
|
||||
|
||||
MainWindowDemoTaskCallback
|
||||
demo_task_callback;
|
||||
|
||||
gpointer
|
||||
demo_task_user_data;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -130,6 +145,32 @@ static void main_window_on_open_investigation_clicked(
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Transmet la demande de tâche de démonstration au contrôleur.
|
||||
*
|
||||
* @param button Bouton ayant reçu le clic.
|
||||
* @param user_data Pointeur vers MainWindow.
|
||||
*/
|
||||
static void main_window_on_demo_task_clicked(
|
||||
GtkButton *button,
|
||||
gpointer user_data
|
||||
)
|
||||
{
|
||||
MainWindow *main_window = user_data;
|
||||
|
||||
(void) button;
|
||||
|
||||
if (main_window == NULL ||
|
||||
main_window->demo_task_callback == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
main_window->demo_task_callback(
|
||||
main_window->demo_task_user_data
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Transmet la demande de fermeture au contrôleur.
|
||||
*
|
||||
|
|
@ -156,13 +197,18 @@ static void main_window_on_quit_clicked(
|
|||
);
|
||||
}
|
||||
|
||||
MainWindow *main_window_new(GtkApplication *application)
|
||||
MainWindow *main_window_new(
|
||||
GtkApplication *application,
|
||||
TaskManager *task_manager
|
||||
)
|
||||
{
|
||||
MainWindow *main_window = NULL;
|
||||
GtkWidget *sidebar_widget = NULL;
|
||||
GtkWidget *workspace_widget = NULL;
|
||||
GtkWidget *task_panel_widget = NULL;
|
||||
|
||||
if (application == NULL)
|
||||
if (application == NULL ||
|
||||
task_manager == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -239,6 +285,11 @@ MainWindow *main_window_new(GtkApplication *application)
|
|||
"Ouvrir une enquête"
|
||||
);
|
||||
|
||||
main_window->demo_task_button =
|
||||
gtk_button_new_with_label(
|
||||
"Tâche de test"
|
||||
);
|
||||
|
||||
main_window->quit_button =
|
||||
gtk_button_new_with_label(
|
||||
"Quitter"
|
||||
|
|
@ -254,6 +305,11 @@ MainWindow *main_window_new(GtkApplication *application)
|
|||
main_window->open_investigation_button
|
||||
);
|
||||
|
||||
gtk_box_append(
|
||||
GTK_BOX(main_window->action_bar),
|
||||
main_window->demo_task_button
|
||||
);
|
||||
|
||||
gtk_box_append(
|
||||
GTK_BOX(main_window->action_bar),
|
||||
main_window->quit_button
|
||||
|
|
@ -277,6 +333,15 @@ MainWindow *main_window_new(GtkApplication *application)
|
|||
main_window
|
||||
);
|
||||
|
||||
g_signal_connect(
|
||||
main_window->demo_task_button,
|
||||
"clicked",
|
||||
G_CALLBACK(
|
||||
main_window_on_demo_task_clicked
|
||||
),
|
||||
main_window
|
||||
);
|
||||
|
||||
g_signal_connect(
|
||||
main_window->quit_button,
|
||||
"clicked",
|
||||
|
|
@ -345,6 +410,32 @@ MainWindow *main_window_new(GtkApplication *application)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
main_window->task_panel = task_panel_new(
|
||||
task_manager
|
||||
);
|
||||
|
||||
if (main_window->task_panel == NULL)
|
||||
{
|
||||
main_window_free(
|
||||
main_window
|
||||
);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
task_panel_widget = task_panel_get_widget(
|
||||
main_window->task_panel
|
||||
);
|
||||
|
||||
if (task_panel_widget == NULL)
|
||||
{
|
||||
main_window_free(
|
||||
main_window
|
||||
);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Placement des deux composants dans GtkPaned.
|
||||
*/
|
||||
|
|
@ -393,6 +484,55 @@ MainWindow *main_window_new(GtkApplication *application)
|
|||
TRUE
|
||||
);
|
||||
|
||||
main_window->content_paned = gtk_paned_new(
|
||||
GTK_ORIENTATION_VERTICAL
|
||||
);
|
||||
|
||||
gtk_widget_set_hexpand(
|
||||
main_window->content_paned,
|
||||
TRUE
|
||||
);
|
||||
|
||||
gtk_widget_set_vexpand(
|
||||
main_window->content_paned,
|
||||
TRUE
|
||||
);
|
||||
|
||||
gtk_paned_set_start_child(
|
||||
GTK_PANED(main_window->content_paned),
|
||||
main_window->main_paned
|
||||
);
|
||||
|
||||
gtk_paned_set_end_child(
|
||||
GTK_PANED(main_window->content_paned),
|
||||
task_panel_widget
|
||||
);
|
||||
|
||||
gtk_paned_set_position(
|
||||
GTK_PANED(main_window->content_paned),
|
||||
MAIN_WINDOW_TASK_PANEL_POSITION
|
||||
);
|
||||
|
||||
gtk_paned_set_resize_start_child(
|
||||
GTK_PANED(main_window->content_paned),
|
||||
TRUE
|
||||
);
|
||||
|
||||
gtk_paned_set_resize_end_child(
|
||||
GTK_PANED(main_window->content_paned),
|
||||
FALSE
|
||||
);
|
||||
|
||||
gtk_paned_set_shrink_start_child(
|
||||
GTK_PANED(main_window->content_paned),
|
||||
TRUE
|
||||
);
|
||||
|
||||
gtk_paned_set_shrink_end_child(
|
||||
GTK_PANED(main_window->content_paned),
|
||||
TRUE
|
||||
);
|
||||
|
||||
main_window->status_label = gtk_label_new(
|
||||
MAIN_WINDOW_NO_INVESTIGATION_STATUS
|
||||
);
|
||||
|
|
@ -436,7 +576,7 @@ MainWindow *main_window_new(GtkApplication *application)
|
|||
|
||||
gtk_box_append(
|
||||
GTK_BOX(main_window->main_box),
|
||||
main_window->main_paned
|
||||
main_window->content_paned
|
||||
);
|
||||
|
||||
gtk_box_append(
|
||||
|
|
@ -449,6 +589,9 @@ MainWindow *main_window_new(GtkApplication *application)
|
|||
main_window->main_box
|
||||
);
|
||||
|
||||
/*
|
||||
* Ce volet vertical sépare la zone principale du panneau d'activité.
|
||||
*/
|
||||
return main_window;
|
||||
}
|
||||
|
||||
|
|
@ -652,6 +795,21 @@ void main_window_set_open_investigation_callback(
|
|||
main_window->open_investigation_user_data = user_data;
|
||||
}
|
||||
|
||||
void main_window_set_demo_task_callback(
|
||||
MainWindow *main_window,
|
||||
MainWindowDemoTaskCallback callback,
|
||||
gpointer user_data
|
||||
)
|
||||
{
|
||||
if (main_window == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
main_window->demo_task_callback = callback;
|
||||
main_window->demo_task_user_data = user_data;
|
||||
}
|
||||
|
||||
void main_window_set_quit_callback(
|
||||
MainWindow *main_window,
|
||||
MainWindowQuitCallback callback,
|
||||
|
|
@ -693,11 +851,57 @@ void main_window_free(
|
|||
}
|
||||
|
||||
/*
|
||||
* La fenêtre et tous ses widgets doivent être détruits pendant que
|
||||
* les structures MainWindow, Sidebar et Workspace existent encore.
|
||||
*
|
||||
* Certains widgets possèdent des callbacks dont user_data pointe vers
|
||||
* ces structures.
|
||||
* TaskPanel possède un timer et un callback enregistré dans
|
||||
* TaskManager. Ils doivent être retirés avant la destruction
|
||||
* des widgets GTK.
|
||||
*/
|
||||
task_panel_free(
|
||||
main_window->task_panel
|
||||
);
|
||||
|
||||
main_window->task_panel = NULL;
|
||||
|
||||
/*
|
||||
* Empêche la destruction du modèle de transmettre une dernière
|
||||
* sélection à Application.
|
||||
*/
|
||||
if (main_window->sidebar != NULL)
|
||||
{
|
||||
sidebar_set_selection_callback(
|
||||
main_window->sidebar,
|
||||
NULL,
|
||||
NULL
|
||||
);
|
||||
|
||||
/*
|
||||
* Le modèle est détaché pendant que le GtkListView existe
|
||||
* encore.
|
||||
*/
|
||||
sidebar_set_tree_model(
|
||||
main_window->sidebar,
|
||||
NULL
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Les structures qui manipulent encore leurs widgets doivent être
|
||||
* nettoyées avant gtk_window_destroy().
|
||||
*/
|
||||
sidebar_free(
|
||||
main_window->sidebar
|
||||
);
|
||||
|
||||
workspace_free(
|
||||
main_window->workspace
|
||||
);
|
||||
|
||||
main_window->sidebar = NULL;
|
||||
main_window->workspace = NULL;
|
||||
|
||||
/*
|
||||
* Les modules ne manipulent plus leurs widgets.
|
||||
* GTK peut maintenant détruire tout l'arbre sans callback vers
|
||||
* des structures déjà libérées.
|
||||
*/
|
||||
if (main_window->window != NULL)
|
||||
{
|
||||
|
|
@ -708,16 +912,7 @@ void main_window_free(
|
|||
main_window->window = NULL;
|
||||
}
|
||||
|
||||
workspace_free(
|
||||
main_window->workspace
|
||||
g_free(
|
||||
main_window
|
||||
);
|
||||
|
||||
sidebar_free(
|
||||
main_window->sidebar
|
||||
);
|
||||
|
||||
main_window->workspace = NULL;
|
||||
main_window->sidebar = NULL;
|
||||
|
||||
g_free(main_window);
|
||||
}
|
||||
|
|
|
|||
1105
src/widgets/task_panel.c
Normal file
1105
src/widgets/task_panel.c
Normal file
File diff suppressed because it is too large
Load diff
Binary file not shown.
Binary file not shown.
BIN
tests/test_error
BIN
tests/test_error
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
tests/test_task_manager
Executable file
BIN
tests/test_task_manager
Executable file
Binary file not shown.
1116
tests/test_task_manager.c
Normal file
1116
tests/test_task_manager.c
Normal file
File diff suppressed because it is too large
Load diff
Binary file not shown.
Loading…
Reference in a new issue