Add section 25.
4
.gitignore
vendored
|
@ -24,8 +24,8 @@ src/color/_build
|
|||
src/turtle/_build
|
||||
src/temp
|
||||
src/le
|
||||
|
||||
doc/GFile_example/_build
|
||||
src/list4/_build
|
||||
src/list5/_build
|
||||
html/*
|
||||
latex/*
|
||||
|
||||
|
|
|
@ -38,3 +38,4 @@ You can read it without download.
|
|||
1. [Combine GtkDrawingArea and TfeTextView](gfm/sec22.md)
|
||||
1. [Tiny turtle graphics interpreter](gfm/sec23.md)
|
||||
1. [GtkListView](gfm/sec24.md)
|
||||
1. [GtkGridView and activate signal](gfm/sec25.md)
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
/* Samples of GFile related objects */
|
||||
/* Copy file program */
|
||||
/* This function uses 'g_input_stream_read' and 'g_output_stream_write'. */
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
void
|
||||
usage () {
|
||||
g_printerr ("Usage: copy file1 file2\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
#define MAXBUF 4096
|
||||
int
|
||||
main (int argc, char **argv) {
|
||||
GFile *file1, *file2;
|
||||
GFileInputStream *istream;
|
||||
GFileOutputStream *ostream;
|
||||
char buf[MAXBUF];
|
||||
gssize size;
|
||||
GError *err = NULL;
|
||||
int status = 0;
|
||||
|
||||
if (argc != 3)
|
||||
usage();
|
||||
file1 = g_file_new_for_commandline_arg (argv[1]);
|
||||
file2 = g_file_new_for_commandline_arg (argv[2]);
|
||||
if (! (istream = g_file_read (file1, NULL, &err))) {
|
||||
g_warning ("%s\n", err->message);
|
||||
return 1;
|
||||
}
|
||||
if (! (ostream = g_file_replace (file2, NULL, TRUE, G_FILE_CREATE_NONE, NULL, &err))) {
|
||||
g_warning ("%s\n", err->message);
|
||||
return 1;
|
||||
}
|
||||
while ((size = g_input_stream_read (G_INPUT_STREAM (istream), buf, MAXBUF, NULL, &err)) > 0) {
|
||||
if ((size = g_output_stream_write (G_OUTPUT_STREAM (ostream), buf, (gsize) size, NULL, &err)) < 0)
|
||||
break;
|
||||
}
|
||||
if (size < 0) {
|
||||
g_warning ("%s\n", err->message);
|
||||
status = 1;
|
||||
}
|
||||
if (! (g_input_stream_close (G_INPUT_STREAM (istream), NULL, &err))) {
|
||||
g_warning ("%s\n", err->message);
|
||||
status = 1;
|
||||
}
|
||||
if (! (g_output_stream_close (G_OUTPUT_STREAM (ostream), NULL, &err))) {
|
||||
g_warning ("%s\n", err->message);
|
||||
status = 1;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
/* Samples of GFile related objects */
|
||||
/* Copy file program */
|
||||
/* This function uses 'g_input_stream_read' and 'g_output_stream_write'. */
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
void
|
||||
usage () {
|
||||
g_printerr ("Usage: gfile_object (file|uri)\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv) {
|
||||
GFile *file;
|
||||
GType type;
|
||||
|
||||
if (argc != 2)
|
||||
usage();
|
||||
file = g_file_new_for_commandline_arg (argv[1]);
|
||||
type = G_OBJECT_TYPE (file);
|
||||
g_print ("%s\n", g_type_name (type));
|
||||
while (type = g_type_parent (type))
|
||||
g_print ("%s\n", g_type_name (type));
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
project('gio_samples', 'c')
|
||||
|
||||
compiler = meson.get_compiler('c')
|
||||
|
||||
giodep = dependency('gio-2.0')
|
||||
|
||||
executable('readline', 'readline.c', dependencies: giodep, install: false)
|
||||
executable('copy', 'copy.c', dependencies: giodep, install: false)
|
||||
executable('gfile_object', 'gfile_object.c', dependencies: giodep, install: false)
|
||||
|
||||
run_command('ruby', '../../src2md.rb', 'readme.src.md', 'readme.md')
|
|
@ -1,84 +0,0 @@
|
|||
/* Samples of GFile related objects */
|
||||
/* Functions here has "sgf" prefix. Sgf is an acronym of "Sample of GFile". */
|
||||
|
||||
/* List a file */
|
||||
/* This program shows how to read lines from a file. */
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <glib/gprintf.h>
|
||||
|
||||
void
|
||||
usage () {
|
||||
g_printerr ("Usage: readline file\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
/* Inputstream objects */
|
||||
/* GInputStream */
|
||||
/* +----- GFileInputStream */
|
||||
/* +--+-- GFilterInputStream */
|
||||
/* +--+-- GBufferedInputStream */
|
||||
/* +----- GDataInputStream */
|
||||
|
||||
GDataInputStream *
|
||||
sgf_open (GFile *file) {
|
||||
g_return_val_if_fail (G_IS_FILE (file), NULL);
|
||||
|
||||
GFileInputStream *stream;
|
||||
GDataInputStream *dstream;
|
||||
GError *err;
|
||||
|
||||
if (! (stream = g_file_read (file, NULL, &err))) {
|
||||
g_warning ("%s\n", err->message);
|
||||
return NULL;
|
||||
}
|
||||
/* 'dstream' is a different instance from 'stream' instance. */
|
||||
/* 'dstream' has "base-stream" property, which is a property of GFilterInputStream, and the property points 'stream'. */
|
||||
/* GBufferedInputStream uses base-stream to read (input) data from its source. */
|
||||
/* Therefore, 'dstream' reads a line through the IO interface of 'stream'. */
|
||||
dstream = g_data_input_stream_new (G_INPUT_STREAM (stream));
|
||||
return dstream;
|
||||
}
|
||||
|
||||
char *
|
||||
sgf_readline (GDataInputStream *dstream) {
|
||||
char *contents;
|
||||
gsize length;
|
||||
GError *err = NULL;
|
||||
|
||||
contents = g_data_input_stream_read_line_utf8 (dstream, &length, NULL, &err);
|
||||
if (! contents && err) {
|
||||
g_warning ("%s\n", err->message);
|
||||
return NULL;
|
||||
}
|
||||
return contents; /* if contents == NULL, then it is EOF */
|
||||
}
|
||||
|
||||
void
|
||||
sgf_close (GDataInputStream *dstream) {
|
||||
GError *err = NULL;
|
||||
|
||||
/* At the same time dstream closes, its base stream (GFileInputStream created by 'sgf_open') is also closed. */
|
||||
/* Because the default of "close-base-stream" property (of GFilterInputStream which is an ancester of GDataInputStream) is TRUE */
|
||||
if (! g_input_stream_close (G_INPUT_STREAM (dstream), NULL, &err))
|
||||
g_warning ("%s\n", err->message);
|
||||
}
|
||||
|
||||
/* ----- main ----- */
|
||||
int
|
||||
main (int argc, char **argv) {
|
||||
GFile *file;
|
||||
GDataInputStream *dstream;
|
||||
char *line;
|
||||
|
||||
if (argc != 2)
|
||||
usage ();
|
||||
file = g_file_new_for_commandline_arg (argv[1]);
|
||||
if (! (dstream = sgf_open (file)))
|
||||
return -1; /* error */
|
||||
while (line = sgf_readline (dstream))
|
||||
g_printf ("%s\n", line);
|
||||
sgf_close (dstream);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,213 +0,0 @@
|
|||
# What are the files in this directory?
|
||||
|
||||
They are examples to show how to use GFile related objects such as GInputStream and GOutPutStream.
|
||||
|
||||
- `meson.build` is used by meson.
|
||||
- `readline.c` is an example to show how to read lines from a file.
|
||||
- `copy.c` is an example to show read and write stream functions with Gio objects.
|
||||
- `gfile_object.c` shows the object implements GFile interface.
|
||||
|
||||
## Compilation
|
||||
|
||||
Type as follows on your command line.
|
||||
|
||||
1. `meson _build`
|
||||
2. `ninja -C _build`
|
||||
|
||||
Then executable files `readline`, `copy` and `gfile_object` are created under `_build` directory.
|
||||
|
||||
## Execution
|
||||
|
||||
Readline prints the contents of a file.
|
||||
The file is given to `readline` as an argument.
|
||||
|
||||
~~~
|
||||
$ _build/readline readline.c
|
||||
/* Samples of GFile related objects */
|
||||
/* Functions here has "sgf" prefix. Sgf is an acronym of "Sample of GFile". */
|
||||
|
||||
/* List a file */
|
||||
/* This program shows how to read lines from a file. */
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
void
|
||||
usage () {
|
||||
g_printerr ("Usage: readline file\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
... ... ...
|
||||
... ... ...
|
||||
~~~
|
||||
|
||||
Readline can also prints a file through the internet.
|
||||
|
||||
~~~
|
||||
$ _build/readline http://www7b.biglobe.ne.jp/~j87107/SINU/index.html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
|
||||
... ... ...
|
||||
... ... ...
|
||||
~~~
|
||||
|
||||
This is because GFile can be constructed for local path or uri.
|
||||
|
||||
Copy is given two arguments.
|
||||
The first one is a source filename and the second one is a destination filename.
|
||||
If the destination file is exist, it will be overwritten.
|
||||
Before the overwriting, the destination file is backed up.
|
||||
The backup file name is appended with tilde ('~').
|
||||
|
||||
~~~
|
||||
$ ls
|
||||
_build copy.c meson.build readline.c readme.md
|
||||
$ _build/copy copy.c sample.txt
|
||||
$ ls
|
||||
_build copy.c meson.build readline.c readme.md sample.txt
|
||||
$ diff copy.c sample.txt
|
||||
$ _build/copy readline.c sample.txt
|
||||
$ ls
|
||||
_build copy.c meson.build readline.c readme.md sample.txt sample.txt~
|
||||
$ diff readline.c sample.txt
|
||||
$ diff copy.c sample.txt~
|
||||
$
|
||||
~~~
|
||||
|
||||
Files can be on the internet.
|
||||
|
||||
~~~
|
||||
$ _build/copy http://www7b.biglobe.ne.jp/~j87107/SINU/Algebra.pdf algebra.pdf
|
||||
$ ls
|
||||
_build algebra.pdf copy.c meson.build readline.c readme.md sample.txt sample.txt~
|
||||
~~~
|
||||
|
||||
Gfile_object shows the object implements GFile interface.
|
||||
|
||||
~~~
|
||||
$ _build/gfile_object gfile_object.c
|
||||
GLocalFile
|
||||
GObject
|
||||
$ _build/gfile_object http://www7b.biglobe.ne.jp/~j87107/SINU/Algebra.pdf
|
||||
GDaemonFile
|
||||
GObject
|
||||
~~~
|
||||
|
||||
`g_file_new_for_path` creates GLocalFile object which implements GFile interface.
|
||||
And it returns the pointer to the object as `GFile *`.
|
||||
`g_file_new_for_uri` creates GDaemonFile object which implements GFile interface.
|
||||
And it returns the pointer to the object as `GFile *`.
|
||||
|
||||
## GFile interface
|
||||
|
||||
GFile is an interface.
|
||||
It is like a pathname.
|
||||
|
||||
If you want to read from a file or write to a file, you need to create a stream object.
|
||||
`copy.c` creates GInputStream and GOutputStream to copy a file.
|
||||
|
||||
~~~C
|
||||
1 /* Samples of GFile related objects */
|
||||
2 /* Copy file program */
|
||||
3 /* This function uses 'g_input_stream_read' and 'g_output_stream_write'. */
|
||||
4
|
||||
5 #include <gio/gio.h>
|
||||
6
|
||||
7 void
|
||||
8 usage () {
|
||||
9 g_printerr ("Usage: copy file1 file2\n");
|
||||
10 exit (1);
|
||||
11 }
|
||||
12
|
||||
13 #define MAXBUF 4096
|
||||
14 int
|
||||
15 main (int argc, char **argv) {
|
||||
16 GFile *file1, *file2;
|
||||
17 GFileInputStream *istream;
|
||||
18 GFileOutputStream *ostream;
|
||||
19 char buf[MAXBUF];
|
||||
20 gssize size;
|
||||
21 GError *err = NULL;
|
||||
22 int status = 0;
|
||||
23
|
||||
24 if (argc != 3)
|
||||
25 usage();
|
||||
26 file1 = g_file_new_for_commandline_arg (argv[1]);
|
||||
27 file2 = g_file_new_for_commandline_arg (argv[2]);
|
||||
28 if (! (istream = g_file_read (file1, NULL, &err))) {
|
||||
29 g_warning ("%s\n", err->message);
|
||||
30 return 1;
|
||||
31 }
|
||||
32 if (! (ostream = g_file_replace (file2, NULL, TRUE, G_FILE_CREATE_NONE, NULL, &err))) {
|
||||
33 g_warning ("%s\n", err->message);
|
||||
34 return 1;
|
||||
35 }
|
||||
36 while ((size = g_input_stream_read (G_INPUT_STREAM (istream), buf, MAXBUF, NULL, &err)) > 0) {
|
||||
37 if ((size = g_output_stream_write (G_OUTPUT_STREAM (ostream), buf, (gsize) size, NULL, &err)) < 0)
|
||||
38 break;
|
||||
39 }
|
||||
40 if (size < 0) {
|
||||
41 g_warning ("%s\n", err->message);
|
||||
42 status = 1;
|
||||
43 }
|
||||
44 if (! (g_input_stream_close (G_INPUT_STREAM (istream), NULL, &err))) {
|
||||
45 g_warning ("%s\n", err->message);
|
||||
46 status = 1;
|
||||
47 }
|
||||
48 if (! (g_output_stream_close (G_OUTPUT_STREAM (ostream), NULL, &err))) {
|
||||
49 g_warning ("%s\n", err->message);
|
||||
50 status = 1;
|
||||
51 }
|
||||
52 return status;
|
||||
53 }
|
||||
54
|
||||
~~~
|
||||
|
||||
## Stream objects
|
||||
|
||||
Stream objects have hierarchy.
|
||||
|
||||
~~~
|
||||
GInputStream
|
||||
+--+-- GFilterInputStream
|
||||
| +--+-- GBufferedInputStream
|
||||
| | +----- GDataInputStream
|
||||
| +----- GConverterInputStream
|
||||
+----- GFileInputStream
|
||||
+----- GMemoryInputStream
|
||||
+----- GUnixInputStream
|
||||
~~~
|
||||
|
||||
GInputStream is the top parent object.
|
||||
It is an abstract object.
|
||||
Streams are created with specific objects such as files, memories and unix file descriptors.
|
||||
For example, the following function creates a GFileInputStream.
|
||||
|
||||
~~~C
|
||||
GFileInputStream *istream = g_file_read (GFile *file, GCancellable *cancellable, GError **error);
|
||||
~~~
|
||||
|
||||
This stream is a child of GInputStream object, so you can apply any functions of GInputStream for `istream`.
|
||||
For example,
|
||||
|
||||
~~~C
|
||||
gssize size = g_input_stream_read (G_INPUT_STREAM (istream), void *buffer, gsize count, GCancellable *cancellable, GError **error);
|
||||
~~~
|
||||
|
||||
This function reads data from `istream` and puts them into `buffer`.
|
||||
|
||||
GDataInputStream is used often.
|
||||
It can read structured data such as sized data (byte, int16, int32 and int64) and lines (new line terminated string).
|
||||
For example,
|
||||
|
||||
~~~C
|
||||
GDataInputStream *dstream = g_data_input_stream_new (G_INPUT_STREAM (istream));
|
||||
char *line = g_data_input_stream_read_line_utf8 (dstream, gsize *size, GCancellable *cancellable, GError **error);
|
||||
~~~
|
||||
|
||||
The program above reads a line from `dstream`.
|
||||
|
|
@ -1,160 +0,0 @@
|
|||
# What are the files in this directory?
|
||||
|
||||
They are examples to show how to use GFile related objects such as GInputStream and GOutPutStream.
|
||||
|
||||
- `meson.build` is used by meson.
|
||||
- `readline.c` is an example to show how to read lines from a file.
|
||||
- `copy.c` is an example to show read and write stream functions with Gio objects.
|
||||
- `gfile_object.c` shows the object implements GFile interface.
|
||||
|
||||
## Compilation
|
||||
|
||||
Type as follows on your command line.
|
||||
|
||||
1. `meson _build`
|
||||
2. `ninja -C _build`
|
||||
|
||||
Then executable files `readline`, `copy` and `gfile_object` are created under `_build` directory.
|
||||
|
||||
## Execution
|
||||
|
||||
Readline prints the contents of a file.
|
||||
The file is given to `readline` as an argument.
|
||||
|
||||
~~~
|
||||
$ _build/readline readline.c
|
||||
/* Samples of GFile related objects */
|
||||
/* Functions here has "sgf" prefix. Sgf is an acronym of "Sample of GFile". */
|
||||
|
||||
/* List a file */
|
||||
/* This program shows how to read lines from a file. */
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
void
|
||||
usage () {
|
||||
g_printerr ("Usage: readline file\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
... ... ...
|
||||
... ... ...
|
||||
~~~
|
||||
|
||||
Readline can also prints a file through the internet.
|
||||
|
||||
~~~
|
||||
$ _build/readline http://www7b.biglobe.ne.jp/~j87107/SINU/index.html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
|
||||
... ... ...
|
||||
... ... ...
|
||||
~~~
|
||||
|
||||
This is because GFile can be constructed for local path or uri.
|
||||
|
||||
Copy is given two arguments.
|
||||
The first one is a source filename and the second one is a destination filename.
|
||||
If the destination file is exist, it will be overwritten.
|
||||
Before the overwriting, the destination file is backed up.
|
||||
The backup file name is appended with tilde ('~').
|
||||
|
||||
~~~
|
||||
$ ls
|
||||
_build copy.c meson.build readline.c readme.md
|
||||
$ _build/copy copy.c sample.txt
|
||||
$ ls
|
||||
_build copy.c meson.build readline.c readme.md sample.txt
|
||||
$ diff copy.c sample.txt
|
||||
$ _build/copy readline.c sample.txt
|
||||
$ ls
|
||||
_build copy.c meson.build readline.c readme.md sample.txt sample.txt~
|
||||
$ diff readline.c sample.txt
|
||||
$ diff copy.c sample.txt~
|
||||
$
|
||||
~~~
|
||||
|
||||
Files can be on the internet.
|
||||
|
||||
~~~
|
||||
$ _build/copy http://www7b.biglobe.ne.jp/~j87107/SINU/Algebra.pdf algebra.pdf
|
||||
$ ls
|
||||
_build algebra.pdf copy.c meson.build readline.c readme.md sample.txt sample.txt~
|
||||
~~~
|
||||
|
||||
Gfile_object shows the object implements GFile interface.
|
||||
|
||||
~~~
|
||||
$ _build/gfile_object gfile_object.c
|
||||
GLocalFile
|
||||
GObject
|
||||
$ _build/gfile_object http://www7b.biglobe.ne.jp/~j87107/SINU/Algebra.pdf
|
||||
GDaemonFile
|
||||
GObject
|
||||
~~~
|
||||
|
||||
`g_file_new_for_path` creates GLocalFile object which implements GFile interface.
|
||||
And it returns the pointer to the object as `GFile *`.
|
||||
`g_file_new_for_uri` creates GDaemonFile object which implements GFile interface.
|
||||
And it returns the pointer to the object as `GFile *`.
|
||||
|
||||
## GFile interface
|
||||
|
||||
GFile is an interface.
|
||||
It is like a pathname.
|
||||
|
||||
If you want to read from a file or write to a file, you need to create a stream object.
|
||||
`copy.c` creates GInputStream and GOutputStream to copy a file.
|
||||
|
||||
@@@include
|
||||
copy.c
|
||||
@@@
|
||||
|
||||
## Stream objects
|
||||
|
||||
Stream objects have hierarchy.
|
||||
|
||||
~~~
|
||||
GInputStream
|
||||
+--+-- GFilterInputStream
|
||||
| +--+-- GBufferedInputStream
|
||||
| | +----- GDataInputStream
|
||||
| +----- GConverterInputStream
|
||||
+----- GFileInputStream
|
||||
+----- GMemoryInputStream
|
||||
+----- GUnixInputStream
|
||||
~~~
|
||||
|
||||
GInputStream is the top parent object.
|
||||
It is an abstract object.
|
||||
Streams are created with specific objects such as files, memories and unix file descriptors.
|
||||
For example, the following function creates a GFileInputStream.
|
||||
|
||||
~~~C
|
||||
GFileInputStream *istream = g_file_read (GFile *file, GCancellable *cancellable, GError **error);
|
||||
~~~
|
||||
|
||||
This stream is a child of GInputStream object, so you can apply any functions of GInputStream for `istream`.
|
||||
For example,
|
||||
|
||||
~~~C
|
||||
gssize size = g_input_stream_read (G_INPUT_STREAM (istream), void *buffer, gsize count, GCancellable *cancellable, GError **error);
|
||||
~~~
|
||||
|
||||
This function reads data from `istream` and puts them into `buffer`.
|
||||
|
||||
GDataInputStream is used often.
|
||||
It can read structured data such as sized data (byte, int16, int32 and int64) and lines (new line terminated string).
|
||||
For example,
|
||||
|
||||
~~~C
|
||||
GDataInputStream *dstream = g_data_input_stream_new (G_INPUT_STREAM (istream));
|
||||
char *line = g_data_input_stream_read_line_utf8 (dstream, gsize *size, GCancellable *cancellable, GError **error);
|
||||
~~~
|
||||
|
||||
The program above reads a line from `dstream`.
|
||||
|
71
gfm/sec24.md
|
@ -1,4 +1,4 @@
|
|||
Up: [Readme.md](../Readme.md), Prev: [Section 23](sec23.md)
|
||||
Up: [Readme.md](../Readme.md), Prev: [Section 23](sec23.md), Next: [Section 25](sec25.md)
|
||||
|
||||
# GtkListView
|
||||
|
||||
|
@ -150,7 +150,7 @@ GtkNoSelection is used, so user can't select any item.
|
|||
30
|
||||
31 /* ----- activate, open, startup handlers ----- */
|
||||
32 static void
|
||||
33 tfe_activate (GApplication *application) {
|
||||
33 app_activate (GApplication *application) {
|
||||
34 GtkApplication *app = GTK_APPLICATION (application);
|
||||
35 GtkWidget *win = gtk_application_window_new (app);
|
||||
36 gtk_window_set_default_size (GTK_WINDOW (win), 600, 400);
|
||||
|
@ -175,7 +175,7 @@ GtkNoSelection is used, so user can't select any item.
|
|||
55 }
|
||||
56
|
||||
57 static void
|
||||
58 tfe_startup (GApplication *application) {
|
||||
58 app_startup (GApplication *application) {
|
||||
59 }
|
||||
60
|
||||
61 /* ----- main ----- */
|
||||
|
@ -186,8 +186,8 @@ GtkNoSelection is used, so user can't select any item.
|
|||
66
|
||||
67 app = gtk_application_new ("com.github.ToshioCP.list1", G_APPLICATION_FLAGS_NONE);
|
||||
68
|
||||
69 g_signal_connect (app, "startup", G_CALLBACK (tfe_startup), NULL);
|
||||
70 g_signal_connect (app, "activate", G_CALLBACK (tfe_activate), NULL);
|
||||
69 g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL);
|
||||
70 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
|
||||
71
|
||||
72 stat =g_application_run (G_APPLICATION (app), argc, argv);
|
||||
73 g_object_unref (app);
|
||||
|
@ -260,7 +260,7 @@ Its name is `list2.c` and located under [src/misc](../src/misc) directory.
|
|||
2
|
||||
3 /* ----- activate, open, startup handlers ----- */
|
||||
4 static void
|
||||
5 tfe_activate (GApplication *application) {
|
||||
5 app_activate (GApplication *application) {
|
||||
6 GtkApplication *app = GTK_APPLICATION (application);
|
||||
7 GtkWidget *win = gtk_application_window_new (app);
|
||||
8 gtk_window_set_default_size (GTK_WINDOW (win), 600, 400);
|
||||
|
@ -297,7 +297,7 @@ Its name is `list2.c` and located under [src/misc](../src/misc) directory.
|
|||
39 }
|
||||
40
|
||||
41 static void
|
||||
42 tfe_startup (GApplication *application) {
|
||||
42 app_startup (GApplication *application) {
|
||||
43 }
|
||||
44
|
||||
45 /* ----- main ----- */
|
||||
|
@ -308,8 +308,8 @@ Its name is `list2.c` and located under [src/misc](../src/misc) directory.
|
|||
50
|
||||
51 app = gtk_application_new ("com.github.ToshioCP.list2", G_APPLICATION_FLAGS_NONE);
|
||||
52
|
||||
53 g_signal_connect (app, "startup", G_CALLBACK (tfe_startup), NULL);
|
||||
54 g_signal_connect (app, "activate", G_CALLBACK (tfe_activate), NULL);
|
||||
53 g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL);
|
||||
54 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
|
||||
55
|
||||
56 stat =g_application_run (G_APPLICATION (app), argc, argv);
|
||||
57 g_object_unref (app);
|
||||
|
@ -424,7 +424,7 @@ The program is located in [src/misc](../src/misc) directory.
|
|||
10
|
||||
11 /* ----- activate, open, startup handlers ----- */
|
||||
12 static void
|
||||
13 tfe_activate (GApplication *application) {
|
||||
13 app_activate (GApplication *application) {
|
||||
14 GtkApplication *app = GTK_APPLICATION (application);
|
||||
15 GtkWidget *win = gtk_application_window_new (app);
|
||||
16 gtk_window_set_default_size (GTK_WINDOW (win), 600, 400);
|
||||
|
@ -455,31 +455,30 @@ The program is located in [src/misc](../src/misc) directory.
|
|||
41 GtkListItemFactory *factory = gtk_builder_list_item_factory_new_from_bytes (NULL, gbytes);
|
||||
42
|
||||
43 GtkWidget *lv = gtk_list_view_new (GTK_SELECTION_MODEL (ns), factory);
|
||||
44 gtk_list_view_set_enable_rubberband (GTK_LIST_VIEW (lv), TRUE);
|
||||
45 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), lv);
|
||||
46 gtk_widget_show (win);
|
||||
47 }
|
||||
48
|
||||
49 static void
|
||||
50 tfe_startup (GApplication *application) {
|
||||
51 }
|
||||
52
|
||||
53 /* ----- main ----- */
|
||||
54 int
|
||||
55 main (int argc, char **argv) {
|
||||
56 GtkApplication *app;
|
||||
57 int stat;
|
||||
58
|
||||
59 app = gtk_application_new ("com.github.ToshioCP.list2", G_APPLICATION_FLAGS_NONE);
|
||||
60
|
||||
61 g_signal_connect (app, "startup", G_CALLBACK (tfe_startup), NULL);
|
||||
62 g_signal_connect (app, "activate", G_CALLBACK (tfe_activate), NULL);
|
||||
63
|
||||
64 stat =g_application_run (G_APPLICATION (app), argc, argv);
|
||||
65 g_object_unref (app);
|
||||
66 return stat;
|
||||
67 }
|
||||
68
|
||||
44 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), lv);
|
||||
45 gtk_widget_show (win);
|
||||
46 }
|
||||
47
|
||||
48 static void
|
||||
49 app_startup (GApplication *application) {
|
||||
50 }
|
||||
51
|
||||
52 /* ----- main ----- */
|
||||
53 int
|
||||
54 main (int argc, char **argv) {
|
||||
55 GtkApplication *app;
|
||||
56 int stat;
|
||||
57
|
||||
58 app = gtk_application_new ("com.github.ToshioCP.list3", G_APPLICATION_FLAGS_NONE);
|
||||
59
|
||||
60 g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL);
|
||||
61 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
|
||||
62
|
||||
63 stat =g_application_run (G_APPLICATION (app), argc, argv);
|
||||
64 g_object_unref (app);
|
||||
65 return stat;
|
||||
66 }
|
||||
67
|
||||
~~~
|
||||
|
||||
Compile and execute it.
|
||||
|
@ -493,4 +492,4 @@ $ ./a.out
|
|||
![screenshot list3](../image/list3.png)
|
||||
|
||||
|
||||
Up: [Readme.md](../Readme.md), Prev: [Section 23](sec23.md)
|
||||
Up: [Readme.md](../Readme.md), Prev: [Section 23](sec23.md), Next: [Section 25](sec25.md)
|
||||
|
|
564
gfm/sec25.md
Normal file
|
@ -0,0 +1,564 @@
|
|||
Up: [Readme.md](../Readme.md), Prev: [Section 24](sec24.md)
|
||||
|
||||
# GtkGridView and activate signal
|
||||
|
||||
GtkGridView is similar to GtkListView.
|
||||
It displays a GListModel as a grid, which is like a square tessellation.
|
||||
|
||||
![Grid](../image/list4.png)
|
||||
|
||||
This is often seen when you use a file browser like nautilus.
|
||||
|
||||
In this section, let's make a very simple file browser `list4`.
|
||||
It just shows the files in the current directory.
|
||||
And a user can choose list or grid by clicking on buttons in the tool bar.
|
||||
Each item in the list or grid has an icon and a filename.
|
||||
In addition, `list4` provides the way to open the `tfe` text editor to show a text file.
|
||||
A user can do that by double clicking on an item or pressing enter key when an item is selected.
|
||||
|
||||
## GtkDirectoryList
|
||||
|
||||
GtkDirectoryList implements GListModel and it contains information of files in a certain directory.
|
||||
The items of the list are GFileInfo objects.
|
||||
|
||||
In the `list4` source files, GtkDirectoryList is described in a ui file and built by GtkBuilder.
|
||||
It is referenced to by a GtkSingleSelection model and the GtkSingleSelection model is referenced to by a GListView or GGridView object.
|
||||
|
||||
~~~
|
||||
GtkListView (model property) => GtkSingleSelection (model property) => GtkDirectoryList
|
||||
GtkGridView (model property) => GtkSingleSelection (model property) => GtkDirectoryList
|
||||
~~~
|
||||
|
||||
![DirectoryList](../image/directorylist.png)
|
||||
|
||||
The part of the ui file `list4.ui` is as follows.
|
||||
|
||||
~~~xml
|
||||
<object class="GtkListView" id="list">
|
||||
<property name="model">
|
||||
<object class="GtkSingleSelection" id="singleselection">
|
||||
<property name="model">
|
||||
<object class="GtkDirectoryList" id="directorylist">
|
||||
<property name="attributes">standard::name,standard::icon,standard::content-type</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
<object class="GtkGridView" id="grid">
|
||||
<property name="model">singleselection</property>
|
||||
</object>
|
||||
~~~
|
||||
|
||||
GtkDirectoryList has an "attributes" property.
|
||||
It is attributes of GFileInfo such as "standard::name", "standard::icon" and "standard::content-type".
|
||||
|
||||
- standard::name is a filename.
|
||||
- standard::icon is an icon of the file. It is a GIcon object.
|
||||
- standard::content-type is a content-type.
|
||||
Content-type is the same as mime type for the internet technology.
|
||||
For example, "text/plain" is a text file, "text/x-csrc" is a C source code and so on.
|
||||
("text/x-csrc"is not registered to IANA media types.
|
||||
Such "x-" subtype is not a standard mime type.)
|
||||
Content type is also used by the desktop system.
|
||||
|
||||
GtkGridView has the same structure as GtkListView.
|
||||
But it is enough to specify its model property to `singleselection` which is the identification of the GtkSingleSelection.
|
||||
Therefore the description for GtkGridView is very short.
|
||||
|
||||
## Ui file of the window
|
||||
|
||||
Look at the screenshot of `list4` at the top of this section.
|
||||
The widgets are built with the following ui file.
|
||||
|
||||
~~~xml
|
||||
1 <?xml version="1.0" encoding="UTF-8"?>
|
||||
2 <interface>
|
||||
3 <object class="GtkApplicationWindow" id="win">
|
||||
4 <property name="title">file list</property>
|
||||
5 <property name="default-width">600</property>
|
||||
6 <property name="default-height">400</property>
|
||||
7 <child>
|
||||
8 <object class="GtkBox" id="boxv">
|
||||
9 <property name="orientation">GTK_ORIENTATION_VERTICAL</property>
|
||||
10 <child>
|
||||
11 <object class="GtkBox" id="boxh">
|
||||
12 <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
|
||||
13 <child>
|
||||
14 <object class="GtkLabel" id="dmy1">
|
||||
15 <property name="hexpand">TRUE</property>
|
||||
16 </object>
|
||||
17 </child>
|
||||
18 <child>
|
||||
19 <object class="GtkButton" id="btnlist">
|
||||
20 <property name="name">btnlist</property>
|
||||
21 <property name="action-name">win.view</property>
|
||||
22 <property name="action-target">'list'</property>
|
||||
23 <child>
|
||||
24 <object class="GtkImage">
|
||||
25 <property name="resource">/com/github/ToshioCP/list4/list.png</property>
|
||||
26 </object>
|
||||
27 </child>
|
||||
28 </object>
|
||||
29 </child>
|
||||
30 <child>
|
||||
31 <object class="GtkButton" id="btngrid">
|
||||
32 <property name="name">btngrid</property>
|
||||
33 <property name="action-name">win.view</property>
|
||||
34 <property name="action-target">'grid'</property>
|
||||
35 <child>
|
||||
36 <object class="GtkImage">
|
||||
37 <property name="resource">/com/github/ToshioCP/list4/grid.png</property>
|
||||
38 </object>
|
||||
39 </child>
|
||||
40 </object>
|
||||
41 </child>
|
||||
42 <child>
|
||||
43 <object class="GtkLabel" id="dmy2">
|
||||
44 <property name="width-chars">10</property>
|
||||
45 </object>
|
||||
46 </child>
|
||||
47 </object>
|
||||
48 </child>
|
||||
49 <child>
|
||||
50 <object class="GtkScrolledWindow" id="scr">
|
||||
51 <property name="hexpand">TRUE</property>
|
||||
52 <property name="vexpand">TRUE</property>
|
||||
53 </object>
|
||||
54 </child>
|
||||
55 </object>
|
||||
56 </child>
|
||||
57 </object>
|
||||
58 <object class="GtkListView" id="list">
|
||||
59 <property name="model">
|
||||
60 <object class="GtkSingleSelection" id="singleselection">
|
||||
61 <property name="model">
|
||||
62 <object class="GtkDirectoryList" id="directorylist">
|
||||
63 <property name="attributes">standard::name,standard::icon,standard::content-type</property>
|
||||
64 </object>
|
||||
65 </property>
|
||||
66 </object>
|
||||
67 </property>
|
||||
68 </object>
|
||||
69 <object class="GtkGridView" id="grid">
|
||||
70 <property name="model">singleselection</property>
|
||||
71 </object>
|
||||
72 </interface>
|
||||
73
|
||||
~~~
|
||||
|
||||
The file consists of two parts.
|
||||
The first part begins at the third line and ends at the 57th line.
|
||||
This part is the widgets from the top level window to the scrolled window.
|
||||
It also includes two buttons.
|
||||
The second part begins at the 58th line and ends at the 71st line.
|
||||
This is GtkListView and GtkGridView.
|
||||
They are described in the previous section.
|
||||
|
||||
- 13-17, 42-46: Two labels are dummy labels.
|
||||
They just work as a space to put the two buttons at the appropriate position.
|
||||
- 19-41: GtkButton `btnlist` and `btngrid`.
|
||||
These two buttons work as selection buttons to switch from list to grid and vice versa.
|
||||
These two buttons are connected to a stateful action `win.view`.
|
||||
This action is stateful and has a parameter.
|
||||
Such action consists of prefix, action name and parameter.
|
||||
The prefix of the action is `win`, which means the action belongs to the top level window.
|
||||
So, a prefix gives scope of the action.
|
||||
The action name is `view`.
|
||||
The parameters are `list` or `grid`, which show the state of the action.
|
||||
A parameter is also called a target, because it is a target to which the buttons are clicked on to change the action state.
|
||||
We often write the detailed action like "win.view::list" or "win.view::grid".
|
||||
- 21-22: The properties "action-name" and "action-target" belong to GtkActionable interface.
|
||||
GtkButton implements GtkActionable.
|
||||
The action name is "win.view" and the target is "list".
|
||||
Generally, a target is GVariant, which can be string, integer, float and so on.
|
||||
You need to use GVariant text format to write GVariant value in ui files.
|
||||
If the type of the GVarinat value is string, then the value with GVariant text format is bounded by single quotes or double quotes.
|
||||
Because ui file is xml format text, single quote cannot be written without escape.
|
||||
Its escape sequence is \'.
|
||||
Therefore, the target 'list' is written as \'list\'.
|
||||
Because the button is connected to the action, "clicked" signal handler isn't needed.
|
||||
- 23-27: The child widget of the button is GtkImage.
|
||||
GtkImage has a "resource" property.
|
||||
It is a GResource and GtkImage reads an image data from the resource and sets the image.
|
||||
This resource is built from 24x24-sized png image data, which is an original icon.
|
||||
- 50-53: GtkScrolledWindow.
|
||||
Its child widget will be GtkListView or GtkGridView.
|
||||
|
||||
The action `view` is created, connected to the "activate" signal handler and inserted to the window (action map) as follows.
|
||||
|
||||
~~~C
|
||||
act_view = g_simple_action_new_stateful ("view", g_variant_type_new("s"), g_variant_new_string ("list"));
|
||||
g_signal_connect (act_view, "activate", G_CALLBACK (view_activated), scr); /* scr is the GtkScrolledWindow object */
|
||||
g_action_map_add_action (G_ACTION_MAP (win), G_ACTION (act_view));
|
||||
~~~
|
||||
|
||||
The signal handler `view_activated` will be explained later.
|
||||
|
||||
## Factories
|
||||
|
||||
Each view (GtkListView and GtkGridView) has its own factory because its items have different structure of widgets.
|
||||
The factories are GtkBuilderListItemFactory objects.
|
||||
Their ui files are as follows.
|
||||
|
||||
factory_list.ui
|
||||
|
||||
~~~xml
|
||||
1 <?xml version="1.0" encoding="UTF-8"?>
|
||||
2 <interface>
|
||||
3 <template class="GtkListItem">
|
||||
4 <property name="child">
|
||||
5 <object class="GtkBox">
|
||||
6 <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
|
||||
7 <property name="spacing">20</property>
|
||||
8 <child>
|
||||
9 <object class="GtkImage">
|
||||
10 <binding name="gicon">
|
||||
11 <closure type="GIcon" function="get_icon">
|
||||
12 <lookup name="item">GtkListItem</lookup>
|
||||
13 </closure>
|
||||
14 </binding>
|
||||
15 </object>
|
||||
16 </child>
|
||||
17 <child>
|
||||
18 <object class="GtkLabel">
|
||||
19 <property name="hexpand">TRUE</property>
|
||||
20 <property name="xalign">0</property>
|
||||
21 <binding name="label">
|
||||
22 <closure type="gchararray" function="get_file_name">
|
||||
23 <lookup name="item">GtkListItem</lookup>
|
||||
24 </closure>
|
||||
25 </binding>
|
||||
26 </object>
|
||||
27 </child>
|
||||
28 </object>
|
||||
29 </property>
|
||||
30 </template>
|
||||
31 </interface>
|
||||
32
|
||||
~~~
|
||||
|
||||
factory_grid.ui
|
||||
|
||||
~~~xml
|
||||
1 <?xml version="1.0" encoding="UTF-8"?>
|
||||
2 <interface>
|
||||
3 <template class="GtkListItem">
|
||||
4 <property name="child">
|
||||
5 <object class="GtkBox">
|
||||
6 <property name="orientation">GTK_ORIENTATION_VERTICAL</property>
|
||||
7 <property name="spacing">20</property>
|
||||
8 <child>
|
||||
9 <object class="GtkImage">
|
||||
10 <property name="icon-size">GTK_ICON_SIZE_LARGE</property>
|
||||
11 <binding name="gicon">
|
||||
12 <closure type="GIcon" function="get_icon">
|
||||
13 <lookup name="item">GtkListItem</lookup>
|
||||
14 </closure>
|
||||
15 </binding>
|
||||
16 </object>
|
||||
17 </child>
|
||||
18 <child>
|
||||
19 <object class="GtkLabel">
|
||||
20 <property name="hexpand">TRUE</property>
|
||||
21 <property name="xalign">0.5</property>
|
||||
22 <binding name="label">
|
||||
23 <closure type="gchararray" function="get_file_name">
|
||||
24 <lookup name="item">GtkListItem</lookup>
|
||||
25 </closure>
|
||||
26 </binding>
|
||||
27 </object>
|
||||
28 </child>
|
||||
29 </object>
|
||||
30 </property>
|
||||
31 </template>
|
||||
32 </interface>
|
||||
33
|
||||
~~~
|
||||
|
||||
The two files above are almost same.
|
||||
The difference is:
|
||||
|
||||
- The orientation of the box
|
||||
- The icon size
|
||||
- The position of the text of the label
|
||||
|
||||
~~~
|
||||
$ cd list4; diff factory_list.ui factory_grid.ui
|
||||
6c6
|
||||
< <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
|
||||
---
|
||||
> <property name="orientation">GTK_ORIENTATION_VERTICAL</property>
|
||||
9a10
|
||||
> <property name="icon-size">GTK_ICON_SIZE_LARGE</property>
|
||||
20c21
|
||||
< <property name="xalign">0</property>
|
||||
---
|
||||
> <property name="xalign">0.5</property>
|
||||
~~~
|
||||
|
||||
Each view item has two properties, "gicon" in GtkImage and "label" in GtkLabel.
|
||||
Because GFileInfo doesn't have properties correspond to icon object or filename, the factory uses closure tag to bind "gicon" and "label" properties to GFileInfo information.
|
||||
A function `get_icon` gets GIcon the GFileInfo object has.
|
||||
And a function `get_file_name` gets a filename the GFileInfo object has.
|
||||
|
||||
~~~C
|
||||
1 GIcon *
|
||||
2 get_icon (GtkListItem *item, GFileInfo *info) {
|
||||
3 GIcon *icon;
|
||||
4
|
||||
5 if (! G_IS_FILE_INFO (info))
|
||||
6 return NULL;
|
||||
7 else {
|
||||
8 icon = g_file_info_get_icon (info);
|
||||
9 g_object_ref (icon);
|
||||
10 return icon;
|
||||
11 }
|
||||
12 }
|
||||
13
|
||||
14 char *
|
||||
15 get_file_name (GtkListItem *item, GFileInfo *info) {
|
||||
16 if (! G_IS_FILE_INFO (info))
|
||||
17 return NULL;
|
||||
18 else
|
||||
19 return g_strdup (g_file_info_get_name (info));
|
||||
20 }
|
||||
~~~
|
||||
|
||||
One important thing is view items own the object or string.
|
||||
It is achieved by `g_object_ref` to increase the reference count by one, or `strdup` to create a copy of the string.
|
||||
The object or string will be automatically freed in unbinding process when the view item is recycled.
|
||||
|
||||
## An activate signal handler of the action
|
||||
|
||||
An activate signal handler `view_activate` switches the view.
|
||||
It does two things.
|
||||
|
||||
- Change the child widget of GtkScrolledWindow.
|
||||
- Change the CSS of buttons to show the current state.
|
||||
|
||||
~~~C
|
||||
1 static void
|
||||
2 view_activated(GSimpleAction *action, GVariant *parameter, gpointer user_data) {
|
||||
3 GtkScrolledWindow *scr = GTK_SCROLLED_WINDOW (user_data);
|
||||
4 const char *view = g_variant_get_string (parameter, NULL);
|
||||
5 const char *other;
|
||||
6 char *css;
|
||||
7
|
||||
8 if (strcmp (view, "list") == 0) {
|
||||
9 other = "grid";
|
||||
10 gtk_scrolled_window_set_child (scr, list);
|
||||
11 }else {
|
||||
12 other = "list";
|
||||
13 gtk_scrolled_window_set_child (scr, grid);
|
||||
14 }
|
||||
15 css = g_strdup_printf ("button#btn%s {background: silver;} button#btn%s {background: white;}", view, other);
|
||||
16 gtk_css_provider_load_from_data (provider, css, -1);
|
||||
17 g_free (css);
|
||||
18 g_action_change_state (G_ACTION (action), parameter);
|
||||
19 }
|
||||
~~~
|
||||
|
||||
The second parameter of this handler is the target of the clicked button.
|
||||
Its type is GVariant.
|
||||
|
||||
- If `btnlist` has been clicked, then `parameter` is "list".
|
||||
- If `btngrid` has been clicked, then `parameter` is "grid".
|
||||
|
||||
The third parameter `user_data` points GtkScrolledWindow, which is set in the `g_signal_connect` function.
|
||||
|
||||
- 4: `g_variant_get_string` gets the string from the GVariant variable.
|
||||
- 8-14: Sets the child of `scr`.
|
||||
- 15-17: Sets the CSS of the buttons.
|
||||
The background of the clicked button will be silver color and the other button will be white.
|
||||
- 18: Changes the state of the action.
|
||||
|
||||
## Activate signal of GtkListView and GtkGridView
|
||||
|
||||
Views (GtkListView and GtkGridView) have an "activate" signal.
|
||||
It is emitted when an item in the view is double clicked or the enter key is pressed.
|
||||
You can do anything you like by connecting the "activate" signal to the handler.
|
||||
|
||||
The example `list4` launches `tfe` text file editor if the item of the list is a text file.
|
||||
|
||||
~~~C
|
||||
static void
|
||||
list_activate (GtkListView *list, int position, gpointer user_data) {
|
||||
GFileInfo *info = G_FILE_INFO (g_list_model_get_item (G_LIST_MODEL (gtk_list_view_get_model (list)), position));
|
||||
launch_tfe_with_file (info);
|
||||
}
|
||||
|
||||
static void
|
||||
grid_activate (GtkGridView *grid, int position, gpointer user_data) {
|
||||
GFileInfo *info = G_FILE_INFO (g_list_model_get_item (G_LIST_MODEL (gtk_grid_view_get_model (grid)), position));
|
||||
launch_tfe_with_file (info);
|
||||
}
|
||||
|
||||
... ...
|
||||
... ...
|
||||
|
||||
g_signal_connect (GTK_LIST_VIEW (list), "activate", G_CALLBACK (list_activate), NULL);
|
||||
g_signal_connect (GTK_GRID_VIEW (grid), "activate", G_CALLBACK (grid_activate), NULL);
|
||||
~~~
|
||||
|
||||
The second parameter of the handlers is the position of the item (GFileInfo) of the GListModel list.
|
||||
So you can get the item with `g_list_model_get_item` function.
|
||||
|
||||
## Content type and launching an application
|
||||
|
||||
The function `launch_tfe_with_file` launches `tfe` with the file whose information has been taken by GFileInfo object, if the file is a text file.
|
||||
|
||||
GFileInfo has information about file type.
|
||||
The file type is like "text/plain", "text/x-csrc" and so on.
|
||||
It is called content type.
|
||||
Content type can be got with `g_file_info_get_content_type` function.
|
||||
|
||||
~~~C
|
||||
1 static void
|
||||
2 launch_tfe_with_file (GFileInfo *info) {
|
||||
3 GError *err = NULL;
|
||||
4 GFile *file;
|
||||
5 GList *files = NULL;
|
||||
6 const char *content_type;
|
||||
7 const char *text_type = "text/";
|
||||
8 GAppInfo *appinfo;
|
||||
9 int i;
|
||||
10
|
||||
11 if (! info)
|
||||
12 return;
|
||||
13 content_type = g_file_info_get_content_type (info);
|
||||
14 g_print ("%s\n", content_type); /* This line can be commented out if unnecessary */
|
||||
15 if (! content_type)
|
||||
16 return;
|
||||
17 for (i=0;i<5;++i) {
|
||||
18 if (content_type[i] != text_type[i])
|
||||
19 return;
|
||||
20 }
|
||||
21 appinfo = g_app_info_create_from_commandline ("tfe", "tfe", G_APP_INFO_CREATE_NONE, &err);
|
||||
22 if (err) {
|
||||
23 g_printerr ("%s\n", err->message);
|
||||
24 return;
|
||||
25 }
|
||||
26 err = NULL;
|
||||
27 file = g_file_new_for_path (g_file_info_get_name (info));
|
||||
28 files = g_list_append (files, file);
|
||||
29 if (! (g_app_info_launch (appinfo, files, NULL, &err))) {
|
||||
30 g_printerr ("%s\n", err->message);
|
||||
31 }
|
||||
32 g_list_free_full (files, g_object_unref);
|
||||
33 g_object_unref (appinfo);
|
||||
34 }
|
||||
~~~
|
||||
|
||||
- 13: Gets the content type of GFileInfo.
|
||||
- 14: Prints the content type.
|
||||
This is only useful to know a content type of a file.
|
||||
- 17-20: If the content type doesn't begin with "text/", then it returns.
|
||||
- 21: Creates GAppInfo object of `tfe` application.
|
||||
GAppInfo is interface and the variable `appinfo` points a GDesktopAppInfo object.
|
||||
GAppInfo is a collection of information of an application.
|
||||
- 29: Launches the application (`tfe`) with an argument `file`.
|
||||
`g_app_info_launch` has four parameters.
|
||||
The first parameter is GAppInfo object.
|
||||
The second parameter is a list of GFile objects.
|
||||
In this function, only one GFile object is given to `tfe`, but you can give more arguments.
|
||||
The third parameter is GAppLaunchContext, but this program gives NULL instead.
|
||||
The last parameter is the pointer to the pointer to GError.
|
||||
- 32: `g_list_free_full` frees the memories used by the list and items.
|
||||
|
||||
At present, my ubuntu runs on Gtk3, not Gtk4.
|
||||
If it runs on Gtk4, using `g_app_info_launch_default_for_uri` is convenient.
|
||||
The function automatically determines the default application from the file and launches it.
|
||||
For example, if the file is text, then it launches gedit with the file.
|
||||
Such functionality comes from desktop.
|
||||
|
||||
## Compilation and execution
|
||||
|
||||
The source files are located in [src/list4](../src/list4) directory.
|
||||
To compile and execute list4, type as follows.
|
||||
|
||||
~~~
|
||||
$ cd list4 # or cd src/list4. It depends your current directory.
|
||||
$ meson _build
|
||||
$ ninja -C _build
|
||||
$ _build/list3
|
||||
~~~
|
||||
|
||||
Then a file list appears as a list style.
|
||||
Click on a button on the tool bar so that you can change the style to grid or back to list.
|
||||
Double click "list4.c" item, then `tfe` text editor runs with the argument "list4.c".
|
||||
The following is the screenshot.
|
||||
|
||||
![Screenshot](../image/screenshot_list4.png)
|
||||
|
||||
## "gbytes" property of GtkBuilderListItemFactory
|
||||
|
||||
GtkBuilderListItemFactory has "gbytes" property.
|
||||
The property contains a byte sequence of ui data.
|
||||
If you use this property, you can put the contents of `factory_list.ui` and `factory_grid.ui`into `list4.ui`.
|
||||
The following shows a part of the new list4.ui file.
|
||||
|
||||
~~~xml
|
||||
<object class="GtkListView" id="list">
|
||||
<property name="model">
|
||||
<object class="GtkSingleSelection" id="singleselection">
|
||||
<property name="model">
|
||||
<object class="GtkDirectoryList" id="directorylist">
|
||||
<property name="attributes">standard::name,standard::icon,standard::content-type</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</property>
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="bytes"><![CDATA[
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<property name="child">
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
|
||||
<property name="spacing">20</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<binding name="gicon">
|
||||
<closure type="GIcon" function="get_icon">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</closure>
|
||||
</binding>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="hexpand">TRUE</property>
|
||||
<property name="xalign">0</property>
|
||||
<binding name="label">
|
||||
<closure type="gchararray" function="get_file_name">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</closure>
|
||||
</binding>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
]]></property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
~~~
|
||||
|
||||
CDATA section begins with "<![CDATA[" and ends with "]]>".
|
||||
The contents of CDATA section is recognized as a string.
|
||||
Any character, even if it is a key syntax marker such as '<' or '>', is recognized literally.
|
||||
Therefore, the text between "<![CDATA[" and "]]>" is inserted to "bytes" property as it is.
|
||||
|
||||
This method decreases the number of ui files.
|
||||
But, the new ui file is a bit complicated especially for the beginners.
|
||||
If you feel some difficulty, it is better for you to separate the ui file.
|
||||
|
||||
A directory [src/list5](../src/list5) includes the ui file above.
|
||||
|
||||
|
||||
Up: [Readme.md](../Readme.md), Prev: [Section 24](sec24.md)
|
BIN
image/directorylist.png
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
image/directorylist.xcf
Normal file
BIN
image/list4.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
image/screenshot_list4.png
Normal file
After Width: | Height: | Size: 173 KiB |
33
src/list4/factory_grid.ui
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<property name="child">
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">GTK_ORIENTATION_VERTICAL</property>
|
||||
<property name="spacing">20</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="icon-size">GTK_ICON_SIZE_LARGE</property>
|
||||
<binding name="gicon">
|
||||
<closure type="GIcon" function="get_icon">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</closure>
|
||||
</binding>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="hexpand">TRUE</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<binding name="label">
|
||||
<closure type="gchararray" function="get_file_name">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</closure>
|
||||
</binding>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
|
32
src/list4/factory_list.ui
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<property name="child">
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
|
||||
<property name="spacing">20</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<binding name="gicon">
|
||||
<closure type="GIcon" function="get_icon">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</closure>
|
||||
</binding>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="hexpand">TRUE</property>
|
||||
<property name="xalign">0</property>
|
||||
<binding name="label">
|
||||
<closure type="gchararray" function="get_file_name">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</closure>
|
||||
</binding>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
|
BIN
src/list4/grid.png
Normal file
After Width: | Height: | Size: 628 B |
BIN
src/list4/list.png
Normal file
After Width: | Height: | Size: 611 B |
171
src/list4/list4.c
Normal file
|
@ -0,0 +1,171 @@
|
|||
#include <gtk/gtk.h>
|
||||
#include <string.h>
|
||||
|
||||
static GtkWidget *list;
|
||||
static GtkWidget *grid;
|
||||
static GdkDisplay *display;
|
||||
static GtkCssProvider *provider;
|
||||
|
||||
static gboolean
|
||||
before_close (GtkWindow *win, gpointer user_data) {
|
||||
g_object_unref (list);
|
||||
g_object_unref (grid);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
view_activated(GSimpleAction *action, GVariant *parameter, gpointer user_data) {
|
||||
GtkScrolledWindow *scr = GTK_SCROLLED_WINDOW (user_data);
|
||||
const char *view = g_variant_get_string (parameter, NULL);
|
||||
const char *other;
|
||||
char *css;
|
||||
|
||||
if (strcmp (view, "list") == 0) {
|
||||
other = "grid";
|
||||
gtk_scrolled_window_set_child (scr, list);
|
||||
}else {
|
||||
other = "list";
|
||||
gtk_scrolled_window_set_child (scr, grid);
|
||||
}
|
||||
css = g_strdup_printf ("button#btn%s {background: silver;} button#btn%s {background: white;}", view, other);
|
||||
gtk_css_provider_load_from_data (provider, css, -1);
|
||||
g_free (css);
|
||||
g_action_change_state (G_ACTION (action), parameter);
|
||||
}
|
||||
|
||||
static void
|
||||
launch_tfe_with_file (GFileInfo *info) {
|
||||
GError *err = NULL;
|
||||
GFile *file;
|
||||
GList *files = NULL;
|
||||
const char *content_type;
|
||||
const char *text_type = "text/";
|
||||
GAppInfo *appinfo;
|
||||
int i;
|
||||
|
||||
if (! info)
|
||||
return;
|
||||
content_type = g_file_info_get_content_type (info);
|
||||
g_print ("%s\n", content_type); /* This line can be commented out if unnecessary */
|
||||
if (! content_type)
|
||||
return;
|
||||
for (i=0;i<5;++i) {
|
||||
if (content_type[i] != text_type[i])
|
||||
return;
|
||||
}
|
||||
appinfo = g_app_info_create_from_commandline ("tfe", "tfe", G_APP_INFO_CREATE_NONE, &err);
|
||||
if (err) {
|
||||
g_printerr ("%s\n", err->message);
|
||||
return;
|
||||
}
|
||||
err = NULL;
|
||||
file = g_file_new_for_path (g_file_info_get_name (info));
|
||||
files = g_list_append (files, file);
|
||||
if (! (g_app_info_launch (appinfo, files, NULL, &err))) {
|
||||
g_printerr ("%s\n", err->message);
|
||||
}
|
||||
g_list_free_full (files, g_object_unref);
|
||||
g_object_unref (appinfo);
|
||||
}
|
||||
|
||||
static void
|
||||
list_activate (GtkListView *list, int position, gpointer user_data) {
|
||||
GFileInfo *info = G_FILE_INFO (g_list_model_get_item (G_LIST_MODEL (gtk_list_view_get_model (list)), position));
|
||||
launch_tfe_with_file (info);
|
||||
}
|
||||
|
||||
static void
|
||||
grid_activate (GtkGridView *grid, int position, gpointer user_data) {
|
||||
GFileInfo *info = G_FILE_INFO (g_list_model_get_item (G_LIST_MODEL (gtk_grid_view_get_model (grid)), position));
|
||||
launch_tfe_with_file (info);
|
||||
}
|
||||
|
||||
GIcon *
|
||||
get_icon (GtkListItem *item, GFileInfo *info) {
|
||||
GIcon *icon;
|
||||
|
||||
if (! G_IS_FILE_INFO (info))
|
||||
return NULL;
|
||||
else {
|
||||
icon = g_file_info_get_icon (info);
|
||||
g_object_ref (icon);
|
||||
return icon;
|
||||
}
|
||||
}
|
||||
|
||||
char *
|
||||
get_file_name (GtkListItem *item, GFileInfo *info) {
|
||||
if (! G_IS_FILE_INFO (info))
|
||||
return NULL;
|
||||
else
|
||||
return g_strdup (g_file_info_get_name (info));
|
||||
}
|
||||
|
||||
/* ----- activate, open, startup handlers ----- */
|
||||
static void
|
||||
app_activate (GApplication *application) {
|
||||
GtkApplication *app = GTK_APPLICATION (application);
|
||||
GFile *file;
|
||||
GSimpleAction *act_view;
|
||||
|
||||
GtkBuilder *build = gtk_builder_new_from_resource ("/com/github/ToshioCP/list4/list4.ui");
|
||||
GtkWidget *win = GTK_WIDGET (gtk_builder_get_object (build, "win"));
|
||||
GtkWidget *scr = GTK_WIDGET (gtk_builder_get_object (build, "scr"));
|
||||
list = GTK_WIDGET (gtk_builder_get_object (build, "list"));
|
||||
grid = GTK_WIDGET (gtk_builder_get_object (build, "grid"));
|
||||
GtkDirectoryList *directorylist = GTK_DIRECTORY_LIST (gtk_builder_get_object (build, "directorylist"));
|
||||
g_object_ref (list);
|
||||
g_object_ref (grid);
|
||||
g_object_unref (build);
|
||||
|
||||
GtkListItemFactory *factory_list = gtk_builder_list_item_factory_new_from_resource (NULL, "/com/github/ToshioCP/list4/factory_list.ui");
|
||||
GtkListItemFactory *factory_grid = gtk_builder_list_item_factory_new_from_resource (NULL, "/com/github/ToshioCP/list4/factory_grid.ui");
|
||||
|
||||
gtk_window_set_application (GTK_WINDOW (win), app);
|
||||
/* First, 'list' is a child of scr. The child will be list or grid according to the clicked button, btnlist or btngrid. */
|
||||
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), list);
|
||||
|
||||
file = g_file_new_for_path (".");
|
||||
gtk_directory_list_set_file (directorylist, file);
|
||||
g_object_unref (file);
|
||||
|
||||
gtk_list_view_set_factory (GTK_LIST_VIEW (list), factory_list);
|
||||
gtk_grid_view_set_factory (GTK_GRID_VIEW (grid), factory_grid);
|
||||
g_signal_connect (GTK_LIST_VIEW (list), "activate", G_CALLBACK (list_activate), NULL);
|
||||
g_signal_connect (GTK_GRID_VIEW (grid), "activate", G_CALLBACK (grid_activate), NULL);
|
||||
|
||||
act_view = g_simple_action_new_stateful ("view", g_variant_type_new("s"), g_variant_new_string ("list"));
|
||||
g_signal_connect (act_view, "activate", G_CALLBACK (view_activated), scr);
|
||||
g_action_map_add_action (G_ACTION_MAP (win), G_ACTION (act_view));
|
||||
|
||||
display = gtk_widget_get_display (GTK_WIDGET (win));
|
||||
provider = gtk_css_provider_new ();
|
||||
gtk_css_provider_load_from_data (provider, "button#btnlist {background: silver;} button#btngrid {background: white;}", -1);
|
||||
gtk_style_context_add_provider_for_display (display, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_USER);
|
||||
|
||||
g_signal_connect (GTK_WINDOW (win), "close-request", G_CALLBACK (before_close), NULL);
|
||||
gtk_widget_show (win);
|
||||
}
|
||||
|
||||
static void
|
||||
app_startup (GApplication *application) {
|
||||
}
|
||||
|
||||
#define APPLICATION_ID "com.github.ToshioCP.list4"
|
||||
|
||||
int
|
||||
main (int argc, char **argv) {
|
||||
GtkApplication *app;
|
||||
int stat;
|
||||
|
||||
app = gtk_application_new (APPLICATION_ID, G_APPLICATION_FLAGS_NONE);
|
||||
|
||||
g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL);
|
||||
g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
|
||||
/* g_signal_connect (app, "open", G_CALLBACK (app_open), NULL);*/
|
||||
|
||||
stat =g_application_run (G_APPLICATION (app), argc, argv);
|
||||
g_object_unref (app);
|
||||
return stat;
|
||||
}
|
||||
|
10
src/list4/list4.gresource.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gresources>
|
||||
<gresource prefix="/com/github/ToshioCP/list4">
|
||||
<file>list4.ui</file>
|
||||
<file>factory_list.ui</file>
|
||||
<file>factory_grid.ui</file>
|
||||
<file>list.png</file>
|
||||
<file>grid.png</file>
|
||||
</gresource>
|
||||
</gresources>
|
73
src/list4/list4.ui
Normal file
|
@ -0,0 +1,73 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<object class="GtkApplicationWindow" id="win">
|
||||
<property name="title">file list</property>
|
||||
<property name="default-width">600</property>
|
||||
<property name="default-height">400</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="boxv">
|
||||
<property name="orientation">GTK_ORIENTATION_VERTICAL</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="boxh">
|
||||
<property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="dmy1">
|
||||
<property name="hexpand">TRUE</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="btnlist">
|
||||
<property name="name">btnlist</property>
|
||||
<property name="action-name">win.view</property>
|
||||
<property name="action-target">'list'</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="resource">/com/github/ToshioCP/list4/list.png</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="btngrid">
|
||||
<property name="name">btngrid</property>
|
||||
<property name="action-name">win.view</property>
|
||||
<property name="action-target">'grid'</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="resource">/com/github/ToshioCP/list4/grid.png</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="dmy2">
|
||||
<property name="width-chars">10</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="scr">
|
||||
<property name="hexpand">TRUE</property>
|
||||
<property name="vexpand">TRUE</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkListView" id="list">
|
||||
<property name="model">
|
||||
<object class="GtkSingleSelection" id="singleselection">
|
||||
<property name="model">
|
||||
<object class="GtkDirectoryList" id="directorylist">
|
||||
<property name="attributes">standard::name,standard::icon,standard::content-type</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
<object class="GtkGridView" id="grid">
|
||||
<property name="model">singleselection</property>
|
||||
</object>
|
||||
</interface>
|
||||
|
11
src/list4/meson.build
Normal file
|
@ -0,0 +1,11 @@
|
|||
project('list4', 'c')
|
||||
|
||||
gtkdep = dependency('gtk4')
|
||||
|
||||
gnome=import('gnome')
|
||||
resources = gnome.compile_resources('resources','list4.gresource.xml')
|
||||
|
||||
sourcefiles=files('list4.c')
|
||||
|
||||
executable('list4', sourcefiles, resources, dependencies: gtkdep, export_dynamic: true, install: false)
|
||||
|
BIN
src/list5/grid.png
Normal file
After Width: | Height: | Size: 628 B |
BIN
src/list5/list.png
Normal file
After Width: | Height: | Size: 611 B |
166
src/list5/list5.c
Normal file
|
@ -0,0 +1,166 @@
|
|||
#include <gtk/gtk.h>
|
||||
#include <string.h>
|
||||
|
||||
static GtkWidget *list;
|
||||
static GtkWidget *grid;
|
||||
static GdkDisplay *display;
|
||||
static GtkCssProvider *provider;
|
||||
|
||||
static gboolean
|
||||
before_close (GtkWindow *win, gpointer user_data) {
|
||||
g_object_unref (list);
|
||||
g_object_unref (grid);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
view_activated(GSimpleAction *action, GVariant *parameter, gpointer user_data) {
|
||||
GtkScrolledWindow *scr = GTK_SCROLLED_WINDOW (user_data);
|
||||
const char *view = g_variant_get_string (parameter, NULL);
|
||||
const char *other;
|
||||
char *css;
|
||||
|
||||
if (strcmp (view, "list") == 0) {
|
||||
other = "grid";
|
||||
gtk_scrolled_window_set_child (scr, list);
|
||||
}else {
|
||||
other = "list";
|
||||
gtk_scrolled_window_set_child (scr, grid);
|
||||
}
|
||||
css = g_strdup_printf ("button#btn%s {background: silver;} button#btn%s {background: white;}", view, other);
|
||||
gtk_css_provider_load_from_data (provider, css, -1);
|
||||
g_free (css);
|
||||
g_action_change_state (G_ACTION (action), parameter);
|
||||
}
|
||||
|
||||
static void
|
||||
launch_tfe_with_file (GFileInfo *info) {
|
||||
GError *err = NULL;
|
||||
GFile *file;
|
||||
GList *files = NULL;
|
||||
const char *content_type;
|
||||
const char *text_type = "text/";
|
||||
GAppInfo *appinfo;
|
||||
int i;
|
||||
|
||||
if (! info)
|
||||
return;
|
||||
content_type = g_file_info_get_content_type (info);
|
||||
g_print ("%s\n", content_type); /* This line can be commented out if unnecessary */
|
||||
if (! content_type)
|
||||
return;
|
||||
for (i=0;i<5;++i) {
|
||||
if (content_type[i] != text_type[i])
|
||||
return;
|
||||
}
|
||||
appinfo = g_app_info_create_from_commandline ("tfe", "tfe", G_APP_INFO_CREATE_NONE, &err);
|
||||
if (err) {
|
||||
g_printerr ("%s\n", err->message);
|
||||
return;
|
||||
}
|
||||
err = NULL;
|
||||
file = g_file_new_for_path (g_file_info_get_name (info));
|
||||
files = g_list_append (files, file);
|
||||
if (! (g_app_info_launch (appinfo, files, NULL, &err))) {
|
||||
g_printerr ("%s\n", err->message);
|
||||
}
|
||||
g_list_free_full (files, g_object_unref);
|
||||
g_object_unref (appinfo);
|
||||
}
|
||||
|
||||
static void
|
||||
list_activate (GtkListView *list, int position, gpointer user_data) {
|
||||
GFileInfo *info = G_FILE_INFO (g_list_model_get_item (G_LIST_MODEL (gtk_list_view_get_model (list)), position));
|
||||
launch_tfe_with_file (info);
|
||||
}
|
||||
|
||||
static void
|
||||
grid_activate (GtkGridView *grid, int position, gpointer user_data) {
|
||||
GFileInfo *info = G_FILE_INFO (g_list_model_get_item (G_LIST_MODEL (gtk_grid_view_get_model (grid)), position));
|
||||
launch_tfe_with_file (info);
|
||||
}
|
||||
|
||||
GIcon *
|
||||
get_icon (GtkListItem *item, GFileInfo *info) {
|
||||
GIcon *icon;
|
||||
|
||||
if (! G_IS_FILE_INFO (info))
|
||||
return NULL;
|
||||
else {
|
||||
icon = g_file_info_get_icon (info);
|
||||
g_object_ref (icon);
|
||||
return icon;
|
||||
}
|
||||
}
|
||||
|
||||
char *
|
||||
get_file_name (GtkListItem *item, GFileInfo *info) {
|
||||
if (! G_IS_FILE_INFO (info))
|
||||
return NULL;
|
||||
else
|
||||
return g_strdup (g_file_info_get_name (info));
|
||||
}
|
||||
|
||||
/* ----- activate, open, startup handlers ----- */
|
||||
static void
|
||||
app_activate (GApplication *application) {
|
||||
GtkApplication *app = GTK_APPLICATION (application);
|
||||
GFile *file;
|
||||
GSimpleAction *act_view;
|
||||
|
||||
GtkBuilder *build = gtk_builder_new_from_resource ("/com/github/ToshioCP/list5/list5.ui");
|
||||
GtkWidget *win = GTK_WIDGET (gtk_builder_get_object (build, "win"));
|
||||
GtkWidget *scr = GTK_WIDGET (gtk_builder_get_object (build, "scr"));
|
||||
list = GTK_WIDGET (gtk_builder_get_object (build, "list"));
|
||||
grid = GTK_WIDGET (gtk_builder_get_object (build, "grid"));
|
||||
GtkDirectoryList *directorylist = GTK_DIRECTORY_LIST (gtk_builder_get_object (build, "directorylist"));
|
||||
g_object_ref (list);
|
||||
g_object_ref (grid);
|
||||
g_object_unref (build);
|
||||
|
||||
gtk_window_set_application (GTK_WINDOW (win), app);
|
||||
/* First, 'list' is a child of scr. The child will be list or grid according to the clicked button, btnlist or btngrid. */
|
||||
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), list);
|
||||
|
||||
file = g_file_new_for_path (".");
|
||||
gtk_directory_list_set_file (directorylist, file);
|
||||
g_object_unref (file);
|
||||
|
||||
g_signal_connect (GTK_LIST_VIEW (list), "activate", G_CALLBACK (list_activate), NULL);
|
||||
g_signal_connect (GTK_GRID_VIEW (grid), "activate", G_CALLBACK (grid_activate), NULL);
|
||||
|
||||
act_view = g_simple_action_new_stateful ("view", g_variant_type_new("s"), g_variant_new_string ("list"));
|
||||
g_signal_connect (act_view, "activate", G_CALLBACK (view_activated), scr);
|
||||
g_action_map_add_action (G_ACTION_MAP (win), G_ACTION (act_view));
|
||||
|
||||
display = gtk_widget_get_display (GTK_WIDGET (win));
|
||||
provider = gtk_css_provider_new ();
|
||||
gtk_css_provider_load_from_data (provider, "button#btnlist {background: silver;} button#btngrid {background: white;}", -1);
|
||||
gtk_style_context_add_provider_for_display (display, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_USER);
|
||||
|
||||
g_signal_connect (GTK_WINDOW (win), "close-request", G_CALLBACK (before_close), NULL);
|
||||
gtk_widget_show (win);
|
||||
}
|
||||
|
||||
static void
|
||||
app_startup (GApplication *application) {
|
||||
}
|
||||
|
||||
#define APPLICATION_ID "com.github.ToshioCP.list5"
|
||||
|
||||
int
|
||||
main (int argc, char **argv) {
|
||||
GtkApplication *app;
|
||||
int stat;
|
||||
|
||||
app = gtk_application_new (APPLICATION_ID, G_APPLICATION_FLAGS_NONE);
|
||||
|
||||
g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL);
|
||||
g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
|
||||
/* g_signal_connect (app, "open", G_CALLBACK (app_open), NULL);*/
|
||||
|
||||
stat =g_application_run (G_APPLICATION (app), argc, argv);
|
||||
g_object_unref (app);
|
||||
return stat;
|
||||
}
|
||||
|
8
src/list5/list5.gresource.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gresources>
|
||||
<gresource prefix="/com/github/ToshioCP/list5">
|
||||
<file>list5.ui</file>
|
||||
<file>list.png</file>
|
||||
<file>grid.png</file>
|
||||
</gresource>
|
||||
</gresources>
|
148
src/list5/list5.ui
Normal file
|
@ -0,0 +1,148 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<object class="GtkApplicationWindow" id="win">
|
||||
<property name="title">file list</property>
|
||||
<property name="default-width">600</property>
|
||||
<property name="default-height">400</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="boxv">
|
||||
<property name="orientation">GTK_ORIENTATION_VERTICAL</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="boxh">
|
||||
<property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="dmy1">
|
||||
<property name="hexpand">TRUE</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="btnlist">
|
||||
<property name="name">btnlist</property>
|
||||
<property name="action-name">win.view</property>
|
||||
<property name="action-target">'list'</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="resource">/com/github/ToshioCP/list4/list.png</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="btngrid">
|
||||
<property name="name">btngrid</property>
|
||||
<property name="action-name">win.view</property>
|
||||
<property name="action-target">'grid'</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="resource">/com/github/ToshioCP/list4/grid.png</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="dmy2">
|
||||
<property name="width-chars">10</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="scr">
|
||||
<property name="hexpand">TRUE</property>
|
||||
<property name="vexpand">TRUE</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkListView" id="list">
|
||||
<property name="model">
|
||||
<object class="GtkSingleSelection" id="singleselection">
|
||||
<property name="model">
|
||||
<object class="GtkDirectoryList" id="directorylist">
|
||||
<property name="attributes">standard::name,standard::icon,standard::content-type</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</property>
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="bytes"><![CDATA[
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<property name="child">
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
|
||||
<property name="spacing">20</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<binding name="gicon">
|
||||
<closure type="GIcon" function="get_icon">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</closure>
|
||||
</binding>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="hexpand">TRUE</property>
|
||||
<property name="xalign">0</property>
|
||||
<binding name="label">
|
||||
<closure type="gchararray" function="get_file_name">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</closure>
|
||||
</binding>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
]]></property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
<object class="GtkGridView" id="grid">
|
||||
<property name="model">singleselection</property>
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="bytes"><![CDATA[
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<property name="child">
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">GTK_ORIENTATION_VERTICAL</property>
|
||||
<property name="spacing">20</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="icon-size">GTK_ICON_SIZE_LARGE</property>
|
||||
<binding name="gicon">
|
||||
<closure type="GIcon" function="get_icon">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</closure>
|
||||
</binding>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="hexpand">TRUE</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<binding name="label">
|
||||
<closure type="gchararray" function="get_file_name">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</closure>
|
||||
</binding>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
]]></property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</interface>
|
||||
|
11
src/list5/meson.build
Normal file
|
@ -0,0 +1,11 @@
|
|||
project('list5', 'c')
|
||||
|
||||
gtkdep = dependency('gtk4')
|
||||
|
||||
gnome=import('gnome')
|
||||
resources = gnome.compile_resources('resources','list5.gresource.xml')
|
||||
|
||||
sourcefiles=files('list5.c')
|
||||
|
||||
executable('list5', sourcefiles, resources, dependencies: gtkdep, export_dynamic: true, install: false)
|
||||
|
|
@ -30,7 +30,7 @@ teardown_cb (GtkListItemFactory *factory, GtkListItem *listitem, gpointer user_d
|
|||
|
||||
/* ----- activate, open, startup handlers ----- */
|
||||
static void
|
||||
tfe_activate (GApplication *application) {
|
||||
app_activate (GApplication *application) {
|
||||
GtkApplication *app = GTK_APPLICATION (application);
|
||||
GtkWidget *win = gtk_application_window_new (app);
|
||||
gtk_window_set_default_size (GTK_WINDOW (win), 600, 400);
|
||||
|
@ -55,7 +55,7 @@ tfe_activate (GApplication *application) {
|
|||
}
|
||||
|
||||
static void
|
||||
tfe_startup (GApplication *application) {
|
||||
app_startup (GApplication *application) {
|
||||
}
|
||||
|
||||
/* ----- main ----- */
|
||||
|
@ -66,8 +66,8 @@ main (int argc, char **argv) {
|
|||
|
||||
app = gtk_application_new ("com.github.ToshioCP.list1", G_APPLICATION_FLAGS_NONE);
|
||||
|
||||
g_signal_connect (app, "startup", G_CALLBACK (tfe_startup), NULL);
|
||||
g_signal_connect (app, "activate", G_CALLBACK (tfe_activate), NULL);
|
||||
g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL);
|
||||
g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
|
||||
|
||||
stat =g_application_run (G_APPLICATION (app), argc, argv);
|
||||
g_object_unref (app);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
/* ----- activate, open, startup handlers ----- */
|
||||
static void
|
||||
tfe_activate (GApplication *application) {
|
||||
app_activate (GApplication *application) {
|
||||
GtkApplication *app = GTK_APPLICATION (application);
|
||||
GtkWidget *win = gtk_application_window_new (app);
|
||||
gtk_window_set_default_size (GTK_WINDOW (win), 600, 400);
|
||||
|
@ -39,7 +39,7 @@ tfe_activate (GApplication *application) {
|
|||
}
|
||||
|
||||
static void
|
||||
tfe_startup (GApplication *application) {
|
||||
app_startup (GApplication *application) {
|
||||
}
|
||||
|
||||
/* ----- main ----- */
|
||||
|
@ -50,8 +50,8 @@ main (int argc, char **argv) {
|
|||
|
||||
app = gtk_application_new ("com.github.ToshioCP.list2", G_APPLICATION_FLAGS_NONE);
|
||||
|
||||
g_signal_connect (app, "startup", G_CALLBACK (tfe_startup), NULL);
|
||||
g_signal_connect (app, "activate", G_CALLBACK (tfe_activate), NULL);
|
||||
g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL);
|
||||
g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
|
||||
|
||||
stat =g_application_run (G_APPLICATION (app), argc, argv);
|
||||
g_object_unref (app);
|
||||
|
|
|
@ -10,7 +10,7 @@ get_file_name (GtkListItem *item, GFileInfo *info) {
|
|||
|
||||
/* ----- activate, open, startup handlers ----- */
|
||||
static void
|
||||
tfe_activate (GApplication *application) {
|
||||
app_activate (GApplication *application) {
|
||||
GtkApplication *app = GTK_APPLICATION (application);
|
||||
GtkWidget *win = gtk_application_window_new (app);
|
||||
gtk_window_set_default_size (GTK_WINDOW (win), 600, 400);
|
||||
|
@ -41,13 +41,12 @@ tfe_activate (GApplication *application) {
|
|||
GtkListItemFactory *factory = gtk_builder_list_item_factory_new_from_bytes (NULL, gbytes);
|
||||
|
||||
GtkWidget *lv = gtk_list_view_new (GTK_SELECTION_MODEL (ns), factory);
|
||||
gtk_list_view_set_enable_rubberband (GTK_LIST_VIEW (lv), TRUE);
|
||||
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), lv);
|
||||
gtk_widget_show (win);
|
||||
}
|
||||
|
||||
static void
|
||||
tfe_startup (GApplication *application) {
|
||||
app_startup (GApplication *application) {
|
||||
}
|
||||
|
||||
/* ----- main ----- */
|
||||
|
@ -56,10 +55,10 @@ main (int argc, char **argv) {
|
|||
GtkApplication *app;
|
||||
int stat;
|
||||
|
||||
app = gtk_application_new ("com.github.ToshioCP.list2", G_APPLICATION_FLAGS_NONE);
|
||||
app = gtk_application_new ("com.github.ToshioCP.list3", G_APPLICATION_FLAGS_NONE);
|
||||
|
||||
g_signal_connect (app, "startup", G_CALLBACK (tfe_startup), NULL);
|
||||
g_signal_connect (app, "activate", G_CALLBACK (tfe_activate), NULL);
|
||||
g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL);
|
||||
g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
|
||||
|
||||
stat =g_application_run (G_APPLICATION (app), argc, argv);
|
||||
g_object_unref (app);
|
||||
|
|
|
@ -303,5 +303,5 @@ $ comp list3
|
|||
$ ./a.out
|
||||
~~~
|
||||
|
||||
![screenshot list3](../image/list3.png){width=6.04cm height=4.41cm}
|
||||
![screenshot list3](../image/list3.png){width=10cm height=7.3cm}
|
||||
|
||||
|
|
345
src/sec25.src.md
Normal file
|
@ -0,0 +1,345 @@
|
|||
# GtkGridView and activate signal
|
||||
|
||||
GtkGridView is similar to GtkListView.
|
||||
It displays a GListModel as a grid, which is like a square tessellation.
|
||||
|
||||
![Grid](../image/list4.png){width=10cm height=6.6cm}
|
||||
|
||||
This is often seen when you use a file browser like nautilus.
|
||||
|
||||
In this section, let's make a very simple file browser `list4`.
|
||||
It just shows the files in the current directory.
|
||||
And a user can choose list or grid by clicking on buttons in the tool bar.
|
||||
Each item in the list or grid has an icon and a filename.
|
||||
In addition, `list4` provides the way to open the `tfe` text editor to show a text file.
|
||||
A user can do that by double clicking on an item or pressing enter key when an item is selected.
|
||||
|
||||
## GtkDirectoryList
|
||||
|
||||
GtkDirectoryList implements GListModel and it contains information of files in a certain directory.
|
||||
The items of the list are GFileInfo objects.
|
||||
|
||||
In the `list4` source files, GtkDirectoryList is described in a ui file and built by GtkBuilder.
|
||||
It is referenced to by a GtkSingleSelection model and the GtkSingleSelection model is referenced to by a GListView or GGridView object.
|
||||
|
||||
~~~
|
||||
GtkListView (model property) => GtkSingleSelection (model property) => GtkDirectoryList
|
||||
GtkGridView (model property) => GtkSingleSelection (model property) => GtkDirectoryList
|
||||
~~~
|
||||
|
||||
![DirectoryList](../image/directorylist.png){width=10cm height=7.5cm}
|
||||
|
||||
The part of the ui file `list4.ui` is as follows.
|
||||
|
||||
~~~xml
|
||||
<object class="GtkListView" id="list">
|
||||
<property name="model">
|
||||
<object class="GtkSingleSelection" id="singleselection">
|
||||
<property name="model">
|
||||
<object class="GtkDirectoryList" id="directorylist">
|
||||
<property name="attributes">standard::name,standard::icon,standard::content-type</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
<object class="GtkGridView" id="grid">
|
||||
<property name="model">singleselection</property>
|
||||
</object>
|
||||
~~~
|
||||
|
||||
GtkDirectoryList has an "attributes" property.
|
||||
It is attributes of GFileInfo such as "standard::name", "standard::icon" and "standard::content-type".
|
||||
|
||||
- standard::name is a filename.
|
||||
- standard::icon is an icon of the file. It is a GIcon object.
|
||||
- standard::content-type is a content-type.
|
||||
Content-type is the same as mime type for the internet technology.
|
||||
For example, "text/plain" is a text file, "text/x-csrc" is a C source code and so on.
|
||||
("text/x-csrc"is not registered to IANA media types.
|
||||
Such "x-" subtype is not a standard mime type.)
|
||||
Content type is also used by the desktop system.
|
||||
|
||||
GtkGridView has the same structure as GtkListView.
|
||||
But it is enough to specify its model property to `singleselection` which is the identification of the GtkSingleSelection.
|
||||
Therefore the description for GtkGridView is very short.
|
||||
|
||||
## Ui file of the window
|
||||
|
||||
Look at the screenshot of `list4` at the top of this section.
|
||||
The widgets are built with the following ui file.
|
||||
|
||||
@@@include
|
||||
list4/list4.ui
|
||||
@@@
|
||||
|
||||
The file consists of two parts.
|
||||
The first part begins at the third line and ends at the 57th line.
|
||||
This part is the widgets from the top level window to the scrolled window.
|
||||
It also includes two buttons.
|
||||
The second part begins at the 58th line and ends at the 71st line.
|
||||
This is GtkListView and GtkGridView.
|
||||
They are described in the previous section.
|
||||
|
||||
- 13-17, 42-46: Two labels are dummy labels.
|
||||
They just work as a space to put the two buttons at the appropriate position.
|
||||
- 19-41: GtkButton `btnlist` and `btngrid`.
|
||||
These two buttons work as selection buttons to switch from list to grid and vice versa.
|
||||
These two buttons are connected to a stateful action `win.view`.
|
||||
This action is stateful and has a parameter.
|
||||
Such action consists of prefix, action name and parameter.
|
||||
The prefix of the action is `win`, which means the action belongs to the top level window.
|
||||
So, a prefix gives scope of the action.
|
||||
The action name is `view`.
|
||||
The parameters are `list` or `grid`, which show the state of the action.
|
||||
A parameter is also called a target, because it is a target to which the buttons are clicked on to change the action state.
|
||||
We often write the detailed action like "win.view::list" or "win.view::grid".
|
||||
- 21-22: The properties "action-name" and "action-target" belong to GtkActionable interface.
|
||||
GtkButton implements GtkActionable.
|
||||
The action name is "win.view" and the target is "list".
|
||||
Generally, a target is GVariant, which can be string, integer, float and so on.
|
||||
You need to use GVariant text format to write GVariant value in ui files.
|
||||
If the type of the GVarinat value is string, then the value with GVariant text format is bounded by single quotes or double quotes.
|
||||
Because ui file is xml format text, single quote cannot be written without escape.
|
||||
Its escape sequence is \'.
|
||||
Therefore, the target 'list' is written as \'list\'.
|
||||
Because the button is connected to the action, "clicked" signal handler isn't needed.
|
||||
- 23-27: The child widget of the button is GtkImage.
|
||||
GtkImage has a "resource" property.
|
||||
It is a GResource and GtkImage reads an image data from the resource and sets the image.
|
||||
This resource is built from 24x24-sized png image data, which is an original icon.
|
||||
- 50-53: GtkScrolledWindow.
|
||||
Its child widget will be GtkListView or GtkGridView.
|
||||
|
||||
The action `view` is created, connected to the "activate" signal handler and inserted to the window (action map) as follows.
|
||||
|
||||
~~~C
|
||||
act_view = g_simple_action_new_stateful ("view", g_variant_type_new("s"), g_variant_new_string ("list"));
|
||||
g_signal_connect (act_view, "activate", G_CALLBACK (view_activated), scr); /* scr is the GtkScrolledWindow object */
|
||||
g_action_map_add_action (G_ACTION_MAP (win), G_ACTION (act_view));
|
||||
~~~
|
||||
|
||||
The signal handler `view_activated` will be explained later.
|
||||
|
||||
## Factories
|
||||
|
||||
Each view (GtkListView and GtkGridView) has its own factory because its items have different structure of widgets.
|
||||
The factories are GtkBuilderListItemFactory objects.
|
||||
Their ui files are as follows.
|
||||
|
||||
factory_list.ui
|
||||
|
||||
@@@include
|
||||
list4/factory_list.ui
|
||||
@@@
|
||||
|
||||
factory_grid.ui
|
||||
|
||||
@@@include
|
||||
list4/factory_grid.ui
|
||||
@@@
|
||||
|
||||
The two files above are almost same.
|
||||
The difference is:
|
||||
|
||||
- The orientation of the box
|
||||
- The icon size
|
||||
- The position of the text of the label
|
||||
|
||||
@@@shell
|
||||
cd list4; diff factory_list.ui factory_grid.ui
|
||||
@@@
|
||||
|
||||
Each view item has two properties, "gicon" in GtkImage and "label" in GtkLabel.
|
||||
Because GFileInfo doesn't have properties correspond to icon object or filename, the factory uses closure tag to bind "gicon" and "label" properties to GFileInfo information.
|
||||
A function `get_icon` gets GIcon the GFileInfo object has.
|
||||
And a function `get_file_name` gets a filename the GFileInfo object has.
|
||||
|
||||
@@@include
|
||||
list4/list4.c get_icon get_file_name
|
||||
@@@
|
||||
|
||||
One important thing is view items own the object or string.
|
||||
It is achieved by `g_object_ref` to increase the reference count by one, or `strdup` to create a copy of the string.
|
||||
The object or string will be automatically freed in unbinding process when the view item is recycled.
|
||||
|
||||
## An activate signal handler of the action
|
||||
|
||||
An activate signal handler `view_activate` switches the view.
|
||||
It does two things.
|
||||
|
||||
- Change the child widget of GtkScrolledWindow.
|
||||
- Change the CSS of buttons to show the current state.
|
||||
|
||||
@@@include
|
||||
list4/list4.c view_activated
|
||||
@@@
|
||||
|
||||
The second parameter of this handler is the target of the clicked button.
|
||||
Its type is GVariant.
|
||||
|
||||
- If `btnlist` has been clicked, then `parameter` is "list".
|
||||
- If `btngrid` has been clicked, then `parameter` is "grid".
|
||||
|
||||
The third parameter `user_data` points GtkScrolledWindow, which is set in the `g_signal_connect` function.
|
||||
|
||||
- 4: `g_variant_get_string` gets the string from the GVariant variable.
|
||||
- 8-14: Sets the child of `scr`.
|
||||
- 15-17: Sets the CSS of the buttons.
|
||||
The background of the clicked button will be silver color and the other button will be white.
|
||||
- 18: Changes the state of the action.
|
||||
|
||||
## Activate signal of GtkListView and GtkGridView
|
||||
|
||||
Views (GtkListView and GtkGridView) have an "activate" signal.
|
||||
It is emitted when an item in the view is double clicked or the enter key is pressed.
|
||||
You can do anything you like by connecting the "activate" signal to the handler.
|
||||
|
||||
The example `list4` launches `tfe` text file editor if the item of the list is a text file.
|
||||
|
||||
~~~C
|
||||
static void
|
||||
list_activate (GtkListView *list, int position, gpointer user_data) {
|
||||
GFileInfo *info = G_FILE_INFO (g_list_model_get_item (G_LIST_MODEL (gtk_list_view_get_model (list)), position));
|
||||
launch_tfe_with_file (info);
|
||||
}
|
||||
|
||||
static void
|
||||
grid_activate (GtkGridView *grid, int position, gpointer user_data) {
|
||||
GFileInfo *info = G_FILE_INFO (g_list_model_get_item (G_LIST_MODEL (gtk_grid_view_get_model (grid)), position));
|
||||
launch_tfe_with_file (info);
|
||||
}
|
||||
|
||||
... ...
|
||||
... ...
|
||||
|
||||
g_signal_connect (GTK_LIST_VIEW (list), "activate", G_CALLBACK (list_activate), NULL);
|
||||
g_signal_connect (GTK_GRID_VIEW (grid), "activate", G_CALLBACK (grid_activate), NULL);
|
||||
~~~
|
||||
|
||||
The second parameter of the handlers is the position of the item (GFileInfo) of the GListModel list.
|
||||
So you can get the item with `g_list_model_get_item` function.
|
||||
|
||||
## Content type and launching an application
|
||||
|
||||
The function `launch_tfe_with_file` launches `tfe` with the file whose information has been taken by GFileInfo object, if the file is a text file.
|
||||
|
||||
GFileInfo has information about file type.
|
||||
The file type is like "text/plain", "text/x-csrc" and so on.
|
||||
It is called content type.
|
||||
Content type can be got with `g_file_info_get_content_type` function.
|
||||
|
||||
@@@include
|
||||
list4/list4.c launch_tfe_with_file
|
||||
@@@
|
||||
|
||||
- 13: Gets the content type of GFileInfo.
|
||||
- 14: Prints the content type.
|
||||
This is only useful to know a content type of a file.
|
||||
- 17-20: If the content type doesn't begin with "text/", then it returns.
|
||||
- 21: Creates GAppInfo object of `tfe` application.
|
||||
GAppInfo is interface and the variable `appinfo` points a GDesktopAppInfo object.
|
||||
GAppInfo is a collection of information of an application.
|
||||
- 29: Launches the application (`tfe`) with an argument `file`.
|
||||
`g_app_info_launch` has four parameters.
|
||||
The first parameter is GAppInfo object.
|
||||
The second parameter is a list of GFile objects.
|
||||
In this function, only one GFile object is given to `tfe`, but you can give more arguments.
|
||||
The third parameter is GAppLaunchContext, but this program gives NULL instead.
|
||||
The last parameter is the pointer to the pointer to GError.
|
||||
- 32: `g_list_free_full` frees the memories used by the list and items.
|
||||
|
||||
At present, my ubuntu runs on Gtk3, not Gtk4.
|
||||
If it runs on Gtk4, using `g_app_info_launch_default_for_uri` is convenient.
|
||||
The function automatically determines the default application from the file and launches it.
|
||||
For example, if the file is text, then it launches gedit with the file.
|
||||
Such functionality comes from desktop.
|
||||
|
||||
## Compilation and execution
|
||||
|
||||
The source files are located in [src/list4](list4) directory.
|
||||
To compile and execute list4, type as follows.
|
||||
|
||||
~~~
|
||||
$ cd list4 # or cd src/list4. It depends your current directory.
|
||||
$ meson _build
|
||||
$ ninja -C _build
|
||||
$ _build/list3
|
||||
~~~
|
||||
|
||||
Then a file list appears as a list style.
|
||||
Click on a button on the tool bar so that you can change the style to grid or back to list.
|
||||
Double click "list4.c" item, then `tfe` text editor runs with the argument "list4.c".
|
||||
The following is the screenshot.
|
||||
|
||||
![Screenshot](../image/screenshot_list4.png){width=8cm height=5.62cm}
|
||||
|
||||
## "gbytes" property of GtkBuilderListItemFactory
|
||||
|
||||
GtkBuilderListItemFactory has "gbytes" property.
|
||||
The property contains a byte sequence of ui data.
|
||||
If you use this property, you can put the contents of `factory_list.ui` and `factory_grid.ui`into `list4.ui`.
|
||||
The following shows a part of the new list4.ui file.
|
||||
|
||||
~~~xml
|
||||
<object class="GtkListView" id="list">
|
||||
<property name="model">
|
||||
<object class="GtkSingleSelection" id="singleselection">
|
||||
<property name="model">
|
||||
<object class="GtkDirectoryList" id="directorylist">
|
||||
<property name="attributes">standard::name,standard::icon,standard::content-type</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</property>
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="bytes"><![CDATA[
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<property name="child">
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
|
||||
<property name="spacing">20</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<binding name="gicon">
|
||||
<closure type="GIcon" function="get_icon">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</closure>
|
||||
</binding>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="hexpand">TRUE</property>
|
||||
<property name="xalign">0</property>
|
||||
<binding name="label">
|
||||
<closure type="gchararray" function="get_file_name">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</closure>
|
||||
</binding>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
]]></property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
~~~
|
||||
|
||||
CDATA section begins with "<![CDATA[" and ends with "]]>".
|
||||
The contents of CDATA section is recognized as a string.
|
||||
Any character, even if it is a key syntax marker such as '<' or '>', is recognized literally.
|
||||
Therefore, the text between "<![CDATA[" and "]]>" is inserted to "bytes" property as it is.
|
||||
|
||||
This method decreases the number of ui files.
|
||||
But, the new ui file is a bit complicated especially for the beginners.
|
||||
If you feel some difficulty, it is better for you to separate the ui file.
|
||||
|
||||
A directory [src/list5](list5) includes the ui file above.
|
||||
|