mirror of
https://github.com/ToshioCP/Gtk4-tutorial.git
synced 2025-01-12 20:03:28 +01:00
1470 lines
137 KiB
HTML
1470 lines
137 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="sec19.html">Prev: section19</a>
|
||
</li>
|
||
|
||
<li class="nav-item">
|
||
<a class="nav-link" href="sec21.html">Next: section21</a>
|
||
</li>
|
||
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</nav>
|
||
<h1
|
||
id="gtkmenubutton-accelerators-font-pango-and-gsettings">GtkMenuButton,
|
||
accelerators, font, pango and gsettings</h1>
|
||
<p>Tfe text editor will be restructured in this section.</p>
|
||
<ul>
|
||
<li>Open, save and close buttons are placed on the toolbar. In addition,
|
||
GtkMenuButton is added to the toolbar. This button shows a popup menu
|
||
when clicked on. Here, popup means widely, including pull-down
|
||
menu.</li>
|
||
<li>New, save-as, preference and quit items are put into the menu.</li>
|
||
</ul>
|
||
<p>This makes the most frequently used operation bound to the tool bar
|
||
buttons. And the others are stored in behind the menus. So, it is more
|
||
practical.</p>
|
||
<p>In addition, the following features are added.</p>
|
||
<ul>
|
||
<li>Accelerators. For example, Ctrl-O reads a file and creates a new
|
||
page.</li>
|
||
<li>Preference dialog for font selection.</li>
|
||
<li>Alert dialog to confirm closing or quitting without saving
|
||
contents.</li>
|
||
<li>GSettings to keep the font selection.</li>
|
||
</ul>
|
||
<figure>
|
||
<img src="image/tfe6.png" alt="tfe6" />
|
||
<figcaption aria-hidden="true">tfe6</figcaption>
|
||
</figure>
|
||
<h2 id="static-variables-shared-by-functions-in-tfeapplication.c">Static
|
||
variables shared by functions in tfeapplication.c</h2>
|
||
<p>The next version of <code>tfe</code> has static variables in
|
||
<code>tfeapplication.c</code>. Static variables are convenient but not
|
||
good for maintenance. So, the final version will remove them and take
|
||
another way to cover the static variables.</p>
|
||
<p>Anyway, the following is the code with regard to the static
|
||
variables.</p>
|
||
<div class="sourceCode" id="cb1"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> GtkDialog <span class="op">*</span>pref<span class="op">;</span> <span class="co">// preference dialog</span></span>
|
||
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> GtkFontButton <span class="op">*</span>fontbtn<span class="op">;</span> <span class="co">// font button</span></span>
|
||
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> GSettings <span class="op">*</span>settings<span class="op">;</span> <span class="co">// GSetting</span></span>
|
||
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> GtkDialog <span class="op">*</span>alert<span class="op">;</span> <span class="co">// alert dialog</span></span>
|
||
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> GtkLabel <span class="op">*</span>lb_alert<span class="op">;</span> <span class="co">// label in the alert dialog</span></span>
|
||
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> GtkButton <span class="op">*</span>btn_accept<span class="op">;</span> <span class="co">// accept button in the alert dialog</span></span>
|
||
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> GtkCssProvider <span class="op">*</span>provider0<span class="op">;</span> <span class="co">//CSS provider for textview padding</span></span>
|
||
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> GtkCssProvider <span class="op">*</span>provider<span class="op">;</span> <span class="co">// CSS provider for fonts</span></span>
|
||
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> gulong pref_close_request_handler_id <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
|
||
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> gulong alert_close_request_handler_id <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
|
||
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> gboolean is_quit<span class="op">;</span> <span class="co">// flag whether to quit or close</span></span></code></pre></div>
|
||
<p>These variables can be referred by any functions in the file.</p>
|
||
<h2 id="signal-tags-in-ui-files">Signal tags in ui files</h2>
|
||
<p>The four buttons are included in the ui file <code>tfe.ui</code>. A
|
||
difference from prior sections is signal tags. The following is
|
||
extracted from <code>tfe.ui</code> and it describes the open button.</p>
|
||
<div class="sourceCode" id="cb2"><pre
|
||
class="sourceCode xml"><code class="sourceCode xml"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><<span class="kw">object</span><span class="ot"> class=</span><span class="st">"GtkButton"</span><span class="ot"> id=</span><span class="st">"btno"</span>></span>
|
||
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"label"</span>>Open</<span class="kw">property</span>></span>
|
||
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a> <<span class="kw">signal</span><span class="ot"> name=</span><span class="st">"clicked"</span><span class="ot"> handler=</span><span class="st">"open_cb"</span><span class="ot"> swapped=</span><span class="st">"TRUE"</span><span class="ot"> object=</span><span class="st">"nb"</span>></<span class="kw">signal</span>></span>
|
||
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a></<span class="kw">object</span>></span></code></pre></div>
|
||
<p>Signal tag specifies the name of the signal, handler and user_data
|
||
object.</p>
|
||
<ul>
|
||
<li>The signal name is “clicked”.</li>
|
||
<li>The handler is “open_cb”.</li>
|
||
<li>The user data object is “nb” (GtkNoteBook instance).</li>
|
||
</ul>
|
||
<p>Swapped attribute has the same effect as
|
||
<code>g_signal_connect_swapped</code> function. So, the signal tag above
|
||
works the same as:</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>g_signal_connect_swapped <span class="op">(</span>btno<span class="op">,</span> <span class="st">"clicked"</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>open_cb<span class="op">),</span> nb<span class="op">);</span></span></code></pre></div>
|
||
<p>This function swaps the button and the forth argument
|
||
(<code>btno</code> and <code>nb</code>) in the handler. If
|
||
<code>g_signal_connect</code> is used, the handler is like this:</p>
|
||
<div class="sourceCode" id="cb4"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="co">/* The parameter user_data is assigned with nb */</span></span>
|
||
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>open_cb <span class="op">(</span>GtkButton <span class="op">*</span>btno<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span> <span class="op">...</span> <span class="op">...</span> <span class="op">}</span></span></code></pre></div>
|
||
<p>If <code>g_signal_connect_swapped</code> is used, the button and the
|
||
user data are swapped.</p>
|
||
<div class="sourceCode" id="cb5"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="co">/* btno and user_data (nb) are exchanged */</span></span>
|
||
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>open_cb <span class="op">(</span>GtkNoteBook <span class="op">*</span>nb<span class="op">)</span> <span class="op">{</span> <span class="op">...</span> <span class="op">...</span> <span class="op">}</span></span></code></pre></div>
|
||
<p>It is good if the button instance is useless in the handler.</p>
|
||
<p>When you use a signal tag in your ui file, you need “-WI,
|
||
–export-dynamic” options to compile. You can achieve this by adding
|
||
“export_dynamic: true” argument to executable function in
|
||
<code>meson.build</code>. And remove static class from the handler.</p>
|
||
<div class="sourceCode" id="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></span>
|
||
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>open_cb <span class="op">(</span>GtkNotebook <span class="op">*</span>nb<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a> notebook_page_open <span class="op">(</span>nb<span class="op">);</span></span>
|
||
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
|
||
<p>If you add static, the function is in the scope of the file and it
|
||
can’t be seen from outside. Then the signal tag can’t find the
|
||
function.</p>
|
||
<h2 id="menu-and-gkmenubutton">Menu and GkMenuButton</h2>
|
||
<p>Traditional menu structure is fine. However, We don’t use all the
|
||
menus or buttons so often. Some mightn’t be clicked at all. Therefore,
|
||
it’s a good idea to put some frequently used buttons on the toolbar and
|
||
the rest into the menu. Such menu are often connected to
|
||
GtkMenuButton.</p>
|
||
<p>Menus are described in <code>menu.ui</code> file.</p>
|
||
<div class="sourceCode" id="cb7"><pre
|
||
class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><span id="cb7-1"><a href="#cb7-1"></a><span class="fu"><?xml</span><span class="ot"> version=</span><span class="st">"1.0"</span><span class="ot"> encoding=</span><span class="st">"UTF-8"</span><span class="fu">?></span></span>
|
||
<span id="cb7-2"><a href="#cb7-2"></a><<span class="kw">interface</span>></span>
|
||
<span id="cb7-3"><a href="#cb7-3"></a> <<span class="kw">menu</span><span class="ot"> id=</span><span class="st">"menu"</span>></span>
|
||
<span id="cb7-4"><a href="#cb7-4"></a> <<span class="kw">section</span>></span>
|
||
<span id="cb7-5"><a href="#cb7-5"></a> <<span class="kw">item</span>></span>
|
||
<span id="cb7-6"><a href="#cb7-6"></a> <<span class="kw">attribute</span><span class="ot"> name=</span><span class="st">"label"</span>>New</<span class="kw">attribute</span>></span>
|
||
<span id="cb7-7"><a href="#cb7-7"></a> <<span class="kw">attribute</span><span class="ot"> name=</span><span class="st">"action"</span>>win.new</<span class="kw">attribute</span>></span>
|
||
<span id="cb7-8"><a href="#cb7-8"></a> </<span class="kw">item</span>></span>
|
||
<span id="cb7-9"><a href="#cb7-9"></a> <<span class="kw">item</span>></span>
|
||
<span id="cb7-10"><a href="#cb7-10"></a> <<span class="kw">attribute</span><span class="ot"> name=</span><span class="st">"label"</span>>Save As…</<span class="kw">attribute</span>></span>
|
||
<span id="cb7-11"><a href="#cb7-11"></a> <<span class="kw">attribute</span><span class="ot"> name=</span><span class="st">"action"</span>>win.saveas</<span class="kw">attribute</span>></span>
|
||
<span id="cb7-12"><a href="#cb7-12"></a> </<span class="kw">item</span>></span>
|
||
<span id="cb7-13"><a href="#cb7-13"></a> </<span class="kw">section</span>></span>
|
||
<span id="cb7-14"><a href="#cb7-14"></a> <<span class="kw">section</span>></span>
|
||
<span id="cb7-15"><a href="#cb7-15"></a> <<span class="kw">item</span>></span>
|
||
<span id="cb7-16"><a href="#cb7-16"></a> <<span class="kw">attribute</span><span class="ot"> name=</span><span class="st">"label"</span>>Preference</<span class="kw">attribute</span>></span>
|
||
<span id="cb7-17"><a href="#cb7-17"></a> <<span class="kw">attribute</span><span class="ot"> name=</span><span class="st">"action"</span>>win.pref</<span class="kw">attribute</span>></span>
|
||
<span id="cb7-18"><a href="#cb7-18"></a> </<span class="kw">item</span>></span>
|
||
<span id="cb7-19"><a href="#cb7-19"></a> </<span class="kw">section</span>></span>
|
||
<span id="cb7-20"><a href="#cb7-20"></a> <<span class="kw">section</span>></span>
|
||
<span id="cb7-21"><a href="#cb7-21"></a> <<span class="kw">item</span>></span>
|
||
<span id="cb7-22"><a href="#cb7-22"></a> <<span class="kw">attribute</span><span class="ot"> name=</span><span class="st">"label"</span>>Quit</<span class="kw">attribute</span>></span>
|
||
<span id="cb7-23"><a href="#cb7-23"></a> <<span class="kw">attribute</span><span class="ot"> name=</span><span class="st">"action"</span>>win.close-all</<span class="kw">attribute</span>></span>
|
||
<span id="cb7-24"><a href="#cb7-24"></a> </<span class="kw">item</span>></span>
|
||
<span id="cb7-25"><a href="#cb7-25"></a> </<span class="kw">section</span>></span>
|
||
<span id="cb7-26"><a href="#cb7-26"></a> </<span class="kw">menu</span>></span>
|
||
<span id="cb7-27"><a href="#cb7-27"></a></<span class="kw">interface</span>></span></code></pre></div>
|
||
<p>There are four items, “New”, “Saveas”, “Preference” and “Quit”.</p>
|
||
<ul>
|
||
<li>“New” menu creates a new empty page.</li>
|
||
<li>“Saveas” menu saves the current page as a different filename from
|
||
the original one.</li>
|
||
<li>“Preference” menu sets preference items. This version of
|
||
<code>tfe</code> has only font preference.</li>
|
||
<li>“Quit” menu quits the application.</li>
|
||
</ul>
|
||
<p>These four menus are not used so often. That’s why they are put into
|
||
the menu behind the menu button.</p>
|
||
<p>All the actions above have “win” scope. Tfe has only one window even
|
||
if the second application runs. So, the scope “app” and “win” have very
|
||
little difference in this application.</p>
|
||
<p>The menus and the menu button are connected with
|
||
<code>gtk_menu_button_set_menu_model</code> function. The variable
|
||
<code>btnm</code> below points a GtkMenuButton object.</p>
|
||
<div class="sourceCode" id="cb8"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a> build <span class="op">=</span> gtk_builder_new_from_resource <span class="op">(</span><span class="st">"/com/github/ToshioCP/tfe/menu.ui"</span><span class="op">);</span></span>
|
||
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a> menu <span class="op">=</span> G_MENU_MODEL <span class="op">(</span>gtk_builder_get_object <span class="op">(</span>build<span class="op">,</span> <span class="st">"menu"</span><span class="op">));</span></span>
|
||
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a> gtk_menu_button_set_menu_model <span class="op">(</span>btnm<span class="op">,</span> menu<span class="op">);</span></span></code></pre></div>
|
||
<h2 id="actions-and-accelerators">Actions and Accelerators</h2>
|
||
<p>Menus are connected to actions. Actions are defined with an array and
|
||
<code>g_action_map_add_action_entries</code> function.</p>
|
||
<div class="sourceCode" id="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">const</span> GActionEntry win_entries<span class="op">[]</span> <span class="op">=</span> <span class="op">{</span></span>
|
||
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a> <span class="op">{</span> <span class="st">"open"</span><span class="op">,</span> open_activated<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">,</span> NULL <span class="op">},</span></span>
|
||
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a> <span class="op">{</span> <span class="st">"save"</span><span class="op">,</span> save_activated<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">,</span> NULL <span class="op">},</span></span>
|
||
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a> <span class="op">{</span> <span class="st">"close"</span><span class="op">,</span> close_activated<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">,</span> NULL <span class="op">},</span></span>
|
||
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a> <span class="op">{</span> <span class="st">"new"</span><span class="op">,</span> new_activated<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">,</span> NULL <span class="op">},</span></span>
|
||
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a> <span class="op">{</span> <span class="st">"saveas"</span><span class="op">,</span> saveas_activated<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">,</span> NULL <span class="op">},</span></span>
|
||
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a> <span class="op">{</span> <span class="st">"pref"</span><span class="op">,</span> pref_activated<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">,</span> NULL <span class="op">},</span></span>
|
||
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true" tabindex="-1"></a> <span class="op">{</span> <span class="st">"close-all"</span><span class="op">,</span> close_all_activated<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">,</span> NULL <span class="op">}</span></span>
|
||
<span id="cb9-9"><a href="#cb9-9" aria-hidden="true" tabindex="-1"></a> <span class="op">};</span></span>
|
||
<span id="cb9-10"><a href="#cb9-10" aria-hidden="true" tabindex="-1"></a> g_action_map_add_action_entries <span class="op">(</span>G_ACTION_MAP <span class="op">(</span>win<span class="op">),</span> win_entries<span class="op">,</span> G_N_ELEMENTS <span class="op">(</span>win_entries<span class="op">),</span> nb<span class="op">);</span></span></code></pre></div>
|
||
<p>There are seven actions, open, save, close, new, saveas, pref and
|
||
close-all. But there were only four menus. New, saveas, pref and
|
||
close-all actions correspond to new, saveas, preference and quit menu
|
||
respectively. The three actions open, save and close doesn’t have
|
||
corresponding menus. Are they necessary? Yes, because they correspond to
|
||
accelerators.</p>
|
||
<p>Accelerators are kinds of short cut key functions. They are defined
|
||
with arrays and <code>gtk_application_set_accels_for_action</code>
|
||
function.</p>
|
||
<div class="sourceCode" id="cb10"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a> <span class="kw">struct</span> <span class="op">{</span></span>
|
||
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span>action<span class="op">;</span></span>
|
||
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span>accels<span class="op">[</span><span class="dv">2</span><span class="op">];</span></span>
|
||
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span> action_accels<span class="op">[]</span> <span class="op">=</span> <span class="op">{</span></span>
|
||
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a> <span class="op">{</span> <span class="st">"win.open"</span><span class="op">,</span> <span class="op">{</span> <span class="st">"<Control>o"</span><span class="op">,</span> NULL <span class="op">}</span> <span class="op">},</span></span>
|
||
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a> <span class="op">{</span> <span class="st">"win.save"</span><span class="op">,</span> <span class="op">{</span> <span class="st">"<Control>s"</span><span class="op">,</span> NULL <span class="op">}</span> <span class="op">},</span></span>
|
||
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a> <span class="op">{</span> <span class="st">"win.close"</span><span class="op">,</span> <span class="op">{</span> <span class="st">"<Control>w"</span><span class="op">,</span> NULL <span class="op">}</span> <span class="op">},</span></span>
|
||
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a> <span class="op">{</span> <span class="st">"win.new"</span><span class="op">,</span> <span class="op">{</span> <span class="st">"<Control>n"</span><span class="op">,</span> NULL <span class="op">}</span> <span class="op">},</span></span>
|
||
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a> <span class="op">{</span> <span class="st">"win.saveas"</span><span class="op">,</span> <span class="op">{</span> <span class="st">"<Shift><Control>s"</span><span class="op">,</span> NULL <span class="op">}</span> <span class="op">},</span></span>
|
||
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true" tabindex="-1"></a> <span class="op">{</span> <span class="st">"win.close-all"</span><span class="op">,</span> <span class="op">{</span> <span class="st">"<Control>q"</span><span class="op">,</span> NULL <span class="op">}</span> <span class="op">},</span></span>
|
||
<span id="cb10-11"><a href="#cb10-11" aria-hidden="true" tabindex="-1"></a> <span class="op">};</span></span>
|
||
<span id="cb10-12"><a href="#cb10-12" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb10-13"><a href="#cb10-13" aria-hidden="true" tabindex="-1"></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> G_N_ELEMENTS<span class="op">(</span>action_accels<span class="op">);</span> i<span class="op">++)</span></span>
|
||
<span id="cb10-14"><a href="#cb10-14" aria-hidden="true" tabindex="-1"></a> gtk_application_set_accels_for_action<span class="op">(</span>GTK_APPLICATION<span class="op">(</span>app<span class="op">),</span> action_accels<span class="op">[</span>i<span class="op">].</span>action<span class="op">,</span> action_accels<span class="op">[</span>i<span class="op">].</span>accels<span class="op">);</span></span></code></pre></div>
|
||
<p>This code is a bit complicated. The array
|
||
<code>action-accels[]</code> is an array of structures. The structure
|
||
is:</p>
|
||
<div class="sourceCode" id="cb11"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a> <span class="kw">struct</span> <span class="op">{</span></span>
|
||
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span>action<span class="op">;</span></span>
|
||
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span>accels<span class="op">[</span><span class="dv">2</span><span class="op">];</span></span>
|
||
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span></code></pre></div>
|
||
<p>The member <code>action</code> is a string. The member
|
||
<code>accels</code> is an array of two strings. For example,</p>
|
||
<div class="sourceCode" id="cb12"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="op">{</span> <span class="st">"win.open"</span><span class="op">,</span> <span class="op">{</span> <span class="st">"<Control>o"</span><span class="op">,</span> NULL <span class="op">}</span> <span class="op">},</span></span></code></pre></div>
|
||
<p>This is the first element of the array
|
||
<code>action_accels</code>.</p>
|
||
<ul>
|
||
<li>The member <code>action</code> is “win.open”. This specifies the
|
||
action “open” belongs to the window object.</li>
|
||
<li>The member <code>accels</code> is an array of two strings,
|
||
“<Control>o” and NULL. The first string specifies a key
|
||
combination. Control key and ‘o’. If you keep pressing the control key
|
||
and push ‘o’ key, then it activates the action <code>win.open</code>.
|
||
The second string NULL (or zero) means the end of the list (array). You
|
||
can define more than one accelerator keys and the list must ends with
|
||
NULL (zero). If you want to do so, the array length needs to be three or
|
||
more. The parser recognizes “<control>o”,
|
||
“<Shift><Alt>F2”, “<Ctrl>minus” and so on. If you want
|
||
to use symbol key like “<Ctrl>-”, use “<Ctrl>minus” instead.
|
||
Such relation between lower case and symbol (character code) is
|
||
specified in <a
|
||
href="https://gitlab.gnome.org/GNOME/gtk/-/blob/master/gdk/gdkkeysyms.h"><code>gdkkeysyms.h</code></a>
|
||
in the GTK 4 source code.</li>
|
||
</ul>
|
||
<h3 id="open-save-and-close-handlers">Open, save and close handlers</h3>
|
||
<p>There are two open handlers. One is a handler for the clicked signal
|
||
on the button. The other is for the activate signal on the action.</p>
|
||
<pre><code>Open button ==(clicked)==> open.cb handler
|
||
Ctrl-o key (accerelator) ==(key down)==> open action activated ==> open_activated handler</code></pre>
|
||
<p>But the behavior of the two handlers are the same. So,
|
||
<code>open_activate</code> just call <code>open.cb</code>.</p>
|
||
<div class="sourceCode" id="cb14"><pre
|
||
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb14-1"><a href="#cb14-1"></a><span class="dt">void</span></span>
|
||
<span id="cb14-2"><a href="#cb14-2"></a>open_cb <span class="op">(</span>GtkNotebook <span class="op">*</span>nb<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb14-3"><a href="#cb14-3"></a> notebook_page_open <span class="op">(</span>nb<span class="op">);</span></span>
|
||
<span id="cb14-4"><a href="#cb14-4"></a><span class="op">}</span></span>
|
||
<span id="cb14-5"><a href="#cb14-5"></a></span>
|
||
<span id="cb14-6"><a href="#cb14-6"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||
<span id="cb14-7"><a href="#cb14-7"></a>open_activated <span class="op">(</span>GSimpleAction <span class="op">*</span>action<span class="op">,</span> GVariant <span class="op">*</span>parameter<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb14-8"><a href="#cb14-8"></a> GtkNotebook <span class="op">*</span>nb <span class="op">=</span> GTK_NOTEBOOK <span class="op">(</span>user_data<span class="op">);</span></span>
|
||
<span id="cb14-9"><a href="#cb14-9"></a> open_cb <span class="op">(</span>nb<span class="op">);</span></span>
|
||
<span id="cb14-10"><a href="#cb14-10"></a><span class="op">}</span></span></code></pre></div>
|
||
<p>The same goes on with the save and close handlers.</p>
|
||
<h2 id="saveas-handler">Saveas handler</h2>
|
||
<p>TfeTextView has a saveas function. So we just write a wrapper
|
||
function in <code>tfenotebook.c</code>.</p>
|
||
<div class="sourceCode" id="cb15"><pre
|
||
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb15-1"><a href="#cb15-1"></a><span class="dt">static</span> TfeTextView <span class="op">*</span></span>
|
||
<span id="cb15-2"><a href="#cb15-2"></a>get_current_textview <span class="op">(</span>GtkNotebook <span class="op">*</span>nb<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb15-3"><a href="#cb15-3"></a> <span class="dt">int</span> i<span class="op">;</span></span>
|
||
<span id="cb15-4"><a href="#cb15-4"></a> GtkWidget <span class="op">*</span>scr<span class="op">;</span></span>
|
||
<span id="cb15-5"><a href="#cb15-5"></a> GtkWidget <span class="op">*</span>tv<span class="op">;</span></span>
|
||
<span id="cb15-6"><a href="#cb15-6"></a></span>
|
||
<span id="cb15-7"><a href="#cb15-7"></a> i <span class="op">=</span> gtk_notebook_get_current_page <span class="op">(</span>nb<span class="op">);</span></span>
|
||
<span id="cb15-8"><a href="#cb15-8"></a> scr <span class="op">=</span> gtk_notebook_get_nth_page <span class="op">(</span>nb<span class="op">,</span> i<span class="op">);</span></span>
|
||
<span id="cb15-9"><a href="#cb15-9"></a> tv <span class="op">=</span> gtk_scrolled_window_get_child <span class="op">(</span>GTK_SCROLLED_WINDOW <span class="op">(</span>scr<span class="op">));</span></span>
|
||
<span id="cb15-10"><a href="#cb15-10"></a> <span class="cf">return</span> TFE_TEXT_VIEW <span class="op">(</span>tv<span class="op">);</span></span>
|
||
<span id="cb15-11"><a href="#cb15-11"></a><span class="op">}</span></span>
|
||
<span id="cb15-12"><a href="#cb15-12"></a></span>
|
||
<span id="cb15-13"><a href="#cb15-13"></a><span class="dt">void</span></span>
|
||
<span id="cb15-14"><a href="#cb15-14"></a>notebook_page_saveas <span class="op">(</span>GtkNotebook <span class="op">*</span>nb<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb15-15"><a href="#cb15-15"></a> g_return_if_fail<span class="op">(</span>GTK_IS_NOTEBOOK <span class="op">(</span>nb<span class="op">));</span></span>
|
||
<span id="cb15-16"><a href="#cb15-16"></a></span>
|
||
<span id="cb15-17"><a href="#cb15-17"></a> TfeTextView <span class="op">*</span>tv<span class="op">;</span></span>
|
||
<span id="cb15-18"><a href="#cb15-18"></a></span>
|
||
<span id="cb15-19"><a href="#cb15-19"></a> tv <span class="op">=</span> get_current_textview <span class="op">(</span>nb<span class="op">);</span></span>
|
||
<span id="cb15-20"><a href="#cb15-20"></a> tfe_text_view_saveas <span class="op">(</span>TFE_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
|
||
<span id="cb15-21"><a href="#cb15-21"></a><span class="op">}</span></span></code></pre></div>
|
||
<p>The function <code>get_current_textview</code> is the same as before.
|
||
The function <code>notebook_page_saveas</code> simply calls
|
||
<code>tfe_text_view_saveas</code>.</p>
|
||
<p>In <code>tfeapplication.c</code>, saveas handler just call
|
||
<code>notebook_page_saveas</code>.</p>
|
||
<div class="sourceCode" id="cb16"><pre
|
||
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb16-1"><a href="#cb16-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||
<span id="cb16-2"><a href="#cb16-2"></a>saveas_activated <span class="op">(</span>GSimpleAction <span class="op">*</span>action<span class="op">,</span> GVariant <span class="op">*</span>parameter<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb16-3"><a href="#cb16-3"></a> GtkNotebook <span class="op">*</span>nb <span class="op">=</span> GTK_NOTEBOOK <span class="op">(</span>user_data<span class="op">);</span></span>
|
||
<span id="cb16-4"><a href="#cb16-4"></a> notebook_page_saveas <span class="op">(</span>nb<span class="op">);</span></span>
|
||
<span id="cb16-5"><a href="#cb16-5"></a><span class="op">}</span></span></code></pre></div>
|
||
<h2 id="preference-and-alert-dialog">Preference and alert dialog</h2>
|
||
<h3 id="preference-dialog">Preference dialog</h3>
|
||
<p>Preference dialog xml definition is added to <code>tfe.ui</code>.</p>
|
||
<div class="sourceCode" id="cb17"><pre
|
||
class="sourceCode xml"><code class="sourceCode xml"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a><<span class="kw">object</span><span class="ot"> class=</span><span class="st">"GtkDialog"</span><span class="ot"> id=</span><span class="st">"pref"</span>></span>
|
||
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"title"</span>>Preferences</<span class="kw">property</span>></span>
|
||
<span id="cb17-3"><a href="#cb17-3" aria-hidden="true" tabindex="-1"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"resizable"</span>>FALSE</<span class="kw">property</span>></span>
|
||
<span id="cb17-4"><a href="#cb17-4" aria-hidden="true" tabindex="-1"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"modal"</span>>TRUE</<span class="kw">property</span>></span>
|
||
<span id="cb17-5"><a href="#cb17-5" aria-hidden="true" tabindex="-1"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"transient-for"</span>>win</<span class="kw">property</span>></span>
|
||
<span id="cb17-6"><a href="#cb17-6" aria-hidden="true" tabindex="-1"></a> <<span class="kw">child</span><span class="ot"> internal-child=</span><span class="st">"content_area"</span>></span>
|
||
<span id="cb17-7"><a href="#cb17-7" aria-hidden="true" tabindex="-1"></a> <<span class="kw">object</span><span class="ot"> class=</span><span class="st">"GtkBox"</span><span class="ot"> id=</span><span class="st">"content_area"</span>></span>
|
||
<span id="cb17-8"><a href="#cb17-8" aria-hidden="true" tabindex="-1"></a> <<span class="kw">child</span>></span>
|
||
<span id="cb17-9"><a href="#cb17-9" aria-hidden="true" tabindex="-1"></a> <<span class="kw">object</span><span class="ot"> class=</span><span class="st">"GtkBox"</span><span class="ot"> id=</span><span class="st">"pref_boxh"</span>></span>
|
||
<span id="cb17-10"><a href="#cb17-10" aria-hidden="true" tabindex="-1"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"orientation"</span>>GTK_ORIENTATION_HORIZONTAL</<span class="kw">property</span>></span>
|
||
<span id="cb17-11"><a href="#cb17-11" aria-hidden="true" tabindex="-1"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"spacing"</span>>12</<span class="kw">property</span>></span>
|
||
<span id="cb17-12"><a href="#cb17-12" aria-hidden="true" tabindex="-1"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"margin-start"</span>>12</<span class="kw">property</span>></span>
|
||
<span id="cb17-13"><a href="#cb17-13" aria-hidden="true" tabindex="-1"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"margin-end"</span>>12</<span class="kw">property</span>></span>
|
||
<span id="cb17-14"><a href="#cb17-14" aria-hidden="true" tabindex="-1"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"margin-top"</span>>12</<span class="kw">property</span>></span>
|
||
<span id="cb17-15"><a href="#cb17-15" aria-hidden="true" tabindex="-1"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"margin-bottom"</span>>12</<span class="kw">property</span>></span>
|
||
<span id="cb17-16"><a href="#cb17-16" aria-hidden="true" tabindex="-1"></a> <<span class="kw">child</span>></span>
|
||
<span id="cb17-17"><a href="#cb17-17" aria-hidden="true" tabindex="-1"></a> <<span class="kw">object</span><span class="ot"> class=</span><span class="st">"GtkLabel"</span><span class="ot"> id=</span><span class="st">"fontlabel"</span>></span>
|
||
<span id="cb17-18"><a href="#cb17-18" aria-hidden="true" tabindex="-1"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"label"</span>>Font:</<span class="kw">property</span>></span>
|
||
<span id="cb17-19"><a href="#cb17-19" aria-hidden="true" tabindex="-1"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"xalign"</span>>1</<span class="kw">property</span>></span>
|
||
<span id="cb17-20"><a href="#cb17-20" aria-hidden="true" tabindex="-1"></a> </<span class="kw">object</span>></span>
|
||
<span id="cb17-21"><a href="#cb17-21" aria-hidden="true" tabindex="-1"></a> </<span class="kw">child</span>></span>
|
||
<span id="cb17-22"><a href="#cb17-22" aria-hidden="true" tabindex="-1"></a> <<span class="kw">child</span>></span>
|
||
<span id="cb17-23"><a href="#cb17-23" aria-hidden="true" tabindex="-1"></a> <<span class="kw">object</span><span class="ot"> class=</span><span class="st">"GtkFontButton"</span><span class="ot"> id=</span><span class="st">"fontbtn"</span>></span>
|
||
<span id="cb17-24"><a href="#cb17-24" aria-hidden="true" tabindex="-1"></a> </<span class="kw">object</span>></span>
|
||
<span id="cb17-25"><a href="#cb17-25" aria-hidden="true" tabindex="-1"></a> </<span class="kw">child</span>></span>
|
||
<span id="cb17-26"><a href="#cb17-26" aria-hidden="true" tabindex="-1"></a> </<span class="kw">object</span>></span>
|
||
<span id="cb17-27"><a href="#cb17-27" aria-hidden="true" tabindex="-1"></a> </<span class="kw">child</span>></span>
|
||
<span id="cb17-28"><a href="#cb17-28" aria-hidden="true" tabindex="-1"></a> </<span class="kw">object</span>></span>
|
||
<span id="cb17-29"><a href="#cb17-29" aria-hidden="true" tabindex="-1"></a> </<span class="kw">child</span>></span>
|
||
<span id="cb17-30"><a href="#cb17-30" aria-hidden="true" tabindex="-1"></a></<span class="kw">object</span>></span></code></pre></div>
|
||
<ul>
|
||
<li>Preference dialog is an independent dialog. It is not a descendant
|
||
widget of the top-level GtkApplicationwindow. Therefore, There’s no
|
||
child tag that surrounds the dialog object.</li>
|
||
<li>There are four properties of the dialog. GtkDialog is a child object
|
||
(not child widget) of GtkWindow, so it inherits all the properties from
|
||
GtkWindow. Title, resizable, modal and transient-for properties are
|
||
inherited from GtkWindow. Transient-for specifies a temporary parent
|
||
window, which the dialog’s location is based on.</li>
|
||
<li>The tag <code><child internal-child="content_area"></code> is
|
||
put at the top of the contents of the dialog. You need to specify a
|
||
GtkBox object tag with content_area id. This object is defined in
|
||
<code>gtkdialog.ui</code> (composite widget) but you need to define it
|
||
again in the child tag. Composite widget will be explained in the next
|
||
section. For further information about GtkDialog ui tags, see:
|
||
<ul>
|
||
<li><a
|
||
href="https://docs.gtk.org/gtk4/class.Builder.html#gtkbuilder-ui-definitions">GTK
|
||
4 API reference – GtkBuilder</a></li>
|
||
<li><a
|
||
href="https://docs.gtk.org/gtk4/class.Dialog.html#gtkdialog-as-gtkbuildable">GTK
|
||
4 API reference – GtkDialog</a></li>
|
||
<li><a
|
||
href="https://gitlab.gnome.org/GNOME/gtk/-/blob/main/gtk/ui/gtkdialog.ui">GtkDialog
|
||
ui file</a></li>
|
||
</ul></li>
|
||
<li>There is a horizontal GtkBox in the content area.</li>
|
||
<li>GtkLabel and GtkFontButton are in the GtkBox.</li>
|
||
</ul>
|
||
<p>I want the preference dialog to keep alive during the application
|
||
lives. So, it is necessary to catch “close-request” signal from the
|
||
dialog and stop the signal propagation. (This signal is emitted when the
|
||
close button, right upper x button of the window, is clicked.) This is
|
||
accomplished by returning TRUE by the signal handler.</p>
|
||
<div class="sourceCode" id="cb18"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> gboolean</span>
|
||
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a>dialog_close_cb <span class="op">(</span>GtkDialog <span class="op">*</span>dialog<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb18-3"><a href="#cb18-3" aria-hidden="true" tabindex="-1"></a> gtk_widget_set_visible <span class="op">(</span>GTK_WIDGET <span class="op">(</span>dialog<span class="op">),</span> false<span class="op">);</span></span>
|
||
<span id="cb18-4"><a href="#cb18-4" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> TRUE<span class="op">;</span></span>
|
||
<span id="cb18-5"><a href="#cb18-5" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
|
||
<span id="cb18-6"><a href="#cb18-6" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb18-7"><a href="#cb18-7" aria-hidden="true" tabindex="-1"></a><span class="op">(</span> in app_startup function <span class="op">)</span></span>
|
||
<span id="cb18-8"><a href="#cb18-8" aria-hidden="true" tabindex="-1"></a>pref_close_request_handler_id <span class="op">=</span> g_signal_connect <span class="op">(</span>GTK_DIALOG <span class="op">(</span>pref<span class="op">),</span> <span class="st">"close-request"</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>dialog_close_cb<span class="op">),</span> NULL<span class="op">);</span></span>
|
||
<span id="cb18-9"><a href="#cb18-9" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span></code></pre></div>
|
||
<p>Generally, signal emission consists of five stages.</p>
|
||
<ol type="1">
|
||
<li>Default handler is invoked if the signal’s flag is
|
||
<code>G_SIGNAL_RUN_FIRST</code>. Default handler is set when a signal is
|
||
registered. It is different from user signal handler, simply called
|
||
signal handler, connected by <code>g_signal_connect</code>series
|
||
function. Default handler can be invoked in either stage 1, 3 or 5. Most
|
||
of the default handlers are <code>G_SIGNAL_RUN_FIRST</code> or
|
||
<code>G_SIGNAL_RUN_LAST</code>.</li>
|
||
<li>Signal handlers are invoked, unless it is connected by
|
||
<code>g_signal_connect_after</code>.</li>
|
||
<li>Default handler is invoked if the signal’s flag is
|
||
<code>G_SIGNAL_RUN_LAST</code>.</li>
|
||
<li>Signal handlers are invoked, if it is connected by
|
||
<code>g_signal_connect_after</code>.</li>
|
||
<li>Default handler is invoked if the signal’s flag is
|
||
<code>G_SIGNAL_RUN_CLEANUP</code>.</li>
|
||
</ol>
|
||
<p>The “close-request” signal is <code>G_SIGNAL_RUN_LAST</code>. So, the
|
||
order of the invocation is:</p>
|
||
<ol type="1">
|
||
<li>Signal handler <code>dialog_close_cb</code></li>
|
||
<li>Default handler</li>
|
||
</ol>
|
||
<p>And If the user signal handler returns TRUE, then other handlers will
|
||
be stopped being invoked. Therefore, the program above prevents the
|
||
invocation of the default handler and stop the closing process of the
|
||
dialog.</p>
|
||
<p>The following codes are extracted from
|
||
<code>tfeapplication.c</code>.</p>
|
||
<div class="sourceCode" id="cb19"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> gulong pref_close_request_handler_id <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
|
||
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> gulong alert_close_request_handler_id <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
|
||
<span id="cb19-3"><a href="#cb19-3" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb19-4"><a href="#cb19-4" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> gboolean</span>
|
||
<span id="cb19-5"><a href="#cb19-5" aria-hidden="true" tabindex="-1"></a>dialog_close_cb <span class="op">(</span>GtkDialog <span class="op">*</span>dialog<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb19-6"><a href="#cb19-6" aria-hidden="true" tabindex="-1"></a> gtk_widget_set_visible <span class="op">(</span>GTK_WIDGET <span class="op">(</span>dialog<span class="op">),</span> false<span class="op">);</span></span>
|
||
<span id="cb19-7"><a href="#cb19-7" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> TRUE<span class="op">;</span></span>
|
||
<span id="cb19-8"><a href="#cb19-8" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
|
||
<span id="cb19-9"><a href="#cb19-9" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb19-10"><a href="#cb19-10" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||
<span id="cb19-11"><a href="#cb19-11" aria-hidden="true" tabindex="-1"></a>pref_activated <span class="op">(</span>GSimpleAction <span class="op">*</span>action<span class="op">,</span> GVariant <span class="op">*</span>parameter<span class="op">,</span> gpointer nb<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb19-12"><a href="#cb19-12" aria-hidden="true" tabindex="-1"></a> gtk_window_present <span class="op">(</span>GTK_WINDOW <span class="op">(</span>pref<span class="op">));</span></span>
|
||
<span id="cb19-13"><a href="#cb19-13" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
|
||
<span id="cb19-14"><a href="#cb19-14" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb19-15"><a href="#cb19-15" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span></span>
|
||
<span id="cb19-16"><a href="#cb19-16" aria-hidden="true" tabindex="-1"></a>app_shutdown <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb19-17"><a href="#cb19-17" aria-hidden="true" tabindex="-1"></a> <span class="op">...</span> <span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb19-18"><a href="#cb19-18" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>pref_close_request_handler_id <span class="op">></span> <span class="dv">0</span><span class="op">)</span></span>
|
||
<span id="cb19-19"><a href="#cb19-19" aria-hidden="true" tabindex="-1"></a> g_signal_handler_disconnect <span class="op">(</span>pref<span class="op">,</span> pref_close_request_handler_id<span class="op">);</span></span>
|
||
<span id="cb19-20"><a href="#cb19-20" aria-hidden="true" tabindex="-1"></a> gtk_window_destroy <span class="op">(</span>GTK_WINDOW <span class="op">(</span>pref<span class="op">));</span></span>
|
||
<span id="cb19-21"><a href="#cb19-21" aria-hidden="true" tabindex="-1"></a> <span class="op">...</span> <span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb19-22"><a href="#cb19-22" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
|
||
<span id="cb19-23"><a href="#cb19-23" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb19-24"><a href="#cb19-24" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||
<span id="cb19-25"><a href="#cb19-25" aria-hidden="true" tabindex="-1"></a>tfe_startup <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb19-26"><a href="#cb19-26" aria-hidden="true" tabindex="-1"></a> <span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb19-27"><a href="#cb19-27" aria-hidden="true" tabindex="-1"></a> pref <span class="op">=</span> GTK_DIALOG <span class="op">(</span>gtk_builder_get_object <span class="op">(</span>build<span class="op">,</span> <span class="st">"pref"</span><span class="op">));</span></span>
|
||
<span id="cb19-28"><a href="#cb19-28" aria-hidden="true" tabindex="-1"></a> pref_close_request_handler_id <span class="op">=</span> g_signal_connect <span class="op">(</span>GTK_DIALOG <span class="op">(</span>pref<span class="op">),</span> <span class="st">"close-request"</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>dialog_close_cb<span class="op">),</span> NULL<span class="op">);</span></span>
|
||
<span id="cb19-29"><a href="#cb19-29" aria-hidden="true" tabindex="-1"></a> <span class="op">...</span> <span class="op">...</span> </span>
|
||
<span id="cb19-30"><a href="#cb19-30" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
|
||
<ul>
|
||
<li>The close-requiest signal on the preference dialog is connected to
|
||
the handler <code>dialog_close_cb</code>. It changes the close behavior
|
||
of the dialog. When the signal is emitted, the visibility is set to
|
||
false and the default handler is canceled. So, the dialog just
|
||
disappears but exists.</li>
|
||
<li>A handler <code>pref_activate</code> shows the preference
|
||
dialog.</li>
|
||
<li>The shutdown handler <code>app_shutdown</code> disconnects the
|
||
handlers from the “close-request”signal and destroys <code>pref</code>
|
||
window.</li>
|
||
</ul>
|
||
<h3 id="alert-dialog">Alert dialog</h3>
|
||
<p>If a user closes a page without saving, it is advisable to show an
|
||
alert for a user to confirm it. Alert dialog is used in such a
|
||
situation.</p>
|
||
<div class="sourceCode" id="cb20"><pre
|
||
class="sourceCode xml"><code class="sourceCode xml"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a> <<span class="kw">object</span><span class="ot"> class=</span><span class="st">"GtkDialog"</span><span class="ot"> id=</span><span class="st">"alert"</span>></span>
|
||
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true" tabindex="-1"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"title"</span>>Are you sure?</<span class="kw">property</span>></span>
|
||
<span id="cb20-3"><a href="#cb20-3" aria-hidden="true" tabindex="-1"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"resizable"</span>>FALSE</<span class="kw">property</span>></span>
|
||
<span id="cb20-4"><a href="#cb20-4" aria-hidden="true" tabindex="-1"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"modal"</span>>TRUE</<span class="kw">property</span>></span>
|
||
<span id="cb20-5"><a href="#cb20-5" aria-hidden="true" tabindex="-1"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"transient-for"</span>>win</<span class="kw">property</span>></span>
|
||
<span id="cb20-6"><a href="#cb20-6" aria-hidden="true" tabindex="-1"></a> <<span class="kw">child</span><span class="ot"> internal-child=</span><span class="st">"content_area"</span>></span>
|
||
<span id="cb20-7"><a href="#cb20-7" aria-hidden="true" tabindex="-1"></a> <<span class="kw">object</span><span class="ot"> class=</span><span class="st">"GtkBox"</span>></span>
|
||
<span id="cb20-8"><a href="#cb20-8" aria-hidden="true" tabindex="-1"></a> <<span class="kw">child</span>></span>
|
||
<span id="cb20-9"><a href="#cb20-9" aria-hidden="true" tabindex="-1"></a> <<span class="kw">object</span><span class="ot"> class=</span><span class="st">"GtkBox"</span>></span>
|
||
<span id="cb20-10"><a href="#cb20-10" aria-hidden="true" tabindex="-1"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"orientation"</span>>GTK_ORIENTATION_HORIZONTAL</<span class="kw">property</span>></span>
|
||
<span id="cb20-11"><a href="#cb20-11" aria-hidden="true" tabindex="-1"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"spacing"</span>>12</<span class="kw">property</span>></span>
|
||
<span id="cb20-12"><a href="#cb20-12" aria-hidden="true" tabindex="-1"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"margin-start"</span>>12</<span class="kw">property</span>></span>
|
||
<span id="cb20-13"><a href="#cb20-13" aria-hidden="true" tabindex="-1"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"margin-end"</span>>12</<span class="kw">property</span>></span>
|
||
<span id="cb20-14"><a href="#cb20-14" aria-hidden="true" tabindex="-1"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"margin-top"</span>>12</<span class="kw">property</span>></span>
|
||
<span id="cb20-15"><a href="#cb20-15" aria-hidden="true" tabindex="-1"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"margin-bottom"</span>>12</<span class="kw">property</span>></span>
|
||
<span id="cb20-16"><a href="#cb20-16" aria-hidden="true" tabindex="-1"></a> <<span class="kw">child</span>></span>
|
||
<span id="cb20-17"><a href="#cb20-17" aria-hidden="true" tabindex="-1"></a> <<span class="kw">object</span><span class="ot"> class=</span><span class="st">"GtkImage"</span>></span>
|
||
<span id="cb20-18"><a href="#cb20-18" aria-hidden="true" tabindex="-1"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"icon-name"</span>>dialog-warning</<span class="kw">property</span>></span>
|
||
<span id="cb20-19"><a href="#cb20-19" aria-hidden="true" tabindex="-1"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"icon-size"</span>>GTK_ICON_SIZE_LARGE</<span class="kw">property</span>></span>
|
||
<span id="cb20-20"><a href="#cb20-20" aria-hidden="true" tabindex="-1"></a> </<span class="kw">object</span>></span>
|
||
<span id="cb20-21"><a href="#cb20-21" aria-hidden="true" tabindex="-1"></a> </<span class="kw">child</span>></span>
|
||
<span id="cb20-22"><a href="#cb20-22" aria-hidden="true" tabindex="-1"></a> <<span class="kw">child</span>></span>
|
||
<span id="cb20-23"><a href="#cb20-23" aria-hidden="true" tabindex="-1"></a> <<span class="kw">object</span><span class="ot"> class=</span><span class="st">"GtkLabel"</span><span class="ot"> id=</span><span class="st">"lb_alert"</span>></span>
|
||
<span id="cb20-24"><a href="#cb20-24" aria-hidden="true" tabindex="-1"></a> </<span class="kw">object</span>></span>
|
||
<span id="cb20-25"><a href="#cb20-25" aria-hidden="true" tabindex="-1"></a> </<span class="kw">child</span>></span>
|
||
<span id="cb20-26"><a href="#cb20-26" aria-hidden="true" tabindex="-1"></a> </<span class="kw">object</span>></span>
|
||
<span id="cb20-27"><a href="#cb20-27" aria-hidden="true" tabindex="-1"></a> </<span class="kw">child</span>></span>
|
||
<span id="cb20-28"><a href="#cb20-28" aria-hidden="true" tabindex="-1"></a> </<span class="kw">object</span>></span>
|
||
<span id="cb20-29"><a href="#cb20-29" aria-hidden="true" tabindex="-1"></a> </<span class="kw">child</span>></span>
|
||
<span id="cb20-30"><a href="#cb20-30" aria-hidden="true" tabindex="-1"></a> <<span class="kw">child</span><span class="ot"> type=</span><span class="st">"action"</span>></span>
|
||
<span id="cb20-31"><a href="#cb20-31" aria-hidden="true" tabindex="-1"></a> <<span class="kw">object</span><span class="ot"> class=</span><span class="st">"GtkButton"</span><span class="ot"> id=</span><span class="st">"btn_cancel"</span>></span>
|
||
<span id="cb20-32"><a href="#cb20-32" aria-hidden="true" tabindex="-1"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"label"</span>>Cancel</<span class="kw">property</span>></span>
|
||
<span id="cb20-33"><a href="#cb20-33" aria-hidden="true" tabindex="-1"></a> </<span class="kw">object</span>></span>
|
||
<span id="cb20-34"><a href="#cb20-34" aria-hidden="true" tabindex="-1"></a> </<span class="kw">child</span>></span>
|
||
<span id="cb20-35"><a href="#cb20-35" aria-hidden="true" tabindex="-1"></a> <<span class="kw">child</span><span class="ot"> type=</span><span class="st">"action"</span>></span>
|
||
<span id="cb20-36"><a href="#cb20-36" aria-hidden="true" tabindex="-1"></a> <<span class="kw">object</span><span class="ot"> class=</span><span class="st">"GtkButton"</span><span class="ot"> id=</span><span class="st">"btn_accept"</span>></span>
|
||
<span id="cb20-37"><a href="#cb20-37" aria-hidden="true" tabindex="-1"></a> <<span class="kw">property</span><span class="ot"> name=</span><span class="st">"label"</span>>Close</<span class="kw">property</span>></span>
|
||
<span id="cb20-38"><a href="#cb20-38" aria-hidden="true" tabindex="-1"></a> </<span class="kw">object</span>></span>
|
||
<span id="cb20-39"><a href="#cb20-39" aria-hidden="true" tabindex="-1"></a> </<span class="kw">child</span>></span>
|
||
<span id="cb20-40"><a href="#cb20-40" aria-hidden="true" tabindex="-1"></a> <<span class="kw">action-widgets</span>></span>
|
||
<span id="cb20-41"><a href="#cb20-41" aria-hidden="true" tabindex="-1"></a> <<span class="kw">action-widget</span><span class="ot"> response=</span><span class="st">"cancel"</span><span class="ot"> default=</span><span class="st">"true"</span>>btn_cancel</<span class="kw">action-widget</span>></span>
|
||
<span id="cb20-42"><a href="#cb20-42" aria-hidden="true" tabindex="-1"></a> <<span class="kw">action-widget</span><span class="ot"> response=</span><span class="st">"accept"</span>>btn_accept</<span class="kw">action-widget</span>></span>
|
||
<span id="cb20-43"><a href="#cb20-43" aria-hidden="true" tabindex="-1"></a> </<span class="kw">action-widgets</span>></span>
|
||
<span id="cb20-44"><a href="#cb20-44" aria-hidden="true" tabindex="-1"></a> <<span class="kw">signal</span><span class="ot"> name=</span><span class="st">"response"</span><span class="ot"> handler=</span><span class="st">"alert_response_cb"</span><span class="ot"> swapped=</span><span class="st">"NO"</span><span class="ot"> object=</span><span class="st">"nb"</span>></<span class="kw">signal</span>></span>
|
||
<span id="cb20-45"><a href="#cb20-45" aria-hidden="true" tabindex="-1"></a> </<span class="kw">object</span>></span></code></pre></div>
|
||
<p>This ui file describes the alert dialog. Some part are the same as
|
||
preference dialog. There are two objects in the content area, GtkImage
|
||
and GtkLabel.</p>
|
||
<p>GtkImage shows an image. The image can comes from files, resources,
|
||
icon theme and so on. The image above displays an icon from the current
|
||
icon theme. You can see icons in the theme by
|
||
<code>gtk4-icon-browser</code>.</p>
|
||
<pre><code>$ gtk4-icon-browser</code></pre>
|
||
<p>The “dialog-warning” icon is something like this.</p>
|
||
<figure>
|
||
<img src="image/dialog_warning.png"
|
||
alt="dialog-warning icon is like …" />
|
||
<figcaption aria-hidden="true">dialog-warning icon is like
|
||
…</figcaption>
|
||
</figure>
|
||
<p>These are made by my hand. The real image on the alert dialog is
|
||
nicer.</p>
|
||
<p>The GtkLabel <code>lb_alert</code> has no text yet. An alert message
|
||
will be inserted in the program.</p>
|
||
<p>There are two child tags which have “action” type. They are button
|
||
objects located in the action area. Action-widgets tag describes the
|
||
actions of the buttons. The button <code>btn_cancel</code> emits
|
||
response signal with cancel response (<code>GTK_RESPONSE_CANCEL</code>)
|
||
if it is clicked on. The button <code>btn_accept</code> emits response
|
||
signal with accept response (<code>GTK_RESPONSE_ACCEPT</code>) if it is
|
||
clicked on. The response signal is connected to
|
||
<code>alert_response_cb</code> handler.</p>
|
||
<p>The alert dialog keeps alive while the application lives. The
|
||
“close-request” signal is stopped by the handler
|
||
<code>dialog_close_cb</code> like the preference dialog.</p>
|
||
<h3 id="alert-dialog-and-close-handlers">Alert dialog and close
|
||
handlers</h3>
|
||
<p>If a user closes a page or quits the application without saving the
|
||
contents, the alert dialog appears. There are four handlers, close_cb,
|
||
close_activated, win_close_request_cb and close_all_activated. The first
|
||
two are called when a notebook page is closed. The others are called
|
||
when the main window is closed — so, all the notebooks are closed.</p>
|
||
<ul>
|
||
<li>close button => close_cb (=> alert dialog)</li>
|
||
<li>Ctrl-W => close_activated => close_cb (=> alert
|
||
dialog)</li>
|
||
<li>Close button (x button at the right top of the main window) =>
|
||
win_close_request_cb (=> alert dialog)</li>
|
||
<li>Quit menu or Ctrl-Q => close_all_activated =>
|
||
win_close_request_cb (=> alert dialog)</li>
|
||
</ul>
|
||
<div class="sourceCode" id="cb22"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> gboolean is_quit<span class="op">;</span></span>
|
||
<span id="cb22-2"><a href="#cb22-2" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb22-3"><a href="#cb22-3" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> gboolean</span>
|
||
<span id="cb22-4"><a href="#cb22-4" aria-hidden="true" tabindex="-1"></a>win_close_request_cb <span class="op">(</span>GtkWindow <span class="op">*</span>win<span class="op">,</span> GtkNotebook <span class="op">*</span>nb<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb22-5"><a href="#cb22-5" aria-hidden="true" tabindex="-1"></a> is_quit <span class="op">=</span> true<span class="op">;</span></span>
|
||
<span id="cb22-6"><a href="#cb22-6" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>has_saved_all <span class="op">(</span>nb<span class="op">))</span></span>
|
||
<span id="cb22-7"><a href="#cb22-7" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> false<span class="op">;</span></span>
|
||
<span id="cb22-8"><a href="#cb22-8" aria-hidden="true" tabindex="-1"></a> <span class="cf">else</span> <span class="op">{</span></span>
|
||
<span id="cb22-9"><a href="#cb22-9" aria-hidden="true" tabindex="-1"></a> gtk_label_set_text <span class="op">(</span>lb_alert<span class="op">,</span> <span class="st">"Contents aren't saved yet.</span><span class="sc">\n</span><span class="st">Are you sure to quit?"</span><span class="op">);</span></span>
|
||
<span id="cb22-10"><a href="#cb22-10" aria-hidden="true" tabindex="-1"></a> gtk_button_set_label <span class="op">(</span>btn_accept<span class="op">,</span> <span class="st">"Quit"</span><span class="op">);</span></span>
|
||
<span id="cb22-11"><a href="#cb22-11" aria-hidden="true" tabindex="-1"></a> gtk_window_present <span class="op">(</span>GTK_WINDOW <span class="op">(</span>alert<span class="op">));</span></span>
|
||
<span id="cb22-12"><a href="#cb22-12" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> true<span class="op">;</span></span>
|
||
<span id="cb22-13"><a href="#cb22-13" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
|
||
<span id="cb22-14"><a href="#cb22-14" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
|
||
<span id="cb22-15"><a href="#cb22-15" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb22-16"><a href="#cb22-16" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span></span>
|
||
<span id="cb22-17"><a href="#cb22-17" aria-hidden="true" tabindex="-1"></a>close_cb <span class="op">(</span>GtkNotebook <span class="op">*</span>nb<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb22-18"><a href="#cb22-18" aria-hidden="true" tabindex="-1"></a> is_quit <span class="op">=</span> false<span class="op">;</span></span>
|
||
<span id="cb22-19"><a href="#cb22-19" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>has_saved <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">)))</span></span>
|
||
<span id="cb22-20"><a href="#cb22-20" aria-hidden="true" tabindex="-1"></a> notebook_page_close <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">));</span></span>
|
||
<span id="cb22-21"><a href="#cb22-21" aria-hidden="true" tabindex="-1"></a> <span class="cf">else</span> <span class="op">{</span></span>
|
||
<span id="cb22-22"><a href="#cb22-22" aria-hidden="true" tabindex="-1"></a> gtk_label_set_text <span class="op">(</span>lb_alert<span class="op">,</span> <span class="st">"Contents aren't saved yet.</span><span class="sc">\n</span><span class="st">Are you sure to close?"</span><span class="op">);</span></span>
|
||
<span id="cb22-23"><a href="#cb22-23" aria-hidden="true" tabindex="-1"></a> gtk_button_set_label <span class="op">(</span>btn_accept<span class="op">,</span> <span class="st">"Close"</span><span class="op">);</span></span>
|
||
<span id="cb22-24"><a href="#cb22-24" aria-hidden="true" tabindex="-1"></a> gtk_window_present <span class="op">(</span>GTK_WINDOW <span class="op">(</span>alert<span class="op">));</span></span>
|
||
<span id="cb22-25"><a href="#cb22-25" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
|
||
<span id="cb22-26"><a href="#cb22-26" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
|
||
<span id="cb22-27"><a href="#cb22-27" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb22-28"><a href="#cb22-28" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||
<span id="cb22-29"><a href="#cb22-29" aria-hidden="true" tabindex="-1"></a>close_activated <span class="op">(</span>GSimpleAction <span class="op">*</span>action<span class="op">,</span> GVariant <span class="op">*</span>parameter<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb22-30"><a href="#cb22-30" aria-hidden="true" tabindex="-1"></a> GtkNotebook <span class="op">*</span>nb <span class="op">=</span> GTK_NOTEBOOK <span class="op">(</span>user_data<span class="op">);</span></span>
|
||
<span id="cb22-31"><a href="#cb22-31" aria-hidden="true" tabindex="-1"></a> close_cb <span class="op">(</span>nb<span class="op">);</span></span>
|
||
<span id="cb22-32"><a href="#cb22-32" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
|
||
<span id="cb22-33"><a href="#cb22-33" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb22-34"><a href="#cb22-34" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||
<span id="cb22-35"><a href="#cb22-35" aria-hidden="true" tabindex="-1"></a>close_all_activated <span class="op">(</span>GSimpleAction <span class="op">*</span>action<span class="op">,</span> GVariant <span class="op">*</span>parameter<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb22-36"><a href="#cb22-36" aria-hidden="true" tabindex="-1"></a> GtkNotebook <span class="op">*</span>nb <span class="op">=</span> GTK_NOTEBOOK <span class="op">(</span>user_data<span class="op">);</span></span>
|
||
<span id="cb22-37"><a href="#cb22-37" aria-hidden="true" tabindex="-1"></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>nb<span class="op">),</span> GTK_TYPE_WINDOW<span class="op">);</span></span>
|
||
<span id="cb22-38"><a href="#cb22-38" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb22-39"><a href="#cb22-39" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(!</span> win_close_request_cb <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> nb<span class="op">))</span> <span class="co">// checks whether contents are saved</span></span>
|
||
<span id="cb22-40"><a href="#cb22-40" 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="cb22-41"><a href="#cb22-41" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
|
||
<span id="cb22-42"><a href="#cb22-42" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb22-43"><a href="#cb22-43" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span></span>
|
||
<span id="cb22-44"><a href="#cb22-44" aria-hidden="true" tabindex="-1"></a>alert_response_cb <span class="op">(</span>GtkDialog <span class="op">*</span>alert<span class="op">,</span> <span class="dt">int</span> response_id<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb22-45"><a href="#cb22-45" aria-hidden="true" tabindex="-1"></a> GtkNotebook <span class="op">*</span>nb <span class="op">=</span> GTK_NOTEBOOK <span class="op">(</span>user_data<span class="op">);</span></span>
|
||
<span id="cb22-46"><a href="#cb22-46" aria-hidden="true" tabindex="-1"></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>nb<span class="op">),</span> GTK_TYPE_WINDOW<span class="op">);</span></span>
|
||
<span id="cb22-47"><a href="#cb22-47" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb22-48"><a href="#cb22-48" aria-hidden="true" tabindex="-1"></a> gtk_widget_set_visible <span class="op">(</span>GTK_WIDGET <span class="op">(</span>alert<span class="op">),</span> false<span class="op">);</span></span>
|
||
<span id="cb22-49"><a href="#cb22-49" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>response_id <span class="op">==</span> GTK_RESPONSE_ACCEPT<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb22-50"><a href="#cb22-50" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>is_quit<span class="op">)</span></span>
|
||
<span id="cb22-51"><a href="#cb22-51" 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="cb22-52"><a href="#cb22-52" aria-hidden="true" tabindex="-1"></a> <span class="cf">else</span></span>
|
||
<span id="cb22-53"><a href="#cb22-53" aria-hidden="true" tabindex="-1"></a> notebook_page_close <span class="op">(</span>nb<span class="op">);</span></span>
|
||
<span id="cb22-54"><a href="#cb22-54" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
|
||
<span id="cb22-55"><a href="#cb22-55" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
|
||
<span id="cb22-56"><a href="#cb22-56" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb22-57"><a href="#cb22-57" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||
<span id="cb22-58"><a href="#cb22-58" aria-hidden="true" tabindex="-1"></a>app_startup <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb22-59"><a href="#cb22-59" aria-hidden="true" tabindex="-1"></a> <span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb22-60"><a href="#cb22-60" aria-hidden="true" tabindex="-1"></a> build <span class="op">=</span> gtk_builder_new_from_resource <span class="op">(</span><span class="st">"/com/github/ToshioCP/tfe/tfe.ui"</span><span class="op">);</span></span>
|
||
<span id="cb22-61"><a href="#cb22-61" aria-hidden="true" tabindex="-1"></a> win <span class="op">=</span> GTK_APPLICATION_WINDOW <span class="op">(</span>gtk_builder_get_object <span class="op">(</span>build<span class="op">,</span> <span class="st">"win"</span><span class="op">));</span></span>
|
||
<span id="cb22-62"><a href="#cb22-62" aria-hidden="true" tabindex="-1"></a> <span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb22-63"><a href="#cb22-63" aria-hidden="true" tabindex="-1"></a> g_signal_connect <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="st">"close-request"</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>win_close_request_cb<span class="op">),</span> nb<span class="op">);</span></span>
|
||
<span id="cb22-64"><a href="#cb22-64" aria-hidden="true" tabindex="-1"></a> <span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb22-65"><a href="#cb22-65" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
|
||
<p>The static variable <code>is_quit</code> is true when user tries to
|
||
quit the application and false otherwise.</p>
|
||
<ul>
|
||
<li>When a user clicks on the close button, <code>close_cb</code>
|
||
handler is invoked. The handler sets <code>is_quit</code> to false. The
|
||
function <code>has_saved</code> returns true if the current page has
|
||
been saved. If it is true, it calls <code>notebook_page_close</code> to
|
||
close the current page. Otherwise, it shows the alert dialog. The
|
||
response signal of the dialog is connected to the handler
|
||
<code>alert_response_cb</code>. It hides the dialog first. Then checks
|
||
the <code>response_id</code>. If it is <code>GTK_RESPONSE_ACCEPT</code>,
|
||
which means the user has clicked on the close button, then it closes the
|
||
current page. Otherwise it does nothing.</li>
|
||
<li>When a user presses “Ctrl-w”, <code>close_activated</code> handler
|
||
is invoked. It just calls <code>close_cb</code>.</li>
|
||
<li>When a user clicks the close button of the main window,
|
||
“close-request” signal is emitted on the window. The signal has been
|
||
connected to the <code>win_close_request_cb</code> handler in advance.
|
||
The connection is done in the start up handler on the application. The
|
||
<code>win_close_request_cb</code> handler sets <code>is_quit</code> to
|
||
be true. If <code>has_save_all</code> returns true, it returns false,
|
||
which means the signal moves to the default handler and the main window
|
||
will close. Otherwise, It shows the alert dialog and returns true. So,
|
||
the signal stops and the default handler won’t be called. But if the
|
||
user clicked accept button in the alert dialog, the response handler
|
||
<code>alert_response_cb</code> calls <code>gtk_window_destroy</code> and
|
||
the main window will be closed.</li>
|
||
<li>When a user clicked on the quit menu or presses “Ctrl-q”, then
|
||
<code>close_all_activated</code> handler is invoked. It calls
|
||
<code>win_close_request_cb</code>. If the return value is false, it
|
||
destroys the main window. Otherwise it does nothing, but
|
||
<code>win_close_request_cb</code> has shown the alert dialog.</li>
|
||
</ul>
|
||
<h3 id="has_saved-and-has_saved_all-functions">Has_saved and
|
||
has_saved_all functions</h3>
|
||
<p>The two functions are defined in the file <code>tfenotebook.c</code>.
|
||
They are public functions.</p>
|
||
<div class="sourceCode" id="cb23"><pre
|
||
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb23-1"><a href="#cb23-1"></a>gboolean</span>
|
||
<span id="cb23-2"><a href="#cb23-2"></a>has_saved <span class="op">(</span>GtkNotebook <span class="op">*</span>nb<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb23-3"><a href="#cb23-3"></a> g_return_val_if_fail <span class="op">(</span>GTK_IS_NOTEBOOK <span class="op">(</span>nb<span class="op">),</span> false<span class="op">);</span></span>
|
||
<span id="cb23-4"><a href="#cb23-4"></a></span>
|
||
<span id="cb23-5"><a href="#cb23-5"></a> TfeTextView <span class="op">*</span>tv<span class="op">;</span></span>
|
||
<span id="cb23-6"><a href="#cb23-6"></a> GtkTextBuffer <span class="op">*</span>tb<span class="op">;</span></span>
|
||
<span id="cb23-7"><a href="#cb23-7"></a></span>
|
||
<span id="cb23-8"><a href="#cb23-8"></a> tv <span class="op">=</span> get_current_textview <span class="op">(</span>nb<span class="op">);</span></span>
|
||
<span id="cb23-9"><a href="#cb23-9"></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="cb23-10"><a href="#cb23-10"></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="cb23-11"><a href="#cb23-11"></a> <span class="cf">return</span> false<span class="op">;</span></span>
|
||
<span id="cb23-12"><a href="#cb23-12"></a> <span class="cf">else</span></span>
|
||
<span id="cb23-13"><a href="#cb23-13"></a> <span class="cf">return</span> true<span class="op">;</span></span>
|
||
<span id="cb23-14"><a href="#cb23-14"></a><span class="op">}</span></span>
|
||
<span id="cb23-15"><a href="#cb23-15"></a></span>
|
||
<span id="cb23-16"><a href="#cb23-16"></a>gboolean</span>
|
||
<span id="cb23-17"><a href="#cb23-17"></a>has_saved_all <span class="op">(</span>GtkNotebook <span class="op">*</span>nb<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb23-18"><a href="#cb23-18"></a> g_return_val_if_fail <span class="op">(</span>GTK_IS_NOTEBOOK <span class="op">(</span>nb<span class="op">),</span> false<span class="op">);</span></span>
|
||
<span id="cb23-19"><a href="#cb23-19"></a></span>
|
||
<span id="cb23-20"><a href="#cb23-20"></a> <span class="dt">int</span> i<span class="op">,</span> n<span class="op">;</span></span>
|
||
<span id="cb23-21"><a href="#cb23-21"></a> GtkWidget <span class="op">*</span>scr<span class="op">;</span></span>
|
||
<span id="cb23-22"><a href="#cb23-22"></a> GtkWidget <span class="op">*</span>tv<span class="op">;</span></span>
|
||
<span id="cb23-23"><a href="#cb23-23"></a> GtkTextBuffer <span class="op">*</span>tb<span class="op">;</span></span>
|
||
<span id="cb23-24"><a href="#cb23-24"></a></span>
|
||
<span id="cb23-25"><a href="#cb23-25"></a> n <span class="op">=</span> gtk_notebook_get_n_pages <span class="op">(</span>nb<span class="op">);</span></span>
|
||
<span id="cb23-26"><a href="#cb23-26"></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<span class="op">;</span> <span class="op">++</span>i<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb23-27"><a href="#cb23-27"></a> scr <span class="op">=</span> gtk_notebook_get_nth_page <span class="op">(</span>nb<span class="op">,</span> i<span class="op">);</span></span>
|
||
<span id="cb23-28"><a href="#cb23-28"></a> tv <span class="op">=</span> gtk_scrolled_window_get_child <span class="op">(</span>GTK_SCROLLED_WINDOW <span class="op">(</span>scr<span class="op">));</span></span>
|
||
<span id="cb23-29"><a href="#cb23-29"></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="cb23-30"><a href="#cb23-30"></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="cb23-31"><a href="#cb23-31"></a> <span class="cf">return</span> false<span class="op">;</span></span>
|
||
<span id="cb23-32"><a href="#cb23-32"></a> <span class="op">}</span></span>
|
||
<span id="cb23-33"><a href="#cb23-33"></a> <span class="cf">return</span> true<span class="op">;</span></span>
|
||
<span id="cb23-34"><a href="#cb23-34"></a><span class="op">}</span></span></code></pre></div>
|
||
<ul>
|
||
<li>1-14: <code>has_saved</code> function.</li>
|
||
<li>10: The function <code>gtk_text_buffer_get_modified</code> returns
|
||
true if the content of the buffer has been modified since the modified
|
||
flag had set false. The flag is set to false when:
|
||
<ul>
|
||
<li>the buffer is created.</li>
|
||
<li>the contents of the buffer is replaced</li>
|
||
<li>the contents of the buffer is saved to a file.</li>
|
||
</ul></li>
|
||
<li>10-13: This function returns true if the contents of the current
|
||
page has been saved and no modification has been made. It returns false,
|
||
if the current page has been modified and hasn’t been saved.</li>
|
||
<li>16-34: <code>has_saved_all</code> function. This function is similar
|
||
to <code>has_saved</code> function. It returns true if all the pages
|
||
have been saved. It returns false if at least one page has been modified
|
||
since it last had been saved.</li>
|
||
</ul>
|
||
<h2 id="notebook-page-tab">Notebook page tab</h2>
|
||
<p>If you have some pages and edit them together, you might be confused
|
||
which file needs to be saved. Common file editors changes the tab when
|
||
the contents are modified. GtkTextBuffer provides “modified-changed”
|
||
signal to notify the modification.</p>
|
||
<div class="sourceCode" id="cb24"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb24-1"><a href="#cb24-1" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||
<span id="cb24-2"><a href="#cb24-2" aria-hidden="true" tabindex="-1"></a>notebook_page_build <span class="op">(</span>GtkNotebook <span class="op">*</span>nb<span class="op">,</span> GtkWidget <span class="op">*</span>tv<span class="op">,</span> <span class="dt">char</span> <span class="op">*</span>filename<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb24-3"><a href="#cb24-3" aria-hidden="true" tabindex="-1"></a> <span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb24-4"><a href="#cb24-4" aria-hidden="true" tabindex="-1"></a> g_signal_connect <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> <span class="st">"change-file"</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>file_changed_cb<span class="op">),</span> NULL<span class="op">);</span></span>
|
||
<span id="cb24-5"><a href="#cb24-5" aria-hidden="true" tabindex="-1"></a> g_signal_connect <span class="op">(</span>tb<span class="op">,</span> <span class="st">"modified-changed"</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>modified_changed_cb<span class="op">),</span> tv<span class="op">);</span></span>
|
||
<span id="cb24-6"><a href="#cb24-6" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
|
||
<p>When a page is built, connect “change-file” and “modified-changed”
|
||
signals to <code>file_changed_cb</code> and
|
||
<code>modified_changed_cb</code> handlers respectively.</p>
|
||
<div class="sourceCode" id="cb25"><pre
|
||
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb25-1"><a href="#cb25-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||
<span id="cb25-2"><a href="#cb25-2"></a>file_changed_cb <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb25-3"><a href="#cb25-3"></a> GtkWidget <span class="op">*</span>nb <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_NOTEBOOK<span class="op">);</span></span>
|
||
<span id="cb25-4"><a href="#cb25-4"></a> GtkWidget <span class="op">*</span>scr<span class="op">;</span></span>
|
||
<span id="cb25-5"><a href="#cb25-5"></a> GtkWidget <span class="op">*</span>label<span class="op">;</span></span>
|
||
<span id="cb25-6"><a href="#cb25-6"></a> GFile <span class="op">*</span>file<span class="op">;</span></span>
|
||
<span id="cb25-7"><a href="#cb25-7"></a> <span class="dt">char</span> <span class="op">*</span>filename<span class="op">;</span></span>
|
||
<span id="cb25-8"><a href="#cb25-8"></a></span>
|
||
<span id="cb25-9"><a href="#cb25-9"></a> <span class="cf">if</span> <span class="op">(!</span> GTK_IS_NOTEBOOK <span class="op">(</span>nb<span class="op">))</span> <span class="co">/* tv not connected to nb yet */</span></span>
|
||
<span id="cb25-10"><a href="#cb25-10"></a> <span class="cf">return</span><span class="op">;</span></span>
|
||
<span id="cb25-11"><a href="#cb25-11"></a> file <span class="op">=</span> tfe_text_view_get_file <span class="op">(</span>tv<span class="op">);</span></span>
|
||
<span id="cb25-12"><a href="#cb25-12"></a> scr <span class="op">=</span> gtk_widget_get_parent <span class="op">(</span>GTK_WIDGET <span class="op">(</span>tv<span class="op">));</span></span>
|
||
<span id="cb25-13"><a href="#cb25-13"></a> <span class="cf">if</span> <span class="op">(</span>G_IS_FILE <span class="op">(</span>file<span class="op">))</span> <span class="op">{</span></span>
|
||
<span id="cb25-14"><a href="#cb25-14"></a> filename <span class="op">=</span> g_file_get_basename <span class="op">(</span>file<span class="op">);</span></span>
|
||
<span id="cb25-15"><a href="#cb25-15"></a> g_object_unref <span class="op">(</span>file<span class="op">);</span></span>
|
||
<span id="cb25-16"><a href="#cb25-16"></a> <span class="op">}</span> <span class="cf">else</span></span>
|
||
<span id="cb25-17"><a href="#cb25-17"></a> filename <span class="op">=</span> get_untitled <span class="op">();</span></span>
|
||
<span id="cb25-18"><a href="#cb25-18"></a> label <span class="op">=</span> gtk_label_new <span class="op">(</span>filename<span class="op">);</span></span>
|
||
<span id="cb25-19"><a href="#cb25-19"></a> gtk_notebook_set_tab_label <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">),</span> scr<span class="op">,</span> label<span class="op">);</span></span>
|
||
<span id="cb25-20"><a href="#cb25-20"></a><span class="op">}</span></span>
|
||
<span id="cb25-21"><a href="#cb25-21"></a></span>
|
||
<span id="cb25-22"><a href="#cb25-22"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||
<span id="cb25-23"><a href="#cb25-23"></a>modified_changed_cb <span class="op">(</span>GtkTextBuffer <span class="op">*</span>tb<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb25-24"><a href="#cb25-24"></a> TfeTextView <span class="op">*</span>tv <span class="op">=</span> TFE_TEXT_VIEW <span class="op">(</span>user_data<span class="op">);</span></span>
|
||
<span id="cb25-25"><a href="#cb25-25"></a> GtkWidget <span class="op">*</span>scr <span class="op">=</span> gtk_widget_get_parent <span class="op">(</span>GTK_WIDGET <span class="op">(</span>tv<span class="op">));</span></span>
|
||
<span id="cb25-26"><a href="#cb25-26"></a> GtkWidget <span class="op">*</span>nb <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_NOTEBOOK<span class="op">);</span></span>
|
||
<span id="cb25-27"><a href="#cb25-27"></a> GtkWidget <span class="op">*</span>label<span class="op">;</span></span>
|
||
<span id="cb25-28"><a href="#cb25-28"></a> <span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span>filename<span class="op">;</span></span>
|
||
<span id="cb25-29"><a href="#cb25-29"></a> <span class="dt">char</span> <span class="op">*</span>text<span class="op">;</span></span>
|
||
<span id="cb25-30"><a href="#cb25-30"></a></span>
|
||
<span id="cb25-31"><a href="#cb25-31"></a> <span class="cf">if</span> <span class="op">(!</span> GTK_IS_NOTEBOOK <span class="op">(</span>nb<span class="op">))</span> <span class="co">/* tv not connected to nb yet */</span></span>
|
||
<span id="cb25-32"><a href="#cb25-32"></a> <span class="cf">return</span><span class="op">;</span></span>
|
||
<span id="cb25-33"><a href="#cb25-33"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>gtk_text_buffer_get_modified <span class="op">(</span>tb<span class="op">))</span> <span class="op">{</span></span>
|
||
<span id="cb25-34"><a href="#cb25-34"></a> filename <span class="op">=</span> gtk_notebook_get_tab_label_text <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">),</span> scr<span class="op">);</span></span>
|
||
<span id="cb25-35"><a href="#cb25-35"></a> text <span class="op">=</span> g_strdup_printf <span class="op">(</span><span class="st">"*%s"</span><span class="op">,</span> filename<span class="op">);</span></span>
|
||
<span id="cb25-36"><a href="#cb25-36"></a> label <span class="op">=</span> gtk_label_new <span class="op">(</span>text<span class="op">);</span></span>
|
||
<span id="cb25-37"><a href="#cb25-37"></a> g_free <span class="op">(</span>text<span class="op">);</span></span>
|
||
<span id="cb25-38"><a href="#cb25-38"></a> gtk_notebook_set_tab_label <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">),</span> scr<span class="op">,</span> label<span class="op">);</span></span>
|
||
<span id="cb25-39"><a href="#cb25-39"></a> <span class="op">}</span> <span class="cf">else</span></span>
|
||
<span id="cb25-40"><a href="#cb25-40"></a> file_changed_cb <span class="op">(</span>tv<span class="op">);</span></span>
|
||
<span id="cb25-41"><a href="#cb25-41"></a><span class="op">}</span></span></code></pre></div>
|
||
<p>The <code>file_changed_cb</code> handler gives a new file name to the
|
||
notebook page tag. The <code>modified_changed_cb</code> handler inserts
|
||
an asterisk at the beginning of the filename. It is a sign that
|
||
indicates the file has been modified but not saved yet.</p>
|
||
<ul>
|
||
<li>1-20: <code>file_changed_cb</code> handler.</li>
|
||
<li>9-10: If the signal emits during the page is being built, it is
|
||
possible that <code>tv</code> isn’t a descendant of <code>nb</code>.
|
||
That is to say, there’s no page corresponds to <code>tv</code>. Then, it
|
||
is unnecessary to change the name of the tab because no tab exists.</li>
|
||
<li>13-15: If <code>file</code> is GFile, then it gets the filename and
|
||
release the reference to <code>file</code>.</li>
|
||
<li>16-17: Otherwise, it assigns “Untitled” (+ a number) to
|
||
<code>filename</code></li>
|
||
<li>18-19: Creates GtkLabel with <code>filename</code> and sets the tab
|
||
of the page with the GtkLabel.</li>
|
||
<li>22-41: <code>modified_changed_cb</code> handler.</li>
|
||
<li>31-32: If <code>tv</code> isn’t a descendant of <code>nb</code>,
|
||
then nothing needs to be done.</li>
|
||
<li>33-35: If the content is modified, then it gets the text of the tab
|
||
and adds asterisk at the beginning of the text.</li>
|
||
<li>36-38: Sets the tab with the filename with the asterisk</li>
|
||
<li>39-40: Otherwise it calls <code>file_changed_cb</code> and updates
|
||
the filename (without an asterisk).</li>
|
||
</ul>
|
||
<h2 id="font">Font</h2>
|
||
<h3 id="gtkfontbutton-and-gtkfontchooser">GtkFontButton and
|
||
GtkFontChooser</h3>
|
||
<p>GtkFontButton is a button class which displays the current font and a
|
||
user can change the font with the button. It opens a font chooser dialog
|
||
if a user clicked on the button. A user can change the font (family,
|
||
style, weight and size) with the dialog. Then the button keeps the new
|
||
font and displays it.</p>
|
||
<p>The button is set with a builder in the application startup process.
|
||
And the signal “font-set” is connected to the handler
|
||
<code>font_set_cb</code>. The signal “font-set” is emitted when the user
|
||
selects a font.</p>
|
||
<div class="sourceCode" id="cb26"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb26-1"><a href="#cb26-1" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||
<span id="cb26-2"><a href="#cb26-2" aria-hidden="true" tabindex="-1"></a>font_set_cb <span class="op">(</span>GtkFontButton <span class="op">*</span>fontbtn<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb26-3"><a href="#cb26-3" aria-hidden="true" tabindex="-1"></a> PangoFontDescription <span class="op">*</span>pango_font_desc<span class="op">;</span></span>
|
||
<span id="cb26-4"><a href="#cb26-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">char</span> <span class="op">*</span>s<span class="op">,</span> <span class="op">*</span>css<span class="op">;</span></span>
|
||
<span id="cb26-5"><a href="#cb26-5" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb26-6"><a href="#cb26-6" aria-hidden="true" tabindex="-1"></a> pango_font_desc <span class="op">=</span> gtk_font_chooser_get_font_desc <span class="op">(</span>GTK_FONT_CHOOSER <span class="op">(</span>fontbtn<span class="op">));</span></span>
|
||
<span id="cb26-7"><a href="#cb26-7" aria-hidden="true" tabindex="-1"></a> s <span class="op">=</span> pfd2css <span class="op">(</span>pango_font_desc<span class="op">);</span> <span class="co">// converts Pango Font Description into CSS style string</span></span>
|
||
<span id="cb26-8"><a href="#cb26-8" aria-hidden="true" tabindex="-1"></a> css <span class="op">=</span> g_strdup_printf <span class="op">(</span><span class="st">"textview {%s}"</span><span class="op">,</span> s<span class="op">);</span></span>
|
||
<span id="cb26-9"><a href="#cb26-9" aria-hidden="true" tabindex="-1"></a> gtk_css_provider_load_from_data <span class="op">(</span>provider<span class="op">,</span> css<span class="op">,</span> <span class="op">-</span><span class="dv">1</span><span class="op">);</span></span>
|
||
<span id="cb26-10"><a href="#cb26-10" aria-hidden="true" tabindex="-1"></a> g_free <span class="op">(</span>s<span class="op">);</span></span>
|
||
<span id="cb26-11"><a href="#cb26-11" aria-hidden="true" tabindex="-1"></a> g_free <span class="op">(</span>css<span class="op">);</span></span>
|
||
<span id="cb26-12"><a href="#cb26-12" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
|
||
<span id="cb26-13"><a href="#cb26-13" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb26-14"><a href="#cb26-14" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||
<span id="cb26-15"><a href="#cb26-15" aria-hidden="true" tabindex="-1"></a>app_startup <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb26-16"><a href="#cb26-16" aria-hidden="true" tabindex="-1"></a> <span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb26-17"><a href="#cb26-17" aria-hidden="true" tabindex="-1"></a> fontbtn <span class="op">=</span> GTK_FONT_BUTTON <span class="op">(</span>gtk_builder_get_object <span class="op">(</span>build<span class="op">,</span> <span class="st">"fontbtn"</span><span class="op">));</span></span>
|
||
<span id="cb26-18"><a href="#cb26-18" aria-hidden="true" tabindex="-1"></a> <span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb26-19"><a href="#cb26-19" aria-hidden="true" tabindex="-1"></a> g_signal_connect <span class="op">(</span>fontbtn<span class="op">,</span> <span class="st">"font-set"</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>font_set_cb<span class="op">),</span> NULL<span class="op">);</span></span>
|
||
<span id="cb26-20"><a href="#cb26-20" aria-hidden="true" tabindex="-1"></a> <span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb26-21"><a href="#cb26-21" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
|
||
<p>GtkFontChooser is an interface implemented by GtkFontButton. The
|
||
function <code>gtk_font_chooser_get_font_desc</code> gets the
|
||
PangoFontDescription of the currently selected font.
|
||
PangoFontDescription includes font family, style, weight and size in it.
|
||
The function <code>pfd2css</code> converts them to CSS style string. The
|
||
following shows the conversion.</p>
|
||
<pre><code>PangoFontDescription:
|
||
font-family: Monospace
|
||
font-style: normal
|
||
font-weight: normal
|
||
font-size: 12pt
|
||
=>
|
||
"font-family: Monospace; font-style: normal; font-weight: 400; font-size: 12pt;"</code></pre>
|
||
<p>Then, <code>font_set_cb</code> creates a CSS string and put it into
|
||
the <code>provider</code> instance. The provider has been added to the
|
||
default display in advance. So, the handler effects the font for the
|
||
contents of the textview immediately.</p>
|
||
<h3 id="css-and-pango">CSS and Pango</h3>
|
||
<p>Convertors from PangoFontDescription to CSS are packed in
|
||
<code>pfd2css.c</code>. The filename means:</p>
|
||
<ul>
|
||
<li>pfd => PangoFontDescripter</li>
|
||
<li>2 => to</li>
|
||
<li>css => CSS (Cascade Style Sheet)</li>
|
||
</ul>
|
||
<p>All the public functions in the file have “pdf2css” prefix.</p>
|
||
<div class="sourceCode" id="cb28"><pre
|
||
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb28-1"><a href="#cb28-1"></a><span class="pp">#include </span><span class="im"><pango/pango.h></span></span>
|
||
<span id="cb28-2"><a href="#cb28-2"></a><span class="pp">#include </span><span class="im">"pfd2css.h"</span></span>
|
||
<span id="cb28-3"><a href="#cb28-3"></a></span>
|
||
<span id="cb28-4"><a href="#cb28-4"></a><span class="co">// Pango font description to CSS style string</span></span>
|
||
<span id="cb28-5"><a href="#cb28-5"></a><span class="co">// Returned string is owned by caller. The caller should free it when it is useless.</span></span>
|
||
<span id="cb28-6"><a href="#cb28-6"></a></span>
|
||
<span id="cb28-7"><a href="#cb28-7"></a><span class="dt">char</span><span class="op">*</span></span>
|
||
<span id="cb28-8"><a href="#cb28-8"></a>pfd2css <span class="op">(</span>PangoFontDescription <span class="op">*</span>pango_font_desc<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb28-9"><a href="#cb28-9"></a> <span class="dt">char</span> <span class="op">*</span>fontsize<span class="op">;</span></span>
|
||
<span id="cb28-10"><a href="#cb28-10"></a></span>
|
||
<span id="cb28-11"><a href="#cb28-11"></a> fontsize <span class="op">=</span> pfd2css_size <span class="op">(</span>pango_font_desc<span class="op">);</span></span>
|
||
<span id="cb28-12"><a href="#cb28-12"></a> <span class="cf">return</span> g_strdup_printf <span class="op">(</span><span class="st">"font-family: </span><span class="sc">\"</span><span class="st">%s</span><span class="sc">\"</span><span class="st">; font-style: %s; font-weight: %d; font-size: %s;"</span><span class="op">,</span></span>
|
||
<span id="cb28-13"><a href="#cb28-13"></a> pfd2css_family <span class="op">(</span>pango_font_desc<span class="op">),</span> pfd2css_style <span class="op">(</span>pango_font_desc<span class="op">),</span></span>
|
||
<span id="cb28-14"><a href="#cb28-14"></a> pfd2css_weight <span class="op">(</span>pango_font_desc<span class="op">),</span> fontsize<span class="op">);</span></span>
|
||
<span id="cb28-15"><a href="#cb28-15"></a> g_free <span class="op">(</span>fontsize<span class="op">);</span> </span>
|
||
<span id="cb28-16"><a href="#cb28-16"></a><span class="op">}</span></span>
|
||
<span id="cb28-17"><a href="#cb28-17"></a></span>
|
||
<span id="cb28-18"><a href="#cb28-18"></a><span class="co">// Each element (family, style, weight and size)</span></span>
|
||
<span id="cb28-19"><a href="#cb28-19"></a></span>
|
||
<span id="cb28-20"><a href="#cb28-20"></a><span class="dt">const</span> <span class="dt">char</span><span class="op">*</span></span>
|
||
<span id="cb28-21"><a href="#cb28-21"></a>pfd2css_family <span class="op">(</span>PangoFontDescription <span class="op">*</span>pango_font_desc<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb28-22"><a href="#cb28-22"></a> <span class="cf">return</span> pango_font_description_get_family <span class="op">(</span>pango_font_desc<span class="op">);</span></span>
|
||
<span id="cb28-23"><a href="#cb28-23"></a><span class="op">}</span></span>
|
||
<span id="cb28-24"><a href="#cb28-24"></a></span>
|
||
<span id="cb28-25"><a href="#cb28-25"></a><span class="dt">const</span> <span class="dt">char</span><span class="op">*</span></span>
|
||
<span id="cb28-26"><a href="#cb28-26"></a>pfd2css_style <span class="op">(</span>PangoFontDescription <span class="op">*</span>pango_font_desc<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb28-27"><a href="#cb28-27"></a> PangoStyle pango_style <span class="op">=</span> pango_font_description_get_style <span class="op">(</span>pango_font_desc<span class="op">);</span></span>
|
||
<span id="cb28-28"><a href="#cb28-28"></a> <span class="cf">switch</span> <span class="op">(</span>pango_style<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb28-29"><a href="#cb28-29"></a> <span class="cf">case</span> PANGO_STYLE_NORMAL<span class="op">:</span></span>
|
||
<span id="cb28-30"><a href="#cb28-30"></a> <span class="cf">return</span> <span class="st">"normal"</span><span class="op">;</span></span>
|
||
<span id="cb28-31"><a href="#cb28-31"></a> <span class="cf">case</span> PANGO_STYLE_ITALIC<span class="op">:</span></span>
|
||
<span id="cb28-32"><a href="#cb28-32"></a> <span class="cf">return</span> <span class="st">"italic"</span><span class="op">;</span></span>
|
||
<span id="cb28-33"><a href="#cb28-33"></a> <span class="cf">case</span> PANGO_STYLE_OBLIQUE<span class="op">:</span></span>
|
||
<span id="cb28-34"><a href="#cb28-34"></a> <span class="cf">return</span> <span class="st">"oblique"</span><span class="op">;</span></span>
|
||
<span id="cb28-35"><a href="#cb28-35"></a> <span class="cf">default</span><span class="op">:</span></span>
|
||
<span id="cb28-36"><a href="#cb28-36"></a> <span class="cf">return</span> <span class="st">"normal"</span><span class="op">;</span></span>
|
||
<span id="cb28-37"><a href="#cb28-37"></a> <span class="op">}</span></span>
|
||
<span id="cb28-38"><a href="#cb28-38"></a><span class="op">}</span></span>
|
||
<span id="cb28-39"><a href="#cb28-39"></a></span>
|
||
<span id="cb28-40"><a href="#cb28-40"></a><span class="dt">int</span></span>
|
||
<span id="cb28-41"><a href="#cb28-41"></a>pfd2css_weight <span class="op">(</span>PangoFontDescription <span class="op">*</span>pango_font_desc<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb28-42"><a href="#cb28-42"></a> PangoWeight pango_weight <span class="op">=</span> pango_font_description_get_weight <span class="op">(</span>pango_font_desc<span class="op">);</span></span>
|
||
<span id="cb28-43"><a href="#cb28-43"></a> <span class="cf">switch</span> <span class="op">(</span>pango_weight<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb28-44"><a href="#cb28-44"></a> <span class="cf">case</span> PANGO_WEIGHT_THIN<span class="op">:</span></span>
|
||
<span id="cb28-45"><a href="#cb28-45"></a> <span class="cf">return</span> <span class="dv">100</span><span class="op">;</span></span>
|
||
<span id="cb28-46"><a href="#cb28-46"></a> <span class="cf">case</span> PANGO_WEIGHT_ULTRALIGHT<span class="op">:</span></span>
|
||
<span id="cb28-47"><a href="#cb28-47"></a> <span class="cf">return</span> <span class="dv">200</span><span class="op">;</span></span>
|
||
<span id="cb28-48"><a href="#cb28-48"></a> <span class="cf">case</span> PANGO_WEIGHT_LIGHT<span class="op">:</span></span>
|
||
<span id="cb28-49"><a href="#cb28-49"></a> <span class="cf">return</span> <span class="dv">300</span><span class="op">;</span></span>
|
||
<span id="cb28-50"><a href="#cb28-50"></a> <span class="cf">case</span> PANGO_WEIGHT_SEMILIGHT<span class="op">:</span></span>
|
||
<span id="cb28-51"><a href="#cb28-51"></a> <span class="cf">return</span> <span class="dv">350</span><span class="op">;</span></span>
|
||
<span id="cb28-52"><a href="#cb28-52"></a> <span class="cf">case</span> PANGO_WEIGHT_BOOK<span class="op">:</span></span>
|
||
<span id="cb28-53"><a href="#cb28-53"></a> <span class="cf">return</span> <span class="dv">380</span><span class="op">;</span></span>
|
||
<span id="cb28-54"><a href="#cb28-54"></a> <span class="cf">case</span> PANGO_WEIGHT_NORMAL<span class="op">:</span></span>
|
||
<span id="cb28-55"><a href="#cb28-55"></a> <span class="cf">return</span> <span class="dv">400</span><span class="op">;</span> <span class="co">/* or "normal" */</span></span>
|
||
<span id="cb28-56"><a href="#cb28-56"></a> <span class="cf">case</span> PANGO_WEIGHT_MEDIUM<span class="op">:</span></span>
|
||
<span id="cb28-57"><a href="#cb28-57"></a> <span class="cf">return</span> <span class="dv">500</span><span class="op">;</span></span>
|
||
<span id="cb28-58"><a href="#cb28-58"></a> <span class="cf">case</span> PANGO_WEIGHT_SEMIBOLD<span class="op">:</span></span>
|
||
<span id="cb28-59"><a href="#cb28-59"></a> <span class="cf">return</span> <span class="dv">600</span><span class="op">;</span></span>
|
||
<span id="cb28-60"><a href="#cb28-60"></a> <span class="cf">case</span> PANGO_WEIGHT_BOLD<span class="op">:</span></span>
|
||
<span id="cb28-61"><a href="#cb28-61"></a> <span class="cf">return</span> <span class="dv">700</span><span class="op">;</span> <span class="co">/* or "bold" */</span></span>
|
||
<span id="cb28-62"><a href="#cb28-62"></a> <span class="cf">case</span> PANGO_WEIGHT_ULTRABOLD<span class="op">:</span></span>
|
||
<span id="cb28-63"><a href="#cb28-63"></a> <span class="cf">return</span> <span class="dv">800</span><span class="op">;</span></span>
|
||
<span id="cb28-64"><a href="#cb28-64"></a> <span class="cf">case</span> PANGO_WEIGHT_HEAVY<span class="op">:</span></span>
|
||
<span id="cb28-65"><a href="#cb28-65"></a> <span class="cf">return</span> <span class="dv">900</span><span class="op">;</span></span>
|
||
<span id="cb28-66"><a href="#cb28-66"></a> <span class="cf">case</span> PANGO_WEIGHT_ULTRAHEAVY<span class="op">:</span></span>
|
||
<span id="cb28-67"><a href="#cb28-67"></a> <span class="cf">return</span> <span class="dv">900</span><span class="op">;</span> <span class="co">/* In PangoWeight definition, the weight is 1000. But CSS allows the weight below 900. */</span></span>
|
||
<span id="cb28-68"><a href="#cb28-68"></a> <span class="cf">default</span><span class="op">:</span></span>
|
||
<span id="cb28-69"><a href="#cb28-69"></a> <span class="cf">return</span> <span class="dv">400</span><span class="op">;</span> <span class="co">/* "normal" */</span></span>
|
||
<span id="cb28-70"><a href="#cb28-70"></a> <span class="op">}</span></span>
|
||
<span id="cb28-71"><a href="#cb28-71"></a><span class="op">}</span></span>
|
||
<span id="cb28-72"><a href="#cb28-72"></a></span>
|
||
<span id="cb28-73"><a href="#cb28-73"></a><span class="dt">char</span> <span class="op">*</span></span>
|
||
<span id="cb28-74"><a href="#cb28-74"></a>pfd2css_size <span class="op">(</span>PangoFontDescription <span class="op">*</span>pango_font_desc<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb28-75"><a href="#cb28-75"></a> <span class="cf">if</span> <span class="op">(</span>pango_font_description_get_size_is_absolute <span class="op">(</span>pango_font_desc<span class="op">))</span></span>
|
||
<span id="cb28-76"><a href="#cb28-76"></a> <span class="cf">return</span> g_strdup_printf <span class="op">(</span><span class="st">"%dpx"</span><span class="op">,</span> pango_font_description_get_size <span class="op">(</span>pango_font_desc<span class="op">)</span> <span class="op">/</span> PANGO_SCALE<span class="op">);</span></span>
|
||
<span id="cb28-77"><a href="#cb28-77"></a> <span class="cf">else</span></span>
|
||
<span id="cb28-78"><a href="#cb28-78"></a> <span class="cf">return</span> g_strdup_printf <span class="op">(</span><span class="st">"%dpt"</span><span class="op">,</span> pango_font_description_get_size <span class="op">(</span>pango_font_desc<span class="op">)</span> <span class="op">/</span> PANGO_SCALE<span class="op">);</span></span>
|
||
<span id="cb28-79"><a href="#cb28-79"></a><span class="op">}</span></span></code></pre></div>
|
||
<ul>
|
||
<li>1: Public functions, constants and structures for Pango is defined
|
||
in <code>pango/pango.h</code>.</li>
|
||
<li>2: Including <code>pdf2css.h</code> makes it possible to call public
|
||
functions anywhere in the file <code>pdf2css.c</code>. Because the
|
||
header file includes declarations of all the public functions.</li>
|
||
<li>7-16: <code>pdf2css</code> function. This function gets font family,
|
||
style, weight and size from a PangoFontDescription instance given as an
|
||
argument. And it builds them to a string. The returned string is owned
|
||
by caller. The caller should free the string when it is useless.</li>
|
||
<li>20-23: <code>pfd2css_famili</code> function. This function gets
|
||
font-family string from a PangoFontDescription instance. The string is
|
||
owned by the PFD instance so caller can’t modify or free the
|
||
string.</li>
|
||
<li>25-38: <code>pdf2css_style</code> function. This function gets
|
||
font-style string from a PangoFontDescription instance. The string is
|
||
static and caller can’t modify or free it.</li>
|
||
<li>40-71: <code>pfd2css_weight</code> function. This function gets
|
||
font-weight integer value from a PangoFontDescription instance. The
|
||
value is in between 100 to 900. It is defined in CSS Fonts Module Level
|
||
3 specification.
|
||
<ul>
|
||
<li>100 - Thin</li>
|
||
<li>200 - Extra Light (Ultra Light)</li>
|
||
<li>300 - Light</li>
|
||
<li>400 - Normal</li>
|
||
<li>500 - Medium</li>
|
||
<li>600 - Semi Bold (Demi Bold)</li>
|
||
<li>700 - Bold</li>
|
||
<li>800 - Extra Bold (Ultra Bold)</li>
|
||
<li>900 - Black (Heavy)</li>
|
||
</ul></li>
|
||
<li>73-79: <code>pdf2css_size</code> function. This function gets
|
||
font-size string from a PangoFontDescription instance. The string is
|
||
owned by caller, so the caller should free it when it is useless.
|
||
PangoFontDescription has absolute or non-absolute size.
|
||
<ul>
|
||
<li>If it is absolute, the size is in device units.</li>
|
||
<li>If it is non-absolute, the size is in points.</li>
|
||
</ul></li>
|
||
<li>The definition of device units is dependent on the output device. It
|
||
will typically be pixels for a screen, and points for a printer.</li>
|
||
<li>Pango holds the size as its own dimensions. The constant
|
||
<code>PANGO_SCALE</code> is the scale between dimensions used for Pango
|
||
distances and device units. <code>PANGO_SCALE</code> is currently 1024,
|
||
but this may be changed in the future. When setting font sizes, device
|
||
units are always considered to be points rather than pixels. If the font
|
||
size is 12pt, the size in pango is
|
||
<code>12*PANGO_SCALE=12*1024=12288</code>.</li>
|
||
</ul>
|
||
<p>For further information, see <a
|
||
href="https://docs.gtk.org/Pango/">Pango API Reference</a>.</p>
|
||
<h2 id="gsettings">GSettings</h2>
|
||
<p>We want to maintain the font data after the application quits. There
|
||
are some ways to implement it.</p>
|
||
<ul>
|
||
<li>Make a configuration file. For example, a text file
|
||
“~/.config/tfe/font.cfg” keeps font information.</li>
|
||
<li>Use GSettings object. The basic idea of GSettings are similar to
|
||
configuration file. Configuration information data is put into a
|
||
database file.</li>
|
||
</ul>
|
||
<p>GSettings is simple and easy to use but a bit hard to understand the
|
||
concept. This subsection describes the concept first and then how to
|
||
program it.</p>
|
||
<h3 id="gsettings-schema">GSettings schema</h3>
|
||
<p>GSettings schema describes a set of keys, value types and some other
|
||
information. GSettings object uses this schema and it writes/reads the
|
||
value of a key to/from the right place in the database.</p>
|
||
<ul>
|
||
<li>A schema has an id. The id must be unique. We often use the same
|
||
string as application id, but schema id and application id are
|
||
different. You can use different name from application id. Schema id is
|
||
a string delimited by periods. For example, “com.github.ToshioCP.tfe” is
|
||
a correct schema id.</li>
|
||
<li>A schema usually has a path. The path is a location in the database.
|
||
Each key is stored under the path. For example, if a key
|
||
<code>font</code> is defined with a path
|
||
<code>/com/github/ToshioCP/tfe/</code>, the key’s location in the
|
||
database is <code>/com/github/ToshioCP/tfe/font</code>. Path is a string
|
||
begins with and ends with a slash (<code>/</code>). And it is delimited
|
||
by slashes.</li>
|
||
<li>GSettings save information as key-value style. Key is a string
|
||
begins with lower case characters followed by lower case, digit or dash
|
||
(<code>-</code>) and ends with lower case or digit. No consecutive
|
||
dashes are allowed. Values can be any type. GSettings stores values as
|
||
GVariant type, which can be, for example, integer, double, boolean,
|
||
string or complex types like an array. The type of values needs to be
|
||
defined in the schema.</li>
|
||
<li>A default value needs to be set for each key.</li>
|
||
<li>A summery and description can be set for each key optionally.</li>
|
||
</ul>
|
||
<p>Schemas are described in an XML format. For example,</p>
|
||
<div class="sourceCode" id="cb29"><pre
|
||
class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><span id="cb29-1"><a href="#cb29-1"></a><span class="fu"><?xml</span><span class="ot"> version=</span><span class="st">"1.0"</span><span class="ot"> encoding=</span><span class="st">"UTF-8"</span><span class="fu">?></span></span>
|
||
<span id="cb29-2"><a href="#cb29-2"></a><<span class="kw">schemalist</span>></span>
|
||
<span id="cb29-3"><a href="#cb29-3"></a> <<span class="kw">schema</span><span class="ot"> path=</span><span class="st">"/com/github/ToshioCP/tfe/"</span><span class="ot"> id=</span><span class="st">"com.github.ToshioCP.tfe"</span>></span>
|
||
<span id="cb29-4"><a href="#cb29-4"></a> <<span class="kw">key</span><span class="ot"> name=</span><span class="st">"font"</span><span class="ot"> type=</span><span class="st">"s"</span>></span>
|
||
<span id="cb29-5"><a href="#cb29-5"></a> <<span class="kw">default</span>>'Monospace 12'</<span class="kw">default</span>></span>
|
||
<span id="cb29-6"><a href="#cb29-6"></a> <<span class="kw">summary</span>>Font</<span class="kw">summary</span>></span>
|
||
<span id="cb29-7"><a href="#cb29-7"></a> <<span class="kw">description</span>>A font to be used for textview.</<span class="kw">description</span>></span>
|
||
<span id="cb29-8"><a href="#cb29-8"></a> </<span class="kw">key</span>></span>
|
||
<span id="cb29-9"><a href="#cb29-9"></a> </<span class="kw">schema</span>></span>
|
||
<span id="cb29-10"><a href="#cb29-10"></a></<span class="kw">schemalist</span>></span></code></pre></div>
|
||
<ul>
|
||
<li>4: The type attribute is “s”. It is GVariant type string. For
|
||
GVariant type string, see <a
|
||
href="https://docs.gtk.org/glib/struct.VariantType.html#gvariant-type-strings">GLib
|
||
API Reference – GVariant Type Strings</a>. Other common types are:
|
||
<ul>
|
||
<li>“b”: gboolean</li>
|
||
<li>“i”: gint32.</li>
|
||
<li>“d”: double.</li>
|
||
</ul></li>
|
||
</ul>
|
||
<p>Further information is in:</p>
|
||
<ul>
|
||
<li><a
|
||
href="https://docs.gtk.org/glib/gvariant-format-strings.html">GLib API
|
||
Reference – GVariant Format Strings</a></li>
|
||
<li><a href="https://docs.gtk.org/glib/gvariant-text.html">GLib API
|
||
Reference – GVariant Text Format</a></li>
|
||
<li><a href="https://docs.gtk.org/glib/struct.Variant.html">GLib API
|
||
Reference – GVariant</a></li>
|
||
<li><a href="https://docs.gtk.org/glib/struct.VariantType.html">GLib API
|
||
Reference – VariantType</a></li>
|
||
</ul>
|
||
<h3 id="gsettings-1">gsettings</h3>
|
||
<p>First, let’s try <code>gsettings</code> application. It is a
|
||
configuration tool for GSettings.</p>
|
||
<pre><code>$ gsettings help
|
||
Usage:
|
||
gsettings --version
|
||
gsettings [--schemadir SCHEMADIR] COMMAND [ARGS?]
|
||
|
||
Commands:
|
||
help Show this information
|
||
list-schemas List installed schemas
|
||
list-relocatable-schemas List relocatable schemas
|
||
list-keys List keys in a schema
|
||
list-children List children of a schema
|
||
list-recursively List keys and values, recursively
|
||
range Queries the range of a key
|
||
describe Queries the description of a key
|
||
get Get the value of a key
|
||
set Set the value of a key
|
||
reset Reset the value of a key
|
||
reset-recursively Reset all values in a given schema
|
||
writable Check if a key is writable
|
||
monitor Watch for changes
|
||
|
||
Use "gsettings help COMMAND" to get detailed help.</code></pre>
|
||
<p>List schemas.</p>
|
||
<pre><code>$ gsettings list-schemas
|
||
org.gnome.rhythmbox.podcast
|
||
ca.desrt.dconf-editor.Demo.Empty
|
||
org.gnome.gedit.preferences.ui
|
||
org.gnome.evolution-data-server.calendar
|
||
org.gnome.rhythmbox.plugins.generic-player
|
||
|
||
... ...
|
||
</code></pre>
|
||
<p>Each line is an id of a schema. Each schema has a key-value
|
||
configuration data. You can see them with list-recursively command.
|
||
Let’s look at the keys and values of <code>org.gnome.calculator</code>
|
||
schema.</p>
|
||
<pre><code>$ gsettings list-recursively org.gnome.calculator
|
||
org.gnome.calculator source-currency ''
|
||
org.gnome.calculator source-units 'degree'
|
||
org.gnome.calculator button-mode 'basic'
|
||
org.gnome.calculator target-currency ''
|
||
org.gnome.calculator base 10
|
||
org.gnome.calculator angle-units 'degrees'
|
||
org.gnome.calculator word-size 64
|
||
org.gnome.calculator accuracy 9
|
||
org.gnome.calculator show-thousands false
|
||
org.gnome.calculator window-position (122, 77)
|
||
org.gnome.calculator refresh-interval 604800
|
||
org.gnome.calculator target-units 'radian'
|
||
org.gnome.calculator precision 2000
|
||
org.gnome.calculator number-format 'automatic'
|
||
org.gnome.calculator show-zeroes false</code></pre>
|
||
<p>This schema is used by GNOME Calculator. Run the calculator and
|
||
change the mode, then check the schema again.</p>
|
||
<pre><code>$ gnome-calculator</code></pre>
|
||
<figure>
|
||
<img src="image/gnome_calculator_basic.png"
|
||
alt="gnome-calculator basic mode" />
|
||
<figcaption aria-hidden="true">gnome-calculator basic mode</figcaption>
|
||
</figure>
|
||
<p>Change the mode to advanced and quit.</p>
|
||
<figure>
|
||
<img src="image/gnome_calculator_advanced.png"
|
||
alt="gnome-calculator advanced mode" />
|
||
<figcaption aria-hidden="true">gnome-calculator advanced
|
||
mode</figcaption>
|
||
</figure>
|
||
<p>Run gsettings and check the value of <code>button-mode</code>.</p>
|
||
<pre><code>$ gsettings list-recursively org.gnome.calculator
|
||
|
||
... ...
|
||
|
||
org.gnome.calculator button-mode 'advanced'
|
||
|
||
... ...
|
||
</code></pre>
|
||
<p>Now we know that GNOME Calculator used gsettings and it has set
|
||
<code>button-mode</code> key to “advanced”. The value remains even the
|
||
calculator quits. So when the calculator runs again, it will appear as
|
||
an advanced mode.</p>
|
||
<h3 id="glib-compile-schemas">glib-compile-schemas</h3>
|
||
<p>GSettings schemas are specified with an XML format. The XML schema
|
||
files must have the filename extension <code>.gschema.xml</code>. The
|
||
following is the XML schema file for the application
|
||
<code>tfe</code>.</p>
|
||
<div class="sourceCode" id="cb35"><pre
|
||
class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><span id="cb35-1"><a href="#cb35-1"></a><span class="fu"><?xml</span><span class="ot"> version=</span><span class="st">"1.0"</span><span class="ot"> encoding=</span><span class="st">"UTF-8"</span><span class="fu">?></span></span>
|
||
<span id="cb35-2"><a href="#cb35-2"></a><<span class="kw">schemalist</span>></span>
|
||
<span id="cb35-3"><a href="#cb35-3"></a> <<span class="kw">schema</span><span class="ot"> path=</span><span class="st">"/com/github/ToshioCP/tfe/"</span><span class="ot"> id=</span><span class="st">"com.github.ToshioCP.tfe"</span>></span>
|
||
<span id="cb35-4"><a href="#cb35-4"></a> <<span class="kw">key</span><span class="ot"> name=</span><span class="st">"font"</span><span class="ot"> type=</span><span class="st">"s"</span>></span>
|
||
<span id="cb35-5"><a href="#cb35-5"></a> <<span class="kw">default</span>>'Monospace 12'</<span class="kw">default</span>></span>
|
||
<span id="cb35-6"><a href="#cb35-6"></a> <<span class="kw">summary</span>>Font</<span class="kw">summary</span>></span>
|
||
<span id="cb35-7"><a href="#cb35-7"></a> <<span class="kw">description</span>>A font to be used for textview.</<span class="kw">description</span>></span>
|
||
<span id="cb35-8"><a href="#cb35-8"></a> </<span class="kw">key</span>></span>
|
||
<span id="cb35-9"><a href="#cb35-9"></a> </<span class="kw">schema</span>></span>
|
||
<span id="cb35-10"><a href="#cb35-10"></a></<span class="kw">schemalist</span>></span></code></pre></div>
|
||
<p>The filename is “com.github.ToshioCP.tfe.gschema.xml”. Schema XML
|
||
filenames are usually the schema id followed by “.gschema.xml” suffix.
|
||
You can use different name from schema id, but it is not
|
||
recommended.</p>
|
||
<ul>
|
||
<li>2: The top level element is <code><schemalist></code>.</li>
|
||
<li>3: schema tag has <code>path</code> and <code>id</code> attributes.
|
||
A path determines where the settings are stored in the conceptual global
|
||
tree of settings. An id identifies the schema.</li>
|
||
<li>4: Key tag has two attributes. Name is the name of the key. Type is
|
||
the type of the value of the key and it is a GVariant Format
|
||
String.</li>
|
||
<li>5: default value of the key <code>font</code> is
|
||
<code>Monospace 12</code>.</li>
|
||
<li>6: Summery and description elements describes the key. They are
|
||
optional, but it is recommended to add them in the XML file.</li>
|
||
</ul>
|
||
<p>The XML file is compiled by glib-compile-schemas. When compiling,
|
||
<code>glib-compile-schemas</code> compiles all the XML files which have
|
||
“.gschema.xml” file extension in the directory given as an argument. It
|
||
converts the XML file into a binary file <code>gschemas.compiled</code>.
|
||
Suppose the XML file above is under <code>tfe6</code> directory.</p>
|
||
<pre><code>$ glib-compile-schemas tfe6</code></pre>
|
||
<p>Then, <code>gschemas.compiled</code> is generated under
|
||
<code>tfe6</code>. When you test your application, set
|
||
<code>GSETTINGS_SCHEMA_DIR</code> environment variable so that GSettings
|
||
objet can find <code>gschemas.compiled</code>.</p>
|
||
<pre><code>$ GSETTINGS_SCHEMA_DIR=(the directory gschemas.compiled is located):$GSETTINGS_SCHEMA_DIR (your application name)</code></pre>
|
||
<p>GSettings object looks for this file by the following process.</p>
|
||
<ul>
|
||
<li>It searches <code>glib-2.0/schemas</code> subdirectories of all the
|
||
directories specified in the environment variable
|
||
<code>XDG_DATA_DIRS</code>. Common directores are
|
||
<code>/usr/share/glib-2.0/schemas</code> and
|
||
`<code>/usr/local/share/glib-2.0/schemas</code>.</li>
|
||
<li>If <code>GSETTINGS_SCHEMA_DIR</code> environment variable is
|
||
defined, it searches all the directories specified in the variable.
|
||
<code>GSETTINGS_SCHEMA_DIR</code> can specify multiple directories
|
||
delimited by colon (:).</li>
|
||
</ul>
|
||
<p>In the directories above, all the <code>.gschema.xml</code> files are
|
||
stored. Therefore, when you install your application, follow the
|
||
instruction below to install your schemas.</p>
|
||
<ol type="1">
|
||
<li>Make <code>.gschema.xml</code> file.</li>
|
||
<li>Copy it to one of the directories above. For example,
|
||
<code>/usr/local/share/glib-2.0/schemas</code>.</li>
|
||
<li>Run <code>glib-compile-schemas</code> on the directory above. You
|
||
maybe need <code>sudo</code>.</li>
|
||
</ol>
|
||
<h3 id="gsettings-object-and-g_settings_bind">GSettings object and
|
||
g_settings_bind</h3>
|
||
<p>Now, we go on to the next topic — how to program GSettings.</p>
|
||
<div class="sourceCode" id="cb38"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb38-1"><a href="#cb38-1" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb38-2"><a href="#cb38-2" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> GSettings <span class="op">*</span>settings<span class="op">;</span></span>
|
||
<span id="cb38-3"><a href="#cb38-3" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb38-4"><a href="#cb38-4" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span></span>
|
||
<span id="cb38-5"><a href="#cb38-5" aria-hidden="true" tabindex="-1"></a>app_shutdown <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb38-6"><a href="#cb38-6" aria-hidden="true" tabindex="-1"></a> <span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb38-7"><a href="#cb38-7" aria-hidden="true" tabindex="-1"></a> g_clear_object <span class="op">(&</span>settings<span class="op">);</span></span>
|
||
<span id="cb38-8"><a href="#cb38-8" aria-hidden="true" tabindex="-1"></a> <span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb38-9"><a href="#cb38-9" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
|
||
<span id="cb38-10"><a href="#cb38-10" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb38-11"><a href="#cb38-11" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||
<span id="cb38-12"><a href="#cb38-12" aria-hidden="true" tabindex="-1"></a>app_startup <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb38-13"><a href="#cb38-13" aria-hidden="true" tabindex="-1"></a> <span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb38-14"><a href="#cb38-14" aria-hidden="true" tabindex="-1"></a> settings <span class="op">=</span> g_settings_new <span class="op">(</span><span class="st">"com.github.ToshioCP.tfe"</span><span class="op">);</span></span>
|
||
<span id="cb38-15"><a href="#cb38-15" aria-hidden="true" tabindex="-1"></a> g_settings_bind <span class="op">(</span>settings<span class="op">,</span> <span class="st">"font"</span><span class="op">,</span> fontbtn<span class="op">,</span> <span class="st">"font"</span><span class="op">,</span> G_SETTINGS_BIND_DEFAULT<span class="op">);</span></span>
|
||
<span id="cb38-16"><a href="#cb38-16" aria-hidden="true" tabindex="-1"></a> <span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb38-17"><a href="#cb38-17" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
|
||
<p>Static variable <code>settings</code> keeps a pointer to a GSettings
|
||
instance. Before application quits, the application releases the
|
||
GSettings instance. The function <code>g_clear_object</code> decreases
|
||
the reference count of the GSettings instance and assigns NULL to the
|
||
variable <code>settings</code>.</p>
|
||
<p>Startup handler creates GSettings instance with the schema id
|
||
“com.github.ToshioCP.tfe” and assigns the pointer to
|
||
<code>settings</code>. The function <code>g_settings_bind</code>
|
||
connects the settings keys (key and value) and the “font” property of
|
||
<code>fontbtn</code>. Then the two values will be always the same. If
|
||
one value changes then the other will automatically change.</p>
|
||
<p>For further information, refer to <a
|
||
href="https://docs.gtk.org/gio/class.Settings.html">GIO API rference –
|
||
GSettings</a>.</p>
|
||
<h2 id="build-with-meson">Build with Meson</h2>
|
||
<h3 id="build-and-test">Build and test</h3>
|
||
<p>Meson provides <code>gnome.compile_schemas</code> method to compile
|
||
XML file in the build directory. This is used to test the application.
|
||
Write the following to the <code>meson.build</code> file.</p>
|
||
<pre><code>gnome.compile_schemas(build_by_default: true, depend_files: 'com.github.ToshioCP.tfe.gschema.xml')</code></pre>
|
||
<ul>
|
||
<li><code>build_by_default</code>: If it is true, the target will be
|
||
build by default.</li>
|
||
<li><code>depend_files</code>: XML files to be compiled.</li>
|
||
</ul>
|
||
<p>In the example above, this method runs
|
||
<code>glib-compile-schemas</code> to generate
|
||
<code>gschemas.compiled</code> from the XML file
|
||
<code>com.github.ToshioCP.tfe.gschema.xml</code>. The file
|
||
<code>gschemas.compiled</code> is located under the build directory. If
|
||
you run meson as <code>meson _build</code> and ninja as
|
||
<code>ninja -C _build</code>, then it is under <code>_build</code>
|
||
directory.</p>
|
||
<p>After compilation, you can test your application like this:</p>
|
||
<pre><code>$ GSETTINGS_SCHEMA_DIR=_build:$GSETTINGS_SCHEMA_DIR _build/tfe</code></pre>
|
||
<h3 id="installation">installation</h3>
|
||
<p>It is a good idea to install your application in
|
||
<code>$HOME/bin</code> or <code>$HOME/.local/bin</code> directory. They
|
||
are local bin directories and work like system bin directories such as
|
||
<code>/bin</code>, <code>/usr/bin</code> or <code>usr/local/bin</code>.
|
||
You need to put <code>--prefix=$HOME</code> or
|
||
<code>--prefix=$HOME/.local</code> option to meson.</p>
|
||
<pre><code>$ meson --prefix=$HOME/.local _build</code></pre>
|
||
<p>If you want to install your application to a system bin directory,
|
||
for example <code>/usr/local/bin</code>, <code>--prefix</code> option
|
||
isn’t necessary.</p>
|
||
<p>Meson recognizes options like this:</p>
|
||
<table>
|
||
<thead>
|
||
<tr class="header">
|
||
<th style="text-align: left;">options</th>
|
||
<th style="text-align: left;">values (default)</th>
|
||
<th style="text-align: left;">values (–prefix=$HOME/.local)</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="odd">
|
||
<td style="text-align: left;">prefix</td>
|
||
<td style="text-align: left;">/usr/local</td>
|
||
<td style="text-align: left;">$HOME/.local</td>
|
||
</tr>
|
||
<tr class="even">
|
||
<td style="text-align: left;">bindir</td>
|
||
<td style="text-align: left;">bin</td>
|
||
<td style="text-align: left;">bin</td>
|
||
</tr>
|
||
<tr class="odd">
|
||
<td style="text-align: left;">datadir</td>
|
||
<td style="text-align: left;">share</td>
|
||
<td style="text-align: left;">share</td>
|
||
</tr>
|
||
<tr class="even">
|
||
<td style="text-align: left;">install directory</td>
|
||
<td style="text-align: left;">/usr/local/bin</td>
|
||
<td style="text-align: left;">$HOME/.local/bin</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>The function <code>executable</code> needs <code>install: true</code>
|
||
to install your program.</p>
|
||
<pre><code>executable('tfe', sourcefiles, resources, dependencies: gtkdep, export_dynamic: true, install: true)</code></pre>
|
||
<p>However, you need to do one more thing. Copy your XML file to your
|
||
schema directory and execute <code>glib-compile-schemas</code> on the
|
||
directory.</p>
|
||
<ul>
|
||
<li><code>install_data</code> function copies a file into a target
|
||
directory.</li>
|
||
<li><code>gnome.post_install</code> function executes
|
||
‘glib-compile-schemas’ with an argument <code>schema_dir</code> as post
|
||
installation. This function is available since Meson 0.57.0. If the
|
||
version is earlier than that, use <code>meson.add_install_script</code>
|
||
instead.</li>
|
||
</ul>
|
||
<pre><code>schema_dir = get_option('prefix') / get_option('datadir') / 'glib-2.0/schemas/'
|
||
install_data('com.github.ToshioCP.tfe.gschema.xml', install_dir: schema_dir)
|
||
gnome.post_install (glib_compile_schemas: true)</code></pre>
|
||
<p>The function <code>get_option</code> returns the value of build
|
||
options. See <a
|
||
href="https://mesonbuild.com/Reference-manual_functions.html#get_option">Meson
|
||
Reference Manual</a>. The operator ‘/’ connects the strings with ‘/’
|
||
separator.</p>
|
||
<table>
|
||
<colgroup>
|
||
<col style="width: 33%" />
|
||
<col style="width: 33%" />
|
||
<col style="width: 33%" />
|
||
</colgroup>
|
||
<thead>
|
||
<tr class="header">
|
||
<th style="text-align: left;">options</th>
|
||
<th style="text-align: left;">values (default)</th>
|
||
<th style="text-align: left;">values (–prefix=$HOME/.local)</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="odd">
|
||
<td style="text-align: left;">prefix</td>
|
||
<td style="text-align: left;">/usr/local</td>
|
||
<td style="text-align: left;">$HOME/.local</td>
|
||
</tr>
|
||
<tr class="even">
|
||
<td style="text-align: left;">datadir</td>
|
||
<td style="text-align: left;">share</td>
|
||
<td style="text-align: left;">share</td>
|
||
</tr>
|
||
<tr class="odd">
|
||
<td style="text-align: left;">schema_dir</td>
|
||
<td style="text-align: left;">/usr/local/share/glib-2.0/schemas</td>
|
||
<td style="text-align: left;">$HOME/.local/share/glib-2.0/schemas</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>The source code of <code>meson.build</code> is as follows.</p>
|
||
<div class="sourceCode" id="cb44"><pre
|
||
class="sourceCode numberSource numberLines"><code class="sourceCode"><span id="cb44-1"><a href="#cb44-1"></a>project('tfe', 'c')</span>
|
||
<span id="cb44-2"><a href="#cb44-2"></a></span>
|
||
<span id="cb44-3"><a href="#cb44-3"></a>gtkdep = dependency('gtk4')</span>
|
||
<span id="cb44-4"><a href="#cb44-4"></a></span>
|
||
<span id="cb44-5"><a href="#cb44-5"></a>gnome=import('gnome')</span>
|
||
<span id="cb44-6"><a href="#cb44-6"></a>resources = gnome.compile_resources('resources','tfe.gresource.xml')</span>
|
||
<span id="cb44-7"><a href="#cb44-7"></a>gnome.compile_schemas(build_by_default: true, depend_files: 'com.github.ToshioCP.tfe.gschema.xml')</span>
|
||
<span id="cb44-8"><a href="#cb44-8"></a></span>
|
||
<span id="cb44-9"><a href="#cb44-9"></a>sourcefiles=files('tfeapplication.c', 'tfenotebook.c', 'pfd2css.c', '../tfetextview/tfetextview.c')</span>
|
||
<span id="cb44-10"><a href="#cb44-10"></a></span>
|
||
<span id="cb44-11"><a href="#cb44-11"></a>executable('tfe', sourcefiles, resources, dependencies: gtkdep, export_dynamic: true, install: true)</span>
|
||
<span id="cb44-12"><a href="#cb44-12"></a></span>
|
||
<span id="cb44-13"><a href="#cb44-13"></a>schema_dir = get_option('prefix') / get_option('datadir') / 'glib-2.0/schemas/'</span>
|
||
<span id="cb44-14"><a href="#cb44-14"></a>install_data('com.github.ToshioCP.tfe.gschema.xml', install_dir: schema_dir)</span>
|
||
<span id="cb44-15"><a href="#cb44-15"></a>gnome.post_install (glib_compile_schemas: true)</span></code></pre></div>
|
||
<p>Source files of <code>tfe</code> is under src/tfe6 directory. Copy
|
||
them to your temporary directory and compile and install it.</p>
|
||
<pre><code>$ meson --prefix=$HOME/.local _build
|
||
$ ninja -C _build
|
||
$ GSETTINGS_SCHEMA_DIR=_build:$GSETTINGS_SCHEMA_DIR _build/tfe # test
|
||
$ ninja -C _build install
|
||
$ ls $HOME/.local/bin
|
||
... ...
|
||
... tfe
|
||
... ...
|
||
$ ls $HOME/.local/share/glib-2.0/schemas
|
||
com.github.ToshioCP.tfe.gschema.xml
|
||
gschema.dtd
|
||
gschemas.compiled
|
||
... ...
|
||
$ tfe</code></pre>
|
||
<p>The screenshot is as follows.</p>
|
||
<figure>
|
||
<img src="image/tfe6.png" alt="tfe6" />
|
||
<figcaption aria-hidden="true">tfe6</figcaption>
|
||
</figure>
|
||
</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>
|