From 0069e99529a5b7158885c85609660dfea764c7dc Mon Sep 17 00:00:00 2001 From: grayTerminal-sh Date: Wed, 22 Jul 2026 07:35:15 +0200 Subject: [PATCH] =?UTF-8?q?Confirmer=20la=20r=C3=A9initialisation=20de=20l?= =?UTF-8?q?a=20disposition=20du=20graphe?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/views/application_message_dialog.h | 41 ++ src/core/application.c | 67 ++- src/views/application_message_dialog.c | 544 +++++++++++++++++++++ 3 files changed, 645 insertions(+), 7 deletions(-) diff --git a/include/views/application_message_dialog.h b/include/views/application_message_dialog.h index a8be14a..6428ff9 100644 --- a/include/views/application_message_dialog.h +++ b/include/views/application_message_dialog.h @@ -18,6 +18,20 @@ typedef enum APPLICATION_MESSAGE_DIALOG_INFORMATION } ApplicationMessageDialogType; +/** + * @brief Callback appelé à la fermeture d'un dialogue de confirmation. + * + * Le callback est appelé une seule fois après un clic sur l'un des boutons + * ou après une fermeture manuelle de la fenêtre. + * + * @param confirmed TRUE si l'utilisateur confirme, sinon FALSE. + * @param user_data Données empruntées fournies par l'appelant. + */ +typedef void (*ApplicationMessageDialogConfirmationCallback)( + gboolean confirmed, + gpointer user_data +); + /** * @brief Affiche une fenêtre modale contenant un message. * @@ -35,4 +49,31 @@ void application_message_dialog_present( const char *message ); +/** + * @brief Affiche un dialogue modal demandant une confirmation. + * + * Le bouton d'annulation transmet FALSE. Le bouton de confirmation transmet + * TRUE. Fermer la fenêtre manuellement équivaut à une annulation. + * + * callback et user_data sont empruntés jusqu'à la fermeture du dialogue. + * confirm_label peut être NULL ou vide pour utiliser « Confirmer ». + * + * @param parent_window Fenêtre parente, ou NULL. + * @param message_type Type de message. + * @param title Titre de la fenêtre, ou NULL. + * @param message Message principal, ou NULL. + * @param confirm_label Libellé du bouton de confirmation, ou NULL. + * @param callback Callback facultatif recevant la réponse. + * @param user_data Données empruntées transmises au callback. + */ +void application_message_dialog_present_confirmation( + GtkWindow *parent_window, + ApplicationMessageDialogType message_type, + const char *title, + const char *message, + const char *confirm_label, + ApplicationMessageDialogConfirmationCallback callback, + gpointer user_data +); + #endif diff --git a/src/core/application.c b/src/core/application.c index 499f5f0..644390a 100644 --- a/src/core/application.c +++ b/src/core/application.c @@ -4278,17 +4278,15 @@ static void application_on_graph_node_moved( } /** - * @brief Ferme proprement l'application. + * @brief Supprime les positions persistées et restaure la grille automatique. * - * @param user_data Pointeur vers Application. + * Cette fonction est appelée uniquement après une confirmation explicite. + * L'affichage et le modèle en mémoire restent inchangés en cas d'échec SQLite. */ -static void application_on_reset_graph_layout_requested( - gpointer user_data +static void application_reset_graph_layout( + Application *application ) { - Application *application = - user_data; - Database *database = NULL; @@ -4402,6 +4400,61 @@ static void application_on_reset_graph_layout_requested( ); } +/** + * @brief Traite la réponse du dialogue de confirmation. + */ +static void application_on_reset_graph_layout_confirmed( + gboolean confirmed, + gpointer user_data +) +{ + Application *application = + user_data; + + if (!confirmed) + { + return; + } + + application_reset_graph_layout( + application + ); +} + +/** + * @brief Demande confirmation avant toute suppression de position. + */ +static void application_on_reset_graph_layout_requested( + gpointer user_data +) +{ + Application *application = + user_data; + + if (application == NULL || + application->main_window == NULL || + application->session == NULL || + application->graph_model == NULL || + application->graph_layout == NULL) + { + return; + } + + application_message_dialog_present_confirmation( + main_window_get_window( + application->main_window + ), + APPLICATION_MESSAGE_DIALOG_WARNING, + "Réinitialiser la disposition du graphe", + "Toutes les positions personnalisées des nœuds seront supprimées. " + "Le graphe retrouvera sa disposition automatique.\n\n" + "Cette action ne peut pas être annulée.", + "Réinitialiser", + application_on_reset_graph_layout_confirmed, + application + ); +} + /** * @brief Ferme proprement l'application. * diff --git a/src/views/application_message_dialog.c b/src/views/application_message_dialog.c index 1a1f0b2..dc0a2c7 100644 --- a/src/views/application_message_dialog.c +++ b/src/views/application_message_dialog.c @@ -16,6 +16,25 @@ #define APPLICATION_MESSAGE_DIALOG_DEFAULT_MESSAGE \ "Aucun détail supplémentaire n'est disponible." +/** + * @brief Libellé par défaut du bouton de confirmation. + */ +#define APPLICATION_MESSAGE_DIALOG_DEFAULT_CONFIRM_LABEL \ + "Confirmer" + +/** + * @brief État privé d'un dialogue de confirmation. + */ +typedef struct +{ + GtkWindow *dialog_window; + + ApplicationMessageDialogConfirmationCallback callback; + gpointer user_data; + + gboolean completed; +} ApplicationMessageDialogConfirmationContext; + /** * @brief Retourne le libellé correspondant au type de message. * @@ -335,3 +354,528 @@ void application_message_dialog_present( dialog_window ); } + +/** + * @brief Libère le contexte associé à une fenêtre de confirmation. + */ +static void application_message_dialog_confirmation_context_free( + gpointer data +) +{ + ApplicationMessageDialogConfirmationContext *context = + data; + + if (context == NULL) + { + return; + } + + context->dialog_window = + NULL; + + context->callback = + NULL; + + context->user_data = + NULL; + + g_free( + context + ); +} + +/** + * @brief Termine une confirmation et transmet son résultat une seule fois. + * + * Lorsque destroy_window vaut TRUE, la fenêtre est détruite avant l'appel + * du callback. Les valeurs du callback sont donc copiées localement avant + * la destruction du contexte attaché à la fenêtre. + */ +static void application_message_dialog_complete_confirmation( + ApplicationMessageDialogConfirmationContext *context, + gboolean confirmed, + gboolean destroy_window +) +{ + ApplicationMessageDialogConfirmationCallback callback = + NULL; + + gpointer user_data = + NULL; + + GtkWindow *dialog_window = + NULL; + + if (context == NULL || + context->completed) + { + return; + } + + context->completed = + TRUE; + + callback = + context->callback; + + user_data = + context->user_data; + + dialog_window = + context->dialog_window; + + context->callback = + NULL; + + context->user_data = + NULL; + + if (destroy_window && + dialog_window != NULL) + { + gtk_window_destroy( + dialog_window + ); + } + + if (callback != NULL) + { + callback( + confirmed, + user_data + ); + } +} + +/** + * @brief Annule le dialogue depuis son bouton. + */ +static void application_message_dialog_on_cancel_clicked( + GtkButton *button, + gpointer user_data +) +{ + ApplicationMessageDialogConfirmationContext *context = + user_data; + + (void) button; + + application_message_dialog_complete_confirmation( + context, + FALSE, + TRUE + ); +} + +/** + * @brief Confirme le dialogue depuis son bouton principal. + */ +static void application_message_dialog_on_confirm_clicked( + GtkButton *button, + gpointer user_data +) +{ + ApplicationMessageDialogConfirmationContext *context = + user_data; + + (void) button; + + application_message_dialog_complete_confirmation( + context, + TRUE, + TRUE + ); +} + +/** + * @brief Traite la fermeture manuelle comme une annulation. + */ +static gboolean application_message_dialog_on_confirmation_close_requested( + GtkWindow *dialog_window, + gpointer user_data +) +{ + ApplicationMessageDialogConfirmationContext *context = + user_data; + + (void) dialog_window; + + application_message_dialog_complete_confirmation( + context, + FALSE, + FALSE + ); + + /* + * FALSE laisse GTK poursuivre la fermeture normale de la fenêtre. + */ + return FALSE; +} + +void application_message_dialog_present_confirmation( + GtkWindow *parent_window, + ApplicationMessageDialogType message_type, + const char *title, + const char *message, + const char *confirm_label, + ApplicationMessageDialogConfirmationCallback callback, + gpointer user_data +) +{ + GtkWindow *dialog_window = + NULL; + + GtkWidget *main_box = + NULL; + + GtkWidget *type_label = + NULL; + + GtkWidget *title_label = + NULL; + + GtkWidget *message_label = + NULL; + + GtkWidget *button_box = + NULL; + + GtkWidget *cancel_button = + NULL; + + GtkWidget *confirm_button = + NULL; + + ApplicationMessageDialogConfirmationContext *context = + NULL; + + const char *safe_title = + NULL; + + const char *safe_message = + NULL; + + const char *safe_confirm_label = + NULL; + + const char *type_text = + NULL; + + safe_title = + application_message_dialog_get_safe_title( + message_type, + title + ); + + safe_message = + application_message_dialog_get_safe_message( + message + ); + + safe_confirm_label = + confirm_label != NULL && + confirm_label[0] != '\0' + ? confirm_label + : APPLICATION_MESSAGE_DIALOG_DEFAULT_CONFIRM_LABEL; + + type_text = + application_message_dialog_get_type_label( + message_type + ); + + context = + g_try_new0( + ApplicationMessageDialogConfirmationContext, + 1 + ); + + if (context == NULL) + { + return; + } + + dialog_window = + GTK_WINDOW( + gtk_window_new() + ); + + context->dialog_window = + dialog_window; + + context->callback = + callback; + + context->user_data = + user_data; + + context->completed = + FALSE; + + g_object_set_data_full( + G_OBJECT( + dialog_window + ), + "application-message-dialog-confirmation-context", + context, + application_message_dialog_confirmation_context_free + ); + + gtk_window_set_title( + dialog_window, + safe_title + ); + + gtk_window_set_default_size( + dialog_window, + APPLICATION_MESSAGE_DIALOG_DEFAULT_WIDTH, + -1 + ); + + gtk_window_set_modal( + dialog_window, + TRUE + ); + + gtk_window_set_destroy_with_parent( + dialog_window, + TRUE + ); + + gtk_window_set_resizable( + dialog_window, + FALSE + ); + + if (parent_window != NULL) + { + gtk_window_set_transient_for( + dialog_window, + parent_window + ); + } + + main_box = + gtk_box_new( + GTK_ORIENTATION_VERTICAL, + 12 + ); + + gtk_widget_set_margin_start( + main_box, + 20 + ); + + gtk_widget_set_margin_end( + main_box, + 20 + ); + + gtk_widget_set_margin_top( + main_box, + 20 + ); + + gtk_widget_set_margin_bottom( + main_box, + 20 + ); + + type_label = + gtk_label_new( + type_text + ); + + gtk_widget_set_halign( + type_label, + GTK_ALIGN_START + ); + + gtk_label_set_xalign( + GTK_LABEL( + type_label + ), + 0.0F + ); + + title_label = + gtk_label_new( + safe_title + ); + + gtk_widget_set_halign( + title_label, + GTK_ALIGN_START + ); + + gtk_label_set_xalign( + GTK_LABEL( + title_label + ), + 0.0F + ); + + gtk_label_set_wrap( + GTK_LABEL( + title_label + ), + TRUE + ); + + gtk_widget_add_css_class( + title_label, + "title-2" + ); + + message_label = + gtk_label_new( + safe_message + ); + + gtk_widget_set_halign( + message_label, + GTK_ALIGN_FILL + ); + + gtk_label_set_xalign( + GTK_LABEL( + message_label + ), + 0.0F + ); + + gtk_label_set_wrap( + GTK_LABEL( + message_label + ), + TRUE + ); + + gtk_label_set_wrap_mode( + GTK_LABEL( + message_label + ), + PANGO_WRAP_WORD_CHAR + ); + + gtk_label_set_selectable( + GTK_LABEL( + message_label + ), + TRUE + ); + + gtk_label_set_max_width_chars( + GTK_LABEL( + message_label + ), + 70 + ); + + button_box = + gtk_box_new( + GTK_ORIENTATION_HORIZONTAL, + 8 + ); + + gtk_widget_set_halign( + button_box, + GTK_ALIGN_END + ); + + cancel_button = + gtk_button_new_with_label( + "Annuler" + ); + + confirm_button = + gtk_button_new_with_label( + safe_confirm_label + ); + + gtk_widget_add_css_class( + confirm_button, + "destructive-action" + ); + + g_signal_connect( + cancel_button, + "clicked", + G_CALLBACK( + application_message_dialog_on_cancel_clicked + ), + context + ); + + g_signal_connect( + confirm_button, + "clicked", + G_CALLBACK( + application_message_dialog_on_confirm_clicked + ), + context + ); + + g_signal_connect( + dialog_window, + "close-request", + G_CALLBACK( + application_message_dialog_on_confirmation_close_requested + ), + context + ); + + gtk_box_append( + GTK_BOX( + button_box + ), + cancel_button + ); + + gtk_box_append( + GTK_BOX( + button_box + ), + confirm_button + ); + + gtk_box_append( + GTK_BOX( + main_box + ), + type_label + ); + + gtk_box_append( + GTK_BOX( + main_box + ), + title_label + ); + + gtk_box_append( + GTK_BOX( + main_box + ), + message_label + ); + + gtk_box_append( + GTK_BOX( + main_box + ), + button_box + ); + + gtk_window_set_child( + dialog_window, + main_box + ); + + gtk_widget_grab_focus( + cancel_button + ); + + gtk_window_present( + dialog_window + ); +}