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



commit 0432609e23c645046c4ae330c47801d842f236e6
Author: Kjell Ahlstedt <kjell ahlstedt bredband net>
Date:   Sat Oct 15 10:31:02 2016 +0200

    Add another part of "Building applications" chapter with example code
    
    * docs/tutorial/C/index-in.docbook: Add sections 4-6 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.
    * docs/tutorial/Makefile.am: Add new image files.
    * configure.ac: Add GLIB_GSETTINGS.
    * examples/Makefile.am: Don't distribute resources.c files.
    * examples/book/buildapp/Makefile.am: Add files in step[4-6].
    * examples/book/buildapp/step[1-2]/Makefile.example: Improved
    * examples/book/buildapp/step[4-6]: New directories with many new files.
    Bug #772391

 configure.ac                                       |    3 +
 docs/tutorial/C/figures/buildapp_app_menu.png      |  Bin 0 -> 72892 bytes
 docs/tutorial/C/figures/buildapp_pref_dialog.png   |  Bin 0 -> 9421 bytes
 docs/tutorial/C/figures/buildapp_search_bar.png    |  Bin 0 -> 61228 bytes
 docs/tutorial/C/index-in.docbook                   |  200 ++++++++++++++++++++
 docs/tutorial/Makefile.am                          |    3 +
 examples/Makefile.am                               |    8 +-
 examples/book/buildapp/Makefile.am                 |  101 +++++++++-
 examples/book/buildapp/step1/Makefile.example      |    9 +-
 examples/book/buildapp/step2/Makefile.example      |   17 +-
 examples/book/buildapp/step4/Makefile.example      |   42 ++++
 examples/book/buildapp/step4/app_menu.ui           |   18 ++
 .../book/buildapp/step4/exampleapp.gresource.xml   |    7 +
 examples/book/buildapp/step4/exampleapplication.cc |  159 ++++++++++++++++
 examples/book/buildapp/step4/exampleapplication.h  |   45 +++++
 examples/book/buildapp/step4/exampleappwindow.cc   |    2 +
 examples/book/buildapp/step4/exampleappwindow.h    |    2 +
 examples/book/buildapp/step4/main.cc               |    2 +
 examples/book/buildapp/step4/window.ui             |   31 +++
 examples/book/buildapp/step5/Makefile.example      |   55 ++++++
 examples/book/buildapp/step5/app_menu.ui           |   18 ++
 .../book/buildapp/step5/exampleapp.gresource.xml   |    8 +
 examples/book/buildapp/step5/exampleapplication.cc |  176 +++++++++++++++++
 examples/book/buildapp/step5/exampleapplication.h  |    2 +
 examples/book/buildapp/step5/exampleappprefs.cc    |   55 ++++++
 examples/book/buildapp/step5/exampleappprefs.h     |   36 ++++
 examples/book/buildapp/step5/exampleappwindow.cc   |   86 +++++++++
 examples/book/buildapp/step5/exampleappwindow.h    |   37 ++++
 examples/book/buildapp/step5/main.cc               |   31 +++
 .../step5/org.gtkmm.exampleapp.gschema.xml         |   20 ++
 examples/book/buildapp/step5/prefs.ui              |   71 +++++++
 examples/book/buildapp/step5/window.ui             |   31 +++
 examples/book/buildapp/step6/Makefile.example      |    2 +
 examples/book/buildapp/step6/app_menu.ui           |   18 ++
 .../book/buildapp/step6/exampleapp.gresource.xml   |    8 +
 examples/book/buildapp/step6/exampleapplication.cc |    2 +
 examples/book/buildapp/step6/exampleapplication.h  |    2 +
 examples/book/buildapp/step6/exampleappprefs.cc    |    2 +
 examples/book/buildapp/step6/exampleappprefs.h     |    2 +
 examples/book/buildapp/step6/exampleappwindow.cc   |  152 +++++++++++++++
 examples/book/buildapp/step6/exampleappwindow.h    |   45 +++++
 examples/book/buildapp/step6/main.cc               |    2 +
 .../step6/org.gtkmm.exampleapp.gschema.xml         |   20 ++
 examples/book/buildapp/step6/prefs.ui              |   71 +++++++
 examples/book/buildapp/step6/window.ui             |   61 ++++++
 45 files changed, 1640 insertions(+), 22 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index b83385b..2bbd6c1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -34,6 +34,9 @@ MM_PKG_CONFIG_SUBST([GTHREAD_CFLAGS], [--cflags-only-other gthread-2.0])
 AC_SUBST(GLIB_COMPILE_RESOURCES, [`$PKG_CONFIG --variable=glib_compile_resources gio-2.0`])
 AC_PROG_CC
 
+# Some example programs use GSettings schema XML files.
+GLIB_GSETTINGS
+
 AC_LANG([C++])
 AC_CHECK_FUNCS([mkfifo])
 
diff --git a/docs/tutorial/C/figures/buildapp_app_menu.png b/docs/tutorial/C/figures/buildapp_app_menu.png
new file mode 100644
index 0000000..e9787cf
Binary files /dev/null and b/docs/tutorial/C/figures/buildapp_app_menu.png differ
diff --git a/docs/tutorial/C/figures/buildapp_pref_dialog.png 
b/docs/tutorial/C/figures/buildapp_pref_dialog.png
new file mode 100644
index 0000000..e7065e3
Binary files /dev/null and b/docs/tutorial/C/figures/buildapp_pref_dialog.png differ
diff --git a/docs/tutorial/C/figures/buildapp_search_bar.png b/docs/tutorial/C/figures/buildapp_search_bar.png
new file mode 100644
index 0000000..444de9e
Binary files /dev/null and b/docs/tutorial/C/figures/buildapp_search_bar.png differ
diff --git a/docs/tutorial/C/index-in.docbook b/docs/tutorial/C/index-in.docbook
index 9d64d4f..da23f69 100644
--- a/docs/tutorial/C/index-in.docbook
+++ b/docs/tutorial/C/index-in.docbook
@@ -7990,11 +7990,211 @@ Our application is beginning to take shape:
 <title>An application menu</title>
 
 <para>
+An application menu is shown by GNOME shell at the top of the screen. It is meant
+to collect infrequently used actions that affect the whole application.
+The application menu is shown either at the top of the screen, or at the top of
+the application's window, depending on which desktop shell you use.
+</para>
+
+<para>
+Just like the application window, we specify our application menu in a ui file,
+and add it as a resource to our binary.
+</para>
+
+<para>
+To associate the app menu with the application, we have to call
+<methodname>Gtk::Application::set_app_menu()</methodname>. Since app menus work by
+activating <classname>Gio::Action</classname>s, we also have to add a suitable set of
+actions to our application.
+</para>
+
+<para>
+Both of these tasks are best done in the <methodname>on_startup()</methodname> default
+signal handler, which is guaranteed to be called once for each primary application instance.
+</para>
+
+<para>
+Our preferences menu item does not do anything yet, but the Quit menu item is fully
+functional. It can also be activated by the usual Ctrl-Q shortcut. The shortcut
+is added with <methodname>Gtk::Application::set_accel_for_action()</methodname>.
+</para>
+
+<para>
+The application menu looks like this:
+</para>
+
+<figure id="figure-buildapp-app-menu">
+  <title>An application menu</title>
+  <screenshot>
+    <graphic format="PNG" fileref="&url_figures_base;buildapp_app_menu.png"/>
+  </screenshot>
+</figure>
+
+<para><ulink url="&url_examples_base;buildapp/step4">Source Code</ulink></para> <!-- Insert 
exampleapp.gresource.xml app_menu.ui -->
+
+</sect1>
+
+<sect1 id="sec-buildapp-pref-dialog">
+<title>A preference dialog</title>
+
+<para>
+A typical application will have some preferences that should be remembered from one run
+to the next. Even for our simple example application, we may want to change the font
+that is used for the content.
+</para>
+
+<para>
+We are going to use <classname>Gio::Settings</classname> to store our preferences.
+<classname>Gio::Settings</classname> requires a schema that describes our settings,
+in our case the <filename>org.gtkmm.exampleapp.gschema.xml</filename> file.
+</para>
+
+<para>
+Before we can make use of this schema in our application, we need to compile it into
+the binary form that <classname>Gio::Settings</classname> expects. GIO provides macros
+to do this in autotools-based projects. See the description of
+<ulink url="https://developer.gnome.org/gio/stable/GSettings.html";>GSettings</ulink>.
+</para>
+
+<para>
+Next, we need to connect our settings to the widgets that they are supposed to control.
+One convenient way to do this is to use <methodname>Gio::Settings::bind()</methodname>
+to bind settings keys to object properties, as we do for the transition setting in
+<classname>ExampleAppWindow</classname>'s constructor.
+<programlisting>
+<![CDATA[m_settings = Gio::Settings::create("org.gtkmm.exampleapp");
+m_settings->bind("transition", m_stack, "transition-type");]]>
+</programlisting>
+</para>
+
+<para>
+The code to connect the font setting is a little more involved, since it corresponds to
+an object property in a <classname>Gtk::TextTag</classname> that we must first create.
+The code is in <methodname>ExampleAppWindow::open_file_view()</methodname>.
+<programlisting>
+<![CDATA[auto tag = buffer->create_tag();
+m_settings->bind("font", tag.operator->(), "font");
+buffer->apply_tag(tag, buffer->begin(), buffer->end());]]>
+</programlisting>
+</para>
+
+<para>
+At this point, the application will already react if you change one of the settings,
+e.g. using the <command>gsettings</command> commandline tool. Of course, we expect
+the application to provide a preference dialog for these. So lets do that now.
+Our preference dialog will be a subclass of <classname>Gtk::Dialog</classname>, and
+we'll use the same techniques that we've already seen in <classname>ExampleAppWindow</classname>:
+a <classname>Gtk::Builder</classname> ui file and settings bindings.
+</para>
+
+<para>
+When we've created the <filename>prefs.ui</filename> file and the <classname>ExampleAppPrefs</classname>
+class, we revisit the <methodname>ExampleApplication::on_action_preferences()</methodname>
+method in our application class, and make it open a new preference dialog.
+<programlisting>
+<![CDATA[auto prefs_dialog = ExampleAppPrefs::create(*get_active_window());
+prefs_dialog->present();]]>
+</programlisting>
+</para>
+
+<para>
+After all this work, our application can now show a preference dialog like this:
+</para>
+
+<figure id="figure-buildapp-pref-dialog">
+  <title>An preference dialog</title>
+  <screenshot>
+    <graphic format="PNG" fileref="&url_figures_base;buildapp_pref_dialog.png"/>
+  </screenshot>
+</figure>
+
+<para><ulink url="&url_examples_base;buildapp/step5">Source Code</ulink></para> <!-- Insert 
exampleapp.gresource.xml prefs.ui org.gtkmm.exampleapp.gschema.xml -->
+
+</sect1>
+
+<sect1 id="sec-buildapp-search-bar">
+<title>Adding a search bar</title>
+
+<para>
+We continue to flesh out the functionality of our application. For now, we add search.
+&gtkmm; supports this with <classname>Gtk::SearchEntry</classname> and <classname>Gtk::SearchBar</classname>.
+The search bar is a widget that can slide in from the top to present a search entry.
+</para>
+
+<para>
+We add a toggle button to the header bar, which can be used to slide out the search bar
+below the header bar. The new widgets are added in the <filename>window.ui</filename> file.
+</para>
+
+<para>
+Implementing the search needs quite a few code changes that we are not going to completely
+go over here. The central piece of the search implementation is a signal handler that
+listens for text changes in the search entry, shown here without error handling.
+<programlisting>
+<![CDATA[void ExampleAppWindow::on_search_text_changed()
+{
+  const auto text = m_searchentry->get_text();
+  auto tab = dynamic_cast<Gtk::ScrolledWindow*>(m_stack->get_visible_child());
+  auto view = dynamic_cast<Gtk::TextView*>(tab->get_child());
+
+  // 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);
+  }
+}]]>
+</programlisting>
+</para>
+
+<para>
+With the search bar, our application now looks like this:
+</para>
+
+<figure id="figure-buildapp-search-bar">
+  <title>Adding a search bar</title>
+  <screenshot>
+    <graphic format="PNG" fileref="&url_figures_base;buildapp_search_bar.png"/>
+  </screenshot>
+</figure>
+
+<para><ulink url="&url_examples_base;buildapp/step6">Source Code</ulink></para> <!-- Insert window.ui -->
+
+</sect1>
+
+<sect1 id="sec-buildapp-side-bar">
+<title>Adding a side bar</title>
+
+<para>
+To be continued.
+</para>
+
+</sect1>
+
+<!--
+<sect1 id="sec-buildapp-properties">
+<title>Properties</title>
+
+<para>
 To be continued.
 </para>
 
 </sect1>
 
+<sect1 id="sec-buildapp-header-bar">
+<title>Header bar</title>
+
+<para>
+To be continued.
+</para>
+
+</sect1>
+-->
+
 </chapter>
 
 <chapter id="chapter-contributing">
diff --git a/docs/tutorial/Makefile.am b/docs/tutorial/Makefile.am
index f3a94a2..0aec9b7 100644
--- a/docs/tutorial/Makefile.am
+++ b/docs/tutorial/Makefile.am
@@ -30,8 +30,11 @@ HELP_MEDIA =                                 \
        figures/box_packing1.png                \
        figures/box_packing2.png                \
        figures/box_packing3.png                \
+       figures/buildapp_app_menu.png \
        figures/buildapp_opening_files.png \
        figures/buildapp_populating_window.png \
+       figures/buildapp_pref_dialog.png \
+       figures/buildapp_search_bar.png \
        figures/buildapp_trivial_app.png \
        figures/buttonbox.png                   \
        figures/buttons.png                     \
diff --git a/examples/Makefile.am b/examples/Makefile.am
index 8496dac..a34dc70 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -294,8 +294,8 @@ book_custom_custom_widget_example_SOURCES =         \
        book/custom/custom_widget/mywidget.cc           \
        book/custom/custom_widget/mywidget.h
 
+nodist_book_dialogs_aboutdialog_example_SOURCES = book/dialogs/aboutdialog/resources.c
 book_dialogs_aboutdialog_example_SOURCES =             \
-       book/dialogs/aboutdialog/resources.c \
        book/dialogs/aboutdialog/examplewindow.cc       \
        book/dialogs/aboutdialog/examplewindow.h        \
        book/dialogs/aboutdialog/main.cc
@@ -345,8 +345,8 @@ book_drawingarea_curve_drawingareacurve_SOURCES =   \
        book/drawingarea/curve/myarea.cc                \
        book/drawingarea/curve/myarea.h
 
+nodist_book_drawingarea_image_drawingareaimage_SOURCES = book/drawingarea/image/resources.c
 book_drawingarea_image_drawingareaimage_SOURCES =      \
-       book/drawingarea/image/resources.c \
        book/drawingarea/image/main.cc                  \
        book/drawingarea/image/myarea.cc                \
        book/drawingarea/image/myarea.h
@@ -524,8 +524,8 @@ book_menus_popup_popup_SOURCES =            \
        book/menus/popup/examplewindow.h        \
        book/menus/popup/main.cc
 
+nodist_book_menus_and_toolbars_example_SOURCES = book/menus_and_toolbars/resources.c
 book_menus_and_toolbars_example_SOURCES =      \
-       book/menus_and_toolbars/resources.c \
        book/menus_and_toolbars/examplewindow.cc\
        book/menus_and_toolbars/examplewindow.h \
        book/menus_and_toolbars/main.cc
@@ -763,7 +763,7 @@ others_treemodelcustom_example_SOURCES =            \
 #others_window_wheelbarrow_SOURCES = \
 #      others/window/wheelbarrow.cc
 
-MAINTAINERCLEANFILES = \
+DISTCLEANFILES = \
   book/dialogs/aboutdialog/resources.c \
   book/drawingarea/image/resources.c \
   book/menus_and_toolbars/resources.c
diff --git a/examples/book/buildapp/Makefile.am b/examples/book/buildapp/Makefile.am
index e4deeda..1627341 100644
--- a/examples/book/buildapp/Makefile.am
+++ b/examples/book/buildapp/Makefile.am
@@ -9,7 +9,12 @@ LDADD       = $(GTKMM_LIBS)
 check_PROGRAMS = \
   step1/exampleapp \
   step2/exampleapp \
-  step3/exampleapp
+  step3/exampleapp \
+  step4/exampleapp \
+  step5/exampleapp \
+  step6/exampleapp
+
+check_DATA = $(GSCHEMAS_COMPILED)
 
 dist_noinst_DATA = \
   README \
@@ -19,7 +24,14 @@ dist_noinst_DATA = \
   $(STEP2_RESOURCES) \
   step2/Makefile.example \
   $(STEP3_RESOURCES) \
-  step3/Makefile.example
+  step3/Makefile.example \
+  $(STEP4_RESOURCES) \
+  step4/Makefile.example \
+  $(STEP5_RESOURCES) \
+  step5/Makefile.example \
+  $(STEP6_RESOURCES) \
+  step6/Makefile.example \
+  $(gsettings_SCHEMAS)
 
 step1_exampleapp_SOURCES = \
   step1/exampleapplication.cc \
@@ -28,25 +40,76 @@ step1_exampleapp_SOURCES = \
   step1/exampleappwindow.h \
   step1/main.cc
 
+nodist_step2_exampleapp_SOURCES = step2/resources.c
 step2_exampleapp_SOURCES = \
-  step2/resources.c \
   step2/exampleapplication.cc \
   step2/exampleapplication.h \
   step2/exampleappwindow.cc \
   step2/exampleappwindow.h \
   step2/main.cc
 
+nodist_step3_exampleapp_SOURCES = step3/resources.c
 step3_exampleapp_SOURCES = \
-  step3/resources.c \
   step3/exampleapplication.cc \
   step3/exampleapplication.h \
   step3/exampleappwindow.cc \
   step3/exampleappwindow.h \
   step3/main.cc
 
-MAINTAINERCLEANFILES = \
+nodist_step4_exampleapp_SOURCES = step4/resources.c
+step4_exampleapp_SOURCES = \
+  step4/exampleapplication.cc \
+  step4/exampleapplication.h \
+  step4/exampleappwindow.cc \
+  step4/exampleappwindow.h \
+  step4/main.cc
+
+nodist_step5_exampleapp_SOURCES = step5/resources.c
+step5_exampleapp_SOURCES = \
+  step5/exampleapplication.cc \
+  step5/exampleapplication.h \
+  step5/exampleappwindow.cc \
+  step5/exampleappwindow.h \
+  step5/exampleappprefs.cc \
+  step5/exampleappprefs.h \
+  step5/main.cc
+
+nodist_step6_exampleapp_SOURCES = step6/resources.c
+step6_exampleapp_SOURCES = \
+  step6/exampleapplication.cc \
+  step6/exampleapplication.h \
+  step6/exampleappwindow.cc \
+  step6/exampleappwindow.h \
+  step6/exampleappprefs.cc \
+  step6/exampleappprefs.h \
+  step6/main.cc
+
+gsettings_SCHEMAS = \
+  step5/org.gtkmm.exampleapp.gschema.xml \
+  step6/org.gtkmm.exampleapp.gschema.xml
+
+GSCHEMAS_COMPILED = \
+  step5/gschemas.compiled \
+  step6/gschemas.compiled
+
+## $(<D) is the source directory, $(@D) 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)
+
+## In most programs you would use @GSETTINGS_RULES@ instead of some of the
+## schema rules in this Makefile.am, but gtkmm-documentation is different.
+## The GSettings schema files belong to the example programs. They shall not be
+## installed. gschemas.compiled files shall be created only by make check.
+
+DISTCLEANFILES = \
   step2/resources.c \
-  step3/resources.c
+  step3/resources.c \
+  step4/resources.c \
+  step5/resources.c \
+  step6/resources.c \
+  $(GSCHEMAS_COMPILED)
 
 STEP2_RESOURCES = \
   step2/exampleapp.gresource.xml \
@@ -62,3 +125,29 @@ STEP3_RESOURCES = \
 step3/resources.c: $(STEP3_RESOURCES)
        $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(dir $<) --generate-source $<
 
+STEP4_RESOURCES = \
+  step4/exampleapp.gresource.xml \
+  step4/window.ui \
+  step4/app_menu.ui
+
+step4/resources.c: $(STEP4_RESOURCES)
+       $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(dir $<) --generate-source $<
+
+STEP5_RESOURCES = \
+  step5/exampleapp.gresource.xml \
+  step5/window.ui \
+  step5/app_menu.ui \
+  step5/prefs.ui
+
+step5/resources.c: $(STEP5_RESOURCES)
+       $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(dir $<) --generate-source $<
+
+STEP6_RESOURCES = \
+  step6/exampleapp.gresource.xml \
+  step6/window.ui \
+  step6/app_menu.ui \
+  step6/prefs.ui
+
+step6/resources.c: $(STEP6_RESOURCES)
+       $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(dir $<) --generate-source $<
+
diff --git a/examples/book/buildapp/step1/Makefile.example b/examples/book/buildapp/step1/Makefile.example
index 8135177..893f576 100644
--- a/examples/book/buildapp/step1/Makefile.example
+++ b/examples/book/buildapp/step1/Makefile.example
@@ -8,11 +8,12 @@ LIBS = `pkg-config --libs gtkmm-3.0`
 
 all: $(PROGRAM)
 
-include $(CXXFILES:%.cc=$(DEPDIR)/%.Po)
+-include $(OBJS:%.o=$(DEPDIR)/%.Po)
 
 %.o: %.cc
+       @mkdir -p $(DEPDIR)
        $(CXX) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $(CXXFLAGS) $<
-       mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+       @mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
 
 $(PROGRAM): $(OBJS)
        $(CXX) -o $(PROGRAM) $(OBJS) $(LIBS)
@@ -22,7 +23,7 @@ clean:
        rm -f $(PROGRAM)
 
 install-desktop-file:
-       mkdir -p ~/.local/share/applications
+       @mkdir -p ~/.local/share/applications
        sed -e "s#@bindir@#$$PWD#" exampleapp.desktop > ~/.local/share/applications/$(PROGRAM).desktop
-       mkdir -p ~/.local/share/icons/hicolor/48x48/apps
+       @mkdir -p ~/.local/share/icons/hicolor/48x48/apps
        cp exampleapp.png ~/.local/share/icons/hicolor/48x48/apps
diff --git a/examples/book/buildapp/step2/Makefile.example b/examples/book/buildapp/step2/Makefile.example
index c000213..7c85510 100644
--- a/examples/book/buildapp/step2/Makefile.example
+++ b/examples/book/buildapp/step2/Makefile.example
@@ -8,23 +8,24 @@ DEPDIR = .deps
 CXXFLAGS = `pkg-config --cflags gtkmm-3.0` -std=c++11 -Wall -g
 CFLAGS = `pkg-config --cflags gtk+-3.0`
 LIBS = `pkg-config --libs gtkmm-3.0`
-GLIB_COMPILE_RESOURCES = `pkg-config --variable=glib_compile_resources gio-2.0`
+GLIB_COMPILE_RESOURCES = $(shell pkg-config --variable=glib_compile_resources gio-2.0)
 
 all: $(PROGRAM)
 
-include $(CXXFILES:%.cc=$(DEPDIR)/%.Po)
-include $(BUILT_SRC:%.c=$(DEPDIR)/%.Po)
+-include $(OBJS:%.o=$(DEPDIR)/%.Po)
 
 resources.c: exampleapp.gresource.xml window.ui
-       $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(dir $<) --generate-source $<
+       $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=. --generate-source $<
 
 %.o: %.c
+       @mkdir -p $(DEPDIR)
        $(CC) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $(CFLAGS) $<
-       mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+       @mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
 
 %.o: %.cc
+       @mkdir -p $(DEPDIR)
        $(CXX) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $(CXXFLAGS) $<
-       mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+       @mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
 
 $(PROGRAM): $(OBJS)
        $(CXX) -o $(PROGRAM) $(OBJS) $(LIBS)
@@ -35,7 +36,7 @@ clean:
        rm -f $(PROGRAM)
 
 install-desktop-file:
-       mkdir -p ~/.local/share/applications
+       @mkdir -p ~/.local/share/applications
        sed -e "s#@bindir@#$$PWD#" ../step1/exampleapp.desktop > 
~/.local/share/applications/$(PROGRAM).desktop
-       mkdir -p ~/.local/share/icons/hicolor/48x48/apps
+       @mkdir -p ~/.local/share/icons/hicolor/48x48/apps
        cp ../step1/exampleapp.png ~/.local/share/icons/hicolor/48x48/apps
diff --git a/examples/book/buildapp/step4/Makefile.example b/examples/book/buildapp/step4/Makefile.example
new file mode 100644
index 0000000..4cad3dc
--- /dev/null
+++ b/examples/book/buildapp/step4/Makefile.example
@@ -0,0 +1,42 @@
+CC ?= gcc
+CXX ?= g++
+PROGRAM = exampleapp
+CXXFILES = main.cc exampleappwindow.cc exampleapplication.cc
+BUILT_SRC = resources.c
+OBJS = $(BUILT_SRC:.c=.o) $(CXXFILES:.cc=.o)
+DEPDIR = .deps
+CXXFLAGS = `pkg-config --cflags gtkmm-3.0` -std=c++11 -Wall -g
+CFLAGS = `pkg-config --cflags gtk+-3.0`
+LIBS = `pkg-config --libs gtkmm-3.0`
+GLIB_COMPILE_RESOURCES = $(shell pkg-config --variable=glib_compile_resources gio-2.0)
+
+all: $(PROGRAM)
+
+-include $(OBJS:%.o=$(DEPDIR)/%.Po)
+
+resources.c: exampleapp.gresource.xml window.ui app_menu.ui
+       $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=. --generate-source $<
+
+%.o: %.c
+       @mkdir -p $(DEPDIR)
+       $(CC) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $(CFLAGS) $<
+       @mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+
+%.o: %.cc
+       @mkdir -p $(DEPDIR)
+       $(CXX) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $(CXXFLAGS) $<
+       @mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+
+$(PROGRAM): $(OBJS)
+       $(CXX) -o $(PROGRAM) $(OBJS) $(LIBS)
+
+clean:
+       rm -f $(BUILT_SRC)
+       rm -f $(OBJS)
+       rm -f $(PROGRAM)
+
+install-desktop-file:
+       @mkdir -p ~/.local/share/applications
+       sed -e "s#@bindir@#$$PWD#" ../step1/exampleapp.desktop > 
~/.local/share/applications/$(PROGRAM).desktop
+       @mkdir -p ~/.local/share/icons/hicolor/48x48/apps
+       cp ../step1/exampleapp.png ~/.local/share/icons/hicolor/48x48/apps
diff --git a/examples/book/buildapp/step4/app_menu.ui b/examples/book/buildapp/step4/app_menu.ui
new file mode 100644
index 0000000..e1e131c
--- /dev/null
+++ b/examples/book/buildapp/step4/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/step4/exampleapp.gresource.xml 
b/examples/book/buildapp/step4/exampleapp.gresource.xml
new file mode 100644
index 0000000..a4c7c2d
--- /dev/null
+++ b/examples/book/buildapp/step4/exampleapp.gresource.xml
@@ -0,0 +1,7 @@
+<?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>
+  </gresource>
+</gresources>
diff --git a/examples/book/buildapp/step4/exampleapplication.cc 
b/examples/book/buildapp/step4/exampleapplication.cc
new file mode 100644
index 0000000..0bebedd
--- /dev/null
+++ b/examples/book/buildapp/step4/exampleapplication.cc
@@ -0,0 +1,159 @@
+/* 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 "exampleapplication.h"
+#include "exampleappwindow.h"
+#include <iostream>
+#include <exception>
+
+ExampleApplication::ExampleApplication()
+: Gtk::Application("org.gtkmm.examples.application", Gio::APPLICATION_HANDLES_OPEN)
+{
+}
+
+Glib::RefPtr<ExampleApplication> ExampleApplication::create()
+{
+  return Glib::RefPtr<ExampleApplication>(new ExampleApplication());
+}
+
+ExampleAppWindow* ExampleApplication::create_appwindow()
+{
+  auto appwindow = ExampleAppWindow::create();
+
+  // Make sure that the application runs for as long this window is still open.
+  add_window(*appwindow);
+
+  // Gtk::Application::add_window() connects a signal handler to the window's
+  // signal_hide(). That handler removes the window from the application.
+  // If it's the last window to be removed, the application stops running.
+  // Gtk::Window::set_application() does not connect a signal handler, but is
+  // otherwise equivalent to Gtk::Application::add_window().
+
+  // Delete the window when it is hidden.
+  appwindow->signal_hide().connect(sigc::bind<Gtk::Window*>(sigc::mem_fun(*this,
+    &ExampleApplication::on_hide_window), appwindow));
+
+  return appwindow;
+}
+
+void ExampleApplication::on_startup()
+{
+  // Call the base class's implementation.
+  Gtk::Application::on_startup();
+
+  // Add actions and keyboard accelerators for the application menu.
+  add_action("preferences", sigc::mem_fun(*this, &ExampleApplication::on_action_preferences));
+  add_action("quit", sigc::mem_fun(*this, &ExampleApplication::on_action_quit));
+  set_accel_for_action("app.quit", "<Ctrl>Q");
+
+  auto refBuilder = Gtk::Builder::create();
+  try
+  {
+    refBuilder->add_from_resource("/org/gtkmm/exampleapp/app_menu.ui");
+  }
+  catch (const Glib::Error& ex)
+  {
+    std::cerr << "ExampleApplication::on_startup(): " << ex.what() << std::endl;
+    return;
+  }
+
+  auto object = refBuilder->get_object("appmenu");
+  auto app_menu = Glib::RefPtr<Gio::MenuModel>::cast_dynamic(object);
+  if (app_menu)
+    set_app_menu(app_menu);
+  else
+    std::cerr << "ExampleApplication::on_startup(): No \"appmenu\" object in app_menu.ui"
+              << std::endl;
+}
+
+void ExampleApplication::on_activate()
+{
+  try
+  {
+    // The application has been started, so let's show a window.
+    auto appwindow = create_appwindow();
+    appwindow->present();
+  }
+  // If create_appwindow() throws an exception (perhaps from Gtk::Builder),
+  // no window has been created, no window has been added to the application,
+  // and therefore the application will stop running.
+  catch (const Glib::Error& ex)
+  {
+    std::cerr << "ExampleApplication::on_activate(): " << ex.what() << std::endl;
+  }
+  catch (const std::exception& ex)
+  {
+    std::cerr << "ExampleApplication::on_activate(): " << ex.what() << std::endl;
+  }
+}
+
+void ExampleApplication::on_open(const Gio::Application::type_vec_files& files,
+  const Glib::ustring& /* hint */)
+{
+  // The application has been asked to open some files,
+  // so let's open a new view for each one.
+  ExampleAppWindow* appwindow = nullptr;
+  auto windows = get_windows();
+  if (windows.size() > 0)
+    appwindow = dynamic_cast<ExampleAppWindow*>(windows[0]);
+
+  try
+  {
+    if (!appwindow)
+      appwindow = create_appwindow();
+
+    for (const auto& file : files)
+      appwindow->open_file_view(file);
+
+    appwindow->present();
+  }
+  catch (const Glib::Error& ex)
+  {
+    std::cerr << "ExampleApplication::on_open(): " << ex.what() << std::endl;
+  }
+  catch (const std::exception& ex)
+  {
+    std::cerr << "ExampleApplication::on_open(): " << ex.what() << std::endl;
+  }
+}
+
+void ExampleApplication::on_hide_window(Gtk::Window* window)
+{
+  delete window;
+}
+
+void ExampleApplication::on_action_preferences()
+{
+
+}
+
+void ExampleApplication::on_action_quit()
+{
+  // Gio::Application::quit() will make Gio::Application::run() return,
+  // but it's a crude way of ending the program. The window is not removed
+  // from the application. Neither the window's nor the application's
+  // destructors will be called, because there will be remaining reference
+  // counts in both of them. If we want the destructors to be called, we
+  // must remove the window from the application. One way of doing this
+  // is to hide the window. See comment in create_appwindow().
+  auto windows = get_windows();
+  for (auto window : windows)
+    window->hide();
+
+  // Not really necessary, when Gtk::Widget::hide() is called, unless
+  // Gio::Application::hold() has been called without a corresponding call
+  // to Gio::Application::release().
+  quit();
+}
diff --git a/examples/book/buildapp/step4/exampleapplication.h 
b/examples/book/buildapp/step4/exampleapplication.h
new file mode 100644
index 0000000..41c6e31
--- /dev/null
+++ b/examples/book/buildapp/step4/exampleapplication.h
@@ -0,0 +1,45 @@
+/* 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_EXAMPLEAPPLICATION_H
+#define GTKMM_EXAMPLEAPPLICATION_H
+
+#include <gtkmm.h>
+
+class ExampleAppWindow;
+
+class ExampleApplication: public Gtk::Application
+{
+protected:
+  ExampleApplication();
+
+public:
+  static Glib::RefPtr<ExampleApplication> create();
+
+protected:
+  // Override default signal handlers:
+  void on_startup() override;
+  void on_activate() override;
+  void on_open(const Gio::Application::type_vec_files& files,
+    const Glib::ustring& hint) override;
+
+private:
+  ExampleAppWindow* create_appwindow();
+  void on_hide_window(Gtk::Window* window);
+  void on_action_preferences();
+  void on_action_quit();
+};
+
+#endif /* GTKMM_EXAMPLEAPPLICATION_H */
diff --git a/examples/book/buildapp/step4/exampleappwindow.cc 
b/examples/book/buildapp/step4/exampleappwindow.cc
new file mode 100644
index 0000000..2091bf6
--- /dev/null
+++ b/examples/book/buildapp/step4/exampleappwindow.cc
@@ -0,0 +1,2 @@
+#include "../step3/exampleappwindow.cc"
+// Equal to the corresponding file in step3
diff --git a/examples/book/buildapp/step4/exampleappwindow.h b/examples/book/buildapp/step4/exampleappwindow.h
new file mode 100644
index 0000000..d8bc1ee
--- /dev/null
+++ b/examples/book/buildapp/step4/exampleappwindow.h
@@ -0,0 +1,2 @@
+#include "../step3/exampleappwindow.h"
+// Equal to the corresponding file in step3
diff --git a/examples/book/buildapp/step4/main.cc b/examples/book/buildapp/step4/main.cc
new file mode 100644
index 0000000..e55616e
--- /dev/null
+++ b/examples/book/buildapp/step4/main.cc
@@ -0,0 +1,2 @@
+#include "../step1/main.cc"
+// Equal to the corresponding file in step1
diff --git a/examples/book/buildapp/step4/window.ui b/examples/book/buildapp/step4/window.ui
new file mode 100644
index 0000000..5f8e5ba
--- /dev/null
+++ b/examples/book/buildapp/step4/window.ui
@@ -0,0 +1,31 @@
+<?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>
+          </object>
+        </child>
+        <child>
+          <object class="GtkStack" id="stack">
+            <property name="visible">True</property>
+          </object>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/examples/book/buildapp/step5/Makefile.example b/examples/book/buildapp/step5/Makefile.example
new file mode 100644
index 0000000..6a1b0d2
--- /dev/null
+++ b/examples/book/buildapp/step5/Makefile.example
@@ -0,0 +1,55 @@
+CC ?= gcc
+CXX ?= g++
+PROGRAM = exampleapp
+CXXFILES = main.cc exampleappwindow.cc exampleapplication.cc exampleappprefs.cc
+BUILT_SRC = resources.c
+OBJS = $(BUILT_SRC:.c=.o) $(CXXFILES:.cc=.o)
+DEPDIR = .deps
+CXXFLAGS = `pkg-config --cflags gtkmm-3.0` -std=c++11 -Wall -g
+CFLAGS = `pkg-config --cflags gtk+-3.0`
+LIBS = `pkg-config --libs gtkmm-3.0`
+GLIB_COMPILE_RESOURCES = $(shell pkg-config --variable=glib_compile_resources gio-2.0)
+GLIB_COMPILE_SCHEMAS   = $(shell pkg-config --variable=glib_compile_schemas gio-2.0)
+
+all: $(PROGRAM) gschemas.compiled
+
+-include $(OBJS:%.o=$(DEPDIR)/%.Po)
+
+gschemas.compiled: org.gtkmm.exampleapp.gschema.xml
+       $(GLIB_COMPILE_SCHEMAS) --strict --dry-run --schema-file=$<
+       $(GLIB_COMPILE_SCHEMAS) .
+
+resources.c: exampleapp.gresource.xml $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir=. 
--generate-dependencies exampleapp.gresource.xml)
+       $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=. --generate-source $<
+
+%.o: %.c
+       @mkdir -p $(DEPDIR)
+       $(CC) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $(CFLAGS) $<
+       @mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+
+%.o: %.cc
+       @mkdir -p $(DEPDIR)
+       $(CXX) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $(CXXFLAGS) $<
+       @mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+
+$(PROGRAM): $(OBJS)
+       $(CXX) -o $(PROGRAM) $(OBJS) $(LIBS)
+
+clean:
+       rm -f gschemas.compiled
+       rm -f $(BUILT_SRC)
+       rm -f $(OBJS)
+       rm -f $(PROGRAM)
+
+install-desktop-file:
+       @mkdir -p ~/.local/share/applications
+       sed -e "s#@bindir@#$$PWD#" ../step1/exampleapp.desktop > 
~/.local/share/applications/$(PROGRAM).desktop
+       @mkdir -p ~/.local/share/icons/hicolor/48x48/apps
+       cp ../step1/exampleapp.png ~/.local/share/icons/hicolor/48x48/apps
+
+install-gschema-file:
+       if test -n "$(GSETTINGS_SCHEMA_DIR)" -a "$(GSETTINGS_SCHEMA_DIR)" != .; then \
+               mkdir -p "$(GSETTINGS_SCHEMA_DIR)"; \
+               cp org.gtkmm.exampleapp.gschema.xml "$(GSETTINGS_SCHEMA_DIR)"; \
+               $(GLIB_COMPILE_SCHEMAS) "$(GSETTINGS_SCHEMA_DIR)"; \
+       fi
diff --git a/examples/book/buildapp/step5/app_menu.ui b/examples/book/buildapp/step5/app_menu.ui
new file mode 100644
index 0000000..e1e131c
--- /dev/null
+++ b/examples/book/buildapp/step5/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/step5/exampleapp.gresource.xml 
b/examples/book/buildapp/step5/exampleapp.gresource.xml
new file mode 100644
index 0000000..579ab8c
--- /dev/null
+++ b/examples/book/buildapp/step5/exampleapp.gresource.xml
@@ -0,0 +1,8 @@
+<?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">prefs.ui</file>
+  </gresource>
+</gresources>
diff --git a/examples/book/buildapp/step5/exampleapplication.cc 
b/examples/book/buildapp/step5/exampleapplication.cc
new file mode 100644
index 0000000..1cdfc56
--- /dev/null
+++ b/examples/book/buildapp/step5/exampleapplication.cc
@@ -0,0 +1,176 @@
+/* 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 "exampleapplication.h"
+#include "exampleappwindow.h"
+#include "exampleappprefs.h"
+#include <iostream>
+#include <exception>
+
+ExampleApplication::ExampleApplication()
+: Gtk::Application("org.gtkmm.examples.application", Gio::APPLICATION_HANDLES_OPEN)
+{
+}
+
+Glib::RefPtr<ExampleApplication> ExampleApplication::create()
+{
+  return Glib::RefPtr<ExampleApplication>(new ExampleApplication());
+}
+
+ExampleAppWindow* ExampleApplication::create_appwindow()
+{
+  auto appwindow = ExampleAppWindow::create();
+
+  // Make sure that the application runs for as long this window is still open.
+  add_window(*appwindow);
+
+  // Gtk::Application::add_window() connects a signal handler to the window's
+  // signal_hide(). That handler removes the window from the application.
+  // If it's the last window to be removed, the application stops running.
+  // Gtk::Window::set_application() does not connect a signal handler, but is
+  // otherwise equivalent to Gtk::Application::add_window().
+
+  // Delete the window when it is hidden.
+  appwindow->signal_hide().connect(sigc::bind<Gtk::Window*>(sigc::mem_fun(*this,
+    &ExampleApplication::on_hide_window), appwindow));
+
+  return appwindow;
+}
+
+void ExampleApplication::on_startup()
+{
+  // Call the base class's implementation.
+  Gtk::Application::on_startup();
+
+  // Add actions and keyboard accelerators for the application menu.
+  add_action("preferences", sigc::mem_fun(*this, &ExampleApplication::on_action_preferences));
+  add_action("quit", sigc::mem_fun(*this, &ExampleApplication::on_action_quit));
+  set_accel_for_action("app.quit", "<Ctrl>Q");
+
+  auto refBuilder = Gtk::Builder::create();
+  try
+  {
+    refBuilder->add_from_resource("/org/gtkmm/exampleapp/app_menu.ui");
+  }
+  catch (const Glib::Error& ex)
+  {
+    std::cerr << "ExampleApplication::on_startup(): " << ex.what() << std::endl;
+    return;
+  }
+
+  auto object = refBuilder->get_object("appmenu");
+  auto app_menu = Glib::RefPtr<Gio::MenuModel>::cast_dynamic(object);
+  if (app_menu)
+    set_app_menu(app_menu);
+  else
+    std::cerr << "ExampleApplication::on_startup(): No \"appmenu\" object in app_menu.ui"
+              << std::endl;
+}
+
+void ExampleApplication::on_activate()
+{
+  try
+  {
+    // The application has been started, so let's show a window.
+    auto appwindow = create_appwindow();
+    appwindow->present();
+  }
+  // If create_appwindow() throws an exception (perhaps from Gtk::Builder),
+  // no window has been created, no window has been added to the application,
+  // and therefore the application will stop running.
+  catch (const Glib::Error& ex)
+  {
+    std::cerr << "ExampleApplication::on_activate(): " << ex.what() << std::endl;
+  }
+  catch (const std::exception& ex)
+  {
+    std::cerr << "ExampleApplication::on_activate(): " << ex.what() << std::endl;
+  }
+}
+
+void ExampleApplication::on_open(const Gio::Application::type_vec_files& files,
+  const Glib::ustring& /* hint */)
+{
+  // The application has been asked to open some files,
+  // so let's open a new view for each one.
+  ExampleAppWindow* appwindow = nullptr;
+  auto windows = get_windows();
+  if (windows.size() > 0)
+    appwindow = dynamic_cast<ExampleAppWindow*>(windows[0]);
+
+  try
+  {
+    if (!appwindow)
+      appwindow = create_appwindow();
+
+    for (const auto& file : files)
+      appwindow->open_file_view(file);
+
+    appwindow->present();
+  }
+  catch (const Glib::Error& ex)
+  {
+    std::cerr << "ExampleApplication::on_open(): " << ex.what() << std::endl;
+  }
+  catch (const std::exception& ex)
+  {
+    std::cerr << "ExampleApplication::on_open(): " << ex.what() << std::endl;
+  }
+}
+
+void ExampleApplication::on_hide_window(Gtk::Window* window)
+{
+  delete window;
+}
+
+void ExampleApplication::on_action_preferences()
+{
+  try
+  {
+    auto prefs_dialog = ExampleAppPrefs::create(*get_active_window());
+    prefs_dialog->present();
+
+    // Delete the dialog when it is hidden.
+    prefs_dialog->signal_hide().connect(sigc::bind<Gtk::Window*>(sigc::mem_fun(*this,
+      &ExampleApplication::on_hide_window), prefs_dialog));
+  }
+  catch (const Glib::Error& ex)
+  {
+    std::cerr << "ExampleApplication::on_action_preferences(): " << ex.what() << std::endl;
+  }
+  catch (const std::exception& ex)
+  {
+    std::cerr << "ExampleApplication::on_action_preferences(): " << ex.what() << std::endl;
+  }
+}
+
+void ExampleApplication::on_action_quit()
+{
+  // Gio::Application::quit() will make Gio::Application::run() return,
+  // but it's a crude way of ending the program. The window is not removed
+  // from the application. Neither the window's nor the application's
+  // destructors will be called, because there will be remaining reference
+  // counts in both of them. If we want the destructors to be called, we
+  // must remove the window from the application. One way of doing this
+  // is to hide the window. See comment in create_appwindow().
+  auto windows = get_windows();
+  for (auto window : windows)
+    window->hide();
+
+  // Not really necessary, when Gtk::Widget::hide() is called, unless
+  // Gio::Application::hold() has been called without a corresponding call
+  // to Gio::Application::release().
+  quit();
+}
diff --git a/examples/book/buildapp/step5/exampleapplication.h 
b/examples/book/buildapp/step5/exampleapplication.h
new file mode 100644
index 0000000..fd7b4e8
--- /dev/null
+++ b/examples/book/buildapp/step5/exampleapplication.h
@@ -0,0 +1,2 @@
+#include "../step4/exampleapplication.h"
+// Equal to the corresponding file in step4
diff --git a/examples/book/buildapp/step5/exampleappprefs.cc b/examples/book/buildapp/step5/exampleappprefs.cc
new file mode 100644
index 0000000..8d5a12a
--- /dev/null
+++ b/examples/book/buildapp/step5/exampleappprefs.cc
@@ -0,0 +1,55 @@
+/* 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 "exampleappprefs.h"
+#include "exampleappwindow.h"
+#include <stdexcept>
+
+ExampleAppPrefs::ExampleAppPrefs(BaseObjectType* cobject,
+  const Glib::RefPtr<Gtk::Builder>& refBuilder)
+: Gtk::Dialog(cobject),
+  m_refBuilder(refBuilder),
+  m_settings(),
+  m_font(nullptr),
+  m_transition(nullptr)
+{
+  m_refBuilder->get_widget("font", m_font);
+  if (!m_font)
+    throw std::runtime_error("No \"font\" object in prefs.ui");
+
+  m_refBuilder->get_widget("transition", m_transition);
+  if (!m_transition)
+    throw std::runtime_error("No \"transition\" object in prefs.ui");
+
+  m_settings = Gio::Settings::create("org.gtkmm.exampleapp");
+  m_settings->bind("font", m_font, "font");
+  m_settings->bind("transition", m_transition, "active-id");
+}
+
+//static
+ExampleAppPrefs* ExampleAppPrefs::create(Gtk::Window& parent)
+{
+  // Load the Builder file and instantiate its widgets.
+  auto refBuilder = Gtk::Builder::create_from_resource("/org/gtkmm/exampleapp/prefs.ui");
+
+  ExampleAppPrefs* dialog = nullptr;
+  refBuilder->get_widget_derived("prefs_dialog", dialog);
+  if (!dialog)
+    throw std::runtime_error("No \"prefs_dialog\" object in prefs.ui");
+
+  dialog->set_transient_for(parent);
+
+  return dialog;
+}
diff --git a/examples/book/buildapp/step5/exampleappprefs.h b/examples/book/buildapp/step5/exampleappprefs.h
new file mode 100644
index 0000000..c539aff
--- /dev/null
+++ b/examples/book/buildapp/step5/exampleappprefs.h
@@ -0,0 +1,36 @@
+/* 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_EXAMPLEAPPPREFS_H_
+#define GTKMM_EXAMPLEAPPPREFS_H_
+
+#include <gtkmm.h>
+
+class ExampleAppPrefs : public Gtk::Dialog
+{
+public:
+  ExampleAppPrefs(BaseObjectType* cobject,
+    const Glib::RefPtr<Gtk::Builder>& refBuilder);
+
+  static ExampleAppPrefs* create(Gtk::Window& parent);
+
+protected:
+  Glib::RefPtr<Gtk::Builder> m_refBuilder;
+  Glib::RefPtr<Gio::Settings> m_settings;
+  Gtk::FontButton* m_font;
+  Gtk::ComboBoxText* m_transition;
+};
+
+#endif /* GTKMM_EXAMPLEAPPPREFS_H_ */
diff --git a/examples/book/buildapp/step5/exampleappwindow.cc 
b/examples/book/buildapp/step5/exampleappwindow.cc
new file mode 100644
index 0000000..7818d27
--- /dev/null
+++ b/examples/book/buildapp/step5/exampleappwindow.cc
@@ -0,0 +1,86 @@
+/* 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>
+
+ExampleAppWindow::ExampleAppWindow(BaseObjectType* cobject,
+  const Glib::RefPtr<Gtk::Builder>& refBuilder)
+: Gtk::ApplicationWindow(cobject),
+  m_refBuilder(refBuilder),
+  m_settings(),
+  m_stack(nullptr)
+{
+  m_refBuilder->get_widget("stack", m_stack);
+  if (!m_stack)
+    throw std::runtime_error("No \"stack\" object in window.ui");
+
+  m_settings = Gio::Settings::create("org.gtkmm.exampleapp");
+  m_settings->bind("transition", m_stack, "transition-type");
+}
+
+//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;
+  }
+
+  // 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());
+}
diff --git a/examples/book/buildapp/step5/exampleappwindow.h b/examples/book/buildapp/step5/exampleappwindow.h
new file mode 100644
index 0000000..284d8e6
--- /dev/null
+++ b/examples/book/buildapp/step5/exampleappwindow.h
@@ -0,0 +1,37 @@
+/* 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:
+  Glib::RefPtr<Gtk::Builder> m_refBuilder;
+  Glib::RefPtr<Gio::Settings> m_settings;
+  Gtk::Stack* m_stack;
+};
+
+#endif /* GTKMM_EXAMPLEAPPWINDOW_H */
diff --git a/examples/book/buildapp/step5/main.cc b/examples/book/buildapp/step5/main.cc
new file mode 100644
index 0000000..9c65e81
--- /dev/null
+++ b/examples/book/buildapp/step5/main.cc
@@ -0,0 +1,31 @@
+/* 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 "exampleapplication.h"
+
+int main(int argc, char* argv[])
+{
+  // Since this example is running uninstalled, we have to help it find its
+  // schema. This is *not* necessary in a properly installed application.
+  Glib::setenv ("GSETTINGS_SCHEMA_DIR", ".", false);
+
+  auto application = ExampleApplication::create();
+
+  // Start the application, showing the initial window,
+  // and opening extra views for any files that it is asked to open,
+  // for instance as a command-line parameter.
+  // run() will return when the last window has been closed.
+  return application->run(argc, argv);
+}
diff --git a/examples/book/buildapp/step5/org.gtkmm.exampleapp.gschema.xml 
b/examples/book/buildapp/step5/org.gtkmm.exampleapp.gschema.xml
new file mode 100644
index 0000000..fa2dbce
--- /dev/null
+++ b/examples/book/buildapp/step5/org.gtkmm.exampleapp.gschema.xml
@@ -0,0 +1,20 @@
+<?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>
+  </schema>
+</schemalist>
diff --git a/examples/book/buildapp/step5/prefs.ui b/examples/book/buildapp/step5/prefs.ui
new file mode 100644
index 0000000..1089e95
--- /dev/null
+++ b/examples/book/buildapp/step5/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/step5/window.ui b/examples/book/buildapp/step5/window.ui
new file mode 100644
index 0000000..5f8e5ba
--- /dev/null
+++ b/examples/book/buildapp/step5/window.ui
@@ -0,0 +1,31 @@
+<?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>
+          </object>
+        </child>
+        <child>
+          <object class="GtkStack" id="stack">
+            <property name="visible">True</property>
+          </object>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/examples/book/buildapp/step6/Makefile.example b/examples/book/buildapp/step6/Makefile.example
new file mode 100644
index 0000000..997766b
--- /dev/null
+++ b/examples/book/buildapp/step6/Makefile.example
@@ -0,0 +1,2 @@
+include ../step5/Makefile.example
+# Equal to the corresponding file in step5
diff --git a/examples/book/buildapp/step6/app_menu.ui b/examples/book/buildapp/step6/app_menu.ui
new file mode 100644
index 0000000..e1e131c
--- /dev/null
+++ b/examples/book/buildapp/step6/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/step6/exampleapp.gresource.xml 
b/examples/book/buildapp/step6/exampleapp.gresource.xml
new file mode 100644
index 0000000..579ab8c
--- /dev/null
+++ b/examples/book/buildapp/step6/exampleapp.gresource.xml
@@ -0,0 +1,8 @@
+<?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">prefs.ui</file>
+  </gresource>
+</gresources>
diff --git a/examples/book/buildapp/step6/exampleapplication.cc 
b/examples/book/buildapp/step6/exampleapplication.cc
new file mode 100644
index 0000000..264c577
--- /dev/null
+++ b/examples/book/buildapp/step6/exampleapplication.cc
@@ -0,0 +1,2 @@
+#include "../step5/exampleapplication.cc"
+// Equal to the corresponding file in step5
diff --git a/examples/book/buildapp/step6/exampleapplication.h 
b/examples/book/buildapp/step6/exampleapplication.h
new file mode 100644
index 0000000..fd7b4e8
--- /dev/null
+++ b/examples/book/buildapp/step6/exampleapplication.h
@@ -0,0 +1,2 @@
+#include "../step4/exampleapplication.h"
+// Equal to the corresponding file in step4
diff --git a/examples/book/buildapp/step6/exampleappprefs.cc b/examples/book/buildapp/step6/exampleappprefs.cc
new file mode 100644
index 0000000..1bbe550
--- /dev/null
+++ b/examples/book/buildapp/step6/exampleappprefs.cc
@@ -0,0 +1,2 @@
+#include "../step5/exampleappprefs.cc"
+// Equal to the corresponding file in step5
diff --git a/examples/book/buildapp/step6/exampleappprefs.h b/examples/book/buildapp/step6/exampleappprefs.h
new file mode 100644
index 0000000..9d9ee18
--- /dev/null
+++ b/examples/book/buildapp/step6/exampleappprefs.h
@@ -0,0 +1,2 @@
+#include "../step5/exampleappprefs.h"
+// Equal to the corresponding file in step5
diff --git a/examples/book/buildapp/step6/exampleappwindow.cc 
b/examples/book/buildapp/step6/exampleappwindow.cc
new file mode 100644
index 0000000..4d5ca82
--- /dev/null
+++ b/examples/book/buildapp/step6/exampleappwindow.cc
@@ -0,0 +1,152 @@
+/* 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>
+
+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_prop_binding()
+{
+  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_settings = Gio::Settings::create("org.gtkmm.exampleapp");
+  m_settings->bind("transition", m_stack, "transition-type");
+
+  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));
+}
+
+//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);
+}
+
+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);
+}
+
diff --git a/examples/book/buildapp/step6/exampleappwindow.h b/examples/book/buildapp/step6/exampleappwindow.h
new file mode 100644
index 0000000..9bcd18b
--- /dev/null
+++ b/examples/book/buildapp/step6/exampleappwindow.h
@@ -0,0 +1,45 @@
+/* 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();
+
+  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;
+  Glib::RefPtr<Glib::Binding> m_prop_binding;
+};
+
+#endif /* GTKMM_EXAMPLEAPPWINDOW_H */
diff --git a/examples/book/buildapp/step6/main.cc b/examples/book/buildapp/step6/main.cc
new file mode 100644
index 0000000..ca1f538
--- /dev/null
+++ b/examples/book/buildapp/step6/main.cc
@@ -0,0 +1,2 @@
+#include "../step5/main.cc"
+// Equal to the corresponding file in step5
diff --git a/examples/book/buildapp/step6/org.gtkmm.exampleapp.gschema.xml 
b/examples/book/buildapp/step6/org.gtkmm.exampleapp.gschema.xml
new file mode 100644
index 0000000..fa2dbce
--- /dev/null
+++ b/examples/book/buildapp/step6/org.gtkmm.exampleapp.gschema.xml
@@ -0,0 +1,20 @@
+<?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>
+  </schema>
+</schemalist>
diff --git a/examples/book/buildapp/step6/prefs.ui b/examples/book/buildapp/step6/prefs.ui
new file mode 100644
index 0000000..1089e95
--- /dev/null
+++ b/examples/book/buildapp/step6/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/step6/window.ui b/examples/book/buildapp/step6/window.ui
new file mode 100644
index 0000000..b41e1c2
--- /dev/null
+++ b/examples/book/buildapp/step6/window.ui
@@ -0,0 +1,61 @@
+<?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>
+          </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="GtkStack" id="stack">
+            <property name="visible">True</property>
+            <property name="transition-duration">500</property>
+          </object>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>


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