feat(gui): connect investigation model to sidebar
This commit is contained in:
parent
a0e6743ac8
commit
f071527d15
7 changed files with 382 additions and 20 deletions
240
docs/tickets/closed/TICKET-011.md
Normal file
240
docs/tickets/closed/TICKET-011.md
Normal file
|
|
@ -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
|
||||
```
|
||||
|
|
@ -6,6 +6,8 @@
|
|||
#ifndef LABFY_INVESTIGATION_MAIN_WINDOW_H
|
||||
#define LABFY_INVESTIGATION_MAIN_WINDOW_H
|
||||
|
||||
#include "core/investigation_tree_model.h"
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
/**
|
||||
|
|
@ -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.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@
|
|||
#ifndef LABFY_INVESTIGATION_SIDEBAR_H
|
||||
#define LABFY_INVESTIGATION_SIDEBAR_H
|
||||
|
||||
#include "core/investigation_tree_model.h"
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
/**
|
||||
|
|
@ -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.
|
||||
*
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -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 <gtk/gtk.h>
|
||||
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
******************************************************************************/
|
||||
|
||||
#include "widgets/sidebar.h"
|
||||
#include "core/investigation_node.h"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue