labfy-investigation/src/widgets/workspace.c
2026-07-22 12:29:17 +02:00

2940 lines
62 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/******************************************************************************
* @file workspace.c
* @brief Implémentation de la zone principale de travail.
******************************************************************************/
#include "widgets/workspace.h"
#include "models/entity_record.h"
#include "models/investigation_graph_model.h"
#include "models/osint_selection_context.h"
#include "models/osint_action_catalog.h"
#include "models/relation_record.h"
#include "widgets/entity_details_panel.h"
#include "widgets/investigation_graph_view.h"
#include <glib.h>
#define WORKSPACE_PAGE_WELCOME "welcome"
#define WORKSPACE_PAGE_NODE_INFORMATION "node-information"
#define WORKSPACE_PAGE_EVIDENCE_INFORMATION \
"evidence-information"
#define WORKSPACE_PAGE_GRAPH_STATE \
"graph-state"
#define WORKSPACE_PAGE_GRAPH_VIEW \
"graph-view"
/**
* @brief État métier représenté par la page du graphe.
*/
typedef enum
{
WORKSPACE_GRAPH_STATE_EMPTY,
WORKSPACE_GRAPH_STATE_LOADING,
WORKSPACE_GRAPH_STATE_READY,
WORKSPACE_GRAPH_STATE_ERROR
} WorkspaceGraphState;
/**
* @struct Workspace
* @brief Représentation interne de la zone de travail.
*/
struct Workspace
{
GtkWidget *root_widget;
GtkWidget *stack;
GtkWidget *welcome_page;
GtkWidget *graph_state_page;
GtkWidget *graph_spinner;
GtkWidget *graph_title_label;
GtkWidget *graph_details_label;
GtkWidget *graph_view_page;
GtkWidget *graph_toolbar;
GtkWidget *reset_graph_layout_button;
GtkWidget *osint_tools_button;
GtkWidget *osint_tools_popover;
GtkWidget *osint_tools_context_label;
GtkWidget *osint_tools_status_label;
GtkWidget *osint_tools_actions_box;
GtkWidget *graph_help_button;
GtkWidget *graph_help_popover;
InvestigationGraphView *graph_view;
EntityDetailsPanel *entity_details_panel;
GtkWidget *relation_details_panel;
GtkWidget *relation_details_title_label;
GtkWidget *relation_details_summary_label;
GtkWidget *node_page;
GtkWidget *evidence_page;
GtkWidget *evidence_name_label;
GtkWidget *evidence_type_label;
GtkWidget *evidence_integrity_label;
GtkWidget *evidence_size_label;
GtkWidget *evidence_imported_at_label;
GtkWidget *evidence_collected_at_label;
GtkWidget *evidence_source_label;
GtkWidget *evidence_description_label;
GtkWidget *evidence_relative_path_label;
GtkWidget *evidence_internal_name_label;
GtkWidget *evidence_identifier_label;
GtkWidget *evidence_sha256_label;
GtkWidget *verify_evidence_button;
char *selected_evidence_identifier;
WorkspaceVerifyEvidenceCallback
verify_evidence_callback;
gpointer
verify_evidence_user_data;
WorkspaceGraphNodeMovedCallback
graph_node_moved_callback;
gpointer
graph_node_moved_user_data;
WorkspaceResetGraphLayoutCallback
reset_graph_layout_callback;
gpointer
reset_graph_layout_user_data;
WorkspaceAddRelationCallback
add_relation_callback;
gpointer
add_relation_user_data;
WorkspaceOsintActionCallback
osint_action_callback;
gpointer
osint_action_user_data;
GtkWidget *node_name_label;
GtkWidget *node_path_label;
GtkWidget *node_type_label;
GtkWidget *node_parent_label;
GtkWidget *node_children_label;
const InvestigationGraphModel *graph_model;
const InvestigationGraphLayout *graph_layout;
OsintSelectionContext *osint_selection_context;
OsintActionCatalog *osint_action_catalog;
WorkspaceGraphState graph_state;
};
/**
* @brief Traite localement l'aperçu ou relaie une action OSINT exécutable.
*/
static void workspace_on_osint_action_clicked(
GtkButton *button,
gpointer user_data
)
{
Workspace *workspace = user_data;
const char *action_identifier = g_object_get_data(
G_OBJECT(button),
"osint-action-identifier"
);
const char *target_value = NULL;
const char *target_identifier = NULL;
if (workspace == NULL || workspace->osint_tools_status_label == NULL)
{
return;
}
if (g_strcmp0(action_identifier, "selection-preview") == 0)
{
gtk_label_set_text(
GTK_LABEL(workspace->osint_tools_status_label),
"Aperçu local : aucun outil externe exécuté."
);
return;
}
target_value = osint_selection_context_get_value(
workspace->osint_selection_context
);
target_identifier = osint_selection_context_get_identifier(
workspace->osint_selection_context
);
if (workspace->osint_action_callback == NULL ||
action_identifier == NULL || target_identifier == NULL ||
target_value == NULL)
{
gtk_label_set_text(
GTK_LABEL(workspace->osint_tools_status_label),
"Impossible de transmettre cette action OSINT."
);
return;
}
gtk_label_set_text(
GTK_LABEL(workspace->osint_tools_status_label),
"Exécution lancée ; suivez sa progression dans les tâches."
);
gtk_popover_popdown(
GTK_POPOVER(workspace->osint_tools_popover)
);
workspace->osint_action_callback(
action_identifier,
target_identifier,
target_value,
workspace->osint_action_user_data
);
}
/**
* @brief Actualise le menu OSINT selon la sélection du graphe.
*
* Aucun outil n'est encore exposé par ce ticket. Le menu prépare le point
* d'entrée contextuel et explique explicitement cet état à l'utilisateur.
*/
static void workspace_update_osint_tools_menu(
Workspace *workspace,
const OsintSelectionContext *context
)
{
const char *selection_kind =
NULL;
const char *selection_identifier =
NULL;
char *context_text =
NULL;
GPtrArray *compatible_actions =
NULL;
GtkWidget *child =
NULL;
if (workspace == NULL ||
workspace->osint_tools_button == NULL ||
workspace->osint_tools_context_label == NULL ||
workspace->osint_tools_status_label == NULL ||
workspace->osint_tools_actions_box == NULL)
{
return;
}
while ((child = gtk_widget_get_first_child(
workspace->osint_tools_actions_box
)) != NULL)
{
gtk_box_remove(GTK_BOX(workspace->osint_tools_actions_box), child);
}
if (osint_selection_context_get_kind(context) ==
OSINT_SELECTION_CONTEXT_KIND_ENTITY)
{
selection_kind =
"Entité";
selection_identifier =
osint_selection_context_get_identifier(context);
}
else if (osint_selection_context_get_kind(context) ==
OSINT_SELECTION_CONTEXT_KIND_RELATION)
{
selection_kind =
"Relation";
selection_identifier =
osint_selection_context_get_identifier(context);
}
gtk_widget_set_sensitive(
workspace->osint_tools_button,
selection_identifier != NULL &&
selection_identifier[0] != '\0'
);
if (selection_identifier == NULL ||
selection_identifier[0] == '\0')
{
gtk_label_set_text(
GTK_LABEL(
workspace->osint_tools_context_label
),
"Sélectionnez un nœud du graphe."
);
gtk_label_set_text(
GTK_LABEL(
workspace->osint_tools_status_label
),
"Les outils compatibles apparaîtront ici."
);
return;
}
compatible_actions = osint_action_catalog_list_compatible(
workspace->osint_action_catalog,
context
);
for (guint action_index = 0;
compatible_actions != NULL &&
action_index < compatible_actions->len;
action_index++)
{
const OsintAction *action = g_ptr_array_index(
compatible_actions,
action_index
);
const char *tool_version = osint_action_get_tool_version(action);
char *button_label = tool_version != NULL && tool_version[0] != '\0'
? g_strdup_printf("%s — %s", osint_action_get_label(action), tool_version)
: g_strdup(osint_action_get_label(action));
GtkWidget *action_button = gtk_button_new_with_label(button_label);
g_free(button_label);
if (action_button == NULL)
{
continue;
}
gtk_widget_set_sensitive(
action_button,
osint_action_is_available(action)
);
gtk_widget_set_tooltip_text(
action_button,
osint_action_is_available(action)
? osint_action_get_description(action)
: osint_action_get_unavailable_reason(action)
);
g_object_set_data_full(
G_OBJECT(action_button),
"osint-action-identifier",
g_strdup(osint_action_get_identifier(action)),
g_free
);
g_signal_connect(
action_button,
"clicked",
G_CALLBACK(workspace_on_osint_action_clicked),
workspace
);
gtk_box_append(
GTK_BOX(workspace->osint_tools_actions_box),
action_button
);
}
context_text =
g_strdup_printf(
"%s sélectionnée\n%s",
selection_kind,
selection_identifier
);
gtk_label_set_text(
GTK_LABEL(
workspace->osint_tools_context_label
),
context_text != NULL
? context_text
: selection_kind
);
gtk_label_set_text(
GTK_LABEL(
workspace->osint_tools_status_label
),
compatible_actions != NULL && compatible_actions->len > 0U
? "Actions compatibles avec la sélection."
: "Aucune action OSINT compatible."
);
g_free(
context_text
);
g_clear_pointer(&compatible_actions, g_ptr_array_unref);
}
/**
* @brief Ajoute une ligne de métadonnée dans la grille.
*
* @return Label GTK destiné à recevoir la valeur.
*/
static GtkWidget *workspace_add_evidence_field(
GtkGrid *grid,
gint row,
const char *title
)
{
GtkWidget *title_label = NULL;
GtkWidget *value_label = NULL;
if (grid == NULL ||
title == NULL ||
title[0] == '\0')
{
return NULL;
}
title_label =
gtk_label_new(
title
);
value_label =
gtk_label_new(
NULL
);
if (title_label == NULL ||
value_label == NULL)
{
g_clear_object(
&value_label
);
g_clear_object(
&title_label
);
return NULL;
}
gtk_label_set_xalign(
GTK_LABEL(title_label),
0.0F
);
gtk_widget_set_valign(
title_label,
GTK_ALIGN_START
);
gtk_widget_add_css_class(
title_label,
"dim-label"
);
gtk_label_set_xalign(
GTK_LABEL(value_label),
0.0F
);
gtk_label_set_wrap(
GTK_LABEL(value_label),
TRUE
);
gtk_label_set_wrap_mode(
GTK_LABEL(value_label),
PANGO_WRAP_WORD_CHAR
);
gtk_label_set_selectable(
GTK_LABEL(value_label),
TRUE
);
gtk_widget_set_hexpand(
value_label,
TRUE
);
gtk_widget_set_halign(
value_label,
GTK_ALIGN_FILL
);
gtk_widget_set_valign(
value_label,
GTK_ALIGN_START
);
gtk_grid_attach(
grid,
title_label,
0,
row,
1,
1
);
gtk_grid_attach(
grid,
value_label,
1,
row,
1,
1
);
return value_label;
}
/**
* @brief Définit une valeur avec un texte de remplacement.
*/
static void workspace_set_field_text(
GtkWidget *label,
const char *value,
const char *fallback
)
{
if (label == NULL)
{
return;
}
gtk_label_set_text(
GTK_LABEL(label),
value != NULL && value[0] != '\0'
? value
: fallback
);
}
/**
* @brief Convertit un statut d'intégrité en texte utilisateur.
*/
static const char *workspace_get_integrity_status_text(
EvidenceIntegrityStatus integrity_status
)
{
switch (integrity_status)
{
case EVIDENCE_INTEGRITY_STATUS_UNKNOWN:
return "Non vérifiée";
case EVIDENCE_INTEGRITY_STATUS_VALID:
return "Valide";
case EVIDENCE_INTEGRITY_STATUS_MISSING:
return "Fichier absent";
case EVIDENCE_INTEGRITY_STATUS_MODIFIED:
return "Fichier modifié";
case EVIDENCE_INTEGRITY_STATUS_ERROR:
return "Erreur de vérification";
default:
return "Statut inconnu";
}
}
/**
* @brief Transmet la sélection du graphe au volet de détails.
*/
static void workspace_on_graph_node_moved(
const char *entity_identifier,
double x,
double y,
gpointer user_data
)
{
Workspace *workspace =
user_data;
if (workspace == NULL ||
workspace->graph_node_moved_callback == NULL ||
entity_identifier == NULL ||
entity_identifier[0] == '\0')
{
return;
}
workspace->graph_node_moved_callback(
entity_identifier,
x,
y,
workspace->graph_node_moved_user_data
);
}
/**
* @brief Transmet la sélection du graphe au volet de détails.
*/
static void workspace_on_graph_node_selected(
const EntityRecord *entity_record,
const RelationRecord *relation_record,
gpointer user_data
)
{
Workspace *workspace =
user_data;
if (workspace == NULL)
{
return;
}
g_clear_pointer(
&workspace->osint_selection_context,
osint_selection_context_free
);
if (entity_record != NULL)
{
workspace->osint_selection_context =
osint_selection_context_new_entity(entity_record, NULL);
}
if (workspace->entity_details_panel != NULL)
{
entity_details_panel_set_entity(
workspace->entity_details_panel,
entity_record
);
}
if (workspace->relation_details_panel != NULL)
{
gtk_widget_set_visible(
workspace->relation_details_panel,
relation_record != NULL
);
}
if (relation_record != NULL &&
workspace->relation_details_title_label != NULL &&
workspace->relation_details_summary_label != NULL)
{
const EntityRecord *source_entity =
investigation_graph_model_find_entity(
workspace->graph_model,
relation_record_get_source_entity_identifier(relation_record)
);
const EntityRecord *target_entity =
investigation_graph_model_find_entity(
workspace->graph_model,
relation_record_get_target_entity_identifier(relation_record)
);
workspace->osint_selection_context =
osint_selection_context_new_relation(
relation_record,
source_entity,
target_entity,
NULL
);
const char *label = relation_record_get_label(relation_record);
char *summary = g_strdup_printf(
"%s → %s\n\nType : %s\nConfiance : %d %%\nUUID : %s",
entity_record_get_value(source_entity),
entity_record_get_value(target_entity),
relation_record_get_relation_type(relation_record),
relation_record_get_confidence(relation_record),
relation_record_get_identifier(relation_record)
);
gtk_label_set_text(
GTK_LABEL(workspace->relation_details_title_label),
label != NULL && label[0] != '\0' ? label : "Relation"
);
gtk_label_set_text(
GTK_LABEL(workspace->relation_details_summary_label),
summary != NULL ? summary : "Informations indisponibles"
);
g_free(summary);
}
workspace_update_osint_tools_menu(
workspace,
workspace->osint_selection_context
);
}
/**
* @brief Relaie la demande d'ajout d'une relation.
*/
static void workspace_on_add_relation_requested(
const char *source_entity_identifier,
gpointer user_data
)
{
Workspace *workspace =
user_data;
if (workspace == NULL ||
workspace->add_relation_callback == NULL ||
source_entity_identifier == NULL ||
!g_uuid_string_is_valid(
source_entity_identifier
))
{
return;
}
workspace->add_relation_callback(
source_entity_identifier,
workspace->add_relation_user_data
);
}
/**
* @brief Désélectionne le nœud lorsque le volet est fermé manuellement.
*/
static void workspace_on_entity_details_closed(
gpointer user_data
)
{
Workspace *workspace =
user_data;
if (workspace == NULL ||
workspace->graph_view == NULL)
{
return;
}
investigation_graph_view_clear_selection(
workspace->graph_view
);
}
/**
* @brief Affiche l'état principal courant du Workspace.
*
* Sans enquête, la page d'accueil est utilisée. Le chargement et les erreurs
* utilisent la page d'état. Un graphe prêt utilise la vue graphique.
*/
static void workspace_show_default(
Workspace *workspace
)
{
if (workspace == NULL)
{
return;
}
switch (workspace->graph_state)
{
case WORKSPACE_GRAPH_STATE_EMPTY:
gtk_stack_set_visible_child_name(
GTK_STACK(workspace->stack),
WORKSPACE_PAGE_WELCOME
);
return;
case WORKSPACE_GRAPH_STATE_READY:
gtk_stack_set_visible_child_name(
GTK_STACK(workspace->stack),
WORKSPACE_PAGE_GRAPH_VIEW
);
return;
case WORKSPACE_GRAPH_STATE_LOADING:
case WORKSPACE_GRAPH_STATE_ERROR:
default:
gtk_stack_set_visible_child_name(
GTK_STACK(workspace->stack),
WORKSPACE_PAGE_GRAPH_STATE
);
return;
}
}
/**
* @brief Transmet la demande de vérification de la preuve affichée.
*/
static void workspace_on_reset_graph_layout_clicked(
GtkButton *button,
gpointer user_data
)
{
Workspace *workspace =
user_data;
(void) button;
if (workspace == NULL ||
workspace->graph_state != WORKSPACE_GRAPH_STATE_READY ||
workspace->graph_model == NULL ||
workspace->reset_graph_layout_callback == NULL)
{
return;
}
workspace->reset_graph_layout_callback(
workspace->reset_graph_layout_user_data
);
}
/**
* @brief Transmet la demande de vérification de la preuve affichée.
*/
static void workspace_on_verify_evidence_clicked(
GtkButton *button,
gpointer user_data
)
{
Workspace *workspace =
user_data;
(void) button;
if (workspace == NULL ||
workspace->verify_evidence_callback == NULL ||
workspace->selected_evidence_identifier == NULL ||
workspace->selected_evidence_identifier[0] == '\0')
{
return;
}
workspace->verify_evidence_callback(
workspace->selected_evidence_identifier,
workspace->verify_evidence_user_data
);
}
Workspace *workspace_new(void)
{
GtkWidget *evidence_content = NULL;
GtkWidget *evidence_separator = NULL;
GtkWidget *evidence_grid = NULL;
Workspace *workspace = NULL;
workspace = g_new0(Workspace, 1);
workspace->root_widget = gtk_box_new(
GTK_ORIENTATION_VERTICAL,
0
);
if (workspace->root_widget == NULL)
{
workspace_free(workspace);
return NULL;
}
gtk_widget_set_hexpand(
workspace->root_widget,
TRUE
);
gtk_widget_set_vexpand(
workspace->root_widget,
TRUE
);
workspace->stack = gtk_stack_new();
if (workspace->stack == NULL)
{
workspace_free(workspace);
return NULL;
}
gtk_widget_set_hexpand(
workspace->stack,
TRUE
);
gtk_widget_set_vexpand(
workspace->stack,
TRUE
);
gtk_box_append(
GTK_BOX(workspace->root_widget),
workspace->stack
);
/*
* Page d'accueil.
*/
workspace->welcome_page = gtk_box_new(
GTK_ORIENTATION_VERTICAL,
12
);
if (workspace->welcome_page == NULL)
{
workspace_free(workspace);
return NULL;
}
gtk_widget_set_halign(
workspace->welcome_page,
GTK_ALIGN_CENTER
);
gtk_widget_set_valign(
workspace->welcome_page,
GTK_ALIGN_CENTER
);
gtk_stack_add_named(
GTK_STACK(workspace->stack),
workspace->welcome_page,
WORKSPACE_PAGE_WELCOME
);
/*
* Page d'informations sur le nœud.
*/
workspace->node_page = gtk_box_new(
GTK_ORIENTATION_VERTICAL,
12
);
if (workspace->node_page == NULL)
{
workspace_free(workspace);
return NULL;
}
gtk_widget_set_halign(
workspace->node_page,
GTK_ALIGN_CENTER
);
gtk_widget_set_valign(
workspace->node_page,
GTK_ALIGN_CENTER
);
workspace->node_name_label = gtk_label_new(NULL);
workspace->node_path_label = gtk_label_new(NULL);
workspace->node_type_label = gtk_label_new(NULL);
workspace->node_parent_label = gtk_label_new(NULL);
workspace->node_children_label = gtk_label_new(NULL);
gtk_box_append(
GTK_BOX(workspace->node_page),
workspace->node_name_label
);
gtk_box_append(
GTK_BOX(workspace->node_page),
workspace->node_path_label
);
gtk_label_set_wrap(
GTK_LABEL(workspace->node_path_label),
TRUE
);
gtk_label_set_selectable(
GTK_LABEL(workspace->node_path_label),
TRUE
);
gtk_widget_set_halign(
workspace->node_path_label,
GTK_ALIGN_CENTER
);
gtk_box_append(
GTK_BOX(workspace->node_page),
workspace->node_type_label
);
gtk_box_append(
GTK_BOX(workspace->node_page),
workspace->node_parent_label
);
gtk_box_append(
GTK_BOX(workspace->node_page),
workspace->node_children_label
);
gtk_stack_add_named(
GTK_STACK(workspace->stack),
workspace->node_page,
WORKSPACE_PAGE_NODE_INFORMATION
);
/*
* Page détaillée d'une preuve.
*/
workspace->evidence_page =
gtk_scrolled_window_new();
if (workspace->evidence_page == NULL)
{
workspace_free(
workspace
);
return NULL;
}
gtk_widget_set_hexpand(
workspace->evidence_page,
TRUE
);
gtk_widget_set_vexpand(
workspace->evidence_page,
TRUE
);
gtk_scrolled_window_set_policy(
GTK_SCROLLED_WINDOW(
workspace->evidence_page
),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC
);
evidence_content =
gtk_box_new(
GTK_ORIENTATION_VERTICAL,
16
);
if (evidence_content == NULL)
{
workspace_free(
workspace
);
return NULL;
}
gtk_widget_set_margin_start(
evidence_content,
24
);
gtk_widget_set_margin_end(
evidence_content,
24
);
gtk_widget_set_margin_top(
evidence_content,
24
);
gtk_widget_set_margin_bottom(
evidence_content,
24
);
workspace->evidence_name_label =
gtk_label_new(
NULL
);
if (workspace->evidence_name_label == NULL)
{
workspace_free(
workspace
);
return NULL;
}
gtk_label_set_xalign(
GTK_LABEL(
workspace->evidence_name_label
),
0.0F
);
gtk_label_set_wrap(
GTK_LABEL(
workspace->evidence_name_label
),
TRUE
);
gtk_label_set_selectable(
GTK_LABEL(
workspace->evidence_name_label
),
TRUE
);
gtk_widget_add_css_class(
workspace->evidence_name_label,
"title-2"
);
gtk_box_append(
GTK_BOX(evidence_content),
workspace->evidence_name_label
);
workspace->verify_evidence_button =
gtk_button_new_with_label(
"Vérifier l'intégrité"
);
if (workspace->verify_evidence_button == NULL)
{
workspace_free(
workspace
);
return NULL;
}
gtk_widget_set_halign(
workspace->verify_evidence_button,
GTK_ALIGN_START
);
gtk_widget_set_tooltip_text(
workspace->verify_evidence_button,
"Recalculer le SHA-256 et le comparer à l'empreinte enregistrée"
);
/*
* Aucun bouton actif tant qu'aucune preuve n'est affichée.
*/
gtk_widget_set_sensitive(
workspace->verify_evidence_button,
FALSE
);
g_signal_connect(
workspace->verify_evidence_button,
"clicked",
G_CALLBACK(
workspace_on_verify_evidence_clicked
),
workspace
);
gtk_box_append(
GTK_BOX(evidence_content),
workspace->verify_evidence_button
);
evidence_separator =
gtk_separator_new(
GTK_ORIENTATION_HORIZONTAL
);
gtk_box_append(
GTK_BOX(evidence_content),
evidence_separator
);
evidence_grid =
gtk_grid_new();
if (evidence_grid == NULL)
{
workspace_free(
workspace
);
return NULL;
}
gtk_grid_set_column_spacing(
GTK_GRID(evidence_grid),
24
);
gtk_grid_set_row_spacing(
GTK_GRID(evidence_grid),
12
);
gtk_widget_set_hexpand(
evidence_grid,
TRUE
);
workspace->evidence_type_label =
workspace_add_evidence_field(
GTK_GRID(evidence_grid),
0,
"Type"
);
workspace->evidence_integrity_label =
workspace_add_evidence_field(
GTK_GRID(evidence_grid),
1,
"Intégrité"
);
workspace->evidence_size_label =
workspace_add_evidence_field(
GTK_GRID(evidence_grid),
2,
"Taille"
);
workspace->evidence_imported_at_label =
workspace_add_evidence_field(
GTK_GRID(evidence_grid),
3,
"Importée le"
);
workspace->evidence_collected_at_label =
workspace_add_evidence_field(
GTK_GRID(evidence_grid),
4,
"Collectée le"
);
workspace->evidence_source_label =
workspace_add_evidence_field(
GTK_GRID(evidence_grid),
5,
"Source"
);
workspace->evidence_description_label =
workspace_add_evidence_field(
GTK_GRID(evidence_grid),
6,
"Description"
);
workspace->evidence_relative_path_label =
workspace_add_evidence_field(
GTK_GRID(evidence_grid),
7,
"Chemin relatif"
);
workspace->evidence_internal_name_label =
workspace_add_evidence_field(
GTK_GRID(evidence_grid),
8,
"Nom interne"
);
workspace->evidence_identifier_label =
workspace_add_evidence_field(
GTK_GRID(evidence_grid),
9,
"Identifiant"
);
workspace->evidence_sha256_label =
workspace_add_evidence_field(
GTK_GRID(evidence_grid),
10,
"SHA-256"
);
if (workspace->evidence_type_label == NULL ||
workspace->evidence_integrity_label == NULL ||
workspace->evidence_size_label == NULL ||
workspace->evidence_imported_at_label == NULL ||
workspace->evidence_collected_at_label == NULL ||
workspace->evidence_source_label == NULL ||
workspace->evidence_description_label == NULL ||
workspace->evidence_relative_path_label == NULL ||
workspace->evidence_internal_name_label == NULL ||
workspace->evidence_identifier_label == NULL ||
workspace->evidence_sha256_label == NULL)
{
workspace_free(
workspace
);
return NULL;
}
gtk_box_append(
GTK_BOX(evidence_content),
evidence_grid
);
gtk_scrolled_window_set_child(
GTK_SCROLLED_WINDOW(
workspace->evidence_page
),
evidence_content
);
gtk_stack_add_named(
GTK_STACK(workspace->stack),
workspace->evidence_page,
WORKSPACE_PAGE_EVIDENCE_INFORMATION
);
/*
* Page d'état utilisée pendant le chargement ou en cas d'erreur.
*/
workspace->graph_state_page =
gtk_box_new(
GTK_ORIENTATION_VERTICAL,
12
);
if (workspace->graph_state_page == NULL)
{
workspace_free(
workspace
);
return NULL;
}
gtk_widget_set_halign(
workspace->graph_state_page,
GTK_ALIGN_CENTER
);
gtk_widget_set_valign(
workspace->graph_state_page,
GTK_ALIGN_CENTER
);
workspace->graph_spinner =
gtk_spinner_new();
workspace->graph_title_label =
gtk_label_new(
NULL
);
workspace->graph_details_label =
gtk_label_new(
NULL
);
if (workspace->graph_spinner == NULL ||
workspace->graph_title_label == NULL ||
workspace->graph_details_label == NULL)
{
workspace_free(
workspace
);
return NULL;
}
gtk_widget_add_css_class(
workspace->graph_title_label,
"title-2"
);
gtk_label_set_wrap(
GTK_LABEL(
workspace->graph_details_label
),
TRUE
);
gtk_label_set_justify(
GTK_LABEL(
workspace->graph_details_label
),
GTK_JUSTIFY_CENTER
);
gtk_widget_set_visible(
workspace->graph_spinner,
FALSE
);
gtk_box_append(
GTK_BOX(
workspace->graph_state_page
),
workspace->graph_spinner
);
gtk_box_append(
GTK_BOX(
workspace->graph_state_page
),
workspace->graph_title_label
);
gtk_box_append(
GTK_BOX(
workspace->graph_state_page
),
workspace->graph_details_label
);
gtk_stack_add_named(
GTK_STACK(workspace->stack),
workspace->graph_state_page,
WORKSPACE_PAGE_GRAPH_STATE
);
/*
* Page graphique : canvas principal et volet de détails superposé.
*/
workspace->graph_view_page =
gtk_overlay_new();
if (workspace->graph_view_page == NULL)
{
workspace_free(
workspace
);
return NULL;
}
gtk_widget_set_hexpand(
workspace->graph_view_page,
TRUE
);
gtk_widget_set_vexpand(
workspace->graph_view_page,
TRUE
);
workspace->graph_view =
investigation_graph_view_new();
workspace->entity_details_panel =
entity_details_panel_new();
workspace->relation_details_panel = gtk_box_new(
GTK_ORIENTATION_VERTICAL,
12
);
workspace->relation_details_title_label = gtk_label_new(NULL);
workspace->relation_details_summary_label = gtk_label_new(NULL);
if (workspace->graph_view == NULL ||
workspace->entity_details_panel == NULL ||
workspace->relation_details_panel == NULL ||
workspace->relation_details_title_label == NULL ||
workspace->relation_details_summary_label == NULL)
{
workspace_free(
workspace
);
return NULL;
}
gtk_widget_set_size_request(workspace->relation_details_panel, 300, -1);
gtk_widget_set_halign(workspace->relation_details_panel, GTK_ALIGN_END);
gtk_widget_set_valign(workspace->relation_details_panel, GTK_ALIGN_START);
gtk_widget_set_margin_end(workspace->relation_details_panel, 12);
gtk_widget_set_margin_top(workspace->relation_details_panel, 56);
gtk_widget_set_margin_start(workspace->relation_details_panel, 12);
gtk_widget_set_margin_bottom(workspace->relation_details_panel, 12);
gtk_widget_add_css_class(workspace->relation_details_panel, "card");
gtk_widget_set_margin_start(workspace->relation_details_title_label, 16);
gtk_widget_set_margin_end(workspace->relation_details_title_label, 16);
gtk_widget_set_margin_top(workspace->relation_details_title_label, 16);
gtk_label_set_xalign(
GTK_LABEL(workspace->relation_details_title_label),
0.0F
);
gtk_widget_add_css_class(
workspace->relation_details_title_label,
"title-3"
);
gtk_widget_set_margin_start(workspace->relation_details_summary_label, 16);
gtk_widget_set_margin_end(workspace->relation_details_summary_label, 16);
gtk_widget_set_margin_bottom(workspace->relation_details_summary_label, 16);
gtk_label_set_xalign(
GTK_LABEL(workspace->relation_details_summary_label),
0.0F
);
gtk_label_set_wrap(
GTK_LABEL(workspace->relation_details_summary_label),
TRUE
);
gtk_label_set_selectable(
GTK_LABEL(workspace->relation_details_summary_label),
TRUE
);
gtk_box_append(
GTK_BOX(workspace->relation_details_panel),
workspace->relation_details_title_label
);
gtk_box_append(
GTK_BOX(workspace->relation_details_panel),
workspace->relation_details_summary_label
);
gtk_widget_set_visible(workspace->relation_details_panel, FALSE);
investigation_graph_view_set_selection_callback(
workspace->graph_view,
workspace_on_graph_node_selected,
workspace
);
investigation_graph_view_set_node_moved_callback(
workspace->graph_view,
workspace_on_graph_node_moved,
workspace
);
entity_details_panel_set_close_callback(
workspace->entity_details_panel,
workspace_on_entity_details_closed,
workspace
);
entity_details_panel_set_add_relation_callback(
workspace->entity_details_panel,
workspace_on_add_relation_requested,
workspace
);
gtk_overlay_set_child(
GTK_OVERLAY(
workspace->graph_view_page
),
investigation_graph_view_get_widget(
workspace->graph_view
)
);
workspace->graph_toolbar =
gtk_box_new(
GTK_ORIENTATION_HORIZONTAL,
4
);
workspace->reset_graph_layout_button =
gtk_button_new_from_icon_name(
"view-refresh-symbolic"
);
workspace->osint_tools_button =
gtk_menu_button_new();
workspace->osint_tools_popover =
gtk_popover_new();
GtkWidget *osint_tools_content =
gtk_box_new(
GTK_ORIENTATION_VERTICAL,
8
);
GtkWidget *osint_tools_title =
gtk_label_new(
"Outils OSINT"
);
workspace->osint_tools_context_label =
gtk_label_new(
"Sélectionnez un nœud du graphe."
);
workspace->osint_tools_status_label =
gtk_label_new(
"Les outils compatibles apparaîtront ici."
);
workspace->osint_tools_actions_box = gtk_box_new(
GTK_ORIENTATION_VERTICAL,
6
);
workspace->osint_action_catalog =
osint_action_catalog_new_defaults();
if (workspace->graph_toolbar == NULL ||
workspace->reset_graph_layout_button == NULL ||
workspace->osint_tools_button == NULL ||
workspace->osint_tools_popover == NULL ||
osint_tools_content == NULL ||
osint_tools_title == NULL ||
workspace->osint_tools_context_label == NULL ||
workspace->osint_tools_status_label == NULL ||
workspace->osint_tools_actions_box == NULL ||
workspace->osint_action_catalog == NULL)
{
workspace_free(
workspace
);
return NULL;
}
gtk_widget_set_halign(
workspace->graph_toolbar,
GTK_ALIGN_START
);
gtk_widget_set_valign(
workspace->graph_toolbar,
GTK_ALIGN_START
);
gtk_widget_set_margin_start(
workspace->graph_toolbar,
12
);
gtk_widget_set_margin_top(
workspace->graph_toolbar,
12
);
gtk_widget_add_css_class(
workspace->graph_toolbar,
"toolbar"
);
gtk_widget_add_css_class(
workspace->reset_graph_layout_button,
"flat"
);
gtk_widget_set_tooltip_text(
workspace->reset_graph_layout_button,
"Réinitialiser la disposition du graphe"
);
gtk_widget_set_sensitive(
workspace->reset_graph_layout_button,
FALSE
);
gtk_menu_button_set_label(
GTK_MENU_BUTTON(
workspace->osint_tools_button
),
"Outils OSINT"
);
gtk_widget_add_css_class(
workspace->osint_tools_button,
"flat"
);
gtk_widget_set_tooltip_text(
workspace->osint_tools_button,
"Afficher les outils compatibles avec la sélection"
);
gtk_widget_set_sensitive(
workspace->osint_tools_button,
FALSE
);
gtk_widget_set_margin_start(
osint_tools_content,
16
);
gtk_widget_set_margin_end(
osint_tools_content,
16
);
gtk_widget_set_margin_top(
osint_tools_content,
14
);
gtk_widget_set_margin_bottom(
osint_tools_content,
14
);
gtk_label_set_xalign(
GTK_LABEL(
osint_tools_title
),
0.0F
);
gtk_widget_add_css_class(
osint_tools_title,
"heading"
);
gtk_label_set_xalign(
GTK_LABEL(
workspace->osint_tools_context_label
),
0.0F
);
gtk_label_set_selectable(
GTK_LABEL(
workspace->osint_tools_context_label
),
TRUE
);
gtk_label_set_xalign(
GTK_LABEL(
workspace->osint_tools_status_label
),
0.0F
);
gtk_widget_add_css_class(
workspace->osint_tools_status_label,
"dim-label"
);
gtk_box_append(
GTK_BOX(
osint_tools_content
),
osint_tools_title
);
gtk_box_append(
GTK_BOX(
osint_tools_content
),
workspace->osint_tools_context_label
);
gtk_box_append(
GTK_BOX(osint_tools_content),
workspace->osint_tools_actions_box
);
gtk_box_append(
GTK_BOX(
osint_tools_content
),
workspace->osint_tools_status_label
);
gtk_popover_set_child(
GTK_POPOVER(
workspace->osint_tools_popover
),
osint_tools_content
);
gtk_menu_button_set_popover(
GTK_MENU_BUTTON(
workspace->osint_tools_button
),
workspace->osint_tools_popover
);
g_signal_connect(
workspace->reset_graph_layout_button,
"clicked",
G_CALLBACK(
workspace_on_reset_graph_layout_clicked
),
workspace
);
gtk_box_append(
GTK_BOX(
workspace->graph_toolbar
),
workspace->reset_graph_layout_button
);
gtk_box_append(
GTK_BOX(
workspace->graph_toolbar
),
workspace->osint_tools_button
);
gtk_overlay_add_overlay(
GTK_OVERLAY(
workspace->graph_view_page
),
workspace->graph_toolbar
);
gtk_overlay_set_clip_overlay(
GTK_OVERLAY(
workspace->graph_view_page
),
workspace->graph_toolbar,
TRUE
);
workspace->graph_help_button =
gtk_menu_button_new();
workspace->graph_help_popover =
gtk_popover_new();
GtkWidget *graph_help_content =
gtk_box_new(
GTK_ORIENTATION_VERTICAL,
8
);
GtkWidget *graph_help_title =
gtk_label_new(
"Commandes du graphe"
);
GtkWidget *graph_help_text =
gtk_label_new(
"• Clic : sélectionner un nœud\n"
"• Glisser un nœud : le déplacer\n"
"• Maj + glisser : déplacer la vue\n"
"• Ctrl + molette : zoomer\n"
"• Pincement : zoomer"
);
if (workspace->graph_help_button == NULL ||
workspace->graph_help_popover == NULL ||
graph_help_content == NULL ||
graph_help_title == NULL ||
graph_help_text == NULL)
{
workspace_free(
workspace
);
return NULL;
}
gtk_menu_button_set_label(
GTK_MENU_BUTTON(
workspace->graph_help_button
),
"?"
);
gtk_widget_add_css_class(
workspace->graph_help_button,
"flat"
);
gtk_widget_set_tooltip_text(
workspace->graph_help_button,
"Afficher les commandes du graphe"
);
gtk_widget_set_can_target(
workspace->graph_help_button,
TRUE
);
gtk_widget_set_halign(
workspace->graph_help_button,
GTK_ALIGN_END
);
gtk_widget_set_valign(
workspace->graph_help_button,
GTK_ALIGN_START
);
gtk_widget_set_margin_end(
workspace->graph_help_button,
12
);
gtk_widget_set_margin_top(
workspace->graph_help_button,
12
);
gtk_widget_set_margin_start(
graph_help_content,
16
);
gtk_widget_set_margin_end(
graph_help_content,
16
);
gtk_widget_set_margin_top(
graph_help_content,
14
);
gtk_widget_set_margin_bottom(
graph_help_content,
14
);
gtk_label_set_xalign(
GTK_LABEL(
graph_help_title
),
0.0F
);
gtk_widget_add_css_class(
graph_help_title,
"heading"
);
gtk_label_set_xalign(
GTK_LABEL(
graph_help_text
),
0.0F
);
gtk_label_set_wrap(
GTK_LABEL(
graph_help_text
),
TRUE
);
gtk_label_set_wrap_mode(
GTK_LABEL(
graph_help_text
),
PANGO_WRAP_WORD_CHAR
);
gtk_label_set_max_width_chars(
GTK_LABEL(
graph_help_text
),
44
);
gtk_box_append(
GTK_BOX(
graph_help_content
),
graph_help_title
);
gtk_box_append(
GTK_BOX(
graph_help_content
),
graph_help_text
);
gtk_popover_set_child(
GTK_POPOVER(
workspace->graph_help_popover
),
graph_help_content
);
gtk_menu_button_set_popover(
GTK_MENU_BUTTON(
workspace->graph_help_button
),
workspace->graph_help_popover
);
gtk_overlay_add_overlay(
GTK_OVERLAY(
workspace->graph_view_page
),
entity_details_panel_get_widget(
workspace->entity_details_panel
)
);
gtk_overlay_add_overlay(
GTK_OVERLAY(workspace->graph_view_page),
workspace->relation_details_panel
);
gtk_overlay_set_clip_overlay(
GTK_OVERLAY(workspace->graph_view_page),
workspace->relation_details_panel,
TRUE
);
gtk_overlay_set_clip_overlay(
GTK_OVERLAY(
workspace->graph_view_page
),
entity_details_panel_get_widget(
workspace->entity_details_panel
),
TRUE
);
gtk_overlay_add_overlay(
GTK_OVERLAY(
workspace->graph_view_page
),
workspace->graph_help_button
);
gtk_overlay_set_clip_overlay(
GTK_OVERLAY(
workspace->graph_view_page
),
workspace->graph_help_button,
TRUE
);
gtk_stack_add_named(
GTK_STACK(workspace->stack),
workspace->graph_view_page,
WORKSPACE_PAGE_GRAPH_VIEW
);
workspace->graph_model =
NULL;
workspace->graph_layout =
NULL;
workspace->graph_state =
WORKSPACE_GRAPH_STATE_EMPTY;
workspace_show_default(
workspace
);
return workspace;
}
GtkWidget *workspace_get_widget(
const Workspace *workspace
)
{
if (workspace == NULL)
{
return NULL;
}
return workspace->root_widget;
}
void workspace_set_selected_node(
Workspace *workspace,
const InvestigationNode *node
)
{
const char *node_name = NULL;
const InvestigationNode *parent_node = NULL;
const char *parent_name = NULL;
const char *node_path = NULL;
InvestigationNodeType node_type;
size_t children_count = 0;
const char *type_text = NULL;
char *parent_text = NULL;
char *children_text = NULL;
if (workspace == NULL)
{
return;
}
g_clear_pointer(
&workspace->selected_evidence_identifier,
g_free
);
if (workspace->verify_evidence_button != NULL)
{
gtk_widget_set_sensitive(
workspace->verify_evidence_button,
FALSE
);
}
if (node == NULL)
{
workspace_show_default(workspace);
return;
}
node_name = investigation_node_get_name(node);
node_path = investigation_node_get_path(node);
node_type = investigation_node_get_type(node);
parent_node = investigation_node_get_parent(node);
gtk_label_set_text(
GTK_LABEL(workspace->node_name_label),
node_name != NULL ? node_name : "(sans nom)"
);
gtk_label_set_text(
GTK_LABEL(workspace->node_path_label),
node_path != NULL ? node_path : "(chemin indisponible)"
);
if (node_type == INVESTIGATION_NODE_DIRECTORY)
{
type_text = "Type : dossier";
}
else
{
type_text = "Type : fichier";
}
gtk_label_set_text(
GTK_LABEL(workspace->node_type_label),
type_text
);
if (parent_node != NULL)
{
parent_name = investigation_node_get_name(parent_node);
parent_text = g_strdup_printf(
"Parent : %s",
parent_name != NULL ? parent_name : "(sans nom)"
);
}
else
{
parent_text = g_strdup("Parent : aucun");
}
gtk_label_set_text(
GTK_LABEL(workspace->node_parent_label),
parent_text
);
if (node_type == INVESTIGATION_NODE_DIRECTORY)
{
children_count =
investigation_node_get_children_count(node);
children_text = g_strdup_printf(
"Enfants : %zu",
children_count
);
gtk_label_set_text(
GTK_LABEL(workspace->node_children_label),
children_text
);
gtk_widget_set_visible(
workspace->node_children_label,
TRUE
);
}
else
{
gtk_label_set_text(
GTK_LABEL(workspace->node_children_label),
""
);
gtk_widget_set_visible(
workspace->node_children_label,
FALSE
);
}
gtk_stack_set_visible_child_name(
GTK_STACK(workspace->stack),
WORKSPACE_PAGE_NODE_INFORMATION
);
g_free(children_text);
g_free(parent_text);
}
void workspace_set_selected_evidence(
Workspace *workspace,
const EvidenceRecord *evidence_record
)
{
char *human_size = NULL;
char *size_text = NULL;
guint64 size_bytes = 0;
if (workspace == NULL)
{
return;
}
g_clear_pointer(
&workspace->selected_evidence_identifier,
g_free
);
if (workspace->verify_evidence_button != NULL)
{
gtk_widget_set_sensitive(
workspace->verify_evidence_button,
FALSE
);
}
if (evidence_record == NULL)
{
workspace_show_default(
workspace
);
return;
}
workspace->selected_evidence_identifier =
g_strdup(
evidence_record_get_identifier(
evidence_record
)
);
if (workspace->selected_evidence_identifier == NULL)
{
workspace_show_default(
workspace
);
return;
}
gtk_widget_set_sensitive(
workspace->verify_evidence_button,
TRUE
);
size_bytes =
evidence_record_get_size_bytes(
evidence_record
);
human_size =
g_format_size_full(
size_bytes,
G_FORMAT_SIZE_IEC_UNITS
);
size_text =
g_strdup_printf(
"%s (%" G_GUINT64_FORMAT " octets)",
human_size != NULL
? human_size
: "taille inconnue",
size_bytes
);
workspace_set_field_text(
workspace->evidence_name_label,
evidence_record_get_original_name(
evidence_record
),
"(sans nom)"
);
workspace_set_field_text(
workspace->evidence_type_label,
evidence_record_get_type_identifier(
evidence_record
),
"(type inconnu)"
);
workspace_set_field_text(
workspace->evidence_integrity_label,
workspace_get_integrity_status_text(
evidence_record_get_integrity_status(
evidence_record
)
),
"Statut inconnu"
);
workspace_set_field_text(
workspace->evidence_size_label,
size_text,
"Taille inconnue"
);
workspace_set_field_text(
workspace->evidence_imported_at_label,
evidence_record_get_imported_at(
evidence_record
),
"(date inconnue)"
);
workspace_set_field_text(
workspace->evidence_collected_at_label,
evidence_record_get_collected_at(
evidence_record
),
"Non renseignée"
);
workspace_set_field_text(
workspace->evidence_source_label,
evidence_record_get_source(
evidence_record
),
"Non renseignée"
);
workspace_set_field_text(
workspace->evidence_description_label,
evidence_record_get_description(
evidence_record
),
"Non renseignée"
);
workspace_set_field_text(
workspace->evidence_relative_path_label,
evidence_record_get_relative_path(
evidence_record
),
"(chemin inconnu)"
);
workspace_set_field_text(
workspace->evidence_internal_name_label,
evidence_record_get_internal_name(
evidence_record
),
"(nom interne inconnu)"
);
workspace_set_field_text(
workspace->evidence_identifier_label,
evidence_record_get_identifier(
evidence_record
),
"(identifiant inconnu)"
);
workspace_set_field_text(
workspace->evidence_sha256_label,
evidence_record_get_sha256(
evidence_record
),
"(empreinte inconnue)"
);
gtk_stack_set_visible_child_name(
GTK_STACK(workspace->stack),
WORKSPACE_PAGE_EVIDENCE_INFORMATION
);
g_free(
size_text
);
g_free(
human_size
);
}
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
);
}
gboolean workspace_select_graph_relation(
Workspace *workspace,
const char *relation_identifier
)
{
if (workspace == NULL || workspace->graph_view == NULL)
{
return FALSE;
}
return investigation_graph_view_select_relation(
workspace->graph_view,
relation_identifier
);
}
void workspace_set_osint_tool_state(
Workspace *workspace,
const char *tool_identifier,
OsintActionToolState state,
const char *version
)
{
if (workspace == NULL || workspace->osint_action_catalog == NULL)
{
return;
}
osint_action_catalog_update_tool_state(
workspace->osint_action_catalog,
tool_identifier,
state,
version
);
workspace_update_osint_tools_menu(
workspace,
workspace->osint_selection_context
);
}
void workspace_set_osint_action_callback(
Workspace *workspace,
WorkspaceOsintActionCallback callback,
gpointer user_data
)
{
if (workspace == NULL)
{
return;
}
workspace->osint_action_callback = callback;
workspace->osint_action_user_data = user_data;
}
void workspace_set_graph_loading(
Workspace *workspace
)
{
if (workspace == NULL)
{
return;
}
g_clear_pointer(
&workspace->selected_evidence_identifier,
g_free
);
if (workspace->verify_evidence_button != NULL)
{
gtk_widget_set_sensitive(
workspace->verify_evidence_button,
FALSE
);
}
entity_details_panel_clear(
workspace->entity_details_panel
);
investigation_graph_view_clear(
workspace->graph_view
);
workspace->graph_model =
NULL;
workspace->graph_layout =
NULL;
workspace->graph_state =
WORKSPACE_GRAPH_STATE_LOADING;
gtk_widget_set_sensitive(
workspace->reset_graph_layout_button,
FALSE
);
gtk_label_set_text(
GTK_LABEL(
workspace->graph_title_label
),
"Chargement de lenquête…"
);
gtk_label_set_text(
GTK_LABEL(
workspace->graph_details_label
),
"Lecture des entités et des relations"
);
gtk_widget_set_visible(
workspace->graph_spinner,
TRUE
);
gtk_spinner_start(
GTK_SPINNER(
workspace->graph_spinner
)
);
workspace_show_default(
workspace
);
}
void workspace_set_graph(
Workspace *workspace,
const InvestigationGraphModel *graph_model,
const InvestigationGraphLayout *graph_layout
)
{
if (workspace == NULL)
{
return;
}
if (graph_model == NULL)
{
workspace_clear_graph(
workspace
);
return;
}
g_clear_pointer(
&workspace->selected_evidence_identifier,
g_free
);
if (workspace->verify_evidence_button != NULL)
{
gtk_widget_set_sensitive(
workspace->verify_evidence_button,
FALSE
);
}
entity_details_panel_clear(
workspace->entity_details_panel
);
investigation_graph_view_clear(
workspace->graph_view
);
workspace->graph_model =
graph_model;
workspace->graph_layout =
graph_layout;
investigation_graph_view_set_layout(
workspace->graph_view,
graph_layout
);
investigation_graph_view_set_graph(
workspace->graph_view,
graph_model
);
workspace->graph_state =
WORKSPACE_GRAPH_STATE_READY;
gtk_widget_set_sensitive(
workspace->reset_graph_layout_button,
TRUE
);
gtk_spinner_stop(
GTK_SPINNER(
workspace->graph_spinner
)
);
gtk_widget_set_visible(
workspace->graph_spinner,
FALSE
);
gtk_label_set_text(
GTK_LABEL(
workspace->graph_title_label
),
""
);
gtk_label_set_text(
GTK_LABEL(
workspace->graph_details_label
),
""
);
workspace_show_default(
workspace
);
}
void workspace_set_graph_error(
Workspace *workspace,
const char *message
)
{
if (workspace == NULL)
{
return;
}
g_clear_pointer(
&workspace->selected_evidence_identifier,
g_free
);
if (workspace->verify_evidence_button != NULL)
{
gtk_widget_set_sensitive(
workspace->verify_evidence_button,
FALSE
);
}
entity_details_panel_clear(
workspace->entity_details_panel
);
investigation_graph_view_clear(
workspace->graph_view
);
workspace->graph_model =
NULL;
workspace->graph_layout =
NULL;
workspace->graph_state =
WORKSPACE_GRAPH_STATE_ERROR;
gtk_widget_set_sensitive(
workspace->reset_graph_layout_button,
FALSE
);
gtk_spinner_stop(
GTK_SPINNER(
workspace->graph_spinner
)
);
gtk_widget_set_visible(
workspace->graph_spinner,
FALSE
);
gtk_label_set_text(
GTK_LABEL(
workspace->graph_title_label
),
"Impossible de charger le graphe de lenquête"
);
gtk_label_set_text(
GTK_LABEL(
workspace->graph_details_label
),
message != NULL &&
message[0] != '\0'
? message
: "Aucun détail supplémentaire nest disponible."
);
workspace_show_default(
workspace
);
}
void workspace_clear_graph(
Workspace *workspace
)
{
if (workspace == NULL)
{
return;
}
g_clear_pointer(
&workspace->selected_evidence_identifier,
g_free
);
if (workspace->verify_evidence_button != NULL)
{
gtk_widget_set_sensitive(
workspace->verify_evidence_button,
FALSE
);
}
entity_details_panel_clear(
workspace->entity_details_panel
);
investigation_graph_view_clear(
workspace->graph_view
);
workspace->graph_model =
NULL;
workspace->graph_layout =
NULL;
workspace->graph_state =
WORKSPACE_GRAPH_STATE_EMPTY;
gtk_widget_set_sensitive(
workspace->reset_graph_layout_button,
FALSE
);
gtk_spinner_stop(
GTK_SPINNER(
workspace->graph_spinner
)
);
gtk_widget_set_visible(
workspace->graph_spinner,
FALSE
);
gtk_label_set_text(
GTK_LABEL(
workspace->graph_title_label
),
""
);
gtk_label_set_text(
GTK_LABEL(
workspace->graph_details_label
),
""
);
workspace_show_default(
workspace
);
}
void workspace_set_verify_evidence_callback(
Workspace *workspace,
WorkspaceVerifyEvidenceCallback callback,
gpointer user_data
)
{
if (workspace == NULL)
{
return;
}
workspace->verify_evidence_callback =
callback;
workspace->verify_evidence_user_data =
user_data;
}
void workspace_set_graph_node_moved_callback(
Workspace *workspace,
WorkspaceGraphNodeMovedCallback callback,
gpointer user_data
)
{
if (workspace == NULL)
{
return;
}
workspace->graph_node_moved_callback =
callback;
workspace->graph_node_moved_user_data =
user_data;
}
void workspace_set_reset_graph_layout_callback(
Workspace *workspace,
WorkspaceResetGraphLayoutCallback callback,
gpointer user_data
)
{
if (workspace == NULL)
{
return;
}
workspace->reset_graph_layout_callback =
callback;
workspace->reset_graph_layout_user_data =
user_data;
}
void workspace_set_add_relation_callback(
Workspace *workspace,
WorkspaceAddRelationCallback callback,
gpointer user_data
)
{
if (workspace == NULL)
{
return;
}
workspace->add_relation_callback =
callback;
workspace->add_relation_user_data =
user_data;
}
void workspace_reset_graph_layout(
Workspace *workspace
)
{
if (workspace == NULL ||
workspace->graph_view == NULL ||
workspace->graph_state != WORKSPACE_GRAPH_STATE_READY ||
workspace->graph_model == NULL)
{
return;
}
entity_details_panel_clear(
workspace->entity_details_panel
);
investigation_graph_view_clear_selection(
workspace->graph_view
);
investigation_graph_view_reset_layout(
workspace->graph_view
);
}
void workspace_free(Workspace *workspace)
{
if (workspace == NULL)
{
return;
}
g_clear_pointer(
&workspace->osint_selection_context,
osint_selection_context_free
);
g_clear_pointer(
&workspace->osint_action_catalog,
osint_action_catalog_free
);
investigation_graph_view_set_selection_callback(
workspace->graph_view,
NULL,
NULL
);
investigation_graph_view_set_node_moved_callback(
workspace->graph_view,
NULL,
NULL
);
entity_details_panel_set_close_callback(
workspace->entity_details_panel,
NULL,
NULL
);
entity_details_panel_set_add_relation_callback(
workspace->entity_details_panel,
NULL,
NULL
);
entity_details_panel_clear(
workspace->entity_details_panel
);
investigation_graph_view_clear(
workspace->graph_view
);
investigation_graph_view_free(
workspace->graph_view
);
entity_details_panel_free(
workspace->entity_details_panel
);
workspace->graph_view =
NULL;
workspace->entity_details_panel =
NULL;
workspace->graph_model =
NULL;
workspace->graph_layout =
NULL;
workspace->verify_evidence_callback =
NULL;
workspace->verify_evidence_user_data =
NULL;
workspace->graph_node_moved_callback =
NULL;
workspace->graph_node_moved_user_data =
NULL;
workspace->reset_graph_layout_callback =
NULL;
workspace->reset_graph_layout_user_data =
NULL;
workspace->add_relation_callback =
NULL;
workspace->add_relation_user_data =
NULL;
workspace->graph_toolbar =
NULL;
workspace->reset_graph_layout_button =
NULL;
workspace->graph_help_button =
NULL;
workspace->graph_help_popover =
NULL;
g_clear_pointer(
&workspace->selected_evidence_identifier,
g_free
);
g_free(workspace);
}