We made a very simple editor in the previous section with GtkTextView, GtkTextBuffer and GtkScrolledWindow. We will add file-read ability to the program and improve it to a file viewer.
The easiest way to give a filename is to use a command line argument.
$ ./a.out filename
The program will open the file and insert its contents into the GtkTextBuffer.
To do this, we need to know how GtkApplication (or GApplication) recognizes arguments. This is described in the GIO API Reference – Application.
When GtkApplication is created, a flag (GApplicationFlags) is given as an argument.
*
GtkApplication (const gchar *application_id, GApplicationFlags flags); gtk_application_new
This tutorial explains only two flags,
G_APPLICATION_DEFAULT_FLAGS
and
G_APPLICATION_HANDLES_OPEN
.
G_APPLICATION_FLAGS_NONE
was used instead of
G_APPLICATION_DEFAULT_FLAGS
before GIO version2.73.3 (GLib
2.73.3 5/Aug/2022). Now it is deprecated and
G_APPLICATION_DEFAULT_FLAGS
is recommended.
For further information, see GIO API Reference – ApplicationFlags and GIO API Reference – g_application_run.
We’ve already used G_APPLICATION_DEFAULT_FLAGS
, as it is
the simplest option, and no command line arguments are allowed. If you
give arguments, an error will occur.
The flag G_APPLICATION_HANDLES_OPEN
is the second
simplest option. It allows arguments but only filenames.
= gtk_application_new ("com.github.ToshioCP.tfv3", G_APPLICATION_HANDLES_OPEN); app
When G_APPLICATION_HANDLES_OPEN
flag is given to the
application, two signals are available.
The handler of the “open” signal is defined as follows.
void
(
open * self,
GApplication,
gpointer files,
gint n_files* hint,
gchar
gpointer user_data)
The parameters are:
files
A file viewer is a program that displays text files. Our file viewer is as follows.
The program is shown below.
#include <gtk/gtk.h>
static void
app_activate (GApplication *app) {
g_printerr ("You need a filename argument.\n");
}
static void
app_open (GApplication *app, GFile ** files, int n_files, char *hint) {
GtkWidget *win;
GtkWidget *scr;
GtkWidget *tv;
GtkTextBuffer *tb;
char *contents;
gsize length;
char *filename;
GError *err = NULL;
win = gtk_application_window_new (GTK_APPLICATION (app));
gtk_window_set_default_size (GTK_WINDOW (win), 400, 300);
scr = gtk_scrolled_window_new ();
gtk_window_set_child (GTK_WINDOW (win), scr);
tv = gtk_text_view_new ();
tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR);
gtk_text_view_set_editable (GTK_TEXT_VIEW (tv), FALSE);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);
if (g_file_load_contents (files[0], NULL, &contents, &length, NULL, &err)) {
gtk_text_buffer_set_text (tb, contents, length);
g_free (contents);
if ((filename = g_file_get_basename (files[0])) != NULL) {
gtk_window_set_title (GTK_WINDOW (win), filename);
g_free (filename);
}
gtk_window_present (GTK_WINDOW (win));
} else {
g_printerr ("%s.\n", err->message);
g_error_free (err);
gtk_window_destroy (GTK_WINDOW (win));
}
}
int
main (int argc, char **argv) {
GtkApplication *app;
int stat;
app = gtk_application_new ("com.github.ToshioCP.tfv3", G_APPLICATION_HANDLES_OPEN);
g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
g_signal_connect (app, "open", G_CALLBACK (app_open), NULL);
stat = g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app);
return stat;
}
Save it as tfv3.c
. If you’ve downloaded this repository,
the file is src/tfv/tfv3.c
. Compile and run it.
$ comp tfv3
$ ./a.out tfv3.c
The function main
has only two changes from the previous
version.
G_APPLICATION_DEFAULT_FLAGS
is replaced by
G_APPLICATION_HANDLES_OPEN
g_signal_connect (app, "open", G_CALLBACK (app_open), NULL)
is added.When the flag G_APPLICATION_HANDLES_OPEN
is given to
gtk_application_new
function, the application behaves like
this:
The handler app_activate
becomes very simple. It just
outputs an error message and returns to the caller. Then the application
quits immediately because no window is created.
The main work is done in the handler app_open
.
GTK_WRAP_WORD_CHAR
in
GtktextViewThe following is the file reading part of the program.
if (g_file_load_contents (files[0], NULL, &contents, &length, NULL, &err)) {
(tb, contents, length);
gtk_text_buffer_set_text (contents);
g_free if ((filename = g_file_get_basename (files[0])) != NULL) {
(GTK_WINDOW (win), filename);
gtk_window_set_title (filename);
g_free }
(GTK_WINDOW (win));
gtk_window_present } else {
("%s.\n", err->message);
g_printerr (err);
g_error_free (GTK_WINDOW (win));
gtk_window_destroy }
The function g_file_load_contents
loads the file
contents into a temporary buffer, which is automatically allocated and
sets contents
to point the buffer. The length of the buffer
is assigned to length
. It returns TRUE
if the
file’s contents are successfully loaded. If an error occurs, it returns
FALSE
and sets the variable err
to point a
newly created GError structure. The caller takes ownership of the GError
structure and is responsible for freeing it. If you want to know the
details about g_file_load_contents, see g file
load contents.
If it has successfully read the file, it inserts the contents into
GtkTextBuffer, frees the temporary buffer pointed by
contents
, sets the title of the window, frees the memories
pointed by filename
and then shows the window.
If it fails, g_file_load_contents
sets err
to point a newly created GError structure. The structure is:
struct GError {
;
GQuark domainint code;
char* message;
}
The message
member is used most often. It points an
error message. A function g_error_free
is used to free the
memory of the structure. See GError.
The program above outputs an error message, frees err
and destroys the window and finally make the program quit.
GtkNotebook is a container widget that contains multiple widgets with tabs. It shows only one child at a time. Another child will be shown when its tab is clicked.
The left image is the window at the startup. The file
pr1.c
is shown and its filename is in the left tab. After
clicking on the right tab, the contents of the file tfv1.c
is shown (the right image).
The following is tfv4.c
. It has GtkNoteBook widget. It
is inserted as a child of GtkApplicationWindow and contains multiple
GtkScrolledWindow.
#include <gtk/gtk.h>
static void
app_activate (GApplication *app) {
g_printerr ("You need filename arguments.\n");
}
static void
app_open (GApplication *app, GFile ** files, gint n_files, gchar *hint) {
GtkWidget *win;
GtkWidget *nb;
GtkWidget *lab;
GtkNotebookPage *nbp;
GtkWidget *scr;
GtkWidget *tv;
GtkTextBuffer *tb;
char *contents;
gsize length;
char *filename;
int i;
GError *err = NULL;
win = gtk_application_window_new (GTK_APPLICATION (app));
gtk_window_set_title (GTK_WINDOW (win), "file viewer");
gtk_window_set_default_size (GTK_WINDOW (win), 600, 400);
nb = gtk_notebook_new ();
gtk_window_set_child (GTK_WINDOW (win), nb);
for (i = 0; i < n_files; i++) {
if (g_file_load_contents (files[i], NULL, &contents, &length, NULL, &err)) {
scr = gtk_scrolled_window_new ();
tv = gtk_text_view_new ();
tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR);
gtk_text_view_set_editable (GTK_TEXT_VIEW (tv), FALSE);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);
gtk_text_buffer_set_text (tb, contents, length);
g_free (contents);
if ((filename = g_file_get_basename (files[i])) != NULL) {
lab = gtk_label_new (filename);
g_free (filename);
} else
lab = gtk_label_new ("");
gtk_notebook_append_page (GTK_NOTEBOOK (nb), scr, lab);
nbp = gtk_notebook_get_page (GTK_NOTEBOOK (nb), scr);
g_object_set (nbp, "tab-expand", TRUE, NULL);
} else {
g_printerr ("%s.\n", err->message);
g_clear_error (&err);
}
}
if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb)) > 0)
gtk_window_present (GTK_WINDOW (win));
else
gtk_window_destroy (GTK_WINDOW (win));
}
int
main (int argc, char **argv) {
GtkApplication *app;
int stat;
app = gtk_application_new ("com.github.ToshioCP.tfv4", G_APPLICATION_HANDLES_OPEN);
g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
g_signal_connect (app, "open", G_CALLBACK (app_open), NULL);
stat = g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app);
return stat;
}
Most of the changes are in the function app_open
. The
numbers at the left of the following items are line numbers in the
source code.
nb
, lab
and
nbp
are defined. They point GtkNotebook, GtkLabel and
GtkNotebookPage respectively.files[i]
points i-th
GFile, which is created by the GtkApplication from the i-th command line
argument.contents
.filename
is freed..gtk_notebook_get_page
returns the GtkNotebookPage of the
child (GtkScrolledWindow).g_object_set
is a general function to set properties of
objects. See GObject API
Reference – g_object_set.g_clear_error (&err)
works like
g_error_free (err); err = NULL
.