From 0f67118f5b3d3d737e681a9ba2d62c8503e7d310 Mon Sep 17 00:00:00 2001 From: grayTerminal-sh Date: Wed, 22 Jul 2026 07:23:31 +0200 Subject: [PATCH] =?UTF-8?q?Ajouter=20la=20r=C3=A9initialisation=20de=20la?= =?UTF-8?q?=20disposition=20du=20graphe?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/views/main_window.h | 35 +++++++ include/widgets/workspace.h | 42 ++++++++ src/core/application.c | 135 +++++++++++++++++++++++- src/views/main_window.c | 79 +++++++++++++- src/widgets/workspace.c | 201 ++++++++++++++++++++++++++++++++++++ 5 files changed, 489 insertions(+), 3 deletions(-) diff --git a/include/views/main_window.h b/include/views/main_window.h index 1ac0615..3d04ef9 100644 --- a/include/views/main_window.h +++ b/include/views/main_window.h @@ -91,6 +91,15 @@ typedef void (*MainWindowGraphNodeMovedCallback)( 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. * @@ -121,6 +130,21 @@ void main_window_set_graph_node_moved_callback( 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 ». * @@ -320,6 +344,17 @@ void main_window_clear_graph( 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. * diff --git a/include/widgets/workspace.h b/include/widgets/workspace.h index ce3bac3..6c2e587 100644 --- a/include/widgets/workspace.h +++ b/include/widgets/workspace.h @@ -54,6 +54,18 @@ typedef void (*WorkspaceGraphNodeMovedCallback)( 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. * @@ -134,6 +146,21 @@ void workspace_set_graph_node_moved_callback( 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. * @@ -187,6 +214,21 @@ void workspace_clear_graph( 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. * diff --git a/src/core/application.c b/src/core/application.c index 64152c6..499f5f0 100644 --- a/src/core/application.c +++ b/src/core/application.c @@ -2825,7 +2825,7 @@ static void application_on_evidence_file_selected( return; } - + Database *database = NULL; EvidenceTypeDao *evidence_type_dao = 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. * @@ -4373,6 +4498,12 @@ static void application_on_activate( application ); + main_window_set_reset_graph_layout_callback( + application->main_window, + application_on_reset_graph_layout_requested, + application + ); + main_window_set_new_investigation_callback( application->main_window, application_on_new_investigation_requested, @@ -4484,7 +4615,7 @@ Application *application_new(void) tool_initializer_free( application->tool_initializer ); - + task_manager_free( application->task_manager ); diff --git a/src/views/main_window.c b/src/views/main_window.c index cfc9f15..5dbb59a 100644 --- a/src/views/main_window.c +++ b/src/views/main_window.c @@ -98,6 +98,12 @@ struct MainWindow gpointer graph_node_moved_user_data; + MainWindowResetGraphLayoutCallback + reset_graph_layout_callback; + + gpointer + reset_graph_layout_user_data; + MainWindowQuitCallback 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. */ @@ -556,6 +583,12 @@ MainWindow *main_window_new( 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( main_window->workspace ); @@ -565,7 +598,7 @@ MainWindow *main_window_new( main_window_free(main_window); return NULL; } - + main_window->task_panel = task_panel_new( 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( MainWindow *main_window, const char *status_text @@ -1119,6 +1166,24 @@ void main_window_set_graph_node_moved_callback( 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( MainWindow *main_window, MainWindowQuitCallback callback, @@ -1241,12 +1306,24 @@ void main_window_free( NULL ); + workspace_set_reset_graph_layout_callback( + main_window->workspace, + NULL, + NULL + ); + main_window->graph_node_moved_callback = NULL; main_window->graph_node_moved_user_data = NULL; + main_window->reset_graph_layout_callback = + NULL; + + main_window->reset_graph_layout_user_data = + NULL; + main_window_clear_graph( main_window ); diff --git a/src/widgets/workspace.c b/src/widgets/workspace.c index 7f0e7e9..9de20ad 100644 --- a/src/widgets/workspace.c +++ b/src/widgets/workspace.c @@ -47,6 +47,8 @@ struct Workspace GtkWidget *graph_details_label; GtkWidget *graph_view_page; + GtkWidget *graph_toolbar; + GtkWidget *reset_graph_layout_button; InvestigationGraphView *graph_view; EntityDetailsPanel *entity_details_panel; @@ -83,6 +85,12 @@ struct Workspace gpointer graph_node_moved_user_data; + WorkspaceResetGraphLayoutCallback + reset_graph_layout_callback; + + gpointer + reset_graph_layout_user_data; + GtkWidget *node_name_label; GtkWidget *node_path_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. */ @@ -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( workspace->graph_view_page @@ -1458,6 +1584,11 @@ void workspace_set_graph_loading( 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 @@ -1548,6 +1679,11 @@ void workspace_set_graph( 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 @@ -1618,6 +1754,11 @@ void workspace_set_graph_error( 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 @@ -1690,6 +1831,11 @@ void workspace_clear_graph( 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 @@ -1756,6 +1902,49 @@ void workspace_set_graph_node_moved_callback( 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) { if (workspace == NULL) @@ -1821,6 +2010,18 @@ void workspace_free(Workspace *workspace) workspace->graph_node_moved_user_data = 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( &workspace->selected_evidence_identifier, g_free