feat(gui): add icons to investigation tree
This commit is contained in:
parent
4ce822ee70
commit
fa57acfe89
6 changed files with 504 additions and 58 deletions
208
docs/tickets/closed/TICKET-019.md
Normal file
208
docs/tickets/closed/TICKET-019.md
Normal file
|
|
@ -0,0 +1,208 @@
|
||||||
|
# Ticket #019
|
||||||
|
|
||||||
|
## Titre
|
||||||
|
|
||||||
|
Afficher une icône selon le type de nœud dans l'arborescence.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Objectif
|
||||||
|
|
||||||
|
Améliorer la lisibilité de `InvestigationTreeView` en affichant une icône
|
||||||
|
devant chaque dossier ou fichier.
|
||||||
|
|
||||||
|
Les icônes doivent être déterminées uniquement dans la couche graphique.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```text
|
||||||
|
InvestigationNode
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
InvestigationTreeView
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
GtkImage + GtkLabel
|
||||||
|
```
|
||||||
|
|
||||||
|
Le Core fournit :
|
||||||
|
|
||||||
|
- le nom ;
|
||||||
|
- le chemin ;
|
||||||
|
- le type.
|
||||||
|
|
||||||
|
La vue choisit l'icône à afficher.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Responsabilités
|
||||||
|
|
||||||
|
### InvestigationTreeView
|
||||||
|
|
||||||
|
Le module doit :
|
||||||
|
|
||||||
|
- afficher une icône devant chaque nœud ;
|
||||||
|
- utiliser une icône de dossier pour les répertoires ;
|
||||||
|
- utiliser une icône générique pour les fichiers inconnus ;
|
||||||
|
- choisir certaines icônes spécialisées selon l'extension ;
|
||||||
|
- conserver le nom du nœud à côté de l'icône ;
|
||||||
|
- rester compatible avec `GtkTreeExpander`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Fichiers concernés
|
||||||
|
|
||||||
|
```text
|
||||||
|
src/widgets/investigation_tree_view.c
|
||||||
|
```
|
||||||
|
|
||||||
|
Éventuellement :
|
||||||
|
|
||||||
|
```text
|
||||||
|
include/widgets/investigation_tree_view.h
|
||||||
|
```
|
||||||
|
|
||||||
|
uniquement si la documentation doit être précisée.
|
||||||
|
|
||||||
|
Aucune modification du Core.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Extensions reconnues
|
||||||
|
|
||||||
|
Première version minimale :
|
||||||
|
|
||||||
|
```text
|
||||||
|
.sqlite .db
|
||||||
|
.jpg .jpeg .png .webp
|
||||||
|
.pdf
|
||||||
|
.txt .md .log
|
||||||
|
.csv
|
||||||
|
.zip .tar .gz .xz
|
||||||
|
.mp4 .mkv .avi .mov
|
||||||
|
```
|
||||||
|
|
||||||
|
Les extensions inconnues utilisent une icône de fichier générique.
|
||||||
|
|
||||||
|
La comparaison doit être insensible à la casse.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Icônes GTK attendues
|
||||||
|
|
||||||
|
Utiliser les noms d'icônes du thème système, par exemple :
|
||||||
|
|
||||||
|
```text
|
||||||
|
folder-symbolic
|
||||||
|
text-x-generic-symbolic
|
||||||
|
application-pdf-symbolic
|
||||||
|
image-x-generic-symbolic
|
||||||
|
video-x-generic-symbolic
|
||||||
|
package-x-generic-symbolic
|
||||||
|
x-office-spreadsheet-symbolic
|
||||||
|
folder-database-symbolic
|
||||||
|
```
|
||||||
|
|
||||||
|
Si une icône spécialisée n'est pas disponible dans le thème, GTK doit
|
||||||
|
retomber proprement sur une icône générique.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Structure visuelle d'une ligne
|
||||||
|
|
||||||
|
```text
|
||||||
|
GtkTreeExpander
|
||||||
|
└── GtkBox horizontal
|
||||||
|
├── GtkImage
|
||||||
|
└── GtkLabel
|
||||||
|
```
|
||||||
|
|
||||||
|
Le label continue d'afficher le nom du nœud.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Fonctions privées suggérées
|
||||||
|
|
||||||
|
```c
|
||||||
|
static const char *investigation_tree_view_get_icon_name(
|
||||||
|
const InvestigationNode *node
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
Cette fonction doit :
|
||||||
|
|
||||||
|
- retourner `folder-symbolic` pour un dossier ;
|
||||||
|
- inspecter l'extension pour un fichier ;
|
||||||
|
- retourner une icône générique si aucune extension ne correspond.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Hors périmètre
|
||||||
|
|
||||||
|
Ce ticket ne doit pas :
|
||||||
|
|
||||||
|
- charger des miniatures ;
|
||||||
|
- analyser le contenu réel des fichiers ;
|
||||||
|
- lire les types MIME ;
|
||||||
|
- modifier le Core ;
|
||||||
|
- ouvrir les fichiers ;
|
||||||
|
- colorer les lignes ;
|
||||||
|
- ajouter des badges ;
|
||||||
|
- ajouter un menu contextuel.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Contraintes techniques
|
||||||
|
|
||||||
|
- GTK4 uniquement ;
|
||||||
|
- utiliser `GtkImage` ;
|
||||||
|
- utiliser les icônes du thème système ;
|
||||||
|
- aucun fichier d'icône embarqué ;
|
||||||
|
- aucun état global ;
|
||||||
|
- aucune lecture directe du système de fichiers ;
|
||||||
|
- compilation sans warning ;
|
||||||
|
- aucun `Gtk-CRITICAL`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Critères d'acceptation
|
||||||
|
|
||||||
|
- [ ] Le projet compile sans warning.
|
||||||
|
- [ ] `make test` reste valide.
|
||||||
|
- [ ] Les dossiers affichent une icône de dossier.
|
||||||
|
- [ ] Les fichiers génériques affichent une icône de fichier.
|
||||||
|
- [ ] Les PDF ont une icône dédiée.
|
||||||
|
- [ ] Les images ont une icône dédiée.
|
||||||
|
- [ ] Les vidéos ont une icône dédiée.
|
||||||
|
- [ ] Les archives ont une icône dédiée.
|
||||||
|
- [ ] Les bases SQLite ont une icône dédiée.
|
||||||
|
- [ ] Le nom du nœud reste visible.
|
||||||
|
- [ ] Le développement et le repli fonctionnent toujours.
|
||||||
|
- [ ] La sélection fonctionne toujours.
|
||||||
|
- [ ] Aucun code Core n'est modifié.
|
||||||
|
- [ ] Aucun `Gtk-CRITICAL`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tests manuels
|
||||||
|
|
||||||
|
1. Ouvrir une enquête contenant plusieurs types de fichiers.
|
||||||
|
2. Vérifier l'icône des dossiers.
|
||||||
|
3. Vérifier une image.
|
||||||
|
4. Vérifier un PDF.
|
||||||
|
5. Vérifier une base SQLite.
|
||||||
|
6. Vérifier une archive.
|
||||||
|
7. Vérifier un fichier inconnu.
|
||||||
|
8. Développer et replier plusieurs branches.
|
||||||
|
9. Sélectionner plusieurs nœuds.
|
||||||
|
10. Lancer `make test`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Commit attendu
|
||||||
|
|
||||||
|
```text
|
||||||
|
feat(gui): add icons to investigation tree
|
||||||
|
```
|
||||||
Binary file not shown.
|
|
@ -11,6 +11,7 @@
|
||||||
#include <glib-object.h>
|
#include <glib-object.h>
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* InvestigationTreeItem
|
* InvestigationTreeItem
|
||||||
|
|
@ -18,7 +19,7 @@
|
||||||
*
|
*
|
||||||
* GListStore exige des objets dérivés de GObject.
|
* GListStore exige des objets dérivés de GObject.
|
||||||
*
|
*
|
||||||
* Cette classe privée adapte donc un InvestigationNode à l'écosystème GTK.
|
* Cette classe privée adapte un InvestigationNode à l'écosystème GTK.
|
||||||
* Elle ne possède jamais le nœud métier : elle conserve seulement son adresse.
|
* Elle ne possède jamais le nœud métier : elle conserve seulement son adresse.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
@ -58,6 +59,10 @@ static void investigation_tree_item_init(
|
||||||
* @brief Crée un adaptateur GTK pour un nœud métier.
|
* @brief Crée un adaptateur GTK pour un nœud métier.
|
||||||
*
|
*
|
||||||
* L'adaptateur ne devient pas propriétaire du nœud.
|
* L'adaptateur ne devient pas propriétaire du nœud.
|
||||||
|
*
|
||||||
|
* @param node Nœud métier observé.
|
||||||
|
*
|
||||||
|
* @return Un nouvel adaptateur, ou NULL.
|
||||||
*/
|
*/
|
||||||
static InvestigationTreeItem *investigation_tree_item_new(
|
static InvestigationTreeItem *investigation_tree_item_new(
|
||||||
const InvestigationNode *node
|
const InvestigationNode *node
|
||||||
|
|
@ -75,6 +80,11 @@ static InvestigationTreeItem *investigation_tree_item_new(
|
||||||
NULL
|
NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (item == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
item->node = node;
|
item->node = node;
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
|
|
@ -82,6 +92,10 @@ static InvestigationTreeItem *investigation_tree_item_new(
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Retourne le nœud métier observé par l'adaptateur.
|
* @brief Retourne le nœud métier observé par l'adaptateur.
|
||||||
|
*
|
||||||
|
* @param item Adaptateur à consulter.
|
||||||
|
*
|
||||||
|
* @return Le nœud observé, ou NULL.
|
||||||
*/
|
*/
|
||||||
static const InvestigationNode *investigation_tree_item_get_node(
|
static const InvestigationNode *investigation_tree_item_get_node(
|
||||||
const InvestigationTreeItem *item
|
const InvestigationTreeItem *item
|
||||||
|
|
@ -108,6 +122,7 @@ struct InvestigationTreeView
|
||||||
GtkSingleSelection *selection_model;
|
GtkSingleSelection *selection_model;
|
||||||
|
|
||||||
const InvestigationTreeModel *tree_model;
|
const InvestigationTreeModel *tree_model;
|
||||||
|
|
||||||
InvestigationTreeViewSelectionCallback selection_callback;
|
InvestigationTreeViewSelectionCallback selection_callback;
|
||||||
gpointer selection_user_data;
|
gpointer selection_user_data;
|
||||||
};
|
};
|
||||||
|
|
@ -121,8 +136,8 @@ struct InvestigationTreeView
|
||||||
* @param item Adaptateur représentant le nœud concerné.
|
* @param item Adaptateur représentant le nœud concerné.
|
||||||
* @param user_data Données privées inutilisées.
|
* @param user_data Données privées inutilisées.
|
||||||
*
|
*
|
||||||
* @return Une liste GTK contenant les enfants, ou NULL pour un fichier
|
* @return Une liste GTK contenant les enfants, ou NULL si le nœud ne possède
|
||||||
* ou un dossier sans enfant.
|
* aucun enfant.
|
||||||
*/
|
*/
|
||||||
static GListModel *investigation_tree_view_create_children_model(
|
static GListModel *investigation_tree_view_create_children_model(
|
||||||
gpointer item,
|
gpointer item,
|
||||||
|
|
@ -154,7 +169,8 @@ static GListModel *investigation_tree_view_create_children_model(
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
children_count = investigation_node_get_children_count(node);
|
children_count =
|
||||||
|
investigation_node_get_children_count(node);
|
||||||
|
|
||||||
if (children_count == 0)
|
if (children_count == 0)
|
||||||
{
|
{
|
||||||
|
|
@ -165,6 +181,11 @@ static GListModel *investigation_tree_view_create_children_model(
|
||||||
investigation_tree_item_get_type()
|
investigation_tree_item_get_type()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (children_store == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
for (size_t index = 0; index < children_count; ++index)
|
for (size_t index = 0; index < children_count; ++index)
|
||||||
{
|
{
|
||||||
const InvestigationNode *child_node = NULL;
|
const InvestigationNode *child_node = NULL;
|
||||||
|
|
@ -180,7 +201,9 @@ static GListModel *investigation_tree_view_create_children_model(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
child_item = investigation_tree_item_new(child_node);
|
child_item = investigation_tree_item_new(
|
||||||
|
child_node
|
||||||
|
);
|
||||||
|
|
||||||
if (child_item == NULL)
|
if (child_item == NULL)
|
||||||
{
|
{
|
||||||
|
|
@ -189,7 +212,6 @@ static GListModel *investigation_tree_view_create_children_model(
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* GListStore prend sa propre référence sur l'objet.
|
* 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(
|
g_list_store_append(
|
||||||
children_store,
|
children_store,
|
||||||
|
|
@ -205,10 +227,16 @@ static GListModel *investigation_tree_view_create_children_model(
|
||||||
/**
|
/**
|
||||||
* @brief Crée les widgets constituant une ligne de l'arborescence.
|
* @brief Crée les widgets constituant une ligne de l'arborescence.
|
||||||
*
|
*
|
||||||
* Une ligne contient :
|
* Structure créée :
|
||||||
*
|
*
|
||||||
* GtkTreeExpander
|
* GtkTreeExpander
|
||||||
* └── GtkLabel
|
* └── GtkBox horizontal
|
||||||
|
* ├── GtkImage
|
||||||
|
* └── GtkLabel
|
||||||
|
*
|
||||||
|
* @param factory Fabrique GTK.
|
||||||
|
* @param list_item Élément de liste à préparer.
|
||||||
|
* @param user_data Données privées inutilisées.
|
||||||
*/
|
*/
|
||||||
static void investigation_tree_view_factory_setup(
|
static void investigation_tree_view_factory_setup(
|
||||||
GtkSignalListItemFactory *factory,
|
GtkSignalListItemFactory *factory,
|
||||||
|
|
@ -217,14 +245,28 @@ static void investigation_tree_view_factory_setup(
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
GtkWidget *expander = NULL;
|
GtkWidget *expander = NULL;
|
||||||
|
GtkWidget *row_box = NULL;
|
||||||
|
GtkWidget *image = NULL;
|
||||||
GtkWidget *label = NULL;
|
GtkWidget *label = NULL;
|
||||||
|
|
||||||
(void)factory;
|
(void)factory;
|
||||||
(void)user_data;
|
(void)user_data;
|
||||||
|
|
||||||
expander = gtk_tree_expander_new();
|
expander = gtk_tree_expander_new();
|
||||||
|
|
||||||
|
row_box = gtk_box_new(
|
||||||
|
GTK_ORIENTATION_HORIZONTAL,
|
||||||
|
6
|
||||||
|
);
|
||||||
|
|
||||||
|
image = gtk_image_new();
|
||||||
label = gtk_label_new(NULL);
|
label = gtk_label_new(NULL);
|
||||||
|
|
||||||
|
gtk_label_set_xalign(
|
||||||
|
GTK_LABEL(label),
|
||||||
|
0.0f
|
||||||
|
);
|
||||||
|
|
||||||
gtk_widget_set_halign(
|
gtk_widget_set_halign(
|
||||||
label,
|
label,
|
||||||
GTK_ALIGN_START
|
GTK_ALIGN_START
|
||||||
|
|
@ -235,9 +277,19 @@ static void investigation_tree_view_factory_setup(
|
||||||
TRUE
|
TRUE
|
||||||
);
|
);
|
||||||
|
|
||||||
|
gtk_box_append(
|
||||||
|
GTK_BOX(row_box),
|
||||||
|
image
|
||||||
|
);
|
||||||
|
|
||||||
|
gtk_box_append(
|
||||||
|
GTK_BOX(row_box),
|
||||||
|
label
|
||||||
|
);
|
||||||
|
|
||||||
gtk_tree_expander_set_child(
|
gtk_tree_expander_set_child(
|
||||||
GTK_TREE_EXPANDER(expander),
|
GTK_TREE_EXPANDER(expander),
|
||||||
label
|
row_box
|
||||||
);
|
);
|
||||||
|
|
||||||
gtk_list_item_set_child(
|
gtk_list_item_set_child(
|
||||||
|
|
@ -246,8 +298,8 @@ static void investigation_tree_view_factory_setup(
|
||||||
);
|
);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Le focus doit appartenir au GtkTreeExpander afin que ses raccourcis
|
* Le focus reste dans GtkTreeExpander afin que les raccourcis
|
||||||
* clavier d'ouverture et de fermeture fonctionnent correctement.
|
* d'ouverture et de fermeture fonctionnent.
|
||||||
*/
|
*/
|
||||||
gtk_list_item_set_focusable(
|
gtk_list_item_set_focusable(
|
||||||
list_item,
|
list_item,
|
||||||
|
|
@ -255,8 +307,87 @@ static void investigation_tree_view_factory_setup(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *investigation_tree_view_get_icon_name(
|
||||||
|
const InvestigationNode *node
|
||||||
|
)
|
||||||
|
{
|
||||||
|
const char *name = NULL;
|
||||||
|
const char *extension = NULL;
|
||||||
|
|
||||||
|
if (node == NULL)
|
||||||
|
{
|
||||||
|
return "text-x-generic-symbolic";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (investigation_node_get_type(node) ==
|
||||||
|
INVESTIGATION_NODE_DIRECTORY)
|
||||||
|
{
|
||||||
|
return "folder-symbolic";
|
||||||
|
}
|
||||||
|
|
||||||
|
name = investigation_node_get_name(node);
|
||||||
|
|
||||||
|
if (name == NULL)
|
||||||
|
{
|
||||||
|
return "text-x-generic-symbolic";
|
||||||
|
}
|
||||||
|
|
||||||
|
extension = strrchr(name, '.');
|
||||||
|
|
||||||
|
if (extension == NULL)
|
||||||
|
{
|
||||||
|
return "text-x-generic-symbolic";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_ascii_strcasecmp(extension, ".sqlite") == 0 ||
|
||||||
|
g_ascii_strcasecmp(extension, ".db") == 0)
|
||||||
|
{
|
||||||
|
return "folder-database-symbolic";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_ascii_strcasecmp(extension, ".jpg") == 0 ||
|
||||||
|
g_ascii_strcasecmp(extension, ".jpeg") == 0 ||
|
||||||
|
g_ascii_strcasecmp(extension, ".png") == 0 ||
|
||||||
|
g_ascii_strcasecmp(extension, ".webp") == 0)
|
||||||
|
{
|
||||||
|
return "image-x-generic-symbolic";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_ascii_strcasecmp(extension, ".pdf") == 0)
|
||||||
|
{
|
||||||
|
return "application-pdf-symbolic";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_ascii_strcasecmp(extension, ".csv") == 0)
|
||||||
|
{
|
||||||
|
return "x-office-spreadsheet-symbolic";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_ascii_strcasecmp(extension, ".zip") == 0 ||
|
||||||
|
g_ascii_strcasecmp(extension, ".tar") == 0 ||
|
||||||
|
g_ascii_strcasecmp(extension, ".gz") == 0 ||
|
||||||
|
g_ascii_strcasecmp(extension, ".xz") == 0)
|
||||||
|
{
|
||||||
|
return "package-x-generic-symbolic";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_ascii_strcasecmp(extension, ".mp4") == 0 ||
|
||||||
|
g_ascii_strcasecmp(extension, ".mkv") == 0 ||
|
||||||
|
g_ascii_strcasecmp(extension, ".avi") == 0 ||
|
||||||
|
g_ascii_strcasecmp(extension, ".mov") == 0)
|
||||||
|
{
|
||||||
|
return "video-x-generic-symbolic";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "text-x-generic-symbolic";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Relie une ligne GTK à un nœud métier.
|
* @brief Relie une ligne GTK à un nœud métier.
|
||||||
|
*
|
||||||
|
* @param factory Fabrique GTK.
|
||||||
|
* @param list_item Élément de liste à remplir.
|
||||||
|
* @param user_data Données privées inutilisées.
|
||||||
*/
|
*/
|
||||||
static void investigation_tree_view_factory_bind(
|
static void investigation_tree_view_factory_bind(
|
||||||
GtkSignalListItemFactory *factory,
|
GtkSignalListItemFactory *factory,
|
||||||
|
|
@ -267,8 +398,13 @@ static void investigation_tree_view_factory_bind(
|
||||||
GtkTreeListRow *tree_row = NULL;
|
GtkTreeListRow *tree_row = NULL;
|
||||||
InvestigationTreeItem *tree_item = NULL;
|
InvestigationTreeItem *tree_item = NULL;
|
||||||
const InvestigationNode *node = NULL;
|
const InvestigationNode *node = NULL;
|
||||||
|
const char *icon_name = NULL;
|
||||||
|
|
||||||
GtkWidget *expander = NULL;
|
GtkWidget *expander = NULL;
|
||||||
|
GtkWidget *row_box = NULL;
|
||||||
|
GtkWidget *image = NULL;
|
||||||
GtkWidget *label = NULL;
|
GtkWidget *label = NULL;
|
||||||
|
|
||||||
const char *node_name = NULL;
|
const char *node_name = NULL;
|
||||||
|
|
||||||
(void)factory;
|
(void)factory;
|
||||||
|
|
@ -283,43 +419,87 @@ static void investigation_tree_view_factory_bind(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tree_item = gtk_tree_list_row_get_item(tree_row);
|
tree_item = gtk_tree_list_row_get_item(
|
||||||
|
tree_row
|
||||||
|
);
|
||||||
|
|
||||||
if (tree_item == NULL)
|
if (tree_item == NULL)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
node = investigation_tree_item_get_node(tree_item);
|
node = investigation_tree_item_get_node(
|
||||||
|
tree_item
|
||||||
|
);
|
||||||
|
|
||||||
if (node == NULL)
|
if (node == NULL)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
expander = gtk_list_item_get_child(list_item);
|
expander = gtk_list_item_get_child(
|
||||||
|
list_item
|
||||||
|
);
|
||||||
|
|
||||||
if (expander == NULL)
|
if (expander == NULL)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
label = gtk_tree_expander_get_child(
|
row_box = gtk_tree_expander_get_child(
|
||||||
GTK_TREE_EXPANDER(expander)
|
GTK_TREE_EXPANDER(expander)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (row_box == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
image = gtk_widget_get_first_child(
|
||||||
|
row_box
|
||||||
|
);
|
||||||
|
|
||||||
|
if (image == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
label = gtk_widget_get_next_sibling(
|
||||||
|
image
|
||||||
|
);
|
||||||
|
|
||||||
if (label == NULL)
|
if (label == NULL)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
node_name = investigation_node_get_name(node);
|
node_name = investigation_node_get_name(
|
||||||
|
node
|
||||||
|
);
|
||||||
|
|
||||||
|
icon_name = investigation_tree_view_get_icon_name(
|
||||||
|
node
|
||||||
|
);
|
||||||
|
|
||||||
gtk_tree_expander_set_list_row(
|
gtk_tree_expander_set_list_row(
|
||||||
GTK_TREE_EXPANDER(expander),
|
GTK_TREE_EXPANDER(expander),
|
||||||
tree_row
|
tree_row
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Pour l'instant l'image reste vide.
|
||||||
|
* Elle sera configurée dans la suite du ticket #019.
|
||||||
|
*/
|
||||||
|
gtk_image_set_from_icon_name(
|
||||||
|
GTK_IMAGE(image),
|
||||||
|
icon_name
|
||||||
|
);
|
||||||
|
|
||||||
|
gtk_image_set_pixel_size(
|
||||||
|
GTK_IMAGE(image),
|
||||||
|
16
|
||||||
|
);
|
||||||
|
|
||||||
gtk_label_set_text(
|
gtk_label_set_text(
|
||||||
GTK_LABEL(label),
|
GTK_LABEL(label),
|
||||||
node_name != NULL ? node_name : ""
|
node_name != NULL ? node_name : ""
|
||||||
|
|
@ -328,6 +508,10 @@ static void investigation_tree_view_factory_bind(
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Détache les données d'une ligne réutilisée par GTK.
|
* @brief Détache les données d'une ligne réutilisée par GTK.
|
||||||
|
*
|
||||||
|
* @param factory Fabrique GTK.
|
||||||
|
* @param list_item Élément de liste à nettoyer.
|
||||||
|
* @param user_data Données privées inutilisées.
|
||||||
*/
|
*/
|
||||||
static void investigation_tree_view_factory_unbind(
|
static void investigation_tree_view_factory_unbind(
|
||||||
GtkSignalListItemFactory *factory,
|
GtkSignalListItemFactory *factory,
|
||||||
|
|
@ -336,27 +520,52 @@ static void investigation_tree_view_factory_unbind(
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
GtkWidget *expander = NULL;
|
GtkWidget *expander = NULL;
|
||||||
|
GtkWidget *row_box = NULL;
|
||||||
|
GtkWidget *image = NULL;
|
||||||
GtkWidget *label = NULL;
|
GtkWidget *label = NULL;
|
||||||
|
|
||||||
(void)factory;
|
(void)factory;
|
||||||
(void)user_data;
|
(void)user_data;
|
||||||
|
|
||||||
expander = gtk_list_item_get_child(list_item);
|
expander = gtk_list_item_get_child(
|
||||||
|
list_item
|
||||||
|
);
|
||||||
|
|
||||||
if (expander == NULL)
|
if (expander == NULL)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
label = gtk_tree_expander_get_child(
|
row_box = gtk_tree_expander_get_child(
|
||||||
GTK_TREE_EXPANDER(expander)
|
GTK_TREE_EXPANDER(expander)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (row_box != NULL)
|
||||||
|
{
|
||||||
|
image = gtk_widget_get_first_child(
|
||||||
|
row_box
|
||||||
|
);
|
||||||
|
|
||||||
|
if (image != NULL)
|
||||||
|
{
|
||||||
|
label = gtk_widget_get_next_sibling(
|
||||||
|
image
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
gtk_tree_expander_set_list_row(
|
gtk_tree_expander_set_list_row(
|
||||||
GTK_TREE_EXPANDER(expander),
|
GTK_TREE_EXPANDER(expander),
|
||||||
NULL
|
NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (image != NULL)
|
||||||
|
{
|
||||||
|
gtk_image_clear(
|
||||||
|
GTK_IMAGE(image)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (label != NULL)
|
if (label != NULL)
|
||||||
{
|
{
|
||||||
gtk_label_set_text(
|
gtk_label_set_text(
|
||||||
|
|
@ -396,9 +605,6 @@ static void investigation_tree_view_emit_selection(
|
||||||
/**
|
/**
|
||||||
* @brief Réagit au changement de l'élément sélectionné.
|
* @brief Réagit au changement de l'élément sélectionné.
|
||||||
*
|
*
|
||||||
* GtkSingleSelection expose une GtkTreeListRow. Cette fonction récupère
|
|
||||||
* l'adaptateur privé contenu dans cette ligne, puis le nœud métier associé.
|
|
||||||
*
|
|
||||||
* @param selection_model Modèle GTK ayant changé de sélection.
|
* @param selection_model Modèle GTK ayant changé de sélection.
|
||||||
* @param property_spec Propriété ayant déclenché la notification.
|
* @param property_spec Propriété ayant déclenché la notification.
|
||||||
* @param user_data Pointeur vers InvestigationTreeView.
|
* @param user_data Pointeur vers InvestigationTreeView.
|
||||||
|
|
@ -422,9 +628,10 @@ static void investigation_tree_view_on_selection_changed(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
selected_item = gtk_single_selection_get_selected_item(
|
selected_item =
|
||||||
GTK_SINGLE_SELECTION(selection_model)
|
gtk_single_selection_get_selected_item(
|
||||||
);
|
GTK_SINGLE_SELECTION(selection_model)
|
||||||
|
);
|
||||||
|
|
||||||
if (selected_item == NULL)
|
if (selected_item == NULL)
|
||||||
{
|
{
|
||||||
|
|
@ -436,9 +643,13 @@ static void investigation_tree_view_on_selection_changed(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tree_row = GTK_TREE_LIST_ROW(selected_item);
|
tree_row = GTK_TREE_LIST_ROW(
|
||||||
|
selected_item
|
||||||
|
);
|
||||||
|
|
||||||
tree_item = gtk_tree_list_row_get_item(tree_row);
|
tree_item = gtk_tree_list_row_get_item(
|
||||||
|
tree_row
|
||||||
|
);
|
||||||
|
|
||||||
if (tree_item == NULL)
|
if (tree_item == NULL)
|
||||||
{
|
{
|
||||||
|
|
@ -450,7 +661,9 @@ static void investigation_tree_view_on_selection_changed(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
node = investigation_tree_item_get_node(tree_item);
|
node = investigation_tree_item_get_node(
|
||||||
|
tree_item
|
||||||
|
);
|
||||||
|
|
||||||
investigation_tree_view_emit_selection(
|
investigation_tree_view_emit_selection(
|
||||||
tree_view,
|
tree_view,
|
||||||
|
|
@ -460,6 +673,8 @@ static void investigation_tree_view_on_selection_changed(
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Retire le modèle GTK actuellement affiché.
|
* @brief Retire le modèle GTK actuellement affiché.
|
||||||
|
*
|
||||||
|
* @param tree_view Vue à nettoyer.
|
||||||
*/
|
*/
|
||||||
static void investigation_tree_view_clear_model(
|
static void investigation_tree_view_clear_model(
|
||||||
InvestigationTreeView *tree_view
|
InvestigationTreeView *tree_view
|
||||||
|
|
@ -480,6 +695,7 @@ static void investigation_tree_view_clear_model(
|
||||||
);
|
);
|
||||||
|
|
||||||
tree_view->tree_model = NULL;
|
tree_view->tree_model = NULL;
|
||||||
|
|
||||||
investigation_tree_view_emit_selection(
|
investigation_tree_view_emit_selection(
|
||||||
tree_view,
|
tree_view,
|
||||||
NULL
|
NULL
|
||||||
|
|
@ -495,6 +711,11 @@ InvestigationTreeView *investigation_tree_view_new(void)
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (tree_view == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
tree_view->factory =
|
tree_view->factory =
|
||||||
GTK_LIST_ITEM_FACTORY(
|
GTK_LIST_ITEM_FACTORY(
|
||||||
gtk_signal_list_item_factory_new()
|
gtk_signal_list_item_factory_new()
|
||||||
|
|
@ -509,21 +730,27 @@ InvestigationTreeView *investigation_tree_view_new(void)
|
||||||
g_signal_connect(
|
g_signal_connect(
|
||||||
tree_view->factory,
|
tree_view->factory,
|
||||||
"setup",
|
"setup",
|
||||||
G_CALLBACK(investigation_tree_view_factory_setup),
|
G_CALLBACK(
|
||||||
|
investigation_tree_view_factory_setup
|
||||||
|
),
|
||||||
NULL
|
NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
g_signal_connect(
|
g_signal_connect(
|
||||||
tree_view->factory,
|
tree_view->factory,
|
||||||
"bind",
|
"bind",
|
||||||
G_CALLBACK(investigation_tree_view_factory_bind),
|
G_CALLBACK(
|
||||||
|
investigation_tree_view_factory_bind
|
||||||
|
),
|
||||||
NULL
|
NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
g_signal_connect(
|
g_signal_connect(
|
||||||
tree_view->factory,
|
tree_view->factory,
|
||||||
"unbind",
|
"unbind",
|
||||||
G_CALLBACK(investigation_tree_view_factory_unbind),
|
G_CALLBACK(
|
||||||
|
investigation_tree_view_factory_unbind
|
||||||
|
),
|
||||||
NULL
|
NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -538,7 +765,8 @@ InvestigationTreeView *investigation_tree_view_new(void)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
tree_view->root_widget = gtk_scrolled_window_new();
|
tree_view->root_widget =
|
||||||
|
gtk_scrolled_window_new();
|
||||||
|
|
||||||
if (tree_view->root_widget == NULL)
|
if (tree_view->root_widget == NULL)
|
||||||
{
|
{
|
||||||
|
|
@ -588,6 +816,7 @@ void investigation_tree_view_set_model(
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
const InvestigationNode *root_node = NULL;
|
const InvestigationNode *root_node = NULL;
|
||||||
|
|
||||||
InvestigationTreeItem *root_item = NULL;
|
InvestigationTreeItem *root_item = NULL;
|
||||||
GListStore *root_store = NULL;
|
GListStore *root_store = NULL;
|
||||||
GtkTreeListModel *gtk_tree_model = NULL;
|
GtkTreeListModel *gtk_tree_model = NULL;
|
||||||
|
|
@ -598,14 +827,19 @@ void investigation_tree_view_set_model(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
investigation_tree_view_clear_model(tree_view);
|
investigation_tree_view_clear_model(
|
||||||
|
tree_view
|
||||||
|
);
|
||||||
|
|
||||||
if (tree_model == NULL)
|
if (tree_model == NULL)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
root_node = investigation_tree_model_get_root(tree_model);
|
root_node =
|
||||||
|
investigation_tree_model_get_root(
|
||||||
|
tree_model
|
||||||
|
);
|
||||||
|
|
||||||
if (root_node == NULL)
|
if (root_node == NULL)
|
||||||
{
|
{
|
||||||
|
|
@ -616,7 +850,14 @@ void investigation_tree_view_set_model(
|
||||||
investigation_tree_item_get_type()
|
investigation_tree_item_get_type()
|
||||||
);
|
);
|
||||||
|
|
||||||
root_item = investigation_tree_item_new(root_node);
|
if (root_store == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
root_item = investigation_tree_item_new(
|
||||||
|
root_node
|
||||||
|
);
|
||||||
|
|
||||||
if (root_item == NULL)
|
if (root_item == NULL)
|
||||||
{
|
{
|
||||||
|
|
@ -631,15 +872,6 @@ void investigation_tree_view_set_model(
|
||||||
|
|
||||||
g_object_unref(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(
|
gtk_tree_model = gtk_tree_list_model_new(
|
||||||
G_LIST_MODEL(root_store),
|
G_LIST_MODEL(root_store),
|
||||||
FALSE,
|
FALSE,
|
||||||
|
|
@ -654,12 +886,6 @@ void investigation_tree_view_set_model(
|
||||||
return;
|
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_single_selection_new(
|
selection_model = gtk_single_selection_new(
|
||||||
G_LIST_MODEL(gtk_tree_model)
|
G_LIST_MODEL(gtk_tree_model)
|
||||||
);
|
);
|
||||||
|
|
@ -683,16 +909,23 @@ void investigation_tree_view_set_model(
|
||||||
g_signal_connect(
|
g_signal_connect(
|
||||||
selection_model,
|
selection_model,
|
||||||
"notify::selected-item",
|
"notify::selected-item",
|
||||||
G_CALLBACK(investigation_tree_view_on_selection_changed),
|
G_CALLBACK(
|
||||||
|
investigation_tree_view_on_selection_changed
|
||||||
|
),
|
||||||
tree_view
|
tree_view
|
||||||
);
|
);
|
||||||
|
|
||||||
tree_view->selection_model = selection_model;
|
tree_view->selection_model =
|
||||||
tree_view->tree_model = tree_model;
|
selection_model;
|
||||||
|
|
||||||
|
tree_view->tree_model =
|
||||||
|
tree_model;
|
||||||
|
|
||||||
gtk_list_view_set_model(
|
gtk_list_view_set_model(
|
||||||
GTK_LIST_VIEW(tree_view->list_view),
|
GTK_LIST_VIEW(tree_view->list_view),
|
||||||
GTK_SELECTION_MODEL(tree_view->selection_model)
|
GTK_SELECTION_MODEL(
|
||||||
|
tree_view->selection_model
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -707,8 +940,11 @@ void investigation_tree_view_set_selection_callback(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tree_view->selection_callback = callback;
|
tree_view->selection_callback =
|
||||||
tree_view->selection_user_data = user_data;
|
callback;
|
||||||
|
|
||||||
|
tree_view->selection_user_data =
|
||||||
|
user_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void investigation_tree_view_free(
|
void investigation_tree_view_free(
|
||||||
|
|
@ -720,15 +956,17 @@ void investigation_tree_view_free(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
investigation_tree_view_clear_model(tree_view);
|
investigation_tree_view_clear_model(
|
||||||
|
tree_view
|
||||||
|
);
|
||||||
|
|
||||||
g_clear_object(
|
g_clear_object(
|
||||||
&tree_view->factory
|
&tree_view->factory
|
||||||
);
|
);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* root_widget et list_view appartiennent à l'arbre de widgets GTK
|
* root_widget et list_view appartiennent à l'arbre GTK
|
||||||
* une fois le composant intégré à une fenêtre.
|
* une fois intégrés dans la fenêtre.
|
||||||
*/
|
*/
|
||||||
g_free(tree_view);
|
g_free(tree_view);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in a new issue