<?xml version="1.0" encoding="utf-8"?>
<page xmlns="http://projectmallard.org/1.0/" xmlns:its="http://www.w3.org/2005/11/its" xmlns:xi="http://www.w3.org/2001/XInclude" type="guide" style="task" id="treeview_simple_liststore.js" xml:lang="fr">
  <info>
  <title type="text">TreeView with ListStore (JavaScript)</title>
    <link type="guide" xref="beginner.js#treeview"/>
    <link type="seealso" xref="GtkApplicationWindow.js"/>
    <link type="seealso" xref="grid.js"/>
    <link type="seealso" xref="label.js"/>
    <revision version="0.1" date="2012-07-04" status="draft"/>

    <credit type="author copyright">
      <name>Taryn Fox</name>
      <email its:translate="no">jewelfox@fursona.net</email>
      <years>2012</years>
    </credit>

    <desc>Un élément graphique qui affiche une liste séparée d'éléments</desc>
  </info>

  <title>TreeView avec ListStore</title>
  <media type="image" mime="image/png" src="media/treeview_simple_liststore_penguins.png"/>
  <p>Un TreeView est une fenêtre affichant le contenu soit d'un ListStore, soit d'un TreeStore. Un ListStore peut être comparé à une feuille de calcul : une liste « plate », en deux dimensions, contenant des données réparties sur des lignes et dans des colonnes. Alors qu'un TreeStore peut déployer ses branches dans différentes directions, à la manière d'un arbre. Dans cet exemple, nous créons un TreeView qui affiche le contenu d'un ListStore contenant des noms et des numéros de téléphone (fictifs) et nous le paramétrons pour que l'<link xref="label.js">étiquette</link> en bas de la fenêtre affiche plus d'informations sur le nom qui a été cliqué.</p>
  <p>Un TreeView n'est pas qu'un simple élément graphique, il contient aussi un certain nombre d'autres plus petits :</p>
  <list>
    <item><p>Les éléments graphiques TreeViewColumn affichent chaque colonne d'informations contenue dans le ListStore. Chacune d'elle a un nom qui peut être affiché dans l'en-tête de la colonne, comme montré dans la capture d'écran.</p></item>
    <item><p>Les éléments graphiques CellRenderer sont « empilés » dans chaque TreeViewColumn et contiennent les instructions sur la manière d'afficher chaque « cellule » individuelle ou chaque élément du ListStore. Il en existe beaucoup de types différents, y compris le CellRendererText utilisé ici et le CellRendererPixbuf qui affiche une image « pixel buffer ».</p></item>
  </list>
  <p>Pour finir, nous allons utiliser un objet nommé TreeIter, qui n'est pas à proprement parler un élément graphique, mais plutôt un curseur invisible qui pointe vers une ligne horizontale du ListStore. Si vous cliquez par exemple sur un nom de la liste téléphonique, cela génère un TreeIter qui pointe vers la ligne sélectionnée et qui sert à indiquer au ListStore quelles informations supplémentaires l'étiquette doit afficher.</p>
  <note><p>Le TreeView est probablement l'élément graphique Gtk le plus compliqué, de par le nombre d'éléments qu'il contient et à cause de la façon dont ils sont imbriqués pour travailler ensemble. Prenez le temps d'apprendre et son fonctionnement en faisant des essais, ou alors essayez de commencer par quelque chose de plus facile si vous n'y arrivez pas.</p></note>
    <links type="section"/>

  <section id="imports">
    <title>Bibliothèques à importer</title>
    <code mime="application/javascript"><![CDATA[
#!/usr/bin/gjs

const GObject = imports.gi.GObject;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Pango = imports.gi.Pango;
]]></code>
    <p>Ce sont les bibliothèques que nous devons importer pour faire fonctionner cette application. N'oubliez pas que la ligne qui informe GNOME que nous allons utiliser Gjs doit toujours se trouver au début.</p>
  </section>

  <section id="applicationwindow">
    <title>Création de la fenêtre de l'application</title>
    <code mime="application/javascript"><![CDATA[
const TreeViewExample = new Lang.Class({
    Name: 'TreeView Example with Simple ListStore',

    // Create the application itself
    _init: function() {
        this.application = new Gtk.Application({
            application_id: 'org.example.jstreeviewsimpleliststore'
        });

    // Connect 'activate' and 'startup' signals to the callback functions
    this.application.connect('activate', Lang.bind(this, this._onActivate));
    this.application.connect('startup', Lang.bind(this, this._onStartup));
    },

    // Callback function for 'activate' signal presents window when active
    _onActivate: function() {
        this._window.present();
    },

    // Callback function for 'startup' signal builds the UI
    _onStartup: function() {
        this._buildUI ();
    },
]]></code>
    <p>Tout le code utilisé pour cet exemple va dans la classe TreeViewExample. Le code ci-dessus crée une <link href="http://www.roojs.com/seed/gir-1.2-gtk-3.0/gjs/Gtk.Application.html">Gtk.Application</link> pour nos éléments graphiques et la fenêtre qui les contient.</p>
    <code mime="application/javascript"><![CDATA[
    // Build the application's UI
    _buildUI: function() {

        // Create the application window
        this._window = new Gtk.ApplicationWindow({
            application: this.application,
            window_position: Gtk.WindowPosition.CENTER,
            default_height: 250,
            default_width: 100,
            border_width: 20,
            title: "My Phone Book"});
]]></code>
    <p>La fonction _buildUI est l'endroit où nous mettons tout le code nécessaire à la création de l'interface utilisateur de l'application. La première étape consiste à créer une <link xref="GtkApplicationWindow.js">Gtk.ApplicationWindow</link> pour y mettre tous nos éléments graphiques.</p>
  </section>

  <section id="liststore">
    <title>Création du ListStore</title>
    <code mime="application/javascript"><![CDATA[
        // Create the underlying liststore for the phonebook
        this._listStore = new Gtk.ListStore ();
        this._listStore.set_column_types ([
            GObject.TYPE_STRING,
            GObject.TYPE_STRING,
            GObject.TYPE_STRING,
            GObject.TYPE_STRING]);
]]></code>
    <p>Créons en premier le ListStore comme nous l'aurions fait pour n'importe quel élément graphique. Appelons ensuite sa méthode set_column_types et passons lui un tableau de types de données GObject (nous aurions pu placer tous les types sur une seule et même ligne, mais pour faciliter la lecture, nous les séparons).</p>
    <p>Les types de données GObject que vous pouvez utiliser incluent :</p>
    <list>
      <item><p><file>GObject.TYPE_BOOLEAN</file> -- true ou false</p></item>
      <item><p><file>GObject.TYPE_FLOAT</file> -- un nombre à virgule flottante</p></item>
      <item><p><file>GObject.TYPE_STRING</file> -- une chaîne de caractères (lettres ou chiffres)</p></item>
      <item><p><file>gtk.gdk.Pixbuf</file> -- une image</p></item>
    </list>
    <p>Dans ce cas, nous créons un ListStore de quatre colonnes contenant chacune des valeurs de type chaînes de caractères.</p>
    <note><p>Pour pouvoir utiliser les types GObject, vous devez placer la ligne <file>const GObject = imports.gi.GObject;</file> au début du code de votre application, comme nous l'avons fait dans cet exemple.</p></note>

    <code mime="application/javascript"><![CDATA[
        // Data to go in the phonebook
        this.phonebook =
        let phonebook =
            [{ name: "Jurg", surname: "Billeter", phone: "555-0123",
                description: "A friendly person."},
             { name: "Johannes", surname: "Schmid", phone: "555-1234",
                description: "Easy phone number to remember."},
             { name: "Julita", surname: "Inca", phone: "555-2345",
                description: "Another friendly person."},
             { name: "Javier", surname: "Jardon", phone: "555-3456",
                description: "Bring fish for his penguins."},
             { name: "Jason", surname: "Clinton", phone: "555-4567",
                description: "His cake's not a lie."},
             { name: "Random J.", surname: "Hacker", phone: "555-5678",
                description: "Very random!"}];
]]></code>
    <p>Voici les informations contenues dans le ListStore. C'est un assortiment d'objets, chacun correspondant à une entrée de notre répertoire téléphonique.</p>
    <p>Notez que le TreeView de la capture d'écran n'affiche pas pour l'instant les données des propriétés « description ». Elles s'afficheront dans l'étiquette du dessous à chaque clic sur une ligne. C'est parce qu'un TreeView et un ListStore sont deux choses bien distinctes et qu'un TreeView peut afficher tout ou une partie du contenu d'un ListStore de différentes façons. Vous pouvez même posséder plusieurs éléments graphiques affichant des éléments d'un même ListStore, comme l'étiquette de notre exemple ou bien encore un second TreeView.</p>

    <code mime="application/javascript"><![CDATA[
        for (i = 0; i < phonebook.length; i++ ) {
            let contact = phonebook [i];
            this._listStore.set (this._listStore.append(), [0, 1, 2, 3],
                [contact.name, contact.surname, contact.phone, contact.description]);
        }
]]></code>
    <p>Cette boucle <file>for</file> place les chaînes de caractères de notre répertoire téléphonique dans notre ListStore dans l'ordre. Pour ce faire, nous passons à la méthode set du ListStore le curseur qui pointe vers la bonne ligne, un tableau qui indique quelles colonnes nous voulons définir et un tableau qui contient les données que nous voulons y mettre.</p>
    <p>La méthode <file>append</file> du ListStore ajoute une ligne horizontale à celui-ci (au début, il n'y en a pas) et renvoie un TreeIter pointant vers cette ligne comme un curseur. Donc, en transmettant <file>this._listStore.append()</file> au ListStore comme propriété, nous créons une nouvelle ligne en indiquant en même temps à la méthode <file>set</file> à quelle ligne elle doit attribuer les données.</p>

  </section>

  <section id="treeview">
    <title>Création du TreeView</title>
    <code mime="application/javascript"><![CDATA[
        // Create the treeview
        this._treeView = new Gtk.TreeView ({
            expand: true,
            model: this._listStore });
]]></code>
    <p>Ici, nous créons un élément graphique TreeView de base, qui s'étend à la fois horizontalement et verticalement pour utiliser autant d'espace que nécessaire. Nous le paramétrons pour utiliser le ListStore que nous avons créé comme étant son « modèle » et d'où proviendront les éléments qu'il affichera.</p>

    <code mime="application/javascript"><![CDATA[
        // Create the columns for the address book
        let firstName = new Gtk.TreeViewColumn ({ title: "First Name" });
        let lastName = new Gtk.TreeViewColumn ({ title: "Last Name" });
        let phone = new Gtk.TreeViewColumn ({ title: "Phone Number" });
]]></code>
    <p>Nous créons maintenant chacun des TreeViewColumns vertical qui s'afficheront dans notre TreeView. Comme le montre la capture d'écran, chaque titre se situe respectivement en haut de sa colonne.</p>

    <code mime="application/javascript"><![CDATA[
        // Create a cell renderer for when bold text is needed
        let bold = new Gtk.CellRendererText ({
            weight: Pango.Weight.BOLD });

        // Create a cell renderer for normal text
        let normal = new Gtk.CellRendererText ();

        // Pack the cell renderers into the columns
        firstName.pack_start (bold, true);
        lastName.pack_start (normal, true);
        phone.pack_start (normal, true);
]]></code>
    <p>Ici, nous créons les CellRenderers que nous allons utiliser pour afficher le texte contenu dans notre ListStore et nous les positionnons dans les TreeViewColumns. Chaque CellRendererText est utilisé pour toutes les entrées de cette colonne. Notre CellRendererText normal (normal) ne génère que du texte brut, alors que celui en gras (bold) utilise du texte plus gras. Nous le mettons dans la première en-tête de colonne et nous indiquons aux deux autres d'utiliser des copies du normal. L'argument « true » utilisé comme second paramètre de la méthode <file>pack_start</file> lui indique d'agrandir les cellules quand cela est possible, au lieu de les laisser compactes.</p>
    <note><p><link href="http://www.pygtk.org/docs/pygtk/pango-constants.html">Voici iciune liste</link> d'autres propriétés de texte que vous pouvez utiliser. Pour pouvoir utiliser les constantes Pango, assurez-vous d'ajouter la ligne <file>const Pango = imports.gi.Pango;</file> au début de votre code comme nous l'avons fait.</p></note>

    <code mime="application/javascript"><![CDATA[
        firstName.add_attribute (bold, "text", 0);
        lastName.add_attribute (normal, "text", 1);
        phone.add_attribute (normal, "text", 2);

        // Insert the columns into the treeview
        this._treeView.insert_column (firstName, 0);
        this._treeView.insert_column (lastName, 1);
        this._treeView.insert_column (phone, 2);
]]></code>
    <p>Après avoir placé les CellRenderers dans les TreeViewColumns, nous utilisons la méthode <file>add_attribute</file> pour indiquer à chaque colonne d'extraire du modèle le texte pour lequel notre TreeView est programmée ; dans ce cas, le ListStore contenant le répertoire téléphonique.</p>
    <list>
      <item><p>Le premier paramètre indique quel CellRenderer nous allons utiliser pour restituer ce que nous extrayons.</p></item>
      <item><p>Le second paramètre indique le type d'information que nous voulons extraire. Dans ce cas, nous lui indiquons que nous voulons du texte.</p></item>
      <item><p>Le troisième paramètre indique de quelle colonne du ListStore nous voulons extraire cette information.</p></item>
    </list>
    <p>Après avoir paramétré ceci, nous utilisons la méthode <file>insert_column</file> pour trier le contenu de nos TreeViewColumns. Notre TreeView est à présent terminée.</p>
    <note><p>Normalement, vous devriez utiliser une boucle pour initialiser votre TreeView, mais dans cet exemple, nous avons détaillé étape par étape ce qui ce passe afin de mieux comprendre.</p></note>
  </section>

  <section id="ui">
    <title>Construction du reste de l'interface utilisateur</title>
    <code mime="application/javascript"><![CDATA[
        // Create the label that shows details for the name you select
        this._label = new Gtk.Label ({ label: "" });

        // Get which item is selected
        this.selection = this._treeView.get_selection();

        // When something new is selected, call _on_changed
        this.selection.connect ('changed', Lang.bind (this, this._onSelectionChanged));
]]></code>
    <p>La méthode <file>get_selection</file> du TreeView retourne un objet appelé un TreeSelection. Un TreeSelection est comme un TreeIter, tout simplement un curseur qui pointe vers une ligne particulière, à la différence que celui-ci pointe vers la ligne qui est marqué visuellement comme sélectionnée.</p>
    <p>Une fois obtenu le TreeSelection qui va avec notre TreeView, nous lui demandons de nous indiquer vers quelle ligne il pointe quand il est modifié. Nous obtenons cela en connectant le signal <file>changed</file> à la fonction _onSelectionChanged que nous avons programmée. Cette fonction modifie le texte affiché par l'étiquette que nous venons de créer.</p>

    <code mime="application/javascript"><![CDATA[
        // Create a grid to organize everything in
        this._grid = new Gtk.Grid;

        // Attach the treeview and label to the grid
        this._grid.attach (this._treeView, 0, 0, 1, 1);
        this._grid.attach (this._label, 0, 1, 1, 1);

        // Add the grid to the window
        this._window.add (this._grid);

        // Show the window and all child widgets
        this._window.show_all();
    },
]]></code>
    <p>Ceci fait, nous créons une <link xref="grid.js">grille (grid)</link> pour y placer le tout, puis nous l'ajoutons à notre fenêtre et demandons à cette fenêtre de s'afficher avec son contenu.</p>
  </section>

  <section id="function">
    <title>Fonction prenant en charge la modification de la sélection</title>

    <code mime="application/javascript"><![CDATA[
    _onSelectionChanged: function () {

        // Grab a treeiter pointing to the current selection
        let [ isSelected, model, iter ] = this.selection.get_selected();

        // Set the label to read off the values stored in the current selection
        this._label.set_label ("\n" +
            this._listStore.get_value (iter, 0) + " " +
            this._listStore.get_value (iter, 1) + " " +
            this._listStore.get_value (iter, 2) + "\n" +
            this._listStore.get_value (iter, 3));

    }

});
]]></code>
    <p>La ligne de code utilisant l'instruction « let » est quelque peu compliquée, mais c'est néanmoins la meilleure façon d'obtenir un TreeIter qui pointe vers la même ligne que notre TreeSelection. Elle doit créer quelques autres références d'objets, mais le seul qui nous intéresse est l'<file>iter</file>.</p>
    <p>Ceci fait, nous appelons la fonction <file>set_label</file> de l'étiquette et nous utilisons la fonction <file>get_value</file> du ListStore autant de fois que nécessaire pour la remplir de toutes les données que nous souhaitons y mettre. Ses paramètres sont un TreeIter pointant vers la ligne dont nous voulons extraire les données et la colonne.</p>
    <p>Ici, nous voulons obtenir les données des quatre colonnes, y compris celles qui sont « masquées » et qui ne font pas partie du TreeView. De cette façon, nous pouvons utiliser notre étiquette pour afficher les chaînes de caractères qui sont trop longues pour tenir dans le TreeView et que nous n'avons pas besoin de voir au premier coup d'œil.</p>

    <code mime="application/javascript"><![CDATA[
// Run the application
let app = new TreeViewExample ();
app.application.run (ARGV);
]]></code>
    <p>Enfin, nous créons une nouvelle instance de la classe TreeViewExample qui est terminée et nous démarrons l'application.</p>
  </section>

  <section id="complete">
    <title>Exemple complet du code</title>
<code mime="application/javascript" style="numbered">#!/usr/bin/gjs

const GObject = imports.gi.GObject;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Pango = imports.gi.Pango;

const TreeViewExample = new Lang.Class({
    Name: 'TreeView Example with Simple ListStore',

    // Create the application itself
    _init: function() {
        this.application = new Gtk.Application({
            application_id: 'org.example.jstreeviewsimpleliststore'
        });

    // Connect 'activate' and 'startup' signals to the callback functions
    this.application.connect('activate', Lang.bind(this, this._onActivate));
    this.application.connect('startup', Lang.bind(this, this._onStartup));
    },

    // Callback function for 'activate' signal presents window when active
    _onActivate: function() {
        this._window.present();
    },

    // Callback function for 'startup' signal builds the UI
    _onStartup: function() {
        this._buildUI ();
    },



    // Build the application's UI
    _buildUI: function() {

        // Create the application window
        this._window = new Gtk.ApplicationWindow({
            application: this.application,
            window_position: Gtk.WindowPosition.CENTER,
            default_height: 250,
            default_width: 100,
            border_width: 20,
            title: "My Phone Book"});

        // Create the underlying liststore for the phonebook
        this._listStore = new Gtk.ListStore ();
        this._listStore.set_column_types ([
            GObject.TYPE_STRING,
            GObject.TYPE_STRING,
            GObject.TYPE_STRING,
            GObject.TYPE_STRING]);

        // Data to go in the phonebook
        let phonebook =
            [{ name: "Jurg", surname: "Billeter", phone: "555-0123",
                description: "A friendly person."},
             { name: "Johannes", surname: "Schmid", phone: "555-1234",
                description: "Easy phone number to remember."},
             { name: "Julita", surname: "Inca", phone: "555-2345",
                description: "Another friendly person."},
             { name: "Javier", surname: "Jardon", phone: "555-3456",
                description: "Bring fish for his penguins."},
             { name: "Jason", surname: "Clinton", phone: "555-4567",
                description: "His cake's not a lie."},
             { name: "Random J.", surname: "Hacker", phone: "555-5678",
                description: "Very random!"}];

        // Put the data in the phonebook
        for (let i = 0; i &lt; phonebook.length; i++ ) {
            let contact = phonebook [i];
            this._listStore.set (this._listStore.append(), [0, 1, 2, 3],
                [contact.name, contact.surname, contact.phone, contact.description]);
        }

        // Create the treeview
        this._treeView = new Gtk.TreeView ({
            expand: true,
            model: this._listStore });

        // Create the columns for the address book
        let firstName = new Gtk.TreeViewColumn ({ title: "First Name" });
        let lastName = new Gtk.TreeViewColumn ({ title: "Last Name" });
        let phone = new Gtk.TreeViewColumn ({ title: "Phone Number" });

        // Create a cell renderer for when bold text is needed
        let bold = new Gtk.CellRendererText ({
            weight: Pango.Weight.BOLD });

        // Create a cell renderer for normal text
        let normal = new Gtk.CellRendererText ();

        // Pack the cell renderers into the columns
        firstName.pack_start (bold, true);
        lastName.pack_start (normal, true);
        phone.pack_start (normal, true);

        // Set each column to pull text from the TreeView's model
        firstName.add_attribute (bold, "text", 0);
        lastName.add_attribute (normal, "text", 1);
        phone.add_attribute (normal, "text", 2);

        // Insert the columns into the treeview
        this._treeView.insert_column (firstName, 0);
        this._treeView.insert_column (lastName, 1);
        this._treeView.insert_column (phone, 2);

        // Create the label that shows details for the name you select
        this._label = new Gtk.Label ({ label: "" });

        // Get which item is selected
        this.selection = this._treeView.get_selection();

        // When something new is selected, call _on_changed
        this.selection.connect ('changed', Lang.bind (this, this._onSelectionChanged));

        // Create a grid to organize everything in
        this._grid = new Gtk.Grid;

        // Attach the treeview and label to the grid
        this._grid.attach (this._treeView, 0, 0, 1, 1);
        this._grid.attach (this._label, 0, 1, 1, 1);

        // Add the grid to the window
        this._window.add (this._grid);

        // Show the window and all child widgets
        this._window.show_all();
    },



    _onSelectionChanged: function () {

        // Grab a treeiter pointing to the current selection
        let [ isSelected, model, iter ] = this.selection.get_selected();

        // Set the label to read off the values stored in the current selection
        this._label.set_label ("\n" +
            this._listStore.get_value (iter, 0) + " " +
            this._listStore.get_value (iter, 1) + " " +
            this._listStore.get_value (iter, 2) + "\n" +
            this._listStore.get_value (iter, 3));

    }

});

// Run the application
let app = new TreeViewExample ();
app.application.run (ARGV);
</code>
  </section>

  <section id="in-depth">
    <title>Documentation approfondie</title>
<p>Dans cet exemple, les éléments suivants sont utilisés :</p>
<list>
  <item><p><link href="http://www.roojs.com/seed/gir-1.2-gtk-3.0/gjs/Gtk.Application.html">Gtk.Application</link></p></item>
  <item><p><link href="http://developer.gnome.org/gtk3/stable/GtkApplicationWindow.html">Gtk.ApplicationWindow</link></p></item>
  <item><p><link href="http://www.roojs.org/seed/gir-1.2-gtk-3.0/gjs/Gtk.CellRendererText.html">Gtk.CellRendererText</link></p></item>
  <item><p><link href="http://www.roojs.org/seed/gir-1.2-gtk-3.0/gjs/Gtk.ListStore.html">Gtk.ListStore</link></p></item>
  <item><p><link href="http://www.roojs.org/seed/gir-1.2-gtk-3.0/gjs/Gtk.TreeIter.html">Gtk.TreeIter</link></p></item>
  <item><p><link href="http://www.roojs.org/seed/gir-1.2-gtk-3.0/gjs/Gtk.TreeSelection.html">Gtk.TreeSelection</link></p></item>
  <item><p><link href="http://www.roojs.org/seed/gir-1.2-gtk-3.0/gjs/Gtk.TreeView.html">Gtk.TreeView</link></p></item>
  <item><p><link href="http://www.roojs.org/seed/gir-1.2-gtk-3.0/gjs/Gtk.TreeViewColumn.html">Gtk.TreeViewColumn</link></p></item>
</list>
  </section>
</page>
