labfy-investigation/src/views/main_window.c
2026-07-22 08:03:24 +02:00

1460 lines
29 KiB
C

/******************************************************************************
* @file main_window.c
* @brief Implémentation de la fenêtre principale.
******************************************************************************/
#include "views/main_window.h"
#include "widgets/sidebar.h"
#include "widgets/workspace.h"
#include "widgets/investigation_tree_view.h"
#include "widgets/task_panel.h"
#include <glib.h>
/**
* @brief Largeur initiale de la fenêtre principale.
*/
#define MAIN_WINDOW_DEFAULT_WIDTH 1100
/**
* @brief Hauteur initiale de la fenêtre principale.
*/
#define MAIN_WINDOW_DEFAULT_HEIGHT 650
/**
* @brief Position initiale de la séparation horizontale.
*/
#define MAIN_WINDOW_SIDEBAR_POSITION 320
/**
* @brief Position initiale de la séparation verticale.
*/
#define MAIN_WINDOW_TASK_PANEL_POSITION 430
/**
* @brief Titre par défaut de la fenêtre.
*/
#define MAIN_WINDOW_DEFAULT_TITLE \
"Labfy Investigation"
/**
* @brief Texte affiché lorsqu'aucune enquête n'est ouverte.
*/
#define MAIN_WINDOW_NO_INVESTIGATION_STATUS \
"Aucune enquête ouverte"
/**
* @struct MainWindow
* @brief Représentation interne de la fenêtre principale.
*
* Cette structure reste privée au module. Elle conserve les composants
* nécessaires à l'organisation générale de l'interface.
*/
struct MainWindow
{
GtkWindow *window;
GtkWidget *main_box;
GtkWidget *action_bar;
GtkWidget *new_investigation_button;
GtkWidget *open_investigation_button;
GtkWidget *import_evidence_button;
GtkWidget *show_graph_button;
GtkWidget *content_paned;
GtkWidget *main_paned;
GtkWidget *status_label;
GtkWidget *quit_button;
Sidebar *sidebar;
Workspace *workspace;
TaskPanel *task_panel;
MainWindowNewInvestigationCallback
new_investigation_callback;
gpointer
new_investigation_user_data;
MainWindowOpenInvestigationCallback
open_investigation_callback;
gpointer
open_investigation_user_data;
MainWindowImportEvidenceCallback
import_evidence_callback;
gpointer
import_evidence_user_data;
MainWindowShowGraphCallback
show_graph_callback;
gpointer
show_graph_user_data;
MainWindowVerifyEvidenceCallback
verify_evidence_callback;
gpointer
verify_evidence_user_data;
MainWindowGraphNodeMovedCallback
graph_node_moved_callback;
gpointer
graph_node_moved_user_data;
MainWindowResetGraphLayoutCallback
reset_graph_layout_callback;
gpointer
reset_graph_layout_user_data;
MainWindowQuitCallback
quit_callback;
gpointer
quit_user_data;
};
/**
* @brief Transmet la demande de création d'une enquête au contrôleur.
*
* @param button Bouton ayant reçu le clic.
* @param user_data Pointeur vers MainWindow.
*/
static void main_window_on_new_investigation_clicked(
GtkButton *button,
gpointer user_data
)
{
MainWindow *main_window = user_data;
(void) button;
if (main_window == NULL ||
main_window->new_investigation_callback == NULL)
{
return;
}
main_window->new_investigation_callback(
main_window->new_investigation_user_data
);
}
/**
* @brief Transmet la demande d'ouverture d'une enquête au contrôleur.
*
* @param button Bouton ayant reçu le clic.
* @param user_data Pointeur vers MainWindow.
*/
static void main_window_on_open_investigation_clicked(
GtkButton *button,
gpointer user_data
)
{
MainWindow *main_window = user_data;
(void) button;
if (main_window == NULL ||
main_window->open_investigation_callback == NULL)
{
return;
}
main_window->open_investigation_callback(
main_window->open_investigation_user_data
);
}
/**
* @brief Transmet la demande d'import d'une preuve au contrôleur.
*
* @param button Bouton ayant reçu le clic.
* @param user_data Pointeur vers MainWindow.
*/
static void main_window_on_import_evidence_clicked(
GtkButton *button,
gpointer user_data
)
{
MainWindow *main_window = user_data;
(void) button;
if (main_window == NULL ||
main_window->import_evidence_callback == NULL)
{
return;
}
main_window->import_evidence_callback(
main_window->import_evidence_user_data
);
}
/**
* @brief Transmet la demande de fermeture au contrôleur.
*
* @param button Bouton ayant reçu le clic.
* @param user_data Pointeur vers MainWindow.
*/
static void main_window_on_show_graph_clicked(
GtkButton *button,
gpointer user_data
)
{
MainWindow *main_window =
user_data;
(void) button;
if (main_window == NULL ||
main_window->show_graph_callback == NULL)
{
return;
}
main_window->show_graph_callback(
main_window->show_graph_user_data
);
}
/**
* @brief Transmet la demande de fermeture au contrôleur.
*
* @param button Bouton ayant reçu le clic.
* @param user_data Pointeur vers MainWindow.
*/
static void main_window_on_quit_clicked(
GtkButton *button,
gpointer user_data
)
{
MainWindow *main_window = user_data;
(void) button;
if (main_window == NULL ||
main_window->quit_callback == NULL)
{
return;
}
main_window->quit_callback(
main_window->quit_user_data
);
}
/**
* @brief Crée un bouton d'action compact avec une icône.
*
* @param icon_name Nom de l'icône symbolique GTK.
* @param tooltip_text Texte de l'infobulle.
*
* @return Nouveau bouton GTK.
*/
static GtkWidget *main_window_create_action_button(
const char *icon_name,
const char *tooltip_text
)
{
GtkWidget *button = NULL;
if (icon_name == NULL ||
icon_name[0] == '\0')
{
return NULL;
}
button =
gtk_button_new_from_icon_name(
icon_name
);
if (button == NULL)
{
return NULL;
}
gtk_widget_add_css_class(
button,
"flat"
);
if (tooltip_text != NULL &&
tooltip_text[0] != '\0')
{
gtk_widget_set_tooltip_text(
button,
tooltip_text
);
}
return button;
}
/**
* @brief Relaie la demande de vérification provenant du Workspace.
*/
static void main_window_on_graph_node_moved(
const char *entity_identifier,
double x,
double y,
gpointer user_data
)
{
MainWindow *main_window =
user_data;
if (main_window == NULL ||
main_window->graph_node_moved_callback == NULL ||
entity_identifier == NULL ||
entity_identifier[0] == '\0')
{
return;
}
main_window->graph_node_moved_callback(
entity_identifier,
x,
y,
main_window->graph_node_moved_user_data
);
}
/**
* @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.
*/
static void main_window_on_verify_evidence_requested(
const char *evidence_identifier,
gpointer user_data
)
{
MainWindow *main_window =
user_data;
if (main_window == NULL ||
main_window->verify_evidence_callback == NULL ||
evidence_identifier == NULL ||
evidence_identifier[0] == '\0')
{
return;
}
main_window->verify_evidence_callback(
evidence_identifier,
main_window->verify_evidence_user_data
);
}
MainWindow *main_window_new(
GtkApplication *application,
TaskManager *task_manager
)
{
MainWindow *main_window = NULL;
GtkWidget *sidebar_widget = NULL;
GtkWidget *workspace_widget = NULL;
GtkWidget *task_panel_widget = NULL;
GtkWidget *action_spacer = NULL;
if (application == NULL ||
task_manager == NULL)
{
return NULL;
}
main_window = g_new0(MainWindow, 1);
main_window->window = GTK_WINDOW(
gtk_application_window_new(application)
);
if (main_window->window == NULL)
{
main_window_free(main_window);
return NULL;
}
gtk_window_set_title(
main_window->window,
MAIN_WINDOW_DEFAULT_TITLE
);
gtk_window_set_default_size(
main_window->window,
MAIN_WINDOW_DEFAULT_WIDTH,
MAIN_WINDOW_DEFAULT_HEIGHT
);
/*
* La boîte principale organise verticalement :
*
* 1. la zone centrale ;
* 2. la barre d'état.
*/
main_window->main_box = gtk_box_new(
GTK_ORIENTATION_VERTICAL,
0
);
/*
* Barre regroupant les actions générales de l'application.
*/
main_window->action_bar = gtk_box_new(
GTK_ORIENTATION_HORIZONTAL,
4
);
gtk_widget_set_margin_start(
main_window->action_bar,
8
);
gtk_widget_set_margin_end(
main_window->action_bar,
8
);
gtk_widget_set_margin_top(
main_window->action_bar,
8
);
gtk_widget_set_margin_bottom(
main_window->action_bar,
8
);
main_window->new_investigation_button =
main_window_create_action_button(
"document-new-symbolic",
"Créer une nouvelle enquête"
);
main_window->open_investigation_button =
main_window_create_action_button(
"document-open-symbolic",
"Ouvrir une enquête"
);
main_window->import_evidence_button =
main_window_create_action_button(
"document-save-symbolic",
"Importer une preuve"
);
main_window->show_graph_button =
main_window_create_action_button(
"go-previous-symbolic",
"Revenir au graphe"
);
main_window->quit_button =
main_window_create_action_button(
"application-exit-symbolic",
"Quitter Labfy Investigation"
);
action_spacer =
gtk_box_new(
GTK_ORIENTATION_HORIZONTAL,
0
);
gtk_widget_set_hexpand(
action_spacer,
TRUE
);
/*
* Aucun import n'est autorisé sans enquête ouverte.
*/
gtk_widget_set_sensitive(
main_window->import_evidence_button,
FALSE
);
gtk_widget_set_sensitive(
main_window->show_graph_button,
FALSE
);
gtk_box_append(
GTK_BOX(main_window->action_bar),
main_window->new_investigation_button
);
gtk_box_append(
GTK_BOX(main_window->action_bar),
main_window->open_investigation_button
);
gtk_box_append(
GTK_BOX(main_window->action_bar),
main_window->import_evidence_button
);
gtk_box_append(
GTK_BOX(main_window->action_bar),
main_window->show_graph_button
);
gtk_box_append(
GTK_BOX(main_window->action_bar),
action_spacer
);
gtk_box_append(
GTK_BOX(main_window->action_bar),
main_window->quit_button
);
g_signal_connect(
main_window->new_investigation_button,
"clicked",
G_CALLBACK(
main_window_on_new_investigation_clicked
),
main_window
);
g_signal_connect(
main_window->open_investigation_button,
"clicked",
G_CALLBACK(
main_window_on_open_investigation_clicked
),
main_window
);
g_signal_connect(
main_window->import_evidence_button,
"clicked",
G_CALLBACK(
main_window_on_import_evidence_clicked
),
main_window
);
g_signal_connect(
main_window->show_graph_button,
"clicked",
G_CALLBACK(
main_window_on_show_graph_clicked
),
main_window
);
g_signal_connect(
main_window->quit_button,
"clicked",
G_CALLBACK(
main_window_on_quit_clicked
),
main_window
);
/*
* GtkPaned sépare horizontalement le panneau latéral
* et la zone de travail.
*/
main_window->main_paned = gtk_paned_new(
GTK_ORIENTATION_HORIZONTAL
);
gtk_widget_set_hexpand(
main_window->main_paned,
TRUE
);
gtk_widget_set_vexpand(
main_window->main_paned,
TRUE
);
/*
* Création du panneau latéral via son propre module.
*
* MainWindow ne connaît pas son contenu interne.
*/
main_window->sidebar = sidebar_new();
if (main_window->sidebar == NULL)
{
main_window_free(main_window);
return NULL;
}
sidebar_widget = sidebar_get_widget(
main_window->sidebar
);
if (sidebar_widget == NULL)
{
main_window_free(main_window);
return NULL;
}
main_window->workspace = workspace_new();
if (main_window->workspace == NULL)
{
main_window_free(main_window);
return NULL;
}
workspace_set_verify_evidence_callback(
main_window->workspace,
main_window_on_verify_evidence_requested,
main_window
);
workspace_set_graph_node_moved_callback(
main_window->workspace,
main_window_on_graph_node_moved,
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
);
if (workspace_widget == NULL)
{
main_window_free(main_window);
return NULL;
}
main_window->task_panel = task_panel_new(
task_manager
);
if (main_window->task_panel == NULL)
{
main_window_free(
main_window
);
return NULL;
}
task_panel_widget = task_panel_get_widget(
main_window->task_panel
);
if (task_panel_widget == NULL)
{
main_window_free(
main_window
);
return NULL;
}
/*
* Placement des deux composants dans GtkPaned.
*/
gtk_paned_set_start_child(
GTK_PANED(main_window->main_paned),
sidebar_widget
);
gtk_paned_set_end_child(
GTK_PANED(main_window->main_paned),
workspace_widget
);
/*
* Position initiale de la poignée de séparation.
*/
gtk_paned_set_position(
GTK_PANED(main_window->main_paned),
MAIN_WINDOW_SIDEBAR_POSITION
);
/*
* La sidebar conserve sa largeur lorsque la fenêtre est agrandie.
* La zone de travail récupère l'espace supplémentaire.
*/
gtk_paned_set_resize_start_child(
GTK_PANED(main_window->main_paned),
FALSE
);
gtk_paned_set_resize_end_child(
GTK_PANED(main_window->main_paned),
TRUE
);
/*
* Les deux panneaux peuvent être réduits manuellement.
*/
gtk_paned_set_shrink_start_child(
GTK_PANED(main_window->main_paned),
TRUE
);
gtk_paned_set_shrink_end_child(
GTK_PANED(main_window->main_paned),
TRUE
);
main_window->content_paned = gtk_paned_new(
GTK_ORIENTATION_VERTICAL
);
gtk_widget_set_hexpand(
main_window->content_paned,
TRUE
);
gtk_widget_set_vexpand(
main_window->content_paned,
TRUE
);
gtk_paned_set_start_child(
GTK_PANED(main_window->content_paned),
main_window->main_paned
);
gtk_paned_set_end_child(
GTK_PANED(main_window->content_paned),
task_panel_widget
);
gtk_paned_set_position(
GTK_PANED(main_window->content_paned),
MAIN_WINDOW_TASK_PANEL_POSITION
);
gtk_paned_set_resize_start_child(
GTK_PANED(main_window->content_paned),
TRUE
);
gtk_paned_set_resize_end_child(
GTK_PANED(main_window->content_paned),
FALSE
);
gtk_paned_set_shrink_start_child(
GTK_PANED(main_window->content_paned),
TRUE
);
gtk_paned_set_shrink_end_child(
GTK_PANED(main_window->content_paned),
TRUE
);
main_window->status_label = gtk_label_new(
MAIN_WINDOW_NO_INVESTIGATION_STATUS
);
gtk_widget_set_halign(
main_window->status_label,
GTK_ALIGN_START
);
gtk_widget_set_margin_start(
main_window->status_label,
8
);
gtk_widget_set_margin_end(
main_window->status_label,
8
);
gtk_widget_set_margin_top(
main_window->status_label,
6
);
gtk_widget_set_margin_bottom(
main_window->status_label,
6
);
/*
* Assemblage vertical :
*
* Barre d'actions
* GtkPaned
* Barre d'état
*/
gtk_box_append(
GTK_BOX(main_window->main_box),
main_window->action_bar
);
gtk_box_append(
GTK_BOX(main_window->main_box),
main_window->content_paned
);
gtk_box_append(
GTK_BOX(main_window->main_box),
main_window->status_label
);
gtk_window_set_child(
main_window->window,
main_window->main_box
);
/*
* Ce volet vertical sépare la zone principale du panneau d'activité.
*/
return main_window;
}
void main_window_present(MainWindow *main_window)
{
if (main_window == NULL || main_window->window == NULL)
{
return;
}
gtk_window_present(main_window->window);
}
GtkWindow *main_window_get_window(
const MainWindow *main_window
)
{
if (main_window == NULL)
{
return NULL;
}
return main_window->window;
}
void main_window_set_tree_model(
MainWindow *main_window,
const InvestigationTreeModel *tree_model
)
{
if (main_window == NULL)
{
return;
}
sidebar_set_tree_model(
main_window->sidebar,
tree_model
);
}
void main_window_set_evidence_model(
MainWindow *main_window,
EvidenceCategoryModel *evidence_category_model
)
{
if (main_window == NULL)
{
return;
}
sidebar_set_evidence_model(
main_window->sidebar,
evidence_category_model
);
}
void main_window_set_investigation(
MainWindow *main_window,
const char *investigation_name,
const char *investigation_root_path
)
{
char *window_title = NULL;
char *status_text = NULL;
gboolean has_name = FALSE;
gboolean has_root_path = FALSE;
if (main_window == NULL)
{
return;
}
has_name =
investigation_name != NULL &&
investigation_name[0] != '\0';
has_root_path =
investigation_root_path != NULL &&
investigation_root_path[0] != '\0';
if (has_name)
{
window_title = g_strdup_printf(
"%s — %s",
MAIN_WINDOW_DEFAULT_TITLE,
investigation_name
);
}
else
{
window_title = g_strdup(
MAIN_WINDOW_DEFAULT_TITLE
);
}
if (has_name && has_root_path)
{
status_text = g_strdup_printf(
"Enquête ouverte : %s — %s",
investigation_name,
investigation_root_path
);
}
else if (has_name)
{
status_text = g_strdup_printf(
"Enquête ouverte : %s",
investigation_name
);
}
else if (has_root_path)
{
status_text = g_strdup_printf(
"Enquête ouverte : %s",
investigation_root_path
);
}
else
{
status_text = g_strdup(
MAIN_WINDOW_NO_INVESTIGATION_STATUS
);
}
if (main_window->window != NULL)
{
gtk_window_set_title(
main_window->window,
window_title
);
}
if (main_window->status_label != NULL)
{
gtk_label_set_text(
GTK_LABEL(main_window->status_label),
status_text
);
}
g_free(status_text);
g_free(window_title);
}
void main_window_set_graph_loading(
MainWindow *main_window
)
{
if (main_window == NULL)
{
return;
}
gtk_widget_set_sensitive(
main_window->show_graph_button,
FALSE
);
workspace_set_graph_loading(
main_window->workspace
);
}
void main_window_set_graph(
MainWindow *main_window,
const InvestigationGraphModel *graph_model,
const InvestigationGraphLayout *graph_layout
)
{
if (main_window == NULL)
{
return;
}
workspace_set_graph(
main_window->workspace,
graph_model,
graph_layout
);
gtk_widget_set_sensitive(
main_window->show_graph_button,
graph_model != NULL
);
}
void main_window_set_graph_error(
MainWindow *main_window,
const char *message
)
{
if (main_window == NULL)
{
return;
}
gtk_widget_set_sensitive(
main_window->show_graph_button,
FALSE
);
workspace_set_graph_error(
main_window->workspace,
message
);
}
void main_window_clear_graph(
MainWindow *main_window
)
{
if (main_window == NULL)
{
return;
}
gtk_widget_set_sensitive(
main_window->show_graph_button,
FALSE
);
workspace_clear_graph(
main_window->workspace
);
}
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
)
{
const char *safe_status_text = NULL;
if (main_window == NULL ||
main_window->status_label == NULL)
{
return;
}
safe_status_text =
status_text != NULL &&
status_text[0] != '\0'
? status_text
: MAIN_WINDOW_NO_INVESTIGATION_STATUS;
gtk_label_set_text(
GTK_LABEL(main_window->status_label),
safe_status_text
);
}
void main_window_set_tree_selection_callback(
MainWindow *main_window,
InvestigationTreeViewSelectionCallback callback,
gpointer user_data
)
{
if (main_window == NULL)
{
return;
}
sidebar_set_selection_callback(
main_window->sidebar,
callback,
user_data
);
}
void main_window_set_evidence_selection_callback(
MainWindow *main_window,
MainWindowEvidenceSelectionCallback callback,
gpointer user_data
)
{
if (main_window == NULL)
{
return;
}
sidebar_set_evidence_selection_callback(
main_window->sidebar,
callback,
user_data
);
}
void main_window_set_new_investigation_callback(
MainWindow *main_window,
MainWindowNewInvestigationCallback callback,
gpointer user_data
)
{
if (main_window == NULL)
{
return;
}
main_window->new_investigation_callback = callback;
main_window->new_investigation_user_data = user_data;
}
void main_window_set_open_investigation_callback(
MainWindow *main_window,
MainWindowOpenInvestigationCallback callback,
gpointer user_data
)
{
if (main_window == NULL)
{
return;
}
main_window->open_investigation_callback = callback;
main_window->open_investigation_user_data = user_data;
}
void main_window_set_import_evidence_callback(
MainWindow *main_window,
MainWindowImportEvidenceCallback callback,
gpointer user_data
)
{
if (main_window == NULL)
{
return;
}
main_window->import_evidence_callback =
callback;
main_window->import_evidence_user_data =
user_data;
}
void main_window_set_show_graph_callback(
MainWindow *main_window,
MainWindowShowGraphCallback callback,
gpointer user_data
)
{
if (main_window == NULL)
{
return;
}
main_window->show_graph_callback =
callback;
main_window->show_graph_user_data =
user_data;
}
void main_window_set_import_evidence_enabled(
MainWindow *main_window,
gboolean enabled
)
{
if (main_window == NULL ||
main_window->import_evidence_button == NULL)
{
return;
}
gtk_widget_set_sensitive(
main_window->import_evidence_button,
enabled
);
}
void main_window_set_verify_evidence_callback(
MainWindow *main_window,
MainWindowVerifyEvidenceCallback callback,
gpointer user_data
)
{
if (main_window == NULL)
{
return;
}
main_window->verify_evidence_callback =
callback;
main_window->verify_evidence_user_data =
user_data;
}
void main_window_set_graph_node_moved_callback(
MainWindow *main_window,
MainWindowGraphNodeMovedCallback callback,
gpointer user_data
)
{
if (main_window == NULL)
{
return;
}
main_window->graph_node_moved_callback =
callback;
main_window->graph_node_moved_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(
MainWindow *main_window,
MainWindowQuitCallback callback,
gpointer user_data
)
{
if (main_window == NULL)
{
return;
}
main_window->quit_callback = callback;
main_window->quit_user_data = user_data;
}
void main_window_set_selected_node(
MainWindow *main_window,
const InvestigationNode *node
)
{
if (main_window == NULL)
{
return;
}
workspace_set_selected_node(
main_window->workspace,
node
);
}
void main_window_set_selected_evidence(
MainWindow *main_window,
const EvidenceRecord *evidence_record
)
{
if (main_window == NULL)
{
return;
}
workspace_set_selected_evidence(
main_window->workspace,
evidence_record
);
}
void main_window_free(
MainWindow *main_window
)
{
if (main_window == NULL)
{
return;
}
/*
* TaskPanel possède un timer et un callback enregistré dans
* TaskManager. Ils doivent être retirés avant la destruction
* des widgets GTK.
*/
task_panel_free(
main_window->task_panel
);
main_window->task_panel = NULL;
/*
* Empêche la destruction du modèle de transmettre une dernière
* sélection à Application.
*/
if (main_window->sidebar != NULL)
{
sidebar_set_selection_callback(
main_window->sidebar,
NULL,
NULL
);
/*
* Le modèle est détaché pendant que le GtkListView existe
* encore.
*/
sidebar_set_tree_model(
main_window->sidebar,
NULL
);
sidebar_set_evidence_selection_callback(
main_window->sidebar,
NULL,
NULL
);
sidebar_set_evidence_model(
main_window->sidebar,
NULL
);
}
/*
* Les structures qui manipulent encore leurs widgets doivent être
* nettoyées avant gtk_window_destroy().
*/
sidebar_free(
main_window->sidebar
);
if (main_window->workspace != NULL)
{
workspace_set_verify_evidence_callback(
main_window->workspace,
NULL,
NULL
);
workspace_set_graph_node_moved_callback(
main_window->workspace,
NULL,
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->show_graph_callback =
NULL;
main_window->show_graph_user_data =
NULL;
main_window_clear_graph(
main_window
);
}
workspace_free(
main_window->workspace
);
main_window->sidebar = NULL;
main_window->workspace = NULL;
/*
* Les modules ne manipulent plus leurs widgets.
* GTK peut maintenant détruire tout l'arbre sans callback vers
* des structures déjà libérées.
*/
if (main_window->window != NULL)
{
gtk_window_destroy(
main_window->window
);
main_window->window = NULL;
}
g_free(
main_window
);
}