Gtk4-tutorial/docs/sec7.html
2023-01-03 15:30:06 +09:00

468 lines
41 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<title>GTK 4 tutorial</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
pre{overflow: visible;}
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::after
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
code span.at { color: #7d9029; } /* Attribute */
code span.bn { color: #40a070; } /* BaseN */
code span.bu { } /* BuiltIn */
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
code span.ch { color: #4070a0; } /* Char */
code span.cn { color: #880000; } /* Constant */
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
code span.dt { color: #902000; } /* DataType */
code span.dv { color: #40a070; } /* DecVal */
code span.er { color: #ff0000; font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #40a070; } /* Float */
code span.fu { color: #06287e; } /* Function */
code span.im { } /* Import */
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
code span.op { color: #666666; } /* Operator */
code span.ot { color: #007020; } /* Other */
code span.pp { color: #bc7a00; } /* Preprocessor */
code span.sc { color: #4070a0; } /* SpecialChar */
code span.ss { color: #bb6688; } /* SpecialString */
code span.st { color: #4070a0; } /* String */
code span.va { color: #19177c; } /* Variable */
code span.vs { color: #4070a0; } /* VerbatimString */
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
td {padding: 2px 6px; border: 1px solid;}
img {display: block; margin-left: auto; margin-right: auto;}
figcaption {text-align: center;}
</style>
</head>
<body style="padding-top: 70px;">
<div class="container">
<nav class="navbar fixed-top navbar-expand-lg navbar-dark bg-primary">
<div class="container-fluid">
<span class="navbar-brand">Gtk4 tutorial</span>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link" href="index.html">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="sec6.html">Prev: section6</a>
</li>
<li class="nav-item">
<a class="nav-link" href="sec8.html">Next: section8</a>
</li>
</ul>
</div>
</div>
</nav>
<h1 id="widgets-3">Widgets (3)</h1>
<h2 id="open-signal">Open signal</h2>
<h3 id="g_application_handles_open-flag">G_APPLICATION_HANDLES_OPEN
flag</h3>
<p>We made a very simple editor in the previous section with
GtkTextView, GtkTextBuffer and GtkScrolledWindow. We will add
file-reading ability to the program and improve it to a file viewer.</p>
<p>The easiest way to give a filename is to use a command line
argument.</p>
<pre><code>$ ./a.out filename</code></pre>
<p>The program will open the file and insert its contents into the
GtkTextBuffer.</p>
<p>To do this, we need to know how GtkApplication (or GApplication)
recognizes arguments. This is described in the <a
href="https://docs.gtk.org/gio/class.Application.html">GIO API Reference
Application</a>.</p>
<p>When GtkApplication is created, a flag (GApplicationFlags) is given
as an argument.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>GtkApplication <span class="op">*</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>gtk_application_new <span class="op">(</span><span class="dt">const</span> gchar <span class="op">*</span>application_id<span class="op">,</span> GApplicationFlags flags<span class="op">);</span></span></code></pre></div>
<p>This tutorial explains only two flags,
<code>G_APPLICATION_DEFAULT_FLAGS</code> and
<code>G_APPLICATION_HANDLES_OPEN</code>.
(<code>G_APPLICATION_FLAGS_NONE</code> was used instead of
<code>G_APPLICATION_DEFAULT_FLAGS</code> before GIO version2.73.3 (GLib
2.73.3 5/Aug/2022). Some GTK 4 applications still use
<code>G_APPLICATION_FLAGS_NONE</code>. But now it is deprecated and
<code>G_APPLICATION_DEFAULT_FLAGS</code> is recommended.) If you want to
handle command line arguments, the
<code>G_APPLICATION_HANDLES_COMMAND_LINE</code> flag is what you
need.</p>
<p>For further information, see <a
href="https://docs.gtk.org/gio/flags.ApplicationFlags.html">GIO API
Reference ApplicationFlags</a> and <a
href="https://docs.gtk.org/gio/method.Application.run.html">GIO API
Reference g_application_run</a>.</p>
<p>Weve already used <code>G_APPLICATION_DEFAULT_FLAGS</code>, as it is
the simplest option, and no command line arguments are allowed. If you
give arguments, an error will occur.</p>
<p>The flag <code>G_APPLICATION_HANDLES_OPEN</code> is the second
simplest option. It allows arguments but only files. The application
assumes all the arguments are filenames.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>app <span class="op">=</span> gtk_application_new <span class="op">(</span><span class="st">&quot;com.github.ToshioCP.tfv3&quot;</span><span class="op">,</span> G_APPLICATION_HANDLES_OPEN<span class="op">);</span></span></code></pre></div>
<h3 id="open-signal-1">open signal</h3>
<p>When <code>G_APPLICATION_HANDLES_OPEN</code> flag is given to the
application, two signals are available.</p>
<ul>
<li>activate signal — This signal is emitted when theres 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="cb4"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>open <span class="op">(</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a> GApplication<span class="op">*</span> self<span class="op">,</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a> gpointer files<span class="op">,</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a> gint n_files<span class="op">,</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a> gchar<span class="op">*</span> hint<span class="op">,</span></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a> gpointer user_data</span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a><span class="op">)</span></span></code></pre></div>
<p>The parameters are:</p>
<ul>
<li><code>self</code> — the application instance (usually
GtkApplication)</li>
<li><code>files</code> — an array of GFiles. [array length=n_files]
[element-type GFile]</li>
<li><code>n_files</code> — the number of the elements of
<code>files</code></li>
<li><code>hint</code> — a hint provided by the calling instance (usually
it can be ignored)</li>
<li><code>user_data</code> — user data set when the signal handler was
connected.</li>
</ul>
<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 text files. Our file viewer
will work as follows.</p>
<ul>
<li>When arguments are given, it recognizes the first argument as a
filename and opens it.</li>
<li>The second argument and after are ignored.</li>
<li>If theres no argument, it shows an error message and quit.</li>
<li>If it successfully opens the file, it reads the contents of the
file, inserts them to GtkTextBuffer and shows the window.</li>
<li>If it fails to open the file, it shows an error message and
quit.</li>
</ul>
<p>The program is shown below.</p>
<div class="sourceCode" id="cb5"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb5-1"><a href="#cb5-1"></a><span class="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span>
<span id="cb5-2"><a href="#cb5-2"></a></span>
<span id="cb5-3"><a href="#cb5-3"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb5-4"><a href="#cb5-4"></a>app_activate <span class="op">(</span>GApplication <span class="op">*</span>app<span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-5"><a href="#cb5-5"></a> g_printerr <span class="op">(</span><span class="st">&quot;You need a filename argument.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb5-6"><a href="#cb5-6"></a><span class="op">}</span></span>
<span id="cb5-7"><a href="#cb5-7"></a></span>
<span id="cb5-8"><a href="#cb5-8"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb5-9"><a href="#cb5-9"></a>app_open <span class="op">(</span>GApplication <span class="op">*</span>app<span class="op">,</span> GFile <span class="op">**</span> files<span class="op">,</span> <span class="dt">int</span> n_files<span class="op">,</span> <span class="dt">char</span> <span class="op">*</span>hint<span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-10"><a href="#cb5-10"></a> GtkWidget <span class="op">*</span>win<span class="op">;</span></span>
<span id="cb5-11"><a href="#cb5-11"></a> GtkWidget <span class="op">*</span>scr<span class="op">;</span></span>
<span id="cb5-12"><a href="#cb5-12"></a> GtkWidget <span class="op">*</span>tv<span class="op">;</span></span>
<span id="cb5-13"><a href="#cb5-13"></a> GtkTextBuffer <span class="op">*</span>tb<span class="op">;</span></span>
<span id="cb5-14"><a href="#cb5-14"></a> <span class="dt">char</span> <span class="op">*</span>contents<span class="op">;</span></span>
<span id="cb5-15"><a href="#cb5-15"></a> gsize length<span class="op">;</span></span>
<span id="cb5-16"><a href="#cb5-16"></a> <span class="dt">char</span> <span class="op">*</span>filename<span class="op">;</span></span>
<span id="cb5-17"><a href="#cb5-17"></a></span>
<span id="cb5-18"><a href="#cb5-18"></a> win <span class="op">=</span> gtk_application_window_new <span class="op">(</span>GTK_APPLICATION <span class="op">(</span>app<span class="op">));</span></span>
<span id="cb5-19"><a href="#cb5-19"></a> gtk_window_set_default_size <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="dv">400</span><span class="op">,</span> <span class="dv">300</span><span class="op">);</span></span>
<span id="cb5-20"><a href="#cb5-20"></a></span>
<span id="cb5-21"><a href="#cb5-21"></a> scr <span class="op">=</span> gtk_scrolled_window_new <span class="op">();</span></span>
<span id="cb5-22"><a href="#cb5-22"></a> gtk_window_set_child <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> scr<span class="op">);</span></span>
<span id="cb5-23"><a href="#cb5-23"></a></span>
<span id="cb5-24"><a href="#cb5-24"></a> tv <span class="op">=</span> gtk_text_view_new <span class="op">();</span></span>
<span id="cb5-25"><a href="#cb5-25"></a> tb <span class="op">=</span> gtk_text_view_get_buffer <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
<span id="cb5-26"><a href="#cb5-26"></a> gtk_text_view_set_wrap_mode <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> GTK_WRAP_WORD_CHAR<span class="op">);</span></span>
<span id="cb5-27"><a href="#cb5-27"></a> gtk_text_view_set_editable <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> FALSE<span class="op">);</span></span>
<span id="cb5-28"><a href="#cb5-28"></a> gtk_scrolled_window_set_child <span class="op">(</span>GTK_SCROLLED_WINDOW <span class="op">(</span>scr<span class="op">),</span> tv<span class="op">);</span></span>
<span id="cb5-29"><a href="#cb5-29"></a></span>
<span id="cb5-30"><a href="#cb5-30"></a> <span class="cf">if</span> <span class="op">(</span>g_file_load_contents <span class="op">(</span>files<span class="op">[</span><span class="dv">0</span><span class="op">],</span> NULL<span class="op">,</span> <span class="op">&amp;</span>contents<span class="op">,</span> <span class="op">&amp;</span>length<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">))</span> <span class="op">{</span></span>
<span id="cb5-31"><a href="#cb5-31"></a> gtk_text_buffer_set_text <span class="op">(</span>tb<span class="op">,</span> contents<span class="op">,</span> length<span class="op">);</span></span>
<span id="cb5-32"><a href="#cb5-32"></a> g_free <span class="op">(</span>contents<span class="op">);</span></span>
<span id="cb5-33"><a href="#cb5-33"></a> <span class="cf">if</span> <span class="op">((</span>filename <span class="op">=</span> g_file_get_basename <span class="op">(</span>files<span class="op">[</span><span class="dv">0</span><span class="op">]))</span> <span class="op">!=</span> NULL<span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-34"><a href="#cb5-34"></a> gtk_window_set_title <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> filename<span class="op">);</span></span>
<span id="cb5-35"><a href="#cb5-35"></a> g_free <span class="op">(</span>filename<span class="op">);</span></span>
<span id="cb5-36"><a href="#cb5-36"></a> <span class="op">}</span></span>
<span id="cb5-37"><a href="#cb5-37"></a> gtk_window_present <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
<span id="cb5-38"><a href="#cb5-38"></a> <span class="op">}</span> <span class="cf">else</span> <span class="op">{</span></span>
<span id="cb5-39"><a href="#cb5-39"></a> <span class="cf">if</span> <span class="op">((</span>filename <span class="op">=</span> g_file_get_path <span class="op">(</span>files<span class="op">[</span><span class="dv">0</span><span class="op">]))</span> <span class="op">!=</span> NULL<span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-40"><a href="#cb5-40"></a> g_printerr <span class="op">(</span><span class="st">&quot;No such file: %s.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">,</span> filename<span class="op">);</span></span>
<span id="cb5-41"><a href="#cb5-41"></a> g_free <span class="op">(</span>filename<span class="op">);</span></span>
<span id="cb5-42"><a href="#cb5-42"></a> <span class="op">}</span> <span class="cf">else</span></span>
<span id="cb5-43"><a href="#cb5-43"></a> g_printerr <span class="op">(</span><span class="st">&quot;File can&#39;t be opened.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb5-44"><a href="#cb5-44"></a> gtk_window_destroy <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
<span id="cb5-45"><a href="#cb5-45"></a> <span class="op">}</span></span>
<span id="cb5-46"><a href="#cb5-46"></a><span class="op">}</span></span>
<span id="cb5-47"><a href="#cb5-47"></a></span>
<span id="cb5-48"><a href="#cb5-48"></a><span class="dt">int</span></span>
<span id="cb5-49"><a href="#cb5-49"></a>main <span class="op">(</span><span class="dt">int</span> argc<span class="op">,</span> <span class="dt">char</span> <span class="op">**</span>argv<span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-50"><a href="#cb5-50"></a> GtkApplication <span class="op">*</span>app<span class="op">;</span></span>
<span id="cb5-51"><a href="#cb5-51"></a> <span class="dt">int</span> stat<span class="op">;</span></span>
<span id="cb5-52"><a href="#cb5-52"></a></span>
<span id="cb5-53"><a href="#cb5-53"></a> app <span class="op">=</span> gtk_application_new <span class="op">(</span><span class="st">&quot;com.github.ToshioCP.tfv3&quot;</span><span class="op">,</span> G_APPLICATION_HANDLES_OPEN<span class="op">);</span></span>
<span id="cb5-54"><a href="#cb5-54"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">&quot;activate&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>app_activate<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb5-55"><a href="#cb5-55"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">&quot;open&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>app_open<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb5-56"><a href="#cb5-56"></a> stat <span class="op">=</span> g_application_run <span class="op">(</span>G_APPLICATION <span class="op">(</span>app<span class="op">),</span> argc<span class="op">,</span> argv<span class="op">);</span></span>
<span id="cb5-57"><a href="#cb5-57"></a> g_object_unref <span class="op">(</span>app<span class="op">);</span></span>
<span id="cb5-58"><a href="#cb5-58"></a> <span class="cf">return</span> stat<span class="op">;</span></span>
<span id="cb5-59"><a href="#cb5-59"></a><span class="op">}</span></span></code></pre></div>
<p>Save it as <code>tfv3.c</code>. Then compile and run it.</p>
<pre><code>$ comp tfv3
$ ./a.out tfv3.c</code></pre>
<figure>
<img src="image/screenshot_tfv3.png" alt="File viewer" />
<figcaption aria-hidden="true">File viewer</figcaption>
</figure>
<p>The function <code>main</code> has only two changes from the previous
version.</p>
<ul>
<li><code>G_APPLICATION_DEFAULT_FLAGS</code> is replaced by
<code>G_APPLICATION_HANDLES_OPEN</code></li>
<li><code>g_signal_connect (app, "open", G_CALLBACK (app_open), NULL)</code>
is added.</li>
</ul>
<p>When the flag <code>G_APPLICATION_HANDLES_OPEN</code> is given to
<code>gtk_application_new</code> function, the application behaves like
this:</p>
<ul>
<li>If the application is run without command line arguments, it emits
“activate” signal when it is activated.</li>
<li>If the application is run with command line arguments, it emits
“open” signal when it is activated.</li>
</ul>
<p>The handler <code>app_activate</code> becomes very simple. It just
outputs the error message and return to the caller. Then the application
quits immediately because no window is created.</p>
<p>The main work is done in the handler <code>app_open</code>.</p>
<ul>
<li>Creates GtkApplicationWindow, GtkScrolledWindow, GtkTextView and
GtkTextBuffer and connects them together</li>
<li>Sets wrap mode to <code>GTK_WRAP_WORD_CHAR</code> in
GtktextView</li>
<li>Sets GtkTextView to non-editable because the program isnt an editor
but only a viewer</li>
<li>Reads the file and inserts the text into GtkTextBuffer (this will be
explained later)</li>
<li>If the file is not opened, outputs an error message and destroys the
window. This makes the application quit.</li>
</ul>
<p>The following is the file reading part of the program again.</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="op">(</span>g_file_load_contents <span class="op">(</span>files<span class="op">[</span><span class="dv">0</span><span class="op">],</span> NULL<span class="op">,</span> <span class="op">&amp;</span>contents<span class="op">,</span> <span class="op">&amp;</span>length<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">))</span> <span class="op">{</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a> gtk_text_buffer_set_text <span class="op">(</span>tb<span class="op">,</span> contents<span class="op">,</span> length<span class="op">);</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a> g_free <span class="op">(</span>contents<span class="op">);</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">((</span>filename <span class="op">=</span> g_file_get_basename <span class="op">(</span>files<span class="op">[</span><span class="dv">0</span><span class="op">]))</span> <span class="op">!=</span> NULL<span class="op">)</span> <span class="op">{</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a> gtk_window_set_title <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> filename<span class="op">);</span></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a> g_free <span class="op">(</span>filename<span class="op">);</span></span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a> gtk_widget_show <span class="op">(</span>win<span class="op">);</span></span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a><span class="op">}</span> <span class="cf">else</span> <span class="op">{</span></span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">((</span>filename <span class="op">=</span> g_file_get_path <span class="op">(</span>files<span class="op">[</span><span class="dv">0</span><span class="op">]))</span> <span class="op">!=</span> NULL<span class="op">)</span> <span class="op">{</span></span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a> g_printerr <span class="op">(</span><span class="st">&quot;No such file: %s.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">,</span> filename<span class="op">);</span></span>
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true" tabindex="-1"></a> g_free <span class="op">(</span>filename<span class="op">);</span></span>
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span> <span class="cf">else</span></span>
<span id="cb7-14"><a href="#cb7-14" aria-hidden="true" tabindex="-1"></a> g_printerr <span class="op">(</span><span class="st">&quot;File can&#39;t be opened.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb7-15"><a href="#cb7-15" aria-hidden="true" tabindex="-1"></a> gtk_window_destroy <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
<span id="cb7-16"><a href="#cb7-16" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>The function <code>g_file_load_contents</code> loads the file
contents into a temporary buffer, which is automatically allocated and
sets <code>contents</code> to point the buffer. The length of the buffer
is assigned to <code>length</code>. It returns <code>TRUE</code> if the
files contents are successfully loaded or <code>FALSE</code> if an
error occurs. If you want to know the details about
g_file_load_contents, see <a
href="https://docs.gtk.org/gio/method.File.load_contents.html">g file
load contents</a>.</p>
<p>If it has successfully read the file, it inserts the contents into
GtkTextBuffer, frees the temporary buffer pointed by
<code>contents</code>, sets the title of the window, frees the memories
pointed by <code>filename</code> and then shows the window. If it fails,
it outputs an error message and destroys the window and finally make the
program quit.</p>
<h2 id="gtknotebook">GtkNotebook</h2>
<p>GtkNotebook is a container widget that contains multiple widgets with
tabs. It shows only one child at a time. Another child will be shown
when its tab is clicked.</p>
<figure>
<img src="image/screenshot_gtk_notebook.png" alt="GtkNotebook" />
<figcaption aria-hidden="true">GtkNotebook</figcaption>
</figure>
<p>The left image is the window at the startup. The file
<code>pr1.c</code> is shown and its filename is in the left tab. After
clicking on the right tab, the contents of the file <code>tfv1.c</code>
is shown. The right image is the screenshot.</p>
<p>The following is <code>tfv4.c</code>. It has GtkNoteBook widget. It
is inserted as a child of GtkApplicationWindow and contains multiple
GtkScrolledWindow.</p>
<div class="sourceCode" id="cb8"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb8-1"><a href="#cb8-1"></a><span class="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span>
<span id="cb8-2"><a href="#cb8-2"></a></span>
<span id="cb8-3"><a href="#cb8-3"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb8-4"><a href="#cb8-4"></a>app_activate <span class="op">(</span>GApplication <span class="op">*</span>app<span class="op">)</span> <span class="op">{</span></span>
<span id="cb8-5"><a href="#cb8-5"></a> g_printerr <span class="op">(</span><span class="st">&quot;You need a filename argument.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb8-6"><a href="#cb8-6"></a><span class="op">}</span></span>
<span id="cb8-7"><a href="#cb8-7"></a></span>
<span id="cb8-8"><a href="#cb8-8"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb8-9"><a href="#cb8-9"></a>app_open <span class="op">(</span>GApplication <span class="op">*</span>app<span class="op">,</span> GFile <span class="op">**</span> files<span class="op">,</span> gint n_files<span class="op">,</span> gchar <span class="op">*</span>hint<span class="op">)</span> <span class="op">{</span></span>
<span id="cb8-10"><a href="#cb8-10"></a> GtkWidget <span class="op">*</span>win<span class="op">;</span></span>
<span id="cb8-11"><a href="#cb8-11"></a> GtkWidget <span class="op">*</span>nb<span class="op">;</span></span>
<span id="cb8-12"><a href="#cb8-12"></a> GtkWidget <span class="op">*</span>lab<span class="op">;</span></span>
<span id="cb8-13"><a href="#cb8-13"></a> GtkNotebookPage <span class="op">*</span>nbp<span class="op">;</span></span>
<span id="cb8-14"><a href="#cb8-14"></a> GtkWidget <span class="op">*</span>scr<span class="op">;</span></span>
<span id="cb8-15"><a href="#cb8-15"></a> GtkWidget <span class="op">*</span>tv<span class="op">;</span></span>
<span id="cb8-16"><a href="#cb8-16"></a> GtkTextBuffer <span class="op">*</span>tb<span class="op">;</span></span>
<span id="cb8-17"><a href="#cb8-17"></a> <span class="dt">char</span> <span class="op">*</span>contents<span class="op">;</span></span>
<span id="cb8-18"><a href="#cb8-18"></a> gsize length<span class="op">;</span></span>
<span id="cb8-19"><a href="#cb8-19"></a> <span class="dt">char</span> <span class="op">*</span>filename<span class="op">;</span></span>
<span id="cb8-20"><a href="#cb8-20"></a> <span class="dt">int</span> i<span class="op">;</span></span>
<span id="cb8-21"><a href="#cb8-21"></a></span>
<span id="cb8-22"><a href="#cb8-22"></a> win <span class="op">=</span> gtk_application_window_new <span class="op">(</span>GTK_APPLICATION <span class="op">(</span>app<span class="op">));</span></span>
<span id="cb8-23"><a href="#cb8-23"></a> gtk_window_set_title <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="st">&quot;file viewer&quot;</span><span class="op">);</span></span>
<span id="cb8-24"><a href="#cb8-24"></a> gtk_window_set_default_size <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="dv">600</span><span class="op">,</span> <span class="dv">400</span><span class="op">);</span></span>
<span id="cb8-25"><a href="#cb8-25"></a></span>
<span id="cb8-26"><a href="#cb8-26"></a> nb <span class="op">=</span> gtk_notebook_new <span class="op">();</span></span>
<span id="cb8-27"><a href="#cb8-27"></a> gtk_window_set_child <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> nb<span class="op">);</span></span>
<span id="cb8-28"><a href="#cb8-28"></a></span>
<span id="cb8-29"><a href="#cb8-29"></a> <span class="cf">for</span> <span class="op">(</span>i <span class="op">=</span> <span class="dv">0</span><span class="op">;</span> i <span class="op">&lt;</span> n_files<span class="op">;</span> i<span class="op">++)</span> <span class="op">{</span></span>
<span id="cb8-30"><a href="#cb8-30"></a> <span class="cf">if</span> <span class="op">(</span>g_file_load_contents <span class="op">(</span>files<span class="op">[</span>i<span class="op">],</span> NULL<span class="op">,</span> <span class="op">&amp;</span>contents<span class="op">,</span> <span class="op">&amp;</span>length<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">))</span> <span class="op">{</span></span>
<span id="cb8-31"><a href="#cb8-31"></a> scr <span class="op">=</span> gtk_scrolled_window_new <span class="op">();</span></span>
<span id="cb8-32"><a href="#cb8-32"></a> tv <span class="op">=</span> gtk_text_view_new <span class="op">();</span></span>
<span id="cb8-33"><a href="#cb8-33"></a> tb <span class="op">=</span> gtk_text_view_get_buffer <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
<span id="cb8-34"><a href="#cb8-34"></a> gtk_text_view_set_wrap_mode <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> GTK_WRAP_WORD_CHAR<span class="op">);</span></span>
<span id="cb8-35"><a href="#cb8-35"></a> gtk_text_view_set_editable <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> FALSE<span class="op">);</span></span>
<span id="cb8-36"><a href="#cb8-36"></a> gtk_scrolled_window_set_child <span class="op">(</span>GTK_SCROLLED_WINDOW <span class="op">(</span>scr<span class="op">),</span> tv<span class="op">);</span></span>
<span id="cb8-37"><a href="#cb8-37"></a></span>
<span id="cb8-38"><a href="#cb8-38"></a> gtk_text_buffer_set_text <span class="op">(</span>tb<span class="op">,</span> contents<span class="op">,</span> length<span class="op">);</span></span>
<span id="cb8-39"><a href="#cb8-39"></a> g_free <span class="op">(</span>contents<span class="op">);</span></span>
<span id="cb8-40"><a href="#cb8-40"></a> <span class="cf">if</span> <span class="op">((</span>filename <span class="op">=</span> g_file_get_basename <span class="op">(</span>files<span class="op">[</span>i<span class="op">]))</span> <span class="op">!=</span> NULL<span class="op">)</span> <span class="op">{</span></span>
<span id="cb8-41"><a href="#cb8-41"></a> lab <span class="op">=</span> gtk_label_new <span class="op">(</span>filename<span class="op">);</span></span>
<span id="cb8-42"><a href="#cb8-42"></a> g_free <span class="op">(</span>filename<span class="op">);</span></span>
<span id="cb8-43"><a href="#cb8-43"></a> <span class="op">}</span> <span class="cf">else</span></span>
<span id="cb8-44"><a href="#cb8-44"></a> lab <span class="op">=</span> gtk_label_new <span class="op">(</span><span class="st">&quot;&quot;</span><span class="op">);</span></span>
<span id="cb8-45"><a href="#cb8-45"></a> gtk_notebook_append_page <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">),</span> scr<span class="op">,</span> lab<span class="op">);</span></span>
<span id="cb8-46"><a href="#cb8-46"></a> nbp <span class="op">=</span> gtk_notebook_get_page <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">),</span> scr<span class="op">);</span></span>
<span id="cb8-47"><a href="#cb8-47"></a> g_object_set <span class="op">(</span>nbp<span class="op">,</span> <span class="st">&quot;tab-expand&quot;</span><span class="op">,</span> TRUE<span class="op">,</span> NULL<span class="op">);</span></span>
<span id="cb8-48"><a href="#cb8-48"></a> <span class="op">}</span> <span class="cf">else</span> <span class="cf">if</span> <span class="op">((</span>filename <span class="op">=</span> g_file_get_path <span class="op">(</span>files<span class="op">[</span>i<span class="op">]))</span> <span class="op">!=</span> NULL<span class="op">)</span> <span class="op">{</span></span>
<span id="cb8-49"><a href="#cb8-49"></a> g_printerr <span class="op">(</span><span class="st">&quot;No such file: %s.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">,</span> filename<span class="op">);</span></span>
<span id="cb8-50"><a href="#cb8-50"></a> g_free <span class="op">(</span>filename<span class="op">);</span></span>
<span id="cb8-51"><a href="#cb8-51"></a> <span class="op">}</span> <span class="cf">else</span></span>
<span id="cb8-52"><a href="#cb8-52"></a> g_printerr <span class="op">(</span><span class="st">&quot;No valid file is given</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb8-53"><a href="#cb8-53"></a> <span class="op">}</span></span>
<span id="cb8-54"><a href="#cb8-54"></a> <span class="cf">if</span> <span class="op">(</span>gtk_notebook_get_n_pages <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">))</span> <span class="op">&gt;</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb8-55"><a href="#cb8-55"></a> gtk_window_present <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
<span id="cb8-56"><a href="#cb8-56"></a> <span class="cf">else</span></span>
<span id="cb8-57"><a href="#cb8-57"></a> gtk_window_destroy <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
<span id="cb8-58"><a href="#cb8-58"></a><span class="op">}</span></span>
<span id="cb8-59"><a href="#cb8-59"></a></span>
<span id="cb8-60"><a href="#cb8-60"></a><span class="dt">int</span></span>
<span id="cb8-61"><a href="#cb8-61"></a>main <span class="op">(</span><span class="dt">int</span> argc<span class="op">,</span> <span class="dt">char</span> <span class="op">**</span>argv<span class="op">)</span> <span class="op">{</span></span>
<span id="cb8-62"><a href="#cb8-62"></a> GtkApplication <span class="op">*</span>app<span class="op">;</span></span>
<span id="cb8-63"><a href="#cb8-63"></a> <span class="dt">int</span> stat<span class="op">;</span></span>
<span id="cb8-64"><a href="#cb8-64"></a></span>
<span id="cb8-65"><a href="#cb8-65"></a> app <span class="op">=</span> gtk_application_new <span class="op">(</span><span class="st">&quot;com.github.ToshioCP.tfv4&quot;</span><span class="op">,</span> G_APPLICATION_HANDLES_OPEN<span class="op">);</span></span>
<span id="cb8-66"><a href="#cb8-66"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">&quot;activate&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>app_activate<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb8-67"><a href="#cb8-67"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">&quot;open&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>app_open<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb8-68"><a href="#cb8-68"></a> stat <span class="op">=</span> g_application_run <span class="op">(</span>G_APPLICATION <span class="op">(</span>app<span class="op">),</span> argc<span class="op">,</span> argv<span class="op">);</span></span>
<span id="cb8-69"><a href="#cb8-69"></a> g_object_unref <span class="op">(</span>app<span class="op">);</span></span>
<span id="cb8-70"><a href="#cb8-70"></a> <span class="cf">return</span> stat<span class="op">;</span></span>
<span id="cb8-71"><a href="#cb8-71"></a><span class="op">}</span></span></code></pre></div>
<p>Most of the changes are in the function <code>app_open</code>. The
numbers at the left of the following items are line numbers in the
source code.</p>
<ul>
<li>11-13: Variables <code>nb</code>, <code>lab</code> and
<code>nbp</code> are defined. They point GtkNotebook, GtkLabel and
GtkNotebookPage respectively.</li>
<li>23: The windows title is set to “file viewer”.</li>
<li>24: The default size of the window is 600x400.</li>
<li>26-27 GtkNotebook is created and inserted to the
GtkApplicationWindow as a child.</li>
<li>29-58 For-loop. The variable <code>files[i]</code> points i-th
GFile, which is created by the GtkApplication from the i-th command line
argument.</li>
<li>31-36 GtkScrollledWindow, GtkTextView are created. GtkTextBuffer is
got from the GtkTextView. The GtkTextView is connected to the
GtkScrolledWindow as a child.</li>
<li>38-39 inserts the contents of the file into GtkTextBuffer and frees
the memory pointed by <code>contents</code>.</li>
<li>40-42: If the filename is taken from the GFile, GtkLabel is created
with the filename. The string <code>filename</code> is freed..</li>
<li>43-44: If it fails to take the filename, empty string GtkLabel is
created.</li>
<li>45-46: Appends a GtkScrolledWindow to the GtkNotebook as a child.
And the GtkLabel is set as the childs tab. At the same time, a
GtkNoteBookPage is created automatically. The function
<code>gtk_notebook_get_page</code> returns the GtkNotebookPage of the
child (GtkScrolledWindow).</li>
<li>47: GtkNotebookPage has “tab-expand” property. If it is set to TRUE
then the tab expands horizontally as long as possible. If it is FALSE,
then the width of the tab is determined by the size of the label.
<code>g_object_set</code> is a general function to set properties of
objects. See <a
href="https://docs.gtk.org/gobject/method.Object.set.html">GObject API
Reference g_object_set</a>.</li>
<li>48-50: If it fails to read the file and a filename is taken from the
GFile, “No such file” message is displayed. The <code>filename</code> is
freed.</li>
<li>51-52: If <code>filename</code> is NULL, the “No valid file is
given” message is displayed.</li>
<li>54-57: If at least one page exists, the window is shown. Otherwise,
the window is destroyed and the application quits.</li>
</ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
</body>
</html>