diff --git a/docs/tickets/closed/TICKET-011.md b/docs/tickets/closed/TICKET-011.md new file mode 100644 index 0000000..cc14b62 --- /dev/null +++ b/docs/tickets/closed/TICKET-011.md @@ -0,0 +1,240 @@ +# Ticket #011 + +## Titre + +Connecter le modèle d'arborescence à la `Sidebar`. + +--- + +## Objectif + +Relier l'enquête sélectionnée au panneau latéral. + +Après la sélection d'un dossier, l'application doit : + +1. créer l'objet `Investigation` ; +2. construire son `InvestigationTreeModel` ; +3. transmettre le modèle à la `Sidebar` ; +4. afficher temporairement le nom du nœud racine dans le panneau latéral. + +Ce ticket valide la communication entre le Core et l'interface graphique. + +--- + +## Architecture + +```text +FolderDialog + │ + ▼ +Application + │ + ├── Investigation + │ + └── InvestigationTreeModel + │ + ▼ + Sidebar +``` + +Le module `Application` reste responsable de la coordination. + +La `Sidebar` ne parcourt jamais directement le système de fichiers. + +--- + +## Responsabilités + +### Application + +Le module `Application` doit : + +- recevoir le dossier sélectionné ; +- créer l'objet `Investigation` ; +- construire le modèle avec `InvestigationTreeBuilder` ; +- conserver le modèle pendant toute la durée de l'enquête ; +- transmettre le modèle à `MainWindow` ; +- libérer l'ancien modèle avant d'en ouvrir un nouveau ; +- libérer le modèle à la fermeture de l'application. + +### MainWindow + +Le module `MainWindow` doit : + +- recevoir un modèle d'arborescence ; +- le transmettre au composant `Sidebar`. + +### Sidebar + +Le module `Sidebar` doit : + +- recevoir un `InvestigationTreeModel` en lecture seule ; +- lire le nœud racine ; +- afficher temporairement son nom dans le titre du panneau ; +- ne jamais détruire le modèle reçu. + +--- + +## Principe de propriété + +`Application` est propriétaire de : + +```text +Investigation +InvestigationTreeModel +MainWindow +``` + +La `Sidebar` reçoit uniquement une référence non propriétaire vers le modèle. + +Elle ne doit jamais appeler : + +```c +investigation_tree_model_free(tree_model); +``` + +Le modèle est libéré uniquement par `Application`. + +--- + +## Fichiers concernés + +```text +src/core/application.c + +include/views/main_window.h +src/views/main_window.c + +include/widgets/sidebar.h +src/widgets/sidebar.c +``` + +Aucun nouveau module n'est nécessaire. + +--- + +## Interfaces publiques à ajouter + +### MainWindow + +```c +void main_window_set_tree_model( + MainWindow *main_window, + const InvestigationTreeModel *tree_model +); +``` + +### Sidebar + +```c +void sidebar_set_tree_model( + Sidebar *sidebar, + const InvestigationTreeModel *tree_model +); +``` + +--- + +## Comportement attendu + +Avant l'ouverture d'une enquête, la `Sidebar` affiche : + +```text +Dossier d'enquête +``` + +Après la sélection du dossier : + +```text +Template +``` + +ou le nom réel du dossier racine sélectionné. + +À ce stade, les enfants ne sont pas encore affichés. + +--- + +## Hors périmètre + +Ce ticket ne doit pas : + +- afficher les enfants du nœud racine ; +- utiliser `GtkTreeListModel` ; +- créer un explorateur de fichiers complet ; +- permettre de sélectionner un nœud ; +- ouvrir un fichier ; +- rafraîchir automatiquement le modèle ; +- modifier le système de fichiers ; +- communiquer avec SQLite. + +--- + +## Gestion des erreurs + +Si la construction du modèle échoue : + +- l'application ne doit pas planter ; +- l'ancien modèle doit rester valide jusqu'à son remplacement explicite ; +- un message d'erreur doit être affiché dans le terminal ; +- la `Sidebar` ne doit recevoir aucun pointeur invalide. + +Si une nouvelle enquête est ouverte avec succès : + +1. créer la nouvelle enquête ; +2. construire le nouveau modèle ; +3. seulement ensuite libérer l'ancienne enquête et l'ancien modèle ; +4. installer les nouveaux objets. + +Cette séquence évite de perdre l'enquête actuellement ouverte en cas d'échec. + +--- + +## Contraintes techniques + +- C17 ; +- aucun état global ; +- aucune lecture du système de fichiers dans `Sidebar` ; +- aucun transfert de propriété vers `Sidebar` ; +- documentation Doxygen ; +- compilation sans warning ; +- absence de `Gtk-CRITICAL` ; +- respect des conventions de nommage. + +--- + +## Critères d'acceptation + +- [ ] Le projet compile sans warning. +- [ ] `make test` reste valide. +- [ ] La sélection d'un dossier construit un modèle. +- [ ] `Application` conserve le modèle. +- [ ] `MainWindow` transmet le modèle à `Sidebar`. +- [ ] La `Sidebar` affiche le nom du nœud racine. +- [ ] L'ouverture successive de deux dossiers fonctionne. +- [ ] L'ancien modèle est correctement libéré. +- [ ] Une erreur de construction ne provoque pas de crash. +- [ ] La fermeture de l'application libère le modèle. +- [ ] Aucun code de parcours du disque n'apparaît dans `Sidebar`. +- [ ] Aucun enfant n'est encore affiché. + +--- + +## Tests manuels + +1. Lancer l'application. +2. Sélectionner le dossier `Template`. +3. Vérifier que la `Sidebar` affiche `Template`. +4. Fermer l'application. +5. Vérifier l'absence de warning critique. +6. Ouvrir successivement deux dossiers différents. +7. Vérifier que le titre de la `Sidebar` est mis à jour. +8. Vérifier que `make test` reste entièrement valide. + +--- + +## Commit attendu + +```text +feat(gui): connect investigation model to sidebar +``` diff --git a/include/views/main_window.h b/include/views/main_window.h index e67c781..0d3e65f 100644 --- a/include/views/main_window.h +++ b/include/views/main_window.h @@ -6,6 +6,8 @@ #ifndef LABFY_INVESTIGATION_MAIN_WINDOW_H #define LABFY_INVESTIGATION_MAIN_WINDOW_H +#include "core/investigation_tree_model.h" + #include /** @@ -40,6 +42,20 @@ GtkWindow *main_window_get_window( const MainWindow *main_window ); +/** + * @brief Transmet un modèle d'arborescence à la fenêtre principale. + * + * La fenêtre principale ne devient pas propriétaire du modèle. + * Elle le transmet uniquement à la Sidebar. + * + * @param main_window Fenêtre principale à mettre à jour. + * @param tree_model Modèle d'arborescence en lecture seule. + */ +void main_window_set_tree_model( + MainWindow *main_window, + const InvestigationTreeModel *tree_model +); + /** * @brief Libère les ressources de la fenêtre. * diff --git a/include/widgets/sidebar.h b/include/widgets/sidebar.h index 65d08b7..8171f99 100644 --- a/include/widgets/sidebar.h +++ b/include/widgets/sidebar.h @@ -6,6 +6,8 @@ #ifndef LABFY_INVESTIGATION_SIDEBAR_H #define LABFY_INVESTIGATION_SIDEBAR_H +#include "core/investigation_tree_model.h" + #include /** @@ -37,6 +39,22 @@ GtkWidget *sidebar_get_widget( const Sidebar *sidebar ); +/** + * @brief Associe un modèle d'arborescence à la barre latérale. + * + * La barre latérale ne devient pas propriétaire du modèle. + * Le modèle doit rester valide pendant toute la durée de son utilisation. + * + * Pour ce ticket, seul le nom du nœud racine est affiché dans le titre. + * + * @param sidebar Barre latérale à mettre à jour. + * @param tree_model Modèle d'arborescence en lecture seule. + */ +void sidebar_set_tree_model( + Sidebar *sidebar, + const InvestigationTreeModel *tree_model +); + /** * @brief Libère la structure d'encapsulation du panneau latéral. * diff --git a/labfy-investigation b/labfy-investigation index 6a7dc5b..4674c74 100755 Binary files a/labfy-investigation and b/labfy-investigation differ diff --git a/src/core/application.c b/src/core/application.c index 5a16e74..d4c0243 100644 --- a/src/core/application.c +++ b/src/core/application.c @@ -7,6 +7,8 @@ #include "views/folder_dialog.h" #include "core/investigation.h" #include "views/main_window.h" +#include "core/investigation_tree_builder.h" +#include "core/investigation_tree_model.h" #include @@ -33,6 +35,7 @@ struct Application GtkApplication *gtk_application; MainWindow *main_window; Investigation *investigation; + InvestigationTreeModel *tree_model; }; /** @@ -51,17 +54,8 @@ static void application_on_folder_selected( ) { Application *application = user_data; - const char *root_path = NULL; - const char *database_path = NULL; - - /* - * Une éventuelle enquête précédemment ouverte doit être libérée avant - * d'en créer une nouvelle. - */ - investigation_free(application->investigation); - application->investigation = NULL; - - application->investigation = investigation_new(folder_path); + Investigation *new_investigation = NULL; + InvestigationTreeModel *new_tree_model = NULL; if (application == NULL) { @@ -74,23 +68,47 @@ static void application_on_folder_selected( return; } - if (application->investigation == NULL) + new_investigation = investigation_new(folder_path); + + if (new_investigation == NULL) { g_warning( "Impossible de créer l'enquête à partir du dossier sélectionné." ); return; } - root_path = investigation_get_root_path( - application->investigation - ); - database_path = investigation_get_database_path( - application->investigation + + new_tree_model = investigation_tree_builder_build( + investigation_get_root_path(new_investigation) ); - g_print("Dossier racine : %s\n", root_path); - g_print("Base de données : %s\n", database_path); + if (new_tree_model == NULL) + { + g_warning( + "Impossible de construire l'arborescence de l'enquête." + ); + + investigation_free(new_investigation); + return; + } + + /* + * Les nouveaux objets sont valides. + * On peut maintenant remplacer les anciens sans perdre l'enquête + * déjà ouverte en cas d'échec. + */ + investigation_tree_model_free(application->tree_model); + investigation_free(application->investigation); + + application->tree_model = new_tree_model; + application->investigation = new_investigation; + + main_window_set_tree_model( + application->main_window, + application->tree_model + ); } + static void application_on_activate( GtkApplication *gtk_application, gpointer user_data @@ -182,8 +200,8 @@ void application_free(Application *application) return; } + investigation_tree_model_free(application->tree_model); investigation_free(application->investigation); - main_window_free(application->main_window); if (application->gtk_application != NULL) diff --git a/src/views/main_window.c b/src/views/main_window.c index ee5d0f9..c1c00aa 100644 --- a/src/views/main_window.c +++ b/src/views/main_window.c @@ -268,6 +268,22 @@ GtkWindow *main_window_get_window( return main_window->window; } +void main_window_set_tree_model( + MainWindow *main_window, + const InvestigationTreeModel *tree_model +) +{ + if (main_window == NULL) + { + return; + } + + sidebar_set_tree_model( + main_window->sidebar, + tree_model + ); +} + void main_window_free(MainWindow *main_window) { if (main_window == NULL) diff --git a/src/widgets/sidebar.c b/src/widgets/sidebar.c index b8ed4a2..78f3651 100644 --- a/src/widgets/sidebar.c +++ b/src/widgets/sidebar.c @@ -4,6 +4,7 @@ ******************************************************************************/ #include "widgets/sidebar.h" +#include "core/investigation_node.h" #include @@ -97,6 +98,59 @@ GtkWidget *sidebar_get_widget( return sidebar->root_widget; } +void sidebar_set_tree_model( + Sidebar *sidebar, + const InvestigationTreeModel *tree_model +) +{ + const InvestigationNode *root_node = NULL; + const char *root_name = NULL; + + if (sidebar == NULL) + { + return; + } + + if (tree_model == NULL) + { + gtk_label_set_text( + GTK_LABEL(sidebar->title_label), + "Dossier d'enquête" + ); + + return; + } + + root_node = investigation_tree_model_get_root(tree_model); + + if (root_node == NULL) + { + gtk_label_set_text( + GTK_LABEL(sidebar->title_label), + "Dossier d'enquête" + ); + + return; + } + + root_name = investigation_node_get_name(root_node); + + if (root_name == NULL || root_name[0] == '\0') + { + gtk_label_set_text( + GTK_LABEL(sidebar->title_label), + "Dossier d'enquête" + ); + + return; + } + + gtk_label_set_text( + GTK_LABEL(sidebar->title_label), + root_name + ); +} + void sidebar_free(Sidebar *sidebar) { if (sidebar == NULL)