mirror of
https://github.com/ToshioCP/Gtk4-tutorial.git
synced 2025-01-12 20:03:28 +01:00
24 KiB
24 KiB
Up: Readme.md, Prev: Section 14, Next: Section 16
tfe5 source files
How to compile and execute tfe text editor.
First, source files are shown in the later subsections. How to download them is written at the end of the previous section.
The following is the instruction of compilation and execution.
- You need meson and ninja.
- Set necessary environment variables.
If you have installed gtk4 under the instruction in Section 2, type
. env.sh
to set the environment variables. - change your current directory to
src/tfe5
directory. - type
meson _build
for configuration. - type
ninja -C _build
for compilation. Then the applicationtfe
is build under the_build
directory. - type
_build/tfe
to execute it.
Then the window appears.
There are four buttons, New
, Open
, Save
and Close
.
- Click on
Open
button, then a FileChooserDialog appears. Choose a file in the list and click onOpen
button. Then the file is read and a new Notebook Page appears. - Edit the file and click on
Save
button, then the text is saved to the original file. - Click
Close
, then the Notebook Page disappears. - Click
Close
again, then theUntitle
Notebook Page disappears and at the same time the appication quits.
This is a very simple editor. It is a good practice for you to add more features.
meson.buld
1 project('tfe', 'c')
2
3 gtkdep = dependency('gtk4')
4
5 gnome=import('gnome')
6 resources = gnome.compile_resources('resources','tfe.gresource.xml')
7
8 sourcefiles=files('tfeapplication.c', 'tfenotebook.c', 'tfetextview.c')
9
10 executable('tfe', sourcefiles, resources, dependencies: gtkdep)
tfe.gresource.xml
1 <?xml version="1.0" encoding="UTF-8"?>
2 <gresources>
3 <gresource prefix="/com/github/ToshioCP/tfe">
4 <file>tfe.ui</file>
5 </gresource>
6 </gresources>
tfe.ui
1 <interface>
2 <object class="GtkApplicationWindow" id="win">
3 <property name="title">file editor</property>
4 <property name="default-width">600</property>
5 <property name="default-height">400</property>
6 <child>
7 <object class="GtkBox" id="boxv">
8 <property name="orientation">GTK_ORIENTATION_VERTICAL</property>
9 <child>
10 <object class="GtkBox" id="boxh">
11 <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
12 <child>
13 <object class="GtkLabel" id="dmy1">
14 <property name="width-chars">10</property>
15 </object>
16 </child>
17 <child>
18 <object class="GtkButton" id="btnn">
19 <property name="label">_New</property>
20 <property name="use-underline">TRUE</property>
21 </object>
22 </child>
23 <child>
24 <object class="GtkButton" id="btno">
25 <property name="label">_Open</property>
26 <property name="use-underline">TRUE</property>
27 </object>
28 </child>
29 <child>
30 <object class="GtkLabel" id="dmy2">
31 <property name="hexpand">TRUE</property>
32 </object>
33 </child>
34 <child>
35 <object class="GtkButton" id="btns">
36 <property name="label">_Save</property>
37 <property name="use-underline">TRUE</property>
38 </object>
39 </child>
40 <child>
41 <object class="GtkButton" id="btnc">
42 <property name="label">_Close</property>
43 <property name="use-underline">TRUE</property>
44 </object>
45 </child>
46 <child>
47 <object class="GtkLabel" id="dmy3">
48 <property name="width-chars">10</property>
49 </object>
50 </child>
51 </object>
52 </child>
53 <child>
54 <object class="GtkNotebook" id="nb">
55 <property name="scrollable">TRUE</property>
56 <property name="hexpand">TRUE</property>
57 <property name="vexpand">TRUE</property>
58 </object>
59 </child>
60 </object>
61 </child>
62 </object>
63 </interface>
64
tfe.h
1 #include <gtk/gtk.h>
2
3 #include "tfetextview.h"
4 #include "tfenotebook.h"
tfeapplication.c
1 #include "tfe.h"
2
3 static void
4 open_clicked (GtkWidget *btno, GtkNotebook *nb) {
5 notebook_page_open (nb);
6 }
7
8 static void
9 new_clicked (GtkWidget *btnn, GtkNotebook *nb) {
10 notebook_page_new (nb);
11 }
12
13 static void
14 save_clicked (GtkWidget *btns, GtkNotebook *nb) {
15 notebook_page_save (nb);
16 }
17
18 static void
19 close_clicked (GtkWidget *btnc, GtkNotebook *nb) {
20 GtkWidget *win;
21 GtkWidget *boxv;
22 gint i;
23
24 if (gtk_notebook_get_n_pages (nb) == 1) {
25 boxv = gtk_widget_get_parent (GTK_WIDGET (nb));
26 win = gtk_widget_get_parent (boxv);
27 gtk_window_destroy (GTK_WINDOW (win));
28 } else {
29 i = gtk_notebook_get_current_page (nb);
30 gtk_notebook_remove_page (GTK_NOTEBOOK (nb), i);
31 }
32 }
33
34 static void
35 tfe_activate (GApplication *application) {
36 GtkApplication *app = GTK_APPLICATION (application);
37 GtkWidget *win;
38 GtkWidget *boxv;
39 GtkNotebook *nb;
40
41 win = GTK_WIDGET (gtk_application_get_active_window (app));
42 boxv = gtk_window_get_child (GTK_WINDOW (win));
43 nb = GTK_NOTEBOOK (gtk_widget_get_last_child (boxv));
44
45 notebook_page_new (nb);
46 gtk_widget_show (GTK_WIDGET (win));
47 }
48
49 static void
50 tfe_open (GApplication *application, GFile ** files, gint n_files, const gchar *hint) {
51 GtkApplication *app = GTK_APPLICATION (application);
52 GtkWidget *win;
53 GtkWidget *boxv;
54 GtkNotebook *nb;
55 int i;
56
57 win = GTK_WIDGET (gtk_application_get_active_window (app));
58 boxv = gtk_window_get_child (GTK_WINDOW (win));
59 nb = GTK_NOTEBOOK (gtk_widget_get_last_child (boxv));
60
61 for (i = 0; i < n_files; i++)
62 notebook_page_new_with_file (nb, files[i]);
63 if (gtk_notebook_get_n_pages (nb) == 0)
64 notebook_page_new (nb);
65 gtk_widget_show (win);
66 }
67
68
69 static void
70 tfe_startup (GApplication *application) {
71 GtkApplication *app = GTK_APPLICATION (application);
72 GtkApplicationWindow *win;
73 GtkNotebook *nb;
74 GtkBuilder *build;
75 GtkButton *btno;
76 GtkButton *btnn;
77 GtkButton *btns;
78 GtkButton *btnc;
79
80 build = gtk_builder_new_from_resource ("/com/github/ToshioCP/tfe/tfe.ui");
81 win = GTK_APPLICATION_WINDOW (gtk_builder_get_object (build, "win"));
82 nb = GTK_NOTEBOOK (gtk_builder_get_object (build, "nb"));
83 gtk_window_set_application (GTK_WINDOW (win), app);
84 btno = GTK_BUTTON (gtk_builder_get_object (build, "btno"));
85 btnn = GTK_BUTTON (gtk_builder_get_object (build, "btnn"));
86 btns = GTK_BUTTON (gtk_builder_get_object (build, "btns"));
87 btnc = GTK_BUTTON (gtk_builder_get_object (build, "btnc"));
88 g_signal_connect (btno, "clicked", G_CALLBACK (open_clicked), nb);
89 g_signal_connect (btnn, "clicked", G_CALLBACK (new_clicked), nb);
90 g_signal_connect (btns, "clicked", G_CALLBACK (save_clicked), nb);
91 g_signal_connect (btnc, "clicked", G_CALLBACK (close_clicked), nb);
92 g_object_unref(build);
93
94 GdkDisplay *display;
95
96 display = gtk_widget_get_display (GTK_WIDGET (win));
97 GtkCssProvider *provider = gtk_css_provider_new ();
98 gtk_css_provider_load_from_data (provider, "textview {padding: 10px; font-family: monospace; font-size: 12pt;}", -1);
99 gtk_style_context_add_provider_for_display (display, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_USER);
100 }
101
102 int
103 main (int argc, char **argv) {
104 GtkApplication *app;
105 int stat;
106
107 app = gtk_application_new ("com.github.ToshioCP.tfe", G_APPLICATION_HANDLES_OPEN);
108
109 g_signal_connect (app, "startup", G_CALLBACK (tfe_startup), NULL);
110 g_signal_connect (app, "activate", G_CALLBACK (tfe_activate), NULL);
111 g_signal_connect (app, "open", G_CALLBACK (tfe_open), NULL);
112
113 stat =g_application_run (G_APPLICATION (app), argc, argv);
114 g_object_unref (app);
115 return stat;
116 }
117
tfenotebook.h
1 void
2 notebook_page_save(GtkNotebook *nb);
3
4 void
5 notebook_page_open (GtkNotebook *nb);
6
7 void
8 notebook_page_new_with_file (GtkNotebook *nb, GFile *file);
9
10 void
11 notebook_page_new (GtkNotebook *nb);
12
tfenotebook.c
1 #include "tfe.h"
2
3 /* The returned string should be freed with g_free() when no longer needed. */
4 static gchar*
5 get_untitled () {
6 static int c = -1;
7 if (++c == 0)
8 return g_strdup_printf("Untitled");
9 else
10 return g_strdup_printf ("Untitled%u", c);
11 }
12
13 static void
14 file_changed (TfeTextView *tv, GtkNotebook *nb) {
15 GFile *file;
16 char *filename;
17 GtkWidget *scr;
18 GtkWidget *label;
19
20 file = tfe_text_view_get_file (tv);
21 scr = gtk_widget_get_parent (GTK_WIDGET (tv));
22 if (G_IS_FILE (file))
23 filename = g_file_get_basename (file);
24 else
25 filename = get_untitled ();
26 label = gtk_label_new (filename);
27 gtk_notebook_set_tab_label (nb, scr, label);
28 g_object_unref (file);
29 g_free (filename);
30 }
31
32 /* Save the contents in the current page */
33 void
34 notebook_page_save(GtkNotebook *nb) {
35 gint i;
36 GtkWidget *scr;
37 GtkWidget *tv;
38
39 i = gtk_notebook_get_current_page (nb);
40 scr = gtk_notebook_get_nth_page (nb, i);
41 tv = gtk_scrolled_window_get_child (GTK_SCROLLED_WINDOW (scr));
42 tfe_text_view_save (TFE_TEXT_VIEW (tv));
43 }
44
45 static void
46 notebook_page_build (GtkNotebook *nb, GtkWidget *tv, char *filename) {
47 GtkWidget *scr;
48 GtkNotebookPage *nbp;
49 GtkWidget *lab;
50 gint i;
51 scr = gtk_scrolled_window_new ();
52
53 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);
54 lab = gtk_label_new (filename);
55 i = gtk_notebook_append_page (nb, scr, lab);
56 nbp = gtk_notebook_get_page (nb, scr);
57 g_object_set (nbp, "tab-expand", TRUE, NULL);
58 gtk_notebook_set_current_page (nb, i);
59 g_signal_connect (GTK_TEXT_VIEW (tv), "change-file", G_CALLBACK (file_changed), nb);
60 }
61
62 static void
63 open_response (TfeTextView *tv, gint response, GtkNotebook *nb) {
64 GFile *file;
65 char *filename;
66
67 if (response != TFE_OPEN_RESPONSE_SUCCESS) {
68 g_object_ref_sink (tv);
69 g_object_unref (tv);
70 }else if (! G_IS_FILE (file = tfe_text_view_get_file (tv))) {
71 g_object_ref_sink (tv);
72 g_object_unref (tv);
73 }else {
74 filename = g_file_get_basename (file);
75 g_object_unref (file);
76 notebook_page_build (nb, GTK_WIDGET (tv), filename);
77 }
78 }
79
80 void
81 notebook_page_open (GtkNotebook *nb) {
82 g_return_if_fail(GTK_IS_NOTEBOOK (nb));
83
84 GtkWidget *tv;
85
86 tv = tfe_text_view_new ();
87 g_signal_connect (TFE_TEXT_VIEW (tv), "open-response", G_CALLBACK (open_response), nb);
88 tfe_text_view_open (TFE_TEXT_VIEW (tv), gtk_widget_get_ancestor (GTK_WIDGET (nb), GTK_TYPE_WINDOW));
89 }
90
91 void
92 notebook_page_new_with_file (GtkNotebook *nb, GFile *file) {
93 g_return_if_fail(GTK_IS_NOTEBOOK (nb));
94 g_return_if_fail(G_IS_FILE (file));
95
96 GtkWidget *tv;
97 char *filename;
98
99 if ((tv = tfe_text_view_new_with_file (file)) == NULL)
100 return; /* read error */
101 filename = g_file_get_basename (file);
102 notebook_page_build (nb, tv, filename);
103 }
104
105 void
106 notebook_page_new (GtkNotebook *nb) {
107 g_return_if_fail(GTK_IS_NOTEBOOK (nb));
108
109 GtkWidget *tv;
110 char *filename;
111
112 tv = tfe_text_view_new ();
113 filename = get_untitled ();
114 notebook_page_build (nb, tv, filename);
115 }
116
tfetextview.h
1 #define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type ()
2 G_DECLARE_FINAL_TYPE (TfeTextView, tfe_text_view, TFE, TEXT_VIEW, GtkTextView)
3
4 /* "open-response" signal response */
5 enum
6 {
7 TFE_OPEN_RESPONSE_SUCCESS,
8 TFE_OPEN_RESPONSE_CANCEL,
9 TFE_OPEN_RESPONSE_ERROR
10 };
11
12 GFile *
13 tfe_text_view_get_file (TfeTextView *tv);
14
15 void
16 tfe_text_view_open (TfeTextView *tv, GtkWidget *win);
17
18 void
19 tfe_text_view_save (TfeTextView *tv);
20
21 void
22 tfe_text_view_saveas (TfeTextView *tv);
23
24 GtkWidget *
25 tfe_text_view_new_with_file (GFile *file);
26
27 GtkWidget *
28 tfe_text_view_new (void);
29
tfetextview.c
1 #include "tfe.h"
2
3 struct _TfeTextView
4 {
5 GtkTextView parent;
6 GFile *file;
7 };
8
9 G_DEFINE_TYPE (TfeTextView, tfe_text_view, GTK_TYPE_TEXT_VIEW);
10
11 enum {
12 CHANGE_FILE,
13 OPEN_RESPONSE,
14 NUMBER_OF_SIGNALS
15 };
16
17 static guint tfe_text_view_signals[NUMBER_OF_SIGNALS];
18
19 static void
20 tfe_text_view_dispose (GObject *gobject) {
21 TfeTextView *tv = TFE_TEXT_VIEW (gobject);
22
23 if (G_IS_FILE (tv->file))
24 g_clear_object (&tv->file);
25
26 G_OBJECT_CLASS (tfe_text_view_parent_class)->dispose (gobject);
27 }
28
29 static void
30 tfe_text_view_init (TfeTextView *tv) {
31 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
32
33 tv->file = NULL;
34 gtk_text_buffer_set_modified (tb, FALSE);
35 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR);
36 }
37
38 static void
39 tfe_text_view_class_init (TfeTextViewClass *class) {
40 GObjectClass *object_class = G_OBJECT_CLASS (class);
41
42 object_class->dispose = tfe_text_view_dispose;
43 tfe_text_view_signals[CHANGE_FILE] = g_signal_newv ("change-file",
44 G_TYPE_FROM_CLASS (class),
45 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
46 NULL /* closure */,
47 NULL /* accumulator */,
48 NULL /* accumulator data */,
49 NULL /* C marshaller */,
50 G_TYPE_NONE /* return_type */,
51 0 /* n_params */,
52 NULL /* param_types */);
53 GType param_types[] = {G_TYPE_INT};
54 tfe_text_view_signals[OPEN_RESPONSE] = g_signal_newv ("open-response",
55 G_TYPE_FROM_CLASS (class),
56 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
57 NULL /* closure */,
58 NULL /* accumulator */,
59 NULL /* accumulator data */,
60 NULL /* C marshaller */,
61 G_TYPE_NONE /* return_type */,
62 1 /* n_params */,
63 param_types);
64 }
65
66 GFile *
67 tfe_text_view_get_file (TfeTextView *tv) {
68 g_return_val_if_fail (TFE_IS_TEXT_VIEW (tv), NULL);
69
70 return g_file_dup (tv->file);
71 }
72
73 static void
74 open_dialog_response(GtkWidget *dialog, gint response, TfeTextView *tv) {
75 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
76 GFile *file;
77 char *contents;
78 gsize length;
79 GtkWidget *message_dialog;
80 GError *err = NULL;
81
82 if (response != GTK_RESPONSE_ACCEPT)
83 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_CANCEL);
84 else if (! G_IS_FILE (file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog))))
85 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_ERROR);
86 else if (! g_file_load_contents (file, NULL, &contents, &length, NULL, &err)) { /* read error */
87 if (G_IS_FILE (file))
88 g_object_unref (file);
89 message_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog), GTK_DIALOG_MODAL,
90 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
91 "%s.\n", err->message);
92 g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
93 gtk_widget_show (message_dialog);
94 g_error_free (err);
95 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_ERROR);
96 } else {
97 gtk_text_buffer_set_text (tb, contents, length);
98 g_free (contents);
99 if (G_IS_FILE (tv->file))
100 g_object_unref (tv->file);
101 tv->file = file;
102 gtk_text_buffer_set_modified (tb, FALSE);
103 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_SUCCESS);
104 }
105 gtk_window_destroy (GTK_WINDOW (dialog));
106 }
107
108 void
109 tfe_text_view_open (TfeTextView *tv, GtkWidget *win) {
110 g_return_if_fail (TFE_IS_TEXT_VIEW (tv));
111 g_return_if_fail (GTK_IS_WINDOW (win));
112
113 GtkWidget *dialog;
114
115 dialog = gtk_file_chooser_dialog_new ("Open file", GTK_WINDOW (win), GTK_FILE_CHOOSER_ACTION_OPEN,
116 "Cancel", GTK_RESPONSE_CANCEL,
117 "Open", GTK_RESPONSE_ACCEPT,
118 NULL);
119 g_signal_connect (dialog, "response", G_CALLBACK (open_dialog_response), tv);
120 gtk_widget_show (dialog);
121 }
122
123 static void
124 saveas_dialog_response (GtkWidget *dialog, gint response, TfeTextView *tv) {
125 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
126 GFile *file;
127
128 if (response == GTK_RESPONSE_ACCEPT) {
129 file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
130 if (G_IS_FILE(file)) {
131 tv->file = file;
132 gtk_text_buffer_set_modified (tb, TRUE);
133 g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
134 tfe_text_view_save (TFE_TEXT_VIEW (tv));
135 }
136 }
137 gtk_window_destroy (GTK_WINDOW (dialog));
138 }
139
140 void
141 tfe_text_view_save (TfeTextView *tv) {
142 g_return_if_fail (TFE_IS_TEXT_VIEW (tv));
143
144 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
145 GtkTextIter start_iter;
146 GtkTextIter end_iter;
147 gchar *contents;
148 GtkWidget *message_dialog;
149 GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW);
150 GError *err = NULL;
151
152 if (! gtk_text_buffer_get_modified (tb))
153 return; /* no necessary to save it */
154 else if (tv->file == NULL)
155 tfe_text_view_saveas (tv);
156 else {
157 gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter);
158 contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE);
159 if (g_file_replace_contents (tv->file, contents, strlen (contents), NULL, TRUE, G_FILE_CREATE_NONE, NULL, NULL, &err))
160 gtk_text_buffer_set_modified (tb, FALSE);
161 else {
162 /* It is possible that tv->file is broken. */
163 /* It is a good idea to set tv->file to NULL. */
164 if (G_IS_FILE (tv->file))
165 g_object_unref (tv->file);
166 tv->file =NULL;
167 g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
168 gtk_text_buffer_set_modified (tb, TRUE);
169 message_dialog = gtk_message_dialog_new (GTK_WINDOW (win), GTK_DIALOG_MODAL,
170 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
171 "%s.\n", err->message);
172 g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
173 gtk_widget_show (message_dialog);
174 g_error_free (err);
175 }
176 }
177 }
178
179 void
180 tfe_text_view_saveas (TfeTextView *tv) {
181 g_return_if_fail (TFE_IS_TEXT_VIEW (tv));
182
183 GtkWidget *dialog;
184 GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW);
185
186 dialog = gtk_file_chooser_dialog_new ("Save file", GTK_WINDOW (win), GTK_FILE_CHOOSER_ACTION_SAVE,
187 "_Cancel", GTK_RESPONSE_CANCEL,
188 "_Save", GTK_RESPONSE_ACCEPT,
189 NULL);
190 g_signal_connect (dialog, "response", G_CALLBACK (saveas_dialog_response), tv);
191 gtk_widget_show (dialog);
192 }
193
194 GtkWidget *
195 tfe_text_view_new_with_file (GFile *file) {
196 g_return_val_if_fail (G_IS_FILE (file), NULL);
197
198 GtkWidget *tv;
199 GtkTextBuffer *tb;
200 char *contents;
201 gsize length;
202
203 if (! g_file_load_contents (file, NULL, &contents, &length, NULL, NULL)) /* read error */
204 return NULL;
205
206 tv = tfe_text_view_new();
207 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
208 gtk_text_buffer_set_text (tb, contents, length);
209 g_free (contents);
210 TFE_TEXT_VIEW (tv)->file = g_file_dup (file);
211 return tv;
212 }
213
214 GtkWidget *
215 tfe_text_view_new (void) {
216 return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL));
217 }
218
Total number of lines, words and charcters
$ LANG=C wc tfe5/meson.build tfe5/tfeapplication.c tfe5/tfe.gresource.xml tfe5/tfe.h tfe5/tfenotebook.c tfe5/tfenotebook.h tfe5/tfetextview.c tfe5/tfetextview.h tfe5/tfe.ui
10 17 279 tfe5/meson.build
117 348 3576 tfe5/tfeapplication.c
6 9 153 tfe5/tfe.gresource.xml
4 6 72 tfe5/tfe.h
116 321 2992 tfe5/tfenotebook.c
12 17 196 tfe5/tfenotebook.h
218 635 7769 tfe5/tfetextview.c
29 49 561 tfe5/tfetextview.h
64 105 2266 tfe5/tfe.ui
576 1507 17864 total
Up: Readme.md, Prev: Section 14, Next: Section 16