Indented block is replaced by fenced block. fixed #2.

This commit is contained in:
Toshio Sekiya 2021-01-25 21:20:15 +09:00
parent 880dd92c82
commit 7f4442e07a
18 changed files with 456 additions and 296 deletions

View file

@ -39,13 +39,15 @@ First, think about instance of objects.
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 */
typedef struct _TfeTextView TfeTextView;
~~~C
/* This typedef statement is automaticaly generated by the macro G_DECLARE_FINAL_TYPE */
typedef struct _TfeTextView TfeTextView;
struct _TfeTextView {
GtkTextView parent;
GFile *file;
};
struct _TfeTextView {
GtkTextView parent;
GFile *file;
};
~~~
The members of the structure are:
@ -59,28 +61,30 @@ 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 sourcefiles of GTK and GLib.
The following is extracts from the source files (not exactly the same).
typedef struct _GObject GObject;
typedef struct _GObject GInitiallyUnowned;
struct _GObject
{
GTypeInstance g_type_instance;
volatile guint ref_count;
GData *qdata;
};
~~~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 _GtkWidget GtkWidget;
struct _GtkWidget
{
GInitiallyUnowned parent_instance;
GtkWidgetPrivate *priv;
};
typedef struct _GtkTextView GtkTextView;
struct _GtkTextView
{
GtkWidget parent_instance;
GtkTextViewPrivate *priv;
};
typedef struct _GtkTextView GtkTextView;
struct _GtkTextView
{
GtkWidget parent_instance;
GtkTextViewPrivate *priv;
};
~~~
In each structure, its parent instance is declared at the top of the members.
So, every ancestors is included in the child instance.
@ -199,14 +203,18 @@ For example, GObject class is declared in `gobject.h` in GLib source files.
I'd like to explain some of the members.
There's a pointer to the function `dispose` in line 22.
void (*dispose) (GObject *object);
~~~C
void (*dispose) (GObject *object);
~~~
The declaration is a bit complicated.
The asterisk before the identifier `dispose` means pointer.
So, the pointer `disopse` points a function which has one parameter , which points a GObject structure, and returns no value because of void type.
In the same way, line 23 says `finalize` is a pointer to the function which has one paremeter, which points a GObject structure, and returns no value.
void (*finalize) (GObject *object);
~~~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.
@ -398,13 +406,15 @@ In the desposing process, the object uses the pointer in its class to call the h
Therefore, `tfe_text_view_dispose` needs to be registerd 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.
static void
tfe_text_view_class_init (TfeTextViewClass *class) {
GObjectClass *object_class = G_OBJECT_CLASS (class);
~~~C
static void
tfe_text_view_class_init (TfeTextViewClass *class) {
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->dispose = tfe_text_view_dispose;
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.
@ -418,7 +428,9 @@ 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.
G_OBJECT_CLASS (tfe_text_view_parent_class)->dispose (gobject);
~~~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.

View file

@ -45,13 +45,15 @@ This signal is emitted instead of the return value of the function.
Static variable is used to store the signal ID.
If you need to register two or more signals, static array is usually used.
enum {
CHANGE_FILE,
OPEN_RESPONSE,
NUMBER_OF_SIGNALS
};
~~~C
enum {
CHANGE_FILE,
OPEN_RESPONSE,
NUMBER_OF_SIGNALS
};
static guint tfe_text_view_signals[NUMBER_OF_SIGNALS];
static guint tfe_text_view_signals[NUMBER_OF_SIGNALS];
~~~
Signal registration codes are written in the class initialization function.
@ -105,8 +107,10 @@ Such fundamental types are described in [GObject API reference](https://develope
The handlers are as follows.
void change_file_handler (TfeTextView *tv, gpointer user_data);
void open_response_handler (TfeTextView *tv, guint parameter, gpointer user_data);
~~~C
void change_file_handler (TfeTextView *tv, gpointer user_data);
void open_response_handler (TfeTextView *tv, guint parameter, gpointer user_data);
~~~
- Because "change-file" signal doesn't have parameter, the handler's parameter is TfeTextView object and user data.
- Because "open-response" signal has one parameter, the handler's parameter is TfeTextView object, the parameter and user data.
@ -116,13 +120,15 @@ The handlers are as follows.
The parameter is defined in `tfetextview.h` because it is public.
/* "open-response" signal response */
enum
{
TFE_OPEN_RESPONSE_SUCCESS,
TFE_OPEN_RESPONSE_CANCEL,
TFE_OPEN_RESPONSE_ERROR
};
~~~C
/* "open-response" signal response */
enum
{
TFE_OPEN_RESPONSE_SUCCESS,
TFE_OPEN_RESPONSE_CANCEL,
TFE_OPEN_RESPONSE_ERROR
};
~~~
- `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 has canceled to open a file.
@ -137,9 +143,11 @@ The signals "change-file" is connected to a callback function `file_changed` out
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.
g_signal_connect (GTK_TEXT_VIEW (tv), "change-file", G_CALLBACK (file_changed), nb);
~~~C
g_signal_connect (GTK_TEXT_VIEW (tv), "change-file", G_CALLBACK (file_changed), nb);
g_signal_connect (TFE_TEXT_VIEW (tv), "open-response", G_CALLBACK (open_response), nb);
g_signal_connect (TFE_TEXT_VIEW (tv), "open-response", G_CALLBACK (open_response), nb);
~~~
## Signal emission
@ -150,10 +158,12 @@ The relationship between the signal and object (type) is made up when the signal
`g_signal_emit` is used to emit the signal.
The following is extract from `tfetexties.c`.
g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_SUCCESS);
g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_CANCEL);
g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_ERROR);
~~~C
g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_SUCCESS);
g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_CANCEL);
g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_ERROR);
~~~
- The first argument is the object on which the signal is emitted.
- The second argument is the signal id.

View file

@ -60,11 +60,15 @@ Each function will be explained later in this section.
TfeTextView Object is generated by `tfe_text_view_new` or `tfe_text_view_new_with_file`.
GtkWidget *tfe_text_view_new (void);
~~~C
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);
~~~C
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.
@ -131,12 +135,16 @@ Return `tv`.
Save and saveas functions write the contents in GtkTextBuffer to a file.
void tfe_text_view_save (TfeTextView *tv)
~~~C
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)
~~~C
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.
@ -254,7 +262,9 @@ It gets Gfile from GtkFileChooserDialog, save the buffer to the file by calling
Open function shows GtkFileChooserDialog to the user and let them choose a file.
Then read the file and set it to GtkTextBuffer.
void tfe_text_view_open (TfeTextView *tv, GtkWidget *win);
~~~C
void tfe_text_view_open (TfeTextView *tv, GtkWidget *win);
~~~
TfeTextView object `tv` has to be generated in advance.
This function is usually called just after `tv` has been generated.

View file

@ -107,19 +107,25 @@ It implies that CSS can also be apllied to GTK windowing system.
The syntax of CSS is as follws.
selector { color: yellow; padding-top: 10px; ...}
~~~css
selector { color: yellow; padding-top: 10px; ...}
~~~
Every widget has CSS node.
For example GtkTextView has `textview` node.
If you want to set style to GtkTextView, set "textview" to the selector.
textview {color: yeallow; ...}
~~~css
textview {color: yeallow; ...}
~~~
Class, ID and some other things can be applied to the selector like Web CSS. Refer GTK4 API reference for further information.
In line 30, the CSS is a string.
textview {padding: 10px; font-family: monospace; font-size: 12pt;}
~~~css
textview {padding: 10px; font-family: monospace; font-size: 12pt;}
~~~
- padding is a space between the border and contents.
This space makes the text easier to read.
@ -149,21 +155,23 @@ Look at the source file of `startup` handler again.
It is possible to add the provider to the context of GtkTextView instead of GdkDiplay.
To do so, rewrite `tfe_text_view_new`.
GtkWidget *
tfe_text_view_new (void) {
GtkWidget *tv;
~~~C
GtkWidget *
tfe_text_view_new (void) {
GtkWidget *tv;
tv = gtk_widget_new (TFE_TYPE_TEXT_VIEW, NULL);
tv = gtk_widget_new (TFE_TYPE_TEXT_VIEW, NULL);
GtkStyleContext *context;
GtkStyleContext *context;
context = gtk_widget_get_style_context (GTK_WIDGET (tv));
GtkCssProvider *provider = gtk_css_provider_new ();
gtk_css_provider_load_from_data (provider, "textview {padding: 10px; font-family: monospace; font-size: 12pt;}", -1);
gtk_style_context_add_provider (context, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_USER);
context = gtk_widget_get_style_context (GTK_WIDGET (tv));
GtkCssProvider *provider = gtk_css_provider_new ();
gtk_css_provider_load_from_data (provider, "textview {padding: 10px; font-family: monospace; font-size: 12pt;}", -1);
gtk_style_context_add_provider (context, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_USER);
return tv;
}
return tv;
}
~~~
CSS set to the context takes precedence over the one set to the display.

View file

@ -18,15 +18,17 @@ TRUE corresponds to fullscreen and FALSE to non-fullscreen.
The following is an example code to implement a fullscreen menu except the signal handler.
The signal handler will be described after the explanation of this code.
static void
on_activate (GApplication *app, gpointer user_data) {
... ... ...
GSimpleAction *act_fullscreen = g_simple_action_new_stateful ("fullscreen",
NULL, g_variant_new_boolean (FALSE));
GMenuItem *menu_item_fullscreen = g_menu_item_new ("Full Screen", "win.fullscreen");
g_signal_connect (act_fullscreen, "change-state", G_CALLBACK (fullscreen_changed), win);
... ... ...
}
~~~C
static void
on_activate (GApplication *app, gpointer user_data) {
... ... ...
GSimpleAction *act_fullscreen = g_simple_action_new_stateful ("fullscreen",
NULL, g_variant_new_boolean (FALSE));
GMenuItem *menu_item_fullscreen = g_menu_item_new ("Full Screen", "win.fullscreen");
g_signal_connect (act_fullscreen, "change-state", G_CALLBACK (fullscreen_changed), win);
... ... ...
}
~~~
- `act_fullscreen` is GSimpleAction.
It is generated by `g_simple_action_new_stateful`.
@ -52,14 +54,16 @@ Then, the default behaviour for boolean-stated actions with a NULL parameter typ
The following is the "change-state" signal handler.
static void
fullscreen_changed(GSimpleAction *action, GVariant *value, gpointer win) {
if (g_variant_get_boolean (value))
gtk_window_maximize (GTK_WINDOW (win));
else
gtk_window_unmaximize (GTK_WINDOW (win));
g_simple_action_set_state (action, value);
}
~~~C
static void
fullscreen_changed(GSimpleAction *action, GVariant *value, gpointer win) {
if (g_variant_get_boolean (value))
gtk_window_maximize (GTK_WINDOW (win));
else
gtk_window_unmaximize (GTK_WINDOW (win));
g_simple_action_set_state (action, value);
}
~~~
- There are three parameters.
The first parameter is the action which emits the "change-state" signal.
@ -80,11 +84,15 @@ But the way above is the simplest and best.
GVarient can contain boolean, string or other simple type values.
For example, the following program set TRUE to `value` whose type is GVariant.
GVariant *value = g_variant_new_boolean (TRUE);
~~~C
GVariant *value = g_variant_new_boolean (TRUE);
~~~
Another example is:
GVariant *value2 = g_variant_new_string ("Hello");
~~~C
GVariant *value2 = g_variant_new_string ("Hello");
~~~
`value2` is a GVariant and it has a string type value "Hello".
GVariant can contain other types like int16, int32, int64, double and so on.
@ -92,12 +100,16 @@ GVariant can contain other types like int16, int32, int64, double and so on.
If you want to get the original value, use g\_variant\_get series functions.
For example, you can get the boolean value by g\_variant_get_boolean.
gboolean bool = g_variant_get_boolean (value);
~~~C
gboolean bool = g_variant_get_boolean (value);
~~~
Because `value` has been generated as a boolean type GVariant and `TRUE` value, `bool` equals `TRUE`.
In the same way, you can get a string from `value2`
const gchar *str = g_variant_get_string (value2, NULL);
~~~C
const gchar *str = g_variant_get_string (value2, NULL);
~~~
The second parameter is a pointer to gsize type variable (gsize is defined as unsigned long).
If it isn't NULL, then the length of the string will be set by the function.
@ -114,17 +126,19 @@ The action has a state which values are "red", "green" and "blue".
The values are string.
Those colors are given to the signal handler as a parameter.
static void
on_activate (GApplication *app, gpointer user_data) {
... ... ...
GSimpleAction *act_color = g_simple_action_new_stateful ("color",
g_variant_type_new("s"), g_variant_new_string ("red"));
GMenuItem *menu_item_red = g_menu_item_new ("Red", "win.color::red");
GMenuItem *menu_item_green = g_menu_item_new ("Green", "win.color::green");
GMenuItem *menu_item_blue = g_menu_item_new ("Blue", "win.color::blue");
g_signal_connect (act_color, "activate", G_CALLBACK (color_activated), win);
... ... ...
}
~~~C
static void
on_activate (GApplication *app, gpointer user_data) {
... ... ...
GSimpleAction *act_color = g_simple_action_new_stateful ("color",
g_variant_type_new("s"), g_variant_new_string ("red"));
GMenuItem *menu_item_red = g_menu_item_new ("Red", "win.color::red");
GMenuItem *menu_item_green = g_menu_item_new ("Green", "win.color::green");
GMenuItem *menu_item_blue = g_menu_item_new ("Blue", "win.color::blue");
g_signal_connect (act_color, "activate", G_CALLBACK (color_activated), win);
... ... ...
}
~~~
- `act_color` is GSimpleAction.
It is generated by `g_simple_action_new_stateful`.
@ -150,14 +164,16 @@ Then the default behaviour is to call `g_simple_action_set_state()` to set the s
The following is the "activate" signal handler.
static void
color_activated(GSimpleAction *action, GVariant *parameter, gpointer win) {
gchar *color = g_strdup_printf ("label#lb {background-color: %s;}",
g_variant_get_string (parameter, NULL));
gtk_css_provider_load_from_data (provider, color, -1);
g_free (color);
g_action_change_state (G_ACTION (action), parameter);
}
~~~C
static void
color_activated(GSimpleAction *action, GVariant *parameter, gpointer win) {
gchar *color = g_strdup_printf ("label#lb {background-color: %s;}",
g_variant_get_string (parameter, NULL));
gtk_css_provider_load_from_data (provider, color, -1);
g_free (color);
g_action_change_state (G_ACTION (action), parameter);
}
~~~
- There are three parameters.
The first parameter is the action which emits the "activate" signal.

View file

@ -214,8 +214,10 @@ GtkWindow includes GtkWidget at the top of its object.
The function `gtk_window_new` is defined as follows.
GtkWidget *
gtk_window_new (void);
~~~C
GtkWidget *
gtk_window_new (void);
~~~
By this definition, it returns a pointer to GtkWidget, not GtkWindow.
It actually generates a new GtkWindow object (not GtkWidget) but returns a pointer to GtkWidget.
@ -223,11 +225,15 @@ However,the pointer points the GtkWidget and at the same time it also points Gtk
If you want to use `win` as a pointer to the GtkWindow, you need to cast it.
(GtkWindow *) win
~~~C
(GtkWindow *) win
~~~
Or you can use `GTK_WINDOW` macro that performs a similar function.
GTK_WINDOW (win)
~~~C
GTK_WINDOW (win)
~~~
This is a recommended way.
@ -235,7 +241,9 @@ This is a recommended way.
The function `gtk_window_set_application` is used to connect GtkWidow to GtkApplication.
gtk_window_set_application (GTK_WINDOW (win), GTK_APPLICATION (app));
~~~C
gtk_window_set_application (GTK_WINDOW (win), GTK_APPLICATION (app));
~~~
You need to cast `win` to GtkWindow and `app` to GtkApplication.
`GTK_WINDOW` and `GTK_APPLICATION` macro is appropriate for that.

View file

@ -22,17 +22,21 @@ It is described in the GIO API reference.
When GtkApplication is generated, a flag (its type is GApplicationFlags) is given as an argument.
GtkApplication *
gtk_application_new (const gchar *application_id, GApplicationFlags flags);
~~~C
GtkApplication *
gtk_application_new (const gchar *application_id, GApplicationFlags flags);
~~~
This flag is described in the GApplication section in GIO API reference.
GApplicationFlags' Members
~~~C
GApplicationFlags' Members
G_APPLICATION_FLAGS_NONE Default. (No argument allowed)
... ... ...
G_APPLICATION_HANDLES_OPEN This application handles opening files (in the primary instance).
... ... ...
G_APPLICATION_FLAGS_NONE Default. (No argument allowed)
... ... ...
G_APPLICATION_HANDLES_OPEN This application handles opening files (in the primary instance).
... ... ...
~~~
There are ten flags.
But we only need two of them so far.
@ -47,7 +51,9 @@ The application assumes all the arguments are filenames.
Now we use this flag when generating GtkApplication.
app = gtk_application_new ("com.github.ToshioCP.tfv3", G_APPLICATION_HANDLES_OPEN);
~~~C
app = gtk_application_new ("com.github.ToshioCP.tfv3", G_APPLICATION_HANDLES_OPEN);
~~~
### open signal
@ -58,11 +64,13 @@ When the application starts, two signals are possible.
The handler of open signal is called as follows.
void user_function (GApplication *application,
gpointer files,
gint n_files,
gchar *hint,
gpointer user_data)
~~~C
void user_function (GApplication *application,
gpointer files,
gint n_files,
gchar *hint,
gpointer user_data)
~~~
The parameters are as follows:
@ -176,18 +184,20 @@ The point is the handler `on_open`.
The file reading part of the program is shown again below.
if (g_file_load_contents(files[0], NULL, &contents, &length, NULL, NULL)) {
gtk_text_buffer_set_text(tb, contents, length);
g_free(contents);
filename = g_file_get_basename(files[0]);
gtk_window_set_title (GTK_WINDOW (win), filename);
g_free(filename);
gtk_widget_show (win);
} else {
filename = g_file_get_path(files[0]);
g_print ("No such file: %s.\n", filename);
gtk_window_destroy (GTK_WINDOW (win));
}
~~~C
if (g_file_load_contents(files[0], NULL, &contents, &length, NULL, NULL)) {
gtk_text_buffer_set_text(tb, contents, length);
g_free(contents);
filename = g_file_get_basename(files[0]);
gtk_window_set_title (GTK_WINDOW (win), filename);
g_free(filename);
gtk_widget_show (win);
} else {
filename = g_file_get_path(files[0]);
g_print ("No such file: %s.\n", filename);
gtk_window_destroy (GTK_WINDOW (win));
}
~~~
The function `g_file_load_contents` loads the file contents into a buffer, which is automatically allocated, and set the pointer to the buffer into `contents`.
And the length of the buffer is set to `length`.

View file

@ -24,7 +24,9 @@ Using global variables is easy to implement.
Define a sufficient size array of pointers to GFile.
For example,
GFile *f[20];
~~~C
GFile *f[20];
~~~
And `f[i]` corresponds to i-th GtkNotebookPage.
However, there are two problems.
@ -153,7 +155,9 @@ A handler is a C function.
When a function is connected to a certain signal, we call it handler.
The function `before_close` is invoked when the signal "close-request" is emittd.
g_signal_connect (win, "close-request", G_CALLBACK (before_close), NULL);
~~~C
g_signal_connect (win, "close-request", G_CALLBACK (before_close), NULL);
~~~
The argument win is GtkApplicationWindow, in which the signal "close-request" is defined, and before\_close is the handler.
`G_CALLBACK` cast is necessary for the handler.

View file

@ -268,8 +268,10 @@ So it is an old and widely used program.
Make analyzes Makefile and executes compilers.
All instructions are written in Makefile.
sample.o: sample.c
gcc -o sample.o sample.c
~~~Makefile
sample.o: sample.c
gcc -o sample.o sample.c
~~~
The sample of Malefile above consists of three elements, `sample.o`, `sample.c` and `gcc -0 sample.o sample.c`.

View file

@ -37,13 +37,15 @@ First, think about instance of objects.
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 */
typedef struct _TfeTextView TfeTextView;
~~~C
/* This typedef statement is automaticaly generated by the macro G_DECLARE_FINAL_TYPE */
typedef struct _TfeTextView TfeTextView;
struct _TfeTextView {
GtkTextView parent;
GFile *file;
};
struct _TfeTextView {
GtkTextView parent;
GFile *file;
};
~~~
The members of the structure are:
@ -57,28 +59,30 @@ 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 sourcefiles of GTK and GLib.
The following is extracts from the source files (not exactly the same).
typedef struct _GObject GObject;
typedef struct _GObject GInitiallyUnowned;
struct _GObject
{
GTypeInstance g_type_instance;
volatile guint ref_count;
GData *qdata;
};
~~~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 _GtkWidget GtkWidget;
struct _GtkWidget
{
GInitiallyUnowned parent_instance;
GtkWidgetPrivate *priv;
};
typedef struct _GtkTextView GtkTextView;
struct _GtkTextView
{
GtkWidget parent_instance;
GtkTextViewPrivate *priv;
};
typedef struct _GtkTextView GtkTextView;
struct _GtkTextView
{
GtkWidget parent_instance;
GtkTextViewPrivate *priv;
};
~~~
In each structure, its parent instance is declared at the top of the members.
So, every ancestors is included in the child instance.
@ -144,14 +148,18 @@ For example, GObject class is declared in `gobject.h` in GLib source files.
I'd like to explain some of the members.
There's a pointer to the function `dispose` in line 22.
void (*dispose) (GObject *object);
~~~C
void (*dispose) (GObject *object);
~~~
The declaration is a bit complicated.
The asterisk before the identifier `dispose` means pointer.
So, the pointer `disopse` points a function which has one parameter , which points a GObject structure, and returns no value because of void type.
In the same way, line 23 says `finalize` is a pointer to the function which has one paremeter, which points a GObject structure, and returns no value.
void (*finalize) (GObject *object);
~~~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.
@ -224,13 +232,15 @@ In the desposing process, the object uses the pointer in its class to call the h
Therefore, `tfe_text_view_dispose` needs to be registerd 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.
static void
tfe_text_view_class_init (TfeTextViewClass *class) {
GObjectClass *object_class = G_OBJECT_CLASS (class);
~~~C
static void
tfe_text_view_class_init (TfeTextViewClass *class) {
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->dispose = tfe_text_view_dispose;
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.
@ -244,7 +254,9 @@ 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.
G_OBJECT_CLASS (tfe_text_view_parent_class)->dispose (gobject);
~~~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.

View file

@ -43,13 +43,15 @@ This signal is emitted instead of the return value of the function.
Static variable is used to store the signal ID.
If you need to register two or more signals, static array is usually used.
enum {
CHANGE_FILE,
OPEN_RESPONSE,
NUMBER_OF_SIGNALS
};
~~~C
enum {
CHANGE_FILE,
OPEN_RESPONSE,
NUMBER_OF_SIGNALS
};
static guint tfe_text_view_signals[NUMBER_OF_SIGNALS];
static guint tfe_text_view_signals[NUMBER_OF_SIGNALS];
~~~
Signal registration codes are written in the class initialization function.
@ -75,8 +77,10 @@ Such fundamental types are described in [GObject API reference](https://develope
The handlers are as follows.
void change_file_handler (TfeTextView *tv, gpointer user_data);
void open_response_handler (TfeTextView *tv, guint parameter, gpointer user_data);
~~~C
void change_file_handler (TfeTextView *tv, gpointer user_data);
void open_response_handler (TfeTextView *tv, guint parameter, gpointer user_data);
~~~
- Because "change-file" signal doesn't have parameter, the handler's parameter is TfeTextView object and user data.
- Because "open-response" signal has one parameter, the handler's parameter is TfeTextView object, the parameter and user data.
@ -86,13 +90,15 @@ The handlers are as follows.
The parameter is defined in `tfetextview.h` because it is public.
/* "open-response" signal response */
enum
{
TFE_OPEN_RESPONSE_SUCCESS,
TFE_OPEN_RESPONSE_CANCEL,
TFE_OPEN_RESPONSE_ERROR
};
~~~C
/* "open-response" signal response */
enum
{
TFE_OPEN_RESPONSE_SUCCESS,
TFE_OPEN_RESPONSE_CANCEL,
TFE_OPEN_RESPONSE_ERROR
};
~~~
- `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 has canceled to open a file.
@ -107,9 +113,11 @@ The signals "change-file" is connected to a callback function `file_changed` out
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.
g_signal_connect (GTK_TEXT_VIEW (tv), "change-file", G_CALLBACK (file_changed), nb);
~~~C
g_signal_connect (GTK_TEXT_VIEW (tv), "change-file", G_CALLBACK (file_changed), nb);
g_signal_connect (TFE_TEXT_VIEW (tv), "open-response", G_CALLBACK (open_response), nb);
g_signal_connect (TFE_TEXT_VIEW (tv), "open-response", G_CALLBACK (open_response), nb);
~~~
## Signal emission
@ -120,10 +128,12 @@ The relationship between the signal and object (type) is made up when the signal
`g_signal_emit` is used to emit the signal.
The following is extract from `tfetexties.c`.
g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_SUCCESS);
g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_CANCEL);
g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_ERROR);
~~~C
g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_SUCCESS);
g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_CANCEL);
g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_ERROR);
~~~
- The first argument is the object on which the signal is emitted.
- The second argument is the signal id.

View file

@ -23,11 +23,15 @@ Each function will be explained later in this section.
TfeTextView Object is generated by `tfe_text_view_new` or `tfe_text_view_new_with_file`.
GtkWidget *tfe_text_view_new (void);
~~~C
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);
~~~C
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.
@ -69,12 +73,16 @@ Return `tv`.
Save and saveas functions write the contents in GtkTextBuffer to a file.
void tfe_text_view_save (TfeTextView *tv)
~~~C
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)
~~~C
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.
@ -121,7 +129,9 @@ It gets Gfile from GtkFileChooserDialog, save the buffer to the file by calling
Open function shows GtkFileChooserDialog to the user and let them choose a file.
Then read the file and set it to GtkTextBuffer.
void tfe_text_view_open (TfeTextView *tv, GtkWidget *win);
~~~C
void tfe_text_view_open (TfeTextView *tv, GtkWidget *win);
~~~
TfeTextView object `tv` has to be generated in advance.
This function is usually called just after `tv` has been generated.

View file

@ -56,19 +56,25 @@ It implies that CSS can also be apllied to GTK windowing system.
The syntax of CSS is as follws.
selector { color: yellow; padding-top: 10px; ...}
~~~css
selector { color: yellow; padding-top: 10px; ...}
~~~
Every widget has CSS node.
For example GtkTextView has `textview` node.
If you want to set style to GtkTextView, set "textview" to the selector.
textview {color: yeallow; ...}
~~~css
textview {color: yeallow; ...}
~~~
Class, ID and some other things can be applied to the selector like Web CSS. Refer GTK4 API reference for further information.
In line 30, the CSS is a string.
textview {padding: 10px; font-family: monospace; font-size: 12pt;}
~~~css
textview {padding: 10px; font-family: monospace; font-size: 12pt;}
~~~
- padding is a space between the border and contents.
This space makes the text easier to read.
@ -98,21 +104,23 @@ Look at the source file of `startup` handler again.
It is possible to add the provider to the context of GtkTextView instead of GdkDiplay.
To do so, rewrite `tfe_text_view_new`.
GtkWidget *
tfe_text_view_new (void) {
GtkWidget *tv;
~~~C
GtkWidget *
tfe_text_view_new (void) {
GtkWidget *tv;
tv = gtk_widget_new (TFE_TYPE_TEXT_VIEW, NULL);
tv = gtk_widget_new (TFE_TYPE_TEXT_VIEW, NULL);
GtkStyleContext *context;
GtkStyleContext *context;
context = gtk_widget_get_style_context (GTK_WIDGET (tv));
GtkCssProvider *provider = gtk_css_provider_new ();
gtk_css_provider_load_from_data (provider, "textview {padding: 10px; font-family: monospace; font-size: 12pt;}", -1);
gtk_style_context_add_provider (context, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_USER);
context = gtk_widget_get_style_context (GTK_WIDGET (tv));
GtkCssProvider *provider = gtk_css_provider_new ();
gtk_css_provider_load_from_data (provider, "textview {padding: 10px; font-family: monospace; font-size: 12pt;}", -1);
gtk_style_context_add_provider (context, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_USER);
return tv;
}
return tv;
}
~~~
CSS set to the context takes precedence over the one set to the display.

View file

@ -16,15 +16,17 @@ TRUE corresponds to fullscreen and FALSE to non-fullscreen.
The following is an example code to implement a fullscreen menu except the signal handler.
The signal handler will be described after the explanation of this code.
static void
on_activate (GApplication *app, gpointer user_data) {
... ... ...
GSimpleAction *act_fullscreen = g_simple_action_new_stateful ("fullscreen",
NULL, g_variant_new_boolean (FALSE));
GMenuItem *menu_item_fullscreen = g_menu_item_new ("Full Screen", "win.fullscreen");
g_signal_connect (act_fullscreen, "change-state", G_CALLBACK (fullscreen_changed), win);
... ... ...
}
~~~C
static void
on_activate (GApplication *app, gpointer user_data) {
... ... ...
GSimpleAction *act_fullscreen = g_simple_action_new_stateful ("fullscreen",
NULL, g_variant_new_boolean (FALSE));
GMenuItem *menu_item_fullscreen = g_menu_item_new ("Full Screen", "win.fullscreen");
g_signal_connect (act_fullscreen, "change-state", G_CALLBACK (fullscreen_changed), win);
... ... ...
}
~~~
- `act_fullscreen` is GSimpleAction.
It is generated by `g_simple_action_new_stateful`.
@ -50,14 +52,16 @@ Then, the default behaviour for boolean-stated actions with a NULL parameter typ
The following is the "change-state" signal handler.
static void
fullscreen_changed(GSimpleAction *action, GVariant *value, gpointer win) {
if (g_variant_get_boolean (value))
gtk_window_maximize (GTK_WINDOW (win));
else
gtk_window_unmaximize (GTK_WINDOW (win));
g_simple_action_set_state (action, value);
}
~~~C
static void
fullscreen_changed(GSimpleAction *action, GVariant *value, gpointer win) {
if (g_variant_get_boolean (value))
gtk_window_maximize (GTK_WINDOW (win));
else
gtk_window_unmaximize (GTK_WINDOW (win));
g_simple_action_set_state (action, value);
}
~~~
- There are three parameters.
The first parameter is the action which emits the "change-state" signal.
@ -78,11 +82,15 @@ But the way above is the simplest and best.
GVarient can contain boolean, string or other simple type values.
For example, the following program set TRUE to `value` whose type is GVariant.
GVariant *value = g_variant_new_boolean (TRUE);
~~~C
GVariant *value = g_variant_new_boolean (TRUE);
~~~
Another example is:
GVariant *value2 = g_variant_new_string ("Hello");
~~~C
GVariant *value2 = g_variant_new_string ("Hello");
~~~
`value2` is a GVariant and it has a string type value "Hello".
GVariant can contain other types like int16, int32, int64, double and so on.
@ -90,12 +98,16 @@ GVariant can contain other types like int16, int32, int64, double and so on.
If you want to get the original value, use g\_variant\_get series functions.
For example, you can get the boolean value by g\_variant_get_boolean.
gboolean bool = g_variant_get_boolean (value);
~~~C
gboolean bool = g_variant_get_boolean (value);
~~~
Because `value` has been generated as a boolean type GVariant and `TRUE` value, `bool` equals `TRUE`.
In the same way, you can get a string from `value2`
const gchar *str = g_variant_get_string (value2, NULL);
~~~C
const gchar *str = g_variant_get_string (value2, NULL);
~~~
The second parameter is a pointer to gsize type variable (gsize is defined as unsigned long).
If it isn't NULL, then the length of the string will be set by the function.
@ -112,17 +124,19 @@ The action has a state which values are "red", "green" and "blue".
The values are string.
Those colors are given to the signal handler as a parameter.
static void
on_activate (GApplication *app, gpointer user_data) {
... ... ...
GSimpleAction *act_color = g_simple_action_new_stateful ("color",
g_variant_type_new("s"), g_variant_new_string ("red"));
GMenuItem *menu_item_red = g_menu_item_new ("Red", "win.color::red");
GMenuItem *menu_item_green = g_menu_item_new ("Green", "win.color::green");
GMenuItem *menu_item_blue = g_menu_item_new ("Blue", "win.color::blue");
g_signal_connect (act_color, "activate", G_CALLBACK (color_activated), win);
... ... ...
}
~~~C
static void
on_activate (GApplication *app, gpointer user_data) {
... ... ...
GSimpleAction *act_color = g_simple_action_new_stateful ("color",
g_variant_type_new("s"), g_variant_new_string ("red"));
GMenuItem *menu_item_red = g_menu_item_new ("Red", "win.color::red");
GMenuItem *menu_item_green = g_menu_item_new ("Green", "win.color::green");
GMenuItem *menu_item_blue = g_menu_item_new ("Blue", "win.color::blue");
g_signal_connect (act_color, "activate", G_CALLBACK (color_activated), win);
... ... ...
}
~~~
- `act_color` is GSimpleAction.
It is generated by `g_simple_action_new_stateful`.
@ -148,14 +162,16 @@ Then the default behaviour is to call `g_simple_action_set_state()` to set the s
The following is the "activate" signal handler.
static void
color_activated(GSimpleAction *action, GVariant *parameter, gpointer win) {
gchar *color = g_strdup_printf ("label#lb {background-color: %s;}",
g_variant_get_string (parameter, NULL));
gtk_css_provider_load_from_data (provider, color, -1);
g_free (color);
g_action_change_state (G_ACTION (action), parameter);
}
~~~C
static void
color_activated(GSimpleAction *action, GVariant *parameter, gpointer win) {
gchar *color = g_strdup_printf ("label#lb {background-color: %s;}",
g_variant_get_string (parameter, NULL));
gtk_css_provider_load_from_data (provider, color, -1);
g_free (color);
g_action_change_state (G_ACTION (action), parameter);
}
~~~
- There are three parameters.
The first parameter is the action which emits the "activate" signal.

View file

@ -169,8 +169,10 @@ GtkWindow includes GtkWidget at the top of its object.
The function `gtk_window_new` is defined as follows.
GtkWidget *
gtk_window_new (void);
~~~C
GtkWidget *
gtk_window_new (void);
~~~
By this definition, it returns a pointer to GtkWidget, not GtkWindow.
It actually generates a new GtkWindow object (not GtkWidget) but returns a pointer to GtkWidget.
@ -178,11 +180,15 @@ However,the pointer points the GtkWidget and at the same time it also points Gtk
If you want to use `win` as a pointer to the GtkWindow, you need to cast it.
(GtkWindow *) win
~~~C
(GtkWindow *) win
~~~
Or you can use `GTK_WINDOW` macro that performs a similar function.
GTK_WINDOW (win)
~~~C
GTK_WINDOW (win)
~~~
This is a recommended way.
@ -190,7 +196,9 @@ This is a recommended way.
The function `gtk_window_set_application` is used to connect GtkWidow to GtkApplication.
gtk_window_set_application (GTK_WINDOW (win), GTK_APPLICATION (app));
~~~C
gtk_window_set_application (GTK_WINDOW (win), GTK_APPLICATION (app));
~~~
You need to cast `win` to GtkWindow and `app` to GtkApplication.
`GTK_WINDOW` and `GTK_APPLICATION` macro is appropriate for that.

View file

@ -20,17 +20,21 @@ It is described in the GIO API reference.
When GtkApplication is generated, a flag (its type is GApplicationFlags) is given as an argument.
GtkApplication *
gtk_application_new (const gchar *application_id, GApplicationFlags flags);
~~~C
GtkApplication *
gtk_application_new (const gchar *application_id, GApplicationFlags flags);
~~~
This flag is described in the GApplication section in GIO API reference.
GApplicationFlags' Members
~~~C
GApplicationFlags' Members
G_APPLICATION_FLAGS_NONE Default. (No argument allowed)
... ... ...
G_APPLICATION_HANDLES_OPEN This application handles opening files (in the primary instance).
... ... ...
G_APPLICATION_FLAGS_NONE Default. (No argument allowed)
... ... ...
G_APPLICATION_HANDLES_OPEN This application handles opening files (in the primary instance).
... ... ...
~~~
There are ten flags.
But we only need two of them so far.
@ -45,7 +49,9 @@ The application assumes all the arguments are filenames.
Now we use this flag when generating GtkApplication.
app = gtk_application_new ("com.github.ToshioCP.tfv3", G_APPLICATION_HANDLES_OPEN);
~~~C
app = gtk_application_new ("com.github.ToshioCP.tfv3", G_APPLICATION_HANDLES_OPEN);
~~~
### open signal
@ -56,11 +62,13 @@ When the application starts, two signals are possible.
The handler of open signal is called as follows.
void user_function (GApplication *application,
gpointer files,
gint n_files,
gchar *hint,
gpointer user_data)
~~~C
void user_function (GApplication *application,
gpointer files,
gint n_files,
gchar *hint,
gpointer user_data)
~~~
The parameters are as follows:
@ -117,18 +125,20 @@ The point is the handler `on_open`.
The file reading part of the program is shown again below.
if (g_file_load_contents(files[0], NULL, &contents, &length, NULL, NULL)) {
gtk_text_buffer_set_text(tb, contents, length);
g_free(contents);
filename = g_file_get_basename(files[0]);
gtk_window_set_title (GTK_WINDOW (win), filename);
g_free(filename);
gtk_widget_show (win);
} else {
filename = g_file_get_path(files[0]);
g_print ("No such file: %s.\n", filename);
gtk_window_destroy (GTK_WINDOW (win));
}
~~~C
if (g_file_load_contents(files[0], NULL, &contents, &length, NULL, NULL)) {
gtk_text_buffer_set_text(tb, contents, length);
g_free(contents);
filename = g_file_get_basename(files[0]);
gtk_window_set_title (GTK_WINDOW (win), filename);
g_free(filename);
gtk_widget_show (win);
} else {
filename = g_file_get_path(files[0]);
g_print ("No such file: %s.\n", filename);
gtk_window_destroy (GTK_WINDOW (win));
}
~~~
The function `g_file_load_contents` loads the file contents into a buffer, which is automatically allocated, and set the pointer to the buffer into `contents`.
And the length of the buffer is set to `length`.

View file

@ -22,7 +22,9 @@ Using global variables is easy to implement.
Define a sufficient size array of pointers to GFile.
For example,
GFile *f[20];
~~~C
GFile *f[20];
~~~
And `f[i]` corresponds to i-th GtkNotebookPage.
However, there are two problems.
@ -151,7 +153,9 @@ A handler is a C function.
When a function is connected to a certain signal, we call it handler.
The function `before_close` is invoked when the signal "close-request" is emittd.
g_signal_connect (win, "close-request", G_CALLBACK (before_close), NULL);
~~~C
g_signal_connect (win, "close-request", G_CALLBACK (before_close), NULL);
~~~
The argument win is GtkApplicationWindow, in which the signal "close-request" is defined, and before\_close is the handler.
`G_CALLBACK` cast is necessary for the handler.

View file

@ -78,8 +78,10 @@ So it is an old and widely used program.
Make analyzes Makefile and executes compilers.
All instructions are written in Makefile.
sample.o: sample.c
gcc -o sample.o sample.c
~~~Makefile
sample.o: sample.c
gcc -o sample.o sample.c
~~~
The sample of Malefile above consists of three elements, `sample.o`, `sample.c` and `gcc -0 sample.o sample.c`.