feat(persons): manage attached evidence
This commit is contained in:
parent
67d8db15af
commit
fb491639aa
9 changed files with 537 additions and 0 deletions
|
|
@ -176,6 +176,12 @@ typedef void (*MainWindowOsintActionCallback)(
|
|||
/** @brief Callback appelé pour modifier le nom affiché d'une personne. */
|
||||
typedef void (*MainWindowPersonNameCallback)(const char *entity_identifier,
|
||||
const char *display_name, gpointer user_data);
|
||||
/** @brief Callback appelé pour gérer les preuves d'une personne. */
|
||||
typedef void (*MainWindowPersonEvidenceCallback)(
|
||||
const char *entity_identifier, gpointer user_data);
|
||||
/** @brief Callback appelé quand une entité est sélectionnée. */
|
||||
typedef void (*MainWindowEntitySelectedCallback)(
|
||||
const char *entity_identifier, gpointer user_data);
|
||||
|
||||
/**
|
||||
* @brief Définit le callback de vérification d'une preuve.
|
||||
|
|
@ -299,6 +305,15 @@ void main_window_set_osint_action_callback(
|
|||
/** @brief Définit le callback de modification du nom affiché. */
|
||||
void main_window_set_person_name_callback(MainWindow *main_window,
|
||||
MainWindowPersonNameCallback callback, gpointer user_data);
|
||||
/** @brief Définit le callback de gestion des preuves d'une personne. */
|
||||
void main_window_set_person_evidence_callback(MainWindow *main_window,
|
||||
MainWindowPersonEvidenceCallback callback, gpointer user_data);
|
||||
/** @brief Définit le callback de sélection d'une entité. */
|
||||
void main_window_set_entity_selected_callback(MainWindow *main_window,
|
||||
MainWindowEntitySelectedCallback callback, gpointer user_data);
|
||||
/** @brief Affiche les preuves de la personne sélectionnée. */
|
||||
void main_window_set_person_evidences(MainWindow *main_window,
|
||||
const GPtrArray *evidence_records);
|
||||
|
||||
/**
|
||||
* @brief Définit le callback du bouton « Nouvelle enquête ».
|
||||
|
|
|
|||
29
include/views/manage_entity_evidence_dialog.h
Normal file
29
include/views/manage_entity_evidence_dialog.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/******************************************************************************
|
||||
* @file manage_entity_evidence_dialog.h
|
||||
* @brief Dialogue de sélection des preuves associées à une entité.
|
||||
******************************************************************************/
|
||||
#ifndef LABFY_INVESTIGATION_MANAGE_ENTITY_EVIDENCE_DIALOG_H
|
||||
#define LABFY_INVESTIGATION_MANAGE_ENTITY_EVIDENCE_DIALOG_H
|
||||
#include "models/evidence_record.h"
|
||||
#include <gtk/gtk.h>
|
||||
G_BEGIN_DECLS
|
||||
typedef void (*ManageEntityEvidenceDialogCallback)(
|
||||
GPtrArray *selected_identifiers, gpointer user_data);
|
||||
/**
|
||||
* @brief Présente la sélection multiple de preuves avec aperçu.
|
||||
* @param parent Fenêtre parente.
|
||||
* @param evidence_records Preuves disponibles empruntées.
|
||||
* @param selected_identifiers UUID déjà associés empruntés.
|
||||
* @param investigation_root_path Racine de l'enquête.
|
||||
* @param callback Callback recevant un tableau possédé, ou NULL à l'annulation.
|
||||
* @param user_data Données empruntées du callback.
|
||||
* @param error Destination facultative d'une erreur immédiate.
|
||||
* @return TRUE si le dialogue a été présenté.
|
||||
*/
|
||||
gboolean manage_entity_evidence_dialog_present(GtkWindow *parent,
|
||||
const GPtrArray *evidence_records, const GPtrArray *selected_identifiers,
|
||||
const char *investigation_root_path,
|
||||
ManageEntityEvidenceDialogCallback callback, gpointer user_data,
|
||||
GError **error);
|
||||
G_END_DECLS
|
||||
#endif
|
||||
|
|
@ -66,6 +66,9 @@ typedef void (*EntityDetailsPanelPersonNameCallback)(
|
|||
const char *display_name,
|
||||
gpointer user_data
|
||||
);
|
||||
/** @brief Callback appelé pour gérer les preuves d'une personne. */
|
||||
typedef void (*EntityDetailsPanelPersonEvidenceCallback)(
|
||||
const char *entity_identifier, gpointer user_data);
|
||||
|
||||
/**
|
||||
* @brief Crée un volet de détails fermé.
|
||||
|
|
@ -168,6 +171,17 @@ void entity_details_panel_set_person_name_callback(
|
|||
EntityDetailsPanelPersonNameCallback callback,
|
||||
gpointer user_data
|
||||
);
|
||||
/** @brief Définit le callback de gestion des preuves d'une personne. */
|
||||
void entity_details_panel_set_person_evidence_callback(
|
||||
EntityDetailsPanel *details_panel,
|
||||
EntityDetailsPanelPersonEvidenceCallback callback, gpointer user_data);
|
||||
/**
|
||||
* @brief Affiche les preuves associées à la personne courante.
|
||||
* @param details_panel Volet à mettre à jour.
|
||||
* @param evidence_records Tableau emprunté de EvidenceRecord, ou NULL.
|
||||
*/
|
||||
void entity_details_panel_set_person_evidences(
|
||||
EntityDetailsPanel *details_panel, const GPtrArray *evidence_records);
|
||||
|
||||
/**
|
||||
* @brief Indique si le volet est actuellement ouvert.
|
||||
|
|
|
|||
|
|
@ -120,6 +120,12 @@ typedef void (*WorkspaceOsintActionCallback)(
|
|||
/** @brief Callback appelé pour modifier le nom affiché d'une personne. */
|
||||
typedef void (*WorkspacePersonNameCallback)(const char *entity_identifier,
|
||||
const char *display_name, gpointer user_data);
|
||||
/** @brief Callback appelé pour gérer les preuves d'une personne. */
|
||||
typedef void (*WorkspacePersonEvidenceCallback)(
|
||||
const char *entity_identifier, gpointer user_data);
|
||||
/** @brief Callback appelé quand une entité devient sélectionnée. */
|
||||
typedef void (*WorkspaceEntitySelectedCallback)(
|
||||
const char *entity_identifier, gpointer user_data);
|
||||
|
||||
/** @brief Callback appelé pour modifier les métadonnées d'une preuve. */
|
||||
typedef void (*WorkspaceEditEvidenceCallback)(
|
||||
|
|
@ -377,6 +383,15 @@ void workspace_set_graph_loading(
|
|||
/** @brief Définit le callback de modification du nom affiché. */
|
||||
void workspace_set_person_name_callback(Workspace *workspace,
|
||||
WorkspacePersonNameCallback callback, gpointer user_data);
|
||||
/** @brief Définit le callback de gestion des preuves d'une personne. */
|
||||
void workspace_set_person_evidence_callback(Workspace *workspace,
|
||||
WorkspacePersonEvidenceCallback callback, gpointer user_data);
|
||||
/** @brief Définit le callback de sélection d'une entité. */
|
||||
void workspace_set_entity_selected_callback(Workspace *workspace,
|
||||
WorkspaceEntitySelectedCallback callback, gpointer user_data);
|
||||
/** @brief Affiche les preuves de la personne sélectionnée. */
|
||||
void workspace_set_person_evidences(Workspace *workspace,
|
||||
const GPtrArray *evidence_records);
|
||||
|
||||
/**
|
||||
* @brief Affiche le graphe chargé avec sa disposition persistée.
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@
|
|||
#include "views/osint_dns_review_dialog.h"
|
||||
#include "views/osint_execution_history_dialog.h"
|
||||
#include "dao/evidence_dao.h"
|
||||
#include "dao/evidence_entity_dao.h"
|
||||
#include "dao/entity_dao.h"
|
||||
#include "dao/relation_dao.h"
|
||||
#include "dao/relation_evidence_dao.h"
|
||||
|
|
@ -58,6 +59,7 @@
|
|||
#include "views/create_social_account_dialog.h"
|
||||
#include "views/create_person_dialog.h"
|
||||
#include "views/eml_analysis_dialog.h"
|
||||
#include "views/manage_entity_evidence_dialog.h"
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <errno.h>
|
||||
|
|
@ -5176,6 +5178,144 @@ static void application_on_person_name_changed(const char *entity_identifier,
|
|||
investigation_project_get_database_path(project));
|
||||
}
|
||||
|
||||
/** @brief Charge et affiche les preuves liées à l'entité sélectionnée. */
|
||||
static void application_on_entity_selected(const char *entity_identifier,
|
||||
gpointer user_data)
|
||||
{
|
||||
Application *application = user_data; Database *database = NULL;
|
||||
EvidenceEntityDao *link_dao = NULL; EvidenceDao *evidence_dao = NULL;
|
||||
GPtrArray *identifiers = NULL; GPtrArray *records = NULL;
|
||||
GError *error = NULL;
|
||||
if (application == NULL || application->session == NULL ||
|
||||
entity_identifier == NULL) return;
|
||||
database = investigation_session_get_database(application->session);
|
||||
link_dao = evidence_entity_dao_new(database, &error);
|
||||
if (link_dao == NULL) goto cleanup;
|
||||
evidence_dao = evidence_dao_new(database, &error);
|
||||
if (evidence_dao == NULL) goto cleanup;
|
||||
identifiers = evidence_entity_dao_list_evidence_identifiers(link_dao,
|
||||
entity_identifier, &error);
|
||||
if (identifiers == NULL) goto cleanup;
|
||||
records = g_ptr_array_new_with_free_func((GDestroyNotify)
|
||||
evidence_record_free);
|
||||
for (guint index = 0; index < identifiers->len; index++)
|
||||
{
|
||||
EvidenceRecord *record = evidence_dao_find_by_identifier(evidence_dao,
|
||||
g_ptr_array_index(identifiers, index), &error);
|
||||
if (record == NULL) goto cleanup;
|
||||
g_ptr_array_add(records, record);
|
||||
}
|
||||
main_window_set_person_evidences(application->main_window, records);
|
||||
cleanup:
|
||||
if (error != NULL)
|
||||
main_window_set_person_evidences(application->main_window, NULL);
|
||||
g_clear_error(&error); g_clear_pointer(&records, g_ptr_array_unref);
|
||||
g_clear_pointer(&identifiers, g_ptr_array_unref);
|
||||
evidence_dao_free(evidence_dao); evidence_entity_dao_free(link_dao);
|
||||
}
|
||||
|
||||
/** @brief Contexte possédé par la gestion des preuves d'une personne. */
|
||||
typedef struct { Application *application; char *entity_identifier; }
|
||||
ApplicationPersonEvidenceContext;
|
||||
/** @brief Libère le contexte de gestion des preuves. */
|
||||
static void application_person_evidence_context_free(
|
||||
ApplicationPersonEvidenceContext *context)
|
||||
{
|
||||
if (context == NULL) return;
|
||||
g_free(context->entity_identifier); g_free(context);
|
||||
}
|
||||
/** @brief Synchronise atomiquement les preuves choisies. */
|
||||
static void application_on_person_evidences_selected(GPtrArray *selected,
|
||||
gpointer user_data)
|
||||
{
|
||||
ApplicationPersonEvidenceContext *context = user_data;
|
||||
Application *application = context != NULL ? context->application : NULL;
|
||||
Database *database = NULL; EvidenceEntityDao *dao = NULL;
|
||||
GPtrArray *existing = NULL; GError *error = NULL;
|
||||
gboolean active = FALSE;
|
||||
if (selected == NULL || application == NULL || application->session == NULL)
|
||||
goto cleanup;
|
||||
database = investigation_session_get_database(application->session);
|
||||
dao = evidence_entity_dao_new(database, &error);
|
||||
if (dao == NULL || !database_transaction_begin(database)) goto failure;
|
||||
active = TRUE;
|
||||
existing = evidence_entity_dao_list_evidence_identifiers(dao,
|
||||
context->entity_identifier, &error);
|
||||
if (existing == NULL) goto failure;
|
||||
for (guint index = 0; index < existing->len; index++)
|
||||
{
|
||||
const char *identifier = g_ptr_array_index(existing, index);
|
||||
gboolean retained = FALSE;
|
||||
for (guint wanted = 0; wanted < selected->len; wanted++)
|
||||
if (g_strcmp0(identifier, g_ptr_array_index(selected, wanted)) == 0)
|
||||
retained = TRUE;
|
||||
if (!retained && !evidence_entity_dao_unlink(dao, identifier,
|
||||
context->entity_identifier, &error)) goto failure;
|
||||
}
|
||||
for (guint index = 0; index < selected->len; index++)
|
||||
{
|
||||
const char *identifier = g_ptr_array_index(selected, index);
|
||||
gboolean exists = FALSE;
|
||||
if (!evidence_entity_dao_exists(dao, identifier,
|
||||
context->entity_identifier, &exists, &error) ||
|
||||
(!exists && !evidence_entity_dao_link(dao, identifier,
|
||||
context->entity_identifier, &error))) goto failure;
|
||||
}
|
||||
if (!database_transaction_commit(database)) goto failure;
|
||||
active = FALSE;
|
||||
main_window_set_status(application->main_window,
|
||||
"Pièces jointes de la personne enregistrées.");
|
||||
application_on_entity_selected(context->entity_identifier, application);
|
||||
goto cleanup;
|
||||
failure:
|
||||
if (active) database_transaction_rollback(database);
|
||||
application_present_error(application, "Pièces jointes non enregistrées",
|
||||
error != NULL ? error->message : "La sélection n'a pas pu être enregistrée.");
|
||||
cleanup:
|
||||
g_clear_error(&error); g_clear_pointer(&existing, g_ptr_array_unref);
|
||||
g_clear_pointer(&selected, g_ptr_array_unref);
|
||||
evidence_entity_dao_free(dao);
|
||||
application_person_evidence_context_free(context);
|
||||
}
|
||||
/** @brief Ouvre le gestionnaire des preuves de la personne sélectionnée. */
|
||||
static void application_on_person_evidence_requested(
|
||||
const char *entity_identifier, gpointer user_data)
|
||||
{
|
||||
Application *application = user_data; Database *database = NULL;
|
||||
EvidenceDao *evidence_dao = NULL; EvidenceEntityDao *link_dao = NULL;
|
||||
GPtrArray *records = NULL; GPtrArray *selected = NULL;
|
||||
ApplicationPersonEvidenceContext *context = NULL;
|
||||
const InvestigationProject *project = NULL; GError *error = NULL;
|
||||
if (application == NULL || application->session == NULL) return;
|
||||
database = investigation_session_get_database(application->session);
|
||||
project = investigation_session_get_project(application->session);
|
||||
evidence_dao = evidence_dao_new(database, &error);
|
||||
if (evidence_dao != NULL) records = evidence_dao_list_all(evidence_dao, &error);
|
||||
link_dao = evidence_entity_dao_new(database, &error);
|
||||
if (link_dao != NULL) selected =
|
||||
evidence_entity_dao_list_evidence_identifiers(link_dao,
|
||||
entity_identifier, &error);
|
||||
if (records == NULL || selected == NULL || project == NULL) goto failure;
|
||||
context = g_new0(ApplicationPersonEvidenceContext, 1);
|
||||
context->application = application;
|
||||
context->entity_identifier = g_strdup(entity_identifier);
|
||||
if (context->entity_identifier == NULL ||
|
||||
!manage_entity_evidence_dialog_present(main_window_get_window(
|
||||
application->main_window), records, selected,
|
||||
investigation_project_get_root_path(project),
|
||||
application_on_person_evidences_selected, context, &error))
|
||||
goto failure;
|
||||
context = NULL; goto cleanup;
|
||||
failure:
|
||||
application_person_evidence_context_free(context); context = NULL;
|
||||
application_present_error(application, "Pièces jointes indisponibles",
|
||||
error != NULL ? error->message : "Le gestionnaire n'a pas pu être ouvert.");
|
||||
cleanup:
|
||||
g_clear_error(&error); g_clear_pointer(&records, g_ptr_array_unref);
|
||||
g_clear_pointer(&selected, g_ptr_array_unref);
|
||||
evidence_dao_free(evidence_dao); evidence_entity_dao_free(link_dao);
|
||||
}
|
||||
|
||||
/** @brief Libère le contexte d'édition d'une relation. */
|
||||
static void application_edit_relation_context_free(
|
||||
ApplicationEditRelationContext *context)
|
||||
|
|
@ -6852,6 +6992,10 @@ static void application_on_activate(
|
|||
application_on_person_confidence_changed, application);
|
||||
main_window_set_person_name_callback(application->main_window,
|
||||
application_on_person_name_changed, application);
|
||||
main_window_set_person_evidence_callback(application->main_window,
|
||||
application_on_person_evidence_requested, application);
|
||||
main_window_set_entity_selected_callback(application->main_window,
|
||||
application_on_entity_selected, application);
|
||||
|
||||
main_window_set_osint_action_callback(
|
||||
application->main_window,
|
||||
|
|
|
|||
|
|
@ -139,6 +139,10 @@ struct MainWindow
|
|||
gpointer person_confidence_user_data;
|
||||
MainWindowPersonNameCallback person_name_callback;
|
||||
gpointer person_name_user_data;
|
||||
MainWindowPersonEvidenceCallback person_evidence_callback;
|
||||
gpointer person_evidence_user_data;
|
||||
MainWindowEntitySelectedCallback entity_selected_callback;
|
||||
gpointer entity_selected_user_data;
|
||||
|
||||
MainWindowOsintActionCallback
|
||||
osint_action_callback;
|
||||
|
|
@ -574,6 +578,24 @@ static void main_window_on_person_name_changed(const char *entity_identifier,
|
|||
main_window->person_name_callback(entity_identifier, display_name,
|
||||
main_window->person_name_user_data);
|
||||
}
|
||||
/** @brief Relaie la gestion des preuves d'une personne. */
|
||||
static void main_window_on_person_evidence_requested(const char *identifier,
|
||||
gpointer user_data)
|
||||
{
|
||||
MainWindow *window = user_data;
|
||||
if (window != NULL && window->person_evidence_callback != NULL)
|
||||
window->person_evidence_callback(identifier,
|
||||
window->person_evidence_user_data);
|
||||
}
|
||||
/** @brief Relaie la sélection d'une entité vers le contrôleur. */
|
||||
static void main_window_on_entity_selected(const char *identifier,
|
||||
gpointer user_data)
|
||||
{
|
||||
MainWindow *window = user_data;
|
||||
if (window != NULL && window->entity_selected_callback != NULL)
|
||||
window->entity_selected_callback(identifier,
|
||||
window->entity_selected_user_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Relaie la demande de vérification provenant du Workspace.
|
||||
|
|
@ -923,6 +945,10 @@ MainWindow *main_window_new(
|
|||
main_window_on_person_confidence_changed, main_window);
|
||||
workspace_set_person_name_callback(main_window->workspace,
|
||||
main_window_on_person_name_changed, main_window);
|
||||
workspace_set_person_evidence_callback(main_window->workspace,
|
||||
main_window_on_person_evidence_requested, main_window);
|
||||
workspace_set_entity_selected_callback(main_window->workspace,
|
||||
main_window_on_entity_selected, main_window);
|
||||
|
||||
workspace_set_osint_action_callback(
|
||||
main_window->workspace,
|
||||
|
|
@ -1761,6 +1787,26 @@ void main_window_set_person_name_callback(MainWindow *main_window,
|
|||
main_window->person_name_callback = callback;
|
||||
main_window->person_name_user_data = user_data;
|
||||
}
|
||||
void main_window_set_person_evidence_callback(MainWindow *main_window,
|
||||
MainWindowPersonEvidenceCallback callback, gpointer user_data)
|
||||
{
|
||||
if (main_window == NULL) return;
|
||||
main_window->person_evidence_callback = callback;
|
||||
main_window->person_evidence_user_data = user_data;
|
||||
}
|
||||
void main_window_set_entity_selected_callback(MainWindow *main_window,
|
||||
MainWindowEntitySelectedCallback callback, gpointer user_data)
|
||||
{
|
||||
if (main_window == NULL) return;
|
||||
main_window->entity_selected_callback = callback;
|
||||
main_window->entity_selected_user_data = user_data;
|
||||
}
|
||||
void main_window_set_person_evidences(MainWindow *main_window,
|
||||
const GPtrArray *records)
|
||||
{
|
||||
if (main_window == NULL) return;
|
||||
workspace_set_person_evidences(main_window->workspace, records);
|
||||
}
|
||||
|
||||
void main_window_set_quit_callback(
|
||||
MainWindow *main_window,
|
||||
|
|
|
|||
170
src/views/manage_entity_evidence_dialog.c
Normal file
170
src/views/manage_entity_evidence_dialog.c
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
/******************************************************************************
|
||||
* @file manage_entity_evidence_dialog.c
|
||||
* @brief Dialogue de sélection des preuves associées à une entité.
|
||||
******************************************************************************/
|
||||
#include "views/manage_entity_evidence_dialog.h"
|
||||
#include <gio/gio.h>
|
||||
typedef struct
|
||||
{
|
||||
char *identifier;
|
||||
char *path;
|
||||
GtkCheckButton *check;
|
||||
} EvidenceChoice;
|
||||
typedef struct
|
||||
{
|
||||
GtkWindow *window;
|
||||
GtkStack *preview;
|
||||
GtkPicture *picture;
|
||||
GtkVideo *video;
|
||||
GPtrArray *choices;
|
||||
ManageEntityEvidenceDialogCallback callback;
|
||||
gpointer callback_data;
|
||||
gboolean completed;
|
||||
} ManageEvidenceState;
|
||||
/** @brief Libère une option privée. */
|
||||
static void evidence_choice_free(gpointer data)
|
||||
{
|
||||
EvidenceChoice *choice = data;
|
||||
if (choice == NULL) return;
|
||||
g_free(choice->identifier); g_free(choice->path); g_free(choice);
|
||||
}
|
||||
/** @brief Arrête et efface l'aperçu courant. */
|
||||
static void manage_evidence_clear_preview(ManageEvidenceState *state)
|
||||
{
|
||||
GtkMediaStream *stream = NULL;
|
||||
if (state == NULL || state->video == NULL) return;
|
||||
stream = gtk_video_get_media_stream(state->video);
|
||||
if (stream != NULL) gtk_media_stream_pause(stream);
|
||||
gtk_video_set_media_stream(state->video, NULL);
|
||||
gtk_picture_set_paintable(state->picture, NULL);
|
||||
}
|
||||
/** @brief Libère l'état du dialogue. */
|
||||
static void manage_evidence_state_free(gpointer data)
|
||||
{
|
||||
ManageEvidenceState *state = data;
|
||||
if (state == NULL) return;
|
||||
manage_evidence_clear_preview(state);
|
||||
g_ptr_array_unref(state->choices); g_free(state);
|
||||
}
|
||||
/** @brief Affiche l'aperçu de l'option manipulée. */
|
||||
static void manage_evidence_on_toggled(GtkCheckButton *button, gpointer data)
|
||||
{
|
||||
ManageEvidenceState *state = data;
|
||||
EvidenceChoice *choice = g_object_get_data(G_OBJECT(button), "choice");
|
||||
GFile *file = NULL; GFileInfo *info = NULL; char *mime = NULL;
|
||||
const char *content_type = NULL;
|
||||
if (state == NULL || choice == NULL || choice->path == NULL) return;
|
||||
manage_evidence_clear_preview(state);
|
||||
file = g_file_new_for_path(choice->path);
|
||||
info = g_file_query_info(file, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
|
||||
G_FILE_QUERY_INFO_NONE, NULL, NULL);
|
||||
if (info != NULL) content_type = g_file_info_get_content_type(info);
|
||||
if (content_type != NULL) mime = g_content_type_get_mime_type(content_type);
|
||||
if (mime != NULL && g_str_has_prefix(mime, "image/"))
|
||||
{
|
||||
gtk_picture_set_filename(state->picture, choice->path);
|
||||
gtk_stack_set_visible_child_name(state->preview, "image");
|
||||
}
|
||||
else if (mime != NULL && g_str_has_prefix(mime, "video/"))
|
||||
{
|
||||
GtkMediaStream *stream = gtk_media_file_new_for_filename(choice->path);
|
||||
gtk_video_set_media_stream(state->video, stream); g_object_unref(stream);
|
||||
gtk_stack_set_visible_child_name(state->preview, "video");
|
||||
}
|
||||
else gtk_stack_set_visible_child_name(state->preview, "unsupported");
|
||||
g_free(mime); g_clear_object(&info); g_clear_object(&file);
|
||||
}
|
||||
/** @brief Termine le dialogue en renvoyant la sélection. */
|
||||
static void manage_evidence_on_save(GtkButton *button, gpointer data)
|
||||
{
|
||||
ManageEvidenceState *state = data;
|
||||
GPtrArray *selected = g_ptr_array_new_with_free_func(g_free);
|
||||
(void) button;
|
||||
for (guint index = 0; index < state->choices->len; index++)
|
||||
{
|
||||
EvidenceChoice *choice = g_ptr_array_index(state->choices, index);
|
||||
if (gtk_check_button_get_active(choice->check))
|
||||
g_ptr_array_add(selected, g_strdup(choice->identifier));
|
||||
}
|
||||
state->completed = TRUE; state->callback(selected, state->callback_data);
|
||||
gtk_window_close(state->window);
|
||||
}
|
||||
/** @brief Termine le dialogue par une annulation. */
|
||||
static gboolean manage_evidence_on_close(GtkWindow *window, gpointer data)
|
||||
{
|
||||
ManageEvidenceState *state = data; (void) window;
|
||||
if (!state->completed) state->callback(NULL, state->callback_data);
|
||||
return FALSE;
|
||||
}
|
||||
gboolean manage_entity_evidence_dialog_present(GtkWindow *parent,
|
||||
const GPtrArray *records, const GPtrArray *selected_ids,
|
||||
const char *root_path, ManageEntityEvidenceDialogCallback callback,
|
||||
gpointer callback_data, GError **error)
|
||||
{
|
||||
ManageEvidenceState *state = NULL; GtkWidget *root = NULL;
|
||||
GtkWidget *content = NULL; GtkWidget *list = NULL; GtkWidget *scroll = NULL;
|
||||
GtkWidget *save = NULL;
|
||||
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
|
||||
if (!GTK_IS_WINDOW(parent) || records == NULL || root_path == NULL ||
|
||||
callback == NULL) return FALSE;
|
||||
state = g_new0(ManageEvidenceState, 1);
|
||||
state->choices = g_ptr_array_new_with_free_func(evidence_choice_free);
|
||||
state->callback = callback; state->callback_data = callback_data;
|
||||
state->window = GTK_WINDOW(gtk_window_new());
|
||||
gtk_window_set_title(state->window, "Pièces jointes de la personne");
|
||||
gtk_window_set_transient_for(state->window, parent);
|
||||
gtk_window_set_modal(state->window, TRUE);
|
||||
gtk_window_set_default_size(state->window, 900, 620);
|
||||
root = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
|
||||
gtk_widget_set_margin_top(root, 16); gtk_widget_set_margin_bottom(root, 16);
|
||||
gtk_widget_set_margin_start(root, 16); gtk_widget_set_margin_end(root, 16);
|
||||
content = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12);
|
||||
list = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
|
||||
scroll = gtk_scrolled_window_new(); gtk_widget_set_size_request(scroll, 380, 480);
|
||||
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scroll), list);
|
||||
for (guint index = 0; index < records->len; index++)
|
||||
{
|
||||
const EvidenceRecord *record = g_ptr_array_index(records, index);
|
||||
EvidenceChoice *choice = g_new0(EvidenceChoice, 1); gboolean active = FALSE;
|
||||
char *label = g_strdup_printf("%s — %s",
|
||||
evidence_record_get_original_name(record),
|
||||
evidence_record_get_type_identifier(record));
|
||||
char *candidate = g_build_filename(root_path,
|
||||
evidence_record_get_relative_path(record), NULL);
|
||||
char *canonical_root = g_canonicalize_filename(root_path, NULL);
|
||||
char *canonical_path = g_canonicalize_filename(candidate, NULL);
|
||||
choice->identifier = g_strdup(evidence_record_get_identifier(record));
|
||||
if (canonical_root != NULL && canonical_path != NULL &&
|
||||
g_str_has_prefix(canonical_path, canonical_root) &&
|
||||
(canonical_path[strlen(canonical_root)] == G_DIR_SEPARATOR ||
|
||||
canonical_path[strlen(canonical_root)] == '\0'))
|
||||
choice->path = g_strdup(canonical_path);
|
||||
g_free(canonical_path); g_free(canonical_root); g_free(candidate);
|
||||
choice->check = GTK_CHECK_BUTTON(gtk_check_button_new_with_label(label));
|
||||
g_free(label);
|
||||
for (guint selected = 0; selected_ids != NULL && selected < selected_ids->len; selected++)
|
||||
if (g_strcmp0(choice->identifier, g_ptr_array_index(selected_ids, selected)) == 0) active = TRUE;
|
||||
gtk_check_button_set_active(choice->check, active);
|
||||
g_object_set_data(G_OBJECT(choice->check), "choice", choice);
|
||||
g_signal_connect(choice->check, "toggled", G_CALLBACK(manage_evidence_on_toggled), state);
|
||||
gtk_box_append(GTK_BOX(list), GTK_WIDGET(choice->check));
|
||||
g_ptr_array_add(state->choices, choice);
|
||||
}
|
||||
state->preview = GTK_STACK(gtk_stack_new());
|
||||
state->picture = GTK_PICTURE(gtk_picture_new());
|
||||
gtk_picture_set_content_fit(state->picture, GTK_CONTENT_FIT_CONTAIN);
|
||||
state->video = GTK_VIDEO(gtk_video_new());
|
||||
gtk_stack_add_named(state->preview, gtk_label_new("Sélectionnez une preuve."), "unsupported");
|
||||
gtk_stack_add_named(state->preview, GTK_WIDGET(state->picture), "image");
|
||||
gtk_stack_add_named(state->preview, GTK_WIDGET(state->video), "video");
|
||||
gtk_widget_set_hexpand(GTK_WIDGET(state->preview), TRUE);
|
||||
gtk_box_append(GTK_BOX(content), scroll); gtk_box_append(GTK_BOX(content), GTK_WIDGET(state->preview));
|
||||
save = gtk_button_new_with_label("Enregistrer les pièces jointes");
|
||||
gtk_widget_add_css_class(save, "suggested-action"); gtk_widget_set_halign(save, GTK_ALIGN_END);
|
||||
gtk_box_append(GTK_BOX(root), content); gtk_box_append(GTK_BOX(root), save);
|
||||
gtk_window_set_child(state->window, root);
|
||||
g_object_set_data_full(G_OBJECT(state->window), "state", state, manage_evidence_state_free);
|
||||
g_signal_connect(save, "clicked", G_CALLBACK(manage_evidence_on_save), state);
|
||||
g_signal_connect(state->window, "close-request", G_CALLBACK(manage_evidence_on_close), state);
|
||||
gtk_window_present(state->window); return TRUE;
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@
|
|||
#include "widgets/entity_details_panel.h"
|
||||
|
||||
#include "models/entity_record.h"
|
||||
#include "models/evidence_record.h"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
|
|
@ -35,6 +36,8 @@ struct EntityDetailsPanel
|
|||
GtkWidget *person_confidence_button;
|
||||
GtkEntry *person_name_entry;
|
||||
GtkWidget *person_name_button;
|
||||
GtkWidget *person_evidence_button;
|
||||
GtkWidget *person_evidence_summary_label;
|
||||
|
||||
char *selected_entity_identifier;
|
||||
|
||||
|
|
@ -54,7 +57,19 @@ struct EntityDetailsPanel
|
|||
gpointer person_confidence_user_data;
|
||||
EntityDetailsPanelPersonNameCallback person_name_callback;
|
||||
gpointer person_name_user_data;
|
||||
EntityDetailsPanelPersonEvidenceCallback person_evidence_callback;
|
||||
gpointer person_evidence_user_data;
|
||||
};
|
||||
/** @brief Relaie la demande de gestion des preuves d'une personne. */
|
||||
static void entity_details_panel_on_person_evidence_clicked(GtkButton *button,
|
||||
gpointer user_data)
|
||||
{
|
||||
EntityDetailsPanel *panel = user_data; (void) button;
|
||||
if (panel != NULL && panel->person_evidence_callback != NULL &&
|
||||
panel->selected_entity_identifier != NULL)
|
||||
panel->person_evidence_callback(panel->selected_entity_identifier,
|
||||
panel->person_evidence_user_data);
|
||||
}
|
||||
|
||||
/** @brief Relaie le nouveau nom affiché explicitement enregistré. */
|
||||
static void entity_details_panel_on_person_name_clicked(GtkButton *button,
|
||||
|
|
@ -795,6 +810,22 @@ EntityDetailsPanel *entity_details_panel_new(void)
|
|||
GTK_WIDGET(details_panel->person_name_entry));
|
||||
gtk_box_append(GTK_BOX(details_panel->person_role_box),
|
||||
details_panel->person_name_button);
|
||||
details_panel->person_evidence_button = gtk_button_new_with_label(
|
||||
"Gérer les pièces jointes");
|
||||
details_panel->person_evidence_summary_label = gtk_label_new(
|
||||
"Pièces jointes : chargement…");
|
||||
gtk_label_set_xalign(GTK_LABEL(
|
||||
details_panel->person_evidence_summary_label), 0.0F);
|
||||
gtk_label_set_wrap(GTK_LABEL(
|
||||
details_panel->person_evidence_summary_label), TRUE);
|
||||
gtk_label_set_selectable(GTK_LABEL(
|
||||
details_panel->person_evidence_summary_label), TRUE);
|
||||
gtk_box_append(GTK_BOX(details_panel->person_role_box),
|
||||
gtk_label_new("Preuves associées à cette personne"));
|
||||
gtk_box_append(GTK_BOX(details_panel->person_role_box),
|
||||
details_panel->person_evidence_summary_label);
|
||||
gtk_box_append(GTK_BOX(details_panel->person_role_box),
|
||||
details_panel->person_evidence_button);
|
||||
gtk_box_append(GTK_BOX(details_box), details_panel->person_role_box);
|
||||
g_signal_connect(details_panel->person_role_dropdown, "notify::selected",
|
||||
G_CALLBACK(entity_details_panel_on_person_role_changed), details_panel);
|
||||
|
|
@ -803,6 +834,8 @@ EntityDetailsPanel *entity_details_panel_new(void)
|
|||
details_panel);
|
||||
g_signal_connect(details_panel->person_name_button, "clicked",
|
||||
G_CALLBACK(entity_details_panel_on_person_name_clicked), details_panel);
|
||||
g_signal_connect(details_panel->person_evidence_button, "clicked",
|
||||
G_CALLBACK(entity_details_panel_on_person_evidence_clicked), details_panel);
|
||||
|
||||
if (details_panel->entity_value_label == NULL ||
|
||||
details_panel->entity_type_label == NULL ||
|
||||
|
|
@ -1205,6 +1238,36 @@ void entity_details_panel_set_person_name_callback(
|
|||
details_panel->person_name_callback = callback;
|
||||
details_panel->person_name_user_data = user_data;
|
||||
}
|
||||
void entity_details_panel_set_person_evidence_callback(
|
||||
EntityDetailsPanel *details_panel,
|
||||
EntityDetailsPanelPersonEvidenceCallback callback, gpointer user_data)
|
||||
{
|
||||
if (details_panel == NULL) return;
|
||||
details_panel->person_evidence_callback = callback;
|
||||
details_panel->person_evidence_user_data = user_data;
|
||||
}
|
||||
void entity_details_panel_set_person_evidences(EntityDetailsPanel *panel,
|
||||
const GPtrArray *records)
|
||||
{
|
||||
GString *text = NULL;
|
||||
if (panel == NULL || panel->person_evidence_summary_label == NULL) return;
|
||||
text = g_string_new(NULL);
|
||||
if (records == NULL || records->len == 0)
|
||||
g_string_append(text, "Aucune pièce jointe");
|
||||
else
|
||||
for (guint index = 0; index < records->len; index++)
|
||||
{
|
||||
const EvidenceRecord *record = g_ptr_array_index(records, index);
|
||||
if (record == NULL) continue;
|
||||
if (text->len > 0) g_string_append_c(text, '\n');
|
||||
g_string_append_printf(text, "• %s — %s",
|
||||
evidence_record_get_original_name(record),
|
||||
evidence_record_get_type_identifier(record));
|
||||
}
|
||||
gtk_label_set_text(GTK_LABEL(panel->person_evidence_summary_label),
|
||||
text->str);
|
||||
g_string_free(text, TRUE);
|
||||
}
|
||||
|
||||
gboolean entity_details_panel_is_open(
|
||||
const EntityDetailsPanel *details_panel
|
||||
|
|
|
|||
|
|
@ -143,6 +143,10 @@ struct Workspace
|
|||
gpointer person_confidence_user_data;
|
||||
WorkspacePersonNameCallback person_name_callback;
|
||||
gpointer person_name_user_data;
|
||||
WorkspacePersonEvidenceCallback person_evidence_callback;
|
||||
gpointer person_evidence_user_data;
|
||||
WorkspaceEntitySelectedCallback entity_selected_callback;
|
||||
gpointer entity_selected_user_data;
|
||||
|
||||
WorkspaceOsintActionCallback
|
||||
osint_action_callback;
|
||||
|
|
@ -741,6 +745,10 @@ static void workspace_on_graph_node_selected(
|
|||
{
|
||||
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)
|
||||
|
|
@ -920,6 +928,15 @@ static void workspace_on_person_name_changed(const char *entity_identifier,
|
|||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Désélectionne le nœud lorsque le volet est fermé manuellement.
|
||||
|
|
@ -1855,6 +1872,9 @@ Workspace *workspace_new(void)
|
|||
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);
|
||||
|
||||
gtk_overlay_set_child(
|
||||
GTK_OVERLAY(
|
||||
|
|
@ -3335,6 +3355,27 @@ void workspace_set_person_name_callback(Workspace *workspace,
|
|||
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_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
|
||||
|
|
|
|||
Loading…
Reference in a new issue