From fa57acfe891b3803551c2f62a99a8cb74745588d Mon Sep 17 00:00:00 2001 From: grayTerminal-sh Date: Tue, 14 Jul 2026 18:44:43 +0200 Subject: [PATCH] feat(gui): add icons to investigation tree --- docs/tickets/closed/TICKET-019.md | 208 +++++++++++++++ labfy-investigation | Bin 98424 -> 104472 bytes src/widgets/investigation_tree_view.c | 354 +++++++++++++++++++++----- tests/test_investigation_node | Bin 25824 -> 0 bytes tests/test_investigation_tree_builder | Bin 22848 -> 0 bytes tests/test_investigation_tree_model | Bin 17368 -> 0 bytes 6 files changed, 504 insertions(+), 58 deletions(-) create mode 100644 docs/tickets/closed/TICKET-019.md delete mode 100755 tests/test_investigation_node delete mode 100755 tests/test_investigation_tree_builder delete mode 100755 tests/test_investigation_tree_model diff --git a/docs/tickets/closed/TICKET-019.md b/docs/tickets/closed/TICKET-019.md new file mode 100644 index 0000000..5612eef --- /dev/null +++ b/docs/tickets/closed/TICKET-019.md @@ -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 +``` diff --git a/labfy-investigation b/labfy-investigation index d827f497b380efc3429ff12d111b28a49692cda6..681950a73edc8e0256367b072c39d08913ee1e8b 100755 GIT binary patch delta 30987 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 z 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 Sz7j6tH%tCbHh(B$sZH| delta 27389 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 diff --git a/src/widgets/investigation_tree_view.c b/src/widgets/investigation_tree_view.c index e8090ca..f3b79e2 100644 --- a/src/widgets/investigation_tree_view.c +++ b/src/widgets/investigation_tree_view.c @@ -11,6 +11,7 @@ #include #include #include +#include /* * InvestigationTreeItem @@ -18,7 +19,7 @@ * * 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. */ @@ -58,6 +59,10 @@ static void investigation_tree_item_init( * @brief Crée un adaptateur GTK pour un nœud métier. * * 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( const InvestigationNode *node @@ -75,6 +80,11 @@ static InvestigationTreeItem *investigation_tree_item_new( NULL ); + if (item == NULL) + { + return NULL; + } + item->node = node; return item; @@ -82,6 +92,10 @@ static InvestigationTreeItem *investigation_tree_item_new( /** * @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( const InvestigationTreeItem *item @@ -108,6 +122,7 @@ struct InvestigationTreeView GtkSingleSelection *selection_model; const InvestigationTreeModel *tree_model; + InvestigationTreeViewSelectionCallback selection_callback; gpointer selection_user_data; }; @@ -121,8 +136,8 @@ struct InvestigationTreeView * @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. + * @return Une liste GTK contenant les enfants, ou NULL si le nœud ne possède + * aucun enfant. */ static GListModel *investigation_tree_view_create_children_model( gpointer item, @@ -154,7 +169,8 @@ static GListModel *investigation_tree_view_create_children_model( return NULL; } - children_count = investigation_node_get_children_count(node); + children_count = + investigation_node_get_children_count(node); if (children_count == 0) { @@ -165,6 +181,11 @@ static GListModel *investigation_tree_view_create_children_model( investigation_tree_item_get_type() ); + if (children_store == NULL) + { + return NULL; + } + for (size_t index = 0; index < children_count; ++index) { const InvestigationNode *child_node = NULL; @@ -180,7 +201,9 @@ static GListModel *investigation_tree_view_create_children_model( continue; } - child_item = investigation_tree_item_new(child_node); + child_item = investigation_tree_item_new( + child_node + ); 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. - * Nous pouvons donc libérer notre référence locale juste après. */ g_list_store_append( 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. * - * Une ligne contient : + * Structure créée : * * 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( GtkSignalListItemFactory *factory, @@ -217,14 +245,28 @@ static void investigation_tree_view_factory_setup( ) { GtkWidget *expander = NULL; + GtkWidget *row_box = NULL; + GtkWidget *image = NULL; GtkWidget *label = NULL; (void)factory; (void)user_data; expander = gtk_tree_expander_new(); + + row_box = gtk_box_new( + GTK_ORIENTATION_HORIZONTAL, + 6 + ); + + image = gtk_image_new(); label = gtk_label_new(NULL); + gtk_label_set_xalign( + GTK_LABEL(label), + 0.0f + ); + gtk_widget_set_halign( label, GTK_ALIGN_START @@ -235,9 +277,19 @@ static void investigation_tree_view_factory_setup( 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(expander), - label + row_box ); 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 - * clavier d'ouverture et de fermeture fonctionnent correctement. + * Le focus reste dans GtkTreeExpander afin que les raccourcis + * d'ouverture et de fermeture fonctionnent. */ gtk_list_item_set_focusable( 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. + * + * @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( GtkSignalListItemFactory *factory, @@ -267,8 +398,13 @@ static void investigation_tree_view_factory_bind( GtkTreeListRow *tree_row = NULL; InvestigationTreeItem *tree_item = NULL; const InvestigationNode *node = NULL; + const char *icon_name = NULL; + GtkWidget *expander = NULL; + GtkWidget *row_box = NULL; + GtkWidget *image = NULL; GtkWidget *label = NULL; + const char *node_name = NULL; (void)factory; @@ -283,43 +419,87 @@ static void investigation_tree_view_factory_bind( 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) { return; } - node = investigation_tree_item_get_node(tree_item); + node = investigation_tree_item_get_node( + tree_item + ); if (node == NULL) { return; } - expander = gtk_list_item_get_child(list_item); + expander = gtk_list_item_get_child( + list_item + ); if (expander == NULL) { return; } - label = gtk_tree_expander_get_child( + row_box = gtk_tree_expander_get_child( 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) { 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(expander), 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(label), 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. + * + * @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( GtkSignalListItemFactory *factory, @@ -336,27 +520,52 @@ static void investigation_tree_view_factory_unbind( ) { GtkWidget *expander = NULL; + GtkWidget *row_box = NULL; + GtkWidget *image = NULL; GtkWidget *label = NULL; (void)factory; (void)user_data; - expander = gtk_list_item_get_child(list_item); + expander = gtk_list_item_get_child( + list_item + ); if (expander == NULL) { return; } - label = gtk_tree_expander_get_child( + row_box = gtk_tree_expander_get_child( 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(expander), NULL ); + if (image != NULL) + { + gtk_image_clear( + GTK_IMAGE(image) + ); + } + if (label != NULL) { 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é. * - * 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 property_spec Propriété ayant déclenché la notification. * @param user_data Pointeur vers InvestigationTreeView. @@ -422,9 +628,10 @@ static void investigation_tree_view_on_selection_changed( return; } - selected_item = gtk_single_selection_get_selected_item( - GTK_SINGLE_SELECTION(selection_model) - ); + selected_item = + gtk_single_selection_get_selected_item( + GTK_SINGLE_SELECTION(selection_model) + ); if (selected_item == NULL) { @@ -436,9 +643,13 @@ static void investigation_tree_view_on_selection_changed( 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) { @@ -450,7 +661,9 @@ static void investigation_tree_view_on_selection_changed( return; } - node = investigation_tree_item_get_node(tree_item); + node = investigation_tree_item_get_node( + tree_item + ); investigation_tree_view_emit_selection( tree_view, @@ -460,6 +673,8 @@ static void investigation_tree_view_on_selection_changed( /** * @brief Retire le modèle GTK actuellement affiché. + * + * @param tree_view Vue à nettoyer. */ static void investigation_tree_view_clear_model( InvestigationTreeView *tree_view @@ -480,6 +695,7 @@ static void investigation_tree_view_clear_model( ); tree_view->tree_model = NULL; + investigation_tree_view_emit_selection( tree_view, NULL @@ -495,6 +711,11 @@ InvestigationTreeView *investigation_tree_view_new(void) 1 ); + if (tree_view == NULL) + { + return NULL; + } + tree_view->factory = GTK_LIST_ITEM_FACTORY( gtk_signal_list_item_factory_new() @@ -509,21 +730,27 @@ InvestigationTreeView *investigation_tree_view_new(void) g_signal_connect( tree_view->factory, "setup", - G_CALLBACK(investigation_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), + G_CALLBACK( + investigation_tree_view_factory_bind + ), NULL ); g_signal_connect( tree_view->factory, "unbind", - G_CALLBACK(investigation_tree_view_factory_unbind), + G_CALLBACK( + investigation_tree_view_factory_unbind + ), NULL ); @@ -538,7 +765,8 @@ InvestigationTreeView *investigation_tree_view_new(void) return NULL; } - tree_view->root_widget = gtk_scrolled_window_new(); + tree_view->root_widget = + gtk_scrolled_window_new(); if (tree_view->root_widget == NULL) { @@ -588,6 +816,7 @@ void investigation_tree_view_set_model( ) { const InvestigationNode *root_node = NULL; + InvestigationTreeItem *root_item = NULL; GListStore *root_store = NULL; GtkTreeListModel *gtk_tree_model = NULL; @@ -598,14 +827,19 @@ void investigation_tree_view_set_model( return; } - investigation_tree_view_clear_model(tree_view); + investigation_tree_view_clear_model( + tree_view + ); if (tree_model == NULL) { return; } - root_node = investigation_tree_model_get_root(tree_model); + root_node = + investigation_tree_model_get_root( + tree_model + ); if (root_node == NULL) { @@ -616,7 +850,14 @@ void investigation_tree_view_set_model( 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) { @@ -631,15 +872,6 @@ void investigation_tree_view_set_model( 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, @@ -654,12 +886,6 @@ void investigation_tree_view_set_model( 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( G_LIST_MODEL(gtk_tree_model) ); @@ -683,16 +909,23 @@ void investigation_tree_view_set_model( g_signal_connect( selection_model, "notify::selected-item", - G_CALLBACK(investigation_tree_view_on_selection_changed), + G_CALLBACK( + investigation_tree_view_on_selection_changed + ), tree_view ); - tree_view->selection_model = selection_model; - tree_view->tree_model = tree_model; + tree_view->selection_model = + selection_model; + + tree_view->tree_model = + tree_model; gtk_list_view_set_model( 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; } - tree_view->selection_callback = callback; - tree_view->selection_user_data = user_data; + tree_view->selection_callback = + callback; + + tree_view->selection_user_data = + user_data; } void investigation_tree_view_free( @@ -720,15 +956,17 @@ void investigation_tree_view_free( return; } - investigation_tree_view_clear_model(tree_view); + 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. + * root_widget et list_view appartiennent à l'arbre GTK + * une fois intégrés dans la fenêtre. */ g_free(tree_view); } diff --git a/tests/test_investigation_node b/tests/test_investigation_node deleted file mode 100755 index b7614efa3254dd41638e0c2b0d87f582c089bf9e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25824 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 diff --git a/tests/test_investigation_tree_builder b/tests/test_investigation_tree_builder deleted file mode 100755 index d6cd4844176447be928bb7a3bda21977ee4298de..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22848 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