feat(gui): create investigation tree view
This commit is contained in:
parent
f071527d15
commit
d9ba83b683
5 changed files with 851 additions and 0 deletions
|
|
@ -696,6 +696,10 @@ Avant d'ajouter une bibliothèque externe, il convient de vérifier :
|
||||||
- si GTK ou GLib proposent déjà la fonctionnalité ;
|
- si GTK ou GLib proposent déjà la fonctionnalité ;
|
||||||
- si la nouvelle dépendance apporte un réel bénéfice.
|
- si la nouvelle dépendance apporte un réel bénéfice.
|
||||||
|
|
||||||
|
Labfy Investigation est un projet GTK4 natif.
|
||||||
|
Aucun nouveau code ne doit utiliser une API dépréciée ou héritée de GTK3.
|
||||||
|
Les nouveaux développements utilisent exclusivement les composants modernes de GTK4.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Licence
|
# Licence
|
||||||
|
|
|
||||||
167
docs/tickets/open/TICKET-012.md
Normal file
167
docs/tickets/open/TICKET-012.md
Normal file
|
|
@ -0,0 +1,167 @@
|
||||||
|
# Ticket #012
|
||||||
|
|
||||||
|
## Titre
|
||||||
|
|
||||||
|
Créer le composant `InvestigationTreeView`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Objectif
|
||||||
|
|
||||||
|
Créer un composant graphique spécialisé chargé d'afficher un
|
||||||
|
`InvestigationTreeModel` à l'aide des widgets modernes de GTK4.
|
||||||
|
|
||||||
|
Ce composant servira d'adaptateur entre le modèle métier et les widgets GTK.
|
||||||
|
|
||||||
|
À ce stade, il n'est pas encore intégré dans la `Sidebar`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```text
|
||||||
|
InvestigationTreeModel
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
InvestigationTreeView
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
GtkTreeListModel
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
GtkListView
|
||||||
|
```
|
||||||
|
|
||||||
|
Le composant appartient à la couche graphique.
|
||||||
|
|
||||||
|
Le Core ne dépend jamais de GTK.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Responsabilités
|
||||||
|
|
||||||
|
Le module doit :
|
||||||
|
|
||||||
|
- recevoir un `InvestigationTreeModel` ;
|
||||||
|
- créer un `GtkTreeListModel` ;
|
||||||
|
- créer un `GtkListView` ;
|
||||||
|
- utiliser un `GtkSignalListItemFactory` ;
|
||||||
|
- utiliser un `GtkTreeExpander` ;
|
||||||
|
- afficher le nom des nœuds ;
|
||||||
|
- permettre le développement et le repli des dossiers ;
|
||||||
|
- fournir un widget GTK réutilisable.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Hors périmètre
|
||||||
|
|
||||||
|
Ce ticket ne doit pas :
|
||||||
|
|
||||||
|
- intégrer le composant dans la Sidebar ;
|
||||||
|
- permettre la sélection d'un nœud ;
|
||||||
|
- afficher des icônes ;
|
||||||
|
- ouvrir des fichiers ;
|
||||||
|
- modifier l'arborescence ;
|
||||||
|
- communiquer avec SQLite ;
|
||||||
|
- rafraîchir automatiquement le modèle.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Principe de propriété
|
||||||
|
|
||||||
|
Le composant ne devient jamais propriétaire du
|
||||||
|
`InvestigationTreeModel`.
|
||||||
|
|
||||||
|
Il reçoit uniquement une référence.
|
||||||
|
|
||||||
|
En revanche, il est propriétaire de tous les objets GTK qu'il crée.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Fichiers concernés
|
||||||
|
|
||||||
|
```text
|
||||||
|
include/widgets/investigation_tree_view.h
|
||||||
|
src/widgets/investigation_tree_view.c
|
||||||
|
```
|
||||||
|
|
||||||
|
Aucun autre module ne doit être modifié.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Interface publique
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef struct InvestigationTreeView InvestigationTreeView;
|
||||||
|
```
|
||||||
|
|
||||||
|
```c
|
||||||
|
InvestigationTreeView *investigation_tree_view_new(void);
|
||||||
|
```
|
||||||
|
|
||||||
|
```c
|
||||||
|
GtkWidget *investigation_tree_view_get_widget(
|
||||||
|
const InvestigationTreeView *tree_view
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
```c
|
||||||
|
void investigation_tree_view_set_model(
|
||||||
|
InvestigationTreeView *tree_view,
|
||||||
|
const InvestigationTreeModel *tree_model
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
```c
|
||||||
|
void investigation_tree_view_free(
|
||||||
|
InvestigationTreeView *tree_view
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Dépendances GTK
|
||||||
|
|
||||||
|
Le composant doit utiliser exclusivement :
|
||||||
|
|
||||||
|
- GtkTreeListModel
|
||||||
|
- GtkListView
|
||||||
|
- GtkTreeExpander
|
||||||
|
- GtkSignalListItemFactory
|
||||||
|
|
||||||
|
Aucun widget GTK3.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Contraintes techniques
|
||||||
|
|
||||||
|
- C17
|
||||||
|
- GTK4 uniquement
|
||||||
|
- aucune variable globale
|
||||||
|
- aucune dépendance vers SQLite
|
||||||
|
- aucune lecture directe du système de fichiers
|
||||||
|
- documentation Doxygen
|
||||||
|
- compilation sans warning
|
||||||
|
- aucune fuite mémoire
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Critères d'acceptation
|
||||||
|
|
||||||
|
- [ ] Le projet compile.
|
||||||
|
- [ ] Le composant peut être créé.
|
||||||
|
- [ ] Le composant fournit un widget GTK.
|
||||||
|
- [ ] Un modèle peut être associé.
|
||||||
|
- [ ] Les dossiers peuvent être développés.
|
||||||
|
- [ ] Les fichiers apparaissent.
|
||||||
|
- [ ] Le composant ne possède pas le modèle.
|
||||||
|
- [ ] Aucun Gtk-CRITICAL.
|
||||||
|
- [ ] Aucun warning.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Commit attendu
|
||||||
|
|
||||||
|
```text
|
||||||
|
feat(gui): create investigation tree view
|
||||||
|
```
|
||||||
74
include/widgets/investigation_tree_view.h
Normal file
74
include/widgets/investigation_tree_view.h
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
/******************************************************************************
|
||||||
|
* @file investigation_tree_view.h
|
||||||
|
* @brief Interface publique de la vue arborescente d'une enquête.
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#ifndef LABFY_INVESTIGATION_INVESTIGATION_TREE_VIEW_H
|
||||||
|
#define LABFY_INVESTIGATION_INVESTIGATION_TREE_VIEW_H
|
||||||
|
|
||||||
|
#include "core/investigation_tree_model.h"
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Représentation opaque de la vue arborescente d'une enquête.
|
||||||
|
*
|
||||||
|
* La structure réelle est définie dans investigation_tree_view.c.
|
||||||
|
* Les autres modules manipulent uniquement un pointeur vers
|
||||||
|
* InvestigationTreeView.
|
||||||
|
*/
|
||||||
|
typedef struct InvestigationTreeView InvestigationTreeView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Crée une nouvelle vue arborescente vide.
|
||||||
|
*
|
||||||
|
* Aucun modèle métier n'est associé au composant lors de sa création.
|
||||||
|
*
|
||||||
|
* @return Une nouvelle vue, ou NULL si sa création échoue.
|
||||||
|
*/
|
||||||
|
InvestigationTreeView *investigation_tree_view_new(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Retourne le widget GTK racine de la vue arborescente.
|
||||||
|
*
|
||||||
|
* Le widget retourné appartient au module InvestigationTreeView.
|
||||||
|
* Le code appelant ne doit ni le détruire ni en libérer la référence.
|
||||||
|
*
|
||||||
|
* @param tree_view Vue arborescente à consulter.
|
||||||
|
*
|
||||||
|
* @return Le widget racine, ou NULL si tree_view vaut NULL.
|
||||||
|
*/
|
||||||
|
GtkWidget *investigation_tree_view_get_widget(
|
||||||
|
const InvestigationTreeView *tree_view
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Associe un modèle métier à la vue arborescente.
|
||||||
|
*
|
||||||
|
* La vue ne devient pas propriétaire du modèle. Le modèle doit rester valide
|
||||||
|
* pendant toute la durée de son affichage.
|
||||||
|
*
|
||||||
|
* Passer NULL retire le modèle actuel et vide la vue.
|
||||||
|
*
|
||||||
|
* @param tree_view Vue arborescente à mettre à jour.
|
||||||
|
* @param tree_model Modèle métier en lecture seule, ou NULL.
|
||||||
|
*/
|
||||||
|
void investigation_tree_view_set_model(
|
||||||
|
InvestigationTreeView *tree_view,
|
||||||
|
const InvestigationTreeModel *tree_model
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Libère la structure d'encapsulation de la vue.
|
||||||
|
*
|
||||||
|
* Cette fonction accepte NULL.
|
||||||
|
*
|
||||||
|
* Les objets GTK intégrés à l'arbre de widgets restent gérés par GTK.
|
||||||
|
*
|
||||||
|
* @param tree_view Vue arborescente à libérer.
|
||||||
|
*/
|
||||||
|
void investigation_tree_view_free(
|
||||||
|
InvestigationTreeView *tree_view
|
||||||
|
);
|
||||||
|
|
||||||
|
#endif
|
||||||
Binary file not shown.
606
src/widgets/investigation_tree_view.c
Normal file
606
src/widgets/investigation_tree_view.c
Normal file
|
|
@ -0,0 +1,606 @@
|
||||||
|
/******************************************************************************
|
||||||
|
* @file investigation_tree_view.c
|
||||||
|
* @brief Affichage GTK4 de l'arborescence d'une enquête.
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include "widgets/investigation_tree_view.h"
|
||||||
|
|
||||||
|
#include "core/investigation_node.h"
|
||||||
|
|
||||||
|
#include <gio/gio.h>
|
||||||
|
#include <glib-object.h>
|
||||||
|
#include <glib.h>
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* InvestigationTreeItem
|
||||||
|
* ---------------------
|
||||||
|
*
|
||||||
|
* GListStore exige des objets dérivés de GObject.
|
||||||
|
*
|
||||||
|
* Cette classe privée adapte donc un InvestigationNode à l'écosystème GTK.
|
||||||
|
* Elle ne possède jamais le nœud métier : elle conserve seulement son adresse.
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct _InvestigationTreeItem
|
||||||
|
{
|
||||||
|
GObject parent_instance;
|
||||||
|
|
||||||
|
const InvestigationNode *node;
|
||||||
|
} InvestigationTreeItem;
|
||||||
|
|
||||||
|
typedef struct _InvestigationTreeItemClass
|
||||||
|
{
|
||||||
|
GObjectClass parent_class;
|
||||||
|
} InvestigationTreeItemClass;
|
||||||
|
|
||||||
|
G_DEFINE_TYPE(
|
||||||
|
InvestigationTreeItem,
|
||||||
|
investigation_tree_item,
|
||||||
|
G_TYPE_OBJECT
|
||||||
|
)
|
||||||
|
|
||||||
|
static void investigation_tree_item_class_init(
|
||||||
|
InvestigationTreeItemClass *item_class
|
||||||
|
)
|
||||||
|
{
|
||||||
|
(void)item_class;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void investigation_tree_item_init(
|
||||||
|
InvestigationTreeItem *item
|
||||||
|
)
|
||||||
|
{
|
||||||
|
item->node = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Crée un adaptateur GTK pour un nœud métier.
|
||||||
|
*
|
||||||
|
* L'adaptateur ne devient pas propriétaire du nœud.
|
||||||
|
*/
|
||||||
|
static InvestigationTreeItem *investigation_tree_item_new(
|
||||||
|
const InvestigationNode *node
|
||||||
|
)
|
||||||
|
{
|
||||||
|
InvestigationTreeItem *item = NULL;
|
||||||
|
|
||||||
|
if (node == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
item = g_object_new(
|
||||||
|
investigation_tree_item_get_type(),
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
item->node = node;
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Retourne le nœud métier observé par l'adaptateur.
|
||||||
|
*/
|
||||||
|
static const InvestigationNode *investigation_tree_item_get_node(
|
||||||
|
const InvestigationTreeItem *item
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (item == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return item->node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @struct InvestigationTreeView
|
||||||
|
* @brief Représentation interne de la vue d'arborescence.
|
||||||
|
*/
|
||||||
|
struct InvestigationTreeView
|
||||||
|
{
|
||||||
|
GtkWidget *root_widget;
|
||||||
|
GtkWidget *list_view;
|
||||||
|
|
||||||
|
GtkListItemFactory *factory;
|
||||||
|
GtkSelectionModel *selection_model;
|
||||||
|
|
||||||
|
const InvestigationTreeModel *tree_model;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Crée une liste GTK contenant les enfants d'un nœud.
|
||||||
|
*
|
||||||
|
* Cette fonction est appelée par GtkTreeListModel lorsqu'un dossier doit
|
||||||
|
* être développé.
|
||||||
|
*
|
||||||
|
* @param item Adaptateur représentant le nœud concerné.
|
||||||
|
* @param user_data Données privées inutilisées.
|
||||||
|
*
|
||||||
|
* @return Une liste GTK contenant les enfants, ou NULL pour un fichier
|
||||||
|
* ou un dossier sans enfant.
|
||||||
|
*/
|
||||||
|
static GListModel *investigation_tree_view_create_children_model(
|
||||||
|
gpointer item,
|
||||||
|
gpointer user_data
|
||||||
|
)
|
||||||
|
{
|
||||||
|
InvestigationTreeItem *tree_item = item;
|
||||||
|
const InvestigationNode *node = NULL;
|
||||||
|
GListStore *children_store = NULL;
|
||||||
|
size_t children_count = 0;
|
||||||
|
|
||||||
|
(void)user_data;
|
||||||
|
|
||||||
|
if (tree_item == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
node = investigation_tree_item_get_node(tree_item);
|
||||||
|
|
||||||
|
if (node == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (investigation_node_get_type(node) !=
|
||||||
|
INVESTIGATION_NODE_DIRECTORY)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
children_count = investigation_node_get_children_count(node);
|
||||||
|
|
||||||
|
if (children_count == 0)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
children_store = g_list_store_new(
|
||||||
|
investigation_tree_item_get_type()
|
||||||
|
);
|
||||||
|
|
||||||
|
for (size_t index = 0; index < children_count; ++index)
|
||||||
|
{
|
||||||
|
const InvestigationNode *child_node = NULL;
|
||||||
|
InvestigationTreeItem *child_item = NULL;
|
||||||
|
|
||||||
|
child_node = investigation_node_get_child(
|
||||||
|
node,
|
||||||
|
index
|
||||||
|
);
|
||||||
|
|
||||||
|
if (child_node == NULL)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
child_item = investigation_tree_item_new(child_node);
|
||||||
|
|
||||||
|
if (child_item == NULL)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GListStore prend sa propre référence sur l'objet.
|
||||||
|
* Nous pouvons donc libérer notre référence locale juste après.
|
||||||
|
*/
|
||||||
|
g_list_store_append(
|
||||||
|
children_store,
|
||||||
|
child_item
|
||||||
|
);
|
||||||
|
|
||||||
|
g_object_unref(child_item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return G_LIST_MODEL(children_store);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Crée les widgets constituant une ligne de l'arborescence.
|
||||||
|
*
|
||||||
|
* Une ligne contient :
|
||||||
|
*
|
||||||
|
* GtkTreeExpander
|
||||||
|
* └── GtkLabel
|
||||||
|
*/
|
||||||
|
static void investigation_tree_view_factory_setup(
|
||||||
|
GtkSignalListItemFactory *factory,
|
||||||
|
GtkListItem *list_item,
|
||||||
|
gpointer user_data
|
||||||
|
)
|
||||||
|
{
|
||||||
|
GtkWidget *expander = NULL;
|
||||||
|
GtkWidget *label = NULL;
|
||||||
|
|
||||||
|
(void)factory;
|
||||||
|
(void)user_data;
|
||||||
|
|
||||||
|
expander = gtk_tree_expander_new();
|
||||||
|
label = gtk_label_new(NULL);
|
||||||
|
|
||||||
|
gtk_widget_set_halign(
|
||||||
|
label,
|
||||||
|
GTK_ALIGN_START
|
||||||
|
);
|
||||||
|
|
||||||
|
gtk_widget_set_hexpand(
|
||||||
|
label,
|
||||||
|
TRUE
|
||||||
|
);
|
||||||
|
|
||||||
|
gtk_tree_expander_set_child(
|
||||||
|
GTK_TREE_EXPANDER(expander),
|
||||||
|
label
|
||||||
|
);
|
||||||
|
|
||||||
|
gtk_list_item_set_child(
|
||||||
|
list_item,
|
||||||
|
expander
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Le focus doit appartenir au GtkTreeExpander afin que ses raccourcis
|
||||||
|
* clavier d'ouverture et de fermeture fonctionnent correctement.
|
||||||
|
*/
|
||||||
|
gtk_list_item_set_focusable(
|
||||||
|
list_item,
|
||||||
|
FALSE
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Relie une ligne GTK à un nœud métier.
|
||||||
|
*/
|
||||||
|
static void investigation_tree_view_factory_bind(
|
||||||
|
GtkSignalListItemFactory *factory,
|
||||||
|
GtkListItem *list_item,
|
||||||
|
gpointer user_data
|
||||||
|
)
|
||||||
|
{
|
||||||
|
GtkTreeListRow *tree_row = NULL;
|
||||||
|
InvestigationTreeItem *tree_item = NULL;
|
||||||
|
const InvestigationNode *node = NULL;
|
||||||
|
GtkWidget *expander = NULL;
|
||||||
|
GtkWidget *label = NULL;
|
||||||
|
const char *node_name = NULL;
|
||||||
|
|
||||||
|
(void)factory;
|
||||||
|
(void)user_data;
|
||||||
|
|
||||||
|
tree_row = GTK_TREE_LIST_ROW(
|
||||||
|
gtk_list_item_get_item(list_item)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (tree_row == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tree_item = gtk_tree_list_row_get_item(tree_row);
|
||||||
|
|
||||||
|
if (tree_item == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
node = investigation_tree_item_get_node(tree_item);
|
||||||
|
|
||||||
|
if (node == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
expander = gtk_list_item_get_child(list_item);
|
||||||
|
|
||||||
|
if (expander == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
label = gtk_tree_expander_get_child(
|
||||||
|
GTK_TREE_EXPANDER(expander)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (label == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
node_name = investigation_node_get_name(node);
|
||||||
|
|
||||||
|
gtk_tree_expander_set_list_row(
|
||||||
|
GTK_TREE_EXPANDER(expander),
|
||||||
|
tree_row
|
||||||
|
);
|
||||||
|
|
||||||
|
gtk_label_set_text(
|
||||||
|
GTK_LABEL(label),
|
||||||
|
node_name != NULL ? node_name : ""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Détache les données d'une ligne réutilisée par GTK.
|
||||||
|
*/
|
||||||
|
static void investigation_tree_view_factory_unbind(
|
||||||
|
GtkSignalListItemFactory *factory,
|
||||||
|
GtkListItem *list_item,
|
||||||
|
gpointer user_data
|
||||||
|
)
|
||||||
|
{
|
||||||
|
GtkWidget *expander = NULL;
|
||||||
|
GtkWidget *label = NULL;
|
||||||
|
|
||||||
|
(void)factory;
|
||||||
|
(void)user_data;
|
||||||
|
|
||||||
|
expander = gtk_list_item_get_child(list_item);
|
||||||
|
|
||||||
|
if (expander == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
label = gtk_tree_expander_get_child(
|
||||||
|
GTK_TREE_EXPANDER(expander)
|
||||||
|
);
|
||||||
|
|
||||||
|
gtk_tree_expander_set_list_row(
|
||||||
|
GTK_TREE_EXPANDER(expander),
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
if (label != NULL)
|
||||||
|
{
|
||||||
|
gtk_label_set_text(
|
||||||
|
GTK_LABEL(label),
|
||||||
|
""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Retire le modèle GTK actuellement affiché.
|
||||||
|
*/
|
||||||
|
static void investigation_tree_view_clear_model(
|
||||||
|
InvestigationTreeView *tree_view
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (tree_view == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gtk_list_view_set_model(
|
||||||
|
GTK_LIST_VIEW(tree_view->list_view),
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
g_clear_object(
|
||||||
|
&tree_view->selection_model
|
||||||
|
);
|
||||||
|
|
||||||
|
tree_view->tree_model = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
InvestigationTreeView *investigation_tree_view_new(void)
|
||||||
|
{
|
||||||
|
InvestigationTreeView *tree_view = NULL;
|
||||||
|
|
||||||
|
tree_view = g_new0(
|
||||||
|
InvestigationTreeView,
|
||||||
|
1
|
||||||
|
);
|
||||||
|
|
||||||
|
tree_view->factory =
|
||||||
|
GTK_LIST_ITEM_FACTORY(
|
||||||
|
gtk_signal_list_item_factory_new()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (tree_view->factory == NULL)
|
||||||
|
{
|
||||||
|
investigation_tree_view_free(tree_view);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_signal_connect(
|
||||||
|
tree_view->factory,
|
||||||
|
"setup",
|
||||||
|
G_CALLBACK(investigation_tree_view_factory_setup),
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
g_signal_connect(
|
||||||
|
tree_view->factory,
|
||||||
|
"bind",
|
||||||
|
G_CALLBACK(investigation_tree_view_factory_bind),
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
g_signal_connect(
|
||||||
|
tree_view->factory,
|
||||||
|
"unbind",
|
||||||
|
G_CALLBACK(investigation_tree_view_factory_unbind),
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
tree_view->list_view = gtk_list_view_new(
|
||||||
|
NULL,
|
||||||
|
g_object_ref(tree_view->factory)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (tree_view->list_view == NULL)
|
||||||
|
{
|
||||||
|
investigation_tree_view_free(tree_view);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
tree_view->root_widget = gtk_scrolled_window_new();
|
||||||
|
|
||||||
|
if (tree_view->root_widget == NULL)
|
||||||
|
{
|
||||||
|
investigation_tree_view_free(tree_view);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
gtk_widget_set_hexpand(
|
||||||
|
tree_view->root_widget,
|
||||||
|
TRUE
|
||||||
|
);
|
||||||
|
|
||||||
|
gtk_widget_set_vexpand(
|
||||||
|
tree_view->root_widget,
|
||||||
|
TRUE
|
||||||
|
);
|
||||||
|
|
||||||
|
gtk_scrolled_window_set_policy(
|
||||||
|
GTK_SCROLLED_WINDOW(tree_view->root_widget),
|
||||||
|
GTK_POLICY_AUTOMATIC,
|
||||||
|
GTK_POLICY_AUTOMATIC
|
||||||
|
);
|
||||||
|
|
||||||
|
gtk_scrolled_window_set_child(
|
||||||
|
GTK_SCROLLED_WINDOW(tree_view->root_widget),
|
||||||
|
tree_view->list_view
|
||||||
|
);
|
||||||
|
|
||||||
|
return tree_view;
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkWidget *investigation_tree_view_get_widget(
|
||||||
|
const InvestigationTreeView *tree_view
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (tree_view == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tree_view->root_widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
void investigation_tree_view_set_model(
|
||||||
|
InvestigationTreeView *tree_view,
|
||||||
|
const InvestigationTreeModel *tree_model
|
||||||
|
)
|
||||||
|
{
|
||||||
|
const InvestigationNode *root_node = NULL;
|
||||||
|
InvestigationTreeItem *root_item = NULL;
|
||||||
|
GListStore *root_store = NULL;
|
||||||
|
GtkTreeListModel *gtk_tree_model = NULL;
|
||||||
|
GtkNoSelection *selection_model = NULL;
|
||||||
|
|
||||||
|
if (tree_view == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
investigation_tree_view_clear_model(tree_view);
|
||||||
|
|
||||||
|
if (tree_model == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
root_node = investigation_tree_model_get_root(tree_model);
|
||||||
|
|
||||||
|
if (root_node == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
root_store = g_list_store_new(
|
||||||
|
investigation_tree_item_get_type()
|
||||||
|
);
|
||||||
|
|
||||||
|
root_item = investigation_tree_item_new(root_node);
|
||||||
|
|
||||||
|
if (root_item == NULL)
|
||||||
|
{
|
||||||
|
g_object_unref(root_store);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_list_store_append(
|
||||||
|
root_store,
|
||||||
|
root_item
|
||||||
|
);
|
||||||
|
|
||||||
|
g_object_unref(root_item);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* gtk_tree_list_model_new() prend possession de root_store.
|
||||||
|
*
|
||||||
|
* passthrough = FALSE :
|
||||||
|
* le modèle expose des GtkTreeListRow, nécessaires à GtkTreeExpander.
|
||||||
|
*
|
||||||
|
* autoexpand = FALSE :
|
||||||
|
* les dossiers commencent repliés.
|
||||||
|
*/
|
||||||
|
gtk_tree_model = gtk_tree_list_model_new(
|
||||||
|
G_LIST_MODEL(root_store),
|
||||||
|
FALSE,
|
||||||
|
FALSE,
|
||||||
|
investigation_tree_view_create_children_model,
|
||||||
|
NULL,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
if (gtk_tree_model == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GtkNoSelection satisfait l'interface GtkSelectionModel demandée
|
||||||
|
* par GtkListView, sans encore autoriser la sélection.
|
||||||
|
*
|
||||||
|
* Le constructeur prend possession de gtk_tree_model.
|
||||||
|
*/
|
||||||
|
selection_model = gtk_no_selection_new(
|
||||||
|
G_LIST_MODEL(gtk_tree_model)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (selection_model == NULL)
|
||||||
|
{
|
||||||
|
g_object_unref(gtk_tree_model);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tree_view->selection_model =
|
||||||
|
GTK_SELECTION_MODEL(selection_model);
|
||||||
|
|
||||||
|
tree_view->tree_model = tree_model;
|
||||||
|
|
||||||
|
gtk_list_view_set_model(
|
||||||
|
GTK_LIST_VIEW(tree_view->list_view),
|
||||||
|
tree_view->selection_model
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void investigation_tree_view_free(
|
||||||
|
InvestigationTreeView *tree_view
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (tree_view == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
investigation_tree_view_clear_model(tree_view);
|
||||||
|
|
||||||
|
g_clear_object(
|
||||||
|
&tree_view->factory
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* root_widget et list_view appartiennent à l'arbre de widgets GTK
|
||||||
|
* une fois le composant intégré à une fenêtre.
|
||||||
|
*/
|
||||||
|
g_free(tree_view);
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue