mirror of
https://github.com/ToshioCP/Gtk4-tutorial.git
synced 2025-01-27 19:58:29 +01:00
594 lines
52 KiB
HTML
594 lines
52 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="sec12.html">Prev: section12</a>
|
||
</li>
|
||
|
||
<li class="nav-item">
|
||
<a class="nav-link" href="sec14.html">Next: section14</a>
|
||
</li>
|
||
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</nav>
|
||
<h1 id="functions-in-tfetextview">Functions in TfeTextView</h1>
|
||
<p>In this section I will explain functions in TfeTextView object.</p>
|
||
<h2 id="tfe.h-and-tfetextview.h">tfe.h and tfetextview.h</h2>
|
||
<p><code>tfe.h</code> is a top header file and it includes
|
||
<code>gtk.h</code> and all the header files. C source files
|
||
<code>tfeapplication.c</code> and <code>tfenotebook.c</code> include
|
||
<code>tfe.h</code> at the beginning.</p>
|
||
<div class="sourceCode" id="cb1"><pre
|
||
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1"></a><span class="pp">#include </span><span class="im"><gtk/gtk.h></span></span>
|
||
<span id="cb1-2"><a href="#cb1-2"></a></span>
|
||
<span id="cb1-3"><a href="#cb1-3"></a><span class="pp">#include </span><span class="im">"../tfetextview/tfetextview.h"</span></span>
|
||
<span id="cb1-4"><a href="#cb1-4"></a><span class="pp">#include </span><span class="im">"tfenotebook.h"</span></span></code></pre></div>
|
||
<p><code>../tfetextview/tfetextview.h</code> is a header file which
|
||
describes the public functions in <code>tfetextview.c</code>.</p>
|
||
<div class="sourceCode" id="cb2"><pre
|
||
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb2-1"><a href="#cb2-1"></a><span class="pp">#ifndef __TFE_TEXT_VIEW_H__</span></span>
|
||
<span id="cb2-2"><a href="#cb2-2"></a><span class="pp">#define __TFE_TEXT_VIEW_H__</span></span>
|
||
<span id="cb2-3"><a href="#cb2-3"></a></span>
|
||
<span id="cb2-4"><a href="#cb2-4"></a><span class="pp">#include </span><span class="im"><gtk/gtk.h></span></span>
|
||
<span id="cb2-5"><a href="#cb2-5"></a></span>
|
||
<span id="cb2-6"><a href="#cb2-6"></a><span class="pp">#define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type ()</span></span>
|
||
<span id="cb2-7"><a href="#cb2-7"></a>G_DECLARE_FINAL_TYPE <span class="op">(</span>TfeTextView<span class="op">,</span> tfe_text_view<span class="op">,</span> TFE<span class="op">,</span> TEXT_VIEW<span class="op">,</span> GtkTextView<span class="op">)</span></span>
|
||
<span id="cb2-8"><a href="#cb2-8"></a></span>
|
||
<span id="cb2-9"><a href="#cb2-9"></a><span class="co">/* "open-response" signal response */</span></span>
|
||
<span id="cb2-10"><a href="#cb2-10"></a><span class="kw">enum</span> TfeTextViewOpenResponseType</span>
|
||
<span id="cb2-11"><a href="#cb2-11"></a><span class="op">{</span></span>
|
||
<span id="cb2-12"><a href="#cb2-12"></a> TFE_OPEN_RESPONSE_SUCCESS<span class="op">,</span></span>
|
||
<span id="cb2-13"><a href="#cb2-13"></a> TFE_OPEN_RESPONSE_CANCEL<span class="op">,</span></span>
|
||
<span id="cb2-14"><a href="#cb2-14"></a> TFE_OPEN_RESPONSE_ERROR</span>
|
||
<span id="cb2-15"><a href="#cb2-15"></a><span class="op">};</span></span>
|
||
<span id="cb2-16"><a href="#cb2-16"></a></span>
|
||
<span id="cb2-17"><a href="#cb2-17"></a>GFile <span class="op">*</span></span>
|
||
<span id="cb2-18"><a href="#cb2-18"></a>tfe_text_view_get_file <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">);</span></span>
|
||
<span id="cb2-19"><a href="#cb2-19"></a></span>
|
||
<span id="cb2-20"><a href="#cb2-20"></a><span class="dt">void</span></span>
|
||
<span id="cb2-21"><a href="#cb2-21"></a>tfe_text_view_open <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">,</span> GtkWindow <span class="op">*</span>win<span class="op">);</span></span>
|
||
<span id="cb2-22"><a href="#cb2-22"></a></span>
|
||
<span id="cb2-23"><a href="#cb2-23"></a><span class="dt">void</span></span>
|
||
<span id="cb2-24"><a href="#cb2-24"></a>tfe_text_view_save <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">);</span></span>
|
||
<span id="cb2-25"><a href="#cb2-25"></a></span>
|
||
<span id="cb2-26"><a href="#cb2-26"></a><span class="dt">void</span></span>
|
||
<span id="cb2-27"><a href="#cb2-27"></a>tfe_text_view_saveas <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">);</span></span>
|
||
<span id="cb2-28"><a href="#cb2-28"></a></span>
|
||
<span id="cb2-29"><a href="#cb2-29"></a>GtkWidget <span class="op">*</span></span>
|
||
<span id="cb2-30"><a href="#cb2-30"></a>tfe_text_view_new_with_file <span class="op">(</span>GFile <span class="op">*</span>file<span class="op">);</span></span>
|
||
<span id="cb2-31"><a href="#cb2-31"></a></span>
|
||
<span id="cb2-32"><a href="#cb2-32"></a>GtkWidget <span class="op">*</span></span>
|
||
<span id="cb2-33"><a href="#cb2-33"></a>tfe_text_view_new <span class="op">(</span><span class="dt">void</span><span class="op">);</span></span>
|
||
<span id="cb2-34"><a href="#cb2-34"></a></span>
|
||
<span id="cb2-35"><a href="#cb2-35"></a><span class="pp">#endif </span><span class="co">/* __TFE_TEXT_VIEW_H__ */</span></span></code></pre></div>
|
||
<ul>
|
||
<li>1,2,35: Thanks to these three lines, the following lines are
|
||
included only once.</li>
|
||
<li>4: Includes gtk4 header files. The header file <code>gtk4</code>
|
||
also has the same mechanism to avoid including it multiple times.</li>
|
||
<li>6-7: These two lines define TfeTextView type, its class structure
|
||
and some useful macros.</li>
|
||
<li>9-15: A definition of the value of the parameter of “open-response”
|
||
signal.</li>
|
||
<li>17-33: Declarations of public functions on TfeTextView.</li>
|
||
</ul>
|
||
<h2 id="functions-to-create-tfetextview-instances">Functions to create
|
||
TfeTextView instances</h2>
|
||
<p>A TfeTextView instance is created with <code>tfe_text_view_new</code>
|
||
or <code>tfe_text_view_new_with_file</code>.</p>
|
||
<div class="sourceCode" id="cb3"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>GtkWidget <span class="op">*</span>tfe_text_view_new <span class="op">(</span><span class="dt">void</span><span class="op">);</span></span></code></pre></div>
|
||
<p><code>tfe_text_view_new</code> just creates a new TfeTextView
|
||
instance and returns the pointer to the new instance.</p>
|
||
<div class="sourceCode" id="cb4"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a>GtkWidget <span class="op">*</span>tfe_text_view_new_with_file <span class="op">(</span>GFile <span class="op">*</span>file<span class="op">);</span></span></code></pre></div>
|
||
<p><code>tfe_text_view_new_with_file</code> is given a Gfile object as
|
||
an argument and it loads the file into the GtkTextBuffer instance, then
|
||
returns the pointer to the new instance. If an error occurs during the
|
||
creation process, NULL is returned.</p>
|
||
<p>Each function is defined as follows.</p>
|
||
<div class="sourceCode" id="cb5"><pre
|
||
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb5-1"><a href="#cb5-1"></a>GtkWidget <span class="op">*</span></span>
|
||
<span id="cb5-2"><a href="#cb5-2"></a>tfe_text_view_new_with_file <span class="op">(</span>GFile <span class="op">*</span>file<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb5-3"><a href="#cb5-3"></a> g_return_val_if_fail <span class="op">(</span>G_IS_FILE <span class="op">(</span>file<span class="op">),</span> NULL<span class="op">);</span></span>
|
||
<span id="cb5-4"><a href="#cb5-4"></a></span>
|
||
<span id="cb5-5"><a href="#cb5-5"></a> GtkWidget <span class="op">*</span>tv<span class="op">;</span></span>
|
||
<span id="cb5-6"><a href="#cb5-6"></a> GtkTextBuffer <span class="op">*</span>tb<span class="op">;</span></span>
|
||
<span id="cb5-7"><a href="#cb5-7"></a> <span class="dt">char</span> <span class="op">*</span>contents<span class="op">;</span></span>
|
||
<span id="cb5-8"><a href="#cb5-8"></a> gsize length<span class="op">;</span></span>
|
||
<span id="cb5-9"><a href="#cb5-9"></a></span>
|
||
<span id="cb5-10"><a href="#cb5-10"></a> <span class="cf">if</span> <span class="op">(!</span> g_file_load_contents <span class="op">(</span>file<span class="op">,</span> NULL<span class="op">,</span> <span class="op">&</span>contents<span class="op">,</span> <span class="op">&</span>length<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">))</span> <span class="co">/* read error */</span></span>
|
||
<span id="cb5-11"><a href="#cb5-11"></a> <span class="cf">return</span> NULL<span class="op">;</span></span>
|
||
<span id="cb5-12"><a href="#cb5-12"></a></span>
|
||
<span id="cb5-13"><a href="#cb5-13"></a> <span class="cf">if</span> <span class="op">((</span>tv <span class="op">=</span> tfe_text_view_new<span class="op">())</span> <span class="op">!=</span> NULL<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb5-14"><a href="#cb5-14"></a> tb <span class="op">=</span> gtk_text_view_get_buffer <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
|
||
<span id="cb5-15"><a href="#cb5-15"></a> gtk_text_buffer_set_text <span class="op">(</span>tb<span class="op">,</span> contents<span class="op">,</span> length<span class="op">);</span></span>
|
||
<span id="cb5-16"><a href="#cb5-16"></a> TFE_TEXT_VIEW <span class="op">(</span>tv<span class="op">)-></span>file <span class="op">=</span> g_file_dup <span class="op">(</span>file<span class="op">);</span></span>
|
||
<span id="cb5-17"><a href="#cb5-17"></a> gtk_text_buffer_set_modified <span class="op">(</span>tb<span class="op">,</span> FALSE<span class="op">);</span></span>
|
||
<span id="cb5-18"><a href="#cb5-18"></a> <span class="op">}</span></span>
|
||
<span id="cb5-19"><a href="#cb5-19"></a> g_free <span class="op">(</span>contents<span class="op">);</span></span>
|
||
<span id="cb5-20"><a href="#cb5-20"></a> <span class="cf">return</span> tv<span class="op">;</span></span>
|
||
<span id="cb5-21"><a href="#cb5-21"></a><span class="op">}</span></span>
|
||
<span id="cb5-22"><a href="#cb5-22"></a></span>
|
||
<span id="cb5-23"><a href="#cb5-23"></a>GtkWidget <span class="op">*</span></span>
|
||
<span id="cb5-24"><a href="#cb5-24"></a>tfe_text_view_new <span class="op">(</span><span class="dt">void</span><span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb5-25"><a href="#cb5-25"></a> <span class="cf">return</span> GTK_WIDGET <span class="op">(</span>g_object_new <span class="op">(</span>TFE_TYPE_TEXT_VIEW<span class="op">,</span> NULL<span class="op">));</span></span>
|
||
<span id="cb5-26"><a href="#cb5-26"></a><span class="op">}</span></span></code></pre></div>
|
||
<ul>
|
||
<li>23-25: <code>tfe_text_view_new</code> function. Just returns the
|
||
value from the function <code>g_object_new</code> but casts it to the
|
||
pointer to GtkWidget. Initialization is done in
|
||
<code>tfe_text_view_init</code> which is called in the process of
|
||
<code>g_object_new</code> function.</li>
|
||
<li>1-21: <code>tfe_text_view_new_with_file</code> function.</li>
|
||
<li>3: <code>g_return_val_if_fail</code> is described in <a
|
||
href="https://docs.gtk.org/glib/func.return_val_if_fail.html">GLib API
|
||
Reference, g_return_val_if_fail</a>. And also <a
|
||
href="https://docs.gtk.org/glib/logging.html">GLib API Reference,
|
||
Message Logging</a>. It tests whether the argument <code>file</code> is
|
||
a pointer to GFile. If it’s true, then the program goes on to the next
|
||
line. If it’s false, then it returns NULL (the second argument)
|
||
immediately. And at the same time it logs out the error message (usually
|
||
the log is outputted to stderr or stdout). This function is used to
|
||
check the programmer’s error. If an error occurs, the solution is
|
||
usually to change the (caller) program and fix the bug. You need to
|
||
distinguish programmer’s errors and runtime errors. You shouldn’t use
|
||
this function to find runtime errors.</li>
|
||
<li>10-11: If an error occurs when reading the file, then the function
|
||
returns NULL.</li>
|
||
<li>13: Calls the function <code>tfe_text_view_new</code>. The function
|
||
creates TfeTextView instance and returns the pointer to the instance. If
|
||
an error happens in <code>tfe_text_view_new</code>, it returns
|
||
NULL.</li>
|
||
<li>14: Gets the pointer to GtkTextBuffer corresponds to
|
||
<code>tv</code>. The pointer is assigned to <code>tb</code></li>
|
||
<li>15: Assigns the contents read from the file to GtkTextBuffer pointed
|
||
by <code>tb</code>.</li>
|
||
<li>16: Duplicates <code>file</code> and sets <code>tv->file</code>
|
||
to point it.</li>
|
||
<li>17: The function
|
||
<code>gtk_text_buffer_set_modified (tb, FALSE)</code> sets the
|
||
modification flag of <code>tb</code> to FALSE. The modification flag
|
||
indicates that the contents of the buffer has been modified. It is used
|
||
when the contents are saved. If the modification flag is FALSE, it
|
||
doesn’t need to save the contents.</li>
|
||
<li>19: Frees the memories pointed by <code>contents</code>.</li>
|
||
<li>20: Returns <code>tv</code>, which is a pointer to the newly created
|
||
TfeTextView instance. If an error happens, NULL is returned.</li>
|
||
</ul>
|
||
<h2 id="save-and-saveas-functions">Save and saveas functions</h2>
|
||
<p>Save and saveas functions write the contents in the GtkTextBuffer to
|
||
a file.</p>
|
||
<div class="sourceCode" id="cb6"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> tfe_text_view_save <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">)</span></span></code></pre></div>
|
||
<p>The function <code>tfe_text_view_save</code> writes the contents in
|
||
the GtkTextBuffer to a file specified by <code>tv->file</code>. If
|
||
<code>tv->file</code> is NULL, then it shows GtkFileChooserDialog and
|
||
prompts the user to choose a file to save. Then it saves the contents to
|
||
the file and sets <code>tv->file</code> to point the GFile instance
|
||
for the file.</p>
|
||
<div class="sourceCode" id="cb7"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> tfe_text_view_saveas <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">)</span></span></code></pre></div>
|
||
<p>The function <code>saveas</code> uses GtkFileChooserDialog and
|
||
prompts the user to select a existed file or specify a new file to save.
|
||
Then, the function changes <code>tv->file</code> and save the
|
||
contents to the specified file. If an error occurs, it is shown to the
|
||
user through the message dialog. The error is managed only in the
|
||
TfeTextView and no information is notified to the caller.</p>
|
||
<div class="sourceCode" id="cb8"><pre
|
||
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb8-1"><a href="#cb8-1"></a><span class="dt">static</span> gboolean</span>
|
||
<span id="cb8-2"><a href="#cb8-2"></a>save_file <span class="op">(</span>GFile <span class="op">*</span>file<span class="op">,</span> GtkTextBuffer <span class="op">*</span>tb<span class="op">,</span> GtkWindow <span class="op">*</span>win<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb8-3"><a href="#cb8-3"></a> GtkTextIter start_iter<span class="op">;</span></span>
|
||
<span id="cb8-4"><a href="#cb8-4"></a> GtkTextIter end_iter<span class="op">;</span></span>
|
||
<span id="cb8-5"><a href="#cb8-5"></a> gchar <span class="op">*</span>contents<span class="op">;</span></span>
|
||
<span id="cb8-6"><a href="#cb8-6"></a> gboolean stat<span class="op">;</span></span>
|
||
<span id="cb8-7"><a href="#cb8-7"></a> GtkWidget <span class="op">*</span>message_dialog<span class="op">;</span></span>
|
||
<span id="cb8-8"><a href="#cb8-8"></a> GError <span class="op">*</span>err <span class="op">=</span> NULL<span class="op">;</span></span>
|
||
<span id="cb8-9"><a href="#cb8-9"></a></span>
|
||
<span id="cb8-10"><a href="#cb8-10"></a> gtk_text_buffer_get_bounds <span class="op">(</span>tb<span class="op">,</span> <span class="op">&</span>start_iter<span class="op">,</span> <span class="op">&</span>end_iter<span class="op">);</span></span>
|
||
<span id="cb8-11"><a href="#cb8-11"></a> contents <span class="op">=</span> gtk_text_buffer_get_text <span class="op">(</span>tb<span class="op">,</span> <span class="op">&</span>start_iter<span class="op">,</span> <span class="op">&</span>end_iter<span class="op">,</span> FALSE<span class="op">);</span></span>
|
||
<span id="cb8-12"><a href="#cb8-12"></a> <span class="cf">if</span> <span class="op">(</span>g_file_replace_contents <span class="op">(</span>file<span class="op">,</span> contents<span class="op">,</span> strlen <span class="op">(</span>contents<span class="op">),</span> NULL<span class="op">,</span> TRUE<span class="op">,</span> G_FILE_CREATE_NONE<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">,</span> <span class="op">&</span>err<span class="op">))</span> <span class="op">{</span></span>
|
||
<span id="cb8-13"><a href="#cb8-13"></a> gtk_text_buffer_set_modified <span class="op">(</span>tb<span class="op">,</span> FALSE<span class="op">);</span></span>
|
||
<span id="cb8-14"><a href="#cb8-14"></a> stat <span class="op">=</span> TRUE<span class="op">;</span></span>
|
||
<span id="cb8-15"><a href="#cb8-15"></a> <span class="op">}</span> <span class="cf">else</span> <span class="op">{</span></span>
|
||
<span id="cb8-16"><a href="#cb8-16"></a> message_dialog <span class="op">=</span> gtk_message_dialog_new <span class="op">(</span>win<span class="op">,</span> GTK_DIALOG_MODAL<span class="op">,</span></span>
|
||
<span id="cb8-17"><a href="#cb8-17"></a> GTK_MESSAGE_ERROR<span class="op">,</span> GTK_BUTTONS_CLOSE<span class="op">,</span></span>
|
||
<span id="cb8-18"><a href="#cb8-18"></a> <span class="st">"%s.</span><span class="sc">\n</span><span class="st">"</span><span class="op">,</span> err<span class="op">-></span>message<span class="op">);</span></span>
|
||
<span id="cb8-19"><a href="#cb8-19"></a> g_signal_connect <span class="op">(</span>message_dialog<span class="op">,</span> <span class="st">"response"</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>gtk_window_destroy<span class="op">),</span> NULL<span class="op">);</span></span>
|
||
<span id="cb8-20"><a href="#cb8-20"></a> gtk_widget_show <span class="op">(</span>message_dialog<span class="op">);</span></span>
|
||
<span id="cb8-21"><a href="#cb8-21"></a> g_error_free <span class="op">(</span>err<span class="op">);</span></span>
|
||
<span id="cb8-22"><a href="#cb8-22"></a> stat <span class="op">=</span> FALSE<span class="op">;</span></span>
|
||
<span id="cb8-23"><a href="#cb8-23"></a> <span class="op">}</span></span>
|
||
<span id="cb8-24"><a href="#cb8-24"></a> g_free <span class="op">(</span>contents<span class="op">);</span></span>
|
||
<span id="cb8-25"><a href="#cb8-25"></a> <span class="cf">return</span> stat<span class="op">;</span></span>
|
||
<span id="cb8-26"><a href="#cb8-26"></a><span class="op">}</span></span>
|
||
<span id="cb8-27"><a href="#cb8-27"></a></span>
|
||
<span id="cb8-28"><a href="#cb8-28"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||
<span id="cb8-29"><a href="#cb8-29"></a>saveas_dialog_response <span class="op">(</span>GtkWidget <span class="op">*</span>dialog<span class="op">,</span> gint response<span class="op">,</span> TfeTextView <span class="op">*</span>tv<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb8-30"><a href="#cb8-30"></a> GtkTextBuffer <span class="op">*</span>tb <span class="op">=</span> gtk_text_view_get_buffer <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
|
||
<span id="cb8-31"><a href="#cb8-31"></a> GFile <span class="op">*</span>file<span class="op">;</span></span>
|
||
<span id="cb8-32"><a href="#cb8-32"></a> GtkWidget <span class="op">*</span>win <span class="op">=</span> gtk_widget_get_ancestor <span class="op">(</span>GTK_WIDGET <span class="op">(</span>tv<span class="op">),</span> GTK_TYPE_WINDOW<span class="op">);</span></span>
|
||
<span id="cb8-33"><a href="#cb8-33"></a></span>
|
||
<span id="cb8-34"><a href="#cb8-34"></a> <span class="cf">if</span> <span class="op">(</span>response <span class="op">==</span> GTK_RESPONSE_ACCEPT<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb8-35"><a href="#cb8-35"></a> file <span class="op">=</span> gtk_file_chooser_get_file <span class="op">(</span>GTK_FILE_CHOOSER <span class="op">(</span>dialog<span class="op">));</span></span>
|
||
<span id="cb8-36"><a href="#cb8-36"></a> <span class="cf">if</span> <span class="op">(!</span> G_IS_FILE <span class="op">(</span>file<span class="op">))</span></span>
|
||
<span id="cb8-37"><a href="#cb8-37"></a> g_warning <span class="op">(</span><span class="st">"TfeTextView: gtk_file_chooser_get_file returns non GFile.</span><span class="sc">\n</span><span class="st">"</span><span class="op">);</span></span>
|
||
<span id="cb8-38"><a href="#cb8-38"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>save_file<span class="op">(</span>file<span class="op">,</span> tb<span class="op">,</span> GTK_WINDOW <span class="op">(</span>win<span class="op">)))</span> <span class="op">{</span></span>
|
||
<span id="cb8-39"><a href="#cb8-39"></a> <span class="cf">if</span> <span class="op">(</span>G_IS_FILE <span class="op">(</span>tv<span class="op">-></span>file<span class="op">))</span></span>
|
||
<span id="cb8-40"><a href="#cb8-40"></a> g_object_unref <span class="op">(</span>tv<span class="op">-></span>file<span class="op">);</span></span>
|
||
<span id="cb8-41"><a href="#cb8-41"></a> tv<span class="op">-></span>file <span class="op">=</span> file<span class="op">;</span></span>
|
||
<span id="cb8-42"><a href="#cb8-42"></a> g_signal_emit <span class="op">(</span>tv<span class="op">,</span> tfe_text_view_signals<span class="op">[</span>CHANGE_FILE<span class="op">],</span> <span class="dv">0</span><span class="op">);</span></span>
|
||
<span id="cb8-43"><a href="#cb8-43"></a> <span class="op">}</span> <span class="cf">else</span></span>
|
||
<span id="cb8-44"><a href="#cb8-44"></a> g_object_unref <span class="op">(</span>file<span class="op">);</span></span>
|
||
<span id="cb8-45"><a href="#cb8-45"></a> <span class="op">}</span></span>
|
||
<span id="cb8-46"><a href="#cb8-46"></a> gtk_window_destroy <span class="op">(</span>GTK_WINDOW <span class="op">(</span>dialog<span class="op">));</span></span>
|
||
<span id="cb8-47"><a href="#cb8-47"></a><span class="op">}</span></span>
|
||
<span id="cb8-48"><a href="#cb8-48"></a></span>
|
||
<span id="cb8-49"><a href="#cb8-49"></a><span class="dt">void</span></span>
|
||
<span id="cb8-50"><a href="#cb8-50"></a>tfe_text_view_save <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb8-51"><a href="#cb8-51"></a> g_return_if_fail <span class="op">(</span>TFE_IS_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
|
||
<span id="cb8-52"><a href="#cb8-52"></a></span>
|
||
<span id="cb8-53"><a href="#cb8-53"></a> GtkTextBuffer <span class="op">*</span>tb <span class="op">=</span> gtk_text_view_get_buffer <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
|
||
<span id="cb8-54"><a href="#cb8-54"></a> GtkWidget <span class="op">*</span>win <span class="op">=</span> gtk_widget_get_ancestor <span class="op">(</span>GTK_WIDGET <span class="op">(</span>tv<span class="op">),</span> GTK_TYPE_WINDOW<span class="op">);</span></span>
|
||
<span id="cb8-55"><a href="#cb8-55"></a></span>
|
||
<span id="cb8-56"><a href="#cb8-56"></a> <span class="cf">if</span> <span class="op">(!</span> gtk_text_buffer_get_modified <span class="op">(</span>tb<span class="op">))</span></span>
|
||
<span id="cb8-57"><a href="#cb8-57"></a> <span class="cf">return</span><span class="op">;</span> <span class="co">/* no need to save it */</span></span>
|
||
<span id="cb8-58"><a href="#cb8-58"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>tv<span class="op">-></span>file <span class="op">==</span> NULL<span class="op">)</span></span>
|
||
<span id="cb8-59"><a href="#cb8-59"></a> tfe_text_view_saveas <span class="op">(</span>tv<span class="op">);</span></span>
|
||
<span id="cb8-60"><a href="#cb8-60"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(!</span> G_IS_FILE <span class="op">(</span>tv<span class="op">-></span>file<span class="op">))</span></span>
|
||
<span id="cb8-61"><a href="#cb8-61"></a> g_error <span class="op">(</span><span class="st">"TfeTextView: The pointer tv->file isn't NULL nor GFile.</span><span class="sc">\n</span><span class="st">"</span><span class="op">);</span></span>
|
||
<span id="cb8-62"><a href="#cb8-62"></a> <span class="cf">else</span></span>
|
||
<span id="cb8-63"><a href="#cb8-63"></a> save_file <span class="op">(</span>tv<span class="op">-></span>file<span class="op">,</span> tb<span class="op">,</span> GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
|
||
<span id="cb8-64"><a href="#cb8-64"></a><span class="op">}</span></span>
|
||
<span id="cb8-65"><a href="#cb8-65"></a></span>
|
||
<span id="cb8-66"><a href="#cb8-66"></a><span class="dt">void</span></span>
|
||
<span id="cb8-67"><a href="#cb8-67"></a>tfe_text_view_saveas <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb8-68"><a href="#cb8-68"></a> g_return_if_fail <span class="op">(</span>TFE_IS_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
|
||
<span id="cb8-69"><a href="#cb8-69"></a></span>
|
||
<span id="cb8-70"><a href="#cb8-70"></a> GtkWidget <span class="op">*</span>dialog<span class="op">;</span></span>
|
||
<span id="cb8-71"><a href="#cb8-71"></a> GtkWidget <span class="op">*</span>win <span class="op">=</span> gtk_widget_get_ancestor <span class="op">(</span>GTK_WIDGET <span class="op">(</span>tv<span class="op">),</span> GTK_TYPE_WINDOW<span class="op">);</span></span>
|
||
<span id="cb8-72"><a href="#cb8-72"></a></span>
|
||
<span id="cb8-73"><a href="#cb8-73"></a> dialog <span class="op">=</span> gtk_file_chooser_dialog_new <span class="op">(</span><span class="st">"Save file"</span><span class="op">,</span> GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> GTK_FILE_CHOOSER_ACTION_SAVE<span class="op">,</span></span>
|
||
<span id="cb8-74"><a href="#cb8-74"></a> <span class="st">"Cancel"</span><span class="op">,</span> GTK_RESPONSE_CANCEL<span class="op">,</span></span>
|
||
<span id="cb8-75"><a href="#cb8-75"></a> <span class="st">"Save"</span><span class="op">,</span> GTK_RESPONSE_ACCEPT<span class="op">,</span></span>
|
||
<span id="cb8-76"><a href="#cb8-76"></a> NULL<span class="op">);</span></span>
|
||
<span id="cb8-77"><a href="#cb8-77"></a> g_signal_connect <span class="op">(</span>dialog<span class="op">,</span> <span class="st">"response"</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>saveas_dialog_response<span class="op">),</span> tv<span class="op">);</span></span>
|
||
<span id="cb8-78"><a href="#cb8-78"></a> gtk_widget_show <span class="op">(</span>dialog<span class="op">);</span></span>
|
||
<span id="cb8-79"><a href="#cb8-79"></a><span class="op">}</span></span></code></pre></div>
|
||
<ul>
|
||
<li>1-26: <code>save_file</code> function. This function is called from
|
||
<code>saveas_dialog_response</code> and <code>tfe_text_view_save</code>.
|
||
This function saves the contents of the buffer to the file given as an
|
||
argument. If error happens, it displays an error message. The class of
|
||
this function is <code>static</code>. Therefore, only functions in this
|
||
file (<code>tfeTetview.c</code>) call this function. Such static
|
||
functions usally don’t have <code>g_return_val_if_fail</code>
|
||
function.</li>
|
||
<li>10-11: Gets the text contents from the buffer.</li>
|
||
<li>12-14: Saves the contents to the file. If no error happens, set the
|
||
modified flag to be FALSE. This means that the buffer is not modified
|
||
since it has been saved. And set the return status <code>stat</code> to
|
||
be TRUE.</li>
|
||
<li>15-23: If it fails to save the contents, displays an error
|
||
message.</li>
|
||
<li>16-18: Creates a message dialog with the error message.</li>
|
||
<li>19: Connects the “response” signal to
|
||
<code>gtk_window_destroy</code>, so that the dialog disappears when a
|
||
user clicked on the button.</li>
|
||
<li>20-21: Shows the window, frees <code>err</code> and set
|
||
<code>stat</code> to be FLASE.</li>
|
||
<li>24: Frees <code>contents</code>.</li>
|
||
<li>25: Returns to the caller.</li>
|
||
<li>28-47: <code>saveas_dialog_response</code> function. This is a
|
||
signal handler for the “response” signal on GtkFileChooserDialog
|
||
instance created by <code>tfe_text_view_saveas</code> function. This
|
||
handler analyzes the response and determines whether to save the
|
||
contents.</li>
|
||
<li>34-45: If the response is <code>GTK_RESPONSE_ACCEPT</code>, the user
|
||
has clicked on the <code>Save</code> button. So, it tries to save.</li>
|
||
<li>35: Gets the GFile <code>file</code> from GtkFileChooserDialog.</li>
|
||
<li>36-37: If it doesn’t point GFile, it outputs an error message to the
|
||
log.</li>
|
||
<li>38: Otherwise, it calls <code>save_file</code> to save the contents
|
||
to the file.</li>
|
||
<li>39-42: If <code>save_file</code> has successfully saved the
|
||
contents, <code>tv->file</code> is updated. If the old GFile pointed
|
||
by <code>tv->file</code> exists, it is freed in advance. Emits
|
||
“change-file” signal.</li>
|
||
<li>44: Unrefs <code>file</code>.</li>
|
||
<li>46: destroys the file chooser dialog.</li>
|
||
<li>49-64: <code>tfe_text_view_save</code> function.</li>
|
||
<li>51: <code>tfe_text_view_save</code> is public, i.e. it is open to
|
||
the other files. So, it doesn’t have <code>static</code> class. Public
|
||
functions should check the parameter type with
|
||
<code>g_return_if_fail</code> function. If <code>tv</code> is not a
|
||
pointer to a TfeTextView instance, then it logs an error message and
|
||
immediately returns. This function is similar to
|
||
<code>g_return_val_if_fail</code>, but no value is returned because
|
||
<code>tfe_text_view_save</code> doesn’t return a value.</li>
|
||
<li>53-54: Gets GtkTextBuffer instance and GtkWidget instance and
|
||
assignes them to <code>tb</code> and<code>win</code> respectively.</li>
|
||
<li>56-57: If the buffer hasn’t modified, then it doesn’t need to save
|
||
it. So the function returns.</li>
|
||
<li>58-59: If <code>tv->file</code> is NULL, no file has given yet.
|
||
It calls <code>tfe_text_view_saveas</code> which prompts a user to
|
||
select a file or specify a new file to save.</li>
|
||
<li>60-61: If <code>tv->file</code> doesn’t point GFile, somethig bad
|
||
has happened. Logs an error message.</li>
|
||
<li>62-63: Calls <code>save_file</code> to save the contents to the
|
||
file.</li>
|
||
<li>66-79: <code>tfe_text_view_saveas</code> function. It shows
|
||
GtkFileChooserDialog and prompts the user to choose a file.</li>
|
||
<li>73-76: Creates GtkFileChooserDialog. The title is “Save file”.
|
||
Transient parent of the dialog is <code>win</code>, which is the
|
||
top-level window. The action is save mode. The buttons are Cancel and
|
||
Save.</li>
|
||
<li>77: connects the “response” signal of the dialog and
|
||
<code>saveas_dialog_response</code> handler.</li>
|
||
<li>78: Shows the dialog.</li>
|
||
</ul>
|
||
<figure>
|
||
<img src="image/saveas.png" alt="Saveas process" />
|
||
<figcaption aria-hidden="true">Saveas process</figcaption>
|
||
</figure>
|
||
<p>When you use GtkFileChooserDialog, you need to divide the program
|
||
into two parts. One is a function which creates GtkFileChooserDialog and
|
||
the other is a signal handler. The function just creates and shows
|
||
GtkFileChooserDialog. The rest is done by the handler. It gets Gfile
|
||
from GtkFileChooserDialog and saves the buffer to the file by calling
|
||
<code>save_file</code>.</p>
|
||
<h2 id="open-function">Open function</h2>
|
||
<p>Open function shows GtkFileChooserDialog to users and prompts them to
|
||
choose a file. Then it reads the file and puts the text into
|
||
GtkTextBuffer.</p>
|
||
<div class="sourceCode" id="cb9"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> tfe_text_view_open <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">,</span> GtkWindow <span class="op">*</span>win<span class="op">);</span></span></code></pre></div>
|
||
<p>The parameter <code>win</code> is the top-level window. It will be a
|
||
transient parent window of GtkFileChooserDialog when the dialog is
|
||
created. This allows window managers to keep the dialog on top of the
|
||
parent window, or center the dialog over the parent window. It is
|
||
possible to give no parent window to the dialog. However, it is
|
||
encouraged to give a parent window to dialog. This function might be
|
||
called just after <code>tv</code> has been created. In that case,
|
||
<code>tv</code> has not been incorporated into the widget hierarchy.
|
||
Therefore it is impossible to get the top-level window from
|
||
<code>tv</code>. That’s why the function needs <code>win</code>
|
||
parameter.</p>
|
||
<p>This function is usually called when the buffer of <code>tv</code> is
|
||
empty. However, even if the buffer is not empty,
|
||
<code>tfe_text_view_open</code> doesn’t treat it as an error. If you
|
||
want to revert the buffer, calling this function is appropriate.
|
||
Otherwise probably bad things will happen.</p>
|
||
<div class="sourceCode" id="cb10"><pre
|
||
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb10-1"><a href="#cb10-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||
<span id="cb10-2"><a href="#cb10-2"></a>open_dialog_response<span class="op">(</span>GtkWidget <span class="op">*</span>dialog<span class="op">,</span> gint response<span class="op">,</span> TfeTextView <span class="op">*</span>tv<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb10-3"><a href="#cb10-3"></a> GtkTextBuffer <span class="op">*</span>tb <span class="op">=</span> gtk_text_view_get_buffer <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
|
||
<span id="cb10-4"><a href="#cb10-4"></a> GFile <span class="op">*</span>file<span class="op">;</span></span>
|
||
<span id="cb10-5"><a href="#cb10-5"></a> <span class="dt">char</span> <span class="op">*</span>contents<span class="op">;</span></span>
|
||
<span id="cb10-6"><a href="#cb10-6"></a> gsize length<span class="op">;</span></span>
|
||
<span id="cb10-7"><a href="#cb10-7"></a> GtkWidget <span class="op">*</span>message_dialog<span class="op">;</span></span>
|
||
<span id="cb10-8"><a href="#cb10-8"></a> GError <span class="op">*</span>err <span class="op">=</span> NULL<span class="op">;</span></span>
|
||
<span id="cb10-9"><a href="#cb10-9"></a></span>
|
||
<span id="cb10-10"><a href="#cb10-10"></a> <span class="cf">if</span> <span class="op">(</span>response <span class="op">!=</span> GTK_RESPONSE_ACCEPT<span class="op">)</span></span>
|
||
<span id="cb10-11"><a href="#cb10-11"></a> g_signal_emit <span class="op">(</span>tv<span class="op">,</span> tfe_text_view_signals<span class="op">[</span>OPEN_RESPONSE<span class="op">],</span> <span class="dv">0</span><span class="op">,</span> TFE_OPEN_RESPONSE_CANCEL<span class="op">);</span></span>
|
||
<span id="cb10-12"><a href="#cb10-12"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(!</span> G_IS_FILE <span class="op">(</span>file <span class="op">=</span> gtk_file_chooser_get_file <span class="op">(</span>GTK_FILE_CHOOSER <span class="op">(</span>dialog<span class="op">))))</span> <span class="op">{</span></span>
|
||
<span id="cb10-13"><a href="#cb10-13"></a> g_warning <span class="op">(</span><span class="st">"TfeTextView: gtk_file_chooser_get_file returns non GFile.</span><span class="sc">\n</span><span class="st">"</span><span class="op">);</span></span>
|
||
<span id="cb10-14"><a href="#cb10-14"></a> g_signal_emit <span class="op">(</span>tv<span class="op">,</span> tfe_text_view_signals<span class="op">[</span>OPEN_RESPONSE<span class="op">],</span> <span class="dv">0</span><span class="op">,</span> TFE_OPEN_RESPONSE_ERROR<span class="op">);</span></span>
|
||
<span id="cb10-15"><a href="#cb10-15"></a> <span class="op">}</span> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(!</span> g_file_load_contents <span class="op">(</span>file<span class="op">,</span> NULL<span class="op">,</span> <span class="op">&</span>contents<span class="op">,</span> <span class="op">&</span>length<span class="op">,</span> NULL<span class="op">,</span> <span class="op">&</span>err<span class="op">))</span> <span class="op">{</span> <span class="co">/* read error */</span></span>
|
||
<span id="cb10-16"><a href="#cb10-16"></a> g_object_unref <span class="op">(</span>file<span class="op">);</span></span>
|
||
<span id="cb10-17"><a href="#cb10-17"></a> message_dialog <span class="op">=</span> gtk_message_dialog_new <span class="op">(</span>GTK_WINDOW <span class="op">(</span>dialog<span class="op">),</span> GTK_DIALOG_MODAL<span class="op">,</span></span>
|
||
<span id="cb10-18"><a href="#cb10-18"></a> GTK_MESSAGE_ERROR<span class="op">,</span> GTK_BUTTONS_CLOSE<span class="op">,</span></span>
|
||
<span id="cb10-19"><a href="#cb10-19"></a> <span class="st">"%s.</span><span class="sc">\n</span><span class="st">"</span><span class="op">,</span> err<span class="op">-></span>message<span class="op">);</span></span>
|
||
<span id="cb10-20"><a href="#cb10-20"></a> g_signal_connect <span class="op">(</span>message_dialog<span class="op">,</span> <span class="st">"response"</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>gtk_window_destroy<span class="op">),</span> NULL<span class="op">);</span></span>
|
||
<span id="cb10-21"><a href="#cb10-21"></a> gtk_widget_show <span class="op">(</span>message_dialog<span class="op">);</span></span>
|
||
<span id="cb10-22"><a href="#cb10-22"></a> g_error_free <span class="op">(</span>err<span class="op">);</span></span>
|
||
<span id="cb10-23"><a href="#cb10-23"></a> g_signal_emit <span class="op">(</span>tv<span class="op">,</span> tfe_text_view_signals<span class="op">[</span>OPEN_RESPONSE<span class="op">],</span> <span class="dv">0</span><span class="op">,</span> TFE_OPEN_RESPONSE_ERROR<span class="op">);</span></span>
|
||
<span id="cb10-24"><a href="#cb10-24"></a> <span class="op">}</span> <span class="cf">else</span> <span class="op">{</span></span>
|
||
<span id="cb10-25"><a href="#cb10-25"></a> gtk_text_buffer_set_text <span class="op">(</span>tb<span class="op">,</span> contents<span class="op">,</span> length<span class="op">);</span></span>
|
||
<span id="cb10-26"><a href="#cb10-26"></a> g_free <span class="op">(</span>contents<span class="op">);</span></span>
|
||
<span id="cb10-27"><a href="#cb10-27"></a> <span class="cf">if</span> <span class="op">(</span>G_IS_FILE <span class="op">(</span>tv<span class="op">-></span>file<span class="op">))</span></span>
|
||
<span id="cb10-28"><a href="#cb10-28"></a> g_object_unref <span class="op">(</span>tv<span class="op">-></span>file<span class="op">);</span></span>
|
||
<span id="cb10-29"><a href="#cb10-29"></a> tv<span class="op">-></span>file <span class="op">=</span> file<span class="op">;</span></span>
|
||
<span id="cb10-30"><a href="#cb10-30"></a> gtk_text_buffer_set_modified <span class="op">(</span>tb<span class="op">,</span> FALSE<span class="op">);</span></span>
|
||
<span id="cb10-31"><a href="#cb10-31"></a> g_signal_emit <span class="op">(</span>tv<span class="op">,</span> tfe_text_view_signals<span class="op">[</span>OPEN_RESPONSE<span class="op">],</span> <span class="dv">0</span><span class="op">,</span> TFE_OPEN_RESPONSE_SUCCESS<span class="op">);</span></span>
|
||
<span id="cb10-32"><a href="#cb10-32"></a> g_signal_emit <span class="op">(</span>tv<span class="op">,</span> tfe_text_view_signals<span class="op">[</span>CHANGE_FILE<span class="op">],</span> <span class="dv">0</span><span class="op">);</span></span>
|
||
<span id="cb10-33"><a href="#cb10-33"></a> <span class="op">}</span></span>
|
||
<span id="cb10-34"><a href="#cb10-34"></a> gtk_window_destroy <span class="op">(</span>GTK_WINDOW <span class="op">(</span>dialog<span class="op">));</span></span>
|
||
<span id="cb10-35"><a href="#cb10-35"></a><span class="op">}</span></span>
|
||
<span id="cb10-36"><a href="#cb10-36"></a></span>
|
||
<span id="cb10-37"><a href="#cb10-37"></a><span class="dt">void</span></span>
|
||
<span id="cb10-38"><a href="#cb10-38"></a>tfe_text_view_open <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">,</span> GtkWindow <span class="op">*</span>win<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb10-39"><a href="#cb10-39"></a> g_return_if_fail <span class="op">(</span>TFE_IS_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
|
||
<span id="cb10-40"><a href="#cb10-40"></a> g_return_if_fail <span class="op">(</span>GTK_IS_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
|
||
<span id="cb10-41"><a href="#cb10-41"></a></span>
|
||
<span id="cb10-42"><a href="#cb10-42"></a> GtkWidget <span class="op">*</span>dialog<span class="op">;</span></span>
|
||
<span id="cb10-43"><a href="#cb10-43"></a></span>
|
||
<span id="cb10-44"><a href="#cb10-44"></a> dialog <span class="op">=</span> gtk_file_chooser_dialog_new <span class="op">(</span><span class="st">"Open file"</span><span class="op">,</span> win<span class="op">,</span> GTK_FILE_CHOOSER_ACTION_OPEN<span class="op">,</span></span>
|
||
<span id="cb10-45"><a href="#cb10-45"></a> <span class="st">"Cancel"</span><span class="op">,</span> GTK_RESPONSE_CANCEL<span class="op">,</span></span>
|
||
<span id="cb10-46"><a href="#cb10-46"></a> <span class="st">"Open"</span><span class="op">,</span> GTK_RESPONSE_ACCEPT<span class="op">,</span></span>
|
||
<span id="cb10-47"><a href="#cb10-47"></a> NULL<span class="op">);</span></span>
|
||
<span id="cb10-48"><a href="#cb10-48"></a> g_signal_connect <span class="op">(</span>dialog<span class="op">,</span> <span class="st">"response"</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>open_dialog_response<span class="op">),</span> tv<span class="op">);</span></span>
|
||
<span id="cb10-49"><a href="#cb10-49"></a> gtk_widget_show <span class="op">(</span>dialog<span class="op">);</span></span>
|
||
<span id="cb10-50"><a href="#cb10-50"></a><span class="op">}</span></span></code></pre></div>
|
||
<ul>
|
||
<li>37-50: <code>tfe_text_view_open</code> function.</li>
|
||
<li>44-47: Creates GtkFileChooserDialog. The title is “Open file”.
|
||
Transient parent window is the top-level window of the application,
|
||
which is given by the caller. The action is open mode. The buttons are
|
||
Cancel and Open.</li>
|
||
<li>48: connects the “response” signal of the dialog and
|
||
<code>open_dialog_response</code> signal handler.</li>
|
||
<li>49: Shows the dialog.</li>
|
||
<li>1-35: <code>open_dialog_response</code> signal handler.</li>
|
||
<li>10-11: If the response from GtkFileChooserDialog is not
|
||
<code>GTK_RESPONSE_ACCEPT</code>, the user has clicked on the “Cancel”
|
||
button or close button on the header bar. Then, “open-response” signal
|
||
is emitted. The parameter of the signal is
|
||
<code>TFE_OPEN_RESPONSE_CANCEL</code>.</li>
|
||
<li>12-14: Gets the pointer to the Gfile by
|
||
<code>gtk_file_chooser_get_file</code>. If it doesn’t point GFile, maybe
|
||
an error has occurred. Then it emits “open-response” signal with the
|
||
parameter <code>TFE_OPEN_RESPONSE_ERROR</code>.</li>
|
||
<li>15-23: If an error occurs at file reading, then it decreases the
|
||
reference count of the Gfile, shows a message dialog to report the error
|
||
to the user and emits “open-response” signal with the parameter
|
||
<code>TFE_OPEN_RESPONSE_ERROR</code>.</li>
|
||
<li>24-33: If the file has successfully been read, then the text is
|
||
inserted to GtkTextBuffer, frees the temporary buffer pointed by
|
||
<code>contents</code> and sets <code>tv->file</code> to point the
|
||
file (no duplication is not necessary). Then, it emits “open-response”
|
||
signal with the parameter <code>TFE_OPEN_RESPONSE_SUCCESS</code> and
|
||
emits “change-file” signal.</li>
|
||
<li>34: destroys GtkFileCooserDialog.</li>
|
||
</ul>
|
||
<p>Now let’s think about the whole process between the caller and
|
||
TfeTextView. It is shown in the following diagram and you would think
|
||
that it is really complicated. Because signal is the only way for
|
||
GtkFileChooserDialog to communicate with others. In GTK 3,
|
||
<code>gtk_dialog_run</code> function is available. It simplifies the
|
||
process. However, in GTK 4, <code>gtk_dialog_run</code> is unavailable
|
||
any more.</p>
|
||
<figure>
|
||
<img src="image/open.png" alt="Caller and TfeTextView" />
|
||
<figcaption aria-hidden="true">Caller and TfeTextView</figcaption>
|
||
</figure>
|
||
<ol type="1">
|
||
<li>A caller gets a pointer <code>tv</code> to a TfeTextView instance by
|
||
calling <code>tfe_text_view_new</code>.</li>
|
||
<li>The caller connects the handler (left bottom in the diagram) and the
|
||
signal “open-response”.</li>
|
||
<li>It calls <code>tfe_text_view_open</code> to prompt the user to
|
||
select a file from GtkFileChooserDialog.</li>
|
||
<li>The dialog emits a signal and it invokes the handler
|
||
<code>open_dialog_response</code>.</li>
|
||
<li>The handler reads the file and inserts the text into GtkTextBuffer
|
||
and emits a signal to inform the status as a response code.</li>
|
||
<li>The handler out of the TfeTextView receives the signal.</li>
|
||
</ol>
|
||
<h2 id="getting-gfile">Getting Gfile</h2>
|
||
<p><code>gtk_text_view_get_file</code> is a simple function shown as
|
||
follows.</p>
|
||
<div class="sourceCode" id="cb11"><pre
|
||
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb11-1"><a href="#cb11-1"></a>GFile <span class="op">*</span></span>
|
||
<span id="cb11-2"><a href="#cb11-2"></a>tfe_text_view_get_file <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb11-3"><a href="#cb11-3"></a> g_return_val_if_fail <span class="op">(</span>TFE_IS_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> NULL<span class="op">);</span></span>
|
||
<span id="cb11-4"><a href="#cb11-4"></a></span>
|
||
<span id="cb11-5"><a href="#cb11-5"></a> <span class="cf">if</span> <span class="op">(</span>G_IS_FILE <span class="op">(</span>tv<span class="op">-></span>file<span class="op">))</span></span>
|
||
<span id="cb11-6"><a href="#cb11-6"></a> <span class="cf">return</span> g_file_dup <span class="op">(</span>tv<span class="op">-></span>file<span class="op">);</span></span>
|
||
<span id="cb11-7"><a href="#cb11-7"></a> <span class="cf">else</span></span>
|
||
<span id="cb11-8"><a href="#cb11-8"></a> <span class="cf">return</span> NULL<span class="op">;</span></span>
|
||
<span id="cb11-9"><a href="#cb11-9"></a><span class="op">}</span></span></code></pre></div>
|
||
<p>The important thing is to duplicate <code>tv->file</code>.
|
||
Otherwise, if the caller frees the GFile object,
|
||
<code>tv->file</code> is no more guaranteed to point the GFile.
|
||
Another reason to use <code>g_file_dup</code> is that GFile isn’t
|
||
thread-safe. If you use GFile in the different thread, the duplication
|
||
is necessary. See <a
|
||
href="https://docs.gtk.org/gio/method.File.dup.html">Gio API Reference,
|
||
g_file_dup</a>.</p>
|
||
<h2 id="the-api-document-and-source-file-of-tfetextview.c">The API
|
||
document and source file of tfetextview.c</h2>
|
||
<p>Refer API document of TfeTextView. Its original markdown file is
|
||
under the directory <code>src/tfetextview</code>.</p>
|
||
<p>All the source files are listed in <a href="sec16.html">Section
|
||
16</a>. You can find them under src/tfe5 and src/tfetextview
|
||
directories.</p>
|
||
</div>
|
||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
|
||
</body>
|
||
</html>
|