mirror of
https://github.com/ToshioCP/Gtk4-tutorial.git
synced 2025-01-12 20:03:28 +01:00
Store html files into "docs" directory so that the github page works.
This commit is contained in:
parent
1863e32902
commit
43ec2d2a10
35 changed files with 14116 additions and 11 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -28,7 +28,6 @@ src/list5/_build
|
|||
src/expression/_build
|
||||
src/column/_build
|
||||
latex/*
|
||||
html/*
|
||||
|
||||
# backup file
|
||||
*~
|
||||
|
|
22
Rakefile
22
Rakefile
|
@ -13,14 +13,16 @@ otherfiles = ["src/turtle/turtle_doc.src.md",
|
|||
srcfiles = secfiles + otherfiles
|
||||
abstract = Src_file.new "src/abstract.src.md"
|
||||
|
||||
# docs is a directory for html files.
|
||||
html_dir = 'docs'
|
||||
mdfiles = srcfiles.map {|file| "gfm/" + file.to_md}
|
||||
htmlfiles = srcfiles.map {|file| "html/" + file.to_html}
|
||||
htmlfiles = srcfiles.map {|file| "#{html_dir}/" + file.to_html}
|
||||
sectexfiles = secfiles.map {|file| "latex/" + file.to_tex}
|
||||
othertexfiles = otherfiles.map {|file| "latex/" + file.to_tex}
|
||||
texfiles = sectexfiles + othertexfiles
|
||||
abstract_tex = "latex/"+abstract.to_tex
|
||||
|
||||
["gfm", "html", "latex"].each{|d| Dir.mkdir(d) unless Dir.exist?(d)}
|
||||
["gfm", html_dir, "latex"].each{|d| Dir.mkdir(d) unless Dir.exist?(d)}
|
||||
|
||||
CLEAN.append(*mdfiles)
|
||||
CLEAN << "Readme.md"
|
||||
|
@ -69,10 +71,10 @@ pair(srcfiles, mdfiles).each do |src, dst, i|
|
|||
end
|
||||
end
|
||||
|
||||
task html: %w[html/index.html] + htmlfiles
|
||||
task html: %W[#{html_dir}/index.html] + htmlfiles
|
||||
|
||||
file "html/index.html" => [abstract] + secfiles do
|
||||
abstract_md = "html/#{abstract.to_md}"
|
||||
file "#{html_dir}/index.html" => [abstract] + secfiles do
|
||||
abstract_md = "#{html_dir}/#{abstract.to_md}"
|
||||
src2md abstract, abstract_md, "html"
|
||||
buf = ["# Gtk4 Tutorial for beginners\n", "\n"]\
|
||||
+ File.readlines(abstract_md)\
|
||||
|
@ -82,15 +84,15 @@ file "html/index.html" => [abstract] + secfiles do
|
|||
h = File.open(secfile.path){|file| file.readline}.sub(/^#* */,"").chomp
|
||||
buf << "1. [#{h}](#{secfile.to_html})\n"
|
||||
end
|
||||
File.write("html/index.md", buf.join)
|
||||
sh "pandoc -o html/index.html html/index.md"
|
||||
File.delete "html/index.md"
|
||||
add_head_tail_html "html/index.html"
|
||||
File.write("#{html_dir}/index.md", buf.join)
|
||||
sh "pandoc -o #{html_dir}/index.html #{html_dir}/index.md"
|
||||
File.delete "#{html_dir}/index.md"
|
||||
add_head_tail_html "#{html_dir}/index.html"
|
||||
end
|
||||
|
||||
pair(srcfiles, htmlfiles).each do |src, dst, i|
|
||||
file dst => [src] + src.c_files do
|
||||
html_md = "html/#{src.to_md}"
|
||||
html_md = "#{html_dir}/#{src.to_md}"
|
||||
src2md src, html_md, "html"
|
||||
if src.instance_of? Sec_file
|
||||
if secfiles.size == 1
|
||||
|
|
368
docs/Readme_for_developers.html
Normal file
368
docs/Readme_for_developers.html
Normal file
|
@ -0,0 +1,368 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>Gtk4 tutorial for beginners</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
body {width: 1080px; margin: 0 auto; font-size: large;}
|
||||
h2 {padding: 10px; background-color: #d0f0d0; }
|
||||
div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
|
||||
th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
|
||||
td {padding: 2px 6px; border: 1px solid;}
|
||||
img {display: block; margin-left: auto; margin-right: auto;}
|
||||
figcaption {text-align: center;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1 id="how-to-build-gtk4-tutorial">How to build Gtk4 Tutorial</h1>
|
||||
<h2 id="quick-start-guide">Quick start guide</h2>
|
||||
<ol type="1">
|
||||
<li>You need linux operationg system, ruby, rake, pandoc and latex system.</li>
|
||||
<li>download this repository and uncompress the files.</li>
|
||||
<li>change your current directory to the top directory of the source files.</li>
|
||||
<li>type <code>rake html</code> to create html files. The files are created under <code>html</code> directory.</li>
|
||||
<li>type <code>rake pdf</code> to create pdf file. The file is created under <code>latex</code> directory.</li>
|
||||
</ol>
|
||||
<h2 id="prerequisites">Prerequisites</h2>
|
||||
<ul>
|
||||
<li>Linux operationg system. The programs in the repository has been tested on Ubuntu 21.04.</li>
|
||||
<li>Download the files in this repository. There are two ways to download.
|
||||
<ol type="1">
|
||||
<li>Use git. Type <code>git clone https://github.com/ToshioCP/Gtk4-tutorial.git</code> on the command-line.</li>
|
||||
<li>Download a zip file. Click on the code button (green button) in the top page of this repository. Then, click “Download ZIP”.</li>
|
||||
</ol></li>
|
||||
<li>Ruby and rake.</li>
|
||||
<li>Pandoc. It is used to convert markdown to html and latex.</li>
|
||||
<li>Latex system. Texlive2020 or later version is recommended. It is used to generate pdf file.</li>
|
||||
</ul>
|
||||
<h2 id="github-flavored-markdown">Github flavored markdown</h2>
|
||||
<p>When you see <a href="https://github.com/ToshioCP/Gtk4-tutorial">gtk4_tutorial github page</a>, you’ll find the contents of <code>Readme.md</code> below the list of files. This file is written in markdown language. A markdown file has <code>.md</code> suffix.</p>
|
||||
<p>There are several kinds of markdown language. <code>Readme.md</code> uses ‘github flavored markdown’, which is often shortened as GFM. Markdown files in the <code>gfm</code> directory also written in GFM. If you are not familiar with it, refer to the page <a href="https://github.github.com/gfm/">github flavor markdown spec</a>.</p>
|
||||
<h2 id="pandocs-markdown">Pandoc’s markdown</h2>
|
||||
<p>This tutorial also uses another markdown – pandoc’s markdown. Pandoc is a converter between markdown, html, latex, word docx and so on. This type of markdown is used to convert markdown to html and latex.</p>
|
||||
<h2 id="src.md-file">Src.md file</h2>
|
||||
<p>Src.md file has “.src.md” suffix. The syntax of .src.md file is similar to markdown but it has a special command which isn’t included in markdown syntax. It is @@@ command. The command starts with a line that begins with “@@@” and it ends with a line “@@@”. For example,</p>
|
||||
<pre><code>@@@include
|
||||
tfeapplication.c
|
||||
@@@</code></pre>
|
||||
<p>There are four types of @@@ command.</p>
|
||||
<h3 id="include">@@<span class="citation" data-cites="include">@include</span></h3>
|
||||
<p>This type of @@@ command starts with a line “@@<span class="citation" data-cites="include">@include</span>”.</p>
|
||||
<pre><code>@@@include
|
||||
tfeapplication.c
|
||||
@@@</code></pre>
|
||||
<p>This command replaces itself with the text read from the C source files surrounded by <code>@@@include</code> and <code>@@@</code>. If a function list follows the filename, only the functions are read. If no function list is given, the command can read any text file other than C source file.</p>
|
||||
<pre><code>@@@include
|
||||
tfeapplication.c main startup
|
||||
@@@</code></pre>
|
||||
<p>The command above is replaced by the contents of <code>main</code> and <code>startup</code> functions in <code>tfeapplication.c</code>.</p>
|
||||
<pre><code>@@@include
|
||||
lib_src2md.rb
|
||||
@@@</code></pre>
|
||||
<p>This command is replaced by the contents of <code>lib_src2md.rb</code> which is a ruby script (not C file).</p>
|
||||
<p>The inserted text is converted to fence code block. Fence code block begins with <code>~~~</code> and ends with <code>~~~</code>. The contents are displayed verbatim. <code>~~~</code> is look like a fence so the block is called “fence code block”.</p>
|
||||
<p>If the target markdown is GFM, then an info string follows the beginning fence. The following example shows that the @@@ command includes a C source file <code>sample.c</code>.</p>
|
||||
<pre><code>$ cat src/sample.c
|
||||
int
|
||||
main (int argc, char **argv) {
|
||||
... ...
|
||||
}
|
||||
$cat src/sample.src.md
|
||||
... ...
|
||||
@@@include -N
|
||||
sample.c
|
||||
@@@
|
||||
... ...
|
||||
$ ruby src2md.rb src/sample.src.md gfm/sample.md
|
||||
$ cat gfm/sample.md
|
||||
... ...
|
||||
~~~C
|
||||
int
|
||||
main (int argc, char **argv) {
|
||||
... ...
|
||||
}
|
||||
~~~
|
||||
... ...</code></pre>
|
||||
<p>Info strings are usually languages like C, ruby, xml and so on. This string is decided with the filename extension.</p>
|
||||
<ul>
|
||||
<li><code>.c</code> => C</li>
|
||||
<li><code>.rb</code> => ruby</li>
|
||||
<li><code>.xml</code> => xml</li>
|
||||
</ul>
|
||||
<p>The supported language is written in line 290 and 291 in <code>lib/lib_src2md.rb</code>.</p>
|
||||
<p>A line number is inserted at the top of each line in the code block. If you don’t want to insert it, give “-N” option to @@<span class="citation" data-cites="include">@include</span> command.</p>
|
||||
<p>Options:</p>
|
||||
<ul>
|
||||
<li><code>-n</code>: Inserts a line number at the top of each line (default).</li>
|
||||
<li><code>-N</code>: No line number is inserted.</li>
|
||||
</ul>
|
||||
<p>The following shows that line numbers are inserted at the beginning of lines.</p>
|
||||
<pre><code>$cat src/sample.src.md
|
||||
... ...
|
||||
@@@include
|
||||
sample.c
|
||||
@@@
|
||||
... ...
|
||||
$ ruby src2md.rb src/sample.src.md gfm/sample.md
|
||||
$ cat gfm/sample.md
|
||||
... ...
|
||||
~~~C
|
||||
1 int
|
||||
2 main (int argc, char **argv) {
|
||||
... ...
|
||||
14 }
|
||||
~~~
|
||||
... ...</code></pre>
|
||||
<p>If the target markdown is an intermediate file to html, then another type of info string follows the beginning fence. If @@<span class="citation" data-cites="include">@include</span> command doesn’t have -N option, then the generated markdown is:</p>
|
||||
<pre><code>~~~{.C .numberLines}
|
||||
int
|
||||
main (int argc, char **argv) {
|
||||
... ...
|
||||
}
|
||||
~~~</code></pre>
|
||||
<p>The info string <code>.C</code> specifies C language. The info string <code>.numberLines</code> is a class of the pandoc markdown. If the class is given, pandoc generates CSS to insert line numbers to the source code in the html file. That’s why the fence code block in the markdown doesn’t have line numbers, which is different from gfm markdown. If <code>-N</code> option is given, then the info string is <code>{.C}</code> only.</p>
|
||||
<p>If the target markdown is an intermediate file to latex, then the same info string follows the beginning fence.</p>
|
||||
<pre><code>~~~{.C .numberLines}
|
||||
int
|
||||
main (int argc, char **argv) {
|
||||
... ...
|
||||
}
|
||||
~~~</code></pre>
|
||||
<p>Rake uses pandoc with –listings option when it converts markdown to latex. The generated latex file uses listings package to list source files instead of verbatim environment. The markdwon above is converted to the following latex source file.</p>
|
||||
<pre><code>\begin{lstlisting}[language=C, numbers=left]
|
||||
int
|
||||
main (int argc, char **argv) {
|
||||
... ...
|
||||
}
|
||||
\end{lstlisting}</code></pre>
|
||||
<p>Listing package can color or emphasize keywords, strings, comments and directives. But it doesn’t analyze the syntax or token of the language, so the kind of emphasis target is limited.</p>
|
||||
<p>@@<span class="citation" data-cites="include">@include</span> command have two advantages.</p>
|
||||
<ol type="1">
|
||||
<li>Less typing.</li>
|
||||
<li>You don’t need to modify your .src.md file, even if the C source file is modified.</li>
|
||||
</ol>
|
||||
<h3 id="shell">@@<span class="citation" data-cites="shell">@shell</span></h3>
|
||||
<p>This type of @@@ command starts with a line begins with “@@<span class="citation" data-cites="shell">@shell</span>”.</p>
|
||||
<pre><code>@@@shell
|
||||
shell command
|
||||
... ...
|
||||
@@@</code></pre>
|
||||
<p>This command replaces itself with:</p>
|
||||
<ul>
|
||||
<li>the shell command</li>
|
||||
<li>the standard output from the shell command</li>
|
||||
</ul>
|
||||
<p>For example,</p>
|
||||
<pre><code>@@@shell
|
||||
wc Rakefile
|
||||
@@@</code></pre>
|
||||
<p>This is converted to:</p>
|
||||
<pre><code>~~~
|
||||
$ wc Rakefile
|
||||
164 475 4971 Rakefile
|
||||
~~~</code></pre>
|
||||
<h3 id="if-series">@@<span class="citation" data-cites="if">@if</span> series</h3>
|
||||
<p>This type of @@@ command starts with a line begins with “@@<span class="citation" data-cites="if">@if</span>”, and followed by “@@<span class="citation" data-cites="elif">@elif</span>”, “@@<span class="citation" data-cites="else">@else</span>” or “@@<span class="citation" data-cites="end">@end</span>”. This command is similar to “#if”, “#elif”, #else" and “#endif” directives in C preprocessor. For example,</p>
|
||||
<pre><code>@@@if gfm
|
||||
Refer to [tfetextview API reference](../src/tfetextview_doc.md)
|
||||
@@@elif html
|
||||
Refer to [tfetextview API reference](../src/tfetextview_doc.html)
|
||||
@@@elif latex
|
||||
Refer to tfetextview API reference in appendix.
|
||||
@@@end</code></pre>
|
||||
<p><code>@@@if</code> and <code>@@@elif</code> have conditions. They are <code>gfm</code>, <code>html</code> or <code>latex</code> so far.</p>
|
||||
<ul>
|
||||
<li>gfm: if the target is GFM</li>
|
||||
<li>html: if the target is html</li>
|
||||
<li>latex: if the target is latex (or pdf).</li>
|
||||
</ul>
|
||||
<p>Other type of conditions may be available in the future version.</p>
|
||||
<p>The code analyzing @@<span class="citation" data-cites="if">@if</span> series command is rather complicated. It is based on the state diagram below.</p>
|
||||
<figure>
|
||||
<img src="../image/state_diagram.png" alt="" /><figcaption>state diagram</figcaption>
|
||||
</figure>
|
||||
<h3 id="table">@@<span class="citation" data-cites="table">@table</span></h3>
|
||||
<p>This type of @@@ command starts with a line begins with “@@<span class="citation" data-cites="table">@table</span>”. The contents of this command is a table of GFM or pandoc’s markdown. The command makes a table easy to read. For example, a text file <code>sample.md</code> has a table like this:</p>
|
||||
<pre><code>Price list
|
||||
|
||||
@@@table
|
||||
|item|price|
|
||||
|:---:|:---:|
|
||||
|mouse|$10|
|
||||
|PC|$500|
|
||||
@@@</code></pre>
|
||||
<p>The command changes this into:</p>
|
||||
<pre><code>Price list
|
||||
|
||||
|item |price|
|
||||
|:---:|:---:|
|
||||
|mouse| $10 |
|
||||
| PC |$500 |</code></pre>
|
||||
<p>This command just changes the appearance of the table. There’s no influence on html/latex files that is converted from the markdown.</p>
|
||||
<p>A script <code>mktbl.rb</code> supports this command. If you run the script like this:</p>
|
||||
<pre><code>$ ruby mktbl.rb sample.md</code></pre>
|
||||
<p>Then, the appearance of the table will be changed The script also makes a backup file <code>sample.md.bak</code>.</p>
|
||||
<p>The task of the script seems easy, but the program is not so simple. The script <code>mktbl.rb</code> uses a library <code>lib/lib_src2md.rb</code></p>
|
||||
<p>@@<span class="citation" data-cites="commands">@commands</span> are effective in the whole text. This means you can’t stop the @@<span class="citation" data-cites="commands">@commands</span>. But sometimes you want to show the commands literally like this document. One solution is to add four blanks at the top of the line. Then @@<span class="citation" data-cites="commands">@commands</span> are not effective because @@<span class="citation" data-cites="commands">@commands</span> must be at the top of the line.</p>
|
||||
<h2 id="conversion">Conversion</h2>
|
||||
<p>The @@@ commands above (@@<span class="citation" data-cites="include">@include</span>, @@<span class="citation" data-cites="shell">@shell</span>, @@<span class="citation" data-cites="if">@if</span> series and @@<span class="citation" data-cites="table">@table</span>) are carried out by a method <code>src2md</code>, which is in the file <code>lib/lib_src2md.rb</code>. This method converts <code>src.md</code> file into <code>md</code> file. In addition, some other conversions are made by <code>src2md</code> method.</p>
|
||||
<ul>
|
||||
<li>Relative links are changed according to the change of the base directory.</li>
|
||||
<li>Size option in image link is removed when the destination is GFM or html.</li>
|
||||
<li>Relative link is removed when the destination is latex.</li>
|
||||
</ul>
|
||||
<p>The order of the conversions are:</p>
|
||||
<ol type="1">
|
||||
<li>@@<span class="citation" data-cites="if">@if</span></li>
|
||||
<li>@@<span class="citation" data-cites="table">@table</span></li>
|
||||
<li>@@<span class="citation" data-cites="include">@include</span></li>
|
||||
<li>@@<span class="citation" data-cites="shell">@shell</span></li>
|
||||
<li>others</li>
|
||||
</ol>
|
||||
<p>There is <code>src2md.rb</code> file in the top directory of this repository. It just invokes the method <code>src2md</code>. In the same way, the method is called in the action in <code>Rakefile</code>.</p>
|
||||
<h2 id="directory-structure">Directory structure</h2>
|
||||
<p>There are six directories under <code>gtk4_tutorial</code> directory. They are <code>gfm</code>, <code>src</code>, <code>image</code>, <code>html</code>, <code>latex</code> and <code>lib</code>. Three directories <code>gfm</code>, <code>html</code> and <code>latex</code> are the destination directories for GFM, html and latex files respectively. It is possible that these three directories don’t exist before the conversion.</p>
|
||||
<ul>
|
||||
<li>src: This directory contains .src.md files and C-related source files.</li>
|
||||
<li>image: This directory contains image files like png or jpg.</li>
|
||||
<li>gfm: <code>rake</code> converts src.md files to GFM files and store them in this directory.</li>
|
||||
<li>html: This directory is empty at first. <code>rake html</code> will convert src.md files to html files and store them in this directory.</li>
|
||||
<li>latex: This directory is empty at first. <code>rake latex</code> will convert src.md files to latex files and store them in this directory. <code>rake pdf</code> creates pdf file in <code>latex</code> directory.</li>
|
||||
<li>lib: This directory includes ruby library files.</li>
|
||||
</ul>
|
||||
<h2 id="src-directory-and-the-top-directory">Src directory and the top directory</h2>
|
||||
<p>Src directory contains .src.md files and C-related source files. The top directory, which is gtk_tutorial directory, contains <code>Rakefile</code>, <code>src2md.rb</code> and some other files. When <code>Readme.md</code> is generated, it will be located at the top directory. <code>Readme.md</code> has title, abstract, table of contents with links to GFM files.</p>
|
||||
<p>Rakefile describes how to convert .src.md files into GFM files. Rake carries out the conversion according to the <code>Rakefile</code>.</p>
|
||||
<h2 id="the-name-of-files-in-src-directory">The name of files in src directory</h2>
|
||||
<p>Each file in <code>src</code> directory is an abstract, sections of the whole document and other .src.md files. An <code>abstract.src.md</code> contains the abstract of this tutorial. Each section filename is “sec”, number of the section and “.src.md” suffix. For example, “sec1.src.md”, “sec5.src.md” or “sec12.src.md”. They are the files correspond to section 1, section 5 and section 12 respectively.</p>
|
||||
<h2 id="c-source-file-directory">C source file directory</h2>
|
||||
<p>Most of .src.md files have <code>@@@include</code> commands and they include C source files. Such C source files are located in the subdirectories of <code>src</code> directory.</p>
|
||||
<p>Those C files have been compiled and tested. When you compile source files, some auxiliary files and a target file like <code>a.out</code> are created. Or <code>_build</code> directory is made when <code>meson</code> and <code>ninja</code> is used when compiling. Those files are not tracked by <code>git</code> because they are specified in <code>.gitignore</code>.</p>
|
||||
<p>The name of the subdirectories should be independent of section names. It is because of renumbering, which will be explained in the next subsection.</p>
|
||||
<h2 id="renumbering">Renumbering</h2>
|
||||
<p>Sometimes you might want to insert a section. For example, you want to insert it between section 4 and section 5. You can make a temporary section 4.5, that is a rational number between 4 and 5. However, section numbers are usually integer so section 4.5 must be changed to section 5. And the numbers of the following sections must be increased by one.</p>
|
||||
<p>This renumbering is done by a method <code>renum!</code> of the class <code>Sec_files</code>. The method and class is written in <code>lib/lib_sec_file.rb</code>.</p>
|
||||
<ul>
|
||||
<li>It changes file names.</li>
|
||||
<li>If there are references to sections in .src.md files, the section numbers will be automatically renumbered.</li>
|
||||
</ul>
|
||||
<h2 id="rakefile">Rakefile</h2>
|
||||
<p>Rakefile is a similar file to Makefile but controlled by rake, which is a make-like program written in ruby. Rakefile in this tutorial has the following tasks.</p>
|
||||
<ul>
|
||||
<li>md: generate GFM markdown files. This is the default.</li>
|
||||
<li>html: generate html files.</li>
|
||||
<li>pdf: generate latex files and a pdf file, which is compiled by lualatex.</li>
|
||||
<li>all: generate md, html and pdf files.</li>
|
||||
</ul>
|
||||
<p>Rake does renumbering before the tasks above.</p>
|
||||
<h2 id="generate-gfm-markdown-files">Generate GFM markdown files</h2>
|
||||
<p>Markdown files (GFM) are generated by rake.</p>
|
||||
<pre><code>$ rake</code></pre>
|
||||
<p>This command generates <code>Readme.md</code> with <code>src/abstract.src.md</code> and titles of .src.md files. At the same time, it converts each .src.md file into GFM file under <code>gfm</code> directory. Navigation lines are added at the top and bottom of each markdown section file.</p>
|
||||
<p>You can describe width and height of images in .src.md files. For example,</p>
|
||||
<pre><code>![sample image](../image/sample_image.png)</code></pre>
|
||||
<p>The size between left brace and right brace is used in latex file and it is not fit to GFM syntax. So the size is removed in the conversion.</p>
|
||||
<p>If a .src.md file has relative URL link, it will be changed by conversion. Because .src.md files are located under <code>src</code> directory and GFM files are located under <code>gfm</code> directory, base URL of GFM files is different from base URL of .src.md files. For example, <code>[src/sample.c](../src/sample.c)</code> is translated to <code>[src/sample.c](../src/sample.c)</code>.</p>
|
||||
<p>If a link points another .src.md file, then the target filename will be changed to .md file. For example, <code>[Section 5](sec5.html)</code> is translated to <code>[Section 5](../src/sec5.md)</code>.</p>
|
||||
<p>If you want to clean the directory, that means remove all the generated markdown files, type <code>rake clean</code>.</p>
|
||||
<pre><code>$ rake clean</code></pre>
|
||||
<p>Sometimes this is necessary before generating GFM files.</p>
|
||||
<pre><code>$ rake clean
|
||||
$ rake</code></pre>
|
||||
<p>For example, if you append a new section and other files are still the same as before, <code>rake clean</code> is necessary. Because the navigation of the previous section of the newly added section needs to be updated. If you don’t do <code>rake clean</code>, then it won’t be updated because the the timestamp of .md file in gfm is newer than the one of .src.md file. In this case, using <code>touch</code> to the previous section .src.md also works to update the file.</p>
|
||||
<p>If you see the github repository (ToshioCP/Gtk4-tutorial), <code>Readme.md</code> is shown below the code. And <code>Readme.md</code> includes links to each markdown files. The repository not only stores source files but also shows the whole tutorial.</p>
|
||||
<h2 id="generate-html-files">Generate html files</h2>
|
||||
<p>Src.md files can be translated to html files. You need pandoc to do this. Most linux distribution has pandoc package. Refer to your distribution document to install it.</p>
|
||||
<p>Type <code>rake html</code> to generate html files.</p>
|
||||
<pre><code>$ rake html</code></pre>
|
||||
<p>First, it generates pandoc’s markdown files under <code>html</code> directory. Then, pandoc converts them to html files. The width and height of image files are removed.</p>
|
||||
<p><code>index.html</code> is the top html file. If you want to clean <code>html</code> directory, type <code>rake cleanhtml</code></p>
|
||||
<pre><code>$ rake cleanhtml</code></pre>
|
||||
<p>Every html file has stylesheet in its header. This is created by <code>lib/lib_add_head_tail_html.rb</code>. This script has a sample markdown code and convert it with pandoc and <code>-s</code> option. Pandoc generates a html file with header. The script extracts the header and use it for html files. You can customize the style by modifying <code>lib/lib_add_head_tail_html.rb</code>.</p>
|
||||
<p><code>html</code> directory contains all the necessary html files. So if you want to upload the html files to your own web site, just upload the files in the <code>html</code> directory.</p>
|
||||
<h2 id="generate-a-pdf-file">Generate a pdf file</h2>
|
||||
<p>You need pandoc to convert markdown files into latex source files.</p>
|
||||
<p>Type <code>rake pdf</code> to generate latex files and finally make a pdf file.</p>
|
||||
<pre><code>$ rake pdf</code></pre>
|
||||
<p>First, it generates pandoc’s markdown files under <code>latex</code> directory. Then, pandoc converts them into latex files. Links to files or directories are removed because latex doesn’t support them. However, links to full URL and image files are kept. Image size is set with the size between the left brace and right brace.</p>
|
||||
<pre><code>![sample image](../image/sample_image.png)</code></pre>
|
||||
<p>You need to specify appropriate width and height. It is almost <code>0.015 x pixels</code> cm. For example, if the width of an image is 400 pixels, the width in a latex file will be almost 6cm.</p>
|
||||
<p>A file <code>main.tex</code> is the root file of all the generated latex files. It has <code>\input</code> commands, which inserts each section file, between <code>\begin{document}</code> and <code>\end{document}</code>. It also has <code>\input</code>, which inserts <code>helper.tex</code>, in the preamble. Two files <code>main.tex</code> and <code>helper.tex</code> are created by <code>lib/lib_gen_main_tex.rb</code>. It has a sample markdown code and converts it witn <code>pandoc -s</code>. Then, it extracts the preamble in the generated file and puts it into <code>helper.tex</code>. You can customize <code>helper.tex</code> by modifying <code>lib/lib_gen_main_tex.rb</code>.</p>
|
||||
<p>Finally, lualatex compiles the <code>main.tex</code> into a pdf file.</p>
|
||||
<p>If you want to clean <code>latex</code> directory, type <code>rake cleanlatex</code></p>
|
||||
<pre><code>$ rake cleanlatex</code></pre>
|
||||
<p>This removes all the latex source files and a pdf file.</p>
|
||||
</body>
|
||||
</html>
|
141
docs/index.html
Normal file
141
docs/index.html
Normal file
|
@ -0,0 +1,141 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>Gtk4 tutorial for beginners</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
body {width: 1080px; margin: 0 auto; font-size: large;}
|
||||
h2 {padding: 10px; background-color: #d0f0d0; }
|
||||
div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
|
||||
th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
|
||||
td {padding: 2px 6px; border: 1px solid;}
|
||||
img {display: block; margin-left: auto; margin-right: auto;}
|
||||
figcaption {text-align: center;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1 id="gtk4-tutorial-for-beginners">Gtk4 Tutorial for beginners</h1>
|
||||
<p>The github page of this tutorial is also available. Click <a href="https://toshiocp.github.io/Gtk4-tutorial/">here</a>.</p>
|
||||
<h4 id="contents-of-this-repository">Contents of this Repository</h4>
|
||||
<p>This tutorial illustrates how to write C programs with the Gtk4 library. It focuses on beginners so the contents are limited to the basics. The table of contents is at the end of this abstract.</p>
|
||||
<ul>
|
||||
<li>Section 3 to 21 describes the basics, with the example of a simple editor <code>tfe</code> (Text File Editor).</li>
|
||||
<li>Section 22 to 25 describes GtkDrawingArea.</li>
|
||||
<li>Section 26 to 29 describes the list model and the list view (GtkListView, GtkGridView and GtkColumnView). It also describes 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>
|
||||
<h4 id="gtk4-documentation">Gtk4 Documentation</h4>
|
||||
<p>Please refer to <a href="https://docs.gtk.org/gtk4/index.html">Gtk 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>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 Gtk4 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 Gtk4 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 <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 Gtk4 Tutorial</a>”) that describes how to make them.</p>
|
||||
<h2 id="table-of-contents">Table of contents</h2>
|
||||
<ol type="1">
|
||||
<li><a href="sec1.html">Prerequisite and License</a></li>
|
||||
<li><a href="sec2.html">Installation of Gtk4 to Linux 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="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="sec10.html">Build system</a></li>
|
||||
<li><a href="sec11.html">Initialization and destruction of instances</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="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>
|
||||
<li><a href="sec20.html">GtkMenuButton, accelerators, font, pango and gsettings</a></li>
|
||||
<li><a href="sec21.html">Template XML and composite widget</a></li>
|
||||
<li><a href="sec22.html">GtkDrawingArea and Cairo</a></li>
|
||||
<li><a href="sec23.html">Periodic Events</a></li>
|
||||
<li><a href="sec24.html">Combine GtkDrawingArea and TfeTextView</a></li>
|
||||
<li><a href="sec25.html">Tiny turtle graphics interpreter</a></li>
|
||||
<li><a href="sec26.html">GtkListView</a></li>
|
||||
<li><a href="sec27.html">GtkGridView and activate signal</a></li>
|
||||
<li><a href="sec28.html">GtkExpression</a></li>
|
||||
<li><a href="sec29.html">GtkColumnView</a></li>
|
||||
</ol>
|
||||
</body>
|
||||
</html>
|
114
docs/sec1.html
Normal file
114
docs/sec1.html
Normal file
|
@ -0,0 +1,114 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>Gtk4 tutorial for beginners</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
body {width: 1080px; margin: 0 auto; font-size: large;}
|
||||
h2 {padding: 10px; background-color: #d0f0d0; }
|
||||
div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
|
||||
th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
|
||||
td {padding: 2px 6px; border: 1px solid;}
|
||||
img {display: block; margin-left: auto; margin-right: auto;}
|
||||
figcaption {text-align: center;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Up: <a href="index.html">index.html</a>, Next: <a href="sec2.html">Section 2</a></p>
|
||||
<h1 id="prerequisite-and-license">Prerequisite and License</h1>
|
||||
<h2 id="prerequisite">Prerequisite</h2>
|
||||
<h3 id="gtk4-on-a-linux-os">Gtk4 on a Linux OS</h3>
|
||||
<p>This tutorial is about Gtk4 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 <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>Gtk4. The stable version of Gtk on Linux distributions is version three at present. You need to install Gtk4 to your computer. See <a href="sec3.html">Section 3</a> for the installation of Gtk4.</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>You need:</p>
|
||||
<ul>
|
||||
<li>Linux distribution like Ubuntu.</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>
|
||||
</ul>
|
||||
<h2 id="license">License</h2>
|
||||
<p>Copyright (C) 2020 ToshioCP (Toshio Sekiya)</p>
|
||||
<p>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.</p>
|
||||
<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 License</a> for more details.</p>
|
||||
<p>Up: <a href="index.html">index.html</a>, Next: <a href="sec2.html">Section 2</a></p>
|
||||
</body>
|
||||
</html>
|
362
docs/sec10.html
Normal file
362
docs/sec10.html
Normal file
|
@ -0,0 +1,362 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>Gtk4 tutorial for beginners</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
body {width: 1080px; margin: 0 auto; font-size: large;}
|
||||
h2 {padding: 10px; background-color: #d0f0d0; }
|
||||
div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
|
||||
th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
|
||||
td {padding: 2px 6px; border: 1px solid;}
|
||||
img {display: block; margin-left: auto; margin-right: auto;}
|
||||
figcaption {text-align: center;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec9.html">Section 9</a>, Next: <a href="sec11.html">Section 11</a></p>
|
||||
<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>
|
||||
<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>
|
||||
</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>
|
||||
<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>
|
||||
</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>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>
|
||||
<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-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-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-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>
|
||||
<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>
|
||||
<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-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-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-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-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-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-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>
|
||||
<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>
|
||||
<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-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-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-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-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-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>
|
||||
<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>
|
||||
<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>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>
|
||||
<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>
|
||||
</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>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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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.
|
||||
<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>
|
||||
</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>Up: <a href="index.html">index.html</a>, Prev: <a href="sec9.html">Section 9</a>, Next: <a href="sec11.html">Section 11</a></p>
|
||||
</body>
|
||||
</html>
|
405
docs/sec11.html
Normal file
405
docs/sec11.html
Normal file
|
@ -0,0 +1,405 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>Gtk4 tutorial for beginners</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
body {width: 1080px; margin: 0 auto; font-size: large;}
|
||||
h2 {padding: 10px; background-color: #d0f0d0; }
|
||||
div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
|
||||
th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
|
||||
td {padding: 2px 6px; border: 1px solid;}
|
||||
img {display: block; margin-left: auto; margin-right: auto;}
|
||||
figcaption {text-align: center;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec10.html">Section 10</a>, Next: <a href="sec12.html">Section 12</a></p>
|
||||
<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, <a href="../src/tfe5">src/tfe5</a> and <a href="../src/tfetextview">src/tfetextview</a>.</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>
|
||||
<ul>
|
||||
<li><code>tfe.c</code> includes everything other than TfeTextView. It should be divided at least into two parts, <code>tfeapplication.c</code> and <code>tfenotebook.c</code>.</li>
|
||||
<li>Header files also need to be organized.</li>
|
||||
</ul>
|
||||
<p>However, first of all, 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 destructing TfeTextView?</li>
|
||||
<li>TfeTextView should read/write a file by itself or not?</li>
|
||||
<li>How it communicates with objects outside?</li>
|
||||
</ul>
|
||||
<p>You need to know at least class, instance and signals before thinking about them. I will explain them in this section and the next section. After that I will explain:</p>
|
||||
<ul>
|
||||
<li>Organizing functions.</li>
|
||||
<li>How to use GtkFileChooserDialog</li>
|
||||
</ul>
|
||||
<h2 id="gobject-and-its-children">GObject and its children</h2>
|
||||
<p>GObject and its children are objects, which have both class and instance. First, think about instance of objects. Instance is structured memory. THe structure is described as C language structure. The following is a structure of TfeTextView.</p>
|
||||
<div class="sourceCode" id="cb1"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true"></a><span class="co">/* This typedef statement is automatically generated by the macro G_DECLARE_FINAL_TYPE */</span></span>
|
||||
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true"></a><span class="kw">typedef</span> <span class="kw">struct</span> _TfeTextView TfeTextView;</span>
|
||||
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true"></a></span>
|
||||
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true"></a><span class="kw">struct</span> _TfeTextView {</span>
|
||||
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true"></a> GtkTextView parent;</span>
|
||||
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true"></a> GFile *file;</span>
|
||||
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true"></a>};</span></code></pre></div>
|
||||
<p>The members of the structure are:</p>
|
||||
<ul>
|
||||
<li>The type of <code>parent</code> is GtkTextView which is C structure. It is declared in <code>gtktextview.h</code>. GtkTextView is the parent of TfeTextView.</li>
|
||||
<li><code>file</code> is a pointer to GFile. It can be NULL if no file corresponds to the TfeTextView object.</li>
|
||||
</ul>
|
||||
<p>Notice the program above is the declaration of the structure, not the definition. So, no memories are allocated at this moment. They are to be allocated when <code>tfe_text_view_new</code> function is invoked. The memory allocated with <code>tfe_text_view_new</code> is an instance of TfeTextView object. Therefore, There can be multiple TfeTextView instances if <code>tfe_text_view_new</code> is called multiple times.</p>
|
||||
<p>You can find the declaration of the ancestors of TfeTextView in the source files of GTK and GLib. The following is extracts from the source files (not exactly the same).</p>
|
||||
<div class="sourceCode" id="cb2"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true"></a><span class="kw">typedef</span> <span class="kw">struct</span> _GObject GObject;</span>
|
||||
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true"></a><span class="kw">typedef</span> <span class="kw">struct</span> _GObject GInitiallyUnowned;</span>
|
||||
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true"></a><span class="kw">struct</span> _GObject</span>
|
||||
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true"></a>{</span>
|
||||
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true"></a> GTypeInstance g_type_instance;</span>
|
||||
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true"></a> <span class="dt">volatile</span> guint ref_count;</span>
|
||||
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true"></a> GData *qdata;</span>
|
||||
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true"></a>};</span>
|
||||
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true"></a></span>
|
||||
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true"></a><span class="kw">typedef</span> <span class="kw">struct</span> _GtkWidget GtkWidget;</span>
|
||||
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true"></a><span class="kw">struct</span> _GtkWidget</span>
|
||||
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true"></a>{</span>
|
||||
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true"></a> GInitiallyUnowned parent_instance;</span>
|
||||
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true"></a> GtkWidgetPrivate *priv;</span>
|
||||
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true"></a>};</span>
|
||||
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true"></a></span>
|
||||
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true"></a><span class="kw">typedef</span> <span class="kw">struct</span> _GtkTextView GtkTextView;</span>
|
||||
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true"></a><span class="kw">struct</span> _GtkTextView</span>
|
||||
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true"></a>{</span>
|
||||
<span id="cb2-20"><a href="#cb2-20" aria-hidden="true"></a> GtkWidget parent_instance;</span>
|
||||
<span id="cb2-21"><a href="#cb2-21" aria-hidden="true"></a> GtkTextViewPrivate *priv;</span>
|
||||
<span id="cb2-22"><a href="#cb2-22" aria-hidden="true"></a>};</span></code></pre></div>
|
||||
<p>In each structure, its parent is declared at the top of the members. So, every ancestors is included in the child instance. This is very important. It guarantees a child widget to inherit all the features from ancestors. The structure of <code>TfeTextView</code> is like the following diagram.</p>
|
||||
<figure>
|
||||
<img src="../image/TfeTextView.png" alt="" /><figcaption>The structure of the instance TfeTextView</figcaption>
|
||||
</figure>
|
||||
<h2 id="initialization-of-a-tfetextview-instance">Initialization of a TfeTextView instance</h2>
|
||||
<p>The function <code>tfe_text_view_new</code> creates a new TfeTextView instance.</p>
|
||||
<div class="sourceCode" id="cb3"><pre class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1"></a>GtkWidget *</span>
|
||||
<span id="cb3-2"><a href="#cb3-2"></a>tfe_text_view_new (<span class="dt">void</span>) {</span>
|
||||
<span id="cb3-3"><a href="#cb3-3"></a> <span class="cf">return</span> GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL));</span>
|
||||
<span id="cb3-4"><a href="#cb3-4"></a>}</span></code></pre></div>
|
||||
<p>When this function is involed, 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>
|
||||
<ul>
|
||||
<li>11: A function pointed by <code>constructor</code> is called when the instance is generated. It completes the initialization of the instance.</li>
|
||||
<li>23: A function pointed by <code>dispose</code> is called when the instance destructs itself. Destruction process is divided into two phases. The first one is called disposing. In this phase, the instance releases all the references to other instances. The second phase is finalizing.</li>
|
||||
<li>24: A function pointed by <code>finalize</code> finishes the destruction process.</li>
|
||||
<li>The other pointers point to functions which are called while the instance lives.</li>
|
||||
</ul>
|
||||
<p>These functions are called class methods. The methods are open to its descendants. But not open to the objects which are not the descendants.</p>
|
||||
<h2 id="tfetextview-class">TfeTextView class</h2>
|
||||
<p>TfeTextView class is a structure and it includes all its ancestors’ class in it.</p>
|
||||
<div class="sourceCode" id="cb8"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true"></a><span class="kw">typedef</span> _TfeTextView TfeTextView;</span>
|
||||
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true"></a><span class="kw">struct</span> _TfeTextView {</span>
|
||||
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true"></a> GtkTextView parent;</span>
|
||||
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true"></a> GFile *file;</span>
|
||||
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true"></a>};</span></code></pre></div>
|
||||
<p>TfeTextView structure has GtkTextView type as the first member. In the same way, GtkTextView has its parent type (GtkWidget) as the first member. GtkWidget has its parent type (GtkInitiallyUnowned) as the first member. The structure of GtkInitiallyUnowned is the same as GObject. Therefore, TFeTextView includes GObject, GtkWidget and GtkTextView in itself.</p>
|
||||
<pre><code>GObject -- GInitiallyUnowned -- GtkWidget -- GtkTextView -- TfeTextView</code></pre>
|
||||
<p>The following is extracts from the source files (not exactly the same).</p>
|
||||
<div class="sourceCode" id="cb10"><pre class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb10-1"><a href="#cb10-1"></a><span class="kw">struct</span> _GtkWidgetClass</span>
|
||||
<span id="cb10-2"><a href="#cb10-2"></a>{</span>
|
||||
<span id="cb10-3"><a href="#cb10-3"></a> GInitiallyUnownedClass parent_class;</span>
|
||||
<span id="cb10-4"><a href="#cb10-4"></a></span>
|
||||
<span id="cb10-5"><a href="#cb10-5"></a> <span class="co">/*< 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-24"><a href="#cb10-24"></a></span>
|
||||
<span id="cb10-25"><a href="#cb10-25"></a> <span class="co">/* size requests */</span></span>
|
||||
<span id="cb10-26"><a href="#cb10-26"></a> GtkSizeRequestMode (* get_request_mode) (GtkWidget *widget);</span>
|
||||
<span id="cb10-27"><a href="#cb10-27"></a> <span class="dt">void</span> (* measure) (GtkWidget *widget,</span>
|
||||
<span id="cb10-28"><a href="#cb10-28"></a> GtkOrientation orientation,</span>
|
||||
<span id="cb10-29"><a href="#cb10-29"></a> <span class="dt">int</span> for_size,</span>
|
||||
<span id="cb10-30"><a href="#cb10-30"></a> <span class="dt">int</span> *minimum,</span>
|
||||
<span id="cb10-31"><a href="#cb10-31"></a> <span class="dt">int</span> *natural,</span>
|
||||
<span id="cb10-32"><a href="#cb10-32"></a> <span class="dt">int</span> *minimum_baseline,</span>
|
||||
<span id="cb10-33"><a href="#cb10-33"></a> <span class="dt">int</span> *natural_baseline);</span>
|
||||
<span id="cb10-34"><a href="#cb10-34"></a></span>
|
||||
<span id="cb10-35"><a href="#cb10-35"></a> <span class="co">/* Mnemonics */</span></span>
|
||||
<span id="cb10-36"><a href="#cb10-36"></a> gboolean (* mnemonic_activate) (GtkWidget *widget,</span>
|
||||
<span id="cb10-37"><a href="#cb10-37"></a> gboolean group_cycling);</span>
|
||||
<span id="cb10-38"><a href="#cb10-38"></a></span>
|
||||
<span id="cb10-39"><a href="#cb10-39"></a> <span class="co">/* explicit focus */</span></span>
|
||||
<span id="cb10-40"><a href="#cb10-40"></a> gboolean (* grab_focus) (GtkWidget *widget);</span>
|
||||
<span id="cb10-41"><a href="#cb10-41"></a> gboolean (* focus) (GtkWidget *widget,</span>
|
||||
<span id="cb10-42"><a href="#cb10-42"></a> GtkDirectionType direction);</span>
|
||||
<span id="cb10-43"><a href="#cb10-43"></a> <span class="dt">void</span> (* set_focus_child) (GtkWidget *widget,</span>
|
||||
<span id="cb10-44"><a href="#cb10-44"></a> GtkWidget *child);</span>
|
||||
<span id="cb10-45"><a href="#cb10-45"></a></span>
|
||||
<span id="cb10-46"><a href="#cb10-46"></a> <span class="co">/* keyboard navigation */</span></span>
|
||||
<span id="cb10-47"><a href="#cb10-47"></a> <span class="dt">void</span> (* move_focus) (GtkWidget *widget,</span>
|
||||
<span id="cb10-48"><a href="#cb10-48"></a> GtkDirectionType direction);</span>
|
||||
<span id="cb10-49"><a href="#cb10-49"></a> gboolean (* keynav_failed) (GtkWidget *widget,</span>
|
||||
<span id="cb10-50"><a href="#cb10-50"></a> GtkDirectionType direction);</span>
|
||||
<span id="cb10-51"><a href="#cb10-51"></a></span>
|
||||
<span id="cb10-52"><a href="#cb10-52"></a> gboolean (* query_tooltip) (GtkWidget *widget,</span>
|
||||
<span id="cb10-53"><a href="#cb10-53"></a> <span class="dt">int</span> x,</span>
|
||||
<span id="cb10-54"><a href="#cb10-54"></a> <span class="dt">int</span> y,</span>
|
||||
<span id="cb10-55"><a href="#cb10-55"></a> gboolean keyboard_tooltip,</span>
|
||||
<span id="cb10-56"><a href="#cb10-56"></a> GtkTooltip *tooltip);</span>
|
||||
<span id="cb10-57"><a href="#cb10-57"></a></span>
|
||||
<span id="cb10-58"><a href="#cb10-58"></a> <span class="dt">void</span> (* compute_expand) (GtkWidget *widget,</span>
|
||||
<span id="cb10-59"><a href="#cb10-59"></a> gboolean *hexpand_p,</span>
|
||||
<span id="cb10-60"><a href="#cb10-60"></a> gboolean *vexpand_p);</span>
|
||||
<span id="cb10-61"><a href="#cb10-61"></a></span>
|
||||
<span id="cb10-62"><a href="#cb10-62"></a> <span class="dt">void</span> (* css_changed) (GtkWidget *widget,</span>
|
||||
<span id="cb10-63"><a href="#cb10-63"></a> GtkCssStyleChange *change);</span>
|
||||
<span id="cb10-64"><a href="#cb10-64"></a></span>
|
||||
<span id="cb10-65"><a href="#cb10-65"></a> <span class="dt">void</span> (* system_setting_changed) (GtkWidget *widget,</span>
|
||||
<span id="cb10-66"><a href="#cb10-66"></a> GtkSystemSetting settings);</span>
|
||||
<span id="cb10-67"><a href="#cb10-67"></a></span>
|
||||
<span id="cb10-68"><a href="#cb10-68"></a> <span class="dt">void</span> (* snapshot) (GtkWidget *widget,</span>
|
||||
<span id="cb10-69"><a href="#cb10-69"></a> GtkSnapshot *snapshot);</span>
|
||||
<span id="cb10-70"><a href="#cb10-70"></a></span>
|
||||
<span id="cb10-71"><a href="#cb10-71"></a> gboolean (* contains) (GtkWidget *widget,</span>
|
||||
<span id="cb10-72"><a href="#cb10-72"></a> <span class="dt">double</span> x,</span>
|
||||
<span id="cb10-73"><a href="#cb10-73"></a> <span class="dt">double</span> y);</span>
|
||||
<span id="cb10-74"><a href="#cb10-74"></a></span>
|
||||
<span id="cb10-75"><a href="#cb10-75"></a> <span class="co">/*< 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-78"><a href="#cb10-78"></a></span>
|
||||
<span id="cb10-79"><a href="#cb10-79"></a> gpointer padding[<span class="dv">8</span>];</span>
|
||||
<span id="cb10-80"><a href="#cb10-80"></a>};</span>
|
||||
<span id="cb10-81"><a href="#cb10-81"></a></span>
|
||||
<span id="cb10-82"><a href="#cb10-82"></a><span class="kw">struct</span> _GtkTextViewClass</span>
|
||||
<span id="cb10-83"><a href="#cb10-83"></a>{</span>
|
||||
<span id="cb10-84"><a href="#cb10-84"></a> GtkWidgetClass parent_class;</span>
|
||||
<span id="cb10-85"><a href="#cb10-85"></a></span>
|
||||
<span id="cb10-86"><a href="#cb10-86"></a> <span class="co">/*< 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-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-118"><a href="#cb10-118"></a></span>
|
||||
<span id="cb10-119"><a href="#cb10-119"></a><span class="co">/* The following definition is generated by the macro G_DECLARE_FINAL_TYPE */</span></span>
|
||||
<span id="cb10-120"><a href="#cb10-120"></a><span class="kw">typedef</span> <span class="kw">struct</span> {</span>
|
||||
<span id="cb10-121"><a href="#cb10-121"></a> GtkTextView parent_class;</span>
|
||||
<span id="cb10-122"><a href="#cb10-122"></a>} TfeTextViewClass;</span></code></pre></div>
|
||||
<ul>
|
||||
<li>120-122: This three lines are generated by the macro <code>G_DECLARE_FINAL_TYPE</code>. So, they are not written in either <code>tfe_text_view.h</code> or <code>tfe_text_view.c</code>.</li>
|
||||
<li>3, 84, 121: Each derived class puts its parent class at the first member of its structure. It is the same as instance structures.</li>
|
||||
<li>Class members in ancestors are open to the descendant class. So, they can be changed in <code>tfe_text_view_class_init</code> function. For example, the <code>dispose</code> pointer in GObjectClass will be overridden later in <code>tfe_text_view_class_init</code>. (Override is an object oriented programming terminology. Override is rewriting ancestors’ class methods in the descendant class.)</li>
|
||||
<li>Some class methods are often overridden. <code>set_property</code>, <code>get_property</code>, <code>dispose</code>, <code>finalize</code> and <code>constructed</code> are such methods.</li>
|
||||
</ul>
|
||||
<p>TfeTextViewClass includes its ancestors’ class in it. It is illustrated in the following diagram.</p>
|
||||
<figure>
|
||||
<img src="../image/TfeTextViewClass.png" alt="" /><figcaption>The structure of TfeTextView Class</figcaption>
|
||||
</figure>
|
||||
<h2 id="destruction-of-tfetextview">Destruction of TfeTextView</h2>
|
||||
<p>Every Object derived from GObject has a reference count. If an object A refers to an object B, then A keeps a pointer to B in A and at the same time increases the reference count of B by one with the function <code>g_object_ref (B)</code>. If A 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>
|
||||
</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>
|
||||
<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-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>
|
||||
<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>
|
||||
</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>
|
||||
<figure>
|
||||
<img src="../image/dispose_handler.png" alt="" /><figcaption>dispose handlers</figcaption>
|
||||
</figure>
|
||||
<p>Now, look at the <code>tfe_text_view_dispose</code> program above. It first releases the reference to GFile object pointed by <code>tv->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>Up: <a href="index.html">index.html</a>, Prev: <a href="sec10.html">Section 10</a>, Next: <a href="sec12.html">Section 12</a></p>
|
||||
</body>
|
||||
</html>
|
206
docs/sec12.html
Normal file
206
docs/sec12.html
Normal file
|
@ -0,0 +1,206 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>Gtk4 tutorial for beginners</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
body {width: 1080px; margin: 0 auto; font-size: large;}
|
||||
h2 {padding: 10px; background-color: #d0f0d0; }
|
||||
div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
|
||||
th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
|
||||
td {padding: 2px 6px; border: 1px solid;}
|
||||
img {display: block; margin-left: auto; margin-right: auto;}
|
||||
figcaption {text-align: center;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec11.html">Section 11</a>, Next: <a href="sec13.html">Section 13</a></p>
|
||||
<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>
|
||||
<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>
|
||||
</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>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>
|
||||
</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>
|
||||
</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>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>
|
||||
<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-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-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>
|
||||
<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>
|
||||
</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>
|
||||
<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>
|
||||
</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>
|
||||
<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>
|
||||
</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>
|
||||
<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>
|
||||
<ul>
|
||||
<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>
|
||||
</ul>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec11.html">Section 11</a>, Next: <a href="sec13.html">Section 13</a></p>
|
||||
</body>
|
||||
</html>
|
402
docs/sec13.html
Normal file
402
docs/sec13.html
Normal file
|
@ -0,0 +1,402 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>Gtk4 tutorial for beginners</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
body {width: 1080px; margin: 0 auto; font-size: large;}
|
||||
h2 {padding: 10px; background-color: #d0f0d0; }
|
||||
div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
|
||||
th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
|
||||
td {padding: 2px 6px; border: 1px solid;}
|
||||
img {display: block; margin-left: auto; margin-right: auto;}
|
||||
figcaption {text-align: center;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec12.html">Section 12</a>, Next: <a href="sec14.html">Section 14</a></p>
|
||||
<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>
|
||||
<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 (TfeTextView, tfe_text_view, TFE, TEXT_VIEW, GtkTextView)</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>
|
||||
<span id="cb2-12"><a href="#cb2-12"></a> TFE_OPEN_RESPONSE_SUCCESS,</span>
|
||||
<span id="cb2-13"><a href="#cb2-13"></a> TFE_OPEN_RESPONSE_CANCEL,</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>
|
||||
<span id="cb2-16"><a href="#cb2-16"></a></span>
|
||||
<span id="cb2-17"><a href="#cb2-17"></a>GFile *</span>
|
||||
<span id="cb2-18"><a href="#cb2-18"></a>tfe_text_view_get_file (TfeTextView *tv);</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 (TfeTextView *tv, GtkWindow *win);</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 (TfeTextView *tv);</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 (TfeTextView *tv);</span>
|
||||
<span id="cb2-28"><a href="#cb2-28"></a></span>
|
||||
<span id="cb2-29"><a href="#cb2-29"></a>GtkWidget *</span>
|
||||
<span id="cb2-30"><a href="#cb2-30"></a>tfe_text_view_new_with_file (GFile *file);</span>
|
||||
<span id="cb2-31"><a href="#cb2-31"></a></span>
|
||||
<span id="cb2-32"><a href="#cb2-32"></a>GtkWidget *</span>
|
||||
<span id="cb2-33"><a href="#cb2-33"></a>tfe_text_view_new (<span class="dt">void</span>);</span>
|
||||
<span id="cb2-34"><a href="#cb2-34"></a></span>
|
||||
<span id="cb2-35"><a href="#cb2-35"></a><span class="pp">#endif </span><span class="co">/* __TFE_TEXT_VIEW_H__ */</span></span></code></pre></div>
|
||||
<ul>
|
||||
<li>1,2,35: Thanks to these three lines, the following lines are included only once.</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>
|
||||
<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>
|
||||
<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"></a>GtkWidget *tfe_text_view_new (<span class="dt">void</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"></a>GtkWidget *tfe_text_view_new_with_file (GFile *file);</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>
|
||||
<span id="cb5-2"><a href="#cb5-2"></a>tfe_text_view_new_with_file (GFile *file) {</span>
|
||||
<span id="cb5-3"><a href="#cb5-3"></a> g_return_val_if_fail (G_IS_FILE (file), NULL);</span>
|
||||
<span id="cb5-4"><a href="#cb5-4"></a></span>
|
||||
<span id="cb5-5"><a href="#cb5-5"></a> GtkWidget *tv;</span>
|
||||
<span id="cb5-6"><a href="#cb5-6"></a> GtkTextBuffer *tb;</span>
|
||||
<span id="cb5-7"><a href="#cb5-7"></a> <span class="dt">char</span> *contents;</span>
|
||||
<span id="cb5-8"><a href="#cb5-8"></a> gsize length;</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> (! g_file_load_contents (file, NULL, &contents, &length, NULL, NULL)) <span class="co">/* read error */</span></span>
|
||||
<span id="cb5-11"><a href="#cb5-11"></a> <span class="cf">return</span> NULL;</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> ((tv = tfe_text_view_new()) != NULL) {</span>
|
||||
<span id="cb5-14"><a href="#cb5-14"></a> tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));</span>
|
||||
<span id="cb5-15"><a href="#cb5-15"></a> gtk_text_buffer_set_text (tb, contents, length);</span>
|
||||
<span id="cb5-16"><a href="#cb5-16"></a> TFE_TEXT_VIEW (tv)->file = g_file_dup (file);</span>
|
||||
<span id="cb5-17"><a href="#cb5-17"></a> gtk_text_buffer_set_modified (tb, FALSE);</span>
|
||||
<span id="cb5-18"><a href="#cb5-18"></a> }</span>
|
||||
<span id="cb5-19"><a href="#cb5-19"></a> g_free (contents);</span>
|
||||
<span id="cb5-20"><a href="#cb5-20"></a> <span class="cf">return</span> tv;</span>
|
||||
<span id="cb5-21"><a href="#cb5-21"></a>}</span>
|
||||
<span id="cb5-22"><a href="#cb5-22"></a></span>
|
||||
<span id="cb5-23"><a href="#cb5-23"></a>GtkWidget *</span>
|
||||
<span id="cb5-24"><a href="#cb5-24"></a>tfe_text_view_new (<span class="dt">void</span>) {</span>
|
||||
<span id="cb5-25"><a href="#cb5-25"></a> <span class="cf">return</span> GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL));</span>
|
||||
<span id="cb5-26"><a href="#cb5-26"></a>}</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>
|
||||
<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, 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) immediately. And at the same time it logs out the error message (usually the log is outputted to stderr or stdout). This function is used to check the programmer’s error. If an error occurs, the solution is usually to change the (caller) program and fix the bug. You need to distinguish programmer’s errors and runtime errors. You shouldn’t use this function to find runtime errors.</li>
|
||||
<li>10-11: If an error occurs when reading the file, then the function returns NULL.</li>
|
||||
<li>13: Calls the function <code>tfe_text_view_new</code>. The function creates TfeTextView instance and returns the pointer to the instance. If an error happens in <code>tfe_text_view_new</code>, it returns NULL.</li>
|
||||
<li>14: Gets the pointer to GtkTextBuffer corresponds to <code>tv</code>. The pointer is assigned to <code>tb</code></li>
|
||||
<li>15: Assigns the contents read from the file to GtkTextBuffer pointed by <code>tb</code>.</li>
|
||||
<li>16: Duplicates <code>file</code> and sets <code>tv->file</code> to point it.</li>
|
||||
<li>17: The function <code>gtk_text_buffer_set_modified (tb, FALSE)</code> sets the modification flag of <code>tb</code> to FALSE. The modification flag indicates that the contents of the buffer is modified. It is used when the contents are saved. If the modification flag is FALSE, it doesn’t need to save the contents.</li>
|
||||
<li>19: Frees the memories pointed by <code>contents</code>.</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>
|
||||
<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"></a><span class="dt">void</span> tfe_text_view_save (TfeTextView *tv)</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"></a><span class="dt">void</span> tfe_text_view_saveas (TfeTextView *tv)</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 (GFile *file, GtkTextBuffer *tb, GtkWindow *win) {</span>
|
||||
<span id="cb8-3"><a href="#cb8-3"></a> GtkTextIter start_iter;</span>
|
||||
<span id="cb8-4"><a href="#cb8-4"></a> GtkTextIter end_iter;</span>
|
||||
<span id="cb8-5"><a href="#cb8-5"></a> gchar *contents;</span>
|
||||
<span id="cb8-6"><a href="#cb8-6"></a> gboolean stat;</span>
|
||||
<span id="cb8-7"><a href="#cb8-7"></a> GtkWidget *message_dialog;</span>
|
||||
<span id="cb8-8"><a href="#cb8-8"></a> GError *err = NULL;</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 (tb, &start_iter, &end_iter);</span>
|
||||
<span id="cb8-11"><a href="#cb8-11"></a> contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE);</span>
|
||||
<span id="cb8-12"><a href="#cb8-12"></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="cb8-13"><a href="#cb8-13"></a> gtk_text_buffer_set_modified (tb, FALSE);</span>
|
||||
<span id="cb8-14"><a href="#cb8-14"></a> stat = TRUE;</span>
|
||||
<span id="cb8-15"><a href="#cb8-15"></a> } <span class="cf">else</span> {</span>
|
||||
<span id="cb8-16"><a href="#cb8-16"></a> message_dialog = gtk_message_dialog_new (win, GTK_DIALOG_MODAL,</span>
|
||||
<span id="cb8-17"><a href="#cb8-17"></a> GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,</span>
|
||||
<span id="cb8-18"><a href="#cb8-18"></a> <span class="st">"%s.</span><span class="sc">\n</span><span class="st">"</span>, err->message);</span>
|
||||
<span id="cb8-19"><a href="#cb8-19"></a> g_signal_connect (message_dialog, <span class="st">"response"</span>, G_CALLBACK (gtk_window_destroy), NULL);</span>
|
||||
<span id="cb8-20"><a href="#cb8-20"></a> gtk_widget_show (message_dialog);</span>
|
||||
<span id="cb8-21"><a href="#cb8-21"></a> g_error_free (err);</span>
|
||||
<span id="cb8-22"><a href="#cb8-22"></a> stat = FALSE;</span>
|
||||
<span id="cb8-23"><a href="#cb8-23"></a> }</span>
|
||||
<span id="cb8-24"><a href="#cb8-24"></a> g_free (contents);</span>
|
||||
<span id="cb8-25"><a href="#cb8-25"></a> <span class="cf">return</span> stat;</span>
|
||||
<span id="cb8-26"><a href="#cb8-26"></a>}</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 (GtkWidget *dialog, gint response, TfeTextView *tv) {</span>
|
||||
<span id="cb8-30"><a href="#cb8-30"></a> GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));</span>
|
||||
<span id="cb8-31"><a href="#cb8-31"></a> GFile *file;</span>
|
||||
<span id="cb8-32"><a href="#cb8-32"></a> GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW);</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> (response == GTK_RESPONSE_ACCEPT) {</span>
|
||||
<span id="cb8-35"><a href="#cb8-35"></a> file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));</span>
|
||||
<span id="cb8-36"><a href="#cb8-36"></a> <span class="cf">if</span> (! G_IS_FILE (file))</span>
|
||||
<span id="cb8-37"><a href="#cb8-37"></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="cb8-38"><a href="#cb8-38"></a> <span class="cf">else</span> <span class="cf">if</span> (save_file(file, tb, GTK_WINDOW (win))) {</span>
|
||||
<span id="cb8-39"><a href="#cb8-39"></a> <span class="cf">if</span> (G_IS_FILE (tv->file))</span>
|
||||
<span id="cb8-40"><a href="#cb8-40"></a> g_object_unref (tv->file);</span>
|
||||
<span id="cb8-41"><a href="#cb8-41"></a> tv->file = file;</span>
|
||||
<span id="cb8-42"><a href="#cb8-42"></a> g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], <span class="dv">0</span>);</span>
|
||||
<span id="cb8-43"><a href="#cb8-43"></a> } <span class="cf">else</span></span>
|
||||
<span id="cb8-44"><a href="#cb8-44"></a> g_object_unref (file);</span>
|
||||
<span id="cb8-45"><a href="#cb8-45"></a> }</span>
|
||||
<span id="cb8-46"><a href="#cb8-46"></a> gtk_window_destroy (GTK_WINDOW (dialog));</span>
|
||||
<span id="cb8-47"><a href="#cb8-47"></a>}</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 (TfeTextView *tv) {</span>
|
||||
<span id="cb8-51"><a href="#cb8-51"></a> g_return_if_fail (TFE_IS_TEXT_VIEW (tv));</span>
|
||||
<span id="cb8-52"><a href="#cb8-52"></a></span>
|
||||
<span id="cb8-53"><a href="#cb8-53"></a> GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));</span>
|
||||
<span id="cb8-54"><a href="#cb8-54"></a> GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW);</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> (! gtk_text_buffer_get_modified (tb))</span>
|
||||
<span id="cb8-57"><a href="#cb8-57"></a> <span class="cf">return</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> (tv->file == NULL)</span>
|
||||
<span id="cb8-59"><a href="#cb8-59"></a> tfe_text_view_saveas (tv);</span>
|
||||
<span id="cb8-60"><a href="#cb8-60"></a> <span class="cf">else</span> <span class="cf">if</span> (! G_IS_FILE (tv->file))</span>
|
||||
<span id="cb8-61"><a href="#cb8-61"></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="cb8-62"><a href="#cb8-62"></a> <span class="cf">else</span></span>
|
||||
<span id="cb8-63"><a href="#cb8-63"></a> save_file (tv->file, tb, GTK_WINDOW (win));</span>
|
||||
<span id="cb8-64"><a href="#cb8-64"></a>}</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 (TfeTextView *tv) {</span>
|
||||
<span id="cb8-68"><a href="#cb8-68"></a> g_return_if_fail (TFE_IS_TEXT_VIEW (tv));</span>
|
||||
<span id="cb8-69"><a href="#cb8-69"></a></span>
|
||||
<span id="cb8-70"><a href="#cb8-70"></a> GtkWidget *dialog;</span>
|
||||
<span id="cb8-71"><a href="#cb8-71"></a> GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW);</span>
|
||||
<span id="cb8-72"><a href="#cb8-72"></a></span>
|
||||
<span id="cb8-73"><a href="#cb8-73"></a> dialog = gtk_file_chooser_dialog_new (<span class="st">"Save file"</span>, GTK_WINDOW (win), GTK_FILE_CHOOSER_ACTION_SAVE,</span>
|
||||
<span id="cb8-74"><a href="#cb8-74"></a> <span class="st">"Cancel"</span>, GTK_RESPONSE_CANCEL,</span>
|
||||
<span id="cb8-75"><a href="#cb8-75"></a> <span class="st">"Save"</span>, GTK_RESPONSE_ACCEPT,</span>
|
||||
<span id="cb8-76"><a href="#cb8-76"></a> NULL);</span>
|
||||
<span id="cb8-77"><a href="#cb8-77"></a> g_signal_connect (dialog, <span class="st">"response"</span>, G_CALLBACK (saveas_dialog_response), tv);</span>
|
||||
<span id="cb8-78"><a href="#cb8-78"></a> gtk_widget_show (dialog);</span>
|
||||
<span id="cb8-79"><a href="#cb8-79"></a>}</span></code></pre></div>
|
||||
<ul>
|
||||
<li>1-26: <code>save_file</code> function. This function 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>
|
||||
<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>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>
|
||||
</ul>
|
||||
<figure>
|
||||
<img src="../image/saveas.png" alt="" /><figcaption>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 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"></a><span class="dt">void</span> tfe_text_view_open (TfeTextView *tv, GtkWindow *win);</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>
|
||||
<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(GtkWidget *dialog, gint response, TfeTextView *tv) {</span>
|
||||
<span id="cb10-3"><a href="#cb10-3"></a> GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));</span>
|
||||
<span id="cb10-4"><a href="#cb10-4"></a> GFile *file;</span>
|
||||
<span id="cb10-5"><a href="#cb10-5"></a> <span class="dt">char</span> *contents;</span>
|
||||
<span id="cb10-6"><a href="#cb10-6"></a> gsize length;</span>
|
||||
<span id="cb10-7"><a href="#cb10-7"></a> GtkWidget *message_dialog;</span>
|
||||
<span id="cb10-8"><a href="#cb10-8"></a> GError *err = NULL;</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> (response != GTK_RESPONSE_ACCEPT)</span>
|
||||
<span id="cb10-11"><a href="#cb10-11"></a> g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], <span class="dv">0</span>, TFE_OPEN_RESPONSE_CANCEL);</span>
|
||||
<span id="cb10-12"><a href="#cb10-12"></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-13"><a href="#cb10-13"></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-14"><a href="#cb10-14"></a> g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], <span class="dv">0</span>, TFE_OPEN_RESPONSE_ERROR);</span>
|
||||
<span id="cb10-15"><a href="#cb10-15"></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-16"><a href="#cb10-16"></a> g_object_unref (file);</span>
|
||||
<span id="cb10-17"><a href="#cb10-17"></a> message_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog), GTK_DIALOG_MODAL,</span>
|
||||
<span id="cb10-18"><a href="#cb10-18"></a> GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,</span>
|
||||
<span id="cb10-19"><a href="#cb10-19"></a> <span class="st">"%s.</span><span class="sc">\n</span><span class="st">"</span>, err->message);</span>
|
||||
<span id="cb10-20"><a href="#cb10-20"></a> g_signal_connect (message_dialog, <span class="st">"response"</span>, G_CALLBACK (gtk_window_destroy), NULL);</span>
|
||||
<span id="cb10-21"><a href="#cb10-21"></a> gtk_widget_show (message_dialog);</span>
|
||||
<span id="cb10-22"><a href="#cb10-22"></a> g_error_free (err);</span>
|
||||
<span id="cb10-23"><a href="#cb10-23"></a> g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], <span class="dv">0</span>, TFE_OPEN_RESPONSE_ERROR);</span>
|
||||
<span id="cb10-24"><a href="#cb10-24"></a> } <span class="cf">else</span> {</span>
|
||||
<span id="cb10-25"><a href="#cb10-25"></a> gtk_text_buffer_set_text (tb, contents, length);</span>
|
||||
<span id="cb10-26"><a href="#cb10-26"></a> g_free (contents);</span>
|
||||
<span id="cb10-27"><a href="#cb10-27"></a> <span class="cf">if</span> (G_IS_FILE (tv->file))</span>
|
||||
<span id="cb10-28"><a href="#cb10-28"></a> g_object_unref (tv->file);</span>
|
||||
<span id="cb10-29"><a href="#cb10-29"></a> tv->file = file;</span>
|
||||
<span id="cb10-30"><a href="#cb10-30"></a> gtk_text_buffer_set_modified (tb, FALSE);</span>
|
||||
<span id="cb10-31"><a href="#cb10-31"></a> g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], <span class="dv">0</span>, TFE_OPEN_RESPONSE_SUCCESS);</span>
|
||||
<span id="cb10-32"><a href="#cb10-32"></a> g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], <span class="dv">0</span>);</span>
|
||||
<span id="cb10-33"><a href="#cb10-33"></a> }</span>
|
||||
<span id="cb10-34"><a href="#cb10-34"></a> gtk_window_destroy (GTK_WINDOW (dialog));</span>
|
||||
<span id="cb10-35"><a href="#cb10-35"></a>}</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 (TfeTextView *tv, GtkWindow *win) {</span>
|
||||
<span id="cb10-39"><a href="#cb10-39"></a> g_return_if_fail (TFE_IS_TEXT_VIEW (tv));</span>
|
||||
<span id="cb10-40"><a href="#cb10-40"></a> g_return_if_fail (GTK_IS_WINDOW (win));</span>
|
||||
<span id="cb10-41"><a href="#cb10-41"></a></span>
|
||||
<span id="cb10-42"><a href="#cb10-42"></a> GtkWidget *dialog;</span>
|
||||
<span id="cb10-43"><a href="#cb10-43"></a></span>
|
||||
<span id="cb10-44"><a href="#cb10-44"></a> dialog = gtk_file_chooser_dialog_new (<span class="st">"Open file"</span>, win, GTK_FILE_CHOOSER_ACTION_OPEN,</span>
|
||||
<span id="cb10-45"><a href="#cb10-45"></a> <span class="st">"Cancel"</span>, GTK_RESPONSE_CANCEL,</span>
|
||||
<span id="cb10-46"><a href="#cb10-46"></a> <span class="st">"Open"</span>, GTK_RESPONSE_ACCEPT,</span>
|
||||
<span id="cb10-47"><a href="#cb10-47"></a> NULL);</span>
|
||||
<span id="cb10-48"><a href="#cb10-48"></a> g_signal_connect (dialog, <span class="st">"response"</span>, G_CALLBACK (open_dialog_response), tv);</span>
|
||||
<span id="cb10-49"><a href="#cb10-49"></a> gtk_widget_show (dialog);</span>
|
||||
<span id="cb10-50"><a href="#cb10-50"></a>}</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 <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 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>
|
||||
</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 Gtk3, <code>gtk_dialog_run</code> function is available. It simplifies the process. However, in Gtk4, <code>gtk_dialog_run</code> is unavailable any more.</p>
|
||||
<figure>
|
||||
<img src="../image/open.png" alt="" /><figcaption>Caller and TfeTextView</figcaption>
|
||||
</figure>
|
||||
<ol type="1">
|
||||
<li>A caller gets a pointer <code>tv</code> to a TfeTextView instance by calling <code>tfe_text_view_new</code>.</li>
|
||||
<li>The caller connects the handler (left bottom in the diagram) and the signal “open-response”.</li>
|
||||
<li>It calls <code>tfe_text_view_open</code> to prompt the user to select a file from GtkFileChooserDialog.</li>
|
||||
<li>The dialog emits a signal and it invokes the handler <code>open_dialog_response</code>.</li>
|
||||
<li>The handler reads the file and inserts the text into GtkTextBuffer 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>
|
||||
<span id="cb11-2"><a href="#cb11-2"></a>tfe_text_view_get_file (TfeTextView *tv) {</span>
|
||||
<span id="cb11-3"><a href="#cb11-3"></a> g_return_val_if_fail (TFE_IS_TEXT_VIEW (tv), NULL);</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> <span class="cf">return</span> g_file_dup (tv->file);</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>
|
||||
<span id="cb11-9"><a href="#cb11-9"></a>}</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, 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 <a href="../html/tfetextview_doc.html">API document of TfeTextView</a>. 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 <a href="../src/tfe5">src/tfe5</a> and <a href="../src/tfetextview">src/tfetextview</a> directories.</p>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec12.html">Section 12</a>, Next: <a href="sec14.html">Section 14</a></p>
|
||||
</body>
|
||||
</html>
|
318
docs/sec14.html
Normal file
318
docs/sec14.html
Normal file
|
@ -0,0 +1,318 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>Gtk4 tutorial for beginners</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
body {width: 1080px; margin: 0 auto; font-size: large;}
|
||||
h2 {padding: 10px; background-color: #d0f0d0; }
|
||||
div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
|
||||
th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
|
||||
td {padding: 2px 6px; border: 1px solid;}
|
||||
img {display: block; margin-left: auto; margin-right: auto;}
|
||||
figcaption {text-align: center;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec13.html">Section 13</a>, Next: <a href="sec15.html">Section 15</a></p>
|
||||
<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>
|
||||
<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-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-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-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>
|
||||
<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>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>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>
|
||||
<ul>
|
||||
<li><code>tfe_text_view_save</code></li>
|
||||
<li><code>tef_text_view_open</code></li>
|
||||
<li><code>tfe_text_view_new_with_file</code></li>
|
||||
<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>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>
|
||||
<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-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-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>
|
||||
<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>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>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>
|
||||
</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>
|
||||
<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-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>
|
||||
<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>
|
||||
</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>
|
||||
<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-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>
|
||||
<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>
|
||||
</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="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>
|
||||
<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>
|
||||
</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>
|
||||
<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>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>
|
||||
</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>
|
||||
<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>
|
||||
</ul>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec13.html">Section 13</a>, Next: <a href="sec15.html">Section 15</a></p>
|
||||
</body>
|
||||
</html>
|
306
docs/sec15.html
Normal file
306
docs/sec15.html
Normal file
|
@ -0,0 +1,306 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>Gtk4 tutorial for beginners</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
body {width: 1080px; margin: 0 auto; font-size: large;}
|
||||
h2 {padding: 10px; background-color: #d0f0d0; }
|
||||
div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
|
||||
th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
|
||||
td {padding: 2px 6px; border: 1px solid;}
|
||||
img {display: block; margin-left: auto; margin-right: auto;}
|
||||
figcaption {text-align: center;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec14.html">Section 14</a>, Next: <a href="sec16.html">Section 16</a></p>
|
||||
<h1 id="tfeapplication.c">tfeapplication.c</h1>
|
||||
<p><code>tfeapplication.c</code> includes all the code other than <code>tfetxtview.c</code> and <code>tfenotebook.c</code>. It does:</p>
|
||||
<ul>
|
||||
<li>Application support, mainly handling command line arguments.</li>
|
||||
<li>Builds widgets using ui file.</li>
|
||||
<li>Connects button signals and their handlers.</li>
|
||||
<li>Manages CSS.</li>
|
||||
</ul>
|
||||
<h2 id="main">main</h2>
|
||||
<p>The function <code>main</code> is the first invoked function in C language. It connects the command line given by the user and Gtk application.</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">#define APPLICATION_ID "com.github.ToshioCP.tfe"</span></span>
|
||||
<span id="cb1-2"><a href="#cb1-2"></a></span>
|
||||
<span id="cb1-3"><a href="#cb1-3"></a><span class="dt">int</span></span>
|
||||
<span id="cb1-4"><a href="#cb1-4"></a>main (<span class="dt">int</span> argc, <span class="dt">char</span> **argv) {</span>
|
||||
<span id="cb1-5"><a href="#cb1-5"></a> GtkApplication *app;</span>
|
||||
<span id="cb1-6"><a href="#cb1-6"></a> <span class="dt">int</span> stat;</span>
|
||||
<span id="cb1-7"><a href="#cb1-7"></a></span>
|
||||
<span id="cb1-8"><a href="#cb1-8"></a> app = gtk_application_new (APPLICATION_ID, G_APPLICATION_HANDLES_OPEN);</span>
|
||||
<span id="cb1-9"><a href="#cb1-9"></a></span>
|
||||
<span id="cb1-10"><a href="#cb1-10"></a> g_signal_connect (app, <span class="st">"startup"</span>, G_CALLBACK (app_startup), NULL);</span>
|
||||
<span id="cb1-11"><a href="#cb1-11"></a> g_signal_connect (app, <span class="st">"activate"</span>, G_CALLBACK (app_activate), NULL);</span>
|
||||
<span id="cb1-12"><a href="#cb1-12"></a> g_signal_connect (app, <span class="st">"open"</span>, G_CALLBACK (app_open), NULL);</span>
|
||||
<span id="cb1-13"><a href="#cb1-13"></a></span>
|
||||
<span id="cb1-14"><a href="#cb1-14"></a> stat =g_application_run (G_APPLICATION (app), argc, argv);</span>
|
||||
<span id="cb1-15"><a href="#cb1-15"></a> g_object_unref (app);</span>
|
||||
<span id="cb1-16"><a href="#cb1-16"></a> <span class="cf">return</span> stat;</span>
|
||||
<span id="cb1-17"><a href="#cb1-17"></a>}</span></code></pre></div>
|
||||
<ul>
|
||||
<li>1: Defines the application id. It is easy to find the application id, and better than the id is embedded in <code>gtk_application_new</code>.</li>
|
||||
<li>8: Creates GtkApplication object.</li>
|
||||
<li>10-12: Connects “startup”, “activate” and “open” signals to their handlers.</li>
|
||||
<li>14: Runs the application.</li>
|
||||
<li>15-16: releases the reference to the application and returns the status.</li>
|
||||
</ul>
|
||||
<h2 id="startup-signal-handler">startup signal handler</h2>
|
||||
<p>Startup signal is emitted just after the GtkApplication instance is initialized. What the signal handler needs to do is initialization of the application.</p>
|
||||
<ul>
|
||||
<li>Builds the widgets using ui file.</li>
|
||||
<li>Connects button signals and their handlers.</li>
|
||||
<li>Sets CSS.</li>
|
||||
</ul>
|
||||
<p>The handler is as follows.</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>app_startup (GApplication *application) {</span>
|
||||
<span id="cb2-3"><a href="#cb2-3"></a> GtkApplication *app = GTK_APPLICATION (application);</span>
|
||||
<span id="cb2-4"><a href="#cb2-4"></a> GtkBuilder *build;</span>
|
||||
<span id="cb2-5"><a href="#cb2-5"></a> GtkApplicationWindow *win;</span>
|
||||
<span id="cb2-6"><a href="#cb2-6"></a> GtkNotebook *nb;</span>
|
||||
<span id="cb2-7"><a href="#cb2-7"></a> GtkButton *btno;</span>
|
||||
<span id="cb2-8"><a href="#cb2-8"></a> GtkButton *btnn;</span>
|
||||
<span id="cb2-9"><a href="#cb2-9"></a> GtkButton *btns;</span>
|
||||
<span id="cb2-10"><a href="#cb2-10"></a> GtkButton *btnc;</span>
|
||||
<span id="cb2-11"><a href="#cb2-11"></a></span>
|
||||
<span id="cb2-12"><a href="#cb2-12"></a> build = gtk_builder_new_from_resource (<span class="st">"/com/github/ToshioCP/tfe/tfe.ui"</span>);</span>
|
||||
<span id="cb2-13"><a href="#cb2-13"></a> win = GTK_APPLICATION_WINDOW (gtk_builder_get_object (build, <span class="st">"win"</span>));</span>
|
||||
<span id="cb2-14"><a href="#cb2-14"></a> nb = GTK_NOTEBOOK (gtk_builder_get_object (build, <span class="st">"nb"</span>));</span>
|
||||
<span id="cb2-15"><a href="#cb2-15"></a> gtk_window_set_application (GTK_WINDOW (win), app);</span>
|
||||
<span id="cb2-16"><a href="#cb2-16"></a> btno = GTK_BUTTON (gtk_builder_get_object (build, <span class="st">"btno"</span>));</span>
|
||||
<span id="cb2-17"><a href="#cb2-17"></a> btnn = GTK_BUTTON (gtk_builder_get_object (build, <span class="st">"btnn"</span>));</span>
|
||||
<span id="cb2-18"><a href="#cb2-18"></a> btns = GTK_BUTTON (gtk_builder_get_object (build, <span class="st">"btns"</span>));</span>
|
||||
<span id="cb2-19"><a href="#cb2-19"></a> btnc = GTK_BUTTON (gtk_builder_get_object (build, <span class="st">"btnc"</span>));</span>
|
||||
<span id="cb2-20"><a href="#cb2-20"></a> g_signal_connect_swapped (btno, <span class="st">"clicked"</span>, G_CALLBACK (open_cb), nb);</span>
|
||||
<span id="cb2-21"><a href="#cb2-21"></a> g_signal_connect_swapped (btnn, <span class="st">"clicked"</span>, G_CALLBACK (new_cb), nb);</span>
|
||||
<span id="cb2-22"><a href="#cb2-22"></a> g_signal_connect_swapped (btns, <span class="st">"clicked"</span>, G_CALLBACK (save_cb), nb);</span>
|
||||
<span id="cb2-23"><a href="#cb2-23"></a> g_signal_connect_swapped (btnc, <span class="st">"clicked"</span>, G_CALLBACK (close_cb), nb);</span>
|
||||
<span id="cb2-24"><a href="#cb2-24"></a> g_object_unref(build);</span>
|
||||
<span id="cb2-25"><a href="#cb2-25"></a></span>
|
||||
<span id="cb2-26"><a href="#cb2-26"></a>GdkDisplay *display;</span>
|
||||
<span id="cb2-27"><a href="#cb2-27"></a></span>
|
||||
<span id="cb2-28"><a href="#cb2-28"></a> display = gtk_widget_get_display (GTK_WIDGET (win));</span>
|
||||
<span id="cb2-29"><a href="#cb2-29"></a> GtkCssProvider *provider = gtk_css_provider_new ();</span>
|
||||
<span id="cb2-30"><a href="#cb2-30"></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="cb2-31"><a href="#cb2-31"></a> gtk_style_context_add_provider_for_display (display, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);</span>
|
||||
<span id="cb2-32"><a href="#cb2-32"></a>}</span></code></pre></div>
|
||||
<ul>
|
||||
<li>12-15: Builds widgets using ui file (resource). Connects the top-level window and the application with <code>gtk_window_set_application</code>.</li>
|
||||
<li>16-23: Gets buttons and connects their signals and handlers.</li>
|
||||
<li>24: Releases the reference to GtkBuilder.</li>
|
||||
<li>26-31: Sets CSS. CSS in Gtk is similar to CSS in HTML. You can set margin, border, padding, color, font and so on with CSS. In this program CSS is in line 30. It sets padding, font-family and font size of GtkTextView.</li>
|
||||
<li>26-28: GdkDisplay is used to set CSS. CSS will be explained in the next subsection.</li>
|
||||
</ul>
|
||||
<h2 id="css-in-gtk">CSS in Gtk</h2>
|
||||
<p>CSS is an abbreviation of Cascading Style Sheet. It is originally used with HTML to describe the presentation semantics of a document. You might have found that the widgets in Gtk is similar to a window in a browser. It implies that CSS can also be applied to Gtk windowing system.</p>
|
||||
<h3 id="css-nodes-selectors">CSS nodes, selectors</h3>
|
||||
<p>The syntax of CSS is as follows.</p>
|
||||
<pre><code>selector { color: yellow; padding-top: 10px; ...}</code></pre>
|
||||
<p>Every widget has CSS node. For example GtkTextView has <code>textview</code> node. If you want to set style to GtkTextView, 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">Gtk4 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>
|
||||
<ul>
|
||||
<li>padding is a space between the border and contents. This space makes the textview easier to read.</li>
|
||||
<li>font-family is a name of font. “monospace” is one of the generic family font keywords.</li>
|
||||
<li>font-size is set to 12pt.</li>
|
||||
</ul>
|
||||
<h3 id="gtkstylecontext-gtkcssprovider-and-gdkdisplay">GtkStyleContext, GtkCSSProvider and GdkDisplay</h3>
|
||||
<p>GtkStyleContext is an object that stores styling information affecting a widget. Each widget is connected to the corresponding GtkStyleContext. You can get the context by <code>gtk_widget_get_style_context</code>.</p>
|
||||
<p>GtkCssProvider is an object which parses CSS in order to style widgets.</p>
|
||||
<p>To apply your CSS to widgets, you need to add GtkStyleProvider (the interface of GtkCSSProvider) to GtkStyleContext. However, instead, you can add it to GdkDisplay of the window (usually top-level window).</p>
|
||||
<p>Look at the source file of <code>startup</code> handler again.</p>
|
||||
<ul>
|
||||
<li>28: The display is obtained by <code>gtk_widget_get_display</code>.</li>
|
||||
<li>29: Creates a GtkCssProvider instance.</li>
|
||||
<li>30: Puts the CSS into the provider.</li>
|
||||
<li>31: Adds the provider to the display. The last argument of <code>gtk_style_context_add_provider_for_display</code> is the priority of the style provider. <code>GTK_STYLE_PROVIDER_PRIORITY_APPLICATION</code> is a priority for application-specific style information. <code>GTK_STYLE_PROVIDER_PRIORITY_USER</code> is also often used and it is the highest priority. So, <code>GTK_STYLE_PROVIDER_PRIORITY_USER</code> is often used to a specific widget.</li>
|
||||
</ul>
|
||||
<p>It is possible to add the provider to the context of GtkTextView instead of GdkDiplay. To do so, rewrite <code>tfe_text_view_new</code>. First, get the GtkStyleContext object of a TfeTextView object. Then adds the CSS provider to the context.</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>GtkWidget *</span>
|
||||
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true"></a>tfe_text_view_new (<span class="dt">void</span>) {</span>
|
||||
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true"></a> GtkWidget *tv;</span>
|
||||
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true"></a></span>
|
||||
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true"></a> tv = gtk_widget_new (TFE_TYPE_TEXT_VIEW, NULL);</span>
|
||||
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true"></a></span>
|
||||
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true"></a> GtkStyleContext *context;</span>
|
||||
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true"></a></span>
|
||||
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true"></a> context = gtk_widget_get_style_context (GTK_WIDGET (tv));</span>
|
||||
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true"></a> GtkCssProvider *provider = gtk_css_provider_new ();</span>
|
||||
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true"></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-12"><a href="#cb6-12" aria-hidden="true"></a> gtk_style_context_add_provider (context, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_USER);</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="cf">return</span> tv;</span>
|
||||
<span id="cb6-15"><a href="#cb6-15" aria-hidden="true"></a>}</span></code></pre></div>
|
||||
<p>CSS in the context takes precedence over CSS in the display.</p>
|
||||
<h2 id="activate-and-open-handler">activate and open handler</h2>
|
||||
<p>The handler of “activate” and “open” signal are <code>app_activate</code> and <code>app_open</code> respectively. They just create a new 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>app_activate (GApplication *application) {</span>
|
||||
<span id="cb7-3"><a href="#cb7-3"></a> GtkApplication *app = GTK_APPLICATION (application);</span>
|
||||
<span id="cb7-4"><a href="#cb7-4"></a> GtkWidget *win = GTK_WIDGET (gtk_application_get_active_window (app));</span>
|
||||
<span id="cb7-5"><a href="#cb7-5"></a> GtkWidget *boxv = gtk_window_get_child (GTK_WINDOW (win));</span>
|
||||
<span id="cb7-6"><a href="#cb7-6"></a> GtkNotebook *nb = GTK_NOTEBOOK (gtk_widget_get_last_child (boxv));</span>
|
||||
<span id="cb7-7"><a href="#cb7-7"></a></span>
|
||||
<span id="cb7-8"><a href="#cb7-8"></a> notebook_page_new (nb);</span>
|
||||
<span id="cb7-9"><a href="#cb7-9"></a> gtk_widget_show (GTK_WIDGET (win));</span>
|
||||
<span id="cb7-10"><a href="#cb7-10"></a>}</span>
|
||||
<span id="cb7-11"><a href="#cb7-11"></a></span>
|
||||
<span id="cb7-12"><a href="#cb7-12"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb7-13"><a href="#cb7-13"></a>app_open (GApplication *application, GFile ** files, gint n_files, <span class="dt">const</span> gchar *hint) {</span>
|
||||
<span id="cb7-14"><a href="#cb7-14"></a> GtkApplication *app = GTK_APPLICATION (application);</span>
|
||||
<span id="cb7-15"><a href="#cb7-15"></a> GtkWidget *win = GTK_WIDGET (gtk_application_get_active_window (app));</span>
|
||||
<span id="cb7-16"><a href="#cb7-16"></a> GtkWidget *boxv = gtk_window_get_child (GTK_WINDOW (win));</span>
|
||||
<span id="cb7-17"><a href="#cb7-17"></a> GtkNotebook *nb = GTK_NOTEBOOK (gtk_widget_get_last_child (boxv));</span>
|
||||
<span id="cb7-18"><a href="#cb7-18"></a> <span class="dt">int</span> i;</span>
|
||||
<span id="cb7-19"><a href="#cb7-19"></a></span>
|
||||
<span id="cb7-20"><a href="#cb7-20"></a> <span class="cf">for</span> (i = <span class="dv">0</span>; i < n_files; i++)</span>
|
||||
<span id="cb7-21"><a href="#cb7-21"></a> notebook_page_new_with_file (nb, files[i]);</span>
|
||||
<span id="cb7-22"><a href="#cb7-22"></a> <span class="cf">if</span> (gtk_notebook_get_n_pages (nb) == <span class="dv">0</span>)</span>
|
||||
<span id="cb7-23"><a href="#cb7-23"></a> notebook_page_new (nb);</span>
|
||||
<span id="cb7-24"><a href="#cb7-24"></a> gtk_widget_show (win);</span>
|
||||
<span id="cb7-25"><a href="#cb7-25"></a>}</span></code></pre></div>
|
||||
<ul>
|
||||
<li>1-10: <code>app_activate</code>.</li>
|
||||
<li>8-10: Creates a new page and shows the window.</li>
|
||||
<li>12-25: <code>app_open</code>.</li>
|
||||
<li>20-21: Creates notebook pages with files.</li>
|
||||
<li>22-23: If no page has created, maybe because of read error, then it creates an empty page.</li>
|
||||
<li>24: Shows the window.</li>
|
||||
</ul>
|
||||
<p>These codes have become really simple thanks to tfenotebook.c and tfetextview.c.</p>
|
||||
<h2 id="primary-instance">Primary instance</h2>
|
||||
<p>Only one GApplication instance can be run at a time per session. The session is a bit difficult concept and also platform-dependent, but roughly speaking, it corresponds to a graphical desktop login. When you use your PC, you probably login first, then your desktop appears until you log off. This is the session.</p>
|
||||
<p>However, Linux is multi process OS and you can run two or more instances of the same application. Isn’t it a contradiction?</p>
|
||||
<p>When first instance is launched, then it registers itself with its application ID (for example, <code>com.github.ToshioCP.tfe</code>). Just after the registration, startup signal is emitted, then activate or open signal is emitted and the instance’s main loop runs. I wrote “startup signal is emitted just after the application instance is initialized” in the prior subsection. More precisely, it is emitted just after the registration.</p>
|
||||
<p>If another instance which has the same application ID is invoked, it also tries to register itself. Because this is the second instance, the registration of the ID has already done, so it fails. Because of the failure startup signal isn’t emitted. After that, activate or open signal is emitted in the primary instance, not the second instance. The primary instance receives the signal and its handler is invoked. On the other hand, the second instance doesn’t receive the signal and it immediately quits.</p>
|
||||
<p>Try to run two instances in a row.</p>
|
||||
<pre><code>$ ./_build/tfe &
|
||||
[1] 84453
|
||||
$ ./build/tfe tfeapplication.c
|
||||
$</code></pre>
|
||||
<p>First, the primary instance opens a window. Then, after the second instance is run, a new notebook page with the contents of <code>tfeapplication.c</code> appears in the primary instance’s window. This is because the open signal is emitted in the primary instance. The second instance immediately quits so shell prompt soon appears.</p>
|
||||
<h2 id="a-series-of-handlers-correspond-to-the-button-signals">a series of handlers correspond to the button signals</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="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb9-2"><a href="#cb9-2"></a>open_cb (GtkNotebook *nb) {</span>
|
||||
<span id="cb9-3"><a href="#cb9-3"></a> notebook_page_open (nb);</span>
|
||||
<span id="cb9-4"><a href="#cb9-4"></a>}</span>
|
||||
<span id="cb9-5"><a href="#cb9-5"></a></span>
|
||||
<span id="cb9-6"><a href="#cb9-6"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb9-7"><a href="#cb9-7"></a>new_cb (GtkNotebook *nb) {</span>
|
||||
<span id="cb9-8"><a href="#cb9-8"></a> notebook_page_new (nb);</span>
|
||||
<span id="cb9-9"><a href="#cb9-9"></a>}</span>
|
||||
<span id="cb9-10"><a href="#cb9-10"></a></span>
|
||||
<span id="cb9-11"><a href="#cb9-11"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb9-12"><a href="#cb9-12"></a>save_cb (GtkNotebook *nb) {</span>
|
||||
<span id="cb9-13"><a href="#cb9-13"></a> notebook_page_save (nb);</span>
|
||||
<span id="cb9-14"><a href="#cb9-14"></a>}</span>
|
||||
<span id="cb9-15"><a href="#cb9-15"></a></span>
|
||||
<span id="cb9-16"><a href="#cb9-16"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb9-17"><a href="#cb9-17"></a>close_cb (GtkNotebook *nb) {</span>
|
||||
<span id="cb9-18"><a href="#cb9-18"></a> notebook_page_close (GTK_NOTEBOOK (nb));</span>
|
||||
<span id="cb9-19"><a href="#cb9-19"></a>}</span></code></pre></div>
|
||||
<p><code>open_cb</code>, <code>new_cb</code>, <code>save_cb</code> and <code>close_cb</code> just call corresponding notebook page functions.</p>
|
||||
<h2 id="meson.build">meson.build</h2>
|
||||
<div class="sourceCode" id="cb10"><pre class="sourceCode numberSource numberLines"><code class="sourceCode"><span id="cb10-1"><a href="#cb10-1"></a>project('tfe', 'c')</span>
|
||||
<span id="cb10-2"><a href="#cb10-2"></a></span>
|
||||
<span id="cb10-3"><a href="#cb10-3"></a>gtkdep = dependency('gtk4')</span>
|
||||
<span id="cb10-4"><a href="#cb10-4"></a></span>
|
||||
<span id="cb10-5"><a href="#cb10-5"></a>gnome=import('gnome')</span>
|
||||
<span id="cb10-6"><a href="#cb10-6"></a>resources = gnome.compile_resources('resources','tfe.gresource.xml')</span>
|
||||
<span id="cb10-7"><a href="#cb10-7"></a></span>
|
||||
<span id="cb10-8"><a href="#cb10-8"></a>sourcefiles=files('tfeapplication.c', 'tfenotebook.c', '../tfetextview/tfetextview.c')</span>
|
||||
<span id="cb10-9"><a href="#cb10-9"></a></span>
|
||||
<span id="cb10-10"><a href="#cb10-10"></a>executable('tfe', sourcefiles, resources, dependencies: gtkdep)</span></code></pre></div>
|
||||
<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 <a href="../src/tfe5">source files</a> 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 href="https://github.com/ToshioCP/Gtk4-tutorial">repository</a>. There are two options.</p>
|
||||
<ul>
|
||||
<li>Use git and clone.</li>
|
||||
<li>Run your browser and open the <a href="https://github.com/ToshioCP/Gtk4-tutorial">top page</a>. Then click on “Code” button and click “Download ZIP” in the popup menu. After that, unzip the archive file.</li>
|
||||
</ul>
|
||||
<p>If you use git, run the terminal and type the following.</p>
|
||||
<pre><code>$ git clone https://github.com/ToshioCP/Gtk4-tutorial.git</code></pre>
|
||||
<p>The source files are under <a href="../src/tfe5"><code>/src/tfe5</code></a> directory.</p>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec14.html">Section 14</a>, Next: <a href="sec16.html">Section 16</a></p>
|
||||
</body>
|
||||
</html>
|
730
docs/sec16.html
Normal file
730
docs/sec16.html
Normal file
|
@ -0,0 +1,730 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>Gtk4 tutorial for beginners</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
body {width: 1080px; margin: 0 auto; font-size: large;}
|
||||
h2 {padding: 10px; background-color: #d0f0d0; }
|
||||
div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
|
||||
th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
|
||||
td {padding: 2px 6px; border: 1px solid;}
|
||||
img {display: block; margin-left: auto; margin-right: auto;}
|
||||
figcaption {text-align: center;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec15.html">Section 15</a>, Next: <a href="sec17.html">Section 17</a></p>
|
||||
<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>
|
||||
<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>
|
||||
</ul>
|
||||
<pre><code>$ . env.sh</code></pre>
|
||||
<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 <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>
|
||||
</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
|
||||
10 17 294 tfe5/meson.build
|
||||
99 304 3205 tfe5/tfeapplication.c
|
||||
6 9 153 tfe5/tfe.gresource.xml
|
||||
4 6 87 tfe5/tfe.h
|
||||
140 378 3601 tfe5/tfenotebook.c
|
||||
15 21 241 tfe5/tfenotebook.h
|
||||
229 671 8017 tfetextview/tfetextview.c
|
||||
35 60 701 tfetextview/tfetextview.h
|
||||
61 100 2073 tfe5/tfe.ui
|
||||
599 1566 18372 total</code></pre>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec15.html">Section 15</a>, Next: <a href="sec17.html">Section 17</a></p>
|
||||
</body>
|
||||
</html>
|
230
docs/sec17.html
Normal file
230
docs/sec17.html
Normal file
|
@ -0,0 +1,230 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>Gtk4 tutorial for beginners</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
body {width: 1080px; margin: 0 auto; font-size: large;}
|
||||
h2 {padding: 10px; background-color: #d0f0d0; }
|
||||
div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
|
||||
th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
|
||||
td {padding: 2px 6px; border: 1px solid;}
|
||||
img {display: block; margin-left: auto; margin-right: auto;}
|
||||
figcaption {text-align: center;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec16.html">Section 16</a>, Next: <a href="sec18.html">Section 18</a></p>
|
||||
<h1 id="menu-and-action">Menu and action</h1>
|
||||
<h2 id="menu">Menu</h2>
|
||||
<p>Users often use menus to tell a command to the computer. It is like this:</p>
|
||||
<figure>
|
||||
<img src="../image/menu.png" alt="" /><figcaption>Menu</figcaption>
|
||||
</figure>
|
||||
<p>Now let’s analyze the menu above. There are two types of object.</p>
|
||||
<ul>
|
||||
<li>“File”, “Edit”, “View”, “Cut”, “Copy”, “Paste” and “Select All”. They are called “menu item” or simply “item”. When the user clicks one of these items, then something will happen.</li>
|
||||
<li>Menubar, submenu referenced by “Edit” item and two sections. They are called “menu”. Menu is an ordered list of items. They are similar to arrays.</li>
|
||||
</ul>
|
||||
<figure>
|
||||
<img src="../image/menu_structure.png" alt="" /><figcaption>Menu structure</figcaption>
|
||||
</figure>
|
||||
<ul>
|
||||
<li>Menubar is a menu which has three items, which are “File”, “Edit” and “View”.</li>
|
||||
<li>The menu item labeled “Edit” has a link to the submenu which has two items. These two items don’t have labels. Each item refers to a section.</li>
|
||||
<li>The first section is a menu which has three items – “Cut”, “Copy” and “Paste”.</li>
|
||||
<li>The second section is a menu which has one item – “Select All”.</li>
|
||||
</ul>
|
||||
<p>Menus can build a complicated structure thanks to the links of menu items.</p>
|
||||
<h2 id="gmenumodel-gmenu-and-gmenuitem">GMenuModel, GMenu and GMenuItem</h2>
|
||||
<p>GMenuModel is an abstract object which represents a menu. GMenu is a simple implementation of GMenuModel and a child object of GMenuModel.</p>
|
||||
<pre><code>GObject -- GMenuModel -- GMenu</code></pre>
|
||||
<p>Because GMenuModel is an abstract object, it isn’t instantiatable. Therefore, it doesn’t have any functions to create its instance. If you want to create a menu, use <code>g_menu_new</code> to create a GMenu instance. GMenu inherits all the functions of GMenuModel because of the child object.</p>
|
||||
<p>GMenuItem is an object directly derived from GObject. GMenuItem and Gmenu (or GMenuModel) don’t have a parent-child relationship.</p>
|
||||
<pre><code>GObject -- GMenuModel -- GMenu
|
||||
GObject -- GMenuItem</code></pre>
|
||||
<p>GMenuItem has attributes. One of the attributes is label. For example, there is a menu item which has “Edit” label in the first diagram in this section. “Cut”, “Copy”, “Paste” and “Select All” are also the labels of the menu items. Other attributes will be explained later.</p>
|
||||
<p>Some menu items have a link to another GMenu. There are two types of links, submenu and section.</p>
|
||||
<p>GMenuItem can be inserted, appended or prepended to GMenu. When it is inserted, all of the attributes and link values of the item are copied and used to form a new item within the menu. The GMenuItem itself is not really inserted. Therefore, after the insertion, GMenuItem is useless and it should be freed. The same goes for appending or prepending.</p>
|
||||
<p>The following code shows how to append GMenuItem to GMenu.</p>
|
||||
<pre><code>GMenu *menu = g_menu_new ();
|
||||
GMenuItem *menu_item_quit = g_menu_item_new ("Quit", "app.quit");
|
||||
g_menu_append_item (menu, menu_item_quit);
|
||||
g_object_unref (menu_item_quit);</code></pre>
|
||||
<h2 id="menu-and-action-1">Menu and action</h2>
|
||||
<p>One of the attributes of menu items is an action. This attribute points an action object.</p>
|
||||
<p>There are two action objects, GSimpleAction and GPropertyAction. GSimpleAction is often used. And it is used with a menu item. Only GSimpleAction is described in this section.</p>
|
||||
<p>An action corresponds to a menu item will be activated when the menu item is clicked. Then the action emits an activate signal.</p>
|
||||
<ol type="1">
|
||||
<li>menu item is clicked.</li>
|
||||
<li>The corresponding action is activated.</li>
|
||||
<li>The action emits a signal.</li>
|
||||
<li>The connected handler is invoked.</li>
|
||||
</ol>
|
||||
<p>The following code is an example.</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="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true"></a>quit_activated(GSimpleAction *action, GVariant *parameter, gpointer app) { ... ... ...}</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>GSimpleAction *act_quit = g_simple_action_new (<span class="st">"quit"</span>, NULL);</span>
|
||||
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true"></a>g_action_map_add_action (G_ACTION_MAP (app), G_ACTION (act_quit));</span>
|
||||
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true"></a>g_signal_connect (act_quit, <span class="st">"activate"</span>, G_CALLBACK (quit_activated), app);</span>
|
||||
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true"></a>GMenuItem *menu_item_quit = g_menu_item_new (<span class="st">"Quit"</span>, <span class="st">"app.quit"</span>);</span></code></pre></div>
|
||||
<ol type="1">
|
||||
<li><code>menu_item_quit</code> is a menu item. It has a label “Quit” and is connected to an action “app.quit”. “app” is a prefix and “quit” is a name of the action. The prefix “app” means that the action belongs to a GtkApplication instance. If the menu is clicked, then the corresponding action “quit” which belongs to the GtkApplication will be activated.</li>
|
||||
<li><code>act_quit</code> is an action. It has a name “quit”. The function <code>g_simple_action_new</code> creates a stateless action. So, <code>act_quit</code> is stateless. The meaning of stateless will be explained later. The argument <code>NULL</code> means that the action doesn’t have an parameter. Most of the actions are stateless and have no parameter.</li>
|
||||
<li>The action <code>act_quit</code> is added to the GtkApplication instance with <code>g_action_map_add_action</code>. When <code>act_quit</code> is activated, it will emit “activate” signal.</li>
|
||||
<li>“activate” signal of the action is connected to the handler <code>quit_activated</code>. So, if the action is activated, the handler will be invoked.</li>
|
||||
</ol>
|
||||
<h2 id="simple-example">Simple example</h2>
|
||||
<p>The following is a simple example of menus and actions.</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>quit_activated(GSimpleAction *action, GVariant *parameter, gpointer user_data) {</span>
|
||||
<span id="cb5-5"><a href="#cb5-5"></a> GApplication *app = G_APPLICATION (user_data);</span>
|
||||
<span id="cb5-6"><a href="#cb5-6"></a></span>
|
||||
<span id="cb5-7"><a href="#cb5-7"></a> g_application_quit (app);</span>
|
||||
<span id="cb5-8"><a href="#cb5-8"></a>}</span>
|
||||
<span id="cb5-9"><a href="#cb5-9"></a></span>
|
||||
<span id="cb5-10"><a href="#cb5-10"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb5-11"><a href="#cb5-11"></a>app_activate (GApplication *app, gpointer user_data) {</span>
|
||||
<span id="cb5-12"><a href="#cb5-12"></a> GtkWidget *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">"menu1"</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> GSimpleAction *act_quit = g_simple_action_new (<span class="st">"quit"</span>, NULL);</span>
|
||||
<span id="cb5-17"><a href="#cb5-17"></a> g_action_map_add_action (G_ACTION_MAP (app), G_ACTION (act_quit));</span>
|
||||
<span id="cb5-18"><a href="#cb5-18"></a> g_signal_connect (act_quit, <span class="st">"activate"</span>, G_CALLBACK (quit_activated), app);</span>
|
||||
<span id="cb5-19"><a href="#cb5-19"></a></span>
|
||||
<span id="cb5-20"><a href="#cb5-20"></a> GMenu *menubar = g_menu_new ();</span>
|
||||
<span id="cb5-21"><a href="#cb5-21"></a> GMenuItem *menu_item_menu = g_menu_item_new (<span class="st">"Menu"</span>, NULL);</span>
|
||||
<span id="cb5-22"><a href="#cb5-22"></a> GMenu *menu = g_menu_new ();</span>
|
||||
<span id="cb5-23"><a href="#cb5-23"></a> GMenuItem *menu_item_quit = g_menu_item_new (<span class="st">"Quit"</span>, <span class="st">"app.quit"</span>);</span>
|
||||
<span id="cb5-24"><a href="#cb5-24"></a> g_menu_append_item (menu, menu_item_quit);</span>
|
||||
<span id="cb5-25"><a href="#cb5-25"></a> g_object_unref (menu_item_quit);</span>
|
||||
<span id="cb5-26"><a href="#cb5-26"></a> g_menu_item_set_submenu (menu_item_menu, G_MENU_MODEL (menu));</span>
|
||||
<span id="cb5-27"><a href="#cb5-27"></a> g_menu_append_item (menubar, menu_item_menu);</span>
|
||||
<span id="cb5-28"><a href="#cb5-28"></a> g_object_unref (menu_item_menu);</span>
|
||||
<span id="cb5-29"><a href="#cb5-29"></a></span>
|
||||
<span id="cb5-30"><a href="#cb5-30"></a> gtk_application_set_menubar (GTK_APPLICATION (app), G_MENU_MODEL (menubar));</span>
|
||||
<span id="cb5-31"><a href="#cb5-31"></a> gtk_application_window_set_show_menubar (GTK_APPLICATION_WINDOW (win), TRUE);</span>
|
||||
<span id="cb5-32"><a href="#cb5-32"></a> gtk_window_present (GTK_WINDOW (win));</span>
|
||||
<span id="cb5-33"><a href="#cb5-33"></a><span class="co">/* gtk_widget_show (win); is also OKay instead of gtk_window_present. */</span></span>
|
||||
<span id="cb5-34"><a href="#cb5-34"></a>}</span>
|
||||
<span id="cb5-35"><a href="#cb5-35"></a></span>
|
||||
<span id="cb5-36"><a href="#cb5-36"></a><span class="pp">#define APPLICATION_ID "com.github.ToshioCP.menu1"</span></span>
|
||||
<span id="cb5-37"><a href="#cb5-37"></a></span>
|
||||
<span id="cb5-38"><a href="#cb5-38"></a><span class="dt">int</span></span>
|
||||
<span id="cb5-39"><a href="#cb5-39"></a>main (<span class="dt">int</span> argc, <span class="dt">char</span> **argv) {</span>
|
||||
<span id="cb5-40"><a href="#cb5-40"></a> GtkApplication *app;</span>
|
||||
<span id="cb5-41"><a href="#cb5-41"></a> <span class="dt">int</span> stat;</span>
|
||||
<span id="cb5-42"><a href="#cb5-42"></a></span>
|
||||
<span id="cb5-43"><a href="#cb5-43"></a> app = gtk_application_new (APPLICATION_ID, G_APPLICATION_FLAGS_NONE);</span>
|
||||
<span id="cb5-44"><a href="#cb5-44"></a> g_signal_connect (app, <span class="st">"activate"</span>, G_CALLBACK (app_activate), NULL);</span>
|
||||
<span id="cb5-45"><a href="#cb5-45"></a></span>
|
||||
<span id="cb5-46"><a href="#cb5-46"></a> stat =g_application_run (G_APPLICATION (app), argc, argv);</span>
|
||||
<span id="cb5-47"><a href="#cb5-47"></a> g_object_unref (app);</span>
|
||||
<span id="cb5-48"><a href="#cb5-48"></a> <span class="cf">return</span> stat;</span>
|
||||
<span id="cb5-49"><a href="#cb5-49"></a>}</span></code></pre></div>
|
||||
<ul>
|
||||
<li>3-8: <code>quit_activated</code> is a handler of the “activate” signal on the action <code>act_quit</code>. Handlers of the “activate” signal have three parameters.
|
||||
<ol type="1">
|
||||
<li>The action instance on which the signal is emitted.</li>
|
||||
<li>Parameter. In this example it is <code>NULL</code> because the second argument of <code>g_simple_action_new</code> (line 15) is <code>NULL</code>. You don’ t need to care about it.</li>
|
||||
<li>User data. It is the fourth parameter in the <code>g_signal_connect</code> (line 18) that connects the action and the handler.</li>
|
||||
</ol></li>
|
||||
<li>7: A function <code>g_application_quit</code> immediately quits the application.</li>
|
||||
<li>10-34: <code>app_activate</code> is a handler of “activate” signal on the GtkApplication instance.</li>
|
||||
<li>12-14: Creates a GtkApplicationWindow <code>win</code>. And sets the title and the default size.</li>
|
||||
<li>16: Creates GSimpleAction <code>act_quit</code>. It is stateless. The first argument of <code>g_simple_action_new</code> is a name of the action and the second argument is a parameter. If you don’t need the parameter, pass <code>NULL</code>. Therefore, <code>act_quit</code> has a name “quit” and no parameter.</li>
|
||||
<li>17: Adds the action to GtkApplication <code>app</code>. GtkApplication implements an interface GActionMap and GActionGroup. GtkApplication (GActionMap) can have a group of actions and the actions are added with the function <code>g_action_map_add_action</code>. This function is described in <a href="https://docs.gtk.org/gio/method.ActionMap.add_action.html">Gio API Reference, g_action_map_add_action</a>.</li>
|
||||
<li>18: Connects “activate” signal of the action and the handler <code>quit_activated</code>.</li>
|
||||
<li>20-23: Creates GMenu and GMenuItem instances. <code>menubar</code> and <code>menu</code> are GMenu. <code>menu_item_menu</code> and <code>menu_item_quit</code> are GMenuItem. <code>menu_item_menu</code> has a label “Menu” and no action. <code>menu_item_quit</code> has a label “Quit” and an action “app.quit”. The action “app.quit” is a combination of “app” and “quit”. “app” is a prefix and it means that the action belongs to GtkApplication. “quit” is the name of the action. Therefore, “app.quit” points the action which belongs to the GtkApplication instance and is named “quit”.</li>
|
||||
<li>24-25: Appends <code>menu_item_quit</code> to <code>menu</code>. As I mentioned before, all the attributes and links are copied and used to form a new item in <code>menu</code>. Therefore after the appending, <code>menu_item_quit</code> is no longer needed. It is freed by <code>g_object_unref</code>.</li>
|
||||
<li>26: Sets the submenu link in <code>menu_item_menu</code> to point <code>menu</code>.</li>
|
||||
<li>27-28: Appends <code>menu_item_menu</code> to <code>menubar</code>. Then frees <code>menu_item_menu</code>. GMenu and GMenuItem are connected and finally a menu is made up. The structure of the menu is shown in the diagram below.</li>
|
||||
<li>30: The menu is inserted to GtkApplication.</li>
|
||||
<li>31: Sets GtkApplicationWindow to show the menubar.</li>
|
||||
<li>32: Shows the window.</li>
|
||||
</ul>
|
||||
<figure>
|
||||
<img src="../image/menu1.png" alt="" /><figcaption>menu and action</figcaption>
|
||||
</figure>
|
||||
<figure>
|
||||
<img src="../image/menu1_screenshot.png" alt="" /><figcaption>Screenshot of menu1</figcaption>
|
||||
</figure>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec16.html">Section 16</a>, Next: <a href="sec18.html">Section 18</a></p>
|
||||
</body>
|
||||
</html>
|
328
docs/sec18.html
Normal file
328
docs/sec18.html
Normal file
|
@ -0,0 +1,328 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>Gtk4 tutorial for beginners</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
body {width: 1080px; margin: 0 auto; font-size: large;}
|
||||
h2 {padding: 10px; background-color: #d0f0d0; }
|
||||
div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
|
||||
th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
|
||||
td {padding: 2px 6px; border: 1px solid;}
|
||||
img {display: block; margin-left: auto; margin-right: auto;}
|
||||
figcaption {text-align: center;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec17.html">Section 17</a>, Next: <a href="sec19.html">Section 19</a></p>
|
||||
<h1 id="stateful-action">Stateful action</h1>
|
||||
<p>Some actions have states. The typical values of states is boolean or string. However, other types of states are possible if you want.</p>
|
||||
<p>There’s an example <code>menu2_int16.c</code> in the <code>src/men</code> directory. It behaves the same as <code>menu2.c</code>. But it uses gint16 type of states instead of string type.</p>
|
||||
<p>Actions which have states are called stateful.</p>
|
||||
<h2 id="stateful-action-without-a-parameter">Stateful action without a parameter</h2>
|
||||
<p>Some menus are called toggle menu. For example, fullscreen menu has a state which has two values – fullscreen and non-fullscreen. The value of the state is changed every time the menu is clicked. An action corresponds to the fullscreen menu also have a state. Its value is TRUE or FALSE and it is called boolean value. TRUE corresponds to fullscreen and FALSE to non-fullscreen.</p>
|
||||
<p>The following is an example code to implement a fullscreen menu except the signal handler. The signal handler will be described after the explanation of this code.</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="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true"></a>app_activate (GApplication *app, gpointer user_data) {</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> GSimpleAction *act_fullscreen = g_simple_action_new_stateful (<span class="st">"fullscreen"</span>,</span>
|
||||
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true"></a> NULL, g_variant_new_boolean (FALSE));</span>
|
||||
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true"></a> GMenuItem *menu_item_fullscreen = g_menu_item_new (<span class="st">"Full Screen"</span>, <span class="st">"win.fullscreen"</span>);</span>
|
||||
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true"></a> g_signal_connect (act_fullscreen, <span class="st">"change-state"</span>, G_CALLBACK (fullscreen_changed), win);</span>
|
||||
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true"></a> ... ... ...</span>
|
||||
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true"></a>}</span></code></pre></div>
|
||||
<ul>
|
||||
<li><code>act_fullscreen</code> is a GSimpleAction instance. It is created with <code>g_simple_action_new_stateful</code>. The function has three arguments. The first argument “fullscreen” is the name of the action. The second argument is a parameter type. <code>NULL</code> means the action doesn’t have a parameter. The third argument is the initial state of the action. It is a GVariant value. GVariant will be explained in the next subsection. The function <code>g_variant_new_boolean (FALSE)</code> returns a GVariant value which is the boolean value <code>FALSE</code>.</li>
|
||||
<li><code>menu_item_fullscreen</code> is a GMenuItem instance. There are two arguments. The first argument “Full Screen” is a label of <code>menu_item_fullscreen</code>. The second argument is an action. The action “win.fullscreen” has a prefix “win” and an action name “fullscreen”. The prefix says that the action belongs to the window.</li>
|
||||
<li>connects the action <code>act_fullscreen</code> and the “change-state” signal handler <code>fullscreen_changed</code>. If the fullscreen menu is clicked, then the corresponding action <code>act_fullscreen</code> is activated. But no handler is connected to the “activate” signal. Then, the default behavior for boolean-stated actions with a NULL parameter type like <code>act_fullscreen</code> is to toggle them via the “change-state” signal.</li>
|
||||
</ul>
|
||||
<p>The following is the “change-state” signal handler.</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="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true"></a>fullscreen_changed(GSimpleAction *action, GVariant *value, gpointer win) {</span>
|
||||
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true"></a> <span class="cf">if</span> (g_variant_get_boolean (value))</span>
|
||||
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true"></a> gtk_window_maximize (GTK_WINDOW (win));</span>
|
||||
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true"></a> <span class="cf">else</span></span>
|
||||
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true"></a> gtk_window_unmaximize (GTK_WINDOW (win));</span>
|
||||
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true"></a> g_simple_action_set_state (action, value);</span>
|
||||
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true"></a>}</span></code></pre></div>
|
||||
<ul>
|
||||
<li>There are three parameters. The first parameter is the action which emits the “change-state” signal. The second parameter is the value of the new state of the action. The third parameter is a user data which is set in <code>g_signal_connect</code>.</li>
|
||||
<li>If the value is boolean type and <code>TRUE</code>, then it maximizes the window. Otherwise unmaximizes.</li>
|
||||
<li>Sets the state of the action with <code>value</code>. Note: the second argument was the toggled state value, but at this stage the state of the action has the original value. So, you need to set the state with the new value by <code>g_simple_action_set_state</code>.</li>
|
||||
</ul>
|
||||
<p>You can use “activate” signal instead of “change-state” signal, or both signals. But the way above is the simplest and the best.</p>
|
||||
<h3 id="gvariant">GVariant</h3>
|
||||
<p>GVarient can contain boolean, string or other type values. For example, the following program assigns TRUE to <code>value</code> whose type is GVariant.</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>GVariant *value = g_variant_new_boolean (TRUE);</span></code></pre></div>
|
||||
<p>Another example is:</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>GVariant *value2 = g_variant_new_string (<span class="st">"Hello"</span>);</span></code></pre></div>
|
||||
<p><code>value2</code> is a GVariant and it has a string type value “Hello”. GVariant can contain other types like int16, int32, int64, double and so on.</p>
|
||||
<p>If you want to get the original value, use g_variant_get series functions. For example, you can get the boolean value by g_variant_get_boolean.</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>gboolean <span class="dt">bool</span> = g_variant_get_boolean (value);</span></code></pre></div>
|
||||
<p>Because <code>value</code> has been created as a boolean type GVariant and <code>TRUE</code> value, <code>bool</code> equals <code>TRUE</code>. In the same way, you can get a string from <code>value2</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">const</span> <span class="dt">char</span> *str = g_variant_get_string (value2, NULL);</span></code></pre></div>
|
||||
<p>The second parameter is a pointer to gsize type variable (gsize is defined as unsigned long). If it isn’t NULL, then the length of the string will be set by the function. If it is NULL, nothing happens. The returned string <code>str</code> can’t be changed.</p>
|
||||
<h2 id="stateful-action-with-a-parameter">Stateful action with a parameter</h2>
|
||||
<p>Another example of stateful actions is an action corresponds to color select menus. For example, there are three menus and each menu has red, green or blue color respectively. They determine the background color of a certain widget. One action is connected to the three menus. The action has a state which values are “red”, “green” and “blue”. The values are string. Those colors are given to the signal handler as a parameter.</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">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true"></a>app_activate (GApplication *app, gpointer user_data) {</span>
|
||||
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true"></a> ... ... ...</span>
|
||||
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true"></a> GSimpleAction *act_color = g_simple_action_new_stateful (<span class="st">"color"</span>,</span>
|
||||
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true"></a> g_variant_type_new(<span class="st">"s"</span>), g_variant_new_string (<span class="st">"red"</span>));</span>
|
||||
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true"></a> GMenuItem *menu_item_red = g_menu_item_new (<span class="st">"Red"</span>, <span class="st">"win.color::red"</span>);</span>
|
||||
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true"></a> GMenuItem *menu_item_green = g_menu_item_new (<span class="st">"Green"</span>, <span class="st">"win.color::green"</span>);</span>
|
||||
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true"></a> GMenuItem *menu_item_blue = g_menu_item_new (<span class="st">"Blue"</span>, <span class="st">"win.color::blue"</span>);</span>
|
||||
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true"></a> g_signal_connect (act_color, <span class="st">"activate"</span>, G_CALLBACK (color_activated), win);</span>
|
||||
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true"></a> ... ... ...</span>
|
||||
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true"></a>}</span></code></pre></div>
|
||||
<ul>
|
||||
<li><code>act_color</code> is a GSimpleAction instance. It is created with <code>g_simple_action_new_stateful</code>. The function has three arguments. The first argument “color” is the name of the action. The second argument is a parameter type which is GVariantType. <code>g_variant_type_new("s")</code> creates GVariantType which is a string type (<code>G_VARIANT_TYPE_STRING</code>). The third argument is the initial state of the action. It is a GVariant. GVariantType will be explained in the next subsection. The function <code>g_variant_new_string ("red")</code> returns a GVariant value which has the string value “red”.</li>
|
||||
<li><code>menu_item_red</code> is a GMenuItem instance. There are two arguments. The first argument “Red” is the label of <code>menu_item_red</code>. The second argument is a detailed action. Its prefix is “win”, action name is “color” and target is “red”. Target is sent to the action as a parameter. The same goes for <code>menu_item_green</code> and <code>menu_item_blue</code>.</li>
|
||||
<li>connects the action <code>act_color</code> and the “activate” signal handler <code>color_activated</code>. If one of the three menus is clicked, then the action <code>act_color</code> is activated with the target (parameter) which is given by the menu. No handler is connected to “change-state” signal. Then the default behavior is to call <code>g_simple_action_set_state()</code> to set the state to the requested value.</li>
|
||||
</ul>
|
||||
<p>The following is the “activate” signal handler.</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="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true"></a>color_activated(GSimpleAction *action, GVariant *parameter, gpointer win) {</span>
|
||||
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true"></a> <span class="dt">char</span> *color = g_strdup_printf (<span class="st">"label#lb {background-color: %s;}"</span>,</span>
|
||||
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true"></a> g_variant_get_string (parameter, NULL));</span>
|
||||
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true"></a> gtk_css_provider_load_from_data (provider, color, -<span class="dv">1</span>);</span>
|
||||
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true"></a> g_free (color);</span>
|
||||
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true"></a> g_action_change_state (G_ACTION (action), parameter);</span>
|
||||
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true"></a>}</span></code></pre></div>
|
||||
<ul>
|
||||
<li>There are three parameters. The first parameter is the action which emits the “activate” signal. The second parameter is the parameter given to the action. It is a color specified by the menu. The third parameter is a user data which is set in <code>g_signal_connect</code>.</li>
|
||||
<li><code>color</code> is a CSS string created by <code>g_strdup_printf</code>. The parameter of <code>g_strdup_printf</code> is the same as printf C standard function. <code>g_variant_get_string</code> gets the string contained in <code>parameter</code>. You mustn’t change or free the string.</li>
|
||||
<li>Sets the color of the css provider.</li>
|
||||
<li>Frees the string <code>color</code>.</li>
|
||||
<li>Changes the state by <code>g_action_change_state</code>. The function just sets the state of the action to the parameter by <code>g_simple_action_set_state</code>. Therefore, you can use <code>g_simple_action_set_state</code> instead of <code>g_action_change_state</code>.</li>
|
||||
</ul>
|
||||
<p>Note: If you have set a “change-state” signal handler, <code>g_action_change_state</code> will emit “change-state” signal instead of calling <code>g_simple_action_set_state</code>.</p>
|
||||
<h3 id="gvarianttype">GVariantType</h3>
|
||||
<p>GVariantType gives a type of GVariant. GVariant can contain many kinds of types. And the type often needs to be recognized at runtime. GVariantType provides such functionality.</p>
|
||||
<p>GVariantType is created with a string which expresses a type.</p>
|
||||
<ul>
|
||||
<li>“b” means boolean type.</li>
|
||||
<li>“s” means string type.</li>
|
||||
</ul>
|
||||
<p>The following program is a simple example. It finally outputs the string “s”.</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"><glib.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">int</span></span>
|
||||
<span id="cb9-4"><a href="#cb9-4"></a>main (<span class="dt">int</span> argc, <span class="dt">char</span> **argv) {</span>
|
||||
<span id="cb9-5"><a href="#cb9-5"></a> GVariantType *vtype = g_variant_type_new (<span class="st">"s"</span>);</span>
|
||||
<span id="cb9-6"><a href="#cb9-6"></a> <span class="dt">const</span> <span class="dt">char</span> *type_string = g_variant_type_peek_string (vtype);</span>
|
||||
<span id="cb9-7"><a href="#cb9-7"></a> g_print (<span class="st">"%s</span><span class="sc">\n</span><span class="st">"</span>,type_string);</span>
|
||||
<span id="cb9-8"><a href="#cb9-8"></a>}</span></code></pre></div>
|
||||
<ul>
|
||||
<li><code>g_variant_type_new</code> creates GVariantType. It uses a type string “s” which means string.</li>
|
||||
<li><code>g_variant_type_peek_string</code> takes a peek at <code>vtype</code>. It is the string “s” given to <code>vtype</code> when it was created.</li>
|
||||
<li>prints the string to the terminal.</li>
|
||||
</ul>
|
||||
<h2 id="example-code">Example code</h2>
|
||||
<p>The following code includes stateful actions above. This program has menus like this:</p>
|
||||
<figure>
|
||||
<img src="../image/menu2.png" alt="" /><figcaption>menu2</figcaption>
|
||||
</figure>
|
||||
<ul>
|
||||
<li>Fullscreen menu toggles the size of the window between maximum and non-maximum. If the window is maximum size, which is called full screen, then a check mark is put before “fullscreen” label.</li>
|
||||
<li>Red, green and blue menu determines the back ground color of the label, which is the child widget of the window. The menus have radio buttons on the left of the menus. And the radio button of the selected menu turns on.</li>
|
||||
<li>Quit menu quits the application.</li>
|
||||
</ul>
|
||||
<p>The code is as follows.</p>
|
||||
<div class="sourceCode" id="cb10"><pre class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb10-1"><a href="#cb10-1"></a><span class="pp">#include </span><span class="im"><gtk/gtk.h></span></span>
|
||||
<span id="cb10-2"><a href="#cb10-2"></a></span>
|
||||
<span id="cb10-3"><a href="#cb10-3"></a><span class="dt">static</span> GtkCssProvider *provider;</span>
|
||||
<span id="cb10-4"><a href="#cb10-4"></a></span>
|
||||
<span id="cb10-5"><a href="#cb10-5"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb10-6"><a href="#cb10-6"></a>fullscreen_changed(GSimpleAction *action, GVariant *value, gpointer win) {</span>
|
||||
<span id="cb10-7"><a href="#cb10-7"></a> <span class="cf">if</span> (g_variant_get_boolean (value))</span>
|
||||
<span id="cb10-8"><a href="#cb10-8"></a> gtk_window_maximize (GTK_WINDOW (win));</span>
|
||||
<span id="cb10-9"><a href="#cb10-9"></a> <span class="cf">else</span></span>
|
||||
<span id="cb10-10"><a href="#cb10-10"></a> gtk_window_unmaximize (GTK_WINDOW (win));</span>
|
||||
<span id="cb10-11"><a href="#cb10-11"></a> g_simple_action_set_state (action, value);</span>
|
||||
<span id="cb10-12"><a href="#cb10-12"></a>}</span>
|
||||
<span id="cb10-13"><a href="#cb10-13"></a></span>
|
||||
<span id="cb10-14"><a href="#cb10-14"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb10-15"><a href="#cb10-15"></a>color_activated(GSimpleAction *action, GVariant *parameter, gpointer win) {</span>
|
||||
<span id="cb10-16"><a href="#cb10-16"></a> <span class="dt">char</span> *color = g_strdup_printf (<span class="st">"label#lb {background-color: %s;}"</span>, g_variant_get_string (parameter, NULL));</span>
|
||||
<span id="cb10-17"><a href="#cb10-17"></a> gtk_css_provider_load_from_data (provider, color, -<span class="dv">1</span>);</span>
|
||||
<span id="cb10-18"><a href="#cb10-18"></a> g_free (color);</span>
|
||||
<span id="cb10-19"><a href="#cb10-19"></a> g_action_change_state (G_ACTION (action), parameter);</span>
|
||||
<span id="cb10-20"><a href="#cb10-20"></a>}</span>
|
||||
<span id="cb10-21"><a href="#cb10-21"></a></span>
|
||||
<span id="cb10-22"><a href="#cb10-22"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb10-23"><a href="#cb10-23"></a>quit_activated(GSimpleAction *action, GVariant *parameter, gpointer app)</span>
|
||||
<span id="cb10-24"><a href="#cb10-24"></a>{</span>
|
||||
<span id="cb10-25"><a href="#cb10-25"></a> g_application_quit (G_APPLICATION(app));</span>
|
||||
<span id="cb10-26"><a href="#cb10-26"></a>}</span>
|
||||
<span id="cb10-27"><a href="#cb10-27"></a></span>
|
||||
<span id="cb10-28"><a href="#cb10-28"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb10-29"><a href="#cb10-29"></a>app_activate (GApplication *app, gpointer user_data) {</span>
|
||||
<span id="cb10-30"><a href="#cb10-30"></a> GtkWidget *win = gtk_application_window_new (GTK_APPLICATION (app));</span>
|
||||
<span id="cb10-31"><a href="#cb10-31"></a> gtk_window_set_title (GTK_WINDOW (win), <span class="st">"menu2"</span>);</span>
|
||||
<span id="cb10-32"><a href="#cb10-32"></a> gtk_window_set_default_size (GTK_WINDOW (win), <span class="dv">400</span>, <span class="dv">300</span>);</span>
|
||||
<span id="cb10-33"><a href="#cb10-33"></a></span>
|
||||
<span id="cb10-34"><a href="#cb10-34"></a> GtkWidget *lb = gtk_label_new (NULL);</span>
|
||||
<span id="cb10-35"><a href="#cb10-35"></a> gtk_widget_set_name (lb, <span class="st">"lb"</span>); <span class="co">/* the name is used by CSS Selector */</span></span>
|
||||
<span id="cb10-36"><a href="#cb10-36"></a> gtk_window_set_child (GTK_WINDOW (win), lb);</span>
|
||||
<span id="cb10-37"><a href="#cb10-37"></a></span>
|
||||
<span id="cb10-38"><a href="#cb10-38"></a> GSimpleAction *act_fullscreen</span>
|
||||
<span id="cb10-39"><a href="#cb10-39"></a> = g_simple_action_new_stateful (<span class="st">"fullscreen"</span>, NULL, g_variant_new_boolean (FALSE));</span>
|
||||
<span id="cb10-40"><a href="#cb10-40"></a> GSimpleAction *act_color</span>
|
||||
<span id="cb10-41"><a href="#cb10-41"></a> = g_simple_action_new_stateful (<span class="st">"color"</span>, g_variant_type_new(<span class="st">"s"</span>), g_variant_new_string (<span class="st">"red"</span>));</span>
|
||||
<span id="cb10-42"><a href="#cb10-42"></a> GSimpleAction *act_quit</span>
|
||||
<span id="cb10-43"><a href="#cb10-43"></a> = g_simple_action_new (<span class="st">"quit"</span>, NULL);</span>
|
||||
<span id="cb10-44"><a href="#cb10-44"></a></span>
|
||||
<span id="cb10-45"><a href="#cb10-45"></a> GMenu *menubar = g_menu_new ();</span>
|
||||
<span id="cb10-46"><a href="#cb10-46"></a> GMenu *menu = g_menu_new ();</span>
|
||||
<span id="cb10-47"><a href="#cb10-47"></a> GMenu *section1 = g_menu_new ();</span>
|
||||
<span id="cb10-48"><a href="#cb10-48"></a> GMenu *section2 = g_menu_new ();</span>
|
||||
<span id="cb10-49"><a href="#cb10-49"></a> GMenu *section3 = g_menu_new ();</span>
|
||||
<span id="cb10-50"><a href="#cb10-50"></a> GMenuItem *menu_item_fullscreen = g_menu_item_new (<span class="st">"Full Screen"</span>, <span class="st">"win.fullscreen"</span>);</span>
|
||||
<span id="cb10-51"><a href="#cb10-51"></a> GMenuItem *menu_item_red = g_menu_item_new (<span class="st">"Red"</span>, <span class="st">"win.color::red"</span>);</span>
|
||||
<span id="cb10-52"><a href="#cb10-52"></a> GMenuItem *menu_item_green = g_menu_item_new (<span class="st">"Green"</span>, <span class="st">"win.color::green"</span>);</span>
|
||||
<span id="cb10-53"><a href="#cb10-53"></a> GMenuItem *menu_item_blue = g_menu_item_new (<span class="st">"Blue"</span>, <span class="st">"win.color::blue"</span>);</span>
|
||||
<span id="cb10-54"><a href="#cb10-54"></a> GMenuItem *menu_item_quit = g_menu_item_new (<span class="st">"Quit"</span>, <span class="st">"app.quit"</span>);</span>
|
||||
<span id="cb10-55"><a href="#cb10-55"></a></span>
|
||||
<span id="cb10-56"><a href="#cb10-56"></a> g_signal_connect (act_fullscreen, <span class="st">"change-state"</span>, G_CALLBACK (fullscreen_changed), win);</span>
|
||||
<span id="cb10-57"><a href="#cb10-57"></a> g_signal_connect (act_color, <span class="st">"activate"</span>, G_CALLBACK (color_activated), win);</span>
|
||||
<span id="cb10-58"><a href="#cb10-58"></a> g_signal_connect (act_quit, <span class="st">"activate"</span>, G_CALLBACK (quit_activated), app);</span>
|
||||
<span id="cb10-59"><a href="#cb10-59"></a> g_action_map_add_action (G_ACTION_MAP (win), G_ACTION (act_fullscreen));</span>
|
||||
<span id="cb10-60"><a href="#cb10-60"></a> g_action_map_add_action (G_ACTION_MAP (win), G_ACTION (act_color));</span>
|
||||
<span id="cb10-61"><a href="#cb10-61"></a> g_action_map_add_action (G_ACTION_MAP (app), G_ACTION (act_quit));</span>
|
||||
<span id="cb10-62"><a href="#cb10-62"></a></span>
|
||||
<span id="cb10-63"><a href="#cb10-63"></a> g_menu_append_item (section1, menu_item_fullscreen);</span>
|
||||
<span id="cb10-64"><a href="#cb10-64"></a> g_menu_append_item (section2, menu_item_red);</span>
|
||||
<span id="cb10-65"><a href="#cb10-65"></a> g_menu_append_item (section2, menu_item_green);</span>
|
||||
<span id="cb10-66"><a href="#cb10-66"></a> g_menu_append_item (section2, menu_item_blue);</span>
|
||||
<span id="cb10-67"><a href="#cb10-67"></a> g_menu_append_item (section3, menu_item_quit);</span>
|
||||
<span id="cb10-68"><a href="#cb10-68"></a> g_object_unref (menu_item_red);</span>
|
||||
<span id="cb10-69"><a href="#cb10-69"></a> g_object_unref (menu_item_green);</span>
|
||||
<span id="cb10-70"><a href="#cb10-70"></a> g_object_unref (menu_item_blue);</span>
|
||||
<span id="cb10-71"><a href="#cb10-71"></a> g_object_unref (menu_item_fullscreen);</span>
|
||||
<span id="cb10-72"><a href="#cb10-72"></a> g_object_unref (menu_item_quit);</span>
|
||||
<span id="cb10-73"><a href="#cb10-73"></a></span>
|
||||
<span id="cb10-74"><a href="#cb10-74"></a> g_menu_append_section (menu, NULL, G_MENU_MODEL (section1));</span>
|
||||
<span id="cb10-75"><a href="#cb10-75"></a> g_menu_append_section (menu, <span class="st">"Color"</span>, G_MENU_MODEL (section2));</span>
|
||||
<span id="cb10-76"><a href="#cb10-76"></a> g_menu_append_section (menu, NULL, G_MENU_MODEL (section3));</span>
|
||||
<span id="cb10-77"><a href="#cb10-77"></a> g_menu_append_submenu (menubar, <span class="st">"Menu"</span>, G_MENU_MODEL (menu));</span>
|
||||
<span id="cb10-78"><a href="#cb10-78"></a></span>
|
||||
<span id="cb10-79"><a href="#cb10-79"></a> gtk_application_set_menubar (GTK_APPLICATION (app), G_MENU_MODEL (menubar));</span>
|
||||
<span id="cb10-80"><a href="#cb10-80"></a> gtk_application_window_set_show_menubar (GTK_APPLICATION_WINDOW (win), TRUE);</span>
|
||||
<span id="cb10-81"><a href="#cb10-81"></a></span>
|
||||
<span id="cb10-82"><a href="#cb10-82"></a><span class="co">/* GtkCssProvider *provider = gtk_css_provider_new ();*/</span></span>
|
||||
<span id="cb10-83"><a href="#cb10-83"></a> provider = gtk_css_provider_new ();</span>
|
||||
<span id="cb10-84"><a href="#cb10-84"></a> GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (win));</span>
|
||||
<span id="cb10-85"><a href="#cb10-85"></a> gtk_css_provider_load_from_data (provider, <span class="st">"label#lb {background-color: red;}"</span>, -<span class="dv">1</span>);</span>
|
||||
<span id="cb10-86"><a href="#cb10-86"></a> gtk_style_context_add_provider_for_display (display, GTK_STYLE_PROVIDER (provider),</span>
|
||||
<span id="cb10-87"><a href="#cb10-87"></a> GTK_STYLE_PROVIDER_PRIORITY_USER);</span>
|
||||
<span id="cb10-88"><a href="#cb10-88"></a></span>
|
||||
<span id="cb10-89"><a href="#cb10-89"></a><span class="co">/* gtk_widget_show (win);*/</span></span>
|
||||
<span id="cb10-90"><a href="#cb10-90"></a> gtk_window_present (GTK_WINDOW (win));</span>
|
||||
<span id="cb10-91"><a href="#cb10-91"></a>}</span>
|
||||
<span id="cb10-92"><a href="#cb10-92"></a></span>
|
||||
<span id="cb10-93"><a href="#cb10-93"></a><span class="pp">#define APPLICATION_ID "com.github.ToshioCP.menu2"</span></span>
|
||||
<span id="cb10-94"><a href="#cb10-94"></a></span>
|
||||
<span id="cb10-95"><a href="#cb10-95"></a><span class="dt">int</span></span>
|
||||
<span id="cb10-96"><a href="#cb10-96"></a>main (<span class="dt">int</span> argc, <span class="dt">char</span> **argv) {</span>
|
||||
<span id="cb10-97"><a href="#cb10-97"></a> GtkApplication *app;</span>
|
||||
<span id="cb10-98"><a href="#cb10-98"></a> <span class="dt">int</span> stat;</span>
|
||||
<span id="cb10-99"><a href="#cb10-99"></a></span>
|
||||
<span id="cb10-100"><a href="#cb10-100"></a> app = gtk_application_new (APPLICATION_ID, G_APPLICATION_FLAGS_NONE);</span>
|
||||
<span id="cb10-101"><a href="#cb10-101"></a> g_signal_connect (app, <span class="st">"activate"</span>, G_CALLBACK (app_activate), NULL);</span>
|
||||
<span id="cb10-102"><a href="#cb10-102"></a></span>
|
||||
<span id="cb10-103"><a href="#cb10-103"></a> stat =g_application_run (G_APPLICATION (app), argc, argv);</span>
|
||||
<span id="cb10-104"><a href="#cb10-104"></a> g_object_unref (app);</span>
|
||||
<span id="cb10-105"><a href="#cb10-105"></a> <span class="cf">return</span> stat;</span>
|
||||
<span id="cb10-106"><a href="#cb10-106"></a>}</span></code></pre></div>
|
||||
<ul>
|
||||
<li>5-26: Signal handlers. They have already been explained.</li>
|
||||
<li>30-36: <code>win</code> and <code>lb</code> are GtkApplicationWindow and GtkLabel respectively. <code>win</code> has a title “menu2” and its default size is 400x300. <code>lb</code> is named as “lb”. The name is used in CSS. <code>lb</code> is set to <code>win</code> as a child.</li>
|
||||
<li>38-43: Three actions are defined. They are:
|
||||
<ul>
|
||||
<li>stateful and has no parameter. It has a toggle state.</li>
|
||||
<li>stateful and has a parameter. Parameter is a string type.</li>
|
||||
<li>stateless and has no parameter.</li>
|
||||
</ul></li>
|
||||
<li>45-54: Creates GMenu and GMenuItem. There are three sections.</li>
|
||||
<li>56-61: Signals are connected to handlers. And actions are added to GActionMap. Because <code>act_fullscreen</code> and <code>act_color</code> have “win” prefix and belong to GtkApplicationWindow, they are added to <code>win</code>. GtkApplicationWindow implements GActionModel interface like GtkApplication. <code>act_quit</code> has “app” prefix and belongs to GtkApplication. It is added to <code>app</code>.</li>
|
||||
<li>63-77: Connects and builds the menus. Useless GMenuItem are freed.</li>
|
||||
<li>79-80: GMenuModel <code>menubar</code> is inserted to <code>app</code>. Sets show menubar property of <code>win</code> to <code>TRUE</code>. Note: <code>gtk_application_window_set_show_menubar</code> creates GtkPopoverMenubar from GMenuModel. This is a different point between Gtk3 and Gtk4. And you can use GtkPopoverMenubar directly and set it as a descendant widget of the window. You may use GtkBox as a child widget of the window and insert GtkPopoverMenubar as the first child of the box.</li>
|
||||
<li>82-87: Sets CSS. <code>provider</code> is GtkCssProvider which is defined in line three as a static variable. Its CSS data is: <code>label#lb {background-color: red;}</code>. “label#lb” is called selector. “label” is the node of GtkLabel. “#” precedes an ID which is an identifiable name of the widget. “lb” is the name of GtkLabel <code>lb</code>. (See line 35). The style is surrounded by open and close braces. The style is applied to GtkLabel which has a name “lb”. Other GtkLabel have no effect from this. The provider is added to GdkDisplay.</li>
|
||||
<li>90: Shows the window.</li>
|
||||
</ul>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec17.html">Section 17</a>, Next: <a href="sec19.html">Section 19</a></p>
|
||||
</body>
|
||||
</html>
|
383
docs/sec19.html
Normal file
383
docs/sec19.html
Normal file
|
@ -0,0 +1,383 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>Gtk4 tutorial for beginners</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
body {width: 1080px; margin: 0 auto; font-size: large;}
|
||||
h2 {padding: 10px; background-color: #d0f0d0; }
|
||||
div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
|
||||
th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
|
||||
td {padding: 2px 6px; border: 1px solid;}
|
||||
img {display: block; margin-left: auto; margin-right: auto;}
|
||||
figcaption {text-align: center;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec18.html">Section 18</a>, Next: <a href="sec20.html">Section 20</a></p>
|
||||
<h1 id="ui-file-for-menu-and-action-entries">Ui file for menu and action entries</h1>
|
||||
<h2 id="ui-file-for-menu">Ui file for menu</h2>
|
||||
<p>You might have thought that building menus is really bothersome. Yes, the program was complicated and it needs lots of time to code it. The situation is similar to building widgets. When we built widgets, using ui file was a good way to avoid such complicated coding. The same goes for menus.</p>
|
||||
<p>The ui file for menus has interface, menu tags. The file starts and ends with interface tag.</p>
|
||||
<div class="sourceCode" id="cb1"><pre class="sourceCode xml"><code class="sourceCode xml"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true"></a><span class="kw"><interface></span></span>
|
||||
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true"></a> <span class="kw"><menu</span><span class="ot"> id=</span><span class="st">"menubar"</span><span class="kw">></span></span>
|
||||
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true"></a> <span class="kw"></menu></span></span>
|
||||
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true"></a><span class="kw"></interface></span></span></code></pre></div>
|
||||
<p><code>menu</code> tag corresponds to GMenu object. <code>id</code> attribute defines the name of the object. It will be referred by GtkBuilder.</p>
|
||||
<div class="sourceCode" id="cb2"><pre class="sourceCode xml"><code class="sourceCode xml"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true"></a><span class="kw"><submenu></span></span>
|
||||
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span>File<span class="kw"></attribute></span></span>
|
||||
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true"></a> <span class="kw"><item></span></span>
|
||||
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span>New<span class="kw"></attribute></span></span>
|
||||
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"action"</span><span class="kw">></span>win.new<span class="kw"></attribute></span></span>
|
||||
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true"></a> <span class="kw"></item></span></span>
|
||||
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true"></a><span class="kw"></submenu></span></span></code></pre></div>
|
||||
<p><code>item</code> tag corresponds to item in GMenu which has the same structure as GMenuItem. The item above has a label attribute. Its value is “New”. The item also has an action attribute and its value is “win.new”. “win” is a prefix and “new” is an action name. <code>submenu</code> tag corresponds to both GMenuItem and GMenu. The GMenuItem has a link to GMenu.</p>
|
||||
<p>The ui file above can be described as follows.</p>
|
||||
<div class="sourceCode" id="cb3"><pre class="sourceCode xml"><code class="sourceCode xml"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true"></a><span class="kw"><item></span></span>
|
||||
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span>File<span class="kw"></attribute></span></span>
|
||||
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true"></a> <span class="kw"><link</span><span class="ot"> name=</span><span class="st">"submenu"</span><span class="kw">></span></span>
|
||||
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true"></a> <span class="kw"><item></span></span>
|
||||
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span>New<span class="kw"></attribute></span></span>
|
||||
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"action"</span><span class="kw">></span>win.new<span class="kw"></attribute></span></span>
|
||||
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true"></a> <span class="kw"></item></span></span>
|
||||
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true"></a> <span class="kw"></link></span></span>
|
||||
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true"></a><span class="kw"></item></span></span></code></pre></div>
|
||||
<p><code>link</code> tag expresses the link to submenu. And at the same time it also expresses the submenu itself. This file illustrates the relationship between the menus and items better than the prior ui file. But <code>submenu</code> tag is simple and easy to understand. So, we usually prefer the former ui file style.</p>
|
||||
<p>The following is a screenshot of the sample program in this section. Its name is <code>menu3</code>.</p>
|
||||
<figure>
|
||||
<img src="../image/menu3.png" alt="" /><figcaption>menu3</figcaption>
|
||||
</figure>
|
||||
<p>The following is the ui file of the menu in <code>menu3</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"><interface></span></span>
|
||||
<span id="cb4-3"><a href="#cb4-3"></a> <span class="kw"><menu</span><span class="ot"> id=</span><span class="st">"menubar"</span><span class="kw">></span></span>
|
||||
<span id="cb4-4"><a href="#cb4-4"></a> <span class="kw"><submenu></span></span>
|
||||
<span id="cb4-5"><a href="#cb4-5"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span>File<span class="kw"></attribute></span></span>
|
||||
<span id="cb4-6"><a href="#cb4-6"></a> <span class="kw"><section></span></span>
|
||||
<span id="cb4-7"><a href="#cb4-7"></a> <span class="kw"><item></span></span>
|
||||
<span id="cb4-8"><a href="#cb4-8"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span>New<span class="kw"></attribute></span></span>
|
||||
<span id="cb4-9"><a href="#cb4-9"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"action"</span><span class="kw">></span>win.new<span class="kw"></attribute></span></span>
|
||||
<span id="cb4-10"><a href="#cb4-10"></a> <span class="kw"></item></span></span>
|
||||
<span id="cb4-11"><a href="#cb4-11"></a> <span class="kw"><item></span></span>
|
||||
<span id="cb4-12"><a href="#cb4-12"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span>Open<span class="kw"></attribute></span></span>
|
||||
<span id="cb4-13"><a href="#cb4-13"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"action"</span><span class="kw">></span>win.open<span class="kw"></attribute></span></span>
|
||||
<span id="cb4-14"><a href="#cb4-14"></a> <span class="kw"></item></span></span>
|
||||
<span id="cb4-15"><a href="#cb4-15"></a> <span class="kw"></section></span></span>
|
||||
<span id="cb4-16"><a href="#cb4-16"></a> <span class="kw"><section></span></span>
|
||||
<span id="cb4-17"><a href="#cb4-17"></a> <span class="kw"><item></span></span>
|
||||
<span id="cb4-18"><a href="#cb4-18"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span>Save<span class="kw"></attribute></span></span>
|
||||
<span id="cb4-19"><a href="#cb4-19"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"action"</span><span class="kw">></span>win.save<span class="kw"></attribute></span></span>
|
||||
<span id="cb4-20"><a href="#cb4-20"></a> <span class="kw"></item></span></span>
|
||||
<span id="cb4-21"><a href="#cb4-21"></a> <span class="kw"><item></span></span>
|
||||
<span id="cb4-22"><a href="#cb4-22"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span>Save As…<span class="kw"></attribute></span></span>
|
||||
<span id="cb4-23"><a href="#cb4-23"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"action"</span><span class="kw">></span>win.saveas<span class="kw"></attribute></span></span>
|
||||
<span id="cb4-24"><a href="#cb4-24"></a> <span class="kw"></item></span></span>
|
||||
<span id="cb4-25"><a href="#cb4-25"></a> <span class="kw"></section></span></span>
|
||||
<span id="cb4-26"><a href="#cb4-26"></a> <span class="kw"><section></span></span>
|
||||
<span id="cb4-27"><a href="#cb4-27"></a> <span class="kw"><item></span></span>
|
||||
<span id="cb4-28"><a href="#cb4-28"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span>Close<span class="kw"></attribute></span></span>
|
||||
<span id="cb4-29"><a href="#cb4-29"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"action"</span><span class="kw">></span>win.close<span class="kw"></attribute></span></span>
|
||||
<span id="cb4-30"><a href="#cb4-30"></a> <span class="kw"></item></span></span>
|
||||
<span id="cb4-31"><a href="#cb4-31"></a> <span class="kw"></section></span></span>
|
||||
<span id="cb4-32"><a href="#cb4-32"></a> <span class="kw"><section></span></span>
|
||||
<span id="cb4-33"><a href="#cb4-33"></a> <span class="kw"><item></span></span>
|
||||
<span id="cb4-34"><a href="#cb4-34"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span>Quit<span class="kw"></attribute></span></span>
|
||||
<span id="cb4-35"><a href="#cb4-35"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"action"</span><span class="kw">></span>app.quit<span class="kw"></attribute></span></span>
|
||||
<span id="cb4-36"><a href="#cb4-36"></a> <span class="kw"></item></span></span>
|
||||
<span id="cb4-37"><a href="#cb4-37"></a> <span class="kw"></section></span></span>
|
||||
<span id="cb4-38"><a href="#cb4-38"></a> <span class="kw"></submenu></span></span>
|
||||
<span id="cb4-39"><a href="#cb4-39"></a> <span class="kw"><submenu></span></span>
|
||||
<span id="cb4-40"><a href="#cb4-40"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span>Edit<span class="kw"></attribute></span></span>
|
||||
<span id="cb4-41"><a href="#cb4-41"></a> <span class="kw"><section></span></span>
|
||||
<span id="cb4-42"><a href="#cb4-42"></a> <span class="kw"><item></span></span>
|
||||
<span id="cb4-43"><a href="#cb4-43"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span>Cut<span class="kw"></attribute></span></span>
|
||||
<span id="cb4-44"><a href="#cb4-44"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"action"</span><span class="kw">></span>win.cut<span class="kw"></attribute></span></span>
|
||||
<span id="cb4-45"><a href="#cb4-45"></a> <span class="kw"></item></span></span>
|
||||
<span id="cb4-46"><a href="#cb4-46"></a> <span class="kw"><item></span></span>
|
||||
<span id="cb4-47"><a href="#cb4-47"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span>Copy<span class="kw"></attribute></span></span>
|
||||
<span id="cb4-48"><a href="#cb4-48"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"action"</span><span class="kw">></span>win.copy<span class="kw"></attribute></span></span>
|
||||
<span id="cb4-49"><a href="#cb4-49"></a> <span class="kw"></item></span></span>
|
||||
<span id="cb4-50"><a href="#cb4-50"></a> <span class="kw"><item></span></span>
|
||||
<span id="cb4-51"><a href="#cb4-51"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span>Paste<span class="kw"></attribute></span></span>
|
||||
<span id="cb4-52"><a href="#cb4-52"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"action"</span><span class="kw">></span>win.paste<span class="kw"></attribute></span></span>
|
||||
<span id="cb4-53"><a href="#cb4-53"></a> <span class="kw"></item></span></span>
|
||||
<span id="cb4-54"><a href="#cb4-54"></a> <span class="kw"></section></span></span>
|
||||
<span id="cb4-55"><a href="#cb4-55"></a> <span class="kw"><section></span></span>
|
||||
<span id="cb4-56"><a href="#cb4-56"></a> <span class="kw"><item></span></span>
|
||||
<span id="cb4-57"><a href="#cb4-57"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span>Select All<span class="kw"></attribute></span></span>
|
||||
<span id="cb4-58"><a href="#cb4-58"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"action"</span><span class="kw">></span>win.selectall<span class="kw"></attribute></span></span>
|
||||
<span id="cb4-59"><a href="#cb4-59"></a> <span class="kw"></item></span></span>
|
||||
<span id="cb4-60"><a href="#cb4-60"></a> <span class="kw"></section></span></span>
|
||||
<span id="cb4-61"><a href="#cb4-61"></a> <span class="kw"></submenu></span></span>
|
||||
<span id="cb4-62"><a href="#cb4-62"></a> <span class="kw"><submenu></span></span>
|
||||
<span id="cb4-63"><a href="#cb4-63"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span>View<span class="kw"></attribute></span></span>
|
||||
<span id="cb4-64"><a href="#cb4-64"></a> <span class="kw"><section></span></span>
|
||||
<span id="cb4-65"><a href="#cb4-65"></a> <span class="kw"><item></span></span>
|
||||
<span id="cb4-66"><a href="#cb4-66"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span>Full Screen<span class="kw"></attribute></span></span>
|
||||
<span id="cb4-67"><a href="#cb4-67"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"action"</span><span class="kw">></span>win.fullscreen<span class="kw"></attribute></span></span>
|
||||
<span id="cb4-68"><a href="#cb4-68"></a> <span class="kw"></item></span></span>
|
||||
<span id="cb4-69"><a href="#cb4-69"></a> <span class="kw"></section></span></span>
|
||||
<span id="cb4-70"><a href="#cb4-70"></a> <span class="kw"></submenu></span></span>
|
||||
<span id="cb4-71"><a href="#cb4-71"></a> <span class="kw"></menu></span></span>
|
||||
<span id="cb4-72"><a href="#cb4-72"></a><span class="kw"></interface></span></span></code></pre></div>
|
||||
<p>The ui file is converted to the resource by the resource compiler <code>glib-compile-resouces</code> with xml file below.</p>
|
||||
<div class="sourceCode" id="cb5"><pre class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><span id="cb5-1"><a href="#cb5-1"></a><span class="kw"><?xml</span> version="1.0" encoding="UTF-8"<span class="kw">?></span></span>
|
||||
<span id="cb5-2"><a href="#cb5-2"></a><span class="kw"><gresources></span></span>
|
||||
<span id="cb5-3"><a href="#cb5-3"></a> <span class="kw"><gresource</span><span class="ot"> prefix=</span><span class="st">"/com/github/ToshioCP/menu3"</span><span class="kw">></span></span>
|
||||
<span id="cb5-4"><a href="#cb5-4"></a> <span class="kw"><file></span>menu3.ui<span class="kw"></file></span></span>
|
||||
<span id="cb5-5"><a href="#cb5-5"></a> <span class="kw"></gresource></span></span>
|
||||
<span id="cb5-6"><a href="#cb5-6"></a><span class="kw"></gresources></span></span></code></pre></div>
|
||||
<p>GtkBuilder builds menus from the resource.</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>GtkBuilder *builder = gtk_builder_new_from_resource (<span class="st">"/com/github/ToshioCP/menu3/menu3.ui"</span>);</span>
|
||||
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true"></a>GMenuModel *menubar = G_MENU_MODEL (gtk_builder_get_object (builder, <span class="st">"menubar"</span>));</span>
|
||||
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true"></a></span>
|
||||
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true"></a>gtk_application_set_menubar (GTK_APPLICATION (app), menubar);</span>
|
||||
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true"></a>g_object_unref (builder);</span></code></pre></div>
|
||||
<p>It is important that <code>builder</code> is unreferred after the GMenuModel <code>menubar</code> is inserted to the application. If you do it before setting, bad thing will happen – your computer might freeze.</p>
|
||||
<h2 id="action-entry">Action entry</h2>
|
||||
<p>The coding for building actions and signal handlers is bothersome work as well. Therefore, it should be automated. You can implement them easily with GActionEntry structure and <code>g_action_map_add_action_entries</code> function.</p>
|
||||
<p>GActionEntry contains action name, signal handlers, parameter and state.</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="kw">typedef</span> <span class="kw">struct</span> _GActionEntry GActionEntry;</span>
|
||||
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true"></a></span>
|
||||
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true"></a><span class="kw">struct</span> _GActionEntry</span>
|
||||
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true"></a>{</span>
|
||||
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true"></a> <span class="co">/* action name */</span></span>
|
||||
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true"></a> <span class="dt">const</span> gchar *name;</span>
|
||||
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true"></a> <span class="co">/* activate handler */</span></span>
|
||||
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true"></a> <span class="dt">void</span> (* activate) (GSimpleAction *action, GVariant *parameter, gpointer user_data);</span>
|
||||
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true"></a> <span class="co">/* the type of the parameter given as a single GVariant type string */</span></span>
|
||||
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true"></a> <span class="dt">const</span> gchar *parameter_type;</span>
|
||||
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true"></a> <span class="co">/* initial state given in GVariant text format */</span></span>
|
||||
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true"></a> <span class="dt">const</span> gchar *state;</span>
|
||||
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true"></a> <span class="co">/* change-state handler */</span></span>
|
||||
<span id="cb7-14"><a href="#cb7-14" aria-hidden="true"></a> <span class="dt">void</span> (* change_state) (GSimpleAction *action, GVariant *value, gpointer user_data);</span>
|
||||
<span id="cb7-15"><a href="#cb7-15" aria-hidden="true"></a> <span class="co">/*< private >*/</span></span>
|
||||
<span id="cb7-16"><a href="#cb7-16" aria-hidden="true"></a> gsize padding[<span class="dv">3</span>];</span>
|
||||
<span id="cb7-17"><a href="#cb7-17" aria-hidden="true"></a>};</span></code></pre></div>
|
||||
<p>For example, the actions in the previous section are:</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="st">"fullscreen"</span>, NULL, NULL, <span class="st">"false"</span>, fullscreen_changed }</span>
|
||||
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true"></a>{ <span class="st">"color"</span>, color_activated, <span class="st">"s"</span>, <span class="st">"red"</span>, NULL }</span>
|
||||
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true"></a>{ <span class="st">"quit"</span>, quit_activated, NULL, NULL, NULL },</span></code></pre></div>
|
||||
<p>And <code>g_action_map_add_action_entries</code> does all the process instead of the functions you have needed.</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"></a><span class="dt">const</span> GActionEntry app_entries[] = {</span>
|
||||
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true"></a> { <span class="st">"quit"</span>, quit_activated, NULL, NULL, NULL }</span>
|
||||
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true"></a>};</span>
|
||||
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true"></a>g_action_map_add_action_entries (G_ACTION_MAP (app), app_entries,</span>
|
||||
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true"></a> G_N_ELEMENTS (app_entries), app);</span></code></pre></div>
|
||||
<p>The code above does:</p>
|
||||
<ul>
|
||||
<li>Builds the “quit” action</li>
|
||||
<li>Connects the action and the “activate” signal handler <code>quit_activated</code></li>
|
||||
<li>Adds the action to the action map <code>app</code>.</li>
|
||||
</ul>
|
||||
<p>The same goes for the other actions.</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="dt">const</span> GActionEntry win_entries[] = {</span>
|
||||
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true"></a> { <span class="st">"fullscreen"</span>, NULL, NULL, <span class="st">"false"</span>, fullscreen_changed },</span>
|
||||
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true"></a> { <span class="st">"color"</span>, color_activated, <span class="st">"s"</span>, <span class="st">"red"</span>, NULL }</span>
|
||||
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true"></a>};</span>
|
||||
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true"></a>g_action_map_add_action_entries (G_ACTION_MAP (win), win_entries,</span>
|
||||
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true"></a> G_N_ELEMENTS (win_entries), win);</span></code></pre></div>
|
||||
<p>The code above does:</p>
|
||||
<ul>
|
||||
<li>Builds a “fullscreen” action and “color” action.</li>
|
||||
<li>Connects the “fullscreen” action and the “change-state” signal handler <code>fullscreen_changed</code></li>
|
||||
<li>Its initial state is set to FALSE.</li>
|
||||
<li>Connects the “color” action and the “activate” signal handler <code>color_activated</code></li>
|
||||
<li>Its parameter type is string and the initial value is “red”.</li>
|
||||
<li>Adds the actions to the action map <code>win</code>.</li>
|
||||
</ul>
|
||||
<h2 id="example-code">Example code</h2>
|
||||
<p>The C source code of <code>menu3</code> and <code>meson.build</code> is 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><span class="pp">#include </span><span class="im"><gtk/gtk.h></span></span>
|
||||
<span id="cb11-2"><a href="#cb11-2"></a></span>
|
||||
<span id="cb11-3"><a href="#cb11-3"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb11-4"><a href="#cb11-4"></a>new_activated (GSimpleAction *action, GVariant *parameter, gpointer win) {</span>
|
||||
<span id="cb11-5"><a href="#cb11-5"></a>}</span>
|
||||
<span id="cb11-6"><a href="#cb11-6"></a></span>
|
||||
<span id="cb11-7"><a href="#cb11-7"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb11-8"><a href="#cb11-8"></a>open_activated (GSimpleAction *action, GVariant *parameter, gpointer win) {</span>
|
||||
<span id="cb11-9"><a href="#cb11-9"></a>}</span>
|
||||
<span id="cb11-10"><a href="#cb11-10"></a></span>
|
||||
<span id="cb11-11"><a href="#cb11-11"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb11-12"><a href="#cb11-12"></a>save_activated (GSimpleAction *action, GVariant *parameter, gpointer win) {</span>
|
||||
<span id="cb11-13"><a href="#cb11-13"></a>}</span>
|
||||
<span id="cb11-14"><a href="#cb11-14"></a></span>
|
||||
<span id="cb11-15"><a href="#cb11-15"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb11-16"><a href="#cb11-16"></a>saveas_activated (GSimpleAction *action, GVariant *parameter, gpointer win) {</span>
|
||||
<span id="cb11-17"><a href="#cb11-17"></a>}</span>
|
||||
<span id="cb11-18"><a href="#cb11-18"></a></span>
|
||||
<span id="cb11-19"><a href="#cb11-19"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb11-20"><a href="#cb11-20"></a>close_activated (GSimpleAction *action, GVariant *parameter, gpointer win) {</span>
|
||||
<span id="cb11-21"><a href="#cb11-21"></a>}</span>
|
||||
<span id="cb11-22"><a href="#cb11-22"></a></span>
|
||||
<span id="cb11-23"><a href="#cb11-23"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb11-24"><a href="#cb11-24"></a>cut_activated (GSimpleAction *action, GVariant *parameter, gpointer win) {</span>
|
||||
<span id="cb11-25"><a href="#cb11-25"></a>}</span>
|
||||
<span id="cb11-26"><a href="#cb11-26"></a></span>
|
||||
<span id="cb11-27"><a href="#cb11-27"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb11-28"><a href="#cb11-28"></a>copy_activated (GSimpleAction *action, GVariant *parameter, gpointer win) {</span>
|
||||
<span id="cb11-29"><a href="#cb11-29"></a>}</span>
|
||||
<span id="cb11-30"><a href="#cb11-30"></a></span>
|
||||
<span id="cb11-31"><a href="#cb11-31"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb11-32"><a href="#cb11-32"></a>paste_activated (GSimpleAction *action, GVariant *parameter, gpointer win) {</span>
|
||||
<span id="cb11-33"><a href="#cb11-33"></a>}</span>
|
||||
<span id="cb11-34"><a href="#cb11-34"></a></span>
|
||||
<span id="cb11-35"><a href="#cb11-35"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb11-36"><a href="#cb11-36"></a>selectall_activated (GSimpleAction *action, GVariant *parameter, gpointer win) {</span>
|
||||
<span id="cb11-37"><a href="#cb11-37"></a>}</span>
|
||||
<span id="cb11-38"><a href="#cb11-38"></a></span>
|
||||
<span id="cb11-39"><a href="#cb11-39"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb11-40"><a href="#cb11-40"></a>fullscreen_changed (GSimpleAction *action, GVariant *state, gpointer win) {</span>
|
||||
<span id="cb11-41"><a href="#cb11-41"></a> <span class="cf">if</span> (g_variant_get_boolean (state))</span>
|
||||
<span id="cb11-42"><a href="#cb11-42"></a> gtk_window_maximize (GTK_WINDOW (win));</span>
|
||||
<span id="cb11-43"><a href="#cb11-43"></a> <span class="cf">else</span></span>
|
||||
<span id="cb11-44"><a href="#cb11-44"></a> gtk_window_unmaximize (GTK_WINDOW (win));</span>
|
||||
<span id="cb11-45"><a href="#cb11-45"></a> g_simple_action_set_state (action, state);</span>
|
||||
<span id="cb11-46"><a href="#cb11-46"></a>}</span>
|
||||
<span id="cb11-47"><a href="#cb11-47"></a></span>
|
||||
<span id="cb11-48"><a href="#cb11-48"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb11-49"><a href="#cb11-49"></a>quit_activated (GSimpleAction *action, GVariant *parameter, gpointer app)</span>
|
||||
<span id="cb11-50"><a href="#cb11-50"></a>{</span>
|
||||
<span id="cb11-51"><a href="#cb11-51"></a> g_application_quit (G_APPLICATION(app));</span>
|
||||
<span id="cb11-52"><a href="#cb11-52"></a>}</span>
|
||||
<span id="cb11-53"><a href="#cb11-53"></a></span>
|
||||
<span id="cb11-54"><a href="#cb11-54"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb11-55"><a href="#cb11-55"></a>app_activate (GApplication *app, gpointer user_data) {</span>
|
||||
<span id="cb11-56"><a href="#cb11-56"></a> GtkWidget *win = gtk_application_window_new (GTK_APPLICATION (app));</span>
|
||||
<span id="cb11-57"><a href="#cb11-57"></a></span>
|
||||
<span id="cb11-58"><a href="#cb11-58"></a> <span class="dt">const</span> GActionEntry win_entries[] = {</span>
|
||||
<span id="cb11-59"><a href="#cb11-59"></a> { <span class="st">"new"</span>, new_activated, NULL, NULL, NULL },</span>
|
||||
<span id="cb11-60"><a href="#cb11-60"></a> { <span class="st">"open"</span>, open_activated, NULL, NULL, NULL },</span>
|
||||
<span id="cb11-61"><a href="#cb11-61"></a> { <span class="st">"save"</span>, save_activated, NULL, NULL, NULL },</span>
|
||||
<span id="cb11-62"><a href="#cb11-62"></a> { <span class="st">"saveas"</span>, saveas_activated, NULL, NULL, NULL },</span>
|
||||
<span id="cb11-63"><a href="#cb11-63"></a> { <span class="st">"close"</span>, close_activated, NULL, NULL, NULL },</span>
|
||||
<span id="cb11-64"><a href="#cb11-64"></a> { <span class="st">"cut"</span>, cut_activated, NULL, NULL, NULL },</span>
|
||||
<span id="cb11-65"><a href="#cb11-65"></a> { <span class="st">"copy"</span>, copy_activated, NULL, NULL, NULL },</span>
|
||||
<span id="cb11-66"><a href="#cb11-66"></a> { <span class="st">"paste"</span>, paste_activated, NULL, NULL, NULL },</span>
|
||||
<span id="cb11-67"><a href="#cb11-67"></a> { <span class="st">"selectall"</span>, selectall_activated, NULL, NULL, NULL },</span>
|
||||
<span id="cb11-68"><a href="#cb11-68"></a> { <span class="st">"fullscreen"</span>, NULL, NULL, <span class="st">"false"</span>, fullscreen_changed }</span>
|
||||
<span id="cb11-69"><a href="#cb11-69"></a> };</span>
|
||||
<span id="cb11-70"><a href="#cb11-70"></a> g_action_map_add_action_entries (G_ACTION_MAP (win), win_entries, G_N_ELEMENTS (win_entries), win);</span>
|
||||
<span id="cb11-71"><a href="#cb11-71"></a></span>
|
||||
<span id="cb11-72"><a href="#cb11-72"></a> gtk_application_window_set_show_menubar (GTK_APPLICATION_WINDOW (win), TRUE);</span>
|
||||
<span id="cb11-73"><a href="#cb11-73"></a></span>
|
||||
<span id="cb11-74"><a href="#cb11-74"></a> gtk_window_set_title (GTK_WINDOW (win), <span class="st">"menu3"</span>);</span>
|
||||
<span id="cb11-75"><a href="#cb11-75"></a> gtk_window_set_default_size (GTK_WINDOW (win), <span class="dv">400</span>, <span class="dv">300</span>);</span>
|
||||
<span id="cb11-76"><a href="#cb11-76"></a> gtk_widget_show (win);</span>
|
||||
<span id="cb11-77"><a href="#cb11-77"></a>}</span>
|
||||
<span id="cb11-78"><a href="#cb11-78"></a></span>
|
||||
<span id="cb11-79"><a href="#cb11-79"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb11-80"><a href="#cb11-80"></a>app_startup (GApplication *app, gpointer user_data) {</span>
|
||||
<span id="cb11-81"><a href="#cb11-81"></a> GtkBuilder *builder = gtk_builder_new_from_resource (<span class="st">"/com/github/ToshioCP/menu3/menu3.ui"</span>);</span>
|
||||
<span id="cb11-82"><a href="#cb11-82"></a> GMenuModel *menubar = G_MENU_MODEL (gtk_builder_get_object (builder, <span class="st">"menubar"</span>));</span>
|
||||
<span id="cb11-83"><a href="#cb11-83"></a></span>
|
||||
<span id="cb11-84"><a href="#cb11-84"></a> gtk_application_set_menubar (GTK_APPLICATION (app), menubar);</span>
|
||||
<span id="cb11-85"><a href="#cb11-85"></a> g_object_unref (builder);</span>
|
||||
<span id="cb11-86"><a href="#cb11-86"></a></span>
|
||||
<span id="cb11-87"><a href="#cb11-87"></a> <span class="dt">const</span> GActionEntry app_entries[] = {</span>
|
||||
<span id="cb11-88"><a href="#cb11-88"></a> { <span class="st">"quit"</span>, quit_activated, NULL, NULL, NULL }</span>
|
||||
<span id="cb11-89"><a href="#cb11-89"></a> };</span>
|
||||
<span id="cb11-90"><a href="#cb11-90"></a> g_action_map_add_action_entries (G_ACTION_MAP (app), app_entries, G_N_ELEMENTS (app_entries), app);</span>
|
||||
<span id="cb11-91"><a href="#cb11-91"></a>}</span>
|
||||
<span id="cb11-92"><a href="#cb11-92"></a></span>
|
||||
<span id="cb11-93"><a href="#cb11-93"></a><span class="pp">#define APPLICATION_ID "com.github.ToshioCP.menu3"</span></span>
|
||||
<span id="cb11-94"><a href="#cb11-94"></a></span>
|
||||
<span id="cb11-95"><a href="#cb11-95"></a><span class="dt">int</span></span>
|
||||
<span id="cb11-96"><a href="#cb11-96"></a>main (<span class="dt">int</span> argc, <span class="dt">char</span> **argv) {</span>
|
||||
<span id="cb11-97"><a href="#cb11-97"></a> GtkApplication *app;</span>
|
||||
<span id="cb11-98"><a href="#cb11-98"></a> <span class="dt">int</span> stat;</span>
|
||||
<span id="cb11-99"><a href="#cb11-99"></a></span>
|
||||
<span id="cb11-100"><a href="#cb11-100"></a> app = gtk_application_new (APPLICATION_ID, G_APPLICATION_FLAGS_NONE);</span>
|
||||
<span id="cb11-101"><a href="#cb11-101"></a> g_signal_connect (app, <span class="st">"startup"</span>, G_CALLBACK (app_startup), NULL);</span>
|
||||
<span id="cb11-102"><a href="#cb11-102"></a> g_signal_connect (app, <span class="st">"activate"</span>, G_CALLBACK (app_activate), NULL);</span>
|
||||
<span id="cb11-103"><a href="#cb11-103"></a></span>
|
||||
<span id="cb11-104"><a href="#cb11-104"></a> stat =g_application_run (G_APPLICATION (app), argc, argv);</span>
|
||||
<span id="cb11-105"><a href="#cb11-105"></a> g_object_unref (app);</span>
|
||||
<span id="cb11-106"><a href="#cb11-106"></a> <span class="cf">return</span> stat;</span>
|
||||
<span id="cb11-107"><a href="#cb11-107"></a>}</span></code></pre></div>
|
||||
<p>meson.build</p>
|
||||
<div class="sourceCode" id="cb12"><pre class="sourceCode numberSource numberLines"><code class="sourceCode"><span id="cb12-1"><a href="#cb12-1"></a>project('menu3', 'c')</span>
|
||||
<span id="cb12-2"><a href="#cb12-2"></a></span>
|
||||
<span id="cb12-3"><a href="#cb12-3"></a>gtkdep = dependency('gtk4')</span>
|
||||
<span id="cb12-4"><a href="#cb12-4"></a></span>
|
||||
<span id="cb12-5"><a href="#cb12-5"></a>gnome=import('gnome')</span>
|
||||
<span id="cb12-6"><a href="#cb12-6"></a>resources = gnome.compile_resources('resources','menu3.gresource.xml')</span>
|
||||
<span id="cb12-7"><a href="#cb12-7"></a></span>
|
||||
<span id="cb12-8"><a href="#cb12-8"></a>sourcefiles=files('menu3.c')</span>
|
||||
<span id="cb12-9"><a href="#cb12-9"></a></span>
|
||||
<span id="cb12-10"><a href="#cb12-10"></a>executable('menu3', sourcefiles, resources, dependencies: gtkdep)</span></code></pre></div>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec18.html">Section 18</a>, Next: <a href="sec20.html">Section 20</a></p>
|
||||
</body>
|
||||
</html>
|
307
docs/sec2.html
Normal file
307
docs/sec2.html
Normal file
|
@ -0,0 +1,307 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>Gtk4 tutorial for beginners</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
body {width: 1080px; margin: 0 auto; font-size: large;}
|
||||
h2 {padding: 10px; background-color: #d0f0d0; }
|
||||
div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
|
||||
th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
|
||||
td {padding: 2px 6px; border: 1px solid;}
|
||||
img {display: block; margin-left: auto; margin-right: auto;}
|
||||
figcaption {text-align: center;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec1.html">Section 1</a>, Next: <a href="sec3.html">Section 3</a></p>
|
||||
<h1 id="installation-of-gtk4-to-linux-distributions">Installation of Gtk4 to Linux distributions</h1>
|
||||
<p>This section describes how to install Gtk4 into Linux distributions.</p>
|
||||
<p>This tutorial including this section is without any warranty. If you install Gtk4 to your computer, do it at your own risk.</p>
|
||||
<p>This section is written on 12/August/2021. “At present” means 12/August/2021 in this section.</p>
|
||||
<p>There are three possible way to install Gtk4.</p>
|
||||
<ul>
|
||||
<li>Install it from the distribution packages.</li>
|
||||
<li>Build it from the source file.</li>
|
||||
<li>Install Gnome 40 distribution.</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 Gtk4 packages in Ubuntu 21.04.</p>
|
||||
<pre><code>$ sudo apt-get install libgtk-4-bin libgtk-4-common libgtk-4-dev libgtk-4-doc</code></pre>
|
||||
<p>Arch, Debian and Fedora are also possible. See <a href="https://www.gtk.org/docs/installations/linux#installing-gtk-from-packages">Installing GTK from packages</a>. If you’ve installed Gtk4 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 Gtk4 packages, you need to build it from the source. Or, if you want the latest version of Gtk4, you also need to build it from the source. At present, the version of Gtk4 on Ubuntu 21.04 is 4.0.3.</p>
|
||||
<p>I installed Gtk4 in January 2021. So, the following is old information, 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-gtk4-installation">Prerequisites for Gtk4 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 packages necessary for each software below.</li>
|
||||
</ul>
|
||||
<h3 id="installation-target">Installation target</h3>
|
||||
<p>I installed Gtk4 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 Gtk4. 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, Gdk-pixbuf 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="gdk-pixbuf-and-gtk-doc-installation">Gdk-pixbuf 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="gtk4-installation">Gtk4 installation</h3>
|
||||
<p>If you want the latest development version of Gtk4, 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 Gtk4, 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">Gtk4 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 Gtk4 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 Gtk4 applications. Otherwise it’s not necessary. If you changed the environment variables above and run Gtk3 applications, it probably causes serious damage.</p>
|
||||
<h3 id="compiling-gtk4-applications">Compiling Gtk4 applications</h3>
|
||||
<p>Before you compile Gtk4 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 Gtk4 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 Gnome40. Gnome 40 is a new version of Gnome desktop system. And Gtk4 is installed in the distribution. See <a href="https://forty.gnome.org/">Gnome 40 website</a> first.</p>
|
||||
<p><em>However, Gnome40 is not necessary to compile and run Gtk4 applications.</em></p>
|
||||
<p>There are only three choices at present.</p>
|
||||
<ul>
|
||||
<li>Gnome OS</li>
|
||||
<li>Fedora 34</li>
|
||||
<li>openSUSE</li>
|
||||
</ul>
|
||||
<p>I’ve tried installing Fedora 34 with gnome-boxes.</p>
|
||||
<p>There are two ways to install them.</p>
|
||||
<ul>
|
||||
<li>Install them to a hard disk. This is a usual installation.</li>
|
||||
<li>Install them to a virtual machine.</li>
|
||||
</ul>
|
||||
<p>I’ve chosen the second way. I’ve tried installing 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 installer of Fedora 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 Gtk4 libraries already. But you need to install the Gtk4 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="test-for-compiling-a-gtk4-application">Test for compiling a Gtk4 application</h3>
|
||||
<p>You can test the Gtk4 development package by compiling files which are based on Gtk4. 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>Up: <a href="index.html">index.html</a>, Prev: <a href="sec1.html">Section 1</a>, Next: <a href="sec3.html">Section 3</a></p>
|
||||
</body>
|
||||
</html>
|
938
docs/sec20.html
Normal file
938
docs/sec20.html
Normal file
|
@ -0,0 +1,938 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>Gtk4 tutorial for beginners</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
body {width: 1080px; margin: 0 auto; font-size: large;}
|
||||
h2 {padding: 10px; background-color: #d0f0d0; }
|
||||
div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
|
||||
th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
|
||||
td {padding: 2px 6px; border: 1px solid;}
|
||||
img {display: block; margin-left: auto; margin-right: auto;}
|
||||
figcaption {text-align: center;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec19.html">Section 19</a>, Next: <a href="sec21.html">Section 21</a></p>
|
||||
<h1 id="gtkmenubutton-accelerators-font-pango-and-gsettings">GtkMenuButton, accelerators, font, pango and gsettings</h1>
|
||||
<p>Traditional menu structure is fine. However, buttons or menu items we often use are not so many. Some mightn’t be clicked at all. Therefore, it’s a good idea to put some frequently used buttons on the toolbar and put the rest of the less frequently used operations into the menu. Such menu are often connected to GtkMenuButton.</p>
|
||||
<p>We will restructure tfe text file editor in this section. It will be more practical. The buttons are changed to:</p>
|
||||
<ul>
|
||||
<li>Put open, save and close buttons to the toolbar. In addition, GtkMenuButton is added to the toolbar. This button shows a popup menu when clicked on. Here, popup means widely, including pull-down menu.</li>
|
||||
<li>Put new, save as, preference and quit items to the menu under the menu button.</li>
|
||||
</ul>
|
||||
<h2 id="signal-elements-in-ui-files">Signal elements in ui files</h2>
|
||||
<p>The four buttons are included in the ui file <code>tfe.ui</code>. The difference from prior sections is signal tag. The following is extracted from <code>tfe.ui</code> and it describes the open button.</p>
|
||||
<div class="sourceCode" id="cb1"><pre class="sourceCode xml"><code class="sourceCode xml"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true"></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="cb1-2"><a href="#cb1-2" aria-hidden="true"></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="cb1-3"><a href="#cb1-3" aria-hidden="true"></a> <span class="kw"><signal</span><span class="ot"> name=</span><span class="st">"clicked"</span><span class="ot"> handler=</span><span class="st">"open_cb"</span><span class="ot"> swapped=</span><span class="st">"TRUE"</span><span class="ot"> object=</span><span class="st">"nb"</span><span class="kw">></signal></span></span>
|
||||
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true"></a><span class="kw"></object></span></span></code></pre></div>
|
||||
<p>Signal tag specifies the name of the signal, handler and user_data object. They are the value of name, handler and object attributes. Swapped attribute has the same meaning as <code>g_signal_connect_swapped</code> function. So, the signal tag above works the same as the function 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"></a>g_signal_connect_swapped (btno, <span class="st">"clicked"</span>, G_CALLBACK (open_cb), nb);</span></code></pre></div>
|
||||
<p>You need to compile the source file with “-WI, –export-dynamic” options. You can achieve this by adding “export_dynamic: true” argument to executable function in <code>meson.build</code>. And remove static class from the handler.</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="dt">void</span></span>
|
||||
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true"></a>open_cb (GtkNotebook *nb) {</span>
|
||||
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true"></a> notebook_page_open (nb);</span>
|
||||
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true"></a>}</span></code></pre></div>
|
||||
<p>If you add static, the function is in the scope of the file and it can’t be seen from outside. Then the signal tag can’t find the function.</p>
|
||||
<h2 id="menu-and-gkmenubutton">Menu and GkMenuButton</h2>
|
||||
<p>Menus are described in <code>menu.ui</code> file.</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"><interface></span></span>
|
||||
<span id="cb4-3"><a href="#cb4-3"></a> <span class="kw"><menu</span><span class="ot"> id=</span><span class="st">"menu"</span><span class="kw">></span></span>
|
||||
<span id="cb4-4"><a href="#cb4-4"></a> <span class="kw"><section></span></span>
|
||||
<span id="cb4-5"><a href="#cb4-5"></a> <span class="kw"><item></span></span>
|
||||
<span id="cb4-6"><a href="#cb4-6"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span>New<span class="kw"></attribute></span></span>
|
||||
<span id="cb4-7"><a href="#cb4-7"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"action"</span><span class="kw">></span>win.new<span class="kw"></attribute></span></span>
|
||||
<span id="cb4-8"><a href="#cb4-8"></a> <span class="kw"></item></span></span>
|
||||
<span id="cb4-9"><a href="#cb4-9"></a> <span class="kw"><item></span></span>
|
||||
<span id="cb4-10"><a href="#cb4-10"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span>Save As…<span class="kw"></attribute></span></span>
|
||||
<span id="cb4-11"><a href="#cb4-11"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"action"</span><span class="kw">></span>win.saveas<span class="kw"></attribute></span></span>
|
||||
<span id="cb4-12"><a href="#cb4-12"></a> <span class="kw"></item></span></span>
|
||||
<span id="cb4-13"><a href="#cb4-13"></a> <span class="kw"></section></span></span>
|
||||
<span id="cb4-14"><a href="#cb4-14"></a> <span class="kw"><section></span></span>
|
||||
<span id="cb4-15"><a href="#cb4-15"></a> <span class="kw"><item></span></span>
|
||||
<span id="cb4-16"><a href="#cb4-16"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span>Preference<span class="kw"></attribute></span></span>
|
||||
<span id="cb4-17"><a href="#cb4-17"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"action"</span><span class="kw">></span>win.pref<span class="kw"></attribute></span></span>
|
||||
<span id="cb4-18"><a href="#cb4-18"></a> <span class="kw"></item></span></span>
|
||||
<span id="cb4-19"><a href="#cb4-19"></a> <span class="kw"></section></span></span>
|
||||
<span id="cb4-20"><a href="#cb4-20"></a> <span class="kw"><section></span></span>
|
||||
<span id="cb4-21"><a href="#cb4-21"></a> <span class="kw"><item></span></span>
|
||||
<span id="cb4-22"><a href="#cb4-22"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span>Quit<span class="kw"></attribute></span></span>
|
||||
<span id="cb4-23"><a href="#cb4-23"></a> <span class="kw"><attribute</span><span class="ot"> name=</span><span class="st">"action"</span><span class="kw">></span>win.close-all<span class="kw"></attribute></span></span>
|
||||
<span id="cb4-24"><a href="#cb4-24"></a> <span class="kw"></item></span></span>
|
||||
<span id="cb4-25"><a href="#cb4-25"></a> <span class="kw"></section></span></span>
|
||||
<span id="cb4-26"><a href="#cb4-26"></a> <span class="kw"></menu></span></span>
|
||||
<span id="cb4-27"><a href="#cb4-27"></a><span class="kw"></interface></span></span></code></pre></div>
|
||||
<p>There are four items, “New”, “Saveas”, “Preference” and “Quit”.</p>
|
||||
<ul>
|
||||
<li>“New” menu creates a new empty page.</li>
|
||||
<li>“Saveas” menu saves the current page as a new filename.</li>
|
||||
<li>“Preference” menu sets preference items. This version of <code>tfe</code> has only font preference.</li>
|
||||
<li>“Quit” menu quits the application.</li>
|
||||
</ul>
|
||||
<p>These four menus are not used so often. That’s why they are put to the menu behind the menu button.</p>
|
||||
<p>The menus and the menu button are connected with <code>gtk_menu_button_set_menu_model</code> function. The variable <code>btnm</code> below points a GtkMenuButton object.</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> build = gtk_builder_new_from_resource (<span class="st">"/com/github/ToshioCP/tfe/menu.ui"</span>);</span>
|
||||
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true"></a> menu = G_MENU_MODEL (gtk_builder_get_object (build, <span class="st">"menu"</span>));</span>
|
||||
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true"></a> gtk_menu_button_set_menu_model (btnm, menu);</span></code></pre></div>
|
||||
<h2 id="actions-and-accelerators">Actions and Accelerators</h2>
|
||||
<p>Menus are connected to actions. Actions are defined with an array and <code>g_action_map_add_action_entries</code> 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"></a> <span class="dt">const</span> GActionEntry win_entries[] = {</span>
|
||||
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true"></a> { <span class="st">"open"</span>, open_activated, NULL, NULL, NULL },</span>
|
||||
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true"></a> { <span class="st">"save"</span>, save_activated, NULL, NULL, NULL },</span>
|
||||
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true"></a> { <span class="st">"close"</span>, close_activated, NULL, NULL, NULL },</span>
|
||||
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true"></a> { <span class="st">"new"</span>, new_activated, NULL, NULL, NULL },</span>
|
||||
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true"></a> { <span class="st">"saveas"</span>, saveas_activated, NULL, NULL, NULL },</span>
|
||||
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true"></a> { <span class="st">"pref"</span>, pref_activated, NULL, NULL, NULL },</span>
|
||||
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true"></a> { <span class="st">"close-all"</span>, quit_activated, NULL, NULL, NULL }</span>
|
||||
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true"></a> };</span>
|
||||
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true"></a> g_action_map_add_action_entries (G_ACTION_MAP (win), win_entries, G_N_ELEMENTS (win_entries), nb);</span></code></pre></div>
|
||||
<p>There are seven actions, open, save, close, new, saveas, pref and close-all. But there were only four menus. New, saveas, pref and close-all actions correspond to new, saveas, preference and quit menu respectively. The three actions open, save and close doesn’t have corresponding menus. Are thy necessary? These actions are defined because of accelerators.</p>
|
||||
<p>Accelerators are a kind of short cut key function. They are defined with arrays and <code>gtk_application_set_accels_for_action</code> function.</p>
|
||||
<div class="sourceCode" id="cb7"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true"></a> <span class="kw">struct</span> {</span>
|
||||
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true"></a> <span class="dt">const</span> <span class="dt">char</span> *action;</span>
|
||||
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true"></a> <span class="dt">const</span> <span class="dt">char</span> *accels[<span class="dv">2</span>];</span>
|
||||
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true"></a> } action_accels[] = {</span>
|
||||
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true"></a> { <span class="st">"win.open"</span>, { <span class="st">"<Control>o"</span>, NULL } },</span>
|
||||
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true"></a> { <span class="st">"win.save"</span>, { <span class="st">"<Control>s"</span>, NULL } },</span>
|
||||
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true"></a> { <span class="st">"win.close"</span>, { <span class="st">"<Control>w"</span>, NULL } },</span>
|
||||
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true"></a> { <span class="st">"win.new"</span>, { <span class="st">"<Control>n"</span>, NULL } },</span>
|
||||
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true"></a> { <span class="st">"win.saveas"</span>, { <span class="st">"<Shift><Control>s"</span>, NULL } },</span>
|
||||
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true"></a> { <span class="st">"win.close-all"</span>, { <span class="st">"<Control>q"</span>, NULL } },</span>
|
||||
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true"></a> };</span>
|
||||
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true"></a></span>
|
||||
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true"></a> <span class="cf">for</span> (i = <span class="dv">0</span>; i < G_N_ELEMENTS(action_accels); i++)</span>
|
||||
<span id="cb7-14"><a href="#cb7-14" aria-hidden="true"></a> gtk_application_set_accels_for_action(GTK_APPLICATION(app), action_accels[i].action, action_accels[i].accels);</span></code></pre></div>
|
||||
<p>This code is a bit complicated. The array <code>action-accels[]</code> is an array of structures. The structure is:</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">struct</span> {</span>
|
||||
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true"></a> <span class="dt">const</span> <span class="dt">char</span> *action;</span>
|
||||
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true"></a> <span class="dt">const</span> <span class="dt">char</span> *accels[<span class="dv">2</span>];</span>
|
||||
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true"></a> }</span></code></pre></div>
|
||||
<p>The member <code>action</code> is a string. The member <code>accels</code> is an array of two strings. For example,</p>
|
||||
<div class="sourceCode" id="cb9"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true"></a>{ <span class="st">"win.open"</span>, { <span class="st">"<Control>o"</span>, NULL } },</span></code></pre></div>
|
||||
<p>This is the first element of the array <code>action_accels</code>.</p>
|
||||
<ul>
|
||||
<li>The member <code>action</code> is “win.open”. This specifies the action “open” belongs to the window object.</li>
|
||||
<li>The member <code>accels</code> is an array of two strings, “<Control>o” and NULL. The first string specifies a key combination. Control key and ‘o’. If you keep pressing the control key and push ‘o’ key, then it activates the action <code>win.open</code>. The second string NULL (or zero) means the end of the list (array). You can define more than one accelerator keys and the list must ends with NULL (zero). If you want to do so, the array length needs to be three or more. The parser recognizes “<control>o”, “<Shift><Alt>F2”, “<Ctrl>minus” and so on. If you want to use symbol key like “<Ctrl>-”, use “<Ctrl>minus” instead. Such relation between lower case and symbol (its character code) is specified in <a href="https://gitlab.gnome.org/GNOME/gtk/-/blob/master/gdk/gdkkeysyms.h"><code>gdkkeysyms.h</code></a> in the Gtk4 source code.</li>
|
||||
</ul>
|
||||
<h2 id="saveas-handler">Saveas handler</h2>
|
||||
<p>TfeTextView has already had a saveas function. So, only we need to write is the wrapper function in <code>tfenotebook.c</code>.</p>
|
||||
<div class="sourceCode" id="cb10"><pre class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb10-1"><a href="#cb10-1"></a><span class="dt">static</span> TfeTextView *</span>
|
||||
<span id="cb10-2"><a href="#cb10-2"></a>get_current_textview (GtkNotebook *nb) {</span>
|
||||
<span id="cb10-3"><a href="#cb10-3"></a> <span class="dt">int</span> i;</span>
|
||||
<span id="cb10-4"><a href="#cb10-4"></a> GtkWidget *scr;</span>
|
||||
<span id="cb10-5"><a href="#cb10-5"></a> GtkWidget *tv;</span>
|
||||
<span id="cb10-6"><a href="#cb10-6"></a></span>
|
||||
<span id="cb10-7"><a href="#cb10-7"></a> i = gtk_notebook_get_current_page (nb);</span>
|
||||
<span id="cb10-8"><a href="#cb10-8"></a> scr = gtk_notebook_get_nth_page (nb, i);</span>
|
||||
<span id="cb10-9"><a href="#cb10-9"></a> tv = gtk_scrolled_window_get_child (GTK_SCROLLED_WINDOW (scr));</span>
|
||||
<span id="cb10-10"><a href="#cb10-10"></a> <span class="cf">return</span> TFE_TEXT_VIEW (tv);</span>
|
||||
<span id="cb10-11"><a href="#cb10-11"></a>}</span>
|
||||
<span id="cb10-12"><a href="#cb10-12"></a></span>
|
||||
<span id="cb10-13"><a href="#cb10-13"></a><span class="dt">void</span></span>
|
||||
<span id="cb10-14"><a href="#cb10-14"></a>notebook_page_saveas (GtkNotebook *nb) {</span>
|
||||
<span id="cb10-15"><a href="#cb10-15"></a> g_return_if_fail(GTK_IS_NOTEBOOK (nb));</span>
|
||||
<span id="cb10-16"><a href="#cb10-16"></a></span>
|
||||
<span id="cb10-17"><a href="#cb10-17"></a> TfeTextView *tv;</span>
|
||||
<span id="cb10-18"><a href="#cb10-18"></a></span>
|
||||
<span id="cb10-19"><a href="#cb10-19"></a> tv = get_current_textview (nb);</span>
|
||||
<span id="cb10-20"><a href="#cb10-20"></a> tfe_text_view_saveas (TFE_TEXT_VIEW (tv));</span>
|
||||
<span id="cb10-21"><a href="#cb10-21"></a>}</span></code></pre></div>
|
||||
<p>The function <code>get_current_textview</code> is the same as before. The function <code>notebook_page_saveas</code> simply calls <code>tfe_text_view_saveas</code>.</p>
|
||||
<p>In <code>tfeapplication.c</code>, saveas handler just call <code>notebook_page_saveas</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>saveas_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {</span>
|
||||
<span id="cb11-3"><a href="#cb11-3"></a> GtkNotebook *nb = GTK_NOTEBOOK (user_data);</span>
|
||||
<span id="cb11-4"><a href="#cb11-4"></a> notebook_page_saveas (nb);</span>
|
||||
<span id="cb11-5"><a href="#cb11-5"></a>}</span></code></pre></div>
|
||||
<h2 id="preference-and-alert-dialog">Preference and alert dialog</h2>
|
||||
<h3 id="preference-dialog">Preference dialog</h3>
|
||||
<p>Preference dialog xml definition is added to <code>tfe.ui</code>.</p>
|
||||
<div class="sourceCode" id="cb12"><pre class="sourceCode xml"><code class="sourceCode xml"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true"></a><span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkDialog"</span><span class="ot"> id=</span><span class="st">"pref"</span><span class="kw">></span></span>
|
||||
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"title"</span><span class="kw">></span>Preferences<span class="kw"></property></span></span>
|
||||
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"resizable"</span><span class="kw">></span>FALSE<span class="kw"></property></span></span>
|
||||
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"modal"</span><span class="kw">></span>TRUE<span class="kw"></property></span></span>
|
||||
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"transient-for"</span><span class="kw">></span>win<span class="kw"></property></span></span>
|
||||
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true"></a> <span class="kw"><child</span><span class="ot"> internal-child=</span><span class="st">"content_area"</span><span class="kw">></span></span>
|
||||
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkBox"</span><span class="ot"> id=</span><span class="st">"content_area"</span><span class="kw">></span></span>
|
||||
<span id="cb12-8"><a href="#cb12-8" aria-hidden="true"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb12-9"><a href="#cb12-9" aria-hidden="true"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkBox"</span><span class="ot"> id=</span><span class="st">"pref_boxh"</span><span class="kw">></span></span>
|
||||
<span id="cb12-10"><a href="#cb12-10" aria-hidden="true"></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="cb12-11"><a href="#cb12-11" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"spacing"</span><span class="kw">></span>12<span class="kw"></property></span></span>
|
||||
<span id="cb12-12"><a href="#cb12-12" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"margin-start"</span><span class="kw">></span>12<span class="kw"></property></span></span>
|
||||
<span id="cb12-13"><a href="#cb12-13" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"margin-end"</span><span class="kw">></span>12<span class="kw"></property></span></span>
|
||||
<span id="cb12-14"><a href="#cb12-14" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"margin-top"</span><span class="kw">></span>12<span class="kw"></property></span></span>
|
||||
<span id="cb12-15"><a href="#cb12-15" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"margin-bottom"</span><span class="kw">></span>12<span class="kw"></property></span></span>
|
||||
<span id="cb12-16"><a href="#cb12-16" aria-hidden="true"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb12-17"><a href="#cb12-17" aria-hidden="true"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkLabel"</span><span class="ot"> id=</span><span class="st">"fontlabel"</span><span class="kw">></span></span>
|
||||
<span id="cb12-18"><a href="#cb12-18" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span>Font:<span class="kw"></property></span></span>
|
||||
<span id="cb12-19"><a href="#cb12-19" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"xalign"</span><span class="kw">></span>1<span class="kw"></property></span></span>
|
||||
<span id="cb12-20"><a href="#cb12-20" aria-hidden="true"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb12-21"><a href="#cb12-21" aria-hidden="true"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb12-22"><a href="#cb12-22" aria-hidden="true"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb12-23"><a href="#cb12-23" aria-hidden="true"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkFontButton"</span><span class="ot"> id=</span><span class="st">"fontbtn"</span><span class="kw">></span></span>
|
||||
<span id="cb12-24"><a href="#cb12-24" aria-hidden="true"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb12-25"><a href="#cb12-25" aria-hidden="true"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb12-26"><a href="#cb12-26" aria-hidden="true"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb12-27"><a href="#cb12-27" aria-hidden="true"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb12-28"><a href="#cb12-28" aria-hidden="true"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb12-29"><a href="#cb12-29" aria-hidden="true"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb12-30"><a href="#cb12-30" aria-hidden="true"></a><span class="kw"></object></span></span></code></pre></div>
|
||||
<ul>
|
||||
<li>Preference dialog is an independent dialog. It is not a descendant widget of the top-level GtkApplicationwindow <code>win</code>. Therefore, There’s no child tag that surrounds the dialog object.</li>
|
||||
<li>There are four properties of the dialog. GtkDialog is a child object (not child widget) of GtkWindow, so it inherits all the properties from GtkWindow. Title, resizable, modal and transient-for properties are inherited from GtkWindow. Transient-for specifies a temporary parent window, which the dialog’s location is based on.</li>
|
||||
<li>internal-child attribute is used in the child tag above. GtkDialog has a GtkBox child widget. Its id is “content_area” in <code>gtkdialog.ui</code>, which is the ui file of GtkDialog. (It is in the Gtk4 source files.) This box is provided for users to add content widgets in it. The tag <code><child internal-child="content_area"></code> is put at the top of the contents. Then you need to specify an object tag and define its class as GtkBox and its id as content_area. This object is defined in <code>gtkdialog.ui</code> but you need to define it again in the child tag.</li>
|
||||
<li>In the content area, defines GtkBox, GtkLabel and GtkFontButton.</li>
|
||||
</ul>
|
||||
<p>I want the preference dialog to keep alive during the application lives. So, it is necessary to catch “close-request” signal from the dialog and stop the signal propagation. This is accomplished by returning TRUE by the signal handler.</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>pref_close_cb (GtkDialog *pref, gpointer user_data) {</span>
|
||||
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true"></a> <span class="cf">return</span> TRUE;</span>
|
||||
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true"></a>}</span>
|
||||
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true"></a></span>
|
||||
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true"></a>g_signal_connect (GTK_DIALOG (pref), <span class="st">"close-request"</span>, G_CALLBACK (pref_close_cb), NULL);</span></code></pre></div>
|
||||
<p>Generally, signal emission consists of five stages.</p>
|
||||
<ol type="1">
|
||||
<li>Default handler is invoked if the signal’s flag is <code>G_SIGNAL_RUN_FIRST</code>. Default handler is set when a signal is registered. It is different from user signal handler, simply called signal handler, connected by <code>g_signal_connect</code>series function. Default handler can be invoked in either stage 1, 3 or 5. Most of the default handlers are <code>G_SIGNAL_RUN_FIRST</code> or <code>G_SIGNAL_RUN_LAST</code>.</li>
|
||||
<li>Signal handlers are invoked, unless it is connected by <code>g_signal_connect_after</code>.</li>
|
||||
<li>Default handler is invoked if the signal’s flag is <code>G_SIGNAL_RUN_LAST</code>.</li>
|
||||
<li>Signal handlers are invoked, if it is connected by <code>g_signal_connect_after</code>.</li>
|
||||
<li>Default handler is invoked if the signal’s flag is <code>G_SIGNAL_RUN_CLEANUP</code>.</li>
|
||||
</ol>
|
||||
<p>In the case of “close-request” signal, the default handler’s flag is <code>G_SIGNAL_RUN_LAST</code>. The handler <code>pref_close_cb</code> is not connected by <code>g_signal_connect_after</code>. So the number of stages are two.</p>
|
||||
<ol type="1">
|
||||
<li>Signal handler <code>pref_close_cb</code> is invoked.</li>
|
||||
<li>Default handler is invoked.</li>
|
||||
</ol>
|
||||
<p>And If the user signal handler returns TRUE, then other handlers will be stopped being invoked. Therefore, the program above prevents the invocation of the default handler and stop the closing process of the dialog.</p>
|
||||
<p>The following codes are extracted from <code>tfeapplication.c</code>.</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"></a><span class="dt">static</span> gulong pref_close_request_handler_id = <span class="dv">0</span>;</span>
|
||||
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true"></a><span class="dt">static</span> gulong alert_close_request_handler_id = <span class="dv">0</span>;</span>
|
||||
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true"></a></span>
|
||||
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true"></a>... ...</span>
|
||||
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true"></a></span>
|
||||
<span id="cb14-6"><a href="#cb14-6" aria-hidden="true"></a><span class="dt">static</span> gboolean</span>
|
||||
<span id="cb14-7"><a href="#cb14-7" aria-hidden="true"></a>dialog_close_cb (GtkDialog *dialog, gpointer user_data) {</span>
|
||||
<span id="cb14-8"><a href="#cb14-8" aria-hidden="true"></a> gtk_widget_hide (GTK_WIDGET (dialog));</span>
|
||||
<span id="cb14-9"><a href="#cb14-9" aria-hidden="true"></a> <span class="cf">return</span> TRUE;</span>
|
||||
<span id="cb14-10"><a href="#cb14-10" aria-hidden="true"></a>}</span>
|
||||
<span id="cb14-11"><a href="#cb14-11" aria-hidden="true"></a></span>
|
||||
<span id="cb14-12"><a href="#cb14-12" aria-hidden="true"></a>... ...</span>
|
||||
<span id="cb14-13"><a href="#cb14-13" aria-hidden="true"></a></span>
|
||||
<span id="cb14-14"><a href="#cb14-14" aria-hidden="true"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb14-15"><a href="#cb14-15" aria-hidden="true"></a>pref_activated (GSimpleAction *action, GVariant *parameter, gpointer nb) {</span>
|
||||
<span id="cb14-16"><a href="#cb14-16" aria-hidden="true"></a> gtk_widget_show (GTK_WIDGET (pref));</span>
|
||||
<span id="cb14-17"><a href="#cb14-17" aria-hidden="true"></a>}</span>
|
||||
<span id="cb14-18"><a href="#cb14-18" aria-hidden="true"></a></span>
|
||||
<span id="cb14-19"><a href="#cb14-19" aria-hidden="true"></a>... ...</span>
|
||||
<span id="cb14-20"><a href="#cb14-20" aria-hidden="true"></a></span>
|
||||
<span id="cb14-21"><a href="#cb14-21" aria-hidden="true"></a><span class="co">/* ----- quit application ----- */</span></span>
|
||||
<span id="cb14-22"><a href="#cb14-22" aria-hidden="true"></a><span class="dt">void</span></span>
|
||||
<span id="cb14-23"><a href="#cb14-23" aria-hidden="true"></a>tfe_application_quit (GtkWindow *win) {</span>
|
||||
<span id="cb14-24"><a href="#cb14-24" aria-hidden="true"></a> <span class="cf">if</span> (pref_close_request_handler_id > <span class="dv">0</span>)</span>
|
||||
<span id="cb14-25"><a href="#cb14-25" aria-hidden="true"></a> g_signal_handler_disconnect (pref, pref_close_request_handler_id);</span>
|
||||
<span id="cb14-26"><a href="#cb14-26" aria-hidden="true"></a> <span class="cf">if</span> (alert_close_request_handler_id > <span class="dv">0</span>)</span>
|
||||
<span id="cb14-27"><a href="#cb14-27" aria-hidden="true"></a> g_signal_handler_disconnect (alert, alert_close_request_handler_id);</span>
|
||||
<span id="cb14-28"><a href="#cb14-28" aria-hidden="true"></a> g_clear_object (&settings);</span>
|
||||
<span id="cb14-29"><a href="#cb14-29" aria-hidden="true"></a> gtk_window_destroy (GTK_WINDOW (alert));</span>
|
||||
<span id="cb14-30"><a href="#cb14-30" aria-hidden="true"></a> gtk_window_destroy (GTK_WINDOW (pref));</span>
|
||||
<span id="cb14-31"><a href="#cb14-31" aria-hidden="true"></a> gtk_window_destroy (win);</span>
|
||||
<span id="cb14-32"><a href="#cb14-32" aria-hidden="true"></a>}</span>
|
||||
<span id="cb14-33"><a href="#cb14-33" aria-hidden="true"></a></span>
|
||||
<span id="cb14-34"><a href="#cb14-34" aria-hidden="true"></a>... ...</span>
|
||||
<span id="cb14-35"><a href="#cb14-35" aria-hidden="true"></a></span>
|
||||
<span id="cb14-36"><a href="#cb14-36" aria-hidden="true"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb14-37"><a href="#cb14-37" aria-hidden="true"></a>tfe_startup (GApplication *application) {</span>
|
||||
<span id="cb14-38"><a href="#cb14-38" aria-hidden="true"></a></span>
|
||||
<span id="cb14-39"><a href="#cb14-39" aria-hidden="true"></a> ... ...</span>
|
||||
<span id="cb14-40"><a href="#cb14-40" aria-hidden="true"></a></span>
|
||||
<span id="cb14-41"><a href="#cb14-41" aria-hidden="true"></a> pref = GTK_DIALOG (gtk_builder_get_object (build, <span class="st">"pref"</span>));</span>
|
||||
<span id="cb14-42"><a href="#cb14-42" aria-hidden="true"></a> pref_close_request_handler_id = g_signal_connect (GTK_DIALOG (pref), <span class="st">"close-request"</span>, G_CALLBACK (dialog_close_cb), NULL);</span>
|
||||
<span id="cb14-43"><a href="#cb14-43" aria-hidden="true"></a></span>
|
||||
<span id="cb14-44"><a href="#cb14-44" aria-hidden="true"></a> ... ... </span>
|
||||
<span id="cb14-45"><a href="#cb14-45" aria-hidden="true"></a>}</span></code></pre></div>
|
||||
<p>The function <code>tfe_application_quit</code> destroys top-level windows and quits the application. It first disconnects the handlers from the signal “close-request”.</p>
|
||||
<h3 id="alert-dialog">Alert dialog</h3>
|
||||
<p>If a user closes a page which hasn’t been saved, it is advisable to show an alert to confirm it. Alert dialog is used in this application for such a situation.</p>
|
||||
<div class="sourceCode" id="cb15"><pre class="sourceCode xml"><code class="sourceCode xml"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkDialog"</span><span class="ot"> id=</span><span class="st">"alert"</span><span class="kw">></span></span>
|
||||
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"title"</span><span class="kw">></span>Are you sure?<span class="kw"></property></span></span>
|
||||
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"resizable"</span><span class="kw">></span>FALSE<span class="kw"></property></span></span>
|
||||
<span id="cb15-4"><a href="#cb15-4" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"modal"</span><span class="kw">></span>TRUE<span class="kw"></property></span></span>
|
||||
<span id="cb15-5"><a href="#cb15-5" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"transient-for"</span><span class="kw">></span>win<span class="kw"></property></span></span>
|
||||
<span id="cb15-6"><a href="#cb15-6" aria-hidden="true"></a> <span class="kw"><child</span><span class="ot"> internal-child=</span><span class="st">"content_area"</span><span class="kw">></span></span>
|
||||
<span id="cb15-7"><a href="#cb15-7" aria-hidden="true"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkBox"</span><span class="kw">></span></span>
|
||||
<span id="cb15-8"><a href="#cb15-8" aria-hidden="true"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb15-9"><a href="#cb15-9" aria-hidden="true"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkBox"</span><span class="kw">></span></span>
|
||||
<span id="cb15-10"><a href="#cb15-10" aria-hidden="true"></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="cb15-11"><a href="#cb15-11" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"spacing"</span><span class="kw">></span>12<span class="kw"></property></span></span>
|
||||
<span id="cb15-12"><a href="#cb15-12" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"margin-start"</span><span class="kw">></span>12<span class="kw"></property></span></span>
|
||||
<span id="cb15-13"><a href="#cb15-13" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"margin-end"</span><span class="kw">></span>12<span class="kw"></property></span></span>
|
||||
<span id="cb15-14"><a href="#cb15-14" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"margin-top"</span><span class="kw">></span>12<span class="kw"></property></span></span>
|
||||
<span id="cb15-15"><a href="#cb15-15" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"margin-bottom"</span><span class="kw">></span>12<span class="kw"></property></span></span>
|
||||
<span id="cb15-16"><a href="#cb15-16" aria-hidden="true"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb15-17"><a href="#cb15-17" aria-hidden="true"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkImage"</span><span class="kw">></span></span>
|
||||
<span id="cb15-18"><a href="#cb15-18" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"icon-name"</span><span class="kw">></span>dialog-warning<span class="kw"></property></span></span>
|
||||
<span id="cb15-19"><a href="#cb15-19" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"icon-size"</span><span class="kw">></span>GTK_ICON_SIZE_LARGE<span class="kw"></property></span></span>
|
||||
<span id="cb15-20"><a href="#cb15-20" aria-hidden="true"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb15-21"><a href="#cb15-21" aria-hidden="true"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb15-22"><a href="#cb15-22" aria-hidden="true"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb15-23"><a href="#cb15-23" aria-hidden="true"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkLabel"</span><span class="ot"> id=</span><span class="st">"lb_alert"</span><span class="kw">></span></span>
|
||||
<span id="cb15-24"><a href="#cb15-24" aria-hidden="true"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb15-25"><a href="#cb15-25" aria-hidden="true"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb15-26"><a href="#cb15-26" aria-hidden="true"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb15-27"><a href="#cb15-27" aria-hidden="true"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb15-28"><a href="#cb15-28" aria-hidden="true"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb15-29"><a href="#cb15-29" aria-hidden="true"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb15-30"><a href="#cb15-30" aria-hidden="true"></a> <span class="kw"><child</span><span class="ot"> type=</span><span class="st">"action"</span><span class="kw">></span></span>
|
||||
<span id="cb15-31"><a href="#cb15-31" aria-hidden="true"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkButton"</span><span class="ot"> id=</span><span class="st">"btn_cancel"</span><span class="kw">></span></span>
|
||||
<span id="cb15-32"><a href="#cb15-32" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span>Cancel<span class="kw"></property></span></span>
|
||||
<span id="cb15-33"><a href="#cb15-33" aria-hidden="true"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb15-34"><a href="#cb15-34" aria-hidden="true"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb15-35"><a href="#cb15-35" aria-hidden="true"></a> <span class="kw"><child</span><span class="ot"> type=</span><span class="st">"action"</span><span class="kw">></span></span>
|
||||
<span id="cb15-36"><a href="#cb15-36" aria-hidden="true"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkButton"</span><span class="ot"> id=</span><span class="st">"btn_accept"</span><span class="kw">></span></span>
|
||||
<span id="cb15-37"><a href="#cb15-37" aria-hidden="true"></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="cb15-38"><a href="#cb15-38" aria-hidden="true"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb15-39"><a href="#cb15-39" aria-hidden="true"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb15-40"><a href="#cb15-40" aria-hidden="true"></a> <span class="kw"><action-widgets></span></span>
|
||||
<span id="cb15-41"><a href="#cb15-41" aria-hidden="true"></a> <span class="kw"><action-widget</span><span class="ot"> response=</span><span class="st">"cancel"</span><span class="ot"> default=</span><span class="st">"true"</span><span class="kw">></span>btn_cancel<span class="kw"></action-widget></span></span>
|
||||
<span id="cb15-42"><a href="#cb15-42" aria-hidden="true"></a> <span class="kw"><action-widget</span><span class="ot"> response=</span><span class="st">"accept"</span><span class="kw">></span>btn_accept<span class="kw"></action-widget></span></span>
|
||||
<span id="cb15-43"><a href="#cb15-43" aria-hidden="true"></a> <span class="kw"></action-widgets></span></span>
|
||||
<span id="cb15-44"><a href="#cb15-44" aria-hidden="true"></a> <span class="kw"><signal</span><span class="ot"> name=</span><span class="st">"response"</span><span class="ot"> handler=</span><span class="st">"alert_response_cb"</span><span class="ot"> swapped=</span><span class="st">"NO"</span><span class="ot"> object=</span><span class="st">"nb"</span><span class="kw">></signal></span></span>
|
||||
<span id="cb15-45"><a href="#cb15-45" aria-hidden="true"></a> <span class="kw"></object></span></span></code></pre></div>
|
||||
<p>This ui file describes the alert dialog. Some part are the same as preference dialog. There are two objects in the content area, GtkImage and GtkLabel.</p>
|
||||
<p>GtkImage shows an image. The image can comes from files, resources, icon theme and so on. The image above displays an icon from the current icon theme. You can see icons in the theme by <code>gtk4-icon-browser</code>.</p>
|
||||
<pre><code>$ gtk4-icon-browser</code></pre>
|
||||
<p>The icon named “dialog-warning” is something like this.</p>
|
||||
<figure>
|
||||
<img src="../image/dialog_warning.png" alt="" /><figcaption>dialog-warning icon is like …</figcaption>
|
||||
</figure>
|
||||
<p>These are made by my hand. The real image on the alert dialog is nicer.</p>
|
||||
<p>The GtkLabel <code>lb_alert</code> has no text yet. An alert message will be inserted by the program later.</p>
|
||||
<p>There are two child tags which have “action” type. They are button objects located in the action area. Action-widgets tag describes the actions of the buttons. <code>btn_cancel</code> button emits response signal with cancel response (<code>GTK_RESPONSE_CANCEL</code>) if it is clicked on. <code>btn_accept</code> button emits response signal with accept response (<code>GTK_RESPONSE_ACCEPT</code>) if it is clicked on. The response signal is connected to <code>alert_response_cb</code> handler.</p>
|
||||
<p>The alert dialog keeps alive while the application lives. The “close-request” signal is stopped by the handler <code>dialog_close_cb</code> like the preference dialog.</p>
|
||||
<h2 id="close-and-quit-handlers">Close and quit handlers</h2>
|
||||
<p>If a user closes a page or quits the application without saving the contents, the application alerts.</p>
|
||||
<div class="sourceCode" id="cb17"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true"></a><span class="dt">static</span> gboolean is_quit;</span>
|
||||
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true"></a></span>
|
||||
<span id="cb17-3"><a href="#cb17-3" aria-hidden="true"></a>... ...</span>
|
||||
<span id="cb17-4"><a href="#cb17-4" aria-hidden="true"></a></span>
|
||||
<span id="cb17-5"><a href="#cb17-5" aria-hidden="true"></a><span class="dt">void</span></span>
|
||||
<span id="cb17-6"><a href="#cb17-6" aria-hidden="true"></a>close_cb (GtkNotebook *nb) {</span>
|
||||
<span id="cb17-7"><a href="#cb17-7" aria-hidden="true"></a> is_quit = false;</span>
|
||||
<span id="cb17-8"><a href="#cb17-8" aria-hidden="true"></a> <span class="cf">if</span> (has_saved (GTK_NOTEBOOK (nb)))</span>
|
||||
<span id="cb17-9"><a href="#cb17-9" aria-hidden="true"></a> notebook_page_close (GTK_NOTEBOOK (nb));</span>
|
||||
<span id="cb17-10"><a href="#cb17-10" aria-hidden="true"></a> <span class="cf">else</span> {</span>
|
||||
<span id="cb17-11"><a href="#cb17-11" aria-hidden="true"></a> gtk_label_set_text (lb_alert, <span class="st">"Contents aren't saved yet.</span><span class="sc">\n</span><span class="st">Are you sure to close?"</span>);</span>
|
||||
<span id="cb17-12"><a href="#cb17-12" aria-hidden="true"></a> gtk_button_set_label (close_btn_close, <span class="st">"Close"</span>);</span>
|
||||
<span id="cb17-13"><a href="#cb17-13" aria-hidden="true"></a> gtk_widget_show (GTK_WIDGET (alert));</span>
|
||||
<span id="cb17-14"><a href="#cb17-14" aria-hidden="true"></a> }</span>
|
||||
<span id="cb17-15"><a href="#cb17-15" aria-hidden="true"></a>}</span>
|
||||
<span id="cb17-16"><a href="#cb17-16" aria-hidden="true"></a></span>
|
||||
<span id="cb17-17"><a href="#cb17-17" aria-hidden="true"></a>... ...</span>
|
||||
<span id="cb17-18"><a href="#cb17-18" aria-hidden="true"></a></span>
|
||||
<span id="cb17-19"><a href="#cb17-19" aria-hidden="true"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb17-20"><a href="#cb17-20" aria-hidden="true"></a>close_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {</span>
|
||||
<span id="cb17-21"><a href="#cb17-21" aria-hidden="true"></a> GtkNotebook *nb = GTK_NOTEBOOK (user_data);</span>
|
||||
<span id="cb17-22"><a href="#cb17-22" aria-hidden="true"></a> close_cb (nb);</span>
|
||||
<span id="cb17-23"><a href="#cb17-23" aria-hidden="true"></a>}</span>
|
||||
<span id="cb17-24"><a href="#cb17-24" aria-hidden="true"></a></span>
|
||||
<span id="cb17-25"><a href="#cb17-25" aria-hidden="true"></a>... ...</span>
|
||||
<span id="cb17-26"><a href="#cb17-26" aria-hidden="true"></a></span>
|
||||
<span id="cb17-27"><a href="#cb17-27" aria-hidden="true"></a><span class="dt">void</span></span>
|
||||
<span id="cb17-28"><a href="#cb17-28" aria-hidden="true"></a>alert_response_cb (GtkDialog *alert, <span class="dt">int</span> response_id, gpointer user_data) {</span>
|
||||
<span id="cb17-29"><a href="#cb17-29" aria-hidden="true"></a> GtkNotebook *nb = GTK_NOTEBOOK (user_data);</span>
|
||||
<span id="cb17-30"><a href="#cb17-30" aria-hidden="true"></a> GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (nb), GTK_TYPE_WINDOW);</span>
|
||||
<span id="cb17-31"><a href="#cb17-31" aria-hidden="true"></a></span>
|
||||
<span id="cb17-32"><a href="#cb17-32" aria-hidden="true"></a> gtk_widget_hide (GTK_WIDGET (alert));</span>
|
||||
<span id="cb17-33"><a href="#cb17-33" aria-hidden="true"></a> <span class="cf">if</span> (response_id == GTK_RESPONSE_ACCEPT) {</span>
|
||||
<span id="cb17-34"><a href="#cb17-34" aria-hidden="true"></a> <span class="cf">if</span> (is_quit)</span>
|
||||
<span id="cb17-35"><a href="#cb17-35" aria-hidden="true"></a> tfe_application_quit (GTK_WINDOW (win));</span>
|
||||
<span id="cb17-36"><a href="#cb17-36" aria-hidden="true"></a> <span class="cf">else</span></span>
|
||||
<span id="cb17-37"><a href="#cb17-37" aria-hidden="true"></a> notebook_page_close (nb);</span>
|
||||
<span id="cb17-38"><a href="#cb17-38" aria-hidden="true"></a> }</span>
|
||||
<span id="cb17-39"><a href="#cb17-39" aria-hidden="true"></a>}</span>
|
||||
<span id="cb17-40"><a href="#cb17-40" aria-hidden="true"></a></span>
|
||||
<span id="cb17-41"><a href="#cb17-41" aria-hidden="true"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb17-42"><a href="#cb17-42" aria-hidden="true"></a>quit_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {</span>
|
||||
<span id="cb17-43"><a href="#cb17-43" aria-hidden="true"></a> GtkNotebook *nb = GTK_NOTEBOOK (user_data);</span>
|
||||
<span id="cb17-44"><a href="#cb17-44" aria-hidden="true"></a> GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (nb), GTK_TYPE_WINDOW);</span>
|
||||
<span id="cb17-45"><a href="#cb17-45" aria-hidden="true"></a></span>
|
||||
<span id="cb17-46"><a href="#cb17-46" aria-hidden="true"></a> is_quit = true;</span>
|
||||
<span id="cb17-47"><a href="#cb17-47" aria-hidden="true"></a> <span class="cf">if</span> (has_saved_all (nb))</span>
|
||||
<span id="cb17-48"><a href="#cb17-48" aria-hidden="true"></a> tfe_application_quit (GTK_WINDOW (win));</span>
|
||||
<span id="cb17-49"><a href="#cb17-49" aria-hidden="true"></a> <span class="cf">else</span> {</span>
|
||||
<span id="cb17-50"><a href="#cb17-50" aria-hidden="true"></a> gtk_label_set_text (lb_alert, <span class="st">"Contents aren't saved yet.</span><span class="sc">\n</span><span class="st">Are you sure to quit?"</span>);</span>
|
||||
<span id="cb17-51"><a href="#cb17-51" aria-hidden="true"></a> gtk_button_set_label (btn_accept, <span class="st">"Quit"</span>);</span>
|
||||
<span id="cb17-52"><a href="#cb17-52" aria-hidden="true"></a> gtk_widget_show (GTK_WIDGET (alert));</span>
|
||||
<span id="cb17-53"><a href="#cb17-53" aria-hidden="true"></a> }</span>
|
||||
<span id="cb17-54"><a href="#cb17-54" aria-hidden="true"></a>}</span>
|
||||
<span id="cb17-55"><a href="#cb17-55" aria-hidden="true"></a></span>
|
||||
<span id="cb17-56"><a href="#cb17-56" aria-hidden="true"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb17-57"><a href="#cb17-57" aria-hidden="true"></a>tfe_startup (GApplication *application) {</span>
|
||||
<span id="cb17-58"><a href="#cb17-58" aria-hidden="true"></a></span>
|
||||
<span id="cb17-59"><a href="#cb17-59" aria-hidden="true"></a> ... ...</span>
|
||||
<span id="cb17-60"><a href="#cb17-60" aria-hidden="true"></a></span>
|
||||
<span id="cb17-61"><a href="#cb17-61" aria-hidden="true"></a> alert = GTK_DIALOG (gtk_builder_get_object (build, <span class="st">"alert"</span>));</span>
|
||||
<span id="cb17-62"><a href="#cb17-62" aria-hidden="true"></a> alert_close_request_handler_id = g_signal_connect (GTK_DIALOG (alert), <span class="st">"close-request"</span>, G_CALLBACK (dialog_close_cb), NULL);</span>
|
||||
<span id="cb17-63"><a href="#cb17-63" aria-hidden="true"></a> lb_alert = GTK_LABEL (gtk_builder_get_object (build, <span class="st">"lb_alert"</span>));</span>
|
||||
<span id="cb17-64"><a href="#cb17-64" aria-hidden="true"></a> btn_accept = GTK_BUTTON (gtk_builder_get_object (build, <span class="st">"btn_accept"</span>));</span>
|
||||
<span id="cb17-65"><a href="#cb17-65" aria-hidden="true"></a></span>
|
||||
<span id="cb17-66"><a href="#cb17-66" aria-hidden="true"></a> ... ...</span>
|
||||
<span id="cb17-67"><a href="#cb17-67" aria-hidden="true"></a></span>
|
||||
<span id="cb17-68"><a href="#cb17-68" aria-hidden="true"></a>}</span></code></pre></div>
|
||||
<p>The static variable <code>is_quit</code> is true when user tries to quit the application and false otherwise. When user presses “Ctrl-w”, <code>close_activated</code> handler is invoked. It just calls <code>close_cb</code>. When user clicks on the close button, <code>close_cb</code> handler is invoked.</p>
|
||||
<p>The handler sets <code>is_quit</code> to false. The function <code>has_saved</code> returns true if the current page has been saved. If it is true, it calls <code>notebook_page_close</code> to close the current page. Otherwise, it sets the message of the dialog and the label of the button, then shows the alert dialog.</p>
|
||||
<p>The response signal of the dialog is connected to the handler <code>alert_response_cb</code>. It hides the dialog first. Then checks the <code>response_id</code>. If it is <code>GTK_RESPONSE_ACCEPT</code>, which means user clicked on the close button, then it closes the current page. Otherwise it does nothing.</p>
|
||||
<p>When user press “Ctrl-q” or clicked on the quit menu, then <code>quit_activated</code> handler is invoked. The handler sets <code>is_quit</code> to true. The function <code>has_saved_all</code> returns true if all the pages have been saved. If it is true, it calls <code>tfe_application_quit</code> to quit the application. Otherwise, it sets the message of the dialog and the label of the button, then shows the alert dialog.</p>
|
||||
<p>If the user clicked on the buttons on the alert dialog, <code>alert_resoponse_cb</code> is invoked. It hides the dialog and checks the <code>response_id</code>. If it is <code>GTK_RESPONSE_ACCEPT</code>, which means user clicked on the quit button, then it calls <code>tfe_application_quit</code> to quit the application. Otherwise it does nothing.</p>
|
||||
<p>The static variables <code>alert</code>, <code>lb_alert</code> and <code>btn_accept</code> are set in the startup handler. And the signal “close-request” and <code>dialog_close_cb</code> handler are connected.</p>
|
||||
<div class="sourceCode" id="cb18"><pre class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb18-1"><a href="#cb18-1"></a>gboolean</span>
|
||||
<span id="cb18-2"><a href="#cb18-2"></a>has_saved (GtkNotebook *nb) {</span>
|
||||
<span id="cb18-3"><a href="#cb18-3"></a> g_return_val_if_fail (GTK_IS_NOTEBOOK (nb), false);</span>
|
||||
<span id="cb18-4"><a href="#cb18-4"></a></span>
|
||||
<span id="cb18-5"><a href="#cb18-5"></a> TfeTextView *tv;</span>
|
||||
<span id="cb18-6"><a href="#cb18-6"></a> GtkTextBuffer *tb;</span>
|
||||
<span id="cb18-7"><a href="#cb18-7"></a></span>
|
||||
<span id="cb18-8"><a href="#cb18-8"></a> tv = get_current_textview (nb);</span>
|
||||
<span id="cb18-9"><a href="#cb18-9"></a> tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));</span>
|
||||
<span id="cb18-10"><a href="#cb18-10"></a> <span class="cf">if</span> (gtk_text_buffer_get_modified (tb))</span>
|
||||
<span id="cb18-11"><a href="#cb18-11"></a> <span class="cf">return</span> false;</span>
|
||||
<span id="cb18-12"><a href="#cb18-12"></a> <span class="cf">else</span></span>
|
||||
<span id="cb18-13"><a href="#cb18-13"></a> <span class="cf">return</span> true;</span>
|
||||
<span id="cb18-14"><a href="#cb18-14"></a>}</span>
|
||||
<span id="cb18-15"><a href="#cb18-15"></a></span>
|
||||
<span id="cb18-16"><a href="#cb18-16"></a>gboolean</span>
|
||||
<span id="cb18-17"><a href="#cb18-17"></a>has_saved_all (GtkNotebook *nb) {</span>
|
||||
<span id="cb18-18"><a href="#cb18-18"></a> g_return_val_if_fail (GTK_IS_NOTEBOOK (nb), false);</span>
|
||||
<span id="cb18-19"><a href="#cb18-19"></a></span>
|
||||
<span id="cb18-20"><a href="#cb18-20"></a> <span class="dt">int</span> i, n;</span>
|
||||
<span id="cb18-21"><a href="#cb18-21"></a> GtkWidget *scr;</span>
|
||||
<span id="cb18-22"><a href="#cb18-22"></a> GtkWidget *tv;</span>
|
||||
<span id="cb18-23"><a href="#cb18-23"></a> GtkTextBuffer *tb;</span>
|
||||
<span id="cb18-24"><a href="#cb18-24"></a></span>
|
||||
<span id="cb18-25"><a href="#cb18-25"></a> n = gtk_notebook_get_n_pages (nb);</span>
|
||||
<span id="cb18-26"><a href="#cb18-26"></a> <span class="cf">for</span> (i = <span class="dv">0</span>; i < n; ++i) {</span>
|
||||
<span id="cb18-27"><a href="#cb18-27"></a> scr = gtk_notebook_get_nth_page (nb, i);</span>
|
||||
<span id="cb18-28"><a href="#cb18-28"></a> tv = gtk_scrolled_window_get_child (GTK_SCROLLED_WINDOW (scr));</span>
|
||||
<span id="cb18-29"><a href="#cb18-29"></a> tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));</span>
|
||||
<span id="cb18-30"><a href="#cb18-30"></a> <span class="cf">if</span> (gtk_text_buffer_get_modified (tb))</span>
|
||||
<span id="cb18-31"><a href="#cb18-31"></a> <span class="cf">return</span> false;</span>
|
||||
<span id="cb18-32"><a href="#cb18-32"></a> }</span>
|
||||
<span id="cb18-33"><a href="#cb18-33"></a> <span class="cf">return</span> true;</span>
|
||||
<span id="cb18-34"><a href="#cb18-34"></a>}</span></code></pre></div>
|
||||
<ul>
|
||||
<li>1-14: <code>has_saved</code> function.</li>
|
||||
<li>10: The function <code>gtk_text_buffer_get_modified</code> returns true if the content of the buffer has been modified since the modified flag had set false. The flag is set to false when:
|
||||
<ul>
|
||||
<li>the buffer is created.</li>
|
||||
<li>the contents of the buffer is replaced</li>
|
||||
<li>the contents of the buffer is saved to a file.</li>
|
||||
</ul></li>
|
||||
<li>11-13: This function returns true if the contents of the current page has been saved and no modification has been made. It returns false, if the current page has been modified and hasn’t been saved.</li>
|
||||
<li>16-33: <code>has_saved_all</code> function. This function is similar to <code>has_saved</code> function. It returns true if all the pages have been saved. It returns false if at least one page has been modified since it last had been saved.</li>
|
||||
</ul>
|
||||
<h2 id="notebook-page-tab">Notebook page tab</h2>
|
||||
<p>If you have some pages and edit them together, you might be confused which file needs to be saved. Common file editors changes the tab when the contents are modified. GtkTextBuffer provides “modified-changed” signal to notify the modification.</p>
|
||||
<div class="sourceCode" id="cb19"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true"></a>notebook_page_build (GtkNotebook *nb, GtkWidget *tv, <span class="dt">char</span> *filename) {</span>
|
||||
<span id="cb19-3"><a href="#cb19-3" aria-hidden="true"></a> ... ...</span>
|
||||
<span id="cb19-4"><a href="#cb19-4" aria-hidden="true"></a> g_signal_connect (GTK_TEXT_VIEW (tv), <span class="st">"change-file"</span>, G_CALLBACK (file_changed_cb), NULL);</span>
|
||||
<span id="cb19-5"><a href="#cb19-5" aria-hidden="true"></a> g_signal_connect (tb, <span class="st">"modified-changed"</span>, G_CALLBACK (modified_changed_cb), tv);</span>
|
||||
<span id="cb19-6"><a href="#cb19-6" aria-hidden="true"></a>}</span></code></pre></div>
|
||||
<p>When a page is built, connect “change-file” and “modified-changed” signals to <code>file_changed_cb</code> and <code>modified_changed_cb</code> handlers respectively.</p>
|
||||
<div class="sourceCode" id="cb20"><pre class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb20-1"><a href="#cb20-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb20-2"><a href="#cb20-2"></a>file_changed_cb (TfeTextView *tv) {</span>
|
||||
<span id="cb20-3"><a href="#cb20-3"></a> GtkWidget *nb = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_NOTEBOOK);</span>
|
||||
<span id="cb20-4"><a href="#cb20-4"></a> GtkWidget *scr;</span>
|
||||
<span id="cb20-5"><a href="#cb20-5"></a> GtkWidget *label;</span>
|
||||
<span id="cb20-6"><a href="#cb20-6"></a> GFile *file;</span>
|
||||
<span id="cb20-7"><a href="#cb20-7"></a> <span class="dt">char</span> *filename;</span>
|
||||
<span id="cb20-8"><a href="#cb20-8"></a></span>
|
||||
<span id="cb20-9"><a href="#cb20-9"></a> <span class="cf">if</span> (! GTK_IS_NOTEBOOK (nb)) <span class="co">/* tv not connected to nb yet */</span></span>
|
||||
<span id="cb20-10"><a href="#cb20-10"></a> <span class="cf">return</span>;</span>
|
||||
<span id="cb20-11"><a href="#cb20-11"></a> file = tfe_text_view_get_file (tv);</span>
|
||||
<span id="cb20-12"><a href="#cb20-12"></a> scr = gtk_widget_get_parent (GTK_WIDGET (tv));</span>
|
||||
<span id="cb20-13"><a href="#cb20-13"></a> <span class="cf">if</span> (G_IS_FILE (file)) {</span>
|
||||
<span id="cb20-14"><a href="#cb20-14"></a> filename = g_file_get_basename (file);</span>
|
||||
<span id="cb20-15"><a href="#cb20-15"></a> g_object_unref (file);</span>
|
||||
<span id="cb20-16"><a href="#cb20-16"></a> } <span class="cf">else</span></span>
|
||||
<span id="cb20-17"><a href="#cb20-17"></a> filename = get_untitled ();</span>
|
||||
<span id="cb20-18"><a href="#cb20-18"></a> label = gtk_label_new (filename);</span>
|
||||
<span id="cb20-19"><a href="#cb20-19"></a> gtk_notebook_set_tab_label (GTK_NOTEBOOK (nb), scr, label);</span>
|
||||
<span id="cb20-20"><a href="#cb20-20"></a>}</span>
|
||||
<span id="cb20-21"><a href="#cb20-21"></a></span>
|
||||
<span id="cb20-22"><a href="#cb20-22"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb20-23"><a href="#cb20-23"></a>modified_changed_cb (GtkTextBuffer *tb, gpointer user_data) {</span>
|
||||
<span id="cb20-24"><a href="#cb20-24"></a> TfeTextView *tv = TFE_TEXT_VIEW (user_data);</span>
|
||||
<span id="cb20-25"><a href="#cb20-25"></a> GtkWidget *scr = gtk_widget_get_parent (GTK_WIDGET (tv));</span>
|
||||
<span id="cb20-26"><a href="#cb20-26"></a> GtkWidget *nb = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_NOTEBOOK);</span>
|
||||
<span id="cb20-27"><a href="#cb20-27"></a> GtkWidget *label;</span>
|
||||
<span id="cb20-28"><a href="#cb20-28"></a> <span class="dt">const</span> <span class="dt">char</span> *filename;</span>
|
||||
<span id="cb20-29"><a href="#cb20-29"></a> <span class="dt">char</span> *text;</span>
|
||||
<span id="cb20-30"><a href="#cb20-30"></a></span>
|
||||
<span id="cb20-31"><a href="#cb20-31"></a> <span class="cf">if</span> (! GTK_IS_NOTEBOOK (nb)) <span class="co">/* tv not connected to nb yet */</span></span>
|
||||
<span id="cb20-32"><a href="#cb20-32"></a> <span class="cf">return</span>;</span>
|
||||
<span id="cb20-33"><a href="#cb20-33"></a> <span class="cf">else</span> <span class="cf">if</span> (gtk_text_buffer_get_modified (tb)) {</span>
|
||||
<span id="cb20-34"><a href="#cb20-34"></a> filename = gtk_notebook_get_tab_label_text (GTK_NOTEBOOK (nb), scr);</span>
|
||||
<span id="cb20-35"><a href="#cb20-35"></a> text = g_strdup_printf (<span class="st">"*%s"</span>, filename);</span>
|
||||
<span id="cb20-36"><a href="#cb20-36"></a> label = gtk_label_new (text);</span>
|
||||
<span id="cb20-37"><a href="#cb20-37"></a> g_free (text);</span>
|
||||
<span id="cb20-38"><a href="#cb20-38"></a> gtk_notebook_set_tab_label (GTK_NOTEBOOK (nb), scr, label);</span>
|
||||
<span id="cb20-39"><a href="#cb20-39"></a> } <span class="cf">else</span></span>
|
||||
<span id="cb20-40"><a href="#cb20-40"></a> file_changed_cb (tv);</span>
|
||||
<span id="cb20-41"><a href="#cb20-41"></a>}</span></code></pre></div>
|
||||
<ul>
|
||||
<li>1-20: <code>file_changed_cb</code> handler.</li>
|
||||
<li>9-10: If the signal emits during the page is being built, it is possible that <code>tv</code> isn’t a descendant of <code>nb</code>. That is, there’s no page corresponds to <code>tv</code>. Then, it isn’t necessary to change the name of the tab because no tab exists.</li>
|
||||
<li>13-15: If <code>file</code> is GFile, then it gets the filename and unrefs <code>file</code>.</li>
|
||||
<li>16-17: Otherwise, <code>file</code> is probably NULL and it assigns “Untitled” related name to <code>filename</code></li>
|
||||
<li>18-19: Creates GtkLabel with <code>filename</code> and sets the tab of the page with the GtkLabel.</li>
|
||||
<li>22-41: <code>modified_changed_cb</code> handler.</li>
|
||||
<li>31-32: If <code>tv</code> isn’t a descendant of <code>nb</code>, then nothing needs to be done.</li>
|
||||
<li>33-35: If the content is modified, then it gets the text of the tab and adds asterisk at the beginning of the text.</li>
|
||||
<li>36-38: Sets the tab with the asterisk prepended text.</li>
|
||||
<li>39-40: Otherwise the modified bit is off. It is because content is saved. It calls <code>file_changed_cb</code> and resets the filename, that means it leaves out the asterisk.</li>
|
||||
</ul>
|
||||
<h2 id="font">Font</h2>
|
||||
<h3 id="gtkfontbutton-and-gtkfontchooser">GtkFontButton and GtkFontChooser</h3>
|
||||
<p>The GtkFontButton is a button which displays the current font. It opens a font chooser dialog if a user clicked on the button. A user can change the font (family, style, weight and size) with the dialog. Then the button keeps the new font and displays it.</p>
|
||||
<p>The button and its signal “font-set” is initialized in the application startup process.</p>
|
||||
<div class="sourceCode" id="cb21"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb21-2"><a href="#cb21-2" aria-hidden="true"></a>font_set_cb (GtkFontButton *fontbtn, gpointer user_data) {</span>
|
||||
<span id="cb21-3"><a href="#cb21-3" aria-hidden="true"></a> GtkWindow *win = GTK_WINDOW (user_data);</span>
|
||||
<span id="cb21-4"><a href="#cb21-4" aria-hidden="true"></a> PangoFontDescription *pango_font_desc;</span>
|
||||
<span id="cb21-5"><a href="#cb21-5" aria-hidden="true"></a></span>
|
||||
<span id="cb21-6"><a href="#cb21-6" aria-hidden="true"></a> pango_font_desc = gtk_font_chooser_get_font_desc (GTK_FONT_CHOOSER (fontbtn));</span>
|
||||
<span id="cb21-7"><a href="#cb21-7" aria-hidden="true"></a> set_font_for_display_with_pango_font_desc (win, pango_font_desc);</span>
|
||||
<span id="cb21-8"><a href="#cb21-8" aria-hidden="true"></a>}</span>
|
||||
<span id="cb21-9"><a href="#cb21-9" aria-hidden="true"></a></span>
|
||||
<span id="cb21-10"><a href="#cb21-10" aria-hidden="true"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb21-11"><a href="#cb21-11" aria-hidden="true"></a>tfe_startup (GApplication *application) {</span>
|
||||
<span id="cb21-12"><a href="#cb21-12" aria-hidden="true"></a></span>
|
||||
<span id="cb21-13"><a href="#cb21-13" aria-hidden="true"></a> ... ...</span>
|
||||
<span id="cb21-14"><a href="#cb21-14" aria-hidden="true"></a></span>
|
||||
<span id="cb21-15"><a href="#cb21-15" aria-hidden="true"></a> fontbtn = GTK_FONT_BUTTON (gtk_builder_get_object (build, <span class="st">"fontbtn"</span>));</span>
|
||||
<span id="cb21-16"><a href="#cb21-16" aria-hidden="true"></a> g_signal_connect (fontbtn, <span class="st">"font-set"</span>, G_CALLBACK (font_set_cb), win);</span>
|
||||
<span id="cb21-17"><a href="#cb21-17" aria-hidden="true"></a></span>
|
||||
<span id="cb21-18"><a href="#cb21-18" aria-hidden="true"></a> ... ...</span>
|
||||
<span id="cb21-19"><a href="#cb21-19" aria-hidden="true"></a></span>
|
||||
<span id="cb21-20"><a href="#cb21-20" aria-hidden="true"></a>}</span></code></pre></div>
|
||||
<p>In the startup handler, set the variable <code>fontbtn</code> to point the GtkFontButton object. Then connect the “font-set” signal to <code>font_set_cb</code> handler. The signal “font-set” is emitted when the user selects a font.</p>
|
||||
<p>GtkFontChooser is an interface implemented by GtkFontButton. The function <code>gtk_font_chooser_get_font_desc</code> gets the PangoFontDescription of the currently selected font.</p>
|
||||
<p>Another function <code>gtk_font_chooser_get_font</code> returns a font name which includes family, style, weight and size. I thought it might be able to be applied to tfe editor. The font name can be used to the <code>font</code> property of GtkTextTag as it is. But it can’t be used to the CSS without converting the string to fit. CSS is appropriate to change the font of entire text in all the buffers. I think GtkTextTag is less appropriate. If you know a good solution, please post it to <a href="https://github.com/ToshioCP/Gtk4-tutorial/issues">issue</a> and let me know.</p>
|
||||
<p>It takes many codes to set the CSS from the PangoFontDescription so the task is left to the function <code>set_font_for_display_with_pango_font_desc</code>.</p>
|
||||
<h3 id="css-and-pango">CSS and Pango</h3>
|
||||
<p>A new file <code>css.c</code> is made for functions related to CSS.</p>
|
||||
<div class="sourceCode" id="cb22"><pre class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb22-1"><a href="#cb22-1"></a><span class="pp">#include </span><span class="im">"tfe.h"</span></span>
|
||||
<span id="cb22-2"><a href="#cb22-2"></a></span>
|
||||
<span id="cb22-3"><a href="#cb22-3"></a><span class="dt">void</span></span>
|
||||
<span id="cb22-4"><a href="#cb22-4"></a>set_css_for_display (GtkWindow *win, <span class="dt">const</span> <span class="dt">char</span> *css) {</span>
|
||||
<span id="cb22-5"><a href="#cb22-5"></a> GdkDisplay *display;</span>
|
||||
<span id="cb22-6"><a href="#cb22-6"></a></span>
|
||||
<span id="cb22-7"><a href="#cb22-7"></a> display = gtk_widget_get_display (GTK_WIDGET (win));</span>
|
||||
<span id="cb22-8"><a href="#cb22-8"></a> GtkCssProvider *provider = gtk_css_provider_new ();</span>
|
||||
<span id="cb22-9"><a href="#cb22-9"></a> gtk_css_provider_load_from_data (provider, css, -<span class="dv">1</span>);</span>
|
||||
<span id="cb22-10"><a href="#cb22-10"></a> gtk_style_context_add_provider_for_display (display, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_USER);</span>
|
||||
<span id="cb22-11"><a href="#cb22-11"></a>}</span>
|
||||
<span id="cb22-12"><a href="#cb22-12"></a></span>
|
||||
<span id="cb22-13"><a href="#cb22-13"></a><span class="dt">void</span></span>
|
||||
<span id="cb22-14"><a href="#cb22-14"></a>set_font_for_display (GtkWindow *win, <span class="dt">const</span> <span class="dt">char</span> *fontfamily, <span class="dt">const</span> <span class="dt">char</span> *fontstyle, <span class="dt">const</span> <span class="dt">char</span> *fontweight, <span class="dt">int</span> fontsize) {</span>
|
||||
<span id="cb22-15"><a href="#cb22-15"></a> <span class="dt">char</span> *textview_css;</span>
|
||||
<span id="cb22-16"><a href="#cb22-16"></a></span>
|
||||
<span id="cb22-17"><a href="#cb22-17"></a> textview_css = g_strdup_printf (<span class="st">"textview {padding: 10px; font-family: </span><span class="sc">\"</span><span class="st">%s</span><span class="sc">\"</span><span class="st">; font-style: %s; font-weight: %s; font-size: %dpt;}"</span>,</span>
|
||||
<span id="cb22-18"><a href="#cb22-18"></a> fontfamily, fontstyle, fontweight, fontsize);</span>
|
||||
<span id="cb22-19"><a href="#cb22-19"></a> set_css_for_display (win, textview_css);</span>
|
||||
<span id="cb22-20"><a href="#cb22-20"></a> g_free (textview_css);</span>
|
||||
<span id="cb22-21"><a href="#cb22-21"></a>} </span>
|
||||
<span id="cb22-22"><a href="#cb22-22"></a></span>
|
||||
<span id="cb22-23"><a href="#cb22-23"></a><span class="dt">void</span></span>
|
||||
<span id="cb22-24"><a href="#cb22-24"></a>set_font_for_display_with_pango_font_desc (GtkWindow *win, PangoFontDescription *pango_font_desc) {</span>
|
||||
<span id="cb22-25"><a href="#cb22-25"></a> PangoStyle pango_style;</span>
|
||||
<span id="cb22-26"><a href="#cb22-26"></a> PangoWeight pango_weight; </span>
|
||||
<span id="cb22-27"><a href="#cb22-27"></a> <span class="dt">const</span> <span class="dt">char</span> *family;</span>
|
||||
<span id="cb22-28"><a href="#cb22-28"></a> <span class="dt">const</span> <span class="dt">char</span> *style;</span>
|
||||
<span id="cb22-29"><a href="#cb22-29"></a> <span class="dt">const</span> <span class="dt">char</span> *weight;</span>
|
||||
<span id="cb22-30"><a href="#cb22-30"></a> <span class="dt">int</span> fontsize;</span>
|
||||
<span id="cb22-31"><a href="#cb22-31"></a></span>
|
||||
<span id="cb22-32"><a href="#cb22-32"></a> family = pango_font_description_get_family (pango_font_desc);</span>
|
||||
<span id="cb22-33"><a href="#cb22-33"></a> pango_style = pango_font_description_get_style (pango_font_desc);</span>
|
||||
<span id="cb22-34"><a href="#cb22-34"></a> <span class="cf">switch</span> (pango_style) {</span>
|
||||
<span id="cb22-35"><a href="#cb22-35"></a> <span class="cf">case</span> PANGO_STYLE_NORMAL:</span>
|
||||
<span id="cb22-36"><a href="#cb22-36"></a> style = <span class="st">"normal"</span>;</span>
|
||||
<span id="cb22-37"><a href="#cb22-37"></a> <span class="cf">break</span>;</span>
|
||||
<span id="cb22-38"><a href="#cb22-38"></a> <span class="cf">case</span> PANGO_STYLE_ITALIC:</span>
|
||||
<span id="cb22-39"><a href="#cb22-39"></a> style = <span class="st">"italic"</span>;</span>
|
||||
<span id="cb22-40"><a href="#cb22-40"></a> <span class="cf">break</span>;</span>
|
||||
<span id="cb22-41"><a href="#cb22-41"></a> <span class="cf">case</span> PANGO_STYLE_OBLIQUE:</span>
|
||||
<span id="cb22-42"><a href="#cb22-42"></a> style = <span class="st">"oblique"</span>;</span>
|
||||
<span id="cb22-43"><a href="#cb22-43"></a> <span class="cf">break</span>;</span>
|
||||
<span id="cb22-44"><a href="#cb22-44"></a> <span class="cf">default</span>:</span>
|
||||
<span id="cb22-45"><a href="#cb22-45"></a> style = <span class="st">"normal"</span>;</span>
|
||||
<span id="cb22-46"><a href="#cb22-46"></a> <span class="cf">break</span>;</span>
|
||||
<span id="cb22-47"><a href="#cb22-47"></a> }</span>
|
||||
<span id="cb22-48"><a href="#cb22-48"></a> pango_weight = pango_font_description_get_weight (pango_font_desc);</span>
|
||||
<span id="cb22-49"><a href="#cb22-49"></a> <span class="cf">switch</span> (pango_weight) {</span>
|
||||
<span id="cb22-50"><a href="#cb22-50"></a> <span class="cf">case</span> PANGO_WEIGHT_THIN:</span>
|
||||
<span id="cb22-51"><a href="#cb22-51"></a> weight = <span class="st">"100"</span>;</span>
|
||||
<span id="cb22-52"><a href="#cb22-52"></a> <span class="cf">break</span>;</span>
|
||||
<span id="cb22-53"><a href="#cb22-53"></a> <span class="cf">case</span> PANGO_WEIGHT_ULTRALIGHT:</span>
|
||||
<span id="cb22-54"><a href="#cb22-54"></a> weight = <span class="st">"200"</span>;</span>
|
||||
<span id="cb22-55"><a href="#cb22-55"></a> <span class="cf">break</span>;</span>
|
||||
<span id="cb22-56"><a href="#cb22-56"></a> <span class="cf">case</span> PANGO_WEIGHT_LIGHT:</span>
|
||||
<span id="cb22-57"><a href="#cb22-57"></a> weight = <span class="st">"300"</span>;</span>
|
||||
<span id="cb22-58"><a href="#cb22-58"></a> <span class="cf">break</span>;</span>
|
||||
<span id="cb22-59"><a href="#cb22-59"></a> <span class="cf">case</span> PANGO_WEIGHT_SEMILIGHT:</span>
|
||||
<span id="cb22-60"><a href="#cb22-60"></a> weight = <span class="st">"350"</span>;</span>
|
||||
<span id="cb22-61"><a href="#cb22-61"></a> <span class="cf">break</span>;</span>
|
||||
<span id="cb22-62"><a href="#cb22-62"></a> <span class="cf">case</span> PANGO_WEIGHT_BOOK:</span>
|
||||
<span id="cb22-63"><a href="#cb22-63"></a> weight = <span class="st">"380"</span>;</span>
|
||||
<span id="cb22-64"><a href="#cb22-64"></a> <span class="cf">break</span>;</span>
|
||||
<span id="cb22-65"><a href="#cb22-65"></a> <span class="cf">case</span> PANGO_WEIGHT_NORMAL:</span>
|
||||
<span id="cb22-66"><a href="#cb22-66"></a> weight = <span class="st">"400"</span>; <span class="co">/* or "normal" */</span></span>
|
||||
<span id="cb22-67"><a href="#cb22-67"></a> <span class="cf">break</span>;</span>
|
||||
<span id="cb22-68"><a href="#cb22-68"></a> <span class="cf">case</span> PANGO_WEIGHT_MEDIUM:</span>
|
||||
<span id="cb22-69"><a href="#cb22-69"></a> weight = <span class="st">"500"</span>;</span>
|
||||
<span id="cb22-70"><a href="#cb22-70"></a> <span class="cf">break</span>;</span>
|
||||
<span id="cb22-71"><a href="#cb22-71"></a> <span class="cf">case</span> PANGO_WEIGHT_SEMIBOLD:</span>
|
||||
<span id="cb22-72"><a href="#cb22-72"></a> weight = <span class="st">"600"</span>;</span>
|
||||
<span id="cb22-73"><a href="#cb22-73"></a> <span class="cf">break</span>;</span>
|
||||
<span id="cb22-74"><a href="#cb22-74"></a> <span class="cf">case</span> PANGO_WEIGHT_BOLD:</span>
|
||||
<span id="cb22-75"><a href="#cb22-75"></a> weight = <span class="st">"700"</span>; <span class="co">/* or "bold" */</span></span>
|
||||
<span id="cb22-76"><a href="#cb22-76"></a> <span class="cf">break</span>;</span>
|
||||
<span id="cb22-77"><a href="#cb22-77"></a> <span class="cf">case</span> PANGO_WEIGHT_ULTRABOLD:</span>
|
||||
<span id="cb22-78"><a href="#cb22-78"></a> weight = <span class="st">"800"</span>;</span>
|
||||
<span id="cb22-79"><a href="#cb22-79"></a> <span class="cf">break</span>;</span>
|
||||
<span id="cb22-80"><a href="#cb22-80"></a> <span class="cf">case</span> PANGO_WEIGHT_HEAVY:</span>
|
||||
<span id="cb22-81"><a href="#cb22-81"></a> weight = <span class="st">"900"</span>;</span>
|
||||
<span id="cb22-82"><a href="#cb22-82"></a> <span class="cf">break</span>;</span>
|
||||
<span id="cb22-83"><a href="#cb22-83"></a> <span class="cf">case</span> PANGO_WEIGHT_ULTRAHEAVY:</span>
|
||||
<span id="cb22-84"><a href="#cb22-84"></a> weight = <span class="st">"900"</span>; <span class="co">/* In PangoWeight definition, the weight is 1000. But CSS allows the weight below 900. */</span></span>
|
||||
<span id="cb22-85"><a href="#cb22-85"></a> <span class="cf">break</span>;</span>
|
||||
<span id="cb22-86"><a href="#cb22-86"></a> <span class="cf">default</span>:</span>
|
||||
<span id="cb22-87"><a href="#cb22-87"></a> weight = <span class="st">"normal"</span>;</span>
|
||||
<span id="cb22-88"><a href="#cb22-88"></a> <span class="cf">break</span>;</span>
|
||||
<span id="cb22-89"><a href="#cb22-89"></a> }</span>
|
||||
<span id="cb22-90"><a href="#cb22-90"></a> fontsize = pango_font_description_get_size (pango_font_desc) / PANGO_SCALE;</span>
|
||||
<span id="cb22-91"><a href="#cb22-91"></a> set_font_for_display (win, family, style, weight, fontsize);</span>
|
||||
<span id="cb22-92"><a href="#cb22-92"></a>}</span></code></pre></div>
|
||||
<ul>
|
||||
<li>3-11: <code>set_css_for_display</code>. This function sets CSS for GdkDisplay. The content of the function is the same as the part of startup handler in the previous version of <code>tfeapplication.c</code>.</li>
|
||||
<li>13-20: <code>set_font_for_display</code>. This function sets CSS with font-family, font-style, font-weight and font-size.
|
||||
<ul>
|
||||
<li>font-family is a name of a font. For example, sans-serif, monospace, Helvetica and “Noto Sans” are font-family. It is recommended to quote font family names that contains white space, digits, or punctuation characters other than hyphens.</li>
|
||||
<li>font-style is one of normal, italic and oblique.</li>
|
||||
<li>font-weight specifies the thickness of a font. It is normal or bold. It can be specified with a number between 100 and 900. Normal is the same as 400. Bold is 700.</li>
|
||||
<li>font-size specifies the size of a font. Small, medium, large and 12pt are font-size.</li>
|
||||
</ul></li>
|
||||
<li>17: Makes CSS text. The function <code>g_strdup_printf</code> creates a new string with printf-like formatting.</li>
|
||||
<li>23-92: <code>set_font_for_display_with_pango_font_desc</code>. This function takes out font-family, font-style, font-weight and font-size from the PangoFontDescription object and calls <code>set_font</code>for_display`.</li>
|
||||
<li>32: Gets the font-family of <code>pango_font_desc</code>.</li>
|
||||
<li>33-47: Gets the font-style of <code>pango_font_desc</code>. The functions <code>pango_font_description_get_style</code> returns an enumerated value.</li>
|
||||
<li>48-89: Gets the font-weight of <code>pango_font_desc</code>. The function <code>pango_font_description_get_weight</code> returns an enumerated value. They corresponds to the numbers from 100 to 900.</li>
|
||||
<li>90: Gets the font-size of <code>pango_font_desc</code>. The function <code>pango_font_description_get_size</code> returns the size of a font. The unit of this size is (1/PANGO_SCALE)pt. If the font size is 10pt, the function returns 10<em>PANGO_SCALE. PANGO_SCALE is defined as 1024. Therefore, 10</em>PANGO_SCALE is 10240.</li>
|
||||
<li>91: calls <code>set_font_for_display</code> to set CSS for the GdkDisplay.</li>
|
||||
</ul>
|
||||
<p>For further information, see <a href="https://docs.gtk.org/Pango/">Pango API Reference</a>.</p>
|
||||
<h2 id="gsettings">GSettings</h2>
|
||||
<p>We want to maintain the font data after the application quits. There are some ways to implement it.</p>
|
||||
<ul>
|
||||
<li>Make a configuration file. For example, a text file “~/.config/tfe/font.cfg” keeps font information.</li>
|
||||
<li>Use GSettings object. The basic idea of GSettings are similar to configuration file. Configuration information data is put into a database file.</li>
|
||||
</ul>
|
||||
<p>The coding with GSettings object is simple and easy. However, it is a bit hard to understand the concept. This subsection describes the concept first and then how to program it.</p>
|
||||
<h3 id="gsettings-schema">GSettings schema</h3>
|
||||
<p>GSettings schema describes a set of keys, value types and some other information. GSettings object uses this schema and it writes/reads the value of a key to/from the right place in the database.</p>
|
||||
<ul>
|
||||
<li>A schema has an id. The id must be unique. We often use the same string as application id, but schema id and application id are different. You can use different name from application id. Schema id is a string delimited by periods. For example, “com.github.ToshioCP.tfe” is a correct schema id.</li>
|
||||
<li>A schema usually has a path. The path is a location in the database. Each key is stored under the path. For example, if a key <code>font</code> is defined with a path <code>/com/github/ToshioCP/tfe/</code>, the key’s location in the database is <code>/com/github/ToshioCP/tfe/font</code>. Path is a string begins with and ends with a slash (<code>/</code>). And it is delimited by slashes.</li>
|
||||
<li>GSettings save information as key-value style. Key is a string begins with lower case characters followed by lower case, digit or dash (<code>-</code>) and ends with lower case or digit. No consecutive dashes are allowed. Values can be any type. GSettings stores values as GVariant type, which may contain, for example, integer, double, boolean, string or complex types like an array. The type of values needs to be defined in the schema.</li>
|
||||
<li>A default value needs to be set for each key.</li>
|
||||
<li>A summery and description can be set for each key optionally.</li>
|
||||
</ul>
|
||||
<p>Schemas are described in an XML format. For example,</p>
|
||||
<div class="sourceCode" id="cb23"><pre class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><span id="cb23-1"><a href="#cb23-1"></a><span class="kw"><?xml</span> version="1.0" encoding="UTF-8"<span class="kw">?></span></span>
|
||||
<span id="cb23-2"><a href="#cb23-2"></a><span class="kw"><schemalist></span></span>
|
||||
<span id="cb23-3"><a href="#cb23-3"></a> <span class="kw"><schema</span><span class="ot"> path=</span><span class="st">"/com/github/ToshioCP/tfe/"</span><span class="ot"> id=</span><span class="st">"com.github.ToshioCP.tfe"</span><span class="kw">></span></span>
|
||||
<span id="cb23-4"><a href="#cb23-4"></a> <span class="kw"><key</span><span class="ot"> name=</span><span class="st">"font"</span><span class="ot"> type=</span><span class="st">"s"</span><span class="kw">></span></span>
|
||||
<span id="cb23-5"><a href="#cb23-5"></a> <span class="kw"><default></span>'Monospace 12'<span class="kw"></default></span></span>
|
||||
<span id="cb23-6"><a href="#cb23-6"></a> <span class="kw"><summary></span>Font<span class="kw"></summary></span></span>
|
||||
<span id="cb23-7"><a href="#cb23-7"></a> <span class="kw"><description></span>The font to be used for textview.<span class="kw"></description></span></span>
|
||||
<span id="cb23-8"><a href="#cb23-8"></a> <span class="kw"></key></span></span>
|
||||
<span id="cb23-9"><a href="#cb23-9"></a> <span class="kw"></schema></span></span>
|
||||
<span id="cb23-10"><a href="#cb23-10"></a><span class="kw"></schemalist></span></span></code></pre></div>
|
||||
<ul>
|
||||
<li>4: The type attribute is “s”. It is <a href="https://docs.gtk.org/glib/struct.VariantType.html#gvariant-type-strings">GLib API Reference, GVariant Type Strings</a>. Other common types are:
|
||||
<ul>
|
||||
<li>“b”: gboolean</li>
|
||||
<li>“i”: gint32.</li>
|
||||
<li>“d”: double.</li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
<p>Further information is in <a href="https://docs.gtk.org/glib/struct.VariantType.html">Glib API Reference, VarientType</a>.</p>
|
||||
<h3 id="gsettings-1">gsettings</h3>
|
||||
<p>First, let’s try <code>gsettings</code> application. It is a configuration tool for GSettings.</p>
|
||||
<pre><code>$ gsettings help
|
||||
Usage:
|
||||
gsettings --version
|
||||
gsettings [--schemadir SCHEMADIR] COMMAND [ARGS?]
|
||||
|
||||
Commands:
|
||||
help Show this information
|
||||
list-schemas List installed schemas
|
||||
list-relocatable-schemas List relocatable schemas
|
||||
list-keys List keys in a schema
|
||||
list-children List children of a schema
|
||||
list-recursively List keys and values, recursively
|
||||
range Queries the range of a key
|
||||
describe Queries the description of a key
|
||||
get Get the value of a key
|
||||
set Set the value of a key
|
||||
reset Reset the value of a key
|
||||
reset-recursively Reset all values in a given schema
|
||||
writable Check if a key is writable
|
||||
monitor Watch for changes
|
||||
|
||||
Use "gsettings help COMMAND" to get detailed help.</code></pre>
|
||||
<p>List schemas.</p>
|
||||
<pre><code>$ gsettings list-schemas
|
||||
org.gnome.rhythmbox.podcast
|
||||
ca.desrt.dconf-editor.Demo.Empty
|
||||
org.gnome.gedit.preferences.ui
|
||||
org.gnome.evolution-data-server.calendar
|
||||
org.gnome.rhythmbox.plugins.generic-player
|
||||
|
||||
... ...
|
||||
</code></pre>
|
||||
<p>Each line is an id of a schema. Each schema has a key-value configuration data. You can see them with list-recursively command. Let’s look at the keys and values of <code>org.gnome.calculator</code> schema.</p>
|
||||
<pre><code>$ gsettings list-recursively org.gnome.calculator
|
||||
org.gnome.calculator source-currency ''
|
||||
org.gnome.calculator source-units 'degree'
|
||||
org.gnome.calculator button-mode 'basic'
|
||||
org.gnome.calculator target-currency ''
|
||||
org.gnome.calculator base 10
|
||||
org.gnome.calculator angle-units 'degrees'
|
||||
org.gnome.calculator word-size 64
|
||||
org.gnome.calculator accuracy 9
|
||||
org.gnome.calculator show-thousands false
|
||||
org.gnome.calculator window-position (122, 77)
|
||||
org.gnome.calculator refresh-interval 604800
|
||||
org.gnome.calculator target-units 'radian'
|
||||
org.gnome.calculator precision 2000
|
||||
org.gnome.calculator number-format 'automatic'
|
||||
org.gnome.calculator show-zeroes false</code></pre>
|
||||
<p>This schema is used by Gnome Calculator. Run the calculator and change the mode, then check the schema again.</p>
|
||||
<pre><code>$ gnome-calculator</code></pre>
|
||||
<figure>
|
||||
<img src="../image/gnome_calculator_basic.png" alt="" /><figcaption>gnome-calculator basic mode</figcaption>
|
||||
</figure>
|
||||
<p>Then, change the mode to advanced and quit.</p>
|
||||
<figure>
|
||||
<img src="../image/gnome_calculator_advanced.png" alt="" /><figcaption>gnome-calculator advanced mode</figcaption>
|
||||
</figure>
|
||||
<p>Run gsettings and check whether the value of <code>button-mode</code> changes.</p>
|
||||
<pre><code>$ gsettings list-recursively org.gnome.calculator
|
||||
|
||||
... ...
|
||||
|
||||
org.gnome.calculator button-mode 'advanced'
|
||||
|
||||
... ...
|
||||
</code></pre>
|
||||
<p>Now we know that Gnome Calculator used gsettings and it has set <code>button-mode</code> key to “advanced”. The value remains even the calculator quits. So when the calculator is run again, it will appear as an advanced mode calculator.</p>
|
||||
<h3 id="glib-compile-schemas">glib-compile-schemas</h3>
|
||||
<p>GSettings schemas are specified with an XML format. The XML schema files must have the filename extension <code>.gschema.xml</code>. The following is the XML schema file for the application <code>tfe</code>.</p>
|
||||
<div class="sourceCode" id="cb29"><pre class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><span id="cb29-1"><a href="#cb29-1"></a><span class="kw"><?xml</span> version="1.0" encoding="UTF-8"<span class="kw">?></span></span>
|
||||
<span id="cb29-2"><a href="#cb29-2"></a><span class="kw"><schemalist></span></span>
|
||||
<span id="cb29-3"><a href="#cb29-3"></a> <span class="kw"><schema</span><span class="ot"> path=</span><span class="st">"/com/github/ToshioCP/tfe/"</span><span class="ot"> id=</span><span class="st">"com.github.ToshioCP.tfe"</span><span class="kw">></span></span>
|
||||
<span id="cb29-4"><a href="#cb29-4"></a> <span class="kw"><key</span><span class="ot"> name=</span><span class="st">"font"</span><span class="ot"> type=</span><span class="st">"s"</span><span class="kw">></span></span>
|
||||
<span id="cb29-5"><a href="#cb29-5"></a> <span class="kw"><default></span>'Monospace 12'<span class="kw"></default></span></span>
|
||||
<span id="cb29-6"><a href="#cb29-6"></a> <span class="kw"><summary></span>Font<span class="kw"></summary></span></span>
|
||||
<span id="cb29-7"><a href="#cb29-7"></a> <span class="kw"><description></span>The font to be used for textview.<span class="kw"></description></span></span>
|
||||
<span id="cb29-8"><a href="#cb29-8"></a> <span class="kw"></key></span></span>
|
||||
<span id="cb29-9"><a href="#cb29-9"></a> <span class="kw"></schema></span></span>
|
||||
<span id="cb29-10"><a href="#cb29-10"></a><span class="kw"></schemalist></span></span></code></pre></div>
|
||||
<p>The filename is “com.github.ToshioCP.tfe.gschema.xml”. Schema XML filenames are usually the schema id followed by “.gschema.xml” suffix. You can use different name from schema id, but it is not recommended.</p>
|
||||
<ul>
|
||||
<li>2: The top level element is <code><schemalist></code>.</li>
|
||||
<li>3: schema tag has <code>path</code> and <code>id</code> attributes. A path determines where the settings are stored in the conceptual global tree of settings. An id identifies the schema.</li>
|
||||
<li>4: Key tag has two attributes. Name is the name of the key. Type is the type of the value of the key and specified with <a href="https://docs.gtk.org/glib/struct.VariantType.html">GLib API Reference, VariantType</a>.</li>
|
||||
<li>5: default value of the key <code>font</code> is <code>Monospace 12</code>.</li>
|
||||
<li>6: Summery and description elements describes the key. They are optional, but it is recommended to add them in the XML file.</li>
|
||||
</ul>
|
||||
<p>The XML file is compiled by glib-compile-schemas. When compiling, <code>glib-compile-schemas</code> compiles all the XML files which have “.gschema.xml” file extension in the directory given as an argument. It converts the XML file into a binary file <code>gschemas.compiled</code>. Suppose the XML file above is under <code>tfe6</code> directory.</p>
|
||||
<pre><code>$ glib-compile-schemas tfe6</code></pre>
|
||||
<p>Then, <code>gschemas.compiled</code> is generated under <code>tfe6</code>. When you test your application, set <code>GSETTINGS_SCHEMA_DIR</code> so that GSettings objet can find <code>gschemas.compiled</code>.</p>
|
||||
<pre><code>$ GSETTINGS_SCHEMA_DIR=(the directory gschemas.compiled is located):$GSETTINGS_SCHEMA_DIR (your application name)</code></pre>
|
||||
<p>This is because GSettings object searches <code>GSETTINGS_SCHEMA_DIR</code> for <code>gschemas.compiled</code>.</p>
|
||||
<p>GSettings object looks for this file by the following process.</p>
|
||||
<ul>
|
||||
<li>It searches <code>glib-2.0/schemas</code> subdirectories of all the directories specified in the environment variable <code>XDG_DATA_DIRS</code>. Most common directory is <code>/usr/share/glib-2.0/schemas</code>.</li>
|
||||
<li>If <code>GSETTINGS_SCHEMA_DIR</code> environment variable is defined, it searches all the directories specified in the variable. <code>GSETTINGS_SCHEMA_DIR</code> can specify multiple directories delimited by colon (:).</li>
|
||||
</ul>
|
||||
<p>In the directories above, all the <code>.gschema.xml</code> files are stored. Therefore, when you install your application, follow the instruction below to install your schemas.</p>
|
||||
<ol type="1">
|
||||
<li>Make <code>.gschema.xml</code> file.</li>
|
||||
<li>Copy it to one of the directories above. For example, <code>/usr/local/share/glib-2.0/schemas</code>.</li>
|
||||
<li>Run <code>glib-compile-schemas</code> on the directory above.</li>
|
||||
</ol>
|
||||
<h3 id="meson.build">Meson.build</h3>
|
||||
<p>Meson provides <code>gnome.compile_schemas</code> method to compile XML file in the build directory. This is used to test the application. Write the following to the <code>meson.build</code> file.</p>
|
||||
<pre><code>gnome.compile_schemas(build_by_default: true, depend_files: 'com.github.ToshioCP.tfe.gschema.xml')</code></pre>
|
||||
<ul>
|
||||
<li><code>build_by_default</code>: If it is true, the target will be build by default.</li>
|
||||
<li><code>depend_files</code>: XML files to be compiled.</li>
|
||||
</ul>
|
||||
<p>In the example above, this method runs <code>glib-compile-schemas</code> to generate <code>gschemas.compiled</code> from the XML file <code>com.github.ToshioCP.tfe.gschema.xml</code>. The file <code>gschemas.compiled</code> is located under the build directory. If you run meson as <code>meson _build</code> and ninja as <code>ninja -C _build</code>, then it is under <code>_build</code> directory.</p>
|
||||
<p>After compilation, you can test your application like this:</p>
|
||||
<pre><code>$ GSETTINGS_SCHEMA_DIR=_build:$GSETTINGS_SCHEMA_DIR _build/tfe</code></pre>
|
||||
<h3 id="gsettings-object-and-g_settings_bind">GSettings object and g_settings_bind</h3>
|
||||
<p>Write gsettings related codes to `tfeapplication.c’.</p>
|
||||
<div class="sourceCode" id="cb34"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb34-1"><a href="#cb34-1" aria-hidden="true"></a>... ...</span>
|
||||
<span id="cb34-2"><a href="#cb34-2" aria-hidden="true"></a><span class="dt">static</span> GSettings *settings;</span>
|
||||
<span id="cb34-3"><a href="#cb34-3" aria-hidden="true"></a>... ...</span>
|
||||
<span id="cb34-4"><a href="#cb34-4" aria-hidden="true"></a></span>
|
||||
<span id="cb34-5"><a href="#cb34-5" aria-hidden="true"></a><span class="dt">void</span></span>
|
||||
<span id="cb34-6"><a href="#cb34-6" aria-hidden="true"></a>tfe_application_quit (GtkWindow *win) {</span>
|
||||
<span id="cb34-7"><a href="#cb34-7" aria-hidden="true"></a> ... ...</span>
|
||||
<span id="cb34-8"><a href="#cb34-8" aria-hidden="true"></a> g_clear_object (&settings);</span>
|
||||
<span id="cb34-9"><a href="#cb34-9" aria-hidden="true"></a> ... ...</span>
|
||||
<span id="cb34-10"><a href="#cb34-10" aria-hidden="true"></a>}</span>
|
||||
<span id="cb34-11"><a href="#cb34-11" aria-hidden="true"></a></span>
|
||||
<span id="cb34-12"><a href="#cb34-12" aria-hidden="true"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb34-13"><a href="#cb34-13" aria-hidden="true"></a>tfe_startup (GApplication *application) {</span>
|
||||
<span id="cb34-14"><a href="#cb34-14" aria-hidden="true"></a> ... ...</span>
|
||||
<span id="cb34-15"><a href="#cb34-15" aria-hidden="true"></a> settings = g_settings_new (<span class="st">"com.github.ToshioCP.tfe"</span>);</span>
|
||||
<span id="cb34-16"><a href="#cb34-16" aria-hidden="true"></a> g_settings_bind (settings, <span class="st">"font"</span>, fontbtn, <span class="st">"font"</span>, G_SETTINGS_BIND_DEFAULT);</span>
|
||||
<span id="cb34-17"><a href="#cb34-17" aria-hidden="true"></a> ... ...</span>
|
||||
<span id="cb34-18"><a href="#cb34-18" aria-hidden="true"></a>}</span></code></pre></div>
|
||||
<p>Static variable <code>settings</code> keeps a pointer to GSettings instance. Before application quits, the application releases the GSettings instance. The function <code>g_clear_object</code> is used.</p>
|
||||
<p>Startup handler creates GSettings instance with the schema id “com.github.ToshioCP.tfe” and assigns the pointer to <code>settings</code>. The function <code>g_settings_bind</code> connects the settings keys (key and value) and the “font” property of <code>fontbtn</code>. Then the two values will be always the same. If one value changes then the other will automatically change.</p>
|
||||
<p>You need to make an effort to understand GSettings concept, but coding is very simple. Just create a GSettings object and bind it to a property of an object.</p>
|
||||
<h2 id="installation">Installation</h2>
|
||||
<p>It is a good idea to install your application in <code>$HOME/local/bin</code> directory if you have installed Gtk4 from the source (See Section 2). Then you need to put <code>--prefix=$HOME/local</code> option to meson like this.</p>
|
||||
<pre><code>$ meson --prefix=$HOME/local _build</code></pre>
|
||||
<p>If you’ve installed Gtk4 from the distribution package, <code>--prefix</code> option isn’t necessary. You just install <code>tfe</code> to the default bin directory like <code>/usr/local/bin</code>.</p>
|
||||
<p>Modify <code>meson.build</code> and add install option and set it true in executable function.</p>
|
||||
<pre><code>executable('tfe', sourcefiles, resources, dependencies: gtkdep, export_dynamic: true, install: true)</code></pre>
|
||||
<p>You can install your application by:</p>
|
||||
<pre><code>$ ninja -C _build install</code></pre>
|
||||
<p>However, you need to do one more thing. Copy your XML file to <code>$HOME/local/share/glib-2.0/schemas/</code>, which is specified in <code>GSETTINGS_SCHEMA_DIR</code> environment variable, and run <code>glib-compile-schemas</code> on that directory.</p>
|
||||
<pre><code>schema_dir = get_option('prefix') / get_option('datadir') / 'glib-2.0/schemas/'
|
||||
install_data('com.github.ToshioCP.tfe.gschema.xml', install_dir: schema_dir)</code></pre>
|
||||
<ul>
|
||||
<li>get_option: This function returns the value of build options. The default value of the option ‘prefix’ is “/usr/local”, but it is “$HOME/local” because we have run meson with prefix option. The default value of the option ‘datadir’ is “share”. The operator ‘/’ connects the strings with ‘/’ separator. So, <code>$HOME/local/share/glib-2.0/schemas</code> is assigned to the variable <code>schema_dir</code>.</li>
|
||||
<li>install_data: This function installs the data to the install directory.</li>
|
||||
</ul>
|
||||
<p>Meson can run a post compile script.</p>
|
||||
<pre><code>meson.add_install_script('glib-compile-schemas', schema_dir)</code></pre>
|
||||
<p>This method runs ‘glib-compile-schemas’ with an argument <code>schema_dir</code>. The following is <code>meson.build</code>.</p>
|
||||
<div class="sourceCode" id="cb40"><pre class="sourceCode numberSource numberLines"><code class="sourceCode"><span id="cb40-1"><a href="#cb40-1"></a>project('tfe', 'c')</span>
|
||||
<span id="cb40-2"><a href="#cb40-2"></a></span>
|
||||
<span id="cb40-3"><a href="#cb40-3"></a>gtkdep = dependency('gtk4')</span>
|
||||
<span id="cb40-4"><a href="#cb40-4"></a></span>
|
||||
<span id="cb40-5"><a href="#cb40-5"></a>gnome=import('gnome')</span>
|
||||
<span id="cb40-6"><a href="#cb40-6"></a>resources = gnome.compile_resources('resources','tfe.gresource.xml')</span>
|
||||
<span id="cb40-7"><a href="#cb40-7"></a>gnome.compile_schemas(build_by_default: true, depend_files: 'com.github.ToshioCP.tfe.gschema.xml')</span>
|
||||
<span id="cb40-8"><a href="#cb40-8"></a></span>
|
||||
<span id="cb40-9"><a href="#cb40-9"></a>sourcefiles=files('tfeapplication.c', 'tfenotebook.c', 'css.c', '../tfetextview/tfetextview.c')</span>
|
||||
<span id="cb40-10"><a href="#cb40-10"></a></span>
|
||||
<span id="cb40-11"><a href="#cb40-11"></a>executable('tfe', sourcefiles, resources, dependencies: gtkdep, export_dynamic: true, install: true)</span>
|
||||
<span id="cb40-12"><a href="#cb40-12"></a></span>
|
||||
<span id="cb40-13"><a href="#cb40-13"></a>schema_dir = get_option('prefix') / get_option('datadir') / 'glib-2.0/schemas/'</span>
|
||||
<span id="cb40-14"><a href="#cb40-14"></a>install_data('com.github.ToshioCP.tfe.gschema.xml', install_dir: schema_dir)</span>
|
||||
<span id="cb40-15"><a href="#cb40-15"></a>meson.add_install_script('glib-compile-schemas', schema_dir)</span></code></pre></div>
|
||||
<p>Source files of <code>tfe</code> is under <a href="../src/tfe6">src/tfe6</a> directory. Copy them to your temporary directory and try to compile and install.</p>
|
||||
<pre><code>$ meson --prefix=$HOME/local _build
|
||||
$ ninja -C _build
|
||||
$ GSETTINGS_SCHEMA_DIR=_build:$GSETTINGS_SCHEMA_DIR _build/tfe
|
||||
$ ninja -C _build install
|
||||
$ tfe
|
||||
$ ls $HOME/local/bin
|
||||
... ...
|
||||
... tfe
|
||||
... ...
|
||||
$ ls $HOME/local/share/glib-2.0/schemas
|
||||
com.github.ToshioCP.tfe.gschema.xml
|
||||
gschema.dtd
|
||||
gschemas.compiled
|
||||
... ...</code></pre>
|
||||
<p>The screenshot is as follows.</p>
|
||||
<figure>
|
||||
<img src="../image/tfe6.png" alt="" /><figcaption>tfe6</figcaption>
|
||||
</figure>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec19.html">Section 19</a>, Next: <a href="sec21.html">Section 21</a></p>
|
||||
</body>
|
||||
</html>
|
771
docs/sec21.html
Normal file
771
docs/sec21.html
Normal file
|
@ -0,0 +1,771 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>Gtk4 tutorial for beginners</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
body {width: 1080px; margin: 0 auto; font-size: large;}
|
||||
h2 {padding: 10px; background-color: #d0f0d0; }
|
||||
div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
|
||||
th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
|
||||
td {padding: 2px 6px; border: 1px solid;}
|
||||
img {display: block; margin-left: auto; margin-right: auto;}
|
||||
figcaption {text-align: center;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec20.html">Section 20</a>, Next: <a href="sec22.html">Section 22</a></p>
|
||||
<h1 id="template-xml-and-composite-widget">Template XML and composite widget</h1>
|
||||
<p>The tfe program in the previous section is not so good because many things are crammed into <code>tfepplication.c</code>. Many static variables in <code>tfepplication.c</code> shows that.</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="dt">static</span> GtkDialog *pref;</span>
|
||||
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true"></a><span class="dt">static</span> GtkFontButton *fontbtn;</span>
|
||||
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true"></a><span class="dt">static</span> GSettings *settings;</span>
|
||||
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true"></a><span class="dt">static</span> GtkDialog *alert;</span>
|
||||
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true"></a><span class="dt">static</span> GtkLabel *lb_alert;</span>
|
||||
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true"></a><span class="dt">static</span> GtkButton *btn_accept;</span>
|
||||
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true"></a></span>
|
||||
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true"></a><span class="dt">static</span> gulong pref_close_request_handler_id = <span class="dv">0</span>;</span>
|
||||
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true"></a><span class="dt">static</span> gulong alert_close_request_handler_id = <span class="dv">0</span>;</span>
|
||||
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true"></a><span class="dt">static</span> gboolean is_quit;</span></code></pre></div>
|
||||
<p>Generally, if there are many global or static variables in the program, it is not a good program. Such programs are difficult to maintain.</p>
|
||||
<p>The file <code>tfeapplication.c</code> should be divided into several files.</p>
|
||||
<ul>
|
||||
<li><code>tfeapplication.c</code> only has codes related to GtkApplication.</li>
|
||||
<li>A file for GtkApplicationWindow</li>
|
||||
<li>A file for a preference dialog</li>
|
||||
<li>A file for an alert dialog</li>
|
||||
</ul>
|
||||
<p>The preference dialog is defined by a ui file. And it has GtkBox, GtkLabel and GtkFontButton in it. Such widget is called composite widget. Composite widget is a child object (not child widget) of a widget. For example, the preference composite widget is a child object of GtkDialog. Composite widget can be built from template XML. Next subsection shows how to build a preference dialog.</p>
|
||||
<h2 id="preference-dialog">Preference dialog</h2>
|
||||
<p>First, write a template XML file.</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"><template</span><span class="ot"> class=</span><span class="st">"TfePref"</span><span class="ot"> parent=</span><span class="st">"GtkDialog"</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>Preferences<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">"resizable"</span><span class="kw">></span>FALSE<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">"modal"</span><span class="kw">></span>TRUE<span class="kw"></property></span></span>
|
||||
<span id="cb2-7"><a href="#cb2-7"></a> <span class="kw"><child</span><span class="ot"> internal-child=</span><span class="st">"content_area"</span><span class="kw">></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">"content_area"</span><span class="kw">></span></span>
|
||||
<span id="cb2-9"><a href="#cb2-9"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb2-10"><a href="#cb2-10"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkBox"</span><span class="ot"> id=</span><span class="st">"pref_boxh"</span><span class="kw">></span></span>
|
||||
<span id="cb2-11"><a href="#cb2-11"></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-12"><a href="#cb2-12"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"spacing"</span><span class="kw">></span>12<span class="kw"></property></span></span>
|
||||
<span id="cb2-13"><a href="#cb2-13"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"margin-start"</span><span class="kw">></span>12<span class="kw"></property></span></span>
|
||||
<span id="cb2-14"><a href="#cb2-14"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"margin-end"</span><span class="kw">></span>12<span class="kw"></property></span></span>
|
||||
<span id="cb2-15"><a href="#cb2-15"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"margin-top"</span><span class="kw">></span>12<span class="kw"></property></span></span>
|
||||
<span id="cb2-16"><a href="#cb2-16"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"margin-bottom"</span><span class="kw">></span>12<span class="kw"></property></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"><object</span><span class="ot"> class=</span><span class="st">"GtkLabel"</span><span class="ot"> id=</span><span class="st">"fontlabel"</span><span class="kw">></span></span>
|
||||
<span id="cb2-19"><a href="#cb2-19"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span>Font:<span class="kw"></property></span></span>
|
||||
<span id="cb2-20"><a href="#cb2-20"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"xalign"</span><span class="kw">></span>1<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">"GtkFontButton"</span><span class="ot"> id=</span><span class="st">"fontbtn"</span><span class="kw">></span></span>
|
||||
<span id="cb2-25"><a href="#cb2-25"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb2-26"><a href="#cb2-26"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb2-27"><a href="#cb2-27"></a> <span class="kw"></object></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>
|
||||
<span id="cb2-30"><a href="#cb2-30"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb2-31"><a href="#cb2-31"></a> <span class="kw"></template></span></span>
|
||||
<span id="cb2-32"><a href="#cb2-32"></a><span class="kw"></interface></span></span></code></pre></div>
|
||||
<ul>
|
||||
<li>3: Template tag specifies a composite widget. The value of a class attribute is the object name of the composite widget. This XML file names the object “TfePref”. It is defined in a C source file and it will be shown later. A parent attribute specifies the direct parent object of the composite widget. <code>TfePref</code> is a child object of <code>GtkDialog</code>. Therefore the value of the attribute is “GtkDialog”. A parent attribute is optional but it is recommended to specify.</li>
|
||||
</ul>
|
||||
<p>Other lines are the same as before. The object <code>TfePref</code> is defined in <code>tfepref.h</code> and <code>tfepref.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">#ifndef __TFE_PREF_H__</span></span>
|
||||
<span id="cb3-2"><a href="#cb3-2"></a><span class="pp">#define __TFE_PREF_H__</span></span>
|
||||
<span id="cb3-3"><a href="#cb3-3"></a></span>
|
||||
<span id="cb3-4"><a href="#cb3-4"></a><span class="pp">#include </span><span class="im"><gtk/gtk.h></span></span>
|
||||
<span id="cb3-5"><a href="#cb3-5"></a></span>
|
||||
<span id="cb3-6"><a href="#cb3-6"></a><span class="pp">#define TFE_TYPE_PREF tfe_pref_get_type ()</span></span>
|
||||
<span id="cb3-7"><a href="#cb3-7"></a>G_DECLARE_FINAL_TYPE (TfePref, tfe_pref, TFE, PREF, GtkDialog)</span>
|
||||
<span id="cb3-8"><a href="#cb3-8"></a></span>
|
||||
<span id="cb3-9"><a href="#cb3-9"></a>GtkWidget *</span>
|
||||
<span id="cb3-10"><a href="#cb3-10"></a>tfe_pref_new (GtkWindow *win);</span>
|
||||
<span id="cb3-11"><a href="#cb3-11"></a></span>
|
||||
<span id="cb3-12"><a href="#cb3-12"></a><span class="pp">#endif </span><span class="co">/* __TFE_PREF_H__ */</span></span></code></pre></div>
|
||||
<ul>
|
||||
<li>6-7: When you define a new object, you need to write these two lines. Refer to <a href="sec8.html">Section 8</a>.</li>
|
||||
<li>9-10: <code>tfe_pref_new</code> creates a new TfePref object. It has a parameter <code>win</code> which is used as a transient parent window to show the dialog.</li>
|
||||
</ul>
|
||||
<div class="sourceCode" id="cb4"><pre class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1"></a><span class="pp">#include </span><span class="im">"tfepref.h"</span></span>
|
||||
<span id="cb4-2"><a href="#cb4-2"></a></span>
|
||||
<span id="cb4-3"><a href="#cb4-3"></a><span class="kw">struct</span> _TfePref</span>
|
||||
<span id="cb4-4"><a href="#cb4-4"></a>{</span>
|
||||
<span id="cb4-5"><a href="#cb4-5"></a> GtkDialog parent;</span>
|
||||
<span id="cb4-6"><a href="#cb4-6"></a> GSettings *settings;</span>
|
||||
<span id="cb4-7"><a href="#cb4-7"></a> GtkFontButton *fontbtn;</span>
|
||||
<span id="cb4-8"><a href="#cb4-8"></a>};</span>
|
||||
<span id="cb4-9"><a href="#cb4-9"></a></span>
|
||||
<span id="cb4-10"><a href="#cb4-10"></a>G_DEFINE_TYPE (TfePref, tfe_pref, GTK_TYPE_DIALOG);</span>
|
||||
<span id="cb4-11"><a href="#cb4-11"></a></span>
|
||||
<span id="cb4-12"><a href="#cb4-12"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb4-13"><a href="#cb4-13"></a>tfe_pref_dispose (GObject *gobject) {</span>
|
||||
<span id="cb4-14"><a href="#cb4-14"></a> TfePref *pref = TFE_PREF (gobject);</span>
|
||||
<span id="cb4-15"><a href="#cb4-15"></a></span>
|
||||
<span id="cb4-16"><a href="#cb4-16"></a> g_clear_object (&pref->settings);</span>
|
||||
<span id="cb4-17"><a href="#cb4-17"></a> G_OBJECT_CLASS (tfe_pref_parent_class)->dispose (gobject);</span>
|
||||
<span id="cb4-18"><a href="#cb4-18"></a>}</span>
|
||||
<span id="cb4-19"><a href="#cb4-19"></a></span>
|
||||
<span id="cb4-20"><a href="#cb4-20"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb4-21"><a href="#cb4-21"></a>tfe_pref_init (TfePref *pref) {</span>
|
||||
<span id="cb4-22"><a href="#cb4-22"></a> gtk_widget_init_template (GTK_WIDGET (pref));</span>
|
||||
<span id="cb4-23"><a href="#cb4-23"></a> pref->settings = g_settings_new (<span class="st">"com.github.ToshioCP.tfe"</span>);</span>
|
||||
<span id="cb4-24"><a href="#cb4-24"></a> g_settings_bind (pref->settings, <span class="st">"font"</span>, pref->fontbtn, <span class="st">"font"</span>, G_SETTINGS_BIND_DEFAULT);</span>
|
||||
<span id="cb4-25"><a href="#cb4-25"></a>}</span>
|
||||
<span id="cb4-26"><a href="#cb4-26"></a></span>
|
||||
<span id="cb4-27"><a href="#cb4-27"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb4-28"><a href="#cb4-28"></a>tfe_pref_class_init (TfePrefClass *class) {</span>
|
||||
<span id="cb4-29"><a href="#cb4-29"></a> GObjectClass *object_class = G_OBJECT_CLASS (class);</span>
|
||||
<span id="cb4-30"><a href="#cb4-30"></a></span>
|
||||
<span id="cb4-31"><a href="#cb4-31"></a> object_class->dispose = tfe_pref_dispose;</span>
|
||||
<span id="cb4-32"><a href="#cb4-32"></a> gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class), <span class="st">"/com/github/ToshioCP/tfe/tfepref.ui"</span>);</span>
|
||||
<span id="cb4-33"><a href="#cb4-33"></a> gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), TfePref, fontbtn);</span>
|
||||
<span id="cb4-34"><a href="#cb4-34"></a>}</span>
|
||||
<span id="cb4-35"><a href="#cb4-35"></a></span>
|
||||
<span id="cb4-36"><a href="#cb4-36"></a>GtkWidget *</span>
|
||||
<span id="cb4-37"><a href="#cb4-37"></a>tfe_pref_new (GtkWindow *win) {</span>
|
||||
<span id="cb4-38"><a href="#cb4-38"></a> <span class="cf">return</span> GTK_WIDGET (g_object_new (TFE_TYPE_PREF, <span class="st">"transient-for"</span>, win, NULL));</span>
|
||||
<span id="cb4-39"><a href="#cb4-39"></a>}</span></code></pre></div>
|
||||
<ul>
|
||||
<li>3-8: The structure of an instance of this object. It has two variables, settings and fontbtn.</li>
|
||||
<li>10: <code>G_DEFINE_TYPE</code> macro. This macro registers the TfePref type.</li>
|
||||
<li>12-18: Dispose handler. This handler is called when the instance is destroyed. The destruction process has two stages, disposing and finalizing. When disposing, the instance releases all the references (to the other instances). TfePref object holds a reference to the GSettings instance. It is released in line 16. After that parents dispose handler is called in line 17. For further information about destruction process, refer to <a href="sec11.html">Section 11</a>.</li>
|
||||
<li>27-34: Class initialization function.</li>
|
||||
<li>31: Set the dispose handler.</li>
|
||||
<li>32: <code>gtk_widget_class_set_template_from_resource</code> function associates the description in the XML file (<code>tfepref.ui</code>) with the widget. At this moment no instance is created. It just make the class to know the structure of the object. That’s why the top level tag is not <code><object></code> but <code><template></code> in the XML file.</li>
|
||||
<li>33: <code>gtk_widget_class_bind_template_child</code> function binds a private variable of the object with a child object in the template. This function is a macro. The name of the private variable (<code>fontbtn</code> in line 7) and the id <code>fontbtn</code> in the XML file (line 24) must be the same. The pointer to the instance will be assigned to the variable <code>fontbtn</code> when the instance is created.</li>
|
||||
<li>20-25: Instance initialization function.</li>
|
||||
<li>22: Creates the instance based on the template in the class. The template has been made during the class initialization process.</li>
|
||||
<li>23: Create GSettings instance with the id “com.github.ToshioCP.tfe”.</li>
|
||||
<li>24: Bind the font key in the GSettings object to the font property in the GtkFontButton.</li>
|
||||
<li>36-39: The function <code>tfe_pref_new</code> creates an instance of TfePref. The parameter <code>win</code> is a transient parent.</li>
|
||||
</ul>
|
||||
<p>Now, It is very simple to use this dialog. A caller just creates this object and shows it.</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>TfePref *pref;</span>
|
||||
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true"></a>pref = tfe_pref_new (win) <span class="co">/* win is the top-level window */</span></span>
|
||||
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true"></a>gtk_widget_show (GTK_WINDOW (win));</span></code></pre></div>
|
||||
<p>This instance is automatically destroyed when a user clicks on the close button. That’s all. If you want to show the dialog again, just create and show it.</p>
|
||||
<h2 id="alert-dialog">Alert dialog</h2>
|
||||
<p>It is almost same as preference dialog.</p>
|
||||
<p>Its XML file is:</p>
|
||||
<div class="sourceCode" id="cb6"><pre class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><span id="cb6-1"><a href="#cb6-1"></a><span class="kw"><?xml</span> version="1.0" encoding="UTF-8"<span class="kw">?></span></span>
|
||||
<span id="cb6-2"><a href="#cb6-2"></a><span class="kw"><interface></span></span>
|
||||
<span id="cb6-3"><a href="#cb6-3"></a> <span class="kw"><template</span><span class="ot"> class=</span><span class="st">"TfeAlert"</span><span class="ot"> parent=</span><span class="st">"GtkDialog"</span><span class="kw">></span></span>
|
||||
<span id="cb6-4"><a href="#cb6-4"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"title"</span><span class="kw">></span>Are you sure?<span class="kw"></property></span></span>
|
||||
<span id="cb6-5"><a href="#cb6-5"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"resizable"</span><span class="kw">></span>FALSE<span class="kw"></property></span></span>
|
||||
<span id="cb6-6"><a href="#cb6-6"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"modal"</span><span class="kw">></span>TRUE<span class="kw"></property></span></span>
|
||||
<span id="cb6-7"><a href="#cb6-7"></a> <span class="kw"><child</span><span class="ot"> internal-child=</span><span class="st">"content_area"</span><span class="kw">></span></span>
|
||||
<span id="cb6-8"><a href="#cb6-8"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkBox"</span><span class="kw">></span></span>
|
||||
<span id="cb6-9"><a href="#cb6-9"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb6-10"><a href="#cb6-10"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkBox"</span><span class="kw">></span></span>
|
||||
<span id="cb6-11"><a href="#cb6-11"></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="cb6-12"><a href="#cb6-12"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"spacing"</span><span class="kw">></span>12<span class="kw"></property></span></span>
|
||||
<span id="cb6-13"><a href="#cb6-13"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"margin-start"</span><span class="kw">></span>12<span class="kw"></property></span></span>
|
||||
<span id="cb6-14"><a href="#cb6-14"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"margin-end"</span><span class="kw">></span>12<span class="kw"></property></span></span>
|
||||
<span id="cb6-15"><a href="#cb6-15"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"margin-top"</span><span class="kw">></span>12<span class="kw"></property></span></span>
|
||||
<span id="cb6-16"><a href="#cb6-16"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"margin-bottom"</span><span class="kw">></span>12<span class="kw"></property></span></span>
|
||||
<span id="cb6-17"><a href="#cb6-17"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb6-18"><a href="#cb6-18"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkImage"</span><span class="kw">></span></span>
|
||||
<span id="cb6-19"><a href="#cb6-19"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"icon-name"</span><span class="kw">></span>dialog-warning<span class="kw"></property></span></span>
|
||||
<span id="cb6-20"><a href="#cb6-20"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"icon-size"</span><span class="kw">></span>GTK_ICON_SIZE_LARGE<span class="kw"></property></span></span>
|
||||
<span id="cb6-21"><a href="#cb6-21"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb6-22"><a href="#cb6-22"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb6-23"><a href="#cb6-23"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb6-24"><a href="#cb6-24"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkLabel"</span><span class="ot"> id=</span><span class="st">"lb_alert"</span><span class="kw">></span></span>
|
||||
<span id="cb6-25"><a href="#cb6-25"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb6-26"><a href="#cb6-26"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb6-27"><a href="#cb6-27"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb6-28"><a href="#cb6-28"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb6-29"><a href="#cb6-29"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb6-30"><a href="#cb6-30"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb6-31"><a href="#cb6-31"></a> <span class="kw"><child</span><span class="ot"> type=</span><span class="st">"action"</span><span class="kw">></span></span>
|
||||
<span id="cb6-32"><a href="#cb6-32"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkButton"</span><span class="ot"> id=</span><span class="st">"btn_cancel"</span><span class="kw">></span></span>
|
||||
<span id="cb6-33"><a href="#cb6-33"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span>Cancel<span class="kw"></property></span></span>
|
||||
<span id="cb6-34"><a href="#cb6-34"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb6-35"><a href="#cb6-35"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb6-36"><a href="#cb6-36"></a> <span class="kw"><child</span><span class="ot"> type=</span><span class="st">"action"</span><span class="kw">></span></span>
|
||||
<span id="cb6-37"><a href="#cb6-37"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkButton"</span><span class="ot"> id=</span><span class="st">"btn_accept"</span><span class="kw">></span></span>
|
||||
<span id="cb6-38"><a href="#cb6-38"></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="cb6-39"><a href="#cb6-39"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb6-40"><a href="#cb6-40"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb6-41"><a href="#cb6-41"></a> <span class="kw"><action-widgets></span></span>
|
||||
<span id="cb6-42"><a href="#cb6-42"></a> <span class="kw"><action-widget</span><span class="ot"> response=</span><span class="st">"cancel"</span><span class="ot"> default=</span><span class="st">"true"</span><span class="kw">></span>btn_cancel<span class="kw"></action-widget></span></span>
|
||||
<span id="cb6-43"><a href="#cb6-43"></a> <span class="kw"><action-widget</span><span class="ot"> response=</span><span class="st">"accept"</span><span class="kw">></span>btn_accept<span class="kw"></action-widget></span></span>
|
||||
<span id="cb6-44"><a href="#cb6-44"></a> <span class="kw"></action-widgets></span></span>
|
||||
<span id="cb6-45"><a href="#cb6-45"></a> <span class="kw"></template></span></span>
|
||||
<span id="cb6-46"><a href="#cb6-46"></a><span class="kw"></interface></span></span></code></pre></div>
|
||||
<p>The header file is:</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">#ifndef __TFE_ALERT_H__</span></span>
|
||||
<span id="cb7-2"><a href="#cb7-2"></a><span class="pp">#define __TFE_ALERT_H__</span></span>
|
||||
<span id="cb7-3"><a href="#cb7-3"></a></span>
|
||||
<span id="cb7-4"><a href="#cb7-4"></a><span class="pp">#include </span><span class="im"><gtk/gtk.h></span></span>
|
||||
<span id="cb7-5"><a href="#cb7-5"></a></span>
|
||||
<span id="cb7-6"><a href="#cb7-6"></a><span class="pp">#define TFE_TYPE_ALERT tfe_alert_get_type ()</span></span>
|
||||
<span id="cb7-7"><a href="#cb7-7"></a>G_DECLARE_FINAL_TYPE (TfeAlert, tfe_alert, TFE, ALERT, GtkDialog)</span>
|
||||
<span id="cb7-8"><a href="#cb7-8"></a></span>
|
||||
<span id="cb7-9"><a href="#cb7-9"></a><span class="dt">void</span></span>
|
||||
<span id="cb7-10"><a href="#cb7-10"></a>tfe_alert_set_message (TfeAlert *alert, <span class="dt">const</span> <span class="dt">char</span> *message);</span>
|
||||
<span id="cb7-11"><a href="#cb7-11"></a></span>
|
||||
<span id="cb7-12"><a href="#cb7-12"></a><span class="dt">void</span></span>
|
||||
<span id="cb7-13"><a href="#cb7-13"></a>tfe_alert_set_button_label (TfeAlert *alert, <span class="dt">const</span> <span class="dt">char</span> *label);</span>
|
||||
<span id="cb7-14"><a href="#cb7-14"></a></span>
|
||||
<span id="cb7-15"><a href="#cb7-15"></a>GtkWidget *</span>
|
||||
<span id="cb7-16"><a href="#cb7-16"></a>tfe_alert_new (GtkWindow *win);</span>
|
||||
<span id="cb7-17"><a href="#cb7-17"></a></span>
|
||||
<span id="cb7-18"><a href="#cb7-18"></a><span class="pp">#endif </span><span class="co">/* __TFE_ALERT_H__ */</span></span></code></pre></div>
|
||||
<p>There are three public functions. The functions <code>tfe_alert_set_message</code> and <code>tfe_alert_set_button_label</code> sets the label and button name of the alert dialog. For example, if you want to show an alert that the user tries to close without saving the content, set them like:</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>tfe_alert_set_message (alert, <span class="st">"Are you really close without saving?"</span>); <span class="co">/* alert points to a TfeAlert instance */</span></span>
|
||||
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true"></a>tfe_alert_set_button_label (alert, <span class="st">"Close"</span>);</span></code></pre></div>
|
||||
<p>The function <code>tfe_alert_new</code> creates a TfeAlert dialog.</p>
|
||||
<p>The C source file 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">"tfealert.h"</span></span>
|
||||
<span id="cb9-2"><a href="#cb9-2"></a></span>
|
||||
<span id="cb9-3"><a href="#cb9-3"></a><span class="kw">struct</span> _TfeAlert</span>
|
||||
<span id="cb9-4"><a href="#cb9-4"></a>{</span>
|
||||
<span id="cb9-5"><a href="#cb9-5"></a> GtkDialog parent;</span>
|
||||
<span id="cb9-6"><a href="#cb9-6"></a> GtkLabel *lb_alert;</span>
|
||||
<span id="cb9-7"><a href="#cb9-7"></a> GtkButton *btn_accept;</span>
|
||||
<span id="cb9-8"><a href="#cb9-8"></a>};</span>
|
||||
<span id="cb9-9"><a href="#cb9-9"></a></span>
|
||||
<span id="cb9-10"><a href="#cb9-10"></a>G_DEFINE_TYPE (TfeAlert, tfe_alert, GTK_TYPE_DIALOG);</span>
|
||||
<span id="cb9-11"><a href="#cb9-11"></a></span>
|
||||
<span id="cb9-12"><a href="#cb9-12"></a><span class="dt">void</span></span>
|
||||
<span id="cb9-13"><a href="#cb9-13"></a>tfe_alert_set_message (TfeAlert *alert, <span class="dt">const</span> <span class="dt">char</span> *message) {</span>
|
||||
<span id="cb9-14"><a href="#cb9-14"></a> gtk_label_set_text (alert->lb_alert, message);</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><span class="dt">void</span></span>
|
||||
<span id="cb9-18"><a href="#cb9-18"></a>tfe_alert_set_button_label (TfeAlert *alert, <span class="dt">const</span> <span class="dt">char</span> *label) {</span>
|
||||
<span id="cb9-19"><a href="#cb9-19"></a> gtk_button_set_label (alert->btn_accept, label);</span>
|
||||
<span id="cb9-20"><a href="#cb9-20"></a>}</span>
|
||||
<span id="cb9-21"><a href="#cb9-21"></a></span>
|
||||
<span id="cb9-22"><a href="#cb9-22"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb9-23"><a href="#cb9-23"></a>tfe_alert_init (TfeAlert *alert) {</span>
|
||||
<span id="cb9-24"><a href="#cb9-24"></a> gtk_widget_init_template (GTK_WIDGET (alert));</span>
|
||||
<span id="cb9-25"><a href="#cb9-25"></a>}</span>
|
||||
<span id="cb9-26"><a href="#cb9-26"></a></span>
|
||||
<span id="cb9-27"><a href="#cb9-27"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb9-28"><a href="#cb9-28"></a>tfe_alert_class_init (TfeAlertClass *class) {</span>
|
||||
<span id="cb9-29"><a href="#cb9-29"></a> gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class), <span class="st">"/com/github/ToshioCP/tfe/tfealert.ui"</span>);</span>
|
||||
<span id="cb9-30"><a href="#cb9-30"></a> gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), TfeAlert, lb_alert);</span>
|
||||
<span id="cb9-31"><a href="#cb9-31"></a> gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), TfeAlert, btn_accept);</span>
|
||||
<span id="cb9-32"><a href="#cb9-32"></a>}</span>
|
||||
<span id="cb9-33"><a href="#cb9-33"></a></span>
|
||||
<span id="cb9-34"><a href="#cb9-34"></a>GtkWidget *</span>
|
||||
<span id="cb9-35"><a href="#cb9-35"></a>tfe_alert_new (GtkWindow *win) {</span>
|
||||
<span id="cb9-36"><a href="#cb9-36"></a> <span class="cf">return</span> GTK_WIDGET (g_object_new (TFE_TYPE_ALERT, <span class="st">"transient-for"</span>, win, NULL));</span>
|
||||
<span id="cb9-37"><a href="#cb9-37"></a>}</span></code></pre></div>
|
||||
<p>The program is almost same as <code>tfepref.c</code>.</p>
|
||||
<p>The instruction how to use this object is as follows.</p>
|
||||
<ol type="1">
|
||||
<li>Write a “response” signal handler.</li>
|
||||
<li>Create a TfeAlert object.</li>
|
||||
<li>Connect “response” signal to a handler</li>
|
||||
<li>Show the dialog</li>
|
||||
<li>In the signal handler, do something with regard to the response-id. Then destroy the dialog.</li>
|
||||
</ol>
|
||||
<h2 id="top-level-window">Top-level window</h2>
|
||||
<p>In the same way, create a child object of GtkApplicationWindow. The object name is “TfeWindow”.</p>
|
||||
<div class="sourceCode" id="cb10"><pre class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><span id="cb10-1"><a href="#cb10-1"></a><span class="kw"><?xml</span> version="1.0" encoding="UTF-8"<span class="kw">?></span></span>
|
||||
<span id="cb10-2"><a href="#cb10-2"></a><span class="kw"><interface></span></span>
|
||||
<span id="cb10-3"><a href="#cb10-3"></a> <span class="kw"><template</span><span class="ot"> class=</span><span class="st">"TfeWindow"</span><span class="ot"> parent=</span><span class="st">"GtkApplicationWindow"</span><span class="kw">></span></span>
|
||||
<span id="cb10-4"><a href="#cb10-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="cb10-5"><a href="#cb10-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="cb10-6"><a href="#cb10-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="cb10-7"><a href="#cb10-7"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb10-8"><a href="#cb10-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="cb10-9"><a href="#cb10-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="cb10-10"><a href="#cb10-10"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb10-11"><a href="#cb10-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="cb10-12"><a href="#cb10-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="cb10-13"><a href="#cb10-13"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb10-14"><a href="#cb10-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="cb10-15"><a href="#cb10-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="cb10-16"><a href="#cb10-16"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb10-17"><a href="#cb10-17"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb10-18"><a href="#cb10-18"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb10-19"><a href="#cb10-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">"btno"</span><span class="kw">></span></span>
|
||||
<span id="cb10-20"><a href="#cb10-20"></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="cb10-21"><a href="#cb10-21"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"action-name"</span><span class="kw">></span>win.open<span class="kw"></property></span></span>
|
||||
<span id="cb10-22"><a href="#cb10-22"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb10-23"><a href="#cb10-23"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb10-24"><a href="#cb10-24"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb10-25"><a href="#cb10-25"></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="cb10-26"><a href="#cb10-26"></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="cb10-27"><a href="#cb10-27"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"action-name"</span><span class="kw">></span>win.save<span class="kw"></property></span></span>
|
||||
<span id="cb10-28"><a href="#cb10-28"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb10-29"><a href="#cb10-29"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb10-30"><a href="#cb10-30"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb10-31"><a href="#cb10-31"></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="cb10-32"><a href="#cb10-32"></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="cb10-33"><a href="#cb10-33"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb10-34"><a href="#cb10-34"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb10-35"><a href="#cb10-35"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb10-36"><a href="#cb10-36"></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="cb10-37"><a href="#cb10-37"></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="cb10-38"><a href="#cb10-38"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"action-name"</span><span class="kw">></span>win.close<span class="kw"></property></span></span>
|
||||
<span id="cb10-39"><a href="#cb10-39"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb10-40"><a href="#cb10-40"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb10-41"><a href="#cb10-41"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb10-42"><a href="#cb10-42"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkMenuButton"</span><span class="ot"> id=</span><span class="st">"btnm"</span><span class="kw">></span></span>
|
||||
<span id="cb10-43"><a href="#cb10-43"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"direction"</span><span class="kw">></span>down<span class="kw"></property></span></span>
|
||||
<span id="cb10-44"><a href="#cb10-44"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"halign"</span><span class="kw">></span>start<span class="kw"></property></span></span>
|
||||
<span id="cb10-45"><a href="#cb10-45"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"icon-name"</span><span class="kw">></span>open-menu-symbolic<span class="kw"></property></span></span>
|
||||
<span id="cb10-46"><a href="#cb10-46"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb10-47"><a href="#cb10-47"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb10-48"><a href="#cb10-48"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb10-49"><a href="#cb10-49"></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="cb10-50"><a href="#cb10-50"></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="cb10-51"><a href="#cb10-51"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb10-52"><a href="#cb10-52"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb10-53"><a href="#cb10-53"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb10-54"><a href="#cb10-54"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb10-55"><a href="#cb10-55"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb10-56"><a href="#cb10-56"></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="cb10-57"><a href="#cb10-57"></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="cb10-58"><a href="#cb10-58"></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="cb10-59"><a href="#cb10-59"></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="cb10-60"><a href="#cb10-60"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb10-61"><a href="#cb10-61"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb10-62"><a href="#cb10-62"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb10-63"><a href="#cb10-63"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb10-64"><a href="#cb10-64"></a> <span class="kw"></template></span></span>
|
||||
<span id="cb10-65"><a href="#cb10-65"></a><span class="kw"></interface></span></span></code></pre></div>
|
||||
<p>This XML file is almost same as before except template tag and “action-name” property in buttons.</p>
|
||||
<p>GtkButton implements GtkActionable interface, which has “action-name” property. If this property is set, GtkButton activates the action when it is clicked. For example, if an open button is clicked, “win.open” action will be activated and <code>open_activated</code> handler will be invoked.</p>
|
||||
<p>This action is also used by “<Control>o” accelerator (See the source code of <code>tfewindow.c</code> below). If you use “clicked” signal for the button, you need its signal handler. Then, there are two handlers:</p>
|
||||
<ul>
|
||||
<li>a handler for the “clicked” signal on the button</li>
|
||||
<li>a handler for the “activate” signal on the “win.open” action, to which “<Control>o” accelerator is connected</li>
|
||||
</ul>
|
||||
<p>These two handlers do almost same thing. It is inefficient. Connecting buttons to actions is a good way to reduce unnecessary codes.</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="pp">#ifndef __TFE_WINDOW_H__</span></span>
|
||||
<span id="cb11-2"><a href="#cb11-2"></a><span class="pp">#define __TFE_WINDOW_H__</span></span>
|
||||
<span id="cb11-3"><a href="#cb11-3"></a></span>
|
||||
<span id="cb11-4"><a href="#cb11-4"></a><span class="pp">#include </span><span class="im"><gtk/gtk.h></span></span>
|
||||
<span id="cb11-5"><a href="#cb11-5"></a></span>
|
||||
<span id="cb11-6"><a href="#cb11-6"></a><span class="pp">#define TFE_TYPE_WINDOW tfe_window_get_type ()</span></span>
|
||||
<span id="cb11-7"><a href="#cb11-7"></a>G_DECLARE_FINAL_TYPE (TfeWindow, tfe_window, TFE, WINDOW, GtkApplicationWindow)</span>
|
||||
<span id="cb11-8"><a href="#cb11-8"></a></span>
|
||||
<span id="cb11-9"><a href="#cb11-9"></a><span class="dt">void</span></span>
|
||||
<span id="cb11-10"><a href="#cb11-10"></a>tfe_window_notebook_page_new (TfeWindow *win);</span>
|
||||
<span id="cb11-11"><a href="#cb11-11"></a></span>
|
||||
<span id="cb11-12"><a href="#cb11-12"></a><span class="dt">void</span></span>
|
||||
<span id="cb11-13"><a href="#cb11-13"></a>tfe_window_notebook_page_new_with_files (TfeWindow *win, GFile **files, <span class="dt">int</span> n_files);</span>
|
||||
<span id="cb11-14"><a href="#cb11-14"></a></span>
|
||||
<span id="cb11-15"><a href="#cb11-15"></a>GtkWidget *</span>
|
||||
<span id="cb11-16"><a href="#cb11-16"></a>tfe_window_new (GtkApplication *app);</span>
|
||||
<span id="cb11-17"><a href="#cb11-17"></a></span>
|
||||
<span id="cb11-18"><a href="#cb11-18"></a><span class="pp">#endif </span><span class="co">/* __TFE_WINDOW_H__ */</span></span></code></pre></div>
|
||||
<p>There are three public functions. The function <code>tfe_window_notebook_page_new</code> creates a new notebook page. This is a wrapper function for <code>notebook_page_new</code>. It is called by GtkApplication object. The function <code>tfe_window_notebook_page_new_with_files</code> creates notebook pages with a contents read from the given files. The function <code>tfe_window_new</code> creates a TfeWindow instance.</p>
|
||||
<div class="sourceCode" id="cb12"><pre class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb12-1"><a href="#cb12-1"></a><span class="pp">#include </span><span class="im">"tfewindow.h"</span></span>
|
||||
<span id="cb12-2"><a href="#cb12-2"></a><span class="pp">#include </span><span class="im">"tfenotebook.h"</span></span>
|
||||
<span id="cb12-3"><a href="#cb12-3"></a><span class="pp">#include </span><span class="im">"tfepref.h"</span></span>
|
||||
<span id="cb12-4"><a href="#cb12-4"></a><span class="pp">#include </span><span class="im">"tfealert.h"</span></span>
|
||||
<span id="cb12-5"><a href="#cb12-5"></a><span class="pp">#include </span><span class="im">"css.h"</span></span>
|
||||
<span id="cb12-6"><a href="#cb12-6"></a></span>
|
||||
<span id="cb12-7"><a href="#cb12-7"></a><span class="kw">struct</span> _TfeWindow {</span>
|
||||
<span id="cb12-8"><a href="#cb12-8"></a> GtkApplicationWindow parent;</span>
|
||||
<span id="cb12-9"><a href="#cb12-9"></a> GtkMenuButton *btnm;</span>
|
||||
<span id="cb12-10"><a href="#cb12-10"></a> GtkNotebook *nb;</span>
|
||||
<span id="cb12-11"><a href="#cb12-11"></a> GSettings *settings;</span>
|
||||
<span id="cb12-12"><a href="#cb12-12"></a> gboolean is_quit;</span>
|
||||
<span id="cb12-13"><a href="#cb12-13"></a>};</span>
|
||||
<span id="cb12-14"><a href="#cb12-14"></a></span>
|
||||
<span id="cb12-15"><a href="#cb12-15"></a>G_DEFINE_TYPE (TfeWindow, tfe_window, GTK_TYPE_APPLICATION_WINDOW);</span>
|
||||
<span id="cb12-16"><a href="#cb12-16"></a></span>
|
||||
<span id="cb12-17"><a href="#cb12-17"></a><span class="co">/* alert response signal handler */</span></span>
|
||||
<span id="cb12-18"><a href="#cb12-18"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb12-19"><a href="#cb12-19"></a>alert_response_cb (GtkDialog *alert, <span class="dt">int</span> response_id, gpointer user_data) {</span>
|
||||
<span id="cb12-20"><a href="#cb12-20"></a> TfeWindow *win = TFE_WINDOW (user_data);</span>
|
||||
<span id="cb12-21"><a href="#cb12-21"></a></span>
|
||||
<span id="cb12-22"><a href="#cb12-22"></a> <span class="cf">if</span> (response_id == GTK_RESPONSE_ACCEPT) {</span>
|
||||
<span id="cb12-23"><a href="#cb12-23"></a> <span class="cf">if</span> (win->is_quit)</span>
|
||||
<span id="cb12-24"><a href="#cb12-24"></a> gtk_window_destroy(GTK_WINDOW (win));</span>
|
||||
<span id="cb12-25"><a href="#cb12-25"></a> <span class="cf">else</span></span>
|
||||
<span id="cb12-26"><a href="#cb12-26"></a> notebook_page_close (win->nb);</span>
|
||||
<span id="cb12-27"><a href="#cb12-27"></a> }</span>
|
||||
<span id="cb12-28"><a href="#cb12-28"></a> gtk_window_destroy (GTK_WINDOW (alert));</span>
|
||||
<span id="cb12-29"><a href="#cb12-29"></a>}</span>
|
||||
<span id="cb12-30"><a href="#cb12-30"></a></span>
|
||||
<span id="cb12-31"><a href="#cb12-31"></a><span class="co">/* ----- action activated handlers ----- */</span></span>
|
||||
<span id="cb12-32"><a href="#cb12-32"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb12-33"><a href="#cb12-33"></a>open_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {</span>
|
||||
<span id="cb12-34"><a href="#cb12-34"></a> TfeWindow *win = TFE_WINDOW (user_data);</span>
|
||||
<span id="cb12-35"><a href="#cb12-35"></a></span>
|
||||
<span id="cb12-36"><a href="#cb12-36"></a> notebook_page_open (GTK_NOTEBOOK (win->nb));</span>
|
||||
<span id="cb12-37"><a href="#cb12-37"></a>}</span>
|
||||
<span id="cb12-38"><a href="#cb12-38"></a></span>
|
||||
<span id="cb12-39"><a href="#cb12-39"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb12-40"><a href="#cb12-40"></a>save_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {</span>
|
||||
<span id="cb12-41"><a href="#cb12-41"></a> TfeWindow *win = TFE_WINDOW (user_data);</span>
|
||||
<span id="cb12-42"><a href="#cb12-42"></a></span>
|
||||
<span id="cb12-43"><a href="#cb12-43"></a> notebook_page_save (GTK_NOTEBOOK (win->nb));</span>
|
||||
<span id="cb12-44"><a href="#cb12-44"></a>}</span>
|
||||
<span id="cb12-45"><a href="#cb12-45"></a></span>
|
||||
<span id="cb12-46"><a href="#cb12-46"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb12-47"><a href="#cb12-47"></a>close_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {</span>
|
||||
<span id="cb12-48"><a href="#cb12-48"></a> TfeWindow *win = TFE_WINDOW (user_data);</span>
|
||||
<span id="cb12-49"><a href="#cb12-49"></a> TfeAlert *alert;</span>
|
||||
<span id="cb12-50"><a href="#cb12-50"></a></span>
|
||||
<span id="cb12-51"><a href="#cb12-51"></a> <span class="cf">if</span> (has_saved (win->nb))</span>
|
||||
<span id="cb12-52"><a href="#cb12-52"></a> notebook_page_close (win->nb);</span>
|
||||
<span id="cb12-53"><a href="#cb12-53"></a> <span class="cf">else</span> {</span>
|
||||
<span id="cb12-54"><a href="#cb12-54"></a> win->is_quit = false;</span>
|
||||
<span id="cb12-55"><a href="#cb12-55"></a> alert = TFE_ALERT (tfe_alert_new (GTK_WINDOW (win)));</span>
|
||||
<span id="cb12-56"><a href="#cb12-56"></a> tfe_alert_set_message (alert, <span class="st">"Contents aren't saved yet.</span><span class="sc">\n</span><span class="st">Are you sure to close?"</span>);</span>
|
||||
<span id="cb12-57"><a href="#cb12-57"></a> tfe_alert_set_button_label (alert, <span class="st">"Close"</span>);</span>
|
||||
<span id="cb12-58"><a href="#cb12-58"></a> g_signal_connect (GTK_DIALOG (alert), <span class="st">"response"</span>, G_CALLBACK (alert_response_cb), win);</span>
|
||||
<span id="cb12-59"><a href="#cb12-59"></a> gtk_widget_show (GTK_WIDGET (alert));</span>
|
||||
<span id="cb12-60"><a href="#cb12-60"></a> }</span>
|
||||
<span id="cb12-61"><a href="#cb12-61"></a>}</span>
|
||||
<span id="cb12-62"><a href="#cb12-62"></a></span>
|
||||
<span id="cb12-63"><a href="#cb12-63"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb12-64"><a href="#cb12-64"></a>new_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {</span>
|
||||
<span id="cb12-65"><a href="#cb12-65"></a> TfeWindow *win = TFE_WINDOW (user_data);</span>
|
||||
<span id="cb12-66"><a href="#cb12-66"></a></span>
|
||||
<span id="cb12-67"><a href="#cb12-67"></a> notebook_page_new (GTK_NOTEBOOK (win->nb));</span>
|
||||
<span id="cb12-68"><a href="#cb12-68"></a>}</span>
|
||||
<span id="cb12-69"><a href="#cb12-69"></a></span>
|
||||
<span id="cb12-70"><a href="#cb12-70"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb12-71"><a href="#cb12-71"></a>saveas_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {</span>
|
||||
<span id="cb12-72"><a href="#cb12-72"></a> TfeWindow *win = TFE_WINDOW (user_data);</span>
|
||||
<span id="cb12-73"><a href="#cb12-73"></a></span>
|
||||
<span id="cb12-74"><a href="#cb12-74"></a> notebook_page_saveas (GTK_NOTEBOOK (win->nb));</span>
|
||||
<span id="cb12-75"><a href="#cb12-75"></a>}</span>
|
||||
<span id="cb12-76"><a href="#cb12-76"></a></span>
|
||||
<span id="cb12-77"><a href="#cb12-77"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb12-78"><a href="#cb12-78"></a>pref_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {</span>
|
||||
<span id="cb12-79"><a href="#cb12-79"></a> TfeWindow *win = TFE_WINDOW (user_data);</span>
|
||||
<span id="cb12-80"><a href="#cb12-80"></a> GtkWidget *pref;</span>
|
||||
<span id="cb12-81"><a href="#cb12-81"></a></span>
|
||||
<span id="cb12-82"><a href="#cb12-82"></a> pref = tfe_pref_new (GTK_WINDOW (win));</span>
|
||||
<span id="cb12-83"><a href="#cb12-83"></a> gtk_widget_show (pref);</span>
|
||||
<span id="cb12-84"><a href="#cb12-84"></a>}</span>
|
||||
<span id="cb12-85"><a href="#cb12-85"></a></span>
|
||||
<span id="cb12-86"><a href="#cb12-86"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb12-87"><a href="#cb12-87"></a>quit_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {</span>
|
||||
<span id="cb12-88"><a href="#cb12-88"></a> TfeWindow *win = TFE_WINDOW (user_data);</span>
|
||||
<span id="cb12-89"><a href="#cb12-89"></a></span>
|
||||
<span id="cb12-90"><a href="#cb12-90"></a> TfeAlert *alert;</span>
|
||||
<span id="cb12-91"><a href="#cb12-91"></a></span>
|
||||
<span id="cb12-92"><a href="#cb12-92"></a> <span class="cf">if</span> (has_saved_all (GTK_NOTEBOOK (win->nb)))</span>
|
||||
<span id="cb12-93"><a href="#cb12-93"></a> gtk_window_destroy (GTK_WINDOW (win));</span>
|
||||
<span id="cb12-94"><a href="#cb12-94"></a> <span class="cf">else</span> {</span>
|
||||
<span id="cb12-95"><a href="#cb12-95"></a> win->is_quit = true;</span>
|
||||
<span id="cb12-96"><a href="#cb12-96"></a> alert = TFE_ALERT (tfe_alert_new (GTK_WINDOW (win)));</span>
|
||||
<span id="cb12-97"><a href="#cb12-97"></a> tfe_alert_set_message (alert, <span class="st">"Contents aren't saved yet.</span><span class="sc">\n</span><span class="st">Are you sure to quit?"</span>);</span>
|
||||
<span id="cb12-98"><a href="#cb12-98"></a> tfe_alert_set_button_label (alert, <span class="st">"Quit"</span>);</span>
|
||||
<span id="cb12-99"><a href="#cb12-99"></a> g_signal_connect (GTK_DIALOG (alert), <span class="st">"response"</span>, G_CALLBACK (alert_response_cb), win);</span>
|
||||
<span id="cb12-100"><a href="#cb12-100"></a> gtk_widget_show (GTK_WIDGET (alert));</span>
|
||||
<span id="cb12-101"><a href="#cb12-101"></a> }</span>
|
||||
<span id="cb12-102"><a href="#cb12-102"></a>}</span>
|
||||
<span id="cb12-103"><a href="#cb12-103"></a></span>
|
||||
<span id="cb12-104"><a href="#cb12-104"></a><span class="co">/* gsettings changed::font signal handler */</span></span>
|
||||
<span id="cb12-105"><a href="#cb12-105"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb12-106"><a href="#cb12-106"></a>changed_font_cb (GSettings *settings, <span class="dt">char</span> *key, gpointer user_data) {</span>
|
||||
<span id="cb12-107"><a href="#cb12-107"></a> GtkWindow *win = GTK_WINDOW (user_data); </span>
|
||||
<span id="cb12-108"><a href="#cb12-108"></a> <span class="dt">char</span> *font;</span>
|
||||
<span id="cb12-109"><a href="#cb12-109"></a> PangoFontDescription *pango_font_desc;</span>
|
||||
<span id="cb12-110"><a href="#cb12-110"></a></span>
|
||||
<span id="cb12-111"><a href="#cb12-111"></a> font = g_settings_get_string (settings, <span class="st">"font"</span>);</span>
|
||||
<span id="cb12-112"><a href="#cb12-112"></a> pango_font_desc = pango_font_description_from_string (font);</span>
|
||||
<span id="cb12-113"><a href="#cb12-113"></a> g_free (font);</span>
|
||||
<span id="cb12-114"><a href="#cb12-114"></a> set_font_for_display_with_pango_font_desc (win, pango_font_desc);</span>
|
||||
<span id="cb12-115"><a href="#cb12-115"></a>}</span>
|
||||
<span id="cb12-116"><a href="#cb12-116"></a></span>
|
||||
<span id="cb12-117"><a href="#cb12-117"></a><span class="co">/* --- public functions --- */</span></span>
|
||||
<span id="cb12-118"><a href="#cb12-118"></a></span>
|
||||
<span id="cb12-119"><a href="#cb12-119"></a><span class="dt">void</span></span>
|
||||
<span id="cb12-120"><a href="#cb12-120"></a>tfe_window_notebook_page_new (TfeWindow *win) {</span>
|
||||
<span id="cb12-121"><a href="#cb12-121"></a> notebook_page_new (win->nb);</span>
|
||||
<span id="cb12-122"><a href="#cb12-122"></a>}</span>
|
||||
<span id="cb12-123"><a href="#cb12-123"></a></span>
|
||||
<span id="cb12-124"><a href="#cb12-124"></a><span class="dt">void</span></span>
|
||||
<span id="cb12-125"><a href="#cb12-125"></a>tfe_window_notebook_page_new_with_files (TfeWindow *win, GFile **files, <span class="dt">int</span> n_files) {</span>
|
||||
<span id="cb12-126"><a href="#cb12-126"></a> <span class="dt">int</span> i;</span>
|
||||
<span id="cb12-127"><a href="#cb12-127"></a></span>
|
||||
<span id="cb12-128"><a href="#cb12-128"></a> <span class="cf">for</span> (i = <span class="dv">0</span>; i < n_files; i++)</span>
|
||||
<span id="cb12-129"><a href="#cb12-129"></a> notebook_page_new_with_file (win->nb, files[i]);</span>
|
||||
<span id="cb12-130"><a href="#cb12-130"></a> <span class="cf">if</span> (gtk_notebook_get_n_pages (win->nb) == <span class="dv">0</span>)</span>
|
||||
<span id="cb12-131"><a href="#cb12-131"></a> notebook_page_new (win->nb);</span>
|
||||
<span id="cb12-132"><a href="#cb12-132"></a>}</span>
|
||||
<span id="cb12-133"><a href="#cb12-133"></a></span>
|
||||
<span id="cb12-134"><a href="#cb12-134"></a><span class="co">/* --- TfeWindow object construction/destruction --- */</span> </span>
|
||||
<span id="cb12-135"><a href="#cb12-135"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb12-136"><a href="#cb12-136"></a>tfe_window_dispose (GObject *gobject) {</span>
|
||||
<span id="cb12-137"><a href="#cb12-137"></a> TfeWindow *window = TFE_WINDOW (gobject);</span>
|
||||
<span id="cb12-138"><a href="#cb12-138"></a></span>
|
||||
<span id="cb12-139"><a href="#cb12-139"></a> g_clear_object (&window->settings);</span>
|
||||
<span id="cb12-140"><a href="#cb12-140"></a> G_OBJECT_CLASS (tfe_window_parent_class)->dispose (gobject);</span>
|
||||
<span id="cb12-141"><a href="#cb12-141"></a>}</span>
|
||||
<span id="cb12-142"><a href="#cb12-142"></a></span>
|
||||
<span id="cb12-143"><a href="#cb12-143"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb12-144"><a href="#cb12-144"></a>tfe_window_init (TfeWindow *win) {</span>
|
||||
<span id="cb12-145"><a href="#cb12-145"></a> GtkBuilder *build;</span>
|
||||
<span id="cb12-146"><a href="#cb12-146"></a> GMenuModel *menu;</span>
|
||||
<span id="cb12-147"><a href="#cb12-147"></a></span>
|
||||
<span id="cb12-148"><a href="#cb12-148"></a> gtk_widget_init_template (GTK_WIDGET (win));</span>
|
||||
<span id="cb12-149"><a href="#cb12-149"></a></span>
|
||||
<span id="cb12-150"><a href="#cb12-150"></a> build = gtk_builder_new_from_resource (<span class="st">"/com/github/ToshioCP/tfe/menu.ui"</span>);</span>
|
||||
<span id="cb12-151"><a href="#cb12-151"></a> menu = G_MENU_MODEL (gtk_builder_get_object (build, <span class="st">"menu"</span>));</span>
|
||||
<span id="cb12-152"><a href="#cb12-152"></a> gtk_menu_button_set_menu_model (win->btnm, menu);</span>
|
||||
<span id="cb12-153"><a href="#cb12-153"></a> g_object_unref(build);</span>
|
||||
<span id="cb12-154"><a href="#cb12-154"></a></span>
|
||||
<span id="cb12-155"><a href="#cb12-155"></a> win->settings = g_settings_new (<span class="st">"com.github.ToshioCP.tfe"</span>);</span>
|
||||
<span id="cb12-156"><a href="#cb12-156"></a> g_signal_connect (win->settings, <span class="st">"changed::font"</span>, G_CALLBACK (changed_font_cb), win);</span>
|
||||
<span id="cb12-157"><a href="#cb12-157"></a></span>
|
||||
<span id="cb12-158"><a href="#cb12-158"></a><span class="co">/* ----- action ----- */</span></span>
|
||||
<span id="cb12-159"><a href="#cb12-159"></a> <span class="dt">const</span> GActionEntry win_entries[] = {</span>
|
||||
<span id="cb12-160"><a href="#cb12-160"></a> { <span class="st">"open"</span>, open_activated, NULL, NULL, NULL },</span>
|
||||
<span id="cb12-161"><a href="#cb12-161"></a> { <span class="st">"save"</span>, save_activated, NULL, NULL, NULL },</span>
|
||||
<span id="cb12-162"><a href="#cb12-162"></a> { <span class="st">"close"</span>, close_activated, NULL, NULL, NULL },</span>
|
||||
<span id="cb12-163"><a href="#cb12-163"></a> { <span class="st">"new"</span>, new_activated, NULL, NULL, NULL },</span>
|
||||
<span id="cb12-164"><a href="#cb12-164"></a> { <span class="st">"saveas"</span>, saveas_activated, NULL, NULL, NULL },</span>
|
||||
<span id="cb12-165"><a href="#cb12-165"></a> { <span class="st">"pref"</span>, pref_activated, NULL, NULL, NULL },</span>
|
||||
<span id="cb12-166"><a href="#cb12-166"></a> { <span class="st">"close-all"</span>, quit_activated, NULL, NULL, NULL }</span>
|
||||
<span id="cb12-167"><a href="#cb12-167"></a> };</span>
|
||||
<span id="cb12-168"><a href="#cb12-168"></a> g_action_map_add_action_entries (G_ACTION_MAP (win), win_entries, G_N_ELEMENTS (win_entries), win);</span>
|
||||
<span id="cb12-169"><a href="#cb12-169"></a></span>
|
||||
<span id="cb12-170"><a href="#cb12-170"></a> changed_font_cb(win->settings, <span class="st">"font"</span>, win);</span>
|
||||
<span id="cb12-171"><a href="#cb12-171"></a>}</span>
|
||||
<span id="cb12-172"><a href="#cb12-172"></a></span>
|
||||
<span id="cb12-173"><a href="#cb12-173"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb12-174"><a href="#cb12-174"></a>tfe_window_class_init (TfeWindowClass *class) {</span>
|
||||
<span id="cb12-175"><a href="#cb12-175"></a> GObjectClass *object_class = G_OBJECT_CLASS (class);</span>
|
||||
<span id="cb12-176"><a href="#cb12-176"></a></span>
|
||||
<span id="cb12-177"><a href="#cb12-177"></a> object_class->dispose = tfe_window_dispose;</span>
|
||||
<span id="cb12-178"><a href="#cb12-178"></a> gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class), <span class="st">"/com/github/ToshioCP/tfe/tfewindow.ui"</span>);</span>
|
||||
<span id="cb12-179"><a href="#cb12-179"></a> gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), TfeWindow, btnm);</span>
|
||||
<span id="cb12-180"><a href="#cb12-180"></a> gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), TfeWindow, nb);</span>
|
||||
<span id="cb12-181"><a href="#cb12-181"></a>}</span>
|
||||
<span id="cb12-182"><a href="#cb12-182"></a></span>
|
||||
<span id="cb12-183"><a href="#cb12-183"></a>GtkWidget *</span>
|
||||
<span id="cb12-184"><a href="#cb12-184"></a>tfe_window_new (GtkApplication *app) {</span>
|
||||
<span id="cb12-185"><a href="#cb12-185"></a> <span class="cf">return</span> GTK_WIDGET (g_object_new (TFE_TYPE_WINDOW, <span class="st">"application"</span>, app, NULL));</span>
|
||||
<span id="cb12-186"><a href="#cb12-186"></a>}</span></code></pre></div>
|
||||
<ul>
|
||||
<li>17-29: <code>alert_response_cb</code> is a call back function of the “response” signal of TfeAlert dialog. This is the same as before except <code>gtk_window_destroy(GTK_WINDOW (win))</code> is used instead of <code>tfe_application_quit</code>.</li>
|
||||
<li>31-102: Handlers of action activated signal. The <code>user_data</code> is a pointer to TfeWindow instance.</li>
|
||||
<li>104-115: A handler of “changed::font” signal of GSettings object.</li>
|
||||
<li>111: Gets the font from GSettings data.</li>
|
||||
<li>112: Gets a PangoFontDescription from the font. In the previous version, the program gets the font description from the GtkFontButton. The button data and GSettings data are the same. Therefore, the data got here is the same as the data in the GtkFontButton. In addition, we don’t need to worry about the preference dialog is alive or not thanks to the GSettings.</li>
|
||||
<li>114: Sets CSS on the display with the font description.</li>
|
||||
<li>117-132: Public functions.</li>
|
||||
<li>134-141: Dispose handler. The GSettings object needs to be released.</li>
|
||||
<li>143-171: Instance initialization function.</li>
|
||||
<li>148: Creates a composite widget instance with the template.</li>
|
||||
<li>151-153: Insert <code>menu</code> to the menu button.</li>
|
||||
<li>155-156: Creates a GSettings instance with the id “com.github.ToshioCP.tfe”. Connects “changed::font” signal to the handler <code>changed_font_cb</code>. This signal emits when the GSettings data is changed. The second part “font” of the signal name “changed::font” is called details. Signals can have details. If a GSettings instance has more than one key, “changed” signal emits only if the key, which has the same name as the detail, changes its value. For example, Suppose a GSettings object has three keys “a”, “b” and “c”.
|
||||
<ul>
|
||||
<li>“changed::a” is emitted when the value of the key “a” is changed. It isn’t emitted when the value of “b” or “c” is changed.</li>
|
||||
<li>“changed::b” is emitted when the value of the key “b” is changed. It isn’t emitted when the value of “a” or “c” is changed.</li>
|
||||
<li>“changed::c” is emitted when the value of the key “c” is changed. It isn’t emitted when the value of “a” or “b” is changed. In this version of tfe, there is only one key (“font”). So, even if the signal doesn’t have a detail, the result is the same. But in the future version, it will probably need details.</li>
|
||||
</ul></li>
|
||||
<li>158-168: Creates actions.</li>
|
||||
<li>170: Sets CSS font.</li>
|
||||
<li>173-181: Class initialization function.</li>
|
||||
<li>177: Sets the dispose handler.</li>
|
||||
<li>178: Sets the composite widget template</li>
|
||||
<li>179-180: Binds private variable with child objects in the template.</li>
|
||||
<li>183-186: <code>tfe_window_new</code>. This function creates TfeWindow instance.</li>
|
||||
</ul>
|
||||
<h2 id="tfeapplication">TfeApplication</h2>
|
||||
<p>The file <code>tfeapplication.c</code> is now very simple.</p>
|
||||
<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="pp">#include </span><span class="im">"tfewindow.h"</span></span>
|
||||
<span id="cb13-2"><a href="#cb13-2"></a></span>
|
||||
<span id="cb13-3"><a href="#cb13-3"></a><span class="co">/* ----- activate, open, startup handlers ----- */</span></span>
|
||||
<span id="cb13-4"><a href="#cb13-4"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb13-5"><a href="#cb13-5"></a>app_activate (GApplication *application) {</span>
|
||||
<span id="cb13-6"><a href="#cb13-6"></a> GtkApplication *app = GTK_APPLICATION (application);</span>
|
||||
<span id="cb13-7"><a href="#cb13-7"></a> GtkWidget *win = GTK_WIDGET (gtk_application_get_active_window (app));</span>
|
||||
<span id="cb13-8"><a href="#cb13-8"></a></span>
|
||||
<span id="cb13-9"><a href="#cb13-9"></a> tfe_window_notebook_page_new (TFE_WINDOW (win));</span>
|
||||
<span id="cb13-10"><a href="#cb13-10"></a> gtk_widget_show (GTK_WIDGET (win));</span>
|
||||
<span id="cb13-11"><a href="#cb13-11"></a>}</span>
|
||||
<span id="cb13-12"><a href="#cb13-12"></a></span>
|
||||
<span id="cb13-13"><a href="#cb13-13"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb13-14"><a href="#cb13-14"></a>app_open (GApplication *application, GFile ** files, gint n_files, <span class="dt">const</span> gchar *hint) {</span>
|
||||
<span id="cb13-15"><a href="#cb13-15"></a> GtkApplication *app = GTK_APPLICATION (application);</span>
|
||||
<span id="cb13-16"><a href="#cb13-16"></a> GtkWidget *win = GTK_WIDGET (gtk_application_get_active_window (app));</span>
|
||||
<span id="cb13-17"><a href="#cb13-17"></a></span>
|
||||
<span id="cb13-18"><a href="#cb13-18"></a> tfe_window_notebook_page_new_with_files (TFE_WINDOW (win), files, n_files);</span>
|
||||
<span id="cb13-19"><a href="#cb13-19"></a> gtk_widget_show (win);</span>
|
||||
<span id="cb13-20"><a href="#cb13-20"></a>}</span>
|
||||
<span id="cb13-21"><a href="#cb13-21"></a></span>
|
||||
<span id="cb13-22"><a href="#cb13-22"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb13-23"><a href="#cb13-23"></a>app_startup (GApplication *application) {</span>
|
||||
<span id="cb13-24"><a href="#cb13-24"></a> GtkApplication *app = GTK_APPLICATION (application);</span>
|
||||
<span id="cb13-25"><a href="#cb13-25"></a> <span class="dt">int</span> i;</span>
|
||||
<span id="cb13-26"><a href="#cb13-26"></a></span>
|
||||
<span id="cb13-27"><a href="#cb13-27"></a> tfe_window_new (app);</span>
|
||||
<span id="cb13-28"><a href="#cb13-28"></a></span>
|
||||
<span id="cb13-29"><a href="#cb13-29"></a><span class="co">/* ----- accelerator ----- */</span> </span>
|
||||
<span id="cb13-30"><a href="#cb13-30"></a> <span class="kw">struct</span> {</span>
|
||||
<span id="cb13-31"><a href="#cb13-31"></a> <span class="dt">const</span> <span class="dt">char</span> *action;</span>
|
||||
<span id="cb13-32"><a href="#cb13-32"></a> <span class="dt">const</span> <span class="dt">char</span> *accels[<span class="dv">2</span>];</span>
|
||||
<span id="cb13-33"><a href="#cb13-33"></a> } action_accels[] = {</span>
|
||||
<span id="cb13-34"><a href="#cb13-34"></a> { <span class="st">"win.open"</span>, { <span class="st">"<Control>o"</span>, NULL } },</span>
|
||||
<span id="cb13-35"><a href="#cb13-35"></a> { <span class="st">"win.save"</span>, { <span class="st">"<Control>s"</span>, NULL } },</span>
|
||||
<span id="cb13-36"><a href="#cb13-36"></a> { <span class="st">"win.close"</span>, { <span class="st">"<Control>w"</span>, NULL } },</span>
|
||||
<span id="cb13-37"><a href="#cb13-37"></a> { <span class="st">"win.new"</span>, { <span class="st">"<Control>n"</span>, NULL } },</span>
|
||||
<span id="cb13-38"><a href="#cb13-38"></a> { <span class="st">"win.saveas"</span>, { <span class="st">"<Shift><Control>s"</span>, NULL } },</span>
|
||||
<span id="cb13-39"><a href="#cb13-39"></a> { <span class="st">"win.close-all"</span>, { <span class="st">"<Control>q"</span>, NULL } },</span>
|
||||
<span id="cb13-40"><a href="#cb13-40"></a> };</span>
|
||||
<span id="cb13-41"><a href="#cb13-41"></a></span>
|
||||
<span id="cb13-42"><a href="#cb13-42"></a> <span class="cf">for</span> (i = <span class="dv">0</span>; i < G_N_ELEMENTS(action_accels); i++)</span>
|
||||
<span id="cb13-43"><a href="#cb13-43"></a> gtk_application_set_accels_for_action(GTK_APPLICATION(app), action_accels[i].action, action_accels[i].accels);</span>
|
||||
<span id="cb13-44"><a href="#cb13-44"></a>}</span>
|
||||
<span id="cb13-45"><a href="#cb13-45"></a></span>
|
||||
<span id="cb13-46"><a href="#cb13-46"></a><span class="co">/* ----- main ----- */</span></span>
|
||||
<span id="cb13-47"><a href="#cb13-47"></a></span>
|
||||
<span id="cb13-48"><a href="#cb13-48"></a><span class="pp">#define APPLICATION_ID "com.github.ToshioCP.tfe"</span></span>
|
||||
<span id="cb13-49"><a href="#cb13-49"></a></span>
|
||||
<span id="cb13-50"><a href="#cb13-50"></a><span class="dt">int</span></span>
|
||||
<span id="cb13-51"><a href="#cb13-51"></a>main (<span class="dt">int</span> argc, <span class="dt">char</span> **argv) {</span>
|
||||
<span id="cb13-52"><a href="#cb13-52"></a> GtkApplication *app;</span>
|
||||
<span id="cb13-53"><a href="#cb13-53"></a> <span class="dt">int</span> stat;</span>
|
||||
<span id="cb13-54"><a href="#cb13-54"></a></span>
|
||||
<span id="cb13-55"><a href="#cb13-55"></a> app = gtk_application_new (APPLICATION_ID, G_APPLICATION_HANDLES_OPEN);</span>
|
||||
<span id="cb13-56"><a href="#cb13-56"></a></span>
|
||||
<span id="cb13-57"><a href="#cb13-57"></a> g_signal_connect (app, <span class="st">"startup"</span>, G_CALLBACK (app_startup), NULL);</span>
|
||||
<span id="cb13-58"><a href="#cb13-58"></a> g_signal_connect (app, <span class="st">"activate"</span>, G_CALLBACK (app_activate), NULL);</span>
|
||||
<span id="cb13-59"><a href="#cb13-59"></a> g_signal_connect (app, <span class="st">"open"</span>, G_CALLBACK (app_open), NULL);</span>
|
||||
<span id="cb13-60"><a href="#cb13-60"></a></span>
|
||||
<span id="cb13-61"><a href="#cb13-61"></a> stat =g_application_run (G_APPLICATION (app), argc, argv);</span>
|
||||
<span id="cb13-62"><a href="#cb13-62"></a> g_object_unref (app);</span>
|
||||
<span id="cb13-63"><a href="#cb13-63"></a> <span class="cf">return</span> stat;</span>
|
||||
<span id="cb13-64"><a href="#cb13-64"></a>}</span></code></pre></div>
|
||||
<ul>
|
||||
<li>4-11: Activate signal handler. It uses <code>tfe_window_notebook_page_new</code> instead of <code>notebook_page_new</code>.</li>
|
||||
<li>13-20: Open signal handler. Thanks to <code>tfe_window_notebook_page_new_with_files</code>, this handler becomes very simple.</li>
|
||||
<li>22-44: Startup signal handler. Most of the tasks are moved to TfeWindow, the remaining tasks are creating a window and setting accelerations.</li>
|
||||
<li>48-64: A function <code>main</code>.</li>
|
||||
</ul>
|
||||
<h2 id="other-files">Other files</h2>
|
||||
<p>Resource XML file.</p>
|
||||
<div class="sourceCode" id="cb14"><pre class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><span id="cb14-1"><a href="#cb14-1"></a><span class="kw"><?xml</span> version="1.0" encoding="UTF-8"<span class="kw">?></span></span>
|
||||
<span id="cb14-2"><a href="#cb14-2"></a><span class="kw"><gresources></span></span>
|
||||
<span id="cb14-3"><a href="#cb14-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="cb14-4"><a href="#cb14-4"></a> <span class="kw"><file></span>tfewindow.ui<span class="kw"></file></span></span>
|
||||
<span id="cb14-5"><a href="#cb14-5"></a> <span class="kw"><file></span>tfepref.ui<span class="kw"></file></span></span>
|
||||
<span id="cb14-6"><a href="#cb14-6"></a> <span class="kw"><file></span>tfealert.ui<span class="kw"></file></span></span>
|
||||
<span id="cb14-7"><a href="#cb14-7"></a> <span class="kw"><file></span>menu.ui<span class="kw"></file></span></span>
|
||||
<span id="cb14-8"><a href="#cb14-8"></a> <span class="kw"></gresource></span></span>
|
||||
<span id="cb14-9"><a href="#cb14-9"></a><span class="kw"></gresources></span></span></code></pre></div>
|
||||
<p>GSchema XML file</p>
|
||||
<div class="sourceCode" id="cb15"><pre class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><span id="cb15-1"><a href="#cb15-1"></a><span class="kw"><?xml</span> version="1.0" encoding="UTF-8"<span class="kw">?></span></span>
|
||||
<span id="cb15-2"><a href="#cb15-2"></a><span class="kw"><schemalist></span></span>
|
||||
<span id="cb15-3"><a href="#cb15-3"></a> <span class="kw"><schema</span><span class="ot"> path=</span><span class="st">"/com/github/ToshioCP/tfe/"</span><span class="ot"> id=</span><span class="st">"com.github.ToshioCP.tfe"</span><span class="kw">></span></span>
|
||||
<span id="cb15-4"><a href="#cb15-4"></a> <span class="kw"><key</span><span class="ot"> name=</span><span class="st">"font"</span><span class="ot"> type=</span><span class="st">"s"</span><span class="kw">></span></span>
|
||||
<span id="cb15-5"><a href="#cb15-5"></a> <span class="kw"><default></span>'Monospace 12'<span class="kw"></default></span></span>
|
||||
<span id="cb15-6"><a href="#cb15-6"></a> <span class="kw"><summary></span>Font<span class="kw"></summary></span></span>
|
||||
<span id="cb15-7"><a href="#cb15-7"></a> <span class="kw"><description></span>The font to be used for textview.<span class="kw"></description></span></span>
|
||||
<span id="cb15-8"><a href="#cb15-8"></a> <span class="kw"></key></span></span>
|
||||
<span id="cb15-9"><a href="#cb15-9"></a> <span class="kw"></schema></span></span>
|
||||
<span id="cb15-10"><a href="#cb15-10"></a><span class="kw"></schemalist></span></span></code></pre></div>
|
||||
<p>Meson.build</p>
|
||||
<div class="sourceCode" id="cb16"><pre class="sourceCode numberSource numberLines"><code class="sourceCode"><span id="cb16-1"><a href="#cb16-1"></a>project('tfe', 'c')</span>
|
||||
<span id="cb16-2"><a href="#cb16-2"></a></span>
|
||||
<span id="cb16-3"><a href="#cb16-3"></a>gtkdep = dependency('gtk4')</span>
|
||||
<span id="cb16-4"><a href="#cb16-4"></a></span>
|
||||
<span id="cb16-5"><a href="#cb16-5"></a>gnome=import('gnome')</span>
|
||||
<span id="cb16-6"><a href="#cb16-6"></a>resources = gnome.compile_resources('resources','tfe.gresource.xml')</span>
|
||||
<span id="cb16-7"><a href="#cb16-7"></a>gnome.compile_schemas(build_by_default: true, depend_files: 'com.github.ToshioCP.tfe.gschema.xml')</span>
|
||||
<span id="cb16-8"><a href="#cb16-8"></a></span>
|
||||
<span id="cb16-9"><a href="#cb16-9"></a>sourcefiles=files('tfeapplication.c', 'tfewindow.c', 'tfenotebook.c', 'tfepref.c', 'tfealert.c', 'css.c', '../tfetextview/tfetextview.c')</span>
|
||||
<span id="cb16-10"><a href="#cb16-10"></a></span>
|
||||
<span id="cb16-11"><a href="#cb16-11"></a>executable('tfe', sourcefiles, resources, dependencies: gtkdep, export_dynamic: true, install: true)</span>
|
||||
<span id="cb16-12"><a href="#cb16-12"></a></span>
|
||||
<span id="cb16-13"><a href="#cb16-13"></a>schema_dir = get_option('prefix') / get_option('datadir') / 'glib-2.0/schemas/'</span>
|
||||
<span id="cb16-14"><a href="#cb16-14"></a>install_data('com.github.ToshioCP.tfe.gschema.xml', install_dir: schema_dir)</span>
|
||||
<span id="cb16-15"><a href="#cb16-15"></a>meson.add_install_script('glib-compile-schemas', schema_dir)</span></code></pre></div>
|
||||
<h2 id="compilation-and-installation.">Compilation and installation.</h2>
|
||||
<p>If you build Gtk4 from the source, use <code>--prefix</code> option.</p>
|
||||
<pre><code>$ meson --prefix=$HOME/local _build
|
||||
$ ninja -C _build
|
||||
$ ninja -C _build install</code></pre>
|
||||
<p>If you install Gtk4 from the distribution packages, you don’t need the prefix option. Maybe you need root privilege to install it.</p>
|
||||
<pre><code>$ meson _build
|
||||
$ ninja -C _build
|
||||
$ ninja -C _build install # or 'sudo ninja -C _build install'</code></pre>
|
||||
<p>Source files are in <a href="../src/tfe7">src/tfe7</a> directory.</p>
|
||||
<p>We made a very small text editor. You can add features to this editor. When you add a new feature, care about the structure of the program. Maybe you need to divide a file into several files like this section. It isn’t good to put many things into one file. And it is important to think about the relationship between source files and widget structures. It is appropriate that they correspond to each other in many cases.</p>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec20.html">Section 20</a>, Next: <a href="sec22.html">Section 22</a></p>
|
||||
</body>
|
||||
</html>
|
248
docs/sec22.html
Normal file
248
docs/sec22.html
Normal file
|
@ -0,0 +1,248 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>Gtk4 tutorial for beginners</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
body {width: 1080px; margin: 0 auto; font-size: large;}
|
||||
h2 {padding: 10px; background-color: #d0f0d0; }
|
||||
div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
|
||||
th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
|
||||
td {padding: 2px 6px; border: 1px solid;}
|
||||
img {display: block; margin-left: auto; margin-right: auto;}
|
||||
figcaption {text-align: center;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec21.html">Section 21</a>, Next: <a href="sec23.html">Section 23</a></p>
|
||||
<h1 id="gtkdrawingarea-and-cairo">GtkDrawingArea and Cairo</h1>
|
||||
<p>If you want to draw dynamically on the screen, like an image window of gimp graphics editor, the GtkDrawingArea widget is the most suitable widget. You can freely draw or redraw an image in this widget. This is called custom drawing.</p>
|
||||
<p>GtkDrawingArea provides a cairo drawing context so users can draw images by using cairo functions. In this section, I will explain:</p>
|
||||
<ol type="1">
|
||||
<li>Cairo, but only briefly; and</li>
|
||||
<li>GtkDrawingArea, with a very simple example.</li>
|
||||
</ol>
|
||||
<h2 id="cairo">Cairo</h2>
|
||||
<p>Cairo is a set of two dimensional graphical drawing functions (or graphics library). There is a lot of documentation on <a href="https://www.cairographics.org/">Cairo’s website</a>. If you aren’t familiar with Cairo, it is worth reading their <a href="https://www.cairographics.org/tutorial/">tutorial</a>.</p>
|
||||
<p>The following is a gentle introduction to the Cairo library and how to use it. Firstly, in order to use Cairo you need to know about surfaces, sources, masks, destinations, cairo context and transformations.</p>
|
||||
<ul>
|
||||
<li>A surface represents an image. It is like a canvas. We can draw shapes and images with different colors on surfaces.</li>
|
||||
<li>The source pattern, or simply source, is like paint, which will be transferred to destination surface by cairo functions.</li>
|
||||
<li>The mask describes the area to be used in the copy;</li>
|
||||
<li>The destination is a target surface;</li>
|
||||
<li>The cairo context manages the transfer from source to destination, through mask with its functions; For example, <code>cairo_stroke</code> is a function to draw a path to the destination by the transfer.</li>
|
||||
<li>A transformation can be applied before the transfer completes. The transformation which is applied is called affine, which is a mathematical term meaning transofrmations that preserve straight lines. Scaling, rotating, reflecting, shearing and translating are all examples of affine transformations. They are mathematically represented by matrix multiplication and vector addition. In this section we don’t use it, instead we will only use the identity transformation. This means that the coordinates in the source and mask are the same as the coordinates in destination.</li>
|
||||
</ul>
|
||||
<figure>
|
||||
<img src="../image/cairo.png" alt="" /><figcaption>Stroke a rectangle</figcaption>
|
||||
</figure>
|
||||
<p>The instruction is as follows:</p>
|
||||
<ol type="1">
|
||||
<li>Create a surface. This will be the destination.</li>
|
||||
<li>Create a cairo context with the surface, the surface will be the destination of the context.</li>
|
||||
<li>Create a source pattern within the context.</li>
|
||||
<li>Create paths, which are lines, rectangles, arcs, texts or more complicated shapes in the mask.</li>
|
||||
<li>Use a drawing operator such as <code>cairo_stroke</code> to transfer the paint in the source to the destination.</li>
|
||||
<li>Save the destination surface to a file if necessary.</li>
|
||||
</ol>
|
||||
<p>Here’s a simple example program that draws a small square and saves it as a png file.</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"><cairo.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">int</span></span>
|
||||
<span id="cb1-4"><a href="#cb1-4"></a>main (<span class="dt">int</span> argc, <span class="dt">char</span> **argv)</span>
|
||||
<span id="cb1-5"><a href="#cb1-5"></a>{</span>
|
||||
<span id="cb1-6"><a href="#cb1-6"></a> cairo_surface_t *surface;</span>
|
||||
<span id="cb1-7"><a href="#cb1-7"></a> cairo_t *cr;</span>
|
||||
<span id="cb1-8"><a href="#cb1-8"></a> <span class="dt">int</span> width = <span class="dv">100</span>;</span>
|
||||
<span id="cb1-9"><a href="#cb1-9"></a> <span class="dt">int</span> height = <span class="dv">100</span>;</span>
|
||||
<span id="cb1-10"><a href="#cb1-10"></a> <span class="dt">int</span> square_size = <span class="fl">40.0</span>;</span>
|
||||
<span id="cb1-11"><a href="#cb1-11"></a></span>
|
||||
<span id="cb1-12"><a href="#cb1-12"></a> <span class="co">/* Create surface and cairo */</span></span>
|
||||
<span id="cb1-13"><a href="#cb1-13"></a> surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);</span>
|
||||
<span id="cb1-14"><a href="#cb1-14"></a> cr = cairo_create (surface);</span>
|
||||
<span id="cb1-15"><a href="#cb1-15"></a></span>
|
||||
<span id="cb1-16"><a href="#cb1-16"></a> <span class="co">/* Drawing starts here. */</span></span>
|
||||
<span id="cb1-17"><a href="#cb1-17"></a> <span class="co">/* Paint the background white */</span></span>
|
||||
<span id="cb1-18"><a href="#cb1-18"></a> cairo_set_source_rgb (cr, <span class="fl">1.0</span>, <span class="fl">1.0</span>, <span class="fl">1.0</span>);</span>
|
||||
<span id="cb1-19"><a href="#cb1-19"></a> cairo_paint (cr);</span>
|
||||
<span id="cb1-20"><a href="#cb1-20"></a> <span class="co">/* Draw a black rectangle */</span></span>
|
||||
<span id="cb1-21"><a href="#cb1-21"></a> cairo_set_source_rgb (cr, <span class="fl">0.0</span>, <span class="fl">0.0</span>, <span class="fl">0.0</span>);</span>
|
||||
<span id="cb1-22"><a href="#cb1-22"></a> cairo_set_line_width (cr, <span class="fl">2.0</span>);</span>
|
||||
<span id="cb1-23"><a href="#cb1-23"></a> cairo_rectangle (cr,</span>
|
||||
<span id="cb1-24"><a href="#cb1-24"></a> width/<span class="fl">2.0</span> - square_size/<span class="dv">2</span>,</span>
|
||||
<span id="cb1-25"><a href="#cb1-25"></a> height/<span class="fl">2.0</span> - square_size/<span class="dv">2</span>,</span>
|
||||
<span id="cb1-26"><a href="#cb1-26"></a> square_size,</span>
|
||||
<span id="cb1-27"><a href="#cb1-27"></a> square_size);</span>
|
||||
<span id="cb1-28"><a href="#cb1-28"></a> cairo_stroke (cr);</span>
|
||||
<span id="cb1-29"><a href="#cb1-29"></a></span>
|
||||
<span id="cb1-30"><a href="#cb1-30"></a> <span class="co">/* Write the surface to a png file and clean up cairo and surface. */</span></span>
|
||||
<span id="cb1-31"><a href="#cb1-31"></a> cairo_surface_write_to_png (surface, <span class="st">"rectangle.png"</span>);</span>
|
||||
<span id="cb1-32"><a href="#cb1-32"></a> cairo_destroy (cr);</span>
|
||||
<span id="cb1-33"><a href="#cb1-33"></a> cairo_surface_destroy (surface);</span>
|
||||
<span id="cb1-34"><a href="#cb1-34"></a></span>
|
||||
<span id="cb1-35"><a href="#cb1-35"></a> <span class="cf">return</span> <span class="dv">0</span>;</span>
|
||||
<span id="cb1-36"><a href="#cb1-36"></a>}</span></code></pre></div>
|
||||
<ul>
|
||||
<li>1: Includes the header file of Cairo.</li>
|
||||
<li>6: <code>cairo_surface_t</code> is the type of a surface.</li>
|
||||
<li>7: <code>cairo_t</code> is the type of a cairo context.</li>
|
||||
<li>8-10: <code>width</code> and <code>height</code> are the size of <code>surface</code>. <code>square_size</code> is the size of a square to be drawn on the surface.</li>
|
||||
<li>13: <code>cairo_image_surface_create</code> creates an image surface. <code>CAIRO_FORMAT_RGB24</code> is a constant which means that each pixel has red, green and blue data, and each data point is an 8 bits number (for 24 bits in total). Modern displays have this type of color depth. Width and height are in pixels and given as integers.</li>
|
||||
<li>14: Creates cairo context. The surface given as an argument will be the destination of the context.</li>
|
||||
<li>18: <code>cairo_set_source_rgb</code> creates a source pattern, which in this case is a solid white paint. The second to fourth argument are red, green and blue color values respectively, and they are of type float. The values are between zero (0.0) and one (1.0), with black being given by (0.0,0.0,0.0) and white by (1.0,1.0,1.0).</li>
|
||||
<li>19: <code>cairo_paint</code> copies everywhere in the source to destination. The destination is filled with white pixels with this command.</li>
|
||||
<li>21: Sets the source color to black.</li>
|
||||
<li>22: <code>cairo_set_line_width</code> set the width of lines. In this case, the line width is set to be two pixels and will end up that same size. (It is because the transformation is identity. If the transformation isn’t identity, for example scaling with the factor three, the actual width in destination will be six (2x3=6) pixels.)</li>
|
||||
<li>23: Draws a rectangle (square) on the mask. The square is located at the center.</li>
|
||||
<li>24: <code>cairo_stroke</code> transfer the source to destination through the rectangle in the mask.</li>
|
||||
<li>27: Outputs the image to a png file <code>rectangle.png</code>.</li>
|
||||
<li>28: Destroys the context. At the same time the source is destroyed.</li>
|
||||
<li>29: Destroys the surface.</li>
|
||||
</ul>
|
||||
<p>To compile this, type the following.</p>
|
||||
<pre><code>$ gcc `pkg-config --cflags cairo` cairo.c `pkg-config --libs cairo`</code></pre>
|
||||
<figure>
|
||||
<img src="../image/rectangle.png" alt="" /><figcaption>rectangle.png</figcaption>
|
||||
</figure>
|
||||
<p>See the <a href="https://www.cairographics.org/">Cairo’s website</a> for more details.</p>
|
||||
<h2 id="gtkdrawingarea">GtkDrawingArea</h2>
|
||||
<p>The following is a very simple example.</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>draw_function (GtkDrawingArea *area, cairo_t *cr, <span class="dt">int</span> width, <span class="dt">int</span> height, gpointer user_data) {</span>
|
||||
<span id="cb3-5"><a href="#cb3-5"></a> <span class="dt">int</span> square_size = <span class="fl">40.0</span>;</span>
|
||||
<span id="cb3-6"><a href="#cb3-6"></a></span>
|
||||
<span id="cb3-7"><a href="#cb3-7"></a> cairo_set_source_rgb (cr, <span class="fl">1.0</span>, <span class="fl">1.0</span>, <span class="fl">1.0</span>); <span class="co">/* white */</span></span>
|
||||
<span id="cb3-8"><a href="#cb3-8"></a> cairo_paint (cr);</span>
|
||||
<span id="cb3-9"><a href="#cb3-9"></a> cairo_set_line_width (cr, <span class="fl">2.0</span>);</span>
|
||||
<span id="cb3-10"><a href="#cb3-10"></a> cairo_set_source_rgb (cr, <span class="fl">0.0</span>, <span class="fl">0.0</span>, <span class="fl">0.0</span>); <span class="co">/* black */</span></span>
|
||||
<span id="cb3-11"><a href="#cb3-11"></a> cairo_rectangle (cr,</span>
|
||||
<span id="cb3-12"><a href="#cb3-12"></a> width/<span class="fl">2.0</span> - square_size/<span class="dv">2</span>,</span>
|
||||
<span id="cb3-13"><a href="#cb3-13"></a> height/<span class="fl">2.0</span> - square_size/<span class="dv">2</span>,</span>
|
||||
<span id="cb3-14"><a href="#cb3-14"></a> square_size,</span>
|
||||
<span id="cb3-15"><a href="#cb3-15"></a> square_size);</span>
|
||||
<span id="cb3-16"><a href="#cb3-16"></a> cairo_stroke (cr);</span>
|
||||
<span id="cb3-17"><a href="#cb3-17"></a>}</span>
|
||||
<span id="cb3-18"><a href="#cb3-18"></a></span>
|
||||
<span id="cb3-19"><a href="#cb3-19"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb3-20"><a href="#cb3-20"></a>app_activate (GApplication *app, gpointer user_data) {</span>
|
||||
<span id="cb3-21"><a href="#cb3-21"></a> GtkWidget *win = gtk_application_window_new (GTK_APPLICATION (app));</span>
|
||||
<span id="cb3-22"><a href="#cb3-22"></a> GtkWidget *area = gtk_drawing_area_new ();</span>
|
||||
<span id="cb3-23"><a href="#cb3-23"></a></span>
|
||||
<span id="cb3-24"><a href="#cb3-24"></a> gtk_window_set_title (GTK_WINDOW (win), <span class="st">"da1"</span>);</span>
|
||||
<span id="cb3-25"><a href="#cb3-25"></a> gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (area), draw_function, NULL, NULL);</span>
|
||||
<span id="cb3-26"><a href="#cb3-26"></a> gtk_window_set_child (GTK_WINDOW (win), area);</span>
|
||||
<span id="cb3-27"><a href="#cb3-27"></a></span>
|
||||
<span id="cb3-28"><a href="#cb3-28"></a> gtk_widget_show (win);</span>
|
||||
<span id="cb3-29"><a href="#cb3-29"></a>}</span>
|
||||
<span id="cb3-30"><a href="#cb3-30"></a></span>
|
||||
<span id="cb3-31"><a href="#cb3-31"></a><span class="pp">#define APPLICATION_ID "com.github.ToshioCP.da1"</span></span>
|
||||
<span id="cb3-32"><a href="#cb3-32"></a></span>
|
||||
<span id="cb3-33"><a href="#cb3-33"></a><span class="dt">int</span></span>
|
||||
<span id="cb3-34"><a href="#cb3-34"></a>main (<span class="dt">int</span> argc, <span class="dt">char</span> **argv) {</span>
|
||||
<span id="cb3-35"><a href="#cb3-35"></a> GtkApplication *app;</span>
|
||||
<span id="cb3-36"><a href="#cb3-36"></a> <span class="dt">int</span> stat;</span>
|
||||
<span id="cb3-37"><a href="#cb3-37"></a></span>
|
||||
<span id="cb3-38"><a href="#cb3-38"></a> app = gtk_application_new (APPLICATION_ID, G_APPLICATION_FLAGS_NONE);</span>
|
||||
<span id="cb3-39"><a href="#cb3-39"></a> g_signal_connect (app, <span class="st">"activate"</span>, G_CALLBACK (app_activate), NULL);</span>
|
||||
<span id="cb3-40"><a href="#cb3-40"></a> stat =g_application_run (G_APPLICATION (app), argc, argv);</span>
|
||||
<span id="cb3-41"><a href="#cb3-41"></a> g_object_unref (app);</span>
|
||||
<span id="cb3-42"><a href="#cb3-42"></a> <span class="cf">return</span> stat;</span>
|
||||
<span id="cb3-43"><a href="#cb3-43"></a>}</span></code></pre></div>
|
||||
<p>The function <code>main</code> is almost same as before. The two functions <code>app_activate</code> and <code>draw_function</code> are important in this example.</p>
|
||||
<ul>
|
||||
<li>18: Creates a GtkDrawingArea instance; and</li>
|
||||
<li>21: Sets a drawing function of the widget. GtkDrawingArea widget uses the function to draw the contents of itself whenever its necessary. For example, when a user drag a mouse pointer and resize a top-level window, GtkDrawingArea also changes the size. Then, the whole window needs to be redrawn. For the information of <code>gtk_drawing_area_set_draw_func</code>, see <a href="https://docs.gtk.org/gtk4/method.DrawingArea.set_draw_func.html">Gtk API Reference, gtk_drawing_area_set_draw_func</a>.</li>
|
||||
</ul>
|
||||
<p>The drawing function has five parameters.</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="dt">void</span> drawing_function (GtkDrawingArea *drawing_area, cairo_t *cr, <span class="dt">int</span> width, <span class="dt">int</span> height,</span>
|
||||
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true"></a> gpointer user_data);</span></code></pre></div>
|
||||
<p>The first parameter is the GtkDrawingArea widget. You can’t change any properties, for example <code>content-width</code> or <code>content-height</code>, in this function. The second parameter is a cairo context given by the widget. The destination surface of the context is connected to the contents of the widget. What you draw to this surface will appear in the widget on the screen. The third and fourth parameters are the size of the destination surface. Now, look at the program example again.</p>
|
||||
<ul>
|
||||
<li>3-13: The drawing function.</li>
|
||||
<li>7-8: Sets the source to be white and paint the destination white.</li>
|
||||
<li>9: Sets the line width to be 2.</li>
|
||||
<li>10: Sets the source to be black.</li>
|
||||
<li>11: Adds a rectangle to the mask.</li>
|
||||
<li>12: Draws the rectangle with black color to the destination.</li>
|
||||
</ul>
|
||||
<p>Compile and run it, then a window with a black rectangle (square) appears. Try resizing the window. The square always appears at the center of the window because the drawing function is invoked each time the window is resized.</p>
|
||||
<figure>
|
||||
<img src="../image/da1.png" alt="" /><figcaption>Square in the window</figcaption>
|
||||
</figure>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec21.html">Section 21</a>, Next: <a href="sec23.html">Section 23</a></p>
|
||||
</body>
|
||||
</html>
|
440
docs/sec23.html
Normal file
440
docs/sec23.html
Normal file
|
@ -0,0 +1,440 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>Gtk4 tutorial for beginners</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
body {width: 1080px; margin: 0 auto; font-size: large;}
|
||||
h2 {padding: 10px; background-color: #d0f0d0; }
|
||||
div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
|
||||
th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
|
||||
td {padding: 2px 6px; border: 1px solid;}
|
||||
img {display: block; margin-left: auto; margin-right: auto;}
|
||||
figcaption {text-align: center;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec22.html">Section 22</a>, Next: <a href="sec24.html">Section 24</a></p>
|
||||
<h1 id="periodic-events">Periodic Events</h1>
|
||||
<p>This chapter was written by Paul Schulz <a href="mailto:paul@mawsonlakes.org" class="email">paul@mawsonlakes.org</a>.</p>
|
||||
<h2 id="how-do-we-create-an-animation">How do we create an animation?</h2>
|
||||
<p>In this section we will continue to build on our previous work. We will create an analog clock application. By adding a function which periodically redraws GtkDrawingArea, the clock will be able to continuously display the time.</p>
|
||||
<p>The application uses a compiled in ‘resource’ file, so if the GTK4 libraries and their dependencies are installed and available, the application will run from anywhere.</p>
|
||||
<p>The program also makes use of some standard mathematical and time handling functions.</p>
|
||||
<p>The clocks mechanics were taken from a Cairo drawing example, using gtkmm4, which can be found <a href="https://developer-old.gnome.org/gtkmm-tutorial/stable/sec-drawing-clock-example.html.en">here</a>.</p>
|
||||
<p>The complete code is at the end.</p>
|
||||
<h2 id="drawing-the-clock-face-hour-minute-and-second-hands">Drawing the clock face, hour, minute and second hands</h2>
|
||||
<p>The <code>draw_clock()</code> function does all the work. See the in-file comments for an explanation of how the Cairo drawing works.</p>
|
||||
<p>For a detailed reference of what each of the Cairo functions does see the <a href="https://www.cairographics.org/manual/cairo-cairo-t.html">cairo_t reference</a>.</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>draw_clock (GtkDrawingArea *area, cairo_t *cr, <span class="dt">int</span> width, <span class="dt">int</span> height, gpointer user_data) {</span>
|
||||
<span id="cb1-3"><a href="#cb1-3"></a></span>
|
||||
<span id="cb1-4"><a href="#cb1-4"></a> <span class="co">// Scale to unit square and translate (0, 0) to be (0.5, 0.5), i.e.</span></span>
|
||||
<span id="cb1-5"><a href="#cb1-5"></a> <span class="co">// the center of the window</span></span>
|
||||
<span id="cb1-6"><a href="#cb1-6"></a> cairo_scale(cr, width, height);</span>
|
||||
<span id="cb1-7"><a href="#cb1-7"></a> cairo_translate(cr, <span class="fl">0.5</span>, <span class="fl">0.5</span>);</span>
|
||||
<span id="cb1-8"><a href="#cb1-8"></a></span>
|
||||
<span id="cb1-9"><a href="#cb1-9"></a> <span class="co">// Set the line width and save the cairo drawing state.</span></span>
|
||||
<span id="cb1-10"><a href="#cb1-10"></a> cairo_set_line_width(cr, m_line_width);</span>
|
||||
<span id="cb1-11"><a href="#cb1-11"></a> cairo_save(cr);</span>
|
||||
<span id="cb1-12"><a href="#cb1-12"></a></span>
|
||||
<span id="cb1-13"><a href="#cb1-13"></a> <span class="co">// Set the background to a slightly transparent green.</span></span>
|
||||
<span id="cb1-14"><a href="#cb1-14"></a> cairo_set_source_rgba(cr, <span class="fl">0.337</span>, <span class="fl">0.612</span>, <span class="fl">0.117</span>, <span class="fl">0.9</span>); <span class="co">// green</span></span>
|
||||
<span id="cb1-15"><a href="#cb1-15"></a> cairo_paint(cr);</span>
|
||||
<span id="cb1-16"><a href="#cb1-16"></a></span>
|
||||
<span id="cb1-17"><a href="#cb1-17"></a> <span class="co">// Resore back to precious drawing state and draw the circular path</span></span>
|
||||
<span id="cb1-18"><a href="#cb1-18"></a> <span class="co">// representing the clockface. Save this state (including the path) so we</span></span>
|
||||
<span id="cb1-19"><a href="#cb1-19"></a> <span class="co">// can reuse it.</span></span>
|
||||
<span id="cb1-20"><a href="#cb1-20"></a> cairo_restore(cr);</span>
|
||||
<span id="cb1-21"><a href="#cb1-21"></a> cairo_arc(cr, <span class="fl">0.0</span>, <span class="fl">0.0</span>, m_radius, <span class="fl">0.0</span>, <span class="fl">2.0</span> * M_PI);</span>
|
||||
<span id="cb1-22"><a href="#cb1-22"></a> cairo_save(cr);</span>
|
||||
<span id="cb1-23"><a href="#cb1-23"></a></span>
|
||||
<span id="cb1-24"><a href="#cb1-24"></a> <span class="co">// Fill the clockface with white</span></span>
|
||||
<span id="cb1-25"><a href="#cb1-25"></a> cairo_set_source_rgba(cr, <span class="fl">1.0</span>, <span class="fl">1.0</span>, <span class="fl">1.0</span>, <span class="fl">0.8</span>);</span>
|
||||
<span id="cb1-26"><a href="#cb1-26"></a> cairo_fill_preserve(cr);</span>
|
||||
<span id="cb1-27"><a href="#cb1-27"></a> <span class="co">// Restore the path, paint the outside of the clock face.</span></span>
|
||||
<span id="cb1-28"><a href="#cb1-28"></a> cairo_restore(cr);</span>
|
||||
<span id="cb1-29"><a href="#cb1-29"></a> cairo_stroke_preserve(cr);</span>
|
||||
<span id="cb1-30"><a href="#cb1-30"></a> <span class="co">// Set the 'clip region' to the inside of the path (fill region).</span></span>
|
||||
<span id="cb1-31"><a href="#cb1-31"></a> cairo_clip(cr);</span>
|
||||
<span id="cb1-32"><a href="#cb1-32"></a></span>
|
||||
<span id="cb1-33"><a href="#cb1-33"></a> <span class="co">// Clock ticks</span></span>
|
||||
<span id="cb1-34"><a href="#cb1-34"></a> <span class="cf">for</span> (<span class="dt">int</span> i = <span class="dv">0</span>; i < <span class="dv">12</span>; i++)</span>
|
||||
<span id="cb1-35"><a href="#cb1-35"></a> {</span>
|
||||
<span id="cb1-36"><a href="#cb1-36"></a> <span class="co">// Major tick size</span></span>
|
||||
<span id="cb1-37"><a href="#cb1-37"></a> <span class="dt">double</span> inset = <span class="fl">0.05</span>;</span>
|
||||
<span id="cb1-38"><a href="#cb1-38"></a></span>
|
||||
<span id="cb1-39"><a href="#cb1-39"></a> <span class="co">// Save the graphics state, restore after drawing tick to maintain pen</span></span>
|
||||
<span id="cb1-40"><a href="#cb1-40"></a> <span class="co">// size</span></span>
|
||||
<span id="cb1-41"><a href="#cb1-41"></a> cairo_save(cr);</span>
|
||||
<span id="cb1-42"><a href="#cb1-42"></a> cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);</span>
|
||||
<span id="cb1-43"><a href="#cb1-43"></a></span>
|
||||
<span id="cb1-44"><a href="#cb1-44"></a> <span class="co">// Minor ticks are shorter, and narrower.</span></span>
|
||||
<span id="cb1-45"><a href="#cb1-45"></a> <span class="cf">if</span>(i % <span class="dv">3</span> != <span class="dv">0</span>)</span>
|
||||
<span id="cb1-46"><a href="#cb1-46"></a> {</span>
|
||||
<span id="cb1-47"><a href="#cb1-47"></a> inset *= <span class="fl">0.8</span>;</span>
|
||||
<span id="cb1-48"><a href="#cb1-48"></a> cairo_set_line_width(cr, <span class="fl">0.03</span>);</span>
|
||||
<span id="cb1-49"><a href="#cb1-49"></a> }</span>
|
||||
<span id="cb1-50"><a href="#cb1-50"></a></span>
|
||||
<span id="cb1-51"><a href="#cb1-51"></a> <span class="co">// Draw tick mark</span></span>
|
||||
<span id="cb1-52"><a href="#cb1-52"></a> cairo_move_to(</span>
|
||||
<span id="cb1-53"><a href="#cb1-53"></a> cr,</span>
|
||||
<span id="cb1-54"><a href="#cb1-54"></a> (m_radius - inset) * cos (i * M_PI / <span class="fl">6.0</span>),</span>
|
||||
<span id="cb1-55"><a href="#cb1-55"></a> (m_radius - inset) * sin (i * M_PI / <span class="fl">6.0</span>));</span>
|
||||
<span id="cb1-56"><a href="#cb1-56"></a> cairo_line_to(</span>
|
||||
<span id="cb1-57"><a href="#cb1-57"></a> cr,</span>
|
||||
<span id="cb1-58"><a href="#cb1-58"></a> m_radius * cos (i * M_PI / <span class="fl">6.0</span>),</span>
|
||||
<span id="cb1-59"><a href="#cb1-59"></a> m_radius * sin (i * M_PI / <span class="fl">6.0</span>));</span>
|
||||
<span id="cb1-60"><a href="#cb1-60"></a> cairo_stroke(cr);</span>
|
||||
<span id="cb1-61"><a href="#cb1-61"></a> cairo_restore(cr); <span class="co">/* stack-pen-size */</span></span>
|
||||
<span id="cb1-62"><a href="#cb1-62"></a> }</span>
|
||||
<span id="cb1-63"><a href="#cb1-63"></a></span>
|
||||
<span id="cb1-64"><a href="#cb1-64"></a> <span class="co">// Draw the analog hands</span></span>
|
||||
<span id="cb1-65"><a href="#cb1-65"></a></span>
|
||||
<span id="cb1-66"><a href="#cb1-66"></a> <span class="co">// Get the current Unix time, convert to the local time and break into time</span></span>
|
||||
<span id="cb1-67"><a href="#cb1-67"></a> <span class="co">// structure to read various time parts.</span></span>
|
||||
<span id="cb1-68"><a href="#cb1-68"></a> <span class="dt">time_t</span> rawtime;</span>
|
||||
<span id="cb1-69"><a href="#cb1-69"></a> time(&rawtime);</span>
|
||||
<span id="cb1-70"><a href="#cb1-70"></a> <span class="kw">struct</span> tm * timeinfo = localtime (&rawtime);</span>
|
||||
<span id="cb1-71"><a href="#cb1-71"></a></span>
|
||||
<span id="cb1-72"><a href="#cb1-72"></a> <span class="co">// Calculate the angles of the hands of our clock</span></span>
|
||||
<span id="cb1-73"><a href="#cb1-73"></a> <span class="dt">double</span> hours = timeinfo->tm_hour * M_PI / <span class="fl">6.0</span>;</span>
|
||||
<span id="cb1-74"><a href="#cb1-74"></a> <span class="dt">double</span> minutes = timeinfo->tm_min * M_PI / <span class="fl">30.0</span>;</span>
|
||||
<span id="cb1-75"><a href="#cb1-75"></a> <span class="dt">double</span> seconds = timeinfo->tm_sec * M_PI / <span class="fl">30.0</span>;</span>
|
||||
<span id="cb1-76"><a href="#cb1-76"></a></span>
|
||||
<span id="cb1-77"><a href="#cb1-77"></a> <span class="co">// Save the graphics state</span></span>
|
||||
<span id="cb1-78"><a href="#cb1-78"></a> cairo_save(cr);</span>
|
||||
<span id="cb1-79"><a href="#cb1-79"></a> cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);</span>
|
||||
<span id="cb1-80"><a href="#cb1-80"></a></span>
|
||||
<span id="cb1-81"><a href="#cb1-81"></a> cairo_save(cr);</span>
|
||||
<span id="cb1-82"><a href="#cb1-82"></a></span>
|
||||
<span id="cb1-83"><a href="#cb1-83"></a> <span class="co">// Draw the seconds hand</span></span>
|
||||
<span id="cb1-84"><a href="#cb1-84"></a> cairo_set_line_width(cr, m_line_width / <span class="fl">3.0</span>);</span>
|
||||
<span id="cb1-85"><a href="#cb1-85"></a> cairo_set_source_rgba(cr, <span class="fl">0.7</span>, <span class="fl">0.7</span>, <span class="fl">0.7</span>, <span class="fl">0.8</span>); <span class="co">// gray</span></span>
|
||||
<span id="cb1-86"><a href="#cb1-86"></a> cairo_move_to(cr, <span class="fl">0.0</span>, <span class="fl">0.0</span>);</span>
|
||||
<span id="cb1-87"><a href="#cb1-87"></a> cairo_line_to(cr,</span>
|
||||
<span id="cb1-88"><a href="#cb1-88"></a> sin(seconds) * (m_radius * <span class="fl">0.9</span>),</span>
|
||||
<span id="cb1-89"><a href="#cb1-89"></a> -cos(seconds) * (m_radius * <span class="fl">0.9</span>));</span>
|
||||
<span id="cb1-90"><a href="#cb1-90"></a> cairo_stroke(cr);</span>
|
||||
<span id="cb1-91"><a href="#cb1-91"></a> cairo_restore(cr);</span>
|
||||
<span id="cb1-92"><a href="#cb1-92"></a></span>
|
||||
<span id="cb1-93"><a href="#cb1-93"></a> <span class="co">// Draw the minutes hand</span></span>
|
||||
<span id="cb1-94"><a href="#cb1-94"></a> cairo_set_source_rgba(cr, <span class="fl">0.117</span>, <span class="fl">0.337</span>, <span class="fl">0.612</span>, <span class="fl">0.9</span>); <span class="co">// blue</span></span>
|
||||
<span id="cb1-95"><a href="#cb1-95"></a> cairo_move_to(cr, <span class="dv">0</span>, <span class="dv">0</span>);</span>
|
||||
<span id="cb1-96"><a href="#cb1-96"></a> cairo_line_to(cr,</span>
|
||||
<span id="cb1-97"><a href="#cb1-97"></a> sin(minutes + seconds / <span class="dv">60</span>) * (m_radius * <span class="fl">0.8</span>),</span>
|
||||
<span id="cb1-98"><a href="#cb1-98"></a> -cos(minutes + seconds / <span class="dv">60</span>) * (m_radius * <span class="fl">0.8</span>));</span>
|
||||
<span id="cb1-99"><a href="#cb1-99"></a> cairo_stroke(cr);</span>
|
||||
<span id="cb1-100"><a href="#cb1-100"></a></span>
|
||||
<span id="cb1-101"><a href="#cb1-101"></a> <span class="co">// draw the hours hand</span></span>
|
||||
<span id="cb1-102"><a href="#cb1-102"></a> cairo_set_source_rgba(cr, <span class="fl">0.337</span>, <span class="fl">0.612</span>, <span class="fl">0.117</span>, <span class="fl">0.9</span>); <span class="co">// green</span></span>
|
||||
<span id="cb1-103"><a href="#cb1-103"></a> cairo_move_to(cr, <span class="fl">0.0</span>, <span class="fl">0.0</span>);</span>
|
||||
<span id="cb1-104"><a href="#cb1-104"></a> cairo_line_to(cr,</span>
|
||||
<span id="cb1-105"><a href="#cb1-105"></a> sin(hours + minutes / <span class="fl">12.0</span>) * (m_radius * <span class="fl">0.5</span>),</span>
|
||||
<span id="cb1-106"><a href="#cb1-106"></a> -cos(hours + minutes / <span class="fl">12.0</span>) * (m_radius * <span class="fl">0.5</span>));</span>
|
||||
<span id="cb1-107"><a href="#cb1-107"></a> cairo_stroke(cr);</span>
|
||||
<span id="cb1-108"><a href="#cb1-108"></a> cairo_restore(cr);</span>
|
||||
<span id="cb1-109"><a href="#cb1-109"></a></span>
|
||||
<span id="cb1-110"><a href="#cb1-110"></a> <span class="co">// Draw a little dot in the middle</span></span>
|
||||
<span id="cb1-111"><a href="#cb1-111"></a> cairo_arc(cr, <span class="fl">0.0</span>, <span class="fl">0.0</span>, m_line_width / <span class="fl">3.0</span>, <span class="fl">0.0</span>, <span class="fl">2.0</span> * M_PI);</span>
|
||||
<span id="cb1-112"><a href="#cb1-112"></a> cairo_fill(cr);</span>
|
||||
<span id="cb1-113"><a href="#cb1-113"></a>}</span></code></pre></div>
|
||||
<p>In order for the clock to be drawn, the drawing function <code>draw_clock()</code> needs to be registered with GTK4. This is done in the <code>app_activate()</code> function (on line 24).</p>
|
||||
<p>Whenever the application needs to redraw the GtkDrawingArea, it will now call <code>draw_clock()</code>.</p>
|
||||
<p>There is still a problem though. In order to animate the clock we need to also tell the application that the clock needs to be redrawn every second. This process starts by registering (on the next line, line 15) a timeout function with <code>g_timeout_add()</code> that will wakeup and run another function <code>time_handler</code>, every second (or 1000ms).</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>app_activate (GApplication *app, gpointer user_data) {</span>
|
||||
<span id="cb2-3"><a href="#cb2-3"></a> GtkWidget *win;</span>
|
||||
<span id="cb2-4"><a href="#cb2-4"></a> GtkWidget *clock;</span>
|
||||
<span id="cb2-5"><a href="#cb2-5"></a> GtkBuilder *build;</span>
|
||||
<span id="cb2-6"><a href="#cb2-6"></a></span>
|
||||
<span id="cb2-7"><a href="#cb2-7"></a> build = gtk_builder_new_from_resource (<span class="st">"/com/github/ToshioCP/tfc/tfc.ui"</span>);</span>
|
||||
<span id="cb2-8"><a href="#cb2-8"></a> win = GTK_WIDGET (gtk_builder_get_object (build, <span class="st">"win"</span>));</span>
|
||||
<span id="cb2-9"><a href="#cb2-9"></a> gtk_window_set_application (GTK_WINDOW (win), GTK_APPLICATION (app));</span>
|
||||
<span id="cb2-10"><a href="#cb2-10"></a></span>
|
||||
<span id="cb2-11"><a href="#cb2-11"></a> clock = GTK_WIDGET (gtk_builder_get_object (build, <span class="st">"clock"</span>));</span>
|
||||
<span id="cb2-12"><a href="#cb2-12"></a> g_object_unref(build);</span>
|
||||
<span id="cb2-13"><a href="#cb2-13"></a></span>
|
||||
<span id="cb2-14"><a href="#cb2-14"></a> gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA (clock), draw_clock, NULL, NULL);</span>
|
||||
<span id="cb2-15"><a href="#cb2-15"></a> g_timeout_add(<span class="dv">1000</span>, (GSourceFunc) time_handler, (gpointer) clock);</span>
|
||||
<span id="cb2-16"><a href="#cb2-16"></a> gtk_widget_show(win);</span>
|
||||
<span id="cb2-17"><a href="#cb2-17"></a></span>
|
||||
<span id="cb2-18"><a href="#cb2-18"></a>}</span></code></pre></div>
|
||||
<p>Our <code>time_handler()</code> function is very simple, as it just calls <code>gtk_widget_queue_draw()</code> which schedules a redraw of the widget.</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>gboolean</span>
|
||||
<span id="cb3-2"><a href="#cb3-2"></a>time_handler(GtkWidget* widget) {</span>
|
||||
<span id="cb3-3"><a href="#cb3-3"></a> gtk_widget_queue_draw(widget);</span>
|
||||
<span id="cb3-4"><a href="#cb3-4"></a></span>
|
||||
<span id="cb3-5"><a href="#cb3-5"></a> <span class="cf">return</span> TRUE;</span>
|
||||
<span id="cb3-6"><a href="#cb3-6"></a>}</span></code></pre></div>
|
||||
<p>.. and that is all there is to it. If you compile and run the example you will get a ticking analog clock.</p>
|
||||
<p>If you get this working, you can try modifying some of the code in <code>draw_clock()</code> to tweak the application (such as change the color or size and length of the hands) or even add text, or create a digital clock.</p>
|
||||
<h2 id="the-complete-code">The Complete code</h2>
|
||||
<p>You can find the source files in the <code>tfc</code> directory. it can be compiled with <code>./comp tfc</code>.</p>
|
||||
<p><code>tfc.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="pp">#include </span><span class="im"><gtk/gtk.h></span></span>
|
||||
<span id="cb4-2"><a href="#cb4-2"></a><span class="pp">#include </span><span class="im"><math.h></span></span>
|
||||
<span id="cb4-3"><a href="#cb4-3"></a><span class="pp">#include </span><span class="im"><time.h></span></span>
|
||||
<span id="cb4-4"><a href="#cb4-4"></a></span>
|
||||
<span id="cb4-5"><a href="#cb4-5"></a><span class="dt">float</span> m_radius = <span class="fl">0.42</span>;</span>
|
||||
<span id="cb4-6"><a href="#cb4-6"></a><span class="dt">float</span> m_line_width = <span class="fl">0.05</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>draw_clock (GtkDrawingArea *area, cairo_t *cr, <span class="dt">int</span> width, <span class="dt">int</span> height, gpointer user_data) {</span>
|
||||
<span id="cb4-10"><a href="#cb4-10"></a></span>
|
||||
<span id="cb4-11"><a href="#cb4-11"></a> <span class="co">// Scale to unit square and translate (0, 0) to be (0.5, 0.5), i.e.</span></span>
|
||||
<span id="cb4-12"><a href="#cb4-12"></a> <span class="co">// the center of the window</span></span>
|
||||
<span id="cb4-13"><a href="#cb4-13"></a> cairo_scale(cr, width, height);</span>
|
||||
<span id="cb4-14"><a href="#cb4-14"></a> cairo_translate(cr, <span class="fl">0.5</span>, <span class="fl">0.5</span>);</span>
|
||||
<span id="cb4-15"><a href="#cb4-15"></a></span>
|
||||
<span id="cb4-16"><a href="#cb4-16"></a> <span class="co">// Set the line width and save the cairo drawing state.</span></span>
|
||||
<span id="cb4-17"><a href="#cb4-17"></a> cairo_set_line_width(cr, m_line_width);</span>
|
||||
<span id="cb4-18"><a href="#cb4-18"></a> cairo_save(cr);</span>
|
||||
<span id="cb4-19"><a href="#cb4-19"></a></span>
|
||||
<span id="cb4-20"><a href="#cb4-20"></a> <span class="co">// Set the background to a slightly transparent green.</span></span>
|
||||
<span id="cb4-21"><a href="#cb4-21"></a> cairo_set_source_rgba(cr, <span class="fl">0.337</span>, <span class="fl">0.612</span>, <span class="fl">0.117</span>, <span class="fl">0.9</span>); <span class="co">// green</span></span>
|
||||
<span id="cb4-22"><a href="#cb4-22"></a> cairo_paint(cr);</span>
|
||||
<span id="cb4-23"><a href="#cb4-23"></a></span>
|
||||
<span id="cb4-24"><a href="#cb4-24"></a> <span class="co">// Resore back to precious drawing state and draw the circular path</span></span>
|
||||
<span id="cb4-25"><a href="#cb4-25"></a> <span class="co">// representing the clockface. Save this state (including the path) so we</span></span>
|
||||
<span id="cb4-26"><a href="#cb4-26"></a> <span class="co">// can reuse it.</span></span>
|
||||
<span id="cb4-27"><a href="#cb4-27"></a> cairo_restore(cr);</span>
|
||||
<span id="cb4-28"><a href="#cb4-28"></a> cairo_arc(cr, <span class="fl">0.0</span>, <span class="fl">0.0</span>, m_radius, <span class="fl">0.0</span>, <span class="fl">2.0</span> * M_PI);</span>
|
||||
<span id="cb4-29"><a href="#cb4-29"></a> cairo_save(cr);</span>
|
||||
<span id="cb4-30"><a href="#cb4-30"></a></span>
|
||||
<span id="cb4-31"><a href="#cb4-31"></a> <span class="co">// Fill the clockface with white</span></span>
|
||||
<span id="cb4-32"><a href="#cb4-32"></a> cairo_set_source_rgba(cr, <span class="fl">1.0</span>, <span class="fl">1.0</span>, <span class="fl">1.0</span>, <span class="fl">0.8</span>);</span>
|
||||
<span id="cb4-33"><a href="#cb4-33"></a> cairo_fill_preserve(cr);</span>
|
||||
<span id="cb4-34"><a href="#cb4-34"></a> <span class="co">// Restore the path, paint the outside of the clock face.</span></span>
|
||||
<span id="cb4-35"><a href="#cb4-35"></a> cairo_restore(cr);</span>
|
||||
<span id="cb4-36"><a href="#cb4-36"></a> cairo_stroke_preserve(cr);</span>
|
||||
<span id="cb4-37"><a href="#cb4-37"></a> <span class="co">// Set the 'clip region' to the inside of the path (fill region).</span></span>
|
||||
<span id="cb4-38"><a href="#cb4-38"></a> cairo_clip(cr);</span>
|
||||
<span id="cb4-39"><a href="#cb4-39"></a></span>
|
||||
<span id="cb4-40"><a href="#cb4-40"></a> <span class="co">// Clock ticks</span></span>
|
||||
<span id="cb4-41"><a href="#cb4-41"></a> <span class="cf">for</span> (<span class="dt">int</span> i = <span class="dv">0</span>; i < <span class="dv">12</span>; i++)</span>
|
||||
<span id="cb4-42"><a href="#cb4-42"></a> {</span>
|
||||
<span id="cb4-43"><a href="#cb4-43"></a> <span class="co">// Major tick size</span></span>
|
||||
<span id="cb4-44"><a href="#cb4-44"></a> <span class="dt">double</span> inset = <span class="fl">0.05</span>;</span>
|
||||
<span id="cb4-45"><a href="#cb4-45"></a></span>
|
||||
<span id="cb4-46"><a href="#cb4-46"></a> <span class="co">// Save the graphics state, restore after drawing tick to maintain pen</span></span>
|
||||
<span id="cb4-47"><a href="#cb4-47"></a> <span class="co">// size</span></span>
|
||||
<span id="cb4-48"><a href="#cb4-48"></a> cairo_save(cr);</span>
|
||||
<span id="cb4-49"><a href="#cb4-49"></a> cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);</span>
|
||||
<span id="cb4-50"><a href="#cb4-50"></a></span>
|
||||
<span id="cb4-51"><a href="#cb4-51"></a> <span class="co">// Minor ticks are shorter, and narrower.</span></span>
|
||||
<span id="cb4-52"><a href="#cb4-52"></a> <span class="cf">if</span>(i % <span class="dv">3</span> != <span class="dv">0</span>)</span>
|
||||
<span id="cb4-53"><a href="#cb4-53"></a> {</span>
|
||||
<span id="cb4-54"><a href="#cb4-54"></a> inset *= <span class="fl">0.8</span>;</span>
|
||||
<span id="cb4-55"><a href="#cb4-55"></a> cairo_set_line_width(cr, <span class="fl">0.03</span>);</span>
|
||||
<span id="cb4-56"><a href="#cb4-56"></a> }</span>
|
||||
<span id="cb4-57"><a href="#cb4-57"></a></span>
|
||||
<span id="cb4-58"><a href="#cb4-58"></a> <span class="co">// Draw tick mark</span></span>
|
||||
<span id="cb4-59"><a href="#cb4-59"></a> cairo_move_to(</span>
|
||||
<span id="cb4-60"><a href="#cb4-60"></a> cr,</span>
|
||||
<span id="cb4-61"><a href="#cb4-61"></a> (m_radius - inset) * cos (i * M_PI / <span class="fl">6.0</span>),</span>
|
||||
<span id="cb4-62"><a href="#cb4-62"></a> (m_radius - inset) * sin (i * M_PI / <span class="fl">6.0</span>));</span>
|
||||
<span id="cb4-63"><a href="#cb4-63"></a> cairo_line_to(</span>
|
||||
<span id="cb4-64"><a href="#cb4-64"></a> cr,</span>
|
||||
<span id="cb4-65"><a href="#cb4-65"></a> m_radius * cos (i * M_PI / <span class="fl">6.0</span>),</span>
|
||||
<span id="cb4-66"><a href="#cb4-66"></a> m_radius * sin (i * M_PI / <span class="fl">6.0</span>));</span>
|
||||
<span id="cb4-67"><a href="#cb4-67"></a> cairo_stroke(cr);</span>
|
||||
<span id="cb4-68"><a href="#cb4-68"></a> cairo_restore(cr); <span class="co">/* stack-pen-size */</span></span>
|
||||
<span id="cb4-69"><a href="#cb4-69"></a> }</span>
|
||||
<span id="cb4-70"><a href="#cb4-70"></a></span>
|
||||
<span id="cb4-71"><a href="#cb4-71"></a> <span class="co">// Draw the analog hands</span></span>
|
||||
<span id="cb4-72"><a href="#cb4-72"></a></span>
|
||||
<span id="cb4-73"><a href="#cb4-73"></a> <span class="co">// Get the current Unix time, convert to the local time and break into time</span></span>
|
||||
<span id="cb4-74"><a href="#cb4-74"></a> <span class="co">// structure to read various time parts.</span></span>
|
||||
<span id="cb4-75"><a href="#cb4-75"></a> <span class="dt">time_t</span> rawtime;</span>
|
||||
<span id="cb4-76"><a href="#cb4-76"></a> time(&rawtime);</span>
|
||||
<span id="cb4-77"><a href="#cb4-77"></a> <span class="kw">struct</span> tm * timeinfo = localtime (&rawtime);</span>
|
||||
<span id="cb4-78"><a href="#cb4-78"></a></span>
|
||||
<span id="cb4-79"><a href="#cb4-79"></a> <span class="co">// Calculate the angles of the hands of our clock</span></span>
|
||||
<span id="cb4-80"><a href="#cb4-80"></a> <span class="dt">double</span> hours = timeinfo->tm_hour * M_PI / <span class="fl">6.0</span>;</span>
|
||||
<span id="cb4-81"><a href="#cb4-81"></a> <span class="dt">double</span> minutes = timeinfo->tm_min * M_PI / <span class="fl">30.0</span>;</span>
|
||||
<span id="cb4-82"><a href="#cb4-82"></a> <span class="dt">double</span> seconds = timeinfo->tm_sec * M_PI / <span class="fl">30.0</span>;</span>
|
||||
<span id="cb4-83"><a href="#cb4-83"></a></span>
|
||||
<span id="cb4-84"><a href="#cb4-84"></a> <span class="co">// Save the graphics state</span></span>
|
||||
<span id="cb4-85"><a href="#cb4-85"></a> cairo_save(cr);</span>
|
||||
<span id="cb4-86"><a href="#cb4-86"></a> cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);</span>
|
||||
<span id="cb4-87"><a href="#cb4-87"></a></span>
|
||||
<span id="cb4-88"><a href="#cb4-88"></a> cairo_save(cr);</span>
|
||||
<span id="cb4-89"><a href="#cb4-89"></a></span>
|
||||
<span id="cb4-90"><a href="#cb4-90"></a> <span class="co">// Draw the seconds hand</span></span>
|
||||
<span id="cb4-91"><a href="#cb4-91"></a> cairo_set_line_width(cr, m_line_width / <span class="fl">3.0</span>);</span>
|
||||
<span id="cb4-92"><a href="#cb4-92"></a> cairo_set_source_rgba(cr, <span class="fl">0.7</span>, <span class="fl">0.7</span>, <span class="fl">0.7</span>, <span class="fl">0.8</span>); <span class="co">// gray</span></span>
|
||||
<span id="cb4-93"><a href="#cb4-93"></a> cairo_move_to(cr, <span class="fl">0.0</span>, <span class="fl">0.0</span>);</span>
|
||||
<span id="cb4-94"><a href="#cb4-94"></a> cairo_line_to(cr,</span>
|
||||
<span id="cb4-95"><a href="#cb4-95"></a> sin(seconds) * (m_radius * <span class="fl">0.9</span>),</span>
|
||||
<span id="cb4-96"><a href="#cb4-96"></a> -cos(seconds) * (m_radius * <span class="fl">0.9</span>));</span>
|
||||
<span id="cb4-97"><a href="#cb4-97"></a> cairo_stroke(cr);</span>
|
||||
<span id="cb4-98"><a href="#cb4-98"></a> cairo_restore(cr);</span>
|
||||
<span id="cb4-99"><a href="#cb4-99"></a></span>
|
||||
<span id="cb4-100"><a href="#cb4-100"></a> <span class="co">// Draw the minutes hand</span></span>
|
||||
<span id="cb4-101"><a href="#cb4-101"></a> cairo_set_source_rgba(cr, <span class="fl">0.117</span>, <span class="fl">0.337</span>, <span class="fl">0.612</span>, <span class="fl">0.9</span>); <span class="co">// blue</span></span>
|
||||
<span id="cb4-102"><a href="#cb4-102"></a> cairo_move_to(cr, <span class="dv">0</span>, <span class="dv">0</span>);</span>
|
||||
<span id="cb4-103"><a href="#cb4-103"></a> cairo_line_to(cr,</span>
|
||||
<span id="cb4-104"><a href="#cb4-104"></a> sin(minutes + seconds / <span class="dv">60</span>) * (m_radius * <span class="fl">0.8</span>),</span>
|
||||
<span id="cb4-105"><a href="#cb4-105"></a> -cos(minutes + seconds / <span class="dv">60</span>) * (m_radius * <span class="fl">0.8</span>));</span>
|
||||
<span id="cb4-106"><a href="#cb4-106"></a> cairo_stroke(cr);</span>
|
||||
<span id="cb4-107"><a href="#cb4-107"></a></span>
|
||||
<span id="cb4-108"><a href="#cb4-108"></a> <span class="co">// draw the hours hand</span></span>
|
||||
<span id="cb4-109"><a href="#cb4-109"></a> cairo_set_source_rgba(cr, <span class="fl">0.337</span>, <span class="fl">0.612</span>, <span class="fl">0.117</span>, <span class="fl">0.9</span>); <span class="co">// green</span></span>
|
||||
<span id="cb4-110"><a href="#cb4-110"></a> cairo_move_to(cr, <span class="fl">0.0</span>, <span class="fl">0.0</span>);</span>
|
||||
<span id="cb4-111"><a href="#cb4-111"></a> cairo_line_to(cr,</span>
|
||||
<span id="cb4-112"><a href="#cb4-112"></a> sin(hours + minutes / <span class="fl">12.0</span>) * (m_radius * <span class="fl">0.5</span>),</span>
|
||||
<span id="cb4-113"><a href="#cb4-113"></a> -cos(hours + minutes / <span class="fl">12.0</span>) * (m_radius * <span class="fl">0.5</span>));</span>
|
||||
<span id="cb4-114"><a href="#cb4-114"></a> cairo_stroke(cr);</span>
|
||||
<span id="cb4-115"><a href="#cb4-115"></a> cairo_restore(cr);</span>
|
||||
<span id="cb4-116"><a href="#cb4-116"></a></span>
|
||||
<span id="cb4-117"><a href="#cb4-117"></a> <span class="co">// Draw a little dot in the middle</span></span>
|
||||
<span id="cb4-118"><a href="#cb4-118"></a> cairo_arc(cr, <span class="fl">0.0</span>, <span class="fl">0.0</span>, m_line_width / <span class="fl">3.0</span>, <span class="fl">0.0</span>, <span class="fl">2.0</span> * M_PI);</span>
|
||||
<span id="cb4-119"><a href="#cb4-119"></a> cairo_fill(cr);</span>
|
||||
<span id="cb4-120"><a href="#cb4-120"></a>}</span>
|
||||
<span id="cb4-121"><a href="#cb4-121"></a></span>
|
||||
<span id="cb4-122"><a href="#cb4-122"></a></span>
|
||||
<span id="cb4-123"><a href="#cb4-123"></a>gboolean</span>
|
||||
<span id="cb4-124"><a href="#cb4-124"></a>time_handler(GtkWidget* widget) {</span>
|
||||
<span id="cb4-125"><a href="#cb4-125"></a> gtk_widget_queue_draw(widget);</span>
|
||||
<span id="cb4-126"><a href="#cb4-126"></a></span>
|
||||
<span id="cb4-127"><a href="#cb4-127"></a> <span class="cf">return</span> TRUE;</span>
|
||||
<span id="cb4-128"><a href="#cb4-128"></a>}</span>
|
||||
<span id="cb4-129"><a href="#cb4-129"></a></span>
|
||||
<span id="cb4-130"><a href="#cb4-130"></a></span>
|
||||
<span id="cb4-131"><a href="#cb4-131"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb4-132"><a href="#cb4-132"></a>app_activate (GApplication *app, gpointer user_data) {</span>
|
||||
<span id="cb4-133"><a href="#cb4-133"></a> GtkWidget *win;</span>
|
||||
<span id="cb4-134"><a href="#cb4-134"></a> GtkWidget *clock;</span>
|
||||
<span id="cb4-135"><a href="#cb4-135"></a> GtkBuilder *build;</span>
|
||||
<span id="cb4-136"><a href="#cb4-136"></a></span>
|
||||
<span id="cb4-137"><a href="#cb4-137"></a> build = gtk_builder_new_from_resource (<span class="st">"/com/github/ToshioCP/tfc/tfc.ui"</span>);</span>
|
||||
<span id="cb4-138"><a href="#cb4-138"></a> win = GTK_WIDGET (gtk_builder_get_object (build, <span class="st">"win"</span>));</span>
|
||||
<span id="cb4-139"><a href="#cb4-139"></a> gtk_window_set_application (GTK_WINDOW (win), GTK_APPLICATION (app));</span>
|
||||
<span id="cb4-140"><a href="#cb4-140"></a></span>
|
||||
<span id="cb4-141"><a href="#cb4-141"></a> clock = GTK_WIDGET (gtk_builder_get_object (build, <span class="st">"clock"</span>));</span>
|
||||
<span id="cb4-142"><a href="#cb4-142"></a> g_object_unref(build);</span>
|
||||
<span id="cb4-143"><a href="#cb4-143"></a></span>
|
||||
<span id="cb4-144"><a href="#cb4-144"></a> gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA (clock), draw_clock, NULL, NULL);</span>
|
||||
<span id="cb4-145"><a href="#cb4-145"></a> g_timeout_add(<span class="dv">1000</span>, (GSourceFunc) time_handler, (gpointer) clock);</span>
|
||||
<span id="cb4-146"><a href="#cb4-146"></a> gtk_widget_show(win);</span>
|
||||
<span id="cb4-147"><a href="#cb4-147"></a></span>
|
||||
<span id="cb4-148"><a href="#cb4-148"></a>}</span>
|
||||
<span id="cb4-149"><a href="#cb4-149"></a></span>
|
||||
<span id="cb4-150"><a href="#cb4-150"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb4-151"><a href="#cb4-151"></a>app_open (GApplication *app, GFile **files, gint n_files, gchar *hint, gpointer user_data) {</span>
|
||||
<span id="cb4-152"><a href="#cb4-152"></a> app_activate(app,user_data);</span>
|
||||
<span id="cb4-153"><a href="#cb4-153"></a>}</span>
|
||||
<span id="cb4-154"><a href="#cb4-154"></a></span>
|
||||
<span id="cb4-155"><a href="#cb4-155"></a><span class="dt">int</span></span>
|
||||
<span id="cb4-156"><a href="#cb4-156"></a>main (<span class="dt">int</span> argc, <span class="dt">char</span> **argv) {</span>
|
||||
<span id="cb4-157"><a href="#cb4-157"></a> GtkApplication *app;</span>
|
||||
<span id="cb4-158"><a href="#cb4-158"></a> <span class="dt">int</span> stat;</span>
|
||||
<span id="cb4-159"><a href="#cb4-159"></a></span>
|
||||
<span id="cb4-160"><a href="#cb4-160"></a> app = gtk_application_new (<span class="st">"com.github.ToshioCP.tfc"</span>, G_APPLICATION_HANDLES_OPEN);</span>
|
||||
<span id="cb4-161"><a href="#cb4-161"></a> g_signal_connect (app, <span class="st">"activate"</span>, G_CALLBACK (app_activate), NULL);</span>
|
||||
<span id="cb4-162"><a href="#cb4-162"></a> g_signal_connect (app, <span class="st">"open"</span>, G_CALLBACK (app_open), NULL);</span>
|
||||
<span id="cb4-163"><a href="#cb4-163"></a> stat = g_application_run (G_APPLICATION (app), argc, argv);</span>
|
||||
<span id="cb4-164"><a href="#cb4-164"></a> g_object_unref (app);</span>
|
||||
<span id="cb4-165"><a href="#cb4-165"></a> <span class="cf">return</span> stat;</span>
|
||||
<span id="cb4-166"><a href="#cb4-166"></a>}</span></code></pre></div>
|
||||
<p><code>tfc.ui</code></p>
|
||||
<div class="sourceCode" id="cb5"><pre class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><span id="cb5-1"><a href="#cb5-1"></a><span class="kw"><?xml</span> version="1.0" encoding="UTF-8"<span class="kw">?></span></span>
|
||||
<span id="cb5-2"><a href="#cb5-2"></a><span class="kw"><interface></span></span>
|
||||
<span id="cb5-3"><a href="#cb5-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="cb5-4"><a href="#cb5-4"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"title"</span><span class="kw">></span>Clock<span class="kw"></property></span></span>
|
||||
<span id="cb5-5"><a href="#cb5-5"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"default-width"</span><span class="kw">></span>200<span class="kw"></property></span></span>
|
||||
<span id="cb5-6"><a href="#cb5-6"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"default-height"</span><span class="kw">></span>200<span class="kw"></property></span></span>
|
||||
<span id="cb5-7"><a href="#cb5-7"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb5-8"><a href="#cb5-8"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkDrawingArea"</span><span class="ot"> id=</span><span class="st">"clock"</span><span class="kw">></span></span>
|
||||
<span id="cb5-9"><a href="#cb5-9"></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="cb5-10"><a href="#cb5-10"></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="cb5-11"><a href="#cb5-11"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb5-12"><a href="#cb5-12"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb5-13"><a href="#cb5-13"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb5-14"><a href="#cb5-14"></a><span class="kw"></interface></span></span></code></pre></div>
|
||||
<p><code>tfc.gresource.xml</code></p>
|
||||
<div class="sourceCode" id="cb6"><pre class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><span id="cb6-1"><a href="#cb6-1"></a><span class="kw"><?xml</span> version="1.0" encoding="UTF-8"<span class="kw">?></span></span>
|
||||
<span id="cb6-2"><a href="#cb6-2"></a><span class="kw"><gresources></span></span>
|
||||
<span id="cb6-3"><a href="#cb6-3"></a> <span class="kw"><gresource</span><span class="ot"> prefix=</span><span class="st">"/com/github/ToshioCP/tfc"</span><span class="kw">></span></span>
|
||||
<span id="cb6-4"><a href="#cb6-4"></a> <span class="kw"><file></span>tfc.ui<span class="kw"></file></span></span>
|
||||
<span id="cb6-5"><a href="#cb6-5"></a> <span class="kw"></gresource></span></span>
|
||||
<span id="cb6-6"><a href="#cb6-6"></a><span class="kw"></gresources></span></span></code></pre></div>
|
||||
<p><code>comp</code></p>
|
||||
<div class="sourceCode" id="cb7"><pre class="sourceCode numberSource numberLines"><code class="sourceCode"><span id="cb7-1"><a href="#cb7-1"></a>glib-compile-resources $1.gresource.xml --target=$1.gresource.c --generate-source</span>
|
||||
<span id="cb7-2"><a href="#cb7-2"></a>gcc `pkg-config --cflags gtk4` $1.gresource.c $1.c `pkg-config --libs gtk4` -lm</span></code></pre></div>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec22.html">Section 22</a>, Next: <a href="sec24.html">Section 24</a></p>
|
||||
</body>
|
||||
</html>
|
378
docs/sec24.html
Normal file
378
docs/sec24.html
Normal file
|
@ -0,0 +1,378 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>Gtk4 tutorial for beginners</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
body {width: 1080px; margin: 0 auto; font-size: large;}
|
||||
h2 {padding: 10px; background-color: #d0f0d0; }
|
||||
div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
|
||||
th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
|
||||
td {padding: 2px 6px; border: 1px solid;}
|
||||
img {display: block; margin-left: auto; margin-right: auto;}
|
||||
figcaption {text-align: center;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec23.html">Section 23</a>, Next: <a href="sec25.html">Section 25</a></p>
|
||||
<h1 id="combine-gtkdrawingarea-and-tfetextview">Combine GtkDrawingArea and TfeTextView</h1>
|
||||
<p>Now, we will make a new application which has GtkDrawingArea and TfeTextView in it. Its name is “color”. If you write a name of a color in TfeTextView and click on the <code>run</code> button, then the color of GtkDrawingArea changes to the color given by you.</p>
|
||||
<figure>
|
||||
<img src="../image/color.png" alt="" /><figcaption>color</figcaption>
|
||||
</figure>
|
||||
<p>The following colors are available.</p>
|
||||
<ul>
|
||||
<li>white</li>
|
||||
<li>black</li>
|
||||
<li>red</li>
|
||||
<li>green</li>
|
||||
<li>blue</li>
|
||||
</ul>
|
||||
<p>In addition the following two options are also available.</p>
|
||||
<ul>
|
||||
<li>light: Make the color of the drawing area lighter.</li>
|
||||
<li>dark: Make the color of the drawing area darker.</li>
|
||||
</ul>
|
||||
<p>This application can only do very simple things. However, it tells us that if we add powerful parser to it, we will be able to make it more efficient. I want to show it to you in the later section by making a turtle graphics language like Logo program language.</p>
|
||||
<p>In this section, we focus on how to bind the two objects.</p>
|
||||
<h2 id="color.ui-and-color.gresource.xml">Color.ui and color.gresource.xml</h2>
|
||||
<p>First, We need to make the ui file of the widgets. The image in the previous subsection gives us the structure of the widgets. Title bar, four buttons in the tool bar and two widgets textview and drawing area. The ui file is as follows.</p>
|
||||
<div class="sourceCode" id="cb1"><pre class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><span id="cb1-1"><a href="#cb1-1"></a><span class="kw"><?xml</span> version="1.0" encoding="UTF-8"<span class="kw">?></span></span>
|
||||
<span id="cb1-2"><a href="#cb1-2"></a><span class="kw"><interface></span></span>
|
||||
<span id="cb1-3"><a href="#cb1-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="cb1-4"><a href="#cb1-4"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"title"</span><span class="kw">></span>color changer<span class="kw"></property></span></span>
|
||||
<span id="cb1-5"><a href="#cb1-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="cb1-6"><a href="#cb1-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="cb1-7"><a href="#cb1-7"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb1-8"><a href="#cb1-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="cb1-9"><a href="#cb1-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="cb1-10"><a href="#cb1-10"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb1-11"><a href="#cb1-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">"boxh1"</span><span class="kw">></span></span>
|
||||
<span id="cb1-12"><a href="#cb1-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="cb1-13"><a href="#cb1-13"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb1-14"><a href="#cb1-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="cb1-15"><a href="#cb1-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="cb1-16"><a href="#cb1-16"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb1-17"><a href="#cb1-17"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb1-18"><a href="#cb1-18"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb1-19"><a href="#cb1-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">"btnr"</span><span class="kw">></span></span>
|
||||
<span id="cb1-20"><a href="#cb1-20"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span>Run<span class="kw"></property></span></span>
|
||||
<span id="cb1-21"><a href="#cb1-21"></a> <span class="kw"><signal</span><span class="ot"> name=</span><span class="st">"clicked"</span><span class="ot"> handler=</span><span class="st">"run_cb"</span><span class="kw">></signal></span></span>
|
||||
<span id="cb1-22"><a href="#cb1-22"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb1-23"><a href="#cb1-23"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb1-24"><a href="#cb1-24"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb1-25"><a href="#cb1-25"></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="cb1-26"><a href="#cb1-26"></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="cb1-27"><a href="#cb1-27"></a> <span class="kw"><signal</span><span class="ot"> name=</span><span class="st">"clicked"</span><span class="ot"> handler=</span><span class="st">"open_cb"</span><span class="kw">></signal></span></span>
|
||||
<span id="cb1-28"><a href="#cb1-28"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb1-29"><a href="#cb1-29"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb1-30"><a href="#cb1-30"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb1-31"><a href="#cb1-31"></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="cb1-32"><a href="#cb1-32"></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="cb1-33"><a href="#cb1-33"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb1-34"><a href="#cb1-34"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb1-35"><a href="#cb1-35"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb1-36"><a href="#cb1-36"></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="cb1-37"><a href="#cb1-37"></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="cb1-38"><a href="#cb1-38"></a> <span class="kw"><signal</span><span class="ot"> name=</span><span class="st">"clicked"</span><span class="ot"> handler=</span><span class="st">"save_cb"</span><span class="kw">></signal></span></span>
|
||||
<span id="cb1-39"><a href="#cb1-39"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb1-40"><a href="#cb1-40"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb1-41"><a href="#cb1-41"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb1-42"><a href="#cb1-42"></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="cb1-43"><a href="#cb1-43"></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="cb1-44"><a href="#cb1-44"></a> <span class="kw"><signal</span><span class="ot"> name=</span><span class="st">"clicked"</span><span class="ot"> handler=</span><span class="st">"close_cb"</span><span class="kw">></signal></span></span>
|
||||
<span id="cb1-45"><a href="#cb1-45"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb1-46"><a href="#cb1-46"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb1-47"><a href="#cb1-47"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb1-48"><a href="#cb1-48"></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="cb1-49"><a href="#cb1-49"></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="cb1-50"><a href="#cb1-50"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb1-51"><a href="#cb1-51"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb1-52"><a href="#cb1-52"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb1-53"><a href="#cb1-53"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb1-54"><a href="#cb1-54"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb1-55"><a href="#cb1-55"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkBox"</span><span class="ot"> id=</span><span class="st">"boxh2"</span><span class="kw">></span></span>
|
||||
<span id="cb1-56"><a href="#cb1-56"></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="cb1-57"><a href="#cb1-57"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"homogeneous"</span><span class="kw">></span>TRUE<span class="kw"></property></span></span>
|
||||
<span id="cb1-58"><a href="#cb1-58"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb1-59"><a href="#cb1-59"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkScrolledWindow"</span><span class="ot"> id=</span><span class="st">"scr"</span><span class="kw">></span></span>
|
||||
<span id="cb1-60"><a href="#cb1-60"></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="cb1-61"><a href="#cb1-61"></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="cb1-62"><a href="#cb1-62"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb1-63"><a href="#cb1-63"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"TfeTextView"</span><span class="ot"> id=</span><span class="st">"tv"</span><span class="kw">></span></span>
|
||||
<span id="cb1-64"><a href="#cb1-64"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"wrap-mode"</span><span class="kw">></span>GTK_WRAP_WORD_CHAR<span class="kw"></property></span></span>
|
||||
<span id="cb1-65"><a href="#cb1-65"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb1-66"><a href="#cb1-66"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb1-67"><a href="#cb1-67"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb1-68"><a href="#cb1-68"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb1-69"><a href="#cb1-69"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb1-70"><a href="#cb1-70"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkDrawingArea"</span><span class="ot"> id=</span><span class="st">"da"</span><span class="kw">></span></span>
|
||||
<span id="cb1-71"><a href="#cb1-71"></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="cb1-72"><a href="#cb1-72"></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="cb1-73"><a href="#cb1-73"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb1-74"><a href="#cb1-74"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb1-75"><a href="#cb1-75"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb1-76"><a href="#cb1-76"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb1-77"><a href="#cb1-77"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb1-78"><a href="#cb1-78"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb1-79"><a href="#cb1-79"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb1-80"><a href="#cb1-80"></a><span class="kw"></interface></span></span></code></pre></div>
|
||||
<ul>
|
||||
<li>10-53: This part is the tool bar which has four buttons, <code>Run</code>, <code>Open</code>, <code>Save</code> and <code>Close</code>. This is similar to the toolbar of tfe text editor in <a href="sec9.html">Section 9</a>. There are two differences. <code>Run</code> button replaces <code>New</code> button. A signal element is added to each button object. It has “name” attribute which is a signal name and “handler” attribute which is the name of its signal handler function. Options “-WI, –export-dynamic” CFLAG is necessary when you compile the application. You can achieve this by adding “export_dynamic: true” argument to executable function in <code>meson.build</code>. And be careful that the handler must be defined without ‘static’ class.</li>
|
||||
<li>54-76: Puts GtkScrolledWindow and GtkDrawingArea into GtkBox. GtkBox has “homogeneous property” with TRUE value, so the two children have the same width in the box. TfeTextView is a child of GtkScrolledWindow.</li>
|
||||
</ul>
|
||||
<p>The xml file for the resource compiler is almost same as before. Just substitute “color” for “tfe”.</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"><gresources></span></span>
|
||||
<span id="cb2-3"><a href="#cb2-3"></a> <span class="kw"><gresource</span><span class="ot"> prefix=</span><span class="st">"/com/github/ToshioCP/color"</span><span class="kw">></span></span>
|
||||
<span id="cb2-4"><a href="#cb2-4"></a> <span class="kw"><file></span>color.ui<span class="kw"></file></span></span>
|
||||
<span id="cb2-5"><a href="#cb2-5"></a> <span class="kw"></gresource></span></span>
|
||||
<span id="cb2-6"><a href="#cb2-6"></a><span class="kw"></gresources></span></span></code></pre></div>
|
||||
<h2 id="tfetextview.h-tfetextview.c-and-color.h">Tfetextview.h, tfetextview.c and color.h</h2>
|
||||
<p>First two files are the same as before. Color.h just includes tfetextview.h.</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="pp">#include </span><span class="im">"../tfetextview/tfetextview.h"</span></span></code></pre></div>
|
||||
<h2 id="colorapplication.c">Colorapplication.c</h2>
|
||||
<p>This is the main file. It deals with:</p>
|
||||
<ul>
|
||||
<li>Building widgets by GtkBuilder.</li>
|
||||
<li>Setting a drawing function of GtkDrawingArea. And connecting a handler to “resize” signal on GtkDrawingArea.</li>
|
||||
<li>Implementing each call back functions. Particularly, <code>Run</code> signal handler is the point in this program.</li>
|
||||
</ul>
|
||||
<p>The following is <code>colorapplication.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="pp">#include </span><span class="im">"color.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> GtkWidget *win;</span>
|
||||
<span id="cb4-4"><a href="#cb4-4"></a><span class="dt">static</span> GtkWidget *tv;</span>
|
||||
<span id="cb4-5"><a href="#cb4-5"></a><span class="dt">static</span> GtkWidget *da;</span>
|
||||
<span id="cb4-6"><a href="#cb4-6"></a></span>
|
||||
<span id="cb4-7"><a href="#cb4-7"></a><span class="dt">static</span> cairo_surface_t *surface = NULL;</span>
|
||||
<span id="cb4-8"><a href="#cb4-8"></a></span>
|
||||
<span id="cb4-9"><a href="#cb4-9"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb4-10"><a href="#cb4-10"></a>run (<span class="dt">void</span>) {</span>
|
||||
<span id="cb4-11"><a href="#cb4-11"></a> GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));</span>
|
||||
<span id="cb4-12"><a href="#cb4-12"></a> GtkTextIter start_iter;</span>
|
||||
<span id="cb4-13"><a href="#cb4-13"></a> GtkTextIter end_iter;</span>
|
||||
<span id="cb4-14"><a href="#cb4-14"></a> <span class="dt">char</span> *contents;</span>
|
||||
<span id="cb4-15"><a href="#cb4-15"></a> cairo_t *cr;</span>
|
||||
<span id="cb4-16"><a href="#cb4-16"></a></span>
|
||||
<span id="cb4-17"><a href="#cb4-17"></a> gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter);</span>
|
||||
<span id="cb4-18"><a href="#cb4-18"></a> contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE);</span>
|
||||
<span id="cb4-19"><a href="#cb4-19"></a> <span class="cf">if</span> (surface) {</span>
|
||||
<span id="cb4-20"><a href="#cb4-20"></a> cr = cairo_create (surface);</span>
|
||||
<span id="cb4-21"><a href="#cb4-21"></a> <span class="cf">if</span> (g_strcmp0 (<span class="st">"red"</span>, contents) == <span class="dv">0</span>)</span>
|
||||
<span id="cb4-22"><a href="#cb4-22"></a> cairo_set_source_rgb (cr, <span class="dv">1</span>, <span class="dv">0</span>, <span class="dv">0</span>);</span>
|
||||
<span id="cb4-23"><a href="#cb4-23"></a> <span class="cf">else</span> <span class="cf">if</span> (g_strcmp0 (<span class="st">"green"</span>, contents) == <span class="dv">0</span>)</span>
|
||||
<span id="cb4-24"><a href="#cb4-24"></a> cairo_set_source_rgb (cr, <span class="dv">0</span>, <span class="dv">1</span>, <span class="dv">0</span>);</span>
|
||||
<span id="cb4-25"><a href="#cb4-25"></a> <span class="cf">else</span> <span class="cf">if</span> (g_strcmp0 (<span class="st">"blue"</span>, contents) == <span class="dv">0</span>)</span>
|
||||
<span id="cb4-26"><a href="#cb4-26"></a> cairo_set_source_rgb (cr, <span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">1</span>);</span>
|
||||
<span id="cb4-27"><a href="#cb4-27"></a> <span class="cf">else</span> <span class="cf">if</span> (g_strcmp0 (<span class="st">"white"</span>, contents) == <span class="dv">0</span>)</span>
|
||||
<span id="cb4-28"><a href="#cb4-28"></a> cairo_set_source_rgb (cr, <span class="dv">1</span>, <span class="dv">1</span>, <span class="dv">1</span>);</span>
|
||||
<span id="cb4-29"><a href="#cb4-29"></a> <span class="cf">else</span> <span class="cf">if</span> (g_strcmp0 (<span class="st">"black"</span>, contents) == <span class="dv">0</span>)</span>
|
||||
<span id="cb4-30"><a href="#cb4-30"></a> cairo_set_source_rgb (cr, <span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">0</span>);</span>
|
||||
<span id="cb4-31"><a href="#cb4-31"></a> <span class="cf">else</span> <span class="cf">if</span> (g_strcmp0 (<span class="st">"light"</span>, contents) == <span class="dv">0</span>)</span>
|
||||
<span id="cb4-32"><a href="#cb4-32"></a> cairo_set_source_rgba (cr, <span class="dv">1</span>, <span class="dv">1</span>, <span class="dv">1</span>, <span class="fl">0.5</span>);</span>
|
||||
<span id="cb4-33"><a href="#cb4-33"></a> <span class="cf">else</span> <span class="cf">if</span> (g_strcmp0 (<span class="st">"dark"</span>, contents) == <span class="dv">0</span>)</span>
|
||||
<span id="cb4-34"><a href="#cb4-34"></a> cairo_set_source_rgba (cr, <span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">0</span>, <span class="fl">0.5</span>);</span>
|
||||
<span id="cb4-35"><a href="#cb4-35"></a> <span class="cf">else</span></span>
|
||||
<span id="cb4-36"><a href="#cb4-36"></a> cairo_set_source_surface (cr, surface, <span class="dv">0</span>, <span class="dv">0</span>);</span>
|
||||
<span id="cb4-37"><a href="#cb4-37"></a> cairo_paint (cr);</span>
|
||||
<span id="cb4-38"><a href="#cb4-38"></a> cairo_destroy (cr);</span>
|
||||
<span id="cb4-39"><a href="#cb4-39"></a> }</span>
|
||||
<span id="cb4-40"><a href="#cb4-40"></a> g_free (contents);</span>
|
||||
<span id="cb4-41"><a href="#cb4-41"></a>}</span>
|
||||
<span id="cb4-42"><a href="#cb4-42"></a></span>
|
||||
<span id="cb4-43"><a href="#cb4-43"></a><span class="dt">void</span></span>
|
||||
<span id="cb4-44"><a href="#cb4-44"></a>run_cb (GtkWidget *btnr) {</span>
|
||||
<span id="cb4-45"><a href="#cb4-45"></a> run ();</span>
|
||||
<span id="cb4-46"><a href="#cb4-46"></a> gtk_widget_queue_draw (GTK_WIDGET (da));</span>
|
||||
<span id="cb4-47"><a href="#cb4-47"></a>}</span>
|
||||
<span id="cb4-48"><a href="#cb4-48"></a></span>
|
||||
<span id="cb4-49"><a href="#cb4-49"></a><span class="dt">void</span></span>
|
||||
<span id="cb4-50"><a href="#cb4-50"></a>open_cb (GtkWidget *btno) {</span>
|
||||
<span id="cb4-51"><a href="#cb4-51"></a> tfe_text_view_open (TFE_TEXT_VIEW (tv), GTK_WINDOW (win));</span>
|
||||
<span id="cb4-52"><a href="#cb4-52"></a>}</span>
|
||||
<span id="cb4-53"><a href="#cb4-53"></a></span>
|
||||
<span id="cb4-54"><a href="#cb4-54"></a><span class="dt">void</span></span>
|
||||
<span id="cb4-55"><a href="#cb4-55"></a>save_cb (GtkWidget *btns) {</span>
|
||||
<span id="cb4-56"><a href="#cb4-56"></a> tfe_text_view_save (TFE_TEXT_VIEW (tv));</span>
|
||||
<span id="cb4-57"><a href="#cb4-57"></a>}</span>
|
||||
<span id="cb4-58"><a href="#cb4-58"></a></span>
|
||||
<span id="cb4-59"><a href="#cb4-59"></a><span class="dt">void</span></span>
|
||||
<span id="cb4-60"><a href="#cb4-60"></a>close_cb (GtkWidget *btnc) {</span>
|
||||
<span id="cb4-61"><a href="#cb4-61"></a> <span class="cf">if</span> (surface)</span>
|
||||
<span id="cb4-62"><a href="#cb4-62"></a> cairo_surface_destroy (surface);</span>
|
||||
<span id="cb4-63"><a href="#cb4-63"></a> gtk_window_destroy (GTK_WINDOW (win));</span>
|
||||
<span id="cb4-64"><a href="#cb4-64"></a>}</span>
|
||||
<span id="cb4-65"><a href="#cb4-65"></a></span>
|
||||
<span id="cb4-66"><a href="#cb4-66"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb4-67"><a href="#cb4-67"></a>resize_cb (GtkDrawingArea *drawing_area, <span class="dt">int</span> width, <span class="dt">int</span> height, gpointer user_data) {</span>
|
||||
<span id="cb4-68"><a href="#cb4-68"></a> <span class="cf">if</span> (surface)</span>
|
||||
<span id="cb4-69"><a href="#cb4-69"></a> cairo_surface_destroy (surface);</span>
|
||||
<span id="cb4-70"><a href="#cb4-70"></a> surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);</span>
|
||||
<span id="cb4-71"><a href="#cb4-71"></a> run ();</span>
|
||||
<span id="cb4-72"><a href="#cb4-72"></a>}</span>
|
||||
<span id="cb4-73"><a href="#cb4-73"></a></span>
|
||||
<span id="cb4-74"><a href="#cb4-74"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb4-75"><a href="#cb4-75"></a>draw_func (GtkDrawingArea *drawing_area, cairo_t *cr, <span class="dt">int</span> width, <span class="dt">int</span> height, gpointer user_data) {</span>
|
||||
<span id="cb4-76"><a href="#cb4-76"></a> <span class="cf">if</span> (surface) {</span>
|
||||
<span id="cb4-77"><a href="#cb4-77"></a> cairo_set_source_surface (cr, surface, <span class="dv">0</span>, <span class="dv">0</span>);</span>
|
||||
<span id="cb4-78"><a href="#cb4-78"></a> cairo_paint (cr);</span>
|
||||
<span id="cb4-79"><a href="#cb4-79"></a> }</span>
|
||||
<span id="cb4-80"><a href="#cb4-80"></a>}</span>
|
||||
<span id="cb4-81"><a href="#cb4-81"></a></span>
|
||||
<span id="cb4-82"><a href="#cb4-82"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb4-83"><a href="#cb4-83"></a>app_activate (GApplication *application) {</span>
|
||||
<span id="cb4-84"><a href="#cb4-84"></a> gtk_widget_show (win);</span>
|
||||
<span id="cb4-85"><a href="#cb4-85"></a>}</span>
|
||||
<span id="cb4-86"><a href="#cb4-86"></a></span>
|
||||
<span id="cb4-87"><a href="#cb4-87"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb4-88"><a href="#cb4-88"></a>app_startup (GApplication *application) {</span>
|
||||
<span id="cb4-89"><a href="#cb4-89"></a> GtkApplication *app = GTK_APPLICATION (application);</span>
|
||||
<span id="cb4-90"><a href="#cb4-90"></a> GtkBuilder *build;</span>
|
||||
<span id="cb4-91"><a href="#cb4-91"></a></span>
|
||||
<span id="cb4-92"><a href="#cb4-92"></a> build = gtk_builder_new_from_resource (<span class="st">"/com/github/ToshioCP/color/color.ui"</span>);</span>
|
||||
<span id="cb4-93"><a href="#cb4-93"></a> win = GTK_WIDGET (gtk_builder_get_object (build, <span class="st">"win"</span>));</span>
|
||||
<span id="cb4-94"><a href="#cb4-94"></a> gtk_window_set_application (GTK_WINDOW (win), app);</span>
|
||||
<span id="cb4-95"><a href="#cb4-95"></a> tv = GTK_WIDGET (gtk_builder_get_object (build, <span class="st">"tv"</span>));</span>
|
||||
<span id="cb4-96"><a href="#cb4-96"></a> da = GTK_WIDGET (gtk_builder_get_object (build, <span class="st">"da"</span>));</span>
|
||||
<span id="cb4-97"><a href="#cb4-97"></a> g_object_unref(build);</span>
|
||||
<span id="cb4-98"><a href="#cb4-98"></a> g_signal_connect (GTK_DRAWING_AREA (da), <span class="st">"resize"</span>, G_CALLBACK (resize_cb), NULL);</span>
|
||||
<span id="cb4-99"><a href="#cb4-99"></a> gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (da), draw_func, NULL, NULL);</span>
|
||||
<span id="cb4-100"><a href="#cb4-100"></a></span>
|
||||
<span id="cb4-101"><a href="#cb4-101"></a>GdkDisplay *display;</span>
|
||||
<span id="cb4-102"><a href="#cb4-102"></a></span>
|
||||
<span id="cb4-103"><a href="#cb4-103"></a> display = gtk_widget_get_display (GTK_WIDGET (win));</span>
|
||||
<span id="cb4-104"><a href="#cb4-104"></a> GtkCssProvider *provider = gtk_css_provider_new ();</span>
|
||||
<span id="cb4-105"><a href="#cb4-105"></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="cb4-106"><a href="#cb4-106"></a> gtk_style_context_add_provider_for_display (display, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_USER);</span>
|
||||
<span id="cb4-107"><a href="#cb4-107"></a>}</span>
|
||||
<span id="cb4-108"><a href="#cb4-108"></a></span>
|
||||
<span id="cb4-109"><a href="#cb4-109"></a><span class="pp">#define APPLICATION_ID "com.github.ToshioCP.color"</span></span>
|
||||
<span id="cb4-110"><a href="#cb4-110"></a></span>
|
||||
<span id="cb4-111"><a href="#cb4-111"></a><span class="dt">int</span></span>
|
||||
<span id="cb4-112"><a href="#cb4-112"></a>main (<span class="dt">int</span> argc, <span class="dt">char</span> **argv) {</span>
|
||||
<span id="cb4-113"><a href="#cb4-113"></a> GtkApplication *app;</span>
|
||||
<span id="cb4-114"><a href="#cb4-114"></a> <span class="dt">int</span> stat;</span>
|
||||
<span id="cb4-115"><a href="#cb4-115"></a></span>
|
||||
<span id="cb4-116"><a href="#cb4-116"></a> app = gtk_application_new (APPLICATION_ID, G_APPLICATION_FLAGS_NONE);</span>
|
||||
<span id="cb4-117"><a href="#cb4-117"></a></span>
|
||||
<span id="cb4-118"><a href="#cb4-118"></a> g_signal_connect (app, <span class="st">"startup"</span>, G_CALLBACK (app_startup), NULL);</span>
|
||||
<span id="cb4-119"><a href="#cb4-119"></a> g_signal_connect (app, <span class="st">"activate"</span>, G_CALLBACK (app_activate), NULL);</span>
|
||||
<span id="cb4-120"><a href="#cb4-120"></a></span>
|
||||
<span id="cb4-121"><a href="#cb4-121"></a> stat =g_application_run (G_APPLICATION (app), argc, argv);</span>
|
||||
<span id="cb4-122"><a href="#cb4-122"></a> g_object_unref (app);</span>
|
||||
<span id="cb4-123"><a href="#cb4-123"></a> <span class="cf">return</span> stat;</span>
|
||||
<span id="cb4-124"><a href="#cb4-124"></a>}</span></code></pre></div>
|
||||
<ul>
|
||||
<li>109-124: The function <code>main</code> is almost same as before but there are some differences. The application ID is “com.github.ToshioCP.color”. <code>G_APPLICATION_FLAGS_NONE</code> is specified so no open signal handler is necessary.</li>
|
||||
<li>87-107: Startup handler.</li>
|
||||
<li>92-97: Builds widgets. The pointers of the top window, TfeTextView and GtkDrawingArea objects are stored to static variables <code>win</code>, <code>tv</code> and <code>da</code> respectively. This is because these objects are often used in handlers. They never be rewritten so they’re thread safe.</li>
|
||||
<li>98: connects “resize” signal and the handler.</li>
|
||||
<li>99: sets the drawing function.</li>
|
||||
<li>82-85: Activate handler, which just shows the widgets.</li>
|
||||
<li>74-80: The drawing function. It just copies <code>surface</code> to destination.</li>
|
||||
<li>66-72: Resize handler. Re-creates the surface to fit its width and height for the drawing area and paints by calling the function <code>run</code>.</li>
|
||||
<li>59-64: Close handler. It destroys <code>surface</code> if it exists. Then it destroys the top-level window and quits the application.</li>
|
||||
<li>49-57: Open and save handler. They just call the corresponding functions of TfeTextView.</li>
|
||||
<li>43-47: Run handler. It calls run function to paint the surface. After that <code>gtk_widget_queue_draw</code> is called. This function adds the widget (GtkDrawingArea) to the queue to be redrawn. It is important to know that the window is redrawn whenever it is necessary. For example, when another window is moved and uncovers part of the widget, or when the window containing it is resized. But repainting <code>surface</code> is not automatically notified to gtk. Therefore, you need to call <code>gtk_widget_queue_draw</code> to redraw the widget.</li>
|
||||
<li>9-41: Run function paints the surface. First, it gets the contents of GtkTextBuffer. Then it compares it to “red”, “green” and so on. If it matches the color, then the surface is painted the color. If it matches “light” or “dark”, then the color of the surface is lightened or darkened respectively. Alpha channel is used.</li>
|
||||
</ul>
|
||||
<h2 id="meson.build">Meson.build</h2>
|
||||
<p>This file is almost same as before. An argument “export_dynamic: true” is added to executable function.</p>
|
||||
<div class="sourceCode" id="cb5"><pre class="sourceCode numberSource numberLines"><code class="sourceCode"><span id="cb5-1"><a href="#cb5-1"></a>project('color', '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','color.gresource.xml')</span>
|
||||
<span id="cb5-7"><a href="#cb5-7"></a></span>
|
||||
<span id="cb5-8"><a href="#cb5-8"></a>sourcefiles=files('colorapplication.c', '../tfetextview/tfetextview.c')</span>
|
||||
<span id="cb5-9"><a href="#cb5-9"></a></span>
|
||||
<span id="cb5-10"><a href="#cb5-10"></a>executable('color', sourcefiles, resources, dependencies: gtkdep, export_dynamic: true)</span></code></pre></div>
|
||||
<h2 id="compile-and-execute-it">Compile and execute it</h2>
|
||||
<p>First you need to export some variables (refer to <a href="sec2.html">Section 2</a>) if you’ve installed Gtk4 from the source. If you’ve installed Gtk4 from the distribution packages, you don’t need to do this.</p>
|
||||
<pre><code>$ . env.sh</code></pre>
|
||||
<p>Then type the following to compile it.</p>
|
||||
<pre><code>$ meson _build
|
||||
$ ninja -C _build</code></pre>
|
||||
<p>The application is made in <code>_build</code> directory. Type the following to execute it.</p>
|
||||
<pre><code>$ _build/color</code></pre>
|
||||
<p>Type “red”, “green”, “blue”, “white”, black“,”light" or “dark” in the TfeTextView. Then, click on <code>Run</code> button. Make sure the color of GtkDrawingArea changes.</p>
|
||||
<p>In this program TfeTextView is used to change the color. You can use buttons or menus instead of textview. Probably it is more appropriate. Using textview is unnatural. It is a good practice to make such application by yourself.</p>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec23.html">Section 23</a>, Next: <a href="sec25.html">Section 25</a></p>
|
||||
</body>
|
||||
</html>
|
1996
docs/sec25.html
Normal file
1996
docs/sec25.html
Normal file
File diff suppressed because it is too large
Load diff
481
docs/sec26.html
Normal file
481
docs/sec26.html
Normal file
|
@ -0,0 +1,481 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>Gtk4 tutorial for beginners</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
body {width: 1080px; margin: 0 auto; font-size: large;}
|
||||
h2 {padding: 10px; background-color: #d0f0d0; }
|
||||
div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
|
||||
th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
|
||||
td {padding: 2px 6px; border: 1px solid;}
|
||||
img {display: block; margin-left: auto; margin-right: auto;}
|
||||
figcaption {text-align: center;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec25.html">Section 25</a>, Next: <a href="sec27.html">Section 27</a></p>
|
||||
<h1 id="gtklistview">GtkListView</h1>
|
||||
<p>Gtk4 has added new list objects GtkListView, GtkGridView and GtkColumnView. The new feature is described in <a href="https://docs.gtk.org/gtk4/section-list-widget.html">Gtk API Reference, List Widget Overview</a>.</p>
|
||||
<p>Gtk4 has other means to implement lists. They are GtkListBox and GtkTreeView which are took over from Gtk3. There’s an article in <a href="https://blog.gtk.org/2020/06/07/scalable-lists-in-gtk-4/">Gtk Development blog</a> about list widgets by Matthias Clasen. He described why GtkListView are developed to replace GtkListBox and GtkTreeView.</p>
|
||||
<p>I want to explain GtkListView and its related objects in this tutorial.</p>
|
||||
<h2 id="outline">Outline</h2>
|
||||
<p>A list is a sequential data structure. For example, an ordered string sequence “one”, “two”, “three”, “four” is a list. Each element of the list is called item. A list is like an array, but in many cases it is implemented with pointers which point to the next item of the list. And it has a start point. So, each item can be referred by the index of the item (first item, second item, …, nth item, …). There are two cases. One is the index starts from one (one-based) and the other is it starts from zero (zero-based).</p>
|
||||
<p>Gio provides GListModel interface. It is a zero-based list of the same type of GObject objects, or objects that implement the same interface. An object implements GListModel is usually not a widget. So, the list is not displayed on the screen directly. There’s another object GtkListView which is a widget to display the list. The items in the list need to be connected to the items in GtkListView. GtkListItemFactory object maps items in the list to GListView.</p>
|
||||
<figure>
|
||||
<img src="../image/list.png" alt="" /><figcaption>List</figcaption>
|
||||
</figure>
|
||||
<p>The instruction to build the whole list related objects is:</p>
|
||||
<ol type="1">
|
||||
<li>Implement the list object which implements GListModel.</li>
|
||||
<li>Build widgets and put GtkListView as a child of GtkScrolledWindow.</li>
|
||||
<li>Set GtkListItemFactory.</li>
|
||||
</ol>
|
||||
<h2 id="glistmodel">GListModel</h2>
|
||||
<p>If you want to make a list of strings with GListModel, for example, “one”, “two”, “three”, “four”, note that strings can’t be items of the list. Because GListModel is a list of GObject objects and strings aren’t GObject objects. So, you need a wrapper which is a GObject and contains a string. GtkStringObject is the wrapper object and GStringList, implements GListModel, is a list of GtkStringObject.</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="dt">char</span> *array[] = {<span class="st">"one"</span>, <span class="st">"two"</span>, <span class="st">"three"</span>, <span class="st">"four"</span>, NULL};</span>
|
||||
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true"></a>GtkStringList *stringlist = gtk_string_list_new ((<span class="dt">const</span> <span class="dt">char</span> * <span class="dt">const</span> *) array);</span></code></pre></div>
|
||||
<p>The function <code>gtk_string_list_new</code> creates GtkStringList object. Its items are GtkStringObject objects which contain the strings “one”, “two”, “three” and “four”. There are functions to add items to the list or remove items from the list.</p>
|
||||
<ul>
|
||||
<li><code>gtk_string_list_append</code> appends an item to the list</li>
|
||||
<li><code>gtk_string_list_remove</code> removes an item from the list</li>
|
||||
<li><code>gtk_string_list_get_string</code> gets a string in the list</li>
|
||||
</ul>
|
||||
<p>See <a href="https://docs.gtk.org/gtk4/class.StringList.html">Gtk4 API Reference, GtkStringList</a> for further information.</p>
|
||||
<p>I’ll explain the other list objects later.</p>
|
||||
<h2 id="gtkselectionmodel">GtkSelectionModel</h2>
|
||||
<p>GtkSelectionModel is an interface to support for selections. Thanks to this model, user can select items by clicking on them. It is implemented by GtkMultiSelection, GtkNoSelection and GtkSingleSelection objects. These three objects are usually enough to build an application. They are created with GListModel. You can also create them alone and add GListModel later.</p>
|
||||
<ul>
|
||||
<li>GtkMultiSelection supports multiple selection.</li>
|
||||
<li>GtkNoSelection supports no selection. This is a wrapper to GListModel when GtkSelectionModel is needed.</li>
|
||||
<li>GtkSingleSelection supports single selection.</li>
|
||||
</ul>
|
||||
<h2 id="gtklistview-1">GtkListView</h2>
|
||||
<p>GtkListView is a widget to show GListModel items. GtkListItem is used by GtkListView to represent items of a list model. But, GtkListItem itself is not a widget, so a user needs to set a widget, for example GtkLabel, as a child of GtkListItem to display an item of the list model. “item” property of GtkListItem points an object that belongs to the list model.</p>
|
||||
<figure>
|
||||
<img src="../image/gtklistitem.png" alt="" /><figcaption>GtkListItem</figcaption>
|
||||
</figure>
|
||||
<p>In case the number of items is very big, for example more than a thousand, GtkListItem is recycled and connected to another item which is newly displayed. This recycle makes the number of GtkListItem objects fairly small, less than 200. This is very effective to restrain the growth of memory consumption so that GListModel can contain lots of items, for example, more than a million items.</p>
|
||||
<h2 id="gtklistitemfactory">GtkListItemFactory</h2>
|
||||
<p>GtkListItemFactory creates or recycles GtkListItem and connects it with an item of the list model. There are two child objects of this factory, GtkSignalListItemFactory and GtkBuilderListItemFactory.</p>
|
||||
<h3 id="gtksignallistitemfactory">GtkSignalListItemFactory</h3>
|
||||
<p>GtkSignalListItemFactory provides signals for users to configure a GtkListItem object. There are four signals.</p>
|
||||
<ol type="1">
|
||||
<li>“setup” is emitted to set up GtkListItem object. A user sets its child widget in the handler. For example, creates a GtkLabel widget and sets the child property of GtkListItem to it. This setting is kept even the GtkListItem instance is recycled (to bind to another item of GListModel).</li>
|
||||
<li>“bind” is emitted to bind an item in the list model to the widget. For example, a user gets the item from “item” property of the GtkListItem instance. Then gets the string of the item and sets the label property of the GtkLabel instance with the string. This signal is emitted when the GtkListItem is newly created, recycled or some changes has happened to the item of the list.</li>
|
||||
<li>“unbind” is emitted to unbind an item. A user undoes everything done in step 2 in the signal handler. If some object are created in step 2, they must be destroyed.</li>
|
||||
<li>“teardown” is emitted to undo everything done in step 1. So, the widget created in step 1 must be destroyed. After this signal, the list item will be destroyed.</li>
|
||||
</ol>
|
||||
<p>The following program <code>list1.c</code> shows the list of strings “one”, “two”, “three” and “four”. GtkNoSelection is used, so user can’t select any item.</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>
|
||||
<span id="cb2-2"><a href="#cb2-2"></a></span>
|
||||
<span id="cb2-3"><a href="#cb2-3"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb2-4"><a href="#cb2-4"></a>setup_cb (GtkListItemFactory *factory, GtkListItem *listitem, gpointer user_data) {</span>
|
||||
<span id="cb2-5"><a href="#cb2-5"></a> GtkWidget *lb = gtk_label_new (NULL);</span>
|
||||
<span id="cb2-6"><a href="#cb2-6"></a> gtk_list_item_set_child (listitem, lb);</span>
|
||||
<span id="cb2-7"><a href="#cb2-7"></a>}</span>
|
||||
<span id="cb2-8"><a href="#cb2-8"></a></span>
|
||||
<span id="cb2-9"><a href="#cb2-9"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb2-10"><a href="#cb2-10"></a>bind_cb (GtkSignalListItemFactory *self, GtkListItem *listitem, gpointer user_data) {</span>
|
||||
<span id="cb2-11"><a href="#cb2-11"></a> GtkWidget *lb = gtk_list_item_get_child (listitem);</span>
|
||||
<span id="cb2-12"><a href="#cb2-12"></a> GtkStringObject *strobj = gtk_list_item_get_item (listitem);</span>
|
||||
<span id="cb2-13"><a href="#cb2-13"></a> <span class="dt">const</span> <span class="dt">char</span> *text = gtk_string_object_get_string (strobj);</span>
|
||||
<span id="cb2-14"><a href="#cb2-14"></a></span>
|
||||
<span id="cb2-15"><a href="#cb2-15"></a> gtk_label_set_text (GTK_LABEL (lb), text);</span>
|
||||
<span id="cb2-16"><a href="#cb2-16"></a>}</span>
|
||||
<span id="cb2-17"><a href="#cb2-17"></a></span>
|
||||
<span id="cb2-18"><a href="#cb2-18"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb2-19"><a href="#cb2-19"></a>unbind_cb (GtkSignalListItemFactory *self, GtkListItem *listitem, gpointer user_data) {</span>
|
||||
<span id="cb2-20"><a href="#cb2-20"></a> <span class="co">/* There's nothing to do here. */</span></span>
|
||||
<span id="cb2-21"><a href="#cb2-21"></a> <span class="co">/* If you does something like setting a signal in bind_cb, */</span></span>
|
||||
<span id="cb2-22"><a href="#cb2-22"></a> <span class="co">/* then disconnecting the signal is necessary in unbind_cb. */</span></span>
|
||||
<span id="cb2-23"><a href="#cb2-23"></a>}</span>
|
||||
<span id="cb2-24"><a href="#cb2-24"></a></span>
|
||||
<span id="cb2-25"><a href="#cb2-25"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb2-26"><a href="#cb2-26"></a>teardown_cb (GtkListItemFactory *factory, GtkListItem *listitem, gpointer user_data) {</span>
|
||||
<span id="cb2-27"><a href="#cb2-27"></a> gtk_list_item_set_child (listitem, NULL);</span>
|
||||
<span id="cb2-28"><a href="#cb2-28"></a><span class="co">/* When the child of listitem is set to NULL, the reference to GtkLabel will be released and lb will be destroyed. */</span></span>
|
||||
<span id="cb2-29"><a href="#cb2-29"></a><span class="co">/* Therefore, g_object_unref () for the GtkLabel object doesn't need in the user code. */</span></span>
|
||||
<span id="cb2-30"><a href="#cb2-30"></a>}</span>
|
||||
<span id="cb2-31"><a href="#cb2-31"></a></span>
|
||||
<span id="cb2-32"><a href="#cb2-32"></a><span class="co">/* ----- activate, open, startup handlers ----- */</span></span>
|
||||
<span id="cb2-33"><a href="#cb2-33"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb2-34"><a href="#cb2-34"></a>app_activate (GApplication *application) {</span>
|
||||
<span id="cb2-35"><a href="#cb2-35"></a> GtkApplication *app = GTK_APPLICATION (application);</span>
|
||||
<span id="cb2-36"><a href="#cb2-36"></a> GtkWidget *win = gtk_application_window_new (app);</span>
|
||||
<span id="cb2-37"><a href="#cb2-37"></a> gtk_window_set_default_size (GTK_WINDOW (win), <span class="dv">600</span>, <span class="dv">400</span>);</span>
|
||||
<span id="cb2-38"><a href="#cb2-38"></a> GtkWidget *scr = gtk_scrolled_window_new ();</span>
|
||||
<span id="cb2-39"><a href="#cb2-39"></a> gtk_window_set_child (GTK_WINDOW (win), scr);</span>
|
||||
<span id="cb2-40"><a href="#cb2-40"></a></span>
|
||||
<span id="cb2-41"><a href="#cb2-41"></a> <span class="dt">char</span> *array[] = {</span>
|
||||
<span id="cb2-42"><a href="#cb2-42"></a> <span class="st">"one"</span>, <span class="st">"two"</span>, <span class="st">"three"</span>, <span class="st">"four"</span>, NULL</span>
|
||||
<span id="cb2-43"><a href="#cb2-43"></a> };</span>
|
||||
<span id="cb2-44"><a href="#cb2-44"></a> GtkStringList *sl = gtk_string_list_new ((<span class="dt">const</span> <span class="dt">char</span> * <span class="dt">const</span> *) array);</span>
|
||||
<span id="cb2-45"><a href="#cb2-45"></a> GtkNoSelection *ns = gtk_no_selection_new (G_LIST_MODEL (sl));</span>
|
||||
<span id="cb2-46"><a href="#cb2-46"></a></span>
|
||||
<span id="cb2-47"><a href="#cb2-47"></a> GtkListItemFactory *factory = gtk_signal_list_item_factory_new ();</span>
|
||||
<span id="cb2-48"><a href="#cb2-48"></a> g_signal_connect (factory, <span class="st">"setup"</span>, G_CALLBACK (setup_cb), NULL);</span>
|
||||
<span id="cb2-49"><a href="#cb2-49"></a> g_signal_connect (factory, <span class="st">"bind"</span>, G_CALLBACK (bind_cb), NULL);</span>
|
||||
<span id="cb2-50"><a href="#cb2-50"></a> g_signal_connect (factory, <span class="st">"unbind"</span>, G_CALLBACK (unbind_cb), NULL);</span>
|
||||
<span id="cb2-51"><a href="#cb2-51"></a> g_signal_connect (factory, <span class="st">"teardown"</span>, G_CALLBACK (teardown_cb), NULL);</span>
|
||||
<span id="cb2-52"><a href="#cb2-52"></a></span>
|
||||
<span id="cb2-53"><a href="#cb2-53"></a> GtkWidget *lv = gtk_list_view_new (GTK_SELECTION_MODEL (ns), factory);</span>
|
||||
<span id="cb2-54"><a href="#cb2-54"></a> gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), lv);</span>
|
||||
<span id="cb2-55"><a href="#cb2-55"></a> gtk_widget_show (win);</span>
|
||||
<span id="cb2-56"><a href="#cb2-56"></a>}</span>
|
||||
<span id="cb2-57"><a href="#cb2-57"></a></span>
|
||||
<span id="cb2-58"><a href="#cb2-58"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb2-59"><a href="#cb2-59"></a>app_startup (GApplication *application) {</span>
|
||||
<span id="cb2-60"><a href="#cb2-60"></a>}</span>
|
||||
<span id="cb2-61"><a href="#cb2-61"></a></span>
|
||||
<span id="cb2-62"><a href="#cb2-62"></a><span class="co">/* ----- main ----- */</span></span>
|
||||
<span id="cb2-63"><a href="#cb2-63"></a><span class="pp">#define APPLICATION_ID "com.github.ToshioCP.list1"</span></span>
|
||||
<span id="cb2-64"><a href="#cb2-64"></a></span>
|
||||
<span id="cb2-65"><a href="#cb2-65"></a><span class="dt">int</span></span>
|
||||
<span id="cb2-66"><a href="#cb2-66"></a>main (<span class="dt">int</span> argc, <span class="dt">char</span> **argv) {</span>
|
||||
<span id="cb2-67"><a href="#cb2-67"></a> GtkApplication *app;</span>
|
||||
<span id="cb2-68"><a href="#cb2-68"></a> <span class="dt">int</span> stat;</span>
|
||||
<span id="cb2-69"><a href="#cb2-69"></a></span>
|
||||
<span id="cb2-70"><a href="#cb2-70"></a> app = gtk_application_new (APPLICATION_ID, G_APPLICATION_FLAGS_NONE);</span>
|
||||
<span id="cb2-71"><a href="#cb2-71"></a></span>
|
||||
<span id="cb2-72"><a href="#cb2-72"></a> g_signal_connect (app, <span class="st">"startup"</span>, G_CALLBACK (app_startup), NULL);</span>
|
||||
<span id="cb2-73"><a href="#cb2-73"></a> g_signal_connect (app, <span class="st">"activate"</span>, G_CALLBACK (app_activate), NULL);</span>
|
||||
<span id="cb2-74"><a href="#cb2-74"></a></span>
|
||||
<span id="cb2-75"><a href="#cb2-75"></a> stat =g_application_run (G_APPLICATION (app), argc, argv);</span>
|
||||
<span id="cb2-76"><a href="#cb2-76"></a> g_object_unref (app);</span>
|
||||
<span id="cb2-77"><a href="#cb2-77"></a> <span class="cf">return</span> stat;</span>
|
||||
<span id="cb2-78"><a href="#cb2-78"></a>}</span></code></pre></div>
|
||||
<p>The file <code>list1.c</code> is located under the directory <a href="../src/misc">src/misc</a>. Make a shell script below and save it to your bin directory. (If you’ve installed Gtk4 from the source to $HOME/local, then your bin directory is $Home/local/bin. Otherwise, $Home/bin is your private bin directory.)</p>
|
||||
<div class="sourceCode" id="cb3"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true"></a><span class="fu">gcc</span> <span class="kw">`</span><span class="ex">pkg-config</span> --cflags gtk4<span class="kw">`</span> <span class="va">$1</span>.c <span class="kw">`</span><span class="ex">pkg-config</span> --libs gtk4<span class="kw">`</span></span></code></pre></div>
|
||||
<p>Change the current directory to the directory includes <code>list1.c</code> and type as follows.</p>
|
||||
<pre><code>$ chmod 755 $HOME/local/bin/comp # or chmod 755 $Home/bin/comp
|
||||
$ comp list1
|
||||
$ ./a.out</code></pre>
|
||||
<p>Then, <code>list1.c</code> has been compiled and executed.</p>
|
||||
<figure>
|
||||
<img src="../image/list1.png" alt="" /><figcaption>list1</figcaption>
|
||||
</figure>
|
||||
<p>I think the program is not so difficult. If you feel some difficulty, read this section again, especially GtkSignalListItemFactory subsubsection.</p>
|
||||
<h3 id="gtkbuilderlistitemfactory">GtkBuilderListItemFactory</h3>
|
||||
<p>GtkBuilderListItemFactory is another GtkListItemFactory. Its behavior is defined with ui file.</p>
|
||||
<div class="sourceCode" id="cb5"><pre class="sourceCode xml"><code class="sourceCode xml"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true"></a><span class="kw"><interface></span></span>
|
||||
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true"></a> <span class="kw"><template</span><span class="ot"> class=</span><span class="st">"GtkListItem"</span><span class="kw">></span></span>
|
||||
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"child"</span><span class="kw">></span></span>
|
||||
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkLabel"</span><span class="kw">></span></span>
|
||||
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true"></a> <span class="kw"><binding</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span></span>
|
||||
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true"></a> <span class="kw"><lookup</span><span class="ot"> name=</span><span class="st">"string"</span><span class="ot"> type=</span><span class="st">"GtkStringObject"</span><span class="kw">></span></span>
|
||||
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true"></a> <span class="kw"><lookup</span><span class="ot"> name=</span><span class="st">"item"</span><span class="kw">></span>GtkListItem<span class="kw"></lookup></span></span>
|
||||
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true"></a> <span class="kw"></lookup></span></span>
|
||||
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true"></a> <span class="kw"></binding></span></span>
|
||||
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true"></a> <span class="kw"></property></span></span>
|
||||
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true"></a> <span class="kw"></template></span></span>
|
||||
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true"></a><span class="kw"></interface></span></span></code></pre></div>
|
||||
<p>Template tag is used to define GtkListItem. And its child property is GtkLabel object. The factory sees this template and creates GtkLabel and sets the child property of GtkListItem. This is the same as what setup handler of GtkSignalListItemFactory did.</p>
|
||||
<p>Then, bind the label property of GtkLabel to string property of GtkStringObject. The string object is referred to by item property of GtkListItem. So, the lookup tag is like this:</p>
|
||||
<pre><code>string <- GtkStringObject <- item <- GtkListItem</code></pre>
|
||||
<p>The last lookup tag has a content <code>GtkListItem</code>. Usually, C type like <code>GtkListItem</code> doesn’t appear in the content of tags. This is a special case. There is an explanation about it in the <a href="https://blog.gtk.org/2020/09/05/a-primer-on-gtklistview/">GTK Development Blog</a> by Matthias Clasen.</p>
|
||||
<blockquote>
|
||||
<p>Remember that the classname (GtkListItem) in a ui template is used as the “this” pointer referring to the object that is being instantiated.</p>
|
||||
</blockquote>
|
||||
<p>Therefore, GtkListItem instance is used as the <code>this</code> object of the lookup tag when it is evaluated. <code>this</code> object will be explained in <a href="sec28.html">section 28</a>.</p>
|
||||
<p>The C source code is as follows. Its name is <code>list2.c</code> and located under <a href="../src/misc">src/misc</a> directory.</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="co">/* ----- activate, open, startup handlers ----- */</span></span>
|
||||
<span id="cb7-4"><a href="#cb7-4"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb7-5"><a href="#cb7-5"></a>app_activate (GApplication *application) {</span>
|
||||
<span id="cb7-6"><a href="#cb7-6"></a> GtkApplication *app = GTK_APPLICATION (application);</span>
|
||||
<span id="cb7-7"><a href="#cb7-7"></a> GtkWidget *win = gtk_application_window_new (app);</span>
|
||||
<span id="cb7-8"><a href="#cb7-8"></a> gtk_window_set_default_size (GTK_WINDOW (win), <span class="dv">600</span>, <span class="dv">400</span>);</span>
|
||||
<span id="cb7-9"><a href="#cb7-9"></a> GtkWidget *scr = gtk_scrolled_window_new ();</span>
|
||||
<span id="cb7-10"><a href="#cb7-10"></a> gtk_window_set_child (GTK_WINDOW (win), scr);</span>
|
||||
<span id="cb7-11"><a href="#cb7-11"></a></span>
|
||||
<span id="cb7-12"><a href="#cb7-12"></a> <span class="dt">char</span> *array[] = {</span>
|
||||
<span id="cb7-13"><a href="#cb7-13"></a> <span class="st">"one"</span>, <span class="st">"two"</span>, <span class="st">"three"</span>, <span class="st">"four"</span>, NULL</span>
|
||||
<span id="cb7-14"><a href="#cb7-14"></a> };</span>
|
||||
<span id="cb7-15"><a href="#cb7-15"></a> GtkStringList *sl = gtk_string_list_new ((<span class="dt">const</span> <span class="dt">char</span> * <span class="dt">const</span> *) array);</span>
|
||||
<span id="cb7-16"><a href="#cb7-16"></a> GtkSingleSelection *ss = gtk_single_selection_new (G_LIST_MODEL (sl));</span>
|
||||
<span id="cb7-17"><a href="#cb7-17"></a></span>
|
||||
<span id="cb7-18"><a href="#cb7-18"></a> <span class="dt">const</span> <span class="dt">char</span> *ui_string =</span>
|
||||
<span id="cb7-19"><a href="#cb7-19"></a><span class="st">"<interface>"</span></span>
|
||||
<span id="cb7-20"><a href="#cb7-20"></a> <span class="st">"<template class=</span><span class="sc">\"</span><span class="st">GtkListItem</span><span class="sc">\"</span><span class="st">>"</span></span>
|
||||
<span id="cb7-21"><a href="#cb7-21"></a> <span class="st">"<property name=</span><span class="sc">\"</span><span class="st">child</span><span class="sc">\"</span><span class="st">>"</span></span>
|
||||
<span id="cb7-22"><a href="#cb7-22"></a> <span class="st">"<object class=</span><span class="sc">\"</span><span class="st">GtkLabel</span><span class="sc">\"</span><span class="st">>"</span></span>
|
||||
<span id="cb7-23"><a href="#cb7-23"></a> <span class="st">"<binding name=</span><span class="sc">\"</span><span class="st">label</span><span class="sc">\"</span><span class="st">>"</span></span>
|
||||
<span id="cb7-24"><a href="#cb7-24"></a> <span class="st">"<lookup name=</span><span class="sc">\"</span><span class="st">string</span><span class="sc">\"</span><span class="st"> type=</span><span class="sc">\"</span><span class="st">GtkStringObject</span><span class="sc">\"</span><span class="st">>"</span></span>
|
||||
<span id="cb7-25"><a href="#cb7-25"></a> <span class="st">"<lookup name=</span><span class="sc">\"</span><span class="st">item</span><span class="sc">\"</span><span class="st">>GtkListItem</lookup>"</span></span>
|
||||
<span id="cb7-26"><a href="#cb7-26"></a> <span class="st">"</lookup>"</span></span>
|
||||
<span id="cb7-27"><a href="#cb7-27"></a> <span class="st">"</binding>"</span></span>
|
||||
<span id="cb7-28"><a href="#cb7-28"></a> <span class="st">"</object>"</span></span>
|
||||
<span id="cb7-29"><a href="#cb7-29"></a> <span class="st">"</property>"</span></span>
|
||||
<span id="cb7-30"><a href="#cb7-30"></a> <span class="st">"</template>"</span></span>
|
||||
<span id="cb7-31"><a href="#cb7-31"></a><span class="st">"</interface>"</span></span>
|
||||
<span id="cb7-32"><a href="#cb7-32"></a>;</span>
|
||||
<span id="cb7-33"><a href="#cb7-33"></a> GBytes *gbytes = g_bytes_new_static (ui_string, strlen (ui_string));</span>
|
||||
<span id="cb7-34"><a href="#cb7-34"></a> GtkListItemFactory *factory = gtk_builder_list_item_factory_new_from_bytes (NULL, gbytes);</span>
|
||||
<span id="cb7-35"><a href="#cb7-35"></a></span>
|
||||
<span id="cb7-36"><a href="#cb7-36"></a> GtkWidget *lv = gtk_list_view_new (GTK_SELECTION_MODEL (ss), factory);</span>
|
||||
<span id="cb7-37"><a href="#cb7-37"></a> gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), lv);</span>
|
||||
<span id="cb7-38"><a href="#cb7-38"></a> gtk_widget_show (win);</span>
|
||||
<span id="cb7-39"><a href="#cb7-39"></a>}</span>
|
||||
<span id="cb7-40"><a href="#cb7-40"></a></span>
|
||||
<span id="cb7-41"><a href="#cb7-41"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb7-42"><a href="#cb7-42"></a>app_startup (GApplication *application) {</span>
|
||||
<span id="cb7-43"><a href="#cb7-43"></a>}</span>
|
||||
<span id="cb7-44"><a href="#cb7-44"></a></span>
|
||||
<span id="cb7-45"><a href="#cb7-45"></a><span class="co">/* ----- main ----- */</span></span>
|
||||
<span id="cb7-46"><a href="#cb7-46"></a><span class="pp">#define APPLICATION_ID "com.github.ToshioCP.list2"</span></span>
|
||||
<span id="cb7-47"><a href="#cb7-47"></a></span>
|
||||
<span id="cb7-48"><a href="#cb7-48"></a><span class="dt">int</span></span>
|
||||
<span id="cb7-49"><a href="#cb7-49"></a>main (<span class="dt">int</span> argc, <span class="dt">char</span> **argv) {</span>
|
||||
<span id="cb7-50"><a href="#cb7-50"></a> GtkApplication *app;</span>
|
||||
<span id="cb7-51"><a href="#cb7-51"></a> <span class="dt">int</span> stat;</span>
|
||||
<span id="cb7-52"><a href="#cb7-52"></a></span>
|
||||
<span id="cb7-53"><a href="#cb7-53"></a> app = gtk_application_new (APPLICATION_ID, G_APPLICATION_FLAGS_NONE);</span>
|
||||
<span id="cb7-54"><a href="#cb7-54"></a></span>
|
||||
<span id="cb7-55"><a href="#cb7-55"></a> g_signal_connect (app, <span class="st">"startup"</span>, G_CALLBACK (app_startup), NULL);</span>
|
||||
<span id="cb7-56"><a href="#cb7-56"></a> g_signal_connect (app, <span class="st">"activate"</span>, G_CALLBACK (app_activate), NULL);</span>
|
||||
<span id="cb7-57"><a href="#cb7-57"></a></span>
|
||||
<span id="cb7-58"><a href="#cb7-58"></a> stat =g_application_run (G_APPLICATION (app), argc, argv);</span>
|
||||
<span id="cb7-59"><a href="#cb7-59"></a> g_object_unref (app);</span>
|
||||
<span id="cb7-60"><a href="#cb7-60"></a> <span class="cf">return</span> stat;</span>
|
||||
<span id="cb7-61"><a href="#cb7-61"></a>}</span></code></pre></div>
|
||||
<p>No signal handler is needed for GtkBulderListItemFactory. GtkSingleSelection is used, so user can select one item at a time.</p>
|
||||
<p>Because this is a small program, the ui data is given as a string.</p>
|
||||
<h2 id="gtkdirectorylist">GtkDirectoryList</h2>
|
||||
<p>GtkDirectoryList is a list model containing GFileInfo objects which are information of files under a certain directory. It uses <code>g_file_enumerate_children_async()</code> to get the GFileInfo objects. The list model is created by <code>gtk_directory_list_new</code> function.</p>
|
||||
<div class="sourceCode" id="cb8"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true"></a>GtkDirectoryList *gtk_directory_list_new (<span class="dt">const</span> <span class="dt">char</span> *attributes, GFile *file);</span></code></pre></div>
|
||||
<p><code>attributes</code> is a comma separated list of file attributes. File attributes are key-value pairs. A key consists of a namespace and a name. For example, “standard::name” key is the name of a file. “standard” means general file information. “name” means filename. The following table shows some example.</p>
|
||||
<table>
|
||||
<colgroup>
|
||||
<col style="width: 19%" />
|
||||
<col style="width: 80%" />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr class="header">
|
||||
<th style="text-align: left;">key</th>
|
||||
<th style="text-align: left;">meaning</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="odd">
|
||||
<td style="text-align: left;">standard::type</td>
|
||||
<td style="text-align: left;">file type. for example, regular file, directory, symbolic link, etc.</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td style="text-align: left;">standard::name</td>
|
||||
<td style="text-align: left;">filename</td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td style="text-align: left;">standard::size</td>
|
||||
<td style="text-align: left;">file size in bytes</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td style="text-align: left;">access::can-read</td>
|
||||
<td style="text-align: left;">read privilege if the user is able to read the file</td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td style="text-align: left;">time::modified</td>
|
||||
<td style="text-align: left;">the time the file was last modified in seconds since the UNIX epoch</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>The current directory is “.”. The following program makes GtkDirectoryList <code>dl</code> and its contents are GFileInfo objects under the current directory.</p>
|
||||
<div class="sourceCode" id="cb9"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true"></a>GFile *file = g_file_new_for_path (<span class="st">"."</span>);</span>
|
||||
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true"></a>GtkDirectoryList *dl = gtk_directory_list_new (<span class="st">"standard::name"</span>, file);</span>
|
||||
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true"></a>g_object_unref (file);</span></code></pre></div>
|
||||
<p>It is not so difficult to make file listing program by changing <code>list2.c</code> in the previous subsection. One problem is that GInfoFile doesn’t have properties. Lookup tag look for a property, so it is useless for looking for a filename from a GFileInfo object. Instead, closure tag is appropriate in this case. Closure tag specifies a function and the type of the return value of the function.</p>
|
||||
<div class="sourceCode" id="cb10"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true"></a><span class="dt">char</span> *</span>
|
||||
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true"></a>get_file_name (GtkListItem *item, GFileInfo *info) {</span>
|
||||
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true"></a> <span class="cf">if</span> (! G_IS_FILE_INFO (info))</span>
|
||||
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true"></a> <span class="cf">return</span> NULL;</span>
|
||||
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true"></a> <span class="cf">else</span></span>
|
||||
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true"></a> <span class="cf">return</span> g_strdup (g_file_info_get_name (info));</span>
|
||||
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true"></a>}</span>
|
||||
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true"></a></span>
|
||||
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true"></a>... ...</span>
|
||||
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true"></a>... ...</span>
|
||||
<span id="cb10-11"><a href="#cb10-11" aria-hidden="true"></a></span>
|
||||
<span id="cb10-12"><a href="#cb10-12" aria-hidden="true"></a><span class="st">"<interface>"</span></span>
|
||||
<span id="cb10-13"><a href="#cb10-13" aria-hidden="true"></a> <span class="st">"<template class=</span><span class="sc">\"</span><span class="st">GtkListItem</span><span class="sc">\"</span><span class="st">>"</span></span>
|
||||
<span id="cb10-14"><a href="#cb10-14" aria-hidden="true"></a> <span class="st">"<property name=</span><span class="sc">\"</span><span class="st">child</span><span class="sc">\"</span><span class="st">>"</span></span>
|
||||
<span id="cb10-15"><a href="#cb10-15" aria-hidden="true"></a> <span class="st">"<object class=</span><span class="sc">\"</span><span class="st">GtkLabel</span><span class="sc">\"</span><span class="st">>"</span></span>
|
||||
<span id="cb10-16"><a href="#cb10-16" aria-hidden="true"></a> <span class="st">"<binding name=</span><span class="sc">\"</span><span class="st">label</span><span class="sc">\"</span><span class="st">>"</span></span>
|
||||
<span id="cb10-17"><a href="#cb10-17" aria-hidden="true"></a> <span class="st">"<closure type=</span><span class="sc">\"</span><span class="st">gchararray</span><span class="sc">\"</span><span class="st"> function=</span><span class="sc">\"</span><span class="st">get_file_name</span><span class="sc">\"</span><span class="st">>"</span></span>
|
||||
<span id="cb10-18"><a href="#cb10-18" aria-hidden="true"></a> <span class="st">"<lookup name=</span><span class="sc">\"</span><span class="st">item</span><span class="sc">\"</span><span class="st">>GtkListItem</lookup>"</span></span>
|
||||
<span id="cb10-19"><a href="#cb10-19" aria-hidden="true"></a> <span class="st">"</closure>"</span></span>
|
||||
<span id="cb10-20"><a href="#cb10-20" aria-hidden="true"></a> <span class="st">"</binding>"</span></span>
|
||||
<span id="cb10-21"><a href="#cb10-21" aria-hidden="true"></a> <span class="st">"</object>"</span></span>
|
||||
<span id="cb10-22"><a href="#cb10-22" aria-hidden="true"></a> <span class="st">"</property>"</span></span>
|
||||
<span id="cb10-23"><a href="#cb10-23" aria-hidden="true"></a> <span class="st">"</template>"</span></span>
|
||||
<span id="cb10-24"><a href="#cb10-24" aria-hidden="true"></a><span class="st">"</interface>"</span></span></code></pre></div>
|
||||
<ul>
|
||||
<li>“gchararray” is the type name of strings. “gchar” is the same as “char” type. Therefore, “gchararray” is “an array of char type”, which is the same as string type. It is used to get the type of GValue object. GValue is a generic value and it can contain various type of values. For example, the type name can be gboolean, gchar (char), gint (int), gfloat (float), gdouble (double), gchararray (char *) and so on. These type names are the names of the fundamental types that are registered to the type system. See <a href="https://github.com/ToshioCP/Gobject-tutorial/blob/main/gfm/sec5.md#gvalue">GObject tutorial</a>.</li>
|
||||
<li>closure tag has type attribute and function attribute. Function attribute specifies the function name and type attribute specifies the type of the return value of the function. The contents of closure tag (it is between <closure…> and</closure>) is parameters of the function. <code><lookup name="item">GtkListItem</lookup></code> gives the value of the item property of the GtkListItem. This will be the second argument of the function. The first parameter is always the GListItem instance.</li>
|
||||
<li><code>gtk_file_name</code> function first check the <code>info</code> parameter. Because it can be NULL when GListItem <code>item</code> is unbound. If its GFileInfo, then return the filename (copy of the filename).</li>
|
||||
</ul>
|
||||
<p>The whole program (<code>list3.c</code>) is as follows. The program is located in <a href="../src/misc">src/misc</a> directory.</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="pp">#include </span><span class="im"><gtk/gtk.h></span></span>
|
||||
<span id="cb11-2"><a href="#cb11-2"></a></span>
|
||||
<span id="cb11-3"><a href="#cb11-3"></a><span class="dt">char</span> *</span>
|
||||
<span id="cb11-4"><a href="#cb11-4"></a>get_file_name (GtkListItem *item, GFileInfo *info) {</span>
|
||||
<span id="cb11-5"><a href="#cb11-5"></a> <span class="cf">if</span> (! G_IS_FILE_INFO (info))</span>
|
||||
<span id="cb11-6"><a href="#cb11-6"></a> <span class="cf">return</span> NULL;</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> g_strdup (g_file_info_get_name (info));</span>
|
||||
<span id="cb11-9"><a href="#cb11-9"></a>}</span>
|
||||
<span id="cb11-10"><a href="#cb11-10"></a></span>
|
||||
<span id="cb11-11"><a href="#cb11-11"></a><span class="co">/* ----- activate, open, startup handlers ----- */</span></span>
|
||||
<span id="cb11-12"><a href="#cb11-12"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb11-13"><a href="#cb11-13"></a>app_activate (GApplication *application) {</span>
|
||||
<span id="cb11-14"><a href="#cb11-14"></a> GtkApplication *app = GTK_APPLICATION (application);</span>
|
||||
<span id="cb11-15"><a href="#cb11-15"></a> GtkWidget *win = gtk_application_window_new (app);</span>
|
||||
<span id="cb11-16"><a href="#cb11-16"></a> gtk_window_set_default_size (GTK_WINDOW (win), <span class="dv">600</span>, <span class="dv">400</span>);</span>
|
||||
<span id="cb11-17"><a href="#cb11-17"></a> GtkWidget *scr = gtk_scrolled_window_new ();</span>
|
||||
<span id="cb11-18"><a href="#cb11-18"></a> gtk_window_set_child (GTK_WINDOW (win), scr);</span>
|
||||
<span id="cb11-19"><a href="#cb11-19"></a></span>
|
||||
<span id="cb11-20"><a href="#cb11-20"></a> GFile *file = g_file_new_for_path (<span class="st">"."</span>);</span>
|
||||
<span id="cb11-21"><a href="#cb11-21"></a> GtkDirectoryList *dl = gtk_directory_list_new (<span class="st">"standard::name"</span>, file);</span>
|
||||
<span id="cb11-22"><a href="#cb11-22"></a> g_object_unref (file);</span>
|
||||
<span id="cb11-23"><a href="#cb11-23"></a> GtkNoSelection *ns = gtk_no_selection_new (G_LIST_MODEL (dl));</span>
|
||||
<span id="cb11-24"><a href="#cb11-24"></a></span>
|
||||
<span id="cb11-25"><a href="#cb11-25"></a> <span class="dt">const</span> <span class="dt">char</span> *ui_string =</span>
|
||||
<span id="cb11-26"><a href="#cb11-26"></a><span class="st">"<interface>"</span></span>
|
||||
<span id="cb11-27"><a href="#cb11-27"></a> <span class="st">"<template class=</span><span class="sc">\"</span><span class="st">GtkListItem</span><span class="sc">\"</span><span class="st">>"</span></span>
|
||||
<span id="cb11-28"><a href="#cb11-28"></a> <span class="st">"<property name=</span><span class="sc">\"</span><span class="st">child</span><span class="sc">\"</span><span class="st">>"</span></span>
|
||||
<span id="cb11-29"><a href="#cb11-29"></a> <span class="st">"<object class=</span><span class="sc">\"</span><span class="st">GtkLabel</span><span class="sc">\"</span><span class="st">>"</span></span>
|
||||
<span id="cb11-30"><a href="#cb11-30"></a> <span class="st">"<binding name=</span><span class="sc">\"</span><span class="st">label</span><span class="sc">\"</span><span class="st">>"</span></span>
|
||||
<span id="cb11-31"><a href="#cb11-31"></a> <span class="st">"<closure type=</span><span class="sc">\"</span><span class="st">gchararray</span><span class="sc">\"</span><span class="st"> function=</span><span class="sc">\"</span><span class="st">get_file_name</span><span class="sc">\"</span><span class="st">>"</span></span>
|
||||
<span id="cb11-32"><a href="#cb11-32"></a> <span class="st">"<lookup name=</span><span class="sc">\"</span><span class="st">item</span><span class="sc">\"</span><span class="st">>GtkListItem</lookup>"</span></span>
|
||||
<span id="cb11-33"><a href="#cb11-33"></a> <span class="st">"</closure>"</span></span>
|
||||
<span id="cb11-34"><a href="#cb11-34"></a> <span class="st">"</binding>"</span></span>
|
||||
<span id="cb11-35"><a href="#cb11-35"></a> <span class="st">"</object>"</span></span>
|
||||
<span id="cb11-36"><a href="#cb11-36"></a> <span class="st">"</property>"</span></span>
|
||||
<span id="cb11-37"><a href="#cb11-37"></a> <span class="st">"</template>"</span></span>
|
||||
<span id="cb11-38"><a href="#cb11-38"></a><span class="st">"</interface>"</span></span>
|
||||
<span id="cb11-39"><a href="#cb11-39"></a>;</span>
|
||||
<span id="cb11-40"><a href="#cb11-40"></a> GBytes *gbytes = g_bytes_new_static (ui_string, strlen (ui_string));</span>
|
||||
<span id="cb11-41"><a href="#cb11-41"></a> GtkListItemFactory *factory = gtk_builder_list_item_factory_new_from_bytes (NULL, gbytes);</span>
|
||||
<span id="cb11-42"><a href="#cb11-42"></a></span>
|
||||
<span id="cb11-43"><a href="#cb11-43"></a> GtkWidget *lv = gtk_list_view_new (GTK_SELECTION_MODEL (ns), factory);</span>
|
||||
<span id="cb11-44"><a href="#cb11-44"></a> gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), lv);</span>
|
||||
<span id="cb11-45"><a href="#cb11-45"></a> gtk_widget_show (win);</span>
|
||||
<span id="cb11-46"><a href="#cb11-46"></a>}</span>
|
||||
<span id="cb11-47"><a href="#cb11-47"></a></span>
|
||||
<span id="cb11-48"><a href="#cb11-48"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb11-49"><a href="#cb11-49"></a>app_startup (GApplication *application) {</span>
|
||||
<span id="cb11-50"><a href="#cb11-50"></a>}</span>
|
||||
<span id="cb11-51"><a href="#cb11-51"></a></span>
|
||||
<span id="cb11-52"><a href="#cb11-52"></a><span class="co">/* ----- main ----- */</span></span>
|
||||
<span id="cb11-53"><a href="#cb11-53"></a><span class="pp">#define APPLICATION_ID "com.github.ToshioCP.list3"</span></span>
|
||||
<span id="cb11-54"><a href="#cb11-54"></a></span>
|
||||
<span id="cb11-55"><a href="#cb11-55"></a><span class="dt">int</span></span>
|
||||
<span id="cb11-56"><a href="#cb11-56"></a>main (<span class="dt">int</span> argc, <span class="dt">char</span> **argv) {</span>
|
||||
<span id="cb11-57"><a href="#cb11-57"></a> GtkApplication *app;</span>
|
||||
<span id="cb11-58"><a href="#cb11-58"></a> <span class="dt">int</span> stat;</span>
|
||||
<span id="cb11-59"><a href="#cb11-59"></a></span>
|
||||
<span id="cb11-60"><a href="#cb11-60"></a> app = gtk_application_new (APPLICATION_ID, G_APPLICATION_FLAGS_NONE);</span>
|
||||
<span id="cb11-61"><a href="#cb11-61"></a></span>
|
||||
<span id="cb11-62"><a href="#cb11-62"></a> g_signal_connect (app, <span class="st">"startup"</span>, G_CALLBACK (app_startup), NULL);</span>
|
||||
<span id="cb11-63"><a href="#cb11-63"></a> g_signal_connect (app, <span class="st">"activate"</span>, G_CALLBACK (app_activate), NULL);</span>
|
||||
<span id="cb11-64"><a href="#cb11-64"></a></span>
|
||||
<span id="cb11-65"><a href="#cb11-65"></a> stat =g_application_run (G_APPLICATION (app), argc, argv);</span>
|
||||
<span id="cb11-66"><a href="#cb11-66"></a> g_object_unref (app);</span>
|
||||
<span id="cb11-67"><a href="#cb11-67"></a> <span class="cf">return</span> stat;</span>
|
||||
<span id="cb11-68"><a href="#cb11-68"></a>}</span></code></pre></div>
|
||||
<p>The ui data (xml data above) is used to build the GListItem template at runtime. GtkBuilder refers to the symbol table to find the function <code>get_file_name</code>.</p>
|
||||
<p>Generally, a symbol table is used by a linker to link objects to an executable file. It includes function names and their location. A linker usually doesn’t put a symbol table into the created executable file. But if <code>--export-dynamic</code> option is given, the linker adds the symbol table to the executable file.</p>
|
||||
<p>To accomplish it, an option <code>-Wl,--export-dynamic</code> is given to the C compiler.</p>
|
||||
<ul>
|
||||
<li><code>-Wl</code> is a C compiler option that passes the following option to the linker.</li>
|
||||
<li><code>--export-dynamic</code> is a linker option. The following is cited from the linker document. “When creating a dynamically linked executable, add all symbols to the dynamic symbol table. The dynamic symbol table is the set of symbols which are visible from dynamic objects at run time.”</li>
|
||||
</ul>
|
||||
<p>Compile and execute it.</p>
|
||||
<pre><code>$ gcc -Wl,--export-dynamic `pkg-config --cflags gtk4` list3.c `pkg-config --libs gtk4`</code></pre>
|
||||
<p>You can also make a shell script to compile <code>list3.c</code></p>
|
||||
<div class="sourceCode" id="cb13"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true"></a><span class="fu">gcc</span> -Wl,--export-dynamic <span class="kw">`</span><span class="ex">pkg-config</span> --cflags gtk4<span class="kw">`</span> <span class="va">$1</span>.c <span class="kw">`</span><span class="ex">pkg-config</span> --libs gtk4<span class="kw">`</span></span></code></pre></div>
|
||||
<p>Save this one liner to a file <code>comp</code>. Then, copy it to <code>$HOME/bin</code> and give it executable permission.</p>
|
||||
<pre><code>$ cp comp $HOME/bin/comp
|
||||
$ chmod +x $HOME/bin/comp</code></pre>
|
||||
<p>You can compile <code>list3.c</code> and execute it, like this:</p>
|
||||
<pre><code>$ comp list3
|
||||
$ ./a.out</code></pre>
|
||||
<figure>
|
||||
<img src="../image/list3.png" alt="" /><figcaption>screenshot list3</figcaption>
|
||||
</figure>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec25.html">Section 25</a>, Next: <a href="sec27.html">Section 27</a></p>
|
||||
</body>
|
||||
</html>
|
491
docs/sec27.html
Normal file
491
docs/sec27.html
Normal file
|
@ -0,0 +1,491 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>Gtk4 tutorial for beginners</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
body {width: 1080px; margin: 0 auto; font-size: large;}
|
||||
h2 {padding: 10px; background-color: #d0f0d0; }
|
||||
div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
|
||||
th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
|
||||
td {padding: 2px 6px; border: 1px solid;}
|
||||
img {display: block; margin-left: auto; margin-right: auto;}
|
||||
figcaption {text-align: center;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec26.html">Section 26</a>, Next: <a href="sec28.html">Section 28</a></p>
|
||||
<h1 id="gtkgridview-and-activate-signal">GtkGridView and activate signal</h1>
|
||||
<p>GtkGridView is similar to GtkListView. It displays a GListModel as a grid, which is like a square tessellation.</p>
|
||||
<figure>
|
||||
<img src="../image/list4.png" alt="" /><figcaption>Grid</figcaption>
|
||||
</figure>
|
||||
<p>This is often seen when you use a file browser like nautilus.</p>
|
||||
<p>In this section, let’s make a very simple file browser <code>list4</code>. It just shows the files in the current directory. And a user can choose list or grid by clicking on buttons in the tool bar. Each item in the list or grid has an icon and a filename. In addition, <code>list4</code> provides the way to open the <code>tfe</code> text editor to show a text file. A user can do that by double clicking on an item or pressing enter key when an item is selected.</p>
|
||||
<h2 id="gtkdirectorylist">GtkDirectoryList</h2>
|
||||
<p>GtkDirectoryList implements GListModel and it contains information of files in a certain directory. The items of the list are GFileInfo objects.</p>
|
||||
<p>In the <code>list4</code> source files, GtkDirectoryList is described in a ui file and built by GtkBuilder. The GtkDirectoryList instance is assigned to the “model” property of a GtkSingleSelection instance. And the GtkSingleSelection instance is assigned to the “model” property of a GListView or GGridView instance.</p>
|
||||
<pre><code>GtkListView (model property) => GtkSingleSelection (model property) => GtkDirectoryList
|
||||
GtkGridView (model property) => GtkSingleSelection (model property) => GtkDirectoryList</code></pre>
|
||||
<figure>
|
||||
<img src="../image/directorylist.png" alt="" /><figcaption>DirectoryList</figcaption>
|
||||
</figure>
|
||||
<p>The following is the part of the ui file <code>list4.ui</code>. It defines GtkListView, GtkSingleSelection and GtkDirectoryList. It also defines GtkGridView and GtkSingleSelection.</p>
|
||||
<div class="sourceCode" id="cb2"><pre class="sourceCode xml"><code class="sourceCode xml"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true"></a><span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkListView"</span><span class="ot"> id=</span><span class="st">"list"</span><span class="kw">></span></span>
|
||||
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"model"</span><span class="kw">></span></span>
|
||||
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkSingleSelection"</span><span class="ot"> id=</span><span class="st">"singleselection"</span><span class="kw">></span></span>
|
||||
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"model"</span><span class="kw">></span></span>
|
||||
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkDirectoryList"</span><span class="ot"> id=</span><span class="st">"directorylist"</span><span class="kw">></span></span>
|
||||
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"attributes"</span><span class="kw">></span>standard::name,standard::icon,standard::content-type<span class="kw"></property></span></span>
|
||||
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true"></a> <span class="kw"></property></span></span>
|
||||
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true"></a> <span class="kw"></property></span></span>
|
||||
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true"></a><span class="kw"></object></span></span>
|
||||
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true"></a><span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkGridView"</span><span class="ot"> id=</span><span class="st">"grid"</span><span class="kw">></span></span>
|
||||
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"model"</span><span class="kw">></span>singleselection<span class="kw"></property></span></span>
|
||||
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true"></a><span class="kw"></object></span></span></code></pre></div>
|
||||
<p>GtkDirectoryList has an “attributes” property. It is attributes of GFileInfo such as “standard::name”, “standard::icon” and “standard::content-type”.</p>
|
||||
<ul>
|
||||
<li>standard::name is a filename.</li>
|
||||
<li>standard::icon is an icon of the file. It is a GIcon object.</li>
|
||||
<li>standard::content-type is a content-type. Content-type is the same as mime type for the internet technology. For example, “text/plain” is a text file, “text/x-csrc” is a C source code and so on. (“text/x-csrc”is not registered to IANA media types. Such “x-” subtype is not a standard mime type.) Content type is also used by the desktop system.</li>
|
||||
</ul>
|
||||
<p>GtkGridView has the same structure as GtkListView. But it is enough to specify its model property to <code>singleselection</code> which is the identification of the GtkSingleSelection. Therefore the description for GtkGridView is very short.</p>
|
||||
<h2 id="ui-file-of-the-window">Ui file of the window</h2>
|
||||
<p>Look at the screenshot of <code>list4</code> at the top of this section. The widgets are built with the following ui file.</p>
|
||||
<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"><interface></span></span>
|
||||
<span id="cb3-3"><a href="#cb3-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="cb3-4"><a href="#cb3-4"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"title"</span><span class="kw">></span>file list<span class="kw"></property></span></span>
|
||||
<span id="cb3-5"><a href="#cb3-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="cb3-6"><a href="#cb3-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="cb3-7"><a href="#cb3-7"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb3-8"><a href="#cb3-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="cb3-9"><a href="#cb3-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="cb3-10"><a href="#cb3-10"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb3-11"><a href="#cb3-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="cb3-12"><a href="#cb3-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="cb3-13"><a href="#cb3-13"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb3-14"><a href="#cb3-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="cb3-15"><a href="#cb3-15"></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="cb3-16"><a href="#cb3-16"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb3-17"><a href="#cb3-17"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb3-18"><a href="#cb3-18"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb3-19"><a href="#cb3-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">"btnlist"</span><span class="kw">></span></span>
|
||||
<span id="cb3-20"><a href="#cb3-20"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"name"</span><span class="kw">></span>btnlist<span class="kw"></property></span></span>
|
||||
<span id="cb3-21"><a href="#cb3-21"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"action-name"</span><span class="kw">></span>win.view<span class="kw"></property></span></span>
|
||||
<span id="cb3-22"><a href="#cb3-22"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"action-target"</span><span class="kw">></span><span class="dv">&apos;</span>list<span class="dv">&apos;</span><span class="kw"></property></span></span>
|
||||
<span id="cb3-23"><a href="#cb3-23"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb3-24"><a href="#cb3-24"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkImage"</span><span class="kw">></span></span>
|
||||
<span id="cb3-25"><a href="#cb3-25"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"resource"</span><span class="kw">></span>/com/github/ToshioCP/list4/list.png<span class="kw"></property></span></span>
|
||||
<span id="cb3-26"><a href="#cb3-26"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb3-27"><a href="#cb3-27"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb3-28"><a href="#cb3-28"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb3-29"><a href="#cb3-29"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb3-30"><a href="#cb3-30"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb3-31"><a href="#cb3-31"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkButton"</span><span class="ot"> id=</span><span class="st">"btngrid"</span><span class="kw">></span></span>
|
||||
<span id="cb3-32"><a href="#cb3-32"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"name"</span><span class="kw">></span>btngrid<span class="kw"></property></span></span>
|
||||
<span id="cb3-33"><a href="#cb3-33"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"action-name"</span><span class="kw">></span>win.view<span class="kw"></property></span></span>
|
||||
<span id="cb3-34"><a href="#cb3-34"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"action-target"</span><span class="kw">></span><span class="dv">&apos;</span>grid<span class="dv">&apos;</span><span class="kw"></property></span></span>
|
||||
<span id="cb3-35"><a href="#cb3-35"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb3-36"><a href="#cb3-36"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkImage"</span><span class="kw">></span></span>
|
||||
<span id="cb3-37"><a href="#cb3-37"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"resource"</span><span class="kw">></span>/com/github/ToshioCP/list4/grid.png<span class="kw"></property></span></span>
|
||||
<span id="cb3-38"><a href="#cb3-38"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb3-39"><a href="#cb3-39"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb3-40"><a href="#cb3-40"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb3-41"><a href="#cb3-41"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb3-42"><a href="#cb3-42"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb3-43"><a href="#cb3-43"></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="cb3-44"><a href="#cb3-44"></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="cb3-45"><a href="#cb3-45"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb3-46"><a href="#cb3-46"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb3-47"><a href="#cb3-47"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb3-48"><a href="#cb3-48"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb3-49"><a href="#cb3-49"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb3-50"><a href="#cb3-50"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkScrolledWindow"</span><span class="ot"> id=</span><span class="st">"scr"</span><span class="kw">></span></span>
|
||||
<span id="cb3-51"><a href="#cb3-51"></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="cb3-52"><a href="#cb3-52"></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="cb3-53"><a href="#cb3-53"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb3-54"><a href="#cb3-54"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb3-55"><a href="#cb3-55"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb3-56"><a href="#cb3-56"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb3-57"><a href="#cb3-57"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb3-58"><a href="#cb3-58"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkListView"</span><span class="ot"> id=</span><span class="st">"list"</span><span class="kw">></span></span>
|
||||
<span id="cb3-59"><a href="#cb3-59"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"model"</span><span class="kw">></span></span>
|
||||
<span id="cb3-60"><a href="#cb3-60"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkSingleSelection"</span><span class="ot"> id=</span><span class="st">"singleselection"</span><span class="kw">></span></span>
|
||||
<span id="cb3-61"><a href="#cb3-61"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"model"</span><span class="kw">></span></span>
|
||||
<span id="cb3-62"><a href="#cb3-62"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkDirectoryList"</span><span class="ot"> id=</span><span class="st">"directorylist"</span><span class="kw">></span></span>
|
||||
<span id="cb3-63"><a href="#cb3-63"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"attributes"</span><span class="kw">></span>standard::name,standard::icon,standard::content-type<span class="kw"></property></span></span>
|
||||
<span id="cb3-64"><a href="#cb3-64"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb3-65"><a href="#cb3-65"></a> <span class="kw"></property></span></span>
|
||||
<span id="cb3-66"><a href="#cb3-66"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb3-67"><a href="#cb3-67"></a> <span class="kw"></property></span></span>
|
||||
<span id="cb3-68"><a href="#cb3-68"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb3-69"><a href="#cb3-69"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkGridView"</span><span class="ot"> id=</span><span class="st">"grid"</span><span class="kw">></span></span>
|
||||
<span id="cb3-70"><a href="#cb3-70"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"model"</span><span class="kw">></span>singleselection<span class="kw"></property></span></span>
|
||||
<span id="cb3-71"><a href="#cb3-71"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb3-72"><a href="#cb3-72"></a><span class="kw"></interface></span></span></code></pre></div>
|
||||
<p>The file consists of two parts. The first part begins at the third line and ends at the 57th line. This part is the widgets from the top level window to the scrolled window. It also includes two buttons. The second part begins at the 58th line and ends at the 71st line. This is the part of GtkListView and GtkGridView. They are described in the previous section.</p>
|
||||
<ul>
|
||||
<li>13-17, 42-46: Two labels are dummy labels. They just work as a space to put the two buttons at the appropriate position.</li>
|
||||
<li>19-41: GtkButton <code>btnlist</code> and <code>btngrid</code>. These two buttons work as selection buttons to switch from list to grid and vice versa. These two buttons are connected to a stateful action <code>win.view</code>. This action is stateful and has a parameter. Such action consists of prefix, action name and parameter. The prefix of the action is <code>win</code>, which means the action belongs to the top level window. So, a prefix gives the scope of the action. The action name is <code>view</code>. The parameters are <code>list</code> or <code>grid</code>, which show the state of the action. A parameter is also called a target, because it is a target to which the buttons are clicked on to change the action state. We often write the detailed action like “win.view::list” or “win.view::grid”.</li>
|
||||
<li>21-22: The properties “action-name” and “action-target” belong to GtkActionable interface. GtkButton implements GtkActionable. The action name is “win.view” and the target is “list”. Generally, a target is GVariant, which can be string, integer, float and so on. You need to use GVariant text format to write GVariant value in ui files. If the type of the GVariant value is string, then the value with GVariant text format is bounded by single quotes or double quotes. Because ui file is xml format text, single quote cannot be written without escape. Its escape sequence is &apos;. Therefore, the target ‘list’ is written as &apos;list&apos;. Because the button is connected to the action, “clicked” signal handler isn’t needed.</li>
|
||||
<li>23-27: The child widget of the button is GtkImage. GtkImage has a “resource” property. It is a GResource and GtkImage reads an image data from the resource and sets the image. This resource is built from 24x24-sized png image data, which is an original icon.</li>
|
||||
<li>50-53: GtkScrolledWindow. Its child widget will be GtkListView or GtkGridView.</li>
|
||||
</ul>
|
||||
<p>The action <code>view</code> is created, connected to the “activate” signal handler and inserted to the window (action map) as follows.</p>
|
||||
<div class="sourceCode" id="cb4"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true"></a> act_view = g_simple_action_new_stateful (<span class="st">"view"</span>, g_variant_type_new(<span class="st">"s"</span>), g_variant_new_string (<span class="st">"list"</span>));</span>
|
||||
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true"></a> g_signal_connect (act_view, <span class="st">"activate"</span>, G_CALLBACK (view_activated), scr); <span class="co">/* scr is the GtkScrolledWindow object */</span></span>
|
||||
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true"></a> g_action_map_add_action (G_ACTION_MAP (win), G_ACTION (act_view));</span></code></pre></div>
|
||||
<p>The signal handler <code>view_activated</code> will be explained later.</p>
|
||||
<h2 id="factories">Factories</h2>
|
||||
<p>Each view (GtkListView and GtkGridView) has its own factory because its items have different structure of widgets. The factories are GtkBuilderListItemFactory objects. Their ui files are as follows.</p>
|
||||
<p>factory_list.ui</p>
|
||||
<div class="sourceCode" id="cb5"><pre class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><span id="cb5-1"><a href="#cb5-1"></a><span class="kw"><?xml</span> version="1.0" encoding="UTF-8"<span class="kw">?></span></span>
|
||||
<span id="cb5-2"><a href="#cb5-2"></a><span class="kw"><interface></span></span>
|
||||
<span id="cb5-3"><a href="#cb5-3"></a> <span class="kw"><template</span><span class="ot"> class=</span><span class="st">"GtkListItem"</span><span class="kw">></span></span>
|
||||
<span id="cb5-4"><a href="#cb5-4"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"child"</span><span class="kw">></span></span>
|
||||
<span id="cb5-5"><a href="#cb5-5"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkBox"</span><span class="kw">></span></span>
|
||||
<span id="cb5-6"><a href="#cb5-6"></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="cb5-7"><a href="#cb5-7"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"spacing"</span><span class="kw">></span>20<span class="kw"></property></span></span>
|
||||
<span id="cb5-8"><a href="#cb5-8"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb5-9"><a href="#cb5-9"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkImage"</span><span class="kw">></span></span>
|
||||
<span id="cb5-10"><a href="#cb5-10"></a> <span class="kw"><binding</span><span class="ot"> name=</span><span class="st">"gicon"</span><span class="kw">></span></span>
|
||||
<span id="cb5-11"><a href="#cb5-11"></a> <span class="kw"><closure</span><span class="ot"> type=</span><span class="st">"GIcon"</span><span class="ot"> function=</span><span class="st">"get_icon"</span><span class="kw">></span></span>
|
||||
<span id="cb5-12"><a href="#cb5-12"></a> <span class="kw"><lookup</span><span class="ot"> name=</span><span class="st">"item"</span><span class="kw">></span>GtkListItem<span class="kw"></lookup></span></span>
|
||||
<span id="cb5-13"><a href="#cb5-13"></a> <span class="kw"></closure></span></span>
|
||||
<span id="cb5-14"><a href="#cb5-14"></a> <span class="kw"></binding></span></span>
|
||||
<span id="cb5-15"><a href="#cb5-15"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb5-16"><a href="#cb5-16"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb5-17"><a href="#cb5-17"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb5-18"><a href="#cb5-18"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkLabel"</span><span class="kw">></span></span>
|
||||
<span id="cb5-19"><a href="#cb5-19"></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="cb5-20"><a href="#cb5-20"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"xalign"</span><span class="kw">></span>0<span class="kw"></property></span></span>
|
||||
<span id="cb5-21"><a href="#cb5-21"></a> <span class="kw"><binding</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span></span>
|
||||
<span id="cb5-22"><a href="#cb5-22"></a> <span class="kw"><closure</span><span class="ot"> type=</span><span class="st">"gchararray"</span><span class="ot"> function=</span><span class="st">"get_file_name"</span><span class="kw">></span></span>
|
||||
<span id="cb5-23"><a href="#cb5-23"></a> <span class="kw"><lookup</span><span class="ot"> name=</span><span class="st">"item"</span><span class="kw">></span>GtkListItem<span class="kw"></lookup></span></span>
|
||||
<span id="cb5-24"><a href="#cb5-24"></a> <span class="kw"></closure></span></span>
|
||||
<span id="cb5-25"><a href="#cb5-25"></a> <span class="kw"></binding></span></span>
|
||||
<span id="cb5-26"><a href="#cb5-26"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb5-27"><a href="#cb5-27"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb5-28"><a href="#cb5-28"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb5-29"><a href="#cb5-29"></a> <span class="kw"></property></span></span>
|
||||
<span id="cb5-30"><a href="#cb5-30"></a> <span class="kw"></template></span></span>
|
||||
<span id="cb5-31"><a href="#cb5-31"></a><span class="kw"></interface></span></span></code></pre></div>
|
||||
<p>factory_grid.ui</p>
|
||||
<div class="sourceCode" id="cb6"><pre class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><span id="cb6-1"><a href="#cb6-1"></a><span class="kw"><?xml</span> version="1.0" encoding="UTF-8"<span class="kw">?></span></span>
|
||||
<span id="cb6-2"><a href="#cb6-2"></a><span class="kw"><interface></span></span>
|
||||
<span id="cb6-3"><a href="#cb6-3"></a> <span class="kw"><template</span><span class="ot"> class=</span><span class="st">"GtkListItem"</span><span class="kw">></span></span>
|
||||
<span id="cb6-4"><a href="#cb6-4"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"child"</span><span class="kw">></span></span>
|
||||
<span id="cb6-5"><a href="#cb6-5"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkBox"</span><span class="kw">></span></span>
|
||||
<span id="cb6-6"><a href="#cb6-6"></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="cb6-7"><a href="#cb6-7"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"spacing"</span><span class="kw">></span>20<span class="kw"></property></span></span>
|
||||
<span id="cb6-8"><a href="#cb6-8"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb6-9"><a href="#cb6-9"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkImage"</span><span class="kw">></span></span>
|
||||
<span id="cb6-10"><a href="#cb6-10"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"icon-size"</span><span class="kw">></span>GTK_ICON_SIZE_LARGE<span class="kw"></property></span></span>
|
||||
<span id="cb6-11"><a href="#cb6-11"></a> <span class="kw"><binding</span><span class="ot"> name=</span><span class="st">"gicon"</span><span class="kw">></span></span>
|
||||
<span id="cb6-12"><a href="#cb6-12"></a> <span class="kw"><closure</span><span class="ot"> type=</span><span class="st">"GIcon"</span><span class="ot"> function=</span><span class="st">"get_icon"</span><span class="kw">></span></span>
|
||||
<span id="cb6-13"><a href="#cb6-13"></a> <span class="kw"><lookup</span><span class="ot"> name=</span><span class="st">"item"</span><span class="kw">></span>GtkListItem<span class="kw"></lookup></span></span>
|
||||
<span id="cb6-14"><a href="#cb6-14"></a> <span class="kw"></closure></span></span>
|
||||
<span id="cb6-15"><a href="#cb6-15"></a> <span class="kw"></binding></span></span>
|
||||
<span id="cb6-16"><a href="#cb6-16"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb6-17"><a href="#cb6-17"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb6-18"><a href="#cb6-18"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb6-19"><a href="#cb6-19"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkLabel"</span><span class="kw">></span></span>
|
||||
<span id="cb6-20"><a href="#cb6-20"></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="cb6-21"><a href="#cb6-21"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"xalign"</span><span class="kw">></span>0.5<span class="kw"></property></span></span>
|
||||
<span id="cb6-22"><a href="#cb6-22"></a> <span class="kw"><binding</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span></span>
|
||||
<span id="cb6-23"><a href="#cb6-23"></a> <span class="kw"><closure</span><span class="ot"> type=</span><span class="st">"gchararray"</span><span class="ot"> function=</span><span class="st">"get_file_name"</span><span class="kw">></span></span>
|
||||
<span id="cb6-24"><a href="#cb6-24"></a> <span class="kw"><lookup</span><span class="ot"> name=</span><span class="st">"item"</span><span class="kw">></span>GtkListItem<span class="kw"></lookup></span></span>
|
||||
<span id="cb6-25"><a href="#cb6-25"></a> <span class="kw"></closure></span></span>
|
||||
<span id="cb6-26"><a href="#cb6-26"></a> <span class="kw"></binding></span></span>
|
||||
<span id="cb6-27"><a href="#cb6-27"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb6-28"><a href="#cb6-28"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb6-29"><a href="#cb6-29"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb6-30"><a href="#cb6-30"></a> <span class="kw"></property></span></span>
|
||||
<span id="cb6-31"><a href="#cb6-31"></a> <span class="kw"></template></span></span>
|
||||
<span id="cb6-32"><a href="#cb6-32"></a><span class="kw"></interface></span></span></code></pre></div>
|
||||
<p>The two files above are almost same. The difference is:</p>
|
||||
<ul>
|
||||
<li>The orientation of the box</li>
|
||||
<li>The icon size</li>
|
||||
<li>The position of the text of the label</li>
|
||||
</ul>
|
||||
<pre><code>$ cd list4; diff factory_list.ui factory_grid.ui
|
||||
6c6
|
||||
< <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
|
||||
---
|
||||
> <property name="orientation">GTK_ORIENTATION_VERTICAL</property>
|
||||
9a10
|
||||
> <property name="icon-size">GTK_ICON_SIZE_LARGE</property>
|
||||
20c21
|
||||
< <property name="xalign">0</property>
|
||||
---
|
||||
> <property name="xalign">0.5</property></code></pre>
|
||||
<p>Each view item has two properties, “gicon” property of GtkImage and “label” property of GtkLabel. Because GFileInfo doesn’t have properties correspond to icon or filename, the factory uses closure tag to bind “gicon” and “label” properties to GFileInfo information. A function <code>get_icon</code> gets GIcon the GFileInfo object has. And a function <code>get_file_name</code> gets a filename the GFileInfo object has.</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>GIcon *</span>
|
||||
<span id="cb8-2"><a href="#cb8-2"></a>get_icon (GtkListItem *item, GFileInfo *info) {</span>
|
||||
<span id="cb8-3"><a href="#cb8-3"></a> GIcon *icon;</span>
|
||||
<span id="cb8-4"><a href="#cb8-4"></a></span>
|
||||
<span id="cb8-5"><a href="#cb8-5"></a> <span class="cf">if</span> (! G_IS_FILE_INFO (info))</span>
|
||||
<span id="cb8-6"><a href="#cb8-6"></a> <span class="cf">return</span> NULL;</span>
|
||||
<span id="cb8-7"><a href="#cb8-7"></a> <span class="cf">else</span> {</span>
|
||||
<span id="cb8-8"><a href="#cb8-8"></a> icon = g_file_info_get_icon (info);</span>
|
||||
<span id="cb8-9"><a href="#cb8-9"></a> g_object_ref (icon);</span>
|
||||
<span id="cb8-10"><a href="#cb8-10"></a> <span class="cf">return</span> icon;</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>
|
||||
<span id="cb8-14"><a href="#cb8-14"></a><span class="dt">char</span> *</span>
|
||||
<span id="cb8-15"><a href="#cb8-15"></a>get_file_name (GtkListItem *item, GFileInfo *info) {</span>
|
||||
<span id="cb8-16"><a href="#cb8-16"></a> <span class="cf">if</span> (! G_IS_FILE_INFO (info))</span>
|
||||
<span id="cb8-17"><a href="#cb8-17"></a> <span class="cf">return</span> NULL;</span>
|
||||
<span id="cb8-18"><a href="#cb8-18"></a> <span class="cf">else</span></span>
|
||||
<span id="cb8-19"><a href="#cb8-19"></a> <span class="cf">return</span> g_strdup (g_file_info_get_name (info));</span>
|
||||
<span id="cb8-20"><a href="#cb8-20"></a>}</span></code></pre></div>
|
||||
<p>One important thing is view items own the instance or string. It is achieved by <code>g_object_ref</code> to increase the reference count by one, or <code>strdup</code> to create a copy of the string. The object or string will be automatically freed in unbinding process when the view item is recycled.</p>
|
||||
<h2 id="an-activate-signal-handler-of-the-action">An activate signal handler of the action</h2>
|
||||
<p>An activate signal handler <code>view_activate</code> switches the view. It does two things.</p>
|
||||
<ul>
|
||||
<li>Changes the child widget of GtkScrolledWindow.</li>
|
||||
<li>Changes the CSS of buttons to show the current state.</li>
|
||||
</ul>
|
||||
<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>view_activated(GSimpleAction *action, GVariant *parameter, gpointer user_data) {</span>
|
||||
<span id="cb9-3"><a href="#cb9-3"></a> GtkScrolledWindow *scr = GTK_SCROLLED_WINDOW (user_data);</span>
|
||||
<span id="cb9-4"><a href="#cb9-4"></a> <span class="dt">const</span> <span class="dt">char</span> *view = g_variant_get_string (parameter, NULL);</span>
|
||||
<span id="cb9-5"><a href="#cb9-5"></a> <span class="dt">const</span> <span class="dt">char</span> *other;</span>
|
||||
<span id="cb9-6"><a href="#cb9-6"></a> <span class="dt">char</span> *css;</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> (strcmp (view, <span class="st">"list"</span>) == <span class="dv">0</span>) {</span>
|
||||
<span id="cb9-9"><a href="#cb9-9"></a> other = <span class="st">"grid"</span>;</span>
|
||||
<span id="cb9-10"><a href="#cb9-10"></a> gtk_scrolled_window_set_child (scr, list);</span>
|
||||
<span id="cb9-11"><a href="#cb9-11"></a> }<span class="cf">else</span> {</span>
|
||||
<span id="cb9-12"><a href="#cb9-12"></a> other = <span class="st">"list"</span>;</span>
|
||||
<span id="cb9-13"><a href="#cb9-13"></a> gtk_scrolled_window_set_child (scr, grid);</span>
|
||||
<span id="cb9-14"><a href="#cb9-14"></a> }</span>
|
||||
<span id="cb9-15"><a href="#cb9-15"></a> css = g_strdup_printf (<span class="st">"button#btn%s {background: silver;} button#btn%s {background: white;}"</span>, view, other);</span>
|
||||
<span id="cb9-16"><a href="#cb9-16"></a> gtk_css_provider_load_from_data (provider, css, -<span class="dv">1</span>);</span>
|
||||
<span id="cb9-17"><a href="#cb9-17"></a> g_free (css);</span>
|
||||
<span id="cb9-18"><a href="#cb9-18"></a> g_action_change_state (G_ACTION (action), parameter);</span>
|
||||
<span id="cb9-19"><a href="#cb9-19"></a>}</span></code></pre></div>
|
||||
<p>The second parameter of this handler is the target of the clicked button. Its type is GVariant.</p>
|
||||
<ul>
|
||||
<li>If <code>btnlist</code> has been clicked, then <code>parameter</code> is a GVariant of the string “list”.</li>
|
||||
<li>If <code>btngrid</code> has been clicked, then <code>parameter</code> is a GVariant of the string “grid”.</li>
|
||||
</ul>
|
||||
<p>The third parameter <code>user_data</code> points GtkScrolledWindow, which is set in the <code>g_signal_connect</code> function.</p>
|
||||
<ul>
|
||||
<li>4: <code>g_variant_get_string</code> gets the string from the GVariant variable.</li>
|
||||
<li>8-14: Sets the child of <code>scr</code>. The function <code>gtk_scrolled_window_set_child</code> decreases the reference count of the old child by one. And it increases the reference count of the new child by one.</li>
|
||||
<li>15-17: Sets the CSS of the buttons. The background of the clicked button will be silver color and the other button will be white.</li>
|
||||
<li>18: Changes the state of the action.</li>
|
||||
</ul>
|
||||
<h2 id="activate-signal-of-gtklistview-and-gtkgridview">Activate signal of GtkListView and GtkGridView</h2>
|
||||
<p>Views (GtkListView and GtkGridView) have an “activate” signal. It is emitted when an item in the view is double clicked or the enter key is pressed. You can do anything you like by connecting the “activate” signal to the handler.</p>
|
||||
<p>The example <code>list4</code> launches <code>tfe</code> text file editor if the item of the list is a text file.</p>
|
||||
<div class="sourceCode" id="cb10"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true"></a>list_activate (GtkListView *list, <span class="dt">int</span> position, gpointer user_data) {</span>
|
||||
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true"></a> GFileInfo *info = G_FILE_INFO (g_list_model_get_item (G_LIST_MODEL (gtk_list_view_get_model (list)), position));</span>
|
||||
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true"></a> launch_tfe_with_file (info);</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>
|
||||
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true"></a>grid_activate (GtkGridView *grid, <span class="dt">int</span> position, gpointer user_data) {</span>
|
||||
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true"></a> GFileInfo *info = G_FILE_INFO (g_list_model_get_item (G_LIST_MODEL (gtk_grid_view_get_model (grid)), position));</span>
|
||||
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true"></a> launch_tfe_with_file (info);</span>
|
||||
<span id="cb10-11"><a href="#cb10-11" aria-hidden="true"></a>}</span>
|
||||
<span id="cb10-12"><a href="#cb10-12" aria-hidden="true"></a></span>
|
||||
<span id="cb10-13"><a href="#cb10-13" aria-hidden="true"></a>... ...</span>
|
||||
<span id="cb10-14"><a href="#cb10-14" aria-hidden="true"></a>... ...</span>
|
||||
<span id="cb10-15"><a href="#cb10-15" aria-hidden="true"></a></span>
|
||||
<span id="cb10-16"><a href="#cb10-16" aria-hidden="true"></a> g_signal_connect (GTK_LIST_VIEW (list), <span class="st">"activate"</span>, G_CALLBACK (list_activate), NULL);</span>
|
||||
<span id="cb10-17"><a href="#cb10-17" aria-hidden="true"></a> g_signal_connect (GTK_GRID_VIEW (grid), <span class="st">"activate"</span>, G_CALLBACK (grid_activate), NULL);</span></code></pre></div>
|
||||
<p>The second parameter of the handlers is the position of the item (GFileInfo) of the GListModel. So you can get the item with <code>g_list_model_get_item</code> function.</p>
|
||||
<h2 id="content-type-and-launching-an-application">Content type and launching an application</h2>
|
||||
<p>The function <code>launch_tfe_with_file</code> gets a file from the GFileInfo instance. If the file is a text file, it launches <code>tfe</code> with the file.</p>
|
||||
<p>GFileInfo has information about file type. The file type is like “text/plain”, “text/x-csrc” and so on. It is called content type. Content type can be got with <code>g_file_info_get_content_type</code> function.</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>launch_tfe_with_file (GFileInfo *info) {</span>
|
||||
<span id="cb11-3"><a href="#cb11-3"></a> GError *err = NULL;</span>
|
||||
<span id="cb11-4"><a href="#cb11-4"></a> GFile *file;</span>
|
||||
<span id="cb11-5"><a href="#cb11-5"></a> GList *files = NULL;</span>
|
||||
<span id="cb11-6"><a href="#cb11-6"></a> <span class="dt">const</span> <span class="dt">char</span> *content_type;</span>
|
||||
<span id="cb11-7"><a href="#cb11-7"></a> <span class="dt">const</span> <span class="dt">char</span> *text_type = <span class="st">"text/"</span>;</span>
|
||||
<span id="cb11-8"><a href="#cb11-8"></a> GAppInfo *appinfo;</span>
|
||||
<span id="cb11-9"><a href="#cb11-9"></a> <span class="dt">int</span> i;</span>
|
||||
<span id="cb11-10"><a href="#cb11-10"></a></span>
|
||||
<span id="cb11-11"><a href="#cb11-11"></a> <span class="cf">if</span> (! info)</span>
|
||||
<span id="cb11-12"><a href="#cb11-12"></a> <span class="cf">return</span>;</span>
|
||||
<span id="cb11-13"><a href="#cb11-13"></a> content_type = g_file_info_get_content_type (info);</span>
|
||||
<span id="cb11-14"><a href="#cb11-14"></a>g_print (<span class="st">"%s</span><span class="sc">\n</span><span class="st">"</span>, content_type); <span class="co">/* This line can be commented out if unnecessary */</span></span>
|
||||
<span id="cb11-15"><a href="#cb11-15"></a> <span class="cf">if</span> (! content_type)</span>
|
||||
<span id="cb11-16"><a href="#cb11-16"></a> <span class="cf">return</span>;</span>
|
||||
<span id="cb11-17"><a href="#cb11-17"></a> <span class="cf">for</span> (i=<span class="dv">0</span>;i<<span class="dv">5</span>;++i) {</span>
|
||||
<span id="cb11-18"><a href="#cb11-18"></a> <span class="cf">if</span> (content_type[i] != text_type[i])</span>
|
||||
<span id="cb11-19"><a href="#cb11-19"></a> <span class="cf">return</span>;</span>
|
||||
<span id="cb11-20"><a href="#cb11-20"></a> }</span>
|
||||
<span id="cb11-21"><a href="#cb11-21"></a> appinfo = g_app_info_create_from_commandline (<span class="st">"tfe"</span>, <span class="st">"tfe"</span>, G_APP_INFO_CREATE_NONE, &err);</span>
|
||||
<span id="cb11-22"><a href="#cb11-22"></a> <span class="cf">if</span> (err) {</span>
|
||||
<span id="cb11-23"><a href="#cb11-23"></a> g_printerr (<span class="st">"%s</span><span class="sc">\n</span><span class="st">"</span>, err->message);</span>
|
||||
<span id="cb11-24"><a href="#cb11-24"></a> g_error_free (err);</span>
|
||||
<span id="cb11-25"><a href="#cb11-25"></a> <span class="cf">return</span>;</span>
|
||||
<span id="cb11-26"><a href="#cb11-26"></a> }</span>
|
||||
<span id="cb11-27"><a href="#cb11-27"></a> err = NULL;</span>
|
||||
<span id="cb11-28"><a href="#cb11-28"></a> file = g_file_new_for_path (g_file_info_get_name (info));</span>
|
||||
<span id="cb11-29"><a href="#cb11-29"></a> files = g_list_append (files, file);</span>
|
||||
<span id="cb11-30"><a href="#cb11-30"></a> <span class="cf">if</span> (! (g_app_info_launch (appinfo, files, NULL, &err))) {</span>
|
||||
<span id="cb11-31"><a href="#cb11-31"></a> g_printerr (<span class="st">"%s</span><span class="sc">\n</span><span class="st">"</span>, err->message);</span>
|
||||
<span id="cb11-32"><a href="#cb11-32"></a> g_error_free (err);</span>
|
||||
<span id="cb11-33"><a href="#cb11-33"></a> }</span>
|
||||
<span id="cb11-34"><a href="#cb11-34"></a> g_list_free_full (files, g_object_unref);</span>
|
||||
<span id="cb11-35"><a href="#cb11-35"></a> g_object_unref (appinfo);</span>
|
||||
<span id="cb11-36"><a href="#cb11-36"></a>}</span></code></pre></div>
|
||||
<ul>
|
||||
<li>13: Gets the content type of the file from GFileInfo.</li>
|
||||
<li>14: Prints the content type. This is only useful to know a content type of a file. You can delete it if unnecessary.</li>
|
||||
<li>17-20: If the content type doesn’t begin with “text/”, then it returns.</li>
|
||||
<li>21: Creates GAppInfo object of <code>tfe</code> application. GAppInfo is an interface and the variable <code>appinfo</code> points a GDesktopAppInfo instance. GAppInfo is a collection of information of an application.</li>
|
||||
<li>30: Launches the application (<code>tfe</code>) with an argument <code>file</code>. <code>g_app_info_launch</code> has four parameters. The first parameter is GAppInfo object. The second parameter is a list of GFile objects. In this function, only one GFile instance is given to <code>tfe</code>, but you can give more arguments. The third parameter is GAppLaunchContext, but this program gives NULL instead. The last parameter is the pointer to the pointer to a GError.</li>
|
||||
<li>34: <code>g_list_free_full</code> frees the memories used by the list and items.</li>
|
||||
</ul>
|
||||
<p>If your distribution supports Gtk4, using <code>g_app_info_launch_default_for_uri</code> is convenient. The function automatically determines the default application from the file and launches it. For example, if the file is text, then it launches gedit with the file. Such functionality comes from desktop.</p>
|
||||
<h2 id="compilation-and-execution">Compilation and execution</h2>
|
||||
<p>The source files are located in <a href="../src/list4">src/list4</a> directory. To compile and execute list4, type as follows.</p>
|
||||
<pre><code>$ cd list4 # or cd src/list4. It depends your current directory.
|
||||
$ meson _build
|
||||
$ ninja -C _build
|
||||
$ _build/list4</code></pre>
|
||||
<p>Then a file list appears as a list style. Click on a button on the tool bar so that you can change the style to grid or back to list. Double click “list4.c” item, then <code>tfe</code> text editor runs with the argument “list4.c”. The following is the screenshot.</p>
|
||||
<figure>
|
||||
<img src="../image/screenshot_list4.png" alt="" /><figcaption>Screenshot</figcaption>
|
||||
</figure>
|
||||
<h2 id="gbytes-property-of-gtkbuilderlistitemfactory">“gbytes” property of GtkBuilderListItemFactory</h2>
|
||||
<p>GtkBuilderListItemFactory has “gbytes” property. The property contains a byte sequence of ui data. If you use this property, you can put the contents of <code>factory_list.ui</code> and <code>factory_grid.ui</code>into <code>list4.ui</code>. The following shows a part of the new ui file (<code>list5.ui</code>).</p>
|
||||
<div class="sourceCode" id="cb13"><pre class="sourceCode xml"><code class="sourceCode xml"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkListView"</span><span class="ot"> id=</span><span class="st">"list"</span><span class="kw">></span></span>
|
||||
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"model"</span><span class="kw">></span></span>
|
||||
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkSingleSelection"</span><span class="ot"> id=</span><span class="st">"singleselection"</span><span class="kw">></span></span>
|
||||
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"model"</span><span class="kw">></span></span>
|
||||
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkDirectoryList"</span><span class="ot"> id=</span><span class="st">"directorylist"</span><span class="kw">></span></span>
|
||||
<span id="cb13-6"><a href="#cb13-6" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"attributes"</span><span class="kw">></span>standard::name,standard::icon,standard::content-type<span class="kw"></property></span></span>
|
||||
<span id="cb13-7"><a href="#cb13-7" aria-hidden="true"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb13-8"><a href="#cb13-8" aria-hidden="true"></a> <span class="kw"></property></span></span>
|
||||
<span id="cb13-9"><a href="#cb13-9" aria-hidden="true"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb13-10"><a href="#cb13-10" aria-hidden="true"></a> <span class="kw"></property></span></span>
|
||||
<span id="cb13-11"><a href="#cb13-11" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"factory"</span><span class="kw">></span></span>
|
||||
<span id="cb13-12"><a href="#cb13-12" aria-hidden="true"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkBuilderListItemFactory"</span><span class="kw">></span></span>
|
||||
<span id="cb13-13"><a href="#cb13-13" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"bytes"</span><span class="kw">></span><span class="bn"><![CDATA[</span></span>
|
||||
<span id="cb13-14"><a href="#cb13-14" aria-hidden="true"></a><?xml version="1.0" encoding="UTF-8"?></span>
|
||||
<span id="cb13-15"><a href="#cb13-15" aria-hidden="true"></a><interface></span>
|
||||
<span id="cb13-16"><a href="#cb13-16" aria-hidden="true"></a> <template class="GtkListItem"></span>
|
||||
<span id="cb13-17"><a href="#cb13-17" aria-hidden="true"></a> <property name="child"></span>
|
||||
<span id="cb13-18"><a href="#cb13-18" aria-hidden="true"></a> <object class="GtkBox"></span>
|
||||
<span id="cb13-19"><a href="#cb13-19" aria-hidden="true"></a> <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property></span>
|
||||
<span id="cb13-20"><a href="#cb13-20" aria-hidden="true"></a> <property name="spacing">20</property></span>
|
||||
<span id="cb13-21"><a href="#cb13-21" aria-hidden="true"></a> <child></span>
|
||||
<span id="cb13-22"><a href="#cb13-22" aria-hidden="true"></a> <object class="GtkImage"></span>
|
||||
<span id="cb13-23"><a href="#cb13-23" aria-hidden="true"></a> <binding name="gicon"></span>
|
||||
<span id="cb13-24"><a href="#cb13-24" aria-hidden="true"></a> <closure type="GIcon" function="get_icon"></span>
|
||||
<span id="cb13-25"><a href="#cb13-25" aria-hidden="true"></a> <lookup name="item">GtkListItem</lookup></span>
|
||||
<span id="cb13-26"><a href="#cb13-26" aria-hidden="true"></a> </closure></span>
|
||||
<span id="cb13-27"><a href="#cb13-27" aria-hidden="true"></a> </binding></span>
|
||||
<span id="cb13-28"><a href="#cb13-28" aria-hidden="true"></a> </object></span>
|
||||
<span id="cb13-29"><a href="#cb13-29" aria-hidden="true"></a> </child></span>
|
||||
<span id="cb13-30"><a href="#cb13-30" aria-hidden="true"></a> <child></span>
|
||||
<span id="cb13-31"><a href="#cb13-31" aria-hidden="true"></a> <object class="GtkLabel"></span>
|
||||
<span id="cb13-32"><a href="#cb13-32" aria-hidden="true"></a> <property name="hexpand">TRUE</property></span>
|
||||
<span id="cb13-33"><a href="#cb13-33" aria-hidden="true"></a> <property name="xalign">0</property></span>
|
||||
<span id="cb13-34"><a href="#cb13-34" aria-hidden="true"></a> <binding name="label"></span>
|
||||
<span id="cb13-35"><a href="#cb13-35" aria-hidden="true"></a> <closure type="gchararray" function="get_file_name"></span>
|
||||
<span id="cb13-36"><a href="#cb13-36" aria-hidden="true"></a> <lookup name="item">GtkListItem</lookup></span>
|
||||
<span id="cb13-37"><a href="#cb13-37" aria-hidden="true"></a> </closure></span>
|
||||
<span id="cb13-38"><a href="#cb13-38" aria-hidden="true"></a> </binding></span>
|
||||
<span id="cb13-39"><a href="#cb13-39" aria-hidden="true"></a> </object></span>
|
||||
<span id="cb13-40"><a href="#cb13-40" aria-hidden="true"></a> </child></span>
|
||||
<span id="cb13-41"><a href="#cb13-41" aria-hidden="true"></a> </object></span>
|
||||
<span id="cb13-42"><a href="#cb13-42" aria-hidden="true"></a> </property></span>
|
||||
<span id="cb13-43"><a href="#cb13-43" aria-hidden="true"></a> </template></span>
|
||||
<span id="cb13-44"><a href="#cb13-44" aria-hidden="true"></a></interface></span>
|
||||
<span id="cb13-45"><a href="#cb13-45" aria-hidden="true"></a> <span class="bn">]]></span><span class="kw"></property></span></span>
|
||||
<span id="cb13-46"><a href="#cb13-46" aria-hidden="true"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb13-47"><a href="#cb13-47" aria-hidden="true"></a> <span class="kw"></property></span></span>
|
||||
<span id="cb13-48"><a href="#cb13-48" aria-hidden="true"></a> <span class="kw"></object></span></span></code></pre></div>
|
||||
<p>CDATA section begins with “<[CDATA[" and ends with "]]>”. The contents of CDATA section is recognized as a string. Any character, even if it is a key syntax marker such as ‘<’ or ‘>’, is recognized literally. Therefore, the text between “<[CDATA[" and "]]>” is inserted to “bytes” property as it is.</p>
|
||||
<p>This method decreases the number of ui files. But, the new ui file is a bit complicated especially for the beginners. If you feel some difficulty, it is better for you to separate the ui file.</p>
|
||||
<p>A directory <a href="../src/list5">src/list5</a> includes the ui file above.</p>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec26.html">Section 26</a>, Next: <a href="sec28.html">Section 28</a></p>
|
||||
</body>
|
||||
</html>
|
517
docs/sec28.html
Normal file
517
docs/sec28.html
Normal file
|
@ -0,0 +1,517 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>Gtk4 tutorial for beginners</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
body {width: 1080px; margin: 0 auto; font-size: large;}
|
||||
h2 {padding: 10px; background-color: #d0f0d0; }
|
||||
div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
|
||||
th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
|
||||
td {padding: 2px 6px; border: 1px solid;}
|
||||
img {display: block; margin-left: auto; margin-right: auto;}
|
||||
figcaption {text-align: center;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec27.html">Section 27</a>, Next: <a href="sec29.html">Section 29</a></p>
|
||||
<h1 id="gtkexpression">GtkExpression</h1>
|
||||
<p>GtkExpression is a fundamental type. It is not a descendant of GObject. GtkExpression provides a way to describe references to values. GtkExpression needs to be evaluated to obtain a value.</p>
|
||||
<p>It is similar to arithmetic calculation.</p>
|
||||
<pre><code>1 + 2 = 3</code></pre>
|
||||
<p><code>1+2</code> is an expression. It shows the way how to calculate. <code>3</code> is the value comes from the expression. Evaluation is to calculate the expression and get the value.</p>
|
||||
<p>GtkExpression is a way to get a value. Evaluation is like a calculation. A value is got by evaluating the expression.</p>
|
||||
<p>First, I want to show you the C file of the example for GtkExpression. Its name is <code>exp.c</code> and located under <a href="../src/expression">src/expression</a> directory. You don’t need to understand the details now, just look at it. It will be explained in the next subsection.</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>
|
||||
<span id="cb2-2"><a href="#cb2-2"></a></span>
|
||||
<span id="cb2-3"><a href="#cb2-3"></a>GtkWidget *win1;</span>
|
||||
<span id="cb2-4"><a href="#cb2-4"></a><span class="dt">int</span> width, height;</span>
|
||||
<span id="cb2-5"><a href="#cb2-5"></a>GtkExpressionWatch *watch_width;</span>
|
||||
<span id="cb2-6"><a href="#cb2-6"></a>GtkExpressionWatch *watch_height;</span>
|
||||
<span id="cb2-7"><a href="#cb2-7"></a></span>
|
||||
<span id="cb2-8"><a href="#cb2-8"></a><span class="co">/* Notify is called when "default-width" or "default-height" property is changed. */</span></span>
|
||||
<span id="cb2-9"><a href="#cb2-9"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb2-10"><a href="#cb2-10"></a>notify (gpointer user_data) {</span>
|
||||
<span id="cb2-11"><a href="#cb2-11"></a> GValue value = G_VALUE_INIT;</span>
|
||||
<span id="cb2-12"><a href="#cb2-12"></a> <span class="dt">char</span> *title;</span>
|
||||
<span id="cb2-13"><a href="#cb2-13"></a></span>
|
||||
<span id="cb2-14"><a href="#cb2-14"></a> <span class="cf">if</span> (watch_width && gtk_expression_watch_evaluate (watch_width, &value))</span>
|
||||
<span id="cb2-15"><a href="#cb2-15"></a> width = g_value_get_int (&value);</span>
|
||||
<span id="cb2-16"><a href="#cb2-16"></a> g_value_unset (&value);</span>
|
||||
<span id="cb2-17"><a href="#cb2-17"></a> <span class="cf">if</span> (watch_height && gtk_expression_watch_evaluate (watch_height, &value))</span>
|
||||
<span id="cb2-18"><a href="#cb2-18"></a> height = g_value_get_int (&value);</span>
|
||||
<span id="cb2-19"><a href="#cb2-19"></a> g_value_unset (&value);</span>
|
||||
<span id="cb2-20"><a href="#cb2-20"></a> title = g_strdup_printf (<span class="st">"%d x %d"</span>, width, height);</span>
|
||||
<span id="cb2-21"><a href="#cb2-21"></a> gtk_window_set_title (GTK_WINDOW (win1), title);</span>
|
||||
<span id="cb2-22"><a href="#cb2-22"></a> g_free (title);</span>
|
||||
<span id="cb2-23"><a href="#cb2-23"></a>}</span>
|
||||
<span id="cb2-24"><a href="#cb2-24"></a></span>
|
||||
<span id="cb2-25"><a href="#cb2-25"></a><span class="co">/* This function is used by closure tag in exp.ui. */</span></span>
|
||||
<span id="cb2-26"><a href="#cb2-26"></a><span class="dt">char</span> *</span>
|
||||
<span id="cb2-27"><a href="#cb2-27"></a>set_title (GtkWidget *win, <span class="dt">int</span> width, <span class="dt">int</span> height) {</span>
|
||||
<span id="cb2-28"><a href="#cb2-28"></a> <span class="cf">return</span> g_strdup_printf (<span class="st">"%d x %d"</span>, width, height);</span>
|
||||
<span id="cb2-29"><a href="#cb2-29"></a>}</span>
|
||||
<span id="cb2-30"><a href="#cb2-30"></a></span>
|
||||
<span id="cb2-31"><a href="#cb2-31"></a><span class="co">/* ----- activate, open, startup handlers ----- */</span></span>
|
||||
<span id="cb2-32"><a href="#cb2-32"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb2-33"><a href="#cb2-33"></a>app_activate (GApplication *application) {</span>
|
||||
<span id="cb2-34"><a href="#cb2-34"></a> GtkApplication *app = GTK_APPLICATION (application);</span>
|
||||
<span id="cb2-35"><a href="#cb2-35"></a> GtkWidget *box;</span>
|
||||
<span id="cb2-36"><a href="#cb2-36"></a> GtkWidget *label1, *label2, *label3;</span>
|
||||
<span id="cb2-37"><a href="#cb2-37"></a> GtkWidget *entry;</span>
|
||||
<span id="cb2-38"><a href="#cb2-38"></a> GtkEntryBuffer *buffer;</span>
|
||||
<span id="cb2-39"><a href="#cb2-39"></a> GtkBuilder *build;</span>
|
||||
<span id="cb2-40"><a href="#cb2-40"></a> GtkExpression *expression, *expression1, *expression2;</span>
|
||||
<span id="cb2-41"><a href="#cb2-41"></a> GValue value = G_VALUE_INIT;</span>
|
||||
<span id="cb2-42"><a href="#cb2-42"></a> <span class="dt">char</span> *s;</span>
|
||||
<span id="cb2-43"><a href="#cb2-43"></a></span>
|
||||
<span id="cb2-44"><a href="#cb2-44"></a> <span class="co">/* Creates GtkApplicationWindow instance. */</span></span>
|
||||
<span id="cb2-45"><a href="#cb2-45"></a> <span class="co">/* The codes below are complecated. It does the same as "win1 = gtk_application_window_new (app);". */</span></span>
|
||||
<span id="cb2-46"><a href="#cb2-46"></a> <span class="co">/* The codes are written just to show how to use GtkExpression. */</span></span>
|
||||
<span id="cb2-47"><a href="#cb2-47"></a> expression = gtk_cclosure_expression_new (GTK_TYPE_APPLICATION_WINDOW, NULL, <span class="dv">0</span>, NULL,</span>
|
||||
<span id="cb2-48"><a href="#cb2-48"></a> G_CALLBACK (gtk_application_window_new), NULL, NULL);</span>
|
||||
<span id="cb2-49"><a href="#cb2-49"></a> <span class="cf">if</span> (gtk_expression_evaluate (expression, app, &value)) {</span>
|
||||
<span id="cb2-50"><a href="#cb2-50"></a> win1 = GTK_WIDGET (g_value_get_object (&value)); <span class="co">/* GtkApplicationWindow */</span></span>
|
||||
<span id="cb2-51"><a href="#cb2-51"></a> g_object_ref (win1);</span>
|
||||
<span id="cb2-52"><a href="#cb2-52"></a> g_print (<span class="st">"Got GtkApplicationWindow instance.</span><span class="sc">\n</span><span class="st">"</span>);</span>
|
||||
<span id="cb2-53"><a href="#cb2-53"></a> }<span class="cf">else</span></span>
|
||||
<span id="cb2-54"><a href="#cb2-54"></a> g_print (<span class="st">"The cclosure expression wasn't evaluated correctly.</span><span class="sc">\n</span><span class="st">"</span>);</span>
|
||||
<span id="cb2-55"><a href="#cb2-55"></a> gtk_expression_unref (expression);</span>
|
||||
<span id="cb2-56"><a href="#cb2-56"></a> g_value_unset (&value); <span class="co">/* At the same time, the reference count of win1 is decreased by one. */</span></span>
|
||||
<span id="cb2-57"><a href="#cb2-57"></a></span>
|
||||
<span id="cb2-58"><a href="#cb2-58"></a> <span class="co">/* Builds a window with components */</span></span>
|
||||
<span id="cb2-59"><a href="#cb2-59"></a> box = gtk_box_new (GTK_ORIENTATION_VERTICAL, <span class="dv">10</span>);</span>
|
||||
<span id="cb2-60"><a href="#cb2-60"></a> label1 = gtk_label_new (NULL);</span>
|
||||
<span id="cb2-61"><a href="#cb2-61"></a> label2 = gtk_label_new (NULL);</span>
|
||||
<span id="cb2-62"><a href="#cb2-62"></a> label3 = gtk_label_new (NULL);</span>
|
||||
<span id="cb2-63"><a href="#cb2-63"></a> buffer = gtk_entry_buffer_new (NULL, <span class="dv">0</span>);</span>
|
||||
<span id="cb2-64"><a href="#cb2-64"></a> entry = gtk_entry_new_with_buffer (buffer);</span>
|
||||
<span id="cb2-65"><a href="#cb2-65"></a> gtk_box_append (GTK_BOX (box), label1);</span>
|
||||
<span id="cb2-66"><a href="#cb2-66"></a> gtk_box_append (GTK_BOX (box), label2);</span>
|
||||
<span id="cb2-67"><a href="#cb2-67"></a> gtk_box_append (GTK_BOX (box), label3);</span>
|
||||
<span id="cb2-68"><a href="#cb2-68"></a> gtk_box_append (GTK_BOX (box), entry);</span>
|
||||
<span id="cb2-69"><a href="#cb2-69"></a> gtk_window_set_child (GTK_WINDOW (win1), box);</span>
|
||||
<span id="cb2-70"><a href="#cb2-70"></a></span>
|
||||
<span id="cb2-71"><a href="#cb2-71"></a> <span class="co">/* Constant expression */</span></span>
|
||||
<span id="cb2-72"><a href="#cb2-72"></a> expression = gtk_constant_expression_new (G_TYPE_INT,<span class="dv">100</span>);</span>
|
||||
<span id="cb2-73"><a href="#cb2-73"></a> <span class="cf">if</span> (gtk_expression_evaluate (expression, NULL, &value)) {</span>
|
||||
<span id="cb2-74"><a href="#cb2-74"></a> s = g_strdup_printf (<span class="st">"%d"</span>, g_value_get_int (&value));</span>
|
||||
<span id="cb2-75"><a href="#cb2-75"></a> gtk_label_set_text (GTK_LABEL (label1), s);</span>
|
||||
<span id="cb2-76"><a href="#cb2-76"></a> g_free (s);</span>
|
||||
<span id="cb2-77"><a href="#cb2-77"></a> } <span class="cf">else</span></span>
|
||||
<span id="cb2-78"><a href="#cb2-78"></a> g_print (<span class="st">"The constant expression wasn't evaluated correctly.</span><span class="sc">\n</span><span class="st">"</span>);</span>
|
||||
<span id="cb2-79"><a href="#cb2-79"></a> gtk_expression_unref (expression);</span>
|
||||
<span id="cb2-80"><a href="#cb2-80"></a> g_value_unset (&value);</span>
|
||||
<span id="cb2-81"><a href="#cb2-81"></a></span>
|
||||
<span id="cb2-82"><a href="#cb2-82"></a> <span class="co">/* Property expression and binding*/</span></span>
|
||||
<span id="cb2-83"><a href="#cb2-83"></a> expression1 = gtk_property_expression_new (GTK_TYPE_ENTRY, NULL, <span class="st">"buffer"</span>);</span>
|
||||
<span id="cb2-84"><a href="#cb2-84"></a> expression2 = gtk_property_expression_new (GTK_TYPE_ENTRY_BUFFER, expression1, <span class="st">"text"</span>);</span>
|
||||
<span id="cb2-85"><a href="#cb2-85"></a> gtk_expression_bind (expression2, label2, <span class="st">"label"</span>, entry);</span>
|
||||
<span id="cb2-86"><a href="#cb2-86"></a></span>
|
||||
<span id="cb2-87"><a href="#cb2-87"></a> <span class="co">/* Constant expression instead of "this" instance */</span></span>
|
||||
<span id="cb2-88"><a href="#cb2-88"></a> expression1 = gtk_constant_expression_new (GTK_TYPE_APPLICATION, app);</span>
|
||||
<span id="cb2-89"><a href="#cb2-89"></a> expression2 = gtk_property_expression_new (GTK_TYPE_APPLICATION, expression1, <span class="st">"application-id"</span>);</span>
|
||||
<span id="cb2-90"><a href="#cb2-90"></a> <span class="cf">if</span> (gtk_expression_evaluate (expression2, NULL, &value))</span>
|
||||
<span id="cb2-91"><a href="#cb2-91"></a> gtk_label_set_text (GTK_LABEL (label3), g_value_get_string (&value));</span>
|
||||
<span id="cb2-92"><a href="#cb2-92"></a> <span class="cf">else</span></span>
|
||||
<span id="cb2-93"><a href="#cb2-93"></a> g_print (<span class="st">"The property expression wasn't evaluated correctly.</span><span class="sc">\n</span><span class="st">"</span>);</span>
|
||||
<span id="cb2-94"><a href="#cb2-94"></a> gtk_expression_unref (expression1); <span class="co">/* expression 2 is also freed. */</span></span>
|
||||
<span id="cb2-95"><a href="#cb2-95"></a> g_value_unset (&value);</span>
|
||||
<span id="cb2-96"><a href="#cb2-96"></a></span>
|
||||
<span id="cb2-97"><a href="#cb2-97"></a> width = <span class="dv">800</span>;</span>
|
||||
<span id="cb2-98"><a href="#cb2-98"></a> height = <span class="dv">600</span>;</span>
|
||||
<span id="cb2-99"><a href="#cb2-99"></a> gtk_window_set_default_size (GTK_WINDOW (win1), width, height);</span>
|
||||
<span id="cb2-100"><a href="#cb2-100"></a> notify(NULL);</span>
|
||||
<span id="cb2-101"><a href="#cb2-101"></a></span>
|
||||
<span id="cb2-102"><a href="#cb2-102"></a> <span class="co">/* GtkExpressionWatch */</span></span>
|
||||
<span id="cb2-103"><a href="#cb2-103"></a> expression1 = gtk_property_expression_new (GTK_TYPE_WINDOW, NULL, <span class="st">"default-width"</span>);</span>
|
||||
<span id="cb2-104"><a href="#cb2-104"></a> watch_width = gtk_expression_watch (expression1, win1, notify, NULL, NULL);</span>
|
||||
<span id="cb2-105"><a href="#cb2-105"></a> expression2 = gtk_property_expression_new (GTK_TYPE_WINDOW, NULL, <span class="st">"default-height"</span>);</span>
|
||||
<span id="cb2-106"><a href="#cb2-106"></a> watch_height = gtk_expression_watch (expression2, win1, notify, NULL, NULL);</span>
|
||||
<span id="cb2-107"><a href="#cb2-107"></a></span>
|
||||
<span id="cb2-108"><a href="#cb2-108"></a> gtk_widget_show (win1);</span>
|
||||
<span id="cb2-109"><a href="#cb2-109"></a></span>
|
||||
<span id="cb2-110"><a href="#cb2-110"></a> <span class="co">/* Builds a window with exp.ui resource */</span></span>
|
||||
<span id="cb2-111"><a href="#cb2-111"></a> build = gtk_builder_new_from_resource (<span class="st">"/com/github/ToshioCP/exp/exp.ui"</span>);</span>
|
||||
<span id="cb2-112"><a href="#cb2-112"></a> GtkWidget *win2 = GTK_WIDGET (gtk_builder_get_object (build, <span class="st">"win2"</span>));</span>
|
||||
<span id="cb2-113"><a href="#cb2-113"></a> gtk_window_set_application (GTK_WINDOW (win2), app);</span>
|
||||
<span id="cb2-114"><a href="#cb2-114"></a> g_object_unref (build);</span>
|
||||
<span id="cb2-115"><a href="#cb2-115"></a></span>
|
||||
<span id="cb2-116"><a href="#cb2-116"></a> gtk_widget_show (win2);</span>
|
||||
<span id="cb2-117"><a href="#cb2-117"></a>}</span>
|
||||
<span id="cb2-118"><a href="#cb2-118"></a></span>
|
||||
<span id="cb2-119"><a href="#cb2-119"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb2-120"><a href="#cb2-120"></a>app_startup (GApplication *application) {</span>
|
||||
<span id="cb2-121"><a href="#cb2-121"></a>}</span>
|
||||
<span id="cb2-122"><a href="#cb2-122"></a></span>
|
||||
<span id="cb2-123"><a href="#cb2-123"></a><span class="pp">#define APPLICATION_ID "com.github.ToshioCP.exp"</span></span>
|
||||
<span id="cb2-124"><a href="#cb2-124"></a></span>
|
||||
<span id="cb2-125"><a href="#cb2-125"></a><span class="dt">int</span></span>
|
||||
<span id="cb2-126"><a href="#cb2-126"></a>main (<span class="dt">int</span> argc, <span class="dt">char</span> **argv) {</span>
|
||||
<span id="cb2-127"><a href="#cb2-127"></a> GtkApplication *app;</span>
|
||||
<span id="cb2-128"><a href="#cb2-128"></a> <span class="dt">int</span> stat;</span>
|
||||
<span id="cb2-129"><a href="#cb2-129"></a></span>
|
||||
<span id="cb2-130"><a href="#cb2-130"></a> app = gtk_application_new (APPLICATION_ID, G_APPLICATION_FLAGS_NONE);</span>
|
||||
<span id="cb2-131"><a href="#cb2-131"></a></span>
|
||||
<span id="cb2-132"><a href="#cb2-132"></a> g_signal_connect (app, <span class="st">"startup"</span>, G_CALLBACK (app_startup), NULL);</span>
|
||||
<span id="cb2-133"><a href="#cb2-133"></a> g_signal_connect (app, <span class="st">"activate"</span>, G_CALLBACK (app_activate), NULL);</span>
|
||||
<span id="cb2-134"><a href="#cb2-134"></a><span class="co">/* g_signal_connect (app, "open", G_CALLBACK (app_open), NULL);*/</span></span>
|
||||
<span id="cb2-135"><a href="#cb2-135"></a></span>
|
||||
<span id="cb2-136"><a href="#cb2-136"></a> stat =g_application_run (G_APPLICATION (app), argc, argv);</span>
|
||||
<span id="cb2-137"><a href="#cb2-137"></a> g_object_unref (app);</span>
|
||||
<span id="cb2-138"><a href="#cb2-138"></a> <span class="cf">return</span> stat;</span>
|
||||
<span id="cb2-139"><a href="#cb2-139"></a>}</span></code></pre></div>
|
||||
<p><code>exp.c</code> consists of five functions.</p>
|
||||
<ul>
|
||||
<li><code>notify</code></li>
|
||||
<li><code>set_title</code></li>
|
||||
<li><code>app_activate</code>. This is a handler of “activate” signal on GtkApplication instance.</li>
|
||||
<li><code>app_startup</code>. This is a handler of “startup”signal. But nothing is done in this function.</li>
|
||||
<li><code>main</code>.</li>
|
||||
</ul>
|
||||
<p>The function <code>app_activate</code> is an actual main body in <code>exp.c</code>.</p>
|
||||
<h2 id="constant-expression">Constant expression</h2>
|
||||
<p>Constant expression provides constant value or instance when it is evaluated.</p>
|
||||
<ul>
|
||||
<li>72-80: A constant expression. It is extracted and put into here.</li>
|
||||
</ul>
|
||||
<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> expression = gtk_constant_expression_new (G_TYPE_INT,<span class="dv">100</span>);</span>
|
||||
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true"></a> <span class="cf">if</span> (gtk_expression_evaluate (expression, NULL, &value)) {</span>
|
||||
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true"></a> s = g_strdup_printf (<span class="st">"%d"</span>, g_value_get_int (&value));</span>
|
||||
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true"></a> gtk_label_set_text (GTK_LABEL (label1), s);</span>
|
||||
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true"></a> g_free (s);</span>
|
||||
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true"></a> } <span class="cf">else</span></span>
|
||||
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true"></a> g_print (<span class="st">"The constant expression wasn't evaluated correctly.</span><span class="sc">\n</span><span class="st">"</span>);</span>
|
||||
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true"></a> gtk_expression_unref (expression);</span>
|
||||
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true"></a> g_value_unset (&value);</span></code></pre></div>
|
||||
<ul>
|
||||
<li>Constant expression is created with <code>gtk_constant_expression_new</code> function. The parameter of the function is a type (GType) and a value (or instance).</li>
|
||||
<li><code>gtk_expression_evaluate</code> evaluates the expression. It has three parameters, the expression to evaluate, <code>this</code> instance and GValue for being set with the value. <code>this</code> instance isn’t necessary for constant expressions. Therefore the second argument is NULL. <code>gtk_expression_evaluate</code> returns TRUE if it successfully evaluates the expression. Otherwise it returns FALSE.</li>
|
||||
<li>If it returns TRUE, the GValue <code>value</code> is set with the value of the expression. The type of the value is int. <code>g_strdup_printf</code> converts the value to a string <code>s</code>.</li>
|
||||
<li>GtkLabel <code>label1</code> is set with <code>s</code>. The string <code>s</code> needs to be freed.</li>
|
||||
<li>If the evaluation fails a message is outputted to stderr.</li>
|
||||
<li>The expression and GValue are freed.</li>
|
||||
</ul>
|
||||
<p>Constant expression is usually used to give a constant value or instance to another expression.</p>
|
||||
<h2 id="property-expression">Property expression</h2>
|
||||
<p>Property expression looks up a property in a GObject object. For example, a property expression that refers “label” property in GtkLabel object is created like this.</p>
|
||||
<div class="sourceCode" id="cb4"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true"></a>expression = gtk_property_expression_new (GTK_TYPE_LABEL, another_expression, <span class="st">"label"</span>);</span></code></pre></div>
|
||||
<p><code>another_expression</code> is expected to give a GtkLabel instance when it is evaluated. For example,</p>
|
||||
<div class="sourceCode" id="cb5"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true"></a>label = gtk_label_new (<span class="st">"Hello"</span>);</span>
|
||||
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true"></a>another_expression = gtk_constant_expression_new (GTK_TYPE_LABEL, label);</span>
|
||||
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true"></a>expression = gtk_property_expression_new (GTK_TYPE_LABEL, another_expression, <span class="st">"label"</span>);</span></code></pre></div>
|
||||
<p>If <code>expression</code> is evaluated, the second parameter <code>another_expression</code> is evaluated in advance. The value of <code>another_expression</code> is <code>label</code> (GtkLabel instance). Then, <code>expression</code> looks up “label” property of <code>label</code> and the evaluation result is “Hello”.</p>
|
||||
<p>In the example above, the second argument of <code>gtk_property_expression_new</code> is another expression. But the second argument can be NULL. If it is NULL, <code>this</code> instance is used instead. <code>this</code> is given by <code>gtk_expression_evaluate</code> function at the evaluation.</p>
|
||||
<p>Now look at <code>exp.c</code>. The lines from 83 to 85 is extracted here.</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> expression1 = gtk_property_expression_new (GTK_TYPE_ENTRY, NULL, <span class="st">"buffer"</span>);</span>
|
||||
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true"></a> expression2 = gtk_property_expression_new (GTK_TYPE_ENTRY_BUFFER, expression1, <span class="st">"text"</span>);</span>
|
||||
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true"></a> gtk_expression_bind (expression2, label2, <span class="st">"label"</span>, entry);</span></code></pre></div>
|
||||
<ul>
|
||||
<li><code>expression1</code> looks up “buffer” property of <code>this</code> object, which is <code>GTK_TYPE_ENTRY</code> type.</li>
|
||||
<li><code>expression2</code> looks up “text” property of GtkEntryBuffer object given by <code>epression1</code>.</li>
|
||||
<li><code>gtk_expression_bind</code> binds a property to a value given by the expression. In this program, it binds a “label” property in <code>label2</code> to the value evaluated with <code>expresion2</code> with <code>entry</code> as <code>this</code> object. The evaluation process is as follows.
|
||||
<ol type="1">
|
||||
<li><code>expression2</code> is evaluated. But it includes <code>expression1</code> so <code>expression1</code> is evaluated in advance.</li>
|
||||
<li>Because the second argument of <code>expression1</code> is NULL, <code>this</code> object is used. <code>this</code> is given by <code>gtk_expression_bind</code>. It is <code>entry</code> (GtkEntry instance). <code>expression1</code> looks up “buffer” property in <code>entry</code>. It is a GtkEntryBuffer instance <code>buffer</code>. (See line 64 in <code>exp.c</code>.)</li>
|
||||
<li>Then, <code>expression2</code> looks up “text” property in <code>buffer</code>. It is a text held in <code>entry</code>.</li>
|
||||
<li>The text is assigned to “label” property in <code>label2</code>.</li>
|
||||
</ol></li>
|
||||
<li><code>gtk_expression_bind</code> creates a GtkExpressionWatch. (But it isn’t assigned to a variable in the program above. If you want to keep the GtkExpressionWatch instance, assign it to a variable.)</li>
|
||||
</ul>
|
||||
<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> GtkExpressionWatch *watch;</span>
|
||||
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true"></a> watch = gtk_expression_bind (expression2, label2, <span class="st">"label"</span>, entry);</span></code></pre></div>
|
||||
<ul>
|
||||
<li>Whenever the value from <code>expression2</code> changes, it evaluates <code>expression2</code> and set “label” property in <code>label2</code>. So, the change of the text in <code>entry</code> makes the “label” property reflect it immediately.</li>
|
||||
</ul>
|
||||
<h2 id="closure-expression">Closure expression</h2>
|
||||
<p>Closure expression calls closure when it is evaluated. A closure is a generic representation of a callback (a pointer to a function). For information about closure, see <a href="https://docs.gtk.org/gobject/concepts.html#the-gobject-messaging-system">GObject API Reference, The GObject messaging system</a>. A closure expression is created with <code>gtk_cclosure_expression_new</code> function.</p>
|
||||
<div class="sourceCode" id="cb8"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true"></a>GtkExpression *</span>
|
||||
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true"></a>gtk_cclosure_expression_new (GType value_type,</span>
|
||||
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true"></a> GClosureMarshal marshal,</span>
|
||||
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true"></a> guint n_params,</span>
|
||||
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true"></a> GtkExpression **params,</span>
|
||||
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true"></a> GCallback callback_func,</span>
|
||||
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true"></a> gpointer user_data,</span>
|
||||
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true"></a> GClosureNotify user_destroy);</span></code></pre></div>
|
||||
<ul>
|
||||
<li><code>value_type</code> is the type of the value when it is evaluated.</li>
|
||||
<li><code>marshal</code> is a marshaller. You can assign NULL. If it is NULL, then <code>g_cclosure_marshal_generic ()</code> is used as a marshaller. It is a generic marshaller function implemented via libffi.</li>
|
||||
<li><code>n_params</code> is the number of parameters.</li>
|
||||
<li><code>params</code> points expressions for each parameter of the call back function.</li>
|
||||
<li><code>callback_func</code> is a callback function.</li>
|
||||
<li><code>user_data</code> is user data. You can add it for the closure. It is like <code>user_data</code> in <code>g_signal_connect</code>. If it is not necessary, assign NULL.</li>
|
||||
<li><code>user_destroy</code> is a destroy notify for <code>user_data</code>. It is called to destroy <code>user_data</code> when it is no longer needed. If NULL is assigned to <code>user_data</code>, assign NULL to <code>user_destroy</code>, too.</li>
|
||||
</ul>
|
||||
<p>The following is extracted from <code>exp.c</code>. It is from line 47 to line 56.</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"></a>expression = gtk_cclosure_expression_new (GTK_TYPE_APPLICATION_WINDOW, NULL, <span class="dv">0</span>, NULL,</span>
|
||||
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true"></a> G_CALLBACK (gtk_application_window_new), NULL, NULL);</span>
|
||||
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true"></a><span class="cf">if</span> (gtk_expression_evaluate (expression, app, &value)) {</span>
|
||||
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true"></a> win1 = GTK_WIDGET (g_value_get_object (&value)); <span class="co">/* GtkApplicationWindow */</span></span>
|
||||
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true"></a> g_object_ref (win1);</span>
|
||||
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true"></a> g_print (<span class="st">"Got GtkApplicationWindow object.</span><span class="sc">\n</span><span class="st">"</span>);</span>
|
||||
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true"></a>}<span class="cf">else</span></span>
|
||||
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true"></a> g_print (<span class="st">"The cclosure expression wasn't evaluated correctly.</span><span class="sc">\n</span><span class="st">"</span>);</span>
|
||||
<span id="cb9-9"><a href="#cb9-9" aria-hidden="true"></a>gtk_expression_unref (expression);</span>
|
||||
<span id="cb9-10"><a href="#cb9-10" aria-hidden="true"></a>g_value_unset (&value); <span class="co">/* At the same time, the reference count of win1 is decreased by one. */</span></span></code></pre></div>
|
||||
<p>The callback function is <code>gtk_application_window_new</code>. This function has one parameter which is an instance of GtkApplication. And it returns newly created GtkApplicationWindow instance. So, the first argument is <code>GTK_TYPE_APPLICATION_WINDOW</code> which is the type of the return value. The second argument is NULL so general marshaller <code>g_cclosure_marshal_generic ()</code> will be used. I think assigning NULL works in most cases when you program in C language.</p>
|
||||
<p>The arguments given to the call back function are <code>this</code> object and parameters which are the fourth argument of <code>gtk_cclosure_expression_new</code>. So, the number of arguments is <code>n_params + 1</code>. Because <code>gtk_application_window_new</code> has one parameter, so <code>n_params</code> is zero and <code>**params</code> is NULL. No user data is necessary, so <code>user_data</code> and <code>user_destroy</code> are NULL.</p>
|
||||
<p><code>gtk_expression_evaluate</code> evaluates the expression. <code>this</code> instance will be the first argument for <code>gtk_application_window_new</code>, so it is <code>app</code>.</p>
|
||||
<p>If the evaluation succeeds, the GValue <code>value</code> holds a newly created GtkApplicationWindow instance. It is assigned to <code>win1</code>. The GValue will be unset when it is no longer used. And when it is unset, the GtkApplicationWindow instance will be released and its reference count will be decreased by one. It is necessary to increase the reference count by one in advance to keep the instance. <code>gtk_expression_unref</code> frees <code>expression</code> and <code>value</code> is unset.</p>
|
||||
<p>As a result, we got a GtkApplicationWindow instance <code>win1</code>. We can do the same by:</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>win1 = gtk_application_window_new (app);</span></code></pre></div>
|
||||
<p>The example is more complicated and not practical than this one line code. The aim of the example is just to show how closure expression works.</p>
|
||||
<p>Closure expression is flexible than other type of expression because you can specify your own callback function.</p>
|
||||
<h2 id="gtkexpressionwatch">GtkExpressionWatch</h2>
|
||||
<p>GtkExpressionWatch watches an expression and if the value of the expression changes it calls its notify handler.</p>
|
||||
<p>The example uses GtkExpressionWatch in the line 103 to 106.</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"></a>expression1 = gtk_property_expression_new (GTK_TYPE_WINDOW, NULL, <span class="st">"default-width"</span>);</span>
|
||||
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true"></a>watch_width = gtk_expression_watch (expression1, win1, notify, NULL, NULL);</span>
|
||||
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true"></a>expression2 = gtk_property_expression_new (GTK_TYPE_WINDOW, NULL, <span class="st">"default-height"</span>);</span>
|
||||
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true"></a>watch_height = gtk_expression_watch (expression2, win1, notify, NULL, NULL);</span></code></pre></div>
|
||||
<p>The expressions above refer to “default-width” and “default-height” properties of GtkWindow. The variable <code>watch_width</code> watches <code>expression1</code>. The second argument <code>win1</code> is <code>this</code> instance for <code>expression1</code>. So, <code>watch_width</code> watches the value of “default-width” property of <code>win1</code>. If the value changes, it calls <code>notify</code> handler. The fourth and fifth arguments are NULL because no user data is necessary.</p>
|
||||
<p>The variable <code>watch_height</code> connects <code>notify</code> handler to <code>expression2</code>. So, <code>notiry</code> is also called when “default-height” changes.</p>
|
||||
<p>The handler <code>norify</code> is as follows.</p>
|
||||
<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>notify (gpointer user_data) {</span>
|
||||
<span id="cb12-3"><a href="#cb12-3"></a> GValue value = G_VALUE_INIT;</span>
|
||||
<span id="cb12-4"><a href="#cb12-4"></a> <span class="dt">char</span> *title;</span>
|
||||
<span id="cb12-5"><a href="#cb12-5"></a></span>
|
||||
<span id="cb12-6"><a href="#cb12-6"></a> <span class="cf">if</span> (watch_width && gtk_expression_watch_evaluate (watch_width, &value))</span>
|
||||
<span id="cb12-7"><a href="#cb12-7"></a> width = g_value_get_int (&value);</span>
|
||||
<span id="cb12-8"><a href="#cb12-8"></a> g_value_unset (&value);</span>
|
||||
<span id="cb12-9"><a href="#cb12-9"></a> <span class="cf">if</span> (watch_height && gtk_expression_watch_evaluate (watch_height, &value))</span>
|
||||
<span id="cb12-10"><a href="#cb12-10"></a> height = g_value_get_int (&value);</span>
|
||||
<span id="cb12-11"><a href="#cb12-11"></a> g_value_unset (&value);</span>
|
||||
<span id="cb12-12"><a href="#cb12-12"></a> title = g_strdup_printf (<span class="st">"%d x %d"</span>, width, height);</span>
|
||||
<span id="cb12-13"><a href="#cb12-13"></a> gtk_window_set_title (GTK_WINDOW (win1), title);</span>
|
||||
<span id="cb12-14"><a href="#cb12-14"></a> g_free (title);</span>
|
||||
<span id="cb12-15"><a href="#cb12-15"></a>}</span></code></pre></div>
|
||||
<ul>
|
||||
<li>6-11: Evaluates <code>expression1</code> and <code>expression2</code> with <code>expression_watch_evaluate</code> function.</li>
|
||||
<li>12: Creates a string <code>title</code>. It contains the width and height, for example, “800 x 600”.</li>
|
||||
<li>13: Sets the title of <code>win1</code> with the string <code>title</code>.</li>
|
||||
</ul>
|
||||
<p>The title of the window reflects the size of the window.</p>
|
||||
<h2 id="exp.ui">exp.ui</h2>
|
||||
<p><code>exp.c</code> builds a GtkWindow instance <code>win2</code> with <code>exp.ui</code>. The ui file <code>exp.ui</code> includes tags to create GtkExpressions. The tags are:</p>
|
||||
<ul>
|
||||
<li>constant tag to create constant expression</li>
|
||||
<li>lookup tag to create property expression</li>
|
||||
<li>closure tag to create closure expression</li>
|
||||
<li>binding tag to bind a property to an expression</li>
|
||||
</ul>
|
||||
<p>The window <code>win2</code> behaves like <code>win1</code>. Because similar expressions are built with the ui file.</p>
|
||||
<div class="sourceCode" id="cb13"><pre class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><span id="cb13-1"><a href="#cb13-1"></a><span class="kw"><?xml</span> version="1.0" encoding="UTF-8"<span class="kw">?></span></span>
|
||||
<span id="cb13-2"><a href="#cb13-2"></a><span class="kw"><interface></span></span>
|
||||
<span id="cb13-3"><a href="#cb13-3"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkWindow"</span><span class="ot"> id=</span><span class="st">"win2"</span><span class="kw">></span></span>
|
||||
<span id="cb13-4"><a href="#cb13-4"></a> <span class="kw"><binding</span><span class="ot"> name=</span><span class="st">"title"</span><span class="kw">></span></span>
|
||||
<span id="cb13-5"><a href="#cb13-5"></a> <span class="kw"><closure</span><span class="ot"> type=</span><span class="st">"gchararray"</span><span class="ot"> function=</span><span class="st">"set_title"</span><span class="kw">></span></span>
|
||||
<span id="cb13-6"><a href="#cb13-6"></a> <span class="kw"><lookup</span><span class="ot"> name=</span><span class="st">"default-width"</span><span class="ot"> type=</span><span class="st">"GtkWindow"</span><span class="kw">></lookup></span></span>
|
||||
<span id="cb13-7"><a href="#cb13-7"></a> <span class="kw"><lookup</span><span class="ot"> name=</span><span class="st">"default-height"</span><span class="ot"> type=</span><span class="st">"GtkWindow"</span><span class="kw">></lookup></span></span>
|
||||
<span id="cb13-8"><a href="#cb13-8"></a> <span class="kw"></closure></span></span>
|
||||
<span id="cb13-9"><a href="#cb13-9"></a> <span class="kw"></binding></span></span>
|
||||
<span id="cb13-10"><a href="#cb13-10"></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="cb13-11"><a href="#cb13-11"></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="cb13-12"><a href="#cb13-12"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb13-13"><a href="#cb13-13"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkBox"</span><span class="kw">></span></span>
|
||||
<span id="cb13-14"><a href="#cb13-14"></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="cb13-15"><a href="#cb13-15"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb13-16"><a href="#cb13-16"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkLabel"</span><span class="kw">></span></span>
|
||||
<span id="cb13-17"><a href="#cb13-17"></a> <span class="kw"><binding</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span></span>
|
||||
<span id="cb13-18"><a href="#cb13-18"></a> <span class="kw"><constant</span><span class="ot"> type=</span><span class="st">"gint"</span><span class="kw">></span>100<span class="kw"></constant></span></span>
|
||||
<span id="cb13-19"><a href="#cb13-19"></a> <span class="kw"></binding></span></span>
|
||||
<span id="cb13-20"><a href="#cb13-20"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb13-21"><a href="#cb13-21"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb13-22"><a href="#cb13-22"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb13-23"><a href="#cb13-23"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkLabel"</span><span class="kw">></span></span>
|
||||
<span id="cb13-24"><a href="#cb13-24"></a> <span class="kw"><binding</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span></span>
|
||||
<span id="cb13-25"><a href="#cb13-25"></a> <span class="kw"><lookup</span><span class="ot"> name=</span><span class="st">"text"</span><span class="kw">></span></span>
|
||||
<span id="cb13-26"><a href="#cb13-26"></a> <span class="kw"><lookup</span><span class="ot"> name=</span><span class="st">"buffer"</span><span class="kw">></span></span>
|
||||
<span id="cb13-27"><a href="#cb13-27"></a> entry</span>
|
||||
<span id="cb13-28"><a href="#cb13-28"></a> <span class="kw"></lookup></span></span>
|
||||
<span id="cb13-29"><a href="#cb13-29"></a> <span class="kw"></lookup></span></span>
|
||||
<span id="cb13-30"><a href="#cb13-30"></a> <span class="kw"></binding></span></span>
|
||||
<span id="cb13-31"><a href="#cb13-31"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb13-32"><a href="#cb13-32"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb13-33"><a href="#cb13-33"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb13-34"><a href="#cb13-34"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkLabel"</span><span class="kw">></span></span>
|
||||
<span id="cb13-35"><a href="#cb13-35"></a> <span class="kw"><binding</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span></span>
|
||||
<span id="cb13-36"><a href="#cb13-36"></a> <span class="kw"><lookup</span><span class="ot"> name=</span><span class="st">"application-id"</span><span class="kw">></span></span>
|
||||
<span id="cb13-37"><a href="#cb13-37"></a> <span class="kw"><lookup</span><span class="ot"> name=</span><span class="st">"application"</span><span class="kw">></span>win2<span class="kw"></lookup></span></span>
|
||||
<span id="cb13-38"><a href="#cb13-38"></a> <span class="kw"></lookup></span></span>
|
||||
<span id="cb13-39"><a href="#cb13-39"></a> <span class="kw"></binding></span></span>
|
||||
<span id="cb13-40"><a href="#cb13-40"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb13-41"><a href="#cb13-41"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb13-42"><a href="#cb13-42"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb13-43"><a href="#cb13-43"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkEntry"</span><span class="ot"> id=</span><span class="st">"entry"</span><span class="kw">></span></span>
|
||||
<span id="cb13-44"><a href="#cb13-44"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"buffer"</span><span class="kw">></span></span>
|
||||
<span id="cb13-45"><a href="#cb13-45"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkEntryBuffer"</span><span class="kw">></object></span></span>
|
||||
<span id="cb13-46"><a href="#cb13-46"></a> <span class="kw"></property></span></span>
|
||||
<span id="cb13-47"><a href="#cb13-47"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb13-48"><a href="#cb13-48"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb13-49"><a href="#cb13-49"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb13-50"><a href="#cb13-50"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb13-51"><a href="#cb13-51"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb13-52"><a href="#cb13-52"></a><span class="kw"></interface></span></span></code></pre></div>
|
||||
<h3 id="constant-tag">Constant tag</h3>
|
||||
<p>A constant tag corresponds to a constant expression.</p>
|
||||
<ul>
|
||||
<li>18: Constant tag. The constant expression is created with the tag. It returns 100, the type is “gint”, when it is evaluated. The type “gint” is a name of <code>G_TYPE_INT</code> type. Similarly, the types which is registered to the type system has type and name. For example, “gchararray” is a name of <code>G_TYPE_STRING</code> type. You need to use the name of types for the <code>type</code>attribute. See <a href="https://github.com/ToshioCP/Gobject-tutorial/blob/main/gfm/sec5.md#gvalue">GObject tutorial</a>.</li>
|
||||
<li>17-19: Binding tag corresponds to <code>gtk_expression_bind</code> function. <code>name</code> attribute specifies the “label” property of the GtkLabel object just before the binding tag. The expression returns a int type GValue. On the other hand “label” property holds a string type GValue. When a GValue is copied to another GValue, the type is automatically converted if possible. In this case, an int <code>100</code> is converted to a string <code>"100"</code>.</li>
|
||||
</ul>
|
||||
<p>These binding and constant tag works. But they are not good. A property tag is more straightforward.</p>
|
||||
<div class="sourceCode" id="cb14"><pre class="sourceCode xml"><code class="sourceCode xml"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true"></a><span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkLabel"</span><span class="kw">></span></span>
|
||||
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span>100<span class="kw"></property></span></span>
|
||||
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true"></a><span class="kw"></object></span></span></code></pre></div>
|
||||
<p>This example just shows the way how to use constant tag. Constant tag is mainly used to give a constant argument to a closure.</p>
|
||||
<h3 id="lookup-tag">Lookup tag</h3>
|
||||
<p>A lookup tag corresponds to a property expression. Line 23 to 31 is copied here.</p>
|
||||
<div class="sourceCode" id="cb15"><pre class="sourceCode xml"><code class="sourceCode xml"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkLabel"</span><span class="kw">></span></span>
|
||||
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true"></a> <span class="kw"><binding</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span></span>
|
||||
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true"></a> <span class="kw"><lookup</span><span class="ot"> name=</span><span class="st">"text"</span><span class="kw">></span></span>
|
||||
<span id="cb15-4"><a href="#cb15-4" aria-hidden="true"></a> <span class="kw"><lookup</span><span class="ot"> name=</span><span class="st">"buffer"</span><span class="kw">></span></span>
|
||||
<span id="cb15-5"><a href="#cb15-5" aria-hidden="true"></a> entry</span>
|
||||
<span id="cb15-6"><a href="#cb15-6" aria-hidden="true"></a> <span class="kw"></lookup></span></span>
|
||||
<span id="cb15-7"><a href="#cb15-7" aria-hidden="true"></a> <span class="kw"></lookup></span></span>
|
||||
<span id="cb15-8"><a href="#cb15-8" aria-hidden="true"></a> <span class="kw"></binding></span></span>
|
||||
<span id="cb15-9"><a href="#cb15-9" aria-hidden="true"></a> <span class="kw"></object></span></span></code></pre></div>
|
||||
<ul>
|
||||
<li>binding tag binds a “label” property in GtkLabel to an expression. The expression is defined with a lookup tag.</li>
|
||||
<li>The lookup tag defines a property expression looks up a “text” property in the instance which is defined in the next expression. The next expression is created with the lookup tag. The expression looks up the <code>buffer</code> property of the <code>entry</code> instance. The <code>entry</code> instance is defined in the line 43. It is a GtkEntry <code>entry</code>. A lookup tag takes an instance in some ways to look up for a property.
|
||||
<ul>
|
||||
<li>If it has no contents, it takes <code>this</code> instance when it is evaluated.</li>
|
||||
<li>If it has a content of a tag for an expression, which is constant, lookup or closure tag, the value of the expression will be the instance to look up when it is evaluated.</li>
|
||||
<li>If it has a content of an id of an object, then the instance of the object will be taken as the instance to lookup.</li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
<p>As a result, the label of the GtkLabel instance are bound to the text in the field of GtkEntry. If a user input a text in the field in the GtkEntry, GtkLabel displays the same text.</p>
|
||||
<p>Another lookup tag is in the lines from 34 to 40.</p>
|
||||
<div class="sourceCode" id="cb16"><pre class="sourceCode xml"><code class="sourceCode xml"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkLabel"</span><span class="kw">></span></span>
|
||||
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true"></a> <span class="kw"><binding</span><span class="ot"> name=</span><span class="st">"label"</span><span class="kw">></span></span>
|
||||
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true"></a> <span class="kw"><lookup</span><span class="ot"> name=</span><span class="st">"application-id"</span><span class="kw">></span></span>
|
||||
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true"></a> <span class="kw"><lookup</span><span class="ot"> name=</span><span class="st">"application"</span><span class="kw">></span>win2<span class="kw"></lookup></span></span>
|
||||
<span id="cb16-5"><a href="#cb16-5" aria-hidden="true"></a> <span class="kw"></lookup></span></span>
|
||||
<span id="cb16-6"><a href="#cb16-6" aria-hidden="true"></a> <span class="kw"></binding></span></span>
|
||||
<span id="cb16-7"><a href="#cb16-7" aria-hidden="true"></a> <span class="kw"></object></span></span></code></pre></div>
|
||||
<ul>
|
||||
<li>Two expressions are nested.</li>
|
||||
<li>A lookup tag looks up “application-id” property of the next expression.</li>
|
||||
<li>The next lookup tag looks up “application” property of <code>win2</code> instance.</li>
|
||||
</ul>
|
||||
<p>As a result, the “label” property in the GtkLabel instance is bound to the “application-id” property. The nested tag makes a chain like:</p>
|
||||
<pre><code>"label" <= "application-id" <= "application" <= `win2`</code></pre>
|
||||
<p>By the way, the application of <code>win2</code> is set after the objects in ui file are built. Look at <code>exp.c</code>. <code>gtk_window_set_application</code> is called after <code>gtk_build_new_from_resource</code>.</p>
|
||||
<div class="sourceCode" id="cb18"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true"></a>build = gtk_builder_new_from_resource (<span class="st">"/com/github/ToshioCP/exp/exp.ui"</span>);</span>
|
||||
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true"></a>GtkWidget *win2 = GTK_WIDGET (gtk_builder_get_object (build, <span class="st">"win2"</span>));</span>
|
||||
<span id="cb18-3"><a href="#cb18-3" aria-hidden="true"></a>gtk_window_set_application (GTK_WINDOW (win2), app);</span></code></pre></div>
|
||||
<p>Therefore, before the call for <code>gtk_window_set_application</code>, the “application” property of <code>win2</code> is <em>not</em> set. So, the evaluation of <code><lookup name="application">win2</lookup></code> fails. And the evaluation of <code><lookup name="application-id"></code> also fails. A function <code>gtk_expression_bind ()</code>, which corresponds to <code>binding</code> tag, doesn’t update the target property if the expression fails. So, the “label” property isn’t updated at the first evaluation.</p>
|
||||
<p>Note that an evaluation can fail. The care is especially necessary when you write a callback for a closure tag which has contents of expressions like lookup tags. The expressions are given to the callback as an argument. If an expression fails the argument will be NULL. You need to check if the argument exactly points the instance that is expected by the callback.</p>
|
||||
<h3 id="closure-tag">Closure tag</h3>
|
||||
<p>The lines from 3 to 9 include a closure tag.</p>
|
||||
<div class="sourceCode" id="cb19"><pre class="sourceCode xml"><code class="sourceCode xml"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkWindow"</span><span class="ot"> id=</span><span class="st">"win2"</span><span class="kw">></span></span>
|
||||
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true"></a> <span class="kw"><binding</span><span class="ot"> name=</span><span class="st">"title"</span><span class="kw">></span></span>
|
||||
<span id="cb19-3"><a href="#cb19-3" aria-hidden="true"></a> <span class="kw"><closure</span><span class="ot"> type=</span><span class="st">"gchararray"</span><span class="ot"> function=</span><span class="st">"set_title"</span><span class="kw">></span></span>
|
||||
<span id="cb19-4"><a href="#cb19-4" aria-hidden="true"></a> <span class="kw"><lookup</span><span class="ot"> name=</span><span class="st">"default-width"</span><span class="ot"> type=</span><span class="st">"GtkWindow"</span><span class="kw">></lookup></span></span>
|
||||
<span id="cb19-5"><a href="#cb19-5" aria-hidden="true"></a> <span class="kw"><lookup</span><span class="ot"> name=</span><span class="st">"default-height"</span><span class="ot"> type=</span><span class="st">"GtkWindow"</span><span class="kw">></lookup></span></span>
|
||||
<span id="cb19-6"><a href="#cb19-6" aria-hidden="true"></a> <span class="kw"></closure></span></span>
|
||||
<span id="cb19-7"><a href="#cb19-7" aria-hidden="true"></a> <span class="kw"></binding></span></span></code></pre></div>
|
||||
<ul>
|
||||
<li>A binding tag corresponds to a <code>gtk_expression_bind</code> function. <code>name</code> attribute specifies the “title” property of <code>win2</code>. Binding tag gives <code>win2</code> as the <code>this</code> instance to the expressions, which are the contents of the binding tag. So, closure tag and lookup tags use <code>win2</code> as the <code>this</code> object when they are evaluated.</li>
|
||||
<li>A closure tag corresponds to a closure expression. Its callback function is <code>set_title</code> and it returns “gchararray” type, which is “an array of characters” i.e. a string. The contents of the closure tag are assigned to parameters of the function. So, <code>set_title</code> has three parameters, <code>win2</code> (<code>this</code> instance), default width and default height.</li>
|
||||
<li>Lookup tags correspond to property expressions. They lookup “default-width” and “default-height” properties of <code>win2</code> (<code>this</code> instance).</li>
|
||||
<li>Binding tab creates GtkExpressionWatch automatically, so “title” property reflects the changes of “default-width” and “default-height” properties.</li>
|
||||
</ul>
|
||||
<p><code>set_title</code> function in <code>exp.c</code> is as follows.</p>
|
||||
<div class="sourceCode" id="cb20"><pre class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb20-1"><a href="#cb20-1"></a><span class="dt">char</span> *</span>
|
||||
<span id="cb20-2"><a href="#cb20-2"></a>set_title (GtkWidget *win, <span class="dt">int</span> width, <span class="dt">int</span> height) {</span>
|
||||
<span id="cb20-3"><a href="#cb20-3"></a> <span class="cf">return</span> g_strdup_printf (<span class="st">"%d x %d"</span>, width, height);</span>
|
||||
<span id="cb20-4"><a href="#cb20-4"></a>}</span></code></pre></div>
|
||||
<p>It just creates a string, for example, “800 x 600”, and returns it.</p>
|
||||
<p>You’ve probably been noticed that ui file is easier and clearer than the corresponding C program. One of the most useful case of GtkExpression is building GtkListItem instance with GtkBuilderListItemFatory. Such case has already been described in the prior two sections.</p>
|
||||
<p>It will be used in the next section to build GtkListItem in GtkColumnView, which is the most useful view object for GListModel.</p>
|
||||
<h2 id="compilation-and-execution">Compilation and execution</h2>
|
||||
<p>All the sources are in <a href="../src/expression">src/expression</a> directory. Change your current directory to the directory and run meson and ninja. Then, execute the application.</p>
|
||||
<pre><code>$ meson _build
|
||||
$ ninja -C _build
|
||||
$ build/exp</code></pre>
|
||||
<p>Then, two windows appear.</p>
|
||||
<figure>
|
||||
<img src="../image/expression.png" alt="" /><figcaption>Expression</figcaption>
|
||||
</figure>
|
||||
<p>If you put some text in the field of the entry, then the same text appears in the second GtkLabel. Because the “label” property of the second GtkLabel instance is bound to the text in the GtkEntryBuffer.</p>
|
||||
<p>If you resize the window, then the size appears in the title bar because the “title” property is bound to “default-width” and “default-height” properties.</p>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec27.html">Section 27</a>, Next: <a href="sec29.html">Section 29</a></p>
|
||||
</body>
|
||||
</html>
|
491
docs/sec29.html
Normal file
491
docs/sec29.html
Normal file
|
@ -0,0 +1,491 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>Gtk4 tutorial for beginners</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
body {width: 1080px; margin: 0 auto; font-size: large;}
|
||||
h2 {padding: 10px; background-color: #d0f0d0; }
|
||||
div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
|
||||
th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
|
||||
td {padding: 2px 6px; border: 1px solid;}
|
||||
img {display: block; margin-left: auto; margin-right: auto;}
|
||||
figcaption {text-align: center;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec28.html">Section 28</a></p>
|
||||
<h1 id="gtkcolumnview">GtkColumnView</h1>
|
||||
<h2 id="gtkcolumnview-1">GtkColumnView</h2>
|
||||
<p>GtkColumnView is like GtkListView, but it has multiple columns. Each column is GtkColumnViewColumn.</p>
|
||||
<figure>
|
||||
<img src="../image/column_view.png" alt="" /><figcaption>Column View</figcaption>
|
||||
</figure>
|
||||
<ul>
|
||||
<li>GtkColumnView has “model” property. The property points a GtkSelectionModel object.</li>
|
||||
<li>Each GtkColumnViewColumn has “factory” property. The property points a GtkListItemFactory (GtkSignalListItemFactory or GtkBuilderListItemFactory).</li>
|
||||
<li>The factory connects GtkListItem, which belongs to GtkColumnViewColumn, and items of GtkSelectionModel. And the factory builds the descendants widgets of GtkColumnView to display the item on the display. This process is the same as the one in GtkListView.</li>
|
||||
</ul>
|
||||
<p>The following diagram shows the image how it works.</p>
|
||||
<figure>
|
||||
<img src="../image/column.png" alt="" /><figcaption>ColumnView</figcaption>
|
||||
</figure>
|
||||
<p>The example in this section is a window that displays information of files in a current directory. The information is the name, size and last modified datetime of files. So, there are three columns.</p>
|
||||
<p>In addition, the example uses GtkSortListModel and GtkSorter to sort the information.</p>
|
||||
<h2 id="column.ui">column.ui</h2>
|
||||
<p>Ui file specifies whole widgets and their structure.</p>
|
||||
<div class="sourceCode" id="cb1"><pre class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><span id="cb1-1"><a href="#cb1-1"></a><span class="kw"><?xml</span> version="1.0" encoding="UTF-8"<span class="kw">?></span></span>
|
||||
<span id="cb1-2"><a href="#cb1-2"></a><span class="kw"><interface></span></span>
|
||||
<span id="cb1-3"><a href="#cb1-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="cb1-4"><a href="#cb1-4"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"title"</span><span class="kw">></span>file list<span class="kw"></property></span></span>
|
||||
<span id="cb1-5"><a href="#cb1-5"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"default-width"</span><span class="kw">></span>800<span class="kw"></property></span></span>
|
||||
<span id="cb1-6"><a href="#cb1-6"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"default-height"</span><span class="kw">></span>600<span class="kw"></property></span></span>
|
||||
<span id="cb1-7"><a href="#cb1-7"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb1-8"><a href="#cb1-8"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkScrolledWindow"</span><span class="ot"> id=</span><span class="st">"scr"</span><span class="kw">></span></span>
|
||||
<span id="cb1-9"><a href="#cb1-9"></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="cb1-10"><a href="#cb1-10"></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="cb1-11"><a href="#cb1-11"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb1-12"><a href="#cb1-12"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkColumnView"</span><span class="ot"> id=</span><span class="st">"columnview"</span><span class="kw">></span></span>
|
||||
<span id="cb1-13"><a href="#cb1-13"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"model"</span><span class="kw">></span></span>
|
||||
<span id="cb1-14"><a href="#cb1-14"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkSingleSelection"</span><span class="ot"> id=</span><span class="st">"singleselection"</span><span class="kw">></span></span>
|
||||
<span id="cb1-15"><a href="#cb1-15"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"model"</span><span class="kw">></span></span>
|
||||
<span id="cb1-16"><a href="#cb1-16"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkSortListModel"</span><span class="ot"> id=</span><span class="st">"sortlist"</span><span class="kw">></span></span>
|
||||
<span id="cb1-17"><a href="#cb1-17"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"model"</span><span class="kw">></span></span>
|
||||
<span id="cb1-18"><a href="#cb1-18"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkDirectoryList"</span><span class="ot"> id=</span><span class="st">"directorylist"</span><span class="kw">></span></span>
|
||||
<span id="cb1-19"><a href="#cb1-19"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"attributes"</span><span class="kw">></span>standard::name,standard::icon,standard::size,time::modified<span class="kw"></property></span></span>
|
||||
<span id="cb1-20"><a href="#cb1-20"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb1-21"><a href="#cb1-21"></a> <span class="kw"></property></span></span>
|
||||
<span id="cb1-22"><a href="#cb1-22"></a> <span class="kw"><binding</span><span class="ot"> name=</span><span class="st">"sorter"</span><span class="kw">></span></span>
|
||||
<span id="cb1-23"><a href="#cb1-23"></a> <span class="kw"><lookup</span><span class="ot"> name=</span><span class="st">"sorter"</span><span class="kw">></span>columnview<span class="kw"></lookup></span></span>
|
||||
<span id="cb1-24"><a href="#cb1-24"></a> <span class="kw"></binding></span></span>
|
||||
<span id="cb1-25"><a href="#cb1-25"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb1-26"><a href="#cb1-26"></a> <span class="kw"></property></span></span>
|
||||
<span id="cb1-27"><a href="#cb1-27"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb1-28"><a href="#cb1-28"></a> <span class="kw"></property></span></span>
|
||||
<span id="cb1-29"><a href="#cb1-29"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb1-30"><a href="#cb1-30"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkColumnViewColumn"</span><span class="ot"> id=</span><span class="st">"column1"</span><span class="kw">></span></span>
|
||||
<span id="cb1-31"><a href="#cb1-31"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"title"</span><span class="kw">></span>Name<span class="kw"></property></span></span>
|
||||
<span id="cb1-32"><a href="#cb1-32"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"expand"</span><span class="kw">></span>TRUE<span class="kw"></property></span></span>
|
||||
<span id="cb1-33"><a href="#cb1-33"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"factory"</span><span class="kw">></span></span>
|
||||
<span id="cb1-34"><a href="#cb1-34"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkBuilderListItemFactory"</span><span class="kw">></span></span>
|
||||
<span id="cb1-35"><a href="#cb1-35"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"bytes"</span><span class="kw">></span><span class="bn"><![CDATA[</span></span>
|
||||
<span id="cb1-36"><a href="#cb1-36"></a><?xml version="1.0" encoding="UTF-8"?></span>
|
||||
<span id="cb1-37"><a href="#cb1-37"></a><interface></span>
|
||||
<span id="cb1-38"><a href="#cb1-38"></a> <template class="GtkListItem"></span>
|
||||
<span id="cb1-39"><a href="#cb1-39"></a> <property name="child"></span>
|
||||
<span id="cb1-40"><a href="#cb1-40"></a> <object class="GtkBox"></span>
|
||||
<span id="cb1-41"><a href="#cb1-41"></a> <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property></span>
|
||||
<span id="cb1-42"><a href="#cb1-42"></a> <property name="spacing">20</property></span>
|
||||
<span id="cb1-43"><a href="#cb1-43"></a> <child></span>
|
||||
<span id="cb1-44"><a href="#cb1-44"></a> <object class="GtkImage"></span>
|
||||
<span id="cb1-45"><a href="#cb1-45"></a> <binding name="gicon"></span>
|
||||
<span id="cb1-46"><a href="#cb1-46"></a> <closure type="GIcon" function="get_icon_factory"></span>
|
||||
<span id="cb1-47"><a href="#cb1-47"></a> <lookup name="item">GtkListItem</lookup></span>
|
||||
<span id="cb1-48"><a href="#cb1-48"></a> </closure></span>
|
||||
<span id="cb1-49"><a href="#cb1-49"></a> </binding></span>
|
||||
<span id="cb1-50"><a href="#cb1-50"></a> </object></span>
|
||||
<span id="cb1-51"><a href="#cb1-51"></a> </child></span>
|
||||
<span id="cb1-52"><a href="#cb1-52"></a> <child></span>
|
||||
<span id="cb1-53"><a href="#cb1-53"></a> <object class="GtkLabel"></span>
|
||||
<span id="cb1-54"><a href="#cb1-54"></a> <property name="hexpand">TRUE</property></span>
|
||||
<span id="cb1-55"><a href="#cb1-55"></a> <property name="xalign">0</property></span>
|
||||
<span id="cb1-56"><a href="#cb1-56"></a> <binding name="label"></span>
|
||||
<span id="cb1-57"><a href="#cb1-57"></a> <closure type="gchararray" function="get_file_name_factory"></span>
|
||||
<span id="cb1-58"><a href="#cb1-58"></a> <lookup name="item">GtkListItem</lookup></span>
|
||||
<span id="cb1-59"><a href="#cb1-59"></a> </closure></span>
|
||||
<span id="cb1-60"><a href="#cb1-60"></a> </binding></span>
|
||||
<span id="cb1-61"><a href="#cb1-61"></a> </object></span>
|
||||
<span id="cb1-62"><a href="#cb1-62"></a> </child></span>
|
||||
<span id="cb1-63"><a href="#cb1-63"></a> </object></span>
|
||||
<span id="cb1-64"><a href="#cb1-64"></a> </property></span>
|
||||
<span id="cb1-65"><a href="#cb1-65"></a> </template></span>
|
||||
<span id="cb1-66"><a href="#cb1-66"></a></interface></span>
|
||||
<span id="cb1-67"><a href="#cb1-67"></a> <span class="bn">]]></span><span class="kw"></property></span></span>
|
||||
<span id="cb1-68"><a href="#cb1-68"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb1-69"><a href="#cb1-69"></a> <span class="kw"></property></span></span>
|
||||
<span id="cb1-70"><a href="#cb1-70"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"sorter"</span><span class="kw">></span></span>
|
||||
<span id="cb1-71"><a href="#cb1-71"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkStringSorter"</span><span class="ot"> id=</span><span class="st">"sorter_name"</span><span class="kw">></span></span>
|
||||
<span id="cb1-72"><a href="#cb1-72"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"expression"</span><span class="kw">></span></span>
|
||||
<span id="cb1-73"><a href="#cb1-73"></a> <span class="kw"><closure</span><span class="ot"> type=</span><span class="st">"gchararray"</span><span class="ot"> function=</span><span class="st">"get_file_name"</span><span class="kw">></span></span>
|
||||
<span id="cb1-74"><a href="#cb1-74"></a> <span class="kw"></closure></span></span>
|
||||
<span id="cb1-75"><a href="#cb1-75"></a> <span class="kw"></property></span></span>
|
||||
<span id="cb1-76"><a href="#cb1-76"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb1-77"><a href="#cb1-77"></a> <span class="kw"></property></span></span>
|
||||
<span id="cb1-78"><a href="#cb1-78"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb1-79"><a href="#cb1-79"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb1-80"><a href="#cb1-80"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb1-81"><a href="#cb1-81"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkColumnViewColumn"</span><span class="ot"> id=</span><span class="st">"column2"</span><span class="kw">></span></span>
|
||||
<span id="cb1-82"><a href="#cb1-82"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"title"</span><span class="kw">></span>Size<span class="kw"></property></span></span>
|
||||
<span id="cb1-83"><a href="#cb1-83"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"factory"</span><span class="kw">></span></span>
|
||||
<span id="cb1-84"><a href="#cb1-84"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkBuilderListItemFactory"</span><span class="kw">></span></span>
|
||||
<span id="cb1-85"><a href="#cb1-85"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"bytes"</span><span class="kw">></span><span class="bn"><![CDATA[</span></span>
|
||||
<span id="cb1-86"><a href="#cb1-86"></a><?xml version="1.0" encoding="UTF-8"?></span>
|
||||
<span id="cb1-87"><a href="#cb1-87"></a><interface></span>
|
||||
<span id="cb1-88"><a href="#cb1-88"></a> <template class="GtkListItem"></span>
|
||||
<span id="cb1-89"><a href="#cb1-89"></a> <property name="child"></span>
|
||||
<span id="cb1-90"><a href="#cb1-90"></a> <object class="GtkLabel"></span>
|
||||
<span id="cb1-91"><a href="#cb1-91"></a> <property name="hexpand">TRUE</property></span>
|
||||
<span id="cb1-92"><a href="#cb1-92"></a> <property name="xalign">0</property></span>
|
||||
<span id="cb1-93"><a href="#cb1-93"></a> <binding name="label"></span>
|
||||
<span id="cb1-94"><a href="#cb1-94"></a> <closure type="gchararray" function="get_file_size_factory"></span>
|
||||
<span id="cb1-95"><a href="#cb1-95"></a> <lookup name="item">GtkListItem</lookup></span>
|
||||
<span id="cb1-96"><a href="#cb1-96"></a> </closure></span>
|
||||
<span id="cb1-97"><a href="#cb1-97"></a> </binding></span>
|
||||
<span id="cb1-98"><a href="#cb1-98"></a> </object></span>
|
||||
<span id="cb1-99"><a href="#cb1-99"></a> </property></span>
|
||||
<span id="cb1-100"><a href="#cb1-100"></a> </template></span>
|
||||
<span id="cb1-101"><a href="#cb1-101"></a></interface></span>
|
||||
<span id="cb1-102"><a href="#cb1-102"></a> <span class="bn">]]></span><span class="kw"></property></span></span>
|
||||
<span id="cb1-103"><a href="#cb1-103"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb1-104"><a href="#cb1-104"></a> <span class="kw"></property></span></span>
|
||||
<span id="cb1-105"><a href="#cb1-105"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"sorter"</span><span class="kw">></span></span>
|
||||
<span id="cb1-106"><a href="#cb1-106"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkNumericSorter"</span><span class="ot"> id=</span><span class="st">"sorter_size"</span><span class="kw">></span></span>
|
||||
<span id="cb1-107"><a href="#cb1-107"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"expression"</span><span class="kw">></span></span>
|
||||
<span id="cb1-108"><a href="#cb1-108"></a> <span class="kw"><closure</span><span class="ot"> type=</span><span class="st">"gint64"</span><span class="ot"> function=</span><span class="st">"get_file_size"</span><span class="kw">></span></span>
|
||||
<span id="cb1-109"><a href="#cb1-109"></a> <span class="kw"></closure></span></span>
|
||||
<span id="cb1-110"><a href="#cb1-110"></a> <span class="kw"></property></span></span>
|
||||
<span id="cb1-111"><a href="#cb1-111"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"sort-order"</span><span class="kw">></span>GTK_SORT_ASCENDING<span class="kw"></property></span></span>
|
||||
<span id="cb1-112"><a href="#cb1-112"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb1-113"><a href="#cb1-113"></a> <span class="kw"></property></span></span>
|
||||
<span id="cb1-114"><a href="#cb1-114"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb1-115"><a href="#cb1-115"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb1-116"><a href="#cb1-116"></a> <span class="kw"><child></span></span>
|
||||
<span id="cb1-117"><a href="#cb1-117"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkColumnViewColumn"</span><span class="ot"> id=</span><span class="st">"column3"</span><span class="kw">></span></span>
|
||||
<span id="cb1-118"><a href="#cb1-118"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"title"</span><span class="kw">></span>Date modified<span class="kw"></property></span></span>
|
||||
<span id="cb1-119"><a href="#cb1-119"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"factory"</span><span class="kw">></span></span>
|
||||
<span id="cb1-120"><a href="#cb1-120"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkBuilderListItemFactory"</span><span class="kw">></span></span>
|
||||
<span id="cb1-121"><a href="#cb1-121"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"bytes"</span><span class="kw">></span><span class="bn"><![CDATA[</span></span>
|
||||
<span id="cb1-122"><a href="#cb1-122"></a><?xml version="1.0" encoding="UTF-8"?></span>
|
||||
<span id="cb1-123"><a href="#cb1-123"></a><interface></span>
|
||||
<span id="cb1-124"><a href="#cb1-124"></a> <template class="GtkListItem"></span>
|
||||
<span id="cb1-125"><a href="#cb1-125"></a> <property name="child"></span>
|
||||
<span id="cb1-126"><a href="#cb1-126"></a> <object class="GtkLabel"></span>
|
||||
<span id="cb1-127"><a href="#cb1-127"></a> <property name="hexpand">TRUE</property></span>
|
||||
<span id="cb1-128"><a href="#cb1-128"></a> <property name="xalign">0</property></span>
|
||||
<span id="cb1-129"><a href="#cb1-129"></a> <binding name="label"></span>
|
||||
<span id="cb1-130"><a href="#cb1-130"></a> <closure type="gchararray" function="get_file_time_modified_factory"></span>
|
||||
<span id="cb1-131"><a href="#cb1-131"></a> <lookup name="item">GtkListItem</lookup></span>
|
||||
<span id="cb1-132"><a href="#cb1-132"></a> </closure></span>
|
||||
<span id="cb1-133"><a href="#cb1-133"></a> </binding></span>
|
||||
<span id="cb1-134"><a href="#cb1-134"></a> </object></span>
|
||||
<span id="cb1-135"><a href="#cb1-135"></a> </property></span>
|
||||
<span id="cb1-136"><a href="#cb1-136"></a> </template></span>
|
||||
<span id="cb1-137"><a href="#cb1-137"></a></interface></span>
|
||||
<span id="cb1-138"><a href="#cb1-138"></a> <span class="bn">]]></span><span class="kw"></property></span></span>
|
||||
<span id="cb1-139"><a href="#cb1-139"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb1-140"><a href="#cb1-140"></a> <span class="kw"></property></span></span>
|
||||
<span id="cb1-141"><a href="#cb1-141"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"sorter"</span><span class="kw">></span></span>
|
||||
<span id="cb1-142"><a href="#cb1-142"></a> <span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkNumericSorter"</span><span class="ot"> id=</span><span class="st">"sorter_datetime_modified"</span><span class="kw">></span></span>
|
||||
<span id="cb1-143"><a href="#cb1-143"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"expression"</span><span class="kw">></span></span>
|
||||
<span id="cb1-144"><a href="#cb1-144"></a> <span class="kw"><closure</span><span class="ot"> type=</span><span class="st">"gint64"</span><span class="ot"> function=</span><span class="st">"get_file_unixtime_modified"</span><span class="kw">></span></span>
|
||||
<span id="cb1-145"><a href="#cb1-145"></a> <span class="kw"></closure></span></span>
|
||||
<span id="cb1-146"><a href="#cb1-146"></a> <span class="kw"></property></span></span>
|
||||
<span id="cb1-147"><a href="#cb1-147"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"sort-order"</span><span class="kw">></span>GTK_SORT_ASCENDING<span class="kw"></property></span></span>
|
||||
<span id="cb1-148"><a href="#cb1-148"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb1-149"><a href="#cb1-149"></a> <span class="kw"></property></span></span>
|
||||
<span id="cb1-150"><a href="#cb1-150"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb1-151"><a href="#cb1-151"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb1-152"><a href="#cb1-152"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb1-153"><a href="#cb1-153"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb1-154"><a href="#cb1-154"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb1-155"><a href="#cb1-155"></a> <span class="kw"></child></span></span>
|
||||
<span id="cb1-156"><a href="#cb1-156"></a> <span class="kw"></object></span></span>
|
||||
<span id="cb1-157"><a href="#cb1-157"></a><span class="kw"></interface></span></span></code></pre></div>
|
||||
<ul>
|
||||
<li>3-12: Widget parent-child relationship is GtkApplicationWindow => GtkScrolledWindow => GtkColumnView.</li>
|
||||
<li>12-18: GtkColumnView has “model” property. It points GtkSelectionModel interface. In this ui file, GtkSingleSelection is used as GtkSelectionModel. GtkSingleSelection is an object that implements GtkSelectionModel. And again, it has “model” property. It points GtkSortListModel. This list model supports sorting the list. It will be explained in the later subsection. And it also has “model” property. It points GtkDirectoryList. Therefore, the chain is: GtkColumnView => GtkSingleSelection => GtkSortListModel => GtkDirectoryList.</li>
|
||||
<li>18-20: GtkDirectoryList. It is a list of GFileInfo, which holds information of files under a directory. It has “attributes” property. It specifies what attributes is kept in each GFileInfo.
|
||||
<ul>
|
||||
<li>“standard::name” is a name of the file.</li>
|
||||
<li>“standard::icon” is a GIcon object of the file</li>
|
||||
<li>“standard::size” is the file size.</li>
|
||||
<li>“time::modified” is the date and time the file was last modified.</li>
|
||||
</ul></li>
|
||||
<li>29-79: The first GtkColumnViewColumn object. There are four properties, “title”, “expand”, factory" and “sorter”.</li>
|
||||
<li>31: Sets the “title” property with “Name”. This is the title on the header of the column.</li>
|
||||
<li>32: Sets the “expand” property to TRUE to allow the column to expand as much as possible. (See the image above).</li>
|
||||
<li>33- 69: Sets the “factory” property with GtkBuilderListItemFactory. The factory has “bytes” property which holds a ui string to define a template to build GtkListItem composite widget. The CDATA section (line 36-66) is the ui string to put into the “bytes” property. The contents are the same as the ui file <code>factory_list.ui</code> in the section 27.</li>
|
||||
<li>70-77: Sets the “sorter” property with GtkStringSorter object. This object provides a sorter that compares strings. It has “expression” property which is set with GtkExpression. A closure tag with a string type function <code>get_file_name</code> is used here. The function will be explained later.</li>
|
||||
<li>80-115: The second GtkColumnViewColumn object. Its “title”, “factory” and “sorter” properties are set. GtkNumericSorter is used.</li>
|
||||
<li>116-151: The third GtkColumnViewColumn object. Its “title”, “factory” and “sorter” properties are set. GtkNumericSorter is used.</li>
|
||||
</ul>
|
||||
<h2 id="gtksortlistmodel-and-gtksorter">GtkSortListModel and GtkSorter</h2>
|
||||
<p>GtkSortListModel is a list model that sorts its elements according to a GtkSorter. It has “sorter” property that is set with GtkSorter. The property is bound to “sorter” property of GtkColumnView in line 22 to 24.</p>
|
||||
<div class="sourceCode" id="cb2"><pre class="sourceCode xml"><code class="sourceCode xml"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true"></a><span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkSortListModel"</span><span class="ot"> id=</span><span class="st">"sortlist"</span><span class="kw">></span></span>
|
||||
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true"></a>... ... ...</span>
|
||||
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true"></a> <span class="kw"><binding</span><span class="ot"> name=</span><span class="st">"sorter"</span><span class="kw">></span></span>
|
||||
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true"></a> <span class="kw"><lookup</span><span class="ot"> name=</span><span class="st">"sorter"</span><span class="kw">></span>columnview<span class="kw"></lookup></span></span>
|
||||
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true"></a> <span class="kw"></binding></span></span></code></pre></div>
|
||||
<p>Therefore, <code>columnview</code> determines the way how to sort the list model. The “sorter” property of GtkColumnView is read-only property and it is a special sorter. It reflects the user’s sorting choice. If a user clicks the header of a column, then the sorter (“sorter” property) of the column is referenced by “sorter” property of the GtkColumnView. If the user clicks the header of another column, then the “sorter” property of the GtkColumnView refers to the newly clicked column’s “sorter” property.</p>
|
||||
<p>The binding above makes a indirect connection between the “sorter” property of GtkSortListModel and the “sorter” property of each column.</p>
|
||||
<p>GtkSorter has several child objects.</p>
|
||||
<ul>
|
||||
<li>GtkStringSorter compares strings.</li>
|
||||
<li>GtkNumericSorter compares numbers.</li>
|
||||
<li>GtkCustomSorter uses a callback to compare.</li>
|
||||
<li>GtkMultiSorter combines multiple sorters.</li>
|
||||
</ul>
|
||||
<p>The example uses GtkStringSorter and GtkNumericSorter.</p>
|
||||
<p>GtkStringSorter uses GtkExpression to get the strings from the objects. The GtkExpression is stored in the “expression” property of GtkStringSorter. For example, in the ui file above, the GtkExpression is in the line 71 to 76.</p>
|
||||
<div class="sourceCode" id="cb3"><pre class="sourceCode xml"><code class="sourceCode xml"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true"></a><span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkStringSorter"</span><span class="ot"> id=</span><span class="st">"sorter_name"</span><span class="kw">></span></span>
|
||||
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"expression"</span><span class="kw">></span></span>
|
||||
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true"></a> <span class="kw"><closure</span><span class="ot"> type=</span><span class="st">"gchararray"</span><span class="ot"> function=</span><span class="st">"get_file_name"</span><span class="kw">></span></span>
|
||||
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true"></a> <span class="kw"></closure></span></span>
|
||||
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true"></a> <span class="kw"></property></span></span>
|
||||
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true"></a><span class="kw"></object></span></span></code></pre></div>
|
||||
<p>The GtkExpression calls <code>get_file_name</code> function when it is evaluated.</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">char</span> *</span>
|
||||
<span id="cb4-2"><a href="#cb4-2"></a>get_file_name (GFileInfo *info) {</span>
|
||||
<span id="cb4-3"><a href="#cb4-3"></a> g_return_val_if_fail (G_IS_FILE_INFO (info), NULL);</span>
|
||||
<span id="cb4-4"><a href="#cb4-4"></a></span>
|
||||
<span id="cb4-5"><a href="#cb4-5"></a> <span class="cf">return</span> g_strdup(g_file_info_get_name (info));</span>
|
||||
<span id="cb4-6"><a href="#cb4-6"></a>}</span></code></pre></div>
|
||||
<p>The function is given the item (GFileInfo) of the GtkSortListModel as an argument (<code>this</code> object). The function retrieves a filename from <code>info</code>. The string is owned by <code>info</code> so it is necessary to duplicate it. And it returns the copied string. The string will be owned by the expression.</p>
|
||||
<p>GtkNumericSorter compares numbers. It is used in the line 106 to 112 and line 142 to 148. The lines from 106 to 112 is:</p>
|
||||
<div class="sourceCode" id="cb5"><pre class="sourceCode xml"><code class="sourceCode xml"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true"></a><span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkNumericSorter"</span><span class="ot"> id=</span><span class="st">"sorter_size"</span><span class="kw">></span></span>
|
||||
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"expression"</span><span class="kw">></span></span>
|
||||
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true"></a> <span class="kw"><closure</span><span class="ot"> type=</span><span class="st">"gint64"</span><span class="ot"> function=</span><span class="st">"get_file_size"</span><span class="kw">></span></span>
|
||||
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true"></a> <span class="kw"></closure></span></span>
|
||||
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true"></a> <span class="kw"></property></span></span>
|
||||
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"sort-order"</span><span class="kw">></span>GTK_SORT_ASCENDING<span class="kw"></property></span></span>
|
||||
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true"></a><span class="kw"></object></span></span></code></pre></div>
|
||||
<p>The closure tag specifies a callback function <code>get_file_size</code>.</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>goffset</span>
|
||||
<span id="cb6-2"><a href="#cb6-2"></a>get_file_size (GFileInfo *info) {</span>
|
||||
<span id="cb6-3"><a href="#cb6-3"></a> g_return_val_if_fail (G_IS_FILE_INFO (info), -<span class="dv">1</span>);</span>
|
||||
<span id="cb6-4"><a href="#cb6-4"></a></span>
|
||||
<span id="cb6-5"><a href="#cb6-5"></a> <span class="cf">return</span> g_file_info_get_size (info);</span>
|
||||
<span id="cb6-6"><a href="#cb6-6"></a>}</span></code></pre></div>
|
||||
<p>It just returns the size of <code>info</code>. The type of the size is <code>goffset</code>. The type <code>goffset</code> is the same as <code>gint64</code>.</p>
|
||||
<p>The lines from 142 to 148 is:</p>
|
||||
<div class="sourceCode" id="cb7"><pre class="sourceCode xml"><code class="sourceCode xml"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true"></a><span class="kw"><object</span><span class="ot"> class=</span><span class="st">"GtkNumericSorter"</span><span class="ot"> id=</span><span class="st">"sorter_datetime_modified"</span><span class="kw">></span></span>
|
||||
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"expression"</span><span class="kw">></span></span>
|
||||
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true"></a> <span class="kw"><closure</span><span class="ot"> type=</span><span class="st">"gint64"</span><span class="ot"> function=</span><span class="st">"get_file_unixtime_modified"</span><span class="kw">></span></span>
|
||||
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true"></a> <span class="kw"></closure></span></span>
|
||||
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true"></a> <span class="kw"></property></span></span>
|
||||
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true"></a> <span class="kw"><property</span><span class="ot"> name=</span><span class="st">"sort-order"</span><span class="kw">></span>GTK_SORT_ASCENDING<span class="kw"></property></span></span>
|
||||
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true"></a><span class="kw"></object></span></span></code></pre></div>
|
||||
<p>The closure tag specifies a callback function <code>get_file_unixtime_modified</code>.</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>gint64</span>
|
||||
<span id="cb8-2"><a href="#cb8-2"></a>get_file_unixtime_modified (GFileInfo *info) {</span>
|
||||
<span id="cb8-3"><a href="#cb8-3"></a> g_return_val_if_fail (G_IS_FILE_INFO (info), -<span class="dv">1</span>);</span>
|
||||
<span id="cb8-4"><a href="#cb8-4"></a></span>
|
||||
<span id="cb8-5"><a href="#cb8-5"></a> GDateTime *dt;</span>
|
||||
<span id="cb8-6"><a href="#cb8-6"></a></span>
|
||||
<span id="cb8-7"><a href="#cb8-7"></a> dt = g_file_info_get_modification_date_time (info);</span>
|
||||
<span id="cb8-8"><a href="#cb8-8"></a> <span class="cf">return</span> g_date_time_to_unix (dt);</span>
|
||||
<span id="cb8-9"><a href="#cb8-9"></a>}</span></code></pre></div>
|
||||
<p>It gets the modification date and time (GDateTime type) of <code>info</code>. Then it gets a unix time from <code>dt</code>. Unix time, sometimes called unix epoch, is the number of seconds that have elapsed since 00:00:00 UTC on 1 January 1970. It returns the unix time (gint64 type).</p>
|
||||
<h2 id="column.c">column.c</h2>
|
||||
<p><code>column.c</code> is as follows.</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="co">/* functions (closures) for GtkBuilderListItemFactory */</span></span>
|
||||
<span id="cb9-4"><a href="#cb9-4"></a>GIcon *</span>
|
||||
<span id="cb9-5"><a href="#cb9-5"></a>get_icon_factory (GtkListItem *item, GFileInfo *info) {</span>
|
||||
<span id="cb9-6"><a href="#cb9-6"></a> GIcon *icon;</span>
|
||||
<span id="cb9-7"><a href="#cb9-7"></a> <span class="cf">if</span> (! G_IS_FILE_INFO (info))</span>
|
||||
<span id="cb9-8"><a href="#cb9-8"></a> <span class="cf">return</span> NULL;</span>
|
||||
<span id="cb9-9"><a href="#cb9-9"></a> <span class="cf">else</span> {</span>
|
||||
<span id="cb9-10"><a href="#cb9-10"></a> icon = g_file_info_get_icon (info);</span>
|
||||
<span id="cb9-11"><a href="#cb9-11"></a> g_object_ref (icon);</span>
|
||||
<span id="cb9-12"><a href="#cb9-12"></a> <span class="cf">return</span> icon;</span>
|
||||
<span id="cb9-13"><a href="#cb9-13"></a> }</span>
|
||||
<span id="cb9-14"><a href="#cb9-14"></a>}</span>
|
||||
<span id="cb9-15"><a href="#cb9-15"></a></span>
|
||||
<span id="cb9-16"><a href="#cb9-16"></a><span class="dt">char</span> *</span>
|
||||
<span id="cb9-17"><a href="#cb9-17"></a>get_file_name_factory (GtkListItem *item, GFileInfo *info) {</span>
|
||||
<span id="cb9-18"><a href="#cb9-18"></a> <span class="cf">if</span> (! G_IS_FILE_INFO (info))</span>
|
||||
<span id="cb9-19"><a href="#cb9-19"></a> <span class="cf">return</span> NULL;</span>
|
||||
<span id="cb9-20"><a href="#cb9-20"></a> <span class="cf">else</span></span>
|
||||
<span id="cb9-21"><a href="#cb9-21"></a> <span class="cf">return</span> g_strdup (g_file_info_get_name (info));</span>
|
||||
<span id="cb9-22"><a href="#cb9-22"></a>}</span>
|
||||
<span id="cb9-23"><a href="#cb9-23"></a></span>
|
||||
<span id="cb9-24"><a href="#cb9-24"></a><span class="dt">char</span> *</span>
|
||||
<span id="cb9-25"><a href="#cb9-25"></a>get_file_size_factory (GtkListItem *item, GFileInfo *info) {</span>
|
||||
<span id="cb9-26"><a href="#cb9-26"></a> <span class="co">/* goffset is gint64 */</span></span>
|
||||
<span id="cb9-27"><a href="#cb9-27"></a> goffset size;</span>
|
||||
<span id="cb9-28"><a href="#cb9-28"></a></span>
|
||||
<span id="cb9-29"><a href="#cb9-29"></a> <span class="cf">if</span> (! G_IS_FILE_INFO (info))</span>
|
||||
<span id="cb9-30"><a href="#cb9-30"></a> <span class="cf">return</span> NULL;</span>
|
||||
<span id="cb9-31"><a href="#cb9-31"></a> <span class="cf">else</span> {</span>
|
||||
<span id="cb9-32"><a href="#cb9-32"></a> size = g_file_info_get_size (info);</span>
|
||||
<span id="cb9-33"><a href="#cb9-33"></a> <span class="cf">return</span> g_strdup_printf (<span class="st">"%ld"</span>, (<span class="dt">long</span> <span class="dt">int</span>) size);</span>
|
||||
<span id="cb9-34"><a href="#cb9-34"></a> }</span>
|
||||
<span id="cb9-35"><a href="#cb9-35"></a>}</span>
|
||||
<span id="cb9-36"><a href="#cb9-36"></a></span>
|
||||
<span id="cb9-37"><a href="#cb9-37"></a><span class="dt">char</span> *</span>
|
||||
<span id="cb9-38"><a href="#cb9-38"></a>get_file_time_modified_factory (GtkListItem *item, GFileInfo *info) {</span>
|
||||
<span id="cb9-39"><a href="#cb9-39"></a> GDateTime *dt;</span>
|
||||
<span id="cb9-40"><a href="#cb9-40"></a></span>
|
||||
<span id="cb9-41"><a href="#cb9-41"></a> <span class="cf">if</span> (! G_IS_FILE_INFO (info))</span>
|
||||
<span id="cb9-42"><a href="#cb9-42"></a> <span class="cf">return</span> NULL;</span>
|
||||
<span id="cb9-43"><a href="#cb9-43"></a> <span class="cf">else</span> {</span>
|
||||
<span id="cb9-44"><a href="#cb9-44"></a> dt = g_file_info_get_modification_date_time (info);</span>
|
||||
<span id="cb9-45"><a href="#cb9-45"></a> <span class="cf">return</span> g_date_time_format (dt, <span class="st">"%F"</span>);</span>
|
||||
<span id="cb9-46"><a href="#cb9-46"></a> }</span>
|
||||
<span id="cb9-47"><a href="#cb9-47"></a>}</span>
|
||||
<span id="cb9-48"><a href="#cb9-48"></a></span>
|
||||
<span id="cb9-49"><a href="#cb9-49"></a><span class="co">/* Functions (closures) for GtkSorter */</span></span>
|
||||
<span id="cb9-50"><a href="#cb9-50"></a><span class="dt">char</span> *</span>
|
||||
<span id="cb9-51"><a href="#cb9-51"></a>get_file_name (GFileInfo *info) {</span>
|
||||
<span id="cb9-52"><a href="#cb9-52"></a> g_return_val_if_fail (G_IS_FILE_INFO (info), NULL);</span>
|
||||
<span id="cb9-53"><a href="#cb9-53"></a></span>
|
||||
<span id="cb9-54"><a href="#cb9-54"></a> <span class="cf">return</span> g_strdup(g_file_info_get_name (info));</span>
|
||||
<span id="cb9-55"><a href="#cb9-55"></a>}</span>
|
||||
<span id="cb9-56"><a href="#cb9-56"></a></span>
|
||||
<span id="cb9-57"><a href="#cb9-57"></a>goffset</span>
|
||||
<span id="cb9-58"><a href="#cb9-58"></a>get_file_size (GFileInfo *info) {</span>
|
||||
<span id="cb9-59"><a href="#cb9-59"></a> g_return_val_if_fail (G_IS_FILE_INFO (info), -<span class="dv">1</span>);</span>
|
||||
<span id="cb9-60"><a href="#cb9-60"></a></span>
|
||||
<span id="cb9-61"><a href="#cb9-61"></a> <span class="cf">return</span> g_file_info_get_size (info);</span>
|
||||
<span id="cb9-62"><a href="#cb9-62"></a>}</span>
|
||||
<span id="cb9-63"><a href="#cb9-63"></a></span>
|
||||
<span id="cb9-64"><a href="#cb9-64"></a>gint64</span>
|
||||
<span id="cb9-65"><a href="#cb9-65"></a>get_file_unixtime_modified (GFileInfo *info) {</span>
|
||||
<span id="cb9-66"><a href="#cb9-66"></a> g_return_val_if_fail (G_IS_FILE_INFO (info), -<span class="dv">1</span>);</span>
|
||||
<span id="cb9-67"><a href="#cb9-67"></a></span>
|
||||
<span id="cb9-68"><a href="#cb9-68"></a> GDateTime *dt;</span>
|
||||
<span id="cb9-69"><a href="#cb9-69"></a></span>
|
||||
<span id="cb9-70"><a href="#cb9-70"></a> dt = g_file_info_get_modification_date_time (info);</span>
|
||||
<span id="cb9-71"><a href="#cb9-71"></a> <span class="cf">return</span> g_date_time_to_unix (dt);</span>
|
||||
<span id="cb9-72"><a href="#cb9-72"></a>}</span>
|
||||
<span id="cb9-73"><a href="#cb9-73"></a></span>
|
||||
<span id="cb9-74"><a href="#cb9-74"></a><span class="co">/* ----- activate, open, startup handlers ----- */</span></span>
|
||||
<span id="cb9-75"><a href="#cb9-75"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb9-76"><a href="#cb9-76"></a>app_activate (GApplication *application) {</span>
|
||||
<span id="cb9-77"><a href="#cb9-77"></a> GtkApplication *app = GTK_APPLICATION (application);</span>
|
||||
<span id="cb9-78"><a href="#cb9-78"></a> GFile *file;</span>
|
||||
<span id="cb9-79"><a href="#cb9-79"></a></span>
|
||||
<span id="cb9-80"><a href="#cb9-80"></a> GtkBuilder *build = gtk_builder_new_from_resource (<span class="st">"/com/github/ToshioCP/column/column.ui"</span>);</span>
|
||||
<span id="cb9-81"><a href="#cb9-81"></a> GtkWidget *win = GTK_WIDGET (gtk_builder_get_object (build, <span class="st">"win"</span>));</span>
|
||||
<span id="cb9-82"><a href="#cb9-82"></a> GtkDirectoryList *directorylist = GTK_DIRECTORY_LIST (gtk_builder_get_object (build, <span class="st">"directorylist"</span>));</span>
|
||||
<span id="cb9-83"><a href="#cb9-83"></a> g_object_unref (build);</span>
|
||||
<span id="cb9-84"><a href="#cb9-84"></a></span>
|
||||
<span id="cb9-85"><a href="#cb9-85"></a> gtk_window_set_application (GTK_WINDOW (win), app);</span>
|
||||
<span id="cb9-86"><a href="#cb9-86"></a></span>
|
||||
<span id="cb9-87"><a href="#cb9-87"></a> file = g_file_new_for_path (<span class="st">"."</span>);</span>
|
||||
<span id="cb9-88"><a href="#cb9-88"></a> gtk_directory_list_set_file (directorylist, file);</span>
|
||||
<span id="cb9-89"><a href="#cb9-89"></a> g_object_unref (file);</span>
|
||||
<span id="cb9-90"><a href="#cb9-90"></a></span>
|
||||
<span id="cb9-91"><a href="#cb9-91"></a> gtk_widget_show (win);</span>
|
||||
<span id="cb9-92"><a href="#cb9-92"></a>}</span>
|
||||
<span id="cb9-93"><a href="#cb9-93"></a></span>
|
||||
<span id="cb9-94"><a href="#cb9-94"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb9-95"><a href="#cb9-95"></a>app_startup (GApplication *application) {</span>
|
||||
<span id="cb9-96"><a href="#cb9-96"></a>}</span>
|
||||
<span id="cb9-97"><a href="#cb9-97"></a></span>
|
||||
<span id="cb9-98"><a href="#cb9-98"></a><span class="pp">#define APPLICATION_ID "com.github.ToshioCP.columnview"</span></span>
|
||||
<span id="cb9-99"><a href="#cb9-99"></a></span>
|
||||
<span id="cb9-100"><a href="#cb9-100"></a><span class="dt">int</span></span>
|
||||
<span id="cb9-101"><a href="#cb9-101"></a>main (<span class="dt">int</span> argc, <span class="dt">char</span> **argv) {</span>
|
||||
<span id="cb9-102"><a href="#cb9-102"></a> GtkApplication *app;</span>
|
||||
<span id="cb9-103"><a href="#cb9-103"></a> <span class="dt">int</span> stat;</span>
|
||||
<span id="cb9-104"><a href="#cb9-104"></a></span>
|
||||
<span id="cb9-105"><a href="#cb9-105"></a> app = gtk_application_new (APPLICATION_ID, G_APPLICATION_FLAGS_NONE);</span>
|
||||
<span id="cb9-106"><a href="#cb9-106"></a></span>
|
||||
<span id="cb9-107"><a href="#cb9-107"></a> g_signal_connect (app, <span class="st">"startup"</span>, G_CALLBACK (app_startup), NULL);</span>
|
||||
<span id="cb9-108"><a href="#cb9-108"></a> g_signal_connect (app, <span class="st">"activate"</span>, G_CALLBACK (app_activate), NULL);</span>
|
||||
<span id="cb9-109"><a href="#cb9-109"></a><span class="co">/* g_signal_connect (app, "open", G_CALLBACK (app_open), NULL);*/</span></span>
|
||||
<span id="cb9-110"><a href="#cb9-110"></a></span>
|
||||
<span id="cb9-111"><a href="#cb9-111"></a> stat =g_application_run (G_APPLICATION (app), argc, argv);</span>
|
||||
<span id="cb9-112"><a href="#cb9-112"></a> g_object_unref (app);</span>
|
||||
<span id="cb9-113"><a href="#cb9-113"></a> <span class="cf">return</span> stat;</span>
|
||||
<span id="cb9-114"><a href="#cb9-114"></a>}</span></code></pre></div>
|
||||
<ul>
|
||||
<li>4-47: Functions for the closure tag in the “bytes” property of GtkBuilderListItemFactory. These are almost same as the functions in section 26 and 26.</li>
|
||||
<li>50-72: Functions for the closure in the expression property of GtkStringSorter or GtkNumericSorter.</li>
|
||||
<li>75-92: <code>app_activate</code> is an “activate” handler of GApplication.</li>
|
||||
<li>80-83: Builds objects with ui resource and gets <code>win</code> and <code>directorylist</code>.</li>
|
||||
<li>85: Sets the application of the top level window with <code>app</code>.</li>
|
||||
<li>87-89: Sets the file of <code>directorylist</code> with “.” (current directory).</li>
|
||||
<li>94-96: Startup handler.</li>
|
||||
<li>98-114: <code>main</code> function.</li>
|
||||
</ul>
|
||||
<p><code>exp.c</code> is simple and short thanks to <code>exp.ui</code>.</p>
|
||||
<h2 id="compilation-and-execution.">Compilation and execution.</h2>
|
||||
<p>All the source files are in <a href="../src/column">src/column</a> directory. Change your current directory to the directory and type the following.</p>
|
||||
<pre><code>$ meson _build
|
||||
$ ninja -C _build
|
||||
$ _build/column</code></pre>
|
||||
<p>Then, a window appears.</p>
|
||||
<figure>
|
||||
<img src="../image/column_view.png" alt="" /><figcaption>Column View</figcaption>
|
||||
</figure>
|
||||
<p>If you click the header of a column, then the whole lists are sorted by the column. If you click the header of another column, then the whole lists are sorted by the newly selected column.</p>
|
||||
<p>GtkColumnView is very useful and it can manage very big GListModel. It is possible to use it for file list, application list, database frontend and so on.</p>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec28.html">Section 28</a></p>
|
||||
</body>
|
||||
</html>
|
258
docs/sec3.html
Normal file
258
docs/sec3.html
Normal file
|
@ -0,0 +1,258 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>Gtk4 tutorial for beginners</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
body {width: 1080px; margin: 0 auto; font-size: large;}
|
||||
h2 {padding: 10px; background-color: #d0f0d0; }
|
||||
div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
|
||||
th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
|
||||
td {padding: 2px 6px; border: 1px solid;}
|
||||
img {display: block; margin-left: auto; margin-right: auto;}
|
||||
figcaption {text-align: center;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec2.html">Section 2</a>, Next: <a href="sec4.html">Section 4</a></p>
|
||||
<h1 id="gtkapplication-and-gtkapplicationwindow">GtkApplication and 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 Gtk4 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>
|
||||
<ul>
|
||||
<li>Create a GtkApplication instance.</li>
|
||||
<li>Run the application.</li>
|
||||
</ul>
|
||||
<p>That’s all. Very simple. The following is the C code representing the scenario above.</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">int</span></span>
|
||||
<span id="cb1-4"><a href="#cb1-4"></a>main (<span class="dt">int</span> argc, <span class="dt">char</span> **argv) {</span>
|
||||
<span id="cb1-5"><a href="#cb1-5"></a> GtkApplication *app;</span>
|
||||
<span id="cb1-6"><a href="#cb1-6"></a> <span class="dt">int</span> stat;</span>
|
||||
<span id="cb1-7"><a href="#cb1-7"></a></span>
|
||||
<span id="cb1-8"><a href="#cb1-8"></a> app = gtk_application_new (<span class="st">"com.github.ToshioCP.pr1"</span>, G_APPLICATION_FLAGS_NONE);</span>
|
||||
<span id="cb1-9"><a href="#cb1-9"></a> stat =g_application_run (G_APPLICATION (app), argc, argv);</span>
|
||||
<span id="cb1-10"><a href="#cb1-10"></a> g_object_unref (app);</span>
|
||||
<span id="cb1-11"><a href="#cb1-11"></a> <span class="cf">return</span> stat;</span>
|
||||
<span id="cb1-12"><a href="#cb1-12"></a>}</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 <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>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>
|
||||
<p>The C compiler gcc generates an executable file, <code>a.out</code>. Let’s run it.</p>
|
||||
<pre><code>$ ./a.out
|
||||
|
||||
(a.out:13533): GLib-GIO-WARNING **: 15:30:17.449: Your application does not implement
|
||||
g_application_activate() and has no handlers connected to the "activate" signal.
|
||||
It should do one of these.
|
||||
$</code></pre>
|
||||
<p>Oh, it just produces an error message. This error message means that the GtkApplication object ran, without a doubt. Now, let’s think about what this message means.</p>
|
||||
<h3 id="signal">signal</h3>
|
||||
<p>The message tells us that:</p>
|
||||
<ol type="1">
|
||||
<li>The application GtkApplication doesn’t implement <code>g_application_activate()</code>,</li>
|
||||
<li>It has no handlers connected to the “activate” signal, and</li>
|
||||
<li>You will need to solve at least one of these.</li>
|
||||
</ol>
|
||||
<p>These two causes of the error are related to signals. So, I will 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>
|
||||
<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>
|
||||
</ol>
|
||||
<p>Signals are defined in objects. For example, the “activate” signal belongs to the GApplication object, which is a parent object of GtkApplication object.</p>
|
||||
<p>The GApplication object is a child object of the GObject object. GObject is the top object in the hierarchy of all the objects.</p>
|
||||
<pre><code>GObject -- GApplication -- GtkApplication
|
||||
<---parent --->child</code></pre>
|
||||
<p>A child object inherits signals, functions, properties and so on from its parent object. So, GtkApplication also has the “activate” signal.</p>
|
||||
<p>Now we can solve the problem in <code>pr1.c</code>. We need to connect the “activate” signal to a handler. We use a function <code>g_signal_connect</code> which connects a signal to a handler.</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 (GApplication *app, gpointer *user_data) {</span>
|
||||
<span id="cb5-5"><a href="#cb5-5"></a> g_print (<span class="st">"GtkApplication is activated.</span><span class="sc">\n</span><span class="st">"</span>);</span>
|
||||
<span id="cb5-6"><a href="#cb5-6"></a>}</span>
|
||||
<span id="cb5-7"><a href="#cb5-7"></a></span>
|
||||
<span id="cb5-8"><a href="#cb5-8"></a><span class="dt">int</span></span>
|
||||
<span id="cb5-9"><a href="#cb5-9"></a>main (<span class="dt">int</span> argc, <span class="dt">char</span> **argv) {</span>
|
||||
<span id="cb5-10"><a href="#cb5-10"></a> GtkApplication *app;</span>
|
||||
<span id="cb5-11"><a href="#cb5-11"></a> <span class="dt">int</span> stat;</span>
|
||||
<span id="cb5-12"><a href="#cb5-12"></a></span>
|
||||
<span id="cb5-13"><a href="#cb5-13"></a> app = gtk_application_new (<span class="st">"com.github.ToshioCP.pr2"</span>, G_APPLICATION_FLAGS_NONE);</span>
|
||||
<span id="cb5-14"><a href="#cb5-14"></a> g_signal_connect (app, <span class="st">"activate"</span>, G_CALLBACK (app_activate), NULL);</span>
|
||||
<span id="cb5-15"><a href="#cb5-15"></a> stat =g_application_run (G_APPLICATION (app), argc, argv);</span>
|
||||
<span id="cb5-16"><a href="#cb5-16"></a> g_object_unref (app);</span>
|
||||
<span id="cb5-17"><a href="#cb5-17"></a> <span class="cf">return</span> stat;</span>
|
||||
<span id="cb5-18"><a href="#cb5-18"></a>}</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>
|
||||
<ol type="1">
|
||||
<li>An instance to which the signal belongs.</li>
|
||||
<li>The name of the signal.</li>
|
||||
<li>A handler function (also called callback), which needs to be casted 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 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>
|
||||
<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`
|
||||
$ ./a.out
|
||||
GtkApplication is activated.
|
||||
$</code></pre>
|
||||
<p>OK, well done. However, you may have noticed that it’s painful to type such a long line to compile. It is a good idea to use shell script to solve this problem. Make a text file which contains the following line.</p>
|
||||
<pre><code>gcc `pkg-config --cflags gtk4` $1.c `pkg-config --libs gtk4`</code></pre>
|
||||
<p>Then, save it under the directory $HOME/bin, which is usually /home/(username)/bin. (If your user name is James, then the directory is /home/james/bin). And turn on the execute bit of the file. If the filename is <code>comp</code>, do like this:</p>
|
||||
<pre><code>$ chmod 755 $HOME/bin/comp
|
||||
$ ls -log $HOME/bin
|
||||
... ... ...
|
||||
-rwxr-xr-x 1 62 May 23 08:21 comp
|
||||
... ... ...</code></pre>
|
||||
<p>If this is the first time that you make a $HOME/bin directory and save a file in it, then you need to logout and login again.</p>
|
||||
<pre><code>$ comp pr2
|
||||
$ ./a.out
|
||||
GtkApplication is activated.
|
||||
$</code></pre>
|
||||
<h2 id="gtkwindow-and-gtkapplicationwindow">GtkWindow and GtkApplicationWindow</h2>
|
||||
<h3 id="gtkwindow">GtkWindow</h3>
|
||||
<p>A message “GtkApplication is activated.” was printed out in the previous subsection. It was good in terms of a test of GtkApplication. However, it is insufficient because Gtk is a framework for graphical user interface (GUI). Now we go ahead with adding a window into this program. What we need to do is:</p>
|
||||
<ol type="1">
|
||||
<li>Create a GtkWindow.</li>
|
||||
<li>Connect it to GtkApplication.</li>
|
||||
<li>Show the window.</li>
|
||||
</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 (GApplication *app, gpointer user_data) {</span>
|
||||
<span id="cb10-3"><a href="#cb10-3"></a> GtkWidget *win;</span>
|
||||
<span id="cb10-4"><a href="#cb10-4"></a></span>
|
||||
<span id="cb10-5"><a href="#cb10-5"></a> win = gtk_window_new ();</span>
|
||||
<span id="cb10-6"><a href="#cb10-6"></a> gtk_window_set_application (GTK_WINDOW (win), GTK_APPLICATION (app));</span>
|
||||
<span id="cb10-7"><a href="#cb10-7"></a> gtk_widget_show (win);</span>
|
||||
<span id="cb10-8"><a href="#cb10-8"></a>}</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 derive.</p>
|
||||
<pre><code>parent <-----> child
|
||||
GtkWidget -- GtkWindow</code></pre>
|
||||
<p>GtkWindow includes GtkWidget at the top of its object.</p>
|
||||
<figure>
|
||||
<img src="../image/window_widget.png" alt="" /><figcaption>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"></a>GtkWidget *</span>
|
||||
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true"></a>gtk_window_new (<span class="dt">void</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"></a>(GtkWindow *) 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"></a>GTK_WINDOW (win)</span></code></pre></div>
|
||||
<p>This is a recommended way.</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"></a>gtk_window_set_application (GTK_WINDOW (win), GTK_APPLICATION (app));</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>
|
||||
<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 connected to GtkApplication, GtkApplication doesn’t need to wait anything. As it destroys itself, the GtkWindow is also destroyed.</p>
|
||||
<h4 id="show-the-window.">Show the window.</h4>
|
||||
<p>The function <code>gtk_widget_show</code> is used to show the window.</p>
|
||||
<p>Gtk4 changes the default widget visibility to on, so every widget 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>
|
||||
<pre><code>$ comp pr3
|
||||
$ ./a.out</code></pre>
|
||||
<p>A small window appears.</p>
|
||||
<figure>
|
||||
<img src="../image/screenshot_pr3.png" alt="" /><figcaption>Screenshot of the window</figcaption>
|
||||
</figure>
|
||||
<p>Click on the close button then the window disappears and the program 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>
|
||||
<p>Now rewrite the program and use GtkAppliction Window.</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 (GApplication *app, gpointer user_data) {</span>
|
||||
<span id="cb17-3"><a href="#cb17-3"></a> GtkWidget *win;</span>
|
||||
<span id="cb17-4"><a href="#cb17-4"></a></span>
|
||||
<span id="cb17-5"><a href="#cb17-5"></a> win = gtk_application_window_new (GTK_APPLICATION (app));</span>
|
||||
<span id="cb17-6"><a href="#cb17-6"></a> gtk_window_set_title (GTK_WINDOW (win), <span class="st">"pr4"</span>);</span>
|
||||
<span id="cb17-7"><a href="#cb17-7"></a> gtk_window_set_default_size (GTK_WINDOW (win), <span class="dv">400</span>, <span class="dv">300</span>);</span>
|
||||
<span id="cb17-8"><a href="#cb17-8"></a> gtk_widget_show (win);</span>
|
||||
<span id="cb17-9"><a href="#cb17-9"></a>}</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 <code>gtk_window_set_application</code> any more.</p>
|
||||
<p>The program sets the title and the default size of the window. Compile it and run <code>a.out</code>, then you will see a bigger window with its title “pr4”.</p>
|
||||
<figure>
|
||||
<img src="../image/screenshot_pr4.png" alt="" /><figcaption>Screenshot of the window</figcaption>
|
||||
</figure>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec2.html">Section 2</a>, Next: <a href="sec4.html">Section 4</a></p>
|
||||
</body>
|
||||
</html>
|
332
docs/sec4.html
Normal file
332
docs/sec4.html
Normal file
|
@ -0,0 +1,332 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>Gtk4 tutorial for beginners</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
body {width: 1080px; margin: 0 auto; font-size: large;}
|
||||
h2 {padding: 10px; background-color: #d0f0d0; }
|
||||
div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
|
||||
th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
|
||||
td {padding: 2px 6px; border: 1px solid;}
|
||||
img {display: block; margin-left: auto; margin-right: auto;}
|
||||
figcaption {text-align: center;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec3.html">Section 3</a>, Next: <a href="sec5.html">Section 5</a></p>
|
||||
<h1 id="widgets-1">Widgets (1)</h1>
|
||||
<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>
|
||||
<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-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-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-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-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-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>
|
||||
<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>
|
||||
</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>
|
||||
<pre><code>$ cd misc; diff pr4.c lb1.c
|
||||
5a6
|
||||
> GtkWidget *lab;
|
||||
8c9
|
||||
< gtk_window_set_title (GTK_WINDOW (win), "pr4");
|
||||
---
|
||||
> gtk_window_set_title (GTK_WINDOW (win), "lb1");
|
||||
9a11,14
|
||||
>
|
||||
> lab = gtk_label_new ("Hello.");
|
||||
> 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.lb1", G_APPLICATION_FLAGS_NONE);</code></pre>
|
||||
<p>This tells us:</p>
|
||||
<ul>
|
||||
<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>
|
||||
<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>
|
||||
<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-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-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-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-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-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-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>
|
||||
<figure>
|
||||
<img src="../image/screenshot_lb2.png" alt="" /><figcaption>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>
|
||||
<pre><code>$ cd misc; diff lb2.c lb3.c
|
||||
5c5,6
|
||||
< g_print ("Clicked.\n");
|
||||
---
|
||||
> GtkWindow *win = GTK_WINDOW (user_data);
|
||||
> gtk_window_destroy (win);
|
||||
14c15
|
||||
< gtk_window_set_title (GTK_WINDOW (win), "lb2");
|
||||
---
|
||||
> gtk_window_set_title (GTK_WINDOW (win), "lb3");
|
||||
17c18
|
||||
< btn = gtk_button_new_with_label ("Click me");
|
||||
---
|
||||
> btn = gtk_button_new_with_label ("Quit");
|
||||
19c20
|
||||
< 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);
|
||||
---
|
||||
> app = gtk_application_new ("com.github.ToshioCP.lb3", G_APPLICATION_FLAGS_NONE);
|
||||
35d35
|
||||
< </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>
|
||||
</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>
|
||||
<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>
|
||||
<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 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>
|
||||
</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>
|
||||
<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-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-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-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>
|
||||
<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>
|
||||
<figure>
|
||||
<img src="../image/screenshot_lb4.png" alt="" /><figcaption>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>Up: <a href="index.html">index.html</a>, Prev: <a href="sec3.html">Section 3</a>, Next: <a href="sec5.html">Section 5</a></p>
|
||||
</body>
|
||||
</html>
|
222
docs/sec5.html
Normal file
222
docs/sec5.html
Normal file
|
@ -0,0 +1,222 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>Gtk4 tutorial for beginners</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
body {width: 1080px; margin: 0 auto; font-size: large;}
|
||||
h2 {padding: 10px; background-color: #d0f0d0; }
|
||||
div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
|
||||
th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
|
||||
td {padding: 2px 6px; border: 1px solid;}
|
||||
img {display: block; margin-left: auto; margin-right: auto;}
|
||||
figcaption {text-align: center;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec4.html">Section 4</a>, Next: <a href="sec6.html">Section 6</a></p>
|
||||
<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>
|
||||
<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-9"><a href="#cb1-9"></a></span>
|
||||
<span id="cb1-10"><a href="#cb1-10"></a> text =</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>
|
||||
<span id="cb1-14"><a href="#cb1-14"></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="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-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-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-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-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-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-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>
|
||||
<p>Now compile and run it.</p>
|
||||
<figure>
|
||||
<img src="../image/screenshot_tfv1.png" alt="" /><figcaption>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>
|
||||
<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>
|
||||
</ul>
|
||||
<p>Modify <code>tfv1.c</code> and save it as <code>tfv2.c</code>. The difference between these two files is small.</p>
|
||||
<pre><code>$ cd tfv; diff tfv1.c tfv2.c
|
||||
5a6
|
||||
> GtkWidget *scr;
|
||||
24a26,28
|
||||
> scr = gtk_scrolled_window_new ();
|
||||
> gtk_window_set_child (GTK_WINDOW (win), scr);
|
||||
>
|
||||
30c34
|
||||
< gtk_window_set_child (GTK_WINDOW (win), tv);
|
||||
---
|
||||
> 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.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>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec4.html">Section 4</a>, Next: <a href="sec6.html">Section 6</a></p>
|
||||
</body>
|
||||
</html>
|
194
docs/sec6.html
Normal file
194
docs/sec6.html
Normal file
|
@ -0,0 +1,194 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>Gtk4 tutorial for beginners</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
body {width: 1080px; margin: 0 auto; font-size: large;}
|
||||
h2 {padding: 10px; background-color: #d0f0d0; }
|
||||
div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
|
||||
th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
|
||||
td {padding: 2px 6px; border: 1px solid;}
|
||||
img {display: block; margin-left: auto; margin-right: auto;}
|
||||
figcaption {text-align: center;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec5.html">Section 5</a>, Next: <a href="sec7.html">Section 7</a></p>
|
||||
<h1 id="string-and-memory-management">String 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>
|
||||
<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="dt">char</span> a[<span class="dv">10</span>], *b;</span>
|
||||
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true"></a></span>
|
||||
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true"></a>a[<span class="dv">0</span>] = <span class="ch">'H'</span>;</span>
|
||||
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true"></a>a[<span class="dv">1</span>] = <span class="ch">'e'</span>;</span>
|
||||
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true"></a>a[<span class="dv">2</span>] = <span class="ch">'l'</span>;</span>
|
||||
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true"></a>a[<span class="dv">3</span>] = <span class="ch">'l'</span>;</span>
|
||||
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true"></a>a[<span class="dv">4</span>] = <span class="ch">'o'</span>;</span>
|
||||
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true"></a>a[<span class="dv">5</span>] = <span class="ch">'\0'</span>;</span>
|
||||
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true"></a></span>
|
||||
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true"></a>b = a;</span>
|
||||
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true"></a><span class="co">/* *b is 'H' */</span></span>
|
||||
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true"></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>
|
||||
<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>
|
||||
</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>
|
||||
<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="dt">char</span> *s;</span>
|
||||
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true"></a>s = <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>
|
||||
<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>*(s+<span class="dv">1</span>) = <span class="ch">'a'</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>
|
||||
<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"></a></span>
|
||||
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true"></a><span class="dt">static</span> <span class="dt">char</span> a[] = {<span class="ch">'H'</span>, <span class="ch">'e'</span>, <span class="ch">'l'</span>, <span class="ch">'l'</span>, <span class="ch">'o'</span>, <span class="ch">'\0'</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><span class="dt">void</span></span>
|
||||
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true"></a>print_strings (<span class="dt">void</span>) {</span>
|
||||
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true"></a> <span class="dt">char</span> b[] = <span class="st">"Hello"</span>;</span>
|
||||
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true"></a></span>
|
||||
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true"></a> a[<span class="dv">1</span>] = <span class="ch">'a'</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"></a> b[<span class="dv">1</span>] = <span class="ch">'a'</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"></a></span>
|
||||
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true"></a> printf (<span class="st">"%s</span><span class="sc">\n</span><span class="st">"</span>, a); <span class="co">/* Hallo */</span></span>
|
||||
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true"></a> printf (<span class="st">"%s</span><span class="sc">\n</span><span class="st">"</span>, b); <span class="co">/* Hallo */</span></span>
|
||||
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true"></a>}</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 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>
|
||||
<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>
|
||||
<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 <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"></a>g_new (struct_type, n_struct)</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 array.</li>
|
||||
<li><code>n_struct</code> is the size of the array.</li>
|
||||
<li>The return value is a pointer to the array. Its type is a pointer to <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"></a><span class="dt">char</span> *s;</span>
|
||||
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true"></a>s = g_new (<span class="dt">char</span>, <span class="dv">10</span>);</span>
|
||||
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true"></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"></a></span>
|
||||
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true"></a><span class="kw">struct</span> tuple {<span class="dt">int</span> x, y;} *t;</span>
|
||||
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true"></a>t = g_new (<span class="kw">struct</span> tuple, <span class="dv">5</span>);</span>
|
||||
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true"></a><span class="co">/* t points an array of struct tuple. */</span></span>
|
||||
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true"></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"></a><span class="dt">void</span></span>
|
||||
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true"></a>g_free (gpointer mem);</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"></a>g_free (s);</span>
|
||||
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true"></a><span class="co">/* Frees the memory allocated to s. */</span></span>
|
||||
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true"></a></span>
|
||||
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true"></a>g_free (t);</span>
|
||||
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true"></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"></a><span class="dt">char</span> *s;</span>
|
||||
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true"></a>s = g_strdup (<span class="st">"Hello"</span>);</span>
|
||||
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true"></a>g_free (s);</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 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 href="https://docs.gtk.org/glib/func.strdup.html">GLib API Reference</a>. The following is extracted from the reference.</p>
|
||||
<blockquote>
|
||||
<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"></a><span class="dt">const</span> <span class="dt">char</span> *</span>
|
||||
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true"></a>g_quark_to_string (GQuark quark);</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>
|
||||
<div class="sourceCode" id="cb11"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true"></a><span class="dt">const</span> <span class="dt">int</span> x = <span class="dv">10</span>; <span class="co">/* initialization is OK. */</span></span>
|
||||
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true"></a></span>
|
||||
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true"></a>x = <span class="dv">20</span>; <span class="co">/* This is illegal because x is qualified with const */</span></span></code></pre></div>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec5.html">Section 5</a>, Next: <a href="sec7.html">Section 7</a></p>
|
||||
</body>
|
||||
</html>
|
341
docs/sec7.html
Normal file
341
docs/sec7.html
Normal file
|
@ -0,0 +1,341 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>Gtk4 tutorial for beginners</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
body {width: 1080px; margin: 0 auto; font-size: large;}
|
||||
h2 {padding: 10px; background-color: #d0f0d0; }
|
||||
div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
|
||||
th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
|
||||
td {padding: 2px 6px; border: 1px solid;}
|
||||
img {display: block; margin-left: auto; margin-right: auto;}
|
||||
figcaption {text-align: center;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec6.html">Section 6</a>, Next: <a href="sec8.html">Section 8</a></p>
|
||||
<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>
|
||||
<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>
|
||||
<h3 id="open-signal-1">open signal</h3>
|
||||
<p>Now, when the application starts, two signals can be emitted.</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>
|
||||
</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>
|
||||
<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>
|
||||
</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>
|
||||
<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>
|
||||
</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>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>
|
||||
</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>
|
||||
<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>
|
||||
</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>
|
||||
<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>
|
||||
</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>
|
||||
<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>
|
||||
<figure>
|
||||
<img src="../image/screenshot_gtk_notebook.png" alt="" /><figcaption>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>
|
||||
<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>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>
|
||||
</ul>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec6.html">Section 6</a>, Next: <a href="sec8.html">Section 8</a></p>
|
||||
</body>
|
||||
</html>
|
356
docs/sec8.html
Normal file
356
docs/sec8.html
Normal file
|
@ -0,0 +1,356 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>Gtk4 tutorial for beginners</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
body {width: 1080px; margin: 0 auto; font-size: large;}
|
||||
h2 {padding: 10px; background-color: #d0f0d0; }
|
||||
div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
|
||||
th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
|
||||
td {padding: 2px 6px; border: 1px solid;}
|
||||
img {display: block; margin-left: auto; margin-right: auto;}
|
||||
figcaption {text-align: center;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec7.html">Section 7</a>, Next: <a href="sec9.html">Section 9</a></p>
|
||||
<h1 id="defining-a-child-object">Defining a Child object</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 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>
|
||||
<ul>
|
||||
<li>Memory to store a pointer to the GFile instance.</li>
|
||||
<li>A function to write the file.</li>
|
||||
</ul>
|
||||
<p>There are a couple of ways to store the details of GFile.</p>
|
||||
<ul>
|
||||
<li>Use global variables; or</li>
|
||||
<li>Make a child object, which can extend the instance memory for the GFile object.</li>
|
||||
</ul>
|
||||
<p>Using global variables is easy to implement. Define a sufficient size array of pointers 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"></a>GFile *f[<span class="dv">20</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>
|
||||
<figure>
|
||||
<img src="../image/child.png" alt="" /><figcaption>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 the separate tutorial](https://github.com/ToshioCP/Gobject-tutorial).</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>
|
||||
<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="pp">#define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type ()</span></span>
|
||||
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true"></a>G_DECLARE_FINAL_TYPE (TfeTextView, tfe_text_view, TFE, TEXT_VIEW, GtkTextView)</span>
|
||||
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true"></a></span>
|
||||
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true"></a><span class="kw">struct</span> _TfeTextView</span>
|
||||
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true"></a>{</span>
|
||||
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true"></a> GtkTextView parent;</span>
|
||||
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true"></a> GFile *file;</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>G_DEFINE_TYPE (TfeTextView, tfe_text_view, GTK_TYPE_TEXT_VIEW);</span>
|
||||
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true"></a></span>
|
||||
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true"></a>tfe_text_view_init (TfeTextView *tv) {</span>
|
||||
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true"></a>}</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 class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true"></a>tfe_text_view_class_init (TfeTextViewClass *class) {</span>
|
||||
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true"></a>}</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><span class="dt">void</span></span>
|
||||
<span id="cb2-21"><a href="#cb2-21" aria-hidden="true"></a>tfe_text_view_set_file (TfeTextView *tv, GFile *f) {</span>
|
||||
<span id="cb2-22"><a href="#cb2-22" aria-hidden="true"></a> tv -> file = f;</span>
|
||||
<span id="cb2-23"><a href="#cb2-23" aria-hidden="true"></a>}</span>
|
||||
<span id="cb2-24"><a href="#cb2-24" aria-hidden="true"></a></span>
|
||||
<span id="cb2-25"><a href="#cb2-25" aria-hidden="true"></a>GFile *</span>
|
||||
<span id="cb2-26"><a href="#cb2-26" aria-hidden="true"></a>tfe_text_view_get_file (TfeTextView *tv) {</span>
|
||||
<span id="cb2-27"><a href="#cb2-27" aria-hidden="true"></a> <span class="cf">return</span> tv -> file;</span>
|
||||
<span id="cb2-28"><a href="#cb2-28" aria-hidden="true"></a>}</span>
|
||||
<span id="cb2-29"><a href="#cb2-29" aria-hidden="true"></a></span>
|
||||
<span id="cb2-30"><a href="#cb2-30" aria-hidden="true"></a>GtkWidget *</span>
|
||||
<span id="cb2-31"><a href="#cb2-31" aria-hidden="true"></a>tfe_text_view_new (<span class="dt">void</span>) {</span>
|
||||
<span id="cb2-32"><a href="#cb2-32" aria-hidden="true"></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" aria-hidden="true"></a>}</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>
|
||||
<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>
|
||||
<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>
|
||||
<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 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. 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>
|
||||
<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>
|
||||
<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>g_signal_connect (win, <span class="st">"close-request"</span>, G_CALLBACK (before_close), NULL);</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 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 (GtkWindow *win, gpointer user_data) {</span>
|
||||
<span id="cb4-3"><a href="#cb4-3"></a> GtkWidget *nb = GTK_WIDGET (user_data);</span>
|
||||
<span id="cb4-4"><a href="#cb4-4"></a> GtkWidget *scr;</span>
|
||||
<span id="cb4-5"><a href="#cb4-5"></a> GtkWidget *tv;</span>
|
||||
<span id="cb4-6"><a href="#cb4-6"></a> GFile *file;</span>
|
||||
<span id="cb4-7"><a href="#cb4-7"></a> <span class="dt">char</span> *pathname;</span>
|
||||
<span id="cb4-8"><a href="#cb4-8"></a> GtkTextBuffer *tb;</span>
|
||||
<span id="cb4-9"><a href="#cb4-9"></a> GtkTextIter start_iter;</span>
|
||||
<span id="cb4-10"><a href="#cb4-10"></a> GtkTextIter end_iter;</span>
|
||||
<span id="cb4-11"><a href="#cb4-11"></a> <span class="dt">char</span> *contents;</span>
|
||||
<span id="cb4-12"><a href="#cb4-12"></a> <span class="dt">unsigned</span> <span class="dt">int</span> n;</span>
|
||||
<span id="cb4-13"><a href="#cb4-13"></a> <span class="dt">unsigned</span> <span class="dt">int</span> i;</span>
|
||||
<span id="cb4-14"><a href="#cb4-14"></a></span>
|
||||
<span id="cb4-15"><a href="#cb4-15"></a> n = gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb));</span>
|
||||
<span id="cb4-16"><a href="#cb4-16"></a> <span class="cf">for</span> (i = <span class="dv">0</span>; i < n; ++i) {</span>
|
||||
<span id="cb4-17"><a href="#cb4-17"></a> scr = gtk_notebook_get_nth_page (GTK_NOTEBOOK (nb), i);</span>
|
||||
<span id="cb4-18"><a href="#cb4-18"></a> tv = gtk_scrolled_window_get_child (GTK_SCROLLED_WINDOW (scr));</span>
|
||||
<span id="cb4-19"><a href="#cb4-19"></a> file = tfe_text_view_get_file (TFE_TEXT_VIEW (tv));</span>
|
||||
<span id="cb4-20"><a href="#cb4-20"></a> tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));</span>
|
||||
<span id="cb4-21"><a href="#cb4-21"></a> gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter);</span>
|
||||
<span id="cb4-22"><a href="#cb4-22"></a> contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE);</span>
|
||||
<span id="cb4-23"><a href="#cb4-23"></a> <span class="cf">if</span> (! g_file_replace_contents (file, contents, strlen (contents), NULL, TRUE, G_FILE_CREATE_NONE, NULL, NULL, NULL)) {</span>
|
||||
<span id="cb4-24"><a href="#cb4-24"></a> pathname = g_file_get_path (file);</span>
|
||||
<span id="cb4-25"><a href="#cb4-25"></a> g_print (<span class="st">"ERROR : Can't save %s."</span>, pathname);</span>
|
||||
<span id="cb4-26"><a href="#cb4-26"></a> g_free (pathname);</span>
|
||||
<span id="cb4-27"><a href="#cb4-27"></a> }</span>
|
||||
<span id="cb4-28"><a href="#cb4-28"></a> g_free (contents);</span>
|
||||
<span id="cb4-29"><a href="#cb4-29"></a> }</span>
|
||||
<span id="cb4-30"><a href="#cb4-30"></a> <span class="cf">return</span> FALSE;</span>
|
||||
<span id="cb4-31"><a href="#cb4-31"></a>}</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>
|
||||
</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>
|
||||
<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-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 (TfeTextView, tfe_text_view, TFE, TEXT_VIEW, GtkTextView)</span>
|
||||
<span id="cb5-7"><a href="#cb5-7"></a></span>
|
||||
<span id="cb5-8"><a href="#cb5-8"></a><span class="kw">struct</span> _TfeTextView</span>
|
||||
<span id="cb5-9"><a href="#cb5-9"></a>{</span>
|
||||
<span id="cb5-10"><a href="#cb5-10"></a> GtkTextView parent;</span>
|
||||
<span id="cb5-11"><a href="#cb5-11"></a> GFile *file;</span>
|
||||
<span id="cb5-12"><a href="#cb5-12"></a>};</span>
|
||||
<span id="cb5-13"><a href="#cb5-13"></a></span>
|
||||
<span id="cb5-14"><a href="#cb5-14"></a>G_DEFINE_TYPE (TfeTextView, tfe_text_view, GTK_TYPE_TEXT_VIEW);</span>
|
||||
<span id="cb5-15"><a href="#cb5-15"></a></span>
|
||||
<span id="cb5-16"><a href="#cb5-16"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||||
<span id="cb5-17"><a href="#cb5-17"></a>tfe_text_view_init (TfeTextView *tv) {</span>
|
||||
<span id="cb5-18"><a href="#cb5-18"></a>}</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 (TfeTextViewClass *class) {</span>
|
||||
<span id="cb5-22"><a href="#cb5-22"></a>}</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 (TfeTextView *tv, GFile *f) {</span>
|
||||
<span id="cb5-26"><a href="#cb5-26"></a> tv -> file = f;</span>
|
||||
<span id="cb5-27"><a href="#cb5-27"></a>}</span>
|
||||
<span id="cb5-28"><a href="#cb5-28"></a></span>
|
||||
<span id="cb5-29"><a href="#cb5-29"></a>GFile *</span>
|
||||
<span id="cb5-30"><a href="#cb5-30"></a>tfe_text_view_get_file (TfeTextView *tv) {</span>
|
||||
<span id="cb5-31"><a href="#cb5-31"></a> <span class="cf">return</span> tv -> file;</span>
|
||||
<span id="cb5-32"><a href="#cb5-32"></a>}</span>
|
||||
<span id="cb5-33"><a href="#cb5-33"></a></span>
|
||||
<span id="cb5-34"><a href="#cb5-34"></a>GtkWidget *</span>
|
||||
<span id="cb5-35"><a href="#cb5-35"></a>tfe_text_view_new (<span class="dt">void</span>) {</span>
|
||||
<span id="cb5-36"><a href="#cb5-36"></a> <span class="cf">return</span> GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL));</span>
|
||||
<span id="cb5-37"><a href="#cb5-37"></a>}</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 (GtkWindow *win, gpointer user_data) {</span>
|
||||
<span id="cb5-43"><a href="#cb5-43"></a> GtkWidget *nb = GTK_WIDGET (user_data);</span>
|
||||
<span id="cb5-44"><a href="#cb5-44"></a> GtkWidget *scr;</span>
|
||||
<span id="cb5-45"><a href="#cb5-45"></a> GtkWidget *tv;</span>
|
||||
<span id="cb5-46"><a href="#cb5-46"></a> GFile *file;</span>
|
||||
<span id="cb5-47"><a href="#cb5-47"></a> <span class="dt">char</span> *pathname;</span>
|
||||
<span id="cb5-48"><a href="#cb5-48"></a> GtkTextBuffer *tb;</span>
|
||||
<span id="cb5-49"><a href="#cb5-49"></a> GtkTextIter start_iter;</span>
|
||||
<span id="cb5-50"><a href="#cb5-50"></a> GtkTextIter end_iter;</span>
|
||||
<span id="cb5-51"><a href="#cb5-51"></a> <span class="dt">char</span> *contents;</span>
|
||||
<span id="cb5-52"><a href="#cb5-52"></a> <span class="dt">unsigned</span> <span class="dt">int</span> n;</span>
|
||||
<span id="cb5-53"><a href="#cb5-53"></a> <span class="dt">unsigned</span> <span class="dt">int</span> i;</span>
|
||||
<span id="cb5-54"><a href="#cb5-54"></a></span>
|
||||
<span id="cb5-55"><a href="#cb5-55"></a> n = gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb));</span>
|
||||
<span id="cb5-56"><a href="#cb5-56"></a> <span class="cf">for</span> (i = <span class="dv">0</span>; i < n; ++i) {</span>
|
||||
<span id="cb5-57"><a href="#cb5-57"></a> scr = gtk_notebook_get_nth_page (GTK_NOTEBOOK (nb), i);</span>
|
||||
<span id="cb5-58"><a href="#cb5-58"></a> tv = gtk_scrolled_window_get_child (GTK_SCROLLED_WINDOW (scr));</span>
|
||||
<span id="cb5-59"><a href="#cb5-59"></a> file = tfe_text_view_get_file (TFE_TEXT_VIEW (tv));</span>
|
||||
<span id="cb5-60"><a href="#cb5-60"></a> tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));</span>
|
||||
<span id="cb5-61"><a href="#cb5-61"></a> gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter);</span>
|
||||
<span id="cb5-62"><a href="#cb5-62"></a> contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE);</span>
|
||||
<span id="cb5-63"><a href="#cb5-63"></a> <span class="cf">if</span> (! g_file_replace_contents (file, contents, strlen (contents), NULL, TRUE, G_FILE_CREATE_NONE, NULL, NULL, NULL)) {</span>
|
||||
<span id="cb5-64"><a href="#cb5-64"></a> pathname = g_file_get_path (file);</span>
|
||||
<span id="cb5-65"><a href="#cb5-65"></a> g_print (<span class="st">"ERROR : Can't save %s."</span>, pathname);</span>
|
||||
<span id="cb5-66"><a href="#cb5-66"></a> g_free (pathname);</span>
|
||||
<span id="cb5-67"><a href="#cb5-67"></a> }</span>
|
||||
<span id="cb5-68"><a href="#cb5-68"></a> g_free (contents);</span>
|
||||
<span id="cb5-69"><a href="#cb5-69"></a> }</span>
|
||||
<span id="cb5-70"><a href="#cb5-70"></a> <span class="cf">return</span> FALSE;</span>
|
||||
<span id="cb5-71"><a href="#cb5-71"></a>}</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 (GApplication *app, gpointer user_data) {</span>
|
||||
<span id="cb5-75"><a href="#cb5-75"></a> g_print (<span class="st">"You need to give filenames as arguments.</span><span class="sc">\n</span><span class="st">"</span>);</span>
|
||||
<span id="cb5-76"><a href="#cb5-76"></a>}</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 (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer user_data) {</span>
|
||||
<span id="cb5-80"><a href="#cb5-80"></a> GtkWidget *win;</span>
|
||||
<span id="cb5-81"><a href="#cb5-81"></a> GtkWidget *nb;</span>
|
||||
<span id="cb5-82"><a href="#cb5-82"></a> GtkWidget *lab;</span>
|
||||
<span id="cb5-83"><a href="#cb5-83"></a> GtkNotebookPage *nbp;</span>
|
||||
<span id="cb5-84"><a href="#cb5-84"></a> GtkWidget *scr;</span>
|
||||
<span id="cb5-85"><a href="#cb5-85"></a> GtkWidget *tv;</span>
|
||||
<span id="cb5-86"><a href="#cb5-86"></a> GtkTextBuffer *tb;</span>
|
||||
<span id="cb5-87"><a href="#cb5-87"></a> <span class="dt">char</span> *contents;</span>
|
||||
<span id="cb5-88"><a href="#cb5-88"></a> gsize length;</span>
|
||||
<span id="cb5-89"><a href="#cb5-89"></a> <span class="dt">char</span> *filename;</span>
|
||||
<span id="cb5-90"><a href="#cb5-90"></a> <span class="dt">int</span> i;</span>
|
||||
<span id="cb5-91"><a href="#cb5-91"></a></span>
|
||||
<span id="cb5-92"><a href="#cb5-92"></a> win = gtk_application_window_new (GTK_APPLICATION (app));</span>
|
||||
<span id="cb5-93"><a href="#cb5-93"></a> gtk_window_set_title (GTK_WINDOW (win), <span class="st">"file editor"</span>);</span>
|
||||
<span id="cb5-94"><a href="#cb5-94"></a> gtk_window_maximize (GTK_WINDOW (win));</span>
|
||||
<span id="cb5-95"><a href="#cb5-95"></a></span>
|
||||
<span id="cb5-96"><a href="#cb5-96"></a> nb = gtk_notebook_new ();</span>
|
||||
<span id="cb5-97"><a href="#cb5-97"></a> gtk_window_set_child (GTK_WINDOW (win), nb);</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> (i = <span class="dv">0</span>; i < n_files; i++) {</span>
|
||||
<span id="cb5-100"><a href="#cb5-100"></a> <span class="cf">if</span> (g_file_load_contents (files[i], NULL, &contents, &length, NULL, NULL)) {</span>
|
||||
<span id="cb5-101"><a href="#cb5-101"></a> scr = gtk_scrolled_window_new ();</span>
|
||||
<span id="cb5-102"><a href="#cb5-102"></a> tv = tfe_text_view_new ();</span>
|
||||
<span id="cb5-103"><a href="#cb5-103"></a> tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));</span>
|
||||
<span id="cb5-104"><a href="#cb5-104"></a> gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR);</span>
|
||||
<span id="cb5-105"><a href="#cb5-105"></a> gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);</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 (TFE_TEXT_VIEW (tv), g_file_dup (files[i]));</span>
|
||||
<span id="cb5-108"><a href="#cb5-108"></a> gtk_text_buffer_set_text (tb, contents, length);</span>
|
||||
<span id="cb5-109"><a href="#cb5-109"></a> g_free (contents);</span>
|
||||
<span id="cb5-110"><a href="#cb5-110"></a> filename = g_file_get_basename (files[i]);</span>
|
||||
<span id="cb5-111"><a href="#cb5-111"></a> lab = gtk_label_new (filename);</span>
|
||||
<span id="cb5-112"><a href="#cb5-112"></a> gtk_notebook_append_page (GTK_NOTEBOOK (nb), scr, lab);</span>
|
||||
<span id="cb5-113"><a href="#cb5-113"></a> nbp = gtk_notebook_get_page (GTK_NOTEBOOK (nb), scr);</span>
|
||||
<span id="cb5-114"><a href="#cb5-114"></a> g_object_set (nbp, <span class="st">"tab-expand"</span>, TRUE, NULL);</span>
|
||||
<span id="cb5-115"><a href="#cb5-115"></a> g_free (filename);</span>
|
||||
<span id="cb5-116"><a href="#cb5-116"></a> } <span class="cf">else</span> <span class="cf">if</span> ((filename = g_file_get_path (files[i])) != NULL) {</span>
|
||||
<span id="cb5-117"><a href="#cb5-117"></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-118"><a href="#cb5-118"></a> g_free (filename);</span>
|
||||
<span id="cb5-119"><a href="#cb5-119"></a> } <span class="cf">else</span></span>
|
||||
<span id="cb5-120"><a href="#cb5-120"></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-121"><a href="#cb5-121"></a> }</span>
|
||||
<span id="cb5-122"><a href="#cb5-122"></a> <span class="cf">if</span> (gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb)) > <span class="dv">0</span>) {</span>
|
||||
<span id="cb5-123"><a href="#cb5-123"></a> g_signal_connect (win, <span class="st">"close-request"</span>, G_CALLBACK (before_close), nb);</span>
|
||||
<span id="cb5-124"><a href="#cb5-124"></a> gtk_widget_show (win);</span>
|
||||
<span id="cb5-125"><a href="#cb5-125"></a> } <span class="cf">else</span></span>
|
||||
<span id="cb5-126"><a href="#cb5-126"></a> gtk_window_destroy (GTK_WINDOW (win));</span>
|
||||
<span id="cb5-127"><a href="#cb5-127"></a>}</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="dt">int</span> argc, <span class="dt">char</span> **argv) {</span>
|
||||
<span id="cb5-131"><a href="#cb5-131"></a> GtkApplication *app;</span>
|
||||
<span id="cb5-132"><a href="#cb5-132"></a> <span class="dt">int</span> stat;</span>
|
||||
<span id="cb5-133"><a href="#cb5-133"></a></span>
|
||||
<span id="cb5-134"><a href="#cb5-134"></a> app = gtk_application_new (<span class="st">"com.github.ToshioCP.tfe1"</span>, G_APPLICATION_HANDLES_OPEN);</span>
|
||||
<span id="cb5-135"><a href="#cb5-135"></a> g_signal_connect (app, <span class="st">"activate"</span>, G_CALLBACK (app_activate), NULL);</span>
|
||||
<span id="cb5-136"><a href="#cb5-136"></a> g_signal_connect (app, <span class="st">"open"</span>, G_CALLBACK (app_open), NULL);</span>
|
||||
<span id="cb5-137"><a href="#cb5-137"></a> stat =g_application_run (G_APPLICATION (app), argc, argv);</span>
|
||||
<span id="cb5-138"><a href="#cb5-138"></a> g_object_unref (app);</span>
|
||||
<span id="cb5-139"><a href="#cb5-139"></a> <span class="cf">return</span> stat;</span>
|
||||
<span id="cb5-140"><a href="#cb5-140"></a>}</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>
|
||||
</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 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>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec7.html">Section 7</a>, Next: <a href="sec9.html">Section 9</a></p>
|
||||
</body>
|
||||
</html>
|
463
docs/sec9.html
Normal file
463
docs/sec9.html
Normal file
|
@ -0,0 +1,463 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>Gtk4 tutorial for beginners</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
body {width: 1080px; margin: 0 auto; font-size: large;}
|
||||
h2 {padding: 10px; background-color: #d0f0d0; }
|
||||
div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
|
||||
th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
|
||||
td {padding: 2px 6px; border: 1px solid;}
|
||||
img {display: block; margin-left: auto; margin-right: auto;}
|
||||
figcaption {text-align: center;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Up: <a href="index.html">index.html</a>, Prev: <a href="sec8.html">Section 8</a>, Next: <a href="sec10.html">Section 10</a></p>
|
||||
<h1 id="the-user-interface-ui-file-and-gtkbuilder">The User Interface (UI) file and GtkBuilder</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>
|
||||
<figure>
|
||||
<img src="../image/screenshot_tfe2.png" alt="" /><figcaption>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>
|
||||
<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-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-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-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-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-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-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-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-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>
|
||||
<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>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>
|
||||
</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>
|
||||
<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>
|
||||
<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>
|
||||
</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>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>
|
||||
</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>
|
||||
<pre><code>$ cd tfe; diff tfe2.c tfe3.c
|
||||
58a59
|
||||
> GtkBuilder *build;
|
||||
60,103c61,65
|
||||
< GtkWidget *boxv;
|
||||
< GtkWidget *boxh;
|
||||
< GtkWidget *dmy1;
|
||||
< GtkWidget *dmy2;
|
||||
< GtkWidget *dmy3;
|
||||
< GtkWidget *btnn; /* button for new */
|
||||
< GtkWidget *btno; /* button for open */
|
||||
< GtkWidget *btns; /* button for save */
|
||||
< GtkWidget *btnc; /* button for close */
|
||||
<
|
||||
< win = gtk_application_window_new (GTK_APPLICATION (app));
|
||||
< gtk_window_set_title (GTK_WINDOW (win), "file editor");
|
||||
< gtk_window_set_default_size (GTK_WINDOW (win), 600, 400);
|
||||
<
|
||||
< boxv = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
|
||||
< gtk_window_set_child (GTK_WINDOW (win), boxv);
|
||||
<
|
||||
< boxh = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
|
||||
< gtk_box_append (GTK_BOX (boxv), boxh);
|
||||
<
|
||||
< dmy1 = gtk_label_new(NULL); /* dummy label for left space */
|
||||
< gtk_label_set_width_chars (GTK_LABEL (dmy1), 10);
|
||||
< dmy2 = gtk_label_new(NULL); /* dummy label for center space */
|
||||
< gtk_widget_set_hexpand (dmy2, TRUE);
|
||||
< dmy3 = gtk_label_new(NULL); /* dummy label for right space */
|
||||
< gtk_label_set_width_chars (GTK_LABEL (dmy3), 10);
|
||||
< btnn = gtk_button_new_with_label ("New");
|
||||
< btno = gtk_button_new_with_label ("Open");
|
||||
< btns = gtk_button_new_with_label ("Save");
|
||||
< btnc = gtk_button_new_with_label ("Close");
|
||||
<
|
||||
< gtk_box_append (GTK_BOX (boxh), dmy1);
|
||||
< gtk_box_append (GTK_BOX (boxh), btnn);
|
||||
< gtk_box_append (GTK_BOX (boxh), btno);
|
||||
< gtk_box_append (GTK_BOX (boxh), dmy2);
|
||||
< gtk_box_append (GTK_BOX (boxh), btns);
|
||||
< gtk_box_append (GTK_BOX (boxh), btnc);
|
||||
< gtk_box_append (GTK_BOX (boxh), dmy3);
|
||||
<
|
||||
< nb = gtk_notebook_new ();
|
||||
< gtk_widget_set_hexpand (nb, TRUE);
|
||||
< gtk_widget_set_vexpand (nb, TRUE);
|
||||
< gtk_box_append (GTK_BOX (boxv), nb);
|
||||
<
|
||||
---
|
||||
> build = gtk_builder_new_from_file ("tfe3.ui");
|
||||
> win = GTK_WIDGET (gtk_builder_get_object (build, "win"));
|
||||
> gtk_window_set_application (GTK_WINDOW (win), GTK_APPLICATION (app));
|
||||
> nb = GTK_WIDGET (gtk_builder_get_object (build, "nb"));
|
||||
> g_object_unref(build);
|
||||
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>
|
||||
<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-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 <a href="../src/tfe">src/tfe</a> directory. If you want to see it, click the link above.</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>
|
||||
<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>
|
||||
</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>
|
||||
<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>
|
||||
</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>
|
||||
<pre><code>$ LANG=C glib-compile-resources --help
|
||||
Usage:
|
||||
glib-compile-resources [OPTION?] FILE
|
||||
|
||||
Compile a resource specification into a resource file.
|
||||
Resource specification files have the extension .gresource.xml,
|
||||
and the resource file have the extension called .gresource.
|
||||
|
||||
Help Options:
|
||||
-h, --help Show help options
|
||||
|
||||
Application Options:
|
||||
--version Show program version and exit
|
||||
--target=FILE Name of the output file
|
||||
--sourcedir=DIRECTORY The directories to load files referenced in FILE from (default: current directory)
|
||||
--generate Generate output in the format selected for by the target filename extension
|
||||
--generate-header Generate source header
|
||||
--generate-source Generate source code used to link in the resource file into your code
|
||||
--generate-dependencies Generate dependency list
|
||||
--dependency-file=FILE Name of the dependency file to generate
|
||||
--generate-phony-targets Include phony targets in the generated dependency file
|
||||
--manual-register Don?t automatically create and register resource
|
||||
--internal Don?t export functions; declare them G_GNUC_INTERNAL
|
||||
--external-data Don?t embed resource data in the C file; assume it's linked externally instead
|
||||
--c-name C identifier name used for the generated source code
|
||||
</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>Up: <a href="index.html">index.html</a>, Prev: <a href="sec8.html">Section 8</a>, Next: <a href="sec10.html">Section 10</a></p>
|
||||
</body>
|
||||
</html>
|
215
docs/tfetextview_doc.html
Normal file
215
docs/tfetextview_doc.html
Normal file
|
@ -0,0 +1,215 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>Gtk4 tutorial for beginners</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
body {width: 1080px; margin: 0 auto; font-size: large;}
|
||||
h2 {padding: 10px; background-color: #d0f0d0; }
|
||||
div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
|
||||
th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
|
||||
td {padding: 2px 6px; border: 1px solid;}
|
||||
img {display: block; margin-left: auto; margin-right: auto;}
|
||||
figcaption {text-align: center;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<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>
|
||||
<h2 id="functions">Functions</h2>
|
||||
<ul>
|
||||
<li>GFile *<a href="../src/tfetextview/#tfe_text_view_get_file">tfe_text_view_get_file ()</a></li>
|
||||
<li>void <a href="../src/tfetextview/#tfe_text_view_open">tfe_text_view_open ()</a></li>
|
||||
<li>void <a href="../src/tfetextview/#tfe_text_view_save">tfe_text_view_save ()</a></li>
|
||||
<li>void <a href="../src/tfetextview/#tfe_text_view_saveas">tfe_text_view_saveas ()</a></li>
|
||||
<li>GtkWidget *<a href="../src/tfetextview/#tfe_text_view_new_with_file">tfe_text_view_new_with_file ()</a></li>
|
||||
<li>GtkWidget *<a href="../src/tfetextview/#tfe_text_view_new">tfe_text_view_new ()</a></li>
|
||||
</ul>
|
||||
<h2 id="signals">Signals</h2>
|
||||
<ul>
|
||||
<li>void <a href="../src/tfetextview/#change-file">change-file</a></li>
|
||||
<li>void <a href="../src/tfetextview/#open-response">open-response</a></li>
|
||||
</ul>
|
||||
<h2 id="types-and-values">Types and Values</h2>
|
||||
<ul>
|
||||
<li><a href="../src/tfetextview/#tfetextview-1">TfeTextView</a></li>
|
||||
<li><a href="../src/tfetextview/#tfetextviewclass">TfeTextViewClass</a></li>
|
||||
<li><a href="../src/tfetextview/#enum-tfetextviewopenresponsetype">TfeTextViewOpenResponseType</a></li>
|
||||
</ul>
|
||||
<h2 id="object-hierarchy">Object Hierarchy</h2>
|
||||
<pre><code>GObject
|
||||
+--GInitiallyUnowned
|
||||
+--GtkWidget
|
||||
+--GtkTextView
|
||||
+--TfeTextView</code></pre>
|
||||
<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>
|
||||
<h2 id="functions-1">Functions</h2>
|
||||
<h3 id="tfe_text_view_get_file">tfe_text_view_get_file()</h3>
|
||||
<pre><code>GFile *
|
||||
tfe_text_view_get_file (TfeTextView *tv);</code></pre>
|
||||
<p>Returns the copy of the GFile in the TfeTextView.</p>
|
||||
<p>Parameters</p>
|
||||
<ul>
|
||||
<li>tv: a TfeTextView</li>
|
||||
</ul>
|
||||
<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>parameters</p>
|
||||
<ul>
|
||||
<li>tv: a TfeTextView</li>
|
||||
<li>win: the top level window</li>
|
||||
</ul>
|
||||
<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>Parameters</p>
|
||||
<ul>
|
||||
<li>tv: a TfeTextView</li>
|
||||
</ul>
|
||||
<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>Parameters</p>
|
||||
<ul>
|
||||
<li>tv: a TfeTextView</li>
|
||||
</ul>
|
||||
<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>Parameters</p>
|
||||
<ul>
|
||||
<li>file: a GFile</li>
|
||||
</ul>
|
||||
<p>Returns</p>
|
||||
<ul>
|
||||
<li>a new TfeTextView.</li>
|
||||
</ul>
|
||||
<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>Returns</p>
|
||||
<ul>
|
||||
<li>a new TfeTextView.</li>
|
||||
</ul>
|
||||
<h2 id="types-and-values-1">Types and Values</h2>
|
||||
<h3 id="tfetextview">TfeTextView</h3>
|
||||
<pre><code>typedef struct _TfeTextView TfeTextView
|
||||
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>
|
||||
<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>
|
||||
<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>
|
||||
</ul>
|
||||
<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>
|
||||
<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>
|
||||
</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>
|
||||
</body>
|
||||
</html>
|
372
docs/turtle_doc.html
Normal file
372
docs/turtle_doc.html
Normal file
|
@ -0,0 +1,372 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>Gtk4 tutorial for beginners</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
body {width: 1080px; margin: 0 auto; font-size: large;}
|
||||
h2 {padding: 10px; background-color: #d0f0d0; }
|
||||
div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
|
||||
table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
|
||||
th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
|
||||
td {padding: 2px 6px; border: 1px solid;}
|
||||
img {display: block; margin-left: auto; margin-right: auto;}
|
||||
figcaption {text-align: center;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1 id="turtles-manual">Turtle’s manual</h1>
|
||||
<p>Turtle is a simple interpreter for turtle graphics.</p>
|
||||
<h2 id="prerequisite-and-compiling">Prerequisite and compiling</h2>
|
||||
<p>Turtle is written in C language. You need:</p>
|
||||
<ul>
|
||||
<li>Linux. Turtle is tested on ubuntu 20.10</li>
|
||||
<li>gcc, meson and ninja</li>
|
||||
<li>gtk4</li>
|
||||
</ul>
|
||||
<p>It is easy to compile the source file of turtle. If you have installed gtk4 with an option <code>--prefix=$HOME/local</code>, put the same option to meson so that you can install <code>turtle</code> under the directory <code>$HOME/local/bin</code>. The instruction is:</p>
|
||||
<pre><code>$ meson --prefix=$HOME/local _build
|
||||
$ ninja -C _build
|
||||
$ ninja -C _build install</code></pre>
|
||||
<p>Type the following command then turtle shows the following window.</p>
|
||||
<pre><code>$ turtle</code></pre>
|
||||
<figure>
|
||||
<img src="../src/turtle/image/turtle1.png" alt="" /><figcaption>Screenshot just after it’s executed</figcaption>
|
||||
</figure>
|
||||
<p>The left half is a text editor and the right half is a surface. Surface is like a canvas to draw shapes.</p>
|
||||
<p>Write turtle language in the text editor and click on <code>run</code> button, then the program will be executed and it draws shapes on the surface.</p>
|
||||
<figure>
|
||||
<img src="../src/turtle/image/turtle_tree.png" alt="" /><figcaption>Tree</figcaption>
|
||||
</figure>
|
||||
<p>If you add the following line in <code>turtle.h</code>, then codes to inform the status will also be compiled. However, the speed will be quite slow because of the output messages.</p>
|
||||
<pre><code># define debug 1</code></pre>
|
||||
<h2 id="example">Example</h2>
|
||||
<p>Imagine a turtle. The turtle has a pen and initially he is at the center of the screen, facing to the north (to the north means up on the screen). You can let the turtle down the pen or up the pen. You can order the turtle to move forward.</p>
|
||||
<pre><code>pd
|
||||
fd 100</code></pre>
|
||||
<ul>
|
||||
<li>pd: Pen Down. The turtle put the pen down so that the turtle will draw a line if he/she moves.</li>
|
||||
<li>fd 100: move ForwarD 100. The turtle goes forward 100 pixels.</li>
|
||||
</ul>
|
||||
<p>If you click on <code>run</code> button, then a line segment appears on the screen. One of the endpoints of the line segment is at the center of the surface and the other is at 100 pixels up from the center. The point at the center is the start point of the turtle and the other endpoint is the end point of the movement.</p>
|
||||
<p>If the turtle picks the pen up, then no line segment appears.</p>
|
||||
<pre><code>pu
|
||||
fd 100</code></pre>
|
||||
<p>The command <code>pu</code> means “Pen Up”.</p>
|
||||
<p>The turtle can change the direction.</p>
|
||||
<pre><code>pd
|
||||
fd 100
|
||||
tr 90
|
||||
fd 100</code></pre>
|
||||
<p>The command <code>tr</code> is “Turn Right”. The argument is angle with degrees. Therefore, <code>tr 90</code> means “Turn right by 90 degrees”. If you click on the <code>run</code>button, then two line segments appears. One is vertical and the other is horizontal.</p>
|
||||
<figure>
|
||||
<img src="../src/turtle/image/turtle2.png" alt="" /><figcaption>Two line segments on the surface</figcaption>
|
||||
</figure>
|
||||
<h2 id="background-and-foreground-color">Background and foreground color</h2>
|
||||
<p>Colors are specified with RGB. A vector (r, g, b) denotes RGB color. Each of the elements is a real number between 0 and 1.</p>
|
||||
<ul>
|
||||
<li>Red is (1.0, 0.0, 0.0). You can write (1, 0, 0) instead.</li>
|
||||
<li>Green is (0.0, 1.0, 0.0)</li>
|
||||
<li>Blue is (0.0, 0.0, 1.0)</li>
|
||||
<li>Black is (0.0, 0.0, 0.0)</li>
|
||||
<li>White is (1.0, 1.0, 1.0)</li>
|
||||
</ul>
|
||||
<p>You can express a variety of colors by changing each element.</p>
|
||||
<p>There are two commands to change colors.</p>
|
||||
<ul>
|
||||
<li>bc: Background Color. <code>bc (1,0,0)</code> changes the background color to red. This command clear the surface and change the background color. So, the shapes on the surface disappears.</li>
|
||||
<li>fc: Foreground Color. <code>fc (0,1,0)</code> changes the foreground color to green. This command changes the pen color. The prior shapes on the surface aren’t affected. After this command, the turtle draws lines with the new color.</li>
|
||||
</ul>
|
||||
<figure>
|
||||
<img src="../src/turtle/image/turtle3.png" alt="" /><figcaption>Change the foreground color</figcaption>
|
||||
</figure>
|
||||
<h2 id="other-simple-commands">Other simple commands</h2>
|
||||
<ul>
|
||||
<li>pw: Pen Width. This is the same as pen size or line width. For example, <code>pw 5</code> makes lines thick and <code>pw 1</code> makes it thin.</li>
|
||||
<li>rs: ReSet. The turtle moves back to the initial position and direction. In addition, The command initialize the pen, line width (pen size), and foreground color. The pen is down, the line width is 2 and the foreground color is black.</li>
|
||||
</ul>
|
||||
<p>An order such as <code>fd 100</code>, <code>pd</code> and so on is a statement. Statements are executed in the order from the top to the end</p>
|
||||
<h2 id="comment-and-spaces">Comment and spaces</h2>
|
||||
<p>Characters between <code>#</code> (hash mark) and <code>\n</code> (new line) inclusive are comment. If the comment is at the end of the file, the trailing new line can be left out. Comments are ignored.</p>
|
||||
<pre><code># draw a triangle
|
||||
fd 100 # forward 100 pixels<NEW LINE>
|
||||
tr 120 # turn right by 90 degrees<NEW LINE>
|
||||
fd 100<NEW LINE>
|
||||
tr 120<NEW LINE>
|
||||
fd 100 # Now a triangle appears.<EOF></code></pre>
|
||||
<p><NEW LINE> and <EOF> indicate newline code and end of file respectively. The comments in the line 1, 2, 3 and 6 are correct syntactically.</p>
|
||||
<p>Spaces (white space, tab and new line) are ignored. They are used only as delimiters. Tabs are recognized as eight spaces to calculate the column number.</p>
|
||||
<h2 id="variables-and-expressions">Variables and expressions</h2>
|
||||
<p>Variable begins alphabet followed by alphabet or digit. Key words like <code>fd</code>, <code>tr</code> can’t be variables. <code>Distance</code> and <code>angle5</code> are variables, but <code>1step</code> isn’t a variable because the first character isn’t alphabet. Variable names are case sensitive. Variables keep real numbers. Their type is the same as <code>double</code> in C language. Integers are casted to real numbers automatically. So 1 and 1.0 are the same value. Numbers begin digits, not signs (<code>+</code> or <code>-</code>).</p>
|
||||
<ul>
|
||||
<li>100, 20.34 and 0.01 are numbers</li>
|
||||
<li>+100 isn’t a number. It causes syntax error. Use 100 instead.</li>
|
||||
<li>-100 isn’t a number. But turtle recognizes it unary minus and a number 100. So turtle calculate it and the result is -100.</li>
|
||||
<li>100 + -20: This is recognized 100 + (- 20). However, using bracket, 100 + (-20), is better for easy reading.</li>
|
||||
</ul>
|
||||
<pre><code>distance = 100
|
||||
fd distance</code></pre>
|
||||
<p>A value 100 is assigned to the variable <code>distance</code> in the first line. Assignment is a statement. Most of statements begin with commands like <code>fd</code>. Assignment is the only exception.</p>
|
||||
<p>The example above draws a line segment of 100 pixels long.</p>
|
||||
<p>You can use variables in expressions. There are 8 kinds of calculations available.</p>
|
||||
<ul>
|
||||
<li>addition: x + y</li>
|
||||
<li>subtraction: x - y</li>
|
||||
<li>multiplication: x * y</li>
|
||||
<li>division: x / y</li>
|
||||
<li>unary minus: - x</li>
|
||||
<li>logical equal: x = y. This symbol <code>=</code> works as <code>==</code> in C language.</li>
|
||||
<li>greater than: x > y</li>
|
||||
<li>less than: x < y</li>
|
||||
</ul>
|
||||
<p>The last three symbols are mainly used in the condition of if statement.</p>
|
||||
<p>Variables are registered to a symbol table when it is assigned a value for the first time. Evaluating a variable before the registration isn’t allowed and occurs an error.</p>
|
||||
<h2 id="if-statement">If statement</h2>
|
||||
<p>Turtle language has very simple if statement.</p>
|
||||
<pre><code>if (x > 50) {
|
||||
fd x
|
||||
}</code></pre>
|
||||
<p>There is no else part.</p>
|
||||
<h2 id="procedures">Procedures</h2>
|
||||
<p>Procedures are similar to functions in C language. The difference is that procedures don’t have return values.</p>
|
||||
<pre><code>dp triangle (side) {
|
||||
fd side
|
||||
tr 120
|
||||
fd side
|
||||
tr 120
|
||||
fd side
|
||||
}
|
||||
|
||||
triangle (100)</code></pre>
|
||||
<p><code>dp</code> (Define Procedure) is a key word followed by procedure name, parameters, and body. Procedure names start alphabet followed by alphabet or digit. Parameters are a list of variables. For example</p>
|
||||
<pre><code>dp abc (a) { ... ... }
|
||||
dp abc (a, b) { ... ... }
|
||||
dp abc (a, b, c) { ... ... }</code></pre>
|
||||
<p>Body is a sequence of statements. The statements aren’t executed when the procedure is defined. They will be executed when the procedure is called later.</p>
|
||||
<p>Procedures are called by the name followed by arguments.</p>
|
||||
<pre><code>dp proc (a, b, c) { ... ... }
|
||||
|
||||
proc (100, 0, -20*3)</code></pre>
|
||||
<p>The number of parameters and arguments must be the same. Arguments can be any expressions. When you call a procedure, brackets following the procedure name must exist even if the procedure has no argument.</p>
|
||||
<p>Procedure names and variable names don’t conflict.</p>
|
||||
<pre><code>dp a () {fd a}
|
||||
a=100
|
||||
a ()</code></pre>
|
||||
<p>This is a correct program.</p>
|
||||
<ul>
|
||||
<li>1: Defines a procedure <code>a</code>. A variable <code>a</code> is in its body.</li>
|
||||
<li>2: Assigns 100 to a variable <code>a</code>.</li>
|
||||
<li>3: Procedure <code>a</code> is called.</li>
|
||||
</ul>
|
||||
<p>However, using the same name to a procedure and variable makes confusing. You should avoid that.</p>
|
||||
<h2 id="recursive-call">Recursive call</h2>
|
||||
<p>Procedures can be called recursively.</p>
|
||||
<pre><code>dp repeat (n) {
|
||||
n = n - 1
|
||||
if (n < 0) {
|
||||
rt
|
||||
}
|
||||
fd 100
|
||||
tr 90
|
||||
repeat (n)
|
||||
}
|
||||
|
||||
repeat (4)</code></pre>
|
||||
<p>Repeat is called in the body of repeat. The call to itself is a recursive call. Parameters are created and set each time the procedure is called. So, parameter <code>n</code> is 4 at the first call but it is 3 at the second call. Each time the procedure is called, the parameter <code>n</code> decreases by one. Finally, it becomes less than zero, then the procedures return.</p>
|
||||
<p>The program above draws a square.</p>
|
||||
<p>Turtle doesn’t have any primary loop statements. It should probably be added to the future version. However, the program above shows that we can program loop with a recursive call.</p>
|
||||
<h2 id="fractal-curves">Fractal curves</h2>
|
||||
<p>Recursive call can be applied to draw fractal curves. Fractal curves appear when a procedure is applied to it repeatedly. The procedure replaces a part of the curve with the contracted curve.</p>
|
||||
<figure>
|
||||
<img src="../src/turtle/image/turtle_tree.png" alt="" /><figcaption>Tree</figcaption>
|
||||
</figure>
|
||||
<p>This shape is called tree. The basic pattern of this shape is a line segment. It is the first stage. The second stage adds two shorter line segments at the endpoint of the original segment. The new segment has 70 percent length to the original segment and the orientation is +30 or -30 degrees different. The third stage adds two shorter line segments to the second stage line segments. And repeats it several times.</p>
|
||||
<p>This repeating is programmed by recursive call. Two more examples are shown here. They are Koch curve and Square Koch curve.</p>
|
||||
<figure>
|
||||
<img src="../src/turtle/image/turtle_koch.png" alt="" /><figcaption>Koch curve</figcaption>
|
||||
</figure>
|
||||
<figure>
|
||||
<img src="../src/turtle/image/turtle_square_koch.png" alt="" /><figcaption>Square Koch curve</figcaption>
|
||||
</figure>
|
||||
<h2 id="tokens-and-punctuations">Tokens and punctuations</h2>
|
||||
<p>The following is the list of tokens.</p>
|
||||
<p>Keywords:</p>
|
||||
<ul>
|
||||
<li>pu: pen up</li>
|
||||
<li>pd: pen down</li>
|
||||
<li>pw: pen width = line width</li>
|
||||
<li>fd: forward</li>
|
||||
<li>tr: turn right</li>
|
||||
<li>bc: background color</li>
|
||||
<li>fc: foreground color</li>
|
||||
<li>if: if statement</li>
|
||||
<li>rt: return</li>
|
||||
<li>rs: reset</li>
|
||||
<li>dp: define procedure</li>
|
||||
</ul>
|
||||
<p>identifiers and numbers:</p>
|
||||
<ul>
|
||||
<li>identifier: This is used for the name of variables, parameters and procedures. It is expressed <code>[a-zA-Z][a-zA-Z0-9]*</code> in regular expression.</li>
|
||||
<li>number: This is expressed <code>(0|[1-9][0-9]*)(\.[0-9]+)?</code> in regular expression. It doesn’t have <code>+</code> or <code>-</code> sign because they bring some syntactic confusion. However negative number such as <code>-10</code> can be recognized as unary minus and a number.</li>
|
||||
</ul>
|
||||
<p>Symbols for expression</p>
|
||||
<ul>
|
||||
<li><code>=</code></li>
|
||||
<li><code>></code></li>
|
||||
<li><code><</code></li>
|
||||
<li><code>+</code></li>
|
||||
<li><code>-</code></li>
|
||||
<li><code>*</code></li>
|
||||
<li><code>/</code></li>
|
||||
<li><code>(</code></li>
|
||||
<li><code>)</code></li>
|
||||
</ul>
|
||||
<p>Delimiters</p>
|
||||
<ul>
|
||||
<li><code>(</code></li>
|
||||
<li><code>)</code></li>
|
||||
<li><code>{</code></li>
|
||||
<li><code>}</code></li>
|
||||
<li><code>,</code></li>
|
||||
</ul>
|
||||
<p>Comments and spaces:</p>
|
||||
<ul>
|
||||
<li>comment: This is characters between <code>#</code> and new line inclusive. If a comment is at the end of the file, the trailing new line can be left out.</li>
|
||||
<li>white space:</li>
|
||||
<li>horizontal tab: tab is recognized as eight spaces.</li>
|
||||
<li>new line: This is the end of a line.</li>
|
||||
</ul>
|
||||
<p>These characters are used to separate tokens explicitly. They doesn’t have any syntactic meaning and are ignored by the parser.</p>
|
||||
<h2 id="grammar">Grammar</h2>
|
||||
<pre><code>program:
|
||||
statement
|
||||
| program statement
|
||||
;
|
||||
|
||||
statement:
|
||||
primary_procedure
|
||||
| procedure_definition
|
||||
;
|
||||
|
||||
primary_procedure:
|
||||
PU
|
||||
| PD
|
||||
| PW expression
|
||||
| FD expression
|
||||
| TR expression
|
||||
| BC '(' expression ',' expression ',' expression ')'
|
||||
| FC '(' expression ',' expression ',' expression ')'
|
||||
| ID '=' expression
|
||||
| IF '(' expression ')' '{' primary_procedure_list '}'
|
||||
| RT
|
||||
| RS
|
||||
| ID '(' ')'
|
||||
| ID '(' argument_list ')'
|
||||
;
|
||||
|
||||
procedure_definition:
|
||||
DP ID '(' ')' '{' primary_procedure_list '}'
|
||||
| DP ID '(' parameter_list ')' '{' primary_procedure_list '}'
|
||||
;
|
||||
|
||||
parameter_list:
|
||||
ID
|
||||
| parameter_list ',' ID
|
||||
;
|
||||
|
||||
argument_list:
|
||||
expression
|
||||
| argument_list ',' expression
|
||||
;
|
||||
|
||||
primary_procedure_list:
|
||||
primary_procedure
|
||||
| primary_procedure_list primary_procedure
|
||||
;
|
||||
|
||||
expression:
|
||||
expression '=' expression
|
||||
| expression '>' expression
|
||||
| expression '<' expression
|
||||
| expression '+' expression
|
||||
| expression '-' expression
|
||||
| expression '*' expression
|
||||
| expression '/' expression
|
||||
| '-' expression %prec UMINUS
|
||||
| '(' expression ')'
|
||||
| ID
|
||||
| NUM
|
||||
;</code></pre>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in a new issue