4069 lines
110 KiB
C
4069 lines
110 KiB
C
/******************************************************************************
|
||
* @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 "widgets/evidence_preview_widget.h"
|
||
#include "widgets/ocr_provenance_overlay.h"
|
||
#include "views/evidence_identity_ocr_dialog.h"
|
||
|
||
#include <glib.h>
|
||
#include <gio/gio.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 *relation_evidences_label;
|
||
GtkWidget *edit_relation_button;
|
||
GtkWidget *delete_relation_button;
|
||
GtkWidget *close_relation_details_button;
|
||
char *selected_relation_identifier;
|
||
|
||
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 *evidence_observations_box;
|
||
GtkWidget *verify_evidence_button;
|
||
GtkWidget *edit_evidence_button;
|
||
GtkWidget *analyze_eml_button;
|
||
GtkWidget *analyze_rib_button;
|
||
GtkWidget *extract_metadata_button;
|
||
GtkWidget *recover_pdf_password_button;
|
||
GtkWidget *analyze_identity_button;
|
||
GtkWidget *identity_ocr_section;
|
||
GtkDropDown *identity_ocr_selector;
|
||
GtkStringList *identity_ocr_labels;
|
||
GtkLabel *identity_ocr_summary;
|
||
GtkTextBuffer *identity_ocr_raw;
|
||
GtkTextBuffer *identity_ocr_corrected;
|
||
GtkBox *identity_ocr_fields;
|
||
GtkWidget *revise_identity_button;
|
||
GtkWidget *rerun_identity_button;
|
||
OcrProvenanceOverlay *identity_ocr_overlay;
|
||
GPtrArray *identity_ocr_records;
|
||
WorkspaceIdentityOcrCallback identity_ocr_callback;
|
||
gpointer identity_ocr_user_data;
|
||
EvidencePreviewWidget *evidence_preview;
|
||
|
||
char *selected_evidence_identifier;
|
||
|
||
WorkspaceVerifyEvidenceCallback
|
||
verify_evidence_callback;
|
||
|
||
gpointer
|
||
verify_evidence_user_data;
|
||
|
||
WorkspaceEditEvidenceCallback edit_evidence_callback;
|
||
gpointer edit_evidence_user_data;
|
||
WorkspaceAnalyzeEmlCallback analyze_eml_callback;
|
||
gpointer analyze_eml_user_data;
|
||
WorkspaceAnalyzeRibCallback analyze_rib_callback;
|
||
gpointer analyze_rib_user_data;
|
||
WorkspaceExtractMetadataCallback extract_metadata_callback;
|
||
gpointer extract_metadata_user_data;
|
||
WorkspaceRecoverPdfPasswordCallback recover_pdf_password_callback;
|
||
gpointer recover_pdf_password_user_data;
|
||
WorkspaceObservationRemoveCallback observation_remove_callback;
|
||
gpointer observation_remove_user_data;
|
||
|
||
WorkspaceGraphNodeMovedCallback
|
||
graph_node_moved_callback;
|
||
|
||
gpointer
|
||
graph_node_moved_user_data;
|
||
WorkspaceGraphTransformChangedCallback graph_transform_changed_callback;
|
||
gpointer graph_transform_changed_user_data;
|
||
|
||
WorkspaceExtractionDropCallback extraction_drop_callback;
|
||
gpointer extraction_drop_user_data;
|
||
|
||
WorkspaceResetGraphLayoutCallback
|
||
reset_graph_layout_callback;
|
||
|
||
gpointer
|
||
reset_graph_layout_user_data;
|
||
|
||
WorkspaceAddRelationCallback
|
||
add_relation_callback;
|
||
|
||
gpointer
|
||
add_relation_user_data;
|
||
|
||
WorkspaceEditRelationCallback edit_relation_callback;
|
||
WorkspaceDeleteRelationCallback delete_relation_callback;
|
||
gpointer delete_relation_user_data;
|
||
WorkspaceRelationSelectedCallback relation_selected_callback;
|
||
gpointer relation_selected_user_data;
|
||
gpointer edit_relation_user_data;
|
||
WorkspacePersonRoleCallback person_role_callback;
|
||
gpointer person_role_user_data;
|
||
WorkspacePersonConfidenceCallback person_confidence_callback;
|
||
gpointer person_confidence_user_data;
|
||
WorkspacePersonNameCallback person_name_callback;
|
||
gpointer person_name_user_data;
|
||
WorkspacePersonEvidenceCallback person_evidence_callback;
|
||
gpointer person_evidence_user_data;
|
||
WorkspaceEvidenceActivatedCallback evidence_activated_callback;
|
||
gpointer evidence_activated_user_data;
|
||
WorkspaceEntitySelectedCallback entity_selected_callback;
|
||
gpointer entity_selected_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;
|
||
gboolean saved_graph_transform_valid;
|
||
double saved_graph_zoom;
|
||
double saved_graph_offset_x;
|
||
double saved_graph_offset_y;
|
||
};
|
||
|
||
/**
|
||
* @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),
|
||
g_str_has_prefix(action_identifier, "history-")
|
||
? "Ouverture de l'historique enregistré."
|
||
: "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
|
||
);
|
||
|
||
{
|
||
GtkWidget *history_button = gtk_button_new_with_label(
|
||
"Historique OSINT"
|
||
);
|
||
const char *history_action =
|
||
osint_selection_context_get_kind(context) ==
|
||
OSINT_SELECTION_CONTEXT_KIND_RELATION
|
||
? "history-relation" : "history-entity";
|
||
gtk_widget_set_tooltip_text(
|
||
history_button,
|
||
"Consulter les exécutions OSINT enregistrées pour cette sélection"
|
||
);
|
||
g_object_set_data_full(
|
||
G_OBJECT(history_button), "osint-action-identifier",
|
||
g_strdup(history_action), g_free
|
||
);
|
||
g_signal_connect(
|
||
history_button, "clicked",
|
||
G_CALLBACK(workspace_on_osint_action_clicked), workspace
|
||
);
|
||
gtk_box_append(
|
||
GTK_BOX(workspace->osint_tools_actions_box), history_button
|
||
);
|
||
}
|
||
|
||
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 Ouvre le menu OSINT depuis le bouton de la fiche entité. */
|
||
static void workspace_on_entity_osint_requested(const char *entity_identifier,
|
||
gpointer user_data)
|
||
{
|
||
Workspace *workspace = user_data;
|
||
(void) entity_identifier;
|
||
if (workspace == NULL || workspace->osint_tools_popover == NULL) return;
|
||
workspace_update_osint_tools_menu(workspace,
|
||
workspace->osint_selection_context);
|
||
gtk_popover_popup(GTK_POPOVER(workspace->osint_tools_popover));
|
||
}
|
||
|
||
/**
|
||
* @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 Arrête et détache le média vidéo actuellement affiché. */
|
||
static void workspace_clear_evidence_preview(Workspace *workspace)
|
||
{
|
||
if (workspace == NULL) return;
|
||
evidence_preview_widget_clear(workspace->evidence_preview);
|
||
}
|
||
|
||
/** @brief Convertit le statut d'une relation en texte utilisateur. */
|
||
static const char *workspace_get_relation_status_text(RelationStatus status)
|
||
{
|
||
switch (status)
|
||
{
|
||
case RELATION_STATUS_ACTIVE: return "Active";
|
||
case RELATION_STATUS_ARCHIVED: return "Archivée";
|
||
case RELATION_STATUS_DELETED: return "Supprimée";
|
||
case RELATION_STATUS_DISPUTED: return "Contestée";
|
||
case RELATION_STATUS_UNKNOWN:
|
||
default: return "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
|
||
);
|
||
}
|
||
|
||
static void workspace_on_graph_transform_changed(gpointer user_data)
|
||
{
|
||
Workspace *workspace = user_data;
|
||
if (workspace != NULL && workspace->graph_transform_changed_callback != NULL)
|
||
workspace->graph_transform_changed_callback(
|
||
workspace->graph_transform_changed_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
|
||
);
|
||
g_clear_pointer(&workspace->selected_relation_identifier, g_free);
|
||
|
||
if (entity_record != NULL)
|
||
{
|
||
workspace->osint_selection_context =
|
||
osint_selection_context_new_entity(entity_record, NULL);
|
||
if (workspace->entity_selected_callback != NULL)
|
||
workspace->entity_selected_callback(
|
||
entity_record_get_identifier(entity_record),
|
||
workspace->entity_selected_user_data);
|
||
}
|
||
|
||
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_can_target(workspace->relation_details_panel,
|
||
relation_record != 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)
|
||
{
|
||
workspace->selected_relation_identifier = g_strdup(
|
||
relation_record_get_identifier(relation_record));
|
||
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 %%\nStatut : %s\n"
|
||
"Justification : %s\nCréée le : %s\nModifiée le : %s\nUUID : %s",
|
||
source_entity != NULL ? entity_record_get_value(source_entity) :
|
||
"Source inconnue",
|
||
target_entity != NULL ? entity_record_get_value(target_entity) :
|
||
"Cible inconnue",
|
||
relation_record_get_relation_type_label(relation_record),
|
||
relation_record_get_confidence(relation_record),
|
||
workspace_get_relation_status_text(
|
||
relation_record_get_status(relation_record)),
|
||
relation_record_get_justification(relation_record) != NULL
|
||
? relation_record_get_justification(relation_record)
|
||
: "Non renseignée",
|
||
relation_record_get_created_at(relation_record),
|
||
relation_record_get_updated_at(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);
|
||
|
||
if (workspace->relation_selected_callback != NULL)
|
||
workspace->relation_selected_callback(
|
||
workspace->selected_relation_identifier,
|
||
workspace->relation_selected_user_data);
|
||
}
|
||
|
||
workspace_update_osint_tools_menu(
|
||
workspace,
|
||
workspace->osint_selection_context
|
||
);
|
||
}
|
||
|
||
/** @brief Relaie la demande de modification de la relation affichée. */
|
||
static void workspace_on_edit_relation_clicked(GtkButton *button,
|
||
gpointer user_data)
|
||
{
|
||
Workspace *workspace = user_data;
|
||
(void) button;
|
||
if (workspace == NULL || workspace->edit_relation_callback == NULL ||
|
||
workspace->selected_relation_identifier == NULL)
|
||
{
|
||
return;
|
||
}
|
||
workspace->edit_relation_callback(workspace->selected_relation_identifier,
|
||
workspace->edit_relation_user_data);
|
||
}
|
||
|
||
/** @brief Relaie la demande de suppression de la relation affichée. */
|
||
static void workspace_on_delete_relation_clicked(GtkButton *button,
|
||
gpointer user_data)
|
||
{
|
||
Workspace *workspace = user_data;
|
||
(void) button;
|
||
if (workspace == NULL || workspace->delete_relation_callback == NULL ||
|
||
workspace->selected_relation_identifier == NULL) return;
|
||
workspace->delete_relation_callback(workspace->selected_relation_identifier,
|
||
workspace->delete_relation_user_data);
|
||
}
|
||
|
||
/** @brief Ferme le volet de relation et désélectionne le graphe. */
|
||
static void workspace_on_close_relation_details_clicked(GtkButton *button,
|
||
gpointer user_data)
|
||
{
|
||
Workspace *workspace = user_data;
|
||
(void) button;
|
||
if (workspace != NULL)
|
||
investigation_graph_view_clear_selection(workspace->graph_view);
|
||
}
|
||
|
||
/**
|
||
* @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 Relaie le changement de catégorie d'une personne. */
|
||
static void workspace_on_person_role_changed(const char *entity_identifier,
|
||
PersonRole role, gpointer user_data)
|
||
{
|
||
Workspace *workspace = user_data;
|
||
if (workspace != NULL && workspace->person_role_callback != NULL)
|
||
workspace->person_role_callback(entity_identifier, role,
|
||
workspace->person_role_user_data);
|
||
}
|
||
|
||
/** @brief Relaie la modification de confiance d'une personne. */
|
||
static void workspace_on_person_confidence_changed(
|
||
const char *entity_identifier, gint confidence, gpointer user_data)
|
||
{
|
||
Workspace *workspace = user_data;
|
||
if (workspace != NULL && workspace->person_confidence_callback != NULL)
|
||
workspace->person_confidence_callback(entity_identifier, confidence,
|
||
workspace->person_confidence_user_data);
|
||
}
|
||
|
||
/** @brief Relaie la modification du nom affiché d'une personne. */
|
||
static void workspace_on_person_name_changed(const char *entity_identifier,
|
||
const char *display_name, gpointer user_data)
|
||
{
|
||
Workspace *workspace = user_data;
|
||
if (workspace != NULL && workspace->person_name_callback != NULL)
|
||
workspace->person_name_callback(entity_identifier, display_name,
|
||
workspace->person_name_user_data);
|
||
}
|
||
/** @brief Relaie la gestion des preuves d'une personne. */
|
||
static void workspace_on_person_evidence_requested(const char *identifier,
|
||
gpointer user_data)
|
||
{
|
||
Workspace *workspace = user_data;
|
||
if (workspace != NULL && workspace->person_evidence_callback != NULL)
|
||
workspace->person_evidence_callback(identifier,
|
||
workspace->person_evidence_user_data);
|
||
}
|
||
|
||
static void workspace_on_evidence_activated(const char *identifier,
|
||
gpointer user_data)
|
||
{
|
||
Workspace *workspace = user_data;
|
||
if (workspace != NULL && workspace->evidence_activated_callback != NULL)
|
||
workspace->evidence_activated_callback(identifier,
|
||
workspace->evidence_activated_user_data);
|
||
}
|
||
|
||
static void workspace_on_extraction_dropped(const char *file_path,
|
||
const char *target_entity_identifier, gpointer user_data)
|
||
{
|
||
Workspace *workspace = user_data;
|
||
if (workspace != NULL && workspace->extraction_drop_callback != NULL)
|
||
workspace->extraction_drop_callback(file_path, target_entity_identifier,
|
||
workspace->extraction_drop_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
|
||
);
|
||
}
|
||
|
||
/** @brief Transmet la demande de modification de la preuve affichée. */
|
||
static void workspace_on_edit_evidence_clicked(
|
||
GtkButton *button, gpointer user_data
|
||
)
|
||
{
|
||
Workspace *workspace = user_data;
|
||
(void) button;
|
||
if (workspace == NULL || workspace->edit_evidence_callback == NULL ||
|
||
workspace->selected_evidence_identifier == NULL) return;
|
||
workspace->edit_evidence_callback(
|
||
workspace->selected_evidence_identifier,
|
||
workspace->edit_evidence_user_data);
|
||
}
|
||
/** @brief Transmet la demande d'analyse de la preuve EML affichée. */
|
||
static void workspace_on_analyze_eml_clicked(GtkButton *button, gpointer data)
|
||
{
|
||
Workspace *workspace = data; (void) button;
|
||
if (workspace != NULL && workspace->analyze_eml_callback != NULL &&
|
||
workspace->selected_evidence_identifier != NULL)
|
||
workspace->analyze_eml_callback(workspace->selected_evidence_identifier,
|
||
workspace->analyze_eml_user_data);
|
||
}
|
||
/** @brief Transmet la demande d'analyse OCR du RIB affiché. */
|
||
static void workspace_on_analyze_rib_clicked(GtkButton *button, gpointer data)
|
||
{
|
||
Workspace *workspace = data; (void) button;
|
||
if (workspace != NULL && workspace->analyze_rib_callback != NULL &&
|
||
workspace->selected_evidence_identifier != NULL)
|
||
workspace->analyze_rib_callback(workspace->selected_evidence_identifier,
|
||
workspace->analyze_rib_user_data);
|
||
}
|
||
|
||
/** @brief Transmet la demande d'extraction des métadonnées. */
|
||
static void workspace_on_extract_metadata_clicked(GtkButton *button,
|
||
gpointer data)
|
||
{
|
||
Workspace *workspace = data;
|
||
(void) button;
|
||
if (workspace != NULL && workspace->extract_metadata_callback != NULL &&
|
||
workspace->selected_evidence_identifier != NULL)
|
||
workspace->extract_metadata_callback(
|
||
workspace->selected_evidence_identifier,
|
||
workspace->extract_metadata_user_data);
|
||
}
|
||
|
||
/** @brief Transmet la demande de récupération du mot de passe PDF. */
|
||
static void workspace_on_recover_pdf_password_clicked(GtkButton *button,
|
||
gpointer data)
|
||
{
|
||
Workspace *workspace = data;
|
||
(void) button;
|
||
if (workspace != NULL && workspace->recover_pdf_password_callback != NULL &&
|
||
workspace->selected_evidence_identifier != NULL)
|
||
workspace->recover_pdf_password_callback(
|
||
workspace->selected_evidence_identifier,
|
||
workspace->recover_pdf_password_user_data);
|
||
}
|
||
|
||
WorkspaceIdentityOcrRecord *workspace_identity_ocr_record_new(
|
||
IdentityOcrRun *owned_run, const char *person_identifier,
|
||
const char *executed_at, const char *text_relative_path,
|
||
const char *text_sha256, const char *tsv_relative_path,
|
||
const char *tsv_sha256)
|
||
{
|
||
if (owned_run == NULL) return NULL;
|
||
WorkspaceIdentityOcrRecord *record =
|
||
g_new0(WorkspaceIdentityOcrRecord, 1);
|
||
record->run = owned_run;
|
||
record->person_identifier = g_strdup(person_identifier);
|
||
record->executed_at = g_strdup(executed_at);
|
||
record->text_relative_path = g_strdup(text_relative_path);
|
||
record->text_sha256 = g_strdup(text_sha256);
|
||
record->tsv_relative_path = g_strdup(tsv_relative_path);
|
||
record->tsv_sha256 = g_strdup(tsv_sha256);
|
||
return record;
|
||
}
|
||
|
||
void workspace_identity_ocr_record_free(WorkspaceIdentityOcrRecord *record)
|
||
{
|
||
if (record == NULL) return;
|
||
identity_ocr_run_free(record->run);
|
||
g_free(record->person_identifier);
|
||
g_free(record->executed_at);
|
||
g_free(record->text_relative_path);
|
||
g_free(record->text_sha256);
|
||
g_free(record->tsv_relative_path);
|
||
g_free(record->tsv_sha256);
|
||
g_free(record);
|
||
}
|
||
|
||
static void workspace_clear_box(GtkBox *box)
|
||
{
|
||
GtkWidget *child = gtk_widget_get_first_child(GTK_WIDGET(box));
|
||
while (child != NULL) {
|
||
GtkWidget *next = gtk_widget_get_next_sibling(child);
|
||
gtk_box_remove(box, child);
|
||
child = next;
|
||
}
|
||
}
|
||
|
||
static void workspace_render_identity_ocr(Workspace *workspace)
|
||
{
|
||
guint selected = gtk_drop_down_get_selected(
|
||
workspace->identity_ocr_selector);
|
||
workspace_clear_box(workspace->identity_ocr_fields);
|
||
gtk_text_buffer_set_text(workspace->identity_ocr_raw, "", -1);
|
||
gtk_text_buffer_set_text(workspace->identity_ocr_corrected, "", -1);
|
||
ocr_provenance_overlay_clear(workspace->identity_ocr_overlay,
|
||
selected + 1U);
|
||
if (workspace->identity_ocr_records == NULL ||
|
||
selected >= workspace->identity_ocr_records->len) {
|
||
gtk_label_set_text(workspace->identity_ocr_summary,
|
||
"Aucune analyse OCR sélectionnée.");
|
||
gtk_widget_set_sensitive(workspace->revise_identity_button, FALSE);
|
||
return;
|
||
}
|
||
WorkspaceIdentityOcrRecord *record = g_ptr_array_index(
|
||
workspace->identity_ocr_records, selected);
|
||
IdentityOcrRun *run = record->run;
|
||
char *summary = g_strdup_printf(
|
||
"Run actif : %s\nUTC : %s\nPersonne liée : %s\n"
|
||
"Document : %s — %s — page %u\nLangues : %s\n"
|
||
"Notes : %s\nArtefact texte : %s\nSHA-256 texte : %s\n"
|
||
"Artefact TSV : %s\nSHA-256 TSV : %s\nSHA-256 preuve : %s",
|
||
identity_ocr_run_get_identifier(run),
|
||
record->executed_at != NULL ? record->executed_at : "inconnue",
|
||
record->person_identifier != NULL
|
||
? record->person_identifier : "indisponible",
|
||
identity_ocr_run_get_document_type(run),
|
||
identity_ocr_run_get_document_side(run),
|
||
identity_ocr_run_get_page(run),
|
||
identity_ocr_run_get_languages(run),
|
||
identity_ocr_run_get_factual_notes(run) != NULL
|
||
? identity_ocr_run_get_factual_notes(run) : "Aucune note",
|
||
record->text_relative_path, record->text_sha256,
|
||
record->tsv_relative_path, record->tsv_sha256,
|
||
identity_ocr_run_get_expected_sha256(run));
|
||
gtk_label_set_text(workspace->identity_ocr_summary, summary);
|
||
g_free(summary);
|
||
gtk_text_buffer_set_text(workspace->identity_ocr_raw,
|
||
identity_ocr_run_get_raw_text(run), -1);
|
||
gtk_text_buffer_set_text(workspace->identity_ocr_corrected,
|
||
identity_ocr_run_get_corrected_transcription(run) != NULL
|
||
? identity_ocr_run_get_corrected_transcription(run)
|
||
: "Aucune transcription corrigée", -1);
|
||
const GPtrArray *fields = identity_ocr_run_get_fields(run);
|
||
for (guint index = 0; fields != NULL && index < fields->len; index++) {
|
||
IdentityFieldObservation *field = g_ptr_array_index(
|
||
(GPtrArray *) fields, index);
|
||
char *field_text = g_strdup_printf(
|
||
"%s — brut : %s — corrigé : %s — origine : %s — statut : %d",
|
||
identity_field_observation_get_code(field),
|
||
identity_field_observation_get_raw_value(field) != NULL
|
||
? identity_field_observation_get_raw_value(field) : "absent",
|
||
identity_field_observation_get_corrected_value(field) != NULL
|
||
? identity_field_observation_get_corrected_value(field)
|
||
: "absente",
|
||
identity_field_observation_get_origin(field),
|
||
identity_field_observation_get_status(field));
|
||
GtkWidget *label = gtk_label_new(field_text);
|
||
gtk_label_set_wrap(GTK_LABEL(label), TRUE);
|
||
gtk_label_set_xalign(GTK_LABEL(label), 0.0f);
|
||
gtk_box_append(workspace->identity_ocr_fields, label);
|
||
g_free(field_text);
|
||
if (!ocr_provenance_overlay_has_region(
|
||
workspace->identity_ocr_overlay))
|
||
ocr_provenance_overlay_set_field(
|
||
workspace->identity_ocr_overlay, field, selected + 1U);
|
||
}
|
||
gtk_widget_set_sensitive(workspace->revise_identity_button, TRUE);
|
||
}
|
||
|
||
static void workspace_on_identity_ocr_selected(
|
||
GObject *object, GParamSpec *pspec, gpointer data)
|
||
{
|
||
(void) object;
|
||
(void) pspec;
|
||
workspace_render_identity_ocr(data);
|
||
}
|
||
|
||
static void workspace_on_identity_ocr_action(
|
||
GtkButton *button, gpointer data)
|
||
{
|
||
Workspace *workspace = data;
|
||
gboolean revise = button ==
|
||
GTK_BUTTON(workspace->revise_identity_button);
|
||
const char *run_identifier = NULL;
|
||
if (revise && workspace->identity_ocr_records != NULL) {
|
||
guint selected = gtk_drop_down_get_selected(
|
||
workspace->identity_ocr_selector);
|
||
if (selected < workspace->identity_ocr_records->len) {
|
||
WorkspaceIdentityOcrRecord *record = g_ptr_array_index(
|
||
workspace->identity_ocr_records, selected);
|
||
run_identifier =
|
||
identity_ocr_run_get_identifier(record->run);
|
||
}
|
||
}
|
||
if (workspace->identity_ocr_callback != NULL &&
|
||
(!revise || run_identifier != NULL))
|
||
workspace->identity_ocr_callback(
|
||
workspace->selected_evidence_identifier,
|
||
revise, run_identifier, workspace->identity_ocr_user_data);
|
||
}
|
||
|
||
Workspace *workspace_new(TaskManager *task_manager)
|
||
{
|
||
GtkWidget *evidence_content = NULL;
|
||
GtkWidget *evidence_separator = NULL;
|
||
GtkWidget *evidence_grid = NULL;
|
||
|
||
Workspace *workspace = NULL;
|
||
|
||
if (task_manager == NULL) return 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,
|
||
8
|
||
);
|
||
|
||
if (evidence_content == NULL)
|
||
{
|
||
workspace_free(
|
||
workspace
|
||
);
|
||
|
||
return NULL;
|
||
}
|
||
|
||
gtk_widget_set_margin_start(
|
||
evidence_content,
|
||
12
|
||
);
|
||
|
||
gtk_widget_set_margin_end(
|
||
evidence_content,
|
||
12
|
||
);
|
||
|
||
gtk_widget_set_margin_top(
|
||
evidence_content,
|
||
12
|
||
);
|
||
|
||
gtk_widget_set_margin_bottom(
|
||
evidence_content,
|
||
12
|
||
);
|
||
|
||
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->evidence_preview = evidence_preview_widget_new(
|
||
task_manager, NULL, NULL);
|
||
if (workspace->evidence_preview == NULL) {
|
||
workspace_free(workspace);
|
||
return NULL;
|
||
}
|
||
gtk_box_append(GTK_BOX(evidence_content),
|
||
evidence_preview_widget_get_widget(workspace->evidence_preview));
|
||
|
||
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
|
||
);
|
||
|
||
workspace->edit_evidence_button = gtk_button_new_with_label(
|
||
"Modifier les métadonnées");
|
||
gtk_widget_set_halign(workspace->edit_evidence_button, GTK_ALIGN_START);
|
||
gtk_widget_set_sensitive(workspace->edit_evidence_button, FALSE);
|
||
gtk_widget_set_tooltip_text(workspace->edit_evidence_button,
|
||
"Corriger le classement sans modifier le fichier original");
|
||
g_signal_connect(workspace->edit_evidence_button, "clicked",
|
||
G_CALLBACK(workspace_on_edit_evidence_clicked), workspace);
|
||
gtk_box_append(GTK_BOX(evidence_content), workspace->edit_evidence_button);
|
||
workspace->analyze_eml_button = gtk_button_new_with_label(
|
||
"Analyser l’e-mail");
|
||
gtk_widget_set_halign(workspace->analyze_eml_button, GTK_ALIGN_START);
|
||
gtk_widget_set_sensitive(workspace->analyze_eml_button, FALSE);
|
||
gtk_widget_set_tooltip_text(workspace->analyze_eml_button,
|
||
"Vérifier la preuve puis lancer l’analyse complète en arrière-plan");
|
||
g_signal_connect(workspace->analyze_eml_button, "clicked",
|
||
G_CALLBACK(workspace_on_analyze_eml_clicked), workspace);
|
||
gtk_box_append(GTK_BOX(evidence_content), workspace->analyze_eml_button);
|
||
workspace->analyze_rib_button = gtk_button_new_with_label(
|
||
"Analyser le RIB par OCR");
|
||
gtk_widget_set_halign(workspace->analyze_rib_button, GTK_ALIGN_START);
|
||
gtk_widget_set_sensitive(workspace->analyze_rib_button, FALSE);
|
||
g_signal_connect(workspace->analyze_rib_button, "clicked",
|
||
G_CALLBACK(workspace_on_analyze_rib_clicked), workspace);
|
||
gtk_box_append(GTK_BOX(evidence_content), workspace->analyze_rib_button);
|
||
workspace->extract_metadata_button = gtk_button_new_with_label(
|
||
"Extraire les métadonnées");
|
||
gtk_widget_set_halign(workspace->extract_metadata_button, GTK_ALIGN_START);
|
||
gtk_widget_set_sensitive(workspace->extract_metadata_button, FALSE);
|
||
gtk_widget_set_tooltip_text(workspace->extract_metadata_button,
|
||
"Analyser localement une copie avec ExifTool");
|
||
g_signal_connect(workspace->extract_metadata_button, "clicked",
|
||
G_CALLBACK(workspace_on_extract_metadata_clicked), workspace);
|
||
gtk_box_append(GTK_BOX(evidence_content),
|
||
workspace->extract_metadata_button);
|
||
workspace->recover_pdf_password_button = gtk_button_new_with_label(
|
||
"Récupérer le mot de passe PDF");
|
||
gtk_widget_set_halign(workspace->recover_pdf_password_button,
|
||
GTK_ALIGN_START);
|
||
gtk_widget_set_sensitive(workspace->recover_pdf_password_button, FALSE);
|
||
g_signal_connect(workspace->recover_pdf_password_button, "clicked",
|
||
G_CALLBACK(workspace_on_recover_pdf_password_clicked), workspace);
|
||
gtk_box_append(GTK_BOX(evidence_content),
|
||
workspace->recover_pdf_password_button);
|
||
workspace->analyze_identity_button = gtk_button_new_with_label(
|
||
"Analyser comme document d’identité");
|
||
gtk_widget_set_name(workspace->analyze_identity_button,
|
||
"workspace-analyze-identity");
|
||
gtk_widget_set_halign(workspace->analyze_identity_button,
|
||
GTK_ALIGN_START);
|
||
gtk_widget_set_sensitive(workspace->analyze_identity_button, FALSE);
|
||
g_signal_connect(workspace->analyze_identity_button, "clicked",
|
||
G_CALLBACK(workspace_on_identity_ocr_action), workspace);
|
||
gtk_box_append(GTK_BOX(evidence_content),
|
||
workspace->analyze_identity_button);
|
||
|
||
workspace->identity_ocr_section =
|
||
gtk_box_new(GTK_ORIENTATION_VERTICAL, 8);
|
||
gtk_widget_set_name(workspace->identity_ocr_section,
|
||
"workspace-identity-ocr-section");
|
||
gtk_box_append(GTK_BOX(workspace->identity_ocr_section),
|
||
gtk_label_new("Analyse OCR d’identité"));
|
||
workspace->identity_ocr_labels = gtk_string_list_new(NULL);
|
||
workspace->identity_ocr_selector = GTK_DROP_DOWN(
|
||
gtk_drop_down_new(G_LIST_MODEL(g_object_ref(
|
||
workspace->identity_ocr_labels)), NULL));
|
||
gtk_widget_set_name(GTK_WIDGET(workspace->identity_ocr_selector),
|
||
"workspace-identity-ocr-selector");
|
||
gtk_box_append(GTK_BOX(workspace->identity_ocr_section),
|
||
GTK_WIDGET(workspace->identity_ocr_selector));
|
||
workspace->identity_ocr_summary = GTK_LABEL(gtk_label_new(NULL));
|
||
gtk_widget_set_name(GTK_WIDGET(workspace->identity_ocr_summary),
|
||
"workspace-identity-ocr-active-run");
|
||
gtk_label_set_wrap(workspace->identity_ocr_summary, TRUE);
|
||
gtk_label_set_xalign(workspace->identity_ocr_summary, 0.0f);
|
||
gtk_box_append(GTK_BOX(workspace->identity_ocr_section),
|
||
GTK_WIDGET(workspace->identity_ocr_summary));
|
||
GtkWidget *raw_view = gtk_text_view_new();
|
||
gtk_widget_set_name(raw_view, "workspace-identity-ocr-raw");
|
||
gtk_text_view_set_editable(GTK_TEXT_VIEW(raw_view), FALSE);
|
||
workspace->identity_ocr_raw =
|
||
gtk_text_view_get_buffer(GTK_TEXT_VIEW(raw_view));
|
||
gtk_box_append(GTK_BOX(workspace->identity_ocr_section),
|
||
gtk_label_new("Texte OCR brut"));
|
||
gtk_box_append(GTK_BOX(workspace->identity_ocr_section), raw_view);
|
||
GtkWidget *corrected_view = gtk_text_view_new();
|
||
gtk_widget_set_name(corrected_view,
|
||
"workspace-identity-ocr-corrected");
|
||
gtk_text_view_set_editable(GTK_TEXT_VIEW(corrected_view), FALSE);
|
||
workspace->identity_ocr_corrected =
|
||
gtk_text_view_get_buffer(GTK_TEXT_VIEW(corrected_view));
|
||
gtk_box_append(GTK_BOX(workspace->identity_ocr_section),
|
||
gtk_label_new("Transcription corrigée"));
|
||
gtk_box_append(GTK_BOX(workspace->identity_ocr_section),
|
||
corrected_view);
|
||
workspace->identity_ocr_fields = GTK_BOX(
|
||
gtk_box_new(GTK_ORIENTATION_VERTICAL, 6));
|
||
gtk_box_append(GTK_BOX(workspace->identity_ocr_section),
|
||
GTK_WIDGET(workspace->identity_ocr_fields));
|
||
workspace->identity_ocr_overlay = ocr_provenance_overlay_new();
|
||
gtk_box_append(GTK_BOX(workspace->identity_ocr_section),
|
||
ocr_provenance_overlay_get_widget(
|
||
workspace->identity_ocr_overlay));
|
||
GtkWidget *identity_actions =
|
||
gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
|
||
workspace->revise_identity_button =
|
||
gtk_button_new_with_label("Réviser l’analyse OCR");
|
||
workspace->rerun_identity_button =
|
||
gtk_button_new_with_label("Relancer une nouvelle analyse OCR");
|
||
gtk_widget_set_name(workspace->revise_identity_button,
|
||
"workspace-revise-identity-ocr");
|
||
gtk_widget_set_name(workspace->rerun_identity_button,
|
||
"workspace-rerun-identity-ocr");
|
||
g_signal_connect(workspace->revise_identity_button, "clicked",
|
||
G_CALLBACK(workspace_on_identity_ocr_action), workspace);
|
||
g_signal_connect(workspace->rerun_identity_button, "clicked",
|
||
G_CALLBACK(workspace_on_identity_ocr_action), workspace);
|
||
gtk_box_append(GTK_BOX(identity_actions),
|
||
workspace->revise_identity_button);
|
||
gtk_box_append(GTK_BOX(identity_actions),
|
||
workspace->rerun_identity_button);
|
||
gtk_box_append(GTK_BOX(workspace->identity_ocr_section),
|
||
identity_actions);
|
||
gtk_widget_set_visible(workspace->identity_ocr_section, FALSE);
|
||
gtk_box_append(GTK_BOX(evidence_content),
|
||
workspace->identity_ocr_section);
|
||
g_signal_connect(workspace->identity_ocr_selector,
|
||
"notify::selected",
|
||
G_CALLBACK(workspace_on_identity_ocr_selected), workspace);
|
||
|
||
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"
|
||
);
|
||
GtkWidget *observations_title = gtk_label_new(
|
||
"Observations extraites");
|
||
gtk_label_set_xalign(GTK_LABEL(observations_title), 0.0f);
|
||
gtk_widget_add_css_class(observations_title, "heading");
|
||
workspace->evidence_observations_box =
|
||
gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
|
||
gtk_grid_attach(GTK_GRID(evidence_grid), observations_title, 0, 11, 1, 1);
|
||
gtk_grid_attach(GTK_GRID(evidence_grid),
|
||
workspace->evidence_observations_box, 1, 11, 1, 1);
|
||
|
||
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->evidence_observations_box == 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);
|
||
workspace->relation_evidences_label = gtk_label_new(
|
||
"Pièces jointes : chargement…");
|
||
workspace->edit_relation_button = gtk_button_new_with_label("Modifier");
|
||
workspace->delete_relation_button = gtk_button_new_with_label(
|
||
"Supprimer la relation");
|
||
workspace->close_relation_details_button =
|
||
gtk_button_new_from_icon_name("window-close-symbolic");
|
||
|
||
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->relation_evidences_label == NULL ||
|
||
workspace->edit_relation_button == NULL ||
|
||
workspace->delete_relation_button == NULL ||
|
||
workspace->close_relation_details_button == 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);
|
||
/* `view` fournit le fond opaque du thème, comme le volet d'entité. */
|
||
gtk_widget_add_css_class(workspace->relation_details_panel, "view");
|
||
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_widget_set_margin_start(workspace->relation_evidences_label, 16);
|
||
gtk_widget_set_margin_end(workspace->relation_evidences_label, 16);
|
||
gtk_widget_set_margin_bottom(workspace->relation_evidences_label, 12);
|
||
gtk_label_set_xalign(GTK_LABEL(workspace->relation_evidences_label), 0.0F);
|
||
gtk_label_set_wrap(GTK_LABEL(workspace->relation_evidences_label), TRUE);
|
||
gtk_label_set_selectable(GTK_LABEL(workspace->relation_evidences_label),
|
||
TRUE);
|
||
gtk_box_append(
|
||
GTK_BOX(workspace->relation_details_panel),
|
||
workspace->relation_details_title_label
|
||
);
|
||
gtk_widget_set_tooltip_text(workspace->close_relation_details_button,
|
||
"Fermer le volet");
|
||
gtk_widget_add_css_class(workspace->close_relation_details_button, "flat");
|
||
gtk_box_append(GTK_BOX(workspace->relation_details_panel),
|
||
workspace->close_relation_details_button);
|
||
gtk_box_append(
|
||
GTK_BOX(workspace->relation_details_panel),
|
||
workspace->relation_details_summary_label
|
||
);
|
||
gtk_box_append(GTK_BOX(workspace->relation_details_panel),
|
||
workspace->relation_evidences_label);
|
||
gtk_box_append(GTK_BOX(workspace->relation_details_panel),
|
||
workspace->edit_relation_button);
|
||
gtk_widget_add_css_class(workspace->delete_relation_button,
|
||
"destructive-action");
|
||
gtk_box_append(GTK_BOX(workspace->relation_details_panel),
|
||
workspace->delete_relation_button);
|
||
g_signal_connect(workspace->edit_relation_button, "clicked",
|
||
G_CALLBACK(workspace_on_edit_relation_clicked), workspace);
|
||
g_signal_connect(workspace->delete_relation_button, "clicked",
|
||
G_CALLBACK(workspace_on_delete_relation_clicked), workspace);
|
||
g_signal_connect(workspace->close_relation_details_button, "clicked",
|
||
G_CALLBACK(workspace_on_close_relation_details_clicked), workspace);
|
||
gtk_widget_set_visible(workspace->relation_details_panel, FALSE);
|
||
gtk_widget_set_can_target(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
|
||
);
|
||
investigation_graph_view_set_transform_changed_callback(
|
||
workspace->graph_view, workspace_on_graph_transform_changed, workspace);
|
||
investigation_graph_view_set_extraction_drop_callback(
|
||
workspace->graph_view, workspace_on_extraction_dropped, 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
|
||
);
|
||
|
||
entity_details_panel_set_person_role_callback(
|
||
workspace->entity_details_panel, workspace_on_person_role_changed,
|
||
workspace);
|
||
entity_details_panel_set_person_confidence_callback(
|
||
workspace->entity_details_panel,
|
||
workspace_on_person_confidence_changed, workspace);
|
||
entity_details_panel_set_person_name_callback(
|
||
workspace->entity_details_panel, workspace_on_person_name_changed,
|
||
workspace);
|
||
entity_details_panel_set_person_evidence_callback(
|
||
workspace->entity_details_panel,
|
||
workspace_on_person_evidence_requested, workspace);
|
||
entity_details_panel_set_evidence_activated_callback(
|
||
workspace->entity_details_panel, workspace_on_evidence_activated,
|
||
workspace);
|
||
entity_details_panel_set_osint_callback(workspace->entity_details_panel,
|
||
workspace_on_entity_osint_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;
|
||
}
|
||
|
||
static void workspace_on_remove_observation_clicked(GtkButton *button,
|
||
gpointer user_data)
|
||
{
|
||
Workspace *workspace = user_data;
|
||
const char *identifier = g_object_get_data(G_OBJECT(button),
|
||
"observation-identifier");
|
||
if (workspace != NULL && workspace->observation_remove_callback != NULL)
|
||
workspace->observation_remove_callback(identifier,
|
||
workspace->observation_remove_user_data);
|
||
}
|
||
|
||
void workspace_set_evidence_observations(Workspace *workspace,
|
||
const GPtrArray *observations)
|
||
{
|
||
if (workspace == NULL || workspace->evidence_observations_box == NULL)
|
||
return;
|
||
GtkWidget *child = gtk_widget_get_first_child(
|
||
workspace->evidence_observations_box);
|
||
while (child != NULL)
|
||
{
|
||
GtkWidget *next = gtk_widget_get_next_sibling(child);
|
||
gtk_box_remove(GTK_BOX(workspace->evidence_observations_box), child);
|
||
child = next;
|
||
}
|
||
if (observations == NULL || observations->len == 0)
|
||
{
|
||
gtk_box_append(GTK_BOX(workspace->evidence_observations_box),
|
||
gtk_label_new("Aucune observation confirmée pour cette preuve."));
|
||
return;
|
||
}
|
||
for (guint index = 0; index < observations->len; index++)
|
||
{
|
||
const EvidenceObservation *item = g_ptr_array_index(
|
||
(GPtrArray *) observations, index);
|
||
GtkWidget *row = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
|
||
char *text = g_strdup_printf(
|
||
"%s\nType : %s — rôle : %s\nSource : %s #%u — provenance : %s\n"
|
||
"Validation : %s — intégrée : %s\nGraphe : %s",
|
||
item->value, item->type_identifier, item->role,
|
||
item->source_header, item->occurrence, item->provenance_kind,
|
||
item->verification_status, item->integrated_at,
|
||
item->entity_identifier == NULL ? "Non ajoutée" :
|
||
(g_strcmp0(item->promotion_kind, "created") == 0
|
||
? "Entité créée" :
|
||
g_strcmp0(item->promotion_kind, "reused") == 0
|
||
? "Entité existante réutilisée" : "Promotion historique"));
|
||
GtkWidget *label = gtk_label_new(text);
|
||
gtk_label_set_xalign(GTK_LABEL(label), 0.0f);
|
||
gtk_label_set_selectable(GTK_LABEL(label), TRUE);
|
||
gtk_widget_set_hexpand(label, TRUE);
|
||
gtk_box_append(GTK_BOX(row), label);
|
||
if (item->entity_identifier != NULL)
|
||
{
|
||
GtkWidget *remove = gtk_button_new_with_label("Retirer du graphe");
|
||
gtk_widget_add_css_class(remove, "flat");
|
||
gtk_widget_set_tooltip_text(remove,
|
||
"Détache cette observation sans supprimer son contenu.");
|
||
g_object_set_data_full(G_OBJECT(remove), "observation-identifier",
|
||
g_strdup(item->identifier), g_free);
|
||
g_signal_connect(remove, "clicked",
|
||
G_CALLBACK(workspace_on_remove_observation_clicked), workspace);
|
||
gtk_box_append(GTK_BOX(row), remove);
|
||
}
|
||
gtk_box_append(GTK_BOX(workspace->evidence_observations_box), row);
|
||
g_free(text);
|
||
}
|
||
}
|
||
|
||
void workspace_set_observation_remove_callback(Workspace *workspace,
|
||
WorkspaceObservationRemoveCallback callback, gpointer user_data)
|
||
{
|
||
if (workspace == NULL) return;
|
||
workspace->observation_remove_callback = callback;
|
||
workspace->observation_remove_user_data = user_data;
|
||
}
|
||
|
||
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 (workspace->edit_evidence_button != NULL)
|
||
gtk_widget_set_sensitive(workspace->edit_evidence_button, FALSE);
|
||
if (workspace->analyze_eml_button != NULL)
|
||
gtk_widget_set_sensitive(workspace->analyze_eml_button, FALSE);
|
||
if (workspace->analyze_rib_button != NULL)
|
||
gtk_widget_set_sensitive(workspace->analyze_rib_button, FALSE);
|
||
if (workspace->extract_metadata_button != NULL)
|
||
gtk_widget_set_sensitive(workspace->extract_metadata_button, FALSE);
|
||
if (workspace->recover_pdf_password_button != NULL)
|
||
gtk_widget_set_sensitive(workspace->recover_pdf_password_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),
|
||
node_type == INVESTIGATION_NODE_FILE
|
||
? WORKSPACE_PAGE_EVIDENCE_INFORMATION
|
||
: WORKSPACE_PAGE_NODE_INFORMATION
|
||
);
|
||
|
||
if (node_type == INVESTIGATION_NODE_FILE)
|
||
{
|
||
gtk_label_set_text(GTK_LABEL(workspace->evidence_name_label),
|
||
node_name != NULL ? node_name : "Fichier");
|
||
workspace_set_evidence_preview(workspace, node_path, node_name);
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
workspace_clear_evidence_preview(workspace);
|
||
workspace_set_identity_ocr_runs(workspace, NULL);
|
||
|
||
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
|
||
);
|
||
gtk_widget_set_sensitive(workspace->edit_evidence_button, TRUE);
|
||
{
|
||
const char *name = evidence_record_get_original_name(evidence_record);
|
||
char *lower = name != NULL ? g_ascii_strdown(name, -1) : NULL;
|
||
gtk_widget_set_sensitive(workspace->analyze_eml_button,
|
||
lower != NULL && g_str_has_suffix(lower, ".eml"));
|
||
gtk_widget_set_sensitive(workspace->analyze_rib_button,
|
||
lower != NULL && (g_str_has_suffix(lower, ".jpg") ||
|
||
g_str_has_suffix(lower, ".jpeg") || g_str_has_suffix(lower, ".png")));
|
||
gtk_widget_set_sensitive(workspace->extract_metadata_button,
|
||
lower != NULL && (g_str_has_suffix(lower, ".jpg") ||
|
||
g_str_has_suffix(lower, ".jpeg") ||
|
||
g_str_has_suffix(lower, ".png") ||
|
||
g_str_has_suffix(lower, ".mov") ||
|
||
g_str_has_suffix(lower, ".mp4") ||
|
||
g_str_has_suffix(lower, ".pdf")));
|
||
gtk_widget_set_sensitive(workspace->recover_pdf_password_button,
|
||
lower != NULL && g_str_has_suffix(lower, ".pdf"));
|
||
gtk_widget_set_sensitive(workspace->analyze_identity_button,
|
||
evidence_identity_ocr_dialog_file_is_compatible(name) ||
|
||
evidence_identity_ocr_dialog_file_is_compatible(
|
||
evidence_record_get_internal_name(evidence_record)));
|
||
g_free(lower);
|
||
}
|
||
|
||
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
|
||
);
|
||
}
|
||
|
||
void workspace_set_eml_analysis_available(
|
||
Workspace *workspace,
|
||
gboolean available)
|
||
{
|
||
if (workspace != NULL && workspace->analyze_eml_button != NULL)
|
||
gtk_widget_set_sensitive(
|
||
workspace->analyze_eml_button, available);
|
||
}
|
||
|
||
void workspace_set_identity_ocr_runs(
|
||
Workspace *workspace, GPtrArray *owned_records)
|
||
{
|
||
if (workspace == NULL) {
|
||
g_clear_pointer(&owned_records, g_ptr_array_unref);
|
||
return;
|
||
}
|
||
g_clear_pointer(&workspace->identity_ocr_records, g_ptr_array_unref);
|
||
workspace->identity_ocr_records = owned_records;
|
||
guint old_count = g_list_model_get_n_items(
|
||
G_LIST_MODEL(workspace->identity_ocr_labels));
|
||
gtk_string_list_splice(workspace->identity_ocr_labels,
|
||
0, old_count, NULL);
|
||
for (guint index = 0;
|
||
owned_records != NULL && index < owned_records->len; index++) {
|
||
WorkspaceIdentityOcrRecord *record =
|
||
g_ptr_array_index(owned_records, index);
|
||
char *short_id = g_strndup(
|
||
identity_ocr_run_get_identifier(record->run), 8);
|
||
char *label = g_strdup_printf(
|
||
"%s — %s/%s — page %u — %s",
|
||
record->executed_at != NULL ? record->executed_at : "UTC inconnue",
|
||
identity_ocr_run_get_document_type(record->run),
|
||
identity_ocr_run_get_document_side(record->run),
|
||
identity_ocr_run_get_page(record->run), short_id);
|
||
gtk_string_list_append(workspace->identity_ocr_labels, label);
|
||
g_free(label);
|
||
g_free(short_id);
|
||
}
|
||
gboolean available =
|
||
owned_records != NULL && owned_records->len > 0;
|
||
gtk_widget_set_visible(workspace->identity_ocr_section, available);
|
||
gtk_widget_set_sensitive(workspace->revise_identity_button, available);
|
||
gtk_widget_set_sensitive(workspace->rerun_identity_button, available);
|
||
if (available)
|
||
gtk_drop_down_set_selected(workspace->identity_ocr_selector,
|
||
owned_records->len - 1);
|
||
else
|
||
workspace_render_identity_ocr(workspace);
|
||
}
|
||
|
||
void workspace_set_identity_ocr_callback(
|
||
Workspace *workspace, WorkspaceIdentityOcrCallback callback,
|
||
gpointer user_data)
|
||
{
|
||
if (workspace == NULL) return;
|
||
workspace->identity_ocr_callback = callback;
|
||
workspace->identity_ocr_user_data = user_data;
|
||
}
|
||
|
||
void workspace_set_evidence_preview(Workspace *workspace,
|
||
const char *file_path, const char *display_name)
|
||
{
|
||
char *contents = NULL;
|
||
gsize length = 0;
|
||
char *sha256 = NULL;
|
||
char *root = NULL;
|
||
char *name = NULL;
|
||
EvidencePreviewRequest *request = NULL;
|
||
if (workspace == NULL) return;
|
||
workspace_clear_evidence_preview(workspace);
|
||
if (file_path == NULL || !g_file_get_contents(
|
||
file_path, &contents, &length, NULL)) return;
|
||
sha256 = g_compute_checksum_for_data(G_CHECKSUM_SHA256,
|
||
(const guchar *) contents, length);
|
||
root = g_path_get_dirname(file_path);
|
||
name = g_path_get_basename(file_path);
|
||
request = evidence_preview_request_new(root,
|
||
display_name != NULL ? display_name : name, name, sha256, NULL, 0);
|
||
evidence_preview_widget_show(workspace->evidence_preview, request,
|
||
display_name);
|
||
evidence_preview_request_free(request);
|
||
g_free(name);
|
||
g_free(root);
|
||
g_free(sha256);
|
||
g_free(contents);
|
||
}
|
||
|
||
void workspace_show_evidence_preview(Workspace *workspace,
|
||
const char *investigation_root, const EvidenceRecord *record)
|
||
{
|
||
EvidencePreviewRequest *request;
|
||
char *size;
|
||
char *metadata;
|
||
if (workspace == NULL) return;
|
||
if (investigation_root == NULL || record == NULL) {
|
||
workspace_clear_evidence_preview(workspace);
|
||
return;
|
||
}
|
||
size = g_format_size(evidence_record_get_size_bytes(record));
|
||
metadata = g_strdup_printf(
|
||
"Nom original : %s\nUUID : %s\nType métier : %s\n"
|
||
"MIME enregistré : %s\nTaille : %s\nSHA-256 : %s\n"
|
||
"Intégrité enregistrée : %s",
|
||
evidence_record_get_original_name(record),
|
||
evidence_record_get_identifier(record),
|
||
evidence_record_get_type_identifier(record),
|
||
evidence_record_get_mime_type(record) != NULL
|
||
? evidence_record_get_mime_type(record) : "Non renseigné",
|
||
size, evidence_record_get_sha256(record),
|
||
workspace_get_integrity_status_text(
|
||
evidence_record_get_integrity_status(record)));
|
||
request = evidence_preview_request_new(investigation_root,
|
||
evidence_record_get_identifier(record),
|
||
evidence_record_get_relative_path(record),
|
||
evidence_record_get_sha256(record),
|
||
evidence_record_get_mime_type(record), 0);
|
||
evidence_preview_widget_show(workspace->evidence_preview,
|
||
request, metadata);
|
||
evidence_preview_request_free(request);
|
||
g_free(metadata);
|
||
g_free(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
|
||
);
|
||
|
||
workspace->saved_graph_transform_valid =
|
||
investigation_graph_view_get_view_transform(
|
||
workspace->graph_view,
|
||
&workspace->saved_graph_zoom,
|
||
&workspace->saved_graph_offset_x,
|
||
&workspace->saved_graph_offset_y);
|
||
|
||
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 l’enquê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
|
||
);
|
||
|
||
if (workspace->saved_graph_transform_valid)
|
||
{
|
||
investigation_graph_view_set_view_transform(
|
||
workspace->graph_view,
|
||
workspace->saved_graph_zoom,
|
||
workspace->saved_graph_offset_x,
|
||
workspace->saved_graph_offset_y);
|
||
workspace->saved_graph_transform_valid = FALSE;
|
||
}
|
||
|
||
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
|
||
);
|
||
}
|
||
|
||
gboolean workspace_get_graph_view_transform(
|
||
Workspace *workspace, double *zoom, double *offset_x, double *offset_y)
|
||
{
|
||
if (workspace == NULL || workspace->graph_state != WORKSPACE_GRAPH_STATE_READY)
|
||
return FALSE;
|
||
return investigation_graph_view_get_view_transform(
|
||
workspace->graph_view, zoom, offset_x, offset_y);
|
||
}
|
||
|
||
void workspace_set_graph_view_transform(
|
||
Workspace *workspace, double zoom, double offset_x, double offset_y)
|
||
{
|
||
if (workspace == NULL || workspace->graph_state != WORKSPACE_GRAPH_STATE_READY)
|
||
return;
|
||
investigation_graph_view_set_view_transform(
|
||
workspace->graph_view, zoom, offset_x, offset_y);
|
||
}
|
||
|
||
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 l’enquête"
|
||
);
|
||
|
||
gtk_label_set_text(
|
||
GTK_LABEL(
|
||
workspace->graph_details_label
|
||
),
|
||
message != NULL &&
|
||
message[0] != '\0'
|
||
? message
|
||
: "Aucun détail supplémentaire n’est 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_edit_evidence_callback(
|
||
Workspace *workspace, WorkspaceEditEvidenceCallback callback,
|
||
gpointer user_data
|
||
)
|
||
{
|
||
if (workspace == NULL) return;
|
||
workspace->edit_evidence_callback = callback;
|
||
workspace->edit_evidence_user_data = user_data;
|
||
}
|
||
void workspace_set_analyze_eml_callback(Workspace *workspace,
|
||
WorkspaceAnalyzeEmlCallback callback, gpointer user_data)
|
||
{
|
||
if (workspace == NULL) return;
|
||
workspace->analyze_eml_callback = callback;
|
||
workspace->analyze_eml_user_data = user_data;
|
||
}
|
||
void workspace_set_analyze_rib_callback(Workspace *workspace,
|
||
WorkspaceAnalyzeRibCallback callback, gpointer user_data)
|
||
{
|
||
if (workspace == NULL) return;
|
||
workspace->analyze_rib_callback = callback;
|
||
workspace->analyze_rib_user_data = user_data;
|
||
}
|
||
void workspace_set_extract_metadata_callback(Workspace *workspace,
|
||
WorkspaceExtractMetadataCallback callback, gpointer user_data)
|
||
{
|
||
if (workspace == NULL) return;
|
||
workspace->extract_metadata_callback = callback;
|
||
workspace->extract_metadata_user_data = user_data;
|
||
}
|
||
void workspace_set_recover_pdf_password_callback(Workspace *workspace,
|
||
WorkspaceRecoverPdfPasswordCallback callback, gpointer user_data)
|
||
{
|
||
if (workspace == NULL) return;
|
||
workspace->recover_pdf_password_callback = callback;
|
||
workspace->recover_pdf_password_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_graph_transform_changed_callback(
|
||
Workspace *workspace,
|
||
WorkspaceGraphTransformChangedCallback callback,
|
||
gpointer user_data)
|
||
{
|
||
if (workspace == NULL) return;
|
||
workspace->graph_transform_changed_callback = callback;
|
||
workspace->graph_transform_changed_user_data = user_data;
|
||
}
|
||
|
||
void workspace_set_extraction_drop_callback(
|
||
Workspace *workspace,
|
||
WorkspaceExtractionDropCallback callback,
|
||
gpointer user_data)
|
||
{
|
||
if (workspace == NULL) return;
|
||
workspace->extraction_drop_callback = callback;
|
||
workspace->extraction_drop_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_set_edit_relation_callback(Workspace *workspace,
|
||
WorkspaceEditRelationCallback callback, gpointer user_data)
|
||
{
|
||
if (workspace == NULL) return;
|
||
workspace->edit_relation_callback = callback;
|
||
workspace->edit_relation_user_data = user_data;
|
||
}
|
||
|
||
void workspace_set_delete_relation_callback(Workspace *workspace,
|
||
WorkspaceDeleteRelationCallback callback, gpointer user_data)
|
||
{
|
||
if (workspace == NULL) return;
|
||
workspace->delete_relation_callback = callback;
|
||
workspace->delete_relation_user_data = user_data;
|
||
}
|
||
|
||
void workspace_set_relation_selected_callback(Workspace *workspace,
|
||
WorkspaceRelationSelectedCallback callback, gpointer user_data)
|
||
{
|
||
if (workspace == NULL) return;
|
||
workspace->relation_selected_callback = callback;
|
||
workspace->relation_selected_user_data = user_data;
|
||
}
|
||
|
||
void workspace_set_relation_evidences(Workspace *workspace,
|
||
const GPtrArray *evidence_records)
|
||
{
|
||
GString *text = NULL;
|
||
if (workspace == NULL || workspace->relation_evidences_label == NULL)
|
||
return;
|
||
text = g_string_new("Pièces jointes");
|
||
if (evidence_records == NULL || evidence_records->len == 0)
|
||
g_string_append(text, " : aucune");
|
||
else
|
||
{
|
||
g_string_append_printf(text, " (%u) :", evidence_records->len);
|
||
for (guint index = 0; index < evidence_records->len; index++)
|
||
{
|
||
const EvidenceRecord *record = g_ptr_array_index(evidence_records,
|
||
index);
|
||
if (record == NULL) continue;
|
||
g_string_append_printf(text, "\n• %s — %s",
|
||
evidence_record_get_original_name(record),
|
||
evidence_record_get_type_identifier(record));
|
||
}
|
||
}
|
||
gtk_label_set_text(GTK_LABEL(workspace->relation_evidences_label),
|
||
text->str);
|
||
g_string_free(text, TRUE);
|
||
}
|
||
|
||
void workspace_set_person_role_callback(Workspace *workspace,
|
||
WorkspacePersonRoleCallback callback, gpointer user_data)
|
||
{
|
||
if (workspace == NULL) return;
|
||
workspace->person_role_callback = callback;
|
||
workspace->person_role_user_data = user_data;
|
||
}
|
||
|
||
void workspace_set_person_confidence_callback(Workspace *workspace,
|
||
WorkspacePersonConfidenceCallback callback, gpointer user_data)
|
||
{
|
||
if (workspace == NULL) return;
|
||
workspace->person_confidence_callback = callback;
|
||
workspace->person_confidence_user_data = user_data;
|
||
}
|
||
|
||
void workspace_set_person_name_callback(Workspace *workspace,
|
||
WorkspacePersonNameCallback callback, gpointer user_data)
|
||
{
|
||
if (workspace == NULL) return;
|
||
workspace->person_name_callback = callback;
|
||
workspace->person_name_user_data = user_data;
|
||
}
|
||
void workspace_set_person_evidence_callback(Workspace *workspace,
|
||
WorkspacePersonEvidenceCallback callback, gpointer user_data)
|
||
{
|
||
if (workspace == NULL) return;
|
||
workspace->person_evidence_callback = callback;
|
||
workspace->person_evidence_user_data = user_data;
|
||
}
|
||
void workspace_set_evidence_activated_callback(
|
||
Workspace *workspace,
|
||
WorkspaceEvidenceActivatedCallback callback,
|
||
gpointer user_data)
|
||
{
|
||
if (workspace == NULL) return;
|
||
workspace->evidence_activated_callback = callback;
|
||
workspace->evidence_activated_user_data = user_data;
|
||
}
|
||
void workspace_set_entity_selected_callback(Workspace *workspace,
|
||
WorkspaceEntitySelectedCallback callback, gpointer user_data)
|
||
{
|
||
if (workspace == NULL) return;
|
||
workspace->entity_selected_callback = callback;
|
||
workspace->entity_selected_user_data = user_data;
|
||
}
|
||
void workspace_set_person_evidences(Workspace *workspace,
|
||
const GPtrArray *records)
|
||
{
|
||
if (workspace == NULL) return;
|
||
entity_details_panel_set_person_evidences(
|
||
workspace->entity_details_panel, records);
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
workspace_clear_evidence_preview(workspace);
|
||
g_clear_pointer(&workspace->evidence_preview,
|
||
evidence_preview_widget_free);
|
||
|
||
g_clear_pointer(
|
||
&workspace->osint_selection_context,
|
||
osint_selection_context_free
|
||
);
|
||
g_clear_pointer(&workspace->selected_relation_identifier, g_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
|
||
);
|
||
investigation_graph_view_set_transform_changed_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_set_person_role_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_clear_pointer(&workspace->identity_ocr_records, g_ptr_array_unref);
|
||
g_clear_object(&workspace->identity_ocr_labels);
|
||
g_clear_pointer(&workspace->identity_ocr_overlay,
|
||
ocr_provenance_overlay_free);
|
||
|
||
g_free(workspace);
|
||
}
|