18 KiB
Ticket #029 — Intégrer InvestigationSession au cycle de vie GTK
Contexte
Le ticket #028 a ajouté InvestigationSession, qui permet d’ouvrir une enquête existante de manière contrôlée.
Une session valide possède désormais :
- un
InvestigationProject; - une connexion
Databaseouverte ; - un
InvestigationRecordchargé depuis SQLite.
L’application GTK utilise encore l’ancien objet Investigation pour représenter le dossier sélectionné.
Son fonctionnement actuel est approximativement le suivant :
FolderDialog
↓
Investigation
↓
InvestigationTreeBuilder
↓
InvestigationTreeModel
↓
MainWindow
Cette organisation ne conserve pas la connexion SQLite et n’utilise pas les informations persistées dans la table investigation.
Il faut désormais remplacer l’ancien objet détenu par Application par une véritable InvestigationSession.
Objectif
Faire de InvestigationSession le contexte actif de l’application.
Après la sélection d’un dossier :
- ouvrir une nouvelle
InvestigationSession; - récupérer le chemin racine depuis son
InvestigationProject; - construire le nouvel
InvestigationTreeModel; - ne remplacer l’ancienne enquête qu’après validation complète ;
- mettre à jour la fenêtre principale ;
- conserver la session jusqu’à la fermeture de l’application.
L’application ne doit posséder qu’une seule session active à la fois.
Architecture attendue
FolderDialog
│
▼
Application
│
├── InvestigationSession
│ ├── InvestigationProject
│ ├── Database
│ └── InvestigationRecord
│
├── InvestigationTreeModel
│
└── MainWindow
├── Sidebar
├── Workspace
└── Barre d’état
Principe de remplacement transactionnel
L’ouverture d’une nouvelle enquête doit suivre cet ordre :
ancienne session toujours active
↓
ouvrir la nouvelle session
↓
construire le nouvel arbre
↓
vérifier que les deux objets sont valides
↓
remplacer l’ancien arbre
↓
fermer l’ancienne session
↓
installer la nouvelle session
↓
mettre à jour MainWindow
En cas d’échec avant le remplacement :
ancienne session conservée
ancienne arborescence conservée
nouvelle ressource libérée
message d’erreur produit
L’application ne doit jamais perdre une enquête déjà ouverte simplement parce qu’une nouvelle sélection est invalide.
Travail à réaliser
1. Remplacer l’ancien objet dans Application
Modifier :
src/core/application.c
La structure privée actuelle contient notamment :
Investigation *investigation;
InvestigationTreeModel *tree_model;
Remplacer le premier champ par :
InvestigationSession *session;
La structure doit devenir au minimum :
struct Application
{
GtkApplication *gtk_application;
MainWindow *main_window;
InvestigationSession *session;
InvestigationTreeModel *tree_model;
};
Ne pas exposer cette structure dans le header public.
2. Modifier les dépendances de application.c
Supprimer :
#include "core/investigation.h"
Ajouter :
#include "core/investigation_session.h"
#include "core/investigation_project.h"
#include "models/investigation_record.h"
Conserver les dépendances nécessaires à :
FolderDialog
MainWindow
InvestigationTreeBuilder
InvestigationTreeModel
GTK
application.c ne doit pas inclure directement :
#include <sqlite3.h>
et ne doit appeler aucune fonction sqlite3_*.
3. Ouvrir la session après la sélection du dossier
Dans :
application_on_folder_selected()
remplacer la création de l’ancien objet :
investigation_new(folder_path)
par :
investigation_session_open(
folder_path,
&error
);
Utiliser des variables temporaires :
InvestigationSession *new_session = NULL;
InvestigationTreeModel *new_tree_model = NULL;
GError *error = NULL;
Ne jamais écrire directement dans :
application->session
application->tree_model
avant que toute l’ouverture soit validée.
4. Gérer une sélection annulée
Lorsque :
folder_path == NULL
la fonction doit simplement retourner.
L’enquête déjà ouverte doit rester active.
Aucune session ne doit être fermée.
Aucune erreur ne doit être affichée.
Un message de diagnostic avec g_print() reste acceptable :
Sélection annulée.
5. Gérer l’échec d’ouverture de session
Si :
new_session == NULL
la fonction doit :
- afficher un avertissement contenant le message du
GError; - libérer le
GError; - conserver l’ancienne session ;
- conserver l’ancien arbre ;
- ne pas modifier la fenêtre principale.
Exemple de diagnostic acceptable :
g_warning(
"Impossible d'ouvrir l'enquête : %s",
error != NULL ? error->message : "erreur inconnue"
);
Le dialogue graphique d’erreur est hors périmètre de ce ticket.
6. Construire l’arborescence depuis la session
Récupérer le projet de la nouvelle session :
const InvestigationProject *project = NULL;
avec :
project = investigation_session_get_project(
new_session
);
Récupérer ensuite son chemin racine :
const char *root_path = NULL;
avec :
root_path = investigation_project_get_root_path(
project
);
Construire le nouvel arbre avec :
new_tree_model = investigation_tree_builder_build(
root_path
);
La logique de construction du chemin ne doit pas être dupliquée dans application.c.
7. Gérer l’échec de construction de l’arbre
Si InvestigationTreeBuilder échoue après l’ouverture de la session :
new_session valide
new_tree_model == NULL
la fonction doit :
investigation_session_close(new_session);
puis retourner.
L’ancienne session et l’ancien arbre doivent rester actifs.
Le message suivant est acceptable :
Impossible de construire l'arborescence de l'enquête.
8. Remplacer les anciens objets uniquement après validation
Lorsque new_session et new_tree_model sont tous deux valides :
investigation_tree_model_free(
application->tree_model
);
investigation_session_close(
application->session
);
application->tree_model = new_tree_model;
application->session = new_session;
Après le transfert :
new_tree_model = NULL;
new_session = NULL;
Cette remise à NULL n’est pas obligatoire si la fonction retourne immédiatement, mais elle est recommandée pour rendre la propriété explicite.
9. Conserver la connexion SQLite ouverte
Après une ouverture réussie, la session doit rester stockée dans :
application->session
Il est interdit de fermer la session à la fin de :
application_on_folder_selected()
La connexion Database doit rester utilisable pendant toute la durée d’ouverture de l’enquête.
10. Ajouter un accesseur interne ou public
Ajouter dans :
include/core/application.h
la déclaration suivante :
const InvestigationSession *application_get_session(
const Application *application
);
Implémenter dans :
src/core/application.c
const InvestigationSession *application_get_session(
const Application *application
)
{
if (application == NULL)
{
return NULL;
}
return application->session;
}
Le pointeur retourné appartient à Application.
Le code appelant ne doit pas fermer cette session.
Cet accesseur servira aux futurs contrôleurs et DAO.
11. Mettre à jour la fenêtre principale
Ajouter dans :
include/views/main_window.h
la fonction :
void main_window_set_investigation(
MainWindow *main_window,
const char *investigation_name,
const char *investigation_root_path
);
Implémenter dans :
src/views/main_window.c
Cette fonction doit :
- accepter
main_window == NULL; - accepter des chaînes
NULLsans planter ; - mettre à jour le titre de la fenêtre ;
- mettre à jour la barre d’état.
Titre attendu
Lorsque le nom est valide :
Labfy Investigation — <nom>
Exemple :
Labfy Investigation — Enquete_Session
Lorsque le nom est absent :
Labfy Investigation
Construire le titre avec GLib :
g_strdup_printf()
puis le libérer avec :
g_free()
Barre d’état attendue
Lorsque l’enquête est ouverte :
Enquête ouverte : <nom> — <chemin racine>
Lorsque certaines valeurs sont absentes, utiliser une formulation sûre sans déréférencer NULL.
La barre d’état ne doit pas conserver un pointeur vers une chaîne temporaire.
gtk_label_set_text() copie le texte fourni.
12. Récupérer le nom persistant
Dans application_on_folder_selected(), récupérer :
const InvestigationRecord *record = NULL;
const char *investigation_name = NULL;
avec :
record = investigation_session_get_record(
application->session
);
investigation_name = investigation_record_get_name(
record
);
Utiliser les données du InvestigationRecord.
Ne pas reconstruire le nom à partir du dernier composant du chemin.
13. Mettre à jour la fenêtre après le modèle
Après l’installation de la nouvelle session et du nouvel arbre :
main_window_set_tree_model(
application->main_window,
application->tree_model
);
puis :
main_window_set_investigation(
application->main_window,
investigation_name,
root_path
);
L’ordre attendu est :
nouvelle session installée
nouvel arbre installé
sidebar mise à jour
titre et statut mis à jour
14. Fermer la session dans application_free()
Remplacer :
investigation_free(application->investigation);
par :
investigation_session_close(
application->session
);
L’ordre de nettoyage conseillé est :
investigation_tree_model_free(
application->tree_model
);
investigation_session_close(
application->session
);
main_window_free(
application->main_window
);
Puis libérer GtkApplication et Application comme actuellement.
application_free(NULL) doit rester valide.
15. Ne plus utiliser l’ancien objet dans l’application
À la fin du ticket, les symboles suivants ne doivent plus apparaître dans application.c :
Investigation *
investigation_new
investigation_free
investigation_get_root_path
investigation_get_database_path
Le module historique investigation.c n’est pas supprimé dans ce ticket.
Sa suppression éventuelle sera effectuée séparément après vérification qu’aucun autre composant ne l’utilise.
Tests et validations
16. Tests automatisés existants
Aucun nouveau test GTK automatisé n’est obligatoire dans ce ticket.
Tous les tests existants doivent rester valides :
make test
En particulier :
InvestigationProject
InvestigationSession
InvestigationTreeBuilder
InvestigationTreeModel
InvestigationDao
17. Test manuel d’ouverture valide
Créer ou utiliser une enquête valide.
Lancer :
make
make run
Sélectionner le dossier racine de l’enquête.
Vérifier :
- la fenêtre reste ouverte ;
- l’arborescence apparaît dans la sidebar ;
- le titre contient le nom persistant de l’enquête ;
- la barre d’état contient le nom et le chemin racine ;
- aucune erreur SQLite n’apparaît ;
- la session reste active après la fin du callback.
18. Test manuel d’annulation
Relancer l’application et annuler la sélection.
Vérifier :
- aucun crash ;
- aucun warning critique ;
- la fenêtre reste ouverte ;
- la barre d’état reste sur :
Aucune enquête ouverte
19. Test manuel d’un dossier invalide
Sélectionner un dossier qui ne contient pas :
00_BaseDeDonnees/Enquete.sqlite
Vérifier :
- aucun crash ;
- un warning explicite est affiché ;
- aucun fichier SQLite n’est créé ;
- la fenêtre reste utilisable ;
- aucune fausse enquête n’apparaît dans la sidebar.
20. Test manuel de remplacement
Si l’interface permet une seconde sélection pendant la même exécution :
- ouvrir une enquête valide A ;
- tenter d’ouvrir un dossier invalide ;
- vérifier que A reste affichée ;
- ouvrir une enquête valide B ;
- vérifier que B remplace A.
Si une seconde sélection n’est pas encore accessible dans l’interface, cette validation sera complétée lors de l’ajout de l’action « Ouvrir ».
La logique du callback doit néanmoins déjà préserver l’ancienne session.
Gestion de la mémoire
Les propriétaires doivent être clairement définis.
Application possède
GtkApplication
MainWindow
InvestigationSession
InvestigationTreeModel
InvestigationSession possède
InvestigationProject
Database
InvestigationRecord
Variables temporaires du callback
new_session
new_tree_model
GError
En cas d’échec :
new_session fermée si elle existe
new_tree_model libéré si nécessaire
GError libéré
ancienne session conservée
ancien tree model conservé
En cas de succès :
anciens objets libérés
nouveaux objets transférés dans Application
aucun double free
Critères d’acceptation
Applicationpossède unInvestigationSession *.Applicationne possède plus d’Investigation *.- Le callback ouvre une session avec
investigation_session_open(). - Le chemin racine provient de
InvestigationProject. - Le nom provient d’
InvestigationRecord. - L’arbre est construit depuis le chemin de la session.
- L’ancienne session reste active si l’ouverture échoue.
- L’ancien arbre reste actif si l’ouverture échoue.
- La nouvelle session est fermée si la construction de l’arbre échoue.
- Les anciens objets ne sont remplacés qu’après validation complète.
- La session reste ouverte après le callback.
application_free()ferme la session.application_get_session(NULL)retourneNULL.main_window_set_investigation()accepteNULL.- Le titre affiche le nom de l’enquête.
- La barre d’état affiche le nom et le chemin racine.
application.cn’appelle aucune fonction SQLite.- Aucun chemin SQLite n’est reconstruit dans
application.c. - Les anciens tests restent valides.
makeréussit sans warning.make testréussit.- Le test manuel d’ouverture valide réussit.
- Le test manuel d’annulation réussit.
- Le test manuel de dossier invalide réussit.
git diff --checkne retourne aucune erreur.
Audit attendu
La commande suivante ne doit rien afficher :
rg -n \
'investigation_new|investigation_free|investigation_get_root_path|investigation_get_database_path|Investigation \*' \
src/core/application.c
La commande suivante ne doit rien afficher :
rg -n \
'sqlite3_|#include <sqlite3.h>|00_BaseDeDonnees|Enquete.sqlite' \
src/core/application.c
Vérifier la présence de la nouvelle session :
rg -n \
'InvestigationSession|investigation_session_' \
src/core/application.c \
include/core/application.h
Vérifier la mise à jour de la fenêtre :
rg -n \
'main_window_set_investigation' \
include/views/main_window.h \
src/views/main_window.c \
src/core/application.c
Hors périmètre
Ce ticket ne doit pas ajouter :
- une action de menu « Ouvrir » ;
- un raccourci clavier ;
- un dialogue graphique détaillé pour les erreurs ;
- la création d’une enquête ;
- la fermeture manuelle d’une enquête ;
- la réparation d’un chemin racine incohérent ;
- la modification de la base SQLite ;
- les DAO des preuves ou des entités ;
- la restauration automatique de la dernière enquête ;
- la persistance des préférences utilisateur ;
- le verrouillage multi-instance ;
- la suppression de l’ancien module
Investigation.
Fichiers principalement concernés
include/core/application.h
src/core/application.c
include/views/main_window.h
src/views/main_window.c
Fichiers utilisés sans modification attendue :
include/core/investigation_session.h
src/core/investigation_session.c
include/core/investigation_project.h
src/core/investigation_project.c
include/models/investigation_record.h
src/models/investigation_record.c
include/core/investigation_tree_builder.h
src/core/investigation_tree_builder.c
Le Makefile ne devrait pas nécessiter de nouvelle cible, car les sources de production sont détectées automatiquement avec :
SRC := $(shell find src -name "*.c")
Résultat attendu
À la fin du ticket, la sélection d’un dossier doit ouvrir une véritable session d’enquête.
L’application doit conserver ensemble :
la connexion SQLite
les métadonnées persistées
le contexte de fichiers
l’arborescence
l’état visuel de la fenêtre
Le flux final doit être :
sélection du dossier
↓
InvestigationSession ouverte
↓
InvestigationTreeModel construit
↓
session installée dans Application
↓
arbre affiché dans MainWindow
↓
nom et chemin affichés
Commit attendu
Avant le commit :
make clean
make
make test
git diff --check
git status --short
Préparer les fichiers :
git add \
include/core/application.h \
src/core/application.c \
include/views/main_window.h \
src/views/main_window.c
Contrôler :
git diff --cached --stat
git diff --cached
Créer le commit :
git commit -m "feat(app): integrate investigation session"
Puis pousser après validation complète :
git push