mirror of
https://github.com/ToshioCP/Gtk4-tutorial.git
synced 2025-01-12 20:03:28 +01:00
Tfetextview.c and tfetextview.h is moved to tfetextview folder. Bugs and typo error are fixed.
This commit is contained in:
parent
204709834a
commit
c3c4668597
55 changed files with 1375 additions and 1792 deletions
|
@ -13,7 +13,7 @@ You can post it to [github issues](https://github.com/ToshioCP/Gtk4-tutorial/iss
|
||||||
The latest version of the tutorial is located at [Gtk4-tutorial githup repository](https://github.com/ToshioCP/Gtk4-tutorial).
|
The latest version of the tutorial is located at [Gtk4-tutorial githup repository](https://github.com/ToshioCP/Gtk4-tutorial).
|
||||||
You can read it without download.
|
You can read it without download.
|
||||||
|
|
||||||
1. [Prerequisite and Licence](gfm/sec1.md)
|
1. [Prerequisite and License](gfm/sec1.md)
|
||||||
1. [Installation of gtk4 to linux distributions](gfm/sec2.md)
|
1. [Installation of gtk4 to linux distributions](gfm/sec2.md)
|
||||||
1. [GtkApplication and GtkApplicationWindow](gfm/sec3.md)
|
1. [GtkApplication and GtkApplicationWindow](gfm/sec3.md)
|
||||||
1. [Widgets (1)](gfm/sec4.md)
|
1. [Widgets (1)](gfm/sec4.md)
|
||||||
|
@ -25,7 +25,7 @@ You can read it without download.
|
||||||
1. [Instance and class](gfm/sec10.md)
|
1. [Instance and class](gfm/sec10.md)
|
||||||
1. [Signals](gfm/sec11.md)
|
1. [Signals](gfm/sec11.md)
|
||||||
1. [Functions in TfeTextView](gfm/sec12.md)
|
1. [Functions in TfeTextView](gfm/sec12.md)
|
||||||
1. [Functions with GtkNotebook](gfm/sec13.md)
|
1. [Functions in GtkNotebook](gfm/sec13.md)
|
||||||
1. [tfeapplication.c](gfm/sec14.md)
|
1. [tfeapplication.c](gfm/sec14.md)
|
||||||
1. [tfe5 source files](gfm/sec15.md)
|
1. [tfe5 source files](gfm/sec15.md)
|
||||||
1. [Menu and action](gfm/sec16.md)
|
1. [Menu and action](gfm/sec16.md)
|
||||||
|
|
24
gfm/sec1.md
24
gfm/sec1.md
|
@ -1,20 +1,20 @@
|
||||||
Up: [Readme.md](../Readme.md), Next: [Section 2](sec2.md)
|
Up: [Readme.md](../Readme.md), Next: [Section 2](sec2.md)
|
||||||
|
|
||||||
# Prerequisite and Licence
|
# Prerequisite and License
|
||||||
|
|
||||||
## Prerequisite
|
## Prerequisite
|
||||||
|
|
||||||
### Tutorial document
|
### Tutorial document
|
||||||
|
|
||||||
This tutorial is about gtk4 libraries.
|
This tutorial is about gtk4 libraries.
|
||||||
It is originally used on linux with C compiler, but now it is used more widely, on windows and macOS, with Vala, python and so on.
|
It is originally used on Linux with C compiler, but now it is used more widely, on windows and macOS, with Vala, python and so on.
|
||||||
However, this tutorial describes only _C programs on linux_.
|
However, this tutorial describes only _C programs on Linux_.
|
||||||
|
|
||||||
If you want to try the examples in the tutorial, you need:
|
If you want to try the examples in the tutorial, you need:
|
||||||
|
|
||||||
- PC with linux distribution like ubuntu, debian and so on.
|
- PC with Linux distribution like Ubuntu, Debian and so on.
|
||||||
- Gcc
|
- Gcc
|
||||||
- Gtk4. Gtk included linux distributions is version three at present.
|
- Gtk4. Gtk included Linux distributions is version three at present.
|
||||||
You need to install gtk4 to your computer.
|
You need to install gtk4 to your computer.
|
||||||
Refer to [gtk4 gitlab repository](https://gitlab.gnome.org/GNOME/gtk).
|
Refer to [gtk4 gitlab repository](https://gitlab.gnome.org/GNOME/gtk).
|
||||||
However, it might make some trouble like, for example, your pc doesn't recognize usb port
|
However, it might make some trouble like, for example, your pc doesn't recognize usb port
|
||||||
|
@ -23,32 +23,32 @@ Therefore, I strongly recommend you not to install gtk4 to `/usr/local` on the c
|
||||||
Instead,
|
Instead,
|
||||||
|
|
||||||
- Install it to another computer only used to try gtk4.
|
- Install it to another computer only used to try gtk4.
|
||||||
- Install it to your home directory, for example `$HOME/local`, in order to separte gtk4 from your system.
|
- Install it to your home directory, for example `$HOME/local`, in order to separate gtk4 from your system.
|
||||||
|
|
||||||
The second choice will be explained in [Section 3](sec3.md).
|
The second choice will be explained in [Section 3](sec3.md).
|
||||||
|
|
||||||
### Software
|
### Software
|
||||||
|
|
||||||
This repository inclludes ruby programs.
|
This repository includes ruby programs.
|
||||||
They are used to generate markdown files, html files, latex files and a pdf file.
|
They are used to generate markdown files, html files, latex files and a pdf file.
|
||||||
|
|
||||||
You need:
|
You need:
|
||||||
|
|
||||||
- Linux distribution like ubuntu.
|
- Linux distribution like Ubuntu.
|
||||||
- Ruby programing language.
|
- Ruby programming language.
|
||||||
There are two ways to install it.
|
There are two ways to install it.
|
||||||
One is install the distribution's package.
|
One is install the distribution's package.
|
||||||
The other is using rbenv and ruby-build.
|
The other is using rbenv and ruby-build.
|
||||||
If you want to use the latest version of ruby, use rbenv.
|
If you want to use the latest version of ruby, use rbenv.
|
||||||
- Rake.
|
- Rake.
|
||||||
It is a gem, which is a library written in ruby.
|
It is a gem, which is a library written in ruby.
|
||||||
You can install it as a package of your ditribution or use gem command.
|
You can install it as a package of your distribution or use gem command.
|
||||||
|
|
||||||
## Licence
|
## License
|
||||||
|
|
||||||
Copyright (C) 2020 ToshioCP (Toshio Sekiya)
|
Copyright (C) 2020 ToshioCP (Toshio Sekiya)
|
||||||
|
|
||||||
Gtk4 tutorial repository containes the tutorial document and softwares such as converters, generators and controlers.
|
Gtk4 tutorial repository contains the tutorial document and software such as converters, generators and controllers.
|
||||||
All of them make up the 'Gtk4 tutorial' package.
|
All of them make up the 'Gtk4 tutorial' package.
|
||||||
This package is simply called 'Gtk4 tutorial' in the following description.
|
This package is simply called 'Gtk4 tutorial' in the following description.
|
||||||
'Gtk4 tutorial' is free; you can redistribute it and/or modify it under the terms of the GNU General Public License
|
'Gtk4 tutorial' is free; you can redistribute it and/or modify it under the terms of the GNU General Public License
|
||||||
|
|
91
gfm/sec10.md
91
gfm/sec10.md
|
@ -6,6 +6,7 @@ This section and the following four sections are explanations about the next ver
|
||||||
It is tfe5.
|
It is tfe5.
|
||||||
It has many changes from the prior version.
|
It has many changes from the prior version.
|
||||||
All the sources are listed in [Section 15](sec15.md).
|
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
|
## Encapsulation
|
||||||
|
|
||||||
|
@ -23,24 +24,24 @@ 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 generating (or initializing) TfeTextView?
|
||||||
- What is necessary to GFile when destructing TfeTextView?
|
- What is necessary to GFile when destructing TfeTextView?
|
||||||
- TfeTextView should read/write a file by itself or not?
|
- TfeTextView should read/write a file by itself or not?
|
||||||
- How it communicate with objects outside?
|
- How it communicates with objects outside?
|
||||||
|
|
||||||
You need to know at least class/instance and signals before thinking about them.
|
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.
|
I will explain them in this section and the next section.
|
||||||
After that I will explain:
|
After that I will explain:
|
||||||
|
|
||||||
- Organizing functions.
|
- Organizing functions.
|
||||||
- How to use FileChooserDialog
|
- How to use GtkFileChooserDialog
|
||||||
|
|
||||||
## GObject and its children
|
## GObject and its children
|
||||||
|
|
||||||
GObject and its children are objects, which have both class and instance.
|
GObject and its children are objects, which have both class and instance.
|
||||||
First, think about instance of objects.
|
First, think about instance of objects.
|
||||||
Instance is structured memories and the structure is described as C language structure.
|
Instance is structured memories and described as C language structure.
|
||||||
The following is a structure of TfeTextView.
|
The following is a structure of TfeTextView.
|
||||||
|
|
||||||
~~~C
|
~~~C
|
||||||
/* This typedef statement is automaticaly generated by the macro G_DECLARE_FINAL_TYPE */
|
/* This typedef statement is automatically generated by the macro G_DECLARE_FINAL_TYPE */
|
||||||
typedef struct _TfeTextView TfeTextView;
|
typedef struct _TfeTextView TfeTextView;
|
||||||
|
|
||||||
struct _TfeTextView {
|
struct _TfeTextView {
|
||||||
|
@ -51,14 +52,14 @@ struct _TfeTextView {
|
||||||
|
|
||||||
The members of the structure are:
|
The members of the structure are:
|
||||||
|
|
||||||
- `parent` is the structure of GtkTextView which is the parent object of TfeTextView.
|
- `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.
|
- `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.
|
Notice the program above is the declaration of the structure, not the definition.
|
||||||
So, no memories are allocated at this moment.
|
So, no memories are allocated at this moment.
|
||||||
They are to be allocated when `tfe_text_view_new` function is invoked.
|
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.
|
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).
|
The following is extracts from the source files (not exactly the same).
|
||||||
|
|
||||||
~~~C
|
~~~C
|
||||||
|
@ -86,7 +87,7 @@ struct _GtkTextView
|
||||||
};
|
};
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
In each structure, its parent instance is declared at the top of the members.
|
In each structure, its parent instance is declared at the top of members.
|
||||||
So, every ancestors is included in the child instance.
|
So, every ancestors is included in the child instance.
|
||||||
This is very important.
|
This is very important.
|
||||||
It guarantees a child widget to derive all the features from ancestors.
|
It guarantees a child widget to derive all the features from ancestors.
|
||||||
|
@ -108,10 +109,10 @@ The function `tfe_text_view_new` generates a new TfeTextView instance.
|
||||||
|
|
||||||
When this function is run, the following procedure is gone through.
|
When this function is run, the following procedure is gone through.
|
||||||
|
|
||||||
1. Initialize GObject instance in TfeTextView instance.
|
1. Initialize GObject part in TfeTextView instance.
|
||||||
2. Initialize GtkWidget instance in TfeTextView instance.
|
2. Initialize GtkWidget part in TfeTextView instance.
|
||||||
3. Initialize GtkTextView instance in TfeTextView instance.
|
3. Initialize GtkTextView part in TfeTextView instance.
|
||||||
4. Initialize TfeTextView instance.
|
4. Initialize TfeTextView part in TfeTextView instance.
|
||||||
|
|
||||||
Step one through three is done automatically.
|
Step one through three is done automatically.
|
||||||
Step four is done by the function `tfe_text_view_init`.
|
Step four is done by the function `tfe_text_view_init`.
|
||||||
|
@ -122,30 +123,19 @@ Step four is done by the function `tfe_text_view_init`.
|
||||||
~~~C
|
~~~C
|
||||||
1 static void
|
1 static void
|
||||||
2 tfe_text_view_init (TfeTextView *tv) {
|
2 tfe_text_view_init (TfeTextView *tv) {
|
||||||
3 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
3 tv->file = NULL;
|
||||||
4
|
4 }
|
||||||
5 tv->file = NULL;
|
|
||||||
6 gtk_text_buffer_set_modified (tb, FALSE);
|
|
||||||
7 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR);
|
|
||||||
8 }
|
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
`tfe_text_view_init` initializes the instance.
|
This function just initializes `tv->file` to be `NULL`.
|
||||||
|
|
||||||
- 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
|
## Functions and Classes
|
||||||
|
|
||||||
In Gtk, all objects derived from GObject have class and instance.
|
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.
|
Instance is memories which has a structure defined by C structure declaration as I mentioned in the previous two subsections.
|
||||||
An instance can be generated two times or more.
|
Each object can have more than one instance.
|
||||||
Those instances have the same structure.
|
Those instances have the same structure.
|
||||||
Instance, which is structured memories, only keeps status of the object.
|
Instance, which is structured memories, only keeps status of the instance.
|
||||||
Therefore, it is insufficient to define its behavior.
|
Therefore, it is insufficient to define its behavior.
|
||||||
We need at least two things.
|
We need at least two things.
|
||||||
One is functions and the other is class.
|
One is functions and the other is class.
|
||||||
|
@ -156,7 +146,7 @@ These functions are similar to object methods in object oriented languages such
|
||||||
Functions are public, which means that they are expected to be used by other objects.
|
Functions are public, which means that they are expected to be used by other objects.
|
||||||
|
|
||||||
Class comprises mainly pointers to functions.
|
Class comprises mainly pointers to functions.
|
||||||
Those functions are used by the object itself or its descendent objects.
|
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.
|
For example, GObject class is declared in `gobject.h` in GLib source files.
|
||||||
|
|
||||||
~~~C
|
~~~C
|
||||||
|
@ -209,8 +199,8 @@ void (*dispose) (GObject *object);
|
||||||
|
|
||||||
The declaration is a bit complicated.
|
The declaration is a bit complicated.
|
||||||
The asterisk before the identifier `dispose` means pointer.
|
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 because of void type.
|
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 paremeter, 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
|
~~~C
|
||||||
void (*finalize) (GObject *object);
|
void (*finalize) (GObject *object);
|
||||||
|
@ -219,7 +209,11 @@ void (*finalize) (GObject *object);
|
||||||
Look at the declaration of `_GObjectClass` so that you would find that most of the members are pointers to functions.
|
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.
|
- 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 and the instance releases all the references to other instances. The second one 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.
|
||||||
|
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.
|
- 23: A function pointed by `finalize` finishes the destruction process.
|
||||||
- The other pointers point to functions which are called while the instance lives.
|
- The other pointers point to functions which are called while the instance lives.
|
||||||
|
|
||||||
|
@ -347,11 +341,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`.
|
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.
|
- 2, 73, 106: Each derived class puts its parent class at the first member of its structure.
|
||||||
It is the same as instance structures.
|
It is the same as instance structures.
|
||||||
- Class members in ancestors are open to the descendent class.
|
- Class members in ancestors are open to the descendant class.
|
||||||
So, they can be changed in `tfe_text_view_class_init` function.
|
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`.
|
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 an object oriented programming terminology.
|
||||||
Override is rewriting ancestors' class methods in the descendent class.)
|
Override is rewriting ancestors' class methods in the descendant class.)
|
||||||
- Some class methods are often overridden.
|
- Some class methods are often overridden.
|
||||||
`set_property`, `get_property`, `dispose`, `finalize` and `constructed` are such methods.
|
`set_property`, `get_property`, `dispose`, `finalize` and `constructed` are such methods.
|
||||||
|
|
||||||
|
@ -363,24 +357,26 @@ It is illustrated in the following diagram.
|
||||||
## Destruction of TfeTextView
|
## Destruction of TfeTextView
|
||||||
|
|
||||||
Every Object derived from GObject has a reference count.
|
Every Object derived from GObject has a reference count.
|
||||||
If an object A uses 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 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 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 two objects A and B refer to C, then the reference count of C is two.
|
||||||
After A used C and if A no longer needs C, A discards the pointer to C and decreases the reference count in C by one.
|
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.
|
Now the reference count of C is one.
|
||||||
In the same way, when B no longer needs C, B discards the pointer to C and decreases the reference count in C by 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.
|
At this moment, no object refers C and the reference count of C is zero.
|
||||||
This means C is no longer useful.
|
This means C is no longer useful.
|
||||||
Then C destructs itself and finally the memories allocated to C is freed.
|
Then C destructs itself and finally the memories allocated to C is freed.
|
||||||
|
|
||||||
![Reference count of B](../image/refcount.png)
|
![Reference count of B](../image/refcount.png)
|
||||||
|
|
||||||
The idea above is based on an assumption that an object refered by nothing has reference count of zero.
|
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.
|
When the reference count drops to zero, the object starts its destruction process.
|
||||||
The destruction process is split in two phases: disposing and finalizing.
|
The destruction process is spitted into two phases: disposing and finalizing.
|
||||||
In the disposing process, the object invokes the handler pointed by `dispose` in its class to release all references to other objects.
|
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 handler pointed by `finalize` in its class to complete the destruction process.
|
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.
|
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.
|
But GFile pointed by `tv->file` needs to decrease its reference count by one.
|
||||||
|
@ -399,10 +395,11 @@ You must write the code in the dispose handler `tfe_text_view_dispose`.
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
- 5,6: If `tv->file` points a GFile, decrease its reference count.
|
- 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`.
|
`g_clear_object` decreases the reference count and assigns NULL to `tv->file`.
|
||||||
- 8: invoke parent's despose handler. (This will be explained later.)
|
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 desposing process, the object uses the pointer in its class to call the handler.
|
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.
|
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.
|
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.
|
||||||
|
|
||||||
|
@ -422,7 +419,7 @@ Look at the following diagram.
|
||||||
There are four classes -- GObjectClass (GInitiallyUnownedClass), GtkWidgetClass, GtkTextViewClass and TfeTextViewClass.
|
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`.
|
Each class has its own dispose handler -- `dh1`, `dh2`, `dh3` and `tfe_text_view_dispose`.
|
||||||
|
|
||||||
![dispose handers](../image/dispose_handler.png)
|
![dispose handlers](../image/dispose_handler.png)
|
||||||
|
|
||||||
Now, look at the `tfe_text_view_dispose` program above.
|
Now, look at the `tfe_text_view_dispose` program above.
|
||||||
It first releases the reference to GFile object pointed by `tv->file`.
|
It first releases the reference to GFile object pointed by `tv->file`.
|
||||||
|
@ -434,7 +431,7 @@ 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.
|
`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.
|
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 instanse.
|
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.
|
`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`.
|
After that, `dh3` calls `dh2`, and `dh2` calls `dh1`.
|
||||||
Finally all the references are released.
|
Finally all the references are released.
|
||||||
|
|
54
gfm/sec11.md
54
gfm/sec11.md
|
@ -19,18 +19,18 @@ Then the handler, which has been connected to the signal, is invoked.
|
||||||
|
|
||||||
The caller of the function or the handler connected to the signal is usually outside of the object.
|
The caller of the function or the handler connected to the signal is usually outside of the object.
|
||||||
One of the difference between these two is that the object is active or passive.
|
One of the difference between these two is that the object is active or passive.
|
||||||
In functions the object responds to the caller.
|
In functions the object passively responds to the caller.
|
||||||
In signals the object actively sends a signal to the handler.
|
In signals the object actively sends a signal to the handler.
|
||||||
|
|
||||||
GObject signal can be registered, connected and emitted.
|
GObject signals are registered, connected and emitted.
|
||||||
|
|
||||||
1. A signal is registered with the object type on which it can be emitted.
|
1. Signals are registered with the object type on which they can be emitted.
|
||||||
This is done usually when the class is initialized.
|
The registration is done usually when the class is initialized.
|
||||||
2. It is connected to a handler by `g_connect_signal` or its family functions.
|
2. Signals are connected to handlers by `g_connect_signal` or its family functions.
|
||||||
3. When it is emmitted, the connected handler is invoked.
|
3. When Signals are emitted, the connected handlers are invoked.
|
||||||
|
|
||||||
Step one and three are done in the object on which the signal is emitted.
|
Step one and three are done in the object on which the signal belongs.
|
||||||
Step two is usually done outside the objects.
|
Step two is usually done outside the object.
|
||||||
|
|
||||||
## Signal registration
|
## Signal registration
|
||||||
|
|
||||||
|
@ -39,11 +39,11 @@ In TfeTextView, two signals are registered.
|
||||||
- "change-file" signal.
|
- "change-file" signal.
|
||||||
This signal is emitted when `tv->file` is changed.
|
This signal is emitted when `tv->file` is changed.
|
||||||
- "open-response" signal.
|
- "open-response" signal.
|
||||||
`tfe_text_view_open` function is not able to return the status because of using GtkFileChooserDialog.
|
`tfe_text_view_open` function is not able to return the status because it uses GtkFileChooserDialog.
|
||||||
This signal is emitted instead of the return value of the function.
|
This signal is emitted instead of the return value of the function.
|
||||||
|
|
||||||
Static variable is used to store the signal ID.
|
A static variable or array is used to store the signal ID.
|
||||||
If you need to register two or more signals, static array is usually used.
|
A static array is used to register two or more signals.
|
||||||
|
|
||||||
~~~C
|
~~~C
|
||||||
enum {
|
enum {
|
||||||
|
@ -87,7 +87,7 @@ Signal registration codes are written in the class initialization function.
|
||||||
27 }
|
27 }
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
- 6-15: Register "change-file"signal.
|
- 6-15: Registers "change-file" signal.
|
||||||
`g_signal_newv` function is used.
|
`g_signal_newv` function is used.
|
||||||
This signal has no default handler (object method handler).
|
This signal has no default handler (object method handler).
|
||||||
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.
|
||||||
|
@ -95,7 +95,7 @@ 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 return value of `g_signal_newv` is the signal id.
|
||||||
The type of signal id is guint, which is the same as unsigned int.
|
The type of signal id is guint, which is the same as unsigned int.
|
||||||
It is used when the signal is emitted.
|
It is used when the signal is emitted.
|
||||||
- 16-26: Register "open-response" signal.
|
- 16-26: Registers "open-response" signal.
|
||||||
This signal has a parameter.
|
This signal has a parameter.
|
||||||
- 25: Number of the parameter.
|
- 25: Number of the parameter.
|
||||||
"open-response" signal has one parameter.
|
"open-response" signal has one parameter.
|
||||||
|
@ -105,20 +105,20 @@ It has one element, which is `G_TYPE_INT`.
|
||||||
`G_TYPE_INT` is a type of integer.
|
`G_TYPE_INT` is a type of integer.
|
||||||
Such fundamental types are described in [GObject API reference](https://developer.gnome.org/gobject/stable/gobject-Type-Information.html).
|
Such fundamental types are described in [GObject API reference](https://developer.gnome.org/gobject/stable/gobject-Type-Information.html).
|
||||||
|
|
||||||
The handlers are as follows.
|
The handlers are declared as follows.
|
||||||
|
|
||||||
~~~C
|
~~~C
|
||||||
void change_file_handler (TfeTextView *tv, gpointer user_data);
|
void change_file_handler (TfeTextView *tv, gpointer user_data);
|
||||||
void open_response_handler (TfeTextView *tv, guint parameter, 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 "change-file" signal doesn't have parameter, the handler's parameters are a TfeTextView object and user data.
|
||||||
- Because "open-response" signal has one parameter, the handler's parameter is TfeTextView object, the parameter and user data.
|
- Because "open-response" signal has one parameter, the handler's parameters are a TfeTextView object, the signal's parameter and a user data.
|
||||||
- `tv` is the object instance on which the signal is emitted.
|
- `tv` is the object instance on which the signal is emitted.
|
||||||
- `user_data` comes from the fourth argument of `g_signal_connect`.
|
- `user_data` comes from the fourth argument of `g_signal_connect`.
|
||||||
- `parameter` comes from the fourth argument of `g_signal_emit`.
|
- `parameter` comes from the fourth argument of `g_signal_emit`.
|
||||||
|
|
||||||
The parameter is defined in `tfetextview.h` because it is public.
|
The values of the parameter is defined in `tfetextview.h` because they are public.
|
||||||
|
|
||||||
~~~C
|
~~~C
|
||||||
/* "open-response" signal response */
|
/* "open-response" signal response */
|
||||||
|
@ -130,18 +130,22 @@ enum
|
||||||
};
|
};
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
- `TFE_OPEN_RESPONSE_SUCCESS` is set when `tfe_text_view_open` successfully has opend a file and loaded it.
|
- The parameter is set to `TFE_OPEN_RESPONSE_SUCCESS` when `tfe_text_view_open` successfully has opened a file and loaded it.
|
||||||
- `TFE_OPEN_RESPONSE_CANCEL` is set when the user has canceled to open a file.
|
- The parameter is set to `TFE_OPEN_RESPONSE_CANCEL` when the user has canceled to open a file.
|
||||||
- `TFE_OPEN_RESPONSE_ERROR` is set when error has occured.
|
- The parameter is set to `TFE_OPEN_RESPONSE_ERROR` when error has occurred.
|
||||||
|
|
||||||
## Signal connection
|
## Signal connection
|
||||||
|
|
||||||
A signal and a handler are connected by the function `g_signal_connect`.
|
A signal and a handler are connected by the function `g_signal_connect`.
|
||||||
There are some similar functions like `g_signal_connect_after`, `g_signal_connect_swapped` and so on.
|
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.
|
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.
|
The signals "change-file" is connected to a callback function outside of TfeTextView object.
|
||||||
In the same way, the signals "open-response" is connected to a callback function `open_response` outside of TfeTextView object.
|
In the same way, the signals "open-response" is connected to a callback function outside of TfeTextView object.
|
||||||
The functions `file_changed` and `open_response` will be explained later.
|
Those callback functions are defined by users.
|
||||||
|
|
||||||
|
In the program tfe, callback functions are defined in `tfenotebook.c`.
|
||||||
|
And their names are `file_changed` and `open_response`.
|
||||||
|
They will be explained later.
|
||||||
|
|
||||||
~~~C
|
~~~C
|
||||||
g_signal_connect (GTK_TEXT_VIEW (tv), "change-file", G_CALLBACK (file_changed), nb);
|
g_signal_connect (GTK_TEXT_VIEW (tv), "change-file", G_CALLBACK (file_changed), nb);
|
||||||
|
@ -155,7 +159,7 @@ Signals are emitted on the object.
|
||||||
The type of the object is the second argument of `g_signal_newv`.
|
The type of the object is the second argument of `g_signal_newv`.
|
||||||
The relationship between the signal and object (type) is made up when the signal is generated.
|
The relationship between the signal and object (type) is made up when the signal is generated.
|
||||||
|
|
||||||
`g_signal_emit` is used to emit the signal.
|
A function `g_signal_emit` is used to emit the signal.
|
||||||
The following lines are extracted from `tfetextview.c`.
|
The following lines are extracted from `tfetextview.c`.
|
||||||
Each line is quoted from a different line.
|
Each line is quoted from a different line.
|
||||||
|
|
||||||
|
@ -170,7 +174,7 @@ g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_ER
|
||||||
- The second argument is the signal id.
|
- The second argument is the signal id.
|
||||||
- The third argument is the detail of the signal.
|
- The third argument is the detail of the signal.
|
||||||
"change-file" signal and "open-response" signal doesn't have details and the argument is zero when no details.
|
"change-file" signal and "open-response" signal doesn't have details and the argument is zero when no details.
|
||||||
- "change-file" signal doesn't have parameter, so no fourth parameter.
|
- "change-file" signal doesn't have parameter, so there's no fourth parameter.
|
||||||
- "open-response" signal has one parameter.
|
- "open-response" signal has one parameter.
|
||||||
The fourth parameter is the parameter.
|
The fourth parameter is the parameter.
|
||||||
|
|
||||||
|
|
381
gfm/sec12.md
381
gfm/sec12.md
|
@ -2,59 +2,66 @@ Up: [Readme.md](../Readme.md), Prev: [Section 11](sec11.md), Next: [Section 13]
|
||||||
|
|
||||||
# Functions in TfeTextView
|
# Functions in TfeTextView
|
||||||
|
|
||||||
In this section I will explain each function in TfeTextView object.
|
In this section I will explain functions in TfeTextView object.
|
||||||
|
|
||||||
### tfe.h and tfetextview.h
|
### tfe.h and tfetextview.h
|
||||||
|
|
||||||
`tfe.h` is a top header file and it includes `gtk.h` and all the header files.
|
`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.
|
C source files `tfeapplication.c` and `tfenotebook.c` include `tfe.h` at the beginning.
|
||||||
|
|
||||||
~~~C
|
~~~C
|
||||||
1 #include <gtk/gtk.h>
|
1 #include <gtk/gtk.h>
|
||||||
2
|
2
|
||||||
3 #include "tfetextview.h"
|
3 #include "../tfetextview/tfetextview.h"
|
||||||
4 #include "tfenotebook.h"
|
4 #include "tfenotebook.h"
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
`tfetextview.h` is a header file which describes the public functions in `tfetextview.c`.
|
`../tfetextview/tfetextview.h` is a header file which describes the public functions in `tfetextview.c`.
|
||||||
|
|
||||||
~~~C
|
~~~C
|
||||||
1 #define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type ()
|
1 #ifndef __TFE_TEXT_VIEW_H__
|
||||||
2 G_DECLARE_FINAL_TYPE (TfeTextView, tfe_text_view, TFE, TEXT_VIEW, GtkTextView)
|
2 #define __TFE_TEXT_VIEW_H__
|
||||||
3
|
3
|
||||||
4 /* "open-response" signal response */
|
4 #include <gtk/gtk.h>
|
||||||
5 enum
|
5
|
||||||
6 {
|
6 #define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type ()
|
||||||
7 TFE_OPEN_RESPONSE_SUCCESS,
|
7 G_DECLARE_FINAL_TYPE (TfeTextView, tfe_text_view, TFE, TEXT_VIEW, GtkTextView)
|
||||||
8 TFE_OPEN_RESPONSE_CANCEL,
|
8
|
||||||
9 TFE_OPEN_RESPONSE_ERROR
|
9 /* "open-response" signal response */
|
||||||
10 };
|
10 enum TfeTextViewOpenResponseType
|
||||||
11
|
11 {
|
||||||
12 GFile *
|
12 TFE_OPEN_RESPONSE_SUCCESS,
|
||||||
13 tfe_text_view_get_file (TfeTextView *tv);
|
13 TFE_OPEN_RESPONSE_CANCEL,
|
||||||
14
|
14 TFE_OPEN_RESPONSE_ERROR
|
||||||
15 void
|
15 };
|
||||||
16 tfe_text_view_open (TfeTextView *tv, GtkWidget *win);
|
16
|
||||||
17
|
17 GFile *
|
||||||
18 void
|
18 tfe_text_view_get_file (TfeTextView *tv);
|
||||||
19 tfe_text_view_save (TfeTextView *tv);
|
19
|
||||||
20
|
20 void
|
||||||
21 void
|
21 tfe_text_view_open (TfeTextView *tv, GtkWidget *win);
|
||||||
22 tfe_text_view_saveas (TfeTextView *tv);
|
22
|
||||||
23
|
23 void
|
||||||
24 GtkWidget *
|
24 tfe_text_view_save (TfeTextView *tv);
|
||||||
25 tfe_text_view_new_with_file (GFile *file);
|
25
|
||||||
26
|
26 void
|
||||||
27 GtkWidget *
|
27 tfe_text_view_saveas (TfeTextView *tv);
|
||||||
28 tfe_text_view_new (void);
|
28
|
||||||
29
|
29 GtkWidget *
|
||||||
|
30 tfe_text_view_new_with_file (GFile *file);
|
||||||
|
31
|
||||||
|
32 GtkWidget *
|
||||||
|
33 tfe_text_view_new (void);
|
||||||
|
34
|
||||||
|
35 #endif /* __TFE_TEXT_VIEW_H__ */
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
- 1-2: These two lines are used to define TfeTextView.
|
- 1,2,35: Thanks to these three lines, the following lines are included only once.
|
||||||
- 4-10: Definitions of parameter used in the handler of "open-response" signal.
|
- 4: Includes gtk4 header files.
|
||||||
- 12-28: Public functions on GtkTextView.
|
The header file `gtk4` also has the same mechanism to avoid including it multiple times.
|
||||||
|
- 6-7: These two lines define TfeTextView.
|
||||||
Each function will be explained later in this section.
|
- 9-15: A definition of the value of the parameter of "open-response" signal.
|
||||||
|
- 17-33: Declaration of public functions on GtkTextView.
|
||||||
|
|
||||||
## Functions to generate TfeTextView object
|
## Functions to generate TfeTextView object
|
||||||
|
|
||||||
|
@ -71,14 +78,6 @@ 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.
|
`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 occurs during the generation process, NULL is returned.
|
If an error occurs during the generation process, NULL is returned.
|
||||||
|
|
||||||
Each function is defined as follows.
|
Each function is defined as follows.
|
||||||
|
@ -110,10 +109,10 @@ Each function is defined as follows.
|
||||||
24 }
|
24 }
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
- 21-24: `tfe_text_view_new`.
|
- 21-24: `tfe_text_view_new` function.
|
||||||
Just returns the value from the function `g_object_new` but casted to the pointer to GtkWidget.
|
Just returns the value from the function `g_object_new` but casts it to the pointer to GtkWidget.
|
||||||
Initialization is done in `tfe_text_view_init` which is called in the process of `gtk_widget_new` function.
|
Initialization is done in `tfe_text_view_init` which is called in the process of `g_object_new` function.
|
||||||
- 1-19: `tfe_text_view_new_with_file`
|
- 1-19: `tfe_text_view_new_with_file` function.
|
||||||
- 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).
|
- 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.
|
It tests whether the argument `file` is a pointer to GFile.
|
||||||
If it's true, then the program goes on to the next line.
|
If it's true, then the program goes on to the next line.
|
||||||
|
@ -124,12 +123,14 @@ If an error occurs, the solution is usually to change the (caller) program and f
|
||||||
You need to distinguish programmer's errors and runtime errors.
|
You need to distinguish programmer's errors and runtime errors.
|
||||||
You shouldn't use this function to find runtime errors.
|
You shouldn't use this function to find runtime errors.
|
||||||
- 10-11: If an error occurs when reading the file, then return NULL.
|
- 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`.
|
- 13: Calls the function `tfe_text_view_new`.
|
||||||
The pointer to GtkTextBuffer is set to `tb`
|
The function generates TfeTextView instance and returns the pointer to the instance.
|
||||||
Set the contents read from the file to GtkTextBuffer `tb`.
|
- 14: Gets the pointer to GtkTextBuffer corresponds to `tv`.
|
||||||
Free the memories pointed by `contents`.
|
The pointer is assigned to `tb`
|
||||||
Duplicate `file` and set it to `tv->file`.
|
- 15: Assigns the contents read from the file to GtkTextBuffer pointed by `tb`.
|
||||||
Return `tv`.
|
- 16: Frees the memories pointed by `contents`.
|
||||||
|
- 17: Duplicates `file` and sets `tv->file` to point it.
|
||||||
|
- 18: Returns `tv`, which is a pointer to the newly created TfeTextView instance..
|
||||||
|
|
||||||
## Save and saveas functions
|
## Save and saveas functions
|
||||||
|
|
||||||
|
@ -139,17 +140,18 @@ Save and saveas functions write the contents in GtkTextBuffer to a file.
|
||||||
void tfe_text_view_save (TfeTextView *tv)
|
void tfe_text_view_save (TfeTextView *tv)
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
`save` function writes the contents in GtkTextBuffer to a file specified by `tv->file`.
|
The function `save` 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`.
|
If `tv->file` is NULL, then it shows GtkFileChooserDialog and prompts the user to choose a file to save.
|
||||||
|
Then it saves the contents to the file and sets `tv->file` to point the GFile instance of the file.
|
||||||
|
|
||||||
~~~C
|
~~~C
|
||||||
void tfe_text_view_saveas (TfeTextView *tv)
|
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.
|
The function `saveas` uses GtkFileChooserDialog and prompts the user to select a existed file or specify a new file to save.
|
||||||
|
Then, the function changes `tv->file` and save the contents to the specified file.
|
||||||
If an error occurs, it is shown to the user through the message dialog.
|
If an error occurs, 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.
|
The error is managed only in the TfeTextView instance and no information is notified to the caller.
|
||||||
|
|
||||||
~~~C
|
~~~C
|
||||||
1 static void
|
1 static void
|
||||||
|
@ -160,121 +162,131 @@ The error is managed only in the object and no information is notified to the ca
|
||||||
6 if (response == GTK_RESPONSE_ACCEPT) {
|
6 if (response == GTK_RESPONSE_ACCEPT) {
|
||||||
7 file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
|
7 file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
|
||||||
8 if (G_IS_FILE(file)) {
|
8 if (G_IS_FILE(file)) {
|
||||||
9 tv->file = file;
|
9 if (G_IS_FILE (tv->file))
|
||||||
10 gtk_text_buffer_set_modified (tb, TRUE);
|
10 g_object_unref (tv->file);
|
||||||
11 g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
|
11 tv->file = file;
|
||||||
12 tfe_text_view_save (TFE_TEXT_VIEW (tv));
|
12 gtk_text_buffer_set_modified (tb, TRUE);
|
||||||
13 }
|
13 g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
|
||||||
14 }
|
14 tfe_text_view_save (TFE_TEXT_VIEW (tv));
|
||||||
15 gtk_window_destroy (GTK_WINDOW (dialog));
|
15 }
|
||||||
16 }
|
16 }
|
||||||
17
|
17 gtk_window_destroy (GTK_WINDOW (dialog));
|
||||||
18 void
|
18 }
|
||||||
19 tfe_text_view_save (TfeTextView *tv) {
|
19
|
||||||
20 g_return_if_fail (TFE_IS_TEXT_VIEW (tv));
|
20 void
|
||||||
21
|
21 tfe_text_view_save (TfeTextView *tv) {
|
||||||
22 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
22 g_return_if_fail (TFE_IS_TEXT_VIEW (tv));
|
||||||
23 GtkTextIter start_iter;
|
23
|
||||||
24 GtkTextIter end_iter;
|
24 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
||||||
25 gchar *contents;
|
25 GtkTextIter start_iter;
|
||||||
26 GtkWidget *message_dialog;
|
26 GtkTextIter end_iter;
|
||||||
27 GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW);
|
27 gchar *contents;
|
||||||
28 GError *err = NULL;
|
28 GtkWidget *message_dialog;
|
||||||
29
|
29 GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW);
|
||||||
30 if (! gtk_text_buffer_get_modified (tb))
|
30 GError *err = NULL;
|
||||||
31 return; /* no necessary to save it */
|
31
|
||||||
32 else if (tv->file == NULL)
|
32 if (! gtk_text_buffer_get_modified (tb))
|
||||||
33 tfe_text_view_saveas (tv);
|
33 return; /* no need to save it */
|
||||||
34 else {
|
34 else if (tv->file == NULL)
|
||||||
35 gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter);
|
35 tfe_text_view_saveas (tv);
|
||||||
36 contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE);
|
36 else {
|
||||||
37 if (g_file_replace_contents (tv->file, contents, strlen (contents), NULL, TRUE, G_FILE_CREATE_NONE, NULL, NULL, &err))
|
37 gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter);
|
||||||
38 gtk_text_buffer_set_modified (tb, FALSE);
|
38 contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE);
|
||||||
39 else {
|
39 if (g_file_replace_contents (tv->file, contents, strlen (contents), NULL, TRUE, G_FILE_CREATE_NONE, NULL, NULL, &err))
|
||||||
40 /* It is possible that tv->file is broken. */
|
40 gtk_text_buffer_set_modified (tb, FALSE);
|
||||||
41 /* It is a good idea to set tv->file to NULL. */
|
41 else {
|
||||||
42 if (G_IS_FILE (tv->file))
|
42 /* It is possible that tv->file is broken or you don't have permission to write. */
|
||||||
43 g_object_unref (tv->file);
|
43 /* It is a good idea to set tv->file to NULL. */
|
||||||
44 tv->file =NULL;
|
44 if (G_IS_FILE (tv->file))
|
||||||
45 g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
|
45 g_object_unref (tv->file);
|
||||||
46 gtk_text_buffer_set_modified (tb, TRUE);
|
46 tv->file =NULL;
|
||||||
47 message_dialog = gtk_message_dialog_new (GTK_WINDOW (win), GTK_DIALOG_MODAL,
|
47 g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
|
||||||
48 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
|
48 message_dialog = gtk_message_dialog_new (GTK_WINDOW (win), GTK_DIALOG_MODAL,
|
||||||
49 "%s.\n", err->message);
|
49 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
|
||||||
50 g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
|
50 "%s.\n", err->message);
|
||||||
51 gtk_widget_show (message_dialog);
|
51 g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
|
||||||
52 g_error_free (err);
|
52 gtk_widget_show (message_dialog);
|
||||||
53 }
|
53 g_error_free (err);
|
||||||
54 }
|
54 }
|
||||||
55 }
|
55 }
|
||||||
56
|
56 }
|
||||||
57 void
|
57
|
||||||
58 tfe_text_view_saveas (TfeTextView *tv) {
|
58 void
|
||||||
59 g_return_if_fail (TFE_IS_TEXT_VIEW (tv));
|
59 tfe_text_view_saveas (TfeTextView *tv) {
|
||||||
60
|
60 g_return_if_fail (TFE_IS_TEXT_VIEW (tv));
|
||||||
61 GtkWidget *dialog;
|
61
|
||||||
62 GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW);
|
62 GtkWidget *dialog;
|
||||||
63
|
63 GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW);
|
||||||
64 dialog = gtk_file_chooser_dialog_new ("Save file", GTK_WINDOW (win), GTK_FILE_CHOOSER_ACTION_SAVE,
|
64
|
||||||
65 "_Cancel", GTK_RESPONSE_CANCEL,
|
65 dialog = gtk_file_chooser_dialog_new ("Save file", GTK_WINDOW (win), GTK_FILE_CHOOSER_ACTION_SAVE,
|
||||||
66 "_Save", GTK_RESPONSE_ACCEPT,
|
66 "Cancel", GTK_RESPONSE_CANCEL,
|
||||||
67 NULL);
|
67 "Save", GTK_RESPONSE_ACCEPT,
|
||||||
68 g_signal_connect (dialog, "response", G_CALLBACK (saveas_dialog_response), tv);
|
68 NULL);
|
||||||
69 gtk_widget_show (dialog);
|
69 g_signal_connect (dialog, "response", G_CALLBACK (saveas_dialog_response), tv);
|
||||||
70 }
|
70 gtk_widget_show (dialog);
|
||||||
|
71 }
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
- 18-55: `Tfe_text_view_save` function.
|
- 20-56: `Tfe_text_view_save` function.
|
||||||
- 20: If `tv` is not a pointer to TfeTextView, then it logs an error message and immediately returns.
|
- 22: If `tv` is not a pointer to TfeTextView, then it logs an error message and immediately returns.
|
||||||
This function is similar to `g_return_val_if_fail` function, but no value is returned because `tfe_text_view_save` doesn't return a value.
|
This function is similar to `g_return_val_if_fail` function, but no value is returned because `tfe_text_view_save` doesn't return a value.
|
||||||
- 30-31: If the buffer hasn't modified, then it doesn't need to save it.
|
- 32-33: If the buffer hasn't modified, then it doesn't need to save it.
|
||||||
So the function returns.
|
So the function returns.
|
||||||
- 32-33: If `tv->file` is NULL, no file has given yet.
|
- 34-35: If `tv->file` is NULL, no file has given yet.
|
||||||
It calls `tfe_text_view_saveas`, which lets the user to choose a file to save.
|
It calls `tfe_text_view_saveas` which prompts a user to select a file or specify a new file to save.
|
||||||
- 35-36: Get the contents of the GtkTextBuffer and set its pointer to `contents`.
|
- 37-38: Gets the contents of the GtkTextBuffer and sets `contents` to point it.
|
||||||
- 37-38: Save the content to the file.
|
- 39-40: Saves the content to the file.
|
||||||
If it succeeds, reset the modified bit in the GtkTextBuffer.
|
If it succeeds, it resets the modified bit in the GtkTextBuffer.
|
||||||
- 39-53: If file writing fails, it assigns NULL to `tv->file`.
|
- 42-53: If file writing fails, it assigns NULL to `tv->file`.
|
||||||
Emits "change-file" signal.
|
Emits "change-file" signal.
|
||||||
Shows the error message dialog (47-51).
|
Shows the error message dialog (48-52).
|
||||||
Because the handler is `gtk_window_destroy`, the dialog disappears when user clicks on the button in the dialog.
|
Because the handler is `gtk_window_destroy`, the dialog disappears when user clicks on the button in the dialog.
|
||||||
- 57-70: `tfe_text_view_saveas` function.
|
Frees the GError object.
|
||||||
It shows GtkFileChooserDialog and lets the user choose a file and give it to the signal handler.
|
- 58-71: `tfe_text_view_saveas` function.
|
||||||
- 64-67: Generate GtkFileChooserDialog.
|
It shows GtkFileChooserDialog and prompts the user to choose a file.
|
||||||
|
- 65-68: Generates GtkFileChooserDialog.
|
||||||
The title is "Save file".
|
The title is "Save file".
|
||||||
Transient parent of the dialog is `win`, which is the top level window.
|
Transient parent of the dialog is `win`, which is the top level window.
|
||||||
The action is save mode.
|
The action is save mode.
|
||||||
The buttons are Cancel and Save.
|
The buttons are Cancel and Save.
|
||||||
- 68: connect the "response" signal of the dialog and `saveas_dialog_response` handler.
|
- 69: connects the "response" signal of the dialog and `saveas_dialog_response` handler.
|
||||||
- 1-16: `saveas_dialog_response` signal handler.
|
- 1-18: `saveas_dialog_response` signal handler.
|
||||||
- 6-14: 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 to `tv->file`, turn on the modified bit of the GtkTextBuffer, emits "change-file" signal then call `tfe_text_view_save` to save the buffer to the file.
|
- 6-16: If the response is `GTK_RESPONSE_ACCEPT`, then it gets a pointer to the GFile object.
|
||||||
|
Then, it sets `tv->file` to point the GFile.
|
||||||
|
And turns on the modified bit of the GtkTextBuffer, emits "change-file" signal.
|
||||||
|
Finally, it calls `tfe_text_view_save` to save the buffer to the file.
|
||||||
|
|
||||||
![Saveas process](../image/saveas.png)
|
![Saveas process](../image/saveas.png)
|
||||||
|
|
||||||
When you use GtkFileChooserDialog, you need to divide the program into two parts.
|
When you use GtkFileChooserDialog, you need to divide the program into two parts.
|
||||||
They are a function which generates GtkFileChooserDialog and the signal handler.
|
One is are a function which generates GtkFileChooserDialog and the other is a signal handler.
|
||||||
The function just generates and shows the dialog.
|
The function just generates and shows GtkFileChooserDialog.
|
||||||
The rest is done by the handler.
|
The rest is done by the handler.
|
||||||
It gets Gfile from GtkFileChooserDialog, save the buffer to the file by calling `tfe_text_view_save`.
|
It gets Gfile from GtkFileChooserDialog and saves the buffer to the file by calling `tfe_text_view_save`.
|
||||||
|
|
||||||
## Open function
|
## Open function
|
||||||
|
|
||||||
Open function shows GtkFileChooserDialog to the user and let them choose a file.
|
Open function shows GtkFileChooserDialog to users and prompts them to choose a file.
|
||||||
Then read the file and set it to GtkTextBuffer.
|
Then it reads the file and puts the text to GtkTextBuffer.
|
||||||
|
|
||||||
~~~C
|
~~~C
|
||||||
void tfe_text_view_open (TfeTextView *tv, GtkWidget *win);
|
void tfe_text_view_open (TfeTextView *tv, GtkWidget *win);
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
TfeTextView object `tv` has to be generated in advance.
|
The parameter `win` is the top window.
|
||||||
This function is usually called just after `tv` has been generated.
|
It will be a transient parent window of GtkFileChooserDialog when the dialog is generated..
|
||||||
And its buffer is empty, `tv->file` is NULL and `tv` has not set to the widget hierarchy.
|
This allows window managers to keep the dialog on top of the parent window, or center the dialog over the parent window.
|
||||||
Even if the buffer is not empty, `tfe_text_view_open` doesn't treat it as an error.
|
It is possible to give no parent window to the dialog.
|
||||||
If you want to revert the buffer, calling this function is apropreate.
|
However, it is encouraged to give a parent window to dialog.
|
||||||
Otherwise probably bad things will happen.
|
This function might be called just after `tv` has been generated.
|
||||||
|
In that case, `tv` has not been incorporated into the widget hierarchy.
|
||||||
|
Therefore it is impossible to get the top window from `tv`.
|
||||||
|
That's why the function needs `win` parameter.
|
||||||
|
|
||||||
GtkWidget `win` is expected to be the top level window of the application.
|
This function is usually called when the buffer of `tv` is empty.
|
||||||
It will be used as a transient parent window for the argument to the function `gtk_file_chooser_dialog_new`.
|
However, even if the buffer 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 appropriate.
|
||||||
|
Otherwise probably bad things will happen.
|
||||||
|
|
||||||
~~~C
|
~~~C
|
||||||
1 static void
|
1 static void
|
||||||
|
@ -308,42 +320,44 @@ It will be used as a transient parent window for the argument to the function `g
|
||||||
29 tv->file = file;
|
29 tv->file = file;
|
||||||
30 gtk_text_buffer_set_modified (tb, FALSE);
|
30 gtk_text_buffer_set_modified (tb, FALSE);
|
||||||
31 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_SUCCESS);
|
31 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_SUCCESS);
|
||||||
32 }
|
32 g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
|
||||||
33 gtk_window_destroy (GTK_WINDOW (dialog));
|
33 }
|
||||||
34 }
|
34 gtk_window_destroy (GTK_WINDOW (dialog));
|
||||||
35
|
35 }
|
||||||
36 void
|
36
|
||||||
37 tfe_text_view_open (TfeTextView *tv, GtkWidget *win) {
|
37 void
|
||||||
38 g_return_if_fail (TFE_IS_TEXT_VIEW (tv));
|
38 tfe_text_view_open (TfeTextView *tv, GtkWidget *win) {
|
||||||
39 g_return_if_fail (GTK_IS_WINDOW (win));
|
39 g_return_if_fail (TFE_IS_TEXT_VIEW (tv));
|
||||||
40
|
40 g_return_if_fail (GTK_IS_WINDOW (win));
|
||||||
41 GtkWidget *dialog;
|
41
|
||||||
42
|
42 GtkWidget *dialog;
|
||||||
43 dialog = gtk_file_chooser_dialog_new ("Open file", GTK_WINDOW (win), GTK_FILE_CHOOSER_ACTION_OPEN,
|
43
|
||||||
44 "Cancel", GTK_RESPONSE_CANCEL,
|
44 dialog = gtk_file_chooser_dialog_new ("Open file", GTK_WINDOW (win), GTK_FILE_CHOOSER_ACTION_OPEN,
|
||||||
45 "Open", GTK_RESPONSE_ACCEPT,
|
45 "Cancel", GTK_RESPONSE_CANCEL,
|
||||||
46 NULL);
|
46 "Open", GTK_RESPONSE_ACCEPT,
|
||||||
47 g_signal_connect (dialog, "response", G_CALLBACK (open_dialog_response), tv);
|
47 NULL);
|
||||||
48 gtk_widget_show (dialog);
|
48 g_signal_connect (dialog, "response", G_CALLBACK (open_dialog_response), tv);
|
||||||
49 }
|
49 gtk_widget_show (dialog);
|
||||||
|
50 }
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
- 36-49: `tfe_text_view_open` function.
|
- 37-50: `tfe_text_view_open` function.
|
||||||
- 43: Generate GtkFileChooserDialog.
|
- 44-47: Generates GtkFileChooserDialog.
|
||||||
The title is "Open file".
|
The title is "Open file".
|
||||||
Ttransient parent window is the top window of the application, which is given by the caller.
|
Transient parent window is the top window of the application, which is given by the caller.
|
||||||
The action is open mode.
|
The action is open mode.
|
||||||
The buttons are Cancel and Open.
|
The buttons are Cancel and Open.
|
||||||
- 47: connect the "reponse" signal of the dialog and `open_dialog_response` signal handler.
|
- 48: connects the "response" signal of the dialog and `open_dialog_response` signal handler.
|
||||||
- 48: Show the dialog.
|
- 49: Shows the dialog.
|
||||||
- 1-34: `open_dialog_response` signal handler.
|
- 1-35: `open_dialog_response` signal handler.
|
||||||
- 10-11: 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 parameter `TFE_OPEN_RESPONSE_CANCEL`.
|
- 10-11: 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 parameter `TFE_OPEN_RESPONSE_CANCEL`.
|
||||||
- 12-13: Get a pointer to Gfile by `gtk_file_chooser_get_file`.
|
- 12-13: Gets a pointer to Gfile by `gtk_file_chooser_get_file`.
|
||||||
If it is not GFile, maybe an error occured.
|
If it is not GFile, maybe an error occurred.
|
||||||
Then it emits "open-response" signal with the parameter `TFE_OPEN_RESPONSE_ERROR`.
|
Then it emits "open-response" signal with the parameter `TFE_OPEN_RESPONSE_ERROR`.
|
||||||
- 14-23: If an error occurs when it 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`.
|
- 14-23: If an error occurs at file reading, then it decreases the reference count of the 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-32: If the file has successfully read, then the text is set to GtkTextBuffer, free the temporary buffer pointed by `contents`, set file to `tv->file` (no duplication or unref is not necessary) and emits "open-response" signal with the parameter `TFE_OPEN_RESPONSE_SUCCESS`.
|
- 24-33: If the file has successfully read, then the text is inserted to GtkTextBuffer, frees the temporary buffer pointed by `contents` and sets `tv->file` to point the file (no duplication or unref is not necessary).
|
||||||
- 33: close GtkFileCooserDialog.
|
Then, it emits "open-response" signal with the parameter `TFE_OPEN_RESPONSE_SUCCESS` and emits "change-file" signal.
|
||||||
|
- 34: closes GtkFileCooserDialog.
|
||||||
|
|
||||||
Now let's think about the whole process between the other object (caller) and TfeTextView.
|
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.
|
It is shown in the following diagram and you would think that it is really complicated.
|
||||||
|
@ -354,12 +368,12 @@ However, in Gtk4, `gtk_dialog_run`is unavailable any more.
|
||||||
|
|
||||||
![Caller and TfeTextView](../image/open.png)
|
![Caller and TfeTextView](../image/open.png)
|
||||||
|
|
||||||
1. A caller get a pointer `tv` to TfeTextView by calling `tfe_text_view_new`.
|
1. A caller gets a pointer `tv` to TfeTextView by calling `tfe_text_view_new`.
|
||||||
2. The caller connects the handler (left bottom in the diagram) and the signal "open-response".
|
2. The caller connects the handler (left bottom in the diagram) and the signal "open-response".
|
||||||
3. It calls `tfe_text_view_open` to let the user select a file from GtkFileChooserDialog.
|
3. It calls `tfe_text_view_open` to prompt the user to select a file from GtkFileChooserDialog.
|
||||||
4. The dialog emits a signal and it invokes the handler `open_dialog_response`.
|
4. The dialog emits a signal and it invokes the handler `open_dialog_response`.
|
||||||
5. The handler read the file and set it into GtkTextBuffer and emits a signal to inform the response status.
|
5. The handler reads the file and inserts the text into GtkTextBuffer and emits a signal to inform the response status.
|
||||||
6. The handler outside TfeTextView recieves the signal.
|
6. The handler outside TfeTextView receives the signal.
|
||||||
|
|
||||||
## Get file function
|
## Get file function
|
||||||
|
|
||||||
|
@ -374,12 +388,13 @@ However, in Gtk4, `gtk_dialog_run`is unavailable any more.
|
||||||
6 }
|
6 }
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
The important thing is duplicate `tv->file`.
|
The important thing is to duplicate `tv->file`.
|
||||||
Otherwise, if the caller free the GFile object, `tv->file` is no more guaranteed to point the GFile.
|
Otherwise, if the caller frees the GFile object, `tv->file` is no more guaranteed to point the GFile.
|
||||||
|
|
||||||
## Source file of tfetextview.c
|
## Source file of tfetextview.c
|
||||||
|
|
||||||
All the source files are listed in [Section 15](sec15.md).
|
All the source files are listed in [Section 15](sec15.md).
|
||||||
|
You can find them under [src/tfe5](../src/tfe5) and [src/tfetextview](../tfetextview) directories.
|
||||||
|
|
||||||
|
|
||||||
Up: [Readme.md](../Readme.md), Prev: [Section 11](sec11.md), Next: [Section 13](sec13.md)
|
Up: [Readme.md](../Readme.md), Prev: [Section 11](sec11.md), Next: [Section 13](sec13.md)
|
||||||
|
|
136
gfm/sec13.md
136
gfm/sec13.md
|
@ -1,10 +1,12 @@
|
||||||
Up: [Readme.md](../Readme.md), Prev: [Section 12](sec12.md), Next: [Section 14](sec14.md)
|
Up: [Readme.md](../Readme.md), Prev: [Section 12](sec12.md), Next: [Section 14](sec14.md)
|
||||||
|
|
||||||
# Functions with GtkNotebook
|
# Functions in GtkNotebook
|
||||||
|
|
||||||
GtkNotebook is a very important object in the text file editor `tfe`.
|
GtkNotebook is a very important object in the text file editor `tfe`.
|
||||||
It connects the application and TfeTextView objects.
|
It connects the application and TfeTextView objects.
|
||||||
`tfenotebook.h` and `tfenotebook.c` have a set of functions related to GtkTextbook.
|
A set of functions related to GtkNotebook are declared in `tfenotebook.h`.
|
||||||
|
The word "tfenotebook" is used only in filenames.
|
||||||
|
There's no "TfeNotebook" object.
|
||||||
|
|
||||||
~~~C
|
~~~C
|
||||||
1 void
|
1 void
|
||||||
|
@ -21,13 +23,13 @@ It connects the application and TfeTextView objects.
|
||||||
12
|
12
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
This header file shows the public functions in `tfenotebook.c`.
|
This header file describes the public functions in `tfenotebook.c`.
|
||||||
|
|
||||||
- 10-11: `notebook_page_new` generates a new GtkNotebookPage and adds GtkScrolledWindow and TfeTextView on the page.
|
- 10-11: The function `notebook_page_new` generates a new GtkNotebookPage and adds GtkScrolledWindow and TfeTextView to the page.
|
||||||
- 7-8: `notebook_page_new_with_file` generates a new GtkNotebookPage and adds GtkScrolledWindow and TfeTextView on the page. The file is read and set into GtkTextBuffer.
|
- 7-8: The function `notebook_page_new_with_file` generates a new GtkNotebookPage and adds GtkScrolledWindow and TfeTextView to the page. A file is read and inserted into GtkTextBuffer.
|
||||||
The GFile `file` is copied and set in the TfeTextView object.
|
The GFile `file` is copied and inserted to the TfeTextView object.
|
||||||
- 4-5: `notebook_page_open` shows a file chooser dialog. Then, user chooses a file and the file is set into GtkTextBuffer.
|
- 4-5: `notebook_page_open` shows a file chooser dialog. Then, user chooses a file and the file is inserted into GtkTextBuffer.
|
||||||
- 1-2: `notebook_page_save` saves the contents in GtkTextBuffer into the file, which has been set in the TfeTextView.
|
- 1-2: `notebook_page_save` saves the contents in GtkTextBuffer into the file, which is got from the TfeTextView.
|
||||||
|
|
||||||
You probably find that the functions above are higher level functions of
|
You probably find that the functions above are higher level functions of
|
||||||
|
|
||||||
|
@ -36,7 +38,7 @@ You probably find that the functions above are higher level functions of
|
||||||
- `tef_text_view_open`
|
- `tef_text_view_open`
|
||||||
- `tfe_text_view_save`
|
- `tfe_text_view_save`
|
||||||
|
|
||||||
respectively.
|
respectively.
|
||||||
|
|
||||||
There are two layers.
|
There are two layers.
|
||||||
One of them is `tfe_text_view ...`, which is the lower level layer.
|
One of them is `tfe_text_view ...`, which is the lower level layer.
|
||||||
|
@ -64,45 +66,51 @@ Now let's look at each program of the functions.
|
||||||
15 gint i;
|
15 gint i;
|
||||||
16 scr = gtk_scrolled_window_new ();
|
16 scr = gtk_scrolled_window_new ();
|
||||||
17
|
17
|
||||||
18 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);
|
18 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR);
|
||||||
19 lab = gtk_label_new (filename);
|
19 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);
|
||||||
20 i = gtk_notebook_append_page (nb, scr, lab);
|
20 lab = gtk_label_new (filename);
|
||||||
21 nbp = gtk_notebook_get_page (nb, scr);
|
21 i = gtk_notebook_append_page (nb, scr, lab);
|
||||||
22 g_object_set (nbp, "tab-expand", TRUE, NULL);
|
22 nbp = gtk_notebook_get_page (nb, scr);
|
||||||
23 gtk_notebook_set_current_page (nb, i);
|
23 g_object_set (nbp, "tab-expand", TRUE, NULL);
|
||||||
24 g_signal_connect (GTK_TEXT_VIEW (tv), "change-file", G_CALLBACK (file_changed), nb);
|
24 gtk_notebook_set_current_page (nb, i);
|
||||||
25 }
|
25 g_signal_connect (GTK_TEXT_VIEW (tv), "change-file", G_CALLBACK (file_changed), nb);
|
||||||
26
|
26 }
|
||||||
27 void
|
27
|
||||||
28 notebook_page_new (GtkNotebook *nb) {
|
28 void
|
||||||
29 g_return_if_fail(GTK_IS_NOTEBOOK (nb));
|
29 notebook_page_new (GtkNotebook *nb) {
|
||||||
30
|
30 g_return_if_fail(GTK_IS_NOTEBOOK (nb));
|
||||||
31 GtkWidget *tv;
|
31
|
||||||
32 char *filename;
|
32 GtkWidget *tv;
|
||||||
33
|
33 char *filename;
|
||||||
34 tv = tfe_text_view_new ();
|
34
|
||||||
35 filename = get_untitled ();
|
35 tv = tfe_text_view_new ();
|
||||||
36 notebook_page_build (nb, tv, filename);
|
36 filename = get_untitled ();
|
||||||
37 }
|
37 notebook_page_build (nb, tv, filename);
|
||||||
|
38 }
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
- 27-37: `notebook_page_new` function.
|
- 28-38: `notebook_page_new` function.
|
||||||
- 29: `g_return_if_fail` is used to check the argument.
|
- 30: `g_return_if_fail` is used to check the argument.
|
||||||
- 34: Generate TfeTextView object.
|
- 35: Generates TfeTextView object.
|
||||||
- 35: Generate filename, which is "Untitled", "Untitled2", ... .
|
- 36: Generates filename, which is "Untitled", "Untitled2", ... .
|
||||||
- 1-8: `get_untitled` function.
|
- 1-8: `get_untitled` function.
|
||||||
- 3: Static variable `c` is initialized at the first call of this function. After that `c` keeps its value except it is changed explicitly.
|
- 3: Static variable `c` is initialized at the first call of this function. After that `c` keeps its value except it is changed explicitly.
|
||||||
- 4-7: Increase `c` by one and if it is zero then the name is "Untitled". If it is a positive integer then the name is "Untitled\<the integer\>", for example, "Untitled1", "Untitled2", and so on.
|
- 4-7: Increases `c` by one and if it is zero then it returns "Untitled". If it is a positive integer then the it returns "Untitled\<the integer\>", for example, "Untitled1", "Untitled2", and so on.
|
||||||
It returns the name.
|
The function `g_strdup_printf` generates a string and it should be freed by `g_free` when it becomes useless.
|
||||||
`g_strdup_printf` generates a string and it should be freed by `g_free` function.
|
The caller of `get_untitled` is in charge of freeing the string.
|
||||||
The caller of `get_untitled` is in charge of freeing the memories of the string.
|
- 37: calls `notebook_page_build` to build the contents of the page.
|
||||||
- 36: call `notebook_page_build` to build the contents of the page.
|
- 10- 26: `notebook_page_build` function.
|
||||||
- 10- 25: `notebook_page_build` function.
|
- 16: Generates GtkScrolledWindow.
|
||||||
- 17-18: Generate GtkScrolledWindow and set `tv` to its child.
|
- 18: Sets the wrap mode of `tv` to GTK_WRAP_WORD_CHAR so that lines are broken between words or graphemes.
|
||||||
- 19-20: Generate GtkLabel, then append it to GtkNotebookPage.
|
- 19: Inserts `tv` to GtkscrolledWindow as a child.
|
||||||
- 21-22: Set "tab-expand" property to TRUE.
|
- 20-21: Generates GtkLabel, then appends it to GtkNotebookPage.
|
||||||
- 23: Set the page to the current page.
|
- 22-23: Sets "tab-expand" property to TRUE.
|
||||||
- 24: Connect "change-file" signal and `file_changed` handler.
|
The function g\_object\_set sets properties on an object.
|
||||||
|
The object is any object derived from GObject.
|
||||||
|
In many cases, an object has its own function to set its properties, but sometimes not.
|
||||||
|
In that case, use g\_object\_set to set the property.
|
||||||
|
- 24: Sets the current page of `nb` to `i` which is the number of the GtkNotebookPage above.
|
||||||
|
- 25: Connects "change-file" signal and `file_changed` handler.
|
||||||
|
|
||||||
## notebook\_page\_new\_with\_file
|
## notebook\_page\_new\_with\_file
|
||||||
|
|
||||||
|
@ -122,9 +130,10 @@ The caller of `get_untitled` is in charge of freeing the memories of the string.
|
||||||
13 }
|
13 }
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
- 9-10: Call `tfe_text_view_new_with_file`.
|
- 9-10: Calls `tfe_text_view_new_with_file`.
|
||||||
If it returns NULL, then do nothing and return because of an error.
|
If the function returns NULL, then it does nothing and returns.
|
||||||
-11-13: Get the filename , build the contents of the page.
|
The return value NULL means that an error has happened.
|
||||||
|
- 11-12: Gets the filename and builds the contents of the page.
|
||||||
|
|
||||||
## notebook\_page\_open
|
## notebook\_page\_open
|
||||||
|
|
||||||
|
@ -160,20 +169,22 @@ If it returns NULL, then do nothing and return because of an error.
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
- 19-28: `notebook_page_open` function.
|
- 19-28: `notebook_page_open` function.
|
||||||
- 25: Generate TfeTextView object.
|
- 25: Generates TfeTextView object.
|
||||||
- 26: Connect the signal "open-response" and the handler `open_response`.
|
- 26: Connects the signal "open-response" and the handler `open_response`.
|
||||||
- 27: Call `tfe_text_view_open`.
|
- 27: Calls `tfe_text_view_open`.
|
||||||
It emits "open-response" signal to inform the status after the series of functions run.
|
The function emits an "open-response" signal to inform the status.
|
||||||
- 1-17: `open_response` handler.
|
- 1-17: `open_response` handler.
|
||||||
This is the post-function of `notebook_page_open`.
|
This is the post-function of `notebook_page_open`.
|
||||||
- 6-8: If the status is NOT `TFE_OPEN_RESPONSE_SUCCESS`, cancel what we did in `notebook_page_open`.
|
- 6-8: If the status is NOT `TFE_OPEN_RESPONSE_SUCCESS`, it cancels what it did in `notebook_page_open`.
|
||||||
The object `tv` hasn't been a child widget of some other widget yet.
|
The object `tv` hasn't been a child widget of some other widget yet.
|
||||||
Such object has floating reference.
|
Such object has floating reference.
|
||||||
It needs to do `g_object_ref_sink` and clear the floating reference before `g_object_unref`.
|
You need to call `g_object_ref_sink` first.
|
||||||
- 9-11: If `tfe_text_view_get_file` returns a pointer not to point GFile, then something bad happens. Cancel what we did.
|
Then the floating reference is converted into an ordinary reference.
|
||||||
|
Now you call `g_object_unref` to decrease the reference count by one.
|
||||||
|
- 9-11: If `tfe_text_view_get_file` returns a pointer not to point GFile, then something bad happens.
|
||||||
Sink and unref `tv`.
|
Sink and unref `tv`.
|
||||||
- 12-16: Otherwise, everything is okay.
|
- 12-16: Otherwise, everything is okay.
|
||||||
Get the filename, build the contents of the page.
|
Gets the filename, builds the contents of the page.
|
||||||
|
|
||||||
## notebook\_page\_save
|
## notebook\_page\_save
|
||||||
|
|
||||||
|
@ -197,7 +208,7 @@ Get the filename, build the contents of the page.
|
||||||
## file\_changed handler
|
## file\_changed handler
|
||||||
|
|
||||||
The function `file_changed` is a handler connected to "change-file" signal.
|
The function `file_changed` is a handler connected to "change-file" signal.
|
||||||
If `tv->file` is changed, TfeTextView emits this signal.
|
If the file in TfeTextView is changed, it emits this signal.
|
||||||
This handler changes the label of GtkNotebookPage.
|
This handler changes the label of GtkNotebookPage.
|
||||||
|
|
||||||
~~~C
|
~~~C
|
||||||
|
@ -221,13 +232,12 @@ This handler changes the label of GtkNotebookPage.
|
||||||
18 }
|
18 }
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
- 8: Get GFile from TfeTextView.
|
- 8: Gets GFile from TfeTextView.
|
||||||
- 9: Get GkScrolledWindow which is the parent widget of `tv`.
|
- 9: Gets GkScrolledWindow which is the parent widget of `tv`.
|
||||||
- 10-13: If `file` points GFile, then assign the filename of the GFile into `filename`.
|
- 10-13: If `file` points GFile, then assigns the filename of the GFile into `filename`.
|
||||||
Otherwise (file is NULL), assign untitled string to `filename`.
|
Otherwise (file is NULL), assigns untitled string to `filename`.
|
||||||
- 14-15: Generate a label with the filename and set it into GtkNotebookPage.
|
- 14-15: Generates a label with the filename and inserts it into GtkNotebookPage.
|
||||||
- 16-17: Free `filename` and unref `file`.
|
- 16-17: Unrefs `file` and frees `filename`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Up: [Readme.md](../Readme.md), Prev: [Section 12](sec12.md), Next: [Section 14](sec14.md)
|
Up: [Readme.md](../Readme.md), Prev: [Section 12](sec12.md), Next: [Section 14](sec14.md)
|
||||||
|
|
56
gfm/sec14.md
56
gfm/sec14.md
|
@ -33,19 +33,19 @@ It connects the command line given by the user and GTK application.
|
||||||
15 }
|
15 }
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
- 6: Generate GtkApplication object.
|
- 6: Generates GtkApplication object.
|
||||||
- 8-10: Connect "startup", "activate" and "open signals to their handlers.
|
- 8-10: Connects "startup", "activate" and "open signals to their handlers.
|
||||||
- 12: Run the application.
|
- 12: Runs the application.
|
||||||
- 13-14: release the reference to the application and return the status.
|
- 13-14: releases the reference to the application and returns the status.
|
||||||
|
|
||||||
## startup signal handler
|
## startup signal handler
|
||||||
|
|
||||||
Startup signal is emitted just after the application is generated.
|
Startup signal is emitted just after the application is generated.
|
||||||
What the signal handler needs to do is initialization of the application.
|
What the signal handler needs to do is initialization of the application.
|
||||||
|
|
||||||
- Build the widgets using ui file.
|
- Builds the widgets using ui file.
|
||||||
- Connect button signals and their handlers.
|
- Connects button signals and their handlers.
|
||||||
- Set CSS.
|
- Sets CSS.
|
||||||
|
|
||||||
The handler is as follows.
|
The handler is as follows.
|
||||||
|
|
||||||
|
@ -84,11 +84,11 @@ The handler is as follows.
|
||||||
32 }
|
32 }
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
- 12-15: Build widgets using ui file (resource).
|
- 12-15: Builds widgets using ui file (resource).
|
||||||
Connect the top window and the application using `gtk_window_set_application`.
|
Connects the top window and the application using `gtk_window_set_application`.
|
||||||
- 16-23: Get buttons and connect their signals and handlers.
|
- 16-23: Gets buttons and connects their signals and handlers.
|
||||||
- 24: Release the reference to GtkBuilder.
|
- 24: Releases the reference to GtkBuilder.
|
||||||
- 26-31: Set CSS.
|
- 26-31: Sets CSS.
|
||||||
CSS in GTK is similar to CSS in HTML.
|
CSS in GTK is similar to CSS in HTML.
|
||||||
You can set margin, border, padding, color, font and so on with CSS.
|
You can set margin, border, padding, color, font and so on with CSS.
|
||||||
In this program CSS is in line 30.
|
In this program CSS is in line 30.
|
||||||
|
@ -100,12 +100,12 @@ CSS will be explained in the next subsection.
|
||||||
|
|
||||||
CSS is an abbreviation of Cascading Style Sheet.
|
CSS is an abbreviation of Cascading Style Sheet.
|
||||||
It is originally used with HTML to describe the presentation semantics of a document.
|
It is originally used with HTML to describe the presentation semantics of a document.
|
||||||
You might have found that the widgets in GTK is simialr to the window in a browser.
|
You might have found that the widgets in GTK is similar to the window in a browser.
|
||||||
It implies that CSS can also be apllied to GTK windowing system.
|
It implies that CSS can also be applied to GTK windowing system.
|
||||||
|
|
||||||
### CSS nodes, selectors
|
### CSS nodes, selectors
|
||||||
|
|
||||||
The syntax of CSS is as follws.
|
The syntax of CSS is as follows.
|
||||||
|
|
||||||
~~~css
|
~~~css
|
||||||
selector { color: yellow; padding-top: 10px; ...}
|
selector { color: yellow; padding-top: 10px; ...}
|
||||||
|
@ -113,7 +113,7 @@ selector { color: yellow; padding-top: 10px; ...}
|
||||||
|
|
||||||
Every widget has CSS node.
|
Every widget has CSS node.
|
||||||
For example GtkTextView has `textview` node.
|
For example GtkTextView has `textview` node.
|
||||||
If you want to set style to GtkTextView, set "textview" to the selector.
|
If you want to set style to GtkTextView, substitute "textview" for the selector.
|
||||||
|
|
||||||
~~~css
|
~~~css
|
||||||
textview {color: yellow; ...}
|
textview {color: yellow; ...}
|
||||||
|
@ -148,9 +148,9 @@ However, instead, you can add it to GdkDisplay of the window (usually top level
|
||||||
Look at the source file of `startup` handler again.
|
Look at the source file of `startup` handler again.
|
||||||
|
|
||||||
- 28: The display is obtained by `gtk_widget_get_display`.
|
- 28: The display is obtained by `gtk_widget_get_display`.
|
||||||
- 29: Generate GtkCssProvider.
|
- 29: Generates GtkCssProvider.
|
||||||
- 30: Set the CSS into the provider.
|
- 30: Puts the CSS into the provider.
|
||||||
- 31: Add the provider to the display.
|
- 31: Adds the provider to the display.
|
||||||
|
|
||||||
It is possible to add the provider to the context of GtkTextView instead of GdkDiplay.
|
It is possible to add the provider to the context of GtkTextView instead of GdkDiplay.
|
||||||
To do so, rewrite `tfe_text_view_new`.
|
To do so, rewrite `tfe_text_view_new`.
|
||||||
|
@ -173,7 +173,7 @@ tfe_text_view_new (void) {
|
||||||
}
|
}
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
CSS set to the context takes precedence over the one set to the display.
|
CSS in the context takes precedence over CSS in the display.
|
||||||
|
|
||||||
## activate and open handler
|
## activate and open handler
|
||||||
|
|
||||||
|
@ -217,20 +217,20 @@ They just generate a new GtkNotebookPage.
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
- 1-14: `tfe_activate`.
|
- 1-14: `tfe_activate`.
|
||||||
- 8-10: Get GtkNotebook object.
|
- 8-10: Gets GtkNotebook object.
|
||||||
- 12-13: Generate a new GtkNotebookPage and show the window.
|
- 12-13: Generates a new GtkNotebookPage and show the window.
|
||||||
- 16-33: `tfe_open`.
|
- 16-33: `tfe_open`.
|
||||||
- 24-26: Get GtkNotebook object.
|
- 24-26: Gets GtkNotebook object.
|
||||||
- 28-29: Generate GtkNotebookPage with files.
|
- 28-29: Generates GtkNotebookPage with files.
|
||||||
- 30-31: If opening and reading file failed and no GtkNotebookPage has generated, then generate a empty page.
|
- 30-31: If opening and reading file failed and no GtkNotebookPage has generated, then it generates a empty page.
|
||||||
- 32: Show the window.
|
- 32: Shows the window.
|
||||||
|
|
||||||
These codes have become really simple thanks to tfenotebook.c and tfetextview.c.
|
These codes have become really simple thanks to tfenotebook.c and tfetextview.c.
|
||||||
|
|
||||||
## Primary instance
|
## Primary instance
|
||||||
|
|
||||||
Only one GApplication instance can be run at a time per session.
|
Only one GApplication instance can be run at a time per session.
|
||||||
The session is a bit diffcult concept and also platform-dependent, but roughly speaking, it corresponds to a graphical desktop login.
|
The session is a bit difficult concept and also platform-dependent, but roughly speaking, it corresponds to a graphical desktop login.
|
||||||
When you use your PC, you probably login first, then your desktop appears until you log off.
|
When you use your PC, you probably login first, then your desktop appears until you log off.
|
||||||
This is the session.
|
This is the session.
|
||||||
|
|
||||||
|
@ -313,7 +313,7 @@ First, get the top level window and call `gtk_window_destroy`.
|
||||||
5 gnome=import('gnome')
|
5 gnome=import('gnome')
|
||||||
6 resources = gnome.compile_resources('resources','tfe.gresource.xml')
|
6 resources = gnome.compile_resources('resources','tfe.gresource.xml')
|
||||||
7
|
7
|
||||||
8 sourcefiles=files('tfeapplication.c', 'tfenotebook.c', 'tfetextview.c')
|
8 sourcefiles=files('tfeapplication.c', 'tfenotebook.c', '../tfetextview/tfetextview.c')
|
||||||
9
|
9
|
||||||
10 executable('tfe', sourcefiles, resources, dependencies: gtkdep)
|
10 executable('tfe', sourcefiles, resources, dependencies: gtkdep)
|
||||||
~~~
|
~~~
|
||||||
|
|
565
gfm/sec15.md
565
gfm/sec15.md
|
@ -41,7 +41,7 @@ It is a good practice for you to add more features.
|
||||||
5 gnome=import('gnome')
|
5 gnome=import('gnome')
|
||||||
6 resources = gnome.compile_resources('resources','tfe.gresource.xml')
|
6 resources = gnome.compile_resources('resources','tfe.gresource.xml')
|
||||||
7
|
7
|
||||||
8 sourcefiles=files('tfeapplication.c', 'tfenotebook.c', 'tfetextview.c')
|
8 sourcefiles=files('tfeapplication.c', 'tfenotebook.c', '../tfetextview/tfetextview.c')
|
||||||
9
|
9
|
||||||
10 executable('tfe', sourcefiles, resources, dependencies: gtkdep)
|
10 executable('tfe', sourcefiles, resources, dependencies: gtkdep)
|
||||||
~~~
|
~~~
|
||||||
|
@ -131,7 +131,7 @@ It is a good practice for you to add more features.
|
||||||
~~~C
|
~~~C
|
||||||
1 #include <gtk/gtk.h>
|
1 #include <gtk/gtk.h>
|
||||||
2
|
2
|
||||||
3 #include "tfetextview.h"
|
3 #include "../tfetextview/tfetextview.h"
|
||||||
4 #include "tfenotebook.h"
|
4 #include "tfenotebook.h"
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
|
@ -329,239 +329,246 @@ It is a good practice for you to add more features.
|
||||||
50 gint i;
|
50 gint i;
|
||||||
51 scr = gtk_scrolled_window_new ();
|
51 scr = gtk_scrolled_window_new ();
|
||||||
52
|
52
|
||||||
53 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);
|
53 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR);
|
||||||
54 lab = gtk_label_new (filename);
|
54 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);
|
||||||
55 i = gtk_notebook_append_page (nb, scr, lab);
|
55 lab = gtk_label_new (filename);
|
||||||
56 nbp = gtk_notebook_get_page (nb, scr);
|
56 i = gtk_notebook_append_page (nb, scr, lab);
|
||||||
57 g_object_set (nbp, "tab-expand", TRUE, NULL);
|
57 nbp = gtk_notebook_get_page (nb, scr);
|
||||||
58 gtk_notebook_set_current_page (nb, i);
|
58 g_object_set (nbp, "tab-expand", TRUE, NULL);
|
||||||
59 g_signal_connect (GTK_TEXT_VIEW (tv), "change-file", G_CALLBACK (file_changed), nb);
|
59 gtk_notebook_set_current_page (nb, i);
|
||||||
60 }
|
60 g_signal_connect (GTK_TEXT_VIEW (tv), "change-file", G_CALLBACK (file_changed), nb);
|
||||||
61
|
61 }
|
||||||
62 static void
|
62
|
||||||
63 open_response (TfeTextView *tv, gint response, GtkNotebook *nb) {
|
63 static void
|
||||||
64 GFile *file;
|
64 open_response (TfeTextView *tv, gint response, GtkNotebook *nb) {
|
||||||
65 char *filename;
|
65 GFile *file;
|
||||||
66
|
66 char *filename;
|
||||||
67 if (response != TFE_OPEN_RESPONSE_SUCCESS) {
|
67
|
||||||
68 g_object_ref_sink (tv);
|
68 if (response != TFE_OPEN_RESPONSE_SUCCESS) {
|
||||||
69 g_object_unref (tv);
|
69 g_object_ref_sink (tv);
|
||||||
70 }else if (! G_IS_FILE (file = tfe_text_view_get_file (tv))) {
|
70 g_object_unref (tv);
|
||||||
71 g_object_ref_sink (tv);
|
71 }else if (! G_IS_FILE (file = tfe_text_view_get_file (tv))) {
|
||||||
72 g_object_unref (tv);
|
72 g_object_ref_sink (tv);
|
||||||
73 }else {
|
73 g_object_unref (tv);
|
||||||
74 filename = g_file_get_basename (file);
|
74 }else {
|
||||||
75 g_object_unref (file);
|
75 filename = g_file_get_basename (file);
|
||||||
76 notebook_page_build (nb, GTK_WIDGET (tv), filename);
|
76 g_object_unref (file);
|
||||||
77 }
|
77 notebook_page_build (nb, GTK_WIDGET (tv), filename);
|
||||||
78 }
|
78 }
|
||||||
79
|
79 }
|
||||||
80 void
|
80
|
||||||
81 notebook_page_open (GtkNotebook *nb) {
|
81 void
|
||||||
82 g_return_if_fail(GTK_IS_NOTEBOOK (nb));
|
82 notebook_page_open (GtkNotebook *nb) {
|
||||||
83
|
83 g_return_if_fail(GTK_IS_NOTEBOOK (nb));
|
||||||
84 GtkWidget *tv;
|
84
|
||||||
85
|
85 GtkWidget *tv;
|
||||||
86 tv = tfe_text_view_new ();
|
86
|
||||||
87 g_signal_connect (TFE_TEXT_VIEW (tv), "open-response", G_CALLBACK (open_response), nb);
|
87 tv = tfe_text_view_new ();
|
||||||
88 tfe_text_view_open (TFE_TEXT_VIEW (tv), gtk_widget_get_ancestor (GTK_WIDGET (nb), GTK_TYPE_WINDOW));
|
88 g_signal_connect (TFE_TEXT_VIEW (tv), "open-response", G_CALLBACK (open_response), nb);
|
||||||
89 }
|
89 tfe_text_view_open (TFE_TEXT_VIEW (tv), gtk_widget_get_ancestor (GTK_WIDGET (nb), GTK_TYPE_WINDOW));
|
||||||
90
|
90 }
|
||||||
91 void
|
91
|
||||||
92 notebook_page_new_with_file (GtkNotebook *nb, GFile *file) {
|
92 void
|
||||||
93 g_return_if_fail(GTK_IS_NOTEBOOK (nb));
|
93 notebook_page_new_with_file (GtkNotebook *nb, GFile *file) {
|
||||||
94 g_return_if_fail(G_IS_FILE (file));
|
94 g_return_if_fail(GTK_IS_NOTEBOOK (nb));
|
||||||
95
|
95 g_return_if_fail(G_IS_FILE (file));
|
||||||
96 GtkWidget *tv;
|
96
|
||||||
97 char *filename;
|
97 GtkWidget *tv;
|
||||||
98
|
98 char *filename;
|
||||||
99 if ((tv = tfe_text_view_new_with_file (file)) == NULL)
|
99
|
||||||
100 return; /* read error */
|
100 if ((tv = tfe_text_view_new_with_file (file)) == NULL)
|
||||||
101 filename = g_file_get_basename (file);
|
101 return; /* read error */
|
||||||
102 notebook_page_build (nb, tv, filename);
|
102 filename = g_file_get_basename (file);
|
||||||
103 }
|
103 notebook_page_build (nb, tv, filename);
|
||||||
104
|
104 }
|
||||||
105 void
|
105
|
||||||
106 notebook_page_new (GtkNotebook *nb) {
|
106 void
|
||||||
107 g_return_if_fail(GTK_IS_NOTEBOOK (nb));
|
107 notebook_page_new (GtkNotebook *nb) {
|
||||||
108
|
108 g_return_if_fail(GTK_IS_NOTEBOOK (nb));
|
||||||
109 GtkWidget *tv;
|
109
|
||||||
110 char *filename;
|
110 GtkWidget *tv;
|
||||||
111
|
111 char *filename;
|
||||||
112 tv = tfe_text_view_new ();
|
112
|
||||||
113 filename = get_untitled ();
|
113 tv = tfe_text_view_new ();
|
||||||
114 notebook_page_build (nb, tv, filename);
|
114 filename = get_untitled ();
|
||||||
115 }
|
115 notebook_page_build (nb, tv, filename);
|
||||||
116
|
116 }
|
||||||
|
117
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
## tfetextview.h
|
## tfetextview.h
|
||||||
|
|
||||||
~~~C
|
~~~C
|
||||||
1 #define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type ()
|
1 #ifndef __TFE_TEXT_VIEW_H__
|
||||||
2 G_DECLARE_FINAL_TYPE (TfeTextView, tfe_text_view, TFE, TEXT_VIEW, GtkTextView)
|
2 #define __TFE_TEXT_VIEW_H__
|
||||||
3
|
3
|
||||||
4 /* "open-response" signal response */
|
4 #include <gtk/gtk.h>
|
||||||
5 enum
|
5
|
||||||
6 {
|
6 #define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type ()
|
||||||
7 TFE_OPEN_RESPONSE_SUCCESS,
|
7 G_DECLARE_FINAL_TYPE (TfeTextView, tfe_text_view, TFE, TEXT_VIEW, GtkTextView)
|
||||||
8 TFE_OPEN_RESPONSE_CANCEL,
|
8
|
||||||
9 TFE_OPEN_RESPONSE_ERROR
|
9 /* "open-response" signal response */
|
||||||
10 };
|
10 enum TfeTextViewOpenResponseType
|
||||||
11
|
11 {
|
||||||
12 GFile *
|
12 TFE_OPEN_RESPONSE_SUCCESS,
|
||||||
13 tfe_text_view_get_file (TfeTextView *tv);
|
13 TFE_OPEN_RESPONSE_CANCEL,
|
||||||
14
|
14 TFE_OPEN_RESPONSE_ERROR
|
||||||
15 void
|
15 };
|
||||||
16 tfe_text_view_open (TfeTextView *tv, GtkWidget *win);
|
16
|
||||||
17
|
17 GFile *
|
||||||
18 void
|
18 tfe_text_view_get_file (TfeTextView *tv);
|
||||||
19 tfe_text_view_save (TfeTextView *tv);
|
19
|
||||||
20
|
20 void
|
||||||
21 void
|
21 tfe_text_view_open (TfeTextView *tv, GtkWidget *win);
|
||||||
22 tfe_text_view_saveas (TfeTextView *tv);
|
22
|
||||||
23
|
23 void
|
||||||
24 GtkWidget *
|
24 tfe_text_view_save (TfeTextView *tv);
|
||||||
25 tfe_text_view_new_with_file (GFile *file);
|
25
|
||||||
26
|
26 void
|
||||||
27 GtkWidget *
|
27 tfe_text_view_saveas (TfeTextView *tv);
|
||||||
28 tfe_text_view_new (void);
|
28
|
||||||
29
|
29 GtkWidget *
|
||||||
|
30 tfe_text_view_new_with_file (GFile *file);
|
||||||
|
31
|
||||||
|
32 GtkWidget *
|
||||||
|
33 tfe_text_view_new (void);
|
||||||
|
34
|
||||||
|
35 #endif /* __TFE_TEXT_VIEW_H__ */
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
## tfetextview.c
|
## tfetextview.c
|
||||||
|
|
||||||
~~~C
|
~~~C
|
||||||
1 #include "tfe.h"
|
1 #include <string.h>
|
||||||
2
|
2 #include "tfetextview.h"
|
||||||
3 struct _TfeTextView
|
3
|
||||||
4 {
|
4 struct _TfeTextView
|
||||||
5 GtkTextView parent;
|
5 {
|
||||||
6 GFile *file;
|
6 GtkTextView parent;
|
||||||
7 };
|
7 GFile *file;
|
||||||
8
|
8 };
|
||||||
9 G_DEFINE_TYPE (TfeTextView, tfe_text_view, GTK_TYPE_TEXT_VIEW);
|
9
|
||||||
10
|
10 G_DEFINE_TYPE (TfeTextView, tfe_text_view, GTK_TYPE_TEXT_VIEW);
|
||||||
11 enum {
|
11
|
||||||
12 CHANGE_FILE,
|
12 enum {
|
||||||
13 OPEN_RESPONSE,
|
13 CHANGE_FILE,
|
||||||
14 NUMBER_OF_SIGNALS
|
14 OPEN_RESPONSE,
|
||||||
15 };
|
15 NUMBER_OF_SIGNALS
|
||||||
16
|
16 };
|
||||||
17 static guint tfe_text_view_signals[NUMBER_OF_SIGNALS];
|
17
|
||||||
18
|
18 static guint tfe_text_view_signals[NUMBER_OF_SIGNALS];
|
||||||
19 static void
|
19
|
||||||
20 tfe_text_view_dispose (GObject *gobject) {
|
20 static void
|
||||||
21 TfeTextView *tv = TFE_TEXT_VIEW (gobject);
|
21 tfe_text_view_dispose (GObject *gobject) {
|
||||||
22
|
22 TfeTextView *tv = TFE_TEXT_VIEW (gobject);
|
||||||
23 if (G_IS_FILE (tv->file))
|
23
|
||||||
24 g_clear_object (&tv->file);
|
24 if (G_IS_FILE (tv->file))
|
||||||
25
|
25 g_clear_object (&tv->file);
|
||||||
26 G_OBJECT_CLASS (tfe_text_view_parent_class)->dispose (gobject);
|
26
|
||||||
27 }
|
27 G_OBJECT_CLASS (tfe_text_view_parent_class)->dispose (gobject);
|
||||||
28
|
28 }
|
||||||
29 static void
|
29
|
||||||
30 tfe_text_view_init (TfeTextView *tv) {
|
30 static void
|
||||||
31 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
31 tfe_text_view_init (TfeTextView *tv) {
|
||||||
32
|
32 tv->file = NULL;
|
||||||
33 tv->file = NULL;
|
33 }
|
||||||
34 gtk_text_buffer_set_modified (tb, FALSE);
|
34
|
||||||
35 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR);
|
35 static void
|
||||||
36 }
|
36 tfe_text_view_class_init (TfeTextViewClass *class) {
|
||||||
37
|
37 GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||||
38 static void
|
38
|
||||||
39 tfe_text_view_class_init (TfeTextViewClass *class) {
|
39 object_class->dispose = tfe_text_view_dispose;
|
||||||
40 GObjectClass *object_class = G_OBJECT_CLASS (class);
|
40 tfe_text_view_signals[CHANGE_FILE] = g_signal_newv ("change-file",
|
||||||
41
|
41 G_TYPE_FROM_CLASS (class),
|
||||||
42 object_class->dispose = tfe_text_view_dispose;
|
42 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
|
||||||
43 tfe_text_view_signals[CHANGE_FILE] = g_signal_newv ("change-file",
|
43 NULL /* closure */,
|
||||||
44 G_TYPE_FROM_CLASS (class),
|
44 NULL /* accumulator */,
|
||||||
45 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
|
45 NULL /* accumulator data */,
|
||||||
46 NULL /* closure */,
|
46 NULL /* C marshaller */,
|
||||||
47 NULL /* accumulator */,
|
47 G_TYPE_NONE /* return_type */,
|
||||||
48 NULL /* accumulator data */,
|
48 0 /* n_params */,
|
||||||
49 NULL /* C marshaller */,
|
49 NULL /* param_types */);
|
||||||
50 G_TYPE_NONE /* return_type */,
|
50 GType param_types[] = {G_TYPE_INT};
|
||||||
51 0 /* n_params */,
|
51 tfe_text_view_signals[OPEN_RESPONSE] = g_signal_newv ("open-response",
|
||||||
52 NULL /* param_types */);
|
52 G_TYPE_FROM_CLASS (class),
|
||||||
53 GType param_types[] = {G_TYPE_INT};
|
53 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
|
||||||
54 tfe_text_view_signals[OPEN_RESPONSE] = g_signal_newv ("open-response",
|
54 NULL /* closure */,
|
||||||
55 G_TYPE_FROM_CLASS (class),
|
55 NULL /* accumulator */,
|
||||||
56 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
|
56 NULL /* accumulator data */,
|
||||||
57 NULL /* closure */,
|
57 NULL /* C marshaller */,
|
||||||
58 NULL /* accumulator */,
|
58 G_TYPE_NONE /* return_type */,
|
||||||
59 NULL /* accumulator data */,
|
59 1 /* n_params */,
|
||||||
60 NULL /* C marshaller */,
|
60 param_types);
|
||||||
61 G_TYPE_NONE /* return_type */,
|
61 }
|
||||||
62 1 /* n_params */,
|
62
|
||||||
63 param_types);
|
63 GFile *
|
||||||
64 }
|
64 tfe_text_view_get_file (TfeTextView *tv) {
|
||||||
65
|
65 g_return_val_if_fail (TFE_IS_TEXT_VIEW (tv), NULL);
|
||||||
66 GFile *
|
66
|
||||||
67 tfe_text_view_get_file (TfeTextView *tv) {
|
67 return g_file_dup (tv->file);
|
||||||
68 g_return_val_if_fail (TFE_IS_TEXT_VIEW (tv), NULL);
|
68 }
|
||||||
69
|
69
|
||||||
70 return g_file_dup (tv->file);
|
70 static void
|
||||||
71 }
|
71 open_dialog_response(GtkWidget *dialog, gint response, TfeTextView *tv) {
|
||||||
72
|
72 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
||||||
73 static void
|
73 GFile *file;
|
||||||
74 open_dialog_response(GtkWidget *dialog, gint response, TfeTextView *tv) {
|
74 char *contents;
|
||||||
75 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
75 gsize length;
|
||||||
76 GFile *file;
|
76 GtkWidget *message_dialog;
|
||||||
77 char *contents;
|
77 GError *err = NULL;
|
||||||
78 gsize length;
|
78
|
||||||
79 GtkWidget *message_dialog;
|
79 if (response != GTK_RESPONSE_ACCEPT)
|
||||||
80 GError *err = NULL;
|
80 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_CANCEL);
|
||||||
81
|
81 else if (! G_IS_FILE (file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog))))
|
||||||
82 if (response != GTK_RESPONSE_ACCEPT)
|
82 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_ERROR);
|
||||||
83 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_CANCEL);
|
83 else if (! g_file_load_contents (file, NULL, &contents, &length, NULL, &err)) { /* read error */
|
||||||
84 else if (! G_IS_FILE (file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog))))
|
84 if (G_IS_FILE (file))
|
||||||
85 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_ERROR);
|
85 g_object_unref (file);
|
||||||
86 else if (! g_file_load_contents (file, NULL, &contents, &length, NULL, &err)) { /* read error */
|
86 message_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog), GTK_DIALOG_MODAL,
|
||||||
87 if (G_IS_FILE (file))
|
87 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
|
||||||
88 g_object_unref (file);
|
88 "%s.\n", err->message);
|
||||||
89 message_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog), GTK_DIALOG_MODAL,
|
89 g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
|
||||||
90 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
|
90 gtk_widget_show (message_dialog);
|
||||||
91 "%s.\n", err->message);
|
91 g_error_free (err);
|
||||||
92 g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
|
92 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_ERROR);
|
||||||
93 gtk_widget_show (message_dialog);
|
93 } else {
|
||||||
94 g_error_free (err);
|
94 gtk_text_buffer_set_text (tb, contents, length);
|
||||||
95 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_ERROR);
|
95 g_free (contents);
|
||||||
96 } else {
|
96 if (G_IS_FILE (tv->file))
|
||||||
97 gtk_text_buffer_set_text (tb, contents, length);
|
97 g_object_unref (tv->file);
|
||||||
98 g_free (contents);
|
98 tv->file = file;
|
||||||
99 if (G_IS_FILE (tv->file))
|
99 gtk_text_buffer_set_modified (tb, FALSE);
|
||||||
100 g_object_unref (tv->file);
|
100 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_SUCCESS);
|
||||||
101 tv->file = file;
|
101 g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
|
||||||
102 gtk_text_buffer_set_modified (tb, FALSE);
|
102 }
|
||||||
103 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_SUCCESS);
|
103 gtk_window_destroy (GTK_WINDOW (dialog));
|
||||||
104 }
|
104 }
|
||||||
105 gtk_window_destroy (GTK_WINDOW (dialog));
|
105
|
||||||
106 }
|
106 void
|
||||||
107
|
107 tfe_text_view_open (TfeTextView *tv, GtkWidget *win) {
|
||||||
108 void
|
108 g_return_if_fail (TFE_IS_TEXT_VIEW (tv));
|
||||||
109 tfe_text_view_open (TfeTextView *tv, GtkWidget *win) {
|
109 g_return_if_fail (GTK_IS_WINDOW (win));
|
||||||
110 g_return_if_fail (TFE_IS_TEXT_VIEW (tv));
|
110
|
||||||
111 g_return_if_fail (GTK_IS_WINDOW (win));
|
111 GtkWidget *dialog;
|
||||||
112
|
112
|
||||||
113 GtkWidget *dialog;
|
113 dialog = gtk_file_chooser_dialog_new ("Open file", GTK_WINDOW (win), GTK_FILE_CHOOSER_ACTION_OPEN,
|
||||||
114
|
114 "Cancel", GTK_RESPONSE_CANCEL,
|
||||||
115 dialog = gtk_file_chooser_dialog_new ("Open file", GTK_WINDOW (win), GTK_FILE_CHOOSER_ACTION_OPEN,
|
115 "Open", GTK_RESPONSE_ACCEPT,
|
||||||
116 "Cancel", GTK_RESPONSE_CANCEL,
|
116 NULL);
|
||||||
117 "Open", GTK_RESPONSE_ACCEPT,
|
117 g_signal_connect (dialog, "response", G_CALLBACK (open_dialog_response), tv);
|
||||||
118 NULL);
|
118 gtk_widget_show (dialog);
|
||||||
119 g_signal_connect (dialog, "response", G_CALLBACK (open_dialog_response), tv);
|
119 }
|
||||||
120 gtk_widget_show (dialog);
|
120
|
||||||
121 }
|
121 static void
|
||||||
122
|
122 saveas_dialog_response (GtkWidget *dialog, gint response, TfeTextView *tv) {
|
||||||
123 static void
|
123 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
||||||
124 saveas_dialog_response (GtkWidget *dialog, gint response, TfeTextView *tv) {
|
124 GFile *file;
|
||||||
125 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
125
|
||||||
126 GFile *file;
|
126 if (response == GTK_RESPONSE_ACCEPT) {
|
||||||
127
|
127 file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
|
||||||
128 if (response == GTK_RESPONSE_ACCEPT) {
|
128 if (G_IS_FILE(file)) {
|
||||||
129 file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
|
129 if (G_IS_FILE (tv->file))
|
||||||
130 if (G_IS_FILE(file)) {
|
130 g_object_unref (tv->file);
|
||||||
131 tv->file = file;
|
131 tv->file = file;
|
||||||
132 gtk_text_buffer_set_modified (tb, TRUE);
|
132 gtk_text_buffer_set_modified (tb, TRUE);
|
||||||
133 g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
|
133 g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
|
||||||
|
@ -584,7 +591,7 @@ It is a good practice for you to add more features.
|
||||||
150 GError *err = NULL;
|
150 GError *err = NULL;
|
||||||
151
|
151
|
||||||
152 if (! gtk_text_buffer_get_modified (tb))
|
152 if (! gtk_text_buffer_get_modified (tb))
|
||||||
153 return; /* no necessary to save it */
|
153 return; /* no need to save it */
|
||||||
154 else if (tv->file == NULL)
|
154 else if (tv->file == NULL)
|
||||||
155 tfe_text_view_saveas (tv);
|
155 tfe_text_view_saveas (tv);
|
||||||
156 else {
|
156 else {
|
||||||
|
@ -593,77 +600,77 @@ It is a good practice for you to add more features.
|
||||||
159 if (g_file_replace_contents (tv->file, contents, strlen (contents), NULL, TRUE, G_FILE_CREATE_NONE, NULL, NULL, &err))
|
159 if (g_file_replace_contents (tv->file, contents, strlen (contents), NULL, TRUE, G_FILE_CREATE_NONE, NULL, NULL, &err))
|
||||||
160 gtk_text_buffer_set_modified (tb, FALSE);
|
160 gtk_text_buffer_set_modified (tb, FALSE);
|
||||||
161 else {
|
161 else {
|
||||||
162 /* It is possible that tv->file is broken. */
|
162 /* It is possible that tv->file is broken or you don't have permission to write. */
|
||||||
163 /* It is a good idea to set tv->file to NULL. */
|
163 /* It is a good idea to set tv->file to NULL. */
|
||||||
164 if (G_IS_FILE (tv->file))
|
164 if (G_IS_FILE (tv->file))
|
||||||
165 g_object_unref (tv->file);
|
165 g_object_unref (tv->file);
|
||||||
166 tv->file =NULL;
|
166 tv->file =NULL;
|
||||||
167 g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
|
167 g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
|
||||||
168 gtk_text_buffer_set_modified (tb, TRUE);
|
168 message_dialog = gtk_message_dialog_new (GTK_WINDOW (win), GTK_DIALOG_MODAL,
|
||||||
169 message_dialog = gtk_message_dialog_new (GTK_WINDOW (win), GTK_DIALOG_MODAL,
|
169 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
|
||||||
170 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
|
170 "%s.\n", err->message);
|
||||||
171 "%s.\n", err->message);
|
171 g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
|
||||||
172 g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
|
172 gtk_widget_show (message_dialog);
|
||||||
173 gtk_widget_show (message_dialog);
|
173 g_error_free (err);
|
||||||
174 g_error_free (err);
|
174 }
|
||||||
175 }
|
175 }
|
||||||
176 }
|
176 }
|
||||||
177 }
|
177
|
||||||
178
|
178 void
|
||||||
179 void
|
179 tfe_text_view_saveas (TfeTextView *tv) {
|
||||||
180 tfe_text_view_saveas (TfeTextView *tv) {
|
180 g_return_if_fail (TFE_IS_TEXT_VIEW (tv));
|
||||||
181 g_return_if_fail (TFE_IS_TEXT_VIEW (tv));
|
181
|
||||||
182
|
182 GtkWidget *dialog;
|
||||||
183 GtkWidget *dialog;
|
183 GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW);
|
||||||
184 GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW);
|
184
|
||||||
185
|
185 dialog = gtk_file_chooser_dialog_new ("Save file", GTK_WINDOW (win), GTK_FILE_CHOOSER_ACTION_SAVE,
|
||||||
186 dialog = gtk_file_chooser_dialog_new ("Save file", GTK_WINDOW (win), GTK_FILE_CHOOSER_ACTION_SAVE,
|
186 "Cancel", GTK_RESPONSE_CANCEL,
|
||||||
187 "_Cancel", GTK_RESPONSE_CANCEL,
|
187 "Save", GTK_RESPONSE_ACCEPT,
|
||||||
188 "_Save", GTK_RESPONSE_ACCEPT,
|
188 NULL);
|
||||||
189 NULL);
|
189 g_signal_connect (dialog, "response", G_CALLBACK (saveas_dialog_response), tv);
|
||||||
190 g_signal_connect (dialog, "response", G_CALLBACK (saveas_dialog_response), tv);
|
190 gtk_widget_show (dialog);
|
||||||
191 gtk_widget_show (dialog);
|
191 }
|
||||||
192 }
|
192
|
||||||
193
|
193 GtkWidget *
|
||||||
194 GtkWidget *
|
194 tfe_text_view_new_with_file (GFile *file) {
|
||||||
195 tfe_text_view_new_with_file (GFile *file) {
|
195 g_return_val_if_fail (G_IS_FILE (file), NULL);
|
||||||
196 g_return_val_if_fail (G_IS_FILE (file), NULL);
|
196
|
||||||
197
|
197 GtkWidget *tv;
|
||||||
198 GtkWidget *tv;
|
198 GtkTextBuffer *tb;
|
||||||
199 GtkTextBuffer *tb;
|
199 char *contents;
|
||||||
200 char *contents;
|
200 gsize length;
|
||||||
201 gsize length;
|
201
|
||||||
202
|
202 if (! g_file_load_contents (file, NULL, &contents, &length, NULL, NULL)) /* read error */
|
||||||
203 if (! g_file_load_contents (file, NULL, &contents, &length, NULL, NULL)) /* read error */
|
203 return NULL;
|
||||||
204 return NULL;
|
204
|
||||||
205
|
205 tv = tfe_text_view_new();
|
||||||
206 tv = tfe_text_view_new();
|
206 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
||||||
207 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
207 gtk_text_buffer_set_text (tb, contents, length);
|
||||||
208 gtk_text_buffer_set_text (tb, contents, length);
|
208 g_free (contents);
|
||||||
209 g_free (contents);
|
209 TFE_TEXT_VIEW (tv)->file = g_file_dup (file);
|
||||||
210 TFE_TEXT_VIEW (tv)->file = g_file_dup (file);
|
210 return tv;
|
||||||
211 return tv;
|
211 }
|
||||||
212 }
|
212
|
||||||
213
|
213 GtkWidget *
|
||||||
214 GtkWidget *
|
214 tfe_text_view_new (void) {
|
||||||
215 tfe_text_view_new (void) {
|
215 return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL));
|
||||||
216 return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL));
|
216 }
|
||||||
217 }
|
217
|
||||||
218
|
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
## Total number of lines, words and charcters
|
## Total number of lines, words and characters
|
||||||
|
|
||||||
$ LANG=C wc tfe5/meson.build tfe5/tfeapplication.c tfe5/tfe.gresource.xml tfe5/tfe.h tfe5/tfenotebook.c tfe5/tfenotebook.h tfe5/tfetextview.c tfe5/tfetextview.h tfe5/tfe.ui
|
$ LANG=C wc tfe5/meson.build tfe5/tfeapplication.c tfe5/tfe.gresource.xml tfe5/tfe.h tfe5/tfenotebook.c tfe5/tfenotebook.h tfetextview/tfetextview.c tfetextview/tfetextview.h tfe5/tfe.ui
|
||||||
10 17 279 tfe5/meson.build
|
10 17 294 tfe5/meson.build
|
||||||
117 348 3576 tfe5/tfeapplication.c
|
117 348 3576 tfe5/tfeapplication.c
|
||||||
6 9 153 tfe5/tfe.gresource.xml
|
6 9 153 tfe5/tfe.gresource.xml
|
||||||
4 6 72 tfe5/tfe.h
|
4 6 87 tfe5/tfe.h
|
||||||
116 321 2992 tfe5/tfenotebook.c
|
117 325 3064 tfe5/tfenotebook.c
|
||||||
12 17 196 tfe5/tfenotebook.h
|
12 17 196 tfe5/tfenotebook.h
|
||||||
218 635 7769 tfe5/tfetextview.c
|
217 637 7725 tfetextview/tfetextview.c
|
||||||
29 49 561 tfe5/tfetextview.h
|
35 60 701 tfetextview/tfetextview.h
|
||||||
64 105 2266 tfe5/tfe.ui
|
64 105 2266 tfe5/tfe.ui
|
||||||
576 1507 17864 total
|
582 1524 18062 total
|
||||||
|
|
||||||
|
|
||||||
Up: [Readme.md](../Readme.md), Prev: [Section 14](sec14.md), Next: [Section 16](sec16.md)
|
Up: [Readme.md](../Readme.md), Prev: [Section 14](sec14.md), Next: [Section 16](sec16.md)
|
||||||
|
|
30
gfm/sec16.md
30
gfm/sec16.md
|
@ -33,7 +33,7 @@ Menus can build a complicated structure thanks to the links of menu items.
|
||||||
|
|
||||||
## GMenuModel, GMenu and GMenuItem
|
## GMenuModel, GMenu and GMenuItem
|
||||||
|
|
||||||
GMenuModel is an abstact object which represents a menu.
|
GMenuModel is an abstract object which represents a menu.
|
||||||
GMenu is a simple implementation of GMenuModel and a child object of GMenuModel.
|
GMenu is a simple implementation of GMenuModel and a child object of GMenuModel.
|
||||||
|
|
||||||
GObjct -- GMenuModel -- GMenu
|
GObjct -- GMenuModel -- GMenu
|
||||||
|
@ -51,7 +51,7 @@ GMenuItem and Gmenu (or GMenuModel) don't have a parent-child relationship.
|
||||||
Usually, GMenuItem has attributes.
|
Usually, GMenuItem has attributes.
|
||||||
One of the attributes is label.
|
One of the attributes is label.
|
||||||
For example, there is a menu item which has "Edit" label in the first diagram in this section.
|
For example, there is a menu item which has "Edit" label in the first diagram in this section.
|
||||||
"Cut", "Copy", "Paste" and "Select All" are also the lables of menu items.
|
"Cut", "Copy", "Paste" and "Select All" are also the labels of menu items.
|
||||||
Other attributes will be explained later.
|
Other attributes will be explained later.
|
||||||
|
|
||||||
Some menu items have a link to another GMenu.
|
Some menu items have a link to another GMenu.
|
||||||
|
@ -179,18 +179,18 @@ You don' t need to care about it.
|
||||||
It is the fourth parameter in the `g_signal_connect` (line 17) that has connected the action and the handler.
|
It is the fourth parameter in the `g_signal_connect` (line 17) that has connected the action and the handler.
|
||||||
- 6: A function `g_application_quit` immediately quits the application.
|
- 6: A function `g_application_quit` immediately quits the application.
|
||||||
- 9-33: `on_activate` is a handler of "activate" signal on GtkApplication.
|
- 9-33: `on_activate` is a handler of "activate" signal on GtkApplication.
|
||||||
- 11-13: Generate a GtkApplicationWindow and set a pointer to it to `win`. And set the title and default size.
|
- 11-13: Generates a GtkApplicationWindow and assigns a pointer to it to `win`. And sets the title and default size.
|
||||||
- 15: Generate GSimpleAction `act_quit`.
|
- 15: Generates GSimpleAction `act_quit`.
|
||||||
It is stateless.
|
It is stateless.
|
||||||
The first argument of `g_simple_action_new` is a name of the action and the second argument is a parameter.
|
The first argument of `g_simple_action_new` is a name of the action and the second argument is a parameter.
|
||||||
If you don't need the parameter, set it `NULL`.
|
If you don't need the parameter, pass `NULL`.
|
||||||
Therefore, `act_quit` has a name "quit" and no parameter.
|
Therefore, `act_quit` has a name "quit" and no parameter.
|
||||||
- 16: Add the action to GtkApplication `app`.
|
- 16: Adds the action to GtkApplication `app`.
|
||||||
GtkApplication implements an interface GActionMap and GActionGroup.
|
GtkApplication implements an interface GActionMap and GActionGroup.
|
||||||
And GtkApplication can have a group of actions and actions are added by the function `g_action_map_add_action`.
|
And GtkApplication can have a group of actions and actions are added by the function `g_action_map_add_action`.
|
||||||
This function is described in GMenuModel section in GIO API reference.
|
This function is described in GMenuModel section in GIO API reference.
|
||||||
- 17: Connect "activate" signal of the action and the handler `quit_activated`.
|
- 17: Connects "activate" signal of the action and the handler `quit_activated`.
|
||||||
- 19-22: Generate GMenu and GMenuItem.
|
- 19-22: Generates GMenu and GMenuItem.
|
||||||
`menubar` and `menu` are GMenu.
|
`menubar` and `menu` are GMenu.
|
||||||
`menu_item_menu` and `menu_item_quit` are GMenuItem.
|
`menu_item_menu` and `menu_item_quit` are GMenuItem.
|
||||||
`menu_item_menu` has a label "Menu" and no action.
|
`menu_item_menu` has a label "Menu" and no action.
|
||||||
|
@ -198,19 +198,19 @@ This function is described in GMenuModel section in GIO API reference.
|
||||||
The second argument "app.quit" is a combination of "app" and "quit".
|
The second argument "app.quit" is a combination of "app" and "quit".
|
||||||
"app" is a prefix and it means that the action belongs to GtkApplication. "quit" is the name of the action.
|
"app" is a prefix and it means that the action belongs to GtkApplication. "quit" is the name of the action.
|
||||||
Therefore, it points the action which belongs to GtkApplication and has the name "quit" -- it is `act_quit`.
|
Therefore, it points the action which belongs to GtkApplication and has the name "quit" -- it is `act_quit`.
|
||||||
- 23-24: Append `act_quit` to `menu`.
|
- 23-24: Appends `act_quit` to `menu`.
|
||||||
As I mentioned before, all the attribute and link values are copied and used to form a new item within `menu`.
|
As I mentioned before, all the attribute and link values are copied and used to form a new item within `menu`.
|
||||||
Therefore after the appending, `menu` has a copy of `act_quit` in itself and `act_quit` is no longer needed.
|
Therefore after the appending, `menu` has a copy of `act_quit` in itself and `act_quit` is no longer needed.
|
||||||
It is freed by `g_object_unref`.
|
It is freed by `g_object_unref`.
|
||||||
- 25: Set a submenu link to `menu_item_menu`.
|
- 25: Sets a submenu link to `menu_item_menu`.
|
||||||
And the link points the GMenu `menu`.
|
And the link points the GMenu `menu`.
|
||||||
- 26-27: Append `menu_item_menu` to `menubar`.
|
- 26-27: Appends `menu_item_menu` to `menubar`.
|
||||||
Then free `menu_item_menu`.
|
Then frees `menu_item_menu`.
|
||||||
GMenu and GMenuItem are connected and finally a menu is made up.
|
GMenu and GMenuItem are connected and finally a menu is made up.
|
||||||
The structure of the menu is shown in the diagram below.
|
The structure of the menu is shown in the diagram below.
|
||||||
- 29: The menu is set to GtkApplication.
|
- 29: The menu is inserted to GtkApplication.
|
||||||
- 30: Set GtkApplicationWindow to show the menubar.
|
- 30: Sets GtkApplicationWindow to show the menubar.
|
||||||
- 31: Show the window.
|
- 31: Shows the window.
|
||||||
|
|
||||||
![menu and action](../image/menu1.png)
|
![menu and action](../image/menu1.png)
|
||||||
|
|
||||||
|
|
44
gfm/sec17.md
44
gfm/sec17.md
|
@ -47,10 +47,10 @@ The second argument is called detailed action.
|
||||||
Detailed action has three parts, prefix, action name and target.
|
Detailed action has three parts, prefix, action name and target.
|
||||||
"win.fullscreen" means that the prefix is "win", the action name is "fullscreen" and there's no target.
|
"win.fullscreen" means that the prefix is "win", the action name is "fullscreen" and there's no target.
|
||||||
The prefix says that the action belongs to the window.
|
The prefix says that the action belongs to the window.
|
||||||
- connect the action `act_fullscreen` and the "change-state" signal handler `fullscreen_changed`.
|
- connects the action `act_fullscreen` and the "change-state" signal handler `fullscreen_changed`.
|
||||||
If the fullscreen menu is clicked, then the corresponding action `act_fullscreen` is activated.
|
If the fullscreen menu is clicked, then the corresponding action `act_fullscreen` is activated.
|
||||||
But no handler is connected to "activate" signal.
|
But no handler is connected to "activate" signal.
|
||||||
Then, the default behaviour for boolean-stated actions with a NULL parameter type like `act_fullscreen` is to toggle them via the “change-state” signal.
|
Then, the default behavior for boolean-stated actions with a NULL parameter type like `act_fullscreen` is to toggle them via the “change-state” signal.
|
||||||
|
|
||||||
The following is the "change-state" signal handler.
|
The following is the "change-state" signal handler.
|
||||||
|
|
||||||
|
@ -69,20 +69,20 @@ fullscreen_changed(GSimpleAction *action, GVariant *value, gpointer win) {
|
||||||
The first parameter is the action which emits the "change-state" signal.
|
The first parameter is the action which emits the "change-state" signal.
|
||||||
The second parameter is the value of the state of the action.
|
The second parameter is the value of the state of the action.
|
||||||
But it is toggled because of no "activate" signal handler.
|
But it is toggled because of no "activate" signal handler.
|
||||||
Ther third parameter is a user data which is set in `g_signal_connect`.
|
The third parameter is a user data which is set in `g_signal_connect`.
|
||||||
- If the value is boolean type and `TRUE`, then maximize the window.
|
- If the value is boolean type and `TRUE`, then maximize the window.
|
||||||
Otherwise unmaximize.
|
Otherwise unmaximize.
|
||||||
- Set `value` to the state of the action.
|
- Sets the state of the action to `value`.
|
||||||
Note: the second argument was the toggled state value, but at this stage the state of the action has the original value.
|
Note: the second argument was the toggled state value, but at this stage the state of the action has the original value.
|
||||||
So, you need to set the new value by `g_simple_action_set_state`.
|
So, you need to set the state to the new value by `g_simple_action_set_state`.
|
||||||
|
|
||||||
You can use "activate" signal instead ot "change-state" signal, or both signals.
|
You can use "activate" signal instead of "change-state" signal, or both signals.
|
||||||
But the way above is the simplest and best.
|
But the way above is the simplest and best.
|
||||||
|
|
||||||
### GVariant
|
### GVariant
|
||||||
|
|
||||||
GVarient can contain boolean, string or other simple type values.
|
GVarient can contain boolean, string or other simple type values.
|
||||||
For example, the following program set TRUE to `value` whose type is GVariant.
|
For example, the following program assigns TRUE to `value` whose type is GVariant.
|
||||||
|
|
||||||
~~~C
|
~~~C
|
||||||
GVariant *value = g_variant_new_boolean (TRUE);
|
GVariant *value = g_variant_new_boolean (TRUE);
|
||||||
|
@ -157,10 +157,10 @@ The second argument is a detailed action.
|
||||||
Its prefix is "win", action name is "color" and target is "red".
|
Its prefix is "win", action name is "color" and target is "red".
|
||||||
Target is sent to the action as a parameter.
|
Target is sent to the action as a parameter.
|
||||||
The same goes for `menu_item_green` and `menu_item_blue`.
|
The same goes for `menu_item_green` and `menu_item_blue`.
|
||||||
- connect the action `act_color` and the "activate" signal handler `color_activate`.
|
- connects the action `act_color` and the "activate" signal handler `color_activate`.
|
||||||
If one of the three menus is clicked, then the action `act_color` is activated with a parameter to which the menu item gives its target.
|
If one of the three menus is clicked, then the action `act_color` is activated with a parameter to which the menu item gives its target.
|
||||||
No handler is connected to "change-state" signal.
|
No handler is connected to "change-state" signal.
|
||||||
Then the default behaviour is to call `g_simple_action_set_state()` to set the state to the requested value.
|
Then the default behavior is to call `g_simple_action_set_state()` to set the state to the requested value.
|
||||||
|
|
||||||
The following is the "activate" signal handler.
|
The following is the "activate" signal handler.
|
||||||
|
|
||||||
|
@ -183,10 +183,10 @@ The third parameter is a user data which is set in `g_signal_connect`.
|
||||||
- `color` is a CSS string generated by `g_strdup_printf`.
|
- `color` is a CSS string generated by `g_strdup_printf`.
|
||||||
The parameter of `g_strdup_printf` is the same as printf C standard function.
|
The parameter of `g_strdup_printf` is the same as printf C standard function.
|
||||||
`g_variant_get_string` get the string contained in `parameter`.
|
`g_variant_get_string` get the string contained in `parameter`.
|
||||||
- Set the color to the css provider.
|
- Sets the color of the css provider.
|
||||||
- Free the string `color`.
|
- Frees the string `color`.
|
||||||
- Change the state by `g_action_change_state`.
|
- Changes the state by `g_action_change_state`.
|
||||||
The function just set the parameter to the state of the action by `g_simple_action_set_state`.
|
The function just sets the state of the action to the parameter by `g_simple_action_set_state`.
|
||||||
Therefore, you can use `g_simple_action_set_state` instead of `g_action_change_state`.
|
Therefore, you can use `g_simple_action_set_state` instead of `g_action_change_state`.
|
||||||
|
|
||||||
Note: If you have set a "change-state" signal handler, `g_action_change_state` will emit "change-state" signal instead of calling `g_simple_action_set_state`.
|
Note: If you have set a "change-state" signal handler, `g_action_change_state` will emit "change-state" signal instead of calling `g_simple_action_set_state`.
|
||||||
|
@ -221,7 +221,7 @@ It finally output the string "s".
|
||||||
It uses a type string "s" which means string.
|
It uses a type string "s" which means string.
|
||||||
- `g_variant_type_peek_string` takes a peek at `vtype`.
|
- `g_variant_type_peek_string` takes a peek at `vtype`.
|
||||||
It is the string "s" given at the generation time.
|
It is the string "s" given at the generation time.
|
||||||
- print the string to the terminal.
|
- prints the string to the terminal.
|
||||||
|
|
||||||
## Example code
|
## Example code
|
||||||
The following code includes stateful actions above.
|
The following code includes stateful actions above.
|
||||||
|
@ -349,7 +349,7 @@ The code is as follows.
|
||||||
- 5-26: Signal handlers.
|
- 5-26: Signal handlers.
|
||||||
They have been explained in this section.
|
They have been explained in this section.
|
||||||
- 30-36: `win` and `lb` are GtkApplicationWindow and GtkLabel respectively.
|
- 30-36: `win` and `lb` are GtkApplicationWindow and GtkLabel respectively.
|
||||||
`win` has a title "menu2" and its defaust size is 400x300.
|
`win` has a title "menu2" and its default size is 400x300.
|
||||||
`lb` is named as "lb".
|
`lb` is named as "lb".
|
||||||
The name is used in CSS.
|
The name is used in CSS.
|
||||||
`lb` is set to `win` as a child.
|
`lb` is set to `win` as a child.
|
||||||
|
@ -360,7 +360,7 @@ It has a toggle state.
|
||||||
- stateful and has a parameter.
|
- stateful and has a parameter.
|
||||||
Parameter is a string type.
|
Parameter is a string type.
|
||||||
- stateless and has no parameter.
|
- stateless and has no parameter.
|
||||||
- 45-54: Generate GMenu and GMenuItem.
|
- 45-54: Generates GMenu and GMenuItem.
|
||||||
There are three sections.
|
There are three sections.
|
||||||
- 56-61: Signals are connected to handlers.
|
- 56-61: Signals are connected to handlers.
|
||||||
And actions are added to GActionMap.
|
And actions are added to GActionMap.
|
||||||
|
@ -369,15 +369,15 @@ they are added to `win`.
|
||||||
GtkApplicationWindow implements GActionModel interface like GtkApplication.
|
GtkApplicationWindow implements GActionModel interface like GtkApplication.
|
||||||
`act_quit` has "app" prefix and belongs to GtkApplication.
|
`act_quit` has "app" prefix and belongs to GtkApplication.
|
||||||
It is added to `app`.
|
It is added to `app`.
|
||||||
- 63-77: Connect and build the menus.
|
- 63-77: Connects and builds the menus.
|
||||||
Useless GMenuItem are freed.
|
Useless GMenuItem are freed.
|
||||||
- 79-80: GMenuModel `menubar` is set to `app`.
|
- 79-80: GMenuModel `menubar` is inserted to `app`.
|
||||||
Set show menubar property to `TRUE` in `win`.
|
Sets show menubar property to `TRUE` in `win`.
|
||||||
Note: `gtk_application_window_set_show_menubar` generates GtkPopoverMenubar from GMenuModel.
|
Note: `gtk_application_window_set_show_menubar` generates GtkPopoverMenubar from GMenuModel.
|
||||||
This is a different point between Gtk3 and Gtk4.
|
This is a different point between Gtk3 and Gtk4.
|
||||||
And you can use GtkPopoverMenubar directly and set it as a descendant widget of the window.
|
And you can use GtkPopoverMenubar directly and set it as a descendant widget of the window.
|
||||||
You may use GtkBox as a child widget of the window and set GtkPopoverMenubar as the first child of the box.
|
You may use GtkBox as a child widget of the window and insert GtkPopoverMenubar as the first child of the box.
|
||||||
- 82-87: Set CSS.
|
- 82-87: Sets CSS.
|
||||||
`provider` is GtkCssProvider which is defined in line three as a static variable.
|
`provider` is GtkCssProvider which is defined in line three as a static variable.
|
||||||
Its CSS data is:
|
Its CSS data is:
|
||||||
`label#lb {background-color: red;}`.
|
`label#lb {background-color: red;}`.
|
||||||
|
@ -390,7 +390,7 @@ The style is surrounded by open and close braces.
|
||||||
The style is applied to GtkLabel which has a name "lb".
|
The style is applied to GtkLabel which has a name "lb".
|
||||||
Other GtkLabel have no effect from this.
|
Other GtkLabel have no effect from this.
|
||||||
The provider is added to GdkDisplay.
|
The provider is added to GdkDisplay.
|
||||||
- 90: Show the window.
|
- 90: Shows the window.
|
||||||
|
|
||||||
|
|
||||||
Up: [Readme.md](../Readme.md), Prev: [Section 16](sec16.md), Next: [Section 18](sec18.md)
|
Up: [Readme.md](../Readme.md), Prev: [Section 16](sec16.md), Next: [Section 18](sec18.md)
|
||||||
|
|
20
gfm/sec18.md
20
gfm/sec18.md
|
@ -22,7 +22,7 @@ The file starts and ends with interface tag.
|
||||||
|
|
||||||
`menu` tag corresponds to GMenu object.
|
`menu` tag corresponds to GMenu object.
|
||||||
`id` attribute defines the name of the object.
|
`id` attribute defines the name of the object.
|
||||||
It will be refered by GtkBuilder.
|
It will be referred by GtkBuilder.
|
||||||
|
|
||||||
~~~xml
|
~~~xml
|
||||||
<submenu>
|
<submenu>
|
||||||
|
@ -144,7 +144,7 @@ The following is the ui file of the menu in `menu3`.
|
||||||
72 </interface>
|
72 </interface>
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
The ui file is converted to the resource by the resouce compiler `glib-compile-resouces` with xml file below.
|
The ui file is converted to the resource by the resource compiler `glib-compile-resouces` with xml file below.
|
||||||
|
|
||||||
~~~xml
|
~~~xml
|
||||||
1 <?xml version="1.0" encoding="UTF-8"?>
|
1 <?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
@ -165,7 +165,7 @@ gtk_application_set_menubar (GTK_APPLICATION (app), menubar);
|
||||||
g_object_unref (builder);
|
g_object_unref (builder);
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
It is important that `builder` is unreferred after the GMenuModel `menubar` is set to the application.
|
It is important that `builder` is unreferred after the GMenuModel `menubar` is inserted to the application.
|
||||||
If you do it before setting, bad thing will happen -- your computer might freeze.
|
If you do it before setting, bad thing will happen -- your computer might freeze.
|
||||||
|
|
||||||
## Action entry
|
## Action entry
|
||||||
|
@ -215,9 +215,9 @@ g_action_map_add_action_entries (G_ACTION_MAP (app), app_entries,
|
||||||
|
|
||||||
The code above does:
|
The code above does:
|
||||||
|
|
||||||
- Build the "quit" action
|
- Builds the "quit" action
|
||||||
- Connect the action and the "activate" signal handler `quit_activate`
|
- Connects the action and the "activate" signal handler `quit_activate`
|
||||||
- Add the action to the action map `app`.
|
- Adds the action to the action map `app`.
|
||||||
|
|
||||||
The same goes for the other actions.
|
The same goes for the other actions.
|
||||||
|
|
||||||
|
@ -231,12 +231,12 @@ g_action_map_add_action_entries (G_ACTION_MAP (win), win_entries,
|
||||||
~~~
|
~~~
|
||||||
The code above does:
|
The code above does:
|
||||||
|
|
||||||
- Build a "fullscreen" action and "color" action.
|
- Builds a "fullscreen" action and "color" action.
|
||||||
- Connect the "fullscreen" action and the "change-state" signal handler `fullscreen_changed`
|
- Connects the "fullscreen" action and the "change-state" signal handler `fullscreen_changed`
|
||||||
- Its initial state is set to FALSE.
|
- Its initial state is set to FALSE.
|
||||||
- Connect the "color" action and the "activate" signal handler `color_activate`
|
- Connects the "color" action and the "activate" signal handler `color_activate`
|
||||||
- Its parameter type is string and the initial value is "red".
|
- Its parameter type is string and the initial value is "red".
|
||||||
- Add the actions to the action map `win`.
|
- Adds the actions to the action map `win`.
|
||||||
|
|
||||||
## Example code
|
## Example code
|
||||||
|
|
||||||
|
|
58
gfm/sec19.md
58
gfm/sec19.md
|
@ -17,15 +17,15 @@ In this section, I will explain:
|
||||||
Cairo is a two dimensional graphics library.
|
Cairo is a two dimensional graphics library.
|
||||||
First, you need to know surface, source, mask, destination, cairo context and transformation.
|
First, you need to know surface, source, mask, destination, cairo context and transformation.
|
||||||
|
|
||||||
- surface represents an image.
|
- Surface represents an image.
|
||||||
It is like a canvas.
|
It is like a canvas.
|
||||||
We can draw shapes and images with different colors on surfaces.
|
We can draw shapes and images with different colors on surfaces.
|
||||||
- source pattern, or simply source, is a kind of paint, which will be transfered to destination surface by cairo functions.
|
- Source pattern, or simply source, is a kind of paint, which will be transferred to destination surface by cairo functions.
|
||||||
- mask is image mask used in the transference.
|
- Mask is image mask used in the transference.
|
||||||
- destination is a target surface.
|
- Destination is a target surface.
|
||||||
- cairo context manages the transference from source to destination through mask with its functions.
|
- Cairo context manages the transference from source to destination through mask with its functions.
|
||||||
For example, `cairo_stroke` is a function to draw a path to the destination by the transference.
|
For example, `cairo_stroke` is a function to draw a path to the destination by the transference.
|
||||||
- transformation is applied before the transfer completes.
|
- Transformation is applied before the transfer completes.
|
||||||
The transformation is called affine, which is a mathematics terminology, and represented by matrix multiplication and vector addition.
|
The transformation is called affine, which is a mathematics terminology, and represented by matrix multiplication and vector addition.
|
||||||
Scaling, rotation, reflection, shearing and translation are examples of affine transformation.
|
Scaling, rotation, reflection, shearing and translation are examples of affine transformation.
|
||||||
In this section, we don't use it.
|
In this section, we don't use it.
|
||||||
|
@ -37,8 +37,8 @@ Therefore, the coordinate in source and mask is the same as the coordinate in de
|
||||||
The instruction is as follows:
|
The instruction is as follows:
|
||||||
|
|
||||||
1. Create a surface.
|
1. Create a surface.
|
||||||
This will be a destnation.
|
This will be a destination.
|
||||||
2. Create a cairo context with the surface and set it to the destination.
|
2. Create a cairo context with the surface and the surface will be the destination of the context.
|
||||||
3. Create a source pattern within the context.
|
3. Create a source pattern within the context.
|
||||||
4. Create paths, which are lines, rectangles, arcs, texts or more complicated shapes, to generate a mask.
|
4. Create paths, which are lines, rectangles, arcs, texts or more complicated shapes, to generate a mask.
|
||||||
5. Use drawing operator such as `cairo_stroke` to transfer the paint in the source to the destination.
|
5. Use drawing operator such as `cairo_stroke` to transfer the paint in the source to the destination.
|
||||||
|
@ -80,31 +80,31 @@ Here's a simple example code that draws a small square and save it as a png file
|
||||||
31 }
|
31 }
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
- 1: Include the header file of cairo.
|
- 1: Includes the header file of cairo.
|
||||||
- 12: `cairo_image_surface_create` creates an image surface.
|
- 12: `cairo_image_surface_create` creates an image surface.
|
||||||
`CAIRO_FORMAT_RGB24` is a constant which means that each pixel has red, green and blue data.
|
`CAIRO_FORMAT_RGB24` is a constant which means that each pixel has red, green and blue data.
|
||||||
Each data has 8 bit quantity.
|
Each data has 8 bit quantity.
|
||||||
Modern displays have this type of color depth.
|
Modern displays have this type of color depth.
|
||||||
Width and hieight are pixels and given as integers.
|
Width and height are pixels and given as integers.
|
||||||
- 13: Create cairo context.
|
- 13: Creates cairo context.
|
||||||
The surface given as an argument will be set to the destination of the context.
|
The surface given as an argument will be the destination of the context.
|
||||||
- 17: `cairo_set_source_rgb` creates a source pattern, which is a solid white paint.
|
- 17: `cairo_set_source_rgb` creates a source pattern, which is a solid white paint.
|
||||||
The second to fourth argument is red, green and blue color depth respectively.
|
The second to fourth argument is red, green and blue color depth respectively.
|
||||||
Their type is float and the values are between zero and one.
|
Their type is float and the values are between zero and one.
|
||||||
(0,0,0) is black and (1,1,1) is white.
|
(0,0,0) is black and (1,1,1) is white.
|
||||||
- 18: `cairo_paint` copies everywhere in the source to destination.
|
- 18: `cairo_paint` copies everywhere in the source to destination.
|
||||||
The destination is filled with white pixels by this command.
|
The destination is filled with white pixels by this command.
|
||||||
- 20: Set the source color to black.
|
- 20: Sets the source color to black.
|
||||||
- 21: `cairo_set_line_width` set the width of lines.
|
- 21: `cairo_set_line_width` set the width of lines.
|
||||||
In this case, the line width is set to two pixels.
|
In this case, the line width is set to two pixels.
|
||||||
(It is because the transformation is identity.
|
(It is because the transformation is identity.
|
||||||
If we set it different one, for example scaling with the factor three, the actual width in destnation is six (2x3=6) pixels.)
|
If the transformation isn't identity, for example scaling with the factor three, the actual width in destination will be six (2x3=6) pixels.)
|
||||||
- 22: Draw a rectangle (square).
|
- 22: Draws a rectangle (square).
|
||||||
The top-left coordinate is (width/2.0-20.0, height/2.0-20.0) and the width and height have the same length 40.0.
|
The top-left coordinate is (width/2.0-20.0, height/2.0-20.0) and the width and height have the same length 40.0.
|
||||||
- 23: `cairo_stroke` transfer the source to destnation through the rectangle in mask.
|
- 23: `cairo_stroke` transfer the source to destination through the rectangle in mask.
|
||||||
- 26: Output the image to a png file `rectangle.png`.
|
- 26: Outputs the image to a png file `rectangle.png`.
|
||||||
- 27: Destroy the context. At the same time the source is destroyed.
|
- 27: Destroys the context. At the same time the source is destroyed.
|
||||||
- 28: Destroy the destnation surface.
|
- 28: Destroys the destination surface.
|
||||||
|
|
||||||
To compile this, type the following.
|
To compile this, type the following.
|
||||||
|
|
||||||
|
@ -164,10 +164,10 @@ The following is a very simple example.
|
||||||
The function `main` is almost same as before.
|
The function `main` is almost same as before.
|
||||||
The two functions `on_activate` and `draw_function` is important in this example.
|
The two functions `on_activate` and `draw_function` is important in this example.
|
||||||
|
|
||||||
- 16: Generate a GtkDrawingArea object.
|
- 16: Generates a GtkDrawingArea object.
|
||||||
- 20,21: Set the width and height of the contents of the GtkDrawingArea widget.
|
- 20,21: Sets the width and height of the contents of the GtkDrawingArea widget.
|
||||||
These width and height is the size of the destination surface of the cairo context provided by the widget.
|
These width and height is the size of the destination surface of the cairo context provided by the widget.
|
||||||
- 22: Set a drawng function to the widget.
|
- 22: Sets a drawing function of the widget.
|
||||||
GtkDrawingArea widget uses the function to draw the contents of itself whenever its necessary.
|
GtkDrawingArea widget uses the function to draw the contents of itself whenever its necessary.
|
||||||
For example, when a user drag a mouse pointer and resize a top level window, GtkDrawingArea also changes the size.
|
For example, when a user drag a mouse pointer and resize a top level window, GtkDrawingArea also changes the size.
|
||||||
Then, the whole window needs to be redrawn.
|
Then, the whole window needs to be redrawn.
|
||||||
|
@ -180,16 +180,16 @@ The drawing function has five parameters.
|
||||||
The first parameter is the GtkDrawingArea widget which calls the drawing function.
|
The first parameter is the GtkDrawingArea widget which calls the drawing function.
|
||||||
However, you can't change any properties, for example `content-width` or `content-height`, in this function.
|
However, you can't change any properties, for example `content-width` or `content-height`, in this function.
|
||||||
The second parameter is a cairo context given by the widget.
|
The second parameter is a cairo context given by the widget.
|
||||||
The destnation surface of the context is connected to the contents of the widget.
|
The destination surface of the context is connected to the contents of the widget.
|
||||||
What you draw to this surface will appear in the widget on the screen.
|
What you draw to this surface will appear in the widget on the screen.
|
||||||
The third and fourth paranmeters are the size of the destination surface.
|
The third and fourth parameters are the size of the destination surface.
|
||||||
|
|
||||||
- 3-11: The drawing function.
|
- 3-11: The drawing function.
|
||||||
- 4-5: Set the source to be white and paint the destination white.
|
- 4-5: Sets the source to be white and paint the destination white.
|
||||||
- 7: Set the line width to be 2.
|
- 7: Sets the line width to be 2.
|
||||||
- 8: Set the source to be black.
|
- 8: Sets the source to be black.
|
||||||
- 9: Add a rectangle to the mask.
|
- 9: Adds a rectangle to the mask.
|
||||||
- 10: Draw the rectangle with black color to the destination.
|
- 10: Draws the rectangle with black color to the destination.
|
||||||
|
|
||||||
Compile and run it, then a window with a black rectangle (square) appears.
|
Compile and run it, then a window with a black rectangle (square) appears.
|
||||||
Try resizing the window.
|
Try resizing the window.
|
||||||
|
|
10
gfm/sec2.md
10
gfm/sec2.md
|
@ -3,7 +3,7 @@ Up: [Readme.md](../Readme.md), Prev: [Section 1](sec1.md), Next: [Section 3](se
|
||||||
# Installation of gtk4 to linux distributions
|
# Installation of gtk4 to linux distributions
|
||||||
|
|
||||||
This section describes how to install gtk4 into linux distributions.
|
This section describes how to install gtk4 into linux distributions.
|
||||||
However, I only have an experience to install it to ubuntu 20.10.
|
However, I only have an experience to install it to Ubuntu 20.10.
|
||||||
Probably you need more than the explanation below.
|
Probably you need more than the explanation below.
|
||||||
|
|
||||||
This tutorial including this section is without any warranty.
|
This tutorial including this section is without any warranty.
|
||||||
|
@ -24,9 +24,9 @@ If you want to install it in the system area, `/opt/gtk4` is one of good choices
|
||||||
[Gtk4 API Reference](https://gnome.pages.gitlab.gnome.org/gtk/gtk/gtk-building.html) gives an installation example to `/opt/gtk4`.
|
[Gtk4 API Reference](https://gnome.pages.gitlab.gnome.org/gtk/gtk/gtk-building.html) gives an installation example to `/opt/gtk4`.
|
||||||
|
|
||||||
Don't install it to `/usr/local` which is the default.
|
Don't install it to `/usr/local` which is the default.
|
||||||
It is used by ubuntu applications, which are not build on gtk4.
|
It is used by Ubuntu applications, which are not build on gtk4.
|
||||||
Therefore, the risk is high and probably bad things will happen.
|
Therefore, the risk is high and probably bad things will happen.
|
||||||
Actually I did it and I needed to reinstall ubuntu.
|
Actually I did it and I needed to reinstall Ubuntu.
|
||||||
|
|
||||||
## Glib installation
|
## Glib installation
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ or
|
||||||
|
|
||||||
$ source env.sh
|
$ source env.sh
|
||||||
|
|
||||||
This command carries out the commands in `env.sh` and changes the environment variables above in the corrent shell.
|
This command carries out the commands in `env.sh` and changes the environment variables above in the current shell.
|
||||||
|
|
||||||
## Pango installation
|
## Pango installation
|
||||||
|
|
||||||
|
@ -100,7 +100,7 @@ It installs Pnago-1.0.gir under `$HOME/local/share/gir-1.0`.
|
||||||
If you installed pango without `--prefix` option, then it would be located at `/usr/local/share/gir-1.0`.
|
If you installed pango without `--prefix` option, then it would be located at `/usr/local/share/gir-1.0`.
|
||||||
This directory (/usr/local/share) is used by applications.
|
This directory (/usr/local/share) is used by applications.
|
||||||
They find the directory by the environment variable `XDG_DATA_DIRS`.
|
They find the directory by the environment variable `XDG_DATA_DIRS`.
|
||||||
It is a text file which keep the list of 'share' directoryes like `/usr/share`, `usr/local/share` and so on.
|
It is a text file which keep the list of 'share' directories like `/usr/share`, `usr/local/share` and so on.
|
||||||
Now `$HOME/local/share` needs to be added to `XDG_DATA_DIRS`, or error will occur in the later compilation.
|
Now `$HOME/local/share` needs to be added to `XDG_DATA_DIRS`, or error will occur in the later compilation.
|
||||||
|
|
||||||
$ export XDG_DATA_DIRS=$HOME/local/share:$XDG_DATA_DIRS
|
$ export XDG_DATA_DIRS=$HOME/local/share:$XDG_DATA_DIRS
|
||||||
|
|
36
gfm/sec20.md
36
gfm/sec20.md
|
@ -127,7 +127,7 @@ It has "name" attribute which is a signal name and "handler" attribute which is
|
||||||
Options "-WI, --export-dynamic" CFLAG is necessary when you compile the application.
|
Options "-WI, --export-dynamic" CFLAG is necessary when you compile the application.
|
||||||
You can achieve this by adding "export_dynamic: true" argument to executable function in `meson.build`.
|
You can achieve this by adding "export_dynamic: true" argument to executable function in `meson.build`.
|
||||||
And be careful that the handler must be defined without 'static' class.
|
And be careful that the handler must be defined without 'static' class.
|
||||||
- 54-76: Put GtkScrolledWindow and GtkDrawingArea into GtkBox.
|
- 54-76: Puts GtkScrolledWindow and GtkDrawingArea into GtkBox.
|
||||||
GtkBox has "homogeneous property with TRUE value, so the two children have the same width in the box.
|
GtkBox has "homogeneous property with TRUE value, so the two children have the same width in the box.
|
||||||
TfeTextView is a child of GtkScrolledWindow.
|
TfeTextView is a child of GtkScrolledWindow.
|
||||||
|
|
||||||
|
@ -149,17 +149,13 @@ First two files are almost same as before.
|
||||||
The only difference is the header file in tfettextview.c.
|
The only difference is the header file in tfettextview.c.
|
||||||
|
|
||||||
$ diff tfe5/tfetextview.c color/tfetextview.c
|
$ diff tfe5/tfetextview.c color/tfetextview.c
|
||||||
1c1
|
|
||||||
< #include "tfe.h"
|
|
||||||
---
|
|
||||||
> #include "color.h"
|
|
||||||
|
|
||||||
Color.h just includes tfetextview.h.
|
Color.h just includes tfetextview.h.
|
||||||
|
|
||||||
~~~C
|
~~~C
|
||||||
1 #include <gtk/gtk.h>
|
1 #include <gtk/gtk.h>
|
||||||
2
|
2
|
||||||
3 #include "tfetextview.h"
|
3 #include "../tfetextview/tfetextview.h"
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
# Colorapplication.c
|
# Colorapplication.c
|
||||||
|
@ -167,10 +163,10 @@ Color.h just includes tfetextview.h.
|
||||||
This is the main file.
|
This is the main file.
|
||||||
It deals with:
|
It deals with:
|
||||||
|
|
||||||
- Build widgets by GtkBuilder.
|
- Building widgets by GtkBuilder.
|
||||||
- Set drawing function to GtkDrawingArea.
|
- Seting a drawing function of GtkDrawingArea.
|
||||||
And connect a handler to "resize" signal on GtkDrawingArea.
|
And connecting a handler to "resize" signal on GtkDrawingArea.
|
||||||
- Implement each call back functions.
|
- Implementing each call back functions.
|
||||||
Particularly, `Run` signal handler is the point in this program.
|
Particularly, `Run` signal handler is the point in this program.
|
||||||
|
|
||||||
The following is `colorapplication.c`.
|
The following is `colorapplication.c`.
|
||||||
|
@ -304,18 +300,18 @@ The following is `colorapplication.c`.
|
||||||
The application ID is "com.github.ToshioCP.color".
|
The application ID is "com.github.ToshioCP.color".
|
||||||
`G_APPLICATION_FLAGS_NONE` is specified so no open signal handler is necessary.
|
`G_APPLICATION_FLAGS_NONE` is specified so no open signal handler is necessary.
|
||||||
- 86-106: Startup handler.
|
- 86-106: Startup handler.
|
||||||
- 91-96: Build widgets.
|
- 91-96: Builds widgets.
|
||||||
The pointers of the top window, TfeTextView and GtkDrawingArea objects are stored to static variables `win`, `tv` and `da` respectively.
|
The pointers of the top window, TfeTextView and GtkDrawingArea objects are stored to static variables `win`, `tv` and `da` respectively.
|
||||||
This is because these objects are often used in handlers.
|
This is because these objects are often used in handlers.
|
||||||
They never be rewritten so they're thread safe.
|
They never be rewritten so they're thread safe.
|
||||||
- 97: connect "resize" signal and the handler.
|
- 97: connects "resize" signal and the handler.
|
||||||
- 98: set the drawing function.
|
- 98: sets the drawing function.
|
||||||
- 81-84: Activate handler, which just show the widgets.
|
- 81-84: Activates handler, which just shows the widgets.
|
||||||
- 73-79: The drawing function.
|
- 73-79: The drawing function.
|
||||||
It just copy `surface` to destination.
|
It just copies `surface` to destination.
|
||||||
- 65-71: Resize handler.
|
- 65-71: Resize handler.
|
||||||
Re-create the surface to fit the width and height of the drawing area and paint by calling the function `run`.
|
Re-creates the surface to fit the width and height of the drawing area and paints by calling the function `run`.
|
||||||
- 58-63: Close handler.
|
- 58-63: Closes the handler.
|
||||||
It destroys `surface` if it exists.
|
It destroys `surface` if it exists.
|
||||||
Then it destroys the top window and quits the application.
|
Then it destroys the top window and quits the application.
|
||||||
- 48-56: Open and save handler.
|
- 48-56: Open and save handler.
|
||||||
|
@ -328,9 +324,9 @@ It is important to know that the drawing function is called when it is necessary
|
||||||
For example, when another window is moved and uncovers part of the widget, or when the window containing it is resized.
|
For example, when another window is moved and uncovers part of the widget, or when the window containing it is resized.
|
||||||
But repaint of `surface` is not automatically notified to gtk.
|
But repaint of `surface` is not automatically notified to gtk.
|
||||||
Therefore, you need to call `gtk_widget_queue_draw` to redraw the widget.
|
Therefore, you need to call `gtk_widget_queue_draw` to redraw the widget.
|
||||||
- 9-40: Run function paint the surface.
|
- 9-40: Run function paints the surface.
|
||||||
First, it gets the contents of GtkTextBuffer.
|
First, it gets the contents of GtkTextBuffer.
|
||||||
Then compare it to "red", "green" and so on.
|
Then it compares it to "red", "green" and so on.
|
||||||
If it matches the color, then the surface is painted the color.
|
If it matches the color, then the surface is painted the color.
|
||||||
If it matches "light" or "dark", then the color of the surface is lightened or darkened respectively.
|
If it matches "light" or "dark", then the color of the surface is lightened or darkened respectively.
|
||||||
Alpha channel is used.
|
Alpha channel is used.
|
||||||
|
@ -348,7 +344,7 @@ An argument "export_dynamic: true" is added to executable function.
|
||||||
5 gnome=import('gnome')
|
5 gnome=import('gnome')
|
||||||
6 resources = gnome.compile_resources('resources','color.gresource.xml')
|
6 resources = gnome.compile_resources('resources','color.gresource.xml')
|
||||||
7
|
7
|
||||||
8 sourcefiles=files('colorapplication.c', 'tfetextview.c')
|
8 sourcefiles=files('colorapplication.c', '../tfetextview/tfetextview.c')
|
||||||
9
|
9
|
||||||
10 executable('color', sourcefiles, resources, dependencies: gtkdep, export_dynamic: true)
|
10 executable('color', sourcefiles, resources, dependencies: gtkdep, export_dynamic: true)
|
||||||
~~~
|
~~~
|
||||||
|
|
|
@ -7,7 +7,7 @@ Up: [Readme.md](../Readme.md), Prev: [Section 2](sec2.md), Next: [Section 4](se
|
||||||
### GtkApplication and g\_application\_run
|
### GtkApplication and g\_application\_run
|
||||||
|
|
||||||
Usually people write a programming code to make an application.
|
Usually people write a programming code to make an application.
|
||||||
What are appications?
|
What are applications?
|
||||||
Applications are software that runs using libraries, which includes OS, frameworks and so on.
|
Applications are software that runs using libraries, which includes OS, frameworks and so on.
|
||||||
In Gtk4 programming, GtkApplication is an object that runs on GTK libraries.
|
In Gtk4 programming, GtkApplication is an object that runs on GTK libraries.
|
||||||
|
|
||||||
|
@ -202,7 +202,7 @@ Now rewrite the function `on_activate`.
|
||||||
8 }
|
8 }
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
Widget is an abstract concept that includes all the GUI interfaces such as windows, dialogs, buttons, multiline text, containers and so on.
|
Widget is an abstract concept that includes all the GUI interfaces such as windows, dialogs, buttons, multi-line text, containers and so on.
|
||||||
And GtkWidget is a base object from which all the GUI objects derive.
|
And GtkWidget is a base object from which all the GUI objects derive.
|
||||||
|
|
||||||
parent <-----> child
|
parent <-----> child
|
||||||
|
|
|
@ -141,9 +141,9 @@ The following program shows how to catch the signal and do something.
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
Look at the line 17 to 19.
|
Look at the line 17 to 19.
|
||||||
First, generate a GtkButton widget `btn` with a label "Click me".
|
First, it generates a GtkButton widget `btn` with a label "Click me".
|
||||||
Then, set it to the window `win` as a child.
|
Then, adds the button to the window `win` as a child.
|
||||||
Finally, connect a "clicked" signal of the button to a handler (function) `click_cb`.
|
Finally, connects a "clicked" signal of the button to a handler (function) `click_cb`.
|
||||||
So, if `btn` is clicked, the function `click_cb` is invoked.
|
So, if `btn` is clicked, the function `click_cb` is invoked.
|
||||||
The suffix cb means "call back".
|
The suffix cb means "call back".
|
||||||
|
|
||||||
|
@ -233,7 +233,7 @@ It arranges two or more child widgets into a single row or column.
|
||||||
The following procedure shows the way to add two buttons in a window.
|
The following procedure shows the way to add two buttons in a window.
|
||||||
|
|
||||||
- Generate GtkApplicationWindow.
|
- Generate GtkApplicationWindow.
|
||||||
- Generate GtkBox and set it a child of GtkApplicationWindow.
|
- Generate GtkBox and add it to GtkApplicationWindow as a child.
|
||||||
- Generate GtkButton and append it to GtkBox.
|
- Generate GtkButton and append it to GtkBox.
|
||||||
- Generate another GtkButton and append it to GtkBox.
|
- Generate another GtkButton and append it to GtkBox.
|
||||||
|
|
||||||
|
|
10
gfm/sec5.md
10
gfm/sec5.md
|
@ -6,7 +6,7 @@ Up: [Readme.md](../Readme.md), Prev: [Section 4](sec4.md), Next: [Section 6](se
|
||||||
|
|
||||||
### GtkTextView and GtkTextBuffer
|
### GtkTextView and GtkTextBuffer
|
||||||
|
|
||||||
GtkTextview is a widget for multiline text editing.
|
GtkTextview is a widget for multi-line text editing.
|
||||||
GtkTextBuffer is a text buffer which is connected to GtkTextView.
|
GtkTextBuffer is a text buffer which is connected to GtkTextView.
|
||||||
See a sample program `tfv1.c` below.
|
See a sample program `tfv1.c` below.
|
||||||
|
|
||||||
|
@ -66,9 +66,9 @@ In the next line, the pointer to the buffer is got and assigned to `tb`.
|
||||||
Then, the text from line 10 to 20 is assigned to the buffer.
|
Then, the text from line 10 to 20 is assigned to the buffer.
|
||||||
|
|
||||||
GtkTextView has a wrap mode.
|
GtkTextView has a wrap mode.
|
||||||
When `GTK_WRAP_WORD_CHAR` is set, text wraps in between words, or if that is not enough, also between graphemes.
|
When it is set to `GTK_WRAP_WORD_CHAR`, text wraps in between words, or if that is not enough, also between graphemes.
|
||||||
|
|
||||||
In line 30, `tv` is set to `win` as a child.
|
In line 30, `tv` is added to `win` as a child.
|
||||||
|
|
||||||
Now compile and run it.
|
Now compile and run it.
|
||||||
|
|
||||||
|
@ -86,8 +86,8 @@ You can solve it by putting GtkScrolledWindow between GtkApplicationWindow and G
|
||||||
|
|
||||||
What we need to do is:
|
What we need to do is:
|
||||||
|
|
||||||
- Generate GtkScrolledWindow and set it as a child of GtkApplicationWindow.
|
- Generate GtkScrolledWindow and insert it to GtkApplicationWindow as a child.
|
||||||
- Set GtkTextView as a child of GtkScrolledWindow.
|
- insert GtkTextView to GtkScrolledWindow as a child.
|
||||||
|
|
||||||
Modify `tfv1.c` and save it as `tfv2.c`.
|
Modify `tfv1.c` and save it as `tfv2.c`.
|
||||||
The difference between these two files is very little.
|
The difference between these two files is very little.
|
||||||
|
|
36
gfm/sec6.md
36
gfm/sec6.md
|
@ -15,7 +15,7 @@ When the program starts, we give a filename as an argument.
|
||||||
|
|
||||||
$ ./a.out filename
|
$ ./a.out filename
|
||||||
|
|
||||||
Then it opens the file and set it into GtkTextBuffer.
|
Then it opens the file and inserts it into GtkTextBuffer.
|
||||||
|
|
||||||
At the beginning of the implementation, we need to know how GtkApplication (or GApplication) recognizes arguments.
|
At the beginning of the implementation, we need to know how GtkApplication (or GApplication) recognizes arguments.
|
||||||
It is described in the GIO API reference.
|
It is described in the GIO API reference.
|
||||||
|
@ -89,10 +89,10 @@ The way how to read a file using GFiles will be described in the next section.
|
||||||
A file viewer is a program that shows a text file given as an argument.
|
A file viewer is a program that shows a text file given as an argument.
|
||||||
It works as follows.
|
It works as follows.
|
||||||
|
|
||||||
- If it is given arguments, it recognizes the first argument as a filename and open it.
|
- If it is given arguments, it recognizes the first argument as a filename and opens it.
|
||||||
- If opening the file succeeds, read and set it to GtkTextBuffer and show the window.
|
- If opening the file succeeds, it reads the contents of the file and inserts it to GtkTextBuffer and shows the window.
|
||||||
- If it fails to open the file, show an error message and quit.
|
- If it fails to open the file, shows an error message and quits.
|
||||||
- If there's no argument, show an error message and quit.
|
- If there's no argument, it shows an error message and quits.
|
||||||
- If there are two or more arguments, the second one and after are ignored.
|
- If there are two or more arguments, the second one and after are ignored.
|
||||||
|
|
||||||
The program is as follows.
|
The program is as follows.
|
||||||
|
@ -177,10 +177,10 @@ The application quits immediately because no window is generated.
|
||||||
The point is the handler `on_open`.
|
The point is the handler `on_open`.
|
||||||
|
|
||||||
- It generates GtkApplicationWindow, GtkScrolledWindow, GtkTextView and GtkTextBuffer and connects them.
|
- It generates GtkApplicationWindow, GtkScrolledWindow, GtkTextView and GtkTextBuffer and connects them.
|
||||||
- Set wrap mode to `GTK_WRAP_WORD_CHAR` in GtktextView.
|
- Sets wrap mode to `GTK_WRAP_WORD_CHAR` in GtktextView.
|
||||||
- Set non-editable to GtkTextView because the program isn't an editor but only a viewer.
|
- Sets GtkTextView to non-editable because the program isn't an editor but only a viewer.
|
||||||
- Read the file and set it to GtkTextBuffer (this will be explained in detail later).
|
- Reads the file and inserts the text into GtkTextBuffer (this will be explained in detail later).
|
||||||
- If the file is not opened then output an error message and destroy the window. It makes the application quit.
|
- If the file is not opened then outputs an error message and destroys the window. It makes the application quit.
|
||||||
|
|
||||||
The file reading part of the program is shown again below.
|
The file reading part of the program is shown again below.
|
||||||
|
|
||||||
|
@ -199,12 +199,12 @@ if (g_file_load_contents(files[0], NULL, &contents, &length, NULL, NULL)) {
|
||||||
}
|
}
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
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`.
|
The function `g_file_load_contents` loads the file contents into a buffer, which is automatically allocated, and sets `contents` to point the buffer.
|
||||||
And the length of the buffer is set to `length`.
|
And the length of the buffer is set to `length`.
|
||||||
It returns `TRUE` if the file's contents were successfully loaded. `FALSE` if there were errors.
|
It returns `TRUE` if the file's contents were successfully loaded. `FALSE` if there were errors.
|
||||||
|
|
||||||
If the function succeeds, set the contents into GtkTextBuffer, free the buffer memories pointed by `contents`, set the filename to the title of the window,
|
If the function succeeds, it inserts the contents into GtkTextBuffer, frees the buffer memories pointed by `contents`, sets the title of the window,
|
||||||
free the memories pointed by `filename` and show the window.
|
frees the memories pointed by `filename` and shows the window.
|
||||||
If it fails, it outputs an error message and destroys the window.
|
If it fails, it outputs an error message and destroys the window.
|
||||||
|
|
||||||
## GtkNotebook
|
## GtkNotebook
|
||||||
|
@ -300,21 +300,21 @@ Now I want to show you the program `tfv4.c`.
|
||||||
Most of the change is in the function `on_open`.
|
Most of the change is in the function `on_open`.
|
||||||
The numbers at the left of the following items are line numbers in the source code.
|
The numbers at the left of the following items are line numbers in the source code.
|
||||||
|
|
||||||
- 11-13: Variables `nb`, `lab` and `nbp` are defined and point GtkNotebook, GtkLabel and GtkNotebookPage respectively.
|
- 11-13: Variables `nb`, `lab` and `nbp` are defined and points GtkNotebook, GtkLabel and GtkNotebookPage respectively.
|
||||||
- 23: The window's title is set to "file viewer".
|
- 23: The window's title is set to "file viewer".
|
||||||
- 25: The size of the window is set to maximum because a big window is appropriate for file viewers.
|
- 25: The size of the window is set to maximum because a big window is appropriate for file viewers.
|
||||||
- 27-28 GtkNotebook is generated and set it as a child of the GtkApplicationWindow.
|
- 27-28 GtkNotebook is generated and inserted to the GtkApplicationWindow as a child.
|
||||||
- 30-52 For-loop. Each loop corresponds to an argument. And files[i] is GFile object with respect to the i-th argument.
|
- 30-52 For-loop. Each loop corresponds to an argument. And files[i] is GFile object with respect to the i-th argument.
|
||||||
- 32-37 GtkScrollledWindow, GtkTextView and GtkTextBuffer are generated and GtkTextView is connected to GtkScrolledWindow as a child.
|
- 32-37 GtkScrollledWindow, GtkTextView and GtkTextBuffer are generated and GtkTextView is connected to GtkScrolledWindow as a child.
|
||||||
They corresponds to each file, so they are generated inside the for-loop.
|
They corresponds to each file, so they are generated inside the for-loop.
|
||||||
- 39-42 Set the contents of the file into GtkTextBuffer and free the memory pointed by `contents`. Get the filename and generate GtkLabel with the filename.
|
- 39-42 inserts the contents of the file into GtkTextBuffer and frees the memory pointed by `contents`. Gets the filename and generates GtkLabel with the filename.
|
||||||
- 43: Append GtkScrolledWindow and GtkLabel to GtkNotebook. The appended objects are children of automatically generated GtkNotebookPage object. Therefore, the structure is like this:
|
- 43: Appends GtkScrolledWindow and GtkLabel to GtkNotebook. The appended objects are children of automatically generated GtkNotebookPage object. Therefore, the structure is like this:
|
||||||
|
|
||||||
GtkNotebook -- GtkNotebookPage -- (GtkScrolledWindow and GtkLabel)
|
GtkNotebook -- GtkNotebookPage -- (GtkScrolledWindow and GtkLabel)
|
||||||
|
|
||||||
- 44: Get GtkNotebookPage object and set its pointer to `nbp`.
|
- 44: Gets GtkNotebookPage object and sets `nbp` to point the GtkNotebookPage.
|
||||||
- 45: GtkNotebookPage has a property "tab-expand". If it is set to TRUE then the tab expand horizontally as long as possible. If FALSE, then the width of the tab is determined by the size of the label. `g_object_set` is a general function to set properties in any objects.
|
- 45: GtkNotebookPage has a property "tab-expand". If it is set to TRUE then the tab expand horizontally as long as possible. If FALSE, then the width of the tab is determined by the size of the label. `g_object_set` is a general function to set properties in any objects.
|
||||||
- 46: free the memory pointed by `filename`
|
- 46: frees the memory pointed by `filename`
|
||||||
- 53-56: If at least one file was read, then the number of GtkNotebookPage is greater than zero. If it's true, then show the window. If it's false, then destroy the window.
|
- 53-56: If at least one file was read, then the number of GtkNotebookPage is greater than zero. If it's true, then show the window. If it's false, then destroy the window.
|
||||||
|
|
||||||
|
|
||||||
|
|
41
gfm/sec7.md
41
gfm/sec7.md
|
@ -18,7 +18,7 @@ We just add two things to the file viewer.
|
||||||
A couple of ways are possible to get memories to keep GFile.
|
A couple of ways are possible to get memories to keep GFile.
|
||||||
|
|
||||||
- Use global variables.
|
- Use global variables.
|
||||||
- make a child widget object and extend the memories allocated to the widget.
|
- make a child object so that it can extend the memories for the GFile object.
|
||||||
|
|
||||||
Using global variables is easy to implement.
|
Using global variables is easy to implement.
|
||||||
Define a sufficient size array of pointers to GFile.
|
Define a sufficient size array of pointers to GFile.
|
||||||
|
@ -50,12 +50,11 @@ It has everything that GtkTextView has.
|
||||||
For example, TfeTextView has GtkTextbuffer corresponds to GtkTextView inside TfeTextView.
|
For example, TfeTextView has GtkTextbuffer corresponds to GtkTextView inside TfeTextView.
|
||||||
And important thing is that TfeTextView can have a memory to keep a pointer to GFile.
|
And important thing is that TfeTextView can have a memory to keep a pointer to GFile.
|
||||||
|
|
||||||
However, to understand the general theory about gobjects is very hard especially for beginners.
|
However, to understand the general theory about Gobject is very hard especially for beginners.
|
||||||
So, I will just show you the way how to write the code and avoid the theoretical side in the next subsection.
|
So, I will just show you the way how to write the code and avoid the theoretical side in the next subsection.
|
||||||
|
|
||||||
## How to define a child widget of GtkTextView
|
## How to define a child widget of GtkTextView
|
||||||
|
|
||||||
|
|
||||||
Let's define TfeTextView object which is a child object of GtkTextView.
|
Let's define TfeTextView object which is a child object of GtkTextView.
|
||||||
First, look at the program below.
|
First, look at the program below.
|
||||||
|
|
||||||
|
@ -97,7 +96,7 @@ tfe_text_view_new (void) {
|
||||||
|
|
||||||
If you are curious about the background theory of this program, It's very good for you.
|
If you are curious about the background theory of this program, It's very good for you.
|
||||||
Because knowing the theory is very important for you to program GTK applications.
|
Because knowing the theory is very important for you to program GTK applications.
|
||||||
Look at GObject API reference.
|
Look at [GObject API reference](https://developer.gnome.org/gobject/stable/).
|
||||||
All you need is described in it.
|
All you need is described in it.
|
||||||
However, it's a tough journey especially for beginners.
|
However, it's a tough journey especially for beginners.
|
||||||
For now, you don't need to know such difficult theory.
|
For now, you don't need to know such difficult theory.
|
||||||
|
@ -127,7 +126,7 @@ Usually you don't need to do anything.
|
||||||
- Define class init function (tfe\_text\_view\_class\_init).
|
- Define class init function (tfe\_text\_view\_class\_init).
|
||||||
You don't need to do anything in this widget.
|
You don't need to do anything in this widget.
|
||||||
- Write function codes you want to add (tfe\_text\_view\_set\_file and tfe\_text\_view\_get\_file).
|
- Write function codes you want to add (tfe\_text\_view\_set\_file and tfe\_text\_view\_get\_file).
|
||||||
`tv` is a pointer to TfeTextView object instance which is a C-struture declared with the tag \_TfeTextView.
|
`tv` is a pointer to TfeTextView object instance which is a C-structure declared with the tag \_TfeTextView.
|
||||||
So, the structure has a member `file` as a pointer to GFile.
|
So, the structure has a member `file` as a pointer to GFile.
|
||||||
`tv->file = f` is an assignment of `f` to a member `file` of the structure pointed by `tv`.
|
`tv->file = f` is an assignment of `f` to a member `file` of the structure pointed by `tv`.
|
||||||
This is an example how to use the extended memory in a child widget.
|
This is an example how to use the extended memory in a child widget.
|
||||||
|
@ -148,7 +147,14 @@ It will be modified later.
|
||||||
|
|
||||||
## Close-request signal
|
## Close-request signal
|
||||||
|
|
||||||
After editing a file, `tfe1.c` writes files just before the window closes.
|
Imagine that you use this editor.
|
||||||
|
First, you run the editor with arguments.
|
||||||
|
The arguments are filenames.
|
||||||
|
The editor reads the files and shows its window with the text of files in it.
|
||||||
|
Then you edit the text.
|
||||||
|
After you finish editing, you exit the editor.
|
||||||
|
The editor updates files just before the window closes.
|
||||||
|
|
||||||
GtkWindow emits "close-request" signal before it closes.
|
GtkWindow emits "close-request" signal before it closes.
|
||||||
We connect the signal and the handler `before_close`.
|
We connect the signal and the handler `before_close`.
|
||||||
A handler is a C function.
|
A handler is a C function.
|
||||||
|
@ -159,9 +165,9 @@ The function `before_close` is invoked when the signal "close-request" is emitte
|
||||||
g_signal_connect (win, "close-request", G_CALLBACK (before_close), NULL);
|
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.
|
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.
|
`G_CALLBACK` cast is necessary for the handler.
|
||||||
The program of before\_close is as follows.
|
The program of `before_close` is as follows.
|
||||||
|
|
||||||
~~~C
|
~~~C
|
||||||
1 static gboolean
|
1 static gboolean
|
||||||
|
@ -193,11 +199,14 @@ The program of before\_close is as follows.
|
||||||
|
|
||||||
The numbers on the left of items are line numbers in the source code.
|
The numbers on the left of items are line numbers in the source code.
|
||||||
|
|
||||||
- 13: Get the number of pages `nb` has.
|
- 13: Gets the number of pages `nb` has.
|
||||||
- 14-23: For loop with regard to the index to each pages.
|
- 14-23: For loop with regard to the index to each pages.
|
||||||
- 15-17: Get GtkScrolledWindow, TfeTextView and a pointer to GFile. The pointer was stored when `on_open` handler had run. It will be shown later.
|
- 15-17: Gets GtkScrolledWindow, TfeTextView and a pointer to GFile.
|
||||||
- 18-20: Get GtkTextBuffer and contents. start\_iter and end\_iter is iterators of the buffer. I don't want to explain them now because it would take a lot of time. Just remember these lines for the present.
|
The pointer was stored when `on_open` handler had run. It will be shown later.
|
||||||
- 21: Write the file.
|
- 18-20: Gets GtkTextBuffer and contents. `start_iter` and `end_iter` are iterators of the buffer.
|
||||||
|
I don't want to explain them now because it would take a lot of time.
|
||||||
|
Just remember these lines for the present.
|
||||||
|
- 21: Writes the file.
|
||||||
|
|
||||||
## Source code of tfe1.c
|
## Source code of tfe1.c
|
||||||
|
|
||||||
|
@ -272,7 +281,7 @@ Now I will show you all the source code of `tfe1`.c.
|
||||||
66
|
66
|
||||||
67 static void
|
67 static void
|
||||||
68 on_activate (GApplication *app, gpointer user_data) {
|
68 on_activate (GApplication *app, gpointer user_data) {
|
||||||
69 g_print ("You need a filename argument.\n");
|
69 g_print ("You need to give filenames as arguments.\n");
|
||||||
70 }
|
70 }
|
||||||
71
|
71
|
||||||
72 static void
|
72 static void
|
||||||
|
@ -342,11 +351,11 @@ Now I will show you all the source code of `tfe1`.c.
|
||||||
136
|
136
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
- 102: set the pointer to GFile into TfeTextView.
|
- 102: Sets the pointer to GFile into TfeTextView.
|
||||||
`files[i]` is a pointer to GFile structure.
|
`files[i]` is a pointer to GFile structure.
|
||||||
It will be freed by the system. So you need to copy it.
|
It will be freed by the system. So you need to copy it.
|
||||||
`g_file_dup` duplicate the given GFile structure.
|
`g_file_dup` duplicates the given GFile structure.
|
||||||
- 118: connect "close-request" signal and `before_close` handler.
|
- 118: Connects "close-request" signal and `before_close` handler.
|
||||||
The fourth argument is called user data and it is given to the signal handler.
|
The fourth argument is called user data and it is given to the signal handler.
|
||||||
So, `nb` is given to `before_close` as the second argument.
|
So, `nb` is given to `before_close` as the second argument.
|
||||||
|
|
||||||
|
|
47
gfm/sec8.md
47
gfm/sec8.md
|
@ -5,7 +5,7 @@ Up: [Readme.md](../Readme.md), Prev: [Section 7](sec7.md), Next: [Section 9](se
|
||||||
## New, open and save button
|
## New, open and save button
|
||||||
|
|
||||||
We made the simplest editor in the previous section.
|
We made the simplest editor in the previous section.
|
||||||
It reads the files in `on_open` function at start-up and writes it when closing the window.
|
It reads the files in `on_open` function at start-up and writes them when closing the window.
|
||||||
It works but is not good.
|
It works but is not good.
|
||||||
It is better to make "New", "Open", "Save" and "Close" buttons.
|
It is better to make "New", "Open", "Save" and "Close" buttons.
|
||||||
This section describes how to put those buttons into the window.
|
This section describes how to put those buttons into the window.
|
||||||
|
@ -107,21 +107,21 @@ The function `on_open` in the source code `tfe2.c` is as follows.
|
||||||
|
|
||||||
The point is how to build the window.
|
The point is how to build the window.
|
||||||
|
|
||||||
- 25-27: Generate GtkApplicationWindow and set its title and default size.
|
- 25-27: Generates GtkApplicationWindow and sets the title and default size.
|
||||||
- 29-30: Generate GtkBox `boxv`.
|
- 29-30: Generates GtkBox `boxv`.
|
||||||
It is a vertical box and a child of GtkApplicationWindow.
|
It is a vertical box and a child of GtkApplicationWindow.
|
||||||
It has two children.
|
It has two children.
|
||||||
The first child is a horizontal box includes buttons.
|
The first child is a horizontal box includes buttons.
|
||||||
The second child is GtkNotebook.
|
The second child is GtkNotebook.
|
||||||
- 32-33: Generate GtkBox `boxh` and append it to 'boxv' as a first child.
|
- 32-33: Generates GtkBox `boxh` and appends it to 'boxv' as a first child.
|
||||||
- 35-40: Generate three dummy labels.
|
- 35-40: Generates three dummy labels.
|
||||||
The labels `dmy1` and `dmy3` has a character width of ten.
|
The labels `dmy1` and `dmy3` has a character width of ten.
|
||||||
The other label `dmy2` is set hexpand property TRUE.
|
The other label `dmy2` has hexpand property which is set to be TRUE.
|
||||||
This makes the label expands horizontally as long as possible.
|
This makes the label expands horizontally as long as possible.
|
||||||
- 41-44: Generate four buttons.
|
- 41-44: Generates four buttons.
|
||||||
- 46-52: Append these GtkLabel and GtkButton to `boxh`.
|
- 46-52: Appends these GtkLabel and GtkButton to `boxh`.
|
||||||
- 54-57: Generate GtkNotebook and set hexpand and vexpand properties TRUE.
|
- 54-57: Generates GtkNotebook and sets hexpand and vexpand properties TRUE.
|
||||||
This makes it expands horizontally and vertically as big as possible.
|
This makes it expand horizontally and vertically as big as possible.
|
||||||
It is appended to `boxv` as the second child.
|
It is appended to `boxv` as the second child.
|
||||||
|
|
||||||
The number of lines is 33(=57-25+1) to build the widgets.
|
The number of lines is 33(=57-25+1) to build the widgets.
|
||||||
|
@ -131,7 +131,7 @@ Are there any good solution to reduce these work?
|
||||||
|
|
||||||
Gtk provides GtkBuilder.
|
Gtk provides GtkBuilder.
|
||||||
It reads ui data and builds a window.
|
It reads ui data and builds a window.
|
||||||
It reduces the cumbersom work.
|
It reduces the cumbersome work.
|
||||||
|
|
||||||
## Ui file
|
## Ui file
|
||||||
|
|
||||||
|
@ -200,7 +200,8 @@ First, let's look at the ui file `tfe3.ui` that defines a structure of the widge
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
This is coded with XML structure.
|
This is coded with XML structure.
|
||||||
Constructs beginning with `<` and ending with `>` are called tags, and are divided into two parts, start tag and end tag.
|
Constructs beginning with `<` and ending with `>` are called tags.
|
||||||
|
And there are two types of tags, start tag and end tag.
|
||||||
For example, `<interface>` is a start tag and `</interface>` is an end tag.
|
For example, `<interface>` is a start tag and `</interface>` is an end tag.
|
||||||
Ui file begins and ends with interface tags.
|
Ui file begins and ends with interface tags.
|
||||||
Some tags, for example, object tags can have a class and id attributes inside the start tag.
|
Some tags, for example, object tags can have a class and id attributes inside the start tag.
|
||||||
|
@ -213,7 +214,7 @@ And the three properties of the window are defined.
|
||||||
For example, line 7 tells us that GtkBox object which id is "boxv" is a child of `win`.
|
For example, line 7 tells us that GtkBox object which id is "boxv" is a child of `win`.
|
||||||
|
|
||||||
Compare this ui file and the lines 25-57 in the source code of `on_open` function.
|
Compare this ui file and the lines 25-57 in the source code of `on_open` function.
|
||||||
Those two decribe the same structure of widgets.
|
Those two describe the same structure of widgets.
|
||||||
|
|
||||||
## GtkBuilder
|
## GtkBuilder
|
||||||
|
|
||||||
|
@ -228,7 +229,8 @@ gtk_window_set_application (GTK_WINDOW (win), GTK_APPLICATION (app));
|
||||||
nb = GTK_WIDGET (gtk_builder_get_object (build, "nb"));
|
nb = GTK_WIDGET (gtk_builder_get_object (build, "nb"));
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
The function `gtk_builder_new_from_file` reads the file given as an argument, build the widgets, generate GtkBuilder object and set pointers to the widgets in it.
|
The function `gtk_builder_new_from_file` reads the file given as an argument.
|
||||||
|
Then, it builds the widgets and pointers to them, creates GtkBuilder object and put the pointers in it.
|
||||||
The function `gtk_builder_get_object (build, "win")` returns the pointer to the widget `win`, which is the id in the ui file.
|
The function `gtk_builder_get_object (build, "win")` returns the pointer to the widget `win`, which is the id in the ui file.
|
||||||
All the widgets are connected based on the parent-children relationship described in the ui file.
|
All the widgets are connected based on the parent-children relationship described in the ui file.
|
||||||
We only need `win` and `nb` for the program after this, so we don't need to take out any other widgets.
|
We only need `win` and `nb` for the program after this, so we don't need to take out any other widgets.
|
||||||
|
@ -297,8 +299,7 @@ This reduces lines in the C source file.
|
||||||
Therefore 37 lines are reduced.
|
Therefore 37 lines are reduced.
|
||||||
Using ui file not only shortens C source files, but also makes the widgets' structure clear.
|
Using ui file not only shortens C source files, but also makes the widgets' structure clear.
|
||||||
|
|
||||||
Now I'll show you the C source code `tfe3.c`.
|
Now I'll show you `on_open` function in the C source code `tfe3.c`.
|
||||||
Only functions `on_open` are shown as follows.
|
|
||||||
|
|
||||||
~~~C
|
~~~C
|
||||||
1 static void
|
1 static void
|
||||||
|
@ -351,9 +352,9 @@ Only functions `on_open` are shown as follows.
|
||||||
48 }
|
48 }
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
The source code of `tfe3.c` is stored in [src/tfe](https://github.com/ToshioCP/Gtk4-tutorial/tree/main/src/tfe) directory.
|
The whole source code of `tfe3.c` is stored in [src/tfe](https://github.com/ToshioCP/Gtk4-tutorial/tree/main/src/tfe) directory.
|
||||||
If you want to see it, click the link above.
|
If you want to see it, click the link above.
|
||||||
In the same way, you can get the source files below in the directory [src/tfe](https://github.com/ToshioCP/Gtk4-tutorial/tree/main/src/tfe).
|
You can also get the source files below.
|
||||||
|
|
||||||
### Using ui string
|
### Using ui string
|
||||||
|
|
||||||
|
@ -385,8 +386,8 @@ So ui file is not necessary on runtime.
|
||||||
The disadvantage is that writing C string is a bit bothersome because of the double quotes.
|
The disadvantage is that writing C string is a bit bothersome because of the double quotes.
|
||||||
If you want to use this method, you should write a script that transforms ui file into C-string.
|
If you want to use this method, you should write a script that transforms ui file into C-string.
|
||||||
|
|
||||||
- add backslash before each double quote.
|
- Add backslash before each double quote.
|
||||||
- add double quote at the left and right.
|
- Add double quote at the left and right.
|
||||||
|
|
||||||
### Using Gresource
|
### Using Gresource
|
||||||
|
|
||||||
|
@ -408,10 +409,10 @@ It describes resource files.
|
||||||
6 </gresources>
|
6 </gresources>
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
- 2: gresources tag can include mulitple gresources (gresource tags).
|
- 2: `gresources` tag can include multiple gresources (gresource tags).
|
||||||
However, this xml has only one gresource.
|
However, this xml has only one gresource.
|
||||||
- 3: The gresource has a prefix `/com/github/ToshioCP/tfe3`.
|
- 3: The gresource has a prefix `/com/github/ToshioCP/tfe3`.
|
||||||
- 4: The gresource has tfe3.ui.
|
- 4: The gresource has `tfe3.ui`.
|
||||||
And it is pointed by `/com/github/ToshioCP/tfe3/tfe3.ui` because it needs prefix.
|
And it is pointed by `/com/github/ToshioCP/tfe3/tfe3.ui` because it needs prefix.
|
||||||
If you want to add more files, then insert them between line 4 and 5.
|
If you want to add more files, then insert them between line 4 and 5.
|
||||||
|
|
||||||
|
@ -450,7 +451,7 @@ Now run the compiler.
|
||||||
$ glib-compile-resources tfe3.gresource.xml --target=resources.c --generate-source
|
$ glib-compile-resources tfe3.gresource.xml --target=resources.c --generate-source
|
||||||
|
|
||||||
Then a C source file `resources.c` is generated.
|
Then a C source file `resources.c` is generated.
|
||||||
Modify tfe3.c and save it as tfe3_r.c
|
Modify `tfe3.c` and save it as `tfe3_r.c`.
|
||||||
|
|
||||||
~~~C
|
~~~C
|
||||||
#include "resources.c"
|
#include "resources.c"
|
||||||
|
|
52
gfm/sec9.md
52
gfm/sec9.md
|
@ -4,13 +4,13 @@ Up: [Readme.md](../Readme.md), Prev: [Section 8](sec8.md), Next: [Section 10](s
|
||||||
|
|
||||||
## What do we need to think about to manage big source files?
|
## What do we need to think about to manage big source files?
|
||||||
|
|
||||||
We've managed to compile a small editor so far.
|
We've compiled a small editor so far.
|
||||||
But Some bad signs are beginning to appear.
|
But Some bad signs are beginning to appear.
|
||||||
|
|
||||||
- We have only one C source file and put everything into it.
|
- We've had only one C source file and put everything into it.
|
||||||
We need to sort it out.
|
We need to sort it out.
|
||||||
- There are two compilers, `gcc` and `glib-compile-resources`.
|
- There are two compilers, `gcc` and `glib-compile-resources`.
|
||||||
We want to control them by one building tool.
|
We should control them by one building tool.
|
||||||
|
|
||||||
These ideas are useful to manage big source files.
|
These ideas are useful to manage big source files.
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ It is a good idea to separate them into two files, `tfetextview.c` and `tfe.c`.
|
||||||
Now we have three source files, `tfetextview.c`, `tfe.c` and `tfe3.ui`.
|
Now we have three source files, `tfetextview.c`, `tfe.c` and `tfe3.ui`.
|
||||||
The `3` of `tfe3.ui` is like a version number.
|
The `3` of `tfe3.ui` is like a version number.
|
||||||
Managing version with filenames is one possible idea but it may make bothersome problem.
|
Managing version with filenames is one possible idea but it may make bothersome problem.
|
||||||
You need to rewrite filename in each version and it affects to contents of sourcefiles that refer to filenames.
|
You need to rewrite filename in each version and it affects to contents of source files that refer to filenames.
|
||||||
So, we should take `3` away from the filename.
|
So, we should take `3` away from the filename.
|
||||||
|
|
||||||
In `tfe.c` the function `tfe_text_view_new` is invoked to generate TfeTextView.
|
In `tfe.c` the function `tfe_text_view_new` is invoked to generate TfeTextView.
|
||||||
|
@ -254,16 +254,16 @@ Dividing a file makes it easy to maintain source files.
|
||||||
But now we are faced with a new problem.
|
But now we are faced with a new problem.
|
||||||
The building step increases.
|
The building step increases.
|
||||||
|
|
||||||
- Compile the ui file `tfe.ui` into `resources.c`.
|
- Compiling the ui file `tfe.ui` into `resources.c`.
|
||||||
- Compile `tfe.c` into `tfe.o` (object file).
|
- Compiling `tfe.c` into `tfe.o` (object file).
|
||||||
- Compile `tfetextview.c` into `tfetextview.o`.
|
- Compiling `tfetextview.c` into `tfetextview.o`.
|
||||||
- Compile `resources.c` into `resources.o`.
|
- Compiling `resources.c` into `resources.o`.
|
||||||
- Link all the object files into application `tfe`.
|
- Linking all the object files into application `tfe`.
|
||||||
|
|
||||||
Now build tool is necessary to manage it.
|
Now build tool is necessary to manage it.
|
||||||
Make is one of the build tools.
|
Make is one of the build tools.
|
||||||
It was originally created in 1976.
|
It was created in 1976.
|
||||||
So it is an old and widely used program.
|
It is an old and widely used program.
|
||||||
|
|
||||||
Make analyzes Makefile and executes compilers.
|
Make analyzes Makefile and executes compilers.
|
||||||
All instructions are written in Makefile.
|
All instructions are written in Makefile.
|
||||||
|
@ -286,7 +286,7 @@ The rule is:
|
||||||
If a prerequisite modified later than a target, then make executes the recipe.
|
If a prerequisite modified later than a target, then make executes the recipe.
|
||||||
|
|
||||||
In the example above, if `sample.c` is modified after the generation of `sample.o`, then make executes gcc and compile `sample.c` into `sample.o`.
|
In the example above, if `sample.c` is modified after the generation of `sample.o`, then make executes gcc and compile `sample.c` into `sample.o`.
|
||||||
If the modification time of `sample.c` is older then the generation of `sample.o`, then no compiling is necesarry, so make does nothing.
|
If the modification time of `sample.c` is older then the generation of `sample.o`, then no compiling is necessary, so make does nothing.
|
||||||
|
|
||||||
The Makefile for `tfe` is as follows.
|
The Makefile for `tfe` is as follows.
|
||||||
|
|
||||||
|
@ -368,19 +368,19 @@ Rake has task and file task, which is similar to target, prerequisite and recipe
|
||||||
|
|
||||||
What `Rakefile` describes is almost same as `Makefile` in the previous subsection.
|
What `Rakefile` describes is almost same as `Makefile` in the previous subsection.
|
||||||
|
|
||||||
- 3-6: define target file, source file and so on.
|
- 3-6: Defines target file, source file and so on.
|
||||||
- 1, 8: Load clean library. And define CLEAN file list.
|
- 1, 8: Loads clean library. And defines CLEAN file list.
|
||||||
The files included by CLEAN will be removed when `rake clean` is typed on the command line.
|
The files included by CLEAN will be removed when `rake clean` is typed on the command line.
|
||||||
- 10: default target depends on targetfile.
|
- 10: Default target depends on targetfile.
|
||||||
default is the final goal of tasks.
|
Default is the final goal of tasks.
|
||||||
- 12-14: targetfile depends on objfiles.
|
- 12-14: Targetfile depends on objfiles.
|
||||||
The variable `t` is a task object.
|
The variable `t` is a task object.
|
||||||
- t.name is a target name
|
- t.name is a target name
|
||||||
- t.prerequisites is an array of prerequisits.
|
- t.prerequisites is an array of prerequisites.
|
||||||
- t.source is the first element of prerequisites.
|
- t.source is the first element of prerequisites.
|
||||||
- sh is a method to give the following string to shell as an argument and execute the shell.
|
- sh is a method to give the following string to shell as an argument and execute the shell.
|
||||||
- 16-21: Loop by each element of the array of objfiles. Each object depends on corresponding source file.
|
- 16-21: There's a loop by each element of the array of objfiles. Each object depends on corresponding source file.
|
||||||
- 23-25: resouce file depends on xml file and ui file.
|
- 23-25: Resource file depends on xml file and ui file.
|
||||||
|
|
||||||
Rakefile might seem to be difficult for beginners.
|
Rakefile might seem to be difficult for beginners.
|
||||||
But, you can use any ruby syntax in Rakefile, so it is really flexible.
|
But, you can use any ruby syntax in Rakefile, so it is really flexible.
|
||||||
|
@ -410,17 +410,17 @@ To use meson, you first need to write `meson.build` file.
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
- 1: The function `project` defines things about the project.
|
- 1: The function `project` defines things about the project.
|
||||||
The first parameter is the name of the project and the second is the programing language.
|
The first parameter is the name of the project and the second is the programming language.
|
||||||
- 2: `dependency` function defines a dependency that is taken by `pkg-config`.
|
- 2: `dependency` function defines a dependency that is taken by `pkg-config`.
|
||||||
We put `gtk4` as an argument.
|
We put `gtk4` as an argument.
|
||||||
- 5: `import` function inports a module.
|
- 5: `import` function imports a module.
|
||||||
In line 5, gnome module is imported and assignd to the variable `gnome`.
|
In line 5, gnome module is imported and assigned to the variable `gnome`.
|
||||||
gnome module provides helper tools to build GTK programs.
|
gnome module provides helper tools to build GTK programs.
|
||||||
- 6: `.compile_resources` is a method of gnome module and compile files to resources under the instruction of xml file.
|
- 6: `.compile_resources` is a method of gnome module and compile files to resources under the instruction of xml file.
|
||||||
In line 6, the resource filename is `resources`, which means `resources.c` and `resources.h`, and xml file is `tfe.gresource.xml`.
|
In line 6, the resource filename is `resources`, which means `resources.c` and `resources.h`, and xml file is `tfe.gresource.xml`.
|
||||||
This method generates C source file by default.
|
This method generates C source file by default.
|
||||||
- 8: define source files.
|
- 8: Defines source files.
|
||||||
- 10: executable function generates a target file by building source files.
|
- 10: Executable function generates a target file by building source files.
|
||||||
The first parameter is the filename of the target. The following parameters are source files.
|
The first parameter is the filename of the target. The following parameters are source files.
|
||||||
The last parameter has a option `dependencies`.
|
The last parameter has a option `dependencies`.
|
||||||
In line 10 it is `gtkdep` which is defined in line 3.
|
In line 10 it is `gtkdep` which is defined in line 3.
|
||||||
|
@ -435,6 +435,8 @@ Then, the executable file `tfe` is generated under the directory `_build`.
|
||||||
$ _build/tfe tfe.c tfetextview.c
|
$ _build/tfe tfe.c tfetextview.c
|
||||||
|
|
||||||
Then the window appears.
|
Then the window appears.
|
||||||
|
And two notebook pages are in the window.
|
||||||
|
One notebook is `tfe.c` and the other is `tfetextview.c`.
|
||||||
|
|
||||||
I've shown you three build tools.
|
I've shown you three build tools.
|
||||||
I think meson and ninja is the best choice for the present.
|
I think meson and ninja is the best choice for the present.
|
||||||
|
|
|
@ -152,7 +152,7 @@ def change_rel_link line, org_dir, new_dir
|
||||||
left = ""
|
left = ""
|
||||||
right = line
|
right = line
|
||||||
while right =~ /(!?\[[^\]]*\])\(([^\)]*)\)/
|
while right =~ /(!?\[[^\]]*\])\(([^\)]*)\)/
|
||||||
left = $`
|
left += $`
|
||||||
right = $'
|
right = $'
|
||||||
name = $1
|
name = $1
|
||||||
link = $2
|
link = $2
|
||||||
|
@ -168,7 +168,7 @@ def change_rel_link line, org_dir, new_dir
|
||||||
end
|
end
|
||||||
|
|
||||||
def fold line, width
|
def fold line, width
|
||||||
if width <=0
|
if width <= 0
|
||||||
return line
|
return line
|
||||||
end
|
end
|
||||||
tmp = []
|
tmp = []
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
#include "tfetextview.h"
|
#include "../tfetextview/tfetextview.h"
|
||||||
|
|
|
@ -5,6 +5,6 @@ gtkdep = dependency('gtk4')
|
||||||
gnome=import('gnome')
|
gnome=import('gnome')
|
||||||
resources = gnome.compile_resources('resources','color.gresource.xml')
|
resources = gnome.compile_resources('resources','color.gresource.xml')
|
||||||
|
|
||||||
sourcefiles=files('colorapplication.c', 'tfetextview.c')
|
sourcefiles=files('colorapplication.c', '../tfetextview/tfetextview.c')
|
||||||
|
|
||||||
executable('color', sourcefiles, resources, dependencies: gtkdep, export_dynamic: true)
|
executable('color', sourcefiles, resources, dependencies: gtkdep, export_dynamic: true)
|
||||||
|
|
|
@ -1,218 +0,0 @@
|
||||||
#include "color.h"
|
|
||||||
|
|
||||||
struct _TfeTextView
|
|
||||||
{
|
|
||||||
GtkTextView parent;
|
|
||||||
GFile *file;
|
|
||||||
};
|
|
||||||
|
|
||||||
G_DEFINE_TYPE (TfeTextView, tfe_text_view, GTK_TYPE_TEXT_VIEW);
|
|
||||||
|
|
||||||
enum {
|
|
||||||
CHANGE_FILE,
|
|
||||||
OPEN_RESPONSE,
|
|
||||||
NUMBER_OF_SIGNALS
|
|
||||||
};
|
|
||||||
|
|
||||||
static guint tfe_text_view_signals[NUMBER_OF_SIGNALS];
|
|
||||||
|
|
||||||
static void
|
|
||||||
tfe_text_view_dispose (GObject *gobject) {
|
|
||||||
TfeTextView *tv = TFE_TEXT_VIEW (gobject);
|
|
||||||
|
|
||||||
if (G_IS_FILE (tv->file))
|
|
||||||
g_clear_object (&tv->file);
|
|
||||||
|
|
||||||
G_OBJECT_CLASS (tfe_text_view_parent_class)->dispose (gobject);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
tfe_text_view_init (TfeTextView *tv) {
|
|
||||||
GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
|
||||||
|
|
||||||
tv->file = NULL;
|
|
||||||
gtk_text_buffer_set_modified (tb, FALSE);
|
|
||||||
gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
tfe_text_view_class_init (TfeTextViewClass *class) {
|
|
||||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
|
||||||
|
|
||||||
object_class->dispose = tfe_text_view_dispose;
|
|
||||||
tfe_text_view_signals[CHANGE_FILE] = g_signal_newv ("change-file",
|
|
||||||
G_TYPE_FROM_CLASS (class),
|
|
||||||
G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
|
|
||||||
NULL /* closure */,
|
|
||||||
NULL /* accumulator */,
|
|
||||||
NULL /* accumulator data */,
|
|
||||||
NULL /* C marshaller */,
|
|
||||||
G_TYPE_NONE /* return_type */,
|
|
||||||
0 /* n_params */,
|
|
||||||
NULL /* param_types */);
|
|
||||||
GType param_types[] = {G_TYPE_INT};
|
|
||||||
tfe_text_view_signals[OPEN_RESPONSE] = g_signal_newv ("open-response",
|
|
||||||
G_TYPE_FROM_CLASS (class),
|
|
||||||
G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
|
|
||||||
NULL /* closure */,
|
|
||||||
NULL /* accumulator */,
|
|
||||||
NULL /* accumulator data */,
|
|
||||||
NULL /* C marshaller */,
|
|
||||||
G_TYPE_NONE /* return_type */,
|
|
||||||
1 /* n_params */,
|
|
||||||
param_types);
|
|
||||||
}
|
|
||||||
|
|
||||||
GFile *
|
|
||||||
tfe_text_view_get_file (TfeTextView *tv) {
|
|
||||||
g_return_val_if_fail (TFE_IS_TEXT_VIEW (tv), NULL);
|
|
||||||
|
|
||||||
return g_file_dup (tv->file);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
GtkWidget *message_dialog;
|
|
||||||
GError *err = NULL;
|
|
||||||
|
|
||||||
if (response != GTK_RESPONSE_ACCEPT)
|
|
||||||
g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_CANCEL);
|
|
||||||
else if (! G_IS_FILE (file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog))))
|
|
||||||
g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_ERROR);
|
|
||||||
else if (! g_file_load_contents (file, NULL, &contents, &length, NULL, &err)) { /* read error */
|
|
||||||
if (G_IS_FILE (file))
|
|
||||||
g_object_unref (file);
|
|
||||||
message_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog), GTK_DIALOG_MODAL,
|
|
||||||
GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
|
|
||||||
"%s.\n", err->message);
|
|
||||||
g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
|
|
||||||
gtk_widget_show (message_dialog);
|
|
||||||
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 (tb, contents, length);
|
|
||||||
g_free (contents);
|
|
||||||
if (G_IS_FILE (tv->file))
|
|
||||||
g_object_unref (tv->file);
|
|
||||||
tv->file = file;
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
tfe_text_view_open (TfeTextView *tv, GtkWidget *win) {
|
|
||||||
g_return_if_fail (TFE_IS_TEXT_VIEW (tv));
|
|
||||||
g_return_if_fail (GTK_IS_WINDOW (win));
|
|
||||||
|
|
||||||
GtkWidget *dialog;
|
|
||||||
|
|
||||||
dialog = gtk_file_chooser_dialog_new ("Open file", GTK_WINDOW (win), GTK_FILE_CHOOSER_ACTION_OPEN,
|
|
||||||
"Cancel", GTK_RESPONSE_CANCEL,
|
|
||||||
"Open", GTK_RESPONSE_ACCEPT,
|
|
||||||
NULL);
|
|
||||||
g_signal_connect (dialog, "response", G_CALLBACK (open_dialog_response), tv);
|
|
||||||
gtk_widget_show (dialog);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
gtk_window_destroy (GTK_WINDOW (dialog));
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
GtkWidget *message_dialog;
|
|
||||||
GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW);
|
|
||||||
GError *err = NULL;
|
|
||||||
|
|
||||||
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 (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))
|
|
||||||
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. */
|
|
||||||
if (G_IS_FILE (tv->file))
|
|
||||||
g_object_unref (tv->file);
|
|
||||||
tv->file =NULL;
|
|
||||||
g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
|
|
||||||
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);
|
|
||||||
g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
|
|
||||||
gtk_widget_show (message_dialog);
|
|
||||||
g_error_free (err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
tfe_text_view_saveas (TfeTextView *tv) {
|
|
||||||
g_return_if_fail (TFE_IS_TEXT_VIEW (tv));
|
|
||||||
|
|
||||||
GtkWidget *dialog;
|
|
||||||
GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW);
|
|
||||||
|
|
||||||
dialog = gtk_file_chooser_dialog_new ("Save file", GTK_WINDOW (win), GTK_FILE_CHOOSER_ACTION_SAVE,
|
|
||||||
"_Cancel", GTK_RESPONSE_CANCEL,
|
|
||||||
"_Save", GTK_RESPONSE_ACCEPT,
|
|
||||||
NULL);
|
|
||||||
g_signal_connect (dialog, "response", G_CALLBACK (saveas_dialog_response), tv);
|
|
||||||
gtk_widget_show (dialog);
|
|
||||||
}
|
|
||||||
|
|
||||||
GtkWidget *
|
|
||||||
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;
|
|
||||||
|
|
||||||
if (! g_file_load_contents (file, NULL, &contents, &length, NULL, NULL)) /* read error */
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
tv = tfe_text_view_new();
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
GtkWidget *
|
|
||||||
tfe_text_view_new (void) {
|
|
||||||
return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL));
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
#define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type ()
|
|
||||||
G_DECLARE_FINAL_TYPE (TfeTextView, tfe_text_view, TFE, TEXT_VIEW, GtkTextView)
|
|
||||||
|
|
||||||
/* "open-response" signal response */
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
TFE_OPEN_RESPONSE_SUCCESS,
|
|
||||||
TFE_OPEN_RESPONSE_CANCEL,
|
|
||||||
TFE_OPEN_RESPONSE_ERROR
|
|
||||||
};
|
|
||||||
|
|
||||||
GFile *
|
|
||||||
tfe_text_view_get_file (TfeTextView *tv);
|
|
||||||
|
|
||||||
void
|
|
||||||
tfe_text_view_open (TfeTextView *tv, GtkWidget *win);
|
|
||||||
|
|
||||||
void
|
|
||||||
tfe_text_view_save (TfeTextView *tv);
|
|
||||||
|
|
||||||
void
|
|
||||||
tfe_text_view_saveas (TfeTextView *tv);
|
|
||||||
|
|
||||||
GtkWidget *
|
|
||||||
tfe_text_view_new_with_file (GFile *file);
|
|
||||||
|
|
||||||
GtkWidget *
|
|
||||||
tfe_text_view_new (void);
|
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
# Prerequisite and Licence
|
# Prerequisite and License
|
||||||
|
|
||||||
## Prerequisite
|
## Prerequisite
|
||||||
|
|
||||||
### Tutorial document
|
### Tutorial document
|
||||||
|
|
||||||
This tutorial is about gtk4 libraries.
|
This tutorial is about gtk4 libraries.
|
||||||
It is originally used on linux with C compiler, but now it is used more widely, on windows and macOS, with Vala, python and so on.
|
It is originally used on Linux with C compiler, but now it is used more widely, on windows and macOS, with Vala, python and so on.
|
||||||
However, this tutorial describes only _C programs on linux_.
|
However, this tutorial describes only _C programs on Linux_.
|
||||||
|
|
||||||
If you want to try the examples in the tutorial, you need:
|
If you want to try the examples in the tutorial, you need:
|
||||||
|
|
||||||
- PC with linux distribution like ubuntu, debian and so on.
|
- PC with Linux distribution like Ubuntu, Debian and so on.
|
||||||
- Gcc
|
- Gcc
|
||||||
- Gtk4. Gtk included linux distributions is version three at present.
|
- Gtk4. Gtk included Linux distributions is version three at present.
|
||||||
You need to install gtk4 to your computer.
|
You need to install gtk4 to your computer.
|
||||||
Refer to [gtk4 gitlab repository](https://gitlab.gnome.org/GNOME/gtk).
|
Refer to [gtk4 gitlab repository](https://gitlab.gnome.org/GNOME/gtk).
|
||||||
However, it might make some trouble like, for example, your pc doesn't recognize usb port
|
However, it might make some trouble like, for example, your pc doesn't recognize usb port
|
||||||
|
@ -21,32 +21,32 @@ Therefore, I strongly recommend you not to install gtk4 to `/usr/local` on the c
|
||||||
Instead,
|
Instead,
|
||||||
|
|
||||||
- Install it to another computer only used to try gtk4.
|
- Install it to another computer only used to try gtk4.
|
||||||
- Install it to your home directory, for example `$HOME/local`, in order to separte gtk4 from your system.
|
- Install it to your home directory, for example `$HOME/local`, in order to separate gtk4 from your system.
|
||||||
|
|
||||||
The second choice will be explained in [Section 3](sec3.src.md).
|
The second choice will be explained in [Section 3](sec3.src.md).
|
||||||
|
|
||||||
### Software
|
### Software
|
||||||
|
|
||||||
This repository inclludes ruby programs.
|
This repository includes ruby programs.
|
||||||
They are used to generate markdown files, html files, latex files and a pdf file.
|
They are used to generate markdown files, html files, latex files and a pdf file.
|
||||||
|
|
||||||
You need:
|
You need:
|
||||||
|
|
||||||
- Linux distribution like ubuntu.
|
- Linux distribution like Ubuntu.
|
||||||
- Ruby programing language.
|
- Ruby programming language.
|
||||||
There are two ways to install it.
|
There are two ways to install it.
|
||||||
One is install the distribution's package.
|
One is install the distribution's package.
|
||||||
The other is using rbenv and ruby-build.
|
The other is using rbenv and ruby-build.
|
||||||
If you want to use the latest version of ruby, use rbenv.
|
If you want to use the latest version of ruby, use rbenv.
|
||||||
- Rake.
|
- Rake.
|
||||||
It is a gem, which is a library written in ruby.
|
It is a gem, which is a library written in ruby.
|
||||||
You can install it as a package of your ditribution or use gem command.
|
You can install it as a package of your distribution or use gem command.
|
||||||
|
|
||||||
## Licence
|
## License
|
||||||
|
|
||||||
Copyright (C) 2020 ToshioCP (Toshio Sekiya)
|
Copyright (C) 2020 ToshioCP (Toshio Sekiya)
|
||||||
|
|
||||||
Gtk4 tutorial repository containes the tutorial document and softwares such as converters, generators and controlers.
|
Gtk4 tutorial repository contains the tutorial document and software such as converters, generators and controllers.
|
||||||
All of them make up the 'Gtk4 tutorial' package.
|
All of them make up the 'Gtk4 tutorial' package.
|
||||||
This package is simply called 'Gtk4 tutorial' in the following description.
|
This package is simply called 'Gtk4 tutorial' in the following description.
|
||||||
'Gtk4 tutorial' is free; you can redistribute it and/or modify it under the terms of the GNU General Public License
|
'Gtk4 tutorial' is free; you can redistribute it and/or modify it under the terms of the GNU General Public License
|
||||||
|
|
|
@ -4,6 +4,7 @@ This section and the following four sections are explanations about the next ver
|
||||||
It is tfe5.
|
It is tfe5.
|
||||||
It has many changes from the prior version.
|
It has many changes from the prior version.
|
||||||
All the sources are listed in [Section 15](sec15.src.md).
|
All the sources are listed in [Section 15](sec15.src.md).
|
||||||
|
They are located in two directories, [src/tfe5](tfe5) and [src/tfetextview](tfetextview).
|
||||||
|
|
||||||
## Encapsulation
|
## Encapsulation
|
||||||
|
|
||||||
|
@ -21,24 +22,24 @@ 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 generating (or initializing) TfeTextView?
|
||||||
- What is necessary to GFile when destructing TfeTextView?
|
- What is necessary to GFile when destructing TfeTextView?
|
||||||
- TfeTextView should read/write a file by itself or not?
|
- TfeTextView should read/write a file by itself or not?
|
||||||
- How it communicate with objects outside?
|
- How it communicates with objects outside?
|
||||||
|
|
||||||
You need to know at least class/instance and signals before thinking about them.
|
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.
|
I will explain them in this section and the next section.
|
||||||
After that I will explain:
|
After that I will explain:
|
||||||
|
|
||||||
- Organizing functions.
|
- Organizing functions.
|
||||||
- How to use FileChooserDialog
|
- How to use GtkFileChooserDialog
|
||||||
|
|
||||||
## GObject and its children
|
## GObject and its children
|
||||||
|
|
||||||
GObject and its children are objects, which have both class and instance.
|
GObject and its children are objects, which have both class and instance.
|
||||||
First, think about instance of objects.
|
First, think about instance of objects.
|
||||||
Instance is structured memories and the structure is described as C language structure.
|
Instance is structured memories and described as C language structure.
|
||||||
The following is a structure of TfeTextView.
|
The following is a structure of TfeTextView.
|
||||||
|
|
||||||
~~~C
|
~~~C
|
||||||
/* This typedef statement is automaticaly generated by the macro G_DECLARE_FINAL_TYPE */
|
/* This typedef statement is automatically generated by the macro G_DECLARE_FINAL_TYPE */
|
||||||
typedef struct _TfeTextView TfeTextView;
|
typedef struct _TfeTextView TfeTextView;
|
||||||
|
|
||||||
struct _TfeTextView {
|
struct _TfeTextView {
|
||||||
|
@ -49,14 +50,14 @@ struct _TfeTextView {
|
||||||
|
|
||||||
The members of the structure are:
|
The members of the structure are:
|
||||||
|
|
||||||
- `parent` is the structure of GtkTextView which is the parent object of TfeTextView.
|
- `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.
|
- `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.
|
Notice the program above is the declaration of the structure, not the definition.
|
||||||
So, no memories are allocated at this moment.
|
So, no memories are allocated at this moment.
|
||||||
They are to be allocated when `tfe_text_view_new` function is invoked.
|
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.
|
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).
|
The following is extracts from the source files (not exactly the same).
|
||||||
|
|
||||||
~~~C
|
~~~C
|
||||||
|
@ -84,7 +85,7 @@ struct _GtkTextView
|
||||||
};
|
};
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
In each structure, its parent instance is declared at the top of the members.
|
In each structure, its parent instance is declared at the top of members.
|
||||||
So, every ancestors is included in the child instance.
|
So, every ancestors is included in the child instance.
|
||||||
This is very important.
|
This is very important.
|
||||||
It guarantees a child widget to derive all the features from ancestors.
|
It guarantees a child widget to derive all the features from ancestors.
|
||||||
|
@ -97,14 +98,14 @@ The structure of `TfeTextView` is like the following diagram.
|
||||||
|
|
||||||
The function `tfe_text_view_new` generates a new TfeTextView instance.
|
The function `tfe_text_view_new` generates a new TfeTextView instance.
|
||||||
|
|
||||||
@@@ tfe5/tfetextview.c tfe_text_view_new
|
@@@ tfetextview/tfetextview.c tfe_text_view_new
|
||||||
|
|
||||||
When this function is run, the following procedure is gone through.
|
When this function is run, the following procedure is gone through.
|
||||||
|
|
||||||
1. Initialize GObject instance in TfeTextView instance.
|
1. Initialize GObject part in TfeTextView instance.
|
||||||
2. Initialize GtkWidget instance in TfeTextView instance.
|
2. Initialize GtkWidget part in TfeTextView instance.
|
||||||
3. Initialize GtkTextView instance in TfeTextView instance.
|
3. Initialize GtkTextView part in TfeTextView instance.
|
||||||
4. Initialize TfeTextView instance.
|
4. Initialize TfeTextView part in TfeTextView instance.
|
||||||
|
|
||||||
Step one through three is done automatically.
|
Step one through three is done automatically.
|
||||||
Step four is done by the function `tfe_text_view_init`.
|
Step four is done by the function `tfe_text_view_init`.
|
||||||
|
@ -112,24 +113,17 @@ 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.
|
> 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.
|
> You can find them in the GTK or GLib source files.
|
||||||
|
|
||||||
@@@ tfe5/tfetextview.c tfe_text_view_init
|
@@@ tfetextview/tfetextview.c tfe_text_view_init
|
||||||
|
|
||||||
`tfe_text_view_init` initializes the instance.
|
This function just initializes `tv->file` to be `NULL`.
|
||||||
|
|
||||||
- 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
|
## Functions and Classes
|
||||||
|
|
||||||
In Gtk, all objects derived from GObject have class and instance.
|
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.
|
Instance is memories which has a structure defined by C structure declaration as I mentioned in the previous two subsections.
|
||||||
An instance can be generated two times or more.
|
Each object can have more than one instance.
|
||||||
Those instances have the same structure.
|
Those instances have the same structure.
|
||||||
Instance, which is structured memories, only keeps status of the object.
|
Instance, which is structured memories, only keeps status of the instance.
|
||||||
Therefore, it is insufficient to define its behavior.
|
Therefore, it is insufficient to define its behavior.
|
||||||
We need at least two things.
|
We need at least two things.
|
||||||
One is functions and the other is class.
|
One is functions and the other is class.
|
||||||
|
@ -140,7 +134,7 @@ These functions are similar to object methods in object oriented languages such
|
||||||
Functions are public, which means that they are expected to be used by other objects.
|
Functions are public, which means that they are expected to be used by other objects.
|
||||||
|
|
||||||
Class comprises mainly pointers to functions.
|
Class comprises mainly pointers to functions.
|
||||||
Those functions are used by the object itself or its descendent objects.
|
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.
|
For example, GObject class is declared in `gobject.h` in GLib source files.
|
||||||
|
|
||||||
@@@ class_gobject.c
|
@@@ class_gobject.c
|
||||||
|
@ -154,8 +148,8 @@ void (*dispose) (GObject *object);
|
||||||
|
|
||||||
The declaration is a bit complicated.
|
The declaration is a bit complicated.
|
||||||
The asterisk before the identifier `dispose` means pointer.
|
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 because of void type.
|
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 paremeter, 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
|
~~~C
|
||||||
void (*finalize) (GObject *object);
|
void (*finalize) (GObject *object);
|
||||||
|
@ -164,7 +158,11 @@ void (*finalize) (GObject *object);
|
||||||
Look at the declaration of `_GObjectClass` so that you would find that most of the members are pointers to functions.
|
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.
|
- 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 and the instance releases all the references to other instances. The second one 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.
|
||||||
|
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.
|
- 23: A function pointed by `finalize` finishes the destruction process.
|
||||||
- The other pointers point to functions which are called while the instance lives.
|
- The other pointers point to functions which are called while the instance lives.
|
||||||
|
|
||||||
|
@ -183,11 +181,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`.
|
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.
|
- 2, 73, 106: Each derived class puts its parent class at the first member of its structure.
|
||||||
It is the same as instance structures.
|
It is the same as instance structures.
|
||||||
- Class members in ancestors are open to the descendent class.
|
- Class members in ancestors are open to the descendant class.
|
||||||
So, they can be changed in `tfe_text_view_class_init` function.
|
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`.
|
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 an object oriented programming terminology.
|
||||||
Override is rewriting ancestors' class methods in the descendent class.)
|
Override is rewriting ancestors' class methods in the descendant class.)
|
||||||
- Some class methods are often overridden.
|
- Some class methods are often overridden.
|
||||||
`set_property`, `get_property`, `dispose`, `finalize` and `constructed` are such methods.
|
`set_property`, `get_property`, `dispose`, `finalize` and `constructed` are such methods.
|
||||||
|
|
||||||
|
@ -199,36 +197,39 @@ It is illustrated in the following diagram.
|
||||||
## Destruction of TfeTextView
|
## Destruction of TfeTextView
|
||||||
|
|
||||||
Every Object derived from GObject has a reference count.
|
Every Object derived from GObject has a reference count.
|
||||||
If an object A uses 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 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 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 two objects A and B refer to C, then the reference count of C is two.
|
||||||
After A used C and if A no longer needs C, A discards the pointer to C and decreases the reference count in C by one.
|
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.
|
Now the reference count of C is one.
|
||||||
In the same way, when B no longer needs C, B discards the pointer to C and decreases the reference count in C by 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.
|
At this moment, no object refers C and the reference count of C is zero.
|
||||||
This means C is no longer useful.
|
This means C is no longer useful.
|
||||||
Then C destructs itself and finally the memories allocated to C is freed.
|
Then C destructs itself and finally the memories allocated to C is freed.
|
||||||
|
|
||||||
![Reference count of B](../image/refcount.png){width=15.855cm height=2.475cm}
|
![Reference count of B](../image/refcount.png){width=15.855cm height=2.475cm}
|
||||||
|
|
||||||
The idea above is based on an assumption that an object refered by nothing has reference count of zero.
|
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.
|
When the reference count drops to zero, the object starts its destruction process.
|
||||||
The destruction process is split in two phases: disposing and finalizing.
|
The destruction process is spitted into two phases: disposing and finalizing.
|
||||||
In the disposing process, the object invokes the handler pointed by `dispose` in its class to release all references to other objects.
|
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 handler pointed by `finalize` in its class to complete the destruction process.
|
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.
|
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.
|
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`.
|
You must write the code in the dispose handler `tfe_text_view_dispose`.
|
||||||
|
|
||||||
@@@ tfe5/tfetextview.c tfe_text_view_dispose
|
@@@ tfetextview/tfetextview.c tfe_text_view_dispose
|
||||||
|
|
||||||
- 5,6: If `tv->file` points a GFile, decrease its reference count.
|
- 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`.
|
`g_clear_object` decreases the reference count and assigns NULL to `tv->file`.
|
||||||
- 8: invoke parent's despose handler. (This will be explained later.)
|
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 desposing process, the object uses the pointer in its class to call the handler.
|
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.
|
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.
|
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.
|
||||||
|
|
||||||
|
@ -248,7 +249,7 @@ Look at the following diagram.
|
||||||
There are four classes -- GObjectClass (GInitiallyUnownedClass), GtkWidgetClass, GtkTextViewClass and TfeTextViewClass.
|
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`.
|
Each class has its own dispose handler -- `dh1`, `dh2`, `dh3` and `tfe_text_view_dispose`.
|
||||||
|
|
||||||
![dispose handers](../image/dispose_handler.png){width=14.925cm height=4.455cm}
|
![dispose handlers](../image/dispose_handler.png){width=14.925cm height=4.455cm}
|
||||||
|
|
||||||
Now, look at the `tfe_text_view_dispose` program above.
|
Now, look at the `tfe_text_view_dispose` program above.
|
||||||
It first releases the reference to GFile object pointed by `tv->file`.
|
It first releases the reference to GFile object pointed by `tv->file`.
|
||||||
|
@ -260,7 +261,7 @@ 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.
|
`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.
|
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 instanse.
|
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.
|
`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`.
|
After that, `dh3` calls `dh2`, and `dh2` calls `dh1`.
|
||||||
Finally all the references are released.
|
Finally all the references are released.
|
||||||
|
|
|
@ -17,18 +17,18 @@ Then the handler, which has been connected to the signal, is invoked.
|
||||||
|
|
||||||
The caller of the function or the handler connected to the signal is usually outside of the object.
|
The caller of the function or the handler connected to the signal is usually outside of the object.
|
||||||
One of the difference between these two is that the object is active or passive.
|
One of the difference between these two is that the object is active or passive.
|
||||||
In functions the object responds to the caller.
|
In functions the object passively responds to the caller.
|
||||||
In signals the object actively sends a signal to the handler.
|
In signals the object actively sends a signal to the handler.
|
||||||
|
|
||||||
GObject signal can be registered, connected and emitted.
|
GObject signals are registered, connected and emitted.
|
||||||
|
|
||||||
1. A signal is registered with the object type on which it can be emitted.
|
1. Signals are registered with the object type on which they can be emitted.
|
||||||
This is done usually when the class is initialized.
|
The registration is done usually when the class is initialized.
|
||||||
2. It is connected to a handler by `g_connect_signal` or its family functions.
|
2. Signals are connected to handlers by `g_connect_signal` or its family functions.
|
||||||
3. When it is emmitted, the connected handler is invoked.
|
3. When Signals are emitted, the connected handlers are invoked.
|
||||||
|
|
||||||
Step one and three are done in the object on which the signal is emitted.
|
Step one and three are done in the object on which the signal belongs.
|
||||||
Step two is usually done outside the objects.
|
Step two is usually done outside the object.
|
||||||
|
|
||||||
## Signal registration
|
## Signal registration
|
||||||
|
|
||||||
|
@ -37,11 +37,11 @@ In TfeTextView, two signals are registered.
|
||||||
- "change-file" signal.
|
- "change-file" signal.
|
||||||
This signal is emitted when `tv->file` is changed.
|
This signal is emitted when `tv->file` is changed.
|
||||||
- "open-response" signal.
|
- "open-response" signal.
|
||||||
`tfe_text_view_open` function is not able to return the status because of using GtkFileChooserDialog.
|
`tfe_text_view_open` function is not able to return the status because it uses GtkFileChooserDialog.
|
||||||
This signal is emitted instead of the return value of the function.
|
This signal is emitted instead of the return value of the function.
|
||||||
|
|
||||||
Static variable is used to store the signal ID.
|
A static variable or array is used to store the signal ID.
|
||||||
If you need to register two or more signals, static array is usually used.
|
A static array is used to register two or more signals.
|
||||||
|
|
||||||
~~~C
|
~~~C
|
||||||
enum {
|
enum {
|
||||||
|
@ -55,9 +55,9 @@ static guint tfe_text_view_signals[NUMBER_OF_SIGNALS];
|
||||||
|
|
||||||
Signal registration codes are written in the class initialization function.
|
Signal registration codes are written in the class initialization function.
|
||||||
|
|
||||||
@@@ tfe5/tfetextview.c tfe_text_view_class_init
|
@@@ tfetextview/tfetextview.c tfe_text_view_class_init
|
||||||
|
|
||||||
- 6-15: Register "change-file"signal.
|
- 6-15: Registers "change-file" signal.
|
||||||
`g_signal_newv` function is used.
|
`g_signal_newv` function is used.
|
||||||
This signal has no default handler (object method handler).
|
This signal has no default handler (object method handler).
|
||||||
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.
|
||||||
|
@ -65,7 +65,7 @@ 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 return value of `g_signal_newv` is the signal id.
|
||||||
The type of signal id is guint, which is the same as unsigned int.
|
The type of signal id is guint, which is the same as unsigned int.
|
||||||
It is used when the signal is emitted.
|
It is used when the signal is emitted.
|
||||||
- 16-26: Register "open-response" signal.
|
- 16-26: Registers "open-response" signal.
|
||||||
This signal has a parameter.
|
This signal has a parameter.
|
||||||
- 25: Number of the parameter.
|
- 25: Number of the parameter.
|
||||||
"open-response" signal has one parameter.
|
"open-response" signal has one parameter.
|
||||||
|
@ -75,20 +75,20 @@ It has one element, which is `G_TYPE_INT`.
|
||||||
`G_TYPE_INT` is a type of integer.
|
`G_TYPE_INT` is a type of integer.
|
||||||
Such fundamental types are described in [GObject API reference](https://developer.gnome.org/gobject/stable/gobject-Type-Information.html).
|
Such fundamental types are described in [GObject API reference](https://developer.gnome.org/gobject/stable/gobject-Type-Information.html).
|
||||||
|
|
||||||
The handlers are as follows.
|
The handlers are declared as follows.
|
||||||
|
|
||||||
~~~C
|
~~~C
|
||||||
void change_file_handler (TfeTextView *tv, gpointer user_data);
|
void change_file_handler (TfeTextView *tv, gpointer user_data);
|
||||||
void open_response_handler (TfeTextView *tv, guint parameter, 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 "change-file" signal doesn't have parameter, the handler's parameters are a TfeTextView object and user data.
|
||||||
- Because "open-response" signal has one parameter, the handler's parameter is TfeTextView object, the parameter and user data.
|
- Because "open-response" signal has one parameter, the handler's parameters are a TfeTextView object, the signal's parameter and a user data.
|
||||||
- `tv` is the object instance on which the signal is emitted.
|
- `tv` is the object instance on which the signal is emitted.
|
||||||
- `user_data` comes from the fourth argument of `g_signal_connect`.
|
- `user_data` comes from the fourth argument of `g_signal_connect`.
|
||||||
- `parameter` comes from the fourth argument of `g_signal_emit`.
|
- `parameter` comes from the fourth argument of `g_signal_emit`.
|
||||||
|
|
||||||
The parameter is defined in `tfetextview.h` because it is public.
|
The values of the parameter is defined in `tfetextview.h` because they are public.
|
||||||
|
|
||||||
~~~C
|
~~~C
|
||||||
/* "open-response" signal response */
|
/* "open-response" signal response */
|
||||||
|
@ -100,18 +100,22 @@ enum
|
||||||
};
|
};
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
- `TFE_OPEN_RESPONSE_SUCCESS` is set when `tfe_text_view_open` successfully has opend a file and loaded it.
|
- The parameter is set to `TFE_OPEN_RESPONSE_SUCCESS` when `tfe_text_view_open` successfully has opened a file and loaded it.
|
||||||
- `TFE_OPEN_RESPONSE_CANCEL` is set when the user has canceled to open a file.
|
- The parameter is set to `TFE_OPEN_RESPONSE_CANCEL` when the user has canceled to open a file.
|
||||||
- `TFE_OPEN_RESPONSE_ERROR` is set when error has occured.
|
- The parameter is set to `TFE_OPEN_RESPONSE_ERROR` when error has occurred.
|
||||||
|
|
||||||
## Signal connection
|
## Signal connection
|
||||||
|
|
||||||
A signal and a handler are connected by the function `g_signal_connect`.
|
A signal and a handler are connected by the function `g_signal_connect`.
|
||||||
There are some similar functions like `g_signal_connect_after`, `g_signal_connect_swapped` and so on.
|
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.
|
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.
|
The signals "change-file" is connected to a callback function outside of TfeTextView object.
|
||||||
In the same way, the signals "open-response" is connected to a callback function `open_response` outside of TfeTextView object.
|
In the same way, the signals "open-response" is connected to a callback function outside of TfeTextView object.
|
||||||
The functions `file_changed` and `open_response` will be explained later.
|
Those callback functions are defined by users.
|
||||||
|
|
||||||
|
In the program tfe, callback functions are defined in `tfenotebook.c`.
|
||||||
|
And their names are `file_changed` and `open_response`.
|
||||||
|
They will be explained later.
|
||||||
|
|
||||||
~~~C
|
~~~C
|
||||||
g_signal_connect (GTK_TEXT_VIEW (tv), "change-file", G_CALLBACK (file_changed), nb);
|
g_signal_connect (GTK_TEXT_VIEW (tv), "change-file", G_CALLBACK (file_changed), nb);
|
||||||
|
@ -125,7 +129,7 @@ Signals are emitted on the object.
|
||||||
The type of the object is the second argument of `g_signal_newv`.
|
The type of the object is the second argument of `g_signal_newv`.
|
||||||
The relationship between the signal and object (type) is made up when the signal is generated.
|
The relationship between the signal and object (type) is made up when the signal is generated.
|
||||||
|
|
||||||
`g_signal_emit` is used to emit the signal.
|
A function `g_signal_emit` is used to emit the signal.
|
||||||
The following lines are extracted from `tfetextview.c`.
|
The following lines are extracted from `tfetextview.c`.
|
||||||
Each line is quoted from a different line.
|
Each line is quoted from a different line.
|
||||||
|
|
||||||
|
@ -140,7 +144,7 @@ g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_ER
|
||||||
- The second argument is the signal id.
|
- The second argument is the signal id.
|
||||||
- The third argument is the detail of the signal.
|
- The third argument is the detail of the signal.
|
||||||
"change-file" signal and "open-response" signal doesn't have details and the argument is zero when no details.
|
"change-file" signal and "open-response" signal doesn't have details and the argument is zero when no details.
|
||||||
- "change-file" signal doesn't have parameter, so no fourth parameter.
|
- "change-file" signal doesn't have parameter, so there's no fourth parameter.
|
||||||
- "open-response" signal has one parameter.
|
- "open-response" signal has one parameter.
|
||||||
The fourth parameter is the parameter.
|
The fourth parameter is the parameter.
|
||||||
|
|
||||||
|
|
171
src/sec12.src.md
171
src/sec12.src.md
|
@ -1,23 +1,24 @@
|
||||||
# Functions in TfeTextView
|
# Functions in TfeTextView
|
||||||
|
|
||||||
In this section I will explain each function in TfeTextView object.
|
In this section I will explain functions in TfeTextView object.
|
||||||
|
|
||||||
### tfe.h and tfetextview.h
|
### tfe.h and tfetextview.h
|
||||||
|
|
||||||
`tfe.h` is a top header file and it includes `gtk.h` and all the header files.
|
`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.
|
C source files `tfeapplication.c` and `tfenotebook.c` include `tfe.h` at the beginning.
|
||||||
|
|
||||||
@@@ tfe5/tfe.h
|
@@@ tfe5/tfe.h
|
||||||
|
|
||||||
`tfetextview.h` is a header file which describes the public functions in `tfetextview.c`.
|
`../tfetextview/tfetextview.h` is a header file which describes the public functions in `tfetextview.c`.
|
||||||
|
|
||||||
@@@ tfe5/tfetextview.h
|
@@@ tfetextview//tfetextview.h
|
||||||
|
|
||||||
- 1-2: These two lines are used to define TfeTextView.
|
- 1,2,35: Thanks to these three lines, the following lines are included only once.
|
||||||
- 4-10: Definitions of parameter used in the handler of "open-response" signal.
|
- 4: Includes gtk4 header files.
|
||||||
- 12-28: Public functions on GtkTextView.
|
The header file `gtk4` also has the same mechanism to avoid including it multiple times.
|
||||||
|
- 6-7: These two lines define TfeTextView.
|
||||||
Each function will be explained later in this section.
|
- 9-15: A definition of the value of the parameter of "open-response" signal.
|
||||||
|
- 17-33: Declaration of public functions on GtkTextView.
|
||||||
|
|
||||||
## Functions to generate TfeTextView object
|
## Functions to generate TfeTextView object
|
||||||
|
|
||||||
|
@ -34,24 +35,16 @@ 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.
|
`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 occurs during the generation process, NULL is returned.
|
If an error occurs during the generation process, NULL is returned.
|
||||||
|
|
||||||
Each function is defined as follows.
|
Each function is defined as follows.
|
||||||
|
|
||||||
@@@ tfe5/tfetextview.c tfe_text_view_new_with_file tfe_text_view_new
|
@@@ tfetextview/tfetextview.c tfe_text_view_new_with_file tfe_text_view_new
|
||||||
|
|
||||||
- 21-24: `tfe_text_view_new`.
|
- 21-24: `tfe_text_view_new` function.
|
||||||
Just returns the value from the function `g_object_new` but casted to the pointer to GtkWidget.
|
Just returns the value from the function `g_object_new` but casts it to the pointer to GtkWidget.
|
||||||
Initialization is done in `tfe_text_view_init` which is called in the process of `gtk_widget_new` function.
|
Initialization is done in `tfe_text_view_init` which is called in the process of `g_object_new` function.
|
||||||
- 1-19: `tfe_text_view_new_with_file`
|
- 1-19: `tfe_text_view_new_with_file` function.
|
||||||
- 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).
|
- 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.
|
It tests whether the argument `file` is a pointer to GFile.
|
||||||
If it's true, then the program goes on to the next line.
|
If it's true, then the program goes on to the next line.
|
||||||
|
@ -62,12 +55,14 @@ If an error occurs, the solution is usually to change the (caller) program and f
|
||||||
You need to distinguish programmer's errors and runtime errors.
|
You need to distinguish programmer's errors and runtime errors.
|
||||||
You shouldn't use this function to find runtime errors.
|
You shouldn't use this function to find runtime errors.
|
||||||
- 10-11: If an error occurs when reading the file, then return NULL.
|
- 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`.
|
- 13: Calls the function `tfe_text_view_new`.
|
||||||
The pointer to GtkTextBuffer is set to `tb`
|
The function generates TfeTextView instance and returns the pointer to the instance.
|
||||||
Set the contents read from the file to GtkTextBuffer `tb`.
|
- 14: Gets the pointer to GtkTextBuffer corresponds to `tv`.
|
||||||
Free the memories pointed by `contents`.
|
The pointer is assigned to `tb`
|
||||||
Duplicate `file` and set it to `tv->file`.
|
- 15: Assigns the contents read from the file to GtkTextBuffer pointed by `tb`.
|
||||||
Return `tv`.
|
- 16: Frees the memories pointed by `contents`.
|
||||||
|
- 17: Duplicates `file` and sets `tv->file` to point it.
|
||||||
|
- 18: Returns `tv`, which is a pointer to the newly created TfeTextView instance..
|
||||||
|
|
||||||
## Save and saveas functions
|
## Save and saveas functions
|
||||||
|
|
||||||
|
@ -77,90 +72,101 @@ Save and saveas functions write the contents in GtkTextBuffer to a file.
|
||||||
void tfe_text_view_save (TfeTextView *tv)
|
void tfe_text_view_save (TfeTextView *tv)
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
`save` function writes the contents in GtkTextBuffer to a file specified by `tv->file`.
|
The function `save` 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`.
|
If `tv->file` is NULL, then it shows GtkFileChooserDialog and prompts the user to choose a file to save.
|
||||||
|
Then it saves the contents to the file and sets `tv->file` to point the GFile instance of the file.
|
||||||
|
|
||||||
~~~C
|
~~~C
|
||||||
void tfe_text_view_saveas (TfeTextView *tv)
|
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.
|
The function `saveas` uses GtkFileChooserDialog and prompts the user to select a existed file or specify a new file to save.
|
||||||
|
Then, the function changes `tv->file` and save the contents to the specified file.
|
||||||
If an error occurs, it is shown to the user through the message dialog.
|
If an error occurs, 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.
|
The error is managed only in the TfeTextView instance and no information is notified to the caller.
|
||||||
|
|
||||||
@@@ tfe5/tfetextview.c saveas_dialog_response tfe_text_view_save tfe_text_view_saveas
|
@@@ tfetextview/tfetextview.c saveas_dialog_response tfe_text_view_save tfe_text_view_saveas
|
||||||
|
|
||||||
- 18-55: `Tfe_text_view_save` function.
|
- 20-56: `Tfe_text_view_save` function.
|
||||||
- 20: If `tv` is not a pointer to TfeTextView, then it logs an error message and immediately returns.
|
- 22: If `tv` is not a pointer to TfeTextView, then it logs an error message and immediately returns.
|
||||||
This function is similar to `g_return_val_if_fail` function, but no value is returned because `tfe_text_view_save` doesn't return a value.
|
This function is similar to `g_return_val_if_fail` function, but no value is returned because `tfe_text_view_save` doesn't return a value.
|
||||||
- 30-31: If the buffer hasn't modified, then it doesn't need to save it.
|
- 32-33: If the buffer hasn't modified, then it doesn't need to save it.
|
||||||
So the function returns.
|
So the function returns.
|
||||||
- 32-33: If `tv->file` is NULL, no file has given yet.
|
- 34-35: If `tv->file` is NULL, no file has given yet.
|
||||||
It calls `tfe_text_view_saveas`, which lets the user to choose a file to save.
|
It calls `tfe_text_view_saveas` which prompts a user to select a file or specify a new file to save.
|
||||||
- 35-36: Get the contents of the GtkTextBuffer and set its pointer to `contents`.
|
- 37-38: Gets the contents of the GtkTextBuffer and sets `contents` to point it.
|
||||||
- 37-38: Save the content to the file.
|
- 39-40: Saves the content to the file.
|
||||||
If it succeeds, reset the modified bit in the GtkTextBuffer.
|
If it succeeds, it resets the modified bit in the GtkTextBuffer.
|
||||||
- 39-53: If file writing fails, it assigns NULL to `tv->file`.
|
- 42-53: If file writing fails, it assigns NULL to `tv->file`.
|
||||||
Emits "change-file" signal.
|
Emits "change-file" signal.
|
||||||
Shows the error message dialog (47-51).
|
Shows the error message dialog (48-52).
|
||||||
Because the handler is `gtk_window_destroy`, the dialog disappears when user clicks on the button in the dialog.
|
Because the handler is `gtk_window_destroy`, the dialog disappears when user clicks on the button in the dialog.
|
||||||
- 57-70: `tfe_text_view_saveas` function.
|
Frees the GError object.
|
||||||
It shows GtkFileChooserDialog and lets the user choose a file and give it to the signal handler.
|
- 58-71: `tfe_text_view_saveas` function.
|
||||||
- 64-67: Generate GtkFileChooserDialog.
|
It shows GtkFileChooserDialog and prompts the user to choose a file.
|
||||||
|
- 65-68: Generates GtkFileChooserDialog.
|
||||||
The title is "Save file".
|
The title is "Save file".
|
||||||
Transient parent of the dialog is `win`, which is the top level window.
|
Transient parent of the dialog is `win`, which is the top level window.
|
||||||
The action is save mode.
|
The action is save mode.
|
||||||
The buttons are Cancel and Save.
|
The buttons are Cancel and Save.
|
||||||
- 68: connect the "response" signal of the dialog and `saveas_dialog_response` handler.
|
- 69: connects the "response" signal of the dialog and `saveas_dialog_response` handler.
|
||||||
- 1-16: `saveas_dialog_response` signal handler.
|
- 1-18: `saveas_dialog_response` signal handler.
|
||||||
- 6-14: 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 to `tv->file`, turn on the modified bit of the GtkTextBuffer, emits "change-file" signal then call `tfe_text_view_save` to save the buffer to the file.
|
- 6-16: If the response is `GTK_RESPONSE_ACCEPT`, then it gets a pointer to the GFile object.
|
||||||
|
Then, it sets `tv->file` to point the GFile.
|
||||||
|
And turns on the modified bit of the GtkTextBuffer, emits "change-file" signal.
|
||||||
|
Finally, it calls `tfe_text_view_save` to save the buffer to the file.
|
||||||
|
|
||||||
![Saveas process](../image/saveas.png){width=10.7cm height=5.16cm}
|
![Saveas process](../image/saveas.png){width=10.7cm height=5.16cm}
|
||||||
|
|
||||||
When you use GtkFileChooserDialog, you need to divide the program into two parts.
|
When you use GtkFileChooserDialog, you need to divide the program into two parts.
|
||||||
They are a function which generates GtkFileChooserDialog and the signal handler.
|
One is are a function which generates GtkFileChooserDialog and the other is a signal handler.
|
||||||
The function just generates and shows the dialog.
|
The function just generates and shows GtkFileChooserDialog.
|
||||||
The rest is done by the handler.
|
The rest is done by the handler.
|
||||||
It gets Gfile from GtkFileChooserDialog, save the buffer to the file by calling `tfe_text_view_save`.
|
It gets Gfile from GtkFileChooserDialog and saves the buffer to the file by calling `tfe_text_view_save`.
|
||||||
|
|
||||||
## Open function
|
## Open function
|
||||||
|
|
||||||
Open function shows GtkFileChooserDialog to the user and let them choose a file.
|
Open function shows GtkFileChooserDialog to users and prompts them to choose a file.
|
||||||
Then read the file and set it to GtkTextBuffer.
|
Then it reads the file and puts the text to GtkTextBuffer.
|
||||||
|
|
||||||
~~~C
|
~~~C
|
||||||
void tfe_text_view_open (TfeTextView *tv, GtkWidget *win);
|
void tfe_text_view_open (TfeTextView *tv, GtkWidget *win);
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
TfeTextView object `tv` has to be generated in advance.
|
The parameter `win` is the top window.
|
||||||
This function is usually called just after `tv` has been generated.
|
It will be a transient parent window of GtkFileChooserDialog when the dialog is generated..
|
||||||
And its buffer is empty, `tv->file` is NULL and `tv` has not set to the widget hierarchy.
|
This allows window managers to keep the dialog on top of the parent window, or center the dialog over the parent window.
|
||||||
Even if the buffer is not empty, `tfe_text_view_open` doesn't treat it as an error.
|
It is possible to give no parent window to the dialog.
|
||||||
If you want to revert the buffer, calling this function is apropreate.
|
However, it is encouraged to give a parent window to dialog.
|
||||||
|
This function might be called just after `tv` has been generated.
|
||||||
|
In that case, `tv` has not been incorporated into the widget hierarchy.
|
||||||
|
Therefore it is impossible to get the top window from `tv`.
|
||||||
|
That's why the function needs `win` parameter.
|
||||||
|
|
||||||
|
This function is usually called when the buffer of `tv` is empty.
|
||||||
|
However, even if the buffer 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 appropriate.
|
||||||
Otherwise probably bad things will happen.
|
Otherwise probably bad things will happen.
|
||||||
|
|
||||||
GtkWidget `win` is expected to be the top level window of the application.
|
@@@ tfetextview/tfetextview.c open_dialog_response tfe_text_view_open
|
||||||
It will be used as a transient parent window for the argument to the function `gtk_file_chooser_dialog_new`.
|
|
||||||
|
|
||||||
@@@ tfe5/tfetextview.c open_dialog_response tfe_text_view_open
|
- 37-50: `tfe_text_view_open` function.
|
||||||
|
- 44-47: Generates GtkFileChooserDialog.
|
||||||
- 36-49: `tfe_text_view_open` function.
|
|
||||||
- 43: Generate GtkFileChooserDialog.
|
|
||||||
The title is "Open file".
|
The title is "Open file".
|
||||||
Ttransient parent window is the top window of the application, which is given by the caller.
|
Transient parent window is the top window of the application, which is given by the caller.
|
||||||
The action is open mode.
|
The action is open mode.
|
||||||
The buttons are Cancel and Open.
|
The buttons are Cancel and Open.
|
||||||
- 47: connect the "reponse" signal of the dialog and `open_dialog_response` signal handler.
|
- 48: connects the "response" signal of the dialog and `open_dialog_response` signal handler.
|
||||||
- 48: Show the dialog.
|
- 49: Shows the dialog.
|
||||||
- 1-34: `open_dialog_response` signal handler.
|
- 1-35: `open_dialog_response` signal handler.
|
||||||
- 10-11: 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 parameter `TFE_OPEN_RESPONSE_CANCEL`.
|
- 10-11: 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 parameter `TFE_OPEN_RESPONSE_CANCEL`.
|
||||||
- 12-13: Get a pointer to Gfile by `gtk_file_chooser_get_file`.
|
- 12-13: Gets a pointer to Gfile by `gtk_file_chooser_get_file`.
|
||||||
If it is not GFile, maybe an error occured.
|
If it is not GFile, maybe an error occurred.
|
||||||
Then it emits "open-response" signal with the parameter `TFE_OPEN_RESPONSE_ERROR`.
|
Then it emits "open-response" signal with the parameter `TFE_OPEN_RESPONSE_ERROR`.
|
||||||
- 14-23: If an error occurs when it 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`.
|
- 14-23: If an error occurs at file reading, then it decreases the reference count of the 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-32: If the file has successfully read, then the text is set to GtkTextBuffer, free the temporary buffer pointed by `contents`, set file to `tv->file` (no duplication or unref is not necessary) and emits "open-response" signal with the parameter `TFE_OPEN_RESPONSE_SUCCESS`.
|
- 24-33: If the file has successfully read, then the text is inserted to GtkTextBuffer, frees the temporary buffer pointed by `contents` and sets `tv->file` to point the file (no duplication or unref is not necessary).
|
||||||
- 33: close GtkFileCooserDialog.
|
Then, it emits "open-response" signal with the parameter `TFE_OPEN_RESPONSE_SUCCESS` and emits "change-file" signal.
|
||||||
|
- 34: closes GtkFileCooserDialog.
|
||||||
|
|
||||||
Now let's think about the whole process between the other object (caller) and TfeTextView.
|
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.
|
It is shown in the following diagram and you would think that it is really complicated.
|
||||||
|
@ -171,23 +177,24 @@ However, in Gtk4, `gtk_dialog_run`is unavailable any more.
|
||||||
|
|
||||||
![Caller and TfeTextView](../image/open.png){width=12.405cm height=9.225cm}
|
![Caller and TfeTextView](../image/open.png){width=12.405cm height=9.225cm}
|
||||||
|
|
||||||
1. A caller get a pointer `tv` to TfeTextView by calling `tfe_text_view_new`.
|
1. A caller gets a pointer `tv` to TfeTextView by calling `tfe_text_view_new`.
|
||||||
2. The caller connects the handler (left bottom in the diagram) and the signal "open-response".
|
2. The caller connects the handler (left bottom in the diagram) and the signal "open-response".
|
||||||
3. It calls `tfe_text_view_open` to let the user select a file from GtkFileChooserDialog.
|
3. It calls `tfe_text_view_open` to prompt the user to select a file from GtkFileChooserDialog.
|
||||||
4. The dialog emits a signal and it invokes the handler `open_dialog_response`.
|
4. The dialog emits a signal and it invokes the handler `open_dialog_response`.
|
||||||
5. The handler read the file and set it into GtkTextBuffer and emits a signal to inform the response status.
|
5. The handler reads the file and inserts the text into GtkTextBuffer and emits a signal to inform the response status.
|
||||||
6. The handler outside TfeTextView recieves the signal.
|
6. The handler outside TfeTextView receives the signal.
|
||||||
|
|
||||||
## Get file function
|
## Get file function
|
||||||
|
|
||||||
`gtk_text_view_get_file` is a simple function show as follows.
|
`gtk_text_view_get_file` is a simple function show as follows.
|
||||||
|
|
||||||
@@@ tfe5/tfetextview.c tfe_text_view_get_file
|
@@@ tfetextview/tfetextview.c tfe_text_view_get_file
|
||||||
|
|
||||||
The important thing is duplicate `tv->file`.
|
The important thing is to duplicate `tv->file`.
|
||||||
Otherwise, if the caller free the GFile object, `tv->file` is no more guaranteed to point the GFile.
|
Otherwise, if the caller frees the GFile object, `tv->file` is no more guaranteed to point the GFile.
|
||||||
|
|
||||||
## Source file of tfetextview.c
|
## Source file of tfetextview.c
|
||||||
|
|
||||||
All the source files are listed in [Section 15](sec15.src.md).
|
All the source files are listed in [Section 15](sec15.src.md).
|
||||||
|
You can find them under [src/tfe5](tfe5) and [src/tfetextview](../tfetextview) directories.
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,20 @@
|
||||||
# Functions with GtkNotebook
|
# Functions in GtkNotebook
|
||||||
|
|
||||||
GtkNotebook is a very important object in the text file editor `tfe`.
|
GtkNotebook is a very important object in the text file editor `tfe`.
|
||||||
It connects the application and TfeTextView objects.
|
It connects the application and TfeTextView objects.
|
||||||
`tfenotebook.h` and `tfenotebook.c` have a set of functions related to GtkTextbook.
|
A set of functions related to GtkNotebook are declared in `tfenotebook.h`.
|
||||||
|
The word "tfenotebook" is used only in filenames.
|
||||||
|
There's no "TfeNotebook" object.
|
||||||
|
|
||||||
@@@ tfe5/tfenotebook.h
|
@@@ tfe5/tfenotebook.h
|
||||||
|
|
||||||
This header file shows the public functions in `tfenotebook.c`.
|
This header file describes the public functions in `tfenotebook.c`.
|
||||||
|
|
||||||
- 10-11: `notebook_page_new` generates a new GtkNotebookPage and adds GtkScrolledWindow and TfeTextView on the page.
|
- 10-11: The function `notebook_page_new` generates a new GtkNotebookPage and adds GtkScrolledWindow and TfeTextView to the page.
|
||||||
- 7-8: `notebook_page_new_with_file` generates a new GtkNotebookPage and adds GtkScrolledWindow and TfeTextView on the page. The file is read and set into GtkTextBuffer.
|
- 7-8: The function `notebook_page_new_with_file` generates a new GtkNotebookPage and adds GtkScrolledWindow and TfeTextView to the page. A file is read and inserted into GtkTextBuffer.
|
||||||
The GFile `file` is copied and set in the TfeTextView object.
|
The GFile `file` is copied and inserted to the TfeTextView object.
|
||||||
- 4-5: `notebook_page_open` shows a file chooser dialog. Then, user chooses a file and the file is set into GtkTextBuffer.
|
- 4-5: `notebook_page_open` shows a file chooser dialog. Then, user chooses a file and the file is inserted into GtkTextBuffer.
|
||||||
- 1-2: `notebook_page_save` saves the contents in GtkTextBuffer into the file, which has been set in the TfeTextView.
|
- 1-2: `notebook_page_save` saves the contents in GtkTextBuffer into the file, which is got from the TfeTextView.
|
||||||
|
|
||||||
You probably find that the functions above are higher level functions of
|
You probably find that the functions above are higher level functions of
|
||||||
|
|
||||||
|
@ -21,7 +23,7 @@ You probably find that the functions above are higher level functions of
|
||||||
- `tef_text_view_open`
|
- `tef_text_view_open`
|
||||||
- `tfe_text_view_save`
|
- `tfe_text_view_save`
|
||||||
|
|
||||||
respectively.
|
respectively.
|
||||||
|
|
||||||
There are two layers.
|
There are two layers.
|
||||||
One of them is `tfe_text_view ...`, which is the lower level layer.
|
One of them is `tfe_text_view ...`, which is the lower level layer.
|
||||||
|
@ -33,51 +35,59 @@ Now let's look at each program of the functions.
|
||||||
|
|
||||||
@@@ tfe5/tfenotebook.c get_untitled notebook_page_build notebook_page_new
|
@@@ tfe5/tfenotebook.c get_untitled notebook_page_build notebook_page_new
|
||||||
|
|
||||||
- 27-37: `notebook_page_new` function.
|
- 28-38: `notebook_page_new` function.
|
||||||
- 29: `g_return_if_fail` is used to check the argument.
|
- 30: `g_return_if_fail` is used to check the argument.
|
||||||
- 34: Generate TfeTextView object.
|
- 35: Generates TfeTextView object.
|
||||||
- 35: Generate filename, which is "Untitled", "Untitled2", ... .
|
- 36: Generates filename, which is "Untitled", "Untitled2", ... .
|
||||||
- 1-8: `get_untitled` function.
|
- 1-8: `get_untitled` function.
|
||||||
- 3: Static variable `c` is initialized at the first call of this function. After that `c` keeps its value except it is changed explicitly.
|
- 3: Static variable `c` is initialized at the first call of this function. After that `c` keeps its value except it is changed explicitly.
|
||||||
- 4-7: Increase `c` by one and if it is zero then the name is "Untitled". If it is a positive integer then the name is "Untitled\<the integer\>", for example, "Untitled1", "Untitled2", and so on.
|
- 4-7: Increases `c` by one and if it is zero then it returns "Untitled". If it is a positive integer then the it returns "Untitled\<the integer\>", for example, "Untitled1", "Untitled2", and so on.
|
||||||
It returns the name.
|
The function `g_strdup_printf` generates a string and it should be freed by `g_free` when it becomes useless.
|
||||||
`g_strdup_printf` generates a string and it should be freed by `g_free` function.
|
The caller of `get_untitled` is in charge of freeing the string.
|
||||||
The caller of `get_untitled` is in charge of freeing the memories of the string.
|
- 37: calls `notebook_page_build` to build the contents of the page.
|
||||||
- 36: call `notebook_page_build` to build the contents of the page.
|
- 10- 26: `notebook_page_build` function.
|
||||||
- 10- 25: `notebook_page_build` function.
|
- 16: Generates GtkScrolledWindow.
|
||||||
- 17-18: Generate GtkScrolledWindow and set `tv` to its child.
|
- 18: Sets the wrap mode of `tv` to GTK_WRAP_WORD_CHAR so that lines are broken between words or graphemes.
|
||||||
- 19-20: Generate GtkLabel, then append it to GtkNotebookPage.
|
- 19: Inserts `tv` to GtkscrolledWindow as a child.
|
||||||
- 21-22: Set "tab-expand" property to TRUE.
|
- 20-21: Generates GtkLabel, then appends it to GtkNotebookPage.
|
||||||
- 23: Set the page to the current page.
|
- 22-23: Sets "tab-expand" property to TRUE.
|
||||||
- 24: Connect "change-file" signal and `file_changed` handler.
|
The function g\_object\_set sets properties on an object.
|
||||||
|
The object is any object derived from GObject.
|
||||||
|
In many cases, an object has its own function to set its properties, but sometimes not.
|
||||||
|
In that case, use g\_object\_set to set the property.
|
||||||
|
- 24: Sets the current page of `nb` to `i` which is the number of the GtkNotebookPage above.
|
||||||
|
- 25: Connects "change-file" signal and `file_changed` handler.
|
||||||
|
|
||||||
## notebook\_page\_new\_with\_file
|
## notebook\_page\_new\_with\_file
|
||||||
|
|
||||||
@@@ tfe5/tfenotebook.c notebook_page_new_with_file
|
@@@ tfe5/tfenotebook.c notebook_page_new_with_file
|
||||||
|
|
||||||
- 9-10: Call `tfe_text_view_new_with_file`.
|
- 9-10: Calls `tfe_text_view_new_with_file`.
|
||||||
If it returns NULL, then do nothing and return because of an error.
|
If the function returns NULL, then it does nothing and returns.
|
||||||
-11-13: Get the filename , build the contents of the page.
|
The return value NULL means that an error has happened.
|
||||||
|
- 11-12: Gets the filename and builds the contents of the page.
|
||||||
|
|
||||||
## notebook\_page\_open
|
## notebook\_page\_open
|
||||||
|
|
||||||
@@@ tfe5/tfenotebook.c open_response notebook_page_open
|
@@@ tfe5/tfenotebook.c open_response notebook_page_open
|
||||||
|
|
||||||
- 19-28: `notebook_page_open` function.
|
- 19-28: `notebook_page_open` function.
|
||||||
- 25: Generate TfeTextView object.
|
- 25: Generates TfeTextView object.
|
||||||
- 26: Connect the signal "open-response" and the handler `open_response`.
|
- 26: Connects the signal "open-response" and the handler `open_response`.
|
||||||
- 27: Call `tfe_text_view_open`.
|
- 27: Calls `tfe_text_view_open`.
|
||||||
It emits "open-response" signal to inform the status after the series of functions run.
|
The function emits an "open-response" signal to inform the status.
|
||||||
- 1-17: `open_response` handler.
|
- 1-17: `open_response` handler.
|
||||||
This is the post-function of `notebook_page_open`.
|
This is the post-function of `notebook_page_open`.
|
||||||
- 6-8: If the status is NOT `TFE_OPEN_RESPONSE_SUCCESS`, cancel what we did in `notebook_page_open`.
|
- 6-8: If the status is NOT `TFE_OPEN_RESPONSE_SUCCESS`, it cancels what it did in `notebook_page_open`.
|
||||||
The object `tv` hasn't been a child widget of some other widget yet.
|
The object `tv` hasn't been a child widget of some other widget yet.
|
||||||
Such object has floating reference.
|
Such object has floating reference.
|
||||||
It needs to do `g_object_ref_sink` and clear the floating reference before `g_object_unref`.
|
You need to call `g_object_ref_sink` first.
|
||||||
- 9-11: If `tfe_text_view_get_file` returns a pointer not to point GFile, then something bad happens. Cancel what we did.
|
Then the floating reference is converted into an ordinary reference.
|
||||||
|
Now you call `g_object_unref` to decrease the reference count by one.
|
||||||
|
- 9-11: If `tfe_text_view_get_file` returns a pointer not to point GFile, then something bad happens.
|
||||||
Sink and unref `tv`.
|
Sink and unref `tv`.
|
||||||
- 12-16: Otherwise, everything is okay.
|
- 12-16: Otherwise, everything is okay.
|
||||||
Get the filename, build the contents of the page.
|
Gets the filename, builds the contents of the page.
|
||||||
|
|
||||||
## notebook\_page\_save
|
## notebook\_page\_save
|
||||||
|
|
||||||
|
@ -89,16 +99,15 @@ Get the filename, build the contents of the page.
|
||||||
## file\_changed handler
|
## file\_changed handler
|
||||||
|
|
||||||
The function `file_changed` is a handler connected to "change-file" signal.
|
The function `file_changed` is a handler connected to "change-file" signal.
|
||||||
If `tv->file` is changed, TfeTextView emits this signal.
|
If the file in TfeTextView is changed, it emits this signal.
|
||||||
This handler changes the label of GtkNotebookPage.
|
This handler changes the label of GtkNotebookPage.
|
||||||
|
|
||||||
@@@ tfe5/tfenotebook.c file_changed
|
@@@ tfe5/tfenotebook.c file_changed
|
||||||
|
|
||||||
- 8: Get GFile from TfeTextView.
|
- 8: Gets GFile from TfeTextView.
|
||||||
- 9: Get GkScrolledWindow which is the parent widget of `tv`.
|
- 9: Gets GkScrolledWindow which is the parent widget of `tv`.
|
||||||
- 10-13: If `file` points GFile, then assign the filename of the GFile into `filename`.
|
- 10-13: If `file` points GFile, then assigns the filename of the GFile into `filename`.
|
||||||
Otherwise (file is NULL), assign untitled string to `filename`.
|
Otherwise (file is NULL), assigns untitled string to `filename`.
|
||||||
- 14-15: Generate a label with the filename and set it into GtkNotebookPage.
|
- 14-15: Generates a label with the filename and inserts it into GtkNotebookPage.
|
||||||
- 16-17: Free `filename` and unref `file`.
|
- 16-17: Unrefs `file` and frees `filename`.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -15,29 +15,29 @@ It connects the command line given by the user and GTK application.
|
||||||
|
|
||||||
@@@ tfe5/tfeapplication.c main
|
@@@ tfe5/tfeapplication.c main
|
||||||
|
|
||||||
- 6: Generate GtkApplication object.
|
- 6: Generates GtkApplication object.
|
||||||
- 8-10: Connect "startup", "activate" and "open signals to their handlers.
|
- 8-10: Connects "startup", "activate" and "open signals to their handlers.
|
||||||
- 12: Run the application.
|
- 12: Runs the application.
|
||||||
- 13-14: release the reference to the application and return the status.
|
- 13-14: releases the reference to the application and returns the status.
|
||||||
|
|
||||||
## startup signal handler
|
## startup signal handler
|
||||||
|
|
||||||
Startup signal is emitted just after the application is generated.
|
Startup signal is emitted just after the application is generated.
|
||||||
What the signal handler needs to do is initialization of the application.
|
What the signal handler needs to do is initialization of the application.
|
||||||
|
|
||||||
- Build the widgets using ui file.
|
- Builds the widgets using ui file.
|
||||||
- Connect button signals and their handlers.
|
- Connects button signals and their handlers.
|
||||||
- Set CSS.
|
- Sets CSS.
|
||||||
|
|
||||||
The handler is as follows.
|
The handler is as follows.
|
||||||
|
|
||||||
@@@ tfe5/tfeapplication.c tfe_startup
|
@@@ tfe5/tfeapplication.c tfe_startup
|
||||||
|
|
||||||
- 12-15: Build widgets using ui file (resource).
|
- 12-15: Builds widgets using ui file (resource).
|
||||||
Connect the top window and the application using `gtk_window_set_application`.
|
Connects the top window and the application using `gtk_window_set_application`.
|
||||||
- 16-23: Get buttons and connect their signals and handlers.
|
- 16-23: Gets buttons and connects their signals and handlers.
|
||||||
- 24: Release the reference to GtkBuilder.
|
- 24: Releases the reference to GtkBuilder.
|
||||||
- 26-31: Set CSS.
|
- 26-31: Sets CSS.
|
||||||
CSS in GTK is similar to CSS in HTML.
|
CSS in GTK is similar to CSS in HTML.
|
||||||
You can set margin, border, padding, color, font and so on with CSS.
|
You can set margin, border, padding, color, font and so on with CSS.
|
||||||
In this program CSS is in line 30.
|
In this program CSS is in line 30.
|
||||||
|
@ -49,12 +49,12 @@ CSS will be explained in the next subsection.
|
||||||
|
|
||||||
CSS is an abbreviation of Cascading Style Sheet.
|
CSS is an abbreviation of Cascading Style Sheet.
|
||||||
It is originally used with HTML to describe the presentation semantics of a document.
|
It is originally used with HTML to describe the presentation semantics of a document.
|
||||||
You might have found that the widgets in GTK is simialr to the window in a browser.
|
You might have found that the widgets in GTK is similar to the window in a browser.
|
||||||
It implies that CSS can also be apllied to GTK windowing system.
|
It implies that CSS can also be applied to GTK windowing system.
|
||||||
|
|
||||||
### CSS nodes, selectors
|
### CSS nodes, selectors
|
||||||
|
|
||||||
The syntax of CSS is as follws.
|
The syntax of CSS is as follows.
|
||||||
|
|
||||||
~~~css
|
~~~css
|
||||||
selector { color: yellow; padding-top: 10px; ...}
|
selector { color: yellow; padding-top: 10px; ...}
|
||||||
|
@ -62,7 +62,7 @@ selector { color: yellow; padding-top: 10px; ...}
|
||||||
|
|
||||||
Every widget has CSS node.
|
Every widget has CSS node.
|
||||||
For example GtkTextView has `textview` node.
|
For example GtkTextView has `textview` node.
|
||||||
If you want to set style to GtkTextView, set "textview" to the selector.
|
If you want to set style to GtkTextView, substitute "textview" for the selector.
|
||||||
|
|
||||||
~~~css
|
~~~css
|
||||||
textview {color: yellow; ...}
|
textview {color: yellow; ...}
|
||||||
|
@ -97,9 +97,9 @@ However, instead, you can add it to GdkDisplay of the window (usually top level
|
||||||
Look at the source file of `startup` handler again.
|
Look at the source file of `startup` handler again.
|
||||||
|
|
||||||
- 28: The display is obtained by `gtk_widget_get_display`.
|
- 28: The display is obtained by `gtk_widget_get_display`.
|
||||||
- 29: Generate GtkCssProvider.
|
- 29: Generates GtkCssProvider.
|
||||||
- 30: Set the CSS into the provider.
|
- 30: Puts the CSS into the provider.
|
||||||
- 31: Add the provider to the display.
|
- 31: Adds the provider to the display.
|
||||||
|
|
||||||
It is possible to add the provider to the context of GtkTextView instead of GdkDiplay.
|
It is possible to add the provider to the context of GtkTextView instead of GdkDiplay.
|
||||||
To do so, rewrite `tfe_text_view_new`.
|
To do so, rewrite `tfe_text_view_new`.
|
||||||
|
@ -122,7 +122,7 @@ tfe_text_view_new (void) {
|
||||||
}
|
}
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
CSS set to the context takes precedence over the one set to the display.
|
CSS in the context takes precedence over CSS in the display.
|
||||||
|
|
||||||
## activate and open handler
|
## activate and open handler
|
||||||
|
|
||||||
|
@ -132,20 +132,20 @@ They just generate a new GtkNotebookPage.
|
||||||
@@@ tfe5/tfeapplication.c tfe_activate tfe_open
|
@@@ tfe5/tfeapplication.c tfe_activate tfe_open
|
||||||
|
|
||||||
- 1-14: `tfe_activate`.
|
- 1-14: `tfe_activate`.
|
||||||
- 8-10: Get GtkNotebook object.
|
- 8-10: Gets GtkNotebook object.
|
||||||
- 12-13: Generate a new GtkNotebookPage and show the window.
|
- 12-13: Generates a new GtkNotebookPage and show the window.
|
||||||
- 16-33: `tfe_open`.
|
- 16-33: `tfe_open`.
|
||||||
- 24-26: Get GtkNotebook object.
|
- 24-26: Gets GtkNotebook object.
|
||||||
- 28-29: Generate GtkNotebookPage with files.
|
- 28-29: Generates GtkNotebookPage with files.
|
||||||
- 30-31: If opening and reading file failed and no GtkNotebookPage has generated, then generate a empty page.
|
- 30-31: If opening and reading file failed and no GtkNotebookPage has generated, then it generates a empty page.
|
||||||
- 32: Show the window.
|
- 32: Shows the window.
|
||||||
|
|
||||||
These codes have become really simple thanks to tfenotebook.c and tfetextview.c.
|
These codes have become really simple thanks to tfenotebook.c and tfetextview.c.
|
||||||
|
|
||||||
## Primary instance
|
## Primary instance
|
||||||
|
|
||||||
Only one GApplication instance can be run at a time per session.
|
Only one GApplication instance can be run at a time per session.
|
||||||
The session is a bit diffcult concept and also platform-dependent, but roughly speaking, it corresponds to a graphical desktop login.
|
The session is a bit difficult concept and also platform-dependent, but roughly speaking, it corresponds to a graphical desktop login.
|
||||||
When you use your PC, you probably login first, then your desktop appears until you log off.
|
When you use your PC, you probably login first, then your desktop appears until you log off.
|
||||||
This is the session.
|
This is the session.
|
||||||
|
|
||||||
|
|
|
@ -59,14 +59,15 @@ It is a good practice for you to add more features.
|
||||||
|
|
||||||
## tfetextview.h
|
## tfetextview.h
|
||||||
|
|
||||||
@@@ tfe5/tfetextview.h
|
@@@ tfetextview/tfetextview.h
|
||||||
|
|
||||||
## tfetextview.c
|
## tfetextview.c
|
||||||
|
|
||||||
@@@ tfe5/tfetextview.c
|
@@@ tfetextview/tfetextview.c
|
||||||
|
|
||||||
## Total number of lines, words and charcters
|
## Total number of lines, words and characters
|
||||||
|
|
||||||
$$$
|
$$$
|
||||||
LANG=C wc tfe5/meson.build tfe5/tfeapplication.c tfe5/tfe.gresource.xml tfe5/tfe.h tfe5/tfenotebook.c tfe5/tfenotebook.h tfe5/tfetextview.c tfe5/tfetextview.h tfe5/tfe.ui
|
LANG=C wc tfe5/meson.build tfe5/tfeapplication.c tfe5/tfe.gresource.xml tfe5/tfe.h tfe5/tfenotebook.c tfe5/tfenotebook.h tfetextview/tfetextview.c tfetextview/tfetextview.h tfe5/tfe.ui
|
||||||
$$$
|
$$$
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ Menus can build a complicated structure thanks to the links of menu items.
|
||||||
|
|
||||||
## GMenuModel, GMenu and GMenuItem
|
## GMenuModel, GMenu and GMenuItem
|
||||||
|
|
||||||
GMenuModel is an abstact object which represents a menu.
|
GMenuModel is an abstract object which represents a menu.
|
||||||
GMenu is a simple implementation of GMenuModel and a child object of GMenuModel.
|
GMenu is a simple implementation of GMenuModel and a child object of GMenuModel.
|
||||||
|
|
||||||
GObjct -- GMenuModel -- GMenu
|
GObjct -- GMenuModel -- GMenu
|
||||||
|
@ -49,7 +49,7 @@ GMenuItem and Gmenu (or GMenuModel) don't have a parent-child relationship.
|
||||||
Usually, GMenuItem has attributes.
|
Usually, GMenuItem has attributes.
|
||||||
One of the attributes is label.
|
One of the attributes is label.
|
||||||
For example, there is a menu item which has "Edit" label in the first diagram in this section.
|
For example, there is a menu item which has "Edit" label in the first diagram in this section.
|
||||||
"Cut", "Copy", "Paste" and "Select All" are also the lables of menu items.
|
"Cut", "Copy", "Paste" and "Select All" are also the labels of menu items.
|
||||||
Other attributes will be explained later.
|
Other attributes will be explained later.
|
||||||
|
|
||||||
Some menu items have a link to another GMenu.
|
Some menu items have a link to another GMenu.
|
||||||
|
@ -129,18 +129,18 @@ You don' t need to care about it.
|
||||||
It is the fourth parameter in the `g_signal_connect` (line 17) that has connected the action and the handler.
|
It is the fourth parameter in the `g_signal_connect` (line 17) that has connected the action and the handler.
|
||||||
- 6: A function `g_application_quit` immediately quits the application.
|
- 6: A function `g_application_quit` immediately quits the application.
|
||||||
- 9-33: `on_activate` is a handler of "activate" signal on GtkApplication.
|
- 9-33: `on_activate` is a handler of "activate" signal on GtkApplication.
|
||||||
- 11-13: Generate a GtkApplicationWindow and set a pointer to it to `win`. And set the title and default size.
|
- 11-13: Generates a GtkApplicationWindow and assigns a pointer to it to `win`. And sets the title and default size.
|
||||||
- 15: Generate GSimpleAction `act_quit`.
|
- 15: Generates GSimpleAction `act_quit`.
|
||||||
It is stateless.
|
It is stateless.
|
||||||
The first argument of `g_simple_action_new` is a name of the action and the second argument is a parameter.
|
The first argument of `g_simple_action_new` is a name of the action and the second argument is a parameter.
|
||||||
If you don't need the parameter, set it `NULL`.
|
If you don't need the parameter, pass `NULL`.
|
||||||
Therefore, `act_quit` has a name "quit" and no parameter.
|
Therefore, `act_quit` has a name "quit" and no parameter.
|
||||||
- 16: Add the action to GtkApplication `app`.
|
- 16: Adds the action to GtkApplication `app`.
|
||||||
GtkApplication implements an interface GActionMap and GActionGroup.
|
GtkApplication implements an interface GActionMap and GActionGroup.
|
||||||
And GtkApplication can have a group of actions and actions are added by the function `g_action_map_add_action`.
|
And GtkApplication can have a group of actions and actions are added by the function `g_action_map_add_action`.
|
||||||
This function is described in GMenuModel section in GIO API reference.
|
This function is described in GMenuModel section in GIO API reference.
|
||||||
- 17: Connect "activate" signal of the action and the handler `quit_activated`.
|
- 17: Connects "activate" signal of the action and the handler `quit_activated`.
|
||||||
- 19-22: Generate GMenu and GMenuItem.
|
- 19-22: Generates GMenu and GMenuItem.
|
||||||
`menubar` and `menu` are GMenu.
|
`menubar` and `menu` are GMenu.
|
||||||
`menu_item_menu` and `menu_item_quit` are GMenuItem.
|
`menu_item_menu` and `menu_item_quit` are GMenuItem.
|
||||||
`menu_item_menu` has a label "Menu" and no action.
|
`menu_item_menu` has a label "Menu" and no action.
|
||||||
|
@ -148,19 +148,19 @@ This function is described in GMenuModel section in GIO API reference.
|
||||||
The second argument "app.quit" is a combination of "app" and "quit".
|
The second argument "app.quit" is a combination of "app" and "quit".
|
||||||
"app" is a prefix and it means that the action belongs to GtkApplication. "quit" is the name of the action.
|
"app" is a prefix and it means that the action belongs to GtkApplication. "quit" is the name of the action.
|
||||||
Therefore, it points the action which belongs to GtkApplication and has the name "quit" -- it is `act_quit`.
|
Therefore, it points the action which belongs to GtkApplication and has the name "quit" -- it is `act_quit`.
|
||||||
- 23-24: Append `act_quit` to `menu`.
|
- 23-24: Appends `act_quit` to `menu`.
|
||||||
As I mentioned before, all the attribute and link values are copied and used to form a new item within `menu`.
|
As I mentioned before, all the attribute and link values are copied and used to form a new item within `menu`.
|
||||||
Therefore after the appending, `menu` has a copy of `act_quit` in itself and `act_quit` is no longer needed.
|
Therefore after the appending, `menu` has a copy of `act_quit` in itself and `act_quit` is no longer needed.
|
||||||
It is freed by `g_object_unref`.
|
It is freed by `g_object_unref`.
|
||||||
- 25: Set a submenu link to `menu_item_menu`.
|
- 25: Sets a submenu link to `menu_item_menu`.
|
||||||
And the link points the GMenu `menu`.
|
And the link points the GMenu `menu`.
|
||||||
- 26-27: Append `menu_item_menu` to `menubar`.
|
- 26-27: Appends `menu_item_menu` to `menubar`.
|
||||||
Then free `menu_item_menu`.
|
Then frees `menu_item_menu`.
|
||||||
GMenu and GMenuItem are connected and finally a menu is made up.
|
GMenu and GMenuItem are connected and finally a menu is made up.
|
||||||
The structure of the menu is shown in the diagram below.
|
The structure of the menu is shown in the diagram below.
|
||||||
- 29: The menu is set to GtkApplication.
|
- 29: The menu is inserted to GtkApplication.
|
||||||
- 30: Set GtkApplicationWindow to show the menubar.
|
- 30: Sets GtkApplicationWindow to show the menubar.
|
||||||
- 31: Show the window.
|
- 31: Shows the window.
|
||||||
|
|
||||||
![menu and action](../image/menu1.png){width=12.555cm height=3.285cm}
|
![menu and action](../image/menu1.png){width=12.555cm height=3.285cm}
|
||||||
|
|
||||||
|
|
|
@ -45,10 +45,10 @@ The second argument is called detailed action.
|
||||||
Detailed action has three parts, prefix, action name and target.
|
Detailed action has three parts, prefix, action name and target.
|
||||||
"win.fullscreen" means that the prefix is "win", the action name is "fullscreen" and there's no target.
|
"win.fullscreen" means that the prefix is "win", the action name is "fullscreen" and there's no target.
|
||||||
The prefix says that the action belongs to the window.
|
The prefix says that the action belongs to the window.
|
||||||
- connect the action `act_fullscreen` and the "change-state" signal handler `fullscreen_changed`.
|
- connects the action `act_fullscreen` and the "change-state" signal handler `fullscreen_changed`.
|
||||||
If the fullscreen menu is clicked, then the corresponding action `act_fullscreen` is activated.
|
If the fullscreen menu is clicked, then the corresponding action `act_fullscreen` is activated.
|
||||||
But no handler is connected to "activate" signal.
|
But no handler is connected to "activate" signal.
|
||||||
Then, the default behaviour for boolean-stated actions with a NULL parameter type like `act_fullscreen` is to toggle them via the “change-state” signal.
|
Then, the default behavior for boolean-stated actions with a NULL parameter type like `act_fullscreen` is to toggle them via the “change-state” signal.
|
||||||
|
|
||||||
The following is the "change-state" signal handler.
|
The following is the "change-state" signal handler.
|
||||||
|
|
||||||
|
@ -67,20 +67,20 @@ fullscreen_changed(GSimpleAction *action, GVariant *value, gpointer win) {
|
||||||
The first parameter is the action which emits the "change-state" signal.
|
The first parameter is the action which emits the "change-state" signal.
|
||||||
The second parameter is the value of the state of the action.
|
The second parameter is the value of the state of the action.
|
||||||
But it is toggled because of no "activate" signal handler.
|
But it is toggled because of no "activate" signal handler.
|
||||||
Ther third parameter is a user data which is set in `g_signal_connect`.
|
The third parameter is a user data which is set in `g_signal_connect`.
|
||||||
- If the value is boolean type and `TRUE`, then maximize the window.
|
- If the value is boolean type and `TRUE`, then maximize the window.
|
||||||
Otherwise unmaximize.
|
Otherwise unmaximize.
|
||||||
- Set `value` to the state of the action.
|
- Sets the state of the action to `value`.
|
||||||
Note: the second argument was the toggled state value, but at this stage the state of the action has the original value.
|
Note: the second argument was the toggled state value, but at this stage the state of the action has the original value.
|
||||||
So, you need to set the new value by `g_simple_action_set_state`.
|
So, you need to set the state to the new value by `g_simple_action_set_state`.
|
||||||
|
|
||||||
You can use "activate" signal instead ot "change-state" signal, or both signals.
|
You can use "activate" signal instead of "change-state" signal, or both signals.
|
||||||
But the way above is the simplest and best.
|
But the way above is the simplest and best.
|
||||||
|
|
||||||
### GVariant
|
### GVariant
|
||||||
|
|
||||||
GVarient can contain boolean, string or other simple type values.
|
GVarient can contain boolean, string or other simple type values.
|
||||||
For example, the following program set TRUE to `value` whose type is GVariant.
|
For example, the following program assigns TRUE to `value` whose type is GVariant.
|
||||||
|
|
||||||
~~~C
|
~~~C
|
||||||
GVariant *value = g_variant_new_boolean (TRUE);
|
GVariant *value = g_variant_new_boolean (TRUE);
|
||||||
|
@ -155,10 +155,10 @@ The second argument is a detailed action.
|
||||||
Its prefix is "win", action name is "color" and target is "red".
|
Its prefix is "win", action name is "color" and target is "red".
|
||||||
Target is sent to the action as a parameter.
|
Target is sent to the action as a parameter.
|
||||||
The same goes for `menu_item_green` and `menu_item_blue`.
|
The same goes for `menu_item_green` and `menu_item_blue`.
|
||||||
- connect the action `act_color` and the "activate" signal handler `color_activate`.
|
- connects the action `act_color` and the "activate" signal handler `color_activate`.
|
||||||
If one of the three menus is clicked, then the action `act_color` is activated with a parameter to which the menu item gives its target.
|
If one of the three menus is clicked, then the action `act_color` is activated with a parameter to which the menu item gives its target.
|
||||||
No handler is connected to "change-state" signal.
|
No handler is connected to "change-state" signal.
|
||||||
Then the default behaviour is to call `g_simple_action_set_state()` to set the state to the requested value.
|
Then the default behavior is to call `g_simple_action_set_state()` to set the state to the requested value.
|
||||||
|
|
||||||
The following is the "activate" signal handler.
|
The following is the "activate" signal handler.
|
||||||
|
|
||||||
|
@ -181,10 +181,10 @@ The third parameter is a user data which is set in `g_signal_connect`.
|
||||||
- `color` is a CSS string generated by `g_strdup_printf`.
|
- `color` is a CSS string generated by `g_strdup_printf`.
|
||||||
The parameter of `g_strdup_printf` is the same as printf C standard function.
|
The parameter of `g_strdup_printf` is the same as printf C standard function.
|
||||||
`g_variant_get_string` get the string contained in `parameter`.
|
`g_variant_get_string` get the string contained in `parameter`.
|
||||||
- Set the color to the css provider.
|
- Sets the color of the css provider.
|
||||||
- Free the string `color`.
|
- Frees the string `color`.
|
||||||
- Change the state by `g_action_change_state`.
|
- Changes the state by `g_action_change_state`.
|
||||||
The function just set the parameter to the state of the action by `g_simple_action_set_state`.
|
The function just sets the state of the action to the parameter by `g_simple_action_set_state`.
|
||||||
Therefore, you can use `g_simple_action_set_state` instead of `g_action_change_state`.
|
Therefore, you can use `g_simple_action_set_state` instead of `g_action_change_state`.
|
||||||
|
|
||||||
Note: If you have set a "change-state" signal handler, `g_action_change_state` will emit "change-state" signal instead of calling `g_simple_action_set_state`.
|
Note: If you have set a "change-state" signal handler, `g_action_change_state` will emit "change-state" signal instead of calling `g_simple_action_set_state`.
|
||||||
|
@ -210,7 +210,7 @@ It finally output the string "s".
|
||||||
It uses a type string "s" which means string.
|
It uses a type string "s" which means string.
|
||||||
- `g_variant_type_peek_string` takes a peek at `vtype`.
|
- `g_variant_type_peek_string` takes a peek at `vtype`.
|
||||||
It is the string "s" given at the generation time.
|
It is the string "s" given at the generation time.
|
||||||
- print the string to the terminal.
|
- prints the string to the terminal.
|
||||||
|
|
||||||
## Example code
|
## Example code
|
||||||
The following code includes stateful actions above.
|
The following code includes stateful actions above.
|
||||||
|
@ -232,7 +232,7 @@ The code is as follows.
|
||||||
- 5-26: Signal handlers.
|
- 5-26: Signal handlers.
|
||||||
They have been explained in this section.
|
They have been explained in this section.
|
||||||
- 30-36: `win` and `lb` are GtkApplicationWindow and GtkLabel respectively.
|
- 30-36: `win` and `lb` are GtkApplicationWindow and GtkLabel respectively.
|
||||||
`win` has a title "menu2" and its defaust size is 400x300.
|
`win` has a title "menu2" and its default size is 400x300.
|
||||||
`lb` is named as "lb".
|
`lb` is named as "lb".
|
||||||
The name is used in CSS.
|
The name is used in CSS.
|
||||||
`lb` is set to `win` as a child.
|
`lb` is set to `win` as a child.
|
||||||
|
@ -243,7 +243,7 @@ It has a toggle state.
|
||||||
- stateful and has a parameter.
|
- stateful and has a parameter.
|
||||||
Parameter is a string type.
|
Parameter is a string type.
|
||||||
- stateless and has no parameter.
|
- stateless and has no parameter.
|
||||||
- 45-54: Generate GMenu and GMenuItem.
|
- 45-54: Generates GMenu and GMenuItem.
|
||||||
There are three sections.
|
There are three sections.
|
||||||
- 56-61: Signals are connected to handlers.
|
- 56-61: Signals are connected to handlers.
|
||||||
And actions are added to GActionMap.
|
And actions are added to GActionMap.
|
||||||
|
@ -252,15 +252,15 @@ they are added to `win`.
|
||||||
GtkApplicationWindow implements GActionModel interface like GtkApplication.
|
GtkApplicationWindow implements GActionModel interface like GtkApplication.
|
||||||
`act_quit` has "app" prefix and belongs to GtkApplication.
|
`act_quit` has "app" prefix and belongs to GtkApplication.
|
||||||
It is added to `app`.
|
It is added to `app`.
|
||||||
- 63-77: Connect and build the menus.
|
- 63-77: Connects and builds the menus.
|
||||||
Useless GMenuItem are freed.
|
Useless GMenuItem are freed.
|
||||||
- 79-80: GMenuModel `menubar` is set to `app`.
|
- 79-80: GMenuModel `menubar` is inserted to `app`.
|
||||||
Set show menubar property to `TRUE` in `win`.
|
Sets show menubar property to `TRUE` in `win`.
|
||||||
Note: `gtk_application_window_set_show_menubar` generates GtkPopoverMenubar from GMenuModel.
|
Note: `gtk_application_window_set_show_menubar` generates GtkPopoverMenubar from GMenuModel.
|
||||||
This is a different point between Gtk3 and Gtk4.
|
This is a different point between Gtk3 and Gtk4.
|
||||||
And you can use GtkPopoverMenubar directly and set it as a descendant widget of the window.
|
And you can use GtkPopoverMenubar directly and set it as a descendant widget of the window.
|
||||||
You may use GtkBox as a child widget of the window and set GtkPopoverMenubar as the first child of the box.
|
You may use GtkBox as a child widget of the window and insert GtkPopoverMenubar as the first child of the box.
|
||||||
- 82-87: Set CSS.
|
- 82-87: Sets CSS.
|
||||||
`provider` is GtkCssProvider which is defined in line three as a static variable.
|
`provider` is GtkCssProvider which is defined in line three as a static variable.
|
||||||
Its CSS data is:
|
Its CSS data is:
|
||||||
`label#lb {background-color: red;}`.
|
`label#lb {background-color: red;}`.
|
||||||
|
@ -273,5 +273,5 @@ The style is surrounded by open and close braces.
|
||||||
The style is applied to GtkLabel which has a name "lb".
|
The style is applied to GtkLabel which has a name "lb".
|
||||||
Other GtkLabel have no effect from this.
|
Other GtkLabel have no effect from this.
|
||||||
The provider is added to GdkDisplay.
|
The provider is added to GdkDisplay.
|
||||||
- 90: Show the window.
|
- 90: Shows the window.
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ The file starts and ends with interface tag.
|
||||||
|
|
||||||
`menu` tag corresponds to GMenu object.
|
`menu` tag corresponds to GMenu object.
|
||||||
`id` attribute defines the name of the object.
|
`id` attribute defines the name of the object.
|
||||||
It will be refered by GtkBuilder.
|
It will be referred by GtkBuilder.
|
||||||
|
|
||||||
~~~xml
|
~~~xml
|
||||||
<submenu>
|
<submenu>
|
||||||
|
@ -69,7 +69,7 @@ The following is the ui file of the menu in `menu3`.
|
||||||
|
|
||||||
@@@ menu3/menu3.ui
|
@@@ menu3/menu3.ui
|
||||||
|
|
||||||
The ui file is converted to the resource by the resouce compiler `glib-compile-resouces` with xml file below.
|
The ui file is converted to the resource by the resource compiler `glib-compile-resouces` with xml file below.
|
||||||
|
|
||||||
@@@ menu3/menu3.gresource.xml
|
@@@ menu3/menu3.gresource.xml
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ gtk_application_set_menubar (GTK_APPLICATION (app), menubar);
|
||||||
g_object_unref (builder);
|
g_object_unref (builder);
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
It is important that `builder` is unreferred after the GMenuModel `menubar` is set to the application.
|
It is important that `builder` is unreferred after the GMenuModel `menubar` is inserted to the application.
|
||||||
If you do it before setting, bad thing will happen -- your computer might freeze.
|
If you do it before setting, bad thing will happen -- your computer might freeze.
|
||||||
|
|
||||||
## Action entry
|
## Action entry
|
||||||
|
@ -133,9 +133,9 @@ g_action_map_add_action_entries (G_ACTION_MAP (app), app_entries,
|
||||||
|
|
||||||
The code above does:
|
The code above does:
|
||||||
|
|
||||||
- Build the "quit" action
|
- Builds the "quit" action
|
||||||
- Connect the action and the "activate" signal handler `quit_activate`
|
- Connects the action and the "activate" signal handler `quit_activate`
|
||||||
- Add the action to the action map `app`.
|
- Adds the action to the action map `app`.
|
||||||
|
|
||||||
The same goes for the other actions.
|
The same goes for the other actions.
|
||||||
|
|
||||||
|
@ -149,12 +149,12 @@ g_action_map_add_action_entries (G_ACTION_MAP (win), win_entries,
|
||||||
~~~
|
~~~
|
||||||
The code above does:
|
The code above does:
|
||||||
|
|
||||||
- Build a "fullscreen" action and "color" action.
|
- Builds a "fullscreen" action and "color" action.
|
||||||
- Connect the "fullscreen" action and the "change-state" signal handler `fullscreen_changed`
|
- Connects the "fullscreen" action and the "change-state" signal handler `fullscreen_changed`
|
||||||
- Its initial state is set to FALSE.
|
- Its initial state is set to FALSE.
|
||||||
- Connect the "color" action and the "activate" signal handler `color_activate`
|
- Connects the "color" action and the "activate" signal handler `color_activate`
|
||||||
- Its parameter type is string and the initial value is "red".
|
- Its parameter type is string and the initial value is "red".
|
||||||
- Add the actions to the action map `win`.
|
- Adds the actions to the action map `win`.
|
||||||
|
|
||||||
## Example code
|
## Example code
|
||||||
|
|
||||||
|
|
|
@ -15,15 +15,15 @@ In this section, I will explain:
|
||||||
Cairo is a two dimensional graphics library.
|
Cairo is a two dimensional graphics library.
|
||||||
First, you need to know surface, source, mask, destination, cairo context and transformation.
|
First, you need to know surface, source, mask, destination, cairo context and transformation.
|
||||||
|
|
||||||
- surface represents an image.
|
- Surface represents an image.
|
||||||
It is like a canvas.
|
It is like a canvas.
|
||||||
We can draw shapes and images with different colors on surfaces.
|
We can draw shapes and images with different colors on surfaces.
|
||||||
- source pattern, or simply source, is a kind of paint, which will be transfered to destination surface by cairo functions.
|
- Source pattern, or simply source, is a kind of paint, which will be transferred to destination surface by cairo functions.
|
||||||
- mask is image mask used in the transference.
|
- Mask is image mask used in the transference.
|
||||||
- destination is a target surface.
|
- Destination is a target surface.
|
||||||
- cairo context manages the transference from source to destination through mask with its functions.
|
- Cairo context manages the transference from source to destination through mask with its functions.
|
||||||
For example, `cairo_stroke` is a function to draw a path to the destination by the transference.
|
For example, `cairo_stroke` is a function to draw a path to the destination by the transference.
|
||||||
- transformation is applied before the transfer completes.
|
- Transformation is applied before the transfer completes.
|
||||||
The transformation is called affine, which is a mathematics terminology, and represented by matrix multiplication and vector addition.
|
The transformation is called affine, which is a mathematics terminology, and represented by matrix multiplication and vector addition.
|
||||||
Scaling, rotation, reflection, shearing and translation are examples of affine transformation.
|
Scaling, rotation, reflection, shearing and translation are examples of affine transformation.
|
||||||
In this section, we don't use it.
|
In this section, we don't use it.
|
||||||
|
@ -35,8 +35,8 @@ Therefore, the coordinate in source and mask is the same as the coordinate in de
|
||||||
The instruction is as follows:
|
The instruction is as follows:
|
||||||
|
|
||||||
1. Create a surface.
|
1. Create a surface.
|
||||||
This will be a destnation.
|
This will be a destination.
|
||||||
2. Create a cairo context with the surface and set it to the destination.
|
2. Create a cairo context with the surface and the surface will be the destination of the context.
|
||||||
3. Create a source pattern within the context.
|
3. Create a source pattern within the context.
|
||||||
4. Create paths, which are lines, rectangles, arcs, texts or more complicated shapes, to generate a mask.
|
4. Create paths, which are lines, rectangles, arcs, texts or more complicated shapes, to generate a mask.
|
||||||
5. Use drawing operator such as `cairo_stroke` to transfer the paint in the source to the destination.
|
5. Use drawing operator such as `cairo_stroke` to transfer the paint in the source to the destination.
|
||||||
|
@ -46,31 +46,31 @@ Here's a simple example code that draws a small square and save it as a png file
|
||||||
|
|
||||||
@@@ misc/cairo.c
|
@@@ misc/cairo.c
|
||||||
|
|
||||||
- 1: Include the header file of cairo.
|
- 1: Includes the header file of cairo.
|
||||||
- 12: `cairo_image_surface_create` creates an image surface.
|
- 12: `cairo_image_surface_create` creates an image surface.
|
||||||
`CAIRO_FORMAT_RGB24` is a constant which means that each pixel has red, green and blue data.
|
`CAIRO_FORMAT_RGB24` is a constant which means that each pixel has red, green and blue data.
|
||||||
Each data has 8 bit quantity.
|
Each data has 8 bit quantity.
|
||||||
Modern displays have this type of color depth.
|
Modern displays have this type of color depth.
|
||||||
Width and hieight are pixels and given as integers.
|
Width and height are pixels and given as integers.
|
||||||
- 13: Create cairo context.
|
- 13: Creates cairo context.
|
||||||
The surface given as an argument will be set to the destination of the context.
|
The surface given as an argument will be the destination of the context.
|
||||||
- 17: `cairo_set_source_rgb` creates a source pattern, which is a solid white paint.
|
- 17: `cairo_set_source_rgb` creates a source pattern, which is a solid white paint.
|
||||||
The second to fourth argument is red, green and blue color depth respectively.
|
The second to fourth argument is red, green and blue color depth respectively.
|
||||||
Their type is float and the values are between zero and one.
|
Their type is float and the values are between zero and one.
|
||||||
(0,0,0) is black and (1,1,1) is white.
|
(0,0,0) is black and (1,1,1) is white.
|
||||||
- 18: `cairo_paint` copies everywhere in the source to destination.
|
- 18: `cairo_paint` copies everywhere in the source to destination.
|
||||||
The destination is filled with white pixels by this command.
|
The destination is filled with white pixels by this command.
|
||||||
- 20: Set the source color to black.
|
- 20: Sets the source color to black.
|
||||||
- 21: `cairo_set_line_width` set the width of lines.
|
- 21: `cairo_set_line_width` set the width of lines.
|
||||||
In this case, the line width is set to two pixels.
|
In this case, the line width is set to two pixels.
|
||||||
(It is because the transformation is identity.
|
(It is because the transformation is identity.
|
||||||
If we set it different one, for example scaling with the factor three, the actual width in destnation is six (2x3=6) pixels.)
|
If the transformation isn't identity, for example scaling with the factor three, the actual width in destination will be six (2x3=6) pixels.)
|
||||||
- 22: Draw a rectangle (square).
|
- 22: Draws a rectangle (square).
|
||||||
The top-left coordinate is (width/2.0-20.0, height/2.0-20.0) and the width and height have the same length 40.0.
|
The top-left coordinate is (width/2.0-20.0, height/2.0-20.0) and the width and height have the same length 40.0.
|
||||||
- 23: `cairo_stroke` transfer the source to destnation through the rectangle in mask.
|
- 23: `cairo_stroke` transfer the source to destination through the rectangle in mask.
|
||||||
- 26: Output the image to a png file `rectangle.png`.
|
- 26: Outputs the image to a png file `rectangle.png`.
|
||||||
- 27: Destroy the context. At the same time the source is destroyed.
|
- 27: Destroys the context. At the same time the source is destroyed.
|
||||||
- 28: Destroy the destnation surface.
|
- 28: Destroys the destination surface.
|
||||||
|
|
||||||
To compile this, type the following.
|
To compile this, type the following.
|
||||||
|
|
||||||
|
@ -90,10 +90,10 @@ The following is a very simple example.
|
||||||
The function `main` is almost same as before.
|
The function `main` is almost same as before.
|
||||||
The two functions `on_activate` and `draw_function` is important in this example.
|
The two functions `on_activate` and `draw_function` is important in this example.
|
||||||
|
|
||||||
- 16: Generate a GtkDrawingArea object.
|
- 16: Generates a GtkDrawingArea object.
|
||||||
- 20,21: Set the width and height of the contents of the GtkDrawingArea widget.
|
- 20,21: Sets the width and height of the contents of the GtkDrawingArea widget.
|
||||||
These width and height is the size of the destination surface of the cairo context provided by the widget.
|
These width and height is the size of the destination surface of the cairo context provided by the widget.
|
||||||
- 22: Set a drawng function to the widget.
|
- 22: Sets a drawing function of the widget.
|
||||||
GtkDrawingArea widget uses the function to draw the contents of itself whenever its necessary.
|
GtkDrawingArea widget uses the function to draw the contents of itself whenever its necessary.
|
||||||
For example, when a user drag a mouse pointer and resize a top level window, GtkDrawingArea also changes the size.
|
For example, when a user drag a mouse pointer and resize a top level window, GtkDrawingArea also changes the size.
|
||||||
Then, the whole window needs to be redrawn.
|
Then, the whole window needs to be redrawn.
|
||||||
|
@ -106,16 +106,16 @@ The drawing function has five parameters.
|
||||||
The first parameter is the GtkDrawingArea widget which calls the drawing function.
|
The first parameter is the GtkDrawingArea widget which calls the drawing function.
|
||||||
However, you can't change any properties, for example `content-width` or `content-height`, in this function.
|
However, you can't change any properties, for example `content-width` or `content-height`, in this function.
|
||||||
The second parameter is a cairo context given by the widget.
|
The second parameter is a cairo context given by the widget.
|
||||||
The destnation surface of the context is connected to the contents of the widget.
|
The destination surface of the context is connected to the contents of the widget.
|
||||||
What you draw to this surface will appear in the widget on the screen.
|
What you draw to this surface will appear in the widget on the screen.
|
||||||
The third and fourth paranmeters are the size of the destination surface.
|
The third and fourth parameters are the size of the destination surface.
|
||||||
|
|
||||||
- 3-11: The drawing function.
|
- 3-11: The drawing function.
|
||||||
- 4-5: Set the source to be white and paint the destination white.
|
- 4-5: Sets the source to be white and paint the destination white.
|
||||||
- 7: Set the line width to be 2.
|
- 7: Sets the line width to be 2.
|
||||||
- 8: Set the source to be black.
|
- 8: Sets the source to be black.
|
||||||
- 9: Add a rectangle to the mask.
|
- 9: Adds a rectangle to the mask.
|
||||||
- 10: Draw the rectangle with black color to the destination.
|
- 10: Draws the rectangle with black color to the destination.
|
||||||
|
|
||||||
Compile and run it, then a window with a black rectangle (square) appears.
|
Compile and run it, then a window with a black rectangle (square) appears.
|
||||||
Try resizing the window.
|
Try resizing the window.
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# Installation of gtk4 to linux distributions
|
# Installation of gtk4 to linux distributions
|
||||||
|
|
||||||
This section describes how to install gtk4 into linux distributions.
|
This section describes how to install gtk4 into linux distributions.
|
||||||
However, I only have an experience to install it to ubuntu 20.10.
|
However, I only have an experience to install it to Ubuntu 20.10.
|
||||||
Probably you need more than the explanation below.
|
Probably you need more than the explanation below.
|
||||||
|
|
||||||
This tutorial including this section is without any warranty.
|
This tutorial including this section is without any warranty.
|
||||||
|
@ -22,9 +22,9 @@ If you want to install it in the system area, `/opt/gtk4` is one of good choices
|
||||||
[Gtk4 API Reference](https://gnome.pages.gitlab.gnome.org/gtk/gtk/gtk-building.html) gives an installation example to `/opt/gtk4`.
|
[Gtk4 API Reference](https://gnome.pages.gitlab.gnome.org/gtk/gtk/gtk-building.html) gives an installation example to `/opt/gtk4`.
|
||||||
|
|
||||||
Don't install it to `/usr/local` which is the default.
|
Don't install it to `/usr/local` which is the default.
|
||||||
It is used by ubuntu applications, which are not build on gtk4.
|
It is used by Ubuntu applications, which are not build on gtk4.
|
||||||
Therefore, the risk is high and probably bad things will happen.
|
Therefore, the risk is high and probably bad things will happen.
|
||||||
Actually I did it and I needed to reinstall ubuntu.
|
Actually I did it and I needed to reinstall Ubuntu.
|
||||||
|
|
||||||
## Glib installation
|
## Glib installation
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ or
|
||||||
|
|
||||||
$ source env.sh
|
$ source env.sh
|
||||||
|
|
||||||
This command carries out the commands in `env.sh` and changes the environment variables above in the corrent shell.
|
This command carries out the commands in `env.sh` and changes the environment variables above in the current shell.
|
||||||
|
|
||||||
## Pango installation
|
## Pango installation
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ It installs Pnago-1.0.gir under `$HOME/local/share/gir-1.0`.
|
||||||
If you installed pango without `--prefix` option, then it would be located at `/usr/local/share/gir-1.0`.
|
If you installed pango without `--prefix` option, then it would be located at `/usr/local/share/gir-1.0`.
|
||||||
This directory (/usr/local/share) is used by applications.
|
This directory (/usr/local/share) is used by applications.
|
||||||
They find the directory by the environment variable `XDG_DATA_DIRS`.
|
They find the directory by the environment variable `XDG_DATA_DIRS`.
|
||||||
It is a text file which keep the list of 'share' directoryes like `/usr/share`, `usr/local/share` and so on.
|
It is a text file which keep the list of 'share' directories like `/usr/share`, `usr/local/share` and so on.
|
||||||
Now `$HOME/local/share` needs to be added to `XDG_DATA_DIRS`, or error will occur in the later compilation.
|
Now `$HOME/local/share` needs to be added to `XDG_DATA_DIRS`, or error will occur in the later compilation.
|
||||||
|
|
||||||
$ export XDG_DATA_DIRS=$HOME/local/share:$XDG_DATA_DIRS
|
$ export XDG_DATA_DIRS=$HOME/local/share:$XDG_DATA_DIRS
|
||||||
|
|
|
@ -43,7 +43,7 @@ It has "name" attribute which is a signal name and "handler" attribute which is
|
||||||
Options "-WI, --export-dynamic" CFLAG is necessary when you compile the application.
|
Options "-WI, --export-dynamic" CFLAG is necessary when you compile the application.
|
||||||
You can achieve this by adding "export_dynamic: true" argument to executable function in `meson.build`.
|
You can achieve this by adding "export_dynamic: true" argument to executable function in `meson.build`.
|
||||||
And be careful that the handler must be defined without 'static' class.
|
And be careful that the handler must be defined without 'static' class.
|
||||||
- 54-76: Put GtkScrolledWindow and GtkDrawingArea into GtkBox.
|
- 54-76: Puts GtkScrolledWindow and GtkDrawingArea into GtkBox.
|
||||||
GtkBox has "homogeneous property with TRUE value, so the two children have the same width in the box.
|
GtkBox has "homogeneous property with TRUE value, so the two children have the same width in the box.
|
||||||
TfeTextView is a child of GtkScrolledWindow.
|
TfeTextView is a child of GtkScrolledWindow.
|
||||||
|
|
||||||
|
@ -70,10 +70,10 @@ Color.h just includes tfetextview.h.
|
||||||
This is the main file.
|
This is the main file.
|
||||||
It deals with:
|
It deals with:
|
||||||
|
|
||||||
- Build widgets by GtkBuilder.
|
- Building widgets by GtkBuilder.
|
||||||
- Set drawing function to GtkDrawingArea.
|
- Seting a drawing function of GtkDrawingArea.
|
||||||
And connect a handler to "resize" signal on GtkDrawingArea.
|
And connecting a handler to "resize" signal on GtkDrawingArea.
|
||||||
- Implement each call back functions.
|
- Implementing each call back functions.
|
||||||
Particularly, `Run` signal handler is the point in this program.
|
Particularly, `Run` signal handler is the point in this program.
|
||||||
|
|
||||||
The following is `colorapplication.c`.
|
The following is `colorapplication.c`.
|
||||||
|
@ -84,18 +84,18 @@ The following is `colorapplication.c`.
|
||||||
The application ID is "com.github.ToshioCP.color".
|
The application ID is "com.github.ToshioCP.color".
|
||||||
`G_APPLICATION_FLAGS_NONE` is specified so no open signal handler is necessary.
|
`G_APPLICATION_FLAGS_NONE` is specified so no open signal handler is necessary.
|
||||||
- 86-106: Startup handler.
|
- 86-106: Startup handler.
|
||||||
- 91-96: Build widgets.
|
- 91-96: Builds widgets.
|
||||||
The pointers of the top window, TfeTextView and GtkDrawingArea objects are stored to static variables `win`, `tv` and `da` respectively.
|
The pointers of the top window, TfeTextView and GtkDrawingArea objects are stored to static variables `win`, `tv` and `da` respectively.
|
||||||
This is because these objects are often used in handlers.
|
This is because these objects are often used in handlers.
|
||||||
They never be rewritten so they're thread safe.
|
They never be rewritten so they're thread safe.
|
||||||
- 97: connect "resize" signal and the handler.
|
- 97: connects "resize" signal and the handler.
|
||||||
- 98: set the drawing function.
|
- 98: sets the drawing function.
|
||||||
- 81-84: Activate handler, which just show the widgets.
|
- 81-84: Activates handler, which just shows the widgets.
|
||||||
- 73-79: The drawing function.
|
- 73-79: The drawing function.
|
||||||
It just copy `surface` to destination.
|
It just copies `surface` to destination.
|
||||||
- 65-71: Resize handler.
|
- 65-71: Resize handler.
|
||||||
Re-create the surface to fit the width and height of the drawing area and paint by calling the function `run`.
|
Re-creates the surface to fit the width and height of the drawing area and paints by calling the function `run`.
|
||||||
- 58-63: Close handler.
|
- 58-63: Closes the handler.
|
||||||
It destroys `surface` if it exists.
|
It destroys `surface` if it exists.
|
||||||
Then it destroys the top window and quits the application.
|
Then it destroys the top window and quits the application.
|
||||||
- 48-56: Open and save handler.
|
- 48-56: Open and save handler.
|
||||||
|
@ -108,9 +108,9 @@ It is important to know that the drawing function is called when it is necessary
|
||||||
For example, when another window is moved and uncovers part of the widget, or when the window containing it is resized.
|
For example, when another window is moved and uncovers part of the widget, or when the window containing it is resized.
|
||||||
But repaint of `surface` is not automatically notified to gtk.
|
But repaint of `surface` is not automatically notified to gtk.
|
||||||
Therefore, you need to call `gtk_widget_queue_draw` to redraw the widget.
|
Therefore, you need to call `gtk_widget_queue_draw` to redraw the widget.
|
||||||
- 9-40: Run function paint the surface.
|
- 9-40: Run function paints the surface.
|
||||||
First, it gets the contents of GtkTextBuffer.
|
First, it gets the contents of GtkTextBuffer.
|
||||||
Then compare it to "red", "green" and so on.
|
Then it compares it to "red", "green" and so on.
|
||||||
If it matches the color, then the surface is painted the color.
|
If it matches the color, then the surface is painted the color.
|
||||||
If it matches "light" or "dark", then the color of the surface is lightened or darkened respectively.
|
If it matches "light" or "dark", then the color of the surface is lightened or darkened respectively.
|
||||||
Alpha channel is used.
|
Alpha channel is used.
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
### GtkApplication and g\_application\_run
|
### GtkApplication and g\_application\_run
|
||||||
|
|
||||||
Usually people write a programming code to make an application.
|
Usually people write a programming code to make an application.
|
||||||
What are appications?
|
What are applications?
|
||||||
Applications are software that runs using libraries, which includes OS, frameworks and so on.
|
Applications are software that runs using libraries, which includes OS, frameworks and so on.
|
||||||
In Gtk4 programming, GtkApplication is an object that runs on GTK libraries.
|
In Gtk4 programming, GtkApplication is an object that runs on GTK libraries.
|
||||||
|
|
||||||
|
@ -157,7 +157,7 @@ Now rewrite the function `on_activate`.
|
||||||
|
|
||||||
@@@ misc/pr3.c on_activate
|
@@@ misc/pr3.c on_activate
|
||||||
|
|
||||||
Widget is an abstract concept that includes all the GUI interfaces such as windows, dialogs, buttons, multiline text, containers and so on.
|
Widget is an abstract concept that includes all the GUI interfaces such as windows, dialogs, buttons, multi-line text, containers and so on.
|
||||||
And GtkWidget is a base object from which all the GUI objects derive.
|
And GtkWidget is a base object from which all the GUI objects derive.
|
||||||
|
|
||||||
parent <-----> child
|
parent <-----> child
|
||||||
|
|
|
@ -60,9 +60,9 @@ The following program shows how to catch the signal and do something.
|
||||||
@@@ misc/lb2.c
|
@@@ misc/lb2.c
|
||||||
|
|
||||||
Look at the line 17 to 19.
|
Look at the line 17 to 19.
|
||||||
First, generate a GtkButton widget `btn` with a label "Click me".
|
First, it generates a GtkButton widget `btn` with a label "Click me".
|
||||||
Then, set it to the window `win` as a child.
|
Then, adds the button to the window `win` as a child.
|
||||||
Finally, connect a "clicked" signal of the button to a handler (function) `click_cb`.
|
Finally, connects a "clicked" signal of the button to a handler (function) `click_cb`.
|
||||||
So, if `btn` is clicked, the function `click_cb` is invoked.
|
So, if `btn` is clicked, the function `click_cb` is invoked.
|
||||||
The suffix cb means "call back".
|
The suffix cb means "call back".
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ It arranges two or more child widgets into a single row or column.
|
||||||
The following procedure shows the way to add two buttons in a window.
|
The following procedure shows the way to add two buttons in a window.
|
||||||
|
|
||||||
- Generate GtkApplicationWindow.
|
- Generate GtkApplicationWindow.
|
||||||
- Generate GtkBox and set it a child of GtkApplicationWindow.
|
- Generate GtkBox and add it to GtkApplicationWindow as a child.
|
||||||
- Generate GtkButton and append it to GtkBox.
|
- Generate GtkButton and append it to GtkBox.
|
||||||
- Generate another GtkButton and append it to GtkBox.
|
- Generate another GtkButton and append it to GtkBox.
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
### GtkTextView and GtkTextBuffer
|
### GtkTextView and GtkTextBuffer
|
||||||
|
|
||||||
GtkTextview is a widget for multiline text editing.
|
GtkTextview is a widget for multi-line text editing.
|
||||||
GtkTextBuffer is a text buffer which is connected to GtkTextView.
|
GtkTextBuffer is a text buffer which is connected to GtkTextView.
|
||||||
See a sample program `tfv1.c` below.
|
See a sample program `tfv1.c` below.
|
||||||
|
|
||||||
|
@ -17,9 +17,9 @@ In the next line, the pointer to the buffer is got and assigned to `tb`.
|
||||||
Then, the text from line 10 to 20 is assigned to the buffer.
|
Then, the text from line 10 to 20 is assigned to the buffer.
|
||||||
|
|
||||||
GtkTextView has a wrap mode.
|
GtkTextView has a wrap mode.
|
||||||
When `GTK_WRAP_WORD_CHAR` is set, text wraps in between words, or if that is not enough, also between graphemes.
|
When it is set to `GTK_WRAP_WORD_CHAR`, text wraps in between words, or if that is not enough, also between graphemes.
|
||||||
|
|
||||||
In line 30, `tv` is set to `win` as a child.
|
In line 30, `tv` is added to `win` as a child.
|
||||||
|
|
||||||
Now compile and run it.
|
Now compile and run it.
|
||||||
|
|
||||||
|
@ -37,8 +37,8 @@ You can solve it by putting GtkScrolledWindow between GtkApplicationWindow and G
|
||||||
|
|
||||||
What we need to do is:
|
What we need to do is:
|
||||||
|
|
||||||
- Generate GtkScrolledWindow and set it as a child of GtkApplicationWindow.
|
- Generate GtkScrolledWindow and insert it to GtkApplicationWindow as a child.
|
||||||
- Set GtkTextView as a child of GtkScrolledWindow.
|
- insert GtkTextView to GtkScrolledWindow as a child.
|
||||||
|
|
||||||
Modify `tfv1.c` and save it as `tfv2.c`.
|
Modify `tfv1.c` and save it as `tfv2.c`.
|
||||||
The difference between these two files is very little.
|
The difference between these two files is very little.
|
||||||
|
|
|
@ -13,7 +13,7 @@ When the program starts, we give a filename as an argument.
|
||||||
|
|
||||||
$ ./a.out filename
|
$ ./a.out filename
|
||||||
|
|
||||||
Then it opens the file and set it into GtkTextBuffer.
|
Then it opens the file and inserts it into GtkTextBuffer.
|
||||||
|
|
||||||
At the beginning of the implementation, we need to know how GtkApplication (or GApplication) recognizes arguments.
|
At the beginning of the implementation, we need to know how GtkApplication (or GApplication) recognizes arguments.
|
||||||
It is described in the GIO API reference.
|
It is described in the GIO API reference.
|
||||||
|
@ -87,10 +87,10 @@ The way how to read a file using GFiles will be described in the next section.
|
||||||
A file viewer is a program that shows a text file given as an argument.
|
A file viewer is a program that shows a text file given as an argument.
|
||||||
It works as follows.
|
It works as follows.
|
||||||
|
|
||||||
- If it is given arguments, it recognizes the first argument as a filename and open it.
|
- If it is given arguments, it recognizes the first argument as a filename and opens it.
|
||||||
- If opening the file succeeds, read and set it to GtkTextBuffer and show the window.
|
- If opening the file succeeds, it reads the contents of the file and inserts it to GtkTextBuffer and shows the window.
|
||||||
- If it fails to open the file, show an error message and quit.
|
- If it fails to open the file, shows an error message and quits.
|
||||||
- If there's no argument, show an error message and quit.
|
- If there's no argument, it shows an error message and quits.
|
||||||
- If there are two or more arguments, the second one and after are ignored.
|
- If there are two or more arguments, the second one and after are ignored.
|
||||||
|
|
||||||
The program is as follows.
|
The program is as follows.
|
||||||
|
@ -118,10 +118,10 @@ The application quits immediately because no window is generated.
|
||||||
The point is the handler `on_open`.
|
The point is the handler `on_open`.
|
||||||
|
|
||||||
- It generates GtkApplicationWindow, GtkScrolledWindow, GtkTextView and GtkTextBuffer and connects them.
|
- It generates GtkApplicationWindow, GtkScrolledWindow, GtkTextView and GtkTextBuffer and connects them.
|
||||||
- Set wrap mode to `GTK_WRAP_WORD_CHAR` in GtktextView.
|
- Sets wrap mode to `GTK_WRAP_WORD_CHAR` in GtktextView.
|
||||||
- Set non-editable to GtkTextView because the program isn't an editor but only a viewer.
|
- Sets GtkTextView to non-editable because the program isn't an editor but only a viewer.
|
||||||
- Read the file and set it to GtkTextBuffer (this will be explained in detail later).
|
- Reads the file and inserts the text into GtkTextBuffer (this will be explained in detail later).
|
||||||
- If the file is not opened then output an error message and destroy the window. It makes the application quit.
|
- If the file is not opened then outputs an error message and destroys the window. It makes the application quit.
|
||||||
|
|
||||||
The file reading part of the program is shown again below.
|
The file reading part of the program is shown again below.
|
||||||
|
|
||||||
|
@ -140,12 +140,12 @@ if (g_file_load_contents(files[0], NULL, &contents, &length, NULL, NULL)) {
|
||||||
}
|
}
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
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`.
|
The function `g_file_load_contents` loads the file contents into a buffer, which is automatically allocated, and sets `contents` to point the buffer.
|
||||||
And the length of the buffer is set to `length`.
|
And the length of the buffer is set to `length`.
|
||||||
It returns `TRUE` if the file's contents were successfully loaded. `FALSE` if there were errors.
|
It returns `TRUE` if the file's contents were successfully loaded. `FALSE` if there were errors.
|
||||||
|
|
||||||
If the function succeeds, set the contents into GtkTextBuffer, free the buffer memories pointed by `contents`, set the filename to the title of the window,
|
If the function succeeds, it inserts the contents into GtkTextBuffer, frees the buffer memories pointed by `contents`, sets the title of the window,
|
||||||
free the memories pointed by `filename` and show the window.
|
frees the memories pointed by `filename` and shows the window.
|
||||||
If it fails, it outputs an error message and destroys the window.
|
If it fails, it outputs an error message and destroys the window.
|
||||||
|
|
||||||
## GtkNotebook
|
## GtkNotebook
|
||||||
|
@ -169,20 +169,20 @@ Now I want to show you the program `tfv4.c`.
|
||||||
Most of the change is in the function `on_open`.
|
Most of the change is in the function `on_open`.
|
||||||
The numbers at the left of the following items are line numbers in the source code.
|
The numbers at the left of the following items are line numbers in the source code.
|
||||||
|
|
||||||
- 11-13: Variables `nb`, `lab` and `nbp` are defined and point GtkNotebook, GtkLabel and GtkNotebookPage respectively.
|
- 11-13: Variables `nb`, `lab` and `nbp` are defined and points GtkNotebook, GtkLabel and GtkNotebookPage respectively.
|
||||||
- 23: The window's title is set to "file viewer".
|
- 23: The window's title is set to "file viewer".
|
||||||
- 25: The size of the window is set to maximum because a big window is appropriate for file viewers.
|
- 25: The size of the window is set to maximum because a big window is appropriate for file viewers.
|
||||||
- 27-28 GtkNotebook is generated and set it as a child of the GtkApplicationWindow.
|
- 27-28 GtkNotebook is generated and inserted to the GtkApplicationWindow as a child.
|
||||||
- 30-52 For-loop. Each loop corresponds to an argument. And files[i] is GFile object with respect to the i-th argument.
|
- 30-52 For-loop. Each loop corresponds to an argument. And files[i] is GFile object with respect to the i-th argument.
|
||||||
- 32-37 GtkScrollledWindow, GtkTextView and GtkTextBuffer are generated and GtkTextView is connected to GtkScrolledWindow as a child.
|
- 32-37 GtkScrollledWindow, GtkTextView and GtkTextBuffer are generated and GtkTextView is connected to GtkScrolledWindow as a child.
|
||||||
They corresponds to each file, so they are generated inside the for-loop.
|
They corresponds to each file, so they are generated inside the for-loop.
|
||||||
- 39-42 Set the contents of the file into GtkTextBuffer and free the memory pointed by `contents`. Get the filename and generate GtkLabel with the filename.
|
- 39-42 inserts the contents of the file into GtkTextBuffer and frees the memory pointed by `contents`. Gets the filename and generates GtkLabel with the filename.
|
||||||
- 43: Append GtkScrolledWindow and GtkLabel to GtkNotebook. The appended objects are children of automatically generated GtkNotebookPage object. Therefore, the structure is like this:
|
- 43: Appends GtkScrolledWindow and GtkLabel to GtkNotebook. The appended objects are children of automatically generated GtkNotebookPage object. Therefore, the structure is like this:
|
||||||
|
|
||||||
GtkNotebook -- GtkNotebookPage -- (GtkScrolledWindow and GtkLabel)
|
GtkNotebook -- GtkNotebookPage -- (GtkScrolledWindow and GtkLabel)
|
||||||
|
|
||||||
- 44: Get GtkNotebookPage object and set its pointer to `nbp`.
|
- 44: Gets GtkNotebookPage object and sets `nbp` to point the GtkNotebookPage.
|
||||||
- 45: GtkNotebookPage has a property "tab-expand". If it is set to TRUE then the tab expand horizontally as long as possible. If FALSE, then the width of the tab is determined by the size of the label. `g_object_set` is a general function to set properties in any objects.
|
- 45: GtkNotebookPage has a property "tab-expand". If it is set to TRUE then the tab expand horizontally as long as possible. If FALSE, then the width of the tab is determined by the size of the label. `g_object_set` is a general function to set properties in any objects.
|
||||||
- 46: free the memory pointed by `filename`
|
- 46: frees the memory pointed by `filename`
|
||||||
- 53-56: If at least one file was read, then the number of GtkNotebookPage is greater than zero. If it's true, then show the window. If it's false, then destroy the window.
|
- 53-56: If at least one file was read, then the number of GtkNotebookPage is greater than zero. If it's true, then show the window. If it's false, then destroy the window.
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ We just add two things to the file viewer.
|
||||||
A couple of ways are possible to get memories to keep GFile.
|
A couple of ways are possible to get memories to keep GFile.
|
||||||
|
|
||||||
- Use global variables.
|
- Use global variables.
|
||||||
- make a child widget object and extend the memories allocated to the widget.
|
- make a child object so that it can extend the memories for the GFile object.
|
||||||
|
|
||||||
Using global variables is easy to implement.
|
Using global variables is easy to implement.
|
||||||
Define a sufficient size array of pointers to GFile.
|
Define a sufficient size array of pointers to GFile.
|
||||||
|
@ -48,12 +48,11 @@ It has everything that GtkTextView has.
|
||||||
For example, TfeTextView has GtkTextbuffer corresponds to GtkTextView inside TfeTextView.
|
For example, TfeTextView has GtkTextbuffer corresponds to GtkTextView inside TfeTextView.
|
||||||
And important thing is that TfeTextView can have a memory to keep a pointer to GFile.
|
And important thing is that TfeTextView can have a memory to keep a pointer to GFile.
|
||||||
|
|
||||||
However, to understand the general theory about gobjects is very hard especially for beginners.
|
However, to understand the general theory about Gobject is very hard especially for beginners.
|
||||||
So, I will just show you the way how to write the code and avoid the theoretical side in the next subsection.
|
So, I will just show you the way how to write the code and avoid the theoretical side in the next subsection.
|
||||||
|
|
||||||
## How to define a child widget of GtkTextView
|
## How to define a child widget of GtkTextView
|
||||||
|
|
||||||
|
|
||||||
Let's define TfeTextView object which is a child object of GtkTextView.
|
Let's define TfeTextView object which is a child object of GtkTextView.
|
||||||
First, look at the program below.
|
First, look at the program below.
|
||||||
|
|
||||||
|
@ -95,7 +94,7 @@ tfe_text_view_new (void) {
|
||||||
|
|
||||||
If you are curious about the background theory of this program, It's very good for you.
|
If you are curious about the background theory of this program, It's very good for you.
|
||||||
Because knowing the theory is very important for you to program GTK applications.
|
Because knowing the theory is very important for you to program GTK applications.
|
||||||
Look at GObject API reference.
|
Look at [GObject API reference](https://developer.gnome.org/gobject/stable/).
|
||||||
All you need is described in it.
|
All you need is described in it.
|
||||||
However, it's a tough journey especially for beginners.
|
However, it's a tough journey especially for beginners.
|
||||||
For now, you don't need to know such difficult theory.
|
For now, you don't need to know such difficult theory.
|
||||||
|
@ -125,7 +124,7 @@ Usually you don't need to do anything.
|
||||||
- Define class init function (tfe\_text\_view\_class\_init).
|
- Define class init function (tfe\_text\_view\_class\_init).
|
||||||
You don't need to do anything in this widget.
|
You don't need to do anything in this widget.
|
||||||
- Write function codes you want to add (tfe\_text\_view\_set\_file and tfe\_text\_view\_get\_file).
|
- Write function codes you want to add (tfe\_text\_view\_set\_file and tfe\_text\_view\_get\_file).
|
||||||
`tv` is a pointer to TfeTextView object instance which is a C-struture declared with the tag \_TfeTextView.
|
`tv` is a pointer to TfeTextView object instance which is a C-structure declared with the tag \_TfeTextView.
|
||||||
So, the structure has a member `file` as a pointer to GFile.
|
So, the structure has a member `file` as a pointer to GFile.
|
||||||
`tv->file = f` is an assignment of `f` to a member `file` of the structure pointed by `tv`.
|
`tv->file = f` is an assignment of `f` to a member `file` of the structure pointed by `tv`.
|
||||||
This is an example how to use the extended memory in a child widget.
|
This is an example how to use the extended memory in a child widget.
|
||||||
|
@ -146,7 +145,14 @@ It will be modified later.
|
||||||
|
|
||||||
## Close-request signal
|
## Close-request signal
|
||||||
|
|
||||||
After editing a file, `tfe1.c` writes files just before the window closes.
|
Imagine that you use this editor.
|
||||||
|
First, you run the editor with arguments.
|
||||||
|
The arguments are filenames.
|
||||||
|
The editor reads the files and shows its window with the text of files in it.
|
||||||
|
Then you edit the text.
|
||||||
|
After you finish editing, you exit the editor.
|
||||||
|
The editor updates files just before the window closes.
|
||||||
|
|
||||||
GtkWindow emits "close-request" signal before it closes.
|
GtkWindow emits "close-request" signal before it closes.
|
||||||
We connect the signal and the handler `before_close`.
|
We connect the signal and the handler `before_close`.
|
||||||
A handler is a C function.
|
A handler is a C function.
|
||||||
|
@ -157,19 +163,22 @@ The function `before_close` is invoked when the signal "close-request" is emitte
|
||||||
g_signal_connect (win, "close-request", G_CALLBACK (before_close), NULL);
|
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.
|
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.
|
`G_CALLBACK` cast is necessary for the handler.
|
||||||
The program of before\_close is as follows.
|
The program of `before_close` is as follows.
|
||||||
|
|
||||||
@@@ tfe/tfe1.c before_close
|
@@@ tfe/tfe1.c before_close
|
||||||
|
|
||||||
The numbers on the left of items are line numbers in the source code.
|
The numbers on the left of items are line numbers in the source code.
|
||||||
|
|
||||||
- 13: Get the number of pages `nb` has.
|
- 13: Gets the number of pages `nb` has.
|
||||||
- 14-23: For loop with regard to the index to each pages.
|
- 14-23: For loop with regard to the index to each pages.
|
||||||
- 15-17: Get GtkScrolledWindow, TfeTextView and a pointer to GFile. The pointer was stored when `on_open` handler had run. It will be shown later.
|
- 15-17: Gets GtkScrolledWindow, TfeTextView and a pointer to GFile.
|
||||||
- 18-20: Get GtkTextBuffer and contents. start\_iter and end\_iter is iterators of the buffer. I don't want to explain them now because it would take a lot of time. Just remember these lines for the present.
|
The pointer was stored when `on_open` handler had run. It will be shown later.
|
||||||
- 21: Write the file.
|
- 18-20: Gets GtkTextBuffer and contents. `start_iter` and `end_iter` are iterators of the buffer.
|
||||||
|
I don't want to explain them now because it would take a lot of time.
|
||||||
|
Just remember these lines for the present.
|
||||||
|
- 21: Writes the file.
|
||||||
|
|
||||||
## Source code of tfe1.c
|
## Source code of tfe1.c
|
||||||
|
|
||||||
|
@ -177,11 +186,11 @@ Now I will show you all the source code of `tfe1`.c.
|
||||||
|
|
||||||
@@@ tfe/tfe1.c
|
@@@ tfe/tfe1.c
|
||||||
|
|
||||||
- 102: set the pointer to GFile into TfeTextView.
|
- 102: Sets the pointer to GFile into TfeTextView.
|
||||||
`files[i]` is a pointer to GFile structure.
|
`files[i]` is a pointer to GFile structure.
|
||||||
It will be freed by the system. So you need to copy it.
|
It will be freed by the system. So you need to copy it.
|
||||||
`g_file_dup` duplicate the given GFile structure.
|
`g_file_dup` duplicates the given GFile structure.
|
||||||
- 118: connect "close-request" signal and `before_close` handler.
|
- 118: Connects "close-request" signal and `before_close` handler.
|
||||||
The fourth argument is called user data and it is given to the signal handler.
|
The fourth argument is called user data and it is given to the signal handler.
|
||||||
So, `nb` is given to `before_close` as the second argument.
|
So, `nb` is given to `before_close` as the second argument.
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
## New, open and save button
|
## New, open and save button
|
||||||
|
|
||||||
We made the simplest editor in the previous section.
|
We made the simplest editor in the previous section.
|
||||||
It reads the files in `on_open` function at start-up and writes it when closing the window.
|
It reads the files in `on_open` function at start-up and writes them when closing the window.
|
||||||
It works but is not good.
|
It works but is not good.
|
||||||
It is better to make "New", "Open", "Save" and "Close" buttons.
|
It is better to make "New", "Open", "Save" and "Close" buttons.
|
||||||
This section describes how to put those buttons into the window.
|
This section describes how to put those buttons into the window.
|
||||||
|
@ -18,21 +18,21 @@ The function `on_open` in the source code `tfe2.c` is as follows.
|
||||||
|
|
||||||
The point is how to build the window.
|
The point is how to build the window.
|
||||||
|
|
||||||
- 25-27: Generate GtkApplicationWindow and set its title and default size.
|
- 25-27: Generates GtkApplicationWindow and sets the title and default size.
|
||||||
- 29-30: Generate GtkBox `boxv`.
|
- 29-30: Generates GtkBox `boxv`.
|
||||||
It is a vertical box and a child of GtkApplicationWindow.
|
It is a vertical box and a child of GtkApplicationWindow.
|
||||||
It has two children.
|
It has two children.
|
||||||
The first child is a horizontal box includes buttons.
|
The first child is a horizontal box includes buttons.
|
||||||
The second child is GtkNotebook.
|
The second child is GtkNotebook.
|
||||||
- 32-33: Generate GtkBox `boxh` and append it to 'boxv' as a first child.
|
- 32-33: Generates GtkBox `boxh` and appends it to 'boxv' as a first child.
|
||||||
- 35-40: Generate three dummy labels.
|
- 35-40: Generates three dummy labels.
|
||||||
The labels `dmy1` and `dmy3` has a character width of ten.
|
The labels `dmy1` and `dmy3` has a character width of ten.
|
||||||
The other label `dmy2` is set hexpand property TRUE.
|
The other label `dmy2` has hexpand property which is set to be TRUE.
|
||||||
This makes the label expands horizontally as long as possible.
|
This makes the label expands horizontally as long as possible.
|
||||||
- 41-44: Generate four buttons.
|
- 41-44: Generates four buttons.
|
||||||
- 46-52: Append these GtkLabel and GtkButton to `boxh`.
|
- 46-52: Appends these GtkLabel and GtkButton to `boxh`.
|
||||||
- 54-57: Generate GtkNotebook and set hexpand and vexpand properties TRUE.
|
- 54-57: Generates GtkNotebook and sets hexpand and vexpand properties TRUE.
|
||||||
This makes it expands horizontally and vertically as big as possible.
|
This makes it expand horizontally and vertically as big as possible.
|
||||||
It is appended to `boxv` as the second child.
|
It is appended to `boxv` as the second child.
|
||||||
|
|
||||||
The number of lines is 33(=57-25+1) to build the widgets.
|
The number of lines is 33(=57-25+1) to build the widgets.
|
||||||
|
@ -42,7 +42,7 @@ Are there any good solution to reduce these work?
|
||||||
|
|
||||||
Gtk provides GtkBuilder.
|
Gtk provides GtkBuilder.
|
||||||
It reads ui data and builds a window.
|
It reads ui data and builds a window.
|
||||||
It reduces the cumbersom work.
|
It reduces the cumbersome work.
|
||||||
|
|
||||||
## Ui file
|
## Ui file
|
||||||
|
|
||||||
|
@ -51,7 +51,8 @@ First, let's look at the ui file `tfe3.ui` that defines a structure of the widge
|
||||||
@@@ tfe/tfe3.ui
|
@@@ tfe/tfe3.ui
|
||||||
|
|
||||||
This is coded with XML structure.
|
This is coded with XML structure.
|
||||||
Constructs beginning with `<` and ending with `>` are called tags, and are divided into two parts, start tag and end tag.
|
Constructs beginning with `<` and ending with `>` are called tags.
|
||||||
|
And there are two types of tags, start tag and end tag.
|
||||||
For example, `<interface>` is a start tag and `</interface>` is an end tag.
|
For example, `<interface>` is a start tag and `</interface>` is an end tag.
|
||||||
Ui file begins and ends with interface tags.
|
Ui file begins and ends with interface tags.
|
||||||
Some tags, for example, object tags can have a class and id attributes inside the start tag.
|
Some tags, for example, object tags can have a class and id attributes inside the start tag.
|
||||||
|
@ -64,7 +65,7 @@ And the three properties of the window are defined.
|
||||||
For example, line 7 tells us that GtkBox object which id is "boxv" is a child of `win`.
|
For example, line 7 tells us that GtkBox object which id is "boxv" is a child of `win`.
|
||||||
|
|
||||||
Compare this ui file and the lines 25-57 in the source code of `on_open` function.
|
Compare this ui file and the lines 25-57 in the source code of `on_open` function.
|
||||||
Those two decribe the same structure of widgets.
|
Those two describe the same structure of widgets.
|
||||||
|
|
||||||
## GtkBuilder
|
## GtkBuilder
|
||||||
|
|
||||||
|
@ -79,7 +80,8 @@ gtk_window_set_application (GTK_WINDOW (win), GTK_APPLICATION (app));
|
||||||
nb = GTK_WIDGET (gtk_builder_get_object (build, "nb"));
|
nb = GTK_WIDGET (gtk_builder_get_object (build, "nb"));
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
The function `gtk_builder_new_from_file` reads the file given as an argument, build the widgets, generate GtkBuilder object and set pointers to the widgets in it.
|
The function `gtk_builder_new_from_file` reads the file given as an argument.
|
||||||
|
Then, it builds the widgets and pointers to them, creates GtkBuilder object and put the pointers in it.
|
||||||
The function `gtk_builder_get_object (build, "win")` returns the pointer to the widget `win`, which is the id in the ui file.
|
The function `gtk_builder_get_object (build, "win")` returns the pointer to the widget `win`, which is the id in the ui file.
|
||||||
All the widgets are connected based on the parent-children relationship described in the ui file.
|
All the widgets are connected based on the parent-children relationship described in the ui file.
|
||||||
We only need `win` and `nb` for the program after this, so we don't need to take out any other widgets.
|
We only need `win` and `nb` for the program after this, so we don't need to take out any other widgets.
|
||||||
|
@ -93,14 +95,13 @@ $$$
|
||||||
Therefore 37 lines are reduced.
|
Therefore 37 lines are reduced.
|
||||||
Using ui file not only shortens C source files, but also makes the widgets' structure clear.
|
Using ui file not only shortens C source files, but also makes the widgets' structure clear.
|
||||||
|
|
||||||
Now I'll show you the C source code `tfe3.c`.
|
Now I'll show you `on_open` function in the C source code `tfe3.c`.
|
||||||
Only functions `on_open` are shown as follows.
|
|
||||||
|
|
||||||
@@@ tfe/tfe3.c on_open
|
@@@ tfe/tfe3.c on_open
|
||||||
|
|
||||||
The source code of `tfe3.c` is stored in [src/tfe](https://github.com/ToshioCP/Gtk4-tutorial/tree/main/src/tfe) directory.
|
The whole source code of `tfe3.c` is stored in [src/tfe](https://github.com/ToshioCP/Gtk4-tutorial/tree/main/src/tfe) directory.
|
||||||
If you want to see it, click the link above.
|
If you want to see it, click the link above.
|
||||||
In the same way, you can get the source files below in the directory [src/tfe](https://github.com/ToshioCP/Gtk4-tutorial/tree/main/src/tfe).
|
You can also get the source files below.
|
||||||
|
|
||||||
### Using ui string
|
### Using ui string
|
||||||
|
|
||||||
|
@ -132,8 +133,8 @@ So ui file is not necessary on runtime.
|
||||||
The disadvantage is that writing C string is a bit bothersome because of the double quotes.
|
The disadvantage is that writing C string is a bit bothersome because of the double quotes.
|
||||||
If you want to use this method, you should write a script that transforms ui file into C-string.
|
If you want to use this method, you should write a script that transforms ui file into C-string.
|
||||||
|
|
||||||
- add backslash before each double quote.
|
- Add backslash before each double quote.
|
||||||
- add double quote at the left and right.
|
- Add double quote at the left and right.
|
||||||
|
|
||||||
### Using Gresource
|
### Using Gresource
|
||||||
|
|
||||||
|
@ -148,10 +149,10 @@ It describes resource files.
|
||||||
|
|
||||||
@@@ tfe/tfe3.gresource.xml
|
@@@ tfe/tfe3.gresource.xml
|
||||||
|
|
||||||
- 2: gresources tag can include mulitple gresources (gresource tags).
|
- 2: `gresources` tag can include multiple gresources (gresource tags).
|
||||||
However, this xml has only one gresource.
|
However, this xml has only one gresource.
|
||||||
- 3: The gresource has a prefix `/com/github/ToshioCP/tfe3`.
|
- 3: The gresource has a prefix `/com/github/ToshioCP/tfe3`.
|
||||||
- 4: The gresource has tfe3.ui.
|
- 4: The gresource has `tfe3.ui`.
|
||||||
And it is pointed by `/com/github/ToshioCP/tfe3/tfe3.ui` because it needs prefix.
|
And it is pointed by `/com/github/ToshioCP/tfe3/tfe3.ui` because it needs prefix.
|
||||||
If you want to add more files, then insert them between line 4 and 5.
|
If you want to add more files, then insert them between line 4 and 5.
|
||||||
|
|
||||||
|
@ -167,7 +168,7 @@ Now run the compiler.
|
||||||
$ glib-compile-resources tfe3.gresource.xml --target=resources.c --generate-source
|
$ glib-compile-resources tfe3.gresource.xml --target=resources.c --generate-source
|
||||||
|
|
||||||
Then a C source file `resources.c` is generated.
|
Then a C source file `resources.c` is generated.
|
||||||
Modify tfe3.c and save it as tfe3_r.c
|
Modify `tfe3.c` and save it as `tfe3_r.c`.
|
||||||
|
|
||||||
~~~C
|
~~~C
|
||||||
#include "resources.c"
|
#include "resources.c"
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
|
|
||||||
## What do we need to think about to manage big source files?
|
## What do we need to think about to manage big source files?
|
||||||
|
|
||||||
We've managed to compile a small editor so far.
|
We've compiled a small editor so far.
|
||||||
But Some bad signs are beginning to appear.
|
But Some bad signs are beginning to appear.
|
||||||
|
|
||||||
- We have only one C source file and put everything into it.
|
- We've had only one C source file and put everything into it.
|
||||||
We need to sort it out.
|
We need to sort it out.
|
||||||
- There are two compilers, `gcc` and `glib-compile-resources`.
|
- There are two compilers, `gcc` and `glib-compile-resources`.
|
||||||
We want to control them by one building tool.
|
We should control them by one building tool.
|
||||||
|
|
||||||
These ideas are useful to manage big source files.
|
These ideas are useful to manage big source files.
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ It is a good idea to separate them into two files, `tfetextview.c` and `tfe.c`.
|
||||||
Now we have three source files, `tfetextview.c`, `tfe.c` and `tfe3.ui`.
|
Now we have three source files, `tfetextview.c`, `tfe.c` and `tfe3.ui`.
|
||||||
The `3` of `tfe3.ui` is like a version number.
|
The `3` of `tfe3.ui` is like a version number.
|
||||||
Managing version with filenames is one possible idea but it may make bothersome problem.
|
Managing version with filenames is one possible idea but it may make bothersome problem.
|
||||||
You need to rewrite filename in each version and it affects to contents of sourcefiles that refer to filenames.
|
You need to rewrite filename in each version and it affects to contents of source files that refer to filenames.
|
||||||
So, we should take `3` away from the filename.
|
So, we should take `3` away from the filename.
|
||||||
|
|
||||||
In `tfe.c` the function `tfe_text_view_new` is invoked to generate TfeTextView.
|
In `tfe.c` the function `tfe_text_view_new` is invoked to generate TfeTextView.
|
||||||
|
@ -64,16 +64,16 @@ Dividing a file makes it easy to maintain source files.
|
||||||
But now we are faced with a new problem.
|
But now we are faced with a new problem.
|
||||||
The building step increases.
|
The building step increases.
|
||||||
|
|
||||||
- Compile the ui file `tfe.ui` into `resources.c`.
|
- Compiling the ui file `tfe.ui` into `resources.c`.
|
||||||
- Compile `tfe.c` into `tfe.o` (object file).
|
- Compiling `tfe.c` into `tfe.o` (object file).
|
||||||
- Compile `tfetextview.c` into `tfetextview.o`.
|
- Compiling `tfetextview.c` into `tfetextview.o`.
|
||||||
- Compile `resources.c` into `resources.o`.
|
- Compiling `resources.c` into `resources.o`.
|
||||||
- Link all the object files into application `tfe`.
|
- Linking all the object files into application `tfe`.
|
||||||
|
|
||||||
Now build tool is necessary to manage it.
|
Now build tool is necessary to manage it.
|
||||||
Make is one of the build tools.
|
Make is one of the build tools.
|
||||||
It was originally created in 1976.
|
It was created in 1976.
|
||||||
So it is an old and widely used program.
|
It is an old and widely used program.
|
||||||
|
|
||||||
Make analyzes Makefile and executes compilers.
|
Make analyzes Makefile and executes compilers.
|
||||||
All instructions are written in Makefile.
|
All instructions are written in Makefile.
|
||||||
|
@ -96,7 +96,7 @@ The rule is:
|
||||||
If a prerequisite modified later than a target, then make executes the recipe.
|
If a prerequisite modified later than a target, then make executes the recipe.
|
||||||
|
|
||||||
In the example above, if `sample.c` is modified after the generation of `sample.o`, then make executes gcc and compile `sample.c` into `sample.o`.
|
In the example above, if `sample.c` is modified after the generation of `sample.o`, then make executes gcc and compile `sample.c` into `sample.o`.
|
||||||
If the modification time of `sample.c` is older then the generation of `sample.o`, then no compiling is necesarry, so make does nothing.
|
If the modification time of `sample.c` is older then the generation of `sample.o`, then no compiling is necessary, so make does nothing.
|
||||||
|
|
||||||
The Makefile for `tfe` is as follows.
|
The Makefile for `tfe` is as follows.
|
||||||
|
|
||||||
|
@ -132,19 +132,19 @@ Rake has task and file task, which is similar to target, prerequisite and recipe
|
||||||
|
|
||||||
What `Rakefile` describes is almost same as `Makefile` in the previous subsection.
|
What `Rakefile` describes is almost same as `Makefile` in the previous subsection.
|
||||||
|
|
||||||
- 3-6: define target file, source file and so on.
|
- 3-6: Defines target file, source file and so on.
|
||||||
- 1, 8: Load clean library. And define CLEAN file list.
|
- 1, 8: Loads clean library. And defines CLEAN file list.
|
||||||
The files included by CLEAN will be removed when `rake clean` is typed on the command line.
|
The files included by CLEAN will be removed when `rake clean` is typed on the command line.
|
||||||
- 10: default target depends on targetfile.
|
- 10: Default target depends on targetfile.
|
||||||
default is the final goal of tasks.
|
Default is the final goal of tasks.
|
||||||
- 12-14: targetfile depends on objfiles.
|
- 12-14: Targetfile depends on objfiles.
|
||||||
The variable `t` is a task object.
|
The variable `t` is a task object.
|
||||||
- t.name is a target name
|
- t.name is a target name
|
||||||
- t.prerequisites is an array of prerequisits.
|
- t.prerequisites is an array of prerequisites.
|
||||||
- t.source is the first element of prerequisites.
|
- t.source is the first element of prerequisites.
|
||||||
- sh is a method to give the following string to shell as an argument and execute the shell.
|
- sh is a method to give the following string to shell as an argument and execute the shell.
|
||||||
- 16-21: Loop by each element of the array of objfiles. Each object depends on corresponding source file.
|
- 16-21: There's a loop by each element of the array of objfiles. Each object depends on corresponding source file.
|
||||||
- 23-25: resouce file depends on xml file and ui file.
|
- 23-25: Resource file depends on xml file and ui file.
|
||||||
|
|
||||||
Rakefile might seem to be difficult for beginners.
|
Rakefile might seem to be difficult for beginners.
|
||||||
But, you can use any ruby syntax in Rakefile, so it is really flexible.
|
But, you can use any ruby syntax in Rakefile, so it is really flexible.
|
||||||
|
@ -163,17 +163,17 @@ To use meson, you first need to write `meson.build` file.
|
||||||
@@@ tfe4/meson.build
|
@@@ tfe4/meson.build
|
||||||
|
|
||||||
- 1: The function `project` defines things about the project.
|
- 1: The function `project` defines things about the project.
|
||||||
The first parameter is the name of the project and the second is the programing language.
|
The first parameter is the name of the project and the second is the programming language.
|
||||||
- 2: `dependency` function defines a dependency that is taken by `pkg-config`.
|
- 2: `dependency` function defines a dependency that is taken by `pkg-config`.
|
||||||
We put `gtk4` as an argument.
|
We put `gtk4` as an argument.
|
||||||
- 5: `import` function inports a module.
|
- 5: `import` function imports a module.
|
||||||
In line 5, gnome module is imported and assignd to the variable `gnome`.
|
In line 5, gnome module is imported and assigned to the variable `gnome`.
|
||||||
gnome module provides helper tools to build GTK programs.
|
gnome module provides helper tools to build GTK programs.
|
||||||
- 6: `.compile_resources` is a method of gnome module and compile files to resources under the instruction of xml file.
|
- 6: `.compile_resources` is a method of gnome module and compile files to resources under the instruction of xml file.
|
||||||
In line 6, the resource filename is `resources`, which means `resources.c` and `resources.h`, and xml file is `tfe.gresource.xml`.
|
In line 6, the resource filename is `resources`, which means `resources.c` and `resources.h`, and xml file is `tfe.gresource.xml`.
|
||||||
This method generates C source file by default.
|
This method generates C source file by default.
|
||||||
- 8: define source files.
|
- 8: Defines source files.
|
||||||
- 10: executable function generates a target file by building source files.
|
- 10: Executable function generates a target file by building source files.
|
||||||
The first parameter is the filename of the target. The following parameters are source files.
|
The first parameter is the filename of the target. The following parameters are source files.
|
||||||
The last parameter has a option `dependencies`.
|
The last parameter has a option `dependencies`.
|
||||||
In line 10 it is `gtkdep` which is defined in line 3.
|
In line 10 it is `gtkdep` which is defined in line 3.
|
||||||
|
@ -188,6 +188,8 @@ Then, the executable file `tfe` is generated under the directory `_build`.
|
||||||
$ _build/tfe tfe.c tfetextview.c
|
$ _build/tfe tfe.c tfetextview.c
|
||||||
|
|
||||||
Then the window appears.
|
Then the window appears.
|
||||||
|
And two notebook pages are in the window.
|
||||||
|
One notebook is `tfe.c` and the other is `tfetextview.c`.
|
||||||
|
|
||||||
I've shown you three build tools.
|
I've shown you three build tools.
|
||||||
I think meson and ninja is the best choice for the present.
|
I think meson and ninja is the best choice for the present.
|
||||||
|
|
|
@ -66,7 +66,7 @@ before_close (GtkWindow *win, GtkWidget *nb) {
|
||||||
|
|
||||||
static void
|
static void
|
||||||
on_activate (GApplication *app, gpointer user_data) {
|
on_activate (GApplication *app, gpointer user_data) {
|
||||||
g_print ("You need a filename argument.\n");
|
g_print ("You need to give filenames as arguments.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
|
@ -5,6 +5,6 @@ gtkdep = dependency('gtk4')
|
||||||
gnome=import('gnome')
|
gnome=import('gnome')
|
||||||
resources = gnome.compile_resources('resources','tfe.gresource.xml')
|
resources = gnome.compile_resources('resources','tfe.gresource.xml')
|
||||||
|
|
||||||
sourcefiles=files('tfeapplication.c', 'tfenotebook.c', 'tfetextview.c')
|
sourcefiles=files('tfeapplication.c', 'tfenotebook.c', '../tfetextview/tfetextview.c')
|
||||||
|
|
||||||
executable('tfe', sourcefiles, resources, dependencies: gtkdep)
|
executable('tfe', sourcefiles, resources, dependencies: gtkdep)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
#include "tfetextview.h"
|
#include "../tfetextview/tfetextview.h"
|
||||||
#include "tfenotebook.h"
|
#include "tfenotebook.h"
|
||||||
|
|
|
@ -50,6 +50,7 @@ notebook_page_build (GtkNotebook *nb, GtkWidget *tv, char *filename) {
|
||||||
gint i;
|
gint i;
|
||||||
scr = gtk_scrolled_window_new ();
|
scr = gtk_scrolled_window_new ();
|
||||||
|
|
||||||
|
gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR);
|
||||||
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);
|
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);
|
||||||
lab = gtk_label_new (filename);
|
lab = gtk_label_new (filename);
|
||||||
i = gtk_notebook_append_page (nb, scr, lab);
|
i = gtk_notebook_append_page (nb, scr, lab);
|
||||||
|
|
|
@ -1,218 +0,0 @@
|
||||||
#include "tfe.h"
|
|
||||||
|
|
||||||
struct _TfeTextView
|
|
||||||
{
|
|
||||||
GtkTextView parent;
|
|
||||||
GFile *file;
|
|
||||||
};
|
|
||||||
|
|
||||||
G_DEFINE_TYPE (TfeTextView, tfe_text_view, GTK_TYPE_TEXT_VIEW);
|
|
||||||
|
|
||||||
enum {
|
|
||||||
CHANGE_FILE,
|
|
||||||
OPEN_RESPONSE,
|
|
||||||
NUMBER_OF_SIGNALS
|
|
||||||
};
|
|
||||||
|
|
||||||
static guint tfe_text_view_signals[NUMBER_OF_SIGNALS];
|
|
||||||
|
|
||||||
static void
|
|
||||||
tfe_text_view_dispose (GObject *gobject) {
|
|
||||||
TfeTextView *tv = TFE_TEXT_VIEW (gobject);
|
|
||||||
|
|
||||||
if (G_IS_FILE (tv->file))
|
|
||||||
g_clear_object (&tv->file);
|
|
||||||
|
|
||||||
G_OBJECT_CLASS (tfe_text_view_parent_class)->dispose (gobject);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
tfe_text_view_init (TfeTextView *tv) {
|
|
||||||
GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
|
||||||
|
|
||||||
tv->file = NULL;
|
|
||||||
gtk_text_buffer_set_modified (tb, FALSE);
|
|
||||||
gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
tfe_text_view_class_init (TfeTextViewClass *class) {
|
|
||||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
|
||||||
|
|
||||||
object_class->dispose = tfe_text_view_dispose;
|
|
||||||
tfe_text_view_signals[CHANGE_FILE] = g_signal_newv ("change-file",
|
|
||||||
G_TYPE_FROM_CLASS (class),
|
|
||||||
G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
|
|
||||||
NULL /* closure */,
|
|
||||||
NULL /* accumulator */,
|
|
||||||
NULL /* accumulator data */,
|
|
||||||
NULL /* C marshaller */,
|
|
||||||
G_TYPE_NONE /* return_type */,
|
|
||||||
0 /* n_params */,
|
|
||||||
NULL /* param_types */);
|
|
||||||
GType param_types[] = {G_TYPE_INT};
|
|
||||||
tfe_text_view_signals[OPEN_RESPONSE] = g_signal_newv ("open-response",
|
|
||||||
G_TYPE_FROM_CLASS (class),
|
|
||||||
G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
|
|
||||||
NULL /* closure */,
|
|
||||||
NULL /* accumulator */,
|
|
||||||
NULL /* accumulator data */,
|
|
||||||
NULL /* C marshaller */,
|
|
||||||
G_TYPE_NONE /* return_type */,
|
|
||||||
1 /* n_params */,
|
|
||||||
param_types);
|
|
||||||
}
|
|
||||||
|
|
||||||
GFile *
|
|
||||||
tfe_text_view_get_file (TfeTextView *tv) {
|
|
||||||
g_return_val_if_fail (TFE_IS_TEXT_VIEW (tv), NULL);
|
|
||||||
|
|
||||||
return g_file_dup (tv->file);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
GtkWidget *message_dialog;
|
|
||||||
GError *err = NULL;
|
|
||||||
|
|
||||||
if (response != GTK_RESPONSE_ACCEPT)
|
|
||||||
g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_CANCEL);
|
|
||||||
else if (! G_IS_FILE (file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog))))
|
|
||||||
g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_ERROR);
|
|
||||||
else if (! g_file_load_contents (file, NULL, &contents, &length, NULL, &err)) { /* read error */
|
|
||||||
if (G_IS_FILE (file))
|
|
||||||
g_object_unref (file);
|
|
||||||
message_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog), GTK_DIALOG_MODAL,
|
|
||||||
GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
|
|
||||||
"%s.\n", err->message);
|
|
||||||
g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
|
|
||||||
gtk_widget_show (message_dialog);
|
|
||||||
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 (tb, contents, length);
|
|
||||||
g_free (contents);
|
|
||||||
if (G_IS_FILE (tv->file))
|
|
||||||
g_object_unref (tv->file);
|
|
||||||
tv->file = file;
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
tfe_text_view_open (TfeTextView *tv, GtkWidget *win) {
|
|
||||||
g_return_if_fail (TFE_IS_TEXT_VIEW (tv));
|
|
||||||
g_return_if_fail (GTK_IS_WINDOW (win));
|
|
||||||
|
|
||||||
GtkWidget *dialog;
|
|
||||||
|
|
||||||
dialog = gtk_file_chooser_dialog_new ("Open file", GTK_WINDOW (win), GTK_FILE_CHOOSER_ACTION_OPEN,
|
|
||||||
"Cancel", GTK_RESPONSE_CANCEL,
|
|
||||||
"Open", GTK_RESPONSE_ACCEPT,
|
|
||||||
NULL);
|
|
||||||
g_signal_connect (dialog, "response", G_CALLBACK (open_dialog_response), tv);
|
|
||||||
gtk_widget_show (dialog);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
gtk_window_destroy (GTK_WINDOW (dialog));
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
GtkWidget *message_dialog;
|
|
||||||
GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW);
|
|
||||||
GError *err = NULL;
|
|
||||||
|
|
||||||
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 (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))
|
|
||||||
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. */
|
|
||||||
if (G_IS_FILE (tv->file))
|
|
||||||
g_object_unref (tv->file);
|
|
||||||
tv->file =NULL;
|
|
||||||
g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
|
|
||||||
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);
|
|
||||||
g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
|
|
||||||
gtk_widget_show (message_dialog);
|
|
||||||
g_error_free (err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
tfe_text_view_saveas (TfeTextView *tv) {
|
|
||||||
g_return_if_fail (TFE_IS_TEXT_VIEW (tv));
|
|
||||||
|
|
||||||
GtkWidget *dialog;
|
|
||||||
GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW);
|
|
||||||
|
|
||||||
dialog = gtk_file_chooser_dialog_new ("Save file", GTK_WINDOW (win), GTK_FILE_CHOOSER_ACTION_SAVE,
|
|
||||||
"_Cancel", GTK_RESPONSE_CANCEL,
|
|
||||||
"_Save", GTK_RESPONSE_ACCEPT,
|
|
||||||
NULL);
|
|
||||||
g_signal_connect (dialog, "response", G_CALLBACK (saveas_dialog_response), tv);
|
|
||||||
gtk_widget_show (dialog);
|
|
||||||
}
|
|
||||||
|
|
||||||
GtkWidget *
|
|
||||||
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;
|
|
||||||
|
|
||||||
if (! g_file_load_contents (file, NULL, &contents, &length, NULL, NULL)) /* read error */
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
tv = tfe_text_view_new();
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
GtkWidget *
|
|
||||||
tfe_text_view_new (void) {
|
|
||||||
return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL));
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
#define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type ()
|
|
||||||
G_DECLARE_FINAL_TYPE (TfeTextView, tfe_text_view, TFE, TEXT_VIEW, GtkTextView)
|
|
||||||
|
|
||||||
/* "open-response" signal response */
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
TFE_OPEN_RESPONSE_SUCCESS,
|
|
||||||
TFE_OPEN_RESPONSE_CANCEL,
|
|
||||||
TFE_OPEN_RESPONSE_ERROR
|
|
||||||
};
|
|
||||||
|
|
||||||
GFile *
|
|
||||||
tfe_text_view_get_file (TfeTextView *tv);
|
|
||||||
|
|
||||||
void
|
|
||||||
tfe_text_view_open (TfeTextView *tv, GtkWidget *win);
|
|
||||||
|
|
||||||
void
|
|
||||||
tfe_text_view_save (TfeTextView *tv);
|
|
||||||
|
|
||||||
void
|
|
||||||
tfe_text_view_saveas (TfeTextView *tv);
|
|
||||||
|
|
||||||
GtkWidget *
|
|
||||||
tfe_text_view_new_with_file (GFile *file);
|
|
||||||
|
|
||||||
GtkWidget *
|
|
||||||
tfe_text_view_new (void);
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include <string.h>
|
||||||
#include "tfetextview.h"
|
#include "tfetextview.h"
|
||||||
|
|
||||||
struct _TfeTextView
|
struct _TfeTextView
|
||||||
|
@ -28,11 +29,7 @@ tfe_text_view_dispose (GObject *gobject) {
|
||||||
|
|
||||||
static void
|
static void
|
||||||
tfe_text_view_init (TfeTextView *tv) {
|
tfe_text_view_init (TfeTextView *tv) {
|
||||||
GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
|
||||||
|
|
||||||
tv->file = NULL;
|
tv->file = NULL;
|
||||||
gtk_text_buffer_set_modified (tb, FALSE);
|
|
||||||
gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -129,6 +126,8 @@ saveas_dialog_response (GtkWidget *dialog, gint response, TfeTextView *tv) {
|
||||||
if (response == GTK_RESPONSE_ACCEPT) {
|
if (response == GTK_RESPONSE_ACCEPT) {
|
||||||
file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
|
file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
|
||||||
if (G_IS_FILE(file)) {
|
if (G_IS_FILE(file)) {
|
||||||
|
if (G_IS_FILE (tv->file))
|
||||||
|
g_object_unref (tv->file);
|
||||||
tv->file = file;
|
tv->file = file;
|
||||||
gtk_text_buffer_set_modified (tb, TRUE);
|
gtk_text_buffer_set_modified (tb, TRUE);
|
||||||
g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
|
g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
|
||||||
|
@ -151,7 +150,7 @@ tfe_text_view_save (TfeTextView *tv) {
|
||||||
GError *err = NULL;
|
GError *err = NULL;
|
||||||
|
|
||||||
if (! gtk_text_buffer_get_modified (tb))
|
if (! gtk_text_buffer_get_modified (tb))
|
||||||
return; /* no necessary to save it */
|
return; /* no need to save it */
|
||||||
else if (tv->file == NULL)
|
else if (tv->file == NULL)
|
||||||
tfe_text_view_saveas (tv);
|
tfe_text_view_saveas (tv);
|
||||||
else {
|
else {
|
||||||
|
@ -160,13 +159,12 @@ tfe_text_view_save (TfeTextView *tv) {
|
||||||
if (g_file_replace_contents (tv->file, contents, strlen (contents), NULL, TRUE, G_FILE_CREATE_NONE, NULL, NULL, &err))
|
if (g_file_replace_contents (tv->file, contents, strlen (contents), NULL, TRUE, G_FILE_CREATE_NONE, NULL, NULL, &err))
|
||||||
gtk_text_buffer_set_modified (tb, FALSE);
|
gtk_text_buffer_set_modified (tb, FALSE);
|
||||||
else {
|
else {
|
||||||
/* It is possible that tv->file is broken. */
|
/* It is possible that tv->file is broken or you don't have permission to write. */
|
||||||
/* It is a good idea to set tv->file to NULL. */
|
/* It is a good idea to set tv->file to NULL. */
|
||||||
if (G_IS_FILE (tv->file))
|
if (G_IS_FILE (tv->file))
|
||||||
g_object_unref (tv->file);
|
g_object_unref (tv->file);
|
||||||
tv->file =NULL;
|
tv->file =NULL;
|
||||||
g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
|
g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
|
||||||
gtk_text_buffer_set_modified (tb, TRUE);
|
|
||||||
message_dialog = gtk_message_dialog_new (GTK_WINDOW (win), GTK_DIALOG_MODAL,
|
message_dialog = gtk_message_dialog_new (GTK_WINDOW (win), GTK_DIALOG_MODAL,
|
||||||
GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
|
GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
|
||||||
"%s.\n", err->message);
|
"%s.\n", err->message);
|
||||||
|
@ -185,8 +183,8 @@ tfe_text_view_saveas (TfeTextView *tv) {
|
||||||
GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW);
|
GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW);
|
||||||
|
|
||||||
dialog = gtk_file_chooser_dialog_new ("Save file", GTK_WINDOW (win), GTK_FILE_CHOOSER_ACTION_SAVE,
|
dialog = gtk_file_chooser_dialog_new ("Save file", GTK_WINDOW (win), GTK_FILE_CHOOSER_ACTION_SAVE,
|
||||||
"_Cancel", GTK_RESPONSE_CANCEL,
|
"Cancel", GTK_RESPONSE_CANCEL,
|
||||||
"_Save", GTK_RESPONSE_ACCEPT,
|
"Save", GTK_RESPONSE_ACCEPT,
|
||||||
NULL);
|
NULL);
|
||||||
g_signal_connect (dialog, "response", G_CALLBACK (saveas_dialog_response), tv);
|
g_signal_connect (dialog, "response", G_CALLBACK (saveas_dialog_response), tv);
|
||||||
gtk_widget_show (dialog);
|
gtk_widget_show (dialog);
|
||||||
|
|
|
@ -3,12 +3,12 @@
|
||||||
TfeTextView -- Child widget of GtkTextView. It holds GFile the contents of GtkTextBuffer correponds to.
|
TfeTextView -- Child widget of GtkTextView. It holds GFile the contents of GtkTextBuffer correponds to.
|
||||||
|
|
||||||
## Functions
|
## Functions
|
||||||
- GFile *[tfe_text_view_get_file ()](#tfe_text_view_get_file-)
|
- GFile *[tfe_text_view_get_file ()](#tfe_text_view_get_file)
|
||||||
- void [tfe_text_view_open ()](#tfe_text_view_open-)
|
- void [tfe_text_view_open ()](#tfe_text_view_open)
|
||||||
- void [tfe_text_view_save ()](#tfe_text_view_save-)
|
- void [tfe_text_view_save ()](#tfe_text_view_save)
|
||||||
- void [tfe_text_view_saveas ()](#tfe_text_view_saveas-)
|
- void [tfe_text_view_saveas ()](#tfe_text_view_saveas)
|
||||||
- GtkWidget *[tfe_text_view_new_with_file ()](#tfe_text_view_new_with_file-)
|
- GtkWidget *[tfe_text_view_new_with_file ()](#tfe_text_view_new_with_file)
|
||||||
- GtkWidget *[tfe_text_view_new ()](#tfe_text_view_new-)
|
- GtkWidget *[tfe_text_view_new ()](#tfe_text_view_new)
|
||||||
|
|
||||||
## Signals
|
## Signals
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ File manipulation functions have been added to this object.
|
||||||
|
|
||||||
## Functions
|
## Functions
|
||||||
|
|
||||||
### tfe_text_view_get_file ()
|
### tfe_text_view_get_file()
|
||||||
|
|
||||||
~~~
|
~~~
|
||||||
GFile *
|
GFile *
|
||||||
|
@ -57,7 +57,7 @@ Parameters
|
||||||
|
|
||||||
- tv: a TfeTextView
|
- tv: a TfeTextView
|
||||||
|
|
||||||
### tfe_text_view_open ()
|
### tfe_text_view_open()
|
||||||
|
|
||||||
~~~
|
~~~
|
||||||
void
|
void
|
||||||
|
@ -76,7 +76,7 @@ parameters
|
||||||
- tv: a TfeTextView
|
- tv: a TfeTextView
|
||||||
- win: the top level window of the TfeTextView
|
- win: the top level window of the TfeTextView
|
||||||
|
|
||||||
### tfe_text_view_save ()
|
### tfe_text_view_save()
|
||||||
|
|
||||||
~~~
|
~~~
|
||||||
void
|
void
|
||||||
|
@ -91,7 +91,7 @@ Parameters
|
||||||
|
|
||||||
- tv: a TfeTextView
|
- tv: a TfeTextView
|
||||||
|
|
||||||
### tfe_text_view_saveas ()
|
### tfe_text_view_saveas()
|
||||||
|
|
||||||
~~~
|
~~~
|
||||||
void
|
void
|
||||||
|
@ -105,7 +105,7 @@ Parameters
|
||||||
|
|
||||||
- tv: a TfeTextView
|
- tv: a TfeTextView
|
||||||
|
|
||||||
### tfe_text_view_new_with_file ()
|
### tfe_text_view_new_with_file()
|
||||||
|
|
||||||
~~~
|
~~~
|
||||||
GtkWidget *
|
GtkWidget *
|
||||||
|
@ -124,7 +124,7 @@ Returns
|
||||||
|
|
||||||
- a new TfeTextView.
|
- a new TfeTextView.
|
||||||
|
|
||||||
### tfe_text_view_new ()
|
### tfe_text_view_new()
|
||||||
|
|
||||||
~~~
|
~~~
|
||||||
GtkWidget *
|
GtkWidget *
|
||||||
|
|
|
@ -7,15 +7,18 @@ exec ruby -x "$0" "$@"
|
||||||
require_relative 'lib/lib_src2md.rb'
|
require_relative 'lib/lib_src2md.rb'
|
||||||
|
|
||||||
def usage
|
def usage
|
||||||
$stderr.print "Usage: ruby srcd2md.rb src.md_file md_file\n"
|
$stderr.print "Usage: ruby srcd2md.rb src.md_file md_file width\n"
|
||||||
|
$stderr.print " The width is used to fold lines in indented or fenced code blocks.\n"
|
||||||
|
$stderr.print " If the width is negative, no lines are folded.\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
if ARGV.size != 2
|
if ARGV.size != 3
|
||||||
usage
|
usage
|
||||||
exit 1
|
exit 1
|
||||||
end
|
end
|
||||||
|
|
||||||
srcmd = ARGV[0]
|
srcmd = ARGV[0]
|
||||||
md = ARGV[1]
|
md = ARGV[1]
|
||||||
src2md srcmd, md, 80
|
width = ARGV[2]
|
||||||
|
src2md srcmd, md, width.to_i
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue