15 KiB
Up: Readme.md, Prev: Section 10, Next: Section 12
Functions in TfeTextView
In this section I will explain each function in TfeTextView object.
tfe.h and tfetextview.h
tfe.h
is a top header file and it includes gtk.h
and all the header files.
Every C source files, which are tfeapplication.c
, tfenotebook.c
and tfetextview.c
, include tfe.h
at the beginning of each file.
1 #include <gtk/gtk.h>
2
3 #include "tfetextview.h"
4 #include "tfenotebook.h"
tfetextview.h
is a header file which describes the public functions in tfetextview.c
.
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);
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
- 1-2: These two lines are used to define TfeTextView.
- 4-10: Definitions of parameter used in the handler of "open-response" signal.
- 12-28: Public functions on GtkTextView.
Each function will be explained later in this section.
Functions to generate TfeTextView object
TfeTextView Object is generated by tfe_text_view_new
or tfe_text_view_new_with_file
.
GtkWidget *tfe_text_view_new (void);
tfe_text_view_new
just generates a new TfeTextView object and returns the pointer to the new object.
GtkWidget *tfe_text_view_new_with_file (GFile *file);
tfe_text_view_new_with_file
is given a Gfile object as the argument and it loads the file into the GtkTextBuffer object, then returns the pointer to the new object.
Parameter:
file
: a pointer to the GFile object.
Return value:
- A pointer to the generated TfeTextView object but it is casted to a pointer to GtkWidget. If an error occures during the genration process, NULL is returned.
Each function is defined as follows.
1 GtkWidget *
2 tfe_text_view_new_with_file (GFile *file) {
3 g_return_val_if_fail (G_IS_FILE (file), NULL);
4
5 GtkWidget *tv;
6 char *contents;
7 gsize length;
8
9 if (! g_file_load_contents (file, NULL, &contents, &length, NULL, NULL)) /* read error */
10 return NULL;
11
12 tv = tfe_text_view_new();
13 gtk_text_buffer_set_text (TFE_TEXT_VIEW (tv)->tb, contents, length);
14 g_free (contents);
15 TFE_TEXT_VIEW (tv)->file = g_file_dup (file);
16 return tv;
17 }
18
19 GtkWidget *
20 tfe_text_view_new (void) {
21 return gtk_widget_new (TFE_TYPE_TEXT_VIEW, NULL);
22 }
- 18-21:
tfe_text_view_new
. Just returns the value from the functiongtk_widget_new
. Initialization is done intfe_text_view_init
which is called in the process ofgtk_widget_new
function. - 1-16:
tfe_text_view_new_with_file
- 3:
g_return_val_if_fail
is described in Glib API reference. It tests whether the argumentfile
is a pointer to GFile. If it's true, then the program goes on to the next line. If it's false, then it returns NULL (the second argument) immediately. And at the same time it logs out the error message (usually the log is outputted to stderr or stdout). This function is used to check the programmer's error. If an error occurs, the solution is usually to change the (caller) program and fix the bug. You need to distinguish programmer's errors and runtime errors. You shouldn't use this function to find runtime errors. - 9-10: If an error occurs when reading the file, then return NULL.
- 11-15: Generate TfeTextView and set the pointer to it to
tv
. Set the contents read from the file to GtkTextBuffertv->tb
. Free the memories pointed bycontents
. Duplicatefile
and set it totv->file
. Returntv
.
Save and saveas functions
Save and saveas functions write the contents in GtkTextBuffer to a file.
void tfe_text_view_save (TfeTextView *tv)
save
function writes the contents in GtkTextBuffer to a file specified by tv->file
.
If tv->file
is NULL, then it shows GtkFileChooserDialog and lets the user to give a file to the program. After that, it saves the contents to the specified file and set the file into tv->file
.
void tfe_text_view_saveas (TfeTextView *tv)
saveas
function uses GtkFileChooserDialog and lets the user to give a new file to the program. Then, the function changes tv->file
and save the contents to the specified new file.
If an error occures, it is shown to the user through the message dialog. The error is managed only in the object and no information is notified to the caller.
1 static void
2 saveas_dialog_response (GtkWidget *dialog, gint response, TfeTextView *tv) {
3 GFile *file;
4
5 if (response == GTK_RESPONSE_ACCEPT) {
6 file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
7 if (G_IS_FILE(file)) {
8 tv->file = file;
9 tv->changed = TRUE;
10 g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
11 tfe_text_view_save (TFE_TEXT_VIEW (tv));
12 }
13 }
14 gtk_window_destroy (GTK_WINDOW (dialog));
15 }
16
17 void
18 tfe_text_view_save (TfeTextView *tv) {
19 g_return_if_fail (TFE_IS_TEXT_VIEW (tv));
20
21 GtkTextIter start_iter;
22 GtkTextIter end_iter;
23 gchar *contents;
24 GtkWidget *message_dialog;
25 GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW);
26 GError *err = NULL;
27
28 if (! tv->changed)
29 return; /* no necessary to save it */
30 else if (tv->file == NULL)
31 tfe_text_view_saveas (tv);
32 else {
33 gtk_text_buffer_get_bounds (tv->tb, &start_iter, &end_iter);
34 contents = gtk_text_buffer_get_text (tv->tb, &start_iter, &end_iter, FALSE);
35 if (g_file_replace_contents (tv->file, contents, strlen (contents), NULL, TRUE, G_FILE_CREATE_NONE, NULL, NULL, &err))
36 tv->changed = FALSE;
37 else {
38 /* It is possible that tv->file is broken. */
39 /* It is a good idea to set tv->file to NULL. */
40 if (G_IS_FILE (tv->file))
41 g_object_unref (tv->file);
42 tv->file =NULL;
43 g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
44 tv->changed = TRUE;
45 message_dialog = gtk_message_dialog_new (GTK_WINDOW (win), GTK_DIALOG_MODAL,
46 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
47 "%s.\n", err->message);
48 g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
49 gtk_widget_show (message_dialog);
50 g_error_free (err);
51 }
52 }
53 }
54
55 void
56 tfe_text_view_saveas (TfeTextView *tv) {
57 g_return_if_fail (TFE_IS_TEXT_VIEW (tv));
58
59 GtkWidget *dialog;
60 GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW);
61
62 dialog = gtk_file_chooser_dialog_new ("Save file", GTK_WINDOW (win), GTK_FILE_CHOOSER_ACTION_SAVE,
63 "_Cancel", GTK_RESPONSE_CANCEL,
64 "_Save", GTK_RESPONSE_ACCEPT,
65 NULL);
66 g_signal_connect (dialog, "response", G_CALLBACK (saveas_dialog_response), tv);
67 gtk_widget_show (dialog);
68 }
- 17-53:
Tfe_text_view_save
function. - 19: If
tv
is not a pointer to TfeTextView, then it logs an error message and immediately returns. This function is similar tog_return_val_if_fail
function, but no value is returned becausetfe_text_view_save
doesn't return a value. - 28-29: If the buffer hasn't modified, then it doesn't need to save it. So the function returns.
- 30-31: If
tv->file
is NULL, no file has given yet. It callstfe_text_view_save
, which lets the user to choose a file to save. - 33-35: Save the buffer to the file.
If it succeeds, assigns FALSE to
tv->changed
. - 38-50: If file writing fails, it assigns NULL to
tv->file
. Emits "change-file" signal. Shows the error message dialog (45-49). Because the handler isgtk_window_destroy
, the dialog disappears when user clicks on the button in the dialog. - 55-68:
tfe_text_view_saveas
function. It shows GtkFileChooserDialog and lets the user choose a file and give it to the signal handler. - 62: Generate GtkFileChooserDialog.
The title is "Save file".
Transient parent of the dialog is
win
, which is the top level window. The action is save mode. The buttons are Cancel and Save. - 63: connect the "response" signal of the dialog and
saveas_dialog_response
handler. - 1-15:
saveas_dialog_response
signal handler. - 5-13: If the response is
GTK_RESPONSE_ACCEPT
, which is set to the argument when the user has clicked on Save button, then gets a pointer to the GFile object, set it totv->file
, assign TRUE totv->changed
, emits "change-file" signal then calltfe_text_view_save
to save the buffer to the file.
When you use GtkFileChooserDialog, you need to divide the program into two parts. They are a function which generates GtkFileChooserDialog and the signal handler. The function just generates and shows the dialog. The rest is done by the handler. It gets Gfile from GtkFileChooserDialog, save the buffer to the file and do some things necessary.
Open function
Open function shows GtkFileChooserDialog to the user and let him/her choose a file. Then read the file and set it to GtkTextBuffer.
void tfe_text_view_open (TfeTextView *tv)
TfeTextView object tv
has to be generated in advance.
And it should be empty and tv->file
is NULL.
If it is not empty, tfe_text_view_open
doesn't treat it as an error.
If you want to revert the buffer, calling this function is apropreate.
Otherwise probably bad things will happen.
1 static void
2 open_dialog_response(GtkWidget *dialog, gint response, TfeTextView *tv) {
3 GFile *file;
4 char *contents;
5 gsize length;
6 GtkWidget *message_dialog;
7 GError *err = NULL;
8
9 if (response != GTK_RESPONSE_ACCEPT)
10 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_CANCEL);
11 else if (! G_IS_FILE (file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog))))
12 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_ERROR);
13 else if (! g_file_load_contents (file, NULL, &contents, &length, NULL, &err)) { /* read error */
14 if (G_IS_FILE (file))
15 g_object_unref (file);
16 message_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog), GTK_DIALOG_MODAL,
17 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
18 "%s.\n", err->message);
19 g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
20 gtk_widget_show (message_dialog);
21 g_error_free (err);
22 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_ERROR);
23 } else {
24 gtk_text_buffer_set_text (tv->tb, contents, length);
25 g_free (contents);
26 tv->file = file;
27 /* tv->changed = FALSE;*/
28 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_SUCCESS);
29 }
30 gtk_window_destroy (GTK_WINDOW (dialog));
31 }
32
33 void
34 tfe_text_view_open (TfeTextView *tv) {
35 g_return_if_fail (TFE_IS_TEXT_VIEW (tv));
36
37 GtkWidget *dialog;
38
39 dialog = gtk_file_chooser_dialog_new ("Open file", NULL, GTK_FILE_CHOOSER_ACTION_OPEN,
40 "Cancel", GTK_RESPONSE_CANCEL,
41 "Open", GTK_RESPONSE_ACCEPT,
42 NULL);
43 g_signal_connect (dialog, "response", G_CALLBACK (open_dialog_response), tv);
44 gtk_widget_show (dialog);
45 }
- 33-45:
tfe_text_view_open
function. - 39: Generate GtkFileChooserDialog. The title is "Open file". No transient parent window. The action is open mode. The buttons are Cancel and Open.
- 43: connect the "reponse" signal of the dialog and
open_dialog_response
signal handler. - 44: Show the dialog.
- 1-31:
open_dialog_response
signal handler. - 9-10: If the response from GtkFileChooserDialog is not
GTK_RESPONSE_ACCEPT
, which means the user has clicked on the "Cancel" button or close button, then it emits "open-response" signal with the parameterTFE_OPEN_RESPONSE_CANCEL
. - 11-12: Get a pointer to Gfile by
gtk_file_chooser_get_file
. If it is not GFile, maybe an error occured. Then it emits "open-response" signal with the parameterTFE_OPEN_RESPONSE_ERROR
. - 13-22: If an error occurs when it has read the file, then it decreases the reference count of Gfile, shows a message dialog to report the error to the user and emits "open-response" signal with the parameter
TFE_OPEN_RESPONSE_ERROR
. - 24-28: If the file has successfully read, then the text is set to GtkTextBuffer, free the temporary buffer pointed by
contents
, set file totv->file
(no duplication or unref is not necessary) and emits "open-response" signal with the parameterTFE_OPEN_RESPONSE_SUCCESS
. - 30: close GtkFileCooserDialog.
Now let's think about the whole process between the other object (caller) and TfeTextView.
It is shown in the following diagram and you would think that it is really complicated.
Because signal is the only way for GtkFileChooserDialog to communicate with others.
In Gtk3, gtk_dialog_run
function is available.
It simplifies the process.
However, in Gtk4, gtk_dialog_run
is unavailable any more.
- A caller get a pointer
tv
to TfeTextView by callingtfe_text_view_new
. - The caller connects the handler (left bottom in the diagram) and the signal "open-response".
- It calls
tfe_text_view_open
to let the user select a file from GtkFileChooserDialog. - The dialog emits a signal and it invokes the handler
open_dialog_response
. - The handler read the file and set it into GtkTextBuffer and emits a signal to inform the response status.
- The handler outside TfeTextView recieves the signal.
Get file function
gtk_text_view_get_file
is a simple function show as follows.
1 GFile *
2 tfe_text_view_get_file (TfeTextView *tv) {
3 g_return_val_if_fail (TFE_IS_TEXT_VIEW (tv), NULL);
4
5 return g_file_dup (tv->file);
6 }
The important thing is duplicate tv->file
.
Otherwise, if the caller free the GFile object, tv->file
is no more guaranteed to point the GFile.
Source file of tfetextview.c
All the source files are listed in Section 13. Up: Readme.md, Prev: Section 10, Next: Section 12