labfy-investigation/docs/tickets/closed/TICKET-030.md

18 KiB
Raw Blame History

Ticket #030 — Créer une enquête depuis linterface GTK

Contexte

Le ticket #029 a intégré InvestigationSession au cycle de vie de lapplication.

Lapplication sait désormais :

  • ouvrir une enquête existante ;
  • conserver sa connexion SQLite ;
  • charger ses métadonnées persistées ;
  • construire son arborescence ;
  • afficher son nom et son chemin dans la fenêtre principale ;
  • conserver lancienne enquête si une nouvelle ouverture échoue.

Cependant, lorsquaucune enquête nexiste encore, lutilisateur ne peut pas en créer une depuis linterface.

Le dossier sélectionné est actuellement toujours interprété comme une enquête existante. Si la base suivante est absente :

00_BaseDeDonnees/Enquete.sqlite

louverture échoue, ce qui est volontairement sûr.

La création dune nouvelle enquête doit être une action explicite et séparée de louverture.


Objectif

Ajouter une première fonctionnalité GTK réellement utilisable :

Créer une nouvelle enquête

Le flux attendu est :

clic sur « Nouvelle enquête »
        ↓
sélection du dossier parent
        ↓
saisie du nom de lenquête
        ↓
validation des paramètres
        ↓
investigation_project_create()
        ↓
investigation_session_open()
        ↓
construction de larborescence
        ↓
installation dans Application
        ↓
mise à jour de MainWindow

Lutilisateur ne doit jamais avoir à créer manuellement :

00_BaseDeDonnees
Enquete.sqlite
01_Preuves_Originales
...
09_Hash

Architecture attendue

MainWindow
    │
    └── bouton « Nouvelle enquête »
             │
             ▼
CreateInvestigationDialog
             │
             ├── dossier parent
             ├── nom de lenquête
             └── validation
                     │
                     ▼
Application
    │
    ├── investigation_project_create()
    ├── investigation_session_open()
    ├── investigation_tree_builder_build()
    └── main_window_set_investigation()

Travail à réaliser

1. Créer un module de dialogue dédié

Créer :

include/views/create_investigation_dialog.h
src/views/create_investigation_dialog.c

Le module doit rester indépendant de SQLite.

Il ne doit pas inclure :

#include <sqlite3.h>

Il ne doit pas appeler :

database_initialize
investigation_project_create
investigation_session_open

Son rôle est uniquement de recueillir les informations saisies par lutilisateur.


2. Définir le callback public

Dans :

include/views/create_investigation_dialog.h

déclarer :

typedef void (*CreateInvestigationDialogCallback)(
    const char *parent_directory,
    const char *investigation_name,
    gpointer user_data
);

Puis :

void create_investigation_dialog_present(
    GtkWindow *parent_window,
    CreateInvestigationDialogCallback callback,
    gpointer user_data
);

Le callback reçoit :

parent_directory
investigation_name
user_data

En cas dannulation :

parent_directory == NULL
investigation_name == NULL

Les chaînes transmises au callback ne restent valides que pendant lappel.

Le callback doit les copier sil souhaite les conserver.


3. Concevoir le dialogue GTK

Le dialogue doit contenir au minimum :

Titre : Nouvelle enquête

Dossier parent :
[ chemin sélectionné                     ] [ Parcourir ]

Nom de lenquête :
[                                             ]

[ Annuler ] [ Créer ]

Le bouton Créer doit être désactivé tant que :

aucun dossier parent valide nest sélectionné
ou
le nom est vide

Le dialogue peut utiliser :

GtkWindow
GtkBox
GtkLabel
GtkEntry
GtkButton
GtkFileDialog

Ne pas utiliser les anciennes API GTK3 synchrones.


4. Sélectionner le dossier parent

Le bouton :

Parcourir

doit ouvrir un sélecteur de dossier GTK4.

Le dossier choisi représente le parent dans lequel le nouveau dossier denquête sera créé.

Exemple :

Dossier parent :
/home/fy59/Documents/Enquetes

Nom :
Arnaque_Billets

Résultat attendu :

/home/fy59/Documents/Enquetes/Arnaque_Billets

Le dossier parent doit déjà exister.


5. Valider le nom dans le dialogue

Le nom doit être refusé sil est :

NULL
vide
uniquement composé despaces

Il doit aussi être refusé sil contient un séparateur de chemin :

/

et, pour rester portable :

\

Exemples invalides :

Enquetes/Test
Enquetes\Test

Les espaces en début et fin doivent être supprimés avant lenvoi au callback.

Utiliser :

g_strstrip()

sur une copie allouée.

Le dialogue ne doit pas modifier directement le contenu interne de GtkEntry.


6. Ajouter le bouton dans MainWindow

Modifier :

include/views/main_window.h
src/views/main_window.c

Ajouter un bouton visible :

Nouvelle enquête

Il peut être placé dans une barre horizontale au-dessus du GtkPaned.

Organisation attendue :

MainWindow
└── main_box
    ├── action_bar
    │   └── bouton Nouvelle enquête
    ├── main_paned
    └── status_label

Ajouter dans la structure privée :

GtkWidget *action_bar;
GtkWidget *new_investigation_button;

7. Ajouter un callback de fenêtre

Définir dans :

include/views/main_window.h

un type de callback :

typedef void (*MainWindowNewInvestigationCallback)(
    gpointer user_data
);

Ajouter :

void main_window_set_new_investigation_callback(
    MainWindow *main_window,
    MainWindowNewInvestigationCallback callback,
    gpointer user_data
);

MainWindow ne doit pas créer elle-même lenquête.

Elle ne doit faire que transmettre le clic au contrôleur Application.


8. Conserver les données de callback

Dans la structure privée de MainWindow, ajouter :

MainWindowNewInvestigationCallback
    new_investigation_callback;

gpointer
    new_investigation_user_data;

Le bouton GTK doit être relié à un callback privé :

static void main_window_on_new_investigation_clicked(
    GtkButton *button,
    gpointer user_data
);

Ce callback doit appeler :

main_window->new_investigation_callback(
    main_window->new_investigation_user_data
);

uniquement si le callback est défini.


9. Ajouter le contrôleur dans Application

Modifier :

src/core/application.c

Ajouter :

static void application_on_new_investigation_requested(
    gpointer user_data
);

Cette fonction doit ouvrir :

create_investigation_dialog_present()

en utilisant :

main_window_get_window(
    application->main_window
);

10. Traiter le résultat du dialogue

Ajouter :

static void application_on_create_investigation(
    const char *parent_directory,
    const char *investigation_name,
    gpointer user_data
);

En cas dannulation :

parent_directory == NULL
investigation_name == NULL

la fonction doit simplement retourner.

Aucun état existant ne doit être modifié.


11. Créer le projet

Appeler :

char *created_root_path = NULL;

puis :

created_root_path = investigation_project_create(
    parent_directory,
    investigation_name
);

Si la création échoue :

ancienne session conservée
ancien arbre conservé
aucune modification de MainWindow
warning explicite

Exemple :

g_warning(
    "Impossible de créer l'enquête '%s' dans '%s'.",
    investigation_name,
    parent_directory
);

12. Ouvrir immédiatement la nouvelle enquête

Après une création valide, ouvrir :

InvestigationSession *new_session = NULL;
GError *error = NULL;

avec :

new_session = investigation_session_open(
    created_root_path,
    &error
);

La nouvelle enquête doit être utilisable sans redémarrer lapplication.


13. Construire son arbre

Récupérer :

const InvestigationProject *project = NULL;
const char *root_path = NULL;

Puis construire :

InvestigationTreeModel *new_tree_model = NULL;

avec :

new_tree_model = investigation_tree_builder_build(
    root_path
);

14. Factoriser linstallation dune session

Le ticket #029 contient déjà une logique de remplacement dans :

application_on_folder_selected()

Cette logique ne doit pas être dupliquée.

Créer une fonction privée :

static gboolean application_install_session(
    Application *application,
    InvestigationSession *new_session,
    InvestigationTreeModel *new_tree_model
);

Cette fonction doit :

  1. valider ses paramètres ;
  2. récupérer le projet ;
  3. récupérer le record ;
  4. récupérer le chemin racine ;
  5. récupérer le nom ;
  6. libérer lancien arbre ;
  7. fermer lancienne session ;
  8. installer les nouveaux objets ;
  9. mettre à jour la sidebar ;
  10. mettre à jour le titre et la barre détat.

Elle retourne :

TRUE en cas de succès
FALSE en cas déchec

15. Propriété des objets dans la fonction factorisée

Avant lappel réussi à :

application_install_session()

le code appelant possède :

new_session
new_tree_model

En cas de succès, Application devient propriétaire des deux objets.

En cas déchec, le code appelant reste propriétaire et doit les libérer.

Cette règle doit être documentée clairement.


16. Adapter louverture existante

Modifier :

application_on_folder_selected()

pour utiliser également :

application_install_session()

Le flux devient :

investigation_session_open()
        ↓
investigation_tree_builder_build()
        ↓
application_install_session()

Cela garantit que louverture et la création utilisent exactement le même mécanisme dinstallation.


17. Gérer un échec après création du projet

Si le dossier et la base ont été créés avec succès mais que :

investigation_session_open()

ou :

investigation_tree_builder_build()

échouent, ne pas supprimer automatiquement le nouveau projet.

Raison :

la création SQLite a pu réussir
le dossier contient potentiellement déjà des informations utiles
une suppression automatique après création complète serait risquée

Afficher un warning explicite indiquant que le projet a été créé mais na pas pu être ouvert.

Exemple :

Lenquête a été créée dans '<chemin>', mais son ouverture a échoué.

Le chemin doit être laissé à lutilisateur pour diagnostic.


18. Mettre à jour le statut pendant la création

Optionnel mais recommandé :

Avant la création :

Création de lenquête…

Après succès :

Enquête ouverte : <nom> — <chemin>

En cas déchec, le statut précédent doit être restauré ou conservé.

Ne pas laisser le statut bloqué sur :

Création de lenquête…

si lopération échoue.


19. Ajouter une fonction de statut générique

Pour éviter que Application manipule directement GtkLabel, ajouter dans :

include/views/main_window.h
void main_window_set_status(
    MainWindow *main_window,
    const char *status_text
);

Implémenter dans :

src/views/main_window.c

La fonction doit :

  • accepter main_window == NULL ;
  • accepter status_text == NULL ;
  • afficher une chaîne sûre ;
  • utiliser gtk_label_set_text().

Pour status_text == NULL, afficher :

Aucune enquête ouverte

Tests manuels

20. Création valide

Lancer :

make
make run

Cliquer sur :

Nouvelle enquête

Sélectionner :

/home/fy59/Documents/Enquetes

Saisir par exemple :

Test_Enquete

Vérifier la création de :

/home/fy59/Documents/Enquetes/Test_Enquete/

Vérifier la présence de :

00_BaseDeDonnees/Enquete.sqlite
01_Preuves_Originales
02_Preuves_Traitees
03_Chronologie
04_Entites
05_Rapports
06_Exports
07_Notes
08_Sources
09_Hash

Vérifier aussi :

arborescence visible
titre mis à jour
barre détat mise à jour
aucune erreur SQLite

21. Nom vide

Laisser le nom vide.

Le bouton Créer doit rester désactivé.

Aucun dossier ne doit être créé.


22. Nom composé despaces

Saisir uniquement :


Le bouton Créer doit rester désactivé ou la validation doit refuser lopération.

Aucun dossier ne doit être créé.


23. Nom contenant un séparateur

Tester :

Test/Enquete

puis :

Test\Enquete

La création doit être refusée.


24. Dossier déjà existant

Créer une première fois :

Test_Enquete

Puis tenter de recréer le même nom dans le même dossier parent.

Vérifier :

aucun écrasement
aucune modification du projet existant
warning explicite
ancienne session conservée

25. Annulation du dialogue

Ouvrir le dialogue puis cliquer sur :

Annuler

Vérifier :

aucun crash
aucun dossier créé
ancienne session conservée
fenêtre toujours utilisable

26. Création après ouverture dune enquête

Lorsque laction « Nouvelle enquête » est disponible pendant une session active :

  1. ouvrir une enquête A ;
  2. créer une enquête B ;
  3. vérifier que B remplace A uniquement après création et ouverture complètes.

Si la création de B échoue, A doit rester active.


Gestion de la mémoire

Dialogue

Le dialogue possède :

sa fenêtre GTK
ses widgets
ses chaînes temporaires

Il doit être détruit après :

création
annulation
fermeture de la fenêtre

Application

Application possède après succès :

InvestigationSession
InvestigationTreeModel

Variables temporaires

Le callback de création possède temporairement :

created_root_path
new_session
new_tree_model
GError

Tous les chemins déchec doivent libérer les ressources quils possèdent encore.


Critères dacceptation

  • Un bouton Nouvelle enquête est visible.
  • Le bouton ouvre un dialogue dédié.
  • Le dialogue permet de sélectionner un dossier parent.
  • Le dialogue permet de saisir un nom.
  • Le nom vide est refusé.
  • Le nom composé despaces est refusé.
  • Les séparateurs / et \ sont refusés.
  • Le bouton Créer nest actif que lorsque les données sont valides.
  • La création utilise investigation_project_create().
  • La nouvelle enquête est ouverte avec investigation_session_open().
  • Larborescence est construite automatiquement.
  • La session est installée dans Application.
  • Le nom et le chemin sont affichés.
  • Lancienne session est conservée en cas déchec.
  • Lancien arbre est conservé en cas déchec.
  • La logique dinstallation nest pas dupliquée.
  • Le dialogue ne connaît ni SQLite ni Database.
  • Aucun dossier existant nest écrasé.
  • Lannulation ne modifie aucun état.
  • Les anciens tests restent valides.
  • make réussit sans warning.
  • make test réussit.
  • Le test manuel de création valide réussit.
  • git diff --check ne retourne aucune erreur.

Audit attendu

Le dialogue ne doit contenir aucune dépendance métier :

rg -n \
    'sqlite3_|database_|investigation_project_create|investigation_session_open' \
    include/views/create_investigation_dialog.h \
    src/views/create_investigation_dialog.c

Résultat attendu :

aucune sortie

Vérifier la factorisation :

rg -n \
    'application_install_session' \
    src/core/application.c

Vérifier que les deux flux lutilisent :

application_on_folder_selected
application_on_create_investigation

Vérifier labsence de SQLite dans Application :

rg -n \
    'sqlite3_|#include <sqlite3.h>' \
    src/core/application.c

Résultat attendu :

aucune sortie

Hors périmètre

Ce ticket ne doit pas ajouter :

  • une barre de menu complète ;
  • un raccourci clavier ;
  • la suppression dune enquête ;
  • le renommage dune enquête ;
  • le déplacement dune enquête ;
  • la restauration de la dernière enquête ;
  • une liste des enquêtes récentes ;
  • une confirmation de fermeture ;
  • limport de preuves ;
  • les DAO des preuves ;
  • une boîte derreur avancée ;
  • la gestion de modèles denquête personnalisés.

Fichiers principalement concernés

include/views/create_investigation_dialog.h
src/views/create_investigation_dialog.c

include/views/main_window.h
src/views/main_window.c

src/core/application.c

Le fichier suivant ne devrait pas nécessiter de modification :

include/core/application.h

Le Makefile de production détecte automatiquement le nouveau fichier .c avec :

SRC := $(shell find src -name "*.c")

Aucune nouvelle cible de test nest obligatoire pour ce ticket GTK.


Résultat attendu

À la fin du ticket, lutilisateur doit pouvoir lancer lapplication sans disposer dune enquête préalable.

Il doit pouvoir :

ouvrir lapplication
        ↓
cliquer sur « Nouvelle enquête »
        ↓
choisir ~/Documents/Enquetes
        ↓
saisir un nom
        ↓
créer lenquête
        ↓
voir immédiatement son arborescence

Cette fonctionnalité constitue la première opération complète utilisable depuis GTK.


Commit attendu

Avant le commit :

make clean
make
make test
git diff --check
git status --short

Préparer les fichiers :

git add \
    include/views/create_investigation_dialog.h \
    src/views/create_investigation_dialog.c \
    include/views/main_window.h \
    src/views/main_window.c \
    src/core/application.c

Contrôler :

git diff --cached --stat
git diff --cached

Créer le commit :

git commit -m "feat(ui): add investigation creation workflow"

Puis pousser après validation complète :

git push