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 ad6da34..0000000 Binary files a/labfy-investigation/labfy-investigation and /dev/null differ diff --git a/labfy-investigation/src/core/application.c b/labfy-investigation/src/core/application.c index 843b021..5a16e74 100644 --- a/labfy-investigation/src/core/application.c +++ b/labfy-investigation/src/core/application.c @@ -6,6 +6,7 @@ #include "core/application.h" #include "views/folder_dialog.h" #include "core/investigation.h" +#include "views/main_window.h" #include @@ -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); +}