mirror of
https://github.com/ToshioCP/Gtk4-tutorial.git
synced 2025-01-12 20:03:28 +01:00
1298 lines
128 KiB
HTML
1298 lines
128 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>Traditional menu structure is fine. However, buttons or menu items we
|
||
often use are not so many. Some mightn’t be clicked at all. Therefore,
|
||
it’s a good idea to put some frequently used buttons on the toolbar and
|
||
put the rest of the less frequently used operations into the menu. Such
|
||
menu are often connected to GtkMenuButton.</p>
|
||
<p>We will restructure tfe text file editor in this section. It will be
|
||
more practical. The buttons are changed to:</p>
|
||
<ul>
|
||
<li>Put open, save and close buttons to 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>Put new, save as, preference and quit items to the menu under the
|
||
menu button.</li>
|
||
</ul>
|
||
<h2 id="signal-elements-in-ui-files">Signal elements in ui files</h2>
|
||
<p>The four buttons are included in the ui file <code>tfe.ui</code>. The
|
||
difference from prior sections is signal tag. The following is extracted
|
||
from <code>tfe.ui</code> and it describes the open button.</p>
|
||
<div class="sourceCode" id="cb1"><pre
|
||
class="sourceCode xml"><code class="sourceCode xml"><span id="cb1-1"><a href="#cb1-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="cb1-2"><a href="#cb1-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="cb1-3"><a href="#cb1-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="cb1-4"><a href="#cb1-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. They are the value of name, handler and object attributes.
|
||
Swapped attribute has the same meaning as
|
||
<code>g_signal_connect_swapped</code> function. So, the signal tag above
|
||
works the same as the function below.</p>
|
||
<div class="sourceCode" id="cb2"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>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>You need to compile the source file with “-WI, –export-dynamic”
|
||
options. 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="cb3"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span></span>
|
||
<span id="cb3-2"><a href="#cb3-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="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a> notebook_page_open <span class="op">(</span>nb<span class="op">);</span></span>
|
||
<span id="cb3-4"><a href="#cb3-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>Menus are described in <code>menu.ui</code> file.</p>
|
||
<div class="sourceCode" id="cb4"><pre
|
||
class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><span id="cb4-1"><a href="#cb4-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="cb4-2"><a href="#cb4-2"></a><<span class="kw">interface</span>></span>
|
||
<span id="cb4-3"><a href="#cb4-3"></a> <<span class="kw">menu</span><span class="ot"> id=</span><span class="st">"menu"</span>></span>
|
||
<span id="cb4-4"><a href="#cb4-4"></a> <<span class="kw">section</span>></span>
|
||
<span id="cb4-5"><a href="#cb4-5"></a> <<span class="kw">item</span>></span>
|
||
<span id="cb4-6"><a href="#cb4-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="cb4-7"><a href="#cb4-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="cb4-8"><a href="#cb4-8"></a> </<span class="kw">item</span>></span>
|
||
<span id="cb4-9"><a href="#cb4-9"></a> <<span class="kw">item</span>></span>
|
||
<span id="cb4-10"><a href="#cb4-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="cb4-11"><a href="#cb4-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="cb4-12"><a href="#cb4-12"></a> </<span class="kw">item</span>></span>
|
||
<span id="cb4-13"><a href="#cb4-13"></a> </<span class="kw">section</span>></span>
|
||
<span id="cb4-14"><a href="#cb4-14"></a> <<span class="kw">section</span>></span>
|
||
<span id="cb4-15"><a href="#cb4-15"></a> <<span class="kw">item</span>></span>
|
||
<span id="cb4-16"><a href="#cb4-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="cb4-17"><a href="#cb4-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="cb4-18"><a href="#cb4-18"></a> </<span class="kw">item</span>></span>
|
||
<span id="cb4-19"><a href="#cb4-19"></a> </<span class="kw">section</span>></span>
|
||
<span id="cb4-20"><a href="#cb4-20"></a> <<span class="kw">section</span>></span>
|
||
<span id="cb4-21"><a href="#cb4-21"></a> <<span class="kw">item</span>></span>
|
||
<span id="cb4-22"><a href="#cb4-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="cb4-23"><a href="#cb4-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="cb4-24"><a href="#cb4-24"></a> </<span class="kw">item</span>></span>
|
||
<span id="cb4-25"><a href="#cb4-25"></a> </<span class="kw">section</span>></span>
|
||
<span id="cb4-26"><a href="#cb4-26"></a> </<span class="kw">menu</span>></span>
|
||
<span id="cb4-27"><a href="#cb4-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 new filename.</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 to
|
||
the menu behind the menu button.</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="cb5"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb5-1"><a href="#cb5-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="cb5-2"><a href="#cb5-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="cb5-3"><a href="#cb5-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="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">const</span> GActionEntry win_entries<span class="op">[]</span> <span class="op">=</span> <span class="op">{</span></span>
|
||
<span id="cb6-2"><a href="#cb6-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="cb6-3"><a href="#cb6-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="cb6-4"><a href="#cb6-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="cb6-5"><a href="#cb6-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="cb6-6"><a href="#cb6-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="cb6-7"><a href="#cb6-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="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a> <span class="op">{</span> <span class="st">"close-all"</span><span class="op">,</span> quit_activated<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">,</span> NULL <span class="op">}</span></span>
|
||
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a> <span class="op">};</span></span>
|
||
<span id="cb6-10"><a href="#cb6-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 thy necessary? These actions are defined
|
||
because of accelerators.</p>
|
||
<p>Accelerators are a kind of short cut key function. They are defined
|
||
with arrays and <code>gtk_application_set_accels_for_action</code>
|
||
function.</p>
|
||
<div class="sourceCode" id="cb7"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a> <span class="kw">struct</span> <span class="op">{</span></span>
|
||
<span id="cb7-2"><a href="#cb7-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="cb7-3"><a href="#cb7-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="cb7-4"><a href="#cb7-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="cb7-5"><a href="#cb7-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="cb7-6"><a href="#cb7-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="cb7-7"><a href="#cb7-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="cb7-8"><a href="#cb7-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="cb7-9"><a href="#cb7-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="cb7-10"><a href="#cb7-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="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a> <span class="op">};</span></span>
|
||
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb7-13"><a href="#cb7-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="cb7-14"><a href="#cb7-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="cb8"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a> <span class="kw">struct</span> <span class="op">{</span></span>
|
||
<span id="cb8-2"><a href="#cb8-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="cb8-3"><a href="#cb8-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="cb8-4"><a href="#cb8-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="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="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 (its 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>
|
||
<h2 id="saveas-handler">Saveas handler</h2>
|
||
<p>TfeTextView has already had a saveas function. So, only we need to
|
||
write is the wrapper function in <code>tfenotebook.c</code>.</p>
|
||
<div class="sourceCode" id="cb10"><pre
|
||
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb10-1"><a href="#cb10-1"></a><span class="dt">static</span> TfeTextView <span class="op">*</span></span>
|
||
<span id="cb10-2"><a href="#cb10-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="cb10-3"><a href="#cb10-3"></a> <span class="dt">int</span> i<span class="op">;</span></span>
|
||
<span id="cb10-4"><a href="#cb10-4"></a> GtkWidget <span class="op">*</span>scr<span class="op">;</span></span>
|
||
<span id="cb10-5"><a href="#cb10-5"></a> GtkWidget <span class="op">*</span>tv<span class="op">;</span></span>
|
||
<span id="cb10-6"><a href="#cb10-6"></a></span>
|
||
<span id="cb10-7"><a href="#cb10-7"></a> i <span class="op">=</span> gtk_notebook_get_current_page <span class="op">(</span>nb<span class="op">);</span></span>
|
||
<span id="cb10-8"><a href="#cb10-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="cb10-9"><a href="#cb10-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="cb10-10"><a href="#cb10-10"></a> <span class="cf">return</span> TFE_TEXT_VIEW <span class="op">(</span>tv<span class="op">);</span></span>
|
||
<span id="cb10-11"><a href="#cb10-11"></a><span class="op">}</span></span>
|
||
<span id="cb10-12"><a href="#cb10-12"></a></span>
|
||
<span id="cb10-13"><a href="#cb10-13"></a><span class="dt">void</span></span>
|
||
<span id="cb10-14"><a href="#cb10-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="cb10-15"><a href="#cb10-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="cb10-16"><a href="#cb10-16"></a></span>
|
||
<span id="cb10-17"><a href="#cb10-17"></a> TfeTextView <span class="op">*</span>tv<span class="op">;</span></span>
|
||
<span id="cb10-18"><a href="#cb10-18"></a></span>
|
||
<span id="cb10-19"><a href="#cb10-19"></a> tv <span class="op">=</span> get_current_textview <span class="op">(</span>nb<span class="op">);</span></span>
|
||
<span id="cb10-20"><a href="#cb10-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="cb10-21"><a href="#cb10-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="cb11"><pre
|
||
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb11-1"><a href="#cb11-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||
<span id="cb11-2"><a href="#cb11-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="cb11-3"><a href="#cb11-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="cb11-4"><a href="#cb11-4"></a> notebook_page_saveas <span class="op">(</span>nb<span class="op">);</span></span>
|
||
<span id="cb11-5"><a href="#cb11-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="cb12"><pre
|
||
class="sourceCode xml"><code class="sourceCode xml"><span id="cb12-1"><a href="#cb12-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="cb12-2"><a href="#cb12-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="cb12-3"><a href="#cb12-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="cb12-4"><a href="#cb12-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="cb12-5"><a href="#cb12-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="cb12-6"><a href="#cb12-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="cb12-7"><a href="#cb12-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="cb12-8"><a href="#cb12-8" aria-hidden="true" tabindex="-1"></a> <<span class="kw">child</span>></span>
|
||
<span id="cb12-9"><a href="#cb12-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="cb12-10"><a href="#cb12-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="cb12-11"><a href="#cb12-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="cb12-12"><a href="#cb12-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="cb12-13"><a href="#cb12-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="cb12-14"><a href="#cb12-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="cb12-15"><a href="#cb12-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="cb12-16"><a href="#cb12-16" aria-hidden="true" tabindex="-1"></a> <<span class="kw">child</span>></span>
|
||
<span id="cb12-17"><a href="#cb12-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="cb12-18"><a href="#cb12-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="cb12-19"><a href="#cb12-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="cb12-20"><a href="#cb12-20" aria-hidden="true" tabindex="-1"></a> </<span class="kw">object</span>></span>
|
||
<span id="cb12-21"><a href="#cb12-21" aria-hidden="true" tabindex="-1"></a> </<span class="kw">child</span>></span>
|
||
<span id="cb12-22"><a href="#cb12-22" aria-hidden="true" tabindex="-1"></a> <<span class="kw">child</span>></span>
|
||
<span id="cb12-23"><a href="#cb12-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="cb12-24"><a href="#cb12-24" aria-hidden="true" tabindex="-1"></a> </<span class="kw">object</span>></span>
|
||
<span id="cb12-25"><a href="#cb12-25" aria-hidden="true" tabindex="-1"></a> </<span class="kw">child</span>></span>
|
||
<span id="cb12-26"><a href="#cb12-26" aria-hidden="true" tabindex="-1"></a> </<span class="kw">object</span>></span>
|
||
<span id="cb12-27"><a href="#cb12-27" aria-hidden="true" tabindex="-1"></a> </<span class="kw">child</span>></span>
|
||
<span id="cb12-28"><a href="#cb12-28" aria-hidden="true" tabindex="-1"></a> </<span class="kw">object</span>></span>
|
||
<span id="cb12-29"><a href="#cb12-29" aria-hidden="true" tabindex="-1"></a> </<span class="kw">child</span>></span>
|
||
<span id="cb12-30"><a href="#cb12-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 <code>win</code>.
|
||
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>internal-child attribute is used in the child tag above. GtkDialog
|
||
has a GtkBox child widget. Its id is “content_area” in
|
||
<code>gtkdialog.ui</code>, which is the ui file of GtkDialog. (It is in
|
||
the GTK 4 source files.) This box is provided for users to add content
|
||
widgets in it. The tag
|
||
<code><child internal-child="content_area"></code> is put at the
|
||
top of the contents. Then you need to specify an object tag and define
|
||
its class as GtkBox and its id as content_area. This object is defined
|
||
in <code>gtkdialog.ui</code> but you need to define it again in the
|
||
child tag.</li>
|
||
<li>In the content area, defines GtkBox, GtkLabel and
|
||
GtkFontButton.</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 is accomplished by
|
||
returning TRUE by the signal handler.</p>
|
||
<div class="sourceCode" id="cb13"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a>pref_close_cb <span class="op">(</span>GtkDialog <span class="op">*</span>pref<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> TRUE<span class="op">;</span></span>
|
||
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
|
||
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a>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>pref_close_cb<span class="op">),</span> NULL<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>In the case of “close-request” signal, the default handler’s flag is
|
||
<code>G_SIGNAL_RUN_LAST</code>. The handler <code>pref_close_cb</code>
|
||
is not connected by <code>g_signal_connect_after</code>. So the number
|
||
of stages are two.</p>
|
||
<ol type="1">
|
||
<li>Signal handler <code>pref_close_cb</code> is invoked.</li>
|
||
<li>Default handler is invoked.</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="cb14"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb14-1"><a href="#cb14-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="cb14-2"><a href="#cb14-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="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb14-6"><a href="#cb14-6" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> gboolean</span>
|
||
<span id="cb14-7"><a href="#cb14-7" 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="cb14-8"><a href="#cb14-8" aria-hidden="true" tabindex="-1"></a> gtk_widget_hide <span class="op">(</span>GTK_WIDGET <span class="op">(</span>dialog<span class="op">));</span></span>
|
||
<span id="cb14-9"><a href="#cb14-9" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> TRUE<span class="op">;</span></span>
|
||
<span id="cb14-10"><a href="#cb14-10" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
|
||
<span id="cb14-11"><a href="#cb14-11" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb14-12"><a href="#cb14-12" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb14-13"><a href="#cb14-13" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb14-14"><a href="#cb14-14" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||
<span id="cb14-15"><a href="#cb14-15" 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="cb14-16"><a href="#cb14-16" aria-hidden="true" tabindex="-1"></a> gtk_widget_show <span class="op">(</span>GTK_WIDGET <span class="op">(</span>pref<span class="op">));</span></span>
|
||
<span id="cb14-17"><a href="#cb14-17" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
|
||
<span id="cb14-18"><a href="#cb14-18" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb14-19"><a href="#cb14-19" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb14-20"><a href="#cb14-20" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb14-21"><a href="#cb14-21" aria-hidden="true" tabindex="-1"></a><span class="co">/* ----- quit application ----- */</span></span>
|
||
<span id="cb14-22"><a href="#cb14-22" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span></span>
|
||
<span id="cb14-23"><a href="#cb14-23" aria-hidden="true" tabindex="-1"></a>tfe_application_quit <span class="op">(</span>GtkWindow <span class="op">*</span>win<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb14-24"><a href="#cb14-24" 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="cb14-25"><a href="#cb14-25" 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="cb14-26"><a href="#cb14-26" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>alert_close_request_handler_id <span class="op">></span> <span class="dv">0</span><span class="op">)</span></span>
|
||
<span id="cb14-27"><a href="#cb14-27" aria-hidden="true" tabindex="-1"></a> g_signal_handler_disconnect <span class="op">(</span>alert<span class="op">,</span> alert_close_request_handler_id<span class="op">);</span></span>
|
||
<span id="cb14-28"><a href="#cb14-28" aria-hidden="true" tabindex="-1"></a> g_clear_object <span class="op">(&</span>settings<span class="op">);</span></span>
|
||
<span id="cb14-29"><a href="#cb14-29" aria-hidden="true" tabindex="-1"></a> gtk_window_destroy <span class="op">(</span>GTK_WINDOW <span class="op">(</span>alert<span class="op">));</span></span>
|
||
<span id="cb14-30"><a href="#cb14-30" 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="cb14-31"><a href="#cb14-31" aria-hidden="true" tabindex="-1"></a> gtk_window_destroy <span class="op">(</span>win<span class="op">);</span></span>
|
||
<span id="cb14-32"><a href="#cb14-32" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
|
||
<span id="cb14-33"><a href="#cb14-33" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb14-34"><a href="#cb14-34" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb14-35"><a href="#cb14-35" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb14-36"><a href="#cb14-36" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||
<span id="cb14-37"><a href="#cb14-37" 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="cb14-38"><a href="#cb14-38" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb14-39"><a href="#cb14-39" aria-hidden="true" tabindex="-1"></a> <span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb14-40"><a href="#cb14-40" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb14-41"><a href="#cb14-41" 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="cb14-42"><a href="#cb14-42" 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="cb14-43"><a href="#cb14-43" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb14-44"><a href="#cb14-44" aria-hidden="true" tabindex="-1"></a> <span class="op">...</span> <span class="op">...</span> </span>
|
||
<span id="cb14-45"><a href="#cb14-45" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
|
||
<p>The function <code>tfe_application_quit</code> destroys top-level
|
||
windows and quits the application. It first disconnects the handlers
|
||
from the signal “close-request”.</p>
|
||
<h3 id="alert-dialog">Alert dialog</h3>
|
||
<p>If a user closes a page which hasn’t been saved, it is advisable to
|
||
show an alert to confirm it. Alert dialog is used in this application
|
||
for such a situation.</p>
|
||
<div class="sourceCode" id="cb15"><pre
|
||
class="sourceCode xml"><code class="sourceCode xml"><span id="cb15-1"><a href="#cb15-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="cb15-2"><a href="#cb15-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="cb15-3"><a href="#cb15-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="cb15-4"><a href="#cb15-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="cb15-5"><a href="#cb15-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="cb15-6"><a href="#cb15-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="cb15-7"><a href="#cb15-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="cb15-8"><a href="#cb15-8" aria-hidden="true" tabindex="-1"></a> <<span class="kw">child</span>></span>
|
||
<span id="cb15-9"><a href="#cb15-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="cb15-10"><a href="#cb15-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="cb15-11"><a href="#cb15-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="cb15-12"><a href="#cb15-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="cb15-13"><a href="#cb15-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="cb15-14"><a href="#cb15-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="cb15-15"><a href="#cb15-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="cb15-16"><a href="#cb15-16" aria-hidden="true" tabindex="-1"></a> <<span class="kw">child</span>></span>
|
||
<span id="cb15-17"><a href="#cb15-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="cb15-18"><a href="#cb15-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="cb15-19"><a href="#cb15-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="cb15-20"><a href="#cb15-20" aria-hidden="true" tabindex="-1"></a> </<span class="kw">object</span>></span>
|
||
<span id="cb15-21"><a href="#cb15-21" aria-hidden="true" tabindex="-1"></a> </<span class="kw">child</span>></span>
|
||
<span id="cb15-22"><a href="#cb15-22" aria-hidden="true" tabindex="-1"></a> <<span class="kw">child</span>></span>
|
||
<span id="cb15-23"><a href="#cb15-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="cb15-24"><a href="#cb15-24" aria-hidden="true" tabindex="-1"></a> </<span class="kw">object</span>></span>
|
||
<span id="cb15-25"><a href="#cb15-25" aria-hidden="true" tabindex="-1"></a> </<span class="kw">child</span>></span>
|
||
<span id="cb15-26"><a href="#cb15-26" aria-hidden="true" tabindex="-1"></a> </<span class="kw">object</span>></span>
|
||
<span id="cb15-27"><a href="#cb15-27" aria-hidden="true" tabindex="-1"></a> </<span class="kw">child</span>></span>
|
||
<span id="cb15-28"><a href="#cb15-28" aria-hidden="true" tabindex="-1"></a> </<span class="kw">object</span>></span>
|
||
<span id="cb15-29"><a href="#cb15-29" aria-hidden="true" tabindex="-1"></a> </<span class="kw">child</span>></span>
|
||
<span id="cb15-30"><a href="#cb15-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="cb15-31"><a href="#cb15-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="cb15-32"><a href="#cb15-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="cb15-33"><a href="#cb15-33" aria-hidden="true" tabindex="-1"></a> </<span class="kw">object</span>></span>
|
||
<span id="cb15-34"><a href="#cb15-34" aria-hidden="true" tabindex="-1"></a> </<span class="kw">child</span>></span>
|
||
<span id="cb15-35"><a href="#cb15-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="cb15-36"><a href="#cb15-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="cb15-37"><a href="#cb15-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="cb15-38"><a href="#cb15-38" aria-hidden="true" tabindex="-1"></a> </<span class="kw">object</span>></span>
|
||
<span id="cb15-39"><a href="#cb15-39" aria-hidden="true" tabindex="-1"></a> </<span class="kw">child</span>></span>
|
||
<span id="cb15-40"><a href="#cb15-40" aria-hidden="true" tabindex="-1"></a> <<span class="kw">action-widgets</span>></span>
|
||
<span id="cb15-41"><a href="#cb15-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="cb15-42"><a href="#cb15-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="cb15-43"><a href="#cb15-43" aria-hidden="true" tabindex="-1"></a> </<span class="kw">action-widgets</span>></span>
|
||
<span id="cb15-44"><a href="#cb15-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="cb15-45"><a href="#cb15-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 icon named “dialog-warning” 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 by the program later.</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. <code>btn_cancel</code> button emits response
|
||
signal with cancel response (<code>GTK_RESPONSE_CANCEL</code>) if it is
|
||
clicked on. <code>btn_accept</code> button 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>
|
||
<h2 id="close-and-quit-handlers">Close and quit handlers</h2>
|
||
<p>If a user closes a page or quits the application without saving the
|
||
contents, the application alerts.</p>
|
||
<div class="sourceCode" id="cb17"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> gboolean is_quit<span class="op">;</span></span>
|
||
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb17-3"><a href="#cb17-3" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb17-4"><a href="#cb17-4" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb17-5"><a href="#cb17-5" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span></span>
|
||
<span id="cb17-6"><a href="#cb17-6" 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="cb17-7"><a href="#cb17-7" aria-hidden="true" tabindex="-1"></a> is_quit <span class="op">=</span> false<span class="op">;</span></span>
|
||
<span id="cb17-8"><a href="#cb17-8" 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="cb17-9"><a href="#cb17-9" 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="cb17-10"><a href="#cb17-10" aria-hidden="true" tabindex="-1"></a> <span class="cf">else</span> <span class="op">{</span></span>
|
||
<span id="cb17-11"><a href="#cb17-11" 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="cb17-12"><a href="#cb17-12" aria-hidden="true" tabindex="-1"></a> gtk_button_set_label <span class="op">(</span>close_btn_close<span class="op">,</span> <span class="st">"Close"</span><span class="op">);</span></span>
|
||
<span id="cb17-13"><a href="#cb17-13" aria-hidden="true" tabindex="-1"></a> gtk_widget_show <span class="op">(</span>GTK_WIDGET <span class="op">(</span>alert<span class="op">));</span></span>
|
||
<span id="cb17-14"><a href="#cb17-14" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
|
||
<span id="cb17-15"><a href="#cb17-15" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
|
||
<span id="cb17-16"><a href="#cb17-16" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb17-17"><a href="#cb17-17" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb17-18"><a href="#cb17-18" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb17-19"><a href="#cb17-19" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||
<span id="cb17-20"><a href="#cb17-20" 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="cb17-21"><a href="#cb17-21" 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="cb17-22"><a href="#cb17-22" aria-hidden="true" tabindex="-1"></a> close_cb <span class="op">(</span>nb<span class="op">);</span></span>
|
||
<span id="cb17-23"><a href="#cb17-23" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
|
||
<span id="cb17-24"><a href="#cb17-24" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb17-25"><a href="#cb17-25" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb17-26"><a href="#cb17-26" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb17-27"><a href="#cb17-27" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span></span>
|
||
<span id="cb17-28"><a href="#cb17-28" 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="cb17-29"><a href="#cb17-29" 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="cb17-30"><a href="#cb17-30" 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="cb17-31"><a href="#cb17-31" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb17-32"><a href="#cb17-32" aria-hidden="true" tabindex="-1"></a> gtk_widget_hide <span class="op">(</span>GTK_WIDGET <span class="op">(</span>alert<span class="op">));</span></span>
|
||
<span id="cb17-33"><a href="#cb17-33" 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="cb17-34"><a href="#cb17-34" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>is_quit<span class="op">)</span></span>
|
||
<span id="cb17-35"><a href="#cb17-35" aria-hidden="true" tabindex="-1"></a> tfe_application_quit <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
|
||
<span id="cb17-36"><a href="#cb17-36" aria-hidden="true" tabindex="-1"></a> <span class="cf">else</span></span>
|
||
<span id="cb17-37"><a href="#cb17-37" aria-hidden="true" tabindex="-1"></a> notebook_page_close <span class="op">(</span>nb<span class="op">);</span></span>
|
||
<span id="cb17-38"><a href="#cb17-38" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
|
||
<span id="cb17-39"><a href="#cb17-39" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
|
||
<span id="cb17-40"><a href="#cb17-40" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb17-41"><a href="#cb17-41" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||
<span id="cb17-42"><a href="#cb17-42" aria-hidden="true" tabindex="-1"></a>quit_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="cb17-43"><a href="#cb17-43" 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="cb17-44"><a href="#cb17-44" 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="cb17-45"><a href="#cb17-45" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb17-46"><a href="#cb17-46" aria-hidden="true" tabindex="-1"></a> is_quit <span class="op">=</span> true<span class="op">;</span></span>
|
||
<span id="cb17-47"><a href="#cb17-47" 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="cb17-48"><a href="#cb17-48" aria-hidden="true" tabindex="-1"></a> tfe_application_quit <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
|
||
<span id="cb17-49"><a href="#cb17-49" aria-hidden="true" tabindex="-1"></a> <span class="cf">else</span> <span class="op">{</span></span>
|
||
<span id="cb17-50"><a href="#cb17-50" 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="cb17-51"><a href="#cb17-51" 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="cb17-52"><a href="#cb17-52" aria-hidden="true" tabindex="-1"></a> gtk_widget_show <span class="op">(</span>GTK_WIDGET <span class="op">(</span>alert<span class="op">));</span></span>
|
||
<span id="cb17-53"><a href="#cb17-53" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
|
||
<span id="cb17-54"><a href="#cb17-54" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
|
||
<span id="cb17-55"><a href="#cb17-55" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb17-56"><a href="#cb17-56" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||
<span id="cb17-57"><a href="#cb17-57" 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="cb17-58"><a href="#cb17-58" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb17-59"><a href="#cb17-59" aria-hidden="true" tabindex="-1"></a> <span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb17-60"><a href="#cb17-60" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb17-61"><a href="#cb17-61" aria-hidden="true" tabindex="-1"></a> alert <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">"alert"</span><span class="op">));</span></span>
|
||
<span id="cb17-62"><a href="#cb17-62" aria-hidden="true" tabindex="-1"></a> alert_close_request_handler_id <span class="op">=</span> g_signal_connect <span class="op">(</span>GTK_DIALOG <span class="op">(</span>alert<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="cb17-63"><a href="#cb17-63" aria-hidden="true" tabindex="-1"></a> lb_alert <span class="op">=</span> GTK_LABEL <span class="op">(</span>gtk_builder_get_object <span class="op">(</span>build<span class="op">,</span> <span class="st">"lb_alert"</span><span class="op">));</span></span>
|
||
<span id="cb17-64"><a href="#cb17-64" aria-hidden="true" tabindex="-1"></a> btn_accept <span class="op">=</span> GTK_BUTTON <span class="op">(</span>gtk_builder_get_object <span class="op">(</span>build<span class="op">,</span> <span class="st">"btn_accept"</span><span class="op">));</span></span>
|
||
<span id="cb17-65"><a href="#cb17-65" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb17-66"><a href="#cb17-66" aria-hidden="true" tabindex="-1"></a> <span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb17-67"><a href="#cb17-67" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb17-68"><a href="#cb17-68" 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. When user presses “Ctrl-w”,
|
||
<code>close_activated</code> handler is invoked. It just calls
|
||
<code>close_cb</code>. When user clicks on the close button,
|
||
<code>close_cb</code> handler is invoked.</p>
|
||
<p>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 sets the message of the dialog and the label
|
||
of the button, then shows the alert dialog.</p>
|
||
<p>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 user clicked on the close button, then it closes the current
|
||
page. Otherwise it does nothing.</p>
|
||
<p>When user press “Ctrl-q” or clicked on the quit menu, then
|
||
<code>quit_activated</code> handler is invoked. The handler sets
|
||
<code>is_quit</code> to true. The function <code>has_saved_all</code>
|
||
returns true if all the pages have been saved. If it is true, it calls
|
||
<code>tfe_application_quit</code> to quit the application. Otherwise, it
|
||
sets the message of the dialog and the label of the button, then shows
|
||
the alert dialog.</p>
|
||
<p>If the user clicked on the buttons on the alert dialog,
|
||
<code>alert_resoponse_cb</code> is invoked. It hides the dialog and
|
||
checks the <code>response_id</code>. If it is
|
||
<code>GTK_RESPONSE_ACCEPT</code>, which means user clicked on the quit
|
||
button, then it calls <code>tfe_application_quit</code> to quit the
|
||
application. Otherwise it does nothing.</p>
|
||
<p>The static variables <code>alert</code>, <code>lb_alert</code> and
|
||
<code>btn_accept</code> are set in the startup handler. And the signal
|
||
“close-request” and <code>dialog_close_cb</code> handler are
|
||
connected.</p>
|
||
<div class="sourceCode" id="cb18"><pre
|
||
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb18-1"><a href="#cb18-1"></a>gboolean</span>
|
||
<span id="cb18-2"><a href="#cb18-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="cb18-3"><a href="#cb18-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="cb18-4"><a href="#cb18-4"></a></span>
|
||
<span id="cb18-5"><a href="#cb18-5"></a> TfeTextView <span class="op">*</span>tv<span class="op">;</span></span>
|
||
<span id="cb18-6"><a href="#cb18-6"></a> GtkTextBuffer <span class="op">*</span>tb<span class="op">;</span></span>
|
||
<span id="cb18-7"><a href="#cb18-7"></a></span>
|
||
<span id="cb18-8"><a href="#cb18-8"></a> tv <span class="op">=</span> get_current_textview <span class="op">(</span>nb<span class="op">);</span></span>
|
||
<span id="cb18-9"><a href="#cb18-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="cb18-10"><a href="#cb18-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="cb18-11"><a href="#cb18-11"></a> <span class="cf">return</span> false<span class="op">;</span></span>
|
||
<span id="cb18-12"><a href="#cb18-12"></a> <span class="cf">else</span></span>
|
||
<span id="cb18-13"><a href="#cb18-13"></a> <span class="cf">return</span> true<span class="op">;</span></span>
|
||
<span id="cb18-14"><a href="#cb18-14"></a><span class="op">}</span></span>
|
||
<span id="cb18-15"><a href="#cb18-15"></a></span>
|
||
<span id="cb18-16"><a href="#cb18-16"></a>gboolean</span>
|
||
<span id="cb18-17"><a href="#cb18-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="cb18-18"><a href="#cb18-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="cb18-19"><a href="#cb18-19"></a></span>
|
||
<span id="cb18-20"><a href="#cb18-20"></a> <span class="dt">int</span> i<span class="op">,</span> n<span class="op">;</span></span>
|
||
<span id="cb18-21"><a href="#cb18-21"></a> GtkWidget <span class="op">*</span>scr<span class="op">;</span></span>
|
||
<span id="cb18-22"><a href="#cb18-22"></a> GtkWidget <span class="op">*</span>tv<span class="op">;</span></span>
|
||
<span id="cb18-23"><a href="#cb18-23"></a> GtkTextBuffer <span class="op">*</span>tb<span class="op">;</span></span>
|
||
<span id="cb18-24"><a href="#cb18-24"></a></span>
|
||
<span id="cb18-25"><a href="#cb18-25"></a> n <span class="op">=</span> gtk_notebook_get_n_pages <span class="op">(</span>nb<span class="op">);</span></span>
|
||
<span id="cb18-26"><a href="#cb18-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="cb18-27"><a href="#cb18-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="cb18-28"><a href="#cb18-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="cb18-29"><a href="#cb18-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="cb18-30"><a href="#cb18-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="cb18-31"><a href="#cb18-31"></a> <span class="cf">return</span> false<span class="op">;</span></span>
|
||
<span id="cb18-32"><a href="#cb18-32"></a> <span class="op">}</span></span>
|
||
<span id="cb18-33"><a href="#cb18-33"></a> <span class="cf">return</span> true<span class="op">;</span></span>
|
||
<span id="cb18-34"><a href="#cb18-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>11-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-33: <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="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> <span class="dt">void</span></span>
|
||
<span id="cb19-2"><a href="#cb19-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="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> 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="cb19-5"><a href="#cb19-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="cb19-6"><a href="#cb19-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="cb20"><pre
|
||
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb20-1"><a href="#cb20-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||
<span id="cb20-2"><a href="#cb20-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="cb20-3"><a href="#cb20-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="cb20-4"><a href="#cb20-4"></a> GtkWidget <span class="op">*</span>scr<span class="op">;</span></span>
|
||
<span id="cb20-5"><a href="#cb20-5"></a> GtkWidget <span class="op">*</span>label<span class="op">;</span></span>
|
||
<span id="cb20-6"><a href="#cb20-6"></a> GFile <span class="op">*</span>file<span class="op">;</span></span>
|
||
<span id="cb20-7"><a href="#cb20-7"></a> <span class="dt">char</span> <span class="op">*</span>filename<span class="op">;</span></span>
|
||
<span id="cb20-8"><a href="#cb20-8"></a></span>
|
||
<span id="cb20-9"><a href="#cb20-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="cb20-10"><a href="#cb20-10"></a> <span class="cf">return</span><span class="op">;</span></span>
|
||
<span id="cb20-11"><a href="#cb20-11"></a> file <span class="op">=</span> tfe_text_view_get_file <span class="op">(</span>tv<span class="op">);</span></span>
|
||
<span id="cb20-12"><a href="#cb20-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="cb20-13"><a href="#cb20-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="cb20-14"><a href="#cb20-14"></a> filename <span class="op">=</span> g_file_get_basename <span class="op">(</span>file<span class="op">);</span></span>
|
||
<span id="cb20-15"><a href="#cb20-15"></a> g_object_unref <span class="op">(</span>file<span class="op">);</span></span>
|
||
<span id="cb20-16"><a href="#cb20-16"></a> <span class="op">}</span> <span class="cf">else</span></span>
|
||
<span id="cb20-17"><a href="#cb20-17"></a> filename <span class="op">=</span> get_untitled <span class="op">();</span></span>
|
||
<span id="cb20-18"><a href="#cb20-18"></a> label <span class="op">=</span> gtk_label_new <span class="op">(</span>filename<span class="op">);</span></span>
|
||
<span id="cb20-19"><a href="#cb20-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="cb20-20"><a href="#cb20-20"></a><span class="op">}</span></span>
|
||
<span id="cb20-21"><a href="#cb20-21"></a></span>
|
||
<span id="cb20-22"><a href="#cb20-22"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||
<span id="cb20-23"><a href="#cb20-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="cb20-24"><a href="#cb20-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="cb20-25"><a href="#cb20-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="cb20-26"><a href="#cb20-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="cb20-27"><a href="#cb20-27"></a> GtkWidget <span class="op">*</span>label<span class="op">;</span></span>
|
||
<span id="cb20-28"><a href="#cb20-28"></a> <span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span>filename<span class="op">;</span></span>
|
||
<span id="cb20-29"><a href="#cb20-29"></a> <span class="dt">char</span> <span class="op">*</span>text<span class="op">;</span></span>
|
||
<span id="cb20-30"><a href="#cb20-30"></a></span>
|
||
<span id="cb20-31"><a href="#cb20-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="cb20-32"><a href="#cb20-32"></a> <span class="cf">return</span><span class="op">;</span></span>
|
||
<span id="cb20-33"><a href="#cb20-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="cb20-34"><a href="#cb20-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="cb20-35"><a href="#cb20-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="cb20-36"><a href="#cb20-36"></a> label <span class="op">=</span> gtk_label_new <span class="op">(</span>text<span class="op">);</span></span>
|
||
<span id="cb20-37"><a href="#cb20-37"></a> g_free <span class="op">(</span>text<span class="op">);</span></span>
|
||
<span id="cb20-38"><a href="#cb20-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="cb20-39"><a href="#cb20-39"></a> <span class="op">}</span> <span class="cf">else</span></span>
|
||
<span id="cb20-40"><a href="#cb20-40"></a> file_changed_cb <span class="op">(</span>tv<span class="op">);</span></span>
|
||
<span id="cb20-41"><a href="#cb20-41"></a><span class="op">}</span></span></code></pre></div>
|
||
<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, there’s no page corresponds to <code>tv</code>. Then, it isn’t
|
||
necessary 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
|
||
unrefs <code>file</code>.</li>
|
||
<li>16-17: Otherwise, <code>file</code> is probably NULL and it assigns
|
||
“Untitled” related name 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 asterisk prepended text.</li>
|
||
<li>39-40: Otherwise the modified bit is off. It is because content is
|
||
saved. It calls <code>file_changed_cb</code> and resets the filename,
|
||
that means it leaves out the asterisk.</li>
|
||
</ul>
|
||
<h2 id="font">Font</h2>
|
||
<h3 id="gtkfontbutton-and-gtkfontchooser">GtkFontButton and
|
||
GtkFontChooser</h3>
|
||
<p>The GtkFontButton is a button which displays the current font. 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 and its signal “font-set” is initialized in the
|
||
application startup process.</p>
|
||
<div class="sourceCode" id="cb21"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||
<span id="cb21-2"><a href="#cb21-2" aria-hidden="true" tabindex="-1"></a>font_set_cb <span class="op">(</span>GtkFontButton <span class="op">*</span>fontbtn<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb21-3"><a href="#cb21-3" aria-hidden="true" tabindex="-1"></a> GtkWindow <span class="op">*</span>win <span class="op">=</span> GTK_WINDOW <span class="op">(</span>user_data<span class="op">);</span></span>
|
||
<span id="cb21-4"><a href="#cb21-4" aria-hidden="true" tabindex="-1"></a> PangoFontDescription <span class="op">*</span>pango_font_desc<span class="op">;</span></span>
|
||
<span id="cb21-5"><a href="#cb21-5" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb21-6"><a href="#cb21-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="cb21-7"><a href="#cb21-7" aria-hidden="true" tabindex="-1"></a> set_font_for_display_with_pango_font_desc <span class="op">(</span>win<span class="op">,</span> pango_font_desc<span class="op">);</span></span>
|
||
<span id="cb21-8"><a href="#cb21-8" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
|
||
<span id="cb21-9"><a href="#cb21-9" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb21-10"><a href="#cb21-10" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||
<span id="cb21-11"><a href="#cb21-11" 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="cb21-12"><a href="#cb21-12" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb21-13"><a href="#cb21-13" aria-hidden="true" tabindex="-1"></a> <span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb21-14"><a href="#cb21-14" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb21-15"><a href="#cb21-15" 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="cb21-16"><a href="#cb21-16" 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> win<span class="op">);</span></span>
|
||
<span id="cb21-17"><a href="#cb21-17" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb21-18"><a href="#cb21-18" aria-hidden="true" tabindex="-1"></a> <span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb21-19"><a href="#cb21-19" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb21-20"><a href="#cb21-20" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
|
||
<p>In the startup handler, set the variable <code>fontbtn</code> to
|
||
point the GtkFontButton object. Then connect the “font-set” signal to
|
||
<code>font_set_cb</code> handler. The signal “font-set” is emitted when
|
||
the user selects a font.</p>
|
||
<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.</p>
|
||
<p>Another function <code>gtk_font_chooser_get_font</code> returns a
|
||
font name which includes family, style, weight and size. I thought it
|
||
might be able to be applied to tfe editor. The font name can be used to
|
||
the <code>font</code> property of GtkTextTag as it is. But it can’t be
|
||
used to the CSS without converting the string to fit. CSS is appropriate
|
||
to change the font of entire text in all the buffers. I think GtkTextTag
|
||
is less appropriate. If you know a good solution, please post it to <a
|
||
href="https://github.com/ToshioCP/Gtk4-tutorial/issues">issue</a> and
|
||
let me know.</p>
|
||
<p>It takes many codes to set the CSS from the PangoFontDescription so
|
||
the task is left to the function
|
||
<code>set_font_for_display_with_pango_font_desc</code>.</p>
|
||
<h3 id="css-and-pango">CSS and Pango</h3>
|
||
<p>A new file <code>css.c</code> is made for functions related to
|
||
CSS.</p>
|
||
<div class="sourceCode" id="cb22"><pre
|
||
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb22-1"><a href="#cb22-1"></a><span class="pp">#include </span><span class="im">"tfe.h"</span></span>
|
||
<span id="cb22-2"><a href="#cb22-2"></a></span>
|
||
<span id="cb22-3"><a href="#cb22-3"></a><span class="dt">void</span></span>
|
||
<span id="cb22-4"><a href="#cb22-4"></a>set_css_for_display <span class="op">(</span>GtkWindow <span class="op">*</span>win<span class="op">,</span> <span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span>css<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb22-5"><a href="#cb22-5"></a> GdkDisplay <span class="op">*</span>display<span class="op">;</span></span>
|
||
<span id="cb22-6"><a href="#cb22-6"></a></span>
|
||
<span id="cb22-7"><a href="#cb22-7"></a> display <span class="op">=</span> gtk_widget_get_display <span class="op">(</span>GTK_WIDGET <span class="op">(</span>win<span class="op">));</span></span>
|
||
<span id="cb22-8"><a href="#cb22-8"></a> GtkCssProvider <span class="op">*</span>provider <span class="op">=</span> gtk_css_provider_new <span class="op">();</span></span>
|
||
<span id="cb22-9"><a href="#cb22-9"></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="cb22-10"><a href="#cb22-10"></a> gtk_style_context_add_provider_for_display <span class="op">(</span>display<span class="op">,</span> GTK_STYLE_PROVIDER <span class="op">(</span>provider<span class="op">),</span> GTK_STYLE_PROVIDER_PRIORITY_USER<span class="op">);</span></span>
|
||
<span id="cb22-11"><a href="#cb22-11"></a><span class="op">}</span></span>
|
||
<span id="cb22-12"><a href="#cb22-12"></a></span>
|
||
<span id="cb22-13"><a href="#cb22-13"></a><span class="dt">void</span></span>
|
||
<span id="cb22-14"><a href="#cb22-14"></a>set_font_for_display <span class="op">(</span>GtkWindow <span class="op">*</span>win<span class="op">,</span> <span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span>fontfamily<span class="op">,</span> <span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span>fontstyle<span class="op">,</span> <span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span>fontweight<span class="op">,</span> <span class="dt">int</span> fontsize<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb22-15"><a href="#cb22-15"></a> <span class="dt">char</span> <span class="op">*</span>textview_css<span class="op">;</span></span>
|
||
<span id="cb22-16"><a href="#cb22-16"></a></span>
|
||
<span id="cb22-17"><a href="#cb22-17"></a> textview_css <span class="op">=</span> g_strdup_printf <span class="op">(</span><span class="st">"textview {padding: 10px; font-family: </span><span class="sc">\"</span><span class="st">%s</span><span class="sc">\"</span><span class="st">; font-style: %s; font-weight: %s; font-size: %dpt;}"</span><span class="op">,</span></span>
|
||
<span id="cb22-18"><a href="#cb22-18"></a> fontfamily<span class="op">,</span> fontstyle<span class="op">,</span> fontweight<span class="op">,</span> fontsize<span class="op">);</span></span>
|
||
<span id="cb22-19"><a href="#cb22-19"></a> set_css_for_display <span class="op">(</span>win<span class="op">,</span> textview_css<span class="op">);</span></span>
|
||
<span id="cb22-20"><a href="#cb22-20"></a> g_free <span class="op">(</span>textview_css<span class="op">);</span></span>
|
||
<span id="cb22-21"><a href="#cb22-21"></a><span class="op">}</span> </span>
|
||
<span id="cb22-22"><a href="#cb22-22"></a></span>
|
||
<span id="cb22-23"><a href="#cb22-23"></a><span class="dt">void</span></span>
|
||
<span id="cb22-24"><a href="#cb22-24"></a>set_font_for_display_with_pango_font_desc <span class="op">(</span>GtkWindow <span class="op">*</span>win<span class="op">,</span> PangoFontDescription <span class="op">*</span>pango_font_desc<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb22-25"><a href="#cb22-25"></a> PangoStyle pango_style<span class="op">;</span></span>
|
||
<span id="cb22-26"><a href="#cb22-26"></a> PangoWeight pango_weight<span class="op">;</span> </span>
|
||
<span id="cb22-27"><a href="#cb22-27"></a> <span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span>family<span class="op">;</span></span>
|
||
<span id="cb22-28"><a href="#cb22-28"></a> <span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span>style<span class="op">;</span></span>
|
||
<span id="cb22-29"><a href="#cb22-29"></a> <span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span>weight<span class="op">;</span></span>
|
||
<span id="cb22-30"><a href="#cb22-30"></a> <span class="dt">int</span> fontsize<span class="op">;</span></span>
|
||
<span id="cb22-31"><a href="#cb22-31"></a></span>
|
||
<span id="cb22-32"><a href="#cb22-32"></a> family <span class="op">=</span> pango_font_description_get_family <span class="op">(</span>pango_font_desc<span class="op">);</span></span>
|
||
<span id="cb22-33"><a href="#cb22-33"></a> 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="cb22-34"><a href="#cb22-34"></a> <span class="cf">switch</span> <span class="op">(</span>pango_style<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb22-35"><a href="#cb22-35"></a> <span class="cf">case</span> PANGO_STYLE_NORMAL<span class="op">:</span></span>
|
||
<span id="cb22-36"><a href="#cb22-36"></a> style <span class="op">=</span> <span class="st">"normal"</span><span class="op">;</span></span>
|
||
<span id="cb22-37"><a href="#cb22-37"></a> <span class="cf">break</span><span class="op">;</span></span>
|
||
<span id="cb22-38"><a href="#cb22-38"></a> <span class="cf">case</span> PANGO_STYLE_ITALIC<span class="op">:</span></span>
|
||
<span id="cb22-39"><a href="#cb22-39"></a> style <span class="op">=</span> <span class="st">"italic"</span><span class="op">;</span></span>
|
||
<span id="cb22-40"><a href="#cb22-40"></a> <span class="cf">break</span><span class="op">;</span></span>
|
||
<span id="cb22-41"><a href="#cb22-41"></a> <span class="cf">case</span> PANGO_STYLE_OBLIQUE<span class="op">:</span></span>
|
||
<span id="cb22-42"><a href="#cb22-42"></a> style <span class="op">=</span> <span class="st">"oblique"</span><span class="op">;</span></span>
|
||
<span id="cb22-43"><a href="#cb22-43"></a> <span class="cf">break</span><span class="op">;</span></span>
|
||
<span id="cb22-44"><a href="#cb22-44"></a> <span class="cf">default</span><span class="op">:</span></span>
|
||
<span id="cb22-45"><a href="#cb22-45"></a> style <span class="op">=</span> <span class="st">"normal"</span><span class="op">;</span></span>
|
||
<span id="cb22-46"><a href="#cb22-46"></a> <span class="cf">break</span><span class="op">;</span></span>
|
||
<span id="cb22-47"><a href="#cb22-47"></a> <span class="op">}</span></span>
|
||
<span id="cb22-48"><a href="#cb22-48"></a> 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="cb22-49"><a href="#cb22-49"></a> <span class="cf">switch</span> <span class="op">(</span>pango_weight<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb22-50"><a href="#cb22-50"></a> <span class="cf">case</span> PANGO_WEIGHT_THIN<span class="op">:</span></span>
|
||
<span id="cb22-51"><a href="#cb22-51"></a> weight <span class="op">=</span> <span class="st">"100"</span><span class="op">;</span></span>
|
||
<span id="cb22-52"><a href="#cb22-52"></a> <span class="cf">break</span><span class="op">;</span></span>
|
||
<span id="cb22-53"><a href="#cb22-53"></a> <span class="cf">case</span> PANGO_WEIGHT_ULTRALIGHT<span class="op">:</span></span>
|
||
<span id="cb22-54"><a href="#cb22-54"></a> weight <span class="op">=</span> <span class="st">"200"</span><span class="op">;</span></span>
|
||
<span id="cb22-55"><a href="#cb22-55"></a> <span class="cf">break</span><span class="op">;</span></span>
|
||
<span id="cb22-56"><a href="#cb22-56"></a> <span class="cf">case</span> PANGO_WEIGHT_LIGHT<span class="op">:</span></span>
|
||
<span id="cb22-57"><a href="#cb22-57"></a> weight <span class="op">=</span> <span class="st">"300"</span><span class="op">;</span></span>
|
||
<span id="cb22-58"><a href="#cb22-58"></a> <span class="cf">break</span><span class="op">;</span></span>
|
||
<span id="cb22-59"><a href="#cb22-59"></a> <span class="cf">case</span> PANGO_WEIGHT_SEMILIGHT<span class="op">:</span></span>
|
||
<span id="cb22-60"><a href="#cb22-60"></a> weight <span class="op">=</span> <span class="st">"350"</span><span class="op">;</span></span>
|
||
<span id="cb22-61"><a href="#cb22-61"></a> <span class="cf">break</span><span class="op">;</span></span>
|
||
<span id="cb22-62"><a href="#cb22-62"></a> <span class="cf">case</span> PANGO_WEIGHT_BOOK<span class="op">:</span></span>
|
||
<span id="cb22-63"><a href="#cb22-63"></a> weight <span class="op">=</span> <span class="st">"380"</span><span class="op">;</span></span>
|
||
<span id="cb22-64"><a href="#cb22-64"></a> <span class="cf">break</span><span class="op">;</span></span>
|
||
<span id="cb22-65"><a href="#cb22-65"></a> <span class="cf">case</span> PANGO_WEIGHT_NORMAL<span class="op">:</span></span>
|
||
<span id="cb22-66"><a href="#cb22-66"></a> weight <span class="op">=</span> <span class="st">"400"</span><span class="op">;</span> <span class="co">/* or "normal" */</span></span>
|
||
<span id="cb22-67"><a href="#cb22-67"></a> <span class="cf">break</span><span class="op">;</span></span>
|
||
<span id="cb22-68"><a href="#cb22-68"></a> <span class="cf">case</span> PANGO_WEIGHT_MEDIUM<span class="op">:</span></span>
|
||
<span id="cb22-69"><a href="#cb22-69"></a> weight <span class="op">=</span> <span class="st">"500"</span><span class="op">;</span></span>
|
||
<span id="cb22-70"><a href="#cb22-70"></a> <span class="cf">break</span><span class="op">;</span></span>
|
||
<span id="cb22-71"><a href="#cb22-71"></a> <span class="cf">case</span> PANGO_WEIGHT_SEMIBOLD<span class="op">:</span></span>
|
||
<span id="cb22-72"><a href="#cb22-72"></a> weight <span class="op">=</span> <span class="st">"600"</span><span class="op">;</span></span>
|
||
<span id="cb22-73"><a href="#cb22-73"></a> <span class="cf">break</span><span class="op">;</span></span>
|
||
<span id="cb22-74"><a href="#cb22-74"></a> <span class="cf">case</span> PANGO_WEIGHT_BOLD<span class="op">:</span></span>
|
||
<span id="cb22-75"><a href="#cb22-75"></a> weight <span class="op">=</span> <span class="st">"700"</span><span class="op">;</span> <span class="co">/* or "bold" */</span></span>
|
||
<span id="cb22-76"><a href="#cb22-76"></a> <span class="cf">break</span><span class="op">;</span></span>
|
||
<span id="cb22-77"><a href="#cb22-77"></a> <span class="cf">case</span> PANGO_WEIGHT_ULTRABOLD<span class="op">:</span></span>
|
||
<span id="cb22-78"><a href="#cb22-78"></a> weight <span class="op">=</span> <span class="st">"800"</span><span class="op">;</span></span>
|
||
<span id="cb22-79"><a href="#cb22-79"></a> <span class="cf">break</span><span class="op">;</span></span>
|
||
<span id="cb22-80"><a href="#cb22-80"></a> <span class="cf">case</span> PANGO_WEIGHT_HEAVY<span class="op">:</span></span>
|
||
<span id="cb22-81"><a href="#cb22-81"></a> weight <span class="op">=</span> <span class="st">"900"</span><span class="op">;</span></span>
|
||
<span id="cb22-82"><a href="#cb22-82"></a> <span class="cf">break</span><span class="op">;</span></span>
|
||
<span id="cb22-83"><a href="#cb22-83"></a> <span class="cf">case</span> PANGO_WEIGHT_ULTRAHEAVY<span class="op">:</span></span>
|
||
<span id="cb22-84"><a href="#cb22-84"></a> weight <span class="op">=</span> <span class="st">"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="cb22-85"><a href="#cb22-85"></a> <span class="cf">break</span><span class="op">;</span></span>
|
||
<span id="cb22-86"><a href="#cb22-86"></a> <span class="cf">default</span><span class="op">:</span></span>
|
||
<span id="cb22-87"><a href="#cb22-87"></a> weight <span class="op">=</span> <span class="st">"normal"</span><span class="op">;</span></span>
|
||
<span id="cb22-88"><a href="#cb22-88"></a> <span class="cf">break</span><span class="op">;</span></span>
|
||
<span id="cb22-89"><a href="#cb22-89"></a> <span class="op">}</span></span>
|
||
<span id="cb22-90"><a href="#cb22-90"></a> fontsize <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="cb22-91"><a href="#cb22-91"></a> set_font_for_display <span class="op">(</span>win<span class="op">,</span> family<span class="op">,</span> style<span class="op">,</span> weight<span class="op">,</span> fontsize<span class="op">);</span></span>
|
||
<span id="cb22-92"><a href="#cb22-92"></a><span class="op">}</span></span></code></pre></div>
|
||
<ul>
|
||
<li>3-11: <code>set_css_for_display</code>. This function sets CSS for
|
||
GdkDisplay. The content of the function is the same as the part of
|
||
startup handler in the previous version of
|
||
<code>tfeapplication.c</code>.</li>
|
||
<li>13-20: <code>set_font_for_display</code>. This function sets CSS
|
||
with font-family, font-style, font-weight and font-size.
|
||
<ul>
|
||
<li>font-family is a name of a font. For example, sans-serif, monospace,
|
||
Helvetica and “Noto Sans” are font-family. It is recommended to quote
|
||
font family names that contains white space, digits, or punctuation
|
||
characters other than hyphens.</li>
|
||
<li>font-style is one of normal, italic and oblique.</li>
|
||
<li>font-weight specifies the thickness of a font. It is normal or bold.
|
||
It can be specified with a number between 100 and 900. Normal is the
|
||
same as 400. Bold is 700.</li>
|
||
<li>font-size specifies the size of a font. Small, medium, large and
|
||
12pt are font-size.</li>
|
||
</ul></li>
|
||
<li>17: Makes CSS text. The function <code>g_strdup_printf</code>
|
||
creates a new string with printf-like formatting.</li>
|
||
<li>23-92: <code>set_font_for_display_with_pango_font_desc</code>. This
|
||
function takes out font-family, font-style, font-weight and font-size
|
||
from the PangoFontDescription object and calls
|
||
<code>set_font</code>for_display`.</li>
|
||
<li>32: Gets the font-family of <code>pango_font_desc</code>.</li>
|
||
<li>33-47: Gets the font-style of <code>pango_font_desc</code>. The
|
||
functions <code>pango_font_description_get_style</code> returns an
|
||
enumerated value.</li>
|
||
<li>48-89: Gets the font-weight of <code>pango_font_desc</code>. The
|
||
function <code>pango_font_description_get_weight</code> returns an
|
||
enumerated value. They corresponds to the numbers from 100 to 900.</li>
|
||
<li>90: Gets the font-size of <code>pango_font_desc</code>. The function
|
||
<code>pango_font_description_get_size</code> returns the size of a font.
|
||
The unit of this size is (1/PANGO_SCALE)pt. If the font size is 10pt,
|
||
the function returns 10<em>PANGO_SCALE. PANGO_SCALE is defined as 1024.
|
||
Therefore, 10</em>PANGO_SCALE is 10240.</li>
|
||
<li>91: calls <code>set_font_for_display</code> to set CSS for the
|
||
GdkDisplay.</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>The coding with GSettings object is simple and easy. However, it is 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 may contain, 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="cb23"><pre
|
||
class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><span id="cb23-1"><a href="#cb23-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="cb23-2"><a href="#cb23-2"></a><<span class="kw">schemalist</span>></span>
|
||
<span id="cb23-3"><a href="#cb23-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="cb23-4"><a href="#cb23-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="cb23-5"><a href="#cb23-5"></a> <<span class="kw">default</span>>'Monospace 12'</<span class="kw">default</span>></span>
|
||
<span id="cb23-6"><a href="#cb23-6"></a> <<span class="kw">summary</span>>Font</<span class="kw">summary</span>></span>
|
||
<span id="cb23-7"><a href="#cb23-7"></a> <<span class="kw">description</span>>The font to be used for textview.</<span class="kw">description</span>></span>
|
||
<span id="cb23-8"><a href="#cb23-8"></a> </<span class="kw">key</span>></span>
|
||
<span id="cb23-9"><a href="#cb23-9"></a> </<span class="kw">schema</span>></span>
|
||
<span id="cb23-10"><a href="#cb23-10"></a></<span class="kw">schemalist</span>></span></code></pre></div>
|
||
<ul>
|
||
<li>4: The type attribute is “s”. It is <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 <a
|
||
href="https://docs.gtk.org/glib/struct.VariantType.html">GLib API
|
||
Reference, VarientType</a>.</p>
|
||
<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>Then, 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 whether the value of <code>button-mode</code>
|
||
changes.</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 is run again, it will appear as
|
||
an advanced mode calculator.</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="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>>The 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>
|
||
<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 specified with <a
|
||
href="https://docs.gtk.org/glib/struct.VariantType.html">GLib API
|
||
Reference, VariantType</a>.</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> 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>This is because GSettings object searches
|
||
<code>GSETTINGS_SCHEMA_DIR</code> for
|
||
<code>gschemas.compiled</code>.</p>
|
||
<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>. Most common directory is
|
||
<code>/usr/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.</li>
|
||
</ol>
|
||
<h3 id="meson.build">Meson.build</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="gsettings-object-and-g_settings_bind">GSettings object and
|
||
g_settings_bind</h3>
|
||
<p>Write gsettings related codes to `tfeapplication.c’.</p>
|
||
<div class="sourceCode" id="cb34"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb34-1"><a href="#cb34-1" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb34-2"><a href="#cb34-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="cb34-3"><a href="#cb34-3" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb34-4"><a href="#cb34-4" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb34-5"><a href="#cb34-5" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span></span>
|
||
<span id="cb34-6"><a href="#cb34-6" aria-hidden="true" tabindex="-1"></a>tfe_application_quit <span class="op">(</span>GtkWindow <span class="op">*</span>win<span class="op">)</span> <span class="op">{</span></span>
|
||
<span id="cb34-7"><a href="#cb34-7" aria-hidden="true" tabindex="-1"></a> <span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb34-8"><a href="#cb34-8" aria-hidden="true" tabindex="-1"></a> g_clear_object <span class="op">(&</span>settings<span class="op">);</span></span>
|
||
<span id="cb34-9"><a href="#cb34-9" aria-hidden="true" tabindex="-1"></a> <span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb34-10"><a href="#cb34-10" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
|
||
<span id="cb34-11"><a href="#cb34-11" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb34-12"><a href="#cb34-12" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
||
<span id="cb34-13"><a href="#cb34-13" 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="cb34-14"><a href="#cb34-14" aria-hidden="true" tabindex="-1"></a> <span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb34-15"><a href="#cb34-15" 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="cb34-16"><a href="#cb34-16" 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="cb34-17"><a href="#cb34-17" aria-hidden="true" tabindex="-1"></a> <span class="op">...</span> <span class="op">...</span></span>
|
||
<span id="cb34-18"><a href="#cb34-18" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
|
||
<p>Static variable <code>settings</code> keeps a pointer to GSettings
|
||
instance. Before application quits, the application releases the
|
||
GSettings instance. The function <code>g_clear_object</code> is
|
||
used.</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>You need to make an effort to understand GSettings concept, but
|
||
coding is very simple. Just create a GSettings object and bind it to a
|
||
property of an object.</p>
|
||
<h2 id="installation">Installation</h2>
|
||
<p>It is a good idea to install your application in
|
||
<code>$HOME/local/bin</code> directory if you have installed GTK 4 from
|
||
the source (See Section 2). Then you need to put
|
||
<code>--prefix=$HOME/local</code> option to meson like this.</p>
|
||
<pre><code>$ meson --prefix=$HOME/local _build</code></pre>
|
||
<p>If you’ve installed GTK 4 from the distribution package,
|
||
<code>--prefix</code> option isn’t necessary. You just install
|
||
<code>tfe</code> to the default bin directory like
|
||
<code>/usr/local/bin</code>.</p>
|
||
<p>Modify <code>meson.build</code> and add install option and set it
|
||
true in executable function.</p>
|
||
<pre><code>executable('tfe', sourcefiles, resources, dependencies: gtkdep, export_dynamic: true, install: true)</code></pre>
|
||
<p>You can install your application by:</p>
|
||
<pre><code>$ ninja -C _build install</code></pre>
|
||
<p>However, you need to do one more thing. Copy your XML file to
|
||
<code>$HOME/local/share/glib-2.0/schemas/</code>, which is specified in
|
||
<code>GSETTINGS_SCHEMA_DIR</code> environment variable, and run
|
||
<code>glib-compile-schemas</code> on that directory.</p>
|
||
<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)</code></pre>
|
||
<ul>
|
||
<li>get_option: This function returns the value of build options. The
|
||
default value of the option ‘prefix’ is “/usr/local”, but it is
|
||
“$HOME/local” because we have run meson with prefix option. The default
|
||
value of the option ‘datadir’ is “share”. The operator ‘/’ connects the
|
||
strings with ‘/’ separator. So,
|
||
<code>$HOME/local/share/glib-2.0/schemas</code> is assigned to the
|
||
variable <code>schema_dir</code>.</li>
|
||
<li>install_data: This function installs the data to the install
|
||
directory.</li>
|
||
</ul>
|
||
<p>Meson can run a post compile script.</p>
|
||
<pre><code>meson.add_install_script('glib-compile-schemas', schema_dir)</code></pre>
|
||
<p>This method runs ‘glib-compile-schemas’ with an argument
|
||
<code>schema_dir</code>. The following is <code>meson.build</code>.</p>
|
||
<div class="sourceCode" id="cb40"><pre
|
||
class="sourceCode numberSource numberLines"><code class="sourceCode"><span id="cb40-1"><a href="#cb40-1"></a>project('tfe', 'c')</span>
|
||
<span id="cb40-2"><a href="#cb40-2"></a></span>
|
||
<span id="cb40-3"><a href="#cb40-3"></a>gtkdep = dependency('gtk4')</span>
|
||
<span id="cb40-4"><a href="#cb40-4"></a></span>
|
||
<span id="cb40-5"><a href="#cb40-5"></a>gnome=import('gnome')</span>
|
||
<span id="cb40-6"><a href="#cb40-6"></a>resources = gnome.compile_resources('resources','tfe.gresource.xml')</span>
|
||
<span id="cb40-7"><a href="#cb40-7"></a>gnome.compile_schemas(build_by_default: true, depend_files: 'com.github.ToshioCP.tfe.gschema.xml')</span>
|
||
<span id="cb40-8"><a href="#cb40-8"></a></span>
|
||
<span id="cb40-9"><a href="#cb40-9"></a>sourcefiles=files('tfeapplication.c', 'tfenotebook.c', 'css.c', '../tfetextview/tfetextview.c')</span>
|
||
<span id="cb40-10"><a href="#cb40-10"></a></span>
|
||
<span id="cb40-11"><a href="#cb40-11"></a>executable('tfe', sourcefiles, resources, dependencies: gtkdep, export_dynamic: true, install: true)</span>
|
||
<span id="cb40-12"><a href="#cb40-12"></a></span>
|
||
<span id="cb40-13"><a href="#cb40-13"></a>schema_dir = get_option('prefix') / get_option('datadir') / 'glib-2.0/schemas/'</span>
|
||
<span id="cb40-14"><a href="#cb40-14"></a>install_data('com.github.ToshioCP.tfe.gschema.xml', install_dir: schema_dir)</span>
|
||
<span id="cb40-15"><a href="#cb40-15"></a>meson.add_install_script('glib-compile-schemas', schema_dir)</span></code></pre></div>
|
||
<p>Source files of <code>tfe</code> is under src/tfe6 directory. Copy
|
||
them to your temporary directory and try to compile and install.</p>
|
||
<pre><code>$ meson --prefix=$HOME/local _build
|
||
$ ninja -C _build
|
||
$ GSETTINGS_SCHEMA_DIR=_build:$GSETTINGS_SCHEMA_DIR _build/tfe
|
||
$ ninja -C _build install
|
||
$ tfe
|
||
$ ls $HOME/local/bin
|
||
... ...
|
||
... tfe
|
||
... ...
|
||
$ ls $HOME/local/share/glib-2.0/schemas
|
||
com.github.ToshioCP.tfe.gschema.xml
|
||
gschema.dtd
|
||
gschemas.compiled
|
||
... ...</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>
|