feat(sidebar): add entity navigation
This commit is contained in:
parent
1fb3c71c3f
commit
ab6621e95f
8 changed files with 567 additions and 1 deletions
|
|
@ -130,6 +130,7 @@ Le socle actuel comprend notamment :
|
|||
- arborescence des fichiers ;
|
||||
- fenêtre principale GTK4 ;
|
||||
- barre latérale et espace de travail ;
|
||||
- navigation et recherche locale des entités dans la barre latérale ;
|
||||
- affichage graphique des erreurs ;
|
||||
- tâches asynchrones annulables ;
|
||||
- gestionnaire de tâches ;
|
||||
|
|
|
|||
|
|
@ -183,6 +183,21 @@ const EntityRecord *investigation_graph_view_get_selected_entity(
|
|||
const InvestigationGraphView *graph_view
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Sélectionne une entité du graphe par son UUID.
|
||||
*
|
||||
* La vue reste inchangée si l'identifiant est absent du graphe.
|
||||
*
|
||||
* @param graph_view Vue graphique à modifier.
|
||||
* @param entity_identifier UUID de l'entité.
|
||||
*
|
||||
* @return TRUE lorsque l'entité est trouvée et sélectionnée.
|
||||
*/
|
||||
gboolean investigation_graph_view_select_entity(
|
||||
InvestigationGraphView *graph_view,
|
||||
const char *entity_identifier
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Supprime la sélection courante.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include "core/investigation_tree_model.h"
|
||||
#include "widgets/investigation_tree_view.h"
|
||||
#include "widgets/evidence_category_model.h"
|
||||
#include "models/investigation_graph_model.h"
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
|
|
@ -34,6 +35,20 @@ typedef void (*SidebarEvidenceSelectionCallback)(
|
|||
gpointer user_data
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Callback appelé lorsqu'une entité est sélectionnée.
|
||||
*
|
||||
* L'identifiant est emprunté et valide uniquement pendant l'appel.
|
||||
* Une sélection vide transmet NULL.
|
||||
*
|
||||
* @param entity_identifier UUID de l'entité, ou NULL.
|
||||
* @param user_data Données privées fournies par l'appelant.
|
||||
*/
|
||||
typedef void (*SidebarEntitySelectionCallback)(
|
||||
const char *entity_identifier,
|
||||
gpointer user_data
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Crée un nouveau panneau de navigation latéral.
|
||||
*
|
||||
|
|
@ -113,6 +128,32 @@ void sidebar_set_evidence_selection_callback(
|
|||
gpointer user_data
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Installe les entités du graphe dans la barre latérale.
|
||||
*
|
||||
* La Sidebar emprunte graph_model. Passer NULL vide la liste.
|
||||
*
|
||||
* @param sidebar Barre latérale à actualiser.
|
||||
* @param graph_model Graphe emprunté, ou NULL.
|
||||
*/
|
||||
void sidebar_set_entity_model(
|
||||
Sidebar *sidebar,
|
||||
const InvestigationGraphModel *graph_model
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Définit le callback de sélection d'une entité.
|
||||
*
|
||||
* @param sidebar Barre latérale à configurer.
|
||||
* @param callback Callback facultatif.
|
||||
* @param user_data Données privées transmises au callback.
|
||||
*/
|
||||
void sidebar_set_entity_selection_callback(
|
||||
Sidebar *sidebar,
|
||||
SidebarEntitySelectionCallback callback,
|
||||
gpointer user_data
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Libère la structure d'encapsulation du panneau latéral.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -127,6 +127,21 @@ void workspace_set_selected_evidence(
|
|||
const EvidenceRecord *evidence_record
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Sélectionne une entité dans le graphe affiché.
|
||||
*
|
||||
* La sélection actualise également le volet de détails et le contexte OSINT.
|
||||
*
|
||||
* @param workspace Zone de travail.
|
||||
* @param entity_identifier UUID de l'entité à sélectionner.
|
||||
*
|
||||
* @return TRUE lorsque l'entité est trouvée et sélectionnée.
|
||||
*/
|
||||
gboolean workspace_select_graph_entity(
|
||||
Workspace *workspace,
|
||||
const char *entity_identifier
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Définit le callback de vérification de la preuve affichée.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -125,6 +125,29 @@ struct MainWindow
|
|||
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Ouvre dans le workspace l'entité choisie dans la sidebar.
|
||||
*/
|
||||
static void main_window_on_sidebar_entity_selected(
|
||||
const char *entity_identifier,
|
||||
gpointer user_data
|
||||
)
|
||||
{
|
||||
MainWindow *main_window = user_data;
|
||||
|
||||
if (main_window == NULL ||
|
||||
entity_identifier == NULL ||
|
||||
entity_identifier[0] == '\0')
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
workspace_select_graph_entity(
|
||||
main_window->workspace,
|
||||
entity_identifier
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Transmet la demande de création d'une enquête au contrôleur.
|
||||
*
|
||||
|
|
@ -663,6 +686,12 @@ MainWindow *main_window_new(
|
|||
return NULL;
|
||||
}
|
||||
|
||||
sidebar_set_entity_selection_callback(
|
||||
main_window->sidebar,
|
||||
main_window_on_sidebar_entity_selected,
|
||||
main_window
|
||||
);
|
||||
|
||||
workspace_set_verify_evidence_callback(
|
||||
main_window->workspace,
|
||||
main_window_on_verify_evidence_requested,
|
||||
|
|
@ -1040,6 +1069,11 @@ void main_window_set_graph_loading(
|
|||
FALSE
|
||||
);
|
||||
|
||||
sidebar_set_entity_model(
|
||||
main_window->sidebar,
|
||||
NULL
|
||||
);
|
||||
|
||||
workspace_set_graph_loading(
|
||||
main_window->workspace
|
||||
);
|
||||
|
|
@ -1062,6 +1096,11 @@ void main_window_set_graph(
|
|||
graph_layout
|
||||
);
|
||||
|
||||
sidebar_set_entity_model(
|
||||
main_window->sidebar,
|
||||
graph_model
|
||||
);
|
||||
|
||||
gtk_widget_set_sensitive(
|
||||
main_window->show_graph_button,
|
||||
graph_model != NULL
|
||||
|
|
@ -1083,6 +1122,11 @@ void main_window_set_graph_error(
|
|||
FALSE
|
||||
);
|
||||
|
||||
sidebar_set_entity_model(
|
||||
main_window->sidebar,
|
||||
NULL
|
||||
);
|
||||
|
||||
workspace_set_graph_error(
|
||||
main_window->workspace,
|
||||
message
|
||||
|
|
@ -1103,6 +1147,11 @@ void main_window_clear_graph(
|
|||
FALSE
|
||||
);
|
||||
|
||||
sidebar_set_entity_model(
|
||||
main_window->sidebar,
|
||||
NULL
|
||||
);
|
||||
|
||||
workspace_clear_graph(
|
||||
main_window->workspace
|
||||
);
|
||||
|
|
@ -1436,6 +1485,17 @@ void main_window_free(
|
|||
main_window->sidebar,
|
||||
NULL
|
||||
);
|
||||
|
||||
sidebar_set_entity_selection_callback(
|
||||
main_window->sidebar,
|
||||
NULL,
|
||||
NULL
|
||||
);
|
||||
|
||||
sidebar_set_entity_model(
|
||||
main_window->sidebar,
|
||||
NULL
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1526,4 +1586,3 @@ void main_window_free(
|
|||
main_window
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4257,6 +4257,40 @@ const EntityRecord *investigation_graph_view_get_selected_entity(
|
|||
return graph_view->selected_node->entity_record;
|
||||
}
|
||||
|
||||
gboolean investigation_graph_view_select_entity(
|
||||
InvestigationGraphView *graph_view,
|
||||
const char *entity_identifier
|
||||
)
|
||||
{
|
||||
InvestigationGraphNodeLayout *node_layout = NULL;
|
||||
|
||||
if (graph_view == NULL ||
|
||||
graph_view->node_layouts_by_identifier == NULL ||
|
||||
entity_identifier == NULL ||
|
||||
!g_uuid_string_is_valid(entity_identifier))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
node_layout = g_hash_table_lookup(
|
||||
graph_view->node_layouts_by_identifier,
|
||||
entity_identifier
|
||||
);
|
||||
|
||||
if (node_layout == NULL ||
|
||||
node_layout->kind != INVESTIGATION_GRAPH_NODE_KIND_ENTITY)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
investigation_graph_view_set_selected_node(
|
||||
graph_view,
|
||||
node_layout
|
||||
);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void investigation_graph_view_clear_selection(
|
||||
InvestigationGraphView *graph_view
|
||||
)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include "widgets/evidence_tree_view.h"
|
||||
|
||||
#include <glib.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* @brief Largeur initiale du panneau latéral.
|
||||
|
|
@ -27,6 +28,12 @@
|
|||
#define SIDEBAR_PAGE_EVIDENCE \
|
||||
"evidence"
|
||||
|
||||
/**
|
||||
* @brief Identifiant de la page contenant les entités.
|
||||
*/
|
||||
#define SIDEBAR_PAGE_ENTITY \
|
||||
"entity"
|
||||
|
||||
/**
|
||||
* @brief État vide de l'onglet Preuves.
|
||||
*/
|
||||
|
|
@ -61,6 +68,14 @@ struct Sidebar
|
|||
GtkWidget *evidence_empty_page;
|
||||
GtkWidget *evidence_empty_label;
|
||||
|
||||
GtkWidget *entity_page;
|
||||
GtkWidget *entity_search_entry;
|
||||
GtkWidget *entity_count_label;
|
||||
GtkWidget *entity_scrolled_window;
|
||||
GtkWidget *entity_list_box;
|
||||
GtkWidget *entity_empty_label;
|
||||
GtkWidget *entity_no_result_label;
|
||||
|
||||
InvestigationTreeView *tree_view;
|
||||
EvidenceTreeView *evidence_tree_view;
|
||||
|
||||
|
|
@ -68,8 +83,132 @@ struct Sidebar
|
|||
evidence_selection_callback;
|
||||
|
||||
gpointer evidence_selection_user_data;
|
||||
|
||||
SidebarEntitySelectionCallback
|
||||
entity_selection_callback;
|
||||
|
||||
gpointer entity_selection_user_data;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Filtre une ligne d'entité avec le texte de recherche courant.
|
||||
*/
|
||||
static gboolean sidebar_filter_entity_row(
|
||||
GtkListBoxRow *row,
|
||||
gpointer user_data
|
||||
)
|
||||
{
|
||||
Sidebar *sidebar = user_data;
|
||||
const EntityRecord *entity_record = NULL;
|
||||
const char *search_text = NULL;
|
||||
char *normalized_search = NULL;
|
||||
char *normalized_value = NULL;
|
||||
char *normalized_type = NULL;
|
||||
gboolean visible = TRUE;
|
||||
|
||||
if (sidebar == NULL || row == NULL)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
search_text = gtk_editable_get_text(
|
||||
GTK_EDITABLE(sidebar->entity_search_entry)
|
||||
);
|
||||
|
||||
if (search_text == NULL || search_text[0] == '\0')
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
entity_record = g_object_get_data(
|
||||
G_OBJECT(row),
|
||||
"entity-record"
|
||||
);
|
||||
|
||||
if (entity_record == NULL)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
normalized_search = g_utf8_casefold(search_text, -1);
|
||||
normalized_value = g_utf8_casefold(
|
||||
entity_record_get_value(entity_record),
|
||||
-1
|
||||
);
|
||||
normalized_type = g_utf8_casefold(
|
||||
entity_record_get_type_identifier(entity_record),
|
||||
-1
|
||||
);
|
||||
|
||||
visible = normalized_search != NULL &&
|
||||
((normalized_value != NULL &&
|
||||
strstr(normalized_value, normalized_search) != NULL) ||
|
||||
(normalized_type != NULL &&
|
||||
strstr(normalized_type, normalized_search) != NULL));
|
||||
|
||||
g_free(normalized_type);
|
||||
g_free(normalized_value);
|
||||
g_free(normalized_search);
|
||||
|
||||
return visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Réapplique le filtre local des entités.
|
||||
*/
|
||||
static void sidebar_on_entity_search_changed(
|
||||
GtkSearchEntry *search_entry,
|
||||
gpointer user_data
|
||||
)
|
||||
{
|
||||
Sidebar *sidebar = user_data;
|
||||
|
||||
(void) search_entry;
|
||||
|
||||
if (sidebar != NULL && sidebar->entity_list_box != NULL)
|
||||
{
|
||||
gtk_list_box_invalidate_filter(
|
||||
GTK_LIST_BOX(sidebar->entity_list_box)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Relaie la sélection provenant de la liste d'entités.
|
||||
*/
|
||||
static void sidebar_on_entity_row_selected(
|
||||
GtkListBox *list_box,
|
||||
GtkListBoxRow *row,
|
||||
gpointer user_data
|
||||
)
|
||||
{
|
||||
Sidebar *sidebar = user_data;
|
||||
const EntityRecord *entity_record = NULL;
|
||||
const char *entity_identifier = NULL;
|
||||
|
||||
(void) list_box;
|
||||
|
||||
if (sidebar == NULL ||
|
||||
sidebar->entity_selection_callback == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (row != NULL)
|
||||
{
|
||||
entity_record = g_object_get_data(
|
||||
G_OBJECT(row),
|
||||
"entity-record"
|
||||
);
|
||||
entity_identifier = entity_record_get_identifier(entity_record);
|
||||
}
|
||||
|
||||
sidebar->entity_selection_callback(
|
||||
entity_identifier,
|
||||
sidebar->entity_selection_user_data
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Relaie la sélection provenant de EvidenceTreeView.
|
||||
*/
|
||||
|
|
@ -585,6 +724,129 @@ Sidebar *sidebar_new(void)
|
|||
"Preuves"
|
||||
);
|
||||
|
||||
/*
|
||||
* Page Entités : recherche locale, compteur et liste sélectionnable.
|
||||
*/
|
||||
sidebar->entity_page = gtk_box_new(
|
||||
GTK_ORIENTATION_VERTICAL,
|
||||
8
|
||||
);
|
||||
sidebar->entity_search_entry = gtk_search_entry_new();
|
||||
sidebar->entity_count_label = gtk_label_new("0 entité");
|
||||
sidebar->entity_scrolled_window = gtk_scrolled_window_new();
|
||||
sidebar->entity_list_box = gtk_list_box_new();
|
||||
sidebar->entity_empty_label = gtk_label_new(
|
||||
"Aucune entité active dans cette enquête."
|
||||
);
|
||||
sidebar->entity_no_result_label = gtk_label_new(
|
||||
"Aucune entité ne correspond à la recherche."
|
||||
);
|
||||
|
||||
if (sidebar->entity_page == NULL ||
|
||||
sidebar->entity_search_entry == NULL ||
|
||||
sidebar->entity_count_label == NULL ||
|
||||
sidebar->entity_scrolled_window == NULL ||
|
||||
sidebar->entity_list_box == NULL ||
|
||||
sidebar->entity_empty_label == NULL ||
|
||||
sidebar->entity_no_result_label == NULL)
|
||||
{
|
||||
sidebar_free(sidebar);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gtk_widget_set_margin_start(sidebar->entity_search_entry, 8);
|
||||
gtk_widget_set_margin_end(sidebar->entity_search_entry, 8);
|
||||
gtk_search_entry_set_placeholder_text(
|
||||
GTK_SEARCH_ENTRY(sidebar->entity_search_entry),
|
||||
"Rechercher une entité"
|
||||
);
|
||||
gtk_widget_set_halign(
|
||||
sidebar->entity_count_label,
|
||||
GTK_ALIGN_START
|
||||
);
|
||||
gtk_widget_set_margin_start(sidebar->entity_count_label, 12);
|
||||
gtk_widget_add_css_class(
|
||||
sidebar->entity_count_label,
|
||||
"dim-label"
|
||||
);
|
||||
gtk_widget_set_hexpand(sidebar->entity_scrolled_window, TRUE);
|
||||
gtk_widget_set_vexpand(sidebar->entity_scrolled_window, TRUE);
|
||||
gtk_scrolled_window_set_policy(
|
||||
GTK_SCROLLED_WINDOW(sidebar->entity_scrolled_window),
|
||||
GTK_POLICY_NEVER,
|
||||
GTK_POLICY_AUTOMATIC
|
||||
);
|
||||
gtk_scrolled_window_set_has_frame(
|
||||
GTK_SCROLLED_WINDOW(sidebar->entity_scrolled_window),
|
||||
FALSE
|
||||
);
|
||||
gtk_widget_set_hexpand(sidebar->entity_list_box, TRUE);
|
||||
gtk_scrolled_window_set_child(
|
||||
GTK_SCROLLED_WINDOW(sidebar->entity_scrolled_window),
|
||||
sidebar->entity_list_box
|
||||
);
|
||||
gtk_list_box_set_selection_mode(
|
||||
GTK_LIST_BOX(sidebar->entity_list_box),
|
||||
GTK_SELECTION_SINGLE
|
||||
);
|
||||
gtk_list_box_set_filter_func(
|
||||
GTK_LIST_BOX(sidebar->entity_list_box),
|
||||
sidebar_filter_entity_row,
|
||||
sidebar,
|
||||
NULL
|
||||
);
|
||||
gtk_label_set_wrap(
|
||||
GTK_LABEL(sidebar->entity_no_result_label),
|
||||
TRUE
|
||||
);
|
||||
gtk_widget_set_margin_start(sidebar->entity_no_result_label, 20);
|
||||
gtk_widget_set_margin_end(sidebar->entity_no_result_label, 20);
|
||||
gtk_widget_set_margin_top(sidebar->entity_no_result_label, 20);
|
||||
gtk_list_box_set_placeholder(
|
||||
GTK_LIST_BOX(sidebar->entity_list_box),
|
||||
sidebar->entity_no_result_label
|
||||
);
|
||||
gtk_widget_set_halign(sidebar->entity_empty_label, GTK_ALIGN_CENTER);
|
||||
gtk_widget_set_valign(sidebar->entity_empty_label, GTK_ALIGN_CENTER);
|
||||
gtk_widget_set_vexpand(sidebar->entity_empty_label, TRUE);
|
||||
gtk_label_set_wrap(GTK_LABEL(sidebar->entity_empty_label), TRUE);
|
||||
|
||||
g_signal_connect(
|
||||
sidebar->entity_search_entry,
|
||||
"search-changed",
|
||||
G_CALLBACK(sidebar_on_entity_search_changed),
|
||||
sidebar
|
||||
);
|
||||
g_signal_connect(
|
||||
sidebar->entity_list_box,
|
||||
"row-selected",
|
||||
G_CALLBACK(sidebar_on_entity_row_selected),
|
||||
sidebar
|
||||
);
|
||||
|
||||
gtk_box_append(
|
||||
GTK_BOX(sidebar->entity_page),
|
||||
sidebar->entity_search_entry
|
||||
);
|
||||
gtk_box_append(
|
||||
GTK_BOX(sidebar->entity_page),
|
||||
sidebar->entity_count_label
|
||||
);
|
||||
gtk_box_append(
|
||||
GTK_BOX(sidebar->entity_page),
|
||||
sidebar->entity_scrolled_window
|
||||
);
|
||||
gtk_box_append(
|
||||
GTK_BOX(sidebar->entity_page),
|
||||
sidebar->entity_empty_label
|
||||
);
|
||||
gtk_stack_add_titled(
|
||||
GTK_STACK(sidebar->stack),
|
||||
sidebar->entity_page,
|
||||
SIDEBAR_PAGE_ENTITY,
|
||||
"Entités"
|
||||
);
|
||||
|
||||
/*
|
||||
* L'onglet Dossier reste sélectionné au démarrage.
|
||||
*/
|
||||
|
|
@ -751,6 +1013,126 @@ void sidebar_set_evidence_selection_callback(
|
|||
user_data;
|
||||
}
|
||||
|
||||
void sidebar_set_entity_model(
|
||||
Sidebar *sidebar,
|
||||
const InvestigationGraphModel *graph_model
|
||||
)
|
||||
{
|
||||
GPtrArray *entity_records = NULL;
|
||||
GtkWidget *child = NULL;
|
||||
guint entity_index = 0;
|
||||
char *count_text = NULL;
|
||||
|
||||
if (sidebar == NULL || sidebar->entity_list_box == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while ((child = gtk_widget_get_first_child(
|
||||
sidebar->entity_list_box
|
||||
)) != NULL)
|
||||
{
|
||||
gtk_list_box_remove(
|
||||
GTK_LIST_BOX(sidebar->entity_list_box),
|
||||
child
|
||||
);
|
||||
}
|
||||
|
||||
if (graph_model != NULL)
|
||||
{
|
||||
entity_records = investigation_graph_model_list_entities(
|
||||
graph_model,
|
||||
NULL
|
||||
);
|
||||
}
|
||||
|
||||
for (entity_index = 0;
|
||||
entity_records != NULL && entity_index < entity_records->len;
|
||||
entity_index++)
|
||||
{
|
||||
const EntityRecord *entity_record = g_ptr_array_index(
|
||||
entity_records,
|
||||
entity_index
|
||||
);
|
||||
GtkWidget *row = gtk_list_box_row_new();
|
||||
GtkWidget *content = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2);
|
||||
GtkWidget *value_label = gtk_label_new(
|
||||
entity_record_get_value(entity_record)
|
||||
);
|
||||
GtkWidget *type_label = gtk_label_new(
|
||||
entity_record_get_type_identifier(entity_record)
|
||||
);
|
||||
|
||||
if (row == NULL || content == NULL ||
|
||||
value_label == NULL || type_label == NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
gtk_widget_set_margin_start(content, 12);
|
||||
gtk_widget_set_margin_end(content, 12);
|
||||
gtk_widget_set_margin_top(content, 8);
|
||||
gtk_widget_set_margin_bottom(content, 8);
|
||||
gtk_label_set_xalign(GTK_LABEL(value_label), 0.0F);
|
||||
gtk_label_set_ellipsize(
|
||||
GTK_LABEL(value_label),
|
||||
PANGO_ELLIPSIZE_END
|
||||
);
|
||||
gtk_label_set_xalign(GTK_LABEL(type_label), 0.0F);
|
||||
gtk_widget_add_css_class(type_label, "dim-label");
|
||||
gtk_box_append(GTK_BOX(content), value_label);
|
||||
gtk_box_append(GTK_BOX(content), type_label);
|
||||
gtk_list_box_row_set_child(GTK_LIST_BOX_ROW(row), content);
|
||||
g_object_set_data(
|
||||
G_OBJECT(row),
|
||||
"entity-record",
|
||||
(gpointer) entity_record
|
||||
);
|
||||
gtk_list_box_append(
|
||||
GTK_LIST_BOX(sidebar->entity_list_box),
|
||||
row
|
||||
);
|
||||
}
|
||||
|
||||
count_text = g_strdup_printf(
|
||||
"%u %s",
|
||||
entity_records != NULL ? entity_records->len : 0U,
|
||||
entity_records != NULL && entity_records->len > 1U
|
||||
? "entités"
|
||||
: "entité"
|
||||
);
|
||||
gtk_label_set_text(
|
||||
GTK_LABEL(sidebar->entity_count_label),
|
||||
count_text != NULL ? count_text : "0 entité"
|
||||
);
|
||||
gtk_widget_set_visible(
|
||||
sidebar->entity_empty_label,
|
||||
entity_records == NULL || entity_records->len == 0U
|
||||
);
|
||||
gtk_widget_set_visible(
|
||||
sidebar->entity_scrolled_window,
|
||||
entity_records != NULL && entity_records->len > 0U
|
||||
);
|
||||
|
||||
g_free(count_text);
|
||||
g_clear_pointer(&entity_records, g_ptr_array_unref);
|
||||
}
|
||||
|
||||
void sidebar_set_entity_selection_callback(
|
||||
Sidebar *sidebar,
|
||||
SidebarEntitySelectionCallback callback,
|
||||
gpointer user_data
|
||||
)
|
||||
{
|
||||
if (sidebar == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
sidebar->entity_selection_callback = callback;
|
||||
sidebar->entity_selection_user_data = user_data;
|
||||
}
|
||||
|
||||
void sidebar_free(Sidebar *sidebar)
|
||||
{
|
||||
if (sidebar == NULL)
|
||||
|
|
@ -773,6 +1155,9 @@ void sidebar_free(Sidebar *sidebar)
|
|||
);
|
||||
}
|
||||
|
||||
sidebar->entity_selection_callback = NULL;
|
||||
sidebar->entity_selection_user_data = NULL;
|
||||
|
||||
/*
|
||||
* Les widgets GTK sont intégrés dans l'arbre de widgets de la fenêtre.
|
||||
* GTK gère leur destruction lorsque la fenêtre est détruite.
|
||||
|
|
|
|||
|
|
@ -2045,6 +2045,22 @@ void workspace_set_selected_evidence(
|
|||
);
|
||||
}
|
||||
|
||||
gboolean workspace_select_graph_entity(
|
||||
Workspace *workspace,
|
||||
const char *entity_identifier
|
||||
)
|
||||
{
|
||||
if (workspace == NULL || workspace->graph_view == NULL)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return investigation_graph_view_select_entity(
|
||||
workspace->graph_view,
|
||||
entity_identifier
|
||||
);
|
||||
}
|
||||
|
||||
void workspace_set_graph_loading(
|
||||
Workspace *workspace
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in a new issue