feat(ui): display application errors in GTK
This commit is contained in:
parent
9ee7ee61c1
commit
f7b521cda6
19 changed files with 2987 additions and 1 deletions
1299
docs/ROADMAP.md
1299
docs/ROADMAP.md
File diff suppressed because it is too large
Load diff
537
docs/tickets/closed/TICKET-032.md
Normal file
537
docs/tickets/closed/TICKET-032.md
Normal file
|
|
@ -0,0 +1,537 @@
|
|||
# Ticket #032 — Factoriser le chargement et l’installation d’une enquête
|
||||
|
||||
## Contexte
|
||||
|
||||
Après le ticket #031.1, l’application sait :
|
||||
|
||||
- créer une enquête ;
|
||||
- ouvrir une enquête existante ;
|
||||
- remplacer la session active ;
|
||||
- fermer proprement l’application.
|
||||
|
||||
Cependant, les flux « Nouvelle enquête » et « Ouvrir une enquête » dupliquent encore une partie importante de la logique :
|
||||
|
||||
```text
|
||||
investigation_session_open()
|
||||
→ récupération du projet
|
||||
→ récupération du chemin racine
|
||||
→ investigation_tree_builder_build()
|
||||
→ application_install_session()
|
||||
→ nettoyage en cas d’échec
|
||||
```
|
||||
|
||||
Cette duplication augmentera avec les futures fonctions :
|
||||
|
||||
- enquêtes récentes ;
|
||||
- ouverture depuis la ligne de commande ;
|
||||
- restauration de session ;
|
||||
- ouverture depuis un rapport ou une archive.
|
||||
|
||||
## Objectif
|
||||
|
||||
Créer dans `src/core/application.c` une fonction interne unique qui ouvre et installe une enquête à partir de son dossier racine.
|
||||
|
||||
Contrat attendu :
|
||||
|
||||
```c
|
||||
static gboolean application_open_and_install_investigation(
|
||||
Application *application,
|
||||
const char *root_path,
|
||||
GError **error
|
||||
);
|
||||
```
|
||||
|
||||
Cette fonction doit garantir :
|
||||
|
||||
```text
|
||||
succès :
|
||||
Application devient propriétaire de la nouvelle session
|
||||
Application devient propriétaire du nouvel arbre
|
||||
ancienne session libérée
|
||||
ancien arbre libéré
|
||||
fenêtre mise à jour
|
||||
|
||||
échec :
|
||||
ancienne session conservée
|
||||
ancien arbre conservé
|
||||
nouvelle session libérée
|
||||
nouvel arbre libéré
|
||||
erreur transmise à l’appelant
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Travail à réaliser
|
||||
|
||||
## 1. Ajouter un domaine d’erreur privé
|
||||
|
||||
Dans `src/core/application.c`, ajouter un domaine d’erreur uniquement utilisé par le contrôleur.
|
||||
|
||||
Exemple :
|
||||
|
||||
```c
|
||||
typedef enum
|
||||
{
|
||||
APPLICATION_OPEN_ERROR_INVALID_ARGUMENT,
|
||||
APPLICATION_OPEN_ERROR_INVALID_PROJECT,
|
||||
APPLICATION_OPEN_ERROR_TREE_BUILD,
|
||||
APPLICATION_OPEN_ERROR_INSTALL
|
||||
} ApplicationOpenError;
|
||||
```
|
||||
|
||||
Ajouter :
|
||||
|
||||
```c
|
||||
#define APPLICATION_OPEN_ERROR \
|
||||
application_open_error_quark()
|
||||
```
|
||||
|
||||
Puis :
|
||||
|
||||
```c
|
||||
static GQuark application_open_error_quark(void)
|
||||
{
|
||||
return g_quark_from_static_string(
|
||||
"labfy-investigation-application-open-error"
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Le domaine reste privé à `application.c`.
|
||||
|
||||
---
|
||||
|
||||
## 2. Créer la fonction factorisée
|
||||
|
||||
Ajouter :
|
||||
|
||||
```c
|
||||
static gboolean application_open_and_install_investigation(
|
||||
Application *application,
|
||||
const char *root_path,
|
||||
GError **error
|
||||
);
|
||||
```
|
||||
|
||||
La fonction doit :
|
||||
|
||||
1. valider `application` ;
|
||||
2. valider `application->main_window` ;
|
||||
3. valider `root_path` ;
|
||||
4. ouvrir une nouvelle `InvestigationSession` ;
|
||||
5. récupérer son `InvestigationProject` ;
|
||||
6. récupérer le chemin racine canonique ;
|
||||
7. construire un nouvel `InvestigationTreeModel` ;
|
||||
8. appeler `application_install_session()` ;
|
||||
9. transférer la propriété uniquement en cas de succès.
|
||||
|
||||
### Règle de propriété
|
||||
|
||||
Avant `application_install_session()` :
|
||||
|
||||
```text
|
||||
la fonction possède new_session
|
||||
la fonction possède new_tree_model
|
||||
```
|
||||
|
||||
Après succès :
|
||||
|
||||
```text
|
||||
Application possède new_session
|
||||
Application possède new_tree_model
|
||||
```
|
||||
|
||||
Après échec :
|
||||
|
||||
```text
|
||||
la fonction doit libérer les objets qu’elle possède encore
|
||||
```
|
||||
|
||||
### Validation de `GError`
|
||||
|
||||
La fonction doit respecter la convention GLib :
|
||||
|
||||
```c
|
||||
g_return_val_if_fail(
|
||||
error == NULL || *error == NULL,
|
||||
FALSE
|
||||
);
|
||||
```
|
||||
|
||||
L’utilisation de `g_return_val_if_fail()` est acceptable ici pour vérifier le contrat du développeur.
|
||||
|
||||
Les erreurs utilisateur doivent être produites avec :
|
||||
|
||||
```c
|
||||
g_set_error()
|
||||
g_set_error_literal()
|
||||
g_propagate_prefixed_error()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Propager l’erreur de `InvestigationSession`
|
||||
|
||||
Si :
|
||||
|
||||
```c
|
||||
investigation_session_open()
|
||||
```
|
||||
|
||||
échoue, la fonction doit conserver l’erreur métier d’origine et lui ajouter du contexte.
|
||||
|
||||
Exemple conceptuel :
|
||||
|
||||
```text
|
||||
Impossible d’ouvrir l’enquête : la base SQLite est absente
|
||||
```
|
||||
|
||||
Ne pas remplacer l’erreur précise par un simple :
|
||||
|
||||
```text
|
||||
Erreur inconnue
|
||||
```
|
||||
|
||||
lorsqu’un `GError` est disponible.
|
||||
|
||||
---
|
||||
|
||||
## 4. Refactoriser l’ouverture d’une enquête existante
|
||||
|
||||
`application_on_folder_selected()` ne doit plus contenir directement :
|
||||
|
||||
```c
|
||||
investigation_session_open()
|
||||
investigation_tree_builder_build()
|
||||
application_install_session()
|
||||
```
|
||||
|
||||
Elle doit seulement :
|
||||
|
||||
1. accepter l’annulation ;
|
||||
2. appeler `application_open_and_install_investigation()` ;
|
||||
3. journaliser l’erreur ;
|
||||
4. libérer le `GError`.
|
||||
|
||||
Structure attendue :
|
||||
|
||||
```c
|
||||
static void application_on_folder_selected(
|
||||
const char *folder_path,
|
||||
gpointer user_data
|
||||
)
|
||||
{
|
||||
Application *application = user_data;
|
||||
GError *error = NULL;
|
||||
|
||||
if (application == NULL ||
|
||||
folder_path == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!application_open_and_install_investigation(
|
||||
application,
|
||||
folder_path,
|
||||
&error
|
||||
))
|
||||
{
|
||||
g_warning(
|
||||
"Impossible d'ouvrir l'enquête '%s' : %s",
|
||||
folder_path,
|
||||
error != NULL
|
||||
? error->message
|
||||
: "erreur inconnue"
|
||||
);
|
||||
|
||||
g_clear_error(&error);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
L’annulation ne doit pas produire de warning.
|
||||
|
||||
---
|
||||
|
||||
## 5. Refactoriser la création d’une enquête
|
||||
|
||||
`application_on_create_investigation()` doit conserver uniquement :
|
||||
|
||||
1. la création physique du projet ;
|
||||
2. l’appel à la fonction factorisée ;
|
||||
3. le message spécifique indiquant que le projet existe sur le disque si son ouverture échoue.
|
||||
|
||||
Le flux devient :
|
||||
|
||||
```text
|
||||
investigation_project_create()
|
||||
→ application_open_and_install_investigation()
|
||||
```
|
||||
|
||||
La fonction ne doit plus reconstruire elle-même :
|
||||
|
||||
```text
|
||||
session
|
||||
projet
|
||||
chemin canonique
|
||||
arbre
|
||||
installation
|
||||
```
|
||||
|
||||
En cas d’échec après création, le dossier créé reste volontairement sur le disque.
|
||||
|
||||
Le message doit être explicite :
|
||||
|
||||
```text
|
||||
L’enquête a été créée dans « ... », mais son ouverture a échoué : ...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Ne pas modifier `application_install_session()`
|
||||
|
||||
Cette fonction conserve sa responsabilité actuelle :
|
||||
|
||||
- valider les objets prêts à installer ;
|
||||
- libérer l’ancien état ;
|
||||
- transférer la propriété ;
|
||||
- mettre à jour `MainWindow`.
|
||||
|
||||
Le nouveau helper prépare les objets.
|
||||
|
||||
`application_install_session()` réalise le remplacement final.
|
||||
|
||||
---
|
||||
|
||||
# Pseudo-code de la fonction factorisée
|
||||
|
||||
```c
|
||||
static gboolean application_open_and_install_investigation(
|
||||
Application *application,
|
||||
const char *root_path,
|
||||
GError **error
|
||||
)
|
||||
{
|
||||
InvestigationSession *new_session = NULL;
|
||||
InvestigationTreeModel *new_tree_model = NULL;
|
||||
const InvestigationProject *project = NULL;
|
||||
const char *canonical_root_path = NULL;
|
||||
GError *session_error = NULL;
|
||||
|
||||
valider les arguments;
|
||||
|
||||
new_session = investigation_session_open(
|
||||
root_path,
|
||||
&session_error
|
||||
);
|
||||
|
||||
si échec :
|
||||
propager session_error avec contexte;
|
||||
return FALSE;
|
||||
|
||||
project = investigation_session_get_project(
|
||||
new_session
|
||||
);
|
||||
|
||||
valider project;
|
||||
|
||||
canonical_root_path =
|
||||
investigation_project_get_root_path(project);
|
||||
|
||||
valider canonical_root_path;
|
||||
|
||||
new_tree_model =
|
||||
investigation_tree_builder_build(
|
||||
canonical_root_path
|
||||
);
|
||||
|
||||
si échec :
|
||||
produire GError;
|
||||
fermer new_session;
|
||||
return FALSE;
|
||||
|
||||
si application_install_session() échoue :
|
||||
produire GError;
|
||||
libérer new_tree_model;
|
||||
fermer new_session;
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Tests manuels
|
||||
|
||||
## Création valide
|
||||
|
||||
1. lancer l’application ;
|
||||
2. créer une enquête ;
|
||||
3. vérifier le titre ;
|
||||
4. vérifier la barre d’état ;
|
||||
5. vérifier l’arborescence.
|
||||
|
||||
## Ouverture valide
|
||||
|
||||
1. fermer et relancer ;
|
||||
2. ouvrir l’enquête créée ;
|
||||
3. vérifier le même résultat.
|
||||
|
||||
## Remplacement valide
|
||||
|
||||
1. ouvrir une enquête A ;
|
||||
2. ouvrir une enquête B ;
|
||||
3. vérifier que B remplace A.
|
||||
|
||||
## Échec d’ouverture
|
||||
|
||||
1. ouvrir une enquête valide A ;
|
||||
2. tenter d’ouvrir un dossier invalide ;
|
||||
3. vérifier que A reste active ;
|
||||
4. vérifier que l’erreur est journalisée.
|
||||
|
||||
## Échec après création
|
||||
|
||||
Provoquer si possible un échec d’ouverture après création.
|
||||
|
||||
Vérifier :
|
||||
|
||||
- le dossier créé reste présent ;
|
||||
- l’ancienne session reste active ;
|
||||
- aucun double `free` ;
|
||||
- aucun crash.
|
||||
|
||||
## Annulation
|
||||
|
||||
Annuler le sélecteur de dossier.
|
||||
|
||||
Vérifier :
|
||||
|
||||
- aucun warning ;
|
||||
- aucun changement d’état ;
|
||||
- aucun crash.
|
||||
|
||||
---
|
||||
|
||||
# Critères d’acceptation
|
||||
|
||||
- [ ] Une seule fonction ouvre une session et construit son arbre.
|
||||
- [ ] La création utilise cette fonction.
|
||||
- [ ] L’ouverture utilise cette fonction.
|
||||
- [ ] `application_install_session()` n’est pas dupliquée.
|
||||
- [ ] L’ancienne session reste active en cas d’échec.
|
||||
- [ ] L’ancien arbre reste actif en cas d’échec.
|
||||
- [ ] Les nouveaux objets sont libérés en cas d’échec.
|
||||
- [ ] Les erreurs de `InvestigationSession` sont propagées.
|
||||
- [ ] L’annulation ne produit pas d’erreur.
|
||||
- [ ] Aucun code SQLite direct n’est ajouté.
|
||||
- [ ] Aucun comportement GTK visible n’est cassé.
|
||||
- [ ] `make` réussit.
|
||||
- [ ] `make test` réussit.
|
||||
- [ ] `git diff --check` ne retourne aucune erreur.
|
||||
|
||||
---
|
||||
|
||||
# Audit attendu
|
||||
|
||||
La logique d’ouverture ne doit apparaître qu’une seule fois :
|
||||
|
||||
```bash
|
||||
rg -n \
|
||||
'investigation_session_open|investigation_tree_builder_build' \
|
||||
src/core/application.c
|
||||
```
|
||||
|
||||
Résultat attendu :
|
||||
|
||||
```text
|
||||
une occurrence de investigation_session_open
|
||||
une occurrence de investigation_tree_builder_build
|
||||
```
|
||||
|
||||
La création et l’ouverture doivent appeler le helper :
|
||||
|
||||
```bash
|
||||
rg -n \
|
||||
'application_open_and_install_investigation' \
|
||||
src/core/application.c
|
||||
```
|
||||
|
||||
Vérifier l’absence de SQLite direct :
|
||||
|
||||
```bash
|
||||
rg -n \
|
||||
'sqlite3_|#include <sqlite3.h>' \
|
||||
src/core/application.c
|
||||
```
|
||||
|
||||
Résultat attendu :
|
||||
|
||||
```text
|
||||
aucune sortie
|
||||
```
|
||||
|
||||
Vérifier le format :
|
||||
|
||||
```bash
|
||||
git diff --check
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Fichiers concernés
|
||||
|
||||
```text
|
||||
src/core/application.c
|
||||
```
|
||||
|
||||
Aucune modification publique n’est normalement nécessaire dans :
|
||||
|
||||
```text
|
||||
include/core/application.h
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Commit attendu
|
||||
|
||||
```bash
|
||||
make clean
|
||||
make
|
||||
make test
|
||||
git diff --check
|
||||
git status --short
|
||||
```
|
||||
|
||||
```bash
|
||||
git add src/core/application.c
|
||||
```
|
||||
|
||||
```bash
|
||||
git diff --cached --stat
|
||||
git diff --cached
|
||||
```
|
||||
|
||||
```bash
|
||||
git commit -m "refactor(core): centralize investigation loading"
|
||||
git push
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Résultat attendu
|
||||
|
||||
Après ce ticket, tous les futurs points d’entrée utiliseront le même flux :
|
||||
|
||||
```text
|
||||
Création
|
||||
Ouverture manuelle
|
||||
Enquêtes récentes
|
||||
Ligne de commande
|
||||
Restauration de session
|
||||
↓
|
||||
application_open_and_install_investigation()
|
||||
```
|
||||
|
||||
Le ticket #033 pourra ensuite afficher graphiquement les `GError` déjà correctement produits par ce flux.
|
||||
694
docs/tickets/closed/TICKET-033.md
Normal file
694
docs/tickets/closed/TICKET-033.md
Normal file
|
|
@ -0,0 +1,694 @@
|
|||
# Ticket #033 — Afficher les erreurs dans l’interface GTK
|
||||
|
||||
## Contexte
|
||||
|
||||
Le ticket #032 a centralisé le chargement d’une enquête dans :
|
||||
|
||||
```c
|
||||
application_open_and_install_investigation()
|
||||
```
|
||||
|
||||
Cette fonction produit maintenant des `GError` précis et conserve l’ancienne session en cas d’échec.
|
||||
|
||||
Actuellement, les erreurs sont seulement écrites dans le terminal avec :
|
||||
|
||||
```c
|
||||
g_warning()
|
||||
```
|
||||
|
||||
Un utilisateur qui lance Labfy Investigation depuis son menu d’applications ne verra pas ces messages.
|
||||
|
||||
## Objectif
|
||||
|
||||
Créer un module GTK réutilisable capable d’afficher un message d’erreur compréhensible dans une fenêtre modale.
|
||||
|
||||
Le contrôleur doit conserver deux niveaux d’information :
|
||||
|
||||
```text
|
||||
Terminal :
|
||||
message technique complet avec g_warning()
|
||||
|
||||
Interface GTK :
|
||||
titre clair + message compréhensible
|
||||
```
|
||||
|
||||
Le module doit rester générique afin de pouvoir afficher plus tard :
|
||||
|
||||
- erreurs ;
|
||||
- avertissements ;
|
||||
- informations.
|
||||
|
||||
---
|
||||
|
||||
# Architecture attendue
|
||||
|
||||
Créer :
|
||||
|
||||
```text
|
||||
include/views/application_message_dialog.h
|
||||
src/views/application_message_dialog.c
|
||||
```
|
||||
|
||||
Responsabilités :
|
||||
|
||||
```text
|
||||
Application
|
||||
├── décide quand afficher une erreur
|
||||
├── fournit le titre
|
||||
└── fournit le message
|
||||
|
||||
ApplicationMessageDialog
|
||||
├── construit la fenêtre GTK
|
||||
├── affiche le contenu
|
||||
└── gère sa fermeture
|
||||
```
|
||||
|
||||
Le module de dialogue ne doit connaître ni :
|
||||
|
||||
- `Application` ;
|
||||
- `InvestigationSession` ;
|
||||
- SQLite ;
|
||||
- les règles métier de l’enquête.
|
||||
|
||||
---
|
||||
|
||||
# Travail à réaliser
|
||||
|
||||
## 1. Créer l’en-tête public
|
||||
|
||||
Créer :
|
||||
|
||||
```text
|
||||
include/views/application_message_dialog.h
|
||||
```
|
||||
|
||||
Contenu attendu :
|
||||
|
||||
```c
|
||||
/******************************************************************************
|
||||
* @file application_message_dialog.h
|
||||
* @brief Fenêtre modale affichant un message à l'utilisateur.
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef LABFY_INVESTIGATION_APPLICATION_MESSAGE_DIALOG_H
|
||||
#define LABFY_INVESTIGATION_APPLICATION_MESSAGE_DIALOG_H
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
/**
|
||||
* @brief Nature du message affiché.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
APPLICATION_MESSAGE_DIALOG_ERROR,
|
||||
APPLICATION_MESSAGE_DIALOG_WARNING,
|
||||
APPLICATION_MESSAGE_DIALOG_INFORMATION
|
||||
} ApplicationMessageDialogType;
|
||||
|
||||
/**
|
||||
* @brief Affiche une fenêtre modale contenant un message.
|
||||
*
|
||||
* Les chaînes sont copiées par les widgets GTK.
|
||||
*
|
||||
* @param parent_window Fenêtre parente, ou NULL.
|
||||
* @param message_type Type de message.
|
||||
* @param title Titre de la fenêtre.
|
||||
* @param message Message principal.
|
||||
*/
|
||||
void application_message_dialog_present(
|
||||
GtkWindow *parent_window,
|
||||
ApplicationMessageDialogType message_type,
|
||||
const char *title,
|
||||
const char *message
|
||||
);
|
||||
|
||||
#endif
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Créer l’implémentation GTK
|
||||
|
||||
Créer :
|
||||
|
||||
```text
|
||||
src/views/application_message_dialog.c
|
||||
```
|
||||
|
||||
Le dialogue doit être construit avec des widgets GTK4 simples afin de rester indépendant d’une API de dialogue plus récente.
|
||||
|
||||
Structure visuelle recommandée :
|
||||
|
||||
```text
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ Titre │
|
||||
│ │
|
||||
│ Message pouvant occuper plusieurs lignes │
|
||||
│ │
|
||||
│ [ Fermer ] │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
Le module doit utiliser au minimum :
|
||||
|
||||
- `GtkWindow` ;
|
||||
- `GtkBox` ;
|
||||
- `GtkLabel` ;
|
||||
- `GtkButton`.
|
||||
|
||||
Propriétés recommandées :
|
||||
|
||||
```c
|
||||
gtk_window_set_modal(dialog_window, TRUE);
|
||||
gtk_window_set_destroy_with_parent(dialog_window, TRUE);
|
||||
gtk_window_set_resizable(dialog_window, FALSE);
|
||||
```
|
||||
|
||||
Si `parent_window` n’est pas `NULL` :
|
||||
|
||||
```c
|
||||
gtk_window_set_transient_for(
|
||||
dialog_window,
|
||||
parent_window
|
||||
);
|
||||
```
|
||||
|
||||
Le message doit :
|
||||
|
||||
- revenir automatiquement à la ligne ;
|
||||
- être aligné à gauche ;
|
||||
- être sélectionnable pour permettre sa copie ;
|
||||
- avoir une largeur raisonnable.
|
||||
|
||||
Exemple :
|
||||
|
||||
```c
|
||||
gtk_label_set_wrap(
|
||||
GTK_LABEL(message_label),
|
||||
TRUE
|
||||
);
|
||||
|
||||
gtk_label_set_selectable(
|
||||
GTK_LABEL(message_label),
|
||||
TRUE
|
||||
);
|
||||
|
||||
gtk_label_set_xalign(
|
||||
GTK_LABEL(message_label),
|
||||
0.0F
|
||||
);
|
||||
|
||||
gtk_label_set_max_width_chars(
|
||||
GTK_LABEL(message_label),
|
||||
70
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Gérer les arguments invalides
|
||||
|
||||
La fonction doit accepter :
|
||||
|
||||
```text
|
||||
parent_window == NULL
|
||||
title == NULL
|
||||
title vide
|
||||
message == NULL
|
||||
message vide
|
||||
```
|
||||
|
||||
Valeurs de remplacement recommandées :
|
||||
|
||||
```text
|
||||
Titre :
|
||||
Erreur
|
||||
Avertissement
|
||||
Information
|
||||
|
||||
Message :
|
||||
Aucun détail supplémentaire n'est disponible.
|
||||
```
|
||||
|
||||
Le titre par défaut dépend de `message_type`.
|
||||
|
||||
Une valeur inconnue de `message_type` doit être traitée comme une information ou un avertissement, sans crash.
|
||||
|
||||
---
|
||||
|
||||
## 4. Ajouter un callback privé de fermeture
|
||||
|
||||
Dans l’implémentation, ajouter :
|
||||
|
||||
```c
|
||||
static void application_message_dialog_on_close_clicked(
|
||||
GtkButton *button,
|
||||
gpointer user_data
|
||||
);
|
||||
```
|
||||
|
||||
Ce callback doit :
|
||||
|
||||
1. ignorer proprement `button` ;
|
||||
2. vérifier le pointeur de fenêtre ;
|
||||
3. appeler `gtk_window_destroy()`.
|
||||
|
||||
Exemple :
|
||||
|
||||
```c
|
||||
static void application_message_dialog_on_close_clicked(
|
||||
GtkButton *button,
|
||||
gpointer user_data
|
||||
)
|
||||
{
|
||||
GtkWindow *dialog_window = user_data;
|
||||
|
||||
(void) button;
|
||||
|
||||
if (dialog_window == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
gtk_window_destroy(
|
||||
dialog_window
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Aucune structure allouée manuellement ne doit être nécessaire pour ce premier dialogue.
|
||||
|
||||
---
|
||||
|
||||
## 5. Différencier visuellement les types
|
||||
|
||||
Le dialogue doit au minimum afficher un libellé distinct :
|
||||
|
||||
```text
|
||||
Erreur
|
||||
Avertissement
|
||||
Information
|
||||
```
|
||||
|
||||
Une différenciation légère peut être ajoutée avec des classes CSS GTK existantes sur le titre ou le bouton.
|
||||
|
||||
La couleur ne doit jamais être le seul moyen de distinguer le type.
|
||||
|
||||
Aucune feuille CSS spécifique n’est nécessaire dans ce ticket.
|
||||
|
||||
---
|
||||
|
||||
## 6. Ajouter la source au Makefile
|
||||
|
||||
Ajouter :
|
||||
|
||||
```text
|
||||
src/views/application_message_dialog.c
|
||||
```
|
||||
|
||||
à la liste des sources de l’application.
|
||||
|
||||
Le module doit être compilé avec les mêmes options strictes :
|
||||
|
||||
```text
|
||||
-std=c17
|
||||
-Wall
|
||||
-Wextra
|
||||
-Wpedantic
|
||||
-Werror
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Intégration dans `Application`
|
||||
|
||||
## 7. Ajouter l’en-tête
|
||||
|
||||
Dans :
|
||||
|
||||
```text
|
||||
src/core/application.c
|
||||
```
|
||||
|
||||
ajouter :
|
||||
|
||||
```c
|
||||
#include "views/application_message_dialog.h"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Créer un helper privé dans `application.c`
|
||||
|
||||
Ajouter :
|
||||
|
||||
```c
|
||||
static void application_present_error(
|
||||
Application *application,
|
||||
const char *title,
|
||||
const char *message
|
||||
);
|
||||
```
|
||||
|
||||
Implémentation attendue :
|
||||
|
||||
```c
|
||||
static void application_present_error(
|
||||
Application *application,
|
||||
const char *title,
|
||||
const char *message
|
||||
)
|
||||
{
|
||||
GtkWindow *parent_window = NULL;
|
||||
|
||||
if (application != NULL &&
|
||||
application->main_window != NULL)
|
||||
{
|
||||
parent_window = main_window_get_window(
|
||||
application->main_window
|
||||
);
|
||||
}
|
||||
|
||||
application_message_dialog_present(
|
||||
parent_window,
|
||||
APPLICATION_MESSAGE_DIALOG_ERROR,
|
||||
title,
|
||||
message
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Ce helper centralise le choix du parent GTK.
|
||||
|
||||
---
|
||||
|
||||
## 9. Afficher les erreurs d’ouverture
|
||||
|
||||
Dans :
|
||||
|
||||
```c
|
||||
application_on_folder_selected()
|
||||
```
|
||||
|
||||
conserver le `g_warning()` existant.
|
||||
|
||||
Ajouter ensuite :
|
||||
|
||||
```c
|
||||
application_present_error(
|
||||
application,
|
||||
"Ouverture impossible",
|
||||
error != NULL
|
||||
? error->message
|
||||
: "L'enquête sélectionnée n'a pas pu être ouverte."
|
||||
);
|
||||
```
|
||||
|
||||
L’erreur doit être affichée avant :
|
||||
|
||||
```c
|
||||
g_clear_error(&error);
|
||||
```
|
||||
|
||||
L’annulation du sélecteur ne doit toujours afficher aucun message.
|
||||
|
||||
---
|
||||
|
||||
## 10. Afficher les erreurs de création
|
||||
|
||||
Dans :
|
||||
|
||||
```c
|
||||
application_on_create_investigation()
|
||||
```
|
||||
|
||||
traiter les deux échecs.
|
||||
|
||||
### Échec de création physique
|
||||
|
||||
Conserver le `g_warning()` puis afficher :
|
||||
|
||||
```text
|
||||
Titre :
|
||||
Création impossible
|
||||
|
||||
Message :
|
||||
L'enquête n'a pas pu être créée dans le dossier sélectionné.
|
||||
```
|
||||
|
||||
Le message peut contenir :
|
||||
|
||||
- le nom de l’enquête ;
|
||||
- le dossier parent.
|
||||
|
||||
### Projet créé mais impossible à ouvrir
|
||||
|
||||
Conserver le `g_warning()` puis afficher un message indiquant clairement :
|
||||
|
||||
```text
|
||||
L'enquête a été créée sur le disque, mais Labfy n'a pas pu l'ouvrir.
|
||||
```
|
||||
|
||||
Ajouter ensuite le détail du `GError`.
|
||||
|
||||
Le message doit éviter de laisser croire que le dossier créé a été supprimé.
|
||||
|
||||
Une chaîne dynamique peut être construite avec :
|
||||
|
||||
```c
|
||||
g_strdup_printf()
|
||||
```
|
||||
|
||||
Elle doit être libérée après l’appel au dialogue.
|
||||
|
||||
---
|
||||
|
||||
# Exemple d’intégration
|
||||
|
||||
```c
|
||||
char *user_message = NULL;
|
||||
|
||||
user_message = g_strdup_printf(
|
||||
"L'enquête a été créée dans :\n%s\n\n"
|
||||
"Elle n'a cependant pas pu être ouverte :\n%s",
|
||||
created_root_path,
|
||||
error != NULL
|
||||
? error->message
|
||||
: "erreur inconnue"
|
||||
);
|
||||
|
||||
application_present_error(
|
||||
application,
|
||||
"Enquête créée mais non ouverte",
|
||||
user_message
|
||||
);
|
||||
|
||||
g_free(
|
||||
user_message
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Gestion de plusieurs erreurs
|
||||
|
||||
Ce ticket ne crée pas encore de file de notifications.
|
||||
|
||||
Si plusieurs erreurs surviennent successivement, plusieurs fenêtres peuvent être affichées.
|
||||
|
||||
La gestion centralisée des tâches et notifications arrivera avec les tickets #034 et #035.
|
||||
|
||||
---
|
||||
|
||||
# Tests manuels
|
||||
|
||||
## Dossier invalide
|
||||
|
||||
1. lancer l’application ;
|
||||
2. ouvrir une enquête valide A ;
|
||||
3. cliquer sur `Ouvrir une enquête` ;
|
||||
4. sélectionner un dossier qui n’est pas une enquête.
|
||||
|
||||
Vérifier :
|
||||
|
||||
- une fenêtre d’erreur apparaît ;
|
||||
- le message explique l’échec ;
|
||||
- A reste active ;
|
||||
- le terminal contient encore le `g_warning()` ;
|
||||
- aucun crash.
|
||||
|
||||
## Annulation
|
||||
|
||||
1. ouvrir le sélecteur ;
|
||||
2. annuler.
|
||||
|
||||
Vérifier :
|
||||
|
||||
- aucun dialogue ;
|
||||
- aucun warning ;
|
||||
- aucun changement de session.
|
||||
|
||||
## Création dans un emplacement invalide
|
||||
|
||||
Provoquer si possible un échec de création :
|
||||
|
||||
- dossier non accessible en écriture ;
|
||||
- nom déjà utilisé ;
|
||||
- emplacement invalide.
|
||||
|
||||
Vérifier :
|
||||
|
||||
- dialogue visible ;
|
||||
- message compréhensible ;
|
||||
- aucune session remplacée.
|
||||
|
||||
## Projet créé mais ouverture échouée
|
||||
|
||||
Provoquer si possible un échec après la création.
|
||||
|
||||
Vérifier que le dialogue précise :
|
||||
|
||||
- que le dossier existe ;
|
||||
- où il se trouve ;
|
||||
- que seule l’ouverture a échoué.
|
||||
|
||||
## Fermeture du dialogue
|
||||
|
||||
Tester :
|
||||
|
||||
- bouton `Fermer` ;
|
||||
- bouton de fermeture de la fenêtre ;
|
||||
- fermeture de la fenêtre principale.
|
||||
|
||||
Aucun segfault ne doit apparaître.
|
||||
|
||||
## Répétition
|
||||
|
||||
Produire plusieurs erreurs successives.
|
||||
|
||||
Vérifier qu’une nouvelle erreur reste affichable après fermeture de la précédente.
|
||||
|
||||
---
|
||||
|
||||
# Critères d’acceptation
|
||||
|
||||
- [ ] Le module `application_message_dialog` existe.
|
||||
- [ ] Le module ne dépend pas du cœur métier.
|
||||
- [ ] Le dialogue est modal.
|
||||
- [ ] Le dialogue possède une fenêtre parente lorsqu’elle existe.
|
||||
- [ ] Le message revient à la ligne.
|
||||
- [ ] Le message est sélectionnable.
|
||||
- [ ] Les arguments `NULL` sont acceptés.
|
||||
- [ ] Un bouton `Fermer` fonctionne.
|
||||
- [ ] Les erreurs d’ouverture sont visibles dans GTK.
|
||||
- [ ] Les erreurs de création sont visibles dans GTK.
|
||||
- [ ] Les `g_warning()` techniques sont conservés.
|
||||
- [ ] L’annulation ne produit aucun dialogue.
|
||||
- [ ] L’ancienne session est conservée en cas d’échec.
|
||||
- [ ] Aucun appel SQLite direct n’est ajouté.
|
||||
- [ ] Aucun `exit()` direct n’est ajouté.
|
||||
- [ ] `make` réussit.
|
||||
- [ ] `make test` réussit.
|
||||
- [ ] `git diff --check` ne retourne aucune erreur.
|
||||
|
||||
---
|
||||
|
||||
# Audit attendu
|
||||
|
||||
Vérifier l’utilisation du module :
|
||||
|
||||
```bash
|
||||
rg -n \
|
||||
'application_message_dialog|application_present_error' \
|
||||
include/views/application_message_dialog.h \
|
||||
src/views/application_message_dialog.c \
|
||||
src/core/application.c \
|
||||
Makefile
|
||||
```
|
||||
|
||||
Vérifier que les erreurs restent journalisées :
|
||||
|
||||
```bash
|
||||
rg -n \
|
||||
'g_warning' \
|
||||
src/core/application.c
|
||||
```
|
||||
|
||||
Vérifier l’absence de logique métier dans le dialogue :
|
||||
|
||||
```bash
|
||||
rg -n \
|
||||
'Investigation|Database|sqlite3_' \
|
||||
include/views/application_message_dialog.h \
|
||||
src/views/application_message_dialog.c
|
||||
```
|
||||
|
||||
Résultat attendu :
|
||||
|
||||
```text
|
||||
aucune sortie
|
||||
```
|
||||
|
||||
Vérifier l’absence de sortie forcée :
|
||||
|
||||
```bash
|
||||
rg -n \
|
||||
'\bexit\s*\(' \
|
||||
src/core/application.c \
|
||||
src/views/application_message_dialog.c
|
||||
```
|
||||
|
||||
Résultat attendu :
|
||||
|
||||
```text
|
||||
aucune sortie
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Fichiers concernés
|
||||
|
||||
```text
|
||||
include/views/application_message_dialog.h
|
||||
src/views/application_message_dialog.c
|
||||
src/core/application.c
|
||||
Makefile
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Commit attendu
|
||||
|
||||
```bash
|
||||
make clean
|
||||
make
|
||||
make test
|
||||
git diff --check
|
||||
git status --short
|
||||
```
|
||||
|
||||
```bash
|
||||
git add \
|
||||
include/views/application_message_dialog.h \
|
||||
src/views/application_message_dialog.c \
|
||||
src/core/application.c \
|
||||
Makefile
|
||||
```
|
||||
|
||||
```bash
|
||||
git diff --cached --stat
|
||||
git diff --cached
|
||||
```
|
||||
|
||||
```bash
|
||||
git commit -m "feat(ui): display application errors in GTK"
|
||||
git push
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Résultat attendu
|
||||
|
||||
Après ce ticket, l’utilisateur ne dépendra plus du terminal pour comprendre pourquoi une enquête n’a pas pu être créée ou ouverte.
|
||||
|
||||
Le ticket #034 pourra ensuite introduire le gestionnaire de tâches asynchrones sans mélanger la présentation des erreurs avec l’exécution des traitements longs.
|
||||
38
include/views/application_message_dialog.h
Normal file
38
include/views/application_message_dialog.h
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/******************************************************************************
|
||||
* @file application_message_dialog.h
|
||||
* @brief Fenêtre modale affichant un message à l'utilisateur.
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef LABFY_INVESTIGATION_APPLICATION_MESSAGE_DIALOG_H
|
||||
#define LABFY_INVESTIGATION_APPLICATION_MESSAGE_DIALOG_H
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
/**
|
||||
* @brief Nature du message affiché.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
APPLICATION_MESSAGE_DIALOG_ERROR,
|
||||
APPLICATION_MESSAGE_DIALOG_WARNING,
|
||||
APPLICATION_MESSAGE_DIALOG_INFORMATION
|
||||
} ApplicationMessageDialogType;
|
||||
|
||||
/**
|
||||
* @brief Affiche une fenêtre modale contenant un message.
|
||||
*
|
||||
* Les chaînes sont copiées par les widgets GTK.
|
||||
*
|
||||
* @param parent_window Fenêtre parente, ou NULL.
|
||||
* @param message_type Type de message.
|
||||
* @param title Titre de la fenêtre, ou NULL.
|
||||
* @param message Message principal, ou NULL.
|
||||
*/
|
||||
void application_message_dialog_present(
|
||||
GtkWindow *parent_window,
|
||||
ApplicationMessageDialogType message_type,
|
||||
const char *title,
|
||||
const char *message
|
||||
);
|
||||
|
||||
#endif
|
||||
Binary file not shown.
|
|
@ -14,6 +14,7 @@
|
|||
#include "views/create_investigation_dialog.h"
|
||||
#include "views/folder_dialog.h"
|
||||
#include "views/main_window.h"
|
||||
#include "views/application_message_dialog.h"
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
|
|
@ -65,6 +66,37 @@ struct Application
|
|||
InvestigationTreeModel *tree_model;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Affiche une erreur dans une fenêtre GTK.
|
||||
*
|
||||
* @param application Application propriétaire de la fenêtre principale.
|
||||
* @param title Titre compréhensible par l'utilisateur.
|
||||
* @param message Description de l'erreur.
|
||||
*/
|
||||
static void application_present_error(
|
||||
Application *application,
|
||||
const char *title,
|
||||
const char *message
|
||||
)
|
||||
{
|
||||
GtkWindow *parent_window = NULL;
|
||||
|
||||
if (application != NULL &&
|
||||
application->main_window != NULL)
|
||||
{
|
||||
parent_window = main_window_get_window(
|
||||
application->main_window
|
||||
);
|
||||
}
|
||||
|
||||
application_message_dialog_present(
|
||||
parent_window,
|
||||
APPLICATION_MESSAGE_DIALOG_ERROR,
|
||||
title,
|
||||
message
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Installe une nouvelle session et son arbre dans l'application.
|
||||
*
|
||||
|
|
@ -390,6 +422,14 @@ static void application_on_folder_selected(
|
|||
: "erreur inconnue"
|
||||
);
|
||||
|
||||
application_present_error(
|
||||
application,
|
||||
"Ouverture impossible",
|
||||
error != NULL
|
||||
? error->message
|
||||
: "L'enquête sélectionnée n'a pas pu être ouverte."
|
||||
);
|
||||
|
||||
g_clear_error(
|
||||
&error
|
||||
);
|
||||
|
|
@ -412,6 +452,8 @@ static void application_on_create_investigation(
|
|||
Application *application = user_data;
|
||||
|
||||
char *created_root_path = NULL;
|
||||
char *user_message = NULL;
|
||||
|
||||
GError *error = NULL;
|
||||
|
||||
if (application == NULL)
|
||||
|
|
@ -441,6 +483,25 @@ static void application_on_create_investigation(
|
|||
parent_directory
|
||||
);
|
||||
|
||||
user_message = g_strdup_printf(
|
||||
"L'enquête « %s » n'a pas pu être créée dans :\n"
|
||||
"%s\n\n"
|
||||
"Vérifiez que le dossier existe et que vous disposez "
|
||||
"des droits d'écriture nécessaires.",
|
||||
investigation_name,
|
||||
parent_directory
|
||||
);
|
||||
|
||||
application_present_error(
|
||||
application,
|
||||
"Création impossible",
|
||||
user_message
|
||||
);
|
||||
|
||||
g_free(
|
||||
user_message
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -459,11 +520,31 @@ static void application_on_create_investigation(
|
|||
: "erreur inconnue"
|
||||
);
|
||||
|
||||
user_message = g_strdup_printf(
|
||||
"L'enquête a bien été créée sur le disque dans :\n"
|
||||
"%s\n\n"
|
||||
"Labfy Investigation n'a cependant pas pu l'ouvrir :\n"
|
||||
"%s",
|
||||
created_root_path,
|
||||
error != NULL
|
||||
? error->message
|
||||
: "Aucun détail supplémentaire n'est disponible."
|
||||
);
|
||||
|
||||
application_present_error(
|
||||
application,
|
||||
"Enquête créée mais non ouverte",
|
||||
user_message
|
||||
);
|
||||
|
||||
g_free(
|
||||
user_message
|
||||
);
|
||||
|
||||
g_clear_error(
|
||||
&error
|
||||
);
|
||||
}
|
||||
|
||||
g_free(
|
||||
created_root_path
|
||||
);
|
||||
|
|
|
|||
337
src/views/application_message_dialog.c
Normal file
337
src/views/application_message_dialog.c
Normal file
|
|
@ -0,0 +1,337 @@
|
|||
/******************************************************************************
|
||||
* @file application_message_dialog.c
|
||||
* @brief Implémentation de la fenêtre modale de message.
|
||||
******************************************************************************/
|
||||
|
||||
#include "views/application_message_dialog.h"
|
||||
|
||||
/**
|
||||
* @brief Largeur minimale du dialogue.
|
||||
*/
|
||||
#define APPLICATION_MESSAGE_DIALOG_DEFAULT_WIDTH 480
|
||||
|
||||
/**
|
||||
* @brief Message utilisé lorsqu'aucun détail n'est disponible.
|
||||
*/
|
||||
#define APPLICATION_MESSAGE_DIALOG_DEFAULT_MESSAGE \
|
||||
"Aucun détail supplémentaire n'est disponible."
|
||||
|
||||
/**
|
||||
* @brief Retourne le libellé correspondant au type de message.
|
||||
*
|
||||
* @param message_type Type du message.
|
||||
*
|
||||
* @return Libellé statique du type.
|
||||
*/
|
||||
static const char *application_message_dialog_get_type_label(
|
||||
ApplicationMessageDialogType message_type
|
||||
)
|
||||
{
|
||||
switch (message_type)
|
||||
{
|
||||
case APPLICATION_MESSAGE_DIALOG_ERROR:
|
||||
return "Erreur";
|
||||
|
||||
case APPLICATION_MESSAGE_DIALOG_WARNING:
|
||||
return "Avertissement";
|
||||
|
||||
case APPLICATION_MESSAGE_DIALOG_INFORMATION:
|
||||
return "Information";
|
||||
|
||||
default:
|
||||
return "Information";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retourne le titre à utiliser pour le dialogue.
|
||||
*
|
||||
* @param message_type Type du message.
|
||||
* @param title Titre fourni par l'appelant.
|
||||
*
|
||||
* @return Titre fourni ou titre par défaut.
|
||||
*/
|
||||
static const char *application_message_dialog_get_safe_title(
|
||||
ApplicationMessageDialogType message_type,
|
||||
const char *title
|
||||
)
|
||||
{
|
||||
if (title != NULL &&
|
||||
title[0] != '\0')
|
||||
{
|
||||
return title;
|
||||
}
|
||||
|
||||
return application_message_dialog_get_type_label(
|
||||
message_type
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retourne un message toujours valide.
|
||||
*
|
||||
* @param message Message fourni par l'appelant.
|
||||
*
|
||||
* @return Message fourni ou message par défaut.
|
||||
*/
|
||||
static const char *application_message_dialog_get_safe_message(
|
||||
const char *message
|
||||
)
|
||||
{
|
||||
if (message != NULL &&
|
||||
message[0] != '\0')
|
||||
{
|
||||
return message;
|
||||
}
|
||||
|
||||
return APPLICATION_MESSAGE_DIALOG_DEFAULT_MESSAGE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Ferme le dialogue lorsque l'utilisateur clique sur le bouton.
|
||||
*
|
||||
* @param button Bouton ayant reçu le clic.
|
||||
* @param user_data Pointeur vers la fenêtre du dialogue.
|
||||
*/
|
||||
static void application_message_dialog_on_close_clicked(
|
||||
GtkButton *button,
|
||||
gpointer user_data
|
||||
)
|
||||
{
|
||||
GtkWindow *dialog_window = user_data;
|
||||
|
||||
(void) button;
|
||||
|
||||
if (dialog_window == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
gtk_window_destroy(
|
||||
dialog_window
|
||||
);
|
||||
}
|
||||
|
||||
void application_message_dialog_present(
|
||||
GtkWindow *parent_window,
|
||||
ApplicationMessageDialogType message_type,
|
||||
const char *title,
|
||||
const char *message
|
||||
)
|
||||
{
|
||||
GtkWindow *dialog_window = NULL;
|
||||
|
||||
GtkWidget *main_box = NULL;
|
||||
GtkWidget *type_label = NULL;
|
||||
GtkWidget *title_label = NULL;
|
||||
GtkWidget *message_label = NULL;
|
||||
GtkWidget *button_box = NULL;
|
||||
GtkWidget *close_button = NULL;
|
||||
|
||||
const char *safe_title = NULL;
|
||||
const char *safe_message = NULL;
|
||||
const char *type_text = NULL;
|
||||
|
||||
safe_title =
|
||||
application_message_dialog_get_safe_title(
|
||||
message_type,
|
||||
title
|
||||
);
|
||||
|
||||
safe_message =
|
||||
application_message_dialog_get_safe_message(
|
||||
message
|
||||
);
|
||||
|
||||
type_text =
|
||||
application_message_dialog_get_type_label(
|
||||
message_type
|
||||
);
|
||||
|
||||
dialog_window = GTK_WINDOW(
|
||||
gtk_window_new()
|
||||
);
|
||||
|
||||
gtk_window_set_title(
|
||||
dialog_window,
|
||||
safe_title
|
||||
);
|
||||
|
||||
gtk_window_set_default_size(
|
||||
dialog_window,
|
||||
APPLICATION_MESSAGE_DIALOG_DEFAULT_WIDTH,
|
||||
-1
|
||||
);
|
||||
|
||||
gtk_window_set_modal(
|
||||
dialog_window,
|
||||
TRUE
|
||||
);
|
||||
|
||||
gtk_window_set_destroy_with_parent(
|
||||
dialog_window,
|
||||
TRUE
|
||||
);
|
||||
|
||||
gtk_window_set_resizable(
|
||||
dialog_window,
|
||||
FALSE
|
||||
);
|
||||
|
||||
if (parent_window != NULL)
|
||||
{
|
||||
gtk_window_set_transient_for(
|
||||
dialog_window,
|
||||
parent_window
|
||||
);
|
||||
}
|
||||
|
||||
main_box = gtk_box_new(
|
||||
GTK_ORIENTATION_VERTICAL,
|
||||
12
|
||||
);
|
||||
|
||||
gtk_widget_set_margin_start(
|
||||
main_box,
|
||||
20
|
||||
);
|
||||
|
||||
gtk_widget_set_margin_end(
|
||||
main_box,
|
||||
20
|
||||
);
|
||||
|
||||
gtk_widget_set_margin_top(
|
||||
main_box,
|
||||
20
|
||||
);
|
||||
|
||||
gtk_widget_set_margin_bottom(
|
||||
main_box,
|
||||
20
|
||||
);
|
||||
|
||||
type_label = gtk_label_new(
|
||||
type_text
|
||||
);
|
||||
|
||||
gtk_widget_set_halign(
|
||||
type_label,
|
||||
GTK_ALIGN_START
|
||||
);
|
||||
|
||||
gtk_label_set_xalign(
|
||||
GTK_LABEL(type_label),
|
||||
0.0F
|
||||
);
|
||||
|
||||
title_label = gtk_label_new(
|
||||
safe_title
|
||||
);
|
||||
|
||||
gtk_widget_set_halign(
|
||||
title_label,
|
||||
GTK_ALIGN_START
|
||||
);
|
||||
|
||||
gtk_label_set_xalign(
|
||||
GTK_LABEL(title_label),
|
||||
0.0F
|
||||
);
|
||||
|
||||
gtk_label_set_wrap(
|
||||
GTK_LABEL(title_label),
|
||||
TRUE
|
||||
);
|
||||
|
||||
message_label = gtk_label_new(
|
||||
safe_message
|
||||
);
|
||||
|
||||
gtk_widget_set_halign(
|
||||
message_label,
|
||||
GTK_ALIGN_FILL
|
||||
);
|
||||
|
||||
gtk_label_set_xalign(
|
||||
GTK_LABEL(message_label),
|
||||
0.0F
|
||||
);
|
||||
|
||||
gtk_label_set_wrap(
|
||||
GTK_LABEL(message_label),
|
||||
TRUE
|
||||
);
|
||||
|
||||
gtk_label_set_wrap_mode(
|
||||
GTK_LABEL(message_label),
|
||||
PANGO_WRAP_WORD_CHAR
|
||||
);
|
||||
|
||||
gtk_label_set_selectable(
|
||||
GTK_LABEL(message_label),
|
||||
TRUE
|
||||
);
|
||||
|
||||
gtk_label_set_max_width_chars(
|
||||
GTK_LABEL(message_label),
|
||||
70
|
||||
);
|
||||
|
||||
button_box = gtk_box_new(
|
||||
GTK_ORIENTATION_HORIZONTAL,
|
||||
0
|
||||
);
|
||||
|
||||
gtk_widget_set_halign(
|
||||
button_box,
|
||||
GTK_ALIGN_END
|
||||
);
|
||||
|
||||
close_button = gtk_button_new_with_label(
|
||||
"Fermer"
|
||||
);
|
||||
|
||||
g_signal_connect(
|
||||
close_button,
|
||||
"clicked",
|
||||
G_CALLBACK(
|
||||
application_message_dialog_on_close_clicked
|
||||
),
|
||||
dialog_window
|
||||
);
|
||||
|
||||
gtk_box_append(
|
||||
GTK_BOX(button_box),
|
||||
close_button
|
||||
);
|
||||
|
||||
gtk_box_append(
|
||||
GTK_BOX(main_box),
|
||||
type_label
|
||||
);
|
||||
|
||||
gtk_box_append(
|
||||
GTK_BOX(main_box),
|
||||
title_label
|
||||
);
|
||||
|
||||
gtk_box_append(
|
||||
GTK_BOX(main_box),
|
||||
message_label
|
||||
);
|
||||
|
||||
gtk_box_append(
|
||||
GTK_BOX(main_box),
|
||||
button_box
|
||||
);
|
||||
|
||||
gtk_window_set_child(
|
||||
dialog_window,
|
||||
main_box
|
||||
);
|
||||
|
||||
gtk_window_present(
|
||||
dialog_window
|
||||
);
|
||||
}
|
||||
BIN
tests/test_database
Executable file
BIN
tests/test_database
Executable file
Binary file not shown.
BIN
tests/test_error
Executable file
BIN
tests/test_error
Executable file
Binary file not shown.
BIN
tests/test_investigation_dao
Executable file
BIN
tests/test_investigation_dao
Executable file
Binary file not shown.
BIN
tests/test_investigation_node
Executable file
BIN
tests/test_investigation_node
Executable file
Binary file not shown.
BIN
tests/test_investigation_project
Executable file
BIN
tests/test_investigation_project
Executable file
Binary file not shown.
BIN
tests/test_investigation_record
Executable file
BIN
tests/test_investigation_record
Executable file
Binary file not shown.
BIN
tests/test_investigation_session
Executable file
BIN
tests/test_investigation_session
Executable file
Binary file not shown.
BIN
tests/test_investigation_tree_builder
Executable file
BIN
tests/test_investigation_tree_builder
Executable file
Binary file not shown.
BIN
tests/test_investigation_tree_model
Executable file
BIN
tests/test_investigation_tree_model
Executable file
Binary file not shown.
BIN
tests/test_statement
Executable file
BIN
tests/test_statement
Executable file
Binary file not shown.
BIN
tests/test_transaction
Executable file
BIN
tests/test_transaction
Executable file
Binary file not shown.
Loading…
Reference in a new issue