Ajouter la réinitialisation de la disposition du graphe

This commit is contained in:
grayTerminal-sh 2026-07-22 07:23:31 +02:00
parent 0465209d06
commit 0f67118f5b
5 changed files with 489 additions and 3 deletions

View file

@ -91,6 +91,15 @@ typedef void (*MainWindowGraphNodeMovedCallback)(
gpointer user_data gpointer user_data
); );
/**
* @brief Callback appelé lors d'une demande de réinitialisation du graphe.
*
* @param user_data Données privées du callback.
*/
typedef void (*MainWindowResetGraphLayoutCallback)(
gpointer user_data
);
/** /**
* @brief Définit le callback de vérification d'une preuve. * @brief Définit le callback de vérification d'une preuve.
* *
@ -121,6 +130,21 @@ void main_window_set_graph_node_moved_callback(
gpointer user_data gpointer user_data
); );
/**
* @brief Définit le callback de demande de réinitialisation du graphe.
*
* MainWindow relaie uniquement la demande provenant du Workspace.
*
* @param main_window Fenêtre principale.
* @param callback Callback facultatif.
* @param user_data Données privées transmises au callback.
*/
void main_window_set_reset_graph_layout_callback(
MainWindow *main_window,
MainWindowResetGraphLayoutCallback callback,
gpointer user_data
);
/** /**
* @brief Définit le callback du bouton « Nouvelle enquête ». * @brief Définit le callback du bouton « Nouvelle enquête ».
* *
@ -320,6 +344,17 @@ void main_window_clear_graph(
MainWindow *main_window MainWindow *main_window
); );
/**
* @brief Reconstruit visuellement la disposition automatique du graphe.
*
* Cette fonction ne modifie pas SQLite.
*
* @param main_window Fenêtre principale.
*/
void main_window_reset_graph_layout(
MainWindow *main_window
);
/** /**
* @brief Met à jour le texte de la barre d'état. * @brief Met à jour le texte de la barre d'état.
* *

View file

@ -54,6 +54,18 @@ typedef void (*WorkspaceGraphNodeMovedCallback)(
gpointer user_data gpointer user_data
); );
/**
* @brief Callback appelé lors d'une demande de réinitialisation du graphe.
*
* Le Workspace ne modifie ni SQLite ni le modèle de disposition. Il relaie
* uniquement la demande vers la couche applicative.
*
* @param user_data Données empruntées fournies par l'appelant.
*/
typedef void (*WorkspaceResetGraphLayoutCallback)(
gpointer user_data
);
/** /**
* @brief Crée une nouvelle zone de travail. * @brief Crée une nouvelle zone de travail.
* *
@ -134,6 +146,21 @@ void workspace_set_graph_node_moved_callback(
gpointer user_data gpointer user_data
); );
/**
* @brief Définit le callback de demande de réinitialisation du graphe.
*
* Le Workspace emprunte callback et user_data.
*
* @param workspace Zone de travail.
* @param callback Callback facultatif.
* @param user_data Données empruntées transmises au callback.
*/
void workspace_set_reset_graph_layout_callback(
Workspace *workspace,
WorkspaceResetGraphLayoutCallback callback,
gpointer user_data
);
/** /**
* @brief Affiche l'état de chargement du graphe. * @brief Affiche l'état de chargement du graphe.
* *
@ -187,6 +214,21 @@ void workspace_clear_graph(
Workspace *workspace Workspace *workspace
); );
/**
* @brief Reconstruit visuellement la disposition automatique du graphe.
*
* Cette fonction ne touche ni SQLite ni InvestigationGraphLayout. Elle doit
* être appelée par la couche applicative uniquement après la suppression
* réussie des positions persistées.
*
* Le zoom et le déplacement global du canvas sont conservés.
*
* @param workspace Zone de travail.
*/
void workspace_reset_graph_layout(
Workspace *workspace
);
/** /**
* @brief Libère la structure Workspace. * @brief Libère la structure Workspace.
* *

View file

@ -2825,7 +2825,7 @@ static void application_on_evidence_file_selected(
return; return;
} }
Database *database = NULL; Database *database = NULL;
EvidenceTypeDao *evidence_type_dao = NULL; EvidenceTypeDao *evidence_type_dao = NULL;
GPtrArray *evidence_types = NULL; GPtrArray *evidence_types = NULL;
@ -4277,6 +4277,131 @@ static void application_on_graph_node_moved(
); );
} }
/**
* @brief Ferme proprement l'application.
*
* @param user_data Pointeur vers Application.
*/
static void application_on_reset_graph_layout_requested(
gpointer user_data
)
{
Application *application =
user_data;
Database *database =
NULL;
GraphNodePositionDao *position_dao =
NULL;
GError *error =
NULL;
if (application == NULL ||
application->main_window == NULL ||
application->session == NULL ||
application->graph_model == NULL ||
application->graph_layout == NULL)
{
return;
}
database =
investigation_session_get_database(
application->session
);
if (database == NULL)
{
application_present_error(
application,
"Réinitialisation impossible",
"La session ne fournit aucune connexion SQLite."
);
return;
}
position_dao =
graph_node_position_dao_new(
database,
&error
);
if (position_dao == NULL)
{
g_warning(
"Impossible de créer le DAO des positions : %s",
error != NULL
? error->message
: "erreur inconnue"
);
application_present_error(
application,
"Réinitialisation impossible",
error != NULL
? error->message
: "Impossible d'accéder aux positions enregistrées."
);
g_clear_error(
&error
);
return;
}
if (!graph_node_position_dao_delete_all(
position_dao,
&error
))
{
g_warning(
"Impossible de supprimer les positions du graphe : %s",
error != NULL
? error->message
: "erreur inconnue"
);
application_present_error(
application,
"Réinitialisation impossible",
error != NULL
? error->message
: "Les positions enregistrées n'ont pas pu être supprimées."
);
g_clear_error(
&error
);
graph_node_position_dao_free(
position_dao
);
return;
}
graph_node_position_dao_free(
position_dao
);
investigation_graph_layout_clear(
application->graph_layout
);
main_window_reset_graph_layout(
application->main_window
);
main_window_set_status(
application->main_window,
"Disposition du graphe réinitialisée."
);
}
/** /**
* @brief Ferme proprement l'application. * @brief Ferme proprement l'application.
* *
@ -4373,6 +4498,12 @@ static void application_on_activate(
application application
); );
main_window_set_reset_graph_layout_callback(
application->main_window,
application_on_reset_graph_layout_requested,
application
);
main_window_set_new_investigation_callback( main_window_set_new_investigation_callback(
application->main_window, application->main_window,
application_on_new_investigation_requested, application_on_new_investigation_requested,
@ -4484,7 +4615,7 @@ Application *application_new(void)
tool_initializer_free( tool_initializer_free(
application->tool_initializer application->tool_initializer
); );
task_manager_free( task_manager_free(
application->task_manager application->task_manager
); );

View file

@ -98,6 +98,12 @@ struct MainWindow
gpointer gpointer
graph_node_moved_user_data; graph_node_moved_user_data;
MainWindowResetGraphLayoutCallback
reset_graph_layout_callback;
gpointer
reset_graph_layout_user_data;
MainWindowQuitCallback MainWindowQuitCallback
quit_callback; quit_callback;
@ -287,6 +293,27 @@ static void main_window_on_graph_node_moved(
); );
} }
/**
* @brief Relaie la demande de vérification provenant du Workspace.
*/
static void main_window_on_reset_graph_layout_requested(
gpointer user_data
)
{
MainWindow *main_window =
user_data;
if (main_window == NULL ||
main_window->reset_graph_layout_callback == NULL)
{
return;
}
main_window->reset_graph_layout_callback(
main_window->reset_graph_layout_user_data
);
}
/** /**
* @brief Relaie la demande de vérification provenant du Workspace. * @brief Relaie la demande de vérification provenant du Workspace.
*/ */
@ -556,6 +583,12 @@ MainWindow *main_window_new(
main_window main_window
); );
workspace_set_reset_graph_layout_callback(
main_window->workspace,
main_window_on_reset_graph_layout_requested,
main_window
);
workspace_widget = workspace_get_widget( workspace_widget = workspace_get_widget(
main_window->workspace main_window->workspace
); );
@ -565,7 +598,7 @@ MainWindow *main_window_new(
main_window_free(main_window); main_window_free(main_window);
return NULL; return NULL;
} }
main_window->task_panel = task_panel_new( main_window->task_panel = task_panel_new(
task_manager task_manager
); );
@ -957,6 +990,20 @@ void main_window_clear_graph(
); );
} }
void main_window_reset_graph_layout(
MainWindow *main_window
)
{
if (main_window == NULL)
{
return;
}
workspace_reset_graph_layout(
main_window->workspace
);
}
void main_window_set_status( void main_window_set_status(
MainWindow *main_window, MainWindow *main_window,
const char *status_text const char *status_text
@ -1119,6 +1166,24 @@ void main_window_set_graph_node_moved_callback(
user_data; user_data;
} }
void main_window_set_reset_graph_layout_callback(
MainWindow *main_window,
MainWindowResetGraphLayoutCallback callback,
gpointer user_data
)
{
if (main_window == NULL)
{
return;
}
main_window->reset_graph_layout_callback =
callback;
main_window->reset_graph_layout_user_data =
user_data;
}
void main_window_set_quit_callback( void main_window_set_quit_callback(
MainWindow *main_window, MainWindow *main_window,
MainWindowQuitCallback callback, MainWindowQuitCallback callback,
@ -1241,12 +1306,24 @@ void main_window_free(
NULL NULL
); );
workspace_set_reset_graph_layout_callback(
main_window->workspace,
NULL,
NULL
);
main_window->graph_node_moved_callback = main_window->graph_node_moved_callback =
NULL; NULL;
main_window->graph_node_moved_user_data = main_window->graph_node_moved_user_data =
NULL; NULL;
main_window->reset_graph_layout_callback =
NULL;
main_window->reset_graph_layout_user_data =
NULL;
main_window_clear_graph( main_window_clear_graph(
main_window main_window
); );

View file

@ -47,6 +47,8 @@ struct Workspace
GtkWidget *graph_details_label; GtkWidget *graph_details_label;
GtkWidget *graph_view_page; GtkWidget *graph_view_page;
GtkWidget *graph_toolbar;
GtkWidget *reset_graph_layout_button;
InvestigationGraphView *graph_view; InvestigationGraphView *graph_view;
EntityDetailsPanel *entity_details_panel; EntityDetailsPanel *entity_details_panel;
@ -83,6 +85,12 @@ struct Workspace
gpointer gpointer
graph_node_moved_user_data; graph_node_moved_user_data;
WorkspaceResetGraphLayoutCallback
reset_graph_layout_callback;
gpointer
reset_graph_layout_user_data;
GtkWidget *node_name_label; GtkWidget *node_name_label;
GtkWidget *node_path_label; GtkWidget *node_path_label;
GtkWidget *node_type_label; GtkWidget *node_type_label;
@ -380,6 +388,32 @@ static void workspace_show_default(
} }
} }
/**
* @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. * @brief Transmet la demande de vérification de la preuve affichée.
*/ */
@ -1048,6 +1082,98 @@ Workspace *workspace_new(void)
) )
); );
workspace->graph_toolbar =
gtk_box_new(
GTK_ORIENTATION_HORIZONTAL,
4
);
workspace->reset_graph_layout_button =
gtk_button_new_from_icon_name(
"view-refresh-symbolic"
);
if (workspace->graph_toolbar == NULL ||
workspace->reset_graph_layout_button == 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
);
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_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
);
gtk_overlay_add_overlay( gtk_overlay_add_overlay(
GTK_OVERLAY( GTK_OVERLAY(
workspace->graph_view_page workspace->graph_view_page
@ -1458,6 +1584,11 @@ void workspace_set_graph_loading(
workspace->graph_state = workspace->graph_state =
WORKSPACE_GRAPH_STATE_LOADING; WORKSPACE_GRAPH_STATE_LOADING;
gtk_widget_set_sensitive(
workspace->reset_graph_layout_button,
FALSE
);
gtk_label_set_text( gtk_label_set_text(
GTK_LABEL( GTK_LABEL(
workspace->graph_title_label workspace->graph_title_label
@ -1548,6 +1679,11 @@ void workspace_set_graph(
workspace->graph_state = workspace->graph_state =
WORKSPACE_GRAPH_STATE_READY; WORKSPACE_GRAPH_STATE_READY;
gtk_widget_set_sensitive(
workspace->reset_graph_layout_button,
TRUE
);
gtk_spinner_stop( gtk_spinner_stop(
GTK_SPINNER( GTK_SPINNER(
workspace->graph_spinner workspace->graph_spinner
@ -1618,6 +1754,11 @@ void workspace_set_graph_error(
workspace->graph_state = workspace->graph_state =
WORKSPACE_GRAPH_STATE_ERROR; WORKSPACE_GRAPH_STATE_ERROR;
gtk_widget_set_sensitive(
workspace->reset_graph_layout_button,
FALSE
);
gtk_spinner_stop( gtk_spinner_stop(
GTK_SPINNER( GTK_SPINNER(
workspace->graph_spinner workspace->graph_spinner
@ -1690,6 +1831,11 @@ void workspace_clear_graph(
workspace->graph_state = workspace->graph_state =
WORKSPACE_GRAPH_STATE_EMPTY; WORKSPACE_GRAPH_STATE_EMPTY;
gtk_widget_set_sensitive(
workspace->reset_graph_layout_button,
FALSE
);
gtk_spinner_stop( gtk_spinner_stop(
GTK_SPINNER( GTK_SPINNER(
workspace->graph_spinner workspace->graph_spinner
@ -1756,6 +1902,49 @@ void workspace_set_graph_node_moved_callback(
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_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) void workspace_free(Workspace *workspace)
{ {
if (workspace == NULL) if (workspace == NULL)
@ -1821,6 +2010,18 @@ void workspace_free(Workspace *workspace)
workspace->graph_node_moved_user_data = workspace->graph_node_moved_user_data =
NULL; NULL;
workspace->reset_graph_layout_callback =
NULL;
workspace->reset_graph_layout_user_data =
NULL;
workspace->graph_toolbar =
NULL;
workspace->reset_graph_layout_button =
NULL;
g_clear_pointer( g_clear_pointer(
&workspace->selected_evidence_identifier, &workspace->selected_evidence_identifier,
g_free g_free