Update section 1-16. Some bugs are fixed.

This commit is contained in:
Toshio Sekiya 2022-12-21 22:31:33 +09:00
parent afc7a8517d
commit fe3fba623c
87 changed files with 6244 additions and 6750 deletions

View file

@ -13,40 +13,43 @@ The table of contents is at the end of this abstract.
- Section 26 to 29 describes the list model and the list view (GtkListView, GtkGridView and GtkColumnView). - Section 26 to 29 describes the list model and the list view (GtkListView, GtkGridView and GtkColumnView).
It also describes GtkExpression. It also describes GtkExpression.
The latest version of the tutorial is located at [Gtk4-tutorial GitHub repository](https://github.com/ToshioCP/Gtk4-tutorial). The latest version of the tutorial is located at [GTK4-tutorial GitHub repository](https://github.com/ToshioCP/GTK4-tutorial).
You can read it from there directly without having to download anything. You can read it directly without download.
There's a GitHub Page of this tutorial at [`https://toshiocp.github.io/GTK4-tutorial/`](https://toshiocp.github.io/GTK4-tutorial/).
It is easier to read than the repository.
#### GTK 4 Documentation #### GTK 4 Documentation
Please refer to [GTK API Reference](https://docs.gtk.org/gtk4/index.html) Please refer to [GTK 4 API Reference](https://docs.gtk.org/gtk4/index.html)
and [GNOME Developer Documentation Website](https://developer.gnome.org/) for further information. and [GNOME Developer Documentation Website](https://developer.gnome.org/) for further information.
These websites are newly opened lately (Aug/2021). These websites were opened in August of 2021.
The old documentation is located at [GTK Reference Manual](https://developer-old.gnome.org/gtk4/stable/) and [GNOME Developer Center](https://developer-old.gnome.org/). The old documents are located at [GTK Reference Manual](https://developer-old.gnome.org/gtk4/stable/) and [GNOME Developer Center](https://developer-old.gnome.org/).
The new website is in progress at present, so you might need to refer to the old version.
If you want to know about GObject and the type system, please refer to [GObject tutorial](https://github.com/ToshioCP/Gobject-tutorial). If you want to know about GObject and the type system, please refer to [GObject tutorial](https://github.com/ToshioCP/Gobject-tutorial).
The GObject details are easy to understand and also necessary to know when writing GTK 4 programs. GObject is the base of GTK 4, so it is important for developers to understand GObject for writing GTK 4 programs.
#### Contribution #### Contribution
This tutorial is under development and unstable. This tutorial is still under development and unstable.
Even though the codes of the examples have been tested on GTK 4 (version 4.0), bugs may still exist. Even though the codes of the examples have been tested on GTK 4 (version 4.8.1), bugs may still exist.
If you find any bugs, errors or mistakes in the tutorial and C examples, please let me know. If you find any bugs, errors or mistakes in the tutorial and C examples, please let me know.
You can post it to [GitHub issues](https://github.com/ToshioCP/Gtk4-tutorial/issues). You can post it to [GitHub issues](https://github.com/ToshioCP/GTK4-tutorial/issues).
You can also post corrected files as a commit to [pull request](https://github.com/ToshioCP/Gtk4-tutorial/pulls). You can also post updated files to [pull request](https://github.com/ToshioCP/GTK4-tutorial/pulls).
When you make corrections, correct the source files, which are under the 'src' directory, One thing you need to be careful is to correct the source files, which are under the 'src' directory.
then run `rake` to create to create the output file. The GFM files under the 'gfm' directory are automatically updated. Don't modify the files under `gfm` or `html` directories.
After modifying some source files (under `src` directory), run `rake` to create GFM (GitHub Flavoured Markdown) files or run `rake html` to create HTML files.
If you have a question, feel free to post it as an issue. If you have a question, feel free to post it to `issue`.
All questions are helpful and will make this tutorial get better. All questions are helpful and will make this tutorial get better.
#### How to get a HTML or PDF version #### How to get Gtk 4 tutorial with HTML or PDF format
If you want to get a HTML or PDF version, you can make them with `rake`, which is a ruby version of make. If you want to get HTML or PDF format tutorial, make them with `rake` command, which is a ruby version of make.
Type `rake html` for HTML. Type `rake html` for HTML.
Type `rake pdf` for PDF. Type `rake pdf` for PDF.
There is a documentation \("[How to build GTK 4 Tutorial](gfm/Readme_for_developers.md)"\) that describes how to make them. There is a document \("[How to build GTK 4 Tutorial](gfm/Readme_for_developers.md)"\) for further information.
## Table of contents ## Table of contents
@ -55,17 +58,17 @@ There is a documentation \("[How to build GTK 4 Tutorial](gfm/Readme_for_develop
1. [GtkApplication and GtkApplicationWindow](gfm/sec3.md) 1. [GtkApplication and GtkApplicationWindow](gfm/sec3.md)
1. [Widgets (1)](gfm/sec4.md) 1. [Widgets (1)](gfm/sec4.md)
1. [Widgets (2)](gfm/sec5.md) 1. [Widgets (2)](gfm/sec5.md)
1. [String and memory management](gfm/sec6.md) 1. [Strings and memory management](gfm/sec6.md)
1. [Widgets (3)](gfm/sec7.md) 1. [Widgets (3)](gfm/sec7.md)
1. [Defining a child object](gfm/sec8.md) 1. [Defining a final class](gfm/sec8.md)
1. [The User Interface (UI) file and GtkBuilder](gfm/sec9.md) 1. [GtkBuilder and UI file](gfm/sec9.md)
1. [Build system](gfm/sec10.md) 1. [Build system](gfm/sec10.md)
1. [Initialization and destruction of instances](gfm/sec11.md) 1. [Instance Initialization and destruction](gfm/sec11.md)
1. [Signals](gfm/sec12.md) 1. [Signals](gfm/sec12.md)
1. [Functions in TfeTextView](gfm/sec13.md) 1. [Functions in TfeTextView](gfm/sec13.md)
1. [Functions in GtkNotebook](gfm/sec14.md) 1. [Functions in GtkNotebook](gfm/sec14.md)
1. [tfeapplication.c](gfm/sec15.md) 1. [tfeapplication.c](gfm/sec15.md)
1. [tfe5 source files](gfm/sec16.md) 1. [How to build tfe (text file editor)](gfm/sec16.md)
1. [Menu and action](gfm/sec17.md) 1. [Menu and action](gfm/sec17.md)
1. [Stateful action](gfm/sec18.md) 1. [Stateful action](gfm/sec18.md)
1. [Ui file for menu and action entries](gfm/sec19.md) 1. [Ui file for menu and action entries](gfm/sec19.md)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View file

@ -116,44 +116,46 @@ editor <code>tfe</code> (Text File Editor).</li>
GtkExpression.</li> GtkExpression.</li>
</ul> </ul>
<p>The latest version of the tutorial is located at <a <p>The latest version of the tutorial is located at <a
href="https://github.com/ToshioCP/Gtk4-tutorial">Gtk4-tutorial GitHub href="https://github.com/ToshioCP/GTK4-tutorial">GTK4-tutorial GitHub
repository</a>. You can read it from there directly without having to repository</a>. You can read it directly without download.</p>
download anything.</p> <p>Theres a GitHub Page of this tutorial at <a
href="https://toshiocp.github.io/GTK4-tutorial/"><code>https://toshiocp.github.io/GTK4-tutorial/</code></a>.
It is easier to read than the repository.</p>
<h4 id="gtk-4-documentation">GTK 4 Documentation</h4> <h4 id="gtk-4-documentation">GTK 4 Documentation</h4>
<p>Please refer to <a href="https://docs.gtk.org/gtk4/index.html">GTK <p>Please refer to <a href="https://docs.gtk.org/gtk4/index.html">GTK 4
API Reference</a> and <a href="https://developer.gnome.org/">GNOME API Reference</a> and <a href="https://developer.gnome.org/">GNOME
Developer Documentation Website</a> for further information.</p> Developer Documentation Website</a> for further information.</p>
<p>These websites are newly opened lately (Aug/2021). The old <p>These websites were opened in August of 2021. The old documents are
documentation is located at <a located at <a href="https://developer-old.gnome.org/gtk4/stable/">GTK
href="https://developer-old.gnome.org/gtk4/stable/">GTK Reference Reference Manual</a> and <a
Manual</a> and <a href="https://developer-old.gnome.org/">GNOME href="https://developer-old.gnome.org/">GNOME Developer Center</a>.</p>
Developer Center</a>. The new website is in progress at present, so you
might need to refer to the old version.</p>
<p>If you want to know about GObject and the type system, please refer <p>If you want to know about GObject and the type system, please refer
to <a href="https://github.com/ToshioCP/Gobject-tutorial">GObject to <a href="https://github.com/ToshioCP/Gobject-tutorial">GObject
tutorial</a>. The GObject details are easy to understand and also tutorial</a>. GObject is the base of GTK 4, so it is important for
necessary to know when writing GTK 4 programs.</p> developers to understand GObject for writing GTK 4 programs.</p>
<h4 id="contribution">Contribution</h4> <h4 id="contribution">Contribution</h4>
<p>This tutorial is under development and unstable. Even though the <p>This tutorial is still under development and unstable. Even though
codes of the examples have been tested on GTK 4 (version 4.0), bugs may the codes of the examples have been tested on GTK 4 (version 4.8.1),
still exist. If you find any bugs, errors or mistakes in the tutorial bugs may still exist. If you find any bugs, errors or mistakes in the
and C examples, please let me know. You can post it to <a tutorial and C examples, please let me know. You can post it to <a
href="https://github.com/ToshioCP/Gtk4-tutorial/issues">GitHub href="https://github.com/ToshioCP/GTK4-tutorial/issues">GitHub
issues</a>. You can also post corrected files as a commit to <a issues</a>. You can also post updated files to <a
href="https://github.com/ToshioCP/Gtk4-tutorial/pulls">pull request</a>. href="https://github.com/ToshioCP/GTK4-tutorial/pulls">pull request</a>.
When you make corrections, correct the source files, which are under the One thing you need to be careful is to correct the source files, which
src directory, then run <code>rake</code> to create to create the are under the src directory. Dont modify the files under
output file. The GFM files under the gfm directory are automatically <code>gfm</code> or <code>html</code> directories. After modifying some
updated.</p> source files (under <code>src</code> directory), run <code>rake</code>
<p>If you have a question, feel free to post it as an issue. All to create GFM (GitHub Flavoured Markdown) files or run
questions are helpful and will make this tutorial get better.</p> <code>rake html</code> to create HTML files.</p>
<h4 id="how-to-get-a-html-or-pdf-version">How to get a HTML or PDF <p>If you have a question, feel free to post it to <code>issue</code>.
version</h4> All questions are helpful and will make this tutorial get better.</p>
<p>If you want to get a HTML or PDF version, you can make them with <h4 id="how-to-get-gtk-4-tutorial-with-html-or-pdf-format">How to get
<code>rake</code>, which is a ruby version of make. Type Gtk 4 tutorial with HTML or PDF format</h4>
<p>If you want to get HTML or PDF format tutorial, make them with
<code>rake</code> command, which is a ruby version of make. Type
<code>rake html</code> for HTML. Type <code>rake pdf</code> for PDF. <code>rake html</code> for HTML. Type <code>rake pdf</code> for PDF.
There is a documentation (“<a href="Readme_for_developers.html">How to There is a document (“<a href="Readme_for_developers.html">How to build
build GTK 4 Tutorial</a>”) that describes how to make them.</p> GTK 4 Tutorial</a>”) for further information.</p>
<h2 id="table-of-contents">Table of contents</h2> <h2 id="table-of-contents">Table of contents</h2>
<ol type="1"> <ol type="1">
<li><a href="sec1.html">Prerequisite and License</a></li> <li><a href="sec1.html">Prerequisite and License</a></li>
@ -162,19 +164,18 @@ distributions</a></li>
<li><a href="sec3.html">GtkApplication and GtkApplicationWindow</a></li> <li><a href="sec3.html">GtkApplication and GtkApplicationWindow</a></li>
<li><a href="sec4.html">Widgets (1)</a></li> <li><a href="sec4.html">Widgets (1)</a></li>
<li><a href="sec5.html">Widgets (2)</a></li> <li><a href="sec5.html">Widgets (2)</a></li>
<li><a href="sec6.html">String and memory management</a></li> <li><a href="sec6.html">Strings and memory management</a></li>
<li><a href="sec7.html">Widgets (3)</a></li> <li><a href="sec7.html">Widgets (3)</a></li>
<li><a href="sec8.html">Defining a child object</a></li> <li><a href="sec8.html">Defining a final class</a></li>
<li><a href="sec9.html">The User Interface (UI) file and <li><a href="sec9.html">GtkBuilder and UI file</a></li>
GtkBuilder</a></li>
<li><a href="sec10.html">Build system</a></li> <li><a href="sec10.html">Build system</a></li>
<li><a href="sec11.html">Initialization and destruction of <li><a href="sec11.html">Instance Initialization and
instances</a></li> destruction</a></li>
<li><a href="sec12.html">Signals</a></li> <li><a href="sec12.html">Signals</a></li>
<li><a href="sec13.html">Functions in TfeTextView</a></li> <li><a href="sec13.html">Functions in TfeTextView</a></li>
<li><a href="sec14.html">Functions in GtkNotebook</a></li> <li><a href="sec14.html">Functions in GtkNotebook</a></li>
<li><a href="sec15.html">tfeapplication.c</a></li> <li><a href="sec15.html">tfeapplication.c</a></li>
<li><a href="sec16.html">tfe5 source files</a></li> <li><a href="sec16.html">How to build tfe (text file editor)</a></li>
<li><a href="sec17.html">Menu and action</a></li> <li><a href="sec17.html">Menu and action</a></li>
<li><a href="sec18.html">Stateful action</a></li> <li><a href="sec18.html">Stateful action</a></li>
<li><a href="sec19.html">Ui file for menu and action entries</a></li> <li><a href="sec19.html">Ui file for menu and action entries</a></li>

View file

@ -117,36 +117,37 @@ MacOS, with Vala, Python and so on. However, this tutorial describes
only <em>C programs on Linux</em>.</p> only <em>C programs on Linux</em>.</p>
<p>If you want to try the examples in the tutorial, you need:</p> <p>If you want to try the examples in the tutorial, you need:</p>
<ul> <ul>
<li>PC with Linux distribution like Ubuntu, Debian and so on.</li> <li>PC with Linux distribution like Ubuntu or Debian.</li>
<li>Gcc.</li> <li>GCC.</li>
<li>GTK 4. The stable version of Gtk on Linux distributions is version <li>GTK 4. The stable version of GTK is 4.8.2 at present (13/Dec/2022),
three at present. You need to install GTK 4 to your computer. See <a but older version (4.0 or higher) may work. See <a
href="sec3.html">Section 3</a> for the installation of GTK 4.</li> href="sec3.html">Section 3</a> for the installation for GTK 4.</li>
</ul> </ul>
<h3 id="ruby-and-rake-for-making-the-document">Ruby and rake for making <h3 id="ruby-and-rake-for-making-the-document">Ruby and rake for making
the document</h3> the document</h3>
<p>This repository includes Ruby programs. They are used to make <p>This repository includes Ruby programs. They are used to make GFM
Markdown files, HTML files, Latex files and a PDF file.</p> (GitHub Flavoured Markdown) files, HTML files, Latex files and a PDF
file.</p>
<p>You need:</p> <p>You need:</p>
<ul> <ul>
<li>Linux distribution like Ubuntu.</li> <li>Linux.</li>
<li>Ruby programming language. There are two ways to install it. One is <li>Ruby programming language. There are two ways to install it. One is
installing the distributions package. The other is using rbenv and installing the distributions package. The other is using rbenv and
ruby-build. If you want to use the latest version of ruby, use ruby-build. If you want to use the latest version of ruby, use
rbenv.</li> rbenv.</li>
<li>Rake. It is a gem, which is a library written in Ruby. You can <li>Rake. You dont need to install it separately because it is a
install it as a package of your distribution or use gem command.</li> standard library of Ruby.</li>
</ul> </ul>
<h2 id="license">License</h2> <h2 id="license">License</h2>
<p>Copyright (C) 2020 ToshioCP (Toshio Sekiya)</p> <p>Copyright (C) 2020-2022 ToshioCP (Toshio Sekiya)</p>
<p>Gtk4-tutorial repository contains the tutorial document and software <p>GTK4-tutorial repository contains tutorial documents and programs
such as converters, generators and controllers. All of them make up the such as converters, generators and controllers. All of them make up the
Gtk4-tutorial package. This package is simply called Gtk4-tutorial GTK4-tutorial package. This package is simply called GTK4-tutorial
in the following description. Gtk4-tutorial is free; you can in the following description. GTK4-tutorial is free; you can
redistribute it and/or modify it under the terms of the GNU General redistribute it and/or modify it under the terms of the GNU General
Public License as published by the Free Software Foundation; either Public License as published by the Free Software Foundation; either
version 3 of the License or, at your option, any later version.</p> version 3 of the License or, at your option, any later version.</p>
<p>Gtk4-tutorial is distributed in the hope that it will be useful, <p>GTK4-tutorial is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the <a MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the <a
href="https://www.gnu.org/licenses/gpl-3.0.html">GNU General Public href="https://www.gnu.org/licenses/gpl-3.0.html">GNU General Public

View file

@ -5,7 +5,7 @@
<meta name="generator" content="pandoc" /> <meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <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"> <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> <title>GTK 4 tutorial</title>
<style> <style>
code{white-space: pre-wrap;} code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;} span.smallcaps{font-variant: small-caps;}
@ -112,275 +112,373 @@
</div> </div>
</nav> </nav>
<h1 id="build-system">Build system</h1> <h1 id="build-system">Build system</h1>
<h2 id="what-do-we-need-to-think-about-to-manage-big-source-files">What do we need to think about to manage big source files?</h2> <h2 id="managing-big-source-files">Managing big source files</h2>
<p>Weve compiled a small editor so far. But Some bad signs are beginning to appear.</p> <p>Weve compiled a small editor so far. But Some bad signs are
beginning to appear.</p>
<ul> <ul>
<li>Weve had only one C source file and put everything into it. We need to sort it out.</li> <li>Weve had only one C source file and put everything in it. We need
<li>There are two compilers, <code>gcc</code> and <code>glib-compile-resources</code>. We should control them by one building tool.</li> to sort it out.</li>
<li>There are two compilers, <code>gcc</code> and
<code>glib-compile-resources</code>. We should control them by one
building tool.</li>
</ul> </ul>
<p>These ideas are useful to manage big source files.</p> <p>These ideas are useful to manage big source files.</p>
<h2 id="divide-a-c-source-file-into-two-parts.">Divide a C source file into two parts.</h2> <h2 id="divide-a-c-source-file-into-two-parts.">Divide a C source file
<p>When you divide C source file into several parts, each file should contain only one thing. For example, our source has two things, the definition of TfeTextView and functions related to GtkApplication and GtkApplicationWindow. It is a good idea to separate them into two files, <code>tfetextview.c</code> and <code>tfe.c</code>.</p> into two parts.</h2>
<p>When you divide C source file into several parts, each file should
contain one thing. For example, our source has two things, the
definition of TfeTextView and functions related to GtkApplication and
GtkApplicationWindow. It is a good idea to separate them into two files,
<code>tfetextview.c</code> and <code>tfe.c</code>.</p>
<ul> <ul>
<li><code>tfetextview.c</code> includes the definition and functions of TfeTextView.</li> <li><code>tfetextview.c</code> includes the definition and functions of
<li><code>tfe.c</code> includes functions like <code>main</code>, <code>app_activate</code>, <code>app_open</code> and so on, which relate to GtkApplication and GtkApplicationWindow</li> TfeTextView.</li>
<li><code>tfe.c</code> includes functions like <code>main</code>,
<code>app_activate</code>, <code>app_open</code> and so on, which relate
to GtkApplication and GtkApplicationWindow</li>
</ul> </ul>
<p>Now we have three source files, <code>tfetextview.c</code>, <code>tfe.c</code> and <code>tfe3.ui</code>. The <code>3</code> of <code>tfe3.ui</code> is like a version number. Managing version with filenames is one possible idea but it may make bothersome problem. You need to rewrite filename in each version and it affects to contents of source files that refer to filenames. So, we should take <code>3</code> away from the filename.</p> <p>Now we have three source files, <code>tfetextview.c</code>,
<p>In <code>tfe.c</code> the function <code>tfe_text_view_new</code> is invoked to create a TfeTextView instance. But it is defined in <code>tfetextview.c</code>, not <code>tfe.c</code>. The lack of the declaration (not definition) of <code>tfe_text_view_new</code> makes error when <code>tfe.c</code> is compiled. The declaration is necessary in <code>tfe.c</code>. Those public information is usually written in header files. It has <code>.h</code> suffix like <code>tfetextview.h</code> And header files are included by C source files. For example, <code>tfetextview.h</code> is included by <code>tfe.c</code>.</p> <code>tfe.c</code> and <code>tfe3.ui</code>. The <code>3</code> of
<code>tfe3.ui</code> is like a version number. Managing version with
filenames is one possible idea but it may make bothersome problem. You
need to rewrite filename in each version and it affects to contents of
source files that refer to filenames. So, we should take <code>3</code>
away from the filename.</p>
<p>In <code>tfe.c</code> the function <code>tfe_text_view_new</code> is
invoked to create a TfeTextView instance. But it is defined in
<code>tfetextview.c</code>, not <code>tfe.c</code>. The lack of the
declaration (not definition) of <code>tfe_text_view_new</code> makes
error when <code>tfe.c</code> is compiled. The declaration is necessary
in <code>tfe.c</code>. Those public information is usually written in
header files. It has <code>.h</code> suffix like
<code>tfetextview.h</code> And header files are included by C source
files. For example, <code>tfetextview.h</code> is included by
<code>tfe.c</code>.</p>
<p>All the source files are listed below.</p> <p>All the source files are listed below.</p>
<p><code>tfetextview.h</code></p> <p><code>tfetextview.h</code></p>
<div class="sourceCode" id="cb1"><pre class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1"></a><span class="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span> <div class="sourceCode" id="cb1"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1"></a><span class="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span>
<span id="cb1-2"><a href="#cb1-2"></a></span> <span id="cb1-2"><a href="#cb1-2"></a></span>
<span id="cb1-3"><a href="#cb1-3"></a><span class="pp">#define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type ()</span></span> <span id="cb1-3"><a href="#cb1-3"></a><span class="pp">#define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type ()</span></span>
<span id="cb1-4"><a href="#cb1-4"></a>G_DECLARE_FINAL_TYPE (TfeTextView, tfe_text_view, TFE, TEXT_VIEW, GtkTextView)</span> <span id="cb1-4"><a href="#cb1-4"></a>G_DECLARE_FINAL_TYPE <span class="op">(</span>TfeTextView<span class="op">,</span> tfe_text_view<span class="op">,</span> TFE<span class="op">,</span> TEXT_VIEW<span class="op">,</span> GtkTextView<span class="op">)</span></span>
<span id="cb1-5"><a href="#cb1-5"></a></span> <span id="cb1-5"><a href="#cb1-5"></a></span>
<span id="cb1-6"><a href="#cb1-6"></a><span class="dt">void</span></span> <span id="cb1-6"><a href="#cb1-6"></a><span class="dt">void</span></span>
<span id="cb1-7"><a href="#cb1-7"></a>tfe_text_view_set_file (TfeTextView *tv, GFile *f);</span> <span id="cb1-7"><a href="#cb1-7"></a>tfe_text_view_set_file <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">,</span> GFile <span class="op">*</span>f<span class="op">);</span></span>
<span id="cb1-8"><a href="#cb1-8"></a></span> <span id="cb1-8"><a href="#cb1-8"></a></span>
<span id="cb1-9"><a href="#cb1-9"></a>GFile *</span> <span id="cb1-9"><a href="#cb1-9"></a>GFile <span class="op">*</span></span>
<span id="cb1-10"><a href="#cb1-10"></a>tfe_text_view_get_file (TfeTextView *tv);</span> <span id="cb1-10"><a href="#cb1-10"></a>tfe_text_view_get_file <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">);</span></span>
<span id="cb1-11"><a href="#cb1-11"></a></span> <span id="cb1-11"><a href="#cb1-11"></a></span>
<span id="cb1-12"><a href="#cb1-12"></a>GtkWidget *</span> <span id="cb1-12"><a href="#cb1-12"></a>GtkWidget <span class="op">*</span></span>
<span id="cb1-13"><a href="#cb1-13"></a>tfe_text_view_new (<span class="dt">void</span>);</span></code></pre></div> <span id="cb1-13"><a href="#cb1-13"></a>tfe_text_view_new <span class="op">(</span><span class="dt">void</span><span class="op">);</span></span></code></pre></div>
<p><code>tfetextview.c</code></p> <p><code>tfetextview.c</code></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="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span> <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="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span>
<span id="cb2-2"><a href="#cb2-2"></a><span class="pp">#include </span><span class="im">&quot;tfetextview.h&quot;</span></span> <span id="cb2-2"><a href="#cb2-2"></a><span class="pp">#include </span><span class="im">&quot;tfetextview.h&quot;</span></span>
<span id="cb2-3"><a href="#cb2-3"></a></span> <span id="cb2-3"><a href="#cb2-3"></a></span>
<span id="cb2-4"><a href="#cb2-4"></a><span class="kw">struct</span> _TfeTextView</span> <span id="cb2-4"><a href="#cb2-4"></a><span class="kw">struct</span> _TfeTextView</span>
<span id="cb2-5"><a href="#cb2-5"></a>{</span> <span id="cb2-5"><a href="#cb2-5"></a><span class="op">{</span></span>
<span id="cb2-6"><a href="#cb2-6"></a> GtkTextView parent;</span> <span id="cb2-6"><a href="#cb2-6"></a> GtkTextView parent<span class="op">;</span></span>
<span id="cb2-7"><a href="#cb2-7"></a> GFile *file;</span> <span id="cb2-7"><a href="#cb2-7"></a> GFile <span class="op">*</span>file<span class="op">;</span></span>
<span id="cb2-8"><a href="#cb2-8"></a>};</span> <span id="cb2-8"><a href="#cb2-8"></a><span class="op">};</span></span>
<span id="cb2-9"><a href="#cb2-9"></a></span> <span id="cb2-9"><a href="#cb2-9"></a></span>
<span id="cb2-10"><a href="#cb2-10"></a>G_DEFINE_TYPE (TfeTextView, tfe_text_view, GTK_TYPE_TEXT_VIEW);</span> <span id="cb2-10"><a href="#cb2-10"></a>G_DEFINE_TYPE <span class="op">(</span>TfeTextView<span class="op">,</span> tfe_text_view<span class="op">,</span> GTK_TYPE_TEXT_VIEW<span class="op">);</span></span>
<span id="cb2-11"><a href="#cb2-11"></a></span> <span id="cb2-11"><a href="#cb2-11"></a></span>
<span id="cb2-12"><a href="#cb2-12"></a><span class="dt">static</span> <span class="dt">void</span></span> <span id="cb2-12"><a href="#cb2-12"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb2-13"><a href="#cb2-13"></a>tfe_text_view_init (TfeTextView *tv) {</span> <span id="cb2-13"><a href="#cb2-13"></a>tfe_text_view_init <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">)</span> <span class="op">{</span></span>
<span id="cb2-14"><a href="#cb2-14"></a>}</span> <span id="cb2-14"><a href="#cb2-14"></a><span class="op">}</span></span>
<span id="cb2-15"><a href="#cb2-15"></a></span> <span id="cb2-15"><a href="#cb2-15"></a></span>
<span id="cb2-16"><a href="#cb2-16"></a><span class="dt">static</span> <span class="dt">void</span></span> <span id="cb2-16"><a href="#cb2-16"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb2-17"><a href="#cb2-17"></a>tfe_text_view_class_init (TfeTextViewClass *class) {</span> <span id="cb2-17"><a href="#cb2-17"></a>tfe_text_view_class_init <span class="op">(</span>TfeTextViewClass <span class="op">*</span>class<span class="op">)</span> <span class="op">{</span></span>
<span id="cb2-18"><a href="#cb2-18"></a>}</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-19"><a href="#cb2-19"></a></span>
<span id="cb2-20"><a href="#cb2-20"></a><span class="dt">void</span></span> <span id="cb2-20"><a href="#cb2-20"></a><span class="dt">void</span></span>
<span id="cb2-21"><a href="#cb2-21"></a>tfe_text_view_set_file (TfeTextView *tv, GFile *f) {</span> <span id="cb2-21"><a href="#cb2-21"></a>tfe_text_view_set_file <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">,</span> GFile <span class="op">*</span>f<span class="op">)</span> <span class="op">{</span></span>
<span id="cb2-22"><a href="#cb2-22"></a> tv -&gt; file = f;</span> <span id="cb2-22"><a href="#cb2-22"></a> tv <span class="op">-&gt;</span> file <span class="op">=</span> f<span class="op">;</span></span>
<span id="cb2-23"><a href="#cb2-23"></a>}</span> <span id="cb2-23"><a href="#cb2-23"></a><span class="op">}</span></span>
<span id="cb2-24"><a href="#cb2-24"></a></span> <span id="cb2-24"><a href="#cb2-24"></a></span>
<span id="cb2-25"><a href="#cb2-25"></a>GFile *</span> <span id="cb2-25"><a href="#cb2-25"></a>GFile <span class="op">*</span></span>
<span id="cb2-26"><a href="#cb2-26"></a>tfe_text_view_get_file (TfeTextView *tv) {</span> <span id="cb2-26"><a href="#cb2-26"></a>tfe_text_view_get_file <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">)</span> <span class="op">{</span></span>
<span id="cb2-27"><a href="#cb2-27"></a> <span class="cf">return</span> tv -&gt; file;</span> <span id="cb2-27"><a href="#cb2-27"></a> <span class="cf">return</span> tv <span class="op">-&gt;</span> file<span class="op">;</span></span>
<span id="cb2-28"><a href="#cb2-28"></a>}</span> <span id="cb2-28"><a href="#cb2-28"></a><span class="op">}</span></span>
<span id="cb2-29"><a href="#cb2-29"></a></span> <span id="cb2-29"><a href="#cb2-29"></a></span>
<span id="cb2-30"><a href="#cb2-30"></a>GtkWidget *</span> <span id="cb2-30"><a href="#cb2-30"></a>GtkWidget <span class="op">*</span></span>
<span id="cb2-31"><a href="#cb2-31"></a>tfe_text_view_new (<span class="dt">void</span>) {</span> <span id="cb2-31"><a href="#cb2-31"></a>tfe_text_view_new <span class="op">(</span><span class="dt">void</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb2-32"><a href="#cb2-32"></a> <span class="cf">return</span> GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL));</span> <span id="cb2-32"><a href="#cb2-32"></a> <span class="cf">return</span> GTK_WIDGET <span class="op">(</span>g_object_new <span class="op">(</span>TFE_TYPE_TEXT_VIEW<span class="op">,</span> NULL<span class="op">));</span></span>
<span id="cb2-33"><a href="#cb2-33"></a>}</span></code></pre></div> <span id="cb2-33"><a href="#cb2-33"></a><span class="op">}</span></span></code></pre></div>
<p><code>tfe.c</code></p> <p><code>tfe.c</code></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="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span> <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="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span>
<span id="cb3-2"><a href="#cb3-2"></a><span class="pp">#include </span><span class="im">&quot;tfetextview.h&quot;</span></span> <span id="cb3-2"><a href="#cb3-2"></a><span class="pp">#include </span><span class="im">&quot;tfetextview.h&quot;</span></span>
<span id="cb3-3"><a href="#cb3-3"></a></span> <span id="cb3-3"><a href="#cb3-3"></a></span>
<span id="cb3-4"><a href="#cb3-4"></a><span class="dt">static</span> <span class="dt">void</span></span> <span id="cb3-4"><a href="#cb3-4"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb3-5"><a href="#cb3-5"></a>app_activate (GApplication *app, gpointer user_data) {</span> <span id="cb3-5"><a href="#cb3-5"></a>app_activate <span class="op">(</span>GApplication <span class="op">*</span>app<span class="op">)</span> <span class="op">{</span></span>
<span id="cb3-6"><a href="#cb3-6"></a> g_print (<span class="st">&quot;You need a filename argument.</span><span class="sc">\n</span><span class="st">&quot;</span>);</span> <span id="cb3-6"><a href="#cb3-6"></a> g_print <span class="op">(</span><span class="st">&quot;You need a filename argument.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb3-7"><a href="#cb3-7"></a>}</span> <span id="cb3-7"><a href="#cb3-7"></a><span class="op">}</span></span>
<span id="cb3-8"><a href="#cb3-8"></a></span> <span id="cb3-8"><a href="#cb3-8"></a></span>
<span id="cb3-9"><a href="#cb3-9"></a><span class="dt">static</span> <span class="dt">void</span></span> <span id="cb3-9"><a href="#cb3-9"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb3-10"><a href="#cb3-10"></a>app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer user_data) {</span> <span id="cb3-10"><a href="#cb3-10"></a>app_open <span class="op">(</span>GApplication <span class="op">*</span>app<span class="op">,</span> GFile <span class="op">**</span> files<span class="op">,</span> gint n_files<span class="op">,</span> gchar <span class="op">*</span>hint<span class="op">)</span> <span class="op">{</span></span>
<span id="cb3-11"><a href="#cb3-11"></a> GtkWidget *win;</span> <span id="cb3-11"><a href="#cb3-11"></a> GtkWidget <span class="op">*</span>win<span class="op">;</span></span>
<span id="cb3-12"><a href="#cb3-12"></a> GtkWidget *nb;</span> <span id="cb3-12"><a href="#cb3-12"></a> GtkWidget <span class="op">*</span>nb<span class="op">;</span></span>
<span id="cb3-13"><a href="#cb3-13"></a> GtkWidget *lab;</span> <span id="cb3-13"><a href="#cb3-13"></a> GtkWidget <span class="op">*</span>lab<span class="op">;</span></span>
<span id="cb3-14"><a href="#cb3-14"></a> GtkNotebookPage *nbp;</span> <span id="cb3-14"><a href="#cb3-14"></a> GtkNotebookPage <span class="op">*</span>nbp<span class="op">;</span></span>
<span id="cb3-15"><a href="#cb3-15"></a> GtkWidget *scr;</span> <span id="cb3-15"><a href="#cb3-15"></a> GtkWidget <span class="op">*</span>scr<span class="op">;</span></span>
<span id="cb3-16"><a href="#cb3-16"></a> GtkWidget *tv;</span> <span id="cb3-16"><a href="#cb3-16"></a> GtkWidget <span class="op">*</span>tv<span class="op">;</span></span>
<span id="cb3-17"><a href="#cb3-17"></a> GtkTextBuffer *tb;</span> <span id="cb3-17"><a href="#cb3-17"></a> GtkTextBuffer <span class="op">*</span>tb<span class="op">;</span></span>
<span id="cb3-18"><a href="#cb3-18"></a> <span class="dt">char</span> *contents;</span> <span id="cb3-18"><a href="#cb3-18"></a> <span class="dt">char</span> <span class="op">*</span>contents<span class="op">;</span></span>
<span id="cb3-19"><a href="#cb3-19"></a> gsize length;</span> <span id="cb3-19"><a href="#cb3-19"></a> gsize length<span class="op">;</span></span>
<span id="cb3-20"><a href="#cb3-20"></a> <span class="dt">char</span> *filename;</span> <span id="cb3-20"><a href="#cb3-20"></a> <span class="dt">char</span> <span class="op">*</span>filename<span class="op">;</span></span>
<span id="cb3-21"><a href="#cb3-21"></a> <span class="dt">int</span> i;</span> <span id="cb3-21"><a href="#cb3-21"></a> <span class="dt">int</span> i<span class="op">;</span></span>
<span id="cb3-22"><a href="#cb3-22"></a> GtkBuilder *build;</span> <span id="cb3-22"><a href="#cb3-22"></a> GtkBuilder <span class="op">*</span>build<span class="op">;</span></span>
<span id="cb3-23"><a href="#cb3-23"></a></span> <span id="cb3-23"><a href="#cb3-23"></a></span>
<span id="cb3-24"><a href="#cb3-24"></a> build = gtk_builder_new_from_resource (<span class="st">&quot;/com/github/ToshioCP/tfe3/tfe.ui&quot;</span>);</span> <span id="cb3-24"><a href="#cb3-24"></a> build <span class="op">=</span> gtk_builder_new_from_resource <span class="op">(</span><span class="st">&quot;/com/github/ToshioCP/tfe3/tfe.ui&quot;</span><span class="op">);</span></span>
<span id="cb3-25"><a href="#cb3-25"></a> win = GTK_WIDGET (gtk_builder_get_object (build, <span class="st">&quot;win&quot;</span>));</span> <span id="cb3-25"><a href="#cb3-25"></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="cb3-26"><a href="#cb3-26"></a> gtk_window_set_application (GTK_WINDOW (win), GTK_APPLICATION (app));</span> <span id="cb3-26"><a href="#cb3-26"></a> gtk_window_set_application <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> GTK_APPLICATION <span class="op">(</span>app<span class="op">));</span></span>
<span id="cb3-27"><a href="#cb3-27"></a> nb = GTK_WIDGET (gtk_builder_get_object (build, <span class="st">&quot;nb&quot;</span>));</span> <span id="cb3-27"><a href="#cb3-27"></a> nb <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;nb&quot;</span><span class="op">));</span></span>
<span id="cb3-28"><a href="#cb3-28"></a> g_object_unref (build);</span> <span id="cb3-28"><a href="#cb3-28"></a> g_object_unref <span class="op">(</span>build<span class="op">);</span></span>
<span id="cb3-29"><a href="#cb3-29"></a> <span class="cf">for</span> (i = <span class="dv">0</span>; i &lt; n_files; i++) {</span> <span id="cb3-29"><a href="#cb3-29"></a> <span class="cf">for</span> <span class="op">(</span>i <span class="op">=</span> <span class="dv">0</span><span class="op">;</span> i <span class="op">&lt;</span> n_files<span class="op">;</span> i<span class="op">++)</span> <span class="op">{</span></span>
<span id="cb3-30"><a href="#cb3-30"></a> <span class="cf">if</span> (g_file_load_contents (files[i], NULL, &amp;contents, &amp;length, NULL, NULL)) {</span> <span id="cb3-30"><a href="#cb3-30"></a> <span class="cf">if</span> <span class="op">(</span>g_file_load_contents <span class="op">(</span>files<span class="op">[</span>i<span class="op">],</span> NULL<span class="op">,</span> <span class="op">&amp;</span>contents<span class="op">,</span> <span class="op">&amp;</span>length<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">))</span> <span class="op">{</span></span>
<span id="cb3-31"><a href="#cb3-31"></a> scr = gtk_scrolled_window_new ();</span> <span id="cb3-31"><a href="#cb3-31"></a> scr <span class="op">=</span> gtk_scrolled_window_new <span class="op">();</span></span>
<span id="cb3-32"><a href="#cb3-32"></a> tv = tfe_text_view_new ();</span> <span id="cb3-32"><a href="#cb3-32"></a> tv <span class="op">=</span> tfe_text_view_new <span class="op">();</span></span>
<span id="cb3-33"><a href="#cb3-33"></a> tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));</span> <span id="cb3-33"><a href="#cb3-33"></a> tb <span class="op">=</span> gtk_text_view_get_buffer <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
<span id="cb3-34"><a href="#cb3-34"></a> gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR);</span> <span id="cb3-34"><a href="#cb3-34"></a> gtk_text_view_set_wrap_mode <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> GTK_WRAP_WORD_CHAR<span class="op">);</span></span>
<span id="cb3-35"><a href="#cb3-35"></a> gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);</span> <span id="cb3-35"><a href="#cb3-35"></a> gtk_scrolled_window_set_child <span class="op">(</span>GTK_SCROLLED_WINDOW <span class="op">(</span>scr<span class="op">),</span> tv<span class="op">);</span></span>
<span id="cb3-36"><a href="#cb3-36"></a></span> <span id="cb3-36"><a href="#cb3-36"></a></span>
<span id="cb3-37"><a href="#cb3-37"></a> tfe_text_view_set_file (TFE_TEXT_VIEW (tv), g_file_dup (files[i]));</span> <span id="cb3-37"><a href="#cb3-37"></a> tfe_text_view_set_file <span class="op">(</span>TFE_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> g_file_dup <span class="op">(</span>files<span class="op">[</span>i<span class="op">]));</span></span>
<span id="cb3-38"><a href="#cb3-38"></a> gtk_text_buffer_set_text (tb, contents, length);</span> <span id="cb3-38"><a href="#cb3-38"></a> gtk_text_buffer_set_text <span class="op">(</span>tb<span class="op">,</span> contents<span class="op">,</span> length<span class="op">);</span></span>
<span id="cb3-39"><a href="#cb3-39"></a> g_free (contents);</span> <span id="cb3-39"><a href="#cb3-39"></a> g_free <span class="op">(</span>contents<span class="op">);</span></span>
<span id="cb3-40"><a href="#cb3-40"></a> filename = g_file_get_basename (files[i]);</span> <span id="cb3-40"><a href="#cb3-40"></a> filename <span class="op">=</span> g_file_get_basename <span class="op">(</span>files<span class="op">[</span>i<span class="op">]);</span></span>
<span id="cb3-41"><a href="#cb3-41"></a> lab = gtk_label_new (filename);</span> <span id="cb3-41"><a href="#cb3-41"></a> lab <span class="op">=</span> gtk_label_new <span class="op">(</span>filename<span class="op">);</span></span>
<span id="cb3-42"><a href="#cb3-42"></a> gtk_notebook_append_page (GTK_NOTEBOOK (nb), scr, lab);</span> <span id="cb3-42"><a href="#cb3-42"></a> gtk_notebook_append_page <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">),</span> scr<span class="op">,</span> lab<span class="op">);</span></span>
<span id="cb3-43"><a href="#cb3-43"></a> nbp = gtk_notebook_get_page (GTK_NOTEBOOK (nb), scr);</span> <span id="cb3-43"><a href="#cb3-43"></a> nbp <span class="op">=</span> gtk_notebook_get_page <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">),</span> scr<span class="op">);</span></span>
<span id="cb3-44"><a href="#cb3-44"></a> g_object_set (nbp, <span class="st">&quot;tab-expand&quot;</span>, TRUE, NULL);</span> <span id="cb3-44"><a href="#cb3-44"></a> g_object_set <span class="op">(</span>nbp<span class="op">,</span> <span class="st">&quot;tab-expand&quot;</span><span class="op">,</span> TRUE<span class="op">,</span> NULL<span class="op">);</span></span>
<span id="cb3-45"><a href="#cb3-45"></a> g_free (filename);</span> <span id="cb3-45"><a href="#cb3-45"></a> g_free <span class="op">(</span>filename<span class="op">);</span></span>
<span id="cb3-46"><a href="#cb3-46"></a> } <span class="cf">else</span> <span class="cf">if</span> ((filename = g_file_get_path (files[i])) != NULL) {</span> <span id="cb3-46"><a href="#cb3-46"></a> <span class="op">}</span> <span class="cf">else</span> <span class="cf">if</span> <span class="op">((</span>filename <span class="op">=</span> g_file_get_path <span class="op">(</span>files<span class="op">[</span>i<span class="op">]))</span> <span class="op">!=</span> NULL<span class="op">)</span> <span class="op">{</span></span>
<span id="cb3-47"><a href="#cb3-47"></a> g_print (<span class="st">&quot;No such file: %s.</span><span class="sc">\n</span><span class="st">&quot;</span>, filename);</span> <span id="cb3-47"><a href="#cb3-47"></a> g_print <span class="op">(</span><span class="st">&quot;No such file: %s.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">,</span> filename<span class="op">);</span></span>
<span id="cb3-48"><a href="#cb3-48"></a> g_free (filename);</span> <span id="cb3-48"><a href="#cb3-48"></a> g_free <span class="op">(</span>filename<span class="op">);</span></span>
<span id="cb3-49"><a href="#cb3-49"></a> } <span class="cf">else</span></span> <span id="cb3-49"><a href="#cb3-49"></a> <span class="op">}</span> <span class="cf">else</span></span>
<span id="cb3-50"><a href="#cb3-50"></a> g_print (<span class="st">&quot;No valid file is given</span><span class="sc">\n</span><span class="st">&quot;</span>);</span> <span id="cb3-50"><a href="#cb3-50"></a> g_print <span class="op">(</span><span class="st">&quot;No valid file is given</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb3-51"><a href="#cb3-51"></a> }</span> <span id="cb3-51"><a href="#cb3-51"></a> <span class="op">}</span></span>
<span id="cb3-52"><a href="#cb3-52"></a> <span class="cf">if</span> (gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb)) &gt; <span class="dv">0</span>) {</span> <span id="cb3-52"><a href="#cb3-52"></a> <span class="cf">if</span> <span class="op">(</span>gtk_notebook_get_n_pages <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">))</span> <span class="op">&gt;</span> <span class="dv">0</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb3-53"><a href="#cb3-53"></a> gtk_widget_show (win);</span> <span id="cb3-53"><a href="#cb3-53"></a> gtk_widget_show <span class="op">(</span>win<span class="op">);</span></span>
<span id="cb3-54"><a href="#cb3-54"></a> } <span class="cf">else</span></span> <span id="cb3-54"><a href="#cb3-54"></a> <span class="op">}</span> <span class="cf">else</span></span>
<span id="cb3-55"><a href="#cb3-55"></a> gtk_window_destroy (GTK_WINDOW (win));</span> <span id="cb3-55"><a href="#cb3-55"></a> gtk_window_destroy <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
<span id="cb3-56"><a href="#cb3-56"></a>}</span> <span id="cb3-56"><a href="#cb3-56"></a><span class="op">}</span></span>
<span id="cb3-57"><a href="#cb3-57"></a></span> <span id="cb3-57"><a href="#cb3-57"></a></span>
<span id="cb3-58"><a href="#cb3-58"></a><span class="dt">int</span></span> <span id="cb3-58"><a href="#cb3-58"></a><span class="dt">int</span></span>
<span id="cb3-59"><a href="#cb3-59"></a>main (<span class="dt">int</span> argc, <span class="dt">char</span> **argv) {</span> <span id="cb3-59"><a href="#cb3-59"></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="cb3-60"><a href="#cb3-60"></a> GtkApplication *app;</span> <span id="cb3-60"><a href="#cb3-60"></a> GtkApplication <span class="op">*</span>app<span class="op">;</span></span>
<span id="cb3-61"><a href="#cb3-61"></a> <span class="dt">int</span> stat;</span> <span id="cb3-61"><a href="#cb3-61"></a> <span class="dt">int</span> stat<span class="op">;</span></span>
<span id="cb3-62"><a href="#cb3-62"></a></span> <span id="cb3-62"><a href="#cb3-62"></a></span>
<span id="cb3-63"><a href="#cb3-63"></a> app = gtk_application_new (<span class="st">&quot;com.github.ToshioCP.tfe&quot;</span>, G_APPLICATION_HANDLES_OPEN);</span> <span id="cb3-63"><a href="#cb3-63"></a> app <span class="op">=</span> gtk_application_new <span class="op">(</span><span class="st">&quot;com.github.ToshioCP.tfe&quot;</span><span class="op">,</span> G_APPLICATION_HANDLES_OPEN<span class="op">);</span></span>
<span id="cb3-64"><a href="#cb3-64"></a> g_signal_connect (app, <span class="st">&quot;activate&quot;</span>, G_CALLBACK (app_activate), NULL);</span> <span id="cb3-64"><a href="#cb3-64"></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="cb3-65"><a href="#cb3-65"></a> g_signal_connect (app, <span class="st">&quot;open&quot;</span>, G_CALLBACK (app_open), NULL);</span> <span id="cb3-65"><a href="#cb3-65"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">&quot;open&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>app_open<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb3-66"><a href="#cb3-66"></a> stat =g_application_run (G_APPLICATION (app), argc, argv);</span> <span id="cb3-66"><a href="#cb3-66"></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="cb3-67"><a href="#cb3-67"></a> g_object_unref (app);</span> <span id="cb3-67"><a href="#cb3-67"></a> g_object_unref <span class="op">(</span>app<span class="op">);</span></span>
<span id="cb3-68"><a href="#cb3-68"></a> <span class="cf">return</span> stat;</span> <span id="cb3-68"><a href="#cb3-68"></a> <span class="cf">return</span> stat<span class="op">;</span></span>
<span id="cb3-69"><a href="#cb3-69"></a>}</span></code></pre></div> <span id="cb3-69"><a href="#cb3-69"></a><span class="op">}</span></span></code></pre></div>
<p>The ui file <code>tfe.ui</code> is the same as <code>tfe3.ui</code> in the previous section.</p> <p>The ui file <code>tfe.ui</code> is the same as <code>tfe3.ui</code>
in the previous section.</p>
<p><code>tfe.gresource.xml</code></p> <p><code>tfe.gresource.xml</code></p>
<div class="sourceCode" id="cb4"><pre class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><span id="cb4-1"><a href="#cb4-1"></a><span class="kw">&lt;?xml</span> version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;<span class="kw">?&gt;</span></span> <div class="sourceCode" id="cb4"><pre
<span id="cb4-2"><a href="#cb4-2"></a><span class="kw">&lt;gresources&gt;</span></span> class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><span id="cb4-1"><a href="#cb4-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="cb4-3"><a href="#cb4-3"></a> <span class="kw">&lt;gresource</span><span class="ot"> prefix=</span><span class="st">&quot;/com/github/ToshioCP/tfe3&quot;</span><span class="kw">&gt;</span></span> <span id="cb4-2"><a href="#cb4-2"></a>&lt;<span class="kw">gresources</span>&gt;</span>
<span id="cb4-4"><a href="#cb4-4"></a> <span class="kw">&lt;file&gt;</span>tfe.ui<span class="kw">&lt;/file&gt;</span></span> <span id="cb4-3"><a href="#cb4-3"></a> &lt;<span class="kw">gresource</span><span class="ot"> prefix=</span><span class="st">&quot;/com/github/ToshioCP/tfe3&quot;</span>&gt;</span>
<span id="cb4-5"><a href="#cb4-5"></a> <span class="kw">&lt;/gresource&gt;</span></span> <span id="cb4-4"><a href="#cb4-4"></a> &lt;<span class="kw">file</span>&gt;tfe.ui&lt;/<span class="kw">file</span>&gt;</span>
<span id="cb4-6"><a href="#cb4-6"></a><span class="kw">&lt;/gresources&gt;</span></span></code></pre></div> <span id="cb4-5"><a href="#cb4-5"></a> &lt;/<span class="kw">gresource</span>&gt;</span>
<h2 id="make">Make</h2> <span id="cb4-6"><a href="#cb4-6"></a>&lt;/<span class="kw">gresources</span>&gt;</span></code></pre></div>
<p>Dividing a file makes it easy to maintain source files. But now we are faced with a new problem. The building step increases.</p> <p>Dividing a file makes it easy to maintain source files. But now we
face a new problem. The building step increases.</p>
<ul> <ul>
<li>Compiling the ui file <code>tfe.ui</code> into <code>resources.c</code>.</li> <li>Compiling the ui file <code>tfe.ui</code> into
<li>Compiling <code>tfe.c</code> into <code>tfe.o</code> (object file).</li> <code>resources.c</code>.</li>
<li>Compiling <code>tfetextview.c</code> into <code>tfetextview.o</code>.</li> <li>Compiling <code>tfe.c</code> into <code>tfe.o</code> (object
<li>Compiling <code>resources.c</code> into <code>resources.o</code>.</li> file).</li>
<li>Compiling <code>tfetextview.c</code> into
<code>tfetextview.o</code>.</li>
<li>Compiling <code>resources.c</code> into
<code>resources.o</code>.</li>
<li>Linking all the object files into application <code>tfe</code>.</li> <li>Linking all the object files into application <code>tfe</code>.</li>
</ul> </ul>
<p>Now build tool is necessary to manage it. Make is one of the build tools. It was created in 1976. It is an old and widely used program.</p> <p>Build tools manage the steps. Ill show you three build tools, Meson
<p>Make analyzes Makefile and executes compilers. All instructions are written in Makefile.</p> and Ninja, Make and Rake. Meson and Ninja is recommended as a C build
<div class="sourceCode" id="cb5"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true"></a><span class="dv">sample.o:</span><span class="dt"> sample.c</span></span> tool, but others are also fine. Its your choice.</p>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true"></a> gcc -o sample.o sample.c</span></code></pre></div> <h2 id="meson-and-ninja">Meson and Ninja</h2>
<p>The sample of Malefile above consists of three elements, <code>sample.o</code>, <code>sample.c</code> and <code>gcc -o sample.o sample.c</code>.</p> <p>Meson and Ninja is one of the most popular building tool to build C
language program. Many developers use Meson and Ninja lately. For
example, GTK 4 uses them.</p>
<p>You need to make <code>meson.build</code> file first.</p>
<div class="sourceCode" id="cb5"><pre
class="sourceCode numberSource numberLines"><code class="sourceCode"><span id="cb5-1"><a href="#cb5-1"></a>project(&#39;tfe&#39;, &#39;c&#39;)</span>
<span id="cb5-2"><a href="#cb5-2"></a></span>
<span id="cb5-3"><a href="#cb5-3"></a>gtkdep = dependency(&#39;gtk4&#39;)</span>
<span id="cb5-4"><a href="#cb5-4"></a></span>
<span id="cb5-5"><a href="#cb5-5"></a>gnome=import(&#39;gnome&#39;)</span>
<span id="cb5-6"><a href="#cb5-6"></a>resources = gnome.compile_resources(&#39;resources&#39;,&#39;tfe.gresource.xml&#39;)</span>
<span id="cb5-7"><a href="#cb5-7"></a></span>
<span id="cb5-8"><a href="#cb5-8"></a>sourcefiles=files(&#39;tfe.c&#39;, &#39;tfetextview.c&#39;)</span>
<span id="cb5-9"><a href="#cb5-9"></a></span>
<span id="cb5-10"><a href="#cb5-10"></a>executable(&#39;tfe&#39;, sourcefiles, resources, dependencies: gtkdep)</span></code></pre></div>
<ul> <ul>
<li><code>sample.o</code> is called target.</li> <li>1: The function <code>project</code> defines things about the
<li><code>sample.c</code> is prerequisite.</li> project. The first parameter is the name of the project and the second
<li><code>gcc -o sample.o sample.c</code> is recipe. Recipes follow tab characters, not spaces. (It is very important. Use tab not space, or make wont work as you expected).</li> is the programming language.</li>
<li>2: <code>dependency</code> function defines a dependency that is
taken by <code>pkg-config</code>. We put <code>gtk4</code> as an
argument.</li>
<li>5: <code>import</code> function imports a module. In line 5, the
gnome module is imported and assigned to the variable
<code>gnome</code>. The gnome module provides helper tools to build GTK
programs.</li>
<li>6: <code>.compile_resources</code> is a method of the gnome module
and compiles files to resources under the instruction of xml file. In
line 6, the resource filename is <code>resources</code>, which means
<code>resources.c</code> and <code>resources.h</code>, and xml file is
<code>tfe.gresource.xml</code>. This method generates C source file by
default.</li>
<li>8: Defines source files.</li>
<li>10: Executable function generates a target file by compiling source
files. The first parameter is the filename of the target. The following
parameters are source files. The last parameter is an option
<code>dependencies</code>. <code>gtkdep</code> is used in the
compilation.</li>
</ul>
<p>Now run meson and ninja.</p>
<pre><code>$ meson _build
$ ninja -C _build</code></pre>
<p>Then, the executable file <code>tfe</code> is generated under the
directory <code>_build</code>.</p>
<pre><code>$ _build/tfe tfe.c tfetextview.c</code></pre>
<p>A window appears. It includes a notebook with two pages. One is
<code>tfe.c</code> and the other is <code>tfetextview.c</code>.</p>
<p>For further information, see <a href="https://mesonbuild.com/">The
Meson Build system</a>.</p>
<h2 id="make">Make</h2>
<p>Make is a build tool created in 1976. It was a standard build tool
for C compiling, but lately it is replaced by Meson and Ninja.</p>
<p>Make analyzes Makefile and executes compilers. All instructions are
written in Makefile.</p>
<p>For example,</p>
<div class="sourceCode" id="cb8"><pre
class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="dv">sample.o:</span><span class="dt"> sample.c</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a> gcc -o sample.o sample.c</span></code></pre></div>
<p>Malefile above consists of three elements, <code>sample.o</code>,
<code>sample.c</code> and <code>gcc -o sample.o sample.c</code>.</p>
<ul>
<li><code>sample.o</code> is a target.</li>
<li><code>sample.c</code> is a prerequisite.</li>
<li><code>gcc -o sample.o sample.c</code> is a recipe. Recipes follow
tab characters, not spaces. (It is very important. Use tab, or make
wont work as you expected).</li>
</ul> </ul>
<p>The rule is:</p> <p>The rule is:</p>
<p>If a prerequisite modified later than a target, then make executes the recipe.</p> <p>If a prerequisite modified later than a target, then make executes
<p>In the example above, if <code>sample.c</code> is modified after the generation of <code>sample.o</code>, then make executes gcc and compile <code>sample.c</code> into <code>sample.o</code>. If the modification time of <code>sample.c</code> is older then the generation of <code>sample.o</code>, then no compiling is necessary, so make does nothing.</p> the recipe.</p>
<p>In the example above, if <code>sample.c</code> is modified after the
generation of <code>sample.o</code>, then make executes gcc and compile
<code>sample.c</code> into <code>sample.o</code>. If the modification
time of <code>sample.c</code> is older then the generation of
<code>sample.o</code>, then no compiling is necessary, so make does
nothing.</p>
<p>The Makefile for <code>tfe</code> is as follows.</p> <p>The Makefile for <code>tfe</code> is as follows.</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode numberSource makefile numberLines"><code class="sourceCode makefile"><span id="cb6-1"><a href="#cb6-1"></a><span class="dv">all:</span><span class="dt"> tfe</span></span> <div class="sourceCode" id="cb9"><pre
<span id="cb6-2"><a href="#cb6-2"></a></span> class="sourceCode numberSource makefile numberLines"><code class="sourceCode makefile"><span id="cb9-1"><a href="#cb9-1"></a><span class="dv">all:</span><span class="dt"> tfe</span></span>
<span id="cb6-3"><a href="#cb6-3"></a><span class="dv">tfe:</span><span class="dt"> tfe.o tfetextview.o resources.o</span></span> <span id="cb9-2"><a href="#cb9-2"></a></span>
<span id="cb6-4"><a href="#cb6-4"></a> gcc -o tfe tfe.o tfetextview.o resources.o `pkg-config --libs gtk4`</span> <span id="cb9-3"><a href="#cb9-3"></a><span class="dv">tfe:</span><span class="dt"> tfe.o tfetextview.o resources.o</span></span>
<span id="cb6-5"><a href="#cb6-5"></a></span> <span id="cb9-4"><a href="#cb9-4"></a> gcc -o tfe tfe.o tfetextview.o resources.o `pkg-config --libs gtk4`</span>
<span id="cb6-6"><a href="#cb6-6"></a><span class="dv">tfe.o:</span><span class="dt"> tfe.c tfetextview.h</span></span> <span id="cb9-5"><a href="#cb9-5"></a></span>
<span id="cb6-7"><a href="#cb6-7"></a> gcc -c -o tfe.o `pkg-config --cflags gtk4` tfe.c</span> <span id="cb9-6"><a href="#cb9-6"></a><span class="dv">tfe.o:</span><span class="dt"> tfe.c tfetextview.h</span></span>
<span id="cb6-8"><a href="#cb6-8"></a><span class="dv">tfetextview.o:</span><span class="dt"> tfetextview.c tfetextview.h</span></span> <span id="cb9-7"><a href="#cb9-7"></a> gcc -c -o tfe.o `pkg-config --cflags gtk4` tfe.c</span>
<span id="cb6-9"><a href="#cb6-9"></a> gcc -c -o tfetextview.o `pkg-config --cflags gtk4` tfetextview.c</span> <span id="cb9-8"><a href="#cb9-8"></a><span class="dv">tfetextview.o:</span><span class="dt"> tfetextview.c tfetextview.h</span></span>
<span id="cb6-10"><a href="#cb6-10"></a><span class="dv">resources.o:</span><span class="dt"> resources.c</span></span> <span id="cb9-9"><a href="#cb9-9"></a> gcc -c -o tfetextview.o `pkg-config --cflags gtk4` tfetextview.c</span>
<span id="cb6-11"><a href="#cb6-11"></a> gcc -c -o resources.o `pkg-config --cflags gtk4` resources.c</span> <span id="cb9-10"><a href="#cb9-10"></a><span class="dv">resources.o:</span><span class="dt"> resources.c</span></span>
<span id="cb6-12"><a href="#cb6-12"></a></span> <span id="cb9-11"><a href="#cb9-11"></a> gcc -c -o resources.o `pkg-config --cflags gtk4` resources.c</span>
<span id="cb6-13"><a href="#cb6-13"></a><span class="dv">resources.c:</span><span class="dt"> tfe.gresource.xml tfe.ui</span></span> <span id="cb9-12"><a href="#cb9-12"></a></span>
<span id="cb6-14"><a href="#cb6-14"></a> glib-compile-resources tfe.gresource.xml --target=resources.c --generate-source</span> <span id="cb9-13"><a href="#cb9-13"></a><span class="dv">resources.c:</span><span class="dt"> tfe.gresource.xml tfe.ui</span></span>
<span id="cb6-15"><a href="#cb6-15"></a></span> <span id="cb9-14"><a href="#cb9-14"></a> glib-compile-resources tfe.gresource.xml --target=resources.c --generate-source</span>
<span id="cb6-16"><a href="#cb6-16"></a><span class="ot">.Phony:</span><span class="dt"> clean</span></span> <span id="cb9-15"><a href="#cb9-15"></a></span>
<span id="cb6-17"><a href="#cb6-17"></a></span> <span id="cb9-16"><a href="#cb9-16"></a><span class="ot">.Phony:</span><span class="dt"> clean</span></span>
<span id="cb6-18"><a href="#cb6-18"></a><span class="dv">clean:</span></span> <span id="cb9-17"><a href="#cb9-17"></a></span>
<span id="cb6-19"><a href="#cb6-19"></a> rm -f tfe tfe.o tfetextview.o resources.o resources.c</span></code></pre></div> <span id="cb9-18"><a href="#cb9-18"></a><span class="dv">clean:</span></span>
<p>You only need to type <code>make</code>.</p> <span id="cb9-19"><a href="#cb9-19"></a> rm -f tfe tfe.o tfetextview.o resources.o resources.c</span></code></pre></div>
<p>You just type <code>make</code> and everything will be done.</p>
<pre><code>$ make <pre><code>$ make
gcc -c -o tfe.o `pkg-config --cflags gtk4` tfe.c gcc -c -o tfe.o `pkg-config --cflags gtk4` tfe.c
gcc -c -o tfetextview.o `pkg-config --cflags gtk4` tfetextview.c gcc -c -o tfetextview.o `pkg-config --cflags gtk4` tfetextview.c
glib-compile-resources tfe.gresource.xml --target=resources.c --generate-source glib-compile-resources tfe.gresource.xml --target=resources.c --generate-source
gcc -c -o resources.o `pkg-config --cflags gtk4` resources.c gcc -c -o resources.o `pkg-config --cflags gtk4` resources.c
gcc -o tfe tfe.o tfetextview.o resources.o `pkg-config --libs gtk4`</code></pre> gcc -o tfe tfe.o tfetextview.o resources.o `pkg-config --libs gtk4`</code></pre>
<p>I used only very basic rules to write this Makefile. There are many more convenient methods to make it more compact. But it will be long to explain it. So I want to finish explaining make and move on to the next topic.</p> <p>I used only very basic rules to write this Makefile. There are many
more convenient methods to make it more compact. But it will be long to
explain it. So I want to finish with make and move on to the next
topic.</p>
<p>You can download “Gnu Make Manual” from <a
href="https://www.gnu.org/software/make/manual/">GNU website</a>.</p>
<h2 id="rake">Rake</h2> <h2 id="rake">Rake</h2>
<p>Rake is a similar program to make. It is written in Ruby code. If you dont use Ruby, you dont need to read this subsection. However, Ruby is really sophisticated and recommendable script language.</p> <p>Rake is a similar program to make. It is written in Ruby language. If
you dont use Ruby, you dont need to read this subsection. However,
Ruby is really sophisticated and recommendable script language.</p>
<ul> <ul>
<li>Rakefile controls the behavior of <code>rake</code>.</li> <li>Rakefile controls the behavior of <code>rake</code>.</li>
<li>You can write any Ruby code in Rakefile.</li> <li>You can write any Ruby code in Rakefile.</li>
</ul> </ul>
<p>Rake has task and file task, which is similar to target, prerequisite and recipe in make.</p> <p>Rake has task and file task, which is similar to target, prerequisite
<div class="sourceCode" id="cb8"><pre class="sourceCode numberSource ruby numberLines"><code class="sourceCode ruby"><span id="cb8-1"><a href="#cb8-1"></a>require <span class="st">&#39;rake/clean&#39;</span></span> and recipe in make.</p>
<span id="cb8-2"><a href="#cb8-2"></a></span> <div class="sourceCode" id="cb11"><pre
<span id="cb8-3"><a href="#cb8-3"></a>targetfile = <span class="st">&quot;tfe&quot;</span></span> class="sourceCode numberSource ruby numberLines"><code class="sourceCode ruby"><span id="cb11-1"><a href="#cb11-1"></a><span class="fu">require</span> <span class="vs">&#39;rake/clean&#39;</span></span>
<span id="cb8-4"><a href="#cb8-4"></a>srcfiles = <span class="dt">FileList</span>[<span class="st">&quot;tfe.c&quot;</span>, <span class="st">&quot;tfetextview.c&quot;</span>, <span class="st">&quot;resources.c&quot;</span>]</span> <span id="cb11-2"><a href="#cb11-2"></a></span>
<span id="cb8-5"><a href="#cb8-5"></a>rscfile = srcfiles[<span class="dv">2</span>]</span> <span id="cb11-3"><a href="#cb11-3"></a>targetfile <span class="kw">=</span> <span class="st">&quot;tfe&quot;</span></span>
<span id="cb8-6"><a href="#cb8-6"></a>objfiles = srcfiles.gsub(<span class="ot">/.c$/</span>, <span class="st">&#39;.o&#39;</span>)</span> <span id="cb11-4"><a href="#cb11-4"></a>srcfiles <span class="kw">=</span> <span class="dt">FileList</span><span class="kw">[</span><span class="st">&quot;tfe.c&quot;</span>, <span class="st">&quot;tfetextview.c&quot;</span>, <span class="st">&quot;resources.c&quot;</span><span class="kw">]</span></span>
<span id="cb8-7"><a href="#cb8-7"></a></span> <span id="cb11-5"><a href="#cb11-5"></a>uifile <span class="kw">=</span> <span class="st">&quot;tfe.ui&quot;</span></span>
<span id="cb8-8"><a href="#cb8-8"></a><span class="dt">CLEAN</span>.include(targetfile, objfiles, rscfile)</span> <span id="cb11-6"><a href="#cb11-6"></a>rscfile <span class="kw">=</span> srcfiles<span class="kw">[</span><span class="dv">2</span><span class="kw">]</span></span>
<span id="cb8-9"><a href="#cb8-9"></a></span> <span id="cb11-7"><a href="#cb11-7"></a>objfiles <span class="kw">=</span> srcfiles<span class="at">.ext</span>(<span class="st">&quot;.o&quot;</span>)</span>
<span id="cb8-10"><a href="#cb8-10"></a>task <span class="st">default: </span>targetfile</span> <span id="cb11-8"><a href="#cb11-8"></a>gresource_xml <span class="kw">=</span> <span class="st">&quot;tfe.gresource.xml&quot;</span></span>
<span id="cb8-11"><a href="#cb8-11"></a></span> <span id="cb11-9"><a href="#cb11-9"></a></span>
<span id="cb8-12"><a href="#cb8-12"></a>file targetfile =&gt; objfiles <span class="kw">do</span> |t|</span> <span id="cb11-10"><a href="#cb11-10"></a><span class="cn">CLEAN</span><span class="at">.include</span>(targetfile, objfiles, rscfile)</span>
<span id="cb8-13"><a href="#cb8-13"></a> sh <span class="st">&quot;gcc -o </span><span class="ot">#{</span>t.name<span class="ot">}</span><span class="st"> </span><span class="ot">#{</span>t.prerequisites.join(<span class="ch">&#39; &#39;</span>)<span class="ot">}</span><span class="st"> `pkg-config --libs gtk4`&quot;</span></span> <span id="cb11-11"><a href="#cb11-11"></a></span>
<span id="cb8-14"><a href="#cb8-14"></a><span class="kw">end</span></span> <span id="cb11-12"><a href="#cb11-12"></a>task <span class="wa">default: </span>targetfile</span>
<span id="cb8-15"><a href="#cb8-15"></a></span> <span id="cb11-13"><a href="#cb11-13"></a></span>
<span id="cb8-16"><a href="#cb8-16"></a>objfiles.each <span class="kw">do</span> |obj|</span> <span id="cb11-14"><a href="#cb11-14"></a>file targetfile <span class="kw">=&gt;</span> objfiles <span class="cf">do</span> <span class="kw">|</span>t<span class="kw">|</span></span>
<span id="cb8-17"><a href="#cb8-17"></a> src = obj.gsub(<span class="ot">/.o$/</span>,<span class="st">&#39;.c&#39;</span>)</span> <span id="cb11-15"><a href="#cb11-15"></a> sh <span class="st">&quot;gcc -o </span><span class="sc">#{</span>t<span class="at">.name</span><span class="sc">}</span><span class="st"> </span><span class="sc">#{</span>t<span class="at">.prerequisites.join</span>(<span class="ch">&#39; &#39;</span>)<span class="sc">}</span><span class="st"> `pkg-config --libs gtk4`&quot;</span></span>
<span id="cb8-18"><a href="#cb8-18"></a> file obj =&gt; src <span class="kw">do</span> |t|</span> <span id="cb11-16"><a href="#cb11-16"></a><span class="cf">end</span></span>
<span id="cb8-19"><a href="#cb8-19"></a> sh <span class="st">&quot;gcc -c -o </span><span class="ot">#{</span>t.name<span class="ot">}</span><span class="st"> `pkg-config --cflags gtk4` </span><span class="ot">#{</span>t.source<span class="ot">}</span><span class="st">&quot;</span></span> <span id="cb11-17"><a href="#cb11-17"></a></span>
<span id="cb8-20"><a href="#cb8-20"></a> <span class="kw">end</span></span> <span id="cb11-18"><a href="#cb11-18"></a>objfiles<span class="at">.each</span> <span class="cf">do</span> <span class="kw">|</span>obj<span class="kw">|</span></span>
<span id="cb8-21"><a href="#cb8-21"></a><span class="kw">end</span></span> <span id="cb11-19"><a href="#cb11-19"></a> src <span class="kw">=</span> obj<span class="at">.ext</span>(<span class="st">&quot;.c&quot;</span>)</span>
<span id="cb8-22"><a href="#cb8-22"></a></span> <span id="cb11-20"><a href="#cb11-20"></a> file obj <span class="kw">=&gt;</span> src <span class="cf">do</span> <span class="kw">|</span>t<span class="kw">|</span></span>
<span id="cb8-23"><a href="#cb8-23"></a>file rscfile =&gt; [<span class="st">&quot;tfe.gresource.xml&quot;</span>, <span class="st">&quot;tfe.ui&quot;</span>] <span class="kw">do</span> |t|</span> <span id="cb11-21"><a href="#cb11-21"></a> sh <span class="st">&quot;gcc -c -o </span><span class="sc">#{</span>t<span class="at">.name</span><span class="sc">}</span><span class="st"> `pkg-config --cflags gtk4` </span><span class="sc">#{</span>t<span class="at">.source</span><span class="sc">}</span><span class="st">&quot;</span></span>
<span id="cb8-24"><a href="#cb8-24"></a> sh <span class="st">&quot;glib-compile-resources </span><span class="ot">#{</span>t.prerequisites[<span class="dv">0</span>]<span class="ot">}</span><span class="st"> --target=</span><span class="ot">#{</span>t.name<span class="ot">}</span><span class="st"> --generate-source&quot;</span></span> <span id="cb11-22"><a href="#cb11-22"></a> <span class="cf">end</span></span>
<span id="cb8-25"><a href="#cb8-25"></a><span class="kw">end</span></span></code></pre></div> <span id="cb11-23"><a href="#cb11-23"></a><span class="cf">end</span></span>
<p>The contents of the <code>Rakefile</code> is almost same as the <code>Makefile</code> in the previous subsection.</p> <span id="cb11-24"><a href="#cb11-24"></a></span>
<span id="cb11-25"><a href="#cb11-25"></a>file rscfile <span class="kw">=&gt;</span> uifile <span class="cf">do</span> <span class="kw">|</span>t<span class="kw">|</span></span>
<span id="cb11-26"><a href="#cb11-26"></a> sh <span class="st">&quot;glib-compile-resources </span><span class="sc">#{</span>gresource_xml<span class="sc">}</span><span class="st"> --target=</span><span class="sc">#{</span>t<span class="at">.name</span><span class="sc">}</span><span class="st"> --generate-source&quot;</span></span>
<span id="cb11-27"><a href="#cb11-27"></a><span class="cf">end</span></span></code></pre></div>
<p>The contents of the <code>Rakefile</code> is almost same as the
<code>Makefile</code> in the previous subsection.</p>
<ul> <ul>
<li>3-6: Defines target file, source file and so on.</li> <li>3-8: Defines target file, source files and so on.</li>
<li>1, 8: Loads clean library. And defines CLEAN file list. The files included by CLEAN will be removed when <code>rake clean</code> is typed on the command line.</li> <li>1, 10 Requires rake/clean library. And clean files are added to
<li>10: The default target depends on <code>targetfile</code>. The task <code>default</code> is the final goal of tasks.</li> CLEAN. The files included by CLEAN will be removed when
<li>12-14: <code>targetfile</code> depends on <code>objfiles</code>. The variable <code>t</code> is a task object. <code>rake clean</code> is typed on the command line.</li>
<li>12: The default target depends on <code>targetfile</code>. The task
<code>default</code> is the final goal of tasks.</li>
<li>14-16: <code>targetfile</code> depends on <code>objfiles</code>. The
variable <code>t</code> is a task object.
<ul> <ul>
<li><code>t.name</code> is a target name</li> <li><code>t.name</code> is a target name</li>
<li><code>t.prerequisites</code> is an array of prerequisites.</li> <li><code>t.prerequisites</code> is an array of prerequisites.</li>
<li><code>t.source</code> is the first element of prerequisites.</li> <li><code>t.source</code> is the first element of prerequisites.</li>
</ul></li> </ul></li>
<li><code>sh</code> is a method to give the following string to shell as an argument and executes the shell.</li> <li><code>sh</code> is a method to give the following string to shell as
<li>16-21: Theres a loop by each element of the array of <code>objfiles</code>. Each object depends on corresponding source file.</li> an argument and executes the shell.</li>
<li>23-25: Resource file depends on xml file and ui file.</li> <li>18-23: An each iterator of the array <code>objfiles</code>. Each
object depends on corresponding source file.</li>
<li>25-27: Resource file depends on ui file.</li>
</ul> </ul>
<p>Rakefile might seem to be difficult for beginners. But, you can use any Ruby syntax in Rakefile, so it is really flexible. If you practice Ruby and Rakefile, it will be highly productive tools.</p> <p>Rakefile might seem to be difficult for beginners. But, you can use
<h2 id="meson-and-ninja">Meson and ninja</h2> any Ruby syntax in the Rakefile, so it is really flexible. If you
<p>Meson is one of the most popular building tool despite the developing version. And ninja is similar to make but much faster than make. Several years ago, most of the C developers used autotools and make. But now the situation has changed. Many developers are using meson and ninja now.</p> practice Ruby and Rakefile, it will be highly productive tools.</p>
<p>To use meson, you first need to write <code>meson.build</code> file.</p> <p>For further information, see <a
<div class="sourceCode" id="cb9"><pre class="sourceCode numberSource numberLines"><code class="sourceCode"><span id="cb9-1"><a href="#cb9-1"></a>project(&#39;tfe&#39;, &#39;c&#39;)</span> href="https://toshiocp.github.io/Rake-tutorial-for-beginners-en/LearningRake.html">Rake
<span id="cb9-2"><a href="#cb9-2"></a></span> tutorial for beginners</a>.</p>
<span id="cb9-3"><a href="#cb9-3"></a>gtkdep = dependency(&#39;gtk4&#39;)</span>
<span id="cb9-4"><a href="#cb9-4"></a></span>
<span id="cb9-5"><a href="#cb9-5"></a>gnome=import(&#39;gnome&#39;)</span>
<span id="cb9-6"><a href="#cb9-6"></a>resources = gnome.compile_resources(&#39;resources&#39;,&#39;tfe.gresource.xml&#39;)</span>
<span id="cb9-7"><a href="#cb9-7"></a></span>
<span id="cb9-8"><a href="#cb9-8"></a>sourcefiles=files(&#39;tfe.c&#39;, &#39;tfetextview.c&#39;)</span>
<span id="cb9-9"><a href="#cb9-9"></a></span>
<span id="cb9-10"><a href="#cb9-10"></a>executable(&#39;tfe&#39;, sourcefiles, resources, dependencies: gtkdep)</span></code></pre></div>
<ul>
<li>1: The function <code>project</code> defines things about the project. The first parameter is the name of the project and the second is the programming language.</li>
<li>2: <code>dependency</code> function defines a dependency that is taken by <code>pkg-config</code>. We put <code>gtk4</code> as an argument.</li>
<li>5: <code>import</code> function imports a module. In line 5, the gnome module is imported and assigned to the variable <code>gnome</code>. The gnome module provides helper tools to build GTK programs.</li>
<li>6: <code>.compile_resources</code> is a method of the gnome module and compiles files to resources under the instruction of xml file. In line 6, the resource filename is <code>resources</code>, which means <code>resources.c</code> and <code>resources.h</code>, and xml file is <code>tfe.gresource.xml</code>. This method generates C source file by default.</li>
<li>8: Defines source files.</li>
<li>10: Executable function generates a target file by compiling source files. The first parameter is the filename of the target. The following parameters are source files. The last parameter is an option <code>dependencies</code>. <code>gtkdep</code> is used in the compilation.</li>
</ul>
<p>Now run meson and ninja.</p>
<pre><code>$ meson _build
$ ninja -C _build</code></pre>
<p>Then, the executable file <code>tfe</code> is generated under the directory <code>_build</code>.</p>
<pre><code>$ _build/tfe tfe.c tfetextview.c</code></pre>
<p>Then the window appears. And two notebook pages are in the window. One notebook is <code>tfe.c</code> and the other is <code>tfetextview.c</code>.</p>
<p>Ive shown you three build tools. I think meson and ninja is the best choice for the present.</p>
<p>We divided a file into some categorized files and used a build tool. This method is used by many developers.</p>
</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> <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> </body>

View file

@ -5,7 +5,7 @@
<meta name="generator" content="pandoc" /> <meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <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"> <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> <title>GTK 4 tutorial</title>
<style> <style>
code{white-space: pre-wrap;} code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;} span.smallcaps{font-variant: small-caps;}
@ -111,319 +111,503 @@
</div> </div>
</div> </div>
</nav> </nav>
<h1 id="initialization-and-destruction-of-instances">Initialization and destruction of instances</h1> <h1 id="instance-initialization-and-destruction">Instance Initialization
<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> and destruction</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. They
are located in two directories, src/tfe5 and src/tfetextview.</p>
<h2 id="encapsulation">Encapsulation</h2> <h2 id="encapsulation">Encapsulation</h2>
<p>Weve divided C source file into two parts. But it is not enough in terms of encapsulation.</p> <p>Weve divided C source file into two parts. But it is not enough in
terms of encapsulation.</p>
<ul> <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><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> <li>Header files also need to be organized.</li>
</ul> </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> <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> <ul>
<li>What is necessary to GFile when creating (or initializing) TfeTextView?</li> <li>What is necessary to GFile when creating (or initializing)
TfeTextView?</li>
<li>What is necessary to GFile when destructing 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>TfeTextView should read/write a file by itself or not?</li>
<li>How it communicates with objects outside?</li> <li>How it communicates with objects outside?</li>
</ul> </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> <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> <ul>
<li>Organizing functions.</li> <li>Organizing functions.</li>
<li>How to use GtkFileChooserDialog</li> <li>How to use GtkFileChooserDialog</li>
</ul> </ul>
<h2 id="gobject-and-its-children">GObject and its children</h2> <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> <p>GObject and its children are objects, which have both class and
<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> object C structures. First, think about instances. An instance is
<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> memories which has the object structure. The following is the structure
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true"></a></span> of TfeTextView.</p>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true"></a><span class="kw">struct</span> _TfeTextView {</span> <div class="sourceCode" id="cb1"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="co">/* This typedef statement is automatically generated by the macro G_DECLARE_FINAL_TYPE */</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true"></a> GtkTextView parent;</span> <span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="kw">typedef</span> <span class="kw">struct</span> _TfeTextView TfeTextView<span class="op">;</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true"></a> GFile *file;</span> <span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true"></a>};</span></code></pre></div> <span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> _TfeTextView <span class="op">{</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a> GtkTextView parent<span class="op">;</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a> GFile <span class="op">*</span>file<span class="op">;</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="op">};</span></span></code></pre></div>
<p>The members of the structure are:</p> <p>The members of the structure are:</p>
<ul> <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>The type of <code>parent</code> is GtkTextView which is C structure.
<li><code>file</code> is a pointer to GFile. It can be NULL if no file corresponds to the TfeTextView object.</li> It is declared in <code>gtktextview.h</code>. GtkTextView is the parent
of TfeTextView.</li>
<li><code>file</code> is a pointer to a GFile. It can be NULL if no file
corresponds to the TfeTextView instance.</li>
</ul> </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 object structures in
<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> the source files of GTK and GLib. The following is extracted from the
<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> source files (not exactly the same).</p>
<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> <div class="sourceCode" id="cb2"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">typedef</span> <span class="kw">struct</span> _GObject GObject<span class="op">;</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true"></a><span class="kw">struct</span> _GObject</span> <span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="kw">typedef</span> <span class="kw">struct</span> _GObject GInitiallyUnowned<span class="op">;</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true"></a>{</span> <span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> _GObject</span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true"></a> GTypeInstance g_type_instance;</span> <span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="op">{</span></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-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a> GTypeInstance g_type_instance<span class="op">;</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true"></a> GData *qdata;</span> <span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">volatile</span> guint ref_count<span class="op">;</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true"></a>};</span> <span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a> GData <span class="op">*</span>qdata<span class="op">;</span></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true"></a></span> <span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a><span class="op">};</span></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-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true"></a><span class="kw">struct</span> _GtkWidget</span> <span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a><span class="kw">typedef</span> <span class="kw">struct</span> _GtkWidget GtkWidget<span class="op">;</span></span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true"></a>{</span> <span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> _GtkWidget</span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true"></a> GInitiallyUnowned parent_instance;</span> <span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a><span class="op">{</span></span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true"></a> GtkWidgetPrivate *priv;</span> <span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a> GInitiallyUnowned parent_instance<span class="op">;</span></span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true"></a>};</span> <span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a> GtkWidgetPrivate <span class="op">*</span>priv<span class="op">;</span></span>
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true"></a></span> <span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a><span class="op">};</span></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-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true"></a><span class="kw">struct</span> _GtkTextView</span> <span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a><span class="kw">typedef</span> <span class="kw">struct</span> _GtkTextView GtkTextView<span class="op">;</span></span>
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true"></a>{</span> <span id="cb2-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> _GtkTextView</span>
<span id="cb2-20"><a href="#cb2-20" aria-hidden="true"></a> GtkWidget parent_instance;</span> <span id="cb2-19"><a href="#cb2-19" aria-hidden="true" tabindex="-1"></a><span class="op">{</span></span>
<span id="cb2-21"><a href="#cb2-21" aria-hidden="true"></a> GtkTextViewPrivate *priv;</span> <span id="cb2-20"><a href="#cb2-20" aria-hidden="true" tabindex="-1"></a> GtkWidget parent_instance<span class="op">;</span></span>
<span id="cb2-22"><a href="#cb2-22" aria-hidden="true"></a>};</span></code></pre></div> <span id="cb2-21"><a href="#cb2-21" aria-hidden="true" tabindex="-1"></a> GtkTextViewPrivate <span class="op">*</span>priv<span class="op">;</span></span>
<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> <span id="cb2-22"><a href="#cb2-22" aria-hidden="true" tabindex="-1"></a><span class="op">};</span></span></code></pre></div>
<p>In each structure, its parent is declared at the top of the members.
So, all the ancestors are included in the child object. The structure of
<code>TfeTextView</code> is like the following diagram.</p>
<figure> <figure>
<img src="image/TfeTextView.png" alt="" /><figcaption>The structure of the instance TfeTextView</figcaption> <img src="image/TfeTextView.png"
alt="The structure of the instance TfeTextView" />
<figcaption aria-hidden="true">The structure of the instance
TfeTextView</figcaption>
</figure> </figure>
<h2 id="initialization-of-a-tfetextview-instance">Initialization of a TfeTextView instance</h2> <p>Derivable classes (ancestor classes) have their own private data area
<p>The function <code>tfe_text_view_new</code> creates a new TfeTextView instance.</p> which are not included by the structure above. For example, GtkWidget
<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> has GtkWidgetPrivate (C structure) for its private data.</p>
<span id="cb3-2"><a href="#cb3-2"></a>tfe_text_view_new (<span class="dt">void</span>) {</span> <p>Notice declarations are not definitions. So, no memories are
<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> allocated when C structures are declared. Memories are allocated to them
<span id="cb3-4"><a href="#cb3-4"></a>}</span></code></pre></div> from the heap area when the <code>tfe_text_view_new</code> function is
<p>When this function is invoked, a TfeTextView instance is created and initialized. The initialization process is as follows.</p> called. At the same time, the ancestors private area allocated for the
<ol type="1"> TfeTetView. They are hidden from TfeTextView and it cant access to them
<li>Initializes GObject (GInitiallyUnowned) part in TfeTextView instance.</li> directly. The created memory is called instance. When a TfeTextView
<li>Initializes GtkWidget part in TfeTextView instance.</li> instance is created, it is given three data area.</p>
<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> <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>The instance (C structure).</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>GtkWidgetPrivate structure.</li>
<li>24: A function pointed by <code>finalize</code> finishes the destruction process.</li> <li>GtkTextViewPrivate structure.</li>
<li>The other pointers point to functions which are called while the instance lives.</li>
</ul> </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> <p>TfeTextView functions can access to its instance only. The
GtkWidgetPrivate and GtkTextViewPrivate are used by the ancestors
functions. See the following example.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>GtkWidget <span class="op">*</span>tv <span class="op">=</span> tfe_text_view_new <span class="op">();</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>GtkTextBuffer <span class="op">*</span>buffer <span class="op">=</span> gtk_text_view_get_buffer <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span></code></pre></div>
<p>The parents function <code>gtk_text_view_get_buffer</code> accesses
the GtkTextViewPrivate data (owned by <code>tv</code>). There is a
pointer, which points the GtkBuffer, in the private area and the
function returns the pointer. (Actual behavior is a bit more
complicated.)</p>
<p>TfeTextView instances inherit the ancestors functions like this.</p>
<p>A TfeTextView instance is created every time the
<code>tfe_text_view_new</code> function is called. Therefore, multiple
TfeTextView instances can exist.</p>
<h2 id="initialization-of-tfetextview-instances">Initialization of
TfeTextView instances</h2>
<p>The function <code>tfe_text_view_new</code> creates a new TfeTextView
instance.</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>GtkWidget <span class="op">*</span></span>
<span id="cb4-2"><a href="#cb4-2"></a>tfe_text_view_new <span class="op">(</span><span class="dt">void</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-3"><a href="#cb4-3"></a> <span class="cf">return</span> GTK_WIDGET <span class="op">(</span>g_object_new <span class="op">(</span>TFE_TYPE_TEXT_VIEW<span class="op">,</span> <span class="st">&quot;wrap-mode&quot;</span><span class="op">,</span> GTK_WRAP_WORD_CHAR<span class="op">,</span> NULL<span class="op">));</span></span>
<span id="cb4-4"><a href="#cb4-4"></a><span class="op">}</span></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>When the instance is created, GtkWidgetPrivate and
GtkTextViewPrivate structures are also created</li>
<li>Initializes GObject (GInitiallyUnowned) part in the TfeTextView
instance.</li>
<li>Initializes GtkWidget part (the first <code>priv</code>) in the
TfeTextView instance and GtkWidgetPrivate structure.</li>
<li>Initializes GtkTextView part (the second <code>priv</code>) in the
TfeTextView instance and GtkTextViewPrivate structure.</li>
<li>Initializes TfeTextView part (<code>file</code>) in the TfeTextView
instance.</li>
</ol>
<p>The step two through four 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="cb5"><pre
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>tfe_text_view_init <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-3"><a href="#cb5-3"></a> tv<span class="op">-&gt;</span>file <span class="op">=</span> NULL<span class="op">;</span></span>
<span id="cb5-4"><a href="#cb5-4"></a><span class="op">}</span></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). Instances are memory of C structure, which are
described in the previous two subsections. Each object can have more
than one instance. Those instances have the same structure. Instances
just have data. Therefore, it doesnt define objects behavior. We need
at least two things. One is functions and the other is class
methods.</p>
<p>Youve already seen many functions. For example,</p>
<ul>
<li><code>TfeTextView *tfe_text_view_new (void);</code> is a function to
create a TfeTextView instance.</li>
<li><code>GtkTextBuffer *gtk_text_view_get_buffer (GtkTextView *textview)</code>
is a function to get a GtkTextBuffer from GtkTextView.</li>
</ul>
<p>Functions are public, which means that they are expected to be used
by other objects. They are similar to public methods in object oriented
languages.</p>
<p>Class (C structure) mainly consists of pointers to functions. The
functions are called class methods and 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="cb6"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb6-1"><a href="#cb6-1"></a><span class="kw">typedef</span> <span class="kw">struct</span> _GObjectClass GObjectClass<span class="op">;</span></span>
<span id="cb6-2"><a href="#cb6-2"></a><span class="kw">typedef</span> <span class="kw">struct</span> _GObjectClass GInitiallyUnownedClass<span class="op">;</span></span>
<span id="cb6-3"><a href="#cb6-3"></a></span>
<span id="cb6-4"><a href="#cb6-4"></a><span class="kw">struct</span> _GObjectClass</span>
<span id="cb6-5"><a href="#cb6-5"></a><span class="op">{</span></span>
<span id="cb6-6"><a href="#cb6-6"></a> GTypeClass g_type_class<span class="op">;</span></span>
<span id="cb6-7"><a href="#cb6-7"></a></span>
<span id="cb6-8"><a href="#cb6-8"></a> <span class="co">/*&lt; private &gt;*/</span></span>
<span id="cb6-9"><a href="#cb6-9"></a> GSList <span class="op">*</span>construct_properties<span class="op">;</span></span>
<span id="cb6-10"><a href="#cb6-10"></a></span>
<span id="cb6-11"><a href="#cb6-11"></a> <span class="co">/*&lt; public &gt;*/</span></span>
<span id="cb6-12"><a href="#cb6-12"></a> <span class="co">/* seldom overridden */</span></span>
<span id="cb6-13"><a href="#cb6-13"></a> GObject<span class="op">*</span> <span class="op">(*</span>constructor<span class="op">)</span> <span class="op">(</span>GType type<span class="op">,</span></span>
<span id="cb6-14"><a href="#cb6-14"></a> guint n_construct_properties<span class="op">,</span></span>
<span id="cb6-15"><a href="#cb6-15"></a> GObjectConstructParam <span class="op">*</span>construct_properties<span class="op">);</span></span>
<span id="cb6-16"><a href="#cb6-16"></a> <span class="co">/* overridable methods */</span></span>
<span id="cb6-17"><a href="#cb6-17"></a> <span class="dt">void</span> <span class="op">(*</span>set_property<span class="op">)</span> <span class="op">(</span>GObject <span class="op">*</span>object<span class="op">,</span></span>
<span id="cb6-18"><a href="#cb6-18"></a> guint property_id<span class="op">,</span></span>
<span id="cb6-19"><a href="#cb6-19"></a> <span class="dt">const</span> GValue <span class="op">*</span>value<span class="op">,</span></span>
<span id="cb6-20"><a href="#cb6-20"></a> GParamSpec <span class="op">*</span>pspec<span class="op">);</span></span>
<span id="cb6-21"><a href="#cb6-21"></a> <span class="dt">void</span> <span class="op">(*</span>get_property<span class="op">)</span> <span class="op">(</span>GObject <span class="op">*</span>object<span class="op">,</span></span>
<span id="cb6-22"><a href="#cb6-22"></a> guint property_id<span class="op">,</span></span>
<span id="cb6-23"><a href="#cb6-23"></a> GValue <span class="op">*</span>value<span class="op">,</span></span>
<span id="cb6-24"><a href="#cb6-24"></a> GParamSpec <span class="op">*</span>pspec<span class="op">);</span></span>
<span id="cb6-25"><a href="#cb6-25"></a> <span class="dt">void</span> <span class="op">(*</span>dispose<span class="op">)</span> <span class="op">(</span>GObject <span class="op">*</span>object<span class="op">);</span></span>
<span id="cb6-26"><a href="#cb6-26"></a> <span class="dt">void</span> <span class="op">(*</span>finalize<span class="op">)</span> <span class="op">(</span>GObject <span class="op">*</span>object<span class="op">);</span></span>
<span id="cb6-27"><a href="#cb6-27"></a> <span class="co">/* seldom overridden */</span></span>
<span id="cb6-28"><a href="#cb6-28"></a> <span class="dt">void</span> <span class="op">(*</span>dispatch_properties_changed<span class="op">)</span> <span class="op">(</span>GObject <span class="op">*</span>object<span class="op">,</span></span>
<span id="cb6-29"><a href="#cb6-29"></a> guint n_pspecs<span class="op">,</span></span>
<span id="cb6-30"><a href="#cb6-30"></a> GParamSpec <span class="op">**</span>pspecs<span class="op">);</span></span>
<span id="cb6-31"><a href="#cb6-31"></a> <span class="co">/* signals */</span></span>
<span id="cb6-32"><a href="#cb6-32"></a> <span class="dt">void</span> <span class="op">(*</span>notify<span class="op">)</span> <span class="op">(</span>GObject <span class="op">*</span>object<span class="op">,</span></span>
<span id="cb6-33"><a href="#cb6-33"></a> GParamSpec <span class="op">*</span>pspec<span class="op">);</span></span>
<span id="cb6-34"><a href="#cb6-34"></a></span>
<span id="cb6-35"><a href="#cb6-35"></a> <span class="co">/* called when done constructing */</span></span>
<span id="cb6-36"><a href="#cb6-36"></a> <span class="dt">void</span> <span class="op">(*</span>constructed<span class="op">)</span> <span class="op">(</span>GObject <span class="op">*</span>object<span class="op">);</span></span>
<span id="cb6-37"><a href="#cb6-37"></a></span>
<span id="cb6-38"><a href="#cb6-38"></a> <span class="co">/*&lt; private &gt;*/</span></span>
<span id="cb6-39"><a href="#cb6-39"></a> gsize flags<span class="op">;</span></span>
<span id="cb6-40"><a href="#cb6-40"></a></span>
<span id="cb6-41"><a href="#cb6-41"></a> gsize n_construct_properties<span class="op">;</span></span>
<span id="cb6-42"><a href="#cb6-42"></a></span>
<span id="cb6-43"><a href="#cb6-43"></a> gpointer pspecs<span class="op">;</span></span>
<span id="cb6-44"><a href="#cb6-44"></a> gsize n_pspecs<span class="op">;</span></span>
<span id="cb6-45"><a href="#cb6-45"></a></span>
<span id="cb6-46"><a href="#cb6-46"></a> <span class="co">/* padding */</span></span>
<span id="cb6-47"><a href="#cb6-47"></a> gpointer pdummy<span class="op">[</span><span class="dv">3</span><span class="op">];</span></span>
<span id="cb6-48"><a href="#cb6-48"></a><span class="op">};</span></span></code></pre></div>
<p>Theres a pointer to the function <code>dispose</code> in line
23.</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><span class="dt">void</span> <span class="op">(*</span>dispose<span class="op">)</span> <span class="op">(</span>GObject <span class="op">*</span>object<span class="op">);</span></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="cb8"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> <span class="op">(*</span>finalize<span class="op">)</span> <span class="op">(</span>GObject <span class="op">*</span>object<span class="op">);</span></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>13: A function pointed by <code>constructor</code> is called when
the instance is generated. It completes the initialization of the
instance.</li>
<li>25: 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>26: 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> <h2 id="tfetextview-class">TfeTextView class</h2>
<p>TfeTextView class is a structure and it includes all its ancestors class in it.</p> <p>TfeTextView class is a structure and it includes all its ancestors
<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> classes in it. Therefore, classes have similar hierarchy to
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true"></a><span class="kw">struct</span> _TfeTextView {</span> instances.</p>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true"></a> GtkTextView parent;</span> <pre><code>GObjectClass (GInitiallyUnownedClass) -- GtkWidgetClass -- GtkTextViewClass -- TfeTextViewClass</code></pre>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true"></a> GFile *file;</span> <p>The following is extracted from the source codes (not exactly the
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true"></a>};</span></code></pre></div> same).</p>
<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> <div class="sourceCode" id="cb10"><pre
<pre><code>GObject -- GInitiallyUnowned -- GtkWidget -- GtkTextView -- TfeTextView</code></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>
<p>The following is extracts from the source files (not exactly the same).</p> <span id="cb10-2"><a href="#cb10-2"></a><span class="op">{</span></span>
<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-3"><a href="#cb10-3"></a> GInitiallyUnownedClass parent_class<span class="op">;</span></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-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-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-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-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-8"><a href="#cb10-8"></a> <span class="dt">void</span> <span class="op">(*</span> show<span class="op">)</span> <span class="op">(</span>GtkWidget <span class="op">*</span>widget<span class="op">);</span></span>
<span id="cb10-9"><a href="#cb10-9"></a> <span class="dt">void</span> (* hide) (GtkWidget *widget);</span> <span id="cb10-9"><a href="#cb10-9"></a> <span class="dt">void</span> <span class="op">(*</span> hide<span class="op">)</span> <span class="op">(</span>GtkWidget <span class="op">*</span>widget<span class="op">);</span></span>
<span id="cb10-10"><a href="#cb10-10"></a> <span class="dt">void</span> (* map) (GtkWidget *widget);</span> <span id="cb10-10"><a href="#cb10-10"></a> <span class="dt">void</span> <span class="op">(*</span> map<span class="op">)</span> <span class="op">(</span>GtkWidget <span class="op">*</span>widget<span class="op">);</span></span>
<span id="cb10-11"><a href="#cb10-11"></a> <span class="dt">void</span> (* unmap) (GtkWidget *widget);</span> <span id="cb10-11"><a href="#cb10-11"></a> <span class="dt">void</span> <span class="op">(*</span> unmap<span class="op">)</span> <span class="op">(</span>GtkWidget <span class="op">*</span>widget<span class="op">);</span></span>
<span id="cb10-12"><a href="#cb10-12"></a> <span class="dt">void</span> (* realize) (GtkWidget *widget);</span> <span id="cb10-12"><a href="#cb10-12"></a> <span class="dt">void</span> <span class="op">(*</span> realize<span class="op">)</span> <span class="op">(</span>GtkWidget <span class="op">*</span>widget<span class="op">);</span></span>
<span id="cb10-13"><a href="#cb10-13"></a> <span class="dt">void</span> (* unrealize) (GtkWidget *widget);</span> <span id="cb10-13"><a href="#cb10-13"></a> <span class="dt">void</span> <span class="op">(*</span> unrealize<span class="op">)</span> <span class="op">(</span>GtkWidget <span class="op">*</span>widget<span class="op">);</span></span>
<span id="cb10-14"><a href="#cb10-14"></a> <span class="dt">void</span> (* root) (GtkWidget *widget);</span> <span id="cb10-14"><a href="#cb10-14"></a> <span class="dt">void</span> <span class="op">(*</span> root<span class="op">)</span> <span class="op">(</span>GtkWidget <span class="op">*</span>widget<span class="op">);</span></span>
<span id="cb10-15"><a href="#cb10-15"></a> <span class="dt">void</span> (* unroot) (GtkWidget *widget);</span> <span id="cb10-15"><a href="#cb10-15"></a> <span class="dt">void</span> <span class="op">(*</span> unroot<span class="op">)</span> <span class="op">(</span>GtkWidget <span class="op">*</span>widget<span class="op">);</span></span>
<span id="cb10-16"><a href="#cb10-16"></a> <span class="dt">void</span> (* size_allocate) (GtkWidget *widget,</span> <span id="cb10-16"><a href="#cb10-16"></a> <span class="dt">void</span> <span class="op">(*</span> size_allocate<span class="op">)</span> <span class="op">(</span>GtkWidget <span class="op">*</span>widget<span class="op">,</span></span>
<span id="cb10-17"><a href="#cb10-17"></a> <span class="dt">int</span> width,</span> <span id="cb10-17"><a href="#cb10-17"></a> <span class="dt">int</span> width<span class="op">,</span></span>
<span id="cb10-18"><a href="#cb10-18"></a> <span class="dt">int</span> height,</span> <span id="cb10-18"><a href="#cb10-18"></a> <span class="dt">int</span> height<span class="op">,</span></span>
<span id="cb10-19"><a href="#cb10-19"></a> <span class="dt">int</span> baseline);</span> <span id="cb10-19"><a href="#cb10-19"></a> <span class="dt">int</span> baseline<span class="op">);</span></span>
<span id="cb10-20"><a href="#cb10-20"></a> <span class="dt">void</span> (* state_flags_changed) (GtkWidget *widget,</span> <span id="cb10-20"><a href="#cb10-20"></a> <span class="dt">void</span> <span class="op">(*</span> state_flags_changed<span class="op">)</span> <span class="op">(</span>GtkWidget <span class="op">*</span>widget<span class="op">,</span></span>
<span id="cb10-21"><a href="#cb10-21"></a> GtkStateFlags previous_state_flags);</span> <span id="cb10-21"><a href="#cb10-21"></a> GtkStateFlags previous_state_flags<span class="op">);</span></span>
<span id="cb10-22"><a href="#cb10-22"></a> <span class="dt">void</span> (* direction_changed) (GtkWidget *widget,</span> <span id="cb10-22"><a href="#cb10-22"></a> <span class="dt">void</span> <span class="op">(*</span> direction_changed<span class="op">)</span> <span class="op">(</span>GtkWidget <span class="op">*</span>widget<span class="op">,</span></span>
<span id="cb10-23"><a href="#cb10-23"></a> GtkTextDirection previous_direction);</span> <span id="cb10-23"><a href="#cb10-23"></a> GtkTextDirection previous_direction<span class="op">);</span></span>
<span id="cb10-24"><a href="#cb10-24"></a></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-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-26"><a href="#cb10-26"></a> GtkSizeRequestMode <span class="op">(*</span> get_request_mode<span class="op">)</span> <span class="op">(</span>GtkWidget <span class="op">*</span>widget<span class="op">);</span></span>
<span id="cb10-27"><a href="#cb10-27"></a> <span class="dt">void</span> (* measure) (GtkWidget *widget,</span> <span id="cb10-27"><a href="#cb10-27"></a> <span class="dt">void</span> <span class="op">(*</span> measure<span class="op">)</span> <span class="op">(</span>GtkWidget <span class="op">*</span>widget<span class="op">,</span></span>
<span id="cb10-28"><a href="#cb10-28"></a> GtkOrientation orientation,</span> <span id="cb10-28"><a href="#cb10-28"></a> GtkOrientation orientation<span class="op">,</span></span>
<span id="cb10-29"><a href="#cb10-29"></a> <span class="dt">int</span> for_size,</span> <span id="cb10-29"><a href="#cb10-29"></a> <span class="dt">int</span> for_size<span class="op">,</span></span>
<span id="cb10-30"><a href="#cb10-30"></a> <span class="dt">int</span> *minimum,</span> <span id="cb10-30"><a href="#cb10-30"></a> <span class="dt">int</span> <span class="op">*</span>minimum<span class="op">,</span></span>
<span id="cb10-31"><a href="#cb10-31"></a> <span class="dt">int</span> *natural,</span> <span id="cb10-31"><a href="#cb10-31"></a> <span class="dt">int</span> <span class="op">*</span>natural<span class="op">,</span></span>
<span id="cb10-32"><a href="#cb10-32"></a> <span class="dt">int</span> *minimum_baseline,</span> <span id="cb10-32"><a href="#cb10-32"></a> <span class="dt">int</span> <span class="op">*</span>minimum_baseline<span class="op">,</span></span>
<span id="cb10-33"><a href="#cb10-33"></a> <span class="dt">int</span> *natural_baseline);</span> <span id="cb10-33"><a href="#cb10-33"></a> <span class="dt">int</span> <span class="op">*</span>natural_baseline<span class="op">);</span></span>
<span id="cb10-34"><a href="#cb10-34"></a></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-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-36"><a href="#cb10-36"></a> gboolean <span class="op">(*</span> mnemonic_activate<span class="op">)</span> <span class="op">(</span>GtkWidget <span class="op">*</span>widget<span class="op">,</span></span>
<span id="cb10-37"><a href="#cb10-37"></a> gboolean group_cycling);</span> <span id="cb10-37"><a href="#cb10-37"></a> gboolean group_cycling<span class="op">);</span></span>
<span id="cb10-38"><a href="#cb10-38"></a></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-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-40"><a href="#cb10-40"></a> gboolean <span class="op">(*</span> grab_focus<span class="op">)</span> <span class="op">(</span>GtkWidget <span class="op">*</span>widget<span class="op">);</span></span>
<span id="cb10-41"><a href="#cb10-41"></a> gboolean (* focus) (GtkWidget *widget,</span> <span id="cb10-41"><a href="#cb10-41"></a> gboolean <span class="op">(*</span> focus<span class="op">)</span> <span class="op">(</span>GtkWidget <span class="op">*</span>widget<span class="op">,</span></span>
<span id="cb10-42"><a href="#cb10-42"></a> GtkDirectionType direction);</span> <span id="cb10-42"><a href="#cb10-42"></a> GtkDirectionType direction<span class="op">);</span></span>
<span id="cb10-43"><a href="#cb10-43"></a> <span class="dt">void</span> (* set_focus_child) (GtkWidget *widget,</span> <span id="cb10-43"><a href="#cb10-43"></a> <span class="dt">void</span> <span class="op">(*</span> set_focus_child<span class="op">)</span> <span class="op">(</span>GtkWidget <span class="op">*</span>widget<span class="op">,</span></span>
<span id="cb10-44"><a href="#cb10-44"></a> GtkWidget *child);</span> <span id="cb10-44"><a href="#cb10-44"></a> GtkWidget <span class="op">*</span>child<span class="op">);</span></span>
<span id="cb10-45"><a href="#cb10-45"></a></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-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-47"><a href="#cb10-47"></a> <span class="dt">void</span> <span class="op">(*</span> move_focus<span class="op">)</span> <span class="op">(</span>GtkWidget <span class="op">*</span>widget<span class="op">,</span></span>
<span id="cb10-48"><a href="#cb10-48"></a> GtkDirectionType direction);</span> <span id="cb10-48"><a href="#cb10-48"></a> GtkDirectionType direction<span class="op">);</span></span>
<span id="cb10-49"><a href="#cb10-49"></a> gboolean (* keynav_failed) (GtkWidget *widget,</span> <span id="cb10-49"><a href="#cb10-49"></a> gboolean <span class="op">(*</span> keynav_failed<span class="op">)</span> <span class="op">(</span>GtkWidget <span class="op">*</span>widget<span class="op">,</span></span>
<span id="cb10-50"><a href="#cb10-50"></a> GtkDirectionType direction);</span> <span id="cb10-50"><a href="#cb10-50"></a> GtkDirectionType direction<span class="op">);</span></span>
<span id="cb10-51"><a href="#cb10-51"></a></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-52"><a href="#cb10-52"></a> gboolean <span class="op">(*</span> query_tooltip<span class="op">)</span> <span class="op">(</span>GtkWidget <span class="op">*</span>widget<span class="op">,</span></span>
<span id="cb10-53"><a href="#cb10-53"></a> <span class="dt">int</span> x,</span> <span id="cb10-53"><a href="#cb10-53"></a> <span class="dt">int</span> x<span class="op">,</span></span>
<span id="cb10-54"><a href="#cb10-54"></a> <span class="dt">int</span> y,</span> <span id="cb10-54"><a href="#cb10-54"></a> <span class="dt">int</span> y<span class="op">,</span></span>
<span id="cb10-55"><a href="#cb10-55"></a> gboolean keyboard_tooltip,</span> <span id="cb10-55"><a href="#cb10-55"></a> gboolean keyboard_tooltip<span class="op">,</span></span>
<span id="cb10-56"><a href="#cb10-56"></a> GtkTooltip *tooltip);</span> <span id="cb10-56"><a href="#cb10-56"></a> GtkTooltip <span class="op">*</span>tooltip<span class="op">);</span></span>
<span id="cb10-57"><a href="#cb10-57"></a></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-58"><a href="#cb10-58"></a> <span class="dt">void</span> <span class="op">(*</span> compute_expand<span class="op">)</span> <span class="op">(</span>GtkWidget <span class="op">*</span>widget<span class="op">,</span></span>
<span id="cb10-59"><a href="#cb10-59"></a> gboolean *hexpand_p,</span> <span id="cb10-59"><a href="#cb10-59"></a> gboolean <span class="op">*</span>hexpand_p<span class="op">,</span></span>
<span id="cb10-60"><a href="#cb10-60"></a> gboolean *vexpand_p);</span> <span id="cb10-60"><a href="#cb10-60"></a> gboolean <span class="op">*</span>vexpand_p<span class="op">);</span></span>
<span id="cb10-61"><a href="#cb10-61"></a></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-62"><a href="#cb10-62"></a> <span class="dt">void</span> <span class="op">(*</span> css_changed<span class="op">)</span> <span class="op">(</span>GtkWidget <span class="op">*</span>widget<span class="op">,</span></span>
<span id="cb10-63"><a href="#cb10-63"></a> GtkCssStyleChange *change);</span> <span id="cb10-63"><a href="#cb10-63"></a> GtkCssStyleChange <span class="op">*</span>change<span class="op">);</span></span>
<span id="cb10-64"><a href="#cb10-64"></a></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-65"><a href="#cb10-65"></a> <span class="dt">void</span> <span class="op">(*</span> system_setting_changed<span class="op">)</span> <span class="op">(</span>GtkWidget <span class="op">*</span>widget<span class="op">,</span></span>
<span id="cb10-66"><a href="#cb10-66"></a> GtkSystemSetting settings);</span> <span id="cb10-66"><a href="#cb10-66"></a> GtkSystemSetting settings<span class="op">);</span></span>
<span id="cb10-67"><a href="#cb10-67"></a></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-68"><a href="#cb10-68"></a> <span class="dt">void</span> <span class="op">(*</span> snapshot<span class="op">)</span> <span class="op">(</span>GtkWidget <span class="op">*</span>widget<span class="op">,</span></span>
<span id="cb10-69"><a href="#cb10-69"></a> GtkSnapshot *snapshot);</span> <span id="cb10-69"><a href="#cb10-69"></a> GtkSnapshot <span class="op">*</span>snapshot<span class="op">);</span></span>
<span id="cb10-70"><a href="#cb10-70"></a></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-71"><a href="#cb10-71"></a> gboolean <span class="op">(*</span> contains<span class="op">)</span> <span class="op">(</span>GtkWidget <span class="op">*</span>widget<span class="op">,</span></span>
<span id="cb10-72"><a href="#cb10-72"></a> <span class="dt">double</span> x,</span> <span id="cb10-72"><a href="#cb10-72"></a> <span class="dt">double</span> x<span class="op">,</span></span>
<span id="cb10-73"><a href="#cb10-73"></a> <span class="dt">double</span> y);</span> <span id="cb10-73"><a href="#cb10-73"></a> <span class="dt">double</span> y<span class="op">);</span></span>
<span id="cb10-74"><a href="#cb10-74"></a></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-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-76"><a href="#cb10-76"></a></span>
<span id="cb10-77"><a href="#cb10-77"></a> GtkWidgetClassPrivate *priv;</span> <span id="cb10-77"><a href="#cb10-77"></a> GtkWidgetClassPrivate <span class="op">*</span>priv<span class="op">;</span></span>
<span id="cb10-78"><a href="#cb10-78"></a></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-79"><a href="#cb10-79"></a> gpointer padding<span class="op">[</span><span class="dv">8</span><span class="op">];</span></span>
<span id="cb10-80"><a href="#cb10-80"></a>};</span> <span id="cb10-80"><a href="#cb10-80"></a><span class="op">};</span></span>
<span id="cb10-81"><a href="#cb10-81"></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-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-83"><a href="#cb10-83"></a><span class="op">{</span></span>
<span id="cb10-84"><a href="#cb10-84"></a> GtkWidgetClass parent_class;</span> <span id="cb10-84"><a href="#cb10-84"></a> GtkWidgetClass parent_class<span class="op">;</span></span>
<span id="cb10-85"><a href="#cb10-85"></a></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-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-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-88"><a href="#cb10-88"></a> <span class="dt">void</span> <span class="op">(*</span> move_cursor<span class="op">)</span> <span class="op">(</span>GtkTextView <span class="op">*</span>text_view<span class="op">,</span></span>
<span id="cb10-89"><a href="#cb10-89"></a> GtkMovementStep step,</span> <span id="cb10-89"><a href="#cb10-89"></a> GtkMovementStep step<span class="op">,</span></span>
<span id="cb10-90"><a href="#cb10-90"></a> <span class="dt">int</span> count,</span> <span id="cb10-90"><a href="#cb10-90"></a> <span class="dt">int</span> count<span class="op">,</span></span>
<span id="cb10-91"><a href="#cb10-91"></a> gboolean extend_selection);</span> <span id="cb10-91"><a href="#cb10-91"></a> gboolean extend_selection<span class="op">);</span></span>
<span id="cb10-92"><a href="#cb10-92"></a> <span class="dt">void</span> (* set_anchor) (GtkTextView *text_view);</span> <span id="cb10-92"><a href="#cb10-92"></a> <span class="dt">void</span> <span class="op">(*</span> set_anchor<span class="op">)</span> <span class="op">(</span>GtkTextView <span class="op">*</span>text_view<span class="op">);</span></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-93"><a href="#cb10-93"></a> <span class="dt">void</span> <span class="op">(*</span> insert_at_cursor<span class="op">)</span> <span class="op">(</span>GtkTextView <span class="op">*</span>text_view<span class="op">,</span></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-94"><a href="#cb10-94"></a> <span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span>str<span class="op">);</span></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-95"><a href="#cb10-95"></a> <span class="dt">void</span> <span class="op">(*</span> delete_from_cursor<span class="op">)</span> <span class="op">(</span>GtkTextView <span class="op">*</span>text_view<span class="op">,</span></span>
<span id="cb10-96"><a href="#cb10-96"></a> GtkDeleteType type,</span> <span id="cb10-96"><a href="#cb10-96"></a> GtkDeleteType type<span class="op">,</span></span>
<span id="cb10-97"><a href="#cb10-97"></a> <span class="dt">int</span> count);</span> <span id="cb10-97"><a href="#cb10-97"></a> <span class="dt">int</span> count<span class="op">);</span></span>
<span id="cb10-98"><a href="#cb10-98"></a> <span class="dt">void</span> (* backspace) (GtkTextView *text_view);</span> <span id="cb10-98"><a href="#cb10-98"></a> <span class="dt">void</span> <span class="op">(*</span> backspace<span class="op">)</span> <span class="op">(</span>GtkTextView <span class="op">*</span>text_view<span class="op">);</span></span>
<span id="cb10-99"><a href="#cb10-99"></a> <span class="dt">void</span> (* cut_clipboard) (GtkTextView *text_view);</span> <span id="cb10-99"><a href="#cb10-99"></a> <span class="dt">void</span> <span class="op">(*</span> cut_clipboard<span class="op">)</span> <span class="op">(</span>GtkTextView <span class="op">*</span>text_view<span class="op">);</span></span>
<span id="cb10-100"><a href="#cb10-100"></a> <span class="dt">void</span> (* copy_clipboard) (GtkTextView *text_view);</span> <span id="cb10-100"><a href="#cb10-100"></a> <span class="dt">void</span> <span class="op">(*</span> copy_clipboard<span class="op">)</span> <span class="op">(</span>GtkTextView <span class="op">*</span>text_view<span class="op">);</span></span>
<span id="cb10-101"><a href="#cb10-101"></a> <span class="dt">void</span> (* paste_clipboard) (GtkTextView *text_view);</span> <span id="cb10-101"><a href="#cb10-101"></a> <span class="dt">void</span> <span class="op">(*</span> paste_clipboard<span class="op">)</span> <span class="op">(</span>GtkTextView <span class="op">*</span>text_view<span class="op">);</span></span>
<span id="cb10-102"><a href="#cb10-102"></a> <span class="dt">void</span> (* toggle_overwrite) (GtkTextView *text_view);</span> <span id="cb10-102"><a href="#cb10-102"></a> <span class="dt">void</span> <span class="op">(*</span> toggle_overwrite<span class="op">)</span> <span class="op">(</span>GtkTextView <span class="op">*</span>text_view<span class="op">);</span></span>
<span id="cb10-103"><a href="#cb10-103"></a> GtkTextBuffer * (* create_buffer) (GtkTextView *text_view);</span> <span id="cb10-103"><a href="#cb10-103"></a> GtkTextBuffer <span class="op">*</span> <span class="op">(*</span> create_buffer<span class="op">)</span> <span class="op">(</span>GtkTextView <span class="op">*</span>text_view<span class="op">);</span></span>
<span id="cb10-104"><a href="#cb10-104"></a> <span class="dt">void</span> (* snapshot_layer) (GtkTextView *text_view,</span> <span id="cb10-104"><a href="#cb10-104"></a> <span class="dt">void</span> <span class="op">(*</span> snapshot_layer<span class="op">)</span> <span class="op">(</span>GtkTextView <span class="op">*</span>text_view<span class="op">,</span></span>
<span id="cb10-105"><a href="#cb10-105"></a> GtkTextViewLayer layer,</span> <span id="cb10-105"><a href="#cb10-105"></a> GtkTextViewLayer layer<span class="op">,</span></span>
<span id="cb10-106"><a href="#cb10-106"></a> GtkSnapshot *snapshot);</span> <span id="cb10-106"><a href="#cb10-106"></a> GtkSnapshot <span class="op">*</span>snapshot<span class="op">);</span></span>
<span id="cb10-107"><a href="#cb10-107"></a> gboolean (* extend_selection) (GtkTextView *text_view,</span> <span id="cb10-107"><a href="#cb10-107"></a> gboolean <span class="op">(*</span> extend_selection<span class="op">)</span> <span class="op">(</span>GtkTextView <span class="op">*</span>text_view<span class="op">,</span></span>
<span id="cb10-108"><a href="#cb10-108"></a> GtkTextExtendSelection granularity,</span> <span id="cb10-108"><a href="#cb10-108"></a> GtkTextExtendSelection granularity<span class="op">,</span></span>
<span id="cb10-109"><a href="#cb10-109"></a> <span class="dt">const</span> GtkTextIter *location,</span> <span id="cb10-109"><a href="#cb10-109"></a> <span class="dt">const</span> GtkTextIter <span class="op">*</span>location<span class="op">,</span></span>
<span id="cb10-110"><a href="#cb10-110"></a> GtkTextIter *start,</span> <span id="cb10-110"><a href="#cb10-110"></a> GtkTextIter <span class="op">*</span>start<span class="op">,</span></span>
<span id="cb10-111"><a href="#cb10-111"></a> GtkTextIter *end);</span> <span id="cb10-111"><a href="#cb10-111"></a> GtkTextIter <span class="op">*</span>end<span class="op">);</span></span>
<span id="cb10-112"><a href="#cb10-112"></a> <span class="dt">void</span> (* insert_emoji) (GtkTextView *text_view);</span> <span id="cb10-112"><a href="#cb10-112"></a> <span class="dt">void</span> <span class="op">(*</span> insert_emoji<span class="op">)</span> <span class="op">(</span>GtkTextView <span class="op">*</span>text_view<span class="op">);</span></span>
<span id="cb10-113"><a href="#cb10-113"></a></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-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-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-116"><a href="#cb10-116"></a> gpointer padding<span class="op">[</span><span class="dv">8</span><span class="op">];</span></span>
<span id="cb10-117"><a href="#cb10-117"></a>};</span> <span id="cb10-117"><a href="#cb10-117"></a><span class="op">};</span></span>
<span id="cb10-118"><a href="#cb10-118"></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-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-120"><a href="#cb10-120"></a><span class="kw">typedef</span> <span class="kw">struct</span> <span class="op">{</span></span>
<span id="cb10-121"><a href="#cb10-121"></a> GtkTextView parent_class;</span> <span id="cb10-121"><a href="#cb10-121"></a> GtkTextView parent_class<span class="op">;</span></span>
<span id="cb10-122"><a href="#cb10-122"></a>} TfeTextViewClass;</span></code></pre></div> <span id="cb10-122"><a href="#cb10-122"></a><span class="op">}</span> TfeTextViewClass<span class="op">;</span></span></code></pre></div>
<ul> <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>120-122: This three lines are generated by the macro
<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> <code>G_DECLARE_FINAL_TYPE</code>. So, they are not written in either
<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> <code>tfe_text_view.h</code> or <code>tfe_text_view.c</code>.</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> <li>3, 84, 121: Each class has 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>finalize</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> </ul>
<p>TfeTextViewClass includes its ancestors class in it. It is illustrated in the following diagram.</p> <p>TfeTextViewClass includes its ancestors class in it. It is
illustrated in the following diagram.</p>
<figure> <figure>
<img src="image/TfeTextViewClass.png" alt="" /><figcaption>The structure of TfeTextView Class</figcaption> <img src="image/TfeTextViewClass.png"
alt="The structure of TfeTextView Class" />
<figcaption aria-hidden="true">The structure of TfeTextView
Class</figcaption>
</figure> </figure>
<h2 id="destruction-of-tfetextview">Destruction of TfeTextView</h2> <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>Every Object derived from GObject has a reference count. If an object
<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> 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> <figure>
<img src="image/refcount.png" alt="" /><figcaption>Reference count of B</figcaption> <img src="image/refcount.png" alt="Reference count of B" />
<figcaption aria-hidden="true">Reference count of B</figcaption>
</figure> </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>The idea above is based on an assumption that an object referred by
<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> nothing has reference count of zero. When the reference count drops to
<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> zero, the object starts its destruction process. The destruction process
<span id="cb11-2"><a href="#cb11-2"></a>tfe_text_view_dispose (GObject *gobject) {</span> is split into two phases: disposing and finalizing. In the disposing
<span id="cb11-3"><a href="#cb11-3"></a> TfeTextView *tv = TFE_TEXT_VIEW (gobject);</span> process, the object invokes the function pointed by <code>dispose</code>
in its class to release all references to other instances. After that,
it invokes the function pointed by <code>finalize</code> in its class to
complete the destruction process. For example, dispose handler or
dispose method.</p>
<p>In the destruction process, TfeTextView needs to unref the GFile
pointed by <code>tv-&gt;file</code>. You must write 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 <span class="op">(</span>GObject <span class="op">*</span>gobject<span class="op">)</span> <span class="op">{</span></span>
<span id="cb11-3"><a href="#cb11-3"></a> TfeTextView <span class="op">*</span>tv <span class="op">=</span> TFE_TEXT_VIEW <span class="op">(</span>gobject<span class="op">);</span></span>
<span id="cb11-4"><a href="#cb11-4"></a></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-5"><a href="#cb11-5"></a> <span class="cf">if</span> <span class="op">(</span>G_IS_FILE <span class="op">(</span>tv<span class="op">-&gt;</span>file<span class="op">))</span></span>
<span id="cb11-6"><a href="#cb11-6"></a> g_clear_object (&amp;tv-&gt;file);</span> <span id="cb11-6"><a href="#cb11-6"></a> g_clear_object <span class="op">(&amp;</span>tv<span class="op">-&gt;</span>file<span class="op">);</span></span>
<span id="cb11-7"><a href="#cb11-7"></a></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-8"><a href="#cb11-8"></a> G_OBJECT_CLASS <span class="op">(</span>tfe_text_view_parent_class<span class="op">)-&gt;</span>dispose <span class="op">(</span>gobject<span class="op">);</span></span>
<span id="cb11-9"><a href="#cb11-9"></a>}</span></code></pre></div> <span id="cb11-9"><a href="#cb11-9"></a><span class="op">}</span></span></code></pre></div>
<ul> <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>5,6: If <code>tv-&gt;file</code> points a GFile, decrease its
<li>8: invokes parents dispose handler. (This will be explained later.)</li> 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> </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> <p>In the disposing process, the object uses the pointer in its class to
<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> call the handler. Therefore, <code>tfe_text_view_dispose</code> needs to
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true"></a>tfe_text_view_class_init (TfeTextViewClass *class) {</span> be registered in the class when the TfeTextView class is initialized.
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true"></a> GObjectClass *object_class = G_OBJECT_CLASS (class);</span> The function <code>tfe_text_view_class_init</code> is the class
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true"></a></span> initialization function and it is declared in the
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true"></a> object_class-&gt;dispose = tfe_text_view_dispose;</span> <code>G_DEFINE_TYPE</code> macro expansion.</p>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true"></a></span> <div class="sourceCode" id="cb12"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true"></a>}</span></code></pre></div> <span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a>tfe_text_view_class_init <span class="op">(</span>TfeTextViewClass <span class="op">*</span>class<span class="op">)</span> <span class="op">{</span></span>
<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> <span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a> GObjectClass <span class="op">*</span>object_class <span class="op">=</span> G_OBJECT_CLASS <span class="op">(</span>class<span class="op">);</span></span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a> object_class<span class="op">-&gt;</span>dispose <span class="op">=</span> tfe_text_view_dispose<span class="op">;</span></span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></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> <figure>
<img src="image/dispose_handler.png" alt="" /><figcaption>dispose handlers</figcaption> <img src="image/dispose_handler.png" alt="dispose handlers" />
<figcaption aria-hidden="true">dispose handlers</figcaption>
</figure> </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> <p>Now, look at the <code>tfe_text_view_dispose</code> program above. It
<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> first releases the reference to GFile object pointed by
<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> <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" tabindex="-1"></a>G_OBJECT_CLASS <span class="op">(</span>tfe_text_view_parent_class<span class="op">)-&gt;</span>dispose <span class="op">(</span>gobject<span class="op">);</span></span></code></pre></div>
<p>A variable <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. The variable <code>gobject</code> is a pointer to
TfeTextView instance which is casted as a GObject instance. Therefore,
<code>G_OBJECT_CLASS (tfe_text_view_parent_class)-&gt;dispose</code>
points the handler <code>dh3</code> in the diagram above. The statement
<code>G_OBJECT_CLASS (tfe_text_view_parent_class)-&gt;dispose (gobject)</code>
is the same as <code>dh3 (gobject)</code>, which means it releases all
the reference to the other instances in the GtkTextViewPrivate in the
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> </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> <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> </body>

View file

@ -5,7 +5,7 @@
<meta name="generator" content="pandoc" /> <meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <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"> <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> <title>GTK 4 tutorial</title>
<style> <style>
code{white-space: pre-wrap;} code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;} span.smallcaps{font-variant: small-caps;}
@ -113,117 +113,184 @@
</nav> </nav>
<h1 id="signals">Signals</h1> <h1 id="signals">Signals</h1>
<h2 id="signals-1">Signals</h2> <h2 id="signals-1">Signals</h2>
<p>In Gtk programming, each object is encapsulated. And it is not recommended to use global variables because they tend to make the program complicated. So, we need something to communicate between objects. There are two ways to do so.</p> <p>Each object is encapsulated in Gtk programming. And it is not
recommended to use global variables because they are prone to make the
program complicated. So, we need something to communicate between
objects. There are two ways to do so.</p>
<ul> <ul>
<li>Functions. For example, <code>tb = gtk_text_view_get_buffer (tv)</code>. The caller requests <code>tv</code> to give <code>tb</code>, which is a GtkTextBuffer instance connected to <code>tv</code> to the caller.</li> <li>Functions. For example,
<li>Signals. For example, <code>activate</code> signal on GApplication object. When the application is activated, the signal is emitted. Then the handler, which has been connected to the signal, is invoked.</li> <code>tb = gtk_text_view_get_buffer (tv)</code>. The caller requests
<code>tv</code> to give <code>tb</code>, which is a GtkTextBuffer
instance connected to <code>tv</code>.</li>
<li>Signals. For example, <code>activate</code> signal on GApplication
object. When the application is activated, the signal is emitted. Then
the handler, which has been connected to the signal, is invoked.</li>
</ul> </ul>
<p>The caller of the function or the handler connected to the signal is usually out of the object. One of the difference between these two is that the object is active or passive. In functions the object passively responds to the caller. In signals the object actively sends a signal to the handler.</p> <p>The caller of the function or the handler connected to the signal is
usually out of the object. One of the difference between these two is
that the object is active or passive. In functions the object passively
responds to the caller. In signals the object actively sends a signal to
the handler.</p>
<p>GObject signals are registered, connected and emitted.</p> <p>GObject signals are registered, connected and emitted.</p>
<ol type="1"> <ol type="1">
<li>Signals are registered with the object type on which they are emitted. The registration is done usually when the object class is initialized.</li> <li>Signals are registered with the object type on which they are
<li>Signals are connected to handlers by <code>g_connect_signal</code> or its family functions. The connection is usually done out of the object.</li> emitted. The registration is done usually when the object class is
<li>When Signals are emitted, the connected handlers are invoked. Signal is emitted on the instance of the object.</li> initialized.</li>
<li>Signals are connected to handlers by <code>g_connect_signal</code>
or its family functions. The connection is usually done out of the
object.</li>
<li>When Signals are emitted, the connected handlers are invoked.
Signals are emitted on the instance of the object.</li>
</ol> </ol>
<h2 id="signal-registration">Signal registration</h2> <h2 id="signal-registration">Signal registration</h2>
<p>In TfeTextView, two signals are registered.</p> <p>In TfeTextView, two signals are registered.</p>
<ul> <ul>
<li>“change-file” signal. This signal is emitted when <code>tv-&gt;file</code> is changed.</li> <li>“change-file” signal. This signal is emitted when
<li>“open-response” signal. <code>tfe_text_view_open</code> function is not able to return the status because it uses GtkFileChooserDialog. This signal is emitted instead of the return value of the function.</li> <code>tv-&gt;file</code> is changed.</li>
<li>“open-response” signal. <code>tfe_text_view_open</code> function is
not able to return the status because it uses GtkFileChooserDialog. This
signal is emitted instead of the return value of the function.</li>
</ul> </ul>
<p>A static variable or array is used to store the signal ID. A static array is used to register two or more signals.</p> <p>A static variable or array is used to store signal ID.</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="kw">enum</span> {</span> <div class="sourceCode" id="cb1"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">enum</span> <span class="op">{</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true"></a> CHANGE_FILE,</span> <span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a> CHANGE_FILE<span class="op">,</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true"></a> OPEN_RESPONSE,</span> <span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a> OPEN_RESPONSE<span class="op">,</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true"></a> NUMBER_OF_SIGNALS</span> <span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a> NUMBER_OF_SIGNALS</span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true"></a>};</span> <span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="op">};</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true"></a></span> <span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true"></a><span class="dt">static</span> guint tfe_text_view_signals[NUMBER_OF_SIGNALS];</span></code></pre></div> <span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> guint tfe_text_view_signals<span class="op">[</span>NUMBER_OF_SIGNALS<span class="op">];</span></span></code></pre></div>
<p>Signals are registered in the class initialization function.</p> <p>Signals are registered in the class initialization function.</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> <div class="sourceCode" id="cb2"><pre
<span id="cb2-2"><a href="#cb2-2"></a>tfe_text_view_class_init (TfeTextViewClass *class) {</span> 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-3"><a href="#cb2-3"></a> GObjectClass *object_class = G_OBJECT_CLASS (class);</span> <span id="cb2-2"><a href="#cb2-2"></a>tfe_text_view_class_init <span class="op">(</span>TfeTextViewClass <span class="op">*</span>class<span class="op">)</span> <span class="op">{</span></span>
<span id="cb2-3"><a href="#cb2-3"></a> GObjectClass <span class="op">*</span>object_class <span class="op">=</span> G_OBJECT_CLASS <span class="op">(</span>class<span class="op">);</span></span>
<span id="cb2-4"><a href="#cb2-4"></a></span> <span id="cb2-4"><a href="#cb2-4"></a></span>
<span id="cb2-5"><a href="#cb2-5"></a> object_class-&gt;dispose = tfe_text_view_dispose;</span> <span id="cb2-5"><a href="#cb2-5"></a> object_class<span class="op">-&gt;</span>dispose <span class="op">=</span> tfe_text_view_dispose<span class="op">;</span></span>
<span id="cb2-6"><a href="#cb2-6"></a> tfe_text_view_signals[CHANGE_FILE] = g_signal_new (<span class="st">&quot;change-file&quot;</span>,</span> <span id="cb2-6"><a href="#cb2-6"></a> tfe_text_view_signals<span class="op">[</span>CHANGE_FILE<span class="op">]</span> <span class="op">=</span> g_signal_new <span class="op">(</span><span class="st">&quot;change-file&quot;</span><span class="op">,</span></span>
<span id="cb2-7"><a href="#cb2-7"></a> G_TYPE_FROM_CLASS (class),</span> <span id="cb2-7"><a href="#cb2-7"></a> G_TYPE_FROM_CLASS <span class="op">(</span>class<span class="op">),</span></span>
<span id="cb2-8"><a href="#cb2-8"></a> G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,</span> <span id="cb2-8"><a href="#cb2-8"></a> G_SIGNAL_RUN_LAST <span class="op">|</span> G_SIGNAL_NO_RECURSE <span class="op">|</span> G_SIGNAL_NO_HOOKS<span class="op">,</span></span>
<span id="cb2-9"><a href="#cb2-9"></a> <span class="dv">0</span> <span class="co">/* class offset */</span>,</span> <span id="cb2-9"><a href="#cb2-9"></a> <span class="dv">0</span> <span class="co">/* class offset */</span><span class="op">,</span></span>
<span id="cb2-10"><a href="#cb2-10"></a> NULL <span class="co">/* accumulator */</span>,</span> <span id="cb2-10"><a href="#cb2-10"></a> NULL <span class="co">/* accumulator */</span><span class="op">,</span></span>
<span id="cb2-11"><a href="#cb2-11"></a> NULL <span class="co">/* accumulator data */</span>,</span> <span id="cb2-11"><a href="#cb2-11"></a> NULL <span class="co">/* accumulator data */</span><span class="op">,</span></span>
<span id="cb2-12"><a href="#cb2-12"></a> NULL <span class="co">/* C marshaller */</span>,</span> <span id="cb2-12"><a href="#cb2-12"></a> NULL <span class="co">/* C marshaller */</span><span class="op">,</span></span>
<span id="cb2-13"><a href="#cb2-13"></a> G_TYPE_NONE <span class="co">/* return_type */</span>,</span> <span id="cb2-13"><a href="#cb2-13"></a> G_TYPE_NONE <span class="co">/* return_type */</span><span class="op">,</span></span>
<span id="cb2-14"><a href="#cb2-14"></a> <span class="dv">0</span> <span class="co">/* n_params */</span></span> <span id="cb2-14"><a href="#cb2-14"></a> <span class="dv">0</span> <span class="co">/* n_params */</span></span>
<span id="cb2-15"><a href="#cb2-15"></a> );</span> <span id="cb2-15"><a href="#cb2-15"></a> <span class="op">);</span></span>
<span id="cb2-16"><a href="#cb2-16"></a> tfe_text_view_signals[OPEN_RESPONSE] = g_signal_new (<span class="st">&quot;open-response&quot;</span>,</span> <span id="cb2-16"><a href="#cb2-16"></a> tfe_text_view_signals<span class="op">[</span>OPEN_RESPONSE<span class="op">]</span> <span class="op">=</span> g_signal_new <span class="op">(</span><span class="st">&quot;open-response&quot;</span><span class="op">,</span></span>
<span id="cb2-17"><a href="#cb2-17"></a> G_TYPE_FROM_CLASS (class),</span> <span id="cb2-17"><a href="#cb2-17"></a> G_TYPE_FROM_CLASS <span class="op">(</span>class<span class="op">),</span></span>
<span id="cb2-18"><a href="#cb2-18"></a> G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,</span> <span id="cb2-18"><a href="#cb2-18"></a> G_SIGNAL_RUN_LAST <span class="op">|</span> G_SIGNAL_NO_RECURSE <span class="op">|</span> G_SIGNAL_NO_HOOKS<span class="op">,</span></span>
<span id="cb2-19"><a href="#cb2-19"></a> <span class="dv">0</span> <span class="co">/* class offset */</span>,</span> <span id="cb2-19"><a href="#cb2-19"></a> <span class="dv">0</span> <span class="co">/* class offset */</span><span class="op">,</span></span>
<span id="cb2-20"><a href="#cb2-20"></a> NULL <span class="co">/* accumulator */</span>,</span> <span id="cb2-20"><a href="#cb2-20"></a> NULL <span class="co">/* accumulator */</span><span class="op">,</span></span>
<span id="cb2-21"><a href="#cb2-21"></a> NULL <span class="co">/* accumulator data */</span>,</span> <span id="cb2-21"><a href="#cb2-21"></a> NULL <span class="co">/* accumulator data */</span><span class="op">,</span></span>
<span id="cb2-22"><a href="#cb2-22"></a> NULL <span class="co">/* C marshaller */</span>,</span> <span id="cb2-22"><a href="#cb2-22"></a> NULL <span class="co">/* C marshaller */</span><span class="op">,</span></span>
<span id="cb2-23"><a href="#cb2-23"></a> G_TYPE_NONE <span class="co">/* return_type */</span>,</span> <span id="cb2-23"><a href="#cb2-23"></a> G_TYPE_NONE <span class="co">/* return_type */</span><span class="op">,</span></span>
<span id="cb2-24"><a href="#cb2-24"></a> <span class="dv">1</span> <span class="co">/* n_params */</span>,</span> <span id="cb2-24"><a href="#cb2-24"></a> <span class="dv">1</span> <span class="co">/* n_params */</span><span class="op">,</span></span>
<span id="cb2-25"><a href="#cb2-25"></a> G_TYPE_INT</span> <span id="cb2-25"><a href="#cb2-25"></a> G_TYPE_INT</span>
<span id="cb2-26"><a href="#cb2-26"></a> );</span> <span id="cb2-26"><a href="#cb2-26"></a> <span class="op">);</span></span>
<span id="cb2-27"><a href="#cb2-27"></a>}</span></code></pre></div> <span id="cb2-27"><a href="#cb2-27"></a><span class="op">}</span></span></code></pre></div>
<ul> <ul>
<li>6-15: Registers “change-file” signal. <code>g_signal_new</code> function is used. The signal “change-file” has no default handler (object method handler). You usually dont need to set a default handler. If you need it, use <code>g_signal_new_class_handler</code> function. See <a href="https://docs.gtk.org/gobject/func.signal_new_class_handler.html">GObject API Reference, g_signal_new_class_handler</a> for further information.</li> <li>6-15: Registers “change-file” signal. <code>g_signal_new</code>
<li>The return value of <code>g_signal_new</code> is the signal id. The type of signal id is guint, which is the same as unsigned int. It is used in the function <code>g_signal_emit</code>.</li> function is used. The signal “change-file” has no default handler
<li>16-26: Registers “open-response” signal. This signal has a parameter.</li> (object method handler) so the offset (the line 9) is set to zero. You
<li>24: Number of the parameters. “open-response” signal has one parameter.</li> usually dont need a default handler. If you need it, use
<li>25: The type of the parameter. <code>G_TYPE_INT</code> is a type of integer. Such fundamental types are described in <a href="https://developer-old.gnome.org/gobject/stable/gobject-Type-Information.html">GObject reference manual</a>.</li> <code>g_signal_new_class_handler</code> function instead of
<code>g_signal_new</code>. See <a
href="https://docs.gtk.org/gobject/func.signal_new_class_handler.html">GObject
API Reference</a> for further information.</li>
<li>The return value of <code>g_signal_new</code> is the signal id. The
type of signal id is guint, which is the same as unsigned int. It is
used in the function <code>g_signal_emit</code>.</li>
<li>16-26: Registers “open-response” signal. This signal has a
parameter.</li>
<li>24: Number of the parameters. “open-response” signal has one
parameter.</li>
<li>25: The type of the parameter. <code>G_TYPE_INT</code> is a type of
integer. Such fundamental types are described in <a
href="https://developer-old.gnome.org/gobject/stable/gobject-Type-Information.html">GObject
reference manual</a>.</li>
</ul> </ul>
<p>The handlers are declared as follows.</p> <p>The handlers are declared as follows.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true"></a><span class="co">/* &quot;change-file&quot; signal handler */</span></span> <div class="sourceCode" id="cb3"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="co">/* &quot;change-file&quot; signal handler */</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true"></a><span class="dt">void</span></span> <span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true"></a>user_function (TfeTextView *tv,</span> <span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>user_function <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">,</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true"></a> gpointer user_data)</span> <span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a> gpointer user_data<span class="op">)</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true"></a></span> <span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true"></a><span class="co">/* &quot;open-response&quot; signal handler */</span></span> <span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a><span class="co">/* &quot;open-response&quot; signal handler */</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true"></a><span class="dt">void</span></span> <span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true"></a>user_function (TfeTextView *tv,</span> <span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>user_function <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">,</span></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true"></a> TfeTextViewOpenResponseType response-id,</span> <span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a> TfeTextViewOpenResponseType response<span class="op">-</span>id<span class="op">,</span></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true"></a> gpointer user_data)</span></code></pre></div> <span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a> gpointer user_data<span class="op">)</span></span></code></pre></div>
<ul> <ul>
<li>Because “change-file” signal doesnt have parameter, the handlers parameters are a TfeTextView instance and user data.</li> <li>Because “change-file” signal doesnt have parameter, the handlers
<li>Because “open-response” signal has one parameter, the handlers parameters are a TfeTextView instance, the signals parameter and user data.</li> parameters are a TfeTextView instance and user data.</li>
<li><code>tv</code> is the object instance on which the signal is emitted.</li> <li>Because “open-response” signal has one parameter, the handlers
<li><code>user_data</code> comes from the fourth argument of <code>g_signal_connect</code>.</li> parameters are a TfeTextView instance, the signals parameter and user
<li><code>parameter</code> comes from the fourth argument of <code>g_signal_emit</code>.</li> data.</li>
<li><code>tv</code> is the object instance on which the signal is
emitted.</li>
<li><code>user_data</code> comes from the fourth argument of
<code>g_signal_connect</code>.</li>
<li><code>parameter</code> comes from the fourth argument of
<code>g_signal_emit</code>.</li>
</ul> </ul>
<p>The values of the parameter is defined in <code>tfetextview.h</code> because they are public.</p> <p>The values of the parameter is defined in <code>tfetextview.h</code>
<div class="sourceCode" id="cb4"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true"></a><span class="co">/* &quot;open-response&quot; signal response */</span></span> because they are public.</p>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true"></a><span class="kw">enum</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><span class="co">/* &quot;open-response&quot; signal response */</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true"></a>{</span> <span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="kw">enum</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true"></a> TFE_OPEN_RESPONSE_SUCCESS,</span> <span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="op">{</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true"></a> TFE_OPEN_RESPONSE_CANCEL,</span> <span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a> TFE_OPEN_RESPONSE_SUCCESS<span class="op">,</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true"></a> TFE_OPEN_RESPONSE_ERROR</span> <span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a> TFE_OPEN_RESPONSE_CANCEL<span class="op">,</span></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true"></a>};</span></code></pre></div> <span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a> TFE_OPEN_RESPONSE_ERROR</span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a><span class="op">};</span></span></code></pre></div>
<ul> <ul>
<li>The parameter is set to <code>TFE_OPEN_RESPONSE_SUCCESS</code> when <code>tfe_text_view_open</code> has successfully opened a file and read it.</li> <li>The parameter is set to <code>TFE_OPEN_RESPONSE_SUCCESS</code> when
<li>The parameter is set to <code>TFE_OPEN_RESPONSE_CANCEL</code> when the user has canceled.</li> <code>tfe_text_view_open</code> has successfully opened a file and read
<li>The parameter is set to <code>TFE_OPEN_RESPONSE_ERROR</code> when an error has occurred.</li> it.</li>
<li>The parameter is set to <code>TFE_OPEN_RESPONSE_CANCEL</code> when
the user has canceled.</li>
<li>The parameter is set to <code>TFE_OPEN_RESPONSE_ERROR</code> when an
error has occurred.</li>
</ul> </ul>
<h2 id="signal-connection">Signal connection</h2> <h2 id="signal-connection">Signal connection</h2>
<p>A signal and a handler are connected by the function <code>g_signal_connect</code>. There are some similar functions like <code>g_signal_connect_after</code>, <code>g_signal_connect_swapped</code> and so on. However, <code>g_signal_connect</code> is the most common. The signals “change-file” is connected to a callback function out of the TfeTextView object. In the same way, the signals “open-response” is connected to a callback function out of the TfeTextView object. Those callback functions are defined by users.</p> <p>A signal and a handler are connected by the function
<p>In the program <code>tfe</code>, callback functions are defined in <code>tfenotebook.c</code>. And their names are <code>file_changed</code> and <code>open_response</code>. They will be explained later.</p> <code>g_signal_connect</code>. There are some similar functions like
<div class="sourceCode" id="cb5"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true"></a>g_signal_connect (GTK_TEXT_VIEW (tv), <span class="st">&quot;change-file&quot;</span>, G_CALLBACK (file_changed), nb);</span> <code>g_signal_connect_after</code>,
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true"></a></span> <code>g_signal_connect_swapped</code> and so on. However,
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true"></a>g_signal_connect (TFE_TEXT_VIEW (tv), <span class="st">&quot;open-response&quot;</span>, G_CALLBACK (open_response), nb);</span></code></pre></div> <code>g_signal_connect</code> is the most common. The signals
“change-file” is connected to a callback function out of the TfeTextView
object. In the same way, the signals “open-response” is connected to a
callback function out of the TfeTextView object. Those callback
functions are defined by users.</p>
<p>In the program <code>tfe</code>, callback functions are defined in
<code>tfenotebook.c</code>. And their names are
<code>file_changed</code> and <code>open_response</code>. They will be
explained later.</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>g_signal_connect <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> <span class="st">&quot;change-file&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>file_changed<span class="op">),</span> nb<span class="op">);</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>g_signal_connect <span class="op">(</span>TFE_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> <span class="st">&quot;open-response&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>open_response<span class="op">),</span> nb<span class="op">);</span></span></code></pre></div>
<h2 id="signal-emission">Signal emission</h2> <h2 id="signal-emission">Signal emission</h2>
<p>Signals are emitted on an instance. The type of the instance is the second argument of <code>g_signal_new</code>. The relationship between the signal and object type is determined when the signal is registered.</p> <p>Signals are emitted on an instance. The type of the instance is the
<p>A function <code>g_signal_emit</code> is used to emit the signal. The following lines are extracted from <code>tfetextview.c</code>. Each line comes from a different line.</p> second argument of <code>g_signal_new</code>. The relationship between
<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>g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], <span class="dv">0</span>);</span> the signal and object type is determined when the signal is
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true"></a>g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], <span class="dv">0</span>, TFE_OPEN_RESPONSE_SUCCESS);</span> registered.</p>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true"></a>g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], <span class="dv">0</span>, TFE_OPEN_RESPONSE_CANCEL);</span> <p>A function <code>g_signal_emit</code> is used to emit the signal. The
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true"></a>g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], <span class="dv">0</span>, TFE_OPEN_RESPONSE_ERROR);</span></code></pre></div> following lines are extracted from <code>tfetextview.c</code>. Each line
comes from a different line.</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" tabindex="-1"></a>g_signal_emit <span class="op">(</span>tv<span class="op">,</span> tfe_text_view_signals<span class="op">[</span>CHANGE_FILE<span class="op">],</span> <span class="dv">0</span><span class="op">);</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>g_signal_emit <span class="op">(</span>tv<span class="op">,</span> tfe_text_view_signals<span class="op">[</span>OPEN_RESPONSE<span class="op">],</span> <span class="dv">0</span><span class="op">,</span> TFE_OPEN_RESPONSE_SUCCESS<span class="op">);</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>g_signal_emit <span class="op">(</span>tv<span class="op">,</span> tfe_text_view_signals<span class="op">[</span>OPEN_RESPONSE<span class="op">],</span> <span class="dv">0</span><span class="op">,</span> TFE_OPEN_RESPONSE_CANCEL<span class="op">);</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>g_signal_emit <span class="op">(</span>tv<span class="op">,</span> tfe_text_view_signals<span class="op">[</span>OPEN_RESPONSE<span class="op">],</span> <span class="dv">0</span><span class="op">,</span> TFE_OPEN_RESPONSE_ERROR<span class="op">);</span></span></code></pre></div>
<ul> <ul>
<li>The first argument is the instance on which the signal is emitted.</li> <li>The first argument is the instance on which the signal is
emitted.</li>
<li>The second argument is the signal id.</li> <li>The second argument is the signal id.</li>
<li>The third argument is the detail of the signal. “change-file” signal and “open-response” signal doesnt have details and the argument is zero when no details.</li> <li>The third argument is the detail of the signal. “change-file” signal
<li>“change-file” signal doesnt have parameter, so theres no fourth parameter.</li> and “open-response” signal doesnt have details and the argument is zero
<li>“open-response” signal has one parameter. The fourth parameter is the parameter.</li> when no details.</li>
<li>“change-file” signal doesnt have parameter, so theres no fourth
parameter.</li>
<li>“open-response” signal has one parameter. The fourth parameter is
the parameter.</li>
</ul> </ul>
</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> <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>

View file

@ -112,117 +112,129 @@
</div> </div>
</nav> </nav>
<h1 id="functions-in-tfetextview">Functions in TfeTextView</h1> <h1 id="functions-in-tfetextview">Functions in TfeTextView</h1>
<p>In this section I will explain functions in TfeTextView object.</p> <p>TfeTextView functions are described in this section.</p>
<h2 id="tfe.h-and-tfetextview.h">tfe.h and tfetextview.h</h2> <h2 id="tfetextview.h">tfetextview.h</h2>
<p><code>tfe.h</code> is a top header file and it includes <p>The header file <code>tfetextview.h</code> provides:</p>
<code>gtk.h</code> and all the header files. C source files <ul>
<code>tfeapplication.c</code> and <code>tfenotebook.c</code> include <li>The type of TfeTextView, which is
<code>tfe.h</code> at the beginning.</p> <code>TFE_TYPE_TEXT_VIEW</code>.</li>
<li>The expansion of <code>G_DECLARE_FINAL_TYPE</code> includes some
useful macros.</li>
<li>Constants for the <code>open-response</code> signal is
defined..</li>
<li>Public functions of <code>tfetextview.c</code> are declared.</li>
</ul>
<p>Therefore, Any programs use TfeTextView needs to include
<code>tfetextview.h</code>.</p>
<div class="sourceCode" id="cb1"><pre <div class="sourceCode" id="cb1"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1"></a><span class="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span> class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1"></a><span class="pp">#ifndef __TFE_TEXT_VIEW_H__</span></span>
<span id="cb1-2"><a href="#cb1-2"></a></span> <span id="cb1-2"><a href="#cb1-2"></a><span class="pp">#define __TFE_TEXT_VIEW_H__</span></span>
<span id="cb1-3"><a href="#cb1-3"></a><span class="pp">#include </span><span class="im">&quot;../tfetextview/tfetextview.h&quot;</span></span> <span id="cb1-3"><a href="#cb1-3"></a></span>
<span id="cb1-4"><a href="#cb1-4"></a><span class="pp">#include </span><span class="im">&quot;tfenotebook.h&quot;</span></span></code></pre></div> <span id="cb1-4"><a href="#cb1-4"></a><span class="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span>
<p><code>../tfetextview/tfetextview.h</code> is a header file which <span id="cb1-5"><a href="#cb1-5"></a></span>
describes the public functions in <code>tfetextview.c</code>.</p> <span id="cb1-6"><a href="#cb1-6"></a><span class="pp">#define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type ()</span></span>
<div class="sourceCode" id="cb2"><pre <span id="cb1-7"><a href="#cb1-7"></a>G_DECLARE_FINAL_TYPE <span class="op">(</span>TfeTextView<span class="op">,</span> tfe_text_view<span class="op">,</span> TFE<span class="op">,</span> TEXT_VIEW<span class="op">,</span> GtkTextView<span class="op">)</span></span>
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb2-1"><a href="#cb2-1"></a><span class="pp">#ifndef __TFE_TEXT_VIEW_H__</span></span> <span id="cb1-8"><a href="#cb1-8"></a></span>
<span id="cb2-2"><a href="#cb2-2"></a><span class="pp">#define __TFE_TEXT_VIEW_H__</span></span> <span id="cb1-9"><a href="#cb1-9"></a><span class="co">/* &quot;open-response&quot; signal response */</span></span>
<span id="cb2-3"><a href="#cb2-3"></a></span> <span id="cb1-10"><a href="#cb1-10"></a><span class="kw">enum</span> TfeTextViewOpenResponseType</span>
<span id="cb2-4"><a href="#cb2-4"></a><span class="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span> <span id="cb1-11"><a href="#cb1-11"></a><span class="op">{</span></span>
<span id="cb2-5"><a href="#cb2-5"></a></span> <span id="cb1-12"><a href="#cb1-12"></a> TFE_OPEN_RESPONSE_SUCCESS<span class="op">,</span></span>
<span id="cb2-6"><a href="#cb2-6"></a><span class="pp">#define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type ()</span></span> <span id="cb1-13"><a href="#cb1-13"></a> TFE_OPEN_RESPONSE_CANCEL<span class="op">,</span></span>
<span id="cb2-7"><a href="#cb2-7"></a>G_DECLARE_FINAL_TYPE <span class="op">(</span>TfeTextView<span class="op">,</span> tfe_text_view<span class="op">,</span> TFE<span class="op">,</span> TEXT_VIEW<span class="op">,</span> GtkTextView<span class="op">)</span></span> <span id="cb1-14"><a href="#cb1-14"></a> TFE_OPEN_RESPONSE_ERROR</span>
<span id="cb2-8"><a href="#cb2-8"></a></span> <span id="cb1-15"><a href="#cb1-15"></a><span class="op">};</span></span>
<span id="cb2-9"><a href="#cb2-9"></a><span class="co">/* &quot;open-response&quot; signal response */</span></span> <span id="cb1-16"><a href="#cb1-16"></a></span>
<span id="cb2-10"><a href="#cb2-10"></a><span class="kw">enum</span> TfeTextViewOpenResponseType</span> <span id="cb1-17"><a href="#cb1-17"></a>GFile <span class="op">*</span></span>
<span id="cb2-11"><a href="#cb2-11"></a><span class="op">{</span></span> <span id="cb1-18"><a href="#cb1-18"></a>tfe_text_view_get_file <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">);</span></span>
<span id="cb2-12"><a href="#cb2-12"></a> TFE_OPEN_RESPONSE_SUCCESS<span class="op">,</span></span> <span id="cb1-19"><a href="#cb1-19"></a></span>
<span id="cb2-13"><a href="#cb2-13"></a> TFE_OPEN_RESPONSE_CANCEL<span class="op">,</span></span> <span id="cb1-20"><a href="#cb1-20"></a><span class="dt">void</span></span>
<span id="cb2-14"><a href="#cb2-14"></a> TFE_OPEN_RESPONSE_ERROR</span> <span id="cb1-21"><a href="#cb1-21"></a>tfe_text_view_open <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">,</span> GtkWindow <span class="op">*</span>win<span class="op">);</span></span>
<span id="cb2-15"><a href="#cb2-15"></a><span class="op">};</span></span> <span id="cb1-22"><a href="#cb1-22"></a></span>
<span id="cb2-16"><a href="#cb2-16"></a></span> <span id="cb1-23"><a href="#cb1-23"></a><span class="dt">void</span></span>
<span id="cb2-17"><a href="#cb2-17"></a>GFile <span class="op">*</span></span> <span id="cb1-24"><a href="#cb1-24"></a>tfe_text_view_save <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">);</span></span>
<span id="cb2-18"><a href="#cb2-18"></a>tfe_text_view_get_file <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">);</span></span> <span id="cb1-25"><a href="#cb1-25"></a></span>
<span id="cb2-19"><a href="#cb2-19"></a></span> <span id="cb1-26"><a href="#cb1-26"></a><span class="dt">void</span></span>
<span id="cb2-20"><a href="#cb2-20"></a><span class="dt">void</span></span> <span id="cb1-27"><a href="#cb1-27"></a>tfe_text_view_saveas <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">);</span></span>
<span id="cb2-21"><a href="#cb2-21"></a>tfe_text_view_open <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">,</span> GtkWindow <span class="op">*</span>win<span class="op">);</span></span> <span id="cb1-28"><a href="#cb1-28"></a></span>
<span id="cb2-22"><a href="#cb2-22"></a></span> <span id="cb1-29"><a href="#cb1-29"></a>GtkWidget <span class="op">*</span></span>
<span id="cb2-23"><a href="#cb2-23"></a><span class="dt">void</span></span> <span id="cb1-30"><a href="#cb1-30"></a>tfe_text_view_new_with_file <span class="op">(</span>GFile <span class="op">*</span>file<span class="op">);</span></span>
<span id="cb2-24"><a href="#cb2-24"></a>tfe_text_view_save <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">);</span></span> <span id="cb1-31"><a href="#cb1-31"></a></span>
<span id="cb2-25"><a href="#cb2-25"></a></span> <span id="cb1-32"><a href="#cb1-32"></a>GtkWidget <span class="op">*</span></span>
<span id="cb2-26"><a href="#cb2-26"></a><span class="dt">void</span></span> <span id="cb1-33"><a href="#cb1-33"></a>tfe_text_view_new <span class="op">(</span><span class="dt">void</span><span class="op">);</span></span>
<span id="cb2-27"><a href="#cb2-27"></a>tfe_text_view_saveas <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">);</span></span> <span id="cb1-34"><a href="#cb1-34"></a></span>
<span id="cb2-28"><a href="#cb2-28"></a></span> <span id="cb1-35"><a href="#cb1-35"></a><span class="pp">#endif </span><span class="co">/* __TFE_TEXT_VIEW_H__ */</span></span></code></pre></div>
<span id="cb2-29"><a href="#cb2-29"></a>GtkWidget <span class="op">*</span></span>
<span id="cb2-30"><a href="#cb2-30"></a>tfe_text_view_new_with_file <span class="op">(</span>GFile <span class="op">*</span>file<span class="op">);</span></span>
<span id="cb2-31"><a href="#cb2-31"></a></span>
<span id="cb2-32"><a href="#cb2-32"></a>GtkWidget <span class="op">*</span></span>
<span id="cb2-33"><a href="#cb2-33"></a>tfe_text_view_new <span class="op">(</span><span class="dt">void</span><span class="op">);</span></span>
<span id="cb2-34"><a href="#cb2-34"></a></span>
<span id="cb2-35"><a href="#cb2-35"></a><span class="pp">#endif </span><span class="co">/* __TFE_TEXT_VIEW_H__ */</span></span></code></pre></div>
<ul> <ul>
<li>1,2,35: Thanks to these three lines, the following lines are <li>1,2,35: Thanks to these three lines, the following lines are
included only once.</li> included only once. You can use <code>#pragma once</code> instead of
them. It is non-standard but widely used.</li>
<li>4: Includes gtk4 header files. The header file <code>gtk4</code> <li>4: Includes gtk4 header files. The header file <code>gtk4</code>
also has the same mechanism to avoid including it multiple times.</li> also has the same mechanism to avoid including it multiple times.</li>
<li>6-7: These two lines define TfeTextView type, its class structure <li>6-7: These two lines define TfeTextView type, its class structure
and some useful macros.</li> and some useful macros.
<ul>
<li><code>TfeTextView</code> and <code>TfeTextViewClass</code> are
declared as typedef of C structures.</li>
<li>You need to define a structure <code>_TfeTextView</code> later.</li>
<li>The class structure <code>_TfeTextViewClass</code> is defined here.
You dont need to define it by yourself.</li>
<li>Convenience functions <code>TFE_TEXT_VIEW ()</code> for casting and
<code>TFE_IS_TEXT_VIEW</code> for type check are defined.</li>
</ul></li>
<li>9-15: A definition of the value of the parameter of “open-response” <li>9-15: A definition of the value of the parameter of “open-response”
signal.</li> signal.</li>
<li>17-33: Declarations of public functions on TfeTextView.</li> <li>17-33: Declarations of public functions on TfeTextView.</li>
</ul> </ul>
<h2 id="functions-to-create-tfetextview-instances">Functions to create <h2 id="instance-creation-functions">Instance creation Functions</h2>
TfeTextView instances</h2>
<p>A TfeTextView instance is created with <code>tfe_text_view_new</code> <p>A TfeTextView instance is created with <code>tfe_text_view_new</code>
or <code>tfe_text_view_new_with_file</code>.</p> or <code>tfe_text_view_new_with_file</code>.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>GtkWidget <span class="op">*</span>tfe_text_view_new <span class="op">(</span><span class="dt">void</span><span class="op">);</span></span></code></pre></div> <div class="sourceCode" id="cb2"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>GtkWidget <span class="op">*</span>tfe_text_view_new <span class="op">(</span><span class="dt">void</span><span class="op">);</span></span></code></pre></div>
<p><code>tfe_text_view_new</code> just creates a new TfeTextView <p><code>tfe_text_view_new</code> just creates a new TfeTextView
instance and returns the pointer to the new instance.</p> instance and returns the pointer to the new instance.</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>GtkWidget <span class="op">*</span>tfe_text_view_new_with_file <span class="op">(</span>GFile <span class="op">*</span>file<span class="op">);</span></span></code></pre></div> <div class="sourceCode" id="cb3"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>GtkWidget <span class="op">*</span>tfe_text_view_new_with_file <span class="op">(</span>GFile <span class="op">*</span>file<span class="op">);</span></span></code></pre></div>
<p><code>tfe_text_view_new_with_file</code> is given a Gfile object as <p><code>tfe_text_view_new_with_file</code> is given a Gfile object as
an argument and it loads the file into the GtkTextBuffer instance, then an argument and it loads the file into the GtkTextBuffer instance, then
returns the pointer to the new instance. If an error occurs during the returns the pointer to the new instance. If an error occurs during the
creation process, NULL is returned.</p> creation process, NULL is returned.</p>
<p>Each function is defined as follows.</p> <p>Each function is defined as follows.</p>
<div class="sourceCode" id="cb5"><pre <div class="sourceCode" id="cb4"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb5-1"><a href="#cb5-1"></a>GtkWidget <span class="op">*</span></span> class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1"></a>GtkWidget <span class="op">*</span></span>
<span id="cb5-2"><a href="#cb5-2"></a>tfe_text_view_new_with_file <span class="op">(</span>GFile <span class="op">*</span>file<span class="op">)</span> <span class="op">{</span></span> <span id="cb4-2"><a href="#cb4-2"></a>tfe_text_view_new_with_file <span class="op">(</span>GFile <span class="op">*</span>file<span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-3"><a href="#cb5-3"></a> g_return_val_if_fail <span class="op">(</span>G_IS_FILE <span class="op">(</span>file<span class="op">),</span> NULL<span class="op">);</span></span> <span id="cb4-3"><a href="#cb4-3"></a> g_return_val_if_fail <span class="op">(</span>G_IS_FILE <span class="op">(</span>file<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb5-4"><a href="#cb5-4"></a></span> <span id="cb4-4"><a href="#cb4-4"></a></span>
<span id="cb5-5"><a href="#cb5-5"></a> GtkWidget <span class="op">*</span>tv<span class="op">;</span></span> <span id="cb4-5"><a href="#cb4-5"></a> GtkWidget <span class="op">*</span>tv<span class="op">;</span></span>
<span id="cb5-6"><a href="#cb5-6"></a> GtkTextBuffer <span class="op">*</span>tb<span class="op">;</span></span> <span id="cb4-6"><a href="#cb4-6"></a> GtkTextBuffer <span class="op">*</span>tb<span class="op">;</span></span>
<span id="cb5-7"><a href="#cb5-7"></a> <span class="dt">char</span> <span class="op">*</span>contents<span class="op">;</span></span> <span id="cb4-7"><a href="#cb4-7"></a> <span class="dt">char</span> <span class="op">*</span>contents<span class="op">;</span></span>
<span id="cb5-8"><a href="#cb5-8"></a> gsize length<span class="op">;</span></span> <span id="cb4-8"><a href="#cb4-8"></a> gsize length<span class="op">;</span></span>
<span id="cb5-9"><a href="#cb5-9"></a></span> <span id="cb4-9"><a href="#cb4-9"></a></span>
<span id="cb5-10"><a href="#cb5-10"></a> <span class="cf">if</span> <span class="op">(!</span> g_file_load_contents <span class="op">(</span>file<span class="op">,</span> NULL<span class="op">,</span> <span class="op">&amp;</span>contents<span class="op">,</span> <span class="op">&amp;</span>length<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">))</span> <span class="co">/* read error */</span></span> <span id="cb4-10"><a href="#cb4-10"></a> <span class="cf">if</span> <span class="op">(!</span> g_file_load_contents <span class="op">(</span>file<span class="op">,</span> NULL<span class="op">,</span> <span class="op">&amp;</span>contents<span class="op">,</span> <span class="op">&amp;</span>length<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">))</span> <span class="co">/* read error */</span></span>
<span id="cb5-11"><a href="#cb5-11"></a> <span class="cf">return</span> NULL<span class="op">;</span></span> <span id="cb4-11"><a href="#cb4-11"></a> <span class="cf">return</span> NULL<span class="op">;</span></span>
<span id="cb5-12"><a href="#cb5-12"></a></span> <span id="cb4-12"><a href="#cb4-12"></a></span>
<span id="cb5-13"><a href="#cb5-13"></a> <span class="cf">if</span> <span class="op">((</span>tv <span class="op">=</span> tfe_text_view_new<span class="op">())</span> <span class="op">!=</span> NULL<span class="op">)</span> <span class="op">{</span></span> <span id="cb4-13"><a href="#cb4-13"></a> <span class="cf">if</span> <span class="op">((</span>tv <span class="op">=</span> tfe_text_view_new<span class="op">())</span> <span class="op">!=</span> NULL<span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-14"><a href="#cb5-14"></a> tb <span class="op">=</span> gtk_text_view_get_buffer <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span> <span id="cb4-14"><a href="#cb4-14"></a> tb <span class="op">=</span> gtk_text_view_get_buffer <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
<span id="cb5-15"><a href="#cb5-15"></a> gtk_text_buffer_set_text <span class="op">(</span>tb<span class="op">,</span> contents<span class="op">,</span> length<span class="op">);</span></span> <span id="cb4-15"><a href="#cb4-15"></a> gtk_text_buffer_set_text <span class="op">(</span>tb<span class="op">,</span> contents<span class="op">,</span> length<span class="op">);</span></span>
<span id="cb5-16"><a href="#cb5-16"></a> TFE_TEXT_VIEW <span class="op">(</span>tv<span class="op">)-&gt;</span>file <span class="op">=</span> g_file_dup <span class="op">(</span>file<span class="op">);</span></span> <span id="cb4-16"><a href="#cb4-16"></a> TFE_TEXT_VIEW <span class="op">(</span>tv<span class="op">)-&gt;</span>file <span class="op">=</span> g_file_dup <span class="op">(</span>file<span class="op">);</span></span>
<span id="cb5-17"><a href="#cb5-17"></a> gtk_text_buffer_set_modified <span class="op">(</span>tb<span class="op">,</span> FALSE<span class="op">);</span></span> <span id="cb4-17"><a href="#cb4-17"></a> gtk_text_buffer_set_modified <span class="op">(</span>tb<span class="op">,</span> FALSE<span class="op">);</span></span>
<span id="cb5-18"><a href="#cb5-18"></a> <span class="op">}</span></span> <span id="cb4-18"><a href="#cb4-18"></a> <span class="op">}</span></span>
<span id="cb5-19"><a href="#cb5-19"></a> g_free <span class="op">(</span>contents<span class="op">);</span></span> <span id="cb4-19"><a href="#cb4-19"></a> g_free <span class="op">(</span>contents<span class="op">);</span></span>
<span id="cb5-20"><a href="#cb5-20"></a> <span class="cf">return</span> tv<span class="op">;</span></span> <span id="cb4-20"><a href="#cb4-20"></a> <span class="cf">return</span> tv<span class="op">;</span></span>
<span id="cb5-21"><a href="#cb5-21"></a><span class="op">}</span></span> <span id="cb4-21"><a href="#cb4-21"></a><span class="op">}</span></span>
<span id="cb5-22"><a href="#cb5-22"></a></span> <span id="cb4-22"><a href="#cb4-22"></a></span>
<span id="cb5-23"><a href="#cb5-23"></a>GtkWidget <span class="op">*</span></span> <span id="cb4-23"><a href="#cb4-23"></a>GtkWidget <span class="op">*</span></span>
<span id="cb5-24"><a href="#cb5-24"></a>tfe_text_view_new <span class="op">(</span><span class="dt">void</span><span class="op">)</span> <span class="op">{</span></span> <span id="cb4-24"><a href="#cb4-24"></a>tfe_text_view_new <span class="op">(</span><span class="dt">void</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-25"><a href="#cb5-25"></a> <span class="cf">return</span> GTK_WIDGET <span class="op">(</span>g_object_new <span class="op">(</span>TFE_TYPE_TEXT_VIEW<span class="op">,</span> NULL<span class="op">));</span></span> <span id="cb4-25"><a href="#cb4-25"></a> <span class="cf">return</span> GTK_WIDGET <span class="op">(</span>g_object_new <span class="op">(</span>TFE_TYPE_TEXT_VIEW<span class="op">,</span> <span class="st">&quot;wrap-mode&quot;</span><span class="op">,</span> GTK_WRAP_WORD_CHAR<span class="op">,</span> NULL<span class="op">));</span></span>
<span id="cb5-26"><a href="#cb5-26"></a><span class="op">}</span></span></code></pre></div> <span id="cb4-26"><a href="#cb4-26"></a><span class="op">}</span></span></code></pre></div>
<ul> <ul>
<li>23-25: <code>tfe_text_view_new</code> function. Just returns the <li>23-25: <code>tfe_text_view_new</code> function. Just returns the
value from the function <code>g_object_new</code> but casts it to the value from the function <code>g_object_new</code> but casts it to the
pointer to GtkWidget. Initialization is done in pointer to GtkWidget. The function <code>g_object_new</code> creates any
<code>tfe_text_view_init</code> which is called in the process of instances of its descendant class. The arguments are the type of the
<code>g_object_new</code> function.</li> class, property list and NULL. Null is the end mark of the property
list. TfeTextView “wrap-mode” property has GTK_WRAP_WORD_CHAR as the
default value.</li>
<li>1-21: <code>tfe_text_view_new_with_file</code> function.</li> <li>1-21: <code>tfe_text_view_new_with_file</code> function.</li>
<li>3: <code>g_return_val_if_fail</code> is described in <a <li>3: <code>g_return_val_if_fail</code> is described in <a
href="https://docs.gtk.org/glib/func.return_val_if_fail.html">GLib API href="https://docs.gtk.org/glib/func.return_val_if_fail.html">GLib API
Reference, g_return_val_if_fail</a>. And also <a Reference g_return_val_if_fail</a>. And also <a
href="https://docs.gtk.org/glib/logging.html">GLib API Reference, href="https://docs.gtk.org/glib/logging.html">GLib API Reference
Message Logging</a>. It tests whether the argument <code>file</code> is Message Logging</a>. It tests whether the argument <code>file</code> is
a pointer to GFile. If its true, then the program goes on to the next a pointer to GFile. If its true, then the program goes on to the next
line. If its false, then it returns NULL (the second argument) line. If its false, then it returns NULL (the second argument)
@ -254,295 +266,409 @@ doesnt need to save the contents.</li>
<li>20: Returns <code>tv</code>, which is a pointer to the newly created <li>20: Returns <code>tv</code>, which is a pointer to the newly created
TfeTextView instance. If an error happens, NULL is returned.</li> TfeTextView instance. If an error happens, NULL is returned.</li>
</ul> </ul>
<h2 id="save-and-saveas-functions">Save and saveas functions</h2> <h2 id="save-related-functions">Save related functions</h2>
<p>Save and saveas functions write the contents in the GtkTextBuffer to <p>Save and saveas functions write the contents in the GtkTextBuffer to
a file.</p> a file.</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" tabindex="-1"></a><span class="dt">void</span> tfe_text_view_save <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">)</span></span></code></pre></div> <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><span class="dt">void</span> tfe_text_view_save <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">)</span></span></code></pre></div>
<p>The function <code>tfe_text_view_save</code> writes the contents in <p>The function <code>tfe_text_view_save</code> writes the contents in
the GtkTextBuffer to a file specified by <code>tv-&gt;file</code>. If the GtkTextBuffer to a file specified by <code>tv-&gt;file</code>. If
<code>tv-&gt;file</code> is NULL, then it shows GtkFileChooserDialog and <code>tv-&gt;file</code> is NULL, then it shows GtkFileChooserDialog and
prompts the user to choose a file to save. Then it saves the contents to prompts the user to choose a file to save. Then it saves the contents to
the file and sets <code>tv-&gt;file</code> to point the GFile instance the file and sets <code>tv-&gt;file</code> to point the GFile instance
for the file.</p> for the file.</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><span class="dt">void</span> tfe_text_view_saveas <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">)</span></span></code></pre></div> <div class="sourceCode" id="cb6"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> tfe_text_view_saveas <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">)</span></span></code></pre></div>
<p>The function <code>saveas</code> uses GtkFileChooserDialog and <p>The function <code>saveas</code> uses GtkFileChooserDialog and
prompts the user to select a existed file or specify a new file to save. prompts the user to select a existed file or specify a new file to save.
Then, the function changes <code>tv-&gt;file</code> and save the Then, the function changes <code>tv-&gt;file</code> and save the
contents to the specified file. If an error occurs, it is shown to the contents to the specified file. If an error occurs, it is shown to the
user through the message dialog. The error is managed only in the user through the message dialog. The error is managed only in the
TfeTextView and no information is notified to the caller.</p> TfeTextView and no information is notified to the caller.</p>
<div class="sourceCode" id="cb8"><pre <h3 id="save_file-function">save_file function</h3>
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb8-1"><a href="#cb8-1"></a><span class="dt">static</span> gboolean</span> <div class="sourceCode" id="cb7"><pre
<span id="cb8-2"><a href="#cb8-2"></a>save_file <span class="op">(</span>GFile <span class="op">*</span>file<span class="op">,</span> GtkTextBuffer <span class="op">*</span>tb<span class="op">,</span> GtkWindow <span class="op">*</span>win<span class="op">)</span> <span class="op">{</span></span> class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb7-1"><a href="#cb7-1"></a><span class="dt">static</span> gboolean</span>
<span id="cb8-3"><a href="#cb8-3"></a> GtkTextIter start_iter<span class="op">;</span></span> <span id="cb7-2"><a href="#cb7-2"></a>save_file <span class="op">(</span>GFile <span class="op">*</span>file<span class="op">,</span> GtkTextBuffer <span class="op">*</span>tb<span class="op">,</span> GtkWindow <span class="op">*</span>win<span class="op">)</span> <span class="op">{</span></span>
<span id="cb8-4"><a href="#cb8-4"></a> GtkTextIter end_iter<span class="op">;</span></span> <span id="cb7-3"><a href="#cb7-3"></a> GtkTextIter start_iter<span class="op">;</span></span>
<span id="cb8-5"><a href="#cb8-5"></a> gchar <span class="op">*</span>contents<span class="op">;</span></span> <span id="cb7-4"><a href="#cb7-4"></a> GtkTextIter end_iter<span class="op">;</span></span>
<span id="cb8-6"><a href="#cb8-6"></a> gboolean stat<span class="op">;</span></span> <span id="cb7-5"><a href="#cb7-5"></a> <span class="dt">char</span> <span class="op">*</span>contents<span class="op">;</span></span>
<span id="cb8-7"><a href="#cb8-7"></a> GtkWidget <span class="op">*</span>message_dialog<span class="op">;</span></span> <span id="cb7-6"><a href="#cb7-6"></a> gboolean stat<span class="op">;</span></span>
<span id="cb8-8"><a href="#cb8-8"></a> GError <span class="op">*</span>err <span class="op">=</span> NULL<span class="op">;</span></span> <span id="cb7-7"><a href="#cb7-7"></a> GtkWidget <span class="op">*</span>message_dialog<span class="op">;</span></span>
<span id="cb8-9"><a href="#cb8-9"></a></span> <span id="cb7-8"><a href="#cb7-8"></a> GError <span class="op">*</span>err <span class="op">=</span> NULL<span class="op">;</span></span>
<span id="cb8-10"><a href="#cb8-10"></a> gtk_text_buffer_get_bounds <span class="op">(</span>tb<span class="op">,</span> <span class="op">&amp;</span>start_iter<span class="op">,</span> <span class="op">&amp;</span>end_iter<span class="op">);</span></span> <span id="cb7-9"><a href="#cb7-9"></a></span>
<span id="cb8-11"><a href="#cb8-11"></a> contents <span class="op">=</span> gtk_text_buffer_get_text <span class="op">(</span>tb<span class="op">,</span> <span class="op">&amp;</span>start_iter<span class="op">,</span> <span class="op">&amp;</span>end_iter<span class="op">,</span> FALSE<span class="op">);</span></span> <span id="cb7-10"><a href="#cb7-10"></a> gtk_text_buffer_get_bounds <span class="op">(</span>tb<span class="op">,</span> <span class="op">&amp;</span>start_iter<span class="op">,</span> <span class="op">&amp;</span>end_iter<span class="op">);</span></span>
<span id="cb8-12"><a href="#cb8-12"></a> <span class="cf">if</span> <span class="op">(</span>g_file_replace_contents <span class="op">(</span>file<span class="op">,</span> contents<span class="op">,</span> strlen <span class="op">(</span>contents<span class="op">),</span> NULL<span class="op">,</span> TRUE<span class="op">,</span> G_FILE_CREATE_NONE<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">,</span> <span class="op">&amp;</span>err<span class="op">))</span> <span class="op">{</span></span> <span id="cb7-11"><a href="#cb7-11"></a> contents <span class="op">=</span> gtk_text_buffer_get_text <span class="op">(</span>tb<span class="op">,</span> <span class="op">&amp;</span>start_iter<span class="op">,</span> <span class="op">&amp;</span>end_iter<span class="op">,</span> FALSE<span class="op">);</span></span>
<span id="cb8-13"><a href="#cb8-13"></a> gtk_text_buffer_set_modified <span class="op">(</span>tb<span class="op">,</span> FALSE<span class="op">);</span></span> <span id="cb7-12"><a href="#cb7-12"></a> stat <span class="op">=</span> g_file_replace_contents <span class="op">(</span>file<span class="op">,</span> contents<span class="op">,</span> strlen <span class="op">(</span>contents<span class="op">),</span> NULL<span class="op">,</span> TRUE<span class="op">,</span> G_FILE_CREATE_NONE<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">,</span> <span class="op">&amp;</span>err<span class="op">);</span></span>
<span id="cb8-14"><a href="#cb8-14"></a> stat <span class="op">=</span> TRUE<span class="op">;</span></span> <span id="cb7-13"><a href="#cb7-13"></a> <span class="cf">if</span> <span class="op">(</span>stat<span class="op">)</span></span>
<span id="cb8-15"><a href="#cb8-15"></a> <span class="op">}</span> <span class="cf">else</span> <span class="op">{</span></span> <span id="cb7-14"><a href="#cb7-14"></a> gtk_text_buffer_set_modified <span class="op">(</span>tb<span class="op">,</span> FALSE<span class="op">);</span></span>
<span id="cb8-16"><a href="#cb8-16"></a> message_dialog <span class="op">=</span> gtk_message_dialog_new <span class="op">(</span>win<span class="op">,</span> GTK_DIALOG_MODAL<span class="op">,</span></span> <span id="cb7-15"><a href="#cb7-15"></a> <span class="cf">else</span> <span class="op">{</span></span>
<span id="cb8-17"><a href="#cb8-17"></a> GTK_MESSAGE_ERROR<span class="op">,</span> GTK_BUTTONS_CLOSE<span class="op">,</span></span> <span id="cb7-16"><a href="#cb7-16"></a> <span class="co">// Because error message is displayed here, the caller of &#39;save_file&#39; doesn&#39;t need to do anything about error.</span></span>
<span id="cb8-18"><a href="#cb8-18"></a> <span class="st">&quot;%s.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">,</span> err<span class="op">-&gt;</span>message<span class="op">);</span></span> <span id="cb7-17"><a href="#cb7-17"></a> message_dialog <span class="op">=</span> gtk_message_dialog_new <span class="op">(</span>win<span class="op">,</span> GTK_DIALOG_MODAL<span class="op">,</span></span>
<span id="cb8-19"><a href="#cb8-19"></a> g_signal_connect <span class="op">(</span>message_dialog<span class="op">,</span> <span class="st">&quot;response&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>gtk_window_destroy<span class="op">),</span> NULL<span class="op">);</span></span> <span id="cb7-18"><a href="#cb7-18"></a> GTK_MESSAGE_ERROR<span class="op">,</span> GTK_BUTTONS_CLOSE<span class="op">,</span> <span class="st">&quot;%s.&quot;</span><span class="op">,</span> err<span class="op">-&gt;</span>message<span class="op">);</span></span>
<span id="cb8-20"><a href="#cb8-20"></a> gtk_widget_show <span class="op">(</span>message_dialog<span class="op">);</span></span> <span id="cb7-19"><a href="#cb7-19"></a> g_signal_connect <span class="op">(</span>message_dialog<span class="op">,</span> <span class="st">&quot;response&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>gtk_window_destroy<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb8-21"><a href="#cb8-21"></a> g_error_free <span class="op">(</span>err<span class="op">);</span></span> <span id="cb7-20"><a href="#cb7-20"></a> gtk_widget_show <span class="op">(</span>message_dialog<span class="op">);</span></span>
<span id="cb8-22"><a href="#cb8-22"></a> stat <span class="op">=</span> FALSE<span class="op">;</span></span> <span id="cb7-21"><a href="#cb7-21"></a> g_error_free <span class="op">(</span>err<span class="op">);</span></span>
<span id="cb8-23"><a href="#cb8-23"></a> <span class="op">}</span></span> <span id="cb7-22"><a href="#cb7-22"></a> <span class="op">}</span></span>
<span id="cb8-24"><a href="#cb8-24"></a> g_free <span class="op">(</span>contents<span class="op">);</span></span> <span id="cb7-23"><a href="#cb7-23"></a> g_free <span class="op">(</span>contents<span class="op">);</span></span>
<span id="cb8-25"><a href="#cb8-25"></a> <span class="cf">return</span> stat<span class="op">;</span></span> <span id="cb7-24"><a href="#cb7-24"></a> <span class="cf">return</span> stat<span class="op">;</span></span>
<span id="cb8-26"><a href="#cb8-26"></a><span class="op">}</span></span> <span id="cb7-25"><a href="#cb7-25"></a><span class="op">}</span></span></code></pre></div>
<span id="cb8-27"><a href="#cb8-27"></a></span>
<span id="cb8-28"><a href="#cb8-28"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb8-29"><a href="#cb8-29"></a>saveas_dialog_response <span class="op">(</span>GtkWidget <span class="op">*</span>dialog<span class="op">,</span> gint response<span class="op">,</span> TfeTextView <span class="op">*</span>tv<span class="op">)</span> <span class="op">{</span></span>
<span id="cb8-30"><a href="#cb8-30"></a> GtkTextBuffer <span class="op">*</span>tb <span class="op">=</span> gtk_text_view_get_buffer <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
<span id="cb8-31"><a href="#cb8-31"></a> GFile <span class="op">*</span>file<span class="op">;</span></span>
<span id="cb8-32"><a href="#cb8-32"></a> GtkWidget <span class="op">*</span>win <span class="op">=</span> gtk_widget_get_ancestor <span class="op">(</span>GTK_WIDGET <span class="op">(</span>tv<span class="op">),</span> GTK_TYPE_WINDOW<span class="op">);</span></span>
<span id="cb8-33"><a href="#cb8-33"></a></span>
<span id="cb8-34"><a href="#cb8-34"></a> <span class="cf">if</span> <span class="op">(</span>response <span class="op">==</span> GTK_RESPONSE_ACCEPT<span class="op">)</span> <span class="op">{</span></span>
<span id="cb8-35"><a href="#cb8-35"></a> file <span class="op">=</span> gtk_file_chooser_get_file <span class="op">(</span>GTK_FILE_CHOOSER <span class="op">(</span>dialog<span class="op">));</span></span>
<span id="cb8-36"><a href="#cb8-36"></a> <span class="cf">if</span> <span class="op">(!</span> G_IS_FILE <span class="op">(</span>file<span class="op">))</span></span>
<span id="cb8-37"><a href="#cb8-37"></a> g_warning <span class="op">(</span><span class="st">&quot;TfeTextView: gtk_file_chooser_get_file returns non GFile.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb8-38"><a href="#cb8-38"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>save_file<span class="op">(</span>file<span class="op">,</span> tb<span class="op">,</span> GTK_WINDOW <span class="op">(</span>win<span class="op">)))</span> <span class="op">{</span></span>
<span id="cb8-39"><a href="#cb8-39"></a> <span class="cf">if</span> <span class="op">(</span>G_IS_FILE <span class="op">(</span>tv<span class="op">-&gt;</span>file<span class="op">))</span></span>
<span id="cb8-40"><a href="#cb8-40"></a> g_object_unref <span class="op">(</span>tv<span class="op">-&gt;</span>file<span class="op">);</span></span>
<span id="cb8-41"><a href="#cb8-41"></a> tv<span class="op">-&gt;</span>file <span class="op">=</span> file<span class="op">;</span></span>
<span id="cb8-42"><a href="#cb8-42"></a> g_signal_emit <span class="op">(</span>tv<span class="op">,</span> tfe_text_view_signals<span class="op">[</span>CHANGE_FILE<span class="op">],</span> <span class="dv">0</span><span class="op">);</span></span>
<span id="cb8-43"><a href="#cb8-43"></a> <span class="op">}</span> <span class="cf">else</span></span>
<span id="cb8-44"><a href="#cb8-44"></a> g_object_unref <span class="op">(</span>file<span class="op">);</span></span>
<span id="cb8-45"><a href="#cb8-45"></a> <span class="op">}</span></span>
<span id="cb8-46"><a href="#cb8-46"></a> gtk_window_destroy <span class="op">(</span>GTK_WINDOW <span class="op">(</span>dialog<span class="op">));</span></span>
<span id="cb8-47"><a href="#cb8-47"></a><span class="op">}</span></span>
<span id="cb8-48"><a href="#cb8-48"></a></span>
<span id="cb8-49"><a href="#cb8-49"></a><span class="dt">void</span></span>
<span id="cb8-50"><a href="#cb8-50"></a>tfe_text_view_save <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">)</span> <span class="op">{</span></span>
<span id="cb8-51"><a href="#cb8-51"></a> g_return_if_fail <span class="op">(</span>TFE_IS_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
<span id="cb8-52"><a href="#cb8-52"></a></span>
<span id="cb8-53"><a href="#cb8-53"></a> GtkTextBuffer <span class="op">*</span>tb <span class="op">=</span> gtk_text_view_get_buffer <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
<span id="cb8-54"><a href="#cb8-54"></a> GtkWidget <span class="op">*</span>win <span class="op">=</span> gtk_widget_get_ancestor <span class="op">(</span>GTK_WIDGET <span class="op">(</span>tv<span class="op">),</span> GTK_TYPE_WINDOW<span class="op">);</span></span>
<span id="cb8-55"><a href="#cb8-55"></a></span>
<span id="cb8-56"><a href="#cb8-56"></a> <span class="cf">if</span> <span class="op">(!</span> gtk_text_buffer_get_modified <span class="op">(</span>tb<span class="op">))</span></span>
<span id="cb8-57"><a href="#cb8-57"></a> <span class="cf">return</span><span class="op">;</span> <span class="co">/* no need to save it */</span></span>
<span id="cb8-58"><a href="#cb8-58"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>tv<span class="op">-&gt;</span>file <span class="op">==</span> NULL<span class="op">)</span></span>
<span id="cb8-59"><a href="#cb8-59"></a> tfe_text_view_saveas <span class="op">(</span>tv<span class="op">);</span></span>
<span id="cb8-60"><a href="#cb8-60"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(!</span> G_IS_FILE <span class="op">(</span>tv<span class="op">-&gt;</span>file<span class="op">))</span></span>
<span id="cb8-61"><a href="#cb8-61"></a> g_error <span class="op">(</span><span class="st">&quot;TfeTextView: The pointer tv-&gt;file isn&#39;t NULL nor GFile.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb8-62"><a href="#cb8-62"></a> <span class="cf">else</span></span>
<span id="cb8-63"><a href="#cb8-63"></a> save_file <span class="op">(</span>tv<span class="op">-&gt;</span>file<span class="op">,</span> tb<span class="op">,</span> GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
<span id="cb8-64"><a href="#cb8-64"></a><span class="op">}</span></span>
<span id="cb8-65"><a href="#cb8-65"></a></span>
<span id="cb8-66"><a href="#cb8-66"></a><span class="dt">void</span></span>
<span id="cb8-67"><a href="#cb8-67"></a>tfe_text_view_saveas <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">)</span> <span class="op">{</span></span>
<span id="cb8-68"><a href="#cb8-68"></a> g_return_if_fail <span class="op">(</span>TFE_IS_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
<span id="cb8-69"><a href="#cb8-69"></a></span>
<span id="cb8-70"><a href="#cb8-70"></a> GtkWidget <span class="op">*</span>dialog<span class="op">;</span></span>
<span id="cb8-71"><a href="#cb8-71"></a> GtkWidget <span class="op">*</span>win <span class="op">=</span> gtk_widget_get_ancestor <span class="op">(</span>GTK_WIDGET <span class="op">(</span>tv<span class="op">),</span> GTK_TYPE_WINDOW<span class="op">);</span></span>
<span id="cb8-72"><a href="#cb8-72"></a></span>
<span id="cb8-73"><a href="#cb8-73"></a> dialog <span class="op">=</span> gtk_file_chooser_dialog_new <span class="op">(</span><span class="st">&quot;Save file&quot;</span><span class="op">,</span> GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> GTK_FILE_CHOOSER_ACTION_SAVE<span class="op">,</span></span>
<span id="cb8-74"><a href="#cb8-74"></a> <span class="st">&quot;Cancel&quot;</span><span class="op">,</span> GTK_RESPONSE_CANCEL<span class="op">,</span></span>
<span id="cb8-75"><a href="#cb8-75"></a> <span class="st">&quot;Save&quot;</span><span class="op">,</span> GTK_RESPONSE_ACCEPT<span class="op">,</span></span>
<span id="cb8-76"><a href="#cb8-76"></a> NULL<span class="op">);</span></span>
<span id="cb8-77"><a href="#cb8-77"></a> g_signal_connect <span class="op">(</span>dialog<span class="op">,</span> <span class="st">&quot;response&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>saveas_dialog_response<span class="op">),</span> tv<span class="op">);</span></span>
<span id="cb8-78"><a href="#cb8-78"></a> gtk_widget_show <span class="op">(</span>dialog<span class="op">);</span></span>
<span id="cb8-79"><a href="#cb8-79"></a><span class="op">}</span></span></code></pre></div>
<ul> <ul>
<li>1-26: <code>save_file</code> function. This function is called from <li>The function <code>save_file</code> is called from
<code>saveas_dialog_response</code> and <code>tfe_text_view_save</code>. <code>saveas_dialog_response</code> and <code>tfe_text_view_save</code>.
This function saves the contents of the buffer to the file given as an This function saves the contents of the buffer to the file given as an
argument. If error happens, it displays an error message. The class of argument. If error happens, it displays an error message. So, a caller
this function is <code>static</code>. Therefore, only functions in this of this function dont need to take care of errors. The class of this
file (<code>tfeTetview.c</code>) call this function. Such static function is <code>static</code>. Therefore, only functions in this file
functions usally dont have <code>g_return_val_if_fail</code> (<code>tfetextview.c</code>) call this function. Such static functions
function.</li> usually dont have <code>g_return_val_if_fail</code> function.</li>
<li>10-11: Gets the text contents from the buffer.</li> <li>10-11: Gets the text contents from the buffer.</li>
<li>12-14: Saves the contents to the file. If no error happens, set the <li>12: The function <code>g_file_replace_contents</code> writes the
modified flag to be FALSE. This means that the buffer is not modified contents to the file and returns the status (true = success/ false =
since it has been saved. And set the return status <code>stat</code> to fail). It has many parameters, but some of them are almost always given
be TRUE.</li> the same values.
<li>15-23: If it fails to save the contents, displays an error <ul>
message.</li> <li>GFile* file: GFile to which the contents are saved.</li>
<li>16-18: Creates a message dialog with the error message.</li> <li>const char* contents: contents to be saved. The string is owned by
the caller.</li>
<li>gsize length: the length of the contents</li>
<li>const char* etag: entity tag. It is usually NULL.</li>
<li>gboolean make_backup: true to make a backup if the file exists.
false not to make it. the file will be overwritten.</li>
<li>GFileCreateFlags flags: usually <code>G_FILE_CREATE_NONE</code> is
fine.</li>
<li>char** new_etag: new entity tag. It is usually NULL.</li>
<li>GCancellable* cancellable: If a cancellable instance is set, the
other thread can cancel this operation. it is usually NULL.</li>
<li>GError** error: If error happens, GError will be set.</li>
</ul></li>
<li>13,14: If no error happens, set the modified flag to be FALSE. This
means that the buffer is not modified since it has been saved.</li>
<li>15-22: If it fails to save the contents, an error message will be
displayed.</li>
<li>17-18: Creates a message dialog. The parameters are:
<ul>
<li>GtkWindow* parent: transient parent window. This allows window
managers to keep the dialog on top of the parent window, or center the
dialog over the parent window. It is possible to give no parent window
to the dialog. However, it is encouraged to give parents to
dialogs.</li>
<li>GtkDialogFlags flags: GTK_DIALOG_MODAL for modal dialog. A modal
dialog is usually fine.</li>
<li>GtkMessageType type: GTK_MESSAGE_ERROR for error message. Other
options are GTK_MESSAGE_INFO, GTK_MESSAGE_WARNING and so on.</li>
<li>GtkButtonsType buttons: GTK_BUTTON_OK is used most often. Other
option is GTK_BUTTON_YES_NO, GTK_BUTTON_CANCEL and so on.</li>
<li>const gchar* message_format: gchar is the same as char. This format
is the same as printf. Arguments for the message format follow.</li>
</ul></li>
<li>19: Connects the “response” signal to <li>19: Connects the “response” signal to
<code>gtk_window_destroy</code>, so that the dialog disappears when a <code>gtk_window_destroy</code>, so that the dialog disappears when the
user clicked on the button.</li> user clicks on the button.</li>
<li>20-21: Shows the window, frees <code>err</code> and set <li>20: Shows the message dialog.</li>
<code>stat</code> to be FLASE.</li> <li>21: Frees <code>err</code> with <code>g_error_free</code>
<li>24: Frees <code>contents</code>.</li> function.</li>
<li>25: Returns to the caller.</li> <li>23: Frees <code>contents</code>.</li>
<li>28-47: <code>saveas_dialog_response</code> function. This is a <li>24: Returns to the caller.</li>
signal handler for the “response” signal on GtkFileChooserDialog </ul>
instance created by <code>tfe_text_view_saveas</code> function. This <h3 id="saveas_dialog_response-function">saveas_dialog_response
handler analyzes the response and determines whether to save the function</h3>
contents.</li> <div class="sourceCode" id="cb8"><pre
<li>34-45: If the response is <code>GTK_RESPONSE_ACCEPT</code>, the user 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>
has clicked on the <code>Save</code> button. So, it tries to save.</li> <span id="cb8-2"><a href="#cb8-2"></a>saveas_dialog_response <span class="op">(</span>GtkWidget <span class="op">*</span>dialog<span class="op">,</span> gint response<span class="op">,</span> TfeTextView <span class="op">*</span>tv<span class="op">)</span> <span class="op">{</span></span>
<li>35: Gets the GFile <code>file</code> from GtkFileChooserDialog.</li> <span id="cb8-3"><a href="#cb8-3"></a> GtkTextBuffer <span class="op">*</span>tb <span class="op">=</span> gtk_text_view_get_buffer <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
<li>36-37: If it doesnt point GFile, it outputs an error message to the <span id="cb8-4"><a href="#cb8-4"></a> GFile <span class="op">*</span>file<span class="op">;</span></span>
log.</li> <span id="cb8-5"><a href="#cb8-5"></a> GtkWidget <span class="op">*</span>win <span class="op">=</span> gtk_widget_get_ancestor <span class="op">(</span>GTK_WIDGET <span class="op">(</span>tv<span class="op">),</span> GTK_TYPE_WINDOW<span class="op">);</span></span>
<li>38: Otherwise, it calls <code>save_file</code> to save the contents <span id="cb8-6"><a href="#cb8-6"></a></span>
to the file.</li> <span id="cb8-7"><a href="#cb8-7"></a> <span class="cf">if</span> <span class="op">(</span>response <span class="op">==</span> GTK_RESPONSE_ACCEPT<span class="op">)</span> <span class="op">{</span></span>
<li>39-42: If <code>save_file</code> has successfully saved the <span id="cb8-8"><a href="#cb8-8"></a> file <span class="op">=</span> gtk_file_chooser_get_file <span class="op">(</span>GTK_FILE_CHOOSER <span class="op">(</span>dialog<span class="op">));</span></span>
contents, <code>tv-&gt;file</code> is updated. If the old GFile pointed <span id="cb8-9"><a href="#cb8-9"></a> <span class="cf">if</span> <span class="op">(!</span> G_IS_FILE <span class="op">(</span>file<span class="op">))</span></span>
by <code>tv-&gt;file</code> exists, it is freed in advance. Emits <span id="cb8-10"><a href="#cb8-10"></a> g_warning <span class="op">(</span><span class="st">&quot;TfeTextView: gtk_file_chooser_get_file returns non GFile.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
“change-file” signal.</li> <span id="cb8-11"><a href="#cb8-11"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>save_file<span class="op">(</span>file<span class="op">,</span> tb<span class="op">,</span> GTK_WINDOW <span class="op">(</span>win<span class="op">)))</span> <span class="op">{</span></span>
<li>44: Unrefs <code>file</code>.</li> <span id="cb8-12"><a href="#cb8-12"></a> <span class="co">// The following is complicated. The comments here will help your understanding</span></span>
<li>46: destroys the file chooser dialog.</li> <span id="cb8-13"><a href="#cb8-13"></a> <span class="co">// G_IS_FILE(tv-&gt;file) &amp;&amp; tv-&gt;file == file =&gt; nothing to do</span></span>
<li>49-64: <code>tfe_text_view_save</code> function.</li> <span id="cb8-14"><a href="#cb8-14"></a> <span class="co">// G_IS_FILE(tv-&gt;file) &amp;&amp; tv-&gt;file != file =&gt; unref(tv-&gt;file), tv-&gt;file=file, signal emit</span></span>
<li>51: <code>tfe_text_view_save</code> is public, i.e. it is open to <span id="cb8-15"><a href="#cb8-15"></a> <span class="co">// tv-&gt;file==NULL =&gt; tv-&gt;file=file, signal emit</span></span>
the other files. So, it doesnt have <code>static</code> class. Public <span id="cb8-16"><a href="#cb8-16"></a> <span class="cf">if</span> <span class="op">(</span>G_IS_FILE <span class="op">(</span>tv<span class="op">-&gt;</span>file<span class="op">)</span> <span class="op">&amp;&amp;</span> <span class="op">(!</span> g_file_equal <span class="op">(</span>tv<span class="op">-&gt;</span>file<span class="op">,</span> file<span class="op">)))</span></span>
functions should check the parameter type with <span id="cb8-17"><a href="#cb8-17"></a> g_object_unref <span class="op">(</span>tv<span class="op">-&gt;</span>file<span class="op">);</span></span>
<code>g_return_if_fail</code> function. If <code>tv</code> is not a <span id="cb8-18"><a href="#cb8-18"></a> <span class="cf">if</span> <span class="op">(!</span> <span class="op">(</span>G_IS_FILE <span class="op">(</span>tv<span class="op">-&gt;</span>file<span class="op">)</span> <span class="op">&amp;&amp;</span> g_file_equal <span class="op">(</span>tv<span class="op">-&gt;</span>file<span class="op">,</span> file<span class="op">)))</span> <span class="op">{</span></span>
pointer to a TfeTextView instance, then it logs an error message and <span id="cb8-19"><a href="#cb8-19"></a> tv<span class="op">-&gt;</span>file <span class="op">=</span> file<span class="op">;</span> <span class="co">// The ownership of &#39;file&#39; moves to TfeTextView.</span></span>
immediately returns. This function is similar to <span id="cb8-20"><a href="#cb8-20"></a> g_signal_emit <span class="op">(</span>tv<span class="op">,</span> tfe_text_view_signals<span class="op">[</span>CHANGE_FILE<span class="op">],</span> <span class="dv">0</span><span class="op">);</span></span>
<code>g_return_val_if_fail</code>, but no value is returned because <span id="cb8-21"><a href="#cb8-21"></a> <span class="op">}</span></span>
<code>tfe_text_view_save</code> doesnt return a value.</li> <span id="cb8-22"><a href="#cb8-22"></a> g_object_unref <span class="op">(</span>file<span class="op">);</span></span>
<li>53-54: Gets GtkTextBuffer instance and GtkWidget instance and <span id="cb8-23"><a href="#cb8-23"></a> <span class="op">}</span> <span class="cf">else</span></span>
assignes them to <code>tb</code> and<code>win</code> respectively.</li> <span id="cb8-24"><a href="#cb8-24"></a> g_object_unref <span class="op">(</span>file<span class="op">);</span></span>
<li>56-57: If the buffer hasnt modified, then it doesnt need to save <span id="cb8-25"><a href="#cb8-25"></a> <span class="op">}</span></span>
it. So the function returns.</li> <span id="cb8-26"><a href="#cb8-26"></a> gtk_window_destroy <span class="op">(</span>GTK_WINDOW <span class="op">(</span>dialog<span class="op">));</span></span>
<li>58-59: If <code>tv-&gt;file</code> is NULL, no file has given yet. <span id="cb8-27"><a href="#cb8-27"></a><span class="op">}</span></span></code></pre></div>
It calls <code>tfe_text_view_saveas</code> which prompts a user to <ul>
select a file or specify a new file to save.</li> <li>The function <code>saveas_dialog_response</code> is a signal handler
<li>60-61: If <code>tv-&gt;file</code> doesnt point GFile, somethig bad for the “response” signal on GtkFileChooserDialog. This handler analyzes
has happened. Logs an error message.</li> the response and determines whether to save the contents or not.</li>
<li>62-63: Calls <code>save_file</code> to save the contents to the <li>7-25: If the response is <code>GTK_RESPONSE_ACCEPT</code>, the user
file.</li> has clicked on the <code>Save</code> button and the contents will be
<li>66-79: <code>tfe_text_view_saveas</code> function. It shows saved.</li>
GtkFileChooserDialog and prompts the user to choose a file.</li> <li>8: Gets the GFile <code>file</code> from the
<li>73-76: Creates GtkFileChooserDialog. The title is “Save file”. GtkFileChooserDialog.</li>
Transient parent of the dialog is <code>win</code>, which is the <li>9-10: If it doesnt point GFile, a warning message will be output to
top-level window. The action is save mode. The buttons are Cancel and the log. This is not expected.</li>
Save.</li> <li>11: Otherwise, it calls <code>save_file</code> to save the contents
<li>77: connects the “response” signal of the dialog and to the file.</li>
<code>saveas_dialog_response</code> handler.</li> <li>12-22: If <code>save_file</code> has successfully saved the
<li>78: Shows the dialog.</li> contents, the following will be done.
<ul>
<li>If <code>tv-&gt;file</code> is GFile and <code>file</code> is a
different file, unref <code>tv-&gt;file</code>.</li>
<li>If <code>tv-&gt;file</code> is GFile and <code>file</code> points
the same file as <code>tv-&gt;file</code>, nothing needs to do.
Otherwise, <code>tv-&gt;file</code> is set to <code>file</code> and
“change-file” signal is emitted.</li>
</ul></li>
<li>22, 24: Unref <code>file</code>.</li>
<li>26: destroys the file chooser dialog.</li>
</ul>
<h3 id="tfe_text_view_save-function">tfe_text_view_save function</h3>
<div class="sourceCode" id="cb9"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb9-1"><a href="#cb9-1"></a><span class="dt">void</span></span>
<span id="cb9-2"><a href="#cb9-2"></a>tfe_text_view_save <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">)</span> <span class="op">{</span></span>
<span id="cb9-3"><a href="#cb9-3"></a> g_return_if_fail <span class="op">(</span>TFE_IS_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
<span id="cb9-4"><a href="#cb9-4"></a></span>
<span id="cb9-5"><a href="#cb9-5"></a> GtkTextBuffer <span class="op">*</span>tb <span class="op">=</span> gtk_text_view_get_buffer <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
<span id="cb9-6"><a href="#cb9-6"></a> GtkWidget <span class="op">*</span>win <span class="op">=</span> gtk_widget_get_ancestor <span class="op">(</span>GTK_WIDGET <span class="op">(</span>tv<span class="op">),</span> GTK_TYPE_WINDOW<span class="op">);</span></span>
<span id="cb9-7"><a href="#cb9-7"></a></span>
<span id="cb9-8"><a href="#cb9-8"></a> <span class="cf">if</span> <span class="op">(!</span> gtk_text_buffer_get_modified <span class="op">(</span>tb<span class="op">))</span></span>
<span id="cb9-9"><a href="#cb9-9"></a> <span class="cf">return</span><span class="op">;</span> <span class="co">/* no need to save it */</span></span>
<span id="cb9-10"><a href="#cb9-10"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>tv<span class="op">-&gt;</span>file <span class="op">==</span> NULL<span class="op">)</span></span>
<span id="cb9-11"><a href="#cb9-11"></a> tfe_text_view_saveas <span class="op">(</span>tv<span class="op">);</span></span>
<span id="cb9-12"><a href="#cb9-12"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(!</span> G_IS_FILE <span class="op">(</span>tv<span class="op">-&gt;</span>file<span class="op">))</span> <span class="co">// Unexpected error</span></span>
<span id="cb9-13"><a href="#cb9-13"></a> g_error <span class="op">(</span><span class="st">&quot;TfeTextView: The pointer tv-&gt;file isn&#39;t NULL nor GFile.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb9-14"><a href="#cb9-14"></a> <span class="cf">else</span></span>
<span id="cb9-15"><a href="#cb9-15"></a> save_file <span class="op">(</span>tv<span class="op">-&gt;</span>file<span class="op">,</span> tb<span class="op">,</span> GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
<span id="cb9-16"><a href="#cb9-16"></a><span class="op">}</span></span></code></pre></div>
<ul>
<li>The function <code>tfe_text_view_save</code> writes the contents to
the <code>tv-&gt;file</code> file. It calls
<code>tfe_text_view_saveas</code> or <code>save_file</code>.</li>
<li>1-3: The function is public, i.e. it is open to the other objects.
So, it doesnt have <code>static</code> class. Public functions should
check the parameter type with <code>g_return_if_fail</code> function. If
<code>tv</code> is not a pointer to a TfeTextView instance, then it logs
an error message and immediately returns. This function is similar to
<code>g_return_val_if_fail</code>, but no value is returned because
<code>tfe_text_view_save</code> doesnt return a value (void).</li>
<li>5-6: GtkTextBuffer <code>tb</code> and GtkWidget (GtkWindow)
<code>win</code> are set. The function
<code>gtk_widget_get_ancestor (widget, type)</code> returns the first
ancestor of the widget with type. The type is a GType. For example, the
type of GtkWindow is <code>GTK_TYPE_WINDOW</code> and the one of
TfeTextView is <code>TFE_TYPE_TEXT_VIEW</code>. Be careful. The
parent-child relationship here is the one for widgets, not classes. The
top level window may be a GtkApplicationWindow, but it depends on the
application. Because TfeTextView is a library, it cant determine the
top level window type (GtkWindow or GtkApplicationWindow). GtkWindow is
a parent class of GtkApplication window so it is more general.
Therefore, TfeTextView takes GtkWindow as a top level window so that it
can be used by any application.</li>
<li>8-9: If the buffer hasnt modified, it doesnt need to be
saved.</li>
<li>10-11: If <code>tv-&gt;file</code> is NULL, which means no file has
given yet, it calls <code>tfe_text_view_saveas</code> to prompt a user
to select a file and save the contents.</li>
<li>12-13: If <code>tv-&gt;file</code> doesnt point GFile, an error
message is logged out. It is not expected.</li>
<li>14-15: Otherwise, it calls <code>save_file</code> to save the
contents to the file <code>tv-&gt;file</code>.</li>
</ul>
<h3 id="tfe_text_view_saveas-function">tfe_text_view_saveas
function</h3>
<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">void</span></span>
<span id="cb10-2"><a href="#cb10-2"></a>tfe_text_view_saveas <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">)</span> <span class="op">{</span></span>
<span id="cb10-3"><a href="#cb10-3"></a> g_return_if_fail <span class="op">(</span>TFE_IS_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
<span id="cb10-4"><a href="#cb10-4"></a></span>
<span id="cb10-5"><a href="#cb10-5"></a> GtkWidget <span class="op">*</span>dialog<span class="op">;</span></span>
<span id="cb10-6"><a href="#cb10-6"></a> GtkWidget <span class="op">*</span>win <span class="op">=</span> gtk_widget_get_ancestor <span class="op">(</span>GTK_WIDGET <span class="op">(</span>tv<span class="op">),</span> GTK_TYPE_WINDOW<span class="op">);</span></span>
<span id="cb10-7"><a href="#cb10-7"></a></span>
<span id="cb10-8"><a href="#cb10-8"></a> dialog <span class="op">=</span> gtk_file_chooser_dialog_new <span class="op">(</span><span class="st">&quot;Save file&quot;</span><span class="op">,</span> GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> GTK_FILE_CHOOSER_ACTION_SAVE<span class="op">,</span></span>
<span id="cb10-9"><a href="#cb10-9"></a> <span class="st">&quot;Cancel&quot;</span><span class="op">,</span> GTK_RESPONSE_CANCEL<span class="op">,</span></span>
<span id="cb10-10"><a href="#cb10-10"></a> <span class="st">&quot;Save&quot;</span><span class="op">,</span> GTK_RESPONSE_ACCEPT<span class="op">,</span></span>
<span id="cb10-11"><a href="#cb10-11"></a> NULL<span class="op">);</span></span>
<span id="cb10-12"><a href="#cb10-12"></a> g_signal_connect <span class="op">(</span>dialog<span class="op">,</span> <span class="st">&quot;response&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>saveas_dialog_response<span class="op">),</span> tv<span class="op">);</span></span>
<span id="cb10-13"><a href="#cb10-13"></a> gtk_widget_show <span class="op">(</span>dialog<span class="op">);</span></span>
<span id="cb10-14"><a href="#cb10-14"></a><span class="op">}</span></span></code></pre></div>
<ul>
<li>The function <code>tfe_text_view_saveas</code> shows
GtkFileChooserDialog and prompts the user to choose a file and save the
contents.</li>
<li>1-3: Check the type of <code>tv</code> because the caller may be
other object. This function is public.</li>
<li>6: GtkWidget <code>win</code> is set to the top level window.</li>
<li>8-11: Creates GtkFileChooserDialog. It has at least four parameters.
<ul>
<li>const char* title: title of the dialog shown at the bar.</li>
<li>GtkWindow* parent: transient parent window.</li>
<li>GtkFileChooserAction action: action is one of
<code>GTK_FILE_CHOOSER_ACTION_OPEN</code>,
<code>GTK_FILE_CHOOSER_ACTION_SAVE</code> and
<code>GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER</code>. When you want to
save a file, <code>GTK_FILE_CHOOSER_ACTION_SAVE</code> is the
action.</li>
<li>const char* first_button_text: the label text of the first
button.</li>
<li>type: response ID for the sirst button. response ID is one of the
GtkReponseType. See <a
href="https://docs.gtk.org/gtk4/enum.ResponseType.html">GtkResponseType</a>.</li>
<li>followed by pairs of button text and response ID…</li>
<li>NULL is put at the end of the list.</li>
</ul></li>
<li>The GtkFileChooserDialog will have the title “Save file”, transient
parent <code>win</code>, save mode action, cancel and save button.</li>
<li>12: connects the “response” signal and
<code>saveas_dialog_response</code> handler.</li>
<li>13: Shows the dialog.</li>
</ul> </ul>
<figure>
<img src="image/saveas.png" alt="Saveas process" />
<figcaption aria-hidden="true">Saveas process</figcaption>
</figure>
<p>When you use GtkFileChooserDialog, you need to divide the program <p>When you use GtkFileChooserDialog, you need to divide the program
into two parts. One is a function which creates GtkFileChooserDialog and into two parts. One is a function which creates GtkFileChooserDialog and
the other is a signal handler. The function just creates and shows the other is a signal handler. The function just creates and shows
GtkFileChooserDialog. The rest is done by the handler. It gets Gfile GtkFileChooserDialog. The rest is done by the handler. It gets Gfile
from GtkFileChooserDialog and saves the buffer to the file by calling from GtkFileChooserDialog and saves the buffer to the file by calling
<code>save_file</code>.</p> <code>save_file</code>.</p>
<h2 id="open-function">Open function</h2> <figure>
<p>Open function shows GtkFileChooserDialog to users and prompts them to <img src="image/saveas.png" alt="Saveas process" />
choose a file. Then it reads the file and puts the text into <figcaption aria-hidden="true">Saveas process</figcaption>
</figure>
<h2 id="open-related-functions">Open related functions</h2>
<p>Open function shows GtkFileChooserDialog to a user and prompts them
to choose a file. Then it reads the file and puts the text into
GtkTextBuffer.</p> GtkTextBuffer.</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">void</span> tfe_text_view_open <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">,</span> GtkWindow <span class="op">*</span>win<span class="op">);</span></span></code></pre></div> <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><span class="dt">void</span> tfe_text_view_open <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">,</span> GtkWindow <span class="op">*</span>win<span class="op">);</span></span></code></pre></div>
<p>The parameter <code>win</code> is the top-level window. It will be a <p>The parameter <code>win</code> is the top-level window. It will be a
transient parent window of GtkFileChooserDialog when the dialog is transient parent window of GtkFileChooserDialog when the dialog is
created. This allows window managers to keep the dialog on top of the created.</p>
parent window, or center the dialog over the parent window. It is <p>This function may be called just after <code>tv</code> has been
possible to give no parent window to the dialog. However, it is created. In that case, <code>tv</code> has not been incorporated into
encouraged to give a parent window to dialog. This function might be the widget hierarchy. Therefore it is impossible to get the top-level
called just after <code>tv</code> has been created. In that case, window from <code>tv</code>. Thats why the function needs
<code>tv</code> has not been incorporated into the widget hierarchy. <code>win</code> parameter.</p>
Therefore it is impossible to get the top-level window from
<code>tv</code>. Thats why the function needs <code>win</code>
parameter.</p>
<p>This function is usually called when the buffer of <code>tv</code> is <p>This function is usually called when the buffer of <code>tv</code> is
empty. However, even if the buffer is not empty, empty. However, even if the buffer is not empty,
<code>tfe_text_view_open</code> doesnt treat it as an error. If you <code>tfe_text_view_open</code> doesnt treat it as an error. If you
want to revert the buffer, calling this function is appropriate. want to revert the buffer, calling this function is appropriate.</p>
Otherwise probably bad things will happen.</p> <p>Open and read process is divided into two phases. One is showing
<div class="sourceCode" id="cb10"><pre GtkFileChooserDialog and the other is its response handler. The response
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> handler gets the filename, reads the contents of the file and puts it
<span id="cb10-2"><a href="#cb10-2"></a>open_dialog_response<span class="op">(</span>GtkWidget <span class="op">*</span>dialog<span class="op">,</span> gint response<span class="op">,</span> TfeTextView <span class="op">*</span>tv<span class="op">)</span> <span class="op">{</span></span> into the GtkTextBuffer.</p>
<span id="cb10-3"><a href="#cb10-3"></a> GtkTextBuffer <span class="op">*</span>tb <span class="op">=</span> gtk_text_view_get_buffer <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span> <h3 id="open_dialog_response-function">open_dialog_response
<span id="cb10-4"><a href="#cb10-4"></a> GFile <span class="op">*</span>file<span class="op">;</span></span> function</h3>
<span id="cb10-5"><a href="#cb10-5"></a> <span class="dt">char</span> <span class="op">*</span>contents<span class="op">;</span></span> <div class="sourceCode" id="cb12"><pre
<span id="cb10-6"><a href="#cb10-6"></a> gsize length<span class="op">;</span></span> class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb12-1"><a href="#cb12-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb10-7"><a href="#cb10-7"></a> GtkWidget <span class="op">*</span>message_dialog<span class="op">;</span></span> <span id="cb12-2"><a href="#cb12-2"></a>open_dialog_response<span class="op">(</span>GtkWidget <span class="op">*</span>dialog<span class="op">,</span> gint response<span class="op">,</span> TfeTextView <span class="op">*</span>tv<span class="op">)</span> <span class="op">{</span></span>
<span id="cb10-8"><a href="#cb10-8"></a> GError <span class="op">*</span>err <span class="op">=</span> NULL<span class="op">;</span></span> <span id="cb12-3"><a href="#cb12-3"></a> GtkTextBuffer <span class="op">*</span>tb <span class="op">=</span> gtk_text_view_get_buffer <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
<span id="cb10-9"><a href="#cb10-9"></a></span> <span id="cb12-4"><a href="#cb12-4"></a> GFile <span class="op">*</span>file<span class="op">;</span></span>
<span id="cb10-10"><a href="#cb10-10"></a> <span class="cf">if</span> <span class="op">(</span>response <span class="op">!=</span> GTK_RESPONSE_ACCEPT<span class="op">)</span></span> <span id="cb12-5"><a href="#cb12-5"></a> <span class="dt">char</span> <span class="op">*</span>contents<span class="op">;</span></span>
<span id="cb10-11"><a href="#cb10-11"></a> g_signal_emit <span class="op">(</span>tv<span class="op">,</span> tfe_text_view_signals<span class="op">[</span>OPEN_RESPONSE<span class="op">],</span> <span class="dv">0</span><span class="op">,</span> TFE_OPEN_RESPONSE_CANCEL<span class="op">);</span></span> <span id="cb12-6"><a href="#cb12-6"></a> gsize length<span class="op">;</span></span>
<span id="cb10-12"><a href="#cb10-12"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(!</span> G_IS_FILE <span class="op">(</span>file <span class="op">=</span> gtk_file_chooser_get_file <span class="op">(</span>GTK_FILE_CHOOSER <span class="op">(</span>dialog<span class="op">))))</span> <span class="op">{</span></span> <span id="cb12-7"><a href="#cb12-7"></a> GtkWidget <span class="op">*</span>message_dialog<span class="op">;</span></span>
<span id="cb10-13"><a href="#cb10-13"></a> g_warning <span class="op">(</span><span class="st">&quot;TfeTextView: gtk_file_chooser_get_file returns non GFile.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span> <span id="cb12-8"><a href="#cb12-8"></a> GError <span class="op">*</span>err <span class="op">=</span> NULL<span class="op">;</span></span>
<span id="cb10-14"><a href="#cb10-14"></a> g_signal_emit <span class="op">(</span>tv<span class="op">,</span> tfe_text_view_signals<span class="op">[</span>OPEN_RESPONSE<span class="op">],</span> <span class="dv">0</span><span class="op">,</span> TFE_OPEN_RESPONSE_ERROR<span class="op">);</span></span> <span id="cb12-9"><a href="#cb12-9"></a></span>
<span id="cb10-15"><a href="#cb10-15"></a> <span class="op">}</span> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(!</span> g_file_load_contents <span class="op">(</span>file<span class="op">,</span> NULL<span class="op">,</span> <span class="op">&amp;</span>contents<span class="op">,</span> <span class="op">&amp;</span>length<span class="op">,</span> NULL<span class="op">,</span> <span class="op">&amp;</span>err<span class="op">))</span> <span class="op">{</span> <span class="co">/* read error */</span></span> <span id="cb12-10"><a href="#cb12-10"></a> <span class="cf">if</span> <span class="op">(</span>response <span class="op">!=</span> GTK_RESPONSE_ACCEPT<span class="op">)</span></span>
<span id="cb10-16"><a href="#cb10-16"></a> g_object_unref <span class="op">(</span>file<span class="op">);</span></span> <span id="cb12-11"><a href="#cb12-11"></a> g_signal_emit <span class="op">(</span>tv<span class="op">,</span> tfe_text_view_signals<span class="op">[</span>OPEN_RESPONSE<span class="op">],</span> <span class="dv">0</span><span class="op">,</span> TFE_OPEN_RESPONSE_CANCEL<span class="op">);</span></span>
<span id="cb10-17"><a href="#cb10-17"></a> message_dialog <span class="op">=</span> gtk_message_dialog_new <span class="op">(</span>GTK_WINDOW <span class="op">(</span>dialog<span class="op">),</span> GTK_DIALOG_MODAL<span class="op">,</span></span> <span id="cb12-12"><a href="#cb12-12"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(!</span> G_IS_FILE <span class="op">(</span>file <span class="op">=</span> gtk_file_chooser_get_file <span class="op">(</span>GTK_FILE_CHOOSER <span class="op">(</span>dialog<span class="op">))))</span> <span class="op">{</span></span>
<span id="cb10-18"><a href="#cb10-18"></a> GTK_MESSAGE_ERROR<span class="op">,</span> GTK_BUTTONS_CLOSE<span class="op">,</span></span> <span id="cb12-13"><a href="#cb12-13"></a> g_warning <span class="op">(</span><span class="st">&quot;TfeTextView: gtk_file_chooser_get_file returns non GFile.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb10-19"><a href="#cb10-19"></a> <span class="st">&quot;%s.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">,</span> err<span class="op">-&gt;</span>message<span class="op">);</span></span> <span id="cb12-14"><a href="#cb12-14"></a> g_signal_emit <span class="op">(</span>tv<span class="op">,</span> tfe_text_view_signals<span class="op">[</span>OPEN_RESPONSE<span class="op">],</span> <span class="dv">0</span><span class="op">,</span> TFE_OPEN_RESPONSE_ERROR<span class="op">);</span></span>
<span id="cb10-20"><a href="#cb10-20"></a> g_signal_connect <span class="op">(</span>message_dialog<span class="op">,</span> <span class="st">&quot;response&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>gtk_window_destroy<span class="op">),</span> NULL<span class="op">);</span></span> <span id="cb12-15"><a href="#cb12-15"></a> <span class="op">}</span> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(!</span> g_file_load_contents <span class="op">(</span>file<span class="op">,</span> NULL<span class="op">,</span> <span class="op">&amp;</span>contents<span class="op">,</span> <span class="op">&amp;</span>length<span class="op">,</span> NULL<span class="op">,</span> <span class="op">&amp;</span>err<span class="op">))</span> <span class="op">{</span> <span class="co">/* read error */</span></span>
<span id="cb10-21"><a href="#cb10-21"></a> gtk_widget_show <span class="op">(</span>message_dialog<span class="op">);</span></span> <span id="cb12-16"><a href="#cb12-16"></a> g_object_unref <span class="op">(</span>file<span class="op">);</span></span>
<span id="cb10-22"><a href="#cb10-22"></a> g_error_free <span class="op">(</span>err<span class="op">);</span></span> <span id="cb12-17"><a href="#cb12-17"></a> message_dialog <span class="op">=</span> gtk_message_dialog_new <span class="op">(</span>GTK_WINDOW <span class="op">(</span>dialog<span class="op">),</span> GTK_DIALOG_MODAL<span class="op">,</span></span>
<span id="cb10-23"><a href="#cb10-23"></a> g_signal_emit <span class="op">(</span>tv<span class="op">,</span> tfe_text_view_signals<span class="op">[</span>OPEN_RESPONSE<span class="op">],</span> <span class="dv">0</span><span class="op">,</span> TFE_OPEN_RESPONSE_ERROR<span class="op">);</span></span> <span id="cb12-18"><a href="#cb12-18"></a> GTK_MESSAGE_ERROR<span class="op">,</span> GTK_BUTTONS_CLOSE<span class="op">,</span> <span class="st">&quot;%s&quot;</span><span class="op">,</span> err<span class="op">-&gt;</span>message<span class="op">);</span></span>
<span id="cb10-24"><a href="#cb10-24"></a> <span class="op">}</span> <span class="cf">else</span> <span class="op">{</span></span> <span id="cb12-19"><a href="#cb12-19"></a> g_signal_connect <span class="op">(</span>message_dialog<span class="op">,</span> <span class="st">&quot;response&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>gtk_window_destroy<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb10-25"><a href="#cb10-25"></a> gtk_text_buffer_set_text <span class="op">(</span>tb<span class="op">,</span> contents<span class="op">,</span> length<span class="op">);</span></span> <span id="cb12-20"><a href="#cb12-20"></a> gtk_widget_show <span class="op">(</span>message_dialog<span class="op">);</span></span>
<span id="cb10-26"><a href="#cb10-26"></a> g_free <span class="op">(</span>contents<span class="op">);</span></span> <span id="cb12-21"><a href="#cb12-21"></a> g_error_free <span class="op">(</span>err<span class="op">);</span></span>
<span id="cb10-27"><a href="#cb10-27"></a> <span class="cf">if</span> <span class="op">(</span>G_IS_FILE <span class="op">(</span>tv<span class="op">-&gt;</span>file<span class="op">))</span></span> <span id="cb12-22"><a href="#cb12-22"></a> g_signal_emit <span class="op">(</span>tv<span class="op">,</span> tfe_text_view_signals<span class="op">[</span>OPEN_RESPONSE<span class="op">],</span> <span class="dv">0</span><span class="op">,</span> TFE_OPEN_RESPONSE_ERROR<span class="op">);</span></span>
<span id="cb10-28"><a href="#cb10-28"></a> g_object_unref <span class="op">(</span>tv<span class="op">-&gt;</span>file<span class="op">);</span></span> <span id="cb12-23"><a href="#cb12-23"></a> <span class="op">}</span> <span class="cf">else</span> <span class="op">{</span></span>
<span id="cb10-29"><a href="#cb10-29"></a> tv<span class="op">-&gt;</span>file <span class="op">=</span> file<span class="op">;</span></span> <span id="cb12-24"><a href="#cb12-24"></a> gtk_text_buffer_set_text <span class="op">(</span>tb<span class="op">,</span> contents<span class="op">,</span> length<span class="op">);</span></span>
<span id="cb10-30"><a href="#cb10-30"></a> gtk_text_buffer_set_modified <span class="op">(</span>tb<span class="op">,</span> FALSE<span class="op">);</span></span> <span id="cb12-25"><a href="#cb12-25"></a> g_free <span class="op">(</span>contents<span class="op">);</span></span>
<span id="cb10-31"><a href="#cb10-31"></a> g_signal_emit <span class="op">(</span>tv<span class="op">,</span> tfe_text_view_signals<span class="op">[</span>OPEN_RESPONSE<span class="op">],</span> <span class="dv">0</span><span class="op">,</span> TFE_OPEN_RESPONSE_SUCCESS<span class="op">);</span></span> <span id="cb12-26"><a href="#cb12-26"></a> gtk_text_buffer_set_modified <span class="op">(</span>tb<span class="op">,</span> FALSE<span class="op">);</span></span>
<span id="cb10-32"><a href="#cb10-32"></a> g_signal_emit <span class="op">(</span>tv<span class="op">,</span> tfe_text_view_signals<span class="op">[</span>CHANGE_FILE<span class="op">],</span> <span class="dv">0</span><span class="op">);</span></span> <span id="cb12-27"><a href="#cb12-27"></a> <span class="co">// G_IS_FILE(tv-&gt;file) &amp;&amp; tv-&gt;file == file =&gt; unref(tv-&gt;file), tv-&gt;file=file, emit response with SUCCESS</span></span>
<span id="cb10-33"><a href="#cb10-33"></a> <span class="op">}</span></span> <span id="cb12-28"><a href="#cb12-28"></a> <span class="co">// G_IS_FILE(tv-&gt;file) &amp;&amp; tv-&gt;file != file =&gt; unref(tv-&gt;file), tv-&gt;file=file, emit response with SUCCESS, emit change-file</span></span>
<span id="cb10-34"><a href="#cb10-34"></a> gtk_window_destroy <span class="op">(</span>GTK_WINDOW <span class="op">(</span>dialog<span class="op">));</span></span> <span id="cb12-29"><a href="#cb12-29"></a> <span class="co">// tv-&gt;file==NULL =&gt; tv-&gt;file=file, emit response with SUCCESS, emit change-file</span></span>
<span id="cb10-35"><a href="#cb10-35"></a><span class="op">}</span></span> <span id="cb12-30"><a href="#cb12-30"></a> <span class="co">// The order is important. If you unref tv-&gt;file first, you can&#39;t compare tv-&gt;file and file anymore.</span></span>
<span id="cb10-36"><a href="#cb10-36"></a></span> <span id="cb12-31"><a href="#cb12-31"></a> <span class="co">// And open-response signal is emitted after new tv-&gt;file is set. Or the handler can&#39;t catch the new file.</span></span>
<span id="cb10-37"><a href="#cb10-37"></a><span class="dt">void</span></span> <span id="cb12-32"><a href="#cb12-32"></a> <span class="cf">if</span> <span class="op">(!</span> <span class="op">(</span>G_IS_FILE <span class="op">(</span>tv<span class="op">-&gt;</span>file<span class="op">)</span> <span class="op">&amp;&amp;</span> g_file_equal <span class="op">(</span>tv<span class="op">-&gt;</span>file<span class="op">,</span> file<span class="op">)))</span></span>
<span id="cb10-38"><a href="#cb10-38"></a>tfe_text_view_open <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">,</span> GtkWindow <span class="op">*</span>win<span class="op">)</span> <span class="op">{</span></span> <span id="cb12-33"><a href="#cb12-33"></a> g_signal_emit <span class="op">(</span>tv<span class="op">,</span> tfe_text_view_signals<span class="op">[</span>CHANGE_FILE<span class="op">],</span> <span class="dv">0</span><span class="op">);</span></span>
<span id="cb10-39"><a href="#cb10-39"></a> g_return_if_fail <span class="op">(</span>TFE_IS_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span> <span id="cb12-34"><a href="#cb12-34"></a> <span class="cf">if</span> <span class="op">(</span>G_IS_FILE <span class="op">(</span>tv<span class="op">-&gt;</span>file<span class="op">))</span></span>
<span id="cb10-40"><a href="#cb10-40"></a> g_return_if_fail <span class="op">(</span>GTK_IS_WINDOW <span class="op">(</span>win<span class="op">));</span></span> <span id="cb12-35"><a href="#cb12-35"></a> g_object_unref <span class="op">(</span>tv<span class="op">-&gt;</span>file<span class="op">);</span></span>
<span id="cb10-41"><a href="#cb10-41"></a></span> <span id="cb12-36"><a href="#cb12-36"></a> tv<span class="op">-&gt;</span>file <span class="op">=</span> file<span class="op">;</span> <span class="co">// The ownership of &#39;file&#39; moves to TfeTextView</span></span>
<span id="cb10-42"><a href="#cb10-42"></a> GtkWidget <span class="op">*</span>dialog<span class="op">;</span></span> <span id="cb12-37"><a href="#cb12-37"></a> g_signal_emit <span class="op">(</span>tv<span class="op">,</span> tfe_text_view_signals<span class="op">[</span>OPEN_RESPONSE<span class="op">],</span> <span class="dv">0</span><span class="op">,</span> TFE_OPEN_RESPONSE_SUCCESS<span class="op">);</span></span>
<span id="cb10-43"><a href="#cb10-43"></a></span> <span id="cb12-38"><a href="#cb12-38"></a> <span class="op">}</span></span>
<span id="cb10-44"><a href="#cb10-44"></a> dialog <span class="op">=</span> gtk_file_chooser_dialog_new <span class="op">(</span><span class="st">&quot;Open file&quot;</span><span class="op">,</span> win<span class="op">,</span> GTK_FILE_CHOOSER_ACTION_OPEN<span class="op">,</span></span> <span id="cb12-39"><a href="#cb12-39"></a> gtk_window_destroy <span class="op">(</span>GTK_WINDOW <span class="op">(</span>dialog<span class="op">));</span></span>
<span id="cb10-45"><a href="#cb10-45"></a> <span class="st">&quot;Cancel&quot;</span><span class="op">,</span> GTK_RESPONSE_CANCEL<span class="op">,</span></span> <span id="cb12-40"><a href="#cb12-40"></a><span class="op">}</span></span></code></pre></div>
<span id="cb10-46"><a href="#cb10-46"></a> <span class="st">&quot;Open&quot;</span><span class="op">,</span> GTK_RESPONSE_ACCEPT<span class="op">,</span></span>
<span id="cb10-47"><a href="#cb10-47"></a> NULL<span class="op">);</span></span>
<span id="cb10-48"><a href="#cb10-48"></a> g_signal_connect <span class="op">(</span>dialog<span class="op">,</span> <span class="st">&quot;response&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>open_dialog_response<span class="op">),</span> tv<span class="op">);</span></span>
<span id="cb10-49"><a href="#cb10-49"></a> gtk_widget_show <span class="op">(</span>dialog<span class="op">);</span></span>
<span id="cb10-50"><a href="#cb10-50"></a><span class="op">}</span></span></code></pre></div>
<ul> <ul>
<li>37-50: <code>tfe_text_view_open</code> function.</li> <li>2: The handler <code>open_dialog_response</code> has three
<li>44-47: Creates GtkFileChooserDialog. The title is “Open file”. parameters.
Transient parent window is the top-level window of the application, <ul>
which is given by the caller. The action is open mode. The buttons are <li>GtkWidget *dialog: FileChooserDialog on which the “response” signal
Cancel and Open.</li> is emitted.</li>
<li>48: connects the “response” signal of the dialog and <li>gint response: response ID</li>
<code>open_dialog_response</code> signal handler.</li> <li>TfeTextView *tv: textview to put the contents to</li>
<li>49: Shows the dialog.</li> </ul></li>
<li>1-35: <code>open_dialog_response</code> signal handler.</li> <li>10-11: If the response is not <code>GTK_RESPONSE_ACCEPT</code>, the
<li>10-11: If the response from GtkFileChooserDialog is not user has clicked on the “Cancel” button or close button on the header
<code>GTK_RESPONSE_ACCEPT</code>, the user has clicked on the “Cancel” bar. Then, “open-response” signal is emitted. The parameter of the
button or close button on the header bar. Then, “open-response” signal signal is <code>TFE_OPEN_RESPONSE_CANCEL</code>.</li>
is emitted. The parameter of the signal is <li>12-14: Gets the pointer to the GFile by
<code>TFE_OPEN_RESPONSE_CANCEL</code>.</li>
<li>12-14: Gets the pointer to the Gfile by
<code>gtk_file_chooser_get_file</code>. If it doesnt point GFile, maybe <code>gtk_file_chooser_get_file</code>. If it doesnt point GFile, maybe
an error has occurred. Then it emits “open-response” signal with the an error has occurred. It is not expected, though. Then it emits
parameter <code>TFE_OPEN_RESPONSE_ERROR</code>.</li> “open-response” signal with the parameter
<li>15-23: If an error occurs at file reading, then it decreases the <code>TFE_OPEN_RESPONSE_ERROR</code>.</li>
reference count of the Gfile, shows a message dialog to report the error <li>15-22: If an error occurs when file reading, then it decreases the
reference count of the GFile, shows a message dialog to report the error
to the user and emits “open-response” signal with the parameter to the user and emits “open-response” signal with the parameter
<code>TFE_OPEN_RESPONSE_ERROR</code>.</li> <code>TFE_OPEN_RESPONSE_ERROR</code>.</li>
<li>24-33: If the file has successfully been read, then the text is <li>23-38: If the file has been successfully read, the following is
inserted to GtkTextBuffer, frees the temporary buffer pointed by carried out. (It is not simple.)
<code>contents</code> and sets <code>tv-&gt;file</code> to point the <ul>
file (no duplication is not necessary). Then, it emits “open-response” <li>the text is inserted to GtkTextBuffer</li>
signal with the parameter <code>TFE_OPEN_RESPONSE_SUCCESS</code> and <li>the temporary buffer <code>contents</code> is freed</li>
emits “change-file” signal.</li> <li>modify-bit is set to FALSE</li>
<li>34: destroys GtkFileCooserDialog.</li> <li>open-response signal is emitted with
<code>TFE_OPEN_REPONSE_SUCCESS</code></li>
<li>If the file are changed, change-file signal is emitted</li>
<li>If <code>tv-&gt;file</code> isnt NULL,
<code>g_object_unref(tv-&gt;file)</code> is called</li>
<li><code>tv-&gt;file</code> is assigned with <code>file</code></li>
</ul></li>
<li>39: destroys GtkFileChooserDialog.</li>
</ul> </ul>
<p>Now lets think about the whole process between the caller and <h3 id="tfe_text_view_open-function">tfe_text_view_open function</h3>
TfeTextView. It is shown in the following diagram and you would think <div class="sourceCode" id="cb13"><pre
that it is really complicated. Because signal is the only way for class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb13-1"><a href="#cb13-1"></a><span class="dt">void</span></span>
GtkFileChooserDialog to communicate with others. In GTK 3, <span id="cb13-2"><a href="#cb13-2"></a>tfe_text_view_open <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">,</span> GtkWindow <span class="op">*</span>win<span class="op">)</span> <span class="op">{</span></span>
<code>gtk_dialog_run</code> function is available. It simplifies the <span id="cb13-3"><a href="#cb13-3"></a> g_return_if_fail <span class="op">(</span>TFE_IS_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
process. However, in GTK 4, <code>gtk_dialog_run</code> is unavailable <span id="cb13-4"><a href="#cb13-4"></a> <span class="co">// &#39;win&#39; is used for a transient window of the GtkFileChooserDialog.</span></span>
any more.</p> <span id="cb13-5"><a href="#cb13-5"></a> <span class="co">// It can be NULL.</span></span>
<span id="cb13-6"><a href="#cb13-6"></a> g_return_if_fail <span class="op">(</span>GTK_IS_WINDOW <span class="op">(</span>win<span class="op">)</span> <span class="op">||</span> win <span class="op">==</span> NULL<span class="op">);</span></span>
<span id="cb13-7"><a href="#cb13-7"></a></span>
<span id="cb13-8"><a href="#cb13-8"></a> GtkWidget <span class="op">*</span>dialog<span class="op">;</span></span>
<span id="cb13-9"><a href="#cb13-9"></a></span>
<span id="cb13-10"><a href="#cb13-10"></a> dialog <span class="op">=</span> gtk_file_chooser_dialog_new <span class="op">(</span><span class="st">&quot;Open file&quot;</span><span class="op">,</span> win<span class="op">,</span> GTK_FILE_CHOOSER_ACTION_OPEN<span class="op">,</span></span>
<span id="cb13-11"><a href="#cb13-11"></a> <span class="st">&quot;Cancel&quot;</span><span class="op">,</span> GTK_RESPONSE_CANCEL<span class="op">,</span> <span class="st">&quot;Open&quot;</span><span class="op">,</span> GTK_RESPONSE_ACCEPT<span class="op">,</span> NULL<span class="op">);</span></span>
<span id="cb13-12"><a href="#cb13-12"></a> g_signal_connect <span class="op">(</span>dialog<span class="op">,</span> <span class="st">&quot;response&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>open_dialog_response<span class="op">),</span> tv<span class="op">);</span></span>
<span id="cb13-13"><a href="#cb13-13"></a> gtk_widget_show <span class="op">(</span>dialog<span class="op">);</span></span>
<span id="cb13-14"><a href="#cb13-14"></a><span class="op">}</span></span></code></pre></div>
<ul>
<li>3-4: Check the type of the arguments <code>tv</code> and
<code>win</code>. Public functions always need to check the
arguments.</li>
<li>10-11: Creates GtkFileChooserDialog.
<ul>
<li>The title is “Open file”.</li>
<li>Transient parent window is the top-level window
<code>win</code>.</li>
<li>The action is open mode.</li>
<li>The buttons are Cancel and Open.</li>
</ul></li>
<li>12: connects the “response” signal and the handler.</li>
<li>13: Shows the dialog.</li>
</ul>
<p>The whole process between the caller and TfeTextView is shown in the
following diagram. It is really complicated. Because signal is the only
way for GtkFileChooserDialog to communicate with others.</p>
<figure> <figure>
<img src="image/open.png" alt="Caller and TfeTextView" /> <img src="image/open.png" alt="Caller and TfeTextView" />
<figcaption aria-hidden="true">Caller and TfeTextView</figcaption> <figcaption aria-hidden="true">Caller and TfeTextView</figcaption>
@ -560,33 +686,34 @@ select a file from GtkFileChooserDialog.</li>
and emits a signal to inform the status as a response code.</li> and emits a signal to inform the status as a response code.</li>
<li>The handler out of the TfeTextView receives the signal.</li> <li>The handler out of the TfeTextView receives the signal.</li>
</ol> </ol>
<h2 id="getting-gfile">Getting Gfile</h2> <h2 id="getting-gfile-in-tfetextview">Getting GFile in TfeTextView</h2>
<p><code>gtk_text_view_get_file</code> is a simple function shown as <p>You can get the GFile in a TfeTextView instance with
follows.</p> <code>tfe_text_view_get_file</code>. It is very simple.</p>
<div class="sourceCode" id="cb11"><pre <div class="sourceCode" id="cb14"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb11-1"><a href="#cb11-1"></a>GFile <span class="op">*</span></span> class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb14-1"><a href="#cb14-1"></a>GFile <span class="op">*</span></span>
<span id="cb11-2"><a href="#cb11-2"></a>tfe_text_view_get_file <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">)</span> <span class="op">{</span></span> <span id="cb14-2"><a href="#cb14-2"></a>tfe_text_view_get_file <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">)</span> <span class="op">{</span></span>
<span id="cb11-3"><a href="#cb11-3"></a> g_return_val_if_fail <span class="op">(</span>TFE_IS_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> NULL<span class="op">);</span></span> <span id="cb14-3"><a href="#cb14-3"></a> g_return_val_if_fail <span class="op">(</span>TFE_IS_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb11-4"><a href="#cb11-4"></a></span> <span id="cb14-4"><a href="#cb14-4"></a></span>
<span id="cb11-5"><a href="#cb11-5"></a> <span class="cf">if</span> <span class="op">(</span>G_IS_FILE <span class="op">(</span>tv<span class="op">-&gt;</span>file<span class="op">))</span></span> <span id="cb14-5"><a href="#cb14-5"></a> <span class="cf">if</span> <span class="op">(</span>G_IS_FILE <span class="op">(</span>tv<span class="op">-&gt;</span>file<span class="op">))</span></span>
<span id="cb11-6"><a href="#cb11-6"></a> <span class="cf">return</span> g_file_dup <span class="op">(</span>tv<span class="op">-&gt;</span>file<span class="op">);</span></span> <span id="cb14-6"><a href="#cb14-6"></a> <span class="cf">return</span> g_file_dup <span class="op">(</span>tv<span class="op">-&gt;</span>file<span class="op">);</span></span>
<span id="cb11-7"><a href="#cb11-7"></a> <span class="cf">else</span></span> <span id="cb14-7"><a href="#cb14-7"></a> <span class="cf">else</span></span>
<span id="cb11-8"><a href="#cb11-8"></a> <span class="cf">return</span> NULL<span class="op">;</span></span> <span id="cb14-8"><a href="#cb14-8"></a> <span class="cf">return</span> NULL<span class="op">;</span></span>
<span id="cb11-9"><a href="#cb11-9"></a><span class="op">}</span></span></code></pre></div> <span id="cb14-9"><a href="#cb14-9"></a><span class="op">}</span></span></code></pre></div>
<p>The important thing is to duplicate <code>tv-&gt;file</code>. <p>The important thing is to duplicate <code>tv-&gt;file</code>.
Otherwise, if the caller frees the GFile object, Otherwise, if the caller frees the GFile object,
<code>tv-&gt;file</code> is no more guaranteed to point the GFile. <code>tv-&gt;file</code> is no more guaranteed to point the GFile.
Another reason to use <code>g_file_dup</code> is that GFile isnt Another reason to use <code>g_file_dup</code> is that GFile isnt
thread-safe. If you use GFile in the different thread, the duplication thread-safe. If you use GFile in the different thread, the duplication
is necessary. See <a is necessary. See <a
href="https://docs.gtk.org/gio/method.File.dup.html">Gio API Reference, href="https://docs.gtk.org/gio/method.File.dup.html">Gio API Reference
g_file_dup</a>.</p> g_file_dup</a>.</p>
<h2 id="the-api-document-and-source-file-of-tfetextview.c">The API <h2 id="the-api-document-and-source-file-of-tfetextview.c">The API
document and source file of tfetextview.c</h2> document and source file of tfetextview.c</h2>
<p>Refer API document of TfeTextView. Its original markdown file is <p>Refer <a
under the directory <code>src/tfetextview</code>.</p> href="https://toshiocp.github.io/Gtk4-tutorial/tfetextview_doc.html">API
<p>All the source files are listed in <a href="sec16.html">Section document of TfeTextView</a>. The markdown file is under the directory
16</a>. You can find them under src/tfe5 and src/tfetextview <code>src/tfetextview</code>.</p>
<p>You can find all the TfeTextView source codes under src/tfetextview
directories.</p> directories.</p>
</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> <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>

View file

@ -5,7 +5,7 @@
<meta name="generator" content="pandoc" /> <meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <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"> <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> <title>GTK 4 tutorial</title>
<style> <style>
code{white-space: pre-wrap;} code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;} span.smallcaps{font-variant: small-caps;}
@ -112,30 +112,43 @@
</div> </div>
</nav> </nav>
<h1 id="functions-in-gtknotebook">Functions in GtkNotebook</h1> <h1 id="functions-in-gtknotebook">Functions in GtkNotebook</h1>
<p>GtkNotebook is a very important object in the text file editor <code>tfe</code>. It connects the application and TfeTextView objects. A set of public functions are declared in <code>tfenotebook.h</code>. The word “tfenotebook” is used only in filenames. Theres no “TfeNotebook” object.</p> <p>GtkNotebook is a very important object in the text file editor
<div class="sourceCode" id="cb1"><pre class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1"></a><span class="dt">void</span></span> <code>tfe</code>. It connects the application and TfeTextView objects. A
<span id="cb1-2"><a href="#cb1-2"></a>notebook_page_save(GtkNotebook *nb);</span> set of public functions are declared in <code>tfenotebook.h</code>. The
word “tfenotebook” is used only in filenames. Theres no “TfeNotebook”
object.</p>
<div class="sourceCode" id="cb1"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1"></a><span class="dt">void</span></span>
<span id="cb1-2"><a href="#cb1-2"></a>notebook_page_save<span class="op">(</span>GtkNotebook <span class="op">*</span>nb<span class="op">);</span></span>
<span id="cb1-3"><a href="#cb1-3"></a></span> <span id="cb1-3"><a href="#cb1-3"></a></span>
<span id="cb1-4"><a href="#cb1-4"></a><span class="dt">void</span></span> <span id="cb1-4"><a href="#cb1-4"></a><span class="dt">void</span></span>
<span id="cb1-5"><a href="#cb1-5"></a>notebook_page_close (GtkNotebook *nb);</span> <span id="cb1-5"><a href="#cb1-5"></a>notebook_page_close <span class="op">(</span>GtkNotebook <span class="op">*</span>nb<span class="op">);</span></span>
<span id="cb1-6"><a href="#cb1-6"></a></span> <span id="cb1-6"><a href="#cb1-6"></a></span>
<span id="cb1-7"><a href="#cb1-7"></a><span class="dt">void</span></span> <span id="cb1-7"><a href="#cb1-7"></a><span class="dt">void</span></span>
<span id="cb1-8"><a href="#cb1-8"></a>notebook_page_open (GtkNotebook *nb);</span> <span id="cb1-8"><a href="#cb1-8"></a>notebook_page_open <span class="op">(</span>GtkNotebook <span class="op">*</span>nb<span class="op">);</span></span>
<span id="cb1-9"><a href="#cb1-9"></a></span> <span id="cb1-9"><a href="#cb1-9"></a></span>
<span id="cb1-10"><a href="#cb1-10"></a><span class="dt">void</span></span> <span id="cb1-10"><a href="#cb1-10"></a><span class="dt">void</span></span>
<span id="cb1-11"><a href="#cb1-11"></a>notebook_page_new_with_file (GtkNotebook *nb, GFile *file);</span> <span id="cb1-11"><a href="#cb1-11"></a>notebook_page_new_with_file <span class="op">(</span>GtkNotebook <span class="op">*</span>nb<span class="op">,</span> GFile <span class="op">*</span>file<span class="op">);</span></span>
<span id="cb1-12"><a href="#cb1-12"></a></span> <span id="cb1-12"><a href="#cb1-12"></a></span>
<span id="cb1-13"><a href="#cb1-13"></a><span class="dt">void</span></span> <span id="cb1-13"><a href="#cb1-13"></a><span class="dt">void</span></span>
<span id="cb1-14"><a href="#cb1-14"></a>notebook_page_new (GtkNotebook *nb);</span></code></pre></div> <span id="cb1-14"><a href="#cb1-14"></a>notebook_page_new <span class="op">(</span>GtkNotebook <span class="op">*</span>nb<span class="op">);</span></span></code></pre></div>
<p>This header file describes the public functions in <code>tfenotebook.c</code>.</p> <p>This header file describes the public functions in
<code>tfenotebook.c</code>.</p>
<ul> <ul>
<li>1-2: <code>notebook_page_save</code> saves the current page to the file of which the name specified in the tab. If the name is <code>untitled</code> or <code>untitled</code> followed by digits, FileChooserDialog appears and a user can choose or specify a filename.</li> <li>1-2: <code>notebook_page_save</code> saves the current page to the
file of which the name specified in the tab. If the name is
<code>untitled</code> or <code>untitled</code> followed by digits,
FileChooserDialog appears and a user can choose or specify a
filename.</li>
<li>4-5: <code>notebook_page_close</code> closes the current page.</li> <li>4-5: <code>notebook_page_close</code> closes the current page.</li>
<li>7-8: <code>notebook_page_open</code> shows a file chooser dialog and a user can choose a file. The file is inserted to a new page.</li> <li>7-8: <code>notebook_page_open</code> shows a file chooser dialog and
<li>10-11: <code>notebook_page_new_with_file</code> creates a new page and the file given as an argument is read and inserted into the page.</li> a user can choose a file. The file is inserted to a new page.</li>
<li>10-11: <code>notebook_page_new_with_file</code> creates a new page
and a file given as an argument is read and inserted into the page.</li>
<li>13-14: <code>notebook_page_new</code> creates a new empty page.</li> <li>13-14: <code>notebook_page_new</code> creates a new empty page.</li>
</ul> </ul>
<p>You probably find that the functions except <code>notebook_page_close</code> are higher level functions of</p> <p>You probably find that the functions except
<code>notebook_page_close</code> are higher level functions of</p>
<ul> <ul>
<li><code>tfe_text_view_save</code></li> <li><code>tfe_text_view_save</code></li>
<li><code>tef_text_view_open</code></li> <li><code>tef_text_view_open</code></li>
@ -143,199 +156,298 @@
<li><code>tfe_text_view_new</code></li> <li><code>tfe_text_view_new</code></li>
</ul> </ul>
<p>respectively.</p> <p>respectively.</p>
<p>There are two layers. One of them is <code>tfe_text_view ...</code>, which is the lower level layer. The other is <code>note_book ...</code>, which is the higher level layer.</p> <p>There are two layers. One of them is <code>tfe_text_view ...</code>,
which is the lower level layer. The other is <code>note_book ...</code>,
which is the higher level layer.</p>
<p>Now lets look at the program of each function.</p> <p>Now lets look at the program of each function.</p>
<h2 id="notebook_page_new">notebook_page_new</h2> <h2 id="notebook_page_new">notebook_page_new</h2>
<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> gchar*</span> <div class="sourceCode" id="cb2"><pre
<span id="cb2-2"><a href="#cb2-2"></a>get_untitled () {</span> 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">char</span><span class="op">*</span></span>
<span id="cb2-3"><a href="#cb2-3"></a> <span class="dt">static</span> <span class="dt">int</span> c = -<span class="dv">1</span>;</span> <span id="cb2-2"><a href="#cb2-2"></a>get_untitled <span class="op">()</span> <span class="op">{</span></span>
<span id="cb2-4"><a href="#cb2-4"></a> <span class="cf">if</span> (++c == <span class="dv">0</span>) </span> <span id="cb2-3"><a href="#cb2-3"></a> <span class="dt">static</span> <span class="dt">int</span> c <span class="op">=</span> <span class="op">-</span><span class="dv">1</span><span class="op">;</span></span>
<span id="cb2-5"><a href="#cb2-5"></a> <span class="cf">return</span> g_strdup_printf(<span class="st">&quot;Untitled&quot;</span>);</span> <span id="cb2-4"><a href="#cb2-4"></a> <span class="cf">if</span> <span class="op">(++</span>c <span class="op">==</span> <span class="dv">0</span><span class="op">)</span> </span>
<span id="cb2-5"><a href="#cb2-5"></a> <span class="cf">return</span> g_strdup_printf<span class="op">(</span><span class="st">&quot;Untitled&quot;</span><span class="op">);</span></span>
<span id="cb2-6"><a href="#cb2-6"></a> <span class="cf">else</span></span> <span id="cb2-6"><a href="#cb2-6"></a> <span class="cf">else</span></span>
<span id="cb2-7"><a href="#cb2-7"></a> <span class="cf">return</span> g_strdup_printf (<span class="st">&quot;Untitled%u&quot;</span>, c);</span> <span id="cb2-7"><a href="#cb2-7"></a> <span class="cf">return</span> g_strdup_printf <span class="op">(</span><span class="st">&quot;Untitled%u&quot;</span><span class="op">,</span> c<span class="op">);</span></span>
<span id="cb2-8"><a href="#cb2-8"></a>}</span> <span id="cb2-8"><a href="#cb2-8"></a><span class="op">}</span></span>
<span id="cb2-9"><a href="#cb2-9"></a></span> <span id="cb2-9"><a href="#cb2-9"></a></span>
<span id="cb2-10"><a href="#cb2-10"></a><span class="dt">static</span> <span class="dt">void</span></span> <span id="cb2-10"><a href="#cb2-10"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb2-11"><a href="#cb2-11"></a>notebook_page_build (GtkNotebook *nb, GtkWidget *tv, <span class="dt">char</span> *filename) {</span> <span id="cb2-11"><a href="#cb2-11"></a>notebook_page_build <span class="op">(</span>GtkNotebook <span class="op">*</span>nb<span class="op">,</span> GtkWidget <span class="op">*</span>tv<span class="op">,</span> <span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span>filename<span class="op">)</span> <span class="op">{</span></span>
<span id="cb2-12"><a href="#cb2-12"></a> GtkWidget *scr = gtk_scrolled_window_new ();</span> <span id="cb2-12"><a href="#cb2-12"></a> GtkWidget <span class="op">*</span>scr <span class="op">=</span> gtk_scrolled_window_new <span class="op">();</span></span>
<span id="cb2-13"><a href="#cb2-13"></a> GtkNotebookPage *nbp;</span> <span id="cb2-13"><a href="#cb2-13"></a> GtkNotebookPage <span class="op">*</span>nbp<span class="op">;</span></span>
<span id="cb2-14"><a href="#cb2-14"></a> GtkWidget *lab;</span> <span id="cb2-14"><a href="#cb2-14"></a> GtkWidget <span class="op">*</span>lab<span class="op">;</span></span>
<span id="cb2-15"><a href="#cb2-15"></a> <span class="dt">int</span> i;</span> <span id="cb2-15"><a href="#cb2-15"></a> <span class="dt">int</span> i<span class="op">;</span></span>
<span id="cb2-16"><a href="#cb2-16"></a></span> <span id="cb2-16"><a href="#cb2-16"></a></span>
<span id="cb2-17"><a href="#cb2-17"></a> gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR);</span> <span id="cb2-17"><a href="#cb2-17"></a> gtk_scrolled_window_set_child <span class="op">(</span>GTK_SCROLLED_WINDOW <span class="op">(</span>scr<span class="op">),</span> tv<span class="op">);</span></span>
<span id="cb2-18"><a href="#cb2-18"></a> gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);</span> <span id="cb2-18"><a href="#cb2-18"></a> lab <span class="op">=</span> gtk_label_new <span class="op">(</span>filename<span class="op">);</span></span>
<span id="cb2-19"><a href="#cb2-19"></a> lab = gtk_label_new (filename);</span> <span id="cb2-19"><a href="#cb2-19"></a> i <span class="op">=</span> gtk_notebook_append_page <span class="op">(</span>nb<span class="op">,</span> scr<span class="op">,</span> lab<span class="op">);</span></span>
<span id="cb2-20"><a href="#cb2-20"></a> i = gtk_notebook_append_page (nb, scr, lab);</span> <span id="cb2-20"><a href="#cb2-20"></a> nbp <span class="op">=</span> gtk_notebook_get_page <span class="op">(</span>nb<span class="op">,</span> scr<span class="op">);</span></span>
<span id="cb2-21"><a href="#cb2-21"></a> nbp = gtk_notebook_get_page (nb, scr);</span> <span id="cb2-21"><a href="#cb2-21"></a> g_object_set <span class="op">(</span>nbp<span class="op">,</span> <span class="st">&quot;tab-expand&quot;</span><span class="op">,</span> TRUE<span class="op">,</span> NULL<span class="op">);</span></span>
<span id="cb2-22"><a href="#cb2-22"></a> g_object_set (nbp, <span class="st">&quot;tab-expand&quot;</span>, TRUE, NULL);</span> <span id="cb2-22"><a href="#cb2-22"></a> gtk_notebook_set_current_page <span class="op">(</span>nb<span class="op">,</span> i<span class="op">);</span></span>
<span id="cb2-23"><a href="#cb2-23"></a> gtk_notebook_set_current_page (nb, i);</span> <span id="cb2-23"><a href="#cb2-23"></a> g_signal_connect <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> <span class="st">&quot;change-file&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>file_changed_cb<span class="op">),</span> nb<span class="op">);</span></span>
<span id="cb2-24"><a href="#cb2-24"></a> g_signal_connect (GTK_TEXT_VIEW (tv), <span class="st">&quot;change-file&quot;</span>, G_CALLBACK (file_changed_cb), nb);</span> <span id="cb2-24"><a href="#cb2-24"></a><span class="op">}</span></span>
<span id="cb2-25"><a href="#cb2-25"></a>}</span> <span id="cb2-25"><a href="#cb2-25"></a></span>
<span id="cb2-26"><a href="#cb2-26"></a></span> <span id="cb2-26"><a href="#cb2-26"></a><span class="dt">void</span></span>
<span id="cb2-27"><a href="#cb2-27"></a><span class="dt">void</span></span> <span id="cb2-27"><a href="#cb2-27"></a>notebook_page_new <span class="op">(</span>GtkNotebook <span class="op">*</span>nb<span class="op">)</span> <span class="op">{</span></span>
<span id="cb2-28"><a href="#cb2-28"></a>notebook_page_new (GtkNotebook *nb) {</span> <span id="cb2-28"><a href="#cb2-28"></a> g_return_if_fail<span class="op">(</span>GTK_IS_NOTEBOOK <span class="op">(</span>nb<span class="op">));</span></span>
<span id="cb2-29"><a href="#cb2-29"></a> g_return_if_fail(GTK_IS_NOTEBOOK (nb));</span> <span id="cb2-29"><a href="#cb2-29"></a></span>
<span id="cb2-30"><a href="#cb2-30"></a></span> <span id="cb2-30"><a href="#cb2-30"></a> GtkWidget <span class="op">*</span>tv<span class="op">;</span></span>
<span id="cb2-31"><a href="#cb2-31"></a> GtkWidget *tv;</span> <span id="cb2-31"><a href="#cb2-31"></a> <span class="dt">char</span> <span class="op">*</span>filename<span class="op">;</span></span>
<span id="cb2-32"><a href="#cb2-32"></a> <span class="dt">char</span> *filename;</span> <span id="cb2-32"><a href="#cb2-32"></a></span>
<span id="cb2-33"><a href="#cb2-33"></a></span> <span id="cb2-33"><a href="#cb2-33"></a> <span class="cf">if</span> <span class="op">((</span>tv <span class="op">=</span> tfe_text_view_new <span class="op">())</span> <span class="op">==</span> NULL<span class="op">)</span></span>
<span id="cb2-34"><a href="#cb2-34"></a> <span class="cf">if</span> ((tv = tfe_text_view_new ()) == NULL)</span> <span id="cb2-34"><a href="#cb2-34"></a> <span class="cf">return</span><span class="op">;</span></span>
<span id="cb2-35"><a href="#cb2-35"></a> <span class="cf">return</span>;</span> <span id="cb2-35"><a href="#cb2-35"></a> filename <span class="op">=</span> get_untitled <span class="op">();</span></span>
<span id="cb2-36"><a href="#cb2-36"></a> filename = get_untitled ();</span> <span id="cb2-36"><a href="#cb2-36"></a> notebook_page_build <span class="op">(</span>nb<span class="op">,</span> tv<span class="op">,</span> filename<span class="op">);</span></span>
<span id="cb2-37"><a href="#cb2-37"></a> notebook_page_build (nb, tv, filename);</span> <span id="cb2-37"><a href="#cb2-37"></a> g_free <span class="op">(</span>filename<span class="op">);</span></span>
<span id="cb2-38"><a href="#cb2-38"></a>}</span></code></pre></div> <span id="cb2-38"><a href="#cb2-38"></a><span class="op">}</span></span></code></pre></div>
<ul> <ul>
<li>27-38: <code>notebook_page_new</code> function.</li> <li>26-38: <code>notebook_page_new</code> function.</li>
<li>29: <code>g_return_if_fail</code> is used to check the argument.</li> <li>28: <code>g_return_if_fail</code> is used to check the
<li>34: Creates TfeTextView object. If it fails, it returns to the caller.</li> argument.</li>
<li>36: Creates filename, which is “Untitled”, “Untitled1”, … .</li> <li>33-34: Creates TfeTextView object. If it fails, no notebook page is
created and the function returns to the caller.</li>
<li>35: Creates filename, which is “Untitled”, “Untitled1”, … .</li>
<li>1-8: <code>get_untitled</code> function.</li> <li>1-8: <code>get_untitled</code> function.</li>
<li>3: Static variable <code>c</code> is initialized at the first call of this function. After that <code>c</code> keeps its value unless it is changed explicitly.</li> <li>3: Static variable <code>c</code> is initialized at the first call
<li>4-7: Increases <code>c</code> by one and if it is zero then it returns “Untitled”. If it is a positive integer then it returns “Untitled&lt;the integer&gt;”, for example, “Untitled1”, “Untitled2”, and so on. The function <code>g_strdup_printf</code> creates a string and it should be freed by <code>g_free</code> when it becomes useless. The caller of <code>get_untitled</code> is in charge of freeing the string.</li> of this function. After that <code>c</code> keeps its value unless it is
<li>37: calls <code>notebook_page_build</code> to build the contents of the page.</li> changed explicitly.</li>
<li>10- 25: <code>notebook_page_build</code> function.</li> <li>4-7: Increases <code>c</code> by one and if it is zero, it returns
“Untitled”. If it is a positive integer, it returns “Untitled&lt;the
integer&gt;”, for example, “Untitled1”, “Untitled2”, and so on. The
function <code>g_strdup_printf</code> creates a string and it should be
freed by <code>g_free</code> when it becomes useless. The caller of
<code>get_untitled</code> is in charge of freeing the string.</li>
<li>36: calls <code>notebook_page_build</code> to build the contents of
the page.</li>
<li>37: frees <code>filename</code>.</li>
<li>10- 24: <code>notebook_page_build</code> function. A parameter with
<code>const</code> qualifier doesnt change in the function. It means
that the argument <code>filename</code> is owned by the caller. The
caller needs to free it when it becomes useless.</li>
<li>12: Creates GtkScrolledWindow.</li> <li>12: Creates GtkScrolledWindow.</li>
<li>17: Sets the wrap mode of <code>tv</code> to GTK_WRAP_WORD_CHAR so that lines are broken between words or graphemes.</li> <li>17: Inserts <code>tv</code> to GtkscrolledWindow as a child.</li>
<li>18: Inserts <code>tv</code> to GtkscrolledWindow as a child.</li> <li>18-19: Creates GtkLabel, then appends <code>scr</code> and
<li>19-20: Creates GtkLabel, then appends <code>scr</code> and <code>lab</code> to the GtkNotebook instance <code>nb</code>.</li> <code>lab</code> to the GtkNotebook instance <code>nb</code>.</li>
<li>21-22: Sets “tab-expand” property to TRUE. The function <code>g_object_set</code> sets properties on an object. The object is any object derived from GObject. In many cases, an object has its own function to set its properties, but sometimes not. In that case, use <code>g_object_set</code> to set the property.</li> <li>20-21: Sets “tab-expand” property to TRUE. The function
<li>23: Sets the current page of <code>nb</code> to the newly created page.</li> <code>g_object_set</code> sets properties on an object. The object is
<li>24: Connects “change-file” signal and <code>file_changed_cb</code> handler.</li> any object derived from GObject. In many cases, an object has its own
function to set its properties, but sometimes not. In that case, use
<code>g_object_set</code> to set the property.</li>
<li>22: Sets the current page to the newly created page.</li>
<li>23: Connects “change-file” signal and <code>file_changed_cb</code>
handler.</li>
</ul> </ul>
<h2 id="notebook_page_new_with_file">notebook_page_new_with_file</h2> <h2 id="notebook_page_new_with_file">notebook_page_new_with_file</h2>
<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> <div class="sourceCode" id="cb3"><pre
<span id="cb3-2"><a href="#cb3-2"></a>notebook_page_new_with_file (GtkNotebook *nb, GFile *file) {</span> 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-3"><a href="#cb3-3"></a> g_return_if_fail(GTK_IS_NOTEBOOK (nb));</span> <span id="cb3-2"><a href="#cb3-2"></a>notebook_page_new_with_file <span class="op">(</span>GtkNotebook <span class="op">*</span>nb<span class="op">,</span> GFile <span class="op">*</span>file<span class="op">)</span> <span class="op">{</span></span>
<span id="cb3-4"><a href="#cb3-4"></a> g_return_if_fail(G_IS_FILE (file));</span> <span id="cb3-3"><a href="#cb3-3"></a> g_return_if_fail<span class="op">(</span>GTK_IS_NOTEBOOK <span class="op">(</span>nb<span class="op">));</span></span>
<span id="cb3-4"><a href="#cb3-4"></a> g_return_if_fail<span class="op">(</span>G_IS_FILE <span class="op">(</span>file<span class="op">));</span></span>
<span id="cb3-5"><a href="#cb3-5"></a></span> <span id="cb3-5"><a href="#cb3-5"></a></span>
<span id="cb3-6"><a href="#cb3-6"></a> GtkWidget *tv;</span> <span id="cb3-6"><a href="#cb3-6"></a> GtkWidget <span class="op">*</span>tv<span class="op">;</span></span>
<span id="cb3-7"><a href="#cb3-7"></a> <span class="dt">char</span> *filename;</span> <span id="cb3-7"><a href="#cb3-7"></a> <span class="dt">char</span> <span class="op">*</span>filename<span class="op">;</span></span>
<span id="cb3-8"><a href="#cb3-8"></a></span> <span id="cb3-8"><a href="#cb3-8"></a></span>
<span id="cb3-9"><a href="#cb3-9"></a> <span class="cf">if</span> ((tv = tfe_text_view_new_with_file (file)) == NULL)</span> <span id="cb3-9"><a href="#cb3-9"></a> <span class="cf">if</span> <span class="op">((</span>tv <span class="op">=</span> tfe_text_view_new_with_file <span class="op">(</span>file<span class="op">))</span> <span class="op">==</span> NULL<span class="op">)</span></span>
<span id="cb3-10"><a href="#cb3-10"></a> <span class="cf">return</span>; <span class="co">/* read error */</span></span> <span id="cb3-10"><a href="#cb3-10"></a> <span class="cf">return</span><span class="op">;</span> <span class="co">/* read error */</span></span>
<span id="cb3-11"><a href="#cb3-11"></a> filename = g_file_get_basename (file);</span> <span id="cb3-11"><a href="#cb3-11"></a> filename <span class="op">=</span> g_file_get_basename <span class="op">(</span>file<span class="op">);</span></span>
<span id="cb3-12"><a href="#cb3-12"></a> notebook_page_build (nb, tv, filename);</span> <span id="cb3-12"><a href="#cb3-12"></a> notebook_page_build <span class="op">(</span>nb<span class="op">,</span> tv<span class="op">,</span> filename<span class="op">);</span></span>
<span id="cb3-13"><a href="#cb3-13"></a>}</span></code></pre></div> <span id="cb3-13"><a href="#cb3-13"></a> g_free <span class="op">(</span>filename<span class="op">);</span></span>
<span id="cb3-14"><a href="#cb3-14"></a><span class="op">}</span></span></code></pre></div>
<ul> <ul>
<li>9-10: Calls <code>tfe_text_view_new_with_file</code>. If the function returns NULL, an error has happend. Then, it does nothing and returns.</li> <li>9-10: Calls <code>tfe_text_view_new_with_file</code>. If the
<li>11-12: Gets the filename and builds the contents of the page.</li> function returns NULL, an error has happend. Then, it does nothing and
returns.</li>
<li>11-13: Gets the filename, builds the contents of the page and frees
<code>filename</code>.</li>
</ul> </ul>
<h2 id="notebook_page_open">notebook_page_open</h2> <h2 id="notebook_page_open">notebook_page_open</h2>
<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> <div class="sourceCode" id="cb4"><pre
<span id="cb4-2"><a href="#cb4-2"></a>open_response (TfeTextView *tv, <span class="dt">int</span> response, GtkNotebook *nb) {</span> 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-3"><a href="#cb4-3"></a> GFile *file;</span> <span id="cb4-2"><a href="#cb4-2"></a>open_response <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">,</span> <span class="dt">int</span> response<span class="op">,</span> GtkNotebook <span class="op">*</span>nb<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-4"><a href="#cb4-4"></a> <span class="dt">char</span> *filename;</span> <span id="cb4-3"><a href="#cb4-3"></a> GFile <span class="op">*</span>file<span class="op">;</span></span>
<span id="cb4-4"><a href="#cb4-4"></a> <span class="dt">char</span> <span class="op">*</span>filename<span class="op">;</span></span>
<span id="cb4-5"><a href="#cb4-5"></a></span> <span id="cb4-5"><a href="#cb4-5"></a></span>
<span id="cb4-6"><a href="#cb4-6"></a> <span class="cf">if</span> (response != TFE_OPEN_RESPONSE_SUCCESS || ! G_IS_FILE (file = tfe_text_view_get_file (tv))) {</span> <span id="cb4-6"><a href="#cb4-6"></a> <span class="cf">if</span> <span class="op">(</span>response <span class="op">!=</span> TFE_OPEN_RESPONSE_SUCCESS<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-7"><a href="#cb4-7"></a> g_object_ref_sink (tv);</span> <span id="cb4-7"><a href="#cb4-7"></a> g_object_ref_sink <span class="op">(</span>tv<span class="op">);</span></span>
<span id="cb4-8"><a href="#cb4-8"></a> g_object_unref (tv);</span> <span id="cb4-8"><a href="#cb4-8"></a> g_object_unref <span class="op">(</span>tv<span class="op">);</span></span>
<span id="cb4-9"><a href="#cb4-9"></a> }<span class="cf">else</span> {</span> <span id="cb4-9"><a href="#cb4-9"></a> <span class="op">}</span><span class="cf">else</span> <span class="op">{</span></span>
<span id="cb4-10"><a href="#cb4-10"></a> filename = g_file_get_basename (file);</span> <span id="cb4-10"><a href="#cb4-10"></a> file <span class="op">=</span> tfe_text_view_get_file <span class="op">(</span>tv<span class="op">);</span></span>
<span id="cb4-11"><a href="#cb4-11"></a> g_object_unref (file);</span> <span id="cb4-11"><a href="#cb4-11"></a> filename <span class="op">=</span> g_file_get_basename <span class="op">(</span>file<span class="op">);</span></span>
<span id="cb4-12"><a href="#cb4-12"></a> notebook_page_build (nb, GTK_WIDGET (tv), filename);</span> <span id="cb4-12"><a href="#cb4-12"></a> g_object_unref <span class="op">(</span>file<span class="op">);</span></span>
<span id="cb4-13"><a href="#cb4-13"></a> }</span> <span id="cb4-13"><a href="#cb4-13"></a> notebook_page_build <span class="op">(</span>nb<span class="op">,</span> GTK_WIDGET <span class="op">(</span>tv<span class="op">),</span> filename<span class="op">);</span></span>
<span id="cb4-14"><a href="#cb4-14"></a>}</span> <span id="cb4-14"><a href="#cb4-14"></a> g_free <span class="op">(</span>filename<span class="op">);</span></span>
<span id="cb4-15"><a href="#cb4-15"></a></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 class="dt">void</span></span> <span id="cb4-16"><a href="#cb4-16"></a><span class="op">}</span></span>
<span id="cb4-17"><a href="#cb4-17"></a>notebook_page_open (GtkNotebook *nb) {</span> <span id="cb4-17"><a href="#cb4-17"></a></span>
<span id="cb4-18"><a href="#cb4-18"></a> g_return_if_fail(GTK_IS_NOTEBOOK (nb));</span> <span id="cb4-18"><a href="#cb4-18"></a><span class="dt">void</span></span>
<span id="cb4-19"><a href="#cb4-19"></a></span> <span id="cb4-19"><a href="#cb4-19"></a>notebook_page_open <span class="op">(</span>GtkNotebook <span class="op">*</span>nb<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-20"><a href="#cb4-20"></a> GtkWidget *tv;</span> <span id="cb4-20"><a href="#cb4-20"></a> g_return_if_fail<span class="op">(</span>GTK_IS_NOTEBOOK <span class="op">(</span>nb<span class="op">));</span></span>
<span id="cb4-21"><a href="#cb4-21"></a></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> ((tv = tfe_text_view_new ()) == NULL)</span> <span id="cb4-22"><a href="#cb4-22"></a> GtkWidget <span class="op">*</span>tv<span class="op">;</span></span>
<span id="cb4-23"><a href="#cb4-23"></a> <span class="cf">return</span>;</span> <span id="cb4-23"><a href="#cb4-23"></a></span>
<span id="cb4-24"><a href="#cb4-24"></a> g_signal_connect (TFE_TEXT_VIEW (tv), <span class="st">&quot;open-response&quot;</span>, G_CALLBACK (open_response), nb);</span> <span id="cb4-24"><a href="#cb4-24"></a> <span class="cf">if</span> <span class="op">((</span>tv <span class="op">=</span> tfe_text_view_new <span class="op">())</span> <span class="op">==</span> NULL<span class="op">)</span></span>
<span id="cb4-25"><a href="#cb4-25"></a> tfe_text_view_open (TFE_TEXT_VIEW (tv), GTK_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET (nb), GTK_TYPE_WINDOW)));</span> <span id="cb4-25"><a href="#cb4-25"></a> <span class="cf">return</span><span class="op">;</span></span>
<span id="cb4-26"><a href="#cb4-26"></a>}</span></code></pre></div> <span id="cb4-26"><a href="#cb4-26"></a> g_signal_connect <span class="op">(</span>TFE_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> <span class="st">&quot;open-response&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>open_response<span class="op">),</span> nb<span class="op">);</span></span>
<span id="cb4-27"><a href="#cb4-27"></a> tfe_text_view_open <span class="op">(</span>TFE_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> GTK_WINDOW <span class="op">(</span>gtk_widget_get_ancestor <span class="op">(</span>GTK_WIDGET <span class="op">(</span>nb<span class="op">),</span> GTK_TYPE_WINDOW<span class="op">)));</span></span>
<span id="cb4-28"><a href="#cb4-28"></a><span class="op">}</span></span></code></pre></div>
<ul> <ul>
<li>16-26: <code>notebook_page_open</code> function.</li> <li>18-28: <code>notebook_page_open</code> function.</li>
<li>22-23: Creates TfeTextView object. If NULL is returned, an error has happened. Then, it returns to the caller.</li> <li>24-25: Creates TfeTextView object. If NULL is returned, an error has
<li>24: Connects the signal “open-response” and the handler <code>open_response</code>.</li> happened. Then, it returns to the caller.</li>
<li>25: Calls <code>tfe_text_view_open</code>. The “open-response” signal will be emitted later to inform the result of opening and reading a file.</li> <li>26: Connects the signal “open-response” and the handler
<li>1-14: <code>open_response</code> handler.</li> <code>open_response</code>.</li>
<li>6-8: If the response code is NOT <code>TFE_OPEN_RESPONSE_SUCCESS</code> or <code>tfe_text_view_get_file</code> doesnt return the pointer to a GFile, it has failed to open and read a new file. Then, what <code>notebook_page_open</code> did in advance need to be canceled. The instance <code>tv</code> hasnt been a child widget of GtkScrolledWindow yet. Such instance has floating reference. Floating reference will be explained later in this subsection. You need to call <code>g_object_ref_sink</code> first. Then the floating reference is converted into an ordinary reference. Now you call <code>g_object_unref</code> to decrease the reference count by one.</li> <li>27: Calls <code>tfe_text_view_open</code>. The “open-response”
<li>9-13: Otherwise, everything is okay. Gets the filename, builds the contents of the page.</li> signal will be emitted later to inform the result of opening and reading
a file.</li>
<li>1-16: <code>open_response</code> handler.</li>
<li>6-8: If the response code is not
<code>TFE_OPEN_RESPONSE_SUCCESS</code>, it has failed to open and read a
new file. Then, what <code>notebook_page_open</code> did in advance need
to be canceled. The instance <code>tv</code> hasnt been a child widget
of GtkScrolledWindow yet. Such instance has floating reference. Floating
reference will be explained later. You need to call
<code>g_object_ref_sink</code> first. Then the floating reference is
converted into an ordinary reference. Now you call
<code>g_object_unref</code> to decrease the reference count by one.</li>
<li>9-15: Otherwise, everything is okay. Gets the filename, builds the
contents of the page and frees <code>filename</code>.</li>
</ul> </ul>
<p>All the widgets are derived from GInitiallyUnowned. When an instance of GInitiallyUnowned or its descendant is created, the instance has a floating reference. The function <code>g_object_ref_sink</code> converts the floating reference into an ordinary reference. If the instance doesnt have a floating reference, <code>g_object_ref_sink</code> simply increases the reference count by one. On the other hand, when an instance of GObject (not GInitiallyUnowned) is created, no floating reference is given. And the instance has a normal reference count instead of floating reference.</p> <h2 id="floating-reference">Floating reference</h2>
<p>If you use <code>g_object_unref</code> to an instance that has a floating reference, you need to convert the floating reference to a normal reference in advance. See <a href="https://developer-old.gnome.org/gobject/stable/gobject-The-Base-Object-Type.html#gobject-The-Base-Object-Type.description">GObject Reference Manual</a> for further information.</p> <p>All the widgets are derived from GInitiallyUnowned. GObject and
GInitiallyUnowned are almost the same. The difference is like this. When
an instance of GInitiallyUnowned is created, the instance has a floating
reference and its reference count is zero. On the other hand, when an
instance of GObject (not GInitiallyUnowned) is created, no floating
reference is given. And the instance has a normal reference count
instead of floating reference. Their descendants inherits them, so every
widget has a floating reference at first. Non-widget class, for example,
GtkTextBuffer is a direct sub class of GObject and it doesnt have
floating reference. Its reference count is one when it is created.</p>
<p>The function <code>g_object_ref_sink</code> converts the floating
reference into an ordinary reference. If the instance doesnt have a
floating reference, <code>g_object_ref_sink</code> simply increases the
reference count by one. It is used when an widget is added to another
widget as a child.</p>
<pre><code>GtkTextView *tv = gtk_text_view_new (); // floating reference
GtkScrolledWindow *scr = gtk_scrolled_window_new ();
gtk_scrolled_window_set_child (scr, tv); // tv&#39;s reference count is one</code></pre>
<p>When <code>tv</code> is added to <code>scr</code> as a child,
<code>g_object_ref_sink</code> is used.</p>
<pre><code>g_object_ref_sink (tv);</code></pre>
<p>So, the floating reference is converted into an ordinary reference.
That is to say, floating reference is deleted, and reference count turns
to one. Thanks to this, the caller doesnt need to decrease tvs
reference count. If an Object_A is not a descendant of
GInitiallyUnowned, the program is like this:</p>
<pre><code>Object_A *obj_a = object_a_new (); // reference count is one
GtkScrolledWindow *scr = gtk_scrolled_window_new ();
gtk_scrolled_window_set_child (scr, obj_a); // obj_a&#39;s reference count is two
// obj_a is referred by the caller (this program) and scrolled window
g_object_unref (obj_a); // obj_a&#39;s reference count is one because the caller no longer refers obj_a.</code></pre>
<p>This example tells us that the caller needs to unref
<code>obj_a</code>.</p>
<p>If you use <code>g_object_unref</code> to an instance that has a
floating reference, you need to convert the floating reference to a
normal reference in advance. See <a
href="https://docs.gtk.org/gobject/floating-refs.html">GObject API
reference</a> for further information.</p>
<h2 id="notebook_page_close">notebook_page_close</h2> <h2 id="notebook_page_close">notebook_page_close</h2>
<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="dt">void</span></span> <div class="sourceCode" id="cb8"><pre
<span id="cb5-2"><a href="#cb5-2"></a>notebook_page_close (GtkNotebook *nb) {</span> class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb8-1"><a href="#cb8-1"></a><span class="dt">void</span></span>
<span id="cb5-3"><a href="#cb5-3"></a> g_return_if_fail(GTK_IS_NOTEBOOK (nb));</span> <span id="cb8-2"><a href="#cb8-2"></a>notebook_page_close <span class="op">(</span>GtkNotebook <span class="op">*</span>nb<span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-4"><a href="#cb5-4"></a></span> <span id="cb8-3"><a href="#cb8-3"></a> g_return_if_fail<span class="op">(</span>GTK_IS_NOTEBOOK <span class="op">(</span>nb<span class="op">));</span></span>
<span id="cb5-5"><a href="#cb5-5"></a> GtkWidget *win;</span> <span id="cb8-4"><a href="#cb8-4"></a></span>
<span id="cb5-6"><a href="#cb5-6"></a> <span class="dt">int</span> i;</span> <span id="cb8-5"><a href="#cb8-5"></a> GtkWidget <span class="op">*</span>win<span class="op">;</span></span>
<span id="cb5-7"><a href="#cb5-7"></a></span> <span id="cb8-6"><a href="#cb8-6"></a> <span class="dt">int</span> i<span class="op">;</span></span>
<span id="cb5-8"><a href="#cb5-8"></a> <span class="cf">if</span> (gtk_notebook_get_n_pages (nb) == <span class="dv">1</span>) {</span> <span id="cb8-7"><a href="#cb8-7"></a></span>
<span id="cb5-9"><a href="#cb5-9"></a> win = gtk_widget_get_ancestor (GTK_WIDGET (nb), GTK_TYPE_WINDOW);</span> <span id="cb8-8"><a href="#cb8-8"></a> <span class="cf">if</span> <span class="op">(</span>gtk_notebook_get_n_pages <span class="op">(</span>nb<span class="op">)</span> <span class="op">==</span> <span class="dv">1</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-10"><a href="#cb5-10"></a> gtk_window_destroy(GTK_WINDOW (win));</span> <span id="cb8-9"><a href="#cb8-9"></a> win <span class="op">=</span> gtk_widget_get_ancestor <span class="op">(</span>GTK_WIDGET <span class="op">(</span>nb<span class="op">),</span> GTK_TYPE_WINDOW<span class="op">);</span></span>
<span id="cb5-11"><a href="#cb5-11"></a> } <span class="cf">else</span> {</span> <span id="cb8-10"><a href="#cb8-10"></a> gtk_window_destroy<span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
<span id="cb5-12"><a href="#cb5-12"></a> i = gtk_notebook_get_current_page (nb);</span> <span id="cb8-11"><a href="#cb8-11"></a> <span class="op">}</span> <span class="cf">else</span> <span class="op">{</span></span>
<span id="cb5-13"><a href="#cb5-13"></a> gtk_notebook_remove_page (GTK_NOTEBOOK (nb), i);</span> <span id="cb8-12"><a href="#cb8-12"></a> i <span class="op">=</span> gtk_notebook_get_current_page <span class="op">(</span>nb<span class="op">);</span></span>
<span id="cb5-14"><a href="#cb5-14"></a> }</span> <span id="cb8-13"><a href="#cb8-13"></a> gtk_notebook_remove_page <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">),</span> i<span class="op">);</span></span>
<span id="cb5-15"><a href="#cb5-15"></a>}</span></code></pre></div> <span id="cb8-14"><a href="#cb8-14"></a> <span class="op">}</span></span>
<p>This function closes the current page. If the page is the only page the notebook has, then the function destroys the top-level window and quits the application.</p> <span id="cb8-15"><a href="#cb8-15"></a><span class="op">}</span></span></code></pre></div>
<p>This function closes the current page. If the page is the only page
the notebook has, then the function destroys the top-level window and
quits the application.</p>
<ul> <ul>
<li>8-10: If the page is the only page the notebook has, it calls <code>gtk_window_destroy</code> to destroys the top-level window.</li> <li>8-10: If the page is the only page the notebook has, it calls
<li>11-13: Otherwise, removes the current page.</li> <code>gtk_window_destroy</code> to destroys the top-level window.</li>
<li>11-13: Otherwise, removes the current page. The child widget
(TfeTextView) is also destroyed.</li>
</ul> </ul>
<h2 id="notebook_page_save">notebook_page_save</h2> <h2 id="notebook_page_save">notebook_page_save</h2>
<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> TfeTextView *</span> <div class="sourceCode" id="cb9"><pre
<span id="cb6-2"><a href="#cb6-2"></a>get_current_textview (GtkNotebook *nb) {</span> class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb9-1"><a href="#cb9-1"></a><span class="dt">static</span> TfeTextView <span class="op">*</span></span>
<span id="cb6-3"><a href="#cb6-3"></a> <span class="dt">int</span> i;</span> <span id="cb9-2"><a href="#cb9-2"></a>get_current_textview <span class="op">(</span>GtkNotebook <span class="op">*</span>nb<span class="op">)</span> <span class="op">{</span></span>
<span id="cb6-4"><a href="#cb6-4"></a> GtkWidget *scr;</span> <span id="cb9-3"><a href="#cb9-3"></a> <span class="dt">int</span> i<span class="op">;</span></span>
<span id="cb6-5"><a href="#cb6-5"></a> GtkWidget *tv;</span> <span id="cb9-4"><a href="#cb9-4"></a> GtkWidget <span class="op">*</span>scr<span class="op">;</span></span>
<span id="cb6-6"><a href="#cb6-6"></a></span> <span id="cb9-5"><a href="#cb9-5"></a> GtkWidget <span class="op">*</span>tv<span class="op">;</span></span>
<span id="cb6-7"><a href="#cb6-7"></a> i = gtk_notebook_get_current_page (nb);</span> <span id="cb9-6"><a href="#cb9-6"></a></span>
<span id="cb6-8"><a href="#cb6-8"></a> scr = gtk_notebook_get_nth_page (nb, i);</span> <span id="cb9-7"><a href="#cb9-7"></a> i <span class="op">=</span> gtk_notebook_get_current_page <span class="op">(</span>nb<span class="op">);</span></span>
<span id="cb6-9"><a href="#cb6-9"></a> tv = gtk_scrolled_window_get_child (GTK_SCROLLED_WINDOW (scr));</span> <span id="cb9-8"><a href="#cb9-8"></a> scr <span class="op">=</span> gtk_notebook_get_nth_page <span class="op">(</span>nb<span class="op">,</span> i<span class="op">);</span></span>
<span id="cb6-10"><a href="#cb6-10"></a> <span class="cf">return</span> TFE_TEXT_VIEW (tv);</span> <span id="cb9-9"><a href="#cb9-9"></a> tv <span class="op">=</span> gtk_scrolled_window_get_child <span class="op">(</span>GTK_SCROLLED_WINDOW <span class="op">(</span>scr<span class="op">));</span></span>
<span id="cb6-11"><a href="#cb6-11"></a>}</span> <span id="cb9-10"><a href="#cb9-10"></a> <span class="cf">return</span> TFE_TEXT_VIEW <span class="op">(</span>tv<span class="op">);</span></span>
<span id="cb6-12"><a href="#cb6-12"></a></span> <span id="cb9-11"><a href="#cb9-11"></a><span class="op">}</span></span>
<span id="cb6-13"><a href="#cb6-13"></a><span class="dt">void</span></span> <span id="cb9-12"><a href="#cb9-12"></a></span>
<span id="cb6-14"><a href="#cb6-14"></a>notebook_page_save (GtkNotebook *nb) {</span> <span id="cb9-13"><a href="#cb9-13"></a><span class="dt">void</span></span>
<span id="cb6-15"><a href="#cb6-15"></a> g_return_if_fail(GTK_IS_NOTEBOOK (nb));</span> <span id="cb9-14"><a href="#cb9-14"></a>notebook_page_save <span class="op">(</span>GtkNotebook <span class="op">*</span>nb<span class="op">)</span> <span class="op">{</span></span>
<span id="cb6-16"><a href="#cb6-16"></a></span> <span id="cb9-15"><a href="#cb9-15"></a> g_return_if_fail<span class="op">(</span>GTK_IS_NOTEBOOK <span class="op">(</span>nb<span class="op">));</span></span>
<span id="cb6-17"><a href="#cb6-17"></a> TfeTextView *tv;</span> <span id="cb9-16"><a href="#cb9-16"></a></span>
<span id="cb6-18"><a href="#cb6-18"></a></span> <span id="cb9-17"><a href="#cb9-17"></a> TfeTextView <span class="op">*</span>tv<span class="op">;</span></span>
<span id="cb6-19"><a href="#cb6-19"></a> tv = get_current_textview (nb);</span> <span id="cb9-18"><a href="#cb9-18"></a></span>
<span id="cb6-20"><a href="#cb6-20"></a> tfe_text_view_save (TFE_TEXT_VIEW (tv));</span> <span id="cb9-19"><a href="#cb9-19"></a> tv <span class="op">=</span> get_current_textview <span class="op">(</span>nb<span class="op">);</span></span>
<span id="cb6-21"><a href="#cb6-21"></a>}</span></code></pre></div> <span id="cb9-20"><a href="#cb9-20"></a> tfe_text_view_save <span class="op">(</span>TFE_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
<span id="cb9-21"><a href="#cb9-21"></a><span class="op">}</span></span></code></pre></div>
<ul> <ul>
<li>13-21: <code>notebook_page_save</code>.</li> <li>13-21: <code>notebook_page_save</code>.</li>
<li>19: Gets TfeTextView belongs to the current page.</li> <li>19: Gets TfeTextView belongs to the current page.</li>
<li>20: Calls <code>tfe_text_view_save</code>.</li> <li>20: Calls <code>tfe_text_view_save</code>.</li>
<li>1-11: <code>get_current_textview</code>. This function gets the TfeTextView object belongs to the current page.</li> <li>1-11: <code>get_current_textview</code>. This function gets the
TfeTextView object belongs to the current page.</li>
<li>7: Gets the page number of the current page.</li> <li>7: Gets the page number of the current page.</li>
<li>8: Gets the child widget <code>scr</code>, which is a GtkScrolledWindow instance, of the current page.</li> <li>8: Gets the child widget <code>scr</code>, which is a
<li>9-10: Gets the child widget of <code>scr</code>, which is a TfeTextView instance, and returns it.</li> GtkScrolledWindow instance, of the current page.</li>
<li>9-10: Gets the child widget of <code>scr</code>, which is a
TfeTextView instance, and returns it.</li>
</ul> </ul>
<h2 id="file_changed_cb-handler">file_changed_cb handler</h2> <h2 id="file_changed_cb-handler">file_changed_cb handler</h2>
<p>The function <code>file_changed_cb</code> is a handler connected to “change-file” signal. If a file in a TfeTextView instance is changed, it emits this signal. This handler changes the label of GtkNotebookPage.</p> <p>The function <code>file_changed_cb</code> is a handler connected to
<div class="sourceCode" id="cb7"><pre class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb7-1"><a href="#cb7-1"></a><span class="dt">static</span> <span class="dt">void</span></span> “change-file” signal. If a file in a TfeTextView instance is changed, it
<span id="cb7-2"><a href="#cb7-2"></a>file_changed_cb (TfeTextView *tv, GtkNotebook *nb) {</span> emits this signal. This handler changes the label of the
<span id="cb7-3"><a href="#cb7-3"></a> GtkWidget *scr;</span> GtkNotebookPage.</p>
<span id="cb7-4"><a href="#cb7-4"></a> GtkWidget *label;</span> <div class="sourceCode" id="cb10"><pre
<span id="cb7-5"><a href="#cb7-5"></a> GFile *file;</span> 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="cb7-6"><a href="#cb7-6"></a> <span class="dt">char</span> *filename;</span> <span id="cb10-2"><a href="#cb10-2"></a>file_changed_cb <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">,</span> GtkNotebook <span class="op">*</span>nb<span class="op">)</span> <span class="op">{</span></span>
<span id="cb7-7"><a href="#cb7-7"></a></span> <span id="cb10-3"><a href="#cb10-3"></a> GtkWidget <span class="op">*</span>scr<span class="op">;</span></span>
<span id="cb7-8"><a href="#cb7-8"></a> file = tfe_text_view_get_file (tv);</span> <span id="cb10-4"><a href="#cb10-4"></a> GtkWidget <span class="op">*</span>label<span class="op">;</span></span>
<span id="cb7-9"><a href="#cb7-9"></a> scr = gtk_widget_get_parent (GTK_WIDGET (tv));</span> <span id="cb10-5"><a href="#cb10-5"></a> GFile <span class="op">*</span>file<span class="op">;</span></span>
<span id="cb7-10"><a href="#cb7-10"></a> <span class="cf">if</span> (G_IS_FILE (file)) {</span> <span id="cb10-6"><a href="#cb10-6"></a> <span class="dt">char</span> <span class="op">*</span>filename<span class="op">;</span></span>
<span id="cb7-11"><a href="#cb7-11"></a> filename = g_file_get_basename (file);</span> <span id="cb10-7"><a href="#cb10-7"></a></span>
<span id="cb7-12"><a href="#cb7-12"></a> g_object_unref (file);</span> <span id="cb10-8"><a href="#cb10-8"></a> file <span class="op">=</span> tfe_text_view_get_file <span class="op">(</span>tv<span class="op">);</span></span>
<span id="cb7-13"><a href="#cb7-13"></a> } <span class="cf">else</span></span> <span id="cb10-9"><a href="#cb10-9"></a> scr <span class="op">=</span> gtk_widget_get_parent <span class="op">(</span>GTK_WIDGET <span class="op">(</span>tv<span class="op">));</span></span>
<span id="cb7-14"><a href="#cb7-14"></a> filename = get_untitled ();</span> <span id="cb10-10"><a href="#cb10-10"></a> <span class="cf">if</span> <span class="op">(</span>G_IS_FILE <span class="op">(</span>file<span class="op">))</span> <span class="op">{</span></span>
<span id="cb7-15"><a href="#cb7-15"></a> label = gtk_label_new (filename);</span> <span id="cb10-11"><a href="#cb10-11"></a> filename <span class="op">=</span> g_file_get_basename <span class="op">(</span>file<span class="op">);</span></span>
<span id="cb7-16"><a href="#cb7-16"></a> g_free (filename);</span> <span id="cb10-12"><a href="#cb10-12"></a> g_object_unref <span class="op">(</span>file<span class="op">);</span></span>
<span id="cb7-17"><a href="#cb7-17"></a> gtk_notebook_set_tab_label (nb, scr, label);</span> <span id="cb10-13"><a href="#cb10-13"></a> <span class="op">}</span> <span class="cf">else</span></span>
<span id="cb7-18"><a href="#cb7-18"></a>}</span></code></pre></div> <span id="cb10-14"><a href="#cb10-14"></a> filename <span class="op">=</span> get_untitled <span class="op">();</span></span>
<span id="cb10-15"><a href="#cb10-15"></a> label <span class="op">=</span> gtk_label_new <span class="op">(</span>filename<span class="op">);</span></span>
<span id="cb10-16"><a href="#cb10-16"></a> g_free <span class="op">(</span>filename<span class="op">);</span></span>
<span id="cb10-17"><a href="#cb10-17"></a> gtk_notebook_set_tab_label <span class="op">(</span>nb<span class="op">,</span> scr<span class="op">,</span> label<span class="op">);</span></span>
<span id="cb10-18"><a href="#cb10-18"></a><span class="op">}</span></span></code></pre></div>
<ul> <ul>
<li>8: Gets the GFile instance from <code>tv</code>.</li> <li>8: Gets the GFile instance from <code>tv</code>.</li>
<li>9: Gets the GkScrolledWindow instance which is the parent widget of <code>tv</code>.</li> <li>9: Gets the GkScrolledWindow instance which is the parent widget of
<li>10-12: If <code>file</code> points GFile, then assigns the filename of the GFile into <code>filename</code>. Then, unref the GFile object <code>file</code>.</li> <code>tv</code>.</li>
<li>13-14: Otherwise (file is NULL), assigns untitled string to <code>filename</code>.</li> <li>10-12: If <code>file</code> points GFile, then assigns the filename
<li>15-16: Creates a GtkLabel instance <code>label</code> with the filename and set the label of the GtkNotebookPage with <code>label</code>.</li> of the GFile into <code>filename</code>. Then, unref the GFile object
<code>file</code>.</li>
<li>13-14: Otherwise (file is NULL), assigns untitled string to
<code>filename</code>.</li>
<li>15-16: Creates a GtkLabel instance <code>label</code> with the
filename and set the label of the GtkNotebookPage with
<code>label</code>.</li>
</ul> </ul>
</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> <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>

View file

@ -224,7 +224,7 @@ substitute “textview” for the selector.</p>
<pre><code>textview {color: yellow; ...}</code></pre> <pre><code>textview {color: yellow; ...}</code></pre>
<p>Class, ID and some other things can be applied to the selector like <p>Class, ID and some other things can be applied to the selector like
Web CSS. Refer to <a Web CSS. Refer to <a
href="https://docs.gtk.org/gtk4/css-overview.html">GTK 4 API Reference, href="https://docs.gtk.org/gtk4/css-overview.html">GTK 4 API Reference
CSS in Gtk</a> for further information.</p> CSS in Gtk</a> for further information.</p>
<p>In line 30, the CSS is a string.</p> <p>In line 30, the CSS is a string.</p>
<pre><code>textview {padding: 10px; font-family: monospace; font-size: 12pt;}</code></pre> <pre><code>textview {padding: 10px; font-family: monospace; font-size: 12pt;}</code></pre>
@ -396,9 +396,7 @@ class="sourceCode numberSource numberLines"><code class="sourceCode"><span id="c
<p>In this file, just the source file names are modified from the prior <p>In this file, just the source file names are modified from the prior
version.</p> version.</p>
<h2 id="source-files">source files</h2> <h2 id="source-files">source files</h2>
<p>The source files of the text editor <code>tfe</code> will be shown in <p>You can download the files from the <a
the next section.</p>
<p>You can also download the files from the <a
href="https://github.com/ToshioCP/Gtk4-tutorial">repository</a>. There href="https://github.com/ToshioCP/Gtk4-tutorial">repository</a>. There
are two options.</p> are two options.</p>
<ul> <ul>

View file

@ -5,7 +5,7 @@
<meta name="generator" content="pandoc" /> <meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <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"> <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> <title>GTK 4 tutorial</title>
<style> <style>
code{white-space: pre-wrap;} code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;} span.smallcaps{font-variant: small-caps;}
@ -111,644 +111,54 @@
</div> </div>
</div> </div>
</nav> </nav>
<h1 id="tfe5-source-files">tfe5 source files</h1> <h1 id="how-to-build-tfe-text-file-editor">How to build tfe (text file
<h2 id="how-to-compile-and-execute-the-text-editor-tfe.">How to compile and execute the text editor tfe.</h2> editor)</h1>
<p>First, source files are shown in the later subsections. How to download them is written at the end of the <a href="sec15.html">previous section</a>.</p> <h2 id="how-to-compile-and-execute-the-text-editor-tfe.">How to compile
and execute the text editor tfe.</h2>
<p>First, source files are in the <a
href="https://github.com/ToshioCP/Gtk4-tutorial">Gtk4-tutorila
repository</a>. How to download them is written at the end of the <a
href="sec15.html">previous section</a>.</p>
<p>The following is the instruction of compilation and execution.</p> <p>The following is the instruction of compilation and execution.</p>
<ul> <ul>
<li>You need meson and ninja.</li> <li>You need meson and ninja.</li>
<li>Set environment variables if necessary. If you have installed gtk4 from the source and you preferred the option <code>--prefix $HOME/local</code> (see <a href="sec2.html">Section 2</a>), type <code>. env.sh</code> to set the environment variables.</li> <li>If you have installed gtk4 from the source, you need to set
environment variables to suit your installation.</li>
<li>Change your current directory to <code>src/tfe5</code>
directory.</li>
<li>Type <code>meson _build</code> for configuration.</li>
<li>Type <code>ninja -C _build</code> for compilation. Then the
application <code>tfe</code> is built under the <code>_build</code>
directory.</li>
<li>Type <code>_build/tfe</code> to execute it.</li>
</ul> </ul>
<pre><code>$ . env.sh</code></pre> <p>Then the window appears. There are four buttons, <code>New</code>,
<code>Open</code>, <code>Save</code> and <code>Close</code>.</p>
<ul> <ul>
<li>change your current directory to <code>src/tfe5</code> directory.</li> <li>Click on <code>Open</code> button, then a FileChooserDialog appears.
<li>type <code>meson _build</code> for configuration.</li> Choose a file in the list and click on <code>Open</code> button. Then
<li>type <code>ninja -C _build</code> for compilation. Then the application <code>tfe</code> is built under the <code>_build</code> directory.</li> the file is read and a new Notebook Page appears.</li>
<li>type <code>_build/tfe</code> to execute it.</li> <li>Edit the file and click on <code>Save</code> button, then the text
</ul> is saved to the original file.</li>
<p>Then the window appears. There are four buttons, <code>New</code>, <code>Open</code>, <code>Save</code> and <code>Close</code>.</p>
<ul>
<li>Click on <code>Open</code> button, then a FileChooserDialog appears. Choose a file in the list and click on <code>Open</code> button. Then the file is read and a new Notebook Page appears.</li>
<li>Edit the file and click on <code>Save</code> button, then the text is saved to the original file.</li>
<li>Click <code>Close</code>, then the Notebook Page disappears.</li> <li>Click <code>Close</code>, then the Notebook Page disappears.</li>
<li>Click <code>Close</code> again, then the <code>Untitled</code> Notebook Page disappears and at the same time the application quits.</li> <li>Click <code>Close</code> again, then the <code>Untitled</code>
Notebook Page disappears and at the same time the application
quits.</li>
</ul> </ul>
<p>This is a very simple editor. It is a good practice for you to add more features.</p> <p>This is a very simple editor. It is a good practice for you to add
<h2 id="meson.build">meson.build</h2> more features.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode numberSource numberLines"><code class="sourceCode"><span id="cb2-1"><a href="#cb2-1"></a>project(&#39;tfe&#39;, &#39;c&#39;)</span> <h2 id="total-number-of-lines-words-and-characters">Total number of
<span id="cb2-2"><a href="#cb2-2"></a></span> lines, words and characters</h2>
<span id="cb2-3"><a href="#cb2-3"></a>gtkdep = dependency(&#39;gtk4&#39;)</span> <pre><code>$ LANG=C wc tfe5/meson.build tfe5/tfeapplication.c tfe5/tfe.gresource.xml tfe5/tfenotebook.c tfe5/tfenotebook.h tfetextview/tfetextview.c tfetextview/tfetextview.h tfe5/tfe.ui
<span id="cb2-4"><a href="#cb2-4"></a></span>
<span id="cb2-5"><a href="#cb2-5"></a>gnome=import(&#39;gnome&#39;)</span>
<span id="cb2-6"><a href="#cb2-6"></a>resources = gnome.compile_resources(&#39;resources&#39;,&#39;tfe.gresource.xml&#39;)</span>
<span id="cb2-7"><a href="#cb2-7"></a></span>
<span id="cb2-8"><a href="#cb2-8"></a>sourcefiles=files(&#39;tfeapplication.c&#39;, &#39;tfenotebook.c&#39;, &#39;../tfetextview/tfetextview.c&#39;)</span>
<span id="cb2-9"><a href="#cb2-9"></a></span>
<span id="cb2-10"><a href="#cb2-10"></a>executable(&#39;tfe&#39;, sourcefiles, resources, dependencies: gtkdep)</span></code></pre></div>
<h2 id="tfe.gresource.xml">tfe.gresource.xml</h2>
<div class="sourceCode" id="cb3"><pre class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><span id="cb3-1"><a href="#cb3-1"></a><span class="kw">&lt;?xml</span> version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;<span class="kw">?&gt;</span></span>
<span id="cb3-2"><a href="#cb3-2"></a><span class="kw">&lt;gresources&gt;</span></span>
<span id="cb3-3"><a href="#cb3-3"></a> <span class="kw">&lt;gresource</span><span class="ot"> prefix=</span><span class="st">&quot;/com/github/ToshioCP/tfe&quot;</span><span class="kw">&gt;</span></span>
<span id="cb3-4"><a href="#cb3-4"></a> <span class="kw">&lt;file&gt;</span>tfe.ui<span class="kw">&lt;/file&gt;</span></span>
<span id="cb3-5"><a href="#cb3-5"></a> <span class="kw">&lt;/gresource&gt;</span></span>
<span id="cb3-6"><a href="#cb3-6"></a><span class="kw">&lt;/gresources&gt;</span></span></code></pre></div>
<h2 id="tfe.ui">tfe.ui</h2>
<div class="sourceCode" id="cb4"><pre class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><span id="cb4-1"><a href="#cb4-1"></a><span class="kw">&lt;?xml</span> version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;<span class="kw">?&gt;</span></span>
<span id="cb4-2"><a href="#cb4-2"></a><span class="kw">&lt;interface&gt;</span></span>
<span id="cb4-3"><a href="#cb4-3"></a> <span class="kw">&lt;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><span class="kw">&gt;</span></span>
<span id="cb4-4"><a href="#cb4-4"></a> <span class="kw">&lt;property</span><span class="ot"> name=</span><span class="st">&quot;title&quot;</span><span class="kw">&gt;</span>file editor<span class="kw">&lt;/property&gt;</span></span>
<span id="cb4-5"><a href="#cb4-5"></a> <span class="kw">&lt;property</span><span class="ot"> name=</span><span class="st">&quot;default-width&quot;</span><span class="kw">&gt;</span>600<span class="kw">&lt;/property&gt;</span></span>
<span id="cb4-6"><a href="#cb4-6"></a> <span class="kw">&lt;property</span><span class="ot"> name=</span><span class="st">&quot;default-height&quot;</span><span class="kw">&gt;</span>400<span class="kw">&lt;/property&gt;</span></span>
<span id="cb4-7"><a href="#cb4-7"></a> <span class="kw">&lt;child&gt;</span></span>
<span id="cb4-8"><a href="#cb4-8"></a> <span class="kw">&lt;object</span><span class="ot"> class=</span><span class="st">&quot;GtkBox&quot;</span><span class="ot"> id=</span><span class="st">&quot;boxv&quot;</span><span class="kw">&gt;</span></span>
<span id="cb4-9"><a href="#cb4-9"></a> <span class="kw">&lt;property</span><span class="ot"> name=</span><span class="st">&quot;orientation&quot;</span><span class="kw">&gt;</span>GTK_ORIENTATION_VERTICAL<span class="kw">&lt;/property&gt;</span></span>
<span id="cb4-10"><a href="#cb4-10"></a> <span class="kw">&lt;child&gt;</span></span>
<span id="cb4-11"><a href="#cb4-11"></a> <span class="kw">&lt;object</span><span class="ot"> class=</span><span class="st">&quot;GtkBox&quot;</span><span class="ot"> id=</span><span class="st">&quot;boxh&quot;</span><span class="kw">&gt;</span></span>
<span id="cb4-12"><a href="#cb4-12"></a> <span class="kw">&lt;property</span><span class="ot"> name=</span><span class="st">&quot;orientation&quot;</span><span class="kw">&gt;</span>GTK_ORIENTATION_HORIZONTAL<span class="kw">&lt;/property&gt;</span></span>
<span id="cb4-13"><a href="#cb4-13"></a> <span class="kw">&lt;child&gt;</span></span>
<span id="cb4-14"><a href="#cb4-14"></a> <span class="kw">&lt;object</span><span class="ot"> class=</span><span class="st">&quot;GtkLabel&quot;</span><span class="ot"> id=</span><span class="st">&quot;dmy1&quot;</span><span class="kw">&gt;</span></span>
<span id="cb4-15"><a href="#cb4-15"></a> <span class="kw">&lt;property</span><span class="ot"> name=</span><span class="st">&quot;width-chars&quot;</span><span class="kw">&gt;</span>10<span class="kw">&lt;/property&gt;</span></span>
<span id="cb4-16"><a href="#cb4-16"></a> <span class="kw">&lt;/object&gt;</span></span>
<span id="cb4-17"><a href="#cb4-17"></a> <span class="kw">&lt;/child&gt;</span></span>
<span id="cb4-18"><a href="#cb4-18"></a> <span class="kw">&lt;child&gt;</span></span>
<span id="cb4-19"><a href="#cb4-19"></a> <span class="kw">&lt;object</span><span class="ot"> class=</span><span class="st">&quot;GtkButton&quot;</span><span class="ot"> id=</span><span class="st">&quot;btnn&quot;</span><span class="kw">&gt;</span></span>
<span id="cb4-20"><a href="#cb4-20"></a> <span class="kw">&lt;property</span><span class="ot"> name=</span><span class="st">&quot;label&quot;</span><span class="kw">&gt;</span>New<span class="kw">&lt;/property&gt;</span></span>
<span id="cb4-21"><a href="#cb4-21"></a> <span class="kw">&lt;/object&gt;</span></span>
<span id="cb4-22"><a href="#cb4-22"></a> <span class="kw">&lt;/child&gt;</span></span>
<span id="cb4-23"><a href="#cb4-23"></a> <span class="kw">&lt;child&gt;</span></span>
<span id="cb4-24"><a href="#cb4-24"></a> <span class="kw">&lt;object</span><span class="ot"> class=</span><span class="st">&quot;GtkButton&quot;</span><span class="ot"> id=</span><span class="st">&quot;btno&quot;</span><span class="kw">&gt;</span></span>
<span id="cb4-25"><a href="#cb4-25"></a> <span class="kw">&lt;property</span><span class="ot"> name=</span><span class="st">&quot;label&quot;</span><span class="kw">&gt;</span>Open<span class="kw">&lt;/property&gt;</span></span>
<span id="cb4-26"><a href="#cb4-26"></a> <span class="kw">&lt;/object&gt;</span></span>
<span id="cb4-27"><a href="#cb4-27"></a> <span class="kw">&lt;/child&gt;</span></span>
<span id="cb4-28"><a href="#cb4-28"></a> <span class="kw">&lt;child&gt;</span></span>
<span id="cb4-29"><a href="#cb4-29"></a> <span class="kw">&lt;object</span><span class="ot"> class=</span><span class="st">&quot;GtkLabel&quot;</span><span class="ot"> id=</span><span class="st">&quot;dmy2&quot;</span><span class="kw">&gt;</span></span>
<span id="cb4-30"><a href="#cb4-30"></a> <span class="kw">&lt;property</span><span class="ot"> name=</span><span class="st">&quot;hexpand&quot;</span><span class="kw">&gt;</span>TRUE<span class="kw">&lt;/property&gt;</span></span>
<span id="cb4-31"><a href="#cb4-31"></a> <span class="kw">&lt;/object&gt;</span></span>
<span id="cb4-32"><a href="#cb4-32"></a> <span class="kw">&lt;/child&gt;</span></span>
<span id="cb4-33"><a href="#cb4-33"></a> <span class="kw">&lt;child&gt;</span></span>
<span id="cb4-34"><a href="#cb4-34"></a> <span class="kw">&lt;object</span><span class="ot"> class=</span><span class="st">&quot;GtkButton&quot;</span><span class="ot"> id=</span><span class="st">&quot;btns&quot;</span><span class="kw">&gt;</span></span>
<span id="cb4-35"><a href="#cb4-35"></a> <span class="kw">&lt;property</span><span class="ot"> name=</span><span class="st">&quot;label&quot;</span><span class="kw">&gt;</span>Save<span class="kw">&lt;/property&gt;</span></span>
<span id="cb4-36"><a href="#cb4-36"></a> <span class="kw">&lt;/object&gt;</span></span>
<span id="cb4-37"><a href="#cb4-37"></a> <span class="kw">&lt;/child&gt;</span></span>
<span id="cb4-38"><a href="#cb4-38"></a> <span class="kw">&lt;child&gt;</span></span>
<span id="cb4-39"><a href="#cb4-39"></a> <span class="kw">&lt;object</span><span class="ot"> class=</span><span class="st">&quot;GtkButton&quot;</span><span class="ot"> id=</span><span class="st">&quot;btnc&quot;</span><span class="kw">&gt;</span></span>
<span id="cb4-40"><a href="#cb4-40"></a> <span class="kw">&lt;property</span><span class="ot"> name=</span><span class="st">&quot;label&quot;</span><span class="kw">&gt;</span>Close<span class="kw">&lt;/property&gt;</span></span>
<span id="cb4-41"><a href="#cb4-41"></a> <span class="kw">&lt;/object&gt;</span></span>
<span id="cb4-42"><a href="#cb4-42"></a> <span class="kw">&lt;/child&gt;</span></span>
<span id="cb4-43"><a href="#cb4-43"></a> <span class="kw">&lt;child&gt;</span></span>
<span id="cb4-44"><a href="#cb4-44"></a> <span class="kw">&lt;object</span><span class="ot"> class=</span><span class="st">&quot;GtkLabel&quot;</span><span class="ot"> id=</span><span class="st">&quot;dmy3&quot;</span><span class="kw">&gt;</span></span>
<span id="cb4-45"><a href="#cb4-45"></a> <span class="kw">&lt;property</span><span class="ot"> name=</span><span class="st">&quot;width-chars&quot;</span><span class="kw">&gt;</span>10<span class="kw">&lt;/property&gt;</span></span>
<span id="cb4-46"><a href="#cb4-46"></a> <span class="kw">&lt;/object&gt;</span></span>
<span id="cb4-47"><a href="#cb4-47"></a> <span class="kw">&lt;/child&gt;</span></span>
<span id="cb4-48"><a href="#cb4-48"></a> <span class="kw">&lt;/object&gt;</span></span>
<span id="cb4-49"><a href="#cb4-49"></a> <span class="kw">&lt;/child&gt;</span></span>
<span id="cb4-50"><a href="#cb4-50"></a> <span class="kw">&lt;child&gt;</span></span>
<span id="cb4-51"><a href="#cb4-51"></a> <span class="kw">&lt;object</span><span class="ot"> class=</span><span class="st">&quot;GtkNotebook&quot;</span><span class="ot"> id=</span><span class="st">&quot;nb&quot;</span><span class="kw">&gt;</span></span>
<span id="cb4-52"><a href="#cb4-52"></a> <span class="kw">&lt;property</span><span class="ot"> name=</span><span class="st">&quot;scrollable&quot;</span><span class="kw">&gt;</span>TRUE<span class="kw">&lt;/property&gt;</span></span>
<span id="cb4-53"><a href="#cb4-53"></a> <span class="kw">&lt;property</span><span class="ot"> name=</span><span class="st">&quot;hexpand&quot;</span><span class="kw">&gt;</span>TRUE<span class="kw">&lt;/property&gt;</span></span>
<span id="cb4-54"><a href="#cb4-54"></a> <span class="kw">&lt;property</span><span class="ot"> name=</span><span class="st">&quot;vexpand&quot;</span><span class="kw">&gt;</span>TRUE<span class="kw">&lt;/property&gt;</span></span>
<span id="cb4-55"><a href="#cb4-55"></a> <span class="kw">&lt;/object&gt;</span></span>
<span id="cb4-56"><a href="#cb4-56"></a> <span class="kw">&lt;/child&gt;</span></span>
<span id="cb4-57"><a href="#cb4-57"></a> <span class="kw">&lt;/object&gt;</span></span>
<span id="cb4-58"><a href="#cb4-58"></a> <span class="kw">&lt;/child&gt;</span></span>
<span id="cb4-59"><a href="#cb4-59"></a> <span class="kw">&lt;/object&gt;</span></span>
<span id="cb4-60"><a href="#cb4-60"></a><span class="kw">&lt;/interface&gt;</span></span></code></pre></div>
<h2 id="tfe.h">tfe.h</h2>
<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="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span>
<span id="cb5-2"><a href="#cb5-2"></a></span>
<span id="cb5-3"><a href="#cb5-3"></a><span class="pp">#include </span><span class="im">&quot;../tfetextview/tfetextview.h&quot;</span></span>
<span id="cb5-4"><a href="#cb5-4"></a><span class="pp">#include </span><span class="im">&quot;tfenotebook.h&quot;</span></span></code></pre></div>
<h2 id="tfeapplication.c">tfeapplication.c</h2>
<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="pp">#include </span><span class="im">&quot;tfe.h&quot;</span></span>
<span id="cb6-2"><a href="#cb6-2"></a></span>
<span id="cb6-3"><a href="#cb6-3"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb6-4"><a href="#cb6-4"></a>open_cb (GtkNotebook *nb) {</span>
<span id="cb6-5"><a href="#cb6-5"></a> notebook_page_open (nb);</span>
<span id="cb6-6"><a href="#cb6-6"></a>}</span>
<span id="cb6-7"><a href="#cb6-7"></a></span>
<span id="cb6-8"><a href="#cb6-8"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb6-9"><a href="#cb6-9"></a>new_cb (GtkNotebook *nb) {</span>
<span id="cb6-10"><a href="#cb6-10"></a> notebook_page_new (nb);</span>
<span id="cb6-11"><a href="#cb6-11"></a>}</span>
<span id="cb6-12"><a href="#cb6-12"></a></span>
<span id="cb6-13"><a href="#cb6-13"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb6-14"><a href="#cb6-14"></a>save_cb (GtkNotebook *nb) {</span>
<span id="cb6-15"><a href="#cb6-15"></a> notebook_page_save (nb);</span>
<span id="cb6-16"><a href="#cb6-16"></a>}</span>
<span id="cb6-17"><a href="#cb6-17"></a></span>
<span id="cb6-18"><a href="#cb6-18"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb6-19"><a href="#cb6-19"></a>close_cb (GtkNotebook *nb) {</span>
<span id="cb6-20"><a href="#cb6-20"></a> notebook_page_close (GTK_NOTEBOOK (nb));</span>
<span id="cb6-21"><a href="#cb6-21"></a>}</span>
<span id="cb6-22"><a href="#cb6-22"></a></span>
<span id="cb6-23"><a href="#cb6-23"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb6-24"><a href="#cb6-24"></a>app_activate (GApplication *application) {</span>
<span id="cb6-25"><a href="#cb6-25"></a> GtkApplication *app = GTK_APPLICATION (application);</span>
<span id="cb6-26"><a href="#cb6-26"></a> GtkWidget *win = GTK_WIDGET (gtk_application_get_active_window (app));</span>
<span id="cb6-27"><a href="#cb6-27"></a> GtkWidget *boxv = gtk_window_get_child (GTK_WINDOW (win));</span>
<span id="cb6-28"><a href="#cb6-28"></a> GtkNotebook *nb = GTK_NOTEBOOK (gtk_widget_get_last_child (boxv));</span>
<span id="cb6-29"><a href="#cb6-29"></a></span>
<span id="cb6-30"><a href="#cb6-30"></a> notebook_page_new (nb);</span>
<span id="cb6-31"><a href="#cb6-31"></a> gtk_widget_show (GTK_WIDGET (win));</span>
<span id="cb6-32"><a href="#cb6-32"></a>}</span>
<span id="cb6-33"><a href="#cb6-33"></a></span>
<span id="cb6-34"><a href="#cb6-34"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb6-35"><a href="#cb6-35"></a>app_open (GApplication *application, GFile ** files, gint n_files, <span class="dt">const</span> gchar *hint) {</span>
<span id="cb6-36"><a href="#cb6-36"></a> GtkApplication *app = GTK_APPLICATION (application);</span>
<span id="cb6-37"><a href="#cb6-37"></a> GtkWidget *win = GTK_WIDGET (gtk_application_get_active_window (app));</span>
<span id="cb6-38"><a href="#cb6-38"></a> GtkWidget *boxv = gtk_window_get_child (GTK_WINDOW (win));</span>
<span id="cb6-39"><a href="#cb6-39"></a> GtkNotebook *nb = GTK_NOTEBOOK (gtk_widget_get_last_child (boxv));</span>
<span id="cb6-40"><a href="#cb6-40"></a> <span class="dt">int</span> i;</span>
<span id="cb6-41"><a href="#cb6-41"></a></span>
<span id="cb6-42"><a href="#cb6-42"></a> <span class="cf">for</span> (i = <span class="dv">0</span>; i &lt; n_files; i++)</span>
<span id="cb6-43"><a href="#cb6-43"></a> notebook_page_new_with_file (nb, files[i]);</span>
<span id="cb6-44"><a href="#cb6-44"></a> <span class="cf">if</span> (gtk_notebook_get_n_pages (nb) == <span class="dv">0</span>)</span>
<span id="cb6-45"><a href="#cb6-45"></a> notebook_page_new (nb);</span>
<span id="cb6-46"><a href="#cb6-46"></a> gtk_widget_show (win);</span>
<span id="cb6-47"><a href="#cb6-47"></a>}</span>
<span id="cb6-48"><a href="#cb6-48"></a></span>
<span id="cb6-49"><a href="#cb6-49"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb6-50"><a href="#cb6-50"></a>app_startup (GApplication *application) {</span>
<span id="cb6-51"><a href="#cb6-51"></a> GtkApplication *app = GTK_APPLICATION (application);</span>
<span id="cb6-52"><a href="#cb6-52"></a> GtkBuilder *build;</span>
<span id="cb6-53"><a href="#cb6-53"></a> GtkApplicationWindow *win;</span>
<span id="cb6-54"><a href="#cb6-54"></a> GtkNotebook *nb;</span>
<span id="cb6-55"><a href="#cb6-55"></a> GtkButton *btno;</span>
<span id="cb6-56"><a href="#cb6-56"></a> GtkButton *btnn;</span>
<span id="cb6-57"><a href="#cb6-57"></a> GtkButton *btns;</span>
<span id="cb6-58"><a href="#cb6-58"></a> GtkButton *btnc;</span>
<span id="cb6-59"><a href="#cb6-59"></a></span>
<span id="cb6-60"><a href="#cb6-60"></a> build = gtk_builder_new_from_resource (<span class="st">&quot;/com/github/ToshioCP/tfe/tfe.ui&quot;</span>);</span>
<span id="cb6-61"><a href="#cb6-61"></a> win = GTK_APPLICATION_WINDOW (gtk_builder_get_object (build, <span class="st">&quot;win&quot;</span>));</span>
<span id="cb6-62"><a href="#cb6-62"></a> nb = GTK_NOTEBOOK (gtk_builder_get_object (build, <span class="st">&quot;nb&quot;</span>));</span>
<span id="cb6-63"><a href="#cb6-63"></a> gtk_window_set_application (GTK_WINDOW (win), app);</span>
<span id="cb6-64"><a href="#cb6-64"></a> btno = GTK_BUTTON (gtk_builder_get_object (build, <span class="st">&quot;btno&quot;</span>));</span>
<span id="cb6-65"><a href="#cb6-65"></a> btnn = GTK_BUTTON (gtk_builder_get_object (build, <span class="st">&quot;btnn&quot;</span>));</span>
<span id="cb6-66"><a href="#cb6-66"></a> btns = GTK_BUTTON (gtk_builder_get_object (build, <span class="st">&quot;btns&quot;</span>));</span>
<span id="cb6-67"><a href="#cb6-67"></a> btnc = GTK_BUTTON (gtk_builder_get_object (build, <span class="st">&quot;btnc&quot;</span>));</span>
<span id="cb6-68"><a href="#cb6-68"></a> g_signal_connect_swapped (btno, <span class="st">&quot;clicked&quot;</span>, G_CALLBACK (open_cb), nb);</span>
<span id="cb6-69"><a href="#cb6-69"></a> g_signal_connect_swapped (btnn, <span class="st">&quot;clicked&quot;</span>, G_CALLBACK (new_cb), nb);</span>
<span id="cb6-70"><a href="#cb6-70"></a> g_signal_connect_swapped (btns, <span class="st">&quot;clicked&quot;</span>, G_CALLBACK (save_cb), nb);</span>
<span id="cb6-71"><a href="#cb6-71"></a> g_signal_connect_swapped (btnc, <span class="st">&quot;clicked&quot;</span>, G_CALLBACK (close_cb), nb);</span>
<span id="cb6-72"><a href="#cb6-72"></a> g_object_unref(build);</span>
<span id="cb6-73"><a href="#cb6-73"></a></span>
<span id="cb6-74"><a href="#cb6-74"></a>GdkDisplay *display;</span>
<span id="cb6-75"><a href="#cb6-75"></a></span>
<span id="cb6-76"><a href="#cb6-76"></a> display = gtk_widget_get_display (GTK_WIDGET (win));</span>
<span id="cb6-77"><a href="#cb6-77"></a> GtkCssProvider *provider = gtk_css_provider_new ();</span>
<span id="cb6-78"><a href="#cb6-78"></a> gtk_css_provider_load_from_data (provider, <span class="st">&quot;textview {padding: 10px; font-family: monospace; font-size: 12pt;}&quot;</span>, -<span class="dv">1</span>);</span>
<span id="cb6-79"><a href="#cb6-79"></a> gtk_style_context_add_provider_for_display (display, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);</span>
<span id="cb6-80"><a href="#cb6-80"></a>}</span>
<span id="cb6-81"><a href="#cb6-81"></a></span>
<span id="cb6-82"><a href="#cb6-82"></a><span class="pp">#define APPLICATION_ID &quot;com.github.ToshioCP.tfe&quot;</span></span>
<span id="cb6-83"><a href="#cb6-83"></a></span>
<span id="cb6-84"><a href="#cb6-84"></a><span class="dt">int</span></span>
<span id="cb6-85"><a href="#cb6-85"></a>main (<span class="dt">int</span> argc, <span class="dt">char</span> **argv) {</span>
<span id="cb6-86"><a href="#cb6-86"></a> GtkApplication *app;</span>
<span id="cb6-87"><a href="#cb6-87"></a> <span class="dt">int</span> stat;</span>
<span id="cb6-88"><a href="#cb6-88"></a></span>
<span id="cb6-89"><a href="#cb6-89"></a> app = gtk_application_new (APPLICATION_ID, G_APPLICATION_HANDLES_OPEN);</span>
<span id="cb6-90"><a href="#cb6-90"></a></span>
<span id="cb6-91"><a href="#cb6-91"></a> g_signal_connect (app, <span class="st">&quot;startup&quot;</span>, G_CALLBACK (app_startup), NULL);</span>
<span id="cb6-92"><a href="#cb6-92"></a> g_signal_connect (app, <span class="st">&quot;activate&quot;</span>, G_CALLBACK (app_activate), NULL);</span>
<span id="cb6-93"><a href="#cb6-93"></a> g_signal_connect (app, <span class="st">&quot;open&quot;</span>, G_CALLBACK (app_open), NULL);</span>
<span id="cb6-94"><a href="#cb6-94"></a></span>
<span id="cb6-95"><a href="#cb6-95"></a> stat =g_application_run (G_APPLICATION (app), argc, argv);</span>
<span id="cb6-96"><a href="#cb6-96"></a> g_object_unref (app);</span>
<span id="cb6-97"><a href="#cb6-97"></a> <span class="cf">return</span> stat;</span>
<span id="cb6-98"><a href="#cb6-98"></a>}</span></code></pre></div>
<h2 id="tfenotebook.h">tfenotebook.h</h2>
<div class="sourceCode" id="cb7"><pre class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb7-1"><a href="#cb7-1"></a><span class="dt">void</span></span>
<span id="cb7-2"><a href="#cb7-2"></a>notebook_page_save(GtkNotebook *nb);</span>
<span id="cb7-3"><a href="#cb7-3"></a></span>
<span id="cb7-4"><a href="#cb7-4"></a><span class="dt">void</span></span>
<span id="cb7-5"><a href="#cb7-5"></a>notebook_page_close (GtkNotebook *nb);</span>
<span id="cb7-6"><a href="#cb7-6"></a></span>
<span id="cb7-7"><a href="#cb7-7"></a><span class="dt">void</span></span>
<span id="cb7-8"><a href="#cb7-8"></a>notebook_page_open (GtkNotebook *nb);</span>
<span id="cb7-9"><a href="#cb7-9"></a></span>
<span id="cb7-10"><a href="#cb7-10"></a><span class="dt">void</span></span>
<span id="cb7-11"><a href="#cb7-11"></a>notebook_page_new_with_file (GtkNotebook *nb, GFile *file);</span>
<span id="cb7-12"><a href="#cb7-12"></a></span>
<span id="cb7-13"><a href="#cb7-13"></a><span class="dt">void</span></span>
<span id="cb7-14"><a href="#cb7-14"></a>notebook_page_new (GtkNotebook *nb);</span></code></pre></div>
<h2 id="tfenotebook.c">tfenotebook.c</h2>
<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">&quot;tfe.h&quot;</span></span>
<span id="cb8-2"><a href="#cb8-2"></a></span>
<span id="cb8-3"><a href="#cb8-3"></a><span class="co">/* The returned string should be freed with g_free() when no longer needed. */</span></span>
<span id="cb8-4"><a href="#cb8-4"></a><span class="dt">static</span> gchar*</span>
<span id="cb8-5"><a href="#cb8-5"></a>get_untitled () {</span>
<span id="cb8-6"><a href="#cb8-6"></a> <span class="dt">static</span> <span class="dt">int</span> c = -<span class="dv">1</span>;</span>
<span id="cb8-7"><a href="#cb8-7"></a> <span class="cf">if</span> (++c == <span class="dv">0</span>) </span>
<span id="cb8-8"><a href="#cb8-8"></a> <span class="cf">return</span> g_strdup_printf(<span class="st">&quot;Untitled&quot;</span>);</span>
<span id="cb8-9"><a href="#cb8-9"></a> <span class="cf">else</span></span>
<span id="cb8-10"><a href="#cb8-10"></a> <span class="cf">return</span> g_strdup_printf (<span class="st">&quot;Untitled%u&quot;</span>, c);</span>
<span id="cb8-11"><a href="#cb8-11"></a>}</span>
<span id="cb8-12"><a href="#cb8-12"></a></span>
<span id="cb8-13"><a href="#cb8-13"></a><span class="dt">static</span> TfeTextView *</span>
<span id="cb8-14"><a href="#cb8-14"></a>get_current_textview (GtkNotebook *nb) {</span>
<span id="cb8-15"><a href="#cb8-15"></a> <span class="dt">int</span> i;</span>
<span id="cb8-16"><a href="#cb8-16"></a> GtkWidget *scr;</span>
<span id="cb8-17"><a href="#cb8-17"></a> GtkWidget *tv;</span>
<span id="cb8-18"><a href="#cb8-18"></a></span>
<span id="cb8-19"><a href="#cb8-19"></a> i = gtk_notebook_get_current_page (nb);</span>
<span id="cb8-20"><a href="#cb8-20"></a> scr = gtk_notebook_get_nth_page (nb, i);</span>
<span id="cb8-21"><a href="#cb8-21"></a> tv = gtk_scrolled_window_get_child (GTK_SCROLLED_WINDOW (scr));</span>
<span id="cb8-22"><a href="#cb8-22"></a> <span class="cf">return</span> TFE_TEXT_VIEW (tv);</span>
<span id="cb8-23"><a href="#cb8-23"></a>}</span>
<span id="cb8-24"><a href="#cb8-24"></a></span>
<span id="cb8-25"><a href="#cb8-25"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb8-26"><a href="#cb8-26"></a>file_changed_cb (TfeTextView *tv, GtkNotebook *nb) {</span>
<span id="cb8-27"><a href="#cb8-27"></a> GtkWidget *scr;</span>
<span id="cb8-28"><a href="#cb8-28"></a> GtkWidget *label;</span>
<span id="cb8-29"><a href="#cb8-29"></a> GFile *file;</span>
<span id="cb8-30"><a href="#cb8-30"></a> <span class="dt">char</span> *filename;</span>
<span id="cb8-31"><a href="#cb8-31"></a></span>
<span id="cb8-32"><a href="#cb8-32"></a> file = tfe_text_view_get_file (tv);</span>
<span id="cb8-33"><a href="#cb8-33"></a> scr = gtk_widget_get_parent (GTK_WIDGET (tv));</span>
<span id="cb8-34"><a href="#cb8-34"></a> <span class="cf">if</span> (G_IS_FILE (file)) {</span>
<span id="cb8-35"><a href="#cb8-35"></a> filename = g_file_get_basename (file);</span>
<span id="cb8-36"><a href="#cb8-36"></a> g_object_unref (file);</span>
<span id="cb8-37"><a href="#cb8-37"></a> } <span class="cf">else</span></span>
<span id="cb8-38"><a href="#cb8-38"></a> filename = get_untitled ();</span>
<span id="cb8-39"><a href="#cb8-39"></a> label = gtk_label_new (filename);</span>
<span id="cb8-40"><a href="#cb8-40"></a> g_free (filename);</span>
<span id="cb8-41"><a href="#cb8-41"></a> gtk_notebook_set_tab_label (nb, scr, label);</span>
<span id="cb8-42"><a href="#cb8-42"></a>}</span>
<span id="cb8-43"><a href="#cb8-43"></a></span>
<span id="cb8-44"><a href="#cb8-44"></a><span class="dt">void</span></span>
<span id="cb8-45"><a href="#cb8-45"></a>notebook_page_save (GtkNotebook *nb) {</span>
<span id="cb8-46"><a href="#cb8-46"></a> g_return_if_fail(GTK_IS_NOTEBOOK (nb));</span>
<span id="cb8-47"><a href="#cb8-47"></a></span>
<span id="cb8-48"><a href="#cb8-48"></a> TfeTextView *tv;</span>
<span id="cb8-49"><a href="#cb8-49"></a></span>
<span id="cb8-50"><a href="#cb8-50"></a> tv = get_current_textview (nb);</span>
<span id="cb8-51"><a href="#cb8-51"></a> tfe_text_view_save (TFE_TEXT_VIEW (tv));</span>
<span id="cb8-52"><a href="#cb8-52"></a>}</span>
<span id="cb8-53"><a href="#cb8-53"></a></span>
<span id="cb8-54"><a href="#cb8-54"></a><span class="dt">void</span></span>
<span id="cb8-55"><a href="#cb8-55"></a>notebook_page_close (GtkNotebook *nb) {</span>
<span id="cb8-56"><a href="#cb8-56"></a> g_return_if_fail(GTK_IS_NOTEBOOK (nb));</span>
<span id="cb8-57"><a href="#cb8-57"></a></span>
<span id="cb8-58"><a href="#cb8-58"></a> GtkWidget *win;</span>
<span id="cb8-59"><a href="#cb8-59"></a> <span class="dt">int</span> i;</span>
<span id="cb8-60"><a href="#cb8-60"></a></span>
<span id="cb8-61"><a href="#cb8-61"></a> <span class="cf">if</span> (gtk_notebook_get_n_pages (nb) == <span class="dv">1</span>) {</span>
<span id="cb8-62"><a href="#cb8-62"></a> win = gtk_widget_get_ancestor (GTK_WIDGET (nb), GTK_TYPE_WINDOW);</span>
<span id="cb8-63"><a href="#cb8-63"></a> gtk_window_destroy(GTK_WINDOW (win));</span>
<span id="cb8-64"><a href="#cb8-64"></a> } <span class="cf">else</span> {</span>
<span id="cb8-65"><a href="#cb8-65"></a> i = gtk_notebook_get_current_page (nb);</span>
<span id="cb8-66"><a href="#cb8-66"></a> gtk_notebook_remove_page (GTK_NOTEBOOK (nb), i);</span>
<span id="cb8-67"><a href="#cb8-67"></a> }</span>
<span id="cb8-68"><a href="#cb8-68"></a>}</span>
<span id="cb8-69"><a href="#cb8-69"></a></span>
<span id="cb8-70"><a href="#cb8-70"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb8-71"><a href="#cb8-71"></a>notebook_page_build (GtkNotebook *nb, GtkWidget *tv, <span class="dt">char</span> *filename) {</span>
<span id="cb8-72"><a href="#cb8-72"></a> GtkWidget *scr = gtk_scrolled_window_new ();</span>
<span id="cb8-73"><a href="#cb8-73"></a> GtkNotebookPage *nbp;</span>
<span id="cb8-74"><a href="#cb8-74"></a> GtkWidget *lab;</span>
<span id="cb8-75"><a href="#cb8-75"></a> <span class="dt">int</span> i;</span>
<span id="cb8-76"><a href="#cb8-76"></a></span>
<span id="cb8-77"><a href="#cb8-77"></a> gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR);</span>
<span id="cb8-78"><a href="#cb8-78"></a> gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);</span>
<span id="cb8-79"><a href="#cb8-79"></a> lab = gtk_label_new (filename);</span>
<span id="cb8-80"><a href="#cb8-80"></a> i = gtk_notebook_append_page (nb, scr, lab);</span>
<span id="cb8-81"><a href="#cb8-81"></a> nbp = gtk_notebook_get_page (nb, scr);</span>
<span id="cb8-82"><a href="#cb8-82"></a> g_object_set (nbp, <span class="st">&quot;tab-expand&quot;</span>, TRUE, NULL);</span>
<span id="cb8-83"><a href="#cb8-83"></a> gtk_notebook_set_current_page (nb, i);</span>
<span id="cb8-84"><a href="#cb8-84"></a> g_signal_connect (GTK_TEXT_VIEW (tv), <span class="st">&quot;change-file&quot;</span>, G_CALLBACK (file_changed_cb), nb);</span>
<span id="cb8-85"><a href="#cb8-85"></a>}</span>
<span id="cb8-86"><a href="#cb8-86"></a></span>
<span id="cb8-87"><a href="#cb8-87"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb8-88"><a href="#cb8-88"></a>open_response (TfeTextView *tv, <span class="dt">int</span> response, GtkNotebook *nb) {</span>
<span id="cb8-89"><a href="#cb8-89"></a> GFile *file;</span>
<span id="cb8-90"><a href="#cb8-90"></a> <span class="dt">char</span> *filename;</span>
<span id="cb8-91"><a href="#cb8-91"></a></span>
<span id="cb8-92"><a href="#cb8-92"></a> <span class="cf">if</span> (response != TFE_OPEN_RESPONSE_SUCCESS || ! G_IS_FILE (file = tfe_text_view_get_file (tv))) {</span>
<span id="cb8-93"><a href="#cb8-93"></a> g_object_ref_sink (tv);</span>
<span id="cb8-94"><a href="#cb8-94"></a> g_object_unref (tv);</span>
<span id="cb8-95"><a href="#cb8-95"></a> }<span class="cf">else</span> {</span>
<span id="cb8-96"><a href="#cb8-96"></a> filename = g_file_get_basename (file);</span>
<span id="cb8-97"><a href="#cb8-97"></a> g_object_unref (file);</span>
<span id="cb8-98"><a href="#cb8-98"></a> notebook_page_build (nb, GTK_WIDGET (tv), filename);</span>
<span id="cb8-99"><a href="#cb8-99"></a> }</span>
<span id="cb8-100"><a href="#cb8-100"></a>}</span>
<span id="cb8-101"><a href="#cb8-101"></a></span>
<span id="cb8-102"><a href="#cb8-102"></a><span class="dt">void</span></span>
<span id="cb8-103"><a href="#cb8-103"></a>notebook_page_open (GtkNotebook *nb) {</span>
<span id="cb8-104"><a href="#cb8-104"></a> g_return_if_fail(GTK_IS_NOTEBOOK (nb));</span>
<span id="cb8-105"><a href="#cb8-105"></a></span>
<span id="cb8-106"><a href="#cb8-106"></a> GtkWidget *tv;</span>
<span id="cb8-107"><a href="#cb8-107"></a></span>
<span id="cb8-108"><a href="#cb8-108"></a> <span class="cf">if</span> ((tv = tfe_text_view_new ()) == NULL)</span>
<span id="cb8-109"><a href="#cb8-109"></a> <span class="cf">return</span>;</span>
<span id="cb8-110"><a href="#cb8-110"></a> g_signal_connect (TFE_TEXT_VIEW (tv), <span class="st">&quot;open-response&quot;</span>, G_CALLBACK (open_response), nb);</span>
<span id="cb8-111"><a href="#cb8-111"></a> tfe_text_view_open (TFE_TEXT_VIEW (tv), GTK_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET (nb), GTK_TYPE_WINDOW)));</span>
<span id="cb8-112"><a href="#cb8-112"></a>}</span>
<span id="cb8-113"><a href="#cb8-113"></a></span>
<span id="cb8-114"><a href="#cb8-114"></a><span class="dt">void</span></span>
<span id="cb8-115"><a href="#cb8-115"></a>notebook_page_new_with_file (GtkNotebook *nb, GFile *file) {</span>
<span id="cb8-116"><a href="#cb8-116"></a> g_return_if_fail(GTK_IS_NOTEBOOK (nb));</span>
<span id="cb8-117"><a href="#cb8-117"></a> g_return_if_fail(G_IS_FILE (file));</span>
<span id="cb8-118"><a href="#cb8-118"></a></span>
<span id="cb8-119"><a href="#cb8-119"></a> GtkWidget *tv;</span>
<span id="cb8-120"><a href="#cb8-120"></a> <span class="dt">char</span> *filename;</span>
<span id="cb8-121"><a href="#cb8-121"></a></span>
<span id="cb8-122"><a href="#cb8-122"></a> <span class="cf">if</span> ((tv = tfe_text_view_new_with_file (file)) == NULL)</span>
<span id="cb8-123"><a href="#cb8-123"></a> <span class="cf">return</span>; <span class="co">/* read error */</span></span>
<span id="cb8-124"><a href="#cb8-124"></a> filename = g_file_get_basename (file);</span>
<span id="cb8-125"><a href="#cb8-125"></a> notebook_page_build (nb, tv, filename);</span>
<span id="cb8-126"><a href="#cb8-126"></a>}</span>
<span id="cb8-127"><a href="#cb8-127"></a></span>
<span id="cb8-128"><a href="#cb8-128"></a><span class="dt">void</span></span>
<span id="cb8-129"><a href="#cb8-129"></a>notebook_page_new (GtkNotebook *nb) {</span>
<span id="cb8-130"><a href="#cb8-130"></a> g_return_if_fail(GTK_IS_NOTEBOOK (nb));</span>
<span id="cb8-131"><a href="#cb8-131"></a></span>
<span id="cb8-132"><a href="#cb8-132"></a> GtkWidget *tv;</span>
<span id="cb8-133"><a href="#cb8-133"></a> <span class="dt">char</span> *filename;</span>
<span id="cb8-134"><a href="#cb8-134"></a></span>
<span id="cb8-135"><a href="#cb8-135"></a> <span class="cf">if</span> ((tv = tfe_text_view_new ()) == NULL)</span>
<span id="cb8-136"><a href="#cb8-136"></a> <span class="cf">return</span>;</span>
<span id="cb8-137"><a href="#cb8-137"></a> filename = get_untitled ();</span>
<span id="cb8-138"><a href="#cb8-138"></a> notebook_page_build (nb, tv, filename);</span>
<span id="cb8-139"><a href="#cb8-139"></a>}</span></code></pre></div>
<h2 id="tfetextview.h">tfetextview.h</h2>
<div class="sourceCode" id="cb9"><pre class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb9-1"><a href="#cb9-1"></a><span class="pp">#ifndef __TFE_TEXT_VIEW_H__</span></span>
<span id="cb9-2"><a href="#cb9-2"></a><span class="pp">#define __TFE_TEXT_VIEW_H__</span></span>
<span id="cb9-3"><a href="#cb9-3"></a></span>
<span id="cb9-4"><a href="#cb9-4"></a><span class="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span>
<span id="cb9-5"><a href="#cb9-5"></a></span>
<span id="cb9-6"><a href="#cb9-6"></a><span class="pp">#define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type ()</span></span>
<span id="cb9-7"><a href="#cb9-7"></a>G_DECLARE_FINAL_TYPE (TfeTextView, tfe_text_view, TFE, TEXT_VIEW, GtkTextView)</span>
<span id="cb9-8"><a href="#cb9-8"></a></span>
<span id="cb9-9"><a href="#cb9-9"></a><span class="co">/* &quot;open-response&quot; signal response */</span></span>
<span id="cb9-10"><a href="#cb9-10"></a><span class="kw">enum</span> TfeTextViewOpenResponseType</span>
<span id="cb9-11"><a href="#cb9-11"></a>{</span>
<span id="cb9-12"><a href="#cb9-12"></a> TFE_OPEN_RESPONSE_SUCCESS,</span>
<span id="cb9-13"><a href="#cb9-13"></a> TFE_OPEN_RESPONSE_CANCEL,</span>
<span id="cb9-14"><a href="#cb9-14"></a> TFE_OPEN_RESPONSE_ERROR</span>
<span id="cb9-15"><a href="#cb9-15"></a>};</span>
<span id="cb9-16"><a href="#cb9-16"></a></span>
<span id="cb9-17"><a href="#cb9-17"></a>GFile *</span>
<span id="cb9-18"><a href="#cb9-18"></a>tfe_text_view_get_file (TfeTextView *tv);</span>
<span id="cb9-19"><a href="#cb9-19"></a></span>
<span id="cb9-20"><a href="#cb9-20"></a><span class="dt">void</span></span>
<span id="cb9-21"><a href="#cb9-21"></a>tfe_text_view_open (TfeTextView *tv, GtkWindow *win);</span>
<span id="cb9-22"><a href="#cb9-22"></a></span>
<span id="cb9-23"><a href="#cb9-23"></a><span class="dt">void</span></span>
<span id="cb9-24"><a href="#cb9-24"></a>tfe_text_view_save (TfeTextView *tv);</span>
<span id="cb9-25"><a href="#cb9-25"></a></span>
<span id="cb9-26"><a href="#cb9-26"></a><span class="dt">void</span></span>
<span id="cb9-27"><a href="#cb9-27"></a>tfe_text_view_saveas (TfeTextView *tv);</span>
<span id="cb9-28"><a href="#cb9-28"></a></span>
<span id="cb9-29"><a href="#cb9-29"></a>GtkWidget *</span>
<span id="cb9-30"><a href="#cb9-30"></a>tfe_text_view_new_with_file (GFile *file);</span>
<span id="cb9-31"><a href="#cb9-31"></a></span>
<span id="cb9-32"><a href="#cb9-32"></a>GtkWidget *</span>
<span id="cb9-33"><a href="#cb9-33"></a>tfe_text_view_new (<span class="dt">void</span>);</span>
<span id="cb9-34"><a href="#cb9-34"></a></span>
<span id="cb9-35"><a href="#cb9-35"></a><span class="pp">#endif </span><span class="co">/* __TFE_TEXT_VIEW_H__ */</span></span></code></pre></div>
<h2 id="tfetextview.c">tfetextview.c</h2>
<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;string.h&gt;</span></span>
<span id="cb10-2"><a href="#cb10-2"></a><span class="pp">#include </span><span class="im">&quot;tfetextview.h&quot;</span></span>
<span id="cb10-3"><a href="#cb10-3"></a></span>
<span id="cb10-4"><a href="#cb10-4"></a><span class="kw">struct</span> _TfeTextView {</span>
<span id="cb10-5"><a href="#cb10-5"></a> GtkTextView parent;</span>
<span id="cb10-6"><a href="#cb10-6"></a> GFile *file;</span>
<span id="cb10-7"><a href="#cb10-7"></a>};</span>
<span id="cb10-8"><a href="#cb10-8"></a></span>
<span id="cb10-9"><a href="#cb10-9"></a>G_DEFINE_TYPE (TfeTextView, tfe_text_view, GTK_TYPE_TEXT_VIEW);</span>
<span id="cb10-10"><a href="#cb10-10"></a></span>
<span id="cb10-11"><a href="#cb10-11"></a><span class="kw">enum</span> {</span>
<span id="cb10-12"><a href="#cb10-12"></a> CHANGE_FILE,</span>
<span id="cb10-13"><a href="#cb10-13"></a> OPEN_RESPONSE,</span>
<span id="cb10-14"><a href="#cb10-14"></a> NUMBER_OF_SIGNALS</span>
<span id="cb10-15"><a href="#cb10-15"></a>};</span>
<span id="cb10-16"><a href="#cb10-16"></a></span>
<span id="cb10-17"><a href="#cb10-17"></a><span class="dt">static</span> guint tfe_text_view_signals[NUMBER_OF_SIGNALS];</span>
<span id="cb10-18"><a href="#cb10-18"></a></span>
<span id="cb10-19"><a href="#cb10-19"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb10-20"><a href="#cb10-20"></a>tfe_text_view_dispose (GObject *gobject) {</span>
<span id="cb10-21"><a href="#cb10-21"></a> TfeTextView *tv = TFE_TEXT_VIEW (gobject);</span>
<span id="cb10-22"><a href="#cb10-22"></a></span>
<span id="cb10-23"><a href="#cb10-23"></a> <span class="cf">if</span> (G_IS_FILE (tv-&gt;file))</span>
<span id="cb10-24"><a href="#cb10-24"></a> g_clear_object (&amp;tv-&gt;file);</span>
<span id="cb10-25"><a href="#cb10-25"></a></span>
<span id="cb10-26"><a href="#cb10-26"></a> G_OBJECT_CLASS (tfe_text_view_parent_class)-&gt;dispose (gobject);</span>
<span id="cb10-27"><a href="#cb10-27"></a>}</span>
<span id="cb10-28"><a href="#cb10-28"></a></span>
<span id="cb10-29"><a href="#cb10-29"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb10-30"><a href="#cb10-30"></a>tfe_text_view_init (TfeTextView *tv) {</span>
<span id="cb10-31"><a href="#cb10-31"></a> tv-&gt;file = NULL;</span>
<span id="cb10-32"><a href="#cb10-32"></a>}</span>
<span id="cb10-33"><a href="#cb10-33"></a></span>
<span id="cb10-34"><a href="#cb10-34"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb10-35"><a href="#cb10-35"></a>tfe_text_view_class_init (TfeTextViewClass *class) {</span>
<span id="cb10-36"><a href="#cb10-36"></a> GObjectClass *object_class = G_OBJECT_CLASS (class);</span>
<span id="cb10-37"><a href="#cb10-37"></a></span>
<span id="cb10-38"><a href="#cb10-38"></a> object_class-&gt;dispose = tfe_text_view_dispose;</span>
<span id="cb10-39"><a href="#cb10-39"></a> tfe_text_view_signals[CHANGE_FILE] = g_signal_new (<span class="st">&quot;change-file&quot;</span>,</span>
<span id="cb10-40"><a href="#cb10-40"></a> G_TYPE_FROM_CLASS (class),</span>
<span id="cb10-41"><a href="#cb10-41"></a> G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,</span>
<span id="cb10-42"><a href="#cb10-42"></a> <span class="dv">0</span> <span class="co">/* class offset */</span>,</span>
<span id="cb10-43"><a href="#cb10-43"></a> NULL <span class="co">/* accumulator */</span>,</span>
<span id="cb10-44"><a href="#cb10-44"></a> NULL <span class="co">/* accumulator data */</span>,</span>
<span id="cb10-45"><a href="#cb10-45"></a> NULL <span class="co">/* C marshaller */</span>,</span>
<span id="cb10-46"><a href="#cb10-46"></a> G_TYPE_NONE <span class="co">/* return_type */</span>,</span>
<span id="cb10-47"><a href="#cb10-47"></a> <span class="dv">0</span> <span class="co">/* n_params */</span></span>
<span id="cb10-48"><a href="#cb10-48"></a> );</span>
<span id="cb10-49"><a href="#cb10-49"></a> tfe_text_view_signals[OPEN_RESPONSE] = g_signal_new (<span class="st">&quot;open-response&quot;</span>,</span>
<span id="cb10-50"><a href="#cb10-50"></a> G_TYPE_FROM_CLASS (class),</span>
<span id="cb10-51"><a href="#cb10-51"></a> G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,</span>
<span id="cb10-52"><a href="#cb10-52"></a> <span class="dv">0</span> <span class="co">/* class offset */</span>,</span>
<span id="cb10-53"><a href="#cb10-53"></a> NULL <span class="co">/* accumulator */</span>,</span>
<span id="cb10-54"><a href="#cb10-54"></a> NULL <span class="co">/* accumulator data */</span>,</span>
<span id="cb10-55"><a href="#cb10-55"></a> NULL <span class="co">/* C marshaller */</span>,</span>
<span id="cb10-56"><a href="#cb10-56"></a> G_TYPE_NONE <span class="co">/* return_type */</span>,</span>
<span id="cb10-57"><a href="#cb10-57"></a> <span class="dv">1</span> <span class="co">/* n_params */</span>,</span>
<span id="cb10-58"><a href="#cb10-58"></a> G_TYPE_INT</span>
<span id="cb10-59"><a href="#cb10-59"></a> );</span>
<span id="cb10-60"><a href="#cb10-60"></a>}</span>
<span id="cb10-61"><a href="#cb10-61"></a></span>
<span id="cb10-62"><a href="#cb10-62"></a>GFile *</span>
<span id="cb10-63"><a href="#cb10-63"></a>tfe_text_view_get_file (TfeTextView *tv) {</span>
<span id="cb10-64"><a href="#cb10-64"></a> g_return_val_if_fail (TFE_IS_TEXT_VIEW (tv), NULL);</span>
<span id="cb10-65"><a href="#cb10-65"></a></span>
<span id="cb10-66"><a href="#cb10-66"></a> <span class="cf">if</span> (G_IS_FILE (tv-&gt;file))</span>
<span id="cb10-67"><a href="#cb10-67"></a> <span class="cf">return</span> g_file_dup (tv-&gt;file);</span>
<span id="cb10-68"><a href="#cb10-68"></a> <span class="cf">else</span></span>
<span id="cb10-69"><a href="#cb10-69"></a> <span class="cf">return</span> NULL;</span>
<span id="cb10-70"><a href="#cb10-70"></a>}</span>
<span id="cb10-71"><a href="#cb10-71"></a></span>
<span id="cb10-72"><a href="#cb10-72"></a><span class="dt">static</span> gboolean</span>
<span id="cb10-73"><a href="#cb10-73"></a>save_file (GFile *file, GtkTextBuffer *tb, GtkWindow *win) {</span>
<span id="cb10-74"><a href="#cb10-74"></a> GtkTextIter start_iter;</span>
<span id="cb10-75"><a href="#cb10-75"></a> GtkTextIter end_iter;</span>
<span id="cb10-76"><a href="#cb10-76"></a> gchar *contents;</span>
<span id="cb10-77"><a href="#cb10-77"></a> gboolean stat;</span>
<span id="cb10-78"><a href="#cb10-78"></a> GtkWidget *message_dialog;</span>
<span id="cb10-79"><a href="#cb10-79"></a> GError *err = NULL;</span>
<span id="cb10-80"><a href="#cb10-80"></a></span>
<span id="cb10-81"><a href="#cb10-81"></a> gtk_text_buffer_get_bounds (tb, &amp;start_iter, &amp;end_iter);</span>
<span id="cb10-82"><a href="#cb10-82"></a> contents = gtk_text_buffer_get_text (tb, &amp;start_iter, &amp;end_iter, FALSE);</span>
<span id="cb10-83"><a href="#cb10-83"></a> <span class="cf">if</span> (g_file_replace_contents (file, contents, strlen (contents), NULL, TRUE, G_FILE_CREATE_NONE, NULL, NULL, &amp;err)) {</span>
<span id="cb10-84"><a href="#cb10-84"></a> gtk_text_buffer_set_modified (tb, FALSE);</span>
<span id="cb10-85"><a href="#cb10-85"></a> stat = TRUE;</span>
<span id="cb10-86"><a href="#cb10-86"></a> } <span class="cf">else</span> {</span>
<span id="cb10-87"><a href="#cb10-87"></a> message_dialog = gtk_message_dialog_new (win, GTK_DIALOG_MODAL,</span>
<span id="cb10-88"><a href="#cb10-88"></a> GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,</span>
<span id="cb10-89"><a href="#cb10-89"></a> <span class="st">&quot;%s.</span><span class="sc">\n</span><span class="st">&quot;</span>, err-&gt;message);</span>
<span id="cb10-90"><a href="#cb10-90"></a> g_signal_connect (message_dialog, <span class="st">&quot;response&quot;</span>, G_CALLBACK (gtk_window_destroy), NULL);</span>
<span id="cb10-91"><a href="#cb10-91"></a> gtk_widget_show (message_dialog);</span>
<span id="cb10-92"><a href="#cb10-92"></a> g_error_free (err);</span>
<span id="cb10-93"><a href="#cb10-93"></a> stat = FALSE;</span>
<span id="cb10-94"><a href="#cb10-94"></a> }</span>
<span id="cb10-95"><a href="#cb10-95"></a> g_free (contents);</span>
<span id="cb10-96"><a href="#cb10-96"></a> <span class="cf">return</span> stat;</span>
<span id="cb10-97"><a href="#cb10-97"></a>}</span>
<span id="cb10-98"><a href="#cb10-98"></a></span>
<span id="cb10-99"><a href="#cb10-99"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb10-100"><a href="#cb10-100"></a>saveas_dialog_response (GtkWidget *dialog, gint response, TfeTextView *tv) {</span>
<span id="cb10-101"><a href="#cb10-101"></a> GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));</span>
<span id="cb10-102"><a href="#cb10-102"></a> GFile *file;</span>
<span id="cb10-103"><a href="#cb10-103"></a> GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW);</span>
<span id="cb10-104"><a href="#cb10-104"></a></span>
<span id="cb10-105"><a href="#cb10-105"></a> <span class="cf">if</span> (response == GTK_RESPONSE_ACCEPT) {</span>
<span id="cb10-106"><a href="#cb10-106"></a> file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));</span>
<span id="cb10-107"><a href="#cb10-107"></a> <span class="cf">if</span> (! G_IS_FILE (file))</span>
<span id="cb10-108"><a href="#cb10-108"></a> g_warning (<span class="st">&quot;TfeTextView: gtk_file_chooser_get_file returns non GFile.</span><span class="sc">\n</span><span class="st">&quot;</span>);</span>
<span id="cb10-109"><a href="#cb10-109"></a> <span class="cf">else</span> <span class="cf">if</span> (save_file(file, tb, GTK_WINDOW (win))) {</span>
<span id="cb10-110"><a href="#cb10-110"></a> <span class="cf">if</span> (G_IS_FILE (tv-&gt;file))</span>
<span id="cb10-111"><a href="#cb10-111"></a> g_object_unref (tv-&gt;file);</span>
<span id="cb10-112"><a href="#cb10-112"></a> tv-&gt;file = file;</span>
<span id="cb10-113"><a href="#cb10-113"></a> g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], <span class="dv">0</span>);</span>
<span id="cb10-114"><a href="#cb10-114"></a> } <span class="cf">else</span></span>
<span id="cb10-115"><a href="#cb10-115"></a> g_object_unref (file);</span>
<span id="cb10-116"><a href="#cb10-116"></a> }</span>
<span id="cb10-117"><a href="#cb10-117"></a> gtk_window_destroy (GTK_WINDOW (dialog));</span>
<span id="cb10-118"><a href="#cb10-118"></a>}</span>
<span id="cb10-119"><a href="#cb10-119"></a></span>
<span id="cb10-120"><a href="#cb10-120"></a><span class="dt">void</span></span>
<span id="cb10-121"><a href="#cb10-121"></a>tfe_text_view_save (TfeTextView *tv) {</span>
<span id="cb10-122"><a href="#cb10-122"></a> g_return_if_fail (TFE_IS_TEXT_VIEW (tv));</span>
<span id="cb10-123"><a href="#cb10-123"></a></span>
<span id="cb10-124"><a href="#cb10-124"></a> GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));</span>
<span id="cb10-125"><a href="#cb10-125"></a> GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW);</span>
<span id="cb10-126"><a href="#cb10-126"></a></span>
<span id="cb10-127"><a href="#cb10-127"></a> <span class="cf">if</span> (! gtk_text_buffer_get_modified (tb))</span>
<span id="cb10-128"><a href="#cb10-128"></a> <span class="cf">return</span>; <span class="co">/* no need to save it */</span></span>
<span id="cb10-129"><a href="#cb10-129"></a> <span class="cf">else</span> <span class="cf">if</span> (tv-&gt;file == NULL)</span>
<span id="cb10-130"><a href="#cb10-130"></a> tfe_text_view_saveas (tv);</span>
<span id="cb10-131"><a href="#cb10-131"></a> <span class="cf">else</span> <span class="cf">if</span> (! G_IS_FILE (tv-&gt;file))</span>
<span id="cb10-132"><a href="#cb10-132"></a> g_error (<span class="st">&quot;TfeTextView: The pointer tv-&gt;file isn&#39;t NULL nor GFile.</span><span class="sc">\n</span><span class="st">&quot;</span>);</span>
<span id="cb10-133"><a href="#cb10-133"></a> <span class="cf">else</span></span>
<span id="cb10-134"><a href="#cb10-134"></a> save_file (tv-&gt;file, tb, GTK_WINDOW (win));</span>
<span id="cb10-135"><a href="#cb10-135"></a>}</span>
<span id="cb10-136"><a href="#cb10-136"></a></span>
<span id="cb10-137"><a href="#cb10-137"></a><span class="dt">void</span></span>
<span id="cb10-138"><a href="#cb10-138"></a>tfe_text_view_saveas (TfeTextView *tv) {</span>
<span id="cb10-139"><a href="#cb10-139"></a> g_return_if_fail (TFE_IS_TEXT_VIEW (tv));</span>
<span id="cb10-140"><a href="#cb10-140"></a></span>
<span id="cb10-141"><a href="#cb10-141"></a> GtkWidget *dialog;</span>
<span id="cb10-142"><a href="#cb10-142"></a> GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW);</span>
<span id="cb10-143"><a href="#cb10-143"></a></span>
<span id="cb10-144"><a href="#cb10-144"></a> dialog = gtk_file_chooser_dialog_new (<span class="st">&quot;Save file&quot;</span>, GTK_WINDOW (win), GTK_FILE_CHOOSER_ACTION_SAVE,</span>
<span id="cb10-145"><a href="#cb10-145"></a> <span class="st">&quot;Cancel&quot;</span>, GTK_RESPONSE_CANCEL,</span>
<span id="cb10-146"><a href="#cb10-146"></a> <span class="st">&quot;Save&quot;</span>, GTK_RESPONSE_ACCEPT,</span>
<span id="cb10-147"><a href="#cb10-147"></a> NULL);</span>
<span id="cb10-148"><a href="#cb10-148"></a> g_signal_connect (dialog, <span class="st">&quot;response&quot;</span>, G_CALLBACK (saveas_dialog_response), tv);</span>
<span id="cb10-149"><a href="#cb10-149"></a> gtk_widget_show (dialog);</span>
<span id="cb10-150"><a href="#cb10-150"></a>}</span>
<span id="cb10-151"><a href="#cb10-151"></a></span>
<span id="cb10-152"><a href="#cb10-152"></a>GtkWidget *</span>
<span id="cb10-153"><a href="#cb10-153"></a>tfe_text_view_new_with_file (GFile *file) {</span>
<span id="cb10-154"><a href="#cb10-154"></a> g_return_val_if_fail (G_IS_FILE (file), NULL);</span>
<span id="cb10-155"><a href="#cb10-155"></a></span>
<span id="cb10-156"><a href="#cb10-156"></a> GtkWidget *tv;</span>
<span id="cb10-157"><a href="#cb10-157"></a> GtkTextBuffer *tb;</span>
<span id="cb10-158"><a href="#cb10-158"></a> <span class="dt">char</span> *contents;</span>
<span id="cb10-159"><a href="#cb10-159"></a> gsize length;</span>
<span id="cb10-160"><a href="#cb10-160"></a></span>
<span id="cb10-161"><a href="#cb10-161"></a> <span class="cf">if</span> (! g_file_load_contents (file, NULL, &amp;contents, &amp;length, NULL, NULL)) <span class="co">/* read error */</span></span>
<span id="cb10-162"><a href="#cb10-162"></a> <span class="cf">return</span> NULL;</span>
<span id="cb10-163"><a href="#cb10-163"></a></span>
<span id="cb10-164"><a href="#cb10-164"></a> <span class="cf">if</span> ((tv = tfe_text_view_new()) != NULL) {</span>
<span id="cb10-165"><a href="#cb10-165"></a> tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));</span>
<span id="cb10-166"><a href="#cb10-166"></a> gtk_text_buffer_set_text (tb, contents, length);</span>
<span id="cb10-167"><a href="#cb10-167"></a> TFE_TEXT_VIEW (tv)-&gt;file = g_file_dup (file);</span>
<span id="cb10-168"><a href="#cb10-168"></a> gtk_text_buffer_set_modified (tb, FALSE);</span>
<span id="cb10-169"><a href="#cb10-169"></a> }</span>
<span id="cb10-170"><a href="#cb10-170"></a> g_free (contents);</span>
<span id="cb10-171"><a href="#cb10-171"></a> <span class="cf">return</span> tv;</span>
<span id="cb10-172"><a href="#cb10-172"></a>}</span>
<span id="cb10-173"><a href="#cb10-173"></a></span>
<span id="cb10-174"><a href="#cb10-174"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb10-175"><a href="#cb10-175"></a>open_dialog_response(GtkWidget *dialog, gint response, TfeTextView *tv) {</span>
<span id="cb10-176"><a href="#cb10-176"></a> GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));</span>
<span id="cb10-177"><a href="#cb10-177"></a> GFile *file;</span>
<span id="cb10-178"><a href="#cb10-178"></a> <span class="dt">char</span> *contents;</span>
<span id="cb10-179"><a href="#cb10-179"></a> gsize length;</span>
<span id="cb10-180"><a href="#cb10-180"></a> GtkWidget *message_dialog;</span>
<span id="cb10-181"><a href="#cb10-181"></a> GError *err = NULL;</span>
<span id="cb10-182"><a href="#cb10-182"></a></span>
<span id="cb10-183"><a href="#cb10-183"></a> <span class="cf">if</span> (response != GTK_RESPONSE_ACCEPT)</span>
<span id="cb10-184"><a href="#cb10-184"></a> g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], <span class="dv">0</span>, TFE_OPEN_RESPONSE_CANCEL);</span>
<span id="cb10-185"><a href="#cb10-185"></a> <span class="cf">else</span> <span class="cf">if</span> (! G_IS_FILE (file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog)))) {</span>
<span id="cb10-186"><a href="#cb10-186"></a> g_warning (<span class="st">&quot;TfeTextView: gtk_file_chooser_get_file returns non GFile.</span><span class="sc">\n</span><span class="st">&quot;</span>);</span>
<span id="cb10-187"><a href="#cb10-187"></a> g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], <span class="dv">0</span>, TFE_OPEN_RESPONSE_ERROR);</span>
<span id="cb10-188"><a href="#cb10-188"></a> } <span class="cf">else</span> <span class="cf">if</span> (! g_file_load_contents (file, NULL, &amp;contents, &amp;length, NULL, &amp;err)) { <span class="co">/* read error */</span></span>
<span id="cb10-189"><a href="#cb10-189"></a> g_object_unref (file);</span>
<span id="cb10-190"><a href="#cb10-190"></a> message_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog), GTK_DIALOG_MODAL,</span>
<span id="cb10-191"><a href="#cb10-191"></a> GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,</span>
<span id="cb10-192"><a href="#cb10-192"></a> <span class="st">&quot;%s.</span><span class="sc">\n</span><span class="st">&quot;</span>, err-&gt;message);</span>
<span id="cb10-193"><a href="#cb10-193"></a> g_signal_connect (message_dialog, <span class="st">&quot;response&quot;</span>, G_CALLBACK (gtk_window_destroy), NULL);</span>
<span id="cb10-194"><a href="#cb10-194"></a> gtk_widget_show (message_dialog);</span>
<span id="cb10-195"><a href="#cb10-195"></a> g_error_free (err);</span>
<span id="cb10-196"><a href="#cb10-196"></a> g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], <span class="dv">0</span>, TFE_OPEN_RESPONSE_ERROR);</span>
<span id="cb10-197"><a href="#cb10-197"></a> } <span class="cf">else</span> {</span>
<span id="cb10-198"><a href="#cb10-198"></a> gtk_text_buffer_set_text (tb, contents, length);</span>
<span id="cb10-199"><a href="#cb10-199"></a> g_free (contents);</span>
<span id="cb10-200"><a href="#cb10-200"></a> <span class="cf">if</span> (G_IS_FILE (tv-&gt;file))</span>
<span id="cb10-201"><a href="#cb10-201"></a> g_object_unref (tv-&gt;file);</span>
<span id="cb10-202"><a href="#cb10-202"></a> tv-&gt;file = file;</span>
<span id="cb10-203"><a href="#cb10-203"></a> gtk_text_buffer_set_modified (tb, FALSE);</span>
<span id="cb10-204"><a href="#cb10-204"></a> g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], <span class="dv">0</span>, TFE_OPEN_RESPONSE_SUCCESS);</span>
<span id="cb10-205"><a href="#cb10-205"></a> g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], <span class="dv">0</span>);</span>
<span id="cb10-206"><a href="#cb10-206"></a> }</span>
<span id="cb10-207"><a href="#cb10-207"></a> gtk_window_destroy (GTK_WINDOW (dialog));</span>
<span id="cb10-208"><a href="#cb10-208"></a>}</span>
<span id="cb10-209"><a href="#cb10-209"></a></span>
<span id="cb10-210"><a href="#cb10-210"></a><span class="dt">void</span></span>
<span id="cb10-211"><a href="#cb10-211"></a>tfe_text_view_open (TfeTextView *tv, GtkWindow *win) {</span>
<span id="cb10-212"><a href="#cb10-212"></a> g_return_if_fail (TFE_IS_TEXT_VIEW (tv));</span>
<span id="cb10-213"><a href="#cb10-213"></a> g_return_if_fail (GTK_IS_WINDOW (win));</span>
<span id="cb10-214"><a href="#cb10-214"></a></span>
<span id="cb10-215"><a href="#cb10-215"></a> GtkWidget *dialog;</span>
<span id="cb10-216"><a href="#cb10-216"></a></span>
<span id="cb10-217"><a href="#cb10-217"></a> dialog = gtk_file_chooser_dialog_new (<span class="st">&quot;Open file&quot;</span>, win, GTK_FILE_CHOOSER_ACTION_OPEN,</span>
<span id="cb10-218"><a href="#cb10-218"></a> <span class="st">&quot;Cancel&quot;</span>, GTK_RESPONSE_CANCEL,</span>
<span id="cb10-219"><a href="#cb10-219"></a> <span class="st">&quot;Open&quot;</span>, GTK_RESPONSE_ACCEPT,</span>
<span id="cb10-220"><a href="#cb10-220"></a> NULL);</span>
<span id="cb10-221"><a href="#cb10-221"></a> g_signal_connect (dialog, <span class="st">&quot;response&quot;</span>, G_CALLBACK (open_dialog_response), tv);</span>
<span id="cb10-222"><a href="#cb10-222"></a> gtk_widget_show (dialog);</span>
<span id="cb10-223"><a href="#cb10-223"></a>}</span>
<span id="cb10-224"><a href="#cb10-224"></a></span>
<span id="cb10-225"><a href="#cb10-225"></a>GtkWidget *</span>
<span id="cb10-226"><a href="#cb10-226"></a>tfe_text_view_new (<span class="dt">void</span>) {</span>
<span id="cb10-227"><a href="#cb10-227"></a> <span class="cf">return</span> GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL));</span>
<span id="cb10-228"><a href="#cb10-228"></a>}</span></code></pre></div>
<h2 id="total-number-of-lines-words-and-characters">Total number of lines, words and characters</h2>
<pre><code>$ LANG=C wc tfe5/meson.build tfe5/tfeapplication.c tfe5/tfe.gresource.xml tfe5/tfe.h tfe5/tfenotebook.c tfe5/tfenotebook.h tfetextview/tfetextview.c tfetextview/tfetextview.h tfe5/tfe.ui
10 17 294 tfe5/meson.build 10 17 294 tfe5/meson.build
99 304 3205 tfe5/tfeapplication.c 101 308 3274 tfe5/tfeapplication.c
6 9 153 tfe5/tfe.gresource.xml 6 9 153 tfe5/tfe.gresource.xml
4 6 87 tfe5/tfe.h 145 387 3667 tfe5/tfenotebook.c
140 378 3601 tfe5/tfenotebook.c
15 21 241 tfe5/tfenotebook.h 15 21 241 tfe5/tfenotebook.h
229 671 8017 tfetextview/tfetextview.c 239 863 9264 tfetextview/tfetextview.c
35 60 701 tfetextview/tfetextview.h 35 60 701 tfetextview/tfetextview.h
61 100 2073 tfe5/tfe.ui 61 100 2073 tfe5/tfe.ui
599 1566 18372 total</code></pre> 612 1765 19667 total</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> <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> </body>

View file

@ -115,24 +115,21 @@
Linux distributions</h1> Linux distributions</h1>
<p>This section describes how to install GTK 4 into Linux <p>This section describes how to install GTK 4 into Linux
distributions.</p> distributions.</p>
<p>This tutorial is without any warranty. If you want to install GTK 4 <p>There are two ways to install GTK 4.</p>
to your computer, do it at your own risk.</p>
<p>The information in this section is the one on April/27/2022. The
words at present and/or now in this section means
April/27/2022.</p>
<p>There are three possible way to install GTK 4.</p>
<ul> <ul>
<li>Install it from the distribution packages.</li> <li>Install it from the distribution packages.</li>
<li>Build it from the source file.</li> <li>Build it from the source files.</li>
<li>Install a GNOME 40 distribution with the GNOME Boxes.</li>
</ul> </ul>
<h2 id="installation-from-the-distribution-packages">Installation from <h2 id="installation-from-the-distribution-packages">Installation from
the distribution packages</h2> the distribution packages</h2>
<p>The first way is easy to install. It is a recommended way. Ive <p>The first way is the best and easiest way to install it. Ive
installed GTK 4 packages in Ubuntu 21.04. (Now, my Ubuntu version is installed GTK 4 packages (version 4.8.1) in Ubuntu 22.10.</p>
21.10).</p> <pre><code>$ sudo apt install libgtk-4-dev</code></pre>
<pre><code>$ sudo apt-get install libgtk-4-bin libgtk-4-common libgtk-4-dev libgtk-4-doc</code></pre> <p>It is important to install the developing package (libgtk-4-dev). It
<p>Fedora, Arch, Debian and OpenSUSE are also possible. See <a includes C header files. Otherwise, you cant compile any GTK 4 based
programs.</p>
<p>Fedora, Debian, Arch, Gentoo and OpenSUSE also have GTK 4 packages.
Package information for Arch, Debian/Ubuntu and Fedra is described by <a
href="https://www.gtk.org/docs/installations/linux#installing-gtk-from-packages">Installing href="https://www.gtk.org/docs/installations/linux#installing-gtk-from-packages">Installing
GTK from packages</a>. The following table shows the distributions which GTK from packages</a>. The following table shows the distributions which
support GTK 4.</p> support GTK 4.</p>
@ -142,336 +139,54 @@ support GTK 4.</p>
<th style="text-align: center;">Distribution</th> <th style="text-align: center;">Distribution</th>
<th style="text-align: center;">version</th> <th style="text-align: center;">version</th>
<th style="text-align: center;">GTK 4</th> <th style="text-align: center;">GTK 4</th>
<th style="text-align: center;">GNOME 40</th> <th style="text-align: center;">GNOME</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr class="odd"> <tr class="odd">
<td style="text-align: center;">Fedora</td> <td style="text-align: center;">Fedora</td>
<td style="text-align: center;">36</td> <td style="text-align: center;">37</td>
<td style="text-align: center;">4.4.2</td> <td style="text-align: center;">4.8.2-2</td>
<td style="text-align: center;">GNOME 42</td> <td style="text-align: center;">GNOME 43</td>
</tr> </tr>
<tr class="even"> <tr class="even">
<td style="text-align: center;">Ubuntu</td> <td style="text-align: center;">Ubuntu</td>
<td style="text-align: center;">22.04lts</td> <td style="text-align: center;">22.10</td>
<td style="text-align: center;">4.4</td> <td style="text-align: center;">4.8.1</td>
<td style="text-align: center;">GNOME 41(4.6.2)</td> <td style="text-align: center;">GNOME 43.0</td>
</tr> </tr>
<tr class="odd"> <tr class="odd">
<td style="text-align: center;">Debian</td> <td style="text-align: center;">Debian</td>
<td style="text-align: center;">bookworm(testing)</td> <td style="text-align: center;">bookworm(testing)</td>
<td style="text-align: center;">4.6.5</td> <td style="text-align: center;">4.8.2</td>
<td style="text-align: center;">GNOME 42</td> <td style="text-align: center;">GNOME 43</td>
</tr> </tr>
<tr class="even"> <tr class="even">
<td style="text-align: center;">Arch</td> <td style="text-align: center;">Arch</td>
<td style="text-align: center;">rolling release</td> <td style="text-align: center;">rolling release</td>
<td style="text-align: center;">4.6.5</td> <td style="text-align: center;">4.8.2</td>
<td style="text-align: center;">GNOME 42</td> <td style="text-align: center;">GNOME 43</td>
</tr> </tr>
<tr class="odd"> <tr class="odd">
<td style="text-align: center;">Gentoo</td> <td style="text-align: center;">Gentoo</td>
<td style="text-align: center;">rolling release</td> <td style="text-align: center;">rolling release</td>
<td style="text-align: center;">4.6.5</td> <td style="text-align: center;">4.8.2</td>
<td style="text-align: center;">GNOME 42</td> <td style="text-align: center;">GNOME 43</td>
</tr> </tr>
<tr class="even"> <tr class="even">
<td style="text-align: center;">OpenSUSE</td> <td style="text-align: center;">OpenSUSE</td>
<td style="text-align: center;">Tumbleweed(rolling release)</td> <td style="text-align: center;">Tumbleweed(rolling release)</td>
<td style="text-align: center;">4.6.5</td> <td style="text-align: center;">4.8.2</td>
<td style="text-align: center;">GNOME 42</td> <td style="text-align: center;">GNOME 43</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<p>If youve installed GTK 4 from the packages, you dont need to read
the rest of this section.</p>
<h2 id="installation-from-the-source-file">Installation from the source <h2 id="installation-from-the-source-file">Installation from the source
file</h2> file</h2>
<p>If your operating system doesnt have GTK 4 packages, you need to <p>If you want to install a developing version of GTK 4, you need to
build it from the source. Or, if you want the latest version of GTK 4 build it from the source. See <a
(4.6.3), you also need to build it from the source.</p> href="https://docs.gtk.org/gtk4/building.html">Compiling the GTK
<p>I installed GTK 4 from the source in January 2021. So, the following Libraries</a> secion in the GTK 4 API reference.</p>
information is old, especially for the version of each software. For the
latest information, see <a
href="https://docs.gtk.org/gtk4/building.html">GTK API Reference,
Building GTK</a>.</p>
<h3 id="prerequisites-for-gtk-4-installation">Prerequisites for GTK 4
installation</h3>
<ul>
<li>Linux operating system. For example, Ubuntu 20.10 or 20.04LTS. Other
distributions might be OK.</li>
<li>Packages for development such as gcc, meson, ninja, git, wget and so
on.</li>
<li>Dev package is necessary for each software below.</li>
</ul>
<h3 id="installation-target">Installation target</h3>
<p>I installed GTK 4 under the directory <code>$HOME/local</code>. This
is a private user area.</p>
<p>If you want to install it in the system area, <code>/opt/gtk4</code>
is one of good choices. <a
href="https://docs.gtk.org/gtk4/building.html">GTK API Reference,
Building GTK</a> gives an installation example to
<code>/opt/gtk4</code>.</p>
<p>Dont install it to <code>/usr/local</code> which is the default. It
is used by Ubuntu applications which are not build on GTK 4. Therefore,
the risk is high and probably bad things will happen. Actually I did it
and I needed to reinstall Ubuntu.</p>
<h3 id="installation-to-ubuntu-20.10">Installation to Ubuntu 20.10</h3>
<p>Most of the necessary libraries are included by Ubuntu 20.10.
Therefore, they can be installed with <code>apt-get</code> command. You
dont need to install them from the source tarballs. You can skip the
subsections below about prerequisite library installation (GLib, Pango,
GdkPixbuf and GTK-doc).</p>
<h3 id="glib-installation">GLib installation</h3>
<p>If your Ubuntu is 20.04LTS, you need to install prerequisite
libraries from the tarballs. Check the version of your library and if it
is lower than the necessary version, install it from the source.</p>
<p>For example,</p>
<pre><code>$ pkg-config --modversion glib-2.0
2.64.6</code></pre>
<p>The necessary version is 2.66.0 or higher. Therefore, the example
above shows that you need to install GLib.</p>
<p>I installed 2.67.1 which was the latest version at that time (January
2021). Download GLib source files from the repository, then decompress
and extract files.</p>
<pre><code>$ wget https://download.gnome.org/sources/glib/2.67/glib-2.67.1.tar.xz
$ tar -Jxf glib-2.67.1.tar.xz</code></pre>
<p>Some packages are required to build GLib. You can find them if you
run meson.</p>
<pre><code>$ meson --prefix $HOME/local _build</code></pre>
<p>Use apt-get and install the prerequisites. For example,</p>
<pre><code>$ sudo apt-get install -y libpcre2-dev libffi-dev</code></pre>
<p>After that, compile GLib.</p>
<pre><code>$ rm -rf _build
$ meson --prefix $HOME/local _build
$ ninja -C _build
$ ninja -C _build install</code></pre>
<p>Set several environment variables so that the GLib libraries
installed can be used by build tools. Make a text file below and save it
as <code>env.sh</code></p>
<pre><code># compiler
CPPFLAGS=&quot;-I$HOME/local/include&quot;
LDFLAGS=&quot;-L$HOME/local/lib&quot;
PKG_CONFIG_PATH=&quot;$HOME/local/lib/pkgconfig:$HOME/local/lib/x86_64-linux-gnu/pkgconfig&quot;
export CPPFLAGS LDFLAGS PKG_CONFIG_PATH
# linker
LD_LIBRARY_PATH=&quot;$HOME/local/lib/x86_64-linux-gnu/&quot;
PATH=&quot;$HOME/local/bin:$PATH&quot;
export LD_LIBRARY_PATH PATH
# gsetting
export GSETTINGS_SCHEMA_DIR=$HOME/local/share/glib-2.0/schemas</code></pre>
<p>Then, use . (dot) or source command to include these commands to the
current bash.</p>
<pre><code>$ . env.sh</code></pre>
<p>or</p>
<pre><code>$ source env.sh</code></pre>
<p>This command carries out the commands in <code>env.sh</code> and
changes the environment variables above in the current shell.</p>
<h3 id="pango-installation">Pango installation</h3>
<p>Download and untar.</p>
<pre><code>$ wget https://download.gnome.org/sources/pango/1.48/pango-1.48.0.tar.xz
$ tar -Jxf pango-1.48.0.tar.xz</code></pre>
<p>Try meson and check the required packages. Install all the
prerequisites. Then, compile and install Pango.</p>
<pre><code>$ meson --prefix $HOME/local _build
$ ninja -C _build
$ ninja -C _build install</code></pre>
<p>It installs Pango-1.0.gir under
<code>$HOME/local/share/gir-1.0</code>. If you installed Pango without
<code>--prefix</code> option, then it would be located at
<code>/usr/local/share/gir-1.0</code>. This directory (/usr/local/share)
is used by applications. They find the directory by the environment
variable <code>XDG_DATA_DIRS</code>. It is a text file which keep the
list of share directories like <code>/usr/share</code>,
<code>usr/local/share</code> and so on. Now
<code>$HOME/local/share</code> needs to be added to
<code>XDG_DATA_DIRS</code>, or error will occur in the later
compilation.</p>
<pre><code>$ export XDG_DATA_DIRS=$HOME/local/share:$XDG_DATA_DIRS</code></pre>
<h3 id="gdkpixbuf-and-gtk-doc-installation">GdkPixbuf and GTK-Doc
installation</h3>
<p>Download and untar.</p>
<pre><code>$ wget https://download.gnome.org/sources/gdk-pixbuf/2.42/gdk-pixbuf-2.42.2.tar.xz
$ tar -Jxf gdk-pixbuf-2.42.2.tar.xz
$ wget https://download.gnome.org/sources/gtk-doc/1.33/gtk-doc-1.33.1.tar.xz
$ tar -Jxf gtk-doc-1.33.1.tar.xz</code></pre>
<p>Same as before, install prerequisite packages, then compile and
install them.</p>
<p>The installation of GTK-Doc put <code>gtk-doc.pc</code> under
<code>$HOME/local/share/pkgconfig</code>. This file is used by
pkg-config, which is one of the build tools. The directory needs to be
added to the environment variable <code>PKG_CONFIG_PATH</code></p>
<pre><code>$ export PKG_CONFIG_PATH=&quot;$HOME/local/share/pkgconfig:$PKG_CONFIG_PATH&quot;</code></pre>
<h3 id="gtk-4-installation">GTK 4 installation</h3>
<p>If you want the latest development version of GTK 4, use git and
clone the repository.</p>
<pre><code>$ git clone https://gitlab.gnome.org/gnome/gtk.git</code></pre>
<p>If you want a stable version of GTK 4, then download it from <a
href="https://download.gnome.org/sources/gtk/">GNOME source website</a>.
The latest version is 4.3.1 (13/June/2021).</p>
<p>Compile and install it.</p>
<pre><code>$ meson --prefix $HOME/local _build
$ ninja -C _build
$ ninja -C _build install</code></pre>
<p>If you want to know more information, refer to <a
href="https://docs.gtk.org/gtk4/building.html">GTK 4 API Reference,
Building GTK</a>.</p>
<h3 id="modify-env.sh">Modify env.sh</h3>
<p>Because environment variables disappear when you log out, you need to
add them again. Modify <code>env.sh</code>.</p>
<pre><code># compiler
CPPFLAGS=&quot;-I$HOME/local/include&quot;
LDFLAGS=&quot;-L$HOME/local/lib&quot;
PKG_CONFIG_PATH=&quot;$HOME/local/lib/pkgconfig:$HOME/local/lib/x86_64-linux-gnu/pkgconfig:
$HOME/local/share/pkgconfig&quot;
export CPPFLAGS LDFLAGS PKG_CONFIG_PATH
# linker
LD_LIBRARY_PATH=&quot;$HOME/local/lib/x86_64-linux-gnu/&quot;
PATH=&quot;$HOME/local/bin:$PATH&quot;
export LD_LIBRARY_PATH PATH
# gir
XDG_DATA_DIRS=$HOME/local/share:$XDG_DATA_DIRS
export XDG_DATA_DIRS
# gsetting
export GSETTINGS_SCHEMA_DIR=$HOME/local/share/glib-2.0/schemas
# girepository-1.0
export GI_TYPELIB_PATH=$HOME/local/lib/x86_64-linux-gnu/girepository-1.0</code></pre>
<p>Include this file by . (dot) command before using GTK 4
libraries.</p>
<p>You may think you can add them in your <code>.profile</code>. But
its a wrong decision. Never write them to your <code>.profile</code>.
The environment variables above are necessary only when you compile and
run GTK 4 applications. Otherwise its not necessary. If you changed the
environment variables above and run GTK 3 applications, it probably
causes serious damage.</p>
<h3 id="compiling-gtk-4-applications">Compiling GTK 4 applications</h3>
<p>Before you compile GTK 4 applications, define environment variables
above.</p>
<pre><code>$ . env.sh</code></pre>
<p>After that you can compile them without anything. For example, to
compile <code>sample.c</code>, type the following.</p>
<pre><code>$ gcc `pkg-config --cflags gtk4` sample.c `pkg-config --libs gtk4`</code></pre>
<p>To know how to compile GTK 4 applications, refer to the section 3
(GtkApplication and GtkApplicationWindow) and after.</p>
<h2 id="installing-fedora-34-with-gnome-boxes">Installing Fedora 34 with
GNOME Boxes</h2>
<p>The last part of this section is about GNOME 40 and GNOME Boxes.
GNOME 40 is a new version of GNOME desktop system. And GTK 4 is
installed in the distribution. See <a
href="https://forty.gnome.org/">GNOME 40 website</a> first.</p>
<p><em>However, GNOME 40 is not necessary to compile and run GTK 4
applications.</em></p>
<p>There are seven choices at present.</p>
<ul>
<li>GNOME OS</li>
<li>Arch Linux</li>
<li>Gentoo Linux</li>
<li>Fedora 36</li>
<li>openSUSE Tumbleweed</li>
<li>Ubuntu 22.04</li>
<li>Debian bookworm</li>
</ul>
<p>Ive installed Fedora 34 with GNOME Boxes. My OS was Ubuntu 21.04 at
that time. GNOME Boxes creates a virtual machine in Ubuntu and Fedora
will be installed to that virtual machine.</p>
<p>The instruction is as follows.</p>
<ol type="1">
<li>Download Fedora 34 iso file. There is an link at the end of <a
href="https://forty.gnome.org/">GNOME 40 website</a>.</li>
<li>Install gnome-boxes with apt-get command.</li>
</ol>
<pre><code>$ sudo apt-get install gnome-boxes</code></pre>
<ol start="3" type="1">
<li>Run GNOME Boxes.</li>
<li>Click on <code>+</code> button on the top left corner and launch a
box creation wizard by clicking
<code>Create a Virtual Machine ...</code>. Then a dialog appears. Click
on <code>Operationg System Image File</code> and select the iso file you
have downloaded.</li>
<li>Then, the Fedoras installer is executed. Follow the instructions by
the installer. At the end of the installation, the installer instructs
to reboot the system. Click on the right of the title bar and select
reboot or shutdown.</li>
<li>Your display is back to the initial window of GNOME Boxes, but there
is a button <code>Fedora 34 Workstation</code> on the upper left of the
window. Click on the button then Fedora will be executed.</li>
<li>A setup dialog appears. Setup Fedora according to the wizard.</li>
</ol>
<p>Now you can use Fedora. It includes GTK 4 libraries already. But you
need to install the GTK 4 development package. Use <code>dnf</code> to
install <code>gtk4.x86_64</code> package.</p>
<pre><code>$ sudo dnf install gtk4.x86_64</code></pre>
<h3 id="gtk-4-compilation-test">GTK 4 compilation test</h3>
<p>You can test the GTK 4 development packages by compiling files which
are based on GTK 4. Ive tried compiling <code>tfe</code> text editor,
which is written in section 21.</p>
<ol type="1">
<li>Run Firefox.</li>
<li>Open this website (<a
href="https://github.com/ToshioCP/Gtk4-tutorial">Gtk4-Tutorial</a>).</li>
<li>Click on the green button labeled <code>Code</code>.</li>
<li>Select <code>Download ZIP</code> and download the codes from the
repository.</li>
<li>Unzip the file.</li>
<li>Change your current directory to <code>src/tfe7</code>.</li>
<li>Compile it.</li>
</ol>
<pre><code>$ meson _build
bash: meson: command not found...
Install package &#39;meson&#39; to provide command &#39;meson&#39;? [N/y] y
* Waiting in queue...
The following packages have to be installed:
meson-0.56.2-2.fc34.noarch High productivity build system
ninja-build-1.10.2-2.fc34.x86_64 Small build system with a focus on speed
vim-filesystem-2:8.2.2787-1.fc34.noarch VIM filesystem layout
Proceed with changes? [N/y] y
... ...
... ...
The Meson build system
Version: 0.56.2
... ...
... ...
Project name: tfe
Project version: undefined
C compiler for the host machine: cc (gcc 11.0.0 &quot;cc (GCC) 11.0.0 20210210 (Red Hat 11.0.0-0)&quot;)
C linker for the host machine: cc ld.bfd 2.35.1-38
Host machine cpu family: x86_64
Host machine cpu: x86_64
Found pkg-config: /usr/bin/pkg-config (1.7.3)
Run-time dependency gtk4 found: YES 4.2.0
Found pkg-config: /usr/bin/pkg-config (1.7.3)
Program glib-compile-resources found: YES (/usr/bin/glib-compile-resources)
Program glib-compile-schemas found: YES (/usr/bin/glib-compile-schemas)
Program glib-compile-schemas found: YES (/usr/bin/glib-compile-schemas)
Build targets in project: 4
Found ninja-1.10.2 at /usr/bin/ninja
$ ninja -C _build
ninja: Entering directory `_build&#39;
[12/12] Linking target tfe
$ ninja -C _build install
ninja: Entering directory `_build&#39;
[0/1] Installing files.
Installing tfe to /usr/local/bin
Installation failed due to insufficient permissions.
Attempting to use polkit to gain elevated privileges...
Installing tfe to /usr/local/bin
Installing /home/&lt;username&gt;/Gtk4-tutorial-main/src/tfe7/com.github.ToshioCP.tfe.gschema.xml to /usr/local/share/glib-2.0/schemas
Running custom install script &#39;/usr/bin/glib-compile-schemas /usr/local/share/glib-2.0/schemas/&#39;</code></pre>
<ol start="8" type="1">
<li>Execute it.</li>
</ol>
<pre><code>$ tfe</code></pre>
<p>Then, the window of <code>tfe</code> text editor appears. The
compilation and execution have succeeded.</p>
</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> <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> </body>

View file

@ -116,9 +116,9 @@ GtkApplicationWindow</h1>
<h2 id="gtkapplication">GtkApplication</h2> <h2 id="gtkapplication">GtkApplication</h2>
<h3 id="gtkapplication-and-g_application_run">GtkApplication and <h3 id="gtkapplication-and-g_application_run">GtkApplication and
g_application_run</h3> g_application_run</h3>
<p>Usually people write programming code to make an application. What <p>People write programming code to make an application. What are
are applications? Applications are software that runs using libraries, applications? Applications are software that runs using libraries, which
which includes the OS, frameworks and so on. In GTK 4 programming, the includes the OS, frameworks and so on. In GTK 4 programming, the
GtkApplication is a program (or executable) that runs using Gtk GtkApplication is a program (or executable) that runs using Gtk
libraries.</p> libraries.</p>
<p>The basic way to write a GtkApplication is as follows.</p> <p>The basic way to write a GtkApplication is as follows.</p>
@ -136,24 +136,32 @@ class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span i
<span id="cb1-5"><a href="#cb1-5"></a> GtkApplication <span class="op">*</span>app<span class="op">;</span></span> <span id="cb1-5"><a href="#cb1-5"></a> GtkApplication <span class="op">*</span>app<span class="op">;</span></span>
<span id="cb1-6"><a href="#cb1-6"></a> <span class="dt">int</span> stat<span class="op">;</span></span> <span id="cb1-6"><a href="#cb1-6"></a> <span class="dt">int</span> stat<span class="op">;</span></span>
<span id="cb1-7"><a href="#cb1-7"></a></span> <span id="cb1-7"><a href="#cb1-7"></a></span>
<span id="cb1-8"><a href="#cb1-8"></a> app <span class="op">=</span> gtk_application_new <span class="op">(</span><span class="st">&quot;com.github.ToshioCP.pr1&quot;</span><span class="op">,</span> G_APPLICATION_FLAGS_NONE<span class="op">);</span></span> <span id="cb1-8"><a href="#cb1-8"></a> app <span class="op">=</span> gtk_application_new <span class="op">(</span><span class="st">&quot;com.github.ToshioCP.pr1&quot;</span><span class="op">,</span> G_APPLICATION_DEFAULT_FLAGS<span class="op">);</span></span>
<span id="cb1-9"><a href="#cb1-9"></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="cb1-9"><a href="#cb1-9"></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="cb1-10"><a href="#cb1-10"></a> g_object_unref <span class="op">(</span>app<span class="op">);</span></span> <span id="cb1-10"><a href="#cb1-10"></a> g_object_unref <span class="op">(</span>app<span class="op">);</span></span>
<span id="cb1-11"><a href="#cb1-11"></a> <span class="cf">return</span> stat<span class="op">;</span></span> <span id="cb1-11"><a href="#cb1-11"></a> <span class="cf">return</span> stat<span class="op">;</span></span>
<span id="cb1-12"><a href="#cb1-12"></a><span class="op">}</span></span></code></pre></div> <span id="cb1-12"><a href="#cb1-12"></a><span class="op">}</span></span></code></pre></div>
<p>The first line says that this program includes the header files of <p>The first line says that this program includes the header files of
the Gtk libraries. The function <code>main</code> above is a startup the Gtk libraries. The function <code>main</code> is a startup function
function in C language. The variable <code>app</code> is defined as a in C language. The variable <code>app</code> is defined as a pointer to
pointer to a GtkApplication instance. The function a GtkApplication instance. The function <code>gtk_application_new</code>
<code>gtk_application_new</code> creates a GtkApplication instance and creates a GtkApplication instance and returns a pointer to the instance.
returns a pointer to the instance. The GtkApplication instance is a C The GtkApplication instance is a C structure data in which the
structure data in which the information about the application is stored. information about the application is stored. The meaning of the
The meaning of the arguments will be explained later. The function arguments will be explained later. The function
<code>g_application_run</code> runs an application that the instance <code>g_application_run</code> runs an application that the instance
defined. (We often say that the function runs <code>app</code>. defined. (We often say that the function runs <code>app</code>.
Actually, <code>app</code> is not an application but a pointer to the Actually, <code>app</code> is not an application but a pointer to the
instance of the application. However, it is simple and short, and instance of the application. However, it is simple and short, and
probably no confusion occurs.)</p> probably no confusion occurs.)</p>
<p>Here I used the word <code>instance</code>. Instance, class and
object are terminologies in Object Oriented Programming. I use these
words in the same way. But, I will often use “object” instead of
“instance” in this tutorial. That means “object” and “instance” is the
same. Object is a bit ambiguous word. In a broad sense, object has wider
meaning than instance. So, readers should be careful of the contexts to
find the meaning of “object”. In many cases, object and instance are the
same.</p>
<p>To compile this, the following command needs to be run. The string <p>To compile this, the following command needs to be run. The string
<code>pr1.c</code> is the filename of the C source code above.</p> <code>pr1.c</code> is the filename of the C source code above.</p>
<pre><code>$ gcc `pkg-config --cflags gtk4` pr1.c `pkg-config --libs gtk4`</code></pre> <pre><code>$ gcc `pkg-config --cflags gtk4` pr1.c `pkg-config --libs gtk4`</code></pre>
@ -180,16 +188,17 @@ what this message means.</p>
explain that to you first.</p> explain that to you first.</p>
<p>A signal is emitted when something happens. For example, a window is <p>A signal is emitted when something happens. For example, a window is
created, a window is destroyed and so on. The signal “activate” is created, a window is destroyed and so on. The signal “activate” is
emitted when the application is activated, or started. If the signal is emitted when the application is activated. (Activated is a bit different
connected to a function, which is called a signal handler or simply from started, but you can think the both are almost same so far.) If the
handler, then the function is invoked when the signal emits.</p> signal is connected to a function, which is called a signal handler or
simply handler, then the function is invoked when the signal emits.</p>
<p>The flow is like this:</p> <p>The flow is like this:</p>
<ol type="1"> <ol type="1">
<li>Something happens.</li> <li>Something happens.</li>
<li>If its related to a certain signal, then the signal is <li>If its related to a certain signal, then the signal is
emitted.</li> emitted.</li>
<li>If the signal as been connected to a handler, then the handler is <li>If the signal has been connected to a handler in advance, then the
invoked.</li> handler is invoked.</li>
</ol> </ol>
<p>Signals are defined in objects. For example, the “activate” signal <p>Signals are defined in objects. For example, the “activate” signal
belongs to the GApplication object, which is a parent object of belongs to the GApplication object, which is a parent object of
@ -217,16 +226,18 @@ class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span i
<span id="cb5-10"><a href="#cb5-10"></a> GtkApplication <span class="op">*</span>app<span class="op">;</span></span> <span id="cb5-10"><a href="#cb5-10"></a> GtkApplication <span class="op">*</span>app<span class="op">;</span></span>
<span id="cb5-11"><a href="#cb5-11"></a> <span class="dt">int</span> stat<span class="op">;</span></span> <span id="cb5-11"><a href="#cb5-11"></a> <span class="dt">int</span> stat<span class="op">;</span></span>
<span id="cb5-12"><a href="#cb5-12"></a></span> <span id="cb5-12"><a href="#cb5-12"></a></span>
<span id="cb5-13"><a href="#cb5-13"></a> app <span class="op">=</span> gtk_application_new <span class="op">(</span><span class="st">&quot;com.github.ToshioCP.pr2&quot;</span><span class="op">,</span> G_APPLICATION_FLAGS_NONE<span class="op">);</span></span> <span id="cb5-13"><a href="#cb5-13"></a> app <span class="op">=</span> gtk_application_new <span class="op">(</span><span class="st">&quot;com.github.ToshioCP.pr2&quot;</span><span class="op">,</span> G_APPLICATION_DEFAULT_FLAGS<span class="op">);</span></span>
<span id="cb5-14"><a href="#cb5-14"></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="cb5-14"><a href="#cb5-14"></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="cb5-15"><a href="#cb5-15"></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="cb5-15"><a href="#cb5-15"></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="cb5-16"><a href="#cb5-16"></a> g_object_unref <span class="op">(</span>app<span class="op">);</span></span> <span id="cb5-16"><a href="#cb5-16"></a> g_object_unref <span class="op">(</span>app<span class="op">);</span></span>
<span id="cb5-17"><a href="#cb5-17"></a> <span class="cf">return</span> stat<span class="op">;</span></span> <span id="cb5-17"><a href="#cb5-17"></a> <span class="cf">return</span> stat<span class="op">;</span></span>
<span id="cb5-18"><a href="#cb5-18"></a><span class="op">}</span></span></code></pre></div> <span id="cb5-18"><a href="#cb5-18"></a><span class="op">}</span></span></code></pre></div>
<p>First, we define the handler <code>app_activate</code> which simply <p>First, we define the handler <code>app_activate</code> which simply
displays a message. In the function <code>main</code>, we add displays a message. The function <code>g_print</code> is defined in GLib
<code>g_signal_connect</code> before <code>g_application_run</code>. The and its like a printf in the C standard library. In the function
function <code>g_signal_connect</code> has four arguments.</p> <code>main</code>, we add <code>g_signal_connect</code> before
<code>g_application_run</code>. The function
<code>g_signal_connect</code> has four arguments.</p>
<ol type="1"> <ol type="1">
<li>An instance to which the signal belongs.</li> <li>An instance to which the signal belongs.</li>
<li>The name of the signal.</li> <li>The name of the signal.</li>
@ -235,15 +246,36 @@ by <code>G_CALLBACK</code>.</li>
<li>Data to pass to the handler. If no data is necessary, NULL should be <li>Data to pass to the handler. If no data is necessary, NULL should be
given.</li> given.</li>
</ol> </ol>
<p>You can find the description of each signal in the API reference <p>It is described in the <a
manual. For example, “activate” signal is in GApplication section in <a
href="https://docs.gtk.org/gio/signal.Application.activate.html">GIO API
Reference</a>. The handler function is described in it.</p>
<p>In addition, <code>g_signal_connect</code> is described in <a
href="https://docs.gtk.org/gobject/func.signal_connect.html">GObject API href="https://docs.gtk.org/gobject/func.signal_connect.html">GObject API
Reference</a>. API reference manual is very important. You should see Reference</a>. Correctly, <code>g_signal_connect</code> is a macro (not
and understand it to write Gtk applications. They are located in <a a C function).</p>
href="https://docs.gtk.org/">GTK Documentation</a>.</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" tabindex="-1"></a><span class="pp">#define g_signal_connect (</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a> instance<span class="op">,</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a> detailed_signal<span class="op">,</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a> c_handler<span class="op">,</span></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a> data</span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a><span class="op">)</span></span></code></pre></div>
<p>You can find the description of each signal in the API reference
manual. For example, “activate” signal is in <a
href="https://docs.gtk.org/gio/signal.Application.activate.html">GApplication
section</a> in the GIO API Reference.</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><span class="dt">void</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>activate <span class="op">(</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a> GApplication<span class="op">*</span> self<span class="op">,</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a> gpointer user_data</span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a><span class="op">)</span></span></code></pre></div>
<p>This is a declaration of the “activate” signal handler. You can use
any name instead of “activate” in the declaration above. The parameters
are:</p>
<ul>
<li>self is an instance to which the signal belongs.</li>
<li>user_data is a data defined in the fourth argument of the
<code>g_signal_connect</code> function. If it is NULL, then you can
ignore and left out the second parameter.</li>
</ul>
<p>API reference manual is very important. You should see and understand
it.</p>
<p>Lets compile the source file above (<code>pr2.c</code>) and run <p>Lets compile the source file above (<code>pr2.c</code>) and run
it.</p> it.</p>
<pre><code>$ gcc `pkg-config --cflags gtk4` pr2.c `pkg-config --libs gtk4` <pre><code>$ gcc `pkg-config --cflags gtk4` pr2.c `pkg-config --libs gtk4`
@ -285,15 +317,15 @@ program. What we need to do is:</p>
</ol> </ol>
<p>Now rewrite the function <code>app_activate</code>.</p> <p>Now rewrite the function <code>app_activate</code>.</p>
<h4 id="create-a-gtkwindow">Create a GtkWindow</h4> <h4 id="create-a-gtkwindow">Create a GtkWindow</h4>
<div class="sourceCode" id="cb10"><pre <div class="sourceCode" id="cb12"><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> class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb12-1"><a href="#cb12-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb10-2"><a href="#cb10-2"></a>app_activate <span class="op">(</span>GApplication <span class="op">*</span>app<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span> <span id="cb12-2"><a href="#cb12-2"></a>app_activate <span class="op">(</span>GApplication <span class="op">*</span>app<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></span> <span id="cb12-3"><a href="#cb12-3"></a> GtkWidget <span class="op">*</span>win<span class="op">;</span></span>
<span id="cb10-4"><a href="#cb10-4"></a></span> <span id="cb12-4"><a href="#cb12-4"></a></span>
<span id="cb10-5"><a href="#cb10-5"></a> win <span class="op">=</span> gtk_window_new <span class="op">();</span></span> <span id="cb12-5"><a href="#cb12-5"></a> win <span class="op">=</span> gtk_window_new <span class="op">();</span></span>
<span id="cb10-6"><a href="#cb10-6"></a> gtk_window_set_application <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> GTK_APPLICATION <span class="op">(</span>app<span class="op">));</span></span> <span id="cb12-6"><a href="#cb12-6"></a> gtk_window_set_application <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> GTK_APPLICATION <span class="op">(</span>app<span class="op">));</span></span>
<span id="cb10-7"><a href="#cb10-7"></a> gtk_widget_show <span class="op">(</span>win<span class="op">);</span></span> <span id="cb12-7"><a href="#cb12-7"></a> gtk_widget_show <span class="op">(</span>win<span class="op">);</span></span>
<span id="cb10-8"><a href="#cb10-8"></a><span class="op">}</span></span></code></pre></div> <span id="cb12-8"><a href="#cb12-8"></a><span class="op">}</span></span></code></pre></div>
<p>Widget is an abstract concept that includes all the GUI interfaces <p>Widget is an abstract concept that includes all the GUI interfaces
such as windows, dialogs, buttons, multi-line text, containers and so such as windows, dialogs, buttons, multi-line text, containers and so
on. And GtkWidget is a base object from which all the GUI objects on. And GtkWidget is a base object from which all the GUI objects
@ -306,28 +338,29 @@ GtkWidget -- GtkWindow</code></pre>
<figcaption aria-hidden="true">GtkWindow and GtkWidget</figcaption> <figcaption aria-hidden="true">GtkWindow and GtkWidget</figcaption>
</figure> </figure>
<p>The function <code>gtk_window_new</code> is defined as follows.</p> <p>The function <code>gtk_window_new</code> is defined as follows.</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" tabindex="-1"></a>GtkWidget <span class="op">*</span></span> <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>GtkWidget <span class="op">*</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a>gtk_window_new <span class="op">(</span><span class="dt">void</span><span class="op">);</span></span></code></pre></div> <span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a>gtk_window_new <span class="op">(</span><span class="dt">void</span><span class="op">);</span></span></code></pre></div>
<p>By this definition, it returns a pointer to GtkWidget, not GtkWindow. <p>By this definition, it returns a pointer to GtkWidget, not GtkWindow.
It actually creates a new GtkWindow instance (not GtkWidget) but returns It actually creates a new GtkWindow instance (not GtkWidget) but returns
a pointer to GtkWidget. However,the pointer points the GtkWidget and at a pointer to GtkWidget. However,the pointer points the GtkWidget and at
the same time it also points GtkWindow that contains GtkWidget in the same time it also points GtkWindow that contains GtkWidget in
it.</p> it.</p>
<p>If you want to use <code>win</code> as a pointer to the GtkWindow, <p>If you want to use <code>win</code> as a pointer to a GtkWindow type
you need to cast it.</p> instance, you need to cast it.</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" tabindex="-1"></a><span class="op">(</span>GtkWindow <span class="op">*)</span> win</span></code></pre></div> <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><span class="op">(</span>GtkWindow <span class="op">*)</span> win</span></code></pre></div>
<p>Or you can use <code>GTK_WINDOW</code> macro that performs a similar <p>It works, but isnt usually used. Instead, <code>GTK_WINDOW</code>
function.</p> macro is used.</p>
<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>GTK_WINDOW <span class="op">(</span>win<span class="op">)</span></span></code></pre></div> <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>GTK_WINDOW <span class="op">(</span>win<span class="op">)</span></span></code></pre></div>
<p>This is a recommended way.</p> <p>The macro is recommended because it does not only cast but also check
the type.</p>
<h4 id="connect-it-to-gtkapplication.">Connect it to <h4 id="connect-it-to-gtkapplication.">Connect it to
GtkApplication.</h4> GtkApplication.</h4>
<p>The function <code>gtk_window_set_application</code> is used to <p>The function <code>gtk_window_set_application</code> is used to
connect GtkWindow to GtkApplication.</p> connect GtkWindow to GtkApplication.</p>
<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>gtk_window_set_application <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> GTK_APPLICATION <span class="op">(</span>app<span class="op">));</span></span></code></pre></div> <div class="sourceCode" id="cb17"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a>gtk_window_set_application <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> GTK_APPLICATION <span class="op">(</span>app<span class="op">));</span></span></code></pre></div>
<p>You need to cast <code>win</code> to GtkWindow and <code>app</code> <p>You need to cast <code>win</code> to GtkWindow and <code>app</code>
to GtkApplication. <code>GTK_WINDOW</code> and to GtkApplication with <code>GTK_WINDOW</code> and
<code>GTK_APPLICATION</code> macro is appropriate for that.</p> <code>GTK_APPLICATION</code> macro.</p>
<p>GtkApplication continues to run until the related window is <p>GtkApplication continues to run until the related window is
destroyed. If you didnt connect GtkWindow and GtkApplication, destroyed. If you didnt connect GtkWindow and GtkApplication,
GtkApplication destroys itself immediately. Because no window is GtkApplication destroys itself immediately. Because no window is
@ -340,7 +373,7 @@ window.</p>
doesnt need this function to show itself. But, theres an exception. doesnt need this function to show itself. But, theres an exception.
Top window (this term will be explained later) isnt visible when it is Top window (this term will be explained later) isnt visible when it is
created. So you need to use the function above to show the window.</p> created. So you need to use the function above to show the window.</p>
<p>Save the program as <code>pr3.c</code> and compile and run it.</p> <p>Save the program as <code>pr3.c</code>, then compile and run it.</p>
<pre><code>$ comp pr3 <pre><code>$ comp pr3
$ ./a.out</code></pre> $ ./a.out</code></pre>
<p>A small window appears.</p> <p>A small window appears.</p>
@ -352,20 +385,20 @@ $ ./a.out</code></pre>
finishes.</p> finishes.</p>
<h3 id="gtkapplicationwindow">GtkApplicationWindow</h3> <h3 id="gtkapplicationwindow">GtkApplicationWindow</h3>
<p>GtkApplicationWindow is a child object of GtkWindow. It has some <p>GtkApplicationWindow is a child object of GtkWindow. It has some
extra functionality for better integration with GtkApplication. It is extra feature for better integration with GtkApplication. It is
recommended to use it instead of GtkWindow when you use recommended to use it as the top-level window of the application instead
GtkApplication.</p> of GtkWindow.</p>
<p>Now rewrite the program and use GtkApplicationWindow.</p> <p>Now rewrite the program and use GtkApplicationWindow.</p>
<div class="sourceCode" id="cb17"><pre <div class="sourceCode" id="cb19"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb17-1"><a href="#cb17-1"></a><span class="dt">static</span> <span class="dt">void</span></span> class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb19-1"><a href="#cb19-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb17-2"><a href="#cb17-2"></a>app_activate <span class="op">(</span>GApplication <span class="op">*</span>app<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span> <span id="cb19-2"><a href="#cb19-2"></a>app_activate <span class="op">(</span>GApplication <span class="op">*</span>app<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span>
<span id="cb17-3"><a href="#cb17-3"></a> GtkWidget <span class="op">*</span>win<span class="op">;</span></span> <span id="cb19-3"><a href="#cb19-3"></a> GtkWidget <span class="op">*</span>win<span class="op">;</span></span>
<span id="cb17-4"><a href="#cb17-4"></a></span> <span id="cb19-4"><a href="#cb19-4"></a></span>
<span id="cb17-5"><a href="#cb17-5"></a> win <span class="op">=</span> gtk_application_window_new <span class="op">(</span>GTK_APPLICATION <span class="op">(</span>app<span class="op">));</span></span> <span id="cb19-5"><a href="#cb19-5"></a> win <span class="op">=</span> gtk_application_window_new <span class="op">(</span>GTK_APPLICATION <span class="op">(</span>app<span class="op">));</span></span>
<span id="cb17-6"><a href="#cb17-6"></a> gtk_window_set_title <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="st">&quot;pr4&quot;</span><span class="op">);</span></span> <span id="cb19-6"><a href="#cb19-6"></a> gtk_window_set_title <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="st">&quot;pr4&quot;</span><span class="op">);</span></span>
<span id="cb17-7"><a href="#cb17-7"></a> gtk_window_set_default_size <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="dv">400</span><span class="op">,</span> <span class="dv">300</span><span class="op">);</span></span> <span id="cb19-7"><a href="#cb19-7"></a> gtk_window_set_default_size <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="dv">400</span><span class="op">,</span> <span class="dv">300</span><span class="op">);</span></span>
<span id="cb17-8"><a href="#cb17-8"></a> gtk_widget_show <span class="op">(</span>win<span class="op">);</span></span> <span id="cb19-8"><a href="#cb19-8"></a> gtk_widget_show <span class="op">(</span>win<span class="op">);</span></span>
<span id="cb17-9"><a href="#cb17-9"></a><span class="op">}</span></span></code></pre></div> <span id="cb19-9"><a href="#cb19-9"></a><span class="op">}</span></span></code></pre></div>
<p>When you create GtkApplicationWindow, you need to give GtkApplication <p>When you create GtkApplicationWindow, you need to give GtkApplication
instance as an argument. Then it automatically connect these two instance as an argument. Then it automatically connect these two
instances. So you dont need to call instances. So you dont need to call

View file

@ -5,7 +5,7 @@
<meta name="generator" content="pandoc" /> <meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <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"> <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> <title>GTK 4 tutorial</title>
<style> <style>
code{white-space: pre-wrap;} code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;} span.smallcaps{font-variant: small-caps;}
@ -112,46 +112,59 @@
</div> </div>
</nav> </nav>
<h1 id="widgets-1">Widgets (1)</h1> <h1 id="widgets-1">Widgets (1)</h1>
<h2 id="gtklabel-gtkbutton-and-gtkbox">GtkLabel, GtkButton and GtkBox</h2> <h2 id="gtklabel-gtkbutton-and-gtkbox">GtkLabel, GtkButton and
GtkBox</h2>
<h3 id="gtklabel">GtkLabel</h3> <h3 id="gtklabel">GtkLabel</h3>
<p>In the previous section we made a window and displayed it on the screen. Now we go on to the next topic, where we add widgets to this window. The simplest widget is GtkLabel. It is a widget with text in it.</p> <p>In the previous section we made a window and displayed it on the
<div class="sourceCode" id="cb1"><pre class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1"></a><span class="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span> screen. Now we go on to the next topic, where we add widgets to this
window. The simplest widget is GtkLabel. It is a widget with text in
it.</p>
<div class="sourceCode" id="cb1"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1"></a><span class="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span>
<span id="cb1-2"><a href="#cb1-2"></a></span> <span id="cb1-2"><a href="#cb1-2"></a></span>
<span id="cb1-3"><a href="#cb1-3"></a><span class="dt">static</span> <span class="dt">void</span></span> <span id="cb1-3"><a href="#cb1-3"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb1-4"><a href="#cb1-4"></a>app_activate (GApplication *app, gpointer user_data) {</span> <span id="cb1-4"><a href="#cb1-4"></a>app_activate <span class="op">(</span>GApplication <span class="op">*</span>app<span class="op">)</span> <span class="op">{</span></span>
<span id="cb1-5"><a href="#cb1-5"></a> GtkWidget *win;</span> <span id="cb1-5"><a href="#cb1-5"></a> GtkWidget <span class="op">*</span>win<span class="op">;</span></span>
<span id="cb1-6"><a href="#cb1-6"></a> GtkWidget *lab;</span> <span id="cb1-6"><a href="#cb1-6"></a> GtkWidget <span class="op">*</span>lab<span class="op">;</span></span>
<span id="cb1-7"><a href="#cb1-7"></a></span> <span id="cb1-7"><a href="#cb1-7"></a></span>
<span id="cb1-8"><a href="#cb1-8"></a> win = gtk_application_window_new (GTK_APPLICATION (app));</span> <span id="cb1-8"><a href="#cb1-8"></a> win <span class="op">=</span> gtk_application_window_new <span class="op">(</span>GTK_APPLICATION <span class="op">(</span>app<span class="op">));</span></span>
<span id="cb1-9"><a href="#cb1-9"></a> gtk_window_set_title (GTK_WINDOW (win), <span class="st">&quot;lb1&quot;</span>);</span> <span id="cb1-9"><a href="#cb1-9"></a> gtk_window_set_title <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="st">&quot;lb1&quot;</span><span class="op">);</span></span>
<span id="cb1-10"><a href="#cb1-10"></a> gtk_window_set_default_size (GTK_WINDOW (win), <span class="dv">400</span>, <span class="dv">300</span>);</span> <span id="cb1-10"><a href="#cb1-10"></a> gtk_window_set_default_size <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="dv">400</span><span class="op">,</span> <span class="dv">300</span><span class="op">);</span></span>
<span id="cb1-11"><a href="#cb1-11"></a></span> <span id="cb1-11"><a href="#cb1-11"></a></span>
<span id="cb1-12"><a href="#cb1-12"></a> lab = gtk_label_new (<span class="st">&quot;Hello.&quot;</span>);</span> <span id="cb1-12"><a href="#cb1-12"></a> lab <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="cb1-13"><a href="#cb1-13"></a> gtk_window_set_child (GTK_WINDOW (win), lab);</span> <span id="cb1-13"><a href="#cb1-13"></a> gtk_window_set_child <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> lab<span class="op">);</span></span>
<span id="cb1-14"><a href="#cb1-14"></a></span> <span id="cb1-14"><a href="#cb1-14"></a></span>
<span id="cb1-15"><a href="#cb1-15"></a> gtk_widget_show (win);</span> <span id="cb1-15"><a href="#cb1-15"></a> gtk_widget_show <span class="op">(</span>win<span class="op">);</span></span>
<span id="cb1-16"><a href="#cb1-16"></a>}</span> <span id="cb1-16"><a href="#cb1-16"></a><span class="op">}</span></span>
<span id="cb1-17"><a href="#cb1-17"></a></span> <span id="cb1-17"><a href="#cb1-17"></a></span>
<span id="cb1-18"><a href="#cb1-18"></a><span class="dt">int</span></span> <span id="cb1-18"><a href="#cb1-18"></a><span class="dt">int</span></span>
<span id="cb1-19"><a href="#cb1-19"></a>main (<span class="dt">int</span> argc, <span class="dt">char</span> **argv) {</span> <span id="cb1-19"><a href="#cb1-19"></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="cb1-20"><a href="#cb1-20"></a> GtkApplication *app;</span> <span id="cb1-20"><a href="#cb1-20"></a> GtkApplication <span class="op">*</span>app<span class="op">;</span></span>
<span id="cb1-21"><a href="#cb1-21"></a> <span class="dt">int</span> stat;</span> <span id="cb1-21"><a href="#cb1-21"></a> <span class="dt">int</span> stat<span class="op">;</span></span>
<span id="cb1-22"><a href="#cb1-22"></a></span> <span id="cb1-22"><a href="#cb1-22"></a></span>
<span id="cb1-23"><a href="#cb1-23"></a> app = gtk_application_new (<span class="st">&quot;com.github.ToshioCP.lb1&quot;</span>, G_APPLICATION_FLAGS_NONE);</span> <span id="cb1-23"><a href="#cb1-23"></a> app <span class="op">=</span> gtk_application_new <span class="op">(</span><span class="st">&quot;com.github.ToshioCP.lb1&quot;</span><span class="op">,</span> G_APPLICATION_DEFAULT_FLAGS<span class="op">);</span></span>
<span id="cb1-24"><a href="#cb1-24"></a> g_signal_connect (app, <span class="st">&quot;activate&quot;</span>, G_CALLBACK (app_activate), NULL);</span> <span id="cb1-24"><a href="#cb1-24"></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="cb1-25"><a href="#cb1-25"></a> stat =g_application_run (G_APPLICATION (app), argc, argv);</span> <span id="cb1-25"><a href="#cb1-25"></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="cb1-26"><a href="#cb1-26"></a> g_object_unref (app);</span> <span id="cb1-26"><a href="#cb1-26"></a> g_object_unref <span class="op">(</span>app<span class="op">);</span></span>
<span id="cb1-27"><a href="#cb1-27"></a> <span class="cf">return</span> stat;</span> <span id="cb1-27"><a href="#cb1-27"></a> <span class="cf">return</span> stat<span class="op">;</span></span>
<span id="cb1-28"><a href="#cb1-28"></a>}</span></code></pre></div> <span id="cb1-28"><a href="#cb1-28"></a><span class="op">}</span></span></code></pre></div>
<p>Save this program to a file <code>lb1.c</code>. Then compile and run it.</p> <p>Save this program to a file <code>lb1.c</code>. Then compile and run
it.</p>
<pre><code>$ comp lb1 <pre><code>$ comp lb1
$ ./a.out</code></pre> $ ./a.out</code></pre>
<p>A window with a message “Hello.” appears.</p> <p>A window with a message “Hello.” appears.</p>
<figure> <figure>
<img src="image/screenshot_lb1.png" alt="" /><figcaption>Screenshot of the label</figcaption> <img src="image/screenshot_lb1.png" alt="Screenshot of the label" />
<figcaption aria-hidden="true">Screenshot of the label</figcaption>
</figure> </figure>
<p>Theres only a little change between <code>pr4.c</code> and <code>lb1.c</code>. A program <code>diff</code> is good to know the difference between two files.</p> <p>Theres only a little change between <code>pr4.c</code> and
<code>lb1.c</code>. A program <code>diff</code> is good to know the
difference between two files.</p>
<pre><code>$ cd misc; diff pr4.c lb1.c <pre><code>$ cd misc; diff pr4.c lb1.c
4c4
&lt; app_activate (GApplication *app, gpointer user_data) {
---
&gt; app_activate (GApplication *app) {
5a6 5a6
&gt; GtkWidget *lab; &gt; GtkWidget *lab;
8c9 8c9
@ -164,193 +177,290 @@ $ ./a.out</code></pre>
&gt; gtk_window_set_child (GTK_WINDOW (win), lab); &gt; gtk_window_set_child (GTK_WINDOW (win), lab);
&gt; &gt;
18c23 18c23
&lt; app = gtk_application_new (&quot;com.github.ToshioCP.pr4&quot;, G_APPLICATION_FLAGS_NONE); &lt; app = gtk_application_new (&quot;com.github.ToshioCP.pr4&quot;, G_APPLICATION_DEFAULT_FLAGS);
--- ---
&gt; app = gtk_application_new (&quot;com.github.ToshioCP.lb1&quot;, G_APPLICATION_FLAGS_NONE);</code></pre> &gt; app = gtk_application_new (&quot;com.github.ToshioCP.lb1&quot;, G_APPLICATION_DEFAULT_FLAGS);</code></pre>
<p>This tells us:</p> <p>This tells us:</p>
<ul> <ul>
<li>A signal handler <code>app_activate</code> doesnt have
<code>user_data</code> parameter. If the fourth argument of
<code>g_signal_connect</code> is NULL, you can leave out
<code>user_data</code>.</li>
<li>The definition of a new variable <code>lab</code> is added.</li> <li>The definition of a new variable <code>lab</code> is added.</li>
<li>The title of the window is changed.</li> <li>The title of the window is changed.</li>
<li>A label is created and connected to the window as a child.</li> <li>A label is created and connected to the window as a child.</li>
</ul> </ul>
<p>The function <code>gtk_window_set_child (GTK_WINDOW (win), lab)</code> makes the label <code>lab</code> a child widget of the window <code>win</code>. Be careful. A child widget is different from a child object. Objects have parent-child relationships and widgets also have parent-child relationships. But these two relationships are totally different. Dont be confused. In the program <code>lb1.c</code>, <code>lab</code> is a child widget of <code>win</code>. Child widgets are always located in their parent widget on the screen. See how the window has appeared on the screen. The application window includes the label.</p> <p>The function
<p>The window <code>win</code> doesnt have any parents. We call such a window top-level window. An application can have more than one top-level window.</p> <code>gtk_window_set_child (GTK_WINDOW (win), lab)</code> makes the
label <code>lab</code> a child widget of the window <code>win</code>. Be
careful. A child widget is different from a child object. Objects have
parent-child relationships and widgets also have parent-child
relationships. But these two relationships are totally different. Dont
be confused. In the program <code>lb1.c</code>, <code>lab</code> is a
child widget of <code>win</code>. Child widgets are always located in
their parent widget on the screen. See how the window has appeared on
the screen. The application window includes the label.</p>
<p>The window <code>win</code> doesnt have any parents. We call such a
window top-level window. An application can have more than one top-level
window.</p>
<h3 id="gtkbutton">GtkButton</h3> <h3 id="gtkbutton">GtkButton</h3>
<p>The next widget to introduce is GtkButton. It displays a button on the screen with a label or icon on it. In this subsection, we will make a button with a label. When the button is clicked, it emits a “clicked” signal. The following program shows how to catch the signal to then do something.</p> <p>The next widget is GtkButton. It displays a button on the screen with
<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="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span> a label or icon on it. In this subsection, we will make a button with a
label. When the button is clicked, it emits a “clicked” signal. The
following program shows how to catch the signal and do something.</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="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span>
<span id="cb4-2"><a href="#cb4-2"></a></span> <span id="cb4-2"><a href="#cb4-2"></a></span>
<span id="cb4-3"><a href="#cb4-3"></a><span class="dt">static</span> <span class="dt">void</span></span> <span id="cb4-3"><a href="#cb4-3"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb4-4"><a href="#cb4-4"></a>click_cb (GtkButton *btn, gpointer user_data) {</span> <span id="cb4-4"><a href="#cb4-4"></a>click_cb <span class="op">(</span>GtkButton <span class="op">*</span>btn<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-5"><a href="#cb4-5"></a> g_print (<span class="st">&quot;Clicked.</span><span class="sc">\n</span><span class="st">&quot;</span>);</span> <span id="cb4-5"><a href="#cb4-5"></a> g_print <span class="op">(</span><span class="st">&quot;Clicked.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb4-6"><a href="#cb4-6"></a>}</span> <span id="cb4-6"><a href="#cb4-6"></a><span class="op">}</span></span>
<span id="cb4-7"><a href="#cb4-7"></a></span> <span id="cb4-7"><a href="#cb4-7"></a></span>
<span id="cb4-8"><a href="#cb4-8"></a><span class="dt">static</span> <span class="dt">void</span></span> <span id="cb4-8"><a href="#cb4-8"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb4-9"><a href="#cb4-9"></a>app_activate (GApplication *app, gpointer user_data) {</span> <span id="cb4-9"><a href="#cb4-9"></a>app_activate <span class="op">(</span>GApplication <span class="op">*</span>app<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-10"><a href="#cb4-10"></a> GtkWidget *win;</span> <span id="cb4-10"><a href="#cb4-10"></a> GtkWidget <span class="op">*</span>win<span class="op">;</span></span>
<span id="cb4-11"><a href="#cb4-11"></a> GtkWidget *btn;</span> <span id="cb4-11"><a href="#cb4-11"></a> GtkWidget <span class="op">*</span>btn<span class="op">;</span></span>
<span id="cb4-12"><a href="#cb4-12"></a></span> <span id="cb4-12"><a href="#cb4-12"></a></span>
<span id="cb4-13"><a href="#cb4-13"></a> win = gtk_application_window_new (GTK_APPLICATION (app));</span> <span id="cb4-13"><a href="#cb4-13"></a> win <span class="op">=</span> gtk_application_window_new <span class="op">(</span>GTK_APPLICATION <span class="op">(</span>app<span class="op">));</span></span>
<span id="cb4-14"><a href="#cb4-14"></a> gtk_window_set_title (GTK_WINDOW (win), <span class="st">&quot;lb2&quot;</span>);</span> <span id="cb4-14"><a href="#cb4-14"></a> gtk_window_set_title <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="st">&quot;lb2&quot;</span><span class="op">);</span></span>
<span id="cb4-15"><a href="#cb4-15"></a> gtk_window_set_default_size (GTK_WINDOW (win), <span class="dv">400</span>, <span class="dv">300</span>);</span> <span id="cb4-15"><a href="#cb4-15"></a> gtk_window_set_default_size <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="dv">400</span><span class="op">,</span> <span class="dv">300</span><span class="op">);</span></span>
<span id="cb4-16"><a href="#cb4-16"></a></span> <span id="cb4-16"><a href="#cb4-16"></a></span>
<span id="cb4-17"><a href="#cb4-17"></a> btn = gtk_button_new_with_label (<span class="st">&quot;Click me&quot;</span>);</span> <span id="cb4-17"><a href="#cb4-17"></a> btn <span class="op">=</span> gtk_button_new_with_label <span class="op">(</span><span class="st">&quot;Click me&quot;</span><span class="op">);</span></span>
<span id="cb4-18"><a href="#cb4-18"></a> gtk_window_set_child (GTK_WINDOW (win), btn);</span> <span id="cb4-18"><a href="#cb4-18"></a> gtk_window_set_child <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> btn<span class="op">);</span></span>
<span id="cb4-19"><a href="#cb4-19"></a> g_signal_connect (btn, <span class="st">&quot;clicked&quot;</span>, G_CALLBACK (click_cb), NULL);</span> <span id="cb4-19"><a href="#cb4-19"></a> g_signal_connect <span class="op">(</span>btn<span class="op">,</span> <span class="st">&quot;clicked&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>click_cb<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb4-20"><a href="#cb4-20"></a></span> <span id="cb4-20"><a href="#cb4-20"></a></span>
<span id="cb4-21"><a href="#cb4-21"></a> gtk_widget_show (win);</span> <span id="cb4-21"><a href="#cb4-21"></a> gtk_widget_show <span class="op">(</span>win<span class="op">);</span></span>
<span id="cb4-22"><a href="#cb4-22"></a>}</span> <span id="cb4-22"><a href="#cb4-22"></a><span class="op">}</span></span>
<span id="cb4-23"><a href="#cb4-23"></a></span> <span id="cb4-23"><a href="#cb4-23"></a></span>
<span id="cb4-24"><a href="#cb4-24"></a><span class="dt">int</span></span> <span id="cb4-24"><a href="#cb4-24"></a><span class="dt">int</span></span>
<span id="cb4-25"><a href="#cb4-25"></a>main (<span class="dt">int</span> argc, <span class="dt">char</span> **argv) {</span> <span id="cb4-25"><a href="#cb4-25"></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="cb4-26"><a href="#cb4-26"></a> GtkApplication *app;</span> <span id="cb4-26"><a href="#cb4-26"></a> GtkApplication <span class="op">*</span>app<span class="op">;</span></span>
<span id="cb4-27"><a href="#cb4-27"></a> <span class="dt">int</span> stat;</span> <span id="cb4-27"><a href="#cb4-27"></a> <span class="dt">int</span> stat<span class="op">;</span></span>
<span id="cb4-28"><a href="#cb4-28"></a></span> <span id="cb4-28"><a href="#cb4-28"></a></span>
<span id="cb4-29"><a href="#cb4-29"></a> app = gtk_application_new (<span class="st">&quot;com.github.ToshioCP.lb2&quot;</span>, G_APPLICATION_FLAGS_NONE);</span> <span id="cb4-29"><a href="#cb4-29"></a> app <span class="op">=</span> gtk_application_new <span class="op">(</span><span class="st">&quot;com.github.ToshioCP.lb2&quot;</span><span class="op">,</span> G_APPLICATION_DEFAULT_FLAGS<span class="op">);</span></span>
<span id="cb4-30"><a href="#cb4-30"></a> g_signal_connect (app, <span class="st">&quot;activate&quot;</span>, G_CALLBACK (app_activate), NULL);</span> <span id="cb4-30"><a href="#cb4-30"></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="cb4-31"><a href="#cb4-31"></a> stat =g_application_run (G_APPLICATION (app), argc, argv);</span> <span id="cb4-31"><a href="#cb4-31"></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="cb4-32"><a href="#cb4-32"></a> g_object_unref (app);</span> <span id="cb4-32"><a href="#cb4-32"></a> g_object_unref <span class="op">(</span>app<span class="op">);</span></span>
<span id="cb4-33"><a href="#cb4-33"></a> <span class="cf">return</span> stat;</span> <span id="cb4-33"><a href="#cb4-33"></a> <span class="cf">return</span> stat<span class="op">;</span></span>
<span id="cb4-34"><a href="#cb4-34"></a>}</span></code></pre></div> <span id="cb4-34"><a href="#cb4-34"></a><span class="op">}</span></span></code></pre></div>
<p>Look at the line 17 to 19. First, it creates a GtkButton instance <code>btn</code> with a label “Click me”. Then, adds the button to the window <code>win</code> as a child. Finally, connects a “clicked” signal of the button to a handler (function) <code>click_cb</code>. So, if <code>btn</code> is clicked, the function <code>click_cb</code> is invoked. The suffix “cb” means “call back”.</p> <p>Look at the line 17 to 19. First, it creates a GtkButton instance
<p>Name the program <code>lb2.c</code> and save it. Now compile and run it.</p> <code>btn</code> with a label “Click me”. Then, adds the button to the
window <code>win</code> as a child. Finally, connects a “clicked” signal
of the button to the handler <code>click_cb</code>. So, if
<code>btn</code> is clicked, the function <code>click_cb</code> is
invoked. The suffix “cb” means “call back”.</p>
<p>Name the program <code>lb2.c</code> and save it. Now compile and run
it.</p>
<figure> <figure>
<img src="image/screenshot_lb2.png" alt="" /><figcaption>Screenshot of the label</figcaption> <img src="image/screenshot_lb2.png" alt="Screenshot of the label" />
<figcaption aria-hidden="true">Screenshot of the label</figcaption>
</figure> </figure>
<p>A window with the button appears. Click the button (it is a large button, you can click everywhere in the window), then a string “Clicked.” appears on the terminal. It shows the handler was invoked by clicking the button.</p> <p>A window with the button appears. Click the button (it is a large
<p>Its good that we make sure that the clicked signal was caught and the handler was invoked by using <code>g_print</code>. However, using g_print is out of harmony with Gtk which is a GUI library. So, we will change the handler. The following code is <code>lb3.c</code>.</p> button, you can click everywhere in the window), then a string
<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="dt">static</span> <span class="dt">void</span></span> “Clicked.” appears on the terminal. It shows the handler was invoked by
<span id="cb5-2"><a href="#cb5-2"></a>click_cb (GtkButton *btn, gpointer user_data) {</span> clicking the button.</p>
<span id="cb5-3"><a href="#cb5-3"></a> GtkWindow *win = GTK_WINDOW (user_data);</span> <p>Its good that we make sure that the clicked signal was caught and
<span id="cb5-4"><a href="#cb5-4"></a> gtk_window_destroy (win);</span> the handler was invoked by using <code>g_print</code>. However, using
<span id="cb5-5"><a href="#cb5-5"></a>}</span> <code>g_print</code> is out of harmony with GTK, which is a GUI library.
<span id="cb5-6"><a href="#cb5-6"></a></span> So, we will change the handler. The following code is
<span id="cb5-7"><a href="#cb5-7"></a><span class="dt">static</span> <span class="dt">void</span></span> <code>lb3.c</code>.</p>
<span id="cb5-8"><a href="#cb5-8"></a>app_activate (GApplication *app, gpointer user_data) {</span> <div class="sourceCode" id="cb5"><pre
<span id="cb5-9"><a href="#cb5-9"></a> GtkWidget *win;</span> 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-10"><a href="#cb5-10"></a> GtkWidget *btn;</span> <span id="cb5-2"><a href="#cb5-2"></a>click_cb <span class="op">(</span>GtkButton <span class="op">*</span>btn<span class="op">,</span> GtkWindow <span class="op">*</span>win<span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-11"><a href="#cb5-11"></a></span> <span id="cb5-3"><a href="#cb5-3"></a> gtk_window_destroy <span class="op">(</span>win<span class="op">);</span></span>
<span id="cb5-12"><a href="#cb5-12"></a> win = gtk_application_window_new (GTK_APPLICATION (app));</span> <span id="cb5-4"><a href="#cb5-4"></a><span class="op">}</span></span>
<span id="cb5-13"><a href="#cb5-13"></a> gtk_window_set_title (GTK_WINDOW (win), <span class="st">&quot;lb3&quot;</span>);</span> <span id="cb5-5"><a href="#cb5-5"></a></span>
<span id="cb5-14"><a href="#cb5-14"></a> gtk_window_set_default_size (GTK_WINDOW (win), <span class="dv">400</span>, <span class="dv">300</span>);</span> <span id="cb5-6"><a href="#cb5-6"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb5-15"><a href="#cb5-15"></a></span> <span id="cb5-7"><a href="#cb5-7"></a>app_activate <span class="op">(</span>GApplication <span class="op">*</span>app<span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-16"><a href="#cb5-16"></a> btn = gtk_button_new_with_label (<span class="st">&quot;Quit&quot;</span>);</span> <span id="cb5-8"><a href="#cb5-8"></a> GtkWidget <span class="op">*</span>win<span class="op">;</span></span>
<span id="cb5-17"><a href="#cb5-17"></a> gtk_window_set_child (GTK_WINDOW (win), btn);</span> <span id="cb5-9"><a href="#cb5-9"></a> GtkWidget <span class="op">*</span>btn<span class="op">;</span></span>
<span id="cb5-18"><a href="#cb5-18"></a> g_signal_connect (btn, <span class="st">&quot;clicked&quot;</span>, G_CALLBACK (click_cb), win);</span> <span id="cb5-10"><a href="#cb5-10"></a></span>
<span id="cb5-19"><a href="#cb5-19"></a></span> <span id="cb5-11"><a href="#cb5-11"></a> win <span class="op">=</span> gtk_application_window_new <span class="op">(</span>GTK_APPLICATION <span class="op">(</span>app<span class="op">));</span></span>
<span id="cb5-20"><a href="#cb5-20"></a> gtk_widget_show (win);</span> <span id="cb5-12"><a href="#cb5-12"></a> gtk_window_set_title <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="st">&quot;lb3&quot;</span><span class="op">);</span></span>
<span id="cb5-21"><a href="#cb5-21"></a>}</span></code></pre></div> <span id="cb5-13"><a href="#cb5-13"></a> gtk_window_set_default_size <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="dv">400</span><span class="op">,</span> <span class="dv">300</span><span class="op">);</span></span>
<p>And the difference between <code>lb2.c</code> and <code>lb3.c</code> is as follows.</p> <span id="cb5-14"><a href="#cb5-14"></a></span>
<span id="cb5-15"><a href="#cb5-15"></a> btn <span class="op">=</span> gtk_button_new_with_label <span class="op">(</span><span class="st">&quot;Close&quot;</span><span class="op">);</span></span>
<span id="cb5-16"><a href="#cb5-16"></a> gtk_window_set_child <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> btn<span class="op">);</span></span>
<span id="cb5-17"><a href="#cb5-17"></a> g_signal_connect <span class="op">(</span>btn<span class="op">,</span> <span class="st">&quot;clicked&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>click_cb<span class="op">),</span> win<span class="op">);</span></span>
<span id="cb5-18"><a href="#cb5-18"></a></span>
<span id="cb5-19"><a href="#cb5-19"></a> gtk_widget_show <span class="op">(</span>win<span class="op">);</span></span>
<span id="cb5-20"><a href="#cb5-20"></a><span class="op">}</span></span></code></pre></div>
<p>And the difference between <code>lb2.c</code> and <code>lb3.c</code>
is as follows.</p>
<pre><code>$ cd misc; diff lb2.c lb3.c <pre><code>$ cd misc; diff lb2.c lb3.c
5c5,6 4,5c4,5
&lt; click_cb (GtkButton *btn) {
&lt; g_print (&quot;Clicked.\n&quot;); &lt; g_print (&quot;Clicked.\n&quot;);
--- ---
&gt; GtkWindow *win = GTK_WINDOW (user_data); &gt; click_cb (GtkButton *btn, GtkWindow *win) {
&gt; gtk_window_destroy (win); &gt; gtk_window_destroy (win);
14c15 14c14
&lt; gtk_window_set_title (GTK_WINDOW (win), &quot;lb2&quot;); &lt; gtk_window_set_title (GTK_WINDOW (win), &quot;lb2&quot;);
--- ---
&gt; gtk_window_set_title (GTK_WINDOW (win), &quot;lb3&quot;); &gt; gtk_window_set_title (GTK_WINDOW (win), &quot;lb3&quot;);
17c18 17c17
&lt; btn = gtk_button_new_with_label (&quot;Click me&quot;); &lt; btn = gtk_button_new_with_label (&quot;Click me&quot;);
--- ---
&gt; btn = gtk_button_new_with_label (&quot;Quit&quot;); &gt; btn = gtk_button_new_with_label (&quot;Close&quot;);
19c20 19c19
&lt; g_signal_connect (btn, &quot;clicked&quot;, G_CALLBACK (click_cb), NULL); &lt; g_signal_connect (btn, &quot;clicked&quot;, G_CALLBACK (click_cb), NULL);
--- ---
&gt; g_signal_connect (btn, &quot;clicked&quot;, G_CALLBACK (click_cb), win); &gt; g_signal_connect (btn, &quot;clicked&quot;, G_CALLBACK (click_cb), win);
29c30 29c29
&lt; app = gtk_application_new (&quot;com.github.ToshioCP.lb2&quot;, G_APPLICATION_FLAGS_NONE); &lt; app = gtk_application_new (&quot;com.github.ToshioCP.lb2&quot;, G_APPLICATION_DEFAULT_FLAGS);
--- ---
&gt; app = gtk_application_new (&quot;com.github.ToshioCP.lb3&quot;, G_APPLICATION_FLAGS_NONE); &gt; app = gtk_application_new (&quot;com.github.ToshioCP.lb3&quot;, G_APPLICATION_DEFAULT_FLAGS);
35d35 35d34
&lt; </code></pre> &lt; </code></pre>
<p>The changes are:</p> <p>The changes are:</p>
<ul> <ul>
<li>The function <code>g_print</code> in <code>lb2.c</code> was deleted and the two lines above are inserted instead.</li> <li>The function <code>g_print</code> in <code>lb2.c</code> was deleted
<li>The label of <code>btn</code> is changed from “Click me” to “Quit”.</li> and two lines are inserted.
<li>The fourth argument of <code>g_signal_connect</code> is changed from <code>NULL</code> to <code>win</code>.</li> <ul>
<li><code>click_cb</code> has the second parameter, which comes from the
fourth argument of the <code>g_signal_connect</code> at the line 19. One
thing to be careful is the types are different between the second
parameter of <code>click_cb</code> and the fourth argument of
<code>g_signal_connect</code>. The former is <code>GtkWindow *</code>
and the latter is <code>GtkWidget *</code>. The compiler doesnt
complain because <code>g_signal_connect</code> uses gpointer (general
type of pointer). In this program the instance pointed by
<code>win</code> is a GtkApplicationWindow object. It is a descendant of
GtkWindow and GtkWidget class, so both <code>GtkWindow *</code> and
<code>GtkWidget *</code> are correct types for the instance.</li>
<li><code>gtk_destroy (win)</code> destroys the top-level window. Then
the application quits.</li>
</ul></li>
<li>The label of <code>btn</code> is changed from “Click me” to
“Close”.</li>
<li>The fourth argument of <code>g_signal_connect</code> is changed from
<code>NULL</code> to <code>win</code>.</li>
</ul> </ul>
<p>The most important change is the fourth argument of <code>g_signal_connect</code>. This argument is described as “data to pass to handler” in the definition of <code>g_signal_connect</code> in <a href="https://docs.gtk.org/gobject/func.signal_connect.html">GObject API Reference</a>. Therefore, <code>win</code> which is a pointer to GtkApplicationWindow is passed to the handler as a second parameter <code>user_data</code>. The handler then casts it to a pointer to GtkWindow and calls <code>gtk_window_destroy</code> to destroy the top-level window. The application then quits.</p> <p>The most important change is the fourth argument of the
<code>g_signal_connect</code>. This argument is described as “data to
pass to handler” in the definition of <a
href="https://docs.gtk.org/gobject/func.signal_connect.html"><code>g_signal_connect</code></a>.</p>
<h3 id="gtkbox">GtkBox</h3> <h3 id="gtkbox">GtkBox</h3>
<p>GtkWindow and GtkApplicationWindow can have only one child. If you want to add two or more widgets in a window, you need a container widget. GtkBox is one of the containers. It arranges two or more child widgets into a single row or column. The following procedure shows the way to add two buttons in a window.</p> <p>GtkWindow and GtkApplicationWindow can have only one child. If you
want to add two or more widgets in a window, you need a container
widget. GtkBox is one of the containers. It arranges two or more child
widgets into a single row or column. The following procedure shows the
way to add two buttons in a window.</p>
<ul> <ul>
<li>Create a GtkApplicationWindow instance.</li> <li>Create a GtkApplicationWindow instance.</li>
<li>Create a GtkBox instance and add it to the GtkApplicationWindow as a child.</li> <li>Create a GtkBox instance and add it to the GtkApplicationWindow as a
child.</li>
<li>Create a GtkButton instance and append it to the GtkBox.</li> <li>Create a GtkButton instance and append it to the GtkBox.</li>
<li>Create another GtkButton instance and append it to the GtkBox.</li> <li>Create another GtkButton instance and append it to the GtkBox.</li>
</ul> </ul>
<p>After this, the Widgets are connected as the following diagram.</p> <p>After this, the Widgets are connected as the following diagram.</p>
<figure> <figure>
<img src="image/box.png" alt="" /><figcaption>Parent-child relationship</figcaption> <img src="image/box.png" alt="Parent-child relationship" />
<figcaption aria-hidden="true">Parent-child relationship</figcaption>
</figure> </figure>
<p>The program <code>lb4.c</code> includes these widgets. It is as follows.</p> <p>The program <code>lb4.c</code> is as follows.</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb7-1"><a href="#cb7-1"></a><span class="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span> <div class="sourceCode" id="cb7"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb7-1"><a href="#cb7-1"></a><span class="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span>
<span id="cb7-2"><a href="#cb7-2"></a></span> <span id="cb7-2"><a href="#cb7-2"></a></span>
<span id="cb7-3"><a href="#cb7-3"></a><span class="dt">static</span> <span class="dt">void</span></span> <span id="cb7-3"><a href="#cb7-3"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb7-4"><a href="#cb7-4"></a>click1_cb (GtkButton *btn, gpointer user_data) {</span> <span id="cb7-4"><a href="#cb7-4"></a>click1_cb <span class="op">(</span>GtkButton <span class="op">*</span>btn<span class="op">)</span> <span class="op">{</span></span>
<span id="cb7-5"><a href="#cb7-5"></a> <span class="dt">const</span> gchar *s;</span> <span id="cb7-5"><a href="#cb7-5"></a> <span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span>s<span class="op">;</span></span>
<span id="cb7-6"><a href="#cb7-6"></a></span> <span id="cb7-6"><a href="#cb7-6"></a></span>
<span id="cb7-7"><a href="#cb7-7"></a> s = gtk_button_get_label (btn);</span> <span id="cb7-7"><a href="#cb7-7"></a> s <span class="op">=</span> gtk_button_get_label <span class="op">(</span>btn<span class="op">);</span></span>
<span id="cb7-8"><a href="#cb7-8"></a> <span class="cf">if</span> (g_strcmp0 (s, <span class="st">&quot;Hello.&quot;</span>) == <span class="dv">0</span>)</span> <span id="cb7-8"><a href="#cb7-8"></a> <span class="cf">if</span> <span class="op">(</span>g_strcmp0 <span class="op">(</span>s<span class="op">,</span> <span class="st">&quot;Hello.&quot;</span><span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb7-9"><a href="#cb7-9"></a> gtk_button_set_label (btn, <span class="st">&quot;Good-bye.&quot;</span>);</span> <span id="cb7-9"><a href="#cb7-9"></a> gtk_button_set_label <span class="op">(</span>btn<span class="op">,</span> <span class="st">&quot;Good-bye.&quot;</span><span class="op">);</span></span>
<span id="cb7-10"><a href="#cb7-10"></a> <span class="cf">else</span></span> <span id="cb7-10"><a href="#cb7-10"></a> <span class="cf">else</span></span>
<span id="cb7-11"><a href="#cb7-11"></a> gtk_button_set_label (btn, <span class="st">&quot;Hello.&quot;</span>);</span> <span id="cb7-11"><a href="#cb7-11"></a> gtk_button_set_label <span class="op">(</span>btn<span class="op">,</span> <span class="st">&quot;Hello.&quot;</span><span class="op">);</span></span>
<span id="cb7-12"><a href="#cb7-12"></a>}</span> <span id="cb7-12"><a href="#cb7-12"></a><span class="op">}</span></span>
<span id="cb7-13"><a href="#cb7-13"></a></span> <span id="cb7-13"><a href="#cb7-13"></a></span>
<span id="cb7-14"><a href="#cb7-14"></a><span class="dt">static</span> <span class="dt">void</span></span> <span id="cb7-14"><a href="#cb7-14"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb7-15"><a href="#cb7-15"></a>click2_cb (GtkButton *btn, gpointer user_data) {</span> <span id="cb7-15"><a href="#cb7-15"></a>click2_cb <span class="op">(</span>GtkButton <span class="op">*</span>btn<span class="op">,</span> GtkWindow <span class="op">*</span>win<span class="op">)</span> <span class="op">{</span></span>
<span id="cb7-16"><a href="#cb7-16"></a> GtkWindow *win = GTK_WINDOW (user_data);</span> <span id="cb7-16"><a href="#cb7-16"></a> gtk_window_destroy <span class="op">(</span>win<span class="op">);</span></span>
<span id="cb7-17"><a href="#cb7-17"></a> gtk_window_destroy (win);</span> <span id="cb7-17"><a href="#cb7-17"></a><span class="op">}</span></span>
<span id="cb7-18"><a href="#cb7-18"></a>}</span> <span id="cb7-18"><a href="#cb7-18"></a></span>
<span id="cb7-19"><a href="#cb7-19"></a></span> <span id="cb7-19"><a href="#cb7-19"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb7-20"><a href="#cb7-20"></a><span class="dt">static</span> <span class="dt">void</span></span> <span id="cb7-20"><a href="#cb7-20"></a>app_activate <span class="op">(</span>GApplication <span class="op">*</span>app<span class="op">)</span> <span class="op">{</span></span>
<span id="cb7-21"><a href="#cb7-21"></a>app_activate (GApplication *app, gpointer user_data) {</span> <span id="cb7-21"><a href="#cb7-21"></a> GtkWidget <span class="op">*</span>win<span class="op">;</span></span>
<span id="cb7-22"><a href="#cb7-22"></a> GtkWidget *win;</span> <span id="cb7-22"><a href="#cb7-22"></a> GtkWidget <span class="op">*</span>box<span class="op">;</span></span>
<span id="cb7-23"><a href="#cb7-23"></a> GtkWidget *box;</span> <span id="cb7-23"><a href="#cb7-23"></a> GtkWidget <span class="op">*</span>btn1<span class="op">;</span></span>
<span id="cb7-24"><a href="#cb7-24"></a> GtkWidget *btn1;</span> <span id="cb7-24"><a href="#cb7-24"></a> GtkWidget <span class="op">*</span>btn2<span class="op">;</span></span>
<span id="cb7-25"><a href="#cb7-25"></a> GtkWidget *btn2;</span> <span id="cb7-25"><a href="#cb7-25"></a></span>
<span id="cb7-26"><a href="#cb7-26"></a></span> <span id="cb7-26"><a href="#cb7-26"></a> win <span class="op">=</span> gtk_application_window_new <span class="op">(</span>GTK_APPLICATION <span class="op">(</span>app<span class="op">));</span></span>
<span id="cb7-27"><a href="#cb7-27"></a> win = gtk_application_window_new (GTK_APPLICATION (app));</span> <span id="cb7-27"><a href="#cb7-27"></a> gtk_window_set_title <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="st">&quot;lb4&quot;</span><span class="op">);</span></span>
<span id="cb7-28"><a href="#cb7-28"></a> gtk_window_set_title (GTK_WINDOW (win), <span class="st">&quot;lb4&quot;</span>);</span> <span id="cb7-28"><a href="#cb7-28"></a> gtk_window_set_default_size <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="dv">400</span><span class="op">,</span> <span class="dv">300</span><span class="op">);</span></span>
<span id="cb7-29"><a href="#cb7-29"></a> gtk_window_set_default_size (GTK_WINDOW (win), <span class="dv">400</span>, <span class="dv">300</span>);</span> <span id="cb7-29"><a href="#cb7-29"></a></span>
<span id="cb7-30"><a href="#cb7-30"></a></span> <span id="cb7-30"><a href="#cb7-30"></a> box <span class="op">=</span> gtk_box_new <span class="op">(</span>GTK_ORIENTATION_VERTICAL<span class="op">,</span> <span class="dv">5</span><span class="op">);</span></span>
<span id="cb7-31"><a href="#cb7-31"></a> box = gtk_box_new (GTK_ORIENTATION_VERTICAL, <span class="dv">5</span>);</span> <span id="cb7-31"><a href="#cb7-31"></a> gtk_box_set_homogeneous <span class="op">(</span>GTK_BOX <span class="op">(</span>box<span class="op">),</span> TRUE<span class="op">);</span></span>
<span id="cb7-32"><a href="#cb7-32"></a> gtk_box_set_homogeneous (GTK_BOX (box), TRUE);</span> <span id="cb7-32"><a href="#cb7-32"></a> gtk_window_set_child <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> box<span class="op">);</span></span>
<span id="cb7-33"><a href="#cb7-33"></a> gtk_window_set_child (GTK_WINDOW (win), box);</span> <span id="cb7-33"><a href="#cb7-33"></a></span>
<span id="cb7-34"><a href="#cb7-34"></a></span> <span id="cb7-34"><a href="#cb7-34"></a> btn1 <span class="op">=</span> gtk_button_new_with_label <span class="op">(</span><span class="st">&quot;Hello.&quot;</span><span class="op">);</span></span>
<span id="cb7-35"><a href="#cb7-35"></a> btn1 = gtk_button_new_with_label (<span class="st">&quot;Hello.&quot;</span>);</span> <span id="cb7-35"><a href="#cb7-35"></a> g_signal_connect <span class="op">(</span>btn1<span class="op">,</span> <span class="st">&quot;clicked&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>click1_cb<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb7-36"><a href="#cb7-36"></a> g_signal_connect (btn1, <span class="st">&quot;clicked&quot;</span>, G_CALLBACK (click1_cb), NULL);</span> <span id="cb7-36"><a href="#cb7-36"></a></span>
<span id="cb7-37"><a href="#cb7-37"></a></span> <span id="cb7-37"><a href="#cb7-37"></a> btn2 <span class="op">=</span> gtk_button_new_with_label <span class="op">(</span><span class="st">&quot;Close&quot;</span><span class="op">);</span></span>
<span id="cb7-38"><a href="#cb7-38"></a> btn2 = gtk_button_new_with_label (<span class="st">&quot;Quit&quot;</span>);</span> <span id="cb7-38"><a href="#cb7-38"></a> g_signal_connect <span class="op">(</span>btn2<span class="op">,</span> <span class="st">&quot;clicked&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>click2_cb<span class="op">),</span> win<span class="op">);</span></span>
<span id="cb7-39"><a href="#cb7-39"></a> g_signal_connect (btn2, <span class="st">&quot;clicked&quot;</span>, G_CALLBACK (click2_cb), win);</span> <span id="cb7-39"><a href="#cb7-39"></a></span>
<span id="cb7-40"><a href="#cb7-40"></a></span> <span id="cb7-40"><a href="#cb7-40"></a> gtk_box_append <span class="op">(</span>GTK_BOX <span class="op">(</span>box<span class="op">),</span> btn1<span class="op">);</span></span>
<span id="cb7-41"><a href="#cb7-41"></a> gtk_box_append (GTK_BOX (box), btn1);</span> <span id="cb7-41"><a href="#cb7-41"></a> gtk_box_append <span class="op">(</span>GTK_BOX <span class="op">(</span>box<span class="op">),</span> btn2<span class="op">);</span></span>
<span id="cb7-42"><a href="#cb7-42"></a> gtk_box_append (GTK_BOX (box), btn2);</span> <span id="cb7-42"><a href="#cb7-42"></a></span>
<span id="cb7-43"><a href="#cb7-43"></a></span> <span id="cb7-43"><a href="#cb7-43"></a> gtk_widget_show <span class="op">(</span>win<span class="op">);</span></span>
<span id="cb7-44"><a href="#cb7-44"></a> gtk_widget_show (win);</span> <span id="cb7-44"><a href="#cb7-44"></a><span class="op">}</span></span>
<span id="cb7-45"><a href="#cb7-45"></a>}</span> <span id="cb7-45"><a href="#cb7-45"></a></span>
<span id="cb7-46"><a href="#cb7-46"></a></span> <span id="cb7-46"><a href="#cb7-46"></a><span class="dt">int</span></span>
<span id="cb7-47"><a href="#cb7-47"></a><span class="dt">int</span></span> <span id="cb7-47"><a href="#cb7-47"></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="cb7-48"><a href="#cb7-48"></a>main (<span class="dt">int</span> argc, <span class="dt">char</span> **argv) {</span> <span id="cb7-48"><a href="#cb7-48"></a> GtkApplication <span class="op">*</span>app<span class="op">;</span></span>
<span id="cb7-49"><a href="#cb7-49"></a> GtkApplication *app;</span> <span id="cb7-49"><a href="#cb7-49"></a> <span class="dt">int</span> stat<span class="op">;</span></span>
<span id="cb7-50"><a href="#cb7-50"></a> <span class="dt">int</span> stat;</span> <span id="cb7-50"><a href="#cb7-50"></a></span>
<span id="cb7-51"><a href="#cb7-51"></a></span> <span id="cb7-51"><a href="#cb7-51"></a> app <span class="op">=</span> gtk_application_new <span class="op">(</span><span class="st">&quot;com.github.ToshioCP.lb4&quot;</span><span class="op">,</span> G_APPLICATION_DEFAULT_FLAGS<span class="op">);</span></span>
<span id="cb7-52"><a href="#cb7-52"></a> app = gtk_application_new (<span class="st">&quot;com.github.ToshioCP.lb4&quot;</span>, G_APPLICATION_FLAGS_NONE);</span> <span id="cb7-52"><a href="#cb7-52"></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="cb7-53"><a href="#cb7-53"></a> g_signal_connect (app, <span class="st">&quot;activate&quot;</span>, G_CALLBACK (app_activate), NULL);</span> <span id="cb7-53"><a href="#cb7-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="cb7-54"><a href="#cb7-54"></a> stat =g_application_run (G_APPLICATION (app), argc, argv);</span> <span id="cb7-54"><a href="#cb7-54"></a> g_object_unref <span class="op">(</span>app<span class="op">);</span></span>
<span id="cb7-55"><a href="#cb7-55"></a> g_object_unref (app);</span> <span id="cb7-55"><a href="#cb7-55"></a> <span class="cf">return</span> stat<span class="op">;</span></span>
<span id="cb7-56"><a href="#cb7-56"></a> <span class="cf">return</span> stat;</span> <span id="cb7-56"><a href="#cb7-56"></a><span class="op">}</span></span></code></pre></div>
<span id="cb7-57"><a href="#cb7-57"></a>}</span></code></pre></div>
<p>Look at the function <code>app_activate</code>.</p> <p>Look at the function <code>app_activate</code>.</p>
<p>After the creation of a GtkApplicationWindow instance, a GtkBox instance is created.</p> <p>After the creation of a GtkApplicationWindow instance, a GtkBox
<pre><code>box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); instance is created.</p>
gtk_box_set_homogeneous (GTK_BOX (box), TRUE);</code></pre> <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>box <span class="op">=</span> gtk_box_new<span class="op">(</span>GTK_ORIENTATION_VERTICAL<span class="op">,</span> <span class="dv">5</span><span class="op">);</span></span>
<p>The first argument arranges the children of the box vertically. The second argument is the size between the children. The next function fills the box with the children, giving them the same space.</p> <span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a>gtk_box_set_homogeneous <span class="op">(</span>GTK_BOX <span class="op">(</span>box<span class="op">),</span> TRUE<span class="op">);</span></span></code></pre></div>
<p>After that, two buttons <code>btn1</code> and <code>btn2</code> are created and the signal handlers are set. Then, these two buttons are appended to the box.</p> <p>The first argument arranges the children of the box vertically. The
orientation constants are defined like this:</p>
<ul>
<li>GTK_ORIENTATION_VERTICAL: the children widgets are arranged
vertically</li>
<li>GTK_ORIENTATION_HORIZONTAL: the children widgets are arranged
horizontally</li>
</ul>
<p>The second argument is the size between the children. The unit of the
length is pixel.</p>
<p>The next function fills the box with the children, giving them the
same space.</p>
<p>After that, two buttons <code>btn1</code> and <code>btn2</code> are
created and the signal handlers are set. Then, these two buttons are
appended to the box.</p>
<div class="sourceCode" id="cb9"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb9-1"><a href="#cb9-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb9-2"><a href="#cb9-2"></a>click1_cb <span class="op">(</span>GtkButton <span class="op">*</span>btn<span class="op">)</span> <span class="op">{</span></span>
<span id="cb9-3"><a href="#cb9-3"></a> <span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span>s<span class="op">;</span></span>
<span id="cb9-4"><a href="#cb9-4"></a></span>
<span id="cb9-5"><a href="#cb9-5"></a> s <span class="op">=</span> gtk_button_get_label <span class="op">(</span>btn<span class="op">);</span></span>
<span id="cb9-6"><a href="#cb9-6"></a> <span class="cf">if</span> <span class="op">(</span>g_strcmp0 <span class="op">(</span>s<span class="op">,</span> <span class="st">&quot;Hello.&quot;</span><span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb9-7"><a href="#cb9-7"></a> gtk_button_set_label <span class="op">(</span>btn<span class="op">,</span> <span class="st">&quot;Good-bye.&quot;</span><span class="op">);</span></span>
<span id="cb9-8"><a href="#cb9-8"></a> <span class="cf">else</span></span>
<span id="cb9-9"><a href="#cb9-9"></a> gtk_button_set_label <span class="op">(</span>btn<span class="op">,</span> <span class="st">&quot;Hello.&quot;</span><span class="op">);</span></span>
<span id="cb9-10"><a href="#cb9-10"></a><span class="op">}</span></span></code></pre></div>
<p>The function <code>gtk_button_get_lable</code> returns a text from
the label. The string is owned by the button and you cant modify or
free it. The <code>const</code> qualifier is necessary for the string
<code>s</code>. If you change the string, your compiler will give you a
waring.</p>
<p>You always need to be careful with the const qualifier when you see
the GTK 4 API reference.</p>
<figure> <figure>
<img src="image/screenshot_lb4.png" alt="" /><figcaption>Screenshot of the box</figcaption> <img src="image/screenshot_lb4.png" alt="Screenshot of the box" />
<figcaption aria-hidden="true">Screenshot of the box</figcaption>
</figure> </figure>
<p>The handler corresponds to <code>btn1</code> toggles its label. The handler corresponds to <code>btn2</code> destroys the top-level window and the application quits.</p> <p>The handler corresponds to <code>btn1</code> toggles its label. The
handler corresponds to <code>btn2</code> destroys the top-level window
and the application quits.</p>
</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> <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> </body>

View file

@ -5,7 +5,7 @@
<meta name="generator" content="pandoc" /> <meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <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"> <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> <title>GTK 4 tutorial</title>
<style> <style>
code{white-space: pre-wrap;} code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;} span.smallcaps{font-variant: small-caps;}
@ -112,19 +112,24 @@
</div> </div>
</nav> </nav>
<h1 id="widgets-2">Widgets (2)</h1> <h1 id="widgets-2">Widgets (2)</h1>
<h2 id="gtktextview-gtktextbuffer-and-gtkscrolledwindow">GtkTextView, GtkTextBuffer and GtkScrolledWindow</h2> <h2 id="gtktextview-gtktextbuffer-and-gtkscrolledwindow">GtkTextView,
<h3 id="gtktextview-and-gtktextbuffer">GtkTextView and GtkTextBuffer</h3> GtkTextBuffer and GtkScrolledWindow</h2>
<p>GtkTextView is a widget for multi-line text editing. GtkTextBuffer is a text buffer which is connected to GtkTextView. See the sample program <code>tfv1.c</code> below.</p> <h3 id="gtktextview-and-gtktextbuffer">GtkTextView and
<div class="sourceCode" id="cb1"><pre class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1"></a><span class="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span> GtkTextBuffer</h3>
<p>GtkTextView is a widget for multi-line text editing. GtkTextBuffer is
a text buffer which is connected to GtkTextView. See the sample program
<code>tfv1.c</code> below.</p>
<div class="sourceCode" id="cb1"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1"></a><span class="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span>
<span id="cb1-2"><a href="#cb1-2"></a></span> <span id="cb1-2"><a href="#cb1-2"></a></span>
<span id="cb1-3"><a href="#cb1-3"></a><span class="dt">static</span> <span class="dt">void</span></span> <span id="cb1-3"><a href="#cb1-3"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb1-4"><a href="#cb1-4"></a>app_activate (GApplication *app, gpointer user_data) {</span> <span id="cb1-4"><a href="#cb1-4"></a>app_activate <span class="op">(</span>GApplication <span class="op">*</span>app<span class="op">)</span> <span class="op">{</span></span>
<span id="cb1-5"><a href="#cb1-5"></a> GtkWidget *win;</span> <span id="cb1-5"><a href="#cb1-5"></a> GtkWidget <span class="op">*</span>win<span class="op">;</span></span>
<span id="cb1-6"><a href="#cb1-6"></a> GtkWidget *tv;</span> <span id="cb1-6"><a href="#cb1-6"></a> GtkWidget <span class="op">*</span>tv<span class="op">;</span></span>
<span id="cb1-7"><a href="#cb1-7"></a> GtkTextBuffer *tb;</span> <span id="cb1-7"><a href="#cb1-7"></a> GtkTextBuffer <span class="op">*</span>tb<span class="op">;</span></span>
<span id="cb1-8"><a href="#cb1-8"></a> gchar *text;</span> <span id="cb1-8"><a href="#cb1-8"></a> gchar <span class="op">*</span>text<span class="op">;</span></span>
<span id="cb1-9"><a href="#cb1-9"></a></span> <span id="cb1-9"><a href="#cb1-9"></a></span>
<span id="cb1-10"><a href="#cb1-10"></a> text =</span> <span id="cb1-10"><a href="#cb1-10"></a> text <span class="op">=</span></span>
<span id="cb1-11"><a href="#cb1-11"></a> <span class="st">&quot;Once upon a time, there was an old man who was called Taketori-no-Okina. &quot;</span></span> <span id="cb1-11"><a href="#cb1-11"></a> <span class="st">&quot;Once upon a time, there was an old man who was called Taketori-no-Okina. &quot;</span></span>
<span id="cb1-12"><a href="#cb1-12"></a> <span class="st">&quot;It is a japanese word that means a man whose work is making bamboo baskets.</span><span class="sc">\n</span><span class="st">&quot;</span></span> <span id="cb1-12"><a href="#cb1-12"></a> <span class="st">&quot;It is a japanese word that means a man whose work is making bamboo baskets.</span><span class="sc">\n</span><span class="st">&quot;</span></span>
<span id="cb1-13"><a href="#cb1-13"></a> <span class="st">&quot;One day, he went into a mountain and found a shining bamboo. &quot;</span></span> <span id="cb1-13"><a href="#cb1-13"></a> <span class="st">&quot;One day, he went into a mountain and found a shining bamboo. &quot;</span></span>
@ -132,49 +137,77 @@
<span id="cb1-15"><a href="#cb1-15"></a> <span class="st">&quot;He cut it, then there was a small cute baby girl in it. &quot;</span></span> <span id="cb1-15"><a href="#cb1-15"></a> <span class="st">&quot;He cut it, then there was a small cute baby girl in it. &quot;</span></span>
<span id="cb1-16"><a href="#cb1-16"></a> <span class="st">&quot;The girl was shining faintly. &quot;</span></span> <span id="cb1-16"><a href="#cb1-16"></a> <span class="st">&quot;The girl was shining faintly. &quot;</span></span>
<span id="cb1-17"><a href="#cb1-17"></a> <span class="st">&quot;He thought this baby girl is a gift from Heaven and took her home.</span><span class="sc">\n</span><span class="st">&quot;</span></span> <span id="cb1-17"><a href="#cb1-17"></a> <span class="st">&quot;He thought this baby girl is a gift from Heaven and took her home.</span><span class="sc">\n</span><span class="st">&quot;</span></span>
<span id="cb1-18"><a href="#cb1-18"></a> <span class="st">&quot;His wife was surprized at his tale. &quot;</span></span> <span id="cb1-18"><a href="#cb1-18"></a> <span class="st">&quot;His wife was surprized at his story. &quot;</span></span>
<span id="cb1-19"><a href="#cb1-19"></a> <span class="st">&quot;They were very happy because they had no children. &quot;</span></span> <span id="cb1-19"><a href="#cb1-19"></a> <span class="st">&quot;They were very happy because they had no children. &quot;</span></span>
<span id="cb1-20"><a href="#cb1-20"></a> ;</span> <span id="cb1-20"><a href="#cb1-20"></a> <span class="op">;</span></span>
<span id="cb1-21"><a href="#cb1-21"></a> win = gtk_application_window_new (GTK_APPLICATION (app));</span> <span id="cb1-21"><a href="#cb1-21"></a> win <span class="op">=</span> gtk_application_window_new <span class="op">(</span>GTK_APPLICATION <span class="op">(</span>app<span class="op">));</span></span>
<span id="cb1-22"><a href="#cb1-22"></a> gtk_window_set_title (GTK_WINDOW (win), <span class="st">&quot;Taketori&quot;</span>);</span> <span id="cb1-22"><a href="#cb1-22"></a> gtk_window_set_title <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="st">&quot;Taketori&quot;</span><span class="op">);</span></span>
<span id="cb1-23"><a href="#cb1-23"></a> gtk_window_set_default_size (GTK_WINDOW (win), <span class="dv">400</span>, <span class="dv">300</span>);</span> <span id="cb1-23"><a href="#cb1-23"></a> gtk_window_set_default_size <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="dv">400</span><span class="op">,</span> <span class="dv">300</span><span class="op">);</span></span>
<span id="cb1-24"><a href="#cb1-24"></a></span> <span id="cb1-24"><a href="#cb1-24"></a></span>
<span id="cb1-25"><a href="#cb1-25"></a> tv = gtk_text_view_new ();</span> <span id="cb1-25"><a href="#cb1-25"></a> tv <span class="op">=</span> gtk_text_view_new <span class="op">();</span></span>
<span id="cb1-26"><a href="#cb1-26"></a> tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));</span> <span id="cb1-26"><a href="#cb1-26"></a> tb <span class="op">=</span> gtk_text_view_get_buffer <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
<span id="cb1-27"><a href="#cb1-27"></a> gtk_text_buffer_set_text (tb, text, -<span class="dv">1</span>);</span> <span id="cb1-27"><a href="#cb1-27"></a> gtk_text_buffer_set_text <span class="op">(</span>tb<span class="op">,</span> text<span class="op">,</span> <span class="op">-</span><span class="dv">1</span><span class="op">);</span></span>
<span id="cb1-28"><a href="#cb1-28"></a> gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR);</span> <span id="cb1-28"><a href="#cb1-28"></a> gtk_text_view_set_wrap_mode <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> GTK_WRAP_WORD_CHAR<span class="op">);</span></span>
<span id="cb1-29"><a href="#cb1-29"></a></span> <span id="cb1-29"><a href="#cb1-29"></a></span>
<span id="cb1-30"><a href="#cb1-30"></a> gtk_window_set_child (GTK_WINDOW (win), tv);</span> <span id="cb1-30"><a href="#cb1-30"></a> gtk_window_set_child <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> tv<span class="op">);</span></span>
<span id="cb1-31"><a href="#cb1-31"></a></span> <span id="cb1-31"><a href="#cb1-31"></a></span>
<span id="cb1-32"><a href="#cb1-32"></a> gtk_widget_show (win);</span> <span id="cb1-32"><a href="#cb1-32"></a> gtk_widget_show <span class="op">(</span>win<span class="op">);</span></span>
<span id="cb1-33"><a href="#cb1-33"></a>}</span> <span id="cb1-33"><a href="#cb1-33"></a><span class="op">}</span></span>
<span id="cb1-34"><a href="#cb1-34"></a></span> <span id="cb1-34"><a href="#cb1-34"></a></span>
<span id="cb1-35"><a href="#cb1-35"></a><span class="dt">int</span></span> <span id="cb1-35"><a href="#cb1-35"></a><span class="dt">int</span></span>
<span id="cb1-36"><a href="#cb1-36"></a>main (<span class="dt">int</span> argc, <span class="dt">char</span> **argv) {</span> <span id="cb1-36"><a href="#cb1-36"></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="cb1-37"><a href="#cb1-37"></a> GtkApplication *app;</span> <span id="cb1-37"><a href="#cb1-37"></a> GtkApplication <span class="op">*</span>app<span class="op">;</span></span>
<span id="cb1-38"><a href="#cb1-38"></a> <span class="dt">int</span> stat;</span> <span id="cb1-38"><a href="#cb1-38"></a> <span class="dt">int</span> stat<span class="op">;</span></span>
<span id="cb1-39"><a href="#cb1-39"></a></span> <span id="cb1-39"><a href="#cb1-39"></a></span>
<span id="cb1-40"><a href="#cb1-40"></a> app = gtk_application_new (<span class="st">&quot;com.github.ToshioCP.tfv1&quot;</span>, G_APPLICATION_FLAGS_NONE);</span> <span id="cb1-40"><a href="#cb1-40"></a> app <span class="op">=</span> gtk_application_new <span class="op">(</span><span class="st">&quot;com.github.ToshioCP.tfv1&quot;</span><span class="op">,</span> G_APPLICATION_DEFAULT_FLAGS<span class="op">);</span></span>
<span id="cb1-41"><a href="#cb1-41"></a> g_signal_connect (app, <span class="st">&quot;activate&quot;</span>, G_CALLBACK (app_activate), NULL);</span> <span id="cb1-41"><a href="#cb1-41"></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="cb1-42"><a href="#cb1-42"></a> stat = g_application_run (G_APPLICATION (app), argc, argv);</span> <span id="cb1-42"><a href="#cb1-42"></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="cb1-43"><a href="#cb1-43"></a> g_object_unref (app);</span> <span id="cb1-43"><a href="#cb1-43"></a> g_object_unref <span class="op">(</span>app<span class="op">);</span></span>
<span id="cb1-44"><a href="#cb1-44"></a> <span class="cf">return</span> stat;</span> <span id="cb1-44"><a href="#cb1-44"></a> <span class="cf">return</span> stat<span class="op">;</span></span>
<span id="cb1-45"><a href="#cb1-45"></a>}</span></code></pre></div> <span id="cb1-45"><a href="#cb1-45"></a><span class="op">}</span></span></code></pre></div>
<p>Look at line 25. A GtkTextView instance is created and its pointer is assigned to <code>tv</code>. When the GtkTextView instance is created, a GtkTextBuffer instance is also created and connected to the GtkTextView automatically. “GtkTextBuffer instance” will be referred to simply as “GtkTextBuffer” or “buffer”. In the next line, the pointer to the buffer is got and assigned to <code>tb</code>. Then, the text from line 10 to 20 is assigned to the buffer.</p> <p>Look at line 25. A GtkTextView instance is created and its pointer is
<p>GtkTextView has a wrap mode. When it is set to <code>GTK_WRAP_WORD_CHAR</code>, text wraps in between words, or if that is not enough, also between graphemes.</p> assigned to <code>tv</code>. When the GtkTextView instance is created, a
<p>In line 30, <code>tv</code> is added to <code>win</code> as a child.</p> GtkTextBuffer instance is also created and connected to the GtkTextView
automatically. “GtkTextBuffer instance” will be referred to simply as
“GtkTextBuffer” or “buffer”. In the next line, the pointer to the buffer
is assigned to <code>tb</code>. Then, the text from line 10 to 20 is
assigned to the buffer. If the third argument of
<code>gtk_text_buffer_set_text</code> is a positive integer, it is the
length of the text. It it is -1, the string terminates with NULL.</p>
<p>GtkTextView has a wrap mode. When it is set to
<code>GTK_WRAP_WORD_CHAR</code>, text wraps in between words, or if that
is not enough, also between graphemes.</p>
<p>Wrap mode is written in <a
href="https://docs.gtk.org/gtk4/enum.WrapMode.html">Gtk_WrapMode</a> in
the GTK 4 API document.</p>
<p>In line 30, <code>tv</code> is added to <code>win</code> as a
child.</p>
<p>Now compile and run it.</p> <p>Now compile and run it.</p>
<pre><code>$ cd src/tfv
$ comp tfv1
$ ./a.out</code></pre>
<figure> <figure>
<img src="image/screenshot_tfv1.png" alt="" /><figcaption>GtkTextView</figcaption> <img src="image/screenshot_tfv1.png" alt="GtkTextView" />
<figcaption aria-hidden="true">GtkTextView</figcaption>
</figure> </figure>
<p>Theres an I-beam pointer in the window. You can add or delete any characters on the GtkTextView, and your changes are kept in the GtkTextBuffer. If you add more characters beyond the limit of the window, the height increases and the window extends. If the height gets bigger than the height of the display screen, you wont be able to control the size of the window, and change it back to the original size. This is a problem and shows that there is a bug in our program. This can solve it by adding a GtkScrolledWindow between the GtkApplicationWindow and GtkTextView.</p> <p>Theres an I-beam pointer in the window. You can add or delete any
characters on the GtkTextView, and your changes are kept in the
GtkTextBuffer. If you add more characters beyond the limit of the
window, the height increases and the window extends. If the height gets
bigger than the height of the display screen, you wont be able to
control the size of the window or change it back to the original size.
This is a problem, that is to say a bug. This can be solved by adding a
GtkScrolledWindow between the GtkApplicationWindow and GtkTextView.</p>
<h3 id="gtkscrolledwindow">GtkScrolledWindow</h3> <h3 id="gtkscrolledwindow">GtkScrolledWindow</h3>
<p>What we need to do is:</p> <p>What we need to do is:</p>
<ul> <ul>
<li>Create a GtkScrolledWindow and insert it as a child of the GtkApplicationWindow; and</li> <li>Create a GtkScrolledWindow and insert it as a child of the
<li>Insert the GtkTextView widget to the GtkScrolledWindow as a child.</li> GtkApplicationWindow</li>
<li>Insert the GtkTextView widget to the GtkScrolledWindow as a
child.</li>
</ul> </ul>
<p>Modify <code>tfv1.c</code> and save it as <code>tfv2.c</code>. The difference between these two files is small.</p> <p>Modify <code>tfv1.c</code> and save it as <code>tfv2.c</code>. There
is only a few difference between these two files.</p>
<pre><code>$ cd tfv; diff tfv1.c tfv2.c <pre><code>$ cd tfv; diff tfv1.c tfv2.c
5a6 5a6
&gt; GtkWidget *scr; &gt; GtkWidget *scr;
@ -187,60 +220,63 @@
--- ---
&gt; gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv); &gt; gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);
40c44 40c44
&lt; app = gtk_application_new (&quot;com.github.ToshioCP.tfv1&quot;, G_APPLICATION_FLAGS_NONE); &lt; app = gtk_application_new (&quot;com.github.ToshioCP.tfv1&quot;, G_APPLICATION_DEFAULT_FLAGS);
--- ---
&gt; app = gtk_application_new (&quot;com.github.ToshioCP.tfv2&quot;, G_APPLICATION_FLAGS_NONE);</code></pre> &gt; app = gtk_application_new (&quot;com.github.ToshioCP.tfv2&quot;, G_APPLICATION_DEFAULT_FLAGS);</code></pre>
<p>Here is the complete code of <code>tfv2.c</code>.</p> <p>The whole code of <code>tfv2.c</code> is as follows.</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="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span> <div class="sourceCode" id="cb4"><pre
<span id="cb3-2"><a href="#cb3-2"></a></span> class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1"></a><span class="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span>
<span id="cb3-3"><a href="#cb3-3"></a><span class="dt">static</span> <span class="dt">void</span></span> <span id="cb4-2"><a href="#cb4-2"></a></span>
<span id="cb3-4"><a href="#cb3-4"></a>app_activate (GApplication *app, gpointer user_data) {</span> <span id="cb4-3"><a href="#cb4-3"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb3-5"><a href="#cb3-5"></a> GtkWidget *win;</span> <span id="cb4-4"><a href="#cb4-4"></a>app_activate <span class="op">(</span>GApplication <span class="op">*</span>app<span class="op">)</span> <span class="op">{</span></span>
<span id="cb3-6"><a href="#cb3-6"></a> GtkWidget *scr;</span> <span id="cb4-5"><a href="#cb4-5"></a> GtkWidget <span class="op">*</span>win<span class="op">;</span></span>
<span id="cb3-7"><a href="#cb3-7"></a> GtkWidget *tv;</span> <span id="cb4-6"><a href="#cb4-6"></a> GtkWidget <span class="op">*</span>scr<span class="op">;</span></span>
<span id="cb3-8"><a href="#cb3-8"></a> GtkTextBuffer *tb;</span> <span id="cb4-7"><a href="#cb4-7"></a> GtkWidget <span class="op">*</span>tv<span class="op">;</span></span>
<span id="cb3-9"><a href="#cb3-9"></a> gchar *text;</span> <span id="cb4-8"><a href="#cb4-8"></a> GtkTextBuffer <span class="op">*</span>tb<span class="op">;</span></span>
<span id="cb3-10"><a href="#cb3-10"></a></span> <span id="cb4-9"><a href="#cb4-9"></a> gchar <span class="op">*</span>text<span class="op">;</span></span>
<span id="cb3-11"><a href="#cb3-11"></a> text =</span> <span id="cb4-10"><a href="#cb4-10"></a></span>
<span id="cb3-12"><a href="#cb3-12"></a> <span class="st">&quot;Once upon a time, there was an old man who was called Taketori-no-Okina. &quot;</span></span> <span id="cb4-11"><a href="#cb4-11"></a> text <span class="op">=</span></span>
<span id="cb3-13"><a href="#cb3-13"></a> <span class="st">&quot;It is a japanese word that means a man whose work is making bamboo baskets.</span><span class="sc">\n</span><span class="st">&quot;</span></span> <span id="cb4-12"><a href="#cb4-12"></a> <span class="st">&quot;Once upon a time, there was an old man who was called Taketori-no-Okina. &quot;</span></span>
<span id="cb3-14"><a href="#cb3-14"></a> <span class="st">&quot;One day, he went into a mountain and found a shining bamboo. &quot;</span></span> <span id="cb4-13"><a href="#cb4-13"></a> <span class="st">&quot;It is a japanese word that means a man whose work is making bamboo baskets.</span><span class="sc">\n</span><span class="st">&quot;</span></span>
<span id="cb3-15"><a href="#cb3-15"></a> <span class="st">&quot;</span><span class="sc">\&quot;</span><span class="st">What a mysterious bamboo it is!,</span><span class="sc">\&quot;</span><span class="st"> he said. &quot;</span></span> <span id="cb4-14"><a href="#cb4-14"></a> <span class="st">&quot;One day, he went into a mountain and found a shining bamboo. &quot;</span></span>
<span id="cb3-16"><a href="#cb3-16"></a> <span class="st">&quot;He cut it, then there was a small cute baby girl in it. &quot;</span></span> <span id="cb4-15"><a href="#cb4-15"></a> <span class="st">&quot;</span><span class="sc">\&quot;</span><span class="st">What a mysterious bamboo it is!,</span><span class="sc">\&quot;</span><span class="st"> he said. &quot;</span></span>
<span id="cb3-17"><a href="#cb3-17"></a> <span class="st">&quot;The girl was shining faintly. &quot;</span></span> <span id="cb4-16"><a href="#cb4-16"></a> <span class="st">&quot;He cut it, then there was a small cute baby girl in it. &quot;</span></span>
<span id="cb3-18"><a href="#cb3-18"></a> <span class="st">&quot;He thought this baby girl is a gift from Heaven and took her home.</span><span class="sc">\n</span><span class="st">&quot;</span></span> <span id="cb4-17"><a href="#cb4-17"></a> <span class="st">&quot;The girl was shining faintly. &quot;</span></span>
<span id="cb3-19"><a href="#cb3-19"></a> <span class="st">&quot;His wife was surprized at his tale. &quot;</span></span> <span id="cb4-18"><a href="#cb4-18"></a> <span class="st">&quot;He thought this baby girl is a gift from Heaven and took her home.</span><span class="sc">\n</span><span class="st">&quot;</span></span>
<span id="cb3-20"><a href="#cb3-20"></a> <span class="st">&quot;They were very happy because they had no children. &quot;</span></span> <span id="cb4-19"><a href="#cb4-19"></a> <span class="st">&quot;His wife was surprized at his story. &quot;</span></span>
<span id="cb3-21"><a href="#cb3-21"></a> ;</span> <span id="cb4-20"><a href="#cb4-20"></a> <span class="st">&quot;They were very happy because they had no children. &quot;</span></span>
<span id="cb3-22"><a href="#cb3-22"></a> win = gtk_application_window_new (GTK_APPLICATION (app));</span> <span id="cb4-21"><a href="#cb4-21"></a> <span class="op">;</span></span>
<span id="cb3-23"><a href="#cb3-23"></a> gtk_window_set_title (GTK_WINDOW (win), <span class="st">&quot;Taketori&quot;</span>);</span> <span id="cb4-22"><a href="#cb4-22"></a> win <span class="op">=</span> gtk_application_window_new <span class="op">(</span>GTK_APPLICATION <span class="op">(</span>app<span class="op">));</span></span>
<span id="cb3-24"><a href="#cb3-24"></a> gtk_window_set_default_size (GTK_WINDOW (win), <span class="dv">400</span>, <span class="dv">300</span>);</span> <span id="cb4-23"><a href="#cb4-23"></a> gtk_window_set_title <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="st">&quot;Taketori&quot;</span><span class="op">);</span></span>
<span id="cb3-25"><a href="#cb3-25"></a></span> <span id="cb4-24"><a href="#cb4-24"></a> gtk_window_set_default_size <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="dv">400</span><span class="op">,</span> <span class="dv">300</span><span class="op">);</span></span>
<span id="cb3-26"><a href="#cb3-26"></a> scr = gtk_scrolled_window_new ();</span> <span id="cb4-25"><a href="#cb4-25"></a></span>
<span id="cb3-27"><a href="#cb3-27"></a> gtk_window_set_child (GTK_WINDOW (win), scr);</span> <span id="cb4-26"><a href="#cb4-26"></a> scr <span class="op">=</span> gtk_scrolled_window_new <span class="op">();</span></span>
<span id="cb3-28"><a href="#cb3-28"></a></span> <span id="cb4-27"><a href="#cb4-27"></a> gtk_window_set_child <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> scr<span class="op">);</span></span>
<span id="cb3-29"><a href="#cb3-29"></a> tv = gtk_text_view_new ();</span> <span id="cb4-28"><a href="#cb4-28"></a></span>
<span id="cb3-30"><a href="#cb3-30"></a> tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));</span> <span id="cb4-29"><a href="#cb4-29"></a> tv <span class="op">=</span> gtk_text_view_new <span class="op">();</span></span>
<span id="cb3-31"><a href="#cb3-31"></a> gtk_text_buffer_set_text (tb, text, -<span class="dv">1</span>);</span> <span id="cb4-30"><a href="#cb4-30"></a> tb <span class="op">=</span> gtk_text_view_get_buffer <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
<span id="cb3-32"><a href="#cb3-32"></a> gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR);</span> <span id="cb4-31"><a href="#cb4-31"></a> gtk_text_buffer_set_text <span class="op">(</span>tb<span class="op">,</span> text<span class="op">,</span> <span class="op">-</span><span class="dv">1</span><span class="op">);</span></span>
<span id="cb3-33"><a href="#cb3-33"></a></span> <span id="cb4-32"><a href="#cb4-32"></a> gtk_text_view_set_wrap_mode <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> GTK_WRAP_WORD_CHAR<span class="op">);</span></span>
<span id="cb3-34"><a href="#cb3-34"></a> gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);</span> <span id="cb4-33"><a href="#cb4-33"></a></span>
<span id="cb3-35"><a href="#cb3-35"></a></span> <span id="cb4-34"><a href="#cb4-34"></a> gtk_scrolled_window_set_child <span class="op">(</span>GTK_SCROLLED_WINDOW <span class="op">(</span>scr<span class="op">),</span> tv<span class="op">);</span></span>
<span id="cb3-36"><a href="#cb3-36"></a> gtk_widget_show (win);</span> <span id="cb4-35"><a href="#cb4-35"></a></span>
<span id="cb3-37"><a href="#cb3-37"></a>}</span> <span id="cb4-36"><a href="#cb4-36"></a> gtk_widget_show <span class="op">(</span>win<span class="op">);</span></span>
<span id="cb3-38"><a href="#cb3-38"></a></span> <span id="cb4-37"><a href="#cb4-37"></a><span class="op">}</span></span>
<span id="cb3-39"><a href="#cb3-39"></a><span class="dt">int</span></span> <span id="cb4-38"><a href="#cb4-38"></a></span>
<span id="cb3-40"><a href="#cb3-40"></a>main (<span class="dt">int</span> argc, <span class="dt">char</span> **argv) {</span> <span id="cb4-39"><a href="#cb4-39"></a><span class="dt">int</span></span>
<span id="cb3-41"><a href="#cb3-41"></a> GtkApplication *app;</span> <span id="cb4-40"><a href="#cb4-40"></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="cb3-42"><a href="#cb3-42"></a> <span class="dt">int</span> stat;</span> <span id="cb4-41"><a href="#cb4-41"></a> GtkApplication <span class="op">*</span>app<span class="op">;</span></span>
<span id="cb3-43"><a href="#cb3-43"></a></span> <span id="cb4-42"><a href="#cb4-42"></a> <span class="dt">int</span> stat<span class="op">;</span></span>
<span id="cb3-44"><a href="#cb3-44"></a> app = gtk_application_new (<span class="st">&quot;com.github.ToshioCP.tfv2&quot;</span>, G_APPLICATION_FLAGS_NONE);</span> <span id="cb4-43"><a href="#cb4-43"></a></span>
<span id="cb3-45"><a href="#cb3-45"></a> g_signal_connect (app, <span class="st">&quot;activate&quot;</span>, G_CALLBACK (app_activate), NULL);</span> <span id="cb4-44"><a href="#cb4-44"></a> app <span class="op">=</span> gtk_application_new <span class="op">(</span><span class="st">&quot;com.github.ToshioCP.tfv2&quot;</span><span class="op">,</span> G_APPLICATION_DEFAULT_FLAGS<span class="op">);</span></span>
<span id="cb3-46"><a href="#cb3-46"></a> stat = g_application_run (G_APPLICATION (app), argc, argv);</span> <span id="cb4-45"><a href="#cb4-45"></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="cb3-47"><a href="#cb3-47"></a> g_object_unref (app);</span> <span id="cb4-46"><a href="#cb4-46"></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="cb3-48"><a href="#cb3-48"></a> <span class="cf">return</span> stat;</span> <span id="cb4-47"><a href="#cb4-47"></a> g_object_unref <span class="op">(</span>app<span class="op">);</span></span>
<span id="cb3-49"><a href="#cb3-49"></a>}</span></code></pre></div> <span id="cb4-48"><a href="#cb4-48"></a> <span class="cf">return</span> stat<span class="op">;</span></span>
<p>Compile and run it. Notice how this time the window doesnt extend when you type a lot of characters, it just scrolls and displays a slider.</p> <span id="cb4-49"><a href="#cb4-49"></a><span class="op">}</span></span></code></pre></div>
<p>Compile and run it.</p>
<p>Now, the window doesnt extend even if you type a lot of characters,
it just scrolls.</p>
</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> <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> </body>

View file

@ -111,18 +111,17 @@
</div> </div>
</div> </div>
</nav> </nav>
<h1 id="string-and-memory-management">String and memory management</h1> <h1 id="strings-and-memory-management">Strings and memory
management</h1>
<p>GtkTextView and GtkTextBuffer have functions that use string <p>GtkTextView and GtkTextBuffer have functions that use string
parameters or return a string. The knowledge of strings and memory parameters or return a string. The knowledge of strings and memory
management is useful to understand how to use these functions.</p> management is useful to understand how to use these functions.</p>
<h2 id="string-and-memory">String and memory</h2> <h2 id="string-and-memory">String and memory</h2>
<p>A String is an array of characters that is terminated with \0. <p>A String is an array of characters that is terminated with \0.
Strings are not a C type such as char, int, float or double, but exist String is not a C type such as char, int, float or double, but a
as a pointer to a character array. They behaves like a string type which character array. It behaves like a string in other languages. So, the
you may be familiar from other languages. So, this pointer is often pointer is often called a string.</p>
called a string.</p> <p>The following is a sample program.</p>
<p>In the following, <code>a</code> and <code>b</code> defined as
character arrays, and are strings.</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" tabindex="-1"></a><span class="dt">char</span> a<span class="op">[</span><span class="dv">10</span><span class="op">],</span> <span class="op">*</span>b<span class="op">;</span></span> <div class="sourceCode" id="cb1"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="dt">char</span> a<span class="op">[</span><span class="dv">10</span><span class="op">],</span> <span class="op">*</span>b<span class="op">;</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a></span> <span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>a<span class="op">[</span><span class="dv">0</span><span class="op">]</span> <span class="op">=</span> <span class="ch">&#39;H&#39;</span><span class="op">;</span></span> <span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>a<span class="op">[</span><span class="dv">0</span><span class="op">]</span> <span class="op">=</span> <span class="ch">&#39;H&#39;</span><span class="op">;</span></span>
@ -135,97 +134,101 @@ character arrays, and are strings.</p>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a>b <span class="op">=</span> a<span class="op">;</span></span> <span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a>b <span class="op">=</span> a<span class="op">;</span></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a><span class="co">/* *b is &#39;H&#39; */</span></span> <span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a><span class="co">/* *b is &#39;H&#39; */</span></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a><span class="co">/* *(++b) is &#39;e&#39; */</span></span></code></pre></div> <span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a><span class="co">/* *(++b) is &#39;e&#39; */</span></span></code></pre></div>
<p>The array <code>a</code> has <code>char</code> elements and the size <p>An array <code>a</code> is defined as a <code>char</code> type array
of ten. The first six elements are H, e, l, l, o and \0. and its size is ten. The first five elements are H, e, l, l,
This array represents the string “Hello”. The first five elements are o. They are character codes. For example, H is the same as 0x48 or
character codes that correspond to the characters. The sixth element is 72. The sixth element is \0, which is the same as zero, and indicates
\0, which is the same as zero, and indicates that the string ends that the sequence of the data ends there. The array represents the
there. The size of the array is 10, so 4 bytes arent used, but thats string “Hello”.</p>
OK, they are just ignored.</p> <p>The size of the array is 10, so 4 bytes arent used. But its OK.
<p>The variable b is a pointer to a character. Because <code>b</code> They are just ignored. (If a is defined out of functions or its class
is assigned to be <code>a</code>, <code>a</code> and <code>b</code> is static, they are assigned with zero. Otherwise, that is to say, the
point the same character (H). The variable <code>a</code> is defined class is auto or register, they are undefined.)</p>
as an array and it cant be changed. It always point the top address of <p>The variable b is a pointer to a character. It is assigned with
the array. On the other hand, b is a pointer, which is mutable, so <code>a</code>, so <code>b</code> points the first element of
<code>b</code> can be change. It is then possible to write statements <code>a</code> (character H). The array <code>a</code> is immutable.
like <code>++b</code>, which means take the value in b (n address), So <code>a=a+1</code> causes syntax error.</p>
increase it by one, and store that back in <code>b</code>.</p> <p>On the other hand, b is a pointer type variable, which is mutable.
<p>If a pointer is NULL, it points to nothing. So, the pointer is not a So, <code>++b</code>, which increases <code>b</code> by one, is
string. A NULL string on the other hand will be a pointer which points allowed.</p>
to a location that contains <code>\0</code>, which is a string of length <p>If a pointer is NULL, it points nothing. So, the pointer is not a
0 (or ““). Programs that use strings will include bugs if you arent string. It is different from empty string. Empty string is a pointer
careful when using NULL pointers.</p> points <code>\0</code>.</p>
<p>Another annoying problem is the memory that a string is allocated. <p>There are four cases:</p>
There are four cases:</p>
<ul> <ul>
<li>The string is read only;</li> <li>The string is read only</li>
<li>The string is in static memory area;</li> <li>The string is in static memory area</li>
<li>The string is in stack; and</li> <li>The string is in stack</li>
<li>The string is in memory allocated from the heap area.</li> <li>The string is in memory allocated from the heap area</li>
</ul> </ul>
<h2 id="read-only-string">Read only string</h2> <h2 id="read-only-string">Read only string</h2>
<p>A string literal in a C program is surrounded by double quotes and <p>A string literal is surrounded by double quotes like this:</p>
written as the following:</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" tabindex="-1"></a><span class="dt">char</span> <span class="op">*</span>s<span class="op">;</span></span> <div class="sourceCode" id="cb2"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="dt">char</span> <span class="op">*</span>s<span class="op">;</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>s <span class="op">=</span> <span class="st">&quot;Hello&quot;</span></span></code></pre></div> <span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>s <span class="op">=</span> <span class="st">&quot;Hello&quot;</span></span></code></pre></div>
<p>“Hello” is a string literal, and is stored in program memory. A <p>“Hello” is a string literal, and is read only. So, the following
string literal is read only. In the program above, <code>s</code> points program is illegal.</p>
the string literal.</p>
<p>So, the following program is illegal.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="op">*(</span>s<span class="op">+</span><span class="dv">1</span><span class="op">)</span> <span class="op">=</span> <span class="ch">&#39;a&#39;</span><span class="op">;</span></span></code></pre></div> <div class="sourceCode" id="cb3"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="op">*(</span>s<span class="op">+</span><span class="dv">1</span><span class="op">)</span> <span class="op">=</span> <span class="ch">&#39;a&#39;</span><span class="op">;</span></span></code></pre></div>
<p>The result is undefined. Probably a bad thing will happen, for <p>The result is undefined. Probably a bad thing will happen, for
example, a segmentation fault.</p> example, a segmentation fault.</p>
<p>NOTE: The memory of the literal string is allocated when the program <p>NOTE: The memory of the literal string is allocated when the program
is compiled. It is possible to view all the literal strings defined in is compiled. It is possible to see the literal strings with
your program by using the <code>string</code> command.</p> <code>strings</code> command.</p>
<pre><code>$ strings src/tvf/a.out
/lib64/ld-linux-x86-64.so.2
cN&lt;5
... ... ...
... ... ...
Once upon a time, there was an old man who was called Taketori-no-Okina. It is a japanese word that means a man whose work is making bamboo baskets.
One day, he went into a mountain and found a shining bamboo. &quot;What a mysterious bamboo it is!,&quot; he said. He cut it, then there was a small cute baby girl in it. The girl was shining faintly. He thought this baby girl is a gift from Heaven and took her home.
His wife was surprized at his story. They were very happy because they had no children.
... ... ...
... ... ...</code></pre>
<p>It tells us that literal strings are embedded in program binary
codes.</p>
<h2 id="strings-defined-as-arrays">Strings defined as arrays</h2> <h2 id="strings-defined-as-arrays">Strings defined as arrays</h2>
<p>If a string is defined as an array, its in either stored in the <p>If a string is defined as an array, its stored in static memory area
static memory area or stack. This depends on the class of the array. If or stack. It depends on the class of the array. If the arrays class is
the arrays class is <code>static</code>, then its placed in static <code>static</code>, then its placed in static memory area. The
memory area. This allocation and memory address is fixed for the life of allocated memory lives for the life of the program. This area is
the program. This area can be changed and is writable.</p> writable.</p>
<p>If the arrays class is <code>auto</code>, then its placed in stack. <p>If the arrays class is <code>auto</code>, its placed in stack. If
If the array is defined inside a function, its default class is the array is defined inside a function, its default class is
<code>auto</code>. The stack area will disappear when the function exits <code>auto</code>. The stack area will disappear when the function
and returns to the caller. Arrays defined on the stack are writable.</p> returns to the caller. Arrays defined on the stack are writable.</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></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><span class="dt">static</span> <span class="dt">char</span> a<span class="op">[]</span> <span class="op">=</span> <span class="op">{</span><span class="ch">&#39;H&#39;</span><span class="op">,</span> <span class="ch">&#39;e&#39;</span><span class="op">,</span> <span class="ch">&#39;l&#39;</span><span class="op">,</span> <span class="ch">&#39;l&#39;</span><span class="op">,</span> <span class="ch">&#39;o&#39;</span><span class="op">,</span> <span class="ch">&#39;\0&#39;</span><span class="op">};</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">char</span> a<span class="op">[]</span> <span class="op">=</span> <span class="op">{</span><span class="ch">&#39;H&#39;</span><span class="op">,</span> <span class="ch">&#39;e&#39;</span><span class="op">,</span> <span class="ch">&#39;l&#39;</span><span class="op">,</span> <span class="ch">&#39;l&#39;</span><span class="op">,</span> <span class="ch">&#39;o&#39;</span><span class="op">,</span> <span class="ch">&#39;\0&#39;</span><span class="op">};</span></span> <span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a></span> <span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span></span> <span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>print_strings <span class="op">(</span><span class="dt">void</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>print_strings <span class="op">(</span><span class="dt">void</span><span class="op">)</span> <span class="op">{</span></span> <span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">char</span> b<span class="op">[]</span> <span class="op">=</span> <span class="st">&quot;Hello&quot;</span><span class="op">;</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">char</span> b<span class="op">[]</span> <span class="op">=</span> <span class="st">&quot;Hello&quot;</span><span class="op">;</span></span> <span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a></span> <span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a> a<span class="op">[</span><span class="dv">1</span><span class="op">]</span> <span class="op">=</span> <span class="ch">&#39;a&#39;</span><span class="op">;</span> <span class="co">/* Because the array is static, it&#39;s writable. */</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a> a<span class="op">[</span><span class="dv">1</span><span class="op">]</span> <span class="op">=</span> <span class="ch">&#39;a&#39;</span><span class="op">;</span> <span class="co">/* Because the array is static, it&#39;s writable. */</span></span> <span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a> b<span class="op">[</span><span class="dv">1</span><span class="op">]</span> <span class="op">=</span> <span class="ch">&#39;a&#39;</span><span class="op">;</span> <span class="co">/* Because the array is auto, it&#39;s writable. */</span></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a> b<span class="op">[</span><span class="dv">1</span><span class="op">]</span> <span class="op">=</span> <span class="ch">&#39;a&#39;</span><span class="op">;</span> <span class="co">/* Because the array is auto, it&#39;s writable. */</span></span> <span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a></span> <span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a> printf <span class="op">(</span><span class="st">&quot;%s</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">,</span> a<span class="op">);</span> <span class="co">/* Hallo */</span></span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a> printf <span class="op">(</span><span class="st">&quot;%s</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">,</span> a<span class="op">);</span> <span class="co">/* Hallo */</span></span> <span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a> printf <span class="op">(</span><span class="st">&quot;%s</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">,</span> b<span class="op">);</span> <span class="co">/* Hallo */</span></span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a> printf <span class="op">(</span><span class="st">&quot;%s</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">,</span> b<span class="op">);</span> <span class="co">/* Hallo */</span></span> <span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div> <p>The array <code>a</code> is defined out of functions. It is placed in
<p>The array <code>a</code> is defined externally to a function and is the static memory area even if the <code>static</code> class is left
global in its scope. Such variables are placed in static memory area out. The compiler calculates the number of the elements (six) and
even if the <code>static</code> class is left out. The compiler allocates six bytes in the static memory area. Then, it copies “Hello”
calculates the number of the elements in the right hand side (six), and literal string data to the memory.</p>
then creates code that allocates six bytes in the static memory area and <p>The array <code>b</code> is defined inside the function, so its class
copies the data to this memory.</p>
<p>The array <code>b</code> is defined inside the function so its class
is <code>auto</code>. The compiler calculates the number of the elements is <code>auto</code>. The compiler calculates the number of the elements
in the string literal. It has six elements as the zero termination in the string literal. It is six because it has \0 terminator. The
character is also included. The compiler creates code which allocates compiler allocates six bytes in the stack and copies “Hello” litaral
six bytes memory in the stack and copies the data to the memory.</p> string to the stack memory.</p>
<p>Both <code>a</code> and <code>b</code> are writable.</p> <p>Both <code>a</code> and <code>b</code> are writable.</p>
<p>The memory is managed by the executable program. You dont need your <p>The memory is allocated and freed by the program automatically so you
program to allocate or free the memory for <code>a</code> and dont need to allocate or free. The array <code>a</code> is alive during
<code>b</code>. The array <code>a</code> is created then the program is the programs life time. The array <code>b</code> is alive when the
first run and remains for the life of the program. The array function is called until the function returns to the caller.</p>
<code>b</code> is created on the stack then the function is called,
disappears when the function returns.</p>
<h2 id="strings-in-the-heap-area">Strings in the heap area</h2> <h2 id="strings-in-the-heap-area">Strings in the heap area</h2>
<p>You can also get, use and release memory from the heap area. The <p>You can get, use and release memory from the heap area. The standard
standard C library provides <code>malloc</code> to get memory and C library provides <code>malloc</code> to get memory and
<code>free</code> to put back memory. GLib provides the functions <code>free</code> to put back memory. GLib provides the functions
<code>g_new</code> and <code>g_free</code> to do the same thing, with <code>g_new</code> and <code>g_free</code>. They are similar to
support for some additional GLib functionality.</p> <code>malloc</code> and <code>free</code>.</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>g_new <span class="op">(</span>struct_type<span class="op">,</span> n_struct<span class="op">)</span></span></code></pre></div> <div class="sourceCode" id="cb6"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a>g_new <span class="op">(</span>struct_type<span class="op">,</span> n_struct<span class="op">)</span></span></code></pre></div>
<p><code>g_new</code> is a macro to allocate memory for an array.</p> <p><code>g_new</code> is a macro to allocate memory for an array.</p>
<ul> <ul>
<li><code>struct_type</code> is the type of the element of the <li><code>struct_type</code> is the type of the element of the
@ -235,37 +238,37 @@ array.</li>
<code>struct_type</code>.</li> <code>struct_type</code>.</li>
</ul> </ul>
<p>For example,</p> <p>For example,</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" tabindex="-1"></a><span class="dt">char</span> <span class="op">*</span>s<span class="op">;</span></span> <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">char</span> <span class="op">*</span>s<span class="op">;</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>s <span class="op">=</span> g_new <span class="op">(</span><span class="dt">char</span><span class="op">,</span> <span class="dv">10</span><span class="op">);</span></span> <span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>s <span class="op">=</span> g_new <span class="op">(</span><span class="dt">char</span><span class="op">,</span> <span class="dv">10</span><span class="op">);</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a><span class="co">/* s points an array of char. The size of the array is 10. */</span></span> <span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a><span class="co">/* s points an array of char. The size of the array is 10. */</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a></span> <span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> tuple <span class="op">{</span><span class="dt">int</span> x<span class="op">,</span> y<span class="op">;}</span> <span class="op">*</span>t<span class="op">;</span></span> <span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> tuple <span class="op">{</span><span class="dt">int</span> x<span class="op">,</span> y<span class="op">;}</span> <span class="op">*</span>t<span class="op">;</span></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a>t <span class="op">=</span> g_new <span class="op">(</span><span class="kw">struct</span> tuple<span class="op">,</span> <span class="dv">5</span><span class="op">);</span></span> <span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a>t <span class="op">=</span> g_new <span class="op">(</span><span class="kw">struct</span> tuple<span class="op">,</span> <span class="dv">5</span><span class="op">);</span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a><span class="co">/* t points an array of struct tuple. */</span></span> <span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a><span class="co">/* t points an array of struct tuple. */</span></span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a><span class="co">/* The size of the array is 5. */</span></span></code></pre></div> <span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a><span class="co">/* The size of the array is 5. */</span></span></code></pre></div>
<p><code>g_free</code> frees memory.</p> <p><code>g_free</code> frees memory.</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><span class="dt">void</span></span> <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><span class="dt">void</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>g_free <span class="op">(</span>gpointer mem<span class="op">);</span></span></code></pre></div> <span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a>g_free <span class="op">(</span>gpointer mem<span class="op">);</span></span></code></pre></div>
<p>If <code>mem</code> is NULL, <code>g_free</code> does nothing. <p>If <code>mem</code> is NULL, <code>g_free</code> does nothing.
<code>gpointer</code> is a type of general pointer. It is the same as <code>gpointer</code> is a type of general pointer. It is the same as
<code>void *</code>. This pointer can be casted to any pointer type. <code>void *</code>. This pointer can be casted to any pointer type.
Conversely, any pointer type can be casted to <code>gpointer</code>.</p> Conversely, any pointer type can be casted to <code>gpointer</code>.</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>g_free <span class="op">(</span>s<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>g_free <span class="op">(</span>s<span class="op">);</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="co">/* Frees the memory allocated to s. */</span></span> <span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a><span class="co">/* Frees the memory allocated to s. */</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a></span> <span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a>g_free <span class="op">(</span>t<span class="op">);</span></span> <span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a>g_free <span class="op">(</span>t<span class="op">);</span></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a><span class="co">/* Frees the memory allocated to t. */</span></span></code></pre></div> <span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a><span class="co">/* Frees the memory allocated to t. */</span></span></code></pre></div>
<p>If the argument doesnt point allocated memory it will cause an <p>If the argument doesnt point allocated memory it will cause an
error, specifically, a segmentation fault.</p> error, specifically, a segmentation fault.</p>
<p>Some GLib functions allocate memory. For example, <p>Some GLib functions allocate memory. For example,
<code>g_strdup</code> allocates memory and copies a string given as an <code>g_strdup</code> allocates memory and copies a string given as an
argument.</p> argument.</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">char</span> <span class="op">*</span>s<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>s<span class="op">;</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a>s <span class="op">=</span> g_strdup <span class="op">(</span><span class="st">&quot;Hello&quot;</span><span class="op">);</span></span> <span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a>s <span class="op">=</span> g_strdup <span class="op">(</span><span class="st">&quot;Hello&quot;</span><span class="op">);</span></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a>g_free <span class="op">(</span>s<span class="op">);</span></span></code></pre></div> <span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a>g_free <span class="op">(</span>s<span class="op">);</span></span></code></pre></div>
<p>The string literal “Hello” has 6 bytes because the string has \0 at <p>The string literal “Hello” has 6 bytes because the string has \0 at
the end it. <code>g_strdup</code> gets 6 bytes from the heap area and the end. <code>g_strdup</code> gets 6 bytes from the heap area and
copies the string to the memory. <code>s</code> is assigned the top copies the string to the memory. <code>s</code> is assigned the start
address of the memory. <code>g_free</code> returns the memory to the address of the memory. <code>g_free</code> returns the memory to the
heap area.</p> heap area.</p>
<p><code>g_strdup</code> is described in <a <p><code>g_strdup</code> is described in <a
@ -275,26 +278,31 @@ Reference</a>. The following is extracted from the reference.</p>
<p>The returned string should be freed with <code>g_free()</code> when <p>The returned string should be freed with <code>g_free()</code> when
no longer needed.</p> no longer needed.</p>
</blockquote> </blockquote>
<p>The function reference will describe if the returned value needs to <p>If you forget to free the allocated memory it will remain until the
be freed. If you forget to free the allocated memory it will remain program ends. Repeated allocation and no freeing cause memory leak. It
allocated. Repeated use will cause more memory to be allocated to the is a bug and may bring a serious problem.</p>
program, which will grow over time. This is called a memory leak, and <h2 id="const-qualifier">const qualifier</h2>
the only way to address this bug is to close the program (and restart <p>A <code>const</code> qualified variable can be assigned to initialize
it), which will automatically release all of the programs memory back to it. Once it is initialized, it is never allowed to change or free.</p>
the system.</p>
<p>Some GLib functions return a string which mustnt be freed by the
caller.</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>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a>g_quark_to_string <span class="op">(</span>GQuark quark<span class="op">);</span></span></code></pre></div>
<p>This function returns <code>const char*</code> type. The qualifier
<code>const</code> means that the returned value is immutable. The
characters pointed by the returned value arent be allowed to be changed
or freed.</p>
<p>If a variable is qualified with <code>const</code>, the variable
cant be assigned except during initialization.</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><span class="dt">const</span> <span class="dt">int</span> x <span class="op">=</span> <span class="dv">10</span><span class="op">;</span> <span class="co">/* initialization is OK. */</span></span> <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><span class="dt">const</span> <span class="dt">int</span> x <span class="op">=</span> <span class="dv">10</span><span class="op">;</span> <span class="co">/* initialization is OK. */</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a></span> <span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a>x <span class="op">=</span> <span class="dv">20</span><span class="op">;</span> <span class="co">/* This is illegal because x is qualified with const */</span></span></code></pre></div> <span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a>x <span class="op">=</span> <span class="dv">20</span><span class="op">;</span> <span class="co">/* This is illegal because x is qualified with const */</span></span></code></pre></div>
<p>If a function returns <code>const char*</code> type, the string cant
be changed or freed. If a function has a <code>const char *</code> type
parameter, it ensures that the parameter is not changed in the
function.</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" tabindex="-1"></a><span class="co">// You never change or free the returned string.</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a><span class="dt">const</span> <span class="dt">char</span><span class="op">*</span></span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a>gtk_label_get_text <span class="op">(</span></span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a> GtkLabel<span class="op">*</span> self</span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a><span class="op">)</span></span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true" tabindex="-1"></a><span class="co">// Str keeps itself during the function runs</span></span>
<span id="cb12-8"><a href="#cb12-8" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span></span>
<span id="cb12-9"><a href="#cb12-9" aria-hidden="true" tabindex="-1"></a>gtk_label_set_text <span class="op">(</span></span>
<span id="cb12-10"><a href="#cb12-10" aria-hidden="true" tabindex="-1"></a> GtkLabel<span class="op">*</span> self<span class="op">,</span></span>
<span id="cb12-11"><a href="#cb12-11" aria-hidden="true" tabindex="-1"></a> <span class="dt">const</span> <span class="dt">char</span><span class="op">*</span> str</span>
<span id="cb12-12"><a href="#cb12-12" aria-hidden="true" tabindex="-1"></a><span class="op">)</span></span></code></pre></div>
</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> <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> </body>

View file

@ -5,7 +5,7 @@
<meta name="generator" content="pandoc" /> <meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <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"> <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> <title>GTK 4 tutorial</title>
<style> <style>
code{white-space: pre-wrap;} code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;} span.smallcaps{font-variant: small-caps;}
@ -113,252 +113,354 @@
</nav> </nav>
<h1 id="widgets-3">Widgets (3)</h1> <h1 id="widgets-3">Widgets (3)</h1>
<h2 id="open-signal">Open signal</h2> <h2 id="open-signal">Open signal</h2>
<h3 id="g_application_handles_open-flag">G_APPLICATION_HANDLES_OPEN flag</h3> <h3 id="g_application_handles_open-flag">G_APPLICATION_HANDLES_OPEN
<p>The GtkTextView, GtkTextBuffer and GtkScrolledWindow widgets have given us a minimum editor in the previous section. We will now add a function to read a file and rework the program into a file viewer. There are many ways to implement the function and because this is a tutorial for beginners, well take the easiest one.</p> flag</h3>
<p>When the program starts, we will give the filename to open as an argument.</p> <p>We made a very simple editor in the previous section with
GtkTextView, GtkTextBuffer and GtkScrolledWindow. We will add
file-reading ability to the program and improve it to a file viewer.</p>
<p>The easiest way to give a filename is to use a command line
argument.</p>
<pre><code>$ ./a.out filename</code></pre> <pre><code>$ ./a.out filename</code></pre>
<p>It will open the file and insert its contents into the GtkTextBuffer.</p> <p>The program will open the file and insert its contents into the
<p>To do this, we need to know how GtkApplication (or GApplication) recognizes arguments. This is described in the <a href="https://docs.gtk.org/gio/class.Application.html">GIO API Reference, Application</a>.</p> GtkTextBuffer.</p>
<p>When GtkApplication is created, a flag (with the type GApplicationFlags) is provided as an argument.</p> <p>To do this, we need to know how GtkApplication (or GApplication)
<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>GtkApplication *</span> recognizes arguments. This is described in the <a
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true"></a>gtk_application_new (<span class="dt">const</span> gchar *application_id, GApplicationFlags flags);</span></code></pre></div> href="https://docs.gtk.org/gio/class.Application.html">GIO API Reference
<p>This tutorial explains only two flags, <code>G_APPLICATION_FLAGS_NONE</code> and <code>G_APPLICATION_HANDLES_OPEN</code>. If you want to handle command line arguments, the <code>G_APPLICATION_HANDLES_COMMAND_LINE</code> flag is what you need. How to use the new application method is described in <a href="https://docs.gtk.org/gio/method.Application.run.html">GIO API Reference, g_application_run</a>, and the flag is described in the <a href="https://docs.gtk.org/gio/flags.ApplicationFlags.html">GIO API Reference, ApplicationFlags</a>.</p> Application</a>.</p>
<pre><code>GApplicationFlags&#39; Members <p>When GtkApplication is created, a flag (GApplicationFlags) is given
as an argument.</p>
G_APPLICATION_FLAGS_NONE Default. (No argument allowed) <div class="sourceCode" id="cb2"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>GtkApplication <span class="op">*</span></span>
... ... ... <span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>gtk_application_new <span class="op">(</span><span class="dt">const</span> gchar <span class="op">*</span>application_id<span class="op">,</span> GApplicationFlags flags<span class="op">);</span></span></code></pre></div>
G_APPLICATION_HANDLES_OPEN This application handles opening files (in the primary instance). <p>This tutorial explains only two flags,
... ... ...</code></pre> <code>G_APPLICATION_DEFAULT_FLAGS</code> and
<p>There are ten flags in total, but we only need two of them so far. Weve already used <code>G_APPLICATION_FLAGS_NONE</code>, as it is the simplest option, and no arguments are allowed. If you provide arguments when running the application, an error will occur.</p> <code>G_APPLICATION_HANDLES_OPEN</code>.
<p>The flag <code>G_APPLICATION_HANDLES_OPEN</code> is the second simplest option. It allows arguments but only files. The application assumes all the arguments are filenames and we will use this flag when creating our GtkApplication.</p> (<code>G_APPLICATION_FLAGS_NONE</code> was used instead of
<div class="sourceCode" id="cb4"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true"></a>app = gtk_application_new (<span class="st">&quot;com.github.ToshioCP.tfv3&quot;</span>, G_APPLICATION_HANDLES_OPEN);</span></code></pre></div> <code>G_APPLICATION_DEFAULT_FLAGS</code> before GIO version2.73.3 (GLib
2.73.3 5/Aug/2022). Some GTK 4 applications still use
<code>G_APPLICATION_FLAGS_NONE</code>. But now it is deprecated and
<code>G_APPLICATION_DEFAULT_FLAGS</code> is recommended.) If you want to
handle command line arguments, the
<code>G_APPLICATION_HANDLES_COMMAND_LINE</code> flag is what you
need.</p>
<p>For further information, see <a
href="https://docs.gtk.org/gio/flags.ApplicationFlags.html">GIO API
Reference ApplicationFlags</a> and <a
href="https://docs.gtk.org/gio/method.Application.run.html">GIO API
Reference g_application_run</a>.</p>
<p>Weve already used <code>G_APPLICATION_DEFAULT_FLAGS</code>, as it is
the simplest option, and no command line arguments are allowed. If you
give arguments, an error will occur.</p>
<p>The flag <code>G_APPLICATION_HANDLES_OPEN</code> is the second
simplest option. It allows arguments but only files. The application
assumes all the arguments are filenames.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>app <span class="op">=</span> gtk_application_new <span class="op">(</span><span class="st">&quot;com.github.ToshioCP.tfv3&quot;</span><span class="op">,</span> G_APPLICATION_HANDLES_OPEN<span class="op">);</span></span></code></pre></div>
<h3 id="open-signal-1">open signal</h3> <h3 id="open-signal-1">open signal</h3>
<p>Now, when the application starts, two signals can be emitted.</p> <p>When <code>G_APPLICATION_HANDLES_OPEN</code> flag is given to the
application, two signals are available.</p>
<ul> <ul>
<li>activate signal — This signal is emitted when theres no argument.</li> <li>activate signal — This signal is emitted when theres no
<li>open signal — This signal is emitted when there is at least one argument.</li> argument.</li>
<li>open signal — This signal is emitted when there is at least one
argument.</li>
</ul> </ul>
<p>The handler of the “open” signal is defined as follows.</p> <p>The handler of the “open” signal is defined as follows.</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"></a><span class="dt">void</span> user_function (GApplication *application,</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><span class="dt">void</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true"></a> gpointer files,</span> <span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>open <span class="op">(</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true"></a> gint n_files,</span> <span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a> GApplication<span class="op">*</span> self<span class="op">,</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true"></a> gchar *hint,</span> <span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a> gpointer files<span class="op">,</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true"></a> gpointer user_data)</span></code></pre></div> <span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a> gint n_files<span class="op">,</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a> gchar<span class="op">*</span> hint<span class="op">,</span></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a> gpointer user_data</span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a><span class="op">)</span></span></code></pre></div>
<p>The parameters are:</p> <p>The parameters are:</p>
<ul> <ul>
<li><code>application</code> — the application (usually GtkApplication)</li> <li><code>self</code> — the application instance (usually
<li><code>files</code> — an array of GFiles. [array length=n_files] [element-type GFile]</li> GtkApplication)</li>
<li><code>n_files</code> — the number of the elements of <code>files</code></li> <li><code>files</code> — an array of GFiles. [array length=n_files]
<li><code>hint</code> — a hint provided by the calling instance (usually it can be ignored)</li> [element-type GFile]</li>
<li><code>user_data</code> — user data set when the signal handler was connected.</li> <li><code>n_files</code> — the number of the elements of
<code>files</code></li>
<li><code>hint</code> — a hint provided by the calling instance (usually
it can be ignored)</li>
<li><code>user_data</code> — user data set when the signal handler was
connected.</li>
</ul> </ul>
<p>How to read a specified file (GFile) will be described next.</p>
<h2 id="making-a-file-viewer">Making a file viewer</h2> <h2 id="making-a-file-viewer">Making a file viewer</h2>
<h3 id="what-is-a-file-viewer">What is a file viewer?</h3> <h3 id="what-is-a-file-viewer">What is a file viewer?</h3>
<p>A file viewer is a program that displays the text file that is given as an argument. Our file viewer will work as follows.</p> <p>A file viewer is a program that displays text files. Our file viewer
will work as follows.</p>
<ul> <ul>
<li>When arguments are given, it treats the first argument as a filename and opens it.</li> <li>When arguments are given, it recognizes the first argument as a
<li>If opening the file succeeds, it reads the contents of the file and inserts it to GtkTextBuffer and then shows the window.</li> filename and opens it.</li>
<li>If it fails to open the file, it will show an error message and quit.</li> <li>The second argument and after are ignored.</li>
<li>If theres no argument, it will shows an error message and quit.</li> <li>If theres no argument, it shows an error message and quit.</li>
<li>If there are two or more arguments, the second one and any others are ignored.</li> <li>If it successfully opens the file, it reads the contents of the
file, inserts them to GtkTextBuffer and shows the window.</li>
<li>If it fails to open the file, it shows an error message and
quit.</li>
</ul> </ul>
<p>The program which does this is shown below.</p> <p>The program is shown below.</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="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span> <div class="sourceCode" id="cb5"><pre
<span id="cb6-2"><a href="#cb6-2"></a></span> class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb5-1"><a href="#cb5-1"></a><span class="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span>
<span id="cb6-3"><a href="#cb6-3"></a><span class="dt">static</span> <span class="dt">void</span></span> <span id="cb5-2"><a href="#cb5-2"></a></span>
<span id="cb6-4"><a href="#cb6-4"></a>app_activate (GApplication *app, gpointer user_data) {</span> <span id="cb5-3"><a href="#cb5-3"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb6-5"><a href="#cb6-5"></a> g_print (<span class="st">&quot;You need a filename argument.</span><span class="sc">\n</span><span class="st">&quot;</span>);</span> <span id="cb5-4"><a href="#cb5-4"></a>app_activate <span class="op">(</span>GApplication <span class="op">*</span>app<span class="op">)</span> <span class="op">{</span></span>
<span id="cb6-6"><a href="#cb6-6"></a>}</span> <span id="cb5-5"><a href="#cb5-5"></a> g_printerr <span class="op">(</span><span class="st">&quot;You need a filename argument.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb6-7"><a href="#cb6-7"></a></span> <span id="cb5-6"><a href="#cb5-6"></a><span class="op">}</span></span>
<span id="cb6-8"><a href="#cb6-8"></a><span class="dt">static</span> <span class="dt">void</span></span> <span id="cb5-7"><a href="#cb5-7"></a></span>
<span id="cb6-9"><a href="#cb6-9"></a>app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer user_data) {</span> <span id="cb5-8"><a href="#cb5-8"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb6-10"><a href="#cb6-10"></a> GtkWidget *win;</span> <span id="cb5-9"><a href="#cb5-9"></a>app_open <span class="op">(</span>GApplication <span class="op">*</span>app<span class="op">,</span> GFile <span class="op">**</span> files<span class="op">,</span> <span class="dt">int</span> n_files<span class="op">,</span> <span class="dt">char</span> <span class="op">*</span>hint<span class="op">)</span> <span class="op">{</span></span>
<span id="cb6-11"><a href="#cb6-11"></a> GtkWidget *scr;</span> <span id="cb5-10"><a href="#cb5-10"></a> GtkWidget <span class="op">*</span>win<span class="op">;</span></span>
<span id="cb6-12"><a href="#cb6-12"></a> GtkWidget *tv;</span> <span id="cb5-11"><a href="#cb5-11"></a> GtkWidget <span class="op">*</span>scr<span class="op">;</span></span>
<span id="cb6-13"><a href="#cb6-13"></a> GtkTextBuffer *tb;</span> <span id="cb5-12"><a href="#cb5-12"></a> GtkWidget <span class="op">*</span>tv<span class="op">;</span></span>
<span id="cb6-14"><a href="#cb6-14"></a> <span class="dt">char</span> *contents;</span> <span id="cb5-13"><a href="#cb5-13"></a> GtkTextBuffer <span class="op">*</span>tb<span class="op">;</span></span>
<span id="cb6-15"><a href="#cb6-15"></a> gsize length;</span> <span id="cb5-14"><a href="#cb5-14"></a> <span class="dt">char</span> <span class="op">*</span>contents<span class="op">;</span></span>
<span id="cb6-16"><a href="#cb6-16"></a> <span class="dt">char</span> *filename;</span> <span id="cb5-15"><a href="#cb5-15"></a> gsize length<span class="op">;</span></span>
<span id="cb6-17"><a href="#cb6-17"></a></span> <span id="cb5-16"><a href="#cb5-16"></a> <span class="dt">char</span> <span class="op">*</span>filename<span class="op">;</span></span>
<span id="cb6-18"><a href="#cb6-18"></a> win = gtk_application_window_new (GTK_APPLICATION (app));</span> <span id="cb5-17"><a href="#cb5-17"></a></span>
<span id="cb6-19"><a href="#cb6-19"></a> gtk_window_set_default_size (GTK_WINDOW (win), <span class="dv">400</span>, <span class="dv">300</span>);</span> <span id="cb5-18"><a href="#cb5-18"></a> win <span class="op">=</span> gtk_application_window_new <span class="op">(</span>GTK_APPLICATION <span class="op">(</span>app<span class="op">));</span></span>
<span id="cb6-20"><a href="#cb6-20"></a></span> <span id="cb5-19"><a href="#cb5-19"></a> gtk_window_set_default_size <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="dv">400</span><span class="op">,</span> <span class="dv">300</span><span class="op">);</span></span>
<span id="cb6-21"><a href="#cb6-21"></a> scr = gtk_scrolled_window_new ();</span> <span id="cb5-20"><a href="#cb5-20"></a></span>
<span id="cb6-22"><a href="#cb6-22"></a> gtk_window_set_child (GTK_WINDOW (win), scr);</span> <span id="cb5-21"><a href="#cb5-21"></a> scr <span class="op">=</span> gtk_scrolled_window_new <span class="op">();</span></span>
<span id="cb6-23"><a href="#cb6-23"></a></span> <span id="cb5-22"><a href="#cb5-22"></a> gtk_window_set_child <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> scr<span class="op">);</span></span>
<span id="cb6-24"><a href="#cb6-24"></a> tv = gtk_text_view_new ();</span> <span id="cb5-23"><a href="#cb5-23"></a></span>
<span id="cb6-25"><a href="#cb6-25"></a> tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));</span> <span id="cb5-24"><a href="#cb5-24"></a> tv <span class="op">=</span> gtk_text_view_new <span class="op">();</span></span>
<span id="cb6-26"><a href="#cb6-26"></a> gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR);</span> <span id="cb5-25"><a href="#cb5-25"></a> tb <span class="op">=</span> gtk_text_view_get_buffer <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
<span id="cb6-27"><a href="#cb6-27"></a> gtk_text_view_set_editable (GTK_TEXT_VIEW (tv), FALSE);</span> <span id="cb5-26"><a href="#cb5-26"></a> gtk_text_view_set_wrap_mode <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> GTK_WRAP_WORD_CHAR<span class="op">);</span></span>
<span id="cb6-28"><a href="#cb6-28"></a> gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);</span> <span id="cb5-27"><a href="#cb5-27"></a> gtk_text_view_set_editable <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> FALSE<span class="op">);</span></span>
<span id="cb6-29"><a href="#cb6-29"></a></span> <span id="cb5-28"><a href="#cb5-28"></a> gtk_scrolled_window_set_child <span class="op">(</span>GTK_SCROLLED_WINDOW <span class="op">(</span>scr<span class="op">),</span> tv<span class="op">);</span></span>
<span id="cb6-30"><a href="#cb6-30"></a> <span class="cf">if</span> (g_file_load_contents (files[<span class="dv">0</span>], NULL, &amp;contents, &amp;length, NULL, NULL)) {</span> <span id="cb5-29"><a href="#cb5-29"></a></span>
<span id="cb6-31"><a href="#cb6-31"></a> gtk_text_buffer_set_text (tb, contents, length);</span> <span id="cb5-30"><a href="#cb5-30"></a> <span class="cf">if</span> <span class="op">(</span>g_file_load_contents <span class="op">(</span>files<span class="op">[</span><span class="dv">0</span><span class="op">],</span> NULL<span class="op">,</span> <span class="op">&amp;</span>contents<span class="op">,</span> <span class="op">&amp;</span>length<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">))</span> <span class="op">{</span></span>
<span id="cb6-32"><a href="#cb6-32"></a> g_free (contents);</span> <span id="cb5-31"><a href="#cb5-31"></a> gtk_text_buffer_set_text <span class="op">(</span>tb<span class="op">,</span> contents<span class="op">,</span> length<span class="op">);</span></span>
<span id="cb6-33"><a href="#cb6-33"></a> <span class="cf">if</span> ((filename = g_file_get_basename (files[<span class="dv">0</span>])) != NULL) {</span> <span id="cb5-32"><a href="#cb5-32"></a> g_free <span class="op">(</span>contents<span class="op">);</span></span>
<span id="cb6-34"><a href="#cb6-34"></a> gtk_window_set_title (GTK_WINDOW (win), filename);</span> <span id="cb5-33"><a href="#cb5-33"></a> <span class="cf">if</span> <span class="op">((</span>filename <span class="op">=</span> g_file_get_basename <span class="op">(</span>files<span class="op">[</span><span class="dv">0</span><span class="op">]))</span> <span class="op">!=</span> NULL<span class="op">)</span> <span class="op">{</span></span>
<span id="cb6-35"><a href="#cb6-35"></a> g_free (filename);</span> <span id="cb5-34"><a href="#cb5-34"></a> gtk_window_set_title <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> filename<span class="op">);</span></span>
<span id="cb6-36"><a href="#cb6-36"></a> }</span> <span id="cb5-35"><a href="#cb5-35"></a> g_free <span class="op">(</span>filename<span class="op">);</span></span>
<span id="cb6-37"><a href="#cb6-37"></a> gtk_widget_show (win);</span> <span id="cb5-36"><a href="#cb5-36"></a> <span class="op">}</span></span>
<span id="cb6-38"><a href="#cb6-38"></a> } <span class="cf">else</span> {</span> <span id="cb5-37"><a href="#cb5-37"></a> gtk_widget_show <span class="op">(</span>win<span class="op">);</span></span>
<span id="cb6-39"><a href="#cb6-39"></a> <span class="cf">if</span> ((filename = g_file_get_path (files[<span class="dv">0</span>])) != NULL) {</span> <span id="cb5-38"><a href="#cb5-38"></a> <span class="op">}</span> <span class="cf">else</span> <span class="op">{</span></span>
<span id="cb6-40"><a href="#cb6-40"></a> g_print (<span class="st">&quot;No such file: %s.</span><span class="sc">\n</span><span class="st">&quot;</span>, filename);</span> <span id="cb5-39"><a href="#cb5-39"></a> <span class="cf">if</span> <span class="op">((</span>filename <span class="op">=</span> g_file_get_path <span class="op">(</span>files<span class="op">[</span><span class="dv">0</span><span class="op">]))</span> <span class="op">!=</span> NULL<span class="op">)</span> <span class="op">{</span></span>
<span id="cb6-41"><a href="#cb6-41"></a> g_free (filename);</span> <span id="cb5-40"><a href="#cb5-40"></a> g_printerr <span class="op">(</span><span class="st">&quot;No such file: %s.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">,</span> filename<span class="op">);</span></span>
<span id="cb6-42"><a href="#cb6-42"></a> }</span> <span id="cb5-41"><a href="#cb5-41"></a> g_free <span class="op">(</span>filename<span class="op">);</span></span>
<span id="cb6-43"><a href="#cb6-43"></a> gtk_window_destroy (GTK_WINDOW (win));</span> <span id="cb5-42"><a href="#cb5-42"></a> <span class="op">}</span> <span class="cf">else</span></span>
<span id="cb6-44"><a href="#cb6-44"></a> }</span> <span id="cb5-43"><a href="#cb5-43"></a> g_printerr <span class="op">(</span><span class="st">&quot;File can&#39;t be opened.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb6-45"><a href="#cb6-45"></a>}</span> <span id="cb5-44"><a href="#cb5-44"></a> gtk_window_destroy <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
<span id="cb6-46"><a href="#cb6-46"></a></span> <span id="cb5-45"><a href="#cb5-45"></a> <span class="op">}</span></span>
<span id="cb6-47"><a href="#cb6-47"></a><span class="dt">int</span></span> <span id="cb5-46"><a href="#cb5-46"></a><span class="op">}</span></span>
<span id="cb6-48"><a href="#cb6-48"></a>main (<span class="dt">int</span> argc, <span class="dt">char</span> **argv) {</span> <span id="cb5-47"><a href="#cb5-47"></a></span>
<span id="cb6-49"><a href="#cb6-49"></a> GtkApplication *app;</span> <span id="cb5-48"><a href="#cb5-48"></a><span class="dt">int</span></span>
<span id="cb6-50"><a href="#cb6-50"></a> <span class="dt">int</span> stat;</span> <span id="cb5-49"><a href="#cb5-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="cb6-51"><a href="#cb6-51"></a></span> <span id="cb5-50"><a href="#cb5-50"></a> GtkApplication <span class="op">*</span>app<span class="op">;</span></span>
<span id="cb6-52"><a href="#cb6-52"></a> app = gtk_application_new (<span class="st">&quot;com.github.ToshioCP.tfv3&quot;</span>, G_APPLICATION_HANDLES_OPEN);</span> <span id="cb5-51"><a href="#cb5-51"></a> <span class="dt">int</span> stat<span class="op">;</span></span>
<span id="cb6-53"><a href="#cb6-53"></a> g_signal_connect (app, <span class="st">&quot;activate&quot;</span>, G_CALLBACK (app_activate), NULL);</span> <span id="cb5-52"><a href="#cb5-52"></a></span>
<span id="cb6-54"><a href="#cb6-54"></a> g_signal_connect (app, <span class="st">&quot;open&quot;</span>, G_CALLBACK (app_open), NULL);</span> <span id="cb5-53"><a href="#cb5-53"></a> app <span class="op">=</span> gtk_application_new <span class="op">(</span><span class="st">&quot;com.github.ToshioCP.tfv3&quot;</span><span class="op">,</span> G_APPLICATION_HANDLES_OPEN<span class="op">);</span></span>
<span id="cb6-55"><a href="#cb6-55"></a> stat = g_application_run (G_APPLICATION (app), argc, argv);</span> <span id="cb5-54"><a href="#cb5-54"></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="cb6-56"><a href="#cb6-56"></a> g_object_unref (app);</span> <span id="cb5-55"><a href="#cb5-55"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">&quot;open&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>app_open<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb6-57"><a href="#cb6-57"></a> <span class="cf">return</span> stat;</span> <span id="cb5-56"><a href="#cb5-56"></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="cb6-58"><a href="#cb6-58"></a>}</span></code></pre></div> <span id="cb5-57"><a href="#cb5-57"></a> g_object_unref <span class="op">(</span>app<span class="op">);</span></span>
<span id="cb5-58"><a href="#cb5-58"></a> <span class="cf">return</span> stat<span class="op">;</span></span>
<span id="cb5-59"><a href="#cb5-59"></a><span class="op">}</span></span></code></pre></div>
<p>Save it as <code>tfv3.c</code>. Then compile and run it.</p> <p>Save it as <code>tfv3.c</code>. Then compile and run it.</p>
<pre><code>$ comp tfv3 <pre><code>$ comp tfv3
$ ./a.out tfv3.c</code></pre> $ ./a.out tfv3.c</code></pre>
<figure> <figure>
<img src="image/screenshot_tfv3.png" alt="" /><figcaption>File viewer</figcaption> <img src="image/screenshot_tfv3.png" alt="File viewer" />
<figcaption aria-hidden="true">File viewer</figcaption>
</figure> </figure>
<p>Lets explain how the program <code>tfv3.c</code> works. First, the function <code>main</code> has only two changes from the previous version.</p> <p>The function <code>main</code> has only two changes from the previous
version.</p>
<ul> <ul>
<li><code>G_APPLICATION_FLAGS_NONE</code> is replaced by <code>G_APPLICATION_HANDLES_OPEN</code>; and</li> <li><code>G_APPLICATION_DEFAULT_FLAGS</code> is replaced by
<li><code>g_signal_connect (app, "open", G_CALLBACK (on_open), NULL)</code> is added.</li> <code>G_APPLICATION_HANDLES_OPEN</code></li>
<li><code>g_signal_connect (app, "open", G_CALLBACK (app_open), NULL)</code>
is added.</li>
</ul> </ul>
<p>Next, the handler <code>app_activate</code> is added and is very simple. It just outputs the error message and the application quits immediately because no window is created.</p> <p>When the flag <code>G_APPLICATION_HANDLES_OPEN</code> is given to
<p>The main functionality is the in the handler <code>app_open</code>. It</p> <code>gtk_application_new</code> function, the application behaves like
this:</p>
<ul> <ul>
<li>Creates GtkApplicationWindow, GtkScrolledWindow, GtkTextView and GtkTextBuffer and connects them together;</li> <li>If the application is run without command line arguments, it emits
<li>Sets wrap mode to <code>GTK_WRAP_WORD_CHAR</code> in GtktextView;</li> “activate” signal when it is activated.</li>
<li>Sets GtkTextView to non-editable because the program isnt an editor but only a viewer;</li> <li>If the application is run with command line arguments, it emits
<li>Reads the file and inserts the text into GtkTextBuffer (this will be explained in detail later); and</li> “open” signal when it is activated.</li>
<li>If the file is not opened then outputs an error message and destroys the window. This makes the application quit.</li>
</ul> </ul>
<p>The following is the important file reading part of the program and is shown again below.</p> <p>The handler <code>app_activate</code> becomes very simple. It just
<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="cf">if</span> (g_file_load_contents (files[<span class="dv">0</span>], NULL, &amp;contents, &amp;length, NULL, NULL)) {</span> outputs the error message and return to the caller. Then the application
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true"></a> gtk_text_buffer_set_text (tb, contents, length);</span> quits immediately because no window is created.</p>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true"></a> g_free (contents);</span> <p>The main work is done in the handler <code>app_open</code>.</p>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true"></a> <span class="cf">if</span> ((filename = g_file_get_basename (files[<span class="dv">0</span>])) != NULL) {</span> <ul>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true"></a> gtk_window_set_title (GTK_WINDOW (win), filename);</span> <li>Creates GtkApplicationWindow, GtkScrolledWindow, GtkTextView and
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true"></a> g_free (filename);</span> GtkTextBuffer and connects them together</li>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true"></a> }</span> <li>Sets wrap mode to <code>GTK_WRAP_WORD_CHAR</code> in
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true"></a> gtk_widget_show (win);</span> GtktextView</li>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true"></a>} <span class="cf">else</span> {</span> <li>Sets GtkTextView to non-editable because the program isnt an editor
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true"></a> <span class="cf">if</span> ((filename = g_file_get_path (files[<span class="dv">0</span>])) != NULL) {</span> but only a viewer</li>
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true"></a> g_print (<span class="st">&quot;No such file: %s.</span><span class="sc">\n</span><span class="st">&quot;</span>, filename);</span> <li>Reads the file and inserts the text into GtkTextBuffer (this will be
<span id="cb8-12"><a href="#cb8-12" aria-hidden="true"></a> g_free (filename);</span> explained later)</li>
<span id="cb8-13"><a href="#cb8-13" aria-hidden="true"></a> }</span> <li>If the file is not opened, outputs an error message and destroys the
<span id="cb8-14"><a href="#cb8-14" aria-hidden="true"></a> gtk_window_destroy (GTK_WINDOW (win));</span> window. This makes the application quit.</li>
<span id="cb8-15"><a href="#cb8-15" aria-hidden="true"></a>}</span></code></pre></div> </ul>
<p>The function <code>g_file_load_contents</code> loads the file contents into a buffer, which is automatically allocated and sets <code>contents</code> to point that buffer. The length of the buffer is set to <code>length</code>. It returns <code>TRUE</code> if the files contents are successfully loaded and <code>FALSE</code> if an error occurs.</p> <p>The following is the file reading part of the program again.</p>
<p>If this function succeeds, it inserts the contents into GtkTextBuffer, frees the buffer pointed by <code>contents</code>, sets the title of the window, frees the memories pointed by <code>filename</code> and then shows the window. If it fails, it outputs an error message and destroys the window, causing the program to quit.</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><span class="cf">if</span> <span class="op">(</span>g_file_load_contents <span class="op">(</span>files<span class="op">[</span><span class="dv">0</span><span class="op">],</span> NULL<span class="op">,</span> <span class="op">&amp;</span>contents<span class="op">,</span> <span class="op">&amp;</span>length<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">))</span> <span class="op">{</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a> gtk_text_buffer_set_text <span class="op">(</span>tb<span class="op">,</span> contents<span class="op">,</span> length<span class="op">);</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a> g_free <span class="op">(</span>contents<span class="op">);</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">((</span>filename <span class="op">=</span> g_file_get_basename <span class="op">(</span>files<span class="op">[</span><span class="dv">0</span><span class="op">]))</span> <span class="op">!=</span> NULL<span class="op">)</span> <span class="op">{</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a> gtk_window_set_title <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> filename<span class="op">);</span></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a> g_free <span class="op">(</span>filename<span class="op">);</span></span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a> gtk_widget_show <span class="op">(</span>win<span class="op">);</span></span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a><span class="op">}</span> <span class="cf">else</span> <span class="op">{</span></span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">((</span>filename <span class="op">=</span> g_file_get_path <span class="op">(</span>files<span class="op">[</span><span class="dv">0</span><span class="op">]))</span> <span class="op">!=</span> NULL<span class="op">)</span> <span class="op">{</span></span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a> g_printerr <span class="op">(</span><span class="st">&quot;No such file: %s.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">,</span> filename<span class="op">);</span></span>
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true" tabindex="-1"></a> g_free <span class="op">(</span>filename<span class="op">);</span></span>
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span> <span class="cf">else</span></span>
<span id="cb7-14"><a href="#cb7-14" aria-hidden="true" tabindex="-1"></a> g_printerr <span class="op">(</span><span class="st">&quot;File can&#39;t be opened.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb7-15"><a href="#cb7-15" aria-hidden="true" tabindex="-1"></a> gtk_window_destroy <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
<span id="cb7-16"><a href="#cb7-16" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>The function <code>g_file_load_contents</code> loads the file
contents into a temporary buffer, which is automatically allocated and
sets <code>contents</code> to point the buffer. The length of the buffer
is assigned to <code>length</code>. It returns <code>TRUE</code> if the
files contents are successfully loaded or <code>FALSE</code> if an
error occurs. If you want to know the details about
g_file_load_contents, see <a
href="https://docs.gtk.org/gio/method.File.load_contents.html">g file
load contents</a>.</p>
<p>If it has successfully read the file, it inserts the contents into
GtkTextBuffer, frees the temporary buffer pointed by
<code>contents</code>, sets the title of the window, frees the memories
pointed by <code>filename</code> and then shows the window. If it fails,
it outputs an error message and destroys the window and finally make the
program quit.</p>
<h2 id="gtknotebook">GtkNotebook</h2> <h2 id="gtknotebook">GtkNotebook</h2>
<p>GtkNotebook is a container widget that uses tabs and contains multiple children. The child that is displayed depends on which tab has been selected.</p> <p>GtkNotebook is a container widget that contains multiple widgets with
tabs. It shows only one child at a time. Another child will be shown
when its tab is clicked.</p>
<figure> <figure>
<img src="image/screenshot_gtk_notebook.png" alt="" /><figcaption>GtkNotebook</figcaption> <img src="image/screenshot_gtk_notebook.png" alt="GtkNotebook" />
<figcaption aria-hidden="true">GtkNotebook</figcaption>
</figure> </figure>
<p>Looking at the screenshots above, the left one is the window at the startup. It shows the file <code>pr1.c</code> and the filename is in the left tab. After clicking on the right tab, the contents of the file <code>tfv1.c</code> are shown instead. This is shown in the right screenshot.</p> <p>The left image is the window at the startup. The file
<p>The GtkNotebook widget is inserted as a child of GtkApplicationWindow and contains a GtkScrolledWindow for each file that is being displayed. The code to do this is given in <code>tfv4.c</code> and is:</p> <code>pr1.c</code> is shown and its filename is in the left tab. After
<div class="sourceCode" id="cb9"><pre class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb9-1"><a href="#cb9-1"></a><span class="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span> clicking on the right tab, the contents of the file <code>tfv1.c</code>
<span id="cb9-2"><a href="#cb9-2"></a></span> is shown. The right image is the screenshot.</p>
<span id="cb9-3"><a href="#cb9-3"></a><span class="dt">static</span> <span class="dt">void</span></span> <p>The following is <code>tfv4.c</code>. It has GtkNoteBook widget. It
<span id="cb9-4"><a href="#cb9-4"></a>app_activate (GApplication *app, gpointer user_data) {</span> is inserted as a child of GtkApplicationWindow and contains multiple
<span id="cb9-5"><a href="#cb9-5"></a> g_print (<span class="st">&quot;You need a filename argument.</span><span class="sc">\n</span><span class="st">&quot;</span>);</span> GtkScrolledWindow.</p>
<span id="cb9-6"><a href="#cb9-6"></a>}</span> <div class="sourceCode" id="cb8"><pre
<span id="cb9-7"><a href="#cb9-7"></a></span> 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="cb9-8"><a href="#cb9-8"></a><span class="dt">static</span> <span class="dt">void</span></span> <span id="cb8-2"><a href="#cb8-2"></a></span>
<span id="cb9-9"><a href="#cb9-9"></a>app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer user_data) {</span> <span id="cb8-3"><a href="#cb8-3"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb9-10"><a href="#cb9-10"></a> GtkWidget *win;</span> <span id="cb8-4"><a href="#cb8-4"></a>app_activate <span class="op">(</span>GApplication <span class="op">*</span>app<span class="op">)</span> <span class="op">{</span></span>
<span id="cb9-11"><a href="#cb9-11"></a> GtkWidget *nb;</span> <span id="cb8-5"><a href="#cb8-5"></a> g_printerr <span class="op">(</span><span class="st">&quot;You need a filename argument.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb9-12"><a href="#cb9-12"></a> GtkWidget *lab;</span> <span id="cb8-6"><a href="#cb8-6"></a><span class="op">}</span></span>
<span id="cb9-13"><a href="#cb9-13"></a> GtkNotebookPage *nbp;</span> <span id="cb8-7"><a href="#cb8-7"></a></span>
<span id="cb9-14"><a href="#cb9-14"></a> GtkWidget *scr;</span> <span id="cb8-8"><a href="#cb8-8"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb9-15"><a href="#cb9-15"></a> GtkWidget *tv;</span> <span id="cb8-9"><a href="#cb8-9"></a>app_open <span class="op">(</span>GApplication <span class="op">*</span>app<span class="op">,</span> GFile <span class="op">**</span> files<span class="op">,</span> gint n_files<span class="op">,</span> gchar <span class="op">*</span>hint<span class="op">)</span> <span class="op">{</span></span>
<span id="cb9-16"><a href="#cb9-16"></a> GtkTextBuffer *tb;</span> <span id="cb8-10"><a href="#cb8-10"></a> GtkWidget <span class="op">*</span>win<span class="op">;</span></span>
<span id="cb9-17"><a href="#cb9-17"></a> <span class="dt">char</span> *contents;</span> <span id="cb8-11"><a href="#cb8-11"></a> GtkWidget <span class="op">*</span>nb<span class="op">;</span></span>
<span id="cb9-18"><a href="#cb9-18"></a> gsize length;</span> <span id="cb8-12"><a href="#cb8-12"></a> GtkWidget <span class="op">*</span>lab<span class="op">;</span></span>
<span id="cb9-19"><a href="#cb9-19"></a> <span class="dt">char</span> *filename;</span> <span id="cb8-13"><a href="#cb8-13"></a> GtkNotebookPage <span class="op">*</span>nbp<span class="op">;</span></span>
<span id="cb9-20"><a href="#cb9-20"></a> <span class="dt">int</span> i;</span> <span id="cb8-14"><a href="#cb8-14"></a> GtkWidget <span class="op">*</span>scr<span class="op">;</span></span>
<span id="cb9-21"><a href="#cb9-21"></a></span> <span id="cb8-15"><a href="#cb8-15"></a> GtkWidget <span class="op">*</span>tv<span class="op">;</span></span>
<span id="cb9-22"><a href="#cb9-22"></a> win = gtk_application_window_new (GTK_APPLICATION (app));</span> <span id="cb8-16"><a href="#cb8-16"></a> GtkTextBuffer <span class="op">*</span>tb<span class="op">;</span></span>
<span id="cb9-23"><a href="#cb9-23"></a> gtk_window_set_title (GTK_WINDOW (win), <span class="st">&quot;file viewer&quot;</span>);</span> <span id="cb8-17"><a href="#cb8-17"></a> <span class="dt">char</span> <span class="op">*</span>contents<span class="op">;</span></span>
<span id="cb9-24"><a href="#cb9-24"></a> gtk_window_set_default_size (GTK_WINDOW (win), <span class="dv">400</span>, <span class="dv">300</span>);</span> <span id="cb8-18"><a href="#cb8-18"></a> gsize length<span class="op">;</span></span>
<span id="cb9-25"><a href="#cb9-25"></a> gtk_window_maximize (GTK_WINDOW (win));</span> <span id="cb8-19"><a href="#cb8-19"></a> <span class="dt">char</span> <span class="op">*</span>filename<span class="op">;</span></span>
<span id="cb9-26"><a href="#cb9-26"></a></span> <span id="cb8-20"><a href="#cb8-20"></a> <span class="dt">int</span> i<span class="op">;</span></span>
<span id="cb9-27"><a href="#cb9-27"></a> nb = gtk_notebook_new ();</span> <span id="cb8-21"><a href="#cb8-21"></a></span>
<span id="cb9-28"><a href="#cb9-28"></a> gtk_window_set_child (GTK_WINDOW (win), nb);</span> <span id="cb8-22"><a href="#cb8-22"></a> win <span class="op">=</span> gtk_application_window_new <span class="op">(</span>GTK_APPLICATION <span class="op">(</span>app<span class="op">));</span></span>
<span id="cb9-29"><a href="#cb9-29"></a></span> <span id="cb8-23"><a href="#cb8-23"></a> gtk_window_set_title <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="st">&quot;file viewer&quot;</span><span class="op">);</span></span>
<span id="cb9-30"><a href="#cb9-30"></a> <span class="cf">for</span> (i = <span class="dv">0</span>; i &lt; n_files; i++) {</span> <span id="cb8-24"><a href="#cb8-24"></a> gtk_window_set_default_size <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="dv">600</span><span class="op">,</span> <span class="dv">400</span><span class="op">);</span></span>
<span id="cb9-31"><a href="#cb9-31"></a> <span class="cf">if</span> (g_file_load_contents (files[i], NULL, &amp;contents, &amp;length, NULL, NULL)) {</span> <span id="cb8-25"><a href="#cb8-25"></a></span>
<span id="cb9-32"><a href="#cb9-32"></a> scr = gtk_scrolled_window_new ();</span> <span id="cb8-26"><a href="#cb8-26"></a> nb <span class="op">=</span> gtk_notebook_new <span class="op">();</span></span>
<span id="cb9-33"><a href="#cb9-33"></a> tv = gtk_text_view_new ();</span> <span id="cb8-27"><a href="#cb8-27"></a> gtk_window_set_child <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> nb<span class="op">);</span></span>
<span id="cb9-34"><a href="#cb9-34"></a> tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));</span> <span id="cb8-28"><a href="#cb8-28"></a></span>
<span id="cb9-35"><a href="#cb9-35"></a> gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR);</span> <span id="cb8-29"><a href="#cb8-29"></a> <span class="cf">for</span> <span class="op">(</span>i <span class="op">=</span> <span class="dv">0</span><span class="op">;</span> i <span class="op">&lt;</span> n_files<span class="op">;</span> i<span class="op">++)</span> <span class="op">{</span></span>
<span id="cb9-36"><a href="#cb9-36"></a> gtk_text_view_set_editable (GTK_TEXT_VIEW (tv), FALSE);</span> <span id="cb8-30"><a href="#cb8-30"></a> <span class="cf">if</span> <span class="op">(</span>g_file_load_contents <span class="op">(</span>files<span class="op">[</span>i<span class="op">],</span> NULL<span class="op">,</span> <span class="op">&amp;</span>contents<span class="op">,</span> <span class="op">&amp;</span>length<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">))</span> <span class="op">{</span></span>
<span id="cb9-37"><a href="#cb9-37"></a> gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);</span> <span id="cb8-31"><a href="#cb8-31"></a> scr <span class="op">=</span> gtk_scrolled_window_new <span class="op">();</span></span>
<span id="cb9-38"><a href="#cb9-38"></a></span> <span id="cb8-32"><a href="#cb8-32"></a> tv <span class="op">=</span> gtk_text_view_new <span class="op">();</span></span>
<span id="cb9-39"><a href="#cb9-39"></a> gtk_text_buffer_set_text (tb, contents, length);</span> <span id="cb8-33"><a href="#cb8-33"></a> tb <span class="op">=</span> gtk_text_view_get_buffer <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
<span id="cb9-40"><a href="#cb9-40"></a> g_free (contents);</span> <span id="cb8-34"><a href="#cb8-34"></a> gtk_text_view_set_wrap_mode <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> GTK_WRAP_WORD_CHAR<span class="op">);</span></span>
<span id="cb9-41"><a href="#cb9-41"></a> <span class="cf">if</span> ((filename = g_file_get_basename (files[i])) != NULL) {</span> <span id="cb8-35"><a href="#cb8-35"></a> gtk_text_view_set_editable <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> FALSE<span class="op">);</span></span>
<span id="cb9-42"><a href="#cb9-42"></a> lab = gtk_label_new (filename);</span> <span id="cb8-36"><a href="#cb8-36"></a> gtk_scrolled_window_set_child <span class="op">(</span>GTK_SCROLLED_WINDOW <span class="op">(</span>scr<span class="op">),</span> tv<span class="op">);</span></span>
<span id="cb9-43"><a href="#cb9-43"></a> g_free (filename);</span> <span id="cb8-37"><a href="#cb8-37"></a></span>
<span id="cb9-44"><a href="#cb9-44"></a> } <span class="cf">else</span></span> <span id="cb8-38"><a href="#cb8-38"></a> gtk_text_buffer_set_text <span class="op">(</span>tb<span class="op">,</span> contents<span class="op">,</span> length<span class="op">);</span></span>
<span id="cb9-45"><a href="#cb9-45"></a> lab = gtk_label_new (<span class="st">&quot;&quot;</span>);</span> <span id="cb8-39"><a href="#cb8-39"></a> g_free <span class="op">(</span>contents<span class="op">);</span></span>
<span id="cb9-46"><a href="#cb9-46"></a> gtk_notebook_append_page (GTK_NOTEBOOK (nb), scr, lab);</span> <span id="cb8-40"><a href="#cb8-40"></a> <span class="cf">if</span> <span class="op">((</span>filename <span class="op">=</span> g_file_get_basename <span class="op">(</span>files<span class="op">[</span>i<span class="op">]))</span> <span class="op">!=</span> NULL<span class="op">)</span> <span class="op">{</span></span>
<span id="cb9-47"><a href="#cb9-47"></a> nbp = gtk_notebook_get_page (GTK_NOTEBOOK (nb), scr);</span> <span id="cb8-41"><a href="#cb8-41"></a> lab <span class="op">=</span> gtk_label_new <span class="op">(</span>filename<span class="op">);</span></span>
<span id="cb9-48"><a href="#cb9-48"></a> g_object_set (nbp, <span class="st">&quot;tab-expand&quot;</span>, TRUE, NULL);</span> <span id="cb8-42"><a href="#cb8-42"></a> g_free <span class="op">(</span>filename<span class="op">);</span></span>
<span id="cb9-49"><a href="#cb9-49"></a> } <span class="cf">else</span> <span class="cf">if</span> ((filename = g_file_get_path (files[i])) != NULL) {</span> <span id="cb8-43"><a href="#cb8-43"></a> <span class="op">}</span> <span class="cf">else</span></span>
<span id="cb9-50"><a href="#cb9-50"></a> g_print (<span class="st">&quot;No such file: %s.</span><span class="sc">\n</span><span class="st">&quot;</span>, filename);</span> <span id="cb8-44"><a href="#cb8-44"></a> lab <span class="op">=</span> gtk_label_new <span class="op">(</span><span class="st">&quot;&quot;</span><span class="op">);</span></span>
<span id="cb9-51"><a href="#cb9-51"></a> g_free (filename);</span> <span id="cb8-45"><a href="#cb8-45"></a> gtk_notebook_append_page <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">),</span> scr<span class="op">,</span> lab<span class="op">);</span></span>
<span id="cb9-52"><a href="#cb9-52"></a> } <span class="cf">else</span></span> <span id="cb8-46"><a href="#cb8-46"></a> nbp <span class="op">=</span> gtk_notebook_get_page <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">),</span> scr<span class="op">);</span></span>
<span id="cb9-53"><a href="#cb9-53"></a> g_print (<span class="st">&quot;No valid file is given</span><span class="sc">\n</span><span class="st">&quot;</span>);</span> <span id="cb8-47"><a href="#cb8-47"></a> g_object_set <span class="op">(</span>nbp<span class="op">,</span> <span class="st">&quot;tab-expand&quot;</span><span class="op">,</span> TRUE<span class="op">,</span> NULL<span class="op">);</span></span>
<span id="cb9-54"><a href="#cb9-54"></a> }</span> <span id="cb8-48"><a href="#cb8-48"></a> <span class="op">}</span> <span class="cf">else</span> <span class="cf">if</span> <span class="op">((</span>filename <span class="op">=</span> g_file_get_path <span class="op">(</span>files<span class="op">[</span>i<span class="op">]))</span> <span class="op">!=</span> NULL<span class="op">)</span> <span class="op">{</span></span>
<span id="cb9-55"><a href="#cb9-55"></a> <span class="cf">if</span> (gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb)) &gt; <span class="dv">0</span>)</span> <span id="cb8-49"><a href="#cb8-49"></a> g_printerr <span class="op">(</span><span class="st">&quot;No such file: %s.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">,</span> filename<span class="op">);</span></span>
<span id="cb9-56"><a href="#cb9-56"></a> gtk_widget_show (win);</span> <span id="cb8-50"><a href="#cb8-50"></a> g_free <span class="op">(</span>filename<span class="op">);</span></span>
<span id="cb9-57"><a href="#cb9-57"></a> <span class="cf">else</span></span> <span id="cb8-51"><a href="#cb8-51"></a> <span class="op">}</span> <span class="cf">else</span></span>
<span id="cb9-58"><a href="#cb9-58"></a> gtk_window_destroy (GTK_WINDOW (win));</span> <span id="cb8-52"><a href="#cb8-52"></a> g_printerr <span class="op">(</span><span class="st">&quot;No valid file is given</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb9-59"><a href="#cb9-59"></a>}</span> <span id="cb8-53"><a href="#cb8-53"></a> <span class="op">}</span></span>
<span id="cb9-60"><a href="#cb9-60"></a></span> <span id="cb8-54"><a href="#cb8-54"></a> <span class="cf">if</span> <span class="op">(</span>gtk_notebook_get_n_pages <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">))</span> <span class="op">&gt;</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb9-61"><a href="#cb9-61"></a><span class="dt">int</span></span> <span id="cb8-55"><a href="#cb8-55"></a> gtk_widget_show <span class="op">(</span>win<span class="op">);</span></span>
<span id="cb9-62"><a href="#cb9-62"></a>main (<span class="dt">int</span> argc, <span class="dt">char</span> **argv) {</span> <span id="cb8-56"><a href="#cb8-56"></a> <span class="cf">else</span></span>
<span id="cb9-63"><a href="#cb9-63"></a> GtkApplication *app;</span> <span id="cb8-57"><a href="#cb8-57"></a> gtk_window_destroy <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
<span id="cb9-64"><a href="#cb9-64"></a> <span class="dt">int</span> stat;</span> <span id="cb8-58"><a href="#cb8-58"></a><span class="op">}</span></span>
<span id="cb9-65"><a href="#cb9-65"></a></span> <span id="cb8-59"><a href="#cb8-59"></a></span>
<span id="cb9-66"><a href="#cb9-66"></a> app = gtk_application_new (<span class="st">&quot;com.github.ToshioCP.tfv4&quot;</span>, G_APPLICATION_HANDLES_OPEN);</span> <span id="cb8-60"><a href="#cb8-60"></a><span class="dt">int</span></span>
<span id="cb9-67"><a href="#cb9-67"></a> g_signal_connect (app, <span class="st">&quot;activate&quot;</span>, G_CALLBACK (app_activate), NULL);</span> <span id="cb8-61"><a href="#cb8-61"></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="cb9-68"><a href="#cb9-68"></a> g_signal_connect (app, <span class="st">&quot;open&quot;</span>, G_CALLBACK (app_open), NULL);</span> <span id="cb8-62"><a href="#cb8-62"></a> GtkApplication <span class="op">*</span>app<span class="op">;</span></span>
<span id="cb9-69"><a href="#cb9-69"></a> stat = g_application_run (G_APPLICATION (app), argc, argv);</span> <span id="cb8-63"><a href="#cb8-63"></a> <span class="dt">int</span> stat<span class="op">;</span></span>
<span id="cb9-70"><a href="#cb9-70"></a> g_object_unref (app);</span> <span id="cb8-64"><a href="#cb8-64"></a></span>
<span id="cb9-71"><a href="#cb9-71"></a> <span class="cf">return</span> stat;</span> <span id="cb8-65"><a href="#cb8-65"></a> app <span class="op">=</span> gtk_application_new <span class="op">(</span><span class="st">&quot;com.github.ToshioCP.tfv4&quot;</span><span class="op">,</span> G_APPLICATION_HANDLES_OPEN<span class="op">);</span></span>
<span id="cb9-72"><a href="#cb9-72"></a>}</span></code></pre></div> <span id="cb8-66"><a href="#cb8-66"></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>
<p>Most of the changes are in the function <code>app_open</code>. The numbers at the left of the following items are line numbers in the source code.</p> <span id="cb8-67"><a href="#cb8-67"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">&quot;open&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>app_open<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb8-68"><a href="#cb8-68"></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="cb8-69"><a href="#cb8-69"></a> g_object_unref <span class="op">(</span>app<span class="op">);</span></span>
<span id="cb8-70"><a href="#cb8-70"></a> <span class="cf">return</span> stat<span class="op">;</span></span>
<span id="cb8-71"><a href="#cb8-71"></a><span class="op">}</span></span></code></pre></div>
<p>Most of the changes are in the function <code>app_open</code>. The
numbers at the left of the following items are line numbers in the
source code.</p>
<ul> <ul>
<li>11-13: Variables <code>nb</code>, <code>lab</code> and <code>nbp</code> are defined and will point to a new GtkNotebook, GtkLabel and GtkNotebookPage widget respectively.</li> <li>11-13: Variables <code>nb</code>, <code>lab</code> and
<code>nbp</code> are defined. They point GtkNotebook, GtkLabel and
GtkNotebookPage respectively.</li>
<li>23: The windows title is set to “file viewer”.</li> <li>23: The windows title is set to “file viewer”.</li>
<li>25: The size of the window is set to maximum because a big window is appropriate for file viewers.</li> <li>24: The default size of the window is 600x400.</li>
<li>27-28 GtkNotebook is created and inserted to the GtkApplicationWindow as a child.</li> <li>26-27 GtkNotebook is created and inserted to the
<li>30-59 For-loop. Each loop corresponds to a filename argument, and <code>files[i]</code> is GFile object containing the i-th argument.</li> GtkApplicationWindow as a child.</li>
<li>32-37 GtkScrollledWindow, GtkTextView are created and GtkTextBuffer found from the new GtkTextView. GtkTextView is connected to GtkScrolledWindow as a child. Each file gets their own copy of these widgets, so they are created inside the for-loop.</li> <li>29-58 For-loop. The variable <code>files[i]</code> points i-th
<li>39-40 inserts the contents of the file into GtkTextBuffer and frees the memory pointed by <code>contents</code>.</li> GFile, which is created by the GtkApplication from the i-th command line
<li>41-43: Gets the filename and creates GtkLabel with the filename and then frees <code>filename</code>.</li> argument.</li>
<li>44-45: If <code>filename</code> is NULL, creates GtkLabel with the empty string.</li> <li>31-36 GtkScrollledWindow, GtkTextView are created. GtkTextBuffer is
<li>46: Appends GtkScrolledWindow as a page, with the tab GtkLabel, to GtkNotebook. At this time a GtkNoteBookPage widget is created automatically. The GtkScrolledWindow widget is connected to the GtkNotebookPage. Therefore, the structure is like this:</li> got from the GtkTextView. The GtkTextView is connected to the
</ul> GtkScrolledWindow as a child.</li>
<pre><code> GtkNotebook -- GtkNotebookPage -- GtkScrolledWindow</code></pre> <li>38-39 inserts the contents of the file into GtkTextBuffer and frees
<ul> the memory pointed by <code>contents</code>.</li>
<li>47: Gets GtkNotebookPage widget and sets <code>nbp</code> to point to this GtkNotebookPage.</li> <li>40-42: If the filename is taken from the GFile, GtkLabel is created
<li>48: GtkNotebookPage has a property set called “tab-expand”. If it is set to TRUE then the tab expands horizontally as long as possible. If it is FALSE, then the width of the tab is determined by the size of the label. <code>g_object_set</code> is a general function to set properties in objects. See <a href="https://docs.gtk.org/gobject/method.Object.set.html">GObject API Reference, g_object_set</a>.</li> with the filename. The string <code>filename</code> is freed..</li>
<li>49-51: If the file cannot be read, “No such file” message is displayed and the <code>filename</code> buffer is freed.</li> <li>43-44: If it fails to take the filename, empty string GtkLabel is
<li>52-53: If <code>filename</code> is NULL, the “No valid file is given” message is outputted.</li> created.</li>
<li>55-58: If at least one file was read, then the number of GtkNotebookPage is greater than zero. If its true, it shows the window. If its false, it destroys the window, which causes the program to quit.</li> <li>45-46: Appends a GtkScrolledWindow to the GtkNotebook as a child.
And the GtkLabel is set as the childs tab. At the same time, a
GtkNoteBookPage is created automatically. The function
<code>gtk_notebook_get_page</code> returns the GtkNotebookPage of the
child (GtkScrolledWindow).</li>
<li>47: GtkNotebookPage has “tab-expand” property. If it is set to TRUE
then the tab expands horizontally as long as possible. If it is FALSE,
then the width of the tab is determined by the size of the label.
<code>g_object_set</code> is a general function to set properties of
objects. See <a
href="https://docs.gtk.org/gobject/method.Object.set.html">GObject API
Reference g_object_set</a>.</li>
<li>48-50: If it fails to read the file and a filename is taken from the
GFile, “No such file” message is displayed. The <code>filename</code> is
freed.</li>
<li>51-52: If <code>filename</code> is NULL, the “No valid file is
given” message is displayed.</li>
<li>54-57: If at least one page exists, the window is shown. Otherwise,
the window is destroyed and the application quits.</li>
</ul> </ul>
</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> <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>

View file

@ -111,62 +111,60 @@
</div> </div>
</div> </div>
</nav> </nav>
<h1 id="defining-a-child-object">Defining a child object</h1> <h1 id="defining-a-final-class">Defining a final class</h1>
<h2 id="a-very-simple-editor">A very simple editor</h2> <h2 id="a-very-simple-editor">A very simple editor</h2>
<p>In the previous section we made a very simple file viewer. Now we go <p>We made a very simple file viewer in the previous section. Now we go
on to rewrite it and turn it into very simple editor. Its source file is on to rewrite it and turn it into very simple editor. Its source file is
in tfe1.c (text file editor 1).</p> <code>tfe1.c</code> (text file editor 1) under <code>tfe</code>
<p>GtkTextView has a feature for editing multiple lines. Therefore, we directory.</p>
dont need to write the program from scratch, we just add two things to <p>GtkTextView is a multi-line editor. So, we dont need to write the
the file viewer:</p> editor from scratch. We just add two things to the file viewer:</p>
<ul> <ul>
<li>Memory to store a pointer to the GFile instance.</li> <li>Pointers to GFile instances.</li>
<li>A function to write the file.</li> <li>A text-save function.</li>
</ul> </ul>
<p>There are a couple of ways to store the details of GFile.</p> <p>There are a couple of ways to store the pointers.</p>
<ul> <ul>
<li>Use global variables; or</li> <li>Use global variables</li>
<li>Make a child object, which can extend the instance memory for the <li>Make a child class of GtkTextView and its each instance holds a
GFile object.</li> pointer to the GFile instance.</li>
</ul> </ul>
<p>Using global variables is easy to implement. Define a sufficient size <p>Using global variables is easy to implement. Define a sufficient size
array of pointers to GFile. For example,</p> pointer array to GFile. For example,</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" tabindex="-1"></a>GFile <span class="op">*</span>f<span class="op">[</span><span class="dv">20</span><span class="op">];</span></span></code></pre></div> <div class="sourceCode" id="cb1"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>GFile <span class="op">*</span>f<span class="op">[</span><span class="dv">20</span><span class="op">];</span></span></code></pre></div>
<p>The variable <code>f[i]</code> corresponds to the file associated to <p>The variable <code>f[i]</code> corresponds to the file associated
the i-th GtkNotebookPage. There are however two problems with this. The with the i-th GtkNotebookPage.</p>
first concerns the size of the array. If a user gives too many arguments <p>However, There are two problems. The first is the size of the array.
(more than 20 in the example above), it is impossible to store the If a user gives too many arguments (more than 20 in the example above),
additional pointers to the GFile instances. The second is the increasing it is impossible to store all the pointers to the GFile instances. The
difficulty for maintenance of the program. We have a small program so second is difficulty to maintain the program. We have a small program so
far, but however, if you continue developing it, the size of the program far. But, the more you develop the program, the bigger its size grows.
will grow. Generally speaking, the bigger the program size, the more Generally speaking, it is very difficult to maintain global variables in
difficult it is to keep track of and maintain global variables. Global a big program. When you check the global variable, you need to check all
variables can be used and changed anywhere throughout the entire the codes that use the variable.</p>
program.</p> <p>Making a child class is a good idea in terms of maintenance. And we
<p>Making a child object is a good idea in terms of maintenance. One prefer it rather than a global variable.</p>
thing you need to be careful of is the difference between “child object” <p>Be careful that we are thinking about “child class”, not “child
and “child widget”. Here we are describing a “child object”. A child widget”. Child class and child widget are totally different. Class is a
object includes, and expands on its parent object, as a child object term of GObject system. If you are not familiar with GObject, see:</p>
derives everything from the parent object.</p> <ul>
<li><a href="https://docs.gtk.org/gobject/">GObject API
reference</a></li>
<li><a href="https://toshiocp.github.io/Gobject-tutorial/">GObject
tutorial for beginners</a></li>
</ul>
<p>A child class inherits everything from the parent and, in addition,
extends its performance. We will define TfeTextView as a child class of
GtkTextView. It has everything that GtkTextView has and adds a pointer
to a GFile.</p>
<figure> <figure>
<img src="image/child.png" alt="Child object of GtkTextView" /> <img src="image/child.png" alt="Child object of GtkTextView" />
<figcaption aria-hidden="true">Child object of GtkTextView</figcaption> <figcaption aria-hidden="true">Child object of GtkTextView</figcaption>
</figure> </figure>
<p>We will define TfeTextView as a child object of GtkTextView. It has <h2 id="how-to-define-a-child-class-of-gtktextview">How to define a
everything that GtkTextView has. Specifically, TfeTextView has a child class of GtkTextView</h2>
GtkTextbuffer which corresponds to the GtkTextView inside TfeTextView. <p>You need to know GObject system convention. First, look at the
The additional important thing is that TfeTextView can also keep an program below.</p>
additional pointer to GFile.</p>
<p>In general, this is how GObjects work. Understanding the general
theory about Gobjects is difficult, particularly for beginners. So, I
will just show you the way how to write the code and avoid the
theoretical side. If you want to know about GObject system, refer to
another <a
href="https://github.com/ToshioCP/Gobject-tutorial">tutorial</a>.</p>
<h2 id="how-to-define-a-child-object-of-gtktextview">How to define a
child object of GtkTextView</h2>
<p>Lets define the TfeTextView object, which is a child object of
GtkTextView. First, look at the program below.</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" tabindex="-1"></a><span class="pp">#define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type ()</span></span> <div class="sourceCode" id="cb2"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type ()</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>G_DECLARE_FINAL_TYPE <span class="op">(</span>TfeTextView<span class="op">,</span> tfe_text_view<span class="op">,</span> TFE<span class="op">,</span> TEXT_VIEW<span class="op">,</span> GtkTextView<span class="op">)</span></span> <span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>G_DECLARE_FINAL_TYPE <span class="op">(</span>TfeTextView<span class="op">,</span> tfe_text_view<span class="op">,</span> TFE<span class="op">,</span> TEXT_VIEW<span class="op">,</span> GtkTextView<span class="op">)</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a></span> <span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a></span>
@ -200,71 +198,73 @@ GtkTextView. First, look at the program below.</p>
<span id="cb2-31"><a href="#cb2-31" aria-hidden="true" tabindex="-1"></a>tfe_text_view_new <span class="op">(</span><span class="dt">void</span><span class="op">)</span> <span class="op">{</span></span> <span id="cb2-31"><a href="#cb2-31" aria-hidden="true" tabindex="-1"></a>tfe_text_view_new <span class="op">(</span><span class="dt">void</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb2-32"><a href="#cb2-32" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> GTK_WIDGET <span class="op">(</span>g_object_new <span class="op">(</span>TFE_TYPE_TEXT_VIEW<span class="op">,</span> NULL<span class="op">));</span></span> <span id="cb2-32"><a href="#cb2-32" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> GTK_WIDGET <span class="op">(</span>g_object_new <span class="op">(</span>TFE_TYPE_TEXT_VIEW<span class="op">,</span> NULL<span class="op">));</span></span>
<span id="cb2-33"><a href="#cb2-33" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div> <span id="cb2-33"><a href="#cb2-33" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>If you are curious about the background theory of this program,
thats good, because knowing the theory is very important if you want to
program GTK applications. Look at <a
href="https://docs.gtk.org/gobject/">GObject API Reference</a>. All you
need is described there, or refer to <a
href="https://github.com/ToshioCP/Gobject-tutorial">GObject
tutorial</a>. Its a tough journey especially for beginners so for now,
you dont need to know about this difficult theory. It is enough to just
remember the instructions below.</p>
<ul> <ul>
<li>TfeTextView is divided into two parts. Tfe and TextView. Tfe is <li>TfeTextView is divided into two parts. Tfe and TextView. Tfe is
called the prefix, namespace or module. TextView is called the called prefix or namespace. TextView is called object.</li>
object.</li>
<li>There are three differnet identifier patterns. TfeTextView (camel <li>There are three differnet identifier patterns. TfeTextView (camel
case), tfe_text_view (this is used to write functions) and TFE_TEXT_VIEW case), tfe_text_view (this is used for functions) and TFE_TEXT_VIEW
(This is used to cast a pointer to point TfeTextView type).</li> (This is used to cast a object to TfeTextView).</li>
<li>First, define TFE_TYPE_TEXT_VIEW macro as tfe_text_view_get_type (). <li>First, define TFE_TYPE_TEXT_VIEW macro as tfe_text_view_get_type ().
The name is always (prefix)_TYPE_(object) and the letters are upper The name is always (prefix)_TYPE_(object) and the letters are upper
case. And the replacement text is always (prefix)_(object)_get_type () case. And the replacement text is always (prefix)_(object)_get_type ()
and the letters are lower case.</li> and the letters are lower case. This definition is put before
<li>Next, use G_DECLARE_FINAL_TYPE macro. The arguments are the child G_DECLARE_FINAL_TYPE macro.</li>
object name in camel case, lower case with underscore, prefix (upper <li>The arguments of G_DECLARE_FINAL_TYPE macro are the child class name
case), object (upper case with underscore) and parent object name (camel in camel case, lower case with underscore, prefix (upper case), object
case).</li> (upper case with underscore) and parent class name (camel case). The
following two C structure is declared in the expansion of the macro.
<ul>
<li><code>typedef struct _TfeTextView TfeTextView</code></li>
<li><code>typedef struct {GtkTextViewClass parent_class; } TfeTextViewClass;</code></li>
</ul></li>
<li>These declaration tells us that TfeTextView and TfeTextViewClass are
C structures. “TfeTextView” has two meanings, class name and C structure
name. The C structure TfeTextView is called object. Similarly,
TfeTextViewClass is called class.</li>
<li>Declare the structure _TfeTextView. The underscore is necessary. The <li>Declare the structure _TfeTextView. The underscore is necessary. The
first member is the parent object. Notice this is not a pointer but the first member is the parent object (C structure). Notice this is not a
object itself. The second member and after are members of the child pointer but the object itself. The second member and after are members
object. TfeTextView structure has a pointer to a GFile instance as a of the child object. TfeTextView structure has a pointer to a GFile
member.</li> instance as a member.</li>
<li>Use G_DEFINE_TYPE macro. The arguments are the child object name in <li>G_DEFINE_TYPE macro. The arguments are the child object name in
camel case, lower case with underscore and parent object type camel case, lower case with underscore and parent object type
(prefix)_TYPE_(module).</li> (prefix)_TYPE_(module). This macro is mainly used to register the new
<li>Define instance init function (tfe_text_view_init). Usually you class to the type system. Type system is a base system of GObject. Every
dont need to do anything.</li> class has its own type. The types of GObject, GtkWidget and TfeTextView
<li>Define class init function (tfe_text_view_class_init). You dont are G_TYPE_OBJECT, GTK_TYPE_WIDGET and TFE_TYPE_TEXT_VIEW respectively.
need to do anything in this object.</li> Such type (for example, TFE_TYPE_TEXT_VIEW) is a macro and it is
<li>Write function codes you want to add (tfe_text_view_set_file and expanded to a function (tfe_text_view_get_type()). It returns a integer
tfe_text_view_get_file). <code>tv</code> is a pointer to the TfeTextView which is unique among all GObject system classes.</li>
object instance which is a C-structure declared with the tag <li>Instance init function (tfe_text_view_init) is called when the
_TfeTextView. So, the structure has a member <code>file</code> as a instance is created. It is the same as a constructor in other object
pointer to a GFile instance. <code>tv-&gt;file = f</code> is an oriented languages.</li>
assignment of <code>f</code> to a member <code>file</code> of the <li>Class init function (tfe_text_view_class_init) is called when the
structure pointed by <code>tv</code>. This is an example how to use the class is created.</li>
extended memory in a child widget.</li> <li>Two functions tfe_text_view_set_file and tfe_text_view_get_file are
<li>Write a function to create an instance. Its name is public functions. Public functions are open and you can call them
(prefix)_(object)_new. If the parent object function needs parameters, anywhere. They are the same as public method in other object oriented
this function also need them. You sometimes might want to add some languages. <code>tv</code> is a pointer to the TfeTextView object (C
parameters. Its your choice. Use g_object_new function to create the structure). It has a member <code>file</code> and it is pointed by
instance. The arguments are (prefix)_TYPE_(object), a list to initialize <code>tv-&gt;file</code>.</li>
properties and NULL. In this code no property needs to be initialized. <li>TfeTextView instance creation function is
<code>tfe_text_view_new</code>. Its name is (prefix)_(object)_new. It
uses g_object_new function to create the instance. The arguments are
(prefix)_TYPE_(object), a list to initialize properties and NULL. NULL
is the end mark of the property list. No property is initialized here.
And the return value is casted to GtkWidget.</li> And the return value is casted to GtkWidget.</li>
</ul> </ul>
<p>This program is not perfect. It has some problems. It will be <p>This program shows the outline how to define a child class.</p>
modified later.</p>
<h2 id="close-request-signal">Close-request signal</h2> <h2 id="close-request-signal">Close-request signal</h2>
<p>Imagine that you are using this editor. First, you run the editor <p>Imagine that you are using this editor. First, you run the editor
with arguments. The arguments are filenames. The editor reads the files with arguments. The arguments are filenames. The editor reads the files
and shows the window with the text of files in it. Then you edit the and shows the window with the text of files in it. Then you edit the
text. After you finish editing, you exit the editor. The editor updates text. After you finish editing, you exit the editor. The editor updates
files just before the window closes.</p> files just before the window closes.</p>
<p>GtkWindow emits the “close-request” signal before it closes. We <p>GtkWindow emits the “close-request” signal before it closes. We will
connect the signal and the handler <code>before_close</code>. A handler connect the signal and the handler <code>before_close</code>. (A handler
is a C function. When a function is connected to a certain signal, we is a C function which is connected to a signal.) The function
call it a handler. The function <code>before_close</code> is invoked <code>before_close</code> is called when the signal “close-request” is
when the signal “close-request” is emitted.</p> emitted.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></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>before_close<span class="op">),</span> NULL<span class="op">);</span></span></code></pre></div> <div class="sourceCode" id="cb3"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></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>before_close<span class="op">),</span> NULL<span class="op">);</span></span></code></pre></div>
<p>The argument <code>win</code> is a GtkApplicationWindow, in which the <p>The argument <code>win</code> is a GtkApplicationWindow, in which the
signal “close-request” is defined, and <code>before_close</code> is the signal “close-request” is defined, and <code>before_close</code> is the
@ -272,58 +272,70 @@ handler. <code>G_CALLBACK</code> cast is necessary for the handler. The
program of <code>before_close</code> is as follows.</p> program of <code>before_close</code> is as follows.</p>
<div class="sourceCode" id="cb4"><pre <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> gboolean</span> class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1"></a><span class="dt">static</span> gboolean</span>
<span id="cb4-2"><a href="#cb4-2"></a>before_close <span class="op">(</span>GtkWindow <span class="op">*</span>win<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span> <span id="cb4-2"><a href="#cb4-2"></a>before_close <span class="op">(</span>GtkWindow <span class="op">*</span>win<span class="op">,</span> GtkWidget <span class="op">*</span>nb<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-3"><a href="#cb4-3"></a> GtkWidget <span class="op">*</span>nb <span class="op">=</span> GTK_WIDGET <span class="op">(</span>user_data<span class="op">);</span></span> <span id="cb4-3"><a href="#cb4-3"></a> GtkWidget <span class="op">*</span>scr<span class="op">;</span></span>
<span id="cb4-4"><a href="#cb4-4"></a> GtkWidget <span class="op">*</span>scr<span class="op">;</span></span> <span id="cb4-4"><a href="#cb4-4"></a> GtkWidget <span class="op">*</span>tv<span class="op">;</span></span>
<span id="cb4-5"><a href="#cb4-5"></a> GtkWidget <span class="op">*</span>tv<span class="op">;</span></span> <span id="cb4-5"><a href="#cb4-5"></a> GFile <span class="op">*</span>file<span class="op">;</span></span>
<span id="cb4-6"><a href="#cb4-6"></a> GFile <span class="op">*</span>file<span class="op">;</span></span> <span id="cb4-6"><a href="#cb4-6"></a> <span class="dt">char</span> <span class="op">*</span>pathname<span class="op">;</span></span>
<span id="cb4-7"><a href="#cb4-7"></a> <span class="dt">char</span> <span class="op">*</span>pathname<span class="op">;</span></span> <span id="cb4-7"><a href="#cb4-7"></a> GtkTextBuffer <span class="op">*</span>tb<span class="op">;</span></span>
<span id="cb4-8"><a href="#cb4-8"></a> GtkTextBuffer <span class="op">*</span>tb<span class="op">;</span></span> <span id="cb4-8"><a href="#cb4-8"></a> GtkTextIter start_iter<span class="op">;</span></span>
<span id="cb4-9"><a href="#cb4-9"></a> GtkTextIter start_iter<span class="op">;</span></span> <span id="cb4-9"><a href="#cb4-9"></a> GtkTextIter end_iter<span class="op">;</span></span>
<span id="cb4-10"><a href="#cb4-10"></a> GtkTextIter end_iter<span class="op">;</span></span> <span id="cb4-10"><a href="#cb4-10"></a> <span class="dt">char</span> <span class="op">*</span>contents<span class="op">;</span></span>
<span id="cb4-11"><a href="#cb4-11"></a> <span class="dt">char</span> <span class="op">*</span>contents<span class="op">;</span></span> <span id="cb4-11"><a href="#cb4-11"></a> <span class="dt">unsigned</span> <span class="dt">int</span> n<span class="op">;</span></span>
<span id="cb4-12"><a href="#cb4-12"></a> <span class="dt">unsigned</span> <span class="dt">int</span> n<span class="op">;</span></span> <span id="cb4-12"><a href="#cb4-12"></a> <span class="dt">unsigned</span> <span class="dt">int</span> i<span class="op">;</span></span>
<span id="cb4-13"><a href="#cb4-13"></a> <span class="dt">unsigned</span> <span class="dt">int</span> i<span class="op">;</span></span> <span id="cb4-13"><a href="#cb4-13"></a></span>
<span id="cb4-14"><a href="#cb4-14"></a></span> <span id="cb4-14"><a href="#cb4-14"></a> n <span class="op">=</span> gtk_notebook_get_n_pages <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">));</span></span>
<span id="cb4-15"><a href="#cb4-15"></a> n <span class="op">=</span> gtk_notebook_get_n_pages <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">));</span></span> <span id="cb4-15"><a href="#cb4-15"></a> <span class="cf">for</span> <span class="op">(</span>i <span class="op">=</span> <span class="dv">0</span><span class="op">;</span> i <span class="op">&lt;</span> n<span class="op">;</span> <span class="op">++</span>i<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-16"><a href="#cb4-16"></a> <span class="cf">for</span> <span class="op">(</span>i <span class="op">=</span> <span class="dv">0</span><span class="op">;</span> i <span class="op">&lt;</span> n<span class="op">;</span> <span class="op">++</span>i<span class="op">)</span> <span class="op">{</span></span> <span id="cb4-16"><a href="#cb4-16"></a> scr <span class="op">=</span> gtk_notebook_get_nth_page <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">),</span> i<span class="op">);</span></span>
<span id="cb4-17"><a href="#cb4-17"></a> scr <span class="op">=</span> gtk_notebook_get_nth_page <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">),</span> i<span class="op">);</span></span> <span id="cb4-17"><a href="#cb4-17"></a> tv <span class="op">=</span> gtk_scrolled_window_get_child <span class="op">(</span>GTK_SCROLLED_WINDOW <span class="op">(</span>scr<span class="op">));</span></span>
<span id="cb4-18"><a href="#cb4-18"></a> tv <span class="op">=</span> gtk_scrolled_window_get_child <span class="op">(</span>GTK_SCROLLED_WINDOW <span class="op">(</span>scr<span class="op">));</span></span> <span id="cb4-18"><a href="#cb4-18"></a> file <span class="op">=</span> tfe_text_view_get_file <span class="op">(</span>TFE_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
<span id="cb4-19"><a href="#cb4-19"></a> file <span class="op">=</span> tfe_text_view_get_file <span class="op">(</span>TFE_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span> <span id="cb4-19"><a href="#cb4-19"></a> tb <span class="op">=</span> gtk_text_view_get_buffer <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
<span id="cb4-20"><a href="#cb4-20"></a> tb <span class="op">=</span> gtk_text_view_get_buffer <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span> <span id="cb4-20"><a href="#cb4-20"></a> gtk_text_buffer_get_bounds <span class="op">(</span>tb<span class="op">,</span> <span class="op">&amp;</span>start_iter<span class="op">,</span> <span class="op">&amp;</span>end_iter<span class="op">);</span></span>
<span id="cb4-21"><a href="#cb4-21"></a> gtk_text_buffer_get_bounds <span class="op">(</span>tb<span class="op">,</span> <span class="op">&amp;</span>start_iter<span class="op">,</span> <span class="op">&amp;</span>end_iter<span class="op">);</span></span> <span id="cb4-21"><a href="#cb4-21"></a> contents <span class="op">=</span> gtk_text_buffer_get_text <span class="op">(</span>tb<span class="op">,</span> <span class="op">&amp;</span>start_iter<span class="op">,</span> <span class="op">&amp;</span>end_iter<span class="op">,</span> FALSE<span class="op">);</span></span>
<span id="cb4-22"><a href="#cb4-22"></a> contents <span class="op">=</span> gtk_text_buffer_get_text <span class="op">(</span>tb<span class="op">,</span> <span class="op">&amp;</span>start_iter<span class="op">,</span> <span class="op">&amp;</span>end_iter<span class="op">,</span> FALSE<span class="op">);</span></span> <span id="cb4-22"><a href="#cb4-22"></a> <span class="cf">if</span> <span class="op">(!</span> g_file_replace_contents <span class="op">(</span>file<span class="op">,</span> contents<span class="op">,</span> strlen <span class="op">(</span>contents<span class="op">),</span> NULL<span class="op">,</span> TRUE<span class="op">,</span> G_FILE_CREATE_NONE<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">))</span> <span class="op">{</span></span>
<span id="cb4-23"><a href="#cb4-23"></a> <span class="cf">if</span> <span class="op">(!</span> g_file_replace_contents <span class="op">(</span>file<span class="op">,</span> contents<span class="op">,</span> strlen <span class="op">(</span>contents<span class="op">),</span> NULL<span class="op">,</span> TRUE<span class="op">,</span> G_FILE_CREATE_NONE<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">))</span> <span class="op">{</span></span> <span id="cb4-23"><a href="#cb4-23"></a> <span class="cf">if</span> <span class="op">((</span>pathname <span class="op">=</span> g_file_get_path <span class="op">(</span>file<span class="op">))</span> <span class="op">!=</span> NULL<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-24"><a href="#cb4-24"></a> pathname <span class="op">=</span> g_file_get_path <span class="op">(</span>file<span class="op">);</span></span> <span id="cb4-24"><a href="#cb4-24"></a> g_printerr <span class="op">(</span><span class="st">&quot;Can&#39;t save %s.&quot;</span><span class="op">,</span> pathname<span class="op">);</span></span>
<span id="cb4-25"><a href="#cb4-25"></a> g_print <span class="op">(</span><span class="st">&quot;ERROR : Can&#39;t save %s.&quot;</span><span class="op">,</span> pathname<span class="op">);</span></span> <span id="cb4-25"><a href="#cb4-25"></a> g_free <span class="op">(</span>pathname<span class="op">);</span></span>
<span id="cb4-26"><a href="#cb4-26"></a> g_free <span class="op">(</span>pathname<span class="op">);</span></span> <span id="cb4-26"><a href="#cb4-26"></a> <span class="op">}</span> <span class="cf">else</span></span>
<span id="cb4-27"><a href="#cb4-27"></a> <span class="op">}</span></span> <span id="cb4-27"><a href="#cb4-27"></a> g_printerr <span class="op">(</span><span class="st">&quot;Pathname not exist.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb4-28"><a href="#cb4-28"></a> g_free <span class="op">(</span>contents<span class="op">);</span></span> <span id="cb4-28"><a href="#cb4-28"></a> <span class="op">}</span></span>
<span id="cb4-29"><a href="#cb4-29"></a> <span class="op">}</span></span> <span id="cb4-29"><a href="#cb4-29"></a> g_free <span class="op">(</span>contents<span class="op">);</span></span>
<span id="cb4-30"><a href="#cb4-30"></a> <span class="cf">return</span> FALSE<span class="op">;</span></span> <span id="cb4-30"><a href="#cb4-30"></a> g_object_unref <span class="op">(</span>file<span class="op">);</span></span>
<span id="cb4-31"><a href="#cb4-31"></a><span class="op">}</span></span></code></pre></div> <span id="cb4-31"><a href="#cb4-31"></a> <span class="op">}</span></span>
<span id="cb4-32"><a href="#cb4-32"></a> <span class="cf">return</span> FALSE<span class="op">;</span></span>
<span id="cb4-33"><a href="#cb4-33"></a><span class="op">}</span></span></code></pre></div>
<p>The numbers on the left of items are line numbers in the source <p>The numbers on the left of items are line numbers in the source
code.</p> code.</p>
<ul> <ul>
<li>15: Gets the number of pages <code>nb</code> has.</li> <li>14: The number of pages of <code>nb</code> is assigned to
<li>16-29: For loop with regard to the index to each pages.</li> <code>n</code>.</li>
<li>17-19: Gets GtkScrolledWindow, TfeTextView and a pointer to GFile. <li>15-31: For loop with regard to the index to each pages.</li>
The pointer was stored when <code>app_open</code> handler had run. It <li>16-18: <code>scr</code>, <code>tv</code> and <code>file</code> is
will be shown later.</li> assigned pointers to the GtkScrolledWindow, TfeTextView and GFile. The
<li>20-22: Gets GtkTextBuffer and contents. <code>start_iter</code> and GFile of TfeTextView was stored when <code>app_open</code> handler was
<code>end_iter</code> are iterators of the buffer. I dont want to called. It will be shown later.</li>
explain them now because it would take a lot of time. Just remember <li>19-21: <code>tb</code> is assigned the GtkTextBuffer of the
these lines for the present.</li> TfeTextView. The buffer is accessed with iterators. Iterators points
<li>23-27: Writes the contents to the file. If it fails, it outputs an somewhere in the buffer. The function
error message.</li> <code>gtk_text_buffer_get_bounds</code> assigns the start and end of the
<li>28: Frees <code>contents</code>.</li> buffer to <code>start_iter</code> and <code>end_iter</code>
respectively. Then the function <code>gtk_text_buffer_get_text</code>
returns the text between <code>start_iter</code> and
<code>end_iter</code>, which is the whole text in the buffer.</li>
<li>22-28: The text is saved to the file. If it fails, error messages
are displayed.</li>
<li>29: <code>contents</code> are freed.</li>
<li>30: GFile is useless. <code>g_object_unref</code> decreases the
reference count of the GFile. Reference count will be explained in the
later section. The reference count will be zero in this program and the
GFile instance will destroy itself.</li>
</ul> </ul>
<h2 id="source-code-of-tfe1.c">Source code of tfe1.c</h2> <h2 id="source-code-of-tfe1.c">Source code of tfe1.c</h2>
<p>The following is the complete source code of <code>tfe1.c</code>.</p> <p>The following is the whole source code of <code>tfe1.c</code>.</p>
<div class="sourceCode" id="cb5"><pre <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="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span> class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb5-1"><a href="#cb5-1"></a><span class="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span>
<span id="cb5-2"><a href="#cb5-2"></a></span> <span id="cb5-2"><a href="#cb5-2"></a></span>
<span id="cb5-3"><a href="#cb5-3"></a><span class="co">/* Define TfeTextView Widget which is the child object of GtkTextView */</span></span> <span id="cb5-3"><a href="#cb5-3"></a><span class="co">/* Define TfeTextView Widget which is the child class of GtkTextView */</span></span>
<span id="cb5-4"><a href="#cb5-4"></a></span> <span id="cb5-4"><a href="#cb5-4"></a></span>
<span id="cb5-5"><a href="#cb5-5"></a><span class="pp">#define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type ()</span></span> <span id="cb5-5"><a href="#cb5-5"></a><span class="pp">#define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type ()</span></span>
<span id="cb5-6"><a href="#cb5-6"></a>G_DECLARE_FINAL_TYPE <span class="op">(</span>TfeTextView<span class="op">,</span> tfe_text_view<span class="op">,</span> TFE<span class="op">,</span> TEXT_VIEW<span class="op">,</span> GtkTextView<span class="op">)</span></span> <span id="cb5-6"><a href="#cb5-6"></a>G_DECLARE_FINAL_TYPE <span class="op">(</span>TfeTextView<span class="op">,</span> tfe_text_view<span class="op">,</span> TFE<span class="op">,</span> TEXT_VIEW<span class="op">,</span> GtkTextView<span class="op">)</span></span>
@ -338,32 +350,32 @@ class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span i
<span id="cb5-15"><a href="#cb5-15"></a></span> <span id="cb5-15"><a href="#cb5-15"></a></span>
<span id="cb5-16"><a href="#cb5-16"></a><span class="dt">static</span> <span class="dt">void</span></span> <span id="cb5-16"><a href="#cb5-16"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb5-17"><a href="#cb5-17"></a>tfe_text_view_init <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">)</span> <span class="op">{</span></span> <span id="cb5-17"><a href="#cb5-17"></a>tfe_text_view_init <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-18"><a href="#cb5-18"></a><span class="op">}</span></span> <span id="cb5-18"><a href="#cb5-18"></a> tv<span class="op">-&gt;</span>file <span class="op">=</span> NULL<span class="op">;</span></span>
<span id="cb5-19"><a href="#cb5-19"></a></span> <span id="cb5-19"><a href="#cb5-19"></a><span class="op">}</span></span>
<span id="cb5-20"><a href="#cb5-20"></a><span class="dt">static</span> <span class="dt">void</span></span> <span id="cb5-20"><a href="#cb5-20"></a></span>
<span id="cb5-21"><a href="#cb5-21"></a>tfe_text_view_class_init <span class="op">(</span>TfeTextViewClass <span class="op">*</span>class<span class="op">)</span> <span class="op">{</span></span> <span id="cb5-21"><a href="#cb5-21"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb5-22"><a href="#cb5-22"></a><span class="op">}</span></span> <span id="cb5-22"><a href="#cb5-22"></a>tfe_text_view_class_init <span class="op">(</span>TfeTextViewClass <span class="op">*</span>class<span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-23"><a href="#cb5-23"></a></span> <span id="cb5-23"><a href="#cb5-23"></a><span class="op">}</span></span>
<span id="cb5-24"><a href="#cb5-24"></a><span class="dt">void</span></span> <span id="cb5-24"><a href="#cb5-24"></a></span>
<span id="cb5-25"><a href="#cb5-25"></a>tfe_text_view_set_file <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">,</span> GFile <span class="op">*</span>f<span class="op">)</span> <span class="op">{</span></span> <span id="cb5-25"><a href="#cb5-25"></a><span class="dt">void</span></span>
<span id="cb5-26"><a href="#cb5-26"></a> tv <span class="op">-&gt;</span> file <span class="op">=</span> f<span class="op">;</span></span> <span id="cb5-26"><a href="#cb5-26"></a>tfe_text_view_set_file <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">,</span> GFile <span class="op">*</span>f<span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-27"><a href="#cb5-27"></a><span class="op">}</span></span> <span id="cb5-27"><a href="#cb5-27"></a> tv<span class="op">-&gt;</span>file <span class="op">=</span> f<span class="op">;</span></span>
<span id="cb5-28"><a href="#cb5-28"></a></span> <span id="cb5-28"><a href="#cb5-28"></a><span class="op">}</span></span>
<span id="cb5-29"><a href="#cb5-29"></a>GFile <span class="op">*</span></span> <span id="cb5-29"><a href="#cb5-29"></a></span>
<span id="cb5-30"><a href="#cb5-30"></a>tfe_text_view_get_file <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">)</span> <span class="op">{</span></span> <span id="cb5-30"><a href="#cb5-30"></a>GFile <span class="op">*</span></span>
<span id="cb5-31"><a href="#cb5-31"></a> <span class="cf">return</span> tv <span class="op">-&gt;</span> file<span class="op">;</span></span> <span id="cb5-31"><a href="#cb5-31"></a>tfe_text_view_get_file <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-32"><a href="#cb5-32"></a><span class="op">}</span></span> <span id="cb5-32"><a href="#cb5-32"></a> <span class="cf">return</span> tv <span class="op">-&gt;</span> file<span class="op">;</span></span>
<span id="cb5-33"><a href="#cb5-33"></a></span> <span id="cb5-33"><a href="#cb5-33"></a><span class="op">}</span></span>
<span id="cb5-34"><a href="#cb5-34"></a>GtkWidget <span class="op">*</span></span> <span id="cb5-34"><a href="#cb5-34"></a></span>
<span id="cb5-35"><a href="#cb5-35"></a>tfe_text_view_new <span class="op">(</span><span class="dt">void</span><span class="op">)</span> <span class="op">{</span></span> <span id="cb5-35"><a href="#cb5-35"></a>GtkWidget <span class="op">*</span></span>
<span id="cb5-36"><a href="#cb5-36"></a> <span class="cf">return</span> GTK_WIDGET <span class="op">(</span>g_object_new <span class="op">(</span>TFE_TYPE_TEXT_VIEW<span class="op">,</span> NULL<span class="op">));</span></span> <span id="cb5-36"><a href="#cb5-36"></a>tfe_text_view_new <span class="op">(</span><span class="dt">void</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-37"><a href="#cb5-37"></a><span class="op">}</span></span> <span id="cb5-37"><a href="#cb5-37"></a> <span class="cf">return</span> GTK_WIDGET <span class="op">(</span>g_object_new <span class="op">(</span>TFE_TYPE_TEXT_VIEW<span class="op">,</span> NULL<span class="op">));</span></span>
<span id="cb5-38"><a href="#cb5-38"></a></span> <span id="cb5-38"><a href="#cb5-38"></a><span class="op">}</span></span>
<span id="cb5-39"><a href="#cb5-39"></a><span class="co">/* ---------- end of the definition of TfeTextView ---------- */</span></span> <span id="cb5-39"><a href="#cb5-39"></a></span>
<span id="cb5-40"><a href="#cb5-40"></a></span> <span id="cb5-40"><a href="#cb5-40"></a><span class="co">/* ---------- end of the definition of TfeTextView ---------- */</span></span>
<span id="cb5-41"><a href="#cb5-41"></a><span class="dt">static</span> gboolean</span> <span id="cb5-41"><a href="#cb5-41"></a></span>
<span id="cb5-42"><a href="#cb5-42"></a>before_close <span class="op">(</span>GtkWindow <span class="op">*</span>win<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span> <span id="cb5-42"><a href="#cb5-42"></a><span class="dt">static</span> gboolean</span>
<span id="cb5-43"><a href="#cb5-43"></a> GtkWidget <span class="op">*</span>nb <span class="op">=</span> GTK_WIDGET <span class="op">(</span>user_data<span class="op">);</span></span> <span id="cb5-43"><a href="#cb5-43"></a>before_close <span class="op">(</span>GtkWindow <span class="op">*</span>win<span class="op">,</span> GtkWidget <span class="op">*</span>nb<span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-44"><a href="#cb5-44"></a> GtkWidget <span class="op">*</span>scr<span class="op">;</span></span> <span id="cb5-44"><a href="#cb5-44"></a> GtkWidget <span class="op">*</span>scr<span class="op">;</span></span>
<span id="cb5-45"><a href="#cb5-45"></a> GtkWidget <span class="op">*</span>tv<span class="op">;</span></span> <span id="cb5-45"><a href="#cb5-45"></a> GtkWidget <span class="op">*</span>tv<span class="op">;</span></span>
<span id="cb5-46"><a href="#cb5-46"></a> GFile <span class="op">*</span>file<span class="op">;</span></span> <span id="cb5-46"><a href="#cb5-46"></a> GFile <span class="op">*</span>file<span class="op">;</span></span>
@ -384,96 +396,104 @@ class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span i
<span id="cb5-61"><a href="#cb5-61"></a> gtk_text_buffer_get_bounds <span class="op">(</span>tb<span class="op">,</span> <span class="op">&amp;</span>start_iter<span class="op">,</span> <span class="op">&amp;</span>end_iter<span class="op">);</span></span> <span id="cb5-61"><a href="#cb5-61"></a> gtk_text_buffer_get_bounds <span class="op">(</span>tb<span class="op">,</span> <span class="op">&amp;</span>start_iter<span class="op">,</span> <span class="op">&amp;</span>end_iter<span class="op">);</span></span>
<span id="cb5-62"><a href="#cb5-62"></a> contents <span class="op">=</span> gtk_text_buffer_get_text <span class="op">(</span>tb<span class="op">,</span> <span class="op">&amp;</span>start_iter<span class="op">,</span> <span class="op">&amp;</span>end_iter<span class="op">,</span> FALSE<span class="op">);</span></span> <span id="cb5-62"><a href="#cb5-62"></a> contents <span class="op">=</span> gtk_text_buffer_get_text <span class="op">(</span>tb<span class="op">,</span> <span class="op">&amp;</span>start_iter<span class="op">,</span> <span class="op">&amp;</span>end_iter<span class="op">,</span> FALSE<span class="op">);</span></span>
<span id="cb5-63"><a href="#cb5-63"></a> <span class="cf">if</span> <span class="op">(!</span> g_file_replace_contents <span class="op">(</span>file<span class="op">,</span> contents<span class="op">,</span> strlen <span class="op">(</span>contents<span class="op">),</span> NULL<span class="op">,</span> TRUE<span class="op">,</span> G_FILE_CREATE_NONE<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">))</span> <span class="op">{</span></span> <span id="cb5-63"><a href="#cb5-63"></a> <span class="cf">if</span> <span class="op">(!</span> g_file_replace_contents <span class="op">(</span>file<span class="op">,</span> contents<span class="op">,</span> strlen <span class="op">(</span>contents<span class="op">),</span> NULL<span class="op">,</span> TRUE<span class="op">,</span> G_FILE_CREATE_NONE<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">))</span> <span class="op">{</span></span>
<span id="cb5-64"><a href="#cb5-64"></a> pathname <span class="op">=</span> g_file_get_path <span class="op">(</span>file<span class="op">);</span></span> <span id="cb5-64"><a href="#cb5-64"></a> <span class="cf">if</span> <span class="op">((</span>pathname <span class="op">=</span> g_file_get_path <span class="op">(</span>file<span class="op">))</span> <span class="op">!=</span> NULL<span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-65"><a href="#cb5-65"></a> g_print <span class="op">(</span><span class="st">&quot;ERROR : Can&#39;t save %s.&quot;</span><span class="op">,</span> pathname<span class="op">);</span></span> <span id="cb5-65"><a href="#cb5-65"></a> g_printerr <span class="op">(</span><span class="st">&quot;Can&#39;t save %s.&quot;</span><span class="op">,</span> pathname<span class="op">);</span></span>
<span id="cb5-66"><a href="#cb5-66"></a> g_free <span class="op">(</span>pathname<span class="op">);</span></span> <span id="cb5-66"><a href="#cb5-66"></a> g_free <span class="op">(</span>pathname<span class="op">);</span></span>
<span id="cb5-67"><a href="#cb5-67"></a> <span class="op">}</span></span> <span id="cb5-67"><a href="#cb5-67"></a> <span class="op">}</span> <span class="cf">else</span></span>
<span id="cb5-68"><a href="#cb5-68"></a> g_free <span class="op">(</span>contents<span class="op">);</span></span> <span id="cb5-68"><a href="#cb5-68"></a> g_printerr <span class="op">(</span><span class="st">&quot;Pathname not exist.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb5-69"><a href="#cb5-69"></a> <span class="op">}</span></span> <span id="cb5-69"><a href="#cb5-69"></a> <span class="op">}</span></span>
<span id="cb5-70"><a href="#cb5-70"></a> <span class="cf">return</span> FALSE<span class="op">;</span></span> <span id="cb5-70"><a href="#cb5-70"></a> g_free <span class="op">(</span>contents<span class="op">);</span></span>
<span id="cb5-71"><a href="#cb5-71"></a><span class="op">}</span></span> <span id="cb5-71"><a href="#cb5-71"></a> g_object_unref <span class="op">(</span>file<span class="op">);</span></span>
<span id="cb5-72"><a href="#cb5-72"></a></span> <span id="cb5-72"><a href="#cb5-72"></a> <span class="op">}</span></span>
<span id="cb5-73"><a href="#cb5-73"></a><span class="dt">static</span> <span class="dt">void</span></span> <span id="cb5-73"><a href="#cb5-73"></a> <span class="cf">return</span> FALSE<span class="op">;</span></span>
<span id="cb5-74"><a href="#cb5-74"></a>app_activate <span class="op">(</span>GApplication <span class="op">*</span>app<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span> <span id="cb5-74"><a href="#cb5-74"></a><span class="op">}</span></span>
<span id="cb5-75"><a href="#cb5-75"></a> g_print <span class="op">(</span><span class="st">&quot;You need to give filenames as arguments.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span> <span id="cb5-75"><a href="#cb5-75"></a></span>
<span id="cb5-76"><a href="#cb5-76"></a><span class="op">}</span></span> <span id="cb5-76"><a href="#cb5-76"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb5-77"><a href="#cb5-77"></a></span> <span id="cb5-77"><a href="#cb5-77"></a>app_activate <span class="op">(</span>GApplication <span class="op">*</span>app<span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-78"><a href="#cb5-78"></a><span class="dt">static</span> <span class="dt">void</span></span> <span id="cb5-78"><a href="#cb5-78"></a> g_print <span class="op">(</span><span class="st">&quot;You need to give filenames as arguments.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb5-79"><a href="#cb5-79"></a>app_open <span class="op">(</span>GApplication <span class="op">*</span>app<span class="op">,</span> GFile <span class="op">**</span> files<span class="op">,</span> gint n_files<span class="op">,</span> gchar <span class="op">*</span>hint<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span> <span id="cb5-79"><a href="#cb5-79"></a><span class="op">}</span></span>
<span id="cb5-80"><a href="#cb5-80"></a> GtkWidget <span class="op">*</span>win<span class="op">;</span></span> <span id="cb5-80"><a href="#cb5-80"></a></span>
<span id="cb5-81"><a href="#cb5-81"></a> GtkWidget <span class="op">*</span>nb<span class="op">;</span></span> <span id="cb5-81"><a href="#cb5-81"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb5-82"><a href="#cb5-82"></a> GtkWidget <span class="op">*</span>lab<span class="op">;</span></span> <span id="cb5-82"><a href="#cb5-82"></a>app_open <span class="op">(</span>GApplication <span class="op">*</span>app<span class="op">,</span> GFile <span class="op">**</span> files<span class="op">,</span> gint n_files<span class="op">,</span> gchar <span class="op">*</span>hint<span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-83"><a href="#cb5-83"></a> GtkNotebookPage <span class="op">*</span>nbp<span class="op">;</span></span> <span id="cb5-83"><a href="#cb5-83"></a> GtkWidget <span class="op">*</span>win<span class="op">;</span></span>
<span id="cb5-84"><a href="#cb5-84"></a> GtkWidget <span class="op">*</span>scr<span class="op">;</span></span> <span id="cb5-84"><a href="#cb5-84"></a> GtkWidget <span class="op">*</span>nb<span class="op">;</span></span>
<span id="cb5-85"><a href="#cb5-85"></a> GtkWidget <span class="op">*</span>tv<span class="op">;</span></span> <span id="cb5-85"><a href="#cb5-85"></a> GtkWidget <span class="op">*</span>lab<span class="op">;</span></span>
<span id="cb5-86"><a href="#cb5-86"></a> GtkTextBuffer <span class="op">*</span>tb<span class="op">;</span></span> <span id="cb5-86"><a href="#cb5-86"></a> GtkNotebookPage <span class="op">*</span>nbp<span class="op">;</span></span>
<span id="cb5-87"><a href="#cb5-87"></a> <span class="dt">char</span> <span class="op">*</span>contents<span class="op">;</span></span> <span id="cb5-87"><a href="#cb5-87"></a> GtkWidget <span class="op">*</span>scr<span class="op">;</span></span>
<span id="cb5-88"><a href="#cb5-88"></a> gsize length<span class="op">;</span></span> <span id="cb5-88"><a href="#cb5-88"></a> GtkWidget <span class="op">*</span>tv<span class="op">;</span></span>
<span id="cb5-89"><a href="#cb5-89"></a> <span class="dt">char</span> <span class="op">*</span>filename<span class="op">;</span></span> <span id="cb5-89"><a href="#cb5-89"></a> GtkTextBuffer <span class="op">*</span>tb<span class="op">;</span></span>
<span id="cb5-90"><a href="#cb5-90"></a> <span class="dt">int</span> i<span class="op">;</span></span> <span id="cb5-90"><a href="#cb5-90"></a> <span class="dt">char</span> <span class="op">*</span>contents<span class="op">;</span></span>
<span id="cb5-91"><a href="#cb5-91"></a></span> <span id="cb5-91"><a href="#cb5-91"></a> gsize length<span class="op">;</span></span>
<span id="cb5-92"><a href="#cb5-92"></a> win <span class="op">=</span> gtk_application_window_new <span class="op">(</span>GTK_APPLICATION <span class="op">(</span>app<span class="op">));</span></span> <span id="cb5-92"><a href="#cb5-92"></a> <span class="dt">char</span> <span class="op">*</span>filename<span class="op">;</span></span>
<span id="cb5-93"><a href="#cb5-93"></a> gtk_window_set_title <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="st">&quot;file editor&quot;</span><span class="op">);</span></span> <span id="cb5-93"><a href="#cb5-93"></a> <span class="dt">int</span> i<span class="op">;</span></span>
<span id="cb5-94"><a href="#cb5-94"></a> gtk_window_maximize <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span> <span id="cb5-94"><a href="#cb5-94"></a></span>
<span id="cb5-95"><a href="#cb5-95"></a></span> <span id="cb5-95"><a href="#cb5-95"></a> win <span class="op">=</span> gtk_application_window_new <span class="op">(</span>GTK_APPLICATION <span class="op">(</span>app<span class="op">));</span></span>
<span id="cb5-96"><a href="#cb5-96"></a> nb <span class="op">=</span> gtk_notebook_new <span class="op">();</span></span> <span id="cb5-96"><a href="#cb5-96"></a> gtk_window_set_title <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="st">&quot;file editor&quot;</span><span class="op">);</span></span>
<span id="cb5-97"><a href="#cb5-97"></a> gtk_window_set_child <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> nb<span class="op">);</span></span> <span id="cb5-97"><a href="#cb5-97"></a> gtk_window_set_default_size <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="dv">600</span><span class="op">,</span> <span class="dv">400</span><span class="op">);</span></span>
<span id="cb5-98"><a href="#cb5-98"></a></span> <span id="cb5-98"><a href="#cb5-98"></a></span>
<span id="cb5-99"><a href="#cb5-99"></a> <span class="cf">for</span> <span class="op">(</span>i <span class="op">=</span> <span class="dv">0</span><span class="op">;</span> i <span class="op">&lt;</span> n_files<span class="op">;</span> i<span class="op">++)</span> <span class="op">{</span></span> <span id="cb5-99"><a href="#cb5-99"></a> nb <span class="op">=</span> gtk_notebook_new <span class="op">();</span></span>
<span id="cb5-100"><a href="#cb5-100"></a> <span class="cf">if</span> <span class="op">(</span>g_file_load_contents <span class="op">(</span>files<span class="op">[</span>i<span class="op">],</span> NULL<span class="op">,</span> <span class="op">&amp;</span>contents<span class="op">,</span> <span class="op">&amp;</span>length<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">))</span> <span class="op">{</span></span> <span id="cb5-100"><a href="#cb5-100"></a> gtk_window_set_child <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> nb<span class="op">);</span></span>
<span id="cb5-101"><a href="#cb5-101"></a> scr <span class="op">=</span> gtk_scrolled_window_new <span class="op">();</span></span> <span id="cb5-101"><a href="#cb5-101"></a></span>
<span id="cb5-102"><a href="#cb5-102"></a> tv <span class="op">=</span> tfe_text_view_new <span class="op">();</span></span> <span id="cb5-102"><a href="#cb5-102"></a> <span class="cf">for</span> <span class="op">(</span>i <span class="op">=</span> <span class="dv">0</span><span class="op">;</span> i <span class="op">&lt;</span> n_files<span class="op">;</span> i<span class="op">++)</span> <span class="op">{</span></span>
<span id="cb5-103"><a href="#cb5-103"></a> tb <span class="op">=</span> gtk_text_view_get_buffer <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span> <span id="cb5-103"><a href="#cb5-103"></a> <span class="cf">if</span> <span class="op">(</span>g_file_load_contents <span class="op">(</span>files<span class="op">[</span>i<span class="op">],</span> NULL<span class="op">,</span> <span class="op">&amp;</span>contents<span class="op">,</span> <span class="op">&amp;</span>length<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">))</span> <span class="op">{</span></span>
<span id="cb5-104"><a href="#cb5-104"></a> gtk_text_view_set_wrap_mode <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> GTK_WRAP_WORD_CHAR<span class="op">);</span></span> <span id="cb5-104"><a href="#cb5-104"></a> scr <span class="op">=</span> gtk_scrolled_window_new <span class="op">();</span></span>
<span id="cb5-105"><a href="#cb5-105"></a> gtk_scrolled_window_set_child <span class="op">(</span>GTK_SCROLLED_WINDOW <span class="op">(</span>scr<span class="op">),</span> tv<span class="op">);</span></span> <span id="cb5-105"><a href="#cb5-105"></a> tv <span class="op">=</span> tfe_text_view_new <span class="op">();</span></span>
<span id="cb5-106"><a href="#cb5-106"></a></span> <span id="cb5-106"><a href="#cb5-106"></a> tb <span class="op">=</span> gtk_text_view_get_buffer <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
<span id="cb5-107"><a href="#cb5-107"></a> tfe_text_view_set_file <span class="op">(</span>TFE_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> g_file_dup <span class="op">(</span>files<span class="op">[</span>i<span class="op">]));</span></span> <span id="cb5-107"><a href="#cb5-107"></a> gtk_text_view_set_wrap_mode <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> GTK_WRAP_WORD_CHAR<span class="op">);</span></span>
<span id="cb5-108"><a href="#cb5-108"></a> gtk_text_buffer_set_text <span class="op">(</span>tb<span class="op">,</span> contents<span class="op">,</span> length<span class="op">);</span></span> <span id="cb5-108"><a href="#cb5-108"></a> gtk_scrolled_window_set_child <span class="op">(</span>GTK_SCROLLED_WINDOW <span class="op">(</span>scr<span class="op">),</span> tv<span class="op">);</span></span>
<span id="cb5-109"><a href="#cb5-109"></a> g_free <span class="op">(</span>contents<span class="op">);</span></span> <span id="cb5-109"><a href="#cb5-109"></a></span>
<span id="cb5-110"><a href="#cb5-110"></a> filename <span class="op">=</span> g_file_get_basename <span class="op">(</span>files<span class="op">[</span>i<span class="op">]);</span></span> <span id="cb5-110"><a href="#cb5-110"></a> tfe_text_view_set_file <span class="op">(</span>TFE_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> g_file_dup <span class="op">(</span>files<span class="op">[</span>i<span class="op">]));</span></span>
<span id="cb5-111"><a href="#cb5-111"></a> lab <span class="op">=</span> gtk_label_new <span class="op">(</span>filename<span class="op">);</span></span> <span id="cb5-111"><a href="#cb5-111"></a> gtk_text_buffer_set_text <span class="op">(</span>tb<span class="op">,</span> contents<span class="op">,</span> length<span class="op">);</span></span>
<span id="cb5-112"><a href="#cb5-112"></a> gtk_notebook_append_page <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">),</span> scr<span class="op">,</span> lab<span class="op">);</span></span> <span id="cb5-112"><a href="#cb5-112"></a> g_free <span class="op">(</span>contents<span class="op">);</span></span>
<span id="cb5-113"><a href="#cb5-113"></a> nbp <span class="op">=</span> gtk_notebook_get_page <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">),</span> scr<span class="op">);</span></span> <span id="cb5-113"><a href="#cb5-113"></a> filename <span class="op">=</span> g_file_get_basename <span class="op">(</span>files<span class="op">[</span>i<span class="op">]);</span></span>
<span id="cb5-114"><a href="#cb5-114"></a> g_object_set <span class="op">(</span>nbp<span class="op">,</span> <span class="st">&quot;tab-expand&quot;</span><span class="op">,</span> TRUE<span class="op">,</span> NULL<span class="op">);</span></span> <span id="cb5-114"><a href="#cb5-114"></a> lab <span class="op">=</span> gtk_label_new <span class="op">(</span>filename<span class="op">);</span></span>
<span id="cb5-115"><a href="#cb5-115"></a> g_free <span class="op">(</span>filename<span class="op">);</span></span> <span id="cb5-115"><a href="#cb5-115"></a> gtk_notebook_append_page <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">),</span> scr<span class="op">,</span> lab<span class="op">);</span></span>
<span id="cb5-116"><a href="#cb5-116"></a> <span class="op">}</span> <span class="cf">else</span> <span class="cf">if</span> <span class="op">((</span>filename <span class="op">=</span> g_file_get_path <span class="op">(</span>files<span class="op">[</span>i<span class="op">]))</span> <span class="op">!=</span> NULL<span class="op">)</span> <span class="op">{</span></span> <span id="cb5-116"><a href="#cb5-116"></a> nbp <span class="op">=</span> gtk_notebook_get_page <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">),</span> scr<span class="op">);</span></span>
<span id="cb5-117"><a href="#cb5-117"></a> g_print <span class="op">(</span><span class="st">&quot;No such file: %s.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">,</span> filename<span class="op">);</span></span> <span id="cb5-117"><a href="#cb5-117"></a> g_object_set <span class="op">(</span>nbp<span class="op">,</span> <span class="st">&quot;tab-expand&quot;</span><span class="op">,</span> TRUE<span class="op">,</span> NULL<span class="op">);</span></span>
<span id="cb5-118"><a href="#cb5-118"></a> g_free <span class="op">(</span>filename<span class="op">);</span></span> <span id="cb5-118"><a href="#cb5-118"></a> g_free <span class="op">(</span>filename<span class="op">);</span></span>
<span id="cb5-119"><a href="#cb5-119"></a> <span class="op">}</span> <span class="cf">else</span></span> <span id="cb5-119"><a href="#cb5-119"></a> <span class="op">}</span> <span class="cf">else</span> <span class="cf">if</span> <span class="op">((</span>filename <span class="op">=</span> g_file_get_path <span class="op">(</span>files<span class="op">[</span>i<span class="op">]))</span> <span class="op">!=</span> NULL<span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-120"><a href="#cb5-120"></a> g_print <span class="op">(</span><span class="st">&quot;No valid file is given</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span> <span id="cb5-120"><a href="#cb5-120"></a> g_print <span class="op">(</span><span class="st">&quot;No such file: %s.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">,</span> filename<span class="op">);</span></span>
<span id="cb5-121"><a href="#cb5-121"></a> <span class="op">}</span></span> <span id="cb5-121"><a href="#cb5-121"></a> g_free <span class="op">(</span>filename<span class="op">);</span></span>
<span id="cb5-122"><a href="#cb5-122"></a> <span class="cf">if</span> <span class="op">(</span>gtk_notebook_get_n_pages <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">))</span> <span class="op">&gt;</span> <span class="dv">0</span><span class="op">)</span> <span class="op">{</span></span> <span id="cb5-122"><a href="#cb5-122"></a> <span class="op">}</span> <span class="cf">else</span></span>
<span id="cb5-123"><a href="#cb5-123"></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>before_close<span class="op">),</span> nb<span class="op">);</span></span> <span id="cb5-123"><a href="#cb5-123"></a> g_print <span class="op">(</span><span class="st">&quot;No valid file is given</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb5-124"><a href="#cb5-124"></a> gtk_widget_show <span class="op">(</span>win<span class="op">);</span></span> <span id="cb5-124"><a href="#cb5-124"></a> <span class="op">}</span></span>
<span id="cb5-125"><a href="#cb5-125"></a> <span class="op">}</span> <span class="cf">else</span></span> <span id="cb5-125"><a href="#cb5-125"></a> <span class="cf">if</span> <span class="op">(</span>gtk_notebook_get_n_pages <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">))</span> <span class="op">&gt;</span> <span class="dv">0</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-126"><a href="#cb5-126"></a> gtk_window_destroy <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span> <span id="cb5-126"><a href="#cb5-126"></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>before_close<span class="op">),</span> nb<span class="op">);</span></span>
<span id="cb5-127"><a href="#cb5-127"></a><span class="op">}</span></span> <span id="cb5-127"><a href="#cb5-127"></a> gtk_widget_show <span class="op">(</span>win<span class="op">);</span></span>
<span id="cb5-128"><a href="#cb5-128"></a></span> <span id="cb5-128"><a href="#cb5-128"></a> <span class="op">}</span> <span class="cf">else</span></span>
<span id="cb5-129"><a href="#cb5-129"></a><span class="dt">int</span></span> <span id="cb5-129"><a href="#cb5-129"></a> gtk_window_destroy <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
<span id="cb5-130"><a href="#cb5-130"></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="cb5-130"><a href="#cb5-130"></a><span class="op">}</span></span>
<span id="cb5-131"><a href="#cb5-131"></a> GtkApplication <span class="op">*</span>app<span class="op">;</span></span> <span id="cb5-131"><a href="#cb5-131"></a></span>
<span id="cb5-132"><a href="#cb5-132"></a> <span class="dt">int</span> stat<span class="op">;</span></span> <span id="cb5-132"><a href="#cb5-132"></a><span class="dt">int</span></span>
<span id="cb5-133"><a href="#cb5-133"></a></span> <span id="cb5-133"><a href="#cb5-133"></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="cb5-134"><a href="#cb5-134"></a> app <span class="op">=</span> gtk_application_new <span class="op">(</span><span class="st">&quot;com.github.ToshioCP.tfe1&quot;</span><span class="op">,</span> G_APPLICATION_HANDLES_OPEN<span class="op">);</span></span> <span id="cb5-134"><a href="#cb5-134"></a> GtkApplication <span class="op">*</span>app<span class="op">;</span></span>
<span id="cb5-135"><a href="#cb5-135"></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="cb5-135"><a href="#cb5-135"></a> <span class="dt">int</span> stat<span class="op">;</span></span>
<span id="cb5-136"><a href="#cb5-136"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">&quot;open&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>app_open<span class="op">),</span> NULL<span class="op">);</span></span> <span id="cb5-136"><a href="#cb5-136"></a></span>
<span id="cb5-137"><a href="#cb5-137"></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="cb5-137"><a href="#cb5-137"></a> app <span class="op">=</span> gtk_application_new <span class="op">(</span><span class="st">&quot;com.github.ToshioCP.tfe1&quot;</span><span class="op">,</span> G_APPLICATION_HANDLES_OPEN<span class="op">);</span></span>
<span id="cb5-138"><a href="#cb5-138"></a> g_object_unref <span class="op">(</span>app<span class="op">);</span></span> <span id="cb5-138"><a href="#cb5-138"></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="cb5-139"><a href="#cb5-139"></a> <span class="cf">return</span> stat<span class="op">;</span></span> <span id="cb5-139"><a href="#cb5-139"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">&quot;open&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>app_open<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb5-140"><a href="#cb5-140"></a><span class="op">}</span></span></code></pre></div> <span id="cb5-140"><a href="#cb5-140"></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="cb5-141"><a href="#cb5-141"></a> g_object_unref <span class="op">(</span>app<span class="op">);</span></span>
<span id="cb5-142"><a href="#cb5-142"></a> <span class="cf">return</span> stat<span class="op">;</span></span>
<span id="cb5-143"><a href="#cb5-143"></a><span class="op">}</span></span></code></pre></div>
<ul> <ul>
<li>107: Sets the pointer to GFile into TfeTextView. <li>110: The GFile pointer of the TfeTextView is set with
<code>files[i]</code> is a pointer to GFile structure. It will be freed <code>files[i]</code>, which is a GFile created with the command line
by the system. So you need to copy it. <code>g_file_dup</code> argument. But the GFile will be destroyed by the system later. So it
duplicates the given GFile structure.</li> needs to be copied before the assignment. <code>g_file_dup</code>
<li>123: Connects “close-request” signal and <code>before_close</code> duplicates the GFile.</li>
handler. The fourth argument is called user data and it is given to the <li>126: The “close-request” signal is connected to
signal handler. So, <code>nb</code> is given to <code>before_close</code> handler. The fourth argument is called “user
<code>before_close</code> as the second argument.</li> data” and it will be the second argument of the signal handler. So,
<code>nb</code> is given to <code>before_close</code> as the second
argument.</li>
</ul> </ul>
<p>Now compile and run it. Theres a sample file in the directory <p>Now its time to compile and run.</p>
<code>tfe</code>. Type <code>./a.out taketori.txt</code>. Modify the <pre><code>$ cd src/tfe
contents and close the window. Make sure that the file is modified.</p> $ comp tfe1
$ ./a.out taketori.txt`.</code></pre>
<p>Modify the contents and close the window. Make sure that the file is
modified.</p>
<p>Now we got a very simple editor. Its not smart. We need more <p>Now we got a very simple editor. Its not smart. We need more
features like open, save, saveas, change font and so on. We will add features like open, save, saveas, change font and so on. We will add
them in the next section and after.</p> them in the next section and after.</p>

View file

@ -5,7 +5,7 @@
<meta name="generator" content="pandoc" /> <meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <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"> <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> <title>GTK 4 tutorial</title>
<style> <style>
code{white-space: pre-wrap;} code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;} span.smallcaps{font-variant: small-caps;}
@ -111,194 +111,251 @@
</div> </div>
</div> </div>
</nav> </nav>
<h1 id="the-user-interface-ui-file-and-gtkbuilder">The User Interface (UI) file and GtkBuilder</h1> <h1 id="gtkbuilder-and-ui-file">GtkBuilder and UI file</h1>
<h2 id="new-open-and-save-button">New, Open and Save button</h2> <h2 id="new-open-and-save-button">New, Open and Save button</h2>
<p>In the last section we made the almost simplest editor possible. It reads files in the <code>app_open</code> function at start-up and writes them out when closing the window. It works but is not very good. It would be better if we had “New”, “Open”, “Save” and “Close” buttons. This section describes how to put those buttons into the window. Signals and handlers will be explained later.</p> <p>We made very simple editor in the previous section. It reads files at
the start and writes them out at the end of the program. It works, but
is not so good. It would be better if we had “New”, “Open”, “Save” and
“Close” buttons. This section describes how to put those buttons into
the window.</p>
<figure> <figure>
<img src="image/screenshot_tfe2.png" alt="" /><figcaption>Screenshot of the file editor</figcaption> <img src="image/screenshot_tfe2.png"
alt="Screenshot of the file editor" />
<figcaption aria-hidden="true">Screenshot of the file
editor</figcaption>
</figure> </figure>
<p>The screenshot above shows the layout. The function <code>app_open</code> in the source code <code>tfe2.c</code> is as follows.</p> <p>The screenshot above shows the layout. The function
<div class="sourceCode" id="cb1"><pre class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1"></a><span class="dt">static</span> <span class="dt">void</span></span> <code>app_open</code> in the source code <code>tfe2.c</code> is as
<span id="cb1-2"><a href="#cb1-2"></a>app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer user_data) {</span> follows.</p>
<span id="cb1-3"><a href="#cb1-3"></a> GtkWidget *win;</span> <div class="sourceCode" id="cb1"><pre
<span id="cb1-4"><a href="#cb1-4"></a> GtkWidget *nb;</span> class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb1-5"><a href="#cb1-5"></a> GtkWidget *lab;</span> <span id="cb1-2"><a href="#cb1-2"></a>app_open <span class="op">(</span>GApplication <span class="op">*</span>app<span class="op">,</span> GFile <span class="op">**</span> files<span class="op">,</span> gint n_files<span class="op">,</span> gchar <span class="op">*</span>hint<span class="op">)</span> <span class="op">{</span></span>
<span id="cb1-6"><a href="#cb1-6"></a> GtkNotebookPage *nbp;</span> <span id="cb1-3"><a href="#cb1-3"></a> GtkWidget <span class="op">*</span>win<span class="op">;</span></span>
<span id="cb1-7"><a href="#cb1-7"></a> GtkWidget *scr;</span> <span id="cb1-4"><a href="#cb1-4"></a> GtkWidget <span class="op">*</span>nb<span class="op">;</span></span>
<span id="cb1-8"><a href="#cb1-8"></a> GtkWidget *tv;</span> <span id="cb1-5"><a href="#cb1-5"></a> GtkWidget <span class="op">*</span>lab<span class="op">;</span></span>
<span id="cb1-9"><a href="#cb1-9"></a> GtkTextBuffer *tb;</span> <span id="cb1-6"><a href="#cb1-6"></a> GtkNotebookPage <span class="op">*</span>nbp<span class="op">;</span></span>
<span id="cb1-10"><a href="#cb1-10"></a> <span class="dt">char</span> *contents;</span> <span id="cb1-7"><a href="#cb1-7"></a> GtkWidget <span class="op">*</span>scr<span class="op">;</span></span>
<span id="cb1-11"><a href="#cb1-11"></a> gsize length;</span> <span id="cb1-8"><a href="#cb1-8"></a> GtkWidget <span class="op">*</span>tv<span class="op">;</span></span>
<span id="cb1-12"><a href="#cb1-12"></a> <span class="dt">char</span> *filename;</span> <span id="cb1-9"><a href="#cb1-9"></a> GtkTextBuffer <span class="op">*</span>tb<span class="op">;</span></span>
<span id="cb1-13"><a href="#cb1-13"></a> <span class="dt">int</span> i;</span> <span id="cb1-10"><a href="#cb1-10"></a> <span class="dt">char</span> <span class="op">*</span>contents<span class="op">;</span></span>
<span id="cb1-11"><a href="#cb1-11"></a> gsize length<span class="op">;</span></span>
<span id="cb1-12"><a href="#cb1-12"></a> <span class="dt">char</span> <span class="op">*</span>filename<span class="op">;</span></span>
<span id="cb1-13"><a href="#cb1-13"></a> <span class="dt">int</span> i<span class="op">;</span></span>
<span id="cb1-14"><a href="#cb1-14"></a></span> <span id="cb1-14"><a href="#cb1-14"></a></span>
<span id="cb1-15"><a href="#cb1-15"></a> GtkWidget *boxv;</span> <span id="cb1-15"><a href="#cb1-15"></a> GtkWidget <span class="op">*</span>boxv<span class="op">;</span></span>
<span id="cb1-16"><a href="#cb1-16"></a> GtkWidget *boxh;</span> <span id="cb1-16"><a href="#cb1-16"></a> GtkWidget <span class="op">*</span>boxh<span class="op">;</span></span>
<span id="cb1-17"><a href="#cb1-17"></a> GtkWidget *dmy1;</span> <span id="cb1-17"><a href="#cb1-17"></a> GtkWidget <span class="op">*</span>dmy1<span class="op">;</span></span>
<span id="cb1-18"><a href="#cb1-18"></a> GtkWidget *dmy2;</span> <span id="cb1-18"><a href="#cb1-18"></a> GtkWidget <span class="op">*</span>dmy2<span class="op">;</span></span>
<span id="cb1-19"><a href="#cb1-19"></a> GtkWidget *dmy3;</span> <span id="cb1-19"><a href="#cb1-19"></a> GtkWidget <span class="op">*</span>dmy3<span class="op">;</span></span>
<span id="cb1-20"><a href="#cb1-20"></a> GtkWidget *btnn; <span class="co">/* button for new */</span></span> <span id="cb1-20"><a href="#cb1-20"></a> GtkWidget <span class="op">*</span>btnn<span class="op">;</span> <span class="co">/* button for new */</span></span>
<span id="cb1-21"><a href="#cb1-21"></a> GtkWidget *btno; <span class="co">/* button for open */</span></span> <span id="cb1-21"><a href="#cb1-21"></a> GtkWidget <span class="op">*</span>btno<span class="op">;</span> <span class="co">/* button for open */</span></span>
<span id="cb1-22"><a href="#cb1-22"></a> GtkWidget *btns; <span class="co">/* button for save */</span></span> <span id="cb1-22"><a href="#cb1-22"></a> GtkWidget <span class="op">*</span>btns<span class="op">;</span> <span class="co">/* button for save */</span></span>
<span id="cb1-23"><a href="#cb1-23"></a> GtkWidget *btnc; <span class="co">/* button for close */</span></span> <span id="cb1-23"><a href="#cb1-23"></a> GtkWidget <span class="op">*</span>btnc<span class="op">;</span> <span class="co">/* button for close */</span></span>
<span id="cb1-24"><a href="#cb1-24"></a></span> <span id="cb1-24"><a href="#cb1-24"></a></span>
<span id="cb1-25"><a href="#cb1-25"></a> win = gtk_application_window_new (GTK_APPLICATION (app));</span> <span id="cb1-25"><a href="#cb1-25"></a> win <span class="op">=</span> gtk_application_window_new <span class="op">(</span>GTK_APPLICATION <span class="op">(</span>app<span class="op">));</span></span>
<span id="cb1-26"><a href="#cb1-26"></a> gtk_window_set_title (GTK_WINDOW (win), <span class="st">&quot;file editor&quot;</span>);</span> <span id="cb1-26"><a href="#cb1-26"></a> gtk_window_set_title <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="st">&quot;file editor&quot;</span><span class="op">);</span></span>
<span id="cb1-27"><a href="#cb1-27"></a> gtk_window_set_default_size (GTK_WINDOW (win), <span class="dv">600</span>, <span class="dv">400</span>);</span> <span id="cb1-27"><a href="#cb1-27"></a> gtk_window_set_default_size <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="dv">600</span><span class="op">,</span> <span class="dv">400</span><span class="op">);</span></span>
<span id="cb1-28"><a href="#cb1-28"></a></span> <span id="cb1-28"><a href="#cb1-28"></a></span>
<span id="cb1-29"><a href="#cb1-29"></a> boxv = gtk_box_new (GTK_ORIENTATION_VERTICAL, <span class="dv">0</span>);</span> <span id="cb1-29"><a href="#cb1-29"></a> boxv <span class="op">=</span> gtk_box_new <span class="op">(</span>GTK_ORIENTATION_VERTICAL<span class="op">,</span> <span class="dv">0</span><span class="op">);</span></span>
<span id="cb1-30"><a href="#cb1-30"></a> gtk_window_set_child (GTK_WINDOW (win), boxv);</span> <span id="cb1-30"><a href="#cb1-30"></a> gtk_window_set_child <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> boxv<span class="op">);</span></span>
<span id="cb1-31"><a href="#cb1-31"></a></span> <span id="cb1-31"><a href="#cb1-31"></a></span>
<span id="cb1-32"><a href="#cb1-32"></a> boxh = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, <span class="dv">0</span>);</span> <span id="cb1-32"><a href="#cb1-32"></a> boxh <span class="op">=</span> gtk_box_new <span class="op">(</span>GTK_ORIENTATION_HORIZONTAL<span class="op">,</span> <span class="dv">0</span><span class="op">);</span></span>
<span id="cb1-33"><a href="#cb1-33"></a> gtk_box_append (GTK_BOX (boxv), boxh);</span> <span id="cb1-33"><a href="#cb1-33"></a> gtk_box_append <span class="op">(</span>GTK_BOX <span class="op">(</span>boxv<span class="op">),</span> boxh<span class="op">);</span></span>
<span id="cb1-34"><a href="#cb1-34"></a></span> <span id="cb1-34"><a href="#cb1-34"></a></span>
<span id="cb1-35"><a href="#cb1-35"></a> dmy1 = gtk_label_new(NULL); <span class="co">/* dummy label for left space */</span></span> <span id="cb1-35"><a href="#cb1-35"></a> dmy1 <span class="op">=</span> gtk_label_new<span class="op">(</span>NULL<span class="op">);</span> <span class="co">/* dummy label for left space */</span></span>
<span id="cb1-36"><a href="#cb1-36"></a> gtk_label_set_width_chars (GTK_LABEL (dmy1), <span class="dv">10</span>);</span> <span id="cb1-36"><a href="#cb1-36"></a> gtk_label_set_width_chars <span class="op">(</span>GTK_LABEL <span class="op">(</span>dmy1<span class="op">),</span> <span class="dv">10</span><span class="op">);</span></span>
<span id="cb1-37"><a href="#cb1-37"></a> dmy2 = gtk_label_new(NULL); <span class="co">/* dummy label for center space */</span></span> <span id="cb1-37"><a href="#cb1-37"></a> dmy2 <span class="op">=</span> gtk_label_new<span class="op">(</span>NULL<span class="op">);</span> <span class="co">/* dummy label for center space */</span></span>
<span id="cb1-38"><a href="#cb1-38"></a> gtk_widget_set_hexpand (dmy2, TRUE);</span> <span id="cb1-38"><a href="#cb1-38"></a> gtk_widget_set_hexpand <span class="op">(</span>dmy2<span class="op">,</span> TRUE<span class="op">);</span></span>
<span id="cb1-39"><a href="#cb1-39"></a> dmy3 = gtk_label_new(NULL); <span class="co">/* dummy label for right space */</span></span> <span id="cb1-39"><a href="#cb1-39"></a> dmy3 <span class="op">=</span> gtk_label_new<span class="op">(</span>NULL<span class="op">);</span> <span class="co">/* dummy label for right space */</span></span>
<span id="cb1-40"><a href="#cb1-40"></a> gtk_label_set_width_chars (GTK_LABEL (dmy3), <span class="dv">10</span>);</span> <span id="cb1-40"><a href="#cb1-40"></a> gtk_label_set_width_chars <span class="op">(</span>GTK_LABEL <span class="op">(</span>dmy3<span class="op">),</span> <span class="dv">10</span><span class="op">);</span></span>
<span id="cb1-41"><a href="#cb1-41"></a> btnn = gtk_button_new_with_label (<span class="st">&quot;New&quot;</span>);</span> <span id="cb1-41"><a href="#cb1-41"></a> btnn <span class="op">=</span> gtk_button_new_with_label <span class="op">(</span><span class="st">&quot;New&quot;</span><span class="op">);</span></span>
<span id="cb1-42"><a href="#cb1-42"></a> btno = gtk_button_new_with_label (<span class="st">&quot;Open&quot;</span>);</span> <span id="cb1-42"><a href="#cb1-42"></a> btno <span class="op">=</span> gtk_button_new_with_label <span class="op">(</span><span class="st">&quot;Open&quot;</span><span class="op">);</span></span>
<span id="cb1-43"><a href="#cb1-43"></a> btns = gtk_button_new_with_label (<span class="st">&quot;Save&quot;</span>);</span> <span id="cb1-43"><a href="#cb1-43"></a> btns <span class="op">=</span> gtk_button_new_with_label <span class="op">(</span><span class="st">&quot;Save&quot;</span><span class="op">);</span></span>
<span id="cb1-44"><a href="#cb1-44"></a> btnc = gtk_button_new_with_label (<span class="st">&quot;Close&quot;</span>);</span> <span id="cb1-44"><a href="#cb1-44"></a> btnc <span class="op">=</span> gtk_button_new_with_label <span class="op">(</span><span class="st">&quot;Close&quot;</span><span class="op">);</span></span>
<span id="cb1-45"><a href="#cb1-45"></a></span> <span id="cb1-45"><a href="#cb1-45"></a></span>
<span id="cb1-46"><a href="#cb1-46"></a> gtk_box_append (GTK_BOX (boxh), dmy1);</span> <span id="cb1-46"><a href="#cb1-46"></a> gtk_box_append <span class="op">(</span>GTK_BOX <span class="op">(</span>boxh<span class="op">),</span> dmy1<span class="op">);</span></span>
<span id="cb1-47"><a href="#cb1-47"></a> gtk_box_append (GTK_BOX (boxh), btnn);</span> <span id="cb1-47"><a href="#cb1-47"></a> gtk_box_append <span class="op">(</span>GTK_BOX <span class="op">(</span>boxh<span class="op">),</span> btnn<span class="op">);</span></span>
<span id="cb1-48"><a href="#cb1-48"></a> gtk_box_append (GTK_BOX (boxh), btno);</span> <span id="cb1-48"><a href="#cb1-48"></a> gtk_box_append <span class="op">(</span>GTK_BOX <span class="op">(</span>boxh<span class="op">),</span> btno<span class="op">);</span></span>
<span id="cb1-49"><a href="#cb1-49"></a> gtk_box_append (GTK_BOX (boxh), dmy2);</span> <span id="cb1-49"><a href="#cb1-49"></a> gtk_box_append <span class="op">(</span>GTK_BOX <span class="op">(</span>boxh<span class="op">),</span> dmy2<span class="op">);</span></span>
<span id="cb1-50"><a href="#cb1-50"></a> gtk_box_append (GTK_BOX (boxh), btns);</span> <span id="cb1-50"><a href="#cb1-50"></a> gtk_box_append <span class="op">(</span>GTK_BOX <span class="op">(</span>boxh<span class="op">),</span> btns<span class="op">);</span></span>
<span id="cb1-51"><a href="#cb1-51"></a> gtk_box_append (GTK_BOX (boxh), btnc);</span> <span id="cb1-51"><a href="#cb1-51"></a> gtk_box_append <span class="op">(</span>GTK_BOX <span class="op">(</span>boxh<span class="op">),</span> btnc<span class="op">);</span></span>
<span id="cb1-52"><a href="#cb1-52"></a> gtk_box_append (GTK_BOX (boxh), dmy3);</span> <span id="cb1-52"><a href="#cb1-52"></a> gtk_box_append <span class="op">(</span>GTK_BOX <span class="op">(</span>boxh<span class="op">),</span> dmy3<span class="op">);</span></span>
<span id="cb1-53"><a href="#cb1-53"></a></span> <span id="cb1-53"><a href="#cb1-53"></a></span>
<span id="cb1-54"><a href="#cb1-54"></a> nb = gtk_notebook_new ();</span> <span id="cb1-54"><a href="#cb1-54"></a> nb <span class="op">=</span> gtk_notebook_new <span class="op">();</span></span>
<span id="cb1-55"><a href="#cb1-55"></a> gtk_widget_set_hexpand (nb, TRUE);</span> <span id="cb1-55"><a href="#cb1-55"></a> gtk_widget_set_hexpand <span class="op">(</span>nb<span class="op">,</span> TRUE<span class="op">);</span></span>
<span id="cb1-56"><a href="#cb1-56"></a> gtk_widget_set_vexpand (nb, TRUE);</span> <span id="cb1-56"><a href="#cb1-56"></a> gtk_widget_set_vexpand <span class="op">(</span>nb<span class="op">,</span> TRUE<span class="op">);</span></span>
<span id="cb1-57"><a href="#cb1-57"></a> gtk_box_append (GTK_BOX (boxv), nb);</span> <span id="cb1-57"><a href="#cb1-57"></a> gtk_box_append <span class="op">(</span>GTK_BOX <span class="op">(</span>boxv<span class="op">),</span> nb<span class="op">);</span></span>
<span id="cb1-58"><a href="#cb1-58"></a></span> <span id="cb1-58"><a href="#cb1-58"></a></span>
<span id="cb1-59"><a href="#cb1-59"></a> <span class="cf">for</span> (i = <span class="dv">0</span>; i &lt; n_files; i++) {</span> <span id="cb1-59"><a href="#cb1-59"></a> <span class="cf">for</span> <span class="op">(</span>i <span class="op">=</span> <span class="dv">0</span><span class="op">;</span> i <span class="op">&lt;</span> n_files<span class="op">;</span> i<span class="op">++)</span> <span class="op">{</span></span>
<span id="cb1-60"><a href="#cb1-60"></a> <span class="cf">if</span> (g_file_load_contents (files[i], NULL, &amp;contents, &amp;length, NULL, NULL)) {</span> <span id="cb1-60"><a href="#cb1-60"></a> <span class="cf">if</span> <span class="op">(</span>g_file_load_contents <span class="op">(</span>files<span class="op">[</span>i<span class="op">],</span> NULL<span class="op">,</span> <span class="op">&amp;</span>contents<span class="op">,</span> <span class="op">&amp;</span>length<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">))</span> <span class="op">{</span></span>
<span id="cb1-61"><a href="#cb1-61"></a> scr = gtk_scrolled_window_new ();</span> <span id="cb1-61"><a href="#cb1-61"></a> scr <span class="op">=</span> gtk_scrolled_window_new <span class="op">();</span></span>
<span id="cb1-62"><a href="#cb1-62"></a> tv = tfe_text_view_new ();</span> <span id="cb1-62"><a href="#cb1-62"></a> tv <span class="op">=</span> tfe_text_view_new <span class="op">();</span></span>
<span id="cb1-63"><a href="#cb1-63"></a> tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));</span> <span id="cb1-63"><a href="#cb1-63"></a> tb <span class="op">=</span> gtk_text_view_get_buffer <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
<span id="cb1-64"><a href="#cb1-64"></a> gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR);</span> <span id="cb1-64"><a href="#cb1-64"></a> gtk_text_view_set_wrap_mode <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> GTK_WRAP_WORD_CHAR<span class="op">);</span></span>
<span id="cb1-65"><a href="#cb1-65"></a> gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);</span> <span id="cb1-65"><a href="#cb1-65"></a> gtk_scrolled_window_set_child <span class="op">(</span>GTK_SCROLLED_WINDOW <span class="op">(</span>scr<span class="op">),</span> tv<span class="op">);</span></span>
<span id="cb1-66"><a href="#cb1-66"></a></span> <span id="cb1-66"><a href="#cb1-66"></a></span>
<span id="cb1-67"><a href="#cb1-67"></a> tfe_text_view_set_file (TFE_TEXT_VIEW (tv), g_file_dup (files[i]));</span> <span id="cb1-67"><a href="#cb1-67"></a> tfe_text_view_set_file <span class="op">(</span>TFE_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> g_file_dup <span class="op">(</span>files<span class="op">[</span>i<span class="op">]));</span></span>
<span id="cb1-68"><a href="#cb1-68"></a> gtk_text_buffer_set_text (tb, contents, length);</span> <span id="cb1-68"><a href="#cb1-68"></a> gtk_text_buffer_set_text <span class="op">(</span>tb<span class="op">,</span> contents<span class="op">,</span> length<span class="op">);</span></span>
<span id="cb1-69"><a href="#cb1-69"></a> g_free (contents);</span> <span id="cb1-69"><a href="#cb1-69"></a> g_free <span class="op">(</span>contents<span class="op">);</span></span>
<span id="cb1-70"><a href="#cb1-70"></a> filename = g_file_get_basename (files[i]);</span> <span id="cb1-70"><a href="#cb1-70"></a> filename <span class="op">=</span> g_file_get_basename <span class="op">(</span>files<span class="op">[</span>i<span class="op">]);</span></span>
<span id="cb1-71"><a href="#cb1-71"></a> lab = gtk_label_new (filename);</span> <span id="cb1-71"><a href="#cb1-71"></a> lab <span class="op">=</span> gtk_label_new <span class="op">(</span>filename<span class="op">);</span></span>
<span id="cb1-72"><a href="#cb1-72"></a> gtk_notebook_append_page (GTK_NOTEBOOK (nb), scr, lab);</span> <span id="cb1-72"><a href="#cb1-72"></a> gtk_notebook_append_page <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">),</span> scr<span class="op">,</span> lab<span class="op">);</span></span>
<span id="cb1-73"><a href="#cb1-73"></a> nbp = gtk_notebook_get_page (GTK_NOTEBOOK (nb), scr);</span> <span id="cb1-73"><a href="#cb1-73"></a> nbp <span class="op">=</span> gtk_notebook_get_page <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">),</span> scr<span class="op">);</span></span>
<span id="cb1-74"><a href="#cb1-74"></a> g_object_set (nbp, <span class="st">&quot;tab-expand&quot;</span>, TRUE, NULL);</span> <span id="cb1-74"><a href="#cb1-74"></a> g_object_set <span class="op">(</span>nbp<span class="op">,</span> <span class="st">&quot;tab-expand&quot;</span><span class="op">,</span> TRUE<span class="op">,</span> NULL<span class="op">);</span></span>
<span id="cb1-75"><a href="#cb1-75"></a> g_free (filename);</span> <span id="cb1-75"><a href="#cb1-75"></a> g_free <span class="op">(</span>filename<span class="op">);</span></span>
<span id="cb1-76"><a href="#cb1-76"></a> } <span class="cf">else</span> <span class="cf">if</span> ((filename = g_file_get_path (files[i])) != NULL) {</span> <span id="cb1-76"><a href="#cb1-76"></a> <span class="op">}</span> <span class="cf">else</span> <span class="cf">if</span> <span class="op">((</span>filename <span class="op">=</span> g_file_get_path <span class="op">(</span>files<span class="op">[</span>i<span class="op">]))</span> <span class="op">!=</span> NULL<span class="op">)</span> <span class="op">{</span></span>
<span id="cb1-77"><a href="#cb1-77"></a> g_print (<span class="st">&quot;No such file: %s.</span><span class="sc">\n</span><span class="st">&quot;</span>, filename);</span> <span id="cb1-77"><a href="#cb1-77"></a> g_print <span class="op">(</span><span class="st">&quot;No such file: %s.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">,</span> filename<span class="op">);</span></span>
<span id="cb1-78"><a href="#cb1-78"></a> g_free (filename);</span> <span id="cb1-78"><a href="#cb1-78"></a> g_free <span class="op">(</span>filename<span class="op">);</span></span>
<span id="cb1-79"><a href="#cb1-79"></a> } <span class="cf">else</span></span> <span id="cb1-79"><a href="#cb1-79"></a> <span class="op">}</span> <span class="cf">else</span></span>
<span id="cb1-80"><a href="#cb1-80"></a> g_print (<span class="st">&quot;No valid file is given</span><span class="sc">\n</span><span class="st">&quot;</span>);</span> <span id="cb1-80"><a href="#cb1-80"></a> g_print <span class="op">(</span><span class="st">&quot;No valid file is given</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb1-81"><a href="#cb1-81"></a> }</span> <span id="cb1-81"><a href="#cb1-81"></a> <span class="op">}</span></span>
<span id="cb1-82"><a href="#cb1-82"></a> <span class="cf">if</span> (gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb)) &gt; <span class="dv">0</span>) {</span> <span id="cb1-82"><a href="#cb1-82"></a> <span class="cf">if</span> <span class="op">(</span>gtk_notebook_get_n_pages <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">))</span> <span class="op">&gt;</span> <span class="dv">0</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb1-83"><a href="#cb1-83"></a> gtk_widget_show (win);</span> <span id="cb1-83"><a href="#cb1-83"></a> gtk_widget_show <span class="op">(</span>win<span class="op">);</span></span>
<span id="cb1-84"><a href="#cb1-84"></a> } <span class="cf">else</span></span> <span id="cb1-84"><a href="#cb1-84"></a> <span class="op">}</span> <span class="cf">else</span></span>
<span id="cb1-85"><a href="#cb1-85"></a> gtk_window_destroy (GTK_WINDOW (win));</span> <span id="cb1-85"><a href="#cb1-85"></a> gtk_window_destroy <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
<span id="cb1-86"><a href="#cb1-86"></a>}</span></code></pre></div> <span id="cb1-86"><a href="#cb1-86"></a><span class="op">}</span></span></code></pre></div>
<p>The aim is to build the widgets of the main application window.</p> <p>The function <code>app_open</code> builds the widgets in the main
application window.</p>
<ul> <ul>
<li>25-27: Creates a GtkApplicationWindow instance and sets the title and default size.</li> <li>25-27: Creates a GtkApplicationWindow instance and sets the title
<li>29-30: Creates a GtkBox instance <code>boxv</code>. It is a vertical box and a child of GtkApplicationWindow. It has two children. The first child is a horizontal box. The second child is a GtkNotebook.</li> and default size.</li>
<li>32-33: Creates a GtkBox instance <code>boxh</code> and appends it to <code>boxv</code> as a first child.</li> <li>29-30: Creates a GtkBox instance <code>boxv</code>. It is a vertical
<li>35-40: Creates three dummy labels. The labels <code>dmy1</code> and <code>dmy3</code> has a character width of ten. The other label <code>dmy2</code> has hexpand property which is set to be TRUE. This makes the label expands horizontally as long as possible.</li> box and a child of GtkApplicationWindow. It has two children. The first
child is a horizontal box. The second child is a GtkNotebook.</li>
<li>32-33: Creates a GtkBox instance <code>boxh</code> and appends it to
<code>boxv</code> as a first child.</li>
<li>35-40: Creates three dummy labels. The labels <code>dmy1</code> and
<code>dmy3</code> has a character width of ten. The other label
<code>dmy2</code> has hexpand property which is set to be TRUE. This
makes the label expands horizontally as long as possible.</li>
<li>41-44: Creates four buttons.</li> <li>41-44: Creates four buttons.</li>
<li>46-52: Appends these GtkLabel and GtkButton to <code>boxh</code>.</li> <li>46-52: Appends these GtkLabel and GtkButton to
<li>54-57: Creates a GtkNotebook instance and sets hexpand and vexpand properties TRUE. This makes it expand horizontally and vertically as big as possible. It is appended to <code>boxv</code> as the second child.</li> <code>boxh</code>.</li>
<li>54-57: Creates a GtkNotebook instance and sets hexpand and vexpand
properties to be TRUE. This makes it expand horizontally and vertically
as big as possible. It is appended to <code>boxv</code> as the second
child.</li>
</ul> </ul>
<p>The number of lines to build the widgets is 33(=57-25+1). We also needed many additional variables (<code>boxv</code>, <code>boxh</code>, <code>dmy1</code>, …), most of which werent necessary, except for building the widgets. Are there any good solution to reduce these work?</p> <p>The number of widget-build lines is 33(=57-25+1). We also needed many
<p>Gtk provides GtkBuilder. It reads user interface (UI) data and builds a window. It reduces this cumbersome work.</p> variables (<code>boxv</code>, <code>boxh</code>, <code>dmy1</code>, …)
and most of them used only for building the widgets. Are there any good
solution to reduce these work?</p>
<p>Gtk provides GtkBuilder. It reads user interface (UI) data and builds
a window. It reduces this cumbersome work.</p>
<h2 id="the-ui-file">The UI File</h2> <h2 id="the-ui-file">The UI File</h2>
<p>First, lets look at the UI file <code>tfe3.ui</code> that is used to define the widget structure.</p> <p>Look at the UI file <code>tfe3.ui</code> that defines widget
<div class="sourceCode" id="cb2"><pre class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><span id="cb2-1"><a href="#cb2-1"></a><span class="kw">&lt;?xml</span> version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;<span class="kw">?&gt;</span></span> structure.</p>
<span id="cb2-2"><a href="#cb2-2"></a><span class="kw">&lt;interface&gt;</span></span> <div class="sourceCode" id="cb2"><pre
<span id="cb2-3"><a href="#cb2-3"></a> <span class="kw">&lt;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><span class="kw">&gt;</span></span> class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><span id="cb2-1"><a href="#cb2-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="cb2-4"><a href="#cb2-4"></a> <span class="kw">&lt;property</span><span class="ot"> name=</span><span class="st">&quot;title&quot;</span><span class="kw">&gt;</span>file editor<span class="kw">&lt;/property&gt;</span></span> <span id="cb2-2"><a href="#cb2-2"></a>&lt;<span class="kw">interface</span>&gt;</span>
<span id="cb2-5"><a href="#cb2-5"></a> <span class="kw">&lt;property</span><span class="ot"> name=</span><span class="st">&quot;default-width&quot;</span><span class="kw">&gt;</span>600<span class="kw">&lt;/property&gt;</span></span> <span id="cb2-3"><a href="#cb2-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="cb2-6"><a href="#cb2-6"></a> <span class="kw">&lt;property</span><span class="ot"> name=</span><span class="st">&quot;default-height&quot;</span><span class="kw">&gt;</span>400<span class="kw">&lt;/property&gt;</span></span> <span id="cb2-4"><a href="#cb2-4"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;title&quot;</span>&gt;file editor&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb2-7"><a href="#cb2-7"></a> <span class="kw">&lt;child&gt;</span></span> <span id="cb2-5"><a href="#cb2-5"></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="cb2-8"><a href="#cb2-8"></a> <span class="kw">&lt;object</span><span class="ot"> class=</span><span class="st">&quot;GtkBox&quot;</span><span class="ot"> id=</span><span class="st">&quot;boxv&quot;</span><span class="kw">&gt;</span></span> <span id="cb2-6"><a href="#cb2-6"></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="cb2-9"><a href="#cb2-9"></a> <span class="kw">&lt;property</span><span class="ot"> name=</span><span class="st">&quot;orientation&quot;</span><span class="kw">&gt;</span>GTK_ORIENTATION_VERTICAL<span class="kw">&lt;/property&gt;</span></span> <span id="cb2-7"><a href="#cb2-7"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb2-10"><a href="#cb2-10"></a> <span class="kw">&lt;child&gt;</span></span> <span id="cb2-8"><a href="#cb2-8"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkBox&quot;</span><span class="ot"> id=</span><span class="st">&quot;boxv&quot;</span>&gt;</span>
<span id="cb2-11"><a href="#cb2-11"></a> <span class="kw">&lt;object</span><span class="ot"> class=</span><span class="st">&quot;GtkBox&quot;</span><span class="ot"> id=</span><span class="st">&quot;boxh&quot;</span><span class="kw">&gt;</span></span> <span id="cb2-9"><a href="#cb2-9"></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="cb2-12"><a href="#cb2-12"></a> <span class="kw">&lt;property</span><span class="ot"> name=</span><span class="st">&quot;orientation&quot;</span><span class="kw">&gt;</span>GTK_ORIENTATION_HORIZONTAL<span class="kw">&lt;/property&gt;</span></span> <span id="cb2-10"><a href="#cb2-10"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb2-13"><a href="#cb2-13"></a> <span class="kw">&lt;child&gt;</span></span> <span id="cb2-11"><a href="#cb2-11"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkBox&quot;</span><span class="ot"> id=</span><span class="st">&quot;boxh&quot;</span>&gt;</span>
<span id="cb2-14"><a href="#cb2-14"></a> <span class="kw">&lt;object</span><span class="ot"> class=</span><span class="st">&quot;GtkLabel&quot;</span><span class="ot"> id=</span><span class="st">&quot;dmy1&quot;</span><span class="kw">&gt;</span></span> <span id="cb2-12"><a href="#cb2-12"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;orientation&quot;</span>&gt;GTK_ORIENTATION_HORIZONTAL&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb2-15"><a href="#cb2-15"></a> <span class="kw">&lt;property</span><span class="ot"> name=</span><span class="st">&quot;width-chars&quot;</span><span class="kw">&gt;</span>10<span class="kw">&lt;/property&gt;</span></span> <span id="cb2-13"><a href="#cb2-13"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb2-16"><a href="#cb2-16"></a> <span class="kw">&lt;/object&gt;</span></span> <span id="cb2-14"><a href="#cb2-14"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkLabel&quot;</span><span class="ot"> id=</span><span class="st">&quot;dmy1&quot;</span>&gt;</span>
<span id="cb2-17"><a href="#cb2-17"></a> <span class="kw">&lt;/child&gt;</span></span> <span id="cb2-15"><a href="#cb2-15"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;width-chars&quot;</span>&gt;10&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb2-18"><a href="#cb2-18"></a> <span class="kw">&lt;child&gt;</span></span> <span id="cb2-16"><a href="#cb2-16"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb2-19"><a href="#cb2-19"></a> <span class="kw">&lt;object</span><span class="ot"> class=</span><span class="st">&quot;GtkButton&quot;</span><span class="ot"> id=</span><span class="st">&quot;btnn&quot;</span><span class="kw">&gt;</span></span> <span id="cb2-17"><a href="#cb2-17"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb2-20"><a href="#cb2-20"></a> <span class="kw">&lt;property</span><span class="ot"> name=</span><span class="st">&quot;label&quot;</span><span class="kw">&gt;</span>New<span class="kw">&lt;/property&gt;</span></span> <span id="cb2-18"><a href="#cb2-18"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb2-21"><a href="#cb2-21"></a> <span class="kw">&lt;/object&gt;</span></span> <span id="cb2-19"><a href="#cb2-19"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkButton&quot;</span><span class="ot"> id=</span><span class="st">&quot;btnn&quot;</span>&gt;</span>
<span id="cb2-22"><a href="#cb2-22"></a> <span class="kw">&lt;/child&gt;</span></span> <span id="cb2-20"><a href="#cb2-20"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;label&quot;</span>&gt;New&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb2-23"><a href="#cb2-23"></a> <span class="kw">&lt;child&gt;</span></span> <span id="cb2-21"><a href="#cb2-21"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb2-24"><a href="#cb2-24"></a> <span class="kw">&lt;object</span><span class="ot"> class=</span><span class="st">&quot;GtkButton&quot;</span><span class="ot"> id=</span><span class="st">&quot;btno&quot;</span><span class="kw">&gt;</span></span> <span id="cb2-22"><a href="#cb2-22"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb2-25"><a href="#cb2-25"></a> <span class="kw">&lt;property</span><span class="ot"> name=</span><span class="st">&quot;label&quot;</span><span class="kw">&gt;</span>Open<span class="kw">&lt;/property&gt;</span></span> <span id="cb2-23"><a href="#cb2-23"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb2-26"><a href="#cb2-26"></a> <span class="kw">&lt;/object&gt;</span></span> <span id="cb2-24"><a href="#cb2-24"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkButton&quot;</span><span class="ot"> id=</span><span class="st">&quot;btno&quot;</span>&gt;</span>
<span id="cb2-27"><a href="#cb2-27"></a> <span class="kw">&lt;/child&gt;</span></span> <span id="cb2-25"><a href="#cb2-25"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;label&quot;</span>&gt;Open&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb2-28"><a href="#cb2-28"></a> <span class="kw">&lt;child&gt;</span></span> <span id="cb2-26"><a href="#cb2-26"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb2-29"><a href="#cb2-29"></a> <span class="kw">&lt;object</span><span class="ot"> class=</span><span class="st">&quot;GtkLabel&quot;</span><span class="ot"> id=</span><span class="st">&quot;dmy2&quot;</span><span class="kw">&gt;</span></span> <span id="cb2-27"><a href="#cb2-27"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb2-30"><a href="#cb2-30"></a> <span class="kw">&lt;property</span><span class="ot"> name=</span><span class="st">&quot;hexpand&quot;</span><span class="kw">&gt;</span>TRUE<span class="kw">&lt;/property&gt;</span></span> <span id="cb2-28"><a href="#cb2-28"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb2-31"><a href="#cb2-31"></a> <span class="kw">&lt;/object&gt;</span></span> <span id="cb2-29"><a href="#cb2-29"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkLabel&quot;</span><span class="ot"> id=</span><span class="st">&quot;dmy2&quot;</span>&gt;</span>
<span id="cb2-32"><a href="#cb2-32"></a> <span class="kw">&lt;/child&gt;</span></span> <span id="cb2-30"><a href="#cb2-30"></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="cb2-33"><a href="#cb2-33"></a> <span class="kw">&lt;child&gt;</span></span> <span id="cb2-31"><a href="#cb2-31"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb2-34"><a href="#cb2-34"></a> <span class="kw">&lt;object</span><span class="ot"> class=</span><span class="st">&quot;GtkButton&quot;</span><span class="ot"> id=</span><span class="st">&quot;btns&quot;</span><span class="kw">&gt;</span></span> <span id="cb2-32"><a href="#cb2-32"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb2-35"><a href="#cb2-35"></a> <span class="kw">&lt;property</span><span class="ot"> name=</span><span class="st">&quot;label&quot;</span><span class="kw">&gt;</span>Save<span class="kw">&lt;/property&gt;</span></span> <span id="cb2-33"><a href="#cb2-33"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb2-36"><a href="#cb2-36"></a> <span class="kw">&lt;/object&gt;</span></span> <span id="cb2-34"><a href="#cb2-34"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkButton&quot;</span><span class="ot"> id=</span><span class="st">&quot;btns&quot;</span>&gt;</span>
<span id="cb2-37"><a href="#cb2-37"></a> <span class="kw">&lt;/child&gt;</span></span> <span id="cb2-35"><a href="#cb2-35"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;label&quot;</span>&gt;Save&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb2-38"><a href="#cb2-38"></a> <span class="kw">&lt;child&gt;</span></span> <span id="cb2-36"><a href="#cb2-36"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb2-39"><a href="#cb2-39"></a> <span class="kw">&lt;object</span><span class="ot"> class=</span><span class="st">&quot;GtkButton&quot;</span><span class="ot"> id=</span><span class="st">&quot;btnc&quot;</span><span class="kw">&gt;</span></span> <span id="cb2-37"><a href="#cb2-37"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb2-40"><a href="#cb2-40"></a> <span class="kw">&lt;property</span><span class="ot"> name=</span><span class="st">&quot;label&quot;</span><span class="kw">&gt;</span>Close<span class="kw">&lt;/property&gt;</span></span> <span id="cb2-38"><a href="#cb2-38"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb2-41"><a href="#cb2-41"></a> <span class="kw">&lt;/object&gt;</span></span> <span id="cb2-39"><a href="#cb2-39"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkButton&quot;</span><span class="ot"> id=</span><span class="st">&quot;btnc&quot;</span>&gt;</span>
<span id="cb2-42"><a href="#cb2-42"></a> <span class="kw">&lt;/child&gt;</span></span> <span id="cb2-40"><a href="#cb2-40"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;label&quot;</span>&gt;Close&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb2-43"><a href="#cb2-43"></a> <span class="kw">&lt;child&gt;</span></span> <span id="cb2-41"><a href="#cb2-41"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb2-44"><a href="#cb2-44"></a> <span class="kw">&lt;object</span><span class="ot"> class=</span><span class="st">&quot;GtkLabel&quot;</span><span class="ot"> id=</span><span class="st">&quot;dmy3&quot;</span><span class="kw">&gt;</span></span> <span id="cb2-42"><a href="#cb2-42"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb2-45"><a href="#cb2-45"></a> <span class="kw">&lt;property</span><span class="ot"> name=</span><span class="st">&quot;width-chars&quot;</span><span class="kw">&gt;</span>10<span class="kw">&lt;/property&gt;</span></span> <span id="cb2-43"><a href="#cb2-43"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb2-46"><a href="#cb2-46"></a> <span class="kw">&lt;/object&gt;</span></span> <span id="cb2-44"><a href="#cb2-44"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkLabel&quot;</span><span class="ot"> id=</span><span class="st">&quot;dmy3&quot;</span>&gt;</span>
<span id="cb2-47"><a href="#cb2-47"></a> <span class="kw">&lt;/child&gt;</span></span> <span id="cb2-45"><a href="#cb2-45"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;width-chars&quot;</span>&gt;10&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb2-48"><a href="#cb2-48"></a> <span class="kw">&lt;/object&gt;</span></span> <span id="cb2-46"><a href="#cb2-46"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb2-49"><a href="#cb2-49"></a> <span class="kw">&lt;/child&gt;</span></span> <span id="cb2-47"><a href="#cb2-47"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb2-50"><a href="#cb2-50"></a> <span class="kw">&lt;child&gt;</span></span> <span id="cb2-48"><a href="#cb2-48"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb2-51"><a href="#cb2-51"></a> <span class="kw">&lt;object</span><span class="ot"> class=</span><span class="st">&quot;GtkNotebook&quot;</span><span class="ot"> id=</span><span class="st">&quot;nb&quot;</span><span class="kw">&gt;</span></span> <span id="cb2-49"><a href="#cb2-49"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb2-52"><a href="#cb2-52"></a> <span class="kw">&lt;property</span><span class="ot"> name=</span><span class="st">&quot;hexpand&quot;</span><span class="kw">&gt;</span>TRUE<span class="kw">&lt;/property&gt;</span></span> <span id="cb2-50"><a href="#cb2-50"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb2-53"><a href="#cb2-53"></a> <span class="kw">&lt;property</span><span class="ot"> name=</span><span class="st">&quot;vexpand&quot;</span><span class="kw">&gt;</span>TRUE<span class="kw">&lt;/property&gt;</span></span> <span id="cb2-51"><a href="#cb2-51"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkNotebook&quot;</span><span class="ot"> id=</span><span class="st">&quot;nb&quot;</span>&gt;</span>
<span id="cb2-54"><a href="#cb2-54"></a> <span class="kw">&lt;/object&gt;</span></span> <span id="cb2-52"><a href="#cb2-52"></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="cb2-55"><a href="#cb2-55"></a> <span class="kw">&lt;/child&gt;</span></span> <span id="cb2-53"><a href="#cb2-53"></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="cb2-56"><a href="#cb2-56"></a> <span class="kw">&lt;/object&gt;</span></span> <span id="cb2-54"><a href="#cb2-54"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb2-57"><a href="#cb2-57"></a> <span class="kw">&lt;/child&gt;</span></span> <span id="cb2-55"><a href="#cb2-55"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb2-58"><a href="#cb2-58"></a> <span class="kw">&lt;/object&gt;</span></span> <span id="cb2-56"><a href="#cb2-56"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb2-59"><a href="#cb2-59"></a><span class="kw">&lt;/interface&gt;</span></span></code></pre></div> <span id="cb2-57"><a href="#cb2-57"></a> &lt;/<span class="kw">child</span>&gt;</span>
<p>The structure of this file is XML. Constructs that begin with <code>&lt;</code> and end with <code>&gt;</code> are called tags. There are two types of tags, the start tag and the end tag. For example, <code>&lt;interface&gt;</code> is a start tag and <code>&lt;/interface&gt;</code> is an end tag. The UI file begins and ends with interface tags. Some tags, for example object tags, can have a class and id attributes in their start tag.</p> <span id="cb2-58"><a href="#cb2-58"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb2-59"><a href="#cb2-59"></a>&lt;/<span class="kw">interface</span>&gt;</span></code></pre></div>
<p>The is a XML file. Tags begin with <code>&lt;</code> and end with
<code>&gt;</code>. There are two types of tags, the start tag and the
end tag. For example, <code>&lt;interface&gt;</code> is a start tag and
<code>&lt;/interface&gt;</code> is an end tag. The UI file begins and
ends with interface tags. Some tags, for example object tags, can have a
class and id attributes in their start tag.</p>
<ul> <ul>
<li>1: The first line is XML declaration. It specifies that the version of XML is 1.0 and the encoding is UTF-8. Even if the line is left out, GtkBuilder builds objects from the ui file. But ui files must use UTF-8 encoding, or GtkBuilder cant recognize it and a fatal error occurs.</li> <li>1: XML declaration. It specifies that the XML version is 1.0 and the
<li>3-6: An object with <code>GtkApplicationWindow</code> class and <code>win</code> id is defined. This is the top level window. And the three properties of the window are defined. <code>title</code> property is “file editor”, <code>default-width</code> property is 600 and <code>default-height</code> property is 400.</li> encoding is UTF-8.</li>
<li>7: child tag means a child of the widget above. For example, line 7 tells us that GtkBox object which id is “boxv” is a child widget of <code>win</code>.</li> <li>3-6: An object tag with <code>GtkApplicationWindow</code> class and
<code>win</code> id. This is the top level window. And the three
properties of the window are defined. The <code>title</code> property is
“file editor”, <code>default-width</code> property is 600 and
<code>default-height</code> property is 400.</li>
<li>7: child tag means a child widget. For example, line 7 tells us that
GtkBox object with “boxv” id attribute is a child widget of
<code>win</code>.</li>
</ul> </ul>
<p>Compare this ui file and the lines 25-57 in the source code of <code>app_open</code> function. Those two describe the same structure of widgets.</p> <p>Compare this ui file and the lines 25-57 in the <code>tfe2.c</code>
source code. Both builds the same window with its descendant
widgets.</p>
<p>You can check the ui file with <code>gtk4-builder-tool</code>.</p> <p>You can check the ui file with <code>gtk4-builder-tool</code>.</p>
<ul> <ul>
<li><code>gtk4-builder-tool validate &lt;ui file name&gt;</code> validates the ui file. If the ui file includes some syntactical error, <code>gtk4-builder-tool</code> prints the error.</li> <li><code>gtk4-builder-tool validate &lt;ui file name&gt;</code>
<li><code>gtk4-builder-tool simplify &lt;ui file name&gt;</code> simplifies the ui file and prints the result. If <code>--replace</code> option is given, it replaces the ui file with the simplified one. If the ui file specifies a value of property but it is default, then it will be removed. And some values are simplified. For example, “TRUE”and “FALSE” becomes “1” and “0” respectively. However, “TRUE” or “FALSE” is better for maintenance.</li> validates the ui file. If the ui file includes some syntactical error,
<code>gtk4-builder-tool</code> prints the error.</li>
<li><code>gtk4-builder-tool simplify &lt;ui file name&gt;</code>
simplifies the ui file and prints the result. If <code>--replace</code>
option is given, it replaces the ui file with the simplified one. If the
ui file specifies a value of property but it is default, then it will be
removed. And some values are simplified. For example, “TRUE”and “FALSE”
becomes “1” and “0” respectively. However, “TRUE” or “FALSE” is better
for maintenance.</li>
</ul> </ul>
<p>It is a good idea to check your ui file before compiling.</p> <p>It is a good idea to check your ui file before compiling.</p>
<h2 id="gtkbuilder">GtkBuilder</h2> <h2 id="gtkbuilder">GtkBuilder</h2>
<p>GtkBuilder builds widgets based on the ui file.</p> <p>GtkBuilder builds widgets based on a ui file.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true"></a>GtkBuilder *build;</span> <div class="sourceCode" id="cb3"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>GtkBuilder <span class="op">*</span>build<span class="op">;</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true"></a></span> <span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true"></a>build = gtk_builder_new_from_file (<span class="st">&quot;tfe3.ui&quot;</span>);</span> <span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>build <span class="op">=</span> gtk_builder_new_from_file <span class="op">(</span><span class="st">&quot;tfe3.ui&quot;</span><span class="op">);</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true"></a>win = GTK_WIDGET (gtk_builder_get_object (build, <span class="st">&quot;win&quot;</span>));</span> <span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></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="cb3-5"><a href="#cb3-5" aria-hidden="true"></a>gtk_window_set_application (GTK_WINDOW (win), GTK_APPLICATION (app));</span> <span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>gtk_window_set_application <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> GTK_APPLICATION <span class="op">(</span>app<span class="op">));</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true"></a>nb = GTK_WIDGET (gtk_builder_get_object (build, <span class="st">&quot;nb&quot;</span>));</span></code></pre></div> <span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>nb <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;nb&quot;</span><span class="op">));</span></span></code></pre></div>
<p>The function <code>gtk_builder_new_from_file</code> reads the file given as an argument. Then, it builds the widgets and creates GtkBuilder object. The function <code>gtk_builder_get_object (build, "win")</code> returns the pointer to the widget <code>win</code>, which is the id in the ui file. All the widgets are connected based on the parent-children relationship described in the ui file. We only need <code>win</code> and <code>nb</code> for the program after this, so we dont need to take out any other widgets. This reduces lines in the C source file.</p> <p>The function <code>gtk_builder_new_from_file</code> reads the file
given as an argument. Then, it builds the widgets and creates GtkBuilder
object. The function <code>gtk_builder_get_object (build, "win")</code>
returns the pointer to the widget <code>win</code>, which is the id in
the ui file. All the widgets are connected based on the parent-children
relationship described in the ui file. We only need <code>win</code> and
<code>nb</code> for the program below. This reduces lines in the C
source file.</p>
<pre><code>$ cd tfe; diff tfe2.c tfe3.c <pre><code>$ cd tfe; diff tfe2.c tfe3.c
58a59 58a59
&gt; GtkBuilder *build; &gt; GtkBuilder *build;
@ -356,96 +413,124 @@
138c100 138c100
&lt; app = gtk_application_new (&quot;com.github.ToshioCP.tfe2&quot;, G_APPLICATION_HANDLES_OPEN); &lt; app = gtk_application_new (&quot;com.github.ToshioCP.tfe2&quot;, G_APPLICATION_HANDLES_OPEN);
--- ---
&gt; app = gtk_application_new (&quot;com.github.ToshioCP.tfe3&quot;, G_APPLICATION_HANDLES_OPEN);</code></pre> &gt; app = gtk_application_new (&quot;com.github.ToshioCP.tfe3&quot;, G_APPLICATION_HANDLES_OPEN);
<p><code>60,103c61,65</code> means 44 (=103-60+1) lines are changed to 5 (=65-61+1) lines. Therefore, 39 lines are reduced. Using ui file not only shortens C source files, but also makes the widgets structure clear.</p> 144a107
<p>Now Ill show you <code>app_open</code> function in the C file <code>tfe3.c</code>.</p> &gt; </code></pre>
<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="dt">static</span> <span class="dt">void</span></span> <p><code>60,103c61,65</code> means 44 (=103-60+1) lines are changed to 5
<span id="cb5-2"><a href="#cb5-2"></a>app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer user_data) {</span> (=65-61+1) lines. Therefore, 39 lines are reduced. Using ui file not
<span id="cb5-3"><a href="#cb5-3"></a> GtkWidget *win;</span> only shortens C source files, but also makes the widgets structure
<span id="cb5-4"><a href="#cb5-4"></a> GtkWidget *nb;</span> clear.</p>
<span id="cb5-5"><a href="#cb5-5"></a> GtkWidget *lab;</span> <p>Now Ill show you <code>app_open</code> function in the C file
<span id="cb5-6"><a href="#cb5-6"></a> GtkNotebookPage *nbp;</span> <code>tfe3.c</code>.</p>
<span id="cb5-7"><a href="#cb5-7"></a> GtkWidget *scr;</span> <div class="sourceCode" id="cb5"><pre
<span id="cb5-8"><a href="#cb5-8"></a> GtkWidget *tv;</span> 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-9"><a href="#cb5-9"></a> GtkTextBuffer *tb;</span> <span id="cb5-2"><a href="#cb5-2"></a>app_open <span class="op">(</span>GApplication <span class="op">*</span>app<span class="op">,</span> GFile <span class="op">**</span> files<span class="op">,</span> gint n_files<span class="op">,</span> gchar <span class="op">*</span>hint<span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-10"><a href="#cb5-10"></a> <span class="dt">char</span> *contents;</span> <span id="cb5-3"><a href="#cb5-3"></a> GtkWidget <span class="op">*</span>win<span class="op">;</span></span>
<span id="cb5-11"><a href="#cb5-11"></a> gsize length;</span> <span id="cb5-4"><a href="#cb5-4"></a> GtkWidget <span class="op">*</span>nb<span class="op">;</span></span>
<span id="cb5-12"><a href="#cb5-12"></a> <span class="dt">char</span> *filename;</span> <span id="cb5-5"><a href="#cb5-5"></a> GtkWidget <span class="op">*</span>lab<span class="op">;</span></span>
<span id="cb5-13"><a href="#cb5-13"></a> <span class="dt">int</span> i;</span> <span id="cb5-6"><a href="#cb5-6"></a> GtkNotebookPage <span class="op">*</span>nbp<span class="op">;</span></span>
<span id="cb5-14"><a href="#cb5-14"></a> GtkBuilder *build;</span> <span id="cb5-7"><a href="#cb5-7"></a> GtkWidget <span class="op">*</span>scr<span class="op">;</span></span>
<span id="cb5-8"><a href="#cb5-8"></a> GtkWidget <span class="op">*</span>tv<span class="op">;</span></span>
<span id="cb5-9"><a href="#cb5-9"></a> GtkTextBuffer <span class="op">*</span>tb<span class="op">;</span></span>
<span id="cb5-10"><a href="#cb5-10"></a> <span class="dt">char</span> <span class="op">*</span>contents<span class="op">;</span></span>
<span id="cb5-11"><a href="#cb5-11"></a> gsize length<span class="op">;</span></span>
<span id="cb5-12"><a href="#cb5-12"></a> <span class="dt">char</span> <span class="op">*</span>filename<span class="op">;</span></span>
<span id="cb5-13"><a href="#cb5-13"></a> <span class="dt">int</span> i<span class="op">;</span></span>
<span id="cb5-14"><a href="#cb5-14"></a> GtkBuilder <span class="op">*</span>build<span class="op">;</span></span>
<span id="cb5-15"><a href="#cb5-15"></a></span> <span id="cb5-15"><a href="#cb5-15"></a></span>
<span id="cb5-16"><a href="#cb5-16"></a> build = gtk_builder_new_from_file (<span class="st">&quot;tfe3.ui&quot;</span>);</span> <span id="cb5-16"><a href="#cb5-16"></a> build <span class="op">=</span> gtk_builder_new_from_file <span class="op">(</span><span class="st">&quot;tfe3.ui&quot;</span><span class="op">);</span></span>
<span id="cb5-17"><a href="#cb5-17"></a> win = GTK_WIDGET (gtk_builder_get_object (build, <span class="st">&quot;win&quot;</span>));</span> <span id="cb5-17"><a href="#cb5-17"></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="cb5-18"><a href="#cb5-18"></a> gtk_window_set_application (GTK_WINDOW (win), GTK_APPLICATION (app));</span> <span id="cb5-18"><a href="#cb5-18"></a> gtk_window_set_application <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> GTK_APPLICATION <span class="op">(</span>app<span class="op">));</span></span>
<span id="cb5-19"><a href="#cb5-19"></a> nb = GTK_WIDGET (gtk_builder_get_object (build, <span class="st">&quot;nb&quot;</span>));</span> <span id="cb5-19"><a href="#cb5-19"></a> nb <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;nb&quot;</span><span class="op">));</span></span>
<span id="cb5-20"><a href="#cb5-20"></a> g_object_unref(build);</span> <span id="cb5-20"><a href="#cb5-20"></a> g_object_unref<span class="op">(</span>build<span class="op">);</span></span>
<span id="cb5-21"><a href="#cb5-21"></a> <span class="cf">for</span> (i = <span class="dv">0</span>; i &lt; n_files; i++) {</span> <span id="cb5-21"><a href="#cb5-21"></a> <span class="cf">for</span> <span class="op">(</span>i <span class="op">=</span> <span class="dv">0</span><span class="op">;</span> i <span class="op">&lt;</span> n_files<span class="op">;</span> i<span class="op">++)</span> <span class="op">{</span></span>
<span id="cb5-22"><a href="#cb5-22"></a> <span class="cf">if</span> (g_file_load_contents (files[i], NULL, &amp;contents, &amp;length, NULL, NULL)) {</span> <span id="cb5-22"><a href="#cb5-22"></a> <span class="cf">if</span> <span class="op">(</span>g_file_load_contents <span class="op">(</span>files<span class="op">[</span>i<span class="op">],</span> NULL<span class="op">,</span> <span class="op">&amp;</span>contents<span class="op">,</span> <span class="op">&amp;</span>length<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">))</span> <span class="op">{</span></span>
<span id="cb5-23"><a href="#cb5-23"></a> scr = gtk_scrolled_window_new ();</span> <span id="cb5-23"><a href="#cb5-23"></a> scr <span class="op">=</span> gtk_scrolled_window_new <span class="op">();</span></span>
<span id="cb5-24"><a href="#cb5-24"></a> tv = tfe_text_view_new ();</span> <span id="cb5-24"><a href="#cb5-24"></a> tv <span class="op">=</span> tfe_text_view_new <span class="op">();</span></span>
<span id="cb5-25"><a href="#cb5-25"></a> tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));</span> <span id="cb5-25"><a href="#cb5-25"></a> tb <span class="op">=</span> gtk_text_view_get_buffer <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
<span id="cb5-26"><a href="#cb5-26"></a> gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR);</span> <span id="cb5-26"><a href="#cb5-26"></a> gtk_text_view_set_wrap_mode <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> GTK_WRAP_WORD_CHAR<span class="op">);</span></span>
<span id="cb5-27"><a href="#cb5-27"></a> gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);</span> <span id="cb5-27"><a href="#cb5-27"></a> gtk_scrolled_window_set_child <span class="op">(</span>GTK_SCROLLED_WINDOW <span class="op">(</span>scr<span class="op">),</span> tv<span class="op">);</span></span>
<span id="cb5-28"><a href="#cb5-28"></a></span> <span id="cb5-28"><a href="#cb5-28"></a></span>
<span id="cb5-29"><a href="#cb5-29"></a> tfe_text_view_set_file (TFE_TEXT_VIEW (tv), g_file_dup (files[i]));</span> <span id="cb5-29"><a href="#cb5-29"></a> tfe_text_view_set_file <span class="op">(</span>TFE_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> g_file_dup <span class="op">(</span>files<span class="op">[</span>i<span class="op">]));</span></span>
<span id="cb5-30"><a href="#cb5-30"></a> gtk_text_buffer_set_text (tb, contents, length);</span> <span id="cb5-30"><a href="#cb5-30"></a> gtk_text_buffer_set_text <span class="op">(</span>tb<span class="op">,</span> contents<span class="op">,</span> length<span class="op">);</span></span>
<span id="cb5-31"><a href="#cb5-31"></a> g_free (contents);</span> <span id="cb5-31"><a href="#cb5-31"></a> g_free <span class="op">(</span>contents<span class="op">);</span></span>
<span id="cb5-32"><a href="#cb5-32"></a> filename = g_file_get_basename (files[i]);</span> <span id="cb5-32"><a href="#cb5-32"></a> filename <span class="op">=</span> g_file_get_basename <span class="op">(</span>files<span class="op">[</span>i<span class="op">]);</span></span>
<span id="cb5-33"><a href="#cb5-33"></a> lab = gtk_label_new (filename);</span> <span id="cb5-33"><a href="#cb5-33"></a> lab <span class="op">=</span> gtk_label_new <span class="op">(</span>filename<span class="op">);</span></span>
<span id="cb5-34"><a href="#cb5-34"></a> gtk_notebook_append_page (GTK_NOTEBOOK (nb), scr, lab);</span> <span id="cb5-34"><a href="#cb5-34"></a> gtk_notebook_append_page <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">),</span> scr<span class="op">,</span> lab<span class="op">);</span></span>
<span id="cb5-35"><a href="#cb5-35"></a> nbp = gtk_notebook_get_page (GTK_NOTEBOOK (nb), scr);</span> <span id="cb5-35"><a href="#cb5-35"></a> nbp <span class="op">=</span> gtk_notebook_get_page <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">),</span> scr<span class="op">);</span></span>
<span id="cb5-36"><a href="#cb5-36"></a> g_object_set (nbp, <span class="st">&quot;tab-expand&quot;</span>, TRUE, NULL);</span> <span id="cb5-36"><a href="#cb5-36"></a> g_object_set <span class="op">(</span>nbp<span class="op">,</span> <span class="st">&quot;tab-expand&quot;</span><span class="op">,</span> TRUE<span class="op">,</span> NULL<span class="op">);</span></span>
<span id="cb5-37"><a href="#cb5-37"></a> g_free (filename);</span> <span id="cb5-37"><a href="#cb5-37"></a> g_free <span class="op">(</span>filename<span class="op">);</span></span>
<span id="cb5-38"><a href="#cb5-38"></a> } <span class="cf">else</span> <span class="cf">if</span> ((filename = g_file_get_path (files[i])) != NULL) {</span> <span id="cb5-38"><a href="#cb5-38"></a> <span class="op">}</span> <span class="cf">else</span> <span class="cf">if</span> <span class="op">((</span>filename <span class="op">=</span> g_file_get_path <span class="op">(</span>files<span class="op">[</span>i<span class="op">]))</span> <span class="op">!=</span> NULL<span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-39"><a href="#cb5-39"></a> g_print (<span class="st">&quot;No such file: %s.</span><span class="sc">\n</span><span class="st">&quot;</span>, filename);</span> <span id="cb5-39"><a href="#cb5-39"></a> g_print <span class="op">(</span><span class="st">&quot;No such file: %s.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">,</span> filename<span class="op">);</span></span>
<span id="cb5-40"><a href="#cb5-40"></a> g_free (filename);</span> <span id="cb5-40"><a href="#cb5-40"></a> g_free <span class="op">(</span>filename<span class="op">);</span></span>
<span id="cb5-41"><a href="#cb5-41"></a> } <span class="cf">else</span></span> <span id="cb5-41"><a href="#cb5-41"></a> <span class="op">}</span> <span class="cf">else</span></span>
<span id="cb5-42"><a href="#cb5-42"></a> g_print (<span class="st">&quot;No valid file is given</span><span class="sc">\n</span><span class="st">&quot;</span>);</span> <span id="cb5-42"><a href="#cb5-42"></a> g_print <span class="op">(</span><span class="st">&quot;No valid file is given</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb5-43"><a href="#cb5-43"></a> }</span> <span id="cb5-43"><a href="#cb5-43"></a> <span class="op">}</span></span>
<span id="cb5-44"><a href="#cb5-44"></a> <span class="cf">if</span> (gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb)) &gt; <span class="dv">0</span>) {</span> <span id="cb5-44"><a href="#cb5-44"></a> <span class="cf">if</span> <span class="op">(</span>gtk_notebook_get_n_pages <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">))</span> <span class="op">&gt;</span> <span class="dv">0</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-45"><a href="#cb5-45"></a> gtk_widget_show (win);</span> <span id="cb5-45"><a href="#cb5-45"></a> gtk_widget_show <span class="op">(</span>win<span class="op">);</span></span>
<span id="cb5-46"><a href="#cb5-46"></a> } <span class="cf">else</span></span> <span id="cb5-46"><a href="#cb5-46"></a> <span class="op">}</span> <span class="cf">else</span></span>
<span id="cb5-47"><a href="#cb5-47"></a> gtk_window_destroy (GTK_WINDOW (win));</span> <span id="cb5-47"><a href="#cb5-47"></a> gtk_window_destroy <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
<span id="cb5-48"><a href="#cb5-48"></a>}</span></code></pre></div> <span id="cb5-48"><a href="#cb5-48"></a><span class="op">}</span></span></code></pre></div>
<p>The whole source code of <code>tfe3.c</code> is stored in src/tfe directory. If you want to see it, click the link above.</p> <p>The whole source code of <code>tfe3.c</code> is stored in src/tfe
directory.</p>
<h3 id="using-ui-string">Using ui string</h3> <h3 id="using-ui-string">Using ui string</h3>
<p>GtkBuilder can build widgets using string. Use the function <code>gtk_builder_new_from_string</code> instead of <code>gtk_builder_new_from_file</code>.</p> <p>GtkBuilder can build widgets with string. Use
<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">char</span> *uistring;</span> <code>gtk_builder_new_from_string</code> instead of
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true"></a></span> <code>gtk_builder_new_from_file</code>.</p>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true"></a>uistring =</span> <div class="sourceCode" id="cb6"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="dt">char</span> <span class="op">*</span>uistring<span class="op">;</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true"></a><span class="st">&quot;&lt;interface&gt;&quot;</span></span> <span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true"></a> <span class="st">&quot;&lt;object class=&quot;</span>GtkApplicationWindow<span class="st">&quot; id=&quot;</span>win<span class="st">&quot;&gt;&quot;</span></span> <span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>uistring <span class="op">=</span></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true"></a> <span class="st">&quot;&lt;property name=</span><span class="sc">\&quot;</span><span class="st">title</span><span class="sc">\&quot;</span><span class="st">&gt;file editor&lt;/property&gt;&quot;</span></span> <span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a><span class="st">&quot;&lt;interface&gt;&quot;</span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true"></a> <span class="st">&quot;&lt;property name=</span><span class="sc">\&quot;</span><span class="st">default-width</span><span class="sc">\&quot;</span><span class="st">&gt;600&lt;/property&gt;&quot;</span></span> <span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;&lt;object class=&quot;</span>GtkApplicationWindow<span class="st">&quot; id=&quot;</span>win<span class="st">&quot;&gt;&quot;</span></span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true"></a> <span class="st">&quot;&lt;property name=</span><span class="sc">\&quot;</span><span class="st">default-height</span><span class="sc">\&quot;</span><span class="st">&gt;400&lt;/property&gt;&quot;</span></span> <span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;&lt;property name=</span><span class="sc">\&quot;</span><span class="st">title</span><span class="sc">\&quot;</span><span class="st">&gt;file editor&lt;/property&gt;&quot;</span></span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true"></a> <span class="st">&quot;&lt;child&gt;&quot;</span></span> <span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;&lt;property name=</span><span class="sc">\&quot;</span><span class="st">default-width</span><span class="sc">\&quot;</span><span class="st">&gt;600&lt;/property&gt;&quot;</span></span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true"></a> <span class="st">&quot;&lt;object class=</span><span class="sc">\&quot;</span><span class="st">GtkBox</span><span class="sc">\&quot;</span><span class="st"> id=</span><span class="sc">\&quot;</span><span class="st">boxv</span><span class="sc">\&quot;</span><span class="st">&gt;&quot;</span></span> <span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;&lt;property name=</span><span class="sc">\&quot;</span><span class="st">default-height</span><span class="sc">\&quot;</span><span class="st">&gt;400&lt;/property&gt;&quot;</span></span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true"></a> <span class="st">&quot;&lt;property name=&quot;</span>orientation<span class="st">&quot;&gt;GTK_ORIENTATION_VERTICAL&lt;/property&gt;&quot;</span></span> <span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;&lt;child&gt;&quot;</span></span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true"></a>... ... ...</span> <span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;&lt;object class=</span><span class="sc">\&quot;</span><span class="st">GtkBox</span><span class="sc">\&quot;</span><span class="st"> id=</span><span class="sc">\&quot;</span><span class="st">boxv</span><span class="sc">\&quot;</span><span class="st">&gt;&quot;</span></span>
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true"></a>... ... ...</span> <span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;&lt;property name=&quot;</span>orientation<span class="st">&quot;&gt;GTK_ORIENTATION_VERTICAL&lt;/property&gt;&quot;</span></span>
<span id="cb6-14"><a href="#cb6-14" aria-hidden="true"></a><span class="st">&quot;&lt;/interface&gt;&quot;</span>;</span> <span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span> <span class="op">...</span></span>
<span id="cb6-15"><a href="#cb6-15" aria-hidden="true"></a></span> <span id="cb6-13"><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span> <span class="op">...</span></span>
<span id="cb6-16"><a href="#cb6-16" aria-hidden="true"></a>build = gtk_builder_new_from_stringfile (uistring);</span></code></pre></div> <span id="cb6-14"><a href="#cb6-14" aria-hidden="true" tabindex="-1"></a><span class="st">&quot;&lt;/interface&gt;&quot;</span><span class="op">;</span></span>
<p>This method has an advantage and disadvantage. The advantage is that the ui string is written in the source code. So ui file is not necessary on runtime. The disadvantage is that writing C string is a bit bothersome because of the double quotes. If you want to use this method, you should write a script that transforms ui file into C-string.</p> <span id="cb6-15"><a href="#cb6-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-16"><a href="#cb6-16" aria-hidden="true" tabindex="-1"></a>build <span class="op">=</span> gtk_builder_new_from_string <span class="op">(</span>uistring<span class="op">,</span> <span class="op">-</span><span class="dv">1</span><span class="op">);</span></span></code></pre></div>
<p>This method has an advantage and disadvantage. The advantage is that
the ui string is written in the source code. So, no ui file is needed on
runtime. The disadvantage is that writing C string is a bit bothersome
because of the double quotes. If you want to use this method, you should
write a script that transforms ui file into C-string.</p>
<ul> <ul>
<li>Add backslash before each double quote.</li> <li>Add backslash before each double quote.</li>
<li>Add double quotes at the left and right of the string in each line.</li> <li>Add double quotes at the left and right of the string in each
line.</li>
</ul> </ul>
<h3 id="using-gresource">Using Gresource</h3> <h3 id="gresource">Gresource</h3>
<p>Using Gresource is similar to using string. But Gresource is compressed binary data, not text data. And theres a compiler that compiles ui file into Gresource. It can compile not only text files but also binary files such as images, sounds and so on. And after compilation, it bundles them up into one Gresource object.</p> <p>Using Gresource is similar to using string. But Gresource is
<p>An xml file is necessary for the resource compiler <code>glib-compile-resources</code>. It describes resource files.</p> compressed binary data, not text data. And theres a compiler that
<div class="sourceCode" id="cb7"><pre class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><span id="cb7-1"><a href="#cb7-1"></a><span class="kw">&lt;?xml</span> version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;<span class="kw">?&gt;</span></span> compiles ui file into Gresource. It can compile not only text files but
<span id="cb7-2"><a href="#cb7-2"></a><span class="kw">&lt;gresources&gt;</span></span> also binary files such as images, sounds and so on. And after
<span id="cb7-3"><a href="#cb7-3"></a> <span class="kw">&lt;gresource</span><span class="ot"> prefix=</span><span class="st">&quot;/com/github/ToshioCP/tfe3&quot;</span><span class="kw">&gt;</span></span> compilation, it bundles them up into one Gresource object.</p>
<span id="cb7-4"><a href="#cb7-4"></a> <span class="kw">&lt;file&gt;</span>tfe3.ui<span class="kw">&lt;/file&gt;</span></span> <p>An xml file is necessary for the resource compiler
<span id="cb7-5"><a href="#cb7-5"></a> <span class="kw">&lt;/gresource&gt;</span></span> <code>glib-compile-resources</code>. It describes resource files.</p>
<span id="cb7-6"><a href="#cb7-6"></a><span class="kw">&lt;/gresources&gt;</span></span></code></pre></div> <div class="sourceCode" id="cb7"><pre
class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><span id="cb7-1"><a href="#cb7-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="cb7-2"><a href="#cb7-2"></a>&lt;<span class="kw">gresources</span>&gt;</span>
<span id="cb7-3"><a href="#cb7-3"></a> &lt;<span class="kw">gresource</span><span class="ot"> prefix=</span><span class="st">&quot;/com/github/ToshioCP/tfe3&quot;</span>&gt;</span>
<span id="cb7-4"><a href="#cb7-4"></a> &lt;<span class="kw">file</span>&gt;tfe3.ui&lt;/<span class="kw">file</span>&gt;</span>
<span id="cb7-5"><a href="#cb7-5"></a> &lt;/<span class="kw">gresource</span>&gt;</span>
<span id="cb7-6"><a href="#cb7-6"></a>&lt;/<span class="kw">gresources</span>&gt;</span></code></pre></div>
<ul> <ul>
<li>2: <code>gresources</code> tag can include multiple gresources (gresource tags). However, this xml has only one gresource.</li> <li>2: <code>gresources</code> tag can include multiple gresources
<li>3: The gresource has a prefix <code>/com/github/ToshioCP/tfe3</code>.</li> (gresource tags). However, this xml has only one gresource.</li>
<li>4: The gresource has <code>tfe3.ui</code>. And it is pointed by <code>/com/github/ToshioCP/tfe3/tfe3.ui</code> because it needs prefix. If you want to add more files, then insert them between line 4 and 5.</li> <li>3: The gresource has a prefix
<code>/com/github/ToshioCP/tfe3</code>.</li>
<li>4: The name of the gresource is <code>tfe3.ui</code>. The resource
will be pointed with <code>/com/github/ToshioCP/tfe3/tfe3.ui</code> by
GtkBuilder. The pattern is “prefix” + “name”. If you want to add more
files, insert them between line 4 and 5.</li>
</ul> </ul>
<p>Save this xml text to <code>tfe3.gresource.xml</code>. The gresource compiler <code>glib-compile-resources</code> shows its usage with the argument <code>--help</code>.</p> <p>Save this xml text to <code>tfe3.gresource.xml</code>. The gresource
compiler <code>glib-compile-resources</code> shows its usage with the
argument <code>--help</code>.</p>
<pre><code>$ LANG=C glib-compile-resources --help <pre><code>$ LANG=C glib-compile-resources --help
Usage: Usage:
glib-compile-resources [OPTION?] FILE glib-compile-resources [OPTION?] FILE
@ -475,14 +560,20 @@ Application Options:
</code></pre> </code></pre>
<p>Now run the compiler.</p> <p>Now run the compiler.</p>
<pre><code>$ glib-compile-resources tfe3.gresource.xml --target=resources.c --generate-source</code></pre> <pre><code>$ glib-compile-resources tfe3.gresource.xml --target=resources.c --generate-source</code></pre>
<p>Then a C source file <code>resources.c</code> is generated. Modify <code>tfe3.c</code> and save it as <code>tfe3_r.c</code>.</p> <p>Then a C source file <code>resources.c</code> is generated. Modify
<div class="sourceCode" id="cb10"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true"></a><span class="pp">#include </span><span class="im">&quot;resources.c&quot;</span></span> <code>tfe3.c</code> and save it as <code>tfe3_r.c</code>.</p>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true"></a>... ... ...</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="pp">#include </span><span class="im">&quot;resources.c&quot;</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true"></a>... ... ...</span> <span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span> <span class="op">...</span></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true"></a>build = gtk_builder_new_from_resource (<span class="st">&quot;/com/github/ToshioCP/tfe3/tfe3.ui&quot;</span>);</span> <span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span> <span class="op">...</span></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true"></a>... ... ...</span> <span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a>build <span class="op">=</span> gtk_builder_new_from_resource <span class="op">(</span><span class="st">&quot;/com/github/ToshioCP/tfe3/tfe3.ui&quot;</span><span class="op">);</span></span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true"></a>... ... ...</span></code></pre></div> <span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span> <span class="op">...</span></span>
<p>Then, compile and run it. The window appears and it is the same as the screenshot at the beginning of this page.</p> <span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span> <span class="op">...</span></span></code></pre></div>
<p>The function <code>gtk_builder_new_from_resource</code> builds
widgets from a resource.</p>
<p>Then, compile and run it. A window appears and it is the same as the
screenshot at the beginning of this page.</p>
<p>Generally, resource is the best way for C language. If you use other
languages like Ruby, string is better than resource.</p>
</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> <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> </body>

View file

@ -5,7 +5,7 @@
<meta name="generator" content="pandoc" /> <meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <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"> <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> <title>GTK 4 tutorial</title>
<style> <style>
code{white-space: pre-wrap;} code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;} span.smallcaps{font-variant: small-caps;}
@ -106,7 +106,8 @@
</div> </div>
</nav> </nav>
<h1 id="tfetextview-api-reference">TfeTextView API reference</h1> <h1 id="tfetextview-api-reference">TfeTextView API reference</h1>
<p>TfeTextView Child object of GtkTextView. It holds GFile which the contents of GtkTextBuffer correponds to.</p> <p>TfeTextView Child object of GtkTextView. It is connected to a
certain file.</p>
<h2 id="functions">Functions</h2> <h2 id="functions">Functions</h2>
<ul> <ul>
<li>GFile *tfe_text_view_get_file ()</li> <li>GFile *tfe_text_view_get_file ()</li>
@ -136,7 +137,8 @@
<h2 id="includes">Includes</h2> <h2 id="includes">Includes</h2>
<pre><code>#include &lt;gtk/gtk.h&gt;</code></pre> <pre><code>#include &lt;gtk/gtk.h&gt;</code></pre>
<h2 id="description">Description</h2> <h2 id="description">Description</h2>
<p>TfeTextView holds GFile which the contents of GtkTextBuffer corresponds to. File manipulation functions are added to this object.</p> <p>TfeTextView holds GFile corresponds to the contents of GtkTextBuffer.
It has some file manipulation functions.</p>
<h2 id="functions-1">Functions</h2> <h2 id="functions-1">Functions</h2>
<h3 id="tfe_text_view_get_file">tfe_text_view_get_file()</h3> <h3 id="tfe_text_view_get_file">tfe_text_view_get_file()</h3>
<pre><code>GFile * <pre><code>GFile *
@ -149,7 +151,13 @@ tfe_text_view_get_file (TfeTextView *tv);</code></pre>
<h3 id="tfe_text_view_open">tfe_text_view_open()</h3> <h3 id="tfe_text_view_open">tfe_text_view_open()</h3>
<pre><code>void <pre><code>void
tfe_text_view_open (TfeTextView *tv, GtkWidget *win);</code></pre> tfe_text_view_open (TfeTextView *tv, GtkWidget *win);</code></pre>
<p>Just shows a GtkFileChooserDialog so that a user can choose a file to read. This function doesnt do any I/O operations. They are done by the signal handler connected to the <code>response</code> signal emitted by GtkFileChooserDialog. Therefore the caller cant know the I/O status directly from the function. Instead, the status is informed by <code>open-response</code> signal. The caller needs to set a handler to this signal in advance.</p> <p>Just shows a GtkFileChooserDialog so that a user can choose a file to
read. This function doesnt do any I/O operations. They are done by the
signal handler connected to the <code>response</code> signal emitted by
GtkFileChooserDialog. Therefore the caller cant know the I/O status
directly from <code>tfe_text_view_open</code>. Instead, the status is
informed by <code>open-response</code> signal. The caller needs to set a
handler to this signal in advance.</p>
<p>parameters</p> <p>parameters</p>
<ul> <ul>
<li>tv: a TfeTextView</li> <li>tv: a TfeTextView</li>
@ -158,7 +166,9 @@ tfe_text_view_open (TfeTextView *tv, GtkWidget *win);</code></pre>
<h3 id="tfe_text_view_save">tfe_text_view_save()</h3> <h3 id="tfe_text_view_save">tfe_text_view_save()</h3>
<pre><code>void <pre><code>void
tfe_text_view_save (TfeTextView *tv);</code></pre> tfe_text_view_save (TfeTextView *tv);</code></pre>
<p>Saves the contents of a TfeTextView to a file. If <code>tv</code> holds a GFile, it is used. Otherwise, this function shows GtkFileChosserDialog so that a user can choose a file to save.</p> <p>Saves the contents of the TfeTextView to a file. If <code>tv</code>
holds a GFile, it is used. Otherwise, this function shows
GtkFileChooserDialog so that the user can choose a file to save.</p>
<p>Parameters</p> <p>Parameters</p>
<ul> <ul>
<li>tv: a TfeTextView</li> <li>tv: a TfeTextView</li>
@ -166,7 +176,8 @@ tfe_text_view_save (TfeTextView *tv);</code></pre>
<h3 id="tfe_text_view_saveas">tfe_text_view_saveas()</h3> <h3 id="tfe_text_view_saveas">tfe_text_view_saveas()</h3>
<pre><code>void <pre><code>void
tfe_text_view_saveas (TfeTextView *tv);</code></pre> tfe_text_view_saveas (TfeTextView *tv);</code></pre>
<p>Saves the content of a TfeTextView to a file. This function shows GtkFileChosserDialog so that a user can choose a file to save.</p> <p>Saves the content of a TfeTextView to a file. This function shows
GtkFileChooserDialog so that a user can choose a file to save.</p>
<p>Parameters</p> <p>Parameters</p>
<ul> <ul>
<li>tv: a TfeTextView</li> <li>tv: a TfeTextView</li>
@ -174,7 +185,10 @@ tfe_text_view_saveas (TfeTextView *tv);</code></pre>
<h3 id="tfe_text_view_new_with_file">tfe_text_view_new_with_file()</h3> <h3 id="tfe_text_view_new_with_file">tfe_text_view_new_with_file()</h3>
<pre><code>GtkWidget * <pre><code>GtkWidget *
tfe_text_view_new_with_file (GFile *file);</code></pre> tfe_text_view_new_with_file (GFile *file);</code></pre>
<p>Creates a new TfeTextView and reads the contents of the <code>file</code> and set it to the GtkTextBuffer corresponds to the newly created TfeTextView. Then returns the TfeTextView as GtkWidget. If an error happens, it returns <code>NULL</code>.</p> <p>Creates a new TfeTextView and reads the contents of the
<code>file</code> and set it to the GtkTextBuffer corresponds to the
newly created TfeTextView. Then returns the TfeTextView as GtkWidget. If
an error happens, it returns <code>NULL</code>.</p>
<p>Parameters</p> <p>Parameters</p>
<ul> <ul>
<li>file: a GFile</li> <li>file: a GFile</li>
@ -186,7 +200,8 @@ tfe_text_view_new_with_file (GFile *file);</code></pre>
<h3 id="tfe_text_view_new">tfe_text_view_new()</h3> <h3 id="tfe_text_view_new">tfe_text_view_new()</h3>
<pre><code>GtkWidget * <pre><code>GtkWidget *
tfe_text_view_new (void);</code></pre> tfe_text_view_new (void);</code></pre>
<p>Creates a new TfeTextView and returns the TfeTextView as GtkWidget. If an error happens, it returns <code>NULL</code>.</p> <p>Creates a new TfeTextView and returns the TfeTextView as GtkWidget.
If an error happens, it returns <code>NULL</code>.</p>
<p>Returns</p> <p>Returns</p>
<ul> <ul>
<li>a new TfeTextView.</li> <li>a new TfeTextView.</li>
@ -199,37 +214,50 @@ struct _TfeTextView
GtkTextView parent; GtkTextView parent;
GFile *file; GFile *file;
};</code></pre> };</code></pre>
<p>The members of this structure are not allowed to be accessed by any outer objects. If you want to obtain a copy of the GFile, use <code>tfe_text_view_get_file</code>.</p> <p>The members of this structure are not allowed to be accessed by any
outer objects. If you want to obtain a copy of the GFile, use
<code>tfe_text_view_get_file</code>.</p>
<h3 id="tfetextviewclass">TfeTextViewClass</h3> <h3 id="tfetextviewclass">TfeTextViewClass</h3>
<pre><code>typedef struct { <pre><code>typedef struct {
GtkTextViewClass parent_class; GtkTextViewClass parent_class;
} TfeTextViewClass;</code></pre> } TfeTextViewClass;</code></pre>
<p>No member is added because TfeTextView is a final type object.</p> <p>No member is added because TfeTextView is a final type object.</p>
<h3 id="enum-tfetextviewopenresponsetype">enum TfeTextViewOpenResponseType</h3> <h3 id="enum-tfetextviewopenresponsetype">enum
<p>Predefined values for the response id given by <code>open-response</code> signal.</p> TfeTextViewOpenResponseType</h3>
<p>Predefined values for the response id given by
<code>open-response</code> signal.</p>
<p>Members:</p> <p>Members:</p>
<ul> <ul>
<li>TFE_OPEN_RESPONSE_SUCCESS: The file is successfully opened.</li> <li>TFE_OPEN_RESPONSE_SUCCESS: The file is successfully opened.</li>
<li>TFE_OPEN_RESPONSE_CANCEL: Reading file is canceled by the user.</li> <li>TFE_OPEN_RESPONSE_CANCEL: Reading file is canceled by the user.</li>
<li>TFE_OPEN_RESPONSE_ERROR: An error happened during the opening or reading process.</li> <li>TFE_OPEN_RESPONSE_ERROR: An error happened during the opening or
reading process.</li>
</ul> </ul>
<h2 id="properties">Properties</h2>
<h3 id="wrap-mode">wrap-mode</h3>
<p>The property “wrap-mode” belongs to GtkTextView. TfeTextView inherits
it and the value is set to GTK_WRAP_WORD_CHAR as a default.</p>
<h2 id="signals-1">Signals</h2> <h2 id="signals-1">Signals</h2>
<h3 id="change-file">change-file</h3> <h3 id="change-file">change-file</h3>
<pre><code>void <pre><code>void
user_function (TfeTextView *tv, user_function (TfeTextView *tv,
gpointer user_data)</code></pre> gpointer user_data)</code></pre>
<p>Emitted when the GFile in the TfeTextView object is changed. The signal is emitted when:</p> <p>Emitted when the GFile in the TfeTextView object is changed. The
signal is emitted when:</p>
<ul> <ul>
<li>a new file is opened and read</li> <li>a new file is opened and read</li>
<li>a user choose a file with GtkFileChooserDialog and save the contents.</li> <li>a user choose a file with GtkFileChooserDialog and save the
<li>an error occured during I/O operation, and GFile is removed as a result.</li> contents.</li>
<li>an error occured during I/O operation, and GFile is removed as a
result.</li>
</ul> </ul>
<h3 id="open-response">open-response</h3> <h3 id="open-response">open-response</h3>
<pre><code>void <pre><code>void
user_function (TfeTextView *tv, user_function (TfeTextView *tv,
TfeTextViewOpenResponseType response-id, TfeTextViewOpenResponseType response-id,
gpointer user_data)</code></pre> gpointer user_data)</code></pre>
<p>Emitted after the user calls <code>tfe_text_view_open</code>. This signal informs the status of file opening and reading.</p> <p>Emitted after the user calls <code>tfe_text_view_open</code>. This
signal informs the status of file opening and reading.</p>
</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> <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> </body>

View file

@ -8,45 +8,42 @@ Up: [README.md](../README.md), Next: [Section 2](sec2.md)
This tutorial is about GTK 4 libraries. This tutorial is about GTK 4 libraries.
It is originally used on Linux with C compiler, but now it is used more widely, on Windows and MacOS, with Vala, Python and so on. It is originally used on Linux with C compiler, but now it is used more widely, on Windows and MacOS, with Vala, Python and so on.
However, this tutorial describes only _C programs on Linux_. However, this tutorial describes only *C programs on Linux*.
If you want to try the examples in the tutorial, you need: If you want to try the examples in the tutorial, you need:
- PC with Linux distribution like Ubuntu, Debian and so on. - PC with Linux distribution like Ubuntu or Debian.
- Gcc. - GCC.
- GTK 4. - GTK 4.
The stable version of Gtk on Linux distributions is version three at present. The stable version of GTK is 4.8.2 at present (13/Dec/2022), but older version (4.0 or higher) may work.
You need to install GTK 4 to your computer. See [Section 3](sec3.md) for the installation for GTK 4.
See [Section 3](sec3.md) for the installation of GTK 4.
### Ruby and rake for making the document ### Ruby and rake for making the document
This repository includes Ruby programs. This repository includes Ruby programs.
They are used to make Markdown files, HTML files, Latex files and a PDF file. They are used to make GFM (GitHub Flavoured Markdown) files, HTML files, Latex files and a PDF file.
You need: You need:
- Linux distribution like Ubuntu. - Linux.
- Ruby programming language. - Ruby programming language.
There are two ways to install it. There are two ways to install it.
One is installing the distribution's package. One is installing the distribution's package.
The other is using rbenv and ruby-build. The other is using rbenv and ruby-build.
If you want to use the latest version of ruby, use rbenv. If you want to use the latest version of ruby, use rbenv.
- Rake. - Rake.
It is a gem, which is a library written in Ruby. You don't need to install it separately because it is a standard library of Ruby.
You can install it as a package of your distribution or use gem command.
## License ## License
Copyright (C) 2020 ToshioCP (Toshio Sekiya) Copyright (C) 2020-2022 ToshioCP (Toshio Sekiya)
Gtk4-tutorial repository contains the tutorial document and software such as converters, generators and controllers. GTK4-tutorial repository contains tutorial documents and programs such as converters, generators and controllers.
All of them make up the 'Gtk4-tutorial' package. All of them make up the 'GTK4-tutorial' package.
This package is simply called 'Gtk4-tutorial' in the following description. This package is simply called 'GTK4-tutorial' in the following description.
'Gtk4-tutorial' is free; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License or, at your option, any later version. 'GTK4-tutorial' is free; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License or, at your option, any later version.
'Gtk4-tutorial' is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 'GTK4-tutorial' is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the [GNU General Public License](https://www.gnu.org/licenses/gpl-3.0.html) for more details. See the [GNU General Public License](https://www.gnu.org/licenses/gpl-3.0.html) for more details.
Up: [README.md](../README.md), Next: [Section 2](sec2.md) Up: [README.md](../README.md), Next: [Section 2](sec2.md)

View file

@ -1,13 +1,13 @@
Up: [Readme.md](../Readme.md), Prev: [Section 9](sec9.md), Next: [Section 11](sec11.md) Up: [README.md](../README.md), Prev: [Section 9](sec9.md), Next: [Section 11](sec11.md)
# Build system # Build system
## What do we need to think about to manage big source files? ## Managing big source files
We've compiled a small editor so far. We've compiled a small editor so far.
But Some bad signs are beginning to appear. But Some bad signs are beginning to appear.
- We've had only one C source file and put everything into it. - We've had only one C source file and put everything in it.
We need to sort it out. We need to sort it out.
- There are two compilers, `gcc` and `glib-compile-resources`. - There are two compilers, `gcc` and `glib-compile-resources`.
We should control them by one building tool. We should control them by one building tool.
@ -16,7 +16,7 @@ These ideas are useful to manage big source files.
## Divide a C source file into two parts. ## Divide a C source file into two parts.
When you divide C source file into several parts, each file should contain only one thing. When you divide C source file into several parts, each file should contain one thing.
For example, our source has two things, the definition of TfeTextView and functions related to GtkApplication and GtkApplicationWindow. For example, our source has two things, the definition of TfeTextView and functions related to GtkApplication and GtkApplicationWindow.
It is a good idea to separate them into two files, `tfetextview.c` and `tfe.c`. It is a good idea to separate them into two files, `tfetextview.c` and `tfe.c`.
@ -105,12 +105,12 @@ All the source files are listed below.
2 #include "tfetextview.h" 2 #include "tfetextview.h"
3 3
4 static void 4 static void
5 app_activate (GApplication *app, gpointer user_data) { 5 app_activate (GApplication *app) {
6 g_print ("You need a filename argument.\n"); 6 g_print ("You need a filename argument.\n");
7 } 7 }
8 8
9 static void 9 static void
10 app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer user_data) { 10 app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint) {
11 GtkWidget *win; 11 GtkWidget *win;
12 GtkWidget *nb; 12 GtkWidget *nb;
13 GtkWidget *lab; 13 GtkWidget *lab;
@ -186,10 +186,8 @@ The ui file `tfe.ui` is the same as `tfe3.ui` in the previous section.
6 </gresources> 6 </gresources>
~~~ ~~~
## Make
Dividing a file makes it easy to maintain source files. Dividing a file makes it easy to maintain source files.
But now we are faced with a new problem. But now we face a new problem.
The building step increases. The building step increases.
- Compiling the ui file `tfe.ui` into `resources.c`. - Compiling the ui file `tfe.ui` into `resources.c`.
@ -198,141 +196,18 @@ The building step increases.
- Compiling `resources.c` into `resources.o`. - Compiling `resources.c` into `resources.o`.
- Linking all the object files into application `tfe`. - Linking all the object files into application `tfe`.
Now build tool is necessary to manage it. Build tools manage the steps.
Make is one of the build tools. I'll show you three build tools, Meson and Ninja, Make and Rake.
It was created in 1976. Meson and Ninja is recommended as a C build tool, but others are also fine.
It is an old and widely used program. It's your choice.
Make analyzes Makefile and executes compilers. ## Meson and Ninja
All instructions are written in Makefile.
~~~makefile Meson and Ninja is one of the most popular building tool to build C language program.
sample.o: sample.c Many developers use Meson and Ninja lately.
gcc -o sample.o sample.c For example, GTK 4 uses them.
~~~
The sample of Malefile above consists of three elements, `sample.o`, `sample.c` and `gcc -o sample.o sample.c`. You need to make `meson.build` file first.
- `sample.o` is called target.
- `sample.c` is prerequisite.
- `gcc -o sample.o sample.c` is recipe.
Recipes follow tab characters, not spaces.
(It is very important. Use tab not space, or make won't work as you expected).
The rule is:
If a prerequisite modified later than a target, then make executes the recipe.
In the example above, if `sample.c` is modified after the generation of `sample.o`, then make executes gcc and compile `sample.c` into `sample.o`.
If the modification time of `sample.c` is older then the generation of `sample.o`, then no compiling is necessary, so make does nothing.
The Makefile for `tfe` is as follows.
~~~makefile
1 all: tfe
2
3 tfe: tfe.o tfetextview.o resources.o
4 gcc -o tfe tfe.o tfetextview.o resources.o `pkg-config --libs gtk4`
5
6 tfe.o: tfe.c tfetextview.h
7 gcc -c -o tfe.o `pkg-config --cflags gtk4` tfe.c
8 tfetextview.o: tfetextview.c tfetextview.h
9 gcc -c -o tfetextview.o `pkg-config --cflags gtk4` tfetextview.c
10 resources.o: resources.c
11 gcc -c -o resources.o `pkg-config --cflags gtk4` resources.c
12
13 resources.c: tfe.gresource.xml tfe.ui
14 glib-compile-resources tfe.gresource.xml --target=resources.c --generate-source
15
16 .Phony: clean
17
18 clean:
19 rm -f tfe tfe.o tfetextview.o resources.o resources.c
~~~
You only need to type `make`.
$ make
gcc -c -o tfe.o `pkg-config --cflags gtk4` tfe.c
gcc -c -o tfetextview.o `pkg-config --cflags gtk4` tfetextview.c
glib-compile-resources tfe.gresource.xml --target=resources.c --generate-source
gcc -c -o resources.o `pkg-config --cflags gtk4` resources.c
gcc -o tfe tfe.o tfetextview.o resources.o `pkg-config --libs gtk4`
I used only very basic rules to write this Makefile.
There are many more convenient methods to make it more compact.
But it will be long to explain it.
So I want to finish explaining make and move on to the next topic.
## Rake
Rake is a similar program to make.
It is written in Ruby code.
If you don't use Ruby, you don't need to read this subsection.
However, Ruby is really sophisticated and recommendable script language.
- Rakefile controls the behavior of `rake`.
- You can write any Ruby code in Rakefile.
Rake has task and file task, which is similar to target, prerequisite and recipe in make.
~~~ruby
1 require 'rake/clean'
2
3 targetfile = "tfe"
4 srcfiles = FileList["tfe.c", "tfetextview.c", "resources.c"]
5 rscfile = srcfiles[2]
6 objfiles = srcfiles.gsub(/.c$/, '.o')
7
8 CLEAN.include(targetfile, objfiles, rscfile)
9
10 task default: targetfile
11
12 file targetfile => objfiles do |t|
13 sh "gcc -o #{t.name} #{t.prerequisites.join(' ')} `pkg-config --libs gtk4`"
14 end
15
16 objfiles.each do |obj|
17 src = obj.gsub(/.o$/,'.c')
18 file obj => src do |t|
19 sh "gcc -c -o #{t.name} `pkg-config --cflags gtk4` #{t.source}"
20 end
21 end
22
23 file rscfile => ["tfe.gresource.xml", "tfe.ui"] do |t|
24 sh "glib-compile-resources #{t.prerequisites[0]} --target=#{t.name} --generate-source"
25 end
~~~
The contents of the `Rakefile` is almost same as the `Makefile` in the previous subsection.
- 3-6: Defines target file, source file and so on.
- 1, 8: Loads clean library. And defines CLEAN file list.
The files included by CLEAN will be removed when `rake clean` is typed on the command line.
- 10: The default target depends on `targetfile`.
The task `default` is the final goal of tasks.
- 12-14: `targetfile` depends on `objfiles`.
The variable `t` is a task object.
- `t.name` is a target name
- `t.prerequisites` is an array of prerequisites.
- `t.source` is the first element of prerequisites.
- `sh` is a method to give the following string to shell as an argument and executes the shell.
- 16-21: There's a loop by each element of the array of `objfiles`. Each object depends on corresponding source file.
- 23-25: Resource file depends on xml file and ui file.
Rakefile might seem to be difficult for beginners.
But, you can use any Ruby syntax in Rakefile, so it is really flexible.
If you practice Ruby and Rakefile, it will be highly productive tools.
## Meson and ninja
Meson is one of the most popular building tool despite the developing version.
And ninja is similar to make but much faster than make.
Several years ago, most of the C developers used autotools and make.
But now the situation has changed.
Many developers are using meson and ninja now.
To use meson, you first need to write `meson.build` file.
~~~meson ~~~meson
1 project('tfe', 'c') 1 project('tfe', 'c')
@ -372,15 +247,146 @@ Then, the executable file `tfe` is generated under the directory `_build`.
$ _build/tfe tfe.c tfetextview.c $ _build/tfe tfe.c tfetextview.c
Then the window appears. A window appears.
And two notebook pages are in the window. It includes a notebook with two pages.
One notebook is `tfe.c` and the other is `tfetextview.c`. One is `tfe.c` and the other is `tfetextview.c`.
I've shown you three build tools. For further information, see [The Meson Build system](https://mesonbuild.com/).
I think meson and ninja is the best choice for the present.
We divided a file into some categorized files and used a build tool. ## Make
This method is used by many developers.
Make is a build tool created in 1976.
It was a standard build tool for C compiling, but lately it is replaced by Meson and Ninja.
Up: [Readme.md](../Readme.md), Prev: [Section 9](sec9.md), Next: [Section 11](sec11.md) Make analyzes Makefile and executes compilers.
All instructions are written in Makefile.
For example,
~~~makefile
sample.o: sample.c
gcc -o sample.o sample.c
~~~
Malefile above consists of three elements, `sample.o`, `sample.c` and `gcc -o sample.o sample.c`.
- `sample.o` is a target.
- `sample.c` is a prerequisite.
- `gcc -o sample.o sample.c` is a recipe.
Recipes follow tab characters, not spaces.
(It is very important. Use tab, or make won't work as you expected).
The rule is:
If a prerequisite modified later than a target, then make executes the recipe.
In the example above, if `sample.c` is modified after the generation of `sample.o`, then make executes gcc and compile `sample.c` into `sample.o`.
If the modification time of `sample.c` is older then the generation of `sample.o`, then no compiling is necessary, so make does nothing.
The Makefile for `tfe` is as follows.
~~~makefile
1 all: tfe
2
3 tfe: tfe.o tfetextview.o resources.o
4 gcc -o tfe tfe.o tfetextview.o resources.o `pkg-config --libs gtk4`
5
6 tfe.o: tfe.c tfetextview.h
7 gcc -c -o tfe.o `pkg-config --cflags gtk4` tfe.c
8 tfetextview.o: tfetextview.c tfetextview.h
9 gcc -c -o tfetextview.o `pkg-config --cflags gtk4` tfetextview.c
10 resources.o: resources.c
11 gcc -c -o resources.o `pkg-config --cflags gtk4` resources.c
12
13 resources.c: tfe.gresource.xml tfe.ui
14 glib-compile-resources tfe.gresource.xml --target=resources.c --generate-source
15
16 .Phony: clean
17
18 clean:
19 rm -f tfe tfe.o tfetextview.o resources.o resources.c
~~~
You just type `make` and everything will be done.
~~~
$ make
gcc -c -o tfe.o `pkg-config --cflags gtk4` tfe.c
gcc -c -o tfetextview.o `pkg-config --cflags gtk4` tfetextview.c
glib-compile-resources tfe.gresource.xml --target=resources.c --generate-source
gcc -c -o resources.o `pkg-config --cflags gtk4` resources.c
gcc -o tfe tfe.o tfetextview.o resources.o `pkg-config --libs gtk4`
~~~
I used only very basic rules to write this Makefile.
There are many more convenient methods to make it more compact.
But it will be long to explain it.
So I want to finish with make and move on to the next topic.
You can download "Gnu Make Manual" from [GNU website](https://www.gnu.org/software/make/manual/).
## Rake
Rake is a similar program to make.
It is written in Ruby language.
If you don't use Ruby, you don't need to read this subsection.
However, Ruby is really sophisticated and recommendable script language.
- Rakefile controls the behavior of `rake`.
- You can write any Ruby code in Rakefile.
Rake has task and file task, which is similar to target, prerequisite and recipe in make.
~~~ruby
1 require 'rake/clean'
2
3 targetfile = "tfe"
4 srcfiles = FileList["tfe.c", "tfetextview.c", "resources.c"]
5 uifile = "tfe.ui"
6 rscfile = srcfiles[2]
7 objfiles = srcfiles.ext(".o")
8 gresource_xml = "tfe.gresource.xml"
9
10 CLEAN.include(targetfile, objfiles, rscfile)
11
12 task default: targetfile
13
14 file targetfile => objfiles do |t|
15 sh "gcc -o #{t.name} #{t.prerequisites.join(' ')} `pkg-config --libs gtk4`"
16 end
17
18 objfiles.each do |obj|
19 src = obj.ext(".c")
20 file obj => src do |t|
21 sh "gcc -c -o #{t.name} `pkg-config --cflags gtk4` #{t.source}"
22 end
23 end
24
25 file rscfile => uifile do |t|
26 sh "glib-compile-resources #{gresource_xml} --target=#{t.name} --generate-source"
27 end
~~~
The contents of the `Rakefile` is almost same as the `Makefile` in the previous subsection.
- 3-8: Defines target file, source files and so on.
- 1, 10 Requires rake/clean library. And clean files are added to CLEAN.
The files included by CLEAN will be removed when `rake clean` is typed on the command line.
- 12: The default target depends on `targetfile`.
The task `default` is the final goal of tasks.
- 14-16: `targetfile` depends on `objfiles`.
The variable `t` is a task object.
- `t.name` is a target name
- `t.prerequisites` is an array of prerequisites.
- `t.source` is the first element of prerequisites.
- `sh` is a method to give the following string to shell as an argument and executes the shell.
- 18-23: An each iterator of the array `objfiles`. Each object depends on corresponding source file.
- 25-27: Resource file depends on ui file.
Rakefile might seem to be difficult for beginners.
But, you can use any Ruby syntax in the Rakefile, so it is really flexible.
If you practice Ruby and Rakefile, it will be highly productive tools.
For further information, see [Rake tutorial for beginners](https://toshiocp.github.io/Rake-tutorial-for-beginners-en/LearningRake.html).
Up: [README.md](../README.md), Prev: [Section 9](sec9.md), Next: [Section 11](sec11.md)

View file

@ -1,11 +1,10 @@
Up: [Readme.md](../Readme.md), Prev: [Section 10](sec10.md), Next: [Section 12](sec12.md) Up: [README.md](../README.md), Prev: [Section 10](sec10.md), Next: [Section 12](sec12.md)
# Initialization and destruction of instances # Instance Initialization and destruction
A new version of the text file editor (`tfe`) will be made in this section and the following four sections. A new version of the text file editor (`tfe`) will be made in this section and the following four sections.
It is `tfe5`. It is `tfe5`.
There are many changes from the prior version. There are many changes from the prior version.
All the sources are listed in [Section 16](sec16.md).
They are located in two directories, [src/tfe5](../src/tfe5) and [src/tfetextview](../src/tfetextview). They are located in two directories, [src/tfe5](../src/tfe5) and [src/tfetextview](../src/tfetextview).
## Encapsulation ## Encapsulation
@ -35,11 +34,10 @@ After that I will explain:
## GObject and its children ## GObject and its children
GObject and its children are objects, which have both class and instance. GObject and its children are objects, which have both class and object C structures.
First, think about instance of objects. First, think about instances.
Instance is structured memories. An instance is memories which has the object structure.
The structure is described as C language structure. The following is the structure of TfeTextView.
The following is a structure of TfeTextView.
~~~C ~~~C
/* This typedef statement is automatically generated by the macro G_DECLARE_FINAL_TYPE */ /* This typedef statement is automatically generated by the macro G_DECLARE_FINAL_TYPE */
@ -56,16 +54,10 @@ The members of the structure are:
- The type of `parent` is GtkTextView which is C structure. - The type of `parent` is GtkTextView which is C structure.
It is declared in `gtktextview.h`. It is declared in `gtktextview.h`.
GtkTextView is the parent of TfeTextView. GtkTextView is the parent of TfeTextView.
- `file` is a pointer to GFile. It can be NULL if no file corresponds to the TfeTextView object. - `file` is a pointer to a GFile. It can be NULL if no file corresponds to the TfeTextView instance.
Notice the program above is the declaration of the structure, not the definition. You can find the declaration of the ancestors' object structures in the source files of GTK and GLib.
So, no memories are allocated at this moment. The following is extracted from the source files (not exactly the same).
They are to be allocated when `tfe_text_view_new` function is invoked.
The memory allocated with `tfe_text_view_new` is an instance of TfeTextView object.
Therefore, There can be multiple TfeTextView instances if `tfe_text_view_new` is called multiple times.
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).
~~~C ~~~C
typedef struct _GObject GObject; typedef struct _GObject GObject;
@ -93,34 +85,65 @@ struct _GtkTextView
~~~ ~~~
In each structure, its parent is declared at the top of the members. In each structure, its parent is declared at the top of the members.
So, every ancestors is included in the child instance. So, all the ancestors are included in the child object.
This is very important.
It guarantees a child widget to inherit all the features from ancestors.
The structure of `TfeTextView` is like the following diagram. The structure of `TfeTextView` is like the following diagram.
![The structure of the instance TfeTextView](../image/TfeTextView.png) ![The structure of the instance TfeTextView](../image/TfeTextView.png)
Derivable classes (ancestor classes) have their own private data area which are not included by the structure above.
For example, GtkWidget has GtkWidgetPrivate (C structure) for its private data.
## Initialization of a TfeTextView instance Notice declarations are not definitions.
So, no memories are allocated when C structures are declared.
Memories are allocated to them from the heap area when the `tfe_text_view_new` function is called.
At the same time, the ancestors' private area allocated for the TfeTetView.
They are hidden from TfeTextView and it can't access to them directly.
The created memory is called instance.
When a TfeTextView instance is created, it is given three data area.
- The instance (C structure).
- GtkWidgetPrivate structure.
- GtkTextViewPrivate structure.
TfeTextView functions can access to its instance only.
The GtkWidgetPrivate and GtkTextViewPrivate are used by the ancestors' functions.
See the following example.
~~~C
GtkWidget *tv = tfe_text_view_new ();
GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
~~~
The parent's function `gtk_text_view_get_buffer` accesses the GtkTextViewPrivate data (owned by `tv`).
There is a pointer, which points the GtkBuffer, in the private area and the function returns the pointer.
(Actual behavior is a bit more complicated.)
TfeTextView instances inherit the ancestors functions like this.
A TfeTextView instance is created every time the `tfe_text_view_new` function is called.
Therefore, multiple TfeTextView instances can exist.
## Initialization of TfeTextView instances
The function `tfe_text_view_new` creates a new TfeTextView instance. The function `tfe_text_view_new` creates a new TfeTextView instance.
~~~C ~~~C
1 GtkWidget * 1 GtkWidget *
2 tfe_text_view_new (void) { 2 tfe_text_view_new (void) {
3 return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL)); 3 return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, "wrap-mode", GTK_WRAP_WORD_CHAR, NULL));
4 } 4 }
~~~ ~~~
When this function is invoked, a TfeTextView instance is created and initialized. When this function is invoked, a TfeTextView instance is created and initialized.
The initialization process is as follows. The initialization process is as follows.
1. Initializes GObject (GInitiallyUnowned) part in TfeTextView instance. 1. When the instance is created, GtkWidgetPrivate and GtkTextViewPrivate structures are also created
2. Initializes GtkWidget part in TfeTextView instance. 2. Initializes GObject (GInitiallyUnowned) part in the TfeTextView instance.
3. Initializes GtkTextView part in TfeTextView instance. 3. Initializes GtkWidget part (the first `priv`) in the TfeTextView instance and GtkWidgetPrivate structure.
4. Initializes TfeTextView part in TfeTextView instance. 4. Initializes GtkTextView part (the second `priv`) in the TfeTextView instance and GtkTextViewPrivate structure.
5. Initializes TfeTextView part (`file`) in the TfeTextView instance.
The step one through three is done by `g_object_init`, `gtk_widget_init` and `gtk_text_view_init`. The step two through four is done by `g_object_init`, `gtk_widget_init` and `gtk_text_view_init`.
They are called by the system automatically and you don't need to care about them. They are called by the system automatically and you don't need to care about them.
Step four is done by the function `tfe_text_view_init` in `tfetextview.c`. Step four is done by the function `tfe_text_view_init` in `tfetextview.c`.
@ -136,21 +159,25 @@ This function just initializes `tv->file` to be `NULL`.
## Functions and Classes ## Functions and Classes
In Gtk, all objects derived from GObject have class and instance (except abstract object). 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. Instances are memory of C structure, which are described in the previous two subsections.
Each object can have more than one instance. Each object can have more than one instance.
Those instances have the same structure. Those instances have the same structure.
An instance just keeps status of the instance. Instances just have data.
Therefore, it is insufficient to define its behavior. Therefore, it doesn't define object's behavior.
We need at least two things. We need at least two things.
One is functions and the other is class. One is functions and the other is class methods.
You've already seen many functions. You've already seen many functions.
For example, `tfe_text_view_new` is a function to create a TfeTextView instance. For example,
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.
Class comprises mainly pointers to functions. - `TfeTextView *tfe_text_view_new (void);` is a function to create a TfeTextView instance.
Those functions are used by the object itself or its descendant objects. - `GtkTextBuffer *gtk_text_view_get_buffer (GtkTextView *textview)` is a function to get a GtkTextBuffer from GtkTextView.
Functions are public, which means that they are expected to be used by other objects.
They are similar to public methods in object oriented languages.
Class (C structure) mainly consists of pointers to functions.
The functions are called class methods and used by the object itself or its descendant objects.
For example, GObject class is declared in `gobject.h` in GLib source files. For example, GObject class is declared in `gobject.h` in GLib source files.
~~~C ~~~C
@ -160,42 +187,51 @@ For example, GObject class is declared in `gobject.h` in GLib source files.
4 struct _GObjectClass 4 struct _GObjectClass
5 { 5 {
6 GTypeClass g_type_class; 6 GTypeClass g_type_class;
7 /*< private >*/ 7
8 GSList *construct_properties; 8 /*< private >*/
9 /*< public >*/ 9 GSList *construct_properties;
10 /* seldom overridden */ 10
11 GObject* (*constructor) (GType type, 11 /*< public >*/
12 guint n_construct_properties, 12 /* seldom overridden */
13 GObjectConstructParam *construct_properties); 13 GObject* (*constructor) (GType type,
14 /* overridable methods */ 14 guint n_construct_properties,
15 void (*set_property) (GObject *object, 15 GObjectConstructParam *construct_properties);
16 guint property_id, 16 /* overridable methods */
17 const GValue *value, 17 void (*set_property) (GObject *object,
18 GParamSpec *pspec); 18 guint property_id,
19 void (*get_property) (GObject *object, 19 const GValue *value,
20 guint property_id, 20 GParamSpec *pspec);
21 GValue *value, 21 void (*get_property) (GObject *object,
22 GParamSpec *pspec); 22 guint property_id,
23 void (*dispose) (GObject *object); 23 GValue *value,
24 void (*finalize) (GObject *object); 24 GParamSpec *pspec);
25 /* seldom overridden */ 25 void (*dispose) (GObject *object);
26 void (*dispatch_properties_changed) (GObject *object, 26 void (*finalize) (GObject *object);
27 guint n_pspecs, 27 /* seldom overridden */
28 GParamSpec **pspecs); 28 void (*dispatch_properties_changed) (GObject *object,
29 /* signals */ 29 guint n_pspecs,
30 void (*notify) (GObject *object, 30 GParamSpec **pspecs);
31 GParamSpec *pspec); 31 /* signals */
32 /* called when done constructing */ 32 void (*notify) (GObject *object,
33 void (*constructed) (GObject *object); 33 GParamSpec *pspec);
34 /*< private >*/ 34
35 gsize flags; 35 /* called when done constructing */
36 /* padding */ 36 void (*constructed) (GObject *object);
37 gpointer pdummy[6]; 37
38 }; 38 /*< private >*/
39 39 gsize flags;
40
41 gsize n_construct_properties;
42
43 gpointer pspecs;
44 gsize n_pspecs;
45
46 /* padding */
47 gpointer pdummy[3];
48 };
49
~~~ ~~~
I'd like to explain some of the members.
There's a pointer to the function `dispose` in line 23. There's a pointer to the function `dispose` in line 23.
~~~C ~~~C
@ -213,13 +249,13 @@ void (*finalize) (GObject *object);
Look at the declaration of `_GObjectClass` so that you would find that most of the members are pointers to functions. Look at the declaration of `_GObjectClass` so that you would find that most of the members are pointers to functions.
- 11: A function pointed by `constructor` is called when the instance is generated. It completes the initialization of the instance. - 13: A function pointed by `constructor` is called when the instance is generated. It completes the initialization of the instance.
- 23: A function pointed by `dispose` is called when the instance destructs itself. - 25: A function pointed by `dispose` is called when the instance destructs itself.
Destruction process is divided into two phases. Destruction process is divided into two phases.
The first one is called disposing. The first one is called disposing.
In this phase, the instance releases all the references to other instances. In this phase, the instance releases all the references to other instances.
The second phase is finalizing. The second phase is finalizing.
- 24: A function pointed by `finalize` finishes the destruction process. - 26: A function pointed by `finalize` finishes the destruction process.
- The other pointers point to functions which are called while the instance lives. - The other pointers point to functions which are called while the instance lives.
These functions are called class methods. These functions are called class methods.
@ -228,25 +264,14 @@ But not open to the objects which are not the descendants.
## TfeTextView class ## TfeTextView class
TfeTextView class is a structure and it includes all its ancestors' class in it. TfeTextView class is a structure and it includes all its ancestors' classes in it.
Therefore, classes have similar hierarchy to instances.
~~~C ~~~
typedef _TfeTextView TfeTextView; GObjectClass (GInitiallyUnownedClass) -- GtkWidgetClass -- GtkTextViewClass -- TfeTextViewClass
struct _TfeTextView {
GtkTextView parent;
GFile *file;
};
~~~ ~~~
TfeTextView structure has GtkTextView type as the first member. The following is extracted from the source codes (not exactly the same).
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.
GObject -- GInitiallyUnowned -- GtkWidget -- GtkTextView -- TfeTextView
The following is extracts from the source files (not exactly the same).
~~~C ~~~C
1 struct _GtkWidgetClass 1 struct _GtkWidgetClass
@ -353,8 +378,8 @@ The following is extracts from the source files (not exactly the same).
102 void (* toggle_overwrite) (GtkTextView *text_view); 102 void (* toggle_overwrite) (GtkTextView *text_view);
103 GtkTextBuffer * (* create_buffer) (GtkTextView *text_view); 103 GtkTextBuffer * (* create_buffer) (GtkTextView *text_view);
104 void (* snapshot_layer) (GtkTextView *text_view, 104 void (* snapshot_layer) (GtkTextView *text_view,
105 GtkTextViewLayer layer, 105 GtkTextViewLayer layer,
106 GtkSnapshot *snapshot); 106 GtkSnapshot *snapshot);
107 gboolean (* extend_selection) (GtkTextView *text_view, 107 gboolean (* extend_selection) (GtkTextView *text_view,
108 GtkTextExtendSelection granularity, 108 GtkTextExtendSelection granularity,
109 const GtkTextIter *location, 109 const GtkTextIter *location,
@ -376,11 +401,11 @@ The following is extracts from the source files (not exactly the same).
- 120-122: This three lines are generated by the macro `G_DECLARE_FINAL_TYPE`. - 120-122: This three lines are generated by the macro `G_DECLARE_FINAL_TYPE`.
So, they are not written in either `tfe_text_view.h` or `tfe_text_view.c`. So, they are not written in either `tfe_text_view.h` or `tfe_text_view.c`.
- 3, 84, 121: Each derived class puts its parent class at the first member of its structure. - 3, 84, 121: Each class has its parent class at the first member of its structure.
It is the same as instance structures. It is the same as instance structures.
- Class members in ancestors are open to the descendant class. - Class members in ancestors are open to the descendant class.
So, they can be changed in `tfe_text_view_class_init` function. So, they can be changed in `tfe_text_view_class_init` function.
For example, the `dispose` pointer in GObjectClass will be overridden later in `tfe_text_view_class_init`. For example, the `finalize` pointer in GObjectClass will be overridden later in `tfe_text_view_class_init`.
(Override is an object oriented programming terminology. (Override is an object oriented programming terminology.
Override is rewriting ancestors' class methods in the descendant class.) Override is rewriting ancestors' class methods in the descendant class.)
- Some class methods are often overridden. - Some class methods are often overridden.
@ -410,14 +435,12 @@ Then C destructs itself and finally the memories allocated to C is freed.
The idea above is based on an assumption that an object referred by nothing has reference count of zero. 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. When the reference count drops to zero, the object starts its destruction process.
The destruction process is split into two phases: disposing and finalizing. The destruction process is split into two phases: disposing and finalizing.
In the disposing process, the object invokes the function pointed by `dispose` in its class to release all references to other objects. In the disposing process, the object invokes the function pointed by `dispose` in its class to release all references to other instances.
In the finalizing process, it invokes the function pointed by `finalize` in its class to complete the destruction process. After that, it invokes the function pointed by `finalize` in its class to complete the destruction process.
These functions are also called handlers or methods.
For example, dispose handler or dispose method. For example, dispose handler or dispose method.
In the destruction process of TfeTextView, the reference count of widgets related to TfeTextView is automatically decreased. In the destruction process, TfeTextView needs to unref the GFile pointed by `tv->file`.
But GFile pointed by `tv->file` needs to decrease its reference count by one. You must write the dispose handler `tfe_text_view_dispose`.
You must write the code in the dispose handler `tfe_text_view_dispose`.
~~~C ~~~C
1 static void 1 static void
@ -438,7 +461,7 @@ In dispose handlers, we usually use `g_clear_object` rather than `g_object_unref
In the disposing process, the object uses the pointer in its class to call the handler. In the disposing process, the object uses the pointer in its class to call the handler.
Therefore, `tfe_text_view_dispose` needs to be registered in the class when the TfeTextView class is initialized. Therefore, `tfe_text_view_dispose` needs to be registered in the class when the TfeTextView class is initialized.
The function `tfe_text_view_class_init` is the class initialization function and it is declared in the replacement produced by `G_DEFINE_TYPE` macro. The function `tfe_text_view_class_init` is the class initialization function and it is declared in the `G_DEFINE_TYPE` macro expansion.
~~~C ~~~C
static void static void
@ -466,12 +489,11 @@ Then it invokes its parent's dispose handler in line 8.
G_OBJECT_CLASS (tfe_text_view_parent_class)->dispose (gobject); G_OBJECT_CLASS (tfe_text_view_parent_class)->dispose (gobject);
~~~ ~~~
`tfe_text_view_parent_class`,which is made by `G_DEFINE_TYPE` macro, is a pointer that points the parent object class. A variable `tfe_text_view_parent_class`, which is made by `G_DEFINE_TYPE` macro, is a pointer that points the parent object class.
The variable `gobject` is a pointer to TfeTextView instance which is casted as a GObject instance.
Therefore, `G_OBJECT_CLASS (tfe_text_view_parent_class)->dispose` points the handler `dh3` in the diagram above. Therefore, `G_OBJECT_CLASS (tfe_text_view_parent_class)->dispose` points the handler `dh3` in the diagram above.
And `gobject` is a pointer to TfeTextView instance which is casted as a GObject instance. The statement `G_OBJECT_CLASS (tfe_text_view_parent_class)->dispose (gobject)` is the same as `dh3 (gobject)`, which means it releases all the reference to the other instances in the GtkTextViewPrivate in the TfeTextView instance.
`dh3` releases all the references to objects in the GtkTextView part (it is actually the private area pointed by `prev`) in TfeTextView instance.
After that, `dh3` calls `dh2`, and `dh2` calls `dh1`. After that, `dh3` calls `dh2`, and `dh2` calls `dh1`.
Finally all the references are released. Finally all the references are released.
Up: [README.md](../README.md), Prev: [Section 10](sec10.md), Next: [Section 12](sec12.md)
Up: [Readme.md](../Readme.md), Prev: [Section 10](sec10.md), Next: [Section 12](sec12.md)

View file

@ -1,17 +1,17 @@
Up: [Readme.md](../Readme.md), Prev: [Section 11](sec11.md), Next: [Section 13](sec13.md) Up: [README.md](../README.md), Prev: [Section 11](sec11.md), Next: [Section 13](sec13.md)
# Signals # Signals
## Signals ## Signals
In Gtk programming, each object is encapsulated. Each object is encapsulated in Gtk programming.
And it is not recommended to use global variables because they tend to make the program complicated. And it is not recommended to use global variables because they are prone to make the program complicated.
So, we need something to communicate between objects. So, we need something to communicate between objects.
There are two ways to do so. There are two ways to do so.
- Functions. - Functions.
For example, `tb = gtk_text_view_get_buffer (tv)`. For example, `tb = gtk_text_view_get_buffer (tv)`.
The caller requests `tv` to give `tb`, which is a GtkTextBuffer instance connected to `tv` to the caller. The caller requests `tv` to give `tb`, which is a GtkTextBuffer instance connected to `tv`.
- Signals. - Signals.
For example, `activate` signal on GApplication object. For example, `activate` signal on GApplication object.
When the application is activated, the signal is emitted. When the application is activated, the signal is emitted.
@ -29,7 +29,7 @@ The registration is done usually when the object class is initialized.
2. Signals are connected to handlers by `g_connect_signal` or its family functions. 2. Signals are connected to handlers by `g_connect_signal` or its family functions.
The connection is usually done out of the object. The connection is usually done out of the object.
3. When Signals are emitted, the connected handlers are invoked. 3. When Signals are emitted, the connected handlers are invoked.
Signal is emitted on the instance of the object. Signals are emitted on the instance of the object.
## Signal registration ## Signal registration
@ -41,8 +41,7 @@ This signal is emitted when `tv->file` is changed.
`tfe_text_view_open` function is not able to return the status because it uses GtkFileChooserDialog. `tfe_text_view_open` function is not able to return the status because it uses GtkFileChooserDialog.
This signal is emitted instead of the return value of the function. This signal is emitted instead of the return value of the function.
A static variable or array is used to store the signal ID. A static variable or array is used to store signal ID.
A static array is used to register two or more signals.
~~~C ~~~C
enum { enum {
@ -88,10 +87,10 @@ Signals are registered in the class initialization function.
- 6-15: Registers "change-file" signal. - 6-15: Registers "change-file" signal.
`g_signal_new` function is used. `g_signal_new` function is used.
The signal "change-file" has no default handler (object method handler). The signal "change-file" has no default handler (object method handler) so the offset (the line 9) is set to zero.
You usually don't need to set a default handler. You usually don't need a default handler.
If you need it, use `g_signal_new_class_handler` function. If you need it, use `g_signal_new_class_handler` function instead of `g_signal_new`.
See [GObject API Reference, g\_signal\_new\_class\_handler](https://docs.gtk.org/gobject/func.signal_new_class_handler.html) for further information. See [GObject API Reference](https://docs.gtk.org/gobject/func.signal_new_class_handler.html) for further information.
- The return value of `g_signal_new` is the signal id. - The return value of `g_signal_new` is the signal id.
The type of signal id is guint, which is the same as unsigned int. The type of signal id is guint, which is the same as unsigned int.
It is used in the function `g_signal_emit`. It is used in the function `g_signal_emit`.
@ -185,4 +184,4 @@ g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_ER
The fourth parameter is the parameter. The fourth parameter is the parameter.
Up: [Readme.md](../Readme.md), Prev: [Section 11](sec11.md), Next: [Section 13](sec13.md) Up: [README.md](../README.md), Prev: [Section 11](sec11.md), Next: [Section 13](sec13.md)

View file

@ -2,21 +2,18 @@ Up: [README.md](../README.md), Prev: [Section 12](sec12.md), Next: [Section 14]
# Functions in TfeTextView # Functions in TfeTextView
In this section I will explain functions in TfeTextView object. TfeTextView functions are described in this section.
## tfe.h and tfetextview.h ## tfetextview.h
`tfe.h` is a top header file and it includes `gtk.h` and all the header files. The header file `tfetextview.h` provides:
C source files `tfeapplication.c` and `tfenotebook.c` include `tfe.h` at the beginning.
~~~C - The type of TfeTextView, which is `TFE_TYPE_TEXT_VIEW`.
1 #include <gtk/gtk.h> - The expansion of `G_DECLARE_FINAL_TYPE` includes some useful macros.
2 - Constants for the `open-response` signal is defined..
3 #include "../tfetextview/tfetextview.h" - Public functions of `tfetextview.c` are declared.
4 #include "tfenotebook.h"
~~~
`../tfetextview/tfetextview.h` is a header file which describes the public functions in `tfetextview.c`. Therefore, Any programs use TfeTextView needs to include `tfetextview.h`.
~~~C ~~~C
1 #ifndef __TFE_TEXT_VIEW_H__ 1 #ifndef __TFE_TEXT_VIEW_H__
@ -56,14 +53,20 @@ C source files `tfeapplication.c` and `tfenotebook.c` include `tfe.h` at the beg
35 #endif /* __TFE_TEXT_VIEW_H__ */ 35 #endif /* __TFE_TEXT_VIEW_H__ */
~~~ ~~~
- 1,2,35: Thanks to these three lines, the following lines are included only once. - 1,2,35: Thanks to these three lines, the following lines are included only once.
You can use `#pragma once` instead of them.
It is non-standard but widely used.
- 4: Includes gtk4 header files. - 4: Includes gtk4 header files.
The header file `gtk4` also has the same mechanism to avoid including it multiple times. The header file `gtk4` also has the same mechanism to avoid including it multiple times.
- 6-7: These two lines define TfeTextView type, its class structure and some useful macros. - 6-7: These two lines define TfeTextView type, its class structure and some useful macros.
- `TfeTextView` and `TfeTextViewClass` are declared as typedef of C structures.
- You need to define a structure `_TfeTextView` later.
- The class structure `_TfeTextViewClass` is defined here. You don't need to define it by yourself.
- Convenience functions `TFE_TEXT_VIEW ()` for casting and `TFE_IS_TEXT_VIEW` for type check are defined.
- 9-15: A definition of the value of the parameter of "open-response" signal. - 9-15: A definition of the value of the parameter of "open-response" signal.
- 17-33: Declarations of public functions on TfeTextView. - 17-33: Declarations of public functions on TfeTextView.
## Functions to create TfeTextView instances ## Instance creation Functions
A TfeTextView instance is created with `tfe_text_view_new` or `tfe_text_view_new_with_file`. A TfeTextView instance is created with `tfe_text_view_new` or `tfe_text_view_new_with_file`.
@ -107,16 +110,19 @@ Each function is defined as follows.
22 22
23 GtkWidget * 23 GtkWidget *
24 tfe_text_view_new (void) { 24 tfe_text_view_new (void) {
25 return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL)); 25 return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, "wrap-mode", GTK_WRAP_WORD_CHAR, NULL));
26 } 26 }
~~~ ~~~
- 23-25: `tfe_text_view_new` function. - 23-25: `tfe_text_view_new` function.
Just returns the value from the function `g_object_new` but casts it to the pointer to GtkWidget. Just returns the value from the function `g_object_new` but casts it to the pointer to GtkWidget.
Initialization is done in `tfe_text_view_init` which is called in the process of `g_object_new` function. The function `g_object_new` creates any instances of its descendant class.
The arguments are the type of the class, property list and NULL.
Null is the end mark of the property list.
TfeTextView "wrap-mode" property has GTK\_WRAP\_WORD\_CHAR as the default value.
- 1-21: `tfe_text_view_new_with_file` function. - 1-21: `tfe_text_view_new_with_file` function.
- 3: `g_return_val_if_fail` is described in [GLib API Reference, g\_return\_val\_if\_fail](https://docs.gtk.org/glib/func.return_val_if_fail.html). - 3: `g_return_val_if_fail` is described in [GLib API Reference -- g\_return\_val\_if\_fail](https://docs.gtk.org/glib/func.return_val_if_fail.html).
And also [GLib API Reference, Message Logging](https://docs.gtk.org/glib/logging.html). And also [GLib API Reference -- Message Logging](https://docs.gtk.org/glib/logging.html).
It tests whether the argument `file` is a pointer to GFile. It tests whether the argument `file` is a pointer to GFile.
If it's true, then the program goes on to the next line. If it's true, then the program goes on to the next line.
If it's false, then it returns NULL (the second argument) immediately. If it's false, then it returns NULL (the second argument) immediately.
@ -141,7 +147,7 @@ If the modification flag is FALSE, it doesn't need to save the contents.
- 20: Returns `tv`, which is a pointer to the newly created TfeTextView instance. - 20: Returns `tv`, which is a pointer to the newly created TfeTextView instance.
If an error happens, NULL is returned. If an error happens, NULL is returned.
## Save and saveas functions ## Save related functions
Save and saveas functions write the contents in the GtkTextBuffer to a file. Save and saveas functions write the contents in the GtkTextBuffer to a file.
@ -162,143 +168,196 @@ Then, the function changes `tv->file` and save the contents to the specified fil
If an error occurs, it is shown to the user through the message dialog. If an error occurs, it is shown to the user through the message dialog.
The error is managed only in the TfeTextView and no information is notified to the caller. The error is managed only in the TfeTextView and no information is notified to the caller.
### save\_file function
~~~C ~~~C
1 static gboolean 1 static gboolean
2 save_file (GFile *file, GtkTextBuffer *tb, GtkWindow *win) { 2 save_file (GFile *file, GtkTextBuffer *tb, GtkWindow *win) {
3 GtkTextIter start_iter; 3 GtkTextIter start_iter;
4 GtkTextIter end_iter; 4 GtkTextIter end_iter;
5 gchar *contents; 5 char *contents;
6 gboolean stat; 6 gboolean stat;
7 GtkWidget *message_dialog; 7 GtkWidget *message_dialog;
8 GError *err = NULL; 8 GError *err = NULL;
9 9
10 gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter); 10 gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter);
11 contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE); 11 contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE);
12 if (g_file_replace_contents (file, contents, strlen (contents), NULL, TRUE, G_FILE_CREATE_NONE, NULL, NULL, &err)) { 12 stat = g_file_replace_contents (file, contents, strlen (contents), NULL, TRUE, G_FILE_CREATE_NONE, NULL, NULL, &err);
13 gtk_text_buffer_set_modified (tb, FALSE); 13 if (stat)
14 stat = TRUE; 14 gtk_text_buffer_set_modified (tb, FALSE);
15 } else { 15 else {
16 message_dialog = gtk_message_dialog_new (win, GTK_DIALOG_MODAL, 16 // Because error message is displayed here, the caller of 'save_file' doesn't need to do anything about error.
17 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, 17 message_dialog = gtk_message_dialog_new (win, GTK_DIALOG_MODAL,
18 "%s.\n", err->message); 18 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s.", err->message);
19 g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL); 19 g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
20 gtk_widget_show (message_dialog); 20 gtk_widget_show (message_dialog);
21 g_error_free (err); 21 g_error_free (err);
22 stat = FALSE; 22 }
23 } 23 g_free (contents);
24 g_free (contents); 24 return stat;
25 return stat; 25 }
26 }
27
28 static void
29 saveas_dialog_response (GtkWidget *dialog, gint response, TfeTextView *tv) {
30 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
31 GFile *file;
32 GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW);
33
34 if (response == GTK_RESPONSE_ACCEPT) {
35 file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
36 if (! G_IS_FILE (file))
37 g_warning ("TfeTextView: gtk_file_chooser_get_file returns non GFile.\n");
38 else if (save_file(file, tb, GTK_WINDOW (win))) {
39 if (G_IS_FILE (tv->file))
40 g_object_unref (tv->file);
41 tv->file = file;
42 g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
43 } else
44 g_object_unref (file);
45 }
46 gtk_window_destroy (GTK_WINDOW (dialog));
47 }
48
49 void
50 tfe_text_view_save (TfeTextView *tv) {
51 g_return_if_fail (TFE_IS_TEXT_VIEW (tv));
52
53 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
54 GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW);
55
56 if (! gtk_text_buffer_get_modified (tb))
57 return; /* no need to save it */
58 else if (tv->file == NULL)
59 tfe_text_view_saveas (tv);
60 else if (! G_IS_FILE (tv->file))
61 g_error ("TfeTextView: The pointer tv->file isn't NULL nor GFile.\n");
62 else
63 save_file (tv->file, tb, GTK_WINDOW (win));
64 }
65
66 void
67 tfe_text_view_saveas (TfeTextView *tv) {
68 g_return_if_fail (TFE_IS_TEXT_VIEW (tv));
69
70 GtkWidget *dialog;
71 GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW);
72
73 dialog = gtk_file_chooser_dialog_new ("Save file", GTK_WINDOW (win), GTK_FILE_CHOOSER_ACTION_SAVE,
74 "Cancel", GTK_RESPONSE_CANCEL,
75 "Save", GTK_RESPONSE_ACCEPT,
76 NULL);
77 g_signal_connect (dialog, "response", G_CALLBACK (saveas_dialog_response), tv);
78 gtk_widget_show (dialog);
79 }
~~~ ~~~
- 1-26: `save_file` function. - The function `save_file` is called from `saveas_dialog_response` and `tfe_text_view_save`.
This function is called from `saveas_dialog_response` and `tfe_text_view_save`.
This function saves the contents of the buffer to the file given as an argument. This function saves the contents of the buffer to the file given as an argument.
If error happens, it displays an error message. If error happens, it displays an error message.
So, a caller of this function don't need to take care of errors.
The class of this function is `static`. The class of this function is `static`.
Therefore, only functions in this file (`tfeTetview.c`) call this function. Therefore, only functions in this file (`tfetextview.c`) call this function.
Such static functions usally don't have `g_return_val_if_fail` function. Such static functions usually don't have `g_return_val_if_fail` function.
- 10-11: Gets the text contents from the buffer. - 10-11: Gets the text contents from the buffer.
- 12-14: Saves the contents to the file. - 12: The function `g_file_replace_contents` writes the contents to the file and returns the status (true = success/ false = fail).
If no error happens, set the modified flag to be FALSE. It has many parameters, but some of them are almost always given the same values.
- GFile* file: GFile to which the contents are saved.
- const char* contents: contents to be saved. The string is owned by the caller.
- gsize length: the length of the contents
- const char* etag: entity tag. It is usually NULL.
- gboolean make_backup: true to make a backup if the file exists. false not to make it. the file will be overwritten.
- GFileCreateFlags flags: usually `G_FILE_CREATE_NONE` is fine.
- char** new_etag: new entity tag. It is usually NULL.
- GCancellable* cancellable: If a cancellable instance is set, the other thread can cancel this operation. it is usually NULL.
- GError** error: If error happens, GError will be set.
- 13,14: If no error happens, set the modified flag to be FALSE.
This means that the buffer is not modified since it has been saved. This means that the buffer is not modified since it has been saved.
And set the return status `stat` to be TRUE. - 15-22: If it fails to save the contents, an error message will be displayed.
- 15-23: If it fails to save the contents, displays an error message. - 17-18: Creates a message dialog. The parameters are:
- 16-18: Creates a message dialog with the error message. - GtkWindow* parent: transient parent window.
- 19: Connects the "response" signal to `gtk_window_destroy`, so that the dialog disappears when a user clicked on the button. This allows window managers to keep the dialog on top of the parent window, or center the dialog over the parent window.
- 20-21: Shows the window, frees `err` and set `stat` to be FLASE. It is possible to give no parent window to the dialog.
- 24: Frees `contents`. However, it is encouraged to give parents to dialogs.
- 25: Returns to the caller. - GtkDialogFlags flags: GTK\_DIALOG\_MODAL for modal dialog. A modal dialog is usually fine.
- 28-47: `saveas_dialog_response` function. - GtkMessageType type: GTK\_MESSAGE\_ERROR for error message. Other options are GTK\_MESSAGE\_INFO, GTK\_MESSAGE\_WARNING and so on.
This is a signal handler for the "response" signal on GtkFileChooserDialog instance created by `tfe_text_view_saveas` function. - GtkButtonsType buttons: GTK\_BUTTON\_OK is used most often. Other option is GTK\_BUTTON\_YES\_NO, GTK\_BUTTON\_CANCEL and so on.
This handler analyzes the response and determines whether to save the contents. - const gchar* message_format: gchar is the same as char. This format is the same as printf. Arguments for the message format follow.
- 34-45: If the response is `GTK_RESPONSE_ACCEPT`, the user has clicked on the `Save` button. So, it tries to save. - 19: Connects the "response" signal to `gtk_window_destroy`, so that the dialog disappears when the user clicks on the button.
- 35: Gets the GFile `file` from GtkFileChooserDialog. - 20: Shows the message dialog.
- 36-37: If it doesn't point GFile, it outputs an error message to the log. - 21: Frees `err` with `g_error_free` function.
- 38: Otherwise, it calls `save_file` to save the contents to the file. - 23: Frees `contents`.
- 39-42: If `save_file` has successfully saved the contents, `tv->file` is updated. - 24: Returns to the caller.
If the old GFile pointed by `tv->file` exists, it is freed in advance.
Emits "change-file" signal. ### saveas\_dialog\_response function
- 44: Unrefs `file`.
- 46: destroys the file chooser dialog. ~~~C
- 49-64: `tfe_text_view_save` function. 1 static void
- 51: `tfe_text_view_save` is public, i.e. it is open to the other files. 2 saveas_dialog_response (GtkWidget *dialog, gint response, TfeTextView *tv) {
3 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
4 GFile *file;
5 GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW);
6
7 if (response == GTK_RESPONSE_ACCEPT) {
8 file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
9 if (! G_IS_FILE (file))
10 g_warning ("TfeTextView: gtk_file_chooser_get_file returns non GFile.\n");
11 else if (save_file(file, tb, GTK_WINDOW (win))) {
12 // The following is complicated. The comments here will help your understanding
13 // G_IS_FILE(tv->file) && tv->file == file => nothing to do
14 // G_IS_FILE(tv->file) && tv->file != file => unref(tv->file), tv->file=file, signal emit
15 // tv->file==NULL => tv->file=file, signal emit
16 if (G_IS_FILE (tv->file) && (! g_file_equal (tv->file, file)))
17 g_object_unref (tv->file);
18 if (! (G_IS_FILE (tv->file) && g_file_equal (tv->file, file))) {
19 tv->file = file; // The ownership of 'file' moves to TfeTextView.
20 g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
21 }
22 g_object_unref (file);
23 } else
24 g_object_unref (file);
25 }
26 gtk_window_destroy (GTK_WINDOW (dialog));
27 }
~~~
- The function `saveas_dialog_response` is a signal handler for the "response" signal on GtkFileChooserDialog.
This handler analyzes the response and determines whether to save the contents or not.
- 7-25: If the response is `GTK_RESPONSE_ACCEPT`, the user has clicked on the `Save` button and the contents will be saved.
- 8: Gets the GFile `file` from the GtkFileChooserDialog.
- 9-10: If it doesn't point GFile, a warning message will be output to the log. This is not expected.
- 11: Otherwise, it calls `save_file` to save the contents to the file.
- 12-22: If `save_file` has successfully saved the contents, the following will be done.
- If `tv->file` is GFile and `file` is a different file, unref `tv->file`.
- If `tv->file` is GFile and `file` points the same file as `tv->file`, nothing needs to do.
Otherwise, `tv->file` is set to `file` and "change-file" signal is emitted.
- 22, 24: Unref `file`.
- 26: destroys the file chooser dialog.
### tfe\_text\_view\_save function
~~~C
1 void
2 tfe_text_view_save (TfeTextView *tv) {
3 g_return_if_fail (TFE_IS_TEXT_VIEW (tv));
4
5 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
6 GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW);
7
8 if (! gtk_text_buffer_get_modified (tb))
9 return; /* no need to save it */
10 else if (tv->file == NULL)
11 tfe_text_view_saveas (tv);
12 else if (! G_IS_FILE (tv->file)) // Unexpected error
13 g_error ("TfeTextView: The pointer tv->file isn't NULL nor GFile.\n");
14 else
15 save_file (tv->file, tb, GTK_WINDOW (win));
16 }
~~~
- The function `tfe_text_view_save` writes the contents to the `tv->file` file.
It calls `tfe_text_view_saveas` or `save_file`.
- 1-3: The function is public, i.e. it is open to the other objects.
So, it doesn't have `static` class. So, it doesn't have `static` class.
Public functions should check the parameter type with `g_return_if_fail` function. Public functions should check the parameter type with `g_return_if_fail` function.
If `tv` is not a pointer to a TfeTextView instance, then it logs an error message and immediately returns. If `tv` is not a pointer to a TfeTextView instance, then it logs an error message and immediately returns.
This function is similar to `g_return_val_if_fail`, but no value is returned because `tfe_text_view_save` doesn't return a value. This function is similar to `g_return_val_if_fail`, but no value is returned because `tfe_text_view_save` doesn't return a value (void).
- 53-54: Gets GtkTextBuffer instance and GtkWidget instance and assignes them to `tb` and`win` respectively. - 5-6: GtkTextBuffer `tb` and GtkWidget (GtkWindow) `win` are set.
- 56-57: If the buffer hasn't modified, then it doesn't need to save it. The function `gtk_widget_get_ancestor (widget, type)` returns the first ancestor of the widget with type.
So the function returns. The type is a GType.
- 58-59: If `tv->file` is NULL, no file has given yet. For example, the type of GtkWindow is `GTK_TYPE_WINDOW` and the one of TfeTextView is `TFE_TYPE_TEXT_VIEW`.
It calls `tfe_text_view_saveas` which prompts a user to select a file or specify a new file to save. Be careful. The parent-child relationship here is the one for widgets, not classes.
- 60-61: If `tv->file` doesn't point GFile, somethig bad has happened. The top level window may be a GtkApplicationWindow, but it depends on the application.
Logs an error message. Because TfeTextView is a library, it can't determine the top level window type (GtkWindow or GtkApplicationWindow).
- 62-63: Calls `save_file` to save the contents to the file. GtkWindow is a parent class of GtkApplication window so it is more general.
- 66-79: `tfe_text_view_saveas` function. Therefore, TfeTextView takes GtkWindow as a top level window so that it can be used by any application.
It shows GtkFileChooserDialog and prompts the user to choose a file. - 8-9: If the buffer hasn't modified, it doesn't need to be saved.
- 73-76: Creates GtkFileChooserDialog. - 10-11: If `tv->file` is NULL, which means no file has given yet, it calls `tfe_text_view_saveas` to prompt a user to select a file and save the contents.
The title is "Save file". - 12-13: If `tv->file` doesn't point GFile, an error message is logged out. It is not expected.
Transient parent of the dialog is `win`, which is the top-level window. - 14-15: Otherwise, it calls `save_file` to save the contents to the file `tv->file`.
The action is save mode.
The buttons are Cancel and Save.
- 77: connects the "response" signal of the dialog and `saveas_dialog_response` handler.
- 78: Shows the dialog.
![Saveas process](../image/saveas.png) ### tfe\_text\_view\_saveas function
~~~C
1 void
2 tfe_text_view_saveas (TfeTextView *tv) {
3 g_return_if_fail (TFE_IS_TEXT_VIEW (tv));
4
5 GtkWidget *dialog;
6 GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW);
7
8 dialog = gtk_file_chooser_dialog_new ("Save file", GTK_WINDOW (win), GTK_FILE_CHOOSER_ACTION_SAVE,
9 "Cancel", GTK_RESPONSE_CANCEL,
10 "Save", GTK_RESPONSE_ACCEPT,
11 NULL);
12 g_signal_connect (dialog, "response", G_CALLBACK (saveas_dialog_response), tv);
13 gtk_widget_show (dialog);
14 }
~~~
- The function `tfe_text_view_saveas` shows GtkFileChooserDialog and prompts the user to choose a file and save the contents.
- 1-3: Check the type of `tv` because the caller may be other object. This function is public.
- 6: GtkWidget `win` is set to the top level window.
- 8-11: Creates GtkFileChooserDialog. It has at least four parameters.
- const char* title: title of the dialog shown at the bar.
- GtkWindow* parent: transient parent window.
- GtkFileChooserAction action: action is one of `GTK_FILE_CHOOSER_ACTION_OPEN`, `GTK_FILE_CHOOSER_ACTION_SAVE`
and `GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER`.
When you want to save a file, `GTK_FILE_CHOOSER_ACTION_SAVE` is the action.
- const char* first_button_text: the label text of the first button.
- type: response ID for the sirst button. response ID is one of the GtkReponseType.
See [GtkResponseType](https://docs.gtk.org/gtk4/enum.ResponseType.html).
- followed by pairs of button text and response ID...
- NULL is put at the end of the list.
- The GtkFileChooserDialog will have the title "Save file", transient parent `win`, save mode action, cancel and save button.
- 12: connects the "response" signal and `saveas_dialog_response` handler.
- 13: Shows the dialog.
When you use GtkFileChooserDialog, you need to divide the program into two parts. When you use GtkFileChooserDialog, you need to divide the program into two parts.
One is a function which creates GtkFileChooserDialog and the other is a signal handler. One is a function which creates GtkFileChooserDialog and the other is a signal handler.
@ -306,9 +365,11 @@ The function just creates and shows GtkFileChooserDialog.
The rest is done by the handler. The rest is done by the handler.
It gets Gfile from GtkFileChooserDialog and saves the buffer to the file by calling `save_file`. It gets Gfile from GtkFileChooserDialog and saves the buffer to the file by calling `save_file`.
## Open function ![Saveas process](../image/saveas.png)
Open function shows GtkFileChooserDialog to users and prompts them to choose a file. ## Open related functions
Open function shows GtkFileChooserDialog to a user and prompts them to choose a file.
Then it reads the file and puts the text into GtkTextBuffer. Then it reads the file and puts the text into GtkTextBuffer.
~~~C ~~~C
@ -317,10 +378,8 @@ void tfe_text_view_open (TfeTextView *tv, GtkWindow *win);
The parameter `win` is the top-level window. The parameter `win` is the top-level window.
It will be a transient parent window of GtkFileChooserDialog when the dialog is created. It will be a transient parent window of GtkFileChooserDialog when the dialog is created.
This allows window managers to keep the dialog on top of the parent window, or center the dialog over the parent window.
It is possible to give no parent window to the dialog. This function may be called just after `tv` has been created.
However, it is encouraged to give a parent window to dialog.
This function might be called just after `tv` has been created.
In that case, `tv` has not been incorporated into the widget hierarchy. In that case, `tv` has not been incorporated into the widget hierarchy.
Therefore it is impossible to get the top-level window from `tv`. Therefore it is impossible to get the top-level window from `tv`.
That's why the function needs `win` parameter. That's why the function needs `win` parameter.
@ -328,7 +387,12 @@ That's why the function needs `win` parameter.
This function is usually called when the buffer of `tv` is empty. This function is usually called when the buffer of `tv` is empty.
However, even if the buffer is not empty, `tfe_text_view_open` doesn't treat it as an error. However, even if the buffer is not empty, `tfe_text_view_open` doesn't treat it as an error.
If you want to revert the buffer, calling this function is appropriate. If you want to revert the buffer, calling this function is appropriate.
Otherwise probably bad things will happen.
Open and read process is divided into two phases.
One is showing GtkFileChooserDialog and the other is its response handler.
The response handler gets the filename, reads the contents of the file and puts it into the GtkTextBuffer.
### open\_dialog\_response function
~~~C ~~~C
1 static void 1 static void
@ -348,67 +412,85 @@ Otherwise probably bad things will happen.
15 } else if (! g_file_load_contents (file, NULL, &contents, &length, NULL, &err)) { /* read error */ 15 } else if (! g_file_load_contents (file, NULL, &contents, &length, NULL, &err)) { /* read error */
16 g_object_unref (file); 16 g_object_unref (file);
17 message_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog), GTK_DIALOG_MODAL, 17 message_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog), GTK_DIALOG_MODAL,
18 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, 18 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s", err->message);
19 "%s.\n", err->message); 19 g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
20 g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL); 20 gtk_widget_show (message_dialog);
21 gtk_widget_show (message_dialog); 21 g_error_free (err);
22 g_error_free (err); 22 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_ERROR);
23 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_ERROR); 23 } else {
24 } else { 24 gtk_text_buffer_set_text (tb, contents, length);
25 gtk_text_buffer_set_text (tb, contents, length); 25 g_free (contents);
26 g_free (contents); 26 gtk_text_buffer_set_modified (tb, FALSE);
27 if (G_IS_FILE (tv->file)) 27 // G_IS_FILE(tv->file) && tv->file == file => unref(tv->file), tv->file=file, emit response with SUCCESS
28 g_object_unref (tv->file); 28 // G_IS_FILE(tv->file) && tv->file != file => unref(tv->file), tv->file=file, emit response with SUCCESS, emit change-file
29 tv->file = file; 29 // tv->file==NULL => tv->file=file, emit response with SUCCESS, emit change-file
30 gtk_text_buffer_set_modified (tb, FALSE); 30 // The order is important. If you unref tv->file first, you can't compare tv->file and file anymore.
31 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_SUCCESS); 31 // And open-response signal is emitted after new tv->file is set. Or the handler can't catch the new file.
32 g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0); 32 if (! (G_IS_FILE (tv->file) && g_file_equal (tv->file, file)))
33 } 33 g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
34 gtk_window_destroy (GTK_WINDOW (dialog)); 34 if (G_IS_FILE (tv->file))
35 } 35 g_object_unref (tv->file);
36 36 tv->file = file; // The ownership of 'file' moves to TfeTextView
37 void 37 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_SUCCESS);
38 tfe_text_view_open (TfeTextView *tv, GtkWindow *win) { 38 }
39 g_return_if_fail (TFE_IS_TEXT_VIEW (tv)); 39 gtk_window_destroy (GTK_WINDOW (dialog));
40 g_return_if_fail (GTK_IS_WINDOW (win)); 40 }
41
42 GtkWidget *dialog;
43
44 dialog = gtk_file_chooser_dialog_new ("Open file", win, GTK_FILE_CHOOSER_ACTION_OPEN,
45 "Cancel", GTK_RESPONSE_CANCEL,
46 "Open", GTK_RESPONSE_ACCEPT,
47 NULL);
48 g_signal_connect (dialog, "response", G_CALLBACK (open_dialog_response), tv);
49 gtk_widget_show (dialog);
50 }
~~~ ~~~
- 37-50: `tfe_text_view_open` function. - 2: The handler `open_dialog_response` has three parameters.
- 44-47: Creates GtkFileChooserDialog. - GtkWidget *dialog: FileChooserDialog on which the "response" signal is emitted.
The title is "Open file". - gint response: response ID
Transient parent window is the top-level window of the application, which is given by the caller. - TfeTextView *tv: textview to put the contents to
The action is open mode. - 10-11: If the response is not `GTK_RESPONSE_ACCEPT`, the user has clicked on the "Cancel" button or close button on the header bar.
The buttons are Cancel and Open.
- 48: connects the "response" signal of the dialog and `open_dialog_response` signal handler.
- 49: Shows the dialog.
- 1-35: `open_dialog_response` signal handler.
- 10-11: If the response from GtkFileChooserDialog is not `GTK_RESPONSE_ACCEPT`, the user has clicked on the "Cancel" button or close button on the header bar.
Then, "open-response" signal is emitted. Then, "open-response" signal is emitted.
The parameter of the signal is `TFE_OPEN_RESPONSE_CANCEL`. The parameter of the signal is `TFE_OPEN_RESPONSE_CANCEL`.
- 12-14: Gets the pointer to the Gfile by `gtk_file_chooser_get_file`. - 12-14: Gets the pointer to the GFile by `gtk_file_chooser_get_file`.
If it doesn't point GFile, maybe an error has occurred. If it doesn't point GFile, maybe an error has occurred.
It is not expected, though.
Then it emits "open-response" signal with the parameter `TFE_OPEN_RESPONSE_ERROR`. Then it emits "open-response" signal with the parameter `TFE_OPEN_RESPONSE_ERROR`.
- 15-23: If an error occurs at file reading, then it decreases the reference count of the Gfile, shows a message dialog to report the error to the user and emits "open-response" signal with the parameter `TFE_OPEN_RESPONSE_ERROR`. - 15-22: If an error occurs when file reading, then it decreases the reference count of the GFile, shows a message dialog to report the error to the user and emits "open-response" signal with the parameter `TFE_OPEN_RESPONSE_ERROR`.
- 24-33: If the file has successfully been read, then the text is inserted to GtkTextBuffer, frees the temporary buffer pointed by `contents` and sets `tv->file` to point the file (no duplication is not necessary). - 23-38: If the file has been successfully read, the following is carried out. (It is not simple.)
Then, it emits "open-response" signal with the parameter `TFE_OPEN_RESPONSE_SUCCESS` and emits "change-file" signal. - the text is inserted to GtkTextBuffer
- 34: destroys GtkFileCooserDialog. - the temporary buffer `contents` is freed
- modify-bit is set to FALSE
- open-response signal is emitted with `TFE_OPEN_REPONSE_SUCCESS`
- If the file are changed, change-file signal is emitted
- If `tv->file` isn't NULL, `g_object_unref(tv->file)` is called
- `tv->file` is assigned with `file`
- 39: destroys GtkFileChooserDialog.
Now let's think about the whole process between the caller and TfeTextView. ### tfe\_text\_view\_open function
It is shown in the following diagram and you would think that it is really complicated.
~~~C
1 void
2 tfe_text_view_open (TfeTextView *tv, GtkWindow *win) {
3 g_return_if_fail (TFE_IS_TEXT_VIEW (tv));
4 // 'win' is used for a transient window of the GtkFileChooserDialog.
5 // It can be NULL.
6 g_return_if_fail (GTK_IS_WINDOW (win) || win == NULL);
7
8 GtkWidget *dialog;
9
10 dialog = gtk_file_chooser_dialog_new ("Open file", win, GTK_FILE_CHOOSER_ACTION_OPEN,
11 "Cancel", GTK_RESPONSE_CANCEL, "Open", GTK_RESPONSE_ACCEPT, NULL);
12 g_signal_connect (dialog, "response", G_CALLBACK (open_dialog_response), tv);
13 gtk_widget_show (dialog);
14 }
~~~
- 3-4: Check the type of the arguments `tv` and `win`.
Public functions always need to check the arguments.
- 10-11: Creates GtkFileChooserDialog.
- The title is "Open file".
- Transient parent window is the top-level window `win`.
- The action is open mode.
- The buttons are Cancel and Open.
- 12: connects the "response" signal and the handler.
- 13: Shows the dialog.
The whole process between the caller and TfeTextView is shown in the following diagram.
It is really complicated.
Because signal is the only way for GtkFileChooserDialog to communicate with others. Because signal is the only way for GtkFileChooserDialog to communicate with others.
In GTK 3, `gtk_dialog_run` function is available.
It simplifies the process.
However, in GTK 4, `gtk_dialog_run` is unavailable any more.
![Caller and TfeTextView](../image/open.png) ![Caller and TfeTextView](../image/open.png)
@ -419,9 +501,10 @@ However, in GTK 4, `gtk_dialog_run` is unavailable any more.
5. The handler reads the file and inserts the text into GtkTextBuffer and emits a signal to inform the status as a response code. 5. The handler reads the file and inserts the text into GtkTextBuffer and emits a signal to inform the status as a response code.
6. The handler out of the TfeTextView receives the signal. 6. The handler out of the TfeTextView receives the signal.
## Getting Gfile ## Getting GFile in TfeTextView
`gtk_text_view_get_file` is a simple function shown as follows. You can get the GFile in a TfeTextView instance with `tfe_text_view_get_file`.
It is very simple.
~~~C ~~~C
1 GFile * 1 GFile *
@ -439,15 +522,14 @@ The important thing is to duplicate `tv->file`.
Otherwise, if the caller frees the GFile object, `tv->file` is no more guaranteed to point the GFile. Otherwise, if the caller frees the GFile object, `tv->file` is no more guaranteed to point the GFile.
Another reason to use `g_file_dup` is that GFile isn't thread-safe. Another reason to use `g_file_dup` is that GFile isn't thread-safe.
If you use GFile in the different thread, the duplication is necessary. If you use GFile in the different thread, the duplication is necessary.
See [Gio API Reference, g\_file\_dup](https://docs.gtk.org/gio/method.File.dup.html). See [Gio API Reference -- g\_file\_dup](https://docs.gtk.org/gio/method.File.dup.html).
## The API document and source file of tfetextview.c ## The API document and source file of tfetextview.c
Refer [API document of TfeTextView](tfetextview_doc.md). Refer [API document of TfeTextView](tfetextview_doc.md).
It is under the directory `src/tfetextview`. It is under the directory `src/tfetextview`.
All the source files are listed in [Section 16](sec16.md). You can find all the TfeTextView source codes under [src/tfetextview](../src/tfetextview) directories.
You can find them under [src/tfe5](../src/tfe5) and [src/tfetextview](../src/tfetextview) directories.
Up: [README.md](../README.md), Prev: [Section 12](sec12.md), Next: [Section 14](sec14.md) Up: [README.md](../README.md), Prev: [Section 12](sec12.md), Next: [Section 14](sec14.md)

View file

@ -1,4 +1,4 @@
Up: [Readme.md](../Readme.md), Prev: [Section 13](sec13.md), Next: [Section 15](sec15.md) Up: [README.md](../README.md), Prev: [Section 13](sec13.md), Next: [Section 15](sec15.md)
# Functions in GtkNotebook # Functions in GtkNotebook
@ -32,7 +32,7 @@ This header file describes the public functions in `tfenotebook.c`.
If the name is `untitled` or `untitled` followed by digits, FileChooserDialog appears and a user can choose or specify a filename. If the name is `untitled` or `untitled` followed by digits, FileChooserDialog appears and a user can choose or specify a filename.
- 4-5: `notebook_page_close` closes the current page. - 4-5: `notebook_page_close` closes the current page.
- 7-8: `notebook_page_open` shows a file chooser dialog and a user can choose a file. The file is inserted to a new page. - 7-8: `notebook_page_open` shows a file chooser dialog and a user can choose a file. The file is inserted to a new page.
- 10-11: `notebook_page_new_with_file` creates a new page and the file given as an argument is read and inserted into the page. - 10-11: `notebook_page_new_with_file` creates a new page and a file given as an argument is read and inserted into the page.
- 13-14: `notebook_page_new` creates a new empty page. - 13-14: `notebook_page_new` creates a new empty page.
You probably find that the functions except `notebook_page_close` are higher level functions of You probably find that the functions except `notebook_page_close` are higher level functions of
@ -53,7 +53,7 @@ Now let's look at the program of each function.
## notebook\_page\_new ## notebook\_page\_new
~~~C ~~~C
1 static gchar* 1 static char*
2 get_untitled () { 2 get_untitled () {
3 static int c = -1; 3 static int c = -1;
4 if (++c == 0) 4 if (++c == 0)
@ -63,59 +63,62 @@ Now let's look at the program of each function.
8 } 8 }
9 9
10 static void 10 static void
11 notebook_page_build (GtkNotebook *nb, GtkWidget *tv, char *filename) { 11 notebook_page_build (GtkNotebook *nb, GtkWidget *tv, const char *filename) {
12 GtkWidget *scr = gtk_scrolled_window_new (); 12 GtkWidget *scr = gtk_scrolled_window_new ();
13 GtkNotebookPage *nbp; 13 GtkNotebookPage *nbp;
14 GtkWidget *lab; 14 GtkWidget *lab;
15 int i; 15 int i;
16 16
17 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR); 17 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);
18 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv); 18 lab = gtk_label_new (filename);
19 lab = gtk_label_new (filename); 19 i = gtk_notebook_append_page (nb, scr, lab);
20 i = gtk_notebook_append_page (nb, scr, lab); 20 nbp = gtk_notebook_get_page (nb, scr);
21 nbp = gtk_notebook_get_page (nb, scr); 21 g_object_set (nbp, "tab-expand", TRUE, NULL);
22 g_object_set (nbp, "tab-expand", TRUE, NULL); 22 gtk_notebook_set_current_page (nb, i);
23 gtk_notebook_set_current_page (nb, i); 23 g_signal_connect (GTK_TEXT_VIEW (tv), "change-file", G_CALLBACK (file_changed_cb), nb);
24 g_signal_connect (GTK_TEXT_VIEW (tv), "change-file", G_CALLBACK (file_changed_cb), nb); 24 }
25 } 25
26 26 void
27 void 27 notebook_page_new (GtkNotebook *nb) {
28 notebook_page_new (GtkNotebook *nb) { 28 g_return_if_fail(GTK_IS_NOTEBOOK (nb));
29 g_return_if_fail(GTK_IS_NOTEBOOK (nb)); 29
30 30 GtkWidget *tv;
31 GtkWidget *tv; 31 char *filename;
32 char *filename; 32
33 33 if ((tv = tfe_text_view_new ()) == NULL)
34 if ((tv = tfe_text_view_new ()) == NULL) 34 return;
35 return; 35 filename = get_untitled ();
36 filename = get_untitled (); 36 notebook_page_build (nb, tv, filename);
37 notebook_page_build (nb, tv, filename); 37 g_free (filename);
38 } 38 }
~~~ ~~~
- 27-38: `notebook_page_new` function. - 26-38: `notebook_page_new` function.
- 29: `g_return_if_fail` is used to check the argument. - 28: `g_return_if_fail` is used to check the argument.
- 34: Creates TfeTextView object. - 33-34: Creates TfeTextView object.
If it fails, it returns to the caller. If it fails, no notebook page is created and the function returns to the caller.
- 36: Creates filename, which is "Untitled", "Untitled1", ... . - 35: Creates filename, which is "Untitled", "Untitled1", ... .
- 1-8: `get_untitled` function. - 1-8: `get_untitled` function.
- 3: Static variable `c` is initialized at the first call of this function. After that `c` keeps its value unless it is changed explicitly. - 3: Static variable `c` is initialized at the first call of this function. After that `c` keeps its value unless it is changed explicitly.
- 4-7: Increases `c` by one and if it is zero then it returns "Untitled". If it is a positive integer then it returns "Untitled\<the integer\>", for example, "Untitled1", "Untitled2", and so on. - 4-7: Increases `c` by one and if it is zero, it returns "Untitled". If it is a positive integer, it returns "Untitled\<the integer\>", for example, "Untitled1", "Untitled2", and so on.
The function `g_strdup_printf` creates a string and it should be freed by `g_free` when it becomes useless. The function `g_strdup_printf` creates a string and it should be freed by `g_free` when it becomes useless.
The caller of `get_untitled` is in charge of freeing the string. The caller of `get_untitled` is in charge of freeing the string.
- 37: calls `notebook_page_build` to build the contents of the page. - 36: calls `notebook_page_build` to build the contents of the page.
- 10- 25: `notebook_page_build` function. - 37: frees `filename`.
- 10- 24: `notebook_page_build` function.
A parameter with `const` qualifier doesn't change in the function.
It means that the argument `filename` is owned by the caller.
The caller needs to free it when it becomes useless.
- 12: Creates GtkScrolledWindow. - 12: Creates GtkScrolledWindow.
- 17: Sets the wrap mode of `tv` to GTK_WRAP_WORD_CHAR so that lines are broken between words or graphemes. - 17: Inserts `tv` to GtkscrolledWindow as a child.
- 18: Inserts `tv` to GtkscrolledWindow as a child. - 18-19: Creates GtkLabel, then appends `scr` and `lab` to the GtkNotebook instance `nb`.
- 19-20: Creates GtkLabel, then appends `scr` and `lab` to the GtkNotebook instance `nb`. - 20-21: Sets "tab-expand" property to TRUE.
- 21-22: Sets "tab-expand" property to TRUE.
The function `g_object_set` sets properties on an object. The function `g_object_set` sets properties on an object.
The object is any object derived from GObject. The object is any object derived from GObject.
In many cases, an object has its own function to set its properties, but sometimes not. In many cases, an object has its own function to set its properties, but sometimes not.
In that case, use `g_object_set` to set the property. In that case, use `g_object_set` to set the property.
- 23: Sets the current page of `nb` to the newly created page. - 22: Sets the current page to the newly created page.
- 24: Connects "change-file" signal and `file_changed_cb` handler. - 23: Connects "change-file" signal and `file_changed_cb` handler.
## notebook\_page\_new\_with\_file ## notebook\_page\_new\_with\_file
@ -132,13 +135,14 @@ In that case, use `g_object_set` to set the property.
10 return; /* read error */ 10 return; /* read error */
11 filename = g_file_get_basename (file); 11 filename = g_file_get_basename (file);
12 notebook_page_build (nb, tv, filename); 12 notebook_page_build (nb, tv, filename);
13 } 13 g_free (filename);
14 }
~~~ ~~~
- 9-10: Calls `tfe_text_view_new_with_file`. - 9-10: Calls `tfe_text_view_new_with_file`.
If the function returns NULL, an error has happend. If the function returns NULL, an error has happend.
Then, it does nothing and returns. Then, it does nothing and returns.
- 11-12: Gets the filename and builds the contents of the page. - 11-13: Gets the filename, builds the contents of the page and frees `filename`.
## notebook\_page\_open ## notebook\_page\_open
@ -148,58 +152,95 @@ Then, it does nothing and returns.
3 GFile *file; 3 GFile *file;
4 char *filename; 4 char *filename;
5 5
6 if (response != TFE_OPEN_RESPONSE_SUCCESS || ! G_IS_FILE (file = tfe_text_view_get_file (tv))) { 6 if (response != TFE_OPEN_RESPONSE_SUCCESS) {
7 g_object_ref_sink (tv); 7 g_object_ref_sink (tv);
8 g_object_unref (tv); 8 g_object_unref (tv);
9 }else { 9 }else {
10 filename = g_file_get_basename (file); 10 file = tfe_text_view_get_file (tv);
11 g_object_unref (file); 11 filename = g_file_get_basename (file);
12 notebook_page_build (nb, GTK_WIDGET (tv), filename); 12 g_object_unref (file);
13 } 13 notebook_page_build (nb, GTK_WIDGET (tv), filename);
14 } 14 g_free (filename);
15 15 }
16 void 16 }
17 notebook_page_open (GtkNotebook *nb) { 17
18 g_return_if_fail(GTK_IS_NOTEBOOK (nb)); 18 void
19 19 notebook_page_open (GtkNotebook *nb) {
20 GtkWidget *tv; 20 g_return_if_fail(GTK_IS_NOTEBOOK (nb));
21 21
22 if ((tv = tfe_text_view_new ()) == NULL) 22 GtkWidget *tv;
23 return; 23
24 g_signal_connect (TFE_TEXT_VIEW (tv), "open-response", G_CALLBACK (open_response), nb); 24 if ((tv = tfe_text_view_new ()) == NULL)
25 tfe_text_view_open (TFE_TEXT_VIEW (tv), GTK_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET (nb), GTK_TYPE_WINDOW))); 25 return;
26 } 26 g_signal_connect (TFE_TEXT_VIEW (tv), "open-response", G_CALLBACK (open_response), nb);
27 tfe_text_view_open (TFE_TEXT_VIEW (tv), GTK_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET (nb), GTK_TYPE_WINDOW)));
28 }
~~~ ~~~
- 16-26: `notebook_page_open` function. - 18-28: `notebook_page_open` function.
- 22-23: Creates TfeTextView object. - 24-25: Creates TfeTextView object.
If NULL is returned, an error has happened. If NULL is returned, an error has happened.
Then, it returns to the caller. Then, it returns to the caller.
- 24: Connects the signal "open-response" and the handler `open_response`. - 26: Connects the signal "open-response" and the handler `open_response`.
- 25: Calls `tfe_text_view_open`. - 27: Calls `tfe_text_view_open`.
The "open-response" signal will be emitted later to inform the result of opening and reading a file. The "open-response" signal will be emitted later to inform the result of opening and reading a file.
- 1-14: `open_response` handler. - 1-16: `open_response` handler.
- 6-8: If the response code is NOT `TFE_OPEN_RESPONSE_SUCCESS` or `tfe_text_view_get_file` doesn't return the pointer to a GFile, - 6-8: If the response code is not `TFE_OPEN_RESPONSE_SUCCESS`, it has failed to open and read a new file.
it has failed to open and read a new file.
Then, what `notebook_page_open` did in advance need to be canceled. Then, what `notebook_page_open` did in advance need to be canceled.
The instance `tv` hasn't been a child widget of GtkScrolledWindow yet. The instance `tv` hasn't been a child widget of GtkScrolledWindow yet.
Such instance has floating reference. Such instance has floating reference.
Floating reference will be explained later in this subsection. Floating reference will be explained later.
You need to call `g_object_ref_sink` first. You need to call `g_object_ref_sink` first.
Then the floating reference is converted into an ordinary reference. Then the floating reference is converted into an ordinary reference.
Now you call `g_object_unref` to decrease the reference count by one. Now you call `g_object_unref` to decrease the reference count by one.
- 9-13: Otherwise, everything is okay. - 9-15: Otherwise, everything is okay.
Gets the filename, builds the contents of the page. Gets the filename, builds the contents of the page and frees `filename`.
## Floating reference
All the widgets are derived from GInitiallyUnowned. All the widgets are derived from GInitiallyUnowned.
When an instance of GInitiallyUnowned or its descendant is created, the instance has a floating reference. GObject and GInitiallyUnowned are almost the same.
The function `g_object_ref_sink` converts the floating reference into an ordinary reference. The difference is like this.
If the instance doesn't have a floating reference, `g_object_ref_sink` simply increases the reference count by one. When an instance of GInitiallyUnowned is created, the instance has a floating reference and its reference count is zero.
On the other hand, when an instance of GObject (not GInitiallyUnowned) is created, no floating reference is given. On the other hand, when an instance of GObject (not GInitiallyUnowned) is created, no floating reference is given.
And the instance has a normal reference count instead of floating reference. And the instance has a normal reference count instead of floating reference.
Their descendants inherits them, so every widget has a floating reference at first.
Non-widget class, for example, GtkTextBuffer is a direct sub class of GObject and it doesn't have floating reference.
Its reference count is one when it is created.
The function `g_object_ref_sink` converts the floating reference into an ordinary reference.
If the instance doesn't have a floating reference, `g_object_ref_sink` simply increases the reference count by one.
It is used when an widget is added to another widget as a child.
~~~
GtkTextView *tv = gtk_text_view_new (); // floating reference
GtkScrolledWindow *scr = gtk_scrolled_window_new ();
gtk_scrolled_window_set_child (scr, tv); // tv's reference count is one
~~~
When `tv` is added to `scr` as a child, `g_object_ref_sink` is used.
~~~
g_object_ref_sink (tv);
~~~
So, the floating reference is converted into an ordinary reference.
That is to say, floating reference is deleted, and reference count turns to one.
Thanks to this, the caller doesn't need to decrease tv's reference count.
If an Object\_A is not a descendant of GInitiallyUnowned, the program is like this:
~~~
Object_A *obj_a = object_a_new (); // reference count is one
GtkScrolledWindow *scr = gtk_scrolled_window_new ();
gtk_scrolled_window_set_child (scr, obj_a); // obj_a's reference count is two
// obj_a is referred by the caller (this program) and scrolled window
g_object_unref (obj_a); // obj_a's reference count is one because the caller no longer refers obj_a.
~~~
This example tells us that the caller needs to unref `obj_a`.
If you use `g_object_unref` to an instance that has a floating reference, you need to convert the floating reference to a normal reference in advance. If you use `g_object_unref` to an instance that has a floating reference, you need to convert the floating reference to a normal reference in advance.
See [GObject Reference Manual](https://developer-old.gnome.org/gobject/stable/gobject-The-Base-Object-Type.html#gobject-The-Base-Object-Type.description) for further information. See [GObject API reference](https://docs.gtk.org/gobject/floating-refs.html) for further information.
## notebook\_page\_close ## notebook\_page\_close
@ -226,6 +267,7 @@ If the page is the only page the notebook has, then the function destroys the to
- 8-10: If the page is the only page the notebook has, it calls `gtk_window_destroy` to destroys the top-level window. - 8-10: If the page is the only page the notebook has, it calls `gtk_window_destroy` to destroys the top-level window.
- 11-13: Otherwise, removes the current page. - 11-13: Otherwise, removes the current page.
The child widget (TfeTextView) is also destroyed.
## notebook\_page\_save ## notebook\_page\_save
@ -266,7 +308,7 @@ This function gets the TfeTextView object belongs to the current page.
The function `file_changed_cb` is a handler connected to "change-file" signal. The function `file_changed_cb` is a handler connected to "change-file" signal.
If a file in a TfeTextView instance is changed, it emits this signal. If a file in a TfeTextView instance is changed, it emits this signal.
This handler changes the label of GtkNotebookPage. This handler changes the label of the GtkNotebookPage.
~~~C ~~~C
1 static void 1 static void
@ -297,4 +339,4 @@ Then, unref the GFile object `file`.
- 15-16: Creates a GtkLabel instance `label` with the filename and set the label of the GtkNotebookPage with `label`. - 15-16: Creates a GtkLabel instance `label` with the filename and set the label of the GtkNotebookPage with `label`.
Up: [Readme.md](../Readme.md), Prev: [Section 13](sec13.md), Next: [Section 15](sec15.md) Up: [README.md](../README.md), Prev: [Section 13](sec13.md), Next: [Section 15](sec15.md)

View file

@ -124,7 +124,7 @@ textview {color: yellow; ...}
~~~ ~~~
Class, ID and some other things can be applied to the selector like Web CSS. Class, ID and some other things can be applied to the selector like Web CSS.
Refer to [GTK 4 API Reference, CSS in Gtk](https://docs.gtk.org/gtk4/css-overview.html) for further information. Refer to [GTK 4 API Reference -- CSS in Gtk](https://docs.gtk.org/gtk4/css-overview.html) for further information.
In line 30, the CSS is a string. In line 30, the CSS is a string.
@ -306,9 +306,7 @@ In this file, just the source file names are modified from the prior version.
## source files ## source files
The [source files](../src/tfe5) of the text editor `tfe` will be shown in the next section. You can download the files from the [repository](https://github.com/ToshioCP/Gtk4-tutorial).
You can also download the files from the [repository](https://github.com/ToshioCP/Gtk4-tutorial).
There are two options. There are two options.
- Use git and clone. - Use git and clone.

View file

@ -1,26 +1,21 @@
Up: [Readme.md](../Readme.md), Prev: [Section 15](sec15.md), Next: [Section 17](sec17.md) Up: [README.md](../README.md), Prev: [Section 15](sec15.md), Next: [Section 17](sec17.md)
# tfe5 source files # How to build tfe (text file editor)
## How to compile and execute the text editor 'tfe'. ## How to compile and execute the text editor 'tfe'.
First, source files are shown in the later subsections. First, source files are in the [Gtk4-tutorila repository](https://github.com/ToshioCP/Gtk4-tutorial).
How to download them is written at the end of the [previous section](sec15.md). How to download them is written at the end of the [previous section](sec15.md).
The following is the instruction of compilation and execution. The following is the instruction of compilation and execution.
- You need meson and ninja. - You need meson and ninja.
- Set environment variables if necessary. - If you have installed gtk4 from the source, you need to set environment variables to suit your installation.
If you have installed gtk4 from the source and you preferred the option `--prefix $HOME/local` (see [Section 2](sec2.md)), type `. env.sh` to set the environment variables. - Change your current directory to `src/tfe5` directory.
- Type `meson _build` for configuration.
~~~ - Type `ninja -C _build` for compilation.
$ . env.sh
~~~
- change your current directory to `src/tfe5` directory.
- type `meson _build` for configuration.
- type `ninja -C _build` for compilation.
Then the application `tfe` is built under the `_build` directory. Then the application `tfe` is built under the `_build` directory.
- type `_build/tfe` to execute it. - Type `_build/tfe` to execute it.
Then the window appears. Then the window appears.
There are four buttons, `New`, `Open`, `Save` and `Close`. There are four buttons, `New`, `Open`, `Save` and `Close`.
@ -35,665 +30,20 @@ Then the file is read and a new Notebook Page appears.
This is a very simple editor. This is a very simple editor.
It is a good practice for you to add more features. It is a good practice for you to add more features.
## meson.build
~~~meson
1 project('tfe', 'c')
2
3 gtkdep = dependency('gtk4')
4
5 gnome=import('gnome')
6 resources = gnome.compile_resources('resources','tfe.gresource.xml')
7
8 sourcefiles=files('tfeapplication.c', 'tfenotebook.c', '../tfetextview/tfetextview.c')
9
10 executable('tfe', sourcefiles, resources, dependencies: gtkdep)
~~~
## tfe.gresource.xml
~~~xml
1 <?xml version="1.0" encoding="UTF-8"?>
2 <gresources>
3 <gresource prefix="/com/github/ToshioCP/tfe">
4 <file>tfe.ui</file>
5 </gresource>
6 </gresources>
~~~
## tfe.ui
~~~xml
1 <?xml version="1.0" encoding="UTF-8"?>
2 <interface>
3 <object class="GtkApplicationWindow" id="win">
4 <property name="title">file editor</property>
5 <property name="default-width">600</property>
6 <property name="default-height">400</property>
7 <child>
8 <object class="GtkBox" id="boxv">
9 <property name="orientation">GTK_ORIENTATION_VERTICAL</property>
10 <child>
11 <object class="GtkBox" id="boxh">
12 <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
13 <child>
14 <object class="GtkLabel" id="dmy1">
15 <property name="width-chars">10</property>
16 </object>
17 </child>
18 <child>
19 <object class="GtkButton" id="btnn">
20 <property name="label">New</property>
21 </object>
22 </child>
23 <child>
24 <object class="GtkButton" id="btno">
25 <property name="label">Open</property>
26 </object>
27 </child>
28 <child>
29 <object class="GtkLabel" id="dmy2">
30 <property name="hexpand">TRUE</property>
31 </object>
32 </child>
33 <child>
34 <object class="GtkButton" id="btns">
35 <property name="label">Save</property>
36 </object>
37 </child>
38 <child>
39 <object class="GtkButton" id="btnc">
40 <property name="label">Close</property>
41 </object>
42 </child>
43 <child>
44 <object class="GtkLabel" id="dmy3">
45 <property name="width-chars">10</property>
46 </object>
47 </child>
48 </object>
49 </child>
50 <child>
51 <object class="GtkNotebook" id="nb">
52 <property name="scrollable">TRUE</property>
53 <property name="hexpand">TRUE</property>
54 <property name="vexpand">TRUE</property>
55 </object>
56 </child>
57 </object>
58 </child>
59 </object>
60 </interface>
61
~~~
## tfe.h
~~~C
1 #include <gtk/gtk.h>
2
3 #include "../tfetextview/tfetextview.h"
4 #include "tfenotebook.h"
~~~
## tfeapplication.c
~~~C
1 #include "tfe.h"
2
3 static void
4 open_cb (GtkNotebook *nb) {
5 notebook_page_open (nb);
6 }
7
8 static void
9 new_cb (GtkNotebook *nb) {
10 notebook_page_new (nb);
11 }
12
13 static void
14 save_cb (GtkNotebook *nb) {
15 notebook_page_save (nb);
16 }
17
18 static void
19 close_cb (GtkNotebook *nb) {
20 notebook_page_close (GTK_NOTEBOOK (nb));
21 }
22
23 static void
24 app_activate (GApplication *application) {
25 GtkApplication *app = GTK_APPLICATION (application);
26 GtkWidget *win = GTK_WIDGET (gtk_application_get_active_window (app));
27 GtkWidget *boxv = gtk_window_get_child (GTK_WINDOW (win));
28 GtkNotebook *nb = GTK_NOTEBOOK (gtk_widget_get_last_child (boxv));
29
30 notebook_page_new (nb);
31 gtk_widget_show (GTK_WIDGET (win));
32 }
33
34 static void
35 app_open (GApplication *application, GFile ** files, gint n_files, const gchar *hint) {
36 GtkApplication *app = GTK_APPLICATION (application);
37 GtkWidget *win = GTK_WIDGET (gtk_application_get_active_window (app));
38 GtkWidget *boxv = gtk_window_get_child (GTK_WINDOW (win));
39 GtkNotebook *nb = GTK_NOTEBOOK (gtk_widget_get_last_child (boxv));
40 int i;
41
42 for (i = 0; i < n_files; i++)
43 notebook_page_new_with_file (nb, files[i]);
44 if (gtk_notebook_get_n_pages (nb) == 0)
45 notebook_page_new (nb);
46 gtk_widget_show (win);
47 }
48
49 static void
50 app_startup (GApplication *application) {
51 GtkApplication *app = GTK_APPLICATION (application);
52 GtkBuilder *build;
53 GtkApplicationWindow *win;
54 GtkNotebook *nb;
55 GtkButton *btno;
56 GtkButton *btnn;
57 GtkButton *btns;
58 GtkButton *btnc;
59
60 build = gtk_builder_new_from_resource ("/com/github/ToshioCP/tfe/tfe.ui");
61 win = GTK_APPLICATION_WINDOW (gtk_builder_get_object (build, "win"));
62 nb = GTK_NOTEBOOK (gtk_builder_get_object (build, "nb"));
63 gtk_window_set_application (GTK_WINDOW (win), app);
64 btno = GTK_BUTTON (gtk_builder_get_object (build, "btno"));
65 btnn = GTK_BUTTON (gtk_builder_get_object (build, "btnn"));
66 btns = GTK_BUTTON (gtk_builder_get_object (build, "btns"));
67 btnc = GTK_BUTTON (gtk_builder_get_object (build, "btnc"));
68 g_signal_connect_swapped (btno, "clicked", G_CALLBACK (open_cb), nb);
69 g_signal_connect_swapped (btnn, "clicked", G_CALLBACK (new_cb), nb);
70 g_signal_connect_swapped (btns, "clicked", G_CALLBACK (save_cb), nb);
71 g_signal_connect_swapped (btnc, "clicked", G_CALLBACK (close_cb), nb);
72 g_object_unref(build);
73
74 GdkDisplay *display;
75
76 display = gtk_widget_get_display (GTK_WIDGET (win));
77 GtkCssProvider *provider = gtk_css_provider_new ();
78 gtk_css_provider_load_from_data (provider, "textview {padding: 10px; font-family: monospace; font-size: 12pt;}", -1);
79 gtk_style_context_add_provider_for_display (display, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
80 }
81
82 #define APPLICATION_ID "com.github.ToshioCP.tfe"
83
84 int
85 main (int argc, char **argv) {
86 GtkApplication *app;
87 int stat;
88
89 app = gtk_application_new (APPLICATION_ID, G_APPLICATION_HANDLES_OPEN);
90
91 g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL);
92 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
93 g_signal_connect (app, "open", G_CALLBACK (app_open), NULL);
94
95 stat =g_application_run (G_APPLICATION (app), argc, argv);
96 g_object_unref (app);
97 return stat;
98 }
99
~~~
## tfenotebook.h
~~~C
1 void
2 notebook_page_save(GtkNotebook *nb);
3
4 void
5 notebook_page_close (GtkNotebook *nb);
6
7 void
8 notebook_page_open (GtkNotebook *nb);
9
10 void
11 notebook_page_new_with_file (GtkNotebook *nb, GFile *file);
12
13 void
14 notebook_page_new (GtkNotebook *nb);
15
~~~
## tfenotebook.c
~~~C
1 #include "tfe.h"
2
3 /* The returned string should be freed with g_free() when no longer needed. */
4 static gchar*
5 get_untitled () {
6 static int c = -1;
7 if (++c == 0)
8 return g_strdup_printf("Untitled");
9 else
10 return g_strdup_printf ("Untitled%u", c);
11 }
12
13 static TfeTextView *
14 get_current_textview (GtkNotebook *nb) {
15 int i;
16 GtkWidget *scr;
17 GtkWidget *tv;
18
19 i = gtk_notebook_get_current_page (nb);
20 scr = gtk_notebook_get_nth_page (nb, i);
21 tv = gtk_scrolled_window_get_child (GTK_SCROLLED_WINDOW (scr));
22 return TFE_TEXT_VIEW (tv);
23 }
24
25 static void
26 file_changed_cb (TfeTextView *tv, GtkNotebook *nb) {
27 GtkWidget *scr;
28 GtkWidget *label;
29 GFile *file;
30 char *filename;
31
32 file = tfe_text_view_get_file (tv);
33 scr = gtk_widget_get_parent (GTK_WIDGET (tv));
34 if (G_IS_FILE (file)) {
35 filename = g_file_get_basename (file);
36 g_object_unref (file);
37 } else
38 filename = get_untitled ();
39 label = gtk_label_new (filename);
40 g_free (filename);
41 gtk_notebook_set_tab_label (nb, scr, label);
42 }
43
44 void
45 notebook_page_save (GtkNotebook *nb) {
46 g_return_if_fail(GTK_IS_NOTEBOOK (nb));
47
48 TfeTextView *tv;
49
50 tv = get_current_textview (nb);
51 tfe_text_view_save (TFE_TEXT_VIEW (tv));
52 }
53
54 void
55 notebook_page_close (GtkNotebook *nb) {
56 g_return_if_fail(GTK_IS_NOTEBOOK (nb));
57
58 GtkWidget *win;
59 int i;
60
61 if (gtk_notebook_get_n_pages (nb) == 1) {
62 win = gtk_widget_get_ancestor (GTK_WIDGET (nb), GTK_TYPE_WINDOW);
63 gtk_window_destroy(GTK_WINDOW (win));
64 } else {
65 i = gtk_notebook_get_current_page (nb);
66 gtk_notebook_remove_page (GTK_NOTEBOOK (nb), i);
67 }
68 }
69
70 static void
71 notebook_page_build (GtkNotebook *nb, GtkWidget *tv, char *filename) {
72 GtkWidget *scr = gtk_scrolled_window_new ();
73 GtkNotebookPage *nbp;
74 GtkWidget *lab;
75 int i;
76
77 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR);
78 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);
79 lab = gtk_label_new (filename);
80 i = gtk_notebook_append_page (nb, scr, lab);
81 nbp = gtk_notebook_get_page (nb, scr);
82 g_object_set (nbp, "tab-expand", TRUE, NULL);
83 gtk_notebook_set_current_page (nb, i);
84 g_signal_connect (GTK_TEXT_VIEW (tv), "change-file", G_CALLBACK (file_changed_cb), nb);
85 }
86
87 static void
88 open_response (TfeTextView *tv, int response, GtkNotebook *nb) {
89 GFile *file;
90 char *filename;
91
92 if (response != TFE_OPEN_RESPONSE_SUCCESS || ! G_IS_FILE (file = tfe_text_view_get_file (tv))) {
93 g_object_ref_sink (tv);
94 g_object_unref (tv);
95 }else {
96 filename = g_file_get_basename (file);
97 g_object_unref (file);
98 notebook_page_build (nb, GTK_WIDGET (tv), filename);
99 }
100 }
101
102 void
103 notebook_page_open (GtkNotebook *nb) {
104 g_return_if_fail(GTK_IS_NOTEBOOK (nb));
105
106 GtkWidget *tv;
107
108 if ((tv = tfe_text_view_new ()) == NULL)
109 return;
110 g_signal_connect (TFE_TEXT_VIEW (tv), "open-response", G_CALLBACK (open_response), nb);
111 tfe_text_view_open (TFE_TEXT_VIEW (tv), GTK_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET (nb), GTK_TYPE_WINDOW)));
112 }
113
114 void
115 notebook_page_new_with_file (GtkNotebook *nb, GFile *file) {
116 g_return_if_fail(GTK_IS_NOTEBOOK (nb));
117 g_return_if_fail(G_IS_FILE (file));
118
119 GtkWidget *tv;
120 char *filename;
121
122 if ((tv = tfe_text_view_new_with_file (file)) == NULL)
123 return; /* read error */
124 filename = g_file_get_basename (file);
125 notebook_page_build (nb, tv, filename);
126 }
127
128 void
129 notebook_page_new (GtkNotebook *nb) {
130 g_return_if_fail(GTK_IS_NOTEBOOK (nb));
131
132 GtkWidget *tv;
133 char *filename;
134
135 if ((tv = tfe_text_view_new ()) == NULL)
136 return;
137 filename = get_untitled ();
138 notebook_page_build (nb, tv, filename);
139 }
140
~~~
## tfetextview.h
~~~C
1 #ifndef __TFE_TEXT_VIEW_H__
2 #define __TFE_TEXT_VIEW_H__
3
4 #include <gtk/gtk.h>
5
6 #define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type ()
7 G_DECLARE_FINAL_TYPE (TfeTextView, tfe_text_view, TFE, TEXT_VIEW, GtkTextView)
8
9 /* "open-response" signal response */
10 enum TfeTextViewOpenResponseType
11 {
12 TFE_OPEN_RESPONSE_SUCCESS,
13 TFE_OPEN_RESPONSE_CANCEL,
14 TFE_OPEN_RESPONSE_ERROR
15 };
16
17 GFile *
18 tfe_text_view_get_file (TfeTextView *tv);
19
20 void
21 tfe_text_view_open (TfeTextView *tv, GtkWindow *win);
22
23 void
24 tfe_text_view_save (TfeTextView *tv);
25
26 void
27 tfe_text_view_saveas (TfeTextView *tv);
28
29 GtkWidget *
30 tfe_text_view_new_with_file (GFile *file);
31
32 GtkWidget *
33 tfe_text_view_new (void);
34
35 #endif /* __TFE_TEXT_VIEW_H__ */
~~~
## tfetextview.c
~~~C
1 #include <string.h>
2 #include "tfetextview.h"
3
4 struct _TfeTextView {
5 GtkTextView parent;
6 GFile *file;
7 };
8
9 G_DEFINE_TYPE (TfeTextView, tfe_text_view, GTK_TYPE_TEXT_VIEW);
10
11 enum {
12 CHANGE_FILE,
13 OPEN_RESPONSE,
14 NUMBER_OF_SIGNALS
15 };
16
17 static guint tfe_text_view_signals[NUMBER_OF_SIGNALS];
18
19 static void
20 tfe_text_view_dispose (GObject *gobject) {
21 TfeTextView *tv = TFE_TEXT_VIEW (gobject);
22
23 if (G_IS_FILE (tv->file))
24 g_clear_object (&tv->file);
25
26 G_OBJECT_CLASS (tfe_text_view_parent_class)->dispose (gobject);
27 }
28
29 static void
30 tfe_text_view_init (TfeTextView *tv) {
31 tv->file = NULL;
32 }
33
34 static void
35 tfe_text_view_class_init (TfeTextViewClass *class) {
36 GObjectClass *object_class = G_OBJECT_CLASS (class);
37
38 object_class->dispose = tfe_text_view_dispose;
39 tfe_text_view_signals[CHANGE_FILE] = g_signal_new ("change-file",
40 G_TYPE_FROM_CLASS (class),
41 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
42 0 /* class offset */,
43 NULL /* accumulator */,
44 NULL /* accumulator data */,
45 NULL /* C marshaller */,
46 G_TYPE_NONE /* return_type */,
47 0 /* n_params */
48 );
49 tfe_text_view_signals[OPEN_RESPONSE] = g_signal_new ("open-response",
50 G_TYPE_FROM_CLASS (class),
51 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
52 0 /* class offset */,
53 NULL /* accumulator */,
54 NULL /* accumulator data */,
55 NULL /* C marshaller */,
56 G_TYPE_NONE /* return_type */,
57 1 /* n_params */,
58 G_TYPE_INT
59 );
60 }
61
62 GFile *
63 tfe_text_view_get_file (TfeTextView *tv) {
64 g_return_val_if_fail (TFE_IS_TEXT_VIEW (tv), NULL);
65
66 if (G_IS_FILE (tv->file))
67 return g_file_dup (tv->file);
68 else
69 return NULL;
70 }
71
72 static gboolean
73 save_file (GFile *file, GtkTextBuffer *tb, GtkWindow *win) {
74 GtkTextIter start_iter;
75 GtkTextIter end_iter;
76 gchar *contents;
77 gboolean stat;
78 GtkWidget *message_dialog;
79 GError *err = NULL;
80
81 gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter);
82 contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE);
83 if (g_file_replace_contents (file, contents, strlen (contents), NULL, TRUE, G_FILE_CREATE_NONE, NULL, NULL, &err)) {
84 gtk_text_buffer_set_modified (tb, FALSE);
85 stat = TRUE;
86 } else {
87 message_dialog = gtk_message_dialog_new (win, GTK_DIALOG_MODAL,
88 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
89 "%s.\n", err->message);
90 g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
91 gtk_widget_show (message_dialog);
92 g_error_free (err);
93 stat = FALSE;
94 }
95 g_free (contents);
96 return stat;
97 }
98
99 static void
100 saveas_dialog_response (GtkWidget *dialog, gint response, TfeTextView *tv) {
101 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
102 GFile *file;
103 GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW);
104
105 if (response == GTK_RESPONSE_ACCEPT) {
106 file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
107 if (! G_IS_FILE (file))
108 g_warning ("TfeTextView: gtk_file_chooser_get_file returns non GFile.\n");
109 else if (save_file(file, tb, GTK_WINDOW (win))) {
110 if (G_IS_FILE (tv->file))
111 g_object_unref (tv->file);
112 tv->file = file;
113 g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
114 } else
115 g_object_unref (file);
116 }
117 gtk_window_destroy (GTK_WINDOW (dialog));
118 }
119
120 void
121 tfe_text_view_save (TfeTextView *tv) {
122 g_return_if_fail (TFE_IS_TEXT_VIEW (tv));
123
124 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
125 GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW);
126
127 if (! gtk_text_buffer_get_modified (tb))
128 return; /* no need to save it */
129 else if (tv->file == NULL)
130 tfe_text_view_saveas (tv);
131 else if (! G_IS_FILE (tv->file))
132 g_error ("TfeTextView: The pointer tv->file isn't NULL nor GFile.\n");
133 else
134 save_file (tv->file, tb, GTK_WINDOW (win));
135 }
136
137 void
138 tfe_text_view_saveas (TfeTextView *tv) {
139 g_return_if_fail (TFE_IS_TEXT_VIEW (tv));
140
141 GtkWidget *dialog;
142 GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW);
143
144 dialog = gtk_file_chooser_dialog_new ("Save file", GTK_WINDOW (win), GTK_FILE_CHOOSER_ACTION_SAVE,
145 "Cancel", GTK_RESPONSE_CANCEL,
146 "Save", GTK_RESPONSE_ACCEPT,
147 NULL);
148 g_signal_connect (dialog, "response", G_CALLBACK (saveas_dialog_response), tv);
149 gtk_widget_show (dialog);
150 }
151
152 GtkWidget *
153 tfe_text_view_new_with_file (GFile *file) {
154 g_return_val_if_fail (G_IS_FILE (file), NULL);
155
156 GtkWidget *tv;
157 GtkTextBuffer *tb;
158 char *contents;
159 gsize length;
160
161 if (! g_file_load_contents (file, NULL, &contents, &length, NULL, NULL)) /* read error */
162 return NULL;
163
164 if ((tv = tfe_text_view_new()) != NULL) {
165 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
166 gtk_text_buffer_set_text (tb, contents, length);
167 TFE_TEXT_VIEW (tv)->file = g_file_dup (file);
168 gtk_text_buffer_set_modified (tb, FALSE);
169 }
170 g_free (contents);
171 return tv;
172 }
173
174 static void
175 open_dialog_response(GtkWidget *dialog, gint response, TfeTextView *tv) {
176 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
177 GFile *file;
178 char *contents;
179 gsize length;
180 GtkWidget *message_dialog;
181 GError *err = NULL;
182
183 if (response != GTK_RESPONSE_ACCEPT)
184 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_CANCEL);
185 else if (! G_IS_FILE (file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog)))) {
186 g_warning ("TfeTextView: gtk_file_chooser_get_file returns non GFile.\n");
187 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_ERROR);
188 } else if (! g_file_load_contents (file, NULL, &contents, &length, NULL, &err)) { /* read error */
189 g_object_unref (file);
190 message_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog), GTK_DIALOG_MODAL,
191 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
192 "%s.\n", err->message);
193 g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
194 gtk_widget_show (message_dialog);
195 g_error_free (err);
196 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_ERROR);
197 } else {
198 gtk_text_buffer_set_text (tb, contents, length);
199 g_free (contents);
200 if (G_IS_FILE (tv->file))
201 g_object_unref (tv->file);
202 tv->file = file;
203 gtk_text_buffer_set_modified (tb, FALSE);
204 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_SUCCESS);
205 g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
206 }
207 gtk_window_destroy (GTK_WINDOW (dialog));
208 }
209
210 void
211 tfe_text_view_open (TfeTextView *tv, GtkWindow *win) {
212 g_return_if_fail (TFE_IS_TEXT_VIEW (tv));
213 g_return_if_fail (GTK_IS_WINDOW (win));
214
215 GtkWidget *dialog;
216
217 dialog = gtk_file_chooser_dialog_new ("Open file", win, GTK_FILE_CHOOSER_ACTION_OPEN,
218 "Cancel", GTK_RESPONSE_CANCEL,
219 "Open", GTK_RESPONSE_ACCEPT,
220 NULL);
221 g_signal_connect (dialog, "response", G_CALLBACK (open_dialog_response), tv);
222 gtk_widget_show (dialog);
223 }
224
225 GtkWidget *
226 tfe_text_view_new (void) {
227 return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL));
228 }
229
~~~
## Total number of lines, words and characters ## Total number of lines, words and characters
~~~ ~~~
$ LANG=C wc tfe5/meson.build tfe5/tfeapplication.c tfe5/tfe.gresource.xml tfe5/tfe.h tfe5/tfenotebook.c tfe5/tfenotebook.h tfetextview/tfetextview.c tfetextview/tfetextview.h tfe5/tfe.ui $ LANG=C wc tfe5/meson.build tfe5/tfeapplication.c tfe5/tfe.gresource.xml tfe5/tfenotebook.c tfe5/tfenotebook.h tfetextview/tfetextview.c tfetextview/tfetextview.h tfe5/tfe.ui
10 17 294 tfe5/meson.build 10 17 294 tfe5/meson.build
99 304 3205 tfe5/tfeapplication.c 101 308 3274 tfe5/tfeapplication.c
6 9 153 tfe5/tfe.gresource.xml 6 9 153 tfe5/tfe.gresource.xml
4 6 87 tfe5/tfe.h 145 387 3667 tfe5/tfenotebook.c
140 378 3601 tfe5/tfenotebook.c
15 21 241 tfe5/tfenotebook.h 15 21 241 tfe5/tfenotebook.h
229 671 8017 tfetextview/tfetextview.c 239 863 9264 tfetextview/tfetextview.c
35 60 701 tfetextview/tfetextview.h 35 60 701 tfetextview/tfetextview.h
61 100 2073 tfe5/tfe.ui 61 100 2073 tfe5/tfe.ui
599 1566 18372 total 612 1765 19667 total
~~~ ~~~
Up: [Readme.md](../Readme.md), Prev: [Section 15](sec15.md), Next: [Section 17](sec17.md) Up: [README.md](../README.md), Prev: [Section 15](sec15.md), Next: [Section 17](sec17.md)

View file

@ -4,371 +4,40 @@ Up: [README.md](../README.md), Prev: [Section 1](sec1.md), Next: [Section 3](se
This section describes how to install GTK 4 into Linux distributions. This section describes how to install GTK 4 into Linux distributions.
This tutorial is without any warranty. There are two ways to install GTK 4.
If you want to install GTK 4 to your computer, do it at your own risk.
The information in this section is the one on April/27/2022.
The words 'at present' and/or 'now' in this section means 'April/27/2022'.
There are three possible way to install GTK 4.
- Install it from the distribution packages. - Install it from the distribution packages.
- Build it from the source file. - Build it from the source files.
- Install a GNOME 40 distribution with the GNOME Boxes.
## Installation from the distribution packages ## Installation from the distribution packages
The first way is easy to install. The first way is the best and easiest way to install it.
It is a recommended way. I've installed GTK 4 packages (version 4.8.1) in Ubuntu 22.10.
I've installed GTK 4 packages in Ubuntu 21.04.
(Now, my Ubuntu version is 21.10).
~~~ ~~~
$ sudo apt-get install libgtk-4-bin libgtk-4-common libgtk-4-dev libgtk-4-doc $ sudo apt install libgtk-4-dev
~~~ ~~~
Fedora, Arch, Debian and OpenSUSE are also possible. It is important to install the developing package (libgtk-4-dev).
See [Installing GTK from packages](https://www.gtk.org/docs/installations/linux#installing-gtk-from-packages). It includes C header files.
Otherwise, you can't compile any GTK 4 based programs.
Fedora, Debian, Arch, Gentoo and OpenSUSE also have GTK 4 packages.
Package information for Arch, Debian/Ubuntu and Fedra is described by [Installing GTK from packages](https://www.gtk.org/docs/installations/linux#installing-gtk-from-packages).
The following table shows the distributions which support GTK 4. The following table shows the distributions which support GTK 4.
|Distribution| version |GTK 4| GNOME 40 | |Distribution| version | GTK 4 | GNOME |
|:----------:|:-------------------------:|:---:|:-------------:| |:----------:|:-------------------------:|:-----:|:--------:|
| Fedora | 36 |4.4.2| GNOME 42 | | Fedora | 37 |4.8.2-2| GNOME 43 |
| Ubuntu | 22.04lts | 4.4 |GNOME 41(4.6.2)| | Ubuntu | 22.10 | 4.8.1 |GNOME 43.0|
| Debian | bookworm(testing) |4.6.5| GNOME 42 | | Debian | bookworm(testing) | 4.8.2 | GNOME 43 |
| Arch | rolling release |4.6.5| GNOME 42 | | Arch | rolling release | 4.8.2 | GNOME 43 |
| Gentoo | rolling release |4.6.5| GNOME 42 | | Gentoo | rolling release | 4.8.2 | GNOME 43 |
| OpenSUSE |Tumbleweed(rolling release)|4.6.5| GNOME 42 | | OpenSUSE |Tumbleweed(rolling release)| 4.8.2 | GNOME 43 |
If you've installed GTK 4 from the packages, you don't need to read the rest of this section.
## Installation from the source file ## Installation from the source file
If your operating system doesn't have GTK 4 packages, you need to build it from the source. If you want to install a developing version of GTK 4, you need to build it from the source.
Or, if you want the latest version of GTK 4 (4.6.3), you also need to build it from the source. See [Compiling the GTK Libraries](https://docs.gtk.org/gtk4/building.html) secion in the GTK 4 API reference.
I installed GTK 4 from the source in January 2021.
So, the following information is old, especially for the version of each software.
For the latest information, see [GTK API Reference, Building GTK](https://docs.gtk.org/gtk4/building.html).
### Prerequisites for GTK 4 installation
- Linux operating system. For example, Ubuntu 20.10 or 20.04LTS.
Other distributions might be OK.
- Packages for development such as gcc, meson, ninja, git, wget and so on.
- Dev package is necessary for each software below.
### Installation target
I installed GTK 4 under the directory `$HOME/local`.
This is a private user area.
If you want to install it in the system area, `/opt/gtk4` is one of good choices.
[GTK API Reference, Building GTK](https://docs.gtk.org/gtk4/building.html) gives an installation example to `/opt/gtk4`.
Don't install it to `/usr/local` which is the default.
It is used by Ubuntu applications which are not build on GTK 4.
Therefore, the risk is high and probably bad things will happen.
Actually I did it and I needed to reinstall Ubuntu.
### Installation to Ubuntu 20.10
Most of the necessary libraries are included by Ubuntu 20.10.
Therefore, they can be installed with `apt-get` command.
You don't need to install them from the source tarballs.
You can skip the subsections below about prerequisite library installation (GLib, Pango, GdkPixbuf and GTK-doc).
### GLib installation
If your Ubuntu is 20.04LTS, you need to install prerequisite libraries from the tarballs.
Check the version of your library and if it is lower than the necessary version, install it from the source.
For example,
~~~
$ pkg-config --modversion glib-2.0
2.64.6
~~~
The necessary version is 2.66.0 or higher.
Therefore, the example above shows that you need to install GLib.
I installed 2.67.1 which was the latest version at that time (January 2021).
Download GLib source files from the repository, then decompress and extract files.
$ wget https://download.gnome.org/sources/glib/2.67/glib-2.67.1.tar.xz
$ tar -Jxf glib-2.67.1.tar.xz
Some packages are required to build GLib.
You can find them if you run meson.
$ meson --prefix $HOME/local _build
Use apt-get and install the prerequisites.
For example,
$ sudo apt-get install -y libpcre2-dev libffi-dev
After that, compile GLib.
$ rm -rf _build
$ meson --prefix $HOME/local _build
$ ninja -C _build
$ ninja -C _build install
Set several environment variables so that the GLib libraries installed can be used by build tools.
Make a text file below and save it as `env.sh`
# compiler
CPPFLAGS="-I$HOME/local/include"
LDFLAGS="-L$HOME/local/lib"
PKG_CONFIG_PATH="$HOME/local/lib/pkgconfig:$HOME/local/lib/x86_64-linux-gnu/pkgconfig"
export CPPFLAGS LDFLAGS PKG_CONFIG_PATH
# linker
LD_LIBRARY_PATH="$HOME/local/lib/x86_64-linux-gnu/"
PATH="$HOME/local/bin:$PATH"
export LD_LIBRARY_PATH PATH
# gsetting
export GSETTINGS_SCHEMA_DIR=$HOME/local/share/glib-2.0/schemas
Then, use . (dot) or source command to include these commands to the current bash.
$ . env.sh
or
$ source env.sh
This command carries out the commands in `env.sh` and changes the environment variables above in the current shell.
### Pango installation
Download and untar.
$ wget https://download.gnome.org/sources/pango/1.48/pango-1.48.0.tar.xz
$ tar -Jxf pango-1.48.0.tar.xz
Try meson and check the required packages.
Install all the prerequisites.
Then, compile and install Pango.
$ meson --prefix $HOME/local _build
$ ninja -C _build
$ ninja -C _build install
It installs Pango-1.0.gir under `$HOME/local/share/gir-1.0`.
If you installed Pango without `--prefix` option, then it would be located at `/usr/local/share/gir-1.0`.
This directory (/usr/local/share) is used by applications.
They find the directory by the environment variable `XDG_DATA_DIRS`.
It is a text file which keep the list of 'share' directories like `/usr/share`, `usr/local/share` and so on.
Now `$HOME/local/share` needs to be added to `XDG_DATA_DIRS`, or error will occur in the later compilation.
$ export XDG_DATA_DIRS=$HOME/local/share:$XDG_DATA_DIRS
### GdkPixbuf and GTK-Doc installation
Download and untar.
$ wget https://download.gnome.org/sources/gdk-pixbuf/2.42/gdk-pixbuf-2.42.2.tar.xz
$ tar -Jxf gdk-pixbuf-2.42.2.tar.xz
$ wget https://download.gnome.org/sources/gtk-doc/1.33/gtk-doc-1.33.1.tar.xz
$ tar -Jxf gtk-doc-1.33.1.tar.xz
Same as before, install prerequisite packages, then compile and install them.
The installation of GTK-Doc put `gtk-doc.pc` under `$HOME/local/share/pkgconfig`.
This file is used by pkg-config, which is one of the build tools.
The directory needs to be added to the environment variable `PKG_CONFIG_PATH`
$ export PKG_CONFIG_PATH="$HOME/local/share/pkgconfig:$PKG_CONFIG_PATH"
### GTK 4 installation
If you want the latest development version of GTK 4, use git and clone the repository.
$ git clone https://gitlab.gnome.org/gnome/gtk.git
If you want a stable version of GTK 4, then download it from [GNOME source website](https://download.gnome.org/sources/gtk/).
The latest version is 4.3.1 (13/June/2021).
Compile and install it.
$ meson --prefix $HOME/local _build
$ ninja -C _build
$ ninja -C _build install
If you want to know more information, refer to [GTK 4 API Reference, Building GTK](https://docs.gtk.org/gtk4/building.html).
### Modify env.sh
Because environment variables disappear when you log out, you need to add them again.
Modify `env.sh`.
# compiler
CPPFLAGS="-I$HOME/local/include"
LDFLAGS="-L$HOME/local/lib"
PKG_CONFIG_PATH="$HOME/local/lib/pkgconfig:$HOME/local/lib/x86_64-linux-gnu/pkgconfig:
$HOME/local/share/pkgconfig"
export CPPFLAGS LDFLAGS PKG_CONFIG_PATH
# linker
LD_LIBRARY_PATH="$HOME/local/lib/x86_64-linux-gnu/"
PATH="$HOME/local/bin:$PATH"
export LD_LIBRARY_PATH PATH
# gir
XDG_DATA_DIRS=$HOME/local/share:$XDG_DATA_DIRS
export XDG_DATA_DIRS
# gsetting
export GSETTINGS_SCHEMA_DIR=$HOME/local/share/glib-2.0/schemas
# girepository-1.0
export GI_TYPELIB_PATH=$HOME/local/lib/x86_64-linux-gnu/girepository-1.0
Include this file by . (dot) command before using GTK 4 libraries.
You may think you can add them in your `.profile`.
But it's a wrong decision.
Never write them to your `.profile`.
The environment variables above are necessary only when you compile and run GTK 4 applications.
Otherwise it's not necessary.
If you changed the environment variables above and run GTK 3 applications, it probably causes serious damage.
### Compiling GTK 4 applications
Before you compile GTK 4 applications, define environment variables above.
$ . env.sh
After that you can compile them without anything.
For example, to compile `sample.c`, type the following.
$ gcc `pkg-config --cflags gtk4` sample.c `pkg-config --libs gtk4`
To know how to compile GTK 4 applications, refer to the section 3 (GtkApplication and GtkApplicationWindow) and after.
## Installing Fedora 34 with GNOME Boxes
The last part of this section is about GNOME 40 and GNOME Boxes.
GNOME 40 is a new version of GNOME desktop system.
And GTK 4 is installed in the distribution.
See [GNOME 40 website](https://forty.gnome.org/) first.
*However, GNOME 40 is not necessary to compile and run GTK 4 applications.*
There are seven choices at present.
- GNOME OS
- Arch Linux
- Gentoo Linux
- Fedora 36
- openSUSE Tumbleweed
- Ubuntu 22.04
- Debian bookworm
I've installed Fedora 34 with GNOME Boxes.
My OS was Ubuntu 21.04 at that time.
GNOME Boxes creates a virtual machine in Ubuntu and Fedora will be installed to that virtual machine.
The instruction is as follows.
1. Download Fedora 34 iso file.
There is an link at the end of [GNOME 40 website](https://forty.gnome.org/).
2. Install gnome-boxes with apt-get command.
~~~
$ sudo apt-get install gnome-boxes
~~~
3. Run GNOME Boxes.
4. Click on `+` button on the top left corner and launch a box creation wizard by clicking `Create a Virtual Machine ...`.
Then a dialog appears.
Click on `Operationg System Image File` and select the iso file you have downloaded.
5. Then, the Fedora's installer is executed.
Follow the instructions by the installer.
At the end of the installation, the installer instructs to reboot the system.
Click on the right of the title bar and select reboot or shutdown.
6. Your display is back to the initial window of GNOME Boxes, but there is a button `Fedora 34 Workstation` on the upper left of the window.
Click on the button then Fedora will be executed.
7. A setup dialog appears.
Setup Fedora according to the wizard.
Now you can use Fedora.
It includes GTK 4 libraries already.
But you need to install the GTK 4 development package.
Use `dnf` to install `gtk4.x86_64` package.
~~~
$ sudo dnf install gtk4.x86_64
~~~
### GTK 4 compilation test
You can test the GTK 4 development packages by compiling files which are based on GTK 4.
I've tried compiling `tfe` text editor, which is written in section 21.
1. Run Firefox.
2. Open this website \([Gtk4-Tutorial](https://github.com/ToshioCP/Gtk4-tutorial)\).
3. Click on the green button labeled `Code`.
4. Select `Download ZIP` and download the codes from the repository.
5. Unzip the file.
6. Change your current directory to `src/tfe7`.
7. Compile it.
~~~
$ meson _build
bash: meson: command not found...
Install package 'meson' to provide command 'meson'? [N/y] y
* Waiting in queue...
The following packages have to be installed:
meson-0.56.2-2.fc34.noarch High productivity build system
ninja-build-1.10.2-2.fc34.x86_64 Small build system with a focus on speed
vim-filesystem-2:8.2.2787-1.fc34.noarch VIM filesystem layout
Proceed with changes? [N/y] y
... ...
... ...
The Meson build system
Version: 0.56.2
... ...
... ...
Project name: tfe
Project version: undefined
C compiler for the host machine: cc (gcc 11.0.0 "cc (GCC) 11.0.0 20210210 (Red Hat 11.0.0-0)")
C linker for the host machine: cc ld.bfd 2.35.1-38
Host machine cpu family: x86_64
Host machine cpu: x86_64
Found pkg-config: /usr/bin/pkg-config (1.7.3)
Run-time dependency gtk4 found: YES 4.2.0
Found pkg-config: /usr/bin/pkg-config (1.7.3)
Program glib-compile-resources found: YES (/usr/bin/glib-compile-resources)
Program glib-compile-schemas found: YES (/usr/bin/glib-compile-schemas)
Program glib-compile-schemas found: YES (/usr/bin/glib-compile-schemas)
Build targets in project: 4
Found ninja-1.10.2 at /usr/bin/ninja
$ ninja -C _build
ninja: Entering directory `_build'
[12/12] Linking target tfe
$ ninja -C _build install
ninja: Entering directory `_build'
[0/1] Installing files.
Installing tfe to /usr/local/bin
Installation failed due to insufficient permissions.
Attempting to use polkit to gain elevated privileges...
Installing tfe to /usr/local/bin
Installing /home/<username>/Gtk4-tutorial-main/src/tfe7/com.github.ToshioCP.tfe.gschema.xml to /usr/local/share/glib-2.0/schemas
Running custom install script '/usr/bin/glib-compile-schemas /usr/local/share/glib-2.0/schemas/'
~~~
8. Execute it.
~~~
$ tfe
~~~
Then, the window of `tfe` text editor appears.
The compilation and execution have succeeded.
Up: [README.md](../README.md), Prev: [Section 1](sec1.md), Next: [Section 3](sec3.md) Up: [README.md](../README.md), Prev: [Section 1](sec1.md), Next: [Section 3](sec3.md)

View file

@ -6,7 +6,7 @@ Up: [README.md](../README.md), Prev: [Section 2](sec2.md), Next: [Section 4](se
### GtkApplication and g\_application\_run ### GtkApplication and g\_application\_run
Usually people write programming code to make an application. People write programming code to make an application.
What are applications? What are applications?
Applications are software that runs using libraries, which includes the Applications are software that runs using libraries, which includes the
OS, frameworks and so on. OS, frameworks and so on.
@ -30,7 +30,7 @@ The following is the C code representing the scenario above.
5 GtkApplication *app; 5 GtkApplication *app;
6 int stat; 6 int stat;
7 7
8 app = gtk_application_new ("com.github.ToshioCP.pr1", G_APPLICATION_FLAGS_NONE); 8 app = gtk_application_new ("com.github.ToshioCP.pr1", G_APPLICATION_DEFAULT_FLAGS);
9 stat =g_application_run (G_APPLICATION (app), argc, argv); 9 stat =g_application_run (G_APPLICATION (app), argc, argv);
10 g_object_unref (app); 10 g_object_unref (app);
11 return stat; 11 return stat;
@ -39,7 +39,7 @@ The following is the C code representing the scenario above.
~~~ ~~~
The first line says that this program includes the header files of the Gtk libraries. The first line says that this program includes the header files of the Gtk libraries.
The function `main` above is a startup function in C language. The function `main` is a startup function in C language.
The variable `app` is defined as a pointer to a GtkApplication instance. The variable `app` is defined as a pointer to a GtkApplication instance.
The function `gtk_application_new` creates a GtkApplication instance and returns a pointer to the instance. The function `gtk_application_new` creates a GtkApplication instance and returns a pointer to the instance.
The GtkApplication instance is a C structure data in which the information about the application is stored. The GtkApplication instance is a C structure data in which the information about the application is stored.
@ -49,6 +49,16 @@ The function `g_application_run` runs an application that the instance defined.
Actually, `app` is not an application but a pointer to the instance of the application. Actually, `app` is not an application but a pointer to the instance of the application.
However, it is simple and short, and probably no confusion occurs.) However, it is simple and short, and probably no confusion occurs.)
Here I used the word `instance`.
Instance, class and object are terminologies in Object Oriented Programming.
I use these words in the same way.
But, I will often use "object" instead of "instance" in this tutorial.
That means "object" and "instance" is the same.
Object is a bit ambiguous word.
In a broad sense, object has wider meaning than instance.
So, readers should be careful of the contexts to find the meaning of "object".
In many cases, object and instance are the same.
To compile this, the following command needs to be run. To compile this, the following command needs to be run.
The string `pr1.c` is the filename of the C source code above. The string `pr1.c` is the filename of the C source code above.
@ -85,7 +95,8 @@ So, I will explain that to you first.
A signal is emitted when something happens. A signal is emitted when something happens.
For example, a window is created, a window is destroyed and so on. For example, a window is created, a window is destroyed and so on.
The signal "activate" is emitted when the application is activated, or started. The signal "activate" is emitted when the application is activated.
(Activated is a bit different from started, but you can think the both are almost same so far.)
If the signal is connected to a function, which is called a signal handler or If the signal is connected to a function, which is called a signal handler or
simply handler, then the function is invoked when the signal emits. simply handler, then the function is invoked when the signal emits.
@ -93,7 +104,7 @@ The flow is like this:
1. Something happens. 1. Something happens.
2. If it's related to a certain signal, then the signal is emitted. 2. If it's related to a certain signal, then the signal is emitted.
3. If the signal as been connected to a handler, then the handler is invoked. 3. If the signal has been connected to a handler in advance, then the handler is invoked.
Signals are defined in objects. Signals are defined in objects.
For example, the "activate" signal belongs to the GApplication object, which is For example, the "activate" signal belongs to the GApplication object, which is
@ -127,7 +138,7 @@ We use a function `g_signal_connect` which connects a signal to a handler.
10 GtkApplication *app; 10 GtkApplication *app;
11 int stat; 11 int stat;
12 12
13 app = gtk_application_new ("com.github.ToshioCP.pr2", G_APPLICATION_FLAGS_NONE); 13 app = gtk_application_new ("com.github.ToshioCP.pr2", G_APPLICATION_DEFAULT_FLAGS);
14 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL); 14 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
15 stat =g_application_run (G_APPLICATION (app), argc, argv); 15 stat =g_application_run (G_APPLICATION (app), argc, argv);
16 g_object_unref (app); 16 g_object_unref (app);
@ -137,6 +148,7 @@ We use a function `g_signal_connect` which connects a signal to a handler.
~~~ ~~~
First, we define the handler `app_activate` which simply displays a message. First, we define the handler `app_activate` which simply displays a message.
The function `g_print` is defined in GLib and it's like a printf in the C standard library.
In the function `main`, we add `g_signal_connect` before `g_application_run`. In the function `main`, we add `g_signal_connect` before `g_application_run`.
The function `g_signal_connect` has four arguments. The function `g_signal_connect` has four arguments.
@ -145,14 +157,39 @@ The function `g_signal_connect` has four arguments.
3. A handler function (also called callback), which needs to be casted by `G_CALLBACK`. 3. A handler function (also called callback), which needs to be casted by `G_CALLBACK`.
4. Data to pass to the handler. If no data is necessary, NULL should be given. 4. Data to pass to the handler. If no data is necessary, NULL should be given.
You can find the description of each signal in the API reference manual. It is described in the [GObject API Reference](https://docs.gtk.org/gobject/func.signal_connect.html).
For example, "activate" signal is in GApplication section in [GIO API Reference](https://docs.gtk.org/gio/signal.Application.activate.html). Correctly, `g_signal_connect` is a macro (not a C function).
The handler function is described in it.
~~~c
#define g_signal_connect (
instance,
detailed_signal,
c_handler,
data
)
~~~
You can find the description of each signal in the API reference manual.
For example, "activate" signal is in [GApplication section](https://docs.gtk.org/gio/signal.Application.activate.html) in the GIO API Reference.
~~~c
void
activate (
GApplication* self,
gpointer user_data
)
~~~
This is a declaration of the "activate" signal handler.
You can use any name instead of "activate" in the declaration above.
The parameters are:
- self is an instance to which the signal belongs.
- user\_data is a data defined in the fourth argument of the `g_signal_connect` function.
If it is NULL, then you can ignore and left out the second parameter.
In addition, `g_signal_connect` is described in [GObject API Reference](https://docs.gtk.org/gobject/func.signal_connect.html).
API reference manual is very important. API reference manual is very important.
You should see and understand it to write Gtk applications. You should see and understand it.
They are located in ['GTK Documentation'](https://docs.gtk.org/).
Let's compile the source file above (`pr2.c`) and run it. Let's compile the source file above (`pr2.c`) and run it.
@ -246,19 +283,20 @@ By this definition, it returns a pointer to GtkWidget, not GtkWindow.
It actually creates a new GtkWindow instance (not GtkWidget) but returns a pointer to GtkWidget. It actually creates a new GtkWindow instance (not GtkWidget) but returns a pointer to GtkWidget.
However,the pointer points the GtkWidget and at the same time it also points GtkWindow that contains GtkWidget in it. However,the pointer points the GtkWidget and at the same time it also points GtkWindow that contains GtkWidget in it.
If you want to use `win` as a pointer to the GtkWindow, you need to cast it. If you want to use `win` as a pointer to a GtkWindow type instance, you need to cast it.
~~~C ~~~C
(GtkWindow *) win (GtkWindow *) win
~~~ ~~~
Or you can use `GTK_WINDOW` macro that performs a similar function. It works, but isn't usually used.
Instead, `GTK_WINDOW` macro is used.
~~~C ~~~C
GTK_WINDOW (win) GTK_WINDOW (win)
~~~ ~~~
This is a recommended way. The macro is recommended because it does not only cast but also check the type.
#### Connect it to GtkApplication. #### Connect it to GtkApplication.
@ -268,8 +306,7 @@ The function `gtk_window_set_application` is used to connect GtkWindow to GtkApp
gtk_window_set_application (GTK_WINDOW (win), GTK_APPLICATION (app)); gtk_window_set_application (GTK_WINDOW (win), GTK_APPLICATION (app));
~~~ ~~~
You need to cast `win` to GtkWindow and `app` to GtkApplication. You need to cast `win` to GtkWindow and `app` to GtkApplication with `GTK_WINDOW` and `GTK_APPLICATION` macro.
`GTK_WINDOW` and `GTK_APPLICATION` macro is appropriate for that.
GtkApplication continues to run until the related window is destroyed. GtkApplication continues to run until the related window is destroyed.
If you didn't connect GtkWindow and GtkApplication, GtkApplication destroys itself immediately. If you didn't connect GtkWindow and GtkApplication, GtkApplication destroys itself immediately.
@ -285,7 +322,7 @@ But, there's an exception.
Top window (this term will be explained later) isn't visible when it is created. Top window (this term will be explained later) isn't visible when it is created.
So you need to use the function above to show the window. So you need to use the function above to show the window.
Save the program as `pr3.c` and compile and run it. Save the program as `pr3.c`, then compile and run it.
~~~ ~~~
$ comp pr3 $ comp pr3
@ -301,8 +338,8 @@ Click on the close button then the window disappears and the program finishes.
### GtkApplicationWindow ### GtkApplicationWindow
GtkApplicationWindow is a child object of GtkWindow. GtkApplicationWindow is a child object of GtkWindow.
It has some extra functionality for better integration with GtkApplication. It has some extra feature for better integration with GtkApplication.
It is recommended to use it instead of GtkWindow when you use GtkApplication. It is recommended to use it as the top-level window of the application instead of GtkWindow.
Now rewrite the program and use GtkApplicationWindow. Now rewrite the program and use GtkApplicationWindow.

View file

@ -1,4 +1,4 @@
Up: [Readme.md](../Readme.md), Prev: [Section 3](sec3.md), Next: [Section 5](sec5.md) Up: [README.md](../README.md), Prev: [Section 3](sec3.md), Next: [Section 5](sec5.md)
# Widgets (1) # Widgets (1)
@ -15,7 +15,7 @@ It is a widget with text in it.
1 #include <gtk/gtk.h> 1 #include <gtk/gtk.h>
2 2
3 static void 3 static void
4 app_activate (GApplication *app, gpointer user_data) { 4 app_activate (GApplication *app) {
5 GtkWidget *win; 5 GtkWidget *win;
6 GtkWidget *lab; 6 GtkWidget *lab;
7 7
@ -34,7 +34,7 @@ It is a widget with text in it.
20 GtkApplication *app; 20 GtkApplication *app;
21 int stat; 21 int stat;
22 22
23 app = gtk_application_new ("com.github.ToshioCP.lb1", G_APPLICATION_FLAGS_NONE); 23 app = gtk_application_new ("com.github.ToshioCP.lb1", G_APPLICATION_DEFAULT_FLAGS);
24 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL); 24 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
25 stat =g_application_run (G_APPLICATION (app), argc, argv); 25 stat =g_application_run (G_APPLICATION (app), argc, argv);
26 g_object_unref (app); 26 g_object_unref (app);
@ -58,6 +58,10 @@ A program `diff` is good to know the difference between two files.
~~~ ~~~
$ cd misc; diff pr4.c lb1.c $ cd misc; diff pr4.c lb1.c
4c4
< app_activate (GApplication *app, gpointer user_data) {
---
> app_activate (GApplication *app) {
5a6 5a6
> GtkWidget *lab; > GtkWidget *lab;
8c9 8c9
@ -70,13 +74,15 @@ $ cd misc; diff pr4.c lb1.c
> gtk_window_set_child (GTK_WINDOW (win), lab); > gtk_window_set_child (GTK_WINDOW (win), lab);
> >
18c23 18c23
< app = gtk_application_new ("com.github.ToshioCP.pr4", G_APPLICATION_FLAGS_NONE); < app = gtk_application_new ("com.github.ToshioCP.pr4", G_APPLICATION_DEFAULT_FLAGS);
--- ---
> app = gtk_application_new ("com.github.ToshioCP.lb1", G_APPLICATION_FLAGS_NONE); > app = gtk_application_new ("com.github.ToshioCP.lb1", G_APPLICATION_DEFAULT_FLAGS);
~~~ ~~~
This tells us: This tells us:
- A signal handler `app_activate` doesn't have `user_data` parameter.
If the fourth argument of `g_signal_connect` is NULL, you can leave out `user_data`.
- The definition of a new variable `lab` is added. - The definition of a new variable `lab` is added.
- The title of the window is changed. - The title of the window is changed.
- A label is created and connected to the window as a child. - A label is created and connected to the window as a child.
@ -98,22 +104,22 @@ An application can have more than one top-level window.
### GtkButton ### GtkButton
The next widget to introduce is GtkButton. The next widget is GtkButton.
It displays a button on the screen with a label or icon on it. It displays a button on the screen with a label or icon on it.
In this subsection, we will make a button with a label. In this subsection, we will make a button with a label.
When the button is clicked, it emits a "clicked" signal. When the button is clicked, it emits a "clicked" signal.
The following program shows how to catch the signal to then do something. The following program shows how to catch the signal and do something.
~~~C ~~~C
1 #include <gtk/gtk.h> 1 #include <gtk/gtk.h>
2 2
3 static void 3 static void
4 click_cb (GtkButton *btn, gpointer user_data) { 4 click_cb (GtkButton *btn) {
5 g_print ("Clicked.\n"); 5 g_print ("Clicked.\n");
6 } 6 }
7 7
8 static void 8 static void
9 app_activate (GApplication *app, gpointer user_data) { 9 app_activate (GApplication *app) {
10 GtkWidget *win; 10 GtkWidget *win;
11 GtkWidget *btn; 11 GtkWidget *btn;
12 12
@ -133,7 +139,7 @@ The following program shows how to catch the signal to then do something.
26 GtkApplication *app; 26 GtkApplication *app;
27 int stat; 27 int stat;
28 28
29 app = gtk_application_new ("com.github.ToshioCP.lb2", G_APPLICATION_FLAGS_NONE); 29 app = gtk_application_new ("com.github.ToshioCP.lb2", G_APPLICATION_DEFAULT_FLAGS);
30 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL); 30 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
31 stat =g_application_run (G_APPLICATION (app), argc, argv); 31 stat =g_application_run (G_APPLICATION (app), argc, argv);
32 g_object_unref (app); 32 g_object_unref (app);
@ -145,7 +151,7 @@ The following program shows how to catch the signal to then do something.
Look at the line 17 to 19. Look at the line 17 to 19.
First, it creates a GtkButton instance `btn` with a label "Click me". First, it creates a GtkButton instance `btn` with a label "Click me".
Then, adds the button to the window `win` as a child. Then, adds the button to the window `win` as a child.
Finally, connects a "clicked" signal of the button to a handler (function) `click_cb`. Finally, connects a "clicked" signal of the button to the handler `click_cb`.
So, if `btn` is clicked, the function `click_cb` is invoked. So, if `btn` is clicked, the function `click_cb` is invoked.
The suffix "cb" means "call back". The suffix "cb" means "call back".
@ -159,74 +165,78 @@ Click the button (it is a large button, you can click everywhere in the window),
It shows the handler was invoked by clicking the button. It shows the handler was invoked by clicking the button.
It's good that we make sure that the clicked signal was caught and the handler was invoked by using `g_print`. It's good that we make sure that the clicked signal was caught and the handler was invoked by using `g_print`.
However, using g_print is out of harmony with Gtk which is a GUI library. However, using `g_print` is out of harmony with GTK, which is a GUI library.
So, we will change the handler. So, we will change the handler.
The following code is `lb3.c`. The following code is `lb3.c`.
~~~C ~~~C
1 static void 1 static void
2 click_cb (GtkButton *btn, gpointer user_data) { 2 click_cb (GtkButton *btn, GtkWindow *win) {
3 GtkWindow *win = GTK_WINDOW (user_data); 3 gtk_window_destroy (win);
4 gtk_window_destroy (win); 4 }
5 } 5
6 6 static void
7 static void 7 app_activate (GApplication *app) {
8 app_activate (GApplication *app, gpointer user_data) { 8 GtkWidget *win;
9 GtkWidget *win; 9 GtkWidget *btn;
10 GtkWidget *btn; 10
11 11 win = gtk_application_window_new (GTK_APPLICATION (app));
12 win = gtk_application_window_new (GTK_APPLICATION (app)); 12 gtk_window_set_title (GTK_WINDOW (win), "lb3");
13 gtk_window_set_title (GTK_WINDOW (win), "lb3"); 13 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300);
14 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300); 14
15 15 btn = gtk_button_new_with_label ("Close");
16 btn = gtk_button_new_with_label ("Quit"); 16 gtk_window_set_child (GTK_WINDOW (win), btn);
17 gtk_window_set_child (GTK_WINDOW (win), btn); 17 g_signal_connect (btn, "clicked", G_CALLBACK (click_cb), win);
18 g_signal_connect (btn, "clicked", G_CALLBACK (click_cb), win); 18
19 19 gtk_widget_show (win);
20 gtk_widget_show (win); 20 }
21 }
~~~ ~~~
And the difference between `lb2.c` and `lb3.c` is as follows. And the difference between `lb2.c` and `lb3.c` is as follows.
~~~ ~~~
$ cd misc; diff lb2.c lb3.c $ cd misc; diff lb2.c lb3.c
5c5,6 4,5c4,5
< click_cb (GtkButton *btn) {
< g_print ("Clicked.\n"); < g_print ("Clicked.\n");
--- ---
> GtkWindow *win = GTK_WINDOW (user_data); > click_cb (GtkButton *btn, GtkWindow *win) {
> gtk_window_destroy (win); > gtk_window_destroy (win);
14c15 14c14
< gtk_window_set_title (GTK_WINDOW (win), "lb2"); < gtk_window_set_title (GTK_WINDOW (win), "lb2");
--- ---
> gtk_window_set_title (GTK_WINDOW (win), "lb3"); > gtk_window_set_title (GTK_WINDOW (win), "lb3");
17c18 17c17
< btn = gtk_button_new_with_label ("Click me"); < btn = gtk_button_new_with_label ("Click me");
--- ---
> btn = gtk_button_new_with_label ("Quit"); > btn = gtk_button_new_with_label ("Close");
19c20 19c19
< g_signal_connect (btn, "clicked", G_CALLBACK (click_cb), NULL); < g_signal_connect (btn, "clicked", G_CALLBACK (click_cb), NULL);
--- ---
> g_signal_connect (btn, "clicked", G_CALLBACK (click_cb), win); > g_signal_connect (btn, "clicked", G_CALLBACK (click_cb), win);
29c30 29c29
< app = gtk_application_new ("com.github.ToshioCP.lb2", G_APPLICATION_FLAGS_NONE); < app = gtk_application_new ("com.github.ToshioCP.lb2", G_APPLICATION_DEFAULT_FLAGS);
--- ---
> app = gtk_application_new ("com.github.ToshioCP.lb3", G_APPLICATION_FLAGS_NONE); > app = gtk_application_new ("com.github.ToshioCP.lb3", G_APPLICATION_DEFAULT_FLAGS);
35d35 35d34
< <
~~~ ~~~
The changes are: The changes are:
- The function `g_print` in `lb2.c` was deleted and the two lines above are inserted instead. - The function `g_print` in `lb2.c` was deleted and two lines are inserted.
- The label of `btn` is changed from "Click me" to "Quit". - `click_cb` has the second parameter, which comes from the fourth argument of the `g_signal_connect` at the line 19.
One thing to be careful is the types are different between the second parameter of `click_cb` and the fourth argument of `g_signal_connect`.
The former is `GtkWindow *` and the latter is `GtkWidget *`.
The compiler doesn't complain because `g_signal_connect` uses gpointer (general type of pointer).
In this program the instance pointed by `win` is a GtkApplicationWindow object.
It is a descendant of GtkWindow and GtkWidget class, so both `GtkWindow *` and `GtkWidget *` are correct types for the instance.
- `gtk_destroy (win)` destroys the top-level window. Then the application quits.
- The label of `btn` is changed from "Click me" to "Close".
- The fourth argument of `g_signal_connect` is changed from `NULL` to `win`. - The fourth argument of `g_signal_connect` is changed from `NULL` to `win`.
The most important change is the fourth argument of `g_signal_connect`. The most important change is the fourth argument of the `g_signal_connect`.
This argument is described as "data to pass to handler" in the definition of `g_signal_connect` in [GObject API Reference](https://docs.gtk.org/gobject/func.signal_connect.html). This argument is described as "data to pass to handler" in the definition of [`g_signal_connect`](https://docs.gtk.org/gobject/func.signal_connect.html).
Therefore, `win` which is a pointer to GtkApplicationWindow is passed to the handler as a second parameter `user_data`.
The handler then casts it to a pointer to GtkWindow and calls `gtk_window_destroy` to destroy the top-level window.
The application then quits.
### GtkBox ### GtkBox
@ -245,15 +255,14 @@ After this, the Widgets are connected as the following diagram.
![Parent-child relationship](../image/box.png) ![Parent-child relationship](../image/box.png)
The program `lb4.c` includes these widgets. The program `lb4.c` is as follows.
It is as follows.
~~~C ~~~C
1 #include <gtk/gtk.h> 1 #include <gtk/gtk.h>
2 2
3 static void 3 static void
4 click1_cb (GtkButton *btn, gpointer user_data) { 4 click1_cb (GtkButton *btn) {
5 const gchar *s; 5 const char *s;
6 6
7 s = gtk_button_get_label (btn); 7 s = gtk_button_get_label (btn);
8 if (g_strcmp0 (s, "Hello.") == 0) 8 if (g_strcmp0 (s, "Hello.") == 0)
@ -263,68 +272,96 @@ It is as follows.
12 } 12 }
13 13
14 static void 14 static void
15 click2_cb (GtkButton *btn, gpointer user_data) { 15 click2_cb (GtkButton *btn, GtkWindow *win) {
16 GtkWindow *win = GTK_WINDOW (user_data); 16 gtk_window_destroy (win);
17 gtk_window_destroy (win); 17 }
18 } 18
19 19 static void
20 static void 20 app_activate (GApplication *app) {
21 app_activate (GApplication *app, gpointer user_data) { 21 GtkWidget *win;
22 GtkWidget *win; 22 GtkWidget *box;
23 GtkWidget *box; 23 GtkWidget *btn1;
24 GtkWidget *btn1; 24 GtkWidget *btn2;
25 GtkWidget *btn2; 25
26 26 win = gtk_application_window_new (GTK_APPLICATION (app));
27 win = gtk_application_window_new (GTK_APPLICATION (app)); 27 gtk_window_set_title (GTK_WINDOW (win), "lb4");
28 gtk_window_set_title (GTK_WINDOW (win), "lb4"); 28 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300);
29 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300); 29
30 30 box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
31 box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5); 31 gtk_box_set_homogeneous (GTK_BOX (box), TRUE);
32 gtk_box_set_homogeneous (GTK_BOX (box), TRUE); 32 gtk_window_set_child (GTK_WINDOW (win), box);
33 gtk_window_set_child (GTK_WINDOW (win), box); 33
34 34 btn1 = gtk_button_new_with_label ("Hello.");
35 btn1 = gtk_button_new_with_label ("Hello."); 35 g_signal_connect (btn1, "clicked", G_CALLBACK (click1_cb), NULL);
36 g_signal_connect (btn1, "clicked", G_CALLBACK (click1_cb), NULL); 36
37 37 btn2 = gtk_button_new_with_label ("Close");
38 btn2 = gtk_button_new_with_label ("Quit"); 38 g_signal_connect (btn2, "clicked", G_CALLBACK (click2_cb), win);
39 g_signal_connect (btn2, "clicked", G_CALLBACK (click2_cb), win); 39
40 40 gtk_box_append (GTK_BOX (box), btn1);
41 gtk_box_append (GTK_BOX (box), btn1); 41 gtk_box_append (GTK_BOX (box), btn2);
42 gtk_box_append (GTK_BOX (box), btn2); 42
43 43 gtk_widget_show (win);
44 gtk_widget_show (win); 44 }
45 } 45
46 46 int
47 int 47 main (int argc, char **argv) {
48 main (int argc, char **argv) { 48 GtkApplication *app;
49 GtkApplication *app; 49 int stat;
50 int stat; 50
51 51 app = gtk_application_new ("com.github.ToshioCP.lb4", G_APPLICATION_DEFAULT_FLAGS);
52 app = gtk_application_new ("com.github.ToshioCP.lb4", G_APPLICATION_FLAGS_NONE); 52 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
53 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL); 53 stat =g_application_run (G_APPLICATION (app), argc, argv);
54 stat =g_application_run (G_APPLICATION (app), argc, argv); 54 g_object_unref (app);
55 g_object_unref (app); 55 return stat;
56 return stat; 56 }
57 }
~~~ ~~~
Look at the function `app_activate`. Look at the function `app_activate`.
After the creation of a GtkApplicationWindow instance, a GtkBox instance is created. After the creation of a GtkApplicationWindow instance, a GtkBox instance is created.
box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); ~~~C
gtk_box_set_homogeneous (GTK_BOX (box), TRUE); box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
gtk_box_set_homogeneous (GTK_BOX (box), TRUE);
~~~
The first argument arranges the children of the box vertically. The first argument arranges the children of the box vertically.
The orientation constants are defined like this:
- GTK\_ORIENTATION\_VERTICAL: the children widgets are arranged vertically
- GTK\_ORIENTATION\_HORIZONTAL: the children widgets are arranged horizontally
The second argument is the size between the children. The second argument is the size between the children.
The unit of the length is pixel.
The next function fills the box with the children, giving them the same space. The next function fills the box with the children, giving them the same space.
After that, two buttons `btn1` and `btn2` are created and the signal handlers are set. After that, two buttons `btn1` and `btn2` are created and the signal handlers are set.
Then, these two buttons are appended to the box. Then, these two buttons are appended to the box.
~~~C
1 static void
2 click1_cb (GtkButton *btn) {
3 const char *s;
4
5 s = gtk_button_get_label (btn);
6 if (g_strcmp0 (s, "Hello.") == 0)
7 gtk_button_set_label (btn, "Good-bye.");
8 else
9 gtk_button_set_label (btn, "Hello.");
10 }
~~~
The function `gtk_button_get_lable` returns a text from the label.
The string is owned by the button and you can't modify or free it.
The `const` qualifier is necessary for the string `s`.
If you change the string, your compiler will give you a waring.
You always need to be careful with the const qualifier when you see the GTK 4 API reference.
![Screenshot of the box](../image/screenshot_lb4.png) ![Screenshot of the box](../image/screenshot_lb4.png)
The handler corresponds to `btn1` toggles its label. The handler corresponds to `btn1` toggles its label.
The handler corresponds to `btn2` destroys the top-level window and the application quits. The handler corresponds to `btn2` destroys the top-level window and the application quits.
Up: [Readme.md](../Readme.md), Prev: [Section 3](sec3.md), Next: [Section 5](sec5.md) Up: [README.md](../README.md), Prev: [Section 3](sec3.md), Next: [Section 5](sec5.md)

View file

@ -1,4 +1,4 @@
Up: [Readme.md](../Readme.md), Prev: [Section 4](sec4.md), Next: [Section 6](sec6.md) Up: [README.md](../README.md), Prev: [Section 4](sec4.md), Next: [Section 6](sec6.md)
# Widgets (2) # Widgets (2)
@ -14,7 +14,7 @@ See the sample program `tfv1.c` below.
1 #include <gtk/gtk.h> 1 #include <gtk/gtk.h>
2 2
3 static void 3 static void
4 app_activate (GApplication *app, gpointer user_data) { 4 app_activate (GApplication *app) {
5 GtkWidget *win; 5 GtkWidget *win;
6 GtkWidget *tv; 6 GtkWidget *tv;
7 GtkTextBuffer *tb; 7 GtkTextBuffer *tb;
@ -28,7 +28,7 @@ See the sample program `tfv1.c` below.
15 "He cut it, then there was a small cute baby girl in it. " 15 "He cut it, then there was a small cute baby girl in it. "
16 "The girl was shining faintly. " 16 "The girl was shining faintly. "
17 "He thought this baby girl is a gift from Heaven and took her home.\n" 17 "He thought this baby girl is a gift from Heaven and took her home.\n"
18 "His wife was surprized at his tale. " 18 "His wife was surprized at his story. "
19 "They were very happy because they had no children. " 19 "They were very happy because they had no children. "
20 ; 20 ;
21 win = gtk_application_window_new (GTK_APPLICATION (app)); 21 win = gtk_application_window_new (GTK_APPLICATION (app));
@ -50,7 +50,7 @@ See the sample program `tfv1.c` below.
37 GtkApplication *app; 37 GtkApplication *app;
38 int stat; 38 int stat;
39 39
40 app = gtk_application_new ("com.github.ToshioCP.tfv1", G_APPLICATION_FLAGS_NONE); 40 app = gtk_application_new ("com.github.ToshioCP.tfv1", G_APPLICATION_DEFAULT_FLAGS);
41 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL); 41 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
42 stat = g_application_run (G_APPLICATION (app), argc, argv); 42 stat = g_application_run (G_APPLICATION (app), argc, argv);
43 g_object_unref (app); 43 g_object_unref (app);
@ -62,16 +62,26 @@ Look at line 25.
A GtkTextView instance is created and its pointer is assigned to `tv`. A GtkTextView instance is created and its pointer is assigned to `tv`.
When the GtkTextView instance is created, a GtkTextBuffer instance is also created and connected to the GtkTextView automatically. When the GtkTextView instance is created, a GtkTextBuffer instance is also created and connected to the GtkTextView automatically.
"GtkTextBuffer instance" will be referred to simply as "GtkTextBuffer" or "buffer". "GtkTextBuffer instance" will be referred to simply as "GtkTextBuffer" or "buffer".
In the next line, the pointer to the buffer is got and assigned to `tb`. In the next line, the pointer to the buffer is assigned to `tb`.
Then, the text from line 10 to 20 is assigned to the buffer. Then, the text from line 10 to 20 is assigned to the buffer.
If the third argument of `gtk_text_buffer_set_text` is a positive integer, it is the length of the text.
It it is -1, the string terminates with NULL.
GtkTextView has a wrap mode. GtkTextView has a wrap mode.
When it is set to `GTK_WRAP_WORD_CHAR`, text wraps in between words, or if that is not enough, also between graphemes. When it is set to `GTK_WRAP_WORD_CHAR`, text wraps in between words, or if that is not enough, also between graphemes.
Wrap mode is written in [Gtk\_WrapMode](https://docs.gtk.org/gtk4/enum.WrapMode.html) in the GTK 4 API document.
In line 30, `tv` is added to `win` as a child. In line 30, `tv` is added to `win` as a child.
Now compile and run it. Now compile and run it.
```
$ cd src/tfv
$ comp tfv1
$ ./a.out
```
![GtkTextView](../image/screenshot_tfv1.png) ![GtkTextView](../image/screenshot_tfv1.png)
There's an I-beam pointer in the window. There's an I-beam pointer in the window.
@ -79,19 +89,19 @@ You can add or delete any characters on the GtkTextView,
and your changes are kept in the GtkTextBuffer. and your changes are kept in the GtkTextBuffer.
If you add more characters beyond the limit of the window, the height increases and the window extends. If you add more characters beyond the limit of the window, the height increases and the window extends.
If the height gets bigger than the height of the display screen, you won't be If the height gets bigger than the height of the display screen, you won't be
able to control the size of the window, and change it back to the original size. able to control the size of the window or change it back to the original size.
This is a problem and shows that there is a bug in our program. This is a problem, that is to say a bug.
This can solve it by adding a GtkScrolledWindow between the GtkApplicationWindow and GtkTextView. This can be solved by adding a GtkScrolledWindow between the GtkApplicationWindow and GtkTextView.
### GtkScrolledWindow ### GtkScrolledWindow
What we need to do is: What we need to do is:
- Create a GtkScrolledWindow and insert it as a child of the GtkApplicationWindow; and - Create a GtkScrolledWindow and insert it as a child of the GtkApplicationWindow
- Insert the GtkTextView widget to the GtkScrolledWindow as a child. - Insert the GtkTextView widget to the GtkScrolledWindow as a child.
Modify `tfv1.c` and save it as `tfv2.c`. Modify `tfv1.c` and save it as `tfv2.c`.
The difference between these two files is small. There is only a few difference between these two files.
~~~ ~~~
$ cd tfv; diff tfv1.c tfv2.c $ cd tfv; diff tfv1.c tfv2.c
@ -106,18 +116,18 @@ $ cd tfv; diff tfv1.c tfv2.c
--- ---
> gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv); > gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);
40c44 40c44
< app = gtk_application_new ("com.github.ToshioCP.tfv1", G_APPLICATION_FLAGS_NONE); < app = gtk_application_new ("com.github.ToshioCP.tfv1", G_APPLICATION_DEFAULT_FLAGS);
--- ---
> app = gtk_application_new ("com.github.ToshioCP.tfv2", G_APPLICATION_FLAGS_NONE); > app = gtk_application_new ("com.github.ToshioCP.tfv2", G_APPLICATION_DEFAULT_FLAGS);
~~~ ~~~
Here is the complete code of `tfv2.c`. The whole code of `tfv2.c` is as follows.
~~~C ~~~C
1 #include <gtk/gtk.h> 1 #include <gtk/gtk.h>
2 2
3 static void 3 static void
4 app_activate (GApplication *app, gpointer user_data) { 4 app_activate (GApplication *app) {
5 GtkWidget *win; 5 GtkWidget *win;
6 GtkWidget *scr; 6 GtkWidget *scr;
7 GtkWidget *tv; 7 GtkWidget *tv;
@ -132,7 +142,7 @@ Here is the complete code of `tfv2.c`.
16 "He cut it, then there was a small cute baby girl in it. " 16 "He cut it, then there was a small cute baby girl in it. "
17 "The girl was shining faintly. " 17 "The girl was shining faintly. "
18 "He thought this baby girl is a gift from Heaven and took her home.\n" 18 "He thought this baby girl is a gift from Heaven and took her home.\n"
19 "His wife was surprized at his tale. " 19 "His wife was surprized at his story. "
20 "They were very happy because they had no children. " 20 "They were very happy because they had no children. "
21 ; 21 ;
22 win = gtk_application_window_new (GTK_APPLICATION (app)); 22 win = gtk_application_window_new (GTK_APPLICATION (app));
@ -157,7 +167,7 @@ Here is the complete code of `tfv2.c`.
41 GtkApplication *app; 41 GtkApplication *app;
42 int stat; 42 int stat;
43 43
44 app = gtk_application_new ("com.github.ToshioCP.tfv2", G_APPLICATION_FLAGS_NONE); 44 app = gtk_application_new ("com.github.ToshioCP.tfv2", G_APPLICATION_DEFAULT_FLAGS);
45 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL); 45 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
46 stat = g_application_run (G_APPLICATION (app), argc, argv); 46 stat = g_application_run (G_APPLICATION (app), argc, argv);
47 g_object_unref (app); 47 g_object_unref (app);
@ -166,7 +176,8 @@ Here is the complete code of `tfv2.c`.
~~~ ~~~
Compile and run it. Compile and run it.
Notice how this time the window doesn't extend when you type a lot of characters,
it just scrolls and displays a slider.
Up: [Readme.md](../Readme.md), Prev: [Section 4](sec4.md), Next: [Section 6](sec6.md) Now, the window doesn't extend even if you type a lot of characters,
it just scrolls.
Up: [README.md](../README.md), Prev: [Section 4](sec4.md), Next: [Section 6](sec6.md)

View file

@ -1,6 +1,6 @@
Up: [README.md](../README.md), Prev: [Section 5](sec5.md), Next: [Section 7](sec7.md) Up: [README.md](../README.md), Prev: [Section 5](sec5.md), Next: [Section 7](sec7.md)
# String and memory management # Strings and memory management
GtkTextView and GtkTextBuffer have functions that use string parameters or return a string. GtkTextView and GtkTextBuffer have functions that use string parameters or return a string.
The knowledge of strings and memory management is useful to understand how to use these functions. The knowledge of strings and memory management is useful to understand how to use these functions.
@ -8,12 +8,11 @@ The knowledge of strings and memory management is useful to understand how to us
## String and memory ## String and memory
A String is an array of characters that is terminated with '\0'. A String is an array of characters that is terminated with '\0'.
Strings are not a C type such as char, int, float or double, String is not a C type such as char, int, float or double, but a character array.
but exist as a pointer to a character array. They behaves like a string type It behaves like a string in other languages.
which you may be familiar from other languages. So, the pointer is often called 'a string'.
So, this pointer is often called 'a string'.
In the following, `a` and `b` defined as character arrays, and are strings. The following is a sample program.
~~~C ~~~C
char a[10], *b; char a[10], *b;
@ -30,50 +29,50 @@ b = a;
/* *(++b) is 'e' */ /* *(++b) is 'e' */
~~~ ~~~
The array `a` has `char` elements and the size of ten. An array `a` is defined as a `char` type array and its size is ten.
The first six elements are 'H', 'e', 'l', 'l', 'o' and '\0'. The first five elements are 'H', 'e', 'l', 'l', 'o'.
This array represents the string "Hello". They are character codes.
The first five elements are character codes that correspond to the characters. For example, 'H' is the same as 0x48 or 72.
The sixth element is '\0', which is the same as zero, The sixth element is '\0', which is the same as zero, and indicates that the sequence of the data ends there.
and indicates that the string ends there. The array represents the string "Hello".
The size of the array is 10, so 4 bytes aren't used, but that's OK,
they are just ignored. The size of the array is 10, so 4 bytes aren't used.
But it's OK.
They are just ignored.
(If 'a' is defined out of functions or its class is static, they are assigned with zero.
Otherwise, that is to say, the class is auto or register, they are undefined.)
The variable 'b' is a pointer to a character. The variable 'b' is a pointer to a character.
Because `b` is assigned to be `a`, `a` and `b` point the same character ('H'). It is assigned with `a`, so `b` points the first element of `a` (character 'H').
The variable `a` is defined as an array and it can't be changed. The array `a` is immutable.
It always point the top address of the array. So `a=a+1` causes syntax error.
On the other hand, 'b' is a pointer, which is mutable, so `b` can be change.
It is then possible to write statements like `++b`, which means take the value in b (n address),
increase it by one, and store that back in `b`.
If a pointer is NULL, it points to nothing. On the other hand, 'b' is a pointer type variable, which is mutable.
So, `++b`, which increases `b` by one, is allowed.
If a pointer is NULL, it points nothing.
So, the pointer is not a string. So, the pointer is not a string.
A NULL string on the other hand will be a pointer which points to a location It is different from empty string.
that contains `\0`, which is a string of length 0 (or ""). Empty string is a pointer points `\0`.
Programs that use strings will include bugs if you aren't careful when using NULL pointers.
Another annoying problem is the memory that a string is allocated.
There are four cases: There are four cases:
- The string is read only; - The string is read only
- The string is in static memory area; - The string is in static memory area
- The string is in stack; and - The string is in stack
- The string is in memory allocated from the heap area. - The string is in memory allocated from the heap area
## Read only string ## Read only string
A string literal in a C program is surrounded by double quotes and written as the following: A string literal is surrounded by double quotes like this:
~~~C ~~~C
char *s; char *s;
s = "Hello" s = "Hello"
~~~ ~~~
"Hello" is a string literal, and is stored in program memory. "Hello" is a string literal, and is read only.
A string literal is read only.
In the program above, `s` points the string literal.
So, the following program is illegal. So, the following program is illegal.
~~~C ~~~C
@ -83,25 +82,38 @@ So, the following program is illegal.
The result is undefined. The result is undefined.
Probably a bad thing will happen, for example, a segmentation fault. Probably a bad thing will happen, for example, a segmentation fault.
NOTE: The memory of the literal string is allocated when the program is NOTE: The memory of the literal string is allocated when the program is compiled.
compiled. It is possible to view all the literal strings defined in your program It is possible to see the literal strings with `strings` command.
by using the `string` command.
~~~
$ strings src/tvf/a.out
/lib64/ld-linux-x86-64.so.2
cN<5
... ... ...
... ... ...
Once upon a time, there was an old man who was called Taketori-no-Okina. It is a japanese word that means a man whose work is making bamboo baskets.
One day, he went into a mountain and found a shining bamboo. "What a mysterious bamboo it is!," he said. He cut it, then there was a small cute baby girl in it. The girl was shining faintly. He thought this baby girl is a gift from Heaven and took her home.
His wife was surprized at his story. They were very happy because they had no children.
... ... ...
... ... ...
~~~
It tells us that literal strings are embedded in program binary codes.
## Strings defined as arrays ## Strings defined as arrays
If a string is defined as an array, it's in either stored in the static memory area or stack. If a string is defined as an array, it's stored in static memory area or stack.
This depends on the class of the array. It depends on the class of the array.
If the array's class is `static`, then it's placed in static memory area. If the array's class is `static`, then it's placed in static memory area.
This allocation and memory address is fixed for the life of the program. The allocated memory lives for the life of the program.
This area can be changed and is writable. This area is writable.
If the array's class is `auto`, then it's placed in stack. If the array's class is `auto`, it's placed in stack.
If the array is defined inside a function, its default class is `auto`. If the array is defined inside a function, its default class is `auto`.
The stack area will disappear when the function exits and returns to the caller. The stack area will disappear when the function returns to the caller.
Arrays defined on the stack are writable. Arrays defined on the stack are writable.
~~~C ~~~C
static char a[] = {'H', 'e', 'l', 'l', 'o', '\0'}; static char a[] = {'H', 'e', 'l', 'l', 'o', '\0'};
void void
@ -116,30 +128,28 @@ print_strings (void) {
} }
~~~ ~~~
The array `a` is defined externally to a function and is global in its scope. The array `a` is defined out of functions.
Such variables are placed in static memory area even if the `static` class is left out. It is placed in the static memory area even if the `static` class is left out.
The compiler calculates the number of the elements in the right hand side (six), The compiler calculates the number of the elements (six) and allocates six bytes in the static memory area.
and then creates code that allocates six bytes in the static memory area and copies the data to this memory. Then, it copies "Hello" literal string data to the memory.
The array `b` is defined inside the function The array `b` is defined inside the function, so its class is `auto`.
so its class is `auto`.
The compiler calculates the number of the elements in the string literal. The compiler calculates the number of the elements in the string literal.
It has six elements as the zero termination character is also included. It is six because it has '\0' terminator.
The compiler creates code which allocates six bytes memory in the stack and copies the data to the memory. The compiler allocates six bytes in the stack and copies "Hello" litaral string to the stack memory.
Both `a` and `b` are writable. Both `a` and `b` are writable.
The memory is managed by the executable program. The memory is allocated and freed by the program automatically so you don't need to allocate or free.
You don't need your program to allocate or free the memory for `a` and `b`. The array `a` is alive during the program's life time.
The array `a` is created then the program is first run and remains for the life of the program. The array `b` is alive when the function is called until the function returns to the caller.
The array `b` is created on the stack then the function is called, disappears when the function returns.
## Strings in the heap area ## Strings in the heap area
You can also get, use and release memory from the heap area. You can get, use and release memory from the heap area.
The standard C library provides `malloc` to get memory and `free` to put back memory. The standard C library provides `malloc` to get memory and `free` to put back memory.
GLib provides the functions `g_new` and `g_free` to do the same thing, with support for GLib provides the functions `g_new` and `g_free`.
some additional GLib functionality. They are similar to `malloc` and `free`.
~~~C ~~~C
g_new (struct_type, n_struct) g_new (struct_type, n_struct)
@ -197,9 +207,9 @@ s = g_strdup ("Hello");
g_free (s); g_free (s);
~~~ ~~~
The string literal "Hello" has 6 bytes because the string has '\0' at the end it. The string literal "Hello" has 6 bytes because the string has '\0' at the end.
`g_strdup` gets 6 bytes from the heap area and copies the string to the memory. `g_strdup` gets 6 bytes from the heap area and copies the string to the memory.
`s` is assigned the top address of the memory. `s` is assigned the start address of the memory.
`g_free` returns the memory to the heap area. `g_free` returns the memory to the heap area.
`g_strdup` is described in [GLib API Reference](https://docs.gtk.org/glib/func.strdup.html). `g_strdup` is described in [GLib API Reference](https://docs.gtk.org/glib/func.strdup.html).
@ -207,24 +217,14 @@ The following is extracted from the reference.
> The returned string should be freed with `g_free()` when no longer needed. > The returned string should be freed with `g_free()` when no longer needed.
The function reference will describe if the returned value needs to be freed. If you forget to free the allocated memory it will remain until the program ends.
If you forget to free the allocated memory it will remain allocated. Repeated use will cause Repeated allocation and no freeing cause memory leak.
more memory to be allocated to the program, which will grow over time. This is called a memory leak, It is a bug and may bring a serious problem.
and the only way to address this bug is to close the program (and restart it),
which will automatically release all of the programs memory back to the system.
Some GLib functions return a string which mustn't be freed by the caller. ## const qualifier
~~~C A `const` qualified variable can be assigned to initialize it.
const char * Once it is initialized, it is never allowed to change or free.
g_quark_to_string (GQuark quark);
~~~
This function returns `const char*` type.
The qualifier `const` means that the returned value is immutable.
The characters pointed by the returned value aren't be allowed to be changed or freed.
If a variable is qualified with `const`, the variable can't be assigned except during initialization.
~~~C ~~~C
const int x = 10; /* initialization is OK. */ const int x = 10; /* initialization is OK. */
@ -232,4 +232,25 @@ const int x = 10; /* initialization is OK. */
x = 20; /* This is illegal because x is qualified with const */ x = 20; /* This is illegal because x is qualified with const */
~~~ ~~~
If a function returns `const char*` type, the string can't be changed or freed.
If a function has a `const char *` type parameter, it ensures that the parameter is not changed in the function.
~~~C
// You never change or free the returned string.
const char*
gtk_label_get_text (
GtkLabel* self
)
// Str keeps itself during the function runs
void
gtk_label_set_text (
GtkLabel* self,
const char* str
)
~~~
Up: [README.md](../README.md), Prev: [Section 5](sec5.md), Next: [Section 7](sec7.md) Up: [README.md](../README.md), Prev: [Section 5](sec5.md), Next: [Section 7](sec7.md)

View file

@ -1,4 +1,4 @@
Up: [Readme.md](../Readme.md), Prev: [Section 6](sec6.md), Next: [Section 8](sec8.md) Up: [README.md](../README.md), Prev: [Section 6](sec6.md), Next: [Section 8](sec8.md)
# Widgets (3) # Widgets (3)
@ -6,51 +6,40 @@ Up: [Readme.md](../Readme.md), Prev: [Section 6](sec6.md), Next: [Section 8](se
### G\_APPLICATION\_HANDLES\_OPEN flag ### G\_APPLICATION\_HANDLES\_OPEN flag
The GtkTextView, GtkTextBuffer and GtkScrolledWindow widgets have given us a minimum editor We made a very simple editor in the previous section with GtkTextView, GtkTextBuffer and GtkScrolledWindow.
in the previous section. We will add file-reading ability to the program and improve it to a file viewer.
We will now add a function to read a file and rework the program into a file viewer.
There are many ways to implement the function and
because this is a tutorial for beginners, we'll take the easiest one.
When the program starts, we will give the filename to open as an argument. The easiest way to give a filename is to use a command line argument.
$ ./a.out filename $ ./a.out filename
It will open the file and insert its contents into the GtkTextBuffer. The program will open the file and insert its contents into the GtkTextBuffer.
To do this, we need to know how GtkApplication (or GApplication) recognizes arguments. To do this, we need to know how GtkApplication (or GApplication) recognizes arguments.
This is described in the [GIO API Reference, Application](https://docs.gtk.org/gio/class.Application.html). This is described in the [GIO API Reference -- Application](https://docs.gtk.org/gio/class.Application.html).
When GtkApplication is created, a flag (with the type GApplicationFlags) is provided as an argument. When GtkApplication is created, a flag (GApplicationFlags) is given as an argument.
~~~C ~~~C
GtkApplication * GtkApplication *
gtk_application_new (const gchar *application_id, GApplicationFlags flags); gtk_application_new (const gchar *application_id, GApplicationFlags flags);
~~~ ~~~
This tutorial explains only two flags, `G_APPLICATION_FLAGS_NONE` and `G_APPLICATION_HANDLES_OPEN`. This tutorial explains only two flags, `G_APPLICATION_DEFAULT_FLAGS` and `G_APPLICATION_HANDLES_OPEN`.
(`G_APPLICATION_FLAGS_NONE` was used instead of `G_APPLICATION_DEFAULT_FLAGS` before GIO version2.73.3 (GLib 2.73.3 5/Aug/2022).
Some GTK 4 applications still use `G_APPLICATION_FLAGS_NONE`.
But now it is deprecated and `G_APPLICATION_DEFAULT_FLAGS` is recommended.)
If you want to handle command line arguments, the `G_APPLICATION_HANDLES_COMMAND_LINE` flag is what you need. If you want to handle command line arguments, the `G_APPLICATION_HANDLES_COMMAND_LINE` flag is what you need.
How to use the new application method is described in [GIO API Reference, g\_application\_run](https://docs.gtk.org/gio/method.Application.run.html),
and the flag is described in the [GIO API Reference, ApplicationFlags](https://docs.gtk.org/gio/flags.ApplicationFlags.html).
~~~ For further information, see [GIO API Reference -- ApplicationFlags](https://docs.gtk.org/gio/flags.ApplicationFlags.html) and
GApplicationFlags' Members [GIO API Reference -- g\_application\_run](https://docs.gtk.org/gio/method.Application.run.html).
G_APPLICATION_FLAGS_NONE Default. (No argument allowed) We've already used `G_APPLICATION_DEFAULT_FLAGS`, as it is the simplest option, and no command line arguments are allowed.
... ... ... If you give arguments, an error will occur.
G_APPLICATION_HANDLES_OPEN This application handles opening files (in the primary instance).
... ... ...
~~~
There are ten flags in total, but we only need two of them so far.
We've already used `G_APPLICATION_FLAGS_NONE`, as
it is the simplest option, and no arguments are allowed.
If you provide arguments when running the application, an error will occur.
The flag `G_APPLICATION_HANDLES_OPEN` is the second simplest option. The flag `G_APPLICATION_HANDLES_OPEN` is the second simplest option.
It allows arguments but only files. It allows arguments but only files.
The application assumes all the arguments are filenames and we will use this flag when creating The application assumes all the arguments are filenames.
our GtkApplication.
~~~C ~~~C
app = gtk_application_new ("com.github.ToshioCP.tfv3", G_APPLICATION_HANDLES_OPEN); app = gtk_application_new ("com.github.ToshioCP.tfv3", G_APPLICATION_HANDLES_OPEN);
@ -58,7 +47,7 @@ app = gtk_application_new ("com.github.ToshioCP.tfv3", G_APPLICATION_HANDLES_OPE
### open signal ### open signal
Now, when the application starts, two signals can be emitted. When `G_APPLICATION_HANDLES_OPEN` flag is given to the application, two signals are available.
- activate signal --- This signal is emitted when there's no argument. - activate signal --- This signal is emitted when there's no argument.
- open signal --- This signal is emitted when there is at least one argument. - open signal --- This signal is emitted when there is at least one argument.
@ -66,48 +55,49 @@ Now, when the application starts, two signals can be emitted.
The handler of the "open" signal is defined as follows. The handler of the "open" signal is defined as follows.
~~~C ~~~C
void user_function (GApplication *application, void
gpointer files, open (
gint n_files, GApplication* self,
gchar *hint, gpointer files,
gpointer user_data) gint n_files,
gchar* hint,
gpointer user_data
)
~~~ ~~~
The parameters are: The parameters are:
- `application` --- the application (usually GtkApplication) - `self` --- the application instance (usually GtkApplication)
- `files` --- an array of GFiles. [array length=n\_files] [element-type GFile] - `files` --- an array of GFiles. [array length=n\_files] [element-type GFile]
- `n_files` --- the number of the elements of `files` - `n_files` --- the number of the elements of `files`
- `hint` --- a hint provided by the calling instance (usually it can be ignored) - `hint` --- a hint provided by the calling instance (usually it can be ignored)
- `user_data` --- user data set when the signal handler was connected. - `user_data` --- user data set when the signal handler was connected.
How to read a specified file (GFile) will be described next.
## Making a file viewer ## Making a file viewer
### What is a file viewer? ### What is a file viewer?
A file viewer is a program that displays the text file that is given as an argument. A file viewer is a program that displays text files.
Our file viewer will work as follows. Our file viewer will work as follows.
- When arguments are given, it treats the first argument as a filename and opens it. - When arguments are given, it recognizes the first argument as a filename and opens it.
- If opening the file succeeds, it reads the contents of the file and inserts it to GtkTextBuffer and then shows the window. - The second argument and after are ignored.
- If it fails to open the file, it will show an error message and quit. - If there's no argument, it shows an error message and quit.
- If there's no argument, it will shows an error message and quit. - If it successfully opens the file, it reads the contents of the file, inserts them to GtkTextBuffer and shows the window.
- If there are two or more arguments, the second one and any others are ignored. - If it fails to open the file, it shows an error message and quit.
The program which does this is shown below. The program is shown below.
~~~C ~~~C
1 #include <gtk/gtk.h> 1 #include <gtk/gtk.h>
2 2
3 static void 3 static void
4 app_activate (GApplication *app, gpointer user_data) { 4 app_activate (GApplication *app) {
5 g_print ("You need a filename argument.\n"); 5 g_printerr ("You need a filename argument.\n");
6 } 6 }
7 7
8 static void 8 static void
9 app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer user_data) { 9 app_open (GApplication *app, GFile ** files, int n_files, char *hint) {
10 GtkWidget *win; 10 GtkWidget *win;
11 GtkWidget *scr; 11 GtkWidget *scr;
12 GtkWidget *tv; 12 GtkWidget *tv;
@ -138,25 +128,26 @@ The program which does this is shown below.
37 gtk_widget_show (win); 37 gtk_widget_show (win);
38 } else { 38 } else {
39 if ((filename = g_file_get_path (files[0])) != NULL) { 39 if ((filename = g_file_get_path (files[0])) != NULL) {
40 g_print ("No such file: %s.\n", filename); 40 g_printerr ("No such file: %s.\n", filename);
41 g_free (filename); 41 g_free (filename);
42 } 42 } else
43 gtk_window_destroy (GTK_WINDOW (win)); 43 g_printerr ("File can't be opened.\n");
44 } 44 gtk_window_destroy (GTK_WINDOW (win));
45 } 45 }
46 46 }
47 int 47
48 main (int argc, char **argv) { 48 int
49 GtkApplication *app; 49 main (int argc, char **argv) {
50 int stat; 50 GtkApplication *app;
51 51 int stat;
52 app = gtk_application_new ("com.github.ToshioCP.tfv3", G_APPLICATION_HANDLES_OPEN); 52
53 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL); 53 app = gtk_application_new ("com.github.ToshioCP.tfv3", G_APPLICATION_HANDLES_OPEN);
54 g_signal_connect (app, "open", G_CALLBACK (app_open), NULL); 54 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
55 stat = g_application_run (G_APPLICATION (app), argc, argv); 55 g_signal_connect (app, "open", G_CALLBACK (app_open), NULL);
56 g_object_unref (app); 56 stat = g_application_run (G_APPLICATION (app), argc, argv);
57 return stat; 57 g_object_unref (app);
58 } 58 return stat;
59 }
~~~ ~~~
Save it as `tfv3.c`. Save it as `tfv3.c`.
@ -167,25 +158,29 @@ Then compile and run it.
![File viewer](../image/screenshot_tfv3.png) ![File viewer](../image/screenshot_tfv3.png)
Let's explain how the program `tfv3.c` works. The function `main` has only two changes from the previous version.
First, the function `main` has only two changes from the previous version.
- `G_APPLICATION_FLAGS_NONE` is replaced by `G_APPLICATION_HANDLES_OPEN`; and - `G_APPLICATION_DEFAULT_FLAGS` is replaced by `G_APPLICATION_HANDLES_OPEN`
- `g_signal_connect (app, "open", G_CALLBACK (on_open), NULL)` is added. - `g_signal_connect (app, "open", G_CALLBACK (app_open), NULL)` is added.
Next, the handler `app_activate` is added and is very simple. When the flag `G_APPLICATION_HANDLES_OPEN` is given to `gtk_application_new` function, the application behaves like this:
It just outputs the error message and
the application quits immediately because no window is created.
The main functionality is the in the handler `app_open`. It - If the application is run without command line arguments, it emits "activate" signal when it is activated.
- If the application is run with command line arguments, it emits "open" signal when it is activated.
- Creates GtkApplicationWindow, GtkScrolledWindow, GtkTextView and GtkTextBuffer and connects them together; The handler `app_activate` becomes very simple.
- Sets wrap mode to `GTK_WRAP_WORD_CHAR` in GtktextView; It just outputs the error message and return to the caller.
- Sets GtkTextView to non-editable because the program isn't an editor but only a viewer; Then the application quits immediately because no window is created.
- Reads the file and inserts the text into GtkTextBuffer (this will be explained in detail later); and
- If the file is not opened then outputs an error message and destroys the window. This makes the application quit.
The following is the important file reading part of the program and is shown again below. The main work is done in the handler `app_open`.
- Creates GtkApplicationWindow, GtkScrolledWindow, GtkTextView and GtkTextBuffer and connects them together
- Sets wrap mode to `GTK_WRAP_WORD_CHAR` in GtktextView
- Sets GtkTextView to non-editable because the program isn't an editor but only a viewer
- Reads the file and inserts the text into GtkTextBuffer (this will be explained later)
- If the file is not opened, outputs an error message and destroys the window. This makes the application quit.
The following is the file reading part of the program again.
~~~C ~~~C
if (g_file_load_contents (files[0], NULL, &contents, &length, NULL, NULL)) { if (g_file_load_contents (files[0], NULL, &contents, &length, NULL, NULL)) {
@ -198,50 +193,52 @@ if (g_file_load_contents (files[0], NULL, &contents, &length, NULL, NULL)) {
gtk_widget_show (win); gtk_widget_show (win);
} else { } else {
if ((filename = g_file_get_path (files[0])) != NULL) { if ((filename = g_file_get_path (files[0])) != NULL) {
g_print ("No such file: %s.\n", filename); g_printerr ("No such file: %s.\n", filename);
g_free (filename); g_free (filename);
} } else
g_printerr ("File can't be opened.\n");
gtk_window_destroy (GTK_WINDOW (win)); gtk_window_destroy (GTK_WINDOW (win));
} }
~~~ ~~~
The function `g_file_load_contents` loads the file contents into a buffer, The function `g_file_load_contents` loads the file contents into a temporary buffer,
which is automatically allocated and sets `contents` to point that buffer. which is automatically allocated and sets `contents` to point the buffer.
The length of the buffer is set to `length`. The length of the buffer is assigned to `length`.
It returns `TRUE` if the file's contents are successfully loaded and `FALSE` if an error occurs. It returns `TRUE` if the file's contents are successfully loaded or `FALSE` if an error occurs.
If you want to know the details about g\_file\_load\_contents, see [g file load contents](https://docs.gtk.org/gio/method.File.load_contents.html).
If this function succeeds, it inserts the contents into GtkTextBuffer, If it has successfully read the file, it inserts the contents into GtkTextBuffer,
frees the buffer pointed by `contents`, sets the title of the window, frees the temporary buffer pointed by `contents`, sets the title of the window,
frees the memories pointed by `filename` and then shows the window. frees the memories pointed by `filename` and then shows the window.
If it fails, it outputs an error message and destroys the window, causing the program to quit. If it fails, it outputs an error message and destroys the window and finally make the program quit.
## GtkNotebook ## GtkNotebook
GtkNotebook is a container widget that uses tabs and contains multiple children. GtkNotebook is a container widget that contains multiple widgets with tabs.
The child that is displayed depends on which tab has been selected. It shows only one child at a time.
Another child will be shown when its tab is clicked.
![GtkNotebook](../image/screenshot_gtk_notebook.png) ![GtkNotebook](../image/screenshot_gtk_notebook.png)
Looking at the screenshots above, The left image is the window at the startup.
the left one is the window at the startup. The file `pr1.c` is shown and its filename is in the left tab.
It shows the file `pr1.c` and the filename is in the left tab. After clicking on the right tab, the contents of the file `tfv1.c` is shown.
After clicking on the right tab, the contents of the file `tfv1.c` are shown instead. The right image is the screenshot.
This is shown in the right screenshot.
The GtkNotebook widget is inserted as a child of GtkApplicationWindow and contains a GtkScrolledWindow The following is `tfv4.c`.
for each file that is being displayed. It has GtkNoteBook widget.
The code to do this is given in `tfv4.c` and is: It is inserted as a child of GtkApplicationWindow and contains multiple GtkScrolledWindow.
~~~C ~~~C
1 #include <gtk/gtk.h> 1 #include <gtk/gtk.h>
2 2
3 static void 3 static void
4 app_activate (GApplication *app, gpointer user_data) { 4 app_activate (GApplication *app) {
5 g_print ("You need a filename argument.\n"); 5 g_printerr ("You need a filename argument.\n");
6 } 6 }
7 7
8 static void 8 static void
9 app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer user_data) { 9 app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint) {
10 GtkWidget *win; 10 GtkWidget *win;
11 GtkWidget *nb; 11 GtkWidget *nb;
12 GtkWidget *lab; 12 GtkWidget *lab;
@ -256,90 +253,81 @@ The code to do this is given in `tfv4.c` and is:
21 21
22 win = gtk_application_window_new (GTK_APPLICATION (app)); 22 win = gtk_application_window_new (GTK_APPLICATION (app));
23 gtk_window_set_title (GTK_WINDOW (win), "file viewer"); 23 gtk_window_set_title (GTK_WINDOW (win), "file viewer");
24 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300); 24 gtk_window_set_default_size (GTK_WINDOW (win), 600, 400);
25 gtk_window_maximize (GTK_WINDOW (win)); 25
26 26 nb = gtk_notebook_new ();
27 nb = gtk_notebook_new (); 27 gtk_window_set_child (GTK_WINDOW (win), nb);
28 gtk_window_set_child (GTK_WINDOW (win), nb); 28
29 29 for (i = 0; i < n_files; i++) {
30 for (i = 0; i < n_files; i++) { 30 if (g_file_load_contents (files[i], NULL, &contents, &length, NULL, NULL)) {
31 if (g_file_load_contents (files[i], NULL, &contents, &length, NULL, NULL)) { 31 scr = gtk_scrolled_window_new ();
32 scr = gtk_scrolled_window_new (); 32 tv = gtk_text_view_new ();
33 tv = gtk_text_view_new (); 33 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
34 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); 34 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR);
35 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR); 35 gtk_text_view_set_editable (GTK_TEXT_VIEW (tv), FALSE);
36 gtk_text_view_set_editable (GTK_TEXT_VIEW (tv), FALSE); 36 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);
37 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv); 37
38 38 gtk_text_buffer_set_text (tb, contents, length);
39 gtk_text_buffer_set_text (tb, contents, length); 39 g_free (contents);
40 g_free (contents); 40 if ((filename = g_file_get_basename (files[i])) != NULL) {
41 if ((filename = g_file_get_basename (files[i])) != NULL) { 41 lab = gtk_label_new (filename);
42 lab = gtk_label_new (filename); 42 g_free (filename);
43 g_free (filename); 43 } else
44 } else 44 lab = gtk_label_new ("");
45 lab = gtk_label_new (""); 45 gtk_notebook_append_page (GTK_NOTEBOOK (nb), scr, lab);
46 gtk_notebook_append_page (GTK_NOTEBOOK (nb), scr, lab); 46 nbp = gtk_notebook_get_page (GTK_NOTEBOOK (nb), scr);
47 nbp = gtk_notebook_get_page (GTK_NOTEBOOK (nb), scr); 47 g_object_set (nbp, "tab-expand", TRUE, NULL);
48 g_object_set (nbp, "tab-expand", TRUE, NULL); 48 } else if ((filename = g_file_get_path (files[i])) != NULL) {
49 } else if ((filename = g_file_get_path (files[i])) != NULL) { 49 g_printerr ("No such file: %s.\n", filename);
50 g_print ("No such file: %s.\n", filename); 50 g_free (filename);
51 g_free (filename); 51 } else
52 } else 52 g_printerr ("No valid file is given\n");
53 g_print ("No valid file is given\n"); 53 }
54 } 54 if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb)) > 0)
55 if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb)) > 0) 55 gtk_widget_show (win);
56 gtk_widget_show (win); 56 else
57 else 57 gtk_window_destroy (GTK_WINDOW (win));
58 gtk_window_destroy (GTK_WINDOW (win)); 58 }
59 } 59
60 60 int
61 int 61 main (int argc, char **argv) {
62 main (int argc, char **argv) { 62 GtkApplication *app;
63 GtkApplication *app; 63 int stat;
64 int stat; 64
65 65 app = gtk_application_new ("com.github.ToshioCP.tfv4", G_APPLICATION_HANDLES_OPEN);
66 app = gtk_application_new ("com.github.ToshioCP.tfv4", G_APPLICATION_HANDLES_OPEN); 66 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
67 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL); 67 g_signal_connect (app, "open", G_CALLBACK (app_open), NULL);
68 g_signal_connect (app, "open", G_CALLBACK (app_open), NULL); 68 stat = g_application_run (G_APPLICATION (app), argc, argv);
69 stat = g_application_run (G_APPLICATION (app), argc, argv); 69 g_object_unref (app);
70 g_object_unref (app); 70 return stat;
71 return stat; 71 }
72 }
~~~ ~~~
Most of the changes are in the function `app_open`. Most of the changes are in the function `app_open`.
The numbers at the left of the following items are line numbers in the source code. The numbers at the left of the following items are line numbers in the source code.
- 11-13: Variables `nb`, `lab` and `nbp` are defined and will point to a new GtkNotebook, GtkLabel and GtkNotebookPage widget respectively. - 11-13: Variables `nb`, `lab` and `nbp` are defined. They point GtkNotebook, GtkLabel and GtkNotebookPage respectively.
- 23: The window's title is set to "file viewer". - 23: The window's title is set to "file viewer".
- 25: The size of the window is set to maximum because a big window is appropriate for file viewers. - 24: The default size of the window is 600x400.
- 27-28 GtkNotebook is created and inserted to the GtkApplicationWindow as a child. - 26-27 GtkNotebook is created and inserted to the GtkApplicationWindow as a child.
- 30-59 For-loop. Each loop corresponds to a filename argument, and `files[i]` is GFile object containing the i-th argument. - 29-58 For-loop. The variable `files[i]` points i-th GFile, which is created by the GtkApplication from the i-th command line argument.
- 32-37 GtkScrollledWindow, GtkTextView are created and GtkTextBuffer found from the new GtkTextView. - 31-36 GtkScrollledWindow, GtkTextView are created. GtkTextBuffer is got from the GtkTextView.
GtkTextView is connected to GtkScrolledWindow as a child. The GtkTextView is connected to the GtkScrolledWindow as a child.
Each file gets their own copy of these widgets, so they are created inside the for-loop. - 38-39 inserts the contents of the file into GtkTextBuffer and frees the memory pointed by `contents`.
- 39-40 inserts the contents of the file into GtkTextBuffer and frees the memory pointed by `contents`. - 40-42: If the filename is taken from the GFile, GtkLabel is created with the filename. The string `filename` is freed..
- 41-43: Gets the filename and creates GtkLabel with the filename and then frees `filename`. - 43-44: If it fails to take the filename, empty string GtkLabel is created.
- 44-45: If `filename` is NULL, creates GtkLabel with the empty string. - 45-46: Appends a GtkScrolledWindow to the GtkNotebook as a child.
- 46: Appends GtkScrolledWindow as a page, with the tab GtkLabel, to GtkNotebook. And the GtkLabel is set as the child's tab.
At this time a GtkNoteBookPage widget is created automatically. At the same time, a GtkNoteBookPage is created automatically.
The GtkScrolledWindow widget is connected to the GtkNotebookPage. The function `gtk_notebook_get_page` returns the GtkNotebookPage of the child (GtkScrolledWindow).
Therefore, the structure is like this: - 47: GtkNotebookPage has "tab-expand" property.
~~~
GtkNotebook -- GtkNotebookPage -- GtkScrolledWindow
~~~
- 47: Gets GtkNotebookPage widget and sets `nbp` to point to this GtkNotebookPage.
- 48: GtkNotebookPage has a property set called "tab-expand".
If it is set to TRUE then the tab expands horizontally as long as possible. If it is set to TRUE then the tab expands horizontally as long as possible.
If it is FALSE, then the width of the tab is determined by the size of the label. If it is FALSE, then the width of the tab is determined by the size of the label.
`g_object_set` is a general function to set properties in objects. `g_object_set` is a general function to set properties of objects.
See [GObject API Reference, g\_object\_set](https://docs.gtk.org/gobject/method.Object.set.html). See [GObject API Reference -- g\_object\_set](https://docs.gtk.org/gobject/method.Object.set.html).
- 49-51: If the file cannot be read, "No such file" message is displayed and the `filename` buffer is freed. - 48-50: If it fails to read the file and a filename is taken from the GFile, "No such file" message is displayed. The `filename` is freed.
- 52-53: If `filename` is NULL, the "No valid file is given" message is outputted. - 51-52: If `filename` is NULL, the "No valid file is given" message is displayed.
- 55-58: If at least one file was read, then the number of GtkNotebookPage is greater than zero. - 54-57: If at least one page exists, the window is shown.
If it's true, it shows the window. Otherwise, the window is destroyed and the application quits.
If it's false, it destroys the window, which causes the program to quit.
Up: [Readme.md](../Readme.md), Prev: [Section 6](sec6.md), Next: [Section 8](sec8.md) Up: [README.md](../README.md), Prev: [Section 6](sec6.md), Next: [Section 8](sec8.md)

View file

@ -1,62 +1,64 @@
Up: [README.md](../README.md), Prev: [Section 7](sec7.md), Next: [Section 9](sec9.md) Up: [README.md](../README.md), Prev: [Section 7](sec7.md), Next: [Section 9](sec9.md)
# Defining a child object # Defining a final class
## A very simple editor ## A very simple editor
In the previous section we made a very simple file viewer. We made a very simple file viewer in the previous section.
Now we go on to rewrite it and turn it into very simple editor. Now we go on to rewrite it and turn it into very simple editor.
Its source file is in tfe1.c (text file editor 1). Its source file is `tfe1.c` (text file editor 1) under `tfe` directory.
GtkTextView has a feature for editing multiple lines. Therefore, we don't need to GtkTextView is a multi-line editor.
write the program from scratch, we just add two things to the file viewer: So, we don't need to write the editor from scratch.
We just add two things to the file viewer:
- Memory to store a pointer to the GFile instance. - Pointers to GFile instances.
- A function to write the file. - A text-save function.
There are a couple of ways to store the details of GFile. There are a couple of ways to store the pointers.
- Use global variables; or - Use global variables
- Make a child object, which can extend the instance memory for the GFile object. - Make a child class of GtkTextView and its each instance holds a pointer to the GFile instance.
Using global variables is easy to implement. Using global variables is easy to implement.
Define a sufficient size array of pointers to GFile. Define a sufficient size pointer array to GFile.
For example, For example,
~~~C ~~~C
GFile *f[20]; GFile *f[20];
~~~ ~~~
The variable `f[i]` corresponds to the file associated to the i-th GtkNotebookPage. The variable `f[i]` corresponds to the file associated with the i-th GtkNotebookPage.
There are however two problems with this.
The first concerns the size of the array.
If a user gives too many arguments (more than 20 in the example above), it is impossible to store the additional pointers to the GFile instances.
The second is the increasing difficulty for maintenance of the program.
We have a small program so far,
but however, if you continue developing it, the size of the program will grow.
Generally speaking, the bigger the program size, the more difficult it is to keep track of and maintain global variables. Global variables can be used and changed anywhere throughout the entire program.
Making a child object is a good idea in terms of maintenance. However, There are two problems.
One thing you need to be careful of is the difference between "child object" and "child widget". The first is the size of the array.
Here we are describing a "child object". If a user gives too many arguments (more than 20 in the example above), it is impossible to store all the pointers to the GFile instances.
A child object includes, and expands on its parent object, as The second is difficulty to maintain the program.
a child object derives everything from the parent object. We have a small program so far.
But, the more you develop the program, the bigger its size grows.
Generally speaking, it is very difficult to maintain global variables in a big program.
When you check the global variable, you need to check all the codes that use the variable.
Making a child class is a good idea in terms of maintenance.
And we prefer it rather than a global variable.
Be careful that we are thinking about "child class", not "child widget".
Child class and child widget are totally different.
Class is a term of GObject system.
If you are not familiar with GObject, see:
- [GObject API reference](https://docs.gtk.org/gobject/)
- [GObject tutorial for beginners](https://toshiocp.github.io/Gobject-tutorial/)
A child class inherits everything from the parent and, in addition, extends its performance.
We will define TfeTextView as a child class of GtkTextView.
It has everything that GtkTextView has and adds a pointer to a GFile.
![Child object of GtkTextView](../image/child.png) ![Child object of GtkTextView](../image/child.png)
We will define TfeTextView as a child object of GtkTextView. ## How to define a child class of GtkTextView
It has everything that GtkTextView has.
Specifically, TfeTextView has a GtkTextbuffer which corresponds to the GtkTextView inside TfeTextView.
The additional important thing is that TfeTextView can also keep an additional pointer to GFile.
In general, this is how GObjects work. Understanding the general theory about Gobject's is difficult, You need to know GObject system convention.
particularly for beginners.
So, I will just show you the way how to write the code and avoid the theoretical side.
If you want to know about GObject system, refer to another [tutorial](https://github.com/ToshioCP/Gobject-tutorial).
## How to define a child object of GtkTextView
Let's define the TfeTextView object, which is a child object of GtkTextView.
First, look at the program below. First, look at the program below.
~~~C ~~~C
@ -95,55 +97,55 @@ tfe_text_view_new (void) {
} }
~~~ ~~~
If you are curious about the background theory of this program, that's good,
because knowing the theory is very important if you want to program GTK applications.
Look at [GObject API Reference](https://docs.gtk.org/gobject/).
All you need is described there,
or refer to [GObject tutorial](https://github.com/ToshioCP/Gobject-tutorial).
It's a tough journey especially for beginners so for now, you don't need to know about this difficult theory.
It is enough to just remember the instructions below.
- TfeTextView is divided into two parts. - TfeTextView is divided into two parts.
Tfe and TextView. Tfe and TextView.
Tfe is called the prefix, namespace or module. Tfe is called prefix or namespace.
TextView is called the object. TextView is called object.
- There are three differnet identifier patterns. - There are three differnet identifier patterns.
TfeTextView (camel case), tfe\_text\_view (this is used to write functions) and TFE\_TEXT\_VIEW (This is used to cast a pointer to point TfeTextView type). TfeTextView (camel case), tfe\_text\_view (this is used for functions) and TFE\_TEXT\_VIEW (This is used to cast a object to TfeTextView).
- First, define TFE\_TYPE\_TEXT\_VIEW macro as tfe\_text\_view\_get\_type (). - First, define TFE\_TYPE\_TEXT\_VIEW macro as tfe\_text\_view\_get\_type ().
The name is always (prefix)\_TYPE\_(object) and the letters are upper case. The name is always (prefix)\_TYPE\_(object) and the letters are upper case.
And the replacement text is always (prefix)\_(object)\_get\_type () and the letters are lower case. And the replacement text is always (prefix)\_(object)\_get\_type () and the letters are lower case.
- Next, use G\_DECLARE\_FINAL\_TYPE macro. This definition is put before G\_DECLARE\_FINAL\_TYPE macro.
The arguments are the child object name in camel case, lower case with underscore, prefix (upper case), object (upper case with underscore) and parent object name (camel case). - The arguments of G\_DECLARE\_FINAL\_TYPE macro are the child class name in camel case, lower case with underscore, prefix (upper case), object (upper case with underscore) and parent class name (camel case).
The following two C structure is declared in the expansion of the macro.
- `typedef struct _TfeTextView TfeTextView`
- `typedef struct {GtkTextViewClass parent_class; } TfeTextViewClass;`
- These declaration tells us that TfeTextView and TfeTextViewClass are C structures.
"TfeTextView" has two meanings, class name and C structure name.
The C structure TfeTextView is called object.
Similarly, TfeTextViewClass is called class.
- Declare the structure \_TfeTextView. - Declare the structure \_TfeTextView.
The underscore is necessary. The underscore is necessary.
The first member is the parent object. The first member is the parent object (C structure).
Notice this is not a pointer but the object itself. Notice this is not a pointer but the object itself.
The second member and after are members of the child object. The second member and after are members of the child object.
TfeTextView structure has a pointer to a GFile instance as a member. TfeTextView structure has a pointer to a GFile instance as a member.
- Use G\_DEFINE\_TYPE macro. - G\_DEFINE\_TYPE macro.
The arguments are the child object name in camel case, lower case with underscore and parent object type (prefix)\_TYPE\_(module). The arguments are the child object name in camel case, lower case with underscore and parent object type (prefix)\_TYPE\_(module).
- Define instance init function (tfe\_text\_view\_init). This macro is mainly used to register the new class to the type system.
Usually you don't need to do anything. Type system is a base system of GObject.
- Define class init function (tfe\_text\_view\_class\_init). Every class has its own type.
You don't need to do anything in this object. The types of GObject, GtkWidget and TfeTextView are G\_TYPE\_OBJECT, GTK\_TYPE\_WIDGET and TFE\_TYPE\_TEXT\_VIEW respectively.
- Write function codes you want to add (tfe\_text\_view\_set\_file and tfe\_text\_view\_get\_file). Such type (for example, TFE\_TYPE\_TEXT\_VIEW) is a macro and it is expanded to a function (tfe\_text\_view\_get\_type()).
`tv` is a pointer to the TfeTextView object instance which is a C-structure declared with the tag \_TfeTextView. It returns a integer which is unique among all GObject system classes.
So, the structure has a member `file` as a pointer to a GFile instance. - Instance init function (tfe\_text\_view\_init) is called when the instance is created.
`tv->file = f` is an assignment of `f` to a member `file` of the structure pointed by `tv`. It is the same as a constructor in other object oriented languages.
This is an example how to use the extended memory in a child widget. - Class init function (tfe\_text\_view\_class\_init) is called when the class is created.
- Write a function to create an instance. - Two functions tfe\_text\_view\_set\_file and tfe\_text\_view\_get\_file are public functions.
Public functions are open and you can call them anywhere.
They are the same as public method in other object oriented languages.
`tv` is a pointer to the TfeTextView object (C structure).
It has a member `file` and it is pointed by `tv->file`.
- TfeTextView instance creation function is `tfe_text_view_new`.
Its name is (prefix)\_(object)\_new. Its name is (prefix)\_(object)\_new.
If the parent object function needs parameters, this function also need them. It uses g\_object\_new function to create the instance.
You sometimes might want to add some parameters. The arguments are (prefix)\_TYPE\_(object), a list to initialize properties and NULL.
It's your choice. NULL is the end mark of the property list.
Use g\_object\_new function to create the instance. No property is initialized here.
The arguments are (prefix)\_TYPE\_(object), a list to initialize properties and NULL.
In this code no property needs to be initialized.
And the return value is casted to GtkWidget. And the return value is casted to GtkWidget.
This program is not perfect. This program shows the outline how to define a child class.
It has some problems.
It will be modified later.
## Close-request signal ## Close-request signal
@ -156,10 +158,9 @@ After you finish editing, you exit the editor.
The editor updates files just before the window closes. The editor updates files just before the window closes.
GtkWindow emits the "close-request" signal before it closes. GtkWindow emits the "close-request" signal before it closes.
We connect the signal and the handler `before_close`. We will connect the signal and the handler `before_close`.
A handler is a C function. (A handler is a C function which is connected to a signal.)
When a function is connected to a certain signal, we call it a handler. The function `before_close` is called when the signal "close-request" is emitted.
The function `before_close` is invoked when the signal "close-request" is emitted.
~~~C ~~~C
g_signal_connect (win, "close-request", G_CALLBACK (before_close), NULL); g_signal_connect (win, "close-request", G_CALLBACK (before_close), NULL);
@ -171,59 +172,66 @@ The program of `before_close` is as follows.
~~~C ~~~C
1 static gboolean 1 static gboolean
2 before_close (GtkWindow *win, gpointer user_data) { 2 before_close (GtkWindow *win, GtkWidget *nb) {
3 GtkWidget *nb = GTK_WIDGET (user_data); 3 GtkWidget *scr;
4 GtkWidget *scr; 4 GtkWidget *tv;
5 GtkWidget *tv; 5 GFile *file;
6 GFile *file; 6 char *pathname;
7 char *pathname; 7 GtkTextBuffer *tb;
8 GtkTextBuffer *tb; 8 GtkTextIter start_iter;
9 GtkTextIter start_iter; 9 GtkTextIter end_iter;
10 GtkTextIter end_iter; 10 char *contents;
11 char *contents; 11 unsigned int n;
12 unsigned int n; 12 unsigned int i;
13 unsigned int i; 13
14 14 n = gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb));
15 n = gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb)); 15 for (i = 0; i < n; ++i) {
16 for (i = 0; i < n; ++i) { 16 scr = gtk_notebook_get_nth_page (GTK_NOTEBOOK (nb), i);
17 scr = gtk_notebook_get_nth_page (GTK_NOTEBOOK (nb), i); 17 tv = gtk_scrolled_window_get_child (GTK_SCROLLED_WINDOW (scr));
18 tv = gtk_scrolled_window_get_child (GTK_SCROLLED_WINDOW (scr)); 18 file = tfe_text_view_get_file (TFE_TEXT_VIEW (tv));
19 file = tfe_text_view_get_file (TFE_TEXT_VIEW (tv)); 19 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
20 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); 20 gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter);
21 gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter); 21 contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE);
22 contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE); 22 if (! g_file_replace_contents (file, contents, strlen (contents), NULL, TRUE, G_FILE_CREATE_NONE, NULL, NULL, NULL)) {
23 if (! g_file_replace_contents (file, contents, strlen (contents), NULL, TRUE, G_FILE_CREATE_NONE, NULL, NULL, NULL)) { 23 if ((pathname = g_file_get_path (file)) != NULL) {
24 pathname = g_file_get_path (file); 24 g_printerr ("Can't save %s.", pathname);
25 g_print ("ERROR : Can't save %s.", pathname); 25 g_free (pathname);
26 g_free (pathname); 26 } else
27 } 27 g_printerr ("Pathname not exist.\n");
28 g_free (contents); 28 }
29 } 29 g_free (contents);
30 return FALSE; 30 g_object_unref (file);
31 } 31 }
32 return FALSE;
33 }
~~~ ~~~
The numbers on the left of items are line numbers in the source code. The numbers on the left of items are line numbers in the source code.
- 15: Gets the number of pages `nb` has. - 14: The number of pages of `nb` is assigned to `n`.
- 16-29: For loop with regard to the index to each pages. - 15-31: For loop with regard to the index to each pages.
- 17-19: Gets GtkScrolledWindow, TfeTextView and a pointer to GFile. - 16-18: `scr`, `tv` and `file` is assigned pointers to the GtkScrolledWindow, TfeTextView and GFile.
The pointer was stored when `app_open` handler had run. It will be shown later. The GFile of TfeTextView was stored when `app_open` handler was called. It will be shown later.
- 20-22: Gets GtkTextBuffer and contents. `start_iter` and `end_iter` are iterators of the buffer. - 19-21: `tb` is assigned the GtkTextBuffer of the TfeTextView.
I don't want to explain them now because it would take a lot of time. The buffer is accessed with iterators.
Just remember these lines for the present. Iterators points somewhere in the buffer.
- 23-27: Writes the contents to the file. The function `gtk_text_buffer_get_bounds` assigns the start and end of the buffer to `start_iter` and `end_iter` respectively.
If it fails, it outputs an error message. Then the function `gtk_text_buffer_get_text` returns the text between `start_iter` and `end_iter`, which is the whole text in the buffer.
- 28: Frees `contents`. - 22-28: The text is saved to the file.
If it fails, error messages are displayed.
- 29: `contents` are freed.
- 30: GFile is useless. `g_object_unref` decreases the reference count of the GFile.
Reference count will be explained in the later section.
The reference count will be zero in this program and the GFile instance will destroy itself.
## Source code of tfe1.c ## Source code of tfe1.c
The following is the complete source code of `tfe1.c`. The following is the whole source code of `tfe1.c`.
~~~C ~~~C
1 #include <gtk/gtk.h> 1 #include <gtk/gtk.h>
2 2
3 /* Define TfeTextView Widget which is the child object of GtkTextView */ 3 /* Define TfeTextView Widget which is the child class of GtkTextView */
4 4
5 #define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type () 5 #define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type ()
6 G_DECLARE_FINAL_TYPE (TfeTextView, tfe_text_view, TFE, TEXT_VIEW, GtkTextView) 6 G_DECLARE_FINAL_TYPE (TfeTextView, tfe_text_view, TFE, TEXT_VIEW, GtkTextView)
@ -238,32 +246,32 @@ The following is the complete source code of `tfe1.c`.
15 15
16 static void 16 static void
17 tfe_text_view_init (TfeTextView *tv) { 17 tfe_text_view_init (TfeTextView *tv) {
18 } 18 tv->file = NULL;
19 19 }
20 static void 20
21 tfe_text_view_class_init (TfeTextViewClass *class) { 21 static void
22 } 22 tfe_text_view_class_init (TfeTextViewClass *class) {
23 23 }
24 void 24
25 tfe_text_view_set_file (TfeTextView *tv, GFile *f) { 25 void
26 tv -> file = f; 26 tfe_text_view_set_file (TfeTextView *tv, GFile *f) {
27 } 27 tv->file = f;
28 28 }
29 GFile * 29
30 tfe_text_view_get_file (TfeTextView *tv) { 30 GFile *
31 return tv -> file; 31 tfe_text_view_get_file (TfeTextView *tv) {
32 } 32 return tv -> file;
33 33 }
34 GtkWidget * 34
35 tfe_text_view_new (void) { 35 GtkWidget *
36 return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL)); 36 tfe_text_view_new (void) {
37 } 37 return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL));
38 38 }
39 /* ---------- end of the definition of TfeTextView ---------- */ 39
40 40 /* ---------- end of the definition of TfeTextView ---------- */
41 static gboolean 41
42 before_close (GtkWindow *win, gpointer user_data) { 42 static gboolean
43 GtkWidget *nb = GTK_WIDGET (user_data); 43 before_close (GtkWindow *win, GtkWidget *nb) {
44 GtkWidget *scr; 44 GtkWidget *scr;
45 GtkWidget *tv; 45 GtkWidget *tv;
46 GFile *file; 46 GFile *file;
@ -284,96 +292,104 @@ The following is the complete source code of `tfe1.c`.
61 gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter); 61 gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter);
62 contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE); 62 contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE);
63 if (! g_file_replace_contents (file, contents, strlen (contents), NULL, TRUE, G_FILE_CREATE_NONE, NULL, NULL, NULL)) { 63 if (! g_file_replace_contents (file, contents, strlen (contents), NULL, TRUE, G_FILE_CREATE_NONE, NULL, NULL, NULL)) {
64 pathname = g_file_get_path (file); 64 if ((pathname = g_file_get_path (file)) != NULL) {
65 g_print ("ERROR : Can't save %s.", pathname); 65 g_printerr ("Can't save %s.", pathname);
66 g_free (pathname); 66 g_free (pathname);
67 } 67 } else
68 g_free (contents); 68 g_printerr ("Pathname not exist.\n");
69 } 69 }
70 return FALSE; 70 g_free (contents);
71 } 71 g_object_unref (file);
72 72 }
73 static void 73 return FALSE;
74 app_activate (GApplication *app, gpointer user_data) { 74 }
75 g_print ("You need to give filenames as arguments.\n"); 75
76 } 76 static void
77 77 app_activate (GApplication *app) {
78 static void 78 g_print ("You need to give filenames as arguments.\n");
79 app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer user_data) { 79 }
80 GtkWidget *win; 80
81 GtkWidget *nb; 81 static void
82 GtkWidget *lab; 82 app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint) {
83 GtkNotebookPage *nbp; 83 GtkWidget *win;
84 GtkWidget *scr; 84 GtkWidget *nb;
85 GtkWidget *tv; 85 GtkWidget *lab;
86 GtkTextBuffer *tb; 86 GtkNotebookPage *nbp;
87 char *contents; 87 GtkWidget *scr;
88 gsize length; 88 GtkWidget *tv;
89 char *filename; 89 GtkTextBuffer *tb;
90 int i; 90 char *contents;
91 91 gsize length;
92 win = gtk_application_window_new (GTK_APPLICATION (app)); 92 char *filename;
93 gtk_window_set_title (GTK_WINDOW (win), "file editor"); 93 int i;
94 gtk_window_maximize (GTK_WINDOW (win)); 94
95 95 win = gtk_application_window_new (GTK_APPLICATION (app));
96 nb = gtk_notebook_new (); 96 gtk_window_set_title (GTK_WINDOW (win), "file editor");
97 gtk_window_set_child (GTK_WINDOW (win), nb); 97 gtk_window_set_default_size (GTK_WINDOW (win), 600, 400);
98 98
99 for (i = 0; i < n_files; i++) { 99 nb = gtk_notebook_new ();
100 if (g_file_load_contents (files[i], NULL, &contents, &length, NULL, NULL)) { 100 gtk_window_set_child (GTK_WINDOW (win), nb);
101 scr = gtk_scrolled_window_new (); 101
102 tv = tfe_text_view_new (); 102 for (i = 0; i < n_files; i++) {
103 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); 103 if (g_file_load_contents (files[i], NULL, &contents, &length, NULL, NULL)) {
104 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR); 104 scr = gtk_scrolled_window_new ();
105 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv); 105 tv = tfe_text_view_new ();
106 106 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
107 tfe_text_view_set_file (TFE_TEXT_VIEW (tv), g_file_dup (files[i])); 107 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR);
108 gtk_text_buffer_set_text (tb, contents, length); 108 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);
109 g_free (contents); 109
110 filename = g_file_get_basename (files[i]); 110 tfe_text_view_set_file (TFE_TEXT_VIEW (tv), g_file_dup (files[i]));
111 lab = gtk_label_new (filename); 111 gtk_text_buffer_set_text (tb, contents, length);
112 gtk_notebook_append_page (GTK_NOTEBOOK (nb), scr, lab); 112 g_free (contents);
113 nbp = gtk_notebook_get_page (GTK_NOTEBOOK (nb), scr); 113 filename = g_file_get_basename (files[i]);
114 g_object_set (nbp, "tab-expand", TRUE, NULL); 114 lab = gtk_label_new (filename);
115 g_free (filename); 115 gtk_notebook_append_page (GTK_NOTEBOOK (nb), scr, lab);
116 } else if ((filename = g_file_get_path (files[i])) != NULL) { 116 nbp = gtk_notebook_get_page (GTK_NOTEBOOK (nb), scr);
117 g_print ("No such file: %s.\n", filename); 117 g_object_set (nbp, "tab-expand", TRUE, NULL);
118 g_free (filename); 118 g_free (filename);
119 } else 119 } else if ((filename = g_file_get_path (files[i])) != NULL) {
120 g_print ("No valid file is given\n"); 120 g_print ("No such file: %s.\n", filename);
121 } 121 g_free (filename);
122 if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb)) > 0) { 122 } else
123 g_signal_connect (win, "close-request", G_CALLBACK (before_close), nb); 123 g_print ("No valid file is given\n");
124 gtk_widget_show (win); 124 }
125 } else 125 if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb)) > 0) {
126 gtk_window_destroy (GTK_WINDOW (win)); 126 g_signal_connect (win, "close-request", G_CALLBACK (before_close), nb);
127 } 127 gtk_widget_show (win);
128 128 } else
129 int 129 gtk_window_destroy (GTK_WINDOW (win));
130 main (int argc, char **argv) { 130 }
131 GtkApplication *app; 131
132 int stat; 132 int
133 133 main (int argc, char **argv) {
134 app = gtk_application_new ("com.github.ToshioCP.tfe1", G_APPLICATION_HANDLES_OPEN); 134 GtkApplication *app;
135 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL); 135 int stat;
136 g_signal_connect (app, "open", G_CALLBACK (app_open), NULL); 136
137 stat =g_application_run (G_APPLICATION (app), argc, argv); 137 app = gtk_application_new ("com.github.ToshioCP.tfe1", G_APPLICATION_HANDLES_OPEN);
138 g_object_unref (app); 138 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
139 return stat; 139 g_signal_connect (app, "open", G_CALLBACK (app_open), NULL);
140 } 140 stat =g_application_run (G_APPLICATION (app), argc, argv);
141 g_object_unref (app);
142 return stat;
143 }
~~~ ~~~
- 107: Sets the pointer to GFile into TfeTextView. - 110: The GFile pointer of the TfeTextView is set with `files[i]`, which is a GFile created with the command line argument.
`files[i]` is a pointer to GFile structure. But the GFile will be destroyed by the system later.
It will be freed by the system. So you need to copy it. So it needs to be copied before the assignment.
`g_file_dup` duplicates the given GFile structure. `g_file_dup` duplicates the GFile.
- 123: Connects "close-request" signal and `before_close` handler. - 126: The "close-request" signal is connected to `before_close` handler.
The fourth argument is called user data and it is given to the signal handler. The fourth argument is called "user data" and it will be the second argument of the signal handler.
So, `nb` is given to `before_close` as the second argument. So, `nb` is given to `before_close` as the second argument.
Now compile and run it. Now it's time to compile and run.
There's a sample file in the directory `tfe`.
Type `./a.out taketori.txt`. ~~~
$ cd src/tfe
$ comp tfe1
$ ./a.out taketori.txt`.
~~~
Modify the contents and close the window. Modify the contents and close the window.
Make sure that the file is modified. Make sure that the file is modified.

View file

@ -1,15 +1,14 @@
Up: [Readme.md](../Readme.md), Prev: [Section 8](sec8.md), Next: [Section 10](sec10.md) Up: [README.md](../README.md), Prev: [Section 8](sec8.md), Next: [Section 10](sec10.md)
# The User Interface (UI) file and GtkBuilder # GtkBuilder and UI file
## New, Open and Save button ## New, Open and Save button
In the last section we made the almost simplest editor possible. We made very simple editor in the previous section.
It reads files in the `app_open` function at start-up and writes them out when closing the window. It reads files at the start and writes them out at the end of the program.
It works but is not very good. It works, but is not so good.
It would be better if we had "New", "Open", "Save" and "Close" buttons. It would be better if we had "New", "Open", "Save" and "Close" buttons.
This section describes how to put those buttons into the window. This section describes how to put those buttons into the window.
Signals and handlers will be explained later.
![Screenshot of the file editor](../image/screenshot_tfe2.png) ![Screenshot of the file editor](../image/screenshot_tfe2.png)
@ -18,7 +17,7 @@ The function `app_open` in the source code `tfe2.c` is as follows.
~~~C ~~~C
1 static void 1 static void
2 app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer user_data) { 2 app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint) {
3 GtkWidget *win; 3 GtkWidget *win;
4 GtkWidget *nb; 4 GtkWidget *nb;
5 GtkWidget *lab; 5 GtkWidget *lab;
@ -105,7 +104,7 @@ The function `app_open` in the source code `tfe2.c` is as follows.
86 } 86 }
~~~ ~~~
The aim is to build the widgets of the main application window. The function `app_open` builds the widgets in the main application window.
- 25-27: Creates a GtkApplicationWindow instance and sets the title and default size. - 25-27: Creates a GtkApplicationWindow instance and sets the title and default size.
- 29-30: Creates a GtkBox instance `boxv`. - 29-30: Creates a GtkBox instance `boxv`.
@ -120,13 +119,12 @@ The other label `dmy2` has hexpand property which is set to be TRUE.
This makes the label expands horizontally as long as possible. This makes the label expands horizontally as long as possible.
- 41-44: Creates four buttons. - 41-44: Creates four buttons.
- 46-52: Appends these GtkLabel and GtkButton to `boxh`. - 46-52: Appends these GtkLabel and GtkButton to `boxh`.
- 54-57: Creates a GtkNotebook instance and sets hexpand and vexpand properties TRUE. - 54-57: Creates a GtkNotebook instance and sets hexpand and vexpand properties to be TRUE.
This makes it expand horizontally and vertically as big as possible. This makes it expand horizontally and vertically as big as possible.
It is appended to `boxv` as the second child. It is appended to `boxv` as the second child.
The number of lines to build the widgets is 33(=57-25+1). The number of widget-build lines is 33(=57-25+1).
We also needed many additional variables (`boxv`, `boxh`, `dmy1`, ...), We also needed many variables (`boxv`, `boxh`, `dmy1`, ...) and most of them used only for building the widgets.
most of which weren't necessary, except for building the widgets.
Are there any good solution to reduce these work? Are there any good solution to reduce these work?
Gtk provides GtkBuilder. Gtk provides GtkBuilder.
@ -135,7 +133,7 @@ It reduces this cumbersome work.
## The UI File ## The UI File
First, let's look at the UI file `tfe3.ui` that is used to define the widget structure. Look at the UI file `tfe3.ui` that defines widget structure.
~~~xml ~~~xml
1 <?xml version="1.0" encoding="UTF-8"?> 1 <?xml version="1.0" encoding="UTF-8"?>
@ -199,26 +197,24 @@ First, let's look at the UI file `tfe3.ui` that is used to define the widget str
59 </interface> 59 </interface>
~~~ ~~~
The structure of this file is XML. The is a XML file.
Constructs that begin with `<` and end with `>` are called tags. Tags begin with `<` and end with `>`.
There are two types of tags, the start tag and the end tag. There are two types of tags, the start tag and the end tag.
For example, `<interface>` is a start tag and `</interface>` is an end tag. For example, `<interface>` is a start tag and `</interface>` is an end tag.
The UI file begins and ends with interface tags. The UI file begins and ends with interface tags.
Some tags, for example object tags, can have a class and id attributes in their start tag. Some tags, for example object tags, can have a class and id attributes in their start tag.
- 1: The first line is XML declaration. - 1: XML declaration.
It specifies that the version of XML is 1.0 and the encoding is UTF-8. It specifies that the XML version is 1.0 and the encoding is UTF-8.
Even if the line is left out, GtkBuilder builds objects from the ui file. - 3-6: An object tag with `GtkApplicationWindow` class and `win` id.
But ui files must use UTF-8 encoding, or GtkBuilder can't recognize it and a fatal error occurs.
- 3-6: An object with `GtkApplicationWindow` class and `win` id is defined.
This is the top level window. This is the top level window.
And the three properties of the window are defined. And the three properties of the window are defined.
`title` property is "file editor", `default-width` property is 600 and `default-height` property is 400. The `title` property is "file editor", `default-width` property is 600 and `default-height` property is 400.
- 7: child tag means a child of the widget above. - 7: child tag means a child widget.
For example, line 7 tells us that GtkBox object which id is "boxv" is a child widget of `win`. For example, line 7 tells us that GtkBox object with "boxv" id attribute is a child widget of `win`.
Compare this ui file and the lines 25-57 in the source code of `app_open` function. Compare this ui file and the lines 25-57 in the `tfe2.c` source code.
Those two describe the same structure of widgets. Both builds the same window with its descendant widgets.
You can check the ui file with `gtk4-builder-tool`. You can check the ui file with `gtk4-builder-tool`.
@ -235,7 +231,7 @@ It is a good idea to check your ui file before compiling.
## GtkBuilder ## GtkBuilder
GtkBuilder builds widgets based on the ui file. GtkBuilder builds widgets based on a ui file.
~~~C ~~~C
GtkBuilder *build; GtkBuilder *build;
@ -250,7 +246,7 @@ The function `gtk_builder_new_from_file` reads the file given as an argument.
Then, it builds the widgets and creates GtkBuilder object. Then, it builds the widgets and creates GtkBuilder object.
The function `gtk_builder_get_object (build, "win")` returns the pointer to the widget `win`, which is the id in the ui file. The function `gtk_builder_get_object (build, "win")` returns the pointer to the widget `win`, which is the id in the ui file.
All the widgets are connected based on the parent-children relationship described in the ui file. All the widgets are connected based on the parent-children relationship described in the ui file.
We only need `win` and `nb` for the program after this, so we don't need to take out any other widgets. We only need `win` and `nb` for the program below.
This reduces lines in the C source file. This reduces lines in the C source file.
~~~ ~~~
@ -312,6 +308,8 @@ $ cd tfe; diff tfe2.c tfe3.c
< app = gtk_application_new ("com.github.ToshioCP.tfe2", G_APPLICATION_HANDLES_OPEN); < app = gtk_application_new ("com.github.ToshioCP.tfe2", G_APPLICATION_HANDLES_OPEN);
--- ---
> app = gtk_application_new ("com.github.ToshioCP.tfe3", G_APPLICATION_HANDLES_OPEN); > app = gtk_application_new ("com.github.ToshioCP.tfe3", G_APPLICATION_HANDLES_OPEN);
144a107
>
~~~ ~~~
`60,103c61,65` means 44 (=103-60+1) lines are changed to 5 (=65-61+1) lines. `60,103c61,65` means 44 (=103-60+1) lines are changed to 5 (=65-61+1) lines.
@ -322,7 +320,7 @@ Now I'll show you `app_open` function in the C file `tfe3.c`.
~~~C ~~~C
1 static void 1 static void
2 app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer user_data) { 2 app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint) {
3 GtkWidget *win; 3 GtkWidget *win;
4 GtkWidget *nb; 4 GtkWidget *nb;
5 GtkWidget *lab; 5 GtkWidget *lab;
@ -372,12 +370,11 @@ Now I'll show you `app_open` function in the C file `tfe3.c`.
~~~ ~~~
The whole source code of `tfe3.c` is stored in [src/tfe](../src/tfe) directory. The whole source code of `tfe3.c` is stored in [src/tfe](../src/tfe) directory.
If you want to see it, click the link above.
### Using ui string ### Using ui string
GtkBuilder can build widgets using string. GtkBuilder can build widgets with string.
Use the function `gtk_builder_new_from_string` instead of `gtk_builder_new_from_file`. Use `gtk_builder_new_from_string` instead of `gtk_builder_new_from_file`.
~~~C ~~~C
char *uistring; char *uistring;
@ -395,19 +392,19 @@ uistring =
... ... ... ... ... ...
"</interface>"; "</interface>";
build = gtk_builder_new_from_stringfile (uistring); build = gtk_builder_new_from_string (uistring, -1);
~~~ ~~~
This method has an advantage and disadvantage. This method has an advantage and disadvantage.
The advantage is that the ui string is written in the source code. The advantage is that the ui string is written in the source code.
So ui file is not necessary on runtime. So, no ui file is needed on runtime.
The disadvantage is that writing C string is a bit bothersome because of the double quotes. The disadvantage is that writing C string is a bit bothersome because of the double quotes.
If you want to use this method, you should write a script that transforms ui file into C-string. If you want to use this method, you should write a script that transforms ui file into C-string.
- Add backslash before each double quote. - Add backslash before each double quote.
- Add double quotes at the left and right of the string in each line. - Add double quotes at the left and right of the string in each line.
### Using Gresource ### Gresource
Using Gresource is similar to using string. Using Gresource is similar to using string.
But Gresource is compressed binary data, not text data. But Gresource is compressed binary data, not text data.
@ -430,9 +427,10 @@ It describes resource files.
- 2: `gresources` tag can include multiple gresources (gresource tags). - 2: `gresources` tag can include multiple gresources (gresource tags).
However, this xml has only one gresource. However, this xml has only one gresource.
- 3: The gresource has a prefix `/com/github/ToshioCP/tfe3`. - 3: The gresource has a prefix `/com/github/ToshioCP/tfe3`.
- 4: The gresource has `tfe3.ui`. - 4: The name of the gresource is `tfe3.ui`.
And it is pointed by `/com/github/ToshioCP/tfe3/tfe3.ui` because it needs prefix. The resource will be pointed with `/com/github/ToshioCP/tfe3/tfe3.ui` by GtkBuilder.
If you want to add more files, then insert them between line 4 and 5. The pattern is "prefix" + "name".
If you want to add more files, insert them between line 4 and 5.
Save this xml text to `tfe3.gresource.xml`. Save this xml text to `tfe3.gresource.xml`.
The gresource compiler `glib-compile-resources` shows its usage with the argument `--help`. The gresource compiler `glib-compile-resources` shows its usage with the argument `--help`.
@ -469,7 +467,9 @@ Application Options:
Now run the compiler. Now run the compiler.
$ glib-compile-resources tfe3.gresource.xml --target=resources.c --generate-source ~~~
$ glib-compile-resources tfe3.gresource.xml --target=resources.c --generate-source
~~~
Then a C source file `resources.c` is generated. Then a C source file `resources.c` is generated.
Modify `tfe3.c` and save it as `tfe3_r.c`. Modify `tfe3.c` and save it as `tfe3_r.c`.
@ -483,7 +483,12 @@ build = gtk_builder_new_from_resource ("/com/github/ToshioCP/tfe3/tfe3.ui");
... ... ... ... ... ...
~~~ ~~~
Then, compile and run it. The function `gtk_builder_new_from_resource` builds widgets from a resource.
The window appears and it is the same as the screenshot at the beginning of this page.
Up: [Readme.md](../Readme.md), Prev: [Section 8](sec8.md), Next: [Section 10](sec10.md) Then, compile and run it.
A window appears and it is the same as the screenshot at the beginning of this page.
Generally, resource is the best way for C language.
If you use other languages like Ruby, string is better than resource.
Up: [README.md](../README.md), Prev: [Section 8](sec8.md), Next: [Section 10](sec10.md)

View file

@ -1,6 +1,6 @@
# TfeTextView API reference # TfeTextView API reference
TfeTextView -- Child object of GtkTextView. It holds GFile which the contents of GtkTextBuffer correponds to. TfeTextView -- Child object of GtkTextView. It is connected to a certain file.
## Functions ## Functions
- GFile *[tfe_text_view_get_file ()](../src/tfetextview/#tfe_text_view_get_file) - GFile *[tfe_text_view_get_file ()](../src/tfetextview/#tfe_text_view_get_file)
@ -39,8 +39,8 @@ GObject
## Description ## Description
TfeTextView holds GFile which the contents of GtkTextBuffer corresponds to. TfeTextView holds GFile corresponds to the contents of GtkTextBuffer.
File manipulation functions are added to this object. It has some file manipulation functions.
## Functions ## Functions
@ -67,7 +67,7 @@ tfe_text_view_open (TfeTextView *tv, GtkWidget *win);
Just shows a GtkFileChooserDialog so that a user can choose a file to read. Just shows a GtkFileChooserDialog so that a user can choose a file to read.
This function doesn't do any I/O operations. This function doesn't do any I/O operations.
They are done by the signal handler connected to the `response` signal emitted by GtkFileChooserDialog. They are done by the signal handler connected to the `response` signal emitted by GtkFileChooserDialog.
Therefore the caller can't know the I/O status directly from the function. Therefore the caller can't know the I/O status directly from `tfe_text_view_open`.
Instead, the status is informed by `open-response` signal. Instead, the status is informed by `open-response` signal.
The caller needs to set a handler to this signal in advance. The caller needs to set a handler to this signal in advance.
@ -83,9 +83,9 @@ void
tfe_text_view_save (TfeTextView *tv); tfe_text_view_save (TfeTextView *tv);
~~~ ~~~
Saves the contents of a TfeTextView to a file. Saves the contents of the TfeTextView to a file.
If `tv` holds a GFile, it is used. If `tv` holds a GFile, it is used.
Otherwise, this function shows GtkFileChosserDialog so that a user can choose a file to save. Otherwise, this function shows GtkFileChooserDialog so that the user can choose a file to save.
Parameters Parameters
@ -99,7 +99,7 @@ tfe_text_view_saveas (TfeTextView *tv);
~~~ ~~~
Saves the content of a TfeTextView to a file. Saves the content of a TfeTextView to a file.
This function shows GtkFileChosserDialog so that a user can choose a file to save. This function shows GtkFileChooserDialog so that a user can choose a file to save.
Parameters Parameters
@ -174,6 +174,13 @@ Members:
- TFE_OPEN_RESPONSE_CANCEL: Reading file is canceled by the user. - TFE_OPEN_RESPONSE_CANCEL: Reading file is canceled by the user.
- TFE_OPEN_RESPONSE_ERROR: An error happened during the opening or reading process. - TFE_OPEN_RESPONSE_ERROR: An error happened during the opening or reading process.
## Properties
### wrap-mode
The property "wrap-mode" belongs to GtkTextView.
TfeTextView inherits it and the value is set to GTK\_WRAP\_WORD\_CHAR as a default.
## Signals ## Signals
### change-file ### change-file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View file

@ -9,43 +9,46 @@ The table of contents is at the end of this abstract.
- Section 26 to 29 describes the list model and the list view (GtkListView, GtkGridView and GtkColumnView). - Section 26 to 29 describes the list model and the list view (GtkListView, GtkGridView and GtkColumnView).
It also describes GtkExpression. It also describes GtkExpression.
The latest version of the tutorial is located at [Gtk4-tutorial GitHub repository](https://github.com/ToshioCP/Gtk4-tutorial). The latest version of the tutorial is located at [GTK4-tutorial GitHub repository](https://github.com/ToshioCP/GTK4-tutorial).
You can read it from there directly without having to download anything. You can read it directly without download.
There's a GitHub Page of this tutorial at [`https://toshiocp.github.io/GTK4-tutorial/`](https://toshiocp.github.io/GTK4-tutorial/).
It is easier to read than the repository.
#### GTK 4 Documentation #### GTK 4 Documentation
Please refer to [GTK API Reference](https://docs.gtk.org/gtk4/index.html) Please refer to [GTK 4 API Reference](https://docs.gtk.org/gtk4/index.html)
and [GNOME Developer Documentation Website](https://developer.gnome.org/) for further information. and [GNOME Developer Documentation Website](https://developer.gnome.org/) for further information.
These websites are newly opened lately (Aug/2021). These websites were opened in August of 2021.
The old documentation is located at [GTK Reference Manual](https://developer-old.gnome.org/gtk4/stable/) and [GNOME Developer Center](https://developer-old.gnome.org/). The old documents are located at [GTK Reference Manual](https://developer-old.gnome.org/gtk4/stable/) and [GNOME Developer Center](https://developer-old.gnome.org/).
The new website is in progress at present, so you might need to refer to the old version.
If you want to know about GObject and the type system, please refer to [GObject tutorial](https://github.com/ToshioCP/Gobject-tutorial). If you want to know about GObject and the type system, please refer to [GObject tutorial](https://github.com/ToshioCP/Gobject-tutorial).
The GObject details are easy to understand and also necessary to know when writing GTK 4 programs. GObject is the base of GTK 4, so it is important for developers to understand GObject for writing GTK 4 programs.
#### Contribution #### Contribution
This tutorial is under development and unstable. This tutorial is still under development and unstable.
Even though the codes of the examples have been tested on GTK 4 (version 4.0), bugs may still exist. Even though the codes of the examples have been tested on GTK 4 (version 4.8.1), bugs may still exist.
If you find any bugs, errors or mistakes in the tutorial and C examples, please let me know. If you find any bugs, errors or mistakes in the tutorial and C examples, please let me know.
You can post it to [GitHub issues](https://github.com/ToshioCP/Gtk4-tutorial/issues). You can post it to [GitHub issues](https://github.com/ToshioCP/GTK4-tutorial/issues).
You can also post corrected files as a commit to [pull request](https://github.com/ToshioCP/Gtk4-tutorial/pulls). You can also post updated files to [pull request](https://github.com/ToshioCP/GTK4-tutorial/pulls).
When you make corrections, correct the source files, which are under the 'src' directory, One thing you need to be careful is to correct the source files, which are under the 'src' directory.
then run `rake` to create to create the output file. The GFM files under the 'gfm' directory are automatically updated. Don't modify the files under `gfm` or `html` directories.
After modifying some source files (under `src` directory), run `rake` to create GFM (GitHub Flavoured Markdown) files or run `rake html` to create HTML files.
If you have a question, feel free to post it as an issue. If you have a question, feel free to post it to `issue`.
All questions are helpful and will make this tutorial get better. All questions are helpful and will make this tutorial get better.
#### How to get a HTML or PDF version #### How to get Gtk 4 tutorial with HTML or PDF format
If you want to get a HTML or PDF version, you can make them with `rake`, which is a ruby version of make. If you want to get HTML or PDF format tutorial, make them with `rake` command, which is a ruby version of make.
Type `rake html` for HTML. Type `rake html` for HTML.
Type `rake pdf` for PDF. Type `rake pdf` for PDF.
@@@if gfm @@@if gfm
There is a documentation \("[How to build GTK 4 Tutorial](Readme_for_developers.src.md)"\) that describes how to make them. There is a document \("[How to build GTK 4 Tutorial](Readme_for_developers.src.md)"\) for further information.
@@@elif html @@@elif html
There is a documentation \("[How to build GTK 4 Tutorial](Readme_for_developers.src.md)"\) that describes how to make them. There is a document \("[How to build GTK 4 Tutorial](Readme_for_developers.src.md)"\) for further information.
@@@elif latex @@@elif latex
An appendix "How to build GTK 4 Tutorial" describes how to make them. An appendix "How to build GTK 4 Tutorial" describes how to make them.
@@@end @@@end

View file

@ -4,36 +4,46 @@ typedef struct _GObjectClass GInitiallyUnownedClass;
struct _GObjectClass struct _GObjectClass
{ {
GTypeClass g_type_class; GTypeClass g_type_class;
/*< private >*/ /*< private >*/
GSList *construct_properties; GSList *construct_properties;
/*< public >*/ /*< public >*/
/* seldom overridden */ /* seldom overridden */
GObject* (*constructor) (GType type, GObject* (*constructor) (GType type,
guint n_construct_properties, guint n_construct_properties,
GObjectConstructParam *construct_properties); GObjectConstructParam *construct_properties);
/* overridable methods */ /* overridable methods */
void (*set_property) (GObject *object, void (*set_property) (GObject *object,
guint property_id, guint property_id,
const GValue *value, const GValue *value,
GParamSpec *pspec); GParamSpec *pspec);
void (*get_property) (GObject *object, void (*get_property) (GObject *object,
guint property_id, guint property_id,
GValue *value, GValue *value,
GParamSpec *pspec); GParamSpec *pspec);
void (*dispose) (GObject *object); void (*dispose) (GObject *object);
void (*finalize) (GObject *object); void (*finalize) (GObject *object);
/* seldom overridden */ /* seldom overridden */
void (*dispatch_properties_changed) (GObject *object, void (*dispatch_properties_changed) (GObject *object,
guint n_pspecs, guint n_pspecs,
GParamSpec **pspecs); GParamSpec **pspecs);
/* signals */ /* signals */
void (*notify) (GObject *object, void (*notify) (GObject *object,
GParamSpec *pspec); GParamSpec *pspec);
/* called when done constructing */ /* called when done constructing */
void (*constructed) (GObject *object); void (*constructed) (GObject *object);
/*< private >*/ /*< private >*/
gsize flags; gsize flags;
gsize n_construct_properties;
gpointer pspecs;
gsize n_pspecs;
/* padding */ /* padding */
gpointer pdummy[6]; gpointer pdummy[3];
}; };

View file

@ -102,8 +102,8 @@ struct _GtkTextViewClass
void (* toggle_overwrite) (GtkTextView *text_view); void (* toggle_overwrite) (GtkTextView *text_view);
GtkTextBuffer * (* create_buffer) (GtkTextView *text_view); GtkTextBuffer * (* create_buffer) (GtkTextView *text_view);
void (* snapshot_layer) (GtkTextView *text_view, void (* snapshot_layer) (GtkTextView *text_view,
GtkTextViewLayer layer, GtkTextViewLayer layer,
GtkSnapshot *snapshot); GtkSnapshot *snapshot);
gboolean (* extend_selection) (GtkTextView *text_view, gboolean (* extend_selection) (GtkTextView *text_view,
GtkTextExtendSelection granularity, GtkTextExtendSelection granularity,
const GtkTextIter *location, const GtkTextIter *location,

View file

@ -1,7 +1,7 @@
#include <gtk/gtk.h> #include <gtk/gtk.h>
static void static void
app_activate (GApplication *app, gpointer user_data) { app_activate (GApplication *app) {
GtkWidget *win; GtkWidget *win;
GtkWidget *lab; GtkWidget *lab;
@ -20,7 +20,7 @@ main (int argc, char **argv) {
GtkApplication *app; GtkApplication *app;
int stat; int stat;
app = gtk_application_new ("com.github.ToshioCP.lb1", G_APPLICATION_FLAGS_NONE); app = gtk_application_new ("com.github.ToshioCP.lb1", G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL); g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
stat =g_application_run (G_APPLICATION (app), argc, argv); stat =g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app); g_object_unref (app);

View file

@ -1,12 +1,12 @@
#include <gtk/gtk.h> #include <gtk/gtk.h>
static void static void
click_cb (GtkButton *btn, gpointer user_data) { click_cb (GtkButton *btn) {
g_print ("Clicked.\n"); g_print ("Clicked.\n");
} }
static void static void
app_activate (GApplication *app, gpointer user_data) { app_activate (GApplication *app) {
GtkWidget *win; GtkWidget *win;
GtkWidget *btn; GtkWidget *btn;
@ -26,7 +26,7 @@ main (int argc, char **argv) {
GtkApplication *app; GtkApplication *app;
int stat; int stat;
app = gtk_application_new ("com.github.ToshioCP.lb2", G_APPLICATION_FLAGS_NONE); app = gtk_application_new ("com.github.ToshioCP.lb2", G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL); g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
stat =g_application_run (G_APPLICATION (app), argc, argv); stat =g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app); g_object_unref (app);

View file

@ -1,13 +1,12 @@
#include <gtk/gtk.h> #include <gtk/gtk.h>
static void static void
click_cb (GtkButton *btn, gpointer user_data) { click_cb (GtkButton *btn, GtkWindow *win) {
GtkWindow *win = GTK_WINDOW (user_data);
gtk_window_destroy (win); gtk_window_destroy (win);
} }
static void static void
app_activate (GApplication *app, gpointer user_data) { app_activate (GApplication *app) {
GtkWidget *win; GtkWidget *win;
GtkWidget *btn; GtkWidget *btn;
@ -15,7 +14,7 @@ app_activate (GApplication *app, gpointer user_data) {
gtk_window_set_title (GTK_WINDOW (win), "lb3"); gtk_window_set_title (GTK_WINDOW (win), "lb3");
gtk_window_set_default_size (GTK_WINDOW (win), 400, 300); gtk_window_set_default_size (GTK_WINDOW (win), 400, 300);
btn = gtk_button_new_with_label ("Quit"); btn = gtk_button_new_with_label ("Close");
gtk_window_set_child (GTK_WINDOW (win), btn); gtk_window_set_child (GTK_WINDOW (win), btn);
g_signal_connect (btn, "clicked", G_CALLBACK (click_cb), win); g_signal_connect (btn, "clicked", G_CALLBACK (click_cb), win);
@ -27,7 +26,7 @@ main (int argc, char **argv) {
GtkApplication *app; GtkApplication *app;
int stat; int stat;
app = gtk_application_new ("com.github.ToshioCP.lb3", G_APPLICATION_FLAGS_NONE); app = gtk_application_new ("com.github.ToshioCP.lb3", G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL); g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
stat =g_application_run (G_APPLICATION (app), argc, argv); stat =g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app); g_object_unref (app);

View file

@ -1,8 +1,8 @@
#include <gtk/gtk.h> #include <gtk/gtk.h>
static void static void
click1_cb (GtkButton *btn, gpointer user_data) { click1_cb (GtkButton *btn) {
const gchar *s; const char *s;
s = gtk_button_get_label (btn); s = gtk_button_get_label (btn);
if (g_strcmp0 (s, "Hello.") == 0) if (g_strcmp0 (s, "Hello.") == 0)
@ -12,13 +12,12 @@ click1_cb (GtkButton *btn, gpointer user_data) {
} }
static void static void
click2_cb (GtkButton *btn, gpointer user_data) { click2_cb (GtkButton *btn, GtkWindow *win) {
GtkWindow *win = GTK_WINDOW (user_data);
gtk_window_destroy (win); gtk_window_destroy (win);
} }
static void static void
app_activate (GApplication *app, gpointer user_data) { app_activate (GApplication *app) {
GtkWidget *win; GtkWidget *win;
GtkWidget *box; GtkWidget *box;
GtkWidget *btn1; GtkWidget *btn1;
@ -35,7 +34,7 @@ app_activate (GApplication *app, gpointer user_data) {
btn1 = gtk_button_new_with_label ("Hello."); btn1 = gtk_button_new_with_label ("Hello.");
g_signal_connect (btn1, "clicked", G_CALLBACK (click1_cb), NULL); g_signal_connect (btn1, "clicked", G_CALLBACK (click1_cb), NULL);
btn2 = gtk_button_new_with_label ("Quit"); btn2 = gtk_button_new_with_label ("Close");
g_signal_connect (btn2, "clicked", G_CALLBACK (click2_cb), win); g_signal_connect (btn2, "clicked", G_CALLBACK (click2_cb), win);
gtk_box_append (GTK_BOX (box), btn1); gtk_box_append (GTK_BOX (box), btn1);
@ -49,7 +48,7 @@ main (int argc, char **argv) {
GtkApplication *app; GtkApplication *app;
int stat; int stat;
app = gtk_application_new ("com.github.ToshioCP.lb4", G_APPLICATION_FLAGS_NONE); app = gtk_application_new ("com.github.ToshioCP.lb4", G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL); g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
stat =g_application_run (G_APPLICATION (app), argc, argv); stat =g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app); g_object_unref (app);

View file

@ -5,7 +5,7 @@ main (int argc, char **argv) {
GtkApplication *app; GtkApplication *app;
int stat; int stat;
app = gtk_application_new ("com.github.ToshioCP.pr1", G_APPLICATION_FLAGS_NONE); app = gtk_application_new ("com.github.ToshioCP.pr1", G_APPLICATION_DEFAULT_FLAGS);
stat =g_application_run (G_APPLICATION (app), argc, argv); stat =g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app); g_object_unref (app);
return stat; return stat;

View file

@ -10,7 +10,7 @@ main (int argc, char **argv) {
GtkApplication *app; GtkApplication *app;
int stat; int stat;
app = gtk_application_new ("com.github.ToshioCP.pr2", G_APPLICATION_FLAGS_NONE); app = gtk_application_new ("com.github.ToshioCP.pr2", G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL); g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
stat =g_application_run (G_APPLICATION (app), argc, argv); stat =g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app); g_object_unref (app);

View file

@ -14,7 +14,7 @@ main (int argc, char **argv) {
GtkApplication *app; GtkApplication *app;
int stat; int stat;
app = gtk_application_new ("com.github.ToshioCP.pr3", G_APPLICATION_FLAGS_NONE); app = gtk_application_new ("com.github.ToshioCP.pr3", G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL); g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
stat =g_application_run (G_APPLICATION (app), argc, argv); stat =g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app); g_object_unref (app);

View file

@ -15,7 +15,7 @@ main (int argc, char **argv) {
GtkApplication *app; GtkApplication *app;
int stat; int stat;
app = gtk_application_new ("com.github.ToshioCP.pr4", G_APPLICATION_FLAGS_NONE); app = gtk_application_new ("com.github.ToshioCP.pr4", G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL); g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
stat =g_application_run (G_APPLICATION (app), argc, argv); stat =g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app); g_object_unref (app);

View file

@ -6,43 +6,40 @@
This tutorial is about GTK 4 libraries. This tutorial is about GTK 4 libraries.
It is originally used on Linux with C compiler, but now it is used more widely, on Windows and MacOS, with Vala, Python and so on. It is originally used on Linux with C compiler, but now it is used more widely, on Windows and MacOS, with Vala, Python and so on.
However, this tutorial describes only _C programs on Linux_. However, this tutorial describes only *C programs on Linux*.
If you want to try the examples in the tutorial, you need: If you want to try the examples in the tutorial, you need:
- PC with Linux distribution like Ubuntu, Debian and so on. - PC with Linux distribution like Ubuntu or Debian.
- Gcc. - GCC.
- GTK 4. - GTK 4.
The stable version of Gtk on Linux distributions is version three at present. The stable version of GTK is 4.8.2 at present (13/Dec/2022), but older version (4.0 or higher) may work.
You need to install GTK 4 to your computer. See [Section 3](sec3.src.md) for the installation for GTK 4.
See [Section 3](sec3.src.md) for the installation of GTK 4.
### Ruby and rake for making the document ### Ruby and rake for making the document
This repository includes Ruby programs. This repository includes Ruby programs.
They are used to make Markdown files, HTML files, Latex files and a PDF file. They are used to make GFM (GitHub Flavoured Markdown) files, HTML files, Latex files and a PDF file.
You need: You need:
- Linux distribution like Ubuntu. - Linux.
- Ruby programming language. - Ruby programming language.
There are two ways to install it. There are two ways to install it.
One is installing the distribution's package. One is installing the distribution's package.
The other is using rbenv and ruby-build. The other is using rbenv and ruby-build.
If you want to use the latest version of ruby, use rbenv. If you want to use the latest version of ruby, use rbenv.
- Rake. - Rake.
It is a gem, which is a library written in Ruby. You don't need to install it separately because it is a standard library of Ruby.
You can install it as a package of your distribution or use gem command.
## License ## License
Copyright (C) 2020 ToshioCP (Toshio Sekiya) Copyright (C) 2020-2022 ToshioCP (Toshio Sekiya)
Gtk4-tutorial repository contains the tutorial document and software such as converters, generators and controllers. GTK4-tutorial repository contains tutorial documents and programs such as converters, generators and controllers.
All of them make up the 'Gtk4-tutorial' package. All of them make up the 'GTK4-tutorial' package.
This package is simply called 'Gtk4-tutorial' in the following description. This package is simply called 'GTK4-tutorial' in the following description.
'Gtk4-tutorial' is free; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License or, at your option, any later version. 'GTK4-tutorial' is free; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License or, at your option, any later version.
'Gtk4-tutorial' is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 'GTK4-tutorial' is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the [GNU General Public License](https://www.gnu.org/licenses/gpl-3.0.html) for more details. See the [GNU General Public License](https://www.gnu.org/licenses/gpl-3.0.html) for more details.

View file

@ -1,11 +1,11 @@
# Build system # Build system
## What do we need to think about to manage big source files? ## Managing big source files
We've compiled a small editor so far. We've compiled a small editor so far.
But Some bad signs are beginning to appear. But Some bad signs are beginning to appear.
- We've had only one C source file and put everything into it. - We've had only one C source file and put everything in it.
We need to sort it out. We need to sort it out.
- There are two compilers, `gcc` and `glib-compile-resources`. - There are two compilers, `gcc` and `glib-compile-resources`.
We should control them by one building tool. We should control them by one building tool.
@ -14,7 +14,7 @@ These ideas are useful to manage big source files.
## Divide a C source file into two parts. ## Divide a C source file into two parts.
When you divide C source file into several parts, each file should contain only one thing. When you divide C source file into several parts, each file should contain one thing.
For example, our source has two things, the definition of TfeTextView and functions related to GtkApplication and GtkApplicationWindow. For example, our source has two things, the definition of TfeTextView and functions related to GtkApplication and GtkApplicationWindow.
It is a good idea to separate them into two files, `tfetextview.c` and `tfe.c`. It is a good idea to separate them into two files, `tfetextview.c` and `tfe.c`.
@ -64,10 +64,8 @@ The ui file `tfe.ui` is the same as `tfe3.ui` in the previous section.
tfe4/tfe.gresource.xml tfe4/tfe.gresource.xml
@@@ @@@
## Make
Dividing a file makes it easy to maintain source files. Dividing a file makes it easy to maintain source files.
But now we are faced with a new problem. But now we face a new problem.
The building step increases. The building step increases.
- Compiling the ui file `tfe.ui` into `resources.c`. - Compiling the ui file `tfe.ui` into `resources.c`.
@ -76,99 +74,18 @@ The building step increases.
- Compiling `resources.c` into `resources.o`. - Compiling `resources.c` into `resources.o`.
- Linking all the object files into application `tfe`. - Linking all the object files into application `tfe`.
Now build tool is necessary to manage it. Build tools manage the steps.
Make is one of the build tools. I'll show you three build tools, Meson and Ninja, Make and Rake.
It was created in 1976. Meson and Ninja is recommended as a C build tool, but others are also fine.
It is an old and widely used program. It's your choice.
Make analyzes Makefile and executes compilers. ## Meson and Ninja
All instructions are written in Makefile.
~~~makefile Meson and Ninja is one of the most popular building tool to build C language program.
sample.o: sample.c Many developers use Meson and Ninja lately.
gcc -o sample.o sample.c For example, GTK 4 uses them.
~~~
The sample of Malefile above consists of three elements, `sample.o`, `sample.c` and `gcc -o sample.o sample.c`. You need to make `meson.build` file first.
- `sample.o` is called target.
- `sample.c` is prerequisite.
- `gcc -o sample.o sample.c` is recipe.
Recipes follow tab characters, not spaces.
(It is very important. Use tab not space, or make won't work as you expected).
The rule is:
If a prerequisite modified later than a target, then make executes the recipe.
In the example above, if `sample.c` is modified after the generation of `sample.o`, then make executes gcc and compile `sample.c` into `sample.o`.
If the modification time of `sample.c` is older then the generation of `sample.o`, then no compiling is necessary, so make does nothing.
The Makefile for `tfe` is as follows.
@@@include
tfe4/Makefile
@@@
You only need to type `make`.
$ make
gcc -c -o tfe.o `pkg-config --cflags gtk4` tfe.c
gcc -c -o tfetextview.o `pkg-config --cflags gtk4` tfetextview.c
glib-compile-resources tfe.gresource.xml --target=resources.c --generate-source
gcc -c -o resources.o `pkg-config --cflags gtk4` resources.c
gcc -o tfe tfe.o tfetextview.o resources.o `pkg-config --libs gtk4`
I used only very basic rules to write this Makefile.
There are many more convenient methods to make it more compact.
But it will be long to explain it.
So I want to finish explaining make and move on to the next topic.
## Rake
Rake is a similar program to make.
It is written in Ruby code.
If you don't use Ruby, you don't need to read this subsection.
However, Ruby is really sophisticated and recommendable script language.
- Rakefile controls the behavior of `rake`.
- You can write any Ruby code in Rakefile.
Rake has task and file task, which is similar to target, prerequisite and recipe in make.
@@@include
tfe4/Rakefile
@@@
The contents of the `Rakefile` is almost same as the `Makefile` in the previous subsection.
- 3-6: Defines target file, source file and so on.
- 1, 8: Loads clean library. And defines CLEAN file list.
The files included by CLEAN will be removed when `rake clean` is typed on the command line.
- 10: The default target depends on `targetfile`.
The task `default` is the final goal of tasks.
- 12-14: `targetfile` depends on `objfiles`.
The variable `t` is a task object.
- `t.name` is a target name
- `t.prerequisites` is an array of prerequisites.
- `t.source` is the first element of prerequisites.
- `sh` is a method to give the following string to shell as an argument and executes the shell.
- 16-21: There's a loop by each element of the array of `objfiles`. Each object depends on corresponding source file.
- 23-25: Resource file depends on xml file and ui file.
Rakefile might seem to be difficult for beginners.
But, you can use any Ruby syntax in Rakefile, so it is really flexible.
If you practice Ruby and Rakefile, it will be highly productive tools.
## Meson and ninja
Meson is one of the most popular building tool despite the developing version.
And ninja is similar to make but much faster than make.
Several years ago, most of the C developers used autotools and make.
But now the situation has changed.
Many developers are using meson and ninja now.
To use meson, you first need to write `meson.build` file.
@@@include @@@include
tfe4/meson.build tfe4/meson.build
@ -199,13 +116,100 @@ Then, the executable file `tfe` is generated under the directory `_build`.
$ _build/tfe tfe.c tfetextview.c $ _build/tfe tfe.c tfetextview.c
Then the window appears. A window appears.
And two notebook pages are in the window. It includes a notebook with two pages.
One notebook is `tfe.c` and the other is `tfetextview.c`. One is `tfe.c` and the other is `tfetextview.c`.
I've shown you three build tools. For further information, see [The Meson Build system](https://mesonbuild.com/).
I think meson and ninja is the best choice for the present.
We divided a file into some categorized files and used a build tool. ## Make
This method is used by many developers.
Make is a build tool created in 1976.
It was a standard build tool for C compiling, but lately it is replaced by Meson and Ninja.
Make analyzes Makefile and executes compilers.
All instructions are written in Makefile.
For example,
~~~makefile
sample.o: sample.c
gcc -o sample.o sample.c
~~~
Malefile above consists of three elements, `sample.o`, `sample.c` and `gcc -o sample.o sample.c`.
- `sample.o` is a target.
- `sample.c` is a prerequisite.
- `gcc -o sample.o sample.c` is a recipe.
Recipes follow tab characters, not spaces.
(It is very important. Use tab, or make won't work as you expected).
The rule is:
If a prerequisite modified later than a target, then make executes the recipe.
In the example above, if `sample.c` is modified after the generation of `sample.o`, then make executes gcc and compile `sample.c` into `sample.o`.
If the modification time of `sample.c` is older then the generation of `sample.o`, then no compiling is necessary, so make does nothing.
The Makefile for `tfe` is as follows.
@@@include
tfe4/Makefile
@@@
You just type `make` and everything will be done.
~~~
$ make
gcc -c -o tfe.o `pkg-config --cflags gtk4` tfe.c
gcc -c -o tfetextview.o `pkg-config --cflags gtk4` tfetextview.c
glib-compile-resources tfe.gresource.xml --target=resources.c --generate-source
gcc -c -o resources.o `pkg-config --cflags gtk4` resources.c
gcc -o tfe tfe.o tfetextview.o resources.o `pkg-config --libs gtk4`
~~~
I used only very basic rules to write this Makefile.
There are many more convenient methods to make it more compact.
But it will be long to explain it.
So I want to finish with make and move on to the next topic.
You can download "Gnu Make Manual" from [GNU website](https://www.gnu.org/software/make/manual/).
## Rake
Rake is a similar program to make.
It is written in Ruby language.
If you don't use Ruby, you don't need to read this subsection.
However, Ruby is really sophisticated and recommendable script language.
- Rakefile controls the behavior of `rake`.
- You can write any Ruby code in Rakefile.
Rake has task and file task, which is similar to target, prerequisite and recipe in make.
@@@include
tfe4/Rakefile
@@@
The contents of the `Rakefile` is almost same as the `Makefile` in the previous subsection.
- 3-8: Defines target file, source files and so on.
- 1, 10 Requires rake/clean library. And clean files are added to CLEAN.
The files included by CLEAN will be removed when `rake clean` is typed on the command line.
- 12: The default target depends on `targetfile`.
The task `default` is the final goal of tasks.
- 14-16: `targetfile` depends on `objfiles`.
The variable `t` is a task object.
- `t.name` is a target name
- `t.prerequisites` is an array of prerequisites.
- `t.source` is the first element of prerequisites.
- `sh` is a method to give the following string to shell as an argument and executes the shell.
- 18-23: An each iterator of the array `objfiles`. Each object depends on corresponding source file.
- 25-27: Resource file depends on ui file.
Rakefile might seem to be difficult for beginners.
But, you can use any Ruby syntax in the Rakefile, so it is really flexible.
If you practice Ruby and Rakefile, it will be highly productive tools.
For further information, see [Rake tutorial for beginners](https://toshiocp.github.io/Rake-tutorial-for-beginners-en/LearningRake.html).

View file

@ -1,9 +1,8 @@
# Initialization and destruction of instances # Instance Initialization and destruction
A new version of the text file editor (`tfe`) will be made in this section and the following four sections. A new version of the text file editor (`tfe`) will be made in this section and the following four sections.
It is `tfe5`. It is `tfe5`.
There are many changes from the prior version. There are many changes from the prior version.
All the sources are listed in [Section 16](sec16.src.md).
They are located in two directories, [src/tfe5](tfe5) and [src/tfetextview](tfetextview). They are located in two directories, [src/tfe5](tfe5) and [src/tfetextview](tfetextview).
## Encapsulation ## Encapsulation
@ -33,11 +32,10 @@ After that I will explain:
## GObject and its children ## GObject and its children
GObject and its children are objects, which have both class and instance. GObject and its children are objects, which have both class and object C structures.
First, think about instance of objects. First, think about instances.
Instance is structured memories. An instance is memories which has the object structure.
The structure is described as C language structure. The following is the structure of TfeTextView.
The following is a structure of TfeTextView.
~~~C ~~~C
/* This typedef statement is automatically generated by the macro G_DECLARE_FINAL_TYPE */ /* This typedef statement is automatically generated by the macro G_DECLARE_FINAL_TYPE */
@ -54,16 +52,10 @@ The members of the structure are:
- The type of `parent` is GtkTextView which is C structure. - The type of `parent` is GtkTextView which is C structure.
It is declared in `gtktextview.h`. It is declared in `gtktextview.h`.
GtkTextView is the parent of TfeTextView. GtkTextView is the parent of TfeTextView.
- `file` is a pointer to GFile. It can be NULL if no file corresponds to the TfeTextView object. - `file` is a pointer to a GFile. It can be NULL if no file corresponds to the TfeTextView instance.
Notice the program above is the declaration of the structure, not the definition. You can find the declaration of the ancestors' object structures in the source files of GTK and GLib.
So, no memories are allocated at this moment. The following is extracted from the source files (not exactly the same).
They are to be allocated when `tfe_text_view_new` function is invoked.
The memory allocated with `tfe_text_view_new` is an instance of TfeTextView object.
Therefore, There can be multiple TfeTextView instances if `tfe_text_view_new` is called multiple times.
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).
~~~C ~~~C
typedef struct _GObject GObject; typedef struct _GObject GObject;
@ -91,15 +83,45 @@ struct _GtkTextView
~~~ ~~~
In each structure, its parent is declared at the top of the members. In each structure, its parent is declared at the top of the members.
So, every ancestors is included in the child instance. So, all the ancestors are included in the child object.
This is very important.
It guarantees a child widget to inherit all the features from ancestors.
The structure of `TfeTextView` is like the following diagram. The structure of `TfeTextView` is like the following diagram.
![The structure of the instance TfeTextView](../image/TfeTextView.png){width=14.39cm height=2.16cm} ![The structure of the instance TfeTextView](../image/TfeTextView.png){width=14.39cm height=2.16cm}
Derivable classes (ancestor classes) have their own private data area which are not included by the structure above.
For example, GtkWidget has GtkWidgetPrivate (C structure) for its private data.
## Initialization of a TfeTextView instance Notice declarations are not definitions.
So, no memories are allocated when C structures are declared.
Memories are allocated to them from the heap area when the `tfe_text_view_new` function is called.
At the same time, the ancestors' private area allocated for the TfeTetView.
They are hidden from TfeTextView and it can't access to them directly.
The created memory is called instance.
When a TfeTextView instance is created, it is given three data area.
- The instance (C structure).
- GtkWidgetPrivate structure.
- GtkTextViewPrivate structure.
TfeTextView functions can access to its instance only.
The GtkWidgetPrivate and GtkTextViewPrivate are used by the ancestors' functions.
See the following example.
~~~C
GtkWidget *tv = tfe_text_view_new ();
GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
~~~
The parent's function `gtk_text_view_get_buffer` accesses the GtkTextViewPrivate data (owned by `tv`).
There is a pointer, which points the GtkBuffer, in the private area and the function returns the pointer.
(Actual behavior is a bit more complicated.)
TfeTextView instances inherit the ancestors functions like this.
A TfeTextView instance is created every time the `tfe_text_view_new` function is called.
Therefore, multiple TfeTextView instances can exist.
## Initialization of TfeTextView instances
The function `tfe_text_view_new` creates a new TfeTextView instance. The function `tfe_text_view_new` creates a new TfeTextView instance.
@ -110,12 +132,13 @@ tfetextview/tfetextview.c tfe_text_view_new
When this function is invoked, a TfeTextView instance is created and initialized. When this function is invoked, a TfeTextView instance is created and initialized.
The initialization process is as follows. The initialization process is as follows.
1. Initializes GObject (GInitiallyUnowned) part in TfeTextView instance. 1. When the instance is created, GtkWidgetPrivate and GtkTextViewPrivate structures are also created
2. Initializes GtkWidget part in TfeTextView instance. 2. Initializes GObject (GInitiallyUnowned) part in the TfeTextView instance.
3. Initializes GtkTextView part in TfeTextView instance. 3. Initializes GtkWidget part (the first `priv`) in the TfeTextView instance and GtkWidgetPrivate structure.
4. Initializes TfeTextView part in TfeTextView instance. 4. Initializes GtkTextView part (the second `priv`) in the TfeTextView instance and GtkTextViewPrivate structure.
5. Initializes TfeTextView part (`file`) in the TfeTextView instance.
The step one through three is done by `g_object_init`, `gtk_widget_init` and `gtk_text_view_init`. The step two through four is done by `g_object_init`, `gtk_widget_init` and `gtk_text_view_init`.
They are called by the system automatically and you don't need to care about them. They are called by the system automatically and you don't need to care about them.
Step four is done by the function `tfe_text_view_init` in `tfetextview.c`. Step four is done by the function `tfe_text_view_init` in `tfetextview.c`.
@ -128,28 +151,31 @@ This function just initializes `tv->file` to be `NULL`.
## Functions and Classes ## Functions and Classes
In Gtk, all objects derived from GObject have class and instance (except abstract object). 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. Instances are memory of C structure, which are described in the previous two subsections.
Each object can have more than one instance. Each object can have more than one instance.
Those instances have the same structure. Those instances have the same structure.
An instance just keeps status of the instance. Instances just have data.
Therefore, it is insufficient to define its behavior. Therefore, it doesn't define object's behavior.
We need at least two things. We need at least two things.
One is functions and the other is class. One is functions and the other is class methods.
You've already seen many functions. You've already seen many functions.
For example, `tfe_text_view_new` is a function to create a TfeTextView instance. For example,
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.
Class comprises mainly pointers to functions. - `TfeTextView *tfe_text_view_new (void);` is a function to create a TfeTextView instance.
Those functions are used by the object itself or its descendant objects. - `GtkTextBuffer *gtk_text_view_get_buffer (GtkTextView *textview)` is a function to get a GtkTextBuffer from GtkTextView.
Functions are public, which means that they are expected to be used by other objects.
They are similar to public methods in object oriented languages.
Class (C structure) mainly consists of pointers to functions.
The functions are called class methods and used by the object itself or its descendant objects.
For example, GObject class is declared in `gobject.h` in GLib source files. For example, GObject class is declared in `gobject.h` in GLib source files.
@@@include @@@include
class_gobject.c class_gobject.c
@@@ @@@
I'd like to explain some of the members.
There's a pointer to the function `dispose` in line 23. There's a pointer to the function `dispose` in line 23.
~~~C ~~~C
@ -167,13 +193,13 @@ void (*finalize) (GObject *object);
Look at the declaration of `_GObjectClass` so that you would find that most of the members are pointers to functions. Look at the declaration of `_GObjectClass` so that you would find that most of the members are pointers to functions.
- 11: A function pointed by `constructor` is called when the instance is generated. It completes the initialization of the instance. - 13: A function pointed by `constructor` is called when the instance is generated. It completes the initialization of the instance.
- 23: A function pointed by `dispose` is called when the instance destructs itself. - 25: A function pointed by `dispose` is called when the instance destructs itself.
Destruction process is divided into two phases. Destruction process is divided into two phases.
The first one is called disposing. The first one is called disposing.
In this phase, the instance releases all the references to other instances. In this phase, the instance releases all the references to other instances.
The second phase is finalizing. The second phase is finalizing.
- 24: A function pointed by `finalize` finishes the destruction process. - 26: A function pointed by `finalize` finishes the destruction process.
- The other pointers point to functions which are called while the instance lives. - The other pointers point to functions which are called while the instance lives.
These functions are called class methods. These functions are called class methods.
@ -182,25 +208,14 @@ But not open to the objects which are not the descendants.
## TfeTextView class ## TfeTextView class
TfeTextView class is a structure and it includes all its ancestors' class in it. TfeTextView class is a structure and it includes all its ancestors' classes in it.
Therefore, classes have similar hierarchy to instances.
~~~C ~~~
typedef _TfeTextView TfeTextView; GObjectClass (GInitiallyUnownedClass) -- GtkWidgetClass -- GtkTextViewClass -- TfeTextViewClass
struct _TfeTextView {
GtkTextView parent;
GFile *file;
};
~~~ ~~~
TfeTextView structure has GtkTextView type as the first member. The following is extracted from the source codes (not exactly the same).
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.
GObject -- GInitiallyUnowned -- GtkWidget -- GtkTextView -- TfeTextView
The following is extracts from the source files (not exactly the same).
@@@include @@@include
classes.c classes.c
@ -208,11 +223,11 @@ classes.c
- 120-122: This three lines are generated by the macro `G_DECLARE_FINAL_TYPE`. - 120-122: This three lines are generated by the macro `G_DECLARE_FINAL_TYPE`.
So, they are not written in either `tfe_text_view.h` or `tfe_text_view.c`. So, they are not written in either `tfe_text_view.h` or `tfe_text_view.c`.
- 3, 84, 121: Each derived class puts its parent class at the first member of its structure. - 3, 84, 121: Each class has its parent class at the first member of its structure.
It is the same as instance structures. It is the same as instance structures.
- Class members in ancestors are open to the descendant class. - Class members in ancestors are open to the descendant class.
So, they can be changed in `tfe_text_view_class_init` function. So, they can be changed in `tfe_text_view_class_init` function.
For example, the `dispose` pointer in GObjectClass will be overridden later in `tfe_text_view_class_init`. For example, the `finalize` pointer in GObjectClass will be overridden later in `tfe_text_view_class_init`.
(Override is an object oriented programming terminology. (Override is an object oriented programming terminology.
Override is rewriting ancestors' class methods in the descendant class.) Override is rewriting ancestors' class methods in the descendant class.)
- Some class methods are often overridden. - Some class methods are often overridden.
@ -242,17 +257,15 @@ Then C destructs itself and finally the memories allocated to C is freed.
The idea above is based on an assumption that an object referred by nothing has reference count of zero. 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. When the reference count drops to zero, the object starts its destruction process.
The destruction process is split into two phases: disposing and finalizing. The destruction process is split into two phases: disposing and finalizing.
In the disposing process, the object invokes the function pointed by `dispose` in its class to release all references to other objects. In the disposing process, the object invokes the function pointed by `dispose` in its class to release all references to other instances.
In the finalizing process, it invokes the function pointed by `finalize` in its class to complete the destruction process. After that, it invokes the function pointed by `finalize` in its class to complete the destruction process.
These functions are also called handlers or methods.
For example, dispose handler or dispose method. For example, dispose handler or dispose method.
In the destruction process of TfeTextView, the reference count of widgets related to TfeTextView is automatically decreased. In the destruction process, TfeTextView needs to unref the GFile pointed by `tv->file`.
But GFile pointed by `tv->file` needs to decrease its reference count by one. You must write the dispose handler `tfe_text_view_dispose`.
You must write the code in the dispose handler `tfe_text_view_dispose`.
@@@include @@@include
tfetextview/tfetextview.c tfe_text_view_dispose tfetextview/tfetextview.c tfe_text_view_dispose
@@@ @@@
- 5,6: If `tv->file` points a GFile, decrease its reference count. - 5,6: If `tv->file` points a GFile, decrease its reference count.
@ -262,7 +275,7 @@ In dispose handlers, we usually use `g_clear_object` rather than `g_object_unref
In the disposing process, the object uses the pointer in its class to call the handler. In the disposing process, the object uses the pointer in its class to call the handler.
Therefore, `tfe_text_view_dispose` needs to be registered in the class when the TfeTextView class is initialized. Therefore, `tfe_text_view_dispose` needs to be registered in the class when the TfeTextView class is initialized.
The function `tfe_text_view_class_init` is the class initialization function and it is declared in the replacement produced by `G_DEFINE_TYPE` macro. The function `tfe_text_view_class_init` is the class initialization function and it is declared in the `G_DEFINE_TYPE` macro expansion.
~~~C ~~~C
static void static void
@ -290,10 +303,9 @@ Then it invokes its parent's dispose handler in line 8.
G_OBJECT_CLASS (tfe_text_view_parent_class)->dispose (gobject); G_OBJECT_CLASS (tfe_text_view_parent_class)->dispose (gobject);
~~~ ~~~
`tfe_text_view_parent_class`,which is made by `G_DEFINE_TYPE` macro, is a pointer that points the parent object class. A variable `tfe_text_view_parent_class`, which is made by `G_DEFINE_TYPE` macro, is a pointer that points the parent object class.
The variable `gobject` is a pointer to TfeTextView instance which is casted as a GObject instance.
Therefore, `G_OBJECT_CLASS (tfe_text_view_parent_class)->dispose` points the handler `dh3` in the diagram above. Therefore, `G_OBJECT_CLASS (tfe_text_view_parent_class)->dispose` points the handler `dh3` in the diagram above.
And `gobject` is a pointer to TfeTextView instance which is casted as a GObject instance. The statement `G_OBJECT_CLASS (tfe_text_view_parent_class)->dispose (gobject)` is the same as `dh3 (gobject)`, which means it releases all the reference to the other instances in the GtkTextViewPrivate in the TfeTextView instance.
`dh3` releases all the references to objects in the GtkTextView part (it is actually the private area pointed by `prev`) in TfeTextView instance.
After that, `dh3` calls `dh2`, and `dh2` calls `dh1`. After that, `dh3` calls `dh2`, and `dh2` calls `dh1`.
Finally all the references are released. Finally all the references are released.

View file

@ -2,14 +2,14 @@
## Signals ## Signals
In Gtk programming, each object is encapsulated. Each object is encapsulated in Gtk programming.
And it is not recommended to use global variables because they tend to make the program complicated. And it is not recommended to use global variables because they are prone to make the program complicated.
So, we need something to communicate between objects. So, we need something to communicate between objects.
There are two ways to do so. There are two ways to do so.
- Functions. - Functions.
For example, `tb = gtk_text_view_get_buffer (tv)`. For example, `tb = gtk_text_view_get_buffer (tv)`.
The caller requests `tv` to give `tb`, which is a GtkTextBuffer instance connected to `tv` to the caller. The caller requests `tv` to give `tb`, which is a GtkTextBuffer instance connected to `tv`.
- Signals. - Signals.
For example, `activate` signal on GApplication object. For example, `activate` signal on GApplication object.
When the application is activated, the signal is emitted. When the application is activated, the signal is emitted.
@ -27,7 +27,7 @@ The registration is done usually when the object class is initialized.
2. Signals are connected to handlers by `g_connect_signal` or its family functions. 2. Signals are connected to handlers by `g_connect_signal` or its family functions.
The connection is usually done out of the object. The connection is usually done out of the object.
3. When Signals are emitted, the connected handlers are invoked. 3. When Signals are emitted, the connected handlers are invoked.
Signal is emitted on the instance of the object. Signals are emitted on the instance of the object.
## Signal registration ## Signal registration
@ -39,8 +39,7 @@ This signal is emitted when `tv->file` is changed.
`tfe_text_view_open` function is not able to return the status because it uses GtkFileChooserDialog. `tfe_text_view_open` function is not able to return the status because it uses GtkFileChooserDialog.
This signal is emitted instead of the return value of the function. This signal is emitted instead of the return value of the function.
A static variable or array is used to store the signal ID. A static variable or array is used to store signal ID.
A static array is used to register two or more signals.
~~~C ~~~C
enum { enum {
@ -60,10 +59,10 @@ tfetextview/tfetextview.c tfe_text_view_class_init
- 6-15: Registers "change-file" signal. - 6-15: Registers "change-file" signal.
`g_signal_new` function is used. `g_signal_new` function is used.
The signal "change-file" has no default handler (object method handler). The signal "change-file" has no default handler (object method handler) so the offset (the line 9) is set to zero.
You usually don't need to set a default handler. You usually don't need a default handler.
If you need it, use `g_signal_new_class_handler` function. If you need it, use `g_signal_new_class_handler` function instead of `g_signal_new`.
See [GObject API Reference, g\_signal\_new\_class\_handler](https://docs.gtk.org/gobject/func.signal_new_class_handler.html) for further information. See [GObject API Reference](https://docs.gtk.org/gobject/func.signal_new_class_handler.html) for further information.
- The return value of `g_signal_new` is the signal id. - The return value of `g_signal_new` is the signal id.
The type of signal id is guint, which is the same as unsigned int. The type of signal id is guint, which is the same as unsigned int.
It is used in the function `g_signal_emit`. It is used in the function `g_signal_emit`.

View file

@ -1,30 +1,36 @@
# Functions in TfeTextView # Functions in TfeTextView
In this section I will explain functions in TfeTextView object. TfeTextView functions are described in this section.
## tfe.h and tfetextview.h ## tfetextview.h
`tfe.h` is a top header file and it includes `gtk.h` and all the header files. The header file `tfetextview.h` provides:
C source files `tfeapplication.c` and `tfenotebook.c` include `tfe.h` at the beginning.
@@@include - The type of TfeTextView, which is `TFE_TYPE_TEXT_VIEW`.
tfe5/tfe.h - The expansion of `G_DECLARE_FINAL_TYPE` includes some useful macros.
@@@ - Constants for the `open-response` signal is defined..
- Public functions of `tfetextview.c` are declared.
`../tfetextview/tfetextview.h` is a header file which describes the public functions in `tfetextview.c`. Therefore, Any programs use TfeTextView needs to include `tfetextview.h`.
@@@include @@@include
tfetextview//tfetextview.h tfetextview//tfetextview.h
@@@ @@@
- 1,2,35: Thanks to these three lines, the following lines are included only once. - 1,2,35: Thanks to these three lines, the following lines are included only once.
You can use `#pragma once` instead of them.
It is non-standard but widely used.
- 4: Includes gtk4 header files. - 4: Includes gtk4 header files.
The header file `gtk4` also has the same mechanism to avoid including it multiple times. The header file `gtk4` also has the same mechanism to avoid including it multiple times.
- 6-7: These two lines define TfeTextView type, its class structure and some useful macros. - 6-7: These two lines define TfeTextView type, its class structure and some useful macros.
- `TfeTextView` and `TfeTextViewClass` are declared as typedef of C structures.
- You need to define a structure `_TfeTextView` later.
- The class structure `_TfeTextViewClass` is defined here. You don't need to define it by yourself.
- Convenience functions `TFE_TEXT_VIEW ()` for casting and `TFE_IS_TEXT_VIEW` for type check are defined.
- 9-15: A definition of the value of the parameter of "open-response" signal. - 9-15: A definition of the value of the parameter of "open-response" signal.
- 17-33: Declarations of public functions on TfeTextView. - 17-33: Declarations of public functions on TfeTextView.
## Functions to create TfeTextView instances ## Instance creation Functions
A TfeTextView instance is created with `tfe_text_view_new` or `tfe_text_view_new_with_file`. A TfeTextView instance is created with `tfe_text_view_new` or `tfe_text_view_new_with_file`.
@ -49,10 +55,13 @@ tfetextview/tfetextview.c tfe_text_view_new_with_file tfe_text_view_new
- 23-25: `tfe_text_view_new` function. - 23-25: `tfe_text_view_new` function.
Just returns the value from the function `g_object_new` but casts it to the pointer to GtkWidget. Just returns the value from the function `g_object_new` but casts it to the pointer to GtkWidget.
Initialization is done in `tfe_text_view_init` which is called in the process of `g_object_new` function. The function `g_object_new` creates any instances of its descendant class.
The arguments are the type of the class, property list and NULL.
Null is the end mark of the property list.
TfeTextView "wrap-mode" property has GTK\_WRAP\_WORD\_CHAR as the default value.
- 1-21: `tfe_text_view_new_with_file` function. - 1-21: `tfe_text_view_new_with_file` function.
- 3: `g_return_val_if_fail` is described in [GLib API Reference, g\_return\_val\_if\_fail](https://docs.gtk.org/glib/func.return_val_if_fail.html). - 3: `g_return_val_if_fail` is described in [GLib API Reference -- g\_return\_val\_if\_fail](https://docs.gtk.org/glib/func.return_val_if_fail.html).
And also [GLib API Reference, Message Logging](https://docs.gtk.org/glib/logging.html). And also [GLib API Reference -- Message Logging](https://docs.gtk.org/glib/logging.html).
It tests whether the argument `file` is a pointer to GFile. It tests whether the argument `file` is a pointer to GFile.
If it's true, then the program goes on to the next line. If it's true, then the program goes on to the next line.
If it's false, then it returns NULL (the second argument) immediately. If it's false, then it returns NULL (the second argument) immediately.
@ -77,7 +86,7 @@ If the modification flag is FALSE, it doesn't need to save the contents.
- 20: Returns `tv`, which is a pointer to the newly created TfeTextView instance. - 20: Returns `tv`, which is a pointer to the newly created TfeTextView instance.
If an error happens, NULL is returned. If an error happens, NULL is returned.
## Save and saveas functions ## Save related functions
Save and saveas functions write the contents in the GtkTextBuffer to a file. Save and saveas functions write the contents in the GtkTextBuffer to a file.
@ -98,65 +107,118 @@ Then, the function changes `tv->file` and save the contents to the specified fil
If an error occurs, it is shown to the user through the message dialog. If an error occurs, it is shown to the user through the message dialog.
The error is managed only in the TfeTextView and no information is notified to the caller. The error is managed only in the TfeTextView and no information is notified to the caller.
### save\_file function
@@@include @@@include
tfetextview/tfetextview.c save_file saveas_dialog_response tfe_text_view_save tfe_text_view_saveas tfetextview/tfetextview.c save_file
@@@ @@@
- 1-26: `save_file` function. - The function `save_file` is called from `saveas_dialog_response` and `tfe_text_view_save`.
This function is called from `saveas_dialog_response` and `tfe_text_view_save`.
This function saves the contents of the buffer to the file given as an argument. This function saves the contents of the buffer to the file given as an argument.
If error happens, it displays an error message. If error happens, it displays an error message.
So, a caller of this function don't need to take care of errors.
The class of this function is `static`. The class of this function is `static`.
Therefore, only functions in this file (`tfeTetview.c`) call this function. Therefore, only functions in this file (`tfetextview.c`) call this function.
Such static functions usally don't have `g_return_val_if_fail` function. Such static functions usually don't have `g_return_val_if_fail` function.
- 10-11: Gets the text contents from the buffer. - 10-11: Gets the text contents from the buffer.
- 12-14: Saves the contents to the file. - 12: The function `g_file_replace_contents` writes the contents to the file and returns the status (true = success/ false = fail).
If no error happens, set the modified flag to be FALSE. It has many parameters, but some of them are almost always given the same values.
- GFile* file: GFile to which the contents are saved.
- const char* contents: contents to be saved. The string is owned by the caller.
- gsize length: the length of the contents
- const char* etag: entity tag. It is usually NULL.
- gboolean make_backup: true to make a backup if the file exists. false not to make it. the file will be overwritten.
- GFileCreateFlags flags: usually `G_FILE_CREATE_NONE` is fine.
- char** new_etag: new entity tag. It is usually NULL.
- GCancellable* cancellable: If a cancellable instance is set, the other thread can cancel this operation. it is usually NULL.
- GError** error: If error happens, GError will be set.
- 13,14: If no error happens, set the modified flag to be FALSE.
This means that the buffer is not modified since it has been saved. This means that the buffer is not modified since it has been saved.
And set the return status `stat` to be TRUE. - 15-22: If it fails to save the contents, an error message will be displayed.
- 15-23: If it fails to save the contents, displays an error message. - 17-18: Creates a message dialog. The parameters are:
- 16-18: Creates a message dialog with the error message. - GtkWindow* parent: transient parent window.
- 19: Connects the "response" signal to `gtk_window_destroy`, so that the dialog disappears when a user clicked on the button. This allows window managers to keep the dialog on top of the parent window, or center the dialog over the parent window.
- 20-21: Shows the window, frees `err` and set `stat` to be FLASE. It is possible to give no parent window to the dialog.
- 24: Frees `contents`. However, it is encouraged to give parents to dialogs.
- 25: Returns to the caller. - GtkDialogFlags flags: GTK\_DIALOG\_MODAL for modal dialog. A modal dialog is usually fine.
- 28-47: `saveas_dialog_response` function. - GtkMessageType type: GTK\_MESSAGE\_ERROR for error message. Other options are GTK\_MESSAGE\_INFO, GTK\_MESSAGE\_WARNING and so on.
This is a signal handler for the "response" signal on GtkFileChooserDialog instance created by `tfe_text_view_saveas` function. - GtkButtonsType buttons: GTK\_BUTTON\_OK is used most often. Other option is GTK\_BUTTON\_YES\_NO, GTK\_BUTTON\_CANCEL and so on.
This handler analyzes the response and determines whether to save the contents. - const gchar* message_format: gchar is the same as char. This format is the same as printf. Arguments for the message format follow.
- 34-45: If the response is `GTK_RESPONSE_ACCEPT`, the user has clicked on the `Save` button. So, it tries to save. - 19: Connects the "response" signal to `gtk_window_destroy`, so that the dialog disappears when the user clicks on the button.
- 35: Gets the GFile `file` from GtkFileChooserDialog. - 20: Shows the message dialog.
- 36-37: If it doesn't point GFile, it outputs an error message to the log. - 21: Frees `err` with `g_error_free` function.
- 38: Otherwise, it calls `save_file` to save the contents to the file. - 23: Frees `contents`.
- 39-42: If `save_file` has successfully saved the contents, `tv->file` is updated. - 24: Returns to the caller.
If the old GFile pointed by `tv->file` exists, it is freed in advance.
Emits "change-file" signal. ### saveas\_dialog\_response function
- 44: Unrefs `file`.
- 46: destroys the file chooser dialog. @@@include
- 49-64: `tfe_text_view_save` function. tfetextview/tfetextview.c saveas_dialog_response
- 51: `tfe_text_view_save` is public, i.e. it is open to the other files. @@@
- The function `saveas_dialog_response` is a signal handler for the "response" signal on GtkFileChooserDialog.
This handler analyzes the response and determines whether to save the contents or not.
- 7-25: If the response is `GTK_RESPONSE_ACCEPT`, the user has clicked on the `Save` button and the contents will be saved.
- 8: Gets the GFile `file` from the GtkFileChooserDialog.
- 9-10: If it doesn't point GFile, a warning message will be output to the log. This is not expected.
- 11: Otherwise, it calls `save_file` to save the contents to the file.
- 12-22: If `save_file` has successfully saved the contents, the following will be done.
- If `tv->file` is GFile and `file` is a different file, unref `tv->file`.
- If `tv->file` is GFile and `file` points the same file as `tv->file`, nothing needs to do.
Otherwise, `tv->file` is set to `file` and "change-file" signal is emitted.
- 22, 24: Unref `file`.
- 26: destroys the file chooser dialog.
### tfe\_text\_view\_save function
@@@include
tfetextview/tfetextview.c tfe_text_view_save
@@@
- The function `tfe_text_view_save` writes the contents to the `tv->file` file.
It calls `tfe_text_view_saveas` or `save_file`.
- 1-3: The function is public, i.e. it is open to the other objects.
So, it doesn't have `static` class. So, it doesn't have `static` class.
Public functions should check the parameter type with `g_return_if_fail` function. Public functions should check the parameter type with `g_return_if_fail` function.
If `tv` is not a pointer to a TfeTextView instance, then it logs an error message and immediately returns. If `tv` is not a pointer to a TfeTextView instance, then it logs an error message and immediately returns.
This function is similar to `g_return_val_if_fail`, but no value is returned because `tfe_text_view_save` doesn't return a value. This function is similar to `g_return_val_if_fail`, but no value is returned because `tfe_text_view_save` doesn't return a value (void).
- 53-54: Gets GtkTextBuffer instance and GtkWidget instance and assignes them to `tb` and`win` respectively. - 5-6: GtkTextBuffer `tb` and GtkWidget (GtkWindow) `win` are set.
- 56-57: If the buffer hasn't modified, then it doesn't need to save it. The function `gtk_widget_get_ancestor (widget, type)` returns the first ancestor of the widget with type.
So the function returns. The type is a GType.
- 58-59: If `tv->file` is NULL, no file has given yet. For example, the type of GtkWindow is `GTK_TYPE_WINDOW` and the one of TfeTextView is `TFE_TYPE_TEXT_VIEW`.
It calls `tfe_text_view_saveas` which prompts a user to select a file or specify a new file to save. Be careful. The parent-child relationship here is the one for widgets, not classes.
- 60-61: If `tv->file` doesn't point GFile, somethig bad has happened. The top level window may be a GtkApplicationWindow, but it depends on the application.
Logs an error message. Because TfeTextView is a library, it can't determine the top level window type (GtkWindow or GtkApplicationWindow).
- 62-63: Calls `save_file` to save the contents to the file. GtkWindow is a parent class of GtkApplication window so it is more general.
- 66-79: `tfe_text_view_saveas` function. Therefore, TfeTextView takes GtkWindow as a top level window so that it can be used by any application.
It shows GtkFileChooserDialog and prompts the user to choose a file. - 8-9: If the buffer hasn't modified, it doesn't need to be saved.
- 73-76: Creates GtkFileChooserDialog. - 10-11: If `tv->file` is NULL, which means no file has given yet, it calls `tfe_text_view_saveas` to prompt a user to select a file and save the contents.
The title is "Save file". - 12-13: If `tv->file` doesn't point GFile, an error message is logged out. It is not expected.
Transient parent of the dialog is `win`, which is the top-level window. - 14-15: Otherwise, it calls `save_file` to save the contents to the file `tv->file`.
The action is save mode.
The buttons are Cancel and Save.
- 77: connects the "response" signal of the dialog and `saveas_dialog_response` handler.
- 78: Shows the dialog.
![Saveas process](../image/saveas.png){width=10.7cm height=5.16cm} ### tfe\_text\_view\_saveas function
@@@include
tfetextview/tfetextview.c tfe_text_view_saveas
@@@
- The function `tfe_text_view_saveas` shows GtkFileChooserDialog and prompts the user to choose a file and save the contents.
- 1-3: Check the type of `tv` because the caller may be other object. This function is public.
- 6: GtkWidget `win` is set to the top level window.
- 8-11: Creates GtkFileChooserDialog. It has at least four parameters.
- const char* title: title of the dialog shown at the bar.
- GtkWindow* parent: transient parent window.
- GtkFileChooserAction action: action is one of `GTK_FILE_CHOOSER_ACTION_OPEN`, `GTK_FILE_CHOOSER_ACTION_SAVE`
and `GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER`.
When you want to save a file, `GTK_FILE_CHOOSER_ACTION_SAVE` is the action.
- const char* first_button_text: the label text of the first button.
- type: response ID for the sirst button. response ID is one of the GtkReponseType.
See [GtkResponseType](https://docs.gtk.org/gtk4/enum.ResponseType.html).
- followed by pairs of button text and response ID...
- NULL is put at the end of the list.
- The GtkFileChooserDialog will have the title "Save file", transient parent `win`, save mode action, cancel and save button.
- 12: connects the "response" signal and `saveas_dialog_response` handler.
- 13: Shows the dialog.
When you use GtkFileChooserDialog, you need to divide the program into two parts. When you use GtkFileChooserDialog, you need to divide the program into two parts.
One is a function which creates GtkFileChooserDialog and the other is a signal handler. One is a function which creates GtkFileChooserDialog and the other is a signal handler.
@ -164,9 +226,11 @@ The function just creates and shows GtkFileChooserDialog.
The rest is done by the handler. The rest is done by the handler.
It gets Gfile from GtkFileChooserDialog and saves the buffer to the file by calling `save_file`. It gets Gfile from GtkFileChooserDialog and saves the buffer to the file by calling `save_file`.
## Open function ![Saveas process](../image/saveas.png){width=10.7cm height=5.16cm}
Open function shows GtkFileChooserDialog to users and prompts them to choose a file. ## Open related functions
Open function shows GtkFileChooserDialog to a user and prompts them to choose a file.
Then it reads the file and puts the text into GtkTextBuffer. Then it reads the file and puts the text into GtkTextBuffer.
~~~C ~~~C
@ -175,10 +239,8 @@ void tfe_text_view_open (TfeTextView *tv, GtkWindow *win);
The parameter `win` is the top-level window. The parameter `win` is the top-level window.
It will be a transient parent window of GtkFileChooserDialog when the dialog is created. It will be a transient parent window of GtkFileChooserDialog when the dialog is created.
This allows window managers to keep the dialog on top of the parent window, or center the dialog over the parent window.
It is possible to give no parent window to the dialog. This function may be called just after `tv` has been created.
However, it is encouraged to give a parent window to dialog.
This function might be called just after `tv` has been created.
In that case, `tv` has not been incorporated into the widget hierarchy. In that case, `tv` has not been incorporated into the widget hierarchy.
Therefore it is impossible to get the top-level window from `tv`. Therefore it is impossible to get the top-level window from `tv`.
That's why the function needs `win` parameter. That's why the function needs `win` parameter.
@ -186,38 +248,58 @@ That's why the function needs `win` parameter.
This function is usually called when the buffer of `tv` is empty. This function is usually called when the buffer of `tv` is empty.
However, even if the buffer is not empty, `tfe_text_view_open` doesn't treat it as an error. However, even if the buffer is not empty, `tfe_text_view_open` doesn't treat it as an error.
If you want to revert the buffer, calling this function is appropriate. If you want to revert the buffer, calling this function is appropriate.
Otherwise probably bad things will happen.
Open and read process is divided into two phases.
One is showing GtkFileChooserDialog and the other is its response handler.
The response handler gets the filename, reads the contents of the file and puts it into the GtkTextBuffer.
### open\_dialog\_response function
@@@include @@@include
tfetextview/tfetextview.c open_dialog_response tfe_text_view_open tfetextview/tfetextview.c open_dialog_response
@@@ @@@
- 37-50: `tfe_text_view_open` function. - 2: The handler `open_dialog_response` has three parameters.
- 44-47: Creates GtkFileChooserDialog. - GtkWidget *dialog: FileChooserDialog on which the "response" signal is emitted.
The title is "Open file". - gint response: response ID
Transient parent window is the top-level window of the application, which is given by the caller. - TfeTextView *tv: textview to put the contents to
The action is open mode. - 10-11: If the response is not `GTK_RESPONSE_ACCEPT`, the user has clicked on the "Cancel" button or close button on the header bar.
The buttons are Cancel and Open.
- 48: connects the "response" signal of the dialog and `open_dialog_response` signal handler.
- 49: Shows the dialog.
- 1-35: `open_dialog_response` signal handler.
- 10-11: If the response from GtkFileChooserDialog is not `GTK_RESPONSE_ACCEPT`, the user has clicked on the "Cancel" button or close button on the header bar.
Then, "open-response" signal is emitted. Then, "open-response" signal is emitted.
The parameter of the signal is `TFE_OPEN_RESPONSE_CANCEL`. The parameter of the signal is `TFE_OPEN_RESPONSE_CANCEL`.
- 12-14: Gets the pointer to the Gfile by `gtk_file_chooser_get_file`. - 12-14: Gets the pointer to the GFile by `gtk_file_chooser_get_file`.
If it doesn't point GFile, maybe an error has occurred. If it doesn't point GFile, maybe an error has occurred.
It is not expected, though.
Then it emits "open-response" signal with the parameter `TFE_OPEN_RESPONSE_ERROR`. Then it emits "open-response" signal with the parameter `TFE_OPEN_RESPONSE_ERROR`.
- 15-23: If an error occurs at file reading, then it decreases the reference count of the Gfile, shows a message dialog to report the error to the user and emits "open-response" signal with the parameter `TFE_OPEN_RESPONSE_ERROR`. - 15-22: If an error occurs when file reading, then it decreases the reference count of the GFile, shows a message dialog to report the error to the user and emits "open-response" signal with the parameter `TFE_OPEN_RESPONSE_ERROR`.
- 24-33: If the file has successfully been read, then the text is inserted to GtkTextBuffer, frees the temporary buffer pointed by `contents` and sets `tv->file` to point the file (no duplication is not necessary). - 23-38: If the file has been successfully read, the following is carried out. (It is not simple.)
Then, it emits "open-response" signal with the parameter `TFE_OPEN_RESPONSE_SUCCESS` and emits "change-file" signal. - the text is inserted to GtkTextBuffer
- 34: destroys GtkFileCooserDialog. - the temporary buffer `contents` is freed
- modify-bit is set to FALSE
- open-response signal is emitted with `TFE_OPEN_REPONSE_SUCCESS`
- If the file are changed, change-file signal is emitted
- If `tv->file` isn't NULL, `g_object_unref(tv->file)` is called
- `tv->file` is assigned with `file`
- 39: destroys GtkFileChooserDialog.
Now let's think about the whole process between the caller and TfeTextView. ### tfe\_text\_view\_open function
It is shown in the following diagram and you would think that it is really complicated.
@@@include
tfetextview/tfetextview.c tfe_text_view_open
@@@
- 3-4: Check the type of the arguments `tv` and `win`.
Public functions always need to check the arguments.
- 10-11: Creates GtkFileChooserDialog.
- The title is "Open file".
- Transient parent window is the top-level window `win`.
- The action is open mode.
- The buttons are Cancel and Open.
- 12: connects the "response" signal and the handler.
- 13: Shows the dialog.
The whole process between the caller and TfeTextView is shown in the following diagram.
It is really complicated.
Because signal is the only way for GtkFileChooserDialog to communicate with others. Because signal is the only way for GtkFileChooserDialog to communicate with others.
In GTK 3, `gtk_dialog_run` function is available.
It simplifies the process.
However, in GTK 4, `gtk_dialog_run` is unavailable any more.
![Caller and TfeTextView](../image/open.png){width=12.405cm height=9.225cm} ![Caller and TfeTextView](../image/open.png){width=12.405cm height=9.225cm}
@ -228,9 +310,10 @@ However, in GTK 4, `gtk_dialog_run` is unavailable any more.
5. The handler reads the file and inserts the text into GtkTextBuffer and emits a signal to inform the status as a response code. 5. The handler reads the file and inserts the text into GtkTextBuffer and emits a signal to inform the status as a response code.
6. The handler out of the TfeTextView receives the signal. 6. The handler out of the TfeTextView receives the signal.
## Getting Gfile ## Getting GFile in TfeTextView
`gtk_text_view_get_file` is a simple function shown as follows. You can get the GFile in a TfeTextView instance with `tfe_text_view_get_file`.
It is very simple.
@@@include @@@include
tfetextview/tfetextview.c tfe_text_view_get_file tfetextview/tfetextview.c tfe_text_view_get_file
@ -240,7 +323,7 @@ The important thing is to duplicate `tv->file`.
Otherwise, if the caller frees the GFile object, `tv->file` is no more guaranteed to point the GFile. Otherwise, if the caller frees the GFile object, `tv->file` is no more guaranteed to point the GFile.
Another reason to use `g_file_dup` is that GFile isn't thread-safe. Another reason to use `g_file_dup` is that GFile isn't thread-safe.
If you use GFile in the different thread, the duplication is necessary. If you use GFile in the different thread, the duplication is necessary.
See [Gio API Reference, g\_file\_dup](https://docs.gtk.org/gio/method.File.dup.html). See [Gio API Reference -- g\_file\_dup](https://docs.gtk.org/gio/method.File.dup.html).
## The API document and source file of tfetextview.c ## The API document and source file of tfetextview.c
@ -248,13 +331,12 @@ See [Gio API Reference, g\_file\_dup](https://docs.gtk.org/gio/method.File.dup.h
Refer [API document of TfeTextView](../gfm/tfetextview_doc.md). Refer [API document of TfeTextView](../gfm/tfetextview_doc.md).
It is under the directory `src/tfetextview`. It is under the directory `src/tfetextview`.
@@@elif html @@@elif html
Refer [API document of TfeTextView](../html/tfetextview_doc.html). Refer [API document of TfeTextView](https://toshiocp.github.io/Gtk4-tutorial/tfetextview_doc.html).
Its original markdown file is under the directory `src/tfetextview`. The markdown file is under the directory `src/tfetextview`.
@@@elif latex @@@elif latex
Refer API document of TfeTextView in the appendix. Refer API document of TfeTextView in the appendix.
Its original markdown file is under the directory `src/tfetextview`. The markdown file is under the directory `src/tfetextview`.
@@@end @@@end
All the source files are listed in [Section 16](sec16.src.md). You can find all the TfeTextView source codes under [src/tfetextview](tfetextview) directories.
You can find them under [src/tfe5](tfe5) and [src/tfetextview](tfetextview) directories.

View file

@ -16,7 +16,7 @@ This header file describes the public functions in `tfenotebook.c`.
If the name is `untitled` or `untitled` followed by digits, FileChooserDialog appears and a user can choose or specify a filename. If the name is `untitled` or `untitled` followed by digits, FileChooserDialog appears and a user can choose or specify a filename.
- 4-5: `notebook_page_close` closes the current page. - 4-5: `notebook_page_close` closes the current page.
- 7-8: `notebook_page_open` shows a file chooser dialog and a user can choose a file. The file is inserted to a new page. - 7-8: `notebook_page_open` shows a file chooser dialog and a user can choose a file. The file is inserted to a new page.
- 10-11: `notebook_page_new_with_file` creates a new page and the file given as an argument is read and inserted into the page. - 10-11: `notebook_page_new_with_file` creates a new page and a file given as an argument is read and inserted into the page.
- 13-14: `notebook_page_new` creates a new empty page. - 13-14: `notebook_page_new` creates a new empty page.
You probably find that the functions except `notebook_page_close` are higher level functions of You probably find that the functions except `notebook_page_close` are higher level functions of
@ -40,29 +40,32 @@ Now let's look at the program of each function.
tfe5/tfenotebook.c get_untitled notebook_page_build notebook_page_new tfe5/tfenotebook.c get_untitled notebook_page_build notebook_page_new
@@@ @@@
- 27-38: `notebook_page_new` function. - 26-38: `notebook_page_new` function.
- 29: `g_return_if_fail` is used to check the argument. - 28: `g_return_if_fail` is used to check the argument.
- 34: Creates TfeTextView object. - 33-34: Creates TfeTextView object.
If it fails, it returns to the caller. If it fails, no notebook page is created and the function returns to the caller.
- 36: Creates filename, which is "Untitled", "Untitled1", ... . - 35: Creates filename, which is "Untitled", "Untitled1", ... .
- 1-8: `get_untitled` function. - 1-8: `get_untitled` function.
- 3: Static variable `c` is initialized at the first call of this function. After that `c` keeps its value unless it is changed explicitly. - 3: Static variable `c` is initialized at the first call of this function. After that `c` keeps its value unless it is changed explicitly.
- 4-7: Increases `c` by one and if it is zero then it returns "Untitled". If it is a positive integer then it returns "Untitled\<the integer\>", for example, "Untitled1", "Untitled2", and so on. - 4-7: Increases `c` by one and if it is zero, it returns "Untitled". If it is a positive integer, it returns "Untitled\<the integer\>", for example, "Untitled1", "Untitled2", and so on.
The function `g_strdup_printf` creates a string and it should be freed by `g_free` when it becomes useless. The function `g_strdup_printf` creates a string and it should be freed by `g_free` when it becomes useless.
The caller of `get_untitled` is in charge of freeing the string. The caller of `get_untitled` is in charge of freeing the string.
- 37: calls `notebook_page_build` to build the contents of the page. - 36: calls `notebook_page_build` to build the contents of the page.
- 10- 25: `notebook_page_build` function. - 37: frees `filename`.
- 10- 24: `notebook_page_build` function.
A parameter with `const` qualifier doesn't change in the function.
It means that the argument `filename` is owned by the caller.
The caller needs to free it when it becomes useless.
- 12: Creates GtkScrolledWindow. - 12: Creates GtkScrolledWindow.
- 17: Sets the wrap mode of `tv` to GTK_WRAP_WORD_CHAR so that lines are broken between words or graphemes. - 17: Inserts `tv` to GtkscrolledWindow as a child.
- 18: Inserts `tv` to GtkscrolledWindow as a child. - 18-19: Creates GtkLabel, then appends `scr` and `lab` to the GtkNotebook instance `nb`.
- 19-20: Creates GtkLabel, then appends `scr` and `lab` to the GtkNotebook instance `nb`. - 20-21: Sets "tab-expand" property to TRUE.
- 21-22: Sets "tab-expand" property to TRUE.
The function `g_object_set` sets properties on an object. The function `g_object_set` sets properties on an object.
The object is any object derived from GObject. The object is any object derived from GObject.
In many cases, an object has its own function to set its properties, but sometimes not. In many cases, an object has its own function to set its properties, but sometimes not.
In that case, use `g_object_set` to set the property. In that case, use `g_object_set` to set the property.
- 23: Sets the current page of `nb` to the newly created page. - 22: Sets the current page to the newly created page.
- 24: Connects "change-file" signal and `file_changed_cb` handler. - 23: Connects "change-file" signal and `file_changed_cb` handler.
## notebook\_page\_new\_with\_file ## notebook\_page\_new\_with\_file
@ -73,7 +76,7 @@ tfe5/tfenotebook.c notebook_page_new_with_file
- 9-10: Calls `tfe_text_view_new_with_file`. - 9-10: Calls `tfe_text_view_new_with_file`.
If the function returns NULL, an error has happend. If the function returns NULL, an error has happend.
Then, it does nothing and returns. Then, it does nothing and returns.
- 11-12: Gets the filename and builds the contents of the page. - 11-13: Gets the filename, builds the contents of the page and frees `filename`.
## notebook\_page\_open ## notebook\_page\_open
@ -81,35 +84,70 @@ Then, it does nothing and returns.
tfe5/tfenotebook.c open_response notebook_page_open tfe5/tfenotebook.c open_response notebook_page_open
@@@ @@@
- 16-26: `notebook_page_open` function. - 18-28: `notebook_page_open` function.
- 22-23: Creates TfeTextView object. - 24-25: Creates TfeTextView object.
If NULL is returned, an error has happened. If NULL is returned, an error has happened.
Then, it returns to the caller. Then, it returns to the caller.
- 24: Connects the signal "open-response" and the handler `open_response`. - 26: Connects the signal "open-response" and the handler `open_response`.
- 25: Calls `tfe_text_view_open`. - 27: Calls `tfe_text_view_open`.
The "open-response" signal will be emitted later to inform the result of opening and reading a file. The "open-response" signal will be emitted later to inform the result of opening and reading a file.
- 1-14: `open_response` handler. - 1-16: `open_response` handler.
- 6-8: If the response code is NOT `TFE_OPEN_RESPONSE_SUCCESS` or `tfe_text_view_get_file` doesn't return the pointer to a GFile, - 6-8: If the response code is not `TFE_OPEN_RESPONSE_SUCCESS`, it has failed to open and read a new file.
it has failed to open and read a new file.
Then, what `notebook_page_open` did in advance need to be canceled. Then, what `notebook_page_open` did in advance need to be canceled.
The instance `tv` hasn't been a child widget of GtkScrolledWindow yet. The instance `tv` hasn't been a child widget of GtkScrolledWindow yet.
Such instance has floating reference. Such instance has floating reference.
Floating reference will be explained later in this subsection. Floating reference will be explained later.
You need to call `g_object_ref_sink` first. You need to call `g_object_ref_sink` first.
Then the floating reference is converted into an ordinary reference. Then the floating reference is converted into an ordinary reference.
Now you call `g_object_unref` to decrease the reference count by one. Now you call `g_object_unref` to decrease the reference count by one.
- 9-13: Otherwise, everything is okay. - 9-15: Otherwise, everything is okay.
Gets the filename, builds the contents of the page. Gets the filename, builds the contents of the page and frees `filename`.
## Floating reference
All the widgets are derived from GInitiallyUnowned. All the widgets are derived from GInitiallyUnowned.
When an instance of GInitiallyUnowned or its descendant is created, the instance has a floating reference. GObject and GInitiallyUnowned are almost the same.
The function `g_object_ref_sink` converts the floating reference into an ordinary reference. The difference is like this.
If the instance doesn't have a floating reference, `g_object_ref_sink` simply increases the reference count by one. When an instance of GInitiallyUnowned is created, the instance has a floating reference and its reference count is zero.
On the other hand, when an instance of GObject (not GInitiallyUnowned) is created, no floating reference is given. On the other hand, when an instance of GObject (not GInitiallyUnowned) is created, no floating reference is given.
And the instance has a normal reference count instead of floating reference. And the instance has a normal reference count instead of floating reference.
Their descendants inherits them, so every widget has a floating reference at first.
Non-widget class, for example, GtkTextBuffer is a direct sub class of GObject and it doesn't have floating reference.
Its reference count is one when it is created.
The function `g_object_ref_sink` converts the floating reference into an ordinary reference.
If the instance doesn't have a floating reference, `g_object_ref_sink` simply increases the reference count by one.
It is used when an widget is added to another widget as a child.
~~~
GtkTextView *tv = gtk_text_view_new (); // floating reference
GtkScrolledWindow *scr = gtk_scrolled_window_new ();
gtk_scrolled_window_set_child (scr, tv); // tv's reference count is one
~~~
When `tv` is added to `scr` as a child, `g_object_ref_sink` is used.
~~~
g_object_ref_sink (tv);
~~~
So, the floating reference is converted into an ordinary reference.
That is to say, floating reference is deleted, and reference count turns to one.
Thanks to this, the caller doesn't need to decrease tv's reference count.
If an Object\_A is not a descendant of GInitiallyUnowned, the program is like this:
~~~
Object_A *obj_a = object_a_new (); // reference count is one
GtkScrolledWindow *scr = gtk_scrolled_window_new ();
gtk_scrolled_window_set_child (scr, obj_a); // obj_a's reference count is two
// obj_a is referred by the caller (this program) and scrolled window
g_object_unref (obj_a); // obj_a's reference count is one because the caller no longer refers obj_a.
~~~
This example tells us that the caller needs to unref `obj_a`.
If you use `g_object_unref` to an instance that has a floating reference, you need to convert the floating reference to a normal reference in advance. If you use `g_object_unref` to an instance that has a floating reference, you need to convert the floating reference to a normal reference in advance.
See [GObject Reference Manual](https://developer-old.gnome.org/gobject/stable/gobject-The-Base-Object-Type.html#gobject-The-Base-Object-Type.description) for further information. See [GObject API reference](https://docs.gtk.org/gobject/floating-refs.html) for further information.
## notebook\_page\_close ## notebook\_page\_close
@ -122,6 +160,7 @@ If the page is the only page the notebook has, then the function destroys the to
- 8-10: If the page is the only page the notebook has, it calls `gtk_window_destroy` to destroys the top-level window. - 8-10: If the page is the only page the notebook has, it calls `gtk_window_destroy` to destroys the top-level window.
- 11-13: Otherwise, removes the current page. - 11-13: Otherwise, removes the current page.
The child widget (TfeTextView) is also destroyed.
## notebook\_page\_save ## notebook\_page\_save
@ -142,7 +181,7 @@ This function gets the TfeTextView object belongs to the current page.
The function `file_changed_cb` is a handler connected to "change-file" signal. The function `file_changed_cb` is a handler connected to "change-file" signal.
If a file in a TfeTextView instance is changed, it emits this signal. If a file in a TfeTextView instance is changed, it emits this signal.
This handler changes the label of GtkNotebookPage. This handler changes the label of the GtkNotebookPage.
@@@include @@@include
tfe5/tfenotebook.c file_changed_cb tfe5/tfenotebook.c file_changed_cb

View file

@ -121,7 +121,7 @@ textview {color: yellow; ...}
~~~ ~~~
Class, ID and some other things can be applied to the selector like Web CSS. Class, ID and some other things can be applied to the selector like Web CSS.
Refer to [GTK 4 API Reference, CSS in Gtk](https://docs.gtk.org/gtk4/css-overview.html) for further information. Refer to [GTK 4 API Reference -- CSS in Gtk](https://docs.gtk.org/gtk4/css-overview.html) for further information.
In line 30, the CSS is a string. In line 30, the CSS is a string.
@ -256,9 +256,7 @@ In this file, just the source file names are modified from the prior version.
## source files ## source files
The [source files](tfe5) of the text editor `tfe` will be shown in the next section. You can download the files from the [repository](https://github.com/ToshioCP/Gtk4-tutorial).
You can also download the files from the [repository](https://github.com/ToshioCP/Gtk4-tutorial).
There are two options. There are two options.
- Use git and clone. - Use git and clone.

View file

@ -1,24 +1,19 @@
# tfe5 source files # How to build tfe (text file editor)
## How to compile and execute the text editor 'tfe'. ## How to compile and execute the text editor 'tfe'.
First, source files are shown in the later subsections. First, source files are in the [Gtk4-tutorila repository](https://github.com/ToshioCP/Gtk4-tutorial).
How to download them is written at the end of the [previous section](sec15.src.md). How to download them is written at the end of the [previous section](sec15.src.md).
The following is the instruction of compilation and execution. The following is the instruction of compilation and execution.
- You need meson and ninja. - You need meson and ninja.
- Set environment variables if necessary. - If you have installed gtk4 from the source, you need to set environment variables to suit your installation.
If you have installed gtk4 from the source and you preferred the option `--prefix $HOME/local` (see [Section 2](sec2.src.md)), type `. env.sh` to set the environment variables. - Change your current directory to `src/tfe5` directory.
- Type `meson _build` for configuration.
~~~ - Type `ninja -C _build` for compilation.
$ . env.sh
~~~
- change your current directory to `src/tfe5` directory.
- type `meson _build` for configuration.
- type `ninja -C _build` for compilation.
Then the application `tfe` is built under the `_build` directory. Then the application `tfe` is built under the `_build` directory.
- type `_build/tfe` to execute it. - Type `_build/tfe` to execute it.
Then the window appears. Then the window appears.
There are four buttons, `New`, `Open`, `Save` and `Close`. There are four buttons, `New`, `Open`, `Save` and `Close`.
@ -33,63 +28,9 @@ Then the file is read and a new Notebook Page appears.
This is a very simple editor. This is a very simple editor.
It is a good practice for you to add more features. It is a good practice for you to add more features.
## meson.build
@@@include
tfe5/meson.build
@@@
## tfe.gresource.xml
@@@include
tfe5/tfe.gresource.xml
@@@
## tfe.ui
@@@include
tfe5/tfe.ui
@@@
## tfe.h
@@@include
tfe5/tfe.h
@@@
## tfeapplication.c
@@@include
tfe5/tfeapplication.c
@@@
## tfenotebook.h
@@@include
tfe5/tfenotebook.h
@@@
## tfenotebook.c
@@@include
tfe5/tfenotebook.c
@@@
## tfetextview.h
@@@include
tfetextview/tfetextview.h
@@@
## tfetextview.c
@@@include
tfetextview/tfetextview.c
@@@
## Total number of lines, words and characters ## Total number of lines, words and characters
@@@shell @@@shell
LANG=C wc tfe5/meson.build tfe5/tfeapplication.c tfe5/tfe.gresource.xml tfe5/tfe.h tfe5/tfenotebook.c tfe5/tfenotebook.h tfetextview/tfetextview.c tfetextview/tfetextview.h tfe5/tfe.ui LANG=C wc tfe5/meson.build tfe5/tfeapplication.c tfe5/tfe.gresource.xml tfe5/tfenotebook.c tfe5/tfenotebook.h tfetextview/tfetextview.c tfetextview/tfetextview.h tfe5/tfe.ui
@@@ @@@

View file

@ -2,371 +2,40 @@
This section describes how to install GTK 4 into Linux distributions. This section describes how to install GTK 4 into Linux distributions.
This tutorial is without any warranty. There are two ways to install GTK 4.
If you want to install GTK 4 to your computer, do it at your own risk.
The information in this section is the one on April/27/2022.
The words 'at present' and/or 'now' in this section means 'April/27/2022'.
There are three possible way to install GTK 4.
- Install it from the distribution packages. - Install it from the distribution packages.
- Build it from the source file. - Build it from the source files.
- Install a GNOME 40 distribution with the GNOME Boxes.
## Installation from the distribution packages ## Installation from the distribution packages
The first way is easy to install. The first way is the best and easiest way to install it.
It is a recommended way. I've installed GTK 4 packages (version 4.8.1) in Ubuntu 22.10.
I've installed GTK 4 packages in Ubuntu 21.04.
(Now, my Ubuntu version is 21.10).
~~~ ~~~
$ sudo apt-get install libgtk-4-bin libgtk-4-common libgtk-4-dev libgtk-4-doc $ sudo apt install libgtk-4-dev
~~~ ~~~
Fedora, Arch, Debian and OpenSUSE are also possible. It is important to install the developing package (libgtk-4-dev).
See [Installing GTK from packages](https://www.gtk.org/docs/installations/linux#installing-gtk-from-packages). It includes C header files.
Otherwise, you can't compile any GTK 4 based programs.
Fedora, Debian, Arch, Gentoo and OpenSUSE also have GTK 4 packages.
Package information for Arch, Debian/Ubuntu and Fedra is described by [Installing GTK from packages](https://www.gtk.org/docs/installations/linux#installing-gtk-from-packages).
The following table shows the distributions which support GTK 4. The following table shows the distributions which support GTK 4.
@@@table @@@table
|Distribution|version|GTK 4|GNOME 40| |Distribution|version|GTK 4|GNOME|
|:-:|:-:|:-:|:-:| |:-:|:-:|:-:|:-:|
|Fedora|36|4.4.2|GNOME 42| |Fedora|37|4.8.2-2|GNOME 43|
|Ubuntu|22.04lts|4.4|GNOME 41(4.6.2)| |Ubuntu|22.10|4.8.1|GNOME 43.0|
|Debian|bookworm(testing)|4.6.5|GNOME 42| |Debian|bookworm(testing)|4.8.2|GNOME 43|
|Arch|rolling release|4.6.5|GNOME 42| |Arch|rolling release|4.8.2|GNOME 43|
|Gentoo|rolling release|4.6.5|GNOME 42| |Gentoo|rolling release|4.8.2|GNOME 43|
|OpenSUSE|Tumbleweed(rolling release)|4.6.5|GNOME 42| |OpenSUSE|Tumbleweed(rolling release)|4.8.2|GNOME 43|
@@@ @@@
If you've installed GTK 4 from the packages, you don't need to read the rest of this section.
## Installation from the source file ## Installation from the source file
If your operating system doesn't have GTK 4 packages, you need to build it from the source. If you want to install a developing version of GTK 4, you need to build it from the source.
Or, if you want the latest version of GTK 4 (4.6.3), you also need to build it from the source. See [Compiling the GTK Libraries](https://docs.gtk.org/gtk4/building.html) secion in the GTK 4 API reference.
I installed GTK 4 from the source in January 2021.
So, the following information is old, especially for the version of each software.
For the latest information, see [GTK API Reference, Building GTK](https://docs.gtk.org/gtk4/building.html).
### Prerequisites for GTK 4 installation
- Linux operating system. For example, Ubuntu 20.10 or 20.04LTS.
Other distributions might be OK.
- Packages for development such as gcc, meson, ninja, git, wget and so on.
- Dev package is necessary for each software below.
### Installation target
I installed GTK 4 under the directory `$HOME/local`.
This is a private user area.
If you want to install it in the system area, `/opt/gtk4` is one of good choices.
[GTK API Reference, Building GTK](https://docs.gtk.org/gtk4/building.html) gives an installation example to `/opt/gtk4`.
Don't install it to `/usr/local` which is the default.
It is used by Ubuntu applications which are not build on GTK 4.
Therefore, the risk is high and probably bad things will happen.
Actually I did it and I needed to reinstall Ubuntu.
### Installation to Ubuntu 20.10
Most of the necessary libraries are included by Ubuntu 20.10.
Therefore, they can be installed with `apt-get` command.
You don't need to install them from the source tarballs.
You can skip the subsections below about prerequisite library installation (GLib, Pango, GdkPixbuf and GTK-doc).
### GLib installation
If your Ubuntu is 20.04LTS, you need to install prerequisite libraries from the tarballs.
Check the version of your library and if it is lower than the necessary version, install it from the source.
For example,
~~~
$ pkg-config --modversion glib-2.0
2.64.6
~~~
The necessary version is 2.66.0 or higher.
Therefore, the example above shows that you need to install GLib.
I installed 2.67.1 which was the latest version at that time (January 2021).
Download GLib source files from the repository, then decompress and extract files.
$ wget https://download.gnome.org/sources/glib/2.67/glib-2.67.1.tar.xz
$ tar -Jxf glib-2.67.1.tar.xz
Some packages are required to build GLib.
You can find them if you run meson.
$ meson --prefix $HOME/local _build
Use apt-get and install the prerequisites.
For example,
$ sudo apt-get install -y libpcre2-dev libffi-dev
After that, compile GLib.
$ rm -rf _build
$ meson --prefix $HOME/local _build
$ ninja -C _build
$ ninja -C _build install
Set several environment variables so that the GLib libraries installed can be used by build tools.
Make a text file below and save it as `env.sh`
# compiler
CPPFLAGS="-I$HOME/local/include"
LDFLAGS="-L$HOME/local/lib"
PKG_CONFIG_PATH="$HOME/local/lib/pkgconfig:$HOME/local/lib/x86_64-linux-gnu/pkgconfig"
export CPPFLAGS LDFLAGS PKG_CONFIG_PATH
# linker
LD_LIBRARY_PATH="$HOME/local/lib/x86_64-linux-gnu/"
PATH="$HOME/local/bin:$PATH"
export LD_LIBRARY_PATH PATH
# gsetting
export GSETTINGS_SCHEMA_DIR=$HOME/local/share/glib-2.0/schemas
Then, use . (dot) or source command to include these commands to the current bash.
$ . env.sh
or
$ source env.sh
This command carries out the commands in `env.sh` and changes the environment variables above in the current shell.
### Pango installation
Download and untar.
$ wget https://download.gnome.org/sources/pango/1.48/pango-1.48.0.tar.xz
$ tar -Jxf pango-1.48.0.tar.xz
Try meson and check the required packages.
Install all the prerequisites.
Then, compile and install Pango.
$ meson --prefix $HOME/local _build
$ ninja -C _build
$ ninja -C _build install
It installs Pango-1.0.gir under `$HOME/local/share/gir-1.0`.
If you installed Pango without `--prefix` option, then it would be located at `/usr/local/share/gir-1.0`.
This directory (/usr/local/share) is used by applications.
They find the directory by the environment variable `XDG_DATA_DIRS`.
It is a text file which keep the list of 'share' directories like `/usr/share`, `usr/local/share` and so on.
Now `$HOME/local/share` needs to be added to `XDG_DATA_DIRS`, or error will occur in the later compilation.
$ export XDG_DATA_DIRS=$HOME/local/share:$XDG_DATA_DIRS
### GdkPixbuf and GTK-Doc installation
Download and untar.
$ wget https://download.gnome.org/sources/gdk-pixbuf/2.42/gdk-pixbuf-2.42.2.tar.xz
$ tar -Jxf gdk-pixbuf-2.42.2.tar.xz
$ wget https://download.gnome.org/sources/gtk-doc/1.33/gtk-doc-1.33.1.tar.xz
$ tar -Jxf gtk-doc-1.33.1.tar.xz
Same as before, install prerequisite packages, then compile and install them.
The installation of GTK-Doc put `gtk-doc.pc` under `$HOME/local/share/pkgconfig`.
This file is used by pkg-config, which is one of the build tools.
The directory needs to be added to the environment variable `PKG_CONFIG_PATH`
$ export PKG_CONFIG_PATH="$HOME/local/share/pkgconfig:$PKG_CONFIG_PATH"
### GTK 4 installation
If you want the latest development version of GTK 4, use git and clone the repository.
$ git clone https://gitlab.gnome.org/gnome/gtk.git
If you want a stable version of GTK 4, then download it from [GNOME source website](https://download.gnome.org/sources/gtk/).
The latest version is 4.3.1 (13/June/2021).
Compile and install it.
$ meson --prefix $HOME/local _build
$ ninja -C _build
$ ninja -C _build install
If you want to know more information, refer to [GTK 4 API Reference, Building GTK](https://docs.gtk.org/gtk4/building.html).
### Modify env.sh
Because environment variables disappear when you log out, you need to add them again.
Modify `env.sh`.
# compiler
CPPFLAGS="-I$HOME/local/include"
LDFLAGS="-L$HOME/local/lib"
PKG_CONFIG_PATH="$HOME/local/lib/pkgconfig:$HOME/local/lib/x86_64-linux-gnu/pkgconfig:
$HOME/local/share/pkgconfig"
export CPPFLAGS LDFLAGS PKG_CONFIG_PATH
# linker
LD_LIBRARY_PATH="$HOME/local/lib/x86_64-linux-gnu/"
PATH="$HOME/local/bin:$PATH"
export LD_LIBRARY_PATH PATH
# gir
XDG_DATA_DIRS=$HOME/local/share:$XDG_DATA_DIRS
export XDG_DATA_DIRS
# gsetting
export GSETTINGS_SCHEMA_DIR=$HOME/local/share/glib-2.0/schemas
# girepository-1.0
export GI_TYPELIB_PATH=$HOME/local/lib/x86_64-linux-gnu/girepository-1.0
Include this file by . (dot) command before using GTK 4 libraries.
You may think you can add them in your `.profile`.
But it's a wrong decision.
Never write them to your `.profile`.
The environment variables above are necessary only when you compile and run GTK 4 applications.
Otherwise it's not necessary.
If you changed the environment variables above and run GTK 3 applications, it probably causes serious damage.
### Compiling GTK 4 applications
Before you compile GTK 4 applications, define environment variables above.
$ . env.sh
After that you can compile them without anything.
For example, to compile `sample.c`, type the following.
$ gcc `pkg-config --cflags gtk4` sample.c `pkg-config --libs gtk4`
To know how to compile GTK 4 applications, refer to the section 3 (GtkApplication and GtkApplicationWindow) and after.
## Installing Fedora 34 with GNOME Boxes
The last part of this section is about GNOME 40 and GNOME Boxes.
GNOME 40 is a new version of GNOME desktop system.
And GTK 4 is installed in the distribution.
See [GNOME 40 website](https://forty.gnome.org/) first.
*However, GNOME 40 is not necessary to compile and run GTK 4 applications.*
There are seven choices at present.
- GNOME OS
- Arch Linux
- Gentoo Linux
- Fedora 36
- openSUSE Tumbleweed
- Ubuntu 22.04
- Debian bookworm
I've installed Fedora 34 with GNOME Boxes.
My OS was Ubuntu 21.04 at that time.
GNOME Boxes creates a virtual machine in Ubuntu and Fedora will be installed to that virtual machine.
The instruction is as follows.
1. Download Fedora 34 iso file.
There is an link at the end of [GNOME 40 website](https://forty.gnome.org/).
2. Install gnome-boxes with apt-get command.
~~~
$ sudo apt-get install gnome-boxes
~~~
3. Run GNOME Boxes.
4. Click on `+` button on the top left corner and launch a box creation wizard by clicking `Create a Virtual Machine ...`.
Then a dialog appears.
Click on `Operationg System Image File` and select the iso file you have downloaded.
5. Then, the Fedora's installer is executed.
Follow the instructions by the installer.
At the end of the installation, the installer instructs to reboot the system.
Click on the right of the title bar and select reboot or shutdown.
6. Your display is back to the initial window of GNOME Boxes, but there is a button `Fedora 34 Workstation` on the upper left of the window.
Click on the button then Fedora will be executed.
7. A setup dialog appears.
Setup Fedora according to the wizard.
Now you can use Fedora.
It includes GTK 4 libraries already.
But you need to install the GTK 4 development package.
Use `dnf` to install `gtk4.x86_64` package.
~~~
$ sudo dnf install gtk4.x86_64
~~~
### GTK 4 compilation test
You can test the GTK 4 development packages by compiling files which are based on GTK 4.
I've tried compiling `tfe` text editor, which is written in section 21.
1. Run Firefox.
2. Open this website \([Gtk4-Tutorial](https://github.com/ToshioCP/Gtk4-tutorial)\).
3. Click on the green button labeled `Code`.
4. Select `Download ZIP` and download the codes from the repository.
5. Unzip the file.
6. Change your current directory to `src/tfe7`.
7. Compile it.
~~~
$ meson _build
bash: meson: command not found...
Install package 'meson' to provide command 'meson'? [N/y] y
* Waiting in queue...
The following packages have to be installed:
meson-0.56.2-2.fc34.noarch High productivity build system
ninja-build-1.10.2-2.fc34.x86_64 Small build system with a focus on speed
vim-filesystem-2:8.2.2787-1.fc34.noarch VIM filesystem layout
Proceed with changes? [N/y] y
... ...
... ...
The Meson build system
Version: 0.56.2
... ...
... ...
Project name: tfe
Project version: undefined
C compiler for the host machine: cc (gcc 11.0.0 "cc (GCC) 11.0.0 20210210 (Red Hat 11.0.0-0)")
C linker for the host machine: cc ld.bfd 2.35.1-38
Host machine cpu family: x86_64
Host machine cpu: x86_64
Found pkg-config: /usr/bin/pkg-config (1.7.3)
Run-time dependency gtk4 found: YES 4.2.0
Found pkg-config: /usr/bin/pkg-config (1.7.3)
Program glib-compile-resources found: YES (/usr/bin/glib-compile-resources)
Program glib-compile-schemas found: YES (/usr/bin/glib-compile-schemas)
Program glib-compile-schemas found: YES (/usr/bin/glib-compile-schemas)
Build targets in project: 4
Found ninja-1.10.2 at /usr/bin/ninja
$ ninja -C _build
ninja: Entering directory `_build'
[12/12] Linking target tfe
$ ninja -C _build install
ninja: Entering directory `_build'
[0/1] Installing files.
Installing tfe to /usr/local/bin
Installation failed due to insufficient permissions.
Attempting to use polkit to gain elevated privileges...
Installing tfe to /usr/local/bin
Installing /home/<username>/Gtk4-tutorial-main/src/tfe7/com.github.ToshioCP.tfe.gschema.xml to /usr/local/share/glib-2.0/schemas
Running custom install script '/usr/bin/glib-compile-schemas /usr/local/share/glib-2.0/schemas/'
~~~
8. Execute it.
~~~
$ tfe
~~~
Then, the window of `tfe` text editor appears.
The compilation and execution have succeeded.

View file

@ -4,7 +4,7 @@
### GtkApplication and g\_application\_run ### GtkApplication and g\_application\_run
Usually people write programming code to make an application. People write programming code to make an application.
What are applications? What are applications?
Applications are software that runs using libraries, which includes the Applications are software that runs using libraries, which includes the
OS, frameworks and so on. OS, frameworks and so on.
@ -25,7 +25,7 @@ misc/pr1.c
@@@ @@@
The first line says that this program includes the header files of the Gtk libraries. The first line says that this program includes the header files of the Gtk libraries.
The function `main` above is a startup function in C language. The function `main` is a startup function in C language.
The variable `app` is defined as a pointer to a GtkApplication instance. The variable `app` is defined as a pointer to a GtkApplication instance.
The function `gtk_application_new` creates a GtkApplication instance and returns a pointer to the instance. The function `gtk_application_new` creates a GtkApplication instance and returns a pointer to the instance.
The GtkApplication instance is a C structure data in which the information about the application is stored. The GtkApplication instance is a C structure data in which the information about the application is stored.
@ -35,6 +35,16 @@ The function `g_application_run` runs an application that the instance defined.
Actually, `app` is not an application but a pointer to the instance of the application. Actually, `app` is not an application but a pointer to the instance of the application.
However, it is simple and short, and probably no confusion occurs.) However, it is simple and short, and probably no confusion occurs.)
Here I used the word `instance`.
Instance, class and object are terminologies in Object Oriented Programming.
I use these words in the same way.
But, I will often use "object" instead of "instance" in this tutorial.
That means "object" and "instance" is the same.
Object is a bit ambiguous word.
In a broad sense, object has wider meaning than instance.
So, readers should be careful of the contexts to find the meaning of "object".
In many cases, object and instance are the same.
To compile this, the following command needs to be run. To compile this, the following command needs to be run.
The string `pr1.c` is the filename of the C source code above. The string `pr1.c` is the filename of the C source code above.
@ -71,7 +81,8 @@ So, I will explain that to you first.
A signal is emitted when something happens. A signal is emitted when something happens.
For example, a window is created, a window is destroyed and so on. For example, a window is created, a window is destroyed and so on.
The signal "activate" is emitted when the application is activated, or started. The signal "activate" is emitted when the application is activated.
(Activated is a bit different from started, but you can think the both are almost same so far.)
If the signal is connected to a function, which is called a signal handler or If the signal is connected to a function, which is called a signal handler or
simply handler, then the function is invoked when the signal emits. simply handler, then the function is invoked when the signal emits.
@ -79,7 +90,7 @@ The flow is like this:
1. Something happens. 1. Something happens.
2. If it's related to a certain signal, then the signal is emitted. 2. If it's related to a certain signal, then the signal is emitted.
3. If the signal as been connected to a handler, then the handler is invoked. 3. If the signal has been connected to a handler in advance, then the handler is invoked.
Signals are defined in objects. Signals are defined in objects.
For example, the "activate" signal belongs to the GApplication object, which is For example, the "activate" signal belongs to the GApplication object, which is
@ -105,6 +116,7 @@ misc/pr2.c
@@@ @@@
First, we define the handler `app_activate` which simply displays a message. First, we define the handler `app_activate` which simply displays a message.
The function `g_print` is defined in GLib and it's like a printf in the C standard library.
In the function `main`, we add `g_signal_connect` before `g_application_run`. In the function `main`, we add `g_signal_connect` before `g_application_run`.
The function `g_signal_connect` has four arguments. The function `g_signal_connect` has four arguments.
@ -113,14 +125,39 @@ The function `g_signal_connect` has four arguments.
3. A handler function (also called callback), which needs to be casted by `G_CALLBACK`. 3. A handler function (also called callback), which needs to be casted by `G_CALLBACK`.
4. Data to pass to the handler. If no data is necessary, NULL should be given. 4. Data to pass to the handler. If no data is necessary, NULL should be given.
You can find the description of each signal in the API reference manual. It is described in the [GObject API Reference](https://docs.gtk.org/gobject/func.signal_connect.html).
For example, "activate" signal is in GApplication section in [GIO API Reference](https://docs.gtk.org/gio/signal.Application.activate.html). Correctly, `g_signal_connect` is a macro (not a C function).
The handler function is described in it.
~~~c
#define g_signal_connect (
instance,
detailed_signal,
c_handler,
data
)
~~~
You can find the description of each signal in the API reference manual.
For example, "activate" signal is in [GApplication section](https://docs.gtk.org/gio/signal.Application.activate.html) in the GIO API Reference.
~~~c
void
activate (
GApplication* self,
gpointer user_data
)
~~~
This is a declaration of the "activate" signal handler.
You can use any name instead of "activate" in the declaration above.
The parameters are:
- self is an instance to which the signal belongs.
- user\_data is a data defined in the fourth argument of the `g_signal_connect` function.
If it is NULL, then you can ignore and left out the second parameter.
In addition, `g_signal_connect` is described in [GObject API Reference](https://docs.gtk.org/gobject/func.signal_connect.html).
API reference manual is very important. API reference manual is very important.
You should see and understand it to write Gtk applications. You should see and understand it.
They are located in ['GTK Documentation'](https://docs.gtk.org/).
Let's compile the source file above (`pr2.c`) and run it. Let's compile the source file above (`pr2.c`) and run it.
@ -207,19 +244,20 @@ By this definition, it returns a pointer to GtkWidget, not GtkWindow.
It actually creates a new GtkWindow instance (not GtkWidget) but returns a pointer to GtkWidget. It actually creates a new GtkWindow instance (not GtkWidget) but returns a pointer to GtkWidget.
However,the pointer points the GtkWidget and at the same time it also points GtkWindow that contains GtkWidget in it. However,the pointer points the GtkWidget and at the same time it also points GtkWindow that contains GtkWidget in it.
If you want to use `win` as a pointer to the GtkWindow, you need to cast it. If you want to use `win` as a pointer to a GtkWindow type instance, you need to cast it.
~~~C ~~~C
(GtkWindow *) win (GtkWindow *) win
~~~ ~~~
Or you can use `GTK_WINDOW` macro that performs a similar function. It works, but isn't usually used.
Instead, `GTK_WINDOW` macro is used.
~~~C ~~~C
GTK_WINDOW (win) GTK_WINDOW (win)
~~~ ~~~
This is a recommended way. The macro is recommended because it does not only cast but also check the type.
#### Connect it to GtkApplication. #### Connect it to GtkApplication.
@ -229,8 +267,7 @@ The function `gtk_window_set_application` is used to connect GtkWindow to GtkApp
gtk_window_set_application (GTK_WINDOW (win), GTK_APPLICATION (app)); gtk_window_set_application (GTK_WINDOW (win), GTK_APPLICATION (app));
~~~ ~~~
You need to cast `win` to GtkWindow and `app` to GtkApplication. You need to cast `win` to GtkWindow and `app` to GtkApplication with `GTK_WINDOW` and `GTK_APPLICATION` macro.
`GTK_WINDOW` and `GTK_APPLICATION` macro is appropriate for that.
GtkApplication continues to run until the related window is destroyed. GtkApplication continues to run until the related window is destroyed.
If you didn't connect GtkWindow and GtkApplication, GtkApplication destroys itself immediately. If you didn't connect GtkWindow and GtkApplication, GtkApplication destroys itself immediately.
@ -246,7 +283,7 @@ But, there's an exception.
Top window (this term will be explained later) isn't visible when it is created. Top window (this term will be explained later) isn't visible when it is created.
So you need to use the function above to show the window. So you need to use the function above to show the window.
Save the program as `pr3.c` and compile and run it. Save the program as `pr3.c`, then compile and run it.
~~~ ~~~
$ comp pr3 $ comp pr3
@ -262,8 +299,8 @@ Click on the close button then the window disappears and the program finishes.
### GtkApplicationWindow ### GtkApplicationWindow
GtkApplicationWindow is a child object of GtkWindow. GtkApplicationWindow is a child object of GtkWindow.
It has some extra functionality for better integration with GtkApplication. It has some extra feature for better integration with GtkApplication.
It is recommended to use it instead of GtkWindow when you use GtkApplication. It is recommended to use it as the top-level window of the application instead of GtkWindow.
Now rewrite the program and use GtkApplicationWindow. Now rewrite the program and use GtkApplicationWindow.

View file

@ -32,6 +32,8 @@ cd misc; diff pr4.c lb1.c
This tells us: This tells us:
- A signal handler `app_activate` doesn't have `user_data` parameter.
If the fourth argument of `g_signal_connect` is NULL, you can leave out `user_data`.
- The definition of a new variable `lab` is added. - The definition of a new variable `lab` is added.
- The title of the window is changed. - The title of the window is changed.
- A label is created and connected to the window as a child. - A label is created and connected to the window as a child.
@ -53,11 +55,11 @@ An application can have more than one top-level window.
### GtkButton ### GtkButton
The next widget to introduce is GtkButton. The next widget is GtkButton.
It displays a button on the screen with a label or icon on it. It displays a button on the screen with a label or icon on it.
In this subsection, we will make a button with a label. In this subsection, we will make a button with a label.
When the button is clicked, it emits a "clicked" signal. When the button is clicked, it emits a "clicked" signal.
The following program shows how to catch the signal to then do something. The following program shows how to catch the signal and do something.
@@@include @@@include
misc/lb2.c misc/lb2.c
@ -66,7 +68,7 @@ misc/lb2.c
Look at the line 17 to 19. Look at the line 17 to 19.
First, it creates a GtkButton instance `btn` with a label "Click me". First, it creates a GtkButton instance `btn` with a label "Click me".
Then, adds the button to the window `win` as a child. Then, adds the button to the window `win` as a child.
Finally, connects a "clicked" signal of the button to a handler (function) `click_cb`. Finally, connects a "clicked" signal of the button to the handler `click_cb`.
So, if `btn` is clicked, the function `click_cb` is invoked. So, if `btn` is clicked, the function `click_cb` is invoked.
The suffix "cb" means "call back". The suffix "cb" means "call back".
@ -80,7 +82,7 @@ Click the button (it is a large button, you can click everywhere in the window),
It shows the handler was invoked by clicking the button. It shows the handler was invoked by clicking the button.
It's good that we make sure that the clicked signal was caught and the handler was invoked by using `g_print`. It's good that we make sure that the clicked signal was caught and the handler was invoked by using `g_print`.
However, using g_print is out of harmony with Gtk which is a GUI library. However, using `g_print` is out of harmony with GTK, which is a GUI library.
So, we will change the handler. So, we will change the handler.
The following code is `lb3.c`. The following code is `lb3.c`.
@ -96,15 +98,19 @@ cd misc; diff lb2.c lb3.c
The changes are: The changes are:
- The function `g_print` in `lb2.c` was deleted and the two lines above are inserted instead. - The function `g_print` in `lb2.c` was deleted and two lines are inserted.
- The label of `btn` is changed from "Click me" to "Quit". - `click_cb` has the second parameter, which comes from the fourth argument of the `g_signal_connect` at the line 19.
One thing to be careful is the types are different between the second parameter of `click_cb` and the fourth argument of `g_signal_connect`.
The former is `GtkWindow *` and the latter is `GtkWidget *`.
The compiler doesn't complain because `g_signal_connect` uses gpointer (general type of pointer).
In this program the instance pointed by `win` is a GtkApplicationWindow object.
It is a descendant of GtkWindow and GtkWidget class, so both `GtkWindow *` and `GtkWidget *` are correct types for the instance.
- `gtk_destroy (win)` destroys the top-level window. Then the application quits.
- The label of `btn` is changed from "Click me" to "Close".
- The fourth argument of `g_signal_connect` is changed from `NULL` to `win`. - The fourth argument of `g_signal_connect` is changed from `NULL` to `win`.
The most important change is the fourth argument of `g_signal_connect`. The most important change is the fourth argument of the `g_signal_connect`.
This argument is described as "data to pass to handler" in the definition of `g_signal_connect` in [GObject API Reference](https://docs.gtk.org/gobject/func.signal_connect.html). This argument is described as "data to pass to handler" in the definition of [`g_signal_connect`](https://docs.gtk.org/gobject/func.signal_connect.html).
Therefore, `win` which is a pointer to GtkApplicationWindow is passed to the handler as a second parameter `user_data`.
The handler then casts it to a pointer to GtkWindow and calls `gtk_window_destroy` to destroy the top-level window.
The application then quits.
### GtkBox ### GtkBox
@ -123,8 +129,7 @@ After this, the Widgets are connected as the following diagram.
![Parent-child relationship](../image/box.png){width=7.725cm height=2.055cm} ![Parent-child relationship](../image/box.png){width=7.725cm height=2.055cm}
The program `lb4.c` includes these widgets. The program `lb4.c` is as follows.
It is as follows.
@@@include @@@include
misc/lb4.c misc/lb4.c
@ -134,16 +139,36 @@ Look at the function `app_activate`.
After the creation of a GtkApplicationWindow instance, a GtkBox instance is created. After the creation of a GtkApplicationWindow instance, a GtkBox instance is created.
box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); ~~~C
gtk_box_set_homogeneous (GTK_BOX (box), TRUE); box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
gtk_box_set_homogeneous (GTK_BOX (box), TRUE);
~~~
The first argument arranges the children of the box vertically. The first argument arranges the children of the box vertically.
The orientation constants are defined like this:
- GTK\_ORIENTATION\_VERTICAL: the children widgets are arranged vertically
- GTK\_ORIENTATION\_HORIZONTAL: the children widgets are arranged horizontally
The second argument is the size between the children. The second argument is the size between the children.
The unit of the length is pixel.
The next function fills the box with the children, giving them the same space. The next function fills the box with the children, giving them the same space.
After that, two buttons `btn1` and `btn2` are created and the signal handlers are set. After that, two buttons `btn1` and `btn2` are created and the signal handlers are set.
Then, these two buttons are appended to the box. Then, these two buttons are appended to the box.
@@@include
misc/lb4.c click1.cb
@@@
The function `gtk_button_get_lable` returns a text from the label.
The string is owned by the button and you can't modify or free it.
The `const` qualifier is necessary for the string `s`.
If you change the string, your compiler will give you a waring.
You always need to be careful with the const qualifier when you see the GTK 4 API reference.
![Screenshot of the box](../image/screenshot_lb4.png){width=6.3cm height=5.325cm} ![Screenshot of the box](../image/screenshot_lb4.png){width=6.3cm height=5.325cm}
The handler corresponds to `btn1` toggles its label. The handler corresponds to `btn1` toggles its label.

View file

@ -16,16 +16,26 @@ Look at line 25.
A GtkTextView instance is created and its pointer is assigned to `tv`. A GtkTextView instance is created and its pointer is assigned to `tv`.
When the GtkTextView instance is created, a GtkTextBuffer instance is also created and connected to the GtkTextView automatically. When the GtkTextView instance is created, a GtkTextBuffer instance is also created and connected to the GtkTextView automatically.
"GtkTextBuffer instance" will be referred to simply as "GtkTextBuffer" or "buffer". "GtkTextBuffer instance" will be referred to simply as "GtkTextBuffer" or "buffer".
In the next line, the pointer to the buffer is got and assigned to `tb`. In the next line, the pointer to the buffer is assigned to `tb`.
Then, the text from line 10 to 20 is assigned to the buffer. Then, the text from line 10 to 20 is assigned to the buffer.
If the third argument of `gtk_text_buffer_set_text` is a positive integer, it is the length of the text.
It it is -1, the string terminates with NULL.
GtkTextView has a wrap mode. GtkTextView has a wrap mode.
When it is set to `GTK_WRAP_WORD_CHAR`, text wraps in between words, or if that is not enough, also between graphemes. When it is set to `GTK_WRAP_WORD_CHAR`, text wraps in between words, or if that is not enough, also between graphemes.
Wrap mode is written in [Gtk\_WrapMode](https://docs.gtk.org/gtk4/enum.WrapMode.html) in the GTK 4 API document.
In line 30, `tv` is added to `win` as a child. In line 30, `tv` is added to `win` as a child.
Now compile and run it. Now compile and run it.
```
$ cd src/tfv
$ comp tfv1
$ ./a.out
```
![GtkTextView](../image/screenshot_tfv1.png){width=6.3cm height=5.325cm} ![GtkTextView](../image/screenshot_tfv1.png){width=6.3cm height=5.325cm}
There's an I-beam pointer in the window. There's an I-beam pointer in the window.
@ -33,30 +43,31 @@ You can add or delete any characters on the GtkTextView,
and your changes are kept in the GtkTextBuffer. and your changes are kept in the GtkTextBuffer.
If you add more characters beyond the limit of the window, the height increases and the window extends. If you add more characters beyond the limit of the window, the height increases and the window extends.
If the height gets bigger than the height of the display screen, you won't be If the height gets bigger than the height of the display screen, you won't be
able to control the size of the window, and change it back to the original size. able to control the size of the window or change it back to the original size.
This is a problem and shows that there is a bug in our program. This is a problem, that is to say a bug.
This can solve it by adding a GtkScrolledWindow between the GtkApplicationWindow and GtkTextView. This can be solved by adding a GtkScrolledWindow between the GtkApplicationWindow and GtkTextView.
### GtkScrolledWindow ### GtkScrolledWindow
What we need to do is: What we need to do is:
- Create a GtkScrolledWindow and insert it as a child of the GtkApplicationWindow; and - Create a GtkScrolledWindow and insert it as a child of the GtkApplicationWindow
- Insert the GtkTextView widget to the GtkScrolledWindow as a child. - Insert the GtkTextView widget to the GtkScrolledWindow as a child.
Modify `tfv1.c` and save it as `tfv2.c`. Modify `tfv1.c` and save it as `tfv2.c`.
The difference between these two files is small. There is only a few difference between these two files.
@@@shell @@@shell
cd tfv; diff tfv1.c tfv2.c cd tfv; diff tfv1.c tfv2.c
@@@ @@@
Here is the complete code of `tfv2.c`. The whole code of `tfv2.c` is as follows.
@@@include @@@include
tfv/tfv2.c tfv/tfv2.c
@@@ @@@
Compile and run it. Compile and run it.
Notice how this time the window doesn't extend when you type a lot of characters,
it just scrolls and displays a slider. Now, the window doesn't extend even if you type a lot of characters,
it just scrolls.

View file

@ -1,4 +1,4 @@
# String and memory management # Strings and memory management
GtkTextView and GtkTextBuffer have functions that use string parameters or return a string. GtkTextView and GtkTextBuffer have functions that use string parameters or return a string.
The knowledge of strings and memory management is useful to understand how to use these functions. The knowledge of strings and memory management is useful to understand how to use these functions.
@ -6,12 +6,11 @@ The knowledge of strings and memory management is useful to understand how to us
## String and memory ## String and memory
A String is an array of characters that is terminated with '\0'. A String is an array of characters that is terminated with '\0'.
Strings are not a C type such as char, int, float or double, String is not a C type such as char, int, float or double, but a character array.
but exist as a pointer to a character array. They behaves like a string type It behaves like a string in other languages.
which you may be familiar from other languages. So, the pointer is often called 'a string'.
So, this pointer is often called 'a string'.
In the following, `a` and `b` defined as character arrays, and are strings. The following is a sample program.
~~~C ~~~C
char a[10], *b; char a[10], *b;
@ -28,50 +27,50 @@ b = a;
/* *(++b) is 'e' */ /* *(++b) is 'e' */
~~~ ~~~
The array `a` has `char` elements and the size of ten. An array `a` is defined as a `char` type array and its size is ten.
The first six elements are 'H', 'e', 'l', 'l', 'o' and '\0'. The first five elements are 'H', 'e', 'l', 'l', 'o'.
This array represents the string "Hello". They are character codes.
The first five elements are character codes that correspond to the characters. For example, 'H' is the same as 0x48 or 72.
The sixth element is '\0', which is the same as zero, The sixth element is '\0', which is the same as zero, and indicates that the sequence of the data ends there.
and indicates that the string ends there. The array represents the string "Hello".
The size of the array is 10, so 4 bytes aren't used, but that's OK,
they are just ignored. The size of the array is 10, so 4 bytes aren't used.
But it's OK.
They are just ignored.
(If 'a' is defined out of functions or its class is static, they are assigned with zero.
Otherwise, that is to say, the class is auto or register, they are undefined.)
The variable 'b' is a pointer to a character. The variable 'b' is a pointer to a character.
Because `b` is assigned to be `a`, `a` and `b` point the same character ('H'). It is assigned with `a`, so `b` points the first element of `a` (character 'H').
The variable `a` is defined as an array and it can't be changed. The array `a` is immutable.
It always point the top address of the array. So `a=a+1` causes syntax error.
On the other hand, 'b' is a pointer, which is mutable, so `b` can be change.
It is then possible to write statements like `++b`, which means take the value in b (n address),
increase it by one, and store that back in `b`.
If a pointer is NULL, it points to nothing. On the other hand, 'b' is a pointer type variable, which is mutable.
So, `++b`, which increases `b` by one, is allowed.
If a pointer is NULL, it points nothing.
So, the pointer is not a string. So, the pointer is not a string.
A NULL string on the other hand will be a pointer which points to a location It is different from empty string.
that contains `\0`, which is a string of length 0 (or ""). Empty string is a pointer points `\0`.
Programs that use strings will include bugs if you aren't careful when using NULL pointers.
Another annoying problem is the memory that a string is allocated.
There are four cases: There are four cases:
- The string is read only; - The string is read only
- The string is in static memory area; - The string is in static memory area
- The string is in stack; and - The string is in stack
- The string is in memory allocated from the heap area. - The string is in memory allocated from the heap area
## Read only string ## Read only string
A string literal in a C program is surrounded by double quotes and written as the following: A string literal is surrounded by double quotes like this:
~~~C ~~~C
char *s; char *s;
s = "Hello" s = "Hello"
~~~ ~~~
"Hello" is a string literal, and is stored in program memory. "Hello" is a string literal, and is read only.
A string literal is read only.
In the program above, `s` points the string literal.
So, the following program is illegal. So, the following program is illegal.
~~~C ~~~C
@ -81,25 +80,38 @@ So, the following program is illegal.
The result is undefined. The result is undefined.
Probably a bad thing will happen, for example, a segmentation fault. Probably a bad thing will happen, for example, a segmentation fault.
NOTE: The memory of the literal string is allocated when the program is NOTE: The memory of the literal string is allocated when the program is compiled.
compiled. It is possible to view all the literal strings defined in your program It is possible to see the literal strings with `strings` command.
by using the `string` command.
~~~
$ strings src/tvf/a.out
/lib64/ld-linux-x86-64.so.2
cN<5
... ... ...
... ... ...
Once upon a time, there was an old man who was called Taketori-no-Okina. It is a japanese word that means a man whose work is making bamboo baskets.
One day, he went into a mountain and found a shining bamboo. "What a mysterious bamboo it is!," he said. He cut it, then there was a small cute baby girl in it. The girl was shining faintly. He thought this baby girl is a gift from Heaven and took her home.
His wife was surprized at his story. They were very happy because they had no children.
... ... ...
... ... ...
~~~
It tells us that literal strings are embedded in program binary codes.
## Strings defined as arrays ## Strings defined as arrays
If a string is defined as an array, it's in either stored in the static memory area or stack. If a string is defined as an array, it's stored in static memory area or stack.
This depends on the class of the array. It depends on the class of the array.
If the array's class is `static`, then it's placed in static memory area. If the array's class is `static`, then it's placed in static memory area.
This allocation and memory address is fixed for the life of the program. The allocated memory lives for the life of the program.
This area can be changed and is writable. This area is writable.
If the array's class is `auto`, then it's placed in stack. If the array's class is `auto`, it's placed in stack.
If the array is defined inside a function, its default class is `auto`. If the array is defined inside a function, its default class is `auto`.
The stack area will disappear when the function exits and returns to the caller. The stack area will disappear when the function returns to the caller.
Arrays defined on the stack are writable. Arrays defined on the stack are writable.
~~~C ~~~C
static char a[] = {'H', 'e', 'l', 'l', 'o', '\0'}; static char a[] = {'H', 'e', 'l', 'l', 'o', '\0'};
void void
@ -114,30 +126,28 @@ print_strings (void) {
} }
~~~ ~~~
The array `a` is defined externally to a function and is global in its scope. The array `a` is defined out of functions.
Such variables are placed in static memory area even if the `static` class is left out. It is placed in the static memory area even if the `static` class is left out.
The compiler calculates the number of the elements in the right hand side (six), The compiler calculates the number of the elements (six) and allocates six bytes in the static memory area.
and then creates code that allocates six bytes in the static memory area and copies the data to this memory. Then, it copies "Hello" literal string data to the memory.
The array `b` is defined inside the function The array `b` is defined inside the function, so its class is `auto`.
so its class is `auto`.
The compiler calculates the number of the elements in the string literal. The compiler calculates the number of the elements in the string literal.
It has six elements as the zero termination character is also included. It is six because it has '\0' terminator.
The compiler creates code which allocates six bytes memory in the stack and copies the data to the memory. The compiler allocates six bytes in the stack and copies "Hello" litaral string to the stack memory.
Both `a` and `b` are writable. Both `a` and `b` are writable.
The memory is managed by the executable program. The memory is allocated and freed by the program automatically so you don't need to allocate or free.
You don't need your program to allocate or free the memory for `a` and `b`. The array `a` is alive during the program's life time.
The array `a` is created then the program is first run and remains for the life of the program. The array `b` is alive when the function is called until the function returns to the caller.
The array `b` is created on the stack then the function is called, disappears when the function returns.
## Strings in the heap area ## Strings in the heap area
You can also get, use and release memory from the heap area. You can get, use and release memory from the heap area.
The standard C library provides `malloc` to get memory and `free` to put back memory. The standard C library provides `malloc` to get memory and `free` to put back memory.
GLib provides the functions `g_new` and `g_free` to do the same thing, with support for GLib provides the functions `g_new` and `g_free`.
some additional GLib functionality. They are similar to `malloc` and `free`.
~~~C ~~~C
g_new (struct_type, n_struct) g_new (struct_type, n_struct)
@ -195,9 +205,9 @@ s = g_strdup ("Hello");
g_free (s); g_free (s);
~~~ ~~~
The string literal "Hello" has 6 bytes because the string has '\0' at the end it. The string literal "Hello" has 6 bytes because the string has '\0' at the end.
`g_strdup` gets 6 bytes from the heap area and copies the string to the memory. `g_strdup` gets 6 bytes from the heap area and copies the string to the memory.
`s` is assigned the top address of the memory. `s` is assigned the start address of the memory.
`g_free` returns the memory to the heap area. `g_free` returns the memory to the heap area.
`g_strdup` is described in [GLib API Reference](https://docs.gtk.org/glib/func.strdup.html). `g_strdup` is described in [GLib API Reference](https://docs.gtk.org/glib/func.strdup.html).
@ -205,27 +215,38 @@ The following is extracted from the reference.
> The returned string should be freed with `g_free()` when no longer needed. > The returned string should be freed with `g_free()` when no longer needed.
The function reference will describe if the returned value needs to be freed. If you forget to free the allocated memory it will remain until the program ends.
If you forget to free the allocated memory it will remain allocated. Repeated use will cause Repeated allocation and no freeing cause memory leak.
more memory to be allocated to the program, which will grow over time. This is called a memory leak, It is a bug and may bring a serious problem.
and the only way to address this bug is to close the program (and restart it),
which will automatically release all of the programs memory back to the system.
Some GLib functions return a string which mustn't be freed by the caller. ## const qualifier
~~~C A `const` qualified variable can be assigned to initialize it.
const char * Once it is initialized, it is never allowed to change or free.
g_quark_to_string (GQuark quark);
~~~
This function returns `const char*` type.
The qualifier `const` means that the returned value is immutable.
The characters pointed by the returned value aren't be allowed to be changed or freed.
If a variable is qualified with `const`, the variable can't be assigned except during initialization.
~~~C ~~~C
const int x = 10; /* initialization is OK. */ const int x = 10; /* initialization is OK. */
x = 20; /* This is illegal because x is qualified with const */ x = 20; /* This is illegal because x is qualified with const */
~~~ ~~~
If a function returns `const char*` type, the string can't be changed or freed.
If a function has a `const char *` type parameter, it ensures that the parameter is not changed in the function.
~~~C
// You never change or free the returned string.
const char*
gtk_label_get_text (
GtkLabel* self
)
// Str keeps itself during the function runs
void
gtk_label_set_text (
GtkLabel* self,
const char* str
)
~~~

View file

@ -4,51 +4,40 @@
### G\_APPLICATION\_HANDLES\_OPEN flag ### G\_APPLICATION\_HANDLES\_OPEN flag
The GtkTextView, GtkTextBuffer and GtkScrolledWindow widgets have given us a minimum editor We made a very simple editor in the previous section with GtkTextView, GtkTextBuffer and GtkScrolledWindow.
in the previous section. We will add file-reading ability to the program and improve it to a file viewer.
We will now add a function to read a file and rework the program into a file viewer.
There are many ways to implement the function and
because this is a tutorial for beginners, we'll take the easiest one.
When the program starts, we will give the filename to open as an argument. The easiest way to give a filename is to use a command line argument.
$ ./a.out filename $ ./a.out filename
It will open the file and insert its contents into the GtkTextBuffer. The program will open the file and insert its contents into the GtkTextBuffer.
To do this, we need to know how GtkApplication (or GApplication) recognizes arguments. To do this, we need to know how GtkApplication (or GApplication) recognizes arguments.
This is described in the [GIO API Reference, Application](https://docs.gtk.org/gio/class.Application.html). This is described in the [GIO API Reference -- Application](https://docs.gtk.org/gio/class.Application.html).
When GtkApplication is created, a flag (with the type GApplicationFlags) is provided as an argument. When GtkApplication is created, a flag (GApplicationFlags) is given as an argument.
~~~C ~~~C
GtkApplication * GtkApplication *
gtk_application_new (const gchar *application_id, GApplicationFlags flags); gtk_application_new (const gchar *application_id, GApplicationFlags flags);
~~~ ~~~
This tutorial explains only two flags, `G_APPLICATION_FLAGS_NONE` and `G_APPLICATION_HANDLES_OPEN`. This tutorial explains only two flags, `G_APPLICATION_DEFAULT_FLAGS` and `G_APPLICATION_HANDLES_OPEN`.
(`G_APPLICATION_FLAGS_NONE` was used instead of `G_APPLICATION_DEFAULT_FLAGS` before GIO version2.73.3 (GLib 2.73.3 5/Aug/2022).
Some GTK 4 applications still use `G_APPLICATION_FLAGS_NONE`.
But now it is deprecated and `G_APPLICATION_DEFAULT_FLAGS` is recommended.)
If you want to handle command line arguments, the `G_APPLICATION_HANDLES_COMMAND_LINE` flag is what you need. If you want to handle command line arguments, the `G_APPLICATION_HANDLES_COMMAND_LINE` flag is what you need.
How to use the new application method is described in [GIO API Reference, g\_application\_run](https://docs.gtk.org/gio/method.Application.run.html),
and the flag is described in the [GIO API Reference, ApplicationFlags](https://docs.gtk.org/gio/flags.ApplicationFlags.html).
~~~ For further information, see [GIO API Reference -- ApplicationFlags](https://docs.gtk.org/gio/flags.ApplicationFlags.html) and
GApplicationFlags' Members [GIO API Reference -- g\_application\_run](https://docs.gtk.org/gio/method.Application.run.html).
G_APPLICATION_FLAGS_NONE Default. (No argument allowed) We've already used `G_APPLICATION_DEFAULT_FLAGS`, as it is the simplest option, and no command line arguments are allowed.
... ... ... If you give arguments, an error will occur.
G_APPLICATION_HANDLES_OPEN This application handles opening files (in the primary instance).
... ... ...
~~~
There are ten flags in total, but we only need two of them so far.
We've already used `G_APPLICATION_FLAGS_NONE`, as
it is the simplest option, and no arguments are allowed.
If you provide arguments when running the application, an error will occur.
The flag `G_APPLICATION_HANDLES_OPEN` is the second simplest option. The flag `G_APPLICATION_HANDLES_OPEN` is the second simplest option.
It allows arguments but only files. It allows arguments but only files.
The application assumes all the arguments are filenames and we will use this flag when creating The application assumes all the arguments are filenames.
our GtkApplication.
~~~C ~~~C
app = gtk_application_new ("com.github.ToshioCP.tfv3", G_APPLICATION_HANDLES_OPEN); app = gtk_application_new ("com.github.ToshioCP.tfv3", G_APPLICATION_HANDLES_OPEN);
@ -56,7 +45,7 @@ app = gtk_application_new ("com.github.ToshioCP.tfv3", G_APPLICATION_HANDLES_OPE
### open signal ### open signal
Now, when the application starts, two signals can be emitted. When `G_APPLICATION_HANDLES_OPEN` flag is given to the application, two signals are available.
- activate signal --- This signal is emitted when there's no argument. - activate signal --- This signal is emitted when there's no argument.
- open signal --- This signal is emitted when there is at least one argument. - open signal --- This signal is emitted when there is at least one argument.
@ -64,37 +53,38 @@ Now, when the application starts, two signals can be emitted.
The handler of the "open" signal is defined as follows. The handler of the "open" signal is defined as follows.
~~~C ~~~C
void user_function (GApplication *application, void
gpointer files, open (
gint n_files, GApplication* self,
gchar *hint, gpointer files,
gpointer user_data) gint n_files,
gchar* hint,
gpointer user_data
)
~~~ ~~~
The parameters are: The parameters are:
- `application` --- the application (usually GtkApplication) - `self` --- the application instance (usually GtkApplication)
- `files` --- an array of GFiles. [array length=n\_files] [element-type GFile] - `files` --- an array of GFiles. [array length=n\_files] [element-type GFile]
- `n_files` --- the number of the elements of `files` - `n_files` --- the number of the elements of `files`
- `hint` --- a hint provided by the calling instance (usually it can be ignored) - `hint` --- a hint provided by the calling instance (usually it can be ignored)
- `user_data` --- user data set when the signal handler was connected. - `user_data` --- user data set when the signal handler was connected.
How to read a specified file (GFile) will be described next.
## Making a file viewer ## Making a file viewer
### What is a file viewer? ### What is a file viewer?
A file viewer is a program that displays the text file that is given as an argument. A file viewer is a program that displays text files.
Our file viewer will work as follows. Our file viewer will work as follows.
- When arguments are given, it treats the first argument as a filename and opens it. - When arguments are given, it recognizes the first argument as a filename and opens it.
- If opening the file succeeds, it reads the contents of the file and inserts it to GtkTextBuffer and then shows the window. - The second argument and after are ignored.
- If it fails to open the file, it will show an error message and quit. - If there's no argument, it shows an error message and quit.
- If there's no argument, it will shows an error message and quit. - If it successfully opens the file, it reads the contents of the file, inserts them to GtkTextBuffer and shows the window.
- If there are two or more arguments, the second one and any others are ignored. - If it fails to open the file, it shows an error message and quit.
The program which does this is shown below. The program is shown below.
@@@include @@@include
tfv/tfv3.c tfv/tfv3.c
@ -108,25 +98,29 @@ Then compile and run it.
![File viewer](../image/screenshot_tfv3.png){width=6.3cm height=5.325cm} ![File viewer](../image/screenshot_tfv3.png){width=6.3cm height=5.325cm}
Let's explain how the program `tfv3.c` works. The function `main` has only two changes from the previous version.
First, the function `main` has only two changes from the previous version.
- `G_APPLICATION_FLAGS_NONE` is replaced by `G_APPLICATION_HANDLES_OPEN`; and - `G_APPLICATION_DEFAULT_FLAGS` is replaced by `G_APPLICATION_HANDLES_OPEN`
- `g_signal_connect (app, "open", G_CALLBACK (on_open), NULL)` is added. - `g_signal_connect (app, "open", G_CALLBACK (app_open), NULL)` is added.
Next, the handler `app_activate` is added and is very simple. When the flag `G_APPLICATION_HANDLES_OPEN` is given to `gtk_application_new` function, the application behaves like this:
It just outputs the error message and
the application quits immediately because no window is created.
The main functionality is the in the handler `app_open`. It - If the application is run without command line arguments, it emits "activate" signal when it is activated.
- If the application is run with command line arguments, it emits "open" signal when it is activated.
- Creates GtkApplicationWindow, GtkScrolledWindow, GtkTextView and GtkTextBuffer and connects them together; The handler `app_activate` becomes very simple.
- Sets wrap mode to `GTK_WRAP_WORD_CHAR` in GtktextView; It just outputs the error message and return to the caller.
- Sets GtkTextView to non-editable because the program isn't an editor but only a viewer; Then the application quits immediately because no window is created.
- Reads the file and inserts the text into GtkTextBuffer (this will be explained in detail later); and
- If the file is not opened then outputs an error message and destroys the window. This makes the application quit.
The following is the important file reading part of the program and is shown again below. The main work is done in the handler `app_open`.
- Creates GtkApplicationWindow, GtkScrolledWindow, GtkTextView and GtkTextBuffer and connects them together
- Sets wrap mode to `GTK_WRAP_WORD_CHAR` in GtktextView
- Sets GtkTextView to non-editable because the program isn't an editor but only a viewer
- Reads the file and inserts the text into GtkTextBuffer (this will be explained later)
- If the file is not opened, outputs an error message and destroys the window. This makes the application quit.
The following is the file reading part of the program again.
~~~C ~~~C
if (g_file_load_contents (files[0], NULL, &contents, &length, NULL, NULL)) { if (g_file_load_contents (files[0], NULL, &contents, &length, NULL, NULL)) {
@ -139,39 +133,41 @@ if (g_file_load_contents (files[0], NULL, &contents, &length, NULL, NULL)) {
gtk_widget_show (win); gtk_widget_show (win);
} else { } else {
if ((filename = g_file_get_path (files[0])) != NULL) { if ((filename = g_file_get_path (files[0])) != NULL) {
g_print ("No such file: %s.\n", filename); g_printerr ("No such file: %s.\n", filename);
g_free (filename); g_free (filename);
} } else
g_printerr ("File can't be opened.\n");
gtk_window_destroy (GTK_WINDOW (win)); gtk_window_destroy (GTK_WINDOW (win));
} }
~~~ ~~~
The function `g_file_load_contents` loads the file contents into a buffer, The function `g_file_load_contents` loads the file contents into a temporary buffer,
which is automatically allocated and sets `contents` to point that buffer. which is automatically allocated and sets `contents` to point the buffer.
The length of the buffer is set to `length`. The length of the buffer is assigned to `length`.
It returns `TRUE` if the file's contents are successfully loaded and `FALSE` if an error occurs. It returns `TRUE` if the file's contents are successfully loaded or `FALSE` if an error occurs.
If you want to know the details about g\_file\_load\_contents, see [g file load contents](https://docs.gtk.org/gio/method.File.load_contents.html).
If this function succeeds, it inserts the contents into GtkTextBuffer, If it has successfully read the file, it inserts the contents into GtkTextBuffer,
frees the buffer pointed by `contents`, sets the title of the window, frees the temporary buffer pointed by `contents`, sets the title of the window,
frees the memories pointed by `filename` and then shows the window. frees the memories pointed by `filename` and then shows the window.
If it fails, it outputs an error message and destroys the window, causing the program to quit. If it fails, it outputs an error message and destroys the window and finally make the program quit.
## GtkNotebook ## GtkNotebook
GtkNotebook is a container widget that uses tabs and contains multiple children. GtkNotebook is a container widget that contains multiple widgets with tabs.
The child that is displayed depends on which tab has been selected. It shows only one child at a time.
Another child will be shown when its tab is clicked.
![GtkNotebook](../image/screenshot_gtk_notebook.png){width=13.2cm height=5.325cm} ![GtkNotebook](../image/screenshot_gtk_notebook.png){width=13.2cm height=5.325cm}
Looking at the screenshots above, The left image is the window at the startup.
the left one is the window at the startup. The file `pr1.c` is shown and its filename is in the left tab.
It shows the file `pr1.c` and the filename is in the left tab. After clicking on the right tab, the contents of the file `tfv1.c` is shown.
After clicking on the right tab, the contents of the file `tfv1.c` are shown instead. The right image is the screenshot.
This is shown in the right screenshot.
The GtkNotebook widget is inserted as a child of GtkApplicationWindow and contains a GtkScrolledWindow The following is `tfv4.c`.
for each file that is being displayed. It has GtkNoteBook widget.
The code to do this is given in `tfv4.c` and is: It is inserted as a child of GtkApplicationWindow and contains multiple GtkScrolledWindow.
@@@include @@@include
tfv/tfv4.c tfv/tfv4.c
@ -180,34 +176,26 @@ tfv/tfv4.c
Most of the changes are in the function `app_open`. Most of the changes are in the function `app_open`.
The numbers at the left of the following items are line numbers in the source code. The numbers at the left of the following items are line numbers in the source code.
- 11-13: Variables `nb`, `lab` and `nbp` are defined and will point to a new GtkNotebook, GtkLabel and GtkNotebookPage widget respectively. - 11-13: Variables `nb`, `lab` and `nbp` are defined. They point GtkNotebook, GtkLabel and GtkNotebookPage respectively.
- 23: The window's title is set to "file viewer". - 23: The window's title is set to "file viewer".
- 25: The size of the window is set to maximum because a big window is appropriate for file viewers. - 24: The default size of the window is 600x400.
- 27-28 GtkNotebook is created and inserted to the GtkApplicationWindow as a child. - 26-27 GtkNotebook is created and inserted to the GtkApplicationWindow as a child.
- 30-59 For-loop. Each loop corresponds to a filename argument, and `files[i]` is GFile object containing the i-th argument. - 29-58 For-loop. The variable `files[i]` points i-th GFile, which is created by the GtkApplication from the i-th command line argument.
- 32-37 GtkScrollledWindow, GtkTextView are created and GtkTextBuffer found from the new GtkTextView. - 31-36 GtkScrollledWindow, GtkTextView are created. GtkTextBuffer is got from the GtkTextView.
GtkTextView is connected to GtkScrolledWindow as a child. The GtkTextView is connected to the GtkScrolledWindow as a child.
Each file gets their own copy of these widgets, so they are created inside the for-loop. - 38-39 inserts the contents of the file into GtkTextBuffer and frees the memory pointed by `contents`.
- 39-40 inserts the contents of the file into GtkTextBuffer and frees the memory pointed by `contents`. - 40-42: If the filename is taken from the GFile, GtkLabel is created with the filename. The string `filename` is freed..
- 41-43: Gets the filename and creates GtkLabel with the filename and then frees `filename`. - 43-44: If it fails to take the filename, empty string GtkLabel is created.
- 44-45: If `filename` is NULL, creates GtkLabel with the empty string. - 45-46: Appends a GtkScrolledWindow to the GtkNotebook as a child.
- 46: Appends GtkScrolledWindow as a page, with the tab GtkLabel, to GtkNotebook. And the GtkLabel is set as the child's tab.
At this time a GtkNoteBookPage widget is created automatically. At the same time, a GtkNoteBookPage is created automatically.
The GtkScrolledWindow widget is connected to the GtkNotebookPage. The function `gtk_notebook_get_page` returns the GtkNotebookPage of the child (GtkScrolledWindow).
Therefore, the structure is like this: - 47: GtkNotebookPage has "tab-expand" property.
~~~
GtkNotebook -- GtkNotebookPage -- GtkScrolledWindow
~~~
- 47: Gets GtkNotebookPage widget and sets `nbp` to point to this GtkNotebookPage.
- 48: GtkNotebookPage has a property set called "tab-expand".
If it is set to TRUE then the tab expands horizontally as long as possible. If it is set to TRUE then the tab expands horizontally as long as possible.
If it is FALSE, then the width of the tab is determined by the size of the label. If it is FALSE, then the width of the tab is determined by the size of the label.
`g_object_set` is a general function to set properties in objects. `g_object_set` is a general function to set properties of objects.
See [GObject API Reference, g\_object\_set](https://docs.gtk.org/gobject/method.Object.set.html). See [GObject API Reference -- g\_object\_set](https://docs.gtk.org/gobject/method.Object.set.html).
- 49-51: If the file cannot be read, "No such file" message is displayed and the `filename` buffer is freed. - 48-50: If it fails to read the file and a filename is taken from the GFile, "No such file" message is displayed. The `filename` is freed.
- 52-53: If `filename` is NULL, the "No valid file is given" message is outputted. - 51-52: If `filename` is NULL, the "No valid file is given" message is displayed.
- 55-58: If at least one file was read, then the number of GtkNotebookPage is greater than zero. - 54-57: If at least one page exists, the window is shown.
If it's true, it shows the window. Otherwise, the window is destroyed and the application quits.
If it's false, it destroys the window, which causes the program to quit.

View file

@ -1,60 +1,62 @@
# Defining a child object # Defining a final class
## A very simple editor ## A very simple editor
In the previous section we made a very simple file viewer. We made a very simple file viewer in the previous section.
Now we go on to rewrite it and turn it into very simple editor. Now we go on to rewrite it and turn it into very simple editor.
Its source file is in tfe1.c (text file editor 1). Its source file is `tfe1.c` (text file editor 1) under `tfe` directory.
GtkTextView has a feature for editing multiple lines. Therefore, we don't need to GtkTextView is a multi-line editor.
write the program from scratch, we just add two things to the file viewer: So, we don't need to write the editor from scratch.
We just add two things to the file viewer:
- Memory to store a pointer to the GFile instance. - Pointers to GFile instances.
- A function to write the file. - A text-save function.
There are a couple of ways to store the details of GFile. There are a couple of ways to store the pointers.
- Use global variables; or - Use global variables
- Make a child object, which can extend the instance memory for the GFile object. - Make a child class of GtkTextView and its each instance holds a pointer to the GFile instance.
Using global variables is easy to implement. Using global variables is easy to implement.
Define a sufficient size array of pointers to GFile. Define a sufficient size pointer array to GFile.
For example, For example,
~~~C ~~~C
GFile *f[20]; GFile *f[20];
~~~ ~~~
The variable `f[i]` corresponds to the file associated to the i-th GtkNotebookPage. The variable `f[i]` corresponds to the file associated with the i-th GtkNotebookPage.
There are however two problems with this.
The first concerns the size of the array.
If a user gives too many arguments (more than 20 in the example above), it is impossible to store the additional pointers to the GFile instances.
The second is the increasing difficulty for maintenance of the program.
We have a small program so far,
but however, if you continue developing it, the size of the program will grow.
Generally speaking, the bigger the program size, the more difficult it is to keep track of and maintain global variables. Global variables can be used and changed anywhere throughout the entire program.
Making a child object is a good idea in terms of maintenance. However, There are two problems.
One thing you need to be careful of is the difference between "child object" and "child widget". The first is the size of the array.
Here we are describing a "child object". If a user gives too many arguments (more than 20 in the example above), it is impossible to store all the pointers to the GFile instances.
A child object includes, and expands on its parent object, as The second is difficulty to maintain the program.
a child object derives everything from the parent object. We have a small program so far.
But, the more you develop the program, the bigger its size grows.
Generally speaking, it is very difficult to maintain global variables in a big program.
When you check the global variable, you need to check all the codes that use the variable.
Making a child class is a good idea in terms of maintenance.
And we prefer it rather than a global variable.
Be careful that we are thinking about "child class", not "child widget".
Child class and child widget are totally different.
Class is a term of GObject system.
If you are not familiar with GObject, see:
- [GObject API reference](https://docs.gtk.org/gobject/)
- [GObject tutorial for beginners](https://toshiocp.github.io/Gobject-tutorial/)
A child class inherits everything from the parent and, in addition, extends its performance.
We will define TfeTextView as a child class of GtkTextView.
It has everything that GtkTextView has and adds a pointer to a GFile.
![Child object of GtkTextView](../image/child.png){width=9.675cm height=4.89cm} ![Child object of GtkTextView](../image/child.png){width=9.675cm height=4.89cm}
We will define TfeTextView as a child object of GtkTextView. ## How to define a child class of GtkTextView
It has everything that GtkTextView has.
Specifically, TfeTextView has a GtkTextbuffer which corresponds to the GtkTextView inside TfeTextView.
The additional important thing is that TfeTextView can also keep an additional pointer to GFile.
In general, this is how GObjects work. Understanding the general theory about Gobject's is difficult, You need to know GObject system convention.
particularly for beginners.
So, I will just show you the way how to write the code and avoid the theoretical side.
If you want to know about GObject system, refer to another [tutorial](https://github.com/ToshioCP/Gobject-tutorial).
## How to define a child object of GtkTextView
Let's define the TfeTextView object, which is a child object of GtkTextView.
First, look at the program below. First, look at the program below.
~~~C ~~~C
@ -93,55 +95,55 @@ tfe_text_view_new (void) {
} }
~~~ ~~~
If you are curious about the background theory of this program, that's good,
because knowing the theory is very important if you want to program GTK applications.
Look at [GObject API Reference](https://docs.gtk.org/gobject/).
All you need is described there,
or refer to [GObject tutorial](https://github.com/ToshioCP/Gobject-tutorial).
It's a tough journey especially for beginners so for now, you don't need to know about this difficult theory.
It is enough to just remember the instructions below.
- TfeTextView is divided into two parts. - TfeTextView is divided into two parts.
Tfe and TextView. Tfe and TextView.
Tfe is called the prefix, namespace or module. Tfe is called prefix or namespace.
TextView is called the object. TextView is called object.
- There are three differnet identifier patterns. - There are three differnet identifier patterns.
TfeTextView (camel case), tfe\_text\_view (this is used to write functions) and TFE\_TEXT\_VIEW (This is used to cast a pointer to point TfeTextView type). TfeTextView (camel case), tfe\_text\_view (this is used for functions) and TFE\_TEXT\_VIEW (This is used to cast a object to TfeTextView).
- First, define TFE\_TYPE\_TEXT\_VIEW macro as tfe\_text\_view\_get\_type (). - First, define TFE\_TYPE\_TEXT\_VIEW macro as tfe\_text\_view\_get\_type ().
The name is always (prefix)\_TYPE\_(object) and the letters are upper case. The name is always (prefix)\_TYPE\_(object) and the letters are upper case.
And the replacement text is always (prefix)\_(object)\_get\_type () and the letters are lower case. And the replacement text is always (prefix)\_(object)\_get\_type () and the letters are lower case.
- Next, use G\_DECLARE\_FINAL\_TYPE macro. This definition is put before G\_DECLARE\_FINAL\_TYPE macro.
The arguments are the child object name in camel case, lower case with underscore, prefix (upper case), object (upper case with underscore) and parent object name (camel case). - The arguments of G\_DECLARE\_FINAL\_TYPE macro are the child class name in camel case, lower case with underscore, prefix (upper case), object (upper case with underscore) and parent class name (camel case).
The following two C structure is declared in the expansion of the macro.
- `typedef struct _TfeTextView TfeTextView`
- `typedef struct {GtkTextViewClass parent_class; } TfeTextViewClass;`
- These declaration tells us that TfeTextView and TfeTextViewClass are C structures.
"TfeTextView" has two meanings, class name and C structure name.
The C structure TfeTextView is called object.
Similarly, TfeTextViewClass is called class.
- Declare the structure \_TfeTextView. - Declare the structure \_TfeTextView.
The underscore is necessary. The underscore is necessary.
The first member is the parent object. The first member is the parent object (C structure).
Notice this is not a pointer but the object itself. Notice this is not a pointer but the object itself.
The second member and after are members of the child object. The second member and after are members of the child object.
TfeTextView structure has a pointer to a GFile instance as a member. TfeTextView structure has a pointer to a GFile instance as a member.
- Use G\_DEFINE\_TYPE macro. - G\_DEFINE\_TYPE macro.
The arguments are the child object name in camel case, lower case with underscore and parent object type (prefix)\_TYPE\_(module). The arguments are the child object name in camel case, lower case with underscore and parent object type (prefix)\_TYPE\_(module).
- Define instance init function (tfe\_text\_view\_init). This macro is mainly used to register the new class to the type system.
Usually you don't need to do anything. Type system is a base system of GObject.
- Define class init function (tfe\_text\_view\_class\_init). Every class has its own type.
You don't need to do anything in this object. The types of GObject, GtkWidget and TfeTextView are G\_TYPE\_OBJECT, GTK\_TYPE\_WIDGET and TFE\_TYPE\_TEXT\_VIEW respectively.
- Write function codes you want to add (tfe\_text\_view\_set\_file and tfe\_text\_view\_get\_file). Such type (for example, TFE\_TYPE\_TEXT\_VIEW) is a macro and it is expanded to a function (tfe\_text\_view\_get\_type()).
`tv` is a pointer to the TfeTextView object instance which is a C-structure declared with the tag \_TfeTextView. It returns a integer which is unique among all GObject system classes.
So, the structure has a member `file` as a pointer to a GFile instance. - Instance init function (tfe\_text\_view\_init) is called when the instance is created.
`tv->file = f` is an assignment of `f` to a member `file` of the structure pointed by `tv`. It is the same as a constructor in other object oriented languages.
This is an example how to use the extended memory in a child widget. - Class init function (tfe\_text\_view\_class\_init) is called when the class is created.
- Write a function to create an instance. - Two functions tfe\_text\_view\_set\_file and tfe\_text\_view\_get\_file are public functions.
Public functions are open and you can call them anywhere.
They are the same as public method in other object oriented languages.
`tv` is a pointer to the TfeTextView object (C structure).
It has a member `file` and it is pointed by `tv->file`.
- TfeTextView instance creation function is `tfe_text_view_new`.
Its name is (prefix)\_(object)\_new. Its name is (prefix)\_(object)\_new.
If the parent object function needs parameters, this function also need them. It uses g\_object\_new function to create the instance.
You sometimes might want to add some parameters. The arguments are (prefix)\_TYPE\_(object), a list to initialize properties and NULL.
It's your choice. NULL is the end mark of the property list.
Use g\_object\_new function to create the instance. No property is initialized here.
The arguments are (prefix)\_TYPE\_(object), a list to initialize properties and NULL.
In this code no property needs to be initialized.
And the return value is casted to GtkWidget. And the return value is casted to GtkWidget.
This program is not perfect. This program shows the outline how to define a child class.
It has some problems.
It will be modified later.
## Close-request signal ## Close-request signal
@ -154,10 +156,9 @@ After you finish editing, you exit the editor.
The editor updates files just before the window closes. The editor updates files just before the window closes.
GtkWindow emits the "close-request" signal before it closes. GtkWindow emits the "close-request" signal before it closes.
We connect the signal and the handler `before_close`. We will connect the signal and the handler `before_close`.
A handler is a C function. (A handler is a C function which is connected to a signal.)
When a function is connected to a certain signal, we call it a handler. The function `before_close` is called when the signal "close-request" is emitted.
The function `before_close` is invoked when the signal "close-request" is emitted.
~~~C ~~~C
g_signal_connect (win, "close-request", G_CALLBACK (before_close), NULL); g_signal_connect (win, "close-request", G_CALLBACK (before_close), NULL);
@ -173,36 +174,46 @@ tfe/tfe1.c before_close
The numbers on the left of items are line numbers in the source code. The numbers on the left of items are line numbers in the source code.
- 15: Gets the number of pages `nb` has. - 14: The number of pages of `nb` is assigned to `n`.
- 16-29: For loop with regard to the index to each pages. - 15-31: For loop with regard to the index to each pages.
- 17-19: Gets GtkScrolledWindow, TfeTextView and a pointer to GFile. - 16-18: `scr`, `tv` and `file` is assigned pointers to the GtkScrolledWindow, TfeTextView and GFile.
The pointer was stored when `app_open` handler had run. It will be shown later. The GFile of TfeTextView was stored when `app_open` handler was called. It will be shown later.
- 20-22: Gets GtkTextBuffer and contents. `start_iter` and `end_iter` are iterators of the buffer. - 19-21: `tb` is assigned the GtkTextBuffer of the TfeTextView.
I don't want to explain them now because it would take a lot of time. The buffer is accessed with iterators.
Just remember these lines for the present. Iterators points somewhere in the buffer.
- 23-27: Writes the contents to the file. The function `gtk_text_buffer_get_bounds` assigns the start and end of the buffer to `start_iter` and `end_iter` respectively.
If it fails, it outputs an error message. Then the function `gtk_text_buffer_get_text` returns the text between `start_iter` and `end_iter`, which is the whole text in the buffer.
- 28: Frees `contents`. - 22-28: The text is saved to the file.
If it fails, error messages are displayed.
- 29: `contents` are freed.
- 30: GFile is useless. `g_object_unref` decreases the reference count of the GFile.
Reference count will be explained in the later section.
The reference count will be zero in this program and the GFile instance will destroy itself.
## Source code of tfe1.c ## Source code of tfe1.c
The following is the complete source code of `tfe1.c`. The following is the whole source code of `tfe1.c`.
@@@include @@@include
tfe/tfe1.c tfe/tfe1.c
@@@ @@@
- 107: Sets the pointer to GFile into TfeTextView. - 110: The GFile pointer of the TfeTextView is set with `files[i]`, which is a GFile created with the command line argument.
`files[i]` is a pointer to GFile structure. But the GFile will be destroyed by the system later.
It will be freed by the system. So you need to copy it. So it needs to be copied before the assignment.
`g_file_dup` duplicates the given GFile structure. `g_file_dup` duplicates the GFile.
- 123: Connects "close-request" signal and `before_close` handler. - 126: The "close-request" signal is connected to `before_close` handler.
The fourth argument is called user data and it is given to the signal handler. The fourth argument is called "user data" and it will be the second argument of the signal handler.
So, `nb` is given to `before_close` as the second argument. So, `nb` is given to `before_close` as the second argument.
Now compile and run it. Now it's time to compile and run.
There's a sample file in the directory `tfe`.
Type `./a.out taketori.txt`. ~~~
$ cd src/tfe
$ comp tfe1
$ ./a.out taketori.txt`.
~~~
Modify the contents and close the window. Modify the contents and close the window.
Make sure that the file is modified. Make sure that the file is modified.

View file

@ -1,13 +1,12 @@
# The User Interface (UI) file and GtkBuilder # GtkBuilder and UI file
## New, Open and Save button ## New, Open and Save button
In the last section we made the almost simplest editor possible. We made very simple editor in the previous section.
It reads files in the `app_open` function at start-up and writes them out when closing the window. It reads files at the start and writes them out at the end of the program.
It works but is not very good. It works, but is not so good.
It would be better if we had "New", "Open", "Save" and "Close" buttons. It would be better if we had "New", "Open", "Save" and "Close" buttons.
This section describes how to put those buttons into the window. This section describes how to put those buttons into the window.
Signals and handlers will be explained later.
![Screenshot of the file editor](../image/screenshot_tfe2.png){width=9.3cm height=6.825cm} ![Screenshot of the file editor](../image/screenshot_tfe2.png){width=9.3cm height=6.825cm}
@ -18,7 +17,7 @@ The function `app_open` in the source code `tfe2.c` is as follows.
tfe/tfe2.c app_open tfe/tfe2.c app_open
@@@ @@@
The aim is to build the widgets of the main application window. The function `app_open` builds the widgets in the main application window.
- 25-27: Creates a GtkApplicationWindow instance and sets the title and default size. - 25-27: Creates a GtkApplicationWindow instance and sets the title and default size.
- 29-30: Creates a GtkBox instance `boxv`. - 29-30: Creates a GtkBox instance `boxv`.
@ -33,13 +32,12 @@ The other label `dmy2` has hexpand property which is set to be TRUE.
This makes the label expands horizontally as long as possible. This makes the label expands horizontally as long as possible.
- 41-44: Creates four buttons. - 41-44: Creates four buttons.
- 46-52: Appends these GtkLabel and GtkButton to `boxh`. - 46-52: Appends these GtkLabel and GtkButton to `boxh`.
- 54-57: Creates a GtkNotebook instance and sets hexpand and vexpand properties TRUE. - 54-57: Creates a GtkNotebook instance and sets hexpand and vexpand properties to be TRUE.
This makes it expand horizontally and vertically as big as possible. This makes it expand horizontally and vertically as big as possible.
It is appended to `boxv` as the second child. It is appended to `boxv` as the second child.
The number of lines to build the widgets is 33(=57-25+1). The number of widget-build lines is 33(=57-25+1).
We also needed many additional variables (`boxv`, `boxh`, `dmy1`, ...), We also needed many variables (`boxv`, `boxh`, `dmy1`, ...) and most of them used only for building the widgets.
most of which weren't necessary, except for building the widgets.
Are there any good solution to reduce these work? Are there any good solution to reduce these work?
Gtk provides GtkBuilder. Gtk provides GtkBuilder.
@ -48,32 +46,30 @@ It reduces this cumbersome work.
## The UI File ## The UI File
First, let's look at the UI file `tfe3.ui` that is used to define the widget structure. Look at the UI file `tfe3.ui` that defines widget structure.
@@@include @@@include
tfe/tfe3.ui tfe/tfe3.ui
@@@ @@@
The structure of this file is XML. The is a XML file.
Constructs that begin with `<` and end with `>` are called tags. Tags begin with `<` and end with `>`.
There are two types of tags, the start tag and the end tag. There are two types of tags, the start tag and the end tag.
For example, `<interface>` is a start tag and `</interface>` is an end tag. For example, `<interface>` is a start tag and `</interface>` is an end tag.
The UI file begins and ends with interface tags. The UI file begins and ends with interface tags.
Some tags, for example object tags, can have a class and id attributes in their start tag. Some tags, for example object tags, can have a class and id attributes in their start tag.
- 1: The first line is XML declaration. - 1: XML declaration.
It specifies that the version of XML is 1.0 and the encoding is UTF-8. It specifies that the XML version is 1.0 and the encoding is UTF-8.
Even if the line is left out, GtkBuilder builds objects from the ui file. - 3-6: An object tag with `GtkApplicationWindow` class and `win` id.
But ui files must use UTF-8 encoding, or GtkBuilder can't recognize it and a fatal error occurs.
- 3-6: An object with `GtkApplicationWindow` class and `win` id is defined.
This is the top level window. This is the top level window.
And the three properties of the window are defined. And the three properties of the window are defined.
`title` property is "file editor", `default-width` property is 600 and `default-height` property is 400. The `title` property is "file editor", `default-width` property is 600 and `default-height` property is 400.
- 7: child tag means a child of the widget above. - 7: child tag means a child widget.
For example, line 7 tells us that GtkBox object which id is "boxv" is a child widget of `win`. For example, line 7 tells us that GtkBox object with "boxv" id attribute is a child widget of `win`.
Compare this ui file and the lines 25-57 in the source code of `app_open` function. Compare this ui file and the lines 25-57 in the `tfe2.c` source code.
Those two describe the same structure of widgets. Both builds the same window with its descendant widgets.
You can check the ui file with `gtk4-builder-tool`. You can check the ui file with `gtk4-builder-tool`.
@ -90,7 +86,7 @@ It is a good idea to check your ui file before compiling.
## GtkBuilder ## GtkBuilder
GtkBuilder builds widgets based on the ui file. GtkBuilder builds widgets based on a ui file.
~~~C ~~~C
GtkBuilder *build; GtkBuilder *build;
@ -105,7 +101,7 @@ The function `gtk_builder_new_from_file` reads the file given as an argument.
Then, it builds the widgets and creates GtkBuilder object. Then, it builds the widgets and creates GtkBuilder object.
The function `gtk_builder_get_object (build, "win")` returns the pointer to the widget `win`, which is the id in the ui file. The function `gtk_builder_get_object (build, "win")` returns the pointer to the widget `win`, which is the id in the ui file.
All the widgets are connected based on the parent-children relationship described in the ui file. All the widgets are connected based on the parent-children relationship described in the ui file.
We only need `win` and `nb` for the program after this, so we don't need to take out any other widgets. We only need `win` and `nb` for the program below.
This reduces lines in the C source file. This reduces lines in the C source file.
@@@shell @@@shell
@ -123,12 +119,11 @@ tfe/tfe3.c app_open
@@@ @@@
The whole source code of `tfe3.c` is stored in [src/tfe](tfe) directory. The whole source code of `tfe3.c` is stored in [src/tfe](tfe) directory.
If you want to see it, click the link above.
### Using ui string ### Using ui string
GtkBuilder can build widgets using string. GtkBuilder can build widgets with string.
Use the function `gtk_builder_new_from_string` instead of `gtk_builder_new_from_file`. Use `gtk_builder_new_from_string` instead of `gtk_builder_new_from_file`.
~~~C ~~~C
char *uistring; char *uistring;
@ -146,19 +141,19 @@ uistring =
... ... ... ... ... ...
"</interface>"; "</interface>";
build = gtk_builder_new_from_stringfile (uistring); build = gtk_builder_new_from_string (uistring, -1);
~~~ ~~~
This method has an advantage and disadvantage. This method has an advantage and disadvantage.
The advantage is that the ui string is written in the source code. The advantage is that the ui string is written in the source code.
So ui file is not necessary on runtime. So, no ui file is needed on runtime.
The disadvantage is that writing C string is a bit bothersome because of the double quotes. The disadvantage is that writing C string is a bit bothersome because of the double quotes.
If you want to use this method, you should write a script that transforms ui file into C-string. If you want to use this method, you should write a script that transforms ui file into C-string.
- Add backslash before each double quote. - Add backslash before each double quote.
- Add double quotes at the left and right of the string in each line. - Add double quotes at the left and right of the string in each line.
### Using Gresource ### Gresource
Using Gresource is similar to using string. Using Gresource is similar to using string.
But Gresource is compressed binary data, not text data. But Gresource is compressed binary data, not text data.
@ -176,9 +171,10 @@ tfe/tfe3.gresource.xml
- 2: `gresources` tag can include multiple gresources (gresource tags). - 2: `gresources` tag can include multiple gresources (gresource tags).
However, this xml has only one gresource. However, this xml has only one gresource.
- 3: The gresource has a prefix `/com/github/ToshioCP/tfe3`. - 3: The gresource has a prefix `/com/github/ToshioCP/tfe3`.
- 4: The gresource has `tfe3.ui`. - 4: The name of the gresource is `tfe3.ui`.
And it is pointed by `/com/github/ToshioCP/tfe3/tfe3.ui` because it needs prefix. The resource will be pointed with `/com/github/ToshioCP/tfe3/tfe3.ui` by GtkBuilder.
If you want to add more files, then insert them between line 4 and 5. The pattern is "prefix" + "name".
If you want to add more files, insert them between line 4 and 5.
Save this xml text to `tfe3.gresource.xml`. Save this xml text to `tfe3.gresource.xml`.
The gresource compiler `glib-compile-resources` shows its usage with the argument `--help`. The gresource compiler `glib-compile-resources` shows its usage with the argument `--help`.
@ -189,7 +185,9 @@ LANG=C glib-compile-resources --help
Now run the compiler. Now run the compiler.
$ glib-compile-resources tfe3.gresource.xml --target=resources.c --generate-source ~~~
$ glib-compile-resources tfe3.gresource.xml --target=resources.c --generate-source
~~~
Then a C source file `resources.c` is generated. Then a C source file `resources.c` is generated.
Modify `tfe3.c` and save it as `tfe3_r.c`. Modify `tfe3.c` and save it as `tfe3_r.c`.
@ -203,5 +201,10 @@ build = gtk_builder_new_from_resource ("/com/github/ToshioCP/tfe3/tfe3.ui");
... ... ... ... ... ...
~~~ ~~~
The function `gtk_builder_new_from_resource` builds widgets from a resource.
Then, compile and run it. Then, compile and run it.
The window appears and it is the same as the screenshot at the beginning of this page. A window appears and it is the same as the screenshot at the beginning of this page.
Generally, resource is the best way for C language.
If you use other languages like Ruby, string is better than resource.

View file

@ -1,4 +1,4 @@
Once upon a time, there was an old man who was called Taketori-no-Okina. It is a japanese word that means a man whose work is making bamboo baskets. Once upon a time, there was an old man who was called Taketori-no-Okina. It is a japanese word that means a man whose work is making bamboo baskets.
One day, he went into a mountain and found a shining bamboo. "What a mysterious bamboo it is!," he said. He cut it, then there was a small cute baby girl in it. The girl was shining faintly. He thought this baby girl is a gift from Heaven and took her home. One day, he went into a mountain and found a shining bamboo. "What a mysterious bamboo it is!," he said. He cut it, then there was a small cute baby girl in it. The girl was shining faintly. He thought this baby girl is a gift from Heaven and took her home.
His wife was surprized at his tale. They were very happy because they had no children. His wife was surprized at his story. They were very happy because they had no children.

View file

@ -1,6 +1,6 @@
#include <gtk/gtk.h> #include <gtk/gtk.h>
/* Define TfeTextView Widget which is the child object of GtkTextView */ /* Define TfeTextView Widget which is the child class of GtkTextView */
#define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type () #define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type ()
G_DECLARE_FINAL_TYPE (TfeTextView, tfe_text_view, TFE, TEXT_VIEW, GtkTextView) G_DECLARE_FINAL_TYPE (TfeTextView, tfe_text_view, TFE, TEXT_VIEW, GtkTextView)
@ -15,6 +15,7 @@ G_DEFINE_TYPE (TfeTextView, tfe_text_view, GTK_TYPE_TEXT_VIEW);
static void static void
tfe_text_view_init (TfeTextView *tv) { tfe_text_view_init (TfeTextView *tv) {
tv->file = NULL;
} }
static void static void
@ -23,7 +24,7 @@ tfe_text_view_class_init (TfeTextViewClass *class) {
void void
tfe_text_view_set_file (TfeTextView *tv, GFile *f) { tfe_text_view_set_file (TfeTextView *tv, GFile *f) {
tv -> file = f; tv->file = f;
} }
GFile * GFile *
@ -39,8 +40,7 @@ tfe_text_view_new (void) {
/* ---------- end of the definition of TfeTextView ---------- */ /* ---------- end of the definition of TfeTextView ---------- */
static gboolean static gboolean
before_close (GtkWindow *win, gpointer user_data) { before_close (GtkWindow *win, GtkWidget *nb) {
GtkWidget *nb = GTK_WIDGET (user_data);
GtkWidget *scr; GtkWidget *scr;
GtkWidget *tv; GtkWidget *tv;
GFile *file; GFile *file;
@ -61,22 +61,25 @@ before_close (GtkWindow *win, gpointer user_data) {
gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter); gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter);
contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE); contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE);
if (! g_file_replace_contents (file, contents, strlen (contents), NULL, TRUE, G_FILE_CREATE_NONE, NULL, NULL, NULL)) { if (! g_file_replace_contents (file, contents, strlen (contents), NULL, TRUE, G_FILE_CREATE_NONE, NULL, NULL, NULL)) {
pathname = g_file_get_path (file); if ((pathname = g_file_get_path (file)) != NULL) {
g_print ("ERROR : Can't save %s.", pathname); g_printerr ("Can't save %s.", pathname);
g_free (pathname); g_free (pathname);
} else
g_printerr ("Pathname not exist.\n");
} }
g_free (contents); g_free (contents);
g_object_unref (file);
} }
return FALSE; return FALSE;
} }
static void static void
app_activate (GApplication *app, gpointer user_data) { app_activate (GApplication *app) {
g_print ("You need to give filenames as arguments.\n"); g_print ("You need to give filenames as arguments.\n");
} }
static void static void
app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer user_data) { app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint) {
GtkWidget *win; GtkWidget *win;
GtkWidget *nb; GtkWidget *nb;
GtkWidget *lab; GtkWidget *lab;
@ -91,7 +94,7 @@ app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer
win = gtk_application_window_new (GTK_APPLICATION (app)); win = gtk_application_window_new (GTK_APPLICATION (app));
gtk_window_set_title (GTK_WINDOW (win), "file editor"); gtk_window_set_title (GTK_WINDOW (win), "file editor");
gtk_window_maximize (GTK_WINDOW (win)); gtk_window_set_default_size (GTK_WINDOW (win), 600, 400);
nb = gtk_notebook_new (); nb = gtk_notebook_new ();
gtk_window_set_child (GTK_WINDOW (win), nb); gtk_window_set_child (GTK_WINDOW (win), nb);

View file

@ -39,12 +39,12 @@ tfe_text_view_new (void) {
/* ---------- end of the definition of TfeTextView ---------- */ /* ---------- end of the definition of TfeTextView ---------- */
static void static void
app_activate (GApplication *app, gpointer user_data) { app_activate (GApplication *app) {
g_print ("You need a filename argument.\n"); g_print ("You need a filename argument.\n");
} }
static void static void
app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer user_data) { app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint) {
GtkWidget *win; GtkWidget *win;
GtkWidget *nb; GtkWidget *nb;
GtkWidget *lab; GtkWidget *lab;
@ -142,4 +142,3 @@ main (int argc, char **argv) {
g_object_unref (app); g_object_unref (app);
return stat; return stat;
} }

View file

@ -39,12 +39,12 @@ tfe_text_view_new (void) {
/* ---------- end of the definition of TfeTextView ---------- */ /* ---------- end of the definition of TfeTextView ---------- */
static void static void
app_activate (GApplication *app, gpointer user_data) { app_activate (GApplication *app) {
g_print ("You need a filename argument.\n"); g_print ("You need a filename argument.\n");
} }
static void static void
app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer user_data) { app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint) {
GtkWidget *win; GtkWidget *win;
GtkWidget *nb; GtkWidget *nb;
GtkWidget *lab; GtkWidget *lab;

View file

@ -39,12 +39,12 @@ tfe_text_view_new (void) {
/* ---------- end of the definition of TfeTextView ---------- */ /* ---------- end of the definition of TfeTextView ---------- */
static void static void
app_activate (GApplication *app, gpointer user_data) { app_activate (GApplication *app) {
g_print ("You need a filename argument.\n"); g_print ("You need a filename argument.\n");
} }
static void static void
app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer user_data) { app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint) {
GtkWidget *win; GtkWidget *win;
GtkWidget *nb; GtkWidget *nb;
GtkWidget *lab; GtkWidget *lab;

View file

@ -2,8 +2,10 @@ require 'rake/clean'
targetfile = "tfe" targetfile = "tfe"
srcfiles = FileList["tfe.c", "tfetextview.c", "resources.c"] srcfiles = FileList["tfe.c", "tfetextview.c", "resources.c"]
uifile = "tfe.ui"
rscfile = srcfiles[2] rscfile = srcfiles[2]
objfiles = srcfiles.gsub(/.c$/, '.o') objfiles = srcfiles.ext(".o")
gresource_xml = "tfe.gresource.xml"
CLEAN.include(targetfile, objfiles, rscfile) CLEAN.include(targetfile, objfiles, rscfile)
@ -14,12 +16,12 @@ file targetfile => objfiles do |t|
end end
objfiles.each do |obj| objfiles.each do |obj|
src = obj.gsub(/.o$/,'.c') src = obj.ext(".c")
file obj => src do |t| file obj => src do |t|
sh "gcc -c -o #{t.name} `pkg-config --cflags gtk4` #{t.source}" sh "gcc -c -o #{t.name} `pkg-config --cflags gtk4` #{t.source}"
end end
end end
file rscfile => ["tfe.gresource.xml", "tfe.ui"] do |t| file rscfile => uifile do |t|
sh "glib-compile-resources #{t.prerequisites[0]} --target=#{t.name} --generate-source" sh "glib-compile-resources #{gresource_xml} --target=#{t.name} --generate-source"
end end

View file

@ -2,12 +2,12 @@
#include "tfetextview.h" #include "tfetextview.h"
static void static void
app_activate (GApplication *app, gpointer user_data) { app_activate (GApplication *app) {
g_print ("You need a filename argument.\n"); g_print ("You need a filename argument.\n");
} }
static void static void
app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer user_data) { app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint) {
GtkWidget *win; GtkWidget *win;
GtkWidget *nb; GtkWidget *nb;
GtkWidget *lab; GtkWidget *lab;

View file

@ -1,4 +0,0 @@
#include <gtk/gtk.h>
#include "../tfetextview/tfetextview.h"
#include "tfenotebook.h"

View file

@ -1,4 +1,7 @@
#include "tfe.h" #include <gtk/gtk.h>
#include "../tfetextview/tfetextview.h"
#include "tfenotebook.h"
static void static void
open_cb (GtkNotebook *nb) { open_cb (GtkNotebook *nb) {
@ -96,4 +99,3 @@ main (int argc, char **argv) {
g_object_unref (app); g_object_unref (app);
return stat; return stat;
} }

View file

@ -1,7 +1,9 @@
#include "tfe.h" #include <gtk/gtk.h>
#include "../tfetextview/tfetextview.h"
/* The returned string should be freed with g_free() when no longer needed. */ /* The returned string should be freed with g_free() when no longer needed. */
static gchar* static char*
get_untitled () { get_untitled () {
static int c = -1; static int c = -1;
if (++c == 0) if (++c == 0)
@ -67,14 +69,14 @@ notebook_page_close (GtkNotebook *nb) {
} }
} }
// filename is owned by the caller.
static void static void
notebook_page_build (GtkNotebook *nb, GtkWidget *tv, char *filename) { notebook_page_build (GtkNotebook *nb, GtkWidget *tv, const char *filename) {
GtkWidget *scr = gtk_scrolled_window_new (); GtkWidget *scr = gtk_scrolled_window_new ();
GtkNotebookPage *nbp; GtkNotebookPage *nbp;
GtkWidget *lab; GtkWidget *lab;
int i; int i;
gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv); gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);
lab = gtk_label_new (filename); lab = gtk_label_new (filename);
i = gtk_notebook_append_page (nb, scr, lab); i = gtk_notebook_append_page (nb, scr, lab);
@ -89,13 +91,15 @@ open_response (TfeTextView *tv, int response, GtkNotebook *nb) {
GFile *file; GFile *file;
char *filename; char *filename;
if (response != TFE_OPEN_RESPONSE_SUCCESS || ! G_IS_FILE (file = tfe_text_view_get_file (tv))) { if (response != TFE_OPEN_RESPONSE_SUCCESS) {
g_object_ref_sink (tv); g_object_ref_sink (tv);
g_object_unref (tv); g_object_unref (tv);
}else { }else {
file = tfe_text_view_get_file (tv);
filename = g_file_get_basename (file); filename = g_file_get_basename (file);
g_object_unref (file); g_object_unref (file);
notebook_page_build (nb, GTK_WIDGET (tv), filename); notebook_page_build (nb, GTK_WIDGET (tv), filename);
g_free (filename);
} }
} }
@ -123,6 +127,7 @@ notebook_page_new_with_file (GtkNotebook *nb, GFile *file) {
return; /* read error */ return; /* read error */
filename = g_file_get_basename (file); filename = g_file_get_basename (file);
notebook_page_build (nb, tv, filename); notebook_page_build (nb, tv, filename);
g_free (filename);
} }
void void
@ -136,5 +141,5 @@ notebook_page_new (GtkNotebook *nb) {
return; return;
filename = get_untitled (); filename = get_untitled ();
notebook_page_build (nb, tv, filename); notebook_page_build (nb, tv, filename);
g_free (filename);
} }

View file

@ -0,0 +1,9 @@
project ('test', 'c')
gtk4dep = dependency ('gtk4')
sourcefiles_open = ['test_open.c', '../tfetextview.c']
executable ('test_open', sourcefiles_open, dependencies: gtk4dep, install: false)
sourcefiles_save = ['test_save.c', '../tfetextview.c']
executable ('test_save', sourcefiles_save, dependencies: gtk4dep, install: false)

View file

@ -0,0 +1,76 @@
#include <gtk/gtk.h>
#include "../tfetextview.h"
#define APPLICATION_ID "com.github.ToshioCP.testopen"
static void
open_response_cb (TfeTextView *tv, int response) {
GFile *file = tfe_text_view_get_file (TFE_TEXT_VIEW (tv));
char *filename = file ? g_file_get_basename (file) : NULL;
g_printerr ("New file is %s.\n", filename);
switch (response) {
case TFE_OPEN_RESPONSE_SUCCESS:
g_printerr ("Response is SUCCESS.\n");
break;
case TFE_OPEN_RESPONSE_CANCEL:
g_printerr ("Response is CANCEL.\n");
break;
case TFE_OPEN_RESPONSE_ERROR:
g_printerr ("Response is ERROR.\n");
break;
default:
g_printerr ("Unexpected response.\n");
}
}
static void
change_file_cb (TfeTextView *tv, int response) {
g_printerr ("File is changed.\n");
}
/* ----- activate, open, startup handlers ----- */
static void
app_activate (GApplication *application) {
GtkApplication *app = GTK_APPLICATION (application);
GtkWidget *win = gtk_application_window_new (app);
GtkWidget *scr = gtk_scrolled_window_new ();
GtkWidget *tv = tfe_text_view_new ();
gtk_window_set_default_size (GTK_WINDOW (win), 800, 600);
gtk_window_set_child (GTK_WINDOW (win), scr);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);
// tfe_text_view_open
// It is assumed that FileChooserDialog works correctly.
// The user (1) selects a file and clicks on "open" or (2) clicks on "cancel".
// - (1) && (tv->file == NULL) => 1. buffer is set with file contents, 2. tv->file is updated,
// 3. response signal with SUCCESS, 4. file-change signal
// - (1) && (G_IS_FILE (tv->file)) => 1. buffer is set with file contents, 2. tv->file is updated,
// 3. response signal with SUCCESS, (4. file-change signal if file changed)
// - (2) => response signal with CANCEL
// The test program outputs logs with regards to above and the user confirms the behavior.
// The test doesn't assert.
gtk_widget_show (GTK_WIDGET (win));
g_signal_connect (TFE_TEXT_VIEW (tv), "open-response", G_CALLBACK (open_response_cb), NULL);
g_signal_connect (TFE_TEXT_VIEW (tv), "change-file", G_CALLBACK (change_file_cb), NULL);
// open twice
tfe_text_view_open (TFE_TEXT_VIEW (tv), GTK_WINDOW (win));
tfe_text_view_open (TFE_TEXT_VIEW (tv), GTK_WINDOW (win));
}
/* ----- main ----- */
int
main (int argc, char **argv) {
GtkApplication *app;
int stat;
app = gtk_application_new (APPLICATION_ID, G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
stat =g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app);
return stat;
}

View file

@ -0,0 +1,106 @@
#include <stdio.h>
#include <gtk/gtk.h>
#include "../tfetextview.h"
#define APPLICATION_ID "com.github.ToshioCP.testtfetextview"
// NOTICE!! -- Don't choose any existing file to save in FileChooserDialog (except 'yesy.txt')
// senario:
// setup: create test.txt
// create tv (TfeTextView) with test.txt => gtk_text_view_get_text => is the file correct?
// change the buffer with gtk_buffer_insert => save => check the file's contents
// saveas => is change-file signal emitted?
// teardown: remove test.txt
static GFile *
test_create_file (const char *filename, const char *sample) {
FILE *fp = fopen (filename, "w");
fprintf (fp, "%s", sample);
fclose (fp);
return g_file_new_for_path (filename);
}
static char *
test_read_file (const char *filename) {
FILE *fp = fopen (filename, "r");
int c;
int length;
char *s, *t;
// get file size (length)
for (length=0; (c = fgetc (fp)) != EOF; ++length)
;
t = s = g_new (char, length + 1);
fseek (fp, 0, SEEK_SET); // reset the file pointer
while ((c = fgetc (fp)) != EOF)
*t++ = (char) c;
*t = '\0';
fclose (fp);
return s;
}
static void
change_file_cb (TfeTextView *tv, int response) {
g_printerr ("File is changed.\n");
}
/* ----- activate, open, startup handlers ----- */
static void
app_activate (GApplication *application) {
GtkApplication *app = GTK_APPLICATION (application);
GtkWidget *win = gtk_application_window_new (app);
GtkWidget *scr = gtk_scrolled_window_new ();
GtkWidget *tv;
char *s;
gtk_window_set_default_size (GTK_WINDOW (win), 800, 600);
gtk_window_set_child (GTK_WINDOW (win), scr);
// test for tfe_text_view_new_with_file and tfe_text_view_get_file
GFile *file = test_create_file ("test.txt", "I wasn't the outdoor type in my childhood.\n");
tv = tfe_text_view_new_with_file (file);
g_object_ref_sink (tv);
GFile *file2 = tfe_text_view_get_file (TFE_TEXT_VIEW (tv));
if (! g_file_equal (file, file2)) {
g_printerr ("tfe_text_view_get_file didn't return the expected GFile.\n");
g_printerr ("Expected: %s, actual: %s", g_file_get_basename (file), g_file_get_basename(file2));
}
g_object_unref (file2);
// tfe_text_view_save
GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
GtkTextIter iter;
g_signal_connect (TFE_TEXT_VIEW (tv), "change-file", G_CALLBACK (change_file_cb), NULL);
gtk_text_buffer_get_end_iter (tb, &iter);
gtk_text_buffer_insert (tb, &iter, "My mom always got me to play outside.\n", -1);
tfe_text_view_save (TFE_TEXT_VIEW (tv));
s = test_read_file ("test.txt");
char *expected_s = "I wasn't the outdoor type in my childhood.\n"
"My mom always got me to play outside.\n";
if (strcmp (expected_s, s) != 0) {
g_printerr ("tfe_text_view_save didn't work.\n");
g_printerr ("Expected:\n%s\nactual:\n%s\n", expected_s, s);
}
g_free (s);
// tfe_text_view_saveas
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);
gtk_widget_show (win);
tfe_text_view_saveas (TFE_TEXT_VIEW (tv));
}
/* ----- main ----- */
int
main (int argc, char **argv) {
GtkApplication *app;
int stat;
app = gtk_application_new (APPLICATION_ID, G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
stat =g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app);
return stat;
}

View file

@ -73,24 +73,23 @@ static gboolean
save_file (GFile *file, GtkTextBuffer *tb, GtkWindow *win) { save_file (GFile *file, GtkTextBuffer *tb, GtkWindow *win) {
GtkTextIter start_iter; GtkTextIter start_iter;
GtkTextIter end_iter; GtkTextIter end_iter;
gchar *contents; char *contents;
gboolean stat; gboolean stat;
GtkWidget *message_dialog; GtkWidget *message_dialog;
GError *err = NULL; GError *err = NULL;
gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter); gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter);
contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE); contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE);
if (g_file_replace_contents (file, contents, strlen (contents), NULL, TRUE, G_FILE_CREATE_NONE, NULL, NULL, &err)) { stat = g_file_replace_contents (file, contents, strlen (contents), NULL, TRUE, G_FILE_CREATE_NONE, NULL, NULL, &err);
if (stat)
gtk_text_buffer_set_modified (tb, FALSE); gtk_text_buffer_set_modified (tb, FALSE);
stat = TRUE; else {
} else { // Because error message is displayed here, the caller of 'save_file' doesn't need to do anything about error.
message_dialog = gtk_message_dialog_new (win, GTK_DIALOG_MODAL, message_dialog = gtk_message_dialog_new (win, GTK_DIALOG_MODAL,
GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s.", err->message);
"%s.\n", err->message);
g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL); g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
gtk_widget_show (message_dialog); gtk_widget_show (message_dialog);
g_error_free (err); g_error_free (err);
stat = FALSE;
} }
g_free (contents); g_free (contents);
return stat; return stat;
@ -107,10 +106,17 @@ saveas_dialog_response (GtkWidget *dialog, gint response, TfeTextView *tv) {
if (! G_IS_FILE (file)) if (! G_IS_FILE (file))
g_warning ("TfeTextView: gtk_file_chooser_get_file returns non GFile.\n"); g_warning ("TfeTextView: gtk_file_chooser_get_file returns non GFile.\n");
else if (save_file(file, tb, GTK_WINDOW (win))) { else if (save_file(file, tb, GTK_WINDOW (win))) {
if (G_IS_FILE (tv->file)) // The following is complicated. The comments here will help your understanding
// G_IS_FILE(tv->file) && tv->file == file => nothing to do
// G_IS_FILE(tv->file) && tv->file != file => unref(tv->file), tv->file=file, signal emit
// tv->file==NULL => tv->file=file, signal emit
if (G_IS_FILE (tv->file) && (! g_file_equal (tv->file, file)))
g_object_unref (tv->file); g_object_unref (tv->file);
tv->file = file; if (! (G_IS_FILE (tv->file) && g_file_equal (tv->file, file))) {
g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0); tv->file = file; // The ownership of 'file' moves to TfeTextView.
g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
}
g_object_unref (file);
} else } else
g_object_unref (file); g_object_unref (file);
} }
@ -128,7 +134,7 @@ tfe_text_view_save (TfeTextView *tv) {
return; /* no need to save it */ return; /* no need to save it */
else if (tv->file == NULL) else if (tv->file == NULL)
tfe_text_view_saveas (tv); tfe_text_view_saveas (tv);
else if (! G_IS_FILE (tv->file)) else if (! G_IS_FILE (tv->file)) // Unexpected error
g_error ("TfeTextView: The pointer tv->file isn't NULL nor GFile.\n"); g_error ("TfeTextView: The pointer tv->file isn't NULL nor GFile.\n");
else else
save_file (tv->file, tb, GTK_WINDOW (win)); save_file (tv->file, tb, GTK_WINDOW (win));
@ -188,8 +194,7 @@ open_dialog_response(GtkWidget *dialog, gint response, TfeTextView *tv) {
} else if (! g_file_load_contents (file, NULL, &contents, &length, NULL, &err)) { /* read error */ } else if (! g_file_load_contents (file, NULL, &contents, &length, NULL, &err)) { /* read error */
g_object_unref (file); g_object_unref (file);
message_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog), GTK_DIALOG_MODAL, message_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog), GTK_DIALOG_MODAL,
GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s", err->message);
"%s.\n", err->message);
g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL); g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
gtk_widget_show (message_dialog); gtk_widget_show (message_dialog);
g_error_free (err); g_error_free (err);
@ -197,12 +202,18 @@ open_dialog_response(GtkWidget *dialog, gint response, TfeTextView *tv) {
} else { } else {
gtk_text_buffer_set_text (tb, contents, length); gtk_text_buffer_set_text (tb, contents, length);
g_free (contents); g_free (contents);
gtk_text_buffer_set_modified (tb, FALSE);
// G_IS_FILE(tv->file) && tv->file == file => unref(tv->file), tv->file=file, emit response with SUCCESS
// G_IS_FILE(tv->file) && tv->file != file => unref(tv->file), tv->file=file, emit response with SUCCESS, emit change-file
// tv->file==NULL => tv->file=file, emit response with SUCCESS, emit change-file
// The order is important. If you unref tv->file first, you can't compare tv->file and file anymore.
// And open-response signal is emitted after new tv->file is set. Or the handler can't catch the new file.
if (! (G_IS_FILE (tv->file) && g_file_equal (tv->file, file)))
g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
if (G_IS_FILE (tv->file)) if (G_IS_FILE (tv->file))
g_object_unref (tv->file); g_object_unref (tv->file);
tv->file = file; tv->file = file; // The ownership of 'file' moves to TfeTextView
gtk_text_buffer_set_modified (tb, FALSE);
g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_SUCCESS); g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_SUCCESS);
g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
} }
gtk_window_destroy (GTK_WINDOW (dialog)); gtk_window_destroy (GTK_WINDOW (dialog));
} }
@ -210,20 +221,19 @@ open_dialog_response(GtkWidget *dialog, gint response, TfeTextView *tv) {
void void
tfe_text_view_open (TfeTextView *tv, GtkWindow *win) { tfe_text_view_open (TfeTextView *tv, GtkWindow *win) {
g_return_if_fail (TFE_IS_TEXT_VIEW (tv)); g_return_if_fail (TFE_IS_TEXT_VIEW (tv));
g_return_if_fail (GTK_IS_WINDOW (win)); // 'win' is used for a transient window of the GtkFileChooserDialog.
// It can be NULL.
g_return_if_fail (GTK_IS_WINDOW (win) || win == NULL);
GtkWidget *dialog; GtkWidget *dialog;
dialog = gtk_file_chooser_dialog_new ("Open file", win, GTK_FILE_CHOOSER_ACTION_OPEN, dialog = gtk_file_chooser_dialog_new ("Open file", win, GTK_FILE_CHOOSER_ACTION_OPEN,
"Cancel", GTK_RESPONSE_CANCEL, "Cancel", GTK_RESPONSE_CANCEL, "Open", GTK_RESPONSE_ACCEPT, NULL);
"Open", GTK_RESPONSE_ACCEPT,
NULL);
g_signal_connect (dialog, "response", G_CALLBACK (open_dialog_response), tv); g_signal_connect (dialog, "response", G_CALLBACK (open_dialog_response), tv);
gtk_widget_show (dialog); gtk_widget_show (dialog);
} }
GtkWidget * GtkWidget *
tfe_text_view_new (void) { tfe_text_view_new (void) {
return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL)); return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, "wrap-mode", GTK_WRAP_WORD_CHAR, NULL));
} }

View file

@ -1,6 +1,6 @@
# TfeTextView API reference # TfeTextView API reference
TfeTextView -- Child object of GtkTextView. It holds GFile which the contents of GtkTextBuffer correponds to. TfeTextView -- Child object of GtkTextView. It is connected to a certain file.
## Functions ## Functions
- GFile *[tfe_text_view_get_file ()](#tfe_text_view_get_file) - GFile *[tfe_text_view_get_file ()](#tfe_text_view_get_file)
@ -39,8 +39,8 @@ GObject
## Description ## Description
TfeTextView holds GFile which the contents of GtkTextBuffer corresponds to. TfeTextView holds GFile corresponds to the contents of GtkTextBuffer.
File manipulation functions are added to this object. It has some file manipulation functions.
## Functions ## Functions
@ -67,7 +67,7 @@ tfe_text_view_open (TfeTextView *tv, GtkWidget *win);
Just shows a GtkFileChooserDialog so that a user can choose a file to read. Just shows a GtkFileChooserDialog so that a user can choose a file to read.
This function doesn't do any I/O operations. This function doesn't do any I/O operations.
They are done by the signal handler connected to the `response` signal emitted by GtkFileChooserDialog. They are done by the signal handler connected to the `response` signal emitted by GtkFileChooserDialog.
Therefore the caller can't know the I/O status directly from the function. Therefore the caller can't know the I/O status directly from `tfe_text_view_open`.
Instead, the status is informed by `open-response` signal. Instead, the status is informed by `open-response` signal.
The caller needs to set a handler to this signal in advance. The caller needs to set a handler to this signal in advance.
@ -83,9 +83,9 @@ void
tfe_text_view_save (TfeTextView *tv); tfe_text_view_save (TfeTextView *tv);
~~~ ~~~
Saves the contents of a TfeTextView to a file. Saves the contents of the TfeTextView to a file.
If `tv` holds a GFile, it is used. If `tv` holds a GFile, it is used.
Otherwise, this function shows GtkFileChosserDialog so that a user can choose a file to save. Otherwise, this function shows GtkFileChooserDialog so that the user can choose a file to save.
Parameters Parameters
@ -99,7 +99,7 @@ tfe_text_view_saveas (TfeTextView *tv);
~~~ ~~~
Saves the content of a TfeTextView to a file. Saves the content of a TfeTextView to a file.
This function shows GtkFileChosserDialog so that a user can choose a file to save. This function shows GtkFileChooserDialog so that a user can choose a file to save.
Parameters Parameters
@ -174,6 +174,13 @@ Members:
- TFE_OPEN_RESPONSE_CANCEL: Reading file is canceled by the user. - TFE_OPEN_RESPONSE_CANCEL: Reading file is canceled by the user.
- TFE_OPEN_RESPONSE_ERROR: An error happened during the opening or reading process. - TFE_OPEN_RESPONSE_ERROR: An error happened during the opening or reading process.
## Properties
### wrap-mode
The property "wrap-mode" belongs to GtkTextView.
TfeTextView inherits it and the value is set to GTK\_WRAP\_WORD\_CHAR as a default.
## Signals ## Signals
### change-file ### change-file

View file

@ -1,7 +1,7 @@
#include <gtk/gtk.h> #include <gtk/gtk.h>
static void static void
app_activate (GApplication *app, gpointer user_data) { app_activate (GApplication *app) {
GtkWidget *win; GtkWidget *win;
GtkWidget *tv; GtkWidget *tv;
GtkTextBuffer *tb; GtkTextBuffer *tb;
@ -15,7 +15,7 @@ app_activate (GApplication *app, gpointer user_data) {
"He cut it, then there was a small cute baby girl in it. " "He cut it, then there was a small cute baby girl in it. "
"The girl was shining faintly. " "The girl was shining faintly. "
"He thought this baby girl is a gift from Heaven and took her home.\n" "He thought this baby girl is a gift from Heaven and took her home.\n"
"His wife was surprized at his tale. " "His wife was surprized at his story. "
"They were very happy because they had no children. " "They were very happy because they had no children. "
; ;
win = gtk_application_window_new (GTK_APPLICATION (app)); win = gtk_application_window_new (GTK_APPLICATION (app));
@ -37,7 +37,7 @@ main (int argc, char **argv) {
GtkApplication *app; GtkApplication *app;
int stat; int stat;
app = gtk_application_new ("com.github.ToshioCP.tfv1", G_APPLICATION_FLAGS_NONE); app = gtk_application_new ("com.github.ToshioCP.tfv1", G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL); g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
stat = g_application_run (G_APPLICATION (app), argc, argv); stat = g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app); g_object_unref (app);

View file

@ -1,7 +1,7 @@
#include <gtk/gtk.h> #include <gtk/gtk.h>
static void static void
app_activate (GApplication *app, gpointer user_data) { app_activate (GApplication *app) {
GtkWidget *win; GtkWidget *win;
GtkWidget *scr; GtkWidget *scr;
GtkWidget *tv; GtkWidget *tv;
@ -16,7 +16,7 @@ app_activate (GApplication *app, gpointer user_data) {
"He cut it, then there was a small cute baby girl in it. " "He cut it, then there was a small cute baby girl in it. "
"The girl was shining faintly. " "The girl was shining faintly. "
"He thought this baby girl is a gift from Heaven and took her home.\n" "He thought this baby girl is a gift from Heaven and took her home.\n"
"His wife was surprized at his tale. " "His wife was surprized at his story. "
"They were very happy because they had no children. " "They were very happy because they had no children. "
; ;
win = gtk_application_window_new (GTK_APPLICATION (app)); win = gtk_application_window_new (GTK_APPLICATION (app));
@ -41,7 +41,7 @@ main (int argc, char **argv) {
GtkApplication *app; GtkApplication *app;
int stat; int stat;
app = gtk_application_new ("com.github.ToshioCP.tfv2", G_APPLICATION_FLAGS_NONE); app = gtk_application_new ("com.github.ToshioCP.tfv2", G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL); g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
stat = g_application_run (G_APPLICATION (app), argc, argv); stat = g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app); g_object_unref (app);

View file

@ -1,12 +1,12 @@
#include <gtk/gtk.h> #include <gtk/gtk.h>
static void static void
app_activate (GApplication *app, gpointer user_data) { app_activate (GApplication *app) {
g_print ("You need a filename argument.\n"); g_printerr ("You need a filename argument.\n");
} }
static void static void
app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer user_data) { app_open (GApplication *app, GFile ** files, int n_files, char *hint) {
GtkWidget *win; GtkWidget *win;
GtkWidget *scr; GtkWidget *scr;
GtkWidget *tv; GtkWidget *tv;
@ -37,9 +37,10 @@ app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer
gtk_widget_show (win); gtk_widget_show (win);
} else { } else {
if ((filename = g_file_get_path (files[0])) != NULL) { if ((filename = g_file_get_path (files[0])) != NULL) {
g_print ("No such file: %s.\n", filename); g_printerr ("No such file: %s.\n", filename);
g_free (filename); g_free (filename);
} } else
g_printerr ("File can't be opened.\n");
gtk_window_destroy (GTK_WINDOW (win)); gtk_window_destroy (GTK_WINDOW (win));
} }
} }

View file

@ -1,12 +1,12 @@
#include <gtk/gtk.h> #include <gtk/gtk.h>
static void static void
app_activate (GApplication *app, gpointer user_data) { app_activate (GApplication *app) {
g_print ("You need a filename argument.\n"); g_printerr ("You need a filename argument.\n");
} }
static void static void
app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer user_data) { app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint) {
GtkWidget *win; GtkWidget *win;
GtkWidget *nb; GtkWidget *nb;
GtkWidget *lab; GtkWidget *lab;
@ -21,8 +21,7 @@ app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer
win = gtk_application_window_new (GTK_APPLICATION (app)); win = gtk_application_window_new (GTK_APPLICATION (app));
gtk_window_set_title (GTK_WINDOW (win), "file viewer"); gtk_window_set_title (GTK_WINDOW (win), "file viewer");
gtk_window_set_default_size (GTK_WINDOW (win), 400, 300); gtk_window_set_default_size (GTK_WINDOW (win), 600, 400);
gtk_window_maximize (GTK_WINDOW (win));
nb = gtk_notebook_new (); nb = gtk_notebook_new ();
gtk_window_set_child (GTK_WINDOW (win), nb); gtk_window_set_child (GTK_WINDOW (win), nb);
@ -47,10 +46,10 @@ app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer
nbp = gtk_notebook_get_page (GTK_NOTEBOOK (nb), scr); nbp = gtk_notebook_get_page (GTK_NOTEBOOK (nb), scr);
g_object_set (nbp, "tab-expand", TRUE, NULL); g_object_set (nbp, "tab-expand", TRUE, NULL);
} else if ((filename = g_file_get_path (files[i])) != NULL) { } else if ((filename = g_file_get_path (files[i])) != NULL) {
g_print ("No such file: %s.\n", filename); g_printerr ("No such file: %s.\n", filename);
g_free (filename); g_free (filename);
} else } else
g_print ("No valid file is given\n"); g_printerr ("No valid file is given\n");
} }
if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb)) > 0) if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb)) > 0)
gtk_widget_show (win); gtk_widget_show (win);