labfy-investigation/src/widgets/evidence_category_model.c
2026-07-20 11:57:56 +02:00

548 lines
12 KiB
C

/******************************************************************************
* @file evidence_category_model.c
* @brief Regroupement des preuves par catégorie pour l'affichage GTK.
******************************************************************************/
#include "widgets/evidence_category_model.h"
#include "widgets/evidence_category_item.h"
#include "widgets/evidence_list_item.h"
/**
* @brief État interne du modèle.
*/
struct EvidenceCategoryModel
{
GListStore *categories;
};
GQuark evidence_category_model_error_quark(void)
{
return g_quark_from_static_string(
"labfy-investigation-evidence-category-model-error"
);
}
/**
* @brief Compare deux catégories par libellé puis par code métier.
*/
static gint evidence_category_model_compare_categories(
gconstpointer first_pointer,
gconstpointer second_pointer,
gpointer user_data
)
{
const EvidenceCategoryItem *first_category =
first_pointer;
const EvidenceCategoryItem *second_category =
second_pointer;
const char *first_label = NULL;
const char *second_label = NULL;
gint comparison = 0;
(void) user_data;
first_label =
evidence_category_item_get_label(
first_category
);
second_label =
evidence_category_item_get_label(
second_category
);
comparison =
g_utf8_collate(
first_label,
second_label
);
if (comparison != 0)
{
return comparison;
}
return g_strcmp0(
evidence_category_item_get_type_identifier(
first_category
),
evidence_category_item_get_type_identifier(
second_category
)
);
}
/**
* @brief Vérifie qu'une chaîne obligatoire contient du texte.
*/
static gboolean evidence_category_model_value_has_text(
const char *value
)
{
return value != NULL &&
value[0] != '\0';
}
/**
* @brief Libère les références temporaires d'un tableau d'ajouts.
*/
static void evidence_category_model_release_additions(
gpointer *additions,
guint addition_count
)
{
guint addition_index = 0;
if (additions == NULL)
{
return;
}
for (addition_index = 0;
addition_index < addition_count;
addition_index++)
{
g_clear_object(
&additions[addition_index]
);
}
g_free(
additions
);
}
EvidenceCategoryModel *evidence_category_model_new(void)
{
EvidenceCategoryModel *evidence_category_model =
NULL;
evidence_category_model =
g_try_new0(
EvidenceCategoryModel,
1
);
if (evidence_category_model == NULL)
{
return NULL;
}
evidence_category_model->categories =
g_list_store_new(
EVIDENCE_TYPE_CATEGORY_ITEM
);
if (evidence_category_model->categories == NULL)
{
g_free(
evidence_category_model
);
return NULL;
}
return evidence_category_model;
}
void evidence_category_model_free(
EvidenceCategoryModel *evidence_category_model
)
{
if (evidence_category_model == NULL)
{
return;
}
g_clear_object(
&evidence_category_model->categories
);
g_free(
evidence_category_model
);
}
GListModel *evidence_category_model_get_list_model(
EvidenceCategoryModel *evidence_category_model
)
{
if (evidence_category_model == NULL ||
evidence_category_model->categories == NULL)
{
return NULL;
}
return G_LIST_MODEL(
evidence_category_model->categories
);
}
guint evidence_category_model_get_count(
const EvidenceCategoryModel *evidence_category_model
)
{
if (evidence_category_model == NULL ||
evidence_category_model->categories == NULL)
{
return 0;
}
return g_list_model_get_n_items(
G_LIST_MODEL(
evidence_category_model->categories
)
);
}
gboolean evidence_category_model_replace(
EvidenceCategoryModel *evidence_category_model,
GListModel *evidence_items,
GHashTable *type_labels,
GError **error
)
{
GListStore *new_categories = NULL;
GHashTable *categories_by_type =
NULL;
EvidenceListItem *evidence_list_item =
NULL;
EvidenceCategoryItem *evidence_category_item =
NULL;
gpointer *additions = NULL;
const char *type_identifier = NULL;
const char *category_label = NULL;
guint evidence_count = 0;
guint evidence_index = 0;
guint new_category_count = 0;
guint category_index = 0;
guint previous_category_count = 0;
gboolean replaced = FALSE;
g_return_val_if_fail(
error == NULL || *error == NULL,
FALSE
);
if (evidence_category_model == NULL ||
evidence_category_model->categories == NULL ||
evidence_items == NULL)
{
g_set_error_literal(
error,
EVIDENCE_CATEGORY_MODEL_ERROR,
EVIDENCE_CATEGORY_MODEL_ERROR_INVALID_ARGUMENT,
"Le modèle de catégories ou la liste de preuves est invalide."
);
return FALSE;
}
if (!g_type_is_a(
g_list_model_get_item_type(
evidence_items
),
EVIDENCE_TYPE_LIST_ITEM
))
{
g_set_error_literal(
error,
EVIDENCE_CATEGORY_MODEL_ERROR,
EVIDENCE_CATEGORY_MODEL_ERROR_INVALID_ARGUMENT,
"Le modèle fourni ne contient pas des EvidenceListItem."
);
return FALSE;
}
new_categories =
g_list_store_new(
EVIDENCE_TYPE_CATEGORY_ITEM
);
categories_by_type =
g_hash_table_new_full(
g_str_hash,
g_str_equal,
g_free,
NULL
);
if (new_categories == NULL ||
categories_by_type == NULL)
{
g_set_error_literal(
error,
EVIDENCE_CATEGORY_MODEL_ERROR,
EVIDENCE_CATEGORY_MODEL_ERROR_MEMORY,
"Impossible de préparer le regroupement des preuves."
);
goto cleanup;
}
evidence_count =
g_list_model_get_n_items(
evidence_items
);
for (evidence_index = 0;
evidence_index < evidence_count;
evidence_index++)
{
evidence_list_item =
g_list_model_get_item(
evidence_items,
evidence_index
);
if (evidence_list_item == NULL ||
!EVIDENCE_IS_LIST_ITEM(
evidence_list_item
))
{
g_set_error(
error,
EVIDENCE_CATEGORY_MODEL_ERROR,
EVIDENCE_CATEGORY_MODEL_ERROR_INVALID_ITEM,
"La preuve située à l'index %u est invalide.",
evidence_index
);
goto cleanup;
}
type_identifier =
evidence_list_item_get_type_identifier(
evidence_list_item
);
if (!evidence_category_model_value_has_text(
type_identifier
))
{
g_set_error(
error,
EVIDENCE_CATEGORY_MODEL_ERROR,
EVIDENCE_CATEGORY_MODEL_ERROR_INVALID_ITEM,
"La preuve située à l'index %u ne possède aucun type.",
evidence_index
);
goto cleanup;
}
evidence_category_item =
g_hash_table_lookup(
categories_by_type,
type_identifier
);
if (evidence_category_item == NULL)
{
category_label = NULL;
if (type_labels != NULL)
{
category_label =
g_hash_table_lookup(
type_labels,
type_identifier
);
}
/*
* Un type inconnu reste affichable.
* Son code métier devient son libellé de secours.
*/
if (!evidence_category_model_value_has_text(
category_label
))
{
category_label =
type_identifier;
}
evidence_category_item =
evidence_category_item_new(
type_identifier,
category_label
);
if (evidence_category_item == NULL)
{
g_set_error(
error,
EVIDENCE_CATEGORY_MODEL_ERROR,
EVIDENCE_CATEGORY_MODEL_ERROR_CATEGORY,
"Impossible de créer la catégorie '%s'.",
type_identifier
);
goto cleanup;
}
g_list_store_insert_sorted(
new_categories,
G_OBJECT(
evidence_category_item
),
evidence_category_model_compare_categories,
NULL
);
/*
* La table emprunte le pointeur.
* Le GListStore possède la référence.
*/
g_hash_table_insert(
categories_by_type,
g_strdup(
type_identifier
),
evidence_category_item
);
g_object_unref(
evidence_category_item
);
}
if (!evidence_category_item_append(
evidence_category_item,
evidence_list_item
))
{
g_set_error(
error,
EVIDENCE_CATEGORY_MODEL_ERROR,
EVIDENCE_CATEGORY_MODEL_ERROR_CATEGORY,
"Impossible d'ajouter la preuve %u à sa catégorie.",
evidence_index
);
goto cleanup;
}
g_clear_object(
&evidence_list_item
);
evidence_category_item =
NULL;
}
new_category_count =
g_list_model_get_n_items(
G_LIST_MODEL(
new_categories
)
);
previous_category_count =
g_list_model_get_n_items(
G_LIST_MODEL(
evidence_category_model->categories
)
);
if (new_category_count == 0)
{
g_list_store_remove_all(
evidence_category_model->categories
);
replaced = TRUE;
goto cleanup;
}
additions =
g_try_new0(
gpointer,
new_category_count
);
if (additions == NULL)
{
g_set_error_literal(
error,
EVIDENCE_CATEGORY_MODEL_ERROR,
EVIDENCE_CATEGORY_MODEL_ERROR_MEMORY,
"Impossible de préparer le remplacement des catégories."
);
goto cleanup;
}
for (category_index = 0;
category_index < new_category_count;
category_index++)
{
additions[category_index] =
g_list_model_get_item(
G_LIST_MODEL(
new_categories
),
category_index
);
if (additions[category_index] == NULL)
{
g_set_error_literal(
error,
EVIDENCE_CATEGORY_MODEL_ERROR,
EVIDENCE_CATEGORY_MODEL_ERROR_CATEGORY,
"Impossible de lire une catégorie préparée."
);
goto cleanup;
}
}
g_list_store_splice(
evidence_category_model->categories,
0,
previous_category_count,
additions,
new_category_count
);
replaced = TRUE;
cleanup:
g_clear_object(
&evidence_list_item
);
evidence_category_model_release_additions(
additions,
new_category_count
);
if (categories_by_type != NULL)
{
g_hash_table_unref(
categories_by_type
);
}
g_clear_object(
&new_categories
);
return replaced;
}