[gtkmm-documentation] Add last part of "Building applications" chapter with example code



commit 511f27dae2d33b6a73ae7d7b5db8a9b2a4d2b11c
Author: Kjell Ahlstedt <kjell ahlstedt bredband net>
Date:   Wed Oct 19 10:45:51 2016 +0200

    Add last part of "Building applications" chapter with example code
    
    * docs/tutorial/C/index-in.docbook: Add sections 7-9 of the "Building
    applications" chapter. It's a C++/gtkmm version of a chapter in the gtk+
    documentation.
    * docs/tutorial/C/figures/buildapp_*.png: 3 new files. Update the old files.
    * docs/tutorial/Makefile.am: Add new image files.
    * examples/book/buildapp/.gitignore: Ignore gschemas.compiled.
    * examples/book/buildapp/Makefile.am: Add files in step[7-9].
    * examples/book/buildapp/step[7-9]: New directories with many new files.
    Bug #772391

 docs/tutorial/C/figures/buildapp_app_menu.png      |  Bin 72892 -> 55254 bytes
 docs/tutorial/C/figures/buildapp_header_bar.png    |  Bin 0 -> 68463 bytes
 docs/tutorial/C/figures/buildapp_opening_files.png |  Bin 63004 -> 63590 bytes
 .../C/figures/buildapp_populating_window.png       |  Bin 8935 -> 9582 bytes
 docs/tutorial/C/figures/buildapp_pref_dialog.png   |  Bin 9421 -> 9481 bytes
 docs/tutorial/C/figures/buildapp_properties.png    |  Bin 0 -> 68552 bytes
 docs/tutorial/C/figures/buildapp_search_bar.png    |  Bin 61228 -> 62742 bytes
 docs/tutorial/C/figures/buildapp_side_bar.png      |  Bin 0 -> 93714 bytes
 docs/tutorial/C/figures/buildapp_trivial_app.png   |  Bin 5382 -> 5928 bytes
 docs/tutorial/C/index-in.docbook                   |  124 ++++++++-
 docs/tutorial/Makefile.am                          |    3 +
 examples/book/buildapp/.gitignore                  |    1 +
 examples/book/buildapp/Makefile.am                 |   89 ++++++-
 examples/book/buildapp/step7/Makefile.example      |    2 +
 examples/book/buildapp/step7/app_menu.ui           |   18 ++
 .../book/buildapp/step7/exampleapp.gresource.xml   |    9 +
 examples/book/buildapp/step7/exampleapplication.cc |    2 +
 examples/book/buildapp/step7/exampleapplication.h  |    2 +
 examples/book/buildapp/step7/exampleappprefs.cc    |    2 +
 examples/book/buildapp/step7/exampleappprefs.h     |    2 +
 examples/book/buildapp/step7/exampleappwindow.cc   |  252 ++++++++++++++++
 examples/book/buildapp/step7/exampleappwindow.h    |   52 ++++
 examples/book/buildapp/step7/gears_menu.ui         |   12 +
 examples/book/buildapp/step7/main.cc               |    2 +
 .../step7/org.gtkmm.exampleapp.gschema.xml         |   25 ++
 examples/book/buildapp/step7/prefs.ui              |   71 +++++
 examples/book/buildapp/step7/window.ui             |   98 +++++++
 examples/book/buildapp/step8/Makefile.example      |    2 +
 examples/book/buildapp/step8/app_menu.ui           |   18 ++
 .../book/buildapp/step8/exampleapp.gresource.xml   |    9 +
 examples/book/buildapp/step8/exampleapplication.cc |    2 +
 examples/book/buildapp/step8/exampleapplication.h  |    2 +
 examples/book/buildapp/step8/exampleappprefs.cc    |    2 +
 examples/book/buildapp/step8/exampleappprefs.h     |    2 +
 examples/book/buildapp/step8/exampleappwindow.cc   |  299 +++++++++++++++++++
 examples/book/buildapp/step8/exampleappwindow.h    |   56 ++++
 examples/book/buildapp/step8/gears_menu.ui         |   16 +
 examples/book/buildapp/step8/main.cc               |    2 +
 .../step8/org.gtkmm.exampleapp.gschema.xml         |   25 ++
 examples/book/buildapp/step8/prefs.ui              |   71 +++++
 examples/book/buildapp/step8/window.ui             |  115 ++++++++
 examples/book/buildapp/step9/Makefile.example      |    2 +
 examples/book/buildapp/step9/app_menu.ui           |   18 ++
 .../book/buildapp/step9/exampleapp.gresource.xml   |   10 +
 examples/book/buildapp/step9/exampleapp.png        |  Bin 0 -> 4117 bytes
 examples/book/buildapp/step9/exampleapplication.cc |    2 +
 examples/book/buildapp/step9/exampleapplication.h  |    2 +
 examples/book/buildapp/step9/exampleappprefs.cc    |    2 +
 examples/book/buildapp/step9/exampleappprefs.h     |    2 +
 examples/book/buildapp/step9/exampleappwindow.cc   |  308 ++++++++++++++++++++
 examples/book/buildapp/step9/exampleappwindow.h    |    2 +
 examples/book/buildapp/step9/gears_menu.ui         |   16 +
 examples/book/buildapp/step9/main.cc               |    2 +
 .../step9/org.gtkmm.exampleapp.gschema.xml         |   25 ++
 examples/book/buildapp/step9/prefs.ui              |   71 +++++
 examples/book/buildapp/step9/window.ui             |  117 ++++++++
 56 files changed, 1956 insertions(+), 10 deletions(-)
---
diff --git a/docs/tutorial/C/figures/buildapp_app_menu.png b/docs/tutorial/C/figures/buildapp_app_menu.png
index e9787cf..adc7f3a 100644
Binary files a/docs/tutorial/C/figures/buildapp_app_menu.png and 
b/docs/tutorial/C/figures/buildapp_app_menu.png differ
diff --git a/docs/tutorial/C/figures/buildapp_header_bar.png b/docs/tutorial/C/figures/buildapp_header_bar.png
new file mode 100644
index 0000000..442e4a6
Binary files /dev/null and b/docs/tutorial/C/figures/buildapp_header_bar.png differ
diff --git a/docs/tutorial/C/figures/buildapp_opening_files.png 
b/docs/tutorial/C/figures/buildapp_opening_files.png
index c6bec38..a420094 100644
Binary files a/docs/tutorial/C/figures/buildapp_opening_files.png and 
b/docs/tutorial/C/figures/buildapp_opening_files.png differ
diff --git a/docs/tutorial/C/figures/buildapp_populating_window.png 
b/docs/tutorial/C/figures/buildapp_populating_window.png
index 2ae55bc..c2ee1c4 100644
Binary files a/docs/tutorial/C/figures/buildapp_populating_window.png and 
b/docs/tutorial/C/figures/buildapp_populating_window.png differ
diff --git a/docs/tutorial/C/figures/buildapp_pref_dialog.png 
b/docs/tutorial/C/figures/buildapp_pref_dialog.png
index e7065e3..d4363f3 100644
Binary files a/docs/tutorial/C/figures/buildapp_pref_dialog.png and 
b/docs/tutorial/C/figures/buildapp_pref_dialog.png differ
diff --git a/docs/tutorial/C/figures/buildapp_properties.png b/docs/tutorial/C/figures/buildapp_properties.png
new file mode 100644
index 0000000..6737fd6
Binary files /dev/null and b/docs/tutorial/C/figures/buildapp_properties.png differ
diff --git a/docs/tutorial/C/figures/buildapp_search_bar.png b/docs/tutorial/C/figures/buildapp_search_bar.png
index 444de9e..5d4e471 100644
Binary files a/docs/tutorial/C/figures/buildapp_search_bar.png and 
b/docs/tutorial/C/figures/buildapp_search_bar.png differ
diff --git a/docs/tutorial/C/figures/buildapp_side_bar.png b/docs/tutorial/C/figures/buildapp_side_bar.png
new file mode 100644
index 0000000..a46c75b
Binary files /dev/null and b/docs/tutorial/C/figures/buildapp_side_bar.png differ
diff --git a/docs/tutorial/C/figures/buildapp_trivial_app.png 
b/docs/tutorial/C/figures/buildapp_trivial_app.png
index 33a0bdf..3d9667e 100644
Binary files a/docs/tutorial/C/figures/buildapp_trivial_app.png and 
b/docs/tutorial/C/figures/buildapp_trivial_app.png differ
diff --git a/docs/tutorial/C/index-in.docbook b/docs/tutorial/C/index-in.docbook
index da23f69..f132780 100644
--- a/docs/tutorial/C/index-in.docbook
+++ b/docs/tutorial/C/index-in.docbook
@@ -8170,30 +8170,144 @@ With the search bar, our application now looks like this:
 <title>Adding a side bar</title>
 
 <para>
-To be continued.
+As another piece of functionality, we are adding a sidebar, which demonstrates
+<classname>Gtk::MenuButton</classname>, <classname>Gtk::Revealer</classname> and
+<classname>Gtk::ListBox</classname>. The new widgets are added in the <filename>window.ui</filename> file.
 </para>
 
+<para>
+The code to populate the sidebar with buttons for the words found in each file is a
+little too involved to go into here. But we'll look at the code to add the gears menu.
+As expected by now, the gears menu is specified in a <classname>Gtk::Builder</classname>
+ui file, <filename>gears_menu.ui</filename>
+</para>
+
+<para>
+To connect the menu item to the new <literal>show-words</literal> setting, we use a
+<classname>Gio::Action</classname> corresponding to the given <classname>Gio::Settings</classname>
+key. In <classname>ExampleAppWindow</classname>'s constructor:
+<programlisting>
+<![CDATA[// Connect the menu to the MenuButton m_gears, and bind the show-words setting
+// to the win.show-words action and the "Words" menu item.
+// (The connection between action and menu item is specified in gears_menu.ui.)
+auto menu_builder = Gtk::Builder::create_from_resource("/org/gtkmm/exampleapp/gears_menu.ui");
+auto object = menu_builder->get_object("menu");
+auto menu = Glib::RefPtr<Gio::MenuModel>::cast_dynamic(object);
+m_gears->set_menu_model(menu);
+add_action(m_settings->create_action("show-words"));]]>
+</programlisting>
+</para>
+
+<para>
+What our application looks like now:
+</para>
+
+<figure id="figure-buildapp-side-bar">
+  <title>Adding a side bar</title>
+  <screenshot>
+    <graphic format="PNG" fileref="&url_figures_base;buildapp_side_bar.png"/>
+  </screenshot>
+</figure>
+
+<para><ulink url="&url_examples_base;buildapp/step7">Source Code</ulink></para> <!-- Insert 
exampleapp.gresource.xml gears_menu.ui window.ui org.gtkmm.exampleapp.gschema.xml -->
+
+
 </sect1>
 
-<!--
 <sect1 id="sec-buildapp-properties">
 <title>Properties</title>
 
 <para>
-To be continued.
+Widgets and other objects have many useful properties. Here we show some ways to use
+them in new and flexible ways, by wrapping them in actions with <classname>Gio::PropertyAction</classname>
+or by binding them with <classname>Glib::Binding</classname>.
+(At the time of writing (october 2016) <classname>Gio::PropertyAction</classname>
+does not exist. We have to call a <application>glib</application> function directly.)
 </para>
 
+<para>
+To set this up, we add two labels to the header bar in our <filename>window.ui</filename> file,
+named <literal>lines_label</literal> and <literal>lines</literal>, and get pointers to them
+in the application window's constructor, as we've seen a couple of times by now.
+We add a new "Lines" menu item to the gears menu, which triggers the
+<literal>show-lines</literal> action.
+</para>
+
+<para>
+To make this menu item do something, we create a property action for the <literal>visible</literal>
+property of the <literal>lines</literal> label, and add it to the actions of the window.
+The effect of this is that the visibility of the label gets toggled every time the action
+is activated.
+Since we want both labels to appear and disappear together, we bind the <literal>visible</literal>
+property of the <literal>lines_label</literal> widget to the same property of the
+<literal>lines</literal> widget.
+In <classname>ExampleAppWindow</classname>'s constructor:
+<programlisting>add_action(Glib::wrap((GAction*)
+  g_property_action_new("show-lines", m_lines->gobj(), "visible")));
+m_binding_lines_visible = Glib::Binding::bind_property(m_lines->property_visible(),
+  m_lines_label->property_visible());<![CDATA[
+]]>
+</programlisting>
+</para>
+
+<para>
+We also need a function that counts the lines of the currently active tab, and updates
+the <literal>lines</literal> label. See the full source if you are interested in the details.
+</para>
+
+<para>
+This brings our example application to this appearance:
+</para>
+
+<figure id="figure-buildapp-properties">
+  <title>Properties</title>
+  <screenshot>
+    <graphic format="PNG" fileref="&url_figures_base;buildapp_properties.png"/>
+  </screenshot>
+</figure>
+
+<para><ulink url="&url_examples_base;buildapp/step8">Source Code</ulink></para> <!-- Insert gears_menu.ui 
window.ui -->
+
 </sect1>
 
 <sect1 id="sec-buildapp-header-bar">
 <title>Header bar</title>
 
 <para>
-To be continued.
+Our application already uses a <classname>Gtk::HeaderBar</classname>, but so far it still
+gets a 'normal' window titlebar on top of that. This is a bit redundant, and we will now
+tell &gtkmm; to use the header bar as replacement for the titlebar. To do so, we move
+it around to be a direct child of the window, and set its type to be <literal>titlebar</literal>.
+This is done in the <filename>window.ui</filename> file.
+</para>
+
+<para>
+A small extra bonus of using a header bar is that we get a fallback application menu
+button for free. We include an icon in the resource file, and set up this icon as the
+window icon. Then it is displayed in the menu button.
 </para>
 
+<para>
+Here is how the application now looks, if the fallback menu is used:
+</para>
+
+<figure id="figure-buildapp-header-bar">
+  <title>Header bar</title>
+  <screenshot>
+    <graphic format="PNG" fileref="&url_figures_base;buildapp_header_bar.png"/>
+  </screenshot>
+</figure>
+
+<para>
+The <filename>window.ui</filename> file sets a header bar title, but this title is not shown.
+That's because the stack switcher is a child of type <literal>title</literal>. The stack
+switcher becomes a custom title that hides the title label, as if it had been set with
+<methodname>Gtk::HeaderBar::set_custom_title()</methodname>.
+</para>
+
+<para><ulink url="&url_examples_base;buildapp/step9">Source Code</ulink></para> <!-- Insert 
exampleapp.gresource.xml window.ui -->
+
 </sect1>
--->
 
 </chapter>
 
diff --git a/docs/tutorial/Makefile.am b/docs/tutorial/Makefile.am
index 0aec9b7..a0d86b2 100644
--- a/docs/tutorial/Makefile.am
+++ b/docs/tutorial/Makefile.am
@@ -31,10 +31,13 @@ HELP_MEDIA =                                        \
        figures/box_packing2.png                \
        figures/box_packing3.png                \
        figures/buildapp_app_menu.png \
+       figures/buildapp_header_bar.png \
        figures/buildapp_opening_files.png \
        figures/buildapp_populating_window.png \
        figures/buildapp_pref_dialog.png \
+       figures/buildapp_properties.png \
        figures/buildapp_search_bar.png \
+       figures/buildapp_side_bar.png \
        figures/buildapp_trivial_app.png \
        figures/buttonbox.png                   \
        figures/buttons.png                     \
diff --git a/examples/book/buildapp/.gitignore b/examples/book/buildapp/.gitignore
index 3119019..0d8381c 100644
--- a/examples/book/buildapp/.gitignore
+++ b/examples/book/buildapp/.gitignore
@@ -1,2 +1,3 @@
 exampleapp
 resources.c
+gschemas.compiled
diff --git a/examples/book/buildapp/Makefile.am b/examples/book/buildapp/Makefile.am
index 1627341..6f6cb08 100644
--- a/examples/book/buildapp/Makefile.am
+++ b/examples/book/buildapp/Makefile.am
@@ -12,7 +12,10 @@ check_PROGRAMS = \
   step3/exampleapp \
   step4/exampleapp \
   step5/exampleapp \
-  step6/exampleapp
+  step6/exampleapp \
+  step7/exampleapp \
+  step8/exampleapp \
+  step9/exampleapp
 
 check_DATA = $(GSCHEMAS_COMPILED)
 
@@ -31,6 +34,12 @@ dist_noinst_DATA = \
   step5/Makefile.example \
   $(STEP6_RESOURCES) \
   step6/Makefile.example \
+  $(STEP7_RESOURCES) \
+  step7/Makefile.example \
+  $(STEP8_RESOURCES) \
+  step8/Makefile.example \
+  $(STEP9_RESOURCES) \
+  step9/Makefile.example \
   $(gsettings_SCHEMAS)
 
 step1_exampleapp_SOURCES = \
@@ -84,19 +93,55 @@ step6_exampleapp_SOURCES = \
   step6/exampleappprefs.h \
   step6/main.cc
 
+nodist_step7_exampleapp_SOURCES = step7/resources.c
+step7_exampleapp_SOURCES = \
+  step7/exampleapplication.cc \
+  step7/exampleapplication.h \
+  step7/exampleappwindow.cc \
+  step7/exampleappwindow.h \
+  step7/exampleappprefs.cc \
+  step7/exampleappprefs.h \
+  step7/main.cc
+
+nodist_step8_exampleapp_SOURCES = step8/resources.c
+step8_exampleapp_SOURCES = \
+  step8/exampleapplication.cc \
+  step8/exampleapplication.h \
+  step8/exampleappwindow.cc \
+  step8/exampleappwindow.h \
+  step8/exampleappprefs.cc \
+  step8/exampleappprefs.h \
+  step8/main.cc
+
+nodist_step9_exampleapp_SOURCES = step9/resources.c
+step9_exampleapp_SOURCES = \
+  step9/exampleapplication.cc \
+  step9/exampleapplication.h \
+  step9/exampleappwindow.cc \
+  step9/exampleappwindow.h \
+  step9/exampleappprefs.cc \
+  step9/exampleappprefs.h \
+  step9/main.cc
+
 gsettings_SCHEMAS = \
   step5/org.gtkmm.exampleapp.gschema.xml \
-  step6/org.gtkmm.exampleapp.gschema.xml
+  step6/org.gtkmm.exampleapp.gschema.xml \
+  step7/org.gtkmm.exampleapp.gschema.xml \
+  step8/org.gtkmm.exampleapp.gschema.xml \
+  step9/org.gtkmm.exampleapp.gschema.xml
 
 GSCHEMAS_COMPILED = \
   step5/gschemas.compiled \
-  step6/gschemas.compiled
+  step6/gschemas.compiled \
+  step7/gschemas.compiled \
+  step8/gschemas.compiled \
+  step9/gschemas.compiled
 
-## $(<D) is the source directory, $(@D) is the build directory.
+## $(dir $<) is the source directory, $(dir $@) is the build directory.
 ## They are different during make distcheck.
 %/gschemas.compiled: %/org.gtkmm.exampleapp.gschema.xml
        $(AM_V_GEN) $(GLIB_COMPILE_SCHEMAS) --strict --dry-run --schema-file=$<
-       @$(GLIB_COMPILE_SCHEMAS) --targetdir=$(@D) $(<D)
+       @$(GLIB_COMPILE_SCHEMAS) --targetdir=$(dir $@) $(dir $<)
 
 ## In most programs you would use @GSETTINGS_RULES@ instead of some of the
 ## schema rules in this Makefile.am, but gtkmm-documentation is different.
@@ -109,6 +154,9 @@ DISTCLEANFILES = \
   step4/resources.c \
   step5/resources.c \
   step6/resources.c \
+  step7/resources.c \
+  step8/resources.c \
+  step9/resources.c \
   $(GSCHEMAS_COMPILED)
 
 STEP2_RESOURCES = \
@@ -151,3 +199,34 @@ STEP6_RESOURCES = \
 step6/resources.c: $(STEP6_RESOURCES)
        $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(dir $<) --generate-source $<
 
+STEP7_RESOURCES = \
+  step7/exampleapp.gresource.xml \
+  step7/window.ui \
+  step7/app_menu.ui \
+  step7/gears_menu.ui \
+  step7/prefs.ui
+
+step7/resources.c: $(STEP7_RESOURCES)
+       $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(dir $<) --generate-source $<
+
+STEP8_RESOURCES = \
+  step8/exampleapp.gresource.xml \
+  step8/window.ui \
+  step8/app_menu.ui \
+  step8/gears_menu.ui \
+  step8/prefs.ui
+
+step8/resources.c: $(STEP7_RESOURCES)
+       $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(dir $<) --generate-source $<
+
+STEP9_RESOURCES = \
+  step9/exampleapp.gresource.xml \
+  step9/window.ui \
+  step9/app_menu.ui \
+  step9/gears_menu.ui \
+  step9/prefs.ui \
+  step9/exampleapp.png
+
+step9/resources.c: $(STEP7_RESOURCES)
+       $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(dir $<) --generate-source $<
+
diff --git a/examples/book/buildapp/step7/Makefile.example b/examples/book/buildapp/step7/Makefile.example
new file mode 100644
index 0000000..997766b
--- /dev/null
+++ b/examples/book/buildapp/step7/Makefile.example
@@ -0,0 +1,2 @@
+include ../step5/Makefile.example
+# Equal to the corresponding file in step5
diff --git a/examples/book/buildapp/step7/app_menu.ui b/examples/book/buildapp/step7/app_menu.ui
new file mode 100644
index 0000000..e1e131c
--- /dev/null
+++ b/examples/book/buildapp/step7/app_menu.ui
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<interface>
+  <!-- interface-requires gtk+ 3.0 -->
+  <menu id="appmenu">
+    <section>
+      <item>
+        <attribute name="label" translatable="yes">_Preferences</attribute>
+        <attribute name="action">app.preferences</attribute>
+      </item>
+    </section>
+    <section>
+      <item>
+        <attribute name="label" translatable="yes">_Quit</attribute>
+        <attribute name="action">app.quit</attribute>
+      </item>
+    </section>
+  </menu>
+</interface>
diff --git a/examples/book/buildapp/step7/exampleapp.gresource.xml 
b/examples/book/buildapp/step7/exampleapp.gresource.xml
new file mode 100644
index 0000000..dc39879
--- /dev/null
+++ b/examples/book/buildapp/step7/exampleapp.gresource.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+  <gresource prefix="/org/gtkmm/exampleapp">
+    <file preprocess="xml-stripblanks">window.ui</file>
+    <file preprocess="xml-stripblanks">app_menu.ui</file>
+    <file preprocess="xml-stripblanks">gears_menu.ui</file>
+    <file preprocess="xml-stripblanks">prefs.ui</file>
+  </gresource>
+</gresources>
diff --git a/examples/book/buildapp/step7/exampleapplication.cc 
b/examples/book/buildapp/step7/exampleapplication.cc
new file mode 100644
index 0000000..264c577
--- /dev/null
+++ b/examples/book/buildapp/step7/exampleapplication.cc
@@ -0,0 +1,2 @@
+#include "../step5/exampleapplication.cc"
+// Equal to the corresponding file in step5
diff --git a/examples/book/buildapp/step7/exampleapplication.h 
b/examples/book/buildapp/step7/exampleapplication.h
new file mode 100644
index 0000000..fd7b4e8
--- /dev/null
+++ b/examples/book/buildapp/step7/exampleapplication.h
@@ -0,0 +1,2 @@
+#include "../step4/exampleapplication.h"
+// Equal to the corresponding file in step4
diff --git a/examples/book/buildapp/step7/exampleappprefs.cc b/examples/book/buildapp/step7/exampleappprefs.cc
new file mode 100644
index 0000000..1bbe550
--- /dev/null
+++ b/examples/book/buildapp/step7/exampleappprefs.cc
@@ -0,0 +1,2 @@
+#include "../step5/exampleappprefs.cc"
+// Equal to the corresponding file in step5
diff --git a/examples/book/buildapp/step7/exampleappprefs.h b/examples/book/buildapp/step7/exampleappprefs.h
new file mode 100644
index 0000000..9d9ee18
--- /dev/null
+++ b/examples/book/buildapp/step7/exampleappprefs.h
@@ -0,0 +1,2 @@
+#include "../step5/exampleappprefs.h"
+// Equal to the corresponding file in step5
diff --git a/examples/book/buildapp/step7/exampleappwindow.cc 
b/examples/book/buildapp/step7/exampleappwindow.cc
new file mode 100644
index 0000000..0d1eb97
--- /dev/null
+++ b/examples/book/buildapp/step7/exampleappwindow.cc
@@ -0,0 +1,252 @@
+/* gtkmm example Copyright (C) 2016 gtkmm development team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "exampleappwindow.h"
+#include <iostream>
+#include <stdexcept>
+#include <set>
+
+ExampleAppWindow::ExampleAppWindow(BaseObjectType* cobject,
+  const Glib::RefPtr<Gtk::Builder>& refBuilder)
+: Gtk::ApplicationWindow(cobject),
+  m_refBuilder(refBuilder),
+  m_settings(),
+  m_stack(nullptr),
+  m_search(nullptr),
+  m_searchbar(nullptr),
+  m_searchentry(nullptr),
+  m_gears(nullptr),
+  m_sidebar(nullptr),
+  m_words(nullptr),
+  m_prop_binding()
+{
+  // Get widgets from the Gtk::Builder file.
+  m_refBuilder->get_widget("stack", m_stack);
+  if (!m_stack)
+    throw std::runtime_error("No \"stack\" object in window.ui");
+
+  m_refBuilder->get_widget("search", m_search);
+  if (!m_search)
+    throw std::runtime_error("No \"search\" object in window.ui");
+
+  m_refBuilder->get_widget("searchbar", m_searchbar);
+  if (!m_searchbar)
+    throw std::runtime_error("No \"searchbar\" object in window.ui");
+
+  m_refBuilder->get_widget("searchentry", m_searchentry);
+  if (!m_searchentry)
+    throw std::runtime_error("No \"searchentry\" object in window.ui");
+
+  m_refBuilder->get_widget("gears", m_gears);
+  if (!m_gears)
+    throw std::runtime_error("No \"gears\" object in window.ui");
+
+  m_refBuilder->get_widget("sidebar", m_sidebar);
+  if (!m_sidebar)
+    throw std::runtime_error("No \"sidebar\" object in window.ui");
+
+  m_refBuilder->get_widget("words", m_words);
+  if (!m_words)
+    throw std::runtime_error("No \"words\" object in window.ui");
+
+  // Bind settings.
+  m_settings = Gio::Settings::create("org.gtkmm.exampleapp");
+  m_settings->bind("transition", m_stack, "transition-type");
+  m_settings->bind("show-words", m_sidebar, "reveal-child");
+
+  // Bind properties.
+  m_prop_binding = Glib::Binding::bind_property(m_search->property_active(),
+    m_searchbar->property_search_mode_enabled(), Glib::BINDING_BIDIRECTIONAL);
+
+  // Connect signal handlers.
+  m_searchentry->signal_search_changed().connect(
+    sigc::mem_fun(*this, &ExampleAppWindow::on_search_text_changed));
+  m_stack->property_visible_child().signal_changed().connect(
+    sigc::mem_fun(*this, &ExampleAppWindow::on_visible_child_changed));
+  m_sidebar->property_reveal_child().signal_changed().connect(
+    sigc::mem_fun(*this, &ExampleAppWindow::on_reveal_child_changed));
+
+  // Connect the menu to the MenuButton m_gears, and bind the show-words setting
+  // to the win.show-words action and the "Words" menu item.
+  // (The connection between action and menu item is specified in gears_menu.ui.)
+  auto menu_builder = Gtk::Builder::create_from_resource("/org/gtkmm/exampleapp/gears_menu.ui");
+  auto object = menu_builder->get_object("menu");
+  auto menu = Glib::RefPtr<Gio::MenuModel>::cast_dynamic(object);
+  if (!menu)
+    throw std::runtime_error("No \"menu\" object in gears_menu.ui");
+
+  m_gears->set_menu_model(menu);
+  add_action(m_settings->create_action("show-words"));
+}
+
+//static
+ExampleAppWindow* ExampleAppWindow::create()
+{
+  // Load the Builder file and instantiate its widgets.
+  auto refBuilder = Gtk::Builder::create_from_resource("/org/gtkmm/exampleapp/window.ui");
+
+  ExampleAppWindow* window = nullptr;
+  refBuilder->get_widget_derived("app_window", window);
+  if (!window)
+    throw std::runtime_error("No \"app_window\" object in window.ui");
+
+  return window;
+}
+
+void ExampleAppWindow::open_file_view(const Glib::RefPtr<Gio::File>& file)
+{
+  const auto basename = file->get_basename();
+
+  auto scrolled = Gtk::manage(new Gtk::ScrolledWindow());
+  scrolled->set_hexpand(true);
+  scrolled->set_vexpand(true);
+  scrolled->show();
+  auto view = Gtk::manage(new Gtk::TextView());
+  view->set_editable(false);
+  view->set_cursor_visible(false);
+  view->show();
+  scrolled->add(*view);
+  m_stack->add(*scrolled, basename, basename);
+
+  auto buffer = view->get_buffer();
+  try
+  {
+    char* contents = nullptr;
+    gsize length = 0;
+    
+    file->load_contents(contents, length);
+    buffer->set_text(contents, contents+length);
+    g_free(contents);
+  }
+  catch (const Glib::Error& ex)
+  {
+    std::cout << "ExampleAppWindow::open_file_view(\"" << file->get_parse_name()
+      << "\"):\n  " << ex.what() << std::endl;
+    return;
+  }
+
+  // tag is a Glib::RefPtr<Gtk::TextTag>. Gio::Settings::bind() requires a plain
+  // pointer to the tag. It does not accept a RefPtr. There is no Glib::RefPtr::get()
+  // or equivalent. We must use the unintuitive tag.operator->().
+  auto tag = buffer->create_tag();
+  m_settings->bind("font", tag.operator->(), "font");
+  buffer->apply_tag(tag, buffer->begin(), buffer->end());
+
+  m_search->set_sensitive(true);
+  update_words();
+}
+
+void ExampleAppWindow::on_search_text_changed()
+{
+  const auto text = m_searchentry->get_text();
+  if (text.empty())
+    return;
+
+  auto tab = dynamic_cast<Gtk::ScrolledWindow*>(m_stack->get_visible_child());
+  if (!tab)
+  {
+    std::cout << "ExampleAppWindow::on_search_text_changed(): No visible child." << std::endl;
+    return;
+  }
+
+  auto view = dynamic_cast<Gtk::TextView*>(tab->get_child());
+  if (!view)
+  {
+    std::cout << "ExampleAppWindow::on_search_text_changed(): No visible text view." << std::endl;
+    return;
+  }
+
+  // Very simple-minded search implementation.
+  auto buffer = view->get_buffer();
+  Gtk::TextIter match_start;
+  Gtk::TextIter match_end;
+  if (buffer->begin().forward_search(text, Gtk::TEXT_SEARCH_CASE_INSENSITIVE,
+      match_start, match_end))
+  {
+    buffer->select_range(match_start, match_end);
+    view->scroll_to(match_start);
+  }
+}
+
+void ExampleAppWindow::on_visible_child_changed()
+{
+  m_searchbar->set_search_mode(false);
+  update_words();
+}
+
+void ExampleAppWindow::on_find_word(const Gtk::Button* button)
+{
+  m_searchentry->set_text(button->get_label());
+}
+
+void ExampleAppWindow::on_reveal_child_changed()
+{
+  update_words();
+}
+
+void ExampleAppWindow::update_words()
+{
+  auto tab = dynamic_cast<Gtk::ScrolledWindow*>(m_stack->get_visible_child());
+  if (!tab)
+    return;
+
+  auto view = dynamic_cast<Gtk::TextView*>(tab->get_child());
+  if (!view)
+  {
+    std::cout << "ExampleAppWindow::update_words(): No visible text view." << std::endl;
+    return;
+  }
+  auto buffer = view->get_buffer();
+
+  // Find all words in the text buffer.
+  std::set<Glib::ustring> words;
+  auto start = buffer->begin();
+  Gtk::TextIter end;
+  while (start)
+  {
+    while (start && !start.starts_word())
+      ++start;
+
+    if (!start)
+      break;
+
+    end = start;
+    end.forward_word_end();
+    if (start == end)
+      break;
+
+    auto word = buffer->get_text(start, end, false);
+    words.insert(word.lowercase());
+    start = end;
+  }
+
+  // Remove old children from the ListBox.
+  auto old_children = m_words->get_children();
+  for (auto child : old_children)
+  {
+    m_words->remove(*child);
+    delete child;
+  }
+
+  // Add new child buttons, one per unique word.
+  for (const auto& word : words)
+  {
+    auto row = Gtk::manage(new Gtk::Button(word));
+    row->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this,
+      &ExampleAppWindow::on_find_word), row));
+    row->show();
+    m_words->add(*row);
+  }
+}
diff --git a/examples/book/buildapp/step7/exampleappwindow.h b/examples/book/buildapp/step7/exampleappwindow.h
new file mode 100644
index 0000000..35ebacb
--- /dev/null
+++ b/examples/book/buildapp/step7/exampleappwindow.h
@@ -0,0 +1,52 @@
+/* gtkmm example Copyright (C) 2016 gtkmm development team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTKMM_EXAMPLEAPPWINDOW_H_
+#define GTKMM_EXAMPLEAPPWINDOW_H_
+
+#include <gtkmm.h>
+
+class ExampleAppWindow : public Gtk::ApplicationWindow
+{
+public:
+  ExampleAppWindow(BaseObjectType* cobject,
+    const Glib::RefPtr<Gtk::Builder>& refBuilder);
+
+  static ExampleAppWindow* create();
+
+  void open_file_view(const Glib::RefPtr<Gio::File>& file);
+
+protected:
+  // Signal handlers
+  void on_search_text_changed();
+  void on_visible_child_changed();
+  void on_find_word(const Gtk::Button* button);
+  void on_reveal_child_changed();
+
+  void update_words();
+
+  Glib::RefPtr<Gtk::Builder> m_refBuilder;
+  Glib::RefPtr<Gio::Settings> m_settings;
+  Gtk::Stack* m_stack;
+  Gtk::ToggleButton* m_search;
+  Gtk::SearchBar* m_searchbar;
+  Gtk::SearchEntry* m_searchentry;
+  Gtk::MenuButton* m_gears;
+  Gtk::Revealer* m_sidebar;
+  Gtk::ListBox* m_words;
+  Glib::RefPtr<Glib::Binding> m_prop_binding;
+};
+
+#endif /* GTKMM_EXAMPLEAPPWINDOW_H */
diff --git a/examples/book/buildapp/step7/gears_menu.ui b/examples/book/buildapp/step7/gears_menu.ui
new file mode 100644
index 0000000..7e5df31
--- /dev/null
+++ b/examples/book/buildapp/step7/gears_menu.ui
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<interface>
+  <!-- interface-requires gtk+ 3.0 -->
+  <menu id="menu">
+    <section>
+      <item>
+        <attribute name="label" translatable="yes">_Words</attribute>
+        <attribute name="action">win.show-words</attribute>
+      </item>
+    </section>
+  </menu>
+</interface>
diff --git a/examples/book/buildapp/step7/main.cc b/examples/book/buildapp/step7/main.cc
new file mode 100644
index 0000000..ca1f538
--- /dev/null
+++ b/examples/book/buildapp/step7/main.cc
@@ -0,0 +1,2 @@
+#include "../step5/main.cc"
+// Equal to the corresponding file in step5
diff --git a/examples/book/buildapp/step7/org.gtkmm.exampleapp.gschema.xml 
b/examples/book/buildapp/step7/org.gtkmm.exampleapp.gschema.xml
new file mode 100644
index 0000000..b9b30b6
--- /dev/null
+++ b/examples/book/buildapp/step7/org.gtkmm.exampleapp.gschema.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<schemalist>
+  <schema path="/org/gtkmm/exampleapp/" id="org.gtkmm.exampleapp">
+    <key name="font" type="s">
+      <default>'Monospace 12'</default>
+      <summary>Font</summary>
+      <description>The font to be used for content.</description>
+    </key>
+    <key name="transition" type="s">
+      <choices>
+        <choice value='none'/>
+        <choice value='crossfade'/>
+        <choice value='slide-left-right'/>
+      </choices>
+      <default>'none'</default>
+      <summary>Transition</summary>
+      <description>The transition to use when switching tabs.</description>
+    </key>
+    <key name="show-words" type="b">
+      <default>false</default>
+      <summary>Show words</summary>
+      <description>Whether to show a word list in the sidebar.</description>
+    </key>
+  </schema>
+</schemalist>
diff --git a/examples/book/buildapp/step7/prefs.ui b/examples/book/buildapp/step7/prefs.ui
new file mode 100644
index 0000000..1089e95
--- /dev/null
+++ b/examples/book/buildapp/step7/prefs.ui
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.8 -->
+  <object class="GtkDialog" id="prefs_dialog">
+    <property name="title" translatable="yes">Preferences</property>
+    <property name="resizable">False</property>
+    <property name="modal">True</property>
+    <property name="use-header-bar">1</property>
+    <child internal-child="vbox">
+      <object class="GtkBox" id="vbox">
+        <child>
+          <object class="GtkGrid" id="grid">
+            <property name="visible">True</property>
+            <property name="margin">6</property>
+            <property name="row-spacing">12</property>
+            <property name="column-spacing">6</property>
+            <child>
+              <object class="GtkLabel" id="fontlabel">
+                <property name="visible">True</property>
+                <property name="label">_Font:</property>
+                <property name="use-underline">True</property>
+                <property name="mnemonic-widget">font</property>
+                <property name="xalign">1</property>
+              </object>
+              <packing>
+                <property name="left-attach">0</property>
+                <property name="top-attach">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkFontButton" id="font">
+                <property name="visible">True</property>
+              </object>
+              <packing>
+                <property name="left-attach">1</property>
+                <property name="top-attach">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="transitionlabel">
+                <property name="visible">True</property>
+                <property name="label">_Transition:</property>
+                <property name="use-underline">True</property>
+                <property name="mnemonic-widget">transition</property>
+                <property name="xalign">1</property>
+              </object>
+              <packing>
+                <property name="left-attach">0</property>
+                <property name="top-attach">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkComboBoxText" id="transition">
+                <property name="visible">True</property>
+                <items>
+                  <item translatable="yes" id="none">None</item>
+                  <item translatable="yes" id="crossfade">Fade</item>
+                  <item translatable="yes" id="slide-left-right">Slide</item>
+                </items>
+              </object>
+              <packing>
+                <property name="left-attach">1</property>
+                <property name="top-attach">1</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/examples/book/buildapp/step7/window.ui b/examples/book/buildapp/step7/window.ui
new file mode 100644
index 0000000..bc795c7
--- /dev/null
+++ b/examples/book/buildapp/step7/window.ui
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.8 -->
+  <object class="GtkApplicationWindow" id="app_window">
+    <property name="title" translatable="yes">Example Application</property>
+    <property name="default-width">600</property>
+    <property name="default-height">400</property>
+    <child>
+      <object class="GtkBox" id="content_box">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <child>
+          <object class="GtkHeaderBar" id="header">
+            <property name="visible">True</property>
+            <child type="title">
+              <object class="GtkStackSwitcher" id="tabs">
+                <property name="visible">True</property>
+                <property name="stack">stack</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkToggleButton" id="search">
+                <property name="visible">True</property>
+                <property name="sensitive">False</property>
+                <style>
+                  <class name="image-button"/>
+                </style>
+                <child>
+                  <object class="GtkImage" id="search-icon">
+                    <property name="visible">True</property>
+                    <property name="icon-name">edit-find-symbolic</property>
+                    <property name="icon-size">1</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="pack-type">end</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkMenuButton" id="gears">
+                <property name="visible">True</property>
+                <property name="direction">none</property>
+                <property name="use-popover">True</property>
+                <style>
+                  <class name="image-button"/>
+                </style>
+              </object>
+              <packing>
+                <property name="pack-type">end</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="GtkSearchBar" id="searchbar">
+            <property name="visible">True</property>
+            <child>
+              <object class="GtkSearchEntry" id="searchentry">
+                <property name="visible">True</property>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="GtkBox" id="hbox">
+            <property name="visible">True</property>
+            <child>
+              <object class="GtkRevealer" id="sidebar">
+                <property name="visible">True</property>
+                <property name="transition-type">slide-right</property>
+                <child>
+                 <object class="GtkScrolledWindow" id="sidebar-sw">
+                   <property name="visible">True</property>
+                   <property name="hscrollbar-policy">never</property>
+                   <property name="vscrollbar-policy">automatic</property>
+                   <child>
+                     <object class="GtkListBox" id="words">
+                       <property name="visible">True</property>
+                       <property name="selection-mode">none</property>
+                     </object>
+                   </child>
+                 </object>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="GtkStack" id="stack">
+                <property name="visible">True</property>
+                <property name="transition-duration">500</property>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/examples/book/buildapp/step8/Makefile.example b/examples/book/buildapp/step8/Makefile.example
new file mode 100644
index 0000000..997766b
--- /dev/null
+++ b/examples/book/buildapp/step8/Makefile.example
@@ -0,0 +1,2 @@
+include ../step5/Makefile.example
+# Equal to the corresponding file in step5
diff --git a/examples/book/buildapp/step8/app_menu.ui b/examples/book/buildapp/step8/app_menu.ui
new file mode 100644
index 0000000..e1e131c
--- /dev/null
+++ b/examples/book/buildapp/step8/app_menu.ui
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<interface>
+  <!-- interface-requires gtk+ 3.0 -->
+  <menu id="appmenu">
+    <section>
+      <item>
+        <attribute name="label" translatable="yes">_Preferences</attribute>
+        <attribute name="action">app.preferences</attribute>
+      </item>
+    </section>
+    <section>
+      <item>
+        <attribute name="label" translatable="yes">_Quit</attribute>
+        <attribute name="action">app.quit</attribute>
+      </item>
+    </section>
+  </menu>
+</interface>
diff --git a/examples/book/buildapp/step8/exampleapp.gresource.xml 
b/examples/book/buildapp/step8/exampleapp.gresource.xml
new file mode 100644
index 0000000..dc39879
--- /dev/null
+++ b/examples/book/buildapp/step8/exampleapp.gresource.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+  <gresource prefix="/org/gtkmm/exampleapp">
+    <file preprocess="xml-stripblanks">window.ui</file>
+    <file preprocess="xml-stripblanks">app_menu.ui</file>
+    <file preprocess="xml-stripblanks">gears_menu.ui</file>
+    <file preprocess="xml-stripblanks">prefs.ui</file>
+  </gresource>
+</gresources>
diff --git a/examples/book/buildapp/step8/exampleapplication.cc 
b/examples/book/buildapp/step8/exampleapplication.cc
new file mode 100644
index 0000000..264c577
--- /dev/null
+++ b/examples/book/buildapp/step8/exampleapplication.cc
@@ -0,0 +1,2 @@
+#include "../step5/exampleapplication.cc"
+// Equal to the corresponding file in step5
diff --git a/examples/book/buildapp/step8/exampleapplication.h 
b/examples/book/buildapp/step8/exampleapplication.h
new file mode 100644
index 0000000..fd7b4e8
--- /dev/null
+++ b/examples/book/buildapp/step8/exampleapplication.h
@@ -0,0 +1,2 @@
+#include "../step4/exampleapplication.h"
+// Equal to the corresponding file in step4
diff --git a/examples/book/buildapp/step8/exampleappprefs.cc b/examples/book/buildapp/step8/exampleappprefs.cc
new file mode 100644
index 0000000..1bbe550
--- /dev/null
+++ b/examples/book/buildapp/step8/exampleappprefs.cc
@@ -0,0 +1,2 @@
+#include "../step5/exampleappprefs.cc"
+// Equal to the corresponding file in step5
diff --git a/examples/book/buildapp/step8/exampleappprefs.h b/examples/book/buildapp/step8/exampleappprefs.h
new file mode 100644
index 0000000..9d9ee18
--- /dev/null
+++ b/examples/book/buildapp/step8/exampleappprefs.h
@@ -0,0 +1,2 @@
+#include "../step5/exampleappprefs.h"
+// Equal to the corresponding file in step5
diff --git a/examples/book/buildapp/step8/exampleappwindow.cc 
b/examples/book/buildapp/step8/exampleappwindow.cc
new file mode 100644
index 0000000..29526f6
--- /dev/null
+++ b/examples/book/buildapp/step8/exampleappwindow.cc
@@ -0,0 +1,299 @@
+/* gtkmm example Copyright (C) 2016 gtkmm development team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "exampleappwindow.h"
+#include <iostream>
+#include <stdexcept>
+#include <set>
+
+ExampleAppWindow::ExampleAppWindow(BaseObjectType* cobject,
+  const Glib::RefPtr<Gtk::Builder>& refBuilder)
+: Gtk::ApplicationWindow(cobject),
+  m_refBuilder(refBuilder),
+  m_settings(),
+  m_stack(nullptr),
+  m_search(nullptr),
+  m_searchbar(nullptr),
+  m_searchentry(nullptr),
+  m_gears(nullptr),
+  m_sidebar(nullptr),
+  m_words(nullptr),
+  m_lines(nullptr),
+  m_lines_label(nullptr),
+  m_binding_search_enabled(),
+  m_binding_lines_visible()
+{
+  // Get widgets from the Gtk::Builder file.
+  m_refBuilder->get_widget("stack", m_stack);
+  if (!m_stack)
+    throw std::runtime_error("No \"stack\" object in window.ui");
+
+  m_refBuilder->get_widget("search", m_search);
+  if (!m_search)
+    throw std::runtime_error("No \"search\" object in window.ui");
+
+  m_refBuilder->get_widget("searchbar", m_searchbar);
+  if (!m_searchbar)
+    throw std::runtime_error("No \"searchbar\" object in window.ui");
+
+  m_refBuilder->get_widget("searchentry", m_searchentry);
+  if (!m_searchentry)
+    throw std::runtime_error("No \"searchentry\" object in window.ui");
+
+  m_refBuilder->get_widget("gears", m_gears);
+  if (!m_gears)
+    throw std::runtime_error("No \"gears\" object in window.ui");
+
+  m_refBuilder->get_widget("sidebar", m_sidebar);
+  if (!m_sidebar)
+    throw std::runtime_error("No \"sidebar\" object in window.ui");
+
+  m_refBuilder->get_widget("words", m_words);
+  if (!m_words)
+    throw std::runtime_error("No \"words\" object in window.ui");
+
+  m_refBuilder->get_widget("lines", m_lines);
+  if (!m_lines)
+    throw std::runtime_error("No \"lines\" object in window.ui");
+
+  m_refBuilder->get_widget("lines_label", m_lines_label);
+  if (!m_lines_label)
+    throw std::runtime_error("No \"lines_label\" object in window.ui");
+
+  // Bind settings.
+  m_settings = Gio::Settings::create("org.gtkmm.exampleapp");
+  m_settings->bind("transition", m_stack, "transition-type");
+  m_settings->bind("show-words", m_sidebar, "reveal-child");
+
+  // Bind properties of the search button to the search bar.
+  m_binding_search_enabled = Glib::Binding::bind_property(m_search->property_active(),
+    m_searchbar->property_search_mode_enabled(), Glib::BINDING_BIDIRECTIONAL);
+
+  // Connect signal handlers.
+  m_searchentry->signal_search_changed().connect(
+    sigc::mem_fun(*this, &ExampleAppWindow::on_search_text_changed));
+  m_stack->property_visible_child().signal_changed().connect(
+    sigc::mem_fun(*this, &ExampleAppWindow::on_visible_child_changed));
+  m_sidebar->property_reveal_child().signal_changed().connect(
+    sigc::mem_fun(*this, &ExampleAppWindow::on_reveal_child_changed));
+
+  // Connect the menu to the MenuButton m_gears, and bind the show-words setting
+  // to the win.show-words action and the "Words" menu item.
+  // (The connection between action and menu item is specified in gears_menu.ui.)
+  auto menu_builder = Gtk::Builder::create_from_resource("/org/gtkmm/exampleapp/gears_menu.ui");
+  auto object = menu_builder->get_object("menu");
+  auto menu = Glib::RefPtr<Gio::MenuModel>::cast_dynamic(object);
+  if (!menu)
+    throw std::runtime_error("No \"menu\" object in gears_menu.ui");
+
+  m_gears->set_menu_model(menu);
+  add_action(m_settings->create_action("show-words"));
+
+  // Bind the "visible" property of m_lines to the win.show-lines action, to
+  // the "Lines" menu item and to the "visible" property of m_lines_label.
+  // The GPropertyAction class is not wrapped in a glibmm class at the time of
+  // writing (october 2016). We have to call a glib function directly.
+  add_action(Glib::wrap((GAction*)
+    g_property_action_new("show-lines", m_lines->gobj(), "visible")));
+  m_binding_lines_visible = Glib::Binding::bind_property(m_lines->property_visible(),
+    m_lines_label->property_visible());
+}
+
+//static
+ExampleAppWindow* ExampleAppWindow::create()
+{
+  // Load the Builder file and instantiate its widgets.
+  auto refBuilder = Gtk::Builder::create_from_resource("/org/gtkmm/exampleapp/window.ui");
+
+  ExampleAppWindow* window = nullptr;
+  refBuilder->get_widget_derived("app_window", window);
+  if (!window)
+    throw std::runtime_error("No \"app_window\" object in window.ui");
+
+  return window;
+}
+
+void ExampleAppWindow::open_file_view(const Glib::RefPtr<Gio::File>& file)
+{
+  const auto basename = file->get_basename();
+
+  auto scrolled = Gtk::manage(new Gtk::ScrolledWindow());
+  scrolled->set_hexpand(true);
+  scrolled->set_vexpand(true);
+  scrolled->show();
+  auto view = Gtk::manage(new Gtk::TextView());
+  view->set_editable(false);
+  view->set_cursor_visible(false);
+  view->show();
+  scrolled->add(*view);
+  m_stack->add(*scrolled, basename, basename);
+
+  auto buffer = view->get_buffer();
+  try
+  {
+    char* contents = nullptr;
+    gsize length = 0;
+    
+    file->load_contents(contents, length);
+    buffer->set_text(contents, contents+length);
+    g_free(contents);
+  }
+  catch (const Glib::Error& ex)
+  {
+    std::cout << "ExampleAppWindow::open_file_view(\"" << file->get_parse_name()
+      << "\"):\n  " << ex.what() << std::endl;
+    return;
+  }
+
+  // tag is a Glib::RefPtr<Gtk::TextTag>. Gio::Settings::bind() requires a plain
+  // pointer to the tag. It does not accept a RefPtr. There is no Glib::RefPtr::get()
+  // or equivalent. We must use the unintuitive tag.operator->().
+  auto tag = buffer->create_tag();
+  m_settings->bind("font", tag.operator->(), "font");
+  buffer->apply_tag(tag, buffer->begin(), buffer->end());
+
+  m_search->set_sensitive(true);
+  update_words();
+  update_lines();
+}
+
+void ExampleAppWindow::on_search_text_changed()
+{
+  const auto text = m_searchentry->get_text();
+  if (text.empty())
+    return;
+
+  auto tab = dynamic_cast<Gtk::ScrolledWindow*>(m_stack->get_visible_child());
+  if (!tab)
+  {
+    std::cout << "ExampleAppWindow::on_search_text_changed(): No visible child." << std::endl;
+    return;
+  }
+
+  auto view = dynamic_cast<Gtk::TextView*>(tab->get_child());
+  if (!view)
+  {
+    std::cout << "ExampleAppWindow::on_search_text_changed(): No visible text view." << std::endl;
+    return;
+  }
+
+  // Very simple-minded search implementation.
+  auto buffer = view->get_buffer();
+  Gtk::TextIter match_start;
+  Gtk::TextIter match_end;
+  if (buffer->begin().forward_search(text, Gtk::TEXT_SEARCH_CASE_INSENSITIVE,
+      match_start, match_end))
+  {
+    buffer->select_range(match_start, match_end);
+    view->scroll_to(match_start);
+  }
+}
+
+void ExampleAppWindow::on_visible_child_changed()
+{
+  m_searchbar->set_search_mode(false);
+  update_words();
+  update_lines();  
+}
+
+void ExampleAppWindow::on_find_word(const Gtk::Button* button)
+{
+  m_searchentry->set_text(button->get_label());
+}
+
+void ExampleAppWindow::on_reveal_child_changed()
+{
+  update_words();
+}
+
+void ExampleAppWindow::update_words()
+{
+  auto tab = dynamic_cast<Gtk::ScrolledWindow*>(m_stack->get_visible_child());
+  if (!tab)
+    return;
+
+  auto view = dynamic_cast<Gtk::TextView*>(tab->get_child());
+  if (!view)
+  {
+    std::cout << "ExampleAppWindow::update_words(): No visible text view." << std::endl;
+    return;
+  }
+  auto buffer = view->get_buffer();
+
+  // Find all words in the text buffer.
+  std::set<Glib::ustring> words;
+  auto start = buffer->begin();
+  Gtk::TextIter end;
+  while (start)
+  {
+    while (start && !start.starts_word())
+      ++start;
+
+    if (!start)
+      break;
+
+    end = start;
+    end.forward_word_end();
+    if (start == end)
+      break;
+
+    auto word = buffer->get_text(start, end, false);
+    words.insert(word.lowercase());
+    start = end;
+  }
+
+  // Remove old children from the ListBox.
+  auto old_children = m_words->get_children();
+  for (auto child : old_children)
+  {
+    m_words->remove(*child);
+    delete child;
+  }
+
+  // Add new child buttons, one per unique word.
+  for (const auto& word : words)
+  {
+    auto row = Gtk::manage(new Gtk::Button(word));
+    row->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this,
+      &ExampleAppWindow::on_find_word), row));
+    row->show();
+    m_words->add(*row);
+  }
+}
+
+void ExampleAppWindow::update_lines()
+{
+  auto tab = dynamic_cast<Gtk::ScrolledWindow*>(m_stack->get_visible_child());
+  if (!tab)
+    return;
+
+  auto view = dynamic_cast<Gtk::TextView*>(tab->get_child());
+  if (!view)
+  {
+    std::cout << "ExampleAppWindow::update_lines(): No visible text view." << std::endl;
+    return;
+  }
+  auto buffer = view->get_buffer();
+
+  int count = 0;
+  auto iter = buffer->begin();
+  while (iter)
+  {
+    ++count;
+    if (!iter.forward_line())
+      break;
+  }
+  m_lines->set_text(Glib::ustring::format(count));
+}
diff --git a/examples/book/buildapp/step8/exampleappwindow.h b/examples/book/buildapp/step8/exampleappwindow.h
new file mode 100644
index 0000000..bcc3dcb
--- /dev/null
+++ b/examples/book/buildapp/step8/exampleappwindow.h
@@ -0,0 +1,56 @@
+/* gtkmm example Copyright (C) 2016 gtkmm development team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTKMM_EXAMPLEAPPWINDOW_H_
+#define GTKMM_EXAMPLEAPPWINDOW_H_
+
+#include <gtkmm.h>
+
+class ExampleAppWindow : public Gtk::ApplicationWindow
+{
+public:
+  ExampleAppWindow(BaseObjectType* cobject,
+    const Glib::RefPtr<Gtk::Builder>& refBuilder);
+
+  static ExampleAppWindow* create();
+
+  void open_file_view(const Glib::RefPtr<Gio::File>& file);
+
+protected:
+  // Signal handlers
+  void on_search_text_changed();
+  void on_visible_child_changed();
+  void on_find_word(const Gtk::Button* button);
+  void on_reveal_child_changed();
+
+  void update_words();
+  void update_lines();
+
+  Glib::RefPtr<Gtk::Builder> m_refBuilder;
+  Glib::RefPtr<Gio::Settings> m_settings;
+  Gtk::Stack* m_stack;
+  Gtk::ToggleButton* m_search;
+  Gtk::SearchBar* m_searchbar;
+  Gtk::SearchEntry* m_searchentry;
+  Gtk::MenuButton* m_gears;
+  Gtk::Revealer* m_sidebar;
+  Gtk::ListBox* m_words;
+  Gtk::Label* m_lines;
+  Gtk::Label* m_lines_label;
+  Glib::RefPtr<Glib::Binding> m_binding_search_enabled;
+  Glib::RefPtr<Glib::Binding> m_binding_lines_visible;
+};
+
+#endif /* GTKMM_EXAMPLEAPPWINDOW_H */
diff --git a/examples/book/buildapp/step8/gears_menu.ui b/examples/book/buildapp/step8/gears_menu.ui
new file mode 100644
index 0000000..fb0e929
--- /dev/null
+++ b/examples/book/buildapp/step8/gears_menu.ui
@@ -0,0 +1,16 @@
+<?xml version="1.0"?>
+<interface>
+  <!-- interface-requires gtk+ 3.0 -->
+  <menu id="menu">
+    <section>
+      <item>
+        <attribute name="label" translatable="yes">_Words</attribute>
+        <attribute name="action">win.show-words</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">_Lines</attribute>
+        <attribute name="action">win.show-lines</attribute>
+      </item>
+    </section>
+  </menu>
+</interface>
diff --git a/examples/book/buildapp/step8/main.cc b/examples/book/buildapp/step8/main.cc
new file mode 100644
index 0000000..ca1f538
--- /dev/null
+++ b/examples/book/buildapp/step8/main.cc
@@ -0,0 +1,2 @@
+#include "../step5/main.cc"
+// Equal to the corresponding file in step5
diff --git a/examples/book/buildapp/step8/org.gtkmm.exampleapp.gschema.xml 
b/examples/book/buildapp/step8/org.gtkmm.exampleapp.gschema.xml
new file mode 100644
index 0000000..b9b30b6
--- /dev/null
+++ b/examples/book/buildapp/step8/org.gtkmm.exampleapp.gschema.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<schemalist>
+  <schema path="/org/gtkmm/exampleapp/" id="org.gtkmm.exampleapp">
+    <key name="font" type="s">
+      <default>'Monospace 12'</default>
+      <summary>Font</summary>
+      <description>The font to be used for content.</description>
+    </key>
+    <key name="transition" type="s">
+      <choices>
+        <choice value='none'/>
+        <choice value='crossfade'/>
+        <choice value='slide-left-right'/>
+      </choices>
+      <default>'none'</default>
+      <summary>Transition</summary>
+      <description>The transition to use when switching tabs.</description>
+    </key>
+    <key name="show-words" type="b">
+      <default>false</default>
+      <summary>Show words</summary>
+      <description>Whether to show a word list in the sidebar.</description>
+    </key>
+  </schema>
+</schemalist>
diff --git a/examples/book/buildapp/step8/prefs.ui b/examples/book/buildapp/step8/prefs.ui
new file mode 100644
index 0000000..1089e95
--- /dev/null
+++ b/examples/book/buildapp/step8/prefs.ui
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.8 -->
+  <object class="GtkDialog" id="prefs_dialog">
+    <property name="title" translatable="yes">Preferences</property>
+    <property name="resizable">False</property>
+    <property name="modal">True</property>
+    <property name="use-header-bar">1</property>
+    <child internal-child="vbox">
+      <object class="GtkBox" id="vbox">
+        <child>
+          <object class="GtkGrid" id="grid">
+            <property name="visible">True</property>
+            <property name="margin">6</property>
+            <property name="row-spacing">12</property>
+            <property name="column-spacing">6</property>
+            <child>
+              <object class="GtkLabel" id="fontlabel">
+                <property name="visible">True</property>
+                <property name="label">_Font:</property>
+                <property name="use-underline">True</property>
+                <property name="mnemonic-widget">font</property>
+                <property name="xalign">1</property>
+              </object>
+              <packing>
+                <property name="left-attach">0</property>
+                <property name="top-attach">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkFontButton" id="font">
+                <property name="visible">True</property>
+              </object>
+              <packing>
+                <property name="left-attach">1</property>
+                <property name="top-attach">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="transitionlabel">
+                <property name="visible">True</property>
+                <property name="label">_Transition:</property>
+                <property name="use-underline">True</property>
+                <property name="mnemonic-widget">transition</property>
+                <property name="xalign">1</property>
+              </object>
+              <packing>
+                <property name="left-attach">0</property>
+                <property name="top-attach">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkComboBoxText" id="transition">
+                <property name="visible">True</property>
+                <items>
+                  <item translatable="yes" id="none">None</item>
+                  <item translatable="yes" id="crossfade">Fade</item>
+                  <item translatable="yes" id="slide-left-right">Slide</item>
+                </items>
+              </object>
+              <packing>
+                <property name="left-attach">1</property>
+                <property name="top-attach">1</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/examples/book/buildapp/step8/window.ui b/examples/book/buildapp/step8/window.ui
new file mode 100644
index 0000000..c908b59
--- /dev/null
+++ b/examples/book/buildapp/step8/window.ui
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.8 -->
+  <object class="GtkApplicationWindow" id="app_window">
+    <property name="title" translatable="yes">Example Application</property>
+    <property name="default-width">600</property>
+    <property name="default-height">400</property>
+    <child>
+      <object class="GtkBox" id="content_box">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <child>
+          <object class="GtkHeaderBar" id="header">
+            <property name="visible">True</property>
+            <child>
+              <object class="GtkLabel" id="lines_label">
+                <property name="visible">False</property>
+                <property name="label" translatable="yes">Lines:</property>
+              </object>
+              <packing>
+                <property name="pack-type">start</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="lines">
+                <property name="visible">False</property>
+              </object>
+              <packing>
+                <property name="pack-type">start</property>
+              </packing>
+            </child>
+            <child type="title">
+              <object class="GtkStackSwitcher" id="tabs">
+                <property name="visible">True</property>
+                <property name="stack">stack</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkToggleButton" id="search">
+                <property name="visible">True</property>
+                <property name="sensitive">False</property>
+                <style>
+                  <class name="image-button"/>
+                </style>
+                <child>
+                  <object class="GtkImage" id="search-icon">
+                    <property name="visible">True</property>
+                    <property name="icon-name">edit-find-symbolic</property>
+                    <property name="icon-size">1</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="pack-type">end</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkMenuButton" id="gears">
+                <property name="visible">True</property>
+                <property name="direction">none</property>
+                <property name="use-popover">True</property>
+                <style>
+                  <class name="image-button"/>
+                </style>
+              </object>
+              <packing>
+                <property name="pack-type">end</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="GtkSearchBar" id="searchbar">
+            <property name="visible">True</property>
+            <child>
+              <object class="GtkSearchEntry" id="searchentry">
+                <property name="visible">True</property>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="GtkBox" id="hbox">
+            <property name="visible">True</property>
+            <child>
+              <object class="GtkRevealer" id="sidebar">
+                <property name="visible">True</property>
+                <property name="transition-type">slide-right</property>
+                <child>
+                 <object class="GtkScrolledWindow" id="sidebar-sw">
+                   <property name="visible">True</property>
+                   <property name="hscrollbar-policy">never</property>
+                   <property name="vscrollbar-policy">automatic</property>
+                   <child>
+                     <object class="GtkListBox" id="words">
+                       <property name="visible">True</property>
+                       <property name="selection-mode">none</property>
+                     </object>
+                   </child>
+                 </object>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="GtkStack" id="stack">
+                <property name="visible">True</property>
+                <property name="transition-duration">500</property>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/examples/book/buildapp/step9/Makefile.example b/examples/book/buildapp/step9/Makefile.example
new file mode 100644
index 0000000..997766b
--- /dev/null
+++ b/examples/book/buildapp/step9/Makefile.example
@@ -0,0 +1,2 @@
+include ../step5/Makefile.example
+# Equal to the corresponding file in step5
diff --git a/examples/book/buildapp/step9/app_menu.ui b/examples/book/buildapp/step9/app_menu.ui
new file mode 100644
index 0000000..e1e131c
--- /dev/null
+++ b/examples/book/buildapp/step9/app_menu.ui
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<interface>
+  <!-- interface-requires gtk+ 3.0 -->
+  <menu id="appmenu">
+    <section>
+      <item>
+        <attribute name="label" translatable="yes">_Preferences</attribute>
+        <attribute name="action">app.preferences</attribute>
+      </item>
+    </section>
+    <section>
+      <item>
+        <attribute name="label" translatable="yes">_Quit</attribute>
+        <attribute name="action">app.quit</attribute>
+      </item>
+    </section>
+  </menu>
+</interface>
diff --git a/examples/book/buildapp/step9/exampleapp.gresource.xml 
b/examples/book/buildapp/step9/exampleapp.gresource.xml
new file mode 100644
index 0000000..4ffa168
--- /dev/null
+++ b/examples/book/buildapp/step9/exampleapp.gresource.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+  <gresource prefix="/org/gtkmm/exampleapp">
+    <file preprocess="xml-stripblanks">window.ui</file>
+    <file preprocess="xml-stripblanks">app_menu.ui</file>
+    <file preprocess="xml-stripblanks">gears_menu.ui</file>
+    <file preprocess="xml-stripblanks">prefs.ui</file>
+    <file>exampleapp.png</file>
+  </gresource>
+</gresources>
diff --git a/examples/book/buildapp/step9/exampleapp.png b/examples/book/buildapp/step9/exampleapp.png
new file mode 100644
index 0000000..8beb54e
Binary files /dev/null and b/examples/book/buildapp/step9/exampleapp.png differ
diff --git a/examples/book/buildapp/step9/exampleapplication.cc 
b/examples/book/buildapp/step9/exampleapplication.cc
new file mode 100644
index 0000000..264c577
--- /dev/null
+++ b/examples/book/buildapp/step9/exampleapplication.cc
@@ -0,0 +1,2 @@
+#include "../step5/exampleapplication.cc"
+// Equal to the corresponding file in step5
diff --git a/examples/book/buildapp/step9/exampleapplication.h 
b/examples/book/buildapp/step9/exampleapplication.h
new file mode 100644
index 0000000..fd7b4e8
--- /dev/null
+++ b/examples/book/buildapp/step9/exampleapplication.h
@@ -0,0 +1,2 @@
+#include "../step4/exampleapplication.h"
+// Equal to the corresponding file in step4
diff --git a/examples/book/buildapp/step9/exampleappprefs.cc b/examples/book/buildapp/step9/exampleappprefs.cc
new file mode 100644
index 0000000..1bbe550
--- /dev/null
+++ b/examples/book/buildapp/step9/exampleappprefs.cc
@@ -0,0 +1,2 @@
+#include "../step5/exampleappprefs.cc"
+// Equal to the corresponding file in step5
diff --git a/examples/book/buildapp/step9/exampleappprefs.h b/examples/book/buildapp/step9/exampleappprefs.h
new file mode 100644
index 0000000..9d9ee18
--- /dev/null
+++ b/examples/book/buildapp/step9/exampleappprefs.h
@@ -0,0 +1,2 @@
+#include "../step5/exampleappprefs.h"
+// Equal to the corresponding file in step5
diff --git a/examples/book/buildapp/step9/exampleappwindow.cc 
b/examples/book/buildapp/step9/exampleappwindow.cc
new file mode 100644
index 0000000..9b850ab
--- /dev/null
+++ b/examples/book/buildapp/step9/exampleappwindow.cc
@@ -0,0 +1,308 @@
+/* gtkmm example Copyright (C) 2016 gtkmm development team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "exampleappwindow.h"
+#include <iostream>
+#include <stdexcept>
+#include <set>
+
+ExampleAppWindow::ExampleAppWindow(BaseObjectType* cobject,
+  const Glib::RefPtr<Gtk::Builder>& refBuilder)
+: Gtk::ApplicationWindow(cobject),
+  m_refBuilder(refBuilder),
+  m_settings(),
+  m_stack(nullptr),
+  m_search(nullptr),
+  m_searchbar(nullptr),
+  m_searchentry(nullptr),
+  m_gears(nullptr),
+  m_sidebar(nullptr),
+  m_words(nullptr),
+  m_lines(nullptr),
+  m_lines_label(nullptr),
+  m_binding_search_enabled(),
+  m_binding_lines_visible()
+{
+  // Get widgets from the Gtk::Builder file.
+  m_refBuilder->get_widget("stack", m_stack);
+  if (!m_stack)
+    throw std::runtime_error("No \"stack\" object in window.ui");
+
+  m_refBuilder->get_widget("search", m_search);
+  if (!m_search)
+    throw std::runtime_error("No \"search\" object in window.ui");
+
+  m_refBuilder->get_widget("searchbar", m_searchbar);
+  if (!m_searchbar)
+    throw std::runtime_error("No \"searchbar\" object in window.ui");
+
+  m_refBuilder->get_widget("searchentry", m_searchentry);
+  if (!m_searchentry)
+    throw std::runtime_error("No \"searchentry\" object in window.ui");
+
+  m_refBuilder->get_widget("gears", m_gears);
+  if (!m_gears)
+    throw std::runtime_error("No \"gears\" object in window.ui");
+
+  m_refBuilder->get_widget("sidebar", m_sidebar);
+  if (!m_sidebar)
+    throw std::runtime_error("No \"sidebar\" object in window.ui");
+
+  m_refBuilder->get_widget("words", m_words);
+  if (!m_words)
+    throw std::runtime_error("No \"words\" object in window.ui");
+
+  m_refBuilder->get_widget("lines", m_lines);
+  if (!m_lines)
+    throw std::runtime_error("No \"lines\" object in window.ui");
+
+  m_refBuilder->get_widget("lines_label", m_lines_label);
+  if (!m_lines_label)
+    throw std::runtime_error("No \"lines_label\" object in window.ui");
+
+  // Bind settings.
+  m_settings = Gio::Settings::create("org.gtkmm.exampleapp");
+  m_settings->bind("transition", m_stack, "transition-type");
+  m_settings->bind("show-words", m_sidebar, "reveal-child");
+
+  // Bind properties of the search button to the search bar.
+  m_binding_search_enabled = Glib::Binding::bind_property(m_search->property_active(),
+    m_searchbar->property_search_mode_enabled(), Glib::BINDING_BIDIRECTIONAL);
+
+  // Connect signal handlers.
+  m_searchentry->signal_search_changed().connect(
+    sigc::mem_fun(*this, &ExampleAppWindow::on_search_text_changed));
+  m_stack->property_visible_child().signal_changed().connect(
+    sigc::mem_fun(*this, &ExampleAppWindow::on_visible_child_changed));
+  m_sidebar->property_reveal_child().signal_changed().connect(
+    sigc::mem_fun(*this, &ExampleAppWindow::on_reveal_child_changed));
+
+  // Connect the menu to the MenuButton m_gears, and bind the show-words setting
+  // to the win.show-words action and the "Words" menu item.
+  // (The connection between action and menu item is specified in gears_menu.ui.)
+  auto menu_builder = Gtk::Builder::create_from_resource("/org/gtkmm/exampleapp/gears_menu.ui");
+  auto object = menu_builder->get_object("menu");
+  auto menu = Glib::RefPtr<Gio::MenuModel>::cast_dynamic(object);
+  if (!menu)
+    throw std::runtime_error("No \"menu\" object in gears_menu.ui");
+
+  m_gears->set_menu_model(menu);
+  add_action(m_settings->create_action("show-words"));
+
+  // Bind the "visible" property of m_lines to the win.show-lines action, to
+  // the "Lines" menu item and to the "visible" property of m_lines_label.
+  // The GPropertyAction class is not wrapped in a glibmm class at the time of
+  // writing (october 2016). We have to call a glib function directly.
+  add_action(Glib::wrap((GAction*)
+    g_property_action_new("show-lines", m_lines->gobj(), "visible")));
+  m_binding_lines_visible = Glib::Binding::bind_property(m_lines->property_visible(),
+    m_lines_label->property_visible());
+
+  // Display the application menu in the application, not in the desktop environment.
+  auto gtk_settings = Gtk::Settings::get_default();
+  if (gtk_settings)
+    gtk_settings->property_gtk_shell_shows_app_menu() = false;
+  set_show_menubar(true);
+
+  // Set the window icon.
+  set_icon(Gdk::Pixbuf::create_from_resource("/org/gtkmm/exampleapp/exampleapp.png"));
+}
+
+//static
+ExampleAppWindow* ExampleAppWindow::create()
+{
+  // Load the Builder file and instantiate its widgets.
+  auto refBuilder = Gtk::Builder::create_from_resource("/org/gtkmm/exampleapp/window.ui");
+
+  ExampleAppWindow* window = nullptr;
+  refBuilder->get_widget_derived("app_window", window);
+  if (!window)
+    throw std::runtime_error("No \"app_window\" object in window.ui");
+
+  return window;
+}
+
+void ExampleAppWindow::open_file_view(const Glib::RefPtr<Gio::File>& file)
+{
+  const auto basename = file->get_basename();
+
+  auto scrolled = Gtk::manage(new Gtk::ScrolledWindow());
+  scrolled->set_hexpand(true);
+  scrolled->set_vexpand(true);
+  scrolled->show();
+  auto view = Gtk::manage(new Gtk::TextView());
+  view->set_editable(false);
+  view->set_cursor_visible(false);
+  view->show();
+  scrolled->add(*view);
+  m_stack->add(*scrolled, basename, basename);
+
+  auto buffer = view->get_buffer();
+  try
+  {
+    char* contents = nullptr;
+    gsize length = 0;
+    
+    file->load_contents(contents, length);
+    buffer->set_text(contents, contents+length);
+    g_free(contents);
+  }
+  catch (const Glib::Error& ex)
+  {
+    std::cout << "ExampleAppWindow::open_file_view(\"" << file->get_parse_name()
+      << "\"):\n  " << ex.what() << std::endl;
+    return;
+  }
+
+  // tag is a Glib::RefPtr<Gtk::TextTag>. Gio::Settings::bind() requires a plain
+  // pointer to the tag. It does not accept a RefPtr. There is no Glib::RefPtr::get()
+  // or equivalent. We must use the unintuitive tag.operator->().
+  auto tag = buffer->create_tag();
+  m_settings->bind("font", tag.operator->(), "font");
+  buffer->apply_tag(tag, buffer->begin(), buffer->end());
+
+  m_search->set_sensitive(true);
+  update_words();
+  update_lines();
+}
+
+void ExampleAppWindow::on_search_text_changed()
+{
+  const auto text = m_searchentry->get_text();
+  if (text.empty())
+    return;
+
+  auto tab = dynamic_cast<Gtk::ScrolledWindow*>(m_stack->get_visible_child());
+  if (!tab)
+  {
+    std::cout << "ExampleAppWindow::on_search_text_changed(): No visible child." << std::endl;
+    return;
+  }
+
+  auto view = dynamic_cast<Gtk::TextView*>(tab->get_child());
+  if (!view)
+  {
+    std::cout << "ExampleAppWindow::on_search_text_changed(): No visible text view." << std::endl;
+    return;
+  }
+
+  // Very simple-minded search implementation.
+  auto buffer = view->get_buffer();
+  Gtk::TextIter match_start;
+  Gtk::TextIter match_end;
+  if (buffer->begin().forward_search(text, Gtk::TEXT_SEARCH_CASE_INSENSITIVE,
+      match_start, match_end))
+  {
+    buffer->select_range(match_start, match_end);
+    view->scroll_to(match_start);
+  }
+}
+
+void ExampleAppWindow::on_visible_child_changed()
+{
+  m_searchbar->set_search_mode(false);
+  update_words();
+  update_lines();  
+}
+
+void ExampleAppWindow::on_find_word(const Gtk::Button* button)
+{
+  m_searchentry->set_text(button->get_label());
+}
+
+void ExampleAppWindow::on_reveal_child_changed()
+{
+  update_words();
+}
+
+void ExampleAppWindow::update_words()
+{
+  auto tab = dynamic_cast<Gtk::ScrolledWindow*>(m_stack->get_visible_child());
+  if (!tab)
+    return;
+
+  auto view = dynamic_cast<Gtk::TextView*>(tab->get_child());
+  if (!view)
+  {
+    std::cout << "ExampleAppWindow::update_words(): No visible text view." << std::endl;
+    return;
+  }
+  auto buffer = view->get_buffer();
+
+  // Find all words in the text buffer.
+  std::set<Glib::ustring> words;
+  auto start = buffer->begin();
+  Gtk::TextIter end;
+  while (start)
+  {
+    while (start && !start.starts_word())
+      ++start;
+
+    if (!start)
+      break;
+
+    end = start;
+    end.forward_word_end();
+    if (start == end)
+      break;
+
+    auto word = buffer->get_text(start, end, false);
+    words.insert(word.lowercase());
+    start = end;
+  }
+
+  // Remove old children from the ListBox.
+  auto old_children = m_words->get_children();
+  for (auto child : old_children)
+  {
+    m_words->remove(*child);
+    delete child;
+  }
+
+  // Add new child buttons, one per unique word.
+  for (const auto& word : words)
+  {
+    auto row = Gtk::manage(new Gtk::Button(word));
+    row->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this,
+      &ExampleAppWindow::on_find_word), row));
+    row->show();
+    m_words->add(*row);
+  }
+}
+
+void ExampleAppWindow::update_lines()
+{
+  auto tab = dynamic_cast<Gtk::ScrolledWindow*>(m_stack->get_visible_child());
+  if (!tab)
+    return;
+
+  auto view = dynamic_cast<Gtk::TextView*>(tab->get_child());
+  if (!view)
+  {
+    std::cout << "ExampleAppWindow::update_lines(): No visible text view." << std::endl;
+    return;
+  }
+  auto buffer = view->get_buffer();
+
+  int count = 0;
+  auto iter = buffer->begin();
+  while (iter)
+  {
+    ++count;
+    if (!iter.forward_line())
+      break;
+  }
+  m_lines->set_text(Glib::ustring::format(count));
+}
diff --git a/examples/book/buildapp/step9/exampleappwindow.h b/examples/book/buildapp/step9/exampleappwindow.h
new file mode 100644
index 0000000..c96764a
--- /dev/null
+++ b/examples/book/buildapp/step9/exampleappwindow.h
@@ -0,0 +1,2 @@
+#include "../step8/exampleappwindow.h"
+// Equal to the corresponding file in step8
diff --git a/examples/book/buildapp/step9/gears_menu.ui b/examples/book/buildapp/step9/gears_menu.ui
new file mode 100644
index 0000000..fb0e929
--- /dev/null
+++ b/examples/book/buildapp/step9/gears_menu.ui
@@ -0,0 +1,16 @@
+<?xml version="1.0"?>
+<interface>
+  <!-- interface-requires gtk+ 3.0 -->
+  <menu id="menu">
+    <section>
+      <item>
+        <attribute name="label" translatable="yes">_Words</attribute>
+        <attribute name="action">win.show-words</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">_Lines</attribute>
+        <attribute name="action">win.show-lines</attribute>
+      </item>
+    </section>
+  </menu>
+</interface>
diff --git a/examples/book/buildapp/step9/main.cc b/examples/book/buildapp/step9/main.cc
new file mode 100644
index 0000000..ca1f538
--- /dev/null
+++ b/examples/book/buildapp/step9/main.cc
@@ -0,0 +1,2 @@
+#include "../step5/main.cc"
+// Equal to the corresponding file in step5
diff --git a/examples/book/buildapp/step9/org.gtkmm.exampleapp.gschema.xml 
b/examples/book/buildapp/step9/org.gtkmm.exampleapp.gschema.xml
new file mode 100644
index 0000000..b9b30b6
--- /dev/null
+++ b/examples/book/buildapp/step9/org.gtkmm.exampleapp.gschema.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<schemalist>
+  <schema path="/org/gtkmm/exampleapp/" id="org.gtkmm.exampleapp">
+    <key name="font" type="s">
+      <default>'Monospace 12'</default>
+      <summary>Font</summary>
+      <description>The font to be used for content.</description>
+    </key>
+    <key name="transition" type="s">
+      <choices>
+        <choice value='none'/>
+        <choice value='crossfade'/>
+        <choice value='slide-left-right'/>
+      </choices>
+      <default>'none'</default>
+      <summary>Transition</summary>
+      <description>The transition to use when switching tabs.</description>
+    </key>
+    <key name="show-words" type="b">
+      <default>false</default>
+      <summary>Show words</summary>
+      <description>Whether to show a word list in the sidebar.</description>
+    </key>
+  </schema>
+</schemalist>
diff --git a/examples/book/buildapp/step9/prefs.ui b/examples/book/buildapp/step9/prefs.ui
new file mode 100644
index 0000000..1089e95
--- /dev/null
+++ b/examples/book/buildapp/step9/prefs.ui
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.8 -->
+  <object class="GtkDialog" id="prefs_dialog">
+    <property name="title" translatable="yes">Preferences</property>
+    <property name="resizable">False</property>
+    <property name="modal">True</property>
+    <property name="use-header-bar">1</property>
+    <child internal-child="vbox">
+      <object class="GtkBox" id="vbox">
+        <child>
+          <object class="GtkGrid" id="grid">
+            <property name="visible">True</property>
+            <property name="margin">6</property>
+            <property name="row-spacing">12</property>
+            <property name="column-spacing">6</property>
+            <child>
+              <object class="GtkLabel" id="fontlabel">
+                <property name="visible">True</property>
+                <property name="label">_Font:</property>
+                <property name="use-underline">True</property>
+                <property name="mnemonic-widget">font</property>
+                <property name="xalign">1</property>
+              </object>
+              <packing>
+                <property name="left-attach">0</property>
+                <property name="top-attach">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkFontButton" id="font">
+                <property name="visible">True</property>
+              </object>
+              <packing>
+                <property name="left-attach">1</property>
+                <property name="top-attach">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="transitionlabel">
+                <property name="visible">True</property>
+                <property name="label">_Transition:</property>
+                <property name="use-underline">True</property>
+                <property name="mnemonic-widget">transition</property>
+                <property name="xalign">1</property>
+              </object>
+              <packing>
+                <property name="left-attach">0</property>
+                <property name="top-attach">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkComboBoxText" id="transition">
+                <property name="visible">True</property>
+                <items>
+                  <item translatable="yes" id="none">None</item>
+                  <item translatable="yes" id="crossfade">Fade</item>
+                  <item translatable="yes" id="slide-left-right">Slide</item>
+                </items>
+              </object>
+              <packing>
+                <property name="left-attach">1</property>
+                <property name="top-attach">1</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/examples/book/buildapp/step9/window.ui b/examples/book/buildapp/step9/window.ui
new file mode 100644
index 0000000..0261a3c
--- /dev/null
+++ b/examples/book/buildapp/step9/window.ui
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.8 -->
+  <object class="GtkApplicationWindow" id="app_window">
+    <property name="default-width">600</property>
+    <property name="default-height">400</property>
+    <child type="titlebar">
+      <object class="GtkHeaderBar" id="header">
+        <property name="title" translatable="yes">Example Application</property>
+        <property name="visible">True</property>
+        <property name="show-close-button">True</property>
+        <property name="decoration-layout">menu:close</property>
+        <child>
+          <object class="GtkLabel" id="lines_label">
+            <property name="visible">False</property>
+            <property name="label" translatable="yes">Lines:</property>
+          </object>
+          <packing>
+            <property name="pack-type">start</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel" id="lines">
+            <property name="visible">False</property>
+          </object>
+          <packing>
+            <property name="pack-type">start</property>
+          </packing>
+        </child>
+        <child type="title">
+          <object class="GtkStackSwitcher" id="tabs">
+            <property name="visible">True</property>
+            <property name="stack">stack</property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkToggleButton" id="search">
+            <property name="visible">True</property>
+            <property name="sensitive">False</property>
+            <style>
+              <class name="image-button"/>
+            </style>
+            <child>
+              <object class="GtkImage" id="search-icon">
+                <property name="visible">True</property>
+                <property name="icon-name">edit-find-symbolic</property>
+                <property name="icon-size">1</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="pack-type">end</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkMenuButton" id="gears">
+            <property name="visible">True</property>
+            <property name="direction">none</property>
+            <property name="use-popover">True</property>
+            <style>
+              <class name="image-button"/>
+            </style>
+          </object>
+          <packing>
+            <property name="pack-type">end</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <child>
+      <object class="GtkBox" id="content_box">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <child>
+          <object class="GtkSearchBar" id="searchbar">
+            <property name="visible">True</property>
+            <child>
+              <object class="GtkSearchEntry" id="searchentry">
+                <property name="visible">True</property>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="GtkBox" id="hbox">
+            <property name="visible">True</property>
+            <child>
+              <object class="GtkRevealer" id="sidebar">
+                <property name="visible">True</property>
+                <property name="transition-type">slide-right</property>
+                <child>
+                 <object class="GtkScrolledWindow" id="sidebar-sw">
+                   <property name="visible">True</property>
+                   <property name="hscrollbar-policy">never</property>
+                   <property name="vscrollbar-policy">automatic</property>
+                   <child>
+                     <object class="GtkListBox" id="words">
+                       <property name="visible">True</property>
+                       <property name="selection-mode">none</property>
+                     </object>
+                   </child>
+                 </object>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="GtkStack" id="stack">
+                <property name="visible">True</property>
+                <property name="transition-duration">500</property>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]