Bug fixed (section 30, list editor).

This commit is contained in:
Toshio Sekiya 2023-02-04 21:52:42 +09:00
parent d2f89cd1e7
commit 9650830860
5 changed files with 284 additions and 162 deletions

View file

@ -186,53 +186,59 @@ class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span i
<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><span class="op">}</span></span>
<span id="cb2-6"><a href="#cb2-6"></a></span>
<span id="cb2-7"><a href="#cb2-7"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb2-8"><a href="#cb2-8"></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-9"><a href="#cb2-9"></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-10"><a href="#cb2-10"></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-11"><a href="#cb2-11"></a> GtkEntryBuffer <span class="op">*</span>buffer<span class="op">;</span></span>
<span id="cb2-12"><a href="#cb2-12"></a> GBinding <span class="op">*</span>bind<span class="op">;</span></span>
<span id="cb2-13"><a href="#cb2-13"></a></span>
<span id="cb2-14"><a href="#cb2-14"></a> 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-15"><a href="#cb2-15"></a> gtk_entry_buffer_set_text <span class="op">(</span>buffer<span class="op">,</span> le_data_look_string <span class="op">(</span>data<span class="op">),</span> <span class="op">-</span><span class="dv">1</span><span class="op">);</span></span>
<span id="cb2-16"><a href="#cb2-16"></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-17"><a href="#cb2-17"></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-18"><a href="#cb2-18"></a><span class="op">}</span></span>
<span id="cb2-19"><a href="#cb2-19"></a></span>
<span id="cb2-20"><a href="#cb2-20"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb2-21"><a href="#cb2-21"></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-22"><a href="#cb2-22"></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-23"><a href="#cb2-23"></a></span>
<span id="cb2-24"><a href="#cb2-24"></a> g_binding_unbind<span class="op">(</span>bind<span class="op">);</span></span>
<span id="cb2-25"><a href="#cb2-25"></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-26"><a href="#cb2-26"></a><span class="op">}</span></span></code></pre></div>
<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-5: <code>setup2_cb</code> is a setup signal handler on the
<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. Te handler just creates a
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>7-18: <code>bind2_cb</code> is a bind signal handler. It is called
<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 GtkListItem instance when they are
connected. If no GtkListItem is connected, it is NULL. The other is
<code>string</code> which is a content of the line.
<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>9-10: <code>text</code> is a child of the <code>listitem</code> and
it is a GtkText instance. <code>data</code> is an item pointed by the
<code>listitem</code>. It is a LeData instance.</li>
<li>14: Gets the buffer of the <code>text</code>.</li>
<li>15: Sets the text of the buffer to
<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 data and the ownership of the string is taken by the
<code>data</code>. So, the caller dont need to free the string.</li>
<li>16: <code>g_object_bind_property</code> binds a property and another
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
@ -241,19 +247,19 @@ 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>17: GObjec has a table. The key is a string (or GQuark) and the
<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 on the <code>listitem</code> instance. It makes possible for
the “unbind” handler to get the <code>bind</code> instance.</li>
instance. It makes possible for the “unbind” handler to get the
<code>bind</code> instance.</li>
</ul></li>
<li>20-26: <code>unbind2_cb</code> is a unbind signal handler.
<li>22-28: <code>unbind2_cb</code> is a unbind signal handler.
<ul>
<li>22: Retrieves the <code>bind</code> instance from the table in the
<li>24: Retrieves the <code>bind</code> instance from the table in the
<code>listitem</code> instance.</li>
<li>24: Unbind the binding.</li>
<li>25: Removes the value corresponds to the “bind” key.</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
@ -271,6 +277,10 @@ moves.</p>
<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>
@ -284,7 +294,7 @@ 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 binded to the item, NULL is
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>
@ -298,42 +308,56 @@ class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span i
<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> 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-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>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> <span class="op">{</span></span>
<span id="cb3-17"><a href="#cb3-17"></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-18"><a href="#cb3-18"></a> <span class="cf">if</span> <span class="op">(</span>data<span class="op">)</span></span>
<span id="cb3-19"><a href="#cb3-19"></a> le_data_set_listitem <span class="op">(</span>data<span class="op">,</span> listitem<span class="op">);</span></span>
<span id="cb3-20"><a href="#cb3-20"></a><span class="op">}</span></span>
<span id="cb3-21"><a href="#cb3-21"></a></span>
<span id="cb3-22"><a href="#cb3-22"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb3-23"><a href="#cb3-23"></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-24"><a href="#cb3-24"></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-25"><a href="#cb3-25"></a> <span class="cf">if</span> <span class="op">(</span>data<span class="op">)</span></span>
<span id="cb3-26"><a href="#cb3-26"></a> le_data_set_listitem <span class="op">(</span>data<span class="op">,</span> NULL<span class="op">);</span></span>
<span id="cb3-27"><a href="#cb3-27"></a><span class="op">}</span></span></code></pre></div>
<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-13: <code>setup1_cb</code> is a setup signal handler on the
<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 newly created 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 handler is also destroyed. So, you dont need teardown signal
handler.</li>
<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>15-20: <code>bind1_cb</code> is a bind signal handler. It sets the
<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.</li>
<li>22-27: <code>unbind1_cb</code> is an unbind signal handler. It
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”
@ -423,6 +447,15 @@ 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 the list has many items and it needs to be scrolled, a warning
message can be issued.</p>
<pre><code>GtkText - unexpected blinking selection. Removing</code></pre>
<p>I dont have an idea why this happens. But if GtkText “focusable”
property is FALSE, the warning doesnt happen. So it probably come from
focus and scroll. If you know the reason, please let me know.</p>
<p>It is a warning message, not a fatal error. I think we can ignore
it.</p>
</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

@ -78,65 +78,68 @@ The following is their sgnal handlers.
2 setup2_cb (GtkListItemFactory *factory, GtkListItem *listitem) {
3 GtkWidget *text = gtk_text_new ();
4 gtk_list_item_set_child (listitem, GTK_WIDGET (text));
5 }
6
7 static void
8 bind2_cb (GtkListItemFactory *factory, GtkListItem *listitem) {
9 GtkWidget *text = gtk_list_item_get_child (listitem);
10 LeData *data = LE_DATA (gtk_list_item_get_item (listitem));
11 GtkEntryBuffer *buffer;
12 GBinding *bind;
13
14 buffer = gtk_text_get_buffer (GTK_TEXT (text));
15 gtk_entry_buffer_set_text (buffer, le_data_look_string (data), -1);
16 bind = g_object_bind_property (buffer, "text", data, "string", G_BINDING_DEFAULT);
17 g_object_set_data (G_OBJECT (listitem), "bind", bind);
18 }
19
20 static void
21 unbind2_cb (GtkListItemFactory *factory, GtkListItem *listitem) {
22 GBinding *bind = G_BINDING (g_object_get_data (G_OBJECT (listitem), "bind"));
23
24 g_binding_unbind(bind);
25 g_object_set_data (G_OBJECT (listitem), "bind", NULL);
26 }
5 gtk_editable_set_alignment (GTK_EDITABLE (text), 0.0);
6 }
7
8 static void
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));
13 GBinding *bind;
14
15 gtk_editable_set_text (GTK_EDITABLE (text), le_data_look_string (data));
16 gtk_editable_set_position (GTK_EDITABLE (text), 0);
17
18 bind = g_object_bind_property (buffer, "text", data, "string", G_BINDING_DEFAULT);
19 g_object_set_data (G_OBJECT (listitem), "bind", bind);
20 }
21
22 static void
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 }
~~~
- 1-5: `setup2_cb` is a setup signal handler on the GtkSignalListItemFactory.
- 1-6: `setup2_cb` is a setup signal handler on the GtkSignalListItemFactory.
This factory is inserted to the factory property of the second GtkColumnViewColumn.
Te handler just creates a GtkText instance and sets the child of `listitem` to it.
The handler just creates a GtkText instance and sets the child of `listitem` to it.
The instance will be destroyed automatically when the `listitem` is destroyed.
So, teardown signal handler isn't necessary.
- 7-18: `bind2_cb` is a bind signal handler.
- 8-20: `bind2_cb` is a bind signal handler.
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 GtkListItem instance when they are connected.
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.
- 9-10: `text` is a child of the `listitem` and it is a GtkText instance.
`data` is an item pointed by the `listitem`. It is a LeData instance.
- 14: Gets the buffer of the `text`.
- 15: Sets the text of the buffer to `le_data_look_string (data)`.
le\_data\_look\_string returns the string of the data and the ownership of the string is taken by the `data`.
- 10-11: `text` is a child of the `listitem` and it is a GtkText instance.
And `buffer` is a GtkTextBuffer 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.
- 16: `g_object_bind_property` binds a property and another object property.
- 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`).
When a user changes the GtkText text, the same string is immediately put into the `data`.
The function returns a GBinding instance.
This binding is different from bindings of GtkExpression.
This binding needs the existence of the two properties.
- 17: GObjec has a table.
- 19: GObjec has a table.
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 on the `listitem` instance.
This line sets the association from "bind" to `bind` instance.
It makes possible for the "unbind" handler to get the `bind` instance.
- 20-26: `unbind2_cb` is a unbind signal handler.
- 22: Retrieves the `bind` instance from the table in the `listitem` instance.
- 24: Unbind the binding.
- 25: Removes the value corresponds to the "bind" key.
- 22-28: `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.
This technique is not so complicated.
You can use it when you make a cell editable application.
@ -152,6 +155,10 @@ The line editor has the current position of the list.
- When a line is appended or inserted, the line is current.
- When the current line is deleted, no line will be current.
- When a button in the first column of GtkColumnView is clicked, the line will be current.
- 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.
The button of the current line is colored with red and otherwise white.
@ -165,7 +172,7 @@ It is necessary to know the corresponding GtkListItem instance from the item in
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 binded to the item, NULL is assigned.
If there's no GtkListItem bound to the item, NULL is assigned.
~~~C
1 void
@ -179,40 +186,55 @@ If there's no GtkListItem binded to the item, NULL is assigned.
9 setup1_cb (GtkListItemFactory *factory, GtkListItem *listitem) {
10 GtkWidget *button = gtk_button_new ();
11 gtk_list_item_set_child (listitem, button);
12 g_signal_connect (button, "clicked", G_CALLBACK (select_cb), listitem);
13 }
14
15 static void
16 bind1_cb (GtkListItemFactory *factory, GtkListItem *listitem) {
17 LeData *data = LE_DATA (gtk_list_item_get_item (listitem));
18 if (data)
19 le_data_set_listitem (data, listitem);
20 }
21
22 static void
23 unbind1_cb (GtkListItemFactory *factory, GtkListItem *listitem) {
24 LeData *data = LE_DATA (gtk_list_item_get_item (listitem));
25 if (data)
26 le_data_set_listitem (data, NULL);
27 }
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 }
~~~
- 8-13: `setup1_cb` is a setup signal handler on the GtkSignalListItemFactory.
- 8-14: `setup1_cb` 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 `listitem` to a newly created GtkButton instance.
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 handler 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.
- 15-20: `bind1_cb` is a bind signal handler.
- 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.
- 22-27: `unbind1_cb` is an unbind signal handler.
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).
@ -258,7 +280,6 @@ Otherwise bad things will happen.
37 }
~~~
The function `update_current` does several things.
- It has two parameters.
@ -300,4 +321,20 @@ 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 the list has many items and it needs to be scrolled, a warning message can be issued.
~~~
GtkText - unexpected blinking selection. Removing
~~~
I don't have an idea why this happens.
But if GtkText "focusable" property is FALSE, the warning doesn't happen.
So it probably come from focus and scroll.
If you know the reason, please let me know.
It is a warning message, not a fatal error.
I think we can ignore it.
Up: [README.md](../README.md), Prev: [Section 29](sec29.md)

View file

@ -101,12 +101,12 @@ le_data_take_string (LeData *self, char *string) {
GtkListItem *
le_data_get_listitem (LeData *self) {
return g_object_ref (self->listitem);
return self->listitem ? g_object_ref (self->listitem) : NULL;
}
GtkListItem *
le_data_look_listitem (LeData *self) {
return self->listitem;
return self->listitem ? self->listitem : NULL;
}
char *
@ -396,14 +396,25 @@ static void
setup1_cb (GtkListItemFactory *factory, GtkListItem *listitem) {
GtkWidget *button = gtk_button_new ();
gtk_list_item_set_child (listitem, button);
gtk_widget_set_focusable (GTK_WIDGET (button), FALSE);
g_signal_connect (button, "clicked", G_CALLBACK (select_cb), listitem);
}
static void
bind1_cb (GtkListItemFactory *factory, GtkListItem *listitem) {
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)
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
@ -417,17 +428,19 @@ static void
setup2_cb (GtkListItemFactory *factory, GtkListItem *listitem) {
GtkWidget *text = gtk_text_new ();
gtk_list_item_set_child (listitem, GTK_WIDGET (text));
gtk_editable_set_alignment (GTK_EDITABLE (text), 0.0);
}
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));
GtkEntryBuffer *buffer;
GBinding *bind;
buffer = gtk_text_get_buffer (GTK_TEXT (text));
gtk_entry_buffer_set_text (buffer, le_data_look_string (data), -1);
gtk_editable_set_text (GTK_EDITABLE (text), le_data_look_string (data));
gtk_editable_set_position (GTK_EDITABLE (text), 0);
bind = g_object_bind_property (buffer, "text", data, "string", G_BINDING_DEFAULT);
g_object_set_data (G_OBJECT (listitem), "bind", bind);
}
@ -440,6 +453,13 @@ unbind2_cb (GtkListItemFactory *factory, GtkListItem *listitem) {
g_object_set_data (G_OBJECT (listitem), "bind", NULL);
}
// static void
// adjustment_value_changed_cb (GtkAdjustment *adjustment, gpointer user_data) {
// GtkWidget *win = GTK_WIDGET (user_data);
// gtk_widget_set_can_focus (win, FALSE);
// }
static void
le_window_init (LeWindow *win) {
gtk_widget_init_template (GTK_WIDGET (win));
@ -483,6 +503,7 @@ le_window_class_init (LeWindowClass *class) {
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);
// gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), adjustment_value_changed_cb);
}
GtkWidget *

View file

@ -84,6 +84,13 @@
<object class="GtkScrolledWindow">
<property name="hexpand">TRUE</property>
<property name="vexpand">TRUE</property>
<!--
<property name="vadjustment">
<object class="GtkAdjustment">
<signal name="value-changed" handler="adjustment_value_changed_cb" swapped="no" object="LeWindow"/>
</object>
</property>
-->
<child>
<object class="GtkColumnView" id="columnview">
<property name="hexpand">TRUE</property>
@ -104,7 +111,7 @@
<property name="factory">
<object class="GtkSignalListItemFactory">
<signal name="setup" handler="setup1_cb"></signal>
<signal name="bind" handler="bind1_cb"></signal>
<signal name="bind" handler="bind1_cb" swapped="no" object="LeWindow"></signal>
<signal name="unbind" handler="unbind1_cb"></signal>
</object>
</property>

View file

@ -75,41 +75,42 @@ The following is their sgnal handlers.
listeditor/listeditor.c setup2_cb bind2_cb unbind2_cb
@@@
- 1-5: `setup2_cb` is a setup signal handler on the GtkSignalListItemFactory.
- 1-6: `setup2_cb` is a setup signal handler on the GtkSignalListItemFactory.
This factory is inserted to the factory property of the second GtkColumnViewColumn.
Te handler just creates a GtkText instance and sets the child of `listitem` to it.
The handler just creates a GtkText instance and sets the child of `listitem` to it.
The instance will be destroyed automatically when the `listitem` is destroyed.
So, teardown signal handler isn't necessary.
- 7-18: `bind2_cb` is a bind signal handler.
- 8-20: `bind2_cb` is a bind signal handler.
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 GtkListItem instance when they are connected.
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.
- 9-10: `text` is a child of the `listitem` and it is a GtkText instance.
`data` is an item pointed by the `listitem`. It is a LeData instance.
- 14: Gets the buffer of the `text`.
- 15: Sets the text of the buffer to `le_data_look_string (data)`.
le\_data\_look\_string returns the string of the data and the ownership of the string is taken by the `data`.
- 10-11: `text` is a child of the `listitem` and it is a GtkText instance.
And `buffer` is a GtkTextBuffer 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.
- 16: `g_object_bind_property` binds a property and another object property.
- 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`).
When a user changes the GtkText text, the same string is immediately put into the `data`.
The function returns a GBinding instance.
This binding is different from bindings of GtkExpression.
This binding needs the existence of the two properties.
- 17: GObjec has a table.
- 19: GObjec has a table.
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 on the `listitem` instance.
This line sets the association from "bind" to `bind` instance.
It makes possible for the "unbind" handler to get the `bind` instance.
- 20-26: `unbind2_cb` is a unbind signal handler.
- 22: Retrieves the `bind` instance from the table in the `listitem` instance.
- 24: Unbind the binding.
- 25: Removes the value corresponds to the "bind" key.
- 22-28: `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.
This technique is not so complicated.
You can use it when you make a cell editable application.
@ -125,6 +126,10 @@ The line editor has the current position of the list.
- When a line is appended or inserted, the line is current.
- When the current line is deleted, no line will be current.
- When a button in the first column of GtkColumnView is clicked, the line will be current.
- 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.
The button of the current line is colored with red and otherwise white.
@ -138,28 +143,32 @@ It is necessary to know the corresponding GtkListItem instance from the item in
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 binded to the item, NULL is assigned.
If there's no GtkListItem bound to the item, NULL is assigned.
@@@include
listeditor/listeditor.c select_cb setup1_cb bind1_cb unbind1_cb
@@@
- 8-13: `setup1_cb` is a setup signal handler on the GtkSignalListItemFactory.
- 8-14: `setup1_cb` 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 `listitem` to a newly created GtkButton instance.
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 handler 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.
- 15-20: `bind1_cb` is a bind signal handler.
- 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.
- 22-27: `unbind1_cb` is an unbind signal handler.
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).
@ -169,7 +178,6 @@ Otherwise bad things will happen.
listeditor/listeditor.c update_current
@@@
The function `update_current` does several things.
- It has two parameters.
@ -210,3 +218,19 @@ The function `gtk_widget_dispose_template` clears the template children for the
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 the list has many items and it needs to be scrolled, a warning message can be issued.
~~~
GtkText - unexpected blinking selection. Removing
~~~
I don't have an idea why this happens.
But if GtkText "focusable" property is FALSE, the warning doesn't happen.
So it probably come from focus and scroll.
If you know the reason, please let me know.
It is a warning message, not a fatal error.
I think we can ignore it.