GtkNotebook is a very important object in the text file editor
tfe
. It connects the application and TfeTextView objects. A
set of public functions are declared in tfenotebook.h
. The
word “tfenotebook” is used only in filenames. There’s no “TfeNotebook”
object.
The source files are in the directory src/tfe5
. You can
get them by downloading the repository.
void
notebook_page_save(GtkNotebook *nb);
void
notebook_page_close (GtkNotebook *nb);
void
notebook_page_open (GtkNotebook *nb);
void
notebook_page_new_with_file (GtkNotebook *nb, GFile *file);
void
notebook_page_new (GtkNotebook *nb);
This header file describes the public functions in
tfenotebook.c
.
notebook_page_save
saves the current page to the
file of which the name specified in the tab. If the name is
untitled
or untitled
followed by digits, a
file chooser dialog appears and a user can choose or specify a
filename.notebook_page_close
closes the current page.notebook_page_open
shows a file chooser dialog and
a user can choose a file. The contents of the file is inserted to a new
page.notebook_page_new_with_file
creates a new page
and a file given as an argument is read and inserted into the page.notebook_page_new
creates a new empty page.You probably find that the functions except
notebook_page_close
are higher level functions of
tfe_text_view_save
tef_text_view_open
tfe_text_view_new_with_file
tfe_text_view_new
respectively.
There are two layers. One of them is tfe_text_view ...
,
which is the lower level layer. The other is notebook ...
,
which is the higher level layer.
Now let’s look at the program of each function.
static char*
get_untitled () {
static int c = -1;
if (++c == 0)
return g_strdup_printf("Untitled");
else
return g_strdup_printf ("Untitled%u", c);
}
static void
notebook_page_build (GtkNotebook *nb, GtkWidget *tv, const char *filename) {
GtkWidget *scr = gtk_scrolled_window_new ();
GtkNotebookPage *nbp;
GtkWidget *lab;
int i;
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);
lab = gtk_label_new (filename);
i = gtk_notebook_append_page (nb, scr, lab);
nbp = gtk_notebook_get_page (nb, scr);
g_object_set (nbp, "tab-expand", TRUE, NULL);
gtk_notebook_set_current_page (nb, i);
g_signal_connect (GTK_TEXT_VIEW (tv), "change-file", G_CALLBACK (file_changed_cb), nb);
}
void
notebook_page_new (GtkNotebook *nb) {
g_return_if_fail(GTK_IS_NOTEBOOK (nb));
GtkWidget *tv;
char *filename;
tv = tfe_text_view_new ();
filename = get_untitled ();
notebook_page_build (nb, tv, filename);
g_free (filename);
}
notebook_page_new
.g_return_if_fail
checks the argument.
It’s necessary because the function is public.get_untitled
.c
is initialized at the first call
of this function. After that c
keeps its value unless it is
changed explicitly.c
by one and if it is zero, it returns
“Untitled”. If it is a positive integer, it returns “Untitled<the
integer>”, for example, “Untitled1”, “Untitled2”, and so on. The
function g_strdup_printf
creates a string and it should be
freed by g_free
when it becomes useless. The caller of
get_untitled
is in charge of freeing the string.notebook_page_build
to build a new page.filename
.notebook_page_build
. A parameter
with const
qualifier doesn’t change in the function. It
means that the argument filename
is owned by the caller.
The caller needs to free it when it becomes useless.tv
to GtkScrolledWindow as a child.scr
and
lab
to the GtkNotebook instance nb
.g_object_set
sets properties on an object. The object can
be any object derived from GObject. In many cases, an object has its own
function to set its properties, but sometimes doesn’t. In that case, use
g_object_set
to set the property.file_changed_cb
.void
notebook_page_new_with_file (GtkNotebook *nb, GFile *file) {
g_return_if_fail(GTK_IS_NOTEBOOK (nb));
g_return_if_fail(G_IS_FILE (file));
GtkWidget *tv;
char *filename;
if ((tv = tfe_text_view_new_with_file (file)) == NULL)
return; /* read error */
filename = g_file_get_basename (file);
notebook_page_build (nb, tv, filename);
g_free (filename);
}
tfe_text_view_new_with_file
. If the
function returns NULL, an error has happened. Then, it does nothing and
returns.filename
.static void
open_response_cb (TfeTextView *tv, int response, GtkNotebook *nb) {
GFile *file;
char *filename;
if (response != TFE_OPEN_RESPONSE_SUCCESS) {
g_object_ref_sink (tv);
g_object_unref (tv);
}else {
file = tfe_text_view_get_file (tv);
filename = g_file_get_basename (file);
g_object_unref (file);
notebook_page_build (nb, GTK_WIDGET (tv), filename);
g_free (filename);
}
}
void
notebook_page_open (GtkNotebook *nb) {
g_return_if_fail(GTK_IS_NOTEBOOK (nb));
GtkWidget *tv;
tv = tfe_text_view_new ();
g_signal_connect (TFE_TEXT_VIEW (tv), "open-response", G_CALLBACK (open_response_cb), nb);
tfe_text_view_open (TFE_TEXT_VIEW (tv), GTK_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET (nb), GTK_TYPE_WINDOW)));
}
notebook_page_open
.open_response_cb
.tfe_text_view_open
. The “open-response”
signal will be emitted later in this function to inform the result.open_response_cb
.TFE_OPEN_RESPONSE_SUCCESS
, the instance tv
will be destroyed. It has floating reference, which will be explained
later. A floating reference needs to be converted into an ordinary
reference before releasing it. The function
g_object_ref_sink
does that. After that, the function
g_object_unref
releases tv
and decreases the
reference count by one. Finally the reference count becomes zero and
tv
is destroyed.tv
.All the widgets are derived from GInitiallyUnowned. GObject and GInitiallyUnowned are almost the same. The difference is like this. When an instance of GInitiallyUnowned is created, the instance has a “floating reference”. On the other hand, when an instance of GObject (not GInitiallyUnowned) is created, it has “normal reference”. Their descendants inherits them, so every widget has a floating reference just after the creation. Non-widget class, for example, GtkTextBuffer is a direct sub class of GObject and it has normal reference.
The function g_object_ref_sink
converts the floating
reference into a normal reference. If the instance doesn’t have a
floating reference, g_object_ref_sink
simply increases the
reference count by one. It is used when an widget is added to another
widget as a child.
GtkTextView *tv = gtk_text_view_new (); // Floating reference
GtkScrolledWindow *scr = gtk_scrolled_window_new ();
gtk_scrolled_window_set_child (scr, tv); // Scrolled window sink the tv's floating reference and tv's reference count becomes one.
When tv
is added to scr
as a child,
g_object_ref_sink
is used.
g_object_ref_sink (tv);
So, the floating reference is converted into an ordinary reference. That is to say, floating reference is removed, and the normal reference count is one. Thanks to this, the caller doesn’t need to decrease tv’s reference count. If an Object_A is not a descendant of GInitiallyUnowned, the program is like this:
Object_A *obj_a = object_a_new (); // reference count is one
GtkScrolledWindow *scr = gtk_scrolled_window_new ();
gtk_scrolled_window_set_child (scr, obj_a); // obj_a's reference count is two
// obj_a is referred by the caller (this program) and scrolled window
g_object_unref (obj_a); // obj_a's reference count is one because the caller no longer refers obj_a.
This example tells us that the caller needs to unref
obj_a
.
If you use g_object_unref
to an instance that has a
floating reference, you need to convert the floating reference to a
normal reference in advance. See GObject API
reference for further information.
void
notebook_page_close (GtkNotebook *nb) {
g_return_if_fail(GTK_IS_NOTEBOOK (nb));
GtkWidget *win;
int i;
if (gtk_notebook_get_n_pages (nb) == 1) {
win = gtk_widget_get_ancestor (GTK_WIDGET (nb), GTK_TYPE_WINDOW);
gtk_window_destroy(GTK_WINDOW (win));
} else {
i = gtk_notebook_get_current_page (nb);
gtk_notebook_remove_page (GTK_NOTEBOOK (nb), i);
}
}
This function closes the current page. If the page is the only page the notebook has, then the function destroys the top-level window and quits the application.
gtk_window_destroy
to destroy the top-level window.static TfeTextView *
get_current_textview (GtkNotebook *nb) {
int i;
GtkWidget *scr;
GtkWidget *tv;
i = gtk_notebook_get_current_page (nb);
scr = gtk_notebook_get_nth_page (nb, i);
tv = gtk_scrolled_window_get_child (GTK_SCROLLED_WINDOW (scr));
return TFE_TEXT_VIEW (tv);
}
void
notebook_page_save (GtkNotebook *nb) {
g_return_if_fail(GTK_IS_NOTEBOOK (nb));
TfeTextView *tv;
tv = get_current_textview (nb);
tfe_text_view_save (tv);
}
notebook_page_save
.tv
so you don’t need
to care about freeing it.tfe_text_view_save
.get_current_textview
. This function gets the
TfeTextView object belongs to the current page.scr
, which is a
GtkScrolledWindow instance, of the current page. The object
scr
is owned by the notebook nb
. So, the
caller doesn’t need to free it.scr
, which is a
TfeTextView instance, and returns it. The returned instance is owned by
scr
and the caller of get_cuurent_textview
doesn’t need to care about freeing it.The function file_changed_cb
is a handler connected to
“change-file” signal. If a file in a TfeTextView instance is changed,
the instance emits this signal. This handler changes the label of the
GtkNotebookPage.
static void
file_changed_cb (TfeTextView *tv, GtkNotebook *nb) {
GtkWidget *scr;
GtkWidget *label;
GFile *file;
char *filename;
file = tfe_text_view_get_file (tv);
scr = gtk_widget_get_parent (GTK_WIDGET (tv));
if (G_IS_FILE (file)) {
filename = g_file_get_basename (file);
g_object_unref (file);
} else
filename = get_untitled ();
label = gtk_label_new (filename);
g_free (filename);
gtk_notebook_set_tab_label (nb, scr, label);
}
tv
.tv
.file
points a GFile instance, the filename of
the GFile is assigned to filename
. Then, unref the GFile
object file
.Untitled(number)
is assigned to filename
.label
with the
filename and set the label of the GtkNotebookPage with
label
.