From a8e66c40be86adceef6bd192cc932606cec2b759 Mon Sep 17 00:00:00 2001 From: grayTerminal-sh Date: Sun, 12 Jul 2026 20:15:49 +0200 Subject: [PATCH] feat(view): create main window --- CHANGELOG.md | 6 + docs/tickets/closed/TICKET-003.md | 57 ++++++ docs/tickets/closed/TICKET-004.md | 128 ++++++++++++++ .../include/views/main_window.h | 50 ++++++ labfy-investigation/labfy-investigation | Bin 30712 -> 0 bytes labfy-investigation/src/core/application.c | 93 +++++++--- labfy-investigation/src/views/main_window.c | 162 ++++++++++++++++++ 7 files changed, 468 insertions(+), 28 deletions(-) create mode 100644 docs/tickets/closed/TICKET-003.md create mode 100644 docs/tickets/closed/TICKET-004.md create mode 100644 labfy-investigation/include/views/main_window.h delete mode 100755 labfy-investigation/labfy-investigation create mode 100644 labfy-investigation/src/views/main_window.c diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f72fca..7356212 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,3 +6,9 @@ - Création du module `Application`. - Intégration de GTK4. - Affichage de la première fenêtre. + +### Added + +- Module `MainWindow`. +- Séparation de la fenêtre principale du module `Application`. +- Première architecture de la fenêtre principale. diff --git a/docs/tickets/closed/TICKET-003.md b/docs/tickets/closed/TICKET-003.md new file mode 100644 index 0000000..413bf29 --- /dev/null +++ b/docs/tickets/closed/TICKET-003.md @@ -0,0 +1,57 @@ +# Ticket #003 + +## Titre + +Créer le module `Investigation`. + +## Objectif + +Créer l'objet métier représentant une enquête ouverte par Labfy Investigation. + +Le module doit rester indépendant de GTK et de l'interface graphique. + +## Responsabilités + +Le module doit : + +- mémoriser le chemin racine de l'enquête ; +- construire le chemin de la base de données modèle ; +- exposer ces chemins en lecture seule ; +- gérer proprement sa mémoire ; +- vérifier qu'un chemin d'enquête est exploitable. + +## Hors périmètre + +Ce ticket ne doit pas : + +- ouvrir SQLite ; +- créer la base de données ; +- modifier l'arborescence de l'enquête ; +- afficher de message GTK ; +- gérer les preuves ou les entités. + +## Dépendances + +- libc ; +- GLib uniquement si nécessaire pour la gestion des chaînes et chemins. + +Aucune dépendance GTK ou SQLite. + +## API attendue + +Le module doit permettre un usage proche de : + +```c +Investigation *investigation = NULL; + +investigation = investigation_new("/chemin/vers/enquete"); + +if (investigation == NULL) +{ + /* Gestion de l'erreur */ +} + +const char *root_path = investigation_get_root_path(investigation); +const char *database_path = investigation_get_database_path(investigation); + +investigation_free(investigation); diff --git a/docs/tickets/closed/TICKET-004.md b/docs/tickets/closed/TICKET-004.md new file mode 100644 index 0000000..c390f8d --- /dev/null +++ b/docs/tickets/closed/TICKET-004.md @@ -0,0 +1,128 @@ +# Ticket #004 + +## Titre + +Créer la fenêtre principale (`MainWindow`). + +--- + +## Objectif + +Créer la fenêtre principale de Labfy Investigation. + +Cette fenêtre deviendra le point central de l'interface utilisateur. + +À ce stade, elle ne contient qu'une structure vide destinée à accueillir les futurs composants. + +--- + +## Responsabilités + +Le module `MainWindow` doit : + +- créer la fenêtre principale ; +- définir son titre ; +- définir sa taille par défaut ; +- afficher une zone centrale vide ; +- afficher une barre d'état. + +--- + +## Hors périmètre + +Ce ticket ne doit pas : + +- afficher l'arborescence des fichiers ; +- ouvrir une enquête ; +- afficher les preuves ; +- communiquer avec SQLite ; +- gérer les menus. + +--- + +## Architecture + +Le module appartient à la couche **Views**. + +Il est responsable uniquement de l'interface graphique. + +Aucune logique métier ne doit être présente dans ce module. + +--- + +## Dépendances + +- GTK4 + +Aucune dépendance vers SQLite. + +--- + +## Fichiers concernés + +```text +include/views/main_window.h +src/views/main_window.c +``` + +--- + +## Interface attendue + +```c +MainWindow *main_window_new(GtkApplication *application); + +GtkWindow *main_window_get_window( + const MainWindow *main_window +); + +void main_window_present(MainWindow *main_window); + +void main_window_free(MainWindow *main_window); +``` + +--- + +## Interface graphique attendue + +```text +┌───────────────────────────────────────────────────────────────┐ +│ Labfy Investigation │ +├───────────────────────────────────────────────────────────────┤ +│ │ +│ │ +│ Zone de travail │ +│ │ +│ │ +├───────────────────────────────────────────────────────────────┤ +│ Aucune enquête ouverte │ +└───────────────────────────────────────────────────────────────┘ +``` + +--- + +## Critères d'acceptation + +- [ ] Le projet compile sans warning. +- [ ] Une fenêtre s'ouvre correctement. +- [ ] La fenêtre est créée par le module `MainWindow`. +- [ ] `Application` n'appelle plus directement `gtk_application_window_new()`. +- [ ] Aucun état global. +- [ ] Documentation Doxygen. +- [ ] Gestion mémoire correcte. + +--- + +## Tests + +- Lancement de l'application. +- Fermeture de la fenêtre. +- Vérification de l'absence de fuite mémoire. + +--- + +## Commit attendu + +```text +feat(view): create main window +``` diff --git a/labfy-investigation/include/views/main_window.h b/labfy-investigation/include/views/main_window.h new file mode 100644 index 0000000..e67c781 --- /dev/null +++ b/labfy-investigation/include/views/main_window.h @@ -0,0 +1,50 @@ +/****************************************************************************** + * @file main_window.h + * @brief Interface publique de la fenêtre principale. + ******************************************************************************/ + +#ifndef LABFY_INVESTIGATION_MAIN_WINDOW_H +#define LABFY_INVESTIGATION_MAIN_WINDOW_H + +#include + +/** + * @brief Représentation opaque de la fenêtre principale. + */ +typedef struct MainWindow MainWindow; + +/** + * @brief Crée une nouvelle fenêtre principale. + * + * @param application Application GTK. + * + * @return Une nouvelle fenêtre ou NULL en cas d'échec. + */ +MainWindow *main_window_new(GtkApplication *application); + +/** + * @brief Affiche la fenêtre principale. + * + * @param main_window Fenêtre à afficher. + */ +void main_window_present(MainWindow *main_window); + +/** + * @brief Retourne la fenêtre GTK associée. + * + * @param main_window Fenêtre principale. + * + * @return Fenêtre GTK. + */ +GtkWindow *main_window_get_window( + const MainWindow *main_window +); + +/** + * @brief Libère les ressources de la fenêtre. + * + * @param main_window Fenêtre à détruire. + */ +void main_window_free(MainWindow *main_window); + +#endif diff --git a/labfy-investigation/labfy-investigation b/labfy-investigation/labfy-investigation deleted file mode 100755 index ad6da347005c606b1198d2ec3e68cac632a3f5b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30712 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 @@ -30,6 +31,8 @@ struct Application { GtkApplication *gtk_application; + MainWindow *main_window; + Investigation *investigation; }; /** @@ -47,57 +50,83 @@ static void application_on_folder_selected( gpointer user_data ) { - GtkWindow *window = GTK_WINDOW(user_data); - Investigation *investigation = NULL; + Application *application = user_data; const char *root_path = NULL; const char *database_path = NULL; + /* + * Une éventuelle enquête précédemment ouverte doit être libérée avant + * d'en créer une nouvelle. + */ + investigation_free(application->investigation); + application->investigation = NULL; + + application->investigation = investigation_new(folder_path); + + if (application == NULL) + { + return; + } + if (folder_path == NULL) { g_print("Sélection annulée.\n"); return; } - investigation = investigation_new(folder_path); - - if (investigation == NULL) + if (application->investigation == NULL) { - g_warning("Impossible de créer l'enquête à partir du dossier sélectionné."); + g_warning( + "Impossible de créer l'enquête à partir du dossier sélectionné." + ); return; } - - root_path = investigation_get_root_path(investigation); - database_path = investigation_get_database_path(investigation); + root_path = investigation_get_root_path( + application->investigation + ); + database_path = investigation_get_database_path( + application->investigation + ); g_print("Dossier racine : %s\n", root_path); g_print("Base de données : %s\n", database_path); - - investigation_free(investigation); - - gtk_window_set_title(window, "Labfy Investigation"); } -static void application_on_activate(GtkApplication *gtk_application, - gpointer user_data) +static void application_on_activate( + GtkApplication *gtk_application, + gpointer user_data +) { - GtkWidget *window = NULL; + Application *application = user_data; - (void)user_data; + if (application == NULL) + { + return; + } - window = gtk_application_window_new(gtk_application); + /* + * Le signal "activate" peut être reçu plusieurs fois. Si la fenêtre + * existe déjà, il suffit de la présenter au lieu d'en créer une seconde. + */ + if (application->main_window != NULL) + { + main_window_present(application->main_window); + return; + } - gtk_window_set_title(GTK_WINDOW(window), "Labfy Investigation"); - gtk_window_set_default_size( - GTK_WINDOW(window), - DEFAULT_WINDOW_WIDTH, - DEFAULT_WINDOW_HEIGHT - ); + application->main_window = main_window_new(gtk_application); - gtk_window_present(GTK_WINDOW(window)); + if (application->main_window == NULL) + { + g_warning("Impossible de créer la fenêtre principale."); + return; + } + + main_window_present(application->main_window); folder_dialog_select_folder( - GTK_WINDOW(window), + main_window_get_window(application->main_window), application_on_folder_selected, - window + application ); } @@ -128,7 +157,11 @@ Application *application_new(void) return application; } -int application_run(Application *application, int argc, char **argv) +int application_run( + Application *application, + int argc, + char **argv +) { if (application == NULL || application->gtk_application == NULL) { @@ -149,6 +182,10 @@ void application_free(Application *application) return; } + investigation_free(application->investigation); + + main_window_free(application->main_window); + if (application->gtk_application != NULL) { g_object_unref(application->gtk_application); diff --git a/labfy-investigation/src/views/main_window.c b/labfy-investigation/src/views/main_window.c new file mode 100644 index 0000000..5f3edaa --- /dev/null +++ b/labfy-investigation/src/views/main_window.c @@ -0,0 +1,162 @@ +/****************************************************************************** + * @file main_window.c + * @brief Implémentation de la fenêtre principale. + ******************************************************************************/ + +#include "views/main_window.h" + +#include + +/** + * @brief Largeur initiale de la fenêtre principale. + */ +#define MAIN_WINDOW_DEFAULT_WIDTH 1000 + +/** + * @brief Hauteur initiale de la fenêtre principale. + */ +#define MAIN_WINDOW_DEFAULT_HEIGHT 650 + +/** + * @struct MainWindow + * @brief Représentation interne de la fenêtre principale. + * + * Cette structure reste privée au module. Les autres fichiers utilisent + * uniquement le type opaque déclaré dans main_window.h. + */ +struct MainWindow +{ + GtkWindow *window; + GtkWidget *status_label; +}; + +MainWindow *main_window_new(GtkApplication *application) +{ + MainWindow *main_window = NULL; + GtkWidget *main_box = NULL; + GtkWidget *workspace = NULL; + + if (application == 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, + "Labfy Investigation" + ); + + gtk_window_set_default_size( + main_window->window, + MAIN_WINDOW_DEFAULT_WIDTH, + MAIN_WINDOW_DEFAULT_HEIGHT + ); + + main_box = gtk_box_new( + GTK_ORIENTATION_VERTICAL, + 0 + ); + + workspace = gtk_box_new( + GTK_ORIENTATION_VERTICAL, + 0 + ); + + gtk_widget_set_hexpand(workspace, TRUE); + gtk_widget_set_vexpand(workspace, TRUE); + + main_window->status_label = gtk_label_new( + "Aucune enquête ouverte" + ); + + 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 + ); + + gtk_box_append( + GTK_BOX(main_box), + workspace + ); + + gtk_box_append( + GTK_BOX(main_box), + main_window->status_label + ); + + gtk_window_set_child( + main_window->window, + main_box + ); + + 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_free(MainWindow *main_window) +{ + if (main_window == NULL) + { + return; + } + + /* + * La fenêtre principale est possédée et détruite par GTK. + * MainWindow ne possède que sa structure d'encapsulation. + */ + + g_free(main_window); +}