Gtk4-tutorial/docs/sec15.html

431 lines
33 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="sec14.html">Prev: section14</a>
</li>
<li class="nav-item">
<a class="nav-link" href="sec16.html">Next: section16</a>
</li>
</ul>
</div>
</div>
</nav>
<h1 id="tfe-main-program">Tfe main program</h1>
<p>The file <code>tfeapplication.c</code> is a main program of Tfe. It
includes all the code other than <code>tfetextview.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="the-function-main">The function 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 &quot;com.github.ToshioCP.tfe&quot;</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="op">(</span><span class="dt">int</span> argc<span class="op">,</span> <span class="dt">char</span> <span class="op">**</span>argv<span class="op">)</span> <span class="op">{</span></span>
<span id="cb1-5"><a href="#cb1-5"></a> GtkApplication <span class="op">*</span>app<span class="op">;</span></span>
<span id="cb1-6"><a href="#cb1-6"></a> <span class="dt">int</span> stat<span class="op">;</span></span>
<span id="cb1-7"><a href="#cb1-7"></a></span>
<span id="cb1-8"><a href="#cb1-8"></a> app <span class="op">=</span> gtk_application_new <span class="op">(</span>APPLICATION_ID<span class="op">,</span> G_APPLICATION_HANDLES_OPEN<span class="op">);</span></span>
<span id="cb1-9"><a href="#cb1-9"></a></span>
<span id="cb1-10"><a href="#cb1-10"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">&quot;startup&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>app_startup<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb1-11"><a href="#cb1-11"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">&quot;activate&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>app_activate<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb1-12"><a href="#cb1-12"></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="cb1-13"><a href="#cb1-13"></a></span>
<span id="cb1-14"><a href="#cb1-14"></a> stat <span class="op">=</span>g_application_run <span class="op">(</span>G_APPLICATION <span class="op">(</span>app<span class="op">),</span> argc<span class="op">,</span> argv<span class="op">);</span></span>
<span id="cb1-15"><a href="#cb1-15"></a> g_object_unref <span class="op">(</span>app<span class="op">);</span></span>
<span id="cb1-16"><a href="#cb1-16"></a> <span class="cf">return</span> stat<span class="op">;</span></span>
<span id="cb1-17"><a href="#cb1-17"></a><span class="op">}</span></span></code></pre></div>
<ul>
<li>1: Defines the application id. Thanks to the <code>#define</code>
directive, it is easy to find the application id.</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. The handler initializes the whole application which
includes not only GtkApplication instance but also widgets and some
other objects.</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 <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
<span id="cb2-3"><a href="#cb2-3"></a> GtkApplication <span class="op">*</span>app <span class="op">=</span> GTK_APPLICATION <span class="op">(</span>application<span class="op">);</span></span>
<span id="cb2-4"><a href="#cb2-4"></a> GtkBuilder <span class="op">*</span>build<span class="op">;</span></span>
<span id="cb2-5"><a href="#cb2-5"></a> GtkApplicationWindow <span class="op">*</span>win<span class="op">;</span></span>
<span id="cb2-6"><a href="#cb2-6"></a> GtkNotebook <span class="op">*</span>nb<span class="op">;</span></span>
<span id="cb2-7"><a href="#cb2-7"></a> GtkButton <span class="op">*</span>btno<span class="op">;</span></span>
<span id="cb2-8"><a href="#cb2-8"></a> GtkButton <span class="op">*</span>btnn<span class="op">;</span></span>
<span id="cb2-9"><a href="#cb2-9"></a> GtkButton <span class="op">*</span>btns<span class="op">;</span></span>
<span id="cb2-10"><a href="#cb2-10"></a> GtkButton <span class="op">*</span>btnc<span class="op">;</span></span>
<span id="cb2-11"><a href="#cb2-11"></a></span>
<span id="cb2-12"><a href="#cb2-12"></a> build <span class="op">=</span> gtk_builder_new_from_resource <span class="op">(</span><span class="st">&quot;/com/github/ToshioCP/tfe/tfe.ui&quot;</span><span class="op">);</span></span>
<span id="cb2-13"><a href="#cb2-13"></a> win <span class="op">=</span> GTK_APPLICATION_WINDOW <span class="op">(</span>gtk_builder_get_object <span class="op">(</span>build<span class="op">,</span> <span class="st">&quot;win&quot;</span><span class="op">));</span></span>
<span id="cb2-14"><a href="#cb2-14"></a> nb <span class="op">=</span> GTK_NOTEBOOK <span class="op">(</span>gtk_builder_get_object <span class="op">(</span>build<span class="op">,</span> <span class="st">&quot;nb&quot;</span><span class="op">));</span></span>
<span id="cb2-15"><a href="#cb2-15"></a> gtk_window_set_application <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> app<span class="op">);</span></span>
<span id="cb2-16"><a href="#cb2-16"></a> btno <span class="op">=</span> GTK_BUTTON <span class="op">(</span>gtk_builder_get_object <span class="op">(</span>build<span class="op">,</span> <span class="st">&quot;btno&quot;</span><span class="op">));</span></span>
<span id="cb2-17"><a href="#cb2-17"></a> btnn <span class="op">=</span> GTK_BUTTON <span class="op">(</span>gtk_builder_get_object <span class="op">(</span>build<span class="op">,</span> <span class="st">&quot;btnn&quot;</span><span class="op">));</span></span>
<span id="cb2-18"><a href="#cb2-18"></a> btns <span class="op">=</span> GTK_BUTTON <span class="op">(</span>gtk_builder_get_object <span class="op">(</span>build<span class="op">,</span> <span class="st">&quot;btns&quot;</span><span class="op">));</span></span>
<span id="cb2-19"><a href="#cb2-19"></a> btnc <span class="op">=</span> GTK_BUTTON <span class="op">(</span>gtk_builder_get_object <span class="op">(</span>build<span class="op">,</span> <span class="st">&quot;btnc&quot;</span><span class="op">));</span></span>
<span id="cb2-20"><a href="#cb2-20"></a> g_signal_connect_swapped <span class="op">(</span>btno<span class="op">,</span> <span class="st">&quot;clicked&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>open_cb<span class="op">),</span> nb<span class="op">);</span></span>
<span id="cb2-21"><a href="#cb2-21"></a> g_signal_connect_swapped <span class="op">(</span>btnn<span class="op">,</span> <span class="st">&quot;clicked&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>new_cb<span class="op">),</span> nb<span class="op">);</span></span>
<span id="cb2-22"><a href="#cb2-22"></a> g_signal_connect_swapped <span class="op">(</span>btns<span class="op">,</span> <span class="st">&quot;clicked&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>save_cb<span class="op">),</span> nb<span class="op">);</span></span>
<span id="cb2-23"><a href="#cb2-23"></a> g_signal_connect_swapped <span class="op">(</span>btnc<span class="op">,</span> <span class="st">&quot;clicked&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>close_cb<span class="op">),</span> nb<span class="op">);</span></span>
<span id="cb2-24"><a href="#cb2-24"></a> g_object_unref<span class="op">(</span>build<span class="op">);</span></span>
<span id="cb2-25"><a href="#cb2-25"></a></span>
<span id="cb2-26"><a href="#cb2-26"></a>GdkDisplay <span class="op">*</span>display<span class="op">;</span></span>
<span id="cb2-27"><a href="#cb2-27"></a></span>
<span id="cb2-28"><a href="#cb2-28"></a> display <span class="op">=</span> gdk_display_get_default <span class="op">();</span></span>
<span id="cb2-29"><a href="#cb2-29"></a> GtkCssProvider <span class="op">*</span>provider <span class="op">=</span> gtk_css_provider_new <span class="op">();</span></span>
<span id="cb2-30"><a href="#cb2-30"></a> gtk_css_provider_load_from_data <span class="op">(</span>provider<span class="op">,</span> <span class="st">&quot;textview {padding: 10px; font-family: monospace; font-size: 12pt;}&quot;</span><span class="op">,</span> <span class="op">-</span><span class="dv">1</span><span class="op">);</span></span>
<span id="cb2-31"><a href="#cb2-31"></a> gtk_style_context_add_provider_for_display <span class="op">(</span>display<span class="op">,</span> GTK_STYLE_PROVIDER <span class="op">(</span>provider<span class="op">),</span> GTK_STYLE_PROVIDER_PRIORITY_APPLICATION<span class="op">);</span></span>
<span id="cb2-32"><a href="#cb2-32"></a></span>
<span id="cb2-33"><a href="#cb2-33"></a> g_signal_connect <span class="op">(</span>win<span class="op">,</span> <span class="st">&quot;destroy&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>before_destroy<span class="op">),</span> provider<span class="op">);</span></span>
<span id="cb2-34"><a href="#cb2-34"></a> g_object_unref <span class="op">(</span>provider<span class="op">);</span></span>
<span id="cb2-35"><a href="#cb2-35"></a><span class="op">}</span></span></code></pre></div>
<ul>
<li>12-15: Builds widgets using ui 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. The
macro <code>g_signal_connect_swapped</code> connects a signal and
handler like <code>g_signal_connect</code>. The difference is that
<code>g_signal_connect_swapped</code> swaps the user data for the
object. For example, the macro on line 20 swaps <code>nb</code> for
<code>btno</code>. So, the handler expects that the first argument is
<code>nb</code> instead of <code>btno</code>.</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 on line 30. It sets padding, font-family and font size
of GtkTextView. CSS will be explained in the next subsection.</li>
<li>26-28: GdkDisplay is used to set CSS. The default GdkDisplay object
can be obtain with the function <code>gfk_display_get_default</code>.
This function needs to be called after the window creation.</li>
<li>33: Connects “destroy” signal on the main window and before_destroy
handler. This handler is explained in the next subsection.</li>
<li>34: The provider is useless for the startup handler, so it is
released. Note: It doesnt mean the destruction of the provider. It is
referred by the display so the reference count is not zero.</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 widgets in Gtk is similar to elements in HTML. It
tells that CSS can be applied to Gtk windowing system, too.</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 <code>selector</code> above.</p>
<pre><code>textview {color: yellow; ...}</code></pre>
<p>Class, ID and some other things can be applied to the selector like
Web CSS. Refer to <a
href="https://docs.gtk.org/gtk4/css-overview.html">GTK 4 API Reference
CSS in Gtk</a> for further information.</p>
<p>The codes of the startup handler has a CSS string on line 30.</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. The font name “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 deprecated since version 4.10. But two functions
<code>gtk_style_context_add_provider_for_display</code> and
<code>gtk_style_context_remove_provider_for_display</code> are not
deprecated. They add or remove a css provider object to the GdkDisplay
object.</p>
<p>GtkCssProvider is an object which parses CSS for style widgets.</p>
<p>To apply your CSS to widgets, you need to add GtkStyleProvider (the
interface of GtkCssProvider) to the GdkDisplay object. You can get the
default display object with the function
<code>gdk_display_get_default</code>. The returned object is owned by
the function and you dont have its ownership. So, you dont need to
care about releasing it.</p>
<p>Look at the source file of <code>startup</code> handler again.</p>
<ul>
<li>28: The display is obtained by
<code>gdk_display_get_default</code>.</li>
<li>29: Creates a GtkCssProvider instance.</li>
<li>30: Puts the CSS into the provider. The function
<code>gtk_css_provider_load_from_data</code> will be deprecated since
4.12 (Not 4.10). The new function
<code>gtk_css_provider_load_from_string</code> will be used in the
future version of Tfe.</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. Refer to <a
href="https://docs.gtk.org/gtk4/index.html#constants">GTK 4 Reference —
Constants</a> for more information. You can find other constants, which
have “STYLE_PROVIDER_PRIORITY_XXXX” pattern names.</li>
</ul>
<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> <span class="dt">void</span></span>
<span id="cb6-2"><a href="#cb6-2"></a>before_destroy <span class="op">(</span>GtkWidget <span class="op">*</span>win<span class="op">,</span> GtkCssProvider <span class="op">*</span>provider<span class="op">)</span> <span class="op">{</span></span>
<span id="cb6-3"><a href="#cb6-3"></a> GdkDisplay <span class="op">*</span>display <span class="op">=</span> gdk_display_get_default <span class="op">();</span></span>
<span id="cb6-4"><a href="#cb6-4"></a> gtk_style_context_remove_provider_for_display <span class="op">(</span>display<span class="op">,</span> GTK_STYLE_PROVIDER <span class="op">(</span>provider<span class="op">));</span></span>
<span id="cb6-5"><a href="#cb6-5"></a><span class="op">}</span></span></code></pre></div>
<p>When a widget is destroyed, or more precisely during its disposing
process, a “destroy” signal is emitted. The “before_destroy” handler
connects to the signal on the main window. (See the program list of
app_startup.) So, it is called when the window is destroyed.</p>
<p>The handler removes the CSS provider from the GdkDisplay.</p>
<p>Note: CSS providers are removed automatically when the application
quits. So, even if the handler <code>before_destroy</code> is removed,
the application works.</p>
<h2 id="activate-and-open-signal-handler">Activate and open signal
handler</h2>
<p>The handlers of “activate” and “open” signals 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 <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
<span id="cb7-3"><a href="#cb7-3"></a> GtkApplication <span class="op">*</span>app <span class="op">=</span> GTK_APPLICATION <span class="op">(</span>application<span class="op">);</span></span>
<span id="cb7-4"><a href="#cb7-4"></a> GtkWidget <span class="op">*</span>win <span class="op">=</span> GTK_WIDGET <span class="op">(</span>gtk_application_get_active_window <span class="op">(</span>app<span class="op">));</span></span>
<span id="cb7-5"><a href="#cb7-5"></a> GtkWidget <span class="op">*</span>boxv <span class="op">=</span> gtk_window_get_child <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
<span id="cb7-6"><a href="#cb7-6"></a> GtkNotebook <span class="op">*</span>nb <span class="op">=</span> GTK_NOTEBOOK <span class="op">(</span>gtk_widget_get_last_child <span class="op">(</span>boxv<span class="op">));</span></span>
<span id="cb7-7"><a href="#cb7-7"></a></span>
<span id="cb7-8"><a href="#cb7-8"></a> notebook_page_new <span class="op">(</span>nb<span class="op">);</span></span>
<span id="cb7-9"><a href="#cb7-9"></a> gtk_window_present <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
<span id="cb7-10"><a href="#cb7-10"></a><span class="op">}</span></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 <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">,</span> GFile <span class="op">**</span> files<span class="op">,</span> gint n_files<span class="op">,</span> <span class="dt">const</span> gchar <span class="op">*</span>hint<span class="op">)</span> <span class="op">{</span></span>
<span id="cb7-14"><a href="#cb7-14"></a> GtkApplication <span class="op">*</span>app <span class="op">=</span> GTK_APPLICATION <span class="op">(</span>application<span class="op">);</span></span>
<span id="cb7-15"><a href="#cb7-15"></a> GtkWidget <span class="op">*</span>win <span class="op">=</span> GTK_WIDGET <span class="op">(</span>gtk_application_get_active_window <span class="op">(</span>app<span class="op">));</span></span>
<span id="cb7-16"><a href="#cb7-16"></a> GtkWidget <span class="op">*</span>boxv <span class="op">=</span> gtk_window_get_child <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
<span id="cb7-17"><a href="#cb7-17"></a> GtkNotebook <span class="op">*</span>nb <span class="op">=</span> GTK_NOTEBOOK <span class="op">(</span>gtk_widget_get_last_child <span class="op">(</span>boxv<span class="op">));</span></span>
<span id="cb7-18"><a href="#cb7-18"></a> <span class="dt">int</span> i<span class="op">;</span></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> <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>
<span id="cb7-21"><a href="#cb7-21"></a> notebook_page_new_with_file <span class="op">(</span>nb<span class="op">,</span> files<span class="op">[</span>i<span class="op">]);</span></span>
<span id="cb7-22"><a href="#cb7-22"></a> <span class="cf">if</span> <span class="op">(</span>gtk_notebook_get_n_pages <span class="op">(</span>nb<span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb7-23"><a href="#cb7-23"></a> notebook_page_new <span class="op">(</span>nb<span class="op">);</span></span>
<span id="cb7-24"><a href="#cb7-24"></a> gtk_window_present <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
<span id="cb7-25"><a href="#cb7-25"></a><span class="op">}</span></span></code></pre></div>
<ul>
<li>1-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="a-primary-instance">A primary instance</h2>
<p>Only one GApplication instance can be run at a time in a 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. Isnt 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 instances 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 after the
registration.</p>
<p>If another instance which has the same application ID is launched, 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 isnt emitted. After that, activate or open
signal is emitted in the primary instance, not on the second instance.
The primary instance receives the signal and its handler is invoked. On
the other hand, the second instance doesnt receive the signal and it
immediately quits.</p>
<p>Try running two instances in a row.</p>
<pre><code>$ ./_build/tfe &amp;
[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 instances 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 <span class="op">(</span>GtkNotebook <span class="op">*</span>nb<span class="op">)</span> <span class="op">{</span></span>
<span id="cb9-3"><a href="#cb9-3"></a> notebook_page_open <span class="op">(</span>nb<span class="op">);</span></span>
<span id="cb9-4"><a href="#cb9-4"></a><span class="op">}</span></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 <span class="op">(</span>GtkNotebook <span class="op">*</span>nb<span class="op">)</span> <span class="op">{</span></span>
<span id="cb9-8"><a href="#cb9-8"></a> notebook_page_new <span class="op">(</span>nb<span class="op">);</span></span>
<span id="cb9-9"><a href="#cb9-9"></a><span class="op">}</span></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 <span class="op">(</span>GtkNotebook <span class="op">*</span>nb<span class="op">)</span> <span class="op">{</span></span>
<span id="cb9-13"><a href="#cb9-13"></a> notebook_page_save <span class="op">(</span>nb<span class="op">);</span></span>
<span id="cb9-14"><a href="#cb9-14"></a><span class="op">}</span></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 <span class="op">(</span>GtkNotebook <span class="op">*</span>nb<span class="op">)</span> <span class="op">{</span></span>
<span id="cb9-18"><a href="#cb9-18"></a> notebook_page_close <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">));</span></span>
<span id="cb9-19"><a href="#cb9-19"></a><span class="op">}</span></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(&#39;tfe&#39;, &#39;c&#39;)</span>
<span id="cb10-2"><a href="#cb10-2"></a></span>
<span id="cb10-3"><a href="#cb10-3"></a>gtkdep = dependency(&#39;gtk4&#39;)</span>
<span id="cb10-4"><a href="#cb10-4"></a></span>
<span id="cb10-5"><a href="#cb10-5"></a>gnome=import(&#39;gnome&#39;)</span>
<span id="cb10-6"><a href="#cb10-6"></a>resources = gnome.compile_resources(&#39;resources&#39;,&#39;tfe.gresource.xml&#39;)</span>
<span id="cb10-7"><a href="#cb10-7"></a></span>
<span id="cb10-8"><a href="#cb10-8"></a>sourcefiles=files(&#39;tfeapplication.c&#39;, &#39;tfenotebook.c&#39;, &#39;../tfetextview/tfetextview.c&#39;)</span>
<span id="cb10-9"><a href="#cb10-9"></a></span>
<span id="cb10-10"><a href="#cb10-10"></a>executable(&#39;tfe&#39;, 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>You can 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 /src/tfe5 directory.</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
</body>
</html>