Ajouter les relations déplaçables dans le graphe
Affiche les relations comme des nœuds indépendants et persistants. Généralise le stockage des positions aux entités et aux relations. Finalise la création de relation et le rafraîchissement du graphe.
This commit is contained in:
parent
b1d0ec2a1e
commit
bf56b09422
16 changed files with 1286 additions and 325 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Build
|
# Build
|
||||||
labfy-investigation
|
labfy-investigation
|
||||||
*.o
|
*.o
|
||||||
|
*.d
|
||||||
|
|
||||||
# Executables de tests
|
# Executables de tests
|
||||||
/tests/test_*
|
/tests/test_*
|
||||||
|
|
|
||||||
9
Makefile
9
Makefile
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
CC = gcc
|
CC = gcc
|
||||||
|
|
||||||
PKG_CONFIG = pkg-config
|
PKG_CONFIG = pkg-config
|
||||||
|
|
@ -11,6 +10,8 @@ CFLAGS = -std=c17 \
|
||||||
-Werror \
|
-Werror \
|
||||||
-g \
|
-g \
|
||||||
-Iinclude \
|
-Iinclude \
|
||||||
|
-MMD \
|
||||||
|
-MP \
|
||||||
$(shell $(PKG_CONFIG) --cflags gtk4 sqlite3)
|
$(shell $(PKG_CONFIG) --cflags gtk4 sqlite3)
|
||||||
|
|
||||||
LDFLAGS = $(shell $(PKG_CONFIG) --libs gtk4 sqlite3)
|
LDFLAGS = $(shell $(PKG_CONFIG) --libs gtk4 sqlite3)
|
||||||
|
|
@ -62,6 +63,8 @@ SRC := $(shell find src -name "*.c")
|
||||||
|
|
||||||
OBJ := $(SRC:.c=.o)
|
OBJ := $(SRC:.c=.o)
|
||||||
|
|
||||||
|
DEP := $(OBJ:.o=.d)
|
||||||
|
|
||||||
TARGET = labfy-investigation
|
TARGET = labfy-investigation
|
||||||
|
|
||||||
TEST_NODE = tests/test_investigation_node
|
TEST_NODE = tests/test_investigation_node
|
||||||
|
|
@ -649,7 +652,7 @@ run: $(TARGET)
|
||||||
./$(TARGET)
|
./$(TARGET)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(OBJ) $(TARGET) \
|
rm -f $(OBJ) $(DEP) $(TARGET) \
|
||||||
$(TEST_NODE) \
|
$(TEST_NODE) \
|
||||||
$(TEST_TREE_MODEL) \
|
$(TEST_TREE_MODEL) \
|
||||||
$(TEST_TREE_BUILDER) \
|
$(TEST_TREE_BUILDER) \
|
||||||
|
|
@ -695,4 +698,6 @@ clean:
|
||||||
$(TEST_INVESTIGATION_GRAPH_LOAD_TASK) \
|
$(TEST_INVESTIGATION_GRAPH_LOAD_TASK) \
|
||||||
$(TEST_GRAPH_NODE_POSITION_DAO)
|
$(TEST_GRAPH_NODE_POSITION_DAO)
|
||||||
|
|
||||||
|
-include $(DEP)
|
||||||
|
|
||||||
.PHONY: clean run test
|
.PHONY: clean run test
|
||||||
|
|
|
||||||
10
README.md
10
README.md
|
|
@ -23,16 +23,6 @@ Le projet vise à fournir un environnement local, modulaire et traçable pour or
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Labfy Investigation est un poste de travail libre d’investigation numérique et d’OSINT, développé en **C17** avec **GTK4**.
|
|
||||||
|
|
||||||
Le projet vise à fournir un environnement local, modulaire et traçable pour organiser une enquête, préserver les preuves originales, analyser des données, corréler des entités et produire des rapports exploitables.
|
|
||||||
|
|
||||||
> **État du projet : développement actif**
|
|
||||||
>
|
|
||||||
> Le logiciel n’est pas encore prêt pour un usage opérationnel en production. Les formats internes, l’interface et les mécanismes d’intégration peuvent encore évoluer.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Objectifs
|
## Objectifs
|
||||||
|
|
||||||
Labfy Investigation doit permettre de :
|
Labfy Investigation doit permettre de :
|
||||||
|
|
|
||||||
|
|
@ -57,3 +57,45 @@ CREATE TABLE IF NOT EXISTS graph_node_positions
|
||||||
length(trim(updated_at)) > 0
|
length(trim(updated_at)) > 0
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Disposition générique du graphe.
|
||||||
|
*
|
||||||
|
* Contrairement à graph_node_positions, cette table accepte aussi les UUID
|
||||||
|
* des relations. L'ancienne table reste présente pour assurer la compatibilité
|
||||||
|
* avec les enquêtes créées avant l'introduction des nœuds de relation.
|
||||||
|
*/
|
||||||
|
CREATE TABLE IF NOT EXISTS graph_layout_positions
|
||||||
|
(
|
||||||
|
node_id TEXT PRIMARY KEY,
|
||||||
|
x REAL NOT NULL,
|
||||||
|
y REAL NOT NULL,
|
||||||
|
updated_at TEXT NOT NULL,
|
||||||
|
|
||||||
|
CHECK (length(trim(node_id)) > 0),
|
||||||
|
CHECK (length(updated_at) = 20)
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Migration idempotente des positions d'entités déjà enregistrées. */
|
||||||
|
INSERT OR IGNORE INTO graph_layout_positions(node_id, x, y, updated_at)
|
||||||
|
SELECT entity_id, x, y, updated_at
|
||||||
|
FROM graph_node_positions;
|
||||||
|
|
||||||
|
/* Évite qu'une réinitialisation future ne réimporte des coordonnées obsolètes. */
|
||||||
|
DELETE FROM graph_node_positions;
|
||||||
|
|
||||||
|
/* Une clé étrangère polymorphe n'existe pas dans SQLite : ces triggers
|
||||||
|
* suppriment donc les positions devenues orphelines. */
|
||||||
|
CREATE TRIGGER IF NOT EXISTS graph_layout_positions_delete_entity
|
||||||
|
AFTER DELETE ON entites
|
||||||
|
FOR EACH ROW
|
||||||
|
BEGIN
|
||||||
|
DELETE FROM graph_layout_positions WHERE node_id = OLD.id;
|
||||||
|
END;
|
||||||
|
|
||||||
|
CREATE TRIGGER IF NOT EXISTS graph_layout_positions_delete_relation
|
||||||
|
AFTER DELETE ON relations
|
||||||
|
FOR EACH ROW
|
||||||
|
BEGIN
|
||||||
|
DELETE FROM graph_layout_positions WHERE node_id = OLD.id;
|
||||||
|
END;
|
||||||
|
|
|
||||||
|
|
@ -2181,6 +2181,30 @@ des fichiers ou des notes.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 11.5 Disposition générique du graphe
|
||||||
|
|
||||||
|
La disposition visuelle du graphe est un état de présentation et non une
|
||||||
|
donnée métier. Elle est stockée dans `graph_layout_positions`, séparément des
|
||||||
|
tables `entites` et `relations`.
|
||||||
|
|
||||||
|
Chaque ligne associe un UUID de nœud à des coordonnées logiques et à une date
|
||||||
|
UTC de mise à jour. Un nœud peut représenter une entité ou une relation. SQLite
|
||||||
|
ne proposant pas de clé étrangère polymorphe, l'intégrité de ce stockage est
|
||||||
|
assurée par deux triggers qui suppriment la position correspondante lors de la
|
||||||
|
suppression physique d'une entité ou d'une relation.
|
||||||
|
|
||||||
|
La table historique `graph_node_positions` ne référençait que `entites(id)`.
|
||||||
|
À l'ouverture d'une enquête, ses lignes sont copiées de manière idempotente
|
||||||
|
vers `graph_layout_positions`, puis retirées de la table historique afin
|
||||||
|
qu'une réinitialisation volontaire de la disposition ne puisse pas restaurer
|
||||||
|
des coordonnées obsolètes.
|
||||||
|
|
||||||
|
Cette stratégie préserve les positions existantes tout en permettant aux
|
||||||
|
nœuds de relation de suivre exactement le même cycle de chargement,
|
||||||
|
d'enregistrement et de réinitialisation que les nœuds d'entité.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
# 12. Conclusion
|
# 12. Conclusion
|
||||||
|
|
||||||
La base de données de Labfy Investigation constitue bien davantage qu'un simple
|
La base de données de Labfy Investigation constitue bien davantage qu'un simple
|
||||||
|
|
|
||||||
|
|
@ -91,12 +91,12 @@ GPtrArray *graph_node_position_dao_list_all(
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Insère ou met à jour la position d'une entité.
|
* @brief Insère ou met à jour la position d'un nœud.
|
||||||
*
|
*
|
||||||
* La date updated_at est générée en UTC par le DAO.
|
* La date updated_at est générée en UTC par le DAO.
|
||||||
*
|
*
|
||||||
* @param position_dao DAO valide.
|
* @param position_dao DAO valide.
|
||||||
* @param entity_identifier UUID de l'entité.
|
* @param node_identifier UUID du nœud (entité ou relation).
|
||||||
* @param x Coordonnée horizontale logique.
|
* @param x Coordonnée horizontale logique.
|
||||||
* @param y Coordonnée verticale logique.
|
* @param y Coordonnée verticale logique.
|
||||||
* @param error Adresse recevant une éventuelle erreur.
|
* @param error Adresse recevant une éventuelle erreur.
|
||||||
|
|
@ -105,26 +105,26 @@ GPtrArray *graph_node_position_dao_list_all(
|
||||||
*/
|
*/
|
||||||
gboolean graph_node_position_dao_upsert(
|
gboolean graph_node_position_dao_upsert(
|
||||||
GraphNodePositionDao *position_dao,
|
GraphNodePositionDao *position_dao,
|
||||||
const char *entity_identifier,
|
const char *node_identifier,
|
||||||
double x,
|
double x,
|
||||||
double y,
|
double y,
|
||||||
GError **error
|
GError **error
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Supprime la position persistée d'une entité.
|
* @brief Supprime la position persistée d'un nœud.
|
||||||
*
|
*
|
||||||
* L'absence de position n'est pas une erreur.
|
* L'absence de position n'est pas une erreur.
|
||||||
*
|
*
|
||||||
* @param position_dao DAO valide.
|
* @param position_dao DAO valide.
|
||||||
* @param entity_identifier UUID de l'entité.
|
* @param node_identifier UUID du nœud (entité ou relation).
|
||||||
* @param error Adresse recevant une éventuelle erreur.
|
* @param error Adresse recevant une éventuelle erreur.
|
||||||
*
|
*
|
||||||
* @return TRUE si la requête réussit, sinon FALSE.
|
* @return TRUE si la requête réussit, sinon FALSE.
|
||||||
*/
|
*/
|
||||||
gboolean graph_node_position_dao_delete(
|
gboolean graph_node_position_dao_delete(
|
||||||
GraphNodePositionDao *position_dao,
|
GraphNodePositionDao *position_dao,
|
||||||
const char *entity_identifier,
|
const char *node_identifier,
|
||||||
GError **error
|
GError **error
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,11 +42,11 @@ GQuark graph_node_position_error_quark(void);
|
||||||
*
|
*
|
||||||
* Toutes les chaînes sont copiées.
|
* Toutes les chaînes sont copiées.
|
||||||
*
|
*
|
||||||
* entity_identifier doit être un UUID valide.
|
* node_identifier doit être un UUID valide.
|
||||||
* x et y doivent être des nombres finis.
|
* x et y doivent être des nombres finis.
|
||||||
* updated_at doit respecter le format UTC YYYY-MM-DDTHH:MM:SSZ.
|
* updated_at doit respecter le format UTC YYYY-MM-DDTHH:MM:SSZ.
|
||||||
*
|
*
|
||||||
* @param entity_identifier UUID de l'entité.
|
* @param node_identifier UUID du nœud (entité ou relation).
|
||||||
* @param x Coordonnée horizontale logique.
|
* @param x Coordonnée horizontale logique.
|
||||||
* @param y Coordonnée verticale logique.
|
* @param y Coordonnée verticale logique.
|
||||||
* @param updated_at Date UTC de dernière modification.
|
* @param updated_at Date UTC de dernière modification.
|
||||||
|
|
@ -55,7 +55,7 @@ GQuark graph_node_position_error_quark(void);
|
||||||
* @return Nouvelle position, ou NULL lorsque les données sont invalides.
|
* @return Nouvelle position, ou NULL lorsque les données sont invalides.
|
||||||
*/
|
*/
|
||||||
GraphNodePosition *graph_node_position_new(
|
GraphNodePosition *graph_node_position_new(
|
||||||
const char *entity_identifier,
|
const char *node_identifier,
|
||||||
double x,
|
double x,
|
||||||
double y,
|
double y,
|
||||||
const char *updated_at,
|
const char *updated_at,
|
||||||
|
|
@ -74,7 +74,7 @@ void graph_node_position_free(
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Retourne l'UUID de l'entité.
|
* @brief Retourne l'UUID du nœud (entité ou relation).
|
||||||
*
|
*
|
||||||
* La chaîne retournée appartient au modèle.
|
* La chaîne retournée appartient au modèle.
|
||||||
*
|
*
|
||||||
|
|
@ -82,7 +82,7 @@ void graph_node_position_free(
|
||||||
*
|
*
|
||||||
* @return UUID emprunté, ou NULL.
|
* @return UUID emprunté, ou NULL.
|
||||||
*/
|
*/
|
||||||
const char *graph_node_position_get_entity_identifier(
|
const char *graph_node_position_get_node_identifier(
|
||||||
const GraphNodePosition *position
|
const GraphNodePosition *position
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -47,16 +47,16 @@ typedef void (*InvestigationGraphViewSelectionCallback)(
|
||||||
* @brief Callback appelé après le déplacement effectif d'un nœud.
|
* @brief Callback appelé après le déplacement effectif d'un nœud.
|
||||||
*
|
*
|
||||||
* Les coordonnées sont exprimées dans l'espace logique du graphe.
|
* Les coordonnées sont exprimées dans l'espace logique du graphe.
|
||||||
* entity_identifier est emprunté au modèle et reste valable uniquement
|
* node_identifier est emprunté au modèle et reste valable uniquement
|
||||||
* pendant l'appel.
|
* pendant l'appel.
|
||||||
*
|
*
|
||||||
* @param entity_identifier UUID de l'entité déplacée.
|
* @param node_identifier UUID du nœud déplacé (entité ou relation).
|
||||||
* @param x Coordonnée horizontale logique du coin supérieur gauche.
|
* @param x Coordonnée horizontale logique du coin supérieur gauche.
|
||||||
* @param y Coordonnée verticale logique du coin supérieur gauche.
|
* @param y Coordonnée verticale logique du coin supérieur gauche.
|
||||||
* @param user_data Données empruntées fournies par l'appelant.
|
* @param user_data Données empruntées fournies par l'appelant.
|
||||||
*/
|
*/
|
||||||
typedef void (*InvestigationGraphViewNodeMovedCallback)(
|
typedef void (*InvestigationGraphViewNodeMovedCallback)(
|
||||||
const char *entity_identifier,
|
const char *node_identifier,
|
||||||
double x,
|
double x,
|
||||||
double y,
|
double y,
|
||||||
gpointer user_data
|
gpointer user_data
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@
|
||||||
#include "widgets/evidence_category_model.h"
|
#include "widgets/evidence_category_model.h"
|
||||||
#include "core/evidence_integrity_task.h"
|
#include "core/evidence_integrity_task.h"
|
||||||
#include "core/evidence_integrity_verifier.h"
|
#include "core/evidence_integrity_verifier.h"
|
||||||
|
#include "core/relation_service.h"
|
||||||
#include "database/database.h"
|
#include "database/database.h"
|
||||||
|
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
|
|
@ -3341,6 +3342,21 @@ static void application_on_create_relation_completed(
|
||||||
Application *application =
|
Application *application =
|
||||||
user_data;
|
user_data;
|
||||||
|
|
||||||
|
const InvestigationProject *project =
|
||||||
|
NULL;
|
||||||
|
|
||||||
|
Database *database =
|
||||||
|
NULL;
|
||||||
|
|
||||||
|
RelationService *relation_service =
|
||||||
|
NULL;
|
||||||
|
|
||||||
|
RelationRecord *relation_record =
|
||||||
|
NULL;
|
||||||
|
|
||||||
|
GDateTime *current_date_time =
|
||||||
|
NULL;
|
||||||
|
|
||||||
const char *source_identifier =
|
const char *source_identifier =
|
||||||
NULL;
|
NULL;
|
||||||
|
|
||||||
|
|
@ -3350,9 +3366,21 @@ static void application_on_create_relation_completed(
|
||||||
const char *relation_type =
|
const char *relation_type =
|
||||||
NULL;
|
NULL;
|
||||||
|
|
||||||
char *status_message =
|
const char *database_path =
|
||||||
NULL;
|
NULL;
|
||||||
|
|
||||||
|
char *relation_identifier =
|
||||||
|
NULL;
|
||||||
|
|
||||||
|
char *timestamp =
|
||||||
|
NULL;
|
||||||
|
|
||||||
|
GError *error =
|
||||||
|
NULL;
|
||||||
|
|
||||||
|
gboolean relation_created =
|
||||||
|
FALSE;
|
||||||
|
|
||||||
if (application == NULL ||
|
if (application == NULL ||
|
||||||
application->main_window == NULL)
|
application->main_window == NULL)
|
||||||
{
|
{
|
||||||
|
|
@ -3373,6 +3401,17 @@ static void application_on_create_relation_completed(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (application->session == NULL)
|
||||||
|
{
|
||||||
|
application_present_error(
|
||||||
|
application,
|
||||||
|
"Ajout de relation impossible",
|
||||||
|
"Aucune enquête n'est actuellement ouverte."
|
||||||
|
);
|
||||||
|
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
source_identifier =
|
source_identifier =
|
||||||
create_relation_dialog_result_get_source_identifier(
|
create_relation_dialog_result_get_source_identifier(
|
||||||
result
|
result
|
||||||
|
|
@ -3388,41 +3427,225 @@ static void application_on_create_relation_completed(
|
||||||
result
|
result
|
||||||
);
|
);
|
||||||
|
|
||||||
g_message(
|
database =
|
||||||
"Relation préparée : %s -> %s (%s), confiance %d %%.",
|
investigation_session_get_database(
|
||||||
source_identifier != NULL
|
application->session
|
||||||
? source_identifier
|
|
||||||
: "(source absente)",
|
|
||||||
target_identifier != NULL
|
|
||||||
? target_identifier
|
|
||||||
: "(cible absente)",
|
|
||||||
relation_type != NULL
|
|
||||||
? relation_type
|
|
||||||
: "(type absent)",
|
|
||||||
create_relation_dialog_result_get_confidence(
|
|
||||||
result
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
status_message =
|
project =
|
||||||
g_strdup_printf(
|
investigation_session_get_project(
|
||||||
"Relation préparée : %s. "
|
application->session
|
||||||
"L'enregistrement SQLite sera branché à l'étape suivante.",
|
);
|
||||||
relation_type != NULL &&
|
|
||||||
relation_type[0] != '\0'
|
if (database == NULL ||
|
||||||
? relation_type
|
project == NULL)
|
||||||
: "(type inconnu)"
|
{
|
||||||
|
application_present_error(
|
||||||
|
application,
|
||||||
|
"Ajout de relation impossible",
|
||||||
|
"La session d'enquête est invalide."
|
||||||
|
);
|
||||||
|
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
database_path =
|
||||||
|
investigation_project_get_database_path(
|
||||||
|
project
|
||||||
|
);
|
||||||
|
|
||||||
|
if (database_path == NULL ||
|
||||||
|
database_path[0] == '\0')
|
||||||
|
{
|
||||||
|
application_present_error(
|
||||||
|
application,
|
||||||
|
"Ajout de relation impossible",
|
||||||
|
"Le chemin de la base SQLite est invalide."
|
||||||
|
);
|
||||||
|
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
relation_identifier =
|
||||||
|
g_uuid_string_random();
|
||||||
|
|
||||||
|
current_date_time =
|
||||||
|
g_date_time_new_now_utc();
|
||||||
|
|
||||||
|
if (current_date_time != NULL)
|
||||||
|
{
|
||||||
|
timestamp =
|
||||||
|
g_date_time_format(
|
||||||
|
current_date_time,
|
||||||
|
"%Y-%m-%dT%H:%M:%SZ"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (relation_identifier == NULL ||
|
||||||
|
timestamp == NULL)
|
||||||
|
{
|
||||||
|
application_present_error(
|
||||||
|
application,
|
||||||
|
"Ajout de relation impossible",
|
||||||
|
"Impossible de préparer l'identifiant ou la date "
|
||||||
|
"de la nouvelle relation."
|
||||||
|
);
|
||||||
|
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
relation_record =
|
||||||
|
relation_record_new(
|
||||||
|
relation_identifier,
|
||||||
|
source_identifier,
|
||||||
|
target_identifier,
|
||||||
|
relation_type,
|
||||||
|
create_relation_dialog_result_get_label(
|
||||||
|
result
|
||||||
|
),
|
||||||
|
create_relation_dialog_result_get_justification(
|
||||||
|
result
|
||||||
|
),
|
||||||
|
create_relation_dialog_result_get_confidence(
|
||||||
|
result
|
||||||
|
),
|
||||||
|
timestamp,
|
||||||
|
timestamp,
|
||||||
|
RELATION_STATUS_ACTIVE,
|
||||||
|
&error
|
||||||
|
);
|
||||||
|
|
||||||
|
if (relation_record == NULL)
|
||||||
|
{
|
||||||
|
application_present_error(
|
||||||
|
application,
|
||||||
|
"Relation invalide",
|
||||||
|
error != NULL
|
||||||
|
? error->message
|
||||||
|
: "Les informations de la relation sont invalides."
|
||||||
|
);
|
||||||
|
|
||||||
|
g_clear_error(
|
||||||
|
&error
|
||||||
|
);
|
||||||
|
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
relation_service =
|
||||||
|
relation_service_new(
|
||||||
|
database,
|
||||||
|
&error
|
||||||
|
);
|
||||||
|
|
||||||
|
if (relation_service == NULL)
|
||||||
|
{
|
||||||
|
application_present_error(
|
||||||
|
application,
|
||||||
|
"Ajout de relation impossible",
|
||||||
|
error != NULL
|
||||||
|
? error->message
|
||||||
|
: "Impossible de préparer le service des relations."
|
||||||
|
);
|
||||||
|
|
||||||
|
g_clear_error(
|
||||||
|
&error
|
||||||
|
);
|
||||||
|
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Aucune preuve n'est associée pendant ce premier parcours.
|
||||||
|
* Le service accepte donc un tableau NULL.
|
||||||
|
*/
|
||||||
|
if (!relation_service_create(
|
||||||
|
relation_service,
|
||||||
|
relation_record,
|
||||||
|
NULL,
|
||||||
|
&error
|
||||||
|
))
|
||||||
|
{
|
||||||
|
g_warning(
|
||||||
|
"Impossible d'enregistrer la relation '%s' : %s",
|
||||||
|
relation_identifier,
|
||||||
|
error != NULL
|
||||||
|
? error->message
|
||||||
|
: "erreur inconnue"
|
||||||
|
);
|
||||||
|
|
||||||
|
application_present_error(
|
||||||
|
application,
|
||||||
|
"Enregistrement de la relation impossible",
|
||||||
|
error != NULL
|
||||||
|
? error->message
|
||||||
|
: "La relation n'a pas pu être enregistrée."
|
||||||
|
);
|
||||||
|
|
||||||
|
g_clear_error(
|
||||||
|
&error
|
||||||
|
);
|
||||||
|
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
relation_created =
|
||||||
|
TRUE;
|
||||||
|
|
||||||
|
g_message(
|
||||||
|
"Relation enregistrée : %s -> %s (%s), identifiant %s.",
|
||||||
|
source_identifier,
|
||||||
|
target_identifier,
|
||||||
|
relation_type,
|
||||||
|
relation_identifier
|
||||||
);
|
);
|
||||||
|
|
||||||
main_window_set_status(
|
main_window_set_status(
|
||||||
application->main_window,
|
application->main_window,
|
||||||
status_message != NULL
|
"Relation enregistrée. Actualisation du graphe…"
|
||||||
? status_message
|
);
|
||||||
: "Relation préparée."
|
|
||||||
|
/*
|
||||||
|
* Le graphe n'est rechargé qu'après le COMMIT réussi.
|
||||||
|
* En cas d'échec SQLite, l'affichage courant reste intact.
|
||||||
|
*/
|
||||||
|
application_start_graph_loading(
|
||||||
|
application,
|
||||||
|
database_path
|
||||||
|
);
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
|
||||||
|
if (!relation_created &&
|
||||||
|
application->main_window != NULL)
|
||||||
|
{
|
||||||
|
main_window_set_status(
|
||||||
|
application->main_window,
|
||||||
|
"La relation n'a pas été enregistrée."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
relation_service_free(
|
||||||
|
relation_service
|
||||||
|
);
|
||||||
|
|
||||||
|
relation_record_free(
|
||||||
|
relation_record
|
||||||
|
);
|
||||||
|
|
||||||
|
if (current_date_time != NULL)
|
||||||
|
{
|
||||||
|
g_date_time_unref(
|
||||||
|
current_date_time
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(
|
||||||
|
timestamp
|
||||||
);
|
);
|
||||||
|
|
||||||
g_free(
|
g_free(
|
||||||
status_message
|
relation_identifier
|
||||||
);
|
);
|
||||||
|
|
||||||
create_relation_dialog_result_free(
|
create_relation_dialog_result_free(
|
||||||
|
|
|
||||||
|
|
@ -946,7 +946,7 @@ static gboolean investigation_graph_load_task_worker(
|
||||||
|
|
||||||
if (!investigation_graph_layout_set_position(
|
if (!investigation_graph_layout_set_position(
|
||||||
graph_layout,
|
graph_layout,
|
||||||
graph_node_position_get_entity_identifier(
|
graph_node_position_get_node_identifier(
|
||||||
position
|
position
|
||||||
),
|
),
|
||||||
graph_node_position_get_x(
|
graph_node_position_get_x(
|
||||||
|
|
|
||||||
|
|
@ -27,20 +27,20 @@ struct GraphNodePositionDao
|
||||||
*/
|
*/
|
||||||
static const char *const graph_node_position_dao_list_all_sql =
|
static const char *const graph_node_position_dao_list_all_sql =
|
||||||
"SELECT "
|
"SELECT "
|
||||||
" entity_id,"
|
" node_id,"
|
||||||
" x,"
|
" x,"
|
||||||
" y,"
|
" y,"
|
||||||
" updated_at "
|
" updated_at "
|
||||||
"FROM graph_node_positions "
|
"FROM graph_layout_positions "
|
||||||
"ORDER BY entity_id ASC;";
|
"ORDER BY node_id ASC;";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Requête d'insertion ou de mise à jour d'une position.
|
* @brief Requête d'insertion ou de mise à jour d'une position.
|
||||||
*/
|
*/
|
||||||
static const char *const graph_node_position_dao_upsert_sql =
|
static const char *const graph_node_position_dao_upsert_sql =
|
||||||
"INSERT INTO graph_node_positions"
|
"INSERT INTO graph_layout_positions"
|
||||||
"("
|
"("
|
||||||
" entity_id,"
|
" node_id,"
|
||||||
" x,"
|
" x,"
|
||||||
" y,"
|
" y,"
|
||||||
" updated_at"
|
" updated_at"
|
||||||
|
|
@ -52,7 +52,7 @@ static const char *const graph_node_position_dao_upsert_sql =
|
||||||
" ?,"
|
" ?,"
|
||||||
" ?"
|
" ?"
|
||||||
")"
|
")"
|
||||||
"ON CONFLICT(entity_id)"
|
"ON CONFLICT(node_id)"
|
||||||
"DO UPDATE SET "
|
"DO UPDATE SET "
|
||||||
" x = excluded.x,"
|
" x = excluded.x,"
|
||||||
" y = excluded.y,"
|
" y = excluded.y,"
|
||||||
|
|
@ -62,14 +62,14 @@ static const char *const graph_node_position_dao_upsert_sql =
|
||||||
* @brief Requête de suppression d'une position.
|
* @brief Requête de suppression d'une position.
|
||||||
*/
|
*/
|
||||||
static const char *const graph_node_position_dao_delete_sql =
|
static const char *const graph_node_position_dao_delete_sql =
|
||||||
"DELETE FROM graph_node_positions "
|
"DELETE FROM graph_layout_positions "
|
||||||
"WHERE entity_id = ?;";
|
"WHERE node_id = ?;";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Requête de suppression de toutes les positions.
|
* @brief Requête de suppression de toutes les positions.
|
||||||
*/
|
*/
|
||||||
static const char *const graph_node_position_dao_delete_all_sql =
|
static const char *const graph_node_position_dao_delete_all_sql =
|
||||||
"DELETE FROM graph_node_positions;";
|
"DELETE FROM graph_layout_positions;";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Enregistre une erreur littérale.
|
* @brief Enregistre une erreur littérale.
|
||||||
|
|
@ -202,7 +202,7 @@ graph_node_position_dao_read_current_record(
|
||||||
GraphNodePosition *position =
|
GraphNodePosition *position =
|
||||||
NULL;
|
NULL;
|
||||||
|
|
||||||
char *entity_identifier =
|
char *node_identifier =
|
||||||
NULL;
|
NULL;
|
||||||
|
|
||||||
char *updated_at =
|
char *updated_at =
|
||||||
|
|
@ -233,7 +233,7 @@ graph_node_position_dao_read_current_record(
|
||||||
if (!database_statement_column_text(
|
if (!database_statement_column_text(
|
||||||
statement,
|
statement,
|
||||||
0,
|
0,
|
||||||
&entity_identifier
|
&node_identifier
|
||||||
) ||
|
) ||
|
||||||
!database_statement_column_double(
|
!database_statement_column_double(
|
||||||
statement,
|
statement,
|
||||||
|
|
@ -262,7 +262,7 @@ graph_node_position_dao_read_current_record(
|
||||||
|
|
||||||
position =
|
position =
|
||||||
graph_node_position_new(
|
graph_node_position_new(
|
||||||
entity_identifier,
|
node_identifier,
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
updated_at,
|
updated_at,
|
||||||
|
|
@ -302,7 +302,7 @@ cleanup:
|
||||||
);
|
);
|
||||||
|
|
||||||
g_free(
|
g_free(
|
||||||
entity_identifier
|
node_identifier
|
||||||
);
|
);
|
||||||
|
|
||||||
return position;
|
return position;
|
||||||
|
|
@ -576,7 +576,7 @@ error:
|
||||||
|
|
||||||
gboolean graph_node_position_dao_upsert(
|
gboolean graph_node_position_dao_upsert(
|
||||||
GraphNodePositionDao *position_dao,
|
GraphNodePositionDao *position_dao,
|
||||||
const char *entity_identifier,
|
const char *node_identifier,
|
||||||
double x,
|
double x,
|
||||||
double y,
|
double y,
|
||||||
GError **error
|
GError **error
|
||||||
|
|
@ -598,9 +598,9 @@ gboolean graph_node_position_dao_upsert(
|
||||||
|
|
||||||
if (position_dao == NULL ||
|
if (position_dao == NULL ||
|
||||||
position_dao->database == NULL ||
|
position_dao->database == NULL ||
|
||||||
entity_identifier == NULL ||
|
node_identifier == NULL ||
|
||||||
!g_uuid_string_is_valid(
|
!g_uuid_string_is_valid(
|
||||||
entity_identifier
|
node_identifier
|
||||||
) ||
|
) ||
|
||||||
!isfinite(x) ||
|
!isfinite(x) ||
|
||||||
!isfinite(y))
|
!isfinite(y))
|
||||||
|
|
@ -649,7 +649,7 @@ gboolean graph_node_position_dao_upsert(
|
||||||
if (!database_statement_bind_text(
|
if (!database_statement_bind_text(
|
||||||
statement,
|
statement,
|
||||||
1,
|
1,
|
||||||
entity_identifier
|
node_identifier
|
||||||
) ||
|
) ||
|
||||||
!database_statement_bind_double(
|
!database_statement_bind_double(
|
||||||
statement,
|
statement,
|
||||||
|
|
@ -707,7 +707,7 @@ cleanup:
|
||||||
|
|
||||||
gboolean graph_node_position_dao_delete(
|
gboolean graph_node_position_dao_delete(
|
||||||
GraphNodePositionDao *position_dao,
|
GraphNodePositionDao *position_dao,
|
||||||
const char *entity_identifier,
|
const char *node_identifier,
|
||||||
GError **error
|
GError **error
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
|
@ -724,15 +724,15 @@ gboolean graph_node_position_dao_delete(
|
||||||
|
|
||||||
if (position_dao == NULL ||
|
if (position_dao == NULL ||
|
||||||
position_dao->database == NULL ||
|
position_dao->database == NULL ||
|
||||||
entity_identifier == NULL ||
|
node_identifier == NULL ||
|
||||||
!g_uuid_string_is_valid(
|
!g_uuid_string_is_valid(
|
||||||
entity_identifier
|
node_identifier
|
||||||
))
|
))
|
||||||
{
|
{
|
||||||
graph_node_position_dao_set_error_literal(
|
graph_node_position_dao_set_error_literal(
|
||||||
error,
|
error,
|
||||||
GRAPH_NODE_POSITION_DAO_ERROR_INVALID_ARGUMENT,
|
GRAPH_NODE_POSITION_DAO_ERROR_INVALID_ARGUMENT,
|
||||||
"L'identifiant de l'entité est invalide."
|
"L'identifiant du nœud est invalide."
|
||||||
);
|
);
|
||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
@ -759,14 +759,14 @@ gboolean graph_node_position_dao_delete(
|
||||||
if (!database_statement_bind_text(
|
if (!database_statement_bind_text(
|
||||||
statement,
|
statement,
|
||||||
1,
|
1,
|
||||||
entity_identifier
|
node_identifier
|
||||||
))
|
))
|
||||||
{
|
{
|
||||||
graph_node_position_dao_set_database_error(
|
graph_node_position_dao_set_database_error(
|
||||||
position_dao,
|
position_dao,
|
||||||
error,
|
error,
|
||||||
GRAPH_NODE_POSITION_DAO_ERROR_BIND,
|
GRAPH_NODE_POSITION_DAO_ERROR_BIND,
|
||||||
"Impossible de lier l'identifiant de l'entité"
|
"Impossible de lier l'identifiant du nœud"
|
||||||
);
|
);
|
||||||
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
*/
|
*/
|
||||||
struct GraphNodePosition
|
struct GraphNodePosition
|
||||||
{
|
{
|
||||||
char *entity_identifier;
|
char *node_identifier;
|
||||||
|
|
||||||
double x;
|
double x;
|
||||||
double y;
|
double y;
|
||||||
|
|
@ -154,7 +154,7 @@ GQuark graph_node_position_error_quark(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
GraphNodePosition *graph_node_position_new(
|
GraphNodePosition *graph_node_position_new(
|
||||||
const char *entity_identifier,
|
const char *node_identifier,
|
||||||
double x,
|
double x,
|
||||||
double y,
|
double y,
|
||||||
const char *updated_at,
|
const char *updated_at,
|
||||||
|
|
@ -164,7 +164,7 @@ GraphNodePosition *graph_node_position_new(
|
||||||
GraphNodePosition *position =
|
GraphNodePosition *position =
|
||||||
NULL;
|
NULL;
|
||||||
|
|
||||||
char *entity_identifier_copy =
|
char *node_identifier_copy =
|
||||||
NULL;
|
NULL;
|
||||||
|
|
||||||
char *updated_at_copy =
|
char *updated_at_copy =
|
||||||
|
|
@ -175,22 +175,22 @@ GraphNodePosition *graph_node_position_new(
|
||||||
NULL
|
NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
entity_identifier_copy =
|
node_identifier_copy =
|
||||||
graph_node_position_duplicate_trimmed(
|
graph_node_position_duplicate_trimmed(
|
||||||
entity_identifier
|
node_identifier
|
||||||
);
|
);
|
||||||
|
|
||||||
if (entity_identifier_copy == NULL ||
|
if (node_identifier_copy == NULL ||
|
||||||
entity_identifier_copy[0] == '\0' ||
|
node_identifier_copy[0] == '\0' ||
|
||||||
!g_uuid_string_is_valid(
|
!g_uuid_string_is_valid(
|
||||||
entity_identifier_copy
|
node_identifier_copy
|
||||||
))
|
))
|
||||||
{
|
{
|
||||||
g_set_error_literal(
|
g_set_error_literal(
|
||||||
error,
|
error,
|
||||||
GRAPH_NODE_POSITION_ERROR,
|
GRAPH_NODE_POSITION_ERROR,
|
||||||
GRAPH_NODE_POSITION_ERROR_INVALID_IDENTIFIER,
|
GRAPH_NODE_POSITION_ERROR_INVALID_IDENTIFIER,
|
||||||
"L'identifiant de l'entité n'est pas un UUID valide."
|
"L'identifiant du nœud n'est pas un UUID valide."
|
||||||
);
|
);
|
||||||
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
@ -246,8 +246,8 @@ GraphNodePosition *graph_node_position_new(
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
position->entity_identifier =
|
position->node_identifier =
|
||||||
entity_identifier_copy;
|
node_identifier_copy;
|
||||||
|
|
||||||
position->x =
|
position->x =
|
||||||
x;
|
x;
|
||||||
|
|
@ -258,7 +258,7 @@ GraphNodePosition *graph_node_position_new(
|
||||||
position->updated_at =
|
position->updated_at =
|
||||||
updated_at_copy;
|
updated_at_copy;
|
||||||
|
|
||||||
entity_identifier_copy =
|
node_identifier_copy =
|
||||||
NULL;
|
NULL;
|
||||||
|
|
||||||
updated_at_copy =
|
updated_at_copy =
|
||||||
|
|
@ -271,7 +271,7 @@ cleanup:
|
||||||
);
|
);
|
||||||
|
|
||||||
g_free(
|
g_free(
|
||||||
entity_identifier_copy
|
node_identifier_copy
|
||||||
);
|
);
|
||||||
|
|
||||||
return position;
|
return position;
|
||||||
|
|
@ -291,7 +291,7 @@ void graph_node_position_free(
|
||||||
);
|
);
|
||||||
|
|
||||||
g_free(
|
g_free(
|
||||||
position->entity_identifier
|
position->node_identifier
|
||||||
);
|
);
|
||||||
|
|
||||||
g_free(
|
g_free(
|
||||||
|
|
@ -299,12 +299,12 @@ void graph_node_position_free(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *graph_node_position_get_entity_identifier(
|
const char *graph_node_position_get_node_identifier(
|
||||||
const GraphNodePosition *position
|
const GraphNodePosition *position
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return position != NULL
|
return position != NULL
|
||||||
? position->entity_identifier
|
? position->node_identifier
|
||||||
: NULL;
|
: NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Coordonnées privées associées à une entité.
|
* @brief Coordonnées privées associées à un nœud.
|
||||||
*/
|
*/
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
|
|
@ -22,7 +22,7 @@ typedef struct
|
||||||
*/
|
*/
|
||||||
struct InvestigationGraphLayout
|
struct InvestigationGraphLayout
|
||||||
{
|
{
|
||||||
GHashTable *positions_by_entity_identifier;
|
GHashTable *positions_by_node_identifier;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -48,16 +48,16 @@ static void investigation_graph_layout_set_error_literal(
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Vérifie un identifiant d'entité.
|
* @brief Vérifie un identifiant de nœud.
|
||||||
*/
|
*/
|
||||||
static gboolean investigation_graph_layout_identifier_is_valid(
|
static gboolean investigation_graph_layout_identifier_is_valid(
|
||||||
const char *entity_identifier
|
const char *node_identifier
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return entity_identifier != NULL &&
|
return node_identifier != NULL &&
|
||||||
entity_identifier[0] != '\0' &&
|
node_identifier[0] != '\0' &&
|
||||||
g_uuid_string_is_valid(
|
g_uuid_string_is_valid(
|
||||||
entity_identifier
|
node_identifier
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -84,7 +84,7 @@ InvestigationGraphLayout *investigation_graph_layout_new(void)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
layout->positions_by_entity_identifier =
|
layout->positions_by_node_identifier =
|
||||||
g_hash_table_new_full(
|
g_hash_table_new_full(
|
||||||
g_str_hash,
|
g_str_hash,
|
||||||
g_str_equal,
|
g_str_equal,
|
||||||
|
|
@ -92,7 +92,7 @@ InvestigationGraphLayout *investigation_graph_layout_new(void)
|
||||||
g_free
|
g_free
|
||||||
);
|
);
|
||||||
|
|
||||||
if (layout->positions_by_entity_identifier == NULL)
|
if (layout->positions_by_node_identifier == NULL)
|
||||||
{
|
{
|
||||||
investigation_graph_layout_free(
|
investigation_graph_layout_free(
|
||||||
layout
|
layout
|
||||||
|
|
@ -106,7 +106,7 @@ InvestigationGraphLayout *investigation_graph_layout_new(void)
|
||||||
|
|
||||||
gboolean investigation_graph_layout_set_position(
|
gboolean investigation_graph_layout_set_position(
|
||||||
InvestigationGraphLayout *layout,
|
InvestigationGraphLayout *layout,
|
||||||
const char *entity_identifier,
|
const char *node_identifier,
|
||||||
double x,
|
double x,
|
||||||
double y,
|
double y,
|
||||||
GError **error
|
GError **error
|
||||||
|
|
@ -115,7 +115,7 @@ gboolean investigation_graph_layout_set_position(
|
||||||
InvestigationGraphLayoutPosition *position =
|
InvestigationGraphLayoutPosition *position =
|
||||||
NULL;
|
NULL;
|
||||||
|
|
||||||
char *entity_identifier_copy =
|
char *node_identifier_copy =
|
||||||
NULL;
|
NULL;
|
||||||
|
|
||||||
g_return_val_if_fail(
|
g_return_val_if_fail(
|
||||||
|
|
@ -124,7 +124,7 @@ gboolean investigation_graph_layout_set_position(
|
||||||
);
|
);
|
||||||
|
|
||||||
if (layout == NULL ||
|
if (layout == NULL ||
|
||||||
layout->positions_by_entity_identifier == NULL)
|
layout->positions_by_node_identifier == NULL)
|
||||||
{
|
{
|
||||||
investigation_graph_layout_set_error_literal(
|
investigation_graph_layout_set_error_literal(
|
||||||
error,
|
error,
|
||||||
|
|
@ -136,13 +136,13 @@ gboolean investigation_graph_layout_set_position(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!investigation_graph_layout_identifier_is_valid(
|
if (!investigation_graph_layout_identifier_is_valid(
|
||||||
entity_identifier
|
node_identifier
|
||||||
))
|
))
|
||||||
{
|
{
|
||||||
investigation_graph_layout_set_error_literal(
|
investigation_graph_layout_set_error_literal(
|
||||||
error,
|
error,
|
||||||
INVESTIGATION_GRAPH_LAYOUT_ERROR_INVALID_IDENTIFIER,
|
INVESTIGATION_GRAPH_LAYOUT_ERROR_INVALID_IDENTIFIER,
|
||||||
"L'identifiant de l'entité n'est pas un UUID valide."
|
"L'identifiant du nœud n'est pas un UUID valide."
|
||||||
);
|
);
|
||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
@ -160,9 +160,9 @@ gboolean investigation_graph_layout_set_position(
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
entity_identifier_copy =
|
node_identifier_copy =
|
||||||
g_strdup(
|
g_strdup(
|
||||||
entity_identifier
|
node_identifier
|
||||||
);
|
);
|
||||||
|
|
||||||
position =
|
position =
|
||||||
|
|
@ -171,7 +171,7 @@ gboolean investigation_graph_layout_set_position(
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
|
||||||
if (entity_identifier_copy == NULL ||
|
if (node_identifier_copy == NULL ||
|
||||||
position == NULL)
|
position == NULL)
|
||||||
{
|
{
|
||||||
g_free(
|
g_free(
|
||||||
|
|
@ -179,7 +179,7 @@ gboolean investigation_graph_layout_set_position(
|
||||||
);
|
);
|
||||||
|
|
||||||
g_free(
|
g_free(
|
||||||
entity_identifier_copy
|
node_identifier_copy
|
||||||
);
|
);
|
||||||
|
|
||||||
investigation_graph_layout_set_error_literal(
|
investigation_graph_layout_set_error_literal(
|
||||||
|
|
@ -198,8 +198,8 @@ gboolean investigation_graph_layout_set_position(
|
||||||
y;
|
y;
|
||||||
|
|
||||||
g_hash_table_replace(
|
g_hash_table_replace(
|
||||||
layout->positions_by_entity_identifier,
|
layout->positions_by_node_identifier,
|
||||||
entity_identifier_copy,
|
node_identifier_copy,
|
||||||
position
|
position
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -208,7 +208,7 @@ gboolean investigation_graph_layout_set_position(
|
||||||
|
|
||||||
gboolean investigation_graph_layout_get_position(
|
gboolean investigation_graph_layout_get_position(
|
||||||
const InvestigationGraphLayout *layout,
|
const InvestigationGraphLayout *layout,
|
||||||
const char *entity_identifier,
|
const char *node_identifier,
|
||||||
double *x,
|
double *x,
|
||||||
double *y
|
double *y
|
||||||
)
|
)
|
||||||
|
|
@ -217,9 +217,9 @@ gboolean investigation_graph_layout_get_position(
|
||||||
NULL;
|
NULL;
|
||||||
|
|
||||||
if (layout == NULL ||
|
if (layout == NULL ||
|
||||||
layout->positions_by_entity_identifier == NULL ||
|
layout->positions_by_node_identifier == NULL ||
|
||||||
!investigation_graph_layout_identifier_is_valid(
|
!investigation_graph_layout_identifier_is_valid(
|
||||||
entity_identifier
|
node_identifier
|
||||||
))
|
))
|
||||||
{
|
{
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
@ -227,8 +227,8 @@ gboolean investigation_graph_layout_get_position(
|
||||||
|
|
||||||
position =
|
position =
|
||||||
g_hash_table_lookup(
|
g_hash_table_lookup(
|
||||||
layout->positions_by_entity_identifier,
|
layout->positions_by_node_identifier,
|
||||||
entity_identifier
|
node_identifier
|
||||||
);
|
);
|
||||||
|
|
||||||
if (position == NULL)
|
if (position == NULL)
|
||||||
|
|
@ -253,21 +253,21 @@ gboolean investigation_graph_layout_get_position(
|
||||||
|
|
||||||
gboolean investigation_graph_layout_remove_position(
|
gboolean investigation_graph_layout_remove_position(
|
||||||
InvestigationGraphLayout *layout,
|
InvestigationGraphLayout *layout,
|
||||||
const char *entity_identifier
|
const char *node_identifier
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (layout == NULL ||
|
if (layout == NULL ||
|
||||||
layout->positions_by_entity_identifier == NULL ||
|
layout->positions_by_node_identifier == NULL ||
|
||||||
!investigation_graph_layout_identifier_is_valid(
|
!investigation_graph_layout_identifier_is_valid(
|
||||||
entity_identifier
|
node_identifier
|
||||||
))
|
))
|
||||||
{
|
{
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return g_hash_table_remove(
|
return g_hash_table_remove(
|
||||||
layout->positions_by_entity_identifier,
|
layout->positions_by_node_identifier,
|
||||||
entity_identifier
|
node_identifier
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -276,13 +276,13 @@ void investigation_graph_layout_clear(
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (layout == NULL ||
|
if (layout == NULL ||
|
||||||
layout->positions_by_entity_identifier == NULL)
|
layout->positions_by_node_identifier == NULL)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_hash_table_remove_all(
|
g_hash_table_remove_all(
|
||||||
layout->positions_by_entity_identifier
|
layout->positions_by_node_identifier
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -291,13 +291,13 @@ guint investigation_graph_layout_get_count(
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (layout == NULL ||
|
if (layout == NULL ||
|
||||||
layout->positions_by_entity_identifier == NULL)
|
layout->positions_by_node_identifier == NULL)
|
||||||
{
|
{
|
||||||
return 0U;
|
return 0U;
|
||||||
}
|
}
|
||||||
|
|
||||||
return g_hash_table_size(
|
return g_hash_table_size(
|
||||||
layout->positions_by_entity_identifier
|
layout->positions_by_node_identifier
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -311,7 +311,7 @@ void investigation_graph_layout_free(
|
||||||
}
|
}
|
||||||
|
|
||||||
g_clear_pointer(
|
g_clear_pointer(
|
||||||
&layout->positions_by_entity_identifier,
|
&layout->positions_by_node_identifier,
|
||||||
g_hash_table_unref
|
g_hash_table_unref
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1170,10 +1170,10 @@ gboolean create_relation_dialog_present(
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
g_object_unref(
|
/*
|
||||||
target_labels
|
* Le constructeur prend possession de la référence du modèle.
|
||||||
);
|
* Le dialogue ne doit donc pas appeler g_object_unref() ici.
|
||||||
|
*/
|
||||||
target_labels =
|
target_labels =
|
||||||
NULL;
|
NULL;
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -156,7 +156,7 @@ static void test_graph_node_position_dao_fixture_clear(
|
||||||
*/
|
*/
|
||||||
static void test_graph_node_position_dao_insert_entity(
|
static void test_graph_node_position_dao_insert_entity(
|
||||||
Database *database,
|
Database *database,
|
||||||
const char *entity_identifier,
|
const char *node_identifier,
|
||||||
const char *entity_value
|
const char *entity_value
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
|
@ -168,7 +168,7 @@ static void test_graph_node_position_dao_insert_entity(
|
||||||
);
|
);
|
||||||
|
|
||||||
g_assert_nonnull(
|
g_assert_nonnull(
|
||||||
entity_identifier
|
node_identifier
|
||||||
);
|
);
|
||||||
|
|
||||||
g_assert_nonnull(
|
g_assert_nonnull(
|
||||||
|
|
@ -208,7 +208,7 @@ static void test_graph_node_position_dao_insert_entity(
|
||||||
database_statement_bind_text(
|
database_statement_bind_text(
|
||||||
statement,
|
statement,
|
||||||
1,
|
1,
|
||||||
entity_identifier
|
node_identifier
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -254,7 +254,7 @@ static int64_t test_graph_node_position_dao_count_rows(
|
||||||
database_statement_prepare(
|
database_statement_prepare(
|
||||||
database,
|
database,
|
||||||
"SELECT COUNT(*) "
|
"SELECT COUNT(*) "
|
||||||
"FROM graph_node_positions;"
|
"FROM graph_layout_positions;"
|
||||||
);
|
);
|
||||||
|
|
||||||
g_assert_nonnull(
|
g_assert_nonnull(
|
||||||
|
|
@ -454,19 +454,16 @@ static void test_graph_node_position_dao_upsert_insert(void)
|
||||||
GError *error =
|
GError *error =
|
||||||
NULL;
|
NULL;
|
||||||
|
|
||||||
const char *entity_identifier =
|
const char *node_identifier =
|
||||||
"10000000-0000-4000-8000-000000000001";
|
"10000000-0000-4000-8000-000000000001";
|
||||||
|
|
||||||
test_graph_node_position_dao_insert_entity(
|
/* Un nœud générique peut être une relation et n'a pas à exister dans
|
||||||
fixture.database,
|
* la table entites. */
|
||||||
entity_identifier,
|
|
||||||
"entite-position-1"
|
|
||||||
);
|
|
||||||
|
|
||||||
g_assert_true(
|
g_assert_true(
|
||||||
graph_node_position_dao_upsert(
|
graph_node_position_dao_upsert(
|
||||||
fixture.position_dao,
|
fixture.position_dao,
|
||||||
entity_identifier,
|
node_identifier,
|
||||||
120.5,
|
120.5,
|
||||||
-42.25,
|
-42.25,
|
||||||
&error
|
&error
|
||||||
|
|
@ -516,11 +513,11 @@ static void test_graph_node_position_dao_upsert_insert(void)
|
||||||
);
|
);
|
||||||
|
|
||||||
g_assert_cmpstr(
|
g_assert_cmpstr(
|
||||||
graph_node_position_get_entity_identifier(
|
graph_node_position_get_node_identifier(
|
||||||
position
|
position
|
||||||
),
|
),
|
||||||
==,
|
==,
|
||||||
entity_identifier
|
node_identifier
|
||||||
);
|
);
|
||||||
|
|
||||||
g_assert_cmpfloat(
|
g_assert_cmpfloat(
|
||||||
|
|
@ -578,19 +575,19 @@ static void test_graph_node_position_dao_upsert_update(void)
|
||||||
GError *error =
|
GError *error =
|
||||||
NULL;
|
NULL;
|
||||||
|
|
||||||
const char *entity_identifier =
|
const char *node_identifier =
|
||||||
"20000000-0000-4000-8000-000000000002";
|
"20000000-0000-4000-8000-000000000002";
|
||||||
|
|
||||||
test_graph_node_position_dao_insert_entity(
|
test_graph_node_position_dao_insert_entity(
|
||||||
fixture.database,
|
fixture.database,
|
||||||
entity_identifier,
|
node_identifier,
|
||||||
"entite-position-2"
|
"entite-position-2"
|
||||||
);
|
);
|
||||||
|
|
||||||
g_assert_true(
|
g_assert_true(
|
||||||
graph_node_position_dao_upsert(
|
graph_node_position_dao_upsert(
|
||||||
fixture.position_dao,
|
fixture.position_dao,
|
||||||
entity_identifier,
|
node_identifier,
|
||||||
10.0,
|
10.0,
|
||||||
20.0,
|
20.0,
|
||||||
&error
|
&error
|
||||||
|
|
@ -604,7 +601,7 @@ static void test_graph_node_position_dao_upsert_update(void)
|
||||||
g_assert_true(
|
g_assert_true(
|
||||||
graph_node_position_dao_upsert(
|
graph_node_position_dao_upsert(
|
||||||
fixture.position_dao,
|
fixture.position_dao,
|
||||||
entity_identifier,
|
node_identifier,
|
||||||
-300.75,
|
-300.75,
|
||||||
450.5,
|
450.5,
|
||||||
&error
|
&error
|
||||||
|
|
@ -855,7 +852,7 @@ static void test_graph_node_position_dao_list_order(void)
|
||||||
);
|
);
|
||||||
|
|
||||||
g_assert_cmpstr(
|
g_assert_cmpstr(
|
||||||
graph_node_position_get_entity_identifier(
|
graph_node_position_get_node_identifier(
|
||||||
first_position
|
first_position
|
||||||
),
|
),
|
||||||
==,
|
==,
|
||||||
|
|
@ -863,7 +860,7 @@ static void test_graph_node_position_dao_list_order(void)
|
||||||
);
|
);
|
||||||
|
|
||||||
g_assert_cmpstr(
|
g_assert_cmpstr(
|
||||||
graph_node_position_get_entity_identifier(
|
graph_node_position_get_node_identifier(
|
||||||
second_position
|
second_position
|
||||||
),
|
),
|
||||||
==,
|
==,
|
||||||
|
|
@ -887,19 +884,19 @@ static void test_graph_node_position_dao_delete_existing(void)
|
||||||
GError *error =
|
GError *error =
|
||||||
NULL;
|
NULL;
|
||||||
|
|
||||||
const char *entity_identifier =
|
const char *node_identifier =
|
||||||
"60000000-0000-4000-8000-000000000006";
|
"60000000-0000-4000-8000-000000000006";
|
||||||
|
|
||||||
test_graph_node_position_dao_insert_entity(
|
test_graph_node_position_dao_insert_entity(
|
||||||
fixture.database,
|
fixture.database,
|
||||||
entity_identifier,
|
node_identifier,
|
||||||
"entite-position-6"
|
"entite-position-6"
|
||||||
);
|
);
|
||||||
|
|
||||||
g_assert_true(
|
g_assert_true(
|
||||||
graph_node_position_dao_upsert(
|
graph_node_position_dao_upsert(
|
||||||
fixture.position_dao,
|
fixture.position_dao,
|
||||||
entity_identifier,
|
node_identifier,
|
||||||
60.0,
|
60.0,
|
||||||
70.0,
|
70.0,
|
||||||
&error
|
&error
|
||||||
|
|
@ -913,7 +910,7 @@ static void test_graph_node_position_dao_delete_existing(void)
|
||||||
g_assert_true(
|
g_assert_true(
|
||||||
graph_node_position_dao_delete(
|
graph_node_position_dao_delete(
|
||||||
fixture.position_dao,
|
fixture.position_dao,
|
||||||
entity_identifier,
|
node_identifier,
|
||||||
&error
|
&error
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue