feat(gui): add investigation folder selector
This commit is contained in:
parent
df5d9c8b6c
commit
aa2b903487
5 changed files with 236 additions and 0 deletions
38
docs/tickets/closed/TICKET-002.md
Normal file
38
docs/tickets/closed/TICKET-002.md
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
# Ticket #002
|
||||||
|
|
||||||
|
## Titre
|
||||||
|
|
||||||
|
Ajouter le sélecteur de dossier d’enquête.
|
||||||
|
|
||||||
|
## Objectif
|
||||||
|
|
||||||
|
Permettre à l’utilisateur de sélectionner un dossier d’enquête au lancement de l’application.
|
||||||
|
|
||||||
|
## Responsabilités
|
||||||
|
|
||||||
|
- afficher un dialogue GTK de sélection de dossier ;
|
||||||
|
- retourner le dossier sélectionné ;
|
||||||
|
- gérer l’annulation sans erreur ;
|
||||||
|
- transmettre le chemin au module Application.
|
||||||
|
|
||||||
|
## Hors périmètre
|
||||||
|
|
||||||
|
- création de `00_BaseDeDonnees` ;
|
||||||
|
- ouverture de SQLite ;
|
||||||
|
- initialisation du schéma ;
|
||||||
|
- validation complète d’une enquête.
|
||||||
|
|
||||||
|
## Critères d’acceptation
|
||||||
|
|
||||||
|
- [ ] Le dialogue s’ouvre au lancement.
|
||||||
|
- [ ] Un dossier peut être sélectionné.
|
||||||
|
- [ ] L’annulation est gérée proprement.
|
||||||
|
- [ ] Le chemin sélectionné est affiché dans la fenêtre.
|
||||||
|
- [ ] Aucun warning.
|
||||||
|
- [ ] Les fonctions publiques sont documentées.
|
||||||
|
- [ ] Aucun état global.
|
||||||
|
|
||||||
|
## Commit attendu
|
||||||
|
|
||||||
|
```text
|
||||||
|
feat(gui): add investigation folder selector
|
||||||
46
labfy-investigation/include/views/folder_dialog.h
Normal file
46
labfy-investigation/include/views/folder_dialog.h
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
/******************************************************************************
|
||||||
|
* @file folder_dialog.h
|
||||||
|
* @brief Interface du dialogue de sélection d'un dossier d'enquête.
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#ifndef LABFY_INVESTIGATION_FOLDER_DIALOG_H
|
||||||
|
#define LABFY_INVESTIGATION_FOLDER_DIALOG_H
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Fonction appelée après la fermeture du dialogue de sélection.
|
||||||
|
*
|
||||||
|
* @param folder_path Chemin du dossier sélectionné, ou NULL si l'utilisateur
|
||||||
|
* a annulé la sélection.
|
||||||
|
* @param user_data Données transmises lors de l'ouverture du dialogue.
|
||||||
|
*
|
||||||
|
* Le chemin reçu reste valide uniquement pendant l'appel de cette fonction.
|
||||||
|
* Il doit être copié si le code appelant souhaite le conserver.
|
||||||
|
*
|
||||||
|
* @param callback
|
||||||
|
* Fonction appelée lorsque le dialogue est fermé.
|
||||||
|
*
|
||||||
|
* Si l'utilisateur annule la sélection,
|
||||||
|
* folder_path vaut NULL.
|
||||||
|
*/
|
||||||
|
typedef void (*FolderDialogCallback)(
|
||||||
|
const char *folder_path,
|
||||||
|
gpointer user_data
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Ouvre un dialogue permettant de sélectionner un dossier.
|
||||||
|
*
|
||||||
|
* @param parent Fenêtre parente du dialogue.
|
||||||
|
* Peut être NULL.
|
||||||
|
* @param callback Fonction appelée lorsque le dialogue est terminé.
|
||||||
|
* @param user_data Données transmises à la fonction de rappel.
|
||||||
|
*/
|
||||||
|
void folder_dialog_select_folder(
|
||||||
|
GtkWindow *parent,
|
||||||
|
FolderDialogCallback callback,
|
||||||
|
gpointer user_data
|
||||||
|
);
|
||||||
|
|
||||||
|
#endif
|
||||||
Binary file not shown.
|
|
@ -4,6 +4,7 @@
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
#include "core/application.h"
|
#include "core/application.h"
|
||||||
|
#include "views/folder_dialog.h"
|
||||||
|
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
|
@ -39,6 +40,27 @@ struct Application
|
||||||
* @param gtk_application Application GTK ayant reçu le signal.
|
* @param gtk_application Application GTK ayant reçu le signal.
|
||||||
* @param user_data Données utilisateur associées au signal.
|
* @param user_data Données utilisateur associées au signal.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
static void application_on_folder_selected(
|
||||||
|
const char *folder_path,
|
||||||
|
gpointer user_data
|
||||||
|
)
|
||||||
|
{
|
||||||
|
GtkWindow *window = GTK_WINDOW(user_data);
|
||||||
|
|
||||||
|
if (folder_path == NULL)
|
||||||
|
{
|
||||||
|
gtk_window_set_title(
|
||||||
|
window,
|
||||||
|
"Labfy Investigation — aucune enquête sélectionnée"
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gtk_window_set_title(window, folder_path);
|
||||||
|
}
|
||||||
|
|
||||||
static void application_on_activate(GtkApplication *gtk_application,
|
static void application_on_activate(GtkApplication *gtk_application,
|
||||||
gpointer user_data)
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
|
|
@ -56,6 +78,12 @@ static void application_on_activate(GtkApplication *gtk_application,
|
||||||
);
|
);
|
||||||
|
|
||||||
gtk_window_present(GTK_WINDOW(window));
|
gtk_window_present(GTK_WINDOW(window));
|
||||||
|
|
||||||
|
folder_dialog_select_folder(
|
||||||
|
GTK_WINDOW(window),
|
||||||
|
application_on_folder_selected,
|
||||||
|
window
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Application *application_new(void)
|
Application *application_new(void)
|
||||||
|
|
@ -113,3 +141,5 @@ void application_free(Application *application)
|
||||||
|
|
||||||
g_free(application);
|
g_free(application);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
122
labfy-investigation/src/views/folder_dialog.c
Normal file
122
labfy-investigation/src/views/folder_dialog.c
Normal file
|
|
@ -0,0 +1,122 @@
|
||||||
|
/******************************************************************************
|
||||||
|
* @file folder_dialog.c
|
||||||
|
* @brief Implémentation du dialogue de sélection d'un dossier d'enquête.
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include "views/folder_dialog.h"
|
||||||
|
|
||||||
|
#include <gio/gio.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @struct FolderDialogContext
|
||||||
|
* @brief Conserve les données nécessaires pendant l'opération asynchrone.
|
||||||
|
*
|
||||||
|
* Le dialogue GTK ne se termine pas immédiatement. Ce contexte permet donc
|
||||||
|
* de conserver le callback et ses données jusqu'à la fermeture du dialogue.
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
FolderDialogCallback callback;
|
||||||
|
gpointer user_data;
|
||||||
|
} FolderDialogContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Libère le contexte associé au dialogue.
|
||||||
|
*
|
||||||
|
* @param context Contexte à libérer.
|
||||||
|
*/
|
||||||
|
static void folder_dialog_context_free(FolderDialogContext *context)
|
||||||
|
{
|
||||||
|
g_free(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Traite le résultat de la sélection du dossier.
|
||||||
|
*
|
||||||
|
* @param source_object Objet ayant lancé l'opération asynchrone.
|
||||||
|
* @param result Résultat transmis par GTK.
|
||||||
|
* @param user_data Contexte du dialogue.
|
||||||
|
*/
|
||||||
|
static void folder_dialog_on_folder_selected(
|
||||||
|
GObject *source_object,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gpointer user_data
|
||||||
|
)
|
||||||
|
{
|
||||||
|
GtkFileDialog *dialog = GTK_FILE_DIALOG(source_object);
|
||||||
|
FolderDialogContext *context = user_data;
|
||||||
|
GError *error = NULL;
|
||||||
|
GFile *folder = NULL;
|
||||||
|
char *folder_path = NULL;
|
||||||
|
|
||||||
|
folder = gtk_file_dialog_select_folder_finish(dialog, result, &error);
|
||||||
|
|
||||||
|
if (folder == NULL)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Une annulation est un comportement normal de l'utilisateur.
|
||||||
|
* Elle est transmise au code appelant sous la forme d'un chemin NULL.
|
||||||
|
*/
|
||||||
|
if (error != NULL &&
|
||||||
|
!g_error_matches(error, GTK_DIALOG_ERROR, GTK_DIALOG_ERROR_DISMISSED))
|
||||||
|
{
|
||||||
|
g_warning(
|
||||||
|
"Impossible de sélectionner le dossier : %s",
|
||||||
|
error->message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context->callback != NULL)
|
||||||
|
{
|
||||||
|
context->callback(NULL, context->user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_clear_error(&error);
|
||||||
|
folder_dialog_context_free(context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
folder_path = g_file_get_path(folder);
|
||||||
|
|
||||||
|
if (context->callback != NULL)
|
||||||
|
{
|
||||||
|
context->callback(folder_path, context->user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(folder_path);
|
||||||
|
g_object_unref(folder);
|
||||||
|
folder_dialog_context_free(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
void folder_dialog_select_folder(
|
||||||
|
GtkWindow *parent,
|
||||||
|
FolderDialogCallback callback,
|
||||||
|
gpointer user_data
|
||||||
|
)
|
||||||
|
{
|
||||||
|
GtkFileDialog *dialog = NULL;
|
||||||
|
FolderDialogContext *context = NULL;
|
||||||
|
|
||||||
|
context = g_new0(FolderDialogContext, 1);
|
||||||
|
context->callback = callback;
|
||||||
|
context->user_data = user_data;
|
||||||
|
|
||||||
|
dialog = gtk_file_dialog_new();
|
||||||
|
|
||||||
|
gtk_file_dialog_set_title(
|
||||||
|
dialog,
|
||||||
|
"Sélectionner un dossier d'enquête"
|
||||||
|
);
|
||||||
|
|
||||||
|
gtk_file_dialog_set_modal(dialog, TRUE);
|
||||||
|
|
||||||
|
gtk_file_dialog_select_folder(
|
||||||
|
dialog,
|
||||||
|
parent,
|
||||||
|
NULL,
|
||||||
|
folder_dialog_on_folder_selected,
|
||||||
|
context
|
||||||
|
);
|
||||||
|
|
||||||
|
g_object_unref(dialog);
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue