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
|
#ifndef LABFY_INVESTIGATION_MAIN_WINDOW_H
|
||||||
#define LABFY_INVESTIGATION_MAIN_WINDOW_H
|
#define LABFY_INVESTIGATION_MAIN_WINDOW_H
|
||||||
|
|
||||||
|
#include "core/investigation_tree_model.h"
|
||||||
|
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -40,6 +42,20 @@ GtkWindow *main_window_get_window(
|
||||||
const MainWindow *main_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.
|
* @brief Libère les ressources de la fenêtre.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@
|
||||||
#ifndef LABFY_INVESTIGATION_SIDEBAR_H
|
#ifndef LABFY_INVESTIGATION_SIDEBAR_H
|
||||||
#define LABFY_INVESTIGATION_SIDEBAR_H
|
#define LABFY_INVESTIGATION_SIDEBAR_H
|
||||||
|
|
||||||
|
#include "core/investigation_tree_model.h"
|
||||||
|
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -37,6 +39,22 @@ GtkWidget *sidebar_get_widget(
|
||||||
const Sidebar *sidebar
|
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.
|
* @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 "views/folder_dialog.h"
|
||||||
#include "core/investigation.h"
|
#include "core/investigation.h"
|
||||||
#include "views/main_window.h"
|
#include "views/main_window.h"
|
||||||
|
#include "core/investigation_tree_builder.h"
|
||||||
|
#include "core/investigation_tree_model.h"
|
||||||
|
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
|
@ -33,6 +35,7 @@ struct Application
|
||||||
GtkApplication *gtk_application;
|
GtkApplication *gtk_application;
|
||||||
MainWindow *main_window;
|
MainWindow *main_window;
|
||||||
Investigation *investigation;
|
Investigation *investigation;
|
||||||
|
InvestigationTreeModel *tree_model;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -51,17 +54,8 @@ static void application_on_folder_selected(
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
Application *application = user_data;
|
Application *application = user_data;
|
||||||
const char *root_path = NULL;
|
Investigation *new_investigation = NULL;
|
||||||
const char *database_path = NULL;
|
InvestigationTreeModel *new_tree_model = 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);
|
|
||||||
|
|
||||||
if (application == NULL)
|
if (application == NULL)
|
||||||
{
|
{
|
||||||
|
|
@ -74,23 +68,47 @@ static void application_on_folder_selected(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (application->investigation == NULL)
|
new_investigation = investigation_new(folder_path);
|
||||||
|
|
||||||
|
if (new_investigation == NULL)
|
||||||
{
|
{
|
||||||
g_warning(
|
g_warning(
|
||||||
"Impossible de créer l'enquête à partir du dossier sélectionné."
|
"Impossible de créer l'enquête à partir du dossier sélectionné."
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
root_path = investigation_get_root_path(
|
|
||||||
application->investigation
|
new_tree_model = investigation_tree_builder_build(
|
||||||
);
|
investigation_get_root_path(new_investigation)
|
||||||
database_path = investigation_get_database_path(
|
|
||||||
application->investigation
|
|
||||||
);
|
);
|
||||||
|
|
||||||
g_print("Dossier racine : %s\n", root_path);
|
if (new_tree_model == NULL)
|
||||||
g_print("Base de données : %s\n", database_path);
|
{
|
||||||
|
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(
|
static void application_on_activate(
|
||||||
GtkApplication *gtk_application,
|
GtkApplication *gtk_application,
|
||||||
gpointer user_data
|
gpointer user_data
|
||||||
|
|
@ -182,8 +200,8 @@ void application_free(Application *application)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
investigation_tree_model_free(application->tree_model);
|
||||||
investigation_free(application->investigation);
|
investigation_free(application->investigation);
|
||||||
|
|
||||||
main_window_free(application->main_window);
|
main_window_free(application->main_window);
|
||||||
|
|
||||||
if (application->gtk_application != NULL)
|
if (application->gtk_application != NULL)
|
||||||
|
|
|
||||||
|
|
@ -268,6 +268,22 @@ GtkWindow *main_window_get_window(
|
||||||
return main_window->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)
|
void main_window_free(MainWindow *main_window)
|
||||||
{
|
{
|
||||||
if (main_window == NULL)
|
if (main_window == NULL)
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
#include "widgets/sidebar.h"
|
#include "widgets/sidebar.h"
|
||||||
|
#include "core/investigation_node.h"
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
|
||||||
|
|
@ -97,6 +98,59 @@ GtkWidget *sidebar_get_widget(
|
||||||
return sidebar->root_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)
|
void sidebar_free(Sidebar *sidebar)
|
||||||
{
|
{
|
||||||
if (sidebar == NULL)
|
if (sidebar == NULL)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue