Update section 29-33.

This commit is contained in:
Toshio Sekiya 2023-08-01 18:05:16 +09:00
parent bda0f28ad3
commit 3ada90257e
26 changed files with 1323 additions and 1604 deletions

View file

@ -10,7 +10,8 @@ The table of contents is at the end of this abstract.
- Section 3 to 23 describes the basics, with the example of a simple editor `tfe` (Text File Editor).
- Section 24 to 27 describes GtkDrawingArea.
- Section 28 to 32 describes the list model and the list view (GtkListView, GtkGridView and GtkColumnView).
- Section 28 describes Drag and Drop.
- Section 29 to 33 describes the list model and the list view (GtkListView, GtkGridView and GtkColumnView).
It also describes GtkExpression.
The latest version of the tutorial is located at [GTK4-tutorial GitHub repository](https://github.com/ToshioCP/Gtk4-tutorial).

View file

@ -111,7 +111,8 @@ basics. The table of contents is at the end of this abstract.</p>
<li>Section 3 to 23 describes the basics, with the example of a simple
editor <code>tfe</code> (Text File Editor).</li>
<li>Section 24 to 27 describes GtkDrawingArea.</li>
<li>Section 28 to 32 describes the list model and the list view
<li>Section 28 describes Drag and Drop.</li>
<li>Section 29 to 33 describes the list model and the list view
(GtkListView, GtkGridView and GtkColumnView). It also describes
GtkExpression.</li>
</ul>

View file

@ -120,16 +120,17 @@ Reference List Widget Overview</a>.</p>
GtkTreeView which are took over from GTK 3. Theres an article in <a
href="https://blog.gtk.org/2020/06/07/scalable-lists-in-gtk-4/">Gtk
Development blog</a> about list widgets by Matthias Clasen. He described
why GtkListView are developed to replace GtkListBox and GtkTreeView.</p>
why GtkListView are developed to replace GtkTreeView. GtkTreeView is
deprecated since version 4.10.</p>
<p>GtkListView, GtkGridView, GtkColumnView and related objects are
described in Section 26 to 29.</p>
described in Section 29 to 33.</p>
<h2 id="outline">Outline</h2>
<p>A list is a sequential data structure. For example, an ordered string
sequence “one”, “two”, “three”, “four” is a list. Each element is called
item. A list is like an array, but in many cases it is implemented with
pointers which point to the next items of the list. And it has a start
point. So, each item can be referred by the index of the item (first
item, second item, …, nth item, …). There are two cases. One is the
item, second item, …, nth item, …). There are two cases. The one is the
index starts from one (one-based) and the other is from zero
(zero-based).</p>
<p>Gio provides GListModel interface. It is a zero-based list and its
@ -138,12 +139,12 @@ implement the same interface. An object implements GListModel is not a
widget. So, the list is not displayed on the screen directly. Theres
another object GtkListView which is a widget to display the list. The
items in the list need to be connected to the items in GtkListView.
GtkListItemFactory instance maps items in the list to GListView.</p>
GtkListItemFactory instance maps items in the list to GtkListView.</p>
<figure>
<img src="image/list.png" alt="List" />
<figcaption aria-hidden="true">List</figcaption>
</figure>
<h2 id="glistmodel">GListModel</h2>
<h2 id="glistmodel-and-gtkstringlist">GListModel and GtkStringList</h2>
<p>If you want to make a list of strings with GListModel, for example,
“one”, “two”, “three”, “four”, note that strings cant be items of the
list. Because GListModel is a list of GObject objects and strings arent
@ -356,7 +357,7 @@ instantiated.</p>
</blockquote>
<p>Therefore, GtkListItem instance is used as the <code>this</code>
object of the lookup tag when it is evaluated. <code>this</code> object
will be explained in section 30.</p>
will be explained in section 31.</p>
<p>The C source code is as follows. Its name is <code>list2.c</code> and
located under src/misc directory.</p>
<div class="sourceCode" id="cb7"><pre
@ -431,7 +432,7 @@ are information of files under a certain directory. It uses
<code>g_file_enumerate_children_async()</code> to get the GFileInfo
objects. The list model is created by
<code>gtk_directory_list_new</code> function.</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a>GtkDirectoryList <span class="op">*</span>gtk_directory_list_new <span class="op">(</span><span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span>attributes<span class="op">,</span> GFile <span class="op">*</span>file<span class="op">);</span></span></code></pre></div>
<div class="sourceCode" id="cb8"><pre class="sourceCode C"><code class="sourceCode c"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a>GtkDirectoryList <span class="op">*</span>gtk_directory_list_new <span class="op">(</span><span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span>attributes<span class="op">,</span> GFile <span class="op">*</span>file<span class="op">);</span></span></code></pre></div>
<p><code>attributes</code> is a comma separated list of file attributes.
File attributes are key-value pairs. A key consists of a namespace and a
name. For example, “standard::name” key is the name of a file.
@ -477,7 +478,7 @@ seconds since the UNIX epoch</td>
<p>The current directory is “.”. The following program makes
GtkDirectoryList <code>dl</code> and its contents are GFileInfo objects
under the current directory.</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a>GFile <span class="op">*</span>file <span class="op">=</span> g_file_new_for_path <span class="op">(</span><span class="st">&quot;.&quot;</span><span class="op">);</span></span>
<div class="sourceCode" id="cb9"><pre class="sourceCode C"><code class="sourceCode c"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a>GFile <span class="op">*</span>file <span class="op">=</span> g_file_new_for_path <span class="op">(</span><span class="st">&quot;.&quot;</span><span class="op">);</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a>GtkDirectoryList <span class="op">*</span>dl <span class="op">=</span> gtk_directory_list_new <span class="op">(</span><span class="st">&quot;standard::name&quot;</span><span class="op">,</span> file<span class="op">);</span></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a>g_object_unref <span class="op">(</span>file<span class="op">);</span></span></code></pre></div>
<p>It is not so difficult to make file listing program by changing
@ -486,7 +487,7 @@ GInfoFile doesnt have properties. Lookup tag look for a property, so it
is useless for looking for a filename from a GFileInfo object. Instead,
closure tag is appropriate in this case. Closure tag specifies a
function and the type of the return value of the function.</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span></span>
<div class="sourceCode" id="cb10"><pre class="sourceCode C"><code class="sourceCode c"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="dt">char</span> <span class="op">*</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a>get_file_name <span class="op">(</span>GtkListItem <span class="op">*</span>item<span class="op">,</span> GFileInfo <span class="op">*</span>info<span class="op">)</span> <span class="op">{</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> G_IS_FILE_INFO <span class="op">(</span>info<span class="op">)</span> <span class="op">?</span> g_strdup <span class="op">(</span>g_file_info_get_name <span class="op">(</span>info<span class="op">))</span> <span class="op">:</span> NULL<span class="op">;</span></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
@ -526,7 +527,7 @@ the function.
the value of the item property of the GtkListItem. This will be the
second argument of the function. The first parameter is always the
GListItem instance, which is a this object. The this object is
explained in section 28.</li>
explained in section 31.</li>
<li><code>gtk_file_name</code> function is the callback function for the
closure tag. It first checks the <code>info</code> parameter. Because it
can be NULL when GListItem <code>item</code> is unbounded. If its

View file

@ -173,8 +173,7 @@ registered to IANA media types. Such “x-” subtype is not a standard mime
type.) Content type is also used by desktop systems.</li>
</ul>
<p>GtkGridView uses the same GtkSingleSelection instance
(<code>singleselection</code>). So, its model property is set with
it.</p>
(<code>singleselection</code>). So, its model property is set to it.</p>
<h2 id="ui-file-of-the-window">Ui file of the window</h2>
<p>The window is built with the following ui file. (See the screenshot
at the beginning of this section).</p>
@ -291,7 +290,7 @@ GtkListView or GtkGridView.</li>
</ul>
<p>The action <code>view</code> is created, connected to the “activate”
signal handler and inserted to the window (action map) as follows.</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a>act_view <span class="op">=</span> g_simple_action_new_stateful <span class="op">(</span><span class="st">&quot;view&quot;</span><span class="op">,</span> g_variant_type_new<span class="op">(</span><span class="st">&quot;s&quot;</span><span class="op">),</span> g_variant_new_string <span class="op">(</span><span class="st">&quot;list&quot;</span><span class="op">));</span></span>
<div class="sourceCode" id="cb4"><pre class="sourceCode C"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a>act_view <span class="op">=</span> g_simple_action_new_stateful <span class="op">(</span><span class="st">&quot;view&quot;</span><span class="op">,</span> g_variant_type_new<span class="op">(</span><span class="st">&quot;s&quot;</span><span class="op">),</span> g_variant_new_string <span class="op">(</span><span class="st">&quot;list&quot;</span><span class="op">));</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>g_signal_connect <span class="op">(</span>act_view<span class="op">,</span> <span class="st">&quot;activate&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>view_activated<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>g_action_map_add_action <span class="op">(</span>G_ACTION_MAP <span class="op">(</span>win<span class="op">),</span> G_ACTION <span class="op">(</span>act_view<span class="op">));</span></span></code></pre></div>
<p>The signal handler <code>view_activated</code> will be explained
@ -464,7 +463,7 @@ pressed. You can do anything you like by connecting the “activate”
signal to the handler.</p>
<p>The example <code>list4</code> launches <code>tfe</code> text file
editor if the item of the list is a text file.</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<div class="sourceCode" id="cb10"><pre class="sourceCode C"><code class="sourceCode c"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a>list_activate <span class="op">(</span>GtkListView <span class="op">*</span>list<span class="op">,</span> <span class="dt">int</span> position<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a> GFileInfo <span class="op">*</span>info <span class="op">=</span> G_FILE_INFO <span class="op">(</span>g_list_model_get_item <span class="op">(</span>G_LIST_MODEL <span class="op">(</span>gtk_list_view_get_model <span class="op">(</span>list<span class="op">)),</span> position<span class="op">));</span></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a> launch_tfe_with_file <span class="op">(</span>info<span class="op">);</span></span>
@ -558,12 +557,12 @@ list and items.</li>
<code>g_app_info_launch_default_for_uri</code> is convenient. The
function automatically determines the default application from the file
and launches it. For example, if the file is text, then it launches
gedit with the file. Such feature comes from desktop.</p>
GNOME text editor with the file. Such feature comes from desktop.</p>
<h2 id="compilation-and-execution">Compilation and execution</h2>
<p>The source files are located in src/list4 directory. To compile and
execute list4, type as follows.</p>
<pre><code>$ cd list4 # or cd src/list4. It depends your current directory.
$ meson _build
$ meson setup _build
$ ninja -C _build
$ _build/list4</code></pre>
<p>Then a file list appears as a list style. Click on a button on the

View file

@ -191,13 +191,13 @@ value of the expression. The type of the value is int.</li>
</tr>
<tr class="even">
<td style="text-align: left;">G_TYPE_POINTER</td>
<td style="text-align: left;"></td>
<td style="text-align: left;">void *</td>
<td style="text-align: left;">gpointer</td>
<td style="text-align: left;"></td>
<td style="text-align: left;">general pointer</td>
</tr>
<tr class="odd">
<td style="text-align: left;">G_TYPE_STRING</td>
<td style="text-align: left;"></td>
<td style="text-align: left;">char *</td>
<td style="text-align: left;">gchararray</td>
<td style="text-align: left;">null-terminated Cstring</td>
</tr>
@ -250,7 +250,7 @@ instance to another expression.</p>
<p>A property expression (GtkPropertyExpression) looks up a property in
a GObject instance. For example, a property expression that refers
“label” property in a GtkLabel object is created like this.</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a>expression <span class="op">=</span> gtk_property_expression_new <span class="op">(</span>GTK_TYPE_LABEL<span class="op">,</span> another_expression<span class="op">,</span> <span class="st">&quot;label&quot;</span><span class="op">);</span></span></code></pre></div>
<div class="sourceCode" id="cb4"><pre class="sourceCode C"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a>expression <span class="op">=</span> gtk_property_expression_new <span class="op">(</span>GTK_TYPE_LABEL<span class="op">,</span> another_expression<span class="op">,</span> <span class="st">&quot;label&quot;</span><span class="op">);</span></span></code></pre></div>
<p>The second parameter <code>another_expression</code> is one of:</p>
<ul>
<li>An expression that gives a GtkLabel instance when it is
@ -259,14 +259,14 @@ evaluated.</li>
is evaluated. The instance is called <code>this</code> object.</li>
</ul>
<p>For example,</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a>label <span class="op">=</span> gtk_label_new <span class="op">(</span><span class="st">&quot;Hello&quot;</span><span class="op">);</span></span>
<div class="sourceCode" id="cb5"><pre class="sourceCode C"><code class="sourceCode c"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a>label <span class="op">=</span> gtk_label_new <span class="op">(</span><span class="st">&quot;Hello&quot;</span><span class="op">);</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>another_expression <span class="op">=</span> gtk_constant_expression_new <span class="op">(</span>GTK_TYPE_LABEL<span class="op">,</span> label<span class="op">);</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>expression <span class="op">=</span> gtk_property_expression_new <span class="op">(</span>GTK_TYPE_LABEL<span class="op">,</span> another_expression<span class="op">,</span> <span class="st">&quot;label&quot;</span><span class="op">);</span></span></code></pre></div>
<p>If <code>expression</code> is evaluated, the second parameter
<code>another_expression</code> is evaluated in advance. The value of
<code>another_expression</code> is the <code>label</code> (GtkLabel
instance). Then, <code>expression</code> looks up “label” property of
the label and the evaluation results “Hello”.</p>
the label and the evaluation results in “Hello”.</p>
<p>In the example above, the second argument of
<code>gtk_property_expression_new</code> is another expression. But the
second argument can be NULL. If it is NULL, <code>this</code> instance
@ -291,7 +291,7 @@ class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span i
<span id="cb6-14"><a href="#cb6-14"></a> <span class="cf">if</span> <span class="op">(</span>gtk_expression_evaluate <span class="op">(</span>expression<span class="op">,</span> label<span class="op">,</span> <span class="op">&amp;</span>value<span class="op">))</span></span>
<span id="cb6-15"><a href="#cb6-15"></a> g_print <span class="op">(</span><span class="st">&quot;The value is %s.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">,</span> g_value_get_string <span class="op">(&amp;</span>value<span class="op">));</span></span>
<span id="cb6-16"><a href="#cb6-16"></a> <span class="cf">else</span></span>
<span id="cb6-17"><a href="#cb6-17"></a> g_print <span class="op">(</span><span class="st">&quot;The constant expression wasn&#39;t evaluated correctly.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb6-17"><a href="#cb6-17"></a> g_print <span class="op">(</span><span class="st">&quot;The property expression wasn&#39;t evaluated correctly.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb6-18"><a href="#cb6-18"></a> gtk_expression_unref <span class="op">(</span>expression<span class="op">);</span></span>
<span id="cb6-19"><a href="#cb6-19"></a> g_value_unset <span class="op">(&amp;</span>value<span class="op">);</span></span>
<span id="cb6-20"><a href="#cb6-20"></a></span>
@ -311,8 +311,8 @@ the expression with a this instance <code>label</code>. The result is
stored in the GValue <code>value</code>. The function
<code>g_value_get_string</code> gets a string from the GValue. But the
string is owned by the GValue so you must not free the string.</li>
<li>18-19: Release the expression and unset the GValue. At the same time
the string in the GValue is freed.</li>
<li>18-19: Releases the expression and unset the GValue. At the same
time the string in the GValue is freed.</li>
</ul>
<p>If the second argument of <code>gtk_property_expression_new</code>
isnt NULL, it is another expression. The expression is owned by a newly
@ -334,79 +334,44 @@ respectively. When you program in C language, GtkCClosureExpression and
GCClosure are appropriate.</p>
<p>A closure expression is created with
<code>gtk_cclosure_expression_new</code> function.</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a>GtkExpression <span class="op">*</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>gtk_cclosure_expression_new <span class="op">(</span>GType value_type<span class="op">,</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a> GClosureMarshal marshal<span class="op">,</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a> guint n_params<span class="op">,</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a> GtkExpression <span class="op">**</span>params<span class="op">,</span></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a> GCallback callback_func<span class="op">,</span></span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a> gpointer user_data<span class="op">,</span></span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a> GClosureNotify user_destroy<span class="op">);</span></span></code></pre></div>
<ul>
<li><code>value_type</code> is the type of the value when it is
evaluated.</li>
<li><code>marshal</code> is a marshaller. You can assign NULL. If it is
NULL, then <code>g_cclosure_marshal_generic ()</code> is used as a
marshaller. It is a generic marshaller function implemented via
libffi.</li>
<li><code>n_params</code> is the number of parameters.</li>
<li><code>params</code> points expressions for each parameter of the
call back function.</li>
<li><code>callback_func</code> is a callback function. It is given
arguments <code>this</code> and parameters above. So, if
<code>n_params</code> is 3, the number of arguments of the function is
4. (<code>this</code> and <code>params</code>. See below.)</li>
<li><code>user_data</code> is user data. You can add it for the closure.
It is like <code>user_data</code> in <code>g_signal_connect</code>. If
it is not necessary, assign NULL.</li>
<li><code>user_destroy</code> is a destroy notify for
<code>user_data</code>. It is called to destroy <code>user_data</code>
when it is no longer needed. If NULL is assigned to
<code>user_data</code>, assign NULL to <code>user_destroy</code>,
too.</li>
</ul>
<p>Call back functions have the following format.</p>
<pre><code>C-type
callback (this, param1, param2, ...)</code></pre>
<p>For example,</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a>callback <span class="op">(</span>GObject <span class="op">*</span>object<span class="op">,</span> <span class="dt">int</span> x<span class="op">,</span> <span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span>s<span class="op">)</span></span></code></pre></div>
<div class="sourceCode" id="cb7"><pre class="sourceCode C"><code class="sourceCode c"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>callback <span class="op">(</span>GObject <span class="op">*</span>object<span class="op">,</span> <span class="dt">int</span> x<span class="op">,</span> <span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span>s<span class="op">)</span></span></code></pre></div>
<p>The following is <code>exp_closure_simple.c</code> in
<code>src/expression</code>.</p>
<div class="sourceCode" id="cb10"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb10-1"><a href="#cb10-1"></a><span class="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span>
<span id="cb10-2"><a href="#cb10-2"></a></span>
<span id="cb10-3"><a href="#cb10-3"></a><span class="dt">static</span> <span class="dt">int</span></span>
<span id="cb10-4"><a href="#cb10-4"></a>calc <span class="op">(</span>GtkLabel <span class="op">*</span>label<span class="op">)</span> <span class="op">{</span> <span class="co">/* this object */</span></span>
<span id="cb10-5"><a href="#cb10-5"></a> <span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span> s<span class="op">;</span></span>
<span id="cb10-6"><a href="#cb10-6"></a> <span class="dt">int</span> i<span class="op">,</span> j<span class="op">;</span></span>
<span id="cb10-7"><a href="#cb10-7"></a></span>
<span id="cb10-8"><a href="#cb10-8"></a> s <span class="op">=</span> gtk_label_get_text <span class="op">(</span>label<span class="op">);</span> <span class="co">/* s is owned by the label. */</span></span>
<span id="cb10-9"><a href="#cb10-9"></a> sscanf <span class="op">(</span>s<span class="op">,</span> <span class="st">&quot;%d+%d&quot;</span><span class="op">,</span> <span class="op">&amp;</span>i<span class="op">,</span> <span class="op">&amp;</span>j<span class="op">);</span></span>
<span id="cb10-10"><a href="#cb10-10"></a> <span class="cf">return</span> i<span class="op">+</span>j<span class="op">;</span></span>
<span id="cb10-11"><a href="#cb10-11"></a><span class="op">}</span></span>
<span id="cb10-12"><a href="#cb10-12"></a></span>
<span id="cb10-13"><a href="#cb10-13"></a><span class="dt">int</span></span>
<span id="cb10-14"><a href="#cb10-14"></a>main <span class="op">(</span><span class="dt">int</span> argc<span class="op">,</span> <span class="dt">char</span> <span class="op">**</span>argv<span class="op">)</span> <span class="op">{</span></span>
<span id="cb10-15"><a href="#cb10-15"></a> GtkExpression <span class="op">*</span>expression<span class="op">;</span></span>
<span id="cb10-16"><a href="#cb10-16"></a> GValue value <span class="op">=</span> G_VALUE_INIT<span class="op">;</span></span>
<span id="cb10-17"><a href="#cb10-17"></a> GtkLabel <span class="op">*</span>label<span class="op">;</span></span>
<span id="cb10-18"><a href="#cb10-18"></a></span>
<span id="cb10-19"><a href="#cb10-19"></a> gtk_init <span class="op">();</span></span>
<span id="cb10-20"><a href="#cb10-20"></a> label <span class="op">=</span> GTK_LABEL <span class="op">(</span>gtk_label_new <span class="op">(</span><span class="st">&quot;123+456&quot;</span><span class="op">));</span></span>
<span id="cb10-21"><a href="#cb10-21"></a> g_object_ref_sink <span class="op">(</span>label<span class="op">);</span></span>
<span id="cb10-22"><a href="#cb10-22"></a> expression <span class="op">=</span> gtk_cclosure_expression_new <span class="op">(</span>G_TYPE_INT<span class="op">,</span> NULL<span class="op">,</span> <span class="dv">0</span><span class="op">,</span> NULL<span class="op">,</span></span>
<span id="cb10-23"><a href="#cb10-23"></a> G_CALLBACK <span class="op">(</span>calc<span class="op">),</span> NULL<span class="op">,</span> NULL<span class="op">);</span></span>
<span id="cb10-24"><a href="#cb10-24"></a> <span class="cf">if</span> <span class="op">(</span>gtk_expression_evaluate <span class="op">(</span>expression<span class="op">,</span> label<span class="op">,</span> <span class="op">&amp;</span>value<span class="op">))</span> <span class="co">/* &#39;this&#39; object is the label. */</span></span>
<span id="cb10-25"><a href="#cb10-25"></a> g_print <span class="op">(</span><span class="st">&quot;%d</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">,</span> g_value_get_int <span class="op">(&amp;</span>value<span class="op">));</span></span>
<span id="cb10-26"><a href="#cb10-26"></a> <span class="cf">else</span></span>
<span id="cb10-27"><a href="#cb10-27"></a> g_print <span class="op">(</span><span class="st">&quot;The closure expression wasn&#39;t evaluated correctly.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb10-28"><a href="#cb10-28"></a> gtk_expression_unref <span class="op">(</span>expression<span class="op">);</span></span>
<span id="cb10-29"><a href="#cb10-29"></a> g_value_unset <span class="op">(&amp;</span>value<span class="op">);</span></span>
<span id="cb10-30"><a href="#cb10-30"></a> g_object_unref <span class="op">(</span>label<span class="op">);</span></span>
<span id="cb10-31"><a href="#cb10-31"></a> </span>
<span id="cb10-32"><a href="#cb10-32"></a> <span class="cf">return</span> <span class="dv">0</span><span class="op">;</span></span>
<span id="cb10-33"><a href="#cb10-33"></a><span class="op">}</span></span></code></pre></div>
<div class="sourceCode" id="cb8"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb8-1"><a href="#cb8-1"></a><span class="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span>
<span id="cb8-2"><a href="#cb8-2"></a></span>
<span id="cb8-3"><a href="#cb8-3"></a><span class="dt">static</span> <span class="dt">int</span></span>
<span id="cb8-4"><a href="#cb8-4"></a>calc <span class="op">(</span>GtkLabel <span class="op">*</span>label<span class="op">)</span> <span class="op">{</span> <span class="co">/* this object */</span></span>
<span id="cb8-5"><a href="#cb8-5"></a> <span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span> s<span class="op">;</span></span>
<span id="cb8-6"><a href="#cb8-6"></a> <span class="dt">int</span> i<span class="op">,</span> j<span class="op">;</span></span>
<span id="cb8-7"><a href="#cb8-7"></a></span>
<span id="cb8-8"><a href="#cb8-8"></a> s <span class="op">=</span> gtk_label_get_text <span class="op">(</span>label<span class="op">);</span> <span class="co">/* s is owned by the label. */</span></span>
<span id="cb8-9"><a href="#cb8-9"></a> sscanf <span class="op">(</span>s<span class="op">,</span> <span class="st">&quot;%d+%d&quot;</span><span class="op">,</span> <span class="op">&amp;</span>i<span class="op">,</span> <span class="op">&amp;</span>j<span class="op">);</span></span>
<span id="cb8-10"><a href="#cb8-10"></a> <span class="cf">return</span> i<span class="op">+</span>j<span class="op">;</span></span>
<span id="cb8-11"><a href="#cb8-11"></a><span class="op">}</span></span>
<span id="cb8-12"><a href="#cb8-12"></a></span>
<span id="cb8-13"><a href="#cb8-13"></a><span class="dt">int</span></span>
<span id="cb8-14"><a href="#cb8-14"></a>main <span class="op">(</span><span class="dt">int</span> argc<span class="op">,</span> <span class="dt">char</span> <span class="op">**</span>argv<span class="op">)</span> <span class="op">{</span></span>
<span id="cb8-15"><a href="#cb8-15"></a> GtkExpression <span class="op">*</span>expression<span class="op">;</span></span>
<span id="cb8-16"><a href="#cb8-16"></a> GValue value <span class="op">=</span> G_VALUE_INIT<span class="op">;</span></span>
<span id="cb8-17"><a href="#cb8-17"></a> GtkLabel <span class="op">*</span>label<span class="op">;</span></span>
<span id="cb8-18"><a href="#cb8-18"></a></span>
<span id="cb8-19"><a href="#cb8-19"></a> gtk_init <span class="op">();</span></span>
<span id="cb8-20"><a href="#cb8-20"></a> label <span class="op">=</span> GTK_LABEL <span class="op">(</span>gtk_label_new <span class="op">(</span><span class="st">&quot;123+456&quot;</span><span class="op">));</span></span>
<span id="cb8-21"><a href="#cb8-21"></a> g_object_ref_sink <span class="op">(</span>label<span class="op">);</span></span>
<span id="cb8-22"><a href="#cb8-22"></a> expression <span class="op">=</span> gtk_cclosure_expression_new <span class="op">(</span>G_TYPE_INT<span class="op">,</span> NULL<span class="op">,</span> <span class="dv">0</span><span class="op">,</span> NULL<span class="op">,</span></span>
<span id="cb8-23"><a href="#cb8-23"></a> G_CALLBACK <span class="op">(</span>calc<span class="op">),</span> NULL<span class="op">,</span> NULL<span class="op">);</span></span>
<span id="cb8-24"><a href="#cb8-24"></a> <span class="cf">if</span> <span class="op">(</span>gtk_expression_evaluate <span class="op">(</span>expression<span class="op">,</span> label<span class="op">,</span> <span class="op">&amp;</span>value<span class="op">))</span> <span class="co">/* &#39;this&#39; object is the label. */</span></span>
<span id="cb8-25"><a href="#cb8-25"></a> g_print <span class="op">(</span><span class="st">&quot;%d</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">,</span> g_value_get_int <span class="op">(&amp;</span>value<span class="op">));</span></span>
<span id="cb8-26"><a href="#cb8-26"></a> <span class="cf">else</span></span>
<span id="cb8-27"><a href="#cb8-27"></a> g_print <span class="op">(</span><span class="st">&quot;The closure expression wasn&#39;t evaluated correctly.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb8-28"><a href="#cb8-28"></a> gtk_expression_unref <span class="op">(</span>expression<span class="op">);</span></span>
<span id="cb8-29"><a href="#cb8-29"></a> g_value_unset <span class="op">(&amp;</span>value<span class="op">);</span></span>
<span id="cb8-30"><a href="#cb8-30"></a> g_object_unref <span class="op">(</span>label<span class="op">);</span></span>
<span id="cb8-31"><a href="#cb8-31"></a> </span>
<span id="cb8-32"><a href="#cb8-32"></a> <span class="cf">return</span> <span class="dv">0</span><span class="op">;</span></span>
<span id="cb8-33"><a href="#cb8-33"></a><span class="op">}</span></span></code></pre></div>
<ul>
<li>3-11: A call back function. The parameter is only one and it is a
this object. It is a GtkLabel and its label is assumed to be
@ -419,16 +384,17 @@ argument of <code>gtk_cclosure_expression_new</code> is
<code>G_TYPE_POINTER</code>. There is a sample program
<code>exp_closure_with_error_report</code> in
<code>src/expression</code> directory.</li>
<li>19: gtk_init initializes GTK. It is necessary for GtkLabel.</li>
<li>19: The function `gtk_init`` initializes GTK. It is necessary for
GtkLabel.</li>
<li>20: A GtkLabel instance is created with “123+456”.</li>
<li>21: The instance has floating reference. It is changed to an
ordinary reference count.</li>
<li>22-23: Create a closure expression. Its return value type is
<li>22-23: Creates a closure expression. Its return value type is
<code>G_TYPE_INT</code> and no parameters or this object.</li>
<li>24: Evaluates the expression with the label as a this object.</li>
<li>25: If the evaluation successes, show the sum of “123+456”. Its
579.</li>
<li>27: If it fails, show an error message.</li>
<li>25: If the evaluation successes, the sum of “123+456”, which is 579,
is shown.</li>
<li>27: If it fails, an error message appears.</li>
<li>28-30: Releases the expression and the label. Unsets the value.</li>
</ul>
<p>Closure expression is flexible than other type of expression because
@ -436,18 +402,19 @@ you can specify your own callback function.</p>
<h2 id="gtkexpressionwatch">GtkExpressionWatch</h2>
<p>GtkExpressionWatch is a structure, not an object. It represents a
watched GtkExpression. Two functions create GtkExpressionWatch
structure.</p>
structure. They are <code>gtk_expression_bind</code> and
<code>gtk_expression_watch</code>.</p>
<h3 id="gtk_expression_bind-function">gtk_expression_bind function</h3>
<p>This function binds the target objects property to the expression.
If the value of the expression changes, the property reflects the value
immediately.</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a>GtkExpressionWatch<span class="op">*</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a>gtk_expression_bind <span class="op">(</span></span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a> GtkExpression<span class="op">*</span> self<span class="op">,</span></span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a> GObject<span class="op">*</span> target<span class="op">,</span></span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">const</span> <span class="dt">char</span><span class="op">*</span> property<span class="op">,</span></span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a> GObject<span class="op">*</span> this_</span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true" tabindex="-1"></a><span class="op">)</span></span></code></pre></div>
<div class="sourceCode" id="cb9"><pre class="sourceCode C"><code class="sourceCode c"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a>GtkExpressionWatch<span class="op">*</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a>gtk_expression_bind <span class="op">(</span></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a> GtkExpression<span class="op">*</span> self<span class="op">,</span></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a> GObject<span class="op">*</span> target<span class="op">,</span></span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">const</span> <span class="dt">char</span><span class="op">*</span> property<span class="op">,</span></span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a> GObject<span class="op">*</span> this_</span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a><span class="op">)</span></span></code></pre></div>
<p>This function takes the ownership of the expression. So, if you want
to own the expression, call <code>gtk_expression_ref ()</code> to
increase the reference count of the expression. And you should unref it
@ -463,41 +430,41 @@ about releasing the expression.</p>
the scale value increases and the number on the label also increases. In
the same way, if you move it to the left, the number on the label
decreases. The label is bound to the scale value via an adjustment.</p>
<div class="sourceCode" id="cb12"><pre
class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><span id="cb12-1"><a href="#cb12-1"></a><span class="fu">&lt;?xml</span><span class="ot"> version=</span><span class="st">&#39;1.0&#39;</span><span class="ot"> encoding=</span><span class="st">&#39;UTF-8&#39;</span><span class="fu">?&gt;</span></span>
<span id="cb12-2"><a href="#cb12-2"></a>&lt;<span class="kw">interface</span>&gt;</span>
<span id="cb12-3"><a href="#cb12-3"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&#39;GtkApplicationWindow&#39;</span><span class="ot"> id=</span><span class="st">&#39;win&#39;</span>&gt;</span>
<span id="cb12-4"><a href="#cb12-4"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&#39;default-width&#39;</span>&gt;600&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb12-5"><a href="#cb12-5"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb12-6"><a href="#cb12-6"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&#39;GtkBox&#39;</span>&gt;</span>
<span id="cb12-7"><a href="#cb12-7"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&#39;orientation&#39;</span>&gt;GTK_ORIENTATION_VERTICAL&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb12-8"><a href="#cb12-8"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb12-9"><a href="#cb12-9"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&#39;GtkLabel&#39;</span><span class="ot"> id=</span><span class="st">&#39;label&#39;</span>&gt;</span>
<span id="cb12-10"><a href="#cb12-10"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;label&quot;</span>&gt;10&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb12-11"><a href="#cb12-11"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb12-12"><a href="#cb12-12"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb12-13"><a href="#cb12-13"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb12-14"><a href="#cb12-14"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&#39;GtkScale&#39;</span>&gt;</span>
<span id="cb12-15"><a href="#cb12-15"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&#39;adjustment&#39;</span>&gt;</span>
<span id="cb12-16"><a href="#cb12-16"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&#39;GtkAdjustment&#39;</span><span class="ot"> id=</span><span class="st">&#39;adjustment&#39;</span>&gt;</span>
<span id="cb12-17"><a href="#cb12-17"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&#39;upper&#39;</span>&gt;20.0&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb12-18"><a href="#cb12-18"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&#39;lower&#39;</span>&gt;0.0&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb12-19"><a href="#cb12-19"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&#39;value&#39;</span>&gt;10.0&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb12-20"><a href="#cb12-20"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&#39;step-increment&#39;</span>&gt;1.0&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb12-21"><a href="#cb12-21"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&#39;page-increment&#39;</span>&gt;5.0&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb12-22"><a href="#cb12-22"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&#39;page-size&#39;</span>&gt;0.0&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb12-23"><a href="#cb12-23"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb12-24"><a href="#cb12-24"></a> &lt;/<span class="kw">property</span>&gt;</span>
<span id="cb12-25"><a href="#cb12-25"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&#39;digits&#39;</span>&gt;0&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb12-26"><a href="#cb12-26"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&#39;draw-value&#39;</span>&gt;true&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb12-27"><a href="#cb12-27"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&#39;has-origin&#39;</span>&gt;true&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb12-28"><a href="#cb12-28"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&#39;round-digits&#39;</span>&gt;0&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb12-29"><a href="#cb12-29"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb12-30"><a href="#cb12-30"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb12-31"><a href="#cb12-31"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb12-32"><a href="#cb12-32"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb12-33"><a href="#cb12-33"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb12-34"><a href="#cb12-34"></a>&lt;/<span class="kw">interface</span>&gt;</span></code></pre></div>
<div class="sourceCode" id="cb10"><pre
class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><span id="cb10-1"><a href="#cb10-1"></a><span class="fu">&lt;?xml</span><span class="ot"> version=</span><span class="st">&#39;1.0&#39;</span><span class="ot"> encoding=</span><span class="st">&#39;UTF-8&#39;</span><span class="fu">?&gt;</span></span>
<span id="cb10-2"><a href="#cb10-2"></a>&lt;<span class="kw">interface</span>&gt;</span>
<span id="cb10-3"><a href="#cb10-3"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&#39;GtkApplicationWindow&#39;</span><span class="ot"> id=</span><span class="st">&#39;win&#39;</span>&gt;</span>
<span id="cb10-4"><a href="#cb10-4"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&#39;default-width&#39;</span>&gt;600&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb10-5"><a href="#cb10-5"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb10-6"><a href="#cb10-6"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&#39;GtkBox&#39;</span>&gt;</span>
<span id="cb10-7"><a href="#cb10-7"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&#39;orientation&#39;</span>&gt;GTK_ORIENTATION_VERTICAL&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb10-8"><a href="#cb10-8"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb10-9"><a href="#cb10-9"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&#39;GtkLabel&#39;</span><span class="ot"> id=</span><span class="st">&#39;label&#39;</span>&gt;</span>
<span id="cb10-10"><a href="#cb10-10"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;label&quot;</span>&gt;10&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb10-11"><a href="#cb10-11"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb10-12"><a href="#cb10-12"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb10-13"><a href="#cb10-13"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb10-14"><a href="#cb10-14"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&#39;GtkScale&#39;</span>&gt;</span>
<span id="cb10-15"><a href="#cb10-15"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&#39;adjustment&#39;</span>&gt;</span>
<span id="cb10-16"><a href="#cb10-16"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&#39;GtkAdjustment&#39;</span><span class="ot"> id=</span><span class="st">&#39;adjustment&#39;</span>&gt;</span>
<span id="cb10-17"><a href="#cb10-17"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&#39;upper&#39;</span>&gt;20.0&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb10-18"><a href="#cb10-18"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&#39;lower&#39;</span>&gt;0.0&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb10-19"><a href="#cb10-19"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&#39;value&#39;</span>&gt;10.0&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb10-20"><a href="#cb10-20"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&#39;step-increment&#39;</span>&gt;1.0&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb10-21"><a href="#cb10-21"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&#39;page-increment&#39;</span>&gt;5.0&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb10-22"><a href="#cb10-22"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&#39;page-size&#39;</span>&gt;0.0&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb10-23"><a href="#cb10-23"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb10-24"><a href="#cb10-24"></a> &lt;/<span class="kw">property</span>&gt;</span>
<span id="cb10-25"><a href="#cb10-25"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&#39;digits&#39;</span>&gt;0&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb10-26"><a href="#cb10-26"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&#39;draw-value&#39;</span>&gt;true&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb10-27"><a href="#cb10-27"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&#39;has-origin&#39;</span>&gt;true&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb10-28"><a href="#cb10-28"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&#39;round-digits&#39;</span>&gt;0&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb10-29"><a href="#cb10-29"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb10-30"><a href="#cb10-30"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb10-31"><a href="#cb10-31"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb10-32"><a href="#cb10-32"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb10-33"><a href="#cb10-33"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb10-34"><a href="#cb10-34"></a>&lt;/<span class="kw">interface</span>&gt;</span></code></pre></div>
<p>The ui file describes the following parent-child relationship.</p>
<pre><code>GtkApplicationWindow (win) -- GtkBox -+- GtkLabel (label)
+- GtkScale</code></pre>
@ -523,73 +490,73 @@ orange bar appears between the origin and the current point.</li>
changes. For example, if it is zero, the slider moves to an integer
point.</li>
</ul>
<div class="sourceCode" id="cb14"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb14-1"><a href="#cb14-1"></a><span class="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span>
<span id="cb14-2"><a href="#cb14-2"></a></span>
<span id="cb14-3"><a href="#cb14-3"></a>GtkExpressionWatch <span class="op">*</span>watch<span class="op">;</span></span>
<span id="cb14-4"><a href="#cb14-4"></a></span>
<span id="cb14-5"><a href="#cb14-5"></a><span class="dt">static</span> <span class="dt">int</span></span>
<span id="cb14-6"><a href="#cb14-6"></a>f2i <span class="op">(</span>GObject <span class="op">*</span>object<span class="op">,</span> <span class="dt">double</span> d<span class="op">)</span> <span class="op">{</span></span>
<span id="cb14-7"><a href="#cb14-7"></a> <span class="cf">return</span> <span class="op">(</span><span class="dt">int</span><span class="op">)</span> d<span class="op">;</span></span>
<span id="cb14-8"><a href="#cb14-8"></a><span class="op">}</span></span>
<span id="cb14-9"><a href="#cb14-9"></a></span>
<span id="cb14-10"><a href="#cb14-10"></a><span class="dt">static</span> <span class="dt">int</span></span>
<span id="cb14-11"><a href="#cb14-11"></a>close_request_cb <span class="op">(</span>GtkWindow <span class="op">*</span>win<span class="op">)</span> <span class="op">{</span></span>
<span id="cb14-12"><a href="#cb14-12"></a> gtk_expression_watch_unwatch <span class="op">(</span>watch<span class="op">);</span></span>
<span id="cb14-13"><a href="#cb14-13"></a> <span class="cf">return</span> false<span class="op">;</span></span>
<span id="cb14-14"><a href="#cb14-14"></a><span class="op">}</span></span>
<span id="cb14-15"><a href="#cb14-15"></a></span>
<span id="cb14-16"><a href="#cb14-16"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb14-17"><a href="#cb14-17"></a>app_activate <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
<span id="cb14-18"><a href="#cb14-18"></a> GtkApplication <span class="op">*</span>app <span class="op">=</span> GTK_APPLICATION <span class="op">(</span>application<span class="op">);</span></span>
<span id="cb14-19"><a href="#cb14-19"></a> gtk_window_present <span class="op">(</span>gtk_application_get_active_window<span class="op">(</span>app<span class="op">));</span></span>
<span id="cb14-20"><a href="#cb14-20"></a><span class="op">}</span></span>
<span id="cb14-21"><a href="#cb14-21"></a></span>
<span id="cb14-22"><a href="#cb14-22"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb14-23"><a href="#cb14-23"></a>app_startup <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
<span id="cb14-24"><a href="#cb14-24"></a> GtkApplication <span class="op">*</span>app <span class="op">=</span> GTK_APPLICATION <span class="op">(</span>application<span class="op">);</span></span>
<span id="cb14-25"><a href="#cb14-25"></a> GtkBuilder <span class="op">*</span>build<span class="op">;</span></span>
<span id="cb14-26"><a href="#cb14-26"></a> GtkWidget <span class="op">*</span>win<span class="op">,</span> <span class="op">*</span>label<span class="op">;</span></span>
<span id="cb14-27"><a href="#cb14-27"></a> GtkAdjustment <span class="op">*</span>adjustment<span class="op">;</span></span>
<span id="cb14-28"><a href="#cb14-28"></a> GtkExpression <span class="op">*</span>expression<span class="op">,</span> <span class="op">*</span>params<span class="op">[</span><span class="dv">1</span><span class="op">];</span></span>
<span id="cb14-29"><a href="#cb14-29"></a></span>
<span id="cb14-30"><a href="#cb14-30"></a> <span class="co">/* Builds a window with exp.ui resource */</span></span>
<span id="cb14-31"><a href="#cb14-31"></a> build <span class="op">=</span> gtk_builder_new_from_file <span class="op">(</span><span class="st">&quot;exp_bind.ui&quot;</span><span class="op">);</span></span>
<span id="cb14-32"><a href="#cb14-32"></a> win <span class="op">=</span> GTK_WIDGET <span class="op">(</span>gtk_builder_get_object <span class="op">(</span>build<span class="op">,</span> <span class="st">&quot;win&quot;</span><span class="op">));</span></span>
<span id="cb14-33"><a href="#cb14-33"></a> label <span class="op">=</span> GTK_WIDGET <span class="op">(</span>gtk_builder_get_object <span class="op">(</span>build<span class="op">,</span> <span class="st">&quot;label&quot;</span><span class="op">));</span></span>
<span id="cb14-34"><a href="#cb14-34"></a> <span class="co">// scale = GTK_WIDGET (gtk_builder_get_object (build, &quot;scale&quot;));</span></span>
<span id="cb14-35"><a href="#cb14-35"></a> adjustment <span class="op">=</span> GTK_ADJUSTMENT <span class="op">(</span>gtk_builder_get_object <span class="op">(</span>build<span class="op">,</span> <span class="st">&quot;adjustment&quot;</span><span class="op">));</span></span>
<span id="cb14-36"><a href="#cb14-36"></a> gtk_window_set_application <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> app<span class="op">);</span></span>
<span id="cb14-37"><a href="#cb14-37"></a> g_signal_connect <span class="op">(</span>win<span class="op">,</span> <span class="st">&quot;close-request&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>close_request_cb<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb14-38"><a href="#cb14-38"></a> g_object_unref <span class="op">(</span>build<span class="op">);</span></span>
<span id="cb14-39"><a href="#cb14-39"></a></span>
<span id="cb14-40"><a href="#cb14-40"></a> <span class="co">/* GtkExpressionWatch */</span></span>
<span id="cb14-41"><a href="#cb14-41"></a> params<span class="op">[</span><span class="dv">0</span><span class="op">]</span> <span class="op">=</span> gtk_property_expression_new <span class="op">(</span>GTK_TYPE_ADJUSTMENT<span class="op">,</span> NULL<span class="op">,</span> <span class="st">&quot;value&quot;</span><span class="op">);</span></span>
<span id="cb14-42"><a href="#cb14-42"></a> expression <span class="op">=</span> gtk_cclosure_expression_new <span class="op">(</span>G_TYPE_INT<span class="op">,</span> NULL<span class="op">,</span> <span class="dv">1</span><span class="op">,</span> params<span class="op">,</span> G_CALLBACK <span class="op">(</span>f2i<span class="op">),</span> NULL<span class="op">,</span> NULL<span class="op">);</span></span>
<span id="cb14-43"><a href="#cb14-43"></a> watch <span class="op">=</span> gtk_expression_bind <span class="op">(</span>expression<span class="op">,</span> label<span class="op">,</span> <span class="st">&quot;label&quot;</span><span class="op">,</span> adjustment<span class="op">);</span> <span class="co">/* watch takes the ownership of the expression. */</span></span>
<span id="cb14-44"><a href="#cb14-44"></a><span class="op">}</span></span>
<span id="cb14-45"><a href="#cb14-45"></a></span>
<span id="cb14-46"><a href="#cb14-46"></a><span class="pp">#define APPLICATION_ID &quot;com.github.ToshioCP.exp_watch&quot;</span></span>
<span id="cb14-47"><a href="#cb14-47"></a></span>
<span id="cb14-48"><a href="#cb14-48"></a><span class="dt">int</span></span>
<span id="cb14-49"><a href="#cb14-49"></a>main <span class="op">(</span><span class="dt">int</span> argc<span class="op">,</span> <span class="dt">char</span> <span class="op">**</span>argv<span class="op">)</span> <span class="op">{</span></span>
<span id="cb14-50"><a href="#cb14-50"></a> GtkApplication <span class="op">*</span>app<span class="op">;</span></span>
<span id="cb14-51"><a href="#cb14-51"></a> <span class="dt">int</span> stat<span class="op">;</span></span>
<span id="cb14-52"><a href="#cb14-52"></a></span>
<span id="cb14-53"><a href="#cb14-53"></a> app <span class="op">=</span> gtk_application_new <span class="op">(</span>APPLICATION_ID<span class="op">,</span> G_APPLICATION_DEFAULT_FLAGS<span class="op">);</span></span>
<span id="cb14-54"><a href="#cb14-54"></a></span>
<span id="cb14-55"><a href="#cb14-55"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">&quot;startup&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>app_startup<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb14-56"><a href="#cb14-56"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">&quot;activate&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>app_activate<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb14-57"><a href="#cb14-57"></a></span>
<span id="cb14-58"><a href="#cb14-58"></a> stat <span class="op">=</span>g_application_run <span class="op">(</span>G_APPLICATION <span class="op">(</span>app<span class="op">),</span> argc<span class="op">,</span> argv<span class="op">);</span></span>
<span id="cb14-59"><a href="#cb14-59"></a> g_object_unref <span class="op">(</span>app<span class="op">);</span></span>
<span id="cb14-60"><a href="#cb14-60"></a> <span class="cf">return</span> stat<span class="op">;</span></span>
<span id="cb14-61"><a href="#cb14-61"></a><span class="op">}</span></span></code></pre></div>
<div class="sourceCode" id="cb12"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb12-1"><a href="#cb12-1"></a><span class="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span>
<span id="cb12-2"><a href="#cb12-2"></a></span>
<span id="cb12-3"><a href="#cb12-3"></a>GtkExpressionWatch <span class="op">*</span>watch<span class="op">;</span></span>
<span id="cb12-4"><a href="#cb12-4"></a></span>
<span id="cb12-5"><a href="#cb12-5"></a><span class="dt">static</span> <span class="dt">int</span></span>
<span id="cb12-6"><a href="#cb12-6"></a>f2i <span class="op">(</span>GObject <span class="op">*</span>object<span class="op">,</span> <span class="dt">double</span> d<span class="op">)</span> <span class="op">{</span></span>
<span id="cb12-7"><a href="#cb12-7"></a> <span class="cf">return</span> <span class="op">(</span><span class="dt">int</span><span class="op">)</span> d<span class="op">;</span></span>
<span id="cb12-8"><a href="#cb12-8"></a><span class="op">}</span></span>
<span id="cb12-9"><a href="#cb12-9"></a></span>
<span id="cb12-10"><a href="#cb12-10"></a><span class="dt">static</span> <span class="dt">int</span></span>
<span id="cb12-11"><a href="#cb12-11"></a>close_request_cb <span class="op">(</span>GtkWindow <span class="op">*</span>win<span class="op">)</span> <span class="op">{</span></span>
<span id="cb12-12"><a href="#cb12-12"></a> gtk_expression_watch_unwatch <span class="op">(</span>watch<span class="op">);</span></span>
<span id="cb12-13"><a href="#cb12-13"></a> <span class="cf">return</span> false<span class="op">;</span></span>
<span id="cb12-14"><a href="#cb12-14"></a><span class="op">}</span></span>
<span id="cb12-15"><a href="#cb12-15"></a></span>
<span id="cb12-16"><a href="#cb12-16"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb12-17"><a href="#cb12-17"></a>app_activate <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
<span id="cb12-18"><a href="#cb12-18"></a> GtkApplication <span class="op">*</span>app <span class="op">=</span> GTK_APPLICATION <span class="op">(</span>application<span class="op">);</span></span>
<span id="cb12-19"><a href="#cb12-19"></a> gtk_window_present <span class="op">(</span>gtk_application_get_active_window<span class="op">(</span>app<span class="op">));</span></span>
<span id="cb12-20"><a href="#cb12-20"></a><span class="op">}</span></span>
<span id="cb12-21"><a href="#cb12-21"></a></span>
<span id="cb12-22"><a href="#cb12-22"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb12-23"><a href="#cb12-23"></a>app_startup <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
<span id="cb12-24"><a href="#cb12-24"></a> GtkApplication <span class="op">*</span>app <span class="op">=</span> GTK_APPLICATION <span class="op">(</span>application<span class="op">);</span></span>
<span id="cb12-25"><a href="#cb12-25"></a> GtkBuilder <span class="op">*</span>build<span class="op">;</span></span>
<span id="cb12-26"><a href="#cb12-26"></a> GtkWidget <span class="op">*</span>win<span class="op">,</span> <span class="op">*</span>label<span class="op">;</span></span>
<span id="cb12-27"><a href="#cb12-27"></a> GtkAdjustment <span class="op">*</span>adjustment<span class="op">;</span></span>
<span id="cb12-28"><a href="#cb12-28"></a> GtkExpression <span class="op">*</span>expression<span class="op">,</span> <span class="op">*</span>params<span class="op">[</span><span class="dv">1</span><span class="op">];</span></span>
<span id="cb12-29"><a href="#cb12-29"></a></span>
<span id="cb12-30"><a href="#cb12-30"></a> <span class="co">/* Builds a window with exp.ui resource */</span></span>
<span id="cb12-31"><a href="#cb12-31"></a> build <span class="op">=</span> gtk_builder_new_from_resource <span class="op">(</span><span class="st">&quot;/com/github/ToshioCP/exp/exp_bind.ui&quot;</span><span class="op">);</span></span>
<span id="cb12-32"><a href="#cb12-32"></a> win <span class="op">=</span> GTK_WIDGET <span class="op">(</span>gtk_builder_get_object <span class="op">(</span>build<span class="op">,</span> <span class="st">&quot;win&quot;</span><span class="op">));</span></span>
<span id="cb12-33"><a href="#cb12-33"></a> label <span class="op">=</span> GTK_WIDGET <span class="op">(</span>gtk_builder_get_object <span class="op">(</span>build<span class="op">,</span> <span class="st">&quot;label&quot;</span><span class="op">));</span></span>
<span id="cb12-34"><a href="#cb12-34"></a> <span class="co">// scale = GTK_WIDGET (gtk_builder_get_object (build, &quot;scale&quot;));</span></span>
<span id="cb12-35"><a href="#cb12-35"></a> adjustment <span class="op">=</span> GTK_ADJUSTMENT <span class="op">(</span>gtk_builder_get_object <span class="op">(</span>build<span class="op">,</span> <span class="st">&quot;adjustment&quot;</span><span class="op">));</span></span>
<span id="cb12-36"><a href="#cb12-36"></a> gtk_window_set_application <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> app<span class="op">);</span></span>
<span id="cb12-37"><a href="#cb12-37"></a> g_signal_connect <span class="op">(</span>win<span class="op">,</span> <span class="st">&quot;close-request&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>close_request_cb<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb12-38"><a href="#cb12-38"></a> g_object_unref <span class="op">(</span>build<span class="op">);</span></span>
<span id="cb12-39"><a href="#cb12-39"></a></span>
<span id="cb12-40"><a href="#cb12-40"></a> <span class="co">/* GtkExpressionWatch */</span></span>
<span id="cb12-41"><a href="#cb12-41"></a> params<span class="op">[</span><span class="dv">0</span><span class="op">]</span> <span class="op">=</span> gtk_property_expression_new <span class="op">(</span>GTK_TYPE_ADJUSTMENT<span class="op">,</span> NULL<span class="op">,</span> <span class="st">&quot;value&quot;</span><span class="op">);</span></span>
<span id="cb12-42"><a href="#cb12-42"></a> expression <span class="op">=</span> gtk_cclosure_expression_new <span class="op">(</span>G_TYPE_INT<span class="op">,</span> NULL<span class="op">,</span> <span class="dv">1</span><span class="op">,</span> params<span class="op">,</span> G_CALLBACK <span class="op">(</span>f2i<span class="op">),</span> NULL<span class="op">,</span> NULL<span class="op">);</span></span>
<span id="cb12-43"><a href="#cb12-43"></a> watch <span class="op">=</span> gtk_expression_bind <span class="op">(</span>expression<span class="op">,</span> label<span class="op">,</span> <span class="st">&quot;label&quot;</span><span class="op">,</span> adjustment<span class="op">);</span> <span class="co">/* watch takes the ownership of the expression. */</span></span>
<span id="cb12-44"><a href="#cb12-44"></a><span class="op">}</span></span>
<span id="cb12-45"><a href="#cb12-45"></a></span>
<span id="cb12-46"><a href="#cb12-46"></a><span class="pp">#define APPLICATION_ID &quot;com.github.ToshioCP.exp_watch&quot;</span></span>
<span id="cb12-47"><a href="#cb12-47"></a></span>
<span id="cb12-48"><a href="#cb12-48"></a><span class="dt">int</span></span>
<span id="cb12-49"><a href="#cb12-49"></a>main <span class="op">(</span><span class="dt">int</span> argc<span class="op">,</span> <span class="dt">char</span> <span class="op">**</span>argv<span class="op">)</span> <span class="op">{</span></span>
<span id="cb12-50"><a href="#cb12-50"></a> GtkApplication <span class="op">*</span>app<span class="op">;</span></span>
<span id="cb12-51"><a href="#cb12-51"></a> <span class="dt">int</span> stat<span class="op">;</span></span>
<span id="cb12-52"><a href="#cb12-52"></a></span>
<span id="cb12-53"><a href="#cb12-53"></a> app <span class="op">=</span> gtk_application_new <span class="op">(</span>APPLICATION_ID<span class="op">,</span> G_APPLICATION_DEFAULT_FLAGS<span class="op">);</span></span>
<span id="cb12-54"><a href="#cb12-54"></a></span>
<span id="cb12-55"><a href="#cb12-55"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">&quot;startup&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>app_startup<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb12-56"><a href="#cb12-56"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">&quot;activate&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>app_activate<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb12-57"><a href="#cb12-57"></a></span>
<span id="cb12-58"><a href="#cb12-58"></a> stat <span class="op">=</span>g_application_run <span class="op">(</span>G_APPLICATION <span class="op">(</span>app<span class="op">),</span> argc<span class="op">,</span> argv<span class="op">);</span></span>
<span id="cb12-59"><a href="#cb12-59"></a> g_object_unref <span class="op">(</span>app<span class="op">);</span></span>
<span id="cb12-60"><a href="#cb12-60"></a> <span class="cf">return</span> stat<span class="op">;</span></span>
<span id="cb12-61"><a href="#cb12-61"></a><span class="op">}</span></span></code></pre></div>
<p>The point of the program is:</p>
<ul>
<li>41-42: Two expressions are defined. One is a property expression and
the other is a closure expression. The property expression look up the
“value”property of the adjustment instance. The closure expression just
the other is a closure expression. The property expression looks up the
“value” property of the adjustment instance. The closure expression just
converts the double into an integer.</li>
<li>43: <code>gtk_expression_bind</code> binds the label property of the
GtkLabel instance to the integer returned by the closure expression. It
@ -603,24 +570,23 @@ is destroyed.</li>
<code>close_request_cb</code>. This signal is emitted when the close
button is clicked. The handler is called just before the window closes.
It is the right moment to make the GtkExpressionWatch unwatched.</li>
<li>10-14: “close-request” signal handler.
<li>10-14: “close-request” signal handler. The function
<code>gtk_expression_watch_unwatch (watch)</code> makes the watch stop
watching the expression. It releases the expression and calls
<code>gtk_expression_watch_unref (watch)</code> in it.</li>
watching the expression. It also releases the expression.</li>
</ul>
<p>If you want to bind a property to an expression,
<code>gtk_expression_bind</code> is the best choice. You can do it with
<code>gtk_expression_watch</code> function, but it is less suitable.</p>
<h3 id="gtk_expression_watch-function">gtk_expression_watch
function</h3>
<div class="sourceCode" id="cb15"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a>GtkExpressionWatch<span class="op">*</span></span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a>gtk_expression_watch <span class="op">(</span></span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true" tabindex="-1"></a> GtkExpression<span class="op">*</span> self<span class="op">,</span></span>
<span id="cb15-4"><a href="#cb15-4" aria-hidden="true" tabindex="-1"></a> GObject<span class="op">*</span> this_<span class="op">,</span></span>
<span id="cb15-5"><a href="#cb15-5" aria-hidden="true" tabindex="-1"></a> GtkExpressionNotify notify<span class="op">,</span></span>
<span id="cb15-6"><a href="#cb15-6" aria-hidden="true" tabindex="-1"></a> gpointer user_data<span class="op">,</span></span>
<span id="cb15-7"><a href="#cb15-7" aria-hidden="true" tabindex="-1"></a> GDestroyNotify user_destroy</span>
<span id="cb15-8"><a href="#cb15-8" aria-hidden="true" tabindex="-1"></a><span class="op">)</span></span></code></pre></div>
<div class="sourceCode" id="cb13"><pre class="sourceCode C"><code class="sourceCode c"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a>GtkExpressionWatch<span class="op">*</span></span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a>gtk_expression_watch <span class="op">(</span></span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a> GtkExpression<span class="op">*</span> self<span class="op">,</span></span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a> GObject<span class="op">*</span> this_<span class="op">,</span></span>
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a> GtkExpressionNotify notify<span class="op">,</span></span>
<span id="cb13-6"><a href="#cb13-6" aria-hidden="true" tabindex="-1"></a> gpointer user_data<span class="op">,</span></span>
<span id="cb13-7"><a href="#cb13-7" aria-hidden="true" tabindex="-1"></a> GDestroyNotify user_destroy</span>
<span id="cb13-8"><a href="#cb13-8" aria-hidden="true" tabindex="-1"></a><span class="op">)</span></span></code></pre></div>
<p>The function doesnt take the ownership of the expression. It differs
from <code>gtk_expression_bind</code>. So, you need to release the
expression when it is useless. It creates a GtkExpressionWatch
@ -630,10 +596,10 @@ to give it to the callback. The last parameter is a function to destroy
the <code>user_data</code> when the watch is unwatched. Put NULL if you
dont need them.</p>
<p>Notify callback has the following format.</p>
<div class="sourceCode" id="cb16"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span></span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a>notify <span class="op">(</span></span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a> gpointer user_data</span>
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true" tabindex="-1"></a><span class="op">)</span></span></code></pre></div>
<div class="sourceCode" id="cb14"><pre class="sourceCode C"><code class="sourceCode c"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span></span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a>notify <span class="op">(</span></span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a> gpointer user_data</span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a><span class="op">)</span></span></code></pre></div>
<p>This function is used to do something when the value of the
expression changes. But if you want to bind a property to the value, use
<code>gtk_expression_bind</code> instead.</p>
@ -646,63 +612,63 @@ window to the standard output.</p>
</figure>
<p>When you resize the window, the width is displayed in the
terminal.</p>
<div class="sourceCode" id="cb17"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb17-1"><a href="#cb17-1"></a><span class="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span>
<span id="cb17-2"><a href="#cb17-2"></a></span>
<span id="cb17-3"><a href="#cb17-3"></a>GtkExpression <span class="op">*</span>expression<span class="op">;</span></span>
<span id="cb17-4"><a href="#cb17-4"></a>GtkExpressionWatch <span class="op">*</span>watch<span class="op">;</span></span>
<span id="cb17-5"><a href="#cb17-5"></a></span>
<span id="cb17-6"><a href="#cb17-6"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb17-7"><a href="#cb17-7"></a>notify <span class="op">(</span>gpointer user_data<span class="op">)</span> <span class="op">{</span></span>
<span id="cb17-8"><a href="#cb17-8"></a> GValue value <span class="op">=</span> G_VALUE_INIT<span class="op">;</span></span>
<span id="cb17-9"><a href="#cb17-9"></a></span>
<span id="cb17-10"><a href="#cb17-10"></a> <span class="cf">if</span> <span class="op">(</span>gtk_expression_watch_evaluate <span class="op">(</span>watch<span class="op">,</span> <span class="op">&amp;</span>value<span class="op">))</span></span>
<span id="cb17-11"><a href="#cb17-11"></a> g_print <span class="op">(</span><span class="st">&quot;width = %d</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">,</span> g_value_get_int <span class="op">(&amp;</span>value<span class="op">));</span></span>
<span id="cb17-12"><a href="#cb17-12"></a> <span class="cf">else</span></span>
<span id="cb17-13"><a href="#cb17-13"></a> g_print <span class="op">(</span><span class="st">&quot;evaluation failed.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb17-14"><a href="#cb17-14"></a><span class="op">}</span></span>
<span id="cb17-15"><a href="#cb17-15"></a></span>
<span id="cb17-16"><a href="#cb17-16"></a><span class="dt">static</span> <span class="dt">int</span></span>
<span id="cb17-17"><a href="#cb17-17"></a>close_request_cb <span class="op">(</span>GtkWindow <span class="op">*</span>win<span class="op">)</span> <span class="op">{</span></span>
<span id="cb17-18"><a href="#cb17-18"></a> gtk_expression_watch_unwatch <span class="op">(</span>watch<span class="op">);</span></span>
<span id="cb17-19"><a href="#cb17-19"></a> gtk_expression_unref <span class="op">(</span>expression<span class="op">);</span></span>
<span id="cb17-20"><a href="#cb17-20"></a> <span class="cf">return</span> false<span class="op">;</span></span>
<span id="cb17-21"><a href="#cb17-21"></a><span class="op">}</span></span>
<span id="cb17-22"><a href="#cb17-22"></a></span>
<span id="cb17-23"><a href="#cb17-23"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb17-24"><a href="#cb17-24"></a>app_activate <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
<span id="cb17-25"><a href="#cb17-25"></a> GtkApplication <span class="op">*</span>app <span class="op">=</span> GTK_APPLICATION <span class="op">(</span>application<span class="op">);</span></span>
<span id="cb17-26"><a href="#cb17-26"></a> gtk_window_present <span class="op">(</span>gtk_application_get_active_window<span class="op">(</span>app<span class="op">));</span></span>
<span id="cb17-27"><a href="#cb17-27"></a><span class="op">}</span></span>
<span id="cb17-28"><a href="#cb17-28"></a></span>
<span id="cb17-29"><a href="#cb17-29"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb17-30"><a href="#cb17-30"></a>app_startup <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
<span id="cb17-31"><a href="#cb17-31"></a> GtkApplication <span class="op">*</span>app <span class="op">=</span> GTK_APPLICATION <span class="op">(</span>application<span class="op">);</span></span>
<span id="cb17-32"><a href="#cb17-32"></a> GtkWidget <span class="op">*</span>win<span class="op">;</span></span>
<span id="cb17-33"><a href="#cb17-33"></a></span>
<span id="cb17-34"><a href="#cb17-34"></a> win <span class="op">=</span> GTK_WIDGET <span class="op">(</span>gtk_application_window_new <span class="op">(</span>app<span class="op">));</span></span>
<span id="cb17-35"><a href="#cb17-35"></a> g_signal_connect <span class="op">(</span>win<span class="op">,</span> <span class="st">&quot;close-request&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>close_request_cb<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb17-36"><a href="#cb17-36"></a></span>
<span id="cb17-37"><a href="#cb17-37"></a> expression <span class="op">=</span> gtk_property_expression_new <span class="op">(</span>GTK_TYPE_WINDOW<span class="op">,</span> NULL<span class="op">,</span> <span class="st">&quot;default-width&quot;</span><span class="op">);</span></span>
<span id="cb17-38"><a href="#cb17-38"></a> watch <span class="op">=</span> gtk_expression_watch <span class="op">(</span>expression<span class="op">,</span> win<span class="op">,</span> notify<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">);</span></span>
<span id="cb17-39"><a href="#cb17-39"></a><span class="op">}</span></span>
<span id="cb17-40"><a href="#cb17-40"></a></span>
<span id="cb17-41"><a href="#cb17-41"></a><span class="pp">#define APPLICATION_ID &quot;com.github.ToshioCP.exp_watch&quot;</span></span>
<span id="cb17-42"><a href="#cb17-42"></a></span>
<span id="cb17-43"><a href="#cb17-43"></a><span class="dt">int</span></span>
<span id="cb17-44"><a href="#cb17-44"></a>main <span class="op">(</span><span class="dt">int</span> argc<span class="op">,</span> <span class="dt">char</span> <span class="op">**</span>argv<span class="op">)</span> <span class="op">{</span></span>
<span id="cb17-45"><a href="#cb17-45"></a> GtkApplication <span class="op">*</span>app<span class="op">;</span></span>
<span id="cb17-46"><a href="#cb17-46"></a> <span class="dt">int</span> stat<span class="op">;</span></span>
<span id="cb17-47"><a href="#cb17-47"></a></span>
<span id="cb17-48"><a href="#cb17-48"></a> app <span class="op">=</span> gtk_application_new <span class="op">(</span>APPLICATION_ID<span class="op">,</span> G_APPLICATION_DEFAULT_FLAGS<span class="op">);</span></span>
<span id="cb17-49"><a href="#cb17-49"></a></span>
<span id="cb17-50"><a href="#cb17-50"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">&quot;startup&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>app_startup<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb17-51"><a href="#cb17-51"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">&quot;activate&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>app_activate<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb17-52"><a href="#cb17-52"></a></span>
<span id="cb17-53"><a href="#cb17-53"></a> stat <span class="op">=</span>g_application_run <span class="op">(</span>G_APPLICATION <span class="op">(</span>app<span class="op">),</span> argc<span class="op">,</span> argv<span class="op">);</span></span>
<span id="cb17-54"><a href="#cb17-54"></a> g_object_unref <span class="op">(</span>app<span class="op">);</span></span>
<span id="cb17-55"><a href="#cb17-55"></a> <span class="cf">return</span> stat<span class="op">;</span></span>
<span id="cb17-56"><a href="#cb17-56"></a><span class="op">}</span></span></code></pre></div>
<div class="sourceCode" id="cb15"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb15-1"><a href="#cb15-1"></a><span class="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span>
<span id="cb15-2"><a href="#cb15-2"></a></span>
<span id="cb15-3"><a href="#cb15-3"></a>GtkExpression <span class="op">*</span>expression<span class="op">;</span></span>
<span id="cb15-4"><a href="#cb15-4"></a>GtkExpressionWatch <span class="op">*</span>watch<span class="op">;</span></span>
<span id="cb15-5"><a href="#cb15-5"></a></span>
<span id="cb15-6"><a href="#cb15-6"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb15-7"><a href="#cb15-7"></a>notify <span class="op">(</span>gpointer user_data<span class="op">)</span> <span class="op">{</span></span>
<span id="cb15-8"><a href="#cb15-8"></a> GValue value <span class="op">=</span> G_VALUE_INIT<span class="op">;</span></span>
<span id="cb15-9"><a href="#cb15-9"></a></span>
<span id="cb15-10"><a href="#cb15-10"></a> <span class="cf">if</span> <span class="op">(</span>gtk_expression_watch_evaluate <span class="op">(</span>watch<span class="op">,</span> <span class="op">&amp;</span>value<span class="op">))</span></span>
<span id="cb15-11"><a href="#cb15-11"></a> g_print <span class="op">(</span><span class="st">&quot;width = %d</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">,</span> g_value_get_int <span class="op">(&amp;</span>value<span class="op">));</span></span>
<span id="cb15-12"><a href="#cb15-12"></a> <span class="cf">else</span></span>
<span id="cb15-13"><a href="#cb15-13"></a> g_print <span class="op">(</span><span class="st">&quot;evaluation failed.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb15-14"><a href="#cb15-14"></a><span class="op">}</span></span>
<span id="cb15-15"><a href="#cb15-15"></a></span>
<span id="cb15-16"><a href="#cb15-16"></a><span class="dt">static</span> <span class="dt">int</span></span>
<span id="cb15-17"><a href="#cb15-17"></a>close_request_cb <span class="op">(</span>GtkWindow <span class="op">*</span>win<span class="op">)</span> <span class="op">{</span></span>
<span id="cb15-18"><a href="#cb15-18"></a> gtk_expression_watch_unwatch <span class="op">(</span>watch<span class="op">);</span></span>
<span id="cb15-19"><a href="#cb15-19"></a> gtk_expression_unref <span class="op">(</span>expression<span class="op">);</span></span>
<span id="cb15-20"><a href="#cb15-20"></a> <span class="cf">return</span> false<span class="op">;</span></span>
<span id="cb15-21"><a href="#cb15-21"></a><span class="op">}</span></span>
<span id="cb15-22"><a href="#cb15-22"></a></span>
<span id="cb15-23"><a href="#cb15-23"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb15-24"><a href="#cb15-24"></a>app_activate <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
<span id="cb15-25"><a href="#cb15-25"></a> GtkApplication <span class="op">*</span>app <span class="op">=</span> GTK_APPLICATION <span class="op">(</span>application<span class="op">);</span></span>
<span id="cb15-26"><a href="#cb15-26"></a> gtk_window_present <span class="op">(</span>gtk_application_get_active_window<span class="op">(</span>app<span class="op">));</span></span>
<span id="cb15-27"><a href="#cb15-27"></a><span class="op">}</span></span>
<span id="cb15-28"><a href="#cb15-28"></a></span>
<span id="cb15-29"><a href="#cb15-29"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb15-30"><a href="#cb15-30"></a>app_startup <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
<span id="cb15-31"><a href="#cb15-31"></a> GtkApplication <span class="op">*</span>app <span class="op">=</span> GTK_APPLICATION <span class="op">(</span>application<span class="op">);</span></span>
<span id="cb15-32"><a href="#cb15-32"></a> GtkWidget <span class="op">*</span>win<span class="op">;</span></span>
<span id="cb15-33"><a href="#cb15-33"></a></span>
<span id="cb15-34"><a href="#cb15-34"></a> win <span class="op">=</span> GTK_WIDGET <span class="op">(</span>gtk_application_window_new <span class="op">(</span>app<span class="op">));</span></span>
<span id="cb15-35"><a href="#cb15-35"></a> g_signal_connect <span class="op">(</span>win<span class="op">,</span> <span class="st">&quot;close-request&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>close_request_cb<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb15-36"><a href="#cb15-36"></a></span>
<span id="cb15-37"><a href="#cb15-37"></a> expression <span class="op">=</span> gtk_property_expression_new <span class="op">(</span>GTK_TYPE_WINDOW<span class="op">,</span> NULL<span class="op">,</span> <span class="st">&quot;default-width&quot;</span><span class="op">);</span></span>
<span id="cb15-38"><a href="#cb15-38"></a> watch <span class="op">=</span> gtk_expression_watch <span class="op">(</span>expression<span class="op">,</span> win<span class="op">,</span> notify<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">);</span></span>
<span id="cb15-39"><a href="#cb15-39"></a><span class="op">}</span></span>
<span id="cb15-40"><a href="#cb15-40"></a></span>
<span id="cb15-41"><a href="#cb15-41"></a><span class="pp">#define APPLICATION_ID &quot;com.github.ToshioCP.exp_watch&quot;</span></span>
<span id="cb15-42"><a href="#cb15-42"></a></span>
<span id="cb15-43"><a href="#cb15-43"></a><span class="dt">int</span></span>
<span id="cb15-44"><a href="#cb15-44"></a>main <span class="op">(</span><span class="dt">int</span> argc<span class="op">,</span> <span class="dt">char</span> <span class="op">**</span>argv<span class="op">)</span> <span class="op">{</span></span>
<span id="cb15-45"><a href="#cb15-45"></a> GtkApplication <span class="op">*</span>app<span class="op">;</span></span>
<span id="cb15-46"><a href="#cb15-46"></a> <span class="dt">int</span> stat<span class="op">;</span></span>
<span id="cb15-47"><a href="#cb15-47"></a></span>
<span id="cb15-48"><a href="#cb15-48"></a> app <span class="op">=</span> gtk_application_new <span class="op">(</span>APPLICATION_ID<span class="op">,</span> G_APPLICATION_DEFAULT_FLAGS<span class="op">);</span></span>
<span id="cb15-49"><a href="#cb15-49"></a></span>
<span id="cb15-50"><a href="#cb15-50"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">&quot;startup&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>app_startup<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb15-51"><a href="#cb15-51"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">&quot;activate&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>app_activate<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb15-52"><a href="#cb15-52"></a></span>
<span id="cb15-53"><a href="#cb15-53"></a> stat <span class="op">=</span>g_application_run <span class="op">(</span>G_APPLICATION <span class="op">(</span>app<span class="op">),</span> argc<span class="op">,</span> argv<span class="op">);</span></span>
<span id="cb15-54"><a href="#cb15-54"></a> g_object_unref <span class="op">(</span>app<span class="op">);</span></span>
<span id="cb15-55"><a href="#cb15-55"></a> <span class="cf">return</span> stat<span class="op">;</span></span>
<span id="cb15-56"><a href="#cb15-56"></a><span class="op">}</span></span></code></pre></div>
<ul>
<li>37: A property expression looks up the “default-width” property of
the window.</li>
@ -738,37 +704,36 @@ expressions.</li>
content of an object tag. Name attribute specifies the property name of
the object. The content is an expression.</li>
</ul>
<div class="sourceCode" id="cb18"><pre
class="sourceCode xml"><code class="sourceCode xml"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a>&lt;<span class="kw">constant</span><span class="ot"> type=</span><span class="st">&quot;gchararray&quot;</span>&gt;Hello world&lt;/<span class="kw">constant</span>&gt;</span>
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a>&lt;<span class="kw">lookup</span><span class="ot"> name=</span><span class="st">&quot;label&quot;</span><span class="ot"> type=</span><span class="st">&quot;GtkLabel&quot;</span>&gt;label&lt;/<span class="kw">lookup</span>&gt;</span>
<span id="cb18-3"><a href="#cb18-3" aria-hidden="true" tabindex="-1"></a>&lt;<span class="kw">closure</span><span class="ot"> type=</span><span class="st">&quot;gint&quot;</span><span class="ot"> function=</span><span class="st">&quot;callback_function&quot;</span>&gt;&lt;/<span class="kw">closure</span>&gt;</span>
<span id="cb18-4"><a href="#cb18-4" aria-hidden="true" tabindex="-1"></a>&lt;<span class="kw">bind</span><span class="ot"> name=</span><span class="st">&quot;label&quot;</span>&gt;</span>
<span id="cb18-5"><a href="#cb18-5" aria-hidden="true" tabindex="-1"></a> &lt;<span class="kw">lookup</span><span class="ot"> name=</span><span class="st">&quot;default-width&quot;</span>&gt;win&lt;/<span class="kw">lookup</span>&gt;</span>
<span id="cb18-6"><a href="#cb18-6" aria-hidden="true" tabindex="-1"></a>&lt;/<span class="kw">bind</span>&gt;</span></code></pre></div>
<div class="sourceCode" id="cb16"><pre
class="sourceCode xml"><code class="sourceCode xml"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a>&lt;<span class="kw">constant</span><span class="ot"> type=</span><span class="st">&quot;gchararray&quot;</span>&gt;Hello world&lt;/<span class="kw">constant</span>&gt;</span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a>&lt;<span class="kw">lookup</span><span class="ot"> name=</span><span class="st">&quot;label&quot;</span><span class="ot"> type=</span><span class="st">&quot;GtkLabel&quot;</span>&gt;label&lt;/<span class="kw">lookup</span>&gt;</span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a>&lt;<span class="kw">closure</span><span class="ot"> type=</span><span class="st">&quot;gint&quot;</span><span class="ot"> function=</span><span class="st">&quot;callback_function&quot;</span>&gt;&lt;/<span class="kw">closure</span>&gt;</span>
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true" tabindex="-1"></a>&lt;<span class="kw">bind</span><span class="ot"> name=</span><span class="st">&quot;label&quot;</span>&gt;</span>
<span id="cb16-5"><a href="#cb16-5" aria-hidden="true" tabindex="-1"></a> &lt;<span class="kw">lookup</span><span class="ot"> name=</span><span class="st">&quot;default-width&quot;</span>&gt;win&lt;/<span class="kw">lookup</span>&gt;</span>
<span id="cb16-6"><a href="#cb16-6" aria-hidden="true" tabindex="-1"></a>&lt;/<span class="kw">bind</span>&gt;</span></code></pre></div>
<p>These tags are usually used for GtkBuilderListItemFactory.</p>
<div class="sourceCode" id="cb19"><pre
class="sourceCode xml"><code class="sourceCode xml"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a>&lt;<span class="kw">interface</span>&gt;</span>
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a> &lt;<span class="kw">template</span><span class="ot"> class=</span><span class="st">&quot;GtkListItem&quot;</span>&gt;</span>
<span id="cb19-3"><a href="#cb19-3" aria-hidden="true" tabindex="-1"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;child&quot;</span>&gt;</span>
<span id="cb19-4"><a href="#cb19-4" aria-hidden="true" tabindex="-1"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkLabel&quot;</span>&gt;</span>
<span id="cb19-5"><a href="#cb19-5" aria-hidden="true" tabindex="-1"></a> &lt;<span class="kw">binding</span><span class="ot"> name=</span><span class="st">&quot;label&quot;</span>&gt;</span>
<span id="cb19-6"><a href="#cb19-6" aria-hidden="true" tabindex="-1"></a> &lt;<span class="kw">lookup</span><span class="ot"> name=</span><span class="st">&quot;name&quot;</span><span class="ot"> type=</span><span class="st">&quot;string&quot;</span>&gt;</span>
<span id="cb19-7"><a href="#cb19-7" aria-hidden="true" tabindex="-1"></a> &lt;<span class="kw">lookup</span><span class="ot"> name=</span><span class="st">&quot;item&quot;</span>&gt;GtkListItem&lt;/<span class="kw">lookup</span>&gt;</span>
<span id="cb19-8"><a href="#cb19-8" aria-hidden="true" tabindex="-1"></a> &lt;/<span class="kw">lookup</span>&gt;</span>
<span id="cb19-9"><a href="#cb19-9" aria-hidden="true" tabindex="-1"></a> &lt;/<span class="kw">binding</span>&gt;</span>
<span id="cb19-10"><a href="#cb19-10" aria-hidden="true" tabindex="-1"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb19-11"><a href="#cb19-11" aria-hidden="true" tabindex="-1"></a> &lt;/<span class="kw">property</span>&gt;</span>
<span id="cb19-12"><a href="#cb19-12" aria-hidden="true" tabindex="-1"></a> &lt;/<span class="kw">template</span>&gt;</span>
<span id="cb19-13"><a href="#cb19-13" aria-hidden="true" tabindex="-1"></a>&lt;/<span class="kw">interface</span>&gt;</span></code></pre></div>
<div class="sourceCode" id="cb17"><pre
class="sourceCode xml"><code class="sourceCode xml"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a>&lt;<span class="kw">interface</span>&gt;</span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a> &lt;<span class="kw">template</span><span class="ot"> class=</span><span class="st">&quot;GtkListItem&quot;</span>&gt;</span>
<span id="cb17-3"><a href="#cb17-3" aria-hidden="true" tabindex="-1"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;child&quot;</span>&gt;</span>
<span id="cb17-4"><a href="#cb17-4" aria-hidden="true" tabindex="-1"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkLabel&quot;</span>&gt;</span>
<span id="cb17-5"><a href="#cb17-5" aria-hidden="true" tabindex="-1"></a> &lt;<span class="kw">binding</span><span class="ot"> name=</span><span class="st">&quot;label&quot;</span>&gt;</span>
<span id="cb17-6"><a href="#cb17-6" aria-hidden="true" tabindex="-1"></a> &lt;<span class="kw">lookup</span><span class="ot"> name=</span><span class="st">&quot;string&quot;</span><span class="ot"> type=</span><span class="st">&quot;GtkStringObject&quot;</span>&gt;</span>
<span id="cb17-7"><a href="#cb17-7" aria-hidden="true" tabindex="-1"></a> &lt;<span class="kw">lookup</span><span class="ot"> name=</span><span class="st">&quot;item&quot;</span>&gt;GtkListItem&lt;/<span class="kw">lookup</span>&gt;</span>
<span id="cb17-8"><a href="#cb17-8" aria-hidden="true" tabindex="-1"></a> &lt;/<span class="kw">lookup</span>&gt;</span>
<span id="cb17-9"><a href="#cb17-9" aria-hidden="true" tabindex="-1"></a> &lt;/<span class="kw">binding</span>&gt;</span>
<span id="cb17-10"><a href="#cb17-10" aria-hidden="true" tabindex="-1"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb17-11"><a href="#cb17-11" aria-hidden="true" tabindex="-1"></a> &lt;/<span class="kw">property</span>&gt;</span>
<span id="cb17-12"><a href="#cb17-12" aria-hidden="true" tabindex="-1"></a> &lt;/<span class="kw">template</span>&gt;</span>
<span id="cb17-13"><a href="#cb17-13" aria-hidden="true" tabindex="-1"></a>&lt;/<span class="kw">interface</span>&gt;</span></code></pre></div>
<p>GtkBuilderListItemFactory uses GtkBuilder to extends the GtkListItem
with the XML data.</p>
<p>In the xml file above, “GtkListItem” is an instance of the
GtkListItem template. It is the this object given to the expressions.
(The information is in the <a href="https://blog.gtk.org/2020/09/">GTK
Development Blog</a>).</p>
<p>GtkBuilderListItemFactory uses GtkBuilder to build the XML data. It
sets the current object of the GtkBuilder to the GtkListItem
instance.</p>
<p>GtkBuilder calls <code>gtk_expression_bind</code> function in the
binding tag analysis. The function sets the this object like this:</p>
<p>GtkBuilder calls <code>gtk_expression_bind</code> function when it
finds a binding tag. The function sets the this object like this:</p>
<ol type="1">
<li>If the binding tag has object attribute, the object will be the
this object.</li>
@ -794,41 +759,41 @@ constant tags are not used so often.</p>
window. If you type characters in the entry, the same characters appear
on the label.</p>
<p>The ui file is as follows.</p>
<div class="sourceCode" id="cb20"><pre
class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><span id="cb20-1"><a href="#cb20-1"></a><span class="fu">&lt;?xml</span><span class="ot"> version=</span><span class="st">&quot;1.0&quot;</span><span class="ot"> encoding=</span><span class="st">&quot;UTF-8&quot;</span><span class="fu">?&gt;</span></span>
<span id="cb20-2"><a href="#cb20-2"></a>&lt;<span class="kw">interface</span>&gt;</span>
<span id="cb20-3"><a href="#cb20-3"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkApplicationWindow&quot;</span><span class="ot"> id=</span><span class="st">&quot;win&quot;</span>&gt;</span>
<span id="cb20-4"><a href="#cb20-4"></a> &lt;<span class="kw">binding</span><span class="ot"> name=</span><span class="st">&quot;title&quot;</span>&gt;</span>
<span id="cb20-5"><a href="#cb20-5"></a> &lt;<span class="kw">closure</span><span class="ot"> type=</span><span class="st">&quot;gchararray&quot;</span><span class="ot"> function=</span><span class="st">&quot;set_title&quot;</span>&gt;</span>
<span id="cb20-6"><a href="#cb20-6"></a> &lt;<span class="kw">lookup</span><span class="ot"> name=</span><span class="st">&quot;default-width&quot;</span><span class="ot"> type=</span><span class="st">&quot;GtkWindow&quot;</span>&gt;&lt;/<span class="kw">lookup</span>&gt;</span>
<span id="cb20-7"><a href="#cb20-7"></a> &lt;<span class="kw">lookup</span><span class="ot"> name=</span><span class="st">&quot;default-height&quot;</span><span class="ot"> type=</span><span class="st">&quot;GtkWindow&quot;</span>&gt;&lt;/<span class="kw">lookup</span>&gt;</span>
<span id="cb20-8"><a href="#cb20-8"></a> &lt;/<span class="kw">closure</span>&gt;</span>
<span id="cb20-9"><a href="#cb20-9"></a> &lt;/<span class="kw">binding</span>&gt;</span>
<span id="cb20-10"><a href="#cb20-10"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;default-width&quot;</span>&gt;600&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb20-11"><a href="#cb20-11"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;default-height&quot;</span>&gt;400&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb20-12"><a href="#cb20-12"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb20-13"><a href="#cb20-13"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkBox&quot;</span>&gt;</span>
<span id="cb20-14"><a href="#cb20-14"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;orientation&quot;</span>&gt;GTK_ORIENTATION_VERTICAL&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb20-15"><a href="#cb20-15"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb20-16"><a href="#cb20-16"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkLabel&quot;</span>&gt;</span>
<span id="cb20-17"><a href="#cb20-17"></a> &lt;<span class="kw">binding</span><span class="ot"> name=</span><span class="st">&quot;label&quot;</span>&gt;</span>
<span id="cb20-18"><a href="#cb20-18"></a> &lt;<span class="kw">lookup</span><span class="ot"> name=</span><span class="st">&quot;text&quot;</span>&gt;</span>
<span id="cb20-19"><a href="#cb20-19"></a> buffer</span>
<span id="cb20-20"><a href="#cb20-20"></a> &lt;/<span class="kw">lookup</span>&gt;</span>
<span id="cb20-21"><a href="#cb20-21"></a> &lt;/<span class="kw">binding</span>&gt;</span>
<span id="cb20-22"><a href="#cb20-22"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb20-23"><a href="#cb20-23"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb20-24"><a href="#cb20-24"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb20-25"><a href="#cb20-25"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkEntry&quot;</span>&gt;</span>
<span id="cb20-26"><a href="#cb20-26"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;buffer&quot;</span>&gt;</span>
<span id="cb20-27"><a href="#cb20-27"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkEntryBuffer&quot;</span><span class="ot"> id=</span><span class="st">&quot;buffer&quot;</span>&gt;&lt;/<span class="kw">object</span>&gt;</span>
<span id="cb20-28"><a href="#cb20-28"></a> &lt;/<span class="kw">property</span>&gt;</span>
<span id="cb20-29"><a href="#cb20-29"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb20-30"><a href="#cb20-30"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb20-31"><a href="#cb20-31"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb20-32"><a href="#cb20-32"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb20-33"><a href="#cb20-33"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb20-34"><a href="#cb20-34"></a>&lt;/<span class="kw">interface</span>&gt;</span></code></pre></div>
<div class="sourceCode" id="cb18"><pre
class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><span id="cb18-1"><a href="#cb18-1"></a><span class="fu">&lt;?xml</span><span class="ot"> version=</span><span class="st">&quot;1.0&quot;</span><span class="ot"> encoding=</span><span class="st">&quot;UTF-8&quot;</span><span class="fu">?&gt;</span></span>
<span id="cb18-2"><a href="#cb18-2"></a>&lt;<span class="kw">interface</span>&gt;</span>
<span id="cb18-3"><a href="#cb18-3"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkApplicationWindow&quot;</span><span class="ot"> id=</span><span class="st">&quot;win&quot;</span>&gt;</span>
<span id="cb18-4"><a href="#cb18-4"></a> &lt;<span class="kw">binding</span><span class="ot"> name=</span><span class="st">&quot;title&quot;</span>&gt;</span>
<span id="cb18-5"><a href="#cb18-5"></a> &lt;<span class="kw">closure</span><span class="ot"> type=</span><span class="st">&quot;gchararray&quot;</span><span class="ot"> function=</span><span class="st">&quot;set_title&quot;</span>&gt;</span>
<span id="cb18-6"><a href="#cb18-6"></a> &lt;<span class="kw">lookup</span><span class="ot"> name=</span><span class="st">&quot;default-width&quot;</span><span class="ot"> type=</span><span class="st">&quot;GtkWindow&quot;</span>&gt;&lt;/<span class="kw">lookup</span>&gt;</span>
<span id="cb18-7"><a href="#cb18-7"></a> &lt;<span class="kw">lookup</span><span class="ot"> name=</span><span class="st">&quot;default-height&quot;</span><span class="ot"> type=</span><span class="st">&quot;GtkWindow&quot;</span>&gt;&lt;/<span class="kw">lookup</span>&gt;</span>
<span id="cb18-8"><a href="#cb18-8"></a> &lt;/<span class="kw">closure</span>&gt;</span>
<span id="cb18-9"><a href="#cb18-9"></a> &lt;/<span class="kw">binding</span>&gt;</span>
<span id="cb18-10"><a href="#cb18-10"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;default-width&quot;</span>&gt;600&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb18-11"><a href="#cb18-11"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;default-height&quot;</span>&gt;400&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb18-12"><a href="#cb18-12"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb18-13"><a href="#cb18-13"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkBox&quot;</span>&gt;</span>
<span id="cb18-14"><a href="#cb18-14"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;orientation&quot;</span>&gt;GTK_ORIENTATION_VERTICAL&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb18-15"><a href="#cb18-15"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb18-16"><a href="#cb18-16"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkLabel&quot;</span>&gt;</span>
<span id="cb18-17"><a href="#cb18-17"></a> &lt;<span class="kw">binding</span><span class="ot"> name=</span><span class="st">&quot;label&quot;</span>&gt;</span>
<span id="cb18-18"><a href="#cb18-18"></a> &lt;<span class="kw">lookup</span><span class="ot"> name=</span><span class="st">&quot;text&quot;</span>&gt;</span>
<span id="cb18-19"><a href="#cb18-19"></a> buffer</span>
<span id="cb18-20"><a href="#cb18-20"></a> &lt;/<span class="kw">lookup</span>&gt;</span>
<span id="cb18-21"><a href="#cb18-21"></a> &lt;/<span class="kw">binding</span>&gt;</span>
<span id="cb18-22"><a href="#cb18-22"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb18-23"><a href="#cb18-23"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb18-24"><a href="#cb18-24"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb18-25"><a href="#cb18-25"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkEntry&quot;</span>&gt;</span>
<span id="cb18-26"><a href="#cb18-26"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;buffer&quot;</span>&gt;</span>
<span id="cb18-27"><a href="#cb18-27"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkEntryBuffer&quot;</span><span class="ot"> id=</span><span class="st">&quot;buffer&quot;</span>&gt;&lt;/<span class="kw">object</span>&gt;</span>
<span id="cb18-28"><a href="#cb18-28"></a> &lt;/<span class="kw">property</span>&gt;</span>
<span id="cb18-29"><a href="#cb18-29"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb18-30"><a href="#cb18-30"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb18-31"><a href="#cb18-31"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb18-32"><a href="#cb18-32"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb18-33"><a href="#cb18-33"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb18-34"><a href="#cb18-34"></a>&lt;/<span class="kw">interface</span>&gt;</span></code></pre></div>
<ul>
<li>4-9: The title property of the main window is bound to a closure
expression. Its callback function <code>set_title</code> is defined in
@ -844,48 +809,48 @@ defined in line 25. If a user types characters in the entry, the same
characters appear on the label.</li>
</ul>
<p>The C source file is as follows.</p>
<div class="sourceCode" id="cb21"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb21-1"><a href="#cb21-1"></a><span class="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span>
<span id="cb21-2"><a href="#cb21-2"></a></span>
<span id="cb21-3"><a href="#cb21-3"></a><span class="dt">char</span> <span class="op">*</span></span>
<span id="cb21-4"><a href="#cb21-4"></a>set_title <span class="op">(</span>GtkWidget <span class="op">*</span>win<span class="op">,</span> <span class="dt">int</span> width<span class="op">,</span> <span class="dt">int</span> height<span class="op">)</span> <span class="op">{</span></span>
<span id="cb21-5"><a href="#cb21-5"></a> <span class="cf">return</span> g_strdup_printf <span class="op">(</span><span class="st">&quot;%d x %d&quot;</span><span class="op">,</span> width<span class="op">,</span> height<span class="op">);</span></span>
<span id="cb21-6"><a href="#cb21-6"></a><span class="op">}</span></span>
<span id="cb21-7"><a href="#cb21-7"></a></span>
<span id="cb21-8"><a href="#cb21-8"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb21-9"><a href="#cb21-9"></a>app_activate <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
<span id="cb21-10"><a href="#cb21-10"></a> GtkApplication <span class="op">*</span>app <span class="op">=</span> GTK_APPLICATION <span class="op">(</span>application<span class="op">);</span></span>
<span id="cb21-11"><a href="#cb21-11"></a> gtk_window_present <span class="op">(</span>gtk_application_get_active_window<span class="op">(</span>app<span class="op">));</span></span>
<span id="cb21-12"><a href="#cb21-12"></a><span class="op">}</span></span>
<span id="cb21-13"><a href="#cb21-13"></a></span>
<span id="cb21-14"><a href="#cb21-14"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb21-15"><a href="#cb21-15"></a>app_startup <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
<span id="cb21-16"><a href="#cb21-16"></a> GtkApplication <span class="op">*</span>app <span class="op">=</span> GTK_APPLICATION <span class="op">(</span>application<span class="op">);</span></span>
<span id="cb21-17"><a href="#cb21-17"></a> GtkBuilder <span class="op">*</span>build<span class="op">;</span></span>
<span id="cb21-18"><a href="#cb21-18"></a> GtkWidget <span class="op">*</span>win<span class="op">;</span></span>
<span id="cb21-19"><a href="#cb21-19"></a></span>
<span id="cb21-20"><a href="#cb21-20"></a> build <span class="op">=</span> gtk_builder_new_from_resource <span class="op">(</span><span class="st">&quot;/com/github/ToshioCP/exp/exp.ui&quot;</span><span class="op">);</span></span>
<span id="cb21-21"><a href="#cb21-21"></a> win <span class="op">=</span> GTK_WIDGET <span class="op">(</span>gtk_builder_get_object <span class="op">(</span>build<span class="op">,</span> <span class="st">&quot;win&quot;</span><span class="op">));</span></span>
<span id="cb21-22"><a href="#cb21-22"></a> gtk_window_set_application <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> app<span class="op">);</span></span>
<span id="cb21-23"><a href="#cb21-23"></a> g_object_unref <span class="op">(</span>build<span class="op">);</span></span>
<span id="cb21-24"><a href="#cb21-24"></a><span class="op">}</span></span>
<span id="cb21-25"><a href="#cb21-25"></a></span>
<span id="cb21-26"><a href="#cb21-26"></a><span class="pp">#define APPLICATION_ID &quot;com.github.ToshioCP.exp&quot;</span></span>
<span id="cb21-27"><a href="#cb21-27"></a></span>
<span id="cb21-28"><a href="#cb21-28"></a><span class="dt">int</span></span>
<span id="cb21-29"><a href="#cb21-29"></a>main <span class="op">(</span><span class="dt">int</span> argc<span class="op">,</span> <span class="dt">char</span> <span class="op">**</span>argv<span class="op">)</span> <span class="op">{</span></span>
<span id="cb21-30"><a href="#cb21-30"></a> GtkApplication <span class="op">*</span>app<span class="op">;</span></span>
<span id="cb21-31"><a href="#cb21-31"></a> <span class="dt">int</span> stat<span class="op">;</span></span>
<span id="cb21-32"><a href="#cb21-32"></a></span>
<span id="cb21-33"><a href="#cb21-33"></a> app <span class="op">=</span> gtk_application_new <span class="op">(</span>APPLICATION_ID<span class="op">,</span> G_APPLICATION_DEFAULT_FLAGS<span class="op">);</span></span>
<span id="cb21-34"><a href="#cb21-34"></a></span>
<span id="cb21-35"><a href="#cb21-35"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">&quot;startup&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>app_startup<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb21-36"><a href="#cb21-36"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">&quot;activate&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>app_activate<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb21-37"><a href="#cb21-37"></a></span>
<span id="cb21-38"><a href="#cb21-38"></a> stat <span class="op">=</span>g_application_run <span class="op">(</span>G_APPLICATION <span class="op">(</span>app<span class="op">),</span> argc<span class="op">,</span> argv<span class="op">);</span></span>
<span id="cb21-39"><a href="#cb21-39"></a> g_object_unref <span class="op">(</span>app<span class="op">);</span></span>
<span id="cb21-40"><a href="#cb21-40"></a> <span class="cf">return</span> stat<span class="op">;</span></span>
<span id="cb21-41"><a href="#cb21-41"></a><span class="op">}</span></span></code></pre></div>
<div class="sourceCode" id="cb19"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb19-1"><a href="#cb19-1"></a><span class="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span>
<span id="cb19-2"><a href="#cb19-2"></a></span>
<span id="cb19-3"><a href="#cb19-3"></a><span class="dt">char</span> <span class="op">*</span></span>
<span id="cb19-4"><a href="#cb19-4"></a>set_title <span class="op">(</span>GtkWidget <span class="op">*</span>win<span class="op">,</span> <span class="dt">int</span> width<span class="op">,</span> <span class="dt">int</span> height<span class="op">)</span> <span class="op">{</span></span>
<span id="cb19-5"><a href="#cb19-5"></a> <span class="cf">return</span> g_strdup_printf <span class="op">(</span><span class="st">&quot;%d x %d&quot;</span><span class="op">,</span> width<span class="op">,</span> height<span class="op">);</span></span>
<span id="cb19-6"><a href="#cb19-6"></a><span class="op">}</span></span>
<span id="cb19-7"><a href="#cb19-7"></a></span>
<span id="cb19-8"><a href="#cb19-8"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb19-9"><a href="#cb19-9"></a>app_activate <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
<span id="cb19-10"><a href="#cb19-10"></a> GtkApplication <span class="op">*</span>app <span class="op">=</span> GTK_APPLICATION <span class="op">(</span>application<span class="op">);</span></span>
<span id="cb19-11"><a href="#cb19-11"></a> gtk_window_present <span class="op">(</span>gtk_application_get_active_window<span class="op">(</span>app<span class="op">));</span></span>
<span id="cb19-12"><a href="#cb19-12"></a><span class="op">}</span></span>
<span id="cb19-13"><a href="#cb19-13"></a></span>
<span id="cb19-14"><a href="#cb19-14"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb19-15"><a href="#cb19-15"></a>app_startup <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
<span id="cb19-16"><a href="#cb19-16"></a> GtkApplication <span class="op">*</span>app <span class="op">=</span> GTK_APPLICATION <span class="op">(</span>application<span class="op">);</span></span>
<span id="cb19-17"><a href="#cb19-17"></a> GtkBuilder <span class="op">*</span>build<span class="op">;</span></span>
<span id="cb19-18"><a href="#cb19-18"></a> GtkWidget <span class="op">*</span>win<span class="op">;</span></span>
<span id="cb19-19"><a href="#cb19-19"></a></span>
<span id="cb19-20"><a href="#cb19-20"></a> build <span class="op">=</span> gtk_builder_new_from_resource <span class="op">(</span><span class="st">&quot;/com/github/ToshioCP/exp/exp.ui&quot;</span><span class="op">);</span></span>
<span id="cb19-21"><a href="#cb19-21"></a> win <span class="op">=</span> GTK_WIDGET <span class="op">(</span>gtk_builder_get_object <span class="op">(</span>build<span class="op">,</span> <span class="st">&quot;win&quot;</span><span class="op">));</span></span>
<span id="cb19-22"><a href="#cb19-22"></a> gtk_window_set_application <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> app<span class="op">);</span></span>
<span id="cb19-23"><a href="#cb19-23"></a> g_object_unref <span class="op">(</span>build<span class="op">);</span></span>
<span id="cb19-24"><a href="#cb19-24"></a><span class="op">}</span></span>
<span id="cb19-25"><a href="#cb19-25"></a></span>
<span id="cb19-26"><a href="#cb19-26"></a><span class="pp">#define APPLICATION_ID &quot;com.github.ToshioCP.exp&quot;</span></span>
<span id="cb19-27"><a href="#cb19-27"></a></span>
<span id="cb19-28"><a href="#cb19-28"></a><span class="dt">int</span></span>
<span id="cb19-29"><a href="#cb19-29"></a>main <span class="op">(</span><span class="dt">int</span> argc<span class="op">,</span> <span class="dt">char</span> <span class="op">**</span>argv<span class="op">)</span> <span class="op">{</span></span>
<span id="cb19-30"><a href="#cb19-30"></a> GtkApplication <span class="op">*</span>app<span class="op">;</span></span>
<span id="cb19-31"><a href="#cb19-31"></a> <span class="dt">int</span> stat<span class="op">;</span></span>
<span id="cb19-32"><a href="#cb19-32"></a></span>
<span id="cb19-33"><a href="#cb19-33"></a> app <span class="op">=</span> gtk_application_new <span class="op">(</span>APPLICATION_ID<span class="op">,</span> G_APPLICATION_DEFAULT_FLAGS<span class="op">);</span></span>
<span id="cb19-34"><a href="#cb19-34"></a></span>
<span id="cb19-35"><a href="#cb19-35"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">&quot;startup&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>app_startup<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb19-36"><a href="#cb19-36"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">&quot;activate&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>app_activate<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb19-37"><a href="#cb19-37"></a></span>
<span id="cb19-38"><a href="#cb19-38"></a> stat <span class="op">=</span>g_application_run <span class="op">(</span>G_APPLICATION <span class="op">(</span>app<span class="op">),</span> argc<span class="op">,</span> argv<span class="op">);</span></span>
<span id="cb19-39"><a href="#cb19-39"></a> g_object_unref <span class="op">(</span>app<span class="op">);</span></span>
<span id="cb19-40"><a href="#cb19-40"></a> <span class="cf">return</span> stat<span class="op">;</span></span>
<span id="cb19-41"><a href="#cb19-41"></a><span class="op">}</span></span></code></pre></div>
<ul>
<li>4-6: The callback function. It returns a string (w)x(h), where the w
and h are the width and height of the window. String duplication is
@ -893,18 +858,18 @@ necessary.</li>
</ul>
<p>The C source file is very simple because almost everything is done in
the ui file.</p>
<h3 id="conversion-between-gvalues">Conversion between GValues</h3>
<h2 id="conversion-between-gvalues">Conversion between GValues</h2>
<p>If you bind different type properties, type conversion is
automatically done. Suppose a label property (string) is bound to
default-width property (int).</p>
<div class="sourceCode" id="cb22"><pre
class="sourceCode xml"><code class="sourceCode xml"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true" tabindex="-1"></a>&lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkLabel&quot;</span>&gt;</span>
<span id="cb22-2"><a href="#cb22-2" aria-hidden="true" tabindex="-1"></a> &lt;<span class="kw">binding</span><span class="ot"> name=</span><span class="st">&quot;label&quot;</span>&gt;</span>
<span id="cb22-3"><a href="#cb22-3" aria-hidden="true" tabindex="-1"></a> &lt;<span class="kw">lookup</span><span class="ot"> name=</span><span class="st">&quot;default-width&quot;</span>&gt;</span>
<span id="cb22-4"><a href="#cb22-4" aria-hidden="true" tabindex="-1"></a> win</span>
<span id="cb22-5"><a href="#cb22-5" aria-hidden="true" tabindex="-1"></a> &lt;/<span class="kw">lookup</span>&gt;</span>
<span id="cb22-6"><a href="#cb22-6" aria-hidden="true" tabindex="-1"></a> &lt;/<span class="kw">binding</span>&gt;</span>
<span id="cb22-7"><a href="#cb22-7" aria-hidden="true" tabindex="-1"></a>&lt;/<span class="kw">object</span>&gt;</span></code></pre></div>
<div class="sourceCode" id="cb20"><pre
class="sourceCode xml"><code class="sourceCode xml"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a>&lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkLabel&quot;</span>&gt;</span>
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true" tabindex="-1"></a> &lt;<span class="kw">binding</span><span class="ot"> name=</span><span class="st">&quot;label&quot;</span>&gt;</span>
<span id="cb20-3"><a href="#cb20-3" aria-hidden="true" tabindex="-1"></a> &lt;<span class="kw">lookup</span><span class="ot"> name=</span><span class="st">&quot;default-width&quot;</span>&gt;</span>
<span id="cb20-4"><a href="#cb20-4" aria-hidden="true" tabindex="-1"></a> win</span>
<span id="cb20-5"><a href="#cb20-5" aria-hidden="true" tabindex="-1"></a> &lt;/<span class="kw">lookup</span>&gt;</span>
<span id="cb20-6"><a href="#cb20-6" aria-hidden="true" tabindex="-1"></a> &lt;/<span class="kw">binding</span>&gt;</span>
<span id="cb20-7"><a href="#cb20-7" aria-hidden="true" tabindex="-1"></a>&lt;/<span class="kw">object</span>&gt;</span></code></pre></div>
<p>The expression created by the lookup tag returns a int type GValue.
On the other hand “label” property holds a string type GValue. When a
GValue is copied to another GValue, the type is automatically converted
@ -912,6 +877,37 @@ if possible. If the current width is 100, an int <code>100</code> is
converted to a string <code>"100"</code>.</p>
<p>If you use <code>g_object_get</code> and <code>g_object_set</code> to
copy properties, the value is automatically converted.</p>
<h2 id="meson.build">Meson.build</h2>
<p>The source files are in <code>src/expression</code> directory. You
can build all the files at once.</p>
<pre><code>$ cd src/expression
$ meson setup _build
$ ninja -C _build</code></pre>
<p>For example, if you want to run “exp”, which is the executable file
from “exp.c”, type <code>_build/exp</code>. You can run other programs
as well.</p>
<p>The file <code>meson.build</code> is as follows.</p>
<div class="sourceCode" id="cb22"><pre
class="sourceCode numberSource numberLines"><code class="sourceCode"><span id="cb22-1"><a href="#cb22-1"></a>project(&#39;exp&#39;, &#39;c&#39;)</span>
<span id="cb22-2"><a href="#cb22-2"></a></span>
<span id="cb22-3"><a href="#cb22-3"></a>gtkdep = dependency(&#39;gtk4&#39;)</span>
<span id="cb22-4"><a href="#cb22-4"></a></span>
<span id="cb22-5"><a href="#cb22-5"></a>gnome=import(&#39;gnome&#39;)</span>
<span id="cb22-6"><a href="#cb22-6"></a>resources = gnome.compile_resources(&#39;resources&#39;,&#39;exp.gresource.xml&#39;)</span>
<span id="cb22-7"><a href="#cb22-7"></a></span>
<span id="cb22-8"><a href="#cb22-8"></a>sourcefiles=files(&#39;exp.c&#39;)</span>
<span id="cb22-9"><a href="#cb22-9"></a></span>
<span id="cb22-10"><a href="#cb22-10"></a>executable(&#39;exp&#39;, sourcefiles, resources, dependencies: gtkdep, export_dynamic: true, install: false)</span>
<span id="cb22-11"><a href="#cb22-11"></a>executable(&#39;exp_constant&#39;, &#39;exp_constant.c&#39;, dependencies: gtkdep, export_dynamic: true, install: false)</span>
<span id="cb22-12"><a href="#cb22-12"></a>executable(&#39;exp_constant_simple&#39;, &#39;exp_constant_simple.c&#39;, dependencies: gtkdep, export_dynamic: true, install: false)</span>
<span id="cb22-13"><a href="#cb22-13"></a>executable(&#39;exp_property_simple&#39;, &#39;exp_property_simple.c&#39;, dependencies: gtkdep, export_dynamic: true, install: false)</span>
<span id="cb22-14"><a href="#cb22-14"></a>executable(&#39;closure&#39;, &#39;closure.c&#39;, dependencies: gtkdep, export_dynamic: true, install: false)</span>
<span id="cb22-15"><a href="#cb22-15"></a>executable(&#39;closure_each&#39;, &#39;closure_each.c&#39;, dependencies: gtkdep, export_dynamic: true, install: false)</span>
<span id="cb22-16"><a href="#cb22-16"></a>executable(&#39;exp_closure_simple&#39;, &#39;exp_closure_simple.c&#39;, dependencies: gtkdep, export_dynamic: true, install: false)</span>
<span id="cb22-17"><a href="#cb22-17"></a>executable(&#39;exp_closure_with_error_report&#39;, &#39;exp_closure_with_error_report.c&#39;, dependencies: gtkdep, export_dynamic: true, install: false)</span>
<span id="cb22-18"><a href="#cb22-18"></a>executable(&#39;exp_bind&#39;, &#39;exp_bind.c&#39;, resources, dependencies: gtkdep, export_dynamic: true, install: false)</span>
<span id="cb22-19"><a href="#cb22-19"></a>executable(&#39;exp_watch&#39;, &#39;exp_watch.c&#39;, dependencies: gtkdep, export_dynamic: true, install: false)</span>
<span id="cb22-20"><a href="#cb22-20"></a>executable(&#39;exp_test&#39;, &#39;exp_test.c&#39;, resources, dependencies: gtkdep, export_dynamic: true, install: false)</span></code></pre></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
</body>

View file

@ -328,10 +328,9 @@ header of the column.</li>
as much as possible. (See the image above).</li>
<li>33- 69: Sets the “factory” property to GtkBuilderListItemFactory.
The factory has “bytes” property which holds a ui string to define a
template to build GtkListItem composite widget. The CDATA section (line
36-66) is the ui string to put into the “bytes” property. The contents
are the same as the ui file <code>factory_list.ui</code> in the section
27.</li>
template to extend GtkListItem class. The CDATA section (line 36-66) is
the ui string to put into the “bytes” property. The contents are the
same as the ui file <code>factory_list.ui</code> in the section 30.</li>
<li>70-77: Sets the “sorter” property to GtkStringSorter object. This
object provides a sorter that compares strings. It has “expression”
property. A closure tag with a string type function
@ -542,7 +541,8 @@ class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span i
<h2 id="compilation-and-execution.">Compilation and execution.</h2>
<p>All the source files are in <code>src/column</code> directory. Change
your current directory to the directory and type the following.</p>
<pre><code>$ meson _build
<pre><code>$ cd src/colomn
$ meson setup _build
$ ninja -C _build
$ _build/column</code></pre>
<p>Then, a window appears.</p>

View file

@ -116,25 +116,14 @@ and GtkBulderListItemFactory</h2>
the contents of a list. Its binding direction is always from an item of
a list to a child of GtkListItem.</p>
<p>When it comes to dynamic connection, its not enough. For example,
you want to edit the contents of a list. You set a child of GtkListItem
to a GtkText instance so a user can edit a text with it. You need to
bind an item in the list with the buffer of the GtkText. The direction
is opposite from the one with GtkBuilderListItemFactory. It is from the
GtkText instance to the item in the list. You can implement this with
GtkSignalListItemFactory, which is more flexible than
suppose you want to edit the contents of a list. You set a child of
GtkListItem to a GtkText instance so a user can edit a text with it. You
need to bind an item in the list with the buffer of the GtkText. The
direction is opposite from the one with GtkBuilderListItemFactory. It is
from the GtkText instance to the item in the list. You can implement
this with GtkSignalListItemFactory, which is more flexible than
GtkBuilderListItemFactory.</p>
<p>Two things are shown in this section.</p>
<ul>
<li>Binding from a child of a GtkListItem instance to an item of a
list.</li>
<li>Access a child of GtkListItem dynamically. This direction is the
same as the one with GtkBulderListItemFactory. But
GtkBulderListItemFactory uses GtkExpression from the item property of
the GtkListItem. So, it updates its child widget only when the item
property changes. In this example the child reflects the change in the
same item in the list dynamically.</li>
</ul>
<p>This section shows just a part of the source file
<p>This section shows just some parts of the source file
<code>listeditor.c</code>. If you want to see the whole codes, see
<code>src/listeditor</code> directory of the <a
href="https://github.com/ToshioCP/Gtk4-tutorial">Gtk4 tutorial
@ -143,7 +132,7 @@ repository</a>.</p>
<p>The sample program is a list editor and data of the list are strings.
Its the same as a line editor. It reads a text file line by line. Each
line is an item of the list. The list is displayed with GtkColumnView.
There are two columns. The one is a button, which makes the line be a
There are two columns. The one is a button, which shows if the line is a
current line. If the line is the current line, the button is colored
with red. The other is a string which is the contents of the
corresponding item of the list.</p>
@ -159,18 +148,19 @@ href="https://github.com/ToshioCP/Gtk4-tutorial">repository</a>.</li>
<li>Change your current directory to <code>src/listeditor</code>.</li>
<li>Type the following on your commandline.</li>
</ul>
<pre><code>$ meson _build
<pre><code>$ meson setup _build
$ ninja -C _build
$ _build/listeditor</code></pre>
<ul>
<li>Append button: appends a line after the current line, or at the last
line if no current line exists.</li>
<li>Insert button: inserts a line before the current line.</li>
<li>Insert button: inserts a line before the current line, or at the top
line if no current line exists.</li>
<li>Remove button: removes a current line.</li>
<li>Read button: reads a file.</li>
<li>Write button: writes the contents to a file.</li>
<li>close button: close the contents.</li>
<li>quit button: quit the application.</li>
<li>close button: closes the contents.</li>
<li>quit button: quits the application.</li>
<li>Button on the select column: makes the line current.</li>
<li>String column: GtkText. You can edit a string in the field.</li>
</ul>
@ -180,7 +170,7 @@ bar. The file name is shown at the right of the write button.</p>
GtkText instance and an item in the list</h2>
<p>The second column (GtkColumnViewColumn) sets its factory property to
GtkSignalListItemFactory. It uses three signals setup, bind and unbind.
The following is their sgnal handlers.</p>
The following shows the signal handlers.</p>
<div class="sourceCode" id="cb2"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb2-1"><a href="#cb2-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb2-2"><a href="#cb2-2"></a>setup2_cb <span class="op">(</span>GtkListItemFactory <span class="op">*</span>factory<span class="op">,</span> GtkListItem <span class="op">*</span>listitem<span class="op">)</span> <span class="op">{</span></span>
@ -193,7 +183,7 @@ class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span i
<span id="cb2-9"><a href="#cb2-9"></a>bind2_cb <span class="op">(</span>GtkListItemFactory <span class="op">*</span>factory<span class="op">,</span> GtkListItem <span class="op">*</span>listitem<span class="op">)</span> <span class="op">{</span></span>
<span id="cb2-10"><a href="#cb2-10"></a> GtkWidget <span class="op">*</span>text <span class="op">=</span> gtk_list_item_get_child <span class="op">(</span>listitem<span class="op">);</span></span>
<span id="cb2-11"><a href="#cb2-11"></a> GtkEntryBuffer <span class="op">*</span>buffer <span class="op">=</span> gtk_text_get_buffer <span class="op">(</span>GTK_TEXT <span class="op">(</span>text<span class="op">));</span></span>
<span id="cb2-12"><a href="#cb2-12"></a> LeData <span class="op">*</span>data <span class="op">=</span> LE_DATA <span class="op">(</span>gtk_list_item_get_item <span class="op">(</span>listitem<span class="op">));</span></span>
<span id="cb2-12"><a href="#cb2-12"></a> LeData <span class="op">*</span>data <span class="op">=</span> LE_DATA <span class="op">(</span>gtk_list_item_get_item<span class="op">(</span>listitem<span class="op">));</span></span>
<span id="cb2-13"><a href="#cb2-13"></a> GBinding <span class="op">*</span>bind<span class="op">;</span></span>
<span id="cb2-14"><a href="#cb2-14"></a></span>
<span id="cb2-15"><a href="#cb2-15"></a> gtk_editable_set_text <span class="op">(</span>GTK_EDITABLE <span class="op">(</span>text<span class="op">),</span> le_data_look_string <span class="op">(</span>data<span class="op">));</span></span>
@ -207,9 +197,10 @@ class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span i
<span id="cb2-23"><a href="#cb2-23"></a>unbind2_cb <span class="op">(</span>GtkListItemFactory <span class="op">*</span>factory<span class="op">,</span> GtkListItem <span class="op">*</span>listitem<span class="op">)</span> <span class="op">{</span></span>
<span id="cb2-24"><a href="#cb2-24"></a> GBinding <span class="op">*</span>bind <span class="op">=</span> G_BINDING <span class="op">(</span>g_object_get_data <span class="op">(</span>G_OBJECT <span class="op">(</span>listitem<span class="op">),</span> <span class="st">&quot;bind&quot;</span><span class="op">));</span></span>
<span id="cb2-25"><a href="#cb2-25"></a></span>
<span id="cb2-26"><a href="#cb2-26"></a> g_binding_unbind<span class="op">(</span>bind<span class="op">);</span></span>
<span id="cb2-27"><a href="#cb2-27"></a> g_object_set_data <span class="op">(</span>G_OBJECT <span class="op">(</span>listitem<span class="op">),</span> <span class="st">&quot;bind&quot;</span><span class="op">,</span> NULL<span class="op">);</span></span>
<span id="cb2-28"><a href="#cb2-28"></a><span class="op">}</span></span></code></pre></div>
<span id="cb2-26"><a href="#cb2-26"></a> <span class="cf">if</span> <span class="op">(</span>bind<span class="op">)</span></span>
<span id="cb2-27"><a href="#cb2-27"></a> g_binding_unbind<span class="op">(</span>bind<span class="op">);</span></span>
<span id="cb2-28"><a href="#cb2-28"></a> g_object_set_data <span class="op">(</span>G_OBJECT <span class="op">(</span>listitem<span class="op">),</span> <span class="st">&quot;bind&quot;</span><span class="op">,</span> NULL<span class="op">);</span></span>
<span id="cb2-29"><a href="#cb2-29"></a><span class="op">}</span></span></code></pre></div>
<ul>
<li>1-6: <code>setup2_cb</code> is a setup signal handler on the
GtkSignalListItemFactory. This factory is inserted to the factory
@ -221,22 +212,18 @@ is destroyed. So, teardown signal handler isnt necessary.</li>
when the <code>listitem</code> is bound to an item in the list. The list
items are LeData instances. LeData is defined in the file
<code>listeditor.c</code> (the C source file of the list editor). It is
a child class of GObject and has two data. The one is
<code>listitem</code> which points a first column GtkListItem instance
when they are connected. Be careful that the GtkListItem instance is
<em>not</em> the <code>listitem</code> in this handler. If no
GtkListItem is connected, it is NULL. The other is <code>string</code>
which is a content of the line.
a child class of GObject and has string data which is the content of the
line.
<ul>
<li>10-11: <code>text</code> is a child of the <code>listitem</code> and
it is a GtkText instance. And <code>buffer</code> is a GtkTextBuffer
it is a GtkText instance. And <code>buffer</code> is a GtkEntryBuffer
instance of the <code>text</code>.</li>
<li>12: The LeData instance <code>data</code> is an item pointed by the
<code>listitem</code>.</li>
<li>15-16: Sets the text of <code>text</code> to
<code>le_data_look_string (data)</code>. le_data_look_string returns the
string of the <code>data</code> and the ownership of the string is still
taken by the <code>data</code>. So, the caller dont need to free the
taken by the <code>data</code>. So, the caller doesnt need to free the
string.</li>
<li>18: <code>g_object_bind_property</code> binds a property and another
object property. This line binds the “text” property of the
@ -254,16 +241,22 @@ value. This line sets the association from “bind” to <code>bind</code>
instance. It makes possible for the “unbind” handler to get the
<code>bind</code> instance.</li>
</ul></li>
<li>22-28: <code>unbind2_cb</code> is a unbind signal handler.
<li>22-29: <code>unbind2_cb</code> is a unbind signal handler.
<ul>
<li>24: Retrieves the <code>bind</code> instance from the table in the
<code>listitem</code> instance.</li>
<li>26: Unbind the binding.</li>
<li>27: Removes the value corresponds to the “bind” key.</li>
<li>26-27: Unbind the binding.</li>
<li>28: Removes the value corresponds to the “bind” key.</li>
</ul></li>
</ul>
<p>This technique is not so complicated. You can use it when you make a
cell editable application.</p>
<p>If it is impossible to use <code>g_object_bind_property</code>, use a
notify signal on the GtkEntryBuffer instance. You can use “deleted-text”
and “inserted-text” signal instead. The handler of the signals above
copies the text in the GtkEntryBuffer instance to the LeData string.
Connect the notify signal handler in <code>bind2_cb</code> and
disconnect it in <code>unbind2_cb</code>.</p>
<h2 id="change-the-cell-of-gtkcolumnview-dynamically">Change the cell of
GtkColumnView dynamically</h2>
<p>Next topic is to change the GtkColumnView (or GtkListView) cells
@ -289,164 +282,148 @@ GtkSingleSelection selects a line on the display. The current line
doesnt need to be on the display. It is possible to be on the line out
of the Window (GtkScrolledWindow). Actually, the program doesnt use
GtkSingleSelection.</p>
<p>It is necessary to know the corresponding GtkListItem instance from
the item in the list. It is the opposite direction from
<code>gtk_list_item_get_item</code> function. To accomplish this, we set
a <code>listitem</code> element of LeData to point the corresponding
GtkListItem instance. Therefore, items (LeData) in the list always know
the GtkListItem. If theres no GtkListItem bound to the item, NULL is
assigned.</p>
<p>The LeWindow instance has two instance variables for recording the
current line.</p>
<ul>
<li><code>win-&gt;position</code>: An int type variable. It is the
position of the current line. It is zero-based. If no current line
exists, it is -1.</li>
<li><code>win-&gt;current_button</code>: A variable points the button,
located at the first column, on the current line. If no current line
exists, it is NULL.</li>
</ul>
<p>If the current line moves, the following two functions are called.
They updates the two varables.</p>
<div class="sourceCode" id="cb3"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1"></a><span class="dt">void</span></span>
<span id="cb3-2"><a href="#cb3-2"></a>select_cb <span class="op">(</span>GtkButton <span class="op">*</span>btn<span class="op">,</span> GtkListItem <span class="op">*</span>listitem<span class="op">)</span> <span class="op">{</span></span>
<span id="cb3-3"><a href="#cb3-3"></a> LeWindow <span class="op">*</span>win <span class="op">=</span> LE_WINDOW <span class="op">(</span>gtk_widget_get_ancestor <span class="op">(</span>GTK_WIDGET <span class="op">(</span>btn<span class="op">),</span> LE_TYPE_WINDOW<span class="op">));</span></span>
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb3-2"><a href="#cb3-2"></a>update_current_position <span class="op">(</span>LeWindow <span class="op">*</span>win<span class="op">,</span> <span class="dt">int</span> new<span class="op">)</span> <span class="op">{</span></span>
<span id="cb3-3"><a href="#cb3-3"></a> <span class="dt">char</span> <span class="op">*</span>s<span class="op">;</span></span>
<span id="cb3-4"><a href="#cb3-4"></a></span>
<span id="cb3-5"><a href="#cb3-5"></a> update_current <span class="op">(</span>win<span class="op">,</span> gtk_list_item_get_position <span class="op">(</span>listitem<span class="op">));</span></span>
<span id="cb3-6"><a href="#cb3-6"></a><span class="op">}</span></span>
<span id="cb3-7"><a href="#cb3-7"></a></span>
<span id="cb3-8"><a href="#cb3-8"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb3-9"><a href="#cb3-9"></a>setup1_cb <span class="op">(</span>GtkListItemFactory <span class="op">*</span>factory<span class="op">,</span> GtkListItem <span class="op">*</span>listitem<span class="op">)</span> <span class="op">{</span></span>
<span id="cb3-10"><a href="#cb3-10"></a> GtkWidget <span class="op">*</span>button <span class="op">=</span> gtk_button_new <span class="op">();</span></span>
<span id="cb3-11"><a href="#cb3-11"></a> gtk_list_item_set_child <span class="op">(</span>listitem<span class="op">,</span> button<span class="op">);</span></span>
<span id="cb3-12"><a href="#cb3-12"></a> gtk_widget_set_focusable <span class="op">(</span>GTK_WIDGET <span class="op">(</span>button<span class="op">),</span> FALSE<span class="op">);</span></span>
<span id="cb3-13"><a href="#cb3-13"></a> g_signal_connect <span class="op">(</span>button<span class="op">,</span> <span class="st">&quot;clicked&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>select_cb<span class="op">),</span> listitem<span class="op">);</span></span>
<span id="cb3-14"><a href="#cb3-14"></a><span class="op">}</span></span>
<span id="cb3-15"><a href="#cb3-15"></a></span>
<span id="cb3-16"><a href="#cb3-16"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb3-17"><a href="#cb3-17"></a>bind1_cb <span class="op">(</span>GtkListItemFactory <span class="op">*</span>factory<span class="op">,</span> GtkListItem <span class="op">*</span>listitem<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span>
<span id="cb3-18"><a href="#cb3-18"></a> LeWindow <span class="op">*</span>win <span class="op">=</span> LE_WINDOW <span class="op">(</span>user_data<span class="op">);</span></span>
<span id="cb3-19"><a href="#cb3-19"></a> GtkWidget <span class="op">*</span>button <span class="op">=</span> gtk_list_item_get_child <span class="op">(</span>listitem<span class="op">);</span></span>
<span id="cb3-20"><a href="#cb3-20"></a> <span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span>non_current<span class="op">[</span><span class="dv">1</span><span class="op">]</span> <span class="op">=</span> <span class="op">{</span>NULL<span class="op">};</span></span>
<span id="cb3-21"><a href="#cb3-21"></a> <span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span>current<span class="op">[</span><span class="dv">2</span><span class="op">]</span> <span class="op">=</span> <span class="op">{</span><span class="st">&quot;current&quot;</span><span class="op">,</span> NULL<span class="op">};</span></span>
<span id="cb3-22"><a href="#cb3-22"></a> LeData <span class="op">*</span>data <span class="op">=</span> LE_DATA <span class="op">(</span>gtk_list_item_get_item <span class="op">(</span>listitem<span class="op">));</span></span>
<span id="cb3-23"><a href="#cb3-23"></a></span>
<span id="cb3-24"><a href="#cb3-24"></a> <span class="cf">if</span> <span class="op">(</span>data<span class="op">)</span> <span class="op">{</span></span>
<span id="cb3-25"><a href="#cb3-25"></a> le_data_set_listitem <span class="op">(</span>data<span class="op">,</span> listitem<span class="op">);</span></span>
<span id="cb3-26"><a href="#cb3-26"></a> <span class="cf">if</span> <span class="op">(</span>win<span class="op">-&gt;</span>position <span class="op">==</span> gtk_list_item_get_position <span class="op">(</span>listitem<span class="op">))</span></span>
<span id="cb3-27"><a href="#cb3-27"></a> gtk_widget_set_css_classes <span class="op">(</span>GTK_WIDGET <span class="op">(</span>button<span class="op">),</span> current<span class="op">);</span></span>
<span id="cb3-28"><a href="#cb3-28"></a> <span class="cf">else</span></span>
<span id="cb3-29"><a href="#cb3-29"></a> gtk_widget_set_css_classes <span class="op">(</span>GTK_WIDGET <span class="op">(</span>button<span class="op">),</span> non_current<span class="op">);</span></span>
<span id="cb3-30"><a href="#cb3-30"></a> <span class="op">}</span></span>
<span id="cb3-31"><a href="#cb3-31"></a><span class="op">}</span></span>
<span id="cb3-32"><a href="#cb3-32"></a></span>
<span id="cb3-33"><a href="#cb3-33"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb3-34"><a href="#cb3-34"></a>unbind1_cb <span class="op">(</span>GtkListItemFactory <span class="op">*</span>factory<span class="op">,</span> GtkListItem <span class="op">*</span>listitem<span class="op">)</span> <span class="op">{</span></span>
<span id="cb3-35"><a href="#cb3-35"></a> LeData <span class="op">*</span>data <span class="op">=</span> LE_DATA <span class="op">(</span>gtk_list_item_get_item <span class="op">(</span>listitem<span class="op">));</span></span>
<span id="cb3-36"><a href="#cb3-36"></a> <span class="cf">if</span> <span class="op">(</span>data<span class="op">)</span></span>
<span id="cb3-37"><a href="#cb3-37"></a> le_data_set_listitem <span class="op">(</span>data<span class="op">,</span> NULL<span class="op">);</span></span>
<span id="cb3-38"><a href="#cb3-38"></a><span class="op">}</span></span></code></pre></div>
<ul>
<li>8-14: <code>setup1_cb</code> is a setup signal handler on the
GtkSignalListItemFactory. This factory is inserted to the factory
property of the first GtkColumnViewColumn. It sets the child of
<code>listitem</code> to a GtkButton instance. The “clicked” signal on
the button is connected to the handler <code>select_cb</code>. When the
listitem is destroyed, the child (GtkButton) is also destroyed. At the
same time, the connection of the signal and the handler is also
destroyed. So, you dont need teardown signal handler.</li>
<li>1-6: <code>select_cb</code> is a “clicked” signal handler. LeWindow
is defined in <code>listeditor.c</code>. Its a child class of
GtkApplicationWindow. The handler just calls the
<code>update_current</code> function. The function will be explained
later.</li>
<li>16-31: <code>bind1_cb</code> is a bind signal handler. It sets the
“listitem” element of the item (LeData) to point the
<code>listitem</code> (GtkListItem instance). It makes the item possible
to find the corresponding GtkListItem instance. If the item is the
current line, the CSS class of the button includes “current” class.
Otherwise it has no CSS class. This is necessary because the button may
be recycled and it has had former CSS class. The class need to be
updated.</li>
<li>33-38: <code>unbind1_cb</code> is an unbind signal handler. It
removes the <code>listitem</code> instance from the “listitem” element
of the item. The element becomes NULL, which tells no GtkListItem is
bound. When referring GtkListItem, it needs to check the “listitem”
element whether it points a GtkListItem or not (NULL). Otherwise bad
things will happen.</li>
</ul>
<span id="cb3-5"><a href="#cb3-5"></a> win<span class="op">-&gt;</span>position <span class="op">=</span> new<span class="op">;</span></span>
<span id="cb3-6"><a href="#cb3-6"></a> <span class="cf">if</span> <span class="op">(</span>win<span class="op">-&gt;</span>position <span class="op">&gt;=</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb3-7"><a href="#cb3-7"></a> s <span class="op">=</span> g_strdup_printf <span class="op">(</span><span class="st">&quot;%d&quot;</span><span class="op">,</span> win<span class="op">-&gt;</span>position<span class="op">);</span></span>
<span id="cb3-8"><a href="#cb3-8"></a> <span class="cf">else</span></span>
<span id="cb3-9"><a href="#cb3-9"></a> s <span class="op">=</span> <span class="st">&quot;&quot;</span><span class="op">;</span></span>
<span id="cb3-10"><a href="#cb3-10"></a> gtk_label_set_text <span class="op">(</span>GTK_LABEL <span class="op">(</span>win<span class="op">-&gt;</span>position_label<span class="op">),</span> s<span class="op">);</span></span>
<span id="cb3-11"><a href="#cb3-11"></a> <span class="cf">if</span> <span class="op">(*</span>s<span class="op">)</span> <span class="co">// s isn&#39;t an empty string</span></span>
<span id="cb3-12"><a href="#cb3-12"></a> g_free <span class="op">(</span>s<span class="op">);</span></span>
<span id="cb3-13"><a href="#cb3-13"></a><span class="op">}</span></span>
<span id="cb3-14"><a href="#cb3-14"></a></span>
<span id="cb3-15"><a href="#cb3-15"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb3-16"><a href="#cb3-16"></a>update_current_button <span class="op">(</span>LeWindow <span class="op">*</span>win<span class="op">,</span> GtkButton <span class="op">*</span>new_button<span class="op">)</span> <span class="op">{</span></span>
<span id="cb3-17"><a href="#cb3-17"></a> <span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span>non_current<span class="op">[</span><span class="dv">1</span><span class="op">]</span> <span class="op">=</span> <span class="op">{</span>NULL<span class="op">};</span></span>
<span id="cb3-18"><a href="#cb3-18"></a> <span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span>current<span class="op">[</span><span class="dv">2</span><span class="op">]</span> <span class="op">=</span> <span class="op">{</span><span class="st">&quot;current&quot;</span><span class="op">,</span> NULL<span class="op">};</span></span>
<span id="cb3-19"><a href="#cb3-19"></a></span>
<span id="cb3-20"><a href="#cb3-20"></a> <span class="cf">if</span> <span class="op">(</span>win<span class="op">-&gt;</span>current_button<span class="op">)</span> <span class="op">{</span></span>
<span id="cb3-21"><a href="#cb3-21"></a> gtk_widget_set_css_classes <span class="op">(</span>GTK_WIDGET <span class="op">(</span>win<span class="op">-&gt;</span>current_button<span class="op">),</span> non_current<span class="op">);</span></span>
<span id="cb3-22"><a href="#cb3-22"></a> g_object_unref <span class="op">(</span>win<span class="op">-&gt;</span>current_button<span class="op">);</span></span>
<span id="cb3-23"><a href="#cb3-23"></a> <span class="op">}</span></span>
<span id="cb3-24"><a href="#cb3-24"></a> win<span class="op">-&gt;</span>current_button <span class="op">=</span> new_button<span class="op">;</span></span>
<span id="cb3-25"><a href="#cb3-25"></a> <span class="cf">if</span> <span class="op">(</span>win<span class="op">-&gt;</span>current_button<span class="op">)</span> <span class="op">{</span></span>
<span id="cb3-26"><a href="#cb3-26"></a> g_object_ref <span class="op">(</span>win<span class="op">-&gt;</span>current_button<span class="op">);</span></span>
<span id="cb3-27"><a href="#cb3-27"></a> gtk_widget_set_css_classes <span class="op">(</span>GTK_WIDGET <span class="op">(</span>win<span class="op">-&gt;</span>current_button<span class="op">),</span> current<span class="op">);</span></span>
<span id="cb3-28"><a href="#cb3-28"></a> <span class="op">}</span></span>
<span id="cb3-29"><a href="#cb3-29"></a><span class="op">}</span></span></code></pre></div>
<p>The varable <code>win-&gt;position_label</code> points a GtkLabel
instance. The label shows the current line position.</p>
<p>The current button has CSS “current” class. The button is colored red
through the CSS “button.current {background: red;}”.</p>
<p>The order of the call for these two functions is important. The first
function, which updates the position, is usually called first. After
that, a new line is appended or inserted. Then, the second function is
called.</p>
<p>The following functions call the two functions above. Be careful
about the order of the call.</p>
<div class="sourceCode" id="cb4"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb4-2"><a href="#cb4-2"></a>update_current <span class="op">(</span>LeWindow <span class="op">*</span>win<span class="op">,</span> <span class="dt">int</span> new<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-3"><a href="#cb4-3"></a> <span class="dt">char</span> <span class="op">*</span>s<span class="op">;</span></span>
<span id="cb4-4"><a href="#cb4-4"></a> LeData <span class="op">*</span>data<span class="op">;</span></span>
<span id="cb4-5"><a href="#cb4-5"></a> GtkListItem <span class="op">*</span>listitem<span class="op">;</span></span>
<span id="cb4-6"><a href="#cb4-6"></a> GtkButton <span class="op">*</span>button<span class="op">;</span></span>
<span id="cb4-7"><a href="#cb4-7"></a> <span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span>non_current<span class="op">[</span><span class="dv">1</span><span class="op">]</span> <span class="op">=</span> <span class="op">{</span>NULL<span class="op">};</span></span>
<span id="cb4-8"><a href="#cb4-8"></a> <span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span>current<span class="op">[</span><span class="dv">2</span><span class="op">]</span> <span class="op">=</span> <span class="op">{</span><span class="st">&quot;current&quot;</span><span class="op">,</span> NULL<span class="op">};</span></span>
<span id="cb4-9"><a href="#cb4-9"></a></span>
<span id="cb4-10"><a href="#cb4-10"></a> <span class="cf">if</span> <span class="op">(</span>new <span class="op">&gt;=</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb4-11"><a href="#cb4-11"></a> s <span class="op">=</span> g_strdup_printf <span class="op">(</span><span class="st">&quot;%d&quot;</span><span class="op">,</span> new<span class="op">);</span></span>
<span id="cb4-12"><a href="#cb4-12"></a> <span class="cf">else</span></span>
<span id="cb4-13"><a href="#cb4-13"></a> s <span class="op">=</span> <span class="st">&quot;&quot;</span><span class="op">;</span></span>
<span id="cb4-14"><a href="#cb4-14"></a> gtk_label_set_text <span class="op">(</span>GTK_LABEL <span class="op">(</span>win<span class="op">-&gt;</span>position_label<span class="op">),</span> s<span class="op">);</span></span>
<span id="cb4-15"><a href="#cb4-15"></a> <span class="cf">if</span> <span class="op">(*</span>s<span class="op">)</span> <span class="co">// s isn&#39;t an empty string</span></span>
<span id="cb4-16"><a href="#cb4-16"></a> g_free <span class="op">(</span>s<span class="op">);</span></span>
<span id="cb4-17"><a href="#cb4-17"></a></span>
<span id="cb4-18"><a href="#cb4-18"></a> <span class="cf">if</span> <span class="op">(</span>win<span class="op">-&gt;</span>position <span class="op">&gt;=</span><span class="dv">0</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-19"><a href="#cb4-19"></a> data <span class="op">=</span> LE_DATA <span class="op">(</span>g_list_model_get_item <span class="op">(</span>G_LIST_MODEL <span class="op">(</span>win<span class="op">-&gt;</span>liststore<span class="op">),</span> win<span class="op">-&gt;</span>position<span class="op">));</span></span>
<span id="cb4-20"><a href="#cb4-20"></a> <span class="cf">if</span> <span class="op">((</span>listitem <span class="op">=</span> le_data_get_listitem <span class="op">(</span>data<span class="op">))</span> <span class="op">!=</span> NULL<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-21"><a href="#cb4-21"></a> button <span class="op">=</span> GTK_BUTTON <span class="op">(</span>gtk_list_item_get_child <span class="op">(</span>listitem<span class="op">));</span></span>
<span id="cb4-22"><a href="#cb4-22"></a> gtk_widget_set_css_classes <span class="op">(</span>GTK_WIDGET <span class="op">(</span>button<span class="op">),</span> non_current<span class="op">);</span></span>
<span id="cb4-23"><a href="#cb4-23"></a> g_object_unref <span class="op">(</span>listitem<span class="op">);</span></span>
<span id="cb4-24"><a href="#cb4-24"></a> <span class="op">}</span></span>
<span id="cb4-25"><a href="#cb4-25"></a> g_object_unref <span class="op">(</span>data<span class="op">);</span></span>
<span id="cb4-26"><a href="#cb4-26"></a> <span class="op">}</span></span>
<span id="cb4-27"><a href="#cb4-27"></a> win<span class="op">-&gt;</span>position <span class="op">=</span> new<span class="op">;</span></span>
<span id="cb4-28"><a href="#cb4-28"></a> <span class="cf">if</span> <span class="op">(</span>win<span class="op">-&gt;</span>position <span class="op">&gt;=</span><span class="dv">0</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-29"><a href="#cb4-29"></a> data <span class="op">=</span> LE_DATA <span class="op">(</span>g_list_model_get_item <span class="op">(</span>G_LIST_MODEL <span class="op">(</span>win<span class="op">-&gt;</span>liststore<span class="op">),</span> win<span class="op">-&gt;</span>position<span class="op">));</span></span>
<span id="cb4-30"><a href="#cb4-30"></a> <span class="cf">if</span> <span class="op">((</span>listitem <span class="op">=</span> le_data_get_listitem <span class="op">(</span>data<span class="op">))</span> <span class="op">!=</span> NULL<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-31"><a href="#cb4-31"></a> button <span class="op">=</span> GTK_BUTTON <span class="op">(</span>gtk_list_item_get_child <span class="op">(</span>listitem<span class="op">));</span></span>
<span id="cb4-32"><a href="#cb4-32"></a> gtk_widget_set_css_classes <span class="op">(</span>GTK_WIDGET <span class="op">(</span>button<span class="op">),</span> current<span class="op">);</span></span>
<span id="cb4-33"><a href="#cb4-33"></a> g_object_unref <span class="op">(</span>listitem<span class="op">);</span></span>
<span id="cb4-34"><a href="#cb4-34"></a> <span class="op">}</span></span>
<span id="cb4-35"><a href="#cb4-35"></a> g_object_unref <span class="op">(</span>data<span class="op">);</span></span>
<span id="cb4-36"><a href="#cb4-36"></a> <span class="op">}</span></span>
<span id="cb4-37"><a href="#cb4-37"></a><span class="op">}</span></span></code></pre></div>
<p>The function <code>update_current</code> does several things.</p>
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1"></a><span class="dt">void</span></span>
<span id="cb4-2"><a href="#cb4-2"></a>select_cb <span class="op">(</span>GtkButton <span class="op">*</span>btn<span class="op">,</span> GtkListItem <span class="op">*</span>listitem<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-3"><a href="#cb4-3"></a> LeWindow <span class="op">*</span>win <span class="op">=</span> LE_WINDOW <span class="op">(</span>gtk_widget_get_ancestor <span class="op">(</span>GTK_WIDGET <span class="op">(</span>btn<span class="op">),</span> LE_TYPE_WINDOW<span class="op">));</span></span>
<span id="cb4-4"><a href="#cb4-4"></a></span>
<span id="cb4-5"><a href="#cb4-5"></a> update_current_position <span class="op">(</span>win<span class="op">,</span> gtk_list_item_get_position <span class="op">(</span>listitem<span class="op">));</span></span>
<span id="cb4-6"><a href="#cb4-6"></a> update_current_button <span class="op">(</span>win<span class="op">,</span> btn<span class="op">);</span></span>
<span id="cb4-7"><a href="#cb4-7"></a><span class="op">}</span></span>
<span id="cb4-8"><a href="#cb4-8"></a></span>
<span id="cb4-9"><a href="#cb4-9"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb4-10"><a href="#cb4-10"></a>setup1_cb <span class="op">(</span>GtkListItemFactory <span class="op">*</span>factory<span class="op">,</span> GtkListItem <span class="op">*</span>listitem<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-11"><a href="#cb4-11"></a> GtkWidget <span class="op">*</span>button <span class="op">=</span> gtk_button_new <span class="op">();</span></span>
<span id="cb4-12"><a href="#cb4-12"></a> gtk_list_item_set_child <span class="op">(</span>listitem<span class="op">,</span> button<span class="op">);</span></span>
<span id="cb4-13"><a href="#cb4-13"></a> gtk_widget_set_focusable <span class="op">(</span>GTK_WIDGET <span class="op">(</span>button<span class="op">),</span> FALSE<span class="op">);</span></span>
<span id="cb4-14"><a href="#cb4-14"></a> g_signal_connect <span class="op">(</span>button<span class="op">,</span> <span class="st">&quot;clicked&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>select_cb<span class="op">),</span> listitem<span class="op">);</span></span>
<span id="cb4-15"><a href="#cb4-15"></a><span class="op">}</span></span>
<span id="cb4-16"><a href="#cb4-16"></a></span>
<span id="cb4-17"><a href="#cb4-17"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb4-18"><a href="#cb4-18"></a>bind1_cb <span class="op">(</span>GtkListItemFactory <span class="op">*</span>factory<span class="op">,</span> GtkListItem <span class="op">*</span>listitem<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-19"><a href="#cb4-19"></a> LeWindow <span class="op">*</span>win <span class="op">=</span> LE_WINDOW <span class="op">(</span>user_data<span class="op">);</span></span>
<span id="cb4-20"><a href="#cb4-20"></a> GtkWidget <span class="op">*</span>button <span class="op">=</span> gtk_list_item_get_child <span class="op">(</span>listitem<span class="op">);</span></span>
<span id="cb4-21"><a href="#cb4-21"></a></span>
<span id="cb4-22"><a href="#cb4-22"></a> <span class="cf">if</span> <span class="op">(</span>win<span class="op">-&gt;</span>position <span class="op">==</span> gtk_list_item_get_position <span class="op">(</span>listitem<span class="op">))</span></span>
<span id="cb4-23"><a href="#cb4-23"></a> update_current_button <span class="op">(</span>win<span class="op">,</span> GTK_BUTTON <span class="op">(</span>button<span class="op">));</span></span>
<span id="cb4-24"><a href="#cb4-24"></a><span class="op">}</span></span></code></pre></div>
<ul>
<li>It has two parameters. The first one is <code>win</code>, which is
an instance of LeWindow class. It has some elements.
<ul>
<li>win-&gt;position: an Integer. it is the current position. If no
current line exists, it is -1.</li>
<li>win-&gt;position_label: GtkLabel. It shows the current
position.</li>
</ul></li>
<li>The second parameter is <code>new</code>, which is the new current
position. At the beginning of the function, win-&gt;position points the
old position.</li>
<li>10-16: Update the text of GtkLabel.</li>
<li>18-26: If the old position (win-&gt;position) is not negative, the
current line exists. It gets a GtkListItem instance via the item
(LeData) of the list. And it gets the GtkButton instance which is the
child of the GtkListItem. It clears the “css-classes” property of the
<li>1-7: <code>select_cb</code> is a “clicked” signal handler. The
handler just calls the two functions and update the position and
button.</li>
<li>27: Updates win-&gt;position.</li>
<li>28-36: If the new position is not negative (Its possible to be
negative when the current line has been removed), the current line
exists. It sets the “css-classes” property of the button to
<code>{"current", NULL}</code>. It is a NULL-terminated array of
strings. Each string is a CSS class. Now the button has “current” style
class.</li>
<li>9-15: <code>setup1_cb</code> is a setup signal handler on the
GtkSignalListItemFactory. It sets the child of <code>listitem</code> to
a GtkButton instance. The “clicked” signal on the button is connected to
the handler <code>select_cb</code>. When the listitem is destroyed, the
child (GtkButton) is also destroyed. At the same time, the connection of
the signal and the handler is also destroyed. So, you dont need
teardown signal handler.</li>
<li>17-24: <code>bind1_cb</code> is a bind signal handler. Usually, the
position moves before this handler is called. If the item is on the
current line, the button is updated. No unbind handler is
necessary.</li>
</ul>
<p>The color of buttons are determined by the “background” CSS style.
The following CSS is applied to the default GdkDisplay in advance (in
the startup handler of the application).</p>
<p>When a line is added, the current position is updated in advance.</p>
<div class="sourceCode" id="cb5"><pre
class="sourceCode css"><code class="sourceCode css"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a>columnview listview row button<span class="fu">.current</span> {<span class="kw">background</span>: <span class="cn">red</span><span class="op">;</span>}</span></code></pre></div>
<p>The selectors “columnview listview row” is needed before “button”
selector. Otherwise the buttons in the GtkColumnview wont be found. The
button selector has “current” class. So, the only “current” class button
is colored with red. Other buttons are not colored, which means they are
white.</p>
<h2
id="gtk_widget_dispose_template-function">Gtk_widget_dispose_template
function</h2>
<p>The function <code>gtk_widget_dispose_template</code> clears the
template children for the given widget. This is the opposite of
<code>gtk_widget_init_template()</code>. It is a new function of GTK 4.8
version. If your GTK version is lower than 4.8, you need to modify the
program.</p>
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb5-1"><a href="#cb5-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb5-2"><a href="#cb5-2"></a>app_cb <span class="op">(</span>GtkButton <span class="op">*</span>btn<span class="op">,</span> LeWindow <span class="op">*</span>win<span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-3"><a href="#cb5-3"></a> LeData <span class="op">*</span>data <span class="op">=</span> le_data_new_with_data <span class="op">(</span><span class="st">&quot;&quot;</span><span class="op">);</span></span>
<span id="cb5-4"><a href="#cb5-4"></a></span>
<span id="cb5-5"><a href="#cb5-5"></a> <span class="cf">if</span> <span class="op">(</span>win<span class="op">-&gt;</span>position <span class="op">&gt;=</span> <span class="dv">0</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-6"><a href="#cb5-6"></a> update_current_position <span class="op">(</span>win<span class="op">,</span> win<span class="op">-&gt;</span>position <span class="op">+</span> <span class="dv">1</span><span class="op">);</span></span>
<span id="cb5-7"><a href="#cb5-7"></a> g_list_store_insert <span class="op">(</span>win<span class="op">-&gt;</span>liststore<span class="op">,</span> win<span class="op">-&gt;</span>position<span class="op">,</span> data<span class="op">);</span></span>
<span id="cb5-8"><a href="#cb5-8"></a> <span class="op">}</span> <span class="cf">else</span> <span class="op">{</span></span>
<span id="cb5-9"><a href="#cb5-9"></a> update_current_position <span class="op">(</span>win<span class="op">,</span> g_list_model_get_n_items <span class="op">(</span>G_LIST_MODEL <span class="op">(</span>win<span class="op">-&gt;</span>liststore<span class="op">)));</span></span>
<span id="cb5-10"><a href="#cb5-10"></a> g_list_store_append <span class="op">(</span>win<span class="op">-&gt;</span>liststore<span class="op">,</span> data<span class="op">);</span></span>
<span id="cb5-11"><a href="#cb5-11"></a> <span class="op">}</span></span>
<span id="cb5-12"><a href="#cb5-12"></a> g_object_unref <span class="op">(</span>data<span class="op">);</span></span>
<span id="cb5-13"><a href="#cb5-13"></a><span class="op">}</span></span>
<span id="cb5-14"><a href="#cb5-14"></a></span>
<span id="cb5-15"><a href="#cb5-15"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb5-16"><a href="#cb5-16"></a>ins_cb <span class="op">(</span>GtkButton <span class="op">*</span>btn<span class="op">,</span> LeWindow <span class="op">*</span>win<span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-17"><a href="#cb5-17"></a> LeData <span class="op">*</span>data <span class="op">=</span> le_data_new_with_data <span class="op">(</span><span class="st">&quot;&quot;</span><span class="op">);</span></span>
<span id="cb5-18"><a href="#cb5-18"></a></span>
<span id="cb5-19"><a href="#cb5-19"></a> <span class="cf">if</span> <span class="op">(</span>win<span class="op">-&gt;</span>position <span class="op">&gt;=</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb5-20"><a href="#cb5-20"></a> g_list_store_insert <span class="op">(</span>win<span class="op">-&gt;</span>liststore<span class="op">,</span> win<span class="op">-&gt;</span>position<span class="op">,</span> data<span class="op">);</span></span>
<span id="cb5-21"><a href="#cb5-21"></a> <span class="cf">else</span> <span class="op">{</span></span>
<span id="cb5-22"><a href="#cb5-22"></a> update_current_position <span class="op">(</span>win<span class="op">,</span> <span class="dv">0</span><span class="op">);</span></span>
<span id="cb5-23"><a href="#cb5-23"></a> g_list_store_insert <span class="op">(</span>win<span class="op">-&gt;</span>liststore<span class="op">,</span> <span class="dv">0</span><span class="op">,</span> data<span class="op">);</span></span>
<span id="cb5-24"><a href="#cb5-24"></a> <span class="op">}</span></span>
<span id="cb5-25"><a href="#cb5-25"></a> g_object_unref <span class="op">(</span>data<span class="op">);</span></span>
<span id="cb5-26"><a href="#cb5-26"></a><span class="op">}</span></span></code></pre></div>
<p>When a line is removed, the current position becomes -1 and no button
is current.</p>
<div class="sourceCode" id="cb6"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb6-1"><a href="#cb6-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb6-2"><a href="#cb6-2"></a>rm_cb <span class="op">(</span>GtkButton <span class="op">*</span>btn<span class="op">,</span> LeWindow <span class="op">*</span>win<span class="op">)</span> <span class="op">{</span></span>
<span id="cb6-3"><a href="#cb6-3"></a> <span class="cf">if</span> <span class="op">(</span>win<span class="op">-&gt;</span>position <span class="op">&gt;=</span> <span class="dv">0</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb6-4"><a href="#cb6-4"></a> g_list_store_remove <span class="op">(</span>win<span class="op">-&gt;</span>liststore<span class="op">,</span> win<span class="op">-&gt;</span>position<span class="op">);</span></span>
<span id="cb6-5"><a href="#cb6-5"></a> update_current_position <span class="op">(</span>win<span class="op">,</span> <span class="op">-</span><span class="dv">1</span><span class="op">);</span></span>
<span id="cb6-6"><a href="#cb6-6"></a> update_current_button <span class="op">(</span>win<span class="op">,</span> NULL<span class="op">);</span></span>
<span id="cb6-7"><a href="#cb6-7"></a> <span class="op">}</span></span>
<span id="cb6-8"><a href="#cb6-8"></a><span class="op">}</span></span></code></pre></div>
<p>The color of buttons are determined by the “background” CSS style.
The following CSS node is a bit complicated. CSS node
<code>column view</code> has <code>listview</code> child node. It covers
the rows in the GtkColumnView. The <code>listview</code> node is the
same as the one for GtkListView. It has <code>row</code> child node,
which is for each child widget. Therefore, the following node
corresponds buttons on the GtkColumnView widget. In addition, it is
applied to the “current” class.</p>
<div class="sourceCode" id="cb7"><pre
class="sourceCode css"><code class="sourceCode css"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a>columnview listview row button<span class="fu">.current</span> {<span class="kw">background</span>: <span class="cn">red</span><span class="op">;</span>}</span></code></pre></div>
<h2 id="a-waring-from-gtktext">A waring from GtkText</h2>
<p>If your program has the following two, a warning message can be
issued.</p>
@ -462,24 +439,24 @@ probably comes from focus and scroll.</p>
window. When scroll begins, the “value-changed” signal on the vertical
adjustment of the scrolled window is emitted.</p>
<p>The following is extracted from the ui file and C source file.</p>
<div class="sourceCode" id="cb7"><pre
class="sourceCode xml"><code class="sourceCode xml"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a>... ... ...</span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>&lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkScrolledWindow&quot;</span>&gt;</span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;hexpand&quot;</span>&gt;TRUE&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;vexpand&quot;</span>&gt;TRUE&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;vadjustment&quot;</span>&gt;</span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkAdjustment&quot;</span>&gt;</span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a> &lt;<span class="kw">signal</span><span class="ot"> name=</span><span class="st">&quot;value-changed&quot;</span><span class="ot"> handler=</span><span class="st">&quot;adjustment_value_changed_cb&quot;</span><span class="ot"> swapped=</span><span class="st">&quot;no&quot;</span><span class="ot"> object=</span><span class="st">&quot;LeWindow&quot;</span>/&gt;</span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a> &lt;/<span class="kw">property</span>&gt;</span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a>... ... ... </span></code></pre></div>
<div class="sourceCode" id="cb8"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb8-1"><a href="#cb8-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb8-2"><a href="#cb8-2"></a>adjustment_value_changed_cb <span class="op">(</span>GtkAdjustment <span class="op">*</span>adjustment<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span>
<span id="cb8-3"><a href="#cb8-3"></a> GtkWidget <span class="op">*</span>win <span class="op">=</span> GTK_WIDGET <span class="op">(</span>user_data<span class="op">);</span></span>
<span id="cb8-4"><a href="#cb8-4"></a></span>
<span id="cb8-5"><a href="#cb8-5"></a> gtk_window_set_focus <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb8-6"><a href="#cb8-6"></a><span class="op">}</span></span></code></pre></div>
<div class="sourceCode" id="cb9"><pre
class="sourceCode xml"><code class="sourceCode xml"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a>... ... ...</span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a>&lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkScrolledWindow&quot;</span>&gt;</span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;hexpand&quot;</span>&gt;TRUE&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;vexpand&quot;</span>&gt;TRUE&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;vadjustment&quot;</span>&gt;</span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkAdjustment&quot;</span>&gt;</span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a> &lt;<span class="kw">signal</span><span class="ot"> name=</span><span class="st">&quot;value-changed&quot;</span><span class="ot"> handler=</span><span class="st">&quot;adjustment_value_changed_cb&quot;</span><span class="ot"> swapped=</span><span class="st">&quot;no&quot;</span><span class="ot"> object=</span><span class="st">&quot;LeWindow&quot;</span>/&gt;</span>
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true" tabindex="-1"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb9-9"><a href="#cb9-9" aria-hidden="true" tabindex="-1"></a> &lt;/<span class="kw">property</span>&gt;</span>
<span id="cb9-10"><a href="#cb9-10" aria-hidden="true" tabindex="-1"></a>... ... ... </span></code></pre></div>
<div class="sourceCode" id="cb10"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb10-1"><a href="#cb10-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb10-2"><a href="#cb10-2"></a>adjustment_value_changed_cb <span class="op">(</span>GtkAdjustment <span class="op">*</span>adjustment<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span>
<span id="cb10-3"><a href="#cb10-3"></a> GtkWidget <span class="op">*</span>win <span class="op">=</span> GTK_WIDGET <span class="op">(</span>user_data<span class="op">);</span></span>
<span id="cb10-4"><a href="#cb10-4"></a></span>
<span id="cb10-5"><a href="#cb10-5"></a> gtk_window_set_focus <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb10-6"><a href="#cb10-6"></a><span class="op">}</span></span></code></pre></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
</body>

View file

@ -1,486 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<title>GTK 4 tutorial</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
pre{overflow: visible;}
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::after
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
code span.at { color: #7d9029; } /* Attribute */
code span.bn { color: #40a070; } /* BaseN */
code span.bu { } /* BuiltIn */
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
code span.ch { color: #4070a0; } /* Char */
code span.cn { color: #880000; } /* Constant */
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
code span.dt { color: #902000; } /* DataType */
code span.dv { color: #40a070; } /* DecVal */
code span.er { color: #ff0000; font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #40a070; } /* Float */
code span.fu { color: #06287e; } /* Function */
code span.im { } /* Import */
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
code span.op { color: #666666; } /* Operator */
code span.ot { color: #007020; } /* Other */
code span.pp { color: #bc7a00; } /* Preprocessor */
code span.sc { color: #4070a0; } /* SpecialChar */
code span.ss { color: #bb6688; } /* SpecialString */
code span.st { color: #4070a0; } /* String */
code span.va { color: #19177c; } /* Variable */
code span.vs { color: #4070a0; } /* VerbatimString */
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
td {padding: 2px 6px; border: 1px solid;}
img {display: block; margin-left: auto; margin-right: auto;}
figcaption {text-align: center;}
</style>
</head>
<body style="padding-top: 70px;">
<div class="container">
<nav class="navbar fixed-top navbar-expand-lg navbar-dark bg-primary">
<div class="container-fluid">
<span class="navbar-brand">Gtk4 tutorial</span>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link" href="index.html">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="sec33.html">Prev: section33</a>
</li>
</ul>
</div>
</div>
</nav>
<h1 id="gtksignallistitemfactory">GtkSignalListItemFactory</h1>
<h2
id="gtksignallistitemfactory-and-gtkbulderlistitemfactory">GtkSignalListItemFactory
and GtkBulderListItemFactory</h2>
<p>GtkBuilderlistItemFactory is convenient when GtkListView just shows
the contents of a list. Its binding direction is always from an item of
a list to a child of GtkListItem.</p>
<p>When it comes to dynamic connection, its not enough. For example,
you want to edit the contents of a list. You set a child of GtkListItem
to a GtkText instance so a user can edit a text with it. You need to
bind an item in the list with the buffer of the GtkText. The direction
is opposite from the one with GtkBuilderListItemFactory. It is from the
GtkText instance to the item in the list. You can implement this with
GtkSignalListItemFactory, which is more flexible than
GtkBuilderListItemFactory.</p>
<p>Two things are shown in this section.</p>
<ul>
<li>Binding from a child of a GtkListItem instance to an item of a
list.</li>
<li>Access a child of GtkListItem dynamically. This direction is the
same as the one with GtkBulderListItemFactory. But
GtkBulderListItemFactory uses GtkExpression from the item property of
the GtkListItem. So, it updates its child widget only when the item
property changes. In this example the child reflects the change in the
same item in the list dynamically.</li>
</ul>
<p>This section shows just a part of the source file
<code>listeditor.c</code>. If you want to see the whole codes, see
<code>src/listeditor</code> directory of the <a
href="https://github.com/ToshioCP/Gtk4-tutorial">Gtk4 tutorial
repository</a>.</p>
<h2 id="a-list-editor">A list editor</h2>
<p>The sample program is a list editor and data of the list are strings.
Its the same as a line editor. It reads a text file line by line. Each
line is an item of the list. The list is displayed with GtkColumnView.
There are two columns. The one is a button, which makes the line be a
current line. If the line is the current line, the button is colored
with red. The other is a string which is the contents of the
corresponding item of the list.</p>
<figure>
<img src="image/listeditor.png" alt="List editor" />
<figcaption aria-hidden="true">List editor</figcaption>
</figure>
<p>The source files are located at <code>src/listeditor</code>
directory. You can compile end execute it as follows.</p>
<ul>
<li>Download the program from the <a
href="https://github.com/ToshioCP/Gtk4-tutorial">repository</a>.</li>
<li>Change your current directory to <code>src/listeditor</code>.</li>
<li>Type the following on your commandline.</li>
</ul>
<pre><code>$ meson _build
$ ninja -C _build
$ _build/listeditor</code></pre>
<ul>
<li>Append button: appends a line after the current line, or at the last
line if no current line exists.</li>
<li>Insert button: inserts a line before the current line.</li>
<li>Remove button: removes a current line.</li>
<li>Read button: reads a file.</li>
<li>Write button: writes the contents to a file.</li>
<li>close button: close the contents.</li>
<li>quit button: quit the application.</li>
<li>Button on the select column: makes the line current.</li>
<li>String column: GtkText. You can edit a string in the field.</li>
</ul>
<p>The current line number (zero-based) is shown at the left of the tool
bar. The file name is shown at the right of the write button.</p>
<h2 id="connect-a-gtktext-instance-and-an-item-in-the-list">Connect a
GtkText instance and an item in the list</h2>
<p>The second column (GtkColumnViewColumn) sets its factory property to
GtkSignalListItemFactory. It uses three signals setup, bind and unbind.
The following is their sgnal handlers.</p>
<div class="sourceCode" id="cb2"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb2-1"><a href="#cb2-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb2-2"><a href="#cb2-2"></a>setup2_cb <span class="op">(</span>GtkListItemFactory <span class="op">*</span>factory<span class="op">,</span> GtkListItem <span class="op">*</span>listitem<span class="op">)</span> <span class="op">{</span></span>
<span id="cb2-3"><a href="#cb2-3"></a> GtkWidget <span class="op">*</span>text <span class="op">=</span> gtk_text_new <span class="op">();</span></span>
<span id="cb2-4"><a href="#cb2-4"></a> gtk_list_item_set_child <span class="op">(</span>listitem<span class="op">,</span> GTK_WIDGET <span class="op">(</span>text<span class="op">));</span></span>
<span id="cb2-5"><a href="#cb2-5"></a> gtk_editable_set_alignment <span class="op">(</span>GTK_EDITABLE <span class="op">(</span>text<span class="op">),</span> <span class="fl">0.0</span><span class="op">);</span></span>
<span id="cb2-6"><a href="#cb2-6"></a><span class="op">}</span></span>
<span id="cb2-7"><a href="#cb2-7"></a></span>
<span id="cb2-8"><a href="#cb2-8"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb2-9"><a href="#cb2-9"></a>bind2_cb <span class="op">(</span>GtkListItemFactory <span class="op">*</span>factory<span class="op">,</span> GtkListItem <span class="op">*</span>listitem<span class="op">)</span> <span class="op">{</span></span>
<span id="cb2-10"><a href="#cb2-10"></a> GtkWidget <span class="op">*</span>text <span class="op">=</span> gtk_list_item_get_child <span class="op">(</span>listitem<span class="op">);</span></span>
<span id="cb2-11"><a href="#cb2-11"></a> GtkEntryBuffer <span class="op">*</span>buffer <span class="op">=</span> gtk_text_get_buffer <span class="op">(</span>GTK_TEXT <span class="op">(</span>text<span class="op">));</span></span>
<span id="cb2-12"><a href="#cb2-12"></a> LeData <span class="op">*</span>data <span class="op">=</span> LE_DATA <span class="op">(</span>gtk_list_item_get_item <span class="op">(</span>listitem<span class="op">));</span></span>
<span id="cb2-13"><a href="#cb2-13"></a> GBinding <span class="op">*</span>bind<span class="op">;</span></span>
<span id="cb2-14"><a href="#cb2-14"></a></span>
<span id="cb2-15"><a href="#cb2-15"></a> gtk_editable_set_text <span class="op">(</span>GTK_EDITABLE <span class="op">(</span>text<span class="op">),</span> le_data_look_string <span class="op">(</span>data<span class="op">));</span></span>
<span id="cb2-16"><a href="#cb2-16"></a> gtk_editable_set_position <span class="op">(</span>GTK_EDITABLE <span class="op">(</span>text<span class="op">),</span> <span class="dv">0</span><span class="op">);</span></span>
<span id="cb2-17"><a href="#cb2-17"></a></span>
<span id="cb2-18"><a href="#cb2-18"></a> bind <span class="op">=</span> g_object_bind_property <span class="op">(</span>buffer<span class="op">,</span> <span class="st">&quot;text&quot;</span><span class="op">,</span> data<span class="op">,</span> <span class="st">&quot;string&quot;</span><span class="op">,</span> G_BINDING_DEFAULT<span class="op">);</span></span>
<span id="cb2-19"><a href="#cb2-19"></a> g_object_set_data <span class="op">(</span>G_OBJECT <span class="op">(</span>listitem<span class="op">),</span> <span class="st">&quot;bind&quot;</span><span class="op">,</span> bind<span class="op">);</span></span>
<span id="cb2-20"><a href="#cb2-20"></a><span class="op">}</span></span>
<span id="cb2-21"><a href="#cb2-21"></a></span>
<span id="cb2-22"><a href="#cb2-22"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb2-23"><a href="#cb2-23"></a>unbind2_cb <span class="op">(</span>GtkListItemFactory <span class="op">*</span>factory<span class="op">,</span> GtkListItem <span class="op">*</span>listitem<span class="op">)</span> <span class="op">{</span></span>
<span id="cb2-24"><a href="#cb2-24"></a> GBinding <span class="op">*</span>bind <span class="op">=</span> G_BINDING <span class="op">(</span>g_object_get_data <span class="op">(</span>G_OBJECT <span class="op">(</span>listitem<span class="op">),</span> <span class="st">&quot;bind&quot;</span><span class="op">));</span></span>
<span id="cb2-25"><a href="#cb2-25"></a></span>
<span id="cb2-26"><a href="#cb2-26"></a> g_binding_unbind<span class="op">(</span>bind<span class="op">);</span></span>
<span id="cb2-27"><a href="#cb2-27"></a> g_object_set_data <span class="op">(</span>G_OBJECT <span class="op">(</span>listitem<span class="op">),</span> <span class="st">&quot;bind&quot;</span><span class="op">,</span> NULL<span class="op">);</span></span>
<span id="cb2-28"><a href="#cb2-28"></a><span class="op">}</span></span></code></pre></div>
<ul>
<li>1-6: <code>setup2_cb</code> is a setup signal handler on the
GtkSignalListItemFactory. This factory is inserted to the factory
property of the second GtkColumnViewColumn. The handler just creates a
GtkText instance and sets the child of <code>listitem</code> to it. The
instance will be destroyed automatically when the <code>listitem</code>
is destroyed. So, teardown signal handler isnt necessary.</li>
<li>8-20: <code>bind2_cb</code> is a bind signal handler. It is called
when the <code>listitem</code> is bound to an item in the list. The list
items are LeData instances. LeData is defined in the file
<code>listeditor.c</code> (the C source file of the list editor). It is
a child class of GObject and has two data. The one is
<code>listitem</code> which points a first column GtkListItem instance
when they are connected. Be careful that the GtkListItem instance is
<em>not</em> the <code>listitem</code> in this handler. If no
GtkListItem is connected, it is NULL. The other is <code>string</code>
which is a content of the line.
<ul>
<li>10-11: <code>text</code> is a child of the <code>listitem</code> and
it is a GtkText instance. And <code>buffer</code> is a GtkTextBuffer
instance of the <code>text</code>.</li>
<li>12: The LeData instance <code>data</code> is an item pointed by the
<code>listitem</code>.</li>
<li>15-16: Sets the text of <code>text</code> to
<code>le_data_look_string (data)</code>. le_data_look_string returns the
string of the <code>data</code> and the ownership of the string is still
taken by the <code>data</code>. So, the caller dont need to free the
string.</li>
<li>18: <code>g_object_bind_property</code> binds a property and another
object property. This line binds the “text” property of the
<code>buffer</code> (source) and the “string” property of the
<code>data</code> (destination). It is a uni-directional binding
(<code>G_BINDING_DEFAULT</code>). When a user changes the GtkText text,
the same string is immediately put into the <code>data</code>. The
function returns a GBinding instance. This binding is different from
bindings of GtkExpression. This binding needs the existence of the two
properties.</li>
<li>19: GObjec has a table. The key is a string (or GQuark) and the
value is a gpointer (pointer to any type). The function
<code>g_object_set_data</code> sets the association from the key to the
value. This line sets the association from “bind” to <code>bind</code>
instance. It makes possible for the “unbind” handler to get the
<code>bind</code> instance.</li>
</ul></li>
<li>22-28: <code>unbind2_cb</code> is a unbind signal handler.
<ul>
<li>24: Retrieves the <code>bind</code> instance from the table in the
<code>listitem</code> instance.</li>
<li>26: Unbind the binding.</li>
<li>27: Removes the value corresponds to the “bind” key.</li>
</ul></li>
</ul>
<p>This technique is not so complicated. You can use it when you make a
cell editable application.</p>
<h2 id="change-the-cell-of-gtkcolumnview-dynamically">Change the cell of
GtkColumnView dynamically</h2>
<p>Next topic is to change the GtkColumnView (or GtkListView) cells
dynamically. The example changes the color of the buttons, which are
children of GtkListItem instances, as the current line position
moves.</p>
<p>The line editor has the current position of the list.</p>
<ul>
<li>At first, no line is current.</li>
<li>When a line is appended or inserted, the line is current.</li>
<li>When the current line is deleted, no line will be current.</li>
<li>When a button in the first column of GtkColumnView is clicked, the
line will be current.</li>
<li>It is necessary to set the line status (whether current or not) when
a GtkListItem is bound to an item in the list. It is because GtkListItem
is recycled. A GtkListItem was possibly current line before but not
current after recycled. The opposite can also be happen.</li>
</ul>
<p>The button of the current line is colored with red and otherwise
white.</p>
<p>The current line has no relationship to GtkSingleSelection object.
GtkSingleSelection selects a line on the display. The current line
doesnt need to be on the display. It is possible to be on the line out
of the Window (GtkScrolledWindow). Actually, the program doesnt use
GtkSingleSelection.</p>
<p>It is necessary to know the corresponding GtkListItem instance from
the item in the list. It is the opposite direction from
<code>gtk_list_item_get_item</code> function. To accomplish this, we set
a <code>listitem</code> element of LeData to point the corresponding
GtkListItem instance. Therefore, items (LeData) in the list always know
the GtkListItem. If theres no GtkListItem bound to the item, NULL is
assigned.</p>
<div class="sourceCode" id="cb3"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1"></a><span class="dt">void</span></span>
<span id="cb3-2"><a href="#cb3-2"></a>select_cb <span class="op">(</span>GtkButton <span class="op">*</span>btn<span class="op">,</span> GtkListItem <span class="op">*</span>listitem<span class="op">)</span> <span class="op">{</span></span>
<span id="cb3-3"><a href="#cb3-3"></a> LeWindow <span class="op">*</span>win <span class="op">=</span> LE_WINDOW <span class="op">(</span>gtk_widget_get_ancestor <span class="op">(</span>GTK_WIDGET <span class="op">(</span>btn<span class="op">),</span> LE_TYPE_WINDOW<span class="op">));</span></span>
<span id="cb3-4"><a href="#cb3-4"></a></span>
<span id="cb3-5"><a href="#cb3-5"></a> update_current <span class="op">(</span>win<span class="op">,</span> gtk_list_item_get_position <span class="op">(</span>listitem<span class="op">));</span></span>
<span id="cb3-6"><a href="#cb3-6"></a><span class="op">}</span></span>
<span id="cb3-7"><a href="#cb3-7"></a></span>
<span id="cb3-8"><a href="#cb3-8"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb3-9"><a href="#cb3-9"></a>setup1_cb <span class="op">(</span>GtkListItemFactory <span class="op">*</span>factory<span class="op">,</span> GtkListItem <span class="op">*</span>listitem<span class="op">)</span> <span class="op">{</span></span>
<span id="cb3-10"><a href="#cb3-10"></a> GtkWidget <span class="op">*</span>button <span class="op">=</span> gtk_button_new <span class="op">();</span></span>
<span id="cb3-11"><a href="#cb3-11"></a> gtk_list_item_set_child <span class="op">(</span>listitem<span class="op">,</span> button<span class="op">);</span></span>
<span id="cb3-12"><a href="#cb3-12"></a> gtk_widget_set_focusable <span class="op">(</span>GTK_WIDGET <span class="op">(</span>button<span class="op">),</span> FALSE<span class="op">);</span></span>
<span id="cb3-13"><a href="#cb3-13"></a> g_signal_connect <span class="op">(</span>button<span class="op">,</span> <span class="st">&quot;clicked&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>select_cb<span class="op">),</span> listitem<span class="op">);</span></span>
<span id="cb3-14"><a href="#cb3-14"></a><span class="op">}</span></span>
<span id="cb3-15"><a href="#cb3-15"></a></span>
<span id="cb3-16"><a href="#cb3-16"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb3-17"><a href="#cb3-17"></a>bind1_cb <span class="op">(</span>GtkListItemFactory <span class="op">*</span>factory<span class="op">,</span> GtkListItem <span class="op">*</span>listitem<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span>
<span id="cb3-18"><a href="#cb3-18"></a> LeWindow <span class="op">*</span>win <span class="op">=</span> LE_WINDOW <span class="op">(</span>user_data<span class="op">);</span></span>
<span id="cb3-19"><a href="#cb3-19"></a> GtkWidget <span class="op">*</span>button <span class="op">=</span> gtk_list_item_get_child <span class="op">(</span>listitem<span class="op">);</span></span>
<span id="cb3-20"><a href="#cb3-20"></a> <span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span>non_current<span class="op">[</span><span class="dv">1</span><span class="op">]</span> <span class="op">=</span> <span class="op">{</span>NULL<span class="op">};</span></span>
<span id="cb3-21"><a href="#cb3-21"></a> <span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span>current<span class="op">[</span><span class="dv">2</span><span class="op">]</span> <span class="op">=</span> <span class="op">{</span><span class="st">&quot;current&quot;</span><span class="op">,</span> NULL<span class="op">};</span></span>
<span id="cb3-22"><a href="#cb3-22"></a> LeData <span class="op">*</span>data <span class="op">=</span> LE_DATA <span class="op">(</span>gtk_list_item_get_item <span class="op">(</span>listitem<span class="op">));</span></span>
<span id="cb3-23"><a href="#cb3-23"></a></span>
<span id="cb3-24"><a href="#cb3-24"></a> <span class="cf">if</span> <span class="op">(</span>data<span class="op">)</span> <span class="op">{</span></span>
<span id="cb3-25"><a href="#cb3-25"></a> le_data_set_listitem <span class="op">(</span>data<span class="op">,</span> listitem<span class="op">);</span></span>
<span id="cb3-26"><a href="#cb3-26"></a> <span class="cf">if</span> <span class="op">(</span>win<span class="op">-&gt;</span>position <span class="op">==</span> gtk_list_item_get_position <span class="op">(</span>listitem<span class="op">))</span></span>
<span id="cb3-27"><a href="#cb3-27"></a> gtk_widget_set_css_classes <span class="op">(</span>GTK_WIDGET <span class="op">(</span>button<span class="op">),</span> current<span class="op">);</span></span>
<span id="cb3-28"><a href="#cb3-28"></a> <span class="cf">else</span></span>
<span id="cb3-29"><a href="#cb3-29"></a> gtk_widget_set_css_classes <span class="op">(</span>GTK_WIDGET <span class="op">(</span>button<span class="op">),</span> non_current<span class="op">);</span></span>
<span id="cb3-30"><a href="#cb3-30"></a> <span class="op">}</span></span>
<span id="cb3-31"><a href="#cb3-31"></a><span class="op">}</span></span>
<span id="cb3-32"><a href="#cb3-32"></a></span>
<span id="cb3-33"><a href="#cb3-33"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb3-34"><a href="#cb3-34"></a>unbind1_cb <span class="op">(</span>GtkListItemFactory <span class="op">*</span>factory<span class="op">,</span> GtkListItem <span class="op">*</span>listitem<span class="op">)</span> <span class="op">{</span></span>
<span id="cb3-35"><a href="#cb3-35"></a> LeData <span class="op">*</span>data <span class="op">=</span> LE_DATA <span class="op">(</span>gtk_list_item_get_item <span class="op">(</span>listitem<span class="op">));</span></span>
<span id="cb3-36"><a href="#cb3-36"></a> <span class="cf">if</span> <span class="op">(</span>data<span class="op">)</span></span>
<span id="cb3-37"><a href="#cb3-37"></a> le_data_set_listitem <span class="op">(</span>data<span class="op">,</span> NULL<span class="op">);</span></span>
<span id="cb3-38"><a href="#cb3-38"></a><span class="op">}</span></span></code></pre></div>
<ul>
<li>8-14: <code>setup1_cb</code> is a setup signal handler on the
GtkSignalListItemFactory. This factory is inserted to the factory
property of the first GtkColumnViewColumn. It sets the child of
<code>listitem</code> to a GtkButton instance. The “clicked” signal on
the button is connected to the handler <code>select_cb</code>. When the
listitem is destroyed, the child (GtkButton) is also destroyed. At the
same time, the connection of the signal and the handler is also
destroyed. So, you dont need teardown signal handler.</li>
<li>1-6: <code>select_cb</code> is a “clicked” signal handler. LeWindow
is defined in <code>listeditor.c</code>. Its a child class of
GtkApplicationWindow. The handler just calls the
<code>update_current</code> function. The function will be explained
later.</li>
<li>16-31: <code>bind1_cb</code> is a bind signal handler. It sets the
“listitem” element of the item (LeData) to point the
<code>listitem</code> (GtkListItem instance). It makes the item possible
to find the corresponding GtkListItem instance. If the item is the
current line, the CSS class of the button includes “current” class.
Otherwise it has no CSS class. This is necessary because the button may
be recycled and it has had former CSS class. The class need to be
updated.</li>
<li>33-38: <code>unbind1_cb</code> is an unbind signal handler. It
removes the <code>listitem</code> instance from the “listitem” element
of the item. The element becomes NULL, which tells no GtkListItem is
bound. When referring GtkListItem, it needs to check the “listitem”
element whether it points a GtkListItem or not (NULL). Otherwise bad
things will happen.</li>
</ul>
<div class="sourceCode" id="cb4"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb4-2"><a href="#cb4-2"></a>update_current <span class="op">(</span>LeWindow <span class="op">*</span>win<span class="op">,</span> <span class="dt">int</span> new<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-3"><a href="#cb4-3"></a> <span class="dt">char</span> <span class="op">*</span>s<span class="op">;</span></span>
<span id="cb4-4"><a href="#cb4-4"></a> LeData <span class="op">*</span>data<span class="op">;</span></span>
<span id="cb4-5"><a href="#cb4-5"></a> GtkListItem <span class="op">*</span>listitem<span class="op">;</span></span>
<span id="cb4-6"><a href="#cb4-6"></a> GtkButton <span class="op">*</span>button<span class="op">;</span></span>
<span id="cb4-7"><a href="#cb4-7"></a> <span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span>non_current<span class="op">[</span><span class="dv">1</span><span class="op">]</span> <span class="op">=</span> <span class="op">{</span>NULL<span class="op">};</span></span>
<span id="cb4-8"><a href="#cb4-8"></a> <span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span>current<span class="op">[</span><span class="dv">2</span><span class="op">]</span> <span class="op">=</span> <span class="op">{</span><span class="st">&quot;current&quot;</span><span class="op">,</span> NULL<span class="op">};</span></span>
<span id="cb4-9"><a href="#cb4-9"></a></span>
<span id="cb4-10"><a href="#cb4-10"></a> <span class="cf">if</span> <span class="op">(</span>new <span class="op">&gt;=</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb4-11"><a href="#cb4-11"></a> s <span class="op">=</span> g_strdup_printf <span class="op">(</span><span class="st">&quot;%d&quot;</span><span class="op">,</span> new<span class="op">);</span></span>
<span id="cb4-12"><a href="#cb4-12"></a> <span class="cf">else</span></span>
<span id="cb4-13"><a href="#cb4-13"></a> s <span class="op">=</span> <span class="st">&quot;&quot;</span><span class="op">;</span></span>
<span id="cb4-14"><a href="#cb4-14"></a> gtk_label_set_text <span class="op">(</span>GTK_LABEL <span class="op">(</span>win<span class="op">-&gt;</span>position_label<span class="op">),</span> s<span class="op">);</span></span>
<span id="cb4-15"><a href="#cb4-15"></a> <span class="cf">if</span> <span class="op">(*</span>s<span class="op">)</span> <span class="co">// s isn&#39;t an empty string</span></span>
<span id="cb4-16"><a href="#cb4-16"></a> g_free <span class="op">(</span>s<span class="op">);</span></span>
<span id="cb4-17"><a href="#cb4-17"></a></span>
<span id="cb4-18"><a href="#cb4-18"></a> <span class="cf">if</span> <span class="op">(</span>win<span class="op">-&gt;</span>position <span class="op">&gt;=</span><span class="dv">0</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-19"><a href="#cb4-19"></a> data <span class="op">=</span> LE_DATA <span class="op">(</span>g_list_model_get_item <span class="op">(</span>G_LIST_MODEL <span class="op">(</span>win<span class="op">-&gt;</span>liststore<span class="op">),</span> win<span class="op">-&gt;</span>position<span class="op">));</span></span>
<span id="cb4-20"><a href="#cb4-20"></a> <span class="cf">if</span> <span class="op">((</span>listitem <span class="op">=</span> le_data_get_listitem <span class="op">(</span>data<span class="op">))</span> <span class="op">!=</span> NULL<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-21"><a href="#cb4-21"></a> button <span class="op">=</span> GTK_BUTTON <span class="op">(</span>gtk_list_item_get_child <span class="op">(</span>listitem<span class="op">));</span></span>
<span id="cb4-22"><a href="#cb4-22"></a> gtk_widget_set_css_classes <span class="op">(</span>GTK_WIDGET <span class="op">(</span>button<span class="op">),</span> non_current<span class="op">);</span></span>
<span id="cb4-23"><a href="#cb4-23"></a> g_object_unref <span class="op">(</span>listitem<span class="op">);</span></span>
<span id="cb4-24"><a href="#cb4-24"></a> <span class="op">}</span></span>
<span id="cb4-25"><a href="#cb4-25"></a> g_object_unref <span class="op">(</span>data<span class="op">);</span></span>
<span id="cb4-26"><a href="#cb4-26"></a> <span class="op">}</span></span>
<span id="cb4-27"><a href="#cb4-27"></a> win<span class="op">-&gt;</span>position <span class="op">=</span> new<span class="op">;</span></span>
<span id="cb4-28"><a href="#cb4-28"></a> <span class="cf">if</span> <span class="op">(</span>win<span class="op">-&gt;</span>position <span class="op">&gt;=</span><span class="dv">0</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-29"><a href="#cb4-29"></a> data <span class="op">=</span> LE_DATA <span class="op">(</span>g_list_model_get_item <span class="op">(</span>G_LIST_MODEL <span class="op">(</span>win<span class="op">-&gt;</span>liststore<span class="op">),</span> win<span class="op">-&gt;</span>position<span class="op">));</span></span>
<span id="cb4-30"><a href="#cb4-30"></a> <span class="cf">if</span> <span class="op">((</span>listitem <span class="op">=</span> le_data_get_listitem <span class="op">(</span>data<span class="op">))</span> <span class="op">!=</span> NULL<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-31"><a href="#cb4-31"></a> button <span class="op">=</span> GTK_BUTTON <span class="op">(</span>gtk_list_item_get_child <span class="op">(</span>listitem<span class="op">));</span></span>
<span id="cb4-32"><a href="#cb4-32"></a> gtk_widget_set_css_classes <span class="op">(</span>GTK_WIDGET <span class="op">(</span>button<span class="op">),</span> current<span class="op">);</span></span>
<span id="cb4-33"><a href="#cb4-33"></a> g_object_unref <span class="op">(</span>listitem<span class="op">);</span></span>
<span id="cb4-34"><a href="#cb4-34"></a> <span class="op">}</span></span>
<span id="cb4-35"><a href="#cb4-35"></a> g_object_unref <span class="op">(</span>data<span class="op">);</span></span>
<span id="cb4-36"><a href="#cb4-36"></a> <span class="op">}</span></span>
<span id="cb4-37"><a href="#cb4-37"></a><span class="op">}</span></span></code></pre></div>
<p>The function <code>update_current</code> does several things.</p>
<ul>
<li>It has two parameters. The first one is <code>win</code>, which is
an instance of LeWindow class. It has some elements.
<ul>
<li>win-&gt;position: an Integer. it is the current position. If no
current line exists, it is -1.</li>
<li>win-&gt;position_label: GtkLabel. It shows the current
position.</li>
</ul></li>
<li>The second parameter is <code>new</code>, which is the new current
position. At the beginning of the function, win-&gt;position points the
old position.</li>
<li>10-16: Update the text of GtkLabel.</li>
<li>18-26: If the old position (win-&gt;position) is not negative, the
current line exists. It gets a GtkListItem instance via the item
(LeData) of the list. And it gets the GtkButton instance which is the
child of the GtkListItem. It clears the “css-classes” property of the
button.</li>
<li>27: Updates win-&gt;position.</li>
<li>28-36: If the new position is not negative (Its possible to be
negative when the current line has been removed), the current line
exists. It sets the “css-classes” property of the button to
<code>{"current", NULL}</code>. It is a NULL-terminated array of
strings. Each string is a CSS class. Now the button has “current” style
class.</li>
</ul>
<p>The color of buttons are determined by the “background” CSS style.
The following CSS is applied to the default GdkDisplay in advance (in
the startup handler of the application).</p>
<div class="sourceCode" id="cb5"><pre
class="sourceCode css"><code class="sourceCode css"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a>columnview listview row button<span class="fu">.current</span> {<span class="kw">background</span>: <span class="cn">red</span><span class="op">;</span>}</span></code></pre></div>
<p>The selectors “columnview listview row” is needed before “button”
selector. Otherwise the buttons in the GtkColumnview wont be found. The
button selector has “current” class. So, the only “current” class button
is colored with red. Other buttons are not colored, which means they are
white.</p>
<h2
id="gtk_widget_dispose_template-function">Gtk_widget_dispose_template
function</h2>
<p>The function <code>gtk_widget_dispose_template</code> clears the
template children for the given widget. This is the opposite of
<code>gtk_widget_init_template()</code>. It is a new function of GTK 4.8
version. If your GTK version is lower than 4.8, you need to modify the
program.</p>
<h2 id="a-waring-from-gtktext">A waring from GtkText</h2>
<p>If your program has the following two, a warning message can be
issued.</p>
<ul>
<li>The list has many items and it needs to be scrolled.</li>
<li>A GtkText instance is the focus widget.</li>
</ul>
<pre><code>GtkText - unexpected blinking selection. Removing</code></pre>
<p>I dont have an exact idea why this happens. But if GtkText
“focusable” property is FALSE, the warning doesnt happen. So it
probably comes from focus and scroll.</p>
<p>You can avoid this by unsetting any focus widget under the main
window. When scroll begins, the “value-changed” signal on the vertical
adjustment of the scrolled window is emitted.</p>
<p>The following is extracted from the ui file and C source file.</p>
<div class="sourceCode" id="cb7"><pre
class="sourceCode xml"><code class="sourceCode xml"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a>... ... ...</span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>&lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkScrolledWindow&quot;</span>&gt;</span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;hexpand&quot;</span>&gt;TRUE&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;vexpand&quot;</span>&gt;TRUE&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;vadjustment&quot;</span>&gt;</span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkAdjustment&quot;</span>&gt;</span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a> &lt;<span class="kw">signal</span><span class="ot"> name=</span><span class="st">&quot;value-changed&quot;</span><span class="ot"> handler=</span><span class="st">&quot;adjustment_value_changed_cb&quot;</span><span class="ot"> swapped=</span><span class="st">&quot;no&quot;</span><span class="ot"> object=</span><span class="st">&quot;LeWindow&quot;</span>/&gt;</span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a> &lt;/<span class="kw">property</span>&gt;</span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a>... ... ... </span></code></pre></div>
<div class="sourceCode" id="cb8"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb8-1"><a href="#cb8-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb8-2"><a href="#cb8-2"></a>adjustment_value_changed_cb <span class="op">(</span>GtkAdjustment <span class="op">*</span>adjustment<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span>
<span id="cb8-3"><a href="#cb8-3"></a> GtkWidget <span class="op">*</span>win <span class="op">=</span> GTK_WIDGET <span class="op">(</span>user_data<span class="op">);</span></span>
<span id="cb8-4"><a href="#cb8-4"></a></span>
<span id="cb8-5"><a href="#cb8-5"></a> gtk_window_set_focus <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb8-6"><a href="#cb8-6"></a><span class="op">}</span></span></code></pre></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
</body>
</html>

View file

@ -8,9 +8,10 @@ The new feature is described in [Gtk API Reference -- List Widget Overview](http
GTK 4 has other means to implement lists.
They are GtkListBox and GtkTreeView which are took over from GTK 3.
There's an article in [Gtk Development blog](https://blog.gtk.org/2020/06/07/scalable-lists-in-gtk-4/) about list widgets by Matthias Clasen.
He described why GtkListView are developed to replace GtkListBox and GtkTreeView.
He described why GtkListView are developed to replace GtkTreeView.
GtkTreeView is deprecated since version 4.10.
GtkListView, GtkGridView, GtkColumnView and related objects are described in Section 26 to 29.
GtkListView, GtkGridView, GtkColumnView and related objects are described in Section 29 to 33.
## Outline
@ -21,7 +22,7 @@ A list is like an array, but in many cases it is implemented with pointers which
And it has a start point.
So, each item can be referred by the index of the item (first item, second item, ..., nth item, ...).
There are two cases.
One is the index starts from one (one-based) and the other is from zero (zero-based).
The one is the index starts from one (one-based) and the other is from zero (zero-based).
Gio provides GListModel interface.
It is a zero-based list and its items are the same type of GObject descendants, or objects that implement the same interface.
@ -29,11 +30,11 @@ An object implements GListModel is not a widget.
So, the list is not displayed on the screen directly.
There's another object GtkListView which is a widget to display the list.
The items in the list need to be connected to the items in GtkListView.
GtkListItemFactory instance maps items in the list to GListView.
GtkListItemFactory instance maps items in the list to GtkListView.
![List](../image/list.png)
## GListModel
## GListModel and GtkStringList
If you want to make a list of strings with GListModel, for example, "one", "two", "three", "four", note that strings can't be items of the list.
Because GListModel is a list of GObject objects and strings aren't GObject objects.
@ -253,7 +254,7 @@ There is an explanation in the [GTK Development Blog](https://blog.gtk.org/2020/
> Remember that the classname (GtkListItem) in a ui template is used as the “this” pointer referring to the object that is being instantiated.
Therefore, GtkListItem instance is used as the `this` object of the lookup tag when it is evaluated.
`this` object will be explained in [section 30](../src/sec30).
`this` object will be explained in [section 31](../src/sec31).
The C source code is as follows.
Its name is `list2.c` and located under [src/misc](../src/misc) directory.
@ -370,7 +371,7 @@ Instead, closure tag is appropriate in this case.
Closure tag specifies a function and the type of the return value of the function.
~~~C
const char *
char *
get_file_name (GtkListItem *item, GFileInfo *info) {
return G_IS_FILE_INFO (info) ? g_strdup (g_file_info_get_name (info)) : NULL;
}
@ -406,7 +407,7 @@ The contents of closure tag (it is between \<closure...\> and\</closure\>) is pa
`<lookup name="item">GtkListItem</lookup>` gives the value of the item property of the GtkListItem.
This will be the second argument of the function.
The first parameter is always the GListItem instance, which is a 'this' object.
The 'this' object is explained in section 28.
The 'this' object is explained in section 31.
- `gtk_file_name` function is the callback function for the closure tag.
It first checks the `info` parameter.
Because it can be NULL when GListItem `item` is unbounded.
@ -531,5 +532,4 @@ $ ./a.out
![screenshot list3](../image/list3.png)
Up: [README.md](../README.md), Prev: [Section 28](sec28.md), Next: [Section 30](sec30.md)

View file

@ -64,7 +64,7 @@ Such "x-" subtype is not a standard mime type.)
Content type is also used by desktop systems.
GtkGridView uses the same GtkSingleSelection instance (`singleselection`).
So, its model property is set with it.
So, its model property is set to it.
## Ui file of the window
@ -467,7 +467,7 @@ The last parameter is the pointer to the pointer to a GError.
If your distribution supports GTK 4, using `g_app_info_launch_default_for_uri` is convenient.
The function automatically determines the default application from the file and launches it.
For example, if the file is text, then it launches gedit with the file.
For example, if the file is text, then it launches GNOME text editor with the file.
Such feature comes from desktop.
## Compilation and execution
@ -477,7 +477,7 @@ To compile and execute list4, type as follows.
~~~
$ cd list4 # or cd src/list4. It depends your current directory.
$ meson _build
$ meson setup _build
$ ninja -C _build
$ _build/list4
~~~

View file

@ -58,8 +58,8 @@ The type of the value is int.
|G\_TYPE\_INT |int |gint | |
|G\_TYPE\_FLOAT |float |gfloat | |
|G\_TYPE\_DOUBLE |double|gdouble | |
|G\_TYPE\_POINTER | |gpointer | |
|G\_TYPE\_STRING | |gchararray|null-terminated Cstring|
|G\_TYPE\_POINTER |void *|gpointer |general pointer |
|G\_TYPE\_STRING |char *|gchararray|null-terminated Cstring|
|G\_TYPE\_OBJECT | |GObject | |
|GTK\_TYPE\_WINDOW| |GtkWindow | |
@ -119,7 +119,7 @@ expression = gtk_property_expression_new (GTK_TYPE_LABEL, another_expression, "l
If `expression` is evaluated, the second parameter `another_expression` is evaluated in advance.
The value of `another_expression` is the `label` (GtkLabel instance).
Then, `expression` looks up "label" property of the label and the evaluation results "Hello".
Then, `expression` looks up "label" property of the label and the evaluation results in "Hello".
In the example above, the second argument of `gtk_property_expression_new` is another expression.
But the second argument can be NULL.
@ -145,7 +145,7 @@ There's a simple program `exp_property_simple.c` in `src/expression` directory.
14 if (gtk_expression_evaluate (expression, label, &value))
15 g_print ("The value is %s.\n", g_value_get_string (&value));
16 else
17 g_print ("The constant expression wasn't evaluated correctly.\n");
17 g_print ("The property expression wasn't evaluated correctly.\n");
18 gtk_expression_unref (expression);
19 g_value_unset (&value);
20
@ -164,7 +164,7 @@ The expression just knows how to take the property from a future-given GtkLabel
The result is stored in the GValue `value`.
The function `g_value_get_string` gets a string from the GValue.
But the string is owned by the GValue so you must not free the string.
- 18-19: Release the expression and unset the GValue.
- 18-19: Releases the expression and unset the GValue.
At the same time the string in the GValue is freed.
If the second argument of `gtk_property_expression_new` isn't NULL, it is another expression.
@ -195,6 +195,18 @@ gtk_cclosure_expression_new (GType value_type,
gpointer user_data,
GClosureNotify user_destroy);
~~~
@else
~~~{.C}
GtkExpression *
gtk_cclosure_expression_new (GType value_type,
GClosureMarshal marshal,
guint n_params,
GtkExpression **params,
GCallback callback_func,
gpointer user_data,
GClosureNotify user_destroy);
~~~
@end
- `value_type` is the type of the value when it is evaluated.
- `marshal` is a marshaller.
@ -224,6 +236,7 @@ callback (this, param1, param2, ...)
For example,
@@@if gfm
~~~C
int
callback (GObject *object, int x, const char *s)
@ -276,13 +289,13 @@ If you want to return error report, change the return value type to be a pointer
One for error and the other for the sum.
The first argument of `gtk_cclosure_expression_new` is `G_TYPE_POINTER`.
There is a sample program `exp_closure_with_error_report` in `src/expression` directory.
- 19: gtk\_init initializes GTK. It is necessary for GtkLabel.
- 19: The function `gtk_init`` initializes GTK. It is necessary for GtkLabel.
- 20: A GtkLabel instance is created with "123+456".
- 21: The instance has floating reference. It is changed to an ordinary reference count.
- 22-23: Create a closure expression. Its return value type is `G_TYPE_INT` and no parameters or 'this' object.
- 22-23: Creates a closure expression. Its return value type is `G_TYPE_INT` and no parameters or 'this' object.
- 24: Evaluates the expression with the label as a 'this' object.
- 25: If the evaluation successes, show the sum of "123+456". It's 579.
- 27: If it fails, show an error message.
- 25: If the evaluation successes, the sum of "123+456", which is 579, is shown.
- 27: If it fails, an error message appears.
- 28-30: Releases the expression and the label. Unsets the value.
Closure expression is flexible than other type of expression because you can specify your own callback function.
@ -292,6 +305,7 @@ Closure expression is flexible than other type of expression because you can spe
GtkExpressionWatch is a structure, not an object.
It represents a watched GtkExpression.
Two functions create GtkExpressionWatch structure.
They are `gtk_expression_bind` and `gtk_expression_watch`.
### gtk\_expression\_bind function
@ -411,7 +425,7 @@ For example, if it is zero, the slider moves to an integer point.
28 GtkExpression *expression, *params[1];
29
30 /* Builds a window with exp.ui resource */
31 build = gtk_builder_new_from_file ("exp_bind.ui");
31 build = gtk_builder_new_from_resource ("/com/github/ToshioCP/exp/exp_bind.ui");
32 win = GTK_WIDGET (gtk_builder_get_object (build, "win"));
33 label = GTK_WIDGET (gtk_builder_get_object (build, "label"));
34 // scale = GTK_WIDGET (gtk_builder_get_object (build, "scale"));
@ -448,7 +462,7 @@ The point of the program is:
- 41-42: Two expressions are defined.
One is a property expression and the other is a closure expression.
The property expression look up the "value"property of the adjustment instance.
The property expression looks up the "value" property of the adjustment instance.
The closure expression just converts the double into an integer.
- 43: `gtk_expression_bind` binds the label property of the GtkLabel instance to the integer returned by the closure expression.
It creates a GtkExpressionWatch structure.
@ -462,8 +476,8 @@ This signal is emitted when the close button is clicked.
The handler is called just before the window closes.
It is the right moment to make the GtkExpressionWatch unwatched.
- 10-14: "close-request" signal handler.
`gtk_expression_watch_unwatch (watch)` makes the watch stop watching the expression.
It releases the expression and calls `gtk_expression_watch_unref (watch)` in it.
The function `gtk_expression_watch_unwatch (watch)` makes the watch stop watching the expression.
It also releases the expression.
If you want to bind a property to an expression, `gtk_expression_bind` is the best choice.
You can do it with `gtk_expression_watch` function, but it is less suitable.
@ -619,7 +633,7 @@ These tags are usually used for GtkBuilderListItemFactory.
<property name="child">
<object class="GtkLabel">
<binding name="label">
<lookup name="name" type="string">
<lookup name="string" type="GtkStringObject">
<lookup name="item">GtkListItem</lookup>
</lookup>
</binding>
@ -629,14 +643,13 @@ These tags are usually used for GtkBuilderListItemFactory.
</interface>
~~~
GtkBuilderListItemFactory uses GtkBuilder to extends the GtkListItem with the XML data.
In the xml file above, "GtkListItem" is an instance of the GtkListItem template.
It is the 'this' object given to the expressions.
(The information is in the [GTK Development Blog](https://blog.gtk.org/2020/09/)).
GtkBuilderListItemFactory uses GtkBuilder to build the XML data.
It sets the current object of the GtkBuilder to the GtkListItem instance.
GtkBuilder calls `gtk_expression_bind` function in the binding tag analysis.
GtkBuilder calls `gtk_expression_bind` function when it finds a binding tag.
The function sets the 'this' object like this:
1. If the binding tag has object attribute, the object will be the 'this' object.
@ -760,7 +773,7 @@ String duplication is necessary.
The C source file is very simple because almost everything is done in the ui file.
### Conversion between GValues
## Conversion between GValues
If you bind different type properties, type conversion is automatically done.
Suppose a label property (string) is bound to default-width property (int).
@ -782,4 +795,43 @@ If the current width is 100, an int `100` is converted to a string `"100"`.
If you use `g_object_get` and `g_object_set` to copy properties, the value is automatically converted.
## Meson.build
The source files are in `src/expression` directory.
You can build all the files at once.
~~~
$ cd src/expression
$ meson setup _build
$ ninja -C _build
~~~
For example, if you want to run "exp", which is the executable file from "exp.c", type `_build/exp`.
You can run other programs as well.
The file `meson.build` is as follows.
~~~meson
1 project('exp', 'c')
2
3 gtkdep = dependency('gtk4')
4
5 gnome=import('gnome')
6 resources = gnome.compile_resources('resources','exp.gresource.xml')
7
8 sourcefiles=files('exp.c')
9
10 executable('exp', sourcefiles, resources, dependencies: gtkdep, export_dynamic: true, install: false)
11 executable('exp_constant', 'exp_constant.c', dependencies: gtkdep, export_dynamic: true, install: false)
12 executable('exp_constant_simple', 'exp_constant_simple.c', dependencies: gtkdep, export_dynamic: true, install: false)
13 executable('exp_property_simple', 'exp_property_simple.c', dependencies: gtkdep, export_dynamic: true, install: false)
14 executable('closure', 'closure.c', dependencies: gtkdep, export_dynamic: true, install: false)
15 executable('closure_each', 'closure_each.c', dependencies: gtkdep, export_dynamic: true, install: false)
16 executable('exp_closure_simple', 'exp_closure_simple.c', dependencies: gtkdep, export_dynamic: true, install: false)
17 executable('exp_closure_with_error_report', 'exp_closure_with_error_report.c', dependencies: gtkdep, export_dynamic: true, install: false)
18 executable('exp_bind', 'exp_bind.c', resources, dependencies: gtkdep, export_dynamic: true, install: false)
19 executable('exp_watch', 'exp_watch.c', dependencies: gtkdep, export_dynamic: true, install: false)
20 executable('exp_test', 'exp_test.c', resources, dependencies: gtkdep, export_dynamic: true, install: false)
~~~
Up: [README.md](../README.md), Prev: [Section 30](sec30.md), Next: [Section 32](sec32.md)

View file

@ -218,9 +218,9 @@ This is the title on the header of the column.
- 32: Sets the "expand" property to TRUE to allow the column to expand as much as possible.
(See the image above).
- 33- 69: Sets the "factory" property to GtkBuilderListItemFactory.
The factory has "bytes" property which holds a ui string to define a template to build GtkListItem composite widget.
The factory has "bytes" property which holds a ui string to define a template to extend GtkListItem class.
The CDATA section (line 36-66) is the ui string to put into the "bytes" property.
The contents are the same as the ui file `factory_list.ui` in the section 27.
The contents are the same as the ui file `factory_list.ui` in the section 30.
- 70-77: Sets the "sorter" property to GtkStringSorter object.
This object provides a sorter that compares strings.
It has "expression" property.
@ -456,7 +456,8 @@ All the source files are in [`src/column`](../src/column) directory.
Change your current directory to the directory and type the following.
~~~
$ meson _build
$ cd src/colomn
$ meson setup _build
$ ninja -C _build
$ _build/column
~~~

View file

@ -8,23 +8,14 @@ GtkBuilderlistItemFactory is convenient when GtkListView just shows the contents
Its binding direction is always from an item of a list to a child of GtkListItem.
When it comes to dynamic connection, it's not enough.
For example, you want to edit the contents of a list.
For example, suppose you want to edit the contents of a list.
You set a child of GtkListItem to a GtkText instance so a user can edit a text with it.
You need to bind an item in the list with the buffer of the GtkText.
The direction is opposite from the one with GtkBuilderListItemFactory.
It is from the GtkText instance to the item in the list.
You can implement this with GtkSignalListItemFactory, which is more flexible than GtkBuilderListItemFactory.
Two things are shown in this section.
- Binding from a child of a GtkListItem instance to an item of a list.
- Access a child of GtkListItem dynamically.
This direction is the same as the one with GtkBulderListItemFactory.
But GtkBulderListItemFactory uses GtkExpression from the item property of the GtkListItem.
So, it updates its child widget only when the item property changes.
In this example the child reflects the change in the same item in the list dynamically.
This section shows just a part of the source file `listeditor.c`.
This section shows just some parts of the source file `listeditor.c`.
If you want to see the whole codes, see `src/listeditor` directory of the [Gtk4 tutorial repository](https://github.com/ToshioCP/Gtk4-tutorial).
## A list editor
@ -35,7 +26,7 @@ It reads a text file line by line.
Each line is an item of the list.
The list is displayed with GtkColumnView.
There are two columns.
The one is a button, which makes the line be a current line.
The one is a button, which shows if the line is a current line.
If the line is the current line, the button is colored with red.
The other is a string which is the contents of the corresponding item of the list.
@ -49,18 +40,18 @@ You can compile end execute it as follows.
- Type the following on your commandline.
~~~
$ meson _build
$ meson setup _build
$ ninja -C _build
$ _build/listeditor
~~~
- Append button: appends a line after the current line, or at the last line if no current line exists.
- Insert button: inserts a line before the current line.
- Insert button: inserts a line before the current line, or at the top line if no current line exists.
- Remove button: removes a current line.
- Read button: reads a file.
- Write button: writes the contents to a file.
- close button: close the contents.
- quit button: quit the application.
- close button: closes the contents.
- quit button: quits the application.
- Button on the select column: makes the line current.
- String column: GtkText. You can edit a string in the field.
@ -71,7 +62,7 @@ The file name is shown at the right of the write button.
The second column (GtkColumnViewColumn) sets its factory property to GtkSignalListItemFactory.
It uses three signals setup, bind and unbind.
The following is their sgnal handlers.
The following shows the signal handlers.
~~~C
1 static void
@ -85,7 +76,7 @@ The following is their sgnal handlers.
9 bind2_cb (GtkListItemFactory *factory, GtkListItem *listitem) {
10 GtkWidget *text = gtk_list_item_get_child (listitem);
11 GtkEntryBuffer *buffer = gtk_text_get_buffer (GTK_TEXT (text));
12 LeData *data = LE_DATA (gtk_list_item_get_item (listitem));
12 LeData *data = LE_DATA (gtk_list_item_get_item(listitem));
13 GBinding *bind;
14
15 gtk_editable_set_text (GTK_EDITABLE (text), le_data_look_string (data));
@ -99,9 +90,10 @@ The following is their sgnal handlers.
23 unbind2_cb (GtkListItemFactory *factory, GtkListItem *listitem) {
24 GBinding *bind = G_BINDING (g_object_get_data (G_OBJECT (listitem), "bind"));
25
26 g_binding_unbind(bind);
27 g_object_set_data (G_OBJECT (listitem), "bind", NULL);
28 }
26 if (bind)
27 g_binding_unbind(bind);
28 g_object_set_data (G_OBJECT (listitem), "bind", NULL);
29 }
~~~
- 1-6: `setup2_cb` is a setup signal handler on the GtkSignalListItemFactory.
@ -113,17 +105,13 @@ So, teardown signal handler isn't necessary.
It is called when the `listitem` is bound to an item in the list.
The list items are LeData instances.
LeData is defined in the file `listeditor.c` (the C source file of the list editor).
It is a child class of GObject and has two data.
The one is `listitem` which points a first column GtkListItem instance when they are connected.
Be careful that the GtkListItem instance is *not* the `listitem` in this handler.
If no GtkListItem is connected, it is NULL.
The other is `string` which is a content of the line.
It is a child class of GObject and has string data which is the content of the line.
- 10-11: `text` is a child of the `listitem` and it is a GtkText instance.
And `buffer` is a GtkTextBuffer instance of the `text`.
And `buffer` is a GtkEntryBuffer instance of the `text`.
- 12: The LeData instance `data` is an item pointed by the `listitem`.
- 15-16: Sets the text of `text` to `le_data_look_string (data)`.
le\_data\_look\_string returns the string of the `data` and the ownership of the string is still taken by the `data`.
So, the caller don't need to free the string.
So, the caller doesn't need to free the string.
- 18: `g_object_bind_property` binds a property and another object property.
This line binds the "text" property of the `buffer` (source) and the "string" property of the `data` (destination).
It is a uni-directional binding (`G_BINDING_DEFAULT`).
@ -136,14 +124,19 @@ The key is a string (or GQuark) and the value is a gpointer (pointer to any type
The function `g_object_set_data` sets the association from the key to the value.
This line sets the association from "bind" to `bind` instance.
It makes possible for the "unbind" handler to get the `bind` instance.
- 22-28: `unbind2_cb` is a unbind signal handler.
- 22-29: `unbind2_cb` is a unbind signal handler.
- 24: Retrieves the `bind` instance from the table in the `listitem` instance.
- 26: Unbind the binding.
- 27: Removes the value corresponds to the "bind" key.
- 26-27: Unbind the binding.
- 28: Removes the value corresponds to the "bind" key.
This technique is not so complicated.
You can use it when you make a cell editable application.
If it is impossible to use `g_object_bind_property`, use a notify signal on the GtkEntryBuffer instance.
You can use "deleted-text" and "inserted-text" signal instead.
The handler of the signals above copies the text in the GtkEntryBuffer instance to the LeData string.
Connect the notify signal handler in `bind2_cb` and disconnect it in `unbind2_cb`.
## Change the cell of GtkColumnView dynamically
Next topic is to change the GtkColumnView (or GtkListView) cells dynamically.
@ -168,159 +161,157 @@ The current line doesn't need to be on the display.
It is possible to be on the line out of the Window (GtkScrolledWindow).
Actually, the program doesn't use GtkSingleSelection.
It is necessary to know the corresponding GtkListItem instance from the item in the list.
It is the opposite direction from `gtk_list_item_get_item` function.
To accomplish this, we set a `listitem` element of LeData to point the corresponding GtkListItem instance.
Therefore, items (LeData) in the list always know the GtkListItem.
If there's no GtkListItem bound to the item, NULL is assigned.
The LeWindow instance has two instance variables for recording the current line.
- `win->position`: An int type variable. It is the position of the current line. It is zero-based. If no current line exists, it is -1.
- `win->current_button`: A variable points the button, located at the first column, on the current line. If no current line exists, it is NULL.
If the current line moves, the following two functions are called.
They updates the two varables.
~~~C
1 static void
2 update_current_position (LeWindow *win, int new) {
3 char *s;
4
5 win->position = new;
6 if (win->position >= 0)
7 s = g_strdup_printf ("%d", win->position);
8 else
9 s = "";
10 gtk_label_set_text (GTK_LABEL (win->position_label), s);
11 if (*s) // s isn't an empty string
12 g_free (s);
13 }
14
15 static void
16 update_current_button (LeWindow *win, GtkButton *new_button) {
17 const char *non_current[1] = {NULL};
18 const char *current[2] = {"current", NULL};
19
20 if (win->current_button) {
21 gtk_widget_set_css_classes (GTK_WIDGET (win->current_button), non_current);
22 g_object_unref (win->current_button);
23 }
24 win->current_button = new_button;
25 if (win->current_button) {
26 g_object_ref (win->current_button);
27 gtk_widget_set_css_classes (GTK_WIDGET (win->current_button), current);
28 }
29 }
~~~
The varable `win->position_label` points a GtkLabel instance.
The label shows the current line position.
The current button has CSS "current" class.
The button is colored red through the CSS "button.current {background: red;}".
The order of the call for these two functions is important.
The first function, which updates the position, is usually called first.
After that, a new line is appended or inserted.
Then, the second function is called.
The following functions call the two functions above.
Be careful about the order of the call.
~~~C
1 void
2 select_cb (GtkButton *btn, GtkListItem *listitem) {
3 LeWindow *win = LE_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET (btn), LE_TYPE_WINDOW));
4
5 update_current (win, gtk_list_item_get_position (listitem));
6 }
7
8 static void
9 setup1_cb (GtkListItemFactory *factory, GtkListItem *listitem) {
10 GtkWidget *button = gtk_button_new ();
11 gtk_list_item_set_child (listitem, button);
12 gtk_widget_set_focusable (GTK_WIDGET (button), FALSE);
13 g_signal_connect (button, "clicked", G_CALLBACK (select_cb), listitem);
14 }
15
16 static void
17 bind1_cb (GtkListItemFactory *factory, GtkListItem *listitem, gpointer user_data) {
18 LeWindow *win = LE_WINDOW (user_data);
19 GtkWidget *button = gtk_list_item_get_child (listitem);
20 const char *non_current[1] = {NULL};
21 const char *current[2] = {"current", NULL};
22 LeData *data = LE_DATA (gtk_list_item_get_item (listitem));
23
24 if (data) {
25 le_data_set_listitem (data, listitem);
26 if (win->position == gtk_list_item_get_position (listitem))
27 gtk_widget_set_css_classes (GTK_WIDGET (button), current);
28 else
29 gtk_widget_set_css_classes (GTK_WIDGET (button), non_current);
30 }
31 }
32
33 static void
34 unbind1_cb (GtkListItemFactory *factory, GtkListItem *listitem) {
35 LeData *data = LE_DATA (gtk_list_item_get_item (listitem));
36 if (data)
37 le_data_set_listitem (data, NULL);
38 }
5 update_current_position (win, gtk_list_item_get_position (listitem));
6 update_current_button (win, btn);
7 }
8
9 static void
10 setup1_cb (GtkListItemFactory *factory, GtkListItem *listitem) {
11 GtkWidget *button = gtk_button_new ();
12 gtk_list_item_set_child (listitem, button);
13 gtk_widget_set_focusable (GTK_WIDGET (button), FALSE);
14 g_signal_connect (button, "clicked", G_CALLBACK (select_cb), listitem);
15 }
16
17 static void
18 bind1_cb (GtkListItemFactory *factory, GtkListItem *listitem, gpointer user_data) {
19 LeWindow *win = LE_WINDOW (user_data);
20 GtkWidget *button = gtk_list_item_get_child (listitem);
21
22 if (win->position == gtk_list_item_get_position (listitem))
23 update_current_button (win, GTK_BUTTON (button));
24 }
~~~
- 8-14: `setup1_cb` is a setup signal handler on the GtkSignalListItemFactory.
This factory is inserted to the factory property of the first GtkColumnViewColumn.
- 1-7: `select_cb` is a "clicked" signal handler.
The handler just calls the two functions and update the position and button.
- 9-15: `setup1_cb` is a setup signal handler on the GtkSignalListItemFactory.
It sets the child of `listitem` to a GtkButton instance.
The "clicked" signal on the button is connected to the handler `select_cb`.
When the listitem is destroyed, the child (GtkButton) is also destroyed.
At the same time, the connection of the signal and the handler is also destroyed.
So, you don't need teardown signal handler.
- 1-6: `select_cb` is a "clicked" signal handler.
LeWindow is defined in `listeditor.c`.
It's a child class of GtkApplicationWindow.
The handler just calls the `update_current` function.
The function will be explained later.
- 16-31: `bind1_cb` is a bind signal handler.
It sets the "listitem" element of the item (LeData) to point the `listitem` (GtkListItem instance).
It makes the item possible to find the corresponding GtkListItem instance.
If the item is the current line, the CSS class of the button includes "current" class.
Otherwise it has no CSS class.
This is necessary because the button may be recycled and it has had former CSS class.
The class need to be updated.
- 33-38: `unbind1_cb` is an unbind signal handler.
It removes the `listitem` instance from the "listitem" element of the item.
The element becomes NULL, which tells no GtkListItem is bound.
When referring GtkListItem, it needs to check the "listitem" element whether it points a GtkListItem or not (NULL).
Otherwise bad things will happen.
- 17-24: `bind1_cb` is a bind signal handler.
Usually, the position moves before this handler is called.
If the item is on the current line, the button is updated.
No unbind handler is necessary.
When a line is added, the current position is updated in advance.
~~~C
1 static void
2 update_current (LeWindow *win, int new) {
3 char *s;
4 LeData *data;
5 GtkListItem *listitem;
6 GtkButton *button;
7 const char *non_current[1] = {NULL};
8 const char *current[2] = {"current", NULL};
9
10 if (new >= 0)
11 s = g_strdup_printf ("%d", new);
12 else
13 s = "";
14 gtk_label_set_text (GTK_LABEL (win->position_label), s);
15 if (*s) // s isn't an empty string
16 g_free (s);
17
18 if (win->position >=0) {
19 data = LE_DATA (g_list_model_get_item (G_LIST_MODEL (win->liststore), win->position));
20 if ((listitem = le_data_get_listitem (data)) != NULL) {
21 button = GTK_BUTTON (gtk_list_item_get_child (listitem));
22 gtk_widget_set_css_classes (GTK_WIDGET (button), non_current);
23 g_object_unref (listitem);
2 app_cb (GtkButton *btn, LeWindow *win) {
3 LeData *data = le_data_new_with_data ("");
4
5 if (win->position >= 0) {
6 update_current_position (win, win->position + 1);
7 g_list_store_insert (win->liststore, win->position, data);
8 } else {
9 update_current_position (win, g_list_model_get_n_items (G_LIST_MODEL (win->liststore)));
10 g_list_store_append (win->liststore, data);
11 }
12 g_object_unref (data);
13 }
14
15 static void
16 ins_cb (GtkButton *btn, LeWindow *win) {
17 LeData *data = le_data_new_with_data ("");
18
19 if (win->position >= 0)
20 g_list_store_insert (win->liststore, win->position, data);
21 else {
22 update_current_position (win, 0);
23 g_list_store_insert (win->liststore, 0, data);
24 }
25 g_object_unref (data);
26 }
27 win->position = new;
28 if (win->position >=0) {
29 data = LE_DATA (g_list_model_get_item (G_LIST_MODEL (win->liststore), win->position));
30 if ((listitem = le_data_get_listitem (data)) != NULL) {
31 button = GTK_BUTTON (gtk_list_item_get_child (listitem));
32 gtk_widget_set_css_classes (GTK_WIDGET (button), current);
33 g_object_unref (listitem);
34 }
35 g_object_unref (data);
36 }
37 }
~~~
The function `update_current` does several things.
When a line is removed, the current position becomes -1 and no button is current.
- It has two parameters.
The first one is `win`, which is an instance of LeWindow class.
It has some elements.
- win->position: an Integer. it is the current position. If no current line exists, it is -1.
- win->position_label: GtkLabel. It shows the current position.
- The second parameter is `new`, which is the new current position.
At the beginning of the function, win->position points the old position.
- 10-16: Update the text of GtkLabel.
- 18-26: If the old position (win->position) is not negative, the current line exists.
It gets a GtkListItem instance via the item (LeData) of the list.
And it gets the GtkButton instance which is the child of the GtkListItem.
It clears the "css-classes" property of the button.
- 27: Updates win->position.
- 28-36: If the new position is not negative (It's possible to be negative when the current line has been removed), the current line exists.
It sets the "css-classes" property of the button to `{"current", NULL}`.
It is a NULL-terminated array of strings.
Each string is a CSS class.
Now the button has "current" style class.
~~~C
1 static void
2 rm_cb (GtkButton *btn, LeWindow *win) {
3 if (win->position >= 0) {
4 g_list_store_remove (win->liststore, win->position);
5 update_current_position (win, -1);
6 update_current_button (win, NULL);
7 }
8 }
~~~
The color of buttons are determined by the "background" CSS style.
The following CSS is applied to the default GdkDisplay in advance (in the startup handler of the application).
The following CSS node is a bit complicated.
CSS node `column view` has `listview` child node.
It covers the rows in the GtkColumnView.
The `listview` node is the same as the one for GtkListView.
It has `row` child node, which is for each child widget.
Therefore, the following node corresponds buttons on the GtkColumnView widget.
In addition, it is applied to the "current" class.
~~~css
columnview listview row button.current {background: red;}
~~~
The selectors "columnview listview row" is needed before "button" selector.
Otherwise the buttons in the GtkColumnview won't be found.
The button selector has "current" class.
So, the only "current" class button is colored with red.
Other buttons are not colored, which means they are white.
## Gtk\_widget\_dispose\_template function
The function `gtk_widget_dispose_template` clears the template children for the given widget.
This is the opposite of `gtk_widget_init_template()`.
It is a new function of GTK 4.8 version.
If your GTK version is lower than 4.8, you need to modify the program.
## A waring from GtkText
If your program has the following two, a warning message can be issued.

View file

@ -6,7 +6,8 @@ The table of contents is at the end of this abstract.
- Section 3 to 23 describes the basics, with the example of a simple editor `tfe` (Text File Editor).
- Section 24 to 27 describes GtkDrawingArea.
- Section 28 to 32 describes the list model and the list view (GtkListView, GtkGridView and GtkColumnView).
- Section 28 describes Drag and Drop.
- Section 29 to 33 describes the list model and the list view (GtkListView, GtkGridView and GtkColumnView).
It also describes GtkExpression.
The latest version of the tutorial is located at [GTK4-tutorial GitHub repository](https://github.com/ToshioCP/Gtk4-tutorial).

View file

@ -2,5 +2,7 @@
<gresources>
<gresource prefix="/com/github/ToshioCP/exp">
<file>exp.ui</file>
<file>exp_bind.ui</file>
<file>exp_test.ui</file>
</gresource>
</gresources>

View file

@ -28,7 +28,7 @@ app_startup (GApplication *application) {
GtkExpression *expression, *params[1];
/* Builds a window with exp.ui resource */
build = gtk_builder_new_from_file ("exp_bind.ui");
build = gtk_builder_new_from_resource ("/com/github/ToshioCP/exp/exp_bind.ui");
win = GTK_WIDGET (gtk_builder_get_object (build, "win"));
label = GTK_WIDGET (gtk_builder_get_object (build, "label"));
// scale = GTK_WIDGET (gtk_builder_get_object (build, "scale"));

View file

@ -14,7 +14,7 @@ main (int argc, char **argv) {
if (gtk_expression_evaluate (expression, label, &value))
g_print ("The value is %s.\n", g_value_get_string (&value));
else
g_print ("The constant expression wasn't evaluated correctly.\n");
g_print ("The property expression wasn't evaluated correctly.\n");
gtk_expression_unref (expression);
g_value_unset (&value);

View file

@ -23,7 +23,7 @@ app_startup (GApplication *application) {
GtkBuilder *build;
GtkWidget *win;
build = gtk_builder_new_from_file ("exp_test.ui");
build = gtk_builder_new_from_resource ("/com/github/ToshioCP/exp/exp_test.ui");
win = GTK_WIDGET (gtk_builder_get_object (build, "win"));
gtk_window_set_application (GTK_WINDOW (win), app);
g_object_unref (build);

View file

@ -15,6 +15,6 @@ executable('closure', 'closure.c', dependencies: gtkdep, export_dynamic: true, i
executable('closure_each', 'closure_each.c', dependencies: gtkdep, export_dynamic: true, install: false)
executable('exp_closure_simple', 'exp_closure_simple.c', dependencies: gtkdep, export_dynamic: true, install: false)
executable('exp_closure_with_error_report', 'exp_closure_with_error_report.c', dependencies: gtkdep, export_dynamic: true, install: false)
executable('exp_bind', 'exp_bind.c', dependencies: gtkdep, export_dynamic: true, install: false)
executable('exp_bind', 'exp_bind.c', resources, dependencies: gtkdep, export_dynamic: true, install: false)
executable('exp_watch', 'exp_watch.c', dependencies: gtkdep, export_dynamic: true, install: false)
executable('exp_test', 'exp_test.c', dependencies: gtkdep, export_dynamic: true, install: false)
executable('exp_test', 'exp_test.c', resources, dependencies: gtkdep, export_dynamic: true, install: false)

View file

@ -13,7 +13,6 @@ static GParamSpec *ledata_properties[N_PROPERTIES] = {NULL, };
typedef struct _LeData {
GObject parent;
GtkListItem *listitem;
char *string;
} LeData;
@ -41,19 +40,9 @@ le_data_get_property (GObject *object, guint property_id, GValue *value, GParamS
static void
le_data_init (LeData *self) {
self->listitem = NULL;
self->string = NULL;
}
static void
le_data_dispose (GObject *object) {
LeData *self = LE_DATA (object);
if (self->listitem)
g_clear_object (&self->listitem);
G_OBJECT_CLASS (le_data_parent_class)->dispose (object);
}
static void
le_data_finalize (GObject *object) {
LeData *self = LE_DATA (object);
@ -67,7 +56,6 @@ static void
le_data_class_init (LeDataClass *class) {
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
gobject_class->finalize = le_data_dispose;
gobject_class->finalize = le_data_finalize;
gobject_class->set_property = le_data_set_property;
gobject_class->get_property = le_data_get_property;
@ -75,14 +63,16 @@ le_data_class_init (LeDataClass *class) {
g_object_class_install_properties (gobject_class,N_PROPERTIES, ledata_properties);
}
/* setter and getter */
void
le_data_set_listitem (LeData *self, GtkListItem *listitem) {
g_return_if_fail (GTK_IS_LIST_ITEM (listitem) || listitem == NULL);
/* getter and setter */
if (self->listitem)
g_object_unref (self->listitem);
self->listitem = listitem ? g_object_ref (listitem) : NULL;
char *
le_data_get_string (LeData *self) {
return g_strdup (self->string);
}
const char *
le_data_look_string (LeData *self) {
return self->string;
}
void
@ -99,35 +89,9 @@ le_data_take_string (LeData *self, char *string) {
self->string = string;
}
GtkListItem *
le_data_get_listitem (LeData *self) {
return self->listitem ? g_object_ref (self->listitem) : NULL;
}
GtkListItem *
le_data_look_listitem (LeData *self) {
return self->listitem ? self->listitem : NULL;
}
char *
le_data_get_string (LeData *self) {
return g_strdup (self->string);
}
const char *
le_data_look_string (LeData *self) {
return self->string;
}
LeData *
le_data_new_with_data (GtkListItem *listitem, const char *string) {
g_return_val_if_fail (GTK_IS_LIST_ITEM (listitem) || listitem == NULL, NULL);
LeData *data;
data = LE_DATA (g_object_new (LE_TYPE_DATA, NULL));
data->listitem = listitem ? g_object_ref (listitem) : NULL;
data->string = g_strdup (string);
return data;
le_data_new_with_data (const char *string) {
return LE_DATA (g_object_new (LE_TYPE_DATA, "string", string, NULL));
}
LeData *
@ -141,151 +105,149 @@ G_DECLARE_FINAL_TYPE (LeWindow, le_window, LE, WINDOW, GtkApplicationWindow)
typedef struct _LeWindow {
GtkApplicationWindow parent;
int position; /* current position */
GtkButton *current_button;
GFile *file;
GtkWidget *position_label;
GtkWidget *filename;
GtkWidget *columnview;
GtkNoSelection *noselection;
GListStore *liststore;
} LeWindow;
G_DEFINE_TYPE (LeWindow, le_window, GTK_TYPE_APPLICATION_WINDOW)
G_DEFINE_FINAL_TYPE (LeWindow, le_window, GTK_TYPE_APPLICATION_WINDOW)
static void
update_current (LeWindow *win, int new) {
update_current_position (LeWindow *win, int new) {
char *s;
LeData *data;
GtkListItem *listitem;
GtkButton *button;
const char *non_current[1] = {NULL};
const char *current[2] = {"current", NULL};
if (new >= 0)
s = g_strdup_printf ("%d", new);
win->position = new;
if (win->position >= 0)
s = g_strdup_printf ("%d", win->position);
else
s = "";
gtk_label_set_text (GTK_LABEL (win->position_label), s);
if (*s) // s isn't an empty string
g_free (s);
}
if (win->position >=0) {
data = LE_DATA (g_list_model_get_item (G_LIST_MODEL (win->liststore), win->position));
if ((listitem = le_data_get_listitem (data)) != NULL) {
button = GTK_BUTTON (gtk_list_item_get_child (listitem));
gtk_widget_set_css_classes (GTK_WIDGET (button), non_current);
g_object_unref (listitem);
static void
update_current_button (LeWindow *win, GtkButton *new_button) {
const char *non_current[1] = {NULL};
const char *current[2] = {"current", NULL};
if (win->current_button) {
gtk_widget_set_css_classes (GTK_WIDGET (win->current_button), non_current);
g_object_unref (win->current_button);
}
g_object_unref (data);
}
win->position = new;
if (win->position >=0) {
data = LE_DATA (g_list_model_get_item (G_LIST_MODEL (win->liststore), win->position));
if ((listitem = le_data_get_listitem (data)) != NULL) {
button = GTK_BUTTON (gtk_list_item_get_child (listitem));
gtk_widget_set_css_classes (GTK_WIDGET (button), current);
g_object_unref (listitem);
}
g_object_unref (data);
win->current_button = new_button;
if (win->current_button) {
g_object_ref (win->current_button);
gtk_widget_set_css_classes (GTK_WIDGET (win->current_button), current);
}
}
/* ----- Button "clicled" signal handlers ----- */
static void
app_cb (GtkButton *btn, LeWindow *win) {
LeData *data;
LeData *data = le_data_new_with_data ("");
data = le_data_new_with_data (NULL, "");
if (win->position >= 0) {
g_list_store_insert (win->liststore, win->position + 1, data);
update_current (win, win->position + 1);
update_current_position (win, win->position + 1);
g_list_store_insert (win->liststore, win->position, data);
} else {
update_current_position (win, g_list_model_get_n_items (G_LIST_MODEL (win->liststore)));
g_list_store_append (win->liststore, data);
update_current (win, g_list_model_get_n_items (G_LIST_MODEL (win->liststore)) - 1);
}
g_object_unref (data);
}
static void
ins_cb (GtkButton *btn, LeWindow *win) {
LeData *data;
LeData *data = le_data_new_with_data ("");
data = le_data_new_with_data (NULL, "");
if (win->position >= 0) {
if (win->position >= 0)
g_list_store_insert (win->liststore, win->position, data);
win->position += 1;
update_current (win, win->position - 1);
else {
update_current_position (win, 0);
g_list_store_insert (win->liststore, 0, data);
}
g_object_unref (data);
}
static void
rm_cb (GtkButton *btn, LeWindow *win) {
int position;
if (win->position >= 0) {
position = win->position;
win->position = -1;
g_list_store_remove (win->liststore, position);
update_current (win, -1);
g_list_store_remove (win->liststore, win->position);
update_current_position (win, -1);
update_current_button (win, NULL);
}
}
static void
open_dialog_response(GtkWidget *dialog, gint response, LeWindow *win) {
open_dialog_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) {
GtkFileDialog *dialog = GTK_FILE_DIALOG (source_object);
LeWindow *win = LE_WINDOW (user_data);
GFile *file;
char *contents;
GListStore *liststore;
GListStore *liststore_old;
LeData *data;
char *s;
gsize length;
GFileInputStream *stream;
GDataInputStream *dstream;
GError *err = NULL;
LeData *data;
if (response == GTK_RESPONSE_ACCEPT
&& G_IS_FILE (file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog)))
&& g_file_load_contents (file, NULL, &contents, &length, NULL, &err)) {
if (! (stream = g_file_read (file, NULL, &err))) {
if ((file = gtk_file_dialog_open_finish (dialog, res, &err)) == NULL) {
g_warning ("%s\n", err->message);
g_error_free (err);
return;
}
if ((stream = g_file_read (file, NULL, &err)) == NULL) {
g_warning ("%s\n", err->message);
g_error_free (err);
return;
}
dstream = g_data_input_stream_new (G_INPUT_STREAM (stream));
g_object_unref (stream);
while ((contents = g_data_input_stream_read_line_utf8 (dstream, &length, NULL, &err)) != NULL) {
data = le_data_new_with_data (NULL, contents);
liststore = g_list_store_new (LE_TYPE_DATA);
while ((contents = g_data_input_stream_read_line_utf8 (dstream, NULL, NULL, &err)) != NULL) {
data = le_data_new_with_data (contents);
g_list_store_append (liststore, data);
g_free (contents);
g_list_store_append (win->liststore, data);
g_object_unref (data);
}
if (err) {
g_warning ("%s\n", err->message);
if (g_list_model_get_n_items(G_LIST_MODEL (win->liststore)) > 0)
g_list_store_remove_all (win->liststore);
return;
} else if (! g_input_stream_close (G_INPUT_STREAM (dstream), NULL, &err)) { /* EOF */
g_warning ("%s\n", err->message);
g_error_free (err);
g_object_unref (liststore);
return;
}
win->file = file; /* win->file is NULL (has already checked) and it take the ownership of 'file' */
if (! g_input_stream_close (G_INPUT_STREAM (dstream), NULL, &err)) {
g_warning ("%s\n", err->message);
g_error_free (err);
g_object_unref (liststore);
return;
}
/* Now the file has successfully read. */
if (win->file)
g_object_unref (file);
win->file = file; /* The ownership of the GFile moves to the window. */
s = g_file_get_basename (file);
gtk_label_set_text (GTK_LABEL (win->filename), s);
g_free (s);
update_current (win, -1);
}
gtk_window_destroy (GTK_WINDOW (dialog));
liststore_old = win->liststore;
gtk_no_selection_set_model (win->noselection, G_LIST_MODEL (liststore));
win->liststore = liststore; /* The ownership of the stringlist moves to the window. */
g_object_unref (liststore_old);
update_current_position (win, -1);
}
static void
read_cb (GtkButton *btn, LeWindow *win) {
GtkWidget *dialog;
GtkFileDialog *dialog;
if (win->file || g_list_model_get_n_items (G_LIST_MODEL (win->liststore)) > 0)
return;
dialog = gtk_file_chooser_dialog_new ("Open file", GTK_WINDOW (win), GTK_FILE_CHOOSER_ACTION_OPEN,
"Cancel", GTK_RESPONSE_CANCEL,
"Open", GTK_RESPONSE_ACCEPT,
NULL);
g_signal_connect (dialog, "response", G_CALLBACK (open_dialog_response), win);
gtk_window_present (GTK_WINDOW (dialog));
dialog = gtk_file_dialog_new ();
gtk_file_dialog_open (dialog, GTK_WINDOW (win), NULL, open_dialog_cb, win);
g_object_unref (dialog);
}
static void
@ -293,8 +255,9 @@ write_data (LeWindow *win) {
GFileOutputStream *ostream;
gssize size;
gsize length;
LeData *data;
int i, n_items;
LeData *data;
const char *s;
GError *err = NULL;
if (! (ostream = g_file_replace (win->file, NULL, TRUE, G_FILE_CREATE_NONE, NULL, &err))) {
@ -305,18 +268,19 @@ write_data (LeWindow *win) {
n_items = g_list_model_get_n_items (G_LIST_MODEL (win->liststore));
for (i=0; i<n_items; ++i) {
data = LE_DATA (g_list_model_get_item (G_LIST_MODEL (win->liststore), i));
length = (gsize) strlen (le_data_look_string (data));
size = g_output_stream_write (G_OUTPUT_STREAM (ostream), le_data_look_string (data), length, NULL, &err);
s = le_data_look_string (data);
length = (gsize) strlen (s);
size = g_output_stream_write (G_OUTPUT_STREAM (ostream), s, length, NULL, &err);
g_object_unref (data);
if (size < 0) {
g_warning ("%s\n", err->message);
g_error_free (err);
g_clear_error (&err);
break;
}
size = g_output_stream_write (G_OUTPUT_STREAM (ostream), "\n", 1, NULL, &err);
if (size < 0) {
g_warning ("%s\n", err->message);
g_error_free (err);
g_clear_error (&err);
break;
}
}
@ -327,36 +291,34 @@ write_data (LeWindow *win) {
}
static void
saveas_dialog_response (GtkWidget *dialog, gint response, LeWindow *win) {
saveas_dialog_cb (GObject* source_object, GAsyncResult* res, gpointer user_data) {
GtkFileDialog *dialog = GTK_FILE_DIALOG (source_object);
LeWindow *win = LE_WINDOW (user_data);
GFile *file;
GError *err = NULL;
char *s;
if (response == GTK_RESPONSE_ACCEPT) {
file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
if (G_IS_FILE (file)) {
if ((file = gtk_file_dialog_save_finish (dialog, res, &err)) == NULL) {
g_warning ("%s\n", err->message);
g_error_free (err);
return;
}
if (win->file)
g_object_unref (win->file);
win->file = file; /* the ownership is taken by win */
s = g_file_get_basename (file);
gtk_label_set_text (GTK_LABEL (win->filename), s);
g_free (s);
write_data (win);
}
else {
g_warning ("gtk_file_chooser_get_file returns non GFile.\n");
}
}
gtk_window_destroy (GTK_WINDOW (dialog));
}
static void
show_saveas_dialog (LeWindow *win) {
GtkWidget *dialog;
GtkFileDialog *dialog;
dialog = gtk_file_chooser_dialog_new ("Save file", GTK_WINDOW (win), GTK_FILE_CHOOSER_ACTION_SAVE,
"Cancel", GTK_RESPONSE_CANCEL,
"Save", GTK_RESPONSE_ACCEPT,
NULL);
g_signal_connect (dialog, "response", G_CALLBACK (saveas_dialog_response), win);
gtk_widget_show (dialog);
dialog = gtk_file_dialog_new ();
gtk_file_dialog_save (dialog, GTK_WINDOW (win), NULL, saveas_dialog_cb, win);
g_object_unref (dialog);
}
static void
@ -371,14 +333,15 @@ write_cb (GtkButton *btn, LeWindow *win) {
static void
close_cb (GtkButton *btn, LeWindow *win) {
if (g_list_model_get_n_items (G_LIST_MODEL (win->liststore)) > 0)
guint n_items;
if ((n_items = g_list_model_get_n_items (G_LIST_MODEL (win->liststore))) > 0)
g_list_store_remove_all (win->liststore);
if (win->file) {
g_clear_object (&win->file);
gtk_label_set_text (GTK_LABEL (win->filename), "");
}
win->position = -1;
gtk_label_set_text (GTK_LABEL (win->position_label), "");
update_current_position (win, -1);
}
static void
@ -391,7 +354,8 @@ void
select_cb (GtkButton *btn, GtkListItem *listitem) {
LeWindow *win = LE_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET (btn), LE_TYPE_WINDOW));
update_current (win, gtk_list_item_get_position (listitem));
update_current_position (win, gtk_list_item_get_position (listitem));
update_current_button (win, btn);
}
/* ----- Handlers on GtkSignalListItemFacory ----- */
@ -407,24 +371,9 @@ static void
bind1_cb (GtkListItemFactory *factory, GtkListItem *listitem, gpointer user_data) {
LeWindow *win = LE_WINDOW (user_data);
GtkWidget *button = gtk_list_item_get_child (listitem);
const char *non_current[1] = {NULL};
const char *current[2] = {"current", NULL};
LeData *data = LE_DATA (gtk_list_item_get_item (listitem));
if (data) {
le_data_set_listitem (data, listitem);
if (win->position == gtk_list_item_get_position (listitem))
gtk_widget_set_css_classes (GTK_WIDGET (button), current);
else
gtk_widget_set_css_classes (GTK_WIDGET (button), non_current);
}
}
static void
unbind1_cb (GtkListItemFactory *factory, GtkListItem *listitem) {
LeData *data = LE_DATA (gtk_list_item_get_item (listitem));
if (data)
le_data_set_listitem (data, NULL);
update_current_button (win, GTK_BUTTON (button));
}
static void
@ -438,7 +387,7 @@ static void
bind2_cb (GtkListItemFactory *factory, GtkListItem *listitem) {
GtkWidget *text = gtk_list_item_get_child (listitem);
GtkEntryBuffer *buffer = gtk_text_get_buffer (GTK_TEXT (text));
LeData *data = LE_DATA (gtk_list_item_get_item (listitem));
LeData *data = LE_DATA (gtk_list_item_get_item(listitem));
GBinding *bind;
gtk_editable_set_text (GTK_EDITABLE (text), le_data_look_string (data));
@ -452,6 +401,7 @@ static void
unbind2_cb (GtkListItemFactory *factory, GtkListItem *listitem) {
GBinding *bind = G_BINDING (g_object_get_data (G_OBJECT (listitem), "bind"));
if (bind)
g_binding_unbind(bind);
g_object_set_data (G_OBJECT (listitem), "bind", NULL);
}
@ -463,24 +413,29 @@ adjustment_value_changed_cb (GtkAdjustment *adjustment, gpointer user_data) {
gtk_window_set_focus (GTK_WINDOW (win), NULL);
}
static void
le_window_init (LeWindow *win) {
gtk_widget_init_template (GTK_WIDGET (win));
win->position = -1;
win->file =NULL;
}
static void
le_window_dispose (GObject *object) {
LeWindow *win = LE_WINDOW (object);
if (win->current_button)
g_clear_object (&win->current_button);
if (win->file)
g_clear_object (&win->file);
/* this function is available since GTK 4.8 */
gtk_widget_dispose_template (GTK_WIDGET (win), LE_TYPE_WINDOW);
/* chain to the parent */
G_OBJECT_CLASS (le_window_parent_class)->dispose (object);
}
static void
le_window_init (LeWindow *win) {
gtk_widget_init_template (GTK_WIDGET (win));
win->position = -1;
win->current_button = NULL;
win->file =NULL;
}
static void
le_window_class_init (LeWindowClass *class) {
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
@ -491,6 +446,7 @@ le_window_class_init (LeWindowClass *class) {
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), LeWindow, position_label);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), LeWindow, filename);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), LeWindow, columnview);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), LeWindow, noselection);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), LeWindow, liststore);
/* The followint macros are available since GTK 4.8 */
gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), app_cb);
@ -502,7 +458,6 @@ le_window_class_init (LeWindowClass *class) {
gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), quit_cb);
gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), setup1_cb);
gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), bind1_cb);
gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), unbind1_cb);
gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), setup2_cb);
gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), bind2_cb);
gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), unbind2_cb);

View file

@ -2,8 +2,8 @@
<interface>
<template class="LeWindow" parent="GtkApplicationWindow">
<property name="title">list editor</property>
<property name="default-width">600</property>
<property name="default-height">400</property>
<property name="default-width">1000</property>
<property name="default-height">600</property>
<child>
<object class="GtkBox">
<property name="orientation">GTK_ORIENTATION_VERTICAL</property>
@ -95,7 +95,7 @@
<property name="vexpand">TRUE</property>
<property name="focusable">FALSE</property>
<property name="model">
<object class="GtkNoSelection">
<object class="GtkNoSelection" id = "noselection">
<property name="model">
<object class="GListStore" id="liststore">
<property name="item-type">LeData</property>
@ -110,7 +110,6 @@
<object class="GtkSignalListItemFactory">
<signal name="setup" handler="setup1_cb"></signal>
<signal name="bind" handler="bind1_cb" swapped="no" object="LeWindow"></signal>
<signal name="unbind" handler="unbind1_cb"></signal>
</object>
</property>
</object>
@ -118,6 +117,7 @@
<child>
<object class="GtkColumnViewColumn">
<property name="title">String</property>
<property name="expand">TRUE</property>
<property name="factory">
<object class="GtkSignalListItemFactory">
<signal name="setup" handler="setup2_cb"></signal>

View file

@ -6,9 +6,10 @@ The new feature is described in [Gtk API Reference -- List Widget Overview](http
GTK 4 has other means to implement lists.
They are GtkListBox and GtkTreeView which are took over from GTK 3.
There's an article in [Gtk Development blog](https://blog.gtk.org/2020/06/07/scalable-lists-in-gtk-4/) about list widgets by Matthias Clasen.
He described why GtkListView are developed to replace GtkListBox and GtkTreeView.
He described why GtkListView are developed to replace GtkTreeView.
GtkTreeView is deprecated since version 4.10.
GtkListView, GtkGridView, GtkColumnView and related objects are described in Section 26 to 29.
GtkListView, GtkGridView, GtkColumnView and related objects are described in Section 29 to 33.
## Outline
@ -19,7 +20,7 @@ A list is like an array, but in many cases it is implemented with pointers which
And it has a start point.
So, each item can be referred by the index of the item (first item, second item, ..., nth item, ...).
There are two cases.
One is the index starts from one (one-based) and the other is from zero (zero-based).
The one is the index starts from one (one-based) and the other is from zero (zero-based).
Gio provides GListModel interface.
It is a zero-based list and its items are the same type of GObject descendants, or objects that implement the same interface.
@ -27,11 +28,11 @@ An object implements GListModel is not a widget.
So, the list is not displayed on the screen directly.
There's another object GtkListView which is a widget to display the list.
The items in the list need to be connected to the items in GtkListView.
GtkListItemFactory instance maps items in the list to GListView.
GtkListItemFactory instance maps items in the list to GtkListView.
![List](../image/list.png){width=10cm height=7.5cm}
## GListModel
## GListModel and GtkStringList
If you want to make a list of strings with GListModel, for example, "one", "two", "three", "four", note that strings can't be items of the list.
Because GListModel is a list of GObject objects and strings aren't GObject objects.
@ -172,6 +173,7 @@ If you feel some difficulty, read this section again, especially GtkSignalListIt
GtkBuilderListItemFactory is another GtkListItemFactory.
Its behavior is defined with ui file.
@@@if gfm
~~~xml
<interface>
<template class="GtkListItem">
@ -187,6 +189,23 @@ Its behavior is defined with ui file.
</template>
</interface>
~~~
@@@else
~~~{.xml}
<interface>
<template class="GtkListItem">
<property name="child">
<object class="GtkLabel">
<binding name="label">
<lookup name="string" type="GtkStringObject">
<lookup name="item">GtkListItem</lookup>
</lookup>
</binding>
</object>
</property>
</template>
</interface>
~~~
@@@end
Template tag is used to define GtkListItem.
And its child property is GtkLabel object.
@ -209,7 +228,7 @@ There is an explanation in the [GTK Development Blog](https://blog.gtk.org/2020/
> Remember that the classname (GtkListItem) in a ui template is used as the “this” pointer referring to the object that is being instantiated.
Therefore, GtkListItem instance is used as the `this` object of the lookup tag when it is evaluated.
`this` object will be explained in [section 30](sec30).
`this` object will be explained in [section 31](sec31).
The C source code is as follows.
Its name is `list2.c` and located under [src/misc](misc) directory.
@ -229,9 +248,15 @@ GtkDirectoryList is a list model containing GFileInfo objects which are informat
It uses `g_file_enumerate_children_async()` to get the GFileInfo objects.
The list model is created by `gtk_directory_list_new` function.
@@@if gfm
~~~C
GtkDirectoryList *gtk_directory_list_new (const char *attributes, GFile *file);
~~~
@@@else
~~~{.C}
GtkDirectoryList *gtk_directory_list_new (const char *attributes, GFile *file);
~~~
@@@end
`attributes` is a comma separated list of file attributes.
File attributes are key-value pairs.
@ -252,11 +277,19 @@ The following table shows some example.
The current directory is ".".
The following program makes GtkDirectoryList `dl` and its contents are GFileInfo objects under the current directory.
@@@if gfm
~~~C
GFile *file = g_file_new_for_path (".");
GtkDirectoryList *dl = gtk_directory_list_new ("standard::name", file);
g_object_unref (file);
~~~
@@@else
~~~{.C}
GFile *file = g_file_new_for_path (".");
GtkDirectoryList *dl = gtk_directory_list_new ("standard::name", file);
g_object_unref (file);
~~~
@@@end
It is not so difficult to make file listing program by changing `list2.c` in the previous subsection.
One problem is that GInfoFile doesn't have properties.
@ -264,8 +297,9 @@ Lookup tag look for a property, so it is useless for looking for a filename from
Instead, closure tag is appropriate in this case.
Closure tag specifies a function and the type of the return value of the function.
@@@if gfm
~~~C
const char *
char *
get_file_name (GtkListItem *item, GFileInfo *info) {
return G_IS_FILE_INFO (info) ? g_strdup (g_file_info_get_name (info)) : NULL;
}
@ -286,6 +320,30 @@ get_file_name (GtkListItem *item, GFileInfo *info) {
"</template>"
"</interface>"
~~~
@@@else
~~~{.C}
char *
get_file_name (GtkListItem *item, GFileInfo *info) {
return G_IS_FILE_INFO (info) ? g_strdup (g_file_info_get_name (info)) : NULL;
}
... ...
... ...
"<interface>"
"<template class=\"GtkListItem\">"
"<property name=\"child\">"
"<object class=\"GtkLabel\">"
"<binding name=\"label\">"
"<closure type=\"gchararray\" function=\"get_file_name\">"
"<lookup name=\"item\">GtkListItem</lookup>"
"</closure>"
"</binding>"
"</object>"
"</property>"
"</template>"
"</interface>"
~~~
@@@end
- The string "gchararray" is a type name.
The type "gchar" is a type name and it is the same as C type "char".
@ -301,7 +359,7 @@ The contents of closure tag (it is between \<closure...\> and\</closure\>) is pa
`<lookup name="item">GtkListItem</lookup>` gives the value of the item property of the GtkListItem.
This will be the second argument of the function.
The first parameter is always the GListItem instance, which is a 'this' object.
The 'this' object is explained in section 28.
The 'this' object is explained in section 31.
- `gtk_file_name` function is the callback function for the closure tag.
It first checks the `info` parameter.
Because it can be NULL when GListItem `item` is unbounded.
@ -341,9 +399,15 @@ $ gcc -Wl,--export-dynamic `pkg-config --cflags gtk4` list3.c `pkg-config --libs
You can also make a shell script to compile `list3.c`
@@@if gfm
~~~bash
gcc -Wl,--export-dynamic `pkg-config --cflags gtk4` $1.c `pkg-config --libs gtk4`
~~~
@@@else
~~~{.bash}
gcc -Wl,--export-dynamic `pkg-config --cflags gtk4` $1.c `pkg-config --libs gtk4`
~~~
@@@end
Save this one liner to a file `comp`.
Then, copy it to `$HOME/bin` and give it executable permission.
@ -361,4 +425,3 @@ $ ./a.out
~~~
![screenshot list3](../image/list3.png){width=10cm height=7.3cm}

View file

@ -32,6 +32,7 @@ GtkGridView (model property) => GtkSingleSelection (model property) => GtkDirect
The following is a part of the ui file `list4.ui`.
@@@if gfm
~~~xml
<object class="GtkListView" id="list">
<property name="model">
@ -48,6 +49,24 @@ The following is a part of the ui file `list4.ui`.
<property name="model">singleselection</property>
</object>
~~~
@@@else
~~~{.xml}
<object class="GtkListView" id="list">
<property name="model">
<object class="GtkSingleSelection" id="singleselection">
<property name="model">
<object class="GtkDirectoryList" id="directorylist">
<property name="attributes">standard::name,standard::icon,standard::content-type</property>
</object>
</property>
</object>
</property>
</object>
<object class="GtkGridView" id="grid">
<property name="model">singleselection</property>
</object>
~~~
@@@end
GtkDirectoryList has an "attributes" property.
It is attributes of GFileInfo such as "standard::name", "standard::icon" and "standard::content-type".
@ -62,7 +81,7 @@ Such "x-" subtype is not a standard mime type.)
Content type is also used by desktop systems.
GtkGridView uses the same GtkSingleSelection instance (`singleselection`).
So, its model property is set with it.
So, its model property is set to it.
## Ui file of the window
@ -112,11 +131,19 @@ Its child widget will be set with GtkListView or GtkGridView.
The action `view` is created, connected to the "activate" signal handler and inserted to the window (action map) as follows.
@@@if gfm
~~~C
act_view = g_simple_action_new_stateful ("view", g_variant_type_new("s"), g_variant_new_string ("list"));
g_signal_connect (act_view, "activate", G_CALLBACK (view_activated), NULL);
g_action_map_add_action (G_ACTION_MAP (win), G_ACTION (act_view));
~~~
@@@else
~~~{.C}
act_view = g_simple_action_new_stateful ("view", g_variant_type_new("s"), g_variant_new_string ("list"));
g_signal_connect (act_view, "activate", G_CALLBACK (view_activated), NULL);
g_action_map_add_action (G_ACTION_MAP (win), G_ACTION (act_view));
~~~
@@@end
The signal handler `view_activated` will be explained later.
@ -198,6 +225,7 @@ You can do anything you like by connecting the "activate" signal to the handler.
The example `list4` launches `tfe` text file editor if the item of the list is a text file.
@@@if gfm
~~~C
static void
list_activate (GtkListView *list, int position, gpointer user_data) {
@ -217,6 +245,27 @@ grid_activate (GtkGridView *grid, int position, gpointer user_data) {
g_signal_connect (GTK_LIST_VIEW (list), "activate", G_CALLBACK (list_activate), NULL);
g_signal_connect (GTK_GRID_VIEW (grid), "activate", G_CALLBACK (grid_activate), NULL);
~~~
@@@else
~~~{.C}
static void
list_activate (GtkListView *list, int position, gpointer user_data) {
GFileInfo *info = G_FILE_INFO (g_list_model_get_item (G_LIST_MODEL (gtk_list_view_get_model (list)), position));
launch_tfe_with_file (info);
}
static void
grid_activate (GtkGridView *grid, int position, gpointer user_data) {
GFileInfo *info = G_FILE_INFO (g_list_model_get_item (G_LIST_MODEL (gtk_grid_view_get_model (grid)), position));
launch_tfe_with_file (info);
}
... ...
... ...
g_signal_connect (GTK_LIST_VIEW (list), "activate", G_CALLBACK (list_activate), NULL);
g_signal_connect (GTK_GRID_VIEW (grid), "activate", G_CALLBACK (grid_activate), NULL);
~~~
@@@end
The second parameter of each handler is the position of the item (GFileInfo) of the GListModel.
So you can get the item with `g_list_model_get_item` function.
@ -254,7 +303,7 @@ The last parameter is the pointer to the pointer to a GError.
If your distribution supports GTK 4, using `g_app_info_launch_default_for_uri` is convenient.
The function automatically determines the default application from the file and launches it.
For example, if the file is text, then it launches gedit with the file.
For example, if the file is text, then it launches GNOME text editor with the file.
Such feature comes from desktop.
## Compilation and execution
@ -264,7 +313,7 @@ To compile and execute list4, type as follows.
~~~
$ cd list4 # or cd src/list4. It depends your current directory.
$ meson _build
$ meson setup _build
$ ninja -C _build
$ _build/list4
~~~

View file

@ -56,8 +56,8 @@ The type of the value is int.
|G\_TYPE\_INT |int |gint | |
|G\_TYPE\_FLOAT |float |gfloat | |
|G\_TYPE\_DOUBLE |double|gdouble | |
|G\_TYPE\_POINTER | |gpointer | |
|G\_TYPE\_STRING | |gchararray|null-terminated Cstring|
|G\_TYPE\_POINTER |void *|gpointer |general pointer |
|G\_TYPE\_STRING |char *|gchararray|null-terminated Cstring|
|G\_TYPE\_OBJECT | |GObject | |
|GTK\_TYPE\_WINDOW| |GtkWindow | |
@ -79,9 +79,15 @@ Constant expression is usually used to give a constant value or instance to anot
A property expression (GtkPropertyExpression) looks up a property in a GObject instance.
For example, a property expression that refers "label" property in a GtkLabel object is created like this.
@@@if gfm
~~~C
expression = gtk_property_expression_new (GTK_TYPE_LABEL, another_expression, "label");
~~~
@@@else
~~~{.C}
expression = gtk_property_expression_new (GTK_TYPE_LABEL, another_expression, "label");
~~~
@@@end
The second parameter `another_expression` is one of:
@ -91,15 +97,23 @@ The instance is called `this` object.
For example,
@@@if gfm
~~~C
label = gtk_label_new ("Hello");
another_expression = gtk_constant_expression_new (GTK_TYPE_LABEL, label);
expression = gtk_property_expression_new (GTK_TYPE_LABEL, another_expression, "label");
~~~
@@@else
~~~{.C}
label = gtk_label_new ("Hello");
another_expression = gtk_constant_expression_new (GTK_TYPE_LABEL, label);
expression = gtk_property_expression_new (GTK_TYPE_LABEL, another_expression, "label");
~~~
@@@end
If `expression` is evaluated, the second parameter `another_expression` is evaluated in advance.
The value of `another_expression` is the `label` (GtkLabel instance).
Then, `expression` looks up "label" property of the label and the evaluation results "Hello".
Then, `expression` looks up "label" property of the label and the evaluation results in "Hello".
In the example above, the second argument of `gtk_property_expression_new` is another expression.
But the second argument can be NULL.
@ -123,7 +137,7 @@ The expression just knows how to take the property from a future-given GtkLabel
The result is stored in the GValue `value`.
The function `g_value_get_string` gets a string from the GValue.
But the string is owned by the GValue so you must not free the string.
- 18-19: Release the expression and unset the GValue.
- 18-19: Releases the expression and unset the GValue.
At the same time the string in the GValue is freed.
If the second argument of `gtk_property_expression_new` isn't NULL, it is another expression.
@ -144,6 +158,7 @@ When you program in C language, GtkCClosureExpression and GCClosure are appropri
A closure expression is created with `gtk_cclosure_expression_new` function.
@@@if gfm
~~~C
GtkExpression *
gtk_cclosure_expression_new (GType value_type,
@ -154,6 +169,18 @@ gtk_cclosure_expression_new (GType value_type,
gpointer user_data,
GClosureNotify user_destroy);
~~~
@else
~~~{.C}
GtkExpression *
gtk_cclosure_expression_new (GType value_type,
GClosureMarshal marshal,
guint n_params,
GtkExpression **params,
GCallback callback_func,
gpointer user_data,
GClosureNotify user_destroy);
~~~
@end
- `value_type` is the type of the value when it is evaluated.
- `marshal` is a marshaller.
@ -183,10 +210,17 @@ callback (this, param1, param2, ...)
For example,
@@@if gfm
~~~C
int
callback (GObject *object, int x, const char *s)
~~~
@@@else
~~~{.C}
int
callback (GObject *object, int x, const char *s)
~~~
@@@end
The following is `exp_closure_simple.c` in `src/expression`.
@ -203,13 +237,13 @@ If you want to return error report, change the return value type to be a pointer
One for error and the other for the sum.
The first argument of `gtk_cclosure_expression_new` is `G_TYPE_POINTER`.
There is a sample program `exp_closure_with_error_report` in `src/expression` directory.
- 19: gtk\_init initializes GTK. It is necessary for GtkLabel.
- 19: The function `gtk_init`` initializes GTK. It is necessary for GtkLabel.
- 20: A GtkLabel instance is created with "123+456".
- 21: The instance has floating reference. It is changed to an ordinary reference count.
- 22-23: Create a closure expression. Its return value type is `G_TYPE_INT` and no parameters or 'this' object.
- 22-23: Creates a closure expression. Its return value type is `G_TYPE_INT` and no parameters or 'this' object.
- 24: Evaluates the expression with the label as a 'this' object.
- 25: If the evaluation successes, show the sum of "123+456". It's 579.
- 27: If it fails, show an error message.
- 25: If the evaluation successes, the sum of "123+456", which is 579, is shown.
- 27: If it fails, an error message appears.
- 28-30: Releases the expression and the label. Unsets the value.
Closure expression is flexible than other type of expression because you can specify your own callback function.
@ -219,12 +253,14 @@ Closure expression is flexible than other type of expression because you can spe
GtkExpressionWatch is a structure, not an object.
It represents a watched GtkExpression.
Two functions create GtkExpressionWatch structure.
They are `gtk_expression_bind` and `gtk_expression_watch`.
### gtk\_expression\_bind function
This function binds the target object's property to the expression.
If the value of the expression changes, the property reflects the value immediately.
@@@if gfm
~~~C
GtkExpressionWatch*
gtk_expression_bind (
@ -234,6 +270,17 @@ gtk_expression_bind (
GObject* this_
)
~~~
@@@else
~~~{.C}
GtkExpressionWatch*
gtk_expression_bind (
GtkExpression* self,
GObject* target,
const char* property,
GObject* this_
)
~~~
@@@end
This function takes the ownership of the expression.
So, if you want to own the expression, call `gtk_expression_ref ()` to increase the reference count of the expression.
@ -282,7 +329,7 @@ The point of the program is:
- 41-42: Two expressions are defined.
One is a property expression and the other is a closure expression.
The property expression look up the "value"property of the adjustment instance.
The property expression looks up the "value" property of the adjustment instance.
The closure expression just converts the double into an integer.
- 43: `gtk_expression_bind` binds the label property of the GtkLabel instance to the integer returned by the closure expression.
It creates a GtkExpressionWatch structure.
@ -296,14 +343,15 @@ This signal is emitted when the close button is clicked.
The handler is called just before the window closes.
It is the right moment to make the GtkExpressionWatch unwatched.
- 10-14: "close-request" signal handler.
`gtk_expression_watch_unwatch (watch)` makes the watch stop watching the expression.
It releases the expression and calls `gtk_expression_watch_unref (watch)` in it.
The function `gtk_expression_watch_unwatch (watch)` makes the watch stop watching the expression.
It also releases the expression.
If you want to bind a property to an expression, `gtk_expression_bind` is the best choice.
You can do it with `gtk_expression_watch` function, but it is less suitable.
### gtk\_expression\_watch function
@@@if gfm
~~~C
GtkExpressionWatch*
gtk_expression_watch (
@ -314,6 +362,18 @@ gtk_expression_watch (
GDestroyNotify user_destroy
)
~~~
@@@else
~~~{.C}
GtkExpressionWatch*
gtk_expression_watch (
GtkExpression* self,
GObject* this_,
GtkExpressionNotify notify,
gpointer user_data,
GDestroyNotify user_destroy
)
~~~
@@@end
The function doesn't take the ownership of the expression.
It differs from `gtk_expression_bind`.
@ -326,12 +386,21 @@ Put NULL if you don't need them.
Notify callback has the following format.
@@@if gfm
~~~C
void
notify (
gpointer user_data
)
~~~
@@@else
~~~{.C}
void
notify (
gpointer user_data
)
~~~
@@@end
This function is used to do something when the value of the expression changes.
But if you want to bind a property to the value, use `gtk_expression_bind` instead.
@ -381,6 +450,7 @@ It is put in the content of an object tag.
Name attribute specifies the property name of the object.
The content is an expression.
@@@if gfm
~~~xml
<constant type="gchararray">Hello world</constant>
<lookup name="label" type="GtkLabel">label</lookup>
@ -389,16 +459,27 @@ The content is an expression.
<lookup name="default-width">win</lookup>
</bind>
~~~
@@@else
~~~{.xml}
<constant type="gchararray">Hello world</constant>
<lookup name="label" type="GtkLabel">label</lookup>
<closure type="gint" function="callback_function"></closure>
<bind name="label">
<lookup name="default-width">win</lookup>
</bind>
~~~
@@@end
These tags are usually used for GtkBuilderListItemFactory.
@@@if gfm
~~~xml
<interface>
<template class="GtkListItem">
<property name="child">
<object class="GtkLabel">
<binding name="label">
<lookup name="name" type="string">
<lookup name="string" type="GtkStringObject">
<lookup name="item">GtkListItem</lookup>
</lookup>
</binding>
@ -407,15 +488,31 @@ These tags are usually used for GtkBuilderListItemFactory.
</template>
</interface>
~~~
@@@else
~~~{.xml}
<interface>
<template class="GtkListItem">
<property name="child">
<object class="GtkLabel">
<binding name="label">
<lookup name="string" type="GtkStringObject">
<lookup name="item">GtkListItem</lookup>
</lookup>
</binding>
</object>
</property>
</template>
</interface>
~~~
@@@end
GtkBuilderListItemFactory uses GtkBuilder to extends the GtkListItem with the XML data.
In the xml file above, "GtkListItem" is an instance of the GtkListItem template.
It is the 'this' object given to the expressions.
(The information is in the [GTK Development Blog](https://blog.gtk.org/2020/09/)).
GtkBuilderListItemFactory uses GtkBuilder to build the XML data.
It sets the current object of the GtkBuilder to the GtkListItem instance.
GtkBuilder calls `gtk_expression_bind` function in the binding tag analysis.
GtkBuilder calls `gtk_expression_bind` function when it finds a binding tag.
The function sets the 'this' object like this:
1. If the binding tag has object attribute, the object will be the 'this' object.
@ -466,11 +563,12 @@ String duplication is necessary.
The C source file is very simple because almost everything is done in the ui file.
### Conversion between GValues
## Conversion between GValues
If you bind different type properties, type conversion is automatically done.
Suppose a label property (string) is bound to default-width property (int).
@@@if gfm
~~~xml
<object class="GtkLabel">
<binding name="label">
@ -480,6 +578,17 @@ Suppose a label property (string) is bound to default-width property (int).
</binding>
</object>
~~~
@@@else
~~~{.xml}
<object class="GtkLabel">
<binding name="label">
<lookup name="default-width">
win
</lookup>
</binding>
</object>
~~~
@@@end
The expression created by the lookup tag returns a int type GValue.
On the other hand "label" property holds a string type GValue.
@ -487,3 +596,23 @@ When a GValue is copied to another GValue, the type is automatically converted i
If the current width is 100, an int `100` is converted to a string `"100"`.
If you use `g_object_get` and `g_object_set` to copy properties, the value is automatically converted.
## Meson.build
The source files are in `src/expression` directory.
You can build all the files at once.
~~~
$ cd src/expression
$ meson setup _build
$ ninja -C _build
~~~
For example, if you want to run "exp", which is the executable file from "exp.c", type `_build/exp`.
You can run other programs as well.
The file `meson.build` is as follows.
@@@include
expression/meson.build
@@@

View file

@ -60,9 +60,9 @@ This is the title on the header of the column.
- 32: Sets the "expand" property to TRUE to allow the column to expand as much as possible.
(See the image above).
- 33- 69: Sets the "factory" property to GtkBuilderListItemFactory.
The factory has "bytes" property which holds a ui string to define a template to build GtkListItem composite widget.
The factory has "bytes" property which holds a ui string to define a template to extend GtkListItem class.
The CDATA section (line 36-66) is the ui string to put into the "bytes" property.
The contents are the same as the ui file `factory_list.ui` in the section 27.
The contents are the same as the ui file `factory_list.ui` in the section 30.
- 70-77: Sets the "sorter" property to GtkStringSorter object.
This object provides a sorter that compares strings.
It has "expression" property.
@ -196,7 +196,8 @@ All the source files are in [`src/column`](column) directory.
Change your current directory to the directory and type the following.
~~~
$ meson _build
$ cd src/colomn
$ meson setup _build
$ ninja -C _build
$ _build/column
~~~

View file

@ -6,23 +6,14 @@ GtkBuilderlistItemFactory is convenient when GtkListView just shows the contents
Its binding direction is always from an item of a list to a child of GtkListItem.
When it comes to dynamic connection, it's not enough.
For example, you want to edit the contents of a list.
For example, suppose you want to edit the contents of a list.
You set a child of GtkListItem to a GtkText instance so a user can edit a text with it.
You need to bind an item in the list with the buffer of the GtkText.
The direction is opposite from the one with GtkBuilderListItemFactory.
It is from the GtkText instance to the item in the list.
You can implement this with GtkSignalListItemFactory, which is more flexible than GtkBuilderListItemFactory.
Two things are shown in this section.
- Binding from a child of a GtkListItem instance to an item of a list.
- Access a child of GtkListItem dynamically.
This direction is the same as the one with GtkBulderListItemFactory.
But GtkBulderListItemFactory uses GtkExpression from the item property of the GtkListItem.
So, it updates its child widget only when the item property changes.
In this example the child reflects the change in the same item in the list dynamically.
This section shows just a part of the source file `listeditor.c`.
This section shows just some parts of the source file `listeditor.c`.
If you want to see the whole codes, see `src/listeditor` directory of the [Gtk4 tutorial repository](https://github.com/ToshioCP/Gtk4-tutorial).
## A list editor
@ -33,7 +24,7 @@ It reads a text file line by line.
Each line is an item of the list.
The list is displayed with GtkColumnView.
There are two columns.
The one is a button, which makes the line be a current line.
The one is a button, which shows if the line is a current line.
If the line is the current line, the button is colored with red.
The other is a string which is the contents of the corresponding item of the list.
@ -47,18 +38,18 @@ You can compile end execute it as follows.
- Type the following on your commandline.
~~~
$ meson _build
$ meson setup _build
$ ninja -C _build
$ _build/listeditor
~~~
- Append button: appends a line after the current line, or at the last line if no current line exists.
- Insert button: inserts a line before the current line.
- Insert button: inserts a line before the current line, or at the top line if no current line exists.
- Remove button: removes a current line.
- Read button: reads a file.
- Write button: writes the contents to a file.
- close button: close the contents.
- quit button: quit the application.
- close button: closes the contents.
- quit button: quits the application.
- Button on the select column: makes the line current.
- String column: GtkText. You can edit a string in the field.
@ -69,7 +60,7 @@ The file name is shown at the right of the write button.
The second column (GtkColumnViewColumn) sets its factory property to GtkSignalListItemFactory.
It uses three signals setup, bind and unbind.
The following is their sgnal handlers.
The following shows the signal handlers.
@@@include
listeditor/listeditor.c setup2_cb bind2_cb unbind2_cb
@ -84,17 +75,13 @@ So, teardown signal handler isn't necessary.
It is called when the `listitem` is bound to an item in the list.
The list items are LeData instances.
LeData is defined in the file `listeditor.c` (the C source file of the list editor).
It is a child class of GObject and has two data.
The one is `listitem` which points a first column GtkListItem instance when they are connected.
Be careful that the GtkListItem instance is *not* the `listitem` in this handler.
If no GtkListItem is connected, it is NULL.
The other is `string` which is a content of the line.
It is a child class of GObject and has string data which is the content of the line.
- 10-11: `text` is a child of the `listitem` and it is a GtkText instance.
And `buffer` is a GtkTextBuffer instance of the `text`.
And `buffer` is a GtkEntryBuffer instance of the `text`.
- 12: The LeData instance `data` is an item pointed by the `listitem`.
- 15-16: Sets the text of `text` to `le_data_look_string (data)`.
le\_data\_look\_string returns the string of the `data` and the ownership of the string is still taken by the `data`.
So, the caller don't need to free the string.
So, the caller doesn't need to free the string.
- 18: `g_object_bind_property` binds a property and another object property.
This line binds the "text" property of the `buffer` (source) and the "string" property of the `data` (destination).
It is a uni-directional binding (`G_BINDING_DEFAULT`).
@ -107,14 +94,19 @@ The key is a string (or GQuark) and the value is a gpointer (pointer to any type
The function `g_object_set_data` sets the association from the key to the value.
This line sets the association from "bind" to `bind` instance.
It makes possible for the "unbind" handler to get the `bind` instance.
- 22-28: `unbind2_cb` is a unbind signal handler.
- 22-29: `unbind2_cb` is a unbind signal handler.
- 24: Retrieves the `bind` instance from the table in the `listitem` instance.
- 26: Unbind the binding.
- 27: Removes the value corresponds to the "bind" key.
- 26-27: Unbind the binding.
- 28: Removes the value corresponds to the "bind" key.
This technique is not so complicated.
You can use it when you make a cell editable application.
If it is impossible to use `g_object_bind_property`, use a notify signal on the GtkEntryBuffer instance.
You can use "deleted-text" and "inserted-text" signal instead.
The handler of the signals above copies the text in the GtkEntryBuffer instance to the LeData string.
Connect the notify signal handler in `bind2_cb` and disconnect it in `unbind2_cb`.
## Change the cell of GtkColumnView dynamically
Next topic is to change the GtkColumnView (or GtkListView) cells dynamically.
@ -139,85 +131,79 @@ The current line doesn't need to be on the display.
It is possible to be on the line out of the Window (GtkScrolledWindow).
Actually, the program doesn't use GtkSingleSelection.
It is necessary to know the corresponding GtkListItem instance from the item in the list.
It is the opposite direction from `gtk_list_item_get_item` function.
To accomplish this, we set a `listitem` element of LeData to point the corresponding GtkListItem instance.
Therefore, items (LeData) in the list always know the GtkListItem.
If there's no GtkListItem bound to the item, NULL is assigned.
The LeWindow instance has two instance variables for recording the current line.
- `win->position`: An int type variable. It is the position of the current line. It is zero-based. If no current line exists, it is -1.
- `win->current_button`: A variable points the button, located at the first column, on the current line. If no current line exists, it is NULL.
If the current line moves, the following two functions are called.
They updates the two varables.
@@@include
listeditor/listeditor.c select_cb setup1_cb bind1_cb unbind1_cb
listeditor/listeditor.c update_current_position update_current_button
@@@
- 8-14: `setup1_cb` is a setup signal handler on the GtkSignalListItemFactory.
This factory is inserted to the factory property of the first GtkColumnViewColumn.
The varable `win->position_label` points a GtkLabel instance.
The label shows the current line position.
The current button has CSS "current" class.
The button is colored red through the CSS "button.current {background: red;}".
The order of the call for these two functions is important.
The first function, which updates the position, is usually called first.
After that, a new line is appended or inserted.
Then, the second function is called.
The following functions call the two functions above.
Be careful about the order of the call.
@@@include
listeditor/listeditor.c select_cb setup1_cb bind1_cb
@@@
- 1-7: `select_cb` is a "clicked" signal handler.
The handler just calls the two functions and update the position and button.
- 9-15: `setup1_cb` is a setup signal handler on the GtkSignalListItemFactory.
It sets the child of `listitem` to a GtkButton instance.
The "clicked" signal on the button is connected to the handler `select_cb`.
When the listitem is destroyed, the child (GtkButton) is also destroyed.
At the same time, the connection of the signal and the handler is also destroyed.
So, you don't need teardown signal handler.
- 1-6: `select_cb` is a "clicked" signal handler.
LeWindow is defined in `listeditor.c`.
It's a child class of GtkApplicationWindow.
The handler just calls the `update_current` function.
The function will be explained later.
- 16-31: `bind1_cb` is a bind signal handler.
It sets the "listitem" element of the item (LeData) to point the `listitem` (GtkListItem instance).
It makes the item possible to find the corresponding GtkListItem instance.
If the item is the current line, the CSS class of the button includes "current" class.
Otherwise it has no CSS class.
This is necessary because the button may be recycled and it has had former CSS class.
The class need to be updated.
- 33-38: `unbind1_cb` is an unbind signal handler.
It removes the `listitem` instance from the "listitem" element of the item.
The element becomes NULL, which tells no GtkListItem is bound.
When referring GtkListItem, it needs to check the "listitem" element whether it points a GtkListItem or not (NULL).
Otherwise bad things will happen.
- 17-24: `bind1_cb` is a bind signal handler.
Usually, the position moves before this handler is called.
If the item is on the current line, the button is updated.
No unbind handler is necessary.
When a line is added, the current position is updated in advance.
@@@include
listeditor/listeditor.c update_current
listeditor/listeditor.c app_cb ins_cb
@@@
The function `update_current` does several things.
When a line is removed, the current position becomes -1 and no button is current.
- It has two parameters.
The first one is `win`, which is an instance of LeWindow class.
It has some elements.
- win->position: an Integer. it is the current position. If no current line exists, it is -1.
- win->position_label: GtkLabel. It shows the current position.
- The second parameter is `new`, which is the new current position.
At the beginning of the function, win->position points the old position.
- 10-16: Update the text of GtkLabel.
- 18-26: If the old position (win->position) is not negative, the current line exists.
It gets a GtkListItem instance via the item (LeData) of the list.
And it gets the GtkButton instance which is the child of the GtkListItem.
It clears the "css-classes" property of the button.
- 27: Updates win->position.
- 28-36: If the new position is not negative (It's possible to be negative when the current line has been removed), the current line exists.
It sets the "css-classes" property of the button to `{"current", NULL}`.
It is a NULL-terminated array of strings.
Each string is a CSS class.
Now the button has "current" style class.
@@@include
listeditor/listeditor.c rm_cb
@@@
The color of buttons are determined by the "background" CSS style.
The following CSS is applied to the default GdkDisplay in advance (in the startup handler of the application).
The following CSS node is a bit complicated.
CSS node `column view` has `listview` child node.
It covers the rows in the GtkColumnView.
The `listview` node is the same as the one for GtkListView.
It has `row` child node, which is for each child widget.
Therefore, the following node corresponds buttons on the GtkColumnView widget.
In addition, it is applied to the "current" class.
@@@if gfm
~~~css
columnview listview row button.current {background: red;}
~~~
The selectors "columnview listview row" is needed before "button" selector.
Otherwise the buttons in the GtkColumnview won't be found.
The button selector has "current" class.
So, the only "current" class button is colored with red.
Other buttons are not colored, which means they are white.
## Gtk\_widget\_dispose\_template function
The function `gtk_widget_dispose_template` clears the template children for the given widget.
This is the opposite of `gtk_widget_init_template()`.
It is a new function of GTK 4.8 version.
If your GTK version is lower than 4.8, you need to modify the program.
@@@else
~~~{.css}
columnview listview row button.current {background: red;}
~~~
@@@end
## A waring from GtkText