feat: add entity details side panel

This commit is contained in:
grayTerminal-sh 2026-07-21 21:37:33 +02:00
parent 866a304f2f
commit 995939bef8
6 changed files with 2469 additions and 182 deletions

View file

@ -0,0 +1,119 @@
/******************************************************************************
* @file entity_details_panel.h
* @brief Volet latéral affichant les détails d'une entité.
******************************************************************************/
#ifndef LABFY_INVESTIGATION_ENTITY_DETAILS_PANEL_H
#define LABFY_INVESTIGATION_ENTITY_DETAILS_PANEL_H
#include <gtk/gtk.h>
G_BEGIN_DECLS
/**
* @brief Modèle opaque représentant une entité.
*/
typedef struct EntityRecord EntityRecord;
/**
* @brief Représentation opaque du volet de détails.
*/
typedef struct EntityDetailsPanel EntityDetailsPanel;
/**
* @brief Callback appelé lorsque l'utilisateur ferme le volet.
*
* Le callback n'est pas déclenché lors d'une fermeture programmatique avec
* entity_details_panel_clear().
*
* @param user_data Données empruntées fournies par l'appelant.
*/
typedef void (*EntityDetailsPanelCloseCallback)(
gpointer user_data
);
/**
* @brief Crée un volet de détails fermé.
*
* @return Nouveau volet, ou NULL en cas d'échec.
*/
EntityDetailsPanel *entity_details_panel_new(void);
/**
* @brief Retourne le widget GTK racine du volet.
*
* Le widget appartient au module EntityDetailsPanel.
*
* @param details_panel Volet à consulter.
*
* @return Widget GTK racine, ou NULL.
*/
GtkWidget *entity_details_panel_get_widget(
const EntityDetailsPanel *details_panel
);
/**
* @brief Affiche une entité et ouvre le volet.
*
* Les textes sont immédiatement copiés dans les widgets GTK.
* Le pointeur EntityRecord n'est jamais conservé.
*
* Passer NULL équivaut à entity_details_panel_clear().
*
* @param details_panel Volet à mettre à jour.
* @param entity_record Entité empruntée, ou NULL.
*/
void entity_details_panel_set_entity(
EntityDetailsPanel *details_panel,
const EntityRecord *entity_record
);
/**
* @brief Vide et referme le volet sans appeler le callback de fermeture.
*
* @param details_panel Volet à vider.
*/
void entity_details_panel_clear(
EntityDetailsPanel *details_panel
);
/**
* @brief Définit le callback du bouton de fermeture.
*
* Le callback et user_data sont empruntés.
*
* @param details_panel Volet à configurer.
* @param callback Callback à appeler, ou NULL.
* @param user_data Données empruntées du callback.
*/
void entity_details_panel_set_close_callback(
EntityDetailsPanel *details_panel,
EntityDetailsPanelCloseCallback callback,
gpointer user_data
);
/**
* @brief Indique si le volet est actuellement ouvert.
*
* @param details_panel Volet à consulter.
*
* @return TRUE si le volet est ouvert, sinon FALSE.
*/
gboolean entity_details_panel_is_open(
const EntityDetailsPanel *details_panel
);
/**
* @brief Libère la structure d'encapsulation du volet.
*
* Cette fonction accepte NULL.
*
* @param details_panel Volet à libérer.
*/
void entity_details_panel_free(
EntityDetailsPanel *details_panel
);
G_END_DECLS
#endif

View file

@ -10,6 +10,11 @@
G_BEGIN_DECLS
/**
* @brief Représentation opaque d'une entité métier.
*/
typedef struct EntityRecord EntityRecord;
/**
* @brief Représentation opaque du graphe métier d'une enquête.
*/
@ -20,6 +25,19 @@ typedef struct InvestigationGraphModel InvestigationGraphModel;
*/
typedef struct InvestigationGraphView InvestigationGraphView;
/**
* @brief Callback appelé lors d'un changement de sélection.
*
* entity_record est emprunté au graphe. NULL représente une désélection.
*
* @param entity_record Entité sélectionnée empruntée, ou NULL.
* @param user_data Données empruntées fournies par l'appelant.
*/
typedef void (*InvestigationGraphViewSelectionCallback)(
const EntityRecord *entity_record,
gpointer user_data
);
/**
* @brief Crée une nouvelle vue graphique vide.
*
@ -67,6 +85,45 @@ void investigation_graph_view_clear(
InvestigationGraphView *graph_view
);
/**
* @brief Définit le callback de changement de sélection.
*
* Le callback et user_data sont empruntés par la vue.
*
* @param graph_view Vue graphique.
* @param callback Callback à appeler, ou NULL.
* @param user_data Données empruntées du callback.
*/
void investigation_graph_view_set_selection_callback(
InvestigationGraphView *graph_view,
InvestigationGraphViewSelectionCallback callback,
gpointer user_data
);
/**
* @brief Retourne l'entité actuellement sélectionnée.
*
* Le pointeur retourné est emprunté au graphe.
*
* @param graph_view Vue graphique à consulter.
*
* @return Entité sélectionnée empruntée, ou NULL.
*/
const EntityRecord *investigation_graph_view_get_selected_entity(
const InvestigationGraphView *graph_view
);
/**
* @brief Supprime la sélection courante.
*
* Le callback reçoit NULL seulement si une sélection existait.
*
* @param graph_view Vue graphique.
*/
void investigation_graph_view_clear_selection(
InvestigationGraphView *graph_view
);
/**
* @brief Réinitialise le zoom et la position du canvas.
*
@ -78,6 +135,17 @@ void investigation_graph_view_reset_view(
InvestigationGraphView *graph_view
);
/**
* @brief Restaure la disposition déterministe initiale des nœuds.
*
* Le zoom et le déplacement global du canvas sont conservés.
*
* @param graph_view Vue graphique à réorganiser.
*/
void investigation_graph_view_reset_layout(
InvestigationGraphView *graph_view
);
/**
* @brief Libère la structure d'encapsulation de la vue.
*

View file

@ -158,4 +158,3 @@ void workspace_free(
);
#endif

View file

@ -0,0 +1,985 @@
/******************************************************************************
* @file entity_details_panel.c
* @brief Implémentation du volet latéral de détails d'une entité.
******************************************************************************/
#include "widgets/entity_details_panel.h"
#include "models/entity_record.h"
#include <glib.h>
/**
* @struct EntityDetailsPanel
* @brief État privé du volet latéral.
*/
struct EntityDetailsPanel
{
GtkWidget *root_revealer;
GtkWidget *frame;
GtkWidget *close_button;
GtkWidget *entity_title_label;
GtkWidget *entity_value_label;
GtkWidget *entity_type_label;
GtkWidget *entity_description_label;
GtkWidget *entity_confidence_label;
GtkWidget *entity_status_label;
GtkWidget *entity_created_at_label;
GtkWidget *entity_updated_at_label;
GtkWidget *entity_identifier_label;
EntityDetailsPanelCloseCallback close_callback;
gpointer close_user_data;
};
/**
* @brief Définit une valeur avec un texte de remplacement.
*/
static void entity_details_panel_set_field_text(
GtkWidget *label,
const char *value,
const char *fallback
)
{
if (label == NULL)
{
return;
}
gtk_label_set_text(
GTK_LABEL(label),
value != NULL &&
value[0] != '\0'
? value
: fallback
);
}
/**
* @brief Ajoute une ligne de métadonnée dans la grille.
*
* @return Label GTK destiné à recevoir la valeur.
*/
static GtkWidget *entity_details_panel_add_field(
GtkGrid *grid,
gint row,
const char *title
)
{
GtkWidget *title_label =
NULL;
GtkWidget *value_label =
NULL;
if (grid == NULL ||
title == NULL ||
title[0] == '\0')
{
return NULL;
}
title_label =
gtk_label_new(
title
);
value_label =
gtk_label_new(
NULL
);
if (title_label == NULL ||
value_label == NULL)
{
return NULL;
}
gtk_label_set_xalign(
GTK_LABEL(title_label),
0.0F
);
gtk_widget_set_valign(
title_label,
GTK_ALIGN_START
);
gtk_widget_add_css_class(
title_label,
"dim-label"
);
gtk_label_set_xalign(
GTK_LABEL(value_label),
0.0F
);
gtk_label_set_wrap(
GTK_LABEL(value_label),
TRUE
);
gtk_label_set_wrap_mode(
GTK_LABEL(value_label),
PANGO_WRAP_WORD_CHAR
);
gtk_label_set_selectable(
GTK_LABEL(value_label),
TRUE
);
gtk_widget_set_hexpand(
value_label,
TRUE
);
gtk_widget_set_halign(
value_label,
GTK_ALIGN_FILL
);
gtk_widget_set_valign(
value_label,
GTK_ALIGN_START
);
gtk_grid_attach(
grid,
title_label,
0,
row,
1,
1
);
gtk_grid_attach(
grid,
value_label,
1,
row,
1,
1
);
return value_label;
}
/**
* @brief Convertit le statut métier en texte utilisateur.
*/
static const char *entity_details_panel_get_status_text(
EntityStatus status
)
{
switch (status)
{
case ENTITY_STATUS_ACTIVE:
return "Active";
case ENTITY_STATUS_ARCHIVED:
return "Archivée";
case ENTITY_STATUS_DELETED:
return "Supprimée";
case ENTITY_STATUS_UNKNOWN:
default:
return "Inconnu";
}
}
/**
* @brief Ferme le volet depuis le bouton de l'interface.
*/
static void entity_details_panel_on_close_clicked(
GtkButton *button,
gpointer user_data
)
{
EntityDetailsPanel *details_panel =
user_data;
(void) button;
if (details_panel == NULL)
{
return;
}
entity_details_panel_clear(
details_panel
);
if (details_panel->close_callback != NULL)
{
details_panel->close_callback(
details_panel->close_user_data
);
}
}
EntityDetailsPanel *entity_details_panel_new(void)
{
EntityDetailsPanel *details_panel =
NULL;
GtkWidget *content_box =
NULL;
GtkWidget *header_box =
NULL;
GtkWidget *header_label =
NULL;
GtkWidget *separator =
NULL;
GtkWidget *scrolled_window =
NULL;
GtkWidget *details_box =
NULL;
GtkWidget *details_grid =
NULL;
details_panel =
g_try_new0(
EntityDetailsPanel,
1
);
if (details_panel == NULL)
{
return NULL;
}
details_panel->root_revealer =
gtk_revealer_new();
details_panel->frame =
gtk_frame_new(
NULL
);
details_panel->close_button =
gtk_button_new_from_icon_name(
"window-close-symbolic"
);
content_box =
gtk_box_new(
GTK_ORIENTATION_VERTICAL,
0
);
header_box =
gtk_box_new(
GTK_ORIENTATION_HORIZONTAL,
8
);
header_label =
gtk_label_new(
"Détails de lentité"
);
separator =
gtk_separator_new(
GTK_ORIENTATION_HORIZONTAL
);
scrolled_window =
gtk_scrolled_window_new();
details_box =
gtk_box_new(
GTK_ORIENTATION_VERTICAL,
16
);
details_grid =
gtk_grid_new();
if (details_panel->root_revealer == NULL ||
details_panel->frame == NULL ||
details_panel->close_button == NULL ||
content_box == NULL ||
header_box == NULL ||
header_label == NULL ||
separator == NULL ||
scrolled_window == NULL ||
details_box == NULL ||
details_grid == NULL)
{
entity_details_panel_free(
details_panel
);
return NULL;
}
gtk_widget_set_size_request(
details_panel->root_revealer,
360,
-1
);
gtk_widget_set_halign(
details_panel->root_revealer,
GTK_ALIGN_END
);
gtk_widget_set_valign(
details_panel->root_revealer,
GTK_ALIGN_FILL
);
gtk_widget_set_vexpand(
details_panel->root_revealer,
TRUE
);
gtk_revealer_set_transition_type(
GTK_REVEALER(
details_panel->root_revealer
),
GTK_REVEALER_TRANSITION_TYPE_SLIDE_LEFT
);
gtk_revealer_set_transition_duration(
GTK_REVEALER(
details_panel->root_revealer
),
180
);
gtk_revealer_set_reveal_child(
GTK_REVEALER(
details_panel->root_revealer
),
FALSE
);
gtk_widget_set_margin_top(
details_panel->frame,
12
);
gtk_widget_set_margin_end(
details_panel->frame,
12
);
gtk_widget_set_margin_bottom(
details_panel->frame,
12
);
gtk_widget_add_css_class(
details_panel->frame,
"view"
);
gtk_widget_set_margin_start(
header_box,
16
);
gtk_widget_set_margin_end(
header_box,
8
);
gtk_widget_set_margin_top(
header_box,
8
);
gtk_widget_set_margin_bottom(
header_box,
8
);
gtk_label_set_xalign(
GTK_LABEL(header_label),
0.0F
);
gtk_widget_set_hexpand(
header_label,
TRUE
);
gtk_widget_set_halign(
header_label,
GTK_ALIGN_FILL
);
gtk_widget_add_css_class(
header_label,
"heading"
);
gtk_widget_set_tooltip_text(
details_panel->close_button,
"Fermer le volet"
);
gtk_widget_add_css_class(
details_panel->close_button,
"flat"
);
gtk_box_append(
GTK_BOX(header_box),
header_label
);
gtk_box_append(
GTK_BOX(header_box),
details_panel->close_button
);
gtk_box_append(
GTK_BOX(content_box),
header_box
);
gtk_box_append(
GTK_BOX(content_box),
separator
);
gtk_scrolled_window_set_policy(
GTK_SCROLLED_WINDOW(scrolled_window),
GTK_POLICY_NEVER,
GTK_POLICY_AUTOMATIC
);
gtk_widget_set_vexpand(
scrolled_window,
TRUE
);
gtk_widget_set_margin_start(
details_box,
20
);
gtk_widget_set_margin_end(
details_box,
20
);
gtk_widget_set_margin_top(
details_box,
20
);
gtk_widget_set_margin_bottom(
details_box,
20
);
details_panel->entity_title_label =
gtk_label_new(
NULL
);
if (details_panel->entity_title_label == NULL)
{
entity_details_panel_free(
details_panel
);
return NULL;
}
gtk_label_set_xalign(
GTK_LABEL(
details_panel->entity_title_label
),
0.0F
);
gtk_label_set_wrap(
GTK_LABEL(
details_panel->entity_title_label
),
TRUE
);
gtk_label_set_selectable(
GTK_LABEL(
details_panel->entity_title_label
),
TRUE
);
gtk_widget_add_css_class(
details_panel->entity_title_label,
"title-3"
);
gtk_box_append(
GTK_BOX(details_box),
details_panel->entity_title_label
);
gtk_grid_set_column_spacing(
GTK_GRID(details_grid),
16
);
gtk_grid_set_row_spacing(
GTK_GRID(details_grid),
12
);
gtk_widget_set_hexpand(
details_grid,
TRUE
);
details_panel->entity_value_label =
entity_details_panel_add_field(
GTK_GRID(details_grid),
0,
"Valeur"
);
details_panel->entity_type_label =
entity_details_panel_add_field(
GTK_GRID(details_grid),
1,
"Type"
);
details_panel->entity_description_label =
entity_details_panel_add_field(
GTK_GRID(details_grid),
2,
"Description"
);
details_panel->entity_confidence_label =
entity_details_panel_add_field(
GTK_GRID(details_grid),
3,
"Confiance"
);
details_panel->entity_status_label =
entity_details_panel_add_field(
GTK_GRID(details_grid),
4,
"Statut"
);
details_panel->entity_created_at_label =
entity_details_panel_add_field(
GTK_GRID(details_grid),
5,
"Créée le"
);
details_panel->entity_updated_at_label =
entity_details_panel_add_field(
GTK_GRID(details_grid),
6,
"Modifiée le"
);
details_panel->entity_identifier_label =
entity_details_panel_add_field(
GTK_GRID(details_grid),
7,
"Identifiant"
);
if (details_panel->entity_value_label == NULL ||
details_panel->entity_type_label == NULL ||
details_panel->entity_description_label == NULL ||
details_panel->entity_confidence_label == NULL ||
details_panel->entity_status_label == NULL ||
details_panel->entity_created_at_label == NULL ||
details_panel->entity_updated_at_label == NULL ||
details_panel->entity_identifier_label == NULL)
{
entity_details_panel_free(
details_panel
);
return NULL;
}
gtk_box_append(
GTK_BOX(details_box),
details_grid
);
gtk_scrolled_window_set_child(
GTK_SCROLLED_WINDOW(scrolled_window),
details_box
);
gtk_box_append(
GTK_BOX(content_box),
scrolled_window
);
gtk_frame_set_child(
GTK_FRAME(details_panel->frame),
content_box
);
gtk_revealer_set_child(
GTK_REVEALER(
details_panel->root_revealer
),
details_panel->frame
);
g_signal_connect(
details_panel->close_button,
"clicked",
G_CALLBACK(
entity_details_panel_on_close_clicked
),
details_panel
);
entity_details_panel_clear(
details_panel
);
return details_panel;
}
GtkWidget *entity_details_panel_get_widget(
const EntityDetailsPanel *details_panel
)
{
if (details_panel == NULL)
{
return NULL;
}
return details_panel->root_revealer;
}
void entity_details_panel_set_entity(
EntityDetailsPanel *details_panel,
const EntityRecord *entity_record
)
{
const char *title =
NULL;
char *confidence_text =
NULL;
if (details_panel == NULL)
{
return;
}
if (entity_record == NULL)
{
entity_details_panel_clear(
details_panel
);
return;
}
title =
entity_record_get_label(
entity_record
);
if (title == NULL ||
title[0] == '\0')
{
title =
entity_record_get_value(
entity_record
);
}
if (title == NULL ||
title[0] == '\0')
{
title =
entity_record_get_identifier(
entity_record
);
}
confidence_text =
g_strdup_printf(
"%d %%",
entity_record_get_confidence(
entity_record
)
);
entity_details_panel_set_field_text(
details_panel->entity_title_label,
title,
"(entité sans libellé)"
);
entity_details_panel_set_field_text(
details_panel->entity_value_label,
entity_record_get_value(
entity_record
),
"(valeur inconnue)"
);
entity_details_panel_set_field_text(
details_panel->entity_type_label,
entity_record_get_type_identifier(
entity_record
),
"(type inconnu)"
);
entity_details_panel_set_field_text(
details_panel->entity_description_label,
entity_record_get_description(
entity_record
),
"Non renseignée"
);
entity_details_panel_set_field_text(
details_panel->entity_confidence_label,
confidence_text,
"Inconnue"
);
entity_details_panel_set_field_text(
details_panel->entity_status_label,
entity_details_panel_get_status_text(
entity_record_get_status(
entity_record
)
),
"Inconnu"
);
entity_details_panel_set_field_text(
details_panel->entity_created_at_label,
entity_record_get_created_at(
entity_record
),
"(date inconnue)"
);
entity_details_panel_set_field_text(
details_panel->entity_updated_at_label,
entity_record_get_updated_at(
entity_record
),
"(date inconnue)"
);
entity_details_panel_set_field_text(
details_panel->entity_identifier_label,
entity_record_get_identifier(
entity_record
),
"(identifiant inconnu)"
);
gtk_revealer_set_reveal_child(
GTK_REVEALER(
details_panel->root_revealer
),
TRUE
);
g_free(
confidence_text
);
}
void entity_details_panel_clear(
EntityDetailsPanel *details_panel
)
{
if (details_panel == NULL)
{
return;
}
entity_details_panel_set_field_text(
details_panel->entity_title_label,
NULL,
""
);
entity_details_panel_set_field_text(
details_panel->entity_value_label,
NULL,
""
);
entity_details_panel_set_field_text(
details_panel->entity_type_label,
NULL,
""
);
entity_details_panel_set_field_text(
details_panel->entity_description_label,
NULL,
""
);
entity_details_panel_set_field_text(
details_panel->entity_confidence_label,
NULL,
""
);
entity_details_panel_set_field_text(
details_panel->entity_status_label,
NULL,
""
);
entity_details_panel_set_field_text(
details_panel->entity_created_at_label,
NULL,
""
);
entity_details_panel_set_field_text(
details_panel->entity_updated_at_label,
NULL,
""
);
entity_details_panel_set_field_text(
details_panel->entity_identifier_label,
NULL,
""
);
if (details_panel->root_revealer != NULL)
{
gtk_revealer_set_reveal_child(
GTK_REVEALER(
details_panel->root_revealer
),
FALSE
);
}
}
void entity_details_panel_set_close_callback(
EntityDetailsPanel *details_panel,
EntityDetailsPanelCloseCallback callback,
gpointer user_data
)
{
if (details_panel == NULL)
{
return;
}
details_panel->close_callback =
callback;
details_panel->close_user_data =
user_data;
}
gboolean entity_details_panel_is_open(
const EntityDetailsPanel *details_panel
)
{
if (details_panel == NULL ||
details_panel->root_revealer == NULL)
{
return FALSE;
}
return gtk_revealer_get_reveal_child(
GTK_REVEALER(
details_panel->root_revealer
)
);
}
void entity_details_panel_free(
EntityDetailsPanel *details_panel
)
{
if (details_panel == NULL)
{
return;
}
if (details_panel->close_button != NULL)
{
g_signal_handlers_disconnect_by_data(
details_panel->close_button,
details_panel
);
}
details_panel->close_callback =
NULL;
details_panel->close_user_data =
NULL;
details_panel->root_revealer =
NULL;
details_panel->frame =
NULL;
details_panel->close_button =
NULL;
details_panel->entity_title_label =
NULL;
details_panel->entity_value_label =
NULL;
details_panel->entity_type_label =
NULL;
details_panel->entity_description_label =
NULL;
details_panel->entity_confidence_label =
NULL;
details_panel->entity_status_label =
NULL;
details_panel->entity_created_at_label =
NULL;
details_panel->entity_updated_at_label =
NULL;
details_panel->entity_identifier_label =
NULL;
g_free(
details_panel
);
}

File diff suppressed because it is too large Load diff

View file

@ -5,6 +5,7 @@
#include "widgets/workspace.h"
#include "widgets/entity_details_panel.h"
#include "widgets/investigation_graph_view.h"
#include <glib.h>
@ -45,7 +46,10 @@ struct Workspace
GtkWidget *graph_title_label;
GtkWidget *graph_details_label;
GtkWidget *graph_view_page;
InvestigationGraphView *graph_view;
EntityDetailsPanel *entity_details_panel;
GtkWidget *node_page;
@ -250,6 +254,50 @@ static const char *workspace_get_integrity_status_text(
}
}
/**
* @brief Transmet la sélection du graphe au volet de détails.
*/
static void workspace_on_graph_entity_selected(
const EntityRecord *entity_record,
gpointer user_data
)
{
Workspace *workspace =
user_data;
if (workspace == NULL ||
workspace->entity_details_panel == NULL)
{
return;
}
entity_details_panel_set_entity(
workspace->entity_details_panel,
entity_record
);
}
/**
* @brief Désélectionne le nœud lorsque le volet est fermé manuellement.
*/
static void workspace_on_entity_details_closed(
gpointer user_data
)
{
Workspace *workspace =
user_data;
if (workspace == NULL ||
workspace->graph_view == NULL)
{
return;
}
investigation_graph_view_clear_selection(
workspace->graph_view
);
}
/**
* @brief Affiche l'état principal courant du Workspace.
*
@ -896,12 +944,12 @@ Workspace *workspace_new(void)
);
/*
* Vue graphique en lecture seule.
* Page graphique : canvas principal et volet de détails superposé.
*/
workspace->graph_view =
investigation_graph_view_new();
workspace->graph_view_page =
gtk_overlay_new();
if (workspace->graph_view == NULL)
if (workspace->graph_view_page == NULL)
{
workspace_free(
workspace
@ -910,11 +958,75 @@ Workspace *workspace_new(void)
return NULL;
}
gtk_stack_add_named(
GTK_STACK(workspace->stack),
gtk_widget_set_hexpand(
workspace->graph_view_page,
TRUE
);
gtk_widget_set_vexpand(
workspace->graph_view_page,
TRUE
);
workspace->graph_view =
investigation_graph_view_new();
workspace->entity_details_panel =
entity_details_panel_new();
if (workspace->graph_view == NULL ||
workspace->entity_details_panel == NULL)
{
workspace_free(
workspace
);
return NULL;
}
investigation_graph_view_set_selection_callback(
workspace->graph_view,
workspace_on_graph_entity_selected,
workspace
);
entity_details_panel_set_close_callback(
workspace->entity_details_panel,
workspace_on_entity_details_closed,
workspace
);
gtk_overlay_set_child(
GTK_OVERLAY(
workspace->graph_view_page
),
investigation_graph_view_get_widget(
workspace->graph_view
)
);
gtk_overlay_add_overlay(
GTK_OVERLAY(
workspace->graph_view_page
),
entity_details_panel_get_widget(
workspace->entity_details_panel
)
);
gtk_overlay_set_clip_overlay(
GTK_OVERLAY(
workspace->graph_view_page
),
entity_details_panel_get_widget(
workspace->entity_details_panel
),
TRUE
);
gtk_stack_add_named(
GTK_STACK(workspace->stack),
workspace->graph_view_page,
WORKSPACE_PAGE_GRAPH_VIEW
);
@ -1283,6 +1395,10 @@ void workspace_set_graph_loading(
);
}
entity_details_panel_clear(
workspace->entity_details_panel
);
investigation_graph_view_clear(
workspace->graph_view
);
@ -1355,6 +1471,10 @@ void workspace_set_graph(
);
}
entity_details_panel_clear(
workspace->entity_details_panel
);
workspace->graph_model =
graph_model;
@ -1419,6 +1539,10 @@ void workspace_set_graph_error(
);
}
entity_details_panel_clear(
workspace->entity_details_panel
);
investigation_graph_view_clear(
workspace->graph_view
);
@ -1484,6 +1608,10 @@ void workspace_clear_graph(
);
}
entity_details_panel_clear(
workspace->entity_details_panel
);
investigation_graph_view_clear(
workspace->graph_view
);
@ -1549,6 +1677,22 @@ void workspace_free(Workspace *workspace)
return;
}
investigation_graph_view_set_selection_callback(
workspace->graph_view,
NULL,
NULL
);
entity_details_panel_set_close_callback(
workspace->entity_details_panel,
NULL,
NULL
);
entity_details_panel_clear(
workspace->entity_details_panel
);
investigation_graph_view_clear(
workspace->graph_view
);
@ -1557,9 +1701,16 @@ void workspace_free(Workspace *workspace)
workspace->graph_view
);
entity_details_panel_free(
workspace->entity_details_panel
);
workspace->graph_view =
NULL;
workspace->entity_details_panel =
NULL;
workspace->graph_model =
NULL;
@ -1576,4 +1727,3 @@ void workspace_free(Workspace *workspace)
g_free(workspace);
}