diff --git a/.gitignore b/.gitignore index fed3b74..2244345 100755 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ src/tfe5/_build src/tfe5/hello.txt src/menu/a.out src/color/_build +src/turtle html/* latex/* diff --git a/Rakefile b/Rakefile index b9d8d14..3a0c34c 100644 --- a/Rakefile +++ b/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 diff --git a/Readme.md b/Readme.md index 0c83b2b..ec83606 100644 --- a/Readme.md +++ b/Readme.md @@ -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) diff --git a/gfm/sec10.md b/gfm/sec10.md index 447fb85..25b91a4 100644 --- a/gfm/sec10.md +++ b/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. - 1 GtkWidget * - 2 tfe_text_view_new (void) { - 3 return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL)); - 4 } +~~~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,14 +115,16 @@ 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. - 1 static void - 2 tfe_text_view_init (TfeTextView *tv) { - 3 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); - 4 - 5 tv->file = NULL; - 6 gtk_text_buffer_set_modified (tb, FALSE); - 7 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR); - 8 } +~~~C +1 static void +2 tfe_text_view_init (TfeTextView *tv) { +3 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); +4 +5 tv->file = NULL; +6 gtk_text_buffer_set_modified (tb, FALSE); +7 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR); +8 } +~~~ `tfe_text_view_init` initializes the instance. @@ -151,44 +155,46 @@ 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. - 1 typedef struct _GObjectClass GObjectClass; - 2 typedef struct _GObjectClass GInitiallyUnownedClass; - 3 - 4 struct _GObjectClass { - 5 GTypeClass g_type_class; - 6 /*< private >*/ - 7 GSList *construct_properties; - 8 /*< public >*/ - 9 /* seldom overidden */ - 10 GObject* (*constructor) (GType type, - 11 guint n_construct_properties, - 12 GObjectConstructParam *construct_properties); - 13 /* overridable methods */ - 14 void (*set_property) (GObject *object, - 15 guint property_id, - 16 const GValue *value, - 17 GParamSpec *pspec); - 18 void (*get_property) (GObject *object, - 19 guint property_id, - 20 GValue *value, - 21 GParamSpec *pspec); - 22 void (*dispose) (GObject *object); - 23 void (*finalize) (GObject *object); - 24 /* seldom overidden */ - 25 void (*dispatch_properties_changed) (GObject *object, - 26 guint n_pspecs, - 27 GParamSpec **pspecs); - 28 /* signals */ - 29 void (*notify) (GObject *object, - 30 GParamSpec *pspec); - 31 - 32 /* called when done constructing */ - 33 void (*constructed) (GObject *object); - 34 /*< private >*/ - 35 gsize flags; - 36 /* padding */ - 37 gpointer pdummy[6]; - 38 }; +~~~C + 1 typedef struct _GObjectClass GObjectClass; + 2 typedef struct _GObjectClass GInitiallyUnownedClass; + 3 + 4 struct _GObjectClass { + 5 GTypeClass g_type_class; + 6 /*< private >*/ + 7 GSList *construct_properties; + 8 /*< public >*/ + 9 /* seldom overidden */ +10 GObject* (*constructor) (GType type, +11 guint n_construct_properties, +12 GObjectConstructParam *construct_properties); +13 /* overridable methods */ +14 void (*set_property) (GObject *object, +15 guint property_id, +16 const GValue *value, +17 GParamSpec *pspec); +18 void (*get_property) (GObject *object, +19 guint property_id, +20 GValue *value, +21 GParamSpec *pspec); +22 void (*dispose) (GObject *object); +23 void (*finalize) (GObject *object); +24 /* seldom overidden */ +25 void (*dispatch_properties_changed) (GObject *object, +26 guint n_pspecs, +27 GParamSpec **pspecs); +28 /* signals */ +29 void (*notify) (GObject *object, +30 GParamSpec *pspec); +31 +32 /* called when done constructing */ +33 void (*constructed) (GObject *object); +34 /*< private >*/ +35 gsize flags; +36 /* padding */ +37 gpointer pdummy[6]; +38 }; +~~~ I'd like to explain some of the members. There's a pointer to the function `dispose` in line 22. @@ -218,114 +224,116 @@ 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). - 1 struct _GtkWidgetClass { - 2 GInitiallyUnownedClass parent_class; - 3 /*< public >*/ - 4 guint activate_signal; - 5 /* basics */ - 6 void (* show) (GtkWidget *widget); - 7 void (* hide) (GtkWidget *widget); - 8 void (* map) (GtkWidget *widget); - 9 void (* unmap) (GtkWidget *widget); - 10 void (* realize) (GtkWidget *widget); - 11 void (* unrealize) (GtkWidget *widget); - 12 void (* root) (GtkWidget *widget); - 13 void (* unroot) (GtkWidget *widget); - 14 void (* size_allocate) (GtkWidget *widget, - 15 int width, - 16 int height, - 17 int baseline); - 18 void (* state_flags_changed) (GtkWidget *widget, - 19 GtkStateFlags previous_state_flags); - 20 void (* direction_changed) (GtkWidget *widget, - 21 GtkTextDirection previous_direction); - 22 void (* grab_notify) (GtkWidget *widget, - 23 gboolean was_grabbed); - 24 /* size requests */ - 25 GtkSizeRequestMode (* get_request_mode) (GtkWidget *widget); - 26 void (* measure) (GtkWidget *widget, - 27 GtkOrientation orientation, - 28 int for_size, - 29 int *minimum, - 30 int *natural, - 31 int *minimum_baseline, - 32 int *natural_baseline); - 33 /* Mnemonics */ - 34 gboolean (* mnemonic_activate) (GtkWidget *widget, - 35 gboolean group_cycling); - 36 /* explicit focus */ - 37 gboolean (* grab_focus) (GtkWidget *widget); - 38 gboolean (* focus) (GtkWidget *widget, - 39 GtkDirectionType direction); - 40 void (* set_focus_child) (GtkWidget *widget, - 41 GtkWidget *child); - 42 /* keyboard navigation */ - 43 void (* move_focus) (GtkWidget *widget, - 44 GtkDirectionType direction); - 45 gboolean (* keynav_failed) (GtkWidget *widget, - 46 GtkDirectionType direction); - 47 /* accessibility support - 48 */ - 49 AtkObject * (* get_accessible) (GtkWidget *widget); - 50 gboolean (* query_tooltip) (GtkWidget *widget, - 51 gint x, - 52 gint y, - 53 gboolean keyboard_tooltip, - 54 GtkTooltip *tooltip); - 55 void (* compute_expand) (GtkWidget *widget, - 56 gboolean *hexpand_p, - 57 gboolean *vexpand_p); - 58 void (* css_changed) (GtkWidget *widget, - 59 GtkCssStyleChange *change); - 60 void (* system_setting_changed) (GtkWidget *widget, - 61 GtkSystemSetting settings); - 62 void (* snapshot) (GtkWidget *widget, - 63 GtkSnapshot *snapshot); - 64 gboolean (* contains) (GtkWidget *widget, - 65 gdouble x, - 66 gdouble y); - 67 /*< private >*/ - 68 GtkWidgetClassPrivate *priv; - 69 gpointer padding[8]; - 70 }; - 71 - 72 struct _GtkTextViewClass { - 73 GtkWidgetClass parent_class; - 74 /*< public >*/ - 75 void (* move_cursor) (GtkTextView *text_view, - 76 GtkMovementStep step, - 77 gint count, - 78 gboolean extend_selection); - 79 void (* set_anchor) (GtkTextView *text_view); - 80 void (* insert_at_cursor) (GtkTextView *text_view, - 81 const gchar *str); - 82 void (* delete_from_cursor) (GtkTextView *text_view, - 83 GtkDeleteType type, - 84 gint count); - 85 void (* backspace) (GtkTextView *text_view); - 86 void (* cut_clipboard) (GtkTextView *text_view); - 87 void (* copy_clipboard) (GtkTextView *text_view); - 88 void (* paste_clipboard) (GtkTextView *text_view); - 89 void (* toggle_overwrite) (GtkTextView *text_view); - 90 GtkTextBuffer * (* create_buffer) (GtkTextView *text_view); - 91 void (* snapshot_layer) (GtkTextView *text_view, - 92 GtkTextViewLayer layer, - 93 GtkSnapshot *snapshot); - 94 gboolean (* extend_selection) (GtkTextView *text_view, - 95 GtkTextExtendSelection granularity, - 96 const GtkTextIter *location, - 97 GtkTextIter *start, - 98 GtkTextIter *end); - 99 void (* insert_emoji) (GtkTextView *text_view); - 100 /*< private >*/ - 101 gpointer padding[8]; - 102 }; - 103 - 104 /* The following definition is generated by the macro G_DECLARE_FINAL_TYPE - 105 typedef struct { - 106 GtkTextView parent_class; - 107 } TfeTextViewClass; - 108 +~~~C + 1 struct _GtkWidgetClass { + 2 GInitiallyUnownedClass parent_class; + 3 /*< public >*/ + 4 guint activate_signal; + 5 /* basics */ + 6 void (* show) (GtkWidget *widget); + 7 void (* hide) (GtkWidget *widget); + 8 void (* map) (GtkWidget *widget); + 9 void (* unmap) (GtkWidget *widget); + 10 void (* realize) (GtkWidget *widget); + 11 void (* unrealize) (GtkWidget *widget); + 12 void (* root) (GtkWidget *widget); + 13 void (* unroot) (GtkWidget *widget); + 14 void (* size_allocate) (GtkWidget *widget, + 15 int width, + 16 int height, + 17 int baseline); + 18 void (* state_flags_changed) (GtkWidget *widget, + 19 GtkStateFlags previous_state_flags); + 20 void (* direction_changed) (GtkWidget *widget, + 21 GtkTextDirection previous_direction); + 22 void (* grab_notify) (GtkWidget *widget, + 23 gboolean was_grabbed); + 24 /* size requests */ + 25 GtkSizeRequestMode (* get_request_mode) (GtkWidget *widget); + 26 void (* measure) (GtkWidget *widget, + 27 GtkOrientation orientation, + 28 int for_size, + 29 int *minimum, + 30 int *natural, + 31 int *minimum_baseline, + 32 int *natural_baseline); + 33 /* Mnemonics */ + 34 gboolean (* mnemonic_activate) (GtkWidget *widget, + 35 gboolean group_cycling); + 36 /* explicit focus */ + 37 gboolean (* grab_focus) (GtkWidget *widget); + 38 gboolean (* focus) (GtkWidget *widget, + 39 GtkDirectionType direction); + 40 void (* set_focus_child) (GtkWidget *widget, + 41 GtkWidget *child); + 42 /* keyboard navigation */ + 43 void (* move_focus) (GtkWidget *widget, + 44 GtkDirectionType direction); + 45 gboolean (* keynav_failed) (GtkWidget *widget, + 46 GtkDirectionType direction); + 47 /* accessibility support + 48 */ + 49 AtkObject * (* get_accessible) (GtkWidget *widget); + 50 gboolean (* query_tooltip) (GtkWidget *widget, + 51 gint x, + 52 gint y, + 53 gboolean keyboard_tooltip, + 54 GtkTooltip *tooltip); + 55 void (* compute_expand) (GtkWidget *widget, + 56 gboolean *hexpand_p, + 57 gboolean *vexpand_p); + 58 void (* css_changed) (GtkWidget *widget, + 59 GtkCssStyleChange *change); + 60 void (* system_setting_changed) (GtkWidget *widget, + 61 GtkSystemSetting settings); + 62 void (* snapshot) (GtkWidget *widget, + 63 GtkSnapshot *snapshot); + 64 gboolean (* contains) (GtkWidget *widget, + 65 gdouble x, + 66 gdouble y); + 67 /*< private >*/ + 68 GtkWidgetClassPrivate *priv; + 69 gpointer padding[8]; + 70 }; + 71 + 72 struct _GtkTextViewClass { + 73 GtkWidgetClass parent_class; + 74 /*< public >*/ + 75 void (* move_cursor) (GtkTextView *text_view, + 76 GtkMovementStep step, + 77 gint count, + 78 gboolean extend_selection); + 79 void (* set_anchor) (GtkTextView *text_view); + 80 void (* insert_at_cursor) (GtkTextView *text_view, + 81 const gchar *str); + 82 void (* delete_from_cursor) (GtkTextView *text_view, + 83 GtkDeleteType type, + 84 gint count); + 85 void (* backspace) (GtkTextView *text_view); + 86 void (* cut_clipboard) (GtkTextView *text_view); + 87 void (* copy_clipboard) (GtkTextView *text_view); + 88 void (* paste_clipboard) (GtkTextView *text_view); + 89 void (* toggle_overwrite) (GtkTextView *text_view); + 90 GtkTextBuffer * (* create_buffer) (GtkTextView *text_view); + 91 void (* snapshot_layer) (GtkTextView *text_view, + 92 GtkTextViewLayer layer, + 93 GtkSnapshot *snapshot); + 94 gboolean (* extend_selection) (GtkTextView *text_view, + 95 GtkTextExtendSelection granularity, + 96 const GtkTextIter *location, + 97 GtkTextIter *start, + 98 GtkTextIter *end); + 99 void (* insert_emoji) (GtkTextView *text_view); +100 /*< private >*/ +101 gpointer padding[8]; +102 }; +103 +104 /* The following definition is generated by the macro G_DECLARE_FINAL_TYPE +105 typedef struct { +106 GtkTextView parent_class; +107 } TfeTextViewClass; +108 +~~~ - 105-107: This three lines are generated by the macro G\_DECLARE\_FINAL\_TYPE. So, they are not written in either `tfe_text_view.h` or `tfe_text_view.c`. @@ -370,15 +378,17 @@ 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`. - 1 static void - 2 tfe_text_view_dispose (GObject *gobject) { - 3 TfeTextView *tv = TFE_TEXT_VIEW (gobject); - 4 - 5 if (G_IS_FILE (tv->file)) - 6 g_clear_object (&tv->file); - 7 - 8 G_OBJECT_CLASS (tfe_text_view_parent_class)->dispose (gobject); - 9 } +~~~C +1 static void +2 tfe_text_view_dispose (GObject *gobject) { +3 TfeTextView *tv = TFE_TEXT_VIEW (gobject); +4 +5 if (G_IS_FILE (tv->file)) +6 g_clear_object (&tv->file); +7 +8 G_OBJECT_CLASS (tfe_text_view_parent_class)->dispose (gobject); +9 } +~~~ - 5,6: If `tv->file` points a GFile, decrease its reference count. `g_clear_object` decreases the reference count and assigns NULL to `tv->file`. In dispose handlers, we usually use `g_clear_object` rather than `g_object_unref`. diff --git a/gfm/sec11.md b/gfm/sec11.md index a34f00c..acb9267 100644 --- a/gfm/sec11.md +++ b/gfm/sec11.md @@ -55,33 +55,35 @@ If you need to register two or more signals, static array is usually used. Signal registration codes are written in the class initialization function. - 1 static void - 2 tfe_text_view_class_init (TfeTextViewClass *class) { - 3 GObjectClass *object_class = G_OBJECT_CLASS (class); - 4 - 5 object_class->dispose = tfe_text_view_dispose; - 6 tfe_text_view_signals[CHANGE_FILE] = g_signal_newv ("change-file", - 7 G_TYPE_FROM_CLASS (class), - 8 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, - 9 NULL /* closure */, - 10 NULL /* accumulator */, - 11 NULL /* accumulator data */, - 12 NULL /* C marshaller */, - 13 G_TYPE_NONE /* return_type */, - 14 0 /* n_params */, - 15 NULL /* param_types */); - 16 GType param_types[] = {G_TYPE_INT}; - 17 tfe_text_view_signals[OPEN_RESPONSE] = g_signal_newv ("open-response", - 18 G_TYPE_FROM_CLASS (class), - 19 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, - 20 NULL /* closure */, - 21 NULL /* accumulator */, - 22 NULL /* accumulator data */, - 23 NULL /* C marshaller */, - 24 G_TYPE_NONE /* return_type */, - 25 1 /* n_params */, - 26 param_types); - 27 } +~~~C + 1 static void + 2 tfe_text_view_class_init (TfeTextViewClass *class) { + 3 GObjectClass *object_class = G_OBJECT_CLASS (class); + 4 + 5 object_class->dispose = tfe_text_view_dispose; + 6 tfe_text_view_signals[CHANGE_FILE] = g_signal_newv ("change-file", + 7 G_TYPE_FROM_CLASS (class), + 8 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, + 9 NULL /* closure */, +10 NULL /* accumulator */, +11 NULL /* accumulator data */, +12 NULL /* C marshaller */, +13 G_TYPE_NONE /* return_type */, +14 0 /* n_params */, +15 NULL /* param_types */); +16 GType param_types[] = {G_TYPE_INT}; +17 tfe_text_view_signals[OPEN_RESPONSE] = g_signal_newv ("open-response", +18 G_TYPE_FROM_CLASS (class), +19 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, +20 NULL /* closure */, +21 NULL /* accumulator */, +22 NULL /* accumulator data */, +23 NULL /* C marshaller */, +24 G_TYPE_NONE /* return_type */, +25 1 /* n_params */, +26 param_types); +27 } +~~~ - 6-15: Register "change-file"signal. `g_signal_newv` function is used. diff --git a/gfm/sec12.md b/gfm/sec12.md index cc97ef0..dacc9f7 100644 --- a/gfm/sec12.md +++ b/gfm/sec12.md @@ -9,42 +9,46 @@ 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. - 1 #include - 2 - 3 #include "tfetextview.h" - 4 #include "tfenotebook.h" +~~~C +1 #include +2 +3 #include "tfetextview.h" +4 #include "tfenotebook.h" +~~~ `tfetextview.h` is a header file which describes the public functions in `tfetextview.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 - 4 /* "open-response" signal response */ - 5 enum - 6 { - 7 TFE_OPEN_RESPONSE_SUCCESS, - 8 TFE_OPEN_RESPONSE_CANCEL, - 9 TFE_OPEN_RESPONSE_ERROR - 10 }; - 11 - 12 GFile * - 13 tfe_text_view_get_file (TfeTextView *tv); - 14 - 15 void - 16 tfe_text_view_open (TfeTextView *tv, GtkWidget *win); - 17 - 18 void - 19 tfe_text_view_save (TfeTextView *tv); - 20 - 21 void - 22 tfe_text_view_saveas (TfeTextView *tv); - 23 - 24 GtkWidget * - 25 tfe_text_view_new_with_file (GFile *file); - 26 - 27 GtkWidget * - 28 tfe_text_view_new (void); - 29 +~~~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 + 4 /* "open-response" signal response */ + 5 enum + 6 { + 7 TFE_OPEN_RESPONSE_SUCCESS, + 8 TFE_OPEN_RESPONSE_CANCEL, + 9 TFE_OPEN_RESPONSE_ERROR +10 }; +11 +12 GFile * +13 tfe_text_view_get_file (TfeTextView *tv); +14 +15 void +16 tfe_text_view_open (TfeTextView *tv, GtkWidget *win); +17 +18 void +19 tfe_text_view_save (TfeTextView *tv); +20 +21 void +22 tfe_text_view_saveas (TfeTextView *tv); +23 +24 GtkWidget * +25 tfe_text_view_new_with_file (GFile *file); +26 +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,30 +79,32 @@ If an error occures during the genration process, NULL is returned. Each function is defined as follows. - 1 GtkWidget * - 2 tfe_text_view_new_with_file (GFile *file) { - 3 g_return_val_if_fail (G_IS_FILE (file), NULL); - 4 - 5 GtkWidget *tv; - 6 GtkTextBuffer *tb; - 7 char *contents; - 8 gsize length; - 9 - 10 if (! g_file_load_contents (file, NULL, &contents, &length, NULL, NULL)) /* read error */ - 11 return NULL; - 12 - 13 tv = tfe_text_view_new(); - 14 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); - 15 gtk_text_buffer_set_text (tb, contents, length); - 16 g_free (contents); - 17 TFE_TEXT_VIEW (tv)->file = g_file_dup (file); - 18 return tv; - 19 } - 20 - 21 GtkWidget * - 22 tfe_text_view_new (void) { - 23 return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL)); - 24 } +~~~C + 1 GtkWidget * + 2 tfe_text_view_new_with_file (GFile *file) { + 3 g_return_val_if_fail (G_IS_FILE (file), NULL); + 4 + 5 GtkWidget *tv; + 6 GtkTextBuffer *tb; + 7 char *contents; + 8 gsize length; + 9 +10 if (! g_file_load_contents (file, NULL, &contents, &length, NULL, NULL)) /* read error */ +11 return NULL; +12 +13 tv = tfe_text_view_new(); +14 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); +15 gtk_text_buffer_set_text (tb, contents, length); +16 g_free (contents); +17 TFE_TEXT_VIEW (tv)->file = g_file_dup (file); +18 return tv; +19 } +20 +21 GtkWidget * +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,76 +143,78 @@ 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. - 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)); - 4 GFile *file; - 5 - 6 if (response == GTK_RESPONSE_ACCEPT) { - 7 file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog)); - 8 if (G_IS_FILE(file)) { - 9 tv->file = file; - 10 gtk_text_buffer_set_modified (tb, TRUE); - 11 g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0); - 12 tfe_text_view_save (TFE_TEXT_VIEW (tv)); - 13 } - 14 } - 15 gtk_window_destroy (GTK_WINDOW (dialog)); - 16 } - 17 - 18 void - 19 tfe_text_view_save (TfeTextView *tv) { - 20 g_return_if_fail (TFE_IS_TEXT_VIEW (tv)); - 21 - 22 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); - 23 GtkTextIter start_iter; - 24 GtkTextIter end_iter; - 25 gchar *contents; - 26 GtkWidget *message_dialog; - 27 GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW); - 28 GError *err = NULL; - 29 - 30 if (! gtk_text_buffer_get_modified (tb)) - 31 return; /* no necessary to save it */ - 32 else if (tv->file == NULL) - 33 tfe_text_view_saveas (tv); - 34 else { - 35 gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter); - 36 contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE); - 37 if (g_file_replace_contents (tv->file, contents, strlen (contents), NULL, TRUE, G_FILE_CREATE_NONE, NULL, NULL, &err)) - 38 gtk_text_buffer_set_modified (tb, FALSE); - 39 else { - 40 /* It is possible that tv->file is broken. */ - 41 /* It is a good idea to set tv->file to NULL. */ - 42 if (G_IS_FILE (tv->file)) - 43 g_object_unref (tv->file); - 44 tv->file =NULL; - 45 g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0); - 46 gtk_text_buffer_set_modified (tb, TRUE); - 47 message_dialog = gtk_message_dialog_new (GTK_WINDOW (win), GTK_DIALOG_MODAL, - 48 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, - 49 "%s.\n", err->message); - 50 g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL); - 51 gtk_widget_show (message_dialog); - 52 g_error_free (err); - 53 } - 54 } - 55 } - 56 - 57 void - 58 tfe_text_view_saveas (TfeTextView *tv) { - 59 g_return_if_fail (TFE_IS_TEXT_VIEW (tv)); - 60 - 61 GtkWidget *dialog; - 62 GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW); - 63 - 64 dialog = gtk_file_chooser_dialog_new ("Save file", GTK_WINDOW (win), GTK_FILE_CHOOSER_ACTION_SAVE, - 65 "_Cancel", GTK_RESPONSE_CANCEL, - 66 "_Save", GTK_RESPONSE_ACCEPT, - 67 NULL); - 68 g_signal_connect (dialog, "response", G_CALLBACK (saveas_dialog_response), tv); - 69 gtk_widget_show (dialog); - 70 } +~~~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)); + 4 GFile *file; + 5 + 6 if (response == GTK_RESPONSE_ACCEPT) { + 7 file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog)); + 8 if (G_IS_FILE(file)) { + 9 tv->file = file; +10 gtk_text_buffer_set_modified (tb, TRUE); +11 g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0); +12 tfe_text_view_save (TFE_TEXT_VIEW (tv)); +13 } +14 } +15 gtk_window_destroy (GTK_WINDOW (dialog)); +16 } +17 +18 void +19 tfe_text_view_save (TfeTextView *tv) { +20 g_return_if_fail (TFE_IS_TEXT_VIEW (tv)); +21 +22 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); +23 GtkTextIter start_iter; +24 GtkTextIter end_iter; +25 gchar *contents; +26 GtkWidget *message_dialog; +27 GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW); +28 GError *err = NULL; +29 +30 if (! gtk_text_buffer_get_modified (tb)) +31 return; /* no necessary to save it */ +32 else if (tv->file == NULL) +33 tfe_text_view_saveas (tv); +34 else { +35 gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter); +36 contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE); +37 if (g_file_replace_contents (tv->file, contents, strlen (contents), NULL, TRUE, G_FILE_CREATE_NONE, NULL, NULL, &err)) +38 gtk_text_buffer_set_modified (tb, FALSE); +39 else { +40 /* It is possible that tv->file is broken. */ +41 /* It is a good idea to set tv->file to NULL. */ +42 if (G_IS_FILE (tv->file)) +43 g_object_unref (tv->file); +44 tv->file =NULL; +45 g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0); +46 gtk_text_buffer_set_modified (tb, TRUE); +47 message_dialog = gtk_message_dialog_new (GTK_WINDOW (win), GTK_DIALOG_MODAL, +48 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, +49 "%s.\n", err->message); +50 g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL); +51 gtk_widget_show (message_dialog); +52 g_error_free (err); +53 } +54 } +55 } +56 +57 void +58 tfe_text_view_saveas (TfeTextView *tv) { +59 g_return_if_fail (TFE_IS_TEXT_VIEW (tv)); +60 +61 GtkWidget *dialog; +62 GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW); +63 +64 dialog = gtk_file_chooser_dialog_new ("Save file", GTK_WINDOW (win), GTK_FILE_CHOOSER_ACTION_SAVE, +65 "_Cancel", GTK_RESPONSE_CANCEL, +66 "_Save", GTK_RESPONSE_ACCEPT, +67 NULL); +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,55 +266,57 @@ 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`. - 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)); - 4 GFile *file; - 5 char *contents; - 6 gsize length; - 7 GtkWidget *message_dialog; - 8 GError *err = NULL; - 9 - 10 if (response != GTK_RESPONSE_ACCEPT) - 11 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_CANCEL); - 12 else if (! G_IS_FILE (file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog)))) - 13 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_ERROR); - 14 else if (! g_file_load_contents (file, NULL, &contents, &length, NULL, &err)) { /* read error */ - 15 if (G_IS_FILE (file)) - 16 g_object_unref (file); - 17 message_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog), GTK_DIALOG_MODAL, - 18 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, - 19 "%s.\n", err->message); - 20 g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL); - 21 gtk_widget_show (message_dialog); - 22 g_error_free (err); - 23 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_ERROR); - 24 } else { - 25 gtk_text_buffer_set_text (tb, contents, length); - 26 g_free (contents); - 27 if (G_IS_FILE (tv->file)) - 28 g_object_unref (tv->file); - 29 tv->file = file; - 30 gtk_text_buffer_set_modified (tb, FALSE); - 31 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_SUCCESS); - 32 } - 33 gtk_window_destroy (GTK_WINDOW (dialog)); - 34 } - 35 - 36 void - 37 tfe_text_view_open (TfeTextView *tv, GtkWidget *win) { - 38 g_return_if_fail (TFE_IS_TEXT_VIEW (tv)); - 39 g_return_if_fail (GTK_IS_WINDOW (win)); - 40 - 41 GtkWidget *dialog; - 42 - 43 dialog = gtk_file_chooser_dialog_new ("Open file", GTK_WINDOW (win), GTK_FILE_CHOOSER_ACTION_OPEN, - 44 "Cancel", GTK_RESPONSE_CANCEL, - 45 "Open", GTK_RESPONSE_ACCEPT, - 46 NULL); - 47 g_signal_connect (dialog, "response", G_CALLBACK (open_dialog_response), tv); - 48 gtk_widget_show (dialog); - 49 } +~~~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)); + 4 GFile *file; + 5 char *contents; + 6 gsize length; + 7 GtkWidget *message_dialog; + 8 GError *err = NULL; + 9 +10 if (response != GTK_RESPONSE_ACCEPT) +11 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_CANCEL); +12 else if (! G_IS_FILE (file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog)))) +13 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_ERROR); +14 else if (! g_file_load_contents (file, NULL, &contents, &length, NULL, &err)) { /* read error */ +15 if (G_IS_FILE (file)) +16 g_object_unref (file); +17 message_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog), GTK_DIALOG_MODAL, +18 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, +19 "%s.\n", err->message); +20 g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL); +21 gtk_widget_show (message_dialog); +22 g_error_free (err); +23 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_ERROR); +24 } else { +25 gtk_text_buffer_set_text (tb, contents, length); +26 g_free (contents); +27 if (G_IS_FILE (tv->file)) +28 g_object_unref (tv->file); +29 tv->file = file; +30 gtk_text_buffer_set_modified (tb, FALSE); +31 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_SUCCESS); +32 } +33 gtk_window_destroy (GTK_WINDOW (dialog)); +34 } +35 +36 void +37 tfe_text_view_open (TfeTextView *tv, GtkWidget *win) { +38 g_return_if_fail (TFE_IS_TEXT_VIEW (tv)); +39 g_return_if_fail (GTK_IS_WINDOW (win)); +40 +41 GtkWidget *dialog; +42 +43 dialog = gtk_file_chooser_dialog_new ("Open file", GTK_WINDOW (win), GTK_FILE_CHOOSER_ACTION_OPEN, +44 "Cancel", GTK_RESPONSE_CANCEL, +45 "Open", GTK_RESPONSE_ACCEPT, +46 NULL); +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. - 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 } +~~~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. diff --git a/gfm/sec13.md b/gfm/sec13.md index 2104b3e..0259d7d 100644 --- a/gfm/sec13.md +++ b/gfm/sec13.md @@ -6,18 +6,20 @@ 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. - 1 void - 2 notebook_page_save(GtkNotebook *nb); - 3 - 4 void - 5 notebook_page_open (GtkNotebook *nb); - 6 - 7 void - 8 notebook_page_new_with_file (GtkNotebook *nb, GFile *file); - 9 - 10 void - 11 notebook_page_new (GtkNotebook *nb); - 12 +~~~C + 1 void + 2 notebook_page_save(GtkNotebook *nb); + 3 + 4 void + 5 notebook_page_open (GtkNotebook *nb); + 6 + 7 void + 8 notebook_page_new_with_file (GtkNotebook *nb, GFile *file); + 9 +10 void +11 notebook_page_new (GtkNotebook *nb); +12 +~~~ This header file shows the public functions in `tfenotebook.c`. @@ -44,43 +46,45 @@ Now let's look at each program of the functions. ## notebook\_page\_new - 1 static gchar* - 2 get_untitled () { - 3 static int c = -1; - 4 if (++c == 0) - 5 return g_strdup_printf("Untitled"); - 6 else - 7 return g_strdup_printf ("Untitled%u", c); - 8 } - 9 - 10 static void - 11 notebook_page_build (GtkNotebook *nb, GtkWidget *tv, char *filename) { - 12 GtkWidget *scr; - 13 GtkNotebookPage *nbp; - 14 GtkWidget *lab; - 15 gint i; - 16 scr = gtk_scrolled_window_new (); - 17 - 18 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv); - 19 lab = gtk_label_new (filename); - 20 i = gtk_notebook_append_page (nb, scr, lab); - 21 nbp = gtk_notebook_get_page (nb, scr); - 22 g_object_set (nbp, "tab-expand", TRUE, NULL); - 23 gtk_notebook_set_current_page (nb, i); - 24 g_signal_connect (GTK_TEXT_VIEW (tv), "change-file", G_CALLBACK (file_changed), nb); - 25 } - 26 - 27 void - 28 notebook_page_new (GtkNotebook *nb) { - 29 g_return_if_fail(GTK_IS_NOTEBOOK (nb)); - 30 - 31 GtkWidget *tv; - 32 char *filename; - 33 - 34 tv = tfe_text_view_new (); - 35 filename = get_untitled (); - 36 notebook_page_build (nb, tv, filename); - 37 } +~~~C + 1 static gchar* + 2 get_untitled () { + 3 static int c = -1; + 4 if (++c == 0) + 5 return g_strdup_printf("Untitled"); + 6 else + 7 return g_strdup_printf ("Untitled%u", c); + 8 } + 9 +10 static void +11 notebook_page_build (GtkNotebook *nb, GtkWidget *tv, char *filename) { +12 GtkWidget *scr; +13 GtkNotebookPage *nbp; +14 GtkWidget *lab; +15 gint i; +16 scr = gtk_scrolled_window_new (); +17 +18 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv); +19 lab = gtk_label_new (filename); +20 i = gtk_notebook_append_page (nb, scr, lab); +21 nbp = gtk_notebook_get_page (nb, scr); +22 g_object_set (nbp, "tab-expand", TRUE, NULL); +23 gtk_notebook_set_current_page (nb, i); +24 g_signal_connect (GTK_TEXT_VIEW (tv), "change-file", G_CALLBACK (file_changed), nb); +25 } +26 +27 void +28 notebook_page_new (GtkNotebook *nb) { +29 g_return_if_fail(GTK_IS_NOTEBOOK (nb)); +30 +31 GtkWidget *tv; +32 char *filename; +33 +34 tv = tfe_text_view_new (); +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,19 +106,21 @@ The caller of `get_untitled` is in charge of freeing the memories of the string. ## notebook\_page\_new\_with\_file - 1 void - 2 notebook_page_new_with_file (GtkNotebook *nb, GFile *file) { - 3 g_return_if_fail(GTK_IS_NOTEBOOK (nb)); - 4 g_return_if_fail(G_IS_FILE (file)); - 5 - 6 GtkWidget *tv; - 7 char *filename; - 8 - 9 if ((tv = tfe_text_view_new_with_file (file)) == NULL) - 10 return; /* read error */ - 11 filename = g_file_get_basename (file); - 12 notebook_page_build (nb, tv, filename); - 13 } +~~~C + 1 void + 2 notebook_page_new_with_file (GtkNotebook *nb, GFile *file) { + 3 g_return_if_fail(GTK_IS_NOTEBOOK (nb)); + 4 g_return_if_fail(G_IS_FILE (file)); + 5 + 6 GtkWidget *tv; + 7 char *filename; + 8 + 9 if ((tv = tfe_text_view_new_with_file (file)) == NULL) +10 return; /* read error */ +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,34 +128,36 @@ If it returns NULL, then do nothing and return because of an error. ## notebook\_page\_open - 1 static void - 2 open_response (TfeTextView *tv, gint response, GtkNotebook *nb) { - 3 GFile *file; - 4 char *filename; - 5 - 6 if (response != TFE_OPEN_RESPONSE_SUCCESS) { - 7 g_object_ref_sink (tv); - 8 g_object_unref (tv); - 9 }else if (! G_IS_FILE (file = tfe_text_view_get_file (tv))) { - 10 g_object_ref_sink (tv); - 11 g_object_unref (tv); - 12 }else { - 13 filename = g_file_get_basename (file); - 14 g_object_unref (file); - 15 notebook_page_build (nb, GTK_WIDGET (tv), filename); - 16 } - 17 } - 18 - 19 void - 20 notebook_page_open (GtkNotebook *nb) { - 21 g_return_if_fail(GTK_IS_NOTEBOOK (nb)); - 22 - 23 GtkWidget *tv; - 24 - 25 tv = tfe_text_view_new (); - 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 } +~~~C + 1 static void + 2 open_response (TfeTextView *tv, gint response, GtkNotebook *nb) { + 3 GFile *file; + 4 char *filename; + 5 + 6 if (response != TFE_OPEN_RESPONSE_SUCCESS) { + 7 g_object_ref_sink (tv); + 8 g_object_unref (tv); + 9 }else if (! G_IS_FILE (file = tfe_text_view_get_file (tv))) { +10 g_object_ref_sink (tv); +11 g_object_unref (tv); +12 }else { +13 filename = g_file_get_basename (file); +14 g_object_unref (file); +15 notebook_page_build (nb, GTK_WIDGET (tv), filename); +16 } +17 } +18 +19 void +20 notebook_page_open (GtkNotebook *nb) { +21 g_return_if_fail(GTK_IS_NOTEBOOK (nb)); +22 +23 GtkWidget *tv; +24 +25 tv = tfe_text_view_new (); +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,17 +177,19 @@ Get the filename, build the contents of the page. ## notebook\_page\_save - 1 void - 2 notebook_page_save(GtkNotebook *nb) { - 3 gint i; - 4 GtkWidget *scr; - 5 GtkWidget *tv; - 6 - 7 i = gtk_notebook_get_current_page (nb); - 8 scr = gtk_notebook_get_nth_page (nb, i); - 9 tv = gtk_scrolled_window_get_child (GTK_SCROLLED_WINDOW (scr)); - 10 tfe_text_view_save (TFE_TEXT_VIEW (tv)); - 11 } +~~~C + 1 void + 2 notebook_page_save(GtkNotebook *nb) { + 3 gint i; + 4 GtkWidget *scr; + 5 GtkWidget *tv; + 6 + 7 i = gtk_notebook_get_current_page (nb); + 8 scr = gtk_notebook_get_nth_page (nb, i); + 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,24 +200,26 @@ 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. - 1 static void - 2 file_changed (TfeTextView *tv, GtkNotebook *nb) { - 3 GFile *file; - 4 char *filename; - 5 GtkWidget *scr; - 6 GtkWidget *label; - 7 - 8 file = tfe_text_view_get_file (tv); - 9 scr = gtk_widget_get_parent (GTK_WIDGET (tv)); - 10 if (G_IS_FILE (file)) - 11 filename = g_file_get_basename (file); - 12 else - 13 filename = get_untitled (); - 14 label = gtk_label_new (filename); - 15 gtk_notebook_set_tab_label (nb, scr, label); - 16 g_object_unref (file); - 17 g_free (filename); - 18 } +~~~C + 1 static void + 2 file_changed (TfeTextView *tv, GtkNotebook *nb) { + 3 GFile *file; + 4 char *filename; + 5 GtkWidget *scr; + 6 GtkWidget *label; + 7 + 8 file = tfe_text_view_get_file (tv); + 9 scr = gtk_widget_get_parent (GTK_WIDGET (tv)); +10 if (G_IS_FILE (file)) +11 filename = g_file_get_basename (file); +12 else +13 filename = get_untitled (); +14 label = gtk_label_new (filename); +15 gtk_notebook_set_tab_label (nb, scr, label); +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`. diff --git a/gfm/sec14.md b/gfm/sec14.md index 7357be1..c4af9ad 100644 --- a/gfm/sec14.md +++ b/gfm/sec14.md @@ -15,21 +15,23 @@ 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. - 1 int - 2 main (int argc, char **argv) { - 3 GtkApplication *app; - 4 int stat; - 5 - 6 app = gtk_application_new ("com.github.ToshioCP.tfe", G_APPLICATION_HANDLES_OPEN); - 7 - 8 g_signal_connect (app, "startup", G_CALLBACK (tfe_startup), NULL); - 9 g_signal_connect (app, "activate", G_CALLBACK (tfe_activate), NULL); - 10 g_signal_connect (app, "open", G_CALLBACK (tfe_open), NULL); - 11 - 12 stat =g_application_run (G_APPLICATION (app), argc, argv); - 13 g_object_unref (app); - 14 return stat; - 15 } +~~~C + 1 int + 2 main (int argc, char **argv) { + 3 GtkApplication *app; + 4 int stat; + 5 + 6 app = gtk_application_new ("com.github.ToshioCP.tfe", G_APPLICATION_HANDLES_OPEN); + 7 + 8 g_signal_connect (app, "startup", G_CALLBACK (tfe_startup), NULL); + 9 g_signal_connect (app, "activate", G_CALLBACK (tfe_activate), NULL); +10 g_signal_connect (app, "open", G_CALLBACK (tfe_open), NULL); +11 +12 stat =g_application_run (G_APPLICATION (app), argc, argv); +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,38 +49,40 @@ What the signal handler needs to do is initialization of the application. The handler is as follows. - 1 static void - 2 tfe_startup (GApplication *application) { - 3 GtkApplication *app = GTK_APPLICATION (application); - 4 GtkApplicationWindow *win; - 5 GtkNotebook *nb; - 6 GtkBuilder *build; - 7 GtkButton *btno; - 8 GtkButton *btnn; - 9 GtkButton *btns; - 10 GtkButton *btnc; - 11 - 12 build = gtk_builder_new_from_resource ("/com/github/ToshioCP/tfe/tfe.ui"); - 13 win = GTK_APPLICATION_WINDOW (gtk_builder_get_object (build, "win")); - 14 nb = GTK_NOTEBOOK (gtk_builder_get_object (build, "nb")); - 15 gtk_window_set_application (GTK_WINDOW (win), app); - 16 btno = GTK_BUTTON (gtk_builder_get_object (build, "btno")); - 17 btnn = GTK_BUTTON (gtk_builder_get_object (build, "btnn")); - 18 btns = GTK_BUTTON (gtk_builder_get_object (build, "btns")); - 19 btnc = GTK_BUTTON (gtk_builder_get_object (build, "btnc")); - 20 g_signal_connect (btno, "clicked", G_CALLBACK (open_clicked), nb); - 21 g_signal_connect (btnn, "clicked", G_CALLBACK (new_clicked), nb); - 22 g_signal_connect (btns, "clicked", G_CALLBACK (save_clicked), nb); - 23 g_signal_connect (btnc, "clicked", G_CALLBACK (close_clicked), nb); - 24 g_object_unref(build); - 25 - 26 GdkDisplay *display; - 27 - 28 display = gtk_widget_get_display (GTK_WIDGET (win)); - 29 GtkCssProvider *provider = gtk_css_provider_new (); - 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 } +~~~C + 1 static void + 2 tfe_startup (GApplication *application) { + 3 GtkApplication *app = GTK_APPLICATION (application); + 4 GtkApplicationWindow *win; + 5 GtkNotebook *nb; + 6 GtkBuilder *build; + 7 GtkButton *btno; + 8 GtkButton *btnn; + 9 GtkButton *btns; +10 GtkButton *btnc; +11 +12 build = gtk_builder_new_from_resource ("/com/github/ToshioCP/tfe/tfe.ui"); +13 win = GTK_APPLICATION_WINDOW (gtk_builder_get_object (build, "win")); +14 nb = GTK_NOTEBOOK (gtk_builder_get_object (build, "nb")); +15 gtk_window_set_application (GTK_WINDOW (win), app); +16 btno = GTK_BUTTON (gtk_builder_get_object (build, "btno")); +17 btnn = GTK_BUTTON (gtk_builder_get_object (build, "btnn")); +18 btns = GTK_BUTTON (gtk_builder_get_object (build, "btns")); +19 btnc = GTK_BUTTON (gtk_builder_get_object (build, "btnc")); +20 g_signal_connect (btno, "clicked", G_CALLBACK (open_clicked), nb); +21 g_signal_connect (btnn, "clicked", G_CALLBACK (new_clicked), nb); +22 g_signal_connect (btns, "clicked", G_CALLBACK (save_clicked), nb); +23 g_signal_connect (btnc, "clicked", G_CALLBACK (close_clicked), nb); +24 g_object_unref(build); +25 +26 GdkDisplay *display; +27 +28 display = gtk_widget_get_display (GTK_WIDGET (win)); +29 GtkCssProvider *provider = gtk_css_provider_new (); +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,39 +172,41 @@ 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. - 1 static void - 2 tfe_activate (GApplication *application) { - 3 GtkApplication *app = GTK_APPLICATION (application); - 4 GtkWidget *win; - 5 GtkWidget *boxv; - 6 GtkNotebook *nb; - 7 - 8 win = GTK_WIDGET (gtk_application_get_active_window (app)); - 9 boxv = gtk_window_get_child (GTK_WINDOW (win)); - 10 nb = GTK_NOTEBOOK (gtk_widget_get_last_child (boxv)); - 11 - 12 notebook_page_new (nb); - 13 gtk_widget_show (GTK_WIDGET (win)); - 14 } - 15 - 16 static void - 17 tfe_open (GApplication *application, GFile ** files, gint n_files, const gchar *hint) { - 18 GtkApplication *app = GTK_APPLICATION (application); - 19 GtkWidget *win; - 20 GtkWidget *boxv; - 21 GtkNotebook *nb; - 22 int i; - 23 - 24 win = GTK_WIDGET (gtk_application_get_active_window (app)); - 25 boxv = gtk_window_get_child (GTK_WINDOW (win)); - 26 nb = GTK_NOTEBOOK (gtk_widget_get_last_child (boxv)); - 27 - 28 for (i = 0; i < n_files; i++) - 29 notebook_page_new_with_file (nb, files[i]); - 30 if (gtk_notebook_get_n_pages (nb) == 0) - 31 notebook_page_new (nb); - 32 gtk_widget_show (win); - 33 } +~~~C + 1 static void + 2 tfe_activate (GApplication *application) { + 3 GtkApplication *app = GTK_APPLICATION (application); + 4 GtkWidget *win; + 5 GtkWidget *boxv; + 6 GtkNotebook *nb; + 7 + 8 win = GTK_WIDGET (gtk_application_get_active_window (app)); + 9 boxv = gtk_window_get_child (GTK_WINDOW (win)); +10 nb = GTK_NOTEBOOK (gtk_widget_get_last_child (boxv)); +11 +12 notebook_page_new (nb); +13 gtk_widget_show (GTK_WIDGET (win)); +14 } +15 +16 static void +17 tfe_open (GApplication *application, GFile ** files, gint n_files, const gchar *hint) { +18 GtkApplication *app = GTK_APPLICATION (application); +19 GtkWidget *win; +20 GtkWidget *boxv; +21 GtkNotebook *nb; +22 int i; +23 +24 win = GTK_WIDGET (gtk_application_get_active_window (app)); +25 boxv = gtk_window_get_child (GTK_WINDOW (win)); +26 nb = GTK_NOTEBOOK (gtk_widget_get_last_child (boxv)); +27 +28 for (i = 0; i < n_files; i++) +29 notebook_page_new_with_file (nb, files[i]); +30 if (gtk_notebook_get_n_pages (nb) == 0) +31 notebook_page_new (nb); +32 gtk_widget_show (win); +33 } +~~~ - 1-14: `tfe_activate`. - 8-10: Get GtkNotebook object. @@ -249,36 +255,38 @@ The second instance immediately quits so shell prompt soon appears. ## a series of handlers correspond to the button signals - 1 static void - 2 open_clicked (GtkWidget *btno, GtkNotebook *nb) { - 3 notebook_page_open (nb); - 4 } - 5 - 6 static void - 7 new_clicked (GtkWidget *btnn, GtkNotebook *nb) { - 8 notebook_page_new (nb); - 9 } - 10 - 11 static void - 12 save_clicked (GtkWidget *btns, GtkNotebook *nb) { - 13 notebook_page_save (nb); - 14 } - 15 - 16 static void - 17 close_clicked (GtkWidget *btnc, GtkNotebook *nb) { - 18 GtkWidget *win; - 19 GtkWidget *boxv; - 20 gint i; - 21 - 22 if (gtk_notebook_get_n_pages (nb) == 1) { - 23 boxv = gtk_widget_get_parent (GTK_WIDGET (nb)); - 24 win = gtk_widget_get_parent (boxv); - 25 gtk_window_destroy (GTK_WINDOW (win)); - 26 } else { - 27 i = gtk_notebook_get_current_page (nb); - 28 gtk_notebook_remove_page (GTK_NOTEBOOK (nb), i); - 29 } - 30 } +~~~C + 1 static void + 2 open_clicked (GtkWidget *btno, GtkNotebook *nb) { + 3 notebook_page_open (nb); + 4 } + 5 + 6 static void + 7 new_clicked (GtkWidget *btnn, GtkNotebook *nb) { + 8 notebook_page_new (nb); + 9 } +10 +11 static void +12 save_clicked (GtkWidget *btns, GtkNotebook *nb) { +13 notebook_page_save (nb); +14 } +15 +16 static void +17 close_clicked (GtkWidget *btnc, GtkNotebook *nb) { +18 GtkWidget *win; +19 GtkWidget *boxv; +20 gint i; +21 +22 if (gtk_notebook_get_n_pages (nb) == 1) { +23 boxv = gtk_widget_get_parent (GTK_WIDGET (nb)); +24 win = gtk_widget_get_parent (boxv); +25 gtk_window_destroy (GTK_WINDOW (win)); +26 } else { +27 i = gtk_notebook_get_current_page (nb); +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,16 +297,18 @@ First, get the top level window and call `gtk_window_destroy`. ## meson.build - 1 project('tfe', 'c') - 2 - 3 gtkdep = dependency('gtk4') - 4 - 5 gnome=import('gnome') - 6 resources = gnome.compile_resources('resources','tfe.gresource.xml') - 7 - 8 sourcefiles=files('tfeapplication.c', 'tfenotebook.c', 'tfetextview.c') - 9 - 10 executable('tfe', sourcefiles, resources, dependencies: gtkdep) +~~~meson + 1 project('tfe', 'c') + 2 + 3 gtkdep = dependency('gtk4') + 4 + 5 gnome=import('gnome') + 6 resources = gnome.compile_resources('resources','tfe.gresource.xml') + 7 + 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. diff --git a/gfm/sec15.md b/gfm/sec15.md index 7253055..ddb3227 100644 --- a/gfm/sec15.md +++ b/gfm/sec15.md @@ -33,606 +33,624 @@ It is a good practice for you to add more features. ## meson.buld - 1 project('tfe', 'c') - 2 - 3 gtkdep = dependency('gtk4') - 4 - 5 gnome=import('gnome') - 6 resources = gnome.compile_resources('resources','tfe.gresource.xml') - 7 - 8 sourcefiles=files('tfeapplication.c', 'tfenotebook.c', 'tfetextview.c') - 9 - 10 executable('tfe', sourcefiles, resources, dependencies: gtkdep) +~~~meson + 1 project('tfe', 'c') + 2 + 3 gtkdep = dependency('gtk4') + 4 + 5 gnome=import('gnome') + 6 resources = gnome.compile_resources('resources','tfe.gresource.xml') + 7 + 8 sourcefiles=files('tfeapplication.c', 'tfenotebook.c', 'tfetextview.c') + 9 +10 executable('tfe', sourcefiles, resources, dependencies: gtkdep) +~~~ ## tfe.gresource.xml - 1 - 2 - 3 - 4 tfe.ui - 5 - 6 +~~~xml +1 +2 +3 +4 tfe.ui +5 +6 +~~~ ## tfe.ui - 1 - 2 - 3 file editor - 4 600 - 5 400 - 6 - 7 - 8 GTK_ORIENTATION_VERTICAL - 9 - 10 - 11 GTK_ORIENTATION_HORIZONTAL - 12 - 13 - 14 10 - 15 - 16 - 17 - 18 - 19 _New - 20 TRUE - 21 - 22 - 23 - 24 - 25 _Open - 26 TRUE - 27 - 28 - 29 - 30 - 31 TRUE - 32 - 33 - 34 - 35 - 36 _Save - 37 TRUE - 38 - 39 - 40 - 41 - 42 _Close - 43 TRUE - 44 - 45 - 46 - 47 - 48 10 - 49 - 50 - 51 - 52 - 53 - 54 - 55 TRUE - 56 TRUE - 57 TRUE - 58 - 59 - 60 - 61 - 62 - 63 - 64 +~~~xml + 1 + 2 + 3 file editor + 4 600 + 5 400 + 6 + 7 + 8 GTK_ORIENTATION_VERTICAL + 9 +10 +11 GTK_ORIENTATION_HORIZONTAL +12 +13 +14 10 +15 +16 +17 +18 +19 _New +20 TRUE +21 +22 +23 +24 +25 _Open +26 TRUE +27 +28 +29 +30 +31 TRUE +32 +33 +34 +35 +36 _Save +37 TRUE +38 +39 +40 +41 +42 _Close +43 TRUE +44 +45 +46 +47 +48 10 +49 +50 +51 +52 +53 +54 +55 TRUE +56 TRUE +57 TRUE +58 +59 +60 +61 +62 +63 +64 +~~~ ## tfe.h - 1 #include - 2 - 3 #include "tfetextview.h" - 4 #include "tfenotebook.h" +~~~C +1 #include +2 +3 #include "tfetextview.h" +4 #include "tfenotebook.h" +~~~ ## tfeapplication.c - 1 #include "tfe.h" - 2 - 3 static void - 4 open_clicked (GtkWidget *btno, GtkNotebook *nb) { - 5 notebook_page_open (nb); - 6 } - 7 - 8 static void - 9 new_clicked (GtkWidget *btnn, GtkNotebook *nb) { - 10 notebook_page_new (nb); - 11 } - 12 - 13 static void - 14 save_clicked (GtkWidget *btns, GtkNotebook *nb) { - 15 notebook_page_save (nb); - 16 } - 17 - 18 static void - 19 close_clicked (GtkWidget *btnc, GtkNotebook *nb) { - 20 GtkWidget *win; - 21 GtkWidget *boxv; - 22 gint i; - 23 - 24 if (gtk_notebook_get_n_pages (nb) == 1) { - 25 boxv = gtk_widget_get_parent (GTK_WIDGET (nb)); - 26 win = gtk_widget_get_parent (boxv); - 27 gtk_window_destroy (GTK_WINDOW (win)); - 28 } else { - 29 i = gtk_notebook_get_current_page (nb); - 30 gtk_notebook_remove_page (GTK_NOTEBOOK (nb), i); - 31 } - 32 } - 33 - 34 static void - 35 tfe_activate (GApplication *application) { - 36 GtkApplication *app = GTK_APPLICATION (application); - 37 GtkWidget *win; - 38 GtkWidget *boxv; - 39 GtkNotebook *nb; - 40 - 41 win = GTK_WIDGET (gtk_application_get_active_window (app)); - 42 boxv = gtk_window_get_child (GTK_WINDOW (win)); - 43 nb = GTK_NOTEBOOK (gtk_widget_get_last_child (boxv)); - 44 - 45 notebook_page_new (nb); - 46 gtk_widget_show (GTK_WIDGET (win)); - 47 } - 48 - 49 static void - 50 tfe_open (GApplication *application, GFile ** files, gint n_files, const gchar *hint) { - 51 GtkApplication *app = GTK_APPLICATION (application); - 52 GtkWidget *win; - 53 GtkWidget *boxv; - 54 GtkNotebook *nb; - 55 int i; - 56 - 57 win = GTK_WIDGET (gtk_application_get_active_window (app)); - 58 boxv = gtk_window_get_child (GTK_WINDOW (win)); - 59 nb = GTK_NOTEBOOK (gtk_widget_get_last_child (boxv)); - 60 - 61 for (i = 0; i < n_files; i++) - 62 notebook_page_new_with_file (nb, files[i]); - 63 if (gtk_notebook_get_n_pages (nb) == 0) - 64 notebook_page_new (nb); - 65 gtk_widget_show (win); - 66 } - 67 - 68 - 69 static void - 70 tfe_startup (GApplication *application) { - 71 GtkApplication *app = GTK_APPLICATION (application); - 72 GtkApplicationWindow *win; - 73 GtkNotebook *nb; - 74 GtkBuilder *build; - 75 GtkButton *btno; - 76 GtkButton *btnn; - 77 GtkButton *btns; - 78 GtkButton *btnc; - 79 - 80 build = gtk_builder_new_from_resource ("/com/github/ToshioCP/tfe/tfe.ui"); - 81 win = GTK_APPLICATION_WINDOW (gtk_builder_get_object (build, "win")); - 82 nb = GTK_NOTEBOOK (gtk_builder_get_object (build, "nb")); - 83 gtk_window_set_application (GTK_WINDOW (win), app); - 84 btno = GTK_BUTTON (gtk_builder_get_object (build, "btno")); - 85 btnn = GTK_BUTTON (gtk_builder_get_object (build, "btnn")); - 86 btns = GTK_BUTTON (gtk_builder_get_object (build, "btns")); - 87 btnc = GTK_BUTTON (gtk_builder_get_object (build, "btnc")); - 88 g_signal_connect (btno, "clicked", G_CALLBACK (open_clicked), nb); - 89 g_signal_connect (btnn, "clicked", G_CALLBACK (new_clicked), nb); - 90 g_signal_connect (btns, "clicked", G_CALLBACK (save_clicked), nb); - 91 g_signal_connect (btnc, "clicked", G_CALLBACK (close_clicked), nb); - 92 g_object_unref(build); - 93 - 94 GdkDisplay *display; - 95 - 96 display = gtk_widget_get_display (GTK_WIDGET (win)); - 97 GtkCssProvider *provider = gtk_css_provider_new (); - 98 gtk_css_provider_load_from_data (provider, "textview {padding: 10px; font-family: monospace; font-size: 12pt;}", -1); - 99 gtk_style_context_add_provider_for_display (display, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_USER); - 100 } - 101 - 102 int - 103 main (int argc, char **argv) { - 104 GtkApplication *app; - 105 int stat; - 106 - 107 app = gtk_application_new ("com.github.ToshioCP.tfe", G_APPLICATION_HANDLES_OPEN); - 108 - 109 g_signal_connect (app, "startup", G_CALLBACK (tfe_startup), NULL); - 110 g_signal_connect (app, "activate", G_CALLBACK (tfe_activate), NULL); - 111 g_signal_connect (app, "open", G_CALLBACK (tfe_open), NULL); - 112 - 113 stat =g_application_run (G_APPLICATION (app), argc, argv); - 114 g_object_unref (app); - 115 return stat; - 116 } - 117 +~~~C + 1 #include "tfe.h" + 2 + 3 static void + 4 open_clicked (GtkWidget *btno, GtkNotebook *nb) { + 5 notebook_page_open (nb); + 6 } + 7 + 8 static void + 9 new_clicked (GtkWidget *btnn, GtkNotebook *nb) { + 10 notebook_page_new (nb); + 11 } + 12 + 13 static void + 14 save_clicked (GtkWidget *btns, GtkNotebook *nb) { + 15 notebook_page_save (nb); + 16 } + 17 + 18 static void + 19 close_clicked (GtkWidget *btnc, GtkNotebook *nb) { + 20 GtkWidget *win; + 21 GtkWidget *boxv; + 22 gint i; + 23 + 24 if (gtk_notebook_get_n_pages (nb) == 1) { + 25 boxv = gtk_widget_get_parent (GTK_WIDGET (nb)); + 26 win = gtk_widget_get_parent (boxv); + 27 gtk_window_destroy (GTK_WINDOW (win)); + 28 } else { + 29 i = gtk_notebook_get_current_page (nb); + 30 gtk_notebook_remove_page (GTK_NOTEBOOK (nb), i); + 31 } + 32 } + 33 + 34 static void + 35 tfe_activate (GApplication *application) { + 36 GtkApplication *app = GTK_APPLICATION (application); + 37 GtkWidget *win; + 38 GtkWidget *boxv; + 39 GtkNotebook *nb; + 40 + 41 win = GTK_WIDGET (gtk_application_get_active_window (app)); + 42 boxv = gtk_window_get_child (GTK_WINDOW (win)); + 43 nb = GTK_NOTEBOOK (gtk_widget_get_last_child (boxv)); + 44 + 45 notebook_page_new (nb); + 46 gtk_widget_show (GTK_WIDGET (win)); + 47 } + 48 + 49 static void + 50 tfe_open (GApplication *application, GFile ** files, gint n_files, const gchar *hint) { + 51 GtkApplication *app = GTK_APPLICATION (application); + 52 GtkWidget *win; + 53 GtkWidget *boxv; + 54 GtkNotebook *nb; + 55 int i; + 56 + 57 win = GTK_WIDGET (gtk_application_get_active_window (app)); + 58 boxv = gtk_window_get_child (GTK_WINDOW (win)); + 59 nb = GTK_NOTEBOOK (gtk_widget_get_last_child (boxv)); + 60 + 61 for (i = 0; i < n_files; i++) + 62 notebook_page_new_with_file (nb, files[i]); + 63 if (gtk_notebook_get_n_pages (nb) == 0) + 64 notebook_page_new (nb); + 65 gtk_widget_show (win); + 66 } + 67 + 68 + 69 static void + 70 tfe_startup (GApplication *application) { + 71 GtkApplication *app = GTK_APPLICATION (application); + 72 GtkApplicationWindow *win; + 73 GtkNotebook *nb; + 74 GtkBuilder *build; + 75 GtkButton *btno; + 76 GtkButton *btnn; + 77 GtkButton *btns; + 78 GtkButton *btnc; + 79 + 80 build = gtk_builder_new_from_resource ("/com/github/ToshioCP/tfe/tfe.ui"); + 81 win = GTK_APPLICATION_WINDOW (gtk_builder_get_object (build, "win")); + 82 nb = GTK_NOTEBOOK (gtk_builder_get_object (build, "nb")); + 83 gtk_window_set_application (GTK_WINDOW (win), app); + 84 btno = GTK_BUTTON (gtk_builder_get_object (build, "btno")); + 85 btnn = GTK_BUTTON (gtk_builder_get_object (build, "btnn")); + 86 btns = GTK_BUTTON (gtk_builder_get_object (build, "btns")); + 87 btnc = GTK_BUTTON (gtk_builder_get_object (build, "btnc")); + 88 g_signal_connect (btno, "clicked", G_CALLBACK (open_clicked), nb); + 89 g_signal_connect (btnn, "clicked", G_CALLBACK (new_clicked), nb); + 90 g_signal_connect (btns, "clicked", G_CALLBACK (save_clicked), nb); + 91 g_signal_connect (btnc, "clicked", G_CALLBACK (close_clicked), nb); + 92 g_object_unref(build); + 93 + 94 GdkDisplay *display; + 95 + 96 display = gtk_widget_get_display (GTK_WIDGET (win)); + 97 GtkCssProvider *provider = gtk_css_provider_new (); + 98 gtk_css_provider_load_from_data (provider, "textview {padding: 10px; font-family: monospace; font-size: 12pt;}", -1); + 99 gtk_style_context_add_provider_for_display (display, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_USER); +100 } +101 +102 int +103 main (int argc, char **argv) { +104 GtkApplication *app; +105 int stat; +106 +107 app = gtk_application_new ("com.github.ToshioCP.tfe", G_APPLICATION_HANDLES_OPEN); +108 +109 g_signal_connect (app, "startup", G_CALLBACK (tfe_startup), NULL); +110 g_signal_connect (app, "activate", G_CALLBACK (tfe_activate), NULL); +111 g_signal_connect (app, "open", G_CALLBACK (tfe_open), NULL); +112 +113 stat =g_application_run (G_APPLICATION (app), argc, argv); +114 g_object_unref (app); +115 return stat; +116 } +117 +~~~ ## tfenotebook.h - 1 void - 2 notebook_page_save(GtkNotebook *nb); - 3 - 4 void - 5 notebook_page_open (GtkNotebook *nb); - 6 - 7 void - 8 notebook_page_new_with_file (GtkNotebook *nb, GFile *file); - 9 - 10 void - 11 notebook_page_new (GtkNotebook *nb); - 12 +~~~C + 1 void + 2 notebook_page_save(GtkNotebook *nb); + 3 + 4 void + 5 notebook_page_open (GtkNotebook *nb); + 6 + 7 void + 8 notebook_page_new_with_file (GtkNotebook *nb, GFile *file); + 9 +10 void +11 notebook_page_new (GtkNotebook *nb); +12 +~~~ ## tfenotebook.c - 1 #include "tfe.h" - 2 - 3 /* The returned string should be freed with g_free() when no longer needed. */ - 4 static gchar* - 5 get_untitled () { - 6 static int c = -1; - 7 if (++c == 0) - 8 return g_strdup_printf("Untitled"); - 9 else - 10 return g_strdup_printf ("Untitled%u", c); - 11 } - 12 - 13 static void - 14 file_changed (TfeTextView *tv, GtkNotebook *nb) { - 15 GFile *file; - 16 char *filename; - 17 GtkWidget *scr; - 18 GtkWidget *label; - 19 - 20 file = tfe_text_view_get_file (tv); - 21 scr = gtk_widget_get_parent (GTK_WIDGET (tv)); - 22 if (G_IS_FILE (file)) - 23 filename = g_file_get_basename (file); - 24 else - 25 filename = get_untitled (); - 26 label = gtk_label_new (filename); - 27 gtk_notebook_set_tab_label (nb, scr, label); - 28 g_object_unref (file); - 29 g_free (filename); - 30 } - 31 - 32 /* Save the contents in the current page */ - 33 void - 34 notebook_page_save(GtkNotebook *nb) { - 35 gint i; - 36 GtkWidget *scr; - 37 GtkWidget *tv; - 38 - 39 i = gtk_notebook_get_current_page (nb); - 40 scr = gtk_notebook_get_nth_page (nb, i); - 41 tv = gtk_scrolled_window_get_child (GTK_SCROLLED_WINDOW (scr)); - 42 tfe_text_view_save (TFE_TEXT_VIEW (tv)); - 43 } - 44 - 45 static void - 46 notebook_page_build (GtkNotebook *nb, GtkWidget *tv, char *filename) { - 47 GtkWidget *scr; - 48 GtkNotebookPage *nbp; - 49 GtkWidget *lab; - 50 gint i; - 51 scr = gtk_scrolled_window_new (); - 52 - 53 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv); - 54 lab = gtk_label_new (filename); - 55 i = gtk_notebook_append_page (nb, scr, lab); - 56 nbp = gtk_notebook_get_page (nb, scr); - 57 g_object_set (nbp, "tab-expand", TRUE, NULL); - 58 gtk_notebook_set_current_page (nb, i); - 59 g_signal_connect (GTK_TEXT_VIEW (tv), "change-file", G_CALLBACK (file_changed), nb); - 60 } - 61 - 62 static void - 63 open_response (TfeTextView *tv, gint response, GtkNotebook *nb) { - 64 GFile *file; - 65 char *filename; - 66 - 67 if (response != TFE_OPEN_RESPONSE_SUCCESS) { - 68 g_object_ref_sink (tv); - 69 g_object_unref (tv); - 70 }else if (! G_IS_FILE (file = tfe_text_view_get_file (tv))) { - 71 g_object_ref_sink (tv); - 72 g_object_unref (tv); - 73 }else { - 74 filename = g_file_get_basename (file); - 75 g_object_unref (file); - 76 notebook_page_build (nb, GTK_WIDGET (tv), filename); - 77 } - 78 } - 79 - 80 void - 81 notebook_page_open (GtkNotebook *nb) { - 82 g_return_if_fail(GTK_IS_NOTEBOOK (nb)); - 83 - 84 GtkWidget *tv; - 85 - 86 tv = tfe_text_view_new (); - 87 g_signal_connect (TFE_TEXT_VIEW (tv), "open-response", G_CALLBACK (open_response), nb); - 88 tfe_text_view_open (TFE_TEXT_VIEW (tv), gtk_widget_get_ancestor (GTK_WIDGET (nb), GTK_TYPE_WINDOW)); - 89 } - 90 - 91 void - 92 notebook_page_new_with_file (GtkNotebook *nb, GFile *file) { - 93 g_return_if_fail(GTK_IS_NOTEBOOK (nb)); - 94 g_return_if_fail(G_IS_FILE (file)); - 95 - 96 GtkWidget *tv; - 97 char *filename; - 98 - 99 if ((tv = tfe_text_view_new_with_file (file)) == NULL) - 100 return; /* read error */ - 101 filename = g_file_get_basename (file); - 102 notebook_page_build (nb, tv, filename); - 103 } - 104 - 105 void - 106 notebook_page_new (GtkNotebook *nb) { - 107 g_return_if_fail(GTK_IS_NOTEBOOK (nb)); - 108 - 109 GtkWidget *tv; - 110 char *filename; - 111 - 112 tv = tfe_text_view_new (); - 113 filename = get_untitled (); - 114 notebook_page_build (nb, tv, filename); - 115 } - 116 +~~~C + 1 #include "tfe.h" + 2 + 3 /* The returned string should be freed with g_free() when no longer needed. */ + 4 static gchar* + 5 get_untitled () { + 6 static int c = -1; + 7 if (++c == 0) + 8 return g_strdup_printf("Untitled"); + 9 else + 10 return g_strdup_printf ("Untitled%u", c); + 11 } + 12 + 13 static void + 14 file_changed (TfeTextView *tv, GtkNotebook *nb) { + 15 GFile *file; + 16 char *filename; + 17 GtkWidget *scr; + 18 GtkWidget *label; + 19 + 20 file = tfe_text_view_get_file (tv); + 21 scr = gtk_widget_get_parent (GTK_WIDGET (tv)); + 22 if (G_IS_FILE (file)) + 23 filename = g_file_get_basename (file); + 24 else + 25 filename = get_untitled (); + 26 label = gtk_label_new (filename); + 27 gtk_notebook_set_tab_label (nb, scr, label); + 28 g_object_unref (file); + 29 g_free (filename); + 30 } + 31 + 32 /* Save the contents in the current page */ + 33 void + 34 notebook_page_save(GtkNotebook *nb) { + 35 gint i; + 36 GtkWidget *scr; + 37 GtkWidget *tv; + 38 + 39 i = gtk_notebook_get_current_page (nb); + 40 scr = gtk_notebook_get_nth_page (nb, i); + 41 tv = gtk_scrolled_window_get_child (GTK_SCROLLED_WINDOW (scr)); + 42 tfe_text_view_save (TFE_TEXT_VIEW (tv)); + 43 } + 44 + 45 static void + 46 notebook_page_build (GtkNotebook *nb, GtkWidget *tv, char *filename) { + 47 GtkWidget *scr; + 48 GtkNotebookPage *nbp; + 49 GtkWidget *lab; + 50 gint i; + 51 scr = gtk_scrolled_window_new (); + 52 + 53 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv); + 54 lab = gtk_label_new (filename); + 55 i = gtk_notebook_append_page (nb, scr, lab); + 56 nbp = gtk_notebook_get_page (nb, scr); + 57 g_object_set (nbp, "tab-expand", TRUE, NULL); + 58 gtk_notebook_set_current_page (nb, i); + 59 g_signal_connect (GTK_TEXT_VIEW (tv), "change-file", G_CALLBACK (file_changed), nb); + 60 } + 61 + 62 static void + 63 open_response (TfeTextView *tv, gint response, GtkNotebook *nb) { + 64 GFile *file; + 65 char *filename; + 66 + 67 if (response != TFE_OPEN_RESPONSE_SUCCESS) { + 68 g_object_ref_sink (tv); + 69 g_object_unref (tv); + 70 }else if (! G_IS_FILE (file = tfe_text_view_get_file (tv))) { + 71 g_object_ref_sink (tv); + 72 g_object_unref (tv); + 73 }else { + 74 filename = g_file_get_basename (file); + 75 g_object_unref (file); + 76 notebook_page_build (nb, GTK_WIDGET (tv), filename); + 77 } + 78 } + 79 + 80 void + 81 notebook_page_open (GtkNotebook *nb) { + 82 g_return_if_fail(GTK_IS_NOTEBOOK (nb)); + 83 + 84 GtkWidget *tv; + 85 + 86 tv = tfe_text_view_new (); + 87 g_signal_connect (TFE_TEXT_VIEW (tv), "open-response", G_CALLBACK (open_response), nb); + 88 tfe_text_view_open (TFE_TEXT_VIEW (tv), gtk_widget_get_ancestor (GTK_WIDGET (nb), GTK_TYPE_WINDOW)); + 89 } + 90 + 91 void + 92 notebook_page_new_with_file (GtkNotebook *nb, GFile *file) { + 93 g_return_if_fail(GTK_IS_NOTEBOOK (nb)); + 94 g_return_if_fail(G_IS_FILE (file)); + 95 + 96 GtkWidget *tv; + 97 char *filename; + 98 + 99 if ((tv = tfe_text_view_new_with_file (file)) == NULL) +100 return; /* read error */ +101 filename = g_file_get_basename (file); +102 notebook_page_build (nb, tv, filename); +103 } +104 +105 void +106 notebook_page_new (GtkNotebook *nb) { +107 g_return_if_fail(GTK_IS_NOTEBOOK (nb)); +108 +109 GtkWidget *tv; +110 char *filename; +111 +112 tv = tfe_text_view_new (); +113 filename = get_untitled (); +114 notebook_page_build (nb, tv, filename); +115 } +116 +~~~ ## tfetextview.h - 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 - 4 /* "open-response" signal response */ - 5 enum - 6 { - 7 TFE_OPEN_RESPONSE_SUCCESS, - 8 TFE_OPEN_RESPONSE_CANCEL, - 9 TFE_OPEN_RESPONSE_ERROR - 10 }; - 11 - 12 GFile * - 13 tfe_text_view_get_file (TfeTextView *tv); - 14 - 15 void - 16 tfe_text_view_open (TfeTextView *tv, GtkWidget *win); - 17 - 18 void - 19 tfe_text_view_save (TfeTextView *tv); - 20 - 21 void - 22 tfe_text_view_saveas (TfeTextView *tv); - 23 - 24 GtkWidget * - 25 tfe_text_view_new_with_file (GFile *file); - 26 - 27 GtkWidget * - 28 tfe_text_view_new (void); - 29 +~~~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 + 4 /* "open-response" signal response */ + 5 enum + 6 { + 7 TFE_OPEN_RESPONSE_SUCCESS, + 8 TFE_OPEN_RESPONSE_CANCEL, + 9 TFE_OPEN_RESPONSE_ERROR +10 }; +11 +12 GFile * +13 tfe_text_view_get_file (TfeTextView *tv); +14 +15 void +16 tfe_text_view_open (TfeTextView *tv, GtkWidget *win); +17 +18 void +19 tfe_text_view_save (TfeTextView *tv); +20 +21 void +22 tfe_text_view_saveas (TfeTextView *tv); +23 +24 GtkWidget * +25 tfe_text_view_new_with_file (GFile *file); +26 +27 GtkWidget * +28 tfe_text_view_new (void); +29 +~~~ ## tfetextview.c - 1 #include "tfe.h" - 2 - 3 struct _TfeTextView - 4 { - 5 GtkTextView parent; - 6 GFile *file; - 7 }; - 8 - 9 G_DEFINE_TYPE (TfeTextView, tfe_text_view, GTK_TYPE_TEXT_VIEW); - 10 - 11 enum { - 12 CHANGE_FILE, - 13 OPEN_RESPONSE, - 14 NUMBER_OF_SIGNALS - 15 }; - 16 - 17 static guint tfe_text_view_signals[NUMBER_OF_SIGNALS]; - 18 - 19 static void - 20 tfe_text_view_dispose (GObject *gobject) { - 21 TfeTextView *tv = TFE_TEXT_VIEW (gobject); - 22 - 23 if (G_IS_FILE (tv->file)) - 24 g_clear_object (&tv->file); - 25 - 26 G_OBJECT_CLASS (tfe_text_view_parent_class)->dispose (gobject); - 27 } - 28 - 29 static void - 30 tfe_text_view_init (TfeTextView *tv) { - 31 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); - 32 - 33 tv->file = NULL; - 34 gtk_text_buffer_set_modified (tb, FALSE); - 35 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR); - 36 } - 37 - 38 static void - 39 tfe_text_view_class_init (TfeTextViewClass *class) { - 40 GObjectClass *object_class = G_OBJECT_CLASS (class); - 41 - 42 object_class->dispose = tfe_text_view_dispose; - 43 tfe_text_view_signals[CHANGE_FILE] = g_signal_newv ("change-file", - 44 G_TYPE_FROM_CLASS (class), - 45 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, - 46 NULL /* closure */, - 47 NULL /* accumulator */, - 48 NULL /* accumulator data */, - 49 NULL /* C marshaller */, - 50 G_TYPE_NONE /* return_type */, - 51 0 /* n_params */, - 52 NULL /* param_types */); - 53 GType param_types[] = {G_TYPE_INT}; - 54 tfe_text_view_signals[OPEN_RESPONSE] = g_signal_newv ("open-response", - 55 G_TYPE_FROM_CLASS (class), - 56 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, - 57 NULL /* closure */, - 58 NULL /* accumulator */, - 59 NULL /* accumulator data */, - 60 NULL /* C marshaller */, - 61 G_TYPE_NONE /* return_type */, - 62 1 /* n_params */, - 63 param_types); - 64 } - 65 - 66 GFile * - 67 tfe_text_view_get_file (TfeTextView *tv) { - 68 g_return_val_if_fail (TFE_IS_TEXT_VIEW (tv), NULL); - 69 - 70 return g_file_dup (tv->file); - 71 } - 72 - 73 static void - 74 open_dialog_response(GtkWidget *dialog, gint response, TfeTextView *tv) { - 75 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); - 76 GFile *file; - 77 char *contents; - 78 gsize length; - 79 GtkWidget *message_dialog; - 80 GError *err = NULL; - 81 - 82 if (response != GTK_RESPONSE_ACCEPT) - 83 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_CANCEL); - 84 else if (! G_IS_FILE (file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog)))) - 85 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_ERROR); - 86 else if (! g_file_load_contents (file, NULL, &contents, &length, NULL, &err)) { /* read error */ - 87 if (G_IS_FILE (file)) - 88 g_object_unref (file); - 89 message_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog), GTK_DIALOG_MODAL, - 90 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, - 91 "%s.\n", err->message); - 92 g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL); - 93 gtk_widget_show (message_dialog); - 94 g_error_free (err); - 95 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_ERROR); - 96 } else { - 97 gtk_text_buffer_set_text (tb, contents, length); - 98 g_free (contents); - 99 if (G_IS_FILE (tv->file)) - 100 g_object_unref (tv->file); - 101 tv->file = file; - 102 gtk_text_buffer_set_modified (tb, FALSE); - 103 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_SUCCESS); - 104 } - 105 gtk_window_destroy (GTK_WINDOW (dialog)); - 106 } - 107 - 108 void - 109 tfe_text_view_open (TfeTextView *tv, GtkWidget *win) { - 110 g_return_if_fail (TFE_IS_TEXT_VIEW (tv)); - 111 g_return_if_fail (GTK_IS_WINDOW (win)); - 112 - 113 GtkWidget *dialog; - 114 - 115 dialog = gtk_file_chooser_dialog_new ("Open file", GTK_WINDOW (win), GTK_FILE_CHOOSER_ACTION_OPEN, - 116 "Cancel", GTK_RESPONSE_CANCEL, - 117 "Open", GTK_RESPONSE_ACCEPT, - 118 NULL); - 119 g_signal_connect (dialog, "response", G_CALLBACK (open_dialog_response), tv); - 120 gtk_widget_show (dialog); - 121 } - 122 - 123 static void - 124 saveas_dialog_response (GtkWidget *dialog, gint response, TfeTextView *tv) { - 125 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); - 126 GFile *file; - 127 - 128 if (response == GTK_RESPONSE_ACCEPT) { - 129 file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog)); - 130 if (G_IS_FILE(file)) { - 131 tv->file = file; - 132 gtk_text_buffer_set_modified (tb, TRUE); - 133 g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0); - 134 tfe_text_view_save (TFE_TEXT_VIEW (tv)); - 135 } - 136 } - 137 gtk_window_destroy (GTK_WINDOW (dialog)); - 138 } - 139 - 140 void - 141 tfe_text_view_save (TfeTextView *tv) { - 142 g_return_if_fail (TFE_IS_TEXT_VIEW (tv)); - 143 - 144 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); - 145 GtkTextIter start_iter; - 146 GtkTextIter end_iter; - 147 gchar *contents; - 148 GtkWidget *message_dialog; - 149 GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW); - 150 GError *err = NULL; - 151 - 152 if (! gtk_text_buffer_get_modified (tb)) - 153 return; /* no necessary to save it */ - 154 else if (tv->file == NULL) - 155 tfe_text_view_saveas (tv); - 156 else { - 157 gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter); - 158 contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE); - 159 if (g_file_replace_contents (tv->file, contents, strlen (contents), NULL, TRUE, G_FILE_CREATE_NONE, NULL, NULL, &err)) - 160 gtk_text_buffer_set_modified (tb, FALSE); - 161 else { - 162 /* It is possible that tv->file is broken. */ - 163 /* It is a good idea to set tv->file to NULL. */ - 164 if (G_IS_FILE (tv->file)) - 165 g_object_unref (tv->file); - 166 tv->file =NULL; - 167 g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0); - 168 gtk_text_buffer_set_modified (tb, TRUE); - 169 message_dialog = gtk_message_dialog_new (GTK_WINDOW (win), GTK_DIALOG_MODAL, - 170 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, - 171 "%s.\n", err->message); - 172 g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL); - 173 gtk_widget_show (message_dialog); - 174 g_error_free (err); - 175 } - 176 } - 177 } - 178 - 179 void - 180 tfe_text_view_saveas (TfeTextView *tv) { - 181 g_return_if_fail (TFE_IS_TEXT_VIEW (tv)); - 182 - 183 GtkWidget *dialog; - 184 GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW); - 185 - 186 dialog = gtk_file_chooser_dialog_new ("Save file", GTK_WINDOW (win), GTK_FILE_CHOOSER_ACTION_SAVE, - 187 "_Cancel", GTK_RESPONSE_CANCEL, - 188 "_Save", GTK_RESPONSE_ACCEPT, - 189 NULL); - 190 g_signal_connect (dialog, "response", G_CALLBACK (saveas_dialog_response), tv); - 191 gtk_widget_show (dialog); - 192 } - 193 - 194 GtkWidget * - 195 tfe_text_view_new_with_file (GFile *file) { - 196 g_return_val_if_fail (G_IS_FILE (file), NULL); - 197 - 198 GtkWidget *tv; - 199 GtkTextBuffer *tb; - 200 char *contents; - 201 gsize length; - 202 - 203 if (! g_file_load_contents (file, NULL, &contents, &length, NULL, NULL)) /* read error */ - 204 return NULL; - 205 - 206 tv = tfe_text_view_new(); - 207 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); - 208 gtk_text_buffer_set_text (tb, contents, length); - 209 g_free (contents); - 210 TFE_TEXT_VIEW (tv)->file = g_file_dup (file); - 211 return tv; - 212 } - 213 - 214 GtkWidget * - 215 tfe_text_view_new (void) { - 216 return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL)); - 217 } - 218 +~~~C + 1 #include "tfe.h" + 2 + 3 struct _TfeTextView + 4 { + 5 GtkTextView parent; + 6 GFile *file; + 7 }; + 8 + 9 G_DEFINE_TYPE (TfeTextView, tfe_text_view, GTK_TYPE_TEXT_VIEW); + 10 + 11 enum { + 12 CHANGE_FILE, + 13 OPEN_RESPONSE, + 14 NUMBER_OF_SIGNALS + 15 }; + 16 + 17 static guint tfe_text_view_signals[NUMBER_OF_SIGNALS]; + 18 + 19 static void + 20 tfe_text_view_dispose (GObject *gobject) { + 21 TfeTextView *tv = TFE_TEXT_VIEW (gobject); + 22 + 23 if (G_IS_FILE (tv->file)) + 24 g_clear_object (&tv->file); + 25 + 26 G_OBJECT_CLASS (tfe_text_view_parent_class)->dispose (gobject); + 27 } + 28 + 29 static void + 30 tfe_text_view_init (TfeTextView *tv) { + 31 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); + 32 + 33 tv->file = NULL; + 34 gtk_text_buffer_set_modified (tb, FALSE); + 35 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR); + 36 } + 37 + 38 static void + 39 tfe_text_view_class_init (TfeTextViewClass *class) { + 40 GObjectClass *object_class = G_OBJECT_CLASS (class); + 41 + 42 object_class->dispose = tfe_text_view_dispose; + 43 tfe_text_view_signals[CHANGE_FILE] = g_signal_newv ("change-file", + 44 G_TYPE_FROM_CLASS (class), + 45 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, + 46 NULL /* closure */, + 47 NULL /* accumulator */, + 48 NULL /* accumulator data */, + 49 NULL /* C marshaller */, + 50 G_TYPE_NONE /* return_type */, + 51 0 /* n_params */, + 52 NULL /* param_types */); + 53 GType param_types[] = {G_TYPE_INT}; + 54 tfe_text_view_signals[OPEN_RESPONSE] = g_signal_newv ("open-response", + 55 G_TYPE_FROM_CLASS (class), + 56 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, + 57 NULL /* closure */, + 58 NULL /* accumulator */, + 59 NULL /* accumulator data */, + 60 NULL /* C marshaller */, + 61 G_TYPE_NONE /* return_type */, + 62 1 /* n_params */, + 63 param_types); + 64 } + 65 + 66 GFile * + 67 tfe_text_view_get_file (TfeTextView *tv) { + 68 g_return_val_if_fail (TFE_IS_TEXT_VIEW (tv), NULL); + 69 + 70 return g_file_dup (tv->file); + 71 } + 72 + 73 static void + 74 open_dialog_response(GtkWidget *dialog, gint response, TfeTextView *tv) { + 75 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); + 76 GFile *file; + 77 char *contents; + 78 gsize length; + 79 GtkWidget *message_dialog; + 80 GError *err = NULL; + 81 + 82 if (response != GTK_RESPONSE_ACCEPT) + 83 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_CANCEL); + 84 else if (! G_IS_FILE (file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog)))) + 85 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_ERROR); + 86 else if (! g_file_load_contents (file, NULL, &contents, &length, NULL, &err)) { /* read error */ + 87 if (G_IS_FILE (file)) + 88 g_object_unref (file); + 89 message_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog), GTK_DIALOG_MODAL, + 90 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, + 91 "%s.\n", err->message); + 92 g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL); + 93 gtk_widget_show (message_dialog); + 94 g_error_free (err); + 95 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_ERROR); + 96 } else { + 97 gtk_text_buffer_set_text (tb, contents, length); + 98 g_free (contents); + 99 if (G_IS_FILE (tv->file)) +100 g_object_unref (tv->file); +101 tv->file = file; +102 gtk_text_buffer_set_modified (tb, FALSE); +103 g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_SUCCESS); +104 } +105 gtk_window_destroy (GTK_WINDOW (dialog)); +106 } +107 +108 void +109 tfe_text_view_open (TfeTextView *tv, GtkWidget *win) { +110 g_return_if_fail (TFE_IS_TEXT_VIEW (tv)); +111 g_return_if_fail (GTK_IS_WINDOW (win)); +112 +113 GtkWidget *dialog; +114 +115 dialog = gtk_file_chooser_dialog_new ("Open file", GTK_WINDOW (win), GTK_FILE_CHOOSER_ACTION_OPEN, +116 "Cancel", GTK_RESPONSE_CANCEL, +117 "Open", GTK_RESPONSE_ACCEPT, +118 NULL); +119 g_signal_connect (dialog, "response", G_CALLBACK (open_dialog_response), tv); +120 gtk_widget_show (dialog); +121 } +122 +123 static void +124 saveas_dialog_response (GtkWidget *dialog, gint response, TfeTextView *tv) { +125 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); +126 GFile *file; +127 +128 if (response == GTK_RESPONSE_ACCEPT) { +129 file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog)); +130 if (G_IS_FILE(file)) { +131 tv->file = file; +132 gtk_text_buffer_set_modified (tb, TRUE); +133 g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0); +134 tfe_text_view_save (TFE_TEXT_VIEW (tv)); +135 } +136 } +137 gtk_window_destroy (GTK_WINDOW (dialog)); +138 } +139 +140 void +141 tfe_text_view_save (TfeTextView *tv) { +142 g_return_if_fail (TFE_IS_TEXT_VIEW (tv)); +143 +144 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); +145 GtkTextIter start_iter; +146 GtkTextIter end_iter; +147 gchar *contents; +148 GtkWidget *message_dialog; +149 GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW); +150 GError *err = NULL; +151 +152 if (! gtk_text_buffer_get_modified (tb)) +153 return; /* no necessary to save it */ +154 else if (tv->file == NULL) +155 tfe_text_view_saveas (tv); +156 else { +157 gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter); +158 contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE); +159 if (g_file_replace_contents (tv->file, contents, strlen (contents), NULL, TRUE, G_FILE_CREATE_NONE, NULL, NULL, &err)) +160 gtk_text_buffer_set_modified (tb, FALSE); +161 else { +162 /* It is possible that tv->file is broken. */ +163 /* It is a good idea to set tv->file to NULL. */ +164 if (G_IS_FILE (tv->file)) +165 g_object_unref (tv->file); +166 tv->file =NULL; +167 g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0); +168 gtk_text_buffer_set_modified (tb, TRUE); +169 message_dialog = gtk_message_dialog_new (GTK_WINDOW (win), GTK_DIALOG_MODAL, +170 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, +171 "%s.\n", err->message); +172 g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL); +173 gtk_widget_show (message_dialog); +174 g_error_free (err); +175 } +176 } +177 } +178 +179 void +180 tfe_text_view_saveas (TfeTextView *tv) { +181 g_return_if_fail (TFE_IS_TEXT_VIEW (tv)); +182 +183 GtkWidget *dialog; +184 GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_WINDOW); +185 +186 dialog = gtk_file_chooser_dialog_new ("Save file", GTK_WINDOW (win), GTK_FILE_CHOOSER_ACTION_SAVE, +187 "_Cancel", GTK_RESPONSE_CANCEL, +188 "_Save", GTK_RESPONSE_ACCEPT, +189 NULL); +190 g_signal_connect (dialog, "response", G_CALLBACK (saveas_dialog_response), tv); +191 gtk_widget_show (dialog); +192 } +193 +194 GtkWidget * +195 tfe_text_view_new_with_file (GFile *file) { +196 g_return_val_if_fail (G_IS_FILE (file), NULL); +197 +198 GtkWidget *tv; +199 GtkTextBuffer *tb; +200 char *contents; +201 gsize length; +202 +203 if (! g_file_load_contents (file, NULL, &contents, &length, NULL, NULL)) /* read error */ +204 return NULL; +205 +206 tv = tfe_text_view_new(); +207 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); +208 gtk_text_buffer_set_text (tb, contents, length); +209 g_free (contents); +210 TFE_TEXT_VIEW (tv)->file = g_file_dup (file); +211 return tv; +212 } +213 +214 GtkWidget * +215 tfe_text_view_new (void) { +216 return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL)); +217 } +218 +~~~ ## Total number of lines, words and charcters diff --git a/gfm/sec16.md b/gfm/sec16.md index d823bd1..2572a86 100644 --- a/gfm/sec16.md +++ b/gfm/sec16.md @@ -119,53 +119,55 @@ So, if the action is activated, the handler will be invoked. The following is a simple example of menus and actions. - 1 #include - 2 - 3 static void - 4 quit_activated(GSimpleAction *action, GVariant *parameter, gpointer app) - 5 { - 6 g_application_quit (G_APPLICATION(app)); - 7 } - 8 - 9 static void - 10 on_activate (GApplication *app, gpointer user_data) { - 11 GtkWidget *win = gtk_application_window_new (GTK_APPLICATION (app)); - 12 gtk_window_set_title (GTK_WINDOW (win), "menu1"); - 13 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300); - 14 - 15 GSimpleAction *act_quit = g_simple_action_new ("quit", NULL); - 16 g_action_map_add_action (G_ACTION_MAP (app), G_ACTION (act_quit)); - 17 g_signal_connect (act_quit, "activate", G_CALLBACK (quit_activated), app); - 18 - 19 GMenu *menubar = g_menu_new (); - 20 GMenuItem *menu_item_menu = g_menu_item_new ("Menu", NULL); - 21 GMenu *menu = g_menu_new (); - 22 GMenuItem *menu_item_quit = g_menu_item_new ("Quit", "app.quit"); - 23 g_menu_append_item (menu, menu_item_quit); - 24 g_object_unref (menu_item_quit); - 25 g_menu_item_set_submenu (menu_item_menu, G_MENU_MODEL (menu)); - 26 g_menu_append_item (menubar, menu_item_menu); - 27 g_object_unref (menu_item_menu); - 28 - 29 gtk_application_set_menubar (GTK_APPLICATION (app), G_MENU_MODEL (menubar)); - 30 gtk_application_window_set_show_menubar (GTK_APPLICATION_WINDOW (win), TRUE); - 31 gtk_window_present (GTK_WINDOW (win)); - 32 /* gtk_widget_show (win); is also OKay instead of gtk_window_present. */ - 33 } - 34 - 35 int - 36 main (int argc, char **argv) { - 37 GtkApplication *app; - 38 int stat; - 39 - 40 app = gtk_application_new ("com.github.ToshioCP.menu1", G_APPLICATION_FLAGS_NONE); - 41 g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL); - 42 - 43 stat =g_application_run (G_APPLICATION (app), argc, argv); - 44 g_object_unref (app); - 45 return stat; - 46 } - 47 +~~~C + 1 #include + 2 + 3 static void + 4 quit_activated(GSimpleAction *action, GVariant *parameter, gpointer app) + 5 { + 6 g_application_quit (G_APPLICATION(app)); + 7 } + 8 + 9 static void +10 on_activate (GApplication *app, gpointer user_data) { +11 GtkWidget *win = gtk_application_window_new (GTK_APPLICATION (app)); +12 gtk_window_set_title (GTK_WINDOW (win), "menu1"); +13 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300); +14 +15 GSimpleAction *act_quit = g_simple_action_new ("quit", NULL); +16 g_action_map_add_action (G_ACTION_MAP (app), G_ACTION (act_quit)); +17 g_signal_connect (act_quit, "activate", G_CALLBACK (quit_activated), app); +18 +19 GMenu *menubar = g_menu_new (); +20 GMenuItem *menu_item_menu = g_menu_item_new ("Menu", NULL); +21 GMenu *menu = g_menu_new (); +22 GMenuItem *menu_item_quit = g_menu_item_new ("Quit", "app.quit"); +23 g_menu_append_item (menu, menu_item_quit); +24 g_object_unref (menu_item_quit); +25 g_menu_item_set_submenu (menu_item_menu, G_MENU_MODEL (menu)); +26 g_menu_append_item (menubar, menu_item_menu); +27 g_object_unref (menu_item_menu); +28 +29 gtk_application_set_menubar (GTK_APPLICATION (app), G_MENU_MODEL (menubar)); +30 gtk_application_window_set_show_menubar (GTK_APPLICATION_WINDOW (win), TRUE); +31 gtk_window_present (GTK_WINDOW (win)); +32 /* gtk_widget_show (win); is also OKay instead of gtk_window_present. */ +33 } +34 +35 int +36 main (int argc, char **argv) { +37 GtkApplication *app; +38 int stat; +39 +40 app = gtk_application_new ("com.github.ToshioCP.menu1", G_APPLICATION_FLAGS_NONE); +41 g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL); +42 +43 stat =g_application_run (G_APPLICATION (app), argc, argv); +44 g_object_unref (app); +45 return stat; +46 } +47 +~~~ - 3-7: `quit_activated` is a handler of an action `act_quit`. Handlers of actions have three parameters. diff --git a/gfm/sec17.md b/gfm/sec17.md index 8cc2214..b0f63bf 100644 --- a/gfm/sec17.md +++ b/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,14 +190,16 @@ When GVariantType is generated, the type is expressed by the string. The following program is a simple example. It finally output the string "s". - 1 #include - 2 - 3 int - 4 main (int argc, char **argv) { - 5 GVariantType *vtype = g_variant_type_new ("s"); - 6 const gchar *type_string = g_variant_type_peek_string (vtype); - 7 g_print ("%s\n",type_string); - 8 } +~~~C +1 #include +2 +3 int +4 main (int argc, char **argv) { +5 GVariantType *vtype = g_variant_type_new ("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,111 +222,113 @@ And the radio button of the selected menu turns on. The code is as follows. - 1 #include - 2 - 3 static GtkCssProvider *provider; - 4 - 5 static void - 6 fullscreen_changed(GSimpleAction *action, GVariant *value, gpointer win) { - 7 if (g_variant_get_boolean (value)) - 8 gtk_window_maximize (GTK_WINDOW (win)); - 9 else - 10 gtk_window_unmaximize (GTK_WINDOW (win)); - 11 g_simple_action_set_state (action, value); - 12 } - 13 - 14 static void - 15 color_activated(GSimpleAction *action, GVariant *parameter, gpointer win) { - 16 gchar *color = g_strdup_printf ("label#lb {background-color: %s;}", g_variant_get_string (parameter, NULL)); - 17 gtk_css_provider_load_from_data (provider, color, -1); - 18 g_free (color); - 19 g_action_change_state (G_ACTION (action), parameter); - 20 } - 21 - 22 static void - 23 quit_activated(GSimpleAction *action, GVariant *parameter, gpointer app) - 24 { - 25 g_application_quit (G_APPLICATION(app)); - 26 } - 27 - 28 static void - 29 on_activate (GApplication *app, gpointer user_data) { - 30 GtkWidget *win = gtk_application_window_new (GTK_APPLICATION (app)); - 31 gtk_window_set_title (GTK_WINDOW (win), "menu2"); - 32 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300); - 33 - 34 GtkWidget *lb = gtk_label_new (NULL); - 35 gtk_widget_set_name (lb, "lb"); /* the name is used by CSS Selector */ - 36 gtk_window_set_child (GTK_WINDOW (win), lb); - 37 - 38 GSimpleAction *act_fullscreen - 39 = g_simple_action_new_stateful ("fullscreen", NULL, g_variant_new_boolean (FALSE)); - 40 GSimpleAction *act_color - 41 = g_simple_action_new_stateful ("color", g_variant_type_new("s"), g_variant_new_string ("red")); - 42 GSimpleAction *act_quit - 43 = g_simple_action_new ("quit", NULL); - 44 - 45 GMenu *menubar = g_menu_new (); - 46 GMenu *menu = g_menu_new (); - 47 GMenu *section1 = g_menu_new (); - 48 GMenu *section2 = g_menu_new (); - 49 GMenu *section3 = g_menu_new (); - 50 GMenuItem *menu_item_fullscreen = g_menu_item_new ("Full Screen", "win.fullscreen"); - 51 GMenuItem *menu_item_red = g_menu_item_new ("Red", "win.color::red"); - 52 GMenuItem *menu_item_green = g_menu_item_new ("Green", "win.color::green"); - 53 GMenuItem *menu_item_blue = g_menu_item_new ("Blue", "win.color::blue"); - 54 GMenuItem *menu_item_quit = g_menu_item_new ("Quit", "app.quit"); - 55 - 56 g_signal_connect (act_fullscreen, "change-state", G_CALLBACK (fullscreen_changed), win); - 57 g_signal_connect (act_color, "activate", G_CALLBACK (color_activated), win); - 58 g_signal_connect (act_quit, "activate", G_CALLBACK (quit_activated), app); - 59 g_action_map_add_action (G_ACTION_MAP (win), G_ACTION (act_fullscreen)); - 60 g_action_map_add_action (G_ACTION_MAP (win), G_ACTION (act_color)); - 61 g_action_map_add_action (G_ACTION_MAP (app), G_ACTION (act_quit)); - 62 - 63 g_menu_append_item (section1, menu_item_fullscreen); - 64 g_menu_append_item (section2, menu_item_red); - 65 g_menu_append_item (section2, menu_item_green); - 66 g_menu_append_item (section2, menu_item_blue); - 67 g_menu_append_item (section3, menu_item_quit); - 68 g_object_unref (menu_item_red); - 69 g_object_unref (menu_item_green); - 70 g_object_unref (menu_item_blue); - 71 g_object_unref (menu_item_fullscreen); - 72 g_object_unref (menu_item_quit); - 73 - 74 g_menu_append_section (menu, NULL, G_MENU_MODEL (section1)); - 75 g_menu_append_section (menu, "Color", G_MENU_MODEL (section2)); - 76 g_menu_append_section (menu, NULL, G_MENU_MODEL (section3)); - 77 g_menu_append_submenu (menubar, "Menu", G_MENU_MODEL (menu)); - 78 - 79 gtk_application_set_menubar (GTK_APPLICATION (app), G_MENU_MODEL (menubar)); - 80 gtk_application_window_set_show_menubar (GTK_APPLICATION_WINDOW (win), TRUE); - 81 - 82 /* GtkCssProvider *provider = gtk_css_provider_new ();*/ - 83 provider = gtk_css_provider_new (); - 84 GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (win)); - 85 gtk_css_provider_load_from_data (provider, "label#lb {background-color: red;}", -1); - 86 gtk_style_context_add_provider_for_display (display, GTK_STYLE_PROVIDER (provider), - 87 GTK_STYLE_PROVIDER_PRIORITY_USER); - 88 - 89 /* gtk_widget_show (win);*/ - 90 gtk_window_present (GTK_WINDOW (win)); - 91 } - 92 - 93 int - 94 main (int argc, char **argv) { - 95 GtkApplication *app; - 96 int stat; - 97 - 98 app = gtk_application_new ("com.github.ToshioCP.menu2", G_APPLICATION_FLAGS_NONE); - 99 g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL); - 100 - 101 stat =g_application_run (G_APPLICATION (app), argc, argv); - 102 g_object_unref (app); - 103 return stat; - 104 } - 105 +~~~C + 1 #include + 2 + 3 static GtkCssProvider *provider; + 4 + 5 static void + 6 fullscreen_changed(GSimpleAction *action, GVariant *value, gpointer win) { + 7 if (g_variant_get_boolean (value)) + 8 gtk_window_maximize (GTK_WINDOW (win)); + 9 else + 10 gtk_window_unmaximize (GTK_WINDOW (win)); + 11 g_simple_action_set_state (action, value); + 12 } + 13 + 14 static void + 15 color_activated(GSimpleAction *action, GVariant *parameter, gpointer win) { + 16 gchar *color = g_strdup_printf ("label#lb {background-color: %s;}", g_variant_get_string (parameter, NULL)); + 17 gtk_css_provider_load_from_data (provider, color, -1); + 18 g_free (color); + 19 g_action_change_state (G_ACTION (action), parameter); + 20 } + 21 + 22 static void + 23 quit_activated(GSimpleAction *action, GVariant *parameter, gpointer app) + 24 { + 25 g_application_quit (G_APPLICATION(app)); + 26 } + 27 + 28 static void + 29 on_activate (GApplication *app, gpointer user_data) { + 30 GtkWidget *win = gtk_application_window_new (GTK_APPLICATION (app)); + 31 gtk_window_set_title (GTK_WINDOW (win), "menu2"); + 32 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300); + 33 + 34 GtkWidget *lb = gtk_label_new (NULL); + 35 gtk_widget_set_name (lb, "lb"); /* the name is used by CSS Selector */ + 36 gtk_window_set_child (GTK_WINDOW (win), lb); + 37 + 38 GSimpleAction *act_fullscreen + 39 = g_simple_action_new_stateful ("fullscreen", NULL, g_variant_new_boolean (FALSE)); + 40 GSimpleAction *act_color + 41 = g_simple_action_new_stateful ("color", g_variant_type_new("s"), g_variant_new_string ("red")); + 42 GSimpleAction *act_quit + 43 = g_simple_action_new ("quit", NULL); + 44 + 45 GMenu *menubar = g_menu_new (); + 46 GMenu *menu = g_menu_new (); + 47 GMenu *section1 = g_menu_new (); + 48 GMenu *section2 = g_menu_new (); + 49 GMenu *section3 = g_menu_new (); + 50 GMenuItem *menu_item_fullscreen = g_menu_item_new ("Full Screen", "win.fullscreen"); + 51 GMenuItem *menu_item_red = g_menu_item_new ("Red", "win.color::red"); + 52 GMenuItem *menu_item_green = g_menu_item_new ("Green", "win.color::green"); + 53 GMenuItem *menu_item_blue = g_menu_item_new ("Blue", "win.color::blue"); + 54 GMenuItem *menu_item_quit = g_menu_item_new ("Quit", "app.quit"); + 55 + 56 g_signal_connect (act_fullscreen, "change-state", G_CALLBACK (fullscreen_changed), win); + 57 g_signal_connect (act_color, "activate", G_CALLBACK (color_activated), win); + 58 g_signal_connect (act_quit, "activate", G_CALLBACK (quit_activated), app); + 59 g_action_map_add_action (G_ACTION_MAP (win), G_ACTION (act_fullscreen)); + 60 g_action_map_add_action (G_ACTION_MAP (win), G_ACTION (act_color)); + 61 g_action_map_add_action (G_ACTION_MAP (app), G_ACTION (act_quit)); + 62 + 63 g_menu_append_item (section1, menu_item_fullscreen); + 64 g_menu_append_item (section2, menu_item_red); + 65 g_menu_append_item (section2, menu_item_green); + 66 g_menu_append_item (section2, menu_item_blue); + 67 g_menu_append_item (section3, menu_item_quit); + 68 g_object_unref (menu_item_red); + 69 g_object_unref (menu_item_green); + 70 g_object_unref (menu_item_blue); + 71 g_object_unref (menu_item_fullscreen); + 72 g_object_unref (menu_item_quit); + 73 + 74 g_menu_append_section (menu, NULL, G_MENU_MODEL (section1)); + 75 g_menu_append_section (menu, "Color", G_MENU_MODEL (section2)); + 76 g_menu_append_section (menu, NULL, G_MENU_MODEL (section3)); + 77 g_menu_append_submenu (menubar, "Menu", G_MENU_MODEL (menu)); + 78 + 79 gtk_application_set_menubar (GTK_APPLICATION (app), G_MENU_MODEL (menubar)); + 80 gtk_application_window_set_show_menubar (GTK_APPLICATION_WINDOW (win), TRUE); + 81 + 82 /* GtkCssProvider *provider = gtk_css_provider_new ();*/ + 83 provider = gtk_css_provider_new (); + 84 GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (win)); + 85 gtk_css_provider_load_from_data (provider, "label#lb {background-color: red;}", -1); + 86 gtk_style_context_add_provider_for_display (display, GTK_STYLE_PROVIDER (provider), + 87 GTK_STYLE_PROVIDER_PRIORITY_USER); + 88 + 89 /* gtk_widget_show (win);*/ + 90 gtk_window_present (GTK_WINDOW (win)); + 91 } + 92 + 93 int + 94 main (int argc, char **argv) { + 95 GtkApplication *app; + 96 int stat; + 97 + 98 app = gtk_application_new ("com.github.ToshioCP.menu2", G_APPLICATION_FLAGS_NONE); + 99 g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL); +100 +101 stat =g_application_run (G_APPLICATION (app), argc, argv); +102 g_object_unref (app); +103 return stat; +104 } +105 +~~~ - 5-26: Signal handlers. They have been explained in this section. diff --git a/gfm/sec18.md b/gfm/sec18.md index 31b099d..3235b24 100644 --- a/gfm/sec18.md +++ b/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,22 +13,26 @@ The same goes for menus. The ui file for menus has interface, menu tags. The file starts and ends with interface tag. - - - - +~~~xml + + + + +~~~ `menu` tag corresponds to GMenu object. `id` attribute defines the name of the object. It will be refered by GtkBuilder. - - File - - New - win.new - - +~~~xml + + File + + New + win.new + + +~~~ `item` tag corresponds to item in GMenu which has the same structure as GMenuItem. The item above has a label attribute. @@ -40,15 +44,17 @@ The GMenuItem has a link to GMenu. The ui file above can be described as follows. - - File - - - New - win.new - - - +~~~xml + + File + + + New + win.new + + + +~~~ `link` tag expresses the link to submenu. And at the same time it also expresses the submenu itself. @@ -63,95 +69,101 @@ Its name is `menu3`. The following is the ui file of the menu in `menu3`. - 1 - 2 - 3 - 4 - 5 File - 6
- 7 - 8 New - 9 win.new - 10 - 11 - 12 Open - 13 win.open - 14 - 15
- 16
- 17 - 18 Save - 19 win.save - 20 - 21 - 22 Save As… - 23 win.saveas - 24 - 25
- 26
- 27 - 28 Close - 29 win.close - 30 - 31
- 32
- 33 - 34 Quit - 35 app.quit - 36 - 37
- 38
- 39 - 40 Edit - 41
- 42 - 43 Cut - 44 win.cut - 45 - 46 - 47 Copy - 48 win.copy - 49 - 50 - 51 Paste - 52 win.paste - 53 - 54
- 55
- 56 - 57 Select All - 58 win.selectall - 59 - 60
- 61
- 62 - 63 View - 64
- 65 - 66 Full Screen - 67 win.fullscreen - 68 - 69
- 70
- 71
- 72
+~~~xml + 1 + 2 + 3 + 4 + 5 File + 6
+ 7 + 8 New + 9 win.new +10 +11 +12 Open +13 win.open +14 +15
+16
+17 +18 Save +19 win.save +20 +21 +22 Save As… +23 win.saveas +24 +25
+26
+27 +28 Close +29 win.close +30 +31
+32
+33 +34 Quit +35 app.quit +36 +37
+38
+39 +40 Edit +41
+42 +43 Cut +44 win.cut +45 +46 +47 Copy +48 win.copy +49 +50 +51 Paste +52 win.paste +53 +54
+55
+56 +57 Select All +58 win.selectall +59 +60
+61
+62 +63 View +64
+65 +66 Full Screen +67 win.fullscreen +68 +69
+70
+71
+72
+~~~ The ui file is converted to the resource by the resouce compiler `glib-compile-resouces` with xml file below. - 1 - 2 - 3 - 4 menu3.ui - 5 - 6 +~~~xml +1 +2 +3 +4 menu3.ui +5 +6 +~~~ GtkBuilder builds menus from the resource. - GtkBuilder *builder = gtk_builder_new_from_resource ("/com/github/ToshioCP/menu3/menu3.ui"); - GMenuModel *menubar = G_MENU_MODEL (gtk_builder_get_object (builder, "menubar")); +~~~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); +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. - 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 */ - /*< private >*/ - gsize padding[3]; - }; +~~~C +typedef struct _GActionEntry GActionEntry; +struct _GActionEntry +{ + /* 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: - { "fullscreen", NULL, NULL, "false", fullscreen_changed } - { "color", color_activated, "s", "red", NULL } - { "quit", quit_activated, NULL, NULL, NULL }, +~~~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. - 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); +~~~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); +~~~ The code above does: @@ -198,12 +221,14 @@ The code above does: The same goes for the other actions. - 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); - +~~~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); +~~~ The code above does: - Build a "fullscreen" action and "color" action. @@ -217,125 +242,129 @@ The code above does: The C source code of `menu3` and `meson.build` is as follows. - 1 #include - 2 - 3 static void - 4 new_activated (GSimpleAction *action, GVariant *parameter, gpointer win) { - 5 } - 6 - 7 static void - 8 open_activated (GSimpleAction *action, GVariant *parameter, gpointer win) { - 9 } - 10 - 11 static void - 12 save_activated (GSimpleAction *action, GVariant *parameter, gpointer win) { - 13 } - 14 - 15 static void - 16 saveas_activated (GSimpleAction *action, GVariant *parameter, gpointer win) { - 17 } - 18 - 19 static void - 20 close_activated (GSimpleAction *action, GVariant *parameter, gpointer win) { - 21 } - 22 - 23 static void - 24 cut_activated (GSimpleAction *action, GVariant *parameter, gpointer win) { - 25 } - 26 - 27 static void - 28 copy_activated (GSimpleAction *action, GVariant *parameter, gpointer win) { - 29 } - 30 - 31 static void - 32 paste_activated (GSimpleAction *action, GVariant *parameter, gpointer win) { - 33 } - 34 - 35 static void - 36 selectall_activated (GSimpleAction *action, GVariant *parameter, gpointer win) { - 37 } - 38 - 39 static void - 40 fullscreen_changed (GSimpleAction *action, GVariant *state, gpointer win) { - 41 if (g_variant_get_boolean (state)) - 42 gtk_window_maximize (GTK_WINDOW (win)); - 43 else - 44 gtk_window_unmaximize (GTK_WINDOW (win)); - 45 g_simple_action_set_state (action, state); - 46 } - 47 - 48 static void - 49 quit_activated (GSimpleAction *action, GVariant *parameter, gpointer app) - 50 { - 51 g_application_quit (G_APPLICATION(app)); - 52 } - 53 - 54 static void - 55 on_activate (GApplication *app, gpointer user_data) { - 56 GtkWidget *win = gtk_application_window_new (GTK_APPLICATION (app)); - 57 - 58 const GActionEntry win_entries[] = { - 59 { "new", new_activated, NULL, NULL, NULL }, - 60 { "open", open_activated, NULL, NULL, NULL }, - 61 { "save", save_activated, NULL, NULL, NULL }, - 62 { "saveas", saveas_activated, NULL, NULL, NULL }, - 63 { "close", close_activated, NULL, NULL, NULL }, - 64 { "cut", cut_activated, NULL, NULL, NULL }, - 65 { "copy", copy_activated, NULL, NULL, NULL }, - 66 { "paste", paste_activated, NULL, NULL, NULL }, - 67 { "selectall", selectall_activated, NULL, NULL, NULL }, - 68 { "fullscreen", NULL, NULL, "false", fullscreen_changed } - 69 }; - 70 g_action_map_add_action_entries (G_ACTION_MAP (win), win_entries, G_N_ELEMENTS (win_entries), win); - 71 - 72 gtk_application_window_set_show_menubar (GTK_APPLICATION_WINDOW (win), TRUE); - 73 - 74 gtk_window_set_title (GTK_WINDOW (win), "menu3"); - 75 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300); - 76 gtk_widget_show (win); - 77 } - 78 - 79 static void - 80 on_startup (GApplication *app, gpointer user_data) { - 81 GtkBuilder *builder = gtk_builder_new_from_resource ("/com/github/ToshioCP/menu3/menu3.ui"); - 82 GMenuModel *menubar = G_MENU_MODEL (gtk_builder_get_object (builder, "menubar")); - 83 - 84 gtk_application_set_menubar (GTK_APPLICATION (app), menubar); - 85 g_object_unref (builder); - 86 - 87 const GActionEntry app_entries[] = { - 88 { "quit", quit_activated, NULL, NULL, NULL } - 89 }; - 90 g_action_map_add_action_entries (G_ACTION_MAP (app), app_entries, G_N_ELEMENTS (app_entries), app); - 91 } - 92 - 93 int - 94 main (int argc, char **argv) { - 95 GtkApplication *app; - 96 int stat; - 97 - 98 app = gtk_application_new ("com.github.ToshioCP.menu3", G_APPLICATION_FLAGS_NONE); - 99 g_signal_connect (app, "startup", G_CALLBACK (on_startup), NULL); - 100 g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL); - 101 - 102 stat =g_application_run (G_APPLICATION (app), argc, argv); - 103 g_object_unref (app); - 104 return stat; - 105 } - 106 +~~~C + 1 #include + 2 + 3 static void + 4 new_activated (GSimpleAction *action, GVariant *parameter, gpointer win) { + 5 } + 6 + 7 static void + 8 open_activated (GSimpleAction *action, GVariant *parameter, gpointer win) { + 9 } + 10 + 11 static void + 12 save_activated (GSimpleAction *action, GVariant *parameter, gpointer win) { + 13 } + 14 + 15 static void + 16 saveas_activated (GSimpleAction *action, GVariant *parameter, gpointer win) { + 17 } + 18 + 19 static void + 20 close_activated (GSimpleAction *action, GVariant *parameter, gpointer win) { + 21 } + 22 + 23 static void + 24 cut_activated (GSimpleAction *action, GVariant *parameter, gpointer win) { + 25 } + 26 + 27 static void + 28 copy_activated (GSimpleAction *action, GVariant *parameter, gpointer win) { + 29 } + 30 + 31 static void + 32 paste_activated (GSimpleAction *action, GVariant *parameter, gpointer win) { + 33 } + 34 + 35 static void + 36 selectall_activated (GSimpleAction *action, GVariant *parameter, gpointer win) { + 37 } + 38 + 39 static void + 40 fullscreen_changed (GSimpleAction *action, GVariant *state, gpointer win) { + 41 if (g_variant_get_boolean (state)) + 42 gtk_window_maximize (GTK_WINDOW (win)); + 43 else + 44 gtk_window_unmaximize (GTK_WINDOW (win)); + 45 g_simple_action_set_state (action, state); + 46 } + 47 + 48 static void + 49 quit_activated (GSimpleAction *action, GVariant *parameter, gpointer app) + 50 { + 51 g_application_quit (G_APPLICATION(app)); + 52 } + 53 + 54 static void + 55 on_activate (GApplication *app, gpointer user_data) { + 56 GtkWidget *win = gtk_application_window_new (GTK_APPLICATION (app)); + 57 + 58 const GActionEntry win_entries[] = { + 59 { "new", new_activated, NULL, NULL, NULL }, + 60 { "open", open_activated, NULL, NULL, NULL }, + 61 { "save", save_activated, NULL, NULL, NULL }, + 62 { "saveas", saveas_activated, NULL, NULL, NULL }, + 63 { "close", close_activated, NULL, NULL, NULL }, + 64 { "cut", cut_activated, NULL, NULL, NULL }, + 65 { "copy", copy_activated, NULL, NULL, NULL }, + 66 { "paste", paste_activated, NULL, NULL, NULL }, + 67 { "selectall", selectall_activated, NULL, NULL, NULL }, + 68 { "fullscreen", NULL, NULL, "false", fullscreen_changed } + 69 }; + 70 g_action_map_add_action_entries (G_ACTION_MAP (win), win_entries, G_N_ELEMENTS (win_entries), win); + 71 + 72 gtk_application_window_set_show_menubar (GTK_APPLICATION_WINDOW (win), TRUE); + 73 + 74 gtk_window_set_title (GTK_WINDOW (win), "menu3"); + 75 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300); + 76 gtk_widget_show (win); + 77 } + 78 + 79 static void + 80 on_startup (GApplication *app, gpointer user_data) { + 81 GtkBuilder *builder = gtk_builder_new_from_resource ("/com/github/ToshioCP/menu3/menu3.ui"); + 82 GMenuModel *menubar = G_MENU_MODEL (gtk_builder_get_object (builder, "menubar")); + 83 + 84 gtk_application_set_menubar (GTK_APPLICATION (app), menubar); + 85 g_object_unref (builder); + 86 + 87 const GActionEntry app_entries[] = { + 88 { "quit", quit_activated, NULL, NULL, NULL } + 89 }; + 90 g_action_map_add_action_entries (G_ACTION_MAP (app), app_entries, G_N_ELEMENTS (app_entries), app); + 91 } + 92 + 93 int + 94 main (int argc, char **argv) { + 95 GtkApplication *app; + 96 int stat; + 97 + 98 app = gtk_application_new ("com.github.ToshioCP.menu3", G_APPLICATION_FLAGS_NONE); + 99 g_signal_connect (app, "startup", G_CALLBACK (on_startup), NULL); +100 g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL); +101 +102 stat =g_application_run (G_APPLICATION (app), argc, argv); +103 g_object_unref (app); +104 return stat; +105 } +106 +~~~ meson.build - 1 project('menu3', 'c') - 2 - 3 gtkdep = dependency('gtk4') - 4 - 5 gnome=import('gnome') - 6 resources = gnome.compile_resources('resources','menu3.gresource.xml') - 7 - 8 sourcefiles=files('menu3.c') - 9 - 10 executable('menu3', sourcefiles, resources, dependencies: gtkdep) +~~~meson + 1 project('menu3', 'c') + 2 + 3 gtkdep = dependency('gtk4') + 4 + 5 gnome=import('gnome') + 6 resources = gnome.compile_resources('resources','menu3.gresource.xml') + 7 + 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) diff --git a/gfm/sec19.md b/gfm/sec19.md index 160d640..1adebd9 100644 --- a/gfm/sec19.md +++ b/gfm/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,37 +46,39 @@ This will be a destnation. Here's a simple example code that draws a small square and save it as a png file. - 1 #include - 2 - 3 int - 4 main (int argc, char **argv) - 5 { - 6 cairo_surface_t *surface; - 7 cairo_t *cr; - 8 int width = 100; - 9 int height = 100; - 10 - 11 /* Generate surface and cairo */ - 12 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height); - 13 cr = cairo_create (surface); - 14 - 15 /* Drawing starts here. */ - 16 /* Paint the background white */ - 17 cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); - 18 cairo_paint (cr); - 19 /* Draw a black rectangle */ - 20 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); - 21 cairo_set_line_width (cr, 2.0); - 22 cairo_rectangle (cr, width/2.0 - 20.0, height/2.0 - 20.0, 40.0, 40.0); - 23 cairo_stroke (cr); - 24 - 25 /* Write the surface to a png file and clean up cairo and surface. */ - 26 cairo_surface_write_to_png (surface, "rectangle.png"); - 27 cairo_destroy (cr); - 28 cairo_surface_destroy (surface); - 29 - 30 return 0; - 31 } +~~~C + 1 #include + 2 + 3 int + 4 main (int argc, char **argv) + 5 { + 6 cairo_surface_t *surface; + 7 cairo_t *cr; + 8 int width = 100; + 9 int height = 100; +10 +11 /* Generate surface and cairo */ +12 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height); +13 cr = cairo_create (surface); +14 +15 /* Drawing starts here. */ +16 /* Paint the background white */ +17 cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); +18 cairo_paint (cr); +19 /* Draw a black rectangle */ +20 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); +21 cairo_set_line_width (cr, 2.0); +22 cairo_rectangle (cr, width/2.0 - 20.0, height/2.0 - 20.0, 40.0, 40.0); +23 cairo_stroke (cr); +24 +25 /* Write the surface to a png file and clean up cairo and surface. */ +26 cairo_surface_write_to_png (surface, "rectangle.png"); +27 cairo_destroy (cr); +28 cairo_surface_destroy (surface); +29 +30 return 0; +31 } +~~~ - 1: Include the header file of cairo. - 12: `cairo_image_surface_create` creates an image surface. @@ -117,45 +119,47 @@ If you aren't familiar with cairo, it is strongly recommended to read the [tutor The following is a very simple example. - 1 #include - 2 - 3 static void - 4 draw_function (GtkDrawingArea *area, cairo_t *cr, int width, int height, gpointer user_data) { - 5 cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* whilte */ - 6 cairo_paint (cr); - 7 cairo_set_line_width (cr, 2.0); - 8 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */ - 9 cairo_rectangle (cr, width/2.0 - 20.0, height/2.0 - 20.0, 40.0, 40.0); - 10 cairo_stroke (cr); - 11 } - 12 - 13 static void - 14 on_activate (GApplication *app, gpointer user_data) { - 15 GtkWidget *win = gtk_application_window_new (GTK_APPLICATION (app)); - 16 GtkWidget *area = gtk_drawing_area_new (); - 17 - 18 gtk_window_set_title (GTK_WINDOW (win), "da1"); - 19 /* Set initial size of width and height */ - 20 gtk_drawing_area_set_content_width (GTK_DRAWING_AREA (area), 100); - 21 gtk_drawing_area_set_content_height (GTK_DRAWING_AREA (area), 100); - 22 gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (area), draw_function, NULL, NULL); - 23 gtk_window_set_child (GTK_WINDOW (win), area); - 24 - 25 gtk_widget_show (win); - 26 } - 27 - 28 int - 29 main (int argc, char **argv) { - 30 GtkApplication *app; - 31 int stat; - 32 - 33 app = gtk_application_new ("com.github.ToshioCP.da1", G_APPLICATION_FLAGS_NONE); - 34 g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL); - 35 stat =g_application_run (G_APPLICATION (app), argc, argv); - 36 g_object_unref (app); - 37 return stat; - 38 } - 39 +~~~C + 1 #include + 2 + 3 static void + 4 draw_function (GtkDrawingArea *area, cairo_t *cr, int width, int height, gpointer user_data) { + 5 cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* whilte */ + 6 cairo_paint (cr); + 7 cairo_set_line_width (cr, 2.0); + 8 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */ + 9 cairo_rectangle (cr, width/2.0 - 20.0, height/2.0 - 20.0, 40.0, 40.0); +10 cairo_stroke (cr); +11 } +12 +13 static void +14 on_activate (GApplication *app, gpointer user_data) { +15 GtkWidget *win = gtk_application_window_new (GTK_APPLICATION (app)); +16 GtkWidget *area = gtk_drawing_area_new (); +17 +18 gtk_window_set_title (GTK_WINDOW (win), "da1"); +19 /* Set initial size of width and height */ +20 gtk_drawing_area_set_content_width (GTK_DRAWING_AREA (area), 100); +21 gtk_drawing_area_set_content_height (GTK_DRAWING_AREA (area), 100); +22 gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (area), draw_function, NULL, NULL); +23 gtk_window_set_child (GTK_WINDOW (win), area); +24 +25 gtk_widget_show (win); +26 } +27 +28 int +29 main (int argc, char **argv) { +30 GtkApplication *app; +31 int stat; +32 +33 app = gtk_application_new ("com.github.ToshioCP.da1", G_APPLICATION_FLAGS_NONE); +34 g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL); +35 stat =g_application_run (G_APPLICATION (app), argc, argv); +36 g_object_unref (app); +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) diff --git a/gfm/sec2.md b/gfm/sec2.md index 018b9ad..4d434b0 100644 --- a/gfm/sec2.md +++ b/gfm/sec2.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/" diff --git a/gfm/sec20.md b/gfm/sec20.md index 7a555cc..9650084 100644 --- a/gfm/sec20.md +++ b/gfm/sec20.md @@ -34,87 +34,89 @@ 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. - 1 - 2 - 3 color changer - 4 600 - 5 400 - 6 - 7 - 8 GTK_ORIENTATION_VERTICAL - 9 - 10 - 11 GTK_ORIENTATION_HORIZONTAL - 12 - 13 - 14 10 - 15 - 16 - 17 - 18 - 19 Run - 20 - 21 - 22 - 23 - 24 - 25 - 26 Open - 27 - 28 - 29 - 30 - 31 - 32 TRUE - 33 - 34 - 35 - 36 - 37 Save - 38 - 39 - 40 - 41 - 42 - 43 Close - 44 - 45 - 46 - 47 - 48 - 49 10 - 50 - 51 - 52 - 53 - 54 - 55 - 56 GTK_ORIENTATION_HORIZONTAL - 57 TRUE - 58 - 59 - 60 TRUE - 61 TRUE - 62 - 63 - 64 GTK_WRAP_WORD_CHAR - 65 - 66 - 67 - 68 - 69 - 70 - 71 TRUE - 72 TRUE - 73 - 74 - 75 - 76 - 77 - 78 - 79 - 80 - 81 +~~~xml + 1 + 2 + 3 color changer + 4 600 + 5 400 + 6 + 7 + 8 GTK_ORIENTATION_VERTICAL + 9 +10 +11 GTK_ORIENTATION_HORIZONTAL +12 +13 +14 10 +15 +16 +17 +18 +19 Run +20 +21 +22 +23 +24 +25 +26 Open +27 +28 +29 +30 +31 +32 TRUE +33 +34 +35 +36 +37 Save +38 +39 +40 +41 +42 +43 Close +44 +45 +46 +47 +48 +49 10 +50 +51 +52 +53 +54 +55 +56 GTK_ORIENTATION_HORIZONTAL +57 TRUE +58 +59 +60 TRUE +61 TRUE +62 +63 +64 GTK_WRAP_WORD_CHAR +65 +66 +67 +68 +69 +70 +71 TRUE +72 TRUE +73 +74 +75 +76 +77 +78 +79 +80 +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". - 1 - 2 - 3 - 4 color.ui - 5 - 6 +~~~xml +1 +2 +3 +4 color.ui +5 +6 +~~~ # 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. - 1 #include - 2 - 3 #include "tfetextview.h" +~~~C +1 #include +2 +3 #include "tfetextview.h" +~~~ # Colorapplication.c @@ -169,128 +175,130 @@ Particularly, `Run` signal handler is the point in this program. The following is `colorapplication.c`. - 1 #include "color.h" - 2 - 3 static GtkWidget *win; - 4 static GtkWidget *tv; - 5 static GtkWidget *da; - 6 - 7 static cairo_surface_t *surface = NULL; - 8 - 9 static void - 10 run (void) { - 11 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); - 12 GtkTextIter start_iter; - 13 GtkTextIter end_iter; - 14 char *contents; - 15 cairo_t *cr; - 16 - 17 gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter); - 18 contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE); - 19 if (surface) { - 20 cr = cairo_create (surface); - 21 if (g_strcmp0 ("red", contents) == 0) - 22 cairo_set_source_rgb (cr, 1, 0, 0); - 23 else if (g_strcmp0 ("green", contents) == 0) - 24 cairo_set_source_rgb (cr, 0, 1, 0); - 25 else if (g_strcmp0 ("blue", contents) == 0) - 26 cairo_set_source_rgb (cr, 0, 0, 1); - 27 else if (g_strcmp0 ("white", contents) == 0) - 28 cairo_set_source_rgb (cr, 1, 1, 1); - 29 else if (g_strcmp0 ("black", contents) == 0) - 30 cairo_set_source_rgb (cr, 0, 0, 0); - 31 else if (g_strcmp0 ("light", contents) == 0) - 32 cairo_set_source_rgba (cr, 1, 1, 1, 0.5); - 33 else if (g_strcmp0 ("dark", contents) == 0) - 34 cairo_set_source_rgba (cr, 0, 0, 0, 0.5); - 35 else - 36 cairo_set_source_surface (cr, surface, 0, 0); - 37 cairo_paint (cr); - 38 cairo_destroy (cr); - 39 } - 40 } - 41 - 42 void - 43 run_cb (GtkWidget *btnr) { - 44 run (); - 45 gtk_widget_queue_draw (GTK_WIDGET (da)); - 46 } - 47 - 48 void - 49 open_cb (GtkWidget *btno) { - 50 tfe_text_view_open (TFE_TEXT_VIEW (tv), win); - 51 } - 52 - 53 void - 54 save_cb (GtkWidget *btns) { - 55 tfe_text_view_save (TFE_TEXT_VIEW (tv)); - 56 } - 57 - 58 void - 59 close_cb (GtkWidget *btnc) { - 60 if (surface) - 61 cairo_surface_destroy (surface); - 62 gtk_window_destroy (GTK_WINDOW (win)); - 63 } - 64 - 65 static void - 66 resize_cb (GtkDrawingArea *drawing_area, int width, int height, gpointer user_data) { - 67 if (surface) - 68 cairo_surface_destroy (surface); - 69 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); - 70 run (); - 71 } - 72 - 73 static void - 74 draw_func (GtkDrawingArea *drawing_area, cairo_t *cr, int width, int height, gpointer user_data) { - 75 if (surface) { - 76 cairo_set_source_surface (cr, surface, 0, 0); - 77 cairo_paint (cr); - 78 } - 79 } - 80 - 81 static void - 82 activate (GApplication *application) { - 83 gtk_widget_show (win); - 84 } - 85 - 86 static void - 87 startup (GApplication *application) { - 88 GtkApplication *app = GTK_APPLICATION (application); - 89 GtkBuilder *build; - 90 - 91 build = gtk_builder_new_from_resource ("/com/github/ToshioCP/color/color.ui"); - 92 win = GTK_WIDGET (gtk_builder_get_object (build, "win")); - 93 gtk_window_set_application (GTK_WINDOW (win), app); - 94 tv = GTK_WIDGET (gtk_builder_get_object (build, "tv")); - 95 da = GTK_WIDGET (gtk_builder_get_object (build, "da")); - 96 g_object_unref(build); - 97 g_signal_connect (GTK_DRAWING_AREA (da), "resize", G_CALLBACK (resize_cb), NULL); - 98 gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (da), draw_func, NULL, NULL); - 99 - 100 GdkDisplay *display; - 101 - 102 display = gtk_widget_get_display (GTK_WIDGET (win)); - 103 GtkCssProvider *provider = gtk_css_provider_new (); - 104 gtk_css_provider_load_from_data (provider, "textview {padding: 10px; font-family: monospace; font-size: 12pt;}", -1); - 105 gtk_style_context_add_provider_for_display (display, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_USER); - 106 } - 107 - 108 int - 109 main (int argc, char **argv) { - 110 GtkApplication *app; - 111 int stat; - 112 - 113 app = gtk_application_new ("com.github.ToshioCP.color", G_APPLICATION_FLAGS_NONE); - 114 - 115 g_signal_connect (app, "startup", G_CALLBACK (startup), NULL); - 116 g_signal_connect (app, "activate", G_CALLBACK (activate), NULL); - 117 - 118 stat =g_application_run (G_APPLICATION (app), argc, argv); - 119 g_object_unref (app); - 120 return stat; - 121 } - 122 +~~~C + 1 #include "color.h" + 2 + 3 static GtkWidget *win; + 4 static GtkWidget *tv; + 5 static GtkWidget *da; + 6 + 7 static cairo_surface_t *surface = NULL; + 8 + 9 static void + 10 run (void) { + 11 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); + 12 GtkTextIter start_iter; + 13 GtkTextIter end_iter; + 14 char *contents; + 15 cairo_t *cr; + 16 + 17 gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter); + 18 contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE); + 19 if (surface) { + 20 cr = cairo_create (surface); + 21 if (g_strcmp0 ("red", contents) == 0) + 22 cairo_set_source_rgb (cr, 1, 0, 0); + 23 else if (g_strcmp0 ("green", contents) == 0) + 24 cairo_set_source_rgb (cr, 0, 1, 0); + 25 else if (g_strcmp0 ("blue", contents) == 0) + 26 cairo_set_source_rgb (cr, 0, 0, 1); + 27 else if (g_strcmp0 ("white", contents) == 0) + 28 cairo_set_source_rgb (cr, 1, 1, 1); + 29 else if (g_strcmp0 ("black", contents) == 0) + 30 cairo_set_source_rgb (cr, 0, 0, 0); + 31 else if (g_strcmp0 ("light", contents) == 0) + 32 cairo_set_source_rgba (cr, 1, 1, 1, 0.5); + 33 else if (g_strcmp0 ("dark", contents) == 0) + 34 cairo_set_source_rgba (cr, 0, 0, 0, 0.5); + 35 else + 36 cairo_set_source_surface (cr, surface, 0, 0); + 37 cairo_paint (cr); + 38 cairo_destroy (cr); + 39 } + 40 } + 41 + 42 void + 43 run_cb (GtkWidget *btnr) { + 44 run (); + 45 gtk_widget_queue_draw (GTK_WIDGET (da)); + 46 } + 47 + 48 void + 49 open_cb (GtkWidget *btno) { + 50 tfe_text_view_open (TFE_TEXT_VIEW (tv), win); + 51 } + 52 + 53 void + 54 save_cb (GtkWidget *btns) { + 55 tfe_text_view_save (TFE_TEXT_VIEW (tv)); + 56 } + 57 + 58 void + 59 close_cb (GtkWidget *btnc) { + 60 if (surface) + 61 cairo_surface_destroy (surface); + 62 gtk_window_destroy (GTK_WINDOW (win)); + 63 } + 64 + 65 static void + 66 resize_cb (GtkDrawingArea *drawing_area, int width, int height, gpointer user_data) { + 67 if (surface) + 68 cairo_surface_destroy (surface); + 69 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); + 70 run (); + 71 } + 72 + 73 static void + 74 draw_func (GtkDrawingArea *drawing_area, cairo_t *cr, int width, int height, gpointer user_data) { + 75 if (surface) { + 76 cairo_set_source_surface (cr, surface, 0, 0); + 77 cairo_paint (cr); + 78 } + 79 } + 80 + 81 static void + 82 activate (GApplication *application) { + 83 gtk_widget_show (win); + 84 } + 85 + 86 static void + 87 startup (GApplication *application) { + 88 GtkApplication *app = GTK_APPLICATION (application); + 89 GtkBuilder *build; + 90 + 91 build = gtk_builder_new_from_resource ("/com/github/ToshioCP/color/color.ui"); + 92 win = GTK_WIDGET (gtk_builder_get_object (build, "win")); + 93 gtk_window_set_application (GTK_WINDOW (win), app); + 94 tv = GTK_WIDGET (gtk_builder_get_object (build, "tv")); + 95 da = GTK_WIDGET (gtk_builder_get_object (build, "da")); + 96 g_object_unref(build); + 97 g_signal_connect (GTK_DRAWING_AREA (da), "resize", G_CALLBACK (resize_cb), NULL); + 98 gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (da), draw_func, NULL, NULL); + 99 +100 GdkDisplay *display; +101 +102 display = gtk_widget_get_display (GTK_WIDGET (win)); +103 GtkCssProvider *provider = gtk_css_provider_new (); +104 gtk_css_provider_load_from_data (provider, "textview {padding: 10px; font-family: monospace; font-size: 12pt;}", -1); +105 gtk_style_context_add_provider_for_display (display, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_USER); +106 } +107 +108 int +109 main (int argc, char **argv) { +110 GtkApplication *app; +111 int stat; +112 +113 app = gtk_application_new ("com.github.ToshioCP.color", G_APPLICATION_FLAGS_NONE); +114 +115 g_signal_connect (app, "startup", G_CALLBACK (startup), NULL); +116 g_signal_connect (app, "activate", G_CALLBACK (activate), NULL); +117 +118 stat =g_application_run (G_APPLICATION (app), argc, argv); +119 g_object_unref (app); +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,16 +340,18 @@ Alpha channel is used. This file is almost same as before. An argument "export_dynamic: true" is added to executable function. - 1 project('color', 'c') - 2 - 3 gtkdep = dependency('gtk4') - 4 - 5 gnome=import('gnome') - 6 resources = gnome.compile_resources('resources','color.gresource.xml') - 7 - 8 sourcefiles=files('colorapplication.c', 'tfetextview.c') - 9 - 10 executable('color', sourcefiles, resources, dependencies: gtkdep, export_dynamic: true) +~~~meson + 1 project('color', 'c') + 2 + 3 gtkdep = dependency('gtk4') + 4 + 5 gnome=import('gnome') + 6 resources = gnome.compile_resources('resources','color.gresource.xml') + 7 + 8 sourcefiles=files('colorapplication.c', 'tfetextview.c') + 9 +10 executable('color', sourcefiles, resources, dependencies: gtkdep, export_dynamic: true) +~~~ # Compile and execute it diff --git a/gfm/sec3.md b/gfm/sec3.md index 455aa39..d988545 100644 --- a/gfm/sec3.md +++ b/gfm/sec3.md @@ -20,19 +20,21 @@ That's all. Very simple. The following is the C code representing the scenario above. - 1 #include - 2 - 3 int - 4 main (int argc, char **argv) { - 5 GtkApplication *app; - 6 int stat; - 7 - 8 app = gtk_application_new ("com.github.ToshioCP.pr1", G_APPLICATION_FLAGS_NONE); - 9 stat =g_application_run (G_APPLICATION (app), argc, argv); - 10 g_object_unref (app); - 11 return stat; - 12 } - 13 +~~~C + 1 #include + 2 + 3 int + 4 main (int argc, char **argv) { + 5 GtkApplication *app; + 6 int stat; + 7 + 8 app = gtk_application_new ("com.github.ToshioCP.pr1", G_APPLICATION_FLAGS_NONE); + 9 stat =g_application_run (G_APPLICATION (app), argc, argv); +10 g_object_unref (app); +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,25 +101,27 @@ 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. - 1 #include - 2 - 3 static void - 4 on_activate (GApplication *app, gpointer *user_data) { - 5 g_print ("GtkApplication is activated.\n"); - 6 } - 7 - 8 int - 9 main (int argc, char **argv) { - 10 GtkApplication *app; - 11 int stat; - 12 - 13 app = gtk_application_new ("com.github.ToshioCP.pr2", G_APPLICATION_FLAGS_NONE); - 14 g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL); - 15 stat =g_application_run (G_APPLICATION (app), argc, argv); - 16 g_object_unref (app); - 17 return stat; - 18 } - 19 +~~~C + 1 #include + 2 + 3 static void + 4 on_activate (GApplication *app, gpointer *user_data) { + 5 g_print ("GtkApplication is activated.\n"); + 6 } + 7 + 8 int + 9 main (int argc, char **argv) { +10 GtkApplication *app; +11 int stat; +12 +13 app = gtk_application_new ("com.github.ToshioCP.pr2", G_APPLICATION_FLAGS_NONE); +14 g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL); +15 stat =g_application_run (G_APPLICATION (app), argc, argv); +16 g_object_unref (app); +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,14 +191,16 @@ Now rewrite the function `on_activate`. #### Generate a GtkWindow - 1 static void - 2 on_activate (GApplication *app, gpointer user_data) { - 3 GtkWidget *win; - 4 - 5 win = gtk_window_new (); - 6 gtk_window_set_application (GTK_WINDOW (win), GTK_APPLICATION (app)); - 7 gtk_widget_show (win); - 8 } +~~~C +1 static void +2 on_activate (GApplication *app, gpointer user_data) { +3 GtkWidget *win; +4 +5 win = gtk_window_new (); +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,15 +273,17 @@ It is recommended to use it instead of GtkWindow when you use GtkApplication. Now rewrite the program and use GtkAppliction Window. - 1 static void - 2 on_activate (GApplication *app, gpointer user_data) { - 3 GtkWidget *win; - 4 - 5 win = gtk_application_window_new (GTK_APPLICATION (app)); - 6 gtk_window_set_title (GTK_WINDOW (win), "pr4"); - 7 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300); - 8 gtk_widget_show (win); - 9 } +~~~C +1 static void +2 on_activate (GApplication *app, gpointer user_data) { +3 GtkWidget *win; +4 +5 win = gtk_application_window_new (GTK_APPLICATION (app)); +6 gtk_window_set_title (GTK_WINDOW (win), "pr4"); +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. diff --git a/gfm/sec4.md b/gfm/sec4.md index c3d776c..1886d0a 100644 --- a/gfm/sec4.md +++ b/gfm/sec4.md @@ -11,35 +11,37 @@ 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. - 1 #include - 2 - 3 static void - 4 on_activate (GApplication *app, gpointer user_data) { - 5 GtkWidget *win; - 6 GtkWidget *lab; - 7 - 8 win = gtk_application_window_new (GTK_APPLICATION (app)); - 9 gtk_window_set_title (GTK_WINDOW (win), "lb1"); - 10 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300); - 11 - 12 lab = gtk_label_new ("Hello."); - 13 gtk_window_set_child (GTK_WINDOW (win), lab); - 14 - 15 gtk_widget_show (win); - 16 } - 17 - 18 int - 19 main (int argc, char **argv) { - 20 GtkApplication *app; - 21 int stat; - 22 - 23 app = gtk_application_new ("com.github.ToshioCP.lb1", G_APPLICATION_FLAGS_NONE); - 24 g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL); - 25 stat =g_application_run (G_APPLICATION (app), argc, argv); - 26 g_object_unref (app); - 27 return stat; - 28 } - 29 +~~~C + 1 #include + 2 + 3 static void + 4 on_activate (GApplication *app, gpointer user_data) { + 5 GtkWidget *win; + 6 GtkWidget *lab; + 7 + 8 win = gtk_application_window_new (GTK_APPLICATION (app)); + 9 gtk_window_set_title (GTK_WINDOW (win), "lb1"); +10 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300); +11 +12 lab = gtk_label_new ("Hello."); +13 gtk_window_set_child (GTK_WINDOW (win), lab); +14 +15 gtk_widget_show (win); +16 } +17 +18 int +19 main (int argc, char **argv) { +20 GtkApplication *app; +21 int stat; +22 +23 app = gtk_application_new ("com.github.ToshioCP.lb1", G_APPLICATION_FLAGS_NONE); +24 g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL); +25 stat =g_application_run (G_APPLICATION (app), argc, argv); +26 g_object_unref (app); +27 return stat; +28 } +29 +~~~ Save this program to a file `lb1.c`. Then compile and run it. @@ -100,41 +102,43 @@ 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. - 1 #include - 2 - 3 static void - 4 click_cb (GtkButton *btn, gpointer user_data) { - 5 g_print ("Clicked.\n"); - 6 } - 7 - 8 static void - 9 on_activate (GApplication *app, gpointer user_data) { - 10 GtkWidget *win; - 11 GtkWidget *btn; - 12 - 13 win = gtk_application_window_new (GTK_APPLICATION (app)); - 14 gtk_window_set_title (GTK_WINDOW (win), "lb2"); - 15 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300); - 16 - 17 btn = gtk_button_new_with_label ("Click me"); - 18 gtk_window_set_child (GTK_WINDOW (win), btn); - 19 g_signal_connect (btn, "clicked", G_CALLBACK (click_cb), NULL); - 20 - 21 gtk_widget_show (win); - 22 } - 23 - 24 int - 25 main (int argc, char **argv) { - 26 GtkApplication *app; - 27 int stat; - 28 - 29 app = gtk_application_new ("com.github.ToshioCP.lb2", G_APPLICATION_FLAGS_NONE); - 30 g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL); - 31 stat =g_application_run (G_APPLICATION (app), argc, argv); - 32 g_object_unref (app); - 33 return stat; - 34 } - 35 +~~~C + 1 #include + 2 + 3 static void + 4 click_cb (GtkButton *btn, gpointer user_data) { + 5 g_print ("Clicked.\n"); + 6 } + 7 + 8 static void + 9 on_activate (GApplication *app, gpointer user_data) { +10 GtkWidget *win; +11 GtkWidget *btn; +12 +13 win = gtk_application_window_new (GTK_APPLICATION (app)); +14 gtk_window_set_title (GTK_WINDOW (win), "lb2"); +15 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300); +16 +17 btn = gtk_button_new_with_label ("Click me"); +18 gtk_window_set_child (GTK_WINDOW (win), btn); +19 g_signal_connect (btn, "clicked", G_CALLBACK (click_cb), NULL); +20 +21 gtk_widget_show (win); +22 } +23 +24 int +25 main (int argc, char **argv) { +26 GtkApplication *app; +27 int stat; +28 +29 app = gtk_application_new ("com.github.ToshioCP.lb2", G_APPLICATION_FLAGS_NONE); +30 g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL); +31 stat =g_application_run (G_APPLICATION (app), argc, argv); +32 g_object_unref (app); +33 return stat; +34 } +35 +~~~ Look at the line 17 to 19. First, generate a GtkButton widget `btn` with a label "Click me". @@ -157,27 +161,29 @@ 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`. - 1 static void - 2 click_cb (GtkButton *btn, gpointer user_data) { - 3 GtkWindow *win = GTK_WINDOW (user_data); - 4 gtk_window_destroy (win); - 5 } - 6 - 7 static void - 8 on_activate (GApplication *app, gpointer user_data) { - 9 GtkWidget *win; - 10 GtkWidget *btn; - 11 - 12 win = gtk_application_window_new (GTK_APPLICATION (app)); - 13 gtk_window_set_title (GTK_WINDOW (win), "lb3"); - 14 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300); - 15 - 16 btn = gtk_button_new_with_label ("Quit"); - 17 gtk_window_set_child (GTK_WINDOW (win), btn); - 18 g_signal_connect (btn, "clicked", G_CALLBACK (click_cb), win); - 19 - 20 gtk_widget_show (win); - 21 } +~~~C + 1 static void + 2 click_cb (GtkButton *btn, gpointer user_data) { + 3 GtkWindow *win = GTK_WINDOW (user_data); + 4 gtk_window_destroy (win); + 5 } + 6 + 7 static void + 8 on_activate (GApplication *app, gpointer user_data) { + 9 GtkWidget *win; +10 GtkWidget *btn; +11 +12 win = gtk_application_window_new (GTK_APPLICATION (app)); +13 gtk_window_set_title (GTK_WINDOW (win), "lb3"); +14 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300); +15 +16 btn = gtk_button_new_with_label ("Quit"); +17 gtk_window_set_child (GTK_WINDOW (win), btn); +18 g_signal_connect (btn, "clicked", G_CALLBACK (click_cb), win); +19 +20 gtk_widget_show (win); +21 } +~~~ And the difference between `lb2.c` and `lb3.c` is as follows. @@ -237,63 +243,65 @@ After this, the Widgets are connected as following diagram. Now, code it. - 1 #include - 2 - 3 static void - 4 click1_cb (GtkButton *btn, gpointer user_data) { - 5 const gchar *s; - 6 - 7 s = gtk_button_get_label (btn); - 8 if (g_strcmp0 (s, "Hello.") == 0) - 9 gtk_button_set_label (btn, "Good-bye."); - 10 else - 11 gtk_button_set_label (btn, "Hello."); - 12 } - 13 - 14 static void - 15 click2_cb (GtkButton *btn, gpointer user_data) { - 16 GtkWindow *win = GTK_WINDOW (user_data); - 17 gtk_window_destroy (win); - 18 } - 19 - 20 static void - 21 on_activate (GApplication *app, gpointer user_data) { - 22 GtkWidget *win; - 23 GtkWidget *box; - 24 GtkWidget *btn1; - 25 GtkWidget *btn2; - 26 - 27 win = gtk_application_window_new (GTK_APPLICATION (app)); - 28 gtk_window_set_title (GTK_WINDOW (win), "lb4"); - 29 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300); - 30 - 31 box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5); - 32 gtk_box_set_homogeneous (GTK_BOX (box), TRUE); - 33 gtk_window_set_child (GTK_WINDOW (win), box); - 34 - 35 btn1 = gtk_button_new_with_label ("Hello."); - 36 g_signal_connect (btn1, "clicked", G_CALLBACK (click1_cb), NULL); - 37 - 38 btn2 = gtk_button_new_with_label ("Quit"); - 39 g_signal_connect (btn2, "clicked", G_CALLBACK (click2_cb), win); - 40 - 41 gtk_box_append (GTK_BOX (box), btn1); - 42 gtk_box_append (GTK_BOX (box), btn2); - 43 - 44 gtk_widget_show (win); - 45 } - 46 - 47 int - 48 main (int argc, char **argv) { - 49 GtkApplication *app; - 50 int stat; - 51 - 52 app = gtk_application_new ("com.github.ToshioCP.lb4", G_APPLICATION_FLAGS_NONE); - 53 g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL); - 54 stat =g_application_run (G_APPLICATION (app), argc, argv); - 55 g_object_unref (app); - 56 return stat; - 57 } +~~~C + 1 #include + 2 + 3 static void + 4 click1_cb (GtkButton *btn, gpointer user_data) { + 5 const gchar *s; + 6 + 7 s = gtk_button_get_label (btn); + 8 if (g_strcmp0 (s, "Hello.") == 0) + 9 gtk_button_set_label (btn, "Good-bye."); +10 else +11 gtk_button_set_label (btn, "Hello."); +12 } +13 +14 static void +15 click2_cb (GtkButton *btn, gpointer user_data) { +16 GtkWindow *win = GTK_WINDOW (user_data); +17 gtk_window_destroy (win); +18 } +19 +20 static void +21 on_activate (GApplication *app, gpointer user_data) { +22 GtkWidget *win; +23 GtkWidget *box; +24 GtkWidget *btn1; +25 GtkWidget *btn2; +26 +27 win = gtk_application_window_new (GTK_APPLICATION (app)); +28 gtk_window_set_title (GTK_WINDOW (win), "lb4"); +29 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300); +30 +31 box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5); +32 gtk_box_set_homogeneous (GTK_BOX (box), TRUE); +33 gtk_window_set_child (GTK_WINDOW (win), box); +34 +35 btn1 = gtk_button_new_with_label ("Hello."); +36 g_signal_connect (btn1, "clicked", G_CALLBACK (click1_cb), NULL); +37 +38 btn2 = gtk_button_new_with_label ("Quit"); +39 g_signal_connect (btn2, "clicked", G_CALLBACK (click2_cb), win); +40 +41 gtk_box_append (GTK_BOX (box), btn1); +42 gtk_box_append (GTK_BOX (box), btn2); +43 +44 gtk_widget_show (win); +45 } +46 +47 int +48 main (int argc, char **argv) { +49 GtkApplication *app; +50 int stat; +51 +52 app = gtk_application_new ("com.github.ToshioCP.lb4", G_APPLICATION_FLAGS_NONE); +53 g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL); +54 stat =g_application_run (G_APPLICATION (app), argc, argv); +55 g_object_unref (app); +56 return stat; +57 } +~~~ Look at the function `on_activate`. diff --git a/gfm/sec5.md b/gfm/sec5.md index 148928b..8f764b0 100644 --- a/gfm/sec5.md +++ b/gfm/sec5.md @@ -10,52 +10,54 @@ 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. - 1 #include - 2 - 3 static void - 4 on_activate (GApplication *app, gpointer user_data) { - 5 GtkWidget *win; - 6 GtkWidget *tv; - 7 GtkTextBuffer *tb; - 8 gchar *text; - 9 - 10 text = - 11 "Once upon a time, there was an old man who was called Taketori-no-Okina." - 12 "It is a japanese word that means a man whose work is making bamboo baskets.\n" - 13 "One day, he went into a mountain and found a shining bamboo." - 14 "\"What a mysterious bamboo it is!,\" he said." - 15 "He cut it, then there was a small cute baby girl in it." - 16 "The girl was shining faintly." - 17 "He thought this baby girl is a gift from Heaven and took her home.\n" - 18 "His wife was surprized at his tale." - 19 "They were very happy because they had no children." - 20 ; - 21 win = gtk_application_window_new (GTK_APPLICATION (app)); - 22 gtk_window_set_title (GTK_WINDOW (win), "Taketori"); - 23 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300); - 24 - 25 tv = gtk_text_view_new (); - 26 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); - 27 gtk_text_buffer_set_text (tb, text, -1); - 28 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR); - 29 - 30 gtk_window_set_child (GTK_WINDOW (win), tv); - 31 - 32 gtk_widget_show (win); - 33 } - 34 - 35 int - 36 main (int argc, char **argv) { - 37 GtkApplication *app; - 38 int stat; - 39 - 40 app = gtk_application_new ("com.github.ToshioCP.tfv1", G_APPLICATION_FLAGS_NONE); - 41 g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL); - 42 stat =g_application_run (G_APPLICATION (app), argc, argv); - 43 g_object_unref (app); - 44 return stat; - 45 } - 46 +~~~C + 1 #include + 2 + 3 static void + 4 on_activate (GApplication *app, gpointer user_data) { + 5 GtkWidget *win; + 6 GtkWidget *tv; + 7 GtkTextBuffer *tb; + 8 gchar *text; + 9 +10 text = +11 "Once upon a time, there was an old man who was called Taketori-no-Okina." +12 "It is a japanese word that means a man whose work is making bamboo baskets.\n" +13 "One day, he went into a mountain and found a shining bamboo." +14 "\"What a mysterious bamboo it is!,\" he said." +15 "He cut it, then there was a small cute baby girl in it." +16 "The girl was shining faintly." +17 "He thought this baby girl is a gift from Heaven and took her home.\n" +18 "His wife was surprized at his tale." +19 "They were very happy because they had no children." +20 ; +21 win = gtk_application_window_new (GTK_APPLICATION (app)); +22 gtk_window_set_title (GTK_WINDOW (win), "Taketori"); +23 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300); +24 +25 tv = gtk_text_view_new (); +26 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); +27 gtk_text_buffer_set_text (tb, text, -1); +28 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR); +29 +30 gtk_window_set_child (GTK_WINDOW (win), tv); +31 +32 gtk_widget_show (win); +33 } +34 +35 int +36 main (int argc, char **argv) { +37 GtkApplication *app; +38 int stat; +39 +40 app = gtk_application_new ("com.github.ToshioCP.tfv1", G_APPLICATION_FLAGS_NONE); +41 g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL); +42 stat =g_application_run (G_APPLICATION (app), argc, argv); +43 g_object_unref (app); +44 return stat; +45 } +46 +~~~ Look at line 25. GtkTextView is generated and its pointer is assigned to `tv`. @@ -108,56 +110,58 @@ 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`. - 1 #include - 2 - 3 static void - 4 on_activate (GApplication *app, gpointer user_data) { - 5 GtkWidget *win; - 6 GtkWidget *scr; - 7 GtkWidget *tv; - 8 GtkTextBuffer *tb; - 9 gchar *text; - 10 - 11 text = - 12 "Once upon a time, there was an old man who was called Taketori-no-Okina." - 13 "It is a japanese word that means a man whose work is making bamboo baskets.\n" - 14 "One day, he went into a mountain and found a shining bamboo." - 15 "\"What a mysterious bamboo it is!,\" he said." - 16 "He cut it, then there was a small cute baby girl in it." - 17 "The girl was shining faintly." - 18 "He thought this baby girl is a gift from Heaven and took her home.\n" - 19 "His wife was surprized at his tale." - 20 "They were very happy because they had no children." - 21 ; - 22 win = gtk_application_window_new (GTK_APPLICATION (app)); - 23 gtk_window_set_title (GTK_WINDOW (win), "Taketori"); - 24 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300); - 25 - 26 scr = gtk_scrolled_window_new (); - 27 gtk_window_set_child (GTK_WINDOW (win), scr); - 28 - 29 tv = gtk_text_view_new (); - 30 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); - 31 gtk_text_buffer_set_text (tb, text, -1); - 32 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR); - 33 - 34 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv); - 35 - 36 gtk_widget_show (win); - 37 } - 38 - 39 int - 40 main (int argc, char **argv) { - 41 GtkApplication *app; - 42 int stat; - 43 - 44 app = gtk_application_new ("com.github.ToshioCP.tfv2", G_APPLICATION_FLAGS_NONE); - 45 g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL); - 46 stat =g_application_run (G_APPLICATION (app), argc, argv); - 47 g_object_unref (app); - 48 return stat; - 49 } - 50 +~~~C + 1 #include + 2 + 3 static void + 4 on_activate (GApplication *app, gpointer user_data) { + 5 GtkWidget *win; + 6 GtkWidget *scr; + 7 GtkWidget *tv; + 8 GtkTextBuffer *tb; + 9 gchar *text; +10 +11 text = +12 "Once upon a time, there was an old man who was called Taketori-no-Okina." +13 "It is a japanese word that means a man whose work is making bamboo baskets.\n" +14 "One day, he went into a mountain and found a shining bamboo." +15 "\"What a mysterious bamboo it is!,\" he said." +16 "He cut it, then there was a small cute baby girl in it." +17 "The girl was shining faintly." +18 "He thought this baby girl is a gift from Heaven and took her home.\n" +19 "His wife was surprized at his tale." +20 "They were very happy because they had no children." +21 ; +22 win = gtk_application_window_new (GTK_APPLICATION (app)); +23 gtk_window_set_title (GTK_WINDOW (win), "Taketori"); +24 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300); +25 +26 scr = gtk_scrolled_window_new (); +27 gtk_window_set_child (GTK_WINDOW (win), scr); +28 +29 tv = gtk_text_view_new (); +30 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); +31 gtk_text_buffer_set_text (tb, text, -1); +32 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR); +33 +34 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv); +35 +36 gtk_widget_show (win); +37 } +38 +39 int +40 main (int argc, char **argv) { +41 GtkApplication *app; +42 int stat; +43 +44 app = gtk_application_new ("com.github.ToshioCP.tfv2", G_APPLICATION_FLAGS_NONE); +45 g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL); +46 stat =g_application_run (G_APPLICATION (app), argc, argv); +47 g_object_unref (app); +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. diff --git a/gfm/sec6.md b/gfm/sec6.md index b7c8442..cf5eb7d 100644 --- a/gfm/sec6.md +++ b/gfm/sec6.md @@ -89,62 +89,64 @@ It works as follows. The program is as follows. - 1 #include - 2 - 3 static void - 4 on_activate (GApplication *app, gpointer user_data) { - 5 g_print ("You need a filename argument.\n"); - 6 } - 7 - 8 static void - 9 on_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer user_data) { - 10 GtkWidget *win; - 11 GtkWidget *scr; - 12 GtkWidget *tv; - 13 GtkTextBuffer *tb; - 14 char *contents; - 15 gsize length; - 16 char *filename; - 17 - 18 win = gtk_application_window_new (GTK_APPLICATION (app)); - 19 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300); - 20 - 21 scr = gtk_scrolled_window_new (); - 22 gtk_window_set_child (GTK_WINDOW (win), scr); - 23 - 24 tv = gtk_text_view_new (); - 25 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); - 26 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR); - 27 gtk_text_view_set_editable (GTK_TEXT_VIEW (tv), FALSE); - 28 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv); - 29 - 30 if (g_file_load_contents (files[0], NULL, &contents, &length, NULL, NULL)) { - 31 gtk_text_buffer_set_text (tb, contents, length); - 32 g_free (contents); - 33 filename = g_file_get_basename (files[0]); - 34 gtk_window_set_title (GTK_WINDOW (win), filename); - 35 g_free (filename); - 36 gtk_widget_show (win); - 37 } else { - 38 filename = g_file_get_path (files[0]); - 39 g_print ("No such file: %s.\n", filename); - 40 gtk_window_destroy (GTK_WINDOW (win)); - 41 } - 42 } - 43 - 44 int - 45 main (int argc, char **argv) { - 46 GtkApplication *app; - 47 int stat; - 48 - 49 app = gtk_application_new ("com.github.ToshioCP.tfv3", G_APPLICATION_HANDLES_OPEN); - 50 g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL); - 51 g_signal_connect (app, "open", G_CALLBACK (on_open), NULL); - 52 stat =g_application_run (G_APPLICATION (app), argc, argv); - 53 g_object_unref (app); - 54 return stat; - 55 } - 56 +~~~C + 1 #include + 2 + 3 static void + 4 on_activate (GApplication *app, gpointer user_data) { + 5 g_print ("You need a filename argument.\n"); + 6 } + 7 + 8 static void + 9 on_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer user_data) { +10 GtkWidget *win; +11 GtkWidget *scr; +12 GtkWidget *tv; +13 GtkTextBuffer *tb; +14 char *contents; +15 gsize length; +16 char *filename; +17 +18 win = gtk_application_window_new (GTK_APPLICATION (app)); +19 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300); +20 +21 scr = gtk_scrolled_window_new (); +22 gtk_window_set_child (GTK_WINDOW (win), scr); +23 +24 tv = gtk_text_view_new (); +25 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); +26 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR); +27 gtk_text_view_set_editable (GTK_TEXT_VIEW (tv), FALSE); +28 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv); +29 +30 if (g_file_load_contents (files[0], NULL, &contents, &length, NULL, NULL)) { +31 gtk_text_buffer_set_text (tb, contents, length); +32 g_free (contents); +33 filename = g_file_get_basename (files[0]); +34 gtk_window_set_title (GTK_WINDOW (win), filename); +35 g_free (filename); +36 gtk_widget_show (win); +37 } else { +38 filename = g_file_get_path (files[0]); +39 g_print ("No such file: %s.\n", filename); +40 gtk_window_destroy (GTK_WINDOW (win)); +41 } +42 } +43 +44 int +45 main (int argc, char **argv) { +46 GtkApplication *app; +47 int stat; +48 +49 app = gtk_application_new ("com.github.ToshioCP.tfv3", G_APPLICATION_HANDLES_OPEN); +50 g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL); +51 g_signal_connect (app, "open", G_CALLBACK (on_open), NULL); +52 stat =g_application_run (G_APPLICATION (app), argc, argv); +53 g_object_unref (app); +54 return stat; +55 } +56 +~~~ Save it as `tfv3.c`. Then compile and run it. @@ -211,77 +213,79 @@ It is shown in the right screenshot. GtkNotebook widget is between GtkApplicationWindow and GtkScrolledWindow. Now I want to show you the program `tfv4.c`. - 1 #include - 2 - 3 static void - 4 on_activate (GApplication *app, gpointer user_data) { - 5 g_print ("You need a filename argument.\n"); - 6 } - 7 - 8 static void - 9 on_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer user_data) { - 10 GtkWidget *win; - 11 GtkWidget *nb; - 12 GtkWidget *lab; - 13 GtkNotebookPage *nbp; - 14 GtkWidget *scr; - 15 GtkWidget *tv; - 16 GtkTextBuffer *tb; - 17 char *contents; - 18 gsize length; - 19 char *filename; - 20 int i; - 21 - 22 win = gtk_application_window_new (GTK_APPLICATION (app)); - 23 gtk_window_set_title (GTK_WINDOW (win), "file viewer"); - 24 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300); - 25 gtk_window_maximize (GTK_WINDOW (win)); - 26 - 27 nb = gtk_notebook_new (); - 28 gtk_window_set_child (GTK_WINDOW (win), nb); - 29 - 30 for (i = 0; i < n_files; i++) { - 31 if (g_file_load_contents (files[i], NULL, &contents, &length, NULL, NULL)) { - 32 scr = gtk_scrolled_window_new (); - 33 tv = gtk_text_view_new (); - 34 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); - 35 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR); - 36 gtk_text_view_set_editable (GTK_TEXT_VIEW (tv), FALSE); - 37 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv); - 38 - 39 gtk_text_buffer_set_text (tb, contents, length); - 40 g_free (contents); - 41 filename = g_file_get_basename (files[i]); - 42 lab = gtk_label_new (filename); - 43 gtk_notebook_append_page (GTK_NOTEBOOK (nb), scr, lab); - 44 nbp = gtk_notebook_get_page (GTK_NOTEBOOK (nb), scr); - 45 g_object_set (nbp, "tab-expand", TRUE, NULL); - 46 g_free (filename); - 47 } else { - 48 filename = g_file_get_path (files[i]); - 49 g_print ("No such file: %s.\n", filename); - 50 g_free (filename); - 51 } - 52 } - 53 if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb)) > 0) - 54 gtk_widget_show (win); - 55 else - 56 gtk_window_destroy (GTK_WINDOW (win)); - 57 } - 58 - 59 int - 60 main (int argc, char **argv) { - 61 GtkApplication *app; - 62 int stat; - 63 - 64 app = gtk_application_new ("com.github.ToshioCP.tfv4", G_APPLICATION_HANDLES_OPEN); - 65 g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL); - 66 g_signal_connect (app, "open", G_CALLBACK (on_open), NULL); - 67 stat =g_application_run (G_APPLICATION (app), argc, argv); - 68 g_object_unref (app); - 69 return stat; - 70 } - 71 +~~~C + 1 #include + 2 + 3 static void + 4 on_activate (GApplication *app, gpointer user_data) { + 5 g_print ("You need a filename argument.\n"); + 6 } + 7 + 8 static void + 9 on_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer user_data) { +10 GtkWidget *win; +11 GtkWidget *nb; +12 GtkWidget *lab; +13 GtkNotebookPage *nbp; +14 GtkWidget *scr; +15 GtkWidget *tv; +16 GtkTextBuffer *tb; +17 char *contents; +18 gsize length; +19 char *filename; +20 int i; +21 +22 win = gtk_application_window_new (GTK_APPLICATION (app)); +23 gtk_window_set_title (GTK_WINDOW (win), "file viewer"); +24 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300); +25 gtk_window_maximize (GTK_WINDOW (win)); +26 +27 nb = gtk_notebook_new (); +28 gtk_window_set_child (GTK_WINDOW (win), nb); +29 +30 for (i = 0; i < n_files; i++) { +31 if (g_file_load_contents (files[i], NULL, &contents, &length, NULL, NULL)) { +32 scr = gtk_scrolled_window_new (); +33 tv = gtk_text_view_new (); +34 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); +35 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR); +36 gtk_text_view_set_editable (GTK_TEXT_VIEW (tv), FALSE); +37 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv); +38 +39 gtk_text_buffer_set_text (tb, contents, length); +40 g_free (contents); +41 filename = g_file_get_basename (files[i]); +42 lab = gtk_label_new (filename); +43 gtk_notebook_append_page (GTK_NOTEBOOK (nb), scr, lab); +44 nbp = gtk_notebook_get_page (GTK_NOTEBOOK (nb), scr); +45 g_object_set (nbp, "tab-expand", TRUE, NULL); +46 g_free (filename); +47 } else { +48 filename = g_file_get_path (files[i]); +49 g_print ("No such file: %s.\n", filename); +50 g_free (filename); +51 } +52 } +53 if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb)) > 0) +54 gtk_widget_show (win); +55 else +56 gtk_window_destroy (GTK_WINDOW (win)); +57 } +58 +59 int +60 main (int argc, char **argv) { +61 GtkApplication *app; +62 int stat; +63 +64 app = gtk_application_new ("com.github.ToshioCP.tfv4", G_APPLICATION_HANDLES_OPEN); +65 g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL); +66 g_signal_connect (app, "open", G_CALLBACK (on_open), NULL); +67 stat =g_application_run (G_APPLICATION (app), argc, argv); +68 g_object_unref (app); +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. diff --git a/gfm/sec7.md b/gfm/sec7.md index 22359aa..6045a27 100644 --- a/gfm/sec7.md +++ b/gfm/sec7.md @@ -57,39 +57,41 @@ 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. - #define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type () - G_DECLARE_FINAL_TYPE (TfeTextView, tfe_text_view, TFE, TEXT_VIEW, GtkTextView) +~~~C +#define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type () +G_DECLARE_FINAL_TYPE (TfeTextView, tfe_text_view, TFE, TEXT_VIEW, GtkTextView) - struct _TfeTextView - { - GtkTextView parent; - GFile *file; - }; +struct _TfeTextView +{ + GtkTextView parent; + GFile *file; +}; - G_DEFINE_TYPE (TfeTextView, tfe_text_view, GTK_TYPE_TEXT_VIEW); +G_DEFINE_TYPE (TfeTextView, tfe_text_view, GTK_TYPE_TEXT_VIEW); - static void - tfe_text_view_init (TfeTextView *tv) { - } +static void +tfe_text_view_init (TfeTextView *tv) { +} - static void - tfe_text_view_class_init (TfeTextViewClass *class) { - } +static void +tfe_text_view_class_init (TfeTextViewClass *class) { +} - void - tfe_text_view_set_file (TfeTextView *tv, GFile *f) { - tv -> file = f; - } +void +tfe_text_view_set_file (TfeTextView *tv, GFile *f) { + tv -> file = f; +} - GFile * - tfe_text_view_get_file (TfeTextView *tv) { - return tv -> file; - } +GFile * +tfe_text_view_get_file (TfeTextView *tv) { + return tv -> file; +} - GtkWidget * - tfe_text_view_new (void) { - return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL)); - } +GtkWidget * +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,31 +159,33 @@ 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. - 1 static gboolean - 2 before_close (GtkWindow *win, GtkWidget *nb) { - 3 GtkWidget *scr; - 4 GtkWidget *tv; - 5 GFile *file; - 6 GtkTextBuffer *tb; - 7 GtkTextIter start_iter; - 8 GtkTextIter end_iter; - 9 char *contents; - 10 unsigned int n; - 11 unsigned int i; - 12 - 13 n = gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb)); - 14 for (i = 0; i < n; ++i) { - 15 scr = gtk_notebook_get_nth_page (GTK_NOTEBOOK (nb), i); - 16 tv = gtk_scrolled_window_get_child (GTK_SCROLLED_WINDOW (scr)); - 17 file = tfe_text_view_get_file (TFE_TEXT_VIEW (tv)); - 18 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); - 19 gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter); - 20 contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE); - 21 if (! g_file_replace_contents (file, contents, strlen (contents), NULL, TRUE, G_FILE_CREATE_NONE, NULL, NULL, NULL)) - 22 g_print ("ERROR : Can't save %s.", g_file_get_path (file)); - 23 } - 24 return FALSE; - 25 } +~~~C + 1 static gboolean + 2 before_close (GtkWindow *win, GtkWidget *nb) { + 3 GtkWidget *scr; + 4 GtkWidget *tv; + 5 GFile *file; + 6 GtkTextBuffer *tb; + 7 GtkTextIter start_iter; + 8 GtkTextIter end_iter; + 9 char *contents; +10 unsigned int n; +11 unsigned int i; +12 +13 n = gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb)); +14 for (i = 0; i < n; ++i) { +15 scr = gtk_notebook_get_nth_page (GTK_NOTEBOOK (nb), i); +16 tv = gtk_scrolled_window_get_child (GTK_SCROLLED_WINDOW (scr)); +17 file = tfe_text_view_get_file (TFE_TEXT_VIEW (tv)); +18 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); +19 gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter); +20 contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE); +21 if (! g_file_replace_contents (file, contents, strlen (contents), NULL, TRUE, G_FILE_CREATE_NONE, NULL, NULL, NULL)) +22 g_print ("ERROR : Can't save %s.", g_file_get_path (file)); +23 } +24 return FALSE; +25 } +~~~ The numbers on the left of items are line numbers in the source code. @@ -195,142 +199,144 @@ 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. - 1 #include - 2 - 3 /* Define TfeTextView Widget which is the child object of GtkTextView */ - 4 - 5 #define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type () - 6 G_DECLARE_FINAL_TYPE (TfeTextView, tfe_text_view, TFE, TEXT_VIEW, GtkTextView) - 7 - 8 struct _TfeTextView - 9 { - 10 GtkTextView parent; - 11 GFile *file; - 12 }; - 13 - 14 G_DEFINE_TYPE (TfeTextView, tfe_text_view, GTK_TYPE_TEXT_VIEW); - 15 - 16 static void - 17 tfe_text_view_init (TfeTextView *tv) { - 18 } - 19 - 20 static void - 21 tfe_text_view_class_init (TfeTextViewClass *class) { - 22 } - 23 - 24 void - 25 tfe_text_view_set_file (TfeTextView *tv, GFile *f) { - 26 tv -> file = f; - 27 } - 28 - 29 GFile * - 30 tfe_text_view_get_file (TfeTextView *tv) { - 31 return tv -> file; - 32 } - 33 - 34 GtkWidget * - 35 tfe_text_view_new (void) { - 36 return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL)); - 37 } - 38 - 39 /* ---------- end of the definition of TfeTextView ---------- */ - 40 - 41 static gboolean - 42 before_close (GtkWindow *win, GtkWidget *nb) { - 43 GtkWidget *scr; - 44 GtkWidget *tv; - 45 GFile *file; - 46 GtkTextBuffer *tb; - 47 GtkTextIter start_iter; - 48 GtkTextIter end_iter; - 49 char *contents; - 50 unsigned int n; - 51 unsigned int i; - 52 - 53 n = gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb)); - 54 for (i = 0; i < n; ++i) { - 55 scr = gtk_notebook_get_nth_page (GTK_NOTEBOOK (nb), i); - 56 tv = gtk_scrolled_window_get_child (GTK_SCROLLED_WINDOW (scr)); - 57 file = tfe_text_view_get_file (TFE_TEXT_VIEW (tv)); - 58 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); - 59 gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter); - 60 contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE); - 61 if (! g_file_replace_contents (file, contents, strlen (contents), NULL, TRUE, G_FILE_CREATE_NONE, NULL, NULL, NULL)) - 62 g_print ("ERROR : Can't save %s.", g_file_get_path (file)); - 63 } - 64 return FALSE; - 65 } - 66 - 67 static void - 68 on_activate (GApplication *app, gpointer user_data) { - 69 g_print ("You need a filename argument.\n"); - 70 } - 71 - 72 static void - 73 on_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer user_data) { - 74 GtkWidget *win; - 75 GtkWidget *nb; - 76 GtkWidget *lab; - 77 GtkNotebookPage *nbp; - 78 GtkWidget *scr; - 79 GtkWidget *tv; - 80 GtkTextBuffer *tb; - 81 char *contents; - 82 gsize length; - 83 char *filename; - 84 int i; - 85 - 86 win = gtk_application_window_new (GTK_APPLICATION (app)); - 87 gtk_window_set_title (GTK_WINDOW (win), "file editor"); - 88 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300); - 89 gtk_window_maximize (GTK_WINDOW (win)); - 90 - 91 nb = gtk_notebook_new (); - 92 gtk_window_set_child (GTK_WINDOW (win), nb); - 93 - 94 for (i = 0; i < n_files; i++) { - 95 if (g_file_load_contents (files[i], NULL, &contents, &length, NULL, NULL)) { - 96 scr = gtk_scrolled_window_new (); - 97 tv = tfe_text_view_new (); - 98 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); - 99 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR); - 100 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv); - 101 - 102 tfe_text_view_set_file (TFE_TEXT_VIEW (tv), g_file_dup (files[i])); - 103 gtk_text_buffer_set_text (tb, contents, length); - 104 g_free (contents); - 105 filename = g_file_get_basename (files[i]); - 106 lab = gtk_label_new (filename); - 107 gtk_notebook_append_page (GTK_NOTEBOOK (nb), scr, lab); - 108 nbp = gtk_notebook_get_page (GTK_NOTEBOOK (nb), scr); - 109 g_object_set (nbp, "tab-expand", TRUE, NULL); - 110 g_free (filename); - 111 } else { - 112 filename = g_file_get_path (files[i]); - 113 g_print ("No such file: %s.\n", filename); - 114 g_free (filename); - 115 } - 116 } - 117 if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb)) > 0) { - 118 g_signal_connect (win, "close-request", G_CALLBACK (before_close), nb); - 119 gtk_widget_show (win); - 120 } else - 121 gtk_window_destroy (GTK_WINDOW (win)); - 122 } - 123 - 124 int - 125 main (int argc, char **argv) { - 126 GtkApplication *app; - 127 int stat; - 128 - 129 app = gtk_application_new ("com.github.ToshioCP.tfe1", G_APPLICATION_HANDLES_OPEN); - 130 g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL); - 131 g_signal_connect (app, "open", G_CALLBACK (on_open), NULL); - 132 stat =g_application_run (G_APPLICATION (app), argc, argv); - 133 g_object_unref (app); - 134 return stat; - 135 } - 136 +~~~C + 1 #include + 2 + 3 /* Define TfeTextView Widget which is the child object of GtkTextView */ + 4 + 5 #define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type () + 6 G_DECLARE_FINAL_TYPE (TfeTextView, tfe_text_view, TFE, TEXT_VIEW, GtkTextView) + 7 + 8 struct _TfeTextView + 9 { + 10 GtkTextView parent; + 11 GFile *file; + 12 }; + 13 + 14 G_DEFINE_TYPE (TfeTextView, tfe_text_view, GTK_TYPE_TEXT_VIEW); + 15 + 16 static void + 17 tfe_text_view_init (TfeTextView *tv) { + 18 } + 19 + 20 static void + 21 tfe_text_view_class_init (TfeTextViewClass *class) { + 22 } + 23 + 24 void + 25 tfe_text_view_set_file (TfeTextView *tv, GFile *f) { + 26 tv -> file = f; + 27 } + 28 + 29 GFile * + 30 tfe_text_view_get_file (TfeTextView *tv) { + 31 return tv -> file; + 32 } + 33 + 34 GtkWidget * + 35 tfe_text_view_new (void) { + 36 return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL)); + 37 } + 38 + 39 /* ---------- end of the definition of TfeTextView ---------- */ + 40 + 41 static gboolean + 42 before_close (GtkWindow *win, GtkWidget *nb) { + 43 GtkWidget *scr; + 44 GtkWidget *tv; + 45 GFile *file; + 46 GtkTextBuffer *tb; + 47 GtkTextIter start_iter; + 48 GtkTextIter end_iter; + 49 char *contents; + 50 unsigned int n; + 51 unsigned int i; + 52 + 53 n = gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb)); + 54 for (i = 0; i < n; ++i) { + 55 scr = gtk_notebook_get_nth_page (GTK_NOTEBOOK (nb), i); + 56 tv = gtk_scrolled_window_get_child (GTK_SCROLLED_WINDOW (scr)); + 57 file = tfe_text_view_get_file (TFE_TEXT_VIEW (tv)); + 58 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); + 59 gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter); + 60 contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE); + 61 if (! g_file_replace_contents (file, contents, strlen (contents), NULL, TRUE, G_FILE_CREATE_NONE, NULL, NULL, NULL)) + 62 g_print ("ERROR : Can't save %s.", g_file_get_path (file)); + 63 } + 64 return FALSE; + 65 } + 66 + 67 static void + 68 on_activate (GApplication *app, gpointer user_data) { + 69 g_print ("You need a filename argument.\n"); + 70 } + 71 + 72 static void + 73 on_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer user_data) { + 74 GtkWidget *win; + 75 GtkWidget *nb; + 76 GtkWidget *lab; + 77 GtkNotebookPage *nbp; + 78 GtkWidget *scr; + 79 GtkWidget *tv; + 80 GtkTextBuffer *tb; + 81 char *contents; + 82 gsize length; + 83 char *filename; + 84 int i; + 85 + 86 win = gtk_application_window_new (GTK_APPLICATION (app)); + 87 gtk_window_set_title (GTK_WINDOW (win), "file editor"); + 88 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300); + 89 gtk_window_maximize (GTK_WINDOW (win)); + 90 + 91 nb = gtk_notebook_new (); + 92 gtk_window_set_child (GTK_WINDOW (win), nb); + 93 + 94 for (i = 0; i < n_files; i++) { + 95 if (g_file_load_contents (files[i], NULL, &contents, &length, NULL, NULL)) { + 96 scr = gtk_scrolled_window_new (); + 97 tv = tfe_text_view_new (); + 98 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); + 99 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR); +100 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv); +101 +102 tfe_text_view_set_file (TFE_TEXT_VIEW (tv), g_file_dup (files[i])); +103 gtk_text_buffer_set_text (tb, contents, length); +104 g_free (contents); +105 filename = g_file_get_basename (files[i]); +106 lab = gtk_label_new (filename); +107 gtk_notebook_append_page (GTK_NOTEBOOK (nb), scr, lab); +108 nbp = gtk_notebook_get_page (GTK_NOTEBOOK (nb), scr); +109 g_object_set (nbp, "tab-expand", TRUE, NULL); +110 g_free (filename); +111 } else { +112 filename = g_file_get_path (files[i]); +113 g_print ("No such file: %s.\n", filename); +114 g_free (filename); +115 } +116 } +117 if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb)) > 0) { +118 g_signal_connect (win, "close-request", G_CALLBACK (before_close), nb); +119 gtk_widget_show (win); +120 } else +121 gtk_window_destroy (GTK_WINDOW (win)); +122 } +123 +124 int +125 main (int argc, char **argv) { +126 GtkApplication *app; +127 int stat; +128 +129 app = gtk_application_new ("com.github.ToshioCP.tfe1", G_APPLICATION_HANDLES_OPEN); +130 g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL); +131 g_signal_connect (app, "open", G_CALLBACK (on_open), NULL); +132 stat =g_application_run (G_APPLICATION (app), argc, argv); +133 g_object_unref (app); +134 return stat; +135 } +136 +~~~ - 102: set the pointer to GFile into TfeTextView. `files[i]` is a pointer to GFile structure. diff --git a/gfm/sec8.md b/gfm/sec8.md index c04730a..30a6a47 100644 --- a/gfm/sec8.md +++ b/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,92 +16,94 @@ 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. - 1 static void - 2 on_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer user_data) { - 3 GtkWidget *win; - 4 GtkWidget *nb; - 5 GtkWidget *lab; - 6 GtkNotebookPage *nbp; - 7 GtkWidget *scr; - 8 GtkWidget *tv; - 9 GtkTextBuffer *tb; - 10 char *contents; - 11 gsize length; - 12 char *filename; - 13 int i; - 14 - 15 GtkWidget *boxv; - 16 GtkWidget *boxh; - 17 GtkWidget *dmy1; - 18 GtkWidget *dmy2; - 19 GtkWidget *dmy3; - 20 GtkWidget *btnn; /* button for new */ - 21 GtkWidget *btno; /* button for open */ - 22 GtkWidget *btns; /* button for save */ - 23 GtkWidget *btnc; /* button for close */ - 24 - 25 win = gtk_application_window_new (GTK_APPLICATION (app)); - 26 gtk_window_set_title (GTK_WINDOW (win), "file editor"); - 27 gtk_window_set_default_size (GTK_WINDOW (win), 600, 400); - 28 - 29 boxv = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); - 30 gtk_window_set_child (GTK_WINDOW (win), boxv); - 31 - 32 boxh = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); - 33 gtk_box_append (GTK_BOX (boxv), boxh); - 34 - 35 dmy1 = gtk_label_new(NULL); /* dummy label for left space */ - 36 gtk_label_set_width_chars (GTK_LABEL (dmy1), 10); - 37 dmy2 = gtk_label_new(NULL); /* dummy label for center space */ - 38 gtk_widget_set_hexpand (dmy2, TRUE); - 39 dmy3 = gtk_label_new(NULL); /* dummy label for right space */ - 40 gtk_label_set_width_chars (GTK_LABEL (dmy3), 10); - 41 btnn = gtk_button_new_with_label ("New"); - 42 btno = gtk_button_new_with_label ("Open"); - 43 btns = gtk_button_new_with_label ("Save"); - 44 btnc = gtk_button_new_with_label ("Close"); - 45 - 46 gtk_box_append (GTK_BOX (boxh), dmy1); - 47 gtk_box_append (GTK_BOX (boxh), btnn); - 48 gtk_box_append (GTK_BOX (boxh), btno); - 49 gtk_box_append (GTK_BOX (boxh), dmy2); - 50 gtk_box_append (GTK_BOX (boxh), btns); - 51 gtk_box_append (GTK_BOX (boxh), btnc); - 52 gtk_box_append (GTK_BOX (boxh), dmy3); - 53 - 54 nb = gtk_notebook_new (); - 55 gtk_widget_set_hexpand (nb, TRUE); - 56 gtk_widget_set_vexpand (nb, TRUE); - 57 gtk_box_append (GTK_BOX (boxv), nb); - 58 - 59 for (i = 0; i < n_files; i++) { - 60 if (g_file_load_contents (files[i], NULL, &contents, &length, NULL, NULL)) { - 61 scr = gtk_scrolled_window_new (); - 62 tv = tfe_text_view_new (); - 63 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); - 64 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR); - 65 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv); - 66 - 67 tfe_text_view_set_file (TFE_TEXT_VIEW (tv), g_file_dup (files[i])); - 68 gtk_text_buffer_set_text (tb, contents, length); - 69 g_free (contents); - 70 filename = g_file_get_basename (files[i]); - 71 lab = gtk_label_new (filename); - 72 gtk_notebook_append_page (GTK_NOTEBOOK (nb), scr, lab); - 73 nbp = gtk_notebook_get_page (GTK_NOTEBOOK (nb), scr); - 74 g_object_set (nbp, "tab-expand", TRUE, NULL); - 75 g_free (filename); - 76 } else { - 77 filename = g_file_get_path (files[i]); - 78 g_print ("No such file: %s.\n", filename); - 79 g_free (filename); - 80 } - 81 } - 82 if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb)) > 0) { - 83 gtk_widget_show (win); - 84 } else - 85 gtk_window_destroy (GTK_WINDOW (win)); - 86 } +~~~C + 1 static void + 2 on_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer user_data) { + 3 GtkWidget *win; + 4 GtkWidget *nb; + 5 GtkWidget *lab; + 6 GtkNotebookPage *nbp; + 7 GtkWidget *scr; + 8 GtkWidget *tv; + 9 GtkTextBuffer *tb; +10 char *contents; +11 gsize length; +12 char *filename; +13 int i; +14 +15 GtkWidget *boxv; +16 GtkWidget *boxh; +17 GtkWidget *dmy1; +18 GtkWidget *dmy2; +19 GtkWidget *dmy3; +20 GtkWidget *btnn; /* button for new */ +21 GtkWidget *btno; /* button for open */ +22 GtkWidget *btns; /* button for save */ +23 GtkWidget *btnc; /* button for close */ +24 +25 win = gtk_application_window_new (GTK_APPLICATION (app)); +26 gtk_window_set_title (GTK_WINDOW (win), "file editor"); +27 gtk_window_set_default_size (GTK_WINDOW (win), 600, 400); +28 +29 boxv = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); +30 gtk_window_set_child (GTK_WINDOW (win), boxv); +31 +32 boxh = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); +33 gtk_box_append (GTK_BOX (boxv), boxh); +34 +35 dmy1 = gtk_label_new(NULL); /* dummy label for left space */ +36 gtk_label_set_width_chars (GTK_LABEL (dmy1), 10); +37 dmy2 = gtk_label_new(NULL); /* dummy label for center space */ +38 gtk_widget_set_hexpand (dmy2, TRUE); +39 dmy3 = gtk_label_new(NULL); /* dummy label for right space */ +40 gtk_label_set_width_chars (GTK_LABEL (dmy3), 10); +41 btnn = gtk_button_new_with_label ("New"); +42 btno = gtk_button_new_with_label ("Open"); +43 btns = gtk_button_new_with_label ("Save"); +44 btnc = gtk_button_new_with_label ("Close"); +45 +46 gtk_box_append (GTK_BOX (boxh), dmy1); +47 gtk_box_append (GTK_BOX (boxh), btnn); +48 gtk_box_append (GTK_BOX (boxh), btno); +49 gtk_box_append (GTK_BOX (boxh), dmy2); +50 gtk_box_append (GTK_BOX (boxh), btns); +51 gtk_box_append (GTK_BOX (boxh), btnc); +52 gtk_box_append (GTK_BOX (boxh), dmy3); +53 +54 nb = gtk_notebook_new (); +55 gtk_widget_set_hexpand (nb, TRUE); +56 gtk_widget_set_vexpand (nb, TRUE); +57 gtk_box_append (GTK_BOX (boxv), nb); +58 +59 for (i = 0; i < n_files; i++) { +60 if (g_file_load_contents (files[i], NULL, &contents, &length, NULL, NULL)) { +61 scr = gtk_scrolled_window_new (); +62 tv = tfe_text_view_new (); +63 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); +64 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR); +65 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv); +66 +67 tfe_text_view_set_file (TFE_TEXT_VIEW (tv), g_file_dup (files[i])); +68 gtk_text_buffer_set_text (tb, contents, length); +69 g_free (contents); +70 filename = g_file_get_basename (files[i]); +71 lab = gtk_label_new (filename); +72 gtk_notebook_append_page (GTK_NOTEBOOK (nb), scr, lab); +73 nbp = gtk_notebook_get_page (GTK_NOTEBOOK (nb), scr); +74 g_object_set (nbp, "tab-expand", TRUE, NULL); +75 g_free (filename); +76 } else { +77 filename = g_file_get_path (files[i]); +78 g_print ("No such file: %s.\n", filename); +79 g_free (filename); +80 } +81 } +82 if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb)) > 0) { +83 gtk_widget_show (win); +84 } else +85 gtk_window_destroy (GTK_WINDOW (win)); +86 } +~~~ The point is how to build the window. @@ -135,65 +137,67 @@ It reduces the cumbersom work. First, let's look at the ui file `tfe3.ui` that defines a structure of the widgets. - 1 - 2 - 3 file editor - 4 600 - 5 400 - 6 - 7 - 8 GTK_ORIENTATION_VERTICAL - 9 - 10 - 11 GTK_ORIENTATION_HORIZONTAL - 12 - 13 - 14 10 - 15 - 16 - 17 - 18 - 19 New - 20 - 21 - 22 - 23 - 24 Open - 25 - 26 - 27 - 28 - 29 TRUE - 30 - 31 - 32 - 33 - 34 Save - 35 - 36 - 37 - 38 - 39 Close - 40 - 41 - 42 - 43 - 44 10 - 45 - 46 - 47 - 48 - 49 - 50 - 51 TRUE - 52 TRUE - 53 - 54 - 55 - 56 - 57 - 58 - 59 +~~~xml + 1 + 2 + 3 file editor + 4 600 + 5 400 + 6 + 7 + 8 GTK_ORIENTATION_VERTICAL + 9 +10 +11 GTK_ORIENTATION_HORIZONTAL +12 +13 +14 10 +15 +16 +17 +18 +19 New +20 +21 +22 +23 +24 Open +25 +26 +27 +28 +29 TRUE +30 +31 +32 +33 +34 Save +35 +36 +37 +38 +39 Close +40 +41 +42 +43 +44 10 +45 +46 +47 +48 +49 +50 +51 TRUE +52 TRUE +53 +54 +55 +56 +57 +58 +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. - GtkBuilder *build; +~~~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")); +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,54 +301,56 @@ 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. - 1 static void - 2 on_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer user_data) { - 3 GtkWidget *win; - 4 GtkWidget *nb; - 5 GtkWidget *lab; - 6 GtkNotebookPage *nbp; - 7 GtkWidget *scr; - 8 GtkWidget *tv; - 9 GtkTextBuffer *tb; - 10 char *contents; - 11 gsize length; - 12 char *filename; - 13 int i; - 14 GtkBuilder *build; - 15 - 16 build = gtk_builder_new_from_file ("tfe3.ui"); - 17 win = GTK_WIDGET (gtk_builder_get_object (build, "win")); - 18 gtk_window_set_application (GTK_WINDOW (win), GTK_APPLICATION (app)); - 19 nb = GTK_WIDGET (gtk_builder_get_object (build, "nb")); - 20 g_object_unref(build); - 21 for (i = 0; i < n_files; i++) { - 22 if (g_file_load_contents (files[i], NULL, &contents, &length, NULL, NULL)) { - 23 scr = gtk_scrolled_window_new (); - 24 tv = tfe_text_view_new (); - 25 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); - 26 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR); - 27 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv); - 28 - 29 tfe_text_view_set_file (TFE_TEXT_VIEW (tv), g_file_dup (files[i])); - 30 gtk_text_buffer_set_text (tb, contents, length); - 31 g_free (contents); - 32 filename = g_file_get_basename (files[i]); - 33 lab = gtk_label_new (filename); - 34 gtk_notebook_append_page (GTK_NOTEBOOK (nb), scr, lab); - 35 nbp = gtk_notebook_get_page (GTK_NOTEBOOK (nb), scr); - 36 g_object_set (nbp, "tab-expand", TRUE, NULL); - 37 g_free (filename); - 38 } else { - 39 filename = g_file_get_path (files[i]); - 40 g_print ("No such file: %s.\n", filename); - 41 g_free (filename); - 42 } - 43 } - 44 if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb)) > 0) { - 45 gtk_widget_show (win); - 46 } else - 47 gtk_window_destroy (GTK_WINDOW (win)); - 48 } +~~~C + 1 static void + 2 on_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer user_data) { + 3 GtkWidget *win; + 4 GtkWidget *nb; + 5 GtkWidget *lab; + 6 GtkNotebookPage *nbp; + 7 GtkWidget *scr; + 8 GtkWidget *tv; + 9 GtkTextBuffer *tb; +10 char *contents; +11 gsize length; +12 char *filename; +13 int i; +14 GtkBuilder *build; +15 +16 build = gtk_builder_new_from_file ("tfe3.ui"); +17 win = GTK_WIDGET (gtk_builder_get_object (build, "win")); +18 gtk_window_set_application (GTK_WINDOW (win), GTK_APPLICATION (app)); +19 nb = GTK_WIDGET (gtk_builder_get_object (build, "nb")); +20 g_object_unref(build); +21 for (i = 0; i < n_files; i++) { +22 if (g_file_load_contents (files[i], NULL, &contents, &length, NULL, NULL)) { +23 scr = gtk_scrolled_window_new (); +24 tv = tfe_text_view_new (); +25 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); +26 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR); +27 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv); +28 +29 tfe_text_view_set_file (TFE_TEXT_VIEW (tv), g_file_dup (files[i])); +30 gtk_text_buffer_set_text (tb, contents, length); +31 g_free (contents); +32 filename = g_file_get_basename (files[i]); +33 lab = gtk_label_new (filename); +34 gtk_notebook_append_page (GTK_NOTEBOOK (nb), scr, lab); +35 nbp = gtk_notebook_get_page (GTK_NOTEBOOK (nb), scr); +36 g_object_set (nbp, "tab-expand", TRUE, NULL); +37 g_free (filename); +38 } else { +39 filename = g_file_get_path (files[i]); +40 g_print ("No such file: %s.\n", filename); +41 g_free (filename); +42 } +43 } +44 if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb)) > 0) { +45 gtk_widget_show (win); +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,22 +361,24 @@ 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. - char *uistring; +~~~C +char *uistring; - uistring = - "" - "" - "file editor" - "600" - "400" - "" - "" - "GTK_ORIENTATION_VERTICAL" - ... ... ... - ... ... ... - ""; +uistring = +"" + "" + "file editor" + "600" + "400" + "" + "" + "GTK_ORIENTATION_VERTICAL" +... ... ... +... ... ... +""; - build = gtk_builder_new_from_stringfile (uistring); +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. - 1 - 2 - 3 - 4 tfe3.ui - 5 - 6 +~~~xml +1 +2 +3 +4 tfe3.ui +5 +6 +~~~ - 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 - # include "resources.c" - ... ... ... - ... ... ... - build = gtk_builder_new_from_resource ("/com/github/ToshioCP/tfe3/tfe3.ui"); - ... ... ... - ... ... ... +~~~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. diff --git a/gfm/sec9.md b/gfm/sec9.md index f1028bc..965e4aa 100644 --- a/gfm/sec9.md +++ b/gfm/sec9.md @@ -42,201 +42,211 @@ All the source files are listed below. `tfetextview.h` - 1 #include - 2 - 3 #define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type () - 4 G_DECLARE_FINAL_TYPE (TfeTextView, tfe_text_view, TFE, TEXT_VIEW, GtkTextView) - 5 - 6 void - 7 tfe_text_view_set_file (TfeTextView *tv, GFile *f); - 8 - 9 GFile * - 10 tfe_text_view_get_file (TfeTextView *tv); - 11 - 12 GtkWidget * - 13 tfe_text_view_new (void); - 14 +~~~C + 1 #include + 2 + 3 #define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type () + 4 G_DECLARE_FINAL_TYPE (TfeTextView, tfe_text_view, TFE, TEXT_VIEW, GtkTextView) + 5 + 6 void + 7 tfe_text_view_set_file (TfeTextView *tv, GFile *f); + 8 + 9 GFile * +10 tfe_text_view_get_file (TfeTextView *tv); +11 +12 GtkWidget * +13 tfe_text_view_new (void); +14 +~~~ `tfetextview.c` - 1 #include - 2 #include "tfetextview.h" - 3 - 4 struct _TfeTextView - 5 { - 6 GtkTextView parent; - 7 GFile *file; - 8 }; - 9 - 10 G_DEFINE_TYPE (TfeTextView, tfe_text_view, GTK_TYPE_TEXT_VIEW); - 11 - 12 static void - 13 tfe_text_view_init (TfeTextView *tv) { - 14 } - 15 - 16 static void - 17 tfe_text_view_class_init (TfeTextViewClass *class) { - 18 } - 19 - 20 void - 21 tfe_text_view_set_file (TfeTextView *tv, GFile *f) { - 22 tv -> file = f; - 23 } - 24 - 25 GFile * - 26 tfe_text_view_get_file (TfeTextView *tv) { - 27 return tv -> file; - 28 } - 29 - 30 GtkWidget * - 31 tfe_text_view_new (void) { - 32 return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL)); - 33 } - 34 +~~~C + 1 #include + 2 #include "tfetextview.h" + 3 + 4 struct _TfeTextView + 5 { + 6 GtkTextView parent; + 7 GFile *file; + 8 }; + 9 +10 G_DEFINE_TYPE (TfeTextView, tfe_text_view, GTK_TYPE_TEXT_VIEW); +11 +12 static void +13 tfe_text_view_init (TfeTextView *tv) { +14 } +15 +16 static void +17 tfe_text_view_class_init (TfeTextViewClass *class) { +18 } +19 +20 void +21 tfe_text_view_set_file (TfeTextView *tv, GFile *f) { +22 tv -> file = f; +23 } +24 +25 GFile * +26 tfe_text_view_get_file (TfeTextView *tv) { +27 return tv -> file; +28 } +29 +30 GtkWidget * +31 tfe_text_view_new (void) { +32 return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL)); +33 } +34 +~~~ `tfe.c` - 1 #include - 2 #include "tfetextview.h" - 3 - 4 static void - 5 on_activate (GApplication *app, gpointer user_data) { - 6 g_print ("You need a filename argument.\n"); - 7 } - 8 - 9 static void - 10 on_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer user_data) { - 11 GtkWidget *win; - 12 GtkWidget *nb; - 13 GtkWidget *lab; - 14 GtkNotebookPage *nbp; - 15 GtkWidget *scr; - 16 GtkWidget *tv; - 17 GtkTextBuffer *tb; - 18 char *contents; - 19 gsize length; - 20 char *filename; - 21 int i; - 22 GtkBuilder *build; - 23 - 24 build = gtk_builder_new_from_resource ("/com/github/ToshioCP/tfe3/tfe.ui"); - 25 win = GTK_WIDGET (gtk_builder_get_object (build, "win")); - 26 gtk_window_set_application (GTK_WINDOW (win), GTK_APPLICATION (app)); - 27 nb = GTK_WIDGET (gtk_builder_get_object (build, "nb")); - 28 g_object_unref (build); - 29 for (i = 0; i < n_files; i++) { - 30 if (g_file_load_contents (files[i], NULL, &contents, &length, NULL, NULL)) { - 31 scr = gtk_scrolled_window_new (); - 32 tv = tfe_text_view_new (); - 33 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); - 34 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR); - 35 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv); - 36 - 37 tfe_text_view_set_file (TFE_TEXT_VIEW (tv), g_file_dup (files[i])); - 38 gtk_text_buffer_set_text (tb, contents, length); - 39 g_free (contents); - 40 filename = g_file_get_basename (files[i]); - 41 lab = gtk_label_new (filename); - 42 gtk_notebook_append_page (GTK_NOTEBOOK (nb), scr, lab); - 43 nbp = gtk_notebook_get_page (GTK_NOTEBOOK (nb), scr); - 44 g_object_set (nbp, "tab-expand", TRUE, NULL); - 45 g_free (filename); - 46 } else { - 47 filename = g_file_get_path (files[i]); - 48 g_print ("No such file: %s.\n", filename); - 49 g_free (filename); - 50 } - 51 } - 52 if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb)) > 0) { - 53 gtk_widget_show (win); - 54 } else - 55 gtk_window_destroy (GTK_WINDOW (win)); - 56 } - 57 - 58 int - 59 main (int argc, char **argv) { - 60 GtkApplication *app; - 61 int stat; - 62 - 63 app = gtk_application_new ("com.github.ToshioCP.tfe3", G_APPLICATION_HANDLES_OPEN); - 64 g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL); - 65 g_signal_connect (app, "open", G_CALLBACK (on_open), NULL); - 66 stat =g_application_run (G_APPLICATION (app), argc, argv); - 67 g_object_unref (app); - 68 return stat; - 69 } - 70 +~~~C + 1 #include + 2 #include "tfetextview.h" + 3 + 4 static void + 5 on_activate (GApplication *app, gpointer user_data) { + 6 g_print ("You need a filename argument.\n"); + 7 } + 8 + 9 static void +10 on_open (GApplication *app, GFile ** files, gint n_files, gchar *hint, gpointer user_data) { +11 GtkWidget *win; +12 GtkWidget *nb; +13 GtkWidget *lab; +14 GtkNotebookPage *nbp; +15 GtkWidget *scr; +16 GtkWidget *tv; +17 GtkTextBuffer *tb; +18 char *contents; +19 gsize length; +20 char *filename; +21 int i; +22 GtkBuilder *build; +23 +24 build = gtk_builder_new_from_resource ("/com/github/ToshioCP/tfe3/tfe.ui"); +25 win = GTK_WIDGET (gtk_builder_get_object (build, "win")); +26 gtk_window_set_application (GTK_WINDOW (win), GTK_APPLICATION (app)); +27 nb = GTK_WIDGET (gtk_builder_get_object (build, "nb")); +28 g_object_unref (build); +29 for (i = 0; i < n_files; i++) { +30 if (g_file_load_contents (files[i], NULL, &contents, &length, NULL, NULL)) { +31 scr = gtk_scrolled_window_new (); +32 tv = tfe_text_view_new (); +33 tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); +34 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD_CHAR); +35 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv); +36 +37 tfe_text_view_set_file (TFE_TEXT_VIEW (tv), g_file_dup (files[i])); +38 gtk_text_buffer_set_text (tb, contents, length); +39 g_free (contents); +40 filename = g_file_get_basename (files[i]); +41 lab = gtk_label_new (filename); +42 gtk_notebook_append_page (GTK_NOTEBOOK (nb), scr, lab); +43 nbp = gtk_notebook_get_page (GTK_NOTEBOOK (nb), scr); +44 g_object_set (nbp, "tab-expand", TRUE, NULL); +45 g_free (filename); +46 } else { +47 filename = g_file_get_path (files[i]); +48 g_print ("No such file: %s.\n", filename); +49 g_free (filename); +50 } +51 } +52 if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb)) > 0) { +53 gtk_widget_show (win); +54 } else +55 gtk_window_destroy (GTK_WINDOW (win)); +56 } +57 +58 int +59 main (int argc, char **argv) { +60 GtkApplication *app; +61 int stat; +62 +63 app = gtk_application_new ("com.github.ToshioCP.tfe3", G_APPLICATION_HANDLES_OPEN); +64 g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL); +65 g_signal_connect (app, "open", G_CALLBACK (on_open), NULL); +66 stat =g_application_run (G_APPLICATION (app), argc, argv); +67 g_object_unref (app); +68 return stat; +69 } +70 +~~~ `tfe.ui` - 1 - 2 - 3 file editor - 4 600 - 5 400 - 6 - 7 - 8 GTK_ORIENTATION_VERTICAL - 9 - 10 - 11 GTK_ORIENTATION_HORIZONTAL - 12 - 13 - 14 10 - 15 - 16 - 17 - 18 - 19 New - 20 - 21 - 22 - 23 - 24 Open - 25 - 26 - 27 - 28 - 29 TRUE - 30 - 31 - 32 - 33 - 34 Save - 35 - 36 - 37 - 38 - 39 Close - 40 - 41 - 42 - 43 - 44 10 - 45 - 46 - 47 - 48 - 49 - 50 - 51 TRUE - 52 TRUE - 53 - 54 - 55 - 56 - 57 - 58 - 59 +~~~xml + 1 + 2 + 3 file editor + 4 600 + 5 400 + 6 + 7 + 8 GTK_ORIENTATION_VERTICAL + 9 +10 +11 GTK_ORIENTATION_HORIZONTAL +12 +13 +14 10 +15 +16 +17 +18 +19 New +20 +21 +22 +23 +24 Open +25 +26 +27 +28 +29 TRUE +30 +31 +32 +33 +34 Save +35 +36 +37 +38 +39 Close +40 +41 +42 +43 +44 10 +45 +46 +47 +48 +49 +50 +51 TRUE +52 TRUE +53 +54 +55 +56 +57 +58 +59 +~~~ `tfe.gresource.xml` - 1 - 2 - 3 - 4 tfe.ui - 5 - 6 +~~~xml +1 +2 +3 +4 tfe.ui +5 +6 +~~~ ## Make @@ -278,25 +288,27 @@ If the modification time of `sample.c` is older then the generation of `sample.o The Makefile for `tfe` is as follows. - 1 all: tfe - 2 - 3 tfe: tfe.o tfetextview.o resources.o - 4 gcc -o tfe tfe.o tfetextview.o resources.o `pkg-config --libs gtk4` - 5 - 6 tfe.o: tfe.c tfetextview.h - 7 gcc -c -o tfe.o `pkg-config --cflags gtk4` tfe.c - 8 tfetextview.o: tfetextview.c tfetextview.h - 9 gcc -c -o tfetextview.o `pkg-config --cflags gtk4` tfetextview.c - 10 resources.o: resources.c - 11 gcc -c -o resources.o `pkg-config --cflags gtk4` resources.c - 12 - 13 resources.c: tfe.gresource.xml tfe.ui - 14 glib-compile-resources tfe.gresource.xml --target=resources.c --generate-source - 15 - 16 .Phony: clean - 17 - 18 clean: - 19 rm -f tfe tfe.o tfetextview.o resources.o resources.c +~~~makefile + 1 all: tfe + 2 + 3 tfe: tfe.o tfetextview.o resources.o + 4 gcc -o tfe tfe.o tfetextview.o resources.o `pkg-config --libs gtk4` + 5 + 6 tfe.o: tfe.c tfetextview.h + 7 gcc -c -o tfe.o `pkg-config --cflags gtk4` tfe.c + 8 tfetextview.o: tfetextview.c tfetextview.h + 9 gcc -c -o tfetextview.o `pkg-config --cflags gtk4` tfetextview.c +10 resources.o: resources.c +11 gcc -c -o resources.o `pkg-config --cflags gtk4` resources.c +12 +13 resources.c: tfe.gresource.xml tfe.ui +14 glib-compile-resources tfe.gresource.xml --target=resources.c --generate-source +15 +16 .Phony: clean +17 +18 clean: +19 rm -f tfe tfe.o tfetextview.o resources.o resources.c +~~~ You only need to type `make`. @@ -324,31 +336,33 @@ 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. - 1 require 'rake/clean' - 2 - 3 targetfile = "tfe" - 4 srcfiles = FileList["tfe.c", "tfetextview.c", "resources.c"] - 5 rscfile = srcfiles[2] - 6 objfiles = srcfiles.gsub(/.c$/, '.o') - 7 - 8 CLEAN.include(targetfile, objfiles, rscfile) - 9 - 10 task default: targetfile - 11 - 12 file targetfile => objfiles do |t| - 13 sh "gcc -o #{t.name} #{t.prerequisites.join(' ')} `pkg-config --libs gtk4`" - 14 end - 15 - 16 objfiles.each do |obj| - 17 src = obj.gsub(/.o$/,'.c') - 18 file obj => src do |t| - 19 sh "gcc -c -o #{t.name} `pkg-config --cflags gtk4` #{t.source}" - 20 end - 21 end - 22 - 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 +~~~ruby + 1 require 'rake/clean' + 2 + 3 targetfile = "tfe" + 4 srcfiles = FileList["tfe.c", "tfetextview.c", "resources.c"] + 5 rscfile = srcfiles[2] + 6 objfiles = srcfiles.gsub(/.c$/, '.o') + 7 + 8 CLEAN.include(targetfile, objfiles, rscfile) + 9 +10 task default: targetfile +11 +12 file targetfile => objfiles do |t| +13 sh "gcc -o #{t.name} #{t.prerequisites.join(' ')} `pkg-config --libs gtk4`" +14 end +15 +16 objfiles.each do |obj| +17 src = obj.gsub(/.o$/,'.c') +18 file obj => src do |t| +19 sh "gcc -c -o #{t.name} `pkg-config --cflags gtk4` #{t.source}" +20 end +21 end +22 +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,16 +394,18 @@ Many developers are using meson and ninja now. To use meson, you first need to write `meson.build` file. - 1 project('tfe', 'c') - 2 - 3 gtkdep = dependency('gtk4') - 4 - 5 gnome=import('gnome') - 6 resources = gnome.compile_resources('resources','tfe.gresource.xml') - 7 - 8 sourcefiles=files('tfe.c', 'tfetextview.c') - 9 - 10 executable('tfe', sourcefiles, resources, dependencies: gtkdep) +~~~meson + 1 project('tfe', 'c') + 2 + 3 gtkdep = dependency('gtk4') + 4 + 5 gnome=import('gnome') + 6 resources = gnome.compile_resources('resources','tfe.gresource.xml') + 7 + 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. diff --git a/lib/lib_src2md.rb b/lib/lib_src2md.rb index 63579a0..f9deecb 100644 --- a/lib/lib_src2md.rb +++ b/lib/lib_src2md.rb @@ -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 + l = sprintf("%#{ln_width}d %s", n, 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) + 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 - 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. + md_buf << line end - md_buf << line 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 diff --git a/src/sec17.src.md b/src/sec17.src.md index d802296..c64cdfb 100644 --- a/src/sec17.src.md +++ b/src/sec17.src.md @@ -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); diff --git a/src/sec18.src.md b/src/sec18.src.md index fc4088b..0c82550 100644 --- a/src/sec18.src.md +++ b/src/sec18.src.md @@ -11,22 +11,26 @@ The same goes for menus. The ui file for menus has interface, menu tags. The file starts and ends with interface tag. - - - - +~~~xml + + + + +~~~ `menu` tag corresponds to GMenu object. `id` attribute defines the name of the object. It will be refered by GtkBuilder. - - File - - New - win.new - - +~~~xml + + File + + New + win.new + + +~~~ `item` tag corresponds to item in GMenu which has the same structure as GMenuItem. The item above has a label attribute. @@ -38,15 +42,17 @@ The GMenuItem has a link to GMenu. The ui file above can be described as follows. - - File - - - New - win.new - - - +~~~xml + + File + + + New + win.new + + + +~~~ `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. - GtkBuilder *builder = gtk_builder_new_from_resource ("/com/github/ToshioCP/menu3/menu3.ui"); - GMenuModel *menubar = G_MENU_MODEL (gtk_builder_get_object (builder, "menubar")); +~~~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); +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. - 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 */ - /*< private >*/ - gsize padding[3]; - }; +~~~C +typedef struct _GActionEntry GActionEntry; +struct _GActionEntry +{ + /* 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: - { "fullscreen", NULL, NULL, "false", fullscreen_changed } - { "color", color_activated, "s", "red", NULL } - { "quit", quit_activated, NULL, NULL, NULL }, +~~~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. - 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); +~~~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); +~~~ The code above does: @@ -120,12 +139,14 @@ The code above does: The same goes for the other actions. - 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); - +~~~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); +~~~ The code above does: - Build a "fullscreen" action and "color" action. diff --git a/src/sec19.src.md b/src/sec19.src.md index 8e5e012..b103ff6 100644 --- a/src/sec19.src.md +++ b/src/sec19.src.md @@ -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: diff --git a/src/sec2.src.md b/src/sec2.src.md index bbe295e..c1f3d9a 100644 --- a/src/sec2.src.md +++ b/src/sec2.src.md @@ -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/" diff --git a/src/sec20.src.md b/src/sec20.src.md index 738917b..841fd3e 100644 --- a/src/sec20.src.md +++ b/src/sec20.src.md @@ -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. diff --git a/src/sec3.src.md b/src/sec3.src.md index acdd9cf..3ee7dc5 100644 --- a/src/sec3.src.md +++ b/src/sec3.src.md @@ -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. diff --git a/src/sec7.src.md b/src/sec7.src.md index 7e72b43..192e35d 100644 --- a/src/sec7.src.md +++ b/src/sec7.src.md @@ -55,39 +55,41 @@ 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. - #define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type () - G_DECLARE_FINAL_TYPE (TfeTextView, tfe_text_view, TFE, TEXT_VIEW, GtkTextView) +~~~C +#define TFE_TYPE_TEXT_VIEW tfe_text_view_get_type () +G_DECLARE_FINAL_TYPE (TfeTextView, tfe_text_view, TFE, TEXT_VIEW, GtkTextView) - struct _TfeTextView - { - GtkTextView parent; - GFile *file; - }; +struct _TfeTextView +{ + GtkTextView parent; + GFile *file; +}; - G_DEFINE_TYPE (TfeTextView, tfe_text_view, GTK_TYPE_TEXT_VIEW); +G_DEFINE_TYPE (TfeTextView, tfe_text_view, GTK_TYPE_TEXT_VIEW); - static void - tfe_text_view_init (TfeTextView *tv) { - } +static void +tfe_text_view_init (TfeTextView *tv) { +} - static void - tfe_text_view_class_init (TfeTextViewClass *class) { - } +static void +tfe_text_view_class_init (TfeTextViewClass *class) { +} - void - tfe_text_view_set_file (TfeTextView *tv, GFile *f) { - tv -> file = f; - } +void +tfe_text_view_set_file (TfeTextView *tv, GFile *f) { + tv -> file = f; +} - GFile * - tfe_text_view_get_file (TfeTextView *tv) { - return tv -> file; - } +GFile * +tfe_text_view_get_file (TfeTextView *tv) { + return tv -> file; +} - GtkWidget * - tfe_text_view_new (void) { - return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, NULL)); - } +GtkWidget * +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. diff --git a/src/sec8.src.md b/src/sec8.src.md index e9a5b8d..2790fe5 100644 --- a/src/sec8.src.md +++ b/src/sec8.src.md @@ -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. - GtkBuilder *build; +~~~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")); +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,22 +108,24 @@ 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. - char *uistring; +~~~C +char *uistring; - uistring = - "" - "" - "file editor" - "600" - "400" - "" - "" - "GTK_ORIENTATION_VERTICAL" - ... ... ... - ... ... ... - ""; +uistring = +"" + "" + "file editor" + "600" + "400" + "" + "" + "GTK_ORIENTATION_VERTICAL" +... ... ... +... ... ... +""; - build = gtk_builder_new_from_stringfile (uistring); +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 - # include "resources.c" - ... ... ... - ... ... ... - build = gtk_builder_new_from_resource ("/com/github/ToshioCP/tfe3/tfe3.ui"); - ... ... ... - ... ... ... +~~~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.