[gnome-music/wip/mschraal/meson: 5/17] correct libgd git.submodule



commit 2f3952e9ca2b20a036c16ea98a74739d396a7aae
Author: Bilal Elmoussaoui <bil elmoussaoui gmail com>
Date:   Tue Jan 23 12:02:25 2018 +0100

    correct libgd git.submodule

 .gitignore                                         |    1 -
 .gitmodules                                        |    2 +-
 subprojects/libgd/.gitignore                       |   18 +
 subprojects/libgd/Makefile.am                      |  232 ++++
 subprojects/libgd/README                           |  123 ++
 subprojects/libgd/libgd.doap                       |   33 +
 subprojects/libgd/libgd.m4                         |  140 +++
 subprojects/libgd/libgd/gd-icon-utils.c            |  394 +++++++
 subprojects/libgd/libgd/gd-icon-utils.h            |   49 +
 subprojects/libgd/libgd/gd-main-box-child.c        |  186 +++
 subprojects/libgd/libgd/gd-main-box-child.h        |   54 +
 subprojects/libgd/libgd/gd-main-box-generic.c      |  491 ++++++++
 subprojects/libgd/libgd/gd-main-box-generic.h      |   73 ++
 subprojects/libgd/libgd/gd-main-box-item.c         |  242 ++++
 subprojects/libgd/libgd/gd-main-box-item.h         |   55 +
 subprojects/libgd/libgd/gd-main-box.c              |  548 +++++++++
 subprojects/libgd/libgd/gd-main-box.h              |   61 +
 subprojects/libgd/libgd/gd-main-icon-box-child.c   |  409 +++++++
 subprojects/libgd/libgd/gd-main-icon-box-child.h   |   43 +
 subprojects/libgd/libgd/gd-main-icon-box-icon.c    |  366 ++++++
 subprojects/libgd/libgd/gd-main-icon-box-icon.h    |   40 +
 subprojects/libgd/libgd/gd-main-icon-box.c         | 1042 ++++++++++++++++
 subprojects/libgd/libgd/gd-main-icon-box.h         |   44 +
 subprojects/libgd/libgd/gd-main-icon-view.c        |  433 +++++++
 subprojects/libgd/libgd/gd-main-icon-view.h        |   72 ++
 subprojects/libgd/libgd/gd-main-list-view.c        |  346 ++++++
 subprojects/libgd/libgd/gd-main-list-view.h        |   80 ++
 subprojects/libgd/libgd/gd-main-view-generic.c     |  330 ++++++
 subprojects/libgd/libgd/gd-main-view-generic.h     |  117 ++
 subprojects/libgd/libgd/gd-main-view.c             | 1160 ++++++++++++++++++
 subprojects/libgd/libgd/gd-main-view.h             |   65 +
 subprojects/libgd/libgd/gd-margin-container.c      |  379 ++++++
 subprojects/libgd/libgd/gd-margin-container.h      |   75 ++
 subprojects/libgd/libgd/gd-notification.c          |  875 ++++++++++++++
 subprojects/libgd/libgd/gd-notification.h          |   67 ++
 subprojects/libgd/libgd/gd-styled-text-renderer.c  |  124 ++
 subprojects/libgd/libgd/gd-styled-text-renderer.h  |   79 ++
 subprojects/libgd/libgd/gd-tagged-entry.c          | 1242 ++++++++++++++++++++
 subprojects/libgd/libgd/gd-tagged-entry.h          |  117 ++
 .../libgd/libgd/gd-toggle-pixbuf-renderer.c        |  271 +++++
 .../libgd/libgd/gd-toggle-pixbuf-renderer.h        |   75 ++
 subprojects/libgd/libgd/gd-two-lines-renderer.c    |  621 ++++++++++
 subprojects/libgd/libgd/gd-two-lines-renderer.h    |   75 ++
 subprojects/libgd/libgd/gd-types-catalog.c         |  123 ++
 subprojects/libgd/libgd/gd-types-catalog.h         |   31 +
 subprojects/libgd/libgd/gd.h                       |   81 ++
 subprojects/libgd/libgd/meson.build                |  200 ++++
 subprojects/libgd/meson.build                      |   24 +
 subprojects/libgd/meson_options.txt                |   25 +
 subprojects/libgd/meson_readme.md                  |   88 ++
 subprojects/libgd/test-tagged-entry-2.c            |  131 +++
 subprojects/libgd/test-tagged-entry.c              |  111 ++
 52 files changed, 12061 insertions(+), 2 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 78fd2ce..a7ba8d2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,4 @@
 *~
-libgd/
 builddir/
 /*.bak
 /*.lo
diff --git a/.gitmodules b/.gitmodules
index 6283342..e91a9d2 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,3 @@
-[submodule "libgd"]
+[submodule "subprojects/libgd"]
        path = subprojects/libgd
        url = git://git.gnome.org/libgd
diff --git a/subprojects/libgd/.gitignore b/subprojects/libgd/.gitignore
new file mode 100644
index 0000000..aa8289d
--- /dev/null
+++ b/subprojects/libgd/.gitignore
@@ -0,0 +1,18 @@
+*~
+\#*\#
+*.swp
+.deps/
+.libs/
+Gd-1.0.gir
+Gd-1.0.typelib
+gd.gresource.xml
+gd-resources.*
+test-tagged-entry
+test-tagged-entry-2
+Makefile
+Makefile.in
+gd-1.0.vapi
+*.lo
+*.o
+*.la
+.dirstamp
diff --git a/subprojects/libgd/Makefile.am b/subprojects/libgd/Makefile.am
new file mode 100644
index 0000000..5b7ded7
--- /dev/null
+++ b/subprojects/libgd/Makefile.am
@@ -0,0 +1,232 @@
+AUTOMAKE_OPTIONS=subdir-objects
+NULL =
+CLEANFILES =
+MAINTAINERCLEANFILES =
+EXTRA_DIST =
+noinst_DATA =
+
+AM_CPPFLAGS =                                  \
+        -I$(top_srcdir)                                \
+        -DPREFIX=\"$(prefix)\"                 \
+        -DLIBDIR=\"$(libdir)\"                 \
+        -DG_LOG_DOMAIN=\"libgd\"               \
+        -DG_DISABLE_DEPRECATED                 \
+        $(LIBGD_CFLAGS)                                \
+        $(NULL)
+
+noinst_PROGRAMS =
+
+if LIBGD_STATIC
+noinst_LTLIBRARIES = libgd.la
+else
+pkglib_LTLIBRARIES = libgd.la
+endif
+
+libgd_la_LIBADD = $(LIBGD_LIBS) $(LIBM)
+libgd_la_LDFLAGS = -avoid-version
+libgd_la_SOURCES = libgd/gd.h
+nodist_libgd_la_SOURCES =
+
+catalog_sources =                              \
+       libgd/gd-types-catalog.c                \
+       libgd/gd-types-catalog.h                \
+       $(NULL)
+
+nodist_libgd_la_SOURCES += $(catalog_sources)
+EXTRA_DIST += $(catalog_sources)
+
+if LIBGD_GTK_HACKS
+gtk_hacks_sources =                             \
+        libgd/gd-icon-utils.c                  \
+        libgd/gd-icon-utils.h                  \
+        $(NULL)
+
+nodist_libgd_la_SOURCES += $(gtk_hacks_sources)
+EXTRA_DIST += $(gtk_hacks_sources)
+endif
+
+if LIBGD__BOX_COMMON
+box_common_sources =                           \
+       libgd/gd-main-box-child.c               \
+       libgd/gd-main-box-child.h               \
+       libgd/gd-main-box-generic.c             \
+       libgd/gd-main-box-generic.h             \
+       libgd/gd-main-box-item.c                \
+       libgd/gd-main-box-item.h                \
+       $(NULL)
+
+nodist_libgd_la_SOURCES += $(box_common_sources)
+EXTRA_DIST += $(box_common_sources)
+endif
+
+if LIBGD_MAIN_ICON_BOX
+main_icon_box_sources =                                \
+       libgd/gd-main-icon-box.c                \
+       libgd/gd-main-icon-box.h                \
+       libgd/gd-main-icon-box-child.c          \
+       libgd/gd-main-icon-box-child.h          \
+       libgd/gd-main-icon-box-icon.c           \
+       libgd/gd-main-icon-box-icon.h           \
+       $(NULL)
+
+nodist_libgd_la_SOURCES += $(main_icon_box_sources)
+EXTRA_DIST += $(main_icon_box_sources)
+endif
+
+if LIBGD_MAIN_BOX
+main_box_sources =                             \
+       libgd/gd-main-box.c                     \
+       libgd/gd-main-box.h                     \
+       $(NULL)
+
+nodist_libgd_la_SOURCES += $(main_box_sources)
+EXTRA_DIST += $(main_box_sources)
+endif
+
+if LIBGD__VIEW_COMMON
+view_common_sources =                          \
+       libgd/gd-main-view-generic.c            \
+       libgd/gd-main-view-generic.h            \
+       libgd/gd-styled-text-renderer.c         \
+       libgd/gd-styled-text-renderer.h         \
+       libgd/gd-two-lines-renderer.c           \
+       libgd/gd-two-lines-renderer.h           \
+       $(NULL)
+
+nodist_libgd_la_SOURCES += $(view_common_sources)
+EXTRA_DIST += $(view_common_sources)
+endif
+
+if LIBGD_MAIN_ICON_VIEW
+main_icon_view_sources =                       \
+       libgd/gd-main-icon-view.c               \
+       libgd/gd-main-icon-view.h               \
+       libgd/gd-toggle-pixbuf-renderer.c       \
+       libgd/gd-toggle-pixbuf-renderer.h       \
+       $(NULL)
+
+nodist_libgd_la_SOURCES += $(main_icon_view_sources)
+EXTRA_DIST += $(main_icon_view_sources)
+endif
+
+if LIBGD_MAIN_LIST_VIEW
+main_list_view_sources =                       \
+       libgd/gd-main-list-view.c               \
+       libgd/gd-main-list-view.h               \
+       $(NULL)
+
+nodist_libgd_la_SOURCES += $(main_list_view_sources)
+EXTRA_DIST += $(main_list_view_sources)
+endif
+
+if LIBGD_MAIN_VIEW
+main_view_sources =                            \
+       libgd/gd-main-view.c                    \
+       libgd/gd-main-view.h                    \
+       $(NULL)
+
+nodist_libgd_la_SOURCES += $(main_view_sources)
+EXTRA_DIST += $(main_view_sources)
+endif
+
+if LIBGD_MARGIN_CONTAINER
+margin_container_sources =                     \
+       libgd/gd-margin-container.c             \
+       libgd/gd-margin-container.h             \
+       $(NULL)
+
+nodist_libgd_la_SOURCES += $(margin_container_sources)
+EXTRA_DIST += $(margin_container_sources)
+endif
+
+if LIBGD_NOTIFICATION
+notification_sources =                         \
+       libgd/gd-notification.c                 \
+       libgd/gd-notification.h                 \
+       $(NULL)
+
+nodist_libgd_la_SOURCES += $(notification_sources)
+EXTRA_DIST += $(notification_sources)
+endif
+
+if LIBGD_TAGGED_ENTRY
+tagged_entry_sources =                         \
+       libgd/gd-tagged-entry.c                 \
+       libgd/gd-tagged-entry.h                 \
+       $(NULL)
+
+nodist_libgd_la_SOURCES += $(tagged_entry_sources)
+EXTRA_DIST += $(tagged_entry_sources)
+
+noinst_PROGRAMS +=                             \
+       test-tagged-entry                       \
+       test-tagged-entry-2                     \
+       $(null)
+
+test_tagged_entry_SOURCES =                            \
+       test-tagged-entry.c                             \
+       $(NULL)
+test_tagged_entry_LDADD =                              \
+       $(LIBGD_LIBS)                           \
+       libgd.la                                \
+       $(NULL)
+
+test_tagged_entry_2_SOURCES =                  \
+       test-tagged-entry-2.c                   \
+       $(NULL)
+test_tagged_entry_2_LDADD =                    \
+       $(LIBGD_LIBS)                           \
+       libgd.la                                \
+       $(NULL)
+endif
+
+if LIBGD_GIR
+include $(INTROSPECTION_MAKEFILE)
+INTROSPECTION_GIRS = Gd-1.0.gir
+
+Gd-1.0.gir: libgd.la Makefile
+Gd_1_0_gir_NAMESPACE = Gd
+Gd_1_0_gir_VERSION = 1.0
+Gd_1_0_gir_LIBS = libgd.la
+Gd_1_0_gir_CFLAGS = $(AM_CPPFLAGS)
+Gd_1_0_gir_SCANNERFLAGS =                      \
+       --warn-all                              \
+       --symbol-prefix=gd                      \
+       --identifier-prefix=Gd                  \
+        --c-include="libgd/gd.h"               \
+       $(NULL)
+Gd_1_0_gir_INCLUDES = $(LIBGD_GIR_INCLUDES)
+Gd_1_0_gir_FILES = $(nodist_libgd_la_SOURCES)
+
+if LIBGD_STATIC
+noinst_DATA += $(srcdir)/Gd-1.0.gir
+EXTRA_DIST += $(srcdir)/Gd-1.0.gir
+MAINTAINERCLEANFILES += $(srcdir)/Gd-1.0.gir
+else
+girdir= $(pkgdatadir)/gir-1.0
+typelibdir= $(pkglibdir)/girepository-1.0
+
+gir_DATA = $(INTROSPECTION_GIRS)
+typelib_DATA = $(gir_DATA:.gir=.typelib)
+
+CLEANFILES += $(gir_DATA) $(typelib_DATA)
+endif
+endif
+
+if LIBGD_VAPI
+VAPIS = $(srcdir)/gd-1.0.vapi
+
+$(srcdir)/gd-1.0.vapi: $(srcdir)/Gd-1.0.gir
+       $(AM_V_GEN)$(VAPIGEN)                   \
+               --library gd-1.0                \
+               --pkg gtk+-3.0                  \
+               $<
+#This 'touch' is a workaround for vapigen not touching the dest file if
+#its content hasn't changed, which causes the rule to generate the .vapi
+#file to always trigger
+       @touch $@
+
+noinst_DATA += $(VAPIS)
+EXTRA_DIST += $(VAPIS)
+MAINTAINERCLEANFILES += $(VAPIS)
+endif
diff --git a/subprojects/libgd/README b/subprojects/libgd/README
new file mode 100644
index 0000000..fb8c88d
--- /dev/null
+++ b/subprojects/libgd/README
@@ -0,0 +1,123 @@
+=====
+libgd
+=====
+
+Introduction
+============
+
+libgd is a library used by various GNOME 3 styled applications.
+However, it is not a typical library, since it doesn't guarantee
+API/ABI stability, nor does it has official releases tarballs. Only
+the files actually used by your project will be shipped with its
+tarball. Only the necessary dependencies will be checked during
+configure time and used at runtime.
+
+Each application can configure libgd depending on its needs and will
+be able to either link dynamically (privately) or statically link with
+a specific development version.
+
+GObject Introspection based bindings generation such as Javascript or
+Vala are also supported.
+
+More Background
+---------------
+
+libgd originates from the GNOME Documents project (written by Cosimo
+Cecchi), which was one of the first application to follow the novel
+GNOME 3 application design.
+
+Since other applications have similar needs, it makes sense to try to
+reuse and improve the existing work. However, the design being not
+frozen, and the code being not yet matured enough and proven, it is
+not possible for the developers to guarantee API and propose the
+addition to the respective projects (Gtk+, or GLib for example) right
+now. Sharing the code allows to experiment, discuss and test together
+before proposing it upstream.
+
+Traditionally, this problem is solved by copying often outdated
+snippets of code around (due to no API/ABI guarantee), often not
+centralized (libegg).
+
+In the past, there used to be some common aging GNOME application
+libraries above Gtk+ which have been slowly deprecated in favour of
+Gtk+ (gnomeui and friends).
+
+All approaches have pros and cons. A configurable git submodule
+has the following advantages:
+
+- no direct code copying necessary because API/ABI breakage (history
+  is preserved etc..)
+- code is shared and maintained in a common project
+- you can stick to a particular upstream version, or branch off your
+  own version easily if needed (hopefully you find your way back upstream)
+- update the submodule version when your project is ready
+- the libgd options should help you to configure a library to suit
+  your needs, taking care of some of autofoo stuff for you
+
+Usage
+=====
+
+In order to use libgd, an application using autotools needs to:
+
+1. from the top-level project directory, add the submodule:
+    - git submodule add git://git.gnome.org/libgd
+
+2. in autogen.sh, it is recommended to add before autoreconf call:
+    - git submodule update --init --recursive
+
+3. in top-level Makefile.am:
+    - add -I libgd to ACLOCAL_AMFLAGS
+    - add libgd to SUBDIRS, before the project src directory
+
+4. in project configure.ac:
+    - add LIBGD_INIT([list-of-options]) after your project
+      dependencies checks
+    - add libgd/Makefile to AC_CONFIG_FILES
+
+5. from your program Makefile.am, you may now for example:
+    - link with $(top_builddir)/libgd/libgd.la, and include
+      <libgd/gd.h> (adjust your AM_CPPFLAGS as necessary)
+
+You may be interested to look at the commit switching GNOME Boxes from
+private libgd usage to the libgd submodule:
+
+http://git.gnome.org/browse/gnome-boxes/commit/?id=395652458d8b311a25ecb27cc42287602a122b1f
+
+Note for example that the submodule url is "../libgd", which is a
+better alternative for projects hosted on git.gnome.org. It will allow
+the submodule update to reuse the address and the credentials given to
+the toplevel project.
+
+LIBGD_INIT options
+==================
+
+- gtk-hacks
+
+- main-icon-view
+
+- margin-container
+
+- notification
+
+- static
+
+- tagged-entry
+
+- vapi
+
+- gir
+
+How to modify or add an API?
+============================
+
+
+TODO
+====
+
+- add translation support
+- add some form of build test
+- document: options, modification process
+- eventually add documentation generation
+- some licensing check
+- more modularity (not all in libgd.m4/Makefile.am)
+- CSS styling and data: shared only with gnome-themes-standard?
diff --git a/subprojects/libgd/libgd.doap b/subprojects/libgd/libgd.doap
new file mode 100644
index 0000000..944e083
--- /dev/null
+++ b/subprojects/libgd/libgd.doap
@@ -0,0 +1,33 @@
+<Project xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+         xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#";
+         xmlns:foaf="http://xmlns.com/foaf/0.1/";
+         xmlns:gnome="http://api.gnome.org/doap-extensions#";
+         xmlns="http://usefulinc.com/ns/doap#";>
+
+  <name xml:lang="en">libgd</name>
+  <shortdesc xml:lang="en">A common GNOME 3 applications submodule</shortdesc>
+  <description>No long description yet</description>
+
+<!--
+  <homepage rdf:resource="http://live.gnome.org/Libgd"; />
+  <bug-database rdf:resource="http://bugzilla.gnome.org/browse.cgi?product=libgd"/>
+-->
+
+  <category rdf:resource="http://api.gnome.org/doap-extensions#platform"; />
+
+  <maintainer>
+    <foaf:Person>
+      <foaf:name>Cosimo Cecchi</foaf:name>
+      <foaf:mbox rdf:resource="mailto:cosimoc gnome org" />
+      <gnome:userid>cosimoc</gnome:userid>
+    </foaf:Person>
+  </maintainer>
+  <maintainer>
+    <foaf:Person>
+      <foaf:name>Marc-André Lureau</foaf:name>
+      <foaf:mbox rdf:resource="mailto:malureau gnome org" />
+      <gnome:userid>malureau</gnome:userid>
+    </foaf:Person>
+  </maintainer>
+
+</Project>
diff --git a/subprojects/libgd/libgd.m4 b/subprojects/libgd/libgd.m4
new file mode 100644
index 0000000..b71e344
--- /dev/null
+++ b/subprojects/libgd/libgd.m4
@@ -0,0 +1,140 @@
+dnl The option stuff below is based on the similar code from Automake
+
+# _LIBGD_MANGLE_OPTION(NAME)
+# -------------------------
+# Convert NAME to a valid m4 identifier, by replacing invalid characters
+# with underscores, and prepend the _LIBGD_OPTION_ suffix to it.
+AC_DEFUN([_LIBGD_MANGLE_OPTION],
+[[_LIBGD_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])])
+
+# _LIBGD_SET_OPTION(NAME)
+# ----------------------
+# Set option NAME.  If NAME begins with a digit, treat it as a requested
+# Guile version number, and define _LIBGD_GUILE_VERSION to that number.
+# Otherwise, define the option using _LIBGD_MANGLE_OPTION.
+AC_DEFUN([_LIBGD_SET_OPTION],
+[m4_define(_LIBGD_MANGLE_OPTION([$1]), 1)])
+
+# _LIBGD_SET_OPTIONS(OPTIONS)
+# ----------------------------------
+# OPTIONS is a space-separated list of libgd options.
+AC_DEFUN([_LIBGD_SET_OPTIONS],
+[m4_foreach_w([_LIBGD_Option], [$1], [_LIBGD_SET_OPTION(_LIBGD_Option)])])
+
+# _LIBGD_IF_OPTION_SET(NAME,IF-SET,IF-NOT-SET)
+# -------------------------------------------
+# Check if option NAME is set.
+AC_DEFUN([_LIBGD_IF_OPTION_SET],
+[m4_ifset(_LIBGD_MANGLE_OPTION([$1]),[$2],[$3])])
+
+dnl LIBGD_INIT([OPTIONS], [DIR])
+dnl ----------------------------
+dnl OPTIONS      A whitespace-seperated list of options.
+dnl DIR          libgd submodule directory (defaults to 'libgd')
+AC_DEFUN([LIBGD_INIT], [
+    _LIBGD_SET_OPTIONS([$1])
+    AC_SUBST([LIBGD_MODULE_DIR],[m4_if([$2],,[libgd],[$2])])
+
+    AC_REQUIRE([LT_INIT])
+    AC_REQUIRE([AC_CHECK_LIBM])
+    AC_SUBST(LIBM)
+    LIBGD_MODULES="gtk+-3.0 >= 3.7.10"
+    LIBGD_GIR_INCLUDES="Gtk-3.0"
+    LIBGD_SOURCES=""
+
+    AM_CONDITIONAL([LIBGD_STATIC],[_LIBGD_IF_OPTION_SET([static],[true],[false])])
+
+    # main-box:
+    AM_CONDITIONAL([LIBGD_MAIN_BOX],[_LIBGD_IF_OPTION_SET([main-box],[true],[false])])
+    _LIBGD_IF_OPTION_SET([main-box],[
+        _LIBGD_SET_OPTION([main-icon-box])
+        AC_DEFINE([LIBGD_MAIN_BOX], [1], [Description])
+    ])
+
+    # main-icon-box:
+    AM_CONDITIONAL([LIBGD_MAIN_ICON_BOX],[_LIBGD_IF_OPTION_SET([main-icon-box],[true],[false])])
+    _LIBGD_IF_OPTION_SET([main-icon-box],[
+        _LIBGD_SET_OPTION([_box-common])
+        _LIBGD_SET_OPTION([gtk-hacks])
+        AC_DEFINE([LIBGD_MAIN_ICON_BOX], [1], [Description])
+    ])
+
+    # main-view:
+    AM_CONDITIONAL([LIBGD_MAIN_VIEW],[_LIBGD_IF_OPTION_SET([main-view],[true],[false])])
+    _LIBGD_IF_OPTION_SET([main-view],[
+        _LIBGD_SET_OPTION([main-icon-view])
+        _LIBGD_SET_OPTION([main-list-view])
+        _LIBGD_SET_OPTION([gtk-hacks])
+        AC_DEFINE([LIBGD_MAIN_VIEW], [1], [Description])
+    ])
+
+    # main-icon-view:
+    AM_CONDITIONAL([LIBGD_MAIN_ICON_VIEW],[_LIBGD_IF_OPTION_SET([main-icon-view],[true],[false])])
+    _LIBGD_IF_OPTION_SET([main-icon-view],[
+        _LIBGD_SET_OPTION([_view-common])
+        AC_DEFINE([LIBGD_MAIN_ICON_VIEW], [1], [Description])
+    ])
+
+    # main-list-view:
+    AM_CONDITIONAL([LIBGD_MAIN_LIST_VIEW],[_LIBGD_IF_OPTION_SET([main-list-view],[true],[false])])
+    _LIBGD_IF_OPTION_SET([main-list-view],[
+        _LIBGD_SET_OPTION([_view-common])
+        AC_DEFINE([LIBGD_MAIN_LIST_VIEW], [1], [Description])
+    ])
+
+    # margin-container:
+    AM_CONDITIONAL([LIBGD_MARGIN_CONTAINER],[_LIBGD_IF_OPTION_SET([margin-container],[true],[false])])
+    _LIBGD_IF_OPTION_SET([margin-container],[
+        AC_DEFINE([LIBGD_MARGIN_CONTAINER], [1], [Description])
+    ])
+
+    # notification:
+    AM_CONDITIONAL([LIBGD_NOTIFICATION],[_LIBGD_IF_OPTION_SET([notification],[true],[false])])
+    _LIBGD_IF_OPTION_SET([notification],[
+        AC_DEFINE([LIBGD_NOTIFICATION], [1], [Description])
+    ])
+
+    # tagged-entry: Gtk+ widget
+    AM_CONDITIONAL([LIBGD_TAGGED_ENTRY],[_LIBGD_IF_OPTION_SET([tagged-entry],[true],[false])])
+    _LIBGD_IF_OPTION_SET([tagged-entry],[
+        AC_DEFINE([LIBGD_TAGGED_ENTRY], [1], [Description])
+    ])
+
+    # vapi: vala bindings support
+    AM_CONDITIONAL([LIBGD_VAPI],[ _LIBGD_IF_OPTION_SET([vapi],[true],[false])])
+    _LIBGD_IF_OPTION_SET([vapi],[
+        _LIBGD_SET_OPTION([gir])
+        dnl check for vapigen
+        AC_PATH_PROG(VAPIGEN, vapigen, no)
+        AS_IF([test x$VAPIGEN = "xno"],
+              [AC_MSG_ERROR([Cannot find the "vapigen compiler in your PATH])])
+    ])
+
+    # gir: gobject introspection support
+    AM_CONDITIONAL([LIBGD_GIR],[ _LIBGD_IF_OPTION_SET([gir],[true],[false])])
+    _LIBGD_IF_OPTION_SET([gir],[
+        GOBJECT_INTROSPECTION_REQUIRE([0.9.6])
+    ])
+
+    # gtk-hacks: collection of Gtk+ hacks and workarounds
+    AM_CONDITIONAL([LIBGD_GTK_HACKS],[_LIBGD_IF_OPTION_SET([gtk-hacks],[true],[false])])
+    _LIBGD_IF_OPTION_SET([gtk-hacks],[
+        AC_DEFINE([LIBGD_GTK_HACKS], [1], [Description])
+    ])
+
+    # _box-common:
+    AM_CONDITIONAL([LIBGD__BOX_COMMON],[_LIBGD_IF_OPTION_SET([_box-common],[true],[false])])
+    _LIBGD_IF_OPTION_SET([_box-common],[
+        AC_DEFINE([LIBGD__BOX_COMMON], [1], [Description])
+    ])
+
+    # _view-common:
+    AM_CONDITIONAL([LIBGD__VIEW_COMMON],[_LIBGD_IF_OPTION_SET([_view-common],[true],[false])])
+    _LIBGD_IF_OPTION_SET([_view-common],[
+        AC_DEFINE([LIBGD__VIEW_COMMON], [1], [Description])
+    ])
+
+    PKG_CHECK_MODULES(LIBGD, [ $LIBGD_MODULES ])
+    AC_SUBST(LIBGD_GIR_INCLUDES)
+    AC_SUBST(LIBGD_SOURCES)
+])
diff --git a/subprojects/libgd/libgd/gd-icon-utils.c b/subprojects/libgd/libgd/gd-icon-utils.c
new file mode 100644
index 0000000..6c9dd40
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-icon-utils.c
@@ -0,0 +1,394 @@
+/*
+ * Copyright (c) 2011, 2012, 2015, 2016 Red Hat, Inc.
+ *
+ * Gnome Documents is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Gnome Documents 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 Gnome Documents; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Cosimo Cecchi <cosimoc redhat com>
+ *
+ */
+
+#include "gd-icon-utils.h"
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <string.h>
+#include <math.h>
+
+#define _BG_MIN_SIZE 20
+#define _EMBLEM_MIN_SIZE 8
+
+/**
+ * gd_copy_image_surface:
+ * @surface:
+ *
+ * Returns: (transfer full):
+ */
+cairo_surface_t *
+gd_copy_image_surface (cairo_surface_t *surface)
+{
+  cairo_surface_t *copy = NULL;
+  cairo_t *cr;
+  gdouble scale_x;
+  gdouble scale_y;
+
+  copy = cairo_surface_create_similar_image (surface, CAIRO_FORMAT_ARGB32,
+                                             cairo_image_surface_get_width (surface),
+                                             cairo_image_surface_get_height (surface));
+  cairo_surface_get_device_scale (surface, &scale_x, &scale_y);
+  cairo_surface_set_device_scale (copy, scale_x, scale_y);
+
+  cr = cairo_create (copy);
+  cairo_set_source_surface (cr, surface, 0, 0);
+  cairo_paint (cr);
+  cairo_destroy (cr);
+
+  return copy;
+}
+
+/**
+ * gd_create_surface_with_counter:
+ * @widget:
+ * @base:
+ * @number:
+ *
+ * Returns: (transfer full):
+ */
+cairo_surface_t *
+gd_create_surface_with_counter (GtkWidget *widget, cairo_surface_t *base, gint number)
+{
+  GtkStyleContext *context;
+  cairo_t *cr, *emblem_cr;
+  cairo_surface_t *emblem_surface;
+  cairo_surface_t *surface;
+  gint height;
+  gint height_scaled;
+  gint width;
+  gint width_scaled;
+  gint layout_width, layout_height;
+  gint emblem_size;
+  gint emblem_size_scaled;
+  gdouble scale;
+  gdouble scale_x;
+  gdouble scale_y;
+  gchar *str;
+  PangoLayout *layout;
+  PangoAttrList *attr_list;
+  PangoAttribute *attr;
+  PangoFontDescription *desc;
+  GdkRGBA color;
+
+  context = gtk_widget_get_style_context (GTK_WIDGET (widget));
+  gtk_style_context_save (context);
+  gtk_style_context_add_class (context, "documents-counter");
+
+  width_scaled = cairo_image_surface_get_width (base);
+  height_scaled = cairo_image_surface_get_height (base);
+  cairo_surface_get_device_scale (base, &scale_x, &scale_y);
+
+  width = width_scaled / (gint) floor (scale_x),
+  height = height_scaled / (gint) floor (scale_y);
+
+  surface = cairo_surface_create_similar_image (base, CAIRO_FORMAT_ARGB32,
+                                                width_scaled, height_scaled);
+  cairo_surface_set_device_scale (surface, scale_x, scale_y);
+
+  cr = cairo_create (surface);
+  cairo_set_source_surface (cr, base, 0, 0);
+  cairo_paint (cr);
+
+  emblem_size_scaled = MIN (width_scaled / 2, height_scaled / 2);
+  emblem_size = MIN (width / 2, height / 2);
+
+  emblem_surface = cairo_surface_create_similar_image (base, CAIRO_FORMAT_ARGB32,
+                                                       emblem_size_scaled, emblem_size_scaled);
+  cairo_surface_set_device_scale (emblem_surface, scale_x, scale_y);
+
+  emblem_cr = cairo_create (emblem_surface);
+  gtk_render_background (context, emblem_cr,
+                         0, 0, emblem_size, emblem_size);
+
+  if (number > 99)
+    number = 99;
+  if (number < -99)
+    number = -99;
+
+  str = g_strdup_printf ("%d", number);
+  layout = gtk_widget_create_pango_layout (GTK_WIDGET (widget), str);
+  g_free (str);
+
+  pango_layout_get_pixel_size (layout, &layout_width, &layout_height);
+
+  /* scale the layout to be 0.5 of the size still available for drawing */
+  scale = (emblem_size * 0.50) / (MAX (layout_width, layout_height));
+  attr_list = pango_attr_list_new ();
+
+  attr = pango_attr_scale_new (scale);
+  pango_attr_list_insert (attr_list, attr);
+  pango_layout_set_attributes (layout, attr_list);
+
+  gtk_style_context_get (context, GTK_STATE_FLAG_NORMAL, "font", &desc, NULL);
+  pango_layout_set_font_description (layout, desc);
+  pango_font_description_free (desc);
+
+  gtk_style_context_get_color (context, 0, &color);
+  gdk_cairo_set_source_rgba (emblem_cr, &color);
+
+  /* update these values */
+  pango_layout_get_pixel_size (layout, &layout_width, &layout_height);
+
+  cairo_move_to (emblem_cr,
+                 emblem_size / 2 - layout_width / 2,
+                 emblem_size / 2 - layout_height / 2);
+
+  pango_cairo_show_layout (emblem_cr, layout);
+
+  g_object_unref (layout);
+  pango_attr_list_unref (attr_list);
+  cairo_destroy (emblem_cr);
+
+  cairo_set_source_surface (cr, emblem_surface, 
+                            width - emblem_size, height - emblem_size);
+  cairo_paint (cr);
+  cairo_destroy (cr);
+
+  cairo_surface_destroy (emblem_surface);
+  gtk_style_context_restore (context);
+
+  return surface;
+}
+
+/**
+ * gd_create_symbolic_icon_for_scale:
+ * @name:
+ * @base_size:
+ * @scale:
+ *
+ * Returns: (transfer full):
+ */
+GIcon *
+gd_create_symbolic_icon_for_scale (const gchar *name,
+                                   gint base_size,
+                                   gint scale)
+{
+  gchar *symbolic_name;
+  GIcon *icon, *retval = NULL;
+  cairo_surface_t *icon_surface;
+  cairo_surface_t *surface;
+  cairo_t *cr;
+  GtkStyleContext *style;
+  GtkWidgetPath *path;
+  GdkPixbuf *pixbuf;
+  GtkIconTheme *theme;
+  GtkIconInfo *info;
+  gint bg_size;
+  gint emblem_size;
+  gint total_size;
+  gint total_size_scaled;
+
+  total_size = base_size / 2;
+  total_size_scaled = total_size * scale;
+
+  bg_size = MAX (total_size / 2, _BG_MIN_SIZE);
+  emblem_size = MAX (bg_size - 8, _EMBLEM_MIN_SIZE);
+
+  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, total_size_scaled, total_size_scaled);
+  cairo_surface_set_device_scale (surface, (gdouble) scale, (gdouble) scale);
+  cr = cairo_create (surface);
+
+  style = gtk_style_context_new ();
+
+  path = gtk_widget_path_new ();
+  gtk_widget_path_append_type (path, GTK_TYPE_ICON_VIEW);
+  gtk_style_context_set_path (style, path);
+  gtk_widget_path_unref (path);
+
+  gtk_style_context_add_class (style, "documents-icon-bg");
+
+  gtk_render_background (style, cr, (total_size - bg_size) / 2, (total_size - bg_size) / 2, bg_size, 
bg_size);
+
+  symbolic_name = g_strconcat (name, "-symbolic", NULL);
+  icon = g_themed_icon_new_with_default_fallbacks (symbolic_name);
+  g_free (symbolic_name);
+
+  theme = gtk_icon_theme_get_default();
+  info = gtk_icon_theme_lookup_by_gicon_for_scale (theme, icon, emblem_size, scale,
+                                                   GTK_ICON_LOOKUP_FORCE_SIZE);
+  g_object_unref (icon);
+
+  if (info == NULL)
+    goto out;
+
+  pixbuf = gtk_icon_info_load_symbolic_for_context (info, style, NULL, NULL);
+  g_object_unref (info);
+
+  if (pixbuf == NULL)
+    goto out;
+
+  icon_surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, scale, NULL);
+  g_object_unref (pixbuf);
+
+  gtk_render_icon_surface (style, cr, icon_surface, (total_size - emblem_size) / 2,  (total_size - 
emblem_size) / 2);
+  cairo_surface_destroy (icon_surface);
+
+  retval = G_ICON (gdk_pixbuf_get_from_surface (surface, 0, 0, total_size_scaled, total_size_scaled));
+
+ out:
+  g_object_unref (style);
+  cairo_surface_destroy (surface);
+  cairo_destroy (cr);
+
+  return retval;
+}
+
+/**
+ * gd_create_symbolic_icon:
+ * @name:
+ * @base_size:
+ *
+ * Returns: (transfer full):
+ */
+GIcon *
+gd_create_symbolic_icon (const gchar *name,
+                         gint base_size)
+{
+  return gd_create_symbolic_icon_for_scale (name, base_size, 1);
+}
+
+/**
+ * gd_embed_surface_in_frame:
+ * @source_image:
+ * @frame_image_url:
+ * @slice_width:
+ * @border_width:
+ *
+ * Returns: (transfer full):
+ */
+cairo_surface_t *
+gd_embed_surface_in_frame (cairo_surface_t *source_image,
+                           const gchar *frame_image_url,
+                           GtkBorder *slice_width,
+                           GtkBorder *border_width)
+{
+  cairo_surface_t *surface;
+  cairo_t *cr;
+  int source_width, source_height;
+  gchar *css_str;
+  GtkCssProvider *provider;
+  GtkStyleContext *context;
+  GError *error = NULL;
+  GtkWidgetPath *path;
+  gdouble scale_x, scale_y;
+
+  cairo_surface_get_device_scale (source_image, &scale_x, &scale_y);
+
+  source_width = cairo_image_surface_get_width (source_image) / (gint) floor (scale_x),
+  source_height = cairo_image_surface_get_height (source_image) / (gint) floor (scale_y);
+
+  css_str = g_strdup_printf (".embedded-image { border-image: url(\"%s\") %d %d %d %d / %dpx %dpx %dpx %dpx 
}",
+                             frame_image_url,
+                             slice_width->top, slice_width->right, slice_width->bottom, slice_width->left,
+                             border_width->top, border_width->right, border_width->bottom, 
border_width->left);
+  provider = gtk_css_provider_new ();
+  gtk_css_provider_load_from_data (provider, css_str, -1, &error);
+
+  if (error != NULL)
+    {
+      g_warning ("Unable to create the thumbnail frame image: %s", error->message);
+      g_error_free (error);
+      g_free (css_str);
+
+      return g_object_ref (source_image);
+    }
+
+  surface = cairo_surface_create_similar (source_image,
+                                          CAIRO_CONTENT_COLOR_ALPHA,
+                                          source_width, source_height);
+  cr = cairo_create (surface);
+
+  context = gtk_style_context_new ();
+  path = gtk_widget_path_new ();
+  gtk_widget_path_append_type (path, GTK_TYPE_ICON_VIEW);
+
+  gtk_style_context_set_path (context, path);
+  gtk_style_context_add_provider (context,
+                                  GTK_STYLE_PROVIDER (provider),
+                                  GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+
+  cairo_save (cr);
+  cairo_rectangle (cr,
+                  border_width->left,
+                  border_width->top,
+                  source_width - border_width->left - border_width->right,
+                  source_height - border_width->top - border_width->bottom);
+  cairo_clip (cr);
+  gtk_render_icon_surface (context, cr,
+                           source_image,
+                           0, 0);
+  cairo_restore (cr);
+
+  gtk_style_context_save (context);
+  gtk_style_context_add_class (context, "embedded-image");
+
+  gtk_render_frame (context, cr,
+                    0, 0,
+                    source_width, source_height);
+
+  gtk_style_context_restore (context);
+  cairo_destroy (cr);
+
+  gtk_widget_path_unref (path);
+  g_object_unref (provider);
+  g_object_unref (context);
+  g_free (css_str);
+
+  return surface;
+}
+
+/**
+ * gd_embed_image_in_frame:
+ * @source_image:
+ * @frame_image_url:
+ * @slice_width:
+ * @border_width:
+ *
+ * Returns: (transfer full):
+ */
+GdkPixbuf *
+gd_embed_image_in_frame (GdkPixbuf *source_image,
+                         const gchar *frame_image_url,
+                         GtkBorder *slice_width,
+                         GtkBorder *border_width)
+{
+  cairo_surface_t *surface, *embedded_surface;
+  GdkPixbuf *retval;
+
+  surface = gdk_cairo_surface_create_from_pixbuf (source_image,
+                                                  0, NULL);
+
+  /* Force the device scale to 1.0, since pixbufs are always in unscaled
+   * dimensions.
+   */
+  cairo_surface_set_device_scale (surface, 1.0, 1.0);
+  embedded_surface = gd_embed_surface_in_frame (surface, frame_image_url,
+                                                slice_width, border_width);
+  retval = gdk_pixbuf_get_from_surface (embedded_surface,
+                                        0, 0,
+                                        cairo_image_surface_get_width (embedded_surface),
+                                        cairo_image_surface_get_height (embedded_surface));
+
+  cairo_surface_destroy (embedded_surface);
+  cairo_surface_destroy (surface);
+
+  return retval;
+}
diff --git a/subprojects/libgd/libgd/gd-icon-utils.h b/subprojects/libgd/libgd/gd-icon-utils.h
new file mode 100644
index 0000000..12f4f06
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-icon-utils.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2011, 2012, 2015, 2016 Red Hat, Inc.
+ *
+ * Gnome Documents is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Gnome Documents 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 Gnome Documents; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Cosimo Cecchi <cosimoc redhat com>
+ *
+ */
+
+#ifndef __GD_CREATE_SYMBOLIC_ICON_H__
+#define __GD_CREATE_SYMBOLIC_ICON_H__
+
+#include <cairo.h>
+#include <gtk/gtk.h>
+
+cairo_surface_t *gd_copy_image_surface (cairo_surface_t *surface);
+
+cairo_surface_t *gd_create_surface_with_counter (GtkWidget *widget,
+                                                 cairo_surface_t *base,
+                                                 gint number);
+
+GIcon *gd_create_symbolic_icon (const gchar *name,
+                                gint base_size);
+GIcon *gd_create_symbolic_icon_for_scale (const gchar *name,
+                                          gint base_size,
+                                          gint scale);
+
+GdkPixbuf *gd_embed_image_in_frame (GdkPixbuf *source_image,
+                                    const gchar *frame_image_url,
+                                    GtkBorder *slice_width,
+                                    GtkBorder *border_width);
+cairo_surface_t *gd_embed_surface_in_frame (cairo_surface_t *source_image,
+                                            const gchar *frame_image_url,
+                                            GtkBorder *slice_width,
+                                            GtkBorder *border_width);
+
+#endif /* __GD_CREATE_SYMBOLIC_ICON_H__ */
diff --git a/subprojects/libgd/libgd/gd-main-box-child.c b/subprojects/libgd/libgd/gd-main-box-child.c
new file mode 100644
index 0000000..9a1118d
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-main-box-child.c
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2016, 2017 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by 
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Lesser General Public 
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License 
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Debarshi Ray <debarshir gnome org>
+ *
+ */
+
+#include "gd-main-box-child.h"
+
+G_DEFINE_INTERFACE (GdMainBoxChild, gd_main_box_child, GTK_TYPE_WIDGET)
+
+static void
+gd_main_box_child_default_init (GdMainBoxChildInterface *iface)
+{
+  GParamSpec *pspec;
+
+  /**
+   * GdMainBoxChild:item:
+   *
+   * A #GdMainBoxItem that is rendered by the #GdMainBoxChild widget.
+   */
+  pspec = g_param_spec_object ("item",
+                               "Item",
+                               "An item that is rendered by the widget",
+                               GD_TYPE_MAIN_BOX_ITEM,
+                               G_PARAM_CONSTRUCT_ONLY |
+                               G_PARAM_EXPLICIT_NOTIFY |
+                               G_PARAM_READWRITE |
+                               G_PARAM_STATIC_STRINGS);
+  g_object_interface_install_property (iface, pspec);
+
+  /**
+   * GdMainBoxChild:selection-mode:
+   *
+   * Whether the #GdMainBoxChild widget is in selection mode.
+   */
+  pspec = g_param_spec_boolean ("selection-mode",
+                                "Selection mode",
+                                "Whether the child is in selection mode",
+                                FALSE,
+                                G_PARAM_CONSTRUCT |
+                                G_PARAM_EXPLICIT_NOTIFY |
+                                G_PARAM_READWRITE |
+                                G_PARAM_STATIC_STRINGS);
+  g_object_interface_install_property (iface, pspec);
+
+  /**
+   * GdMainBoxChild:show-primary-text:
+   *
+   * Whether the #GdMainBoxChild widget is going to show the
+   * primary-text of its item.
+   */
+  pspec = g_param_spec_boolean ("show-primary-text",
+                                "Show Primary Text",
+                                "Whether the item's primary-text is going to be shown",
+                                FALSE,
+                                G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+  g_object_interface_install_property (iface, pspec);
+
+  /**
+   * GdMainBoxChild:show-secondary-text:
+   *
+   * Whether the #GdMainBoxChild widget is going to show the
+   * secondary-text of its item.
+   */
+  pspec = g_param_spec_boolean ("show-secondary-text",
+                                "Show Secondary Text",
+                                "Whether the item's secondary-text is going to be shown",
+                                FALSE,
+                                G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+  g_object_interface_install_property (iface, pspec);
+}
+
+/**
+ * gd_main_box_child_get_item:
+ * @self:
+ *
+ * Returns: (transfer none): The #GdMainBoxItem
+ */
+GdMainBoxItem *
+gd_main_box_child_get_item (GdMainBoxChild *self)
+{
+  GdMainBoxChildInterface *iface;
+
+  g_return_val_if_fail (GD_IS_MAIN_BOX_CHILD (self), NULL);
+
+  iface = GD_MAIN_BOX_CHILD_GET_IFACE (self);
+
+  return (* iface->get_item) (self);
+}
+
+/**
+ * gd_main_box_child_get_index:
+ * @self:
+ *
+ * Returns: (transfer none): The index
+ */
+gint
+gd_main_box_child_get_index (GdMainBoxChild *self)
+{
+  GdMainBoxChildInterface *iface;
+
+  g_return_val_if_fail (GD_IS_MAIN_BOX_CHILD (self), -1);
+
+  iface = GD_MAIN_BOX_CHILD_GET_IFACE (self);
+
+  return (* iface->get_index) (self);
+}
+
+/**
+ * gd_main_box_child_get_selected:
+ * @self:
+ *
+ * Returns: (transfer none): Whether @self is selected
+ */
+gboolean
+gd_main_box_child_get_selected (GdMainBoxChild *self)
+{
+  GdMainBoxChildInterface *iface;
+
+  g_return_val_if_fail (GD_IS_MAIN_BOX_CHILD (self), FALSE);
+
+  iface = GD_MAIN_BOX_CHILD_GET_IFACE (self);
+
+  return (* iface->get_selected) (self);
+}
+
+/**
+ * gd_main_box_child_get_selection_mode:
+ * @self:
+ *
+ * Returns: (transfer none): Whether @self is in selection mode
+ */
+gboolean
+gd_main_box_child_get_selection_mode (GdMainBoxChild *self)
+{
+  gboolean selection_mode;
+
+  g_return_val_if_fail (GD_IS_MAIN_BOX_CHILD (self), FALSE);
+
+  g_object_get (self, "selection-mode", &selection_mode, NULL);
+  return selection_mode;
+}
+
+/**
+ * gd_main_box_child_set_selected:
+ * @self:
+ * @selected:
+ */
+void
+gd_main_box_child_set_selected (GdMainBoxChild *self, gboolean selected)
+{
+  GdMainBoxChildInterface *iface;
+
+  g_return_if_fail (GD_IS_MAIN_BOX_CHILD (self));
+
+  iface = GD_MAIN_BOX_CHILD_GET_IFACE (self);
+
+  return (* iface->set_selected) (self, selected);
+}
+
+/**
+ * gd_main_box_child_set_selection_mode:
+ * @self:
+ * @selection_mode:
+ */
+void
+gd_main_box_child_set_selection_mode  (GdMainBoxChild *self, gboolean selection_mode)
+{
+  g_return_if_fail (GD_IS_MAIN_BOX_CHILD (self));
+  g_object_set (self, "selection-mode", selection_mode, NULL);
+}
diff --git a/subprojects/libgd/libgd/gd-main-box-child.h b/subprojects/libgd/libgd/gd-main-box-child.h
new file mode 100644
index 0000000..b2600a6
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-main-box-child.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2016 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by 
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Lesser General Public 
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License 
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Debarshi Ray <debarshir gnome org>
+ *
+ */
+
+#ifndef __GD_MAIN_BOX_CHILD_H__
+#define __GD_MAIN_BOX_CHILD_H__
+
+#include <gtk/gtk.h>
+
+#include "gd-main-box-item.h"
+
+G_BEGIN_DECLS
+
+#define GD_TYPE_MAIN_BOX_CHILD gd_main_box_child_get_type()
+G_DECLARE_INTERFACE (GdMainBoxChild, gd_main_box_child, GD, MAIN_BOX_CHILD, GtkWidget)
+
+struct _GdMainBoxChildInterface
+{
+  GTypeInterface base_iface;
+
+  /* vtable */
+  gint             (* get_index)     (GdMainBoxChild *self);
+  GdMainBoxItem  * (* get_item)      (GdMainBoxChild *self);
+  gboolean         (* get_selected)  (GdMainBoxChild *self);
+  void             (* set_selected)  (GdMainBoxChild *self, gboolean selected);
+};
+
+gint             gd_main_box_child_get_index           (GdMainBoxChild *self);
+GdMainBoxItem  * gd_main_box_child_get_item            (GdMainBoxChild *self);
+gboolean         gd_main_box_child_get_selected        (GdMainBoxChild *self);
+gboolean         gd_main_box_child_get_selection_mode  (GdMainBoxChild *self);
+void             gd_main_box_child_set_selected        (GdMainBoxChild *self, gboolean selected);
+void             gd_main_box_child_set_selection_mode  (GdMainBoxChild *self, gboolean selection_mode);
+
+G_END_DECLS
+
+#endif /* __GD_MAIN_BOX_CHILD_H__ */
diff --git a/subprojects/libgd/libgd/gd-main-box-generic.c b/subprojects/libgd/libgd/gd-main-box-generic.c
new file mode 100644
index 0000000..0d18764
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-main-box-generic.c
@@ -0,0 +1,491 @@
+/*
+ * Copyright (c) 2016, 2017 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by 
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Lesser General Public 
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License 
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Debarshi Ray <debarshir gnome org>
+ *
+ */
+
+#include "gd-main-box-generic.h"
+#include "gd-main-box-item.h"
+
+enum
+{
+  ITEM_ACTIVATED,
+  SELECTION_CHANGED,
+  SELECTION_MODE_REQUEST,
+  NUM_SIGNALS
+};
+
+static guint signals[NUM_SIGNALS] = { 0, };
+
+G_DEFINE_INTERFACE (GdMainBoxGeneric, gd_main_box_generic, GTK_TYPE_WIDGET)
+
+static void
+gd_main_box_generic_mark_range_as_selected (GdMainBoxGeneric *self, gint first_element, gint last_element)
+{
+  gint i;
+
+  if (first_element > last_element)
+    {
+      gint tmp;
+
+      tmp = first_element;
+      first_element = last_element;
+      last_element = tmp;
+    }
+
+  for (i = first_element; i <= last_element; i++)
+    {
+      GdMainBoxChild *child;
+
+      child = gd_main_box_generic_get_child_at_index (self, i);
+      gd_main_box_generic_select_child (self, child);
+    }
+}
+
+static void
+gd_main_box_generic_select_range (GdMainBoxGeneric *self, GdMainBoxChild *child)
+{
+  GListModel *model;
+  const gchar *last_selected_id;
+  gint index;
+  gint other_index = -1;
+  guint n_items;
+
+  model = gd_main_box_generic_get_model (self);
+  n_items = g_list_model_get_n_items (model);
+
+  last_selected_id = gd_main_box_generic_get_last_selected_id (self);
+  index = gd_main_box_child_get_index (child);
+
+  if (last_selected_id != NULL)
+    {
+      guint i;
+
+      for (i = 0; i < n_items; i++)
+        {
+          GdMainBoxItem *item;
+          const gchar *id;
+
+          item = GD_MAIN_BOX_ITEM (g_list_model_get_object (model, i));
+          id = gd_main_box_item_get_id (item);
+
+         if (g_strcmp0 (id, last_selected_id) == 0)
+            {
+              other_index = (gint) i;
+              g_object_unref (item);
+              break;
+            }
+
+          g_object_unref (item);
+       }
+    }
+
+  if (other_index == -1)
+    {
+      gint i;
+
+      for (i = index - 1; i >= 0; i--)
+        {
+          GdMainBoxChild *other;
+
+          other = gd_main_box_generic_get_child_at_index (self, i);
+          if (gd_main_box_child_get_selected (other))
+            {
+              other_index = i;
+              break;
+           }
+       }
+    }
+
+  if (other_index == -1)
+    {
+      gint i;
+
+      for (i = index + 1; i < (gint) n_items; i++)
+        {
+          GdMainBoxChild *other;
+
+          other = gd_main_box_generic_get_child_at_index (self, i);
+          if (gd_main_box_child_get_selected (other))
+            {
+              other_index = i;
+              break;
+            }
+        }
+    }
+
+  if (other_index == -1)
+    gd_main_box_generic_select_child (self, child);
+  else
+    gd_main_box_generic_mark_range_as_selected (self, index, other_index);
+}
+
+static void
+gd_main_box_generic_default_init (GdMainBoxGenericInterface *iface)
+{
+  GParamSpec *pspec;
+
+  /**
+   * GdMainBoxGeneric:last-selected-id:
+   *
+   * A unique ID to identify the #GdMainBoxItem object that was most
+   * recently selected.
+   */
+  pspec = g_param_spec_string ("last-selected-id",
+                               "ID",
+                               "A unique ID to identify the most recently selected item",
+                               NULL,
+                               G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_interface_install_property (iface, pspec);
+
+  /**
+   * GdMainBoxGeneric:model:
+   *
+   * A #GListModel that is rendered by the #GdMainBoxGeneric widget.
+   */
+  pspec = g_param_spec_object ("model",
+                               "Model",
+                               "A model that is rendered by the widget",
+                               G_TYPE_LIST_MODEL,
+                               G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+  g_object_interface_install_property (iface, pspec);
+
+  /**
+   * GdMainBoxGeneric:gd-selection-mode:
+   *
+   * Whether the #GdMainBoxGeneric widget is in selection mode.
+   */
+  pspec = g_param_spec_boolean ("gd-selection-mode",
+                                "Selection Mode",
+                                "Whether the widget is in selection mode",
+                                FALSE,
+                                G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+  g_object_interface_install_property (iface, pspec);
+
+  /**
+   * GdMainBoxGeneric:show-primary-text:
+   *
+   * Whether the #GdMainBoxGeneric widget is going to show the
+   * primary-text of each #GdMainBoxItem.
+   */
+  pspec = g_param_spec_boolean ("show-primary-text",
+                                "Show Primary Text",
+                                "Whether each GdMainBoxItem's primary-text is going to be shown",
+                                FALSE,
+                                G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+  g_object_interface_install_property (iface, pspec);
+
+  /**
+   * GdMainBoxGeneric:show-secondary-text:
+   *
+   * Whether the #GdMainBoxGeneric widget is going to show the
+   * secondary-text of each #GdMainBoxItem.
+   */
+  pspec = g_param_spec_boolean ("show-secondary-text",
+                                "Show Secondary Text",
+                                "Whether each GdMainBoxItem's secondary-text is going to be shown",
+                                FALSE,
+                                G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+  g_object_interface_install_property (iface, pspec);
+
+  signals[ITEM_ACTIVATED] = g_signal_new ("item-activated",
+                                          GD_TYPE_MAIN_BOX_GENERIC,
+                                          G_SIGNAL_RUN_LAST,
+                                          0,
+                                          NULL,
+                                          NULL,
+                                          g_cclosure_marshal_VOID__OBJECT,
+                                          G_TYPE_NONE,
+                                          1,
+                                          GD_TYPE_MAIN_BOX_CHILD);
+
+  signals[SELECTION_CHANGED] = g_signal_new ("selection-changed",
+                                             GD_TYPE_MAIN_BOX_GENERIC,
+                                             G_SIGNAL_RUN_LAST,
+                                             0,
+                                             NULL,
+                                             NULL,
+                                             g_cclosure_marshal_VOID__VOID,
+                                             G_TYPE_NONE,
+                                             0);
+
+  signals[SELECTION_MODE_REQUEST] = g_signal_new ("selection-mode-request",
+                                                  GD_TYPE_MAIN_BOX_GENERIC,
+                                                  G_SIGNAL_RUN_LAST,
+                                                  0,
+                                                  NULL,
+                                                  NULL,
+                                                  g_cclosure_marshal_VOID__VOID,
+                                                  G_TYPE_NONE,
+                                                  0);
+}
+
+/**
+ * gd_main_box_generic_get_child_at_index:
+ * @self:
+ * @index:
+ *
+ * Returns: (transfer none): The child at @index.
+ */
+GdMainBoxChild *
+gd_main_box_generic_get_child_at_index (GdMainBoxGeneric *self, gint index)
+{
+  GdMainBoxGenericInterface *iface;
+
+  g_return_val_if_fail (GD_IS_MAIN_BOX_GENERIC (self), NULL);
+
+  iface = GD_MAIN_BOX_GENERIC_GET_IFACE (self);
+
+  return (* iface->get_child_at_index) (self, index);
+}
+
+/**
+ * gd_main_box_generic_get_last_selected_id:
+ * @self:
+ *
+ * Returns: (transfer none): The ID of the most recently selected #GdMainBoxItem.
+ */
+const gchar *
+gd_main_box_generic_get_last_selected_id (GdMainBoxGeneric *self)
+{
+  GdMainBoxGenericInterface *iface;
+
+  g_return_val_if_fail (GD_IS_MAIN_BOX_GENERIC (self), NULL);
+
+  iface = GD_MAIN_BOX_GENERIC_GET_IFACE (self);
+
+  return (* iface->get_last_selected_id) (self);
+}
+
+/**
+ * gd_main_box_generic_get_model:
+ * @self:
+ *
+ * Returns: (transfer none): The associated model
+ */
+GListModel *
+gd_main_box_generic_get_model (GdMainBoxGeneric *self)
+{
+  GdMainBoxGenericInterface *iface;
+
+  g_return_val_if_fail (GD_IS_MAIN_BOX_GENERIC (self), NULL);
+
+  iface = GD_MAIN_BOX_GENERIC_GET_IFACE (self);
+
+  return (* iface->get_model) (self);
+}
+
+/**
+ * gd_main_box_generic_get_selected_children:
+ * @self:
+ *
+ * Returns: (element-type GdMainBoxChild) (transfer container): The
+ * selected children
+ */
+GList *
+gd_main_box_generic_get_selected_children (GdMainBoxGeneric *self)
+{
+  GdMainBoxGenericInterface *iface;
+
+  g_return_val_if_fail (GD_IS_MAIN_BOX_GENERIC (self), NULL);
+
+  iface = GD_MAIN_BOX_GENERIC_GET_IFACE (self);
+
+  return (* iface->get_selected_children) (self);
+}
+
+/**
+ * gd_main_box_generic_get_selection_mode:
+ * @self:
+ *
+ * Returns: (transfer none): Whether @self is in selection mode
+ */
+gboolean
+gd_main_box_generic_get_selection_mode (GdMainBoxGeneric *self)
+{
+  gboolean selection_mode;
+
+  g_return_val_if_fail (GD_IS_MAIN_BOX_GENERIC (self), FALSE);
+
+  g_object_get (self, "gd-selection-mode", &selection_mode, NULL);
+  return selection_mode;
+}
+
+/**
+ * gd_main_box_generic_get_show_primary_text:
+ * @self:
+ *
+ * Returns: (transfer none): Whether @self is going to show the
+ * primary-text of each #GdMainBoxItem
+ */
+gboolean
+gd_main_box_generic_get_show_primary_text (GdMainBoxGeneric *self)
+{
+  gboolean show_primary_text;
+
+  g_return_val_if_fail (GD_IS_MAIN_BOX_GENERIC (self), FALSE);
+
+  g_object_get (self, "show-primary-text", &show_primary_text, NULL);
+  return show_primary_text;
+}
+
+/**
+ * gd_main_box_generic_get_show_secondary_text:
+ * @self:
+ *
+ * Returns: (transfer none): Whether @self is going to show the
+ * secondary-text of each #GdMainBoxItem
+ */
+gboolean
+gd_main_box_generic_get_show_secondary_text (GdMainBoxGeneric *self)
+{
+  gboolean show_secondary_text;
+
+  g_return_val_if_fail (GD_IS_MAIN_BOX_GENERIC (self), FALSE);
+
+  g_object_get (self, "show-secondary-text", &show_secondary_text, NULL);
+  return show_secondary_text;
+}
+
+void
+gd_main_box_generic_select_all (GdMainBoxGeneric *self)
+{
+  GdMainBoxGenericInterface *iface;
+
+  g_return_if_fail (GD_IS_MAIN_BOX_GENERIC (self));
+
+  iface = GD_MAIN_BOX_GENERIC_GET_IFACE (self);
+
+  (* iface->select_all) (self);
+}
+
+void
+gd_main_box_generic_select_child (GdMainBoxGeneric *self, GdMainBoxChild *child)
+{
+  GdMainBoxGenericInterface *iface;
+
+  g_return_if_fail (GD_IS_MAIN_BOX_GENERIC (self));
+  g_return_if_fail (GD_IS_MAIN_BOX_CHILD (child));
+
+  iface = GD_MAIN_BOX_GENERIC_GET_IFACE (self);
+
+  (* iface->select_child) (self, child);
+}
+
+/**
+ * gd_main_box_generic_set_model:
+ * @self:
+ * @model: (allow-none):
+ *
+ */
+void
+gd_main_box_generic_set_model (GdMainBoxGeneric *self, GListModel *model)
+{
+  g_return_if_fail (GD_IS_MAIN_BOX_GENERIC (self));
+  g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
+
+  g_object_set (self, "model", model, NULL);
+}
+
+/**
+ * gd_main_box_generic_set_selection_mode:
+ * @self:
+ * @selection_mode:
+ *
+ */
+void
+gd_main_box_generic_set_selection_mode (GdMainBoxGeneric *self, gboolean selection_mode)
+{
+  g_return_if_fail (GD_IS_MAIN_BOX_GENERIC (self));
+  g_object_set (self, "gd-selection-mode", selection_mode, NULL);
+}
+
+/**
+ * gd_main_box_generic_set_show_primary_text:
+ * @self:
+ * @show_primary_text:
+ *
+ */
+void
+gd_main_box_generic_set_show_primary_text (GdMainBoxGeneric *self, gboolean show_primary_text)
+{
+  g_return_if_fail (GD_IS_MAIN_BOX_GENERIC (self));
+  g_object_set (self, "show-primary-text", show_primary_text, NULL);
+}
+
+/**
+ * gd_main_box_generic_set_show_secondary_text:
+ * @self:
+ * @show_secondary_text:
+ *
+ */
+void
+gd_main_box_generic_set_show_secondary_text (GdMainBoxGeneric *self, gboolean show_secondary_text)
+{
+  g_return_if_fail (GD_IS_MAIN_BOX_GENERIC (self));
+  g_object_set (self, "show-secondary-text", show_secondary_text, NULL);
+}
+
+void
+gd_main_box_generic_unselect_all (GdMainBoxGeneric *self)
+{
+  GdMainBoxGenericInterface *iface;
+
+  g_return_if_fail (GD_IS_MAIN_BOX_GENERIC (self));
+
+  iface = GD_MAIN_BOX_GENERIC_GET_IFACE (self);
+
+  (* iface->unselect_all) (self);
+}
+
+void
+gd_main_box_generic_unselect_child (GdMainBoxGeneric *self, GdMainBoxChild *child)
+{
+  GdMainBoxGenericInterface *iface;
+
+  g_return_if_fail (GD_IS_MAIN_BOX_GENERIC (self));
+  g_return_if_fail (GD_IS_MAIN_BOX_CHILD (child));
+
+  iface = GD_MAIN_BOX_GENERIC_GET_IFACE (self);
+
+  (* iface->unselect_child) (self, child);
+}
+
+void
+gd_main_box_generic_toggle_selection_for_child (GdMainBoxGeneric *self,
+                                                GdMainBoxChild *child,
+                                                gboolean select_range)
+{
+  GListModel *model;
+
+  model = gd_main_box_generic_get_model (self);
+  if (model == NULL)
+    return;
+
+  if (gd_main_box_child_get_selected (child))
+    {
+      gd_main_box_generic_unselect_child (self, child);
+    }
+  else
+    {
+      if (select_range)
+        gd_main_box_generic_select_range (self, child);
+      else
+        gd_main_box_generic_select_child (self, child);
+    }
+}
diff --git a/subprojects/libgd/libgd/gd-main-box-generic.h b/subprojects/libgd/libgd/gd-main-box-generic.h
new file mode 100644
index 0000000..2c9c805
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-main-box-generic.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2016, 2017 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by 
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Lesser General Public 
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License 
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Debarshi Ray <debarshir gnome org>
+ *
+ */
+
+#ifndef __GD_MAIN_BOX_GENERIC_H__
+#define __GD_MAIN_BOX_GENERIC_H__
+
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+
+#include "gd-main-box-child.h"
+
+G_BEGIN_DECLS
+
+#define GD_TYPE_MAIN_BOX_GENERIC gd_main_box_generic_get_type()
+G_DECLARE_INTERFACE (GdMainBoxGeneric, gd_main_box_generic, GD, MAIN_BOX_GENERIC, GtkWidget)
+
+struct _GdMainBoxGenericInterface
+{
+  GTypeInterface base_iface;
+
+  /* vtable */
+  GdMainBoxChild  * (* get_child_at_index)     (GdMainBoxGeneric *self, gint index);
+  const gchar     * (* get_last_selected_id)   (GdMainBoxGeneric *self);
+  GListModel      * (* get_model)              (GdMainBoxGeneric *self);
+  GList           * (* get_selected_children)  (GdMainBoxGeneric *self);
+  void              (* select_all)             (GdMainBoxGeneric *self);
+  void              (* select_child)           (GdMainBoxGeneric *self, GdMainBoxChild *child);
+  void              (* unselect_all)           (GdMainBoxGeneric *self);
+  void              (* unselect_child)         (GdMainBoxGeneric *self, GdMainBoxChild *child);
+};
+
+GdMainBoxChild  * gd_main_box_generic_get_child_at_index       (GdMainBoxGeneric *self, gint index);
+const gchar     * gd_main_box_generic_get_last_selected_id     (GdMainBoxGeneric *self);
+GListModel      * gd_main_box_generic_get_model                (GdMainBoxGeneric *self);
+GList           * gd_main_box_generic_get_selected_children    (GdMainBoxGeneric *self);
+gboolean          gd_main_box_generic_get_selection_mode       (GdMainBoxGeneric *self);
+gboolean          gd_main_box_generic_get_show_primary_text    (GdMainBoxGeneric *self);
+gboolean          gd_main_box_generic_get_show_secondary_text  (GdMainBoxGeneric *self);
+void              gd_main_box_generic_select_all               (GdMainBoxGeneric *self);
+void              gd_main_box_generic_select_child             (GdMainBoxGeneric *self, GdMainBoxChild 
*child);
+void              gd_main_box_generic_set_model                (GdMainBoxGeneric *self, GListModel *model);
+void              gd_main_box_generic_set_selection_mode       (GdMainBoxGeneric *self, gboolean 
selection_mode);
+void              gd_main_box_generic_set_show_primary_text    (GdMainBoxGeneric *self, gboolean 
show_primary_text);
+void              gd_main_box_generic_set_show_secondary_text  (GdMainBoxGeneric *self,
+                                                                gboolean show_secondary_text);
+void              gd_main_box_generic_unselect_all             (GdMainBoxGeneric *self);
+void              gd_main_box_generic_unselect_child           (GdMainBoxGeneric *self, GdMainBoxChild 
*child);
+
+void              gd_main_box_generic_toggle_selection_for_child   (GdMainBoxGeneric  *self,
+                                                                    GdMainBoxChild    *child,
+                                                                    gboolean           select_range);
+
+G_END_DECLS
+
+#endif /* __GD_MAIN_BOX_GENERIC_H__ */
diff --git a/subprojects/libgd/libgd/gd-main-box-item.c b/subprojects/libgd/libgd/gd-main-box-item.c
new file mode 100644
index 0000000..042b0e4
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-main-box-item.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2016 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by 
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Lesser General Public 
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License 
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Debarshi Ray <debarshir gnome org>
+ *
+ */
+
+#include <cairo-gobject.h>
+
+#include "gd-main-box-item.h"
+
+G_DEFINE_INTERFACE (GdMainBoxItem, gd_main_box_item, G_TYPE_OBJECT)
+
+static void
+gd_main_box_item_default_init (GdMainBoxItemInterface *iface)
+{
+  GParamSpec *pspec;
+
+  /**
+   * GdMainBoxItem:id:
+   *
+   * A unique ID to identify the #GdMainBoxItem object.
+   */
+  pspec = g_param_spec_string ("id",
+                               "ID",
+                               "A unique ID to identify the item",
+                               NULL,
+                               G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_interface_install_property (iface, pspec);
+
+  /**
+   * GdMainBoxItem:uri:
+   *
+   * A URI corresponding to the #GdMainBoxItem object.
+   */
+  pspec = g_param_spec_string ("uri",
+                               "URI",
+                               "A URI corresponding to the item",
+                               NULL,
+                               G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_interface_install_property (iface, pspec);
+
+  /**
+   * GdMainBoxItem:primary-text:
+   *
+   * Some text to describe the #GdMainBoxItem object.
+   */
+  pspec = g_param_spec_string ("primary-text",
+                               "Primary Text",
+                               "Some text to describe the item",
+                               NULL,
+                               G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_interface_install_property (iface, pspec);
+
+  /**
+   * GdMainBoxItem:secondary-text:
+   *
+   * Some additional text to describe the #GdMainBoxItem object.
+   */
+  pspec = g_param_spec_string ("secondary-text",
+                               "Secondary Text",
+                               "Some additional text to describe the item",
+                               NULL,
+                               G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_interface_install_property (iface, pspec);
+
+  /**
+   * GdMainBoxItem:icon:
+   *
+   * An icon to visually identify the #GdMainBoxItem object.
+   */
+  pspec = g_param_spec_boxed ("icon",
+                              "Icon",
+                              "An icon to visually identify the item",
+                              CAIRO_GOBJECT_TYPE_SURFACE,
+                              G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_interface_install_property (iface, pspec);
+
+  /**
+   * GdMainBoxItem:mtime:
+   *
+   * The time when the #GdMainBoxItem object was last modified.
+   */
+  pspec = g_param_spec_int64 ("mtime",
+                              "Modification time",
+                              "The time when the item was last modified",
+                              -1,
+                              G_MAXINT64,
+                              -1,
+                              G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_interface_install_property (iface, pspec);
+
+  /**
+   * GdMainBoxItem:pulse:
+   *
+   * Whether to show a progress indicator.
+   */
+  pspec = g_param_spec_boolean ("pulse",
+                                "Pulse",
+                                "Whether to show a progress indicator",
+                                FALSE,
+                                G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_interface_install_property (iface, pspec);
+}
+
+/**
+ * gd_main_box_item_get_id:
+ * @self:
+ *
+ * Returns: (transfer none): The ID
+ */
+const gchar *
+gd_main_box_item_get_id (GdMainBoxItem *self)
+{
+  GdMainBoxItemInterface *iface;
+
+  g_return_val_if_fail (GD_IS_MAIN_BOX_ITEM (self), NULL);
+
+  iface = GD_MAIN_BOX_ITEM_GET_IFACE (self);
+
+  return (* iface->get_id) (self);
+}
+
+/**
+ * gd_main_box_item_get_uri:
+ * @self:
+ *
+ * Returns: (transfer none): The URI
+ */
+const gchar *
+gd_main_box_item_get_uri (GdMainBoxItem *self)
+{
+  GdMainBoxItemInterface *iface;
+
+  g_return_val_if_fail (GD_IS_MAIN_BOX_ITEM (self), NULL);
+
+  iface = GD_MAIN_BOX_ITEM_GET_IFACE (self);
+
+  return (* iface->get_uri) (self);
+}
+
+/**
+ * gd_main_box_item_get_primary_text:
+ * @self:
+ *
+ * Returns: (transfer none): The primary text
+ */
+const gchar *
+gd_main_box_item_get_primary_text (GdMainBoxItem *self)
+{
+  GdMainBoxItemInterface *iface;
+
+  g_return_val_if_fail (GD_IS_MAIN_BOX_ITEM (self), NULL);
+
+  iface = GD_MAIN_BOX_ITEM_GET_IFACE (self);
+
+  return (* iface->get_primary_text) (self);
+}
+
+/**
+ * gd_main_box_item_get_secondary_text:
+ * @self:
+ *
+ * Returns: (transfer none): The secondary text
+ */
+const gchar *
+gd_main_box_item_get_secondary_text (GdMainBoxItem *self)
+{
+  GdMainBoxItemInterface *iface;
+
+  g_return_val_if_fail (GD_IS_MAIN_BOX_ITEM (self), NULL);
+
+  iface = GD_MAIN_BOX_ITEM_GET_IFACE (self);
+
+  return (* iface->get_secondary_text) (self);
+}
+
+/**
+ * gd_main_box_item_get_icon:
+ * @self:
+ *
+ * Returns: (transfer none): The icon
+ */
+cairo_surface_t *
+gd_main_box_item_get_icon (GdMainBoxItem *self)
+{
+  GdMainBoxItemInterface *iface;
+
+  g_return_val_if_fail (GD_IS_MAIN_BOX_ITEM (self), NULL);
+
+  iface = GD_MAIN_BOX_ITEM_GET_IFACE (self);
+
+  return (* iface->get_icon) (self);
+}
+
+/**
+ * gd_main_box_item_get_mtime:
+ * @self:
+ *
+ * Returns: (transfer none): The modification time
+ */
+gint64
+gd_main_box_item_get_mtime (GdMainBoxItem *self)
+{
+  gint64 mtime;
+
+  g_return_val_if_fail (GD_IS_MAIN_BOX_ITEM (self), -1);
+
+  g_object_get (self, "mtime", &mtime, NULL);
+  return mtime;
+}
+
+/**
+ * gd_main_box_item_get_pulse:
+ * @self:
+ *
+ * Returns: (transfer none): Whether to show a progress indicator
+ */
+gboolean
+gd_main_box_item_get_pulse (GdMainBoxItem *self)
+{
+  gboolean pulse;
+
+  g_return_val_if_fail (GD_IS_MAIN_BOX_ITEM (self), FALSE);
+
+  g_object_get (self, "pulse", &pulse, NULL);
+  return pulse;
+}
diff --git a/subprojects/libgd/libgd/gd-main-box-item.h b/subprojects/libgd/libgd/gd-main-box-item.h
new file mode 100644
index 0000000..e37cb07
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-main-box-item.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2016 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by 
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Lesser General Public 
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License 
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Debarshi Ray <debarshir gnome org>
+ *
+ */
+
+#ifndef __GD_MAIN_BOX_ITEM_H__
+#define __GD_MAIN_BOX_ITEM_H__
+
+#include <cairo.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GD_TYPE_MAIN_BOX_ITEM gd_main_box_item_get_type()
+G_DECLARE_INTERFACE (GdMainBoxItem, gd_main_box_item, GD, MAIN_BOX_ITEM, GObject)
+
+struct _GdMainBoxItemInterface
+{
+  GTypeInterface base_iface;
+
+  /* vtable */
+  const gchar      * (* get_id)              (GdMainBoxItem *self);
+  const gchar      * (* get_uri)             (GdMainBoxItem *self);
+  const gchar      * (* get_primary_text)    (GdMainBoxItem *self);
+  const gchar      * (* get_secondary_text)  (GdMainBoxItem *self);
+  cairo_surface_t  * (* get_icon)            (GdMainBoxItem *self);
+};
+
+const gchar      * gd_main_box_item_get_id              (GdMainBoxItem *self);
+const gchar      * gd_main_box_item_get_uri             (GdMainBoxItem *self);
+const gchar      * gd_main_box_item_get_primary_text    (GdMainBoxItem *self);
+const gchar      * gd_main_box_item_get_secondary_text  (GdMainBoxItem *self);
+cairo_surface_t  * gd_main_box_item_get_icon            (GdMainBoxItem *self);
+gint64             gd_main_box_item_get_mtime           (GdMainBoxItem *self);
+gboolean           gd_main_box_item_get_pulse           (GdMainBoxItem *self);
+
+G_END_DECLS
+
+#endif /* __GD_MAIN_BOX_ITEM_H__ */
diff --git a/subprojects/libgd/libgd/gd-main-box.c b/subprojects/libgd/libgd/gd-main-box.c
new file mode 100644
index 0000000..be87b1d
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-main-box.c
@@ -0,0 +1,548 @@
+/*
+ * Copyright (c) 2016, 2017 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by 
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Lesser General Public 
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License 
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Debarshi Ray <debarshir gnome org>
+ *
+ */
+
+#include "gd-main-box.h"
+#include "gd-main-box-child.h"
+#include "gd-main-box-generic.h"
+#include "gd-main-icon-box.h"
+
+#define MAIN_BOX_TYPE_INITIAL -1
+
+typedef struct _GdMainBoxPrivate GdMainBoxPrivate;
+
+struct _GdMainBoxPrivate
+{
+  GListModel *model;
+  GdMainBoxType current_type;
+  GtkWidget *current_box;
+  GtkWidget *frame;
+  gboolean selection_mode;
+  gboolean show_primary_text;
+  gboolean show_secondary_text;
+};
+
+enum
+{
+  PROP_BOX_TYPE = 1,
+  PROP_SELECTION_MODE,
+  PROP_SHOW_PRIMARY_TEXT,
+  PROP_SHOW_SECONDARY_TEXT,
+  PROP_MODEL,
+  NUM_PROPERTIES
+};
+
+enum
+{
+  ITEM_ACTIVATED,
+  SELECTION_CHANGED,
+  SELECTION_MODE_REQUEST,
+  NUM_SIGNALS
+};
+
+static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
+static guint signals[NUM_SIGNALS] = { 0, };
+
+G_DEFINE_TYPE_WITH_PRIVATE (GdMainBox, gd_main_box, GTK_TYPE_BIN)
+
+static void
+gd_main_box_activate_item_for_child (GdMainBox *self, GdMainBoxChild *child)
+{
+  GdMainBoxPrivate *priv;
+  GdMainBoxItem *item;
+
+  priv = gd_main_box_get_instance_private (self);
+
+  if (priv->model == NULL)
+    return;
+
+  item = gd_main_box_child_get_item (child);
+  if (item == NULL)
+    return;
+
+  g_signal_emit (self, signals[ITEM_ACTIVATED], 0, item);
+}
+
+static void
+gd_main_box_apply_selection_mode (GdMainBox *self)
+{
+  GdMainBoxPrivate *priv;
+
+  priv = gd_main_box_get_instance_private (self);
+
+  gd_main_box_generic_set_selection_mode (GD_MAIN_BOX_GENERIC (priv->current_box), priv->selection_mode);
+
+  if (!priv->selection_mode)
+    {
+      if (priv->model != NULL)
+        gd_main_box_unselect_all (self);
+    }
+}
+
+static void
+gd_main_box_item_activated_cb (GdMainBox *self, GdMainBoxChild *child)
+{
+  GdMainBoxPrivate *priv;
+
+  priv = gd_main_box_get_instance_private (self);
+
+  if (!priv->selection_mode)
+    gd_main_box_activate_item_for_child (self, child);
+}
+
+static void
+gd_main_box_selection_changed_cb (GdMainBox *self)
+{
+  g_signal_emit (self, signals[SELECTION_CHANGED], 0);
+}
+
+static void
+gd_main_box_selection_mode_request_cb (GdMainBox *self)
+{
+  g_signal_emit (self, signals[SELECTION_MODE_REQUEST], 0);
+}
+
+static void
+gd_main_box_rebuild (GdMainBox *self)
+{
+  GdMainBoxPrivate *priv;
+
+  priv = gd_main_box_get_instance_private (self);
+
+  if (priv->current_box != NULL)
+    gtk_widget_destroy (priv->current_box);
+
+  switch (priv->current_type)
+    {
+    case GD_MAIN_BOX_ICON:
+      priv->current_box = gd_main_icon_box_new ();
+      break;
+
+    case GD_MAIN_BOX_LIST:
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+
+  gtk_widget_set_hexpand (priv->current_box, TRUE);
+  gtk_widget_set_valign (priv->current_box, GTK_ALIGN_START);
+  g_object_bind_property (self, "show-primary-text",
+                          priv->current_box, "show-primary-text",
+                          G_BINDING_SYNC_CREATE);
+  g_object_bind_property (self, "show-secondary-text",
+                          priv->current_box, "show-secondary-text",
+                          G_BINDING_SYNC_CREATE);
+  gtk_container_add (GTK_CONTAINER (priv->frame), priv->current_box);
+
+  g_signal_connect_swapped (priv->current_box,
+                            "item-activated",
+                            G_CALLBACK (gd_main_box_item_activated_cb),
+                            self);
+  g_signal_connect_swapped (priv->current_box,
+                            "selection-changed",
+                            G_CALLBACK (gd_main_box_selection_changed_cb),
+                            self);
+  g_signal_connect_swapped (priv->current_box,
+                            "selection-mode-request",
+                            G_CALLBACK (gd_main_box_selection_mode_request_cb),
+                            self);
+
+  gd_main_box_generic_set_model (GD_MAIN_BOX_GENERIC (priv->current_box), priv->model);
+  gd_main_box_apply_selection_mode (self);
+
+  gtk_widget_show_all (GTK_WIDGET (self));
+}
+
+static void
+gd_main_box_dispose (GObject *obj)
+{
+  GdMainBox *self = GD_MAIN_BOX (obj);
+  GdMainBoxPrivate *priv;
+
+  priv = gd_main_box_get_instance_private (self);
+
+  g_clear_object (&priv->model);
+
+  G_OBJECT_CLASS (gd_main_box_parent_class)->dispose (obj);
+}
+
+static void
+gd_main_box_init (GdMainBox *self)
+{
+  GdMainBoxPrivate *priv;
+  GtkStyleContext *context;
+
+  priv = gd_main_box_get_instance_private (self);
+
+  priv->frame = gtk_frame_new (NULL);
+  context = gtk_widget_get_style_context (priv->frame);
+  gtk_style_context_add_class (context, "content-view");
+  gtk_container_add (GTK_CONTAINER (self), priv->frame);
+
+  /* so that we get constructed with the right view even at startup */
+  priv->current_type = MAIN_BOX_TYPE_INITIAL;
+}
+
+static void
+gd_main_box_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+  GdMainBox *self = GD_MAIN_BOX (object);
+
+  switch (property_id)
+    {
+    case PROP_BOX_TYPE:
+      g_value_set_int (value, gd_main_box_get_box_type (self));
+      break;
+    case PROP_SELECTION_MODE:
+      g_value_set_boolean (value, gd_main_box_get_selection_mode (self));
+      break;
+    case PROP_SHOW_PRIMARY_TEXT:
+      g_value_set_boolean (value, gd_main_box_get_show_primary_text (self));
+      break;
+    case PROP_SHOW_SECONDARY_TEXT:
+      g_value_set_boolean (value, gd_main_box_get_show_secondary_text (self));
+      break;
+    case PROP_MODEL:
+      g_value_set_object (value, gd_main_box_get_model (self));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gd_main_box_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
+{
+  GdMainBox *self = GD_MAIN_BOX (object);
+
+  switch (property_id)
+    {
+    case PROP_BOX_TYPE:
+      gd_main_box_set_box_type (self, g_value_get_int (value));
+      break;
+    case PROP_SELECTION_MODE:
+      gd_main_box_set_selection_mode (self, g_value_get_boolean (value));
+      break;
+    case PROP_SHOW_PRIMARY_TEXT:
+      gd_main_box_set_show_primary_text (self, g_value_get_boolean (value));
+      break;
+    case PROP_SHOW_SECONDARY_TEXT:
+      gd_main_box_set_show_secondary_text (self, g_value_get_boolean (value));
+      break;
+    case PROP_MODEL:
+      gd_main_box_set_model (self, g_value_get_object (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gd_main_box_class_init (GdMainBoxClass *klass)
+{
+  GObjectClass *oclass = G_OBJECT_CLASS (klass);
+
+  oclass->get_property = gd_main_box_get_property;
+  oclass->set_property = gd_main_box_set_property;
+  oclass->dispose = gd_main_box_dispose;
+
+  properties[PROP_BOX_TYPE] = g_param_spec_int ("box-type",
+                                                "Box type",
+                                                "Box type",
+                                                GD_MAIN_BOX_ICON,
+                                                GD_MAIN_BOX_LIST,
+                                                GD_MAIN_BOX_ICON,
+                                                G_PARAM_EXPLICIT_NOTIFY |
+                                                G_PARAM_READWRITE |
+                                                G_PARAM_CONSTRUCT |
+                                                G_PARAM_STATIC_STRINGS);
+
+  properties[PROP_MODEL] = g_param_spec_object ("model",
+                                                "Model",
+                                                "The GListModel",
+                                                G_TYPE_LIST_MODEL,
+                                                G_PARAM_EXPLICIT_NOTIFY |
+                                                G_PARAM_READWRITE |
+                                                G_PARAM_CONSTRUCT |
+                                                G_PARAM_STATIC_STRINGS);
+
+  properties[PROP_SELECTION_MODE] = g_param_spec_boolean ("selection-mode",
+                                                          "Selection mode",
+                                                          "Whether the view is in selection mode",
+                                                          FALSE,
+                                                          G_PARAM_EXPLICIT_NOTIFY |
+                                                          G_PARAM_READWRITE |
+                                                          G_PARAM_CONSTRUCT |
+                                                          G_PARAM_STATIC_STRINGS);
+
+  properties[PROP_SHOW_PRIMARY_TEXT] = g_param_spec_boolean ("show-primary-text",
+                                                             "Show primary text",
+                                                             "Whether each GdMainBoxItem's primary-text is 
going "
+                                                             "to be shown",
+                                                             FALSE,
+                                                             G_PARAM_EXPLICIT_NOTIFY |
+                                                             G_PARAM_READWRITE |
+                                                             G_PARAM_CONSTRUCT |
+                                                             G_PARAM_STATIC_STRINGS);
+
+  properties[PROP_SHOW_SECONDARY_TEXT] = g_param_spec_boolean ("show-secondary-text",
+                                                               "Show secondary text",
+                                                               "Whether each GdMainBoxItem's secondary-text 
is "
+                                                               "going to be shown",
+                                                               FALSE,
+                                                               G_PARAM_EXPLICIT_NOTIFY |
+                                                               G_PARAM_READWRITE |
+                                                               G_PARAM_CONSTRUCT |
+                                                               G_PARAM_STATIC_STRINGS);
+
+  signals[ITEM_ACTIVATED] = g_signal_new ("item-activated",
+                                          GD_TYPE_MAIN_BOX,
+                                          G_SIGNAL_RUN_LAST,
+                                          0,
+                                          NULL,
+                                          NULL,
+                                          g_cclosure_marshal_VOID__OBJECT,
+                                          G_TYPE_NONE,
+                                          1,
+                                          GD_TYPE_MAIN_BOX_ITEM);
+
+  signals[SELECTION_CHANGED] = g_signal_new ("selection-changed",
+                                             GD_TYPE_MAIN_BOX,
+                                             G_SIGNAL_RUN_LAST,
+                                             0,
+                                             NULL,
+                                             NULL,
+                                             g_cclosure_marshal_VOID__VOID,
+                                             G_TYPE_NONE,
+                                             0);
+
+  signals[SELECTION_MODE_REQUEST] = g_signal_new ("selection-mode-request",
+                                                  GD_TYPE_MAIN_BOX,
+                                                  G_SIGNAL_RUN_LAST,
+                                                  0,
+                                                  NULL,
+                                                  NULL,
+                                                  g_cclosure_marshal_VOID__VOID,
+                                                  G_TYPE_NONE,
+                                                  0);
+
+  g_object_class_install_properties (oclass, NUM_PROPERTIES, properties);
+}
+
+GtkWidget *
+gd_main_box_new (GdMainBoxType type)
+{
+  return g_object_new (GD_TYPE_MAIN_BOX, "box-type", type, NULL);
+}
+
+GdMainBoxType
+gd_main_box_get_box_type (GdMainBox *self)
+{
+  GdMainBoxPrivate *priv;
+
+  priv = gd_main_box_get_instance_private (self);
+  return priv->current_type;
+}
+
+void
+gd_main_box_set_box_type (GdMainBox *self, GdMainBoxType type)
+{
+  GdMainBoxPrivate *priv;
+
+  priv = gd_main_box_get_instance_private (self);
+
+  if (type == priv->current_type)
+    return;
+
+  priv->current_type = type;
+  gd_main_box_rebuild (self);
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_BOX_TYPE]);
+}
+
+gboolean
+gd_main_box_get_selection_mode (GdMainBox *self)
+{
+  GdMainBoxPrivate *priv;
+
+  priv = gd_main_box_get_instance_private (self);
+  return priv->selection_mode;
+}
+
+gboolean
+gd_main_box_get_show_primary_text (GdMainBox *self)
+{
+  GdMainBoxPrivate *priv;
+
+  priv = gd_main_box_get_instance_private (self);
+  return priv->show_primary_text;
+}
+
+gboolean
+gd_main_box_get_show_secondary_text (GdMainBox *self)
+{
+  GdMainBoxPrivate *priv;
+
+  priv = gd_main_box_get_instance_private (self);
+  return priv->show_secondary_text;
+}
+
+void
+gd_main_box_set_selection_mode (GdMainBox *self, gboolean selection_mode)
+{
+  GdMainBoxPrivate *priv;
+
+  priv = gd_main_box_get_instance_private (self);
+
+  if (selection_mode == priv->selection_mode)
+    return;
+
+  priv->selection_mode = selection_mode;
+  gd_main_box_apply_selection_mode (self);
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTION_MODE]);
+}
+
+void
+gd_main_box_set_show_primary_text (GdMainBox *self, gboolean show_primary_text)
+{
+  GdMainBoxPrivate *priv;
+
+  priv = gd_main_box_get_instance_private (self);
+
+  if (show_primary_text == priv->show_primary_text)
+    return;
+
+  priv->show_primary_text = show_primary_text;
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SHOW_PRIMARY_TEXT]);
+}
+
+void
+gd_main_box_set_show_secondary_text (GdMainBox *self, gboolean show_secondary_text)
+{
+  GdMainBoxPrivate *priv;
+
+  priv = gd_main_box_get_instance_private (self);
+
+  if (show_secondary_text == priv->show_secondary_text)
+    return;
+
+  priv->show_secondary_text = show_secondary_text;
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SHOW_SECONDARY_TEXT]);
+}
+
+/**
+ * gd_main_box_get_model:
+ * @self:
+ *
+ * Returns: (transfer none):
+ */
+GListModel *
+gd_main_box_get_model (GdMainBox *self)
+{
+  GdMainBoxPrivate *priv;
+
+  priv = gd_main_box_get_instance_private (self);
+  return priv->model;
+}
+
+/**
+ * gd_main_box_set_model:
+ * @self:
+ * @model: (allow-none):
+ *
+ */
+void
+gd_main_box_set_model (GdMainBox *self, GListModel *model)
+{
+  GdMainBoxPrivate *priv;
+
+  priv = gd_main_box_get_instance_private (self);
+
+  if (!g_set_object (&priv->model, model))
+    return;
+
+  gd_main_box_generic_set_model (GD_MAIN_BOX_GENERIC (priv->current_box), priv->model);
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
+}
+
+/**
+ * gd_main_box_get_generic_box:
+ * @self:
+ *
+ * Returns: (transfer none):
+ */
+GtkWidget *
+gd_main_box_get_generic_box (GdMainBox *self)
+{
+  GdMainBoxPrivate *priv;
+
+  priv = gd_main_box_get_instance_private (self);
+  return priv->current_box;
+}
+
+/**
+ * gd_main_box_get_selection:
+ * @self:
+ *
+ * Returns: (element-type GdMainBoxItem) (transfer full):
+ */
+GList *
+gd_main_box_get_selection (GdMainBox *self)
+{
+  GdMainBoxPrivate *priv;
+  GList *l;
+  GList *selected_children;
+  GList *selection = NULL;
+
+  priv = gd_main_box_get_instance_private (self);
+
+  selected_children = gd_main_box_generic_get_selected_children (GD_MAIN_BOX_GENERIC (priv->current_box));
+  for (l = selected_children; l != NULL; l = l->next)
+    {
+      GdMainBoxChild *child = GD_MAIN_BOX_CHILD (l->data);
+      GdMainBoxItem *item;
+
+      item = gd_main_box_child_get_item (child);
+      selection = g_list_prepend (selection, g_object_ref (item));
+    }
+
+  selection = g_list_reverse (selection);
+  g_list_free (selected_children);
+  return selection;
+}
+
+void
+gd_main_box_select_all (GdMainBox *self)
+{
+  GdMainBoxPrivate *priv;
+
+  priv = gd_main_box_get_instance_private (self);
+  gd_main_box_generic_select_all (GD_MAIN_BOX_GENERIC (priv->current_box));
+}
+
+void
+gd_main_box_unselect_all (GdMainBox *self)
+{
+  GdMainBoxPrivate *priv;
+
+  priv = gd_main_box_get_instance_private (self);
+  gd_main_box_generic_unselect_all (GD_MAIN_BOX_GENERIC (priv->current_box));
+}
diff --git a/subprojects/libgd/libgd/gd-main-box.h b/subprojects/libgd/libgd/gd-main-box.h
new file mode 100644
index 0000000..1ff8038
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-main-box.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2016, 2017 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by 
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Lesser General Public 
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License 
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Debarshi Ray <debarshir gnome org>
+ *
+ */
+
+#ifndef __GD_MAIN_BOX_H__
+#define __GD_MAIN_BOX_H__
+
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GD_TYPE_MAIN_BOX gd_main_box_get_type()
+G_DECLARE_DERIVABLE_TYPE (GdMainBox, gd_main_box, GD, MAIN_BOX, GtkBin)
+
+typedef enum
+{
+  GD_MAIN_BOX_ICON,
+  GD_MAIN_BOX_LIST
+} GdMainBoxType;
+
+struct _GdMainBoxClass
+{
+  GtkBinClass parent_class;
+};
+
+GtkWidget      * gd_main_box_new                      (GdMainBoxType type);
+GdMainBoxType    gd_main_box_get_box_type             (GdMainBox *self);
+GListModel     * gd_main_box_get_model                (GdMainBox *self);
+GList          * gd_main_box_get_selection            (GdMainBox *self);
+gboolean         gd_main_box_get_selection_mode       (GdMainBox *self);
+gboolean         gd_main_box_get_show_primary_text    (GdMainBox *self);
+gboolean         gd_main_box_get_show_secondary_text  (GdMainBox *self);
+void             gd_main_box_select_all               (GdMainBox *self);
+void             gd_main_box_set_box_type             (GdMainBox *self, GdMainBoxType type);
+void             gd_main_box_set_model                (GdMainBox *self, GListModel *model);
+void             gd_main_box_set_selection_mode       (GdMainBox *self, gboolean selection_mode);
+void             gd_main_box_set_show_primary_text    (GdMainBox *self, gboolean show_primary_text);
+void             gd_main_box_set_show_secondary_text  (GdMainBox *self, gboolean show_secondary_text);
+void             gd_main_box_unselect_all             (GdMainBox *self);
+
+G_END_DECLS
+
+#endif /* __GD_MAIN_BOX_H__ */
diff --git a/subprojects/libgd/libgd/gd-main-icon-box-child.c 
b/subprojects/libgd/libgd/gd-main-icon-box-child.c
new file mode 100644
index 0000000..6549885
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-main-icon-box-child.c
@@ -0,0 +1,409 @@
+/*
+ * Copyright (c) 2016, 2017 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by 
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Lesser General Public 
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License 
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Debarshi Ray <debarshir gnome org>
+ *
+ */
+
+#include "gd-main-box-child.h"
+#include "gd-main-icon-box-child.h"
+#include "gd-main-icon-box-icon.h"
+
+#include <gio/gio.h>
+#include <glib.h>
+
+typedef struct _GdMainIconBoxChildPrivate GdMainIconBoxChildPrivate;
+
+struct _GdMainIconBoxChildPrivate
+{
+  GdMainBoxItem *item;
+  GtkWidget *check_button;
+  gboolean selection_mode;
+  gboolean show_primary_text;
+  gboolean show_secondary_text;
+};
+
+enum
+{
+  PROP_ITEM = 1,
+  PROP_SELECTION_MODE,
+  PROP_SHOW_PRIMARY_TEXT,
+  PROP_SHOW_SECONDARY_TEXT,
+  NUM_PROPERTIES
+};
+
+static void gd_main_box_child_interface_init (GdMainBoxChildInterface *iface);
+G_DEFINE_TYPE_WITH_CODE (GdMainIconBoxChild, gd_main_icon_box_child, GTK_TYPE_FLOW_BOX_CHILD,
+                         G_ADD_PRIVATE (GdMainIconBoxChild)
+                         G_IMPLEMENT_INTERFACE (GD_TYPE_MAIN_BOX_CHILD, gd_main_box_child_interface_init))
+
+static gboolean
+gd_main_icon_box_child_bind_text_to_visible (GBinding *binding,
+                                             const GValue *from_value,
+                                             GValue *to_value,
+                                             gpointer user_data)
+{
+  gboolean to_visible;
+  const gchar *from_text;
+
+  from_text = g_value_get_string (from_value);
+  to_visible = from_text != NULL && from_text[0] != '\0' ? TRUE : FALSE;
+  g_value_set_boolean (to_value, to_visible);
+  return TRUE;
+}
+
+static void
+gd_main_icon_box_check_button_toggled (GdMainIconBoxChild *self)
+{
+  GdMainIconBoxChildPrivate *priv;
+  GtkWidget *parent;
+
+  priv = gd_main_icon_box_child_get_instance_private (self);
+
+  parent = gtk_widget_get_parent (GTK_WIDGET (self));
+  if (!GTK_IS_FLOW_BOX (parent))
+    return;
+
+  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->check_button)))
+    gtk_flow_box_select_child (GTK_FLOW_BOX (parent), GTK_FLOW_BOX_CHILD (self));
+  else
+    gtk_flow_box_unselect_child (GTK_FLOW_BOX (parent), GTK_FLOW_BOX_CHILD (self));
+}
+
+static GdMainBoxItem *
+gd_main_icon_box_child_get_item (GdMainBoxChild *child)
+{
+  GdMainIconBoxChild *self = GD_MAIN_ICON_BOX_CHILD (child);
+  GdMainIconBoxChildPrivate *priv;
+
+  priv = gd_main_icon_box_child_get_instance_private (self);
+  return priv->item;
+}
+
+static gint
+gd_main_icon_box_child_get_index (GdMainBoxChild *child)
+{
+  GdMainIconBoxChild *self = GD_MAIN_ICON_BOX_CHILD (child);
+  gint index;
+
+  index = gtk_flow_box_child_get_index (GTK_FLOW_BOX_CHILD (self));
+  return index;
+}
+
+static gboolean
+gd_main_icon_box_child_get_selected (GdMainBoxChild *child)
+{
+  GdMainIconBoxChild *self = GD_MAIN_ICON_BOX_CHILD (child);
+  gboolean selected;
+
+  selected = gtk_flow_box_child_is_selected (GTK_FLOW_BOX_CHILD (self));
+  return selected;
+}
+
+static gboolean
+gd_main_icon_box_child_get_selection_mode (GdMainIconBoxChild *self)
+{
+  GdMainIconBoxChildPrivate *priv;
+
+  priv = gd_main_icon_box_child_get_instance_private (self);
+  return priv->selection_mode;
+}
+
+static gboolean
+gd_main_icon_box_child_get_show_primary_text (GdMainIconBoxChild *self)
+{
+  GdMainIconBoxChildPrivate *priv;
+
+  priv = gd_main_icon_box_child_get_instance_private (self);
+  return priv->show_primary_text;
+}
+
+static gboolean
+gd_main_icon_box_child_get_show_secondary_text (GdMainIconBoxChild *self)
+{
+  GdMainIconBoxChildPrivate *priv;
+
+  priv = gd_main_icon_box_child_get_instance_private (self);
+  return priv->show_secondary_text;
+}
+
+static void
+gd_main_icon_box_child_set_item (GdMainIconBoxChild *self, GdMainBoxItem *item)
+{
+  GdMainIconBoxChildPrivate *priv;
+
+  priv = gd_main_icon_box_child_get_instance_private (self);
+
+  if (!g_set_object (&priv->item, item))
+    return;
+
+  g_object_notify (G_OBJECT (self), "item");
+  gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+static void
+gd_main_icon_box_child_set_selected (GdMainBoxChild *child, gboolean selected)
+{
+  GdMainIconBoxChild *self = GD_MAIN_ICON_BOX_CHILD (child);
+  GdMainIconBoxChildPrivate *priv;
+
+  priv = gd_main_icon_box_child_get_instance_private (self);
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->check_button), selected);
+}
+
+static void
+gd_main_icon_box_child_set_selection_mode (GdMainIconBoxChild *self, gboolean selection_mode)
+{
+  GdMainIconBoxChildPrivate *priv;
+
+  priv = gd_main_icon_box_child_get_instance_private (self);
+
+  if (priv->selection_mode == selection_mode)
+    return;
+
+  priv->selection_mode = selection_mode;
+  g_object_notify (G_OBJECT (self), "selection-mode");
+  gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+static void
+gd_main_icon_box_child_update_layout (GdMainIconBoxChild *self)
+{
+  GdMainIconBoxChildPrivate *priv;
+  GtkWidget *grid;
+  GtkWidget *icon;
+  GtkWidget *overlay;
+
+  priv = gd_main_icon_box_child_get_instance_private (self);
+
+  gtk_container_foreach (GTK_CONTAINER (self), (GtkCallback) gtk_widget_destroy, NULL);
+
+  grid = gtk_grid_new ();
+  gtk_widget_set_valign (grid, GTK_ALIGN_CENTER);
+  gtk_orientable_set_orientation (GTK_ORIENTABLE (grid), GTK_ORIENTATION_VERTICAL);
+  gtk_container_add (GTK_CONTAINER (self), grid);
+
+  overlay = gtk_overlay_new ();
+  gtk_container_add (GTK_CONTAINER (grid), overlay);
+
+  icon = gd_main_icon_box_icon_new (priv->item);
+  gtk_widget_set_hexpand (icon, TRUE);
+  gtk_container_add (GTK_CONTAINER (overlay), icon);
+
+  priv->check_button = gtk_check_button_new ();
+  gtk_widget_set_can_focus (priv->check_button, FALSE);
+  gtk_widget_set_halign (priv->check_button, GTK_ALIGN_END);
+  gtk_widget_set_valign (priv->check_button, GTK_ALIGN_END);
+  gtk_widget_set_no_show_all (priv->check_button, TRUE);
+  g_object_bind_property (self, "selection-mode", priv->check_button, "visible", G_BINDING_SYNC_CREATE);
+  gtk_overlay_add_overlay (GTK_OVERLAY (overlay), priv->check_button);
+  g_signal_connect_swapped (priv->check_button,
+                            "toggled",
+                            G_CALLBACK (gd_main_icon_box_check_button_toggled),
+                            self);
+
+  if (priv->show_primary_text)
+    {
+      GtkWidget *primary_label;
+
+      primary_label = gtk_label_new (NULL);
+      gtk_widget_set_no_show_all (primary_label, TRUE);
+      gtk_label_set_ellipsize (GTK_LABEL (primary_label), PANGO_ELLIPSIZE_MIDDLE);
+      g_object_bind_property (priv->item, "primary-text", primary_label, "label", G_BINDING_SYNC_CREATE);
+      g_object_bind_property_full (priv->item,
+                                   "primary-text",
+                                   primary_label,
+                                   "visible",
+                                   G_BINDING_SYNC_CREATE,
+                                   gd_main_icon_box_child_bind_text_to_visible,
+                                   NULL,
+                                   NULL,
+                                   NULL);
+      gtk_container_add (GTK_CONTAINER (grid), primary_label);
+    }
+
+  if (priv->show_secondary_text)
+    {
+      GtkStyleContext *context;
+      GtkWidget *secondary_label;
+
+      secondary_label = gtk_label_new (NULL);
+      gtk_widget_set_no_show_all (secondary_label, TRUE);
+      gtk_label_set_ellipsize (GTK_LABEL (secondary_label), PANGO_ELLIPSIZE_END);
+      context = gtk_widget_get_style_context (secondary_label);
+      gtk_style_context_add_class (context, "dim-label");
+      g_object_bind_property (priv->item, "secondary-text", secondary_label, "label", G_BINDING_SYNC_CREATE);
+      g_object_bind_property_full (priv->item,
+                                   "secondary-text",
+                                   secondary_label,
+                                   "visible",
+                                   G_BINDING_SYNC_CREATE,
+                                   gd_main_icon_box_child_bind_text_to_visible,
+                                   NULL,
+                                   NULL,
+                                   NULL);
+      gtk_container_add (GTK_CONTAINER (grid), secondary_label);
+    }
+
+  gtk_widget_show_all (grid);
+}
+
+static void
+gd_main_icon_box_child_set_show_primary_text (GdMainIconBoxChild *self, gboolean show_primary_text)
+{
+  GdMainIconBoxChildPrivate *priv;
+
+  priv = gd_main_icon_box_child_get_instance_private (self);
+
+  if (priv->show_primary_text == show_primary_text)
+    return;
+
+  priv->show_primary_text = show_primary_text;
+  gd_main_icon_box_child_update_layout (self);
+  g_object_notify (G_OBJECT (self), "show-primary-text");
+}
+
+static void
+gd_main_icon_box_child_set_show_secondary_text (GdMainIconBoxChild *self, gboolean show_secondary_text)
+{
+  GdMainIconBoxChildPrivate *priv;
+
+  priv = gd_main_icon_box_child_get_instance_private (self);
+
+  if (priv->show_secondary_text == show_secondary_text)
+    return;
+
+  priv->show_secondary_text = show_secondary_text;
+  gd_main_icon_box_child_update_layout (self);
+  g_object_notify (G_OBJECT (self), "show-secondary-text");
+}
+
+static void
+gd_main_icon_box_child_constructed (GObject *obj)
+{
+  GdMainIconBoxChild *self = GD_MAIN_ICON_BOX_CHILD (obj);
+
+  G_OBJECT_CLASS (gd_main_icon_box_child_parent_class)->constructed (obj);
+
+  gd_main_icon_box_child_update_layout (self);
+}
+
+static void
+gd_main_icon_box_child_dispose (GObject *obj)
+{
+  GdMainIconBoxChild *self = GD_MAIN_ICON_BOX_CHILD (obj);
+  GdMainIconBoxChildPrivate *priv;
+
+  priv = gd_main_icon_box_child_get_instance_private (self);
+
+  g_clear_object (&priv->item);
+
+  G_OBJECT_CLASS (gd_main_icon_box_child_parent_class)->dispose (obj);
+}
+
+static void
+gd_main_icon_box_child_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+  GdMainIconBoxChild *self = GD_MAIN_ICON_BOX_CHILD (object);
+
+  switch (property_id)
+    {
+    case PROP_ITEM:
+      g_value_set_object (value, gd_main_icon_box_child_get_item (GD_MAIN_BOX_CHILD (self)));
+      break;
+    case PROP_SELECTION_MODE:
+      g_value_set_boolean (value, gd_main_icon_box_child_get_selection_mode (self));
+      break;
+    case PROP_SHOW_PRIMARY_TEXT:
+      g_value_set_boolean (value, gd_main_icon_box_child_get_show_primary_text (self));
+      break;
+    case PROP_SHOW_SECONDARY_TEXT:
+      g_value_set_boolean (value, gd_main_icon_box_child_get_show_secondary_text (self));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gd_main_icon_box_child_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec 
*pspec)
+{
+  GdMainIconBoxChild *self = GD_MAIN_ICON_BOX_CHILD (object);
+
+  switch (property_id)
+    {
+    case PROP_ITEM:
+      gd_main_icon_box_child_set_item (self, g_value_get_object (value));
+      break;
+    case PROP_SELECTION_MODE:
+      gd_main_icon_box_child_set_selection_mode (self, g_value_get_boolean (value));
+      break;
+    case PROP_SHOW_PRIMARY_TEXT:
+      gd_main_icon_box_child_set_show_primary_text (self, g_value_get_boolean (value));
+      break;
+    case PROP_SHOW_SECONDARY_TEXT:
+      gd_main_icon_box_child_set_show_secondary_text (self, g_value_get_boolean (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gd_main_icon_box_child_init (GdMainIconBoxChild *self)
+{
+  GtkStyleContext *context;
+
+  context = gtk_widget_get_style_context (GTK_WIDGET (self));
+  gtk_style_context_add_class (context, "tile");
+}
+
+static void
+gd_main_icon_box_child_class_init (GdMainIconBoxChildClass *klass)
+{
+  GObjectClass *oclass = G_OBJECT_CLASS (klass);
+
+  oclass->constructed = gd_main_icon_box_child_constructed;
+  oclass->dispose = gd_main_icon_box_child_dispose;
+  oclass->get_property = gd_main_icon_box_child_get_property;
+  oclass->set_property = gd_main_icon_box_child_set_property;
+
+  g_object_class_override_property (oclass, PROP_ITEM, "item");
+  g_object_class_override_property (oclass, PROP_SELECTION_MODE, "selection-mode");
+  g_object_class_override_property (oclass, PROP_SHOW_PRIMARY_TEXT, "show-primary-text");
+  g_object_class_override_property (oclass, PROP_SHOW_SECONDARY_TEXT, "show-secondary-text");
+}
+
+static void
+gd_main_box_child_interface_init (GdMainBoxChildInterface *iface)
+{
+  iface->get_index = gd_main_icon_box_child_get_index;
+  iface->get_item = gd_main_icon_box_child_get_item;
+  iface->get_selected = gd_main_icon_box_child_get_selected;
+  iface->set_selected = gd_main_icon_box_child_set_selected;
+}
+
+GtkWidget *
+gd_main_icon_box_child_new (GdMainBoxItem *item, gboolean selection_mode)
+{
+  return g_object_new (GD_TYPE_MAIN_ICON_BOX_CHILD,
+                       "item", item,
+                       "selection-mode", selection_mode,
+                       NULL);
+}
diff --git a/subprojects/libgd/libgd/gd-main-icon-box-child.h 
b/subprojects/libgd/libgd/gd-main-icon-box-child.h
new file mode 100644
index 0000000..40eb271
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-main-icon-box-child.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2016 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by 
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Lesser General Public 
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License 
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Debarshi Ray <debarshir gnome org>
+ *
+ */
+
+#ifndef __GD_MAIN_ICON_BOX_CHILD_H__
+#define __GD_MAIN_ICON_BOX_CHILD_H__
+
+#include <gtk/gtk.h>
+
+#include "gd-main-box-item.h"
+
+G_BEGIN_DECLS
+
+#define GD_TYPE_MAIN_ICON_BOX_CHILD gd_main_icon_box_child_get_type()
+G_DECLARE_DERIVABLE_TYPE (GdMainIconBoxChild, gd_main_icon_box_child, GD, MAIN_ICON_BOX_CHILD, 
GtkFlowBoxChild)
+
+struct _GdMainIconBoxChildClass
+{
+  GtkFlowBoxChildClass parent_class;
+};
+
+GtkWidget * gd_main_icon_box_child_new (GdMainBoxItem *item, gboolean selection_mode);
+
+G_END_DECLS
+
+#endif /* __GD_MAIN_ICON_BOX_CHILD_H__ */
diff --git a/subprojects/libgd/libgd/gd-main-icon-box-icon.c b/subprojects/libgd/libgd/gd-main-icon-box-icon.c
new file mode 100644
index 0000000..e05efba
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-main-icon-box-icon.c
@@ -0,0 +1,366 @@
+/*
+ * Copyright (c) 2017 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Debarshi Ray <debarshir gnome org>
+ *
+ */
+
+#include "gd-main-icon-box-icon.h"
+
+#include <cairo.h>
+#include <glib.h>
+
+struct _GdMainIconBoxIcon
+{
+  GtkDrawingArea parent_instance;
+  GdMainBoxItem *item;
+  cairo_surface_t *surface_zoomed;
+  gdouble x;
+  gdouble y;
+};
+
+enum
+{
+  PROP_ITEM = 1,
+  NUM_PROPERTIES
+};
+
+static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
+
+G_DEFINE_TYPE (GdMainIconBoxIcon, gd_main_icon_box_icon, GTK_TYPE_DRAWING_AREA)
+
+static cairo_surface_t *
+gd_zoom_image_surface (cairo_surface_t *surface, gint width_zoomed, gint height_zoomed)
+{
+  cairo_t *cr;
+  cairo_format_t format;
+  cairo_pattern_t *pattern;
+  cairo_surface_t *zoomed = NULL;
+  cairo_surface_type_t surface_type;
+  gdouble scale_x;
+  gdouble scale_y;
+  gdouble zoom_x;
+  gdouble zoom_y;
+  gint height;
+  gint width;
+
+  g_return_val_if_fail (surface != NULL, NULL);
+
+  surface_type = cairo_surface_get_type (surface);
+  g_return_val_if_fail (surface_type == CAIRO_SURFACE_TYPE_IMAGE, NULL);
+
+  format = cairo_image_surface_get_format (surface);
+  zoomed = cairo_surface_create_similar_image (surface, format, width_zoomed, height_zoomed);
+  cairo_surface_get_device_scale (surface, &scale_x, &scale_y);
+  cairo_surface_set_device_scale (zoomed, scale_x, scale_y);
+
+  cr = cairo_create (zoomed);
+
+  pattern = cairo_get_source (cr);
+  cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REFLECT);
+  cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+
+  height = cairo_image_surface_get_height (surface);
+  width = cairo_image_surface_get_width (surface);
+  zoom_x = (double) width_zoomed / (gdouble) width;
+  zoom_y = (double) height_zoomed / (gdouble) height;
+  cairo_scale (cr, zoom_x, zoom_y);
+  cairo_set_source_surface (cr, surface, 0, 0);
+
+  cairo_paint (cr);
+  cairo_destroy (cr);
+
+  return zoomed;
+}
+
+static void
+gd_main_icon_box_icon_get_preferred_size (GdMainIconBoxIcon *self, gint *minimum, gint *natural)
+{
+  cairo_surface_t *surface;
+  cairo_surface_type_t surface_type;
+  gint height_scaled;
+  gint width_scaled;
+  gint scale_factor;
+  gint size = 0;
+  gint size_scaled;
+
+  surface = gd_main_box_item_get_icon (self->item);
+  if (surface == NULL)
+    goto out;
+
+  surface_type = cairo_surface_get_type (surface);
+  g_return_if_fail (surface_type == CAIRO_SURFACE_TYPE_IMAGE);
+
+  scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (self));
+  height_scaled = cairo_image_surface_get_height (surface);
+  width_scaled = cairo_image_surface_get_width (surface);
+
+  size_scaled = MAX (height_scaled, width_scaled);
+  size = size_scaled / scale_factor;
+
+ out:
+  if (minimum != NULL)
+    *minimum = size;
+
+  if (natural != NULL)
+    *natural = size;
+}
+
+static void
+gd_main_icon_box_icon_notify_icon (GdMainIconBoxIcon *self)
+{
+  g_clear_pointer (&self->surface_zoomed, (GDestroyNotify) cairo_surface_destroy);
+  gtk_widget_queue_resize (GTK_WIDGET (self));
+}
+
+static gboolean
+gd_main_icon_box_icon_draw (GtkWidget *widget, cairo_t *cr)
+{
+  GdMainIconBoxIcon *self = GD_MAIN_ICON_BOX_ICON (widget);
+
+  if (self->surface_zoomed == NULL)
+    goto out;
+
+  cairo_save (cr);
+  cairo_set_source_surface (cr, self->surface_zoomed, self->x, self->y);
+  cairo_paint (cr);
+  cairo_restore (cr);
+
+ out:
+  return GDK_EVENT_PROPAGATE;
+}
+
+static void
+gd_main_icon_box_icon_get_preferred_height (GtkWidget *widget, gint *minimum, gint *natural)
+{
+  GdMainIconBoxIcon *self = GD_MAIN_ICON_BOX_ICON (widget);
+  gd_main_icon_box_icon_get_preferred_size (self, minimum, natural);
+}
+
+static void
+gd_main_icon_box_icon_get_preferred_width (GtkWidget *widget, gint *minimum, gint *natural)
+{
+  GdMainIconBoxIcon *self = GD_MAIN_ICON_BOX_ICON (widget);
+  gd_main_icon_box_icon_get_preferred_size (self, minimum, natural);
+}
+
+static void
+gd_main_icon_box_icon_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
+{
+  GdMainIconBoxIcon *self = GD_MAIN_ICON_BOX_ICON (widget);
+  cairo_surface_t *surface;
+  cairo_surface_type_t surface_type;
+  gdouble zoom;
+  gint allocation_height_scaled;
+  gint allocation_width_scaled;
+  gint height_scaled;
+  gint height_zoomed_scaled;
+  gint scale_factor;
+  gint width_scaled;
+  gint width_zoomed_scaled;
+
+  GTK_WIDGET_CLASS (gd_main_icon_box_icon_parent_class)->size_allocate (widget, allocation);
+
+  surface = gd_main_box_item_get_icon (self->item);
+  if (surface == NULL)
+    {
+      g_return_if_fail (self->surface_zoomed == NULL);
+      return;
+    }
+
+  surface_type = cairo_surface_get_type (surface);
+  g_return_if_fail (surface_type == CAIRO_SURFACE_TYPE_IMAGE);
+
+  scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (self));
+
+  allocation_height_scaled = allocation->height * scale_factor;
+  allocation_width_scaled = allocation->width * scale_factor;
+
+  if (self->surface_zoomed != NULL)
+    {
+      height_zoomed_scaled = cairo_image_surface_get_height (self->surface_zoomed);
+      width_zoomed_scaled = cairo_image_surface_get_width (self->surface_zoomed);
+      if (height_zoomed_scaled == allocation_height_scaled && width_zoomed_scaled == allocation_width_scaled)
+        return;
+    }
+
+  height_scaled = cairo_image_surface_get_height (surface);
+  width_scaled = cairo_image_surface_get_width (surface);
+
+  if (height_scaled > width_scaled && allocation_height_scaled > height_scaled)
+    {
+      zoom = (gdouble) allocation_height_scaled / (gdouble) height_scaled;
+      height_zoomed_scaled = allocation_height_scaled;
+      width_zoomed_scaled = (gint) (zoom * (gdouble) width_scaled + 0.5);
+
+      if (allocation_width_scaled < width_zoomed_scaled)
+        {
+          zoom = (gdouble) allocation_width_scaled / (gdouble) width_zoomed_scaled;
+          height_zoomed_scaled = (gint) (zoom * (gdouble) height_zoomed_scaled + 0.5);
+          width_zoomed_scaled = allocation_width_scaled;
+        }
+    }
+  else if (height_scaled <= width_scaled && allocation_width_scaled > width_scaled)
+    {
+      zoom = (gdouble) allocation_width_scaled / (gdouble) width_scaled;
+      height_zoomed_scaled = (gint) (zoom * (gdouble) height_scaled + 0.5);
+      width_zoomed_scaled = allocation_width_scaled;
+
+      if (allocation_height_scaled < height_zoomed_scaled)
+        {
+          zoom = (gdouble) allocation_height_scaled / (gdouble) height_zoomed_scaled;
+          height_zoomed_scaled = allocation_height_scaled;
+          width_zoomed_scaled = (gint) (zoom * (gdouble) width_zoomed_scaled + 0.5);
+        }
+    }
+  else
+    {
+      height_zoomed_scaled = height_scaled;
+      width_zoomed_scaled = width_scaled;
+    }
+
+  g_clear_pointer (&self->surface_zoomed, (GDestroyNotify) cairo_surface_destroy);
+  self->surface_zoomed = gd_zoom_image_surface (surface, width_zoomed_scaled, height_zoomed_scaled);
+
+  self->x = (gdouble) (allocation_width_scaled - width_zoomed_scaled) / (2.0 * (gdouble) scale_factor);
+  self->y = (gdouble) (allocation_height_scaled - height_zoomed_scaled) / (2.0 * (gdouble) scale_factor);
+}
+
+static void
+gd_main_icon_box_icon_dispose (GObject *obj)
+{
+  GdMainIconBoxIcon *self = GD_MAIN_ICON_BOX_ICON (obj);
+
+  g_clear_object (&self->item);
+
+  G_OBJECT_CLASS (gd_main_icon_box_icon_parent_class)->dispose (obj);
+}
+
+static void
+gd_main_icon_box_icon_finalize (GObject *obj)
+{
+  GdMainIconBoxIcon *self = GD_MAIN_ICON_BOX_ICON (obj);
+
+  g_clear_pointer (&self->surface_zoomed, (GDestroyNotify) cairo_surface_destroy);
+
+  G_OBJECT_CLASS (gd_main_icon_box_icon_parent_class)->finalize (obj);
+}
+
+static void
+gd_main_icon_box_icon_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+  GdMainIconBoxIcon *self = GD_MAIN_ICON_BOX_ICON (object);
+
+  switch (property_id)
+    {
+    case PROP_ITEM:
+      g_value_set_object (value, gd_main_icon_box_icon_get_item (self));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gd_main_icon_box_icon_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec 
*pspec)
+{
+  GdMainIconBoxIcon *self = GD_MAIN_ICON_BOX_ICON (object);
+
+  switch (property_id)
+    {
+    case PROP_ITEM:
+      gd_main_icon_box_icon_set_item (self, g_value_get_object (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gd_main_icon_box_icon_init (GdMainIconBoxIcon *self)
+{
+}
+
+static void
+gd_main_icon_box_icon_class_init (GdMainIconBoxIconClass *klass)
+{
+  GObjectClass *oclass = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *wclass = GTK_WIDGET_CLASS (klass);
+
+  oclass->dispose = gd_main_icon_box_icon_dispose;
+  oclass->finalize = gd_main_icon_box_icon_finalize;
+  oclass->get_property = gd_main_icon_box_icon_get_property;
+  oclass->set_property = gd_main_icon_box_icon_set_property;
+  wclass->draw = gd_main_icon_box_icon_draw;
+  wclass->get_preferred_height = gd_main_icon_box_icon_get_preferred_height;
+  wclass->get_preferred_width = gd_main_icon_box_icon_get_preferred_width;
+  wclass->size_allocate = gd_main_icon_box_icon_size_allocate;
+
+  properties[PROP_ITEM] = g_param_spec_object ("item",
+                                               "Item",
+                                               "An item that is rendered by the widget",
+                                               GD_TYPE_MAIN_BOX_ITEM,
+                                               G_PARAM_EXPLICIT_NOTIFY |
+                                               G_PARAM_READWRITE |
+                                               G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (oclass, NUM_PROPERTIES, properties);
+}
+
+GtkWidget *
+gd_main_icon_box_icon_new (GdMainBoxItem *item)
+{
+  g_return_val_if_fail (item == NULL || GD_IS_MAIN_BOX_ITEM (item), NULL);
+  return g_object_new (GD_TYPE_MAIN_ICON_BOX_ICON, "item", item, NULL);
+}
+
+GdMainBoxItem *
+gd_main_icon_box_icon_get_item (GdMainIconBoxIcon *self)
+{
+  g_return_val_if_fail (GD_IS_MAIN_ICON_BOX_ICON (self), NULL);
+  return self->item;
+}
+
+void
+gd_main_icon_box_icon_set_item (GdMainIconBoxIcon *self, GdMainBoxItem *item)
+{
+  g_return_if_fail (GD_IS_MAIN_ICON_BOX_ICON (self));
+  g_return_if_fail (item == NULL || GD_IS_MAIN_BOX_ITEM (item));
+
+  if (self->item == item)
+    return;
+
+  if (self->item != NULL)
+    g_signal_handlers_disconnect_by_func (self->item, gd_main_icon_box_icon_notify_icon, self);
+
+  g_clear_pointer (&self->surface_zoomed, (GDestroyNotify) cairo_surface_destroy);
+  g_set_object (&self->item, item);
+
+  if (self->item != NULL)
+    {
+      g_signal_connect_object (self->item,
+                               "notify::icon",
+                               G_CALLBACK (gd_main_icon_box_icon_notify_icon),
+                               self,
+                               G_CONNECT_SWAPPED);
+    }
+
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ITEM]);
+  gtk_widget_queue_resize (GTK_WIDGET (self));
+}
diff --git a/subprojects/libgd/libgd/gd-main-icon-box-icon.h b/subprojects/libgd/libgd/gd-main-icon-box-icon.h
new file mode 100644
index 0000000..7189e0e
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-main-icon-box-icon.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2017 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Debarshi Ray <debarshir gnome org>
+ *
+ */
+
+#ifndef __GD_MAIN_ICON_BOX_ICON_H__
+#define __GD_MAIN_ICON_BOX_ICON_H__
+
+#include <gtk/gtk.h>
+
+#include "gd-main-box-item.h"
+
+G_BEGIN_DECLS
+
+#define GD_TYPE_MAIN_ICON_BOX_ICON gd_main_icon_box_icon_get_type()
+G_DECLARE_FINAL_TYPE (GdMainIconBoxIcon, gd_main_icon_box_icon, GD, MAIN_ICON_BOX_ICON, GtkDrawingArea)
+
+GtkWidget        * gd_main_icon_box_icon_new          (GdMainBoxItem *item);
+GdMainBoxItem    * gd_main_icon_box_icon_get_item     (GdMainIconBoxIcon *self);
+void               gd_main_icon_box_icon_set_item     (GdMainIconBoxIcon *self, GdMainBoxItem *item);
+
+G_END_DECLS
+
+#endif /* __GD_MAIN_ICON_BOX_ICON_H__ */
diff --git a/subprojects/libgd/libgd/gd-main-icon-box.c b/subprojects/libgd/libgd/gd-main-icon-box.c
new file mode 100644
index 0000000..341df19
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-main-icon-box.c
@@ -0,0 +1,1042 @@
+/*
+ * Copyright (c) 2016, 2017 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by 
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Lesser General Public 
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License 
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Debarshi Ray <debarshir gnome org>
+ *
+ */
+
+#include <math.h>
+
+#include <cairo.h>
+#include <gio/gio.h>
+
+#include "gd-icon-utils.h"
+#include "gd-main-icon-box.h"
+#include "gd-main-icon-box-child.h"
+#include "gd-main-box-child.h"
+#include "gd-main-box-generic.h"
+#include "gd-main-box-item.h"
+
+#define MAIN_ICON_BOX_DND_ICON_OFFSET 20
+
+typedef struct _GdMainIconBoxPrivate GdMainIconBoxPrivate;
+
+struct _GdMainIconBoxPrivate
+{
+  GListModel *model;
+  gboolean dnd_started;
+  gboolean key_pressed;
+  gboolean key_shift_pressed;
+  gboolean left_button_released;
+  gboolean left_button_shift_released;
+  gboolean selection_changed;
+  gboolean selection_mode;
+  gboolean show_primary_text;
+  gboolean show_secondary_text;
+  gchar *last_selected_id;
+  gdouble dnd_start_x;
+  gdouble dnd_start_y;
+  gint dnd_button;
+};
+
+enum
+{
+  PROP_LAST_SELECTED_ID = 1,
+  PROP_MODEL,
+  PROP_SELECTION_MODE,
+  PROP_SHOW_PRIMARY_TEXT,
+  PROP_SHOW_SECONDARY_TEXT,
+  NUM_PROPERTIES
+};
+
+static void gd_main_box_generic_interface_init (GdMainBoxGenericInterface *iface);
+G_DEFINE_TYPE_WITH_CODE (GdMainIconBox, gd_main_icon_box, GTK_TYPE_FLOW_BOX,
+                         G_ADD_PRIVATE (GdMainIconBox)
+                         G_IMPLEMENT_INTERFACE (GD_TYPE_MAIN_BOX_GENERIC, 
gd_main_box_generic_interface_init))
+
+GtkWidget *
+gd_main_icon_box_create_widget_func (gpointer item, gpointer user_data)
+{
+  GdMainIconBox *self = GD_MAIN_ICON_BOX (user_data);
+  GdMainIconBoxPrivate *priv;
+  GtkWidget *child;
+
+  g_return_val_if_fail (GD_IS_MAIN_BOX_ITEM (item), NULL);
+
+  priv = gd_main_icon_box_get_instance_private (self);
+
+  child = gd_main_icon_box_child_new (GD_MAIN_BOX_ITEM (item), priv->selection_mode);
+  g_object_bind_property (self, "show-primary-text", child, "show-primary-text", G_BINDING_SYNC_CREATE);
+  g_object_bind_property (self, "show-secondary-text", child, "show-secondary-text", G_BINDING_SYNC_CREATE);
+  gtk_widget_show_all (child);
+
+  return child;
+}
+
+static void
+gd_main_icon_box_update_last_selected_id (GdMainIconBox *self, GdMainBoxChild *child)
+{
+  GdMainIconBoxPrivate *priv;
+  GdMainBoxItem *item;
+  const gchar *id = NULL;
+
+  priv = gd_main_icon_box_get_instance_private (self);
+
+  if (child != NULL)
+    {
+      item = gd_main_box_child_get_item (child);
+      id = gd_main_box_item_get_id (item);
+    }
+
+  if (g_strcmp0 (priv->last_selected_id, id) != 0)
+    {
+      g_free (priv->last_selected_id);
+      priv->last_selected_id = g_strdup (id);
+      g_object_notify (G_OBJECT (self), "last-selected-id");
+    }
+}
+
+static GdMainBoxChild *
+gd_main_icon_box_get_child_at_index (GdMainBoxGeneric *generic, gint index)
+{
+  GdMainIconBox *self = GD_MAIN_ICON_BOX (generic);
+  GtkFlowBoxChild *child;
+
+  child = gtk_flow_box_get_child_at_index (GTK_FLOW_BOX (self), index);
+  return GD_MAIN_BOX_CHILD (child);
+}
+
+static const gchar *
+gd_main_icon_box_get_last_selected_id (GdMainBoxGeneric *generic)
+{
+  GdMainIconBox *self = GD_MAIN_ICON_BOX (generic);
+  GdMainIconBoxPrivate *priv;
+
+  priv = gd_main_icon_box_get_instance_private (self);
+  return priv->last_selected_id;
+}
+
+static GListModel *
+gd_main_icon_box_get_model (GdMainBoxGeneric *generic)
+{
+  GdMainIconBox *self = GD_MAIN_ICON_BOX (generic);
+  GdMainIconBoxPrivate *priv;
+
+  priv = gd_main_icon_box_get_instance_private (self);
+  return priv->model;
+}
+
+static GList *
+gd_main_icon_box_get_selected_children (GdMainBoxGeneric *generic)
+{
+  GdMainIconBox *self = GD_MAIN_ICON_BOX (generic);
+  GList *selected_children;
+
+  selected_children = gtk_flow_box_get_selected_children (GTK_FLOW_BOX (self));
+  return selected_children;
+}
+
+static gboolean
+gd_main_icon_box_get_selection_mode (GdMainIconBox *self)
+{
+  GdMainIconBoxPrivate *priv;
+
+  priv = gd_main_icon_box_get_instance_private (self);
+  return priv->selection_mode;
+}
+
+static gboolean
+gd_main_icon_box_get_show_primary_text (GdMainIconBox *self)
+{
+  GdMainIconBoxPrivate *priv;
+
+  priv = gd_main_icon_box_get_instance_private (self);
+  return priv->show_primary_text;
+}
+
+static gboolean
+gd_main_icon_box_get_show_secondary_text (GdMainIconBox *self)
+{
+  GdMainIconBoxPrivate *priv;
+
+  priv = gd_main_icon_box_get_instance_private (self);
+  return priv->show_secondary_text;
+}
+
+static void
+gd_main_icon_box_select_all_generic (GdMainBoxGeneric *generic)
+{
+  GdMainIconBox *self = GD_MAIN_ICON_BOX (generic);
+  g_signal_emit_by_name (self, "select-all");
+}
+
+static void
+gd_main_icon_box_select_child (GdMainBoxGeneric *generic, GdMainBoxChild *child)
+{
+  GdMainIconBox *self = GD_MAIN_ICON_BOX (generic);
+  gtk_flow_box_select_child (GTK_FLOW_BOX (self), GTK_FLOW_BOX_CHILD (child));
+}
+
+static void
+gd_main_icon_box_set_model (GdMainIconBox *self, GListModel *model)
+{
+  GdMainIconBoxPrivate *priv;
+
+  priv = gd_main_icon_box_get_instance_private (self);
+
+  if (!g_set_object (&priv->model, model))
+    return;
+
+  gtk_flow_box_bind_model (GTK_FLOW_BOX (self),
+                           priv->model,
+                           gd_main_icon_box_create_widget_func,
+                           self,
+                           NULL);
+
+  g_object_notify (G_OBJECT (self), "model");
+}
+
+static void
+gd_main_icon_box_set_selection_mode (GdMainIconBox *self, gboolean selection_mode)
+{
+  GdMainIconBoxPrivate *priv;
+  GList *children;
+  GList *l;
+
+  priv = gd_main_icon_box_get_instance_private (self);
+
+  if (priv->selection_mode == selection_mode)
+    return;
+
+  gd_main_icon_box_update_last_selected_id (self, NULL);
+
+  priv->selection_mode = selection_mode;
+  if (priv->selection_mode)
+    gtk_flow_box_set_selection_mode (GTK_FLOW_BOX (self), GTK_SELECTION_MULTIPLE);
+  else
+    gtk_flow_box_set_selection_mode (GTK_FLOW_BOX (self), GTK_SELECTION_NONE);
+
+  children = gtk_container_get_children (GTK_CONTAINER (self));
+  for (l = children; l != NULL; l = l->next)
+    {
+      GdMainBoxChild *child = GD_MAIN_BOX_CHILD (l->data);
+      gd_main_box_child_set_selection_mode (child, priv->selection_mode);
+    }
+
+  g_object_notify (G_OBJECT (self), "last-selected-id");
+  g_object_notify (G_OBJECT (self), "selection-mode");
+
+  g_list_free (children);
+}
+
+static void
+gd_main_icon_box_set_show_primary_text (GdMainIconBox *self, gboolean show_primary_text)
+{
+  GdMainIconBoxPrivate *priv;
+
+  priv = gd_main_icon_box_get_instance_private (self);
+
+  if (priv->show_primary_text == show_primary_text)
+    return;
+
+  priv->show_primary_text = show_primary_text;
+  g_object_notify (G_OBJECT (self), "show-primary-text");
+}
+
+static void
+gd_main_icon_box_set_show_secondary_text (GdMainIconBox *self, gboolean show_secondary_text)
+{
+  GdMainIconBoxPrivate *priv;
+
+  priv = gd_main_icon_box_get_instance_private (self);
+
+  if (priv->show_secondary_text == show_secondary_text)
+    return;
+
+  priv->show_secondary_text = show_secondary_text;
+  g_object_notify (G_OBJECT (self), "show-secondary-text");
+}
+
+static void
+gd_main_icon_box_unselect_all_generic (GdMainBoxGeneric *generic)
+{
+  GdMainIconBox *self = GD_MAIN_ICON_BOX (generic);
+  g_signal_emit_by_name (self, "unselect-all");
+}
+
+static void
+gd_main_icon_box_unselect_child (GdMainBoxGeneric *generic, GdMainBoxChild *child)
+{
+  GdMainIconBox *self = GD_MAIN_ICON_BOX (generic);
+  gtk_flow_box_unselect_child (GTK_FLOW_BOX (self), GTK_FLOW_BOX_CHILD (child));
+}
+
+static void
+gd_main_icon_box_activate_cursor_child (GtkFlowBox *flow_box)
+{
+  GdMainIconBox *self = GD_MAIN_ICON_BOX (flow_box);
+  GdMainIconBoxPrivate *priv;
+  GdkEvent *event = NULL;
+  gboolean initiating = FALSE;
+
+  priv = gd_main_icon_box_get_instance_private (self);
+
+  /* Use GtkFlowBox::activate-cursor-child instead of
+   * GtkWidget::key-press-event to catch key presses because it is
+   * easier to filter out non-activation keys.
+   */
+
+  event = gtk_get_current_event ();
+  if (event == NULL)
+    goto out;
+
+  if (event->type != GDK_KEY_PRESS)
+    goto out;
+
+  if (!priv->selection_mode && (event->key.state & GDK_CONTROL_MASK) != 0)
+    {
+      g_signal_emit_by_name (self, "selection-mode-request");
+      initiating = TRUE;
+    }
+
+  if (priv->selection_mode)
+    {
+      if (!initiating && (event->key.state & GDK_SHIFT_MASK) != 0)
+        priv->key_shift_pressed = TRUE;
+
+      priv->key_pressed = TRUE;
+    }
+
+ out:
+  GTK_FLOW_BOX_CLASS (gd_main_icon_box_parent_class)->activate_cursor_child (flow_box);
+  g_clear_pointer (&event, (GDestroyNotify) gdk_event_free);
+}
+
+static gboolean
+gd_main_icon_box_button_press_event (GtkWidget *widget, GdkEventButton *event)
+{
+  GdMainIconBox *self = GD_MAIN_ICON_BOX (widget);
+  GdMainIconBoxPrivate *priv;
+  GtkFlowBoxChild *child;
+  gboolean res;
+
+  priv = gd_main_icon_box_get_instance_private (self);
+
+  if (event->type != GDK_BUTTON_PRESS)
+    {
+      res = GDK_EVENT_STOP;
+      goto out;
+    }
+
+  if (event->button != GDK_BUTTON_PRIMARY)
+    goto default_behavior;
+
+  child = gtk_flow_box_get_child_at_pos (GTK_FLOW_BOX (self), (gint) event->x, (gint) event->y);
+  if (child == NULL)
+    goto default_behavior;
+
+  if (priv->selection_mode && !gtk_flow_box_child_is_selected (child))
+    goto default_behavior;
+
+  priv->dnd_button = (gint) event->button;
+  priv->dnd_start_x = event->x;
+  priv->dnd_start_y = event->y;
+
+ default_behavior:
+  res = GTK_WIDGET_CLASS (gd_main_icon_box_parent_class)->button_press_event (widget, event);
+
+ out:
+  return res;
+}
+
+static gboolean
+gd_main_icon_box_button_release_event (GtkWidget *widget, GdkEventButton *event)
+{
+  GdMainIconBox *self = GD_MAIN_ICON_BOX (widget);
+  GdMainIconBoxPrivate *priv;
+  GtkFlowBoxChild *child = NULL;
+  gboolean initiating = FALSE;
+  gboolean res;
+
+  priv = gd_main_icon_box_get_instance_private (self);
+
+  priv->dnd_button = -1;
+  priv->dnd_start_x = -1.0;
+  priv->dnd_start_y = -1.0;
+  priv->dnd_started = FALSE;
+
+  if (event->type != GDK_BUTTON_RELEASE)
+    {
+      res = GDK_EVENT_STOP;
+      goto out;
+    }
+
+  if (!priv->selection_mode &&
+      ((event->button == GDK_BUTTON_PRIMARY && (event->state & GDK_CONTROL_MASK) != 0) ||
+       event->button == GDK_BUTTON_SECONDARY))
+    {
+      g_signal_emit_by_name (self, "selection-mode-request");
+      initiating = TRUE;
+    }
+
+  if (priv->selection_mode)
+    {
+      if (event->button == GDK_BUTTON_PRIMARY)
+        {
+          /* GtkFlowBox doesn't do range selection. It will simply
+           * select a single child for shift + left-click. We need to
+           * detect it so that we can handle it later.
+           *
+           * However, range selection is only possible if we were
+           * already in the selection mode. Therefore, skip it if we
+           * have just requested the selection mode.
+           */
+          if (!initiating && (event->state & GDK_SHIFT_MASK) != 0)
+            priv->left_button_shift_released = TRUE;
+
+          priv->left_button_released = TRUE;
+        }
+      else if (event->button == GDK_BUTTON_SECONDARY)
+        {
+          /* GtkFlowBox completely ignores the right mouse
+           * button.
+           */
+
+          child = gtk_flow_box_get_child_at_pos (GTK_FLOW_BOX (self), (gint) event->x, (gint) event->y);
+          if (child != NULL)
+            {
+              gd_main_box_generic_toggle_selection_for_child (GD_MAIN_BOX_GENERIC (self),
+                                                              GD_MAIN_BOX_CHILD (child),
+                                                              (!initiating &&
+                                                               (event->state & GDK_SHIFT_MASK) != 0));
+            }
+        }
+    }
+
+  /* This is for right-clicks and rubberband selection.
+   *
+   * Rubberband selection is unlike other modes of selection because
+   * GtkFlowBox::selected-children-changed is emitted before the mouse
+   * button is released.
+   */
+  if (priv->selection_changed)
+    {
+      g_signal_emit_by_name (self, "selection-changed");
+      gd_main_icon_box_update_last_selected_id (self, GD_MAIN_BOX_CHILD (child));
+      priv->selection_changed = FALSE;
+    }
+
+  res = GTK_WIDGET_CLASS (gd_main_icon_box_parent_class)->button_release_event (widget, event);
+
+ out:
+  return res;
+}
+
+static void
+gd_main_icon_box_child_activated (GtkFlowBox *flow_box, GtkFlowBoxChild *child)
+{
+  GdMainIconBox *self = GD_MAIN_ICON_BOX (flow_box);
+  GdMainIconBoxPrivate *priv;
+  GdkEvent *event = NULL;
+
+  g_return_if_fail (GD_IS_MAIN_BOX_CHILD (child));
+
+  priv = gd_main_icon_box_get_instance_private (self);
+
+  /* GtkFlowBox might emit child-activated in the middle of a
+   * DnD. See https://bugzilla.gnome.org/show_bug.cgi?id=776306
+   */
+  if (priv->dnd_started)
+    goto out;
+
+  if (!priv->selection_mode)
+    {
+      g_signal_emit_by_name (self, "item-activated", GD_MAIN_BOX_CHILD (child));
+      goto out;
+    }
+
+  event = gtk_get_current_event ();
+  if (event == NULL)
+    goto out;
+
+  if (priv->left_button_released && !priv->selection_changed)
+    {
+      /* If a selected child is left-clicked, GtkFlowBox will activate
+       * it without unselecting it.
+       */
+      gd_main_box_generic_toggle_selection_for_child (GD_MAIN_BOX_GENERIC (self),
+                                                      GD_MAIN_BOX_CHILD (child),
+                                                      FALSE); /* One cannot unselect a range. */
+      priv->left_button_released = FALSE;
+      g_signal_emit_by_name (self, "selection-changed");
+    }
+  else if (priv->key_pressed && !priv->selection_changed)
+    {
+      /* If a selected child is activated by a keybinding, GtkFlowBox
+       * will not unselect it.
+       */
+      gd_main_box_generic_toggle_selection_for_child (GD_MAIN_BOX_GENERIC (self), GD_MAIN_BOX_CHILD (child), 
FALSE);
+      priv->key_pressed = FALSE;
+      g_signal_emit_by_name (self, "selection-changed");
+    }
+  else if (priv->left_button_shift_released || priv->key_shift_pressed)
+    {
+      /* GtkFlowBox doesn't do range selection and simply selects a
+       * single child. We handle it by unselecting the child and then
+       * selecting the range.
+       */
+      gd_main_box_generic_toggle_selection_for_child (GD_MAIN_BOX_GENERIC (self), GD_MAIN_BOX_CHILD (child), 
FALSE);
+      priv->left_button_shift_released = FALSE;
+      priv->key_shift_pressed = FALSE;
+      gd_main_box_generic_toggle_selection_for_child (GD_MAIN_BOX_GENERIC (self), GD_MAIN_BOX_CHILD (child), 
TRUE);
+      g_signal_emit_by_name (self, "selection-changed");
+    }
+  else if (priv->selection_changed)
+    {
+      /* This is for non-shift left-clicks and keyboard activation of
+       * unselected children.
+       */
+      g_signal_emit_by_name (self, "selection-changed");
+    }
+
+  g_signal_emit_by_name (self, "item-activated", GD_MAIN_BOX_CHILD (child));
+
+  if (priv->selection_changed)
+    {
+      gd_main_icon_box_update_last_selected_id (self, GD_MAIN_BOX_CHILD (child));
+      priv->selection_changed = FALSE;
+    }
+
+ out:
+  g_clear_pointer (&event, (GDestroyNotify) gdk_event_free);
+}
+
+static void
+gd_main_icon_box_drag_begin (GtkWidget *widget, GdkDragContext *context)
+{
+  GdMainIconBox *self = GD_MAIN_ICON_BOX (widget);
+  GdMainIconBoxPrivate *priv;
+  GdMainBoxItem *item;
+  GtkFlowBoxChild *child;
+  cairo_surface_t *drag_icon = NULL;
+  cairo_surface_t *icon;
+
+  priv = gd_main_icon_box_get_instance_private (self);
+
+  if (priv->dnd_start_x < 0.0 || priv->dnd_start_y < 0.0)
+    goto out;
+
+  child = gtk_flow_box_get_child_at_pos (GTK_FLOW_BOX (self),
+                                         (gint) priv->dnd_start_x,
+                                         (gint) priv->dnd_start_y);
+  if (child == NULL)
+    goto out;
+
+  item = gd_main_box_child_get_item (GD_MAIN_BOX_CHILD (child));
+  icon = gd_main_box_item_get_icon (item);
+  if (icon == NULL)
+    goto out;
+
+  if (priv->selection_mode)
+    {
+      GList *selected_children;
+      guint length;
+
+      selected_children = gtk_flow_box_get_selected_children (GTK_FLOW_BOX (self));
+      length = g_list_length (selected_children);
+      if (length > 1)
+        drag_icon = gd_create_surface_with_counter (GTK_WIDGET (self), icon, length);
+
+      g_list_free (selected_children);
+    }
+
+  if (drag_icon == NULL)
+    drag_icon = gd_copy_image_surface (icon);
+
+  cairo_surface_set_device_offset (drag_icon, -MAIN_ICON_BOX_DND_ICON_OFFSET, 
-MAIN_ICON_BOX_DND_ICON_OFFSET);
+  gtk_drag_set_icon_surface (context, drag_icon);
+
+ out:
+  g_clear_pointer (&drag_icon, (GDestroyNotify) cairo_surface_destroy);
+}
+
+static void
+gd_main_icon_box_add_child_uri_to_array (GdMainBoxChild *child, GPtrArray *uri_array)
+{
+  GdMainBoxItem *item;
+  const gchar *uri;
+
+  item = gd_main_box_child_get_item (child);
+  uri = gd_main_box_item_get_uri (item);
+  g_ptr_array_add (uri_array, g_strdup (uri));
+}
+
+static void
+gd_main_icon_box_drag_data_get (GtkWidget *widget,
+                                GdkDragContext *context,
+                                GtkSelectionData *data,
+                                guint info,
+                                guint time)
+{
+  GdMainIconBox *self = GD_MAIN_ICON_BOX (widget);
+  GdMainIconBoxPrivate *priv;
+  GPtrArray *uri_array = NULL;
+
+  priv = gd_main_icon_box_get_instance_private (self);
+
+  if (info != 0)
+    goto out;
+
+  if (priv->dnd_start_x < 0.0 || priv->dnd_start_y < 0.0)
+    goto out;
+
+  uri_array = g_ptr_array_new_with_free_func (g_free);
+
+  if (priv->selection_mode)
+    {
+      GList *l;
+      GList *selected_children;
+
+      selected_children = gtk_flow_box_get_selected_children (GTK_FLOW_BOX (self));
+      for (l = selected_children; l != NULL; l = l->next)
+        {
+          GdMainBoxChild *child = GD_MAIN_BOX_CHILD (l->data);
+          gd_main_icon_box_add_child_uri_to_array (child, uri_array);
+        }
+
+      g_list_free (selected_children);
+    }
+  else
+    {
+      GtkFlowBoxChild *child;
+
+      child = gtk_flow_box_get_child_at_pos (GTK_FLOW_BOX (self),
+                                             (gint) priv->dnd_start_x,
+                                             (gint) priv->dnd_start_y);
+
+      if (child != NULL)
+        gd_main_icon_box_add_child_uri_to_array (GD_MAIN_BOX_CHILD (child), uri_array);
+    }
+
+  g_ptr_array_add (uri_array, NULL);
+  gtk_selection_data_set_uris (data, (gchar **) uri_array->pdata);
+
+ out:
+  g_clear_pointer (&uri_array, (GDestroyNotify) g_ptr_array_unref);
+}
+
+static gboolean
+gd_main_icon_box_focus (GtkWidget *widget, GtkDirectionType direction)
+{
+  GdMainIconBox *self = GD_MAIN_ICON_BOX (widget);
+  GdMainIconBoxPrivate *priv;
+  GdkEvent *event = NULL;
+  GdkEvent *fake_event = NULL;
+  gboolean res;
+
+  priv = gd_main_icon_box_get_instance_private (self);
+
+  if (!priv->selection_mode)
+    {
+      res = GTK_WIDGET_CLASS (gd_main_icon_box_parent_class)->focus (widget, direction);
+      goto out;
+    }
+
+  event = gtk_get_current_event ();
+  if (event->type != GDK_KEY_PRESS && event->type != GDK_KEY_RELEASE)
+    {
+      res = GTK_WIDGET_CLASS (gd_main_icon_box_parent_class)->focus (widget, direction);
+      goto out;
+    }
+
+  if ((event->key.state & GDK_CONTROL_MASK) != 0)
+    {
+      res = GTK_WIDGET_CLASS (gd_main_icon_box_parent_class)->focus (widget, direction);
+      goto out;
+    }
+
+  fake_event = gdk_event_copy (event);
+  fake_event->key.state |= GDK_CONTROL_MASK;
+
+  gtk_main_do_event (fake_event);
+  res = GDK_EVENT_STOP;
+
+ out:
+  g_clear_pointer (&fake_event, (GDestroyNotify) gdk_event_free);
+  g_clear_pointer (&event, (GDestroyNotify) gdk_event_free);
+  return res;
+}
+
+static gboolean
+gd_main_icon_box_motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
+{
+  GdMainIconBox *self = GD_MAIN_ICON_BOX (widget);
+  GdMainIconBoxPrivate *priv;
+  GtkTargetList *targets;
+  gboolean res;
+  gint button;
+
+  priv = gd_main_icon_box_get_instance_private (self);
+
+  if (priv->dnd_button < 0)
+    goto out;
+
+  if (!gtk_drag_check_threshold (GTK_WIDGET (self),
+                                 (gint) priv->dnd_start_x,
+                                 (gint) priv->dnd_start_y,
+                                 (gint) event->x,
+                                 (gint) event->y))
+      goto out;
+
+  button = priv->dnd_button;
+  priv->dnd_button = -1;
+  priv->dnd_started = TRUE;
+
+  targets = gtk_drag_source_get_target_list (GTK_WIDGET (self));
+
+  gtk_drag_begin_with_coordinates (GTK_WIDGET (self),
+                                   targets,
+                                   GDK_ACTION_COPY,
+                                   button,
+                                   (GdkEvent *) event,
+                                   (gint) priv->dnd_start_x,
+                                   (gint) priv->dnd_start_y);
+
+ out:
+  res = GTK_WIDGET_CLASS (gd_main_icon_box_parent_class)->motion_notify_event (widget, event);
+  return res;
+}
+
+static gboolean
+gd_main_icon_box_move_cursor (GtkFlowBox *flow_box, GtkMovementStep step, gint count)
+{
+  GdMainIconBox *self = GD_MAIN_ICON_BOX (flow_box);
+  GdMainIconBoxPrivate *priv;
+  GdkEvent *event = NULL;
+  GdkEvent *fake_event = NULL;
+  gboolean res;
+
+  priv = gd_main_icon_box_get_instance_private (self);
+
+  if (!priv->selection_mode)
+    {
+      res = GTK_FLOW_BOX_CLASS (gd_main_icon_box_parent_class)->move_cursor (flow_box, step, count);
+      goto out;
+    }
+
+  event = gtk_get_current_event ();
+  if (event->type != GDK_KEY_PRESS && event->type != GDK_KEY_RELEASE)
+    {
+      res = GTK_FLOW_BOX_CLASS (gd_main_icon_box_parent_class)->move_cursor (flow_box, step, count);
+      goto out;
+    }
+
+  if ((event->key.state & GDK_CONTROL_MASK) != 0 && (event->key.state & GDK_SHIFT_MASK) == 0)
+    {
+      res = GTK_FLOW_BOX_CLASS (gd_main_icon_box_parent_class)->move_cursor (flow_box, step, count);
+      goto out;
+    }
+
+  fake_event = gdk_event_copy (event);
+  fake_event->key.state |= GDK_CONTROL_MASK;
+  fake_event->key.state &= ~GDK_SHIFT_MASK;
+
+  gtk_main_do_event (fake_event);
+  res = GDK_EVENT_STOP;
+
+ out:
+  g_clear_pointer (&fake_event, (GDestroyNotify) gdk_event_free);
+  g_clear_pointer (&event, (GDestroyNotify) gdk_event_free);
+  return res;
+}
+
+static void
+gd_main_icon_box_remove (GtkContainer *container, GtkWidget *widget)
+{
+  GdMainIconBox *self = GD_MAIN_ICON_BOX (container);
+  GdMainIconBoxPrivate *priv;
+
+  priv = gd_main_icon_box_get_instance_private (self);
+
+  GTK_CONTAINER_CLASS (gd_main_icon_box_parent_class)->remove (container, widget);
+
+  if (priv->selection_changed)
+    {
+      g_signal_emit_by_name (self, "selection-changed");
+      priv->selection_changed = FALSE;
+    }
+}
+
+static void
+gd_main_icon_box_select_all_flow_box (GtkFlowBox *flow_box)
+{
+  GdMainIconBox *self = GD_MAIN_ICON_BOX (flow_box);
+  GdMainIconBoxPrivate *priv;
+
+  priv = gd_main_icon_box_get_instance_private (self);
+
+  GTK_FLOW_BOX_CLASS (gd_main_icon_box_parent_class)->select_all (flow_box);
+
+  if (priv->selection_changed)
+    {
+      g_signal_emit_by_name (self, "selection-changed");
+      priv->selection_changed = FALSE;
+    }
+}
+
+static void
+gd_main_icon_box_selected_children_changed (GtkFlowBox *flow_box)
+{
+  GdMainIconBox *self = GD_MAIN_ICON_BOX (flow_box);
+  GdMainIconBoxPrivate *priv;
+
+  priv = gd_main_icon_box_get_instance_private (self);
+
+  GTK_FLOW_BOX_CLASS (gd_main_icon_box_parent_class)->selected_children_changed (flow_box);
+
+  priv->selection_changed = TRUE;
+
+  /* When a range selection is attempted, we override GtkFlowBox's
+   * default behaviour by changing the selection ourselves. Therefore,
+   * there is no need to update the check buttons until the final
+   * selection is available.
+   */
+  if (!priv->key_shift_pressed && !priv->left_button_shift_released)
+    {
+      GList *children;
+      GList *l;
+
+      children = gtk_container_get_children (GTK_CONTAINER (self));
+      for (l = children; l != NULL; l = l->next)
+        {
+          GtkFlowBoxChild *child = GTK_FLOW_BOX_CHILD (l->data);
+          gboolean selected;
+
+          /* Work around the fact that GtkFlowBoxChild:selected is not
+           * a property.
+           */
+          selected = gtk_flow_box_child_is_selected (child);
+          gd_main_box_child_set_selected (GD_MAIN_BOX_CHILD (child), selected);
+        }
+
+      g_list_free (children);
+    }
+}
+
+static void
+gd_main_icon_box_unselect_all_flow_box (GtkFlowBox *flow_box)
+{
+  GdMainIconBox *self = GD_MAIN_ICON_BOX (flow_box);
+  GdMainIconBoxPrivate *priv;
+
+  priv = gd_main_icon_box_get_instance_private (self);
+
+  GTK_FLOW_BOX_CLASS (gd_main_icon_box_parent_class)->unselect_all (flow_box);
+
+  if (priv->selection_changed)
+    {
+      g_signal_emit_by_name (self, "selection-changed");
+      priv->selection_changed = FALSE;
+    }
+}
+
+static void
+gd_main_icon_box_dispose (GObject *obj)
+{
+  GdMainIconBox *self = GD_MAIN_ICON_BOX (obj);
+  GdMainIconBoxPrivate *priv;
+
+  priv = gd_main_icon_box_get_instance_private (self);
+
+  g_clear_object (&priv->model);
+
+  G_OBJECT_CLASS (gd_main_icon_box_parent_class)->dispose (obj);
+}
+
+static void
+gd_main_icon_box_finalize (GObject *obj)
+{
+  GdMainIconBox *self = GD_MAIN_ICON_BOX (obj);
+  GdMainIconBoxPrivate *priv;
+
+  priv = gd_main_icon_box_get_instance_private (self);
+
+  g_free (priv->last_selected_id);
+
+  G_OBJECT_CLASS (gd_main_icon_box_parent_class)->finalize (obj);
+}
+
+static void
+gd_main_icon_box_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+  GdMainIconBox *self = GD_MAIN_ICON_BOX (object);
+
+  switch (property_id)
+    {
+    case PROP_LAST_SELECTED_ID:
+      g_value_set_string (value, gd_main_icon_box_get_last_selected_id (GD_MAIN_BOX_GENERIC (self)));
+      break;
+    case PROP_MODEL:
+      g_value_set_object (value, gd_main_icon_box_get_model (GD_MAIN_BOX_GENERIC (self)));
+      break;
+    case PROP_SELECTION_MODE:
+      g_value_set_boolean (value, gd_main_icon_box_get_selection_mode (self));
+      break;
+    case PROP_SHOW_PRIMARY_TEXT:
+      g_value_set_boolean (value, gd_main_icon_box_get_show_primary_text (self));
+      break;
+    case PROP_SHOW_SECONDARY_TEXT:
+      g_value_set_boolean (value, gd_main_icon_box_get_show_secondary_text (self));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gd_main_icon_box_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
+{
+  GdMainIconBox *self = GD_MAIN_ICON_BOX (object);
+
+  switch (property_id)
+    {
+    case PROP_MODEL:
+      gd_main_icon_box_set_model (self, g_value_get_object (value));
+      break;
+    case PROP_SELECTION_MODE:
+      gd_main_icon_box_set_selection_mode (self, g_value_get_boolean (value));
+      break;
+    case PROP_SHOW_PRIMARY_TEXT:
+      gd_main_icon_box_set_show_primary_text (self, g_value_get_boolean (value));
+      break;
+    case PROP_SHOW_SECONDARY_TEXT:
+      gd_main_icon_box_set_show_secondary_text (self, g_value_get_boolean (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gd_main_icon_box_init (GdMainIconBox *self)
+{
+  GdMainIconBoxPrivate *priv;
+  const GtkTargetEntry targets[] = { { (gchar *) "text/uri-list", GTK_TARGET_OTHER_APP, 0 } };
+
+  priv = gd_main_icon_box_get_instance_private (self);
+
+  gtk_widget_set_can_focus (GTK_WIDGET (self), TRUE);
+  gtk_flow_box_set_homogeneous (GTK_FLOW_BOX (self), TRUE);
+  gtk_flow_box_set_min_children_per_line (GTK_FLOW_BOX (self), 3);
+  gtk_flow_box_set_selection_mode (GTK_FLOW_BOX (self), GTK_SELECTION_NONE);
+
+  /* We need to ensure that rubberband selection and DnD don't step
+   * on each others toes. We set start_button_mask to 0 to retain
+   * control over when to begin a drag.
+   */
+  gtk_drag_source_set (GTK_WIDGET (self), 0, targets, G_N_ELEMENTS (targets), GDK_ACTION_COPY);
+
+  priv->dnd_button = -1;
+  priv->dnd_start_x = -1.0;
+  priv->dnd_start_y = -1.0;
+}
+
+static void
+gd_main_icon_box_class_init (GdMainIconBoxClass *klass)
+{
+  GObjectClass *oclass = G_OBJECT_CLASS (klass);
+  GtkContainerClass *cclass = GTK_CONTAINER_CLASS (klass);
+  GtkFlowBoxClass *fbclass = GTK_FLOW_BOX_CLASS (klass);
+  GtkWidgetClass *wclass = GTK_WIDGET_CLASS (klass);
+  GtkBindingSet *binding_set;
+  GdkModifierType activate_modifiers[] = { 0, /* Otherwise it will go to GtkFlowBoxChild::activate. */
+                                           GDK_SHIFT_MASK,
+                                           GDK_CONTROL_MASK,
+                                           GDK_SHIFT_MASK | GDK_CONTROL_MASK };
+  guint i;
+
+  binding_set = gtk_binding_set_by_class (klass);
+
+  oclass->dispose = gd_main_icon_box_dispose;
+  oclass->finalize = gd_main_icon_box_finalize;
+  oclass->get_property = gd_main_icon_box_get_property;
+  oclass->set_property = gd_main_icon_box_set_property;
+  wclass->button_press_event = gd_main_icon_box_button_press_event;
+  wclass->button_release_event = gd_main_icon_box_button_release_event;
+  wclass->drag_begin = gd_main_icon_box_drag_begin;
+  wclass->drag_data_get = gd_main_icon_box_drag_data_get;
+  wclass->focus = gd_main_icon_box_focus;
+  wclass->motion_notify_event = gd_main_icon_box_motion_notify_event;
+  cclass->remove = gd_main_icon_box_remove;
+  fbclass->activate_cursor_child = gd_main_icon_box_activate_cursor_child;
+  fbclass->child_activated = gd_main_icon_box_child_activated;
+  fbclass->move_cursor = gd_main_icon_box_move_cursor;
+  fbclass->select_all = gd_main_icon_box_select_all_flow_box;
+  fbclass->selected_children_changed = gd_main_icon_box_selected_children_changed;
+  fbclass->unselect_all = gd_main_icon_box_unselect_all_flow_box;
+
+  g_object_class_override_property (oclass, PROP_LAST_SELECTED_ID, "last-selected-id");
+  g_object_class_override_property (oclass, PROP_MODEL, "model");
+  g_object_class_override_property (oclass, PROP_SELECTION_MODE, "gd-selection-mode");
+  g_object_class_override_property (oclass, PROP_SHOW_PRIMARY_TEXT, "show-primary-text");
+  g_object_class_override_property (oclass, PROP_SHOW_SECONDARY_TEXT, "show-secondary-text");
+
+  for (i = 0; i < G_N_ELEMENTS (activate_modifiers); i++)
+    {
+      gtk_binding_entry_add_signal (binding_set,
+                                    GDK_KEY_space, activate_modifiers[i],
+                                    "activate-cursor-child",
+                                    0);
+      gtk_binding_entry_add_signal (binding_set,
+                                    GDK_KEY_KP_Space, activate_modifiers[i],
+                                    "activate-cursor-child",
+                                    0);
+      gtk_binding_entry_add_signal (binding_set,
+                                    GDK_KEY_Return, activate_modifiers[i],
+                                    "activate-cursor-child",
+                                    0);
+      gtk_binding_entry_add_signal (binding_set,
+                                    GDK_KEY_ISO_Enter, activate_modifiers[i],
+                                    "activate-cursor-child",
+                                    0);
+      gtk_binding_entry_add_signal (binding_set,
+                                    GDK_KEY_KP_Enter, activate_modifiers[i],
+                                    "activate-cursor-child",
+                                    0);
+    }
+}
+
+static void
+gd_main_box_generic_interface_init (GdMainBoxGenericInterface *iface)
+{
+  iface->get_child_at_index = gd_main_icon_box_get_child_at_index;
+  iface->get_last_selected_id = gd_main_icon_box_get_last_selected_id;
+  iface->get_model = gd_main_icon_box_get_model;
+  iface->get_selected_children = gd_main_icon_box_get_selected_children;
+  iface->select_all = gd_main_icon_box_select_all_generic;
+  iface->select_child = gd_main_icon_box_select_child;
+  iface->unselect_all = gd_main_icon_box_unselect_all_generic;
+  iface->unselect_child = gd_main_icon_box_unselect_child;
+}
+
+GtkWidget *
+gd_main_icon_box_new (void)
+{
+  return g_object_new (GD_TYPE_MAIN_ICON_BOX, NULL);
+}
diff --git a/subprojects/libgd/libgd/gd-main-icon-box.h b/subprojects/libgd/libgd/gd-main-icon-box.h
new file mode 100644
index 0000000..5dc60fe
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-main-icon-box.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2016 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by 
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Lesser General Public 
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License 
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Debarshi Ray <debarshir gnome org>
+ *
+ */
+
+#ifndef __GD_MAIN_ICON_BOX_H__
+#define __GD_MAIN_ICON_BOX_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GD_TYPE_MAIN_ICON_BOX gd_main_icon_box_get_type()
+G_DECLARE_DERIVABLE_TYPE (GdMainIconBox, gd_main_icon_box, GD, MAIN_ICON_BOX, GtkFlowBox)
+
+struct _GdMainIconBoxClass
+{
+  GtkFlowBoxClass parent_class;
+
+  /* signals */
+  gboolean  (* move_cursor)            (GdMainIconBox *self, GtkMovementStep step, gint count);
+};
+
+GtkWidget * gd_main_icon_box_new (void);
+
+G_END_DECLS
+
+#endif /* __GD_MAIN_ICON_BOX_H__ */
diff --git a/subprojects/libgd/libgd/gd-main-icon-view.c b/subprojects/libgd/libgd/gd-main-icon-view.c
new file mode 100644
index 0000000..bff4d15
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-main-icon-view.c
@@ -0,0 +1,433 @@
+/*
+ * Copyright (c) 2011 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by 
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Lesser General Public 
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License 
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Cosimo Cecchi <cosimoc redhat com>
+ *
+ */
+
+#include "gd-main-icon-view.h"
+#include "gd-main-view-generic.h"
+#include "gd-toggle-pixbuf-renderer.h"
+#include "gd-two-lines-renderer.h"
+
+#include <math.h>
+#include <glib/gi18n.h>
+#include <cairo-gobject.h>
+
+#define VIEW_ITEM_WIDTH 140
+#define VIEW_ITEM_WRAP_WIDTH 128
+#define VIEW_COLUMN_SPACING 20
+#define VIEW_MARGIN 16
+
+struct _GdMainIconViewPrivate {
+  GtkCellRenderer *pixbuf_cell;
+  GtkCellRenderer *text_cell;
+  gboolean selection_mode;
+};
+
+static void gd_main_view_generic_iface_init (GdMainViewGenericIface *iface);
+G_DEFINE_TYPE_WITH_CODE (GdMainIconView, gd_main_icon_view, GTK_TYPE_ICON_VIEW,
+                         G_ADD_PRIVATE (GdMainIconView)
+                         G_IMPLEMENT_INTERFACE (GD_TYPE_MAIN_VIEW_GENERIC,
+                                                gd_main_view_generic_iface_init))
+
+static GtkTreePath*
+get_source_row (GdkDragContext *context)
+{
+  GtkTreeRowReference *ref;
+
+  ref = g_object_get_data (G_OBJECT (context), "gtk-icon-view-source-row");
+
+  if (ref)
+    return gtk_tree_row_reference_get_path (ref);
+  else
+    return NULL;
+}
+
+static void
+set_attributes_from_model (GdMainIconView *self)
+{
+  GdMainIconViewPrivate *priv;
+  GtkTreeModel *model = gtk_icon_view_get_model (GTK_ICON_VIEW (self));
+  GtkCellLayout *layout = GTK_CELL_LAYOUT (self);
+  GType icon_gtype;
+
+  priv = gd_main_icon_view_get_instance_private (self);
+
+  if (!model)
+    return;
+
+  gtk_cell_layout_clear_attributes (layout, priv->pixbuf_cell);
+  gtk_cell_layout_clear_attributes (layout, priv->text_cell);
+
+  gtk_cell_layout_add_attribute (layout, priv->pixbuf_cell,
+                                 "active", GD_MAIN_COLUMN_SELECTED);
+  gtk_cell_layout_add_attribute (layout, priv->pixbuf_cell,
+                                 "pulse", GD_MAIN_COLUMN_PULSE);
+
+  icon_gtype = gtk_tree_model_get_column_type (model, GD_MAIN_COLUMN_ICON);
+  if (icon_gtype == GDK_TYPE_PIXBUF)
+    gtk_cell_layout_add_attribute (layout, priv->pixbuf_cell,
+                                  "pixbuf", GD_MAIN_COLUMN_ICON);
+  else if (icon_gtype == CAIRO_GOBJECT_TYPE_SURFACE)
+    gtk_cell_layout_add_attribute (layout, priv->pixbuf_cell,
+                                  "surface", GD_MAIN_COLUMN_ICON);
+  else
+    g_assert_not_reached ();
+
+  gtk_cell_layout_add_attribute (layout, priv->text_cell,
+                                 "text", GD_MAIN_COLUMN_PRIMARY_TEXT);
+  gtk_cell_layout_add_attribute (layout, priv->text_cell,
+                                 "line-two", GD_MAIN_COLUMN_SECONDARY_TEXT);
+}
+
+static void
+gd_main_icon_view_drag_data_get (GtkWidget *widget,
+                                 GdkDragContext *drag_context,
+                                 GtkSelectionData *data,
+                                 guint info,
+                                 guint time)
+{
+  GdMainIconView *self = GD_MAIN_ICON_VIEW (widget);
+  GdMainIconViewPrivate *priv;
+  GtkTreeModel *model = gtk_icon_view_get_model (GTK_ICON_VIEW (self));
+
+  priv = gd_main_icon_view_get_instance_private (self);
+
+  if (info != 0)
+    return;
+
+  _gd_main_view_generic_dnd_common (model, priv->selection_mode,
+                                    get_source_row (drag_context), data);
+
+  GTK_WIDGET_CLASS (gd_main_icon_view_parent_class)->drag_data_get (widget, drag_context,
+                                                                    data, info, time);
+}
+
+static void
+gd_main_icon_view_constructed (GObject *obj)
+{
+  GdMainIconView *self = GD_MAIN_ICON_VIEW (obj);
+  GdMainIconViewPrivate *priv;
+  GtkCellRenderer *cell;
+  const GtkTargetEntry targets[] = {
+    { (char *) "text/uri-list", GTK_TARGET_OTHER_APP, 0 }
+  };
+
+  priv = gd_main_icon_view_get_instance_private (self);
+
+  G_OBJECT_CLASS (gd_main_icon_view_parent_class)->constructed (obj);
+
+  gtk_widget_set_hexpand (GTK_WIDGET (self), TRUE);
+  gtk_widget_set_vexpand (GTK_WIDGET (self), TRUE);
+  gtk_icon_view_set_selection_mode (GTK_ICON_VIEW (self), GTK_SELECTION_NONE);
+
+  g_object_set (self,
+                "column-spacing", VIEW_COLUMN_SPACING,
+                "margin", VIEW_MARGIN,
+                NULL);
+
+  priv->pixbuf_cell = cell = gd_toggle_pixbuf_renderer_new ();
+  g_object_set (cell,
+                "xalign", 0.5,
+                "yalign", 0.5,
+                NULL);
+  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (self), cell, FALSE);
+
+  priv->text_cell = cell = gd_two_lines_renderer_new ();
+  g_object_set (cell,
+                "xalign", 0.5,
+                "alignment", PANGO_ALIGN_CENTER,
+                "wrap-mode", PANGO_WRAP_WORD_CHAR,
+                "wrap-width", VIEW_ITEM_WRAP_WIDTH,
+                "text-lines", 3,
+                NULL);
+  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (self), cell, FALSE);
+
+  set_attributes_from_model (self);
+
+  gtk_icon_view_enable_model_drag_source (GTK_ICON_VIEW (self),
+                                          GDK_BUTTON1_MASK,
+                                          targets, 1,
+                                          GDK_ACTION_COPY);
+}
+
+static void
+path_from_line_rects (cairo_t *cr,
+                     GdkRectangle *lines,
+                     int n_lines)
+{
+  int start_line, end_line;
+  GdkRectangle *r;
+  int i;
+
+  /* Join rows vertically by extending to the middle */
+  for (i = 0; i < n_lines - 1; i++)
+    {
+      GdkRectangle *r1 = &lines[i];
+      GdkRectangle *r2 = &lines[i+1];
+      int gap = r2->y - (r1->y + r1->height);
+      int old_y;
+
+      r1->height += gap / 2;
+      old_y = r2->y;
+      r2->y = r1->y + r1->height;
+      r2->height += old_y - r2->y;
+    }
+
+  cairo_new_path (cr);
+  start_line = 0;
+
+  do
+    {
+      for (i = start_line; i < n_lines; i++)
+       {
+         r = &lines[i];
+         if (i == start_line)
+           cairo_move_to (cr, r->x + r->width, r->y);
+         else
+           cairo_line_to (cr, r->x + r->width, r->y);
+         cairo_line_to (cr, r->x + r->width, r->y + r->height);
+
+         if (i < n_lines - 1 &&
+             (r->x + r->width < lines[i+1].x ||
+              r->x > lines[i+1].x + lines[i+1].width))
+           {
+             i++;
+             break;
+           }
+       }
+      end_line = i;
+      for (i = end_line - 1; i >= start_line; i--)
+       {
+         r = &lines[i];
+         cairo_line_to (cr, r->x, r->y + r->height);
+         cairo_line_to (cr, r->x, r->y);
+       }
+      cairo_close_path (cr);
+      start_line = end_line;
+    }
+  while (end_line < n_lines);
+}
+
+static gboolean
+gd_main_icon_view_draw (GtkWidget *widget,
+                       cairo_t   *cr)
+{
+  GdMainIconView *self = GD_MAIN_ICON_VIEW (widget);
+  GtkAllocation allocation;
+  GtkStyleContext *context;
+  GdkRectangle line_rect;
+  GdkRectangle rect;
+  GtkTreePath *path;
+  GArray *lines;
+  GtkTreePath *rubberband_start, *rubberband_end;
+
+  GTK_WIDGET_CLASS (gd_main_icon_view_parent_class)->draw (widget, cr);
+
+  _gd_main_view_generic_get_rubberband_range (GD_MAIN_VIEW_GENERIC (self),
+                                             &rubberband_start, &rubberband_end);
+
+  if (rubberband_start)
+    {
+      cairo_save (cr);
+
+      context = gtk_widget_get_style_context (widget);
+
+      gtk_style_context_save (context);
+      gtk_style_context_add_class (context, GTK_STYLE_CLASS_RUBBERBAND);
+
+      path = gtk_tree_path_copy (rubberband_start);
+
+      line_rect.width = 0;
+      lines = g_array_new (FALSE, FALSE, sizeof (GdkRectangle));
+
+      while (gtk_tree_path_compare (path, rubberband_end) <= 0)
+       {
+         if (gtk_icon_view_get_cell_rect (GTK_ICON_VIEW (widget),
+                                          path,
+                                          NULL, &rect))
+           {
+             if (line_rect.width == 0)
+               line_rect = rect;
+             else
+               {
+                 if (rect.y == line_rect.y)
+                   gdk_rectangle_union (&rect, &line_rect, &line_rect);
+                 else
+                   {
+                     g_array_append_val (lines, line_rect);
+                     line_rect = rect;
+                   }
+               }
+           }
+         gtk_tree_path_next (path);
+       }
+
+      if (line_rect.width != 0)
+       g_array_append_val (lines, line_rect);
+
+      if (lines->len > 0)
+       {
+         GtkStateFlags state;
+         cairo_path_t *path;
+         GtkBorder border;
+         GdkRGBA border_color;
+
+         path_from_line_rects (cr, (GdkRectangle *)lines->data, lines->len);
+
+         /* For some reason we need to copy and reapply the path, or it gets
+            eaten by gtk_render_background() */
+         path = cairo_copy_path (cr);
+
+         cairo_save (cr);
+         cairo_clip (cr);
+         gtk_widget_get_allocation (widget, &allocation);
+         gtk_render_background (context, cr,
+                                0, 0,
+                                allocation.width, allocation.height);
+         cairo_restore (cr);
+
+         cairo_append_path (cr, path);
+         cairo_path_destroy (path);
+
+         state = gtk_widget_get_state_flags (widget);
+         gtk_style_context_get_border_color (context,
+                                             state,
+                                             &border_color);
+         gtk_style_context_get_border (context, state,
+                                       &border);
+
+         cairo_set_line_width (cr, border.left);
+         gdk_cairo_set_source_rgba (cr, &border_color);
+         cairo_stroke (cr);
+       }
+      g_array_free (lines, TRUE);
+
+      gtk_tree_path_free (path);
+
+      gtk_style_context_restore (context);
+      cairo_restore (cr);
+    }
+
+  return FALSE;
+}
+
+static void
+gd_main_icon_view_class_init (GdMainIconViewClass *klass)
+{
+  GObjectClass *oclass = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *wclass = GTK_WIDGET_CLASS (klass);
+  GtkBindingSet *binding_set;
+  GdkModifierType activate_modifiers[] = { GDK_SHIFT_MASK, GDK_CONTROL_MASK, GDK_SHIFT_MASK | 
GDK_CONTROL_MASK };
+  guint i;
+
+  binding_set = gtk_binding_set_by_class (klass);
+
+  oclass->constructed = gd_main_icon_view_constructed;
+  wclass->drag_data_get = gd_main_icon_view_drag_data_get;
+  wclass->draw = gd_main_icon_view_draw;
+
+  gtk_widget_class_install_style_property (wclass,
+                                           g_param_spec_int ("check-icon-size",
+                                                             "Check icon size",
+                                                             "Check icon size",
+                                                             -1, G_MAXINT, 40,
+                                                             G_PARAM_READWRITE));
+
+  for (i = 0; i < G_N_ELEMENTS (activate_modifiers); i++)
+    {
+      gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, activate_modifiers[i],
+                                   "activate-cursor-item", 0);
+      gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Space, activate_modifiers[i],
+                                   "activate-cursor-item", 0);
+      gtk_binding_entry_add_signal (binding_set, GDK_KEY_Return, activate_modifiers[i],
+                                   "activate-cursor-item", 0);
+      gtk_binding_entry_add_signal (binding_set, GDK_KEY_ISO_Enter, activate_modifiers[i],
+                                   "activate-cursor-item", 0);
+      gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Enter, activate_modifiers[i],
+                                   "activate-cursor-item", 0);
+    }
+}
+
+static void
+gd_main_icon_view_init (GdMainIconView *self)
+{
+  g_signal_connect (self, "notify::model",
+                   G_CALLBACK (set_attributes_from_model), NULL);
+}
+
+static GtkTreePath *
+gd_main_icon_view_get_path_at_pos (GdMainViewGeneric *mv,
+                                   gint x,
+                                   gint y)
+{
+  return gtk_icon_view_get_path_at_pos (GTK_ICON_VIEW (mv), x, y);
+}
+
+static void
+gd_main_icon_view_set_selection_mode (GdMainViewGeneric *mv,
+                                      gboolean selection_mode)
+{
+  GdMainIconView *self = GD_MAIN_ICON_VIEW (mv);
+  GdMainIconViewPrivate *priv;
+
+  priv = gd_main_icon_view_get_instance_private (self);
+
+  priv->selection_mode = selection_mode;
+
+  g_object_set (priv->pixbuf_cell,
+                "toggle-visible", selection_mode,
+                NULL);
+  gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+static void
+gd_main_icon_view_scroll_to_path (GdMainViewGeneric *mv,
+                                  GtkTreePath *path)
+{
+  gtk_icon_view_scroll_to_path (GTK_ICON_VIEW (mv), path, TRUE, 0.5, 0.5);
+}
+
+static void
+gd_main_icon_view_set_model (GdMainViewGeneric *mv,
+                             GtkTreeModel *model)
+{
+  gtk_icon_view_set_model (GTK_ICON_VIEW (mv), model);
+}
+
+static GtkTreeModel *
+gd_main_icon_view_get_model (GdMainViewGeneric *mv)
+{
+  return gtk_icon_view_get_model (GTK_ICON_VIEW (mv));
+}
+
+static void
+gd_main_view_generic_iface_init (GdMainViewGenericIface *iface)
+{
+  iface->set_model = gd_main_icon_view_set_model;
+  iface->get_model = gd_main_icon_view_get_model;
+  iface->get_path_at_pos = gd_main_icon_view_get_path_at_pos;
+  iface->scroll_to_path = gd_main_icon_view_scroll_to_path;
+  iface->set_selection_mode = gd_main_icon_view_set_selection_mode;
+}
+
+GtkWidget *
+gd_main_icon_view_new (void)
+{
+  return g_object_new (GD_TYPE_MAIN_ICON_VIEW, NULL);
+}
diff --git a/subprojects/libgd/libgd/gd-main-icon-view.h b/subprojects/libgd/libgd/gd-main-icon-view.h
new file mode 100644
index 0000000..c2d1c22
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-main-icon-view.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2011 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by 
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Lesser General Public 
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License 
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Cosimo Cecchi <cosimoc redhat com>
+ *
+ */
+
+#ifndef __GD_MAIN_ICON_VIEW_H__
+#define __GD_MAIN_ICON_VIEW_H__
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GD_TYPE_MAIN_ICON_VIEW gd_main_icon_view_get_type()
+
+#define GD_MAIN_ICON_VIEW(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+   GD_TYPE_MAIN_ICON_VIEW, GdMainIconView))
+
+#define GD_MAIN_ICON_VIEW_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), \
+   GD_TYPE_MAIN_ICON_VIEW, GdMainIconViewClass))
+
+#define GD_IS_MAIN_ICON_VIEW(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+   GD_TYPE_MAIN_ICON_VIEW))
+
+#define GD_IS_MAIN_ICON_VIEW_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+   GD_TYPE_MAIN_ICON_VIEW))
+
+#define GD_MAIN_ICON_VIEW_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+   GD_TYPE_MAIN_ICON_VIEW, GdMainIconViewClass))
+
+typedef struct _GdMainIconView GdMainIconView;
+typedef struct _GdMainIconViewClass GdMainIconViewClass;
+typedef struct _GdMainIconViewPrivate GdMainIconViewPrivate;
+
+struct _GdMainIconView
+{
+  GtkIconView parent;
+};
+
+struct _GdMainIconViewClass
+{
+  GtkIconViewClass parent_class;
+};
+
+GType gd_main_icon_view_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gd_main_icon_view_new (void);
+
+G_END_DECLS
+
+#endif /* __GD_MAIN_ICON_VIEW_H__ */
diff --git a/subprojects/libgd/libgd/gd-main-list-view.c b/subprojects/libgd/libgd/gd-main-list-view.c
new file mode 100644
index 0000000..26128af
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-main-list-view.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright (c) 2011 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by 
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Lesser General Public 
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License 
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Cosimo Cecchi <cosimoc redhat com>
+ *
+ */
+
+#include "gd-main-list-view.h"
+#include "gd-main-view-generic.h"
+#include "gd-two-lines-renderer.h"
+
+#include <cairo-gobject.h>
+#include <glib/gi18n.h>
+
+struct _GdMainListViewPrivate {
+  GtkTreeViewColumn *tree_col;
+  GtkCellRenderer *pixbuf_cell;
+  GtkCellRenderer *selection_cell;
+  GtkCellRenderer *text_cell;
+
+  gboolean selection_mode;
+};
+
+static void gd_main_view_generic_iface_init (GdMainViewGenericIface *iface);
+G_DEFINE_TYPE_WITH_CODE (GdMainListView, gd_main_list_view, GTK_TYPE_TREE_VIEW,
+                         G_IMPLEMENT_INTERFACE (GD_TYPE_MAIN_VIEW_GENERIC,
+                                                gd_main_view_generic_iface_init))
+
+static gboolean gd_main_list_view_draw (GtkWidget *widget,
+                                       cairo_t   *cr);
+
+static GtkTreePath*
+get_source_row (GdkDragContext *context)
+{
+  GtkTreeRowReference *ref =
+    g_object_get_data (G_OBJECT (context), "gtk-tree-view-source-row");
+
+  if (ref)
+    return gtk_tree_row_reference_get_path (ref);
+  else
+    return NULL;
+}
+
+static void
+set_attributes_from_model (GdMainListView *self)
+{
+  GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
+  GType icon_gtype;
+
+  if (!model)
+    return;
+
+  gtk_tree_view_column_clear_attributes (self->priv->tree_col, self->priv->pixbuf_cell);
+  gtk_tree_view_column_clear_attributes (self->priv->tree_col, self->priv->selection_cell);
+  gtk_tree_view_column_clear_attributes (self->priv->tree_col, self->priv->text_cell);
+
+
+  gtk_tree_view_column_add_attribute (self->priv->tree_col, self->priv->selection_cell,
+                                      "active", GD_MAIN_COLUMN_SELECTED);
+
+  icon_gtype = gtk_tree_model_get_column_type (model, GD_MAIN_COLUMN_ICON);
+  if (icon_gtype == GDK_TYPE_PIXBUF)
+    gtk_tree_view_column_add_attribute (self->priv->tree_col, self->priv->pixbuf_cell,
+                                       "pixbuf", GD_MAIN_COLUMN_ICON);
+  else if (icon_gtype == CAIRO_GOBJECT_TYPE_SURFACE)
+    gtk_tree_view_column_add_attribute (self->priv->tree_col, self->priv->pixbuf_cell,
+                                       "surface", GD_MAIN_COLUMN_ICON);
+  else
+    g_assert_not_reached ();
+
+  gtk_tree_view_column_add_attribute (self->priv->tree_col, self->priv->text_cell,
+                                      "text", GD_MAIN_COLUMN_PRIMARY_TEXT);
+  gtk_tree_view_column_add_attribute (self->priv->tree_col, self->priv->text_cell,
+                                      "line-two", GD_MAIN_COLUMN_SECONDARY_TEXT);
+}
+
+static void
+gd_main_list_view_drag_data_get (GtkWidget *widget,
+                                 GdkDragContext *drag_context,
+                                 GtkSelectionData *data,
+                                 guint info,
+                                 guint time)
+{
+  GdMainListView *self = GD_MAIN_LIST_VIEW (widget);
+  GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
+
+  if (info != 0)
+    return;
+
+  _gd_main_view_generic_dnd_common (model,
+                                    self->priv->selection_mode,
+                                    get_source_row (drag_context), data);
+
+  GTK_WIDGET_CLASS (gd_main_list_view_parent_class)->drag_data_get (widget, drag_context,
+                                                                    data, info, time);
+}
+
+static void
+gd_main_list_view_constructed (GObject *obj)
+{
+  GdMainListView *self = GD_MAIN_LIST_VIEW (obj);
+  GtkCellRenderer *cell;
+  GtkTreeSelection *selection;
+  const GtkTargetEntry targets[] = {
+    { "text/uri-list", GTK_TARGET_OTHER_APP, 0 }
+  };
+
+  G_OBJECT_CLASS (gd_main_list_view_parent_class)->constructed (obj);
+
+  gtk_widget_set_hexpand (GTK_WIDGET (self), TRUE);
+  gtk_widget_set_vexpand (GTK_WIDGET (self), TRUE);
+
+  g_object_set (self,
+                "headers-visible", FALSE,
+                "enable-search", FALSE,
+                NULL);
+
+  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
+  gtk_tree_selection_set_mode (selection, GTK_SELECTION_NONE);
+
+  self->priv->tree_col = gtk_tree_view_column_new ();
+  gtk_tree_view_append_column (GTK_TREE_VIEW (self), self->priv->tree_col);
+
+  self->priv->selection_cell = cell = gtk_cell_renderer_toggle_new ();
+  g_object_set (cell, 
+                "visible", FALSE,
+                "xpad", 12,
+                "xalign", 1.0,
+                NULL);
+  gtk_tree_view_column_pack_start (self->priv->tree_col, cell, FALSE);
+
+  self->priv->pixbuf_cell = cell = gtk_cell_renderer_pixbuf_new ();
+  g_object_set (cell,
+                "xalign", 0.5,
+                "yalign", 0.5,
+                "xpad", 12,
+                "ypad", 2,
+                NULL);
+  gtk_tree_view_column_pack_start (self->priv->tree_col, cell, FALSE);
+
+  self->priv->text_cell = cell = gd_two_lines_renderer_new ();
+  g_object_set (cell,
+                "xalign", 0.0,
+                "wrap-mode", PANGO_WRAP_WORD_CHAR,
+                "xpad", 12,
+                "text-lines", 2,
+                NULL);
+  gtk_tree_view_column_pack_start (self->priv->tree_col, cell, TRUE);
+
+  set_attributes_from_model (self);
+
+  gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (self),
+                                          GDK_BUTTON1_MASK,
+                                          targets, 1,
+                                          GDK_ACTION_COPY);
+}
+
+static void
+gd_main_list_view_class_init (GdMainListViewClass *klass)
+{
+  GObjectClass *oclass = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *wclass = GTK_WIDGET_CLASS (klass);
+  GtkBindingSet *binding_set;
+  GdkModifierType activate_modifiers[] = { GDK_SHIFT_MASK, GDK_CONTROL_MASK, GDK_SHIFT_MASK | 
GDK_CONTROL_MASK };
+  guint i;
+
+  binding_set = gtk_binding_set_by_class (klass);
+
+  oclass->constructed = gd_main_list_view_constructed;
+  wclass->drag_data_get = gd_main_list_view_drag_data_get;
+  wclass->draw = gd_main_list_view_draw;
+
+  g_type_class_add_private (klass, sizeof (GdMainListViewPrivate));
+
+  for (i = 0; i < G_N_ELEMENTS (activate_modifiers); i++)
+    {
+      gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, activate_modifiers[i],
+                                   "select-cursor-row", 1,
+                                   G_TYPE_BOOLEAN, TRUE);
+      gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Space, activate_modifiers[i],
+                                   "select-cursor-row", 1,
+                                   G_TYPE_BOOLEAN, TRUE);
+      gtk_binding_entry_add_signal (binding_set, GDK_KEY_Return, activate_modifiers[i],
+                                   "select-cursor-row", 1,
+                                   G_TYPE_BOOLEAN, TRUE);
+      gtk_binding_entry_add_signal (binding_set, GDK_KEY_ISO_Enter, activate_modifiers[i],
+                                   "select-cursor-row", 1,
+                                   G_TYPE_BOOLEAN, TRUE);
+      gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Enter, activate_modifiers[i],
+                                   "select-cursor-row", 1,
+                                   G_TYPE_BOOLEAN, TRUE);
+    }
+
+}
+
+static void
+gd_main_list_view_init (GdMainListView *self)
+{
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GD_TYPE_MAIN_LIST_VIEW, GdMainListViewPrivate);
+
+  g_signal_connect (self, "notify::model",
+                   G_CALLBACK (set_attributes_from_model), NULL);
+}
+
+static GtkTreePath *
+gd_main_list_view_get_path_at_pos (GdMainViewGeneric *mv,
+                                   gint x,
+                                   gint y)
+{
+  GtkTreePath *path = NULL;
+
+  gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (mv), x, y, &path,
+                                 NULL, NULL, NULL);
+
+  return path;
+}
+
+static void
+gd_main_list_view_set_selection_mode (GdMainViewGeneric *mv,
+                                      gboolean selection_mode)
+{
+  GdMainListView *self = GD_MAIN_LIST_VIEW (mv);
+
+  self->priv->selection_mode = selection_mode;
+
+  g_object_set (self->priv->selection_cell,
+                "visible", selection_mode,
+                NULL);
+  gtk_tree_view_column_queue_resize (self->priv->tree_col);
+}
+
+static gboolean
+gd_main_list_view_draw (GtkWidget *widget,
+                       cairo_t   *cr)
+{
+  GdMainListView *self = GD_MAIN_LIST_VIEW (widget);
+  GtkStyleContext *context;
+  GdkRectangle lines_rect;
+  GdkRectangle rect;
+  GtkTreePath *path;
+  GtkTreePath *rubberband_start, *rubberband_end;
+
+  GTK_WIDGET_CLASS (gd_main_list_view_parent_class)->draw (widget, cr);
+
+  _gd_main_view_generic_get_rubberband_range (GD_MAIN_VIEW_GENERIC (self),
+                                             &rubberband_start, &rubberband_end);
+
+  if (rubberband_start)
+    {
+      context = gtk_widget_get_style_context (widget);
+
+      gtk_style_context_save (context);
+      gtk_style_context_add_class (context, GTK_STYLE_CLASS_RUBBERBAND);
+
+      path = gtk_tree_path_copy (rubberband_start);
+
+      lines_rect.width = 0;
+
+      while (gtk_tree_path_compare (path, rubberband_end) <= 0)
+       {
+         gtk_tree_view_get_cell_area (GTK_TREE_VIEW (self),
+                                      path, self->priv->tree_col, &rect);
+         if (lines_rect.width == 0)
+           lines_rect = rect;
+         else
+           gdk_rectangle_union (&rect, &lines_rect, &lines_rect);
+
+         gtk_tree_path_next (path);
+       }
+      gtk_tree_path_free (path);
+
+      gtk_render_background (context, cr,
+                            lines_rect.x, lines_rect.y,
+                            lines_rect.width, lines_rect.height);
+      gtk_render_frame (context, cr,
+                            lines_rect.x, lines_rect.y,
+                            lines_rect.width, lines_rect.height);
+
+
+      gtk_style_context_restore (context);
+    }
+
+  return FALSE;
+}
+
+static void
+gd_main_list_view_scroll_to_path (GdMainViewGeneric *mv,
+                                  GtkTreePath *path)
+{
+  gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (mv), path, NULL, TRUE, 0.5, 0.5);
+}
+
+static void
+gd_main_list_view_set_model (GdMainViewGeneric *mv,
+                             GtkTreeModel *model)
+{
+  gtk_tree_view_set_model (GTK_TREE_VIEW (mv), model);
+}
+
+static GtkTreeModel *
+gd_main_list_view_get_model (GdMainViewGeneric *mv)
+{
+  return gtk_tree_view_get_model (GTK_TREE_VIEW (mv));
+}
+
+static void
+gd_main_view_generic_iface_init (GdMainViewGenericIface *iface)
+{
+  iface->set_model = gd_main_list_view_set_model;
+  iface->get_model = gd_main_list_view_get_model;
+  iface->get_path_at_pos = gd_main_list_view_get_path_at_pos;
+  iface->scroll_to_path = gd_main_list_view_scroll_to_path;
+  iface->set_selection_mode = gd_main_list_view_set_selection_mode;
+}
+
+void
+gd_main_list_view_add_renderer (GdMainListView *self,
+                                GtkCellRenderer *renderer,
+                                GtkTreeCellDataFunc func,
+                                gpointer user_data,
+                                GDestroyNotify destroy)
+{
+  gtk_tree_view_column_pack_start (self->priv->tree_col, renderer, FALSE);
+  gtk_tree_view_column_set_cell_data_func (self->priv->tree_col, renderer,
+                                           func, user_data, destroy);
+}
+
+GtkWidget *
+gd_main_list_view_new (void)
+{
+  return g_object_new (GD_TYPE_MAIN_LIST_VIEW, NULL);
+}
diff --git a/subprojects/libgd/libgd/gd-main-list-view.h b/subprojects/libgd/libgd/gd-main-list-view.h
new file mode 100644
index 0000000..317e9c4
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-main-list-view.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2011 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by 
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Lesser General Public 
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License 
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Cosimo Cecchi <cosimoc redhat com>
+ *
+ */
+
+#ifndef __GD_MAIN_LIST_VIEW_H__
+#define __GD_MAIN_LIST_VIEW_H__
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GD_TYPE_MAIN_LIST_VIEW gd_main_list_view_get_type()
+
+#define GD_MAIN_LIST_VIEW(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+   GD_TYPE_MAIN_LIST_VIEW, GdMainListView))
+
+#define GD_MAIN_LIST_VIEW_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), \
+   GD_TYPE_MAIN_LIST_VIEW, GdMainListViewClass))
+
+#define GD_IS_MAIN_LIST_VIEW(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+   GD_TYPE_MAIN_LIST_VIEW))
+
+#define GD_IS_MAIN_LIST_VIEW_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+   GD_TYPE_MAIN_LIST_VIEW))
+
+#define GD_MAIN_LIST_VIEW_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+   GD_TYPE_MAIN_LIST_VIEW, GdMainListViewClass))
+
+typedef struct _GdMainListView GdMainListView;
+typedef struct _GdMainListViewClass GdMainListViewClass;
+typedef struct _GdMainListViewPrivate GdMainListViewPrivate;
+
+struct _GdMainListView
+{
+  GtkTreeView parent;
+
+  GdMainListViewPrivate *priv;
+};
+
+struct _GdMainListViewClass
+{
+  GtkTreeViewClass parent_class;
+};
+
+GType gd_main_list_view_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gd_main_list_view_new (void);
+
+void gd_main_list_view_add_renderer (GdMainListView *self,
+                                     GtkCellRenderer *renderer,
+                                     GtkTreeCellDataFunc func,
+                                     gpointer user_data,
+                                     GDestroyNotify destroy);
+
+G_END_DECLS
+
+#endif /* __GD_MAIN_LIST_VIEW_H__ */
diff --git a/subprojects/libgd/libgd/gd-main-view-generic.c b/subprojects/libgd/libgd/gd-main-view-generic.c
new file mode 100644
index 0000000..51347e0
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-main-view-generic.c
@@ -0,0 +1,330 @@
+/*
+ * Copyright (c) 2011 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by 
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Lesser General Public 
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License 
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Cosimo Cecchi <cosimoc redhat com>
+ *
+ */
+
+#include "gd-main-view-generic.h"
+
+enum {
+  VIEW_SELECTION_CHANGED,
+  NUM_SIGNALS
+};
+
+static guint signals[NUM_SIGNALS] = { 0, };
+
+typedef GdMainViewGenericIface GdMainViewGenericInterface;
+G_DEFINE_INTERFACE (GdMainViewGeneric, gd_main_view_generic, GTK_TYPE_WIDGET)
+
+static void
+gd_main_view_generic_default_init (GdMainViewGenericInterface *iface)
+{
+  signals[VIEW_SELECTION_CHANGED] =
+    g_signal_new ("view-selection-changed",
+                  GD_TYPE_MAIN_VIEW_GENERIC,
+                  G_SIGNAL_RUN_LAST,
+                  0, NULL, NULL, NULL,
+                  G_TYPE_NONE, 0);
+}
+
+/**
+ * gd_main_view_generic_set_model:
+ * @self:
+ * @model: (allow-none):
+ *
+ */
+void
+gd_main_view_generic_set_model (GdMainViewGeneric *self,
+                                GtkTreeModel *model)
+{
+  GdMainViewGenericInterface *iface;
+
+  iface = GD_MAIN_VIEW_GENERIC_GET_IFACE (self);
+
+  (* iface->set_model) (self, model);
+}
+
+GtkTreePath *
+gd_main_view_generic_get_path_at_pos (GdMainViewGeneric *self,
+                                      gint x,
+                                      gint y)
+{
+  GdMainViewGenericInterface *iface;
+
+  iface = GD_MAIN_VIEW_GENERIC_GET_IFACE (self);
+
+  return (* iface->get_path_at_pos) (self, x, y);
+}
+
+void
+gd_main_view_generic_set_selection_mode (GdMainViewGeneric *self,
+                                         gboolean selection_mode)
+{
+  GdMainViewGenericInterface *iface;
+
+  iface = GD_MAIN_VIEW_GENERIC_GET_IFACE (self);
+
+  (* iface->set_selection_mode) (self, selection_mode);
+}
+
+
+typedef struct {
+  GtkTreePath *rubberband_start;
+  GtkTreePath *rubberband_end;
+} RubberbandInfo;
+
+static void
+rubber_band_info_destroy (RubberbandInfo *info)
+{
+  g_clear_pointer (&info->rubberband_start,
+                  gtk_tree_path_free);
+  g_clear_pointer (&info->rubberband_end,
+                  gtk_tree_path_free);
+  g_slice_free (RubberbandInfo, info);
+}
+
+static RubberbandInfo*
+get_rubber_band_info (GdMainViewGeneric *self)
+{
+  RubberbandInfo *info;
+
+  info = g_object_get_data (G_OBJECT (self), "gd-main-view-generic-rubber-band");
+  if (info == NULL)
+    {
+      info = g_slice_new0 (RubberbandInfo);
+      g_object_set_data_full (G_OBJECT (self), "gd-main-view-generic-rubber-band",
+                             info, (GDestroyNotify)rubber_band_info_destroy);
+    }
+
+  return info;
+}
+
+void
+gd_main_view_generic_set_rubberband_range (GdMainViewGeneric *self,
+                                          GtkTreePath *start,
+                                          GtkTreePath *end)
+{
+  RubberbandInfo *info;
+
+  info = get_rubber_band_info (self);
+
+  if (start == NULL || end == NULL)
+    {
+      g_clear_pointer (&info->rubberband_start,
+                      gtk_tree_path_free);
+      g_clear_pointer (&info->rubberband_end,
+                      gtk_tree_path_free);
+    }
+  else
+    {
+      if (gtk_tree_path_compare (start, end) < 0)
+       {
+         info->rubberband_start = gtk_tree_path_copy (start);
+         info->rubberband_end = gtk_tree_path_copy (end);
+       }
+      else
+       {
+         info->rubberband_start = gtk_tree_path_copy (end);
+         info->rubberband_end = gtk_tree_path_copy (start);
+       }
+    }
+
+  gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+void
+_gd_main_view_generic_get_rubberband_range (GdMainViewGeneric *self,
+                                           GtkTreePath **start,
+                                           GtkTreePath **end)
+{
+  RubberbandInfo *info;
+
+  info = get_rubber_band_info (self);
+
+  *start = info->rubberband_start;
+  *end = info->rubberband_end;
+}
+
+void
+gd_main_view_generic_scroll_to_path (GdMainViewGeneric *self,
+                                     GtkTreePath *path)
+{
+  GdMainViewGenericInterface *iface;
+
+  iface = GD_MAIN_VIEW_GENERIC_GET_IFACE (self);
+
+  (* iface->scroll_to_path) (self, path);
+}
+
+/**
+ * gd_main_view_generic_get_model:
+ *
+ * Returns: (transfer none): The associated model
+ */
+GtkTreeModel *
+gd_main_view_generic_get_model (GdMainViewGeneric *self)
+{
+  GdMainViewGenericInterface *iface;
+
+  iface = GD_MAIN_VIEW_GENERIC_GET_IFACE (self);
+
+  return (* iface->get_model) (self);
+}
+
+static gboolean
+build_selection_uris_foreach (GtkTreeModel *model,
+                              GtkTreePath *path,
+                              GtkTreeIter *iter,
+                              gpointer user_data)
+{
+  GPtrArray *ptr_array = user_data;
+  gchar *uri;
+  gboolean is_selected;
+
+  gtk_tree_model_get (model, iter,
+                      GD_MAIN_COLUMN_URI, &uri,
+                      GD_MAIN_COLUMN_SELECTED, &is_selected,
+                      -1);
+
+  if (is_selected)
+    g_ptr_array_add (ptr_array, uri);
+  else
+    g_free (uri);
+
+  return FALSE;
+}
+
+static gchar **
+model_get_selection_uris (GtkTreeModel *model)
+{
+  GPtrArray *ptr_array = g_ptr_array_new ();
+
+  gtk_tree_model_foreach (model,
+                          build_selection_uris_foreach,
+                          ptr_array);
+  
+  g_ptr_array_add (ptr_array, NULL);
+  return (gchar **) g_ptr_array_free (ptr_array, FALSE);
+}
+
+static gboolean
+set_selection_foreach (GtkTreeModel *model,
+                       GtkTreePath *path,
+                       GtkTreeIter *iter,
+                       gpointer user_data)
+{
+  gboolean selection = GPOINTER_TO_INT (user_data);
+  GtkTreeModel *actual_model;
+  GtkTreeIter real_iter;
+
+  if (GTK_IS_TREE_MODEL_FILTER (model))
+    {
+      actual_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
+      gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model),
+                                                        &real_iter, iter);
+    }
+  else if (GTK_IS_TREE_MODEL_SORT (model))
+    {
+      actual_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (model));
+      gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (model),
+                                                      &real_iter, iter);
+    }
+  else
+    {
+      actual_model = model;
+      real_iter = *iter;
+    }
+
+  if (GTK_IS_LIST_STORE (actual_model))
+    {
+      gtk_list_store_set (GTK_LIST_STORE (actual_model), &real_iter,
+                          GD_MAIN_COLUMN_SELECTED, selection,
+                          -1);
+    }
+  else
+    {
+      gtk_tree_store_set (GTK_TREE_STORE (actual_model), &real_iter,
+                          GD_MAIN_COLUMN_SELECTED, selection,
+                          -1);
+    }
+
+  return FALSE;
+}
+
+static void
+set_all_selection (GdMainViewGeneric *self,
+                   GtkTreeModel *model,
+                   gboolean selection)
+{
+  gtk_tree_model_foreach (model,
+                          set_selection_foreach,
+                          GINT_TO_POINTER (selection));
+  g_signal_emit (self, signals[VIEW_SELECTION_CHANGED], 0);
+}
+
+void
+gd_main_view_generic_select_all (GdMainViewGeneric *self)
+{
+  GtkTreeModel *model = gd_main_view_generic_get_model (self);
+
+  set_all_selection (self, model, TRUE);
+}
+
+void
+gd_main_view_generic_unselect_all (GdMainViewGeneric *self)
+{
+  GtkTreeModel *model = gd_main_view_generic_get_model (self);
+
+  set_all_selection (self, model, FALSE);
+}
+
+void
+_gd_main_view_generic_dnd_common (GtkTreeModel *model,
+                                  gboolean selection_mode,
+                                  GtkTreePath *path,
+                                  GtkSelectionData *data)
+{
+  gchar **uris;
+
+  if (selection_mode)
+    {
+      uris = model_get_selection_uris (model);
+    }
+  else
+    {
+      GtkTreeIter iter;
+      gboolean res;
+      gchar *uri = NULL;
+
+      if (path != NULL)
+        {
+          res = gtk_tree_model_get_iter (model, &iter, path);
+          if (res)
+            gtk_tree_model_get (model, &iter,
+                                GD_MAIN_COLUMN_URI, &uri,
+                                -1);
+        }
+
+      uris = g_new0 (gchar *, 2);
+      uris[0] = uri;
+      uris[1] = NULL;
+    }
+
+  gtk_selection_data_set_uris (data, uris);
+  g_strfreev (uris);
+}
diff --git a/subprojects/libgd/libgd/gd-main-view-generic.h b/subprojects/libgd/libgd/gd-main-view-generic.h
new file mode 100644
index 0000000..dd53e0e
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-main-view-generic.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2011 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by 
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Lesser General Public 
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License 
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Cosimo Cecchi <cosimoc redhat com>
+ *
+ */
+
+#ifndef __GD_MAIN_VIEW_GENERIC_H__
+#define __GD_MAIN_VIEW_GENERIC_H__
+
+#include <glib-object.h>
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+  GD_MAIN_COLUMN_ID,
+  GD_MAIN_COLUMN_URI,
+  GD_MAIN_COLUMN_PRIMARY_TEXT,
+  GD_MAIN_COLUMN_SECONDARY_TEXT,
+  GD_MAIN_COLUMN_ICON,
+  GD_MAIN_COLUMN_MTIME,
+  GD_MAIN_COLUMN_SELECTED,
+  GD_MAIN_COLUMN_PULSE,
+
+  GD_MAIN_COLUMN_LAST
+} GdMainColumns;
+
+#define GD_TYPE_MAIN_VIEW_GENERIC gd_main_view_generic_get_type()
+
+#define GD_MAIN_VIEW_GENERIC(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+   GD_TYPE_MAIN_VIEW_GENERIC, GdMainViewGeneric))
+
+#define GD_MAIN_VIEW_GENERIC_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), \
+   GD_TYPE_MAIN_VIEW_GENERIC, GdMainViewGenericIface))
+
+#define GD_IS_MAIN_VIEW_GENERIC(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+   GD_TYPE_MAIN_VIEW_GENERIC))
+
+#define GD_IS_MAIN_VIEW_GENERIC_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+   GD_TYPE_MAIN_VIEW_GENERIC))
+
+#define GD_MAIN_VIEW_GENERIC_GET_IFACE(obj) \
+  (G_TYPE_INSTANCE_GET_INTERFACE ((obj), \
+   GD_TYPE_MAIN_VIEW_GENERIC, GdMainViewGenericIface))
+
+typedef struct _GdMainViewGeneric GdMainViewGeneric;
+typedef struct _GdMainViewGenericIface GdMainViewGenericIface;
+
+struct _GdMainViewGenericIface
+{
+  GTypeInterface base_iface;
+
+  /* vtable */
+  void          (* set_model)            (GdMainViewGeneric  *self,
+                                          GtkTreeModel       *model);
+  GtkTreeModel * (* get_model)           (GdMainViewGeneric *self);
+
+  GtkTreePath * (* get_path_at_pos)      (GdMainViewGeneric *self,
+                                          gint               x, 
+                                          gint               y);
+  void          (* scroll_to_path)       (GdMainViewGeneric *self,
+                                          GtkTreePath       *path);
+  void          (* set_selection_mode)   (GdMainViewGeneric *self,
+                                          gboolean           selection_mode);
+};
+
+GType gd_main_view_generic_get_type (void) G_GNUC_CONST;
+
+void gd_main_view_generic_set_model (GdMainViewGeneric *self,
+                                     GtkTreeModel *model);
+GtkTreeModel * gd_main_view_generic_get_model (GdMainViewGeneric *self);
+
+void gd_main_view_generic_scroll_to_path (GdMainViewGeneric *self,
+                                          GtkTreePath *path);
+void gd_main_view_generic_set_selection_mode (GdMainViewGeneric *self,
+                                              gboolean selection_mode);
+GtkTreePath * gd_main_view_generic_get_path_at_pos (GdMainViewGeneric *self,
+                                                    gint x,
+                                                    gint y);
+void gd_main_view_generic_select_all (GdMainViewGeneric *self);
+void gd_main_view_generic_unselect_all (GdMainViewGeneric *self);
+void gd_main_view_generic_set_rubberband_range (GdMainViewGeneric *self,
+                                               GtkTreePath *start,
+                                               GtkTreePath *end);
+
+/* private */
+void _gd_main_view_generic_dnd_common (GtkTreeModel *model,
+                                       gboolean selection_mode,
+                                       GtkTreePath *path,
+                                       GtkSelectionData *data);
+void _gd_main_view_generic_get_rubberband_range (GdMainViewGeneric *self,
+                                                GtkTreePath **start,
+                                                GtkTreePath **end);
+
+G_END_DECLS
+
+#endif /* __GD_MAIN_VIEW_GENERIC_H__ */
diff --git a/subprojects/libgd/libgd/gd-main-view.c b/subprojects/libgd/libgd/gd-main-view.c
new file mode 100644
index 0000000..008162b
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-main-view.c
@@ -0,0 +1,1160 @@
+/*
+ * Copyright (c) 2011 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by 
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Lesser General Public 
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License 
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Cosimo Cecchi <cosimoc redhat com>
+ *
+ */
+
+#include "gd-main-view.h"
+
+#include "gd-icon-utils.h"
+#include "gd-main-view-generic.h"
+#include "gd-main-icon-view.h"
+#include "gd-main-list-view.h"
+
+#include <math.h>
+#include <cairo-gobject.h>
+
+#define MAIN_VIEW_TYPE_INITIAL -1
+#define MAIN_VIEW_DND_ICON_OFFSET 20
+#define MAIN_VIEW_RUBBERBAND_SELECT_TRIGGER_LENGTH 32
+
+typedef struct _GdMainViewPrivate GdMainViewPrivate;
+
+struct _GdMainViewPrivate {
+  GdMainViewType current_type;
+  gboolean selection_mode;
+
+  GtkWidget *current_view;
+  GtkTreeModel *model;
+
+  gboolean track_motion;
+  gboolean rubberband_select;
+  GtkTreePath *rubberband_select_first_path;
+  GtkTreePath *rubberband_select_last_path;
+  int button_down_x;
+  int button_down_y;
+
+  gchar *button_press_item_path;
+
+  gchar *last_selected_id;
+};
+
+enum {
+  PROP_VIEW_TYPE = 1,
+  PROP_SELECTION_MODE,
+  PROP_MODEL,
+  NUM_PROPERTIES
+};
+
+enum {
+  ITEM_ACTIVATED = 1,
+  SELECTION_MODE_REQUEST,
+  VIEW_SELECTION_CHANGED,
+  NUM_SIGNALS
+};
+
+static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
+static guint signals[NUM_SIGNALS] = { 0, };
+
+G_DEFINE_TYPE_WITH_PRIVATE (GdMainView, gd_main_view, GTK_TYPE_SCROLLED_WINDOW)
+
+static void
+gd_main_view_dispose (GObject *obj)
+{
+  GdMainView *self = GD_MAIN_VIEW (obj);
+  GdMainViewPrivate *priv;
+
+  priv = gd_main_view_get_instance_private (self);
+
+  g_clear_object (&priv->model);
+
+  G_OBJECT_CLASS (gd_main_view_parent_class)->dispose (obj);
+}
+
+static void
+gd_main_view_finalize (GObject *obj)
+{
+  GdMainView *self = GD_MAIN_VIEW (obj);
+  GdMainViewPrivate *priv;
+
+  priv = gd_main_view_get_instance_private (self);
+
+  g_free (priv->button_press_item_path);
+  g_free (priv->last_selected_id);
+
+  if (priv->rubberband_select_first_path)
+    gtk_tree_path_free (priv->rubberband_select_first_path);
+
+  if (priv->rubberband_select_last_path)
+    gtk_tree_path_free (priv->rubberband_select_last_path);
+
+  G_OBJECT_CLASS (gd_main_view_parent_class)->finalize (obj);
+}
+
+static void
+gd_main_view_init (GdMainView *self)
+{
+  GdMainViewPrivate *priv;
+  GtkStyleContext *context;
+
+  priv = gd_main_view_get_instance_private (self);
+
+  /* so that we get constructed with the right view even at startup */
+  priv->current_type = MAIN_VIEW_TYPE_INITIAL;
+
+  gtk_widget_set_hexpand (GTK_WIDGET (self), TRUE);
+  gtk_widget_set_vexpand (GTK_WIDGET (self), TRUE);
+  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (self), GTK_SHADOW_IN);
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (self),
+                                  GTK_POLICY_NEVER,
+                                  GTK_POLICY_AUTOMATIC);
+
+  context = gtk_widget_get_style_context (GTK_WIDGET (self));
+  gtk_style_context_add_class (context, "documents-scrolledwin");
+}
+
+static void
+gd_main_view_get_property (GObject    *object,
+                           guint       property_id,
+                           GValue     *value,
+                           GParamSpec *pspec)
+{
+  GdMainView *self = GD_MAIN_VIEW (object);
+
+  switch (property_id)
+    {
+    case PROP_VIEW_TYPE:
+      g_value_set_int (value, gd_main_view_get_view_type (self));
+      break;
+    case PROP_SELECTION_MODE:
+      g_value_set_boolean (value, gd_main_view_get_selection_mode (self));
+      break;
+    case PROP_MODEL:
+      g_value_set_object (value, gd_main_view_get_model (self));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gd_main_view_set_property (GObject    *object,
+                           guint       property_id,
+                           const GValue *value,
+                           GParamSpec *pspec)
+{
+  GdMainView *self = GD_MAIN_VIEW (object);
+
+  switch (property_id)
+    {
+    case PROP_VIEW_TYPE:
+      gd_main_view_set_view_type (self, g_value_get_int (value));
+      break;
+    case PROP_SELECTION_MODE:
+      gd_main_view_set_selection_mode (self, g_value_get_boolean (value));
+      break;
+    case PROP_MODEL:
+      gd_main_view_set_model (self, g_value_get_object (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gd_main_view_class_init (GdMainViewClass *klass)
+{
+  GObjectClass *oclass = G_OBJECT_CLASS (klass);
+
+  oclass->get_property = gd_main_view_get_property;
+  oclass->set_property = gd_main_view_set_property;
+  oclass->dispose = gd_main_view_dispose;
+  oclass->finalize = gd_main_view_finalize;
+
+  properties[PROP_VIEW_TYPE] =
+    g_param_spec_int ("view-type",
+                      "View type",
+                      "View type",
+                      GD_MAIN_VIEW_ICON,
+                      GD_MAIN_VIEW_LIST,
+                      GD_MAIN_VIEW_ICON,
+                      G_PARAM_READWRITE |
+                      G_PARAM_CONSTRUCT |
+                      G_PARAM_STATIC_STRINGS);
+
+  properties[PROP_SELECTION_MODE] =
+    g_param_spec_boolean ("selection-mode",
+                          "Selection mode",
+                          "Whether the view is in selection mode",
+                          FALSE,
+                          G_PARAM_READWRITE |
+                          G_PARAM_CONSTRUCT |
+                          G_PARAM_STATIC_STRINGS);
+
+  properties[PROP_MODEL] =
+    g_param_spec_object ("model",
+                         "Model",
+                         "The GtkTreeModel",
+                         GTK_TYPE_TREE_MODEL,
+                         G_PARAM_READWRITE |
+                         G_PARAM_CONSTRUCT |
+                         G_PARAM_STATIC_STRINGS);
+
+  signals[ITEM_ACTIVATED] =
+    g_signal_new ("item-activated",
+                  GD_TYPE_MAIN_VIEW,
+                  G_SIGNAL_RUN_LAST,
+                  0, NULL, NULL, NULL,
+                  G_TYPE_NONE, 2,
+                  G_TYPE_STRING, 
+                  GTK_TYPE_TREE_PATH);
+
+  signals[SELECTION_MODE_REQUEST] =
+    g_signal_new ("selection-mode-request",
+                  GD_TYPE_MAIN_VIEW,
+                  G_SIGNAL_RUN_LAST,
+                  0, NULL, NULL, NULL,
+                  G_TYPE_NONE, 0);
+
+  signals[VIEW_SELECTION_CHANGED] = 
+    g_signal_new ("view-selection-changed",
+                  GD_TYPE_MAIN_VIEW,
+                  G_SIGNAL_RUN_LAST,
+                  0, NULL, NULL, NULL,
+                  G_TYPE_NONE, 0);
+
+  g_object_class_install_properties (oclass, NUM_PROPERTIES, properties);
+}
+
+static GdMainViewGeneric *
+get_generic (GdMainView *self)
+{
+  GdMainViewPrivate *priv;
+
+  priv = gd_main_view_get_instance_private (self);
+
+  if (priv->current_view != NULL)
+    return GD_MAIN_VIEW_GENERIC (priv->current_view);
+
+  return NULL;
+}
+
+static void
+do_select_row (GdMainView *self,
+               GtkTreeIter *iter,
+               gboolean value)
+{
+  GdMainViewPrivate *priv;
+  GtkTreeModel *model;
+  GtkTreeIter my_iter;
+  GtkTreePath *path;
+
+  priv = gd_main_view_get_instance_private (self);
+
+  model = priv->model;
+  my_iter = *iter;
+
+  while (GTK_IS_TREE_MODEL_FILTER (model) ||
+         GTK_IS_TREE_MODEL_SORT (model))
+    {
+      GtkTreeIter child_iter;
+
+      if (GTK_IS_TREE_MODEL_FILTER (model))
+        {
+          GtkTreeModelFilter *filter;
+
+          filter = GTK_TREE_MODEL_FILTER (model);
+          gtk_tree_model_filter_convert_iter_to_child_iter (filter, &child_iter, &my_iter);
+          model = gtk_tree_model_filter_get_model (filter);
+        }
+      else
+        {
+          GtkTreeModelSort *sort;
+
+          sort = GTK_TREE_MODEL_SORT (model);
+          gtk_tree_model_sort_convert_iter_to_child_iter (sort, &child_iter, &my_iter);
+          model = gtk_tree_model_sort_get_model (sort);
+        }
+
+      my_iter = child_iter;
+    }
+
+  if (GTK_IS_LIST_STORE (model))
+    {
+      gtk_list_store_set (GTK_LIST_STORE (model), &my_iter,
+                          GD_MAIN_COLUMN_SELECTED, value,
+                          -1);
+    }
+  else
+    {
+      gtk_tree_store_set (GTK_TREE_STORE (model), &my_iter,
+                          GD_MAIN_COLUMN_SELECTED, value,
+                          -1);
+    }
+
+  /* And tell the view model that something changed */
+  path = gtk_tree_model_get_path (priv->model, iter);
+  if (path)
+    {
+      gtk_tree_model_row_changed (priv->model, path, iter);
+      gtk_tree_path_free (path);
+    }
+}
+
+static void
+selection_mode_do_select_range (GdMainView *self,
+                                GtkTreeIter *first_element,
+                                GtkTreeIter *last_element)
+{
+  GdMainViewPrivate *priv;
+  GtkTreeIter iter;
+  GtkTreePath *path, *last_path;
+  gboolean equal;
+
+  priv = gd_main_view_get_instance_private (self);
+
+  path = gtk_tree_model_get_path (priv->model, first_element);
+  last_path = gtk_tree_model_get_path (priv->model, last_element);
+  if (gtk_tree_path_compare (path, last_path) > 0)
+    {
+      gtk_tree_path_free (last_path);
+      last_path = path;
+      iter = *last_element;
+    }
+  else
+    {
+      gtk_tree_path_free (path);
+      iter = *first_element;
+    }
+
+  do
+    {
+      do_select_row (self, &iter, TRUE);
+
+      path = gtk_tree_model_get_path (priv->model, &iter);
+      equal = (gtk_tree_path_compare (path, last_path) == 0);
+      gtk_tree_path_free (path);
+
+      if (equal)
+        break;
+    }
+  while (gtk_tree_model_iter_next (priv->model, &iter));
+
+  gtk_tree_path_free (last_path);
+}
+
+static void
+selection_mode_select_range (GdMainView *self,
+                             GtkTreeIter *iter)
+{
+  GdMainViewPrivate *priv;
+  GtkTreeIter other;
+  gboolean found = FALSE;
+  gboolean selected;
+  char *id;
+
+  priv = gd_main_view_get_instance_private (self);
+
+  if (priv->last_selected_id != NULL &&
+      gtk_tree_model_get_iter_first (priv->model, &other))
+    {
+      do
+       {
+         gtk_tree_model_get (priv->model, &other,
+                             GD_MAIN_COLUMN_ID, &id,
+                             -1);
+         if (g_strcmp0 (id, priv->last_selected_id) == 0)
+           {
+             g_free (id);
+             found = TRUE;
+             break;
+           }
+         g_free (id);
+       }
+      while (gtk_tree_model_iter_next (priv->model, &other));
+    }
+
+  if (!found)
+    {
+      other = *iter;
+      while (gtk_tree_model_iter_previous (priv->model, &other))
+       {
+         gtk_tree_model_get (priv->model, &other,
+                             GD_MAIN_COLUMN_SELECTED, &selected,
+                             -1);
+
+         if (selected)
+           {
+             found = TRUE;
+             break;
+           }
+       }
+    }
+
+  if (!found)
+    {
+      other = *iter;
+      while (gtk_tree_model_iter_next (priv->model, &other))
+       {
+         gtk_tree_model_get (priv->model, &other,
+                             GD_MAIN_COLUMN_SELECTED, &selected,
+                             -1);
+         if (selected)
+           {
+             found = TRUE;
+             break;
+           }
+       }
+    }
+
+  if (found)
+    selection_mode_do_select_range (self, iter, &other);
+  else
+    {
+      /* no other selected element found, just select the iter */
+      do_select_row (self, iter, TRUE);
+    }
+
+  g_signal_emit (self, signals[VIEW_SELECTION_CHANGED], 0);
+}
+
+static gboolean
+toggle_selection_for_path (GdMainView *self,
+                           GtkTreePath *path,
+                           gboolean select_range)
+{
+  GdMainViewPrivate *priv;
+  gboolean selected;
+  GtkTreeIter iter;
+  char *id;
+
+  priv = gd_main_view_get_instance_private (self);
+
+  if (priv->model == NULL)
+    return FALSE;
+
+  if (!gtk_tree_model_get_iter (priv->model, &iter, path))
+    return FALSE;
+
+  gtk_tree_model_get (priv->model, &iter,
+                      GD_MAIN_COLUMN_SELECTED, &selected,
+                      -1);
+
+  if (selected)
+    {
+      do_select_row (self, &iter, FALSE);
+    }
+  else if (!selected)
+    {
+      if (select_range)
+        selection_mode_select_range (self, &iter);
+      else
+       {
+         gtk_tree_model_get (priv->model, &iter,
+                             GD_MAIN_COLUMN_ID, &id,
+                             -1);
+         g_free (priv->last_selected_id);
+         priv->last_selected_id = id;
+
+          do_select_row (self, &iter, TRUE);
+       }
+    }
+
+  g_signal_emit (self, signals[VIEW_SELECTION_CHANGED], 0);
+
+  return FALSE;
+}
+
+static gboolean
+activate_item_for_path (GdMainView *self,
+                        GtkTreePath *path)
+{
+  GdMainViewPrivate *priv;
+  GtkTreeIter iter;
+  gchar *id;
+
+  priv = gd_main_view_get_instance_private (self);
+
+  if (priv->model == NULL)
+    return FALSE;
+
+  if (!gtk_tree_model_get_iter (priv->model, &iter, path))
+    return FALSE;
+
+  gtk_tree_model_get (priv->model, &iter,
+                      GD_MAIN_COLUMN_ID, &id,
+                      -1);
+
+  g_signal_emit (self, signals[ITEM_ACTIVATED], 0, id, path);
+  g_free (id);
+
+  return FALSE;
+}
+
+static gboolean
+on_button_release_selection_mode (GdMainView *self,
+                                  GdkEventButton *event,
+                                  GtkTreePath *path)
+{
+  return toggle_selection_for_path (self, path, ((event->state & GDK_SHIFT_MASK) != 0));
+}
+
+static gboolean
+on_button_release_view_mode (GdMainView *self,
+                             GdkEventButton *event,
+                             GtkTreePath *path)
+{
+  return activate_item_for_path (self, path);
+}
+
+static gboolean
+event_triggers_selection_mode (GdkEventButton *event)
+{
+  return
+    (event->button == 3) ||
+    ((event->button == 1) && (event->state & GDK_CONTROL_MASK));
+}
+
+static gboolean
+on_button_release_event (GtkWidget *view,
+                         GdkEventButton *event,
+                         gpointer user_data)
+{
+  GdMainView *self = user_data;
+  GdMainViewPrivate *priv;
+  GdMainViewGeneric *generic = get_generic (self);
+  GtkTreePath *path, *start_path, *end_path, *tmp_path;
+  GtkTreeIter iter;
+  gchar *button_release_item_path;
+  gboolean selection_mode;
+  gboolean res, same_item = FALSE;
+  gboolean is_selected;
+
+  priv = gd_main_view_get_instance_private (self);
+
+  /* eat double/triple click events */
+  if (event->type != GDK_BUTTON_RELEASE)
+    return TRUE;
+
+  path = gd_main_view_generic_get_path_at_pos (generic, event->x, event->y);
+
+  if (path != NULL)
+    {
+      button_release_item_path = gtk_tree_path_to_string (path);
+      if (g_strcmp0 (priv->button_press_item_path, button_release_item_path) == 0)
+        same_item = TRUE;
+
+      g_free (button_release_item_path);
+    }
+
+  g_free (priv->button_press_item_path);
+  priv->button_press_item_path = NULL;
+
+  priv->track_motion = FALSE;
+  if (priv->rubberband_select)
+    {
+      priv->rubberband_select = FALSE;
+      gd_main_view_generic_set_rubberband_range (get_generic (self), NULL, NULL);
+      if (priv->rubberband_select_last_path)
+       {
+         if (!priv->selection_mode)
+           g_signal_emit (self, signals[SELECTION_MODE_REQUEST], 0);
+         if (!priv->selection_mode)
+           {
+             res = FALSE;
+             goto out;
+           }
+
+         start_path = gtk_tree_path_copy (priv->rubberband_select_first_path);
+         end_path = gtk_tree_path_copy (priv->rubberband_select_last_path);
+         if (gtk_tree_path_compare (start_path, end_path) > 0)
+           {
+             tmp_path = start_path;
+             start_path = end_path;
+             end_path = tmp_path;
+           }
+
+         while (gtk_tree_path_compare (start_path, end_path) <= 0)
+           {
+             if (gtk_tree_model_get_iter (priv->model,
+                                          &iter, start_path))
+               {
+                 gtk_tree_model_get (priv->model, &iter,
+                                     GD_MAIN_COLUMN_SELECTED, &is_selected,
+                                     -1);
+                  do_select_row (self, &iter, !is_selected);
+               }
+
+             gtk_tree_path_next (start_path);
+           }
+
+          g_signal_emit (self, signals[VIEW_SELECTION_CHANGED], 0);
+
+         gtk_tree_path_free (start_path);
+         gtk_tree_path_free (end_path);
+       }
+
+      g_clear_pointer (&priv->rubberband_select_first_path,
+                      gtk_tree_path_free);
+      g_clear_pointer (&priv->rubberband_select_last_path,
+                      gtk_tree_path_free);
+
+      res = TRUE;
+      goto out;
+    }
+
+  if (!same_item)
+    {
+      res = FALSE;
+      goto out;
+    }
+
+  selection_mode = priv->selection_mode;
+
+  if (!selection_mode)
+    {
+      if (event_triggers_selection_mode (event))
+        {
+          g_signal_emit (self, signals[SELECTION_MODE_REQUEST], 0);
+          if (!priv->selection_mode)
+            {
+              res = FALSE;
+              goto out;
+            }
+          selection_mode = priv->selection_mode;
+        }
+    }
+
+  if (selection_mode)
+    res = on_button_release_selection_mode (self, event, path);
+  else
+    res = on_button_release_view_mode (self, event, path);
+
+ out:
+  gtk_tree_path_free (path);
+  return res;
+}
+
+static gboolean
+on_button_press_event (GtkWidget *view,
+                       GdkEventButton *event,
+                       gpointer user_data)
+{
+  GdMainView *self = user_data;
+  GdMainViewPrivate *priv;
+  GdMainViewGeneric *generic = get_generic (self);
+  GtkTreePath *path;
+  GList *selection, *l;
+  GtkTreePath *sel_path;
+  gboolean found = FALSE;
+  gboolean force_selection;
+
+  priv = gd_main_view_get_instance_private (self);
+
+  path = gd_main_view_generic_get_path_at_pos (generic, event->x, event->y);
+
+  if (path != NULL)
+    priv->button_press_item_path = gtk_tree_path_to_string (path);
+
+  force_selection = event_triggers_selection_mode (event);
+  if (!priv->selection_mode && !force_selection)
+    {
+      gtk_tree_path_free (path);
+      return FALSE;
+    }
+
+  if (path && !force_selection)
+    {
+      selection = gd_main_view_get_selection (self);
+
+      for (l = selection; l != NULL; l = l->next)
+       {
+         sel_path = l->data;
+         if (gtk_tree_path_compare (path, sel_path) == 0)
+           {
+             found = TRUE;
+             break;
+           }
+       }
+
+      if (selection != NULL)
+       g_list_free_full (selection, (GDestroyNotify) gtk_tree_path_free);
+    }
+
+  /* if we did not find the item in the selection, block
+   * drag and drop, while in selection mode
+   */
+  if (!found)
+    {
+      priv->track_motion = TRUE;
+      priv->rubberband_select = FALSE;
+      priv->rubberband_select_first_path = NULL;
+      priv->rubberband_select_last_path = NULL;
+      priv->button_down_x = event->x;
+      priv->button_down_y = event->y;
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+static gboolean
+on_motion_event (GtkWidget      *widget,
+                GdkEventMotion *event,
+                gpointer user_data)
+{
+  GdMainView *self = user_data;
+  GdMainViewPrivate *priv;
+  GtkTreePath *path;
+
+  priv = gd_main_view_get_instance_private (self);
+
+  if (priv->track_motion)
+    {
+      if (!priv->rubberband_select &&
+         (event->x - priv->button_down_x) * (event->x - priv->button_down_x) +
+         (event->y - priv->button_down_y) * (event->y - priv->button_down_y)  >
+         MAIN_VIEW_RUBBERBAND_SELECT_TRIGGER_LENGTH * MAIN_VIEW_RUBBERBAND_SELECT_TRIGGER_LENGTH)
+       {
+         priv->rubberband_select = TRUE;
+         if (priv->button_press_item_path)
+           {
+             priv->rubberband_select_first_path =
+               gtk_tree_path_new_from_string (priv->button_press_item_path);
+           }
+       }
+
+      if (priv->rubberband_select)
+       {
+         path = gd_main_view_generic_get_path_at_pos (get_generic (self), event->x, event->y);
+         if (path != NULL)
+           {
+             if (priv->rubberband_select_first_path == NULL)
+               priv->rubberband_select_first_path = gtk_tree_path_copy (path);
+
+             if (priv->rubberband_select_last_path == NULL ||
+                 gtk_tree_path_compare (priv->rubberband_select_last_path, path) != 0)
+               {
+                 if (priv->rubberband_select_last_path)
+                   gtk_tree_path_free (priv->rubberband_select_last_path);
+                 priv->rubberband_select_last_path = path;
+
+                 gd_main_view_generic_set_rubberband_range (get_generic (self),
+                                                            priv->rubberband_select_first_path,
+                                                            priv->rubberband_select_last_path);
+               }
+             else
+               gtk_tree_path_free (path);
+           }
+       }
+    }
+  return FALSE;
+}
+
+static void
+on_drag_begin (GdMainViewGeneric *generic,
+               GdkDragContext *drag_context,
+               gpointer user_data)
+{
+  GdMainView *self = user_data;
+  GdMainViewPrivate *priv;
+
+  priv = gd_main_view_get_instance_private (self);
+
+  if (priv->button_press_item_path != NULL)
+    {
+      gboolean res;
+      GtkTreeIter iter;
+      gpointer data;
+      cairo_surface_t *surface = NULL;
+      GtkTreePath *path;
+      GType column_gtype;
+
+      path = gtk_tree_path_new_from_string (priv->button_press_item_path);
+      res = gtk_tree_model_get_iter (priv->model,
+                                     &iter, path);
+      if (res)
+        gtk_tree_model_get (priv->model, &iter,
+                            GD_MAIN_COLUMN_ICON, &data,
+                            -1);
+
+      column_gtype = gtk_tree_model_get_column_type (priv->model,
+                                                     GD_MAIN_COLUMN_ICON);
+
+      if (column_gtype == CAIRO_GOBJECT_TYPE_SURFACE)
+        {
+          surface = gd_copy_image_surface (data);
+          cairo_surface_destroy (data);
+        }
+      else if (column_gtype == GDK_TYPE_PIXBUF)
+        {
+          surface = gdk_cairo_surface_create_from_pixbuf (data, 1, NULL);
+          g_object_unref (data);
+        }
+      else
+        g_assert_not_reached ();
+
+      if (priv->selection_mode &&
+          surface != NULL)
+        {
+          GList *selection;
+          cairo_surface_t *counter;
+
+          selection = gd_main_view_get_selection (self);
+
+          if (g_list_length (selection) > 1)
+            {
+              counter = gd_create_surface_with_counter (GTK_WIDGET (self), surface, g_list_length 
(selection));
+              cairo_surface_destroy (surface);
+              surface = counter;
+            }
+
+          if (selection != NULL)
+            g_list_free_full (selection, (GDestroyNotify) gtk_tree_path_free);
+        }
+
+      if (surface != NULL)
+        {
+          cairo_surface_set_device_offset (surface,
+                                           -MAIN_VIEW_DND_ICON_OFFSET,
+                                           -MAIN_VIEW_DND_ICON_OFFSET);
+          gtk_drag_set_icon_surface (drag_context, surface);
+          cairo_surface_destroy (surface);
+        }
+
+      gtk_tree_path_free (path);
+    }
+}
+
+static void
+on_view_path_activated (GdMainView *self,
+                        GtkTreePath *path)
+{
+  GdMainViewPrivate *priv;
+  GdkModifierType state;
+
+  priv = gd_main_view_get_instance_private (self);
+
+  gtk_get_current_event_state (&state);
+
+  if (priv->selection_mode || (state & GDK_CONTROL_MASK) != 0)
+    {
+      if (!priv->selection_mode)
+       g_signal_emit (self, signals[SELECTION_MODE_REQUEST], 0);
+      toggle_selection_for_path (self, path, ((state & GDK_SHIFT_MASK) != 0));
+    }
+  else
+    activate_item_for_path (self, path);
+}
+
+static void
+on_list_view_row_activated (GtkTreeView *tree_view,
+                            GtkTreePath *path,
+                            GtkTreeViewColumn *column,
+                            gpointer user_data)
+{
+  GdMainView *self = user_data;
+  on_view_path_activated (self, path);
+}
+
+static void
+on_icon_view_item_activated (GtkIconView *icon_view,
+                             GtkTreePath *path,
+                             gpointer user_data)
+{
+  GdMainView *self = user_data;
+  on_view_path_activated (self, path);
+}
+
+static void
+on_view_selection_changed (GtkWidget *view,
+                           gpointer user_data)
+{
+  GdMainView *self = user_data;
+
+  g_signal_emit (self, signals[VIEW_SELECTION_CHANGED], 0);
+}
+
+static void
+on_row_deleted_cb (GtkTreeModel *model,
+                   GtkTreePath *path,
+                   gpointer user_data)
+{
+  GdMainView *self = user_data;
+
+  g_signal_emit (self, signals[VIEW_SELECTION_CHANGED], 0);
+}
+
+static void
+gd_main_view_apply_model (GdMainView *self)
+{
+  GdMainViewPrivate *priv;
+  GdMainViewGeneric *generic = get_generic (self);
+
+  priv = gd_main_view_get_instance_private (self);
+  gd_main_view_generic_set_model (generic, priv->model);
+}
+
+static void
+gd_main_view_apply_selection_mode (GdMainView *self)
+{
+  GdMainViewPrivate *priv;
+  GdMainViewGeneric *generic = get_generic (self);
+
+  priv = gd_main_view_get_instance_private (self);
+
+  gd_main_view_generic_set_selection_mode (generic, priv->selection_mode);
+
+  if (!priv->selection_mode)
+    {
+      g_clear_pointer (&priv->last_selected_id, g_free);
+      if (priv->model != NULL)
+        gd_main_view_unselect_all (self);
+    }
+}
+
+static void
+gd_main_view_rebuild (GdMainView *self)
+{
+  GdMainViewPrivate *priv;
+  GtkStyleContext *context;
+
+  priv = gd_main_view_get_instance_private (self);
+
+  if (priv->current_view != NULL)
+    gtk_widget_destroy (priv->current_view);
+
+  if (priv->current_type == GD_MAIN_VIEW_ICON)
+    {
+      priv->current_view = gd_main_icon_view_new ();
+      g_signal_connect (priv->current_view, "item-activated",
+                        G_CALLBACK (on_icon_view_item_activated), self);
+    }
+  else
+    {
+      priv->current_view = gd_main_list_view_new ();
+      g_signal_connect (priv->current_view, "row-activated",
+                        G_CALLBACK (on_list_view_row_activated), self);
+    }
+
+  context = gtk_widget_get_style_context (priv->current_view);
+  gtk_style_context_add_class (context, "content-view");
+
+  gtk_container_add (GTK_CONTAINER (self), priv->current_view);
+
+  g_signal_connect (priv->current_view, "button-press-event",
+                    G_CALLBACK (on_button_press_event), self);
+  g_signal_connect (priv->current_view, "button-release-event",
+                    G_CALLBACK (on_button_release_event), self);
+  g_signal_connect (priv->current_view, "motion-notify-event",
+                    G_CALLBACK (on_motion_event), self);
+  g_signal_connect_after (priv->current_view, "drag-begin",
+                          G_CALLBACK (on_drag_begin), self);
+  g_signal_connect (priv->current_view, "view-selection-changed",
+                    G_CALLBACK (on_view_selection_changed), self);
+
+  gd_main_view_apply_model (self);
+  gd_main_view_apply_selection_mode (self);
+
+  gtk_widget_show_all (GTK_WIDGET (self));
+}
+
+GdMainView *
+gd_main_view_new (GdMainViewType type)
+{
+  return g_object_new (GD_TYPE_MAIN_VIEW,
+                       "view-type", type,
+                       NULL);
+}
+
+void
+gd_main_view_set_view_type (GdMainView *self,
+                            GdMainViewType type)
+{
+  GdMainViewPrivate *priv;
+
+  priv = gd_main_view_get_instance_private (self);
+
+  if (type != priv->current_type)
+    {
+      priv->current_type = type;
+      gd_main_view_rebuild (self);
+
+      g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_VIEW_TYPE]);
+    }
+}
+
+GdMainViewType
+gd_main_view_get_view_type (GdMainView *self)
+{
+  GdMainViewPrivate *priv;
+
+  priv = gd_main_view_get_instance_private (self);
+  return priv->current_type;
+}
+
+void
+gd_main_view_set_selection_mode (GdMainView *self,
+                                 gboolean selection_mode)
+{
+  GdMainViewPrivate *priv;
+
+  priv = gd_main_view_get_instance_private (self);
+
+  if (selection_mode != priv->selection_mode)
+    {
+      priv->selection_mode = selection_mode;
+      gd_main_view_apply_selection_mode (self);
+      g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTION_MODE]);
+    }
+}
+
+gboolean
+gd_main_view_get_selection_mode (GdMainView *self)
+{
+  GdMainViewPrivate *priv;
+
+  priv = gd_main_view_get_instance_private (self);
+  return priv->selection_mode;
+}
+
+/**
+ * gd_main_view_set_model:
+ * @self:
+ * @model: (allow-none):
+ *
+ */
+void
+gd_main_view_set_model (GdMainView *self,
+                        GtkTreeModel *model)
+{
+  GdMainViewPrivate *priv;
+
+  priv = gd_main_view_get_instance_private (self);
+
+  if (model != priv->model)
+    {
+      if (priv->model)
+        g_signal_handlers_disconnect_by_func (priv->model,
+                                              on_row_deleted_cb, self);
+
+      g_clear_object (&priv->model);
+
+      if (model)
+        {
+          priv->model = g_object_ref (model);
+          g_signal_connect (priv->model, "row-deleted",
+                            G_CALLBACK (on_row_deleted_cb), self);
+        }
+      else
+        {
+          priv->model = NULL;
+        }
+
+      gd_main_view_apply_model (self);
+      g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
+    }
+}
+
+/**
+ * gd_main_view_get_model:
+ * @self:
+ *
+ * Returns: (transfer none):
+ */
+GtkTreeModel *
+gd_main_view_get_model (GdMainView *self)
+{
+  GdMainViewPrivate *priv;
+
+  priv = gd_main_view_get_instance_private (self);
+  return priv->model;
+}
+
+/**
+ * gd_main_view_get_generic_view:
+ * @self:
+ *
+ * Returns: (transfer none):
+ */
+GtkWidget *
+gd_main_view_get_generic_view (GdMainView *self)
+{
+  GdMainViewPrivate *priv;
+
+  priv = gd_main_view_get_instance_private (self);
+  return priv->current_view;
+}
+
+static gboolean
+build_selection_list_foreach (GtkTreeModel *model,
+                              GtkTreePath *path,
+                              GtkTreeIter *iter,
+                              gpointer user_data)
+{
+  GList **sel = user_data;
+  gboolean is_selected;
+
+  gtk_tree_model_get (model, iter,
+                      GD_MAIN_COLUMN_SELECTED, &is_selected,
+                      -1);
+
+  if (is_selected)
+    *sel = g_list_prepend (*sel, gtk_tree_path_copy (path));
+
+  return FALSE;
+}
+
+/**
+ * gd_main_view_get_selection:
+ * @self:
+ *
+ * Returns: (element-type GtkTreePath) (transfer full):
+ */
+GList *
+gd_main_view_get_selection (GdMainView *self)
+{
+  GdMainViewPrivate *priv;
+  GList *retval = NULL;
+
+  priv = gd_main_view_get_instance_private (self);
+
+  gtk_tree_model_foreach (priv->model,
+                          build_selection_list_foreach,
+                          &retval);
+
+  return g_list_reverse (retval);
+}
+
+void
+gd_main_view_select_all (GdMainView *self)
+{
+  GdMainViewGeneric *generic = get_generic (self);
+
+  gd_main_view_generic_select_all (generic);
+}
+
+void
+gd_main_view_unselect_all (GdMainView *self)
+{
+  GdMainViewGeneric *generic = get_generic (self);
+
+  gd_main_view_generic_unselect_all (generic);
+}
diff --git a/subprojects/libgd/libgd/gd-main-view.h b/subprojects/libgd/libgd/gd-main-view.h
new file mode 100644
index 0000000..4f8afe7
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-main-view.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2011 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by 
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Lesser General Public 
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License 
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Cosimo Cecchi <cosimoc redhat com>
+ *
+ */
+
+#ifndef __GD_MAIN_VIEW_H__
+#define __GD_MAIN_VIEW_H__
+
+#include <glib-object.h>
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GD_TYPE_MAIN_VIEW gd_main_view_get_type()
+G_DECLARE_DERIVABLE_TYPE (GdMainView, gd_main_view, GD, MAIN_VIEW, GtkScrolledWindow)
+
+typedef enum {
+  GD_MAIN_VIEW_ICON,
+  GD_MAIN_VIEW_LIST
+} GdMainViewType;
+
+struct _GdMainViewClass {
+  GtkScrolledWindowClass parent_class;
+};
+
+GdMainView * gd_main_view_new (GdMainViewType type);
+void         gd_main_view_set_view_type (GdMainView *self,
+                                         GdMainViewType type);
+GdMainViewType gd_main_view_get_view_type (GdMainView *self);
+
+void gd_main_view_set_selection_mode (GdMainView *self,
+                                      gboolean selection_mode);
+gboolean gd_main_view_get_selection_mode (GdMainView *self);
+
+GList * gd_main_view_get_selection (GdMainView *self);
+
+void gd_main_view_select_all (GdMainView *self);
+void gd_main_view_unselect_all (GdMainView *self);
+
+GtkTreeModel * gd_main_view_get_model (GdMainView *self);
+void gd_main_view_set_model (GdMainView *self,
+                             GtkTreeModel *model);
+
+GtkWidget * gd_main_view_get_generic_view (GdMainView *self);
+
+G_END_DECLS
+
+#endif /* __GD_MAIN_VIEW_H__ */
diff --git a/subprojects/libgd/libgd/gd-margin-container.c b/subprojects/libgd/libgd/gd-margin-container.c
new file mode 100644
index 0000000..51bbdd1
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-margin-container.c
@@ -0,0 +1,379 @@
+/*
+ * Copyright (c) 2011 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by 
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Lesser General Public 
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License 
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Cosimo Cecchi <cosimoc redhat com>
+ *
+ */
+
+#include "config.h"
+
+#include "gd-margin-container.h"
+
+G_DEFINE_TYPE_WITH_CODE (GdMarginContainer, gd_margin_container, GTK_TYPE_BIN,
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE,
+                                                NULL))
+
+struct _GdMarginContainerPrivate {
+  gint min_margin;
+  gint max_margin;
+
+  GtkOrientation orientation;
+};
+
+enum {
+  PROP_MIN_MARGIN = 1,
+  PROP_MAX_MARGIN,
+  PROP_ORIENTATION,
+  NUM_PROPERTIES
+};
+
+static void
+gd_margin_container_queue_redraw (GdMarginContainer *self)
+{
+  GtkWidget *child;
+
+  /* Make sure that the widget and children are redrawn with the new setting: */
+  child = gtk_bin_get_child (GTK_BIN (self));
+  if (child)
+    gtk_widget_queue_resize (child);
+
+  gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+static void
+gd_margin_container_set_orientation (GdMarginContainer *self,
+                                     GtkOrientation orientation)
+{
+  if (self->priv->orientation != orientation)
+    {
+      self->priv->orientation = orientation;
+      g_object_notify (G_OBJECT (self), "orientation");
+
+      gd_margin_container_queue_redraw (self);
+    }
+}
+
+static void
+gd_margin_container_set_min_margin (GdMarginContainer *self,
+                                    gint min_margin)
+{
+  if (self->priv->min_margin != min_margin)
+    {
+      self->priv->min_margin = min_margin;
+      g_object_notify (G_OBJECT (self), "min-margin");
+
+      gd_margin_container_queue_redraw (self);
+    }
+}
+
+static void
+gd_margin_container_set_max_margin (GdMarginContainer *self,
+                                    gint max_margin)
+{
+  if (self->priv->max_margin != max_margin)
+    {
+      self->priv->max_margin = max_margin;
+      g_object_notify (G_OBJECT (self), "max-margin");
+
+      gd_margin_container_queue_redraw (self);
+    }
+}
+
+static void
+gd_margin_container_set_property (GObject      *object,
+                                  guint         property_id,
+                                  const GValue *value,
+                                  GParamSpec   *pspec)
+{
+  GdMarginContainer *self = GD_MARGIN_CONTAINER (object);
+
+  switch (property_id)
+    {
+    case PROP_MIN_MARGIN:
+      gd_margin_container_set_min_margin (self, g_value_get_int (value));
+      break;
+    case PROP_MAX_MARGIN:
+      gd_margin_container_set_max_margin (self, g_value_get_int (value));
+      break;
+    case PROP_ORIENTATION:
+      gd_margin_container_set_orientation (self, g_value_get_enum (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gd_margin_container_get_property (GObject    *object,
+                                  guint       property_id,
+                                  GValue     *value,
+                                  GParamSpec *pspec) 
+{
+  GdMarginContainer *self = GD_MARGIN_CONTAINER (object);
+
+  switch (property_id)
+    {
+    case PROP_MIN_MARGIN: 
+      g_value_set_int (value, self->priv->min_margin);
+      break;
+    case PROP_MAX_MARGIN:
+      g_value_set_int (value, self->priv->max_margin);
+      break;
+    case PROP_ORIENTATION:
+      g_value_set_enum (value, self->priv->orientation);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gd_margin_container_size_allocate (GtkWidget *widget,
+                                   GtkAllocation *allocation)
+{
+  GdMarginContainer *self = GD_MARGIN_CONTAINER (widget);
+  GtkWidget *child;
+  GtkAllocation child_allocation;
+  gint avail_width, avail_height;
+
+  child = gtk_bin_get_child (GTK_BIN (widget));
+  gtk_widget_set_allocation (widget, allocation);
+
+  if (child && gtk_widget_get_visible (child))
+    {
+      gint child_nat_width;
+      gint child_nat_height;
+      gint child_width, child_height;
+      gint offset;
+
+      /* available */
+      avail_width = allocation->width;
+      avail_height = allocation->height;
+
+      if (self->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+        avail_width  = MAX (1, avail_width - 2 * self->priv->min_margin);
+      else
+        avail_height = MAX (1, avail_height - 2 * self->priv->min_margin);
+
+      if (gtk_widget_get_request_mode (child) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
+       {
+         gtk_widget_get_preferred_width (child, NULL, &child_nat_width);
+         child_width = MIN (avail_width, child_nat_width);
+
+         gtk_widget_get_preferred_height_for_width (child, child_width, NULL, &child_nat_height);
+         child_height = MIN (avail_height, child_nat_height);
+
+          offset = MIN ((gint) ((avail_height - child_height) / 2), self->priv->max_margin);
+
+          if (offset > 0)
+            child_allocation.height = avail_height  - (offset * 2);
+          else
+            child_allocation.height = avail_height;
+
+          child_allocation.width = MIN (avail_width, child_nat_width);
+       }
+      else
+       {
+         gtk_widget_get_preferred_height (child, NULL, &child_nat_height);
+         child_height = MIN (avail_height, child_nat_height);
+
+         gtk_widget_get_preferred_width_for_height (child, child_height, NULL, &child_nat_width);
+         child_width = MIN (avail_width, child_nat_width);
+
+          offset = MIN ((gint) ((avail_width - child_width) / 2), self->priv->max_margin);
+
+          if (offset > 0)
+            child_allocation.width = avail_width - (offset * 2);
+          else
+            child_allocation.width = avail_width;
+
+          child_allocation.height = MIN (avail_height, child_nat_height);
+       }
+
+      child_allocation.x = offset + allocation->x;
+      child_allocation.y = (avail_height - child_allocation.height) + allocation->y;
+
+      if (self->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+        child_allocation.x += self->priv->min_margin;
+      else
+        child_allocation.y += self->priv->min_margin;
+
+      gtk_widget_size_allocate (child, &child_allocation);
+    }
+}
+
+static void
+gd_margin_container_get_preferred_size (GtkWidget *widget,
+                                        GtkOrientation orientation,
+                                        gint for_size,
+                                        gint *minimum_size,
+                                        gint *natural_size)
+{
+  GdMarginContainer *self = GD_MARGIN_CONTAINER (widget);
+  guint natural, minimum;
+  GtkWidget *child;
+
+  if (orientation == self->priv->orientation)
+    {
+      minimum = self->priv->min_margin * 2;
+      natural = self->priv->max_margin * 2;
+    }
+  else
+    {
+      minimum = 0;
+      natural = 0;
+    }
+
+  if ((child = gtk_bin_get_child (GTK_BIN (widget))) && gtk_widget_get_visible (child))
+    {
+      gint child_min, child_nat;
+
+      if (orientation == GTK_ORIENTATION_HORIZONTAL)
+        {
+         if (for_size < 0)
+           gtk_widget_get_preferred_width (child, &child_min, &child_nat);
+         else
+           {
+             gint min_height;
+
+             gtk_widget_get_preferred_height (child, &min_height, NULL);
+             for_size -= 2 * self->priv->min_margin;
+
+             gtk_widget_get_preferred_width_for_height (child, for_size, &child_min, &child_nat);
+           }
+        }
+      else
+        {
+         if (for_size < 0)
+           gtk_widget_get_preferred_height (child, &child_min, &child_nat);
+         else
+           {
+             gint min_width;
+
+             gtk_widget_get_preferred_width (child, &min_width, NULL);
+             for_size -= 2 * self->priv->min_margin;
+
+             gtk_widget_get_preferred_height_for_width (child, for_size, &child_min, &child_nat);
+           }
+        }
+
+      natural += child_nat;
+
+      if (orientation != self->priv->orientation)
+        minimum += child_min;
+    }
+
+  if (minimum_size != NULL)
+    *minimum_size = minimum;
+  if (natural_size != NULL)
+    *natural_size = natural;
+}
+
+static void
+gd_margin_container_get_preferred_width (GtkWidget *widget,
+                                         gint      *minimum_size,
+                                         gint      *natural_size)
+{
+  gd_margin_container_get_preferred_size (widget, GTK_ORIENTATION_HORIZONTAL,
+                                          -1, minimum_size, natural_size);
+}
+
+static void
+gd_margin_container_get_preferred_height (GtkWidget *widget,
+                                          gint      *minimum_size,
+                                          gint      *natural_size)
+{
+  gd_margin_container_get_preferred_size (widget, GTK_ORIENTATION_VERTICAL,
+                                          -1, minimum_size, natural_size);
+}
+
+static void 
+gd_margin_container_get_preferred_width_for_height (GtkWidget *widget,
+                                                    gint       for_size,
+                                                    gint      *minimum_size,
+                                                    gint      *natural_size)
+{
+  gd_margin_container_get_preferred_size (widget, GTK_ORIENTATION_HORIZONTAL,
+                                          for_size, minimum_size, natural_size);
+}
+
+static void 
+gd_margin_container_get_preferred_height_for_width (GtkWidget *widget,
+                                                    gint       for_size,
+                                                    gint      *minimum_size,
+                                                    gint      *natural_size)
+{
+  gd_margin_container_get_preferred_size (widget, GTK_ORIENTATION_VERTICAL,
+                                          for_size, minimum_size, natural_size);
+}
+
+static void
+gd_margin_container_init (GdMarginContainer *self)
+{
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GD_TYPE_MARGIN_CONTAINER,
+                                            GdMarginContainerPrivate);
+
+  self->priv->orientation = GTK_ORIENTATION_HORIZONTAL;
+
+  gtk_widget_set_has_window (GTK_WIDGET (self), FALSE);
+  gtk_widget_set_redraw_on_allocate (GTK_WIDGET (self), FALSE);
+}
+
+static void
+gd_margin_container_class_init (GdMarginContainerClass *klass)
+{
+  GObjectClass *oclass = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *wclass = GTK_WIDGET_CLASS (klass);
+
+  oclass->get_property = gd_margin_container_get_property;
+  oclass->set_property = gd_margin_container_set_property;
+
+  wclass->size_allocate = gd_margin_container_size_allocate;
+  wclass->get_preferred_width = gd_margin_container_get_preferred_width;
+  wclass->get_preferred_height = gd_margin_container_get_preferred_height;
+  wclass->get_preferred_width_for_height = gd_margin_container_get_preferred_width_for_height;
+  wclass->get_preferred_height_for_width = gd_margin_container_get_preferred_height_for_width;
+
+  gtk_container_class_handle_border_width (GTK_CONTAINER_CLASS (klass));
+
+  g_object_class_install_property (oclass, PROP_MIN_MARGIN,
+                                   g_param_spec_int ("min-margin",
+                                                     "Min margin",
+                                                     "Minimum margin around the child",
+                                                     0, G_MAXINT, 6,
+                                                     G_PARAM_READWRITE |
+                                                     G_PARAM_CONSTRUCT));
+  g_object_class_install_property (oclass, PROP_MAX_MARGIN,
+                                   g_param_spec_int ("max-margin",
+                                                     "Max margin",
+                                                     "Maximum margin around the child",
+                                                     0, G_MAXINT, 6,
+                                                     G_PARAM_READWRITE |
+                                                     G_PARAM_CONSTRUCT));
+  g_object_class_override_property (oclass, PROP_ORIENTATION,
+                                    "orientation");
+;
+  g_type_class_add_private (klass, sizeof (GdMarginContainerPrivate));
+}
+
+GdMarginContainer *
+gd_margin_container_new (void)
+{
+  return g_object_new (GD_TYPE_MARGIN_CONTAINER, NULL);
+}
diff --git a/subprojects/libgd/libgd/gd-margin-container.h b/subprojects/libgd/libgd/gd-margin-container.h
new file mode 100644
index 0000000..3937ea7
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-margin-container.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2011 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by 
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Lesser General Public 
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License 
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Cosimo Cecchi <cosimoc redhat com>
+ *
+ */
+
+#ifndef _GD_MARGIN_CONTAINER_H
+#define _GD_MARGIN_CONTAINER_H
+
+#include <glib-object.h>
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GD_TYPE_MARGIN_CONTAINER gd_margin_container_get_type()
+
+#define GD_MARGIN_CONTAINER(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+   GD_TYPE_MARGIN_CONTAINER, GdMarginContainer))
+
+#define GD_MARGIN_CONTAINER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), \
+   GD_TYPE_MARGIN_CONTAINER, GdMarginContainerClass))
+
+#define GD_IS_MARGIN_CONTAINER(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+   GD_TYPE_MARGIN_CONTAINER))
+
+#define GD_IS_MARGIN_CONTAINER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+   GD_TYPE_MARGIN_CONTAINER))
+
+#define GD_MARGIN_CONTAINER_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+   GD_TYPE_MARGIN_CONTAINER, GdMarginContainerClass))
+
+typedef struct _GdMarginContainer GdMarginContainer;
+typedef struct _GdMarginContainerClass GdMarginContainerClass;
+typedef struct _GdMarginContainerPrivate GdMarginContainerPrivate;
+
+struct _GdMarginContainer
+{
+  GtkBin parent;
+
+  GdMarginContainerPrivate *priv;
+};
+
+struct _GdMarginContainerClass
+{
+  GtkBinClass parent_class;
+};
+
+GType gd_margin_container_get_type (void) G_GNUC_CONST;
+
+GdMarginContainer *gd_margin_container_new (void);
+
+G_END_DECLS
+
+#endif /* _GD_MARGIN_CONTAINER_H */
diff --git a/subprojects/libgd/libgd/gd-notification.c b/subprojects/libgd/libgd/gd-notification.c
new file mode 100644
index 0000000..8153436
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-notification.c
@@ -0,0 +1,875 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * gd-notification
+ * Based on gtk-notification from gnome-contacts:
+ * http://git.gnome.org/browse/gnome-contacts/tree/src/gtk-notification.c?id=3.3.91
+ *
+ * Copyright (C) Erick PĂ©rez Castellanos 2011 <erick red gmail com>
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.";
+ */
+
+#include "gd-notification.h"
+
+/**
+ * SECTION:gdnotification
+ * @short_description: Report notification messages to the user
+ * @include: gtk/gtk.h
+ * @see_also: #GtkStatusbar, #GtkMessageDialog, #GtkInfoBar
+ *
+ * #GdNotification is a widget made for showing notifications to
+ * the user, allowing them to close the notification or wait for it
+ * to time out.
+ *
+ * #GdNotification provides one signal (#GdNotification::dismissed), for when the notification
+ * times out or is closed.
+ *
+ */
+
+#define GTK_PARAM_READWRITE G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
+#define SHADOW_OFFSET_X 2
+#define SHADOW_OFFSET_Y 3
+#define ANIMATION_TIME 200 /* msec */
+#define ANIMATION_STEP 40 /* msec */
+
+enum {
+  PROP_0,
+  PROP_TIMEOUT,
+  PROP_SHOW_CLOSE_BUTTON
+};
+
+struct _GdNotificationPrivate {
+  GtkWidget *close_button;
+  gboolean show_close_button;
+
+  GdkWindow *bin_window;
+
+  int animate_y; /* from 0 to allocation.height */
+  gboolean waiting_for_viewable;
+  gboolean revealed;
+  gboolean dismissed;
+  gboolean sent_dismissed;
+  guint animate_timeout;
+
+  gint timeout;
+  guint timeout_source_id;
+};
+
+enum {
+  DISMISSED,
+  LAST_SIGNAL
+};
+
+static guint notification_signals[LAST_SIGNAL] = { 0 };
+
+static gboolean gd_notification_draw                           (GtkWidget       *widget,
+                                                                 cairo_t         *cr);
+static void     gd_notification_get_preferred_width            (GtkWidget       *widget,
+                                                                 gint            *minimum_size,
+                                                                 gint            *natural_size);
+static void     gd_notification_get_preferred_height_for_width (GtkWidget       *widget,
+                                                                 gint             width,
+                                                                 gint            *minimum_height,
+                                                                 gint            *natural_height);
+static void     gd_notification_get_preferred_height           (GtkWidget       *widget,
+                                                                 gint            *minimum_size,
+                                                                 gint            *natural_size);
+static void     gd_notification_get_preferred_width_for_height (GtkWidget       *widget,
+                                                                 gint             height,
+                                                                 gint            *minimum_width,
+                                                                 gint            *natural_width);
+static void     gd_notification_size_allocate                  (GtkWidget       *widget,
+                                                                 GtkAllocation   *allocation);
+static gboolean gd_notification_timeout_cb                     (gpointer         user_data);
+static void     gd_notification_show                           (GtkWidget       *widget);
+static void     gd_notification_add                            (GtkContainer    *container,
+                                                                 GtkWidget       *child);
+
+/* signals handlers */
+static void     gd_notification_close_button_clicked_cb        (GtkWidget       *widget,
+                                                                 gpointer         user_data);
+
+G_DEFINE_TYPE(GdNotification, gd_notification, GTK_TYPE_BIN);
+
+static void
+gd_notification_init (GdNotification *notification)
+{
+  GtkWidget *close_button_image;
+  GtkStyleContext *context;
+  GdNotificationPrivate *priv;
+
+  context = gtk_widget_get_style_context (GTK_WIDGET (notification));
+  gtk_style_context_add_class (context, GTK_STYLE_CLASS_FRAME);
+  gtk_style_context_add_class (context, "app-notification");
+
+  gtk_widget_set_halign (GTK_WIDGET (notification), GTK_ALIGN_CENTER);
+  gtk_widget_set_valign (GTK_WIDGET (notification), GTK_ALIGN_START);
+
+  gtk_widget_set_has_window (GTK_WIDGET (notification), TRUE);
+
+  priv = notification->priv =
+    G_TYPE_INSTANCE_GET_PRIVATE (notification,
+                                 GD_TYPE_NOTIFICATION,
+                                 GdNotificationPrivate);
+
+  priv->animate_y = 0;
+  priv->close_button = gtk_button_new ();
+  gtk_widget_set_parent (priv->close_button, GTK_WIDGET (notification));
+  gtk_widget_show (priv->close_button);
+  g_object_set (priv->close_button,
+                "relief", GTK_RELIEF_NONE,
+                "focus-on-click", FALSE,
+                NULL);
+  g_signal_connect (priv->close_button,
+                    "clicked",
+                    G_CALLBACK (gd_notification_close_button_clicked_cb),
+                    notification);
+  close_button_image = gtk_image_new_from_icon_name ("window-close-symbolic", GTK_ICON_SIZE_BUTTON);
+  gtk_button_set_image (GTK_BUTTON (notification->priv->close_button), close_button_image);
+
+  priv->timeout_source_id = 0;
+}
+
+static void
+gd_notification_finalize (GObject *object)
+{
+  GdNotification *notification;
+  GdNotificationPrivate *priv;
+
+  g_return_if_fail (GTK_IS_NOTIFICATION (object));
+
+  notification = GD_NOTIFICATION (object);
+  priv = notification->priv;
+
+  if (priv->animate_timeout != 0)
+    g_source_remove (priv->animate_timeout);
+
+  if (priv->timeout_source_id != 0)
+    g_source_remove (priv->timeout_source_id);
+
+  G_OBJECT_CLASS (gd_notification_parent_class)->finalize (object);
+}
+
+static void
+gd_notification_destroy (GtkWidget *widget)
+{
+  GdNotification *notification = GD_NOTIFICATION (widget);
+  GdNotificationPrivate *priv = notification->priv;
+
+  if (!priv->sent_dismissed)
+    {
+      g_signal_emit (notification, notification_signals[DISMISSED], 0);
+      priv->sent_dismissed = TRUE;
+    }
+
+  if (priv->close_button)
+    {
+      gtk_widget_unparent (priv->close_button);
+      priv->close_button = NULL;
+    }
+
+  GTK_WIDGET_CLASS (gd_notification_parent_class)->destroy (widget);
+}
+
+static void
+gd_notification_realize (GtkWidget *widget)
+{
+  GdNotification *notification = GD_NOTIFICATION (widget);
+  GdNotificationPrivate *priv = notification->priv;
+  GtkBin *bin = GTK_BIN (widget);
+  GtkAllocation allocation;
+  GtkWidget *child;
+  GdkWindow *window;
+  GdkWindowAttr attributes;
+  gint attributes_mask;
+
+  gtk_widget_set_realized (widget, TRUE);
+
+  gtk_widget_get_allocation (widget, &allocation);
+
+  attributes.x = allocation.x;
+  attributes.y = allocation.y;
+  attributes.width = allocation.width;
+  attributes.height = allocation.height;
+  attributes.window_type = GDK_WINDOW_CHILD;
+  attributes.wclass = GDK_INPUT_OUTPUT;
+  attributes.visual = gtk_widget_get_visual (widget);
+
+  attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK;
+
+  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
+
+  window = gdk_window_new (gtk_widget_get_parent_window (widget),
+                           &attributes, attributes_mask);
+  gtk_widget_set_window (widget, window);
+  gtk_widget_register_window (widget, window);
+
+  attributes.x = 0;
+  attributes.y = attributes.height + priv->animate_y;
+  attributes.event_mask = gtk_widget_get_events (widget) |
+                          GDK_EXPOSURE_MASK |
+                          GDK_VISIBILITY_NOTIFY_MASK |
+                          GDK_ENTER_NOTIFY_MASK |
+                          GDK_LEAVE_NOTIFY_MASK;
+
+  priv->bin_window = gdk_window_new (window, &attributes, attributes_mask);
+  gtk_widget_register_window (widget, priv->bin_window);
+
+  child = gtk_bin_get_child (bin);
+  if (child)
+    gtk_widget_set_parent_window (child, priv->bin_window);
+  gtk_widget_set_parent_window (priv->close_button, priv->bin_window);
+
+  gdk_window_show (priv->bin_window);
+}
+
+static void
+gd_notification_unrealize (GtkWidget *widget)
+{
+  GdNotification *notification = GD_NOTIFICATION (widget);
+  GdNotificationPrivate *priv = notification->priv;
+
+  gtk_widget_unregister_window (widget, priv->bin_window);
+  gdk_window_destroy (priv->bin_window);
+  priv->bin_window = NULL;
+
+  GTK_WIDGET_CLASS (gd_notification_parent_class)->unrealize (widget);
+}
+
+static int
+animation_target (GdNotification *notification)
+{
+  GdNotificationPrivate *priv = notification->priv;
+  GtkAllocation allocation;
+
+  if (priv->revealed) {
+    gtk_widget_get_allocation (GTK_WIDGET (notification), &allocation);
+    return allocation.height;
+  } else {
+    return 0;
+  }
+}
+
+static gboolean
+animation_timeout_cb (gpointer user_data)
+{
+  GdNotification *notification = GD_NOTIFICATION (user_data);
+  GdNotificationPrivate *priv = notification->priv;
+  GtkAllocation allocation;
+  int target, delta;
+
+  target = animation_target (notification);
+
+  if (priv->animate_y != target) {
+    gtk_widget_get_allocation (GTK_WIDGET (notification), &allocation);
+
+    delta = allocation.height * ANIMATION_STEP / ANIMATION_TIME;
+
+    if (priv->revealed)
+      priv->animate_y += delta;
+    else
+      priv->animate_y -= delta;
+
+    priv->animate_y = CLAMP (priv->animate_y, 0, allocation.height);
+
+    if (priv->bin_window != NULL)
+      gdk_window_move (priv->bin_window,
+                       0,
+                       -allocation.height + priv->animate_y);
+    return G_SOURCE_CONTINUE;
+  }
+
+  if (priv->dismissed && priv->animate_y == 0)
+    gtk_widget_destroy (GTK_WIDGET (notification));
+
+  priv->animate_timeout = 0;
+  return G_SOURCE_REMOVE;
+}
+
+static void
+start_animation (GdNotification *notification)
+{
+  GdNotificationPrivate *priv = notification->priv;
+  int target;
+
+  if (priv->animate_timeout != 0)
+    return; /* Already running */
+
+  target = animation_target (notification);
+  if (priv->animate_y != target)
+    notification->priv->animate_timeout =
+      gdk_threads_add_timeout (ANIMATION_STEP,
+                               animation_timeout_cb,
+                               notification);
+}
+
+static void
+gd_notification_show (GtkWidget *widget)
+{
+  GdNotification *notification = GD_NOTIFICATION (widget);
+  GdNotificationPrivate *priv = notification->priv;
+
+  GTK_WIDGET_CLASS (gd_notification_parent_class)->show (widget);
+  priv->revealed = TRUE;
+  priv->waiting_for_viewable = TRUE;
+}
+
+static void
+gd_notification_hide (GtkWidget *widget)
+{
+  GdNotification *notification = GD_NOTIFICATION (widget);
+  GdNotificationPrivate *priv = notification->priv;
+
+  GTK_WIDGET_CLASS (gd_notification_parent_class)->hide (widget);
+  priv->revealed = FALSE;
+  priv->waiting_for_viewable = FALSE;
+}
+
+static void
+gd_notification_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+  GdNotification *notification = GD_NOTIFICATION (object);
+
+  g_return_if_fail (GTK_IS_NOTIFICATION (object));
+
+  switch (prop_id) {
+  case PROP_TIMEOUT:
+    gd_notification_set_timeout (notification,
+                                 g_value_get_int (value));
+    break;
+  case PROP_SHOW_CLOSE_BUTTON:
+    gd_notification_set_show_close_button (notification,
+                                           g_value_get_boolean (value));
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    break;
+  }
+}
+
+static void
+gd_notification_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+  g_return_if_fail (GTK_IS_NOTIFICATION (object));
+  GdNotification *notification = GD_NOTIFICATION (object);
+
+  switch (prop_id) {
+  case PROP_TIMEOUT:
+    g_value_set_int (value, notification->priv->timeout);
+    break;
+  case PROP_SHOW_CLOSE_BUTTON:
+    g_value_set_boolean (value,
+                         notification->priv->show_close_button);
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    break;
+  }
+}
+
+static void
+gd_notification_forall (GtkContainer *container,
+                         gboolean      include_internals,
+                         GtkCallback   callback,
+                         gpointer      callback_data)
+{
+  GtkBin *bin = GTK_BIN (container);
+  GdNotification *notification = GD_NOTIFICATION (container);
+  GdNotificationPrivate *priv = notification->priv;
+  GtkWidget *child;
+
+  child = gtk_bin_get_child (bin);
+  if (child)
+    (* callback) (child, callback_data);
+
+  if (include_internals)
+    (* callback) (priv->close_button, callback_data);
+}
+
+static void
+unqueue_autohide (GdNotification *notification)
+{
+  GdNotificationPrivate *priv = notification->priv;
+
+  if (priv->timeout_source_id)
+    {
+      g_source_remove (priv->timeout_source_id);
+      priv->timeout_source_id = 0;
+    }
+}
+
+static void
+queue_autohide (GdNotification *notification)
+{
+  GdNotificationPrivate *priv = notification->priv;
+
+  if (priv->timeout_source_id == 0 &&
+      priv->timeout != -1)
+    priv->timeout_source_id =
+      gdk_threads_add_timeout (priv->timeout * 1000,
+                               gd_notification_timeout_cb,
+                               notification);
+}
+
+static gboolean
+gd_notification_visibility_notify_event (GtkWidget          *widget,
+                                          GdkEventVisibility  *event)
+{
+  GdNotification *notification = GD_NOTIFICATION (widget);
+  GdNotificationPrivate *priv = notification->priv;
+
+  if (!gtk_widget_get_visible (widget))
+    return FALSE;
+
+  if (priv->waiting_for_viewable)
+    {
+      start_animation (notification);
+      priv->waiting_for_viewable = FALSE;
+    }
+
+  queue_autohide (notification);
+
+  return FALSE;
+}
+
+static gboolean
+gd_notification_enter_notify (GtkWidget        *widget,
+                              GdkEventCrossing *event)
+{
+  GdNotification *notification = GD_NOTIFICATION (widget);
+  GdNotificationPrivate *priv = notification->priv;
+
+  if ((event->window == priv->bin_window) &&
+      (event->detail != GDK_NOTIFY_INFERIOR))
+    {
+      unqueue_autohide (notification);
+    }
+
+  return FALSE;
+}
+
+static gboolean
+gd_notification_leave_notify (GtkWidget        *widget,
+                              GdkEventCrossing *event)
+{
+  GdNotification *notification = GD_NOTIFICATION (widget);
+  GdNotificationPrivate *priv = notification->priv;
+
+  if ((event->window == priv->bin_window) &&
+      (event->detail != GDK_NOTIFY_INFERIOR))
+    {
+      queue_autohide (notification);
+    }
+
+  return FALSE;
+}
+
+static void
+gd_notification_class_init (GdNotificationClass *klass)
+{
+  GObjectClass* object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass);
+  GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
+
+  object_class->finalize = gd_notification_finalize;
+  object_class->set_property = gd_notification_set_property;
+  object_class->get_property = gd_notification_get_property;
+
+  widget_class->show = gd_notification_show;
+  widget_class->hide = gd_notification_hide;
+  widget_class->destroy = gd_notification_destroy;
+  widget_class->get_preferred_width = gd_notification_get_preferred_width;
+  widget_class->get_preferred_height_for_width = gd_notification_get_preferred_height_for_width;
+  widget_class->get_preferred_height = gd_notification_get_preferred_height;
+  widget_class->get_preferred_width_for_height = gd_notification_get_preferred_width_for_height;
+  widget_class->size_allocate = gd_notification_size_allocate;
+  widget_class->draw = gd_notification_draw;
+  widget_class->realize = gd_notification_realize;
+  widget_class->unrealize = gd_notification_unrealize;
+  widget_class->visibility_notify_event = gd_notification_visibility_notify_event;
+  widget_class->enter_notify_event = gd_notification_enter_notify;
+  widget_class->leave_notify_event = gd_notification_leave_notify;
+
+  container_class->add = gd_notification_add;
+  container_class->forall = gd_notification_forall;
+  gtk_container_class_handle_border_width (container_class);
+
+
+  /**
+   * GdNotification:timeout:
+   *
+   * The time it takes to hide the widget, in seconds.
+   *
+   * Since: 0.1
+   */
+  g_object_class_install_property (object_class,
+                                   PROP_TIMEOUT,
+                                   g_param_spec_int("timeout", "timeout",
+                                                    "The time it takes to hide the widget, in seconds",
+                                                    -1, G_MAXINT, -1,
+                                                    GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+  g_object_class_install_property (object_class,
+                                   PROP_SHOW_CLOSE_BUTTON,
+                                   g_param_spec_boolean("show-close-button", "show-close-button",
+                                                        "Whether to show a stock close button that dismisses 
the notification",
+                                                        TRUE,
+                                                        GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+  notification_signals[DISMISSED] = g_signal_new ("dismissed",
+                                                  G_OBJECT_CLASS_TYPE (klass),
+                                                  G_SIGNAL_RUN_LAST,
+                                                  G_STRUCT_OFFSET (GdNotificationClass, dismissed),
+                                                  NULL,
+                                                  NULL,
+                                                  g_cclosure_marshal_VOID__VOID,
+                                                  G_TYPE_NONE,
+                                                  0);
+
+  g_type_class_add_private (object_class, sizeof (GdNotificationPrivate));
+}
+
+static void
+get_padding_and_border (GdNotification *notification,
+                        GtkBorder *border)
+{
+  GtkStyleContext *context;
+  GtkStateFlags state;
+  GtkBorder tmp;
+
+  context = gtk_widget_get_style_context (GTK_WIDGET (notification));
+  state = gtk_widget_get_state_flags (GTK_WIDGET (notification));
+
+  gtk_style_context_get_padding (context, state, border);
+
+  gtk_style_context_get_border (context, state, &tmp);
+  border->top += tmp.top;
+  border->right += tmp.right;
+  border->bottom += tmp.bottom;
+  border->left += tmp.left;
+}
+
+static gboolean
+gd_notification_draw (GtkWidget *widget, cairo_t *cr)
+{
+  GdNotification *notification = GD_NOTIFICATION (widget);
+  GdNotificationPrivate *priv = notification->priv;
+  GtkStyleContext *context;
+
+  if (gtk_cairo_should_draw_window (cr, priv->bin_window))
+    {
+      context = gtk_widget_get_style_context (widget);
+
+      gtk_render_background (context,  cr,
+                             0, 0,
+                             gtk_widget_get_allocated_width (widget),
+                             gtk_widget_get_allocated_height (widget));
+      gtk_render_frame (context,cr,
+                        0, 0,
+                        gtk_widget_get_allocated_width (widget),
+                        gtk_widget_get_allocated_height (widget));
+
+
+      if (GTK_WIDGET_CLASS (gd_notification_parent_class)->draw)
+        GTK_WIDGET_CLASS (gd_notification_parent_class)->draw(widget, cr);
+    }
+
+  return FALSE;
+}
+
+static void
+gd_notification_add (GtkContainer *container,
+                      GtkWidget    *child)
+{
+  GtkBin *bin = GTK_BIN (container);
+  GdNotification *notification = GD_NOTIFICATION (bin);
+  GdNotificationPrivate *priv = notification->priv;
+
+  g_return_if_fail (gtk_bin_get_child (bin) == NULL);
+
+  gtk_widget_set_parent_window (child, priv->bin_window);
+
+  GTK_CONTAINER_CLASS (gd_notification_parent_class)->add (container, child);
+}
+
+
+static void
+gd_notification_get_preferred_width (GtkWidget *widget, gint *minimum_size, gint *natural_size)
+{
+  GdNotification *notification = GD_NOTIFICATION (widget);
+  GdNotificationPrivate *priv = notification->priv;
+  GtkBin *bin = GTK_BIN (widget);
+  gint child_min, child_nat;
+  GtkWidget *child;
+  GtkBorder padding;
+  gint minimum, natural;
+
+  get_padding_and_border (notification, &padding);
+
+  minimum = 0;
+  natural = 0;
+
+  child = gtk_bin_get_child (bin);
+  if (child && gtk_widget_get_visible (child))
+    {
+      gtk_widget_get_preferred_width (child,
+                                      &child_min, &child_nat);
+      minimum += child_min;
+      natural += child_nat;
+    }
+
+  if (priv->show_close_button)
+    {
+      gtk_widget_get_preferred_width (priv->close_button,
+                                      &child_min, &child_nat);
+      minimum += child_min;
+      natural += child_nat;
+    }
+
+  minimum += padding.left + padding.right + 2 * SHADOW_OFFSET_X;
+  natural += padding.left + padding.right + 2 * SHADOW_OFFSET_X;
+
+ if (minimum_size)
+    *minimum_size = minimum;
+
+  if (natural_size)
+    *natural_size = natural;
+}
+
+static void
+gd_notification_get_preferred_width_for_height (GtkWidget *widget,
+                                                 gint height,
+                                                 gint *minimum_width,
+                                                 gint *natural_width)
+{
+  GdNotification *notification = GD_NOTIFICATION (widget);
+  GdNotificationPrivate *priv = notification->priv;
+  GtkBin *bin = GTK_BIN (widget);
+  gint child_min, child_nat, child_height;
+  GtkWidget *child;
+  GtkBorder padding;
+  gint minimum, natural;
+
+  get_padding_and_border (notification, &padding);
+
+  minimum = 0;
+  natural = 0;
+
+  child_height = height - SHADOW_OFFSET_Y - padding.top - padding.bottom;
+
+  child = gtk_bin_get_child (bin);
+  if (child && gtk_widget_get_visible (child))
+    {
+      gtk_widget_get_preferred_width_for_height (child, child_height,
+                                                 &child_min, &child_nat);
+      minimum += child_min;
+      natural += child_nat;
+    }
+
+  if (priv->show_close_button)
+    {
+      gtk_widget_get_preferred_width_for_height (priv->close_button, child_height,
+                                                 &child_min, &child_nat);
+      minimum += child_min;
+      natural += child_nat;
+    }
+
+  minimum += padding.left + padding.right + 2 * SHADOW_OFFSET_X;
+  natural += padding.left + padding.right + 2 * SHADOW_OFFSET_X;
+
+ if (minimum_width)
+    *minimum_width = minimum;
+
+  if (natural_width)
+    *natural_width = natural;
+}
+
+static void
+gd_notification_get_preferred_height_for_width (GtkWidget *widget,
+                                                 gint width,
+                                                 gint *minimum_height,
+                                                 gint *natural_height)
+{
+  GdNotification *notification = GD_NOTIFICATION (widget);
+  GdNotificationPrivate *priv = notification->priv;
+  GtkBin *bin = GTK_BIN (widget);
+  gint child_min, child_nat, child_width, button_width = 0;
+  GtkWidget *child;
+  GtkBorder padding;
+  gint minimum = 0, natural = 0;
+
+  get_padding_and_border (notification, &padding);
+
+  if (priv->show_close_button)
+    {
+      gtk_widget_get_preferred_height (priv->close_button,
+                                       &minimum, &natural);
+      gtk_widget_get_preferred_width (priv->close_button,
+                                      NULL, &button_width);
+    }
+
+  child = gtk_bin_get_child (bin);
+  if (child && gtk_widget_get_visible (child))
+    {
+      child_width = width - button_width -
+        2 * SHADOW_OFFSET_X - padding.left - padding.right;
+
+      gtk_widget_get_preferred_height_for_width (child, child_width,
+                                                 &child_min, &child_nat);
+      minimum = MAX (minimum, child_min);
+      natural = MAX (natural, child_nat);
+    }
+
+  minimum += padding.top + padding.bottom + SHADOW_OFFSET_Y;
+  natural += padding.top + padding.bottom + SHADOW_OFFSET_Y;
+
+ if (minimum_height)
+    *minimum_height = minimum;
+
+  if (natural_height)
+    *natural_height = natural;
+}
+
+static void
+gd_notification_get_preferred_height (GtkWidget *widget, 
+                                      gint *minimum_height, 
+                                      gint *natural_height)
+{
+  gint width;
+
+  gd_notification_get_preferred_width (widget, &width, NULL);
+  gd_notification_get_preferred_height_for_width (widget, width,
+                                                  minimum_height, natural_height);
+}
+
+static void
+gd_notification_size_allocate (GtkWidget *widget,
+                                GtkAllocation *allocation)
+{
+  GdNotification *notification = GD_NOTIFICATION (widget);
+  GdNotificationPrivate *priv = notification->priv;
+  GtkBin *bin = GTK_BIN (widget);
+  GtkAllocation child_allocation;
+  GtkBorder padding;
+  GtkRequisition button_req;
+  GtkWidget *child;
+
+  gtk_widget_set_allocation (widget, allocation);
+
+  /* If somehow the notification changes while not hidden
+     and we're not animating, immediately follow the resize */
+  if (priv->animate_y > 0 &&
+      !priv->animate_timeout)
+    priv->animate_y = allocation->height;
+
+  get_padding_and_border (notification, &padding);
+
+  if (gtk_widget_get_realized (widget))
+    {
+      gdk_window_move_resize (gtk_widget_get_window (widget),
+                              allocation->x,
+                              allocation->y,
+                              allocation->width,
+                              allocation->height);
+      gdk_window_move_resize (priv->bin_window,
+                              0,
+                              -allocation->height + priv->animate_y,
+                              allocation->width,
+                              allocation->height);
+    }
+
+  child_allocation.x = SHADOW_OFFSET_X + padding.left;
+  child_allocation.y = padding.top;
+
+  if (priv->show_close_button)
+    gtk_widget_get_preferred_size (priv->close_button, &button_req, NULL);
+  else
+    button_req.width = button_req.height = 0;
+
+  child_allocation.height = MAX (1, allocation->height - SHADOW_OFFSET_Y - padding.top - padding.bottom);
+  child_allocation.width = MAX (1, (allocation->width - button_req.width -
+                                    2 * SHADOW_OFFSET_X - padding.left - padding.right));
+
+  child = gtk_bin_get_child (bin);
+  if (child && gtk_widget_get_visible (child))
+    gtk_widget_size_allocate (child, &child_allocation);
+
+  if (priv->show_close_button)
+    {
+      child_allocation.x += child_allocation.width;
+      child_allocation.width = button_req.width;
+      child_allocation.y += (child_allocation.height - button_req.height) / 2;
+      child_allocation.height = button_req.height;
+
+      gtk_widget_size_allocate (priv->close_button, &child_allocation);
+    }
+}
+
+static gboolean
+gd_notification_timeout_cb (gpointer user_data)
+{
+  GdNotification *notification = GD_NOTIFICATION (user_data);
+
+  gd_notification_dismiss (notification);
+
+  return G_SOURCE_REMOVE;
+}
+
+void
+gd_notification_set_timeout (GdNotification *notification,
+                             gint            timeout_sec)
+{
+  GdNotificationPrivate *priv = notification->priv;
+
+  priv->timeout = timeout_sec;
+  g_object_notify (G_OBJECT (notification), "timeout");
+}
+
+void
+gd_notification_set_show_close_button (GdNotification *notification,
+                                       gboolean show_close_button)
+{
+  GdNotificationPrivate *priv = notification->priv;
+
+  priv->show_close_button = show_close_button;
+
+  gtk_widget_set_visible (priv->close_button, show_close_button);
+  gtk_widget_queue_resize (GTK_WIDGET (notification));
+}
+
+void
+gd_notification_dismiss (GdNotification *notification)
+{
+  GdNotificationPrivate *priv = notification->priv;
+
+  unqueue_autohide (notification);
+
+  priv->dismissed = TRUE;
+  priv->revealed = FALSE;
+  start_animation (notification);
+}
+
+static void
+gd_notification_close_button_clicked_cb (GtkWidget *widget, gpointer user_data)
+{
+  GdNotification *notification = GD_NOTIFICATION(user_data);
+
+  gd_notification_dismiss (notification);
+}
+
+GtkWidget *
+gd_notification_new (void)
+{
+  return g_object_new (GD_TYPE_NOTIFICATION, NULL);
+}
diff --git a/subprojects/libgd/libgd/gd-notification.h b/subprojects/libgd/libgd/gd-notification.h
new file mode 100644
index 0000000..8efa191
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-notification.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * gd-notification
+ * Based on gtk-notification from gnome-contacts:
+ * http://git.gnome.org/browse/gnome-contacts/tree/src/gtk-notification.c?id=3.3.91
+ *
+ * Copyright (C) Erick PĂ©rez Castellanos 2011 <erick red gmail com>
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.";
+ */
+
+#ifndef _GD_NOTIFICATION_H_
+#define _GD_NOTIFICATION_H_
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GD_TYPE_NOTIFICATION             (gd_notification_get_type ())
+#define GD_NOTIFICATION(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GD_TYPE_NOTIFICATION, 
GdNotification))
+#define GD_NOTIFICATION_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GD_TYPE_NOTIFICATION, 
GdNotificationClass))
+#define GTK_IS_NOTIFICATION(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GD_TYPE_NOTIFICATION))
+#define GTK_IS_NOTIFICATION_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GD_TYPE_NOTIFICATION))
+#define GD_NOTIFICATION_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GD_TYPE_NOTIFICATION, 
GdNotificationClass))
+
+typedef struct _GdNotificationPrivate GdNotificationPrivate;
+typedef struct _GdNotificationClass GdNotificationClass;
+typedef struct _GdNotification GdNotification;
+
+struct _GdNotificationClass {
+  GtkBinClass parent_class;
+
+  /* Signals */
+  void (*dismissed) (GdNotification *self);
+};
+
+struct _GdNotification {
+  GtkBin parent_instance;
+
+  /*< private > */
+  GdNotificationPrivate *priv;
+};
+
+GType gd_notification_get_type (void) G_GNUC_CONST;
+
+GtkWidget *gd_notification_new         (void);
+void       gd_notification_set_timeout (GdNotification *notification,
+                                        gint            timeout_sec);
+void       gd_notification_dismiss     (GdNotification *notification);
+void       gd_notification_set_show_close_button (GdNotification *notification,
+                                                  gboolean show_close_button);
+
+G_END_DECLS
+
+#endif /* _GD_NOTIFICATION_H_ */
diff --git a/subprojects/libgd/libgd/gd-styled-text-renderer.c 
b/subprojects/libgd/libgd/gd-styled-text-renderer.c
new file mode 100644
index 0000000..50a315e
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-styled-text-renderer.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2011 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by 
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Lesser General Public 
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License 
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Cosimo Cecchi <cosimoc redhat com>
+ *
+ */
+
+#include "gd-styled-text-renderer.h"
+
+G_DEFINE_TYPE (GdStyledTextRenderer, gd_styled_text_renderer, GTK_TYPE_CELL_RENDERER_TEXT);
+
+struct _GdStyledTextRendererPrivate {
+  GList *style_classes;
+};
+
+static void
+gd_styled_text_renderer_render (GtkCellRenderer      *cell,
+                                cairo_t              *cr,
+                                GtkWidget            *widget,
+                                const GdkRectangle   *background_area,
+                                const GdkRectangle   *cell_area,
+                                GtkCellRendererState  flags)
+{
+  GdStyledTextRenderer *self = GD_STYLED_TEXT_RENDERER (cell);
+  GtkStyleContext *context;
+  const gchar *style_class;
+  GList *l;
+
+  context = gtk_widget_get_style_context (widget);
+  gtk_style_context_save (context);
+
+  for (l = self->priv->style_classes; l != NULL; l = l->next)
+    {
+      style_class = l->data;
+      gtk_style_context_add_class (context, style_class);
+    }
+
+  GTK_CELL_RENDERER_CLASS (gd_styled_text_renderer_parent_class)->render 
+    (cell, cr, widget,
+     background_area, cell_area, flags);
+
+  gtk_style_context_restore (context);
+}
+
+static void
+gd_styled_text_renderer_finalize (GObject *obj)
+{
+  GdStyledTextRenderer *self = GD_STYLED_TEXT_RENDERER (obj);
+
+  if (self->priv->style_classes != NULL)
+    {
+      g_list_free_full (self->priv->style_classes, g_free);
+      self->priv->style_classes = NULL;
+    }
+
+  G_OBJECT_CLASS (gd_styled_text_renderer_parent_class)->finalize (obj);
+}
+
+static void
+gd_styled_text_renderer_class_init (GdStyledTextRendererClass *klass)
+{
+  GtkCellRendererClass *crclass = GTK_CELL_RENDERER_CLASS (klass);
+  GObjectClass *oclass = G_OBJECT_CLASS (klass);
+
+  oclass->finalize = gd_styled_text_renderer_finalize;
+  crclass->render = gd_styled_text_renderer_render;
+
+  g_type_class_add_private (klass, sizeof (GdStyledTextRendererPrivate));
+}
+
+static void
+gd_styled_text_renderer_init (GdStyledTextRenderer *self)
+{
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GD_TYPE_STYLED_TEXT_RENDERER,
+                                            GdStyledTextRendererPrivate);
+}
+
+GtkCellRenderer *
+gd_styled_text_renderer_new (void)
+{
+  return g_object_new (GD_TYPE_STYLED_TEXT_RENDERER,
+                       NULL);
+}
+
+void
+gd_styled_text_renderer_add_class (GdStyledTextRenderer *self,
+                                   const gchar *class)
+{
+  if (g_list_find_custom (self->priv->style_classes, class, (GCompareFunc) g_strcmp0))
+    return;
+
+  self->priv->style_classes = g_list_append (self->priv->style_classes, g_strdup (class));
+}
+
+void
+gd_styled_text_renderer_remove_class (GdStyledTextRenderer *self,
+                                      const gchar *class)
+{
+  GList *class_element;
+
+  class_element = g_list_find_custom (self->priv->style_classes, class, (GCompareFunc) g_strcmp0);
+
+  if (class_element == NULL)
+    return;
+
+  self->priv->style_classes = g_list_remove_link (self->priv->style_classes,
+                                                  class_element);
+  g_free (class_element->data);
+  g_list_free_1 (class_element);
+}
diff --git a/subprojects/libgd/libgd/gd-styled-text-renderer.h 
b/subprojects/libgd/libgd/gd-styled-text-renderer.h
new file mode 100644
index 0000000..fc1995b
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-styled-text-renderer.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2011 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by 
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Lesser General Public 
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License 
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Cosimo Cecchi <cosimoc redhat com>
+ *
+ */
+
+#ifndef _GD_STYLED_TEXT_RENDERER_H
+#define _GD_STYLED_TEXT_RENDERER_H
+
+#include <glib-object.h>
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GD_TYPE_STYLED_TEXT_RENDERER gd_styled_text_renderer_get_type()
+
+#define GD_STYLED_TEXT_RENDERER(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+   GD_TYPE_STYLED_TEXT_RENDERER, GdStyledTextRenderer))
+
+#define GD_STYLED_TEXT_RENDERER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), \
+   GD_TYPE_STYLED_TEXT_RENDERER, GdStyledTextRendererClass))
+
+#define GD_IS_STYLED_TEXT_RENDERER(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+   GD_TYPE_STYLED_TEXT_RENDERER))
+
+#define GD_IS_STYLED_TEXT_RENDERER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+   GD_TYPE_STYLED_TEXT_RENDERER))
+
+#define GD_STYLED_TEXT_RENDERER_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+   GD_TYPE_STYLED_TEXT_RENDERER, GdStyledTextRendererClass))
+
+typedef struct _GdStyledTextRenderer GdStyledTextRenderer;
+typedef struct _GdStyledTextRendererClass GdStyledTextRendererClass;
+typedef struct _GdStyledTextRendererPrivate GdStyledTextRendererPrivate;
+
+struct _GdStyledTextRenderer
+{
+  GtkCellRendererText parent;
+
+  GdStyledTextRendererPrivate *priv;
+};
+
+struct _GdStyledTextRendererClass
+{
+  GtkCellRendererTextClass parent_class;
+};
+
+GType gd_styled_text_renderer_get_type (void) G_GNUC_CONST;
+
+GtkCellRenderer *gd_styled_text_renderer_new (void);
+void gd_styled_text_renderer_add_class (GdStyledTextRenderer *self,
+                                        const gchar *class);
+void gd_styled_text_renderer_remove_class (GdStyledTextRenderer *self,
+                                           const gchar *class);
+
+G_END_DECLS
+
+#endif /* _GD_STYLED_TEXT_RENDERER_H */
diff --git a/subprojects/libgd/libgd/gd-tagged-entry.c b/subprojects/libgd/libgd/gd-tagged-entry.c
new file mode 100644
index 0000000..380962c
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-tagged-entry.c
@@ -0,0 +1,1242 @@
+/*
+ * Copyright (c) 2011 Red Hat, Inc.
+ * Copyright (c) 2013 Ignacio Casal Quinteiro
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by 
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Lesser General Public 
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License 
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Cosimo Cecchi <cosimoc redhat com>
+ *
+ */
+
+#include "gd-tagged-entry.h"
+
+#include <math.h>
+
+#define BUTTON_INTERNAL_SPACING 6
+
+struct _GdTaggedEntryTagPrivate {
+  GdTaggedEntry *entry;
+  GdkWindow *window;
+  PangoLayout *layout;
+
+  gchar *label;
+  gchar *style;
+  gboolean has_close_button;
+
+  cairo_surface_t *close_surface;
+  GtkStateFlags last_button_state;
+};
+
+struct _GdTaggedEntryPrivate {
+  GList *tags;
+
+  GdTaggedEntryTag *in_child;
+  gboolean in_child_button;
+  gboolean in_child_active;
+  gboolean in_child_button_active;
+  gboolean button_visible;
+};
+
+enum {
+  SIGNAL_TAG_CLICKED,
+  SIGNAL_TAG_BUTTON_CLICKED,
+  LAST_SIGNAL
+};
+
+enum {
+  PROP_0,
+  PROP_TAG_BUTTON_VISIBLE,
+  NUM_PROPERTIES
+};
+
+enum {
+  PROP_TAG_0,
+  PROP_TAG_LABEL,
+  PROP_TAG_HAS_CLOSE_BUTTON,
+  PROP_TAG_STYLE,
+  NUM_TAG_PROPERTIES
+};
+
+G_DEFINE_TYPE (GdTaggedEntry, gd_tagged_entry, GTK_TYPE_SEARCH_ENTRY)
+G_DEFINE_TYPE (GdTaggedEntryTag, gd_tagged_entry_tag, G_TYPE_OBJECT)
+
+static guint signals[LAST_SIGNAL] = { 0, };
+static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
+static GParamSpec *tag_properties[NUM_TAG_PROPERTIES] = { NULL, };
+
+static void gd_tagged_entry_get_text_area_size (GtkEntry *entry,
+                                                gint *x,
+                                                gint *y,
+                                                gint *width,
+                                                gint *height);
+static gint gd_tagged_entry_tag_get_width (GdTaggedEntryTag *tag,
+                                           GdTaggedEntry *entry);
+static GtkStyleContext * gd_tagged_entry_tag_get_context (GdTaggedEntryTag *tag,
+                                                          GdTaggedEntry *entry);
+
+static void
+gd_tagged_entry_tag_get_margin (GdTaggedEntryTag *tag,
+                                GdTaggedEntry *entry,
+                                GtkBorder *margin)
+{
+  GtkStyleContext *context;
+
+  context = gd_tagged_entry_tag_get_context (tag, entry);
+  gtk_style_context_set_state (context, GTK_STATE_FLAG_NORMAL);
+  gtk_style_context_get_margin (context,
+                                gtk_style_context_get_state (context),
+                                margin);
+  gtk_style_context_restore (context);
+}
+
+static void
+gd_tagged_entry_tag_ensure_close_surface (GdTaggedEntryTag *tag,
+                                          GtkStyleContext *context)
+{
+  GtkIconInfo *info;
+  GdkPixbuf *pixbuf;
+  gint icon_size;
+  gint scale_factor;
+
+  if (tag->priv->close_surface != NULL)
+    return;
+
+  gtk_icon_size_lookup (GTK_ICON_SIZE_MENU,
+                        &icon_size, NULL);
+  scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (tag->priv->entry));
+
+  info = gtk_icon_theme_lookup_icon_for_scale (gtk_icon_theme_get_default (),
+                                               "window-close-symbolic",
+                                               icon_size, scale_factor,
+                                               GTK_ICON_LOOKUP_GENERIC_FALLBACK);
+
+  /* FIXME: we need a fallback icon in case the icon is not found */
+  pixbuf = gtk_icon_info_load_symbolic_for_context (info, context, NULL, NULL);
+  tag->priv->close_surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, scale_factor, tag->priv->window);
+
+  g_object_unref (info);
+  g_object_unref (pixbuf);
+}
+
+static gint
+gd_tagged_entry_tag_panel_get_height (GdTaggedEntryTag *tag,
+                                      GdTaggedEntry *entry)
+{
+  GtkWidget *widget = GTK_WIDGET (entry);
+  gint height, req_height;
+  GtkRequisition requisition;
+  GtkAllocation allocation;
+  GtkBorder margin;
+
+  gtk_widget_get_allocation (widget, &allocation);
+  gtk_widget_get_preferred_size (widget, &requisition, NULL);
+  gd_tagged_entry_tag_get_margin (tag, entry, &margin);
+
+  /* the tag panel height is the whole entry height, minus the tag margins */
+  req_height = requisition.height - gtk_widget_get_margin_top (widget) - gtk_widget_get_margin_bottom 
(widget);
+  height = MIN (req_height, allocation.height) - margin.top - margin.bottom;
+
+  return height;
+}
+
+static void
+gd_tagged_entry_tag_panel_get_position (GdTaggedEntry *self,
+                                        gint *x_out, 
+                                        gint *y_out)
+{
+  GtkWidget *widget = GTK_WIDGET (self);
+  gint text_x, text_y, text_width, text_height, req_height;
+  GtkAllocation allocation;
+  GtkRequisition requisition;
+
+  gtk_widget_get_allocation (widget, &allocation);
+  gtk_widget_get_preferred_size (widget, &requisition, NULL);
+  req_height = requisition.height - gtk_widget_get_margin_top (widget) - gtk_widget_get_margin_bottom 
(widget);
+
+  gd_tagged_entry_get_text_area_size (GTK_ENTRY (self), &text_x, &text_y, &text_width, &text_height);
+
+  /* allocate the panel immediately after the text area */
+  if (x_out)
+    *x_out = allocation.x + text_x + text_width;
+  if (y_out)
+    *y_out = allocation.y + (gint) floor ((allocation.height - req_height) / 2);
+}
+
+static gint
+gd_tagged_entry_tag_panel_get_width (GdTaggedEntry *self)
+{
+  GdTaggedEntryTag *tag;
+  gint width;
+  GList *l;
+
+  width = 0;
+
+  for (l = self->priv->tags; l != NULL; l = l->next)
+    {
+      tag = l->data;
+      width += gd_tagged_entry_tag_get_width (tag, self);
+    }
+
+  return width;
+}
+
+static void
+gd_tagged_entry_tag_ensure_layout (GdTaggedEntryTag *tag,
+                                   GdTaggedEntry *entry)
+{
+  if (tag->priv->layout != NULL)
+    return;
+
+  tag->priv->layout = pango_layout_new (gtk_widget_get_pango_context (GTK_WIDGET (entry)));
+  pango_layout_set_text (tag->priv->layout, tag->priv->label, -1);
+}
+
+static GtkStateFlags
+gd_tagged_entry_tag_get_state (GdTaggedEntryTag *tag,
+                               GdTaggedEntry *entry)
+{
+  GtkStateFlags state = GTK_STATE_FLAG_NORMAL;
+
+  if (entry->priv->in_child == tag)
+    state |= GTK_STATE_FLAG_PRELIGHT;
+
+  if (entry->priv->in_child_active)
+    state |= GTK_STATE_FLAG_ACTIVE;
+
+  return state;
+}
+
+static GtkStateFlags
+gd_tagged_entry_tag_get_button_state (GdTaggedEntryTag *tag,
+                                      GdTaggedEntry *entry)
+{
+  GtkStateFlags state = GTK_STATE_FLAG_NORMAL;
+
+  if (entry->priv->in_child == tag)
+    {
+      if (entry->priv->in_child_button_active)
+        state |= GTK_STATE_FLAG_ACTIVE;
+
+      else if (entry->priv->in_child_button)
+        state |= GTK_STATE_FLAG_PRELIGHT;
+    }
+
+  return state;
+}
+
+static GtkStyleContext *
+gd_tagged_entry_tag_get_context (GdTaggedEntryTag *tag,
+                                 GdTaggedEntry    *entry)
+{
+  GtkWidget *widget = GTK_WIDGET (entry);
+  GtkStyleContext *retval;
+  GList *l, *list;
+
+  retval = gtk_widget_get_style_context (widget);
+  gtk_style_context_save (retval);
+
+  list = gtk_style_context_list_classes (retval);
+  for (l = list; l; l = l->next)
+    gtk_style_context_remove_class (retval, l->data);
+  g_list_free (list);
+  gtk_style_context_add_class (retval, tag->priv->style);
+
+  return retval;
+}
+
+static gint
+gd_tagged_entry_tag_get_width (GdTaggedEntryTag *tag,
+                               GdTaggedEntry *entry)
+{
+  GtkBorder button_padding, button_border, button_margin;
+  GtkStyleContext *context;
+  GtkStateFlags state;
+  gint layout_width;
+  gint button_width;
+  gint scale_factor;
+
+  gd_tagged_entry_tag_ensure_layout (tag, entry);
+  pango_layout_get_pixel_size (tag->priv->layout, &layout_width, NULL);
+
+  context = gd_tagged_entry_tag_get_context (tag, entry);
+  state = gd_tagged_entry_tag_get_state (tag, entry);
+
+  gtk_style_context_set_state (context, state);
+  gtk_style_context_get_padding (context,
+                                 gtk_style_context_get_state (context),
+                                 &button_padding);
+  gtk_style_context_get_border (context,
+                                gtk_style_context_get_state (context),
+                                &button_border);
+  gtk_style_context_get_margin (context,
+                                gtk_style_context_get_state (context),
+                                &button_margin);
+
+  gd_tagged_entry_tag_ensure_close_surface (tag, context);
+
+  gtk_style_context_restore (context);
+
+  button_width = 0;
+  if (entry->priv->button_visible && tag->priv->has_close_button)
+    {
+      scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (entry));
+      button_width = cairo_image_surface_get_width (tag->priv->close_surface) / scale_factor +
+        BUTTON_INTERNAL_SPACING;
+    }
+
+  return layout_width + button_padding.left + button_padding.right +
+    button_border.left + button_border.right +
+    button_margin.left + button_margin.right +
+    button_width;
+}
+
+static void
+gd_tagged_entry_tag_get_size (GdTaggedEntryTag *tag,
+                              GdTaggedEntry *entry,
+                              gint *width_out,
+                              gint *height_out)
+{
+  gint width, panel_height;
+
+  width = gd_tagged_entry_tag_get_width (tag, entry);
+  panel_height = gd_tagged_entry_tag_panel_get_height (tag, entry);
+
+  if (width_out)
+    *width_out = width;
+  if (height_out)
+    *height_out = panel_height;
+}
+
+static void
+gd_tagged_entry_tag_get_relative_allocations (GdTaggedEntryTag *tag,
+                                              GdTaggedEntry *entry,
+                                              GtkStyleContext *context,
+                                              GtkAllocation *background_allocation_out,
+                                              GtkAllocation *layout_allocation_out,
+                                              GtkAllocation *button_allocation_out)
+{
+  GtkAllocation background_allocation, layout_allocation, button_allocation;
+  gint width, height, x, y, pix_width, pix_height;
+  gint layout_width, layout_height;
+  gint scale_factor;
+  GtkBorder padding, border;
+  GtkStateFlags state;
+
+  width = gdk_window_get_width (tag->priv->window);
+  height = gdk_window_get_height (tag->priv->window);
+  scale_factor = gdk_window_get_scale_factor (tag->priv->window);
+
+  state = gd_tagged_entry_tag_get_state (tag, entry);
+  gtk_style_context_save (context);
+  gtk_style_context_set_state (context, state);
+  gtk_style_context_get_margin (context,
+                                gtk_style_context_get_state (context),
+                                &padding);
+  gtk_style_context_restore (context);
+
+  width -= padding.left + padding.right;
+  height -= padding.top + padding.bottom;
+  x = padding.left;
+  y = padding.top;
+
+  background_allocation.x = x;
+  background_allocation.y = y;
+  background_allocation.width = width;
+  background_allocation.height = height;
+
+  layout_allocation = button_allocation = background_allocation;
+
+  gtk_style_context_save (context);
+  gtk_style_context_set_state (context, state);
+  gtk_style_context_get_padding (context,
+                                 gtk_style_context_get_state (context),
+                                 &padding);
+  gtk_style_context_get_border (context,
+                                gtk_style_context_get_state (context),
+                                &border);
+  gtk_style_context_restore (context);
+
+  gd_tagged_entry_tag_ensure_layout (tag, entry);
+  pango_layout_get_pixel_size (tag->priv->layout, &layout_width, &layout_height);
+
+  layout_allocation.x += border.left + padding.left;
+  layout_allocation.y += (layout_allocation.height - layout_height) / 2;
+
+  if (entry->priv->button_visible && tag->priv->has_close_button)
+    {
+      pix_width = cairo_image_surface_get_width (tag->priv->close_surface) / scale_factor;
+      pix_height = cairo_image_surface_get_height (tag->priv->close_surface) / scale_factor;
+    }
+  else
+    {
+      pix_width = 0;
+      pix_height = 0;
+    }
+
+  button_allocation.x += width - pix_width - border.right - padding.right;
+  button_allocation.y += (height - pix_height) / 2;
+  button_allocation.width = pix_width;
+  button_allocation.height = pix_height;
+
+  if (background_allocation_out)
+    *background_allocation_out = background_allocation;
+  if (layout_allocation_out)
+    *layout_allocation_out = layout_allocation;
+  if (button_allocation_out)
+    *button_allocation_out = button_allocation;
+}
+
+static gboolean
+gd_tagged_entry_tag_event_is_button (GdTaggedEntryTag *tag,
+                                     GdTaggedEntry *entry,
+                                     gdouble event_x,
+                                     gdouble event_y)
+{
+  GtkAllocation button_allocation;
+  GtkStyleContext *context;
+
+  if (!entry->priv->button_visible || !tag->priv->has_close_button)
+    return FALSE;
+
+  context = gd_tagged_entry_tag_get_context (tag, entry);
+  gd_tagged_entry_tag_get_relative_allocations (tag, entry, context, NULL, NULL, &button_allocation);
+
+  gtk_style_context_restore (context);
+
+  /* see if the event falls into the button allocation */
+  if ((event_x >= button_allocation.x && 
+       event_x <= button_allocation.x + button_allocation.width) &&
+      (event_y >= button_allocation.y &&
+       event_y <= button_allocation.y + button_allocation.height))
+    return TRUE;
+
+  return FALSE;
+}
+
+gboolean
+gd_tagged_entry_tag_get_area (GdTaggedEntryTag      *tag,
+                              cairo_rectangle_int_t *rect)
+{
+  GtkStyleContext *context;
+  GtkAllocation background_allocation;
+  int window_x, window_y;
+  GtkAllocation alloc;
+
+  g_return_val_if_fail (GD_IS_TAGGED_ENTRY_TAG (tag), FALSE);
+  g_return_val_if_fail (rect != NULL, FALSE);
+
+  gdk_window_get_position (tag->priv->window, &window_x, &window_y);
+  gtk_widget_get_allocation (GTK_WIDGET (tag->priv->entry), &alloc);
+  context = gd_tagged_entry_tag_get_context (tag, tag->priv->entry);
+  gd_tagged_entry_tag_get_relative_allocations (tag, tag->priv->entry, context,
+                                                &background_allocation,
+                                                NULL, NULL);
+  gtk_style_context_restore (context);
+
+  rect->x = window_x - alloc.x + background_allocation.x;
+  rect->y = window_y - alloc.y + background_allocation.y;
+  rect->width = background_allocation.width;
+  rect->height = background_allocation.height;
+
+  return TRUE;
+}
+
+static void
+gd_tagged_entry_tag_draw (GdTaggedEntryTag *tag,
+                          cairo_t *cr,
+                          GdTaggedEntry *entry)
+{
+  GtkStyleContext *context;
+  GtkStateFlags state;
+  GtkAllocation background_allocation, layout_allocation, button_allocation;
+
+  context = gd_tagged_entry_tag_get_context (tag, entry);
+  gd_tagged_entry_tag_get_relative_allocations (tag, entry, context,
+                                                &background_allocation,
+                                                &layout_allocation,
+                                                &button_allocation);
+
+  cairo_save (cr);
+  gtk_cairo_transform_to_window (cr, GTK_WIDGET (entry), tag->priv->window);
+
+  gtk_style_context_save (context);
+
+  state = gd_tagged_entry_tag_get_state (tag, entry);
+  gtk_style_context_set_state (context, state);
+  gtk_render_background (context, cr,
+                         background_allocation.x, background_allocation.y,
+                         background_allocation.width, background_allocation.height); 
+  gtk_render_frame (context, cr,
+                    background_allocation.x, background_allocation.y,
+                    background_allocation.width, background_allocation.height); 
+
+  gtk_render_layout (context, cr,
+                     layout_allocation.x, layout_allocation.y,
+                     tag->priv->layout);
+
+  gtk_style_context_restore (context);
+
+  if (!entry->priv->button_visible || !tag->priv->has_close_button)
+    goto done;
+
+  gtk_style_context_add_class (context, GTK_STYLE_CLASS_BUTTON);
+  state = gd_tagged_entry_tag_get_button_state (tag, entry);
+  gtk_style_context_set_state (context, state);
+
+  /* if the state changed since last time we draw the pixbuf,
+   * clear and redraw it.
+   */
+  if (state != tag->priv->last_button_state)
+    {
+      g_clear_pointer (&tag->priv->close_surface, cairo_surface_destroy);
+      gd_tagged_entry_tag_ensure_close_surface (tag, context);
+
+      tag->priv->last_button_state = state;
+    }
+
+  gtk_render_background (context, cr,
+                         button_allocation.x, button_allocation.y,
+                         button_allocation.width, button_allocation.height);
+  gtk_render_frame (context, cr,
+                         button_allocation.x, button_allocation.y,
+                         button_allocation.width, button_allocation.height);
+
+  gtk_render_icon_surface (context, cr,
+                           tag->priv->close_surface,
+                           button_allocation.x, button_allocation.y);
+
+done:
+  gtk_style_context_restore (context);
+
+  cairo_restore (cr);
+}
+
+static void
+gd_tagged_entry_tag_unrealize (GdTaggedEntryTag *tag)
+{
+  if (tag->priv->window == NULL)
+    return;
+
+  gdk_window_set_user_data (tag->priv->window, NULL);
+  gdk_window_destroy (tag->priv->window);
+  tag->priv->window = NULL;
+}
+
+static void
+gd_tagged_entry_tag_realize (GdTaggedEntryTag *tag,
+                             GdTaggedEntry *entry)
+{
+  GtkWidget *widget = GTK_WIDGET (entry);
+  GdkWindowAttr attributes;
+  gint attributes_mask;
+  gint tag_width, tag_height;
+
+  if (tag->priv->window != NULL)
+    return;
+
+  attributes.window_type = GDK_WINDOW_CHILD;
+  attributes.wclass = GDK_INPUT_ONLY;
+  attributes.event_mask = gtk_widget_get_events (widget);
+  attributes.event_mask |= GDK_BUTTON_PRESS_MASK
+    | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_ENTER_NOTIFY_MASK
+    | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK;
+
+  gd_tagged_entry_tag_get_size (tag, entry, &tag_width, &tag_height);
+  attributes.x = 0;
+  attributes.y = 0;
+  attributes.width = tag_width;
+  attributes.height = tag_height;
+
+  attributes_mask = GDK_WA_X | GDK_WA_Y;
+
+  tag->priv->window = gdk_window_new (gtk_widget_get_window (widget),
+                                &attributes, attributes_mask);
+  gdk_window_set_user_data (tag->priv->window, widget);
+}
+
+static gboolean
+gd_tagged_entry_draw (GtkWidget *widget,
+                      cairo_t *cr)
+{
+  GdTaggedEntry *self = GD_TAGGED_ENTRY (widget);
+  GdTaggedEntryTag *tag;
+  GList *l;
+
+  GTK_WIDGET_CLASS (gd_tagged_entry_parent_class)->draw (widget, cr);
+
+  for (l = self->priv->tags; l != NULL; l = l->next)
+    {
+      tag = l->data;
+      gd_tagged_entry_tag_draw (tag, cr, self);
+    }
+
+  return FALSE;
+}
+
+static void
+gd_tagged_entry_map (GtkWidget *widget)
+{
+  GdTaggedEntry *self = GD_TAGGED_ENTRY (widget);
+  GdTaggedEntryTag *tag;
+  GList *l;
+
+  if (gtk_widget_get_realized (widget) && !gtk_widget_get_mapped (widget))
+    {
+      GTK_WIDGET_CLASS (gd_tagged_entry_parent_class)->map (widget);
+
+      for (l = self->priv->tags; l != NULL; l = l->next)
+        {
+          tag = l->data;
+          gdk_window_show (tag->priv->window);
+        }
+    }
+}
+
+static void
+gd_tagged_entry_unmap (GtkWidget *widget)
+{
+  GdTaggedEntry *self = GD_TAGGED_ENTRY (widget);
+  GdTaggedEntryTag *tag;
+  GList *l;
+
+  if (gtk_widget_get_mapped (widget))
+    {
+      for (l = self->priv->tags; l != NULL; l = l->next)
+        {
+          tag = l->data;
+          gdk_window_hide (tag->priv->window);
+        }
+
+      GTK_WIDGET_CLASS (gd_tagged_entry_parent_class)->unmap (widget);
+    }
+}
+
+static void
+gd_tagged_entry_realize (GtkWidget *widget)
+{
+  GdTaggedEntry *self = GD_TAGGED_ENTRY (widget);
+  GdTaggedEntryTag *tag;
+  GList *l;
+
+  GTK_WIDGET_CLASS (gd_tagged_entry_parent_class)->realize (widget);
+
+  for (l = self->priv->tags; l != NULL; l = l->next)
+    {
+      tag = l->data;
+      gd_tagged_entry_tag_realize (tag, self);
+    }
+}
+
+static void
+gd_tagged_entry_unrealize (GtkWidget *widget)
+{
+  GdTaggedEntry *self = GD_TAGGED_ENTRY (widget);
+  GdTaggedEntryTag *tag;
+  GList *l;
+
+  GTK_WIDGET_CLASS (gd_tagged_entry_parent_class)->unrealize (widget);
+
+  for (l = self->priv->tags; l != NULL; l = l->next)
+    {
+      tag = l->data;
+      gd_tagged_entry_tag_unrealize (tag);
+    }
+}
+
+static void
+gd_tagged_entry_get_text_area_size (GtkEntry *entry,
+                                    gint *x,
+                                    gint *y,
+                                    gint *width,
+                                    gint *height)
+{
+  GdTaggedEntry *self = GD_TAGGED_ENTRY (entry);
+  gint tag_panel_width;
+
+  GTK_ENTRY_CLASS (gd_tagged_entry_parent_class)->get_text_area_size (entry, x, y, width, height);
+
+  tag_panel_width = gd_tagged_entry_tag_panel_get_width (self);
+
+  if (width)
+    *width -= tag_panel_width;
+}
+
+static void
+gd_tagged_entry_size_allocate (GtkWidget *widget,
+                               GtkAllocation *allocation)
+{
+  GdTaggedEntry *self = GD_TAGGED_ENTRY (widget);
+  gint x, y, width, height;
+  GdTaggedEntryTag *tag;
+  GList *l;
+
+  gtk_widget_set_allocation (widget, allocation);
+  GTK_WIDGET_CLASS (gd_tagged_entry_parent_class)->size_allocate (widget, allocation);
+
+  if (gtk_widget_get_realized (widget))
+    {
+      gd_tagged_entry_tag_panel_get_position (self, &x, &y);
+
+      for (l = self->priv->tags; l != NULL; l = l->next)
+        {
+          GtkBorder margin;
+
+          tag = l->data;
+          gd_tagged_entry_tag_get_size (tag, self, &width, &height);
+          gd_tagged_entry_tag_get_margin (tag, self, &margin);
+          gdk_window_move_resize (tag->priv->window, x, y + margin.top, width, height);
+
+          x += width;
+        }
+
+      gtk_widget_queue_draw (widget);
+    }
+}
+
+static void
+gd_tagged_entry_get_preferred_width (GtkWidget *widget,
+                                     gint *minimum,
+                                     gint *natural)
+{
+  GdTaggedEntry *self = GD_TAGGED_ENTRY (widget);
+  gint tag_panel_width;
+
+  GTK_WIDGET_CLASS (gd_tagged_entry_parent_class)->get_preferred_width (widget, minimum, natural);
+
+  tag_panel_width = gd_tagged_entry_tag_panel_get_width (self);
+
+  if (minimum)
+    *minimum += tag_panel_width;
+  if (natural)
+    *natural += tag_panel_width;
+}
+
+static void
+gd_tagged_entry_finalize (GObject *obj)
+{
+  GdTaggedEntry *self = GD_TAGGED_ENTRY (obj);
+
+  if (self->priv->tags != NULL)
+    {
+      g_list_free_full (self->priv->tags, g_object_unref);
+      self->priv->tags = NULL;
+    }
+
+  G_OBJECT_CLASS (gd_tagged_entry_parent_class)->finalize (obj);
+}
+
+static GdTaggedEntryTag *
+gd_tagged_entry_find_tag_by_window (GdTaggedEntry *self,
+                                    GdkWindow *window)
+{
+  GdTaggedEntryTag *tag = NULL, *elem;
+  GList *l;
+
+  for (l = self->priv->tags; l != NULL; l = l->next)
+    {
+      elem = l->data;
+      if (elem->priv->window == window)
+        {
+          tag = elem;
+          break;
+        }
+    }
+
+  return tag;
+}
+
+static gint
+gd_tagged_entry_enter_notify (GtkWidget        *widget,
+                              GdkEventCrossing *event)
+{
+  GdTaggedEntry *self = GD_TAGGED_ENTRY (widget);
+  GdTaggedEntryTag *tag;
+
+  tag = gd_tagged_entry_find_tag_by_window (self, event->window);
+
+  if (tag != NULL)
+    {
+      self->priv->in_child = tag;
+      gtk_widget_queue_draw (widget);
+    }
+
+  return GTK_WIDGET_CLASS (gd_tagged_entry_parent_class)->enter_notify_event (widget, event);
+}
+
+static gint
+gd_tagged_entry_leave_notify (GtkWidget        *widget,
+                              GdkEventCrossing *event)
+{
+  GdTaggedEntry *self = GD_TAGGED_ENTRY (widget);
+
+  if (self->priv->in_child != NULL)
+    {
+      self->priv->in_child = NULL;
+      gtk_widget_queue_draw (widget);
+    }
+
+  return GTK_WIDGET_CLASS (gd_tagged_entry_parent_class)->leave_notify_event (widget, event);
+}
+
+static gint
+gd_tagged_entry_motion_notify (GtkWidget      *widget,
+                               GdkEventMotion *event)
+{
+  GdTaggedEntry *self = GD_TAGGED_ENTRY (widget);
+  GdTaggedEntryTag *tag;
+
+  tag = gd_tagged_entry_find_tag_by_window (self, event->window);
+
+  if (tag != NULL)
+    {
+      gdk_event_request_motions (event);
+
+      self->priv->in_child = tag;
+      self->priv->in_child_button = gd_tagged_entry_tag_event_is_button (tag, self, event->x, event->y);
+      gtk_widget_queue_draw (widget);
+
+      return FALSE;
+    }
+
+  return GTK_WIDGET_CLASS (gd_tagged_entry_parent_class)->motion_notify_event (widget, event);
+}
+
+static gboolean
+gd_tagged_entry_button_release_event (GtkWidget *widget,
+                                      GdkEventButton *event)
+{
+  GdTaggedEntry *self = GD_TAGGED_ENTRY (widget);
+  GdTaggedEntryTag *tag;
+
+  tag = gd_tagged_entry_find_tag_by_window (self, event->window);
+
+  if (tag != NULL)
+    {
+      self->priv->in_child_active = FALSE;
+
+      if (gd_tagged_entry_tag_event_is_button (tag, self, event->x, event->y))
+        {
+          self->priv->in_child_button_active = FALSE;
+          g_signal_emit (self, signals[SIGNAL_TAG_BUTTON_CLICKED], 0, tag);
+        }
+      else
+        {
+          g_signal_emit (self, signals[SIGNAL_TAG_CLICKED], 0, tag);
+        }
+
+      gtk_widget_queue_draw (widget);
+
+      return TRUE;
+    }
+
+  return GTK_WIDGET_CLASS (gd_tagged_entry_parent_class)->button_release_event (widget, event);
+}
+
+static gboolean
+gd_tagged_entry_button_press_event (GtkWidget *widget,
+                                    GdkEventButton *event)
+{
+  GdTaggedEntry *self = GD_TAGGED_ENTRY (widget);
+  GdTaggedEntryTag *tag;
+
+  tag = gd_tagged_entry_find_tag_by_window (self, event->window);
+
+  if (tag != NULL)
+    {
+      if (gd_tagged_entry_tag_event_is_button (tag, self, event->x, event->y))
+        self->priv->in_child_button_active = TRUE;
+      else
+        self->priv->in_child_active = TRUE;
+
+      gtk_widget_queue_draw (widget);
+
+      return TRUE;
+    }
+
+  return GTK_WIDGET_CLASS (gd_tagged_entry_parent_class)->button_press_event (widget, event);
+}
+
+static void
+gd_tagged_entry_init (GdTaggedEntry *self)
+{
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GD_TYPE_TAGGED_ENTRY, GdTaggedEntryPrivate);
+  self->priv->button_visible = TRUE;
+}
+
+static void
+gd_tagged_entry_get_property (GObject      *object,
+                              guint         property_id,
+                              GValue       *value,
+                              GParamSpec   *pspec)
+{
+  GdTaggedEntry *self = GD_TAGGED_ENTRY (object);
+
+  switch (property_id)
+    {
+      case PROP_TAG_BUTTON_VISIBLE:
+        g_value_set_boolean (value, gd_tagged_entry_get_tag_button_visible (self));
+        break;
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    }
+}
+
+static void
+gd_tagged_entry_set_property (GObject      *object,
+                              guint         property_id,
+                              const GValue *value,
+                              GParamSpec   *pspec)
+{
+  GdTaggedEntry *self = GD_TAGGED_ENTRY (object);
+
+  switch (property_id)
+    {
+      case PROP_TAG_BUTTON_VISIBLE:
+        gd_tagged_entry_set_tag_button_visible (self, g_value_get_boolean (value));
+        break;
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    }
+}
+
+static void
+gd_tagged_entry_class_init (GdTaggedEntryClass *klass)
+{
+  GtkWidgetClass *wclass = GTK_WIDGET_CLASS (klass);
+  GtkEntryClass *eclass = GTK_ENTRY_CLASS (klass);
+  GObjectClass *oclass = G_OBJECT_CLASS (klass);
+
+  oclass->finalize = gd_tagged_entry_finalize;
+  oclass->set_property = gd_tagged_entry_set_property;
+  oclass->get_property = gd_tagged_entry_get_property;
+
+  wclass->realize = gd_tagged_entry_realize;
+  wclass->unrealize = gd_tagged_entry_unrealize;
+  wclass->map = gd_tagged_entry_map;
+  wclass->unmap = gd_tagged_entry_unmap;
+  wclass->size_allocate = gd_tagged_entry_size_allocate;
+  wclass->get_preferred_width = gd_tagged_entry_get_preferred_width;
+  wclass->draw = gd_tagged_entry_draw;
+  wclass->enter_notify_event = gd_tagged_entry_enter_notify;
+  wclass->leave_notify_event = gd_tagged_entry_leave_notify;
+  wclass->motion_notify_event = gd_tagged_entry_motion_notify;
+  wclass->button_press_event = gd_tagged_entry_button_press_event;
+  wclass->button_release_event = gd_tagged_entry_button_release_event;
+
+  eclass->get_text_area_size = gd_tagged_entry_get_text_area_size;
+
+  signals[SIGNAL_TAG_CLICKED] =
+    g_signal_new ("tag-clicked",
+                  GD_TYPE_TAGGED_ENTRY,
+                  G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED,
+                  0, NULL, NULL, NULL,
+                  G_TYPE_NONE,
+                  1, GD_TYPE_TAGGED_ENTRY_TAG);
+  signals[SIGNAL_TAG_BUTTON_CLICKED] =
+    g_signal_new ("tag-button-clicked",
+                  GD_TYPE_TAGGED_ENTRY,
+                  G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED,
+                  0, NULL, NULL, NULL,
+                  G_TYPE_NONE,
+                  1, GD_TYPE_TAGGED_ENTRY_TAG);
+
+  properties[PROP_TAG_BUTTON_VISIBLE] =
+    g_param_spec_boolean ("tag-close-visible", "Tag close icon visibility",
+                          "Whether the close button should be shown in tags.", TRUE,
+                          G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS);
+
+  g_type_class_add_private (klass, sizeof (GdTaggedEntryPrivate));
+  g_object_class_install_properties (oclass, NUM_PROPERTIES, properties);
+}
+
+static void
+gd_tagged_entry_tag_init (GdTaggedEntryTag *self)
+{
+  GdTaggedEntryTagPrivate *priv;
+
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GD_TYPE_TAGGED_ENTRY_TAG, GdTaggedEntryTagPrivate);
+  priv = self->priv;
+
+  priv->last_button_state = GTK_STATE_FLAG_NORMAL;
+}
+
+static void
+gd_tagged_entry_tag_finalize (GObject *obj)
+{
+  GdTaggedEntryTag *tag = GD_TAGGED_ENTRY_TAG (obj);
+  GdTaggedEntryTagPrivate *priv = tag->priv;
+
+  if (priv->window != NULL)
+    gd_tagged_entry_tag_unrealize (tag);
+
+  g_clear_object (&priv->layout);
+  g_clear_pointer (&priv->close_surface, cairo_surface_destroy);
+  g_free (priv->label);
+  g_free (priv->style);
+
+  G_OBJECT_CLASS (gd_tagged_entry_tag_parent_class)->finalize (obj);
+}
+
+static void
+gd_tagged_entry_tag_get_property (GObject      *object,
+                                  guint         property_id,
+                                  GValue       *value,
+                                  GParamSpec   *pspec)
+{
+  GdTaggedEntryTag *self = GD_TAGGED_ENTRY_TAG (object);
+
+  switch (property_id)
+    {
+      case PROP_TAG_LABEL:
+        g_value_set_string (value, gd_tagged_entry_tag_get_label (self));
+        break;
+      case PROP_TAG_HAS_CLOSE_BUTTON:
+        g_value_set_boolean (value, gd_tagged_entry_tag_get_has_close_button (self));
+        break;
+      case PROP_TAG_STYLE:
+        g_value_set_string (value, gd_tagged_entry_tag_get_style (self));
+        break;
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    }
+}
+
+static void
+gd_tagged_entry_tag_set_property (GObject      *object,
+                                  guint         property_id,
+                                  const GValue *value,
+                                  GParamSpec   *pspec)
+{
+  GdTaggedEntryTag *self = GD_TAGGED_ENTRY_TAG (object);
+
+  switch (property_id)
+    {
+      case PROP_TAG_LABEL:
+        gd_tagged_entry_tag_set_label (self, g_value_get_string (value));
+        break;
+      case PROP_TAG_HAS_CLOSE_BUTTON:
+        gd_tagged_entry_tag_set_has_close_button (self, g_value_get_boolean (value));
+        break;
+      case PROP_TAG_STYLE:
+        gd_tagged_entry_tag_set_style (self, g_value_get_string (value));
+        break;
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    }
+}
+
+static void
+gd_tagged_entry_tag_class_init (GdTaggedEntryTagClass *klass)
+{
+  GObjectClass *oclass = G_OBJECT_CLASS (klass);
+
+  oclass->finalize = gd_tagged_entry_tag_finalize;
+  oclass->set_property = gd_tagged_entry_tag_set_property;
+  oclass->get_property = gd_tagged_entry_tag_get_property;
+
+  tag_properties[PROP_TAG_LABEL] =
+    g_param_spec_string ("label", "Label",
+                         "Text to show on the tag.", NULL,
+                         G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+  tag_properties[PROP_TAG_HAS_CLOSE_BUTTON] =
+    g_param_spec_boolean ("has-close-button", "Tag has a close button",
+                          "Whether the tag has a close button.", TRUE,
+                          G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+  tag_properties[PROP_TAG_STYLE] =
+    g_param_spec_string ("style", "Style",
+                         "Style of the tag.", "entry-tag",
+                         G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+  g_type_class_add_private (klass, sizeof (GdTaggedEntryTagPrivate));
+  g_object_class_install_properties (oclass, NUM_TAG_PROPERTIES, tag_properties);
+}
+
+GdTaggedEntry *
+gd_tagged_entry_new (void)
+{
+  return g_object_new (GD_TYPE_TAGGED_ENTRY, NULL);
+}
+
+gboolean
+gd_tagged_entry_insert_tag (GdTaggedEntry    *self,
+                            GdTaggedEntryTag *tag,
+                            gint              position)
+{
+  if (g_list_find (self->priv->tags, tag) != NULL)
+    return FALSE;
+
+  tag->priv->entry = self;
+
+  self->priv->tags = g_list_insert (self->priv->tags, g_object_ref (tag), position);
+
+  if (gtk_widget_get_realized (GTK_WIDGET (self)))
+    gd_tagged_entry_tag_realize (tag, self);
+
+  if (gtk_widget_get_mapped (GTK_WIDGET (self)))
+    gdk_window_show_unraised (tag->priv->window);
+
+  gtk_widget_queue_resize (GTK_WIDGET (self));
+
+  return TRUE;
+}
+
+gboolean
+gd_tagged_entry_add_tag (GdTaggedEntry    *self,
+                         GdTaggedEntryTag *tag)
+{
+  return gd_tagged_entry_insert_tag (self, tag, -1);
+}
+
+gboolean
+gd_tagged_entry_remove_tag (GdTaggedEntry    *self,
+                            GdTaggedEntryTag *tag)
+{
+  if (!g_list_find (self->priv->tags, tag))
+    return FALSE;
+
+  gd_tagged_entry_tag_unrealize (tag);
+
+  self->priv->tags = g_list_remove (self->priv->tags, tag);
+  g_object_unref (tag);
+
+  gtk_widget_queue_resize (GTK_WIDGET (self));
+
+  return TRUE;
+}
+
+GdTaggedEntryTag *
+gd_tagged_entry_tag_new (const gchar *label)
+{
+  return g_object_new (GD_TYPE_TAGGED_ENTRY_TAG, "label", label, NULL);
+}
+
+void
+gd_tagged_entry_tag_set_label (GdTaggedEntryTag *tag,
+                               const gchar *label)
+{
+  GdTaggedEntryTagPrivate *priv;
+
+  g_return_if_fail (GD_IS_TAGGED_ENTRY_TAG (tag));
+
+  priv = tag->priv;
+
+  if (g_strcmp0 (priv->label, label) != 0)
+    {
+      GtkWidget *entry;
+
+      g_free (priv->label);
+      priv->label = g_strdup (label);
+      g_clear_object (&priv->layout);
+
+      entry = GTK_WIDGET (tag->priv->entry);
+      if (entry)
+        gtk_widget_queue_resize (entry);
+    }
+}
+
+const gchar *
+gd_tagged_entry_tag_get_label (GdTaggedEntryTag *tag)
+{
+  g_return_val_if_fail (GD_IS_TAGGED_ENTRY_TAG (tag), NULL);
+
+  return tag->priv->label;
+}
+
+void
+gd_tagged_entry_tag_set_has_close_button (GdTaggedEntryTag *tag,
+                                          gboolean has_close_button)
+{
+  GdTaggedEntryTagPrivate *priv;
+
+  g_return_if_fail (GD_IS_TAGGED_ENTRY_TAG (tag));
+
+  priv = tag->priv;
+
+  has_close_button = has_close_button != FALSE;
+  if (priv->has_close_button != has_close_button)
+    {
+      GtkWidget *entry;
+
+      priv->has_close_button = has_close_button;
+      g_clear_object (&priv->layout);
+
+      entry = GTK_WIDGET (priv->entry);
+      if (entry)
+        gtk_widget_queue_resize (entry);
+    }
+}
+
+gboolean
+gd_tagged_entry_tag_get_has_close_button (GdTaggedEntryTag *tag)
+{
+  g_return_val_if_fail (GD_IS_TAGGED_ENTRY_TAG (tag), FALSE);
+
+  return tag->priv->has_close_button;
+}
+
+void
+gd_tagged_entry_tag_set_style (GdTaggedEntryTag *tag,
+                               const gchar *style)
+{
+  GdTaggedEntryTagPrivate *priv;
+
+  g_return_if_fail (GD_IS_TAGGED_ENTRY_TAG (tag));
+
+  priv = tag->priv;
+
+  if (g_strcmp0 (priv->style, style) != 0)
+    {
+      GtkWidget *entry;
+
+      g_free (priv->style);
+      priv->style = g_strdup (style);
+      g_clear_object (&priv->layout);
+
+      entry = GTK_WIDGET (tag->priv->entry);
+      if (entry)
+        gtk_widget_queue_resize (entry);
+    }
+}
+
+const gchar *
+gd_tagged_entry_tag_get_style (GdTaggedEntryTag *tag)
+{
+  g_return_val_if_fail (GD_IS_TAGGED_ENTRY_TAG (tag), NULL);
+
+  return tag->priv->style;
+}
+
+void
+gd_tagged_entry_set_tag_button_visible (GdTaggedEntry *self,
+                                        gboolean       visible)
+{
+  g_return_if_fail (GD_IS_TAGGED_ENTRY (self));
+
+  if (self->priv->button_visible == visible)
+    return;
+
+  self->priv->button_visible = visible;
+  gtk_widget_queue_resize (GTK_WIDGET (self));
+
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TAG_BUTTON_VISIBLE]);
+}
+
+gboolean
+gd_tagged_entry_get_tag_button_visible (GdTaggedEntry *self)
+{
+  g_return_val_if_fail (GD_IS_TAGGED_ENTRY (self), FALSE);
+
+  return self->priv->button_visible;
+}
diff --git a/subprojects/libgd/libgd/gd-tagged-entry.h b/subprojects/libgd/libgd/gd-tagged-entry.h
new file mode 100644
index 0000000..ba9f673
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-tagged-entry.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2011 Red Hat, Inc.
+ * Copyright (c) 2013 Ignacio Casal Quinteiro
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by 
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Lesser General Public 
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License 
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Cosimo Cecchi <cosimoc redhat com>
+ *
+ */
+
+#ifndef __GD_TAGGED_ENTRY_H__
+#define __GD_TAGGED_ENTRY_H__
+
+#include <glib-object.h>
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GD_TYPE_TAGGED_ENTRY gd_tagged_entry_get_type()
+#define GD_TAGGED_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GD_TYPE_TAGGED_ENTRY, GdTaggedEntry))
+#define GD_TAGGED_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GD_TYPE_TAGGED_ENTRY, 
GdTaggedEntryClass))
+#define GD_IS_TAGGED_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GD_TYPE_TAGGED_ENTRY))
+#define GD_IS_TAGGED_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GD_TYPE_TAGGED_ENTRY))
+#define GD_TAGGED_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GD_TYPE_TAGGED_ENTRY, 
GdTaggedEntryClass))
+
+typedef struct _GdTaggedEntry GdTaggedEntry;
+typedef struct _GdTaggedEntryClass GdTaggedEntryClass;
+typedef struct _GdTaggedEntryPrivate GdTaggedEntryPrivate;
+
+typedef struct _GdTaggedEntryTag GdTaggedEntryTag;
+typedef struct _GdTaggedEntryTagClass GdTaggedEntryTagClass;
+typedef struct _GdTaggedEntryTagPrivate GdTaggedEntryTagPrivate;
+
+struct _GdTaggedEntry
+{
+  GtkSearchEntry parent;
+
+  GdTaggedEntryPrivate *priv;
+};
+
+struct _GdTaggedEntryClass
+{
+  GtkSearchEntryClass parent_class;
+};
+
+#define GD_TYPE_TAGGED_ENTRY_TAG gd_tagged_entry_tag_get_type()
+#define GD_TAGGED_ENTRY_TAG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GD_TYPE_TAGGED_ENTRY_TAG, 
GdTaggedEntryTag))
+#define GD_TAGGED_ENTRY_TAG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GD_TYPE_TAGGED_ENTRY_TAG, 
GdTaggedEntryTagClass))
+#define GD_IS_TAGGED_ENTRY_TAG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GD_TYPE_TAGGED_ENTRY_TAG))
+#define GD_IS_TAGGED_ENTRY_TAG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GD_TYPE_TAGGED_ENTRY_TAG))
+#define GD_TAGGED_ENTRY_TAG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GD_TYPE_TAGGED_ENTRY_TAG, 
GdTaggedEntryTagClass))
+
+struct _GdTaggedEntryTag
+{
+  GObject parent;
+
+  GdTaggedEntryTagPrivate *priv;
+};
+
+struct _GdTaggedEntryTagClass
+{
+  GObjectClass parent_class;
+};
+
+GType gd_tagged_entry_get_type (void) G_GNUC_CONST;
+
+GdTaggedEntry *gd_tagged_entry_new (void);
+
+void     gd_tagged_entry_set_tag_button_visible (GdTaggedEntry *self,
+                                                 gboolean       visible);
+gboolean gd_tagged_entry_get_tag_button_visible (GdTaggedEntry *self);
+
+gboolean gd_tagged_entry_insert_tag (GdTaggedEntry    *self,
+                                     GdTaggedEntryTag *tag,
+                                     gint              position);
+
+gboolean gd_tagged_entry_add_tag (GdTaggedEntry    *self,
+                                  GdTaggedEntryTag *tag);
+
+gboolean gd_tagged_entry_remove_tag (GdTaggedEntry *self,
+                                     GdTaggedEntryTag *tag);
+
+GType gd_tagged_entry_tag_get_type (void) G_GNUC_CONST;
+
+GdTaggedEntryTag *gd_tagged_entry_tag_new (const gchar *label);
+
+void gd_tagged_entry_tag_set_label (GdTaggedEntryTag *tag,
+                                    const gchar *label);
+const gchar *gd_tagged_entry_tag_get_label (GdTaggedEntryTag *tag);
+
+void gd_tagged_entry_tag_set_has_close_button (GdTaggedEntryTag *tag,
+                                               gboolean has_close_button);
+gboolean gd_tagged_entry_tag_get_has_close_button (GdTaggedEntryTag *tag);
+
+void gd_tagged_entry_tag_set_style (GdTaggedEntryTag *tag,
+                                    const gchar *style);
+const gchar *gd_tagged_entry_tag_get_style (GdTaggedEntryTag *tag);
+
+gboolean gd_tagged_entry_tag_get_area (GdTaggedEntryTag      *tag,
+                                       cairo_rectangle_int_t *rect);
+
+G_END_DECLS
+
+#endif /* __GD_TAGGED_ENTRY_H__ */
diff --git a/subprojects/libgd/libgd/gd-toggle-pixbuf-renderer.c 
b/subprojects/libgd/libgd/gd-toggle-pixbuf-renderer.c
new file mode 100644
index 0000000..fa23cb6
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-toggle-pixbuf-renderer.c
@@ -0,0 +1,271 @@
+/*
+ * Copyright (c) 2011 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by 
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Lesser General Public 
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License 
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Cosimo Cecchi <cosimoc redhat com>
+ *
+ */
+
+#include "gd-toggle-pixbuf-renderer.h"
+
+G_DEFINE_TYPE (GdTogglePixbufRenderer, gd_toggle_pixbuf_renderer, GTK_TYPE_CELL_RENDERER_PIXBUF);
+
+enum {
+  PROP_ACTIVE = 1,
+  PROP_TOGGLE_VISIBLE,
+  PROP_PULSE,
+  NUM_PROPERTIES
+};
+
+static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
+
+struct _GdTogglePixbufRendererPrivate {
+  gboolean active;
+  gboolean toggle_visible;
+
+  guint pulse;
+};
+
+static void
+render_check (GdTogglePixbufRenderer *self,
+              cairo_t                *cr,
+              GtkWidget              *widget,
+              const GdkRectangle     *cell_area,
+              gint                    icon_size,
+              gint                    xpad,
+              gint                    ypad)
+{
+  GtkStyleContext *context;
+  gint check_x, check_y, x_offset;
+  GtkTextDirection direction;
+
+  context = gtk_widget_get_style_context (widget);
+
+  if (!self->priv->toggle_visible)
+    return;
+
+  direction = gtk_widget_get_direction (widget);
+  if (direction == GTK_TEXT_DIR_RTL)
+    x_offset = xpad;
+  else
+    x_offset = cell_area->width - icon_size - xpad;
+
+  check_x = cell_area->x + x_offset;
+  check_y = cell_area->y + cell_area->height - icon_size - ypad;
+
+  gtk_style_context_save (context);
+  gtk_style_context_add_class (context, GTK_STYLE_CLASS_CHECK);
+
+  if (self->priv->active)
+    gtk_style_context_set_state (context, gtk_widget_get_state_flags (widget) | GTK_STATE_FLAG_CHECKED);
+
+  gtk_render_background (context, cr,
+                         check_x, check_y,
+                         icon_size, icon_size);
+  gtk_render_frame (context, cr,
+                    check_x, check_y,
+                    icon_size, icon_size);
+  gtk_render_check (context, cr,
+                    check_x, check_y,
+                    icon_size, icon_size);
+  gtk_style_context_restore (context);
+}
+
+static void
+render_activity (GdTogglePixbufRenderer *self,
+                 cairo_t                *cr,
+                 GtkWidget              *widget,
+                 const GdkRectangle     *cell_area,
+                 gint                    icon_size,
+                 gint                    xpad,
+                 gint                    ypad)
+{
+  gint x, y, width, height;
+
+  if (self->priv->pulse == 0)
+    return;
+
+  width = cell_area->width / 4;
+  height = cell_area->height / 4;
+
+  x = cell_area->x + (cell_area->width / 2) - (width / 2) - xpad;
+  y = cell_area->y + (cell_area->height / 2) - (height / 2) - ypad;
+
+  G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+  gtk_paint_spinner (gtk_widget_get_style (widget),
+                     cr,
+                     GTK_STATE_FLAG_ACTIVE,
+                     widget,
+                     NULL,
+                     (guint) self->priv->pulse - 1,
+                     x, y,
+                     width, height);
+  G_GNUC_END_IGNORE_DEPRECATIONS;
+}
+
+static void
+gd_toggle_pixbuf_renderer_render (GtkCellRenderer      *cell,
+                                  cairo_t              *cr,
+                                  GtkWidget            *widget,
+                                  const GdkRectangle   *background_area,
+                                  const GdkRectangle   *cell_area,
+                                  GtkCellRendererState  flags)
+{
+  gint icon_size = -1;
+  GdTogglePixbufRenderer *self = GD_TOGGLE_PIXBUF_RENDERER (cell);
+  gint xpad, ypad;
+
+  GTK_CELL_RENDERER_CLASS (gd_toggle_pixbuf_renderer_parent_class)->render
+    (cell, cr, widget,
+     background_area, cell_area, flags);
+
+  gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
+  gtk_widget_style_get (widget,
+                        "check-icon-size", &icon_size,
+                        NULL);
+
+  if (icon_size == -1)
+    icon_size = 40;
+
+  render_activity (self, cr, widget, cell_area, icon_size, xpad, ypad);
+  render_check (self, cr, widget, cell_area, icon_size, xpad, ypad);
+}
+
+static void
+gd_toggle_pixbuf_renderer_get_size (GtkCellRenderer *cell,
+                                    GtkWidget       *widget,
+                                    const GdkRectangle *cell_area,
+                                    gint *x_offset,
+                                    gint *y_offset,
+                                    gint *width,
+                                    gint *height)
+{
+  gint icon_size;
+
+  gtk_widget_style_get (widget,
+                        "check-icon-size", &icon_size,
+                        NULL);
+
+  GTK_CELL_RENDERER_CLASS (gd_toggle_pixbuf_renderer_parent_class)->get_size
+    (cell, widget, cell_area,
+     x_offset, y_offset, width, height);
+
+  *width += icon_size / 4;
+}
+
+static void
+gd_toggle_pixbuf_renderer_get_property (GObject    *object,
+                                        guint       property_id,
+                                        GValue     *value,
+                                        GParamSpec *pspec)
+{
+  GdTogglePixbufRenderer *self = GD_TOGGLE_PIXBUF_RENDERER (object);
+
+  switch (property_id)
+    {
+    case PROP_ACTIVE:
+      g_value_set_boolean (value, self->priv->active);
+      break;
+    case PROP_TOGGLE_VISIBLE:
+      g_value_set_boolean (value, self->priv->toggle_visible);
+      break;
+    case PROP_PULSE:
+      g_value_set_uint (value, self->priv->pulse);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gd_toggle_pixbuf_renderer_set_property (GObject    *object,
+                                        guint       property_id,
+                                        const GValue *value,
+                                        GParamSpec *pspec)
+{
+  GdTogglePixbufRenderer *self = GD_TOGGLE_PIXBUF_RENDERER (object);
+
+  switch (property_id)
+    {
+    case PROP_ACTIVE:
+      self->priv->active = g_value_get_boolean (value);
+      break;
+    case PROP_TOGGLE_VISIBLE:
+      self->priv->toggle_visible = g_value_get_boolean (value);
+      break;
+    case PROP_PULSE:
+      self->priv->pulse = g_value_get_uint (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gd_toggle_pixbuf_renderer_class_init (GdTogglePixbufRendererClass *klass)
+{
+  GObjectClass *oclass = G_OBJECT_CLASS (klass);
+  GtkCellRendererClass *crclass = GTK_CELL_RENDERER_CLASS (klass);
+
+  crclass->render = gd_toggle_pixbuf_renderer_render;
+  crclass->get_size = gd_toggle_pixbuf_renderer_get_size;
+  oclass->get_property = gd_toggle_pixbuf_renderer_get_property;
+  oclass->set_property = gd_toggle_pixbuf_renderer_set_property;
+
+  properties[PROP_ACTIVE] = 
+    g_param_spec_boolean ("active",
+                          "Active",
+                          "Whether the cell renderer is active",
+                          FALSE,
+                          G_PARAM_READWRITE |
+                          G_PARAM_STATIC_STRINGS);
+  properties[PROP_TOGGLE_VISIBLE] =
+    g_param_spec_boolean ("toggle-visible",
+                          "Toggle visible",
+                          "Whether to draw the toggle indicator",
+                          FALSE,
+                          G_PARAM_READWRITE |
+                          G_PARAM_STATIC_STRINGS);
+  properties[PROP_PULSE] =
+    g_param_spec_uint ("pulse",
+                      "Pulse",
+                      "Set to any value other than 0 to display a "
+                      "spinner on top of the pixbuf.",
+                      0,
+                      G_MAXUINT,
+                      0,
+                      G_PARAM_READWRITE |
+                      G_PARAM_STATIC_STRINGS);
+
+  g_type_class_add_private (klass, sizeof (GdTogglePixbufRendererPrivate));
+  g_object_class_install_properties (oclass, NUM_PROPERTIES, properties);
+}
+
+static void
+gd_toggle_pixbuf_renderer_init (GdTogglePixbufRenderer *self)
+{
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GD_TYPE_TOGGLE_PIXBUF_RENDERER,
+                                            GdTogglePixbufRendererPrivate);
+  self->priv->pulse = 0;
+}
+
+GtkCellRenderer *
+gd_toggle_pixbuf_renderer_new (void)
+{
+  return g_object_new (GD_TYPE_TOGGLE_PIXBUF_RENDERER, NULL);
+}
diff --git a/subprojects/libgd/libgd/gd-toggle-pixbuf-renderer.h 
b/subprojects/libgd/libgd/gd-toggle-pixbuf-renderer.h
new file mode 100644
index 0000000..fe54cf4
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-toggle-pixbuf-renderer.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2011 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by 
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Lesser General Public 
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License 
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Cosimo Cecchi <cosimoc redhat com>
+ *
+ */
+
+#ifndef _GD_TOGGLE_PIXBUF_RENDERER_H
+#define _GD_TOGGLE_PIXBUF_RENDERER_H
+
+#include <glib-object.h>
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GD_TYPE_TOGGLE_PIXBUF_RENDERER gd_toggle_pixbuf_renderer_get_type()
+
+#define GD_TOGGLE_PIXBUF_RENDERER(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+   GD_TYPE_TOGGLE_PIXBUF_RENDERER, GdTogglePixbufRenderer))
+
+#define GD_TOGGLE_PIXBUF_RENDERER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), \
+   GD_TYPE_TOGGLE_PIXBUF_RENDERER, GdTogglePixbufRendererClass))
+
+#define GD_IS_TOGGLE_PIXBUF_RENDERER(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+   GD_TYPE_TOGGLE_PIXBUF_RENDERER))
+
+#define GD_IS_TOGGLE_PIXBUF_RENDERER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+   GD_TYPE_TOGGLE_PIXBUF_RENDERER))
+
+#define GD_TOGGLE_PIXBUF_RENDERER_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+   GD_TYPE_TOGGLE_PIXBUF_RENDERER, GdTogglePixbufRendererClass))
+
+typedef struct _GdTogglePixbufRenderer GdTogglePixbufRenderer;
+typedef struct _GdTogglePixbufRendererClass GdTogglePixbufRendererClass;
+typedef struct _GdTogglePixbufRendererPrivate GdTogglePixbufRendererPrivate;
+
+struct _GdTogglePixbufRenderer
+{
+  GtkCellRendererPixbuf parent;
+
+  GdTogglePixbufRendererPrivate *priv;
+};
+
+struct _GdTogglePixbufRendererClass
+{
+  GtkCellRendererPixbufClass parent_class;
+};
+
+GType gd_toggle_pixbuf_renderer_get_type (void) G_GNUC_CONST;
+
+GtkCellRenderer *gd_toggle_pixbuf_renderer_new (void);
+
+G_END_DECLS
+
+#endif /* _GD_TOGGLE_PIXBUF_RENDERER_H */
diff --git a/subprojects/libgd/libgd/gd-two-lines-renderer.c b/subprojects/libgd/libgd/gd-two-lines-renderer.c
new file mode 100644
index 0000000..5a029f7
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-two-lines-renderer.c
@@ -0,0 +1,621 @@
+/*
+ * Copyright (c) 2011 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by 
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Lesser General Public 
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License 
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Cosimo Cecchi <cosimoc redhat com>
+ *
+ */
+
+#include "gd-two-lines-renderer.h"
+#include <string.h>
+
+#define SUBTITLE_DIM_PERCENTAGE 0.55
+#define SUBTITLE_SIZE_PERCENTAGE 0.82
+
+G_DEFINE_TYPE (GdTwoLinesRenderer, gd_two_lines_renderer, GTK_TYPE_CELL_RENDERER_TEXT)
+
+struct _GdTwoLinesRendererPrivate {
+  gchar *line_two;
+  gint text_lines;
+};
+
+enum {
+  PROP_TEXT_LINES = 1,
+  PROP_LINE_TWO,
+  NUM_PROPERTIES
+};
+
+static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
+
+static PangoLayout *
+create_layout_with_attrs (GtkWidget *widget,
+                          const GdkRectangle *cell_area,
+                          GdTwoLinesRenderer *self,
+                          PangoEllipsizeMode ellipsize)
+{
+  PangoLayout *layout;
+  gint wrap_width, xpad;
+  PangoWrapMode wrap_mode;
+  PangoAlignment alignment;
+
+  g_object_get (self,
+                "wrap-width", &wrap_width,
+                "wrap-mode", &wrap_mode,
+                "alignment", &alignment,
+                "xpad", &xpad,
+                NULL);
+
+  layout = pango_layout_new (gtk_widget_get_pango_context (widget));
+
+  pango_layout_set_ellipsize (layout, ellipsize);
+  pango_layout_set_alignment (layout, alignment);
+
+  if (wrap_width != -1)
+    {
+      pango_layout_set_width (layout, wrap_width * PANGO_SCALE);
+      pango_layout_set_wrap (layout, wrap_mode);
+    }
+  else
+    {
+      if (cell_area != NULL)
+        pango_layout_set_width (layout, (cell_area->width - 2 * xpad) * PANGO_SCALE);
+      else
+        pango_layout_set_width (layout, -1);
+
+      pango_layout_set_wrap (layout, PANGO_WRAP_CHAR);
+    }
+
+  return layout;
+}
+
+static void
+apply_subtitle_style_to_layout (GtkStyleContext *context,
+                                PangoLayout     *layout,
+                                GtkStateFlags    flags)
+{
+  PangoFontDescription *desc;
+  PangoAttrList *layout_attr;
+  PangoAttribute *attr_alpha;
+
+  gtk_style_context_save (context);
+  gtk_style_context_set_state (context, flags);
+  gtk_style_context_get (context, gtk_style_context_get_state (context),
+                         "font", &desc,
+                         NULL);
+  gtk_style_context_restore (context);
+
+  /* Set the font size */
+  pango_font_description_set_size (desc, pango_font_description_get_size (desc) * SUBTITLE_SIZE_PERCENTAGE);
+  pango_layout_set_font_description (layout, desc);
+  pango_font_description_free (desc);
+
+  /* Set the font alpha */
+  layout_attr = pango_attr_list_new ();
+  attr_alpha = pango_attr_foreground_alpha_new (SUBTITLE_DIM_PERCENTAGE * 65535);
+  pango_attr_list_insert (layout_attr, attr_alpha);
+
+  pango_layout_set_attributes (layout, layout_attr);
+  pango_attr_list_unref (layout_attr);
+}
+
+static void
+gd_two_lines_renderer_prepare_layouts (GdTwoLinesRenderer *self,
+                                       const GdkRectangle *cell_area,
+                                       GtkWidget *widget,
+                                       PangoLayout **layout_one,
+                                       PangoLayout **layout_two)
+{
+  PangoLayout *line_one;
+  PangoLayout *line_two = NULL;
+  gchar *text = NULL;
+
+  g_object_get (self,
+                "text", &text,
+                NULL);
+
+  line_one = create_layout_with_attrs (widget, cell_area,
+                                       self, PANGO_ELLIPSIZE_MIDDLE);
+
+  if (self->priv->line_two == NULL ||
+      g_strcmp0 (self->priv->line_two, "") == 0)
+    {
+      pango_layout_set_height (line_one, - (self->priv->text_lines));
+
+      if (text != NULL)
+        pango_layout_set_text (line_one, text, -1);
+    }
+  else
+    {
+      GtkStyleContext *context;
+
+      line_two = create_layout_with_attrs (widget, cell_area,
+                                           self, PANGO_ELLIPSIZE_END);
+
+      context = gtk_widget_get_style_context (widget);
+      gtk_style_context_save (context);
+      apply_subtitle_style_to_layout (context, line_two, GTK_STATE_FLAG_NORMAL);
+      gtk_style_context_restore (context);
+
+      pango_layout_set_height (line_one, - (self->priv->text_lines - 1));
+      pango_layout_set_height (line_two, -1);
+      pango_layout_set_text (line_two, self->priv->line_two, -1);
+
+      if (text != NULL)
+        pango_layout_set_text (line_one, text, -1);
+    }
+
+  if (layout_one)
+    *layout_one = line_one;
+  if (layout_two)
+    *layout_two = line_two;
+
+  g_free (text);
+}
+
+static void
+gd_two_lines_renderer_get_size (GtkCellRenderer *cell,
+                                GtkWidget *widget,
+                                PangoLayout *layout_1,
+                                PangoLayout *layout_2,
+                                gint *width,
+                                gint *height,
+                                const GdkRectangle *cell_area,
+                                gint *x_offset_1,
+                                gint *x_offset_2,
+                                gint *y_offset)
+{
+  GdTwoLinesRenderer *self = GD_TWO_LINES_RENDERER (cell);
+  gint xpad, ypad;
+  PangoLayout *layout_one, *layout_two;
+  GdkRectangle layout_one_rect, layout_two_rect, layout_union;
+
+  if (layout_1 == NULL)
+    {
+      gd_two_lines_renderer_prepare_layouts (self, cell_area, widget, &layout_one, &layout_two);
+    }
+  else
+    {
+      layout_one = g_object_ref (layout_1);
+
+      if (layout_2 != NULL)
+        layout_two = g_object_ref (layout_2);
+      else
+        layout_two = NULL;
+    }
+
+  gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
+  pango_layout_get_pixel_extents (layout_one, NULL, (PangoRectangle *) &layout_one_rect);
+
+  if (layout_two != NULL)
+    {
+      pango_layout_get_pixel_extents (layout_two, NULL, (PangoRectangle *) &layout_two_rect);
+
+      layout_union.width = MAX (layout_one_rect.width, layout_two_rect.width);
+      layout_union.height = layout_one_rect.height + layout_two_rect.height;
+    }
+  else
+    {
+      layout_union = layout_one_rect;
+    }
+
+  if (cell_area)
+    {
+      gfloat xalign, yalign;
+
+      gtk_cell_renderer_get_alignment (cell, &xalign, &yalign);
+
+      layout_union.width  = MIN (layout_union.width, cell_area->width - 2 * xpad);
+      layout_union.height = MIN (layout_union.height, cell_area->height - 2 * ypad);
+
+      if (x_offset_1)
+       {
+         if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
+           *x_offset_1 = (1.0 - xalign) * (cell_area->width - (layout_one_rect.width + (2 * xpad)));
+         else 
+           *x_offset_1 = xalign * (cell_area->width - (layout_one_rect.width + (2 * xpad)));
+
+          *x_offset_1 = MAX (*x_offset_1, 0);
+       }
+      if (x_offset_2)
+        {
+          if (layout_two != NULL)
+            {
+              if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
+                *x_offset_2 = (1.0 - xalign) * (cell_area->width - (layout_two_rect.width + (2 * xpad)));
+              else 
+                *x_offset_2 = xalign * (cell_area->width - (layout_two_rect.width + (2 * xpad)));
+
+              *x_offset_2 = MAX (*x_offset_2, 0);
+            }
+          else
+            {
+              *x_offset_2 = 0;
+            }
+        }
+
+      if (y_offset)
+       {
+         *y_offset = yalign * (cell_area->height - (layout_union.height + (2 * ypad)));
+         *y_offset = MAX (*y_offset, 0);
+       }
+    }
+  else
+    {
+      if (x_offset_1) *x_offset_1 = 0;
+      if (x_offset_2) *x_offset_2 = 0;
+      if (y_offset) *y_offset = 0;
+    }
+
+  g_clear_object (&layout_one);
+  g_clear_object (&layout_two);
+
+  if (height)
+    *height = ypad * 2 + layout_union.height;
+
+  if (width)
+    *width = xpad * 2 + layout_union.width;
+}
+
+static void
+gd_two_lines_renderer_render (GtkCellRenderer      *cell,
+                              cairo_t              *cr,
+                              GtkWidget            *widget,
+                              const GdkRectangle   *background_area,
+                              const GdkRectangle   *cell_area,
+                              GtkCellRendererState  flags)
+{
+  GdTwoLinesRenderer *self = GD_TWO_LINES_RENDERER (cell);
+  GtkStyleContext *context;
+  gint line_one_height;
+  GtkStateFlags state;
+  GdkRectangle area, render_area = *cell_area;
+  gint xpad, ypad, x_offset_1, x_offset_2, y_offset;
+  PangoLayout *layout_one, *layout_two;
+  PangoRectangle layout_rect;
+
+  /* fetch common information */
+  context = gtk_widget_get_style_context (widget);
+  gd_two_lines_renderer_prepare_layouts (self, cell_area, widget, &layout_one, &layout_two);
+  gd_two_lines_renderer_get_size (cell, widget,
+                                  layout_one, layout_two,
+                                  NULL, NULL,
+                                  cell_area,
+                                  &x_offset_1, &x_offset_2, &y_offset);
+  gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
+
+  area = *cell_area;
+  area.x += xpad;
+  area.y += ypad;
+
+  /* now render the first layout */
+  pango_layout_get_pixel_extents (layout_one, NULL, &layout_rect);
+
+  render_area = area;
+  render_area.x += x_offset_1 - layout_rect.x;
+
+  gtk_render_layout (context, cr,
+                     render_area.x,
+                     render_area.y,
+                     layout_one);
+
+  /* render the second layout */
+  if (layout_two != NULL)
+    {
+      pango_layout_get_pixel_size (layout_one,
+                                   NULL, &line_one_height);
+
+      gtk_style_context_save (context);
+
+      apply_subtitle_style_to_layout (context, layout_two, flags);
+
+      state = gtk_cell_renderer_get_state (cell, widget, flags);
+      gtk_style_context_set_state (context, state);
+
+      pango_layout_get_pixel_extents (layout_two, NULL, &layout_rect);
+
+      render_area = area;
+      render_area.x += x_offset_2 - layout_rect.x;
+      render_area.y += line_one_height;
+
+      gtk_render_layout (context, cr,
+                         render_area.x,
+                         render_area.y,
+                         layout_two);
+
+      gtk_style_context_restore (context);
+    }
+
+  g_clear_object (&layout_one);
+  g_clear_object (&layout_two);
+}
+
+static void
+gd_two_lines_renderer_get_preferred_width (GtkCellRenderer *cell,
+                                           GtkWidget       *widget,
+                                           gint            *minimum_size,
+                                           gint            *natural_size)
+{
+  PangoContext *context;
+  PangoFontMetrics *metrics;
+  PangoFontDescription *font_desc;
+  GtkStyleContext *style_context;
+  gint nat_width, min_width;
+  gint xpad, char_width, wrap_width, text_width;
+  gint width_chars, ellipsize_chars;
+
+  g_object_get (cell,
+                "xpad", &xpad,
+                "width-chars", &width_chars,
+                "wrap-width", &wrap_width,
+                NULL);
+  style_context = gtk_widget_get_style_context (widget);
+  gtk_cell_renderer_get_padding (cell, &xpad, NULL);
+
+  gd_two_lines_renderer_get_size (cell, widget,
+                                  NULL, NULL,
+                                  &text_width, NULL,
+                                  NULL, 
+                                  NULL, NULL, NULL);
+
+  /* Fetch the average size of a character */
+  context = gtk_widget_get_pango_context (widget);
+  gtk_style_context_save (style_context);
+  gtk_style_context_set_state (style_context, 0);
+  gtk_style_context_get (style_context, gtk_style_context_get_state (style_context),
+                         "font", &font_desc, NULL);
+  gtk_style_context_restore (style_context);
+  metrics = pango_context_get_metrics (context, font_desc,
+                                       pango_context_get_language (context));
+
+  char_width = pango_font_metrics_get_approximate_char_width (metrics);
+
+  pango_font_metrics_unref (metrics);
+  pango_font_description_free (font_desc);
+
+  /* enforce minimum width for ellipsized labels at ~3 chars */
+  ellipsize_chars = 3;
+
+  /* If no width-chars set, minimum for wrapping text will be the wrap-width */
+  if (wrap_width > -1)
+    min_width = xpad * 2 + MIN (text_width, wrap_width);
+  else
+    min_width = xpad * 2 +
+      MIN (text_width,
+           (PANGO_PIXELS (char_width) * MAX (width_chars, ellipsize_chars)));
+
+  if (width_chars > 0)
+    nat_width = xpad * 2 +
+      MAX ((PANGO_PIXELS (char_width) * width_chars), text_width);
+  else
+    nat_width = xpad * 2 + text_width;
+
+  nat_width = MAX (nat_width, min_width);
+
+  if (minimum_size)
+    *minimum_size = min_width;
+
+  if (natural_size)
+    *natural_size = nat_width;
+}
+
+static void
+gd_two_lines_renderer_get_preferred_height_for_width (GtkCellRenderer *cell,
+                                                      GtkWidget       *widget,
+                                                      gint             width,
+                                                      gint            *minimum_size,
+                                                      gint            *natural_size)
+{
+  GdTwoLinesRenderer *self = GD_TWO_LINES_RENDERER (cell);
+  PangoLayout *layout_one, *layout_two;
+  gint text_height, wrap_width;
+  gint xpad, ypad;
+
+  gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
+  g_object_get (cell, "wrap-width", &wrap_width, NULL);
+  gd_two_lines_renderer_prepare_layouts (self, NULL, widget, &layout_one, &layout_two);
+
+  if (wrap_width != -1)
+    wrap_width = MIN (width - 2 * xpad, wrap_width);
+  else
+    wrap_width = width - 2 * xpad;
+
+  pango_layout_set_width (layout_one, wrap_width);
+  if (layout_two != NULL)
+    pango_layout_set_width (layout_two, wrap_width);
+
+  gd_two_lines_renderer_get_size (cell, widget,
+                                  layout_one, layout_two,
+                                  NULL, &text_height,
+                                  NULL, 
+                                  NULL, NULL, NULL);
+
+  text_height += 2 * ypad;
+
+  if (minimum_size != NULL)
+    *minimum_size = text_height;
+
+  if (natural_size != NULL)
+    *natural_size = text_height;
+
+  g_clear_object (&layout_one);
+  g_clear_object (&layout_two);
+}
+
+static void
+gd_two_lines_renderer_get_preferred_height (GtkCellRenderer *cell,
+                                            GtkWidget       *widget,
+                                            gint            *minimum_size,
+                                            gint            *natural_size)
+{
+  gint min_width;
+
+  gtk_cell_renderer_get_preferred_width (cell, widget, &min_width, NULL);
+  gd_two_lines_renderer_get_preferred_height_for_width (cell, widget, min_width,
+                                                        minimum_size, natural_size);
+}
+
+static void
+gd_two_lines_renderer_get_aligned_area (GtkCellRenderer      *cell,
+                                        GtkWidget            *widget,
+                                        GtkCellRendererState  flags,
+                                        const GdkRectangle   *cell_area,
+                                        GdkRectangle         *aligned_area)
+{
+  GdTwoLinesRenderer *self = GD_TWO_LINES_RENDERER (cell);
+  gint x_offset, x_offset_1, x_offset_2, y_offset;
+  PangoLayout *layout_one, *layout_two;
+
+  /* fetch common information */
+  gd_two_lines_renderer_prepare_layouts (self, cell_area, widget, &layout_one, &layout_two);
+  gd_two_lines_renderer_get_size (cell, widget,
+                                  layout_one, layout_two,
+                                  &aligned_area->width, &aligned_area->height,
+                                  cell_area,
+                                  &x_offset_1, &x_offset_2, &y_offset);
+
+  x_offset = MIN (x_offset_1, x_offset_2);
+
+  aligned_area->x = cell_area->x + x_offset;
+  aligned_area->y = cell_area->y;
+
+  g_clear_object (&layout_one);
+  g_clear_object (&layout_two);
+}
+
+static void
+gd_two_lines_renderer_set_line_two (GdTwoLinesRenderer *self,
+                                    const gchar *line_two)
+{
+  if (g_strcmp0 (self->priv->line_two, line_two) == 0)
+    return;
+
+  g_free (self->priv->line_two);
+  self->priv->line_two = g_strdup (line_two);
+
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LINE_TWO]);
+}
+
+static void
+gd_two_lines_renderer_set_text_lines (GdTwoLinesRenderer *self,
+                                      gint text_lines)
+{
+  if (self->priv->text_lines == text_lines)
+    return;
+
+  self->priv->text_lines = text_lines;
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TEXT_LINES]);
+}
+
+static void
+gd_two_lines_renderer_set_property (GObject    *object,
+                                    guint       property_id,
+                                    const GValue     *value,
+                                    GParamSpec *pspec)
+{
+  GdTwoLinesRenderer *self = GD_TWO_LINES_RENDERER (object);
+
+  switch (property_id)
+    {
+    case PROP_TEXT_LINES:
+      gd_two_lines_renderer_set_text_lines (self, g_value_get_int (value));
+      break;
+    case PROP_LINE_TWO:
+      gd_two_lines_renderer_set_line_two (self, g_value_get_string (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gd_two_lines_renderer_get_property (GObject    *object,
+                                    guint       property_id,
+                                    GValue     *value,
+                                    GParamSpec *pspec)
+{
+  GdTwoLinesRenderer *self = GD_TWO_LINES_RENDERER (object);
+
+  switch (property_id)
+    {
+    case PROP_TEXT_LINES:
+      g_value_set_int (value, self->priv->text_lines);
+      break;
+    case PROP_LINE_TWO:
+      g_value_set_string (value, self->priv->line_two);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gd_two_lines_renderer_finalize (GObject *object)
+{
+  GdTwoLinesRenderer *self = GD_TWO_LINES_RENDERER (object);
+
+  g_free (self->priv->line_two);
+
+  G_OBJECT_CLASS (gd_two_lines_renderer_parent_class)->finalize (object);
+}
+
+static void
+gd_two_lines_renderer_class_init (GdTwoLinesRendererClass *klass)
+{
+  GtkCellRendererClass *cclass = GTK_CELL_RENDERER_CLASS (klass);
+  GObjectClass *oclass = G_OBJECT_CLASS (klass);
+
+  cclass->render = gd_two_lines_renderer_render;
+  cclass->get_preferred_width = gd_two_lines_renderer_get_preferred_width;
+  cclass->get_preferred_height = gd_two_lines_renderer_get_preferred_height;
+  cclass->get_preferred_height_for_width = gd_two_lines_renderer_get_preferred_height_for_width;
+  cclass->get_aligned_area = gd_two_lines_renderer_get_aligned_area;
+
+  oclass->set_property = gd_two_lines_renderer_set_property;
+  oclass->get_property = gd_two_lines_renderer_get_property;
+  oclass->finalize = gd_two_lines_renderer_finalize;
+  
+  properties[PROP_TEXT_LINES] =
+    g_param_spec_int ("text-lines",
+                      "Lines of text",
+                      "The total number of lines to be displayed",
+                      2, G_MAXINT, 2,
+                      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+  properties[PROP_LINE_TWO] =
+    g_param_spec_string ("line-two",
+                         "Second line",
+                         "Second line",
+                         NULL,
+                         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+  g_type_class_add_private (klass, sizeof (GdTwoLinesRendererPrivate));
+  g_object_class_install_properties (oclass, NUM_PROPERTIES, properties);
+}
+
+static void
+gd_two_lines_renderer_init (GdTwoLinesRenderer *self)
+{
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GD_TYPE_TWO_LINES_RENDERER,
+                                            GdTwoLinesRendererPrivate);
+}
+
+GtkCellRenderer *
+gd_two_lines_renderer_new (void)
+{
+  return g_object_new (GD_TYPE_TWO_LINES_RENDERER, NULL);
+}
diff --git a/subprojects/libgd/libgd/gd-two-lines-renderer.h b/subprojects/libgd/libgd/gd-two-lines-renderer.h
new file mode 100644
index 0000000..23bf70f
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-two-lines-renderer.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2011 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by 
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Lesser General Public 
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License 
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Cosimo Cecchi <cosimoc redhat com>
+ *
+ */
+
+#ifndef _GD_TWO_LINES_RENDERER_H
+#define _GD_TWO_LINES_RENDERER_H
+
+#include <glib-object.h>
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GD_TYPE_TWO_LINES_RENDERER gd_two_lines_renderer_get_type()
+
+#define GD_TWO_LINES_RENDERER(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+   GD_TYPE_TWO_LINES_RENDERER, GdTwoLinesRenderer))
+
+#define GD_TWO_LINES_RENDERER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), \
+   GD_TYPE_TWO_LINES_RENDERER, GdTwoLinesRendererClass))
+
+#define GD_IS_TWO_LINES_RENDERER(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+   GD_TYPE_TWO_LINES_RENDERER))
+
+#define GD_IS_TWO_LINES_RENDERER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+   GD_TYPE_TWO_LINES_RENDERER))
+
+#define GD_TWO_LINES_RENDERER_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+   GD_TYPE_TWO_LINES_RENDERER, GdTwoLinesRendererClass))
+
+typedef struct _GdTwoLinesRenderer GdTwoLinesRenderer;
+typedef struct _GdTwoLinesRendererClass GdTwoLinesRendererClass;
+typedef struct _GdTwoLinesRendererPrivate GdTwoLinesRendererPrivate;
+
+struct _GdTwoLinesRenderer
+{
+  GtkCellRendererText parent;
+
+  GdTwoLinesRendererPrivate *priv;
+};
+
+struct _GdTwoLinesRendererClass
+{
+  GtkCellRendererTextClass parent_class;
+};
+
+GType gd_two_lines_renderer_get_type (void) G_GNUC_CONST;
+
+GtkCellRenderer *gd_two_lines_renderer_new (void);
+
+G_END_DECLS
+
+#endif /* _GD_TWO_LINES_RENDERER_H */
diff --git a/subprojects/libgd/libgd/gd-types-catalog.c b/subprojects/libgd/libgd/gd-types-catalog.c
new file mode 100644
index 0000000..75f7d57
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-types-catalog.c
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2012 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include "gd-types-catalog.h"
+
+#ifdef LIBGD__BOX_COMMON
+# include "gd-main-box-child.h"
+# include "gd-main-box-generic.h"
+# include "gd-main-box-item.h"
+#endif
+
+#ifdef LIBGD_MAIN_ICON_BOX
+# include "gd-main-icon-box.h"
+# include "gd-main-icon-box-child.h"
+#endif
+
+#ifdef LIBGD_MAIN_BOX
+# include "gd-main-box.h"
+#endif
+
+#ifdef LIBGD__VIEW_COMMON
+# include "gd-main-view-generic.h"
+# include "gd-styled-text-renderer.h"
+# include "gd-two-lines-renderer.h"
+#endif
+
+#ifdef LIBGD_MAIN_ICON_VIEW
+# include "gd-main-icon-view.h"
+# include "gd-toggle-pixbuf-renderer.h"
+#endif
+
+#ifdef LIBGD_MAIN_LIST_VIEW
+# include "gd-main-list-view.h"
+#endif
+
+#ifdef LIBGD_MAIN_VIEW
+# include "gd-main-view.h"
+#endif
+
+#ifdef LIBGD_MARGIN_CONTAINER
+# include "gd-margin-container.h"
+#endif
+
+#ifdef LIBGD_TAGGED_ENTRY
+# include "gd-tagged-entry.h"
+#endif
+
+#ifdef LIBGD_NOTIFICATION
+# include "gd-notification.h"
+#endif
+
+/**
+ * gd_ensure_types:
+ *
+ * This functions must be called during initialization
+ * to make sure the widget types are available to GtkBuilder.
+ */
+void
+gd_ensure_types (void)
+{
+#ifdef LIBGD__BOX_COMMON
+  g_type_ensure (GD_TYPE_MAIN_BOX_CHILD);
+  g_type_ensure (GD_TYPE_MAIN_BOX_GENERIC);
+  g_type_ensure (GD_TYPE_MAIN_BOX_ITEM);
+#endif
+
+#ifdef LIBGD_MAIN_ICON_BOX
+  g_type_ensure (GD_TYPE_MAIN_ICON_BOX);
+  g_type_ensure (GD_TYPE_MAIN_ICON_BOX_CHILD);
+#endif
+
+#ifdef LIBGD_MAIN_BOX
+  g_type_ensure (GD_TYPE_MAIN_BOX);
+#endif
+
+#ifdef LIBGD__VIEW_COMMON
+  g_type_ensure (GD_TYPE_MAIN_VIEW_GENERIC);
+  g_type_ensure (GD_TYPE_STYLED_TEXT_RENDERER);
+  g_type_ensure (GD_TYPE_TWO_LINES_RENDERER);
+#endif
+
+#ifdef LIBGD_MAIN_ICON_VIEW
+  g_type_ensure (GD_TYPE_MAIN_ICON_VIEW);
+  g_type_ensure (GD_TYPE_TOGGLE_PIXBUF_RENDERER);
+#endif
+
+#ifdef LIBGD_MAIN_LIST_VIEW
+  g_type_ensure (GD_TYPE_MAIN_LIST_VIEW);
+#endif
+
+#ifdef LIBGD_MAIN_VIEW
+  g_type_ensure (GD_TYPE_MAIN_VIEW);
+#endif
+
+#ifdef LIBGD_MARGIN_CONTAINER
+  g_type_ensure (GD_TYPE_MARGIN_CONTAINER);
+#endif
+
+#ifdef LIBGD_TAGGED_ENTRY
+  g_type_ensure (GD_TYPE_TAGGED_ENTRY);
+#endif
+
+#ifdef LIBGD_NOTIFICATION
+  g_type_ensure (GD_TYPE_NOTIFICATION);
+#endif
+}
+
diff --git a/subprojects/libgd/libgd/gd-types-catalog.h b/subprojects/libgd/libgd/gd-types-catalog.h
new file mode 100644
index 0000000..fc99416
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-types-catalog.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2012 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __GD_TYPES_CATALOG_H__
+#define __GD_TYPES_CATALOG_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+void gd_ensure_types (void);
+
+G_END_DECLS
+
+#endif /* __GD_TYPES_CATALOG_H__ */
diff --git a/subprojects/libgd/libgd/gd.h b/subprojects/libgd/libgd/gd.h
new file mode 100644
index 0000000..69685c9
--- /dev/null
+++ b/subprojects/libgd/libgd/gd.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2012 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __GD_H__
+#define __GD_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#include <libgd/gd-types-catalog.h>
+
+#ifdef LIBGD_GTK_HACKS
+# include <libgd/gd-icon-utils.h>
+#endif
+
+#ifdef LIBGD__BOX_COMMON
+# include <libgd/gd-main-box-child.h>
+# include <libgd/gd-main-box-generic.h>
+# include <libgd/gd-main-box-item.h>
+#endif
+
+#ifdef LIBGD_MAIN_ICON_BOX
+# include <libgd/gd-main-icon-box.h>
+# include <libgd/gd-main-icon-box-child.h>
+#endif
+
+#ifdef LIBGD_MAIN_BOX
+# include <libgd/gd-main-box.h>
+#endif
+
+#ifdef LIBGD__VIEW_COMMON
+# include <libgd/gd-main-view-generic.h>
+# include <libgd/gd-styled-text-renderer.h>
+# include <libgd/gd-two-lines-renderer.h>
+#endif
+
+#ifdef LIBGD_MAIN_ICON_VIEW
+# include <libgd/gd-main-icon-view.h>
+# include <libgd/gd-toggle-pixbuf-renderer.h>
+#endif
+
+#ifdef LIBGD_MAIN_LIST_VIEW
+# include <libgd/gd-main-list-view.h>
+#endif
+
+#ifdef LIBGD_MAIN_VIEW
+# include <libgd/gd-main-view.h>
+#endif
+
+#ifdef LIBGD_MARGIN_CONTAINER
+# include <libgd/gd-margin-container.h>
+#endif
+
+#ifdef LIBGD_TAGGED_ENTRY
+# include <libgd/gd-tagged-entry.h>
+#endif
+
+#ifdef LIBGD_NOTIFICATION
+# include <libgd/gd-notification.h>
+#endif
+
+G_END_DECLS
+
+#endif /* __GD_H__ */
diff --git a/subprojects/libgd/libgd/meson.build b/subprojects/libgd/libgd/meson.build
new file mode 100644
index 0000000..c9c6d18
--- /dev/null
+++ b/subprojects/libgd/libgd/meson.build
@@ -0,0 +1,200 @@
+gnome = import('gnome')
+
+sources = [
+  'gd.h',
+  'gd-types-catalog.c'
+]
+built_sources = []
+c_args = []
+private_c_args = [
+  '-DG_LOG_DOMAIN="libgd"',
+  '-DG_DISABLE_DEPRECATED',
+]
+
+if get_option('with-gtk-hacks')
+  sources += [
+    'gd-icon-utils.c',
+    'gd-icon-utils.h',
+  ]
+  c_args += '-DLIBGD_GTK_HACKS=1'
+endif
+
+if (get_option('with-main-box') or
+    get_option('with-main-icon-box'))
+  sources += [
+    'gd-main-box-child.c',
+    'gd-main-box-child.h',
+    'gd-main-box-generic.c',
+    'gd-main-box-generic.h',
+    'gd-main-box-item.c',
+    'gd-main-box-item.h'
+  ]
+  c_args += '-DLIBGD__BOX_COMMON=1'
+
+  if get_option('with-main-icon-box')
+    sources += [
+      'gd-main-icon-box.c',
+      'gd-main-icon-box.h',
+      'gd-main-icon-box-child.c',
+      'gd-main-icon-box-child.h',
+      'gd-main-icon-box-icon.c',
+      'gd-main-icon-box-icon.h',
+      'gd-icon-utils.c',
+      'gd-icon-utils.h',
+    ]
+    c_args += '-DLIBGD_MAIN_ICON_BOX=1'
+  endif
+
+  if get_option('with-main-box')
+    sources += [
+      'gd-main-box.c',
+      'gd-main-box.h',
+    ]
+    c_args += '-DLIBGD_MAIN_BOX=1'
+  endif
+endif
+
+if (get_option('with-main-icon-view') or
+    get_option('with-main-list-view') or
+    get_option('with-main-view'))
+  sources += [
+    'gd-main-view-generic.c',
+    'gd-main-view-generic.h',
+    'gd-styled-text-renderer.c',
+    'gd-styled-text-renderer.h',
+    'gd-two-lines-renderer.c',
+    'gd-two-lines-renderer.h',
+  ]
+  c_args += '-DLIBGD__VIEW_COMMON=1'
+
+  if (get_option('with-main-icon-view') or
+      get_option('with-main-view'))
+    sources += [
+      'gd-main-icon-view.c',
+      'gd-main-icon-view.h',
+      'gd-toggle-pixbuf-renderer.c',
+      'gd-toggle-pixbuf-renderer.h'
+    ]
+    c_args += '-DLIBGD_MAIN_ICON_VIEW=1'
+  endif
+
+  if (get_option('with-main-list-view') or
+      get_option('with-main-view'))
+    sources += [
+      'gd-main-list-view.c',
+      'gd-main-list-view.h',
+    ]
+    c_args += '-DLIBGD_MAIN_LIST_VIEW=1'
+  endif
+
+  if get_option('with-main-view')
+    sources += [
+      'gd-main-view.c',
+      'gd-main-view.h',
+    ]
+    c_args += '-DLIBGD_MAIN_VIEW=1'
+  endif
+endif
+
+if get_option('with-margin-container')
+  sources += [
+    'gd-margin-container.c',
+    'gd-margin-container.h',
+  ]
+  c_args += '-DLIBGD_MARGIN_CONTAINER=1'
+endif
+
+if get_option('with-tagged-entry')
+  sources += [
+    'gd-tagged-entry.c',
+    'gd-tagged-entry.h',
+  ]
+  c_args += '-DLIBGD_TAGGED_ENTRY=1'
+endif
+
+if get_option('with-notification')
+  sources += [
+    'gd-notification.c',
+    'gd-notification.h',
+  ]
+  c_args += '-DLIBGD_NOTIFICATION=1'
+endif
+
+if sources.length() == 2
+  error('You must include a feature to be built!')
+endif
+
+# --------- Building -----------
+
+static = get_option('static')
+install_introspection = get_option('with-introspection')
+with_vapi = get_option('with-vapi')
+
+if static
+  libgd_lib = static_library('gd', sources,
+    dependencies: [libgtk, libm],
+    include_directories: libgd_include,
+    c_args: c_args + private_c_args
+  )
+endif
+
+# Currently in Meson building gir requires a shared library
+if not static or (install_introspection or with_vapi)
+  if not static and pkglibdir == ''
+    error('Installing shared library but pkglibdir is unset!')
+  endif
+
+  libgd_shared_lib = shared_library('gd', sources,
+    dependencies: [libgtk, libm],
+    include_directories: libgd_include,
+    c_args: c_args + private_c_args,
+    install: not static,
+    install_dir: pkglibdir
+  )
+
+  if not static
+    libgd_lib = libgd_shared_lib
+  endif
+endif
+
+if install_introspection or with_vapi
+  if install_introspection
+    if pkgdatadir == ''
+      error('Installing introspection but pkgdatadir is unset!')
+    elif pkglibdir == ''
+      error('Installing introspection but pkglibdir is unset!')
+    endif
+  endif
+
+  libgd_gir = gnome.generate_gir(libgd_shared_lib,
+    sources : sources,
+    nsversion : '1.0',
+    namespace : 'Gd',
+    symbol_prefix : 'gd',
+    identifier_prefix : 'Gd',
+    includes : 'Gtk-3.0',
+    include_directories: libgd_include,
+    install: install_introspection,
+    install_dir_gir: join_paths(pkgdatadir, 'gir-1.0'),
+    install_dir_typelib: join_paths(pkglibdir, 'girepository-1.0'),
+    extra_args: [
+      '--c-include=libgd/gd.h',
+    ]
+  )
+  built_sources += libgd_gir
+
+  if get_option('with-vapi')
+    libgd_vapi_dep = gnome.generate_vapi('gd-1.0',
+      sources: libgd_gir[0],
+      packages: ['gtk+-3.0']
+    )
+  endif
+endif
+
+libgd_dep = declare_dependency(
+  link_with: libgd_lib,
+  include_directories: libgd_include,
+  dependencies: libgtk,
+  compile_args: c_args,
+  sources: built_sources
+)
diff --git a/subprojects/libgd/meson.build b/subprojects/libgd/meson.build
new file mode 100644
index 0000000..6d1242c
--- /dev/null
+++ b/subprojects/libgd/meson.build
@@ -0,0 +1,24 @@
+project('libgd', 'c',
+  meson_version: '>= 0.38.0',
+  default_options: ['static=true'],
+)
+
+if not meson.is_subproject()
+  message('WARNING: This project is only intended to be used as a subproject!')
+endif
+
+pkglibdir = get_option('pkglibdir')
+pkgdatadir = get_option('pkgdatadir')
+
+libgtk = dependency('gtk+-3.0', version: '>= 3.7.10')
+cc = meson.get_compiler('c')
+libm = cc.find_library('m', required: false)
+libgd_include = include_directories('.')
+
+subdir('libgd')
+
+if get_option('with-tagged-entry')
+  foreach t : ['test-tagged-entry', 'test-tagged-entry-2']
+    executable(t, t + '.c', dependencies : libgd_dep)
+  endforeach
+endif
diff --git a/subprojects/libgd/meson_options.txt b/subprojects/libgd/meson_options.txt
new file mode 100644
index 0000000..fcab3a0
--- /dev/null
+++ b/subprojects/libgd/meson_options.txt
@@ -0,0 +1,25 @@
+option('pkglibdir', type: 'string', value: '',
+  description: 'The private directory the shared library/typelib will be installed into.'
+)
+option('pkgdatadir', type: 'string', value: '',
+  description: 'The private directory the gir file will be installed into.'
+)
+option('static', type: 'boolean', value: false,
+  description: 'Build as a static library'
+)
+option('with-introspection', type: 'boolean', value: false,
+  description: 'Build gobject-introspection support'
+)
+option('with-vapi', type: 'boolean', value: false,
+  description: 'Build vapi file'
+)
+# Widget options
+option('with-gtk-hacks', type: 'boolean', value: false)
+option('with-main-view', type: 'boolean', value: false)
+option('with-main-icon-view', type: 'boolean', value: false)
+option('with-main-list-view', type: 'boolean', value: false)
+option('with-margin-container', type: 'boolean', value: false)
+option('with-tagged-entry', type: 'boolean', value: false)
+option('with-notification', type: 'boolean', value: false)
+option('with-main-box', type: 'boolean', value: false)
+option('with-main-icon-box', type: 'boolean', value: false)
\ No newline at end of file
diff --git a/subprojects/libgd/meson_readme.md b/subprojects/libgd/meson_readme.md
new file mode 100644
index 0000000..c3e7f26
--- /dev/null
+++ b/subprojects/libgd/meson_readme.md
@@ -0,0 +1,88 @@
+See README for general information. Read below for usage with Meson.
+
+Usage
+=====
+
+libgd is intended to be used as a submodule from other projects. This requires passing default_options to 
the subproject
+which was added in Meson 0.38.0. To see a full list of options you can run `mesonconf $your_build_dir`. If 
building a
+non-static library `pkglibdir` must be set to a private location to install to which you will also want to 
pass (an absolute path)
+with the `install_rpath` keyword to any executables. For introspection files you also must set `pkgdatadir`.
+
+So given a Meson project using git you would run this to do initial setup:
+
+```
+mkdir subprojects
+git submodule add https://git.gnome.org/browse/libgd subprojects/libgd
+```
+
+Then from within your `meson.build` file:
+
+Static Library
+--------------
+
+```meson
+libgd = subproject('libgd',
+  default_options: [
+    'with-tagged-entry=true'
+  ]
+)
+# Pass as dependency to another target
+libgd_dep = libgd.get_variable('libgd_dep')
+```
+
+```c
+#include "libgd/gd.h"
+
+int main(int argc, char **argv)
+{
+  gd_ensure_types(); /* As a test */
+  return 0;
+}
+```
+
+Introspection
+-------------
+
+```meson
+pkglibdir = join_paths(get_option('libdir'), meson.project_name())
+pkgdatadir = join_paths(get_option('datadir'), meson.project_name())
+libgd = subproject('libgd',
+  default_options: [
+    'pkglibdir=' + pkglibdir,
+    'pkgdatadir=' + pkgdatadir,
+    'with-tagged-entry=true',
+    'with-introspection=true',
+    'static=false',
+  ]
+)
+```
+
+```python
+import os
+import gi
+gi.require_version('GIRepository', '2.0')
+from gi.repository import GIRepository
+pkglibdir = '/usr/lib/foo' # This would be defined at build time
+pkggirdir = os.path.join(pkglibdir, 'girepository-1.0')
+GIRepository.Repository.prepend_search_path(pkggirdir)
+GIRepository.Repository.prepend_library_path(pkglibdir)
+gi.require_version('Gd', '1.0')
+```
+
+Vala
+----
+
+```meson
+pkglibdir = join_paths(get_option('libdir'), meson.project_name())
+libgd = subproject('libgd',
+  default_options: [
+    'pkglibdir=' + pkglibdir,
+    'with-tagged-entry=true',
+    'with-vapi=true'
+  ]
+)
+# Pass as dependency to a Vala target
+libgd_vapi_dep = libgd.get_variable('libgd_vapi_dep')
+```
+
+<!-- TODO: Make a Vala example -->
diff --git a/subprojects/libgd/test-tagged-entry-2.c b/subprojects/libgd/test-tagged-entry-2.c
new file mode 100644
index 0000000..465ab83
--- /dev/null
+++ b/subprojects/libgd/test-tagged-entry-2.c
@@ -0,0 +1,131 @@
+#include <gtk/gtk.h>
+#include <libgd/gd-tagged-entry.h>
+
+static GdTaggedEntryTag *toggle_tag;
+
+static void
+on_tag_clicked (GdTaggedEntry *entry,
+                GdTaggedEntryTag *tag,
+                gpointer useless)
+{
+  g_print ("tag clicked: %s\n", gd_tagged_entry_tag_get_label (tag));
+}
+
+static void
+on_tag_button_clicked (GdTaggedEntry *entry,
+                       GdTaggedEntryTag *tag,
+                       gpointer useless)
+{
+  g_print ("tag button clicked: %s\n", gd_tagged_entry_tag_get_label (tag));
+}
+
+static void
+on_toggle_visible (GtkButton *button,
+                   GtkWidget *entry)
+{
+  gboolean active;
+
+  active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
+
+  g_print ("%s tagged entry\n", active ? "show" : "hide");
+  gtk_widget_set_visible (entry, active);
+}
+
+static void
+on_toggle_tag (GtkButton *button,
+               GdTaggedEntry *entry)
+{
+  gboolean active;
+
+  active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
+
+  if (active)
+    {
+      g_print ("adding tag 'Toggle Tag'\n");
+      gd_tagged_entry_insert_tag (entry, toggle_tag, 0);
+    }
+  else
+    {
+      g_print ("removing tag 'Toggle Tag'\n");
+      gd_tagged_entry_remove_tag (entry, toggle_tag);
+    }
+}
+
+gint
+main (gint argc,
+      gchar ** argv)
+{
+  GtkWidget *box;
+  GtkWidget *entry;
+  GtkWidget *search_bar;
+  GtkWidget *search_container;
+  GtkWidget *toggle_tag_button;
+  GtkWidget *toggle_visible_button;
+  GtkWidget *revealer;
+  GtkWidget *window;
+  GdTaggedEntryTag *tag;
+
+  gtk_init (&argc, &argv);
+
+  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  gtk_widget_set_size_request (window, 640, 600);
+
+  box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+  gtk_container_add (GTK_CONTAINER (window), box);
+
+  revealer = gtk_revealer_new ();
+  gtk_revealer_set_reveal_child (GTK_REVEALER (revealer), TRUE);
+  gtk_container_add (GTK_CONTAINER (box), revealer);
+
+  search_bar = gtk_search_bar_new ();
+  gtk_search_bar_set_search_mode (GTK_SEARCH_BAR (search_bar), TRUE);
+  gtk_container_add (GTK_CONTAINER (revealer), search_bar);
+
+  search_container = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+  gtk_widget_set_halign (search_container, GTK_ALIGN_CENTER);
+  gtk_container_add (GTK_CONTAINER (search_bar), search_container);
+
+  entry = GTK_WIDGET (gd_tagged_entry_new ());
+  gtk_widget_set_size_request (entry, 500, -1);
+  g_signal_connect (entry, "tag-clicked",
+                    G_CALLBACK (on_tag_clicked), NULL);
+  g_signal_connect (entry, "tag-button-clicked",
+                    G_CALLBACK (on_tag_button_clicked), NULL);
+  gtk_container_add (GTK_CONTAINER (search_container), entry);
+
+  tag = gd_tagged_entry_tag_new ("Blah1");
+  gd_tagged_entry_add_tag (GD_TAGGED_ENTRY (entry), tag);
+  g_object_unref (tag);
+
+  tag = gd_tagged_entry_tag_new ("Blah2");
+  gd_tagged_entry_tag_set_has_close_button (tag, FALSE);
+  gd_tagged_entry_insert_tag (GD_TAGGED_ENTRY (entry), tag, -1);
+  g_object_unref (tag);
+
+  tag = gd_tagged_entry_tag_new ("Blah3");
+  gd_tagged_entry_tag_set_has_close_button (tag, FALSE);
+  gd_tagged_entry_insert_tag (GD_TAGGED_ENTRY (entry), tag, 0);
+  g_object_unref (tag);
+
+  toggle_visible_button = gtk_toggle_button_new_with_label ("Visible");
+  gtk_widget_set_vexpand (toggle_visible_button, TRUE);
+  gtk_widget_set_valign (toggle_visible_button, GTK_ALIGN_END);
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle_visible_button), TRUE);
+  g_signal_connect (toggle_visible_button, "toggled",
+                    G_CALLBACK (on_toggle_visible), entry);
+  gtk_container_add (GTK_CONTAINER (box), toggle_visible_button);
+
+  toggle_tag = gd_tagged_entry_tag_new ("Toggle Tag");
+
+  toggle_tag_button = gtk_toggle_button_new_with_label ("Toggle Tag");
+  g_signal_connect (toggle_tag_button, "toggled",
+                    G_CALLBACK (on_toggle_tag), entry);
+  gtk_container_add (GTK_CONTAINER (box), toggle_tag_button);
+
+  gtk_widget_show_all (window);
+  gtk_main ();
+
+  gtk_widget_destroy (window);
+
+  return 0;
+}
diff --git a/subprojects/libgd/test-tagged-entry.c b/subprojects/libgd/test-tagged-entry.c
new file mode 100644
index 0000000..0f583d9
--- /dev/null
+++ b/subprojects/libgd/test-tagged-entry.c
@@ -0,0 +1,111 @@
+#include <gtk/gtk.h>
+#include <libgd/gd-tagged-entry.h>
+
+static GdTaggedEntryTag *toggle_tag;
+
+static void
+on_tag_clicked (GdTaggedEntry *entry,
+                GdTaggedEntryTag *tag,
+                gpointer useless)
+{
+  g_print ("tag clicked: %s\n", gd_tagged_entry_tag_get_label (tag));
+}
+
+static void
+on_tag_button_clicked (GdTaggedEntry *entry,
+                       GdTaggedEntryTag *tag,
+                       gpointer useless)
+{
+  g_print ("tag button clicked: %s\n", gd_tagged_entry_tag_get_label (tag));
+}
+
+static void
+on_toggle_visible (GtkButton *button,
+                   GtkWidget *entry)
+{
+  gboolean active;
+
+  active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
+
+  g_print ("%s tagged entry\n", active ? "show" : "hide");
+  gtk_widget_set_visible (entry, active);
+}
+
+static void
+on_toggle_tag (GtkButton *button,
+               GdTaggedEntry *entry)
+{
+  gboolean active;
+
+  active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
+
+  if (active)
+    {
+      g_print ("adding tag 'Toggle Tag'\n");
+      gd_tagged_entry_insert_tag (entry, toggle_tag, 0);
+    }
+  else
+    {
+      g_print ("removing tag 'Toggle Tag'\n");
+      gd_tagged_entry_remove_tag (entry, toggle_tag);
+    }
+}
+
+gint
+main (gint argc,
+      gchar ** argv)
+{
+  GtkWidget *window, *box, *entry, *toggle_visible_button, *toggle_tag_button;
+  GdTaggedEntryTag *tag;
+
+  gtk_init (&argc, &argv);
+
+  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  gtk_widget_set_size_request (window, 300, 0);
+
+  box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+  gtk_container_add (GTK_CONTAINER (window), box);
+
+  entry = GTK_WIDGET (gd_tagged_entry_new ());
+  g_signal_connect(entry, "tag-clicked",
+                   G_CALLBACK (on_tag_clicked), NULL);
+  g_signal_connect(entry, "tag-button-clicked",
+                   G_CALLBACK (on_tag_button_clicked), NULL);
+  gtk_container_add (GTK_CONTAINER (box), entry);
+
+  tag = gd_tagged_entry_tag_new ("Blah1");
+  gd_tagged_entry_add_tag (GD_TAGGED_ENTRY (entry), tag);
+  g_object_unref (tag);
+
+  tag = gd_tagged_entry_tag_new ("Blah2");
+  gd_tagged_entry_tag_set_has_close_button (tag, FALSE);
+  gd_tagged_entry_insert_tag (GD_TAGGED_ENTRY (entry), tag, -1);
+  g_object_unref (tag);
+
+  tag = gd_tagged_entry_tag_new ("Blah3");
+  gd_tagged_entry_tag_set_has_close_button (tag, FALSE);
+  gd_tagged_entry_insert_tag (GD_TAGGED_ENTRY (entry), tag, 0);
+  g_object_unref (tag);
+
+  toggle_visible_button = gtk_toggle_button_new_with_label ("Visible");
+  gtk_widget_set_vexpand (toggle_visible_button, TRUE);
+  gtk_widget_set_valign (toggle_visible_button, GTK_ALIGN_END);
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle_visible_button), TRUE);
+  g_signal_connect (toggle_visible_button, "toggled",
+                    G_CALLBACK (on_toggle_visible), entry);
+  gtk_container_add (GTK_CONTAINER (box), toggle_visible_button);
+
+  toggle_tag = gd_tagged_entry_tag_new ("Toggle Tag");
+
+  toggle_tag_button = gtk_toggle_button_new_with_label ("Toggle Tag");
+  g_signal_connect (toggle_tag_button, "toggled",
+                    G_CALLBACK (on_toggle_tag), entry);
+  gtk_container_add (GTK_CONTAINER (box), toggle_tag_button);
+
+  gtk_widget_show_all (window);
+  gtk_main ();
+
+  gtk_widget_destroy (window);
+
+  return 0;
+}


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