mirror of
https://github.com/ToshioCP/Gtk4-tutorial.git
synced 2025-01-12 20:03:28 +01:00
440 lines
20 KiB
Markdown
440 lines
20 KiB
Markdown
Up: [Readme.md](../Readme.md), Prev: [Section 9](sec9.md), Next: [Section 11](sec11.md)
|
|
|
|
# Instance and class
|
|
|
|
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 in [Section 15](sec15.md).
|
|
They are located in two directories, [src/tfe5](../src/tfe5) and [src/tfetextview](../src/tfetextview).
|
|
|
|
## Encapsulation
|
|
|
|
We've divided C source file into two parts.
|
|
But it is not enough in terms of encapsulation.
|
|
|
|
- `tfe.c` includes everything other than TfeTextView.
|
|
It should be divided at least into two parts, `tfeapplication.c` and `tfenotebook.c`.
|
|
- 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 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?
|
|
- TfeTextView should read/write a file by itself or not?
|
|
- How it communicates with objects outside?
|
|
|
|
You need to know at least class, instance and signals before thinking about them.
|
|
I will explain them in this section and the next section.
|
|
After that I will explain:
|
|
|
|
- Organizing functions.
|
|
- How to use GtkFileChooserDialog
|
|
|
|
## GObject and its children
|
|
|
|
GObject and its children are objects, which have both class and instance.
|
|
First, think about instance of objects.
|
|
Instance is structured memories and described as C language structure.
|
|
The following is a structure of TfeTextView.
|
|
|
|
~~~C
|
|
/* This typedef statement is automatically generated by the macro G_DECLARE_FINAL_TYPE */
|
|
typedef struct _TfeTextView TfeTextView;
|
|
|
|
struct _TfeTextView {
|
|
GtkTextView parent;
|
|
GFile *file;
|
|
};
|
|
~~~
|
|
|
|
The members of the structure are:
|
|
|
|
- `parent` is the instance structure of GtkTextView which is the parent object of TfeTextView.
|
|
- `file` is a pointer to GFile. It can be NULL if no file corresponds to the TfeTextView object.
|
|
|
|
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.
|
|
|
|
You can find the declaration of the ancestors of TfeTextView in the source files of GTK and GLib.
|
|
The following is extracts from the source files (not exactly the same).
|
|
|
|
~~~C
|
|
typedef struct _GObject GObject;
|
|
typedef struct _GObject GInitiallyUnowned;
|
|
struct _GObject
|
|
{
|
|
GTypeInstance g_type_instance;
|
|
volatile guint ref_count;
|
|
GData *qdata;
|
|
};
|
|
|
|
typedef struct _GtkWidget GtkWidget;
|
|
struct _GtkWidget
|
|
{
|
|
GInitiallyUnowned parent_instance;
|
|
GtkWidgetPrivate *priv;
|
|
};
|
|
|
|
typedef struct _GtkTextView GtkTextView;
|
|
struct _GtkTextView
|
|
{
|
|
GtkWidget parent_instance;
|
|
GtkTextViewPrivate *priv;
|
|
};
|
|
~~~
|
|
|
|
In each structure, its parent instance is declared at the top of members.
|
|
So, every ancestors is included in the child instance.
|
|
This is very important.
|
|
It guarantees a child widget to derive all the features from ancestors.
|
|
The structure of `TfeTextView` is like the following diagram.
|
|
|
|
![The structure of the instance TfeTextView](../image/TfeTextView.png)
|
|
|
|
|
|
## Generate TfeTextView instance
|
|
|
|
The function `tfe_text_view_new` generates a new TfeTextView instance.
|
|
|
|
~~~C
|
|
1 GtkWidget *
|
|
2 tfe_text_view_new (void) {
|
|
3 return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL));
|
|
4 }
|
|
~~~
|
|
|
|
When this function is run, the following procedure is gone through.
|
|
|
|
1. Initialize GObject part in TfeTextView instance.
|
|
2. Initialize GtkWidget part in TfeTextView instance.
|
|
3. Initialize GtkTextView part in TfeTextView instance.
|
|
4. Initialize TfeTextView part in TfeTextView instance.
|
|
|
|
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 files.
|
|
|
|
~~~C
|
|
1 static void
|
|
2 tfe_text_view_init (TfeTextView *tv) {
|
|
3 tv->file = NULL;
|
|
4 }
|
|
~~~
|
|
|
|
This function just initializes `tv->file` to be `NULL`.
|
|
|
|
## Functions and Classes
|
|
|
|
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.
|
|
Each object can have more than one instance.
|
|
Those instances have the same structure.
|
|
Instance, which is structured memories, only keeps status of the instance.
|
|
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 or Ruby.
|
|
Functions are public, which means that they are expected to be used by other objects.
|
|
|
|
Class comprises mainly pointers to functions.
|
|
Those functions are used by the object itself or its descendant objects.
|
|
For example, GObject class is declared in `gobject.h` in GLib source files.
|
|
|
|
~~~C
|
|
1 typedef struct _GObjectClass GObjectClass;
|
|
2 typedef struct _GObjectClass GInitiallyUnownedClass;
|
|
3
|
|
4 struct _GObjectClass {
|
|
5 GTypeClass g_type_class;
|
|
6 /*< private >*/
|
|
7 GSList *construct_properties;
|
|
8 /*< public >*/
|
|
9 /* seldom overidden */
|
|
10 GObject* (*constructor) (GType type,
|
|
11 guint n_construct_properties,
|
|
12 GObjectConstructParam *construct_properties);
|
|
13 /* overridable methods */
|
|
14 void (*set_property) (GObject *object,
|
|
15 guint property_id,
|
|
16 const GValue *value,
|
|
17 GParamSpec *pspec);
|
|
18 void (*get_property) (GObject *object,
|
|
19 guint property_id,
|
|
20 GValue *value,
|
|
21 GParamSpec *pspec);
|
|
22 void (*dispose) (GObject *object);
|
|
23 void (*finalize) (GObject *object);
|
|
24 /* seldom overidden */
|
|
25 void (*dispatch_properties_changed) (GObject *object,
|
|
26 guint n_pspecs,
|
|
27 GParamSpec **pspecs);
|
|
28 /* signals */
|
|
29 void (*notify) (GObject *object,
|
|
30 GParamSpec *pspec);
|
|
31
|
|
32 /* called when done constructing */
|
|
33 void (*constructed) (GObject *object);
|
|
34 /*< private >*/
|
|
35 gsize flags;
|
|
36 /* padding */
|
|
37 gpointer pdummy[6];
|
|
38 };
|
|
~~~
|
|
|
|
I'd like to explain some of the members.
|
|
There's a pointer to the function `dispose` in line 22.
|
|
|
|
~~~C
|
|
void (*dispose) (GObject *object);
|
|
~~~
|
|
|
|
The declaration is a bit complicated.
|
|
The asterisk before the identifier `dispose` means pointer.
|
|
So, the pointer `dispose` points to a function which has one parameter, which points a GObject structure, and returns no value.
|
|
In the same way, line 23 says `finalize` is a pointer to the function which has one parameter, which points a GObject structure, and returns no value.
|
|
|
|
~~~C
|
|
void (*finalize) (GObject *object);
|
|
~~~
|
|
|
|
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.
|
|
The first one is called disposing.
|
|
In this phase, the instance releases all the references to other instances.
|
|
The second phase is finalizing.
|
|
- 23: A function pointed by `finalize` finishes the destruction process.
|
|
- The other pointers point to functions which are called while the instance lives.
|
|
|
|
## TfeTextView class
|
|
|
|
TfeTextView class is a structure and it includes all its ancestors' class in it.
|
|
Let's look at all the classes from GObject, which is the top level object, to TfeTextView object, which is the lowest.
|
|
|
|
GObject -- GInitiallyUnowned -- GtkWidget -- GtkTextView -- TfeTextView
|
|
|
|
The following is extracts from the source files (not exactly the same).
|
|
|
|
~~~C
|
|
1 struct _GtkWidgetClass {
|
|
2 GInitiallyUnownedClass parent_class;
|
|
3 /*< public >*/
|
|
4 guint activate_signal;
|
|
5 /* basics */
|
|
6 void (* show) (GtkWidget *widget);
|
|
7 void (* hide) (GtkWidget *widget);
|
|
8 void (* map) (GtkWidget *widget);
|
|
9 void (* unmap) (GtkWidget *widget);
|
|
10 void (* realize) (GtkWidget *widget);
|
|
11 void (* unrealize) (GtkWidget *widget);
|
|
12 void (* root) (GtkWidget *widget);
|
|
13 void (* unroot) (GtkWidget *widget);
|
|
14 void (* size_allocate) (GtkWidget *widget,
|
|
15 int width,
|
|
16 int height,
|
|
17 int baseline);
|
|
18 void (* state_flags_changed) (GtkWidget *widget,
|
|
19 GtkStateFlags previous_state_flags);
|
|
20 void (* direction_changed) (GtkWidget *widget,
|
|
21 GtkTextDirection previous_direction);
|
|
22 void (* grab_notify) (GtkWidget *widget,
|
|
23 gboolean was_grabbed);
|
|
24 /* size requests */
|
|
25 GtkSizeRequestMode (* get_request_mode) (GtkWidget *widget);
|
|
26 void (* measure) (GtkWidget *widget,
|
|
27 GtkOrientation orientation,
|
|
28 int for_size,
|
|
29 int *minimum,
|
|
30 int *natural,
|
|
31 int *minimum_baseline,
|
|
32 int *natural_baseline);
|
|
33 /* Mnemonics */
|
|
34 gboolean (* mnemonic_activate) (GtkWidget *widget,
|
|
35 gboolean group_cycling);
|
|
36 /* explicit focus */
|
|
37 gboolean (* grab_focus) (GtkWidget *widget);
|
|
38 gboolean (* focus) (GtkWidget *widget,
|
|
39 GtkDirectionType direction);
|
|
40 void (* set_focus_child) (GtkWidget *widget,
|
|
41 GtkWidget *child);
|
|
42 /* keyboard navigation */
|
|
43 void (* move_focus) (GtkWidget *widget,
|
|
44 GtkDirectionType direction);
|
|
45 gboolean (* keynav_failed) (GtkWidget *widget,
|
|
46 GtkDirectionType direction);
|
|
47 /* accessibility support
|
|
48 */
|
|
49 AtkObject * (* get_accessible) (GtkWidget *widget);
|
|
50 gboolean (* query_tooltip) (GtkWidget *widget,
|
|
51 gint x,
|
|
52 gint y,
|
|
53 gboolean keyboard_tooltip,
|
|
54 GtkTooltip *tooltip);
|
|
55 void (* compute_expand) (GtkWidget *widget,
|
|
56 gboolean *hexpand_p,
|
|
57 gboolean *vexpand_p);
|
|
58 void (* css_changed) (GtkWidget *widget,
|
|
59 GtkCssStyleChange *change);
|
|
60 void (* system_setting_changed) (GtkWidget *widget,
|
|
61 GtkSystemSetting settings);
|
|
62 void (* snapshot) (GtkWidget *widget,
|
|
63 GtkSnapshot *snapshot);
|
|
64 gboolean (* contains) (GtkWidget *widget,
|
|
65 gdouble x,
|
|
66 gdouble y);
|
|
67 /*< private >*/
|
|
68 GtkWidgetClassPrivate *priv;
|
|
69 gpointer padding[8];
|
|
70 };
|
|
71
|
|
72 struct _GtkTextViewClass {
|
|
73 GtkWidgetClass parent_class;
|
|
74 /*< public >*/
|
|
75 void (* move_cursor) (GtkTextView *text_view,
|
|
76 GtkMovementStep step,
|
|
77 gint count,
|
|
78 gboolean extend_selection);
|
|
79 void (* set_anchor) (GtkTextView *text_view);
|
|
80 void (* insert_at_cursor) (GtkTextView *text_view,
|
|
81 const gchar *str);
|
|
82 void (* delete_from_cursor) (GtkTextView *text_view,
|
|
83 GtkDeleteType type,
|
|
84 gint count);
|
|
85 void (* backspace) (GtkTextView *text_view);
|
|
86 void (* cut_clipboard) (GtkTextView *text_view);
|
|
87 void (* copy_clipboard) (GtkTextView *text_view);
|
|
88 void (* paste_clipboard) (GtkTextView *text_view);
|
|
89 void (* toggle_overwrite) (GtkTextView *text_view);
|
|
90 GtkTextBuffer * (* create_buffer) (GtkTextView *text_view);
|
|
91 void (* snapshot_layer) (GtkTextView *text_view,
|
|
92 GtkTextViewLayer layer,
|
|
93 GtkSnapshot *snapshot);
|
|
94 gboolean (* extend_selection) (GtkTextView *text_view,
|
|
95 GtkTextExtendSelection granularity,
|
|
96 const GtkTextIter *location,
|
|
97 GtkTextIter *start,
|
|
98 GtkTextIter *end);
|
|
99 void (* insert_emoji) (GtkTextView *text_view);
|
|
100 /*< private >*/
|
|
101 gpointer padding[8];
|
|
102 };
|
|
103
|
|
104 /* The following definition is generated by the macro G_DECLARE_FINAL_TYPE */
|
|
105 typedef struct {
|
|
106 GtkTextView parent_class;
|
|
107 } TfeTextViewClass;
|
|
108
|
|
~~~
|
|
|
|
- 105-107: This three lines are generated by the macro G\_DECLARE\_FINAL\_TYPE.
|
|
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 ancestors are open to the descendant 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 programming terminology.
|
|
Override is rewriting ancestors' class methods in the descendant class.)
|
|
- Some class methods are often overridden.
|
|
`set_property`, `get_property`, `dispose`, `finalize` and `constructed` are such methods.
|
|
|
|
TfeTextViewClass includes its ancestors' class in it.
|
|
It is illustrated in the following diagram.
|
|
|
|
![The structure of TfeTextView Class](../image/TfeTextViewClass.png)
|
|
|
|
## Destruction of TfeTextView
|
|
|
|
Every Object derived from GObject has a reference count.
|
|
If an object A refers an object B, then A keeps a pointer to B in A and at the same time increases the reference count of B by one with the function `g_object_ref (B)`.
|
|
If A doesn't need B any longer, then A discards the pointer to B (usually it is done by assigning NULL to the pointer) and decreases the reference count of B by one with the function `g_object_unref (B)`.
|
|
|
|
If two objects A and B refer to C, then the reference count of C is two.
|
|
If A no longer needs C, A discards the pointer to C and decreases the reference count in C by one.
|
|
Now the reference count of C is one.
|
|
In the same way, if B no longer needs C, B discards the pointer to C and decreases the reference count in C by one.
|
|
At this moment, no object refers C and the reference count of C is zero.
|
|
This means C is no longer useful.
|
|
Then C destructs itself and finally the memories allocated to C is freed.
|
|
|
|
![Reference count of B](../image/refcount.png)
|
|
|
|
The idea above is based on an assumption that an object referred by nothing has reference count of zero.
|
|
When the reference count drops to zero, the object starts its destruction process.
|
|
The destruction process is spitted into two phases: disposing and finalizing.
|
|
In the disposing process, the object invokes the function pointed by `dispose` in its class to release all references to other objects.
|
|
In the finalizing process, it invokes the function pointed by `finalize` in its class to complete the destruction process.
|
|
These functions are also called handlers or methods.
|
|
For example, dispose handler or dispose method.
|
|
|
|
In the destruction process of TfeTextView, the reference count of widgets related to TfeTextView is automatically decreased.
|
|
But GFile pointed by `tv->file` needs to decrease its reference count by one.
|
|
You must write the code in the dispose handler `tfe_text_view_dispose`.
|
|
|
|
~~~C
|
|
1 static void
|
|
2 tfe_text_view_dispose (GObject *gobject) {
|
|
3 TfeTextView *tv = TFE_TEXT_VIEW (gobject);
|
|
4
|
|
5 if (G_IS_FILE (tv->file))
|
|
6 g_clear_object (&tv->file);
|
|
7
|
|
8 G_OBJECT_CLASS (tfe_text_view_parent_class)->dispose (gobject);
|
|
9 }
|
|
~~~
|
|
|
|
- 5,6: If `tv->file` points a GFile, decrease its reference count.
|
|
`g_clear_object` decreases the reference count and assigns NULL to `tv->file`.
|
|
In dispose handlers, we usually use `g_clear_object` rather than `g_object_unref`.
|
|
- 8: invokes parent's dispose handler. (This will be explained later.)
|
|
|
|
In the disposing process, the object uses the pointer in its class to call the handler.
|
|
Therefore, `tfe_text_view_dispose` needs to be registered in the class when the TfeTextView class is initialized.
|
|
The function `tfe_text_view_class_init` is the class initialization function and it is declared in the replacement produced by `G_DEFINE_TYPE` macro.
|
|
|
|
~~~C
|
|
static void
|
|
tfe_text_view_class_init (TfeTextViewClass *class) {
|
|
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
|
|
|
object_class->dispose = tfe_text_view_dispose;
|
|
|
|
}
|
|
~~~
|
|
|
|
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.
|
|
Each class has its own dispose handler -- `dh1`, `dh2`, `dh3` and `tfe_text_view_dispose`.
|
|
|
|
![dispose handlers](../image/dispose_handler.png)
|
|
|
|
Now, look at the `tfe_text_view_dispose` program above.
|
|
It first releases the reference to GFile object pointed by `tv->file`.
|
|
Then it invokes its parent's dispose handler in line 8.
|
|
|
|
~~~C
|
|
G_OBJECT_CLASS (tfe_text_view_parent_class)->dispose (gobject);
|
|
~~~
|
|
|
|
`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 instance which is casted as a GObject instance.
|
|
`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.
|
|
|
|
|
|
Up: [Readme.md](../Readme.md), Prev: [Section 9](sec9.md), Next: [Section 11](sec11.md)
|