mirror of
https://github.com/ToshioCP/Gtk4-tutorial.git
synced 2024-11-16 19:50:35 +01:00
468 lines
41 KiB
HTML
468 lines
41 KiB
HTML
<!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>We’ve already used <code>G_APPLICATION_DEFAULT_FLAGS</code>, as it is
|
||
the simplest option, and no command line arguments are allowed. If you
|
||
give arguments, an error will occur.</p>
|
||
<p>The flag <code>G_APPLICATION_HANDLES_OPEN</code> is the second
|
||
simplest option. It allows arguments but only files. The application
|
||
assumes all the arguments are filenames.</p>
|
||
<div class="sourceCode" id="cb3"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>app <span class="op">=</span> gtk_application_new <span class="op">(</span><span class="st">"com.github.ToshioCP.tfv3"</span><span class="op">,</span> G_APPLICATION_HANDLES_OPEN<span class="op">);</span></span></code></pre></div>
|
||
<h3 id="open-signal-1">open signal</h3>
|
||
<p>When <code>G_APPLICATION_HANDLES_OPEN</code> flag is given to the
|
||
application, two signals are available.</p>
|
||
<ul>
|
||
<li>activate signal — This signal is emitted when there’s no
|
||
argument.</li>
|
||
<li>open signal — This signal is emitted when there is at least one
|
||
argument.</li>
|
||
</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 there’s no argument, it shows an error message and quit.</li>
|
||
<li>If it successfully opens the file, it reads the contents of the
|
||
file, inserts them to GtkTextBuffer and shows the window.</li>
|
||
<li>If it fails to open the file, it shows an error message and
|
||
quit.</li>
|
||
</ul>
|
||
<p>The program is shown below.</p>
|
||
<div class="sourceCode" id="cb5"><pre
|
||
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb5-1"><a href="#cb5-1"></a><span class="pp">#include </span><span class="im"><gtk/gtk.h></span></span>
|
||
<span id="cb5-2"><a href="#cb5-2"></a></span>
|
||
<span id="cb5-3"><a href="#cb5-3"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||
<span id="cb5-4"><a href="#cb5-4"></a>app_activate <span class="op">(</span>GApplication <span class="op">*</span>app<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb5-5"><a href="#cb5-5"></a> g_printerr <span class="op">(</span><span class="st">"You need a filename argument.</span><span class="sc">\n</span><span class="st">"</span><span class="op">);</span></span>
|
||
<span id="cb5-6"><a href="#cb5-6"></a><span class="op">}</span></span>
|
||
<span id="cb5-7"><a href="#cb5-7"></a></span>
|
||
<span id="cb5-8"><a href="#cb5-8"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||
<span id="cb5-9"><a href="#cb5-9"></a>app_open <span class="op">(</span>GApplication <span class="op">*</span>app<span class="op">,</span> GFile <span class="op">**</span> files<span class="op">,</span> <span class="dt">int</span> n_files<span class="op">,</span> <span class="dt">char</span> <span class="op">*</span>hint<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb5-10"><a href="#cb5-10"></a> GtkWidget <span class="op">*</span>win<span class="op">;</span></span>
|
||
<span id="cb5-11"><a href="#cb5-11"></a> GtkWidget <span class="op">*</span>scr<span class="op">;</span></span>
|
||
<span id="cb5-12"><a href="#cb5-12"></a> GtkWidget <span class="op">*</span>tv<span class="op">;</span></span>
|
||
<span id="cb5-13"><a href="#cb5-13"></a> GtkTextBuffer <span class="op">*</span>tb<span class="op">;</span></span>
|
||
<span id="cb5-14"><a href="#cb5-14"></a> <span class="dt">char</span> <span class="op">*</span>contents<span class="op">;</span></span>
|
||
<span id="cb5-15"><a href="#cb5-15"></a> gsize length<span class="op">;</span></span>
|
||
<span id="cb5-16"><a href="#cb5-16"></a> <span class="dt">char</span> <span class="op">*</span>filename<span class="op">;</span></span>
|
||
<span id="cb5-17"><a href="#cb5-17"></a></span>
|
||
<span id="cb5-18"><a href="#cb5-18"></a> win <span class="op">=</span> gtk_application_window_new <span class="op">(</span>GTK_APPLICATION <span class="op">(</span>app<span class="op">));</span></span>
|
||
<span id="cb5-19"><a href="#cb5-19"></a> gtk_window_set_default_size <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="dv">400</span><span class="op">,</span> <span class="dv">300</span><span class="op">);</span></span>
|
||
<span id="cb5-20"><a href="#cb5-20"></a></span>
|
||
<span id="cb5-21"><a href="#cb5-21"></a> scr <span class="op">=</span> gtk_scrolled_window_new <span class="op">();</span></span>
|
||
<span id="cb5-22"><a href="#cb5-22"></a> gtk_window_set_child <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> scr<span class="op">);</span></span>
|
||
<span id="cb5-23"><a href="#cb5-23"></a></span>
|
||
<span id="cb5-24"><a href="#cb5-24"></a> tv <span class="op">=</span> gtk_text_view_new <span class="op">();</span></span>
|
||
<span id="cb5-25"><a href="#cb5-25"></a> tb <span class="op">=</span> gtk_text_view_get_buffer <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
|
||
<span id="cb5-26"><a href="#cb5-26"></a> gtk_text_view_set_wrap_mode <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> GTK_WRAP_WORD_CHAR<span class="op">);</span></span>
|
||
<span id="cb5-27"><a href="#cb5-27"></a> gtk_text_view_set_editable <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> FALSE<span class="op">);</span></span>
|
||
<span id="cb5-28"><a href="#cb5-28"></a> gtk_scrolled_window_set_child <span class="op">(</span>GTK_SCROLLED_WINDOW <span class="op">(</span>scr<span class="op">),</span> tv<span class="op">);</span></span>
|
||
<span id="cb5-29"><a href="#cb5-29"></a></span>
|
||
<span id="cb5-30"><a href="#cb5-30"></a> <span class="cf">if</span> <span class="op">(</span>g_file_load_contents <span class="op">(</span>files<span class="op">[</span><span class="dv">0</span><span class="op">],</span> NULL<span class="op">,</span> <span class="op">&</span>contents<span class="op">,</span> <span class="op">&</span>length<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">))</span> <span class="op">{</span></span>
|
||
<span id="cb5-31"><a href="#cb5-31"></a> gtk_text_buffer_set_text <span class="op">(</span>tb<span class="op">,</span> contents<span class="op">,</span> length<span class="op">);</span></span>
|
||
<span id="cb5-32"><a href="#cb5-32"></a> g_free <span class="op">(</span>contents<span class="op">);</span></span>
|
||
<span id="cb5-33"><a href="#cb5-33"></a> <span class="cf">if</span> <span class="op">((</span>filename <span class="op">=</span> g_file_get_basename <span class="op">(</span>files<span class="op">[</span><span class="dv">0</span><span class="op">]))</span> <span class="op">!=</span> NULL<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb5-34"><a href="#cb5-34"></a> gtk_window_set_title <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> filename<span class="op">);</span></span>
|
||
<span id="cb5-35"><a href="#cb5-35"></a> g_free <span class="op">(</span>filename<span class="op">);</span></span>
|
||
<span id="cb5-36"><a href="#cb5-36"></a> <span class="op">}</span></span>
|
||
<span id="cb5-37"><a href="#cb5-37"></a> gtk_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">"No such file: %s.</span><span class="sc">\n</span><span class="st">"</span><span class="op">,</span> filename<span class="op">);</span></span>
|
||
<span id="cb5-41"><a href="#cb5-41"></a> g_free <span class="op">(</span>filename<span class="op">);</span></span>
|
||
<span id="cb5-42"><a href="#cb5-42"></a> <span class="op">}</span> <span class="cf">else</span></span>
|
||
<span id="cb5-43"><a href="#cb5-43"></a> g_printerr <span class="op">(</span><span class="st">"File can't be opened.</span><span class="sc">\n</span><span class="st">"</span><span class="op">);</span></span>
|
||
<span id="cb5-44"><a href="#cb5-44"></a> gtk_window_destroy <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
|
||
<span id="cb5-45"><a href="#cb5-45"></a> <span class="op">}</span></span>
|
||
<span id="cb5-46"><a href="#cb5-46"></a><span class="op">}</span></span>
|
||
<span id="cb5-47"><a href="#cb5-47"></a></span>
|
||
<span id="cb5-48"><a href="#cb5-48"></a><span class="dt">int</span></span>
|
||
<span id="cb5-49"><a href="#cb5-49"></a>main <span class="op">(</span><span class="dt">int</span> argc<span class="op">,</span> <span class="dt">char</span> <span class="op">**</span>argv<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb5-50"><a href="#cb5-50"></a> GtkApplication <span class="op">*</span>app<span class="op">;</span></span>
|
||
<span id="cb5-51"><a href="#cb5-51"></a> <span class="dt">int</span> stat<span class="op">;</span></span>
|
||
<span id="cb5-52"><a href="#cb5-52"></a></span>
|
||
<span id="cb5-53"><a href="#cb5-53"></a> app <span class="op">=</span> gtk_application_new <span class="op">(</span><span class="st">"com.github.ToshioCP.tfv3"</span><span class="op">,</span> G_APPLICATION_HANDLES_OPEN<span class="op">);</span></span>
|
||
<span id="cb5-54"><a href="#cb5-54"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">"activate"</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>app_activate<span class="op">),</span> NULL<span class="op">);</span></span>
|
||
<span id="cb5-55"><a href="#cb5-55"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">"open"</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>app_open<span class="op">),</span> NULL<span class="op">);</span></span>
|
||
<span id="cb5-56"><a href="#cb5-56"></a> stat <span class="op">=</span> g_application_run <span class="op">(</span>G_APPLICATION <span class="op">(</span>app<span class="op">),</span> argc<span class="op">,</span> argv<span class="op">);</span></span>
|
||
<span id="cb5-57"><a href="#cb5-57"></a> g_object_unref <span class="op">(</span>app<span class="op">);</span></span>
|
||
<span id="cb5-58"><a href="#cb5-58"></a> <span class="cf">return</span> stat<span class="op">;</span></span>
|
||
<span id="cb5-59"><a href="#cb5-59"></a><span class="op">}</span></span></code></pre></div>
|
||
<p>Save it as <code>tfv3.c</code>. Then compile and run it.</p>
|
||
<pre><code>$ comp tfv3
|
||
$ ./a.out tfv3.c</code></pre>
|
||
<figure>
|
||
<img src="image/screenshot_tfv3.png" alt="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 isn’t an editor
|
||
but only a viewer</li>
|
||
<li>Reads the file and inserts the text into GtkTextBuffer (this will be
|
||
explained later)</li>
|
||
<li>If the file is not opened, outputs an error message and destroys the
|
||
window. This makes the application quit.</li>
|
||
</ul>
|
||
<p>The following is the file reading part of the program again.</p>
|
||
<div class="sourceCode" id="cb7"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="op">(</span>g_file_load_contents <span class="op">(</span>files<span class="op">[</span><span class="dv">0</span><span class="op">],</span> NULL<span class="op">,</span> <span class="op">&</span>contents<span class="op">,</span> <span class="op">&</span>length<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">))</span> <span class="op">{</span></span>
|
||
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a> gtk_text_buffer_set_text <span class="op">(</span>tb<span class="op">,</span> contents<span class="op">,</span> length<span class="op">);</span></span>
|
||
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a> g_free <span class="op">(</span>contents<span class="op">);</span></span>
|
||
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">((</span>filename <span class="op">=</span> g_file_get_basename <span class="op">(</span>files<span class="op">[</span><span class="dv">0</span><span class="op">]))</span> <span class="op">!=</span> NULL<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a> gtk_window_set_title <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> filename<span class="op">);</span></span>
|
||
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a> g_free <span class="op">(</span>filename<span class="op">);</span></span>
|
||
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
|
||
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a> gtk_widget_show <span class="op">(</span>win<span class="op">);</span></span>
|
||
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a><span class="op">}</span> <span class="cf">else</span> <span class="op">{</span></span>
|
||
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">((</span>filename <span class="op">=</span> g_file_get_path <span class="op">(</span>files<span class="op">[</span><span class="dv">0</span><span class="op">]))</span> <span class="op">!=</span> NULL<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a> g_printerr <span class="op">(</span><span class="st">"No such file: %s.</span><span class="sc">\n</span><span class="st">"</span><span class="op">,</span> filename<span class="op">);</span></span>
|
||
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true" tabindex="-1"></a> g_free <span class="op">(</span>filename<span class="op">);</span></span>
|
||
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span> <span class="cf">else</span></span>
|
||
<span id="cb7-14"><a href="#cb7-14" aria-hidden="true" tabindex="-1"></a> g_printerr <span class="op">(</span><span class="st">"File can't be opened.</span><span class="sc">\n</span><span class="st">"</span><span class="op">);</span></span>
|
||
<span id="cb7-15"><a href="#cb7-15" aria-hidden="true" tabindex="-1"></a> gtk_window_destroy <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
|
||
<span id="cb7-16"><a href="#cb7-16" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
|
||
<p>The function <code>g_file_load_contents</code> loads the file
|
||
contents into a temporary buffer, which is automatically allocated and
|
||
sets <code>contents</code> to point the buffer. The length of the buffer
|
||
is assigned to <code>length</code>. It returns <code>TRUE</code> if the
|
||
file’s contents are successfully loaded or <code>FALSE</code> if an
|
||
error occurs. If you want to know the details about
|
||
g_file_load_contents, see <a
|
||
href="https://docs.gtk.org/gio/method.File.load_contents.html">g file
|
||
load contents</a>.</p>
|
||
<p>If it has successfully read the file, it inserts the contents into
|
||
GtkTextBuffer, frees the temporary buffer pointed by
|
||
<code>contents</code>, sets the title of the window, frees the memories
|
||
pointed by <code>filename</code> and then shows the window. If it fails,
|
||
it outputs an error message and destroys the window and finally make the
|
||
program quit.</p>
|
||
<h2 id="gtknotebook">GtkNotebook</h2>
|
||
<p>GtkNotebook is a container widget that 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"><gtk/gtk.h></span></span>
|
||
<span id="cb8-2"><a href="#cb8-2"></a></span>
|
||
<span id="cb8-3"><a href="#cb8-3"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||
<span id="cb8-4"><a href="#cb8-4"></a>app_activate <span class="op">(</span>GApplication <span class="op">*</span>app<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb8-5"><a href="#cb8-5"></a> g_printerr <span class="op">(</span><span class="st">"You need a filename argument.</span><span class="sc">\n</span><span class="st">"</span><span class="op">);</span></span>
|
||
<span id="cb8-6"><a href="#cb8-6"></a><span class="op">}</span></span>
|
||
<span id="cb8-7"><a href="#cb8-7"></a></span>
|
||
<span id="cb8-8"><a href="#cb8-8"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||
<span id="cb8-9"><a href="#cb8-9"></a>app_open <span class="op">(</span>GApplication <span class="op">*</span>app<span class="op">,</span> GFile <span class="op">**</span> files<span class="op">,</span> gint n_files<span class="op">,</span> gchar <span class="op">*</span>hint<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb8-10"><a href="#cb8-10"></a> GtkWidget <span class="op">*</span>win<span class="op">;</span></span>
|
||
<span id="cb8-11"><a href="#cb8-11"></a> GtkWidget <span class="op">*</span>nb<span class="op">;</span></span>
|
||
<span id="cb8-12"><a href="#cb8-12"></a> GtkWidget <span class="op">*</span>lab<span class="op">;</span></span>
|
||
<span id="cb8-13"><a href="#cb8-13"></a> GtkNotebookPage <span class="op">*</span>nbp<span class="op">;</span></span>
|
||
<span id="cb8-14"><a href="#cb8-14"></a> GtkWidget <span class="op">*</span>scr<span class="op">;</span></span>
|
||
<span id="cb8-15"><a href="#cb8-15"></a> GtkWidget <span class="op">*</span>tv<span class="op">;</span></span>
|
||
<span id="cb8-16"><a href="#cb8-16"></a> GtkTextBuffer <span class="op">*</span>tb<span class="op">;</span></span>
|
||
<span id="cb8-17"><a href="#cb8-17"></a> <span class="dt">char</span> <span class="op">*</span>contents<span class="op">;</span></span>
|
||
<span id="cb8-18"><a href="#cb8-18"></a> gsize length<span class="op">;</span></span>
|
||
<span id="cb8-19"><a href="#cb8-19"></a> <span class="dt">char</span> <span class="op">*</span>filename<span class="op">;</span></span>
|
||
<span id="cb8-20"><a href="#cb8-20"></a> <span class="dt">int</span> i<span class="op">;</span></span>
|
||
<span id="cb8-21"><a href="#cb8-21"></a></span>
|
||
<span id="cb8-22"><a href="#cb8-22"></a> win <span class="op">=</span> gtk_application_window_new <span class="op">(</span>GTK_APPLICATION <span class="op">(</span>app<span class="op">));</span></span>
|
||
<span id="cb8-23"><a href="#cb8-23"></a> gtk_window_set_title <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="st">"file viewer"</span><span class="op">);</span></span>
|
||
<span id="cb8-24"><a href="#cb8-24"></a> gtk_window_set_default_size <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="dv">600</span><span class="op">,</span> <span class="dv">400</span><span class="op">);</span></span>
|
||
<span id="cb8-25"><a href="#cb8-25"></a></span>
|
||
<span id="cb8-26"><a href="#cb8-26"></a> nb <span class="op">=</span> gtk_notebook_new <span class="op">();</span></span>
|
||
<span id="cb8-27"><a href="#cb8-27"></a> gtk_window_set_child <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> nb<span class="op">);</span></span>
|
||
<span id="cb8-28"><a href="#cb8-28"></a></span>
|
||
<span id="cb8-29"><a href="#cb8-29"></a> <span class="cf">for</span> <span class="op">(</span>i <span class="op">=</span> <span class="dv">0</span><span class="op">;</span> i <span class="op"><</span> n_files<span class="op">;</span> i<span class="op">++)</span> <span class="op">{</span></span>
|
||
<span id="cb8-30"><a href="#cb8-30"></a> <span class="cf">if</span> <span class="op">(</span>g_file_load_contents <span class="op">(</span>files<span class="op">[</span>i<span class="op">],</span> NULL<span class="op">,</span> <span class="op">&</span>contents<span class="op">,</span> <span class="op">&</span>length<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">))</span> <span class="op">{</span></span>
|
||
<span id="cb8-31"><a href="#cb8-31"></a> scr <span class="op">=</span> gtk_scrolled_window_new <span class="op">();</span></span>
|
||
<span id="cb8-32"><a href="#cb8-32"></a> tv <span class="op">=</span> gtk_text_view_new <span class="op">();</span></span>
|
||
<span id="cb8-33"><a href="#cb8-33"></a> tb <span class="op">=</span> gtk_text_view_get_buffer <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
|
||
<span id="cb8-34"><a href="#cb8-34"></a> gtk_text_view_set_wrap_mode <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> GTK_WRAP_WORD_CHAR<span class="op">);</span></span>
|
||
<span id="cb8-35"><a href="#cb8-35"></a> gtk_text_view_set_editable <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> FALSE<span class="op">);</span></span>
|
||
<span id="cb8-36"><a href="#cb8-36"></a> gtk_scrolled_window_set_child <span class="op">(</span>GTK_SCROLLED_WINDOW <span class="op">(</span>scr<span class="op">),</span> tv<span class="op">);</span></span>
|
||
<span id="cb8-37"><a href="#cb8-37"></a></span>
|
||
<span id="cb8-38"><a href="#cb8-38"></a> gtk_text_buffer_set_text <span class="op">(</span>tb<span class="op">,</span> contents<span class="op">,</span> length<span class="op">);</span></span>
|
||
<span id="cb8-39"><a href="#cb8-39"></a> g_free <span class="op">(</span>contents<span class="op">);</span></span>
|
||
<span id="cb8-40"><a href="#cb8-40"></a> <span class="cf">if</span> <span class="op">((</span>filename <span class="op">=</span> g_file_get_basename <span class="op">(</span>files<span class="op">[</span>i<span class="op">]))</span> <span class="op">!=</span> NULL<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb8-41"><a href="#cb8-41"></a> lab <span class="op">=</span> gtk_label_new <span class="op">(</span>filename<span class="op">);</span></span>
|
||
<span id="cb8-42"><a href="#cb8-42"></a> g_free <span class="op">(</span>filename<span class="op">);</span></span>
|
||
<span id="cb8-43"><a href="#cb8-43"></a> <span class="op">}</span> <span class="cf">else</span></span>
|
||
<span id="cb8-44"><a href="#cb8-44"></a> lab <span class="op">=</span> gtk_label_new <span class="op">(</span><span class="st">""</span><span class="op">);</span></span>
|
||
<span id="cb8-45"><a href="#cb8-45"></a> gtk_notebook_append_page <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">),</span> scr<span class="op">,</span> lab<span class="op">);</span></span>
|
||
<span id="cb8-46"><a href="#cb8-46"></a> nbp <span class="op">=</span> gtk_notebook_get_page <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">),</span> scr<span class="op">);</span></span>
|
||
<span id="cb8-47"><a href="#cb8-47"></a> g_object_set <span class="op">(</span>nbp<span class="op">,</span> <span class="st">"tab-expand"</span><span class="op">,</span> TRUE<span class="op">,</span> NULL<span class="op">);</span></span>
|
||
<span id="cb8-48"><a href="#cb8-48"></a> <span class="op">}</span> <span class="cf">else</span> <span class="cf">if</span> <span class="op">((</span>filename <span class="op">=</span> g_file_get_path <span class="op">(</span>files<span class="op">[</span>i<span class="op">]))</span> <span class="op">!=</span> NULL<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb8-49"><a href="#cb8-49"></a> g_printerr <span class="op">(</span><span class="st">"No such file: %s.</span><span class="sc">\n</span><span class="st">"</span><span class="op">,</span> filename<span class="op">);</span></span>
|
||
<span id="cb8-50"><a href="#cb8-50"></a> g_free <span class="op">(</span>filename<span class="op">);</span></span>
|
||
<span id="cb8-51"><a href="#cb8-51"></a> <span class="op">}</span> <span class="cf">else</span></span>
|
||
<span id="cb8-52"><a href="#cb8-52"></a> g_printerr <span class="op">(</span><span class="st">"No valid file is given</span><span class="sc">\n</span><span class="st">"</span><span class="op">);</span></span>
|
||
<span id="cb8-53"><a href="#cb8-53"></a> <span class="op">}</span></span>
|
||
<span id="cb8-54"><a href="#cb8-54"></a> <span class="cf">if</span> <span class="op">(</span>gtk_notebook_get_n_pages <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">))</span> <span class="op">></span> <span class="dv">0</span><span class="op">)</span></span>
|
||
<span id="cb8-55"><a href="#cb8-55"></a> gtk_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">"com.github.ToshioCP.tfv4"</span><span class="op">,</span> G_APPLICATION_HANDLES_OPEN<span class="op">);</span></span>
|
||
<span id="cb8-66"><a href="#cb8-66"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">"activate"</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>app_activate<span class="op">),</span> NULL<span class="op">);</span></span>
|
||
<span id="cb8-67"><a href="#cb8-67"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">"open"</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>app_open<span class="op">),</span> NULL<span class="op">);</span></span>
|
||
<span id="cb8-68"><a href="#cb8-68"></a> stat <span class="op">=</span> g_application_run <span class="op">(</span>G_APPLICATION <span class="op">(</span>app<span class="op">),</span> argc<span class="op">,</span> argv<span class="op">);</span></span>
|
||
<span id="cb8-69"><a href="#cb8-69"></a> g_object_unref <span class="op">(</span>app<span class="op">);</span></span>
|
||
<span id="cb8-70"><a href="#cb8-70"></a> <span class="cf">return</span> stat<span class="op">;</span></span>
|
||
<span id="cb8-71"><a href="#cb8-71"></a><span class="op">}</span></span></code></pre></div>
|
||
<p>Most of the changes are in the function <code>app_open</code>. The
|
||
numbers at the left of the following items are line numbers in the
|
||
source code.</p>
|
||
<ul>
|
||
<li>11-13: Variables <code>nb</code>, <code>lab</code> and
|
||
<code>nbp</code> are defined. They point GtkNotebook, GtkLabel and
|
||
GtkNotebookPage respectively.</li>
|
||
<li>23: The window’s 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 child’s tab. At the same time, a
|
||
GtkNoteBookPage is created automatically. The function
|
||
<code>gtk_notebook_get_page</code> returns the GtkNotebookPage of the
|
||
child (GtkScrolledWindow).</li>
|
||
<li>47: GtkNotebookPage has “tab-expand” property. If it is set to TRUE
|
||
then the tab expands horizontally as long as possible. If it is FALSE,
|
||
then the width of the tab is determined by the size of the label.
|
||
<code>g_object_set</code> is a general function to set properties of
|
||
objects. See <a
|
||
href="https://docs.gtk.org/gobject/method.Object.set.html">GObject API
|
||
Reference – g_object_set</a>.</li>
|
||
<li>48-50: If it fails to read the file and a filename is taken from the
|
||
GFile, “No such file” message is displayed. The <code>filename</code> is
|
||
freed.</li>
|
||
<li>51-52: If <code>filename</code> is NULL, the “No valid file is
|
||
given” message is displayed.</li>
|
||
<li>54-57: If at least one page exists, the window is shown. Otherwise,
|
||
the window is destroyed and the application quits.</li>
|
||
</ul>
|
||
</div>
|
||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
|
||
</body>
|
||
</html>
|