# Message Board (C)

Un programme simple utilisant WebKitGTK+ et DOM.
Shaun McCance
shaunm@gnome.org 2010

# Visionneur de messages

      Dans ce tutoriel, vous allez apprendre :

        comment afficher une page Web avec Webkit,
        comment manipuler le contenu d'une page Web en utilisant les fonctions DOM de WebKit.

      Ce tutoriel suppose que vous soyez familier avec le langage de programmation C et que vous possédiez les bases nécessaires à la compréhension de GTK+, y compris comment générer et placer des éléments graphiques et comment associer des fonctions de rappel à des signaux. Consultez  pour apprendre les bases de GTK+.

# Création d'un projet dans Anjuta

La plateforme GNOME inclut WebKitGTK+, bâti sur le puissant environnement HTML WebKit. WebKit est utilisé partout dans GNOME, pas seulement pour afficher des pages Web sur internet, mais aussi pour créer de riches interfaces utilisateur qui peuvent être facilement stylisées par CSS.

Dans ce tutoriel vous allez créer un simple visionneur de messages en utilisant Webkit. Ce visionneur de messages vous permettra de saisir du texte et de l'ajouter à une liste de messages en HTML. Avant de débuter, vous devez créer un projet dans Anjuta.

Dans Anjuta, cliquez sur Fichier Nouveau Projet pour ouvrir l'assistant de nouveau projet.

Sélectionnez GTK+ (simple) dans l'onglet C et cliquez sur Continuer.

Renseignez les champs de la page Informations générales. Mettez message-board comme nom de projet et cliquez sur Continuer.

Désactivez l'option Utiliser GtkBuilder pour l'interface utilisateur car ce tutoriel construit l'interface manuellement.

Vous devez indiquer à Anjuta que vous utilisez WebKitGTK+ dans ce projet. Sur la page Options du projet, sélectionnez l'option Configuration des paquets externes. Cliquez sur Continuer. Sur la page Configuration des paquets externes, cochez webkitgtk-3.0.

Une fois terminé avec l'assistant de nouveau projet, ouvrez le fichier src/main.c depuis l'onglet Projet ou l'onglet Fichiers. Anjuta l'a rempli avec du code GTK+ basique à partir d'un modèle. Comme vous êtes en train de créer un projet WebKit, il vous faut avant tout y inclure les entêtes WebKit. Après la ligne contenant `gtk/gtk.h`, ajoutez cette ligne :

#include <webkit/webkit.h>

Vérifiez que tout fonctionne en construisant ce que vous avez déjà. Cliquez sur Construire Construire le projet, ou appuyez sur Maj F7. À la première construction, il vous est demandé quelques options de configuration. Acceptez celles qui vous sont proposées par défaut, et cliquez sur Exécuter.

Vous pouvez maintenant lancer le programme. Cliquez sur Exécuter Exécuter, ou appuyez sur F3. Une fenêtre vide s'ouvre.

# Mise en forme de votre fenêtre et de votre affichage Web

Maintenant que vous savez afficher une fenêtre, vous pouvez commencer à travailler avec WebKit. Pour les besoins de ce tutoriel, vous allez créer un champ texte ainsi qu'une vue Web et les placer dans une fenêtre. Trouvez la fonction `create_window` et remplacez-la par ce qui suit :

static GtkWidget* create_window (void) { GtkWidget *window, *box, *scroll, *view, *entry;

window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_default_size (GTK_WINDOW (window), 400, 400); gtk_window_set_title (GTK_WINDOW (window), "Message Board"); g_signal_connect (window, "delete-event", G_CALLBACK (gtk_main_quit), NULL);

box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); gtk_container_set_border_width (GTK_CONTAINER (box), 6); gtk_container_add (GTK_CONTAINER (window), box);

entry = gtk_entry_new (); gtk_box_pack_start (GTK_BOX (box), entry, FALSE, FALSE, 0);

scroll = gtk_scrolled_window_new (NULL, NULL); g_object_set (scroll, "shadow-type", GTK_SHADOW_IN, NULL); gtk_box_pack_start (GTK_BOX (box), scroll, TRUE, TRUE, 0);

view = webkit_web_view_new (); gtk_container_add (GTK_CONTAINER (scroll), view); webkit_web_view_load_string (WEBKIT_WEB_VIEW (view), "<html><body></body></html>", "text/html", "UTF-8", NULL);

gtk_widget_show_all (GTK_WIDGET (box)); return window; }

Commencez par générer un objet `GtkWindow` et donnez-lui un titre et des dimensions par défaut. Il vous faut aussi connecter la fonction `gtk_main_quit` au signal `delete-event`. Le signal `delete-event` est émis lors de la fermeture de la fenêtre. La fonction `gtk_main_quit` fait partie de GTK et elle ferme l'application.

Ensuite créez une boîte verticale et ajoutez-la à la fenêtre. Une fenêtre ne peut contenir qu'un seul élément graphique enfant, il faut donc utiliser une boîte pour en ajouter plusieurs. Le second argument de `gtk_box_new` définit la largeur des marges (en pixels) entre chaque enfant et la ligne suivante ajoute une bordure de six pixels autour du tout.

Il vous faut ensuite générer un objet `GtkEntry` et le placer dans la boîte. Les troisième et quatrième arguments de `gtk_box_pack_start` précisent que l'entrée ne doit pas occuper plus d'espace que la boîte n'en dispose. Le quatrième argument représente la largeur des marges souhaitée autour de l'entrée. Dans ce cas, définissez la largeur des marges à zéro, car vous autorisez la boîte à remplir tout l'espace libre.

Avant d'ajouter une vue Web, vous devez créer une fenêtre avec barres de défilement pour la contenir. Cette fenêtre fera apparaître des barres de défilement à droite et en bas si nécessaire, évitant ainsi à votre vue Web de remplir tout votre écran. Cette fois, transmettez `TRUE` et `TRUE` à `gtk_box_pack_start` pour autoriser la fenêtre de défilement (et donc la vue Web) à occuper tout l'espace supplémentaire disponible dans la boîte.

Enfin, vous créez un `WebKitWebView` et l'ajoutez à la fenêtre de défilement. Chargez ensuite une page HTML basique dans la vue Web en appelant `webkit_web_view_load_string` avec les arguments suivants :

## `WEBKIT_WEB_VIEW (view)`

La vue en elle-même. Comme `view` est saisi sous forme de `GtkWidget*`, il vous faut utiliser `WEBKIT_WEB_VIEW` pour intégrer l'objet.

## `"<html><body></body></html>"`

Le plus simple fichier HTML que vous pourriez écrire.

## `« text/html »`

Le type MIME du contenu que vous avez fourni. Dans ce cas vous utilisez du HTML simple.

## `« UTF-8 »`

Le type de codage de caractères du contenu que vous avez fourni. Bien que vous n'ayez probablement utilisé que des caractères ASCII, c'est une bonne pratique de spécifier UTF-8, car c'est le codage par défaut utilisé dans toute la plateforme GNOME.

## `NULL`

L'URI de base. Vous n'en avez pas besoin dans cet exemple, mais vous pourriez fournir un URI file: pour ajouter des images ou d'autres choses pour lesquelles vous devrez utiliser les références d'URI correspondantes.

Chaque fois que vous ajoutez un élément graphique, vous devez appeler `gtk_widget_show` pour le rendre visible. Si vous appelez `gtk_widget_show_all` sur un conteneur comme une `GtkBox`, GTK+ montre automatiquement tous les éléments graphiques qui sont à l'intérieur quelque soit leur altitude (depth). Parfois, vous ne voulez pas appeler `gtk_widget_show_all`, comme quand vous souhaitez faire apparaître ou disparaître dynamiquement des éléments graphiques en réponse à des événements.

Enfin, vous devez appeler `gtk_widget_show_all` sur la boîte. Sinon, aucun des éléments graphiques créés n'est visible (la fenêtre est affichée dans la fonction `main` avec `gtk_widget_show`).

Construisez et exécutez le visionneur de messages une nouvelle fois. Vous devez voir apparaître une fenêtre contenant du texte et une vue Web. Pour l'instant, il ne se passe encore rien, car le champ texte et la vue Web ne savent rien l'un sur l'autre.

# Signaux de connexion

Maintenant, vous allez faire en sorte que le visionneur de messages fasse quelque chose quand vous saisissez du texte dans le champ de texte. Pour ce faire, connectez une fonction de rappel au signal `activate` de l'élément `entry`. GTK+ émet le signal `activate` à chaque fois que l'utilisateur appuie sur la touche Entrée dans le champ. Ajoutez ceci dans `create_window`, n'importe où après la définition de `entry` et `view` :

g_signal_connect (entry, "activate", G_CALLBACK (entry_activate_cb), view);

Vous devez ensuite définir `entry_activate_cb` de la façon suivante, n'importe où au-dessus de `create_window` :

static void entry_activate_cb (GtkEntry *entry, WebKitWebView *view) { WebKitDOMDocument *document; WebKitDOMElement *body, *div;

document = webkit_web_view_get_dom_document (view); body = webkit_dom_document_query_selector (document, "body", NULL); div = webkit_dom_document_create_element (document, "div", NULL); webkit_dom_node_set_text_content (WEBKIT_DOM_NODE (div), gtk_entry_get_text (entry), NULL); webkit_dom_node_append_child (WEBKIT_DOM_NODE (body), WEBKIT_DOM_NODE (div), NULL); gtk_entry_set_text (entry, ""); }

La première chose à faire est d'obtenir un objet `WebKitDOMDocument` qui représente le document HTML affiché dans `view`. Les classes et les méthodes DOM de WebKit vous permettent d'examiner et de manipuler le document HTML et fonctionnent d'une façon très similaire aux API DOM de JavaScript que vous connaissez peut-être déjà.

Une fois que vous avez le document, il vous faut obtenir l'élément `body` pour pouvoir y ajouter des éléments `div`. La fonction `webkit_dom_document_query_selector` vous permet de trouver un élément dans le document en utilisant des sélecteurs CSS. Ceci vous évite de fastidieux aller-retour à travers le document.

shaunm

FIXME : est-ce vrai ? est-ce que « query_selector » prend en charge le CSS, le CSSish, ou quoi ?

Puis, vous créez un nouveau bloc `div` pour contenir le message. Chaque élément que vous créez doit être attaché à un document ; ainsi la fonction pour créer un élément prend le `WebKitDOMDocument` comme premier argument. Vous définissez ensuite le contenu texte de l'élément au contenu du champ texte. Étant donné que `gtk_entry_get_text` retourne un `const gchar*`, vous n'êtes pas obligé de libérer le résultat.

shaunm

Not passing the GError**, but we should give it a quick mention and link to somewhere that explains how GError-handling works.

Enfin, vous devez ajouter le nouveau bloc `div` après le bloc `body` et effacer le champ texte pour pouvoir y saisir quelque chose d'autre. Construisez et exécutez le programme à nouveau et testez-le.

# Le rendre plus joli avec du CSS

À ce stade, votre programme est tout à fait fonctionnel, mais le résultat pas très joli. Vous pouvez styliser votre affichage avec CSS, comme vous le feriez avec n'importe quelle autre page HTML. Il y a plusieurs façons d'attacher du CSS à votre page : vous pouvez l'ajouter au document HTML initial, vous pouvez l'incorporer dans l'attribut `style` des blocs `div`, ou encore le construire en le programmant à partir des API DOM.

Dans ce tutoriel, vous allez ajouter du CSS en utilisant la propriété `user-stylesheet-uri` de l'objet `WebKitWebSetting` attachée à votre vue Web. Pour une application plus élaborée, il serait préférable d'enregistrer et de charger votre fichier HTML. Séparer les informations de style du vrai HTML conduit à pouvoir complètement changer de style dans votre application sans toucher aux fichiers des utilisateurs. Normalement, il faudrait juste installer un fichier avec votre application, mais pour cet exemple, et pour que tout soit dans un seul fichier, nous allons utiliser une astuce nommée un URI de données. Commencez par définir le CSS dans une chaîne de type « static » et placez-la près du début du fichier.

static const guchar CSS[] = "body { margin: 0; padding: 0; }\n" "div { " " -webkit-border-radius: 2px;" " background: -webkit-gradient(linear, 0% 100%, 0% 0%," " from(#f1f1f1), to(white));" " border: solid 1px #c6c6c6;" " -webkit-box-shadow: 0px 0px 2px #c6c6c6;" " margin: 12px; padding: 6px;" "}";

Dans cet exemple, vous n'avez que des blocs `div` dans un élément `body`. Si vous créez un document HTML plus complexe, vous pouvez utiliser le code CSS dont vous avez besoin. En fait, si vous vous sentez à l'aise avec le CSS, vous pouvez essayer de modifier cette partie pour en faire ce qu'il vous plaît.

Pour appliquer le CSS, placez `user-stylesheet-uri` n'importe où dans la fonction `create_window` après la définition de `view`.

shaunm

g_base64_encode has bad args

tmp = g_base64_encode (CSS, strlen((gchar *) CSS)); css = g_strconcat ("data:text/css;charset=utf-8;base64,", tmp, NULL); g_object_set (webkit_web_view_get_settings (WEBKIT_WEB_VIEW (view)), "user-stylesheet-uri", css, NULL); g_free (css); g_free (tmp);

Assurez-vous aussi d'ajouter les déclarations pour les variables `tmp` et `css` en haut de `create_window`.

gchar *tmp, *css;

Un URI de données commence par data:, quelques informations sur le type de contenu et par la façon dont les données sont codées. Les vraies données suivent après une virgule et dans notre cas sont codées en Base64. Contrairement à d'autres protocoles d'URI, comme http:, ftp: et file:, l'URI data: ne spécifie pas où trouver le fichier à charger. Il fournit par contre le contenu complet du fichier.

Le programme ci-dessus code d'abord vos définitions CSS en Base64, puis les combine dans une chaîne constante pour générer un URI de données. La fonction `g_strconcat` peut prendre un nombre infini d'arguments de type chaîne de caractères et les assemble. Par conséquent il vous faut mettre `NULL` comme dernier argument pour lui indiquer où s'arrêter. N'oubliez pas de libérer ces chaînes temporaires après avoir défini la propriété de la feuille de style.

Construisez et exécutez à nouveau le programme. Il doit maintenant fonctionner exactement de la même façon qu'à la fin du chapitre précédent, sauf que cette fois, les messages sont joliment stylisés avec une bordure et un gradient discret en arrière-plan.

# En savoir plus

Le but de ce tutoriel était de vous montrer comment créer une application basique avec GTK+ et WebKit, et également d'afficher un document et de manipuler son contenu. Pour créer une application réelle, vous voudrez probablement en faire un peu plus. Essayez d'ajouter vous-même d'autres fonctionnalités. Voici quelques idées :

Si vous êtes à l'aise avec CSS, essayez de modifier le style d'affichage du message. Débuter avec le CSS est facile, mais en allant plus loin ça devient un objet plus puissant. Vous allez trouver plein de tutoriels CSS sur internet, et tous sont adaptables à cette application.

Pour l'instant, vous perdez tous vos messages chaque fois que vous fermez le visionneur de messages. Essayez d'enregistrer le contenu HTML après chaque message et d'ouvrir le fichier enregistré (s'il existe) au démarrage.

shaunm

Lien vers une méthode pour obtenir de l'HTML de DOM vers les API GIO.

Si vous conservez vos messages pendant longtemps, un jour vous allez vous demander quand ils ont été postés. Ajoutez un horodatage à chaque message posté. Créez quelques blocs `div` enfants supplémentaires avec des classes différentes que vous pouvez styliser dans la feuille CSS.

shaunm

Lien vers « strftime » où autre

Ce programme conserve les messages pour toujours. Réfléchissez à une façon pour autoriser l'utilisateur à supprimer des messages. Peut-être en les faisant disparaître automatiquement lorsqu'ils sont trop vieux ou quand il y a un nombre défini de messages devant eux. Ou alors en ajoutant un lien à chaque message pour pouvoir le supprimer. Ou encore en redéfinissant le menu contextuel quand vous faites un clic-droit sur un message. Ces fonctions impliquent que vous exploriez davantage encore les API DOM de WebKit.