mirror of
https://github.com/ToshioCP/Gtk4-tutorial.git
synced 2025-01-18 10:26:39 +01:00
Modify section 9 and 10.
This commit is contained in:
parent
6878ec19ce
commit
3b70d2bdc6
7 changed files with 60 additions and 63 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -10,6 +10,8 @@ src/tfv/a.out
|
|||
src/tfe/a.out
|
||||
src/tfe/hello.txt
|
||||
src/tfe/resources.c
|
||||
src/tfe5/_build
|
||||
src/tfe5/hello.txt
|
||||
|
||||
# backup file
|
||||
*~
|
||||
|
|
BIN
image/TfeTextView.ods
Executable file
BIN
image/TfeTextView.ods
Executable file
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 54 KiB |
|
@ -28,7 +28,7 @@ This is done usually when the class is initialized.
|
|||
3. When it is emmitted, the connected handler is invoked.
|
||||
|
||||
Step one and three are done in the object on which the signal is emitted.
|
||||
Step two is done outside the objects.
|
||||
Step two is usually done outside the objects.
|
||||
|
||||
## Signal registration
|
||||
|
||||
|
@ -58,7 +58,7 @@ Signal registration codes are written in the class initialization function.
|
|||
- 6-15: Register "change-file"signal.
|
||||
`g_signal_newv` function is used.
|
||||
This signal has no default handler (object method handler).
|
||||
I think you usually don't need to set a default handler in final type object.
|
||||
You usually don't need to set a default handler in final type object.
|
||||
If you need it, put the closure of the handler in line 9.
|
||||
- The return value of `g_signal_newv` is the signal id.
|
||||
The type of signal id is guint, which is the same as unsigned int.
|
||||
|
@ -95,14 +95,14 @@ The parameter is defined in `tfetextview.h` because it is public.
|
|||
};
|
||||
|
||||
- `TFE_OPEN_RESPONSE_SUCCESS` is set when `tfe_text_view_open` successfully has opend a file and loaded it.
|
||||
- `TFE_OPEN_RESPONSE_CANCEL` is set when the user canceled to open a file.
|
||||
- `TFE_OPEN_RESPONSE_ERROR` is set when error occured.
|
||||
- `TFE_OPEN_RESPONSE_CANCEL` is set when the user has canceled to open a file.
|
||||
- `TFE_OPEN_RESPONSE_ERROR` is set when error has occured.
|
||||
|
||||
## Signal connection
|
||||
|
||||
A signal and a handler are connected by the function `g_signal_connect`.
|
||||
There some similar functions like `g_signal_connect_after`, `g_signal_connect_swapped` and so on.
|
||||
But I think `g_signal_connect` is the most common function.
|
||||
There are some similar functions like `g_signal_connect_after`, `g_signal_connect_swapped` and so on.
|
||||
However, `g_signal_connect` is the most common function.
|
||||
The signals "change-file" is connected to a callback function `file_changed` outside of TfeTextView object.
|
||||
In the same way, the signals "open-response" is connected to a callback function `open_response` outside of TfeTextView object.
|
||||
The functions `file_changed` and `open_response` will be explained later.
|
||||
|
|
|
@ -44,10 +44,10 @@ Each function is defined as follows.
|
|||
|
||||
@@@ tfe5/tfetextview.c tfe_text_view_new_with_file tfe_text_view_new
|
||||
|
||||
- 18-21: `tfe_text_view_new`.
|
||||
Just returns the value from the function `gtk_widget_new`.
|
||||
- 21-24: `tfe_text_view_new`.
|
||||
Just returns the value from the function `g_object_new` but casted to the pointer to GtkWidget.
|
||||
Initialization is done in `tfe_text_view_init` which is called in the process of `gtk_widget_new` function.
|
||||
- 1-16: `tfe_text_view_new_with_file`
|
||||
- 1-19: `tfe_text_view_new_with_file`
|
||||
- 3: `g_return_val_if_fail` is described in [Glib API reference](https://developer.gnome.org/glib/stable/glib-Warnings-and-Assertions.html#g-return-val-if-fail).
|
||||
It tests whether the argument `file` is a pointer to GFile.
|
||||
If it's true, then the program goes on to the next line.
|
||||
|
@ -57,9 +57,10 @@ 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 GtkTextBuffer `tv->tb`.
|
||||
- 10-11: If an error occurs when reading the file, then return NULL.
|
||||
- 13-18: Generate TfeTextView and set the pointer to it to `tv`.
|
||||
The pointer to GtkTextBuffer is set to `tb`
|
||||
Set the contents read from the file to GtkTextBuffer `tb`.
|
||||
Free the memories pointed by `contents`.
|
||||
Duplicate `file` and set it to `tv->file`.
|
||||
Return `tv`.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Instance and class
|
||||
|
||||
This section and the following four sections are descriptions about next version of the text file editor (tfe).
|
||||
This section and the following four sections are explanations about the next version of the text file editor (tfe).
|
||||
It is tfe5.
|
||||
It has many changes from the prior version.
|
||||
All the sources are listed after the five sections.
|
||||
|
@ -15,8 +15,8 @@ It should be divided at least into two parts, `tfeapplication.c` and `tfenoteboo
|
|||
- Header files also need to be organized.
|
||||
|
||||
However, first of all, I'd like to focus on the object TfeTextView.
|
||||
It is a child object of GtkTextView.
|
||||
And important thing is it has newly added Gfile in it.
|
||||
It is a child object of GtkTextView and has a new member `file` in it.
|
||||
The important thing is to manage the Gfile object pointed by `file`.
|
||||
|
||||
- What is necessary to GFile when generating (or initializing) TfeTextView?
|
||||
- What is necessary to GFile when destructing TfeTextView?
|
||||
|
@ -34,7 +34,7 @@ After that I will explain:
|
|||
|
||||
GObject and its children are objects, which have both class and instance.
|
||||
First, think about instance of objects.
|
||||
Instance is structured memories and the structure is described using C language structure.
|
||||
Instance is structured memories and the structure is described as C language structure.
|
||||
The following is a structure of TfeTextView.
|
||||
|
||||
/* This typedef statement is automaticaly generated by the macro G_DECLARE_FINAL_TYPE */
|
||||
|
@ -42,19 +42,14 @@ The following is a structure of TfeTextView.
|
|||
|
||||
struct _TfeTextView {
|
||||
GtkTextView parent;
|
||||
GtkTextBuffer *tb;
|
||||
GFile *file;
|
||||
gboolean changed;
|
||||
};
|
||||
|
||||
Each instance has similar structure as above.
|
||||
The members of the structure are:
|
||||
|
||||
- `parent` is the structure of GtkTextView which is the parent object of TfeTextView.
|
||||
- `tb` is a pointer to GtkTextBuffer connected to GtkTextView.
|
||||
- `file` is a pointer to GFile which is a file corresponds to `tb` (or NULL is available).
|
||||
- `changed` is TRUE if the buffer has been modified, FALSE if not.
|
||||
- `file` is a pointer to GFile. It can be NULL if no file corresponds to the TfeTextView object.
|
||||
|
||||
Comparing to the source file in the previous section, `tb` and `changed` are added.
|
||||
Notice the program above is the declaration of the structure, not the definition.
|
||||
So, no memories are allocated at this moment.
|
||||
They are to be allocated when `tfe_text_view_new` function is invoked.
|
||||
|
@ -110,19 +105,19 @@ When this function is run, the following procedure is gone through.
|
|||
Step one through three is done automatically.
|
||||
Step four is done by the function `tfe_text_view_init`.
|
||||
|
||||
> (In the same way, `gtk_text_view_init`, `gtk_widget_init` and `g_object_init` is the initialization functions of GtkTextView, GtkWidget and GObject respectively.
|
||||
> You can find them in the GTK or GLib source file.)
|
||||
> In the same way, `gtk_text_view_init`, `gtk_widget_init` and `g_object_init` is the initialization functions of GtkTextView, GtkWidget and GObject respectively.
|
||||
> You can find them in the GTK or GLib source files.
|
||||
|
||||
@@@ tfe5/tfetextview.c on_changed tfe_text_view_init
|
||||
@@@ tfe5/tfetextview.c tfe_text_view_init
|
||||
|
||||
`tfe_text_view_init` initializes the instance.
|
||||
|
||||
- 8-10: Initialize `tb`, `file` and `changed`.
|
||||
- 11: Set the wrap mode of GtkTextView as GTK\_WRAP\_WORD\_CHAR.
|
||||
- 12: Connect "changed" signal to a handler `on_changed`.
|
||||
"changed" signal is defined in GtkTextBuffer.
|
||||
It is emitted when the contents in the buffer is changed.
|
||||
- 2-4: `on_changed` handler records TRUE to `tv->changed` when "changed" signal is emitted.
|
||||
- 3: Get the pointer to GtkTextBuffer and assign it to `tb`.
|
||||
- 5: Initialize `tv->file = NULL`.
|
||||
- 6: Set modified bit to FALSE. That means the GtkTextBuffer has not modified.
|
||||
When the buffer is modified, it will automatically toggled on the modified bit.
|
||||
Whenever the buffer is saved to disk, call gtk_text_buffer_set_modified (buffer , FALSE).
|
||||
- 7: Set the wrap mode of GtkTextView as GTK\_WRAP\_WORD\_CHAR.
|
||||
|
||||
## Functions and Classes
|
||||
|
||||
|
@ -130,16 +125,18 @@ In Gtk, all objects derived from GObject have class and instance.
|
|||
Instance is memories which has a structure defined by C structure declaration as I mentioned in the previous two subsections.
|
||||
And instance can be generated two or more.
|
||||
Those instances have the same structure.
|
||||
However, structured memories are insufficient to define its behavior.
|
||||
Instance, which is structured memories, only keeps status of the object.
|
||||
Therefore, it is insufficient to define its behavior.
|
||||
We need at least two things.
|
||||
One is functions and the other is class.
|
||||
|
||||
You've already seen many functions, for example, `tfe_text_view_new` is a function to generate TfeTextView instance.
|
||||
These functions are similar to object methods in object oriented languages such as Java and Ruby.
|
||||
You've already seen many functions.
|
||||
For example, `tfe_text_view_new` is a function to generate TfeTextView instance.
|
||||
These functions are similar to object methods in object oriented languages such as Java or Ruby.
|
||||
Functions are public, which means that they are expected to be used by other objects.
|
||||
|
||||
Class comprises mainly pointers to functions.
|
||||
And the functions are used by the object itself or its children objects.
|
||||
Those functions are used by the object itself or its descendent objects.
|
||||
For example, GObject class is declared in `gobject.h` in GLib source files.
|
||||
|
||||
@@@ class_gobject.c
|
||||
|
@ -159,9 +156,9 @@ In the same way, line 23 says `finalize` is a pointer to the function which has
|
|||
Look at the declaration of `_GObjectClass` so that you would find that most of the members are pointers to functions.
|
||||
|
||||
- 10: A function pointed by `constructor` is called when the instance is generated. It completes the initialization of the instance.
|
||||
- 22: A function pointed by `dispose` is called when the instance destructs itself. Destruction process is divided into two phases. First is called disposing and the instance releases all the references to other instances. The second is finalizing.
|
||||
- 22: A function pointed by `dispose` is called when the instance destructs itself. Destruction process is divided into two phases. The first one is called disposing and the instance releases all the references to other instances. The second one is finalizing.
|
||||
- 23: A funtion pointed by `finalize` finishes the destruction process.
|
||||
- The other pointers point functions which are called during the instance lives.
|
||||
- The other pointers point functions which are called while the instance lives.
|
||||
|
||||
## TfeTextView class
|
||||
|
||||
|
@ -178,11 +175,11 @@ The following is extracts from the source files (not exactly the same).
|
|||
So, they are not written in either `tfe_text_view.h` or `tfe_text_view.c`.
|
||||
- 2, 73, 106: Each derived class puts its parent class at the first member of its structure.
|
||||
It is the same as instance structures.
|
||||
- Class members in ancesters are open to their child class.
|
||||
- Class members in ancesters are open to the descendent class.
|
||||
So, they can be changed in `tfe_text_view_class_init` function.
|
||||
For example, the `dispose` pointer in GObjectClass will be overridden later in `tfe_text_view_class_init`.
|
||||
(Override is an object oriented programing terminology.
|
||||
Override is rewriting ancestors' class methods in the child class.)
|
||||
Override is rewriting ancestors' class methods in the descendent class.)
|
||||
- Some class methods are often overridden.
|
||||
`set_property`, `get_property`, `dispose`, `finalize` and `constructed` are such methods.
|
||||
|
||||
|
@ -235,7 +232,7 @@ The function `tfe_text_view_class_init` is the class initialization function and
|
|||
|
||||
}
|
||||
|
||||
Each ancestors' class is generated before TfeTextViewClass.
|
||||
Each ancestors' class has been generated before TfeTextViewClass is generated.
|
||||
Therefore, there are four classes and each class has a pointer to each dispose handler.
|
||||
Look at the following diagram.
|
||||
There are four classes -- GObjectClass (GInitiallyUnownedClass), GtkWidgetClass, GtkTextViewClass and TfeTextViewClass.
|
||||
|
@ -251,7 +248,7 @@ Then it invokes its parent's dispose handler in line 8.
|
|||
|
||||
`tfe_text_view_parent_class`,which is made by `G_DEFINE_TYPE` macro, is a pointer that points the parent object class.
|
||||
Therefore, `G_OBJECT_CLASS (tfe_text_view_parent_class)->dispose` points the handler `dh3` in the diagram above.
|
||||
And `gobject` is a pointer to TfeTextView object which is casted as a GObject instanse.
|
||||
And `gobject` is a pointer to TfeTextView instance which is casted as a GObject instanse.
|
||||
`dh3` releases all the references to objects in the GtkTextView part (it is actually the private area pointed by `prev`) in TfeTextView instance.
|
||||
After that, `dh3` calls `dh2`, and `dh2` calls `dh1`.
|
||||
Finally all the references are released.
|
||||
|
|
|
@ -3,9 +3,7 @@
|
|||
struct _TfeTextView
|
||||
{
|
||||
GtkTextView parent;
|
||||
GtkTextBuffer *tb;
|
||||
GFile *file;
|
||||
gboolean changed;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (TfeTextView, tfe_text_view, GTK_TYPE_TEXT_VIEW);
|
||||
|
@ -18,12 +16,6 @@ enum {
|
|||
|
||||
static guint tfe_text_view_signals[NUMBER_OF_SIGNALS];
|
||||
|
||||
/* Signal handler */
|
||||
static void
|
||||
on_changed (GtkTextBuffer *tb, TfeTextView *tv) {
|
||||
tv->changed=TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
tfe_text_view_dispose (GObject *gobject) {
|
||||
TfeTextView *tv = TFE_TEXT_VIEW (gobject);
|
||||
|
@ -36,11 +28,11 @@ tfe_text_view_dispose (GObject *gobject) {
|
|||
|
||||
static void
|
||||
tfe_text_view_init (TfeTextView *tv) {
|
||||
tv->tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
||||
GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
||||
|
||||
tv->file = NULL;
|
||||
tv->changed = FALSE;
|
||||
gtk_text_buffer_set_modified (tb, FALSE);
|
||||
gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR);
|
||||
g_signal_connect (tv->tb, "changed", G_CALLBACK (on_changed), tv);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -80,6 +72,7 @@ tfe_text_view_get_file (TfeTextView *tv) {
|
|||
|
||||
static void
|
||||
open_dialog_response(GtkWidget *dialog, gint response, TfeTextView *tv) {
|
||||
GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
||||
GFile *file;
|
||||
char *contents;
|
||||
gsize length;
|
||||
|
@ -101,10 +94,10 @@ open_dialog_response(GtkWidget *dialog, gint response, TfeTextView *tv) {
|
|||
g_error_free (err);
|
||||
g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_ERROR);
|
||||
} else {
|
||||
gtk_text_buffer_set_text (tv->tb, contents, length);
|
||||
gtk_text_buffer_set_text (tb, contents, length);
|
||||
g_free (contents);
|
||||
tv->file = file;
|
||||
/* tv->changed = FALSE;*/
|
||||
/* gtk_text_buffer_set_modified (tb, FALSE);*/
|
||||
g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_SUCCESS);
|
||||
}
|
||||
gtk_window_destroy (GTK_WINDOW (dialog));
|
||||
|
@ -126,13 +119,14 @@ tfe_text_view_open (TfeTextView *tv) {
|
|||
|
||||
static void
|
||||
saveas_dialog_response (GtkWidget *dialog, gint response, TfeTextView *tv) {
|
||||
GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
||||
GFile *file;
|
||||
|
||||
if (response == GTK_RESPONSE_ACCEPT) {
|
||||
file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
|
||||
if (G_IS_FILE(file)) {
|
||||
tv->file = file;
|
||||
tv->changed = TRUE;
|
||||
gtk_text_buffer_set_modified (tb, TRUE);
|
||||
g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
|
||||
tfe_text_view_save (TFE_TEXT_VIEW (tv));
|
||||
}
|
||||
|
@ -144,6 +138,7 @@ void
|
|||
tfe_text_view_save (TfeTextView *tv) {
|
||||
g_return_if_fail (TFE_IS_TEXT_VIEW (tv));
|
||||
|
||||
GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
||||
GtkTextIter start_iter;
|
||||
GtkTextIter end_iter;
|
||||
gchar *contents;
|
||||
|
@ -151,15 +146,15 @@ tfe_text_view_save (TfeTextView *tv) {
|
|||
GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW);
|
||||
GError *err = NULL;
|
||||
|
||||
if (! tv->changed)
|
||||
if (! gtk_text_buffer_get_modified (tb))
|
||||
return; /* no necessary to save it */
|
||||
else if (tv->file == NULL)
|
||||
tfe_text_view_saveas (tv);
|
||||
else {
|
||||
gtk_text_buffer_get_bounds (tv->tb, &start_iter, &end_iter);
|
||||
contents = gtk_text_buffer_get_text (tv->tb, &start_iter, &end_iter, FALSE);
|
||||
gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter);
|
||||
contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE);
|
||||
if (g_file_replace_contents (tv->file, contents, strlen (contents), NULL, TRUE, G_FILE_CREATE_NONE, NULL, NULL, &err))
|
||||
tv->changed = FALSE;
|
||||
gtk_text_buffer_set_modified (tb, FALSE);
|
||||
else {
|
||||
/* It is possible that tv->file is broken. */
|
||||
/* It is a good idea to set tv->file to NULL. */
|
||||
|
@ -167,7 +162,7 @@ tfe_text_view_save (TfeTextView *tv) {
|
|||
g_object_unref (tv->file);
|
||||
tv->file =NULL;
|
||||
g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
|
||||
tv->changed = TRUE;
|
||||
gtk_text_buffer_set_modified (tb, TRUE);
|
||||
message_dialog = gtk_message_dialog_new (GTK_WINDOW (win), GTK_DIALOG_MODAL,
|
||||
GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
|
||||
"%s.\n", err->message);
|
||||
|
@ -198,6 +193,7 @@ tfe_text_view_new_with_file (GFile *file) {
|
|||
g_return_val_if_fail (G_IS_FILE (file), NULL);
|
||||
|
||||
GtkWidget *tv;
|
||||
GtkTextBuffer *tb;
|
||||
char *contents;
|
||||
gsize length;
|
||||
|
||||
|
@ -205,7 +201,8 @@ tfe_text_view_new_with_file (GFile *file) {
|
|||
return NULL;
|
||||
|
||||
tv = tfe_text_view_new();
|
||||
gtk_text_buffer_set_text (TFE_TEXT_VIEW (tv)->tb, contents, length);
|
||||
tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
||||
gtk_text_buffer_set_text (tb, contents, length);
|
||||
g_free (contents);
|
||||
TFE_TEXT_VIEW (tv)->file = g_file_dup (file);
|
||||
return tv;
|
||||
|
@ -213,6 +210,6 @@ tfe_text_view_new_with_file (GFile *file) {
|
|||
|
||||
GtkWidget *
|
||||
tfe_text_view_new (void) {
|
||||
return gtk_widget_new (TFE_TYPE_TEXT_VIEW, NULL);
|
||||
return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL));
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue