Update section 1-16. Some bugs are fixed.
47
README.md
|
@ -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).
|
||||
It also describes GtkExpression.
|
||||
|
||||
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.
|
||||
The latest version of the tutorial is located at [GTK4-tutorial GitHub repository](https://github.com/ToshioCP/GTK4-tutorial).
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
These websites are newly opened lately (Aug/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 new website is in progress at present, so you might need to refer to the old version.
|
||||
These websites were opened in August of 2021.
|
||||
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/).
|
||||
|
||||
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
|
||||
|
||||
This tutorial is under development and unstable.
|
||||
Even though the codes of the examples have been tested on GTK 4 (version 4.0), bugs may still exist.
|
||||
This tutorial is still under development and unstable.
|
||||
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.
|
||||
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).
|
||||
When you make corrections, 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.
|
||||
You can post it to [GitHub issues](https://github.com/ToshioCP/GTK4-tutorial/issues).
|
||||
You can also post updated files to [pull request](https://github.com/ToshioCP/GTK4-tutorial/pulls).
|
||||
One thing you need to be careful is to correct the source files, which are under the 'src' directory.
|
||||
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.
|
||||
|
||||
#### 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 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
|
||||
|
||||
|
@ -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. [Widgets (1)](gfm/sec4.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. [Defining a child object](gfm/sec8.md)
|
||||
1. [The User Interface (UI) file and GtkBuilder](gfm/sec9.md)
|
||||
1. [Defining a final class](gfm/sec8.md)
|
||||
1. [GtkBuilder and UI file](gfm/sec9.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. [Functions in TfeTextView](gfm/sec13.md)
|
||||
1. [Functions in GtkNotebook](gfm/sec14.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. [Stateful action](gfm/sec18.md)
|
||||
1. [Ui file for menu and action entries](gfm/sec19.md)
|
||||
|
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 105 KiB |
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 13 KiB |
|
@ -116,44 +116,46 @@ editor <code>tfe</code> (Text File Editor).</li>
|
|||
GtkExpression.</li>
|
||||
</ul>
|
||||
<p>The latest version of the tutorial is located at <a
|
||||
href="https://github.com/ToshioCP/Gtk4-tutorial">Gtk4-tutorial GitHub
|
||||
repository</a>. You can read it from there directly without having to
|
||||
download anything.</p>
|
||||
href="https://github.com/ToshioCP/GTK4-tutorial">GTK4-tutorial GitHub
|
||||
repository</a>. You can read it directly without download.</p>
|
||||
<p>There’s 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>
|
||||
<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
|
||||
Developer Documentation Website</a> for further information.</p>
|
||||
<p>These websites are newly opened lately (Aug/2021). The old
|
||||
documentation is located at <a
|
||||
href="https://developer-old.gnome.org/gtk4/stable/">GTK Reference
|
||||
Manual</a> and <a href="https://developer-old.gnome.org/">GNOME
|
||||
Developer Center</a>. The new website is in progress at present, so you
|
||||
might need to refer to the old version.</p>
|
||||
<p>These websites were opened in August of 2021. The old documents are
|
||||
located at <a href="https://developer-old.gnome.org/gtk4/stable/">GTK
|
||||
Reference Manual</a> and <a
|
||||
href="https://developer-old.gnome.org/">GNOME Developer Center</a>.</p>
|
||||
<p>If you want to know about GObject and the type system, please refer
|
||||
to <a href="https://github.com/ToshioCP/Gobject-tutorial">GObject
|
||||
tutorial</a>. The GObject details are easy to understand and also
|
||||
necessary to know when writing GTK 4 programs.</p>
|
||||
tutorial</a>. GObject is the base of GTK 4, so it is important for
|
||||
developers to understand GObject for writing GTK 4 programs.</p>
|
||||
<h4 id="contribution">Contribution</h4>
|
||||
<p>This tutorial is under development and unstable. Even though the
|
||||
codes of the examples have been tested on GTK 4 (version 4.0), bugs may
|
||||
still exist. If you find any bugs, errors or mistakes in the tutorial
|
||||
and C examples, please let me know. You can post it to <a
|
||||
href="https://github.com/ToshioCP/Gtk4-tutorial/issues">GitHub
|
||||
issues</a>. You can also post corrected files as a commit to <a
|
||||
href="https://github.com/ToshioCP/Gtk4-tutorial/pulls">pull request</a>.
|
||||
When you make corrections, correct the source files, which are under the
|
||||
‘src’ directory, then run <code>rake</code> to create to create the
|
||||
output file. The GFM files under the ‘gfm’ directory are automatically
|
||||
updated.</p>
|
||||
<p>If you have a question, feel free to post it as an issue. All
|
||||
questions are helpful and will make this tutorial get better.</p>
|
||||
<h4 id="how-to-get-a-html-or-pdf-version">How to get a HTML or PDF
|
||||
version</h4>
|
||||
<p>If you want to get a HTML or PDF version, you can make them with
|
||||
<code>rake</code>, which is a ruby version of make. Type
|
||||
<p>This tutorial is still under development and unstable. 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. You can post it to <a
|
||||
href="https://github.com/ToshioCP/GTK4-tutorial/issues">GitHub
|
||||
issues</a>. You can also post updated files to <a
|
||||
href="https://github.com/ToshioCP/GTK4-tutorial/pulls">pull request</a>.
|
||||
One thing you need to be careful is to correct the source files, which
|
||||
are under the ‘src’ directory. Don’t modify the files under
|
||||
<code>gfm</code> or <code>html</code> directories. After modifying some
|
||||
source files (under <code>src</code> directory), run <code>rake</code>
|
||||
to create GFM (GitHub Flavoured Markdown) files or run
|
||||
<code>rake html</code> to create HTML files.</p>
|
||||
<p>If you have a question, feel free to post it to <code>issue</code>.
|
||||
All questions are helpful and will make this tutorial get better.</p>
|
||||
<h4 id="how-to-get-gtk-4-tutorial-with-html-or-pdf-format">How to get
|
||||
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.
|
||||
There is a documentation (“<a href="Readme_for_developers.html">How to
|
||||
build GTK 4 Tutorial</a>”) that describes how to make them.</p>
|
||||
There is a document (“<a href="Readme_for_developers.html">How to build
|
||||
GTK 4 Tutorial</a>”) for further information.</p>
|
||||
<h2 id="table-of-contents">Table of contents</h2>
|
||||
<ol type="1">
|
||||
<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="sec4.html">Widgets (1)</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="sec8.html">Defining a child object</a></li>
|
||||
<li><a href="sec9.html">The User Interface (UI) file and
|
||||
GtkBuilder</a></li>
|
||||
<li><a href="sec8.html">Defining a final class</a></li>
|
||||
<li><a href="sec9.html">GtkBuilder and UI file</a></li>
|
||||
<li><a href="sec10.html">Build system</a></li>
|
||||
<li><a href="sec11.html">Initialization and destruction of
|
||||
instances</a></li>
|
||||
<li><a href="sec11.html">Instance Initialization and
|
||||
destruction</a></li>
|
||||
<li><a href="sec12.html">Signals</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="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="sec18.html">Stateful action</a></li>
|
||||
<li><a href="sec19.html">Ui file for menu and action entries</a></li>
|
||||
|
|
|
@ -117,36 +117,37 @@ MacOS, with Vala, Python and so on. However, this tutorial describes
|
|||
only <em>C programs on Linux</em>.</p>
|
||||
<p>If you want to try the examples in the tutorial, you need:</p>
|
||||
<ul>
|
||||
<li>PC with Linux distribution like Ubuntu, Debian and so on.</li>
|
||||
<li>Gcc.</li>
|
||||
<li>GTK 4. The stable version of Gtk on Linux distributions is version
|
||||
three at present. You need to install GTK 4 to your computer. See <a
|
||||
href="sec3.html">Section 3</a> for the installation of GTK 4.</li>
|
||||
<li>PC with Linux distribution like Ubuntu or Debian.</li>
|
||||
<li>GCC.</li>
|
||||
<li>GTK 4. The stable version of GTK is 4.8.2 at present (13/Dec/2022),
|
||||
but older version (4.0 or higher) may work. See <a
|
||||
href="sec3.html">Section 3</a> for the installation for GTK 4.</li>
|
||||
</ul>
|
||||
<h3 id="ruby-and-rake-for-making-the-document">Ruby and rake for making
|
||||
the document</h3>
|
||||
<p>This repository includes Ruby programs. They are used to make
|
||||
Markdown files, HTML files, Latex files and a PDF file.</p>
|
||||
<p>This repository includes Ruby programs. They are used to make GFM
|
||||
(GitHub Flavoured Markdown) files, HTML files, Latex files and a PDF
|
||||
file.</p>
|
||||
<p>You need:</p>
|
||||
<ul>
|
||||
<li>Linux distribution like Ubuntu.</li>
|
||||
<li>Linux.</li>
|
||||
<li>Ruby programming language. There are two ways to install it. One is
|
||||
installing the distribution’s package. The other is using rbenv and
|
||||
ruby-build. If you want to use the latest version of ruby, use
|
||||
rbenv.</li>
|
||||
<li>Rake. It is a gem, which is a library written in Ruby. You can
|
||||
install it as a package of your distribution or use gem command.</li>
|
||||
<li>Rake. You don’t need to install it separately because it is a
|
||||
standard library of Ruby.</li>
|
||||
</ul>
|
||||
<h2 id="license">License</h2>
|
||||
<p>Copyright (C) 2020 ToshioCP (Toshio Sekiya)</p>
|
||||
<p>Gtk4-tutorial repository contains the tutorial document and software
|
||||
<p>Copyright (C) 2020-2022 ToshioCP (Toshio Sekiya)</p>
|
||||
<p>GTK4-tutorial repository contains tutorial documents and programs
|
||||
such as converters, generators and controllers. All of them make up the
|
||||
‘Gtk4-tutorial’ package. This package is simply called ‘Gtk4-tutorial’
|
||||
in the following description. ‘Gtk4-tutorial’ is free; you can
|
||||
‘GTK4-tutorial’ package. 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.</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
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the <a
|
||||
href="https://www.gnu.org/licenses/gpl-3.0.html">GNU General Public
|
||||
|
|
512
docs/sec10.html
|
@ -5,7 +5,7 @@
|
|||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
||||
<title>Gtk4 tutorial</title>
|
||||
<title>GTK 4 tutorial</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
|
@ -112,275 +112,373 @@
|
|||
</div>
|
||||
</nav>
|
||||
<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>
|
||||
<p>We’ve compiled a small editor so far. But Some bad signs are beginning to appear.</p>
|
||||
<h2 id="managing-big-source-files">Managing big source files</h2>
|
||||
<p>We’ve compiled a small editor so far. But Some bad signs are
|
||||
beginning to appear.</p>
|
||||
<ul>
|
||||
<li>We’ve had only one C source file and put everything into it. We need 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>
|
||||
<li>We’ve had only one C source file and put everything in it. We need
|
||||
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>
|
||||
<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>
|
||||
<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>
|
||||
<h2 id="divide-a-c-source-file-into-two-parts.">Divide a C source file
|
||||
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>
|
||||
<li><code>tfetextview.c</code> includes the definition and functions of 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>
|
||||
<li><code>tfetextview.c</code> includes the definition and functions of
|
||||
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>
|
||||
<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>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>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>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><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"><gtk/gtk.h></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"><gtk/gtk.h></span></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-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-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-9"><a href="#cb1-9"></a>GFile *</span>
|
||||
<span id="cb1-10"><a href="#cb1-10"></a>tfe_text_view_get_file (TfeTextView *tv);</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 <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-12"><a href="#cb1-12"></a>GtkWidget *</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-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="op">(</span><span class="dt">void</span><span class="op">);</span></span></code></pre></div>
|
||||
<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"><gtk/gtk.h></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"><gtk/gtk.h></span></span>
|
||||
<span id="cb2-2"><a href="#cb2-2"></a><span class="pp">#include </span><span class="im">"tfetextview.h"</span></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-5"><a href="#cb2-5"></a>{</span>
|
||||
<span id="cb2-6"><a href="#cb2-6"></a> GtkTextView parent;</span>
|
||||
<span id="cb2-7"><a href="#cb2-7"></a> GFile *file;</span>
|
||||
<span id="cb2-8"><a href="#cb2-8"></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 class="op">;</span></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 class="op">};</span></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-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-14"><a href="#cb2-14"></a>}</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 class="op">}</span></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-17"><a href="#cb2-17"></a>tfe_text_view_class_init (TfeTextViewClass *class) {</span>
|
||||
<span id="cb2-18"><a href="#cb2-18"></a>}</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 class="op">}</span></span>
|
||||
<span id="cb2-19"><a href="#cb2-19"></a></span>
|
||||
<span id="cb2-20"><a href="#cb2-20"></a><span class="dt">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-22"><a href="#cb2-22"></a> tv -> file = f;</span>
|
||||
<span id="cb2-23"><a href="#cb2-23"></a>}</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 <span class="op">-></span> file <span class="op">=</span> f<span class="op">;</span></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-25"><a href="#cb2-25"></a>GFile *</span>
|
||||
<span id="cb2-26"><a href="#cb2-26"></a>tfe_text_view_get_file (TfeTextView *tv) {</span>
|
||||
<span id="cb2-27"><a href="#cb2-27"></a> <span class="cf">return</span> tv -> file;</span>
|
||||
<span id="cb2-28"><a href="#cb2-28"></a>}</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 <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 <span class="op">-></span> file<span class="op">;</span></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-30"><a href="#cb2-30"></a>GtkWidget *</span>
|
||||
<span id="cb2-31"><a href="#cb2-31"></a>tfe_text_view_new (<span class="dt">void</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-33"><a href="#cb2-33"></a>}</span></code></pre></div>
|
||||
<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="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 <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 class="op">}</span></span></code></pre></div>
|
||||
<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"><gtk/gtk.h></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"><gtk/gtk.h></span></span>
|
||||
<span id="cb3-2"><a href="#cb3-2"></a><span class="pp">#include </span><span class="im">"tfetextview.h"</span></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-5"><a href="#cb3-5"></a>app_activate (GApplication *app, gpointer user_data) {</span>
|
||||
<span id="cb3-6"><a href="#cb3-6"></a> g_print (<span class="st">"You need a filename argument.</span><span class="sc">\n</span><span class="st">"</span>);</span>
|
||||
<span id="cb3-7"><a href="#cb3-7"></a>}</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="op">(</span><span class="st">"You need a filename argument.</span><span class="sc">\n</span><span class="st">"</span><span class="op">);</span></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-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-11"><a href="#cb3-11"></a> GtkWidget *win;</span>
|
||||
<span id="cb3-12"><a href="#cb3-12"></a> GtkWidget *nb;</span>
|
||||
<span id="cb3-13"><a href="#cb3-13"></a> GtkWidget *lab;</span>
|
||||
<span id="cb3-14"><a href="#cb3-14"></a> GtkNotebookPage *nbp;</span>
|
||||
<span id="cb3-15"><a href="#cb3-15"></a> GtkWidget *scr;</span>
|
||||
<span id="cb3-16"><a href="#cb3-16"></a> GtkWidget *tv;</span>
|
||||
<span id="cb3-17"><a href="#cb3-17"></a> GtkTextBuffer *tb;</span>
|
||||
<span id="cb3-18"><a href="#cb3-18"></a> <span class="dt">char</span> *contents;</span>
|
||||
<span id="cb3-19"><a href="#cb3-19"></a> gsize length;</span>
|
||||
<span id="cb3-20"><a href="#cb3-20"></a> <span class="dt">char</span> *filename;</span>
|
||||
<span id="cb3-21"><a href="#cb3-21"></a> <span class="dt">int</span> i;</span>
|
||||
<span id="cb3-22"><a href="#cb3-22"></a> GtkBuilder *build;</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 <span class="op">*</span>win<span class="op">;</span></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 <span class="op">*</span>lab<span class="op">;</span></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 <span class="op">*</span>scr<span class="op">;</span></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 <span class="op">*</span>tb<span class="op">;</span></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 class="op">;</span></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 class="op">;</span></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-24"><a href="#cb3-24"></a> build = gtk_builder_new_from_resource (<span class="st">"/com/github/ToshioCP/tfe3/tfe.ui"</span>);</span>
|
||||
<span id="cb3-25"><a href="#cb3-25"></a> win = GTK_WIDGET (gtk_builder_get_object (build, <span class="st">"win"</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-27"><a href="#cb3-27"></a> nb = GTK_WIDGET (gtk_builder_get_object (build, <span class="st">"nb"</span>));</span>
|
||||
<span id="cb3-28"><a href="#cb3-28"></a> g_object_unref (build);</span>
|
||||
<span id="cb3-29"><a href="#cb3-29"></a> <span class="cf">for</span> (i = <span class="dv">0</span>; i < n_files; i++) {</span>
|
||||
<span id="cb3-30"><a href="#cb3-30"></a> <span class="cf">if</span> (g_file_load_contents (files[i], NULL, &contents, &length, NULL, NULL)) {</span>
|
||||
<span id="cb3-31"><a href="#cb3-31"></a> scr = gtk_scrolled_window_new ();</span>
|
||||
<span id="cb3-32"><a href="#cb3-32"></a> tv = tfe_text_view_new ();</span>
|
||||
<span id="cb3-33"><a href="#cb3-33"></a> tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));</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-35"><a href="#cb3-35"></a> gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);</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">"/com/github/ToshioCP/tfe3/tfe.ui"</span><span class="op">);</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">"win"</span><span class="op">));</span></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 <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">"nb"</span><span class="op">));</span></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> <span class="op">(</span>i <span class="op">=</span> <span class="dv">0</span><span class="op">;</span> i <span class="op"><</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> <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">&</span>contents<span class="op">,</span> <span class="op">&</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 <span class="op">=</span> gtk_scrolled_window_new <span class="op">();</span></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 <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 <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 <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-37"><a href="#cb3-37"></a> tfe_text_view_set_file (TFE_TEXT_VIEW (tv), g_file_dup (files[i]));</span>
|
||||
<span id="cb3-38"><a href="#cb3-38"></a> gtk_text_buffer_set_text (tb, contents, length);</span>
|
||||
<span id="cb3-39"><a href="#cb3-39"></a> g_free (contents);</span>
|
||||
<span id="cb3-40"><a href="#cb3-40"></a> filename = g_file_get_basename (files[i]);</span>
|
||||
<span id="cb3-41"><a href="#cb3-41"></a> lab = gtk_label_new (filename);</span>
|
||||
<span id="cb3-42"><a href="#cb3-42"></a> gtk_notebook_append_page (GTK_NOTEBOOK (nb), scr, lab);</span>
|
||||
<span id="cb3-43"><a href="#cb3-43"></a> nbp = gtk_notebook_get_page (GTK_NOTEBOOK (nb), scr);</span>
|
||||
<span id="cb3-44"><a href="#cb3-44"></a> g_object_set (nbp, <span class="st">"tab-expand"</span>, TRUE, NULL);</span>
|
||||
<span id="cb3-45"><a href="#cb3-45"></a> g_free (filename);</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-47"><a href="#cb3-47"></a> g_print (<span class="st">"No such file: %s.</span><span class="sc">\n</span><span class="st">"</span>, filename);</span>
|
||||
<span id="cb3-48"><a href="#cb3-48"></a> g_free (filename);</span>
|
||||
<span id="cb3-49"><a href="#cb3-49"></a> } <span class="cf">else</span></span>
|
||||
<span id="cb3-50"><a href="#cb3-50"></a> g_print (<span class="st">"No valid file is given</span><span class="sc">\n</span><span class="st">"</span>);</span>
|
||||
<span id="cb3-51"><a href="#cb3-51"></a> }</span>
|
||||
<span id="cb3-52"><a href="#cb3-52"></a> <span class="cf">if</span> (gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb)) > <span class="dv">0</span>) {</span>
|
||||
<span id="cb3-53"><a href="#cb3-53"></a> gtk_widget_show (win);</span>
|
||||
<span id="cb3-54"><a href="#cb3-54"></a> } <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-56"><a href="#cb3-56"></a>}</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 <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 <span class="op">(</span>contents<span class="op">);</span></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 <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 <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 <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 <span class="op">(</span>nbp<span class="op">,</span> <span class="st">"tab-expand"</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 <span class="op">(</span>filename<span class="op">);</span></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="op">(</span><span class="st">"No such file: %s.</span><span class="sc">\n</span><span class="st">"</span><span class="op">,</span> filename<span class="op">);</span></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="op">}</span> <span class="cf">else</span></span>
|
||||
<span id="cb3-50"><a href="#cb3-50"></a> g_print <span class="op">(</span><span class="st">"No valid file is given</span><span class="sc">\n</span><span class="st">"</span><span class="op">);</span></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> <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">></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 <span class="op">(</span>win<span class="op">);</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 <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 class="op">}</span></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-59"><a href="#cb3-59"></a>main (<span class="dt">int</span> argc, <span class="dt">char</span> **argv) {</span>
|
||||
<span id="cb3-60"><a href="#cb3-60"></a> GtkApplication *app;</span>
|
||||
<span id="cb3-61"><a href="#cb3-61"></a> <span class="dt">int</span> stat;</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 <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 class="op">;</span></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">"com.github.ToshioCP.tfe"</span>, G_APPLICATION_HANDLES_OPEN);</span>
|
||||
<span id="cb3-64"><a href="#cb3-64"></a> g_signal_connect (app, <span class="st">"activate"</span>, G_CALLBACK (app_activate), NULL);</span>
|
||||
<span id="cb3-65"><a href="#cb3-65"></a> g_signal_connect (app, <span class="st">"open"</span>, G_CALLBACK (app_open), NULL);</span>
|
||||
<span id="cb3-66"><a href="#cb3-66"></a> stat =g_application_run (G_APPLICATION (app), argc, argv);</span>
|
||||
<span id="cb3-67"><a href="#cb3-67"></a> g_object_unref (app);</span>
|
||||
<span id="cb3-68"><a href="#cb3-68"></a> <span class="cf">return</span> stat;</span>
|
||||
<span id="cb3-69"><a href="#cb3-69"></a>}</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>
|
||||
<span id="cb3-63"><a href="#cb3-63"></a> app <span class="op">=</span> gtk_application_new <span class="op">(</span><span class="st">"com.github.ToshioCP.tfe"</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 <span class="op">(</span>app<span class="op">,</span> <span class="st">"activate"</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 <span class="op">(</span>app<span class="op">,</span> <span class="st">"open"</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 <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 <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 class="op">;</span></span>
|
||||
<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><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"><?xml</span> version="1.0" encoding="UTF-8"<span class="kw">?></span></span>
|
||||
<span id="cb4-2"><a href="#cb4-2"></a><span class="kw"><gresources></span></span>
|
||||
<span id="cb4-3"><a href="#cb4-3"></a> <span class="kw"><gresource</span><span class="ot"> prefix=</span><span class="st">"/com/github/ToshioCP/tfe3"</span><span class="kw">></span></span>
|
||||
<span id="cb4-4"><a href="#cb4-4"></a> <span class="kw"><file></span>tfe.ui<span class="kw"></file></span></span>
|
||||
<span id="cb4-5"><a href="#cb4-5"></a> <span class="kw"></gresource></span></span>
|
||||
<span id="cb4-6"><a href="#cb4-6"></a><span class="kw"></gresources></span></span></code></pre></div>
|
||||
<h2 id="make">Make</h2>
|
||||
<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>
|
||||
<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="fu"><?xml</span><span class="ot"> version=</span><span class="st">"1.0"</span><span class="ot"> encoding=</span><span class="st">"UTF-8"</span><span class="fu">?></span></span>
|
||||
<span id="cb4-2"><a href="#cb4-2"></a><<span class="kw">gresources</span>></span>
|
||||
<span id="cb4-3"><a href="#cb4-3"></a> <<span class="kw">gresource</span><span class="ot"> prefix=</span><span class="st">"/com/github/ToshioCP/tfe3"</span>></span>
|
||||
<span id="cb4-4"><a href="#cb4-4"></a> <<span class="kw">file</span>>tfe.ui</<span class="kw">file</span>></span>
|
||||
<span id="cb4-5"><a href="#cb4-5"></a> </<span class="kw">gresource</span>></span>
|
||||
<span id="cb4-6"><a href="#cb4-6"></a></<span class="kw">gresources</span>></span></code></pre></div>
|
||||
<p>Dividing a file makes it easy to maintain source files. But now we
|
||||
face a new problem. The building step increases.</p>
|
||||
<ul>
|
||||
<li>Compiling the ui file <code>tfe.ui</code> into <code>resources.c</code>.</li>
|
||||
<li>Compiling <code>tfe.c</code> into <code>tfe.o</code> (object 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>Compiling the ui file <code>tfe.ui</code> into
|
||||
<code>resources.c</code>.</li>
|
||||
<li>Compiling <code>tfe.c</code> into <code>tfe.o</code> (object
|
||||
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>
|
||||
</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>Make analyzes Makefile and executes compilers. All instructions are written in Makefile.</p>
|
||||
<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>
|
||||
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true"></a> gcc -o sample.o sample.c</span></code></pre></div>
|
||||
<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>Build tools manage the steps. I’ll show you three build tools, Meson
|
||||
and Ninja, Make and Rake. Meson and Ninja is recommended as a C build
|
||||
tool, but others are also fine. It’s your choice.</p>
|
||||
<h2 id="meson-and-ninja">Meson and Ninja</h2>
|
||||
<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('tfe', 'c')</span>
|
||||
<span id="cb5-2"><a href="#cb5-2"></a></span>
|
||||
<span id="cb5-3"><a href="#cb5-3"></a>gtkdep = dependency('gtk4')</span>
|
||||
<span id="cb5-4"><a href="#cb5-4"></a></span>
|
||||
<span id="cb5-5"><a href="#cb5-5"></a>gnome=import('gnome')</span>
|
||||
<span id="cb5-6"><a href="#cb5-6"></a>resources = gnome.compile_resources('resources','tfe.gresource.xml')</span>
|
||||
<span id="cb5-7"><a href="#cb5-7"></a></span>
|
||||
<span id="cb5-8"><a href="#cb5-8"></a>sourcefiles=files('tfe.c', 'tfetextview.c')</span>
|
||||
<span id="cb5-9"><a href="#cb5-9"></a></span>
|
||||
<span id="cb5-10"><a href="#cb5-10"></a>executable('tfe', sourcefiles, resources, dependencies: gtkdep)</span></code></pre></div>
|
||||
<ul>
|
||||
<li><code>sample.o</code> is called target.</li>
|
||||
<li><code>sample.c</code> is prerequisite.</li>
|
||||
<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 won’t work as you expected).</li>
|
||||
<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>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
|
||||
won’t work as you expected).</li>
|
||||
</ul>
|
||||
<p>The rule is:</p>
|
||||
<p>If a prerequisite modified later than a target, then make executes 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>If a prerequisite modified later than a target, then make executes
|
||||
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>
|
||||
<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>
|
||||
<span id="cb6-2"><a href="#cb6-2"></a></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="cb6-4"><a href="#cb6-4"></a> gcc -o tfe tfe.o tfetextview.o resources.o `pkg-config --libs gtk4`</span>
|
||||
<span id="cb6-5"><a href="#cb6-5"></a></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="cb6-7"><a href="#cb6-7"></a> gcc -c -o tfe.o `pkg-config --cflags gtk4` tfe.c</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="cb6-9"><a href="#cb6-9"></a> gcc -c -o tfetextview.o `pkg-config --cflags gtk4` tfetextview.c</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="cb6-11"><a href="#cb6-11"></a> gcc -c -o resources.o `pkg-config --cflags gtk4` resources.c</span>
|
||||
<span id="cb6-12"><a href="#cb6-12"></a></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="cb6-14"><a href="#cb6-14"></a> glib-compile-resources tfe.gresource.xml --target=resources.c --generate-source</span>
|
||||
<span id="cb6-15"><a href="#cb6-15"></a></span>
|
||||
<span id="cb6-16"><a href="#cb6-16"></a><span class="ot">.Phony:</span><span class="dt"> clean</span></span>
|
||||
<span id="cb6-17"><a href="#cb6-17"></a></span>
|
||||
<span id="cb6-18"><a href="#cb6-18"></a><span class="dv">clean:</span></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>
|
||||
<p>You only need to type <code>make</code>.</p>
|
||||
<div class="sourceCode" id="cb9"><pre
|
||||
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="cb9-2"><a href="#cb9-2"></a></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="cb9-4"><a href="#cb9-4"></a> gcc -o tfe tfe.o tfetextview.o resources.o `pkg-config --libs gtk4`</span>
|
||||
<span id="cb9-5"><a href="#cb9-5"></a></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="cb9-7"><a href="#cb9-7"></a> gcc -c -o tfe.o `pkg-config --cflags gtk4` tfe.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="cb9-9"><a href="#cb9-9"></a> gcc -c -o tfetextview.o `pkg-config --cflags gtk4` tfetextview.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="cb9-11"><a href="#cb9-11"></a> gcc -c -o resources.o `pkg-config --cflags gtk4` resources.c</span>
|
||||
<span id="cb9-12"><a href="#cb9-12"></a></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="cb9-14"><a href="#cb9-14"></a> glib-compile-resources tfe.gresource.xml --target=resources.c --generate-source</span>
|
||||
<span id="cb9-15"><a href="#cb9-15"></a></span>
|
||||
<span id="cb9-16"><a href="#cb9-16"></a><span class="ot">.Phony:</span><span class="dt"> clean</span></span>
|
||||
<span id="cb9-17"><a href="#cb9-17"></a></span>
|
||||
<span id="cb9-18"><a href="#cb9-18"></a><span class="dv">clean:</span></span>
|
||||
<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
|
||||
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`</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>
|
||||
<p>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.</p>
|
||||
<p>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.</p>
|
||||
<ul>
|
||||
<li>Rakefile controls the behavior of <code>rake</code>.</li>
|
||||
<li>You can write any Ruby code in Rakefile.</li>
|
||||
</ul>
|
||||
<p>Rake has task and file task, which is similar to target, prerequisite and recipe in make.</p>
|
||||
<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">'rake/clean'</span></span>
|
||||
<span id="cb8-2"><a href="#cb8-2"></a></span>
|
||||
<span id="cb8-3"><a href="#cb8-3"></a>targetfile = <span class="st">"tfe"</span></span>
|
||||
<span id="cb8-4"><a href="#cb8-4"></a>srcfiles = <span class="dt">FileList</span>[<span class="st">"tfe.c"</span>, <span class="st">"tfetextview.c"</span>, <span class="st">"resources.c"</span>]</span>
|
||||
<span id="cb8-5"><a href="#cb8-5"></a>rscfile = srcfiles[<span class="dv">2</span>]</span>
|
||||
<span id="cb8-6"><a href="#cb8-6"></a>objfiles = srcfiles.gsub(<span class="ot">/.c$/</span>, <span class="st">'.o'</span>)</span>
|
||||
<span id="cb8-7"><a href="#cb8-7"></a></span>
|
||||
<span id="cb8-8"><a href="#cb8-8"></a><span class="dt">CLEAN</span>.include(targetfile, objfiles, rscfile)</span>
|
||||
<span id="cb8-9"><a href="#cb8-9"></a></span>
|
||||
<span id="cb8-10"><a href="#cb8-10"></a>task <span class="st">default: </span>targetfile</span>
|
||||
<span id="cb8-11"><a href="#cb8-11"></a></span>
|
||||
<span id="cb8-12"><a href="#cb8-12"></a>file targetfile => objfiles <span class="kw">do</span> |t|</span>
|
||||
<span id="cb8-13"><a href="#cb8-13"></a> sh <span class="st">"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">' '</span>)<span class="ot">}</span><span class="st"> `pkg-config --libs gtk4`"</span></span>
|
||||
<span id="cb8-14"><a href="#cb8-14"></a><span class="kw">end</span></span>
|
||||
<span id="cb8-15"><a href="#cb8-15"></a></span>
|
||||
<span id="cb8-16"><a href="#cb8-16"></a>objfiles.each <span class="kw">do</span> |obj|</span>
|
||||
<span id="cb8-17"><a href="#cb8-17"></a> src = obj.gsub(<span class="ot">/.o$/</span>,<span class="st">'.c'</span>)</span>
|
||||
<span id="cb8-18"><a href="#cb8-18"></a> file obj => src <span class="kw">do</span> |t|</span>
|
||||
<span id="cb8-19"><a href="#cb8-19"></a> sh <span class="st">"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">"</span></span>
|
||||
<span id="cb8-20"><a href="#cb8-20"></a> <span class="kw">end</span></span>
|
||||
<span id="cb8-21"><a href="#cb8-21"></a><span class="kw">end</span></span>
|
||||
<span id="cb8-22"><a href="#cb8-22"></a></span>
|
||||
<span id="cb8-23"><a href="#cb8-23"></a>file rscfile => [<span class="st">"tfe.gresource.xml"</span>, <span class="st">"tfe.ui"</span>] <span class="kw">do</span> |t|</span>
|
||||
<span id="cb8-24"><a href="#cb8-24"></a> sh <span class="st">"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"</span></span>
|
||||
<span id="cb8-25"><a href="#cb8-25"></a><span class="kw">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>
|
||||
<p>Rake has task and file task, which is similar to target, prerequisite
|
||||
and recipe in make.</p>
|
||||
<div class="sourceCode" id="cb11"><pre
|
||||
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">'rake/clean'</span></span>
|
||||
<span id="cb11-2"><a href="#cb11-2"></a></span>
|
||||
<span id="cb11-3"><a href="#cb11-3"></a>targetfile <span class="kw">=</span> <span class="st">"tfe"</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">"tfe.c"</span>, <span class="st">"tfetextview.c"</span>, <span class="st">"resources.c"</span><span class="kw">]</span></span>
|
||||
<span id="cb11-5"><a href="#cb11-5"></a>uifile <span class="kw">=</span> <span class="st">"tfe.ui"</span></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="cb11-7"><a href="#cb11-7"></a>objfiles <span class="kw">=</span> srcfiles<span class="at">.ext</span>(<span class="st">".o"</span>)</span>
|
||||
<span id="cb11-8"><a href="#cb11-8"></a>gresource_xml <span class="kw">=</span> <span class="st">"tfe.gresource.xml"</span></span>
|
||||
<span id="cb11-9"><a href="#cb11-9"></a></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="cb11-11"><a href="#cb11-11"></a></span>
|
||||
<span id="cb11-12"><a href="#cb11-12"></a>task <span class="wa">default: </span>targetfile</span>
|
||||
<span id="cb11-13"><a href="#cb11-13"></a></span>
|
||||
<span id="cb11-14"><a href="#cb11-14"></a>file targetfile <span class="kw">=></span> objfiles <span class="cf">do</span> <span class="kw">|</span>t<span class="kw">|</span></span>
|
||||
<span id="cb11-15"><a href="#cb11-15"></a> sh <span class="st">"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">' '</span>)<span class="sc">}</span><span class="st"> `pkg-config --libs gtk4`"</span></span>
|
||||
<span id="cb11-16"><a href="#cb11-16"></a><span class="cf">end</span></span>
|
||||
<span id="cb11-17"><a href="#cb11-17"></a></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="cb11-19"><a href="#cb11-19"></a> src <span class="kw">=</span> obj<span class="at">.ext</span>(<span class="st">".c"</span>)</span>
|
||||
<span id="cb11-20"><a href="#cb11-20"></a> file obj <span class="kw">=></span> src <span class="cf">do</span> <span class="kw">|</span>t<span class="kw">|</span></span>
|
||||
<span id="cb11-21"><a href="#cb11-21"></a> sh <span class="st">"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">"</span></span>
|
||||
<span id="cb11-22"><a href="#cb11-22"></a> <span class="cf">end</span></span>
|
||||
<span id="cb11-23"><a href="#cb11-23"></a><span class="cf">end</span></span>
|
||||
<span id="cb11-24"><a href="#cb11-24"></a></span>
|
||||
<span id="cb11-25"><a href="#cb11-25"></a>file rscfile <span class="kw">=></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">"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"</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>
|
||||
<li>3-6: Defines target file, source file 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>10: The default target depends on <code>targetfile</code>. The task <code>default</code> is the final goal of tasks.</li>
|
||||
<li>12-14: <code>targetfile</code> depends on <code>objfiles</code>. The variable <code>t</code> is a task object.
|
||||
<li>3-8: Defines target file, source files and so on.</li>
|
||||
<li>1, 10 Requires rake/clean library. And clean files are added to
|
||||
CLEAN. The files included by CLEAN will be removed when
|
||||
<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>
|
||||
<li><code>t.name</code> is a target name</li>
|
||||
<li><code>t.prerequisites</code> is an array of prerequisites.</li>
|
||||
<li><code>t.source</code> is the first element of prerequisites.</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>16-21: There’s a loop by each element of the array of <code>objfiles</code>. Each object depends on corresponding source file.</li>
|
||||
<li>23-25: Resource file depends on xml file and ui file.</li>
|
||||
<li><code>sh</code> is a method to give the following string to shell as
|
||||
an argument and executes the shell.</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>
|
||||
<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>
|
||||
<h2 id="meson-and-ninja">Meson and ninja</h2>
|
||||
<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>
|
||||
<p>To use meson, you first need to write <code>meson.build</code> file.</p>
|
||||
<div class="sourceCode" id="cb9"><pre class="sourceCode numberSource numberLines"><code class="sourceCode"><span id="cb9-1"><a href="#cb9-1"></a>project('tfe', 'c')</span>
|
||||
<span id="cb9-2"><a href="#cb9-2"></a></span>
|
||||
<span id="cb9-3"><a href="#cb9-3"></a>gtkdep = dependency('gtk4')</span>
|
||||
<span id="cb9-4"><a href="#cb9-4"></a></span>
|
||||
<span id="cb9-5"><a href="#cb9-5"></a>gnome=import('gnome')</span>
|
||||
<span id="cb9-6"><a href="#cb9-6"></a>resources = gnome.compile_resources('resources','tfe.gresource.xml')</span>
|
||||
<span id="cb9-7"><a href="#cb9-7"></a></span>
|
||||
<span id="cb9-8"><a href="#cb9-8"></a>sourcefiles=files('tfe.c', 'tfetextview.c')</span>
|
||||
<span id="cb9-9"><a href="#cb9-9"></a></span>
|
||||
<span id="cb9-10"><a href="#cb9-10"></a>executable('tfe', 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>I’ve 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>
|
||||
<p>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.</p>
|
||||
<p>For further information, see <a
|
||||
href="https://toshiocp.github.io/Rake-tutorial-for-beginners-en/LearningRake.html">Rake
|
||||
tutorial for beginners</a>.</p>
|
||||
</div>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
|
||||
</body>
|
||||
|
|
678
docs/sec11.html
|
@ -5,7 +5,7 @@
|
|||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
||||
<title>Gtk4 tutorial</title>
|
||||
<title>GTK 4 tutorial</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
|
@ -111,319 +111,503 @@
|
|||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<h1 id="initialization-and-destruction-of-instances">Initialization and destruction of instances</h1>
|
||||
<p>A new version of the text file editor (<code>tfe</code>) will be made in this section and the following four sections. It is <code>tfe5</code>. There are many changes from the prior version. All the sources are listed in <a href="sec16.html">Section 16</a>. They are located in two directories, src/tfe5 and src/tfetextview.</p>
|
||||
<h1 id="instance-initialization-and-destruction">Instance Initialization
|
||||
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>
|
||||
<p>We’ve divided C source file into two parts. But it is not enough in terms of encapsulation.</p>
|
||||
<p>We’ve divided C source file into two parts. But it is not enough in
|
||||
terms of encapsulation.</p>
|
||||
<ul>
|
||||
<li><code>tfe.c</code> includes everything other than TfeTextView. It should be divided at least into two parts, <code>tfeapplication.c</code> and <code>tfenotebook.c</code>.</li>
|
||||
<li><code>tfe.c</code> includes everything other than TfeTextView. It
|
||||
should be divided at least into two parts, <code>tfeapplication.c</code>
|
||||
and <code>tfenotebook.c</code>.</li>
|
||||
<li>Header files also need to be organized.</li>
|
||||
</ul>
|
||||
<p>However, first of all, I’d 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, I’d like to focus on the object TfeTextView.
|
||||
It is a child object of GtkTextView and has a new member
|
||||
<code>file</code> in it. The important thing is to manage the Gfile
|
||||
object pointed by <code>file</code>.</p>
|
||||
<ul>
|
||||
<li>What is necessary to GFile when creating (or initializing) TfeTextView?</li>
|
||||
<li>What is necessary to GFile when creating (or initializing)
|
||||
TfeTextView?</li>
|
||||
<li>What is necessary to GFile when destructing TfeTextView?</li>
|
||||
<li>TfeTextView should read/write a file by itself or not?</li>
|
||||
<li>How it communicates with objects outside?</li>
|
||||
</ul>
|
||||
<p>You need to know at least class, instance and signals before thinking about them. I will explain them in this section and the next section. After that I will explain:</p>
|
||||
<p>You need to know at least class, instance and signals before thinking
|
||||
about them. I will explain them in this section and the next section.
|
||||
After that I will explain:</p>
|
||||
<ul>
|
||||
<li>Organizing functions.</li>
|
||||
<li>How to use GtkFileChooserDialog</li>
|
||||
</ul>
|
||||
<h2 id="gobject-and-its-children">GObject and its children</h2>
|
||||
<p>GObject and its children are objects, which have both class and instance. First, think about instance of objects. Instance is structured memories. The structure is described as C language structure. The following is a structure of TfeTextView.</p>
|
||||
<div class="sourceCode" id="cb1"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true"></a><span class="co">/* This typedef statement is automatically generated by the macro G_DECLARE_FINAL_TYPE */</span></span>
|
||||
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true"></a><span class="kw">typedef</span> <span class="kw">struct</span> _TfeTextView TfeTextView;</span>
|
||||
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true"></a></span>
|
||||
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true"></a><span class="kw">struct</span> _TfeTextView {</span>
|
||||
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true"></a> GtkTextView parent;</span>
|
||||
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true"></a> GFile *file;</span>
|
||||
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true"></a>};</span></code></pre></div>
|
||||
<p>GObject and its children are objects, which have both class and
|
||||
object C structures. First, think about instances. An instance is
|
||||
memories which has the object structure. The following is the structure
|
||||
of TfeTextView.</p>
|
||||
<div class="sourceCode" id="cb1"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="co">/* This typedef statement is automatically generated by the macro G_DECLARE_FINAL_TYPE */</span></span>
|
||||
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="kw">typedef</span> <span class="kw">struct</span> _TfeTextView TfeTextView<span class="op">;</span></span>
|
||||
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<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>
|
||||
<ul>
|
||||
<li>The type of <code>parent</code> is GtkTextView which is C structure. It is declared in <code>gtktextview.h</code>. GtkTextView is the parent of TfeTextView.</li>
|
||||
<li><code>file</code> is a pointer to GFile. It can be NULL if no file corresponds to the TfeTextView object.</li>
|
||||
<li>The type of <code>parent</code> is GtkTextView which is C structure.
|
||||
It is declared in <code>gtktextview.h</code>. GtkTextView is the parent
|
||||
of TfeTextView.</li>
|
||||
<li><code>file</code> is a pointer to a GFile. It can be NULL if no file
|
||||
corresponds to the TfeTextView instance.</li>
|
||||
</ul>
|
||||
<p>Notice the program above is the declaration of the structure, not the definition. So, no memories are allocated at this moment. They are to be allocated when <code>tfe_text_view_new</code> function is invoked. The memory allocated with <code>tfe_text_view_new</code> is an instance of TfeTextView object. Therefore, There can be multiple TfeTextView instances if <code>tfe_text_view_new</code> is called multiple times.</p>
|
||||
<p>You can find the declaration of the ancestors of TfeTextView in the source files of GTK and GLib. The following is extracts from the source files (not exactly the same).</p>
|
||||
<div class="sourceCode" id="cb2"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true"></a><span class="kw">typedef</span> <span class="kw">struct</span> _GObject GObject;</span>
|
||||
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true"></a><span class="kw">typedef</span> <span class="kw">struct</span> _GObject GInitiallyUnowned;</span>
|
||||
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true"></a><span class="kw">struct</span> _GObject</span>
|
||||
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true"></a>{</span>
|
||||
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true"></a> GTypeInstance g_type_instance;</span>
|
||||
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true"></a> <span class="dt">volatile</span> guint ref_count;</span>
|
||||
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true"></a> GData *qdata;</span>
|
||||
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true"></a>};</span>
|
||||
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true"></a></span>
|
||||
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true"></a><span class="kw">typedef</span> <span class="kw">struct</span> _GtkWidget GtkWidget;</span>
|
||||
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true"></a><span class="kw">struct</span> _GtkWidget</span>
|
||||
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true"></a>{</span>
|
||||
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true"></a> GInitiallyUnowned parent_instance;</span>
|
||||
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true"></a> GtkWidgetPrivate *priv;</span>
|
||||
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true"></a>};</span>
|
||||
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true"></a></span>
|
||||
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true"></a><span class="kw">typedef</span> <span class="kw">struct</span> _GtkTextView GtkTextView;</span>
|
||||
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true"></a><span class="kw">struct</span> _GtkTextView</span>
|
||||
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true"></a>{</span>
|
||||
<span id="cb2-20"><a href="#cb2-20" aria-hidden="true"></a> GtkWidget parent_instance;</span>
|
||||
<span id="cb2-21"><a href="#cb2-21" aria-hidden="true"></a> GtkTextViewPrivate *priv;</span>
|
||||
<span id="cb2-22"><a href="#cb2-22" aria-hidden="true"></a>};</span></code></pre></div>
|
||||
<p>In each structure, its parent is declared at the top of the members. So, every ancestors is included in the child instance. This is very important. It guarantees a child widget to inherit all the features from ancestors. The structure of <code>TfeTextView</code> is like the following diagram.</p>
|
||||
<p>You can find the declaration of the ancestors’ object structures in
|
||||
the source files of GTK and GLib. The following is extracted from the
|
||||
source files (not exactly the same).</p>
|
||||
<div class="sourceCode" id="cb2"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">typedef</span> <span class="kw">struct</span> _GObject GObject<span class="op">;</span></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-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> _GObject</span>
|
||||
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="op">{</span></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-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-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-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a><span class="op">};</span></span>
|
||||
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a></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-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> _GtkWidget</span>
|
||||
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a><span class="op">{</span></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-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-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a><span class="op">};</span></span>
|
||||
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a></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-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> _GtkTextView</span>
|
||||
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true" tabindex="-1"></a><span class="op">{</span></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-21"><a href="#cb2-21" aria-hidden="true" tabindex="-1"></a> GtkTextViewPrivate <span class="op">*</span>priv<span class="op">;</span></span>
|
||||
<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>
|
||||
<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>
|
||||
<h2 id="initialization-of-a-tfetextview-instance">Initialization of a TfeTextView instance</h2>
|
||||
<p>The function <code>tfe_text_view_new</code> creates a new TfeTextView instance.</p>
|
||||
<div class="sourceCode" id="cb3"><pre class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1"></a>GtkWidget *</span>
|
||||
<span id="cb3-2"><a href="#cb3-2"></a>tfe_text_view_new (<span class="dt">void</span>) {</span>
|
||||
<span id="cb3-3"><a href="#cb3-3"></a> <span class="cf">return</span> GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL));</span>
|
||||
<span id="cb3-4"><a href="#cb3-4"></a>}</span></code></pre></div>
|
||||
<p>When this function is invoked, a TfeTextView instance is created and initialized. The initialization process is as follows.</p>
|
||||
<ol type="1">
|
||||
<li>Initializes GObject (GInitiallyUnowned) part in TfeTextView instance.</li>
|
||||
<li>Initializes GtkWidget part in TfeTextView instance.</li>
|
||||
<li>Initializes GtkTextView part in TfeTextView instance.</li>
|
||||
<li>Initializes TfeTextView part in TfeTextView instance.</li>
|
||||
</ol>
|
||||
<p>The step one through three is done by <code>g_object_init</code>, <code>gtk_widget_init</code> and <code>gtk_text_view_init</code>. They are called by the system automatically and you don’t 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->file = NULL;</span>
|
||||
<span id="cb4-4"><a href="#cb4-4"></a>}</span></code></pre></div>
|
||||
<p>This function just initializes <code>tv->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>You’ve 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">/*< private >*/</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">/*< public >*/</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">/*< private >*/</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>I’d like to explain some of the members. There’s 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>
|
||||
<p>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.</p>
|
||||
<p>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 <code>tfe_text_view_new</code> 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.</p>
|
||||
<ul>
|
||||
<li>11: A function pointed by <code>constructor</code> is called when the instance is generated. It completes the initialization of the instance.</li>
|
||||
<li>23: A function pointed by <code>dispose</code> is called when the instance destructs itself. Destruction process is divided into two phases. The first one is called disposing. In this phase, the instance releases all the references to other instances. The second phase is finalizing.</li>
|
||||
<li>24: A function pointed by <code>finalize</code> finishes the destruction process.</li>
|
||||
<li>The other pointers point to functions which are called while the instance lives.</li>
|
||||
<li>The instance (C structure).</li>
|
||||
<li>GtkWidgetPrivate structure.</li>
|
||||
<li>GtkTextViewPrivate structure.</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>
|
||||
<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 parent’s 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">"wrap-mode"</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 don’t 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">-></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->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 doesn’t define object’s behavior. We need
|
||||
at least two things. One is functions and the other is class
|
||||
methods.</p>
|
||||
<p>You’ve 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">/*< private >*/</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">/*< public >*/</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">/*< private >*/</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>There’s 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>
|
||||
<p>TfeTextView class is a structure and it includes all its ancestors’ class in it.</p>
|
||||
<div class="sourceCode" id="cb8"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true"></a><span class="kw">typedef</span> _TfeTextView TfeTextView;</span>
|
||||
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true"></a><span class="kw">struct</span> _TfeTextView {</span>
|
||||
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true"></a> GtkTextView parent;</span>
|
||||
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true"></a> GFile *file;</span>
|
||||
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true"></a>};</span></code></pre></div>
|
||||
<p>TfeTextView structure has GtkTextView type as the first member. In the same way, GtkTextView has its parent type (GtkWidget) as the first member. GtkWidget has its parent type (GtkInitiallyUnowned) as the first member. The structure of GtkInitiallyUnowned is the same as GObject. Therefore, TFeTextView includes GObject, GtkWidget and GtkTextView in itself.</p>
|
||||
<pre><code>GObject -- GInitiallyUnowned -- GtkWidget -- GtkTextView -- TfeTextView</code></pre>
|
||||
<p>The following is extracts from the source files (not exactly the same).</p>
|
||||
<div class="sourceCode" id="cb10"><pre class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb10-1"><a href="#cb10-1"></a><span class="kw">struct</span> _GtkWidgetClass</span>
|
||||
<span id="cb10-2"><a href="#cb10-2"></a>{</span>
|
||||
<span id="cb10-3"><a href="#cb10-3"></a> GInitiallyUnownedClass parent_class;</span>
|
||||
<p>TfeTextView class is a structure and it includes all its ancestors’
|
||||
classes in it. Therefore, classes have similar hierarchy to
|
||||
instances.</p>
|
||||
<pre><code>GObjectClass (GInitiallyUnownedClass) -- GtkWidgetClass -- GtkTextViewClass -- TfeTextViewClass</code></pre>
|
||||
<p>The following is extracted from the source codes (not exactly the
|
||||
same).</p>
|
||||
<div class="sourceCode" id="cb10"><pre
|
||||
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb10-1"><a href="#cb10-1"></a><span class="kw">struct</span> _GtkWidgetClass</span>
|
||||
<span id="cb10-2"><a href="#cb10-2"></a><span class="op">{</span></span>
|
||||
<span id="cb10-3"><a href="#cb10-3"></a> GInitiallyUnownedClass parent_class<span class="op">;</span></span>
|
||||
<span id="cb10-4"><a href="#cb10-4"></a></span>
|
||||
<span id="cb10-5"><a href="#cb10-5"></a> <span class="co">/*< public >*/</span></span>
|
||||
<span id="cb10-6"><a href="#cb10-6"></a></span>
|
||||
<span id="cb10-7"><a href="#cb10-7"></a> <span class="co">/* basics */</span></span>
|
||||
<span id="cb10-8"><a href="#cb10-8"></a> <span class="dt">void</span> (* show) (GtkWidget *widget);</span>
|
||||
<span id="cb10-9"><a href="#cb10-9"></a> <span class="dt">void</span> (* hide) (GtkWidget *widget);</span>
|
||||
<span id="cb10-10"><a href="#cb10-10"></a> <span class="dt">void</span> (* map) (GtkWidget *widget);</span>
|
||||
<span id="cb10-11"><a href="#cb10-11"></a> <span class="dt">void</span> (* unmap) (GtkWidget *widget);</span>
|
||||
<span id="cb10-12"><a href="#cb10-12"></a> <span class="dt">void</span> (* realize) (GtkWidget *widget);</span>
|
||||
<span id="cb10-13"><a href="#cb10-13"></a> <span class="dt">void</span> (* unrealize) (GtkWidget *widget);</span>
|
||||
<span id="cb10-14"><a href="#cb10-14"></a> <span class="dt">void</span> (* root) (GtkWidget *widget);</span>
|
||||
<span id="cb10-15"><a href="#cb10-15"></a> <span class="dt">void</span> (* unroot) (GtkWidget *widget);</span>
|
||||
<span id="cb10-16"><a href="#cb10-16"></a> <span class="dt">void</span> (* size_allocate) (GtkWidget *widget,</span>
|
||||
<span id="cb10-17"><a href="#cb10-17"></a> <span class="dt">int</span> width,</span>
|
||||
<span id="cb10-18"><a href="#cb10-18"></a> <span class="dt">int</span> height,</span>
|
||||
<span id="cb10-19"><a href="#cb10-19"></a> <span class="dt">int</span> baseline);</span>
|
||||
<span id="cb10-20"><a href="#cb10-20"></a> <span class="dt">void</span> (* state_flags_changed) (GtkWidget *widget,</span>
|
||||
<span id="cb10-21"><a href="#cb10-21"></a> GtkStateFlags previous_state_flags);</span>
|
||||
<span id="cb10-22"><a href="#cb10-22"></a> <span class="dt">void</span> (* direction_changed) (GtkWidget *widget,</span>
|
||||
<span id="cb10-23"><a href="#cb10-23"></a> GtkTextDirection previous_direction);</span>
|
||||
<span id="cb10-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> <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> <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> <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> <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> <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> <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> <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> <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 class="op">,</span></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 class="op">);</span></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 class="op">);</span></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 class="op">);</span></span>
|
||||
<span id="cb10-24"><a href="#cb10-24"></a></span>
|
||||
<span id="cb10-25"><a href="#cb10-25"></a> <span class="co">/* size requests */</span></span>
|
||||
<span id="cb10-26"><a href="#cb10-26"></a> GtkSizeRequestMode (* get_request_mode) (GtkWidget *widget);</span>
|
||||
<span id="cb10-27"><a href="#cb10-27"></a> <span class="dt">void</span> (* measure) (GtkWidget *widget,</span>
|
||||
<span id="cb10-28"><a href="#cb10-28"></a> GtkOrientation orientation,</span>
|
||||
<span id="cb10-29"><a href="#cb10-29"></a> <span class="dt">int</span> for_size,</span>
|
||||
<span id="cb10-30"><a href="#cb10-30"></a> <span class="dt">int</span> *minimum,</span>
|
||||
<span id="cb10-31"><a href="#cb10-31"></a> <span class="dt">int</span> *natural,</span>
|
||||
<span id="cb10-32"><a href="#cb10-32"></a> <span class="dt">int</span> *minimum_baseline,</span>
|
||||
<span id="cb10-33"><a href="#cb10-33"></a> <span class="dt">int</span> *natural_baseline);</span>
|
||||
<span id="cb10-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> <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 class="op">,</span></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> <span class="op">*</span>minimum<span class="op">,</span></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> <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> <span class="op">*</span>natural_baseline<span class="op">);</span></span>
|
||||
<span id="cb10-34"><a href="#cb10-34"></a></span>
|
||||
<span id="cb10-35"><a href="#cb10-35"></a> <span class="co">/* Mnemonics */</span></span>
|
||||
<span id="cb10-36"><a href="#cb10-36"></a> gboolean (* mnemonic_activate) (GtkWidget *widget,</span>
|
||||
<span id="cb10-37"><a href="#cb10-37"></a> gboolean group_cycling);</span>
|
||||
<span id="cb10-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 class="op">);</span></span>
|
||||
<span id="cb10-38"><a href="#cb10-38"></a></span>
|
||||
<span id="cb10-39"><a href="#cb10-39"></a> <span class="co">/* explicit focus */</span></span>
|
||||
<span id="cb10-40"><a href="#cb10-40"></a> gboolean (* grab_focus) (GtkWidget *widget);</span>
|
||||
<span id="cb10-41"><a href="#cb10-41"></a> gboolean (* focus) (GtkWidget *widget,</span>
|
||||
<span id="cb10-42"><a href="#cb10-42"></a> GtkDirectionType direction);</span>
|
||||
<span id="cb10-43"><a href="#cb10-43"></a> <span class="dt">void</span> (* set_focus_child) (GtkWidget *widget,</span>
|
||||
<span id="cb10-44"><a href="#cb10-44"></a> GtkWidget *child);</span>
|
||||
<span id="cb10-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 <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 class="op">);</span></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 <span class="op">*</span>child<span class="op">);</span></span>
|
||||
<span id="cb10-45"><a href="#cb10-45"></a></span>
|
||||
<span id="cb10-46"><a href="#cb10-46"></a> <span class="co">/* keyboard navigation */</span></span>
|
||||
<span id="cb10-47"><a href="#cb10-47"></a> <span class="dt">void</span> (* move_focus) (GtkWidget *widget,</span>
|
||||
<span id="cb10-48"><a href="#cb10-48"></a> GtkDirectionType direction);</span>
|
||||
<span id="cb10-49"><a href="#cb10-49"></a> gboolean (* keynav_failed) (GtkWidget *widget,</span>
|
||||
<span id="cb10-50"><a href="#cb10-50"></a> GtkDirectionType direction);</span>
|
||||
<span id="cb10-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 class="op">);</span></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 class="op">);</span></span>
|
||||
<span id="cb10-51"><a href="#cb10-51"></a></span>
|
||||
<span id="cb10-52"><a href="#cb10-52"></a> gboolean (* query_tooltip) (GtkWidget *widget,</span>
|
||||
<span id="cb10-53"><a href="#cb10-53"></a> <span class="dt">int</span> x,</span>
|
||||
<span id="cb10-54"><a href="#cb10-54"></a> <span class="dt">int</span> y,</span>
|
||||
<span id="cb10-55"><a href="#cb10-55"></a> gboolean keyboard_tooltip,</span>
|
||||
<span id="cb10-56"><a href="#cb10-56"></a> GtkTooltip *tooltip);</span>
|
||||
<span id="cb10-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 class="op">,</span></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 class="op">,</span></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-58"><a href="#cb10-58"></a> <span class="dt">void</span> (* compute_expand) (GtkWidget *widget,</span>
|
||||
<span id="cb10-59"><a href="#cb10-59"></a> gboolean *hexpand_p,</span>
|
||||
<span id="cb10-60"><a href="#cb10-60"></a> gboolean *vexpand_p);</span>
|
||||
<span id="cb10-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 <span class="op">*</span>hexpand_p<span class="op">,</span></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-62"><a href="#cb10-62"></a> <span class="dt">void</span> (* css_changed) (GtkWidget *widget,</span>
|
||||
<span id="cb10-63"><a href="#cb10-63"></a> GtkCssStyleChange *change);</span>
|
||||
<span id="cb10-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 <span class="op">*</span>change<span class="op">);</span></span>
|
||||
<span id="cb10-64"><a href="#cb10-64"></a></span>
|
||||
<span id="cb10-65"><a href="#cb10-65"></a> <span class="dt">void</span> (* system_setting_changed) (GtkWidget *widget,</span>
|
||||
<span id="cb10-66"><a href="#cb10-66"></a> GtkSystemSetting settings);</span>
|
||||
<span id="cb10-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 class="op">);</span></span>
|
||||
<span id="cb10-67"><a href="#cb10-67"></a></span>
|
||||
<span id="cb10-68"><a href="#cb10-68"></a> <span class="dt">void</span> (* snapshot) (GtkWidget *widget,</span>
|
||||
<span id="cb10-69"><a href="#cb10-69"></a> GtkSnapshot *snapshot);</span>
|
||||
<span id="cb10-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 <span class="op">*</span>snapshot<span class="op">);</span></span>
|
||||
<span id="cb10-70"><a href="#cb10-70"></a></span>
|
||||
<span id="cb10-71"><a href="#cb10-71"></a> gboolean (* contains) (GtkWidget *widget,</span>
|
||||
<span id="cb10-72"><a href="#cb10-72"></a> <span class="dt">double</span> x,</span>
|
||||
<span id="cb10-73"><a href="#cb10-73"></a> <span class="dt">double</span> y);</span>
|
||||
<span id="cb10-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 class="op">,</span></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-75"><a href="#cb10-75"></a> <span class="co">/*< private >*/</span></span>
|
||||
<span id="cb10-76"><a href="#cb10-76"></a></span>
|
||||
<span id="cb10-77"><a href="#cb10-77"></a> GtkWidgetClassPrivate *priv;</span>
|
||||
<span id="cb10-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-79"><a href="#cb10-79"></a> gpointer padding[<span class="dv">8</span>];</span>
|
||||
<span id="cb10-80"><a href="#cb10-80"></a>};</span>
|
||||
<span id="cb10-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 class="op">};</span></span>
|
||||
<span id="cb10-81"><a href="#cb10-81"></a></span>
|
||||
<span id="cb10-82"><a href="#cb10-82"></a><span class="kw">struct</span> _GtkTextViewClass</span>
|
||||
<span id="cb10-83"><a href="#cb10-83"></a>{</span>
|
||||
<span id="cb10-84"><a href="#cb10-84"></a> GtkWidgetClass parent_class;</span>
|
||||
<span id="cb10-83"><a href="#cb10-83"></a><span class="op">{</span></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-86"><a href="#cb10-86"></a> <span class="co">/*< public >*/</span></span>
|
||||
<span id="cb10-87"><a href="#cb10-87"></a></span>
|
||||
<span id="cb10-88"><a href="#cb10-88"></a> <span class="dt">void</span> (* move_cursor) (GtkTextView *text_view,</span>
|
||||
<span id="cb10-89"><a href="#cb10-89"></a> GtkMovementStep step,</span>
|
||||
<span id="cb10-90"><a href="#cb10-90"></a> <span class="dt">int</span> count,</span>
|
||||
<span id="cb10-91"><a href="#cb10-91"></a> gboolean extend_selection);</span>
|
||||
<span id="cb10-92"><a href="#cb10-92"></a> <span class="dt">void</span> (* set_anchor) (GtkTextView *text_view);</span>
|
||||
<span id="cb10-93"><a href="#cb10-93"></a> <span class="dt">void</span> (* insert_at_cursor) (GtkTextView *text_view,</span>
|
||||
<span id="cb10-94"><a href="#cb10-94"></a> <span class="dt">const</span> <span class="dt">char</span> *str);</span>
|
||||
<span id="cb10-95"><a href="#cb10-95"></a> <span class="dt">void</span> (* delete_from_cursor) (GtkTextView *text_view,</span>
|
||||
<span id="cb10-96"><a href="#cb10-96"></a> GtkDeleteType type,</span>
|
||||
<span id="cb10-97"><a href="#cb10-97"></a> <span class="dt">int</span> count);</span>
|
||||
<span id="cb10-98"><a href="#cb10-98"></a> <span class="dt">void</span> (* backspace) (GtkTextView *text_view);</span>
|
||||
<span id="cb10-99"><a href="#cb10-99"></a> <span class="dt">void</span> (* cut_clipboard) (GtkTextView *text_view);</span>
|
||||
<span id="cb10-100"><a href="#cb10-100"></a> <span class="dt">void</span> (* copy_clipboard) (GtkTextView *text_view);</span>
|
||||
<span id="cb10-101"><a href="#cb10-101"></a> <span class="dt">void</span> (* paste_clipboard) (GtkTextView *text_view);</span>
|
||||
<span id="cb10-102"><a href="#cb10-102"></a> <span class="dt">void</span> (* toggle_overwrite) (GtkTextView *text_view);</span>
|
||||
<span id="cb10-103"><a href="#cb10-103"></a> GtkTextBuffer * (* create_buffer) (GtkTextView *text_view);</span>
|
||||
<span id="cb10-104"><a href="#cb10-104"></a> <span class="dt">void</span> (* snapshot_layer) (GtkTextView *text_view,</span>
|
||||
<span id="cb10-105"><a href="#cb10-105"></a> GtkTextViewLayer layer,</span>
|
||||
<span id="cb10-106"><a href="#cb10-106"></a> GtkSnapshot *snapshot);</span>
|
||||
<span id="cb10-107"><a href="#cb10-107"></a> gboolean (* extend_selection) (GtkTextView *text_view,</span>
|
||||
<span id="cb10-108"><a href="#cb10-108"></a> GtkTextExtendSelection granularity,</span>
|
||||
<span id="cb10-109"><a href="#cb10-109"></a> <span class="dt">const</span> GtkTextIter *location,</span>
|
||||
<span id="cb10-110"><a href="#cb10-110"></a> GtkTextIter *start,</span>
|
||||
<span id="cb10-111"><a href="#cb10-111"></a> GtkTextIter *end);</span>
|
||||
<span id="cb10-112"><a href="#cb10-112"></a> <span class="dt">void</span> (* insert_emoji) (GtkTextView *text_view);</span>
|
||||
<span id="cb10-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 class="op">,</span></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 class="op">);</span></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> <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> <span class="op">*</span>str<span class="op">);</span></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 class="op">,</span></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> <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> <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> <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> <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> <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 <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> <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 class="op">,</span></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 <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 class="op">,</span></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 <span class="op">*</span>start<span class="op">,</span></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> <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-114"><a href="#cb10-114"></a> <span class="co">/*< private >*/</span></span>
|
||||
<span id="cb10-115"><a href="#cb10-115"></a></span>
|
||||
<span id="cb10-116"><a href="#cb10-116"></a> gpointer padding[<span class="dv">8</span>];</span>
|
||||
<span id="cb10-117"><a href="#cb10-117"></a>};</span>
|
||||
<span id="cb10-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 class="op">};</span></span>
|
||||
<span id="cb10-118"><a href="#cb10-118"></a></span>
|
||||
<span id="cb10-119"><a href="#cb10-119"></a><span class="co">/* The following definition is generated by the macro G_DECLARE_FINAL_TYPE */</span></span>
|
||||
<span id="cb10-120"><a href="#cb10-120"></a><span class="kw">typedef</span> <span class="kw">struct</span> {</span>
|
||||
<span id="cb10-121"><a href="#cb10-121"></a> GtkTextView parent_class;</span>
|
||||
<span id="cb10-122"><a href="#cb10-122"></a>} TfeTextViewClass;</span></code></pre></div>
|
||||
<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 class="op">;</span></span>
|
||||
<span id="cb10-122"><a href="#cb10-122"></a><span class="op">}</span> TfeTextViewClass<span class="op">;</span></span></code></pre></div>
|
||||
<ul>
|
||||
<li>120-122: This three lines are generated by the macro <code>G_DECLARE_FINAL_TYPE</code>. So, they are not written in either <code>tfe_text_view.h</code> or <code>tfe_text_view.c</code>.</li>
|
||||
<li>3, 84, 121: Each derived class puts its parent class at the first member of its structure. It is the same as instance structures.</li>
|
||||
<li>Class members in ancestors are open to the descendant class. So, they can be changed in <code>tfe_text_view_class_init</code> function. For example, the <code>dispose</code> pointer in GObjectClass will be overridden later in <code>tfe_text_view_class_init</code>. (Override is an object oriented programming terminology. Override is rewriting ancestors’ class methods in the descendant class.)</li>
|
||||
<li>Some class methods are often overridden. <code>set_property</code>, <code>get_property</code>, <code>dispose</code>, <code>finalize</code> and <code>constructed</code> are such methods.</li>
|
||||
<li>120-122: This three lines are generated by the macro
|
||||
<code>G_DECLARE_FINAL_TYPE</code>. So, they are not written in either
|
||||
<code>tfe_text_view.h</code> or <code>tfe_text_view.c</code>.</li>
|
||||
<li>3, 84, 121: Each 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>
|
||||
<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>
|
||||
<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>
|
||||
<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 doesn’t 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>
|
||||
<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 doesn’t need B any longer, then A
|
||||
discards the pointer to B (usually it is done by assigning NULL to the
|
||||
pointer) and decreases the reference count of B by one with the function
|
||||
<code>g_object_unref (B)</code>.</p>
|
||||
<p>If two objects A and B refer to C, then the reference count of C is
|
||||
two. If A no longer needs C, A discards the pointer to C and decreases
|
||||
the reference count in C by one. Now the reference count of C is one. In
|
||||
the same way, if B no longer needs C, B discards the pointer to C and
|
||||
decreases the reference count in C by one. At this moment, no object
|
||||
refers to C and the reference count of C is zero. This means C is no
|
||||
longer useful. Then C destructs itself and finally the memories
|
||||
allocated to C is freed.</p>
|
||||
<figure>
|
||||
<img src="image/refcount.png" alt="" /><figcaption>Reference count of B</figcaption>
|
||||
<img src="image/refcount.png" alt="Reference count of B" />
|
||||
<figcaption aria-hidden="true">Reference count of B</figcaption>
|
||||
</figure>
|
||||
<p>The idea above is based on an assumption that an object referred by nothing has reference count of zero. When the reference count drops to zero, the object starts its destruction process. The destruction process is split into two phases: disposing and finalizing. In the disposing process, the object invokes the function pointed by <code>dispose</code> in its class to release all references to other objects. In the finalizing process, it invokes the function pointed by <code>finalize</code> in its class to complete the destruction process. These functions are also called handlers or methods. For example, dispose handler or dispose method.</p>
|
||||
<p>In the destruction process of TfeTextView, the reference count of widgets related to TfeTextView is automatically decreased. But GFile pointed by <code>tv->file</code> needs to decrease its reference count by one. You must write the code in the dispose handler <code>tfe_text_view_dispose</code>.</p>
|
||||
<div class="sourceCode" id="cb11"><pre class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb11-1"><a href="#cb11-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb11-2"><a href="#cb11-2"></a>tfe_text_view_dispose (GObject *gobject) {</span>
|
||||
<span id="cb11-3"><a href="#cb11-3"></a> TfeTextView *tv = TFE_TEXT_VIEW (gobject);</span>
|
||||
<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 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->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-5"><a href="#cb11-5"></a> <span class="cf">if</span> (G_IS_FILE (tv->file))</span>
|
||||
<span id="cb11-6"><a href="#cb11-6"></a> g_clear_object (&tv->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">-></span>file<span class="op">))</span></span>
|
||||
<span id="cb11-6"><a href="#cb11-6"></a> g_clear_object <span class="op">(&</span>tv<span class="op">-></span>file<span class="op">);</span></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)->dispose (gobject);</span>
|
||||
<span id="cb11-9"><a href="#cb11-9"></a>}</span></code></pre></div>
|
||||
<span id="cb11-8"><a href="#cb11-8"></a> G_OBJECT_CLASS <span class="op">(</span>tfe_text_view_parent_class<span class="op">)-></span>dispose <span class="op">(</span>gobject<span class="op">);</span></span>
|
||||
<span id="cb11-9"><a href="#cb11-9"></a><span class="op">}</span></span></code></pre></div>
|
||||
<ul>
|
||||
<li>5,6: If <code>tv->file</code> points a GFile, decrease its reference count. <code>g_clear_object</code> decreases the reference count and assigns NULL to <code>tv->file</code>. In dispose handlers, we usually use <code>g_clear_object</code> rather than <code>g_object_unref</code>.</li>
|
||||
<li>8: invokes parent’s dispose handler. (This will be explained later.)</li>
|
||||
<li>5,6: If <code>tv->file</code> points a GFile, decrease its
|
||||
reference count. <code>g_clear_object</code> decreases the reference
|
||||
count and assigns NULL to <code>tv->file</code>. In dispose handlers,
|
||||
we usually use <code>g_clear_object</code> rather than
|
||||
<code>g_object_unref</code>.</li>
|
||||
<li>8: invokes parent’s dispose handler. (This will be explained
|
||||
later.)</li>
|
||||
</ul>
|
||||
<p>In the disposing process, the object uses the pointer in its class to call the handler. Therefore, <code>tfe_text_view_dispose</code> needs to be registered in the class when the TfeTextView class is initialized. The function <code>tfe_text_view_class_init</code> is the class initialization function and it is declared in the replacement produced by <code>G_DEFINE_TYPE</code> macro.</p>
|
||||
<div class="sourceCode" id="cb12"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true"></a>tfe_text_view_class_init (TfeTextViewClass *class) {</span>
|
||||
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true"></a> GObjectClass *object_class = G_OBJECT_CLASS (class);</span>
|
||||
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true"></a></span>
|
||||
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true"></a> object_class->dispose = tfe_text_view_dispose;</span>
|
||||
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true"></a></span>
|
||||
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true"></a>}</span></code></pre></div>
|
||||
<p>Each ancestors’ class has been created before TfeTextViewClass is created. Therefore, there are four classes and each class has a pointer to each dispose handler. Look at the following diagram. There are four classes – GObjectClass (GInitiallyUnownedClass), GtkWidgetClass, GtkTextViewClass and TfeTextViewClass. Each class has its own dispose handler – <code>dh1</code>, <code>dh2</code>, <code>dh3</code> and <code>tfe_text_view_dispose</code>.</p>
|
||||
<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
|
||||
<code>G_DEFINE_TYPE</code> macro expansion.</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="dt">static</span> <span class="dt">void</span></span>
|
||||
<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>
|
||||
<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">-></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>
|
||||
<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>
|
||||
<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->file</code>. Then it invokes its parent’s dispose handler in line 8.</p>
|
||||
<div class="sourceCode" id="cb13"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true"></a>G_OBJECT_CLASS (tfe_text_view_parent_class)->dispose (gobject);</span></code></pre></div>
|
||||
<p><code>tfe_text_view_parent_class</code>,which is made by <code>G_DEFINE_TYPE</code> macro, is a pointer that points the parent object class. Therefore, <code>G_OBJECT_CLASS (tfe_text_view_parent_class)->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>
|
||||
<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->file</code>. Then it invokes its parent’s 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">)-></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)->dispose</code>
|
||||
points the handler <code>dh3</code> in the diagram above. The statement
|
||||
<code>G_OBJECT_CLASS (tfe_text_view_parent_class)->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>
|
||||
<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>
|
||||
|
|
243
docs/sec12.html
|
@ -5,7 +5,7 @@
|
|||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
||||
<title>Gtk4 tutorial</title>
|
||||
<title>GTK 4 tutorial</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
|
@ -113,117 +113,184 @@
|
|||
</nav>
|
||||
<h1 id="signals">Signals</h1>
|
||||
<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>
|
||||
<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>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>
|
||||
<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>.</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>
|
||||
<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>
|
||||
<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 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. Signal is emitted on the instance of the object.</li>
|
||||
<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 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>
|
||||
<h2 id="signal-registration">Signal registration</h2>
|
||||
<p>In TfeTextView, two signals are registered.</p>
|
||||
<ul>
|
||||
<li>“change-file” signal. This signal is emitted when <code>tv->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>
|
||||
<li>“change-file” signal. This signal is emitted when
|
||||
<code>tv->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>
|
||||
<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>
|
||||
<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>
|
||||
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true"></a> CHANGE_FILE,</span>
|
||||
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true"></a> OPEN_RESPONSE,</span>
|
||||
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true"></a> NUMBER_OF_SIGNALS</span>
|
||||
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true"></a>};</span>
|
||||
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true"></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>
|
||||
<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" tabindex="-1"></a><span class="kw">enum</span> <span class="op">{</span></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" tabindex="-1"></a> OPEN_RESPONSE<span class="op">,</span></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" tabindex="-1"></a><span class="op">};</span></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" 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>
|
||||
<div class="sourceCode" id="cb2"><pre class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb2-1"><a href="#cb2-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb2-2"><a href="#cb2-2"></a>tfe_text_view_class_init (TfeTextViewClass *class) {</span>
|
||||
<span id="cb2-3"><a href="#cb2-3"></a> GObjectClass *object_class = G_OBJECT_CLASS (class);</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="dt">static</span> <span class="dt">void</span></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-5"><a href="#cb2-5"></a> object_class->dispose = tfe_text_view_dispose;</span>
|
||||
<span id="cb2-6"><a href="#cb2-6"></a> tfe_text_view_signals[CHANGE_FILE] = g_signal_new (<span class="st">"change-file"</span>,</span>
|
||||
<span id="cb2-7"><a href="#cb2-7"></a> G_TYPE_FROM_CLASS (class),</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-9"><a href="#cb2-9"></a> <span class="dv">0</span> <span class="co">/* class offset */</span>,</span>
|
||||
<span id="cb2-10"><a href="#cb2-10"></a> NULL <span class="co">/* accumulator */</span>,</span>
|
||||
<span id="cb2-11"><a href="#cb2-11"></a> NULL <span class="co">/* accumulator data */</span>,</span>
|
||||
<span id="cb2-12"><a href="#cb2-12"></a> NULL <span class="co">/* C marshaller */</span>,</span>
|
||||
<span id="cb2-13"><a href="#cb2-13"></a> G_TYPE_NONE <span class="co">/* return_type */</span>,</span>
|
||||
<span id="cb2-5"><a href="#cb2-5"></a> object_class<span class="op">-></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<span class="op">[</span>CHANGE_FILE<span class="op">]</span> <span class="op">=</span> g_signal_new <span class="op">(</span><span class="st">"change-file"</span><span class="op">,</span></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 <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 class="op">,</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 class="op">,</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 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-15"><a href="#cb2-15"></a> );</span>
|
||||
<span id="cb2-16"><a href="#cb2-16"></a> tfe_text_view_signals[OPEN_RESPONSE] = g_signal_new (<span class="st">"open-response"</span>,</span>
|
||||
<span id="cb2-17"><a href="#cb2-17"></a> G_TYPE_FROM_CLASS (class),</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-19"><a href="#cb2-19"></a> <span class="dv">0</span> <span class="co">/* class offset */</span>,</span>
|
||||
<span id="cb2-20"><a href="#cb2-20"></a> NULL <span class="co">/* accumulator */</span>,</span>
|
||||
<span id="cb2-21"><a href="#cb2-21"></a> NULL <span class="co">/* accumulator data */</span>,</span>
|
||||
<span id="cb2-22"><a href="#cb2-22"></a> NULL <span class="co">/* C marshaller */</span>,</span>
|
||||
<span id="cb2-23"><a href="#cb2-23"></a> G_TYPE_NONE <span class="co">/* return_type */</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-15"><a href="#cb2-15"></a> <span class="op">);</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">"open-response"</span><span class="op">,</span></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 <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 class="op">,</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 class="op">,</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 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 class="op">,</span></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-27"><a href="#cb2-27"></a>}</span></code></pre></div>
|
||||
<span id="cb2-26"><a href="#cb2-26"></a> <span class="op">);</span></span>
|
||||
<span id="cb2-27"><a href="#cb2-27"></a><span class="op">}</span></span></code></pre></div>
|
||||
<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 don’t 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>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>
|
||||
<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) so the offset (the line 9) is set to zero. You
|
||||
usually don’t need a default handler. If you need it, use
|
||||
<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>
|
||||
<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">/* "change-file" 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-3"><a href="#cb3-3" aria-hidden="true"></a>user_function (TfeTextView *tv,</span>
|
||||
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true"></a> gpointer user_data)</span>
|
||||
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true"></a></span>
|
||||
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true"></a><span class="co">/* "open-response" 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-8"><a href="#cb3-8" aria-hidden="true"></a>user_function (TfeTextView *tv,</span>
|
||||
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true"></a> TfeTextViewOpenResponseType response-id,</span>
|
||||
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true"></a> gpointer user_data)</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="co">/* "change-file" signal handler */</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" 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" tabindex="-1"></a> gpointer user_data<span class="op">)</span></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" tabindex="-1"></a><span class="co">/* "open-response" signal handler */</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" 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" 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" tabindex="-1"></a> gpointer user_data<span class="op">)</span></span></code></pre></div>
|
||||
<ul>
|
||||
<li>Because “change-file” signal doesn’t have parameter, the handler’s parameters are a TfeTextView instance and user data.</li>
|
||||
<li>Because “open-response” signal has one parameter, the handler’s parameters are a TfeTextView instance, the signal’s parameter and user 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>
|
||||
<li>Because “change-file” signal doesn’t have parameter, the handler’s
|
||||
parameters are a TfeTextView instance and user data.</li>
|
||||
<li>Because “open-response” signal has one parameter, the handler’s
|
||||
parameters are a TfeTextView instance, the signal’s parameter and user
|
||||
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>
|
||||
<p>The values of the parameter is defined in <code>tfetextview.h</code> because they are public.</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"></a><span class="co">/* "open-response" signal response */</span></span>
|
||||
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true"></a><span class="kw">enum</span></span>
|
||||
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true"></a>{</span>
|
||||
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true"></a> TFE_OPEN_RESPONSE_SUCCESS,</span>
|
||||
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true"></a> TFE_OPEN_RESPONSE_CANCEL,</span>
|
||||
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true"></a> TFE_OPEN_RESPONSE_ERROR</span>
|
||||
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true"></a>};</span></code></pre></div>
|
||||
<p>The values of the parameter is defined in <code>tfetextview.h</code>
|
||||
because they are public.</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 class="co">/* "open-response" signal response */</span></span>
|
||||
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="kw">enum</span></span>
|
||||
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="op">{</span></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-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a> TFE_OPEN_RESPONSE_CANCEL<span class="op">,</span></span>
|
||||
<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>
|
||||
<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_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>
|
||||
<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_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>
|
||||
<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>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"></a>g_signal_connect (GTK_TEXT_VIEW (tv), <span class="st">"change-file"</span>, G_CALLBACK (file_changed), nb);</span>
|
||||
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true"></a></span>
|
||||
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true"></a>g_signal_connect (TFE_TEXT_VIEW (tv), <span class="st">"open-response"</span>, G_CALLBACK (open_response), nb);</span></code></pre></div>
|
||||
<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>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">"change-file"</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">"open-response"</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>
|
||||
<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>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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>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>
|
||||
<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>
|
||||
<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 third argument is the detail of the signal. “change-file” signal and “open-response” signal doesn’t have details and the argument is zero when no details.</li>
|
||||
<li>“change-file” signal doesn’t have parameter, so there’s no fourth parameter.</li>
|
||||
<li>“open-response” signal has one parameter. The fourth parameter is the parameter.</li>
|
||||
<li>The third argument is the detail of the signal. “change-file” signal
|
||||
and “open-response” signal doesn’t have details and the argument is zero
|
||||
when no details.</li>
|
||||
<li>“change-file” signal doesn’t have parameter, so there’s no fourth
|
||||
parameter.</li>
|
||||
<li>“open-response” signal has one parameter. The fourth parameter is
|
||||
the parameter.</li>
|
||||
</ul>
|
||||
</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>
|
||||
|
|
839
docs/sec13.html
|
@ -112,117 +112,129 @@
|
|||
</div>
|
||||
</nav>
|
||||
<h1 id="functions-in-tfetextview">Functions in TfeTextView</h1>
|
||||
<p>In this section I will explain functions in TfeTextView object.</p>
|
||||
<h2 id="tfe.h-and-tfetextview.h">tfe.h and tfetextview.h</h2>
|
||||
<p><code>tfe.h</code> is a top header file and it includes
|
||||
<code>gtk.h</code> and all the header files. C source files
|
||||
<code>tfeapplication.c</code> and <code>tfenotebook.c</code> include
|
||||
<code>tfe.h</code> at the beginning.</p>
|
||||
<p>TfeTextView functions are described in this section.</p>
|
||||
<h2 id="tfetextview.h">tfetextview.h</h2>
|
||||
<p>The header file <code>tfetextview.h</code> provides:</p>
|
||||
<ul>
|
||||
<li>The type of TfeTextView, which is
|
||||
<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
|
||||
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"><gtk/gtk.h></span></span>
|
||||
<span id="cb1-2"><a href="#cb1-2"></a></span>
|
||||
<span id="cb1-3"><a href="#cb1-3"></a><span class="pp">#include </span><span class="im">"../tfetextview/tfetextview.h"</span></span>
|
||||
<span id="cb1-4"><a href="#cb1-4"></a><span class="pp">#include </span><span class="im">"tfenotebook.h"</span></span></code></pre></div>
|
||||
<p><code>../tfetextview/tfetextview.h</code> is a header file which
|
||||
describes the public functions in <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">#ifndef __TFE_TEXT_VIEW_H__</span></span>
|
||||
<span id="cb2-2"><a href="#cb2-2"></a><span class="pp">#define __TFE_TEXT_VIEW_H__</span></span>
|
||||
<span id="cb2-3"><a href="#cb2-3"></a></span>
|
||||
<span id="cb2-4"><a href="#cb2-4"></a><span class="pp">#include </span><span class="im"><gtk/gtk.h></span></span>
|
||||
<span id="cb2-5"><a href="#cb2-5"></a></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="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="cb2-8"><a href="#cb2-8"></a></span>
|
||||
<span id="cb2-9"><a href="#cb2-9"></a><span class="co">/* "open-response" signal response */</span></span>
|
||||
<span id="cb2-10"><a href="#cb2-10"></a><span class="kw">enum</span> TfeTextViewOpenResponseType</span>
|
||||
<span id="cb2-11"><a href="#cb2-11"></a><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="cb2-13"><a href="#cb2-13"></a> TFE_OPEN_RESPONSE_CANCEL<span class="op">,</span></span>
|
||||
<span id="cb2-14"><a href="#cb2-14"></a> TFE_OPEN_RESPONSE_ERROR</span>
|
||||
<span id="cb2-15"><a href="#cb2-15"></a><span class="op">};</span></span>
|
||||
<span id="cb2-16"><a href="#cb2-16"></a></span>
|
||||
<span id="cb2-17"><a href="#cb2-17"></a>GFile <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="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-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="cb2-22"><a href="#cb2-22"></a></span>
|
||||
<span id="cb2-23"><a href="#cb2-23"></a><span class="dt">void</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="cb2-25"><a href="#cb2-25"></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>tfe_text_view_saveas <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">);</span></span>
|
||||
<span id="cb2-28"><a href="#cb2-28"></a></span>
|
||||
<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>
|
||||
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 class="pp">#define __TFE_TEXT_VIEW_H__</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"><gtk/gtk.h></span></span>
|
||||
<span id="cb1-5"><a href="#cb1-5"></a></span>
|
||||
<span id="cb1-6"><a href="#cb1-6"></a><span class="pp">#define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type ()</span></span>
|
||||
<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>
|
||||
<span id="cb1-8"><a href="#cb1-8"></a></span>
|
||||
<span id="cb1-9"><a href="#cb1-9"></a><span class="co">/* "open-response" signal response */</span></span>
|
||||
<span id="cb1-10"><a href="#cb1-10"></a><span class="kw">enum</span> TfeTextViewOpenResponseType</span>
|
||||
<span id="cb1-11"><a href="#cb1-11"></a><span class="op">{</span></span>
|
||||
<span id="cb1-12"><a href="#cb1-12"></a> TFE_OPEN_RESPONSE_SUCCESS<span class="op">,</span></span>
|
||||
<span id="cb1-13"><a href="#cb1-13"></a> TFE_OPEN_RESPONSE_CANCEL<span class="op">,</span></span>
|
||||
<span id="cb1-14"><a href="#cb1-14"></a> TFE_OPEN_RESPONSE_ERROR</span>
|
||||
<span id="cb1-15"><a href="#cb1-15"></a><span class="op">};</span></span>
|
||||
<span id="cb1-16"><a href="#cb1-16"></a></span>
|
||||
<span id="cb1-17"><a href="#cb1-17"></a>GFile <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="cb1-19"><a href="#cb1-19"></a></span>
|
||||
<span id="cb1-20"><a href="#cb1-20"></a><span class="dt">void</span></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="cb1-22"><a href="#cb1-22"></a></span>
|
||||
<span id="cb1-23"><a href="#cb1-23"></a><span class="dt">void</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="cb1-25"><a href="#cb1-25"></a></span>
|
||||
<span id="cb1-26"><a href="#cb1-26"></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="cb1-28"><a href="#cb1-28"></a></span>
|
||||
<span id="cb1-29"><a href="#cb1-29"></a>GtkWidget <span class="op">*</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="cb1-31"><a href="#cb1-31"></a></span>
|
||||
<span id="cb1-32"><a href="#cb1-32"></a>GtkWidget <span class="op">*</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="cb1-34"><a href="#cb1-34"></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>
|
||||
<ul>
|
||||
<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>
|
||||
also has the same mechanism to avoid including it multiple times.</li>
|
||||
<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 don’t 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”
|
||||
signal.</li>
|
||||
<li>17-33: Declarations of public functions on TfeTextView.</li>
|
||||
</ul>
|
||||
<h2 id="functions-to-create-tfetextview-instances">Functions to create
|
||||
TfeTextView instances</h2>
|
||||
<h2 id="instance-creation-functions">Instance creation Functions</h2>
|
||||
<p>A TfeTextView instance is created with <code>tfe_text_view_new</code>
|
||||
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
|
||||
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
|
||||
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
|
||||
creation process, NULL is returned.</p>
|
||||
<p>Each function is defined as follows.</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>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="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="cb5-4"><a href="#cb5-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="cb5-6"><a href="#cb5-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="cb5-8"><a href="#cb5-8"></a> gsize length<span class="op">;</span></span>
|
||||
<span id="cb5-9"><a href="#cb5-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">&</span>contents<span class="op">,</span> <span class="op">&</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="cb5-12"><a href="#cb5-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="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="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="cb5-16"><a href="#cb5-16"></a> TFE_TEXT_VIEW <span class="op">(</span>tv<span class="op">)-></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="cb5-18"><a href="#cb5-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="cb5-20"><a href="#cb5-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="cb5-22"><a href="#cb5-22"></a></span>
|
||||
<span id="cb5-23"><a href="#cb5-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="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="cb5-26"><a href="#cb5-26"></a><span class="op">}</span></span></code></pre></div>
|
||||
<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_with_file <span class="op">(</span>GFile <span class="op">*</span>file<span class="op">)</span> <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="cb4-4"><a href="#cb4-4"></a></span>
|
||||
<span id="cb4-5"><a href="#cb4-5"></a> GtkWidget <span class="op">*</span>tv<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="cb4-7"><a href="#cb4-7"></a> <span class="dt">char</span> <span class="op">*</span>contents<span class="op">;</span></span>
|
||||
<span id="cb4-8"><a href="#cb4-8"></a> gsize length<span class="op">;</span></span>
|
||||
<span id="cb4-9"><a href="#cb4-9"></a></span>
|
||||
<span id="cb4-10"><a href="#cb4-10"></a> <span class="cf">if</span> <span class="op">(!</span> g_file_load_contents <span class="op">(</span>file<span class="op">,</span> NULL<span class="op">,</span> <span class="op">&</span>contents<span class="op">,</span> <span class="op">&</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-11"><a href="#cb4-11"></a> <span class="cf">return</span> NULL<span class="op">;</span></span>
|
||||
<span id="cb4-12"><a href="#cb4-12"></a></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="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="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="cb4-16"><a href="#cb4-16"></a> TFE_TEXT_VIEW <span class="op">(</span>tv<span class="op">)-></span>file <span class="op">=</span> g_file_dup <span class="op">(</span>file<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="cb4-18"><a href="#cb4-18"></a> <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="cb4-20"><a href="#cb4-20"></a> <span class="cf">return</span> tv<span class="op">;</span></span>
|
||||
<span id="cb4-21"><a href="#cb4-21"></a><span class="op">}</span></span>
|
||||
<span id="cb4-22"><a href="#cb4-22"></a></span>
|
||||
<span id="cb4-23"><a href="#cb4-23"></a>GtkWidget <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="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">"wrap-mode"</span><span class="op">,</span> GTK_WRAP_WORD_CHAR<span class="op">,</span> NULL<span class="op">));</span></span>
|
||||
<span id="cb4-26"><a href="#cb4-26"></a><span class="op">}</span></span></code></pre></div>
|
||||
<ul>
|
||||
<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
|
||||
pointer to GtkWidget. Initialization is done in
|
||||
<code>tfe_text_view_init</code> which is called in the process of
|
||||
<code>g_object_new</code> function.</li>
|
||||
pointer to GtkWidget. The function <code>g_object_new</code> 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.</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
|
||||
href="https://docs.gtk.org/glib/func.return_val_if_fail.html">GLib API
|
||||
Reference, g_return_val_if_fail</a>. And also <a
|
||||
href="https://docs.gtk.org/glib/logging.html">GLib API Reference,
|
||||
Reference – g_return_val_if_fail</a>. And also <a
|
||||
href="https://docs.gtk.org/glib/logging.html">GLib API Reference –
|
||||
Message Logging</a>. It tests whether the argument <code>file</code> is
|
||||
a pointer to GFile. If it’s true, then the program goes on to the next
|
||||
line. If it’s false, then it returns NULL (the second argument)
|
||||
|
@ -254,295 +266,409 @@ doesn’t need to save the contents.</li>
|
|||
<li>20: Returns <code>tv</code>, which is a pointer to the newly created
|
||||
TfeTextView instance. If an error happens, NULL is returned.</li>
|
||||
</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
|
||||
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
|
||||
the GtkTextBuffer to a file specified by <code>tv->file</code>. If
|
||||
<code>tv->file</code> is NULL, then it shows GtkFileChooserDialog and
|
||||
prompts the user to choose a file to save. Then it saves the contents to
|
||||
the file and sets <code>tv->file</code> to point the GFile instance
|
||||
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
|
||||
prompts the user to select a existed file or specify a new file to save.
|
||||
Then, the function changes <code>tv->file</code> and save 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
|
||||
TfeTextView and no information is notified to the caller.</p>
|
||||
<div class="sourceCode" id="cb8"><pre
|
||||
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb8-1"><a href="#cb8-1"></a><span class="dt">static</span> gboolean</span>
|
||||
<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>
|
||||
<span id="cb8-3"><a href="#cb8-3"></a> GtkTextIter start_iter<span class="op">;</span></span>
|
||||
<span id="cb8-4"><a href="#cb8-4"></a> GtkTextIter end_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="cb8-6"><a href="#cb8-6"></a> gboolean stat<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="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="cb8-9"><a href="#cb8-9"></a></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">&</span>start_iter<span class="op">,</span> <span class="op">&</span>end_iter<span class="op">);</span></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">&</span>start_iter<span class="op">,</span> <span class="op">&</span>end_iter<span class="op">,</span> FALSE<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">&</span>err<span class="op">))</span> <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="cb8-14"><a href="#cb8-14"></a> stat <span class="op">=</span> TRUE<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="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="cb8-17"><a href="#cb8-17"></a> GTK_MESSAGE_ERROR<span class="op">,</span> GTK_BUTTONS_CLOSE<span class="op">,</span></span>
|
||||
<span id="cb8-18"><a href="#cb8-18"></a> <span class="st">"%s.</span><span class="sc">\n</span><span class="st">"</span><span class="op">,</span> err<span class="op">-></span>message<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">"response"</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-20"><a href="#cb8-20"></a> gtk_widget_show <span class="op">(</span>message_dialog<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="cb8-22"><a href="#cb8-22"></a> stat <span class="op">=</span> FALSE<span class="op">;</span></span>
|
||||
<span id="cb8-23"><a href="#cb8-23"></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="cb8-25"><a href="#cb8-25"></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="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">"TfeTextView: gtk_file_chooser_get_file returns non GFile.</span><span class="sc">\n</span><span class="st">"</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">-></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">-></span>file<span class="op">);</span></span>
|
||||
<span id="cb8-41"><a href="#cb8-41"></a> tv<span class="op">-></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">-></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">-></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">"TfeTextView: The pointer tv->file isn't NULL nor GFile.</span><span class="sc">\n</span><span class="st">"</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">-></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">"Save file"</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">"Cancel"</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">"Save"</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">"response"</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>
|
||||
<h3 id="save_file-function">save_file function</h3>
|
||||
<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> gboolean</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="cb7-3"><a href="#cb7-3"></a> GtkTextIter start_iter<span class="op">;</span></span>
|
||||
<span id="cb7-4"><a href="#cb7-4"></a> GtkTextIter end_iter<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="cb7-6"><a href="#cb7-6"></a> gboolean stat<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="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="cb7-9"><a href="#cb7-9"></a></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">&</span>start_iter<span class="op">,</span> <span class="op">&</span>end_iter<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">&</span>start_iter<span class="op">,</span> <span class="op">&</span>end_iter<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">&</span>err<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="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="cb7-15"><a href="#cb7-15"></a> <span class="cf">else</span> <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 'save_file' doesn't need to do anything about error.</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="cb7-18"><a href="#cb7-18"></a> GTK_MESSAGE_ERROR<span class="op">,</span> GTK_BUTTONS_CLOSE<span class="op">,</span> <span class="st">"%s."</span><span class="op">,</span> err<span class="op">-></span>message<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">"response"</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-20"><a href="#cb7-20"></a> gtk_widget_show <span class="op">(</span>message_dialog<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="cb7-22"><a href="#cb7-22"></a> <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="cb7-24"><a href="#cb7-24"></a> <span class="cf">return</span> stat<span class="op">;</span></span>
|
||||
<span id="cb7-25"><a href="#cb7-25"></a><span class="op">}</span></span></code></pre></div>
|
||||
<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>.
|
||||
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
|
||||
this function is <code>static</code>. Therefore, only functions in this
|
||||
file (<code>tfeTetview.c</code>) call this function. Such static
|
||||
functions usally don’t have <code>g_return_val_if_fail</code>
|
||||
function.</li>
|
||||
argument. 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 <code>static</code>. Therefore, only functions in this file
|
||||
(<code>tfetextview.c</code>) call this function. Such static functions
|
||||
usually don’t have <code>g_return_val_if_fail</code> function.</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
|
||||
modified flag to be FALSE. This means that the buffer is not modified
|
||||
since it has been saved. And set the return status <code>stat</code> to
|
||||
be TRUE.</li>
|
||||
<li>15-23: If it fails to save the contents, displays an error
|
||||
message.</li>
|
||||
<li>16-18: Creates a message dialog with the error message.</li>
|
||||
<li>12: The function <code>g_file_replace_contents</code> writes the
|
||||
contents to the file and returns the status (true = success/ false =
|
||||
fail). It has many parameters, but some of them are almost always given
|
||||
the same values.
|
||||
<ul>
|
||||
<li>GFile* file: GFile to which the contents are saved.</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
|
||||
<code>gtk_window_destroy</code>, so that the dialog disappears when a
|
||||
user clicked on the button.</li>
|
||||
<li>20-21: Shows the window, frees <code>err</code> and set
|
||||
<code>stat</code> to be FLASE.</li>
|
||||
<li>24: Frees <code>contents</code>.</li>
|
||||
<li>25: Returns to the caller.</li>
|
||||
<li>28-47: <code>saveas_dialog_response</code> function. This is a
|
||||
signal handler for the “response” signal on GtkFileChooserDialog
|
||||
instance created by <code>tfe_text_view_saveas</code> function. This
|
||||
handler analyzes the response and determines whether to save the
|
||||
contents.</li>
|
||||
<li>34-45: If the response is <code>GTK_RESPONSE_ACCEPT</code>, the user
|
||||
has clicked on the <code>Save</code> button. So, it tries to save.</li>
|
||||
<li>35: Gets the GFile <code>file</code> from GtkFileChooserDialog.</li>
|
||||
<li>36-37: If it doesn’t point GFile, it outputs an error message to the
|
||||
log.</li>
|
||||
<li>38: Otherwise, it calls <code>save_file</code> to save the contents
|
||||
to the file.</li>
|
||||
<li>39-42: If <code>save_file</code> has successfully saved the
|
||||
contents, <code>tv->file</code> is updated. If the old GFile pointed
|
||||
by <code>tv->file</code> exists, it is freed in advance. Emits
|
||||
“change-file” signal.</li>
|
||||
<li>44: Unrefs <code>file</code>.</li>
|
||||
<li>46: destroys the file chooser dialog.</li>
|
||||
<li>49-64: <code>tfe_text_view_save</code> function.</li>
|
||||
<li>51: <code>tfe_text_view_save</code> is public, i.e. it is open to
|
||||
the other files. So, it doesn’t 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> doesn’t return a value.</li>
|
||||
<li>53-54: Gets GtkTextBuffer instance and GtkWidget instance and
|
||||
assignes them to <code>tb</code> and<code>win</code> respectively.</li>
|
||||
<li>56-57: If the buffer hasn’t modified, then it doesn’t need to save
|
||||
it. So the function returns.</li>
|
||||
<li>58-59: If <code>tv->file</code> is NULL, no file has given yet.
|
||||
It calls <code>tfe_text_view_saveas</code> which prompts a user to
|
||||
select a file or specify a new file to save.</li>
|
||||
<li>60-61: If <code>tv->file</code> doesn’t point GFile, somethig bad
|
||||
has happened. Logs an error message.</li>
|
||||
<li>62-63: Calls <code>save_file</code> to save the contents to the
|
||||
file.</li>
|
||||
<li>66-79: <code>tfe_text_view_saveas</code> function. It shows
|
||||
GtkFileChooserDialog and prompts the user to choose a file.</li>
|
||||
<li>73-76: Creates GtkFileChooserDialog. The title is “Save file”.
|
||||
Transient parent of the dialog is <code>win</code>, which is the
|
||||
top-level window. The action is save mode. The buttons are Cancel and
|
||||
Save.</li>
|
||||
<li>77: connects the “response” signal of the dialog and
|
||||
<code>saveas_dialog_response</code> handler.</li>
|
||||
<li>78: Shows the dialog.</li>
|
||||
<code>gtk_window_destroy</code>, so that the dialog disappears when the
|
||||
user clicks on the button.</li>
|
||||
<li>20: Shows the message dialog.</li>
|
||||
<li>21: Frees <code>err</code> with <code>g_error_free</code>
|
||||
function.</li>
|
||||
<li>23: Frees <code>contents</code>.</li>
|
||||
<li>24: Returns to the caller.</li>
|
||||
</ul>
|
||||
<h3 id="saveas_dialog_response-function">saveas_dialog_response
|
||||
function</h3>
|
||||
<div class="sourceCode" id="cb8"><pre
|
||||
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb8-1"><a href="#cb8-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb8-2"><a href="#cb8-2"></a>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-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>
|
||||
<span id="cb8-4"><a href="#cb8-4"></a> GFile <span class="op">*</span>file<span class="op">;</span></span>
|
||||
<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>
|
||||
<span id="cb8-6"><a href="#cb8-6"></a></span>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<span id="cb8-10"><a href="#cb8-10"></a> g_warning <span class="op">(</span><span class="st">"TfeTextView: gtk_file_chooser_get_file returns non GFile.</span><span class="sc">\n</span><span class="st">"</span><span class="op">);</span></span>
|
||||
<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>
|
||||
<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>
|
||||
<span id="cb8-13"><a href="#cb8-13"></a> <span class="co">// G_IS_FILE(tv->file) && tv->file == file => nothing to do</span></span>
|
||||
<span id="cb8-14"><a href="#cb8-14"></a> <span class="co">// G_IS_FILE(tv->file) && tv->file != file => unref(tv->file), tv->file=file, signal emit</span></span>
|
||||
<span id="cb8-15"><a href="#cb8-15"></a> <span class="co">// tv->file==NULL => tv->file=file, signal emit</span></span>
|
||||
<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">-></span>file<span class="op">)</span> <span class="op">&&</span> <span class="op">(!</span> g_file_equal <span class="op">(</span>tv<span class="op">-></span>file<span class="op">,</span> file<span class="op">)))</span></span>
|
||||
<span id="cb8-17"><a href="#cb8-17"></a> g_object_unref <span class="op">(</span>tv<span class="op">-></span>file<span class="op">);</span></span>
|
||||
<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">-></span>file<span class="op">)</span> <span class="op">&&</span> g_file_equal <span class="op">(</span>tv<span class="op">-></span>file<span class="op">,</span> file<span class="op">)))</span> <span class="op">{</span></span>
|
||||
<span id="cb8-19"><a href="#cb8-19"></a> tv<span class="op">-></span>file <span class="op">=</span> file<span class="op">;</span> <span class="co">// The ownership of 'file' moves to TfeTextView.</span></span>
|
||||
<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>
|
||||
<span id="cb8-21"><a href="#cb8-21"></a> <span class="op">}</span></span>
|
||||
<span id="cb8-22"><a href="#cb8-22"></a> g_object_unref <span class="op">(</span>file<span class="op">);</span></span>
|
||||
<span id="cb8-23"><a href="#cb8-23"></a> <span class="op">}</span> <span class="cf">else</span></span>
|
||||
<span id="cb8-24"><a href="#cb8-24"></a> g_object_unref <span class="op">(</span>file<span class="op">);</span></span>
|
||||
<span id="cb8-25"><a href="#cb8-25"></a> <span class="op">}</span></span>
|
||||
<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>
|
||||
<span id="cb8-27"><a href="#cb8-27"></a><span class="op">}</span></span></code></pre></div>
|
||||
<ul>
|
||||
<li>The function <code>saveas_dialog_response</code> is a signal handler
|
||||
for the “response” signal on GtkFileChooserDialog. This handler analyzes
|
||||
the response and determines whether to save the contents or not.</li>
|
||||
<li>7-25: If the response is <code>GTK_RESPONSE_ACCEPT</code>, the user
|
||||
has clicked on the <code>Save</code> button and the contents will be
|
||||
saved.</li>
|
||||
<li>8: Gets the GFile <code>file</code> from the
|
||||
GtkFileChooserDialog.</li>
|
||||
<li>9-10: If it doesn’t point GFile, a warning message will be output to
|
||||
the log. This is not expected.</li>
|
||||
<li>11: Otherwise, it calls <code>save_file</code> to save the contents
|
||||
to the file.</li>
|
||||
<li>12-22: If <code>save_file</code> has successfully saved the
|
||||
contents, the following will be done.
|
||||
<ul>
|
||||
<li>If <code>tv->file</code> is GFile and <code>file</code> is a
|
||||
different file, unref <code>tv->file</code>.</li>
|
||||
<li>If <code>tv->file</code> is GFile and <code>file</code> points
|
||||
the same file as <code>tv->file</code>, nothing needs to do.
|
||||
Otherwise, <code>tv->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">-></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">-></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">"TfeTextView: The pointer tv->file isn't NULL nor GFile.</span><span class="sc">\n</span><span class="st">"</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">-></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->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 doesn’t 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> doesn’t 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 can’t 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 hasn’t modified, it doesn’t need to be
|
||||
saved.</li>
|
||||
<li>10-11: If <code>tv->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->file</code> doesn’t 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->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">"Save file"</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">"Cancel"</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">"Save"</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">"response"</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>
|
||||
<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
|
||||
into two parts. One is a function which creates GtkFileChooserDialog and
|
||||
the other is a signal handler. The function just creates and shows
|
||||
GtkFileChooserDialog. The rest is done by the handler. It gets Gfile
|
||||
from GtkFileChooserDialog and saves the buffer to the file by calling
|
||||
<code>save_file</code>.</p>
|
||||
<h2 id="open-function">Open function</h2>
|
||||
<p>Open function shows GtkFileChooserDialog to users and prompts them to
|
||||
choose a file. Then it reads the file and puts the text into
|
||||
<figure>
|
||||
<img src="image/saveas.png" alt="Saveas process" />
|
||||
<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>
|
||||
<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
|
||||
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. However, it is
|
||||
encouraged to give a parent window to dialog. This function might be
|
||||
called just after <code>tv</code> has been created. In that case,
|
||||
<code>tv</code> has not been incorporated into the widget hierarchy.
|
||||
Therefore it is impossible to get the top-level window from
|
||||
<code>tv</code>. That’s why the function needs <code>win</code>
|
||||
parameter.</p>
|
||||
created.</p>
|
||||
<p>This function may be called just after <code>tv</code> has been
|
||||
created. In that case, <code>tv</code> has not been incorporated into
|
||||
the widget hierarchy. Therefore it is impossible to get the top-level
|
||||
window from <code>tv</code>. That’s why the function needs
|
||||
<code>win</code> parameter.</p>
|
||||
<p>This function is usually called when the buffer of <code>tv</code> is
|
||||
empty. However, even if the buffer is not empty,
|
||||
<code>tfe_text_view_open</code> doesn’t treat it as an error. If you
|
||||
want to revert the buffer, calling this function is appropriate.
|
||||
Otherwise probably bad things will happen.</p>
|
||||
<div class="sourceCode" id="cb10"><pre
|
||||
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb10-1"><a href="#cb10-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<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>
|
||||
<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>
|
||||
<span id="cb10-4"><a href="#cb10-4"></a> GFile <span class="op">*</span>file<span class="op">;</span></span>
|
||||
<span id="cb10-5"><a href="#cb10-5"></a> <span class="dt">char</span> <span class="op">*</span>contents<span class="op">;</span></span>
|
||||
<span id="cb10-6"><a href="#cb10-6"></a> gsize length<span class="op">;</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="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="cb10-9"><a href="#cb10-9"></a></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="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="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="cb10-13"><a href="#cb10-13"></a> g_warning <span class="op">(</span><span class="st">"TfeTextView: gtk_file_chooser_get_file returns non GFile.</span><span class="sc">\n</span><span class="st">"</span><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="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">&</span>contents<span class="op">,</span> <span class="op">&</span>length<span class="op">,</span> NULL<span class="op">,</span> <span class="op">&</span>err<span class="op">))</span> <span class="op">{</span> <span class="co">/* read error */</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="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="cb10-18"><a href="#cb10-18"></a> GTK_MESSAGE_ERROR<span class="op">,</span> GTK_BUTTONS_CLOSE<span class="op">,</span></span>
|
||||
<span id="cb10-19"><a href="#cb10-19"></a> <span class="st">"%s.</span><span class="sc">\n</span><span class="st">"</span><span class="op">,</span> err<span class="op">-></span>message<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">"response"</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-21"><a href="#cb10-21"></a> gtk_widget_show <span class="op">(</span>message_dialog<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="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="cb10-24"><a href="#cb10-24"></a> <span class="op">}</span> <span class="cf">else</span> <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="cb10-26"><a href="#cb10-26"></a> g_free <span class="op">(</span>contents<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">-></span>file<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">-></span>file<span class="op">);</span></span>
|
||||
<span id="cb10-29"><a href="#cb10-29"></a> tv<span class="op">-></span>file <span class="op">=</span> file<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="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="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="cb10-33"><a href="#cb10-33"></a> <span class="op">}</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="cb10-35"><a href="#cb10-35"></a><span class="op">}</span></span>
|
||||
<span id="cb10-36"><a href="#cb10-36"></a></span>
|
||||
<span id="cb10-37"><a href="#cb10-37"></a><span class="dt">void</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="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="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="cb10-41"><a href="#cb10-41"></a></span>
|
||||
<span id="cb10-42"><a href="#cb10-42"></a> GtkWidget <span class="op">*</span>dialog<span class="op">;</span></span>
|
||||
<span id="cb10-43"><a href="#cb10-43"></a></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">"Open file"</span><span class="op">,</span> win<span class="op">,</span> GTK_FILE_CHOOSER_ACTION_OPEN<span class="op">,</span></span>
|
||||
<span id="cb10-45"><a href="#cb10-45"></a> <span class="st">"Cancel"</span><span class="op">,</span> GTK_RESPONSE_CANCEL<span class="op">,</span></span>
|
||||
<span id="cb10-46"><a href="#cb10-46"></a> <span class="st">"Open"</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">"response"</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>
|
||||
want to revert the buffer, calling this function is appropriate.</p>
|
||||
<p>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.</p>
|
||||
<h3 id="open_dialog_response-function">open_dialog_response
|
||||
function</h3>
|
||||
<div class="sourceCode" id="cb12"><pre
|
||||
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb12-1"><a href="#cb12-1"></a><span class="dt">static</span> <span class="dt">void</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="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="cb12-4"><a href="#cb12-4"></a> GFile <span class="op">*</span>file<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="cb12-6"><a href="#cb12-6"></a> gsize length<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="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="cb12-9"><a href="#cb12-9"></a></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="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="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="cb12-13"><a href="#cb12-13"></a> g_warning <span class="op">(</span><span class="st">"TfeTextView: gtk_file_chooser_get_file returns non GFile.</span><span class="sc">\n</span><span class="st">"</span><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="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">&</span>contents<span class="op">,</span> <span class="op">&</span>length<span class="op">,</span> NULL<span class="op">,</span> <span class="op">&</span>err<span class="op">))</span> <span class="op">{</span> <span class="co">/* read error */</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="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="cb12-18"><a href="#cb12-18"></a> GTK_MESSAGE_ERROR<span class="op">,</span> GTK_BUTTONS_CLOSE<span class="op">,</span> <span class="st">"%s"</span><span class="op">,</span> err<span class="op">-></span>message<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">"response"</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-20"><a href="#cb12-20"></a> gtk_widget_show <span class="op">(</span>message_dialog<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="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="cb12-23"><a href="#cb12-23"></a> <span class="op">}</span> <span class="cf">else</span> <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="cb12-25"><a href="#cb12-25"></a> g_free <span class="op">(</span>contents<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="cb12-27"><a href="#cb12-27"></a> <span class="co">// G_IS_FILE(tv->file) && tv->file == file => unref(tv->file), tv->file=file, emit response with SUCCESS</span></span>
|
||||
<span id="cb12-28"><a href="#cb12-28"></a> <span class="co">// G_IS_FILE(tv->file) && tv->file != file => unref(tv->file), tv->file=file, emit response with SUCCESS, emit change-file</span></span>
|
||||
<span id="cb12-29"><a href="#cb12-29"></a> <span class="co">// tv->file==NULL => tv->file=file, emit response with SUCCESS, emit change-file</span></span>
|
||||
<span id="cb12-30"><a href="#cb12-30"></a> <span class="co">// The order is important. If you unref tv->file first, you can't compare tv->file and file anymore.</span></span>
|
||||
<span id="cb12-31"><a href="#cb12-31"></a> <span class="co">// And open-response signal is emitted after new tv->file is set. Or the handler can't catch the new file.</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">-></span>file<span class="op">)</span> <span class="op">&&</span> g_file_equal <span class="op">(</span>tv<span class="op">-></span>file<span class="op">,</span> file<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="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">-></span>file<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">-></span>file<span class="op">);</span></span>
|
||||
<span id="cb12-36"><a href="#cb12-36"></a> tv<span class="op">-></span>file <span class="op">=</span> file<span class="op">;</span> <span class="co">// The ownership of 'file' moves to TfeTextView</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="cb12-38"><a href="#cb12-38"></a> <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="cb12-40"><a href="#cb12-40"></a><span class="op">}</span></span></code></pre></div>
|
||||
<ul>
|
||||
<li>37-50: <code>tfe_text_view_open</code> function.</li>
|
||||
<li>44-47: Creates GtkFileChooserDialog. The title is “Open file”.
|
||||
Transient parent window is the top-level window of the application,
|
||||
which is given by the caller. The action is open mode. The buttons are
|
||||
Cancel and Open.</li>
|
||||
<li>48: connects the “response” signal of the dialog and
|
||||
<code>open_dialog_response</code> signal handler.</li>
|
||||
<li>49: Shows the dialog.</li>
|
||||
<li>1-35: <code>open_dialog_response</code> signal handler.</li>
|
||||
<li>10-11: If the response from GtkFileChooserDialog is not
|
||||
<code>GTK_RESPONSE_ACCEPT</code>, the user has clicked on the “Cancel”
|
||||
button or close button on the header bar. Then, “open-response” signal
|
||||
is emitted. The parameter of the signal is
|
||||
<code>TFE_OPEN_RESPONSE_CANCEL</code>.</li>
|
||||
<li>12-14: Gets the pointer to the Gfile by
|
||||
<li>2: The handler <code>open_dialog_response</code> has three
|
||||
parameters.
|
||||
<ul>
|
||||
<li>GtkWidget *dialog: FileChooserDialog on which the “response” signal
|
||||
is emitted.</li>
|
||||
<li>gint response: response ID</li>
|
||||
<li>TfeTextView *tv: textview to put the contents to</li>
|
||||
</ul></li>
|
||||
<li>10-11: If the response is not <code>GTK_RESPONSE_ACCEPT</code>, the
|
||||
user has clicked on the “Cancel” button or close button on the header
|
||||
bar. Then, “open-response” signal is emitted. The parameter of the
|
||||
signal is <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 doesn’t point GFile, maybe
|
||||
an error has occurred. Then it emits “open-response” signal with the
|
||||
parameter <code>TFE_OPEN_RESPONSE_ERROR</code>.</li>
|
||||
<li>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
|
||||
an error has occurred. It is not expected, though. Then it emits
|
||||
“open-response” signal with the parameter
|
||||
<code>TFE_OPEN_RESPONSE_ERROR</code>.</li>
|
||||
<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
|
||||
<code>TFE_OPEN_RESPONSE_ERROR</code>.</li>
|
||||
<li>24-33: If the file has successfully been read, then the text is
|
||||
inserted to GtkTextBuffer, frees the temporary buffer pointed by
|
||||
<code>contents</code> and sets <code>tv->file</code> to point the
|
||||
file (no duplication is not necessary). Then, it emits “open-response”
|
||||
signal with the parameter <code>TFE_OPEN_RESPONSE_SUCCESS</code> and
|
||||
emits “change-file” signal.</li>
|
||||
<li>34: destroys GtkFileCooserDialog.</li>
|
||||
<li>23-38: If the file has been successfully read, the following is
|
||||
carried out. (It is not simple.)
|
||||
<ul>
|
||||
<li>the text is inserted to GtkTextBuffer</li>
|
||||
<li>the temporary buffer <code>contents</code> is freed</li>
|
||||
<li>modify-bit is set to FALSE</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->file</code> isn’t NULL,
|
||||
<code>g_object_unref(tv->file)</code> is called</li>
|
||||
<li><code>tv->file</code> is assigned with <code>file</code></li>
|
||||
</ul></li>
|
||||
<li>39: destroys GtkFileChooserDialog.</li>
|
||||
</ul>
|
||||
<p>Now let’s think about the whole process between the caller and
|
||||
TfeTextView. It is shown in the following diagram and you would think
|
||||
that it is really complicated. Because signal is the only way for
|
||||
GtkFileChooserDialog to communicate with others. In GTK 3,
|
||||
<code>gtk_dialog_run</code> function is available. It simplifies the
|
||||
process. However, in GTK 4, <code>gtk_dialog_run</code> is unavailable
|
||||
any more.</p>
|
||||
<h3 id="tfe_text_view_open-function">tfe_text_view_open function</h3>
|
||||
<div class="sourceCode" id="cb13"><pre
|
||||
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb13-1"><a href="#cb13-1"></a><span class="dt">void</span></span>
|
||||
<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>
|
||||
<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>
|
||||
<span id="cb13-4"><a href="#cb13-4"></a> <span class="co">// 'win' is used for a transient window of the GtkFileChooserDialog.</span></span>
|
||||
<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">"Open file"</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">"Cancel"</span><span class="op">,</span> GTK_RESPONSE_CANCEL<span class="op">,</span> <span class="st">"Open"</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">"response"</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>
|
||||
<img src="image/open.png" alt="Caller and TfeTextView" />
|
||||
<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>
|
||||
<li>The handler out of the TfeTextView receives the signal.</li>
|
||||
</ol>
|
||||
<h2 id="getting-gfile">Getting Gfile</h2>
|
||||
<p><code>gtk_text_view_get_file</code> is a simple function shown as
|
||||
follows.</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>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="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="cb11-4"><a href="#cb11-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">-></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">-></span>file<span class="op">);</span></span>
|
||||
<span id="cb11-7"><a href="#cb11-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="cb11-9"><a href="#cb11-9"></a><span class="op">}</span></span></code></pre></div>
|
||||
<h2 id="getting-gfile-in-tfetextview">Getting GFile in TfeTextView</h2>
|
||||
<p>You can get the GFile in a TfeTextView instance with
|
||||
<code>tfe_text_view_get_file</code>. It is very simple.</p>
|
||||
<div class="sourceCode" id="cb14"><pre
|
||||
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="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="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="cb14-4"><a href="#cb14-4"></a></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">-></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">-></span>file<span class="op">);</span></span>
|
||||
<span id="cb14-7"><a href="#cb14-7"></a> <span class="cf">else</span></span>
|
||||
<span id="cb14-8"><a href="#cb14-8"></a> <span class="cf">return</span> NULL<span class="op">;</span></span>
|
||||
<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->file</code>.
|
||||
Otherwise, if the caller frees the GFile object,
|
||||
<code>tv->file</code> is no more guaranteed to point the GFile.
|
||||
Another reason to use <code>g_file_dup</code> is that GFile isn’t
|
||||
thread-safe. If you use GFile in the different thread, the duplication
|
||||
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>
|
||||
<h2 id="the-api-document-and-source-file-of-tfetextview.c">The API
|
||||
document and source file of tfetextview.c</h2>
|
||||
<p>Refer API document of TfeTextView. Its original markdown file is
|
||||
under the directory <code>src/tfetextview</code>.</p>
|
||||
<p>All the source files are listed in <a href="sec16.html">Section
|
||||
16</a>. You can find them under src/tfe5 and src/tfetextview
|
||||
<p>Refer <a
|
||||
href="https://toshiocp.github.io/Gtk4-tutorial/tfetextview_doc.html">API
|
||||
document of TfeTextView</a>. The markdown file is under the directory
|
||||
<code>src/tfetextview</code>.</p>
|
||||
<p>You can find all the TfeTextView source codes under src/tfetextview
|
||||
directories.</p>
|
||||
</div>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
|
||||
|
|
458
docs/sec14.html
|
@ -5,7 +5,7 @@
|
|||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
||||
<title>Gtk4 tutorial</title>
|
||||
<title>GTK 4 tutorial</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
|
@ -112,30 +112,43 @@
|
|||
</div>
|
||||
</nav>
|
||||
<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. There’s 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(GtkNotebook *nb);</span>
|
||||
<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. There’s 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-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-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-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-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>
|
||||
<p>This header file describes the public functions in <code>tfenotebook.c</code>.</p>
|
||||
<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>
|
||||
<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>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>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>
|
||||
<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>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>
|
||||
</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>
|
||||
<li><code>tfe_text_view_save</code></li>
|
||||
<li><code>tef_text_view_open</code></li>
|
||||
|
@ -143,199 +156,298 @@
|
|||
<li><code>tfe_text_view_new</code></li>
|
||||
</ul>
|
||||
<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 let’s look at the program of each function.</p>
|
||||
<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>
|
||||
<span id="cb2-2"><a href="#cb2-2"></a>get_untitled () {</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-4"><a href="#cb2-4"></a> <span class="cf">if</span> (++c == <span class="dv">0</span>) </span>
|
||||
<span id="cb2-5"><a href="#cb2-5"></a> <span class="cf">return</span> g_strdup_printf(<span class="st">"Untitled"</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="dt">static</span> <span class="dt">char</span><span class="op">*</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-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-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">"Untitled"</span><span class="op">);</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">"Untitled%u"</span>, c);</span>
|
||||
<span id="cb2-8"><a href="#cb2-8"></a>}</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">"Untitled%u"</span><span class="op">,</span> c<span class="op">);</span></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-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-12"><a href="#cb2-12"></a> GtkWidget *scr = gtk_scrolled_window_new ();</span>
|
||||
<span id="cb2-13"><a href="#cb2-13"></a> GtkNotebookPage *nbp;</span>
|
||||
<span id="cb2-14"><a href="#cb2-14"></a> GtkWidget *lab;</span>
|
||||
<span id="cb2-15"><a href="#cb2-15"></a> <span class="dt">int</span> i;</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 <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 <span class="op">*</span>nbp<span class="op">;</span></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 class="op">;</span></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-18"><a href="#cb2-18"></a> gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);</span>
|
||||
<span id="cb2-19"><a href="#cb2-19"></a> lab = gtk_label_new (filename);</span>
|
||||
<span id="cb2-20"><a href="#cb2-20"></a> i = gtk_notebook_append_page (nb, scr, lab);</span>
|
||||
<span id="cb2-21"><a href="#cb2-21"></a> nbp = gtk_notebook_get_page (nb, scr);</span>
|
||||
<span id="cb2-22"><a href="#cb2-22"></a> g_object_set (nbp, <span class="st">"tab-expand"</span>, TRUE, NULL);</span>
|
||||
<span id="cb2-23"><a href="#cb2-23"></a> gtk_notebook_set_current_page (nb, i);</span>
|
||||
<span id="cb2-24"><a href="#cb2-24"></a> g_signal_connect (GTK_TEXT_VIEW (tv), <span class="st">"change-file"</span>, G_CALLBACK (file_changed_cb), nb);</span>
|
||||
<span id="cb2-25"><a href="#cb2-25"></a>}</span>
|
||||
<span id="cb2-26"><a href="#cb2-26"></a></span>
|
||||
<span id="cb2-27"><a href="#cb2-27"></a><span class="dt">void</span></span>
|
||||
<span id="cb2-28"><a href="#cb2-28"></a>notebook_page_new (GtkNotebook *nb) {</span>
|
||||
<span id="cb2-29"><a href="#cb2-29"></a> g_return_if_fail(GTK_IS_NOTEBOOK (nb));</span>
|
||||
<span id="cb2-30"><a href="#cb2-30"></a></span>
|
||||
<span id="cb2-31"><a href="#cb2-31"></a> GtkWidget *tv;</span>
|
||||
<span id="cb2-32"><a href="#cb2-32"></a> <span class="dt">char</span> *filename;</span>
|
||||
<span id="cb2-33"><a href="#cb2-33"></a></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-35"><a href="#cb2-35"></a> <span class="cf">return</span>;</span>
|
||||
<span id="cb2-36"><a href="#cb2-36"></a> filename = get_untitled ();</span>
|
||||
<span id="cb2-37"><a href="#cb2-37"></a> notebook_page_build (nb, tv, filename);</span>
|
||||
<span id="cb2-38"><a href="#cb2-38"></a>}</span></code></pre></div>
|
||||
<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> 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> 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> 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> g_object_set <span class="op">(</span>nbp<span class="op">,</span> <span class="st">"tab-expand"</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> 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> g_signal_connect <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> <span class="st">"change-file"</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><span class="op">}</span></span>
|
||||
<span id="cb2-25"><a href="#cb2-25"></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>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> 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></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> <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>
|
||||
<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">return</span><span class="op">;</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> 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> g_free <span class="op">(</span>filename<span class="op">);</span></span>
|
||||
<span id="cb2-38"><a href="#cb2-38"></a><span class="op">}</span></span></code></pre></div>
|
||||
<ul>
|
||||
<li>27-38: <code>notebook_page_new</code> function.</li>
|
||||
<li>29: <code>g_return_if_fail</code> is used to check the argument.</li>
|
||||
<li>34: Creates TfeTextView object. If it fails, it returns to the caller.</li>
|
||||
<li>36: Creates filename, which is “Untitled”, “Untitled1”, … .</li>
|
||||
<li>26-38: <code>notebook_page_new</code> function.</li>
|
||||
<li>28: <code>g_return_if_fail</code> is used to check the
|
||||
argument.</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>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>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<the integer>”, 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>37: calls <code>notebook_page_build</code> to build the contents of the page.</li>
|
||||
<li>10- 25: <code>notebook_page_build</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>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<the
|
||||
integer>”, 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 doesn’t 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>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>18: Inserts <code>tv</code> to GtkscrolledWindow as a child.</li>
|
||||
<li>19-20: Creates GtkLabel, then appends <code>scr</code> and <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>23: Sets the current page of <code>nb</code> to the newly created page.</li>
|
||||
<li>24: Connects “change-file” signal and <code>file_changed_cb</code> handler.</li>
|
||||
<li>17: Inserts <code>tv</code> to GtkscrolledWindow as a child.</li>
|
||||
<li>18-19: Creates GtkLabel, then appends <code>scr</code> and
|
||||
<code>lab</code> to the GtkNotebook instance <code>nb</code>.</li>
|
||||
<li>20-21: 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>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>
|
||||
<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>
|
||||
<span id="cb3-2"><a href="#cb3-2"></a>notebook_page_new_with_file (GtkNotebook *nb, GFile *file) {</span>
|
||||
<span id="cb3-3"><a href="#cb3-3"></a> g_return_if_fail(GTK_IS_NOTEBOOK (nb));</span>
|
||||
<span id="cb3-4"><a href="#cb3-4"></a> g_return_if_fail(G_IS_FILE (file));</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="dt">void</span></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-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-6"><a href="#cb3-6"></a> GtkWidget *tv;</span>
|
||||
<span id="cb3-7"><a href="#cb3-7"></a> <span class="dt">char</span> *filename;</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> <span class="op">*</span>filename<span class="op">;</span></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-10"><a href="#cb3-10"></a> <span class="cf">return</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-12"><a href="#cb3-12"></a> notebook_page_build (nb, tv, filename);</span>
|
||||
<span id="cb3-13"><a href="#cb3-13"></a>}</span></code></pre></div>
|
||||
<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="op">;</span> <span class="co">/* read error */</span></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 <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> 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>
|
||||
<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>11-12: Gets the filename and builds the contents of the page.</li>
|
||||
<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>11-13: Gets the filename, builds the contents of the page and frees
|
||||
<code>filename</code>.</li>
|
||||
</ul>
|
||||
<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>
|
||||
<span id="cb4-2"><a href="#cb4-2"></a>open_response (TfeTextView *tv, <span class="dt">int</span> response, GtkNotebook *nb) {</span>
|
||||
<span id="cb4-3"><a href="#cb4-3"></a> GFile *file;</span>
|
||||
<span id="cb4-4"><a href="#cb4-4"></a> <span class="dt">char</span> *filename;</span>
|
||||
<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>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-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-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-7"><a href="#cb4-7"></a> g_object_ref_sink (tv);</span>
|
||||
<span id="cb4-8"><a href="#cb4-8"></a> g_object_unref (tv);</span>
|
||||
<span id="cb4-9"><a href="#cb4-9"></a> }<span class="cf">else</span> {</span>
|
||||
<span id="cb4-10"><a href="#cb4-10"></a> filename = g_file_get_basename (file);</span>
|
||||
<span id="cb4-11"><a href="#cb4-11"></a> g_object_unref (file);</span>
|
||||
<span id="cb4-12"><a href="#cb4-12"></a> notebook_page_build (nb, GTK_WIDGET (tv), filename);</span>
|
||||
<span id="cb4-13"><a href="#cb4-13"></a> }</span>
|
||||
<span id="cb4-14"><a href="#cb4-14"></a>}</span>
|
||||
<span id="cb4-15"><a href="#cb4-15"></a></span>
|
||||
<span id="cb4-16"><a href="#cb4-16"></a><span class="dt">void</span></span>
|
||||
<span id="cb4-17"><a href="#cb4-17"></a>notebook_page_open (GtkNotebook *nb) {</span>
|
||||
<span id="cb4-18"><a href="#cb4-18"></a> g_return_if_fail(GTK_IS_NOTEBOOK (nb));</span>
|
||||
<span id="cb4-19"><a href="#cb4-19"></a></span>
|
||||
<span id="cb4-20"><a href="#cb4-20"></a> GtkWidget *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 <span class="op">(</span>tv<span class="op">);</span></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="op">}</span><span class="cf">else</span> <span class="op">{</span></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> 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> g_object_unref <span class="op">(</span>file<span class="op">);</span></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> g_free <span class="op">(</span>filename<span class="op">);</span></span>
|
||||
<span id="cb4-15"><a href="#cb4-15"></a> <span class="op">}</span></span>
|
||||
<span id="cb4-16"><a href="#cb4-16"></a><span class="op">}</span></span>
|
||||
<span id="cb4-17"><a href="#cb4-17"></a></span>
|
||||
<span id="cb4-18"><a href="#cb4-18"></a><span class="dt">void</span></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> 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-22"><a href="#cb4-22"></a> <span class="cf">if</span> ((tv = tfe_text_view_new ()) == NULL)</span>
|
||||
<span id="cb4-23"><a href="#cb4-23"></a> <span class="cf">return</span>;</span>
|
||||
<span id="cb4-24"><a href="#cb4-24"></a> g_signal_connect (TFE_TEXT_VIEW (tv), <span class="st">"open-response"</span>, G_CALLBACK (open_response), nb);</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-26"><a href="#cb4-26"></a>}</span></code></pre></div>
|
||||
<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>
|
||||
<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> <span class="cf">return</span><span class="op">;</span></span>
|
||||
<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">"open-response"</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>
|
||||
<li>16-26: <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: Connects the signal “open-response” and the handler <code>open_response</code>.</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>1-14: <code>open_response</code> handler.</li>
|
||||
<li>6-8: If the response code is NOT <code>TFE_OPEN_RESPONSE_SUCCESS</code> or <code>tfe_text_view_get_file</code> doesn’t 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> hasn’t 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>9-13: Otherwise, everything is okay. Gets the filename, builds the contents of the page.</li>
|
||||
<li>18-28: <code>notebook_page_open</code> function.</li>
|
||||
<li>24-25: Creates TfeTextView object. If NULL is returned, an error has
|
||||
happened. Then, it returns to the caller.</li>
|
||||
<li>26: Connects the signal “open-response” and the handler
|
||||
<code>open_response</code>.</li>
|
||||
<li>27: 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>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> hasn’t 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>
|
||||
<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 doesn’t 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>
|
||||
<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>
|
||||
<h2 id="floating-reference">Floating reference</h2>
|
||||
<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 doesn’t 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 doesn’t 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'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 doesn’t need to decrease tv’s
|
||||
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'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.</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>
|
||||
<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>
|
||||
<span id="cb5-2"><a href="#cb5-2"></a>notebook_page_close (GtkNotebook *nb) {</span>
|
||||
<span id="cb5-3"><a href="#cb5-3"></a> g_return_if_fail(GTK_IS_NOTEBOOK (nb));</span>
|
||||
<span id="cb5-4"><a href="#cb5-4"></a></span>
|
||||
<span id="cb5-5"><a href="#cb5-5"></a> GtkWidget *win;</span>
|
||||
<span id="cb5-6"><a href="#cb5-6"></a> <span class="dt">int</span> i;</span>
|
||||
<span id="cb5-7"><a href="#cb5-7"></a></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="cb5-9"><a href="#cb5-9"></a> win = gtk_widget_get_ancestor (GTK_WIDGET (nb), GTK_TYPE_WINDOW);</span>
|
||||
<span id="cb5-10"><a href="#cb5-10"></a> gtk_window_destroy(GTK_WINDOW (win));</span>
|
||||
<span id="cb5-11"><a href="#cb5-11"></a> } <span class="cf">else</span> {</span>
|
||||
<span id="cb5-12"><a href="#cb5-12"></a> i = gtk_notebook_get_current_page (nb);</span>
|
||||
<span id="cb5-13"><a href="#cb5-13"></a> gtk_notebook_remove_page (GTK_NOTEBOOK (nb), i);</span>
|
||||
<span id="cb5-14"><a href="#cb5-14"></a> }</span>
|
||||
<span id="cb5-15"><a href="#cb5-15"></a>}</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>
|
||||
<div class="sourceCode" id="cb8"><pre
|
||||
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb8-1"><a href="#cb8-1"></a><span class="dt">void</span></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="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="cb8-4"><a href="#cb8-4"></a></span>
|
||||
<span id="cb8-5"><a href="#cb8-5"></a> GtkWidget <span class="op">*</span>win<span class="op">;</span></span>
|
||||
<span id="cb8-6"><a href="#cb8-6"></a> <span class="dt">int</span> i<span class="op">;</span></span>
|
||||
<span id="cb8-7"><a href="#cb8-7"></a></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="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="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="cb8-11"><a href="#cb8-11"></a> <span class="op">}</span> <span class="cf">else</span> <span class="op">{</span></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="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="cb8-14"><a href="#cb8-14"></a> <span class="op">}</span></span>
|
||||
<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>
|
||||
<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>11-13: Otherwise, removes the current page.</li>
|
||||
<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>11-13: Otherwise, removes the current page. The child widget
|
||||
(TfeTextView) is also destroyed.</li>
|
||||
</ul>
|
||||
<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>
|
||||
<span id="cb6-2"><a href="#cb6-2"></a>get_current_textview (GtkNotebook *nb) {</span>
|
||||
<span id="cb6-3"><a href="#cb6-3"></a> <span class="dt">int</span> i;</span>
|
||||
<span id="cb6-4"><a href="#cb6-4"></a> GtkWidget *scr;</span>
|
||||
<span id="cb6-5"><a href="#cb6-5"></a> GtkWidget *tv;</span>
|
||||
<span id="cb6-6"><a href="#cb6-6"></a></span>
|
||||
<span id="cb6-7"><a href="#cb6-7"></a> i = gtk_notebook_get_current_page (nb);</span>
|
||||
<span id="cb6-8"><a href="#cb6-8"></a> scr = gtk_notebook_get_nth_page (nb, i);</span>
|
||||
<span id="cb6-9"><a href="#cb6-9"></a> tv = gtk_scrolled_window_get_child (GTK_SCROLLED_WINDOW (scr));</span>
|
||||
<span id="cb6-10"><a href="#cb6-10"></a> <span class="cf">return</span> TFE_TEXT_VIEW (tv);</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">void</span></span>
|
||||
<span id="cb6-14"><a href="#cb6-14"></a>notebook_page_save (GtkNotebook *nb) {</span>
|
||||
<span id="cb6-15"><a href="#cb6-15"></a> g_return_if_fail(GTK_IS_NOTEBOOK (nb));</span>
|
||||
<span id="cb6-16"><a href="#cb6-16"></a></span>
|
||||
<span id="cb6-17"><a href="#cb6-17"></a> TfeTextView *tv;</span>
|
||||
<span id="cb6-18"><a href="#cb6-18"></a></span>
|
||||
<span id="cb6-19"><a href="#cb6-19"></a> tv = get_current_textview (nb);</span>
|
||||
<span id="cb6-20"><a href="#cb6-20"></a> tfe_text_view_save (TFE_TEXT_VIEW (tv));</span>
|
||||
<span id="cb6-21"><a href="#cb6-21"></a>}</span></code></pre></div>
|
||||
<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> TfeTextView <span class="op">*</span></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="cb9-3"><a href="#cb9-3"></a> <span class="dt">int</span> i<span class="op">;</span></span>
|
||||
<span id="cb9-4"><a href="#cb9-4"></a> GtkWidget <span class="op">*</span>scr<span class="op">;</span></span>
|
||||
<span id="cb9-5"><a href="#cb9-5"></a> GtkWidget <span class="op">*</span>tv<span class="op">;</span></span>
|
||||
<span id="cb9-6"><a href="#cb9-6"></a></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="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="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="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="cb9-11"><a href="#cb9-11"></a><span class="op">}</span></span>
|
||||
<span id="cb9-12"><a href="#cb9-12"></a></span>
|
||||
<span id="cb9-13"><a href="#cb9-13"></a><span class="dt">void</span></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="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="cb9-16"><a href="#cb9-16"></a></span>
|
||||
<span id="cb9-17"><a href="#cb9-17"></a> TfeTextView <span class="op">*</span>tv<span class="op">;</span></span>
|
||||
<span id="cb9-18"><a href="#cb9-18"></a></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="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>
|
||||
<li>13-21: <code>notebook_page_save</code>.</li>
|
||||
<li>19: Gets TfeTextView belongs to the current page.</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>8: Gets the child widget <code>scr</code>, which is a 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>
|
||||
<li>8: Gets the child widget <code>scr</code>, which is a
|
||||
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>
|
||||
<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>
|
||||
<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>
|
||||
<span id="cb7-2"><a href="#cb7-2"></a>file_changed_cb (TfeTextView *tv, GtkNotebook *nb) {</span>
|
||||
<span id="cb7-3"><a href="#cb7-3"></a> GtkWidget *scr;</span>
|
||||
<span id="cb7-4"><a href="#cb7-4"></a> GtkWidget *label;</span>
|
||||
<span id="cb7-5"><a href="#cb7-5"></a> GFile *file;</span>
|
||||
<span id="cb7-6"><a href="#cb7-6"></a> <span class="dt">char</span> *filename;</span>
|
||||
<span id="cb7-7"><a href="#cb7-7"></a></span>
|
||||
<span id="cb7-8"><a href="#cb7-8"></a> file = tfe_text_view_get_file (tv);</span>
|
||||
<span id="cb7-9"><a href="#cb7-9"></a> scr = gtk_widget_get_parent (GTK_WIDGET (tv));</span>
|
||||
<span id="cb7-10"><a href="#cb7-10"></a> <span class="cf">if</span> (G_IS_FILE (file)) {</span>
|
||||
<span id="cb7-11"><a href="#cb7-11"></a> filename = g_file_get_basename (file);</span>
|
||||
<span id="cb7-12"><a href="#cb7-12"></a> g_object_unref (file);</span>
|
||||
<span id="cb7-13"><a href="#cb7-13"></a> } <span class="cf">else</span></span>
|
||||
<span id="cb7-14"><a href="#cb7-14"></a> filename = get_untitled ();</span>
|
||||
<span id="cb7-15"><a href="#cb7-15"></a> label = gtk_label_new (filename);</span>
|
||||
<span id="cb7-16"><a href="#cb7-16"></a> g_free (filename);</span>
|
||||
<span id="cb7-17"><a href="#cb7-17"></a> gtk_notebook_set_tab_label (nb, scr, label);</span>
|
||||
<span id="cb7-18"><a href="#cb7-18"></a>}</span></code></pre></div>
|
||||
<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 the
|
||||
GtkNotebookPage.</p>
|
||||
<div class="sourceCode" id="cb10"><pre
|
||||
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb10-1"><a href="#cb10-1"></a><span class="dt">static</span> <span class="dt">void</span></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="cb10-3"><a href="#cb10-3"></a> GtkWidget <span class="op">*</span>scr<span class="op">;</span></span>
|
||||
<span id="cb10-4"><a href="#cb10-4"></a> GtkWidget <span class="op">*</span>label<span class="op">;</span></span>
|
||||
<span id="cb10-5"><a href="#cb10-5"></a> GFile <span class="op">*</span>file<span class="op">;</span></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="cb10-7"><a href="#cb10-7"></a></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="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="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="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="cb10-12"><a href="#cb10-12"></a> g_object_unref <span class="op">(</span>file<span class="op">);</span></span>
|
||||
<span id="cb10-13"><a href="#cb10-13"></a> <span class="op">}</span> <span class="cf">else</span></span>
|
||||
<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>
|
||||
<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>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>
|
||||
<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>
|
||||
<li>9: Gets the GkScrolledWindow instance which is the parent widget of
|
||||
<code>tv</code>.</li>
|
||||
<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>
|
||||
<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>
|
||||
</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>
|
||||
|
|
|
@ -224,7 +224,7 @@ substitute “textview” for the selector.</p>
|
|||
<pre><code>textview {color: yellow; ...}</code></pre>
|
||||
<p>Class, ID and some other things can be applied to the selector like
|
||||
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>
|
||||
<p>In line 30, the CSS is a string.</p>
|
||||
<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
|
||||
version.</p>
|
||||
<h2 id="source-files">source files</h2>
|
||||
<p>The source files of the text editor <code>tfe</code> will be shown in
|
||||
the next section.</p>
|
||||
<p>You can also download the files from the <a
|
||||
<p>You can download the files from the <a
|
||||
href="https://github.com/ToshioCP/Gtk4-tutorial">repository</a>. There
|
||||
are two options.</p>
|
||||
<ul>
|
||||
|
|
664
docs/sec16.html
|
@ -5,7 +5,7 @@
|
|||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
||||
<title>Gtk4 tutorial</title>
|
||||
<title>GTK 4 tutorial</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
|
@ -111,644 +111,54 @@
|
|||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<h1 id="tfe5-source-files">tfe5 source files</h1>
|
||||
<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 shown in the later subsections. How to download them is written at the end of the <a href="sec15.html">previous section</a>.</p>
|
||||
<h1 id="how-to-build-tfe-text-file-editor">How to build tfe (text file
|
||||
editor)</h1>
|
||||
<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>
|
||||
<ul>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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 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> 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>
|
||||
<p>This is a very simple editor. It is a good practice for you to add more features.</p>
|
||||
<h2 id="meson.build">meson.build</h2>
|
||||
<div class="sourceCode" id="cb2"><pre class="sourceCode numberSource numberLines"><code class="sourceCode"><span id="cb2-1"><a href="#cb2-1"></a>project('tfe', 'c')</span>
|
||||
<span id="cb2-2"><a href="#cb2-2"></a></span>
|
||||
<span id="cb2-3"><a href="#cb2-3"></a>gtkdep = dependency('gtk4')</span>
|
||||
<span id="cb2-4"><a href="#cb2-4"></a></span>
|
||||
<span id="cb2-5"><a href="#cb2-5"></a>gnome=import('gnome')</span>
|
||||
<span id="cb2-6"><a href="#cb2-6"></a>resources = gnome.compile_resources('resources','tfe.gresource.xml')</span>
|
||||
<span id="cb2-7"><a href="#cb2-7"></a></span>
|
||||
<span id="cb2-8"><a href="#cb2-8"></a>sourcefiles=files('tfeapplication.c', 'tfenotebook.c', '../tfetextview/tfetextview.c')</span>
|
||||
<span id="cb2-9"><a href="#cb2-9"></a></span>
|
||||
<span id="cb2-10"><a href="#cb2-10"></a>executable('tfe', 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"><?xml</span> version="1.0" encoding="UTF-8"<span class="kw">?></span></span>
|
||||
<span id="cb3-2"><a href="#cb3-2"></a><span class="kw"><gresources></span></span>
|
||||
<span id="cb3-3"><a href="#cb3-3"></a> <span class="kw"><gresource</span><span class="ot"> prefix=</span><span class="st">"/com/github/ToshioCP/tfe"</span><span class="kw">></span></span>
|
||||
<span id="cb3-4"><a href="#cb3-4"></a> <span class="kw"><file></span>tfe.ui<span class="kw"></file></span></span>
|
||||
<span id="cb3-5"><a href="#cb3-5"></a> <span class="kw"></gresource></span></span>
|
||||
<span id="cb3-6"><a href="#cb3-6"></a><span class="kw"></gresources></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"><?xml</span> version="1.0" encoding="UTF-8"<span class="kw">?></span></span>
|
||||
<span id="cb4-2"><a href="#cb4-2"></a><span class="kw"><interface></span></span>
|
||||
<span id="cb4-3"><a href="#cb4-3"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkApplicationWindow"</span><span class="ot"> id=</span><span class="st">"win"</span><span class="kw">></span></span>
|
||||
<span id="cb4-4"><a href="#cb4-4"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"title"</span><span class="kw">></span>file editor<span class="kw"></property></span></span>
|
||||
<span id="cb4-5"><a href="#cb4-5"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"default-width"</span><span class="kw">></span>600<span class="kw"></property></span></span>
|
||||
<span id="cb4-6"><a href="#cb4-6"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"default-height"</span><span class="kw">></span>400<span class="kw"></property></span></span>
|
||||
<span id="cb4-7"><a href="#cb4-7"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb4-8"><a href="#cb4-8"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkBox"</span><span class="ot"> id=</span><span class="st">"boxv"</span><span class="kw">></span></span>
|
||||
<span id="cb4-9"><a href="#cb4-9"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"orientation"</span><span class="kw">></span>GTK_ORIENTATION_VERTICAL<span class="kw"></property></span></span>
|
||||
<span id="cb4-10"><a href="#cb4-10"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb4-11"><a href="#cb4-11"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkBox"</span><span class="ot"> id=</span><span class="st">"boxh"</span><span class="kw">></span></span>
|
||||
<span id="cb4-12"><a href="#cb4-12"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"orientation"</span><span class="kw">></span>GTK_ORIENTATION_HORIZONTAL<span class="kw"></property></span></span>
|
||||
<span id="cb4-13"><a href="#cb4-13"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb4-14"><a href="#cb4-14"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkLabel"</span><span class="ot"> id=</span><span class="st">"dmy1"</span><span class="kw">></span></span>
|
||||
<span id="cb4-15"><a href="#cb4-15"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"width-chars"</span><span class="kw">></span>10<span class="kw"></property></span></span>
|
||||
<span id="cb4-16"><a href="#cb4-16"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb4-17"><a href="#cb4-17"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb4-18"><a href="#cb4-18"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb4-19"><a href="#cb4-19"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkButton"</span><span class="ot"> id=</span><span class="st">"btnn"</span><span class="kw">></span></span>
|
||||
<span id="cb4-20"><a href="#cb4-20"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span>New<span class="kw"></property></span></span>
|
||||
<span id="cb4-21"><a href="#cb4-21"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb4-22"><a href="#cb4-22"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb4-23"><a href="#cb4-23"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb4-24"><a href="#cb4-24"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkButton"</span><span class="ot"> id=</span><span class="st">"btno"</span><span class="kw">></span></span>
|
||||
<span id="cb4-25"><a href="#cb4-25"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span>Open<span class="kw"></property></span></span>
|
||||
<span id="cb4-26"><a href="#cb4-26"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb4-27"><a href="#cb4-27"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb4-28"><a href="#cb4-28"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb4-29"><a href="#cb4-29"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkLabel"</span><span class="ot"> id=</span><span class="st">"dmy2"</span><span class="kw">></span></span>
|
||||
<span id="cb4-30"><a href="#cb4-30"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"hexpand"</span><span class="kw">></span>TRUE<span class="kw"></property></span></span>
|
||||
<span id="cb4-31"><a href="#cb4-31"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb4-32"><a href="#cb4-32"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb4-33"><a href="#cb4-33"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb4-34"><a href="#cb4-34"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkButton"</span><span class="ot"> id=</span><span class="st">"btns"</span><span class="kw">></span></span>
|
||||
<span id="cb4-35"><a href="#cb4-35"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span>Save<span class="kw"></property></span></span>
|
||||
<span id="cb4-36"><a href="#cb4-36"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb4-37"><a href="#cb4-37"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb4-38"><a href="#cb4-38"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb4-39"><a href="#cb4-39"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkButton"</span><span class="ot"> id=</span><span class="st">"btnc"</span><span class="kw">></span></span>
|
||||
<span id="cb4-40"><a href="#cb4-40"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span>Close<span class="kw"></property></span></span>
|
||||
<span id="cb4-41"><a href="#cb4-41"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb4-42"><a href="#cb4-42"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb4-43"><a href="#cb4-43"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb4-44"><a href="#cb4-44"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkLabel"</span><span class="ot"> id=</span><span class="st">"dmy3"</span><span class="kw">></span></span>
|
||||
<span id="cb4-45"><a href="#cb4-45"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"width-chars"</span><span class="kw">></span>10<span class="kw"></property></span></span>
|
||||
<span id="cb4-46"><a href="#cb4-46"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb4-47"><a href="#cb4-47"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb4-48"><a href="#cb4-48"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb4-49"><a href="#cb4-49"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb4-50"><a href="#cb4-50"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb4-51"><a href="#cb4-51"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkNotebook"</span><span class="ot"> id=</span><span class="st">"nb"</span><span class="kw">></span></span>
|
||||
<span id="cb4-52"><a href="#cb4-52"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"scrollable"</span><span class="kw">></span>TRUE<span class="kw"></property></span></span>
|
||||
<span id="cb4-53"><a href="#cb4-53"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"hexpand"</span><span class="kw">></span>TRUE<span class="kw"></property></span></span>
|
||||
<span id="cb4-54"><a href="#cb4-54"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"vexpand"</span><span class="kw">></span>TRUE<span class="kw"></property></span></span>
|
||||
<span id="cb4-55"><a href="#cb4-55"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb4-56"><a href="#cb4-56"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb4-57"><a href="#cb4-57"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb4-58"><a href="#cb4-58"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb4-59"><a href="#cb4-59"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb4-60"><a href="#cb4-60"></a><span class="kw"></interface></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"><gtk/gtk.h></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">"../tfetextview/tfetextview.h"</span></span>
|
||||
<span id="cb5-4"><a href="#cb5-4"></a><span class="pp">#include </span><span class="im">"tfenotebook.h"</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">"tfe.h"</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 < 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">"/com/github/ToshioCP/tfe/tfe.ui"</span>);</span>
|
||||
<span id="cb6-61"><a href="#cb6-61"></a> win = GTK_APPLICATION_WINDOW (gtk_builder_get_object (build, <span class="st">"win"</span>));</span>
|
||||
<span id="cb6-62"><a href="#cb6-62"></a> nb = GTK_NOTEBOOK (gtk_builder_get_object (build, <span class="st">"nb"</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">"btno"</span>));</span>
|
||||
<span id="cb6-65"><a href="#cb6-65"></a> btnn = GTK_BUTTON (gtk_builder_get_object (build, <span class="st">"btnn"</span>));</span>
|
||||
<span id="cb6-66"><a href="#cb6-66"></a> btns = GTK_BUTTON (gtk_builder_get_object (build, <span class="st">"btns"</span>));</span>
|
||||
<span id="cb6-67"><a href="#cb6-67"></a> btnc = GTK_BUTTON (gtk_builder_get_object (build, <span class="st">"btnc"</span>));</span>
|
||||
<span id="cb6-68"><a href="#cb6-68"></a> g_signal_connect_swapped (btno, <span class="st">"clicked"</span>, G_CALLBACK (open_cb), nb);</span>
|
||||
<span id="cb6-69"><a href="#cb6-69"></a> g_signal_connect_swapped (btnn, <span class="st">"clicked"</span>, G_CALLBACK (new_cb), nb);</span>
|
||||
<span id="cb6-70"><a href="#cb6-70"></a> g_signal_connect_swapped (btns, <span class="st">"clicked"</span>, G_CALLBACK (save_cb), nb);</span>
|
||||
<span id="cb6-71"><a href="#cb6-71"></a> g_signal_connect_swapped (btnc, <span class="st">"clicked"</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">"textview {padding: 10px; font-family: monospace; font-size: 12pt;}"</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 "com.github.ToshioCP.tfe"</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">"startup"</span>, G_CALLBACK (app_startup), NULL);</span>
|
||||
<span id="cb6-92"><a href="#cb6-92"></a> g_signal_connect (app, <span class="st">"activate"</span>, G_CALLBACK (app_activate), NULL);</span>
|
||||
<span id="cb6-93"><a href="#cb6-93"></a> g_signal_connect (app, <span class="st">"open"</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">"tfe.h"</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">"Untitled"</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">"Untitled%u"</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">"tab-expand"</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">"change-file"</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">"open-response"</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"><gtk/gtk.h></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">/* "open-response" 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"><string.h></span></span>
|
||||
<span id="cb10-2"><a href="#cb10-2"></a><span class="pp">#include </span><span class="im">"tfetextview.h"</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->file))</span>
|
||||
<span id="cb10-24"><a href="#cb10-24"></a> g_clear_object (&tv->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)->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->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->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">"change-file"</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">"open-response"</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->file))</span>
|
||||
<span id="cb10-67"><a href="#cb10-67"></a> <span class="cf">return</span> g_file_dup (tv->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, &start_iter, &end_iter);</span>
|
||||
<span id="cb10-82"><a href="#cb10-82"></a> contents = gtk_text_buffer_get_text (tb, &start_iter, &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, &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">"%s.</span><span class="sc">\n</span><span class="st">"</span>, err->message);</span>
|
||||
<span id="cb10-90"><a href="#cb10-90"></a> g_signal_connect (message_dialog, <span class="st">"response"</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">"TfeTextView: gtk_file_chooser_get_file returns non GFile.</span><span class="sc">\n</span><span class="st">"</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->file))</span>
|
||||
<span id="cb10-111"><a href="#cb10-111"></a> g_object_unref (tv->file);</span>
|
||||
<span id="cb10-112"><a href="#cb10-112"></a> tv->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->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->file))</span>
|
||||
<span id="cb10-132"><a href="#cb10-132"></a> g_error (<span class="st">"TfeTextView: The pointer tv->file isn't NULL nor GFile.</span><span class="sc">\n</span><span class="st">"</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->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">"Save file"</span>, GTK_WINDOW (win), GTK_FILE_CHOOSER_ACTION_SAVE,</span>
|
||||
<span id="cb10-145"><a href="#cb10-145"></a> <span class="st">"Cancel"</span>, GTK_RESPONSE_CANCEL,</span>
|
||||
<span id="cb10-146"><a href="#cb10-146"></a> <span class="st">"Save"</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">"response"</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, &contents, &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)->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">"TfeTextView: gtk_file_chooser_get_file returns non GFile.</span><span class="sc">\n</span><span class="st">"</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, &contents, &length, NULL, &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">"%s.</span><span class="sc">\n</span><span class="st">"</span>, err->message);</span>
|
||||
<span id="cb10-193"><a href="#cb10-193"></a> g_signal_connect (message_dialog, <span class="st">"response"</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->file))</span>
|
||||
<span id="cb10-201"><a href="#cb10-201"></a> g_object_unref (tv->file);</span>
|
||||
<span id="cb10-202"><a href="#cb10-202"></a> tv->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">"Open file"</span>, win, GTK_FILE_CHOOSER_ACTION_OPEN,</span>
|
||||
<span id="cb10-218"><a href="#cb10-218"></a> <span class="st">"Cancel"</span>, GTK_RESPONSE_CANCEL,</span>
|
||||
<span id="cb10-219"><a href="#cb10-219"></a> <span class="st">"Open"</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">"response"</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
|
||||
<p>This is a very simple editor. It is a good practice for you to add
|
||||
more features.</p>
|
||||
<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/tfenotebook.c tfe5/tfenotebook.h tfetextview/tfetextview.c tfetextview/tfetextview.h tfe5/tfe.ui
|
||||
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
|
||||
4 6 87 tfe5/tfe.h
|
||||
140 378 3601 tfe5/tfenotebook.c
|
||||
145 387 3667 tfe5/tfenotebook.c
|
||||
15 21 241 tfe5/tfenotebook.h
|
||||
229 671 8017 tfetextview/tfetextview.c
|
||||
239 863 9264 tfetextview/tfetextview.c
|
||||
35 60 701 tfetextview/tfetextview.h
|
||||
61 100 2073 tfe5/tfe.ui
|
||||
599 1566 18372 total</code></pre>
|
||||
612 1765 19667 total</code></pre>
|
||||
</div>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
|
||||
</body>
|
||||
|
|
343
docs/sec2.html
|
@ -115,24 +115,21 @@
|
|||
Linux distributions</h1>
|
||||
<p>This section describes how to install GTK 4 into Linux
|
||||
distributions.</p>
|
||||
<p>This tutorial is without any warranty. If you want to install GTK 4
|
||||
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>
|
||||
<p>There are two ways to install GTK 4.</p>
|
||||
<ul>
|
||||
<li>Install it from the distribution packages.</li>
|
||||
<li>Build it from the source file.</li>
|
||||
<li>Install a GNOME 40 distribution with the GNOME Boxes.</li>
|
||||
<li>Build it from the source files.</li>
|
||||
</ul>
|
||||
<h2 id="installation-from-the-distribution-packages">Installation from
|
||||
the distribution packages</h2>
|
||||
<p>The first way is easy to install. It is a recommended way. I’ve
|
||||
installed GTK 4 packages in Ubuntu 21.04. (Now, my Ubuntu version is
|
||||
21.10).</p>
|
||||
<pre><code>$ sudo apt-get install libgtk-4-bin libgtk-4-common libgtk-4-dev libgtk-4-doc</code></pre>
|
||||
<p>Fedora, Arch, Debian and OpenSUSE are also possible. See <a
|
||||
<p>The first way is the best and easiest way to install it. I’ve
|
||||
installed GTK 4 packages (version 4.8.1) in Ubuntu 22.10.</p>
|
||||
<pre><code>$ sudo apt install libgtk-4-dev</code></pre>
|
||||
<p>It is important to install the developing package (libgtk-4-dev). It
|
||||
includes C header files. Otherwise, you can’t 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
|
||||
GTK from packages</a>. The following table shows the distributions which
|
||||
support GTK 4.</p>
|
||||
|
@ -142,336 +139,54 @@ support GTK 4.</p>
|
|||
<th style="text-align: center;">Distribution</th>
|
||||
<th style="text-align: center;">version</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>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="odd">
|
||||
<td style="text-align: center;">Fedora</td>
|
||||
<td style="text-align: center;">36</td>
|
||||
<td style="text-align: center;">4.4.2</td>
|
||||
<td style="text-align: center;">GNOME 42</td>
|
||||
<td style="text-align: center;">37</td>
|
||||
<td style="text-align: center;">4.8.2-2</td>
|
||||
<td style="text-align: center;">GNOME 43</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td style="text-align: center;">Ubuntu</td>
|
||||
<td style="text-align: center;">22.04lts</td>
|
||||
<td style="text-align: center;">4.4</td>
|
||||
<td style="text-align: center;">GNOME 41(4.6.2)</td>
|
||||
<td style="text-align: center;">22.10</td>
|
||||
<td style="text-align: center;">4.8.1</td>
|
||||
<td style="text-align: center;">GNOME 43.0</td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td style="text-align: center;">Debian</td>
|
||||
<td style="text-align: center;">bookworm(testing)</td>
|
||||
<td style="text-align: center;">4.6.5</td>
|
||||
<td style="text-align: center;">GNOME 42</td>
|
||||
<td style="text-align: center;">4.8.2</td>
|
||||
<td style="text-align: center;">GNOME 43</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td style="text-align: center;">Arch</td>
|
||||
<td style="text-align: center;">rolling release</td>
|
||||
<td style="text-align: center;">4.6.5</td>
|
||||
<td style="text-align: center;">GNOME 42</td>
|
||||
<td style="text-align: center;">4.8.2</td>
|
||||
<td style="text-align: center;">GNOME 43</td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td style="text-align: center;">Gentoo</td>
|
||||
<td style="text-align: center;">rolling release</td>
|
||||
<td style="text-align: center;">4.6.5</td>
|
||||
<td style="text-align: center;">GNOME 42</td>
|
||||
<td style="text-align: center;">4.8.2</td>
|
||||
<td style="text-align: center;">GNOME 43</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td style="text-align: center;">OpenSUSE</td>
|
||||
<td style="text-align: center;">Tumbleweed(rolling release)</td>
|
||||
<td style="text-align: center;">4.6.5</td>
|
||||
<td style="text-align: center;">GNOME 42</td>
|
||||
<td style="text-align: center;">4.8.2</td>
|
||||
<td style="text-align: center;">GNOME 43</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>If you’ve installed GTK 4 from the packages, you don’t need to read
|
||||
the rest of this section.</p>
|
||||
<h2 id="installation-from-the-source-file">Installation from the source
|
||||
file</h2>
|
||||
<p>If your operating system doesn’t have GTK 4 packages, 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.</p>
|
||||
<p>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 <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>Don’t 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
|
||||
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).</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="-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</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="$HOME/local/share/pkgconfig:$PKG_CONFIG_PATH"</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="-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</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
|
||||
it’s 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 it’s 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>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.</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 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.</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. I’ve 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 '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/'</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>
|
||||
<p>If you want to install a developing version of GTK 4, you need to
|
||||
build it from the source. See <a
|
||||
href="https://docs.gtk.org/gtk4/building.html">Compiling the GTK
|
||||
Libraries</a> secion in the GTK 4 API reference.</p>
|
||||
</div>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
|
||||
</body>
|
||||
|
|
159
docs/sec3.html
|
@ -116,9 +116,9 @@ GtkApplicationWindow</h1>
|
|||
<h2 id="gtkapplication">GtkApplication</h2>
|
||||
<h3 id="gtkapplication-and-g_application_run">GtkApplication and
|
||||
g_application_run</h3>
|
||||
<p>Usually people write programming code to make an application. What
|
||||
are applications? Applications are software that runs using libraries,
|
||||
which includes the OS, frameworks and so on. In GTK 4 programming, the
|
||||
<p>People write programming code to make an application. What are
|
||||
applications? Applications are software that runs using libraries, which
|
||||
includes the OS, frameworks and so on. In GTK 4 programming, the
|
||||
GtkApplication is a program (or executable) that runs using Gtk
|
||||
libraries.</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-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-8"><a href="#cb1-8"></a> app <span class="op">=</span> gtk_application_new <span class="op">(</span><span class="st">"com.github.ToshioCP.pr1"</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">"com.github.ToshioCP.pr1"</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-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-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
|
||||
the Gtk libraries. The function <code>main</code> above is a startup
|
||||
function in C language. The variable <code>app</code> is defined as a
|
||||
pointer to a GtkApplication instance. The function
|
||||
<code>gtk_application_new</code> 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 meaning of the arguments will be explained later. The function
|
||||
the Gtk libraries. The function <code>main</code> is a startup function
|
||||
in C language. The variable <code>app</code> is defined as a pointer to
|
||||
a GtkApplication instance. The function <code>gtk_application_new</code>
|
||||
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 meaning of the
|
||||
arguments will be explained later. The function
|
||||
<code>g_application_run</code> runs an application that the instance
|
||||
defined. (We often say that the function runs <code>app</code>.
|
||||
Actually, <code>app</code> is not an application but a pointer to the
|
||||
instance of the application. However, it is simple and short, and
|
||||
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
|
||||
<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>
|
||||
|
@ -180,16 +188,17 @@ what this message means.</p>
|
|||
explain that to you first.</p>
|
||||
<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
|
||||
emitted when the application is activated, or started. 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.</p>
|
||||
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
|
||||
simply handler, then the function is invoked when the signal emits.</p>
|
||||
<p>The flow is like this:</p>
|
||||
<ol type="1">
|
||||
<li>Something happens.</li>
|
||||
<li>If it’s related to a certain signal, then the signal is
|
||||
emitted.</li>
|
||||
<li>If the signal as been connected to a handler, then the handler is
|
||||
invoked.</li>
|
||||
<li>If the signal has been connected to a handler in advance, then the
|
||||
handler is invoked.</li>
|
||||
</ol>
|
||||
<p>Signals are defined in objects. For example, the “activate” signal
|
||||
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-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-13"><a href="#cb5-13"></a> app <span class="op">=</span> gtk_application_new <span class="op">(</span><span class="st">"com.github.ToshioCP.pr2"</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">"com.github.ToshioCP.pr2"</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">"activate"</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-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-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
|
||||
displays a message. In the function <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>
|
||||
displays a message. The function <code>g_print</code> is defined in GLib
|
||||
and it’s like a printf in the C standard library. In the function
|
||||
<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">
|
||||
<li>An instance to which the signal belongs.</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
|
||||
given.</li>
|
||||
</ol>
|
||||
<p>You can find the description of each signal in the API reference
|
||||
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
|
||||
<p>It is described in the <a
|
||||
href="https://docs.gtk.org/gobject/func.signal_connect.html">GObject API
|
||||
Reference</a>. API reference manual is very important. You should see
|
||||
and understand it to write Gtk applications. They are located in <a
|
||||
href="https://docs.gtk.org/">‘GTK Documentation’</a>.</p>
|
||||
Reference</a>. Correctly, <code>g_signal_connect</code> is a macro (not
|
||||
a C function).</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>Let’s compile the source file above (<code>pr2.c</code>) and run
|
||||
it.</p>
|
||||
<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>
|
||||
<p>Now rewrite the function <code>app_activate</code>.</p>
|
||||
<h4 id="create-a-gtkwindow">Create a GtkWindow</h4>
|
||||
<div class="sourceCode" id="cb10"><pre
|
||||
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb10-1"><a href="#cb10-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb10-2"><a href="#cb10-2"></a>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="cb10-4"><a href="#cb10-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="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="cb10-7"><a href="#cb10-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>
|
||||
<div class="sourceCode" id="cb12"><pre
|
||||
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb12-1"><a href="#cb12-1"></a><span class="dt">static</span> <span class="dt">void</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="cb12-3"><a href="#cb12-3"></a> GtkWidget <span class="op">*</span>win<span class="op">;</span></span>
|
||||
<span id="cb12-4"><a href="#cb12-4"></a></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="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="cb12-7"><a href="#cb12-7"></a> gtk_widget_show <span class="op">(</span>win<span class="op">);</span></span>
|
||||
<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
|
||||
such as windows, dialogs, buttons, multi-line text, containers and so
|
||||
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>
|
||||
</figure>
|
||||
<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>
|
||||
<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>
|
||||
<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="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.
|
||||
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.</p>
|
||||
<p>If you want to use <code>win</code> as a pointer to the GtkWindow,
|
||||
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>
|
||||
<p>Or you can use <code>GTK_WINDOW</code> macro that performs a similar
|
||||
function.</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>
|
||||
<p>This is a recommended way.</p>
|
||||
<p>If you want to use <code>win</code> as a pointer to a GtkWindow type
|
||||
instance, you need to cast it.</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><span class="op">(</span>GtkWindow <span class="op">*)</span> win</span></code></pre></div>
|
||||
<p>It works, but isn’t usually used. Instead, <code>GTK_WINDOW</code>
|
||||
macro is used.</p>
|
||||
<div class="sourceCode" id="cb16"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a>GTK_WINDOW <span class="op">(</span>win<span class="op">)</span></span></code></pre></div>
|
||||
<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
|
||||
GtkApplication.</h4>
|
||||
<p>The function <code>gtk_window_set_application</code> is used to
|
||||
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>
|
||||
to GtkApplication. <code>GTK_WINDOW</code> and
|
||||
<code>GTK_APPLICATION</code> macro is appropriate for that.</p>
|
||||
to GtkApplication with <code>GTK_WINDOW</code> and
|
||||
<code>GTK_APPLICATION</code> macro.</p>
|
||||
<p>GtkApplication continues to run until the related window is
|
||||
destroyed. If you didn’t connect GtkWindow and GtkApplication,
|
||||
GtkApplication destroys itself immediately. Because no window is
|
||||
|
@ -340,7 +373,7 @@ window.</p>
|
|||
doesn’t need this function to show itself. But, there’s an exception.
|
||||
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.</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
|
||||
$ ./a.out</code></pre>
|
||||
<p>A small window appears.</p>
|
||||
|
@ -352,20 +385,20 @@ $ ./a.out</code></pre>
|
|||
finishes.</p>
|
||||
<h3 id="gtkapplicationwindow">GtkApplicationWindow</h3>
|
||||
<p>GtkApplicationWindow is a child object of GtkWindow. It has some
|
||||
extra functionality for better integration with GtkApplication. It is
|
||||
recommended to use it instead of GtkWindow when you use
|
||||
GtkApplication.</p>
|
||||
extra feature for better integration with GtkApplication. It is
|
||||
recommended to use it as the top-level window of the application instead
|
||||
of GtkWindow.</p>
|
||||
<p>Now rewrite the program and use GtkApplicationWindow.</p>
|
||||
<div class="sourceCode" id="cb17"><pre
|
||||
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb17-1"><a href="#cb17-1"></a><span class="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="cb17-3"><a href="#cb17-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="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="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">"pr4"</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="cb17-8"><a href="#cb17-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>
|
||||
<div class="sourceCode" id="cb19"><pre
|
||||
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb19-1"><a href="#cb19-1"></a><span class="dt">static</span> <span class="dt">void</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="cb19-3"><a href="#cb19-3"></a> GtkWidget <span class="op">*</span>win<span class="op">;</span></span>
|
||||
<span id="cb19-4"><a href="#cb19-4"></a></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="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">"pr4"</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="cb19-8"><a href="#cb19-8"></a> gtk_widget_show <span class="op">(</span>win<span class="op">);</span></span>
|
||||
<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
|
||||
instance as an argument. Then it automatically connect these two
|
||||
instances. So you don’t need to call
|
||||
|
|
426
docs/sec4.html
|
@ -5,7 +5,7 @@
|
|||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
||||
<title>Gtk4 tutorial</title>
|
||||
<title>GTK 4 tutorial</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
|
@ -112,46 +112,59 @@
|
|||
</div>
|
||||
</nav>
|
||||
<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>
|
||||
<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>
|
||||
<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"><gtk/gtk.h></span></span>
|
||||
<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>
|
||||
<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"><gtk/gtk.h></span></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-4"><a href="#cb1-4"></a>app_activate (GApplication *app, gpointer user_data) {</span>
|
||||
<span id="cb1-5"><a href="#cb1-5"></a> GtkWidget *win;</span>
|
||||
<span id="cb1-6"><a href="#cb1-6"></a> GtkWidget *lab;</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 <span class="op">*</span>win<span class="op">;</span></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-8"><a href="#cb1-8"></a> win = gtk_application_window_new (GTK_APPLICATION (app));</span>
|
||||
<span id="cb1-9"><a href="#cb1-9"></a> gtk_window_set_title (GTK_WINDOW (win), <span class="st">"lb1"</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-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 <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="st">"lb1"</span><span class="op">);</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-12"><a href="#cb1-12"></a> lab = gtk_label_new (<span class="st">"Hello."</span>);</span>
|
||||
<span id="cb1-13"><a href="#cb1-13"></a> gtk_window_set_child (GTK_WINDOW (win), lab);</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">"Hello."</span><span class="op">);</span></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-15"><a href="#cb1-15"></a> gtk_widget_show (win);</span>
|
||||
<span id="cb1-16"><a href="#cb1-16"></a>}</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 class="op">}</span></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-19"><a href="#cb1-19"></a>main (<span class="dt">int</span> argc, <span class="dt">char</span> **argv) {</span>
|
||||
<span id="cb1-20"><a href="#cb1-20"></a> GtkApplication *app;</span>
|
||||
<span id="cb1-21"><a href="#cb1-21"></a> <span class="dt">int</span> stat;</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 <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 class="op">;</span></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">"com.github.ToshioCP.lb1"</span>, G_APPLICATION_FLAGS_NONE);</span>
|
||||
<span id="cb1-24"><a href="#cb1-24"></a> g_signal_connect (app, <span class="st">"activate"</span>, G_CALLBACK (app_activate), NULL);</span>
|
||||
<span id="cb1-25"><a href="#cb1-25"></a> stat =g_application_run (G_APPLICATION (app), argc, argv);</span>
|
||||
<span id="cb1-26"><a href="#cb1-26"></a> g_object_unref (app);</span>
|
||||
<span id="cb1-27"><a href="#cb1-27"></a> <span class="cf">return</span> stat;</span>
|
||||
<span id="cb1-28"><a href="#cb1-28"></a>}</span></code></pre></div>
|
||||
<p>Save this program to a file <code>lb1.c</code>. Then compile and run it.</p>
|
||||
<span id="cb1-23"><a href="#cb1-23"></a> app <span class="op">=</span> gtk_application_new <span class="op">(</span><span class="st">"com.github.ToshioCP.lb1"</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 <span class="op">(</span>app<span class="op">,</span> <span class="st">"activate"</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 <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 <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 class="op">;</span></span>
|
||||
<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>
|
||||
<pre><code>$ comp lb1
|
||||
$ ./a.out</code></pre>
|
||||
<p>A window with a message “Hello.” appears.</p>
|
||||
<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>
|
||||
<p>There’s 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>There’s 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
|
||||
4c4
|
||||
< app_activate (GApplication *app, gpointer user_data) {
|
||||
---
|
||||
> app_activate (GApplication *app) {
|
||||
5a6
|
||||
> GtkWidget *lab;
|
||||
8c9
|
||||
|
@ -164,193 +177,290 @@ $ ./a.out</code></pre>
|
|||
> gtk_window_set_child (GTK_WINDOW (win), lab);
|
||||
>
|
||||
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);</code></pre>
|
||||
> app = gtk_application_new ("com.github.ToshioCP.lb1", G_APPLICATION_DEFAULT_FLAGS);</code></pre>
|
||||
<p>This tells us:</p>
|
||||
<ul>
|
||||
<li>A signal handler <code>app_activate</code> doesn’t 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 title of the window is changed.</li>
|
||||
<li>A label is created and connected to the window as a child.</li>
|
||||
</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. Don’t 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> doesn’t have any parents. We call such a window top-level window. An application can have more than one top-level window.</p>
|
||||
<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. Don’t
|
||||
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> doesn’t 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>
|
||||
<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>
|
||||
<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"><gtk/gtk.h></span></span>
|
||||
<p>The next widget 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 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"><gtk/gtk.h></span></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-4"><a href="#cb4-4"></a>click_cb (GtkButton *btn, gpointer user_data) {</span>
|
||||
<span id="cb4-5"><a href="#cb4-5"></a> g_print (<span class="st">"Clicked.</span><span class="sc">\n</span><span class="st">"</span>);</span>
|
||||
<span id="cb4-6"><a href="#cb4-6"></a>}</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="op">(</span><span class="st">"Clicked.</span><span class="sc">\n</span><span class="st">"</span><span class="op">);</span></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-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-10"><a href="#cb4-10"></a> GtkWidget *win;</span>
|
||||
<span id="cb4-11"><a href="#cb4-11"></a> GtkWidget *btn;</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 <span class="op">*</span>win<span class="op">;</span></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-13"><a href="#cb4-13"></a> win = gtk_application_window_new (GTK_APPLICATION (app));</span>
|
||||
<span id="cb4-14"><a href="#cb4-14"></a> gtk_window_set_title (GTK_WINDOW (win), <span class="st">"lb2"</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-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 <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="st">"lb2"</span><span class="op">);</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-17"><a href="#cb4-17"></a> btn = gtk_button_new_with_label (<span class="st">"Click me"</span>);</span>
|
||||
<span id="cb4-18"><a href="#cb4-18"></a> gtk_window_set_child (GTK_WINDOW (win), btn);</span>
|
||||
<span id="cb4-19"><a href="#cb4-19"></a> g_signal_connect (btn, <span class="st">"clicked"</span>, G_CALLBACK (click_cb), NULL);</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">"Click me"</span><span class="op">);</span></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 <span class="op">(</span>btn<span class="op">,</span> <span class="st">"clicked"</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-21"><a href="#cb4-21"></a> gtk_widget_show (win);</span>
|
||||
<span id="cb4-22"><a href="#cb4-22"></a>}</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 class="op">}</span></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-25"><a href="#cb4-25"></a>main (<span class="dt">int</span> argc, <span class="dt">char</span> **argv) {</span>
|
||||
<span id="cb4-26"><a href="#cb4-26"></a> GtkApplication *app;</span>
|
||||
<span id="cb4-27"><a href="#cb4-27"></a> <span class="dt">int</span> stat;</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 <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 class="op">;</span></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">"com.github.ToshioCP.lb2"</span>, G_APPLICATION_FLAGS_NONE);</span>
|
||||
<span id="cb4-30"><a href="#cb4-30"></a> g_signal_connect (app, <span class="st">"activate"</span>, G_CALLBACK (app_activate), NULL);</span>
|
||||
<span id="cb4-31"><a href="#cb4-31"></a> stat =g_application_run (G_APPLICATION (app), argc, argv);</span>
|
||||
<span id="cb4-32"><a href="#cb4-32"></a> g_object_unref (app);</span>
|
||||
<span id="cb4-33"><a href="#cb4-33"></a> <span class="cf">return</span> stat;</span>
|
||||
<span id="cb4-34"><a href="#cb4-34"></a>}</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>Name the program <code>lb2.c</code> and save it. Now compile and run it.</p>
|
||||
<span id="cb4-29"><a href="#cb4-29"></a> app <span class="op">=</span> gtk_application_new <span class="op">(</span><span class="st">"com.github.ToshioCP.lb2"</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 <span class="op">(</span>app<span class="op">,</span> <span class="st">"activate"</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 <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 <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 class="op">;</span></span>
|
||||
<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 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>
|
||||
<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>
|
||||
<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>It’s 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>
|
||||
<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>click_cb (GtkButton *btn, gpointer user_data) {</span>
|
||||
<span id="cb5-3"><a href="#cb5-3"></a> GtkWindow *win = GTK_WINDOW (user_data);</span>
|
||||
<span id="cb5-4"><a href="#cb5-4"></a> gtk_window_destroy (win);</span>
|
||||
<span id="cb5-5"><a href="#cb5-5"></a>}</span>
|
||||
<span id="cb5-6"><a href="#cb5-6"></a></span>
|
||||
<span id="cb5-7"><a href="#cb5-7"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb5-8"><a href="#cb5-8"></a>app_activate (GApplication *app, gpointer user_data) {</span>
|
||||
<span id="cb5-9"><a href="#cb5-9"></a> GtkWidget *win;</span>
|
||||
<span id="cb5-10"><a href="#cb5-10"></a> GtkWidget *btn;</span>
|
||||
<span id="cb5-11"><a href="#cb5-11"></a></span>
|
||||
<span id="cb5-12"><a href="#cb5-12"></a> win = gtk_application_window_new (GTK_APPLICATION (app));</span>
|
||||
<span id="cb5-13"><a href="#cb5-13"></a> gtk_window_set_title (GTK_WINDOW (win), <span class="st">"lb3"</span>);</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-15"><a href="#cb5-15"></a></span>
|
||||
<span id="cb5-16"><a href="#cb5-16"></a> btn = gtk_button_new_with_label (<span class="st">"Quit"</span>);</span>
|
||||
<span id="cb5-17"><a href="#cb5-17"></a> gtk_window_set_child (GTK_WINDOW (win), btn);</span>
|
||||
<span id="cb5-18"><a href="#cb5-18"></a> g_signal_connect (btn, <span class="st">"clicked"</span>, G_CALLBACK (click_cb), win);</span>
|
||||
<span id="cb5-19"><a href="#cb5-19"></a></span>
|
||||
<span id="cb5-20"><a href="#cb5-20"></a> gtk_widget_show (win);</span>
|
||||
<span id="cb5-21"><a href="#cb5-21"></a>}</span></code></pre></div>
|
||||
<p>And the difference between <code>lb2.c</code> and <code>lb3.c</code> is as follows.</p>
|
||||
<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>It’s good that we make sure that the clicked signal was caught and
|
||||
the handler was invoked by using <code>g_print</code>. However, using
|
||||
<code>g_print</code> 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>
|
||||
<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>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-3"><a href="#cb5-3"></a> gtk_window_destroy <span class="op">(</span>win<span class="op">);</span></span>
|
||||
<span id="cb5-4"><a href="#cb5-4"></a><span class="op">}</span></span>
|
||||
<span id="cb5-5"><a href="#cb5-5"></a></span>
|
||||
<span id="cb5-6"><a href="#cb5-6"></a><span class="dt">static</span> <span class="dt">void</span></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-8"><a href="#cb5-8"></a> GtkWidget <span class="op">*</span>win<span class="op">;</span></span>
|
||||
<span id="cb5-9"><a href="#cb5-9"></a> GtkWidget <span class="op">*</span>btn<span class="op">;</span></span>
|
||||
<span id="cb5-10"><a href="#cb5-10"></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-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">"lb3"</span><span class="op">);</span></span>
|
||||
<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>
|
||||
<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">"Close"</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">"clicked"</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
|
||||
5c5,6
|
||||
4,5c4,5
|
||||
< click_cb (GtkButton *btn) {
|
||||
< g_print ("Clicked.\n");
|
||||
---
|
||||
> GtkWindow *win = GTK_WINDOW (user_data);
|
||||
> click_cb (GtkButton *btn, GtkWindow *win) {
|
||||
> gtk_window_destroy (win);
|
||||
14c15
|
||||
14c14
|
||||
< gtk_window_set_title (GTK_WINDOW (win), "lb2");
|
||||
---
|
||||
> gtk_window_set_title (GTK_WINDOW (win), "lb3");
|
||||
17c18
|
||||
17c17
|
||||
< btn = gtk_button_new_with_label ("Click me");
|
||||
---
|
||||
> btn = gtk_button_new_with_label ("Quit");
|
||||
19c20
|
||||
> btn = gtk_button_new_with_label ("Close");
|
||||
19c19
|
||||
< g_signal_connect (btn, "clicked", G_CALLBACK (click_cb), NULL);
|
||||
---
|
||||
> g_signal_connect (btn, "clicked", G_CALLBACK (click_cb), win);
|
||||
29c30
|
||||
< app = gtk_application_new ("com.github.ToshioCP.lb2", G_APPLICATION_FLAGS_NONE);
|
||||
29c29
|
||||
< app = gtk_application_new ("com.github.ToshioCP.lb2", G_APPLICATION_DEFAULT_FLAGS);
|
||||
---
|
||||
> app = gtk_application_new ("com.github.ToshioCP.lb3", G_APPLICATION_FLAGS_NONE);
|
||||
35d35
|
||||
> app = gtk_application_new ("com.github.ToshioCP.lb3", G_APPLICATION_DEFAULT_FLAGS);
|
||||
35d34
|
||||
< </code></pre>
|
||||
<p>The changes are:</p>
|
||||
<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 label of <code>btn</code> is changed from “Click me” to “Quit”.</li>
|
||||
<li>The fourth argument of <code>g_signal_connect</code> is changed from <code>NULL</code> to <code>win</code>.</li>
|
||||
<li>The function <code>g_print</code> in <code>lb2.c</code> was deleted
|
||||
and two lines are inserted.
|
||||
<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 doesn’t
|
||||
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>
|
||||
<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>
|
||||
<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>
|
||||
<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 another GtkButton instance and append it to the GtkBox.</li>
|
||||
</ul>
|
||||
<p>After this, the Widgets are connected as the following diagram.</p>
|
||||
<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>
|
||||
<p>The program <code>lb4.c</code> includes these widgets. It 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"><gtk/gtk.h></span></span>
|
||||
<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"><gtk/gtk.h></span></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-4"><a href="#cb7-4"></a>click1_cb (GtkButton *btn, gpointer user_data) {</span>
|
||||
<span id="cb7-5"><a href="#cb7-5"></a> <span class="dt">const</span> gchar *s;</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> <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-7"><a href="#cb7-7"></a> s = gtk_button_get_label (btn);</span>
|
||||
<span id="cb7-8"><a href="#cb7-8"></a> <span class="cf">if</span> (g_strcmp0 (s, <span class="st">"Hello."</span>) == <span class="dv">0</span>)</span>
|
||||
<span id="cb7-9"><a href="#cb7-9"></a> gtk_button_set_label (btn, <span class="st">"Good-bye."</span>);</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> <span class="op">(</span>g_strcmp0 <span class="op">(</span>s<span class="op">,</span> <span class="st">"Hello."</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 <span class="op">(</span>btn<span class="op">,</span> <span class="st">"Good-bye."</span><span class="op">);</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">"Hello."</span>);</span>
|
||||
<span id="cb7-12"><a href="#cb7-12"></a>}</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">"Hello."</span><span class="op">);</span></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-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-16"><a href="#cb7-16"></a> GtkWindow *win = GTK_WINDOW (user_data);</span>
|
||||
<span id="cb7-17"><a href="#cb7-17"></a> gtk_window_destroy (win);</span>
|
||||
<span id="cb7-18"><a href="#cb7-18"></a>}</span>
|
||||
<span id="cb7-19"><a href="#cb7-19"></a></span>
|
||||
<span id="cb7-20"><a href="#cb7-20"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb7-21"><a href="#cb7-21"></a>app_activate (GApplication *app, gpointer user_data) {</span>
|
||||
<span id="cb7-22"><a href="#cb7-22"></a> GtkWidget *win;</span>
|
||||
<span id="cb7-23"><a href="#cb7-23"></a> GtkWidget *box;</span>
|
||||
<span id="cb7-24"><a href="#cb7-24"></a> GtkWidget *btn1;</span>
|
||||
<span id="cb7-25"><a href="#cb7-25"></a> GtkWidget *btn2;</span>
|
||||
<span id="cb7-26"><a href="#cb7-26"></a></span>
|
||||
<span id="cb7-27"><a href="#cb7-27"></a> win = gtk_application_window_new (GTK_APPLICATION (app));</span>
|
||||
<span id="cb7-28"><a href="#cb7-28"></a> gtk_window_set_title (GTK_WINDOW (win), <span class="st">"lb4"</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-30"><a href="#cb7-30"></a></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-32"><a href="#cb7-32"></a> gtk_box_set_homogeneous (GTK_BOX (box), TRUE);</span>
|
||||
<span id="cb7-33"><a href="#cb7-33"></a> gtk_window_set_child (GTK_WINDOW (win), box);</span>
|
||||
<span id="cb7-34"><a href="#cb7-34"></a></span>
|
||||
<span id="cb7-35"><a href="#cb7-35"></a> btn1 = gtk_button_new_with_label (<span class="st">"Hello."</span>);</span>
|
||||
<span id="cb7-36"><a href="#cb7-36"></a> g_signal_connect (btn1, <span class="st">"clicked"</span>, G_CALLBACK (click1_cb), NULL);</span>
|
||||
<span id="cb7-37"><a href="#cb7-37"></a></span>
|
||||
<span id="cb7-38"><a href="#cb7-38"></a> btn2 = gtk_button_new_with_label (<span class="st">"Quit"</span>);</span>
|
||||
<span id="cb7-39"><a href="#cb7-39"></a> g_signal_connect (btn2, <span class="st">"clicked"</span>, G_CALLBACK (click2_cb), win);</span>
|
||||
<span id="cb7-40"><a href="#cb7-40"></a></span>
|
||||
<span id="cb7-41"><a href="#cb7-41"></a> gtk_box_append (GTK_BOX (box), btn1);</span>
|
||||
<span id="cb7-42"><a href="#cb7-42"></a> gtk_box_append (GTK_BOX (box), btn2);</span>
|
||||
<span id="cb7-43"><a href="#cb7-43"></a></span>
|
||||
<span id="cb7-44"><a href="#cb7-44"></a> gtk_widget_show (win);</span>
|
||||
<span id="cb7-45"><a href="#cb7-45"></a>}</span>
|
||||
<span id="cb7-46"><a href="#cb7-46"></a></span>
|
||||
<span id="cb7-47"><a href="#cb7-47"></a><span class="dt">int</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-49"><a href="#cb7-49"></a> GtkApplication *app;</span>
|
||||
<span id="cb7-50"><a href="#cb7-50"></a> <span class="dt">int</span> stat;</span>
|
||||
<span id="cb7-51"><a href="#cb7-51"></a></span>
|
||||
<span id="cb7-52"><a href="#cb7-52"></a> app = gtk_application_new (<span class="st">"com.github.ToshioCP.lb4"</span>, G_APPLICATION_FLAGS_NONE);</span>
|
||||
<span id="cb7-53"><a href="#cb7-53"></a> g_signal_connect (app, <span class="st">"activate"</span>, G_CALLBACK (app_activate), NULL);</span>
|
||||
<span id="cb7-54"><a href="#cb7-54"></a> stat =g_application_run (G_APPLICATION (app), argc, argv);</span>
|
||||
<span id="cb7-55"><a href="#cb7-55"></a> g_object_unref (app);</span>
|
||||
<span id="cb7-56"><a href="#cb7-56"></a> <span class="cf">return</span> stat;</span>
|
||||
<span id="cb7-57"><a href="#cb7-57"></a>}</span></code></pre></div>
|
||||
<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> gtk_window_destroy <span class="op">(</span>win<span class="op">);</span></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-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>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> GtkWidget <span class="op">*</span>win<span class="op">;</span></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 <span class="op">*</span>btn1<span class="op">;</span></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></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> gtk_window_set_title <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="st">"lb4"</span><span class="op">);</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></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> 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_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></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">"Hello."</span><span class="op">);</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">"clicked"</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></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">"Close"</span><span class="op">);</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">"clicked"</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></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 <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></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><span class="op">}</span></span>
|
||||
<span id="cb7-45"><a href="#cb7-45"></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>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> GtkApplication <span class="op">*</span>app<span class="op">;</span></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>
|
||||
<span id="cb7-51"><a href="#cb7-51"></a> app <span class="op">=</span> gtk_application_new <span class="op">(</span><span class="st">"com.github.ToshioCP.lb4"</span><span class="op">,</span> G_APPLICATION_DEFAULT_FLAGS<span class="op">);</span></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">"activate"</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> 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> g_object_unref <span class="op">(</span>app<span class="op">);</span></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="op">}</span></span></code></pre></div>
|
||||
<p>Look at the function <code>app_activate</code>.</p>
|
||||
<p>After the creation of a GtkApplicationWindow instance, a GtkBox instance is created.</p>
|
||||
<pre><code>box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
|
||||
gtk_box_set_homogeneous (GTK_BOX (box), TRUE);</code></pre>
|
||||
<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>
|
||||
<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>After the creation of a GtkApplicationWindow instance, a GtkBox
|
||||
instance is created.</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>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="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>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">"Hello."</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">"Good-bye."</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">"Hello."</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 can’t 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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
|
|
222
docs/sec5.html
|
@ -5,7 +5,7 @@
|
|||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
||||
<title>Gtk4 tutorial</title>
|
||||
<title>GTK 4 tutorial</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
|
@ -112,19 +112,24 @@
|
|||
</div>
|
||||
</nav>
|
||||
<h1 id="widgets-2">Widgets (2)</h1>
|
||||
<h2 id="gtktextview-gtktextbuffer-and-gtkscrolledwindow">GtkTextView, GtkTextBuffer and GtkScrolledWindow</h2>
|
||||
<h3 id="gtktextview-and-gtktextbuffer">GtkTextView and 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"><gtk/gtk.h></span></span>
|
||||
<h2 id="gtktextview-gtktextbuffer-and-gtkscrolledwindow">GtkTextView,
|
||||
GtkTextBuffer and GtkScrolledWindow</h2>
|
||||
<h3 id="gtktextview-and-gtktextbuffer">GtkTextView and
|
||||
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"><gtk/gtk.h></span></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-4"><a href="#cb1-4"></a>app_activate (GApplication *app, gpointer user_data) {</span>
|
||||
<span id="cb1-5"><a href="#cb1-5"></a> GtkWidget *win;</span>
|
||||
<span id="cb1-6"><a href="#cb1-6"></a> GtkWidget *tv;</span>
|
||||
<span id="cb1-7"><a href="#cb1-7"></a> GtkTextBuffer *tb;</span>
|
||||
<span id="cb1-8"><a href="#cb1-8"></a> gchar *text;</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 <span class="op">*</span>win<span class="op">;</span></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 <span class="op">*</span>tb<span class="op">;</span></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-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">"Once upon a time, there was an old man who was called Taketori-no-Okina. "</span></span>
|
||||
<span id="cb1-12"><a href="#cb1-12"></a> <span class="st">"It is a japanese word that means a man whose work is making bamboo baskets.</span><span class="sc">\n</span><span class="st">"</span></span>
|
||||
<span id="cb1-13"><a href="#cb1-13"></a> <span class="st">"One day, he went into a mountain and found a shining bamboo. "</span></span>
|
||||
|
@ -132,49 +137,77 @@
|
|||
<span id="cb1-15"><a href="#cb1-15"></a> <span class="st">"He cut it, then there was a small cute baby girl in it. "</span></span>
|
||||
<span id="cb1-16"><a href="#cb1-16"></a> <span class="st">"The girl was shining faintly. "</span></span>
|
||||
<span id="cb1-17"><a href="#cb1-17"></a> <span class="st">"He thought this baby girl is a gift from Heaven and took her home.</span><span class="sc">\n</span><span class="st">"</span></span>
|
||||
<span id="cb1-18"><a href="#cb1-18"></a> <span class="st">"His wife was surprized at his tale. "</span></span>
|
||||
<span id="cb1-18"><a href="#cb1-18"></a> <span class="st">"His wife was surprized at his story. "</span></span>
|
||||
<span id="cb1-19"><a href="#cb1-19"></a> <span class="st">"They were very happy because they had no children. "</span></span>
|
||||
<span id="cb1-20"><a href="#cb1-20"></a> ;</span>
|
||||
<span id="cb1-21"><a href="#cb1-21"></a> win = gtk_application_window_new (GTK_APPLICATION (app));</span>
|
||||
<span id="cb1-22"><a href="#cb1-22"></a> gtk_window_set_title (GTK_WINDOW (win), <span class="st">"Taketori"</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-20"><a href="#cb1-20"></a> <span class="op">;</span></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 <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="st">"Taketori"</span><span class="op">);</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-25"><a href="#cb1-25"></a> tv = gtk_text_view_new ();</span>
|
||||
<span id="cb1-26"><a href="#cb1-26"></a> tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));</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-28"><a href="#cb1-28"></a> gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR);</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 <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 <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 <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-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-32"><a href="#cb1-32"></a> gtk_widget_show (win);</span>
|
||||
<span id="cb1-33"><a href="#cb1-33"></a>}</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 class="op">}</span></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-36"><a href="#cb1-36"></a>main (<span class="dt">int</span> argc, <span class="dt">char</span> **argv) {</span>
|
||||
<span id="cb1-37"><a href="#cb1-37"></a> GtkApplication *app;</span>
|
||||
<span id="cb1-38"><a href="#cb1-38"></a> <span class="dt">int</span> stat;</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 <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 class="op">;</span></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">"com.github.ToshioCP.tfv1"</span>, G_APPLICATION_FLAGS_NONE);</span>
|
||||
<span id="cb1-41"><a href="#cb1-41"></a> g_signal_connect (app, <span class="st">"activate"</span>, G_CALLBACK (app_activate), NULL);</span>
|
||||
<span id="cb1-42"><a href="#cb1-42"></a> stat = g_application_run (G_APPLICATION (app), argc, argv);</span>
|
||||
<span id="cb1-43"><a href="#cb1-43"></a> g_object_unref (app);</span>
|
||||
<span id="cb1-44"><a href="#cb1-44"></a> <span class="cf">return</span> stat;</span>
|
||||
<span id="cb1-45"><a href="#cb1-45"></a>}</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>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>In line 30, <code>tv</code> is added to <code>win</code> as a child.</p>
|
||||
<span id="cb1-40"><a href="#cb1-40"></a> app <span class="op">=</span> gtk_application_new <span class="op">(</span><span class="st">"com.github.ToshioCP.tfv1"</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 <span class="op">(</span>app<span class="op">,</span> <span class="st">"activate"</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 <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 <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 class="op">;</span></span>
|
||||
<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 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>
|
||||
<pre><code>$ cd src/tfv
|
||||
$ comp tfv1
|
||||
$ ./a.out</code></pre>
|
||||
<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>
|
||||
<p>There’s 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 won’t 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>There’s 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 won’t 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>
|
||||
<p>What we need to do is:</p>
|
||||
<ul>
|
||||
<li>Create a GtkScrolledWindow and insert it as a child of the GtkApplicationWindow; and</li>
|
||||
<li>Insert the GtkTextView widget to the GtkScrolledWindow as a child.</li>
|
||||
<li>Create a GtkScrolledWindow and insert it as a child of the
|
||||
GtkApplicationWindow</li>
|
||||
<li>Insert the GtkTextView widget to the GtkScrolledWindow as a
|
||||
child.</li>
|
||||
</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
|
||||
5a6
|
||||
> GtkWidget *scr;
|
||||
|
@ -187,60 +220,63 @@
|
|||
---
|
||||
> gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);
|
||||
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);</code></pre>
|
||||
<p>Here is the complete code of <code>tfv2.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"><gtk/gtk.h></span></span>
|
||||
<span id="cb3-2"><a href="#cb3-2"></a></span>
|
||||
<span id="cb3-3"><a href="#cb3-3"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb3-4"><a href="#cb3-4"></a>app_activate (GApplication *app, gpointer user_data) {</span>
|
||||
<span id="cb3-5"><a href="#cb3-5"></a> GtkWidget *win;</span>
|
||||
<span id="cb3-6"><a href="#cb3-6"></a> GtkWidget *scr;</span>
|
||||
<span id="cb3-7"><a href="#cb3-7"></a> GtkWidget *tv;</span>
|
||||
<span id="cb3-8"><a href="#cb3-8"></a> GtkTextBuffer *tb;</span>
|
||||
<span id="cb3-9"><a href="#cb3-9"></a> gchar *text;</span>
|
||||
<span id="cb3-10"><a href="#cb3-10"></a></span>
|
||||
<span id="cb3-11"><a href="#cb3-11"></a> text =</span>
|
||||
<span id="cb3-12"><a href="#cb3-12"></a> <span class="st">"Once upon a time, there was an old man who was called Taketori-no-Okina. "</span></span>
|
||||
<span id="cb3-13"><a href="#cb3-13"></a> <span class="st">"It is a japanese word that means a man whose work is making bamboo baskets.</span><span class="sc">\n</span><span class="st">"</span></span>
|
||||
<span id="cb3-14"><a href="#cb3-14"></a> <span class="st">"One day, he went into a mountain and found a shining bamboo. "</span></span>
|
||||
<span id="cb3-15"><a href="#cb3-15"></a> <span class="st">"</span><span class="sc">\"</span><span class="st">What a mysterious bamboo it is!,</span><span class="sc">\"</span><span class="st"> he said. "</span></span>
|
||||
<span id="cb3-16"><a href="#cb3-16"></a> <span class="st">"He cut it, then there was a small cute baby girl in it. "</span></span>
|
||||
<span id="cb3-17"><a href="#cb3-17"></a> <span class="st">"The girl was shining faintly. "</span></span>
|
||||
<span id="cb3-18"><a href="#cb3-18"></a> <span class="st">"He thought this baby girl is a gift from Heaven and took her home.</span><span class="sc">\n</span><span class="st">"</span></span>
|
||||
<span id="cb3-19"><a href="#cb3-19"></a> <span class="st">"His wife was surprized at his tale. "</span></span>
|
||||
<span id="cb3-20"><a href="#cb3-20"></a> <span class="st">"They were very happy because they had no children. "</span></span>
|
||||
<span id="cb3-21"><a href="#cb3-21"></a> ;</span>
|
||||
<span id="cb3-22"><a href="#cb3-22"></a> win = gtk_application_window_new (GTK_APPLICATION (app));</span>
|
||||
<span id="cb3-23"><a href="#cb3-23"></a> gtk_window_set_title (GTK_WINDOW (win), <span class="st">"Taketori"</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="cb3-25"><a href="#cb3-25"></a></span>
|
||||
<span id="cb3-26"><a href="#cb3-26"></a> scr = gtk_scrolled_window_new ();</span>
|
||||
<span id="cb3-27"><a href="#cb3-27"></a> gtk_window_set_child (GTK_WINDOW (win), scr);</span>
|
||||
<span id="cb3-28"><a href="#cb3-28"></a></span>
|
||||
<span id="cb3-29"><a href="#cb3-29"></a> tv = gtk_text_view_new ();</span>
|
||||
<span id="cb3-30"><a href="#cb3-30"></a> tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));</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="cb3-32"><a href="#cb3-32"></a> gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR);</span>
|
||||
<span id="cb3-33"><a href="#cb3-33"></a></span>
|
||||
<span id="cb3-34"><a href="#cb3-34"></a> gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);</span>
|
||||
<span id="cb3-35"><a href="#cb3-35"></a></span>
|
||||
<span id="cb3-36"><a href="#cb3-36"></a> gtk_widget_show (win);</span>
|
||||
<span id="cb3-37"><a href="#cb3-37"></a>}</span>
|
||||
<span id="cb3-38"><a href="#cb3-38"></a></span>
|
||||
<span id="cb3-39"><a href="#cb3-39"></a><span class="dt">int</span></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="cb3-41"><a href="#cb3-41"></a> GtkApplication *app;</span>
|
||||
<span id="cb3-42"><a href="#cb3-42"></a> <span class="dt">int</span> stat;</span>
|
||||
<span id="cb3-43"><a href="#cb3-43"></a></span>
|
||||
<span id="cb3-44"><a href="#cb3-44"></a> app = gtk_application_new (<span class="st">"com.github.ToshioCP.tfv2"</span>, G_APPLICATION_FLAGS_NONE);</span>
|
||||
<span id="cb3-45"><a href="#cb3-45"></a> g_signal_connect (app, <span class="st">"activate"</span>, G_CALLBACK (app_activate), NULL);</span>
|
||||
<span id="cb3-46"><a href="#cb3-46"></a> stat = g_application_run (G_APPLICATION (app), argc, argv);</span>
|
||||
<span id="cb3-47"><a href="#cb3-47"></a> g_object_unref (app);</span>
|
||||
<span id="cb3-48"><a href="#cb3-48"></a> <span class="cf">return</span> stat;</span>
|
||||
<span id="cb3-49"><a href="#cb3-49"></a>}</span></code></pre></div>
|
||||
<p>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.</p>
|
||||
> app = gtk_application_new ("com.github.ToshioCP.tfv2", G_APPLICATION_DEFAULT_FLAGS);</code></pre>
|
||||
<p>The whole code of <code>tfv2.c</code> is as follows.</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"><gtk/gtk.h></span></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-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="cb4-5"><a href="#cb4-5"></a> GtkWidget <span class="op">*</span>win<span class="op">;</span></span>
|
||||
<span id="cb4-6"><a href="#cb4-6"></a> GtkWidget <span class="op">*</span>scr<span class="op">;</span></span>
|
||||
<span id="cb4-7"><a href="#cb4-7"></a> GtkWidget <span class="op">*</span>tv<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-9"><a href="#cb4-9"></a> gchar <span class="op">*</span>text<span class="op">;</span></span>
|
||||
<span id="cb4-10"><a href="#cb4-10"></a></span>
|
||||
<span id="cb4-11"><a href="#cb4-11"></a> text <span class="op">=</span></span>
|
||||
<span id="cb4-12"><a href="#cb4-12"></a> <span class="st">"Once upon a time, there was an old man who was called Taketori-no-Okina. "</span></span>
|
||||
<span id="cb4-13"><a href="#cb4-13"></a> <span class="st">"It is a japanese word that means a man whose work is making bamboo baskets.</span><span class="sc">\n</span><span class="st">"</span></span>
|
||||
<span id="cb4-14"><a href="#cb4-14"></a> <span class="st">"One day, he went into a mountain and found a shining bamboo. "</span></span>
|
||||
<span id="cb4-15"><a href="#cb4-15"></a> <span class="st">"</span><span class="sc">\"</span><span class="st">What a mysterious bamboo it is!,</span><span class="sc">\"</span><span class="st"> he said. "</span></span>
|
||||
<span id="cb4-16"><a href="#cb4-16"></a> <span class="st">"He cut it, then there was a small cute baby girl in it. "</span></span>
|
||||
<span id="cb4-17"><a href="#cb4-17"></a> <span class="st">"The girl was shining faintly. "</span></span>
|
||||
<span id="cb4-18"><a href="#cb4-18"></a> <span class="st">"He thought this baby girl is a gift from Heaven and took her home.</span><span class="sc">\n</span><span class="st">"</span></span>
|
||||
<span id="cb4-19"><a href="#cb4-19"></a> <span class="st">"His wife was surprized at his story. "</span></span>
|
||||
<span id="cb4-20"><a href="#cb4-20"></a> <span class="st">"They were very happy because they had no children. "</span></span>
|
||||
<span id="cb4-21"><a href="#cb4-21"></a> <span class="op">;</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="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">"Taketori"</span><span class="op">);</span></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="cb4-25"><a href="#cb4-25"></a></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="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="cb4-28"><a href="#cb4-28"></a></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="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="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="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="cb4-33"><a href="#cb4-33"></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="cb4-35"><a href="#cb4-35"></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="cb4-37"><a href="#cb4-37"></a><span class="op">}</span></span>
|
||||
<span id="cb4-38"><a href="#cb4-38"></a></span>
|
||||
<span id="cb4-39"><a href="#cb4-39"></a><span class="dt">int</span></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="cb4-41"><a href="#cb4-41"></a> GtkApplication <span class="op">*</span>app<span class="op">;</span></span>
|
||||
<span id="cb4-42"><a href="#cb4-42"></a> <span class="dt">int</span> stat<span class="op">;</span></span>
|
||||
<span id="cb4-43"><a href="#cb4-43"></a></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">"com.github.ToshioCP.tfv2"</span><span class="op">,</span> G_APPLICATION_DEFAULT_FLAGS<span class="op">);</span></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">"activate"</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-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="cb4-47"><a href="#cb4-47"></a> g_object_unref <span class="op">(</span>app<span class="op">);</span></span>
|
||||
<span id="cb4-48"><a href="#cb4-48"></a> <span class="cf">return</span> stat<span class="op">;</span></span>
|
||||
<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 doesn’t extend even if you type a lot of characters,
|
||||
it just scrolls.</p>
|
||||
</div>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
|
||||
</body>
|
||||
|
|
250
docs/sec6.html
|
@ -111,18 +111,17 @@
|
|||
</div>
|
||||
</div>
|
||||
</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
|
||||
parameters or return a string. The knowledge of strings and memory
|
||||
management is useful to understand how to use these functions.</p>
|
||||
<h2 id="string-and-memory">String and memory</h2>
|
||||
<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
|
||||
as a pointer to a character array. They behaves like a string type which
|
||||
you may be familiar from other languages. So, this pointer is often
|
||||
called ‘a string’.</p>
|
||||
<p>In the following, <code>a</code> and <code>b</code> defined as
|
||||
character arrays, and are strings.</p>
|
||||
String is not a C type such as char, int, float or double, but a
|
||||
character array. It behaves like a string in other languages. So, the
|
||||
pointer is often called ‘a string’.</p>
|
||||
<p>The following is a sample program.</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>
|
||||
<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">'H'</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-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a><span class="co">/* *b is 'H' */</span></span>
|
||||
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a><span class="co">/* *(++b) is 'e' */</span></span></code></pre></div>
|
||||
<p>The array <code>a</code> has <code>char</code> elements and the size
|
||||
of ten. The first six elements are ‘H’, ‘e’, ‘l’, ‘l’, ‘o’ and ‘\0’.
|
||||
This array represents the string “Hello”. The first five elements are
|
||||
character codes that correspond to the characters. The sixth element is
|
||||
‘\0’, which is the same as zero, and indicates that the string ends
|
||||
there. The size of the array is 10, so 4 bytes aren’t used, but that’s
|
||||
OK, they are just ignored.</p>
|
||||
<p>The variable ‘b’ is a pointer to a character. Because <code>b</code>
|
||||
is assigned to be <code>a</code>, <code>a</code> and <code>b</code>
|
||||
point the same character (‘H’). The variable <code>a</code> is defined
|
||||
as an array and it can’t be changed. It always point the top address of
|
||||
the array. On the other hand, ‘b’ is a pointer, which is mutable, so
|
||||
<code>b</code> can be change. It is then possible to write statements
|
||||
like <code>++b</code>, which means take the value in b (n address),
|
||||
increase it by one, and store that back in <code>b</code>.</p>
|
||||
<p>If a pointer is NULL, it points to nothing. So, the pointer is not a
|
||||
string. A NULL string on the other hand will be a pointer which points
|
||||
to a location that contains <code>\0</code>, which is a string of length
|
||||
0 (or ““). Programs that use strings will include bugs if you aren’t
|
||||
careful when using NULL pointers.</p>
|
||||
<p>Another annoying problem is the memory that a string is allocated.
|
||||
There are four cases:</p>
|
||||
<p>An array <code>a</code> is defined as a <code>char</code> type array
|
||||
and its size is ten. The first five elements are ‘H’, ‘e’, ‘l’, ‘l’,
|
||||
‘o’. They are character codes. For example, ‘H’ is the same as 0x48 or
|
||||
72. The sixth element is ‘\0’, which is the same as zero, and indicates
|
||||
that the sequence of the data ends there. The array represents the
|
||||
string “Hello”.</p>
|
||||
<p>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.)</p>
|
||||
<p>The variable ‘b’ is a pointer to a character. It is assigned with
|
||||
<code>a</code>, so <code>b</code> points the first element of
|
||||
<code>a</code> (character ‘H’). The array <code>a</code> is immutable.
|
||||
So <code>a=a+1</code> causes syntax error.</p>
|
||||
<p>On the other hand, ‘b’ is a pointer type variable, which is mutable.
|
||||
So, <code>++b</code>, which increases <code>b</code> by one, is
|
||||
allowed.</p>
|
||||
<p>If a pointer is NULL, it points nothing. So, the pointer is not a
|
||||
string. It is different from empty string. Empty string is a pointer
|
||||
points <code>\0</code>.</p>
|
||||
<p>There are four cases:</p>
|
||||
<ul>
|
||||
<li>The string is read only;</li>
|
||||
<li>The string is in static memory area;</li>
|
||||
<li>The string is in stack; and</li>
|
||||
<li>The string is in memory allocated from the heap area.</li>
|
||||
<li>The string is read only</li>
|
||||
<li>The string is in static memory area</li>
|
||||
<li>The string is in stack</li>
|
||||
<li>The string is in memory allocated from the heap area</li>
|
||||
</ul>
|
||||
<h2 id="read-only-string">Read only string</h2>
|
||||
<p>A string literal in a C program is surrounded by double quotes and
|
||||
written as the following:</p>
|
||||
<p>A string literal is surrounded by double quotes like this:</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>
|
||||
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>s <span class="op">=</span> <span class="st">"Hello"</span></span></code></pre></div>
|
||||
<p>“Hello” is a string literal, and is stored in program memory. A
|
||||
string literal is read only. In the program above, <code>s</code> points
|
||||
the string literal.</p>
|
||||
<p>So, the following program is illegal.</p>
|
||||
<p>“Hello” is a string literal, and is read only. 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">'a'</span><span class="op">;</span></span></code></pre></div>
|
||||
<p>The result is undefined. Probably a bad thing will happen, for
|
||||
example, a segmentation fault.</p>
|
||||
<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
|
||||
your program by using the <code>string</code> command.</p>
|
||||
is compiled. It is possible to see the literal strings with
|
||||
<code>strings</code> command.</p>
|
||||
<pre><code>$ 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.
|
||||
... ... ...
|
||||
... ... ...</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>
|
||||
<p>If a string is defined as an array, it’s in either stored in the
|
||||
static memory area or stack. This depends on the class of the array. If
|
||||
the array’s class is <code>static</code>, then it’s placed in static
|
||||
memory area. This allocation and memory address is fixed for the life of
|
||||
the program. This area can be changed and is writable.</p>
|
||||
<p>If the array’s class is <code>auto</code>, then it’s placed in stack.
|
||||
If the array is defined inside a function, its default class is
|
||||
<code>auto</code>. The stack area will disappear when the function exits
|
||||
and 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>
|
||||
<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">'H'</span><span class="op">,</span> <span class="ch">'e'</span><span class="op">,</span> <span class="ch">'l'</span><span class="op">,</span> <span class="ch">'l'</span><span class="op">,</span> <span class="ch">'o'</span><span class="op">,</span> <span class="ch">'\0'</span><span class="op">};</span></span>
|
||||
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a><span class="dt">void</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="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">"Hello"</span><span class="op">;</span></span>
|
||||
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a></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">'a'</span><span class="op">;</span> <span class="co">/* Because the array is static, it'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">'a'</span><span class="op">;</span> <span class="co">/* Because the array is auto, it's writable. */</span></span>
|
||||
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a> printf <span class="op">(</span><span class="st">"%s</span><span class="sc">\n</span><span class="st">"</span><span class="op">,</span> a<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">"%s</span><span class="sc">\n</span><span class="st">"</span><span class="op">,</span> b<span class="op">);</span> <span class="co">/* Hallo */</span></span>
|
||||
<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 externally to a function and is
|
||||
global in its scope. Such variables are placed in static memory area
|
||||
even if the <code>static</code> class is left out. The compiler
|
||||
calculates the number of the elements in the right hand side (six), and
|
||||
then creates code that allocates six bytes in the static memory area and
|
||||
copies the data to this memory.</p>
|
||||
<p>The array <code>b</code> is defined inside the function so its class
|
||||
<p>If a string is defined as an array, it’s stored in static memory area
|
||||
or stack. It depends on the class of the array. If the array’s class is
|
||||
<code>static</code>, then it’s placed in static memory area. The
|
||||
allocated memory lives for the life of the program. This area is
|
||||
writable.</p>
|
||||
<p>If the array’s class is <code>auto</code>, it’s placed in stack. If
|
||||
the array is defined inside a function, its default class is
|
||||
<code>auto</code>. The stack area will disappear when the function
|
||||
returns to the caller. Arrays defined on the stack are writable.</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><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">'H'</span><span class="op">,</span> <span class="ch">'e'</span><span class="op">,</span> <span class="ch">'l'</span><span class="op">,</span> <span class="ch">'l'</span><span class="op">,</span> <span class="ch">'o'</span><span class="op">,</span> <span class="ch">'\0'</span><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><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="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">"Hello"</span><span class="op">;</span></span>
|
||||
<span id="cb5-6"><a href="#cb5-6" 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">'a'</span><span class="op">;</span> <span class="co">/* Because the array is static, it'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">'a'</span><span class="op">;</span> <span class="co">/* Because the array is auto, it's writable. */</span></span>
|
||||
<span id="cb5-9"><a href="#cb5-9" 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">"%s</span><span class="sc">\n</span><span class="st">"</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">"%s</span><span class="sc">\n</span><span class="st">"</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>
|
||||
<p>The array <code>a</code> is defined out of functions. It is placed in
|
||||
the static memory area even if the <code>static</code> class is left
|
||||
out. The compiler calculates the number of the elements (six) and
|
||||
allocates six bytes in the static memory area. Then, it copies “Hello”
|
||||
literal string data to the 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
|
||||
in the string literal. It has six elements as the zero termination
|
||||
character is also included. The compiler creates code which allocates
|
||||
six bytes memory in the stack and copies the data to the memory.</p>
|
||||
in the string literal. It is six because it has ‘\0’ terminator. The
|
||||
compiler allocates six bytes in the stack and copies “Hello” litaral
|
||||
string to the stack memory.</p>
|
||||
<p>Both <code>a</code> and <code>b</code> are writable.</p>
|
||||
<p>The memory is managed by the executable program. You don’t need your
|
||||
program to allocate or free the memory for <code>a</code> and
|
||||
<code>b</code>. The array <code>a</code> is created then the program is
|
||||
first run and remains for the life of the program. The array
|
||||
<code>b</code> is created on the stack then the function is called,
|
||||
disappears when the function returns.</p>
|
||||
<p>The memory is allocated and freed by the program automatically so you
|
||||
don’t need to allocate or free. The array <code>a</code> is alive during
|
||||
the program’s life time. The array <code>b</code> is alive when the
|
||||
function is called until the function returns to the caller.</p>
|
||||
<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
|
||||
standard C library provides <code>malloc</code> to get memory and
|
||||
<p>You can get, use and release memory from the heap area. The standard
|
||||
C library provides <code>malloc</code> to get memory and
|
||||
<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
|
||||
support for some additional GLib functionality.</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>
|
||||
<code>g_new</code> and <code>g_free</code>. They are similar to
|
||||
<code>malloc</code> and <code>free</code>.</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_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>
|
||||
<ul>
|
||||
<li><code>struct_type</code> is the type of the element of the
|
||||
|
@ -235,37 +238,37 @@ array.</li>
|
|||
<code>struct_type</code>.</li>
|
||||
</ul>
|
||||
<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>
|
||||
<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="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="cb6-4"><a href="#cb6-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="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="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="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>
|
||||
<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="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="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="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a></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="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="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="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>
|
||||
<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>g_free <span class="op">(</span>gpointer mem<span class="op">);</span></span></code></pre></div>
|
||||
<div class="sourceCode" id="cb8"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span></span>
|
||||
<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.
|
||||
<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.
|
||||
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>
|
||||
<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="cb8-3"><a href="#cb8-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="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>
|
||||
<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="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="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a></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="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 doesn’t point allocated memory it will cause an
|
||||
error, specifically, a segmentation fault.</p>
|
||||
<p>Some GLib functions allocate memory. For example,
|
||||
<code>g_strdup</code> allocates memory and copies a string given as an
|
||||
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>
|
||||
<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">"Hello"</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>
|
||||
<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="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">"Hello"</span><span class="op">);</span></span>
|
||||
<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
|
||||
the end it. <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
|
||||
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 start
|
||||
address of the memory. <code>g_free</code> returns the memory to the
|
||||
heap area.</p>
|
||||
<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
|
||||
no longer needed.</p>
|
||||
</blockquote>
|
||||
<p>The function reference will describe if the returned value needs to
|
||||
be freed. If you forget to free the allocated memory it will remain
|
||||
allocated. Repeated use will cause more memory to be allocated to the
|
||||
program, which will grow over time. This is called a memory leak, 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.</p>
|
||||
<p>Some GLib functions return a string which mustn’t 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 aren’t be allowed to be changed
|
||||
or freed.</p>
|
||||
<p>If a variable is qualified with <code>const</code>, the variable
|
||||
can’t be assigned except during initialization.</p>
|
||||
<p>If you forget to free the allocated memory it will remain until the
|
||||
program ends. Repeated allocation and no freeing cause memory leak. It
|
||||
is a bug and may bring a serious problem.</p>
|
||||
<h2 id="const-qualifier">const qualifier</h2>
|
||||
<p>A <code>const</code> qualified variable can be assigned to initialize
|
||||
it. Once it is initialized, it is never allowed to change or free.</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>
|
||||
<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>
|
||||
<p>If a function returns <code>const char*</code> type, the string can’t
|
||||
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>
|
||||
<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>
|
||||
|
|
544
docs/sec7.html
|
@ -5,7 +5,7 @@
|
|||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
||||
<title>Gtk4 tutorial</title>
|
||||
<title>GTK 4 tutorial</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
|
@ -113,252 +113,354 @@
|
|||
</nav>
|
||||
<h1 id="widgets-3">Widgets (3)</h1>
|
||||
<h2 id="open-signal">Open signal</h2>
|
||||
<h3 id="g_application_handles_open-flag">G_APPLICATION_HANDLES_OPEN flag</h3>
|
||||
<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, we’ll take the easiest one.</p>
|
||||
<p>When the program starts, we will give the filename to open as an argument.</p>
|
||||
<h3 id="g_application_handles_open-flag">G_APPLICATION_HANDLES_OPEN
|
||||
flag</h3>
|
||||
<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>
|
||||
<p>It will open the file and insert its contents into the GtkTextBuffer.</p>
|
||||
<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>
|
||||
<p>When GtkApplication is created, a flag (with the type GApplicationFlags) is provided as an argument.</p>
|
||||
<div class="sourceCode" id="cb2"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true"></a>GtkApplication *</span>
|
||||
<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>
|
||||
<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>
|
||||
<pre><code>GApplicationFlags' Members
|
||||
|
||||
G_APPLICATION_FLAGS_NONE Default. (No argument allowed)
|
||||
... ... ...
|
||||
G_APPLICATION_HANDLES_OPEN This application handles opening files (in the primary instance).
|
||||
... ... ...</code></pre>
|
||||
<p>There are ten flags in total, but we only need two of them so far. We’ve 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>
|
||||
<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>
|
||||
<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">"com.github.ToshioCP.tfv3"</span>, G_APPLICATION_HANDLES_OPEN);</span></code></pre></div>
|
||||
<p>The program will open the file and insert its contents into the
|
||||
GtkTextBuffer.</p>
|
||||
<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>
|
||||
<p>When GtkApplication is created, a flag (GApplicationFlags) is given
|
||||
as an argument.</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>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>
|
||||
<p>This tutorial explains only two flags,
|
||||
<code>G_APPLICATION_DEFAULT_FLAGS</code> and
|
||||
<code>G_APPLICATION_HANDLES_OPEN</code>.
|
||||
(<code>G_APPLICATION_FLAGS_NONE</code> was used instead of
|
||||
<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>We’ve 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">"com.github.ToshioCP.tfv3"</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>
|
||||
<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>
|
||||
<li>activate signal — This signal is emitted when there’s no argument.</li>
|
||||
<li>open signal — This signal is emitted when there is at least one argument.</li>
|
||||
<li>activate signal — This signal is emitted when there’s no
|
||||
argument.</li>
|
||||
<li>open signal — This signal is emitted when there is at least one
|
||||
argument.</li>
|
||||
</ul>
|
||||
<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>
|
||||
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true"></a> gpointer files,</span>
|
||||
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true"></a> gint n_files,</span>
|
||||
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true"></a> gchar *hint,</span>
|
||||
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true"></a> gpointer user_data)</span></code></pre></div>
|
||||
<div class="sourceCode" id="cb4"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span></span>
|
||||
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>open <span class="op">(</span></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="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a> gpointer files<span class="op">,</span></span>
|
||||
<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>
|
||||
<ul>
|
||||
<li><code>application</code> — the application (usually GtkApplication)</li>
|
||||
<li><code>files</code> — an array of GFiles. [array length=n_files] [element-type GFile]</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>
|
||||
<li><code>self</code> — the application instance (usually
|
||||
GtkApplication)</li>
|
||||
<li><code>files</code> — an array of GFiles. [array length=n_files]
|
||||
[element-type GFile]</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>
|
||||
<p>How to read a specified file (GFile) will be described next.</p>
|
||||
<h2 id="making-a-file-viewer">Making a file viewer</h2>
|
||||
<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>
|
||||
<li>When arguments are given, it treats the first argument as a filename and opens it.</li>
|
||||
<li>If opening the file succeeds, it reads the contents of the file and inserts it to GtkTextBuffer and then shows the window.</li>
|
||||
<li>If it fails to open the file, it will show an error message and quit.</li>
|
||||
<li>If there’s no argument, it will 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>When arguments are given, it recognizes the first argument as a
|
||||
filename and opens it.</li>
|
||||
<li>The second argument and after are ignored.</li>
|
||||
<li>If there’s no argument, it shows an error message and quit.</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>
|
||||
<p>The program which does this 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"><gtk/gtk.h></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>app_activate (GApplication *app, gpointer user_data) {</span>
|
||||
<span id="cb6-5"><a href="#cb6-5"></a> g_print (<span class="st">"You need a filename argument.</span><span class="sc">\n</span><span class="st">"</span>);</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>app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer user_data) {</span>
|
||||
<span id="cb6-10"><a href="#cb6-10"></a> GtkWidget *win;</span>
|
||||
<span id="cb6-11"><a href="#cb6-11"></a> GtkWidget *scr;</span>
|
||||
<span id="cb6-12"><a href="#cb6-12"></a> GtkWidget *tv;</span>
|
||||
<span id="cb6-13"><a href="#cb6-13"></a> GtkTextBuffer *tb;</span>
|
||||
<span id="cb6-14"><a href="#cb6-14"></a> <span class="dt">char</span> *contents;</span>
|
||||
<span id="cb6-15"><a href="#cb6-15"></a> gsize length;</span>
|
||||
<span id="cb6-16"><a href="#cb6-16"></a> <span class="dt">char</span> *filename;</span>
|
||||
<span id="cb6-17"><a href="#cb6-17"></a></span>
|
||||
<span id="cb6-18"><a href="#cb6-18"></a> win = gtk_application_window_new (GTK_APPLICATION (app));</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="cb6-20"><a href="#cb6-20"></a></span>
|
||||
<span id="cb6-21"><a href="#cb6-21"></a> scr = gtk_scrolled_window_new ();</span>
|
||||
<span id="cb6-22"><a href="#cb6-22"></a> gtk_window_set_child (GTK_WINDOW (win), scr);</span>
|
||||
<span id="cb6-23"><a href="#cb6-23"></a></span>
|
||||
<span id="cb6-24"><a href="#cb6-24"></a> tv = gtk_text_view_new ();</span>
|
||||
<span id="cb6-25"><a href="#cb6-25"></a> tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));</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="cb6-27"><a href="#cb6-27"></a> gtk_text_view_set_editable (GTK_TEXT_VIEW (tv), FALSE);</span>
|
||||
<span id="cb6-28"><a href="#cb6-28"></a> gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);</span>
|
||||
<span id="cb6-29"><a href="#cb6-29"></a></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, &contents, &length, NULL, NULL)) {</span>
|
||||
<span id="cb6-31"><a href="#cb6-31"></a> gtk_text_buffer_set_text (tb, contents, length);</span>
|
||||
<span id="cb6-32"><a href="#cb6-32"></a> g_free (contents);</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="cb6-34"><a href="#cb6-34"></a> gtk_window_set_title (GTK_WINDOW (win), filename);</span>
|
||||
<span id="cb6-35"><a href="#cb6-35"></a> g_free (filename);</span>
|
||||
<span id="cb6-36"><a href="#cb6-36"></a> }</span>
|
||||
<span id="cb6-37"><a href="#cb6-37"></a> gtk_widget_show (win);</span>
|
||||
<span id="cb6-38"><a href="#cb6-38"></a> } <span class="cf">else</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="cb6-40"><a href="#cb6-40"></a> g_print (<span class="st">"No such file: %s.</span><span class="sc">\n</span><span class="st">"</span>, filename);</span>
|
||||
<span id="cb6-41"><a href="#cb6-41"></a> g_free (filename);</span>
|
||||
<span id="cb6-42"><a href="#cb6-42"></a> }</span>
|
||||
<span id="cb6-43"><a href="#cb6-43"></a> gtk_window_destroy (GTK_WINDOW (win));</span>
|
||||
<span id="cb6-44"><a href="#cb6-44"></a> }</span>
|
||||
<span id="cb6-45"><a href="#cb6-45"></a>}</span>
|
||||
<span id="cb6-46"><a href="#cb6-46"></a></span>
|
||||
<span id="cb6-47"><a href="#cb6-47"></a><span class="dt">int</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="cb6-49"><a href="#cb6-49"></a> GtkApplication *app;</span>
|
||||
<span id="cb6-50"><a href="#cb6-50"></a> <span class="dt">int</span> stat;</span>
|
||||
<span id="cb6-51"><a href="#cb6-51"></a></span>
|
||||
<span id="cb6-52"><a href="#cb6-52"></a> app = gtk_application_new (<span class="st">"com.github.ToshioCP.tfv3"</span>, G_APPLICATION_HANDLES_OPEN);</span>
|
||||
<span id="cb6-53"><a href="#cb6-53"></a> g_signal_connect (app, <span class="st">"activate"</span>, G_CALLBACK (app_activate), NULL);</span>
|
||||
<span id="cb6-54"><a href="#cb6-54"></a> g_signal_connect (app, <span class="st">"open"</span>, G_CALLBACK (app_open), NULL);</span>
|
||||
<span id="cb6-55"><a href="#cb6-55"></a> stat = g_application_run (G_APPLICATION (app), argc, argv);</span>
|
||||
<span id="cb6-56"><a href="#cb6-56"></a> g_object_unref (app);</span>
|
||||
<span id="cb6-57"><a href="#cb6-57"></a> <span class="cf">return</span> stat;</span>
|
||||
<span id="cb6-58"><a href="#cb6-58"></a>}</span></code></pre></div>
|
||||
<p>The program is shown below.</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="pp">#include </span><span class="im"><gtk/gtk.h></span></span>
|
||||
<span id="cb5-2"><a href="#cb5-2"></a></span>
|
||||
<span id="cb5-3"><a href="#cb5-3"></a><span class="dt">static</span> <span class="dt">void</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="cb5-5"><a href="#cb5-5"></a> g_printerr <span class="op">(</span><span class="st">"You need a filename argument.</span><span class="sc">\n</span><span class="st">"</span><span class="op">);</span></span>
|
||||
<span id="cb5-6"><a href="#cb5-6"></a><span class="op">}</span></span>
|
||||
<span id="cb5-7"><a href="#cb5-7"></a></span>
|
||||
<span id="cb5-8"><a href="#cb5-8"></a><span class="dt">static</span> <span class="dt">void</span></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="cb5-10"><a href="#cb5-10"></a> GtkWidget <span class="op">*</span>win<span class="op">;</span></span>
|
||||
<span id="cb5-11"><a href="#cb5-11"></a> GtkWidget <span class="op">*</span>scr<span class="op">;</span></span>
|
||||
<span id="cb5-12"><a href="#cb5-12"></a> GtkWidget <span class="op">*</span>tv<span class="op">;</span></span>
|
||||
<span id="cb5-13"><a href="#cb5-13"></a> GtkTextBuffer <span class="op">*</span>tb<span class="op">;</span></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="cb5-15"><a href="#cb5-15"></a> gsize length<span class="op">;</span></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="cb5-17"><a href="#cb5-17"></a></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="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="cb5-20"><a href="#cb5-20"></a></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="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="cb5-23"><a href="#cb5-23"></a></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="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 <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_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="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="cb5-29"><a href="#cb5-29"></a></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">&</span>contents<span class="op">,</span> <span class="op">&</span>length<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">))</span> <span class="op">{</span></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="cb5-32"><a href="#cb5-32"></a> g_free <span class="op">(</span>contents<span class="op">);</span></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="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="cb5-35"><a href="#cb5-35"></a> g_free <span class="op">(</span>filename<span class="op">);</span></span>
|
||||
<span id="cb5-36"><a href="#cb5-36"></a> <span class="op">}</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="cb5-38"><a href="#cb5-38"></a> <span class="op">}</span> <span class="cf">else</span> <span class="op">{</span></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="cb5-40"><a href="#cb5-40"></a> g_printerr <span class="op">(</span><span class="st">"No such file: %s.</span><span class="sc">\n</span><span class="st">"</span><span class="op">,</span> filename<span class="op">);</span></span>
|
||||
<span id="cb5-41"><a href="#cb5-41"></a> g_free <span class="op">(</span>filename<span class="op">);</span></span>
|
||||
<span id="cb5-42"><a href="#cb5-42"></a> <span class="op">}</span> <span class="cf">else</span></span>
|
||||
<span id="cb5-43"><a href="#cb5-43"></a> g_printerr <span class="op">(</span><span class="st">"File can't be opened.</span><span class="sc">\n</span><span class="st">"</span><span class="op">);</span></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="cb5-45"><a href="#cb5-45"></a> <span class="op">}</span></span>
|
||||
<span id="cb5-46"><a href="#cb5-46"></a><span class="op">}</span></span>
|
||||
<span id="cb5-47"><a href="#cb5-47"></a></span>
|
||||
<span id="cb5-48"><a href="#cb5-48"></a><span class="dt">int</span></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="cb5-50"><a href="#cb5-50"></a> GtkApplication <span class="op">*</span>app<span class="op">;</span></span>
|
||||
<span id="cb5-51"><a href="#cb5-51"></a> <span class="dt">int</span> stat<span class="op">;</span></span>
|
||||
<span id="cb5-52"><a href="#cb5-52"></a></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">"com.github.ToshioCP.tfv3"</span><span class="op">,</span> G_APPLICATION_HANDLES_OPEN<span class="op">);</span></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">"activate"</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-55"><a href="#cb5-55"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">"open"</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-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="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>
|
||||
<pre><code>$ comp tfv3
|
||||
$ ./a.out tfv3.c</code></pre>
|
||||
<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>
|
||||
<p>Let’s 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>
|
||||
<li><code>G_APPLICATION_FLAGS_NONE</code> is replaced by <code>G_APPLICATION_HANDLES_OPEN</code>; and</li>
|
||||
<li><code>g_signal_connect (app, "open", G_CALLBACK (on_open), NULL)</code> is added.</li>
|
||||
<li><code>G_APPLICATION_DEFAULT_FLAGS</code> is replaced by
|
||||
<code>G_APPLICATION_HANDLES_OPEN</code></li>
|
||||
<li><code>g_signal_connect (app, "open", G_CALLBACK (app_open), NULL)</code>
|
||||
is added.</li>
|
||||
</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>The main functionality is the in the handler <code>app_open</code>. It</p>
|
||||
<p>When the flag <code>G_APPLICATION_HANDLES_OPEN</code> is given to
|
||||
<code>gtk_application_new</code> function, the application behaves like
|
||||
this:</p>
|
||||
<ul>
|
||||
<li>Creates GtkApplicationWindow, GtkScrolledWindow, GtkTextView and GtkTextBuffer and connects them together;</li>
|
||||
<li>Sets wrap mode to <code>GTK_WRAP_WORD_CHAR</code> in GtktextView;</li>
|
||||
<li>Sets GtkTextView to non-editable because the program isn’t an editor but only a viewer;</li>
|
||||
<li>Reads the file and inserts the text into GtkTextBuffer (this will be explained in detail later); and</li>
|
||||
<li>If the file is not opened then outputs an error message and destroys the window. This makes the application quit.</li>
|
||||
<li>If the application is run without command line arguments, it emits
|
||||
“activate” signal when it is activated.</li>
|
||||
<li>If the application is run with command line arguments, it emits
|
||||
“open” signal when it is activated.</li>
|
||||
</ul>
|
||||
<p>The following is the important file reading part of the program and is shown again below.</p>
|
||||
<div class="sourceCode" id="cb8"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true"></a><span class="cf">if</span> (g_file_load_contents (files[<span class="dv">0</span>], NULL, &contents, &length, NULL, NULL)) {</span>
|
||||
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true"></a> gtk_text_buffer_set_text (tb, contents, length);</span>
|
||||
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true"></a> g_free (contents);</span>
|
||||
<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>
|
||||
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true"></a> gtk_window_set_title (GTK_WINDOW (win), filename);</span>
|
||||
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true"></a> g_free (filename);</span>
|
||||
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true"></a> }</span>
|
||||
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true"></a> gtk_widget_show (win);</span>
|
||||
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true"></a>} <span class="cf">else</span> {</span>
|
||||
<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>
|
||||
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true"></a> g_print (<span class="st">"No such file: %s.</span><span class="sc">\n</span><span class="st">"</span>, filename);</span>
|
||||
<span id="cb8-12"><a href="#cb8-12" aria-hidden="true"></a> g_free (filename);</span>
|
||||
<span id="cb8-13"><a href="#cb8-13" aria-hidden="true"></a> }</span>
|
||||
<span id="cb8-14"><a href="#cb8-14" aria-hidden="true"></a> gtk_window_destroy (GTK_WINDOW (win));</span>
|
||||
<span id="cb8-15"><a href="#cb8-15" aria-hidden="true"></a>}</span></code></pre></div>
|
||||
<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 file’s contents are successfully loaded and <code>FALSE</code> if an error occurs.</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>
|
||||
<p>The handler <code>app_activate</code> becomes very simple. It just
|
||||
outputs the error message and return to the caller. Then the application
|
||||
quits immediately because no window is created.</p>
|
||||
<p>The main work is done in the handler <code>app_open</code>.</p>
|
||||
<ul>
|
||||
<li>Creates GtkApplicationWindow, GtkScrolledWindow, GtkTextView and
|
||||
GtkTextBuffer and connects them together</li>
|
||||
<li>Sets wrap mode to <code>GTK_WRAP_WORD_CHAR</code> in
|
||||
GtktextView</li>
|
||||
<li>Sets GtkTextView to non-editable because the program isn’t an editor
|
||||
but only a viewer</li>
|
||||
<li>Reads the file and inserts the text into GtkTextBuffer (this will be
|
||||
explained later)</li>
|
||||
<li>If the file is not opened, outputs an error message and destroys the
|
||||
window. This makes the application quit.</li>
|
||||
</ul>
|
||||
<p>The following is the file reading part of the program again.</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">&</span>contents<span class="op">,</span> <span class="op">&</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">"No such file: %s.</span><span class="sc">\n</span><span class="st">"</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">"File can't be opened.</span><span class="sc">\n</span><span class="st">"</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
|
||||
file’s 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>
|
||||
<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>
|
||||
<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>
|
||||
<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 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>
|
||||
<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"><gtk/gtk.h></span></span>
|
||||
<span id="cb9-2"><a href="#cb9-2"></a></span>
|
||||
<span id="cb9-3"><a href="#cb9-3"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb9-4"><a href="#cb9-4"></a>app_activate (GApplication *app, gpointer user_data) {</span>
|
||||
<span id="cb9-5"><a href="#cb9-5"></a> g_print (<span class="st">"You need a filename argument.</span><span class="sc">\n</span><span class="st">"</span>);</span>
|
||||
<span id="cb9-6"><a href="#cb9-6"></a>}</span>
|
||||
<span id="cb9-7"><a href="#cb9-7"></a></span>
|
||||
<span id="cb9-8"><a href="#cb9-8"></a><span class="dt">static</span> <span class="dt">void</span></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="cb9-10"><a href="#cb9-10"></a> GtkWidget *win;</span>
|
||||
<span id="cb9-11"><a href="#cb9-11"></a> GtkWidget *nb;</span>
|
||||
<span id="cb9-12"><a href="#cb9-12"></a> GtkWidget *lab;</span>
|
||||
<span id="cb9-13"><a href="#cb9-13"></a> GtkNotebookPage *nbp;</span>
|
||||
<span id="cb9-14"><a href="#cb9-14"></a> GtkWidget *scr;</span>
|
||||
<span id="cb9-15"><a href="#cb9-15"></a> GtkWidget *tv;</span>
|
||||
<span id="cb9-16"><a href="#cb9-16"></a> GtkTextBuffer *tb;</span>
|
||||
<span id="cb9-17"><a href="#cb9-17"></a> <span class="dt">char</span> *contents;</span>
|
||||
<span id="cb9-18"><a href="#cb9-18"></a> gsize length;</span>
|
||||
<span id="cb9-19"><a href="#cb9-19"></a> <span class="dt">char</span> *filename;</span>
|
||||
<span id="cb9-20"><a href="#cb9-20"></a> <span class="dt">int</span> i;</span>
|
||||
<span id="cb9-21"><a href="#cb9-21"></a></span>
|
||||
<span id="cb9-22"><a href="#cb9-22"></a> win = gtk_application_window_new (GTK_APPLICATION (app));</span>
|
||||
<span id="cb9-23"><a href="#cb9-23"></a> gtk_window_set_title (GTK_WINDOW (win), <span class="st">"file viewer"</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="cb9-25"><a href="#cb9-25"></a> gtk_window_maximize (GTK_WINDOW (win));</span>
|
||||
<span id="cb9-26"><a href="#cb9-26"></a></span>
|
||||
<span id="cb9-27"><a href="#cb9-27"></a> nb = gtk_notebook_new ();</span>
|
||||
<span id="cb9-28"><a href="#cb9-28"></a> gtk_window_set_child (GTK_WINDOW (win), nb);</span>
|
||||
<span id="cb9-29"><a href="#cb9-29"></a></span>
|
||||
<span id="cb9-30"><a href="#cb9-30"></a> <span class="cf">for</span> (i = <span class="dv">0</span>; i < n_files; i++) {</span>
|
||||
<span id="cb9-31"><a href="#cb9-31"></a> <span class="cf">if</span> (g_file_load_contents (files[i], NULL, &contents, &length, NULL, NULL)) {</span>
|
||||
<span id="cb9-32"><a href="#cb9-32"></a> scr = gtk_scrolled_window_new ();</span>
|
||||
<span id="cb9-33"><a href="#cb9-33"></a> tv = gtk_text_view_new ();</span>
|
||||
<span id="cb9-34"><a href="#cb9-34"></a> tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));</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="cb9-36"><a href="#cb9-36"></a> gtk_text_view_set_editable (GTK_TEXT_VIEW (tv), FALSE);</span>
|
||||
<span id="cb9-37"><a href="#cb9-37"></a> gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);</span>
|
||||
<span id="cb9-38"><a href="#cb9-38"></a></span>
|
||||
<span id="cb9-39"><a href="#cb9-39"></a> gtk_text_buffer_set_text (tb, contents, length);</span>
|
||||
<span id="cb9-40"><a href="#cb9-40"></a> g_free (contents);</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="cb9-42"><a href="#cb9-42"></a> lab = gtk_label_new (filename);</span>
|
||||
<span id="cb9-43"><a href="#cb9-43"></a> g_free (filename);</span>
|
||||
<span id="cb9-44"><a href="#cb9-44"></a> } <span class="cf">else</span></span>
|
||||
<span id="cb9-45"><a href="#cb9-45"></a> lab = gtk_label_new (<span class="st">""</span>);</span>
|
||||
<span id="cb9-46"><a href="#cb9-46"></a> gtk_notebook_append_page (GTK_NOTEBOOK (nb), scr, lab);</span>
|
||||
<span id="cb9-47"><a href="#cb9-47"></a> nbp = gtk_notebook_get_page (GTK_NOTEBOOK (nb), scr);</span>
|
||||
<span id="cb9-48"><a href="#cb9-48"></a> g_object_set (nbp, <span class="st">"tab-expand"</span>, TRUE, NULL);</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="cb9-50"><a href="#cb9-50"></a> g_print (<span class="st">"No such file: %s.</span><span class="sc">\n</span><span class="st">"</span>, filename);</span>
|
||||
<span id="cb9-51"><a href="#cb9-51"></a> g_free (filename);</span>
|
||||
<span id="cb9-52"><a href="#cb9-52"></a> } <span class="cf">else</span></span>
|
||||
<span id="cb9-53"><a href="#cb9-53"></a> g_print (<span class="st">"No valid file is given</span><span class="sc">\n</span><span class="st">"</span>);</span>
|
||||
<span id="cb9-54"><a href="#cb9-54"></a> }</span>
|
||||
<span id="cb9-55"><a href="#cb9-55"></a> <span class="cf">if</span> (gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb)) > <span class="dv">0</span>)</span>
|
||||
<span id="cb9-56"><a href="#cb9-56"></a> gtk_widget_show (win);</span>
|
||||
<span id="cb9-57"><a href="#cb9-57"></a> <span class="cf">else</span></span>
|
||||
<span id="cb9-58"><a href="#cb9-58"></a> gtk_window_destroy (GTK_WINDOW (win));</span>
|
||||
<span id="cb9-59"><a href="#cb9-59"></a>}</span>
|
||||
<span id="cb9-60"><a href="#cb9-60"></a></span>
|
||||
<span id="cb9-61"><a href="#cb9-61"></a><span class="dt">int</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="cb9-63"><a href="#cb9-63"></a> GtkApplication *app;</span>
|
||||
<span id="cb9-64"><a href="#cb9-64"></a> <span class="dt">int</span> stat;</span>
|
||||
<span id="cb9-65"><a href="#cb9-65"></a></span>
|
||||
<span id="cb9-66"><a href="#cb9-66"></a> app = gtk_application_new (<span class="st">"com.github.ToshioCP.tfv4"</span>, G_APPLICATION_HANDLES_OPEN);</span>
|
||||
<span id="cb9-67"><a href="#cb9-67"></a> g_signal_connect (app, <span class="st">"activate"</span>, G_CALLBACK (app_activate), NULL);</span>
|
||||
<span id="cb9-68"><a href="#cb9-68"></a> g_signal_connect (app, <span class="st">"open"</span>, G_CALLBACK (app_open), NULL);</span>
|
||||
<span id="cb9-69"><a href="#cb9-69"></a> stat = g_application_run (G_APPLICATION (app), argc, argv);</span>
|
||||
<span id="cb9-70"><a href="#cb9-70"></a> g_object_unref (app);</span>
|
||||
<span id="cb9-71"><a href="#cb9-71"></a> <span class="cf">return</span> stat;</span>
|
||||
<span id="cb9-72"><a href="#cb9-72"></a>}</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>
|
||||
<p>The left image is the window at the startup. The file
|
||||
<code>pr1.c</code> is shown and its filename is in the left tab. After
|
||||
clicking on the right tab, the contents of the file <code>tfv1.c</code>
|
||||
is shown. The right image is the screenshot.</p>
|
||||
<p>The following is <code>tfv4.c</code>. It has GtkNoteBook widget. It
|
||||
is inserted as a child of GtkApplicationWindow and contains multiple
|
||||
GtkScrolledWindow.</p>
|
||||
<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"><gtk/gtk.h></span></span>
|
||||
<span id="cb8-2"><a href="#cb8-2"></a></span>
|
||||
<span id="cb8-3"><a href="#cb8-3"></a><span class="dt">static</span> <span class="dt">void</span></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="cb8-5"><a href="#cb8-5"></a> g_printerr <span class="op">(</span><span class="st">"You need a filename argument.</span><span class="sc">\n</span><span class="st">"</span><span class="op">);</span></span>
|
||||
<span id="cb8-6"><a href="#cb8-6"></a><span class="op">}</span></span>
|
||||
<span id="cb8-7"><a href="#cb8-7"></a></span>
|
||||
<span id="cb8-8"><a href="#cb8-8"></a><span class="dt">static</span> <span class="dt">void</span></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="cb8-10"><a href="#cb8-10"></a> GtkWidget <span class="op">*</span>win<span class="op">;</span></span>
|
||||
<span id="cb8-11"><a href="#cb8-11"></a> GtkWidget <span class="op">*</span>nb<span class="op">;</span></span>
|
||||
<span id="cb8-12"><a href="#cb8-12"></a> GtkWidget <span class="op">*</span>lab<span class="op">;</span></span>
|
||||
<span id="cb8-13"><a href="#cb8-13"></a> GtkNotebookPage <span class="op">*</span>nbp<span class="op">;</span></span>
|
||||
<span id="cb8-14"><a href="#cb8-14"></a> GtkWidget <span class="op">*</span>scr<span class="op">;</span></span>
|
||||
<span id="cb8-15"><a href="#cb8-15"></a> GtkWidget <span class="op">*</span>tv<span class="op">;</span></span>
|
||||
<span id="cb8-16"><a href="#cb8-16"></a> GtkTextBuffer <span class="op">*</span>tb<span class="op">;</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="cb8-18"><a href="#cb8-18"></a> gsize length<span class="op">;</span></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="cb8-20"><a href="#cb8-20"></a> <span class="dt">int</span> i<span class="op">;</span></span>
|
||||
<span id="cb8-21"><a href="#cb8-21"></a></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="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">"file viewer"</span><span class="op">);</span></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="cb8-25"><a href="#cb8-25"></a></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="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="cb8-28"><a href="#cb8-28"></a></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"><</span> n_files<span class="op">;</span> i<span class="op">++)</span> <span class="op">{</span></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">&</span>contents<span class="op">,</span> <span class="op">&</span>length<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">))</span> <span class="op">{</span></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="cb8-32"><a href="#cb8-32"></a> tv <span class="op">=</span> gtk_text_view_new <span class="op">();</span></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="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="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="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="cb8-37"><a href="#cb8-37"></a></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="cb8-39"><a href="#cb8-39"></a> g_free <span class="op">(</span>contents<span class="op">);</span></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="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="cb8-42"><a href="#cb8-42"></a> g_free <span class="op">(</span>filename<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> lab <span class="op">=</span> gtk_label_new <span class="op">(</span><span class="st">""</span><span class="op">);</span></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="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="cb8-47"><a href="#cb8-47"></a> g_object_set <span class="op">(</span>nbp<span class="op">,</span> <span class="st">"tab-expand"</span><span class="op">,</span> TRUE<span class="op">,</span> NULL<span class="op">);</span></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="cb8-49"><a href="#cb8-49"></a> g_printerr <span class="op">(</span><span class="st">"No such file: %s.</span><span class="sc">\n</span><span class="st">"</span><span class="op">,</span> filename<span class="op">);</span></span>
|
||||
<span id="cb8-50"><a href="#cb8-50"></a> g_free <span class="op">(</span>filename<span class="op">);</span></span>
|
||||
<span id="cb8-51"><a href="#cb8-51"></a> <span class="op">}</span> <span class="cf">else</span></span>
|
||||
<span id="cb8-52"><a href="#cb8-52"></a> g_printerr <span class="op">(</span><span class="st">"No valid file is given</span><span class="sc">\n</span><span class="st">"</span><span class="op">);</span></span>
|
||||
<span id="cb8-53"><a href="#cb8-53"></a> <span class="op">}</span></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">></span> <span class="dv">0</span><span class="op">)</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="cb8-56"><a href="#cb8-56"></a> <span class="cf">else</span></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="cb8-58"><a href="#cb8-58"></a><span class="op">}</span></span>
|
||||
<span id="cb8-59"><a href="#cb8-59"></a></span>
|
||||
<span id="cb8-60"><a href="#cb8-60"></a><span class="dt">int</span></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="cb8-62"><a href="#cb8-62"></a> GtkApplication <span class="op">*</span>app<span class="op">;</span></span>
|
||||
<span id="cb8-63"><a href="#cb8-63"></a> <span class="dt">int</span> stat<span class="op">;</span></span>
|
||||
<span id="cb8-64"><a href="#cb8-64"></a></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">"com.github.ToshioCP.tfv4"</span><span class="op">,</span> G_APPLICATION_HANDLES_OPEN<span class="op">);</span></span>
|
||||
<span id="cb8-66"><a href="#cb8-66"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">"activate"</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="cb8-67"><a href="#cb8-67"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">"open"</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>
|
||||
<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 window’s 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>27-28 GtkNotebook is created and inserted to the GtkApplicationWindow as a child.</li>
|
||||
<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>
|
||||
<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>39-40 inserts the contents of the file into GtkTextBuffer and frees the memory pointed by <code>contents</code>.</li>
|
||||
<li>41-43: Gets the filename and creates GtkLabel with the filename and then frees <code>filename</code>.</li>
|
||||
<li>44-45: If <code>filename</code> is NULL, creates GtkLabel with the empty string.</li>
|
||||
<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>
|
||||
</ul>
|
||||
<pre><code> GtkNotebook -- GtkNotebookPage -- GtkScrolledWindow</code></pre>
|
||||
<ul>
|
||||
<li>47: Gets GtkNotebookPage widget and sets <code>nbp</code> to point to this GtkNotebookPage.</li>
|
||||
<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>
|
||||
<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>52-53: If <code>filename</code> is NULL, the “No valid file is given” message is outputted.</li>
|
||||
<li>55-58: If at least one file was read, then the number of GtkNotebookPage is greater than zero. If it’s true, it shows the window. If it’s false, it destroys the window, which causes the program to quit.</li>
|
||||
<li>24: The default size of the window is 600x400.</li>
|
||||
<li>26-27 GtkNotebook is created and inserted to the
|
||||
GtkApplicationWindow as a child.</li>
|
||||
<li>29-58 For-loop. The variable <code>files[i]</code> points i-th
|
||||
GFile, which is created by the GtkApplication from the i-th command line
|
||||
argument.</li>
|
||||
<li>31-36 GtkScrollledWindow, GtkTextView are created. GtkTextBuffer is
|
||||
got from the GtkTextView. The GtkTextView is connected to the
|
||||
GtkScrolledWindow as a child.</li>
|
||||
<li>38-39 inserts the contents of the file into GtkTextBuffer and frees
|
||||
the memory pointed by <code>contents</code>.</li>
|
||||
<li>40-42: If the filename is taken from the GFile, GtkLabel is created
|
||||
with the filename. The string <code>filename</code> is freed..</li>
|
||||
<li>43-44: If it fails to take the filename, empty string GtkLabel is
|
||||
created.</li>
|
||||
<li>45-46: Appends a GtkScrolledWindow to the GtkNotebook as a child.
|
||||
And the GtkLabel is set as the child’s 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>
|
||||
</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>
|
||||
|
|
520
docs/sec8.html
|
@ -111,62 +111,60 @@
|
|||
</div>
|
||||
</div>
|
||||
</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>
|
||||
<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
|
||||
in tfe1.c (text file editor 1).</p>
|
||||
<p>GtkTextView has a feature for editing multiple lines. Therefore, we
|
||||
don’t need to write the program from scratch, we just add two things to
|
||||
the file viewer:</p>
|
||||
<code>tfe1.c</code> (text file editor 1) under <code>tfe</code>
|
||||
directory.</p>
|
||||
<p>GtkTextView is a multi-line editor. So, we don’t need to write the
|
||||
editor from scratch. We just add two things to the file viewer:</p>
|
||||
<ul>
|
||||
<li>Memory to store a pointer to the GFile instance.</li>
|
||||
<li>A function to write the file.</li>
|
||||
<li>Pointers to GFile instances.</li>
|
||||
<li>A text-save function.</li>
|
||||
</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>
|
||||
<li>Use global variables; or</li>
|
||||
<li>Make a child object, which can extend the instance memory for the
|
||||
GFile object.</li>
|
||||
<li>Use global variables</li>
|
||||
<li>Make a child class of GtkTextView and its each instance holds a
|
||||
pointer to the GFile instance.</li>
|
||||
</ul>
|
||||
<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>
|
||||
<p>The variable <code>f[i]</code> corresponds to the file associated to
|
||||
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.</p>
|
||||
<p>Making a child object is a good idea in terms of maintenance. One
|
||||
thing you need to be careful of is the difference between “child object”
|
||||
and “child widget”. Here we are describing a “child object”. A child
|
||||
object includes, and expands on its parent object, as a child object
|
||||
derives everything from the parent object.</p>
|
||||
<p>The variable <code>f[i]</code> corresponds to the file associated
|
||||
with the i-th GtkNotebookPage.</p>
|
||||
<p>However, There are two problems. The first is the size of the array.
|
||||
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. The
|
||||
second is difficulty to maintain the program. 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.</p>
|
||||
<p>Making a child class is a good idea in terms of maintenance. And we
|
||||
prefer it rather than a global variable.</p>
|
||||
<p>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:</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>
|
||||
<img src="image/child.png" alt="Child object of GtkTextView" />
|
||||
<figcaption aria-hidden="true">Child object of GtkTextView</figcaption>
|
||||
</figure>
|
||||
<p>We will define TfeTextView as a child object 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.</p>
|
||||
<p>In general, this is how GObjects work. Understanding the general
|
||||
theory about Gobject’s 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>Let’s define the TfeTextView object, which is a child object of
|
||||
GtkTextView. First, look at the program below.</p>
|
||||
<h2 id="how-to-define-a-child-class-of-gtktextview">How to define a
|
||||
child class of GtkTextView</h2>
|
||||
<p>You need to know GObject system convention. 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>
|
||||
<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>
|
||||
|
@ -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-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>
|
||||
<p>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 <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>. 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.</p>
|
||||
<ul>
|
||||
<li>TfeTextView is divided into two parts. Tfe and TextView. Tfe is
|
||||
called the prefix, namespace or module. TextView is called the
|
||||
object.</li>
|
||||
called prefix or namespace. TextView is called object.</li>
|
||||
<li>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).</li>
|
||||
case), tfe_text_view (this is used for functions) and TFE_TEXT_VIEW
|
||||
(This is used to cast a object to TfeTextView).</li>
|
||||
<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
|
||||
case. And the replacement text is always (prefix)_(object)_get_type ()
|
||||
and the letters are lower case.</li>
|
||||
<li>Next, use 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).</li>
|
||||
and the letters are lower case. This definition is put before
|
||||
G_DECLARE_FINAL_TYPE macro.</li>
|
||||
<li>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.
|
||||
<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
|
||||
first member is the parent object. Notice this is not a pointer but the
|
||||
object itself. The second member and after are members of the child
|
||||
object. TfeTextView structure has a pointer to a GFile instance as a
|
||||
member.</li>
|
||||
<li>Use G_DEFINE_TYPE macro. The arguments are the child object name in
|
||||
first member is the parent object (C structure). Notice this is not a
|
||||
pointer but the object itself. The second member and after are members
|
||||
of the child object. TfeTextView structure has a pointer to a GFile
|
||||
instance as a member.</li>
|
||||
<li>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).</li>
|
||||
<li>Define instance init function (tfe_text_view_init). Usually you
|
||||
don’t need to do anything.</li>
|
||||
<li>Define class init function (tfe_text_view_class_init). You don’t
|
||||
need to do anything in this object.</li>
|
||||
<li>Write function codes you want to add (tfe_text_view_set_file and
|
||||
tfe_text_view_get_file). <code>tv</code> is a pointer to the TfeTextView
|
||||
object instance which is a C-structure declared with the tag
|
||||
_TfeTextView. So, the structure has a member <code>file</code> as a
|
||||
pointer to a GFile instance. <code>tv->file = f</code> is an
|
||||
assignment of <code>f</code> to a member <code>file</code> of the
|
||||
structure pointed by <code>tv</code>. This is an example how to use the
|
||||
extended memory in a child widget.</li>
|
||||
<li>Write a function to create an instance. Its name is
|
||||
(prefix)_(object)_new. If the parent object function needs parameters,
|
||||
this function also need them. You sometimes might want to add some
|
||||
parameters. It’s your choice. Use g_object_new function to create the
|
||||
instance. The arguments are (prefix)_TYPE_(object), a list to initialize
|
||||
properties and NULL. In this code no property needs to be initialized.
|
||||
(prefix)_TYPE_(module). This macro is mainly used to register the new
|
||||
class to the type system. Type system is a base system of GObject. Every
|
||||
class has its own type. The types of GObject, GtkWidget and TfeTextView
|
||||
are G_TYPE_OBJECT, GTK_TYPE_WIDGET and TFE_TYPE_TEXT_VIEW respectively.
|
||||
Such type (for example, TFE_TYPE_TEXT_VIEW) is a macro and it is
|
||||
expanded to a function (tfe_text_view_get_type()). It returns a integer
|
||||
which is unique among all GObject system classes.</li>
|
||||
<li>Instance init function (tfe_text_view_init) is called when the
|
||||
instance is created. It is the same as a constructor in other object
|
||||
oriented languages.</li>
|
||||
<li>Class init function (tfe_text_view_class_init) is called when the
|
||||
class is created.</li>
|
||||
<li>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. <code>tv</code> is a pointer to the TfeTextView object (C
|
||||
structure). It has a member <code>file</code> and it is pointed by
|
||||
<code>tv->file</code>.</li>
|
||||
<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>
|
||||
</ul>
|
||||
<p>This program is not perfect. It has some problems. It will be
|
||||
modified later.</p>
|
||||
<p>This program shows the outline how to define a child class.</p>
|
||||
<h2 id="close-request-signal">Close-request signal</h2>
|
||||
<p>Imagine that you are using this editor. First, you run the editor
|
||||
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
|
||||
text. After you finish editing, you exit the editor. The editor updates
|
||||
files just before the window closes.</p>
|
||||
<p>GtkWindow emits the “close-request” signal before it closes. We
|
||||
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
|
||||
call it a handler. The function <code>before_close</code> is invoked
|
||||
when the signal “close-request” is emitted.</p>
|
||||
<p>GtkWindow emits the “close-request” signal before it closes. We will
|
||||
connect the signal and the handler <code>before_close</code>. (A handler
|
||||
is a C function which is connected to a signal.) The function
|
||||
<code>before_close</code> is called when the signal “close-request” is
|
||||
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">"close-request"</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
|
||||
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>
|
||||
<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>
|
||||
<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-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-4"><a href="#cb4-4"></a> GtkWidget <span class="op">*</span>scr<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-6"><a href="#cb4-6"></a> GFile <span class="op">*</span>file<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-8"><a href="#cb4-8"></a> GtkTextBuffer <span class="op">*</span>tb<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-10"><a href="#cb4-10"></a> GtkTextIter end_iter<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-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-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-14"><a href="#cb4-14"></a></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-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"><</span> n<span class="op">;</span> <span class="op">++</span>i<span class="op">)</span> <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-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-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-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-21"><a href="#cb4-21"></a> gtk_text_buffer_get_bounds <span class="op">(</span>tb<span class="op">,</span> <span class="op">&</span>start_iter<span class="op">,</span> <span class="op">&</span>end_iter<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">&</span>start_iter<span class="op">,</span> <span class="op">&</span>end_iter<span class="op">,</span> FALSE<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-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-25"><a href="#cb4-25"></a> g_print <span class="op">(</span><span class="st">"ERROR : Can't save %s."</span><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-27"><a href="#cb4-27"></a> <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-29"><a href="#cb4-29"></a> <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-31"><a href="#cb4-31"></a><span class="op">}</span></span></code></pre></div>
|
||||
<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>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> 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> 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 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">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>
|
||||
<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> <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"><</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> 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> 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">&</span>start_iter<span class="op">,</span> <span class="op">&</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">&</span>start_iter<span class="op">,</span> <span class="op">&</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>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> g_printerr <span class="op">(</span><span class="st">"Can't save %s."</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> <span class="op">}</span> <span class="cf">else</span></span>
|
||||
<span id="cb4-27"><a href="#cb4-27"></a> g_printerr <span class="op">(</span><span class="st">"Pathname not exist.</span><span class="sc">\n</span><span class="st">"</span><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> g_free <span class="op">(</span>contents<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>
|
||||
<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
|
||||
code.</p>
|
||||
<ul>
|
||||
<li>15: Gets the number of pages <code>nb</code> has.</li>
|
||||
<li>16-29: For loop with regard to the index to each pages.</li>
|
||||
<li>17-19: Gets GtkScrolledWindow, TfeTextView and a pointer to GFile.
|
||||
The pointer was stored when <code>app_open</code> handler had run. It
|
||||
will be shown later.</li>
|
||||
<li>20-22: Gets GtkTextBuffer and contents. <code>start_iter</code> and
|
||||
<code>end_iter</code> are iterators of the buffer. I don’t want to
|
||||
explain them now because it would take a lot of time. Just remember
|
||||
these lines for the present.</li>
|
||||
<li>23-27: Writes the contents to the file. If it fails, it outputs an
|
||||
error message.</li>
|
||||
<li>28: Frees <code>contents</code>.</li>
|
||||
<li>14: The number of pages of <code>nb</code> is assigned to
|
||||
<code>n</code>.</li>
|
||||
<li>15-31: For loop with regard to the index to each pages.</li>
|
||||
<li>16-18: <code>scr</code>, <code>tv</code> and <code>file</code> is
|
||||
assigned pointers to the GtkScrolledWindow, TfeTextView and GFile. The
|
||||
GFile of TfeTextView was stored when <code>app_open</code> handler was
|
||||
called. It will be shown later.</li>
|
||||
<li>19-21: <code>tb</code> is assigned the GtkTextBuffer of the
|
||||
TfeTextView. The buffer is accessed with iterators. Iterators points
|
||||
somewhere in the buffer. The function
|
||||
<code>gtk_text_buffer_get_bounds</code> assigns the start and end of the
|
||||
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>
|
||||
<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
|
||||
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"><gtk/gtk.h></span></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-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>
|
||||
|
@ -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-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-18"><a href="#cb5-18"></a><span class="op">}</span></span>
|
||||
<span id="cb5-19"><a href="#cb5-19"></a></span>
|
||||
<span id="cb5-20"><a href="#cb5-20"></a><span class="dt">static</span> <span class="dt">void</span></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-22"><a href="#cb5-22"></a><span class="op">}</span></span>
|
||||
<span id="cb5-23"><a href="#cb5-23"></a></span>
|
||||
<span id="cb5-24"><a href="#cb5-24"></a><span class="dt">void</span></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-26"><a href="#cb5-26"></a> tv <span class="op">-></span> file <span class="op">=</span> f<span class="op">;</span></span>
|
||||
<span id="cb5-27"><a href="#cb5-27"></a><span class="op">}</span></span>
|
||||
<span id="cb5-28"><a href="#cb5-28"></a></span>
|
||||
<span id="cb5-29"><a href="#cb5-29"></a>GFile <span class="op">*</span></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-31"><a href="#cb5-31"></a> <span class="cf">return</span> tv <span class="op">-></span> file<span class="op">;</span></span>
|
||||
<span id="cb5-32"><a href="#cb5-32"></a><span class="op">}</span></span>
|
||||
<span id="cb5-33"><a href="#cb5-33"></a></span>
|
||||
<span id="cb5-34"><a href="#cb5-34"></a>GtkWidget <span class="op">*</span></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-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-37"><a href="#cb5-37"></a><span class="op">}</span></span>
|
||||
<span id="cb5-38"><a href="#cb5-38"></a></span>
|
||||
<span id="cb5-39"><a href="#cb5-39"></a><span class="co">/* ---------- end of the definition of TfeTextView ---------- */</span></span>
|
||||
<span id="cb5-40"><a href="#cb5-40"></a></span>
|
||||
<span id="cb5-41"><a href="#cb5-41"></a><span class="dt">static</span> gboolean</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-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-18"><a href="#cb5-18"></a> tv<span class="op">-></span>file <span class="op">=</span> NULL<span class="op">;</span></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>
|
||||
<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>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 class="op">}</span></span>
|
||||
<span id="cb5-24"><a href="#cb5-24"></a></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>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> tv<span class="op">-></span>file <span class="op">=</span> f<span class="op">;</span></span>
|
||||
<span id="cb5-28"><a href="#cb5-28"></a><span class="op">}</span></span>
|
||||
<span id="cb5-29"><a href="#cb5-29"></a></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>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="cf">return</span> tv <span class="op">-></span> file<span class="op">;</span></span>
|
||||
<span id="cb5-33"><a href="#cb5-33"></a><span class="op">}</span></span>
|
||||
<span id="cb5-34"><a href="#cb5-34"></a></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>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="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 class="op">}</span></span>
|
||||
<span id="cb5-39"><a href="#cb5-39"></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>
|
||||
<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>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-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>
|
||||
|
@ -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">&</span>start_iter<span class="op">,</span> <span class="op">&</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">&</span>start_iter<span class="op">,</span> <span class="op">&</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-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-65"><a href="#cb5-65"></a> g_print <span class="op">(</span><span class="st">"ERROR : Can't save %s."</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-67"><a href="#cb5-67"></a> <span class="op">}</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-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-71"><a href="#cb5-71"></a><span class="op">}</span></span>
|
||||
<span id="cb5-72"><a href="#cb5-72"></a></span>
|
||||
<span id="cb5-73"><a href="#cb5-73"></a><span class="dt">static</span> <span class="dt">void</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-75"><a href="#cb5-75"></a> g_print <span class="op">(</span><span class="st">"You need to give filenames as arguments.</span><span class="sc">\n</span><span class="st">"</span><span class="op">);</span></span>
|
||||
<span id="cb5-76"><a href="#cb5-76"></a><span class="op">}</span></span>
|
||||
<span id="cb5-77"><a href="#cb5-77"></a></span>
|
||||
<span id="cb5-78"><a href="#cb5-78"></a><span class="dt">static</span> <span class="dt">void</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-80"><a href="#cb5-80"></a> GtkWidget <span class="op">*</span>win<span class="op">;</span></span>
|
||||
<span id="cb5-81"><a href="#cb5-81"></a> GtkWidget <span class="op">*</span>nb<span class="op">;</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-83"><a href="#cb5-83"></a> GtkNotebookPage <span class="op">*</span>nbp<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-85"><a href="#cb5-85"></a> GtkWidget <span class="op">*</span>tv<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-87"><a href="#cb5-87"></a> <span class="dt">char</span> <span class="op">*</span>contents<span class="op">;</span></span>
|
||||
<span id="cb5-88"><a href="#cb5-88"></a> gsize length<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-90"><a href="#cb5-90"></a> <span class="dt">int</span> i<span class="op">;</span></span>
|
||||
<span id="cb5-91"><a href="#cb5-91"></a></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-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">"file editor"</span><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-95"><a href="#cb5-95"></a></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-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-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_printerr <span class="op">(</span><span class="st">"Can't save %s."</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-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_printerr <span class="op">(</span><span class="st">"Pathname not exist.</span><span class="sc">\n</span><span class="st">"</span><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> g_free <span class="op">(</span>contents<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 class="op">}</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><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="dt">static</span> <span class="dt">void</span></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> g_print <span class="op">(</span><span class="st">"You need to give filenames as arguments.</span><span class="sc">\n</span><span class="st">"</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></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>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> 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>nb<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> GtkNotebookPage <span class="op">*</span>nbp<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> GtkWidget <span class="op">*</span>tv<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">char</span> <span class="op">*</span>contents<span class="op">;</span></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> <span class="dt">char</span> <span class="op">*</span>filename<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></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> gtk_window_set_title <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="st">"file editor"</span><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-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"><</span> n_files<span class="op">;</span> i<span class="op">++)</span> <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">&</span>contents<span class="op">,</span> <span class="op">&</span>length<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">))</span> <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-102"><a href="#cb5-102"></a> tv <span class="op">=</span> tfe_text_view_new <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-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-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-106"><a href="#cb5-106"></a></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-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-109"><a href="#cb5-109"></a> g_free <span class="op">(</span>contents<span class="op">);</span></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-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-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-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-114"><a href="#cb5-114"></a> g_object_set <span class="op">(</span>nbp<span class="op">,</span> <span class="st">"tab-expand"</span><span class="op">,</span> TRUE<span class="op">,</span> NULL<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-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-117"><a href="#cb5-117"></a> g_print <span class="op">(</span><span class="st">"No such file: %s.</span><span class="sc">\n</span><span class="st">"</span><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-120"><a href="#cb5-120"></a> g_print <span class="op">(</span><span class="st">"No valid file is given</span><span class="sc">\n</span><span class="st">"</span><span class="op">);</span></span>
|
||||
<span id="cb5-121"><a href="#cb5-121"></a> <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">></span> <span class="dv">0</span><span class="op">)</span> <span class="op">{</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">"close-request"</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-124"><a href="#cb5-124"></a> gtk_widget_show <span class="op">(</span>win<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-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-127"><a href="#cb5-127"></a><span class="op">}</span></span>
|
||||
<span id="cb5-128"><a href="#cb5-128"></a></span>
|
||||
<span id="cb5-129"><a href="#cb5-129"></a><span class="dt">int</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-131"><a href="#cb5-131"></a> GtkApplication <span class="op">*</span>app<span class="op">;</span></span>
|
||||
<span id="cb5-132"><a href="#cb5-132"></a> <span class="dt">int</span> stat<span class="op">;</span></span>
|
||||
<span id="cb5-133"><a href="#cb5-133"></a></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">"com.github.ToshioCP.tfe1"</span><span class="op">,</span> G_APPLICATION_HANDLES_OPEN<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">"activate"</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-136"><a href="#cb5-136"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">"open"</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-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-138"><a href="#cb5-138"></a> g_object_unref <span class="op">(</span>app<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-140"><a href="#cb5-140"></a><span class="op">}</span></span></code></pre></div>
|
||||
<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> 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></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"><</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> <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">&</span>contents<span class="op">,</span> <span class="op">&</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> scr <span class="op">=</span> gtk_scrolled_window_new <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> 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> 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_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></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> 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> g_free <span class="op">(</span>contents<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> 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> 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> 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_object_set <span class="op">(</span>nbp<span class="op">,</span> <span class="st">"tab-expand"</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-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">"No such file: %s.</span><span class="sc">\n</span><span class="st">"</span><span class="op">,</span> filename<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="op">}</span> <span class="cf">else</span></span>
|
||||
<span id="cb5-123"><a href="#cb5-123"></a> g_print <span class="op">(</span><span class="st">"No valid file is given</span><span class="sc">\n</span><span class="st">"</span><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="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">></span> <span class="dv">0</span><span class="op">)</span> <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">"close-request"</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> gtk_widget_show <span class="op">(</span>win<span class="op">);</span></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> 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><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></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> GtkApplication <span class="op">*</span>app<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></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">"com.github.ToshioCP.tfe1"</span><span class="op">,</span> G_APPLICATION_HANDLES_OPEN<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">"activate"</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> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">"open"</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> 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>
|
||||
<li>107: Sets the pointer to GFile into TfeTextView.
|
||||
<code>files[i]</code> is a pointer to GFile structure. It will be freed
|
||||
by the system. So you need to copy it. <code>g_file_dup</code>
|
||||
duplicates the given GFile structure.</li>
|
||||
<li>123: Connects “close-request” signal and <code>before_close</code>
|
||||
handler. The fourth argument is called user data and it is given to the
|
||||
signal handler. So, <code>nb</code> is given to
|
||||
<code>before_close</code> as the second argument.</li>
|
||||
<li>110: The GFile pointer of the TfeTextView is set with
|
||||
<code>files[i]</code>, which is a GFile created with the command line
|
||||
argument. But the GFile will be destroyed by the system later. So it
|
||||
needs to be copied before the assignment. <code>g_file_dup</code>
|
||||
duplicates the GFile.</li>
|
||||
<li>126: The “close-request” signal is connected to
|
||||
<code>before_close</code> handler. The fourth argument is called “user
|
||||
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>
|
||||
<p>Now compile and run it. There’s a sample file in the directory
|
||||
<code>tfe</code>. Type <code>./a.out taketori.txt</code>. Modify the
|
||||
contents and close the window. Make sure that the file is modified.</p>
|
||||
<p>Now it’s time to compile and run.</p>
|
||||
<pre><code>$ cd src/tfe
|
||||
$ 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. It’s not smart. We need more
|
||||
features like open, save, saveas, change font and so on. We will add
|
||||
them in the next section and after.</p>
|
||||
|
|
603
docs/sec9.html
|
@ -5,7 +5,7 @@
|
|||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
||||
<title>Gtk4 tutorial</title>
|
||||
<title>GTK 4 tutorial</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
|
@ -111,194 +111,251 @@
|
|||
</div>
|
||||
</div>
|
||||
</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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<span id="cb1-2"><a href="#cb1-2"></a>app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer user_data) {</span>
|
||||
<span id="cb1-3"><a href="#cb1-3"></a> GtkWidget *win;</span>
|
||||
<span id="cb1-4"><a href="#cb1-4"></a> GtkWidget *nb;</span>
|
||||
<span id="cb1-5"><a href="#cb1-5"></a> GtkWidget *lab;</span>
|
||||
<span id="cb1-6"><a href="#cb1-6"></a> GtkNotebookPage *nbp;</span>
|
||||
<span id="cb1-7"><a href="#cb1-7"></a> GtkWidget *scr;</span>
|
||||
<span id="cb1-8"><a href="#cb1-8"></a> GtkWidget *tv;</span>
|
||||
<span id="cb1-9"><a href="#cb1-9"></a> GtkTextBuffer *tb;</span>
|
||||
<span id="cb1-10"><a href="#cb1-10"></a> <span class="dt">char</span> *contents;</span>
|
||||
<span id="cb1-11"><a href="#cb1-11"></a> gsize length;</span>
|
||||
<span id="cb1-12"><a href="#cb1-12"></a> <span class="dt">char</span> *filename;</span>
|
||||
<span id="cb1-13"><a href="#cb1-13"></a> <span class="dt">int</span> i;</span>
|
||||
<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>
|
||||
<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>
|
||||
<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-3"><a href="#cb1-3"></a> GtkWidget <span class="op">*</span>win<span class="op">;</span></span>
|
||||
<span id="cb1-4"><a href="#cb1-4"></a> GtkWidget <span class="op">*</span>nb<span class="op">;</span></span>
|
||||
<span id="cb1-5"><a href="#cb1-5"></a> GtkWidget <span class="op">*</span>lab<span class="op">;</span></span>
|
||||
<span id="cb1-6"><a href="#cb1-6"></a> GtkNotebookPage <span class="op">*</span>nbp<span class="op">;</span></span>
|
||||
<span id="cb1-7"><a href="#cb1-7"></a> GtkWidget <span class="op">*</span>scr<span class="op">;</span></span>
|
||||
<span id="cb1-8"><a href="#cb1-8"></a> GtkWidget <span class="op">*</span>tv<span class="op">;</span></span>
|
||||
<span id="cb1-9"><a href="#cb1-9"></a> GtkTextBuffer <span class="op">*</span>tb<span class="op">;</span></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-15"><a href="#cb1-15"></a> GtkWidget *boxv;</span>
|
||||
<span id="cb1-16"><a href="#cb1-16"></a> GtkWidget *boxh;</span>
|
||||
<span id="cb1-17"><a href="#cb1-17"></a> GtkWidget *dmy1;</span>
|
||||
<span id="cb1-18"><a href="#cb1-18"></a> GtkWidget *dmy2;</span>
|
||||
<span id="cb1-19"><a href="#cb1-19"></a> GtkWidget *dmy3;</span>
|
||||
<span id="cb1-20"><a href="#cb1-20"></a> GtkWidget *btnn; <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-22"><a href="#cb1-22"></a> GtkWidget *btns; <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-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 <span class="op">*</span>boxh<span class="op">;</span></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 <span class="op">*</span>dmy2<span class="op">;</span></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 <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 <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 <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 <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-25"><a href="#cb1-25"></a> win = gtk_application_window_new (GTK_APPLICATION (app));</span>
|
||||
<span id="cb1-26"><a href="#cb1-26"></a> gtk_window_set_title (GTK_WINDOW (win), <span class="st">"file editor"</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-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 <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="st">"file editor"</span><span class="op">);</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-29"><a href="#cb1-29"></a> boxv = gtk_box_new (GTK_ORIENTATION_VERTICAL, <span class="dv">0</span>);</span>
|
||||
<span id="cb1-30"><a href="#cb1-30"></a> gtk_window_set_child (GTK_WINDOW (win), boxv);</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 <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-32"><a href="#cb1-32"></a> boxh = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, <span class="dv">0</span>);</span>
|
||||
<span id="cb1-33"><a href="#cb1-33"></a> gtk_box_append (GTK_BOX (boxv), boxh);</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 <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-35"><a href="#cb1-35"></a> dmy1 = gtk_label_new(NULL); <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-37"><a href="#cb1-37"></a> dmy2 = gtk_label_new(NULL); <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-39"><a href="#cb1-39"></a> dmy3 = gtk_label_new(NULL); <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-41"><a href="#cb1-41"></a> btnn = gtk_button_new_with_label (<span class="st">"New"</span>);</span>
|
||||
<span id="cb1-42"><a href="#cb1-42"></a> btno = gtk_button_new_with_label (<span class="st">"Open"</span>);</span>
|
||||
<span id="cb1-43"><a href="#cb1-43"></a> btns = gtk_button_new_with_label (<span class="st">"Save"</span>);</span>
|
||||
<span id="cb1-44"><a href="#cb1-44"></a> btnc = gtk_button_new_with_label (<span class="st">"Close"</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 <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 <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 <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 <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 <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 <span class="op">=</span> gtk_button_new_with_label <span class="op">(</span><span class="st">"New"</span><span class="op">);</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">"Open"</span><span class="op">);</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">"Save"</span><span class="op">);</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">"Close"</span><span class="op">);</span></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-47"><a href="#cb1-47"></a> gtk_box_append (GTK_BOX (boxh), btnn);</span>
|
||||
<span id="cb1-48"><a href="#cb1-48"></a> gtk_box_append (GTK_BOX (boxh), btno);</span>
|
||||
<span id="cb1-49"><a href="#cb1-49"></a> gtk_box_append (GTK_BOX (boxh), dmy2);</span>
|
||||
<span id="cb1-50"><a href="#cb1-50"></a> gtk_box_append (GTK_BOX (boxh), btns);</span>
|
||||
<span id="cb1-51"><a href="#cb1-51"></a> gtk_box_append (GTK_BOX (boxh), btnc);</span>
|
||||
<span id="cb1-52"><a href="#cb1-52"></a> gtk_box_append (GTK_BOX (boxh), dmy3);</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 <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 <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 <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 <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 <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 <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-54"><a href="#cb1-54"></a> nb = gtk_notebook_new ();</span>
|
||||
<span id="cb1-55"><a href="#cb1-55"></a> gtk_widget_set_hexpand (nb, TRUE);</span>
|
||||
<span id="cb1-56"><a href="#cb1-56"></a> gtk_widget_set_vexpand (nb, TRUE);</span>
|
||||
<span id="cb1-57"><a href="#cb1-57"></a> gtk_box_append (GTK_BOX (boxv), nb);</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 <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 <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 <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-59"><a href="#cb1-59"></a> <span class="cf">for</span> (i = <span class="dv">0</span>; i < n_files; i++) {</span>
|
||||
<span id="cb1-60"><a href="#cb1-60"></a> <span class="cf">if</span> (g_file_load_contents (files[i], NULL, &contents, &length, NULL, NULL)) {</span>
|
||||
<span id="cb1-61"><a href="#cb1-61"></a> scr = gtk_scrolled_window_new ();</span>
|
||||
<span id="cb1-62"><a href="#cb1-62"></a> tv = tfe_text_view_new ();</span>
|
||||
<span id="cb1-63"><a href="#cb1-63"></a> tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));</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-65"><a href="#cb1-65"></a> gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);</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"><</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> <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">&</span>contents<span class="op">,</span> <span class="op">&</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 <span class="op">=</span> gtk_scrolled_window_new <span class="op">();</span></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 <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 <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 <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-67"><a href="#cb1-67"></a> tfe_text_view_set_file (TFE_TEXT_VIEW (tv), g_file_dup (files[i]));</span>
|
||||
<span id="cb1-68"><a href="#cb1-68"></a> gtk_text_buffer_set_text (tb, contents, length);</span>
|
||||
<span id="cb1-69"><a href="#cb1-69"></a> g_free (contents);</span>
|
||||
<span id="cb1-70"><a href="#cb1-70"></a> filename = g_file_get_basename (files[i]);</span>
|
||||
<span id="cb1-71"><a href="#cb1-71"></a> lab = gtk_label_new (filename);</span>
|
||||
<span id="cb1-72"><a href="#cb1-72"></a> gtk_notebook_append_page (GTK_NOTEBOOK (nb), scr, lab);</span>
|
||||
<span id="cb1-73"><a href="#cb1-73"></a> nbp = gtk_notebook_get_page (GTK_NOTEBOOK (nb), scr);</span>
|
||||
<span id="cb1-74"><a href="#cb1-74"></a> g_object_set (nbp, <span class="st">"tab-expand"</span>, TRUE, NULL);</span>
|
||||
<span id="cb1-75"><a href="#cb1-75"></a> g_free (filename);</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-77"><a href="#cb1-77"></a> g_print (<span class="st">"No such file: %s.</span><span class="sc">\n</span><span class="st">"</span>, filename);</span>
|
||||
<span id="cb1-78"><a href="#cb1-78"></a> g_free (filename);</span>
|
||||
<span id="cb1-79"><a href="#cb1-79"></a> } <span class="cf">else</span></span>
|
||||
<span id="cb1-80"><a href="#cb1-80"></a> g_print (<span class="st">"No valid file is given</span><span class="sc">\n</span><span class="st">"</span>);</span>
|
||||
<span id="cb1-81"><a href="#cb1-81"></a> }</span>
|
||||
<span id="cb1-82"><a href="#cb1-82"></a> <span class="cf">if</span> (gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb)) > <span class="dv">0</span>) {</span>
|
||||
<span id="cb1-83"><a href="#cb1-83"></a> gtk_widget_show (win);</span>
|
||||
<span id="cb1-84"><a href="#cb1-84"></a> } <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-86"><a href="#cb1-86"></a>}</span></code></pre></div>
|
||||
<p>The aim is to build the widgets of the main application window.</p>
|
||||
<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 <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 <span class="op">(</span>contents<span class="op">);</span></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 <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 <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 <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 <span class="op">(</span>nbp<span class="op">,</span> <span class="st">"tab-expand"</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 <span class="op">(</span>filename<span class="op">);</span></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="op">(</span><span class="st">"No such file: %s.</span><span class="sc">\n</span><span class="st">"</span><span class="op">,</span> filename<span class="op">);</span></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="op">}</span> <span class="cf">else</span></span>
|
||||
<span id="cb1-80"><a href="#cb1-80"></a> g_print <span class="op">(</span><span class="st">"No valid file is given</span><span class="sc">\n</span><span class="st">"</span><span class="op">);</span></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> <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">></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 <span class="op">(</span>win<span class="op">);</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 <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 class="op">}</span></span></code></pre></div>
|
||||
<p>The function <code>app_open</code> builds the widgets in the main
|
||||
application window.</p>
|
||||
<ul>
|
||||
<li>25-27: Creates a GtkApplicationWindow instance and sets the title and default size.</li>
|
||||
<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>
|
||||
<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>25-27: Creates a GtkApplicationWindow instance and sets the title
|
||||
and default size.</li>
|
||||
<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>
|
||||
<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>46-52: Appends these GtkLabel and GtkButton to <code>boxh</code>.</li>
|
||||
<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>
|
||||
<li>46-52: Appends these GtkLabel and GtkButton to
|
||||
<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>
|
||||
<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 weren’t necessary, except 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>
|
||||
<p>The number of widget-build lines is 33(=57-25+1). We also needed many
|
||||
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>
|
||||
<p>First, let’s look at the UI file <code>tfe3.ui</code> that is used to define the widget structure.</p>
|
||||
<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"><?xml</span> version="1.0" encoding="UTF-8"<span class="kw">?></span></span>
|
||||
<span id="cb2-2"><a href="#cb2-2"></a><span class="kw"><interface></span></span>
|
||||
<span id="cb2-3"><a href="#cb2-3"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkApplicationWindow"</span><span class="ot"> id=</span><span class="st">"win"</span><span class="kw">></span></span>
|
||||
<span id="cb2-4"><a href="#cb2-4"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"title"</span><span class="kw">></span>file editor<span class="kw"></property></span></span>
|
||||
<span id="cb2-5"><a href="#cb2-5"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"default-width"</span><span class="kw">></span>600<span class="kw"></property></span></span>
|
||||
<span id="cb2-6"><a href="#cb2-6"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"default-height"</span><span class="kw">></span>400<span class="kw"></property></span></span>
|
||||
<span id="cb2-7"><a href="#cb2-7"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb2-8"><a href="#cb2-8"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkBox"</span><span class="ot"> id=</span><span class="st">"boxv"</span><span class="kw">></span></span>
|
||||
<span id="cb2-9"><a href="#cb2-9"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"orientation"</span><span class="kw">></span>GTK_ORIENTATION_VERTICAL<span class="kw"></property></span></span>
|
||||
<span id="cb2-10"><a href="#cb2-10"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb2-11"><a href="#cb2-11"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkBox"</span><span class="ot"> id=</span><span class="st">"boxh"</span><span class="kw">></span></span>
|
||||
<span id="cb2-12"><a href="#cb2-12"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"orientation"</span><span class="kw">></span>GTK_ORIENTATION_HORIZONTAL<span class="kw"></property></span></span>
|
||||
<span id="cb2-13"><a href="#cb2-13"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb2-14"><a href="#cb2-14"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkLabel"</span><span class="ot"> id=</span><span class="st">"dmy1"</span><span class="kw">></span></span>
|
||||
<span id="cb2-15"><a href="#cb2-15"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"width-chars"</span><span class="kw">></span>10<span class="kw"></property></span></span>
|
||||
<span id="cb2-16"><a href="#cb2-16"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb2-17"><a href="#cb2-17"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb2-18"><a href="#cb2-18"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb2-19"><a href="#cb2-19"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkButton"</span><span class="ot"> id=</span><span class="st">"btnn"</span><span class="kw">></span></span>
|
||||
<span id="cb2-20"><a href="#cb2-20"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span>New<span class="kw"></property></span></span>
|
||||
<span id="cb2-21"><a href="#cb2-21"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb2-22"><a href="#cb2-22"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb2-23"><a href="#cb2-23"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb2-24"><a href="#cb2-24"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkButton"</span><span class="ot"> id=</span><span class="st">"btno"</span><span class="kw">></span></span>
|
||||
<span id="cb2-25"><a href="#cb2-25"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span>Open<span class="kw"></property></span></span>
|
||||
<span id="cb2-26"><a href="#cb2-26"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb2-27"><a href="#cb2-27"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb2-28"><a href="#cb2-28"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb2-29"><a href="#cb2-29"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkLabel"</span><span class="ot"> id=</span><span class="st">"dmy2"</span><span class="kw">></span></span>
|
||||
<span id="cb2-30"><a href="#cb2-30"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"hexpand"</span><span class="kw">></span>TRUE<span class="kw"></property></span></span>
|
||||
<span id="cb2-31"><a href="#cb2-31"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb2-32"><a href="#cb2-32"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb2-33"><a href="#cb2-33"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb2-34"><a href="#cb2-34"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkButton"</span><span class="ot"> id=</span><span class="st">"btns"</span><span class="kw">></span></span>
|
||||
<span id="cb2-35"><a href="#cb2-35"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span>Save<span class="kw"></property></span></span>
|
||||
<span id="cb2-36"><a href="#cb2-36"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb2-37"><a href="#cb2-37"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb2-38"><a href="#cb2-38"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb2-39"><a href="#cb2-39"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkButton"</span><span class="ot"> id=</span><span class="st">"btnc"</span><span class="kw">></span></span>
|
||||
<span id="cb2-40"><a href="#cb2-40"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span>Close<span class="kw"></property></span></span>
|
||||
<span id="cb2-41"><a href="#cb2-41"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb2-42"><a href="#cb2-42"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb2-43"><a href="#cb2-43"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb2-44"><a href="#cb2-44"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkLabel"</span><span class="ot"> id=</span><span class="st">"dmy3"</span><span class="kw">></span></span>
|
||||
<span id="cb2-45"><a href="#cb2-45"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"width-chars"</span><span class="kw">></span>10<span class="kw"></property></span></span>
|
||||
<span id="cb2-46"><a href="#cb2-46"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb2-47"><a href="#cb2-47"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb2-48"><a href="#cb2-48"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb2-49"><a href="#cb2-49"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb2-50"><a href="#cb2-50"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb2-51"><a href="#cb2-51"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkNotebook"</span><span class="ot"> id=</span><span class="st">"nb"</span><span class="kw">></span></span>
|
||||
<span id="cb2-52"><a href="#cb2-52"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"hexpand"</span><span class="kw">></span>TRUE<span class="kw"></property></span></span>
|
||||
<span id="cb2-53"><a href="#cb2-53"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"vexpand"</span><span class="kw">></span>TRUE<span class="kw"></property></span></span>
|
||||
<span id="cb2-54"><a href="#cb2-54"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb2-55"><a href="#cb2-55"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb2-56"><a href="#cb2-56"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb2-57"><a href="#cb2-57"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb2-58"><a href="#cb2-58"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb2-59"><a href="#cb2-59"></a><span class="kw"></interface></span></span></code></pre></div>
|
||||
<p>The structure of this file is XML. Constructs that begin with <code><</code> and end with <code>></code> are called tags. There are two types of tags, the start tag and the end tag. For example, <code><interface></code> is a start tag and <code></interface></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>
|
||||
<p>Look at the UI file <code>tfe3.ui</code> that defines widget
|
||||
structure.</p>
|
||||
<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="fu"><?xml</span><span class="ot"> version=</span><span class="st">"1.0"</span><span class="ot"> encoding=</span><span class="st">"UTF-8"</span><span class="fu">?></span></span>
|
||||
<span id="cb2-2"><a href="#cb2-2"></a><<span class="kw">interface</span>></span>
|
||||
<span id="cb2-3"><a href="#cb2-3"></a> <<span class="kw">object</span><span class="ot"> class=</span><span class="st">"GtkApplicationWindow"</span><span class="ot"> id=</span><span class="st">"win"</span>></span>
|
||||
<span id="cb2-4"><a href="#cb2-4"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"title"</span>>file editor</<span class="kw">property</span>></span>
|
||||
<span id="cb2-5"><a href="#cb2-5"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"default-width"</span>>600</<span class="kw">property</span>></span>
|
||||
<span id="cb2-6"><a href="#cb2-6"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"default-height"</span>>400</<span class="kw">property</span>></span>
|
||||
<span id="cb2-7"><a href="#cb2-7"></a> <<span class="kw">child</span>></span>
|
||||
<span id="cb2-8"><a href="#cb2-8"></a> <<span class="kw">object</span><span class="ot"> class=</span><span class="st">"GtkBox"</span><span class="ot"> id=</span><span class="st">"boxv"</span>></span>
|
||||
<span id="cb2-9"><a href="#cb2-9"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"orientation"</span>>GTK_ORIENTATION_VERTICAL</<span class="kw">property</span>></span>
|
||||
<span id="cb2-10"><a href="#cb2-10"></a> <<span class="kw">child</span>></span>
|
||||
<span id="cb2-11"><a href="#cb2-11"></a> <<span class="kw">object</span><span class="ot"> class=</span><span class="st">"GtkBox"</span><span class="ot"> id=</span><span class="st">"boxh"</span>></span>
|
||||
<span id="cb2-12"><a href="#cb2-12"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"orientation"</span>>GTK_ORIENTATION_HORIZONTAL</<span class="kw">property</span>></span>
|
||||
<span id="cb2-13"><a href="#cb2-13"></a> <<span class="kw">child</span>></span>
|
||||
<span id="cb2-14"><a href="#cb2-14"></a> <<span class="kw">object</span><span class="ot"> class=</span><span class="st">"GtkLabel"</span><span class="ot"> id=</span><span class="st">"dmy1"</span>></span>
|
||||
<span id="cb2-15"><a href="#cb2-15"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"width-chars"</span>>10</<span class="kw">property</span>></span>
|
||||
<span id="cb2-16"><a href="#cb2-16"></a> </<span class="kw">object</span>></span>
|
||||
<span id="cb2-17"><a href="#cb2-17"></a> </<span class="kw">child</span>></span>
|
||||
<span id="cb2-18"><a href="#cb2-18"></a> <<span class="kw">child</span>></span>
|
||||
<span id="cb2-19"><a href="#cb2-19"></a> <<span class="kw">object</span><span class="ot"> class=</span><span class="st">"GtkButton"</span><span class="ot"> id=</span><span class="st">"btnn"</span>></span>
|
||||
<span id="cb2-20"><a href="#cb2-20"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"label"</span>>New</<span class="kw">property</span>></span>
|
||||
<span id="cb2-21"><a href="#cb2-21"></a> </<span class="kw">object</span>></span>
|
||||
<span id="cb2-22"><a href="#cb2-22"></a> </<span class="kw">child</span>></span>
|
||||
<span id="cb2-23"><a href="#cb2-23"></a> <<span class="kw">child</span>></span>
|
||||
<span id="cb2-24"><a href="#cb2-24"></a> <<span class="kw">object</span><span class="ot"> class=</span><span class="st">"GtkButton"</span><span class="ot"> id=</span><span class="st">"btno"</span>></span>
|
||||
<span id="cb2-25"><a href="#cb2-25"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"label"</span>>Open</<span class="kw">property</span>></span>
|
||||
<span id="cb2-26"><a href="#cb2-26"></a> </<span class="kw">object</span>></span>
|
||||
<span id="cb2-27"><a href="#cb2-27"></a> </<span class="kw">child</span>></span>
|
||||
<span id="cb2-28"><a href="#cb2-28"></a> <<span class="kw">child</span>></span>
|
||||
<span id="cb2-29"><a href="#cb2-29"></a> <<span class="kw">object</span><span class="ot"> class=</span><span class="st">"GtkLabel"</span><span class="ot"> id=</span><span class="st">"dmy2"</span>></span>
|
||||
<span id="cb2-30"><a href="#cb2-30"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"hexpand"</span>>TRUE</<span class="kw">property</span>></span>
|
||||
<span id="cb2-31"><a href="#cb2-31"></a> </<span class="kw">object</span>></span>
|
||||
<span id="cb2-32"><a href="#cb2-32"></a> </<span class="kw">child</span>></span>
|
||||
<span id="cb2-33"><a href="#cb2-33"></a> <<span class="kw">child</span>></span>
|
||||
<span id="cb2-34"><a href="#cb2-34"></a> <<span class="kw">object</span><span class="ot"> class=</span><span class="st">"GtkButton"</span><span class="ot"> id=</span><span class="st">"btns"</span>></span>
|
||||
<span id="cb2-35"><a href="#cb2-35"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"label"</span>>Save</<span class="kw">property</span>></span>
|
||||
<span id="cb2-36"><a href="#cb2-36"></a> </<span class="kw">object</span>></span>
|
||||
<span id="cb2-37"><a href="#cb2-37"></a> </<span class="kw">child</span>></span>
|
||||
<span id="cb2-38"><a href="#cb2-38"></a> <<span class="kw">child</span>></span>
|
||||
<span id="cb2-39"><a href="#cb2-39"></a> <<span class="kw">object</span><span class="ot"> class=</span><span class="st">"GtkButton"</span><span class="ot"> id=</span><span class="st">"btnc"</span>></span>
|
||||
<span id="cb2-40"><a href="#cb2-40"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"label"</span>>Close</<span class="kw">property</span>></span>
|
||||
<span id="cb2-41"><a href="#cb2-41"></a> </<span class="kw">object</span>></span>
|
||||
<span id="cb2-42"><a href="#cb2-42"></a> </<span class="kw">child</span>></span>
|
||||
<span id="cb2-43"><a href="#cb2-43"></a> <<span class="kw">child</span>></span>
|
||||
<span id="cb2-44"><a href="#cb2-44"></a> <<span class="kw">object</span><span class="ot"> class=</span><span class="st">"GtkLabel"</span><span class="ot"> id=</span><span class="st">"dmy3"</span>></span>
|
||||
<span id="cb2-45"><a href="#cb2-45"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"width-chars"</span>>10</<span class="kw">property</span>></span>
|
||||
<span id="cb2-46"><a href="#cb2-46"></a> </<span class="kw">object</span>></span>
|
||||
<span id="cb2-47"><a href="#cb2-47"></a> </<span class="kw">child</span>></span>
|
||||
<span id="cb2-48"><a href="#cb2-48"></a> </<span class="kw">object</span>></span>
|
||||
<span id="cb2-49"><a href="#cb2-49"></a> </<span class="kw">child</span>></span>
|
||||
<span id="cb2-50"><a href="#cb2-50"></a> <<span class="kw">child</span>></span>
|
||||
<span id="cb2-51"><a href="#cb2-51"></a> <<span class="kw">object</span><span class="ot"> class=</span><span class="st">"GtkNotebook"</span><span class="ot"> id=</span><span class="st">"nb"</span>></span>
|
||||
<span id="cb2-52"><a href="#cb2-52"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"hexpand"</span>>TRUE</<span class="kw">property</span>></span>
|
||||
<span id="cb2-53"><a href="#cb2-53"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"vexpand"</span>>TRUE</<span class="kw">property</span>></span>
|
||||
<span id="cb2-54"><a href="#cb2-54"></a> </<span class="kw">object</span>></span>
|
||||
<span id="cb2-55"><a href="#cb2-55"></a> </<span class="kw">child</span>></span>
|
||||
<span id="cb2-56"><a href="#cb2-56"></a> </<span class="kw">object</span>></span>
|
||||
<span id="cb2-57"><a href="#cb2-57"></a> </<span class="kw">child</span>></span>
|
||||
<span id="cb2-58"><a href="#cb2-58"></a> </<span class="kw">object</span>></span>
|
||||
<span id="cb2-59"><a href="#cb2-59"></a></<span class="kw">interface</span>></span></code></pre></div>
|
||||
<p>The is a XML file. Tags begin with <code><</code> and end with
|
||||
<code>></code>. There are two types of tags, the start tag and the
|
||||
end tag. For example, <code><interface></code> is a start tag and
|
||||
<code></interface></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>
|
||||
<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 can’t recognize it and a fatal error occurs.</li>
|
||||
<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>
|
||||
<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>1: XML declaration. It specifies that the XML version is 1.0 and the
|
||||
encoding is UTF-8.</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>
|
||||
<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>
|
||||
<ul>
|
||||
<li><code>gtk4-builder-tool validate <ui file name></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 simplify <ui file name></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>
|
||||
<li><code>gtk4-builder-tool validate <ui file name></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 simplify <ui file name></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>
|
||||
<p>It is a good idea to check your ui file before compiling.</p>
|
||||
<h2 id="gtkbuilder">GtkBuilder</h2>
|
||||
<p>GtkBuilder builds widgets based on the 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>
|
||||
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true"></a></span>
|
||||
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true"></a>build = gtk_builder_new_from_file (<span class="st">"tfe3.ui"</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">"win"</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-6"><a href="#cb3-6" aria-hidden="true"></a>nb = GTK_WIDGET (gtk_builder_get_object (build, <span class="st">"nb"</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 don’t need to take out any other widgets. This reduces lines in the C source 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" 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" tabindex="-1"></a></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">"tfe3.ui"</span><span class="op">);</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">"win"</span><span class="op">));</span></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" 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">"nb"</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 below. This reduces lines in the C
|
||||
source file.</p>
|
||||
<pre><code>$ cd tfe; diff tfe2.c tfe3.c
|
||||
58a59
|
||||
> GtkBuilder *build;
|
||||
|
@ -356,96 +413,124 @@
|
|||
138c100
|
||||
< app = gtk_application_new ("com.github.ToshioCP.tfe2", G_APPLICATION_HANDLES_OPEN);
|
||||
---
|
||||
> app = gtk_application_new ("com.github.ToshioCP.tfe3", G_APPLICATION_HANDLES_OPEN);</code></pre>
|
||||
<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>
|
||||
<p>Now I’ll show you <code>app_open</code> function in the C file <code>tfe3.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>app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer user_data) {</span>
|
||||
<span id="cb5-3"><a href="#cb5-3"></a> GtkWidget *win;</span>
|
||||
<span id="cb5-4"><a href="#cb5-4"></a> GtkWidget *nb;</span>
|
||||
<span id="cb5-5"><a href="#cb5-5"></a> GtkWidget *lab;</span>
|
||||
<span id="cb5-6"><a href="#cb5-6"></a> GtkNotebookPage *nbp;</span>
|
||||
<span id="cb5-7"><a href="#cb5-7"></a> GtkWidget *scr;</span>
|
||||
<span id="cb5-8"><a href="#cb5-8"></a> GtkWidget *tv;</span>
|
||||
<span id="cb5-9"><a href="#cb5-9"></a> GtkTextBuffer *tb;</span>
|
||||
<span id="cb5-10"><a href="#cb5-10"></a> <span class="dt">char</span> *contents;</span>
|
||||
<span id="cb5-11"><a href="#cb5-11"></a> gsize length;</span>
|
||||
<span id="cb5-12"><a href="#cb5-12"></a> <span class="dt">char</span> *filename;</span>
|
||||
<span id="cb5-13"><a href="#cb5-13"></a> <span class="dt">int</span> i;</span>
|
||||
<span id="cb5-14"><a href="#cb5-14"></a> GtkBuilder *build;</span>
|
||||
> app = gtk_application_new ("com.github.ToshioCP.tfe3", G_APPLICATION_HANDLES_OPEN);
|
||||
144a107
|
||||
> </code></pre>
|
||||
<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>
|
||||
<p>Now I’ll show you <code>app_open</code> function in the C file
|
||||
<code>tfe3.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>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-3"><a href="#cb5-3"></a> GtkWidget <span class="op">*</span>win<span class="op">;</span></span>
|
||||
<span id="cb5-4"><a href="#cb5-4"></a> GtkWidget <span class="op">*</span>nb<span class="op">;</span></span>
|
||||
<span id="cb5-5"><a href="#cb5-5"></a> GtkWidget <span class="op">*</span>lab<span class="op">;</span></span>
|
||||
<span id="cb5-6"><a href="#cb5-6"></a> GtkNotebookPage <span class="op">*</span>nbp<span class="op">;</span></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-16"><a href="#cb5-16"></a> build = gtk_builder_new_from_file (<span class="st">"tfe3.ui"</span>);</span>
|
||||
<span id="cb5-17"><a href="#cb5-17"></a> win = GTK_WIDGET (gtk_builder_get_object (build, <span class="st">"win"</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-19"><a href="#cb5-19"></a> nb = GTK_WIDGET (gtk_builder_get_object (build, <span class="st">"nb"</span>));</span>
|
||||
<span id="cb5-20"><a href="#cb5-20"></a> g_object_unref(build);</span>
|
||||
<span id="cb5-21"><a href="#cb5-21"></a> <span class="cf">for</span> (i = <span class="dv">0</span>; i < n_files; i++) {</span>
|
||||
<span id="cb5-22"><a href="#cb5-22"></a> <span class="cf">if</span> (g_file_load_contents (files[i], NULL, &contents, &length, NULL, NULL)) {</span>
|
||||
<span id="cb5-23"><a href="#cb5-23"></a> scr = gtk_scrolled_window_new ();</span>
|
||||
<span id="cb5-24"><a href="#cb5-24"></a> tv = tfe_text_view_new ();</span>
|
||||
<span id="cb5-25"><a href="#cb5-25"></a> tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));</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-27"><a href="#cb5-27"></a> gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);</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">"tfe3.ui"</span><span class="op">);</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">"win"</span><span class="op">));</span></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 <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">"nb"</span><span class="op">));</span></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> <span class="op">(</span>i <span class="op">=</span> <span class="dv">0</span><span class="op">;</span> i <span class="op"><</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> <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">&</span>contents<span class="op">,</span> <span class="op">&</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 <span class="op">=</span> gtk_scrolled_window_new <span class="op">();</span></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 <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 <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 <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-29"><a href="#cb5-29"></a> tfe_text_view_set_file (TFE_TEXT_VIEW (tv), g_file_dup (files[i]));</span>
|
||||
<span id="cb5-30"><a href="#cb5-30"></a> gtk_text_buffer_set_text (tb, contents, length);</span>
|
||||
<span id="cb5-31"><a href="#cb5-31"></a> g_free (contents);</span>
|
||||
<span id="cb5-32"><a href="#cb5-32"></a> filename = g_file_get_basename (files[i]);</span>
|
||||
<span id="cb5-33"><a href="#cb5-33"></a> lab = gtk_label_new (filename);</span>
|
||||
<span id="cb5-34"><a href="#cb5-34"></a> gtk_notebook_append_page (GTK_NOTEBOOK (nb), scr, lab);</span>
|
||||
<span id="cb5-35"><a href="#cb5-35"></a> nbp = gtk_notebook_get_page (GTK_NOTEBOOK (nb), scr);</span>
|
||||
<span id="cb5-36"><a href="#cb5-36"></a> g_object_set (nbp, <span class="st">"tab-expand"</span>, TRUE, NULL);</span>
|
||||
<span id="cb5-37"><a href="#cb5-37"></a> g_free (filename);</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-39"><a href="#cb5-39"></a> g_print (<span class="st">"No such file: %s.</span><span class="sc">\n</span><span class="st">"</span>, filename);</span>
|
||||
<span id="cb5-40"><a href="#cb5-40"></a> g_free (filename);</span>
|
||||
<span id="cb5-41"><a href="#cb5-41"></a> } <span class="cf">else</span></span>
|
||||
<span id="cb5-42"><a href="#cb5-42"></a> g_print (<span class="st">"No valid file is given</span><span class="sc">\n</span><span class="st">"</span>);</span>
|
||||
<span id="cb5-43"><a href="#cb5-43"></a> }</span>
|
||||
<span id="cb5-44"><a href="#cb5-44"></a> <span class="cf">if</span> (gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb)) > <span class="dv">0</span>) {</span>
|
||||
<span id="cb5-45"><a href="#cb5-45"></a> gtk_widget_show (win);</span>
|
||||
<span id="cb5-46"><a href="#cb5-46"></a> } <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-48"><a href="#cb5-48"></a>}</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>
|
||||
<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 <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 <span class="op">(</span>contents<span class="op">);</span></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 <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 <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 <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 <span class="op">(</span>nbp<span class="op">,</span> <span class="st">"tab-expand"</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 <span class="op">(</span>filename<span class="op">);</span></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="op">(</span><span class="st">"No such file: %s.</span><span class="sc">\n</span><span class="st">"</span><span class="op">,</span> filename<span class="op">);</span></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="op">}</span> <span class="cf">else</span></span>
|
||||
<span id="cb5-42"><a href="#cb5-42"></a> g_print <span class="op">(</span><span class="st">"No valid file is given</span><span class="sc">\n</span><span class="st">"</span><span class="op">);</span></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> <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">></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 <span class="op">(</span>win<span class="op">);</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 <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 class="op">}</span></span></code></pre></div>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true"></a></span>
|
||||
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true"></a>uistring =</span>
|
||||
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true"></a><span class="st">"<interface>"</span></span>
|
||||
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true"></a> <span class="st">"<object class="</span>GtkApplicationWindow<span class="st">" id="</span>win<span class="st">">"</span></span>
|
||||
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true"></a> <span class="st">"<property name=</span><span class="sc">\"</span><span class="st">title</span><span class="sc">\"</span><span class="st">>file editor</property>"</span></span>
|
||||
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true"></a> <span class="st">"<property name=</span><span class="sc">\"</span><span class="st">default-width</span><span class="sc">\"</span><span class="st">>600</property>"</span></span>
|
||||
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true"></a> <span class="st">"<property name=</span><span class="sc">\"</span><span class="st">default-height</span><span class="sc">\"</span><span class="st">>400</property>"</span></span>
|
||||
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true"></a> <span class="st">"<child>"</span></span>
|
||||
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true"></a> <span class="st">"<object class=</span><span class="sc">\"</span><span class="st">GtkBox</span><span class="sc">\"</span><span class="st"> id=</span><span class="sc">\"</span><span class="st">boxv</span><span class="sc">\"</span><span class="st">>"</span></span>
|
||||
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true"></a> <span class="st">"<property name="</span>orientation<span class="st">">GTK_ORIENTATION_VERTICAL</property>"</span></span>
|
||||
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true"></a>... ... ...</span>
|
||||
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true"></a>... ... ...</span>
|
||||
<span id="cb6-14"><a href="#cb6-14" aria-hidden="true"></a><span class="st">"</interface>"</span>;</span>
|
||||
<span id="cb6-15"><a href="#cb6-15" aria-hidden="true"></a></span>
|
||||
<span id="cb6-16"><a href="#cb6-16" aria-hidden="true"></a>build = gtk_builder_new_from_stringfile (uistring);</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 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>
|
||||
<p>GtkBuilder can build widgets with string. Use
|
||||
<code>gtk_builder_new_from_string</code> instead of
|
||||
<code>gtk_builder_new_from_file</code>.</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>uistring<span class="op">;</span></span>
|
||||
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>uistring <span class="op">=</span></span>
|
||||
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a><span class="st">"<interface>"</span></span>
|
||||
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a> <span class="st">"<object class="</span>GtkApplicationWindow<span class="st">" id="</span>win<span class="st">">"</span></span>
|
||||
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a> <span class="st">"<property name=</span><span class="sc">\"</span><span class="st">title</span><span class="sc">\"</span><span class="st">>file editor</property>"</span></span>
|
||||
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a> <span class="st">"<property name=</span><span class="sc">\"</span><span class="st">default-width</span><span class="sc">\"</span><span class="st">>600</property>"</span></span>
|
||||
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a> <span class="st">"<property name=</span><span class="sc">\"</span><span class="st">default-height</span><span class="sc">\"</span><span class="st">>400</property>"</span></span>
|
||||
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a> <span class="st">"<child>"</span></span>
|
||||
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a> <span class="st">"<object class=</span><span class="sc">\"</span><span class="st">GtkBox</span><span class="sc">\"</span><span class="st"> id=</span><span class="sc">\"</span><span class="st">boxv</span><span class="sc">\"</span><span class="st">>"</span></span>
|
||||
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a> <span class="st">"<property name="</span>orientation<span class="st">">GTK_ORIENTATION_VERTICAL</property>"</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-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-14"><a href="#cb6-14" aria-hidden="true" tabindex="-1"></a><span class="st">"</interface>"</span><span class="op">;</span></span>
|
||||
<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>
|
||||
<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>
|
||||
<h3 id="using-gresource">Using Gresource</h3>
|
||||
<p>Using Gresource is similar to using string. But Gresource is compressed binary data, not text data. And there’s 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>An xml file is necessary for the resource compiler <code>glib-compile-resources</code>. It describes resource files.</p>
|
||||
<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"><?xml</span> version="1.0" encoding="UTF-8"<span class="kw">?></span></span>
|
||||
<span id="cb7-2"><a href="#cb7-2"></a><span class="kw"><gresources></span></span>
|
||||
<span id="cb7-3"><a href="#cb7-3"></a> <span class="kw"><gresource</span><span class="ot"> prefix=</span><span class="st">"/com/github/ToshioCP/tfe3"</span><span class="kw">></span></span>
|
||||
<span id="cb7-4"><a href="#cb7-4"></a> <span class="kw"><file></span>tfe3.ui<span class="kw"></file></span></span>
|
||||
<span id="cb7-5"><a href="#cb7-5"></a> <span class="kw"></gresource></span></span>
|
||||
<span id="cb7-6"><a href="#cb7-6"></a><span class="kw"></gresources></span></span></code></pre></div>
|
||||
<h3 id="gresource">Gresource</h3>
|
||||
<p>Using Gresource is similar to using string. But Gresource is
|
||||
compressed binary data, not text data. And there’s 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>An xml file is necessary for the resource compiler
|
||||
<code>glib-compile-resources</code>. It describes resource files.</p>
|
||||
<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"><?xml</span><span class="ot"> version=</span><span class="st">"1.0"</span><span class="ot"> encoding=</span><span class="st">"UTF-8"</span><span class="fu">?></span></span>
|
||||
<span id="cb7-2"><a href="#cb7-2"></a><<span class="kw">gresources</span>></span>
|
||||
<span id="cb7-3"><a href="#cb7-3"></a> <<span class="kw">gresource</span><span class="ot"> prefix=</span><span class="st">"/com/github/ToshioCP/tfe3"</span>></span>
|
||||
<span id="cb7-4"><a href="#cb7-4"></a> <<span class="kw">file</span>>tfe3.ui</<span class="kw">file</span>></span>
|
||||
<span id="cb7-5"><a href="#cb7-5"></a> </<span class="kw">gresource</span>></span>
|
||||
<span id="cb7-6"><a href="#cb7-6"></a></<span class="kw">gresources</span>></span></code></pre></div>
|
||||
<ul>
|
||||
<li>2: <code>gresources</code> tag can include multiple gresources (gresource tags). However, this xml has only one gresource.</li>
|
||||
<li>3: The gresource has a prefix <code>/com/github/ToshioCP/tfe3</code>.</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>2: <code>gresources</code> tag can include multiple gresources
|
||||
(gresource tags). However, this xml has only one gresource.</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>
|
||||
<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
|
||||
Usage:
|
||||
glib-compile-resources [OPTION?] FILE
|
||||
|
@ -475,14 +560,20 @@ Application Options:
|
|||
</code></pre>
|
||||
<p>Now run the compiler.</p>
|
||||
<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>
|
||||
<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">"resources.c"</span></span>
|
||||
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true"></a>... ... ...</span>
|
||||
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true"></a>... ... ...</span>
|
||||
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true"></a>build = gtk_builder_new_from_resource (<span class="st">"/com/github/ToshioCP/tfe3/tfe3.ui"</span>);</span>
|
||||
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true"></a>... ... ...</span>
|
||||
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true"></a>... ... ...</span></code></pre></div>
|
||||
<p>Then, compile and run it. The window appears and it is the same as the screenshot at the beginning of this page.</p>
|
||||
<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>
|
||||
<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">"resources.c"</span></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-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-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">"/com/github/ToshioCP/tfe3/tfe3.ui"</span><span class="op">);</span></span>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
||||
<title>Gtk4 tutorial</title>
|
||||
<title>GTK 4 tutorial</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
|
@ -106,7 +106,8 @@
|
|||
</div>
|
||||
</nav>
|
||||
<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>
|
||||
<ul>
|
||||
<li>GFile *tfe_text_view_get_file ()</li>
|
||||
|
@ -136,7 +137,8 @@
|
|||
<h2 id="includes">Includes</h2>
|
||||
<pre><code>#include <gtk/gtk.h></code></pre>
|
||||
<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>
|
||||
<h3 id="tfe_text_view_get_file">tfe_text_view_get_file()</h3>
|
||||
<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>
|
||||
<pre><code>void
|
||||
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 doesn’t 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 can’t 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 doesn’t 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 can’t 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>
|
||||
<ul>
|
||||
<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>
|
||||
<pre><code>void
|
||||
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>
|
||||
<ul>
|
||||
<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>
|
||||
<pre><code>void
|
||||
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>
|
||||
<ul>
|
||||
<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>
|
||||
<pre><code>GtkWidget *
|
||||
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>
|
||||
<ul>
|
||||
<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>
|
||||
<pre><code>GtkWidget *
|
||||
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>
|
||||
<ul>
|
||||
<li>a new TfeTextView.</li>
|
||||
|
@ -199,37 +214,50 @@ struct _TfeTextView
|
|||
GtkTextView parent;
|
||||
GFile *file;
|
||||
};</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>
|
||||
<pre><code>typedef struct {
|
||||
GtkTextViewClass parent_class;
|
||||
} TfeTextViewClass;</code></pre>
|
||||
<p>No member is added because TfeTextView is a final type object.</p>
|
||||
<h3 id="enum-tfetextviewopenresponsetype">enum TfeTextViewOpenResponseType</h3>
|
||||
<p>Predefined values for the response id given by <code>open-response</code> signal.</p>
|
||||
<h3 id="enum-tfetextviewopenresponsetype">enum
|
||||
TfeTextViewOpenResponseType</h3>
|
||||
<p>Predefined values for the response id given by
|
||||
<code>open-response</code> signal.</p>
|
||||
<p>Members:</p>
|
||||
<ul>
|
||||
<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_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>
|
||||
<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>
|
||||
<h3 id="change-file">change-file</h3>
|
||||
<pre><code>void
|
||||
user_function (TfeTextView *tv,
|
||||
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>
|
||||
<li>a new file is opened and read</li>
|
||||
<li>a user choose a file with GtkFileChooserDialog and save the contents.</li>
|
||||
<li>an error occured during I/O operation, and GFile is removed as a result.</li>
|
||||
<li>a user choose a file with GtkFileChooserDialog and save the
|
||||
contents.</li>
|
||||
<li>an error occured during I/O operation, and GFile is removed as a
|
||||
result.</li>
|
||||
</ul>
|
||||
<h3 id="open-response">open-response</h3>
|
||||
<pre><code>void
|
||||
user_function (TfeTextView *tv,
|
||||
TfeTextViewOpenResponseType response-id,
|
||||
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>
|
||||
<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>
|
||||
|
|
31
gfm/sec1.md
|
@ -8,45 +8,42 @@ Up: [README.md](../README.md), Next: [Section 2](sec2.md)
|
|||
|
||||
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.
|
||||
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:
|
||||
|
||||
- PC with Linux distribution like Ubuntu, Debian and so on.
|
||||
- Gcc.
|
||||
- PC with Linux distribution like Ubuntu or Debian.
|
||||
- GCC.
|
||||
- GTK 4.
|
||||
The stable version of Gtk on Linux distributions is version three at present.
|
||||
You need to install GTK 4 to your computer.
|
||||
See [Section 3](sec3.md) for the installation of GTK 4.
|
||||
The stable version of GTK is 4.8.2 at present (13/Dec/2022), but older version (4.0 or higher) may work.
|
||||
See [Section 3](sec3.md) for the installation for GTK 4.
|
||||
|
||||
### Ruby and rake for making the document
|
||||
|
||||
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:
|
||||
|
||||
- Linux distribution like Ubuntu.
|
||||
- Linux.
|
||||
- Ruby programming language.
|
||||
There are two ways to install it.
|
||||
One is installing the distribution's package.
|
||||
The other is using rbenv and ruby-build.
|
||||
If you want to use the latest version of ruby, use rbenv.
|
||||
- Rake.
|
||||
It is a gem, which is a library written in Ruby.
|
||||
You can install it as a package of your distribution or use gem command.
|
||||
You don't need to install it separately because it is a standard library of Ruby.
|
||||
|
||||
## 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.
|
||||
All of them make up the 'Gtk4-tutorial' package.
|
||||
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 repository contains tutorial documents and programs such as converters, generators and controllers.
|
||||
All of them make up the 'GTK4-tutorial' package.
|
||||
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 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.
|
||||
|
||||
|
||||
Up: [README.md](../README.md), Next: [Section 2](sec2.md)
|
||||
|
|
304
gfm/sec10.md
|
@ -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
|
||||
|
||||
## What do we need to think about to manage big source files?
|
||||
## Managing big source files
|
||||
|
||||
We've compiled a small editor so far.
|
||||
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.
|
||||
- There are two compilers, `gcc` and `glib-compile-resources`.
|
||||
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.
|
||||
|
||||
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.
|
||||
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"
|
||||
3
|
||||
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");
|
||||
7 }
|
||||
8
|
||||
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;
|
||||
12 GtkWidget *nb;
|
||||
13 GtkWidget *lab;
|
||||
|
@ -186,10 +186,8 @@ The ui file `tfe.ui` is the same as `tfe3.ui` in the previous section.
|
|||
6 </gresources>
|
||||
~~~
|
||||
|
||||
## Make
|
||||
|
||||
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.
|
||||
|
||||
- Compiling the ui file `tfe.ui` into `resources.c`.
|
||||
|
@ -198,141 +196,18 @@ The building step increases.
|
|||
- Compiling `resources.c` into `resources.o`.
|
||||
- Linking all the object files into application `tfe`.
|
||||
|
||||
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.
|
||||
Build tools manage the steps.
|
||||
I'll show you three build tools, Meson and Ninja, Make and Rake.
|
||||
Meson and Ninja is recommended as a C build tool, but others are also fine.
|
||||
It's your choice.
|
||||
|
||||
Make analyzes Makefile and executes compilers.
|
||||
All instructions are written in Makefile.
|
||||
## Meson and Ninja
|
||||
|
||||
~~~makefile
|
||||
sample.o: sample.c
|
||||
gcc -o sample.o sample.c
|
||||
~~~
|
||||
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.
|
||||
|
||||
The sample of Malefile above consists of three elements, `sample.o`, `sample.c` and `gcc -o sample.o sample.c`.
|
||||
|
||||
- `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.
|
||||
You need to make `meson.build` file first.
|
||||
|
||||
~~~meson
|
||||
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
|
||||
|
||||
Then the window appears.
|
||||
And two notebook pages are in the window.
|
||||
One notebook is `tfe.c` and the other is `tfetextview.c`.
|
||||
A window appears.
|
||||
It includes a notebook with two pages.
|
||||
One is `tfe.c` and the other is `tfetextview.c`.
|
||||
|
||||
I've shown you three build tools.
|
||||
I think meson and ninja is the best choice for the present.
|
||||
For further information, see [The Meson Build system](https://mesonbuild.com/).
|
||||
|
||||
We divided a file into some categorized files and used a build tool.
|
||||
This method is used by many developers.
|
||||
## Make
|
||||
|
||||
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)
|
||||
|
|
232
gfm/sec11.md
|
@ -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.
|
||||
It is `tfe5`.
|
||||
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).
|
||||
|
||||
## Encapsulation
|
||||
|
@ -35,11 +34,10 @@ After that I will explain:
|
|||
|
||||
## GObject and its children
|
||||
|
||||
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.
|
||||
GObject and its children are objects, which have both class and object C structures.
|
||||
First, think about instances.
|
||||
An instance is memories which has the object structure.
|
||||
The following is the structure of TfeTextView.
|
||||
|
||||
~~~C
|
||||
/* 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.
|
||||
It is declared in `gtktextview.h`.
|
||||
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.
|
||||
So, no memories are allocated at this moment.
|
||||
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).
|
||||
You can find the declaration of the ancestors' object structures in the source files of GTK and GLib.
|
||||
The following is extracted from the source files (not exactly the same).
|
||||
|
||||
~~~C
|
||||
typedef struct _GObject GObject;
|
||||
|
@ -93,34 +85,65 @@ struct _GtkTextView
|
|||
~~~
|
||||
|
||||
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.
|
||||
So, all the ancestors are included in the child object.
|
||||
The structure of `TfeTextView` is like the following diagram.
|
||||
|
||||
![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.
|
||||
|
||||
~~~C
|
||||
1 GtkWidget *
|
||||
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 }
|
||||
~~~
|
||||
|
||||
When this function is invoked, a TfeTextView instance is created and initialized.
|
||||
The initialization process is as follows.
|
||||
|
||||
1. Initializes GObject (GInitiallyUnowned) part in TfeTextView instance.
|
||||
2. Initializes GtkWidget part in TfeTextView instance.
|
||||
3. Initializes GtkTextView part in TfeTextView instance.
|
||||
4. Initializes TfeTextView part in TfeTextView instance.
|
||||
1. When the instance is created, GtkWidgetPrivate and GtkTextViewPrivate structures are also created
|
||||
2. Initializes GObject (GInitiallyUnowned) part in the TfeTextView instance.
|
||||
3. Initializes GtkWidget part (the first `priv`) in the TfeTextView instance and GtkWidgetPrivate structure.
|
||||
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.
|
||||
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
|
||||
|
||||
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.
|
||||
Those instances have the same structure.
|
||||
An instance just keeps status of the instance.
|
||||
Therefore, it is insufficient to define its behavior.
|
||||
Instances just have data.
|
||||
Therefore, it doesn't define object's behavior.
|
||||
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.
|
||||
For example, `tfe_text_view_new` 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.
|
||||
For example,
|
||||
|
||||
Class comprises mainly pointers to functions.
|
||||
Those functions are used by the object itself or its descendant objects.
|
||||
- `TfeTextView *tfe_text_view_new (void);` is a function to create a TfeTextView instance.
|
||||
- `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.
|
||||
|
||||
~~~C
|
||||
|
@ -160,42 +187,51 @@ For example, GObject class is declared in `gobject.h` in GLib source files.
|
|||
4 struct _GObjectClass
|
||||
5 {
|
||||
6 GTypeClass g_type_class;
|
||||
7 /*< private >*/
|
||||
8 GSList *construct_properties;
|
||||
9 /*< public >*/
|
||||
10 /* seldom overridden */
|
||||
11 GObject* (*constructor) (GType type,
|
||||
12 guint n_construct_properties,
|
||||
13 GObjectConstructParam *construct_properties);
|
||||
14 /* overridable methods */
|
||||
15 void (*set_property) (GObject *object,
|
||||
16 guint property_id,
|
||||
17 const GValue *value,
|
||||
18 GParamSpec *pspec);
|
||||
19 void (*get_property) (GObject *object,
|
||||
20 guint property_id,
|
||||
21 GValue *value,
|
||||
22 GParamSpec *pspec);
|
||||
23 void (*dispose) (GObject *object);
|
||||
24 void (*finalize) (GObject *object);
|
||||
25 /* seldom overridden */
|
||||
26 void (*dispatch_properties_changed) (GObject *object,
|
||||
27 guint n_pspecs,
|
||||
28 GParamSpec **pspecs);
|
||||
29 /* signals */
|
||||
30 void (*notify) (GObject *object,
|
||||
31 GParamSpec *pspec);
|
||||
32 /* called when done constructing */
|
||||
33 void (*constructed) (GObject *object);
|
||||
34 /*< private >*/
|
||||
35 gsize flags;
|
||||
36 /* padding */
|
||||
37 gpointer pdummy[6];
|
||||
38 };
|
||||
39
|
||||
7
|
||||
8 /*< private >*/
|
||||
9 GSList *construct_properties;
|
||||
10
|
||||
11 /*< public >*/
|
||||
12 /* seldom overridden */
|
||||
13 GObject* (*constructor) (GType type,
|
||||
14 guint n_construct_properties,
|
||||
15 GObjectConstructParam *construct_properties);
|
||||
16 /* overridable methods */
|
||||
17 void (*set_property) (GObject *object,
|
||||
18 guint property_id,
|
||||
19 const GValue *value,
|
||||
20 GParamSpec *pspec);
|
||||
21 void (*get_property) (GObject *object,
|
||||
22 guint property_id,
|
||||
23 GValue *value,
|
||||
24 GParamSpec *pspec);
|
||||
25 void (*dispose) (GObject *object);
|
||||
26 void (*finalize) (GObject *object);
|
||||
27 /* seldom overridden */
|
||||
28 void (*dispatch_properties_changed) (GObject *object,
|
||||
29 guint n_pspecs,
|
||||
30 GParamSpec **pspecs);
|
||||
31 /* signals */
|
||||
32 void (*notify) (GObject *object,
|
||||
33 GParamSpec *pspec);
|
||||
34
|
||||
35 /* called when done constructing */
|
||||
36 void (*constructed) (GObject *object);
|
||||
37
|
||||
38 /*< private >*/
|
||||
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.
|
||||
|
||||
~~~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.
|
||||
|
||||
- 11: 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.
|
||||
- 13: A function pointed by `constructor` is called when the instance is generated. It completes the initialization of the instance.
|
||||
- 25: A function pointed by `dispose` 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.
|
||||
- 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.
|
||||
|
||||
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 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;
|
||||
struct _TfeTextView {
|
||||
GtkTextView parent;
|
||||
GFile *file;
|
||||
};
|
||||
~~~
|
||||
GObjectClass (GInitiallyUnownedClass) -- GtkWidgetClass -- GtkTextViewClass -- TfeTextViewClass
|
||||
~~~
|
||||
|
||||
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.
|
||||
|
||||
GObject -- GInitiallyUnowned -- GtkWidget -- GtkTextView -- TfeTextView
|
||||
|
||||
The following is extracts from the source files (not exactly the same).
|
||||
The following is extracted from the source codes (not exactly the same).
|
||||
|
||||
~~~C
|
||||
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);
|
||||
103 GtkTextBuffer * (* create_buffer) (GtkTextView *text_view);
|
||||
104 void (* snapshot_layer) (GtkTextView *text_view,
|
||||
105 GtkTextViewLayer layer,
|
||||
106 GtkSnapshot *snapshot);
|
||||
105 GtkTextViewLayer layer,
|
||||
106 GtkSnapshot *snapshot);
|
||||
107 gboolean (* extend_selection) (GtkTextView *text_view,
|
||||
108 GtkTextExtendSelection granularity,
|
||||
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`.
|
||||
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.
|
||||
- Class members in ancestors are open to the descendant class.
|
||||
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 rewriting ancestors' class methods in the descendant class.)
|
||||
- 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.
|
||||
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 `dispose` in its class to release all references to other objects.
|
||||
In the finalizing process, it invokes the function pointed by `finalize` in its class to complete the destruction process.
|
||||
These functions are also called handlers or methods.
|
||||
In the disposing process, the object invokes the function pointed by `dispose` in its class to release all references to other instances.
|
||||
After that, it invokes the function pointed by `finalize` in its class to complete the destruction process.
|
||||
For example, dispose handler or dispose method.
|
||||
|
||||
In the destruction process of TfeTextView, the reference count of widgets related to TfeTextView is automatically decreased.
|
||||
But GFile pointed by `tv->file` needs to decrease its reference count by one.
|
||||
You must write the code in the dispose handler `tfe_text_view_dispose`.
|
||||
In the destruction process, TfeTextView needs to unref the GFile pointed by `tv->file`.
|
||||
You must write the dispose handler `tfe_text_view_dispose`.
|
||||
|
||||
~~~C
|
||||
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.
|
||||
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
|
||||
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);
|
||||
~~~
|
||||
|
||||
`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.
|
||||
And `gobject` is a pointer to TfeTextView instance which is casted as a GObject instance.
|
||||
`dh3` releases all the references to objects in the GtkTextView part (it is actually the private area pointed by `prev`) in TfeTextView 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.
|
||||
After that, `dh3` calls `dh2`, and `dh2` calls `dh1`.
|
||||
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)
|
||||
|
|
23
gfm/sec12.md
|
@ -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
|
||||
|
||||
In Gtk programming, each object is encapsulated.
|
||||
And it is not recommended to use global variables because they tend to make the program complicated.
|
||||
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.
|
||||
|
||||
- Functions.
|
||||
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.
|
||||
For example, `activate` signal on GApplication object.
|
||||
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.
|
||||
The connection is usually done out of the object.
|
||||
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
|
||||
|
||||
|
@ -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.
|
||||
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 array is used to register two or more signals.
|
||||
A static variable or array is used to store signal ID.
|
||||
|
||||
~~~C
|
||||
enum {
|
||||
|
@ -88,10 +87,10 @@ Signals are registered in the class initialization function.
|
|||
|
||||
- 6-15: Registers "change-file" signal.
|
||||
`g_signal_new` function is used.
|
||||
The signal "change-file" has no default handler (object method handler).
|
||||
You usually don't need to set a default handler.
|
||||
If you need it, use `g_signal_new_class_handler` function.
|
||||
See [GObject API Reference, g\_signal\_new\_class\_handler](https://docs.gtk.org/gobject/func.signal_new_class_handler.html) for further information.
|
||||
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 a default handler.
|
||||
If you need it, use `g_signal_new_class_handler` function instead of `g_signal_new`.
|
||||
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 type of signal id is guint, which is the same as unsigned int.
|
||||
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.
|
||||
|
||||
|
||||
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)
|
||||
|
|
472
gfm/sec13.md
|
@ -2,21 +2,18 @@ Up: [README.md](../README.md), Prev: [Section 12](sec12.md), Next: [Section 14]
|
|||
|
||||
# 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.
|
||||
C source files `tfeapplication.c` and `tfenotebook.c` include `tfe.h` at the beginning.
|
||||
The header file `tfetextview.h` provides:
|
||||
|
||||
~~~C
|
||||
1 #include <gtk/gtk.h>
|
||||
2
|
||||
3 #include "../tfetextview/tfetextview.h"
|
||||
4 #include "tfenotebook.h"
|
||||
~~~
|
||||
- The type of TfeTextView, which is `TFE_TYPE_TEXT_VIEW`.
|
||||
- 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`.
|
||||
|
||||
~~~C
|
||||
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__ */
|
||||
~~~
|
||||
|
||||
- 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.
|
||||
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.
|
||||
- `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.
|
||||
- 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`.
|
||||
|
||||
|
@ -107,16 +110,19 @@ Each function is defined as follows.
|
|||
22
|
||||
23 GtkWidget *
|
||||
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 }
|
||||
~~~
|
||||
|
||||
- 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.
|
||||
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.
|
||||
- 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).
|
||||
- 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).
|
||||
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 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.
|
||||
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.
|
||||
|
||||
|
@ -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.
|
||||
The error is managed only in the TfeTextView and no information is notified to the caller.
|
||||
|
||||
### save\_file function
|
||||
|
||||
~~~C
|
||||
1 static gboolean
|
||||
2 save_file (GFile *file, GtkTextBuffer *tb, GtkWindow *win) {
|
||||
3 GtkTextIter start_iter;
|
||||
4 GtkTextIter end_iter;
|
||||
5 gchar *contents;
|
||||
5 char *contents;
|
||||
6 gboolean stat;
|
||||
7 GtkWidget *message_dialog;
|
||||
8 GError *err = NULL;
|
||||
9
|
||||
10 gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter);
|
||||
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)) {
|
||||
13 gtk_text_buffer_set_modified (tb, FALSE);
|
||||
14 stat = TRUE;
|
||||
15 } else {
|
||||
16 message_dialog = gtk_message_dialog_new (win, GTK_DIALOG_MODAL,
|
||||
17 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
|
||||
18 "%s.\n", err->message);
|
||||
12 stat = g_file_replace_contents (file, contents, strlen (contents), NULL, TRUE, G_FILE_CREATE_NONE, NULL, NULL, &err);
|
||||
13 if (stat)
|
||||
14 gtk_text_buffer_set_modified (tb, FALSE);
|
||||
15 else {
|
||||
16 // Because error message is displayed here, the caller of 'save_file' doesn't need to do anything about error.
|
||||
17 message_dialog = gtk_message_dialog_new (win, GTK_DIALOG_MODAL,
|
||||
18 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s.", err->message);
|
||||
19 g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
|
||||
20 gtk_widget_show (message_dialog);
|
||||
21 g_error_free (err);
|
||||
22 stat = FALSE;
|
||||
23 }
|
||||
24 g_free (contents);
|
||||
25 return stat;
|
||||
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 }
|
||||
22 }
|
||||
23 g_free (contents);
|
||||
24 return stat;
|
||||
25 }
|
||||
~~~
|
||||
|
||||
- 1-26: `save_file` function.
|
||||
This function is called from `saveas_dialog_response` and `tfe_text_view_save`.
|
||||
- The function `save_file` 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.
|
||||
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`.
|
||||
Therefore, only functions in this file (`tfeTetview.c`) call this function.
|
||||
Such static functions usally don't have `g_return_val_if_fail` function.
|
||||
Therefore, only functions in this file (`tfetextview.c`) call this function.
|
||||
Such static functions usually don't have `g_return_val_if_fail` function.
|
||||
- 10-11: Gets the text contents from the buffer.
|
||||
- 12-14: Saves the contents to the file.
|
||||
If no error happens, set the modified flag to be FALSE.
|
||||
- 12: The function `g_file_replace_contents` writes the contents to the file and returns the status (true = success/ false = fail).
|
||||
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.
|
||||
And set the return status `stat` to be TRUE.
|
||||
- 15-23: If it fails to save the contents, displays an error message.
|
||||
- 16-18: Creates a message dialog with the error message.
|
||||
- 19: Connects the "response" signal to `gtk_window_destroy`, so that the dialog disappears when a user clicked on the button.
|
||||
- 20-21: Shows the window, frees `err` and set `stat` to be FLASE.
|
||||
- 24: Frees `contents`.
|
||||
- 25: Returns to the caller.
|
||||
- 28-47: `saveas_dialog_response` function.
|
||||
This is a signal handler for the "response" signal on GtkFileChooserDialog instance created by `tfe_text_view_saveas` function.
|
||||
This handler analyzes the response and determines whether to save the contents.
|
||||
- 34-45: If the response is `GTK_RESPONSE_ACCEPT`, the user has clicked on the `Save` button. So, it tries to save.
|
||||
- 35: Gets the GFile `file` from GtkFileChooserDialog.
|
||||
- 36-37: If it doesn't point GFile, it outputs an error message to the log.
|
||||
- 38: Otherwise, it calls `save_file` to save the contents to the file.
|
||||
- 39-42: If `save_file` has successfully saved the contents, `tv->file` is updated.
|
||||
If the old GFile pointed by `tv->file` exists, it is freed in advance.
|
||||
Emits "change-file" signal.
|
||||
- 44: Unrefs `file`.
|
||||
- 46: destroys the file chooser dialog.
|
||||
- 49-64: `tfe_text_view_save` function.
|
||||
- 51: `tfe_text_view_save` is public, i.e. it is open to the other files.
|
||||
- 15-22: If it fails to save the contents, an error message will be displayed.
|
||||
- 17-18: Creates a message dialog. The parameters are:
|
||||
- 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.
|
||||
- GtkDialogFlags flags: GTK\_DIALOG\_MODAL for modal dialog. A modal dialog is usually fine.
|
||||
- GtkMessageType type: GTK\_MESSAGE\_ERROR for error message. Other options are GTK\_MESSAGE\_INFO, GTK\_MESSAGE\_WARNING and so on.
|
||||
- GtkButtonsType buttons: GTK\_BUTTON\_OK is used most often. Other option is GTK\_BUTTON\_YES\_NO, GTK\_BUTTON\_CANCEL and so on.
|
||||
- const gchar* message_format: gchar is the same as char. This format is the same as printf. Arguments for the message format follow.
|
||||
- 19: Connects the "response" signal to `gtk_window_destroy`, so that the dialog disappears when the user clicks on the button.
|
||||
- 20: Shows the message dialog.
|
||||
- 21: Frees `err` with `g_error_free` function.
|
||||
- 23: Frees `contents`.
|
||||
- 24: Returns to the caller.
|
||||
|
||||
### saveas\_dialog\_response function
|
||||
|
||||
~~~C
|
||||
1 static void
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
- 53-54: Gets GtkTextBuffer instance and GtkWidget instance and assignes them to `tb` and`win` respectively.
|
||||
- 56-57: If the buffer hasn't modified, then it doesn't need to save it.
|
||||
So the function returns.
|
||||
- 58-59: If `tv->file` is NULL, no file has given yet.
|
||||
It calls `tfe_text_view_saveas` which prompts a user to select a file or specify a new file to save.
|
||||
- 60-61: If `tv->file` doesn't point GFile, somethig bad has happened.
|
||||
Logs an error message.
|
||||
- 62-63: Calls `save_file` to save the contents to the file.
|
||||
- 66-79: `tfe_text_view_saveas` function.
|
||||
It shows GtkFileChooserDialog and prompts the user to choose a file.
|
||||
- 73-76: Creates GtkFileChooserDialog.
|
||||
The title is "Save file".
|
||||
Transient parent of the dialog is `win`, which is the top-level window.
|
||||
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.
|
||||
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).
|
||||
- 5-6: GtkTextBuffer `tb` and GtkWidget (GtkWindow) `win` are set.
|
||||
The function `gtk_widget_get_ancestor (widget, type)` returns the first ancestor of the widget with type.
|
||||
The type is a GType.
|
||||
For example, the type of GtkWindow is `GTK_TYPE_WINDOW` and the one of TfeTextView is `TFE_TYPE_TEXT_VIEW`.
|
||||
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 can't 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.
|
||||
- 8-9: If the buffer hasn't modified, it doesn't need to be saved.
|
||||
- 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.
|
||||
- 12-13: If `tv->file` doesn't point GFile, an error message is logged out. It is not expected.
|
||||
- 14-15: Otherwise, it calls `save_file` to save the contents to the file `tv->file`.
|
||||
|
||||
![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.
|
||||
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.
|
||||
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.
|
||||
|
||||
~~~C
|
||||
|
@ -317,10 +378,8 @@ void tfe_text_view_open (TfeTextView *tv, GtkWindow *win);
|
|||
|
||||
The parameter `win` is the top-level window.
|
||||
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.
|
||||
However, it is encouraged to give a parent window to dialog.
|
||||
This function might be called just after `tv` has been created.
|
||||
|
||||
This function may be called just after `tv` has been created.
|
||||
In that case, `tv` has not been incorporated into the widget hierarchy.
|
||||
Therefore it is impossible to get the top-level window from `tv`.
|
||||
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.
|
||||
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.
|
||||
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
|
||||
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 */
|
||||
16 g_object_unref (file);
|
||||
17 message_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog), GTK_DIALOG_MODAL,
|
||||
18 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
|
||||
19 "%s.\n", err->message);
|
||||
20 g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
|
||||
21 gtk_widget_show (message_dialog);
|
||||
22 g_error_free (err);
|
||||
23 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_ERROR);
|
||||
24 } else {
|
||||
25 gtk_text_buffer_set_text (tb, contents, length);
|
||||
26 g_free (contents);
|
||||
27 if (G_IS_FILE (tv->file))
|
||||
28 g_object_unref (tv->file);
|
||||
29 tv->file = file;
|
||||
30 gtk_text_buffer_set_modified (tb, FALSE);
|
||||
31 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_SUCCESS);
|
||||
32 g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
|
||||
33 }
|
||||
34 gtk_window_destroy (GTK_WINDOW (dialog));
|
||||
35 }
|
||||
36
|
||||
37 void
|
||||
38 tfe_text_view_open (TfeTextView *tv, GtkWindow *win) {
|
||||
39 g_return_if_fail (TFE_IS_TEXT_VIEW (tv));
|
||||
40 g_return_if_fail (GTK_IS_WINDOW (win));
|
||||
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 }
|
||||
18 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s", err->message);
|
||||
19 g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
|
||||
20 gtk_widget_show (message_dialog);
|
||||
21 g_error_free (err);
|
||||
22 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_ERROR);
|
||||
23 } else {
|
||||
24 gtk_text_buffer_set_text (tb, contents, length);
|
||||
25 g_free (contents);
|
||||
26 gtk_text_buffer_set_modified (tb, FALSE);
|
||||
27 // G_IS_FILE(tv->file) && tv->file == file => unref(tv->file), tv->file=file, emit response with SUCCESS
|
||||
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==NULL => tv->file=file, emit response with SUCCESS, emit change-file
|
||||
30 // The order is important. If you unref tv->file first, you can't compare tv->file and file anymore.
|
||||
31 // And open-response signal is emitted after new tv->file is set. Or the handler can't catch the new file.
|
||||
32 if (! (G_IS_FILE (tv->file) && g_file_equal (tv->file, file)))
|
||||
33 g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
|
||||
34 if (G_IS_FILE (tv->file))
|
||||
35 g_object_unref (tv->file);
|
||||
36 tv->file = file; // The ownership of 'file' moves to TfeTextView
|
||||
37 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_SUCCESS);
|
||||
38 }
|
||||
39 gtk_window_destroy (GTK_WINDOW (dialog));
|
||||
40 }
|
||||
~~~
|
||||
|
||||
- 37-50: `tfe_text_view_open` function.
|
||||
- 44-47: Creates GtkFileChooserDialog.
|
||||
The title is "Open file".
|
||||
Transient parent window is the top-level window of the application, which is given by the caller.
|
||||
The action is open mode.
|
||||
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.
|
||||
- 2: The handler `open_dialog_response` has three parameters.
|
||||
- GtkWidget *dialog: FileChooserDialog on which the "response" signal is emitted.
|
||||
- gint response: response ID
|
||||
- TfeTextView *tv: textview to put the contents to
|
||||
- 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.
|
||||
Then, "open-response" signal is emitted.
|
||||
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.
|
||||
It is not expected, though.
|
||||
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`.
|
||||
- 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).
|
||||
Then, it emits "open-response" signal with the parameter `TFE_OPEN_RESPONSE_SUCCESS` and emits "change-file" signal.
|
||||
- 34: destroys GtkFileCooserDialog.
|
||||
- 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`.
|
||||
- 23-38: If the file has been successfully read, the following is carried out. (It is not simple.)
|
||||
- the text is inserted to GtkTextBuffer
|
||||
- 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.
|
||||
It is shown in the following diagram and you would think that it is really complicated.
|
||||
### tfe\_text\_view\_open function
|
||||
|
||||
~~~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.
|
||||
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)
|
||||
|
||||
|
@ -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.
|
||||
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
|
||||
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.
|
||||
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.
|
||||
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
|
||||
|
||||
Refer [API document of TfeTextView](tfetextview_doc.md).
|
||||
It is under the directory `src/tfetextview`.
|
||||
|
||||
All the source files are listed in [Section 16](sec16.md).
|
||||
You can find them under [src/tfe5](../src/tfe5) and [src/tfetextview](../src/tfetextview) directories.
|
||||
You can find all the TfeTextView source codes under [src/tfetextview](../src/tfetextview) directories.
|
||||
|
||||
|
||||
Up: [README.md](../README.md), Prev: [Section 12](sec12.md), Next: [Section 14](sec14.md)
|
||||
|
|
190
gfm/sec14.md
|
@ -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
|
||||
|
||||
|
@ -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.
|
||||
- 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.
|
||||
- 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.
|
||||
|
||||
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
|
||||
|
||||
~~~C
|
||||
1 static gchar*
|
||||
1 static char*
|
||||
2 get_untitled () {
|
||||
3 static int c = -1;
|
||||
4 if (++c == 0)
|
||||
|
@ -63,59 +63,62 @@ Now let's look at the program of each function.
|
|||
8 }
|
||||
9
|
||||
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 ();
|
||||
13 GtkNotebookPage *nbp;
|
||||
14 GtkWidget *lab;
|
||||
15 int i;
|
||||
16
|
||||
17 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR);
|
||||
18 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);
|
||||
19 lab = gtk_label_new (filename);
|
||||
20 i = gtk_notebook_append_page (nb, scr, lab);
|
||||
21 nbp = gtk_notebook_get_page (nb, scr);
|
||||
22 g_object_set (nbp, "tab-expand", TRUE, NULL);
|
||||
23 gtk_notebook_set_current_page (nb, i);
|
||||
24 g_signal_connect (GTK_TEXT_VIEW (tv), "change-file", G_CALLBACK (file_changed_cb), nb);
|
||||
25 }
|
||||
26
|
||||
27 void
|
||||
28 notebook_page_new (GtkNotebook *nb) {
|
||||
29 g_return_if_fail(GTK_IS_NOTEBOOK (nb));
|
||||
30
|
||||
31 GtkWidget *tv;
|
||||
32 char *filename;
|
||||
33
|
||||
34 if ((tv = tfe_text_view_new ()) == NULL)
|
||||
35 return;
|
||||
36 filename = get_untitled ();
|
||||
37 notebook_page_build (nb, tv, filename);
|
||||
17 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);
|
||||
18 lab = gtk_label_new (filename);
|
||||
19 i = gtk_notebook_append_page (nb, scr, lab);
|
||||
20 nbp = gtk_notebook_get_page (nb, scr);
|
||||
21 g_object_set (nbp, "tab-expand", TRUE, NULL);
|
||||
22 gtk_notebook_set_current_page (nb, i);
|
||||
23 g_signal_connect (GTK_TEXT_VIEW (tv), "change-file", G_CALLBACK (file_changed_cb), nb);
|
||||
24 }
|
||||
25
|
||||
26 void
|
||||
27 notebook_page_new (GtkNotebook *nb) {
|
||||
28 g_return_if_fail(GTK_IS_NOTEBOOK (nb));
|
||||
29
|
||||
30 GtkWidget *tv;
|
||||
31 char *filename;
|
||||
32
|
||||
33 if ((tv = tfe_text_view_new ()) == NULL)
|
||||
34 return;
|
||||
35 filename = get_untitled ();
|
||||
36 notebook_page_build (nb, tv, filename);
|
||||
37 g_free (filename);
|
||||
38 }
|
||||
~~~
|
||||
|
||||
- 27-38: `notebook_page_new` function.
|
||||
- 29: `g_return_if_fail` is used to check the argument.
|
||||
- 34: Creates TfeTextView object.
|
||||
If it fails, it returns to the caller.
|
||||
- 36: Creates filename, which is "Untitled", "Untitled1", ... .
|
||||
- 26-38: `notebook_page_new` function.
|
||||
- 28: `g_return_if_fail` is used to check the argument.
|
||||
- 33-34: Creates TfeTextView object.
|
||||
If it fails, no notebook page is created and the function returns to the caller.
|
||||
- 35: Creates filename, which is "Untitled", "Untitled1", ... .
|
||||
- 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.
|
||||
- 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 caller of `get_untitled` is in charge of freeing the string.
|
||||
- 37: calls `notebook_page_build` to build the contents of the page.
|
||||
- 10- 25: `notebook_page_build` function.
|
||||
- 36: calls `notebook_page_build` to build the contents of the page.
|
||||
- 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.
|
||||
- 17: Sets the wrap mode of `tv` to GTK_WRAP_WORD_CHAR so that lines are broken between words or graphemes.
|
||||
- 18: Inserts `tv` to GtkscrolledWindow as a child.
|
||||
- 19-20: Creates GtkLabel, then appends `scr` and `lab` to the GtkNotebook instance `nb`.
|
||||
- 21-22: Sets "tab-expand" property to TRUE.
|
||||
- 17: Inserts `tv` to GtkscrolledWindow as a child.
|
||||
- 18-19: Creates GtkLabel, then appends `scr` and `lab` to the GtkNotebook instance `nb`.
|
||||
- 20-21: Sets "tab-expand" property to TRUE.
|
||||
The function `g_object_set` 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 `g_object_set` to set the property.
|
||||
- 23: Sets the current page of `nb` to the newly created page.
|
||||
- 24: Connects "change-file" signal and `file_changed_cb` handler.
|
||||
- 22: Sets the current page to the newly created page.
|
||||
- 23: Connects "change-file" signal and `file_changed_cb` handler.
|
||||
|
||||
## notebook\_page\_new\_with\_file
|
||||
|
||||
|
@ -132,13 +135,14 @@ In that case, use `g_object_set` to set the property.
|
|||
10 return; /* read error */
|
||||
11 filename = g_file_get_basename (file);
|
||||
12 notebook_page_build (nb, tv, filename);
|
||||
13 }
|
||||
13 g_free (filename);
|
||||
14 }
|
||||
~~~
|
||||
|
||||
- 9-10: Calls `tfe_text_view_new_with_file`.
|
||||
If the function returns NULL, an error has happend.
|
||||
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
|
||||
|
||||
|
@ -148,58 +152,95 @@ Then, it does nothing and returns.
|
|||
3 GFile *file;
|
||||
4 char *filename;
|
||||
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);
|
||||
8 g_object_unref (tv);
|
||||
9 }else {
|
||||
10 filename = g_file_get_basename (file);
|
||||
11 g_object_unref (file);
|
||||
12 notebook_page_build (nb, GTK_WIDGET (tv), filename);
|
||||
13 }
|
||||
14 }
|
||||
15
|
||||
16 void
|
||||
17 notebook_page_open (GtkNotebook *nb) {
|
||||
18 g_return_if_fail(GTK_IS_NOTEBOOK (nb));
|
||||
19
|
||||
20 GtkWidget *tv;
|
||||
10 file = tfe_text_view_get_file (tv);
|
||||
11 filename = g_file_get_basename (file);
|
||||
12 g_object_unref (file);
|
||||
13 notebook_page_build (nb, GTK_WIDGET (tv), filename);
|
||||
14 g_free (filename);
|
||||
15 }
|
||||
16 }
|
||||
17
|
||||
18 void
|
||||
19 notebook_page_open (GtkNotebook *nb) {
|
||||
20 g_return_if_fail(GTK_IS_NOTEBOOK (nb));
|
||||
21
|
||||
22 if ((tv = tfe_text_view_new ()) == NULL)
|
||||
23 return;
|
||||
24 g_signal_connect (TFE_TEXT_VIEW (tv), "open-response", G_CALLBACK (open_response), nb);
|
||||
25 tfe_text_view_open (TFE_TEXT_VIEW (tv), GTK_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET (nb), GTK_TYPE_WINDOW)));
|
||||
26 }
|
||||
22 GtkWidget *tv;
|
||||
23
|
||||
24 if ((tv = tfe_text_view_new ()) == NULL)
|
||||
25 return;
|
||||
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.
|
||||
- 22-23: Creates TfeTextView object.
|
||||
- 18-28: `notebook_page_open` function.
|
||||
- 24-25: Creates TfeTextView object.
|
||||
If NULL is returned, an error has happened.
|
||||
Then, it returns to the caller.
|
||||
- 24: Connects the signal "open-response" and the handler `open_response`.
|
||||
- 25: Calls `tfe_text_view_open`.
|
||||
- 26: Connects the signal "open-response" and the handler `open_response`.
|
||||
- 27: Calls `tfe_text_view_open`.
|
||||
The "open-response" signal will be emitted later to inform the result of opening and reading a file.
|
||||
- 1-14: `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,
|
||||
it has failed to open and read a new file.
|
||||
- 1-16: `open_response` handler.
|
||||
- 6-8: If the response code is not `TFE_OPEN_RESPONSE_SUCCESS`, it has failed to open and read a new file.
|
||||
Then, what `notebook_page_open` did in advance need to be canceled.
|
||||
The instance `tv` hasn't been a child widget of GtkScrolledWindow yet.
|
||||
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.
|
||||
Then the floating reference is converted into an ordinary reference.
|
||||
Now you call `g_object_unref` to decrease the reference count by one.
|
||||
- 9-13: Otherwise, everything is okay.
|
||||
Gets the filename, builds the contents of the page.
|
||||
- 9-15: Otherwise, everything is okay.
|
||||
Gets the filename, builds the contents of the page and frees `filename`.
|
||||
|
||||
## Floating reference
|
||||
|
||||
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 `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.
|
||||
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 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.
|
||||
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
|
||||
|
||||
|
@ -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.
|
||||
- 11-13: Otherwise, removes the current page.
|
||||
The child widget (TfeTextView) is also destroyed.
|
||||
|
||||
## 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.
|
||||
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
|
||||
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`.
|
||||
|
||||
|
||||
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)
|
||||
|
|
|
@ -124,7 +124,7 @@ textview {color: yellow; ...}
|
|||
~~~
|
||||
|
||||
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.
|
||||
|
||||
|
@ -306,9 +306,7 @@ In this file, just the source file names are modified from the prior version.
|
|||
|
||||
## source files
|
||||
|
||||
The [source files](../src/tfe5) of the text editor `tfe` will be shown in the next section.
|
||||
|
||||
You can also download the files from the [repository](https://github.com/ToshioCP/Gtk4-tutorial).
|
||||
You can download the files from the [repository](https://github.com/ToshioCP/Gtk4-tutorial).
|
||||
There are two options.
|
||||
|
||||
- Use git and clone.
|
||||
|
|
678
gfm/sec16.md
|
@ -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'.
|
||||
|
||||
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).
|
||||
|
||||
The following is the instruction of compilation and execution.
|
||||
|
||||
- You need meson and ninja.
|
||||
- Set environment variables if necessary.
|
||||
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.
|
||||
|
||||
~~~
|
||||
$ . env.sh
|
||||
~~~
|
||||
- change your current directory to `src/tfe5` directory.
|
||||
- type `meson _build` for configuration.
|
||||
- type `ninja -C _build` for compilation.
|
||||
- If you have installed gtk4 from the source, you need to set environment variables to suit your installation.
|
||||
- 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.
|
||||
- type `_build/tfe` to execute it.
|
||||
- Type `_build/tfe` to execute it.
|
||||
|
||||
Then the window appears.
|
||||
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.
|
||||
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
|
||||
|
||||
~~~
|
||||
$ 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
|
||||
99 304 3205 tfe5/tfeapplication.c
|
||||
101 308 3274 tfe5/tfeapplication.c
|
||||
6 9 153 tfe5/tfe.gresource.xml
|
||||
4 6 87 tfe5/tfe.h
|
||||
140 378 3601 tfe5/tfenotebook.c
|
||||
145 387 3667 tfe5/tfenotebook.c
|
||||
15 21 241 tfe5/tfenotebook.h
|
||||
229 671 8017 tfetextview/tfetextview.c
|
||||
239 863 9264 tfetextview/tfetextview.c
|
||||
35 60 701 tfetextview/tfetextview.h
|
||||
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)
|
||||
|
|
373
gfm/sec2.md
|
@ -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 tutorial is without any warranty.
|
||||
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.
|
||||
There are two ways to install GTK 4.
|
||||
|
||||
- Install it from the distribution packages.
|
||||
- Build it from the source file.
|
||||
- Install a GNOME 40 distribution with the GNOME Boxes.
|
||||
- Build it from the source files.
|
||||
|
||||
## Installation from the distribution packages
|
||||
|
||||
The first way is easy to install.
|
||||
It is a recommended way.
|
||||
I've installed GTK 4 packages in Ubuntu 21.04.
|
||||
(Now, my Ubuntu version is 21.10).
|
||||
The first way is the best and easiest way to install it.
|
||||
I've installed GTK 4 packages (version 4.8.1) in Ubuntu 22.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.
|
||||
See [Installing GTK from packages](https://www.gtk.org/docs/installations/linux#installing-gtk-from-packages).
|
||||
It is important to install the developing package (libgtk-4-dev).
|
||||
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.
|
||||
|
||||
|Distribution| version |GTK 4| GNOME 40 |
|
||||
|:----------:|:-------------------------:|:---:|:-------------:|
|
||||
| Fedora | 36 |4.4.2| GNOME 42 |
|
||||
| Ubuntu | 22.04lts | 4.4 |GNOME 41(4.6.2)|
|
||||
| Debian | bookworm(testing) |4.6.5| GNOME 42 |
|
||||
| Arch | rolling release |4.6.5| GNOME 42 |
|
||||
| Gentoo | rolling release |4.6.5| GNOME 42 |
|
||||
| OpenSUSE |Tumbleweed(rolling release)|4.6.5| GNOME 42 |
|
||||
|
||||
If you've installed GTK 4 from the packages, you don't need to read the rest of this section.
|
||||
|Distribution| version | GTK 4 | GNOME |
|
||||
|:----------:|:-------------------------:|:-----:|:--------:|
|
||||
| Fedora | 37 |4.8.2-2| GNOME 43 |
|
||||
| Ubuntu | 22.10 | 4.8.1 |GNOME 43.0|
|
||||
| Debian | bookworm(testing) | 4.8.2 | GNOME 43 |
|
||||
| Arch | rolling release | 4.8.2 | GNOME 43 |
|
||||
| Gentoo | rolling release | 4.8.2 | GNOME 43 |
|
||||
| OpenSUSE |Tumbleweed(rolling release)| 4.8.2 | GNOME 43 |
|
||||
|
||||
## Installation from the source file
|
||||
|
||||
If your operating system doesn't have GTK 4 packages, 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.
|
||||
|
||||
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.
|
||||
If you want to install a developing version of GTK 4, you 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.
|
||||
|
||||
Up: [README.md](../README.md), Prev: [Section 1](sec1.md), Next: [Section 3](sec3.md)
|
||||
|
|
77
gfm/sec3.md
|
@ -6,7 +6,7 @@ Up: [README.md](../README.md), Prev: [Section 2](sec2.md), Next: [Section 4](se
|
|||
|
||||
### 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?
|
||||
Applications are software that runs using libraries, which includes the
|
||||
OS, frameworks and so on.
|
||||
|
@ -30,7 +30,7 @@ The following is the C code representing the scenario above.
|
|||
5 GtkApplication *app;
|
||||
6 int stat;
|
||||
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);
|
||||
10 g_object_unref (app);
|
||||
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 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 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.
|
||||
|
@ -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.
|
||||
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.
|
||||
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.
|
||||
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
|
||||
simply handler, then the function is invoked when the signal emits.
|
||||
|
||||
|
@ -93,7 +104,7 @@ The flow is like this:
|
|||
|
||||
1. Something happens.
|
||||
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.
|
||||
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;
|
||||
11 int stat;
|
||||
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);
|
||||
15 stat =g_application_run (G_APPLICATION (app), argc, argv);
|
||||
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.
|
||||
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`.
|
||||
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`.
|
||||
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.
|
||||
For example, "activate" signal is in GApplication section in [GIO API Reference](https://docs.gtk.org/gio/signal.Application.activate.html).
|
||||
The handler function is described in it.
|
||||
It is described in the [GObject API Reference](https://docs.gtk.org/gobject/func.signal_connect.html).
|
||||
Correctly, `g_signal_connect` is a macro (not a C function).
|
||||
|
||||
~~~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.
|
||||
You should see and understand it to write Gtk applications.
|
||||
They are located in ['GTK Documentation'](https://docs.gtk.org/).
|
||||
You should see and understand 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.
|
||||
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
|
||||
(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
|
||||
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.
|
||||
|
||||
|
@ -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));
|
||||
~~~
|
||||
|
||||
You need to cast `win` to GtkWindow and `app` to GtkApplication.
|
||||
`GTK_WINDOW` and `GTK_APPLICATION` macro is appropriate for that.
|
||||
You need to cast `win` to GtkWindow and `app` to GtkApplication with `GTK_WINDOW` and `GTK_APPLICATION` macro.
|
||||
|
||||
GtkApplication continues to run until the related window is destroyed.
|
||||
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.
|
||||
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
|
||||
|
@ -301,8 +338,8 @@ Click on the close button then the window disappears and the program finishes.
|
|||
### GtkApplicationWindow
|
||||
|
||||
GtkApplicationWindow is a child object of GtkWindow.
|
||||
It has some extra functionality for better integration with GtkApplication.
|
||||
It is recommended to use it instead of GtkWindow when you use GtkApplication.
|
||||
It has some extra feature for better integration with 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.
|
||||
|
||||
|
|
235
gfm/sec4.md
|
@ -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)
|
||||
|
||||
|
@ -15,7 +15,7 @@ It is a widget with text in it.
|
|||
1 #include <gtk/gtk.h>
|
||||
2
|
||||
3 static void
|
||||
4 app_activate (GApplication *app, gpointer user_data) {
|
||||
4 app_activate (GApplication *app) {
|
||||
5 GtkWidget *win;
|
||||
6 GtkWidget *lab;
|
||||
7
|
||||
|
@ -34,7 +34,7 @@ It is a widget with text in it.
|
|||
20 GtkApplication *app;
|
||||
21 int stat;
|
||||
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);
|
||||
25 stat =g_application_run (G_APPLICATION (app), argc, argv);
|
||||
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
|
||||
4c4
|
||||
< app_activate (GApplication *app, gpointer user_data) {
|
||||
---
|
||||
> app_activate (GApplication *app) {
|
||||
5a6
|
||||
> GtkWidget *lab;
|
||||
8c9
|
||||
|
@ -70,13 +74,15 @@ $ cd misc; diff pr4.c lb1.c
|
|||
> gtk_window_set_child (GTK_WINDOW (win), lab);
|
||||
>
|
||||
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:
|
||||
|
||||
- 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 title of the window is changed.
|
||||
- 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
|
||||
|
||||
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.
|
||||
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.
|
||||
The following program shows how to catch the signal and do something.
|
||||
|
||||
~~~C
|
||||
1 #include <gtk/gtk.h>
|
||||
2
|
||||
3 static void
|
||||
4 click_cb (GtkButton *btn, gpointer user_data) {
|
||||
4 click_cb (GtkButton *btn) {
|
||||
5 g_print ("Clicked.\n");
|
||||
6 }
|
||||
7
|
||||
8 static void
|
||||
9 app_activate (GApplication *app, gpointer user_data) {
|
||||
9 app_activate (GApplication *app) {
|
||||
10 GtkWidget *win;
|
||||
11 GtkWidget *btn;
|
||||
12
|
||||
|
@ -133,7 +139,7 @@ The following program shows how to catch the signal to then do something.
|
|||
26 GtkApplication *app;
|
||||
27 int stat;
|
||||
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);
|
||||
31 stat =g_application_run (G_APPLICATION (app), argc, argv);
|
||||
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.
|
||||
First, it creates a GtkButton instance `btn` with a label "Click me".
|
||||
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.
|
||||
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'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.
|
||||
The following code is `lb3.c`.
|
||||
|
||||
~~~C
|
||||
1 static void
|
||||
2 click_cb (GtkButton *btn, gpointer user_data) {
|
||||
3 GtkWindow *win = GTK_WINDOW (user_data);
|
||||
4 gtk_window_destroy (win);
|
||||
5 }
|
||||
6
|
||||
7 static void
|
||||
8 app_activate (GApplication *app, gpointer user_data) {
|
||||
9 GtkWidget *win;
|
||||
10 GtkWidget *btn;
|
||||
11
|
||||
12 win = gtk_application_window_new (GTK_APPLICATION (app));
|
||||
13 gtk_window_set_title (GTK_WINDOW (win), "lb3");
|
||||
14 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300);
|
||||
15
|
||||
16 btn = gtk_button_new_with_label ("Quit");
|
||||
17 gtk_window_set_child (GTK_WINDOW (win), btn);
|
||||
18 g_signal_connect (btn, "clicked", G_CALLBACK (click_cb), win);
|
||||
19
|
||||
20 gtk_widget_show (win);
|
||||
21 }
|
||||
2 click_cb (GtkButton *btn, GtkWindow *win) {
|
||||
3 gtk_window_destroy (win);
|
||||
4 }
|
||||
5
|
||||
6 static void
|
||||
7 app_activate (GApplication *app) {
|
||||
8 GtkWidget *win;
|
||||
9 GtkWidget *btn;
|
||||
10
|
||||
11 win = gtk_application_window_new (GTK_APPLICATION (app));
|
||||
12 gtk_window_set_title (GTK_WINDOW (win), "lb3");
|
||||
13 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300);
|
||||
14
|
||||
15 btn = gtk_button_new_with_label ("Close");
|
||||
16 gtk_window_set_child (GTK_WINDOW (win), btn);
|
||||
17 g_signal_connect (btn, "clicked", G_CALLBACK (click_cb), win);
|
||||
18
|
||||
19 gtk_widget_show (win);
|
||||
20 }
|
||||
~~~
|
||||
|
||||
And the difference between `lb2.c` and `lb3.c` is as follows.
|
||||
|
||||
~~~
|
||||
$ cd misc; diff lb2.c lb3.c
|
||||
5c5,6
|
||||
4,5c4,5
|
||||
< click_cb (GtkButton *btn) {
|
||||
< g_print ("Clicked.\n");
|
||||
---
|
||||
> GtkWindow *win = GTK_WINDOW (user_data);
|
||||
> click_cb (GtkButton *btn, GtkWindow *win) {
|
||||
> gtk_window_destroy (win);
|
||||
14c15
|
||||
14c14
|
||||
< gtk_window_set_title (GTK_WINDOW (win), "lb2");
|
||||
---
|
||||
> gtk_window_set_title (GTK_WINDOW (win), "lb3");
|
||||
17c18
|
||||
17c17
|
||||
< btn = gtk_button_new_with_label ("Click me");
|
||||
---
|
||||
> btn = gtk_button_new_with_label ("Quit");
|
||||
19c20
|
||||
> btn = gtk_button_new_with_label ("Close");
|
||||
19c19
|
||||
< g_signal_connect (btn, "clicked", G_CALLBACK (click_cb), NULL);
|
||||
---
|
||||
> g_signal_connect (btn, "clicked", G_CALLBACK (click_cb), win);
|
||||
29c30
|
||||
< app = gtk_application_new ("com.github.ToshioCP.lb2", G_APPLICATION_FLAGS_NONE);
|
||||
29c29
|
||||
< app = gtk_application_new ("com.github.ToshioCP.lb2", G_APPLICATION_DEFAULT_FLAGS);
|
||||
---
|
||||
> app = gtk_application_new ("com.github.ToshioCP.lb3", G_APPLICATION_FLAGS_NONE);
|
||||
35d35
|
||||
> app = gtk_application_new ("com.github.ToshioCP.lb3", G_APPLICATION_DEFAULT_FLAGS);
|
||||
35d34
|
||||
<
|
||||
~~~
|
||||
|
||||
The changes are:
|
||||
|
||||
- The function `g_print` in `lb2.c` was deleted and the two lines above are inserted instead.
|
||||
- The label of `btn` is changed from "Click me" to "Quit".
|
||||
- The function `g_print` in `lb2.c` was deleted and two lines are inserted.
|
||||
- `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 most important change is the fourth argument of `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).
|
||||
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.
|
||||
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`](https://docs.gtk.org/gobject/func.signal_connect.html).
|
||||
|
||||
### GtkBox
|
||||
|
||||
|
@ -245,15 +255,14 @@ After this, the Widgets are connected as the following diagram.
|
|||
|
||||
![Parent-child relationship](../image/box.png)
|
||||
|
||||
The program `lb4.c` includes these widgets.
|
||||
It is as follows.
|
||||
The program `lb4.c` is as follows.
|
||||
|
||||
~~~C
|
||||
1 #include <gtk/gtk.h>
|
||||
2
|
||||
3 static void
|
||||
4 click1_cb (GtkButton *btn, gpointer user_data) {
|
||||
5 const gchar *s;
|
||||
4 click1_cb (GtkButton *btn) {
|
||||
5 const char *s;
|
||||
6
|
||||
7 s = gtk_button_get_label (btn);
|
||||
8 if (g_strcmp0 (s, "Hello.") == 0)
|
||||
|
@ -263,68 +272,96 @@ It is as follows.
|
|||
12 }
|
||||
13
|
||||
14 static void
|
||||
15 click2_cb (GtkButton *btn, gpointer user_data) {
|
||||
16 GtkWindow *win = GTK_WINDOW (user_data);
|
||||
17 gtk_window_destroy (win);
|
||||
18 }
|
||||
19
|
||||
20 static void
|
||||
21 app_activate (GApplication *app, gpointer user_data) {
|
||||
22 GtkWidget *win;
|
||||
23 GtkWidget *box;
|
||||
24 GtkWidget *btn1;
|
||||
25 GtkWidget *btn2;
|
||||
26
|
||||
27 win = gtk_application_window_new (GTK_APPLICATION (app));
|
||||
28 gtk_window_set_title (GTK_WINDOW (win), "lb4");
|
||||
29 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300);
|
||||
30
|
||||
31 box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
|
||||
32 gtk_box_set_homogeneous (GTK_BOX (box), TRUE);
|
||||
33 gtk_window_set_child (GTK_WINDOW (win), box);
|
||||
34
|
||||
35 btn1 = gtk_button_new_with_label ("Hello.");
|
||||
36 g_signal_connect (btn1, "clicked", G_CALLBACK (click1_cb), NULL);
|
||||
37
|
||||
38 btn2 = gtk_button_new_with_label ("Quit");
|
||||
39 g_signal_connect (btn2, "clicked", G_CALLBACK (click2_cb), win);
|
||||
40
|
||||
41 gtk_box_append (GTK_BOX (box), btn1);
|
||||
42 gtk_box_append (GTK_BOX (box), btn2);
|
||||
43
|
||||
44 gtk_widget_show (win);
|
||||
45 }
|
||||
46
|
||||
47 int
|
||||
48 main (int argc, char **argv) {
|
||||
49 GtkApplication *app;
|
||||
50 int stat;
|
||||
51
|
||||
52 app = gtk_application_new ("com.github.ToshioCP.lb4", G_APPLICATION_FLAGS_NONE);
|
||||
53 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
|
||||
54 stat =g_application_run (G_APPLICATION (app), argc, argv);
|
||||
55 g_object_unref (app);
|
||||
56 return stat;
|
||||
57 }
|
||||
15 click2_cb (GtkButton *btn, GtkWindow *win) {
|
||||
16 gtk_window_destroy (win);
|
||||
17 }
|
||||
18
|
||||
19 static void
|
||||
20 app_activate (GApplication *app) {
|
||||
21 GtkWidget *win;
|
||||
22 GtkWidget *box;
|
||||
23 GtkWidget *btn1;
|
||||
24 GtkWidget *btn2;
|
||||
25
|
||||
26 win = gtk_application_window_new (GTK_APPLICATION (app));
|
||||
27 gtk_window_set_title (GTK_WINDOW (win), "lb4");
|
||||
28 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300);
|
||||
29
|
||||
30 box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
|
||||
31 gtk_box_set_homogeneous (GTK_BOX (box), TRUE);
|
||||
32 gtk_window_set_child (GTK_WINDOW (win), box);
|
||||
33
|
||||
34 btn1 = gtk_button_new_with_label ("Hello.");
|
||||
35 g_signal_connect (btn1, "clicked", G_CALLBACK (click1_cb), NULL);
|
||||
36
|
||||
37 btn2 = gtk_button_new_with_label ("Close");
|
||||
38 g_signal_connect (btn2, "clicked", G_CALLBACK (click2_cb), win);
|
||||
39
|
||||
40 gtk_box_append (GTK_BOX (box), btn1);
|
||||
41 gtk_box_append (GTK_BOX (box), btn2);
|
||||
42
|
||||
43 gtk_widget_show (win);
|
||||
44 }
|
||||
45
|
||||
46 int
|
||||
47 main (int argc, char **argv) {
|
||||
48 GtkApplication *app;
|
||||
49 int stat;
|
||||
50
|
||||
51 app = gtk_application_new ("com.github.ToshioCP.lb4", G_APPLICATION_DEFAULT_FLAGS);
|
||||
52 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
|
||||
53 stat =g_application_run (G_APPLICATION (app), argc, argv);
|
||||
54 g_object_unref (app);
|
||||
55 return stat;
|
||||
56 }
|
||||
~~~
|
||||
|
||||
Look at the function `app_activate`.
|
||||
|
||||
After the creation of a GtkApplicationWindow instance, a GtkBox instance is created.
|
||||
|
||||
box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
|
||||
gtk_box_set_homogeneous (GTK_BOX (box), TRUE);
|
||||
~~~C
|
||||
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 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 unit of the length is pixel.
|
||||
|
||||
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.
|
||||
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)
|
||||
|
||||
The handler corresponds to `btn1` toggles its label.
|
||||
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)
|
||||
|
|
49
gfm/sec5.md
|
@ -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)
|
||||
|
||||
|
@ -14,7 +14,7 @@ See the sample program `tfv1.c` below.
|
|||
1 #include <gtk/gtk.h>
|
||||
2
|
||||
3 static void
|
||||
4 app_activate (GApplication *app, gpointer user_data) {
|
||||
4 app_activate (GApplication *app) {
|
||||
5 GtkWidget *win;
|
||||
6 GtkWidget *tv;
|
||||
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. "
|
||||
16 "The girl was shining faintly. "
|
||||
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. "
|
||||
20 ;
|
||||
21 win = gtk_application_window_new (GTK_APPLICATION (app));
|
||||
|
@ -50,7 +50,7 @@ See the sample program `tfv1.c` below.
|
|||
37 GtkApplication *app;
|
||||
38 int stat;
|
||||
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);
|
||||
42 stat = g_application_run (G_APPLICATION (app), argc, argv);
|
||||
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`.
|
||||
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 `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.
|
||||
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.
|
||||
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.
|
||||
|
||||
Now compile and run it.
|
||||
|
||||
```
|
||||
$ cd src/tfv
|
||||
$ comp tfv1
|
||||
$ ./a.out
|
||||
```
|
||||
|
||||
![GtkTextView](../image/screenshot_tfv1.png)
|
||||
|
||||
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.
|
||||
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
|
||||
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.
|
||||
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.
|
||||
|
||||
### GtkScrolledWindow
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
|
@ -106,18 +116,18 @@ $ cd tfv; diff tfv1.c tfv2.c
|
|||
---
|
||||
> gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);
|
||||
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
|
||||
1 #include <gtk/gtk.h>
|
||||
2
|
||||
3 static void
|
||||
4 app_activate (GApplication *app, gpointer user_data) {
|
||||
4 app_activate (GApplication *app) {
|
||||
5 GtkWidget *win;
|
||||
6 GtkWidget *scr;
|
||||
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. "
|
||||
17 "The girl was shining faintly. "
|
||||
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. "
|
||||
21 ;
|
||||
22 win = gtk_application_window_new (GTK_APPLICATION (app));
|
||||
|
@ -157,7 +167,7 @@ Here is the complete code of `tfv2.c`.
|
|||
41 GtkApplication *app;
|
||||
42 int stat;
|
||||
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);
|
||||
46 stat = g_application_run (G_APPLICATION (app), argc, argv);
|
||||
47 g_object_unref (app);
|
||||
|
@ -166,7 +176,8 @@ Here is the complete code of `tfv2.c`.
|
|||
~~~
|
||||
|
||||
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)
|
||||
|
|
175
gfm/sec6.md
|
@ -1,6 +1,6 @@
|
|||
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.
|
||||
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
|
||||
|
||||
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 as a pointer to a character array. They behaves like a string type
|
||||
which you may be familiar from other languages.
|
||||
So, this pointer is often called 'a string'.
|
||||
String is not a C type such as char, int, float or double, but a character array.
|
||||
It behaves like a string in other languages.
|
||||
So, the 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
|
||||
char a[10], *b;
|
||||
|
@ -30,50 +29,50 @@ b = a;
|
|||
/* *(++b) is 'e' */
|
||||
~~~
|
||||
|
||||
The array `a` has `char` elements and the size of ten.
|
||||
The first six elements are 'H', 'e', 'l', 'l', 'o' and '\0'.
|
||||
This array represents the string "Hello".
|
||||
The first five elements are character codes that correspond to the characters.
|
||||
The sixth element is '\0', which is the same as zero,
|
||||
and indicates that the string ends there.
|
||||
The size of the array is 10, so 4 bytes aren't used, but that's OK,
|
||||
they are just ignored.
|
||||
An array `a` is defined as a `char` type array and its size is ten.
|
||||
The first five elements are 'H', 'e', 'l', 'l', 'o'.
|
||||
They are character codes.
|
||||
For example, 'H' is the same as 0x48 or 72.
|
||||
The sixth element is '\0', which is the same as zero, and indicates that the sequence of the data ends there.
|
||||
The array represents the string "Hello".
|
||||
|
||||
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.
|
||||
Because `b` is assigned to be `a`, `a` and `b` point the same character ('H').
|
||||
The variable `a` is defined as an array and it can't be changed.
|
||||
It always point the top address of the array.
|
||||
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`.
|
||||
It is assigned with `a`, so `b` points the first element of `a` (character 'H').
|
||||
The array `a` is immutable.
|
||||
So `a=a+1` causes syntax error.
|
||||
|
||||
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.
|
||||
A NULL string on the other hand will be a pointer which points to a location
|
||||
that contains `\0`, which is a string of length 0 (or "").
|
||||
Programs that use strings will include bugs if you aren't careful when using NULL pointers.
|
||||
It is different from empty string.
|
||||
Empty string is a pointer points `\0`.
|
||||
|
||||
Another annoying problem is the memory that a string is allocated.
|
||||
There are four cases:
|
||||
|
||||
- The string is read only;
|
||||
- The string is in static memory area;
|
||||
- The string is in stack; and
|
||||
- The string is in memory allocated from the heap area.
|
||||
- The string is read only
|
||||
- The string is in static memory area
|
||||
- The string is in stack
|
||||
- The string is in memory allocated from the heap area
|
||||
|
||||
## 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
|
||||
char *s;
|
||||
s = "Hello"
|
||||
~~~
|
||||
|
||||
"Hello" is a string literal, and is stored in program memory.
|
||||
A string literal is read only.
|
||||
In the program above, `s` points the string literal.
|
||||
|
||||
"Hello" is a string literal, and is read only.
|
||||
So, the following program is illegal.
|
||||
|
||||
~~~C
|
||||
|
@ -83,25 +82,38 @@ So, the following program is illegal.
|
|||
The result is undefined.
|
||||
Probably a bad thing will happen, for example, a segmentation fault.
|
||||
|
||||
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 your program
|
||||
by using the `string` command.
|
||||
NOTE: The memory of the literal string is allocated when the program is compiled.
|
||||
It is possible to see the literal strings with `strings` 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
|
||||
|
||||
If a string is defined as an array, it's in either stored in the static memory area or stack.
|
||||
This depends on the class of the array.
|
||||
If a string is defined as an array, it's stored in static memory area or stack.
|
||||
It depends on the class of the array.
|
||||
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.
|
||||
This area can be changed and is writable.
|
||||
The allocated memory lives for the life of the program.
|
||||
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`.
|
||||
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.
|
||||
|
||||
~~~C
|
||||
|
||||
static char a[] = {'H', 'e', 'l', 'l', 'o', '\0'};
|
||||
|
||||
void
|
||||
|
@ -116,30 +128,28 @@ print_strings (void) {
|
|||
}
|
||||
~~~
|
||||
|
||||
The array `a` is defined externally to a function and is global in its scope.
|
||||
Such variables are placed in 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),
|
||||
and then creates code that allocates six bytes in the static memory area and copies the data to this memory.
|
||||
The array `a` is defined out of functions.
|
||||
It is placed in the static memory area even if the `static` class is left out.
|
||||
The compiler calculates the number of the elements (six) and allocates six bytes in the static memory area.
|
||||
Then, it copies "Hello" literal string data to the memory.
|
||||
|
||||
The array `b` is defined inside the function
|
||||
so its class is `auto`.
|
||||
The array `b` is defined inside the function, so its class is `auto`.
|
||||
The compiler calculates the number of the elements in the string literal.
|
||||
It has six elements as the zero termination character is also included.
|
||||
The compiler creates code which allocates six bytes memory in the stack and copies the data to the memory.
|
||||
It is six because it has '\0' terminator.
|
||||
The compiler allocates six bytes in the stack and copies "Hello" litaral string to the stack memory.
|
||||
|
||||
Both `a` and `b` are writable.
|
||||
|
||||
The memory is managed by the executable program.
|
||||
You don't need your program to allocate or free the memory for `a` and `b`.
|
||||
The array `a` is created then the program is first run and remains for the life of the program.
|
||||
The array `b` is created on the stack then the function is called, disappears when the function returns.
|
||||
The memory is allocated and freed by the program automatically so you don't need to allocate or free.
|
||||
The array `a` is alive during the program's life time.
|
||||
The array `b` is alive when the function is called until the function returns to the caller.
|
||||
|
||||
## 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.
|
||||
GLib provides the functions `g_new` and `g_free` to do the same thing, with support for
|
||||
some additional GLib functionality.
|
||||
GLib provides the functions `g_new` and `g_free`.
|
||||
They are similar to `malloc` and `free`.
|
||||
|
||||
~~~C
|
||||
g_new (struct_type, n_struct)
|
||||
|
@ -197,9 +207,9 @@ s = g_strdup ("Hello");
|
|||
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.
|
||||
`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_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 function reference will describe if the returned value needs to be freed.
|
||||
If you forget to free the allocated memory it will remain allocated. Repeated use will cause
|
||||
more memory to be allocated to the program, which will grow over time. This is called a memory leak,
|
||||
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.
|
||||
If you forget to free the allocated memory it will remain until the program ends.
|
||||
Repeated allocation and no freeing cause memory leak.
|
||||
It is a bug and may bring a serious problem.
|
||||
|
||||
Some GLib functions return a string which mustn't be freed by the caller.
|
||||
## const qualifier
|
||||
|
||||
~~~C
|
||||
const char *
|
||||
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.
|
||||
A `const` qualified variable can be assigned to initialize it.
|
||||
Once it is initialized, it is never allowed to change or free.
|
||||
|
||||
~~~C
|
||||
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 */
|
||||
~~~
|
||||
|
||||
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)
|
||||
|
|
344
gfm/sec7.md
|
@ -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)
|
||||
|
||||
|
@ -6,51 +6,40 @@ Up: [Readme.md](../Readme.md), Prev: [Section 6](sec6.md), Next: [Section 8](se
|
|||
|
||||
### G\_APPLICATION\_HANDLES\_OPEN flag
|
||||
|
||||
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, we'll take the easiest one.
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
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.
|
||||
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
|
||||
GtkApplication *
|
||||
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.
|
||||
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).
|
||||
|
||||
~~~
|
||||
GApplicationFlags' Members
|
||||
For further information, see [GIO API Reference -- ApplicationFlags](https://docs.gtk.org/gio/flags.ApplicationFlags.html) and
|
||||
[GIO API Reference -- g\_application\_run](https://docs.gtk.org/gio/method.Application.run.html).
|
||||
|
||||
G_APPLICATION_FLAGS_NONE Default. (No argument allowed)
|
||||
... ... ...
|
||||
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.
|
||||
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.
|
||||
|
||||
The flag `G_APPLICATION_HANDLES_OPEN` 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.
|
||||
The application assumes all the arguments are filenames.
|
||||
|
||||
~~~C
|
||||
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
|
||||
|
||||
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.
|
||||
- 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.
|
||||
|
||||
~~~C
|
||||
void user_function (GApplication *application,
|
||||
gpointer files,
|
||||
gint n_files,
|
||||
gchar *hint,
|
||||
gpointer user_data)
|
||||
void
|
||||
open (
|
||||
GApplication* self,
|
||||
gpointer files,
|
||||
gint n_files,
|
||||
gchar* hint,
|
||||
gpointer user_data
|
||||
)
|
||||
~~~
|
||||
|
||||
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]
|
||||
- `n_files` --- the number of the elements of `files`
|
||||
- `hint` --- a hint provided by the calling instance (usually it can be ignored)
|
||||
- `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
|
||||
|
||||
### 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.
|
||||
|
||||
- When arguments are given, it treats 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.
|
||||
- If it fails to open the file, it will show an error message and quit.
|
||||
- If there's no argument, it will shows an error message and quit.
|
||||
- If there are two or more arguments, the second one and any others are ignored.
|
||||
- When arguments are given, it recognizes the first argument as a filename and opens it.
|
||||
- The second argument and after are ignored.
|
||||
- If there's no argument, it 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 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
|
||||
1 #include <gtk/gtk.h>
|
||||
2
|
||||
3 static void
|
||||
4 app_activate (GApplication *app, gpointer user_data) {
|
||||
5 g_print ("You need a filename argument.\n");
|
||||
4 app_activate (GApplication *app) {
|
||||
5 g_printerr ("You need a filename argument.\n");
|
||||
6 }
|
||||
7
|
||||
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;
|
||||
11 GtkWidget *scr;
|
||||
12 GtkWidget *tv;
|
||||
|
@ -138,25 +128,26 @@ The program which does this is shown below.
|
|||
37 gtk_widget_show (win);
|
||||
38 } else {
|
||||
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);
|
||||
42 }
|
||||
43 gtk_window_destroy (GTK_WINDOW (win));
|
||||
44 }
|
||||
45 }
|
||||
46
|
||||
47 int
|
||||
48 main (int argc, char **argv) {
|
||||
49 GtkApplication *app;
|
||||
50 int stat;
|
||||
51
|
||||
52 app = gtk_application_new ("com.github.ToshioCP.tfv3", G_APPLICATION_HANDLES_OPEN);
|
||||
53 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
|
||||
54 g_signal_connect (app, "open", G_CALLBACK (app_open), NULL);
|
||||
55 stat = g_application_run (G_APPLICATION (app), argc, argv);
|
||||
56 g_object_unref (app);
|
||||
57 return stat;
|
||||
58 }
|
||||
42 } else
|
||||
43 g_printerr ("File can't be opened.\n");
|
||||
44 gtk_window_destroy (GTK_WINDOW (win));
|
||||
45 }
|
||||
46 }
|
||||
47
|
||||
48 int
|
||||
49 main (int argc, char **argv) {
|
||||
50 GtkApplication *app;
|
||||
51 int stat;
|
||||
52
|
||||
53 app = gtk_application_new ("com.github.ToshioCP.tfv3", G_APPLICATION_HANDLES_OPEN);
|
||||
54 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
|
||||
55 g_signal_connect (app, "open", G_CALLBACK (app_open), NULL);
|
||||
56 stat = g_application_run (G_APPLICATION (app), argc, argv);
|
||||
57 g_object_unref (app);
|
||||
58 return stat;
|
||||
59 }
|
||||
~~~
|
||||
|
||||
Save it as `tfv3.c`.
|
||||
|
@ -167,25 +158,29 @@ Then compile and run it.
|
|||
|
||||
![File viewer](../image/screenshot_tfv3.png)
|
||||
|
||||
Let's explain how the program `tfv3.c` works.
|
||||
First, the function `main` has only two changes from the previous version.
|
||||
The function `main` has only two changes from the previous version.
|
||||
|
||||
- `G_APPLICATION_FLAGS_NONE` is replaced by `G_APPLICATION_HANDLES_OPEN`; and
|
||||
- `g_signal_connect (app, "open", G_CALLBACK (on_open), NULL)` is added.
|
||||
- `G_APPLICATION_DEFAULT_FLAGS` is replaced by `G_APPLICATION_HANDLES_OPEN`
|
||||
- `g_signal_connect (app, "open", G_CALLBACK (app_open), NULL)` is added.
|
||||
|
||||
Next, the handler `app_activate` is added and is very simple.
|
||||
It just outputs the error message and
|
||||
the application quits immediately because no window is created.
|
||||
When the flag `G_APPLICATION_HANDLES_OPEN` is given to `gtk_application_new` function, the application behaves like this:
|
||||
|
||||
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;
|
||||
- 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 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 handler `app_activate` becomes very simple.
|
||||
It just outputs the error message and return to the caller.
|
||||
Then the application quits immediately because no window is created.
|
||||
|
||||
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
|
||||
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);
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
} else
|
||||
g_printerr ("File can't be opened.\n");
|
||||
gtk_window_destroy (GTK_WINDOW (win));
|
||||
}
|
||||
~~~
|
||||
|
||||
The function `g_file_load_contents` loads the file contents into a buffer,
|
||||
which is automatically allocated and sets `contents` to point that buffer.
|
||||
The length of the buffer is set to `length`.
|
||||
It returns `TRUE` if the file's contents are successfully loaded and `FALSE` if an error occurs.
|
||||
The function `g_file_load_contents` loads the file contents into a temporary buffer,
|
||||
which is automatically allocated and sets `contents` to point the buffer.
|
||||
The length of the buffer is assigned to `length`.
|
||||
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,
|
||||
frees the buffer pointed by `contents`, sets the title of the window,
|
||||
If it has successfully read the file, it inserts the contents into GtkTextBuffer,
|
||||
frees the temporary buffer pointed by `contents`, sets the title of 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 is a container widget that uses tabs and contains multiple children.
|
||||
The child that is displayed depends on which tab has been selected.
|
||||
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.
|
||||
|
||||
![GtkNotebook](../image/screenshot_gtk_notebook.png)
|
||||
|
||||
Looking at the screenshots above,
|
||||
the left one is the window at the startup.
|
||||
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` are shown instead.
|
||||
This is shown in the right screenshot.
|
||||
The left image is the window at the startup.
|
||||
The file `pr1.c` is shown and its filename is in the left tab.
|
||||
After clicking on the right tab, the contents of the file `tfv1.c` is shown.
|
||||
The right image is the screenshot.
|
||||
|
||||
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 `tfv4.c` and is:
|
||||
The following is `tfv4.c`.
|
||||
It has GtkNoteBook widget.
|
||||
It is inserted as a child of GtkApplicationWindow and contains multiple GtkScrolledWindow.
|
||||
|
||||
~~~C
|
||||
1 #include <gtk/gtk.h>
|
||||
2
|
||||
3 static void
|
||||
4 app_activate (GApplication *app, gpointer user_data) {
|
||||
5 g_print ("You need a filename argument.\n");
|
||||
4 app_activate (GApplication *app) {
|
||||
5 g_printerr ("You need a filename argument.\n");
|
||||
6 }
|
||||
7
|
||||
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;
|
||||
11 GtkWidget *nb;
|
||||
12 GtkWidget *lab;
|
||||
|
@ -256,90 +253,81 @@ The code to do this is given in `tfv4.c` and is:
|
|||
21
|
||||
22 win = gtk_application_window_new (GTK_APPLICATION (app));
|
||||
23 gtk_window_set_title (GTK_WINDOW (win), "file viewer");
|
||||
24 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300);
|
||||
25 gtk_window_maximize (GTK_WINDOW (win));
|
||||
26
|
||||
27 nb = gtk_notebook_new ();
|
||||
28 gtk_window_set_child (GTK_WINDOW (win), nb);
|
||||
29
|
||||
30 for (i = 0; i < n_files; i++) {
|
||||
31 if (g_file_load_contents (files[i], NULL, &contents, &length, NULL, NULL)) {
|
||||
32 scr = gtk_scrolled_window_new ();
|
||||
33 tv = gtk_text_view_new ();
|
||||
34 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
||||
35 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR);
|
||||
36 gtk_text_view_set_editable (GTK_TEXT_VIEW (tv), FALSE);
|
||||
37 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);
|
||||
38
|
||||
39 gtk_text_buffer_set_text (tb, contents, length);
|
||||
40 g_free (contents);
|
||||
41 if ((filename = g_file_get_basename (files[i])) != NULL) {
|
||||
42 lab = gtk_label_new (filename);
|
||||
43 g_free (filename);
|
||||
44 } else
|
||||
45 lab = gtk_label_new ("");
|
||||
46 gtk_notebook_append_page (GTK_NOTEBOOK (nb), scr, lab);
|
||||
47 nbp = gtk_notebook_get_page (GTK_NOTEBOOK (nb), scr);
|
||||
48 g_object_set (nbp, "tab-expand", TRUE, NULL);
|
||||
49 } else if ((filename = g_file_get_path (files[i])) != NULL) {
|
||||
50 g_print ("No such file: %s.\n", filename);
|
||||
51 g_free (filename);
|
||||
52 } else
|
||||
53 g_print ("No valid file is given\n");
|
||||
54 }
|
||||
55 if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb)) > 0)
|
||||
56 gtk_widget_show (win);
|
||||
57 else
|
||||
58 gtk_window_destroy (GTK_WINDOW (win));
|
||||
59 }
|
||||
60
|
||||
61 int
|
||||
62 main (int argc, char **argv) {
|
||||
63 GtkApplication *app;
|
||||
64 int stat;
|
||||
65
|
||||
66 app = gtk_application_new ("com.github.ToshioCP.tfv4", G_APPLICATION_HANDLES_OPEN);
|
||||
67 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
|
||||
68 g_signal_connect (app, "open", G_CALLBACK (app_open), NULL);
|
||||
69 stat = g_application_run (G_APPLICATION (app), argc, argv);
|
||||
70 g_object_unref (app);
|
||||
71 return stat;
|
||||
72 }
|
||||
24 gtk_window_set_default_size (GTK_WINDOW (win), 600, 400);
|
||||
25
|
||||
26 nb = gtk_notebook_new ();
|
||||
27 gtk_window_set_child (GTK_WINDOW (win), nb);
|
||||
28
|
||||
29 for (i = 0; i < n_files; i++) {
|
||||
30 if (g_file_load_contents (files[i], NULL, &contents, &length, NULL, NULL)) {
|
||||
31 scr = gtk_scrolled_window_new ();
|
||||
32 tv = gtk_text_view_new ();
|
||||
33 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_editable (GTK_TEXT_VIEW (tv), FALSE);
|
||||
36 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);
|
||||
37
|
||||
38 gtk_text_buffer_set_text (tb, contents, length);
|
||||
39 g_free (contents);
|
||||
40 if ((filename = g_file_get_basename (files[i])) != NULL) {
|
||||
41 lab = gtk_label_new (filename);
|
||||
42 g_free (filename);
|
||||
43 } else
|
||||
44 lab = gtk_label_new ("");
|
||||
45 gtk_notebook_append_page (GTK_NOTEBOOK (nb), scr, lab);
|
||||
46 nbp = gtk_notebook_get_page (GTK_NOTEBOOK (nb), scr);
|
||||
47 g_object_set (nbp, "tab-expand", TRUE, NULL);
|
||||
48 } else if ((filename = g_file_get_path (files[i])) != NULL) {
|
||||
49 g_printerr ("No such file: %s.\n", filename);
|
||||
50 g_free (filename);
|
||||
51 } else
|
||||
52 g_printerr ("No valid file is given\n");
|
||||
53 }
|
||||
54 if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb)) > 0)
|
||||
55 gtk_widget_show (win);
|
||||
56 else
|
||||
57 gtk_window_destroy (GTK_WINDOW (win));
|
||||
58 }
|
||||
59
|
||||
60 int
|
||||
61 main (int argc, char **argv) {
|
||||
62 GtkApplication *app;
|
||||
63 int stat;
|
||||
64
|
||||
65 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, "open", G_CALLBACK (app_open), NULL);
|
||||
68 stat = g_application_run (G_APPLICATION (app), argc, argv);
|
||||
69 g_object_unref (app);
|
||||
70 return stat;
|
||||
71 }
|
||||
~~~
|
||||
|
||||
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.
|
||||
|
||||
- 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".
|
||||
- 25: The size of the window is set to maximum because a big window is appropriate for file viewers.
|
||||
- 27-28 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.
|
||||
- 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.
|
||||
- 39-40 inserts the contents of the file into GtkTextBuffer and frees the memory pointed by `contents`.
|
||||
- 41-43: Gets the filename and creates GtkLabel with the filename and then frees `filename`.
|
||||
- 44-45: If `filename` is NULL, creates GtkLabel with the empty string.
|
||||
- 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:
|
||||
|
||||
~~~
|
||||
GtkNotebook -- GtkNotebookPage -- GtkScrolledWindow
|
||||
~~~
|
||||
|
||||
- 47: Gets GtkNotebookPage widget and sets `nbp` to point to this GtkNotebookPage.
|
||||
- 48: GtkNotebookPage has a property set called "tab-expand".
|
||||
- 24: The default size of the window is 600x400.
|
||||
- 26-27 GtkNotebook is created and inserted to the GtkApplicationWindow as a child.
|
||||
- 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.
|
||||
- 31-36 GtkScrollledWindow, GtkTextView are created. GtkTextBuffer is got from the GtkTextView.
|
||||
The GtkTextView is connected to the GtkScrolledWindow as a child.
|
||||
- 38-39 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..
|
||||
- 43-44: If it fails to take the filename, empty string GtkLabel is created.
|
||||
- 45-46: Appends a GtkScrolledWindow to the GtkNotebook as a child.
|
||||
And the GtkLabel is set as the child's tab.
|
||||
At the same time, a GtkNoteBookPage is created automatically.
|
||||
The function `gtk_notebook_get_page` returns the GtkNotebookPage of the child (GtkScrolledWindow).
|
||||
- 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.
|
||||
`g_object_set` is a general function to set properties in objects.
|
||||
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.
|
||||
- 52-53: If `filename` is NULL, the "No valid file is given" message is outputted.
|
||||
- 55-58: If at least one file was read, then the number of GtkNotebookPage is greater than zero.
|
||||
If it's true, it shows the window.
|
||||
If it's false, it destroys the window, which causes the program to quit.
|
||||
`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).
|
||||
- 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.
|
||||
- 51-52: If `filename` is NULL, the "No valid file is given" message is displayed.
|
||||
- 54-57: If at least one page exists, the window is shown.
|
||||
Otherwise, the window is destroyed and the application quits.
|
||||
|
||||
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)
|
||||
|
|
470
gfm/sec8.md
|
@ -1,62 +1,64 @@
|
|||
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
|
||||
|
||||
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.
|
||||
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
|
||||
write the program from scratch, we just add two things to the file viewer:
|
||||
GtkTextView is a multi-line editor.
|
||||
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.
|
||||
- A function to write the file.
|
||||
- Pointers to GFile instances.
|
||||
- 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
|
||||
- Make a child object, which can extend the instance memory for the GFile object.
|
||||
- Use global variables
|
||||
- Make a child class of GtkTextView and its each instance holds a pointer to the GFile instance.
|
||||
|
||||
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,
|
||||
|
||||
~~~C
|
||||
GFile *f[20];
|
||||
~~~
|
||||
|
||||
The variable `f[i]` corresponds to the file associated to 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.
|
||||
The variable `f[i]` corresponds to the file associated with the i-th GtkNotebookPage.
|
||||
|
||||
Making a child object is a good idea in terms of maintenance.
|
||||
One thing you need to be careful of is the difference between "child object" and "child widget".
|
||||
Here we are describing a "child object".
|
||||
A child object includes, and expands on its parent object, as
|
||||
a child object derives everything from the parent object.
|
||||
However, There are two problems.
|
||||
The first is the size of the array.
|
||||
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.
|
||||
The second is difficulty to maintain the program.
|
||||
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)
|
||||
|
||||
We will define TfeTextView as a child object 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.
|
||||
## How to define a child class of GtkTextView
|
||||
|
||||
In general, this is how GObjects work. Understanding the general theory about Gobject's 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 [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.
|
||||
You need to know GObject system convention.
|
||||
First, look at the program below.
|
||||
|
||||
~~~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.
|
||||
Tfe and TextView.
|
||||
Tfe is called the prefix, namespace or module.
|
||||
TextView is called the object.
|
||||
Tfe is called prefix or namespace.
|
||||
TextView is called object.
|
||||
- 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 ().
|
||||
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.
|
||||
- Next, use 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).
|
||||
This definition is put before G\_DECLARE\_FINAL\_TYPE macro.
|
||||
- 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.
|
||||
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.
|
||||
The second member and after are members of the child object.
|
||||
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).
|
||||
- Define instance init function (tfe\_text\_view\_init).
|
||||
Usually you don't need to do anything.
|
||||
- Define class init function (tfe\_text\_view\_class\_init).
|
||||
You don't need to do anything in this object.
|
||||
- Write function codes you want to add (tfe\_text\_view\_set\_file and tfe\_text\_view\_get\_file).
|
||||
`tv` is a pointer to the TfeTextView object instance which is a C-structure declared with the tag \_TfeTextView.
|
||||
So, the structure has a member `file` as a pointer to a GFile instance.
|
||||
`tv->file = f` is an assignment of `f` to a member `file` of the structure pointed by `tv`.
|
||||
This is an example how to use the extended memory in a child widget.
|
||||
- Write a function to create an instance.
|
||||
This macro is mainly used to register the new class to the type system.
|
||||
Type system is a base system of GObject.
|
||||
Every class has its own type.
|
||||
The types of GObject, GtkWidget and TfeTextView are G\_TYPE\_OBJECT, GTK\_TYPE\_WIDGET and TFE\_TYPE\_TEXT\_VIEW respectively.
|
||||
Such type (for example, TFE\_TYPE\_TEXT\_VIEW) is a macro and it is expanded to a function (tfe\_text\_view\_get\_type()).
|
||||
It returns a integer which is unique among all GObject system classes.
|
||||
- Instance init function (tfe\_text\_view\_init) is called when the instance is created.
|
||||
It is the same as a constructor in other object oriented languages.
|
||||
- Class init function (tfe\_text\_view\_class\_init) is called when the class is created.
|
||||
- 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.
|
||||
If the parent object function needs parameters, this function also need them.
|
||||
You sometimes might want to add some parameters.
|
||||
It's your choice.
|
||||
Use g\_object\_new function to create the instance.
|
||||
The arguments are (prefix)\_TYPE\_(object), a list to initialize properties and NULL.
|
||||
In this code no property needs to be initialized.
|
||||
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.
|
||||
|
||||
This program is not perfect.
|
||||
It has some problems.
|
||||
It will be modified later.
|
||||
This program shows the outline how to define a child class.
|
||||
|
||||
## Close-request signal
|
||||
|
||||
|
@ -156,10 +158,9 @@ After you finish editing, you exit the editor.
|
|||
The editor updates files just before the window closes.
|
||||
|
||||
GtkWindow emits the "close-request" signal before it closes.
|
||||
We connect the signal and the handler `before_close`.
|
||||
A handler is a C function.
|
||||
When a function is connected to a certain signal, we call it a handler.
|
||||
The function `before_close` is invoked when the signal "close-request" is emitted.
|
||||
We will connect the signal and the handler `before_close`.
|
||||
(A handler is a C function which is connected to a signal.)
|
||||
The function `before_close` is called when the signal "close-request" is emitted.
|
||||
|
||||
~~~C
|
||||
g_signal_connect (win, "close-request", G_CALLBACK (before_close), NULL);
|
||||
|
@ -171,59 +172,66 @@ The program of `before_close` is as follows.
|
|||
|
||||
~~~C
|
||||
1 static gboolean
|
||||
2 before_close (GtkWindow *win, gpointer user_data) {
|
||||
3 GtkWidget *nb = GTK_WIDGET (user_data);
|
||||
4 GtkWidget *scr;
|
||||
5 GtkWidget *tv;
|
||||
6 GFile *file;
|
||||
7 char *pathname;
|
||||
8 GtkTextBuffer *tb;
|
||||
9 GtkTextIter start_iter;
|
||||
10 GtkTextIter end_iter;
|
||||
11 char *contents;
|
||||
12 unsigned int n;
|
||||
13 unsigned int i;
|
||||
14
|
||||
15 n = gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb));
|
||||
16 for (i = 0; i < n; ++i) {
|
||||
17 scr = gtk_notebook_get_nth_page (GTK_NOTEBOOK (nb), i);
|
||||
18 tv = gtk_scrolled_window_get_child (GTK_SCROLLED_WINDOW (scr));
|
||||
19 file = tfe_text_view_get_file (TFE_TEXT_VIEW (tv));
|
||||
20 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
||||
21 gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter);
|
||||
22 contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE);
|
||||
23 if (! g_file_replace_contents (file, contents, strlen (contents), NULL, TRUE, G_FILE_CREATE_NONE, NULL, NULL, NULL)) {
|
||||
24 pathname = g_file_get_path (file);
|
||||
25 g_print ("ERROR : Can't save %s.", pathname);
|
||||
26 g_free (pathname);
|
||||
27 }
|
||||
28 g_free (contents);
|
||||
29 }
|
||||
30 return FALSE;
|
||||
31 }
|
||||
2 before_close (GtkWindow *win, GtkWidget *nb) {
|
||||
3 GtkWidget *scr;
|
||||
4 GtkWidget *tv;
|
||||
5 GFile *file;
|
||||
6 char *pathname;
|
||||
7 GtkTextBuffer *tb;
|
||||
8 GtkTextIter start_iter;
|
||||
9 GtkTextIter end_iter;
|
||||
10 char *contents;
|
||||
11 unsigned int n;
|
||||
12 unsigned int i;
|
||||
13
|
||||
14 n = gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb));
|
||||
15 for (i = 0; i < n; ++i) {
|
||||
16 scr = gtk_notebook_get_nth_page (GTK_NOTEBOOK (nb), i);
|
||||
17 tv = gtk_scrolled_window_get_child (GTK_SCROLLED_WINDOW (scr));
|
||||
18 file = tfe_text_view_get_file (TFE_TEXT_VIEW (tv));
|
||||
19 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
||||
20 gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter);
|
||||
21 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 ((pathname = g_file_get_path (file)) != NULL) {
|
||||
24 g_printerr ("Can't save %s.", pathname);
|
||||
25 g_free (pathname);
|
||||
26 } else
|
||||
27 g_printerr ("Pathname not exist.\n");
|
||||
28 }
|
||||
29 g_free (contents);
|
||||
30 g_object_unref (file);
|
||||
31 }
|
||||
32 return FALSE;
|
||||
33 }
|
||||
~~~
|
||||
|
||||
The numbers on the left of items are line numbers in the source code.
|
||||
|
||||
- 15: Gets the number of pages `nb` has.
|
||||
- 16-29: For loop with regard to the index to each pages.
|
||||
- 17-19: Gets GtkScrolledWindow, TfeTextView and a pointer to GFile.
|
||||
The pointer was stored when `app_open` handler had run. It will be shown later.
|
||||
- 20-22: Gets GtkTextBuffer and contents. `start_iter` and `end_iter` are iterators of the buffer.
|
||||
I don't want to explain them now because it would take a lot of time.
|
||||
Just remember these lines for the present.
|
||||
- 23-27: Writes the contents to the file.
|
||||
If it fails, it outputs an error message.
|
||||
- 28: Frees `contents`.
|
||||
- 14: The number of pages of `nb` is assigned to `n`.
|
||||
- 15-31: For loop with regard to the index to each pages.
|
||||
- 16-18: `scr`, `tv` and `file` is assigned pointers to the GtkScrolledWindow, TfeTextView and GFile.
|
||||
The GFile of TfeTextView was stored when `app_open` handler was called. It will be shown later.
|
||||
- 19-21: `tb` is assigned the GtkTextBuffer of the TfeTextView.
|
||||
The buffer is accessed with iterators.
|
||||
Iterators points somewhere in the buffer.
|
||||
The function `gtk_text_buffer_get_bounds` assigns the start and end of the buffer to `start_iter` and `end_iter` respectively.
|
||||
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.
|
||||
- 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
|
||||
|
||||
The following is the complete source code of `tfe1.c`.
|
||||
The following is the whole source code of `tfe1.c`.
|
||||
|
||||
~~~C
|
||||
1 #include <gtk/gtk.h>
|
||||
2
|
||||
3 /* Define TfeTextView Widget which is the child object of GtkTextView */
|
||||
3 /* Define TfeTextView Widget which is the child class of GtkTextView */
|
||||
4
|
||||
5 #define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type ()
|
||||
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
|
||||
16 static void
|
||||
17 tfe_text_view_init (TfeTextView *tv) {
|
||||
18 }
|
||||
19
|
||||
20 static void
|
||||
21 tfe_text_view_class_init (TfeTextViewClass *class) {
|
||||
22 }
|
||||
23
|
||||
24 void
|
||||
25 tfe_text_view_set_file (TfeTextView *tv, GFile *f) {
|
||||
26 tv -> file = f;
|
||||
27 }
|
||||
28
|
||||
29 GFile *
|
||||
30 tfe_text_view_get_file (TfeTextView *tv) {
|
||||
31 return tv -> file;
|
||||
32 }
|
||||
33
|
||||
34 GtkWidget *
|
||||
35 tfe_text_view_new (void) {
|
||||
36 return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL));
|
||||
37 }
|
||||
38
|
||||
39 /* ---------- end of the definition of TfeTextView ---------- */
|
||||
40
|
||||
41 static gboolean
|
||||
42 before_close (GtkWindow *win, gpointer user_data) {
|
||||
43 GtkWidget *nb = GTK_WIDGET (user_data);
|
||||
18 tv->file = NULL;
|
||||
19 }
|
||||
20
|
||||
21 static void
|
||||
22 tfe_text_view_class_init (TfeTextViewClass *class) {
|
||||
23 }
|
||||
24
|
||||
25 void
|
||||
26 tfe_text_view_set_file (TfeTextView *tv, GFile *f) {
|
||||
27 tv->file = f;
|
||||
28 }
|
||||
29
|
||||
30 GFile *
|
||||
31 tfe_text_view_get_file (TfeTextView *tv) {
|
||||
32 return tv -> file;
|
||||
33 }
|
||||
34
|
||||
35 GtkWidget *
|
||||
36 tfe_text_view_new (void) {
|
||||
37 return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL));
|
||||
38 }
|
||||
39
|
||||
40 /* ---------- end of the definition of TfeTextView ---------- */
|
||||
41
|
||||
42 static gboolean
|
||||
43 before_close (GtkWindow *win, GtkWidget *nb) {
|
||||
44 GtkWidget *scr;
|
||||
45 GtkWidget *tv;
|
||||
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);
|
||||
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)) {
|
||||
64 pathname = g_file_get_path (file);
|
||||
65 g_print ("ERROR : Can't save %s.", pathname);
|
||||
66 g_free (pathname);
|
||||
67 }
|
||||
68 g_free (contents);
|
||||
69 }
|
||||
70 return FALSE;
|
||||
71 }
|
||||
72
|
||||
73 static void
|
||||
74 app_activate (GApplication *app, gpointer user_data) {
|
||||
75 g_print ("You need to give filenames as arguments.\n");
|
||||
76 }
|
||||
77
|
||||
78 static void
|
||||
79 app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer user_data) {
|
||||
80 GtkWidget *win;
|
||||
81 GtkWidget *nb;
|
||||
82 GtkWidget *lab;
|
||||
83 GtkNotebookPage *nbp;
|
||||
84 GtkWidget *scr;
|
||||
85 GtkWidget *tv;
|
||||
86 GtkTextBuffer *tb;
|
||||
87 char *contents;
|
||||
88 gsize length;
|
||||
89 char *filename;
|
||||
90 int i;
|
||||
91
|
||||
92 win = gtk_application_window_new (GTK_APPLICATION (app));
|
||||
93 gtk_window_set_title (GTK_WINDOW (win), "file editor");
|
||||
94 gtk_window_maximize (GTK_WINDOW (win));
|
||||
95
|
||||
96 nb = gtk_notebook_new ();
|
||||
97 gtk_window_set_child (GTK_WINDOW (win), nb);
|
||||
64 if ((pathname = g_file_get_path (file)) != NULL) {
|
||||
65 g_printerr ("Can't save %s.", pathname);
|
||||
66 g_free (pathname);
|
||||
67 } else
|
||||
68 g_printerr ("Pathname not exist.\n");
|
||||
69 }
|
||||
70 g_free (contents);
|
||||
71 g_object_unref (file);
|
||||
72 }
|
||||
73 return FALSE;
|
||||
74 }
|
||||
75
|
||||
76 static void
|
||||
77 app_activate (GApplication *app) {
|
||||
78 g_print ("You need to give filenames as arguments.\n");
|
||||
79 }
|
||||
80
|
||||
81 static void
|
||||
82 app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint) {
|
||||
83 GtkWidget *win;
|
||||
84 GtkWidget *nb;
|
||||
85 GtkWidget *lab;
|
||||
86 GtkNotebookPage *nbp;
|
||||
87 GtkWidget *scr;
|
||||
88 GtkWidget *tv;
|
||||
89 GtkTextBuffer *tb;
|
||||
90 char *contents;
|
||||
91 gsize length;
|
||||
92 char *filename;
|
||||
93 int i;
|
||||
94
|
||||
95 win = gtk_application_window_new (GTK_APPLICATION (app));
|
||||
96 gtk_window_set_title (GTK_WINDOW (win), "file editor");
|
||||
97 gtk_window_set_default_size (GTK_WINDOW (win), 600, 400);
|
||||
98
|
||||
99 for (i = 0; i < n_files; i++) {
|
||||
100 if (g_file_load_contents (files[i], NULL, &contents, &length, NULL, NULL)) {
|
||||
101 scr = gtk_scrolled_window_new ();
|
||||
102 tv = tfe_text_view_new ();
|
||||
103 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
||||
104 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR);
|
||||
105 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);
|
||||
106
|
||||
107 tfe_text_view_set_file (TFE_TEXT_VIEW (tv), g_file_dup (files[i]));
|
||||
108 gtk_text_buffer_set_text (tb, contents, length);
|
||||
109 g_free (contents);
|
||||
110 filename = g_file_get_basename (files[i]);
|
||||
111 lab = gtk_label_new (filename);
|
||||
112 gtk_notebook_append_page (GTK_NOTEBOOK (nb), scr, lab);
|
||||
113 nbp = gtk_notebook_get_page (GTK_NOTEBOOK (nb), scr);
|
||||
114 g_object_set (nbp, "tab-expand", TRUE, NULL);
|
||||
115 g_free (filename);
|
||||
116 } else if ((filename = g_file_get_path (files[i])) != NULL) {
|
||||
117 g_print ("No such file: %s.\n", filename);
|
||||
118 g_free (filename);
|
||||
119 } else
|
||||
120 g_print ("No valid file is given\n");
|
||||
121 }
|
||||
122 if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb)) > 0) {
|
||||
123 g_signal_connect (win, "close-request", G_CALLBACK (before_close), nb);
|
||||
124 gtk_widget_show (win);
|
||||
125 } else
|
||||
126 gtk_window_destroy (GTK_WINDOW (win));
|
||||
127 }
|
||||
128
|
||||
129 int
|
||||
130 main (int argc, char **argv) {
|
||||
131 GtkApplication *app;
|
||||
132 int stat;
|
||||
133
|
||||
134 app = gtk_application_new ("com.github.ToshioCP.tfe1", G_APPLICATION_HANDLES_OPEN);
|
||||
135 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
|
||||
136 g_signal_connect (app, "open", G_CALLBACK (app_open), NULL);
|
||||
137 stat =g_application_run (G_APPLICATION (app), argc, argv);
|
||||
138 g_object_unref (app);
|
||||
139 return stat;
|
||||
140 }
|
||||
99 nb = gtk_notebook_new ();
|
||||
100 gtk_window_set_child (GTK_WINDOW (win), nb);
|
||||
101
|
||||
102 for (i = 0; i < n_files; i++) {
|
||||
103 if (g_file_load_contents (files[i], NULL, &contents, &length, NULL, NULL)) {
|
||||
104 scr = gtk_scrolled_window_new ();
|
||||
105 tv = tfe_text_view_new ();
|
||||
106 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
||||
107 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR);
|
||||
108 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);
|
||||
109
|
||||
110 tfe_text_view_set_file (TFE_TEXT_VIEW (tv), g_file_dup (files[i]));
|
||||
111 gtk_text_buffer_set_text (tb, contents, length);
|
||||
112 g_free (contents);
|
||||
113 filename = g_file_get_basename (files[i]);
|
||||
114 lab = gtk_label_new (filename);
|
||||
115 gtk_notebook_append_page (GTK_NOTEBOOK (nb), scr, lab);
|
||||
116 nbp = gtk_notebook_get_page (GTK_NOTEBOOK (nb), scr);
|
||||
117 g_object_set (nbp, "tab-expand", TRUE, NULL);
|
||||
118 g_free (filename);
|
||||
119 } else if ((filename = g_file_get_path (files[i])) != NULL) {
|
||||
120 g_print ("No such file: %s.\n", filename);
|
||||
121 g_free (filename);
|
||||
122 } else
|
||||
123 g_print ("No valid file is given\n");
|
||||
124 }
|
||||
125 if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb)) > 0) {
|
||||
126 g_signal_connect (win, "close-request", G_CALLBACK (before_close), nb);
|
||||
127 gtk_widget_show (win);
|
||||
128 } else
|
||||
129 gtk_window_destroy (GTK_WINDOW (win));
|
||||
130 }
|
||||
131
|
||||
132 int
|
||||
133 main (int argc, char **argv) {
|
||||
134 GtkApplication *app;
|
||||
135 int stat;
|
||||
136
|
||||
137 app = gtk_application_new ("com.github.ToshioCP.tfe1", G_APPLICATION_HANDLES_OPEN);
|
||||
138 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
|
||||
139 g_signal_connect (app, "open", G_CALLBACK (app_open), NULL);
|
||||
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.
|
||||
`files[i]` is a pointer to GFile structure.
|
||||
It will be freed by the system. So you need to copy it.
|
||||
`g_file_dup` duplicates the given GFile structure.
|
||||
- 123: Connects "close-request" signal and `before_close` handler.
|
||||
The fourth argument is called user data and it is given to the signal handler.
|
||||
- 110: The GFile pointer of the TfeTextView is set with `files[i]`, which is a GFile created with the command line argument.
|
||||
But the GFile will be destroyed by the system later.
|
||||
So it needs to be copied before the assignment.
|
||||
`g_file_dup` duplicates the GFile.
|
||||
- 126: The "close-request" signal is connected to `before_close` 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.
|
||||
|
||||
Now compile and run it.
|
||||
There's a sample file in the directory `tfe`.
|
||||
Type `./a.out taketori.txt`.
|
||||
Now it's time to compile and run.
|
||||
|
||||
~~~
|
||||
$ cd src/tfe
|
||||
$ comp tfe1
|
||||
$ ./a.out taketori.txt`.
|
||||
~~~
|
||||
|
||||
Modify the contents and close the window.
|
||||
Make sure that the file is modified.
|
||||
|
||||
|
|
87
gfm/sec9.md
|
@ -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
|
||||
|
||||
In the last section we made the almost simplest editor possible.
|
||||
It reads files in the `app_open` function at start-up and writes them out when closing the window.
|
||||
It works but is not very good.
|
||||
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.
|
||||
Signals and handlers will be explained later.
|
||||
|
||||
![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
|
||||
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;
|
||||
4 GtkWidget *nb;
|
||||
5 GtkWidget *lab;
|
||||
|
@ -105,7 +104,7 @@ The function `app_open` in the source code `tfe2.c` is as follows.
|
|||
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.
|
||||
- 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.
|
||||
- 41-44: Creates four buttons.
|
||||
- 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.
|
||||
It is appended to `boxv` as the second child.
|
||||
|
||||
The number of lines to build the widgets is 33(=57-25+1).
|
||||
We also needed many additional variables (`boxv`, `boxh`, `dmy1`, ...),
|
||||
most of which weren't necessary, except for building the widgets.
|
||||
The number of widget-build lines is 33(=57-25+1).
|
||||
We also needed many variables (`boxv`, `boxh`, `dmy1`, ...) and most of them used only for building the widgets.
|
||||
Are there any good solution to reduce these work?
|
||||
|
||||
Gtk provides GtkBuilder.
|
||||
|
@ -135,7 +133,7 @@ It reduces this cumbersome work.
|
|||
|
||||
## 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
|
||||
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>
|
||||
~~~
|
||||
|
||||
The structure of this file is XML.
|
||||
Constructs that begin with `<` and end with `>` are called tags.
|
||||
The is a XML file.
|
||||
Tags begin with `<` and end with `>`.
|
||||
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.
|
||||
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.
|
||||
|
||||
- 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 can't recognize it and a fatal error occurs.
|
||||
- 3-6: An object with `GtkApplicationWindow` class and `win` id is defined.
|
||||
- 1: XML declaration.
|
||||
It specifies that the XML version is 1.0 and the encoding is UTF-8.
|
||||
- 3-6: An object tag with `GtkApplicationWindow` class and `win` id.
|
||||
This is the top level window.
|
||||
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.
|
||||
- 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 `win`.
|
||||
The `title` property is "file editor", `default-width` property is 600 and `default-height` property is 400.
|
||||
- 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 `win`.
|
||||
|
||||
Compare this ui file and the lines 25-57 in the source code of `app_open` function.
|
||||
Those two describe the same structure of widgets.
|
||||
Compare this ui file and the lines 25-57 in the `tfe2.c` source code.
|
||||
Both builds the same window with its descendant widgets.
|
||||
|
||||
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 builds widgets based on the ui file.
|
||||
GtkBuilder builds widgets based on a ui file.
|
||||
|
||||
~~~C
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
|
||||
~~~
|
||||
|
@ -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.tfe3", G_APPLICATION_HANDLES_OPEN);
|
||||
144a107
|
||||
>
|
||||
~~~
|
||||
|
||||
`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
|
||||
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;
|
||||
4 GtkWidget *nb;
|
||||
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.
|
||||
If you want to see it, click the link above.
|
||||
|
||||
### Using ui string
|
||||
|
||||
GtkBuilder can build widgets using string.
|
||||
Use the function `gtk_builder_new_from_string` instead of `gtk_builder_new_from_file`.
|
||||
GtkBuilder can build widgets with string.
|
||||
Use `gtk_builder_new_from_string` instead of `gtk_builder_new_from_file`.
|
||||
|
||||
~~~C
|
||||
char *uistring;
|
||||
|
@ -395,19 +392,19 @@ uistring =
|
|||
... ... ...
|
||||
"</interface>";
|
||||
|
||||
build = gtk_builder_new_from_stringfile (uistring);
|
||||
build = gtk_builder_new_from_string (uistring, -1);
|
||||
~~~
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
- Add backslash before each double quote.
|
||||
- Add double quotes at the left and right of the string in each line.
|
||||
|
||||
### Using Gresource
|
||||
### Gresource
|
||||
|
||||
Using Gresource is similar to using string.
|
||||
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).
|
||||
However, this xml has only one gresource.
|
||||
- 3: The gresource has a prefix `/com/github/ToshioCP/tfe3`.
|
||||
- 4: The gresource has `tfe3.ui`.
|
||||
And it is pointed by `/com/github/ToshioCP/tfe3/tfe3.ui` because it needs prefix.
|
||||
If you want to add more files, then insert them between line 4 and 5.
|
||||
- 4: The name of the gresource is `tfe3.ui`.
|
||||
The resource will be pointed with `/com/github/ToshioCP/tfe3/tfe3.ui` by GtkBuilder.
|
||||
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`.
|
||||
The gresource compiler `glib-compile-resources` shows its usage with the argument `--help`.
|
||||
|
@ -469,7 +467,9 @@ Application Options:
|
|||
|
||||
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.
|
||||
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 window appears and it is the same as the screenshot at the beginning of this page.
|
||||
The function `gtk_builder_new_from_resource` builds widgets from a resource.
|
||||
|
||||
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)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# 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
|
||||
- GFile *[tfe_text_view_get_file ()](../src/tfetextview/#tfe_text_view_get_file)
|
||||
|
@ -39,8 +39,8 @@ GObject
|
|||
|
||||
## Description
|
||||
|
||||
TfeTextView holds GFile which the contents of GtkTextBuffer corresponds to.
|
||||
File manipulation functions are added to this object.
|
||||
TfeTextView holds GFile corresponds to the contents of GtkTextBuffer.
|
||||
It has some file manipulation 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.
|
||||
This function doesn't do any I/O operations.
|
||||
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.
|
||||
The caller needs to set a handler to this signal in advance.
|
||||
|
||||
|
@ -83,9 +83,9 @@ void
|
|||
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.
|
||||
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
|
||||
|
||||
|
@ -99,7 +99,7 @@ tfe_text_view_saveas (TfeTextView *tv);
|
|||
~~~
|
||||
|
||||
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
|
||||
|
||||
|
@ -174,6 +174,13 @@ Members:
|
|||
- TFE_OPEN_RESPONSE_CANCEL: Reading file is canceled by the user.
|
||||
- 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
|
||||
|
||||
### change-file
|
||||
|
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 105 KiB |
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 13 KiB |
|
@ -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).
|
||||
It also describes GtkExpression.
|
||||
|
||||
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.
|
||||
The latest version of the tutorial is located at [GTK4-tutorial GitHub repository](https://github.com/ToshioCP/GTK4-tutorial).
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
These websites are newly opened lately (Aug/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 new website is in progress at present, so you might need to refer to the old version.
|
||||
These websites were opened in August of 2021.
|
||||
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/).
|
||||
|
||||
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
|
||||
|
||||
This tutorial is under development and unstable.
|
||||
Even though the codes of the examples have been tested on GTK 4 (version 4.0), bugs may still exist.
|
||||
This tutorial is still under development and unstable.
|
||||
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.
|
||||
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).
|
||||
When you make corrections, 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.
|
||||
You can post it to [GitHub issues](https://github.com/ToshioCP/GTK4-tutorial/issues).
|
||||
You can also post updated files to [pull request](https://github.com/ToshioCP/GTK4-tutorial/pulls).
|
||||
One thing you need to be careful is to correct the source files, which are under the 'src' directory.
|
||||
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.
|
||||
|
||||
#### 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 pdf` for PDF.
|
||||
@@@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
|
||||
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
|
||||
An appendix "How to build GTK 4 Tutorial" describes how to make them.
|
||||
@@@end
|
||||
|
|
|
@ -4,36 +4,46 @@ typedef struct _GObjectClass GInitiallyUnownedClass;
|
|||
struct _GObjectClass
|
||||
{
|
||||
GTypeClass g_type_class;
|
||||
|
||||
/*< private >*/
|
||||
GSList *construct_properties;
|
||||
|
||||
/*< public >*/
|
||||
/* seldom overridden */
|
||||
GObject* (*constructor) (GType type,
|
||||
guint n_construct_properties,
|
||||
GObjectConstructParam *construct_properties);
|
||||
/* overridable methods */
|
||||
void (*set_property) (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec);
|
||||
void (*get_property) (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec);
|
||||
void (*dispose) (GObject *object);
|
||||
void (*finalize) (GObject *object);
|
||||
void (*set_property) (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec);
|
||||
void (*get_property) (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec);
|
||||
void (*dispose) (GObject *object);
|
||||
void (*finalize) (GObject *object);
|
||||
/* seldom overridden */
|
||||
void (*dispatch_properties_changed) (GObject *object,
|
||||
guint n_pspecs,
|
||||
GParamSpec **pspecs);
|
||||
guint n_pspecs,
|
||||
GParamSpec **pspecs);
|
||||
/* signals */
|
||||
void (*notify) (GObject *object,
|
||||
GParamSpec *pspec);
|
||||
void (*notify) (GObject *object,
|
||||
GParamSpec *pspec);
|
||||
|
||||
/* called when done constructing */
|
||||
void (*constructed) (GObject *object);
|
||||
void (*constructed) (GObject *object);
|
||||
|
||||
/*< private >*/
|
||||
gsize flags;
|
||||
gsize flags;
|
||||
|
||||
gsize n_construct_properties;
|
||||
|
||||
gpointer pspecs;
|
||||
gsize n_pspecs;
|
||||
|
||||
/* padding */
|
||||
gpointer pdummy[6];
|
||||
gpointer pdummy[3];
|
||||
};
|
||||
|
||||
|
|
|
@ -102,8 +102,8 @@ struct _GtkTextViewClass
|
|||
void (* toggle_overwrite) (GtkTextView *text_view);
|
||||
GtkTextBuffer * (* create_buffer) (GtkTextView *text_view);
|
||||
void (* snapshot_layer) (GtkTextView *text_view,
|
||||
GtkTextViewLayer layer,
|
||||
GtkSnapshot *snapshot);
|
||||
GtkTextViewLayer layer,
|
||||
GtkSnapshot *snapshot);
|
||||
gboolean (* extend_selection) (GtkTextView *text_view,
|
||||
GtkTextExtendSelection granularity,
|
||||
const GtkTextIter *location,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include <gtk/gtk.h>
|
||||
|
||||
static void
|
||||
app_activate (GApplication *app, gpointer user_data) {
|
||||
app_activate (GApplication *app) {
|
||||
GtkWidget *win;
|
||||
GtkWidget *lab;
|
||||
|
||||
|
@ -20,7 +20,7 @@ main (int argc, char **argv) {
|
|||
GtkApplication *app;
|
||||
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);
|
||||
stat =g_application_run (G_APPLICATION (app), argc, argv);
|
||||
g_object_unref (app);
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
#include <gtk/gtk.h>
|
||||
|
||||
static void
|
||||
click_cb (GtkButton *btn, gpointer user_data) {
|
||||
click_cb (GtkButton *btn) {
|
||||
g_print ("Clicked.\n");
|
||||
}
|
||||
|
||||
static void
|
||||
app_activate (GApplication *app, gpointer user_data) {
|
||||
app_activate (GApplication *app) {
|
||||
GtkWidget *win;
|
||||
GtkWidget *btn;
|
||||
|
||||
|
@ -26,7 +26,7 @@ main (int argc, char **argv) {
|
|||
GtkApplication *app;
|
||||
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);
|
||||
stat =g_application_run (G_APPLICATION (app), argc, argv);
|
||||
g_object_unref (app);
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
#include <gtk/gtk.h>
|
||||
|
||||
static void
|
||||
click_cb (GtkButton *btn, gpointer user_data) {
|
||||
GtkWindow *win = GTK_WINDOW (user_data);
|
||||
click_cb (GtkButton *btn, GtkWindow *win) {
|
||||
gtk_window_destroy (win);
|
||||
}
|
||||
|
||||
static void
|
||||
app_activate (GApplication *app, gpointer user_data) {
|
||||
app_activate (GApplication *app) {
|
||||
GtkWidget *win;
|
||||
GtkWidget *btn;
|
||||
|
||||
|
@ -15,7 +14,7 @@ app_activate (GApplication *app, gpointer user_data) {
|
|||
gtk_window_set_title (GTK_WINDOW (win), "lb3");
|
||||
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);
|
||||
g_signal_connect (btn, "clicked", G_CALLBACK (click_cb), win);
|
||||
|
||||
|
@ -27,7 +26,7 @@ main (int argc, char **argv) {
|
|||
GtkApplication *app;
|
||||
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);
|
||||
stat =g_application_run (G_APPLICATION (app), argc, argv);
|
||||
g_object_unref (app);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#include <gtk/gtk.h>
|
||||
|
||||
static void
|
||||
click1_cb (GtkButton *btn, gpointer user_data) {
|
||||
const gchar *s;
|
||||
click1_cb (GtkButton *btn) {
|
||||
const char *s;
|
||||
|
||||
s = gtk_button_get_label (btn);
|
||||
if (g_strcmp0 (s, "Hello.") == 0)
|
||||
|
@ -12,13 +12,12 @@ click1_cb (GtkButton *btn, gpointer user_data) {
|
|||
}
|
||||
|
||||
static void
|
||||
click2_cb (GtkButton *btn, gpointer user_data) {
|
||||
GtkWindow *win = GTK_WINDOW (user_data);
|
||||
click2_cb (GtkButton *btn, GtkWindow *win) {
|
||||
gtk_window_destroy (win);
|
||||
}
|
||||
|
||||
static void
|
||||
app_activate (GApplication *app, gpointer user_data) {
|
||||
app_activate (GApplication *app) {
|
||||
GtkWidget *win;
|
||||
GtkWidget *box;
|
||||
GtkWidget *btn1;
|
||||
|
@ -35,7 +34,7 @@ app_activate (GApplication *app, gpointer user_data) {
|
|||
btn1 = gtk_button_new_with_label ("Hello.");
|
||||
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);
|
||||
|
||||
gtk_box_append (GTK_BOX (box), btn1);
|
||||
|
@ -49,7 +48,7 @@ main (int argc, char **argv) {
|
|||
GtkApplication *app;
|
||||
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);
|
||||
stat =g_application_run (G_APPLICATION (app), argc, argv);
|
||||
g_object_unref (app);
|
||||
|
|
|
@ -5,7 +5,7 @@ main (int argc, char **argv) {
|
|||
GtkApplication *app;
|
||||
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);
|
||||
g_object_unref (app);
|
||||
return stat;
|
||||
|
|
|
@ -10,7 +10,7 @@ main (int argc, char **argv) {
|
|||
GtkApplication *app;
|
||||
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);
|
||||
stat =g_application_run (G_APPLICATION (app), argc, argv);
|
||||
g_object_unref (app);
|
||||
|
|
|
@ -14,7 +14,7 @@ main (int argc, char **argv) {
|
|||
GtkApplication *app;
|
||||
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);
|
||||
stat =g_application_run (G_APPLICATION (app), argc, argv);
|
||||
g_object_unref (app);
|
||||
|
|
|
@ -15,7 +15,7 @@ main (int argc, char **argv) {
|
|||
GtkApplication *app;
|
||||
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);
|
||||
stat =g_application_run (G_APPLICATION (app), argc, argv);
|
||||
g_object_unref (app);
|
||||
|
|
|
@ -6,43 +6,40 @@
|
|||
|
||||
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.
|
||||
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:
|
||||
|
||||
- PC with Linux distribution like Ubuntu, Debian and so on.
|
||||
- Gcc.
|
||||
- PC with Linux distribution like Ubuntu or Debian.
|
||||
- GCC.
|
||||
- GTK 4.
|
||||
The stable version of Gtk on Linux distributions is version three at present.
|
||||
You need to install GTK 4 to your computer.
|
||||
See [Section 3](sec3.src.md) for the installation of GTK 4.
|
||||
The stable version of GTK is 4.8.2 at present (13/Dec/2022), but older version (4.0 or higher) may work.
|
||||
See [Section 3](sec3.src.md) for the installation for GTK 4.
|
||||
|
||||
### Ruby and rake for making the document
|
||||
|
||||
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:
|
||||
|
||||
- Linux distribution like Ubuntu.
|
||||
- Linux.
|
||||
- Ruby programming language.
|
||||
There are two ways to install it.
|
||||
One is installing the distribution's package.
|
||||
The other is using rbenv and ruby-build.
|
||||
If you want to use the latest version of ruby, use rbenv.
|
||||
- Rake.
|
||||
It is a gem, which is a library written in Ruby.
|
||||
You can install it as a package of your distribution or use gem command.
|
||||
You don't need to install it separately because it is a standard library of Ruby.
|
||||
|
||||
## 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.
|
||||
All of them make up the 'Gtk4-tutorial' package.
|
||||
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 repository contains tutorial documents and programs such as converters, generators and controllers.
|
||||
All of them make up the 'GTK4-tutorial' package.
|
||||
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 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.
|
||||
|
||||
|
|
210
src/sec10.src.md
|
@ -1,11 +1,11 @@
|
|||
# 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.
|
||||
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.
|
||||
- There are two compilers, `gcc` and `glib-compile-resources`.
|
||||
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.
|
||||
|
||||
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.
|
||||
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
|
||||
@@@
|
||||
|
||||
## Make
|
||||
|
||||
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.
|
||||
|
||||
- Compiling the ui file `tfe.ui` into `resources.c`.
|
||||
|
@ -76,99 +74,18 @@ The building step increases.
|
|||
- Compiling `resources.c` into `resources.o`.
|
||||
- Linking all the object files into application `tfe`.
|
||||
|
||||
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.
|
||||
Build tools manage the steps.
|
||||
I'll show you three build tools, Meson and Ninja, Make and Rake.
|
||||
Meson and Ninja is recommended as a C build tool, but others are also fine.
|
||||
It's your choice.
|
||||
|
||||
Make analyzes Makefile and executes compilers.
|
||||
All instructions are written in Makefile.
|
||||
## Meson and Ninja
|
||||
|
||||
~~~makefile
|
||||
sample.o: sample.c
|
||||
gcc -o sample.o sample.c
|
||||
~~~
|
||||
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.
|
||||
|
||||
The sample of Malefile above consists of three elements, `sample.o`, `sample.c` and `gcc -o sample.o sample.c`.
|
||||
|
||||
- `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.
|
||||
You need to make `meson.build` file first.
|
||||
|
||||
@@@include
|
||||
tfe4/meson.build
|
||||
|
@ -199,13 +116,100 @@ Then, the executable file `tfe` is generated under the directory `_build`.
|
|||
|
||||
$ _build/tfe tfe.c tfetextview.c
|
||||
|
||||
Then the window appears.
|
||||
And two notebook pages are in the window.
|
||||
One notebook is `tfe.c` and the other is `tfetextview.c`.
|
||||
A window appears.
|
||||
It includes a notebook with two pages.
|
||||
One is `tfe.c` and the other is `tfetextview.c`.
|
||||
|
||||
I've shown you three build tools.
|
||||
I think meson and ninja is the best choice for the present.
|
||||
For further information, see [The Meson Build system](https://mesonbuild.com/).
|
||||
|
||||
We divided a file into some categorized files and used a build tool.
|
||||
This method is used by many developers.
|
||||
## Make
|
||||
|
||||
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).
|
||||
|
|
148
src/sec11.src.md
|
@ -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.
|
||||
It is `tfe5`.
|
||||
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).
|
||||
|
||||
## Encapsulation
|
||||
|
@ -33,11 +32,10 @@ After that I will explain:
|
|||
|
||||
## GObject and its children
|
||||
|
||||
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.
|
||||
GObject and its children are objects, which have both class and object C structures.
|
||||
First, think about instances.
|
||||
An instance is memories which has the object structure.
|
||||
The following is the structure of TfeTextView.
|
||||
|
||||
~~~C
|
||||
/* 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.
|
||||
It is declared in `gtktextview.h`.
|
||||
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.
|
||||
So, no memories are allocated at this moment.
|
||||
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).
|
||||
You can find the declaration of the ancestors' object structures in the source files of GTK and GLib.
|
||||
The following is extracted from the source files (not exactly the same).
|
||||
|
||||
~~~C
|
||||
typedef struct _GObject GObject;
|
||||
|
@ -91,15 +83,45 @@ struct _GtkTextView
|
|||
~~~
|
||||
|
||||
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.
|
||||
So, all the ancestors are included in the child object.
|
||||
The structure of `TfeTextView` is like the following diagram.
|
||||
|
||||
![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.
|
||||
|
||||
|
@ -110,12 +132,13 @@ tfetextview/tfetextview.c tfe_text_view_new
|
|||
When this function is invoked, a TfeTextView instance is created and initialized.
|
||||
The initialization process is as follows.
|
||||
|
||||
1. Initializes GObject (GInitiallyUnowned) part in TfeTextView instance.
|
||||
2. Initializes GtkWidget part in TfeTextView instance.
|
||||
3. Initializes GtkTextView part in TfeTextView instance.
|
||||
4. Initializes TfeTextView part in TfeTextView instance.
|
||||
1. When the instance is created, GtkWidgetPrivate and GtkTextViewPrivate structures are also created
|
||||
2. Initializes GObject (GInitiallyUnowned) part in the TfeTextView instance.
|
||||
3. Initializes GtkWidget part (the first `priv`) in the TfeTextView instance and GtkWidgetPrivate structure.
|
||||
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.
|
||||
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
|
||||
|
||||
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.
|
||||
Those instances have the same structure.
|
||||
An instance just keeps status of the instance.
|
||||
Therefore, it is insufficient to define its behavior.
|
||||
Instances just have data.
|
||||
Therefore, it doesn't define object's behavior.
|
||||
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.
|
||||
For example, `tfe_text_view_new` 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.
|
||||
For example,
|
||||
|
||||
Class comprises mainly pointers to functions.
|
||||
Those functions are used by the object itself or its descendant objects.
|
||||
- `TfeTextView *tfe_text_view_new (void);` is a function to create a TfeTextView instance.
|
||||
- `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.
|
||||
|
||||
@@@include
|
||||
class_gobject.c
|
||||
@@@
|
||||
|
||||
I'd like to explain some of the members.
|
||||
There's a pointer to the function `dispose` in line 23.
|
||||
|
||||
~~~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.
|
||||
|
||||
- 11: 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.
|
||||
- 13: A function pointed by `constructor` is called when the instance is generated. It completes the initialization of the instance.
|
||||
- 25: A function pointed by `dispose` 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.
|
||||
- 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.
|
||||
|
||||
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 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;
|
||||
struct _TfeTextView {
|
||||
GtkTextView parent;
|
||||
GFile *file;
|
||||
};
|
||||
~~~
|
||||
GObjectClass (GInitiallyUnownedClass) -- GtkWidgetClass -- GtkTextViewClass -- TfeTextViewClass
|
||||
~~~
|
||||
|
||||
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.
|
||||
|
||||
GObject -- GInitiallyUnowned -- GtkWidget -- GtkTextView -- TfeTextView
|
||||
|
||||
The following is extracts from the source files (not exactly the same).
|
||||
The following is extracted from the source codes (not exactly the same).
|
||||
|
||||
@@@include
|
||||
classes.c
|
||||
|
@ -208,11 +223,11 @@ classes.c
|
|||
|
||||
- 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`.
|
||||
- 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.
|
||||
- Class members in ancestors are open to the descendant class.
|
||||
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 rewriting ancestors' class methods in the descendant class.)
|
||||
- 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.
|
||||
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 `dispose` in its class to release all references to other objects.
|
||||
In the finalizing process, it invokes the function pointed by `finalize` in its class to complete the destruction process.
|
||||
These functions are also called handlers or methods.
|
||||
In the disposing process, the object invokes the function pointed by `dispose` in its class to release all references to other instances.
|
||||
After that, it invokes the function pointed by `finalize` in its class to complete the destruction process.
|
||||
For example, dispose handler or dispose method.
|
||||
|
||||
In the destruction process of TfeTextView, the reference count of widgets related to TfeTextView is automatically decreased.
|
||||
But GFile pointed by `tv->file` needs to decrease its reference count by one.
|
||||
You must write the code in the dispose handler `tfe_text_view_dispose`.
|
||||
In the destruction process, TfeTextView needs to unref the GFile pointed by `tv->file`.
|
||||
You must write the dispose handler `tfe_text_view_dispose`.
|
||||
|
||||
@@@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.
|
||||
|
@ -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.
|
||||
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
|
||||
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);
|
||||
~~~
|
||||
|
||||
`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.
|
||||
And `gobject` is a pointer to TfeTextView instance which is casted as a GObject instance.
|
||||
`dh3` releases all the references to objects in the GtkTextView part (it is actually the private area pointed by `prev`) in TfeTextView 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.
|
||||
After that, `dh3` calls `dh2`, and `dh2` calls `dh1`.
|
||||
Finally all the references are released.
|
||||
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
|
||||
## Signals
|
||||
|
||||
In Gtk programming, each object is encapsulated.
|
||||
And it is not recommended to use global variables because they tend to make the program complicated.
|
||||
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.
|
||||
|
||||
- Functions.
|
||||
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.
|
||||
For example, `activate` signal on GApplication object.
|
||||
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.
|
||||
The connection is usually done out of the object.
|
||||
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
|
||||
|
||||
|
@ -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.
|
||||
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 array is used to register two or more signals.
|
||||
A static variable or array is used to store signal ID.
|
||||
|
||||
~~~C
|
||||
enum {
|
||||
|
@ -60,10 +59,10 @@ tfetextview/tfetextview.c tfe_text_view_class_init
|
|||
|
||||
- 6-15: Registers "change-file" signal.
|
||||
`g_signal_new` function is used.
|
||||
The signal "change-file" has no default handler (object method handler).
|
||||
You usually don't need to set a default handler.
|
||||
If you need it, use `g_signal_new_class_handler` function.
|
||||
See [GObject API Reference, g\_signal\_new\_class\_handler](https://docs.gtk.org/gobject/func.signal_new_class_handler.html) for further information.
|
||||
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 a default handler.
|
||||
If you need it, use `g_signal_new_class_handler` function instead of `g_signal_new`.
|
||||
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 type of signal id is guint, which is the same as unsigned int.
|
||||
It is used in the function `g_signal_emit`.
|
||||
|
|
276
src/sec13.src.md
|
@ -1,30 +1,36 @@
|
|||
# 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.
|
||||
C source files `tfeapplication.c` and `tfenotebook.c` include `tfe.h` at the beginning.
|
||||
The header file `tfetextview.h` provides:
|
||||
|
||||
@@@include
|
||||
tfe5/tfe.h
|
||||
@@@
|
||||
- The type of TfeTextView, which is `TFE_TYPE_TEXT_VIEW`.
|
||||
- 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
|
||||
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.
|
||||
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.
|
||||
- `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.
|
||||
- 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`.
|
||||
|
||||
|
@ -49,10 +55,13 @@ tfetextview/tfetextview.c tfe_text_view_new_with_file tfe_text_view_new
|
|||
|
||||
- 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.
|
||||
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.
|
||||
- 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).
|
||||
- 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).
|
||||
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 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.
|
||||
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.
|
||||
|
||||
|
@ -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.
|
||||
The error is managed only in the TfeTextView and no information is notified to the caller.
|
||||
|
||||
### save\_file function
|
||||
|
||||
@@@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.
|
||||
This function is called from `saveas_dialog_response` and `tfe_text_view_save`.
|
||||
- The function `save_file` 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.
|
||||
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`.
|
||||
Therefore, only functions in this file (`tfeTetview.c`) call this function.
|
||||
Such static functions usally don't have `g_return_val_if_fail` function.
|
||||
Therefore, only functions in this file (`tfetextview.c`) call this function.
|
||||
Such static functions usually don't have `g_return_val_if_fail` function.
|
||||
- 10-11: Gets the text contents from the buffer.
|
||||
- 12-14: Saves the contents to the file.
|
||||
If no error happens, set the modified flag to be FALSE.
|
||||
- 12: The function `g_file_replace_contents` writes the contents to the file and returns the status (true = success/ false = fail).
|
||||
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.
|
||||
And set the return status `stat` to be TRUE.
|
||||
- 15-23: If it fails to save the contents, displays an error message.
|
||||
- 16-18: Creates a message dialog with the error message.
|
||||
- 19: Connects the "response" signal to `gtk_window_destroy`, so that the dialog disappears when a user clicked on the button.
|
||||
- 20-21: Shows the window, frees `err` and set `stat` to be FLASE.
|
||||
- 24: Frees `contents`.
|
||||
- 25: Returns to the caller.
|
||||
- 28-47: `saveas_dialog_response` function.
|
||||
This is a signal handler for the "response" signal on GtkFileChooserDialog instance created by `tfe_text_view_saveas` function.
|
||||
This handler analyzes the response and determines whether to save the contents.
|
||||
- 34-45: If the response is `GTK_RESPONSE_ACCEPT`, the user has clicked on the `Save` button. So, it tries to save.
|
||||
- 35: Gets the GFile `file` from GtkFileChooserDialog.
|
||||
- 36-37: If it doesn't point GFile, it outputs an error message to the log.
|
||||
- 38: Otherwise, it calls `save_file` to save the contents to the file.
|
||||
- 39-42: If `save_file` has successfully saved the contents, `tv->file` is updated.
|
||||
If the old GFile pointed by `tv->file` exists, it is freed in advance.
|
||||
Emits "change-file" signal.
|
||||
- 44: Unrefs `file`.
|
||||
- 46: destroys the file chooser dialog.
|
||||
- 49-64: `tfe_text_view_save` function.
|
||||
- 51: `tfe_text_view_save` is public, i.e. it is open to the other files.
|
||||
- 15-22: If it fails to save the contents, an error message will be displayed.
|
||||
- 17-18: Creates a message dialog. The parameters are:
|
||||
- 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.
|
||||
- GtkDialogFlags flags: GTK\_DIALOG\_MODAL for modal dialog. A modal dialog is usually fine.
|
||||
- GtkMessageType type: GTK\_MESSAGE\_ERROR for error message. Other options are GTK\_MESSAGE\_INFO, GTK\_MESSAGE\_WARNING and so on.
|
||||
- GtkButtonsType buttons: GTK\_BUTTON\_OK is used most often. Other option is GTK\_BUTTON\_YES\_NO, GTK\_BUTTON\_CANCEL and so on.
|
||||
- const gchar* message_format: gchar is the same as char. This format is the same as printf. Arguments for the message format follow.
|
||||
- 19: Connects the "response" signal to `gtk_window_destroy`, so that the dialog disappears when the user clicks on the button.
|
||||
- 20: Shows the message dialog.
|
||||
- 21: Frees `err` with `g_error_free` function.
|
||||
- 23: Frees `contents`.
|
||||
- 24: Returns to the caller.
|
||||
|
||||
### saveas\_dialog\_response function
|
||||
|
||||
@@@include
|
||||
tfetextview/tfetextview.c saveas_dialog_response
|
||||
@@@
|
||||
|
||||
- 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.
|
||||
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.
|
||||
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.
|
||||
- 53-54: Gets GtkTextBuffer instance and GtkWidget instance and assignes them to `tb` and`win` respectively.
|
||||
- 56-57: If the buffer hasn't modified, then it doesn't need to save it.
|
||||
So the function returns.
|
||||
- 58-59: If `tv->file` is NULL, no file has given yet.
|
||||
It calls `tfe_text_view_saveas` which prompts a user to select a file or specify a new file to save.
|
||||
- 60-61: If `tv->file` doesn't point GFile, somethig bad has happened.
|
||||
Logs an error message.
|
||||
- 62-63: Calls `save_file` to save the contents to the file.
|
||||
- 66-79: `tfe_text_view_saveas` function.
|
||||
It shows GtkFileChooserDialog and prompts the user to choose a file.
|
||||
- 73-76: Creates GtkFileChooserDialog.
|
||||
The title is "Save file".
|
||||
Transient parent of the dialog is `win`, which is the top-level window.
|
||||
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.
|
||||
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).
|
||||
- 5-6: GtkTextBuffer `tb` and GtkWidget (GtkWindow) `win` are set.
|
||||
The function `gtk_widget_get_ancestor (widget, type)` returns the first ancestor of the widget with type.
|
||||
The type is a GType.
|
||||
For example, the type of GtkWindow is `GTK_TYPE_WINDOW` and the one of TfeTextView is `TFE_TYPE_TEXT_VIEW`.
|
||||
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 can't 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.
|
||||
- 8-9: If the buffer hasn't modified, it doesn't need to be saved.
|
||||
- 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.
|
||||
- 12-13: If `tv->file` doesn't point GFile, an error message is logged out. It is not expected.
|
||||
- 14-15: Otherwise, it calls `save_file` to save the contents to the file `tv->file`.
|
||||
|
||||
![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.
|
||||
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.
|
||||
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.
|
||||
|
||||
~~~C
|
||||
|
@ -175,10 +239,8 @@ void tfe_text_view_open (TfeTextView *tv, GtkWindow *win);
|
|||
|
||||
The parameter `win` is the top-level window.
|
||||
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.
|
||||
However, it is encouraged to give a parent window to dialog.
|
||||
This function might be called just after `tv` has been created.
|
||||
|
||||
This function may be called just after `tv` has been created.
|
||||
In that case, `tv` has not been incorporated into the widget hierarchy.
|
||||
Therefore it is impossible to get the top-level window from `tv`.
|
||||
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.
|
||||
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.
|
||||
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
|
||||
tfetextview/tfetextview.c open_dialog_response tfe_text_view_open
|
||||
tfetextview/tfetextview.c open_dialog_response
|
||||
@@@
|
||||
|
||||
- 37-50: `tfe_text_view_open` function.
|
||||
- 44-47: Creates GtkFileChooserDialog.
|
||||
The title is "Open file".
|
||||
Transient parent window is the top-level window of the application, which is given by the caller.
|
||||
The action is open mode.
|
||||
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.
|
||||
- 2: The handler `open_dialog_response` has three parameters.
|
||||
- GtkWidget *dialog: FileChooserDialog on which the "response" signal is emitted.
|
||||
- gint response: response ID
|
||||
- TfeTextView *tv: textview to put the contents to
|
||||
- 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.
|
||||
Then, "open-response" signal is emitted.
|
||||
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.
|
||||
It is not expected, though.
|
||||
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`.
|
||||
- 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).
|
||||
Then, it emits "open-response" signal with the parameter `TFE_OPEN_RESPONSE_SUCCESS` and emits "change-file" signal.
|
||||
- 34: destroys GtkFileCooserDialog.
|
||||
- 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`.
|
||||
- 23-38: If the file has been successfully read, the following is carried out. (It is not simple.)
|
||||
- the text is inserted to GtkTextBuffer
|
||||
- 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.
|
||||
It is shown in the following diagram and you would think that it is really complicated.
|
||||
### tfe\_text\_view\_open function
|
||||
|
||||
@@@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.
|
||||
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}
|
||||
|
||||
|
@ -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.
|
||||
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
|
||||
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.
|
||||
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.
|
||||
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
|
||||
|
||||
|
@ -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).
|
||||
It is under the directory `src/tfetextview`.
|
||||
@@@elif html
|
||||
Refer [API document of TfeTextView](../html/tfetextview_doc.html).
|
||||
Its original markdown file is under the directory `src/tfetextview`.
|
||||
Refer [API document of TfeTextView](https://toshiocp.github.io/Gtk4-tutorial/tfetextview_doc.html).
|
||||
The markdown file is under the directory `src/tfetextview`.
|
||||
@@@elif latex
|
||||
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
|
||||
|
||||
All the source files are listed in [Section 16](sec16.src.md).
|
||||
You can find them under [src/tfe5](tfe5) and [src/tfetextview](tfetextview) directories.
|
||||
You can find all the TfeTextView source codes under [src/tfetextview](tfetextview) directories.
|
||||
|
||||
|
|
101
src/sec14.src.md
|
@ -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.
|
||||
- 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.
|
||||
- 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.
|
||||
|
||||
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
|
||||
@@@
|
||||
|
||||
- 27-38: `notebook_page_new` function.
|
||||
- 29: `g_return_if_fail` is used to check the argument.
|
||||
- 34: Creates TfeTextView object.
|
||||
If it fails, it returns to the caller.
|
||||
- 36: Creates filename, which is "Untitled", "Untitled1", ... .
|
||||
- 26-38: `notebook_page_new` function.
|
||||
- 28: `g_return_if_fail` is used to check the argument.
|
||||
- 33-34: Creates TfeTextView object.
|
||||
If it fails, no notebook page is created and the function returns to the caller.
|
||||
- 35: Creates filename, which is "Untitled", "Untitled1", ... .
|
||||
- 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.
|
||||
- 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 caller of `get_untitled` is in charge of freeing the string.
|
||||
- 37: calls `notebook_page_build` to build the contents of the page.
|
||||
- 10- 25: `notebook_page_build` function.
|
||||
- 36: calls `notebook_page_build` to build the contents of the page.
|
||||
- 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.
|
||||
- 17: Sets the wrap mode of `tv` to GTK_WRAP_WORD_CHAR so that lines are broken between words or graphemes.
|
||||
- 18: Inserts `tv` to GtkscrolledWindow as a child.
|
||||
- 19-20: Creates GtkLabel, then appends `scr` and `lab` to the GtkNotebook instance `nb`.
|
||||
- 21-22: Sets "tab-expand" property to TRUE.
|
||||
- 17: Inserts `tv` to GtkscrolledWindow as a child.
|
||||
- 18-19: Creates GtkLabel, then appends `scr` and `lab` to the GtkNotebook instance `nb`.
|
||||
- 20-21: Sets "tab-expand" property to TRUE.
|
||||
The function `g_object_set` 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 `g_object_set` to set the property.
|
||||
- 23: Sets the current page of `nb` to the newly created page.
|
||||
- 24: Connects "change-file" signal and `file_changed_cb` handler.
|
||||
- 22: Sets the current page to the newly created page.
|
||||
- 23: Connects "change-file" signal and `file_changed_cb` handler.
|
||||
|
||||
## 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`.
|
||||
If the function returns NULL, an error has happend.
|
||||
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
|
||||
|
||||
|
@ -81,35 +84,70 @@ Then, it does nothing and returns.
|
|||
tfe5/tfenotebook.c open_response notebook_page_open
|
||||
@@@
|
||||
|
||||
- 16-26: `notebook_page_open` function.
|
||||
- 22-23: Creates TfeTextView object.
|
||||
- 18-28: `notebook_page_open` function.
|
||||
- 24-25: Creates TfeTextView object.
|
||||
If NULL is returned, an error has happened.
|
||||
Then, it returns to the caller.
|
||||
- 24: Connects the signal "open-response" and the handler `open_response`.
|
||||
- 25: Calls `tfe_text_view_open`.
|
||||
- 26: Connects the signal "open-response" and the handler `open_response`.
|
||||
- 27: Calls `tfe_text_view_open`.
|
||||
The "open-response" signal will be emitted later to inform the result of opening and reading a file.
|
||||
- 1-14: `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,
|
||||
it has failed to open and read a new file.
|
||||
- 1-16: `open_response` handler.
|
||||
- 6-8: If the response code is not `TFE_OPEN_RESPONSE_SUCCESS`, it has failed to open and read a new file.
|
||||
Then, what `notebook_page_open` did in advance need to be canceled.
|
||||
The instance `tv` hasn't been a child widget of GtkScrolledWindow yet.
|
||||
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.
|
||||
Then the floating reference is converted into an ordinary reference.
|
||||
Now you call `g_object_unref` to decrease the reference count by one.
|
||||
- 9-13: Otherwise, everything is okay.
|
||||
Gets the filename, builds the contents of the page.
|
||||
- 9-15: Otherwise, everything is okay.
|
||||
Gets the filename, builds the contents of the page and frees `filename`.
|
||||
|
||||
## Floating reference
|
||||
|
||||
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 `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.
|
||||
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 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.
|
||||
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
|
||||
|
||||
|
@ -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.
|
||||
- 11-13: Otherwise, removes the current page.
|
||||
The child widget (TfeTextView) is also destroyed.
|
||||
|
||||
## 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.
|
||||
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
|
||||
tfe5/tfenotebook.c file_changed_cb
|
||||
|
|
|
@ -121,7 +121,7 @@ textview {color: yellow; ...}
|
|||
~~~
|
||||
|
||||
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.
|
||||
|
||||
|
@ -256,9 +256,7 @@ In this file, just the source file names are modified from the prior version.
|
|||
|
||||
## source files
|
||||
|
||||
The [source files](tfe5) of the text editor `tfe` will be shown in the next section.
|
||||
|
||||
You can also download the files from the [repository](https://github.com/ToshioCP/Gtk4-tutorial).
|
||||
You can download the files from the [repository](https://github.com/ToshioCP/Gtk4-tutorial).
|
||||
There are two options.
|
||||
|
||||
- Use git and clone.
|
||||
|
|
|
@ -1,24 +1,19 @@
|
|||
# tfe5 source files
|
||||
# How to build tfe (text file editor)
|
||||
|
||||
## 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).
|
||||
|
||||
The following is the instruction of compilation and execution.
|
||||
|
||||
- You need meson and ninja.
|
||||
- Set environment variables if necessary.
|
||||
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.
|
||||
|
||||
~~~
|
||||
$ . env.sh
|
||||
~~~
|
||||
- change your current directory to `src/tfe5` directory.
|
||||
- type `meson _build` for configuration.
|
||||
- type `ninja -C _build` for compilation.
|
||||
- If you have installed gtk4 from the source, you need to set environment variables to suit your installation.
|
||||
- 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.
|
||||
- type `_build/tfe` to execute it.
|
||||
- Type `_build/tfe` to execute it.
|
||||
|
||||
Then the window appears.
|
||||
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.
|
||||
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
|
||||
|
||||
@@@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
|
||||
@@@
|
||||
|
||||
|
|
371
src/sec2.src.md
|
@ -2,371 +2,40 @@
|
|||
|
||||
This section describes how to install GTK 4 into Linux distributions.
|
||||
|
||||
This tutorial is without any warranty.
|
||||
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.
|
||||
There are two ways to install GTK 4.
|
||||
|
||||
- Install it from the distribution packages.
|
||||
- Build it from the source file.
|
||||
- Install a GNOME 40 distribution with the GNOME Boxes.
|
||||
- Build it from the source files.
|
||||
|
||||
## Installation from the distribution packages
|
||||
|
||||
The first way is easy to install.
|
||||
It is a recommended way.
|
||||
I've installed GTK 4 packages in Ubuntu 21.04.
|
||||
(Now, my Ubuntu version is 21.10).
|
||||
The first way is the best and easiest way to install it.
|
||||
I've installed GTK 4 packages (version 4.8.1) in Ubuntu 22.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.
|
||||
See [Installing GTK from packages](https://www.gtk.org/docs/installations/linux#installing-gtk-from-packages).
|
||||
It is important to install the developing package (libgtk-4-dev).
|
||||
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.
|
||||
|
||||
@@@table
|
||||
|Distribution|version|GTK 4|GNOME 40|
|
||||
|Distribution|version|GTK 4|GNOME|
|
||||
|:-:|:-:|:-:|:-:|
|
||||
|Fedora|36|4.4.2|GNOME 42|
|
||||
|Ubuntu|22.04lts|4.4|GNOME 41(4.6.2)|
|
||||
|Debian|bookworm(testing)|4.6.5|GNOME 42|
|
||||
|Arch|rolling release|4.6.5|GNOME 42|
|
||||
|Gentoo|rolling release|4.6.5|GNOME 42|
|
||||
|OpenSUSE|Tumbleweed(rolling release)|4.6.5|GNOME 42|
|
||||
|Fedora|37|4.8.2-2|GNOME 43|
|
||||
|Ubuntu|22.10|4.8.1|GNOME 43.0|
|
||||
|Debian|bookworm(testing)|4.8.2|GNOME 43|
|
||||
|Arch|rolling release|4.8.2|GNOME 43|
|
||||
|Gentoo|rolling release|4.8.2|GNOME 43|
|
||||
|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
|
||||
|
||||
If your operating system doesn't have GTK 4 packages, 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.
|
||||
|
||||
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.
|
||||
If you want to install a developing version of GTK 4, you 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.
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
### 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?
|
||||
Applications are software that runs using libraries, which includes the
|
||||
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 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 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.
|
||||
|
@ -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.
|
||||
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.
|
||||
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.
|
||||
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
|
||||
simply handler, then the function is invoked when the signal emits.
|
||||
|
||||
|
@ -79,7 +90,7 @@ The flow is like this:
|
|||
|
||||
1. Something happens.
|
||||
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.
|
||||
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.
|
||||
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`.
|
||||
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`.
|
||||
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.
|
||||
For example, "activate" signal is in GApplication section in [GIO API Reference](https://docs.gtk.org/gio/signal.Application.activate.html).
|
||||
The handler function is described in it.
|
||||
It is described in the [GObject API Reference](https://docs.gtk.org/gobject/func.signal_connect.html).
|
||||
Correctly, `g_signal_connect` is a macro (not a C function).
|
||||
|
||||
~~~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.
|
||||
You should see and understand it to write Gtk applications.
|
||||
They are located in ['GTK Documentation'](https://docs.gtk.org/).
|
||||
You should see and understand 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.
|
||||
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
|
||||
(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
|
||||
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.
|
||||
|
||||
|
@ -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));
|
||||
~~~
|
||||
|
||||
You need to cast `win` to GtkWindow and `app` to GtkApplication.
|
||||
`GTK_WINDOW` and `GTK_APPLICATION` macro is appropriate for that.
|
||||
You need to cast `win` to GtkWindow and `app` to GtkApplication with `GTK_WINDOW` and `GTK_APPLICATION` macro.
|
||||
|
||||
GtkApplication continues to run until the related window is destroyed.
|
||||
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.
|
||||
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
|
||||
|
@ -262,8 +299,8 @@ Click on the close button then the window disappears and the program finishes.
|
|||
### GtkApplicationWindow
|
||||
|
||||
GtkApplicationWindow is a child object of GtkWindow.
|
||||
It has some extra functionality for better integration with GtkApplication.
|
||||
It is recommended to use it instead of GtkWindow when you use GtkApplication.
|
||||
It has some extra feature for better integration with 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.
|
||||
|
||||
|
|
|
@ -32,6 +32,8 @@ cd misc; diff pr4.c lb1.c
|
|||
|
||||
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 title of the window is changed.
|
||||
- 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
|
||||
|
||||
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.
|
||||
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.
|
||||
The following program shows how to catch the signal and do something.
|
||||
|
||||
@@@include
|
||||
misc/lb2.c
|
||||
|
@ -66,7 +68,7 @@ misc/lb2.c
|
|||
Look at the line 17 to 19.
|
||||
First, it creates a GtkButton instance `btn` with a label "Click me".
|
||||
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.
|
||||
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'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.
|
||||
The following code is `lb3.c`.
|
||||
|
||||
|
@ -96,15 +98,19 @@ cd misc; diff lb2.c lb3.c
|
|||
|
||||
The changes are:
|
||||
|
||||
- The function `g_print` in `lb2.c` was deleted and the two lines above are inserted instead.
|
||||
- The label of `btn` is changed from "Click me" to "Quit".
|
||||
- The function `g_print` in `lb2.c` was deleted and two lines are inserted.
|
||||
- `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 most important change is the fourth argument of `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).
|
||||
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.
|
||||
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`](https://docs.gtk.org/gobject/func.signal_connect.html).
|
||||
|
||||
### 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}
|
||||
|
||||
The program `lb4.c` includes these widgets.
|
||||
It is as follows.
|
||||
The program `lb4.c` is as follows.
|
||||
|
||||
@@@include
|
||||
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.
|
||||
|
||||
box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
|
||||
gtk_box_set_homogeneous (GTK_BOX (box), TRUE);
|
||||
~~~C
|
||||
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 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 unit of the length is pixel.
|
||||
|
||||
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.
|
||||
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}
|
||||
|
||||
The handler corresponds to `btn1` toggles its label.
|
||||
|
|
|
@ -16,16 +16,26 @@ Look at line 25.
|
|||
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.
|
||||
"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.
|
||||
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.
|
||||
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.
|
||||
|
||||
Now compile and run it.
|
||||
|
||||
```
|
||||
$ cd src/tfv
|
||||
$ comp tfv1
|
||||
$ ./a.out
|
||||
```
|
||||
|
||||
![GtkTextView](../image/screenshot_tfv1.png){width=6.3cm height=5.325cm}
|
||||
|
||||
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.
|
||||
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
|
||||
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.
|
||||
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.
|
||||
|
||||
### GtkScrolledWindow
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
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
|
||||
tfv/tfv2.c
|
||||
@@@
|
||||
|
||||
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.
|
||||
|
|
175
src/sec6.src.md
|
@ -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.
|
||||
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
|
||||
|
||||
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 as a pointer to a character array. They behaves like a string type
|
||||
which you may be familiar from other languages.
|
||||
So, this pointer is often called 'a string'.
|
||||
String is not a C type such as char, int, float or double, but a character array.
|
||||
It behaves like a string in other languages.
|
||||
So, the 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
|
||||
char a[10], *b;
|
||||
|
@ -28,50 +27,50 @@ b = a;
|
|||
/* *(++b) is 'e' */
|
||||
~~~
|
||||
|
||||
The array `a` has `char` elements and the size of ten.
|
||||
The first six elements are 'H', 'e', 'l', 'l', 'o' and '\0'.
|
||||
This array represents the string "Hello".
|
||||
The first five elements are character codes that correspond to the characters.
|
||||
The sixth element is '\0', which is the same as zero,
|
||||
and indicates that the string ends there.
|
||||
The size of the array is 10, so 4 bytes aren't used, but that's OK,
|
||||
they are just ignored.
|
||||
An array `a` is defined as a `char` type array and its size is ten.
|
||||
The first five elements are 'H', 'e', 'l', 'l', 'o'.
|
||||
They are character codes.
|
||||
For example, 'H' is the same as 0x48 or 72.
|
||||
The sixth element is '\0', which is the same as zero, and indicates that the sequence of the data ends there.
|
||||
The array represents the string "Hello".
|
||||
|
||||
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.
|
||||
Because `b` is assigned to be `a`, `a` and `b` point the same character ('H').
|
||||
The variable `a` is defined as an array and it can't be changed.
|
||||
It always point the top address of the array.
|
||||
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`.
|
||||
It is assigned with `a`, so `b` points the first element of `a` (character 'H').
|
||||
The array `a` is immutable.
|
||||
So `a=a+1` causes syntax error.
|
||||
|
||||
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.
|
||||
A NULL string on the other hand will be a pointer which points to a location
|
||||
that contains `\0`, which is a string of length 0 (or "").
|
||||
Programs that use strings will include bugs if you aren't careful when using NULL pointers.
|
||||
It is different from empty string.
|
||||
Empty string is a pointer points `\0`.
|
||||
|
||||
Another annoying problem is the memory that a string is allocated.
|
||||
There are four cases:
|
||||
|
||||
- The string is read only;
|
||||
- The string is in static memory area;
|
||||
- The string is in stack; and
|
||||
- The string is in memory allocated from the heap area.
|
||||
- The string is read only
|
||||
- The string is in static memory area
|
||||
- The string is in stack
|
||||
- The string is in memory allocated from the heap area
|
||||
|
||||
## 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
|
||||
char *s;
|
||||
s = "Hello"
|
||||
~~~
|
||||
|
||||
"Hello" is a string literal, and is stored in program memory.
|
||||
A string literal is read only.
|
||||
In the program above, `s` points the string literal.
|
||||
|
||||
"Hello" is a string literal, and is read only.
|
||||
So, the following program is illegal.
|
||||
|
||||
~~~C
|
||||
|
@ -81,25 +80,38 @@ So, the following program is illegal.
|
|||
The result is undefined.
|
||||
Probably a bad thing will happen, for example, a segmentation fault.
|
||||
|
||||
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 your program
|
||||
by using the `string` command.
|
||||
NOTE: The memory of the literal string is allocated when the program is compiled.
|
||||
It is possible to see the literal strings with `strings` 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
|
||||
|
||||
If a string is defined as an array, it's in either stored in the static memory area or stack.
|
||||
This depends on the class of the array.
|
||||
If a string is defined as an array, it's stored in static memory area or stack.
|
||||
It depends on the class of the array.
|
||||
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.
|
||||
This area can be changed and is writable.
|
||||
The allocated memory lives for the life of the program.
|
||||
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`.
|
||||
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.
|
||||
|
||||
~~~C
|
||||
|
||||
static char a[] = {'H', 'e', 'l', 'l', 'o', '\0'};
|
||||
|
||||
void
|
||||
|
@ -114,30 +126,28 @@ print_strings (void) {
|
|||
}
|
||||
~~~
|
||||
|
||||
The array `a` is defined externally to a function and is global in its scope.
|
||||
Such variables are placed in 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),
|
||||
and then creates code that allocates six bytes in the static memory area and copies the data to this memory.
|
||||
The array `a` is defined out of functions.
|
||||
It is placed in the static memory area even if the `static` class is left out.
|
||||
The compiler calculates the number of the elements (six) and allocates six bytes in the static memory area.
|
||||
Then, it copies "Hello" literal string data to the memory.
|
||||
|
||||
The array `b` is defined inside the function
|
||||
so its class is `auto`.
|
||||
The array `b` is defined inside the function, so its class is `auto`.
|
||||
The compiler calculates the number of the elements in the string literal.
|
||||
It has six elements as the zero termination character is also included.
|
||||
The compiler creates code which allocates six bytes memory in the stack and copies the data to the memory.
|
||||
It is six because it has '\0' terminator.
|
||||
The compiler allocates six bytes in the stack and copies "Hello" litaral string to the stack memory.
|
||||
|
||||
Both `a` and `b` are writable.
|
||||
|
||||
The memory is managed by the executable program.
|
||||
You don't need your program to allocate or free the memory for `a` and `b`.
|
||||
The array `a` is created then the program is first run and remains for the life of the program.
|
||||
The array `b` is created on the stack then the function is called, disappears when the function returns.
|
||||
The memory is allocated and freed by the program automatically so you don't need to allocate or free.
|
||||
The array `a` is alive during the program's life time.
|
||||
The array `b` is alive when the function is called until the function returns to the caller.
|
||||
|
||||
## 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.
|
||||
GLib provides the functions `g_new` and `g_free` to do the same thing, with support for
|
||||
some additional GLib functionality.
|
||||
GLib provides the functions `g_new` and `g_free`.
|
||||
They are similar to `malloc` and `free`.
|
||||
|
||||
~~~C
|
||||
g_new (struct_type, n_struct)
|
||||
|
@ -195,9 +205,9 @@ s = g_strdup ("Hello");
|
|||
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.
|
||||
`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_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 function reference will describe if the returned value needs to be freed.
|
||||
If you forget to free the allocated memory it will remain allocated. Repeated use will cause
|
||||
more memory to be allocated to the program, which will grow over time. This is called a memory leak,
|
||||
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.
|
||||
If you forget to free the allocated memory it will remain until the program ends.
|
||||
Repeated allocation and no freeing cause memory leak.
|
||||
It is a bug and may bring a serious problem.
|
||||
|
||||
Some GLib functions return a string which mustn't be freed by the caller.
|
||||
## const qualifier
|
||||
|
||||
~~~C
|
||||
const char *
|
||||
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.
|
||||
A `const` qualified variable can be assigned to initialize it.
|
||||
Once it is initialized, it is never allowed to change or free.
|
||||
|
||||
~~~C
|
||||
const int x = 10; /* initialization is OK. */
|
||||
|
||||
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
|
||||
)
|
||||
~~~
|
||||
|
||||
|
||||
|
||||
|
|
194
src/sec7.src.md
|
@ -4,51 +4,40 @@
|
|||
|
||||
### G\_APPLICATION\_HANDLES\_OPEN flag
|
||||
|
||||
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, we'll take the easiest one.
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
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.
|
||||
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
|
||||
GtkApplication *
|
||||
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.
|
||||
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).
|
||||
|
||||
~~~
|
||||
GApplicationFlags' Members
|
||||
For further information, see [GIO API Reference -- ApplicationFlags](https://docs.gtk.org/gio/flags.ApplicationFlags.html) and
|
||||
[GIO API Reference -- g\_application\_run](https://docs.gtk.org/gio/method.Application.run.html).
|
||||
|
||||
G_APPLICATION_FLAGS_NONE Default. (No argument allowed)
|
||||
... ... ...
|
||||
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.
|
||||
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.
|
||||
|
||||
The flag `G_APPLICATION_HANDLES_OPEN` 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.
|
||||
The application assumes all the arguments are filenames.
|
||||
|
||||
~~~C
|
||||
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
|
||||
|
||||
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.
|
||||
- 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.
|
||||
|
||||
~~~C
|
||||
void user_function (GApplication *application,
|
||||
gpointer files,
|
||||
gint n_files,
|
||||
gchar *hint,
|
||||
gpointer user_data)
|
||||
void
|
||||
open (
|
||||
GApplication* self,
|
||||
gpointer files,
|
||||
gint n_files,
|
||||
gchar* hint,
|
||||
gpointer user_data
|
||||
)
|
||||
~~~
|
||||
|
||||
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]
|
||||
- `n_files` --- the number of the elements of `files`
|
||||
- `hint` --- a hint provided by the calling instance (usually it can be ignored)
|
||||
- `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
|
||||
|
||||
### 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.
|
||||
|
||||
- When arguments are given, it treats 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.
|
||||
- If it fails to open the file, it will show an error message and quit.
|
||||
- If there's no argument, it will shows an error message and quit.
|
||||
- If there are two or more arguments, the second one and any others are ignored.
|
||||
- When arguments are given, it recognizes the first argument as a filename and opens it.
|
||||
- The second argument and after are ignored.
|
||||
- If there's no argument, it 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 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
|
||||
tfv/tfv3.c
|
||||
|
@ -108,25 +98,29 @@ Then compile and run it.
|
|||
|
||||
![File viewer](../image/screenshot_tfv3.png){width=6.3cm height=5.325cm}
|
||||
|
||||
Let's explain how the program `tfv3.c` works.
|
||||
First, the function `main` has only two changes from the previous version.
|
||||
The function `main` has only two changes from the previous version.
|
||||
|
||||
- `G_APPLICATION_FLAGS_NONE` is replaced by `G_APPLICATION_HANDLES_OPEN`; and
|
||||
- `g_signal_connect (app, "open", G_CALLBACK (on_open), NULL)` is added.
|
||||
- `G_APPLICATION_DEFAULT_FLAGS` is replaced by `G_APPLICATION_HANDLES_OPEN`
|
||||
- `g_signal_connect (app, "open", G_CALLBACK (app_open), NULL)` is added.
|
||||
|
||||
Next, the handler `app_activate` is added and is very simple.
|
||||
It just outputs the error message and
|
||||
the application quits immediately because no window is created.
|
||||
When the flag `G_APPLICATION_HANDLES_OPEN` is given to `gtk_application_new` function, the application behaves like this:
|
||||
|
||||
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;
|
||||
- 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 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 handler `app_activate` becomes very simple.
|
||||
It just outputs the error message and return to the caller.
|
||||
Then the application quits immediately because no window is created.
|
||||
|
||||
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
|
||||
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);
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
} else
|
||||
g_printerr ("File can't be opened.\n");
|
||||
gtk_window_destroy (GTK_WINDOW (win));
|
||||
}
|
||||
~~~
|
||||
|
||||
The function `g_file_load_contents` loads the file contents into a buffer,
|
||||
which is automatically allocated and sets `contents` to point that buffer.
|
||||
The length of the buffer is set to `length`.
|
||||
It returns `TRUE` if the file's contents are successfully loaded and `FALSE` if an error occurs.
|
||||
The function `g_file_load_contents` loads the file contents into a temporary buffer,
|
||||
which is automatically allocated and sets `contents` to point the buffer.
|
||||
The length of the buffer is assigned to `length`.
|
||||
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,
|
||||
frees the buffer pointed by `contents`, sets the title of the window,
|
||||
If it has successfully read the file, it inserts the contents into GtkTextBuffer,
|
||||
frees the temporary buffer pointed by `contents`, sets the title of 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 is a container widget that uses tabs and contains multiple children.
|
||||
The child that is displayed depends on which tab has been selected.
|
||||
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.
|
||||
|
||||
![GtkNotebook](../image/screenshot_gtk_notebook.png){width=13.2cm height=5.325cm}
|
||||
|
||||
Looking at the screenshots above,
|
||||
the left one is the window at the startup.
|
||||
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` are shown instead.
|
||||
This is shown in the right screenshot.
|
||||
The left image is the window at the startup.
|
||||
The file `pr1.c` is shown and its filename is in the left tab.
|
||||
After clicking on the right tab, the contents of the file `tfv1.c` is shown.
|
||||
The right image is the screenshot.
|
||||
|
||||
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 `tfv4.c` and is:
|
||||
The following is `tfv4.c`.
|
||||
It has GtkNoteBook widget.
|
||||
It is inserted as a child of GtkApplicationWindow and contains multiple GtkScrolledWindow.
|
||||
|
||||
@@@include
|
||||
tfv/tfv4.c
|
||||
|
@ -180,34 +176,26 @@ tfv/tfv4.c
|
|||
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.
|
||||
|
||||
- 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".
|
||||
- 25: The size of the window is set to maximum because a big window is appropriate for file viewers.
|
||||
- 27-28 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.
|
||||
- 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.
|
||||
- 39-40 inserts the contents of the file into GtkTextBuffer and frees the memory pointed by `contents`.
|
||||
- 41-43: Gets the filename and creates GtkLabel with the filename and then frees `filename`.
|
||||
- 44-45: If `filename` is NULL, creates GtkLabel with the empty string.
|
||||
- 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:
|
||||
|
||||
~~~
|
||||
GtkNotebook -- GtkNotebookPage -- GtkScrolledWindow
|
||||
~~~
|
||||
|
||||
- 47: Gets GtkNotebookPage widget and sets `nbp` to point to this GtkNotebookPage.
|
||||
- 48: GtkNotebookPage has a property set called "tab-expand".
|
||||
- 24: The default size of the window is 600x400.
|
||||
- 26-27 GtkNotebook is created and inserted to the GtkApplicationWindow as a child.
|
||||
- 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.
|
||||
- 31-36 GtkScrollledWindow, GtkTextView are created. GtkTextBuffer is got from the GtkTextView.
|
||||
The GtkTextView is connected to the GtkScrolledWindow as a child.
|
||||
- 38-39 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..
|
||||
- 43-44: If it fails to take the filename, empty string GtkLabel is created.
|
||||
- 45-46: Appends a GtkScrolledWindow to the GtkNotebook as a child.
|
||||
And the GtkLabel is set as the child's tab.
|
||||
At the same time, a GtkNoteBookPage is created automatically.
|
||||
The function `gtk_notebook_get_page` returns the GtkNotebookPage of the child (GtkScrolledWindow).
|
||||
- 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.
|
||||
`g_object_set` is a general function to set properties in objects.
|
||||
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.
|
||||
- 52-53: If `filename` is NULL, the "No valid file is given" message is outputted.
|
||||
- 55-58: If at least one file was read, then the number of GtkNotebookPage is greater than zero.
|
||||
If it's true, it shows the window.
|
||||
If it's false, it destroys the window, which causes the program to quit.
|
||||
`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).
|
||||
- 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.
|
||||
- 51-52: If `filename` is NULL, the "No valid file is given" message is displayed.
|
||||
- 54-57: If at least one page exists, the window is shown.
|
||||
Otherwise, the window is destroyed and the application quits.
|
||||
|
|
199
src/sec8.src.md
|
@ -1,60 +1,62 @@
|
|||
# Defining a child object
|
||||
# Defining a final class
|
||||
|
||||
## 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.
|
||||
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
|
||||
write the program from scratch, we just add two things to the file viewer:
|
||||
GtkTextView is a multi-line editor.
|
||||
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.
|
||||
- A function to write the file.
|
||||
- Pointers to GFile instances.
|
||||
- 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
|
||||
- Make a child object, which can extend the instance memory for the GFile object.
|
||||
- Use global variables
|
||||
- Make a child class of GtkTextView and its each instance holds a pointer to the GFile instance.
|
||||
|
||||
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,
|
||||
|
||||
~~~C
|
||||
GFile *f[20];
|
||||
~~~
|
||||
|
||||
The variable `f[i]` corresponds to the file associated to 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.
|
||||
The variable `f[i]` corresponds to the file associated with the i-th GtkNotebookPage.
|
||||
|
||||
Making a child object is a good idea in terms of maintenance.
|
||||
One thing you need to be careful of is the difference between "child object" and "child widget".
|
||||
Here we are describing a "child object".
|
||||
A child object includes, and expands on its parent object, as
|
||||
a child object derives everything from the parent object.
|
||||
However, There are two problems.
|
||||
The first is the size of the array.
|
||||
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.
|
||||
The second is difficulty to maintain the program.
|
||||
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}
|
||||
|
||||
We will define TfeTextView as a child object 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.
|
||||
## How to define a child class of GtkTextView
|
||||
|
||||
In general, this is how GObjects work. Understanding the general theory about Gobject's 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 [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.
|
||||
You need to know GObject system convention.
|
||||
First, look at the program below.
|
||||
|
||||
~~~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.
|
||||
Tfe and TextView.
|
||||
Tfe is called the prefix, namespace or module.
|
||||
TextView is called the object.
|
||||
Tfe is called prefix or namespace.
|
||||
TextView is called object.
|
||||
- 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 ().
|
||||
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.
|
||||
- Next, use 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).
|
||||
This definition is put before G\_DECLARE\_FINAL\_TYPE macro.
|
||||
- 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.
|
||||
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.
|
||||
The second member and after are members of the child object.
|
||||
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).
|
||||
- Define instance init function (tfe\_text\_view\_init).
|
||||
Usually you don't need to do anything.
|
||||
- Define class init function (tfe\_text\_view\_class\_init).
|
||||
You don't need to do anything in this object.
|
||||
- Write function codes you want to add (tfe\_text\_view\_set\_file and tfe\_text\_view\_get\_file).
|
||||
`tv` is a pointer to the TfeTextView object instance which is a C-structure declared with the tag \_TfeTextView.
|
||||
So, the structure has a member `file` as a pointer to a GFile instance.
|
||||
`tv->file = f` is an assignment of `f` to a member `file` of the structure pointed by `tv`.
|
||||
This is an example how to use the extended memory in a child widget.
|
||||
- Write a function to create an instance.
|
||||
This macro is mainly used to register the new class to the type system.
|
||||
Type system is a base system of GObject.
|
||||
Every class has its own type.
|
||||
The types of GObject, GtkWidget and TfeTextView are G\_TYPE\_OBJECT, GTK\_TYPE\_WIDGET and TFE\_TYPE\_TEXT\_VIEW respectively.
|
||||
Such type (for example, TFE\_TYPE\_TEXT\_VIEW) is a macro and it is expanded to a function (tfe\_text\_view\_get\_type()).
|
||||
It returns a integer which is unique among all GObject system classes.
|
||||
- Instance init function (tfe\_text\_view\_init) is called when the instance is created.
|
||||
It is the same as a constructor in other object oriented languages.
|
||||
- Class init function (tfe\_text\_view\_class\_init) is called when the class is created.
|
||||
- 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.
|
||||
If the parent object function needs parameters, this function also need them.
|
||||
You sometimes might want to add some parameters.
|
||||
It's your choice.
|
||||
Use g\_object\_new function to create the instance.
|
||||
The arguments are (prefix)\_TYPE\_(object), a list to initialize properties and NULL.
|
||||
In this code no property needs to be initialized.
|
||||
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.
|
||||
|
||||
This program is not perfect.
|
||||
It has some problems.
|
||||
It will be modified later.
|
||||
This program shows the outline how to define a child class.
|
||||
|
||||
## Close-request signal
|
||||
|
||||
|
@ -154,10 +156,9 @@ After you finish editing, you exit the editor.
|
|||
The editor updates files just before the window closes.
|
||||
|
||||
GtkWindow emits the "close-request" signal before it closes.
|
||||
We connect the signal and the handler `before_close`.
|
||||
A handler is a C function.
|
||||
When a function is connected to a certain signal, we call it a handler.
|
||||
The function `before_close` is invoked when the signal "close-request" is emitted.
|
||||
We will connect the signal and the handler `before_close`.
|
||||
(A handler is a C function which is connected to a signal.)
|
||||
The function `before_close` is called when the signal "close-request" is emitted.
|
||||
|
||||
~~~C
|
||||
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.
|
||||
|
||||
- 15: Gets the number of pages `nb` has.
|
||||
- 16-29: For loop with regard to the index to each pages.
|
||||
- 17-19: Gets GtkScrolledWindow, TfeTextView and a pointer to GFile.
|
||||
The pointer was stored when `app_open` handler had run. It will be shown later.
|
||||
- 20-22: Gets GtkTextBuffer and contents. `start_iter` and `end_iter` are iterators of the buffer.
|
||||
I don't want to explain them now because it would take a lot of time.
|
||||
Just remember these lines for the present.
|
||||
- 23-27: Writes the contents to the file.
|
||||
If it fails, it outputs an error message.
|
||||
- 28: Frees `contents`.
|
||||
- 14: The number of pages of `nb` is assigned to `n`.
|
||||
- 15-31: For loop with regard to the index to each pages.
|
||||
- 16-18: `scr`, `tv` and `file` is assigned pointers to the GtkScrolledWindow, TfeTextView and GFile.
|
||||
The GFile of TfeTextView was stored when `app_open` handler was called. It will be shown later.
|
||||
- 19-21: `tb` is assigned the GtkTextBuffer of the TfeTextView.
|
||||
The buffer is accessed with iterators.
|
||||
Iterators points somewhere in the buffer.
|
||||
The function `gtk_text_buffer_get_bounds` assigns the start and end of the buffer to `start_iter` and `end_iter` respectively.
|
||||
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.
|
||||
- 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
|
||||
|
||||
The following is the complete source code of `tfe1.c`.
|
||||
The following is the whole source code of `tfe1.c`.
|
||||
|
||||
@@@include
|
||||
tfe/tfe1.c
|
||||
@@@
|
||||
|
||||
- 107: Sets the pointer to GFile into TfeTextView.
|
||||
`files[i]` is a pointer to GFile structure.
|
||||
It will be freed by the system. So you need to copy it.
|
||||
`g_file_dup` duplicates the given GFile structure.
|
||||
- 123: Connects "close-request" signal and `before_close` handler.
|
||||
The fourth argument is called user data and it is given to the signal handler.
|
||||
- 110: The GFile pointer of the TfeTextView is set with `files[i]`, which is a GFile created with the command line argument.
|
||||
But the GFile will be destroyed by the system later.
|
||||
So it needs to be copied before the assignment.
|
||||
`g_file_dup` duplicates the GFile.
|
||||
- 126: The "close-request" signal is connected to `before_close` 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.
|
||||
|
||||
Now compile and run it.
|
||||
There's a sample file in the directory `tfe`.
|
||||
Type `./a.out taketori.txt`.
|
||||
Now it's time to compile and run.
|
||||
|
||||
~~~
|
||||
$ cd src/tfe
|
||||
$ comp tfe1
|
||||
$ ./a.out taketori.txt`.
|
||||
~~~
|
||||
|
||||
Modify the contents and close the window.
|
||||
Make sure that the file is modified.
|
||||
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
# The User Interface (UI) file and GtkBuilder
|
||||
# GtkBuilder and UI file
|
||||
|
||||
## New, Open and Save button
|
||||
|
||||
In the last section we made the almost simplest editor possible.
|
||||
It reads files in the `app_open` function at start-up and writes them out when closing the window.
|
||||
It works but is not very good.
|
||||
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.
|
||||
Signals and handlers will be explained later.
|
||||
|
||||
![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
|
||||
@@@
|
||||
|
||||
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.
|
||||
- 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.
|
||||
- 41-44: Creates four buttons.
|
||||
- 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.
|
||||
It is appended to `boxv` as the second child.
|
||||
|
||||
The number of lines to build the widgets is 33(=57-25+1).
|
||||
We also needed many additional variables (`boxv`, `boxh`, `dmy1`, ...),
|
||||
most of which weren't necessary, except for building the widgets.
|
||||
The number of widget-build lines is 33(=57-25+1).
|
||||
We also needed many variables (`boxv`, `boxh`, `dmy1`, ...) and most of them used only for building the widgets.
|
||||
Are there any good solution to reduce these work?
|
||||
|
||||
Gtk provides GtkBuilder.
|
||||
|
@ -48,32 +46,30 @@ It reduces this cumbersome work.
|
|||
|
||||
## 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
|
||||
tfe/tfe3.ui
|
||||
@@@
|
||||
|
||||
The structure of this file is XML.
|
||||
Constructs that begin with `<` and end with `>` are called tags.
|
||||
The is a XML file.
|
||||
Tags begin with `<` and end with `>`.
|
||||
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.
|
||||
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.
|
||||
|
||||
- 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 can't recognize it and a fatal error occurs.
|
||||
- 3-6: An object with `GtkApplicationWindow` class and `win` id is defined.
|
||||
- 1: XML declaration.
|
||||
It specifies that the XML version is 1.0 and the encoding is UTF-8.
|
||||
- 3-6: An object tag with `GtkApplicationWindow` class and `win` id.
|
||||
This is the top level window.
|
||||
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.
|
||||
- 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 `win`.
|
||||
The `title` property is "file editor", `default-width` property is 600 and `default-height` property is 400.
|
||||
- 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 `win`.
|
||||
|
||||
Compare this ui file and the lines 25-57 in the source code of `app_open` function.
|
||||
Those two describe the same structure of widgets.
|
||||
Compare this ui file and the lines 25-57 in the `tfe2.c` source code.
|
||||
Both builds the same window with its descendant widgets.
|
||||
|
||||
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 builds widgets based on the ui file.
|
||||
GtkBuilder builds widgets based on a ui file.
|
||||
|
||||
~~~C
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
|
||||
@@@shell
|
||||
|
@ -123,12 +119,11 @@ tfe/tfe3.c app_open
|
|||
@@@
|
||||
|
||||
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
|
||||
|
||||
GtkBuilder can build widgets using string.
|
||||
Use the function `gtk_builder_new_from_string` instead of `gtk_builder_new_from_file`.
|
||||
GtkBuilder can build widgets with string.
|
||||
Use `gtk_builder_new_from_string` instead of `gtk_builder_new_from_file`.
|
||||
|
||||
~~~C
|
||||
char *uistring;
|
||||
|
@ -146,19 +141,19 @@ uistring =
|
|||
... ... ...
|
||||
"</interface>";
|
||||
|
||||
build = gtk_builder_new_from_stringfile (uistring);
|
||||
build = gtk_builder_new_from_string (uistring, -1);
|
||||
~~~
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
- Add backslash before each double quote.
|
||||
- Add double quotes at the left and right of the string in each line.
|
||||
|
||||
### Using Gresource
|
||||
### Gresource
|
||||
|
||||
Using Gresource is similar to using string.
|
||||
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).
|
||||
However, this xml has only one gresource.
|
||||
- 3: The gresource has a prefix `/com/github/ToshioCP/tfe3`.
|
||||
- 4: The gresource has `tfe3.ui`.
|
||||
And it is pointed by `/com/github/ToshioCP/tfe3/tfe3.ui` because it needs prefix.
|
||||
If you want to add more files, then insert them between line 4 and 5.
|
||||
- 4: The name of the gresource is `tfe3.ui`.
|
||||
The resource will be pointed with `/com/github/ToshioCP/tfe3/tfe3.ui` by GtkBuilder.
|
||||
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`.
|
||||
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.
|
||||
|
||||
$ 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.
|
||||
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.
|
||||
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.
|
||||
|
|
|
@ -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.
|
||||
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.
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#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 ()
|
||||
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
|
||||
tfe_text_view_init (TfeTextView *tv) {
|
||||
tv->file = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -23,7 +24,7 @@ tfe_text_view_class_init (TfeTextViewClass *class) {
|
|||
|
||||
void
|
||||
tfe_text_view_set_file (TfeTextView *tv, GFile *f) {
|
||||
tv -> file = f;
|
||||
tv->file = f;
|
||||
}
|
||||
|
||||
GFile *
|
||||
|
@ -39,8 +40,7 @@ tfe_text_view_new (void) {
|
|||
/* ---------- end of the definition of TfeTextView ---------- */
|
||||
|
||||
static gboolean
|
||||
before_close (GtkWindow *win, gpointer user_data) {
|
||||
GtkWidget *nb = GTK_WIDGET (user_data);
|
||||
before_close (GtkWindow *win, GtkWidget *nb) {
|
||||
GtkWidget *scr;
|
||||
GtkWidget *tv;
|
||||
GFile *file;
|
||||
|
@ -61,22 +61,25 @@ before_close (GtkWindow *win, gpointer user_data) {
|
|||
gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter);
|
||||
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)) {
|
||||
pathname = g_file_get_path (file);
|
||||
g_print ("ERROR : Can't save %s.", pathname);
|
||||
g_free (pathname);
|
||||
if ((pathname = g_file_get_path (file)) != NULL) {
|
||||
g_printerr ("Can't save %s.", pathname);
|
||||
g_free (pathname);
|
||||
} else
|
||||
g_printerr ("Pathname not exist.\n");
|
||||
}
|
||||
g_free (contents);
|
||||
g_object_unref (file);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
app_activate (GApplication *app, gpointer user_data) {
|
||||
app_activate (GApplication *app) {
|
||||
g_print ("You need to give filenames as arguments.\n");
|
||||
}
|
||||
|
||||
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 *nb;
|
||||
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));
|
||||
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 ();
|
||||
gtk_window_set_child (GTK_WINDOW (win), nb);
|
||||
|
|
|
@ -39,12 +39,12 @@ tfe_text_view_new (void) {
|
|||
/* ---------- end of the definition of TfeTextView ---------- */
|
||||
|
||||
static void
|
||||
app_activate (GApplication *app, gpointer user_data) {
|
||||
app_activate (GApplication *app) {
|
||||
g_print ("You need a filename argument.\n");
|
||||
}
|
||||
|
||||
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 *nb;
|
||||
GtkWidget *lab;
|
||||
|
@ -142,4 +142,3 @@ main (int argc, char **argv) {
|
|||
g_object_unref (app);
|
||||
return stat;
|
||||
}
|
||||
|
||||
|
|
|
@ -39,12 +39,12 @@ tfe_text_view_new (void) {
|
|||
/* ---------- end of the definition of TfeTextView ---------- */
|
||||
|
||||
static void
|
||||
app_activate (GApplication *app, gpointer user_data) {
|
||||
app_activate (GApplication *app) {
|
||||
g_print ("You need a filename argument.\n");
|
||||
}
|
||||
|
||||
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 *nb;
|
||||
GtkWidget *lab;
|
||||
|
|
|
@ -39,12 +39,12 @@ tfe_text_view_new (void) {
|
|||
/* ---------- end of the definition of TfeTextView ---------- */
|
||||
|
||||
static void
|
||||
app_activate (GApplication *app, gpointer user_data) {
|
||||
app_activate (GApplication *app) {
|
||||
g_print ("You need a filename argument.\n");
|
||||
}
|
||||
|
||||
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 *nb;
|
||||
GtkWidget *lab;
|
||||
|
|
|
@ -2,8 +2,10 @@ require 'rake/clean'
|
|||
|
||||
targetfile = "tfe"
|
||||
srcfiles = FileList["tfe.c", "tfetextview.c", "resources.c"]
|
||||
uifile = "tfe.ui"
|
||||
rscfile = srcfiles[2]
|
||||
objfiles = srcfiles.gsub(/.c$/, '.o')
|
||||
objfiles = srcfiles.ext(".o")
|
||||
gresource_xml = "tfe.gresource.xml"
|
||||
|
||||
CLEAN.include(targetfile, objfiles, rscfile)
|
||||
|
||||
|
@ -14,12 +16,12 @@ file targetfile => objfiles do |t|
|
|||
end
|
||||
|
||||
objfiles.each do |obj|
|
||||
src = obj.gsub(/.o$/,'.c')
|
||||
src = obj.ext(".c")
|
||||
file obj => src do |t|
|
||||
sh "gcc -c -o #{t.name} `pkg-config --cflags gtk4` #{t.source}"
|
||||
end
|
||||
end
|
||||
|
||||
file rscfile => ["tfe.gresource.xml", "tfe.ui"] do |t|
|
||||
sh "glib-compile-resources #{t.prerequisites[0]} --target=#{t.name} --generate-source"
|
||||
file rscfile => uifile do |t|
|
||||
sh "glib-compile-resources #{gresource_xml} --target=#{t.name} --generate-source"
|
||||
end
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
#include "tfetextview.h"
|
||||
|
||||
static void
|
||||
app_activate (GApplication *app, gpointer user_data) {
|
||||
app_activate (GApplication *app) {
|
||||
g_print ("You need a filename argument.\n");
|
||||
}
|
||||
|
||||
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 *nb;
|
||||
GtkWidget *lab;
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
#include <gtk/gtk.h>
|
||||
|
||||
#include "../tfetextview/tfetextview.h"
|
||||
#include "tfenotebook.h"
|
|
@ -1,4 +1,7 @@
|
|||
#include "tfe.h"
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "../tfetextview/tfetextview.h"
|
||||
#include "tfenotebook.h"
|
||||
|
||||
static void
|
||||
open_cb (GtkNotebook *nb) {
|
||||
|
@ -96,4 +99,3 @@ main (int argc, char **argv) {
|
|||
g_object_unref (app);
|
||||
return stat;
|
||||
}
|
||||
|
||||
|
|
|
@ -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. */
|
||||
static gchar*
|
||||
static char*
|
||||
get_untitled () {
|
||||
static int c = -1;
|
||||
if (++c == 0)
|
||||
|
@ -67,14 +69,14 @@ notebook_page_close (GtkNotebook *nb) {
|
|||
}
|
||||
}
|
||||
|
||||
// filename is owned by the caller.
|
||||
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 ();
|
||||
GtkNotebookPage *nbp;
|
||||
GtkWidget *lab;
|
||||
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);
|
||||
lab = gtk_label_new (filename);
|
||||
i = gtk_notebook_append_page (nb, scr, lab);
|
||||
|
@ -89,13 +91,15 @@ open_response (TfeTextView *tv, int response, GtkNotebook *nb) {
|
|||
GFile *file;
|
||||
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_unref (tv);
|
||||
}else {
|
||||
file = tfe_text_view_get_file (tv);
|
||||
filename = g_file_get_basename (file);
|
||||
g_object_unref (file);
|
||||
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 */
|
||||
filename = g_file_get_basename (file);
|
||||
notebook_page_build (nb, tv, filename);
|
||||
g_free (filename);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -136,5 +141,5 @@ notebook_page_new (GtkNotebook *nb) {
|
|||
return;
|
||||
filename = get_untitled ();
|
||||
notebook_page_build (nb, tv, filename);
|
||||
g_free (filename);
|
||||
}
|
||||
|
||||
|
|
9
src/tfetextview/test/meson.build
Normal 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)
|
76
src/tfetextview/test/test_open.c
Normal 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;
|
||||
}
|
||||
|
106
src/tfetextview/test/test_save.c
Normal 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;
|
||||
}
|
||||
|
|
@ -73,24 +73,23 @@ static gboolean
|
|||
save_file (GFile *file, GtkTextBuffer *tb, GtkWindow *win) {
|
||||
GtkTextIter start_iter;
|
||||
GtkTextIter end_iter;
|
||||
gchar *contents;
|
||||
char *contents;
|
||||
gboolean stat;
|
||||
GtkWidget *message_dialog;
|
||||
GError *err = NULL;
|
||||
|
||||
gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter);
|
||||
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);
|
||||
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,
|
||||
GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
|
||||
"%s.\n", err->message);
|
||||
GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s.", err->message);
|
||||
g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
|
||||
gtk_widget_show (message_dialog);
|
||||
g_error_free (err);
|
||||
stat = FALSE;
|
||||
}
|
||||
g_free (contents);
|
||||
return stat;
|
||||
|
@ -107,10 +106,17 @@ saveas_dialog_response (GtkWidget *dialog, gint response, TfeTextView *tv) {
|
|||
if (! G_IS_FILE (file))
|
||||
g_warning ("TfeTextView: gtk_file_chooser_get_file returns non GFile.\n");
|
||||
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);
|
||||
tv->file = file;
|
||||
g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
|
||||
if (! (G_IS_FILE (tv->file) && g_file_equal (tv->file, file))) {
|
||||
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
|
||||
g_object_unref (file);
|
||||
}
|
||||
|
@ -128,7 +134,7 @@ tfe_text_view_save (TfeTextView *tv) {
|
|||
return; /* no need to save it */
|
||||
else if (tv->file == NULL)
|
||||
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");
|
||||
else
|
||||
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 */
|
||||
g_object_unref (file);
|
||||
message_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog), GTK_DIALOG_MODAL,
|
||||
GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
|
||||
"%s.\n", err->message);
|
||||
GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s", err->message);
|
||||
g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
|
||||
gtk_widget_show (message_dialog);
|
||||
g_error_free (err);
|
||||
|
@ -197,12 +202,18 @@ open_dialog_response(GtkWidget *dialog, gint response, TfeTextView *tv) {
|
|||
} else {
|
||||
gtk_text_buffer_set_text (tb, contents, length);
|
||||
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))
|
||||
g_object_unref (tv->file);
|
||||
tv->file = file;
|
||||
gtk_text_buffer_set_modified (tb, FALSE);
|
||||
tv->file = file; // The ownership of 'file' moves to TfeTextView
|
||||
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));
|
||||
}
|
||||
|
@ -210,20 +221,19 @@ open_dialog_response(GtkWidget *dialog, gint response, TfeTextView *tv) {
|
|||
void
|
||||
tfe_text_view_open (TfeTextView *tv, GtkWindow *win) {
|
||||
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;
|
||||
|
||||
dialog = gtk_file_chooser_dialog_new ("Open file", win, GTK_FILE_CHOOSER_ACTION_OPEN,
|
||||
"Cancel", GTK_RESPONSE_CANCEL,
|
||||
"Open", GTK_RESPONSE_ACCEPT,
|
||||
NULL);
|
||||
"Cancel", GTK_RESPONSE_CANCEL, "Open", GTK_RESPONSE_ACCEPT, NULL);
|
||||
g_signal_connect (dialog, "response", G_CALLBACK (open_dialog_response), tv);
|
||||
gtk_widget_show (dialog);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
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));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# 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
|
||||
- GFile *[tfe_text_view_get_file ()](#tfe_text_view_get_file)
|
||||
|
@ -39,8 +39,8 @@ GObject
|
|||
|
||||
## Description
|
||||
|
||||
TfeTextView holds GFile which the contents of GtkTextBuffer corresponds to.
|
||||
File manipulation functions are added to this object.
|
||||
TfeTextView holds GFile corresponds to the contents of GtkTextBuffer.
|
||||
It has some file manipulation 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.
|
||||
This function doesn't do any I/O operations.
|
||||
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.
|
||||
The caller needs to set a handler to this signal in advance.
|
||||
|
||||
|
@ -83,9 +83,9 @@ void
|
|||
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.
|
||||
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
|
||||
|
||||
|
@ -99,7 +99,7 @@ tfe_text_view_saveas (TfeTextView *tv);
|
|||
~~~
|
||||
|
||||
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
|
||||
|
||||
|
@ -174,6 +174,13 @@ Members:
|
|||
- TFE_OPEN_RESPONSE_CANCEL: Reading file is canceled by the user.
|
||||
- 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
|
||||
|
||||
### change-file
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include <gtk/gtk.h>
|
||||
|
||||
static void
|
||||
app_activate (GApplication *app, gpointer user_data) {
|
||||
app_activate (GApplication *app) {
|
||||
GtkWidget *win;
|
||||
GtkWidget *tv;
|
||||
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. "
|
||||
"The girl was shining faintly. "
|
||||
"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. "
|
||||
;
|
||||
win = gtk_application_window_new (GTK_APPLICATION (app));
|
||||
|
@ -37,7 +37,7 @@ main (int argc, char **argv) {
|
|||
GtkApplication *app;
|
||||
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);
|
||||
stat = g_application_run (G_APPLICATION (app), argc, argv);
|
||||
g_object_unref (app);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include <gtk/gtk.h>
|
||||
|
||||
static void
|
||||
app_activate (GApplication *app, gpointer user_data) {
|
||||
app_activate (GApplication *app) {
|
||||
GtkWidget *win;
|
||||
GtkWidget *scr;
|
||||
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. "
|
||||
"The girl was shining faintly. "
|
||||
"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. "
|
||||
;
|
||||
win = gtk_application_window_new (GTK_APPLICATION (app));
|
||||
|
@ -41,7 +41,7 @@ main (int argc, char **argv) {
|
|||
GtkApplication *app;
|
||||
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);
|
||||
stat = g_application_run (G_APPLICATION (app), argc, argv);
|
||||
g_object_unref (app);
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
#include <gtk/gtk.h>
|
||||
|
||||
static void
|
||||
app_activate (GApplication *app, gpointer user_data) {
|
||||
g_print ("You need a filename argument.\n");
|
||||
app_activate (GApplication *app) {
|
||||
g_printerr ("You need a filename argument.\n");
|
||||
}
|
||||
|
||||
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 *scr;
|
||||
GtkWidget *tv;
|
||||
|
@ -37,9 +37,10 @@ app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer
|
|||
gtk_widget_show (win);
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
} else
|
||||
g_printerr ("File can't be opened.\n");
|
||||
gtk_window_destroy (GTK_WINDOW (win));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
#include <gtk/gtk.h>
|
||||
|
||||
static void
|
||||
app_activate (GApplication *app, gpointer user_data) {
|
||||
g_print ("You need a filename argument.\n");
|
||||
app_activate (GApplication *app) {
|
||||
g_printerr ("You need a filename argument.\n");
|
||||
}
|
||||
|
||||
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 *nb;
|
||||
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));
|
||||
gtk_window_set_title (GTK_WINDOW (win), "file viewer");
|
||||
gtk_window_set_default_size (GTK_WINDOW (win), 400, 300);
|
||||
gtk_window_maximize (GTK_WINDOW (win));
|
||||
gtk_window_set_default_size (GTK_WINDOW (win), 600, 400);
|
||||
|
||||
nb = gtk_notebook_new ();
|
||||
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);
|
||||
g_object_set (nbp, "tab-expand", TRUE, 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);
|
||||
} 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)
|
||||
gtk_widget_show (win);
|
||||
|
|