Gtk4-tutorial/docs/sec11.html

431 lines
42 KiB
HTML
Raw Normal View History

<!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>Gtk4 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="sec10.html">Prev: section10</a>
</li>
<li class="nav-item">
<a class="nav-link" href="sec12.html">Next: section12</a>
</li>
</ul>
</div>
</div>
</nav>
<h1 id="initialization-and-destruction-of-instances">Initialization and destruction of instances</h1>
<p>A new version of the text file editor (<code>tfe</code>) will be made in this section and the following four sections. It is <code>tfe5</code>. There are many changes from the prior version. All the sources are listed in <a href="sec16.html">Section 16</a>. They are located in two directories, src/tfe5 and src/tfetextview.</p>
<h2 id="encapsulation">Encapsulation</h2>
<p>Weve divided C source file into two parts. But it is not enough in terms of encapsulation.</p>
<ul>
<li><code>tfe.c</code> includes everything other than TfeTextView. It should be divided at least into two parts, <code>tfeapplication.c</code> and <code>tfenotebook.c</code>.</li>
<li>Header files also need to be organized.</li>
</ul>
<p>However, first of all, Id like to focus on the object TfeTextView. It is a child object of GtkTextView and has a new member <code>file</code> in it. The important thing is to manage the Gfile object pointed by <code>file</code>.</p>
<ul>
<li>What is necessary to GFile when creating (or initializing) TfeTextView?</li>
<li>What is necessary to GFile when destructing TfeTextView?</li>
<li>TfeTextView should read/write a file by itself or not?</li>
<li>How it communicates with objects outside?</li>
</ul>
<p>You need to know at least class, instance and signals before thinking about them. I will explain them in this section and the next section. After that I will explain:</p>
<ul>
<li>Organizing functions.</li>
<li>How to use GtkFileChooserDialog</li>
</ul>
<h2 id="gobject-and-its-children">GObject and its children</h2>
<p>GObject and its children are objects, which have both class and instance. First, think about instance of objects. Instance is structured memories. The structure is described as C language structure. The following is a structure of TfeTextView.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true"></a><span class="co">/* This typedef statement is automatically generated by the macro G_DECLARE_FINAL_TYPE */</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true"></a><span class="kw">typedef</span> <span class="kw">struct</span> _TfeTextView TfeTextView;</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true"></a></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true"></a><span class="kw">struct</span> _TfeTextView {</span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true"></a> GtkTextView parent;</span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true"></a> GFile *file;</span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true"></a>};</span></code></pre></div>
<p>The members of the structure are:</p>
<ul>
<li>The type of <code>parent</code> is GtkTextView which is C structure. It is declared in <code>gtktextview.h</code>. GtkTextView is the parent of TfeTextView.</li>
<li><code>file</code> is a pointer to GFile. It can be NULL if no file corresponds to the TfeTextView object.</li>
</ul>
<p>Notice the program above is the declaration of the structure, not the definition. So, no memories are allocated at this moment. They are to be allocated when <code>tfe_text_view_new</code> function is invoked. The memory allocated with <code>tfe_text_view_new</code> is an instance of TfeTextView object. Therefore, There can be multiple TfeTextView instances if <code>tfe_text_view_new</code> is called multiple times.</p>
<p>You can find the declaration of the ancestors of TfeTextView in the source files of GTK and GLib. The following is extracts from the source files (not exactly the same).</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true"></a><span class="kw">typedef</span> <span class="kw">struct</span> _GObject GObject;</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true"></a><span class="kw">typedef</span> <span class="kw">struct</span> _GObject GInitiallyUnowned;</span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true"></a><span class="kw">struct</span> _GObject</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true"></a>{</span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true"></a> GTypeInstance g_type_instance;</span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true"></a> <span class="dt">volatile</span> guint ref_count;</span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true"></a> GData *qdata;</span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true"></a>};</span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true"></a></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true"></a><span class="kw">typedef</span> <span class="kw">struct</span> _GtkWidget GtkWidget;</span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true"></a><span class="kw">struct</span> _GtkWidget</span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true"></a>{</span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true"></a> GInitiallyUnowned parent_instance;</span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true"></a> GtkWidgetPrivate *priv;</span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true"></a>};</span>
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true"></a></span>
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true"></a><span class="kw">typedef</span> <span class="kw">struct</span> _GtkTextView GtkTextView;</span>
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true"></a><span class="kw">struct</span> _GtkTextView</span>
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true"></a>{</span>
<span id="cb2-20"><a href="#cb2-20" aria-hidden="true"></a> GtkWidget parent_instance;</span>
<span id="cb2-21"><a href="#cb2-21" aria-hidden="true"></a> GtkTextViewPrivate *priv;</span>
<span id="cb2-22"><a href="#cb2-22" aria-hidden="true"></a>};</span></code></pre></div>
<p>In each structure, its parent is declared at the top of the members. So, every ancestors is included in the child instance. This is very important. It guarantees a child widget to inherit all the features from ancestors. The structure of <code>TfeTextView</code> is like the following diagram.</p>
<figure>
<img src="image/TfeTextView.png" alt="" /><figcaption>The structure of the instance TfeTextView</figcaption>
</figure>
<h2 id="initialization-of-a-tfetextview-instance">Initialization of a TfeTextView instance</h2>
<p>The function <code>tfe_text_view_new</code> creates a new TfeTextView instance.</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>GtkWidget *</span>
<span id="cb3-2"><a href="#cb3-2"></a>tfe_text_view_new (<span class="dt">void</span>) {</span>
<span id="cb3-3"><a href="#cb3-3"></a> <span class="cf">return</span> GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL));</span>
<span id="cb3-4"><a href="#cb3-4"></a>}</span></code></pre></div>
<p>When this function is invoked, a TfeTextView instance is created and initialized. The initialization process is as follows.</p>
<ol type="1">
<li>Initializes GObject (GInitiallyUnowned) part in TfeTextView instance.</li>
<li>Initializes GtkWidget part in TfeTextView instance.</li>
<li>Initializes GtkTextView part in TfeTextView instance.</li>
<li>Initializes TfeTextView part in TfeTextView instance.</li>
</ol>
<p>The step one through three is done by <code>g_object_init</code>, <code>gtk_widget_init</code> and <code>gtk_text_view_init</code>. They are called by the system automatically and you dont need to care about them. Step four is done by the function <code>tfe_text_view_init</code> in <code>tfetextview.c</code>.</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>tfe_text_view_init (TfeTextView *tv) {</span>
<span id="cb4-3"><a href="#cb4-3"></a> tv-&gt;file = NULL;</span>
<span id="cb4-4"><a href="#cb4-4"></a>}</span></code></pre></div>
<p>This function just initializes <code>tv-&gt;file</code> to be <code>NULL</code>.</p>
<h2 id="functions-and-classes">Functions and Classes</h2>
<p>In Gtk, all objects derived from GObject have class and instance (except abstract object). An instance is memory of C structure, which is described in the previous two subsections. Each object can have more than one instance. Those instances have the same structure. An instance just keeps status of the instance. Therefore, it is insufficient to define its behavior. We need at least two things. One is functions and the other is class.</p>
<p>Youve already seen many functions. For example, <code>tfe_text_view_new</code> is a function to create a TfeTextView instance. These functions are similar to public object methods in object oriented languages such as Java or Ruby. Functions are public, which means that they are expected to be used by other objects.</p>
<p>Class comprises mainly pointers to functions. Those functions are used by the object itself or its descendant objects. For example, GObject class is declared in <code>gobject.h</code> in GLib source files.</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb5-1"><a href="#cb5-1"></a><span class="kw">typedef</span> <span class="kw">struct</span> _GObjectClass GObjectClass;</span>
<span id="cb5-2"><a href="#cb5-2"></a><span class="kw">typedef</span> <span class="kw">struct</span> _GObjectClass GInitiallyUnownedClass;</span>
<span id="cb5-3"><a href="#cb5-3"></a></span>
<span id="cb5-4"><a href="#cb5-4"></a><span class="kw">struct</span> _GObjectClass</span>
<span id="cb5-5"><a href="#cb5-5"></a>{</span>
<span id="cb5-6"><a href="#cb5-6"></a> GTypeClass g_type_class;</span>
<span id="cb5-7"><a href="#cb5-7"></a> <span class="co">/*&lt; private &gt;*/</span></span>
<span id="cb5-8"><a href="#cb5-8"></a> GSList *construct_properties;</span>
<span id="cb5-9"><a href="#cb5-9"></a> <span class="co">/*&lt; public &gt;*/</span></span>
<span id="cb5-10"><a href="#cb5-10"></a> <span class="co">/* seldom overridden */</span></span>
<span id="cb5-11"><a href="#cb5-11"></a> GObject* (*constructor) (GType type,</span>
<span id="cb5-12"><a href="#cb5-12"></a> guint n_construct_properties,</span>
<span id="cb5-13"><a href="#cb5-13"></a> GObjectConstructParam *construct_properties);</span>
<span id="cb5-14"><a href="#cb5-14"></a> <span class="co">/* overridable methods */</span></span>
<span id="cb5-15"><a href="#cb5-15"></a> <span class="dt">void</span> (*set_property) (GObject *object,</span>
<span id="cb5-16"><a href="#cb5-16"></a> guint property_id,</span>
<span id="cb5-17"><a href="#cb5-17"></a> <span class="dt">const</span> GValue *value,</span>
<span id="cb5-18"><a href="#cb5-18"></a> GParamSpec *pspec);</span>
<span id="cb5-19"><a href="#cb5-19"></a> <span class="dt">void</span> (*get_property) (GObject *object,</span>
<span id="cb5-20"><a href="#cb5-20"></a> guint property_id,</span>
<span id="cb5-21"><a href="#cb5-21"></a> GValue *value,</span>
<span id="cb5-22"><a href="#cb5-22"></a> GParamSpec *pspec);</span>
<span id="cb5-23"><a href="#cb5-23"></a> <span class="dt">void</span> (*dispose) (GObject *object);</span>
<span id="cb5-24"><a href="#cb5-24"></a> <span class="dt">void</span> (*finalize) (GObject *object);</span>
<span id="cb5-25"><a href="#cb5-25"></a> <span class="co">/* seldom overridden */</span></span>
<span id="cb5-26"><a href="#cb5-26"></a> <span class="dt">void</span> (*dispatch_properties_changed) (GObject *object,</span>
<span id="cb5-27"><a href="#cb5-27"></a> guint n_pspecs,</span>
<span id="cb5-28"><a href="#cb5-28"></a> GParamSpec **pspecs);</span>
<span id="cb5-29"><a href="#cb5-29"></a> <span class="co">/* signals */</span></span>
<span id="cb5-30"><a href="#cb5-30"></a> <span class="dt">void</span> (*notify) (GObject *object,</span>
<span id="cb5-31"><a href="#cb5-31"></a> GParamSpec *pspec);</span>
<span id="cb5-32"><a href="#cb5-32"></a> <span class="co">/* called when done constructing */</span></span>
<span id="cb5-33"><a href="#cb5-33"></a> <span class="dt">void</span> (*constructed) (GObject *object);</span>
<span id="cb5-34"><a href="#cb5-34"></a> <span class="co">/*&lt; private &gt;*/</span></span>
<span id="cb5-35"><a href="#cb5-35"></a> gsize flags;</span>
<span id="cb5-36"><a href="#cb5-36"></a> <span class="co">/* padding */</span></span>
<span id="cb5-37"><a href="#cb5-37"></a> gpointer pdummy[<span class="dv">6</span>];</span>
<span id="cb5-38"><a href="#cb5-38"></a>};</span></code></pre></div>
<p>Id like to explain some of the members. Theres a pointer to the function <code>dispose</code> in line 23.</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true"></a><span class="dt">void</span> (*dispose) (GObject *object);</span></code></pre></div>
<p>The declaration is a bit complicated. The asterisk before the identifier <code>dispose</code> means pointer. So, the pointer <code>dispose</code> points to a function which has one parameter, which points a GObject structure, and returns no value. In the same way, line 24 says <code>finalize</code> is a pointer to the function which has one parameter, which points a GObject structure, and returns no value.</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"></a><span class="dt">void</span> (*finalize) (GObject *object);</span></code></pre></div>
<p>Look at the declaration of <code>_GObjectClass</code> so that you would find that most of the members are pointers to functions.</p>
<ul>
<li>11: A function pointed by <code>constructor</code> is called when the instance is generated. It completes the initialization of the instance.</li>
<li>23: A function pointed by <code>dispose</code> is called when the instance destructs itself. Destruction process is divided into two phases. The first one is called disposing. In this phase, the instance releases all the references to other instances. The second phase is finalizing.</li>
<li>24: A function pointed by <code>finalize</code> finishes the destruction process.</li>
<li>The other pointers point to functions which are called while the instance lives.</li>
</ul>
<p>These functions are called class methods. The methods are open to its descendants. But not open to the objects which are not the descendants.</p>
<h2 id="tfetextview-class">TfeTextView class</h2>
<p>TfeTextView class is a structure and it includes all its ancestors class in it.</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"></a><span class="kw">typedef</span> _TfeTextView TfeTextView;</span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true"></a><span class="kw">struct</span> _TfeTextView {</span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true"></a> GtkTextView parent;</span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true"></a> GFile *file;</span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true"></a>};</span></code></pre></div>
<p>TfeTextView structure has GtkTextView type as the first member. In the same way, GtkTextView has its parent type (GtkWidget) as the first member. GtkWidget has its parent type (GtkInitiallyUnowned) as the first member. The structure of GtkInitiallyUnowned is the same as GObject. Therefore, TFeTextView includes GObject, GtkWidget and GtkTextView in itself.</p>
<pre><code>GObject -- GInitiallyUnowned -- GtkWidget -- GtkTextView -- TfeTextView</code></pre>
<p>The following is extracts from the source files (not exactly the same).</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="kw">struct</span> _GtkWidgetClass</span>
<span id="cb10-2"><a href="#cb10-2"></a>{</span>
<span id="cb10-3"><a href="#cb10-3"></a> GInitiallyUnownedClass parent_class;</span>
<span id="cb10-4"><a href="#cb10-4"></a></span>
<span id="cb10-5"><a href="#cb10-5"></a> <span class="co">/*&lt; public &gt;*/</span></span>
<span id="cb10-6"><a href="#cb10-6"></a></span>
<span id="cb10-7"><a href="#cb10-7"></a> <span class="co">/* basics */</span></span>
<span id="cb10-8"><a href="#cb10-8"></a> <span class="dt">void</span> (* show) (GtkWidget *widget);</span>
<span id="cb10-9"><a href="#cb10-9"></a> <span class="dt">void</span> (* hide) (GtkWidget *widget);</span>
<span id="cb10-10"><a href="#cb10-10"></a> <span class="dt">void</span> (* map) (GtkWidget *widget);</span>
<span id="cb10-11"><a href="#cb10-11"></a> <span class="dt">void</span> (* unmap) (GtkWidget *widget);</span>
<span id="cb10-12"><a href="#cb10-12"></a> <span class="dt">void</span> (* realize) (GtkWidget *widget);</span>
<span id="cb10-13"><a href="#cb10-13"></a> <span class="dt">void</span> (* unrealize) (GtkWidget *widget);</span>
<span id="cb10-14"><a href="#cb10-14"></a> <span class="dt">void</span> (* root) (GtkWidget *widget);</span>
<span id="cb10-15"><a href="#cb10-15"></a> <span class="dt">void</span> (* unroot) (GtkWidget *widget);</span>
<span id="cb10-16"><a href="#cb10-16"></a> <span class="dt">void</span> (* size_allocate) (GtkWidget *widget,</span>
<span id="cb10-17"><a href="#cb10-17"></a> <span class="dt">int</span> width,</span>
<span id="cb10-18"><a href="#cb10-18"></a> <span class="dt">int</span> height,</span>
<span id="cb10-19"><a href="#cb10-19"></a> <span class="dt">int</span> baseline);</span>
<span id="cb10-20"><a href="#cb10-20"></a> <span class="dt">void</span> (* state_flags_changed) (GtkWidget *widget,</span>
<span id="cb10-21"><a href="#cb10-21"></a> GtkStateFlags previous_state_flags);</span>
<span id="cb10-22"><a href="#cb10-22"></a> <span class="dt">void</span> (* direction_changed) (GtkWidget *widget,</span>
<span id="cb10-23"><a href="#cb10-23"></a> GtkTextDirection previous_direction);</span>
<span id="cb10-24"><a href="#cb10-24"></a></span>
<span id="cb10-25"><a href="#cb10-25"></a> <span class="co">/* size requests */</span></span>
<span id="cb10-26"><a href="#cb10-26"></a> GtkSizeRequestMode (* get_request_mode) (GtkWidget *widget);</span>
<span id="cb10-27"><a href="#cb10-27"></a> <span class="dt">void</span> (* measure) (GtkWidget *widget,</span>
<span id="cb10-28"><a href="#cb10-28"></a> GtkOrientation orientation,</span>
<span id="cb10-29"><a href="#cb10-29"></a> <span class="dt">int</span> for_size,</span>
<span id="cb10-30"><a href="#cb10-30"></a> <span class="dt">int</span> *minimum,</span>
<span id="cb10-31"><a href="#cb10-31"></a> <span class="dt">int</span> *natural,</span>
<span id="cb10-32"><a href="#cb10-32"></a> <span class="dt">int</span> *minimum_baseline,</span>
<span id="cb10-33"><a href="#cb10-33"></a> <span class="dt">int</span> *natural_baseline);</span>
<span id="cb10-34"><a href="#cb10-34"></a></span>
<span id="cb10-35"><a href="#cb10-35"></a> <span class="co">/* Mnemonics */</span></span>
<span id="cb10-36"><a href="#cb10-36"></a> gboolean (* mnemonic_activate) (GtkWidget *widget,</span>
<span id="cb10-37"><a href="#cb10-37"></a> gboolean group_cycling);</span>
<span id="cb10-38"><a href="#cb10-38"></a></span>
<span id="cb10-39"><a href="#cb10-39"></a> <span class="co">/* explicit focus */</span></span>
<span id="cb10-40"><a href="#cb10-40"></a> gboolean (* grab_focus) (GtkWidget *widget);</span>
<span id="cb10-41"><a href="#cb10-41"></a> gboolean (* focus) (GtkWidget *widget,</span>
<span id="cb10-42"><a href="#cb10-42"></a> GtkDirectionType direction);</span>
<span id="cb10-43"><a href="#cb10-43"></a> <span class="dt">void</span> (* set_focus_child) (GtkWidget *widget,</span>
<span id="cb10-44"><a href="#cb10-44"></a> GtkWidget *child);</span>
<span id="cb10-45"><a href="#cb10-45"></a></span>
<span id="cb10-46"><a href="#cb10-46"></a> <span class="co">/* keyboard navigation */</span></span>
<span id="cb10-47"><a href="#cb10-47"></a> <span class="dt">void</span> (* move_focus) (GtkWidget *widget,</span>
<span id="cb10-48"><a href="#cb10-48"></a> GtkDirectionType direction);</span>
<span id="cb10-49"><a href="#cb10-49"></a> gboolean (* keynav_failed) (GtkWidget *widget,</span>
<span id="cb10-50"><a href="#cb10-50"></a> GtkDirectionType direction);</span>
<span id="cb10-51"><a href="#cb10-51"></a></span>
<span id="cb10-52"><a href="#cb10-52"></a> gboolean (* query_tooltip) (GtkWidget *widget,</span>
<span id="cb10-53"><a href="#cb10-53"></a> <span class="dt">int</span> x,</span>
<span id="cb10-54"><a href="#cb10-54"></a> <span class="dt">int</span> y,</span>
<span id="cb10-55"><a href="#cb10-55"></a> gboolean keyboard_tooltip,</span>
<span id="cb10-56"><a href="#cb10-56"></a> GtkTooltip *tooltip);</span>
<span id="cb10-57"><a href="#cb10-57"></a></span>
<span id="cb10-58"><a href="#cb10-58"></a> <span class="dt">void</span> (* compute_expand) (GtkWidget *widget,</span>
<span id="cb10-59"><a href="#cb10-59"></a> gboolean *hexpand_p,</span>
<span id="cb10-60"><a href="#cb10-60"></a> gboolean *vexpand_p);</span>
<span id="cb10-61"><a href="#cb10-61"></a></span>
<span id="cb10-62"><a href="#cb10-62"></a> <span class="dt">void</span> (* css_changed) (GtkWidget *widget,</span>
<span id="cb10-63"><a href="#cb10-63"></a> GtkCssStyleChange *change);</span>
<span id="cb10-64"><a href="#cb10-64"></a></span>
<span id="cb10-65"><a href="#cb10-65"></a> <span class="dt">void</span> (* system_setting_changed) (GtkWidget *widget,</span>
<span id="cb10-66"><a href="#cb10-66"></a> GtkSystemSetting settings);</span>
<span id="cb10-67"><a href="#cb10-67"></a></span>
<span id="cb10-68"><a href="#cb10-68"></a> <span class="dt">void</span> (* snapshot) (GtkWidget *widget,</span>
<span id="cb10-69"><a href="#cb10-69"></a> GtkSnapshot *snapshot);</span>
<span id="cb10-70"><a href="#cb10-70"></a></span>
<span id="cb10-71"><a href="#cb10-71"></a> gboolean (* contains) (GtkWidget *widget,</span>
<span id="cb10-72"><a href="#cb10-72"></a> <span class="dt">double</span> x,</span>
<span id="cb10-73"><a href="#cb10-73"></a> <span class="dt">double</span> y);</span>
<span id="cb10-74"><a href="#cb10-74"></a></span>
<span id="cb10-75"><a href="#cb10-75"></a> <span class="co">/*&lt; private &gt;*/</span></span>
<span id="cb10-76"><a href="#cb10-76"></a></span>
<span id="cb10-77"><a href="#cb10-77"></a> GtkWidgetClassPrivate *priv;</span>
<span id="cb10-78"><a href="#cb10-78"></a></span>
<span id="cb10-79"><a href="#cb10-79"></a> gpointer padding[<span class="dv">8</span>];</span>
<span id="cb10-80"><a href="#cb10-80"></a>};</span>
<span id="cb10-81"><a href="#cb10-81"></a></span>
<span id="cb10-82"><a href="#cb10-82"></a><span class="kw">struct</span> _GtkTextViewClass</span>
<span id="cb10-83"><a href="#cb10-83"></a>{</span>
<span id="cb10-84"><a href="#cb10-84"></a> GtkWidgetClass parent_class;</span>
<span id="cb10-85"><a href="#cb10-85"></a></span>
<span id="cb10-86"><a href="#cb10-86"></a> <span class="co">/*&lt; public &gt;*/</span></span>
<span id="cb10-87"><a href="#cb10-87"></a></span>
<span id="cb10-88"><a href="#cb10-88"></a> <span class="dt">void</span> (* move_cursor) (GtkTextView *text_view,</span>
<span id="cb10-89"><a href="#cb10-89"></a> GtkMovementStep step,</span>
<span id="cb10-90"><a href="#cb10-90"></a> <span class="dt">int</span> count,</span>
<span id="cb10-91"><a href="#cb10-91"></a> gboolean extend_selection);</span>
<span id="cb10-92"><a href="#cb10-92"></a> <span class="dt">void</span> (* set_anchor) (GtkTextView *text_view);</span>
<span id="cb10-93"><a href="#cb10-93"></a> <span class="dt">void</span> (* insert_at_cursor) (GtkTextView *text_view,</span>
<span id="cb10-94"><a href="#cb10-94"></a> <span class="dt">const</span> <span class="dt">char</span> *str);</span>
<span id="cb10-95"><a href="#cb10-95"></a> <span class="dt">void</span> (* delete_from_cursor) (GtkTextView *text_view,</span>
<span id="cb10-96"><a href="#cb10-96"></a> GtkDeleteType type,</span>
<span id="cb10-97"><a href="#cb10-97"></a> <span class="dt">int</span> count);</span>
<span id="cb10-98"><a href="#cb10-98"></a> <span class="dt">void</span> (* backspace) (GtkTextView *text_view);</span>
<span id="cb10-99"><a href="#cb10-99"></a> <span class="dt">void</span> (* cut_clipboard) (GtkTextView *text_view);</span>
<span id="cb10-100"><a href="#cb10-100"></a> <span class="dt">void</span> (* copy_clipboard) (GtkTextView *text_view);</span>
<span id="cb10-101"><a href="#cb10-101"></a> <span class="dt">void</span> (* paste_clipboard) (GtkTextView *text_view);</span>
<span id="cb10-102"><a href="#cb10-102"></a> <span class="dt">void</span> (* toggle_overwrite) (GtkTextView *text_view);</span>
<span id="cb10-103"><a href="#cb10-103"></a> GtkTextBuffer * (* create_buffer) (GtkTextView *text_view);</span>
<span id="cb10-104"><a href="#cb10-104"></a> <span class="dt">void</span> (* snapshot_layer) (GtkTextView *text_view,</span>
<span id="cb10-105"><a href="#cb10-105"></a> GtkTextViewLayer layer,</span>
<span id="cb10-106"><a href="#cb10-106"></a> GtkSnapshot *snapshot);</span>
<span id="cb10-107"><a href="#cb10-107"></a> gboolean (* extend_selection) (GtkTextView *text_view,</span>
<span id="cb10-108"><a href="#cb10-108"></a> GtkTextExtendSelection granularity,</span>
<span id="cb10-109"><a href="#cb10-109"></a> <span class="dt">const</span> GtkTextIter *location,</span>
<span id="cb10-110"><a href="#cb10-110"></a> GtkTextIter *start,</span>
<span id="cb10-111"><a href="#cb10-111"></a> GtkTextIter *end);</span>
<span id="cb10-112"><a href="#cb10-112"></a> <span class="dt">void</span> (* insert_emoji) (GtkTextView *text_view);</span>
<span id="cb10-113"><a href="#cb10-113"></a></span>
<span id="cb10-114"><a href="#cb10-114"></a> <span class="co">/*&lt; private &gt;*/</span></span>
<span id="cb10-115"><a href="#cb10-115"></a></span>
<span id="cb10-116"><a href="#cb10-116"></a> gpointer padding[<span class="dv">8</span>];</span>
<span id="cb10-117"><a href="#cb10-117"></a>};</span>
<span id="cb10-118"><a href="#cb10-118"></a></span>
<span id="cb10-119"><a href="#cb10-119"></a><span class="co">/* The following definition is generated by the macro G_DECLARE_FINAL_TYPE */</span></span>
<span id="cb10-120"><a href="#cb10-120"></a><span class="kw">typedef</span> <span class="kw">struct</span> {</span>
<span id="cb10-121"><a href="#cb10-121"></a> GtkTextView parent_class;</span>
<span id="cb10-122"><a href="#cb10-122"></a>} TfeTextViewClass;</span></code></pre></div>
<ul>
<li>120-122: This three lines are generated by the macro <code>G_DECLARE_FINAL_TYPE</code>. So, they are not written in either <code>tfe_text_view.h</code> or <code>tfe_text_view.c</code>.</li>
<li>3, 84, 121: Each derived class puts its parent class at the first member of its structure. It is the same as instance structures.</li>
<li>Class members in ancestors are open to the descendant class. So, they can be changed in <code>tfe_text_view_class_init</code> function. For example, the <code>dispose</code> pointer in GObjectClass will be overridden later in <code>tfe_text_view_class_init</code>. (Override is an object oriented programming terminology. Override is rewriting ancestors class methods in the descendant class.)</li>
<li>Some class methods are often overridden. <code>set_property</code>, <code>get_property</code>, <code>dispose</code>, <code>finalize</code> and <code>constructed</code> are such methods.</li>
</ul>
<p>TfeTextViewClass includes its ancestors class in it. It is illustrated in the following diagram.</p>
<figure>
<img src="image/TfeTextViewClass.png" alt="" /><figcaption>The structure of TfeTextView Class</figcaption>
</figure>
<h2 id="destruction-of-tfetextview">Destruction of TfeTextView</h2>
<p>Every Object derived from GObject has a reference count. If an object A refers to an object B, then A keeps a pointer to B in A and at the same time increases the reference count of B by one with the function <code>g_object_ref (B)</code>. If A doesnt need B any longer, then A discards the pointer to B (usually it is done by assigning NULL to the pointer) and decreases the reference count of B by one with the function <code>g_object_unref (B)</code>.</p>
<p>If two objects A and B refer to C, then the reference count of C is two. If A no longer needs C, A discards the pointer to C and decreases the reference count in C by one. Now the reference count of C is one. In the same way, if B no longer needs C, B discards the pointer to C and decreases the reference count in C by one. At this moment, no object refers to C and the reference count of C is zero. This means C is no longer useful. Then C destructs itself and finally the memories allocated to C is freed.</p>
<figure>
<img src="image/refcount.png" alt="" /><figcaption>Reference count of B</figcaption>
</figure>
<p>The idea above is based on an assumption that an object referred by nothing has reference count of zero. When the reference count drops to zero, the object starts its destruction process. The destruction process is split into two phases: disposing and finalizing. In the disposing process, the object invokes the function pointed by <code>dispose</code> in its class to release all references to other objects. In the finalizing process, it invokes the function pointed by <code>finalize</code> in its class to complete the destruction process. These functions are also called handlers or methods. For example, dispose handler or dispose method.</p>
<p>In the destruction process of TfeTextView, the reference count of widgets related to TfeTextView is automatically decreased. But GFile pointed by <code>tv-&gt;file</code> needs to decrease its reference count by one. You must write the code in the dispose handler <code>tfe_text_view_dispose</code>.</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb11-1"><a href="#cb11-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb11-2"><a href="#cb11-2"></a>tfe_text_view_dispose (GObject *gobject) {</span>
<span id="cb11-3"><a href="#cb11-3"></a> TfeTextView *tv = TFE_TEXT_VIEW (gobject);</span>
<span id="cb11-4"><a href="#cb11-4"></a></span>
<span id="cb11-5"><a href="#cb11-5"></a> <span class="cf">if</span> (G_IS_FILE (tv-&gt;file))</span>
<span id="cb11-6"><a href="#cb11-6"></a> g_clear_object (&amp;tv-&gt;file);</span>
<span id="cb11-7"><a href="#cb11-7"></a></span>
<span id="cb11-8"><a href="#cb11-8"></a> G_OBJECT_CLASS (tfe_text_view_parent_class)-&gt;dispose (gobject);</span>
<span id="cb11-9"><a href="#cb11-9"></a>}</span></code></pre></div>
<ul>
<li>5,6: If <code>tv-&gt;file</code> points a GFile, decrease its reference count. <code>g_clear_object</code> decreases the reference count and assigns NULL to <code>tv-&gt;file</code>. In dispose handlers, we usually use <code>g_clear_object</code> rather than <code>g_object_unref</code>.</li>
<li>8: invokes parents dispose handler. (This will be explained later.)</li>
</ul>
<p>In the disposing process, the object uses the pointer in its class to call the handler. Therefore, <code>tfe_text_view_dispose</code> needs to be registered in the class when the TfeTextView class is initialized. The function <code>tfe_text_view_class_init</code> is the class initialization function and it is declared in the replacement produced by <code>G_DEFINE_TYPE</code> macro.</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true"></a>tfe_text_view_class_init (TfeTextViewClass *class) {</span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true"></a> GObjectClass *object_class = G_OBJECT_CLASS (class);</span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true"></a></span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true"></a> object_class-&gt;dispose = tfe_text_view_dispose;</span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true"></a></span>
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true"></a>}</span></code></pre></div>
<p>Each ancestors class has been created before TfeTextViewClass is created. Therefore, there are four classes and each class has a pointer to each dispose handler. Look at the following diagram. There are four classes GObjectClass (GInitiallyUnownedClass), GtkWidgetClass, GtkTextViewClass and TfeTextViewClass. Each class has its own dispose handler <code>dh1</code>, <code>dh2</code>, <code>dh3</code> and <code>tfe_text_view_dispose</code>.</p>
<figure>
<img src="image/dispose_handler.png" alt="" /><figcaption>dispose handlers</figcaption>
</figure>
<p>Now, look at the <code>tfe_text_view_dispose</code> program above. It first releases the reference to GFile object pointed by <code>tv-&gt;file</code>. Then it invokes its parents dispose handler in line 8.</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true"></a>G_OBJECT_CLASS (tfe_text_view_parent_class)-&gt;dispose (gobject);</span></code></pre></div>
<p><code>tfe_text_view_parent_class</code>,which is made by <code>G_DEFINE_TYPE</code> macro, is a pointer that points the parent object class. Therefore, <code>G_OBJECT_CLASS (tfe_text_view_parent_class)-&gt;dispose</code> points the handler <code>dh3</code> in the diagram above. And <code>gobject</code> is a pointer to TfeTextView instance which is casted as a GObject instance. <code>dh3</code> releases all the references to objects in the GtkTextView part (it is actually the private area pointed by <code>prev</code>) in TfeTextView instance. After that, <code>dh3</code> calls <code>dh2</code>, and <code>dh2</code> calls <code>dh1</code>. Finally all the references are released.</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>
</html>