feat(view): create main window
This commit is contained in:
parent
d8242f20ca
commit
a8e66c40be
7 changed files with 468 additions and 28 deletions
|
|
@ -6,3 +6,9 @@
|
||||||
- Création du module `Application`.
|
- Création du module `Application`.
|
||||||
- Intégration de GTK4.
|
- Intégration de GTK4.
|
||||||
- Affichage de la première fenêtre.
|
- 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.
|
||||||
|
|
|
||||||
57
docs/tickets/closed/TICKET-003.md
Normal file
57
docs/tickets/closed/TICKET-003.md
Normal file
|
|
@ -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);
|
||||||
128
docs/tickets/closed/TICKET-004.md
Normal file
128
docs/tickets/closed/TICKET-004.md
Normal file
|
|
@ -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
|
||||||
|
```
|
||||||
50
labfy-investigation/include/views/main_window.h
Normal file
50
labfy-investigation/include/views/main_window.h
Normal file
|
|
@ -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 <gtk/gtk.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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
|
||||||
Binary file not shown.
|
|
@ -6,6 +6,7 @@
|
||||||
#include "core/application.h"
|
#include "core/application.h"
|
||||||
#include "views/folder_dialog.h"
|
#include "views/folder_dialog.h"
|
||||||
#include "core/investigation.h"
|
#include "core/investigation.h"
|
||||||
|
#include "views/main_window.h"
|
||||||
|
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
|
@ -30,6 +31,8 @@
|
||||||
struct Application
|
struct Application
|
||||||
{
|
{
|
||||||
GtkApplication *gtk_application;
|
GtkApplication *gtk_application;
|
||||||
|
MainWindow *main_window;
|
||||||
|
Investigation *investigation;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -47,57 +50,83 @@ static void application_on_folder_selected(
|
||||||
gpointer user_data
|
gpointer user_data
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
GtkWindow *window = GTK_WINDOW(user_data);
|
Application *application = user_data;
|
||||||
Investigation *investigation = NULL;
|
|
||||||
const char *root_path = NULL;
|
const char *root_path = NULL;
|
||||||
const char *database_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)
|
if (folder_path == NULL)
|
||||||
{
|
{
|
||||||
g_print("Sélection annulée.\n");
|
g_print("Sélection annulée.\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
investigation = investigation_new(folder_path);
|
if (application->investigation == NULL)
|
||||||
|
|
||||||
if (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;
|
return;
|
||||||
}
|
}
|
||||||
|
root_path = investigation_get_root_path(
|
||||||
root_path = investigation_get_root_path(investigation);
|
application->investigation
|
||||||
database_path = investigation_get_database_path(investigation);
|
);
|
||||||
|
database_path = investigation_get_database_path(
|
||||||
|
application->investigation
|
||||||
|
);
|
||||||
|
|
||||||
g_print("Dossier racine : %s\n", root_path);
|
g_print("Dossier racine : %s\n", root_path);
|
||||||
g_print("Base de données : %s\n", database_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,
|
static void application_on_activate(
|
||||||
gpointer user_data)
|
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");
|
application->main_window = main_window_new(gtk_application);
|
||||||
gtk_window_set_default_size(
|
|
||||||
GTK_WINDOW(window),
|
|
||||||
DEFAULT_WINDOW_WIDTH,
|
|
||||||
DEFAULT_WINDOW_HEIGHT
|
|
||||||
);
|
|
||||||
|
|
||||||
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(
|
folder_dialog_select_folder(
|
||||||
GTK_WINDOW(window),
|
main_window_get_window(application->main_window),
|
||||||
application_on_folder_selected,
|
application_on_folder_selected,
|
||||||
window
|
application
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -128,7 +157,11 @@ Application *application_new(void)
|
||||||
return application;
|
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)
|
if (application == NULL || application->gtk_application == NULL)
|
||||||
{
|
{
|
||||||
|
|
@ -149,6 +182,10 @@ void application_free(Application *application)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
investigation_free(application->investigation);
|
||||||
|
|
||||||
|
main_window_free(application->main_window);
|
||||||
|
|
||||||
if (application->gtk_application != NULL)
|
if (application->gtk_application != NULL)
|
||||||
{
|
{
|
||||||
g_object_unref(application->gtk_application);
|
g_object_unref(application->gtk_application);
|
||||||
|
|
|
||||||
162
labfy-investigation/src/views/main_window.c
Normal file
162
labfy-investigation/src/views/main_window.c
Normal file
|
|
@ -0,0 +1,162 @@
|
||||||
|
/******************************************************************************
|
||||||
|
* @file main_window.c
|
||||||
|
* @brief Implémentation de la fenêtre principale.
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include "views/main_window.h"
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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);
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue