mirror of
https://github.com/ToshioCP/Gtk4-tutorial.git
synced 2025-01-12 20:03:28 +01:00
Use fenced code block.
This commit is contained in:
parent
26fc01e64e
commit
880dd92c82
31 changed files with 3448 additions and 3100 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -16,6 +16,7 @@ src/tfe5/_build
|
|||
src/tfe5/hello.txt
|
||||
src/menu/a.out
|
||||
src/color/_build
|
||||
src/turtle
|
||||
|
||||
html/*
|
||||
latex/*
|
||||
|
|
101
Rakefile
101
Rakefile
|
@ -156,12 +156,105 @@ EOS
|
|||
main.gsub!(/@@@ abstract\n/, abstract_latex)
|
||||
|
||||
helper = <<'EOS'
|
||||
\usepackage[pdftex]{graphicx}
|
||||
\usepackage[colorlinks=true,linkcolor=black]{hyperref}
|
||||
\usepackage[margin=2.4cm]{geometry}
|
||||
\usepackage{tikz}
|
||||
\usepackage[margin=2.4cm]{geometry}
|
||||
% -------------------------------------------------------------
|
||||
% Following codes are extracted from the preamble generated by:
|
||||
% $ pandoc -s -o sample.tex sample.md
|
||||
% -------------------------------------------------------------
|
||||
\usepackage{lmodern}
|
||||
\usepackage{amssymb,amsmath}
|
||||
\usepackage{ifxetex,ifluatex}
|
||||
\ifnum 0\ifxetex 1\fi\ifluatex 1\fi=0 % if pdftex
|
||||
\usepackage[T1]{fontenc}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage{textcomp} % provide euro and other symbols
|
||||
\else % if luatex or xetex
|
||||
\usepackage{unicode-math}
|
||||
\defaultfontfeatures{Scale=MatchLowercase}
|
||||
\defaultfontfeatures[\rmfamily]{Ligatures=TeX,Scale=1}
|
||||
\fi
|
||||
% Use upquote if available, for straight quotes in verbatim environments
|
||||
\IfFileExists{upquote.sty}{\usepackage{upquote}}{}
|
||||
\IfFileExists{microtype.sty}{% use microtype if available
|
||||
\usepackage[]{microtype}
|
||||
\UseMicrotypeSet[protrusion]{basicmath} % disable protrusion for tt fonts
|
||||
}{}
|
||||
\makeatletter
|
||||
\@ifundefined{KOMAClassName}{% if non-KOMA class
|
||||
\IfFileExists{parskip.sty}{%
|
||||
\usepackage{parskip}
|
||||
}{% else
|
||||
\setlength{\parindent}{0pt}
|
||||
\setlength{\parskip}{6pt plus 2pt minus 1pt}}
|
||||
}{% if KOMA class
|
||||
\KOMAoptions{parskip=half}}
|
||||
\makeatother
|
||||
\usepackage{xcolor}
|
||||
\IfFileExists{xurl.sty}{\usepackage{xurl}}{} % add URL line breaks if available
|
||||
\IfFileExists{bookmark.sty}{\usepackage{bookmark}}{\usepackage{hyperref}}
|
||||
\hypersetup{
|
||||
hidelinks,
|
||||
pdfcreator={LaTeX via pandoc}}
|
||||
\urlstyle{same} % disable monospaced font for URLs
|
||||
\usepackage{color}
|
||||
\usepackage{fancyvrb}
|
||||
\newcommand{\VerbBar}{|}
|
||||
\newcommand{\VERB}{\Verb[commandchars=\\\{\}]}
|
||||
\DefineVerbatimEnvironment{Highlighting}{Verbatim}{commandchars=\\\{\}}
|
||||
% Add ',fontsize=\small' for more characters per line
|
||||
\newenvironment{Shaded}{}{}
|
||||
\newcommand{\AlertTok}[1]{\textcolor[rgb]{1.00,0.00,0.00}{\textbf{#1}}}
|
||||
\newcommand{\AnnotationTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textbf{\textit{#1}}}}
|
||||
\newcommand{\AttributeTok}[1]{\textcolor[rgb]{0.49,0.56,0.16}{#1}}
|
||||
\newcommand{\BaseNTok}[1]{\textcolor[rgb]{0.25,0.63,0.44}{#1}}
|
||||
\newcommand{\BuiltInTok}[1]{#1}
|
||||
\newcommand{\CharTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{#1}}
|
||||
\newcommand{\CommentTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textit{#1}}}
|
||||
\newcommand{\CommentVarTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textbf{\textit{#1}}}}
|
||||
\newcommand{\ConstantTok}[1]{\textcolor[rgb]{0.53,0.00,0.00}{#1}}
|
||||
\newcommand{\ControlFlowTok}[1]{\textcolor[rgb]{0.00,0.44,0.13}{\textbf{#1}}}
|
||||
\newcommand{\DataTypeTok}[1]{\textcolor[rgb]{0.56,0.13,0.00}{#1}}
|
||||
\newcommand{\DecValTok}[1]{\textcolor[rgb]{0.25,0.63,0.44}{#1}}
|
||||
\newcommand{\DocumentationTok}[1]{\textcolor[rgb]{0.73,0.13,0.13}{\textit{#1}}}
|
||||
\newcommand{\ErrorTok}[1]{\textcolor[rgb]{1.00,0.00,0.00}{\textbf{#1}}}
|
||||
\newcommand{\ExtensionTok}[1]{#1}
|
||||
\newcommand{\FloatTok}[1]{\textcolor[rgb]{0.25,0.63,0.44}{#1}}
|
||||
\newcommand{\FunctionTok}[1]{\textcolor[rgb]{0.02,0.16,0.49}{#1}}
|
||||
\newcommand{\ImportTok}[1]{#1}
|
||||
\newcommand{\InformationTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textbf{\textit{#1}}}}
|
||||
\newcommand{\KeywordTok}[1]{\textcolor[rgb]{0.00,0.44,0.13}{\textbf{#1}}}
|
||||
\newcommand{\NormalTok}[1]{#1}
|
||||
\newcommand{\OperatorTok}[1]{\textcolor[rgb]{0.40,0.40,0.40}{#1}}
|
||||
\newcommand{\OtherTok}[1]{\textcolor[rgb]{0.00,0.44,0.13}{#1}}
|
||||
\newcommand{\PreprocessorTok}[1]{\textcolor[rgb]{0.74,0.48,0.00}{#1}}
|
||||
\newcommand{\RegionMarkerTok}[1]{#1}
|
||||
\newcommand{\SpecialCharTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{#1}}
|
||||
\newcommand{\SpecialStringTok}[1]{\textcolor[rgb]{0.73,0.40,0.53}{#1}}
|
||||
\newcommand{\StringTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{#1}}
|
||||
\newcommand{\VariableTok}[1]{\textcolor[rgb]{0.10,0.09,0.49}{#1}}
|
||||
\newcommand{\VerbatimStringTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{#1}}
|
||||
\newcommand{\WarningTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textbf{\textit{#1}}}}
|
||||
\usepackage{graphicx}
|
||||
\makeatletter
|
||||
\def\maxwidth{\ifdim\Gin@nat@width>\linewidth\linewidth\else\Gin@nat@width\fi}
|
||||
\def\maxheight{\ifdim\Gin@nat@height>\textheight\textheight\else\Gin@nat@height\fi}
|
||||
\makeatother
|
||||
% Scale images if necessary, so that they will not overflow the page
|
||||
% margins by default, and it is still possible to overwrite the defaults
|
||||
% using explicit options in \includegraphics[width, height, ...]{}
|
||||
\setkeys{Gin}{width=\maxwidth,height=\maxheight,keepaspectratio}
|
||||
% Set default figure placement to htbp
|
||||
\makeatletter
|
||||
\def\fps@figure{htbp}
|
||||
\makeatother
|
||||
\setlength{\emergencystretch}{3em} % prevent overfull lines
|
||||
\providecommand{\tightlist}{%
|
||||
\setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}}
|
||||
\setcounter{secnumdepth}{-\maxdimen} % remove section numbering
|
||||
|
||||
\author{}
|
||||
\date{}
|
||||
EOS
|
||||
# tasks
|
||||
|
||||
|
@ -262,7 +355,7 @@ end
|
|||
|
||||
0.upto(srcfiles.size - 1) do |i|
|
||||
file "latex/#{srcfiles[i].to_tex}" => (srcfiles[i].c_files << srcfiles[i].path) do
|
||||
src2md srcfiles[i].path, "latex/#{srcfiles[i].to_md}", 80
|
||||
src2md srcfiles[i].path, "latex/#{srcfiles[i].to_md}", 86
|
||||
sh "pandoc -o latex/#{srcfiles[i].to_tex} --top-level-division=chapter latex/#{srcfiles[i].to_md}"
|
||||
File.delete("latex/#{srcfiles[i].to_md}")
|
||||
end
|
||||
|
|
|
@ -20,7 +20,7 @@ You can read it without download.
|
|||
1. [Widgets (2)](gfm/sec5.md)
|
||||
1. [Widgets (3)](gfm/sec6.md)
|
||||
1. [Define Child object](gfm/sec7.md)
|
||||
1. [Ui file and GtkBuiler](gfm/sec8.md)
|
||||
1. [Ui file and GtkBuilder](gfm/sec8.md)
|
||||
1. [Build system](gfm/sec9.md)
|
||||
1. [Instance and class](gfm/sec10.md)
|
||||
1. [Signals](gfm/sec11.md)
|
||||
|
|
10
gfm/sec10.md
10
gfm/sec10.md
|
@ -95,10 +95,12 @@ The structure of `TfeTextView` is like the following diagram.
|
|||
|
||||
The function `tfe_text_view_new` generates a new TfeTextView instance.
|
||||
|
||||
~~~C
|
||||
1 GtkWidget *
|
||||
2 tfe_text_view_new (void) {
|
||||
3 return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL));
|
||||
4 }
|
||||
~~~
|
||||
|
||||
When this function is run, the following procedure is gone through.
|
||||
|
||||
|
@ -113,6 +115,7 @@ Step four is done by the function `tfe_text_view_init`.
|
|||
> In the same way, `gtk_text_view_init`, `gtk_widget_init` and `g_object_init` is the initialization functions of GtkTextView, GtkWidget and GObject respectively.
|
||||
> You can find them in the GTK or GLib source files.
|
||||
|
||||
~~~C
|
||||
1 static void
|
||||
2 tfe_text_view_init (TfeTextView *tv) {
|
||||
3 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
||||
|
@ -121,6 +124,7 @@ Step four is done by the function `tfe_text_view_init`.
|
|||
6 gtk_text_buffer_set_modified (tb, FALSE);
|
||||
7 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR);
|
||||
8 }
|
||||
~~~
|
||||
|
||||
`tfe_text_view_init` initializes the instance.
|
||||
|
||||
|
@ -151,6 +155,7 @@ Class comprises mainly pointers to functions.
|
|||
Those functions are used by the object itself or its descendent objects.
|
||||
For example, GObject class is declared in `gobject.h` in GLib source files.
|
||||
|
||||
~~~C
|
||||
1 typedef struct _GObjectClass GObjectClass;
|
||||
2 typedef struct _GObjectClass GInitiallyUnownedClass;
|
||||
3
|
||||
|
@ -189,6 +194,7 @@ For example, GObject class is declared in `gobject.h` in GLib source files.
|
|||
36 /* padding */
|
||||
37 gpointer pdummy[6];
|
||||
38 };
|
||||
~~~
|
||||
|
||||
I'd like to explain some of the members.
|
||||
There's a pointer to the function `dispose` in line 22.
|
||||
|
@ -218,6 +224,7 @@ Let's look at all the classes from GObject, which is the top level object, to Tf
|
|||
|
||||
The following is extracts from the source files (not exactly the same).
|
||||
|
||||
~~~C
|
||||
1 struct _GtkWidgetClass {
|
||||
2 GInitiallyUnownedClass parent_class;
|
||||
3 /*< public >*/
|
||||
|
@ -326,6 +333,7 @@ The following is extracts from the source files (not exactly the same).
|
|||
106 GtkTextView parent_class;
|
||||
107 } TfeTextViewClass;
|
||||
108
|
||||
~~~
|
||||
|
||||
- 105-107: This three lines are generated by the macro G\_DECLARE\_FINAL\_TYPE.
|
||||
So, they are not written in either `tfe_text_view.h` or `tfe_text_view.c`.
|
||||
|
@ -370,6 +378,7 @@ In the destruction process of TfeTextView, the reference count of widgets relate
|
|||
But GFile pointed by `tv->file` needs to decrease its reference count by one.
|
||||
You must write the code in the dispose handler `tfe_text_view_dispose`.
|
||||
|
||||
~~~C
|
||||
1 static void
|
||||
2 tfe_text_view_dispose (GObject *gobject) {
|
||||
3 TfeTextView *tv = TFE_TEXT_VIEW (gobject);
|
||||
|
@ -379,6 +388,7 @@ You must write the code in the dispose handler `tfe_text_view_dispose`.
|
|||
7
|
||||
8 G_OBJECT_CLASS (tfe_text_view_parent_class)->dispose (gobject);
|
||||
9 }
|
||||
~~~
|
||||
|
||||
- 5,6: If `tv->file` points a GFile, decrease its reference count.
|
||||
`g_clear_object` decreases the reference count and assigns NULL to `tv->file`. In dispose handlers, we usually use `g_clear_object` rather than `g_object_unref`.
|
||||
|
|
|
@ -55,6 +55,7 @@ If you need to register two or more signals, static array is usually used.
|
|||
|
||||
Signal registration codes are written in the class initialization function.
|
||||
|
||||
~~~C
|
||||
1 static void
|
||||
2 tfe_text_view_class_init (TfeTextViewClass *class) {
|
||||
3 GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
|
@ -82,6 +83,7 @@ Signal registration codes are written in the class initialization function.
|
|||
25 1 /* n_params */,
|
||||
26 param_types);
|
||||
27 }
|
||||
~~~
|
||||
|
||||
- 6-15: Register "change-file"signal.
|
||||
`g_signal_newv` function is used.
|
||||
|
|
12
gfm/sec12.md
12
gfm/sec12.md
|
@ -9,13 +9,16 @@ In this section I will explain each function in TfeTextView object.
|
|||
`tfe.h` is a top header file and it includes `gtk.h` and all the header files.
|
||||
Every C source files, which are `tfeapplication.c`, `tfenotebook.c` and `tfetextview.c`, include `tfe.h` at the beginning of each file.
|
||||
|
||||
~~~C
|
||||
1 #include <gtk/gtk.h>
|
||||
2
|
||||
3 #include "tfetextview.h"
|
||||
4 #include "tfenotebook.h"
|
||||
~~~
|
||||
|
||||
`tfetextview.h` is a header file which describes the public functions in `tfetextview.c`.
|
||||
|
||||
~~~C
|
||||
1 #define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type ()
|
||||
2 G_DECLARE_FINAL_TYPE (TfeTextView, tfe_text_view, TFE, TEXT_VIEW, GtkTextView)
|
||||
3
|
||||
|
@ -45,6 +48,7 @@ Every C source files, which are `tfeapplication.c`, `tfenotebook.c` and `tfetext
|
|||
27 GtkWidget *
|
||||
28 tfe_text_view_new (void);
|
||||
29
|
||||
~~~
|
||||
|
||||
- 1-2: These two lines are used to define TfeTextView.
|
||||
- 4-10: Definitions of parameter used in the handler of "open-response" signal.
|
||||
|
@ -75,6 +79,7 @@ If an error occures during the genration process, NULL is returned.
|
|||
|
||||
Each function is defined as follows.
|
||||
|
||||
~~~C
|
||||
1 GtkWidget *
|
||||
2 tfe_text_view_new_with_file (GFile *file) {
|
||||
3 g_return_val_if_fail (G_IS_FILE (file), NULL);
|
||||
|
@ -99,6 +104,7 @@ Each function is defined as follows.
|
|||
22 tfe_text_view_new (void) {
|
||||
23 return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL));
|
||||
24 }
|
||||
~~~
|
||||
|
||||
- 21-24: `tfe_text_view_new`.
|
||||
Just returns the value from the function `g_object_new` but casted to the pointer to GtkWidget.
|
||||
|
@ -137,6 +143,7 @@ If `tv->file` is NULL, then it shows GtkFileChooserDialog and lets the user to g
|
|||
If an error occures, it is shown to the user through the message dialog.
|
||||
The error is managed only in the object and no information is notified to the caller.
|
||||
|
||||
~~~C
|
||||
1 static void
|
||||
2 saveas_dialog_response (GtkWidget *dialog, gint response, TfeTextView *tv) {
|
||||
3 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
||||
|
@ -207,6 +214,7 @@ The error is managed only in the object and no information is notified to the ca
|
|||
68 g_signal_connect (dialog, "response", G_CALLBACK (saveas_dialog_response), tv);
|
||||
69 gtk_widget_show (dialog);
|
||||
70 }
|
||||
~~~
|
||||
|
||||
- 18-55: `Tfe_text_view_save` function.
|
||||
- 20: If `tv` is not a pointer to TfeTextView, then it logs an error message and immediately returns.
|
||||
|
@ -258,6 +266,7 @@ Otherwise probably bad things will happen.
|
|||
GtkWidget `win` is expected to be the top level window of the application.
|
||||
It will be used as a transient parent window for the argument to the function `gtk_file_chooser_dialog_new`.
|
||||
|
||||
~~~C
|
||||
1 static void
|
||||
2 open_dialog_response(GtkWidget *dialog, gint response, TfeTextView *tv) {
|
||||
3 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
||||
|
@ -307,6 +316,7 @@ It will be used as a transient parent window for the argument to the function `g
|
|||
47 g_signal_connect (dialog, "response", G_CALLBACK (open_dialog_response), tv);
|
||||
48 gtk_widget_show (dialog);
|
||||
49 }
|
||||
~~~
|
||||
|
||||
- 36-49: `tfe_text_view_open` function.
|
||||
- 43: Generate GtkFileChooserDialog.
|
||||
|
@ -345,12 +355,14 @@ However, in Gtk4, `gtk_dialog_run`is unavailable any more.
|
|||
|
||||
`gtk_text_view_get_file` is a simple function show as follows.
|
||||
|
||||
~~~C
|
||||
1 GFile *
|
||||
2 tfe_text_view_get_file (TfeTextView *tv) {
|
||||
3 g_return_val_if_fail (TFE_IS_TEXT_VIEW (tv), NULL);
|
||||
4
|
||||
5 return g_file_dup (tv->file);
|
||||
6 }
|
||||
~~~
|
||||
|
||||
The important thing is duplicate `tv->file`.
|
||||
Otherwise, if the caller free the GFile object, `tv->file` is no more guaranteed to point the GFile.
|
||||
|
|
12
gfm/sec13.md
12
gfm/sec13.md
|
@ -6,6 +6,7 @@ GtkNotebook is a very important object in the text file editor `tfe`.
|
|||
It connects the application and TfeTextView objects.
|
||||
`tfenotebook.h` and `tfenotebook.c` have a set of functions related to GtkTextbook.
|
||||
|
||||
~~~C
|
||||
1 void
|
||||
2 notebook_page_save(GtkNotebook *nb);
|
||||
3
|
||||
|
@ -18,6 +19,7 @@ It connects the application and TfeTextView objects.
|
|||
10 void
|
||||
11 notebook_page_new (GtkNotebook *nb);
|
||||
12
|
||||
~~~
|
||||
|
||||
This header file shows the public functions in `tfenotebook.c`.
|
||||
|
||||
|
@ -44,6 +46,7 @@ Now let's look at each program of the functions.
|
|||
|
||||
## notebook\_page\_new
|
||||
|
||||
~~~C
|
||||
1 static gchar*
|
||||
2 get_untitled () {
|
||||
3 static int c = -1;
|
||||
|
@ -81,6 +84,7 @@ Now let's look at each program of the functions.
|
|||
35 filename = get_untitled ();
|
||||
36 notebook_page_build (nb, tv, filename);
|
||||
37 }
|
||||
~~~
|
||||
|
||||
- 27-37: `notebook_page_new` function.
|
||||
- 29: `g_return_if_fail` is used to check the argument.
|
||||
|
@ -102,6 +106,7 @@ The caller of `get_untitled` is in charge of freeing the memories of the string.
|
|||
|
||||
## notebook\_page\_new\_with\_file
|
||||
|
||||
~~~C
|
||||
1 void
|
||||
2 notebook_page_new_with_file (GtkNotebook *nb, GFile *file) {
|
||||
3 g_return_if_fail(GTK_IS_NOTEBOOK (nb));
|
||||
|
@ -115,6 +120,7 @@ The caller of `get_untitled` is in charge of freeing the memories of the string.
|
|||
11 filename = g_file_get_basename (file);
|
||||
12 notebook_page_build (nb, tv, filename);
|
||||
13 }
|
||||
~~~
|
||||
|
||||
- 9-10: Call `tfe_text_view_new_with_file`.
|
||||
If it returns NULL, then do nothing and return because of an error.
|
||||
|
@ -122,6 +128,7 @@ If it returns NULL, then do nothing and return because of an error.
|
|||
|
||||
## notebook\_page\_open
|
||||
|
||||
~~~C
|
||||
1 static void
|
||||
2 open_response (TfeTextView *tv, gint response, GtkNotebook *nb) {
|
||||
3 GFile *file;
|
||||
|
@ -150,6 +157,7 @@ If it returns NULL, then do nothing and return because of an error.
|
|||
26 g_signal_connect (TFE_TEXT_VIEW (tv), "open-response", G_CALLBACK (open_response), nb);
|
||||
27 tfe_text_view_open (TFE_TEXT_VIEW (tv), gtk_widget_get_ancestor (GTK_WIDGET (nb), GTK_TYPE_WINDOW));
|
||||
28 }
|
||||
~~~
|
||||
|
||||
- 19-28: `notebook_page_open` function.
|
||||
- 25: Generate TfeTextView object.
|
||||
|
@ -169,6 +177,7 @@ Get the filename, build the contents of the page.
|
|||
|
||||
## notebook\_page\_save
|
||||
|
||||
~~~C
|
||||
1 void
|
||||
2 notebook_page_save(GtkNotebook *nb) {
|
||||
3 gint i;
|
||||
|
@ -180,6 +189,7 @@ Get the filename, build the contents of the page.
|
|||
9 tv = gtk_scrolled_window_get_child (GTK_SCROLLED_WINDOW (scr));
|
||||
10 tfe_text_view_save (TFE_TEXT_VIEW (tv));
|
||||
11 }
|
||||
~~~
|
||||
|
||||
- 7-9: Get TfeTextView belongs to the current notebook page.
|
||||
- 10: Call `tfe_text_view_save`.
|
||||
|
@ -190,6 +200,7 @@ The function `file_changed` is a handler connected to "change-file" signal.
|
|||
If `tv->file` is changed, TfeTextView emits this signal.
|
||||
This handler changes the label of GtkNotebookPage.
|
||||
|
||||
~~~C
|
||||
1 static void
|
||||
2 file_changed (TfeTextView *tv, GtkNotebook *nb) {
|
||||
3 GFile *file;
|
||||
|
@ -208,6 +219,7 @@ This handler changes the label of GtkNotebookPage.
|
|||
16 g_object_unref (file);
|
||||
17 g_free (filename);
|
||||
18 }
|
||||
~~~
|
||||
|
||||
- 8: Get GFile from TfeTextView.
|
||||
- 9: Get GkScrolledWindow which is the parent widget of `tv`.
|
||||
|
|
10
gfm/sec14.md
10
gfm/sec14.md
|
@ -15,6 +15,7 @@ It does following things.
|
|||
Th function `main` is the first invoked function in C language.
|
||||
It connects the command line given by the user and GTK application.
|
||||
|
||||
~~~C
|
||||
1 int
|
||||
2 main (int argc, char **argv) {
|
||||
3 GtkApplication *app;
|
||||
|
@ -30,6 +31,7 @@ It connects the command line given by the user and GTK application.
|
|||
13 g_object_unref (app);
|
||||
14 return stat;
|
||||
15 }
|
||||
~~~
|
||||
|
||||
- 6: Generate GtkApplication object.
|
||||
- 8-10: Connect "startup", "activate" and "open signals to their handlers.
|
||||
|
@ -47,6 +49,7 @@ What the signal handler needs to do is initialization of the application.
|
|||
|
||||
The handler is as follows.
|
||||
|
||||
~~~C
|
||||
1 static void
|
||||
2 tfe_startup (GApplication *application) {
|
||||
3 GtkApplication *app = GTK_APPLICATION (application);
|
||||
|
@ -79,6 +82,7 @@ The handler is as follows.
|
|||
30 gtk_css_provider_load_from_data (provider, "textview {padding: 10px; font-family: monospace; font-size: 12pt;}", -1);
|
||||
31 gtk_style_context_add_provider_for_display (display, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_USER);
|
||||
32 }
|
||||
~~~
|
||||
|
||||
- 12-15: Build widgets using ui file (resource).
|
||||
Connect the top window and the application using `gtk_window_set_application`.
|
||||
|
@ -168,6 +172,7 @@ CSS set to the context takes precedence over the one set to the display.
|
|||
The handler of "activate" and "open" signal are `tfe_activate` and `tfe_open` respectively.
|
||||
They just generate a new GtkNotebookPage.
|
||||
|
||||
~~~C
|
||||
1 static void
|
||||
2 tfe_activate (GApplication *application) {
|
||||
3 GtkApplication *app = GTK_APPLICATION (application);
|
||||
|
@ -201,6 +206,7 @@ They just generate a new GtkNotebookPage.
|
|||
31 notebook_page_new (nb);
|
||||
32 gtk_widget_show (win);
|
||||
33 }
|
||||
~~~
|
||||
|
||||
- 1-14: `tfe_activate`.
|
||||
- 8-10: Get GtkNotebook object.
|
||||
|
@ -249,6 +255,7 @@ The second instance immediately quits so shell prompt soon appears.
|
|||
|
||||
## a series of handlers correspond to the button signals
|
||||
|
||||
~~~C
|
||||
1 static void
|
||||
2 open_clicked (GtkWidget *btno, GtkNotebook *nb) {
|
||||
3 notebook_page_open (nb);
|
||||
|
@ -279,6 +286,7 @@ The second instance immediately quits so shell prompt soon appears.
|
|||
28 gtk_notebook_remove_page (GTK_NOTEBOOK (nb), i);
|
||||
29 }
|
||||
30 }
|
||||
~~~
|
||||
|
||||
`open_clicked`, `new_clicked` and `save_clicked` just call corresponding notebook page functions.
|
||||
`close_clicked` is a bit complicated.
|
||||
|
@ -289,6 +297,7 @@ First, get the top level window and call `gtk_window_destroy`.
|
|||
|
||||
## meson.build
|
||||
|
||||
~~~meson
|
||||
1 project('tfe', 'c')
|
||||
2
|
||||
3 gtkdep = dependency('gtk4')
|
||||
|
@ -299,6 +308,7 @@ First, get the top level window and call `gtk_window_destroy`.
|
|||
8 sourcefiles=files('tfeapplication.c', 'tfenotebook.c', 'tfetextview.c')
|
||||
9
|
||||
10 executable('tfe', sourcefiles, resources, dependencies: gtkdep)
|
||||
~~~
|
||||
|
||||
In this file, just the source file names are modified.
|
||||
|
||||
|
|
18
gfm/sec15.md
18
gfm/sec15.md
|
@ -33,6 +33,7 @@ It is a good practice for you to add more features.
|
|||
|
||||
## meson.buld
|
||||
|
||||
~~~meson
|
||||
1 project('tfe', 'c')
|
||||
2
|
||||
3 gtkdep = dependency('gtk4')
|
||||
|
@ -43,18 +44,22 @@ It is a good practice for you to add more features.
|
|||
8 sourcefiles=files('tfeapplication.c', 'tfenotebook.c', 'tfetextview.c')
|
||||
9
|
||||
10 executable('tfe', sourcefiles, resources, dependencies: gtkdep)
|
||||
~~~
|
||||
|
||||
## tfe.gresource.xml
|
||||
|
||||
~~~xml
|
||||
1 <?xml version="1.0" encoding="UTF-8"?>
|
||||
2 <gresources>
|
||||
3 <gresource prefix="/com/github/ToshioCP/tfe">
|
||||
4 <file>tfe.ui</file>
|
||||
5 </gresource>
|
||||
6 </gresources>
|
||||
~~~
|
||||
|
||||
## tfe.ui
|
||||
|
||||
~~~xml
|
||||
1 <interface>
|
||||
2 <object class="GtkApplicationWindow" id="win">
|
||||
3 <property name="title">file editor</property>
|
||||
|
@ -119,16 +124,20 @@ It is a good practice for you to add more features.
|
|||
62 </object>
|
||||
63 </interface>
|
||||
64
|
||||
~~~
|
||||
|
||||
## tfe.h
|
||||
|
||||
~~~C
|
||||
1 #include <gtk/gtk.h>
|
||||
2
|
||||
3 #include "tfetextview.h"
|
||||
4 #include "tfenotebook.h"
|
||||
~~~
|
||||
|
||||
## tfeapplication.c
|
||||
|
||||
~~~C
|
||||
1 #include "tfe.h"
|
||||
2
|
||||
3 static void
|
||||
|
@ -246,9 +255,11 @@ It is a good practice for you to add more features.
|
|||
115 return stat;
|
||||
116 }
|
||||
117
|
||||
~~~
|
||||
|
||||
## tfenotebook.h
|
||||
|
||||
~~~C
|
||||
1 void
|
||||
2 notebook_page_save(GtkNotebook *nb);
|
||||
3
|
||||
|
@ -261,9 +272,11 @@ It is a good practice for you to add more features.
|
|||
10 void
|
||||
11 notebook_page_new (GtkNotebook *nb);
|
||||
12
|
||||
~~~
|
||||
|
||||
## tfenotebook.c
|
||||
|
||||
~~~C
|
||||
1 #include "tfe.h"
|
||||
2
|
||||
3 /* The returned string should be freed with g_free() when no longer needed. */
|
||||
|
@ -380,9 +393,11 @@ It is a good practice for you to add more features.
|
|||
114 notebook_page_build (nb, tv, filename);
|
||||
115 }
|
||||
116
|
||||
~~~
|
||||
|
||||
## tfetextview.h
|
||||
|
||||
~~~C
|
||||
1 #define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type ()
|
||||
2 G_DECLARE_FINAL_TYPE (TfeTextView, tfe_text_view, TFE, TEXT_VIEW, GtkTextView)
|
||||
3
|
||||
|
@ -412,9 +427,11 @@ It is a good practice for you to add more features.
|
|||
27 GtkWidget *
|
||||
28 tfe_text_view_new (void);
|
||||
29
|
||||
~~~
|
||||
|
||||
## tfetextview.c
|
||||
|
||||
~~~C
|
||||
1 #include "tfe.h"
|
||||
2
|
||||
3 struct _TfeTextView
|
||||
|
@ -633,6 +650,7 @@ It is a good practice for you to add more features.
|
|||
216 return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL));
|
||||
217 }
|
||||
218
|
||||
~~~
|
||||
|
||||
## Total number of lines, words and charcters
|
||||
|
||||
|
|
|
@ -119,6 +119,7 @@ So, if the action is activated, the handler will be invoked.
|
|||
|
||||
The following is a simple example of menus and actions.
|
||||
|
||||
~~~C
|
||||
1 #include <gtk/gtk.h>
|
||||
2
|
||||
3 static void
|
||||
|
@ -166,6 +167,7 @@ The following is a simple example of menus and actions.
|
|||
45 return stat;
|
||||
46 }
|
||||
47
|
||||
~~~
|
||||
|
||||
- 3-7: `quit_activated` is a handler of an action `act_quit`.
|
||||
Handlers of actions have three parameters.
|
||||
|
|
13
gfm/sec17.md
13
gfm/sec17.md
|
@ -21,7 +21,8 @@ The signal handler will be described after the explanation of this code.
|
|||
static void
|
||||
on_activate (GApplication *app, gpointer user_data) {
|
||||
... ... ...
|
||||
GSimpleAction *act_fullscreen = g_simple_action_new_stateful ("fullscreen", NULL, g_variant_new_boolean (FALSE));
|
||||
GSimpleAction *act_fullscreen = g_simple_action_new_stateful ("fullscreen",
|
||||
NULL, g_variant_new_boolean (FALSE));
|
||||
GMenuItem *menu_item_fullscreen = g_menu_item_new ("Full Screen", "win.fullscreen");
|
||||
g_signal_connect (act_fullscreen, "change-state", G_CALLBACK (fullscreen_changed), win);
|
||||
... ... ...
|
||||
|
@ -116,7 +117,8 @@ Those colors are given to the signal handler as a parameter.
|
|||
static void
|
||||
on_activate (GApplication *app, gpointer user_data) {
|
||||
... ... ...
|
||||
GSimpleAction *act_color = g_simple_action_new_stateful ("color", g_variant_type_new("s"), g_variant_new_string ("red"));
|
||||
GSimpleAction *act_color = g_simple_action_new_stateful ("color",
|
||||
g_variant_type_new("s"), g_variant_new_string ("red"));
|
||||
GMenuItem *menu_item_red = g_menu_item_new ("Red", "win.color::red");
|
||||
GMenuItem *menu_item_green = g_menu_item_new ("Green", "win.color::green");
|
||||
GMenuItem *menu_item_blue = g_menu_item_new ("Blue", "win.color::blue");
|
||||
|
@ -150,7 +152,8 @@ The following is the "activate" signal handler.
|
|||
|
||||
static void
|
||||
color_activated(GSimpleAction *action, GVariant *parameter, gpointer win) {
|
||||
gchar *color = g_strdup_printf ("label#lb {background-color: %s;}", g_variant_get_string (parameter, NULL));
|
||||
gchar *color = g_strdup_printf ("label#lb {background-color: %s;}",
|
||||
g_variant_get_string (parameter, NULL));
|
||||
gtk_css_provider_load_from_data (provider, color, -1);
|
||||
g_free (color);
|
||||
g_action_change_state (G_ACTION (action), parameter);
|
||||
|
@ -187,6 +190,7 @@ When GVariantType is generated, the type is expressed by the string.
|
|||
The following program is a simple example.
|
||||
It finally output the string "s".
|
||||
|
||||
~~~C
|
||||
1 #include <glib.h>
|
||||
2
|
||||
3 int
|
||||
|
@ -195,6 +199,7 @@ It finally output the string "s".
|
|||
6 const gchar *type_string = g_variant_type_peek_string (vtype);
|
||||
7 g_print ("%s\n",type_string);
|
||||
8 }
|
||||
~~~
|
||||
|
||||
- `g_variant_tpe_new` generates GVariantType.
|
||||
It uses a type string "s" which means string.
|
||||
|
@ -217,6 +222,7 @@ And the radio button of the selected menu turns on.
|
|||
|
||||
The code is as follows.
|
||||
|
||||
~~~C
|
||||
1 #include <gtk/gtk.h>
|
||||
2
|
||||
3 static GtkCssProvider *provider;
|
||||
|
@ -322,6 +328,7 @@ The code is as follows.
|
|||
103 return stat;
|
||||
104 }
|
||||
105
|
||||
~~~
|
||||
|
||||
- 5-26: Signal handlers.
|
||||
They have been explained in this section.
|
||||
|
|
51
gfm/sec18.md
51
gfm/sec18.md
|
@ -1,4 +1,4 @@
|
|||
Up: [Readme.md](../Readme.md), Prev: [Section 17](sec17.md)
|
||||
Up: [Readme.md](../Readme.md), Prev: [Section 17](sec17.md), Next: [Section 19](sec19.md)
|
||||
|
||||
# Ui file for menu and action entries
|
||||
|
||||
|
@ -13,15 +13,18 @@ The same goes for menus.
|
|||
The ui file for menus has interface, menu tags.
|
||||
The file starts and ends with interface tag.
|
||||
|
||||
~~~xml
|
||||
<interface>
|
||||
<menu id="menubar">
|
||||
</menu>
|
||||
</interface>
|
||||
~~~
|
||||
|
||||
`menu` tag corresponds to GMenu object.
|
||||
`id` attribute defines the name of the object.
|
||||
It will be refered by GtkBuilder.
|
||||
|
||||
~~~xml
|
||||
<submenu>
|
||||
<attribute name="label">File</attribute>
|
||||
<item>
|
||||
|
@ -29,6 +32,7 @@ It will be refered by GtkBuilder.
|
|||
<attribute name="action">win.new</attribute>
|
||||
</item>
|
||||
</submenu>
|
||||
~~~
|
||||
|
||||
`item` tag corresponds to item in GMenu which has the same structure as GMenuItem.
|
||||
The item above has a label attribute.
|
||||
|
@ -40,6 +44,7 @@ The GMenuItem has a link to GMenu.
|
|||
|
||||
The ui file above can be described as follows.
|
||||
|
||||
~~~xml
|
||||
<item>
|
||||
<attribute name="label">File</attribute>
|
||||
<link name="submenu">
|
||||
|
@ -49,6 +54,7 @@ The ui file above can be described as follows.
|
|||
</item>
|
||||
</link>
|
||||
</item>
|
||||
~~~
|
||||
|
||||
`link` tag expresses the link to submenu.
|
||||
And at the same time it also expresses the submenu itself.
|
||||
|
@ -63,6 +69,7 @@ Its name is `menu3`.
|
|||
|
||||
The following is the ui file of the menu in `menu3`.
|
||||
|
||||
~~~xml
|
||||
1 <?xml version="1.0" encoding="UTF-8"?>
|
||||
2 <interface>
|
||||
3 <menu id="menubar">
|
||||
|
@ -135,23 +142,28 @@ The following is the ui file of the menu in `menu3`.
|
|||
70 </submenu>
|
||||
71 </menu>
|
||||
72 </interface>
|
||||
~~~
|
||||
|
||||
The ui file is converted to the resource by the resouce compiler `glib-compile-resouces` with xml file below.
|
||||
|
||||
~~~xml
|
||||
1 <?xml version="1.0" encoding="UTF-8"?>
|
||||
2 <gresources>
|
||||
3 <gresource prefix="/com/github/ToshioCP/menu3">
|
||||
4 <file>menu3.ui</file>
|
||||
5 </gresource>
|
||||
6 </gresources>
|
||||
~~~
|
||||
|
||||
GtkBuilder builds menus from the resource.
|
||||
|
||||
~~~C
|
||||
GtkBuilder *builder = gtk_builder_new_from_resource ("/com/github/ToshioCP/menu3/menu3.ui");
|
||||
GMenuModel *menubar = G_MENU_MODEL (gtk_builder_get_object (builder, "menubar"));
|
||||
|
||||
gtk_application_set_menubar (GTK_APPLICATION (app), menubar);
|
||||
g_object_unref (builder);
|
||||
~~~
|
||||
|
||||
It is important that `builder` is unreferred after the GMenuModel `menubar` is set to the application.
|
||||
If you do it before setting, bad thing will happen -- your computer might freeze.
|
||||
|
@ -164,31 +176,42 @@ You can implement them easily with GActionEntry structure and `g_action_map_add_
|
|||
|
||||
GActionEntry contains action name, signal handlers, parameter and state.
|
||||
|
||||
~~~C
|
||||
typedef struct _GActionEntry GActionEntry;
|
||||
|
||||
struct _GActionEntry
|
||||
{
|
||||
const gchar *name; /* action name */
|
||||
void (* activate) (GSimpleAction *action, GVariant *parameter, gpointer user_data); /* activate handler */
|
||||
const gchar *parameter_type; /* the type of the parameter given as a single GVariant type string */
|
||||
const gchar *state; /* initial state given in GVariant text format */
|
||||
void (* change_state) (GSimpleAction *action, GVariant *value, gpointer user_data); /* change-state handler */
|
||||
/* action name */
|
||||
const gchar *name;
|
||||
/* activate handler */
|
||||
void (* activate) (GSimpleAction *action, GVariant *parameter, gpointer user_data);
|
||||
/* the type of the parameter given as a single GVariant type string */
|
||||
const gchar *parameter_type;
|
||||
/* initial state given in GVariant text format */
|
||||
const gchar *state;
|
||||
/* change-state handler */
|
||||
void (* change_state) (GSimpleAction *action, GVariant *value, gpointer user_data);
|
||||
/*< private >*/
|
||||
gsize padding[3];
|
||||
};
|
||||
|
||||
~~~
|
||||
For example, the actions in the previous section are:
|
||||
|
||||
~~~C
|
||||
{ "fullscreen", NULL, NULL, "false", fullscreen_changed }
|
||||
{ "color", color_activated, "s", "red", NULL }
|
||||
{ "quit", quit_activated, NULL, NULL, NULL },
|
||||
~~~
|
||||
|
||||
And `g_action_map_add_action_entries` does all the process instead of the functions you have needed.
|
||||
|
||||
~~~C
|
||||
const GActionEntry app_entries[] = {
|
||||
{ "quit", quit_activated, NULL, NULL, NULL }
|
||||
};
|
||||
g_action_map_add_action_entries (G_ACTION_MAP (app), app_entries, G_N_ELEMENTS (app_entries), app);
|
||||
g_action_map_add_action_entries (G_ACTION_MAP (app), app_entries,
|
||||
G_N_ELEMENTS (app_entries), app);
|
||||
~~~
|
||||
|
||||
The code above does:
|
||||
|
||||
|
@ -198,12 +221,14 @@ The code above does:
|
|||
|
||||
The same goes for the other actions.
|
||||
|
||||
~~~C
|
||||
const GActionEntry win_entries[] = {
|
||||
{ "fullscreen", NULL, NULL, "false", fullscreen_changed },
|
||||
{ "color", color_activated, "s", "red", NULL }
|
||||
};
|
||||
g_action_map_add_action_entries (G_ACTION_MAP (win), win_entries, G_N_ELEMENTS (win_entries), win);
|
||||
|
||||
g_action_map_add_action_entries (G_ACTION_MAP (win), win_entries,
|
||||
G_N_ELEMENTS (win_entries), win);
|
||||
~~~
|
||||
The code above does:
|
||||
|
||||
- Build a "fullscreen" action and "color" action.
|
||||
|
@ -217,6 +242,7 @@ The code above does:
|
|||
|
||||
The C source code of `menu3` and `meson.build` is as follows.
|
||||
|
||||
~~~C
|
||||
1 #include <gtk/gtk.h>
|
||||
2
|
||||
3 static void
|
||||
|
@ -323,9 +349,11 @@ The C source code of `menu3` and `meson.build` is as follows.
|
|||
104 return stat;
|
||||
105 }
|
||||
106
|
||||
~~~
|
||||
|
||||
meson.build
|
||||
|
||||
~~~meson
|
||||
1 project('menu3', 'c')
|
||||
2
|
||||
3 gtkdep = dependency('gtk4')
|
||||
|
@ -336,6 +364,7 @@ meson.build
|
|||
8 sourcefiles=files('menu3.c')
|
||||
9
|
||||
10 executable('menu3', sourcefiles, resources, dependencies: gtkdep)
|
||||
~~~
|
||||
|
||||
|
||||
Up: [Readme.md](../Readme.md), Prev: [Section 17](sec17.md)
|
||||
Up: [Readme.md](../Readme.md), Prev: [Section 17](sec17.md), Next: [Section 19](sec19.md)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
Up: [Readme.md](../Readme.md), Prev: [Section 18](sec18.md)
|
||||
Up: [Readme.md](../Readme.md), Prev: [Section 18](sec18.md), Next: [Section 20](sec20.md)
|
||||
|
||||
# GtkDrawingArea and Cairo
|
||||
|
||||
|
@ -46,6 +46,7 @@ This will be a destnation.
|
|||
|
||||
Here's a simple example code that draws a small square and save it as a png file.
|
||||
|
||||
~~~C
|
||||
1 #include <cairo.h>
|
||||
2
|
||||
3 int
|
||||
|
@ -77,6 +78,7 @@ Here's a simple example code that draws a small square and save it as a png file
|
|||
29
|
||||
30 return 0;
|
||||
31 }
|
||||
~~~
|
||||
|
||||
- 1: Include the header file of cairo.
|
||||
- 12: `cairo_image_surface_create` creates an image surface.
|
||||
|
@ -117,6 +119,7 @@ If you aren't familiar with cairo, it is strongly recommended to read the [tutor
|
|||
|
||||
The following is a very simple example.
|
||||
|
||||
~~~C
|
||||
1 #include <gtk/gtk.h>
|
||||
2
|
||||
3 static void
|
||||
|
@ -156,6 +159,7 @@ The following is a very simple example.
|
|||
37 return stat;
|
||||
38 }
|
||||
39
|
||||
~~~
|
||||
|
||||
The function `main` is almost same as before.
|
||||
The two functions `on_activate` and `draw_function` is important in this example.
|
||||
|
@ -194,4 +198,4 @@ The square always appears at the center of the window because the drawing functi
|
|||
![Square in the window](../image/da1.png)
|
||||
|
||||
|
||||
Up: [Readme.md](../Readme.md), Prev: [Section 18](sec18.md)
|
||||
Up: [Readme.md](../Readme.md), Prev: [Section 18](sec18.md), Next: [Section 20](sec20.md)
|
||||
|
|
|
@ -143,7 +143,8 @@ Modify `env.sh`.
|
|||
# compiler
|
||||
CPPFLAGS="-I$HOME/local/include"
|
||||
LDFLAGS="-L$HOME/local/lib"
|
||||
PKG_CONFIG_PATH="$HOME/local/lib/pkgconfig:$HOME/local/lib/x86_64-linux-gnu/pkgconfig:$HOME/local/share/pkgconfig"
|
||||
PKG_CONFIG_PATH="$HOME/local/lib/pkgconfig:$HOME/local/lib/x86_64-linux-gnu/pkgconfig:
|
||||
$HOME/local/share/pkgconfig"
|
||||
export CPPFLAGS LDFLAGS PKG_CONFIG_PATH
|
||||
# linker
|
||||
LD_LIBRARY_PATH="$HOME/local/lib/x86_64-linux-gnu/"
|
||||
|
|
10
gfm/sec20.md
10
gfm/sec20.md
|
@ -34,6 +34,7 @@ The image in the previous subsection gives us the structure of the widgets.
|
|||
Title bar, four buttons in the tool bar and two widgets textview and drawing area.
|
||||
The ui file is as follows.
|
||||
|
||||
~~~xml
|
||||
1 <interface>
|
||||
2 <object class="GtkApplicationWindow" id="win">
|
||||
3 <property name="title">color changer</property>
|
||||
|
@ -115,6 +116,7 @@ The ui file is as follows.
|
|||
79 </object>
|
||||
80 </interface>
|
||||
81
|
||||
~~~
|
||||
|
||||
- 9-53: This part describes the tool bar which has four buttons, `Run`, `Open`, `Save` and `Close`.
|
||||
This is similar to the toolbar of tfe text editor in [Section 8](sec8.md).
|
||||
|
@ -132,12 +134,14 @@ TfeTextView is a child of GtkScrolledWindow.
|
|||
The xml file for the resource compiler is almost same as before.
|
||||
Just substitute "color" for "tfe".
|
||||
|
||||
~~~xml
|
||||
1 <?xml version="1.0" encoding="UTF-8"?>
|
||||
2 <gresources>
|
||||
3 <gresource prefix="/com/github/ToshioCP/color">
|
||||
4 <file>color.ui</file>
|
||||
5 </gresource>
|
||||
6 </gresources>
|
||||
~~~
|
||||
|
||||
# Tfetextview.h, tfetextview.c and color.h
|
||||
|
||||
|
@ -152,9 +156,11 @@ The only difference is the header file in tfettextview.c.
|
|||
|
||||
Color.h just includes tfetextview.h.
|
||||
|
||||
~~~C
|
||||
1 #include <gtk/gtk.h>
|
||||
2
|
||||
3 #include "tfetextview.h"
|
||||
~~~
|
||||
|
||||
# Colorapplication.c
|
||||
|
||||
|
@ -169,6 +175,7 @@ Particularly, `Run` signal handler is the point in this program.
|
|||
|
||||
The following is `colorapplication.c`.
|
||||
|
||||
~~~C
|
||||
1 #include "color.h"
|
||||
2
|
||||
3 static GtkWidget *win;
|
||||
|
@ -291,6 +298,7 @@ The following is `colorapplication.c`.
|
|||
120 return stat;
|
||||
121 }
|
||||
122
|
||||
~~~
|
||||
|
||||
- 108-121: The function `main` is almost same as before but there are some differences.
|
||||
The application ID is "com.github.ToshioCP.color".
|
||||
|
@ -332,6 +340,7 @@ Alpha channel is used.
|
|||
This file is almost same as before.
|
||||
An argument "export_dynamic: true" is added to executable function.
|
||||
|
||||
~~~meson
|
||||
1 project('color', 'c')
|
||||
2
|
||||
3 gtkdep = dependency('gtk4')
|
||||
|
@ -342,6 +351,7 @@ An argument "export_dynamic: true" is added to executable function.
|
|||
8 sourcefiles=files('colorapplication.c', 'tfetextview.c')
|
||||
9
|
||||
10 executable('color', sourcefiles, resources, dependencies: gtkdep, export_dynamic: true)
|
||||
~~~
|
||||
|
||||
# Compile and execute it
|
||||
|
||||
|
|
12
gfm/sec3.md
12
gfm/sec3.md
|
@ -20,6 +20,7 @@ That's all.
|
|||
Very simple.
|
||||
The following is the C code representing the scenario above.
|
||||
|
||||
~~~C
|
||||
1 #include <gtk/gtk.h>
|
||||
2
|
||||
3 int
|
||||
|
@ -33,6 +34,7 @@ The following is the C code representing the scenario above.
|
|||
11 return stat;
|
||||
12 }
|
||||
13
|
||||
~~~
|
||||
|
||||
The first line says that this program includes the header files of the Gtk libraries.
|
||||
The function `main` above is a startup function in C language.
|
||||
|
@ -54,7 +56,9 @@ Let's run it.
|
|||
|
||||
$ ./a.out
|
||||
|
||||
(a.out:13533): GLib-GIO-WARNING **: 15:30:17.449: Your application does not implement g_application_activate() and has no handlers connected to the "activate" signal. It should do one of these.
|
||||
(a.out:13533): GLib-GIO-WARNING **: 15:30:17.449: Your application does not implement
|
||||
g_application_activate() and has no handlers connected to the "activate" signal.
|
||||
It should do one of these.
|
||||
$
|
||||
|
||||
Oh, just an error message.
|
||||
|
@ -97,6 +101,7 @@ Now we can solve the problem in `pr1.c`.
|
|||
We need to connect the activate signal to a handler.
|
||||
We use a function `g_signal_connect` which connects a signal to a handler.
|
||||
|
||||
~~~C
|
||||
1 #include <gtk/gtk.h>
|
||||
2
|
||||
3 static void
|
||||
|
@ -116,6 +121,7 @@ We use a function `g_signal_connect` which connects a signal to a handler.
|
|||
17 return stat;
|
||||
18 }
|
||||
19
|
||||
~~~
|
||||
|
||||
First, we define the handler `on_activate` which simply displays a message.
|
||||
In the function `main`, we add `g_signal_connect` before `g_application_run`.
|
||||
|
@ -185,6 +191,7 @@ Now rewrite the function `on_activate`.
|
|||
|
||||
#### Generate a GtkWindow
|
||||
|
||||
~~~C
|
||||
1 static void
|
||||
2 on_activate (GApplication *app, gpointer user_data) {
|
||||
3 GtkWidget *win;
|
||||
|
@ -193,6 +200,7 @@ Now rewrite the function `on_activate`.
|
|||
6 gtk_window_set_application (GTK_WINDOW (win), GTK_APPLICATION (app));
|
||||
7 gtk_widget_show (win);
|
||||
8 }
|
||||
~~~
|
||||
|
||||
Widget is an abstract concept that includes all the GUI interfaces such as windows, dialogs, buttons, multiline text, containers and so on.
|
||||
And GtkWidget is a base object from which all the GUI objects derive.
|
||||
|
@ -265,6 +273,7 @@ It is recommended to use it instead of GtkWindow when you use GtkApplication.
|
|||
|
||||
Now rewrite the program and use GtkAppliction Window.
|
||||
|
||||
~~~C
|
||||
1 static void
|
||||
2 on_activate (GApplication *app, gpointer user_data) {
|
||||
3 GtkWidget *win;
|
||||
|
@ -274,6 +283,7 @@ Now rewrite the program and use GtkAppliction Window.
|
|||
7 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300);
|
||||
8 gtk_widget_show (win);
|
||||
9 }
|
||||
~~~
|
||||
|
||||
When you generate GtkApplicationWindow, you need to give GtkApplication object as an argument.
|
||||
Then it automatically connect these two objects.
|
||||
|
|
|
@ -11,6 +11,7 @@ Now we go on to the next topic, widgets in the window.
|
|||
The simplest widget is GtkLabel.
|
||||
It is a widget with a string in it.
|
||||
|
||||
~~~C
|
||||
1 #include <gtk/gtk.h>
|
||||
2
|
||||
3 static void
|
||||
|
@ -40,6 +41,7 @@ It is a widget with a string in it.
|
|||
27 return stat;
|
||||
28 }
|
||||
29
|
||||
~~~
|
||||
|
||||
Save this program to a file `lb1.c`.
|
||||
Then compile and run it.
|
||||
|
@ -100,6 +102,7 @@ In this subsection, we will make a button with a label.
|
|||
When a button is clicked on, it emits a "clicked" signal.
|
||||
The following program shows how to catch the signal and do something.
|
||||
|
||||
~~~C
|
||||
1 #include <gtk/gtk.h>
|
||||
2
|
||||
3 static void
|
||||
|
@ -135,6 +138,7 @@ The following program shows how to catch the signal and do something.
|
|||
33 return stat;
|
||||
34 }
|
||||
35
|
||||
~~~
|
||||
|
||||
Look at the line 17 to 19.
|
||||
First, generate a GtkButton widget `btn` with a label "Click me".
|
||||
|
@ -157,6 +161,7 @@ However, using g_print is out of harmony with GTK which is a GUI library.
|
|||
So, we will change the handler.
|
||||
The following code is `lb3.c`.
|
||||
|
||||
~~~C
|
||||
1 static void
|
||||
2 click_cb (GtkButton *btn, gpointer user_data) {
|
||||
3 GtkWindow *win = GTK_WINDOW (user_data);
|
||||
|
@ -178,6 +183,7 @@ The following code is `lb3.c`.
|
|||
19
|
||||
20 gtk_widget_show (win);
|
||||
21 }
|
||||
~~~
|
||||
|
||||
And the difference between `lb2.c` and `lb3.c` is as follows.
|
||||
|
||||
|
@ -237,6 +243,7 @@ After this, the Widgets are connected as following diagram.
|
|||
|
||||
Now, code it.
|
||||
|
||||
~~~C
|
||||
1 #include <gtk/gtk.h>
|
||||
2
|
||||
3 static void
|
||||
|
@ -294,6 +301,7 @@ Now, code it.
|
|||
55 g_object_unref (app);
|
||||
56 return stat;
|
||||
57 }
|
||||
~~~
|
||||
|
||||
Look at the function `on_activate`.
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ GtkTextview is a widget for multiline text editing.
|
|||
GtkTextBuffer is a text buffer which is connected to GtkTextView.
|
||||
See a sample program `tfv1.c` below.
|
||||
|
||||
~~~C
|
||||
1 #include <gtk/gtk.h>
|
||||
2
|
||||
3 static void
|
||||
|
@ -56,6 +57,7 @@ See a sample program `tfv1.c` below.
|
|||
44 return stat;
|
||||
45 }
|
||||
46
|
||||
~~~
|
||||
|
||||
Look at line 25.
|
||||
GtkTextView is generated and its pointer is assigned to `tv`.
|
||||
|
@ -108,6 +110,7 @@ The difference between these two files is very little.
|
|||
|
||||
Though you can modify the source file by this diff output, It's good for you to show `tfv2.c`.
|
||||
|
||||
~~~C
|
||||
1 #include <gtk/gtk.h>
|
||||
2
|
||||
3 static void
|
||||
|
@ -158,6 +161,7 @@ Though you can modify the source file by this diff output, It's good for you to
|
|||
48 return stat;
|
||||
49 }
|
||||
50
|
||||
~~~
|
||||
|
||||
Now compile and run it.
|
||||
This time the window doesn't extend even if you type a lot of characters.
|
||||
|
|
|
@ -89,6 +89,7 @@ It works as follows.
|
|||
|
||||
The program is as follows.
|
||||
|
||||
~~~C
|
||||
1 #include <gtk/gtk.h>
|
||||
2
|
||||
3 static void
|
||||
|
@ -145,6 +146,7 @@ The program is as follows.
|
|||
54 return stat;
|
||||
55 }
|
||||
56
|
||||
~~~
|
||||
|
||||
Save it as `tfv3.c`.
|
||||
Then compile and run it.
|
||||
|
@ -211,6 +213,7 @@ It is shown in the right screenshot.
|
|||
GtkNotebook widget is between GtkApplicationWindow and GtkScrolledWindow.
|
||||
Now I want to show you the program `tfv4.c`.
|
||||
|
||||
~~~C
|
||||
1 #include <gtk/gtk.h>
|
||||
2
|
||||
3 static void
|
||||
|
@ -282,6 +285,7 @@ Now I want to show you the program `tfv4.c`.
|
|||
69 return stat;
|
||||
70 }
|
||||
71
|
||||
~~~
|
||||
|
||||
Most of the change is in the function `on_open`.
|
||||
The numbers at the left of the following items are line numbers in the source code.
|
||||
|
|
|
@ -57,6 +57,7 @@ So, I will just show you the way how to write the code and avoid the theoretical
|
|||
Let's define TfeTextView object which is a child object of GtkTextView.
|
||||
First, look at the program below.
|
||||
|
||||
~~~C
|
||||
#define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (TfeTextView, tfe_text_view, TFE, TEXT_VIEW, GtkTextView)
|
||||
|
||||
|
@ -90,6 +91,7 @@ First, look at the program below.
|
|||
tfe_text_view_new (void) {
|
||||
return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL));
|
||||
}
|
||||
~~~
|
||||
|
||||
If you are curious about the background theory of this program, It's very good for you.
|
||||
Because to know the theory is very important for you to program GTK applications.
|
||||
|
@ -157,6 +159,7 @@ The argument win is GtkApplicationWindow, in which the signal "close-request" is
|
|||
`G_CALLBACK` cast is necessary for the handler.
|
||||
The program of before\_close is as follows.
|
||||
|
||||
~~~C
|
||||
1 static gboolean
|
||||
2 before_close (GtkWindow *win, GtkWidget *nb) {
|
||||
3 GtkWidget *scr;
|
||||
|
@ -182,6 +185,7 @@ The program of before\_close is as follows.
|
|||
23 }
|
||||
24 return FALSE;
|
||||
25 }
|
||||
~~~
|
||||
|
||||
The numbers on the left of items are line numbers in the source code.
|
||||
|
||||
|
@ -195,6 +199,7 @@ The numbers on the left of items are line numbers in the source code.
|
|||
|
||||
Now I will show you all the source code of `tfe1`.c.
|
||||
|
||||
~~~C
|
||||
1 #include <gtk/gtk.h>
|
||||
2
|
||||
3 /* Define TfeTextView Widget which is the child object of GtkTextView */
|
||||
|
@ -331,6 +336,7 @@ Now I will show you all the source code of `tfe1`.c.
|
|||
134 return stat;
|
||||
135 }
|
||||
136
|
||||
~~~
|
||||
|
||||
- 102: set the pointer to GFile into TfeTextView.
|
||||
`files[i]` is a pointer to GFile structure.
|
||||
|
|
16
gfm/sec8.md
16
gfm/sec8.md
|
@ -1,6 +1,6 @@
|
|||
Up: [Readme.md](../Readme.md), Prev: [Section 7](sec7.md), Next: [Section 9](sec9.md)
|
||||
|
||||
# Ui file and GtkBuiler
|
||||
# Ui file and GtkBuilder
|
||||
|
||||
## New, open and save button
|
||||
|
||||
|
@ -16,6 +16,7 @@ Signals and handlers will be explained later.
|
|||
The screenshot above shows the layout.
|
||||
The function `on_open` in the source code `tfe2.c` is as follows.
|
||||
|
||||
~~~C
|
||||
1 static void
|
||||
2 on_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer user_data) {
|
||||
3 GtkWidget *win;
|
||||
|
@ -102,6 +103,7 @@ The function `on_open` in the source code `tfe2.c` is as follows.
|
|||
84 } else
|
||||
85 gtk_window_destroy (GTK_WINDOW (win));
|
||||
86 }
|
||||
~~~
|
||||
|
||||
The point is how to build the window.
|
||||
|
||||
|
@ -135,6 +137,7 @@ It reduces the cumbersom work.
|
|||
|
||||
First, let's look at the ui file `tfe3.ui` that defines a structure of the widgets.
|
||||
|
||||
~~~xml
|
||||
1 <interface>
|
||||
2 <object class="GtkApplicationWindow" id="win">
|
||||
3 <property name="title">file editor</property>
|
||||
|
@ -194,6 +197,7 @@ First, let's look at the ui file `tfe3.ui` that defines a structure of the widge
|
|||
57 </object>
|
||||
58 </interface>
|
||||
59
|
||||
~~~
|
||||
|
||||
This is coded with XML structure.
|
||||
Constructs begin with `<` and end with `>` is called tags.
|
||||
|
@ -216,12 +220,14 @@ Those two decribe the same structure of widgets.
|
|||
|
||||
GtkBuilder builds widgets based on the ui file.
|
||||
|
||||
~~~C
|
||||
GtkBuilder *build;
|
||||
|
||||
build = gtk_builder_new_from_file ("tfe3.ui");
|
||||
win = GTK_WIDGET (gtk_builder_get_object (build, "win"));
|
||||
gtk_window_set_application (GTK_WINDOW (win), GTK_APPLICATION (app));
|
||||
nb = GTK_WIDGET (gtk_builder_get_object (build, "nb"));
|
||||
~~~
|
||||
|
||||
The function `gtk_builder_new_from_file` reads the file given as an argument, build the widgets, generate GtkBuilder object and set pointers to the widgets in it.
|
||||
The function `gtk_builder_get_object (build, "win")` returns the pointer to the widget `win`, which is the id in the ui file.
|
||||
|
@ -295,6 +301,7 @@ Using ui file not only shortens C source files, but also makes the widgets' stru
|
|||
Now I'll show you the C source code `tfe3.c`.
|
||||
Only functions `on_open` are shown as follows.
|
||||
|
||||
~~~C
|
||||
1 static void
|
||||
2 on_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer user_data) {
|
||||
3 GtkWidget *win;
|
||||
|
@ -343,6 +350,7 @@ Only functions `on_open` are shown as follows.
|
|||
46 } else
|
||||
47 gtk_window_destroy (GTK_WINDOW (win));
|
||||
48 }
|
||||
~~~
|
||||
|
||||
The source code of `tfe3.c` is stored in [src/tfe](https://github.com/ToshioCP/Gtk4-tutorial/tree/main/src/tfe) directory.
|
||||
If you want to see it, click the link above.
|
||||
|
@ -353,6 +361,7 @@ In the same way, you can get the source files below in the directory [src/tfe](h
|
|||
GtkBuilder can build widgets using string.
|
||||
Use the function gtk\_builder\_new\_from\_string instead of gtk\_builder\_new\_from\_file.
|
||||
|
||||
~~~C
|
||||
char *uistring;
|
||||
|
||||
uistring =
|
||||
|
@ -369,6 +378,7 @@ Use the function gtk\_builder\_new\_from\_string instead of gtk\_builder\_new\_f
|
|||
"</interface>";
|
||||
|
||||
build = gtk_builder_new_from_stringfile (uistring);
|
||||
~~~
|
||||
|
||||
This method has an advantage and disadvantage.
|
||||
The advantage is that the ui string is written in the source code.
|
||||
|
@ -390,12 +400,14 @@ And after compilation, it bundles them up into one Gresource object.
|
|||
An xml file is necessary for the resource compiler `glib-compile-resources`.
|
||||
It describes resource files.
|
||||
|
||||
~~~xml
|
||||
1 <?xml version="1.0" encoding="UTF-8"?>
|
||||
2 <gresources>
|
||||
3 <gresource prefix="/com/github/ToshioCP/tfe3">
|
||||
4 <file>tfe3.ui</file>
|
||||
5 </gresource>
|
||||
6 </gresources>
|
||||
~~~
|
||||
|
||||
- 2: gresources tag can include mulitple gresources (gresource tags).
|
||||
However, this xml has only one gresource.
|
||||
|
@ -441,12 +453,14 @@ Now run the compiler.
|
|||
Then a C source file `resources.c` is generated.
|
||||
Modify tfe3.c and save it as tfe3_r.c
|
||||
|
||||
~~~C
|
||||
#include "resources.c"
|
||||
... ... ...
|
||||
... ... ...
|
||||
build = gtk_builder_new_from_resource ("/com/github/ToshioCP/tfe3/tfe3.ui");
|
||||
... ... ...
|
||||
... ... ...
|
||||
~~~
|
||||
|
||||
Then, compile and run it.
|
||||
The window appears and it is the same as the screenshot at the beginning of this page.
|
||||
|
|
16
gfm/sec9.md
16
gfm/sec9.md
|
@ -42,6 +42,7 @@ All the source files are listed below.
|
|||
|
||||
`tfetextview.h`
|
||||
|
||||
~~~C
|
||||
1 #include <gtk/gtk.h>
|
||||
2
|
||||
3 #define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type ()
|
||||
|
@ -56,9 +57,11 @@ All the source files are listed below.
|
|||
12 GtkWidget *
|
||||
13 tfe_text_view_new (void);
|
||||
14
|
||||
~~~
|
||||
|
||||
`tfetextview.c`
|
||||
|
||||
~~~C
|
||||
1 #include <gtk/gtk.h>
|
||||
2 #include "tfetextview.h"
|
||||
3
|
||||
|
@ -93,9 +96,11 @@ All the source files are listed below.
|
|||
32 return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL));
|
||||
33 }
|
||||
34
|
||||
~~~
|
||||
|
||||
`tfe.c`
|
||||
|
||||
~~~C
|
||||
1 #include <gtk/gtk.h>
|
||||
2 #include "tfetextview.h"
|
||||
3
|
||||
|
@ -166,9 +171,11 @@ All the source files are listed below.
|
|||
68 return stat;
|
||||
69 }
|
||||
70
|
||||
~~~
|
||||
|
||||
`tfe.ui`
|
||||
|
||||
~~~xml
|
||||
1 <interface>
|
||||
2 <object class="GtkApplicationWindow" id="win">
|
||||
3 <property name="title">file editor</property>
|
||||
|
@ -228,15 +235,18 @@ All the source files are listed below.
|
|||
57 </object>
|
||||
58 </interface>
|
||||
59
|
||||
~~~
|
||||
|
||||
`tfe.gresource.xml`
|
||||
|
||||
~~~xml
|
||||
1 <?xml version="1.0" encoding="UTF-8"?>
|
||||
2 <gresources>
|
||||
3 <gresource prefix="/com/github/ToshioCP/tfe3">
|
||||
4 <file>tfe.ui</file>
|
||||
5 </gresource>
|
||||
6 </gresources>
|
||||
~~~
|
||||
|
||||
## Make
|
||||
|
||||
|
@ -278,6 +288,7 @@ If the modification time of `sample.c` is older then the generation of `sample.o
|
|||
|
||||
The Makefile for `tfe` is as follows.
|
||||
|
||||
~~~makefile
|
||||
1 all: tfe
|
||||
2
|
||||
3 tfe: tfe.o tfetextview.o resources.o
|
||||
|
@ -297,6 +308,7 @@ The Makefile for `tfe` is as follows.
|
|||
17
|
||||
18 clean:
|
||||
19 rm -f tfe tfe.o tfetextview.o resources.o resources.c
|
||||
~~~
|
||||
|
||||
You only need to type `make`.
|
||||
|
||||
|
@ -324,6 +336,7 @@ However, Ruby is really sophisticated and recommendable script language.
|
|||
|
||||
Rake has task and file task, which is similar to target, prerequisite and recipe in make.
|
||||
|
||||
~~~ruby
|
||||
1 require 'rake/clean'
|
||||
2
|
||||
3 targetfile = "tfe"
|
||||
|
@ -349,6 +362,7 @@ Rake has task and file task, which is similar to target, prerequisite and recipe
|
|||
23 file rscfile => ["tfe.gresource.xml", "tfe.ui"] do |t|
|
||||
24 sh "glib-compile-resources #{t.prerequisites[0]} --target=#{t.name} --generate-source"
|
||||
25 end
|
||||
~~~
|
||||
|
||||
What `Rakefile` describes is almost same as `Makefile` in the previous subsection.
|
||||
|
||||
|
@ -380,6 +394,7 @@ Many developers are using meson and ninja now.
|
|||
|
||||
To use meson, you first need to write `meson.build` file.
|
||||
|
||||
~~~meson
|
||||
1 project('tfe', 'c')
|
||||
2
|
||||
3 gtkdep = dependency('gtk4')
|
||||
|
@ -390,6 +405,7 @@ To use meson, you first need to write `meson.build` file.
|
|||
8 sourcefiles=files('tfe.c', 'tfetextview.c')
|
||||
9
|
||||
10 executable('tfe', sourcefiles, resources, dependencies: gtkdep)
|
||||
~~~
|
||||
|
||||
- 1: The function `project` defines things about the project.
|
||||
The first parameter is the name of the project and the second is the programing language.
|
||||
|
|
|
@ -45,16 +45,28 @@ require 'pathname'
|
|||
|
||||
# ---- Folding verbatim lines ----
|
||||
# When C sourcefiles or subshell output are included, the lines are folded to fit in 'width'.
|
||||
# Before they are folded, four space characters are prepended to the line.
|
||||
# Therefore, 'width' must be at least five.
|
||||
# Width must be positive integer.
|
||||
# Otherwise the lines are not folded.
|
||||
|
||||
# This script uses "fenced code blocks" for verbatim lines.
|
||||
# It is available in GFM and pandoc's markdown but not in original markdown.
|
||||
# Two characters backtick (`) and tilde (~) are possible for fences.
|
||||
# This script uses tilde because info string cannot contain any backticks for the backtick code fence.
|
||||
# Info string follows opening fence and it is usually a language name.
|
||||
# ~~~C
|
||||
# int main (int argc, char **argv) {
|
||||
# ........
|
||||
# ~~~
|
||||
# Then the contents are highlighted based on C language syntax.
|
||||
# This script find the language by the suffix of the file name.
|
||||
# .c => C, .h => C, .rb => ruby, Rakefile, => ruby, .xml => xml, .ui => xml, .y => bison, .lex => lex, .build => meson, .md => markdown
|
||||
# Makefile => makefile
|
||||
|
||||
def src2md srcmd, md, width
|
||||
src_buf = IO.readlines srcmd
|
||||
src_dir = File.dirname srcmd
|
||||
md_dir = File.dirname md
|
||||
# type is 'the type of the target', which is one of "markdown", "html" and "latex".
|
||||
type = md_dir == "." ? "markdown" : md_dir
|
||||
type = File.basename md_dir # gfm, html or latex
|
||||
|
||||
md_buf = []
|
||||
comflag = false
|
||||
|
@ -65,14 +77,16 @@ def src2md srcmd, md, width
|
|||
else
|
||||
md_buf << " $ "+line
|
||||
`cd #{src_dir}; #{line.chomp}`.each_line do |l|
|
||||
md_buf << l.gsub(/^/," ")
|
||||
fold(l, width).each_line do |l2|
|
||||
md_buf << l2.gsub(/^/," ")
|
||||
end
|
||||
end
|
||||
end
|
||||
elsif line == "$$$\n"
|
||||
comflag = true
|
||||
elsif line =~ /^@@@\s+(\S+)\s*(.*)\s*$/
|
||||
elsif line =~ /^@@@\s+(\S+)\s*(.*)$/
|
||||
c_file = $1
|
||||
c_functions = $2.split(" ")
|
||||
c_functions = $2.strip.split(" ")
|
||||
if c_file =~ /^\// # absolute path
|
||||
c_file_buf = IO.readlines(c_file)
|
||||
else #relative path
|
||||
|
@ -108,38 +122,33 @@ def src2md srcmd, md, width
|
|||
end
|
||||
end
|
||||
end
|
||||
md_buf << "~~~#{lang(c_file)}\n"
|
||||
ln_width = tmp_buf.size.to_s.length
|
||||
n = 1
|
||||
tmp_buf.each do |l|
|
||||
l = sprintf("%#{ln_width}d %s", n, l)
|
||||
md_buf << l
|
||||
fold(l, width).each_line do |l2|
|
||||
md_buf << l2
|
||||
end
|
||||
n += 1
|
||||
end
|
||||
md_buf << "~~~\n"
|
||||
else
|
||||
md_buf << change_rel_link(line, src_dir, File.dirname(md))
|
||||
end
|
||||
end
|
||||
tmp_buf = md_buf
|
||||
md_buf = []
|
||||
tmp_buf.each do |line|
|
||||
if line =~ /^ / && width.instance_of?(Integer) && width >= 5
|
||||
indent = line =~ /^( *\d+ +)/ ? " "*$1.length : " "
|
||||
while line.instance_of?(String) && line.length > width
|
||||
md_buf << line[0, width]+"\n"
|
||||
line = line[width .. -1].gsub(/^/,indent)
|
||||
end
|
||||
elsif type == "latex"
|
||||
line.gsub!(/(^|[^!])\[([^\]]*)\]\((?~http)\)/,"\\1\\2") # remove link
|
||||
else # type == "markdown" or "html"
|
||||
line.gsub!(/(!\[[^\]]*\]\([^\)]*\)) *{width *= *\d*(|\.\d*)cm *height *= *\d*(|\.\d*)cm}/,"\\1") # remove size option from link to image files.
|
||||
line = change_rel_link(line, src_dir, md_dir)
|
||||
if type == "latex" # remove relative link
|
||||
line.gsub!(/(^|[^!])\[([^\]]*)\]\((?~http)\)/,"\\1\\2")
|
||||
else # type == "gfm" or "html", then remove size option from link to image files.
|
||||
line.gsub!(/(!\[[^\]]*\]\([^\)]*\)) *{width *= *\d*(|\.\d*)cm *height *= *\d*(|\.\d*)cm}/,"\\1")
|
||||
end
|
||||
md_buf << line
|
||||
end
|
||||
end
|
||||
IO.write(md,md_buf.join)
|
||||
end
|
||||
|
||||
def change_rel_link line, src_dir, basedir
|
||||
p_basedir = Pathname.new basedir
|
||||
# Change the base of relative links from org_dir to new_dir
|
||||
def change_rel_link line, org_dir, new_dir
|
||||
p_new_dir = Pathname.new new_dir
|
||||
left = ""
|
||||
right = line
|
||||
while right =~ /(!?\[[^\]]*\])\(([^\)]*)\)/
|
||||
|
@ -150,11 +159,42 @@ def change_rel_link line, src_dir, basedir
|
|||
if name =~ /\[(S|s)ection (\d+)\]/
|
||||
link = "sec#{$2}.md"
|
||||
elsif ! (link =~ /^(http|\/)/)
|
||||
p_link = Pathname.new "#{src_dir}/#{link}"
|
||||
link = p_link.relative_path_from(p_basedir).to_s
|
||||
p_link = Pathname.new "#{org_dir}/#{link}"
|
||||
link = p_link.relative_path_from(p_new_dir).to_s
|
||||
end
|
||||
left += "#{name}(#{link})"
|
||||
end
|
||||
left + right
|
||||
end
|
||||
|
||||
def fold line, width
|
||||
if width <=0
|
||||
return line
|
||||
end
|
||||
tmp = []
|
||||
while line.length > width
|
||||
tmp << line[0, width]+"\n"
|
||||
line = line[width .. -1]
|
||||
end
|
||||
tmp << line
|
||||
tmp.join
|
||||
end
|
||||
|
||||
def lang file
|
||||
tbl = {".c" => "C", ".h" => "C", ".rb" => "ruby", ".xml" => "xml", ".ui" => "xml",
|
||||
".y" => "bison", ".lex" => "lex", ".build" => "meson", ".md" => "markdown" }
|
||||
name = File.basename file
|
||||
if name == "Makefile"
|
||||
return "makefile"
|
||||
elsif name == "Rakefile"
|
||||
return "ruby"
|
||||
else
|
||||
suffix = File.extname name
|
||||
tbl.each do |key, val|
|
||||
if suffix == key
|
||||
return val
|
||||
end
|
||||
end
|
||||
end
|
||||
return ""
|
||||
end
|
||||
|
|
|
@ -19,7 +19,8 @@ The signal handler will be described after the explanation of this code.
|
|||
static void
|
||||
on_activate (GApplication *app, gpointer user_data) {
|
||||
... ... ...
|
||||
GSimpleAction *act_fullscreen = g_simple_action_new_stateful ("fullscreen", NULL, g_variant_new_boolean (FALSE));
|
||||
GSimpleAction *act_fullscreen = g_simple_action_new_stateful ("fullscreen",
|
||||
NULL, g_variant_new_boolean (FALSE));
|
||||
GMenuItem *menu_item_fullscreen = g_menu_item_new ("Full Screen", "win.fullscreen");
|
||||
g_signal_connect (act_fullscreen, "change-state", G_CALLBACK (fullscreen_changed), win);
|
||||
... ... ...
|
||||
|
@ -114,7 +115,8 @@ Those colors are given to the signal handler as a parameter.
|
|||
static void
|
||||
on_activate (GApplication *app, gpointer user_data) {
|
||||
... ... ...
|
||||
GSimpleAction *act_color = g_simple_action_new_stateful ("color", g_variant_type_new("s"), g_variant_new_string ("red"));
|
||||
GSimpleAction *act_color = g_simple_action_new_stateful ("color",
|
||||
g_variant_type_new("s"), g_variant_new_string ("red"));
|
||||
GMenuItem *menu_item_red = g_menu_item_new ("Red", "win.color::red");
|
||||
GMenuItem *menu_item_green = g_menu_item_new ("Green", "win.color::green");
|
||||
GMenuItem *menu_item_blue = g_menu_item_new ("Blue", "win.color::blue");
|
||||
|
@ -148,7 +150,8 @@ The following is the "activate" signal handler.
|
|||
|
||||
static void
|
||||
color_activated(GSimpleAction *action, GVariant *parameter, gpointer win) {
|
||||
gchar *color = g_strdup_printf ("label#lb {background-color: %s;}", g_variant_get_string (parameter, NULL));
|
||||
gchar *color = g_strdup_printf ("label#lb {background-color: %s;}",
|
||||
g_variant_get_string (parameter, NULL));
|
||||
gtk_css_provider_load_from_data (provider, color, -1);
|
||||
g_free (color);
|
||||
g_action_change_state (G_ACTION (action), parameter);
|
||||
|
|
|
@ -11,15 +11,18 @@ The same goes for menus.
|
|||
The ui file for menus has interface, menu tags.
|
||||
The file starts and ends with interface tag.
|
||||
|
||||
~~~xml
|
||||
<interface>
|
||||
<menu id="menubar">
|
||||
</menu>
|
||||
</interface>
|
||||
~~~
|
||||
|
||||
`menu` tag corresponds to GMenu object.
|
||||
`id` attribute defines the name of the object.
|
||||
It will be refered by GtkBuilder.
|
||||
|
||||
~~~xml
|
||||
<submenu>
|
||||
<attribute name="label">File</attribute>
|
||||
<item>
|
||||
|
@ -27,6 +30,7 @@ It will be refered by GtkBuilder.
|
|||
<attribute name="action">win.new</attribute>
|
||||
</item>
|
||||
</submenu>
|
||||
~~~
|
||||
|
||||
`item` tag corresponds to item in GMenu which has the same structure as GMenuItem.
|
||||
The item above has a label attribute.
|
||||
|
@ -38,6 +42,7 @@ The GMenuItem has a link to GMenu.
|
|||
|
||||
The ui file above can be described as follows.
|
||||
|
||||
~~~xml
|
||||
<item>
|
||||
<attribute name="label">File</attribute>
|
||||
<link name="submenu">
|
||||
|
@ -47,6 +52,7 @@ The ui file above can be described as follows.
|
|||
</item>
|
||||
</link>
|
||||
</item>
|
||||
~~~
|
||||
|
||||
`link` tag expresses the link to submenu.
|
||||
And at the same time it also expresses the submenu itself.
|
||||
|
@ -69,11 +75,13 @@ The ui file is converted to the resource by the resouce compiler `glib-compile-r
|
|||
|
||||
GtkBuilder builds menus from the resource.
|
||||
|
||||
~~~C
|
||||
GtkBuilder *builder = gtk_builder_new_from_resource ("/com/github/ToshioCP/menu3/menu3.ui");
|
||||
GMenuModel *menubar = G_MENU_MODEL (gtk_builder_get_object (builder, "menubar"));
|
||||
|
||||
gtk_application_set_menubar (GTK_APPLICATION (app), menubar);
|
||||
g_object_unref (builder);
|
||||
~~~
|
||||
|
||||
It is important that `builder` is unreferred after the GMenuModel `menubar` is set to the application.
|
||||
If you do it before setting, bad thing will happen -- your computer might freeze.
|
||||
|
@ -86,31 +94,42 @@ You can implement them easily with GActionEntry structure and `g_action_map_add_
|
|||
|
||||
GActionEntry contains action name, signal handlers, parameter and state.
|
||||
|
||||
~~~C
|
||||
typedef struct _GActionEntry GActionEntry;
|
||||
|
||||
struct _GActionEntry
|
||||
{
|
||||
const gchar *name; /* action name */
|
||||
void (* activate) (GSimpleAction *action, GVariant *parameter, gpointer user_data); /* activate handler */
|
||||
const gchar *parameter_type; /* the type of the parameter given as a single GVariant type string */
|
||||
const gchar *state; /* initial state given in GVariant text format */
|
||||
void (* change_state) (GSimpleAction *action, GVariant *value, gpointer user_data); /* change-state handler */
|
||||
/* action name */
|
||||
const gchar *name;
|
||||
/* activate handler */
|
||||
void (* activate) (GSimpleAction *action, GVariant *parameter, gpointer user_data);
|
||||
/* the type of the parameter given as a single GVariant type string */
|
||||
const gchar *parameter_type;
|
||||
/* initial state given in GVariant text format */
|
||||
const gchar *state;
|
||||
/* change-state handler */
|
||||
void (* change_state) (GSimpleAction *action, GVariant *value, gpointer user_data);
|
||||
/*< private >*/
|
||||
gsize padding[3];
|
||||
};
|
||||
|
||||
~~~
|
||||
For example, the actions in the previous section are:
|
||||
|
||||
~~~C
|
||||
{ "fullscreen", NULL, NULL, "false", fullscreen_changed }
|
||||
{ "color", color_activated, "s", "red", NULL }
|
||||
{ "quit", quit_activated, NULL, NULL, NULL },
|
||||
~~~
|
||||
|
||||
And `g_action_map_add_action_entries` does all the process instead of the functions you have needed.
|
||||
|
||||
~~~C
|
||||
const GActionEntry app_entries[] = {
|
||||
{ "quit", quit_activated, NULL, NULL, NULL }
|
||||
};
|
||||
g_action_map_add_action_entries (G_ACTION_MAP (app), app_entries, G_N_ELEMENTS (app_entries), app);
|
||||
g_action_map_add_action_entries (G_ACTION_MAP (app), app_entries,
|
||||
G_N_ELEMENTS (app_entries), app);
|
||||
~~~
|
||||
|
||||
The code above does:
|
||||
|
||||
|
@ -120,12 +139,14 @@ The code above does:
|
|||
|
||||
The same goes for the other actions.
|
||||
|
||||
~~~C
|
||||
const GActionEntry win_entries[] = {
|
||||
{ "fullscreen", NULL, NULL, "false", fullscreen_changed },
|
||||
{ "color", color_activated, "s", "red", NULL }
|
||||
};
|
||||
g_action_map_add_action_entries (G_ACTION_MAP (win), win_entries, G_N_ELEMENTS (win_entries), win);
|
||||
|
||||
g_action_map_add_action_entries (G_ACTION_MAP (win), win_entries,
|
||||
G_N_ELEMENTS (win_entries), win);
|
||||
~~~
|
||||
The code above does:
|
||||
|
||||
- Build a "fullscreen" action and "color" action.
|
||||
|
|
|
@ -30,7 +30,7 @@ In this section, we don't use it.
|
|||
That means we only use identity transformation.
|
||||
Therefore, the coordinate in source and mask is the same as the coordinate in destination.
|
||||
|
||||
![Stroke a rectangle](../image/cairo.png)
|
||||
![Stroke a rectangle](../image/cairo.png){width=9.0cm height=6.0cm}
|
||||
|
||||
The instruction is as follows:
|
||||
|
||||
|
|
|
@ -141,7 +141,8 @@ Modify `env.sh`.
|
|||
# compiler
|
||||
CPPFLAGS="-I$HOME/local/include"
|
||||
LDFLAGS="-L$HOME/local/lib"
|
||||
PKG_CONFIG_PATH="$HOME/local/lib/pkgconfig:$HOME/local/lib/x86_64-linux-gnu/pkgconfig:$HOME/local/share/pkgconfig"
|
||||
PKG_CONFIG_PATH="$HOME/local/lib/pkgconfig:$HOME/local/lib/x86_64-linux-gnu/pkgconfig:
|
||||
$HOME/local/share/pkgconfig"
|
||||
export CPPFLAGS LDFLAGS PKG_CONFIG_PATH
|
||||
# linker
|
||||
LD_LIBRARY_PATH="$HOME/local/lib/x86_64-linux-gnu/"
|
||||
|
|
|
@ -4,7 +4,7 @@ Now, we will make a new application which has GtkDrawingArea and TfeTextView in
|
|||
Its name is "color".
|
||||
If you write a color in TfeTextView and click on the `run` button, then the color of GtkDrawingArea changes to the color given by you.
|
||||
|
||||
![color](../image/color.png)
|
||||
![color](../image/color.png){width=7.0cm height=5.13cm}
|
||||
|
||||
The following colors are available.
|
||||
|
||||
|
|
|
@ -40,7 +40,9 @@ Let's run it.
|
|||
|
||||
$ ./a.out
|
||||
|
||||
(a.out:13533): GLib-GIO-WARNING **: 15:30:17.449: Your application does not implement g_application_activate() and has no handlers connected to the "activate" signal. It should do one of these.
|
||||
(a.out:13533): GLib-GIO-WARNING **: 15:30:17.449: Your application does not implement
|
||||
g_application_activate() and has no handlers connected to the "activate" signal.
|
||||
It should do one of these.
|
||||
$
|
||||
|
||||
Oh, just an error message.
|
||||
|
|
|
@ -55,6 +55,7 @@ So, I will just show you the way how to write the code and avoid the theoretical
|
|||
Let's define TfeTextView object which is a child object of GtkTextView.
|
||||
First, look at the program below.
|
||||
|
||||
~~~C
|
||||
#define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (TfeTextView, tfe_text_view, TFE, TEXT_VIEW, GtkTextView)
|
||||
|
||||
|
@ -88,6 +89,7 @@ First, look at the program below.
|
|||
tfe_text_view_new (void) {
|
||||
return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL));
|
||||
}
|
||||
~~~
|
||||
|
||||
If you are curious about the background theory of this program, It's very good for you.
|
||||
Because to know the theory is very important for you to program GTK applications.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Ui file and GtkBuiler
|
||||
# Ui file and GtkBuilder
|
||||
|
||||
## New, open and save button
|
||||
|
||||
|
@ -71,12 +71,14 @@ Those two decribe the same structure of widgets.
|
|||
|
||||
GtkBuilder builds widgets based on the ui file.
|
||||
|
||||
~~~C
|
||||
GtkBuilder *build;
|
||||
|
||||
build = gtk_builder_new_from_file ("tfe3.ui");
|
||||
win = GTK_WIDGET (gtk_builder_get_object (build, "win"));
|
||||
gtk_window_set_application (GTK_WINDOW (win), GTK_APPLICATION (app));
|
||||
nb = GTK_WIDGET (gtk_builder_get_object (build, "nb"));
|
||||
~~~
|
||||
|
||||
The function `gtk_builder_new_from_file` reads the file given as an argument, build the widgets, generate GtkBuilder object and set pointers to the widgets in it.
|
||||
The function `gtk_builder_get_object (build, "win")` returns the pointer to the widget `win`, which is the id in the ui file.
|
||||
|
@ -106,6 +108,7 @@ In the same way, you can get the source files below in the directory [src/tfe](h
|
|||
GtkBuilder can build widgets using string.
|
||||
Use the function gtk\_builder\_new\_from\_string instead of gtk\_builder\_new\_from\_file.
|
||||
|
||||
~~~C
|
||||
char *uistring;
|
||||
|
||||
uistring =
|
||||
|
@ -122,6 +125,7 @@ Use the function gtk\_builder\_new\_from\_string instead of gtk\_builder\_new\_f
|
|||
"</interface>";
|
||||
|
||||
build = gtk_builder_new_from_stringfile (uistring);
|
||||
~~~
|
||||
|
||||
This method has an advantage and disadvantage.
|
||||
The advantage is that the ui string is written in the source code.
|
||||
|
@ -166,12 +170,14 @@ Now run the compiler.
|
|||
Then a C source file `resources.c` is generated.
|
||||
Modify tfe3.c and save it as tfe3_r.c
|
||||
|
||||
~~~C
|
||||
#include "resources.c"
|
||||
... ... ...
|
||||
... ... ...
|
||||
build = gtk_builder_new_from_resource ("/com/github/ToshioCP/tfe3/tfe3.ui");
|
||||
... ... ...
|
||||
... ... ...
|
||||
~~~
|
||||
|
||||
Then, compile and run it.
|
||||
The window appears and it is the same as the screenshot at the beginning of this page.
|
||||
|
|
Loading…
Reference in a new issue