summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore85
-rw-r--r--.gitlab-ci.yml30
-rw-r--r--.gitmodules9
m---------subprojects/gfm0
-rw-r--r--subprojects/gfm/gtkfilterlistmodel.c711
-rw-r--r--subprojects/gfm/gtkfilterlistmodel.h81
-rw-r--r--subprojects/gfm/gtkflattenlistmodel.c541
-rw-r--r--subprojects/gfm/gtkflattenlistmodel.h52
-rw-r--r--subprojects/gfm/gtkintl.h15
-rw-r--r--subprojects/gfm/gtkrbtree.c800
-rw-r--r--subprojects/gfm/gtkrbtreeprivate.h75
-rw-r--r--subprojects/gfm/gtkslicelistmodel.c527
-rw-r--r--subprojects/gfm/gtkslicelistmodel.h66
-rw-r--r--subprojects/gfm/gtksortlistmodel.c563
-rw-r--r--subprojects/gfm/gtksortlistmodel.h68
-rw-r--r--subprojects/gfm/meson.build85
-rw-r--r--subprojects/gfm/meson_options.txt6
m---------subprojects/libgd0
-rw-r--r--subprojects/libgd/Makefile.am230
-rw-r--r--subprojects/libgd/README123
-rw-r--r--subprojects/libgd/libgd.doap35
-rw-r--r--subprojects/libgd/libgd.m4140
-rw-r--r--subprojects/libgd/libgd/gd-icon-utils.c394
-rw-r--r--subprojects/libgd/libgd/gd-icon-utils.h49
-rw-r--r--subprojects/libgd/libgd/gd-main-box-child.c186
-rw-r--r--subprojects/libgd/libgd/gd-main-box-child.h54
-rw-r--r--subprojects/libgd/libgd/gd-main-box-generic.c491
-rw-r--r--subprojects/libgd/libgd/gd-main-box-generic.h73
-rw-r--r--subprojects/libgd/libgd/gd-main-box-item.c242
-rw-r--r--subprojects/libgd/libgd/gd-main-box-item.h55
-rw-r--r--subprojects/libgd/libgd/gd-main-box.c548
-rw-r--r--subprojects/libgd/libgd/gd-main-box.h61
-rw-r--r--subprojects/libgd/libgd/gd-main-icon-box-child.c439
-rw-r--r--subprojects/libgd/libgd/gd-main-icon-box-child.h43
-rw-r--r--subprojects/libgd/libgd/gd-main-icon-box-icon.c366
-rw-r--r--subprojects/libgd/libgd/gd-main-icon-box-icon.h40
-rw-r--r--subprojects/libgd/libgd/gd-main-icon-box.c1042
-rw-r--r--subprojects/libgd/libgd/gd-main-icon-box.h44
-rw-r--r--subprojects/libgd/libgd/gd-main-icon-view.c440
-rw-r--r--subprojects/libgd/libgd/gd-main-icon-view.h42
-rw-r--r--subprojects/libgd/libgd/gd-main-list-view.c345
-rw-r--r--subprojects/libgd/libgd/gd-main-list-view.h80
-rw-r--r--subprojects/libgd/libgd/gd-main-view-generic.c330
-rw-r--r--subprojects/libgd/libgd/gd-main-view-generic.h117
-rw-r--r--subprojects/libgd/libgd/gd-main-view.c1160
-rw-r--r--subprojects/libgd/libgd/gd-main-view.h65
-rw-r--r--subprojects/libgd/libgd/gd-margin-container.c377
-rw-r--r--subprojects/libgd/libgd/gd-margin-container.h75
-rw-r--r--subprojects/libgd/libgd/gd-notification.c870
-rw-r--r--subprojects/libgd/libgd/gd-notification.h67
-rw-r--r--subprojects/libgd/libgd/gd-styled-text-renderer.c134
-rw-r--r--subprojects/libgd/libgd/gd-styled-text-renderer.h51
-rw-r--r--subprojects/libgd/libgd/gd-tagged-entry.c1240
-rw-r--r--subprojects/libgd/libgd/gd-tagged-entry.h117
-rw-r--r--subprojects/libgd/libgd/gd-toggle-pixbuf-renderer.c280
-rw-r--r--subprojects/libgd/libgd/gd-toggle-pixbuf-renderer.h47
-rw-r--r--subprojects/libgd/libgd/gd-two-lines-renderer.c632
-rw-r--r--subprojects/libgd/libgd/gd-two-lines-renderer.h43
-rw-r--r--subprojects/libgd/libgd/gd-types-catalog.c123
-rw-r--r--subprojects/libgd/libgd/gd-types-catalog.h31
-rw-r--r--subprojects/libgd/libgd/gd.h81
-rw-r--r--subprojects/libgd/libgd/meson.build204
-rw-r--r--subprojects/libgd/meson.build24
-rw-r--r--subprojects/libgd/meson_options.txt25
-rw-r--r--subprojects/libgd/meson_readme.md88
-rw-r--r--subprojects/libgd/test-tagged-entry-2.c131
-rw-r--r--subprojects/libgd/test-tagged-entry.c111
m---------subprojects/shared-modules0
-rw-r--r--subprojects/shared-modules/CODEOWNERS11
-rw-r--r--subprojects/shared-modules/README.md29
-rw-r--r--subprojects/shared-modules/SDL/SDL-1.2.15.json40
-rw-r--r--subprojects/shared-modules/SDL/SDL_Pango-0.1.2-API-adds.patch118
-rw-r--r--subprojects/shared-modules/SDL/SDL_image-1.2.12.json19
-rw-r--r--subprojects/shared-modules/SDL/SDL_mixer-1.2.12.json22
-rw-r--r--subprojects/shared-modules/SDL/SDL_net-1.2.8.json19
-rw-r--r--subprojects/shared-modules/SDL/SDL_pango-0.1.2.json23
-rw-r--r--subprojects/shared-modules/SDL/SDL_ttf-2.0.11.json19
-rw-r--r--subprojects/shared-modules/SDL/sdl-check-for-SDL_VIDEO_X11_BACKINGSTORE.patch24
-rw-r--r--subprojects/shared-modules/SDL/sdl-libx11-build.patch59
-rw-r--r--subprojects/shared-modules/cld2/CMakeLists.txt155
-rw-r--r--subprojects/shared-modules/cld2/cld2.json24
-rw-r--r--subprojects/shared-modules/dbus-glib/dbus-glib-0.110.json23
-rw-r--r--subprojects/shared-modules/glew/glew.json26
-rw-r--r--subprojects/shared-modules/glu/glu-9.json12
-rw-r--r--subprojects/shared-modules/gtk2/gtk2-use-adwaita-theme.patch80
-rw-r--r--subprojects/shared-modules/gtk2/gtk2.json109
-rw-r--r--subprojects/shared-modules/gudev/gudev.json27
-rw-r--r--subprojects/shared-modules/intltool/intltool-0.51.json11
-rw-r--r--subprojects/shared-modules/lame/lame-3.99.5.json37
-rw-r--r--subprojects/shared-modules/lame/lame-ansi2knr2devnull.patch43
-rw-r--r--subprojects/shared-modules/lame/lame-gtk1-ac-directives.patch205
-rw-r--r--subprojects/shared-modules/lame/lame-msse.patch17
-rw-r--r--subprojects/shared-modules/lame/lame-tinfo.patch23
-rw-r--r--subprojects/shared-modules/libappindicator/Makefile10
-rw-r--r--subprojects/shared-modules/libappindicator/libappindicator-ftbfs.patch20
-rw-r--r--subprojects/shared-modules/libappindicator/libappindicator-gtk2-12.10.json121
-rw-r--r--subprojects/shared-modules/libappindicator/libappindicator-gtk3-12.10.json121
-rw-r--r--subprojects/shared-modules/libappindicator/libappindicator-gtk3-introspection-12.10.json121
-rw-r--r--subprojects/shared-modules/libappindicator/libappindicator-no-python.patch888
-rw-r--r--subprojects/shared-modules/libappindicator/libappindicator.json.in121
-rw-r--r--subprojects/shared-modules/libmad/Provide-Thumb-2-alternative-code-for-MAD_F_MLN.diff34
-rw-r--r--subprojects/shared-modules/libmad/libmad-0.15.1b-cflags-O2.patch12
-rw-r--r--subprojects/shared-modules/libmad/libmad-0.15.1b-cflags.patch146
-rw-r--r--subprojects/shared-modules/libmad/libmad-0.15.1b-multiarch.patch37
-rw-r--r--subprojects/shared-modules/libmad/libmad-0.15.1b-ppc.patch13
-rw-r--r--subprojects/shared-modules/libmad/libmad.json41
-rw-r--r--subprojects/shared-modules/libmad/libmad.thumb.diff12
-rw-r--r--subprojects/shared-modules/libsecret/libsecret.json23
-rw-r--r--subprojects/shared-modules/lua5.3/lua-5.3.0-autotoolize.patch192
-rw-r--r--subprojects/shared-modules/lua5.3/lua-5.3.0-configure-compat-module.patch35
-rw-r--r--subprojects/shared-modules/lua5.3/lua-5.3.0-idsize.patch12
-rw-r--r--subprojects/shared-modules/lua5.3/lua-5.3.5.json34
-rw-r--r--subprojects/shared-modules/openjpeg/openjpeg.json25
-rw-r--r--subprojects/shared-modules/pygame/audiofile-gcc6.patch21
-rw-r--r--subprojects/shared-modules/pygame/fluidsynth-no-rawmidi.patch69
-rw-r--r--subprojects/shared-modules/pygame/portmidi-no-java.patch105
-rw-r--r--subprojects/shared-modules/pygame/pygame-1.9.4.json112
-rw-r--r--subprojects/shared-modules/python2.7/python-2.7.json52
-rw-r--r--subprojects/shared-modules/qt4/disable-sslv3.patch54
-rw-r--r--subprojects/shared-modules/qt4/qt4-4.8.7-minimal.json110
-rw-r--r--subprojects/shared-modules/qt4/qt4-aarch64.patch514
-rw-r--r--subprojects/shared-modules/qt4/qt4-openssl-1.1.patch409
-rw-r--r--subprojects/shared-modules/smpeg/smpeg-0.4.5.json39
-rw-r--r--subprojects/shared-modules/smpeg/smpeg-am-prog-as.patch12
-rw-r--r--subprojects/shared-modules/smpeg/smpeg-export-mpegaudio-class.patch17
-rw-r--r--subprojects/shared-modules/smpeg/smpeg-gcc6.patch40
-rw-r--r--subprojects/shared-modules/smpeg/smpeg-no-gtk.patch37
127 files changed, 20359 insertions, 124 deletions
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index cbbcb9ca..00000000
--- a/.gitignore
+++ /dev/null
@@ -1,85 +0,0 @@
-*~
-builddir/
-/*.bak
-/*.lo
-/*.o
-/*.orig
-/*.rej
-/*.tab.c
-/*~
-/.*.sw[nop]
-/.deps
-/.dirstamp
-/.gitignore
-/.libs
-/AUTHORS
-/ABOUT-NLS
-/GPATH
-/GRTAGS
-/GSYMS
-/GTAGS
-/ID
-/INSTALL
-/Makefile
-/Makefile.in
-/TAGS
-/_build
-/_libs
-/aclocal.m4
-/autom4te.cache
-/autoscan.log
-/compile
-/config.cache
-/config.guess
-/config.h
-/config.h.in
-/config.log
-/config.lt
-/config.rpath
-/config.status
-/config.status.lineno
-/config.sub
-/configure
-/configure.lineno
-/configure.scan
-/depcomp
-/install-sh
-/libtool
-/ltmain.sh
-/m4
-/missing
-/mkinstalldirs
-/po/*.gmo
-/po/*.mo
-/po/Makefile
-/po/Makefile.in
-/po/Makefile.in.in
-/po/POTFILES
-/po/gnome-music.pot
-/po/stamp-it
-/so_locations
-/stamp-h1
-/tags
-/gnome-music/*.pyc
-/gnome-music/Makefile
-/gnome-music/Makefile.in
-/py-compile
-*.tar.xz
-*.desktop
-*.gresource
-*.compiled
-data/Makefile
-data/Makefile.in
-*.patch
-*.pyc
-Makefile
-gnome-music
-*.valid
-/gnomemusic/Makefile.in
-/data/AboutDialog.ui
-/data/gnome-music.appdata.xml
-/help/Makefile.in
-/help/**/*.page
-/help/**/*.mo
-/help/**/*.stamp
-/help/**/legal.xml
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
deleted file mode 100644
index a484574a..00000000
--- a/.gitlab-ci.yml
+++ /dev/null
@@ -1,30 +0,0 @@
-include: 'https://gitlab.gnome.org/GNOME/citemplates/raw/master/flatpak/flatpak_ci_initiative.yml'
-
-stages:
- - check
- - build-flatpak
- - deploy
-
-variables:
- BUNDLE: "gnome-music-dev.flatpak"
- GIT_SUBMODULE_STRATEGY: normal
-
-flatpak:
- extends: .flatpak
- stage: build-flatpak
- variables:
- APP_ID: "org.gnome.Music.Devel"
- FLATPAK_MODULE: "gnome-music"
- MANIFEST_PATH: "org.gnome.Music.json"
- RUNTIME_REPO: "https://nightly.gnome.org/gnome-nightly.flatpakrepo"
-
-nightly:
- extends: .publish_nightly
- dependencies:
- - flatpak
-
-flake8:
- stage: check
- script:
- - dnf install -y python3-flake8
- - flake8 --ignore E402,W503 --show-source gnomemusic/
diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index 9b66516e..00000000
--- a/.gitmodules
+++ /dev/null
@@ -1,9 +0,0 @@
-[submodule "subprojects/libgd"]
- path = subprojects/libgd
- url = https://gitlab.gnome.org/GNOME/libgd.git
-[submodule "subprojects/shared-modules"]
- path = subprojects/shared-modules
- url = https://github.com/flathub/shared-modules.git
-[submodule "subprojects/gfm"]
- path = subprojects/gfm
- url = https://gitlab.gnome.org/mschraal/gfm.git
diff --git a/subprojects/gfm b/subprojects/gfm
deleted file mode 160000
-Subproject f53bde10101e025c83868bcbad34f7a248d0526
diff --git a/subprojects/gfm/gtkfilterlistmodel.c b/subprojects/gfm/gtkfilterlistmodel.c
new file mode 100644
index 00000000..5cfd4542
--- /dev/null
+++ b/subprojects/gfm/gtkfilterlistmodel.c
@@ -0,0 +1,711 @@
+/*
+ * Copyright © 2018 Benjamin Otte
+ *
+ * This library 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.1 of the License, or (at your option) any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte@gnome.org>
+ */
+
+// #include "config.h"
+
+#include "gtkfilterlistmodel.h"
+
+#include "gtkrbtreeprivate.h"
+#include "gtkintl.h"
+// #include "gtkprivate.h"
+
+/**
+ * SECTION:gtkfilterlistmodel
+ * @title: GtkFilterListModel
+ * @short_description: A list model that filters its items
+ * @see_also: #GListModel
+ *
+ * #GtkFilterListModel is a list model that filters a given other
+ * listmodel.
+ * It hides some elements from the other model according to
+ * criteria given by a #GtkFilterListModelFilterFunc.
+ */
+
+enum {
+ PROP_0,
+ PROP_HAS_FILTER,
+ PROP_ITEM_TYPE,
+ PROP_MODEL,
+ NUM_PROPERTIES
+};
+
+typedef struct _FilterNode FilterNode;
+typedef struct _FilterAugment FilterAugment;
+
+struct _FilterNode
+{
+ guint visible : 1;
+};
+
+struct _FilterAugment
+{
+ guint n_items;
+ guint n_visible;
+};
+
+struct _GtkFilterListModel
+{
+ GObject parent_instance;
+
+ GType item_type;
+ GListModel *model;
+ GtkFilterListModelFilterFunc filter_func;
+ gpointer user_data;
+ GDestroyNotify user_destroy;
+
+ GtkRbTree *items; /* NULL if filter_func == NULL */
+};
+
+struct _GtkFilterListModelClass
+{
+ GObjectClass parent_class;
+};
+
+static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
+
+static FilterNode *
+gtk_filter_list_model_get_nth_filtered (GtkRbTree *tree,
+ guint position,
+ guint *out_unfiltered)
+{
+ FilterNode *node, *tmp;
+ guint unfiltered;
+
+ node = gtk_rb_tree_get_root (tree);
+ unfiltered = 0;
+
+ while (node)
+ {
+ tmp = gtk_rb_tree_node_get_left (node);
+ if (tmp)
+ {
+ FilterAugment *aug = gtk_rb_tree_get_augment (tree, tmp);
+ if (position < aug->n_visible)
+ {
+ node = tmp;
+ continue;
+ }
+ position -= aug->n_visible;
+ unfiltered += aug->n_items;
+ }
+
+ if (node->visible)
+ {
+ if (position == 0)
+ break;
+ position--;
+ }
+
+ unfiltered++;
+
+ node = gtk_rb_tree_node_get_right (node);
+ }
+
+ if (out_unfiltered)
+ *out_unfiltered = unfiltered;
+
+ return node;
+}
+
+static FilterNode *
+gtk_filter_list_model_get_nth (GtkRbTree *tree,
+ guint position,
+ guint *out_filtered)
+{
+ FilterNode *node, *tmp;
+ guint filtered;
+
+ node = gtk_rb_tree_get_root (tree);
+ filtered = 0;
+
+ while (node)
+ {
+ tmp = gtk_rb_tree_node_get_left (node);
+ if (tmp)
+ {
+ FilterAugment *aug = gtk_rb_tree_get_augment (tree, tmp);
+ if (position < aug->n_items)
+ {
+ node = tmp;
+ continue;
+ }
+ position -= aug->n_items;
+ filtered += aug->n_visible;
+ }
+
+ if (position == 0)
+ break;
+
+ position--;
+ if (node->visible)
+ filtered++;
+
+ node = gtk_rb_tree_node_get_right (node);
+ }
+
+ if (out_filtered)
+ *out_filtered = filtered;
+
+ return node;
+}
+
+static GType
+gtk_filter_list_model_get_item_type (GListModel *list)
+{
+ GtkFilterListModel *self = GTK_FILTER_LIST_MODEL (list);
+
+ return self->item_type;
+}
+
+static guint
+gtk_filter_list_model_get_n_items (GListModel *list)
+{
+ GtkFilterListModel *self = GTK_FILTER_LIST_MODEL (list);
+ FilterAugment *aug;
+ FilterNode *node;
+
+ if (self->model == NULL)
+ return 0;
+
+ if (!self->items)
+ return g_list_model_get_n_items (self->model);
+
+ node = gtk_rb_tree_get_root (self->items);
+ if (node == NULL)
+ return 0;
+
+ aug = gtk_rb_tree_get_augment (self->items, node);
+ return aug->n_visible;
+}
+
+static gpointer
+gtk_filter_list_model_get_item (GListModel *list,
+ guint position)
+{
+ GtkFilterListModel *self = GTK_FILTER_LIST_MODEL (list);
+ guint unfiltered;
+
+ if (self->model == NULL)
+ return NULL;
+
+ if (self->items)
+ gtk_filter_list_model_get_nth_filtered (self->items, position, &unfiltered);
+ else
+ unfiltered = position;
+
+ return g_list_model_get_item (self->model, unfiltered);
+}
+
+static void
+gtk_filter_list_model_model_init (GListModelInterface *iface)
+{
+ iface->get_item_type = gtk_filter_list_model_get_item_type;
+ iface->get_n_items = gtk_filter_list_model_get_n_items;
+ iface->get_item = gtk_filter_list_model_get_item;
+}
+
+G_DEFINE_TYPE_WITH_CODE (GtkFilterListModel, gtk_filter_list_model, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_filter_list_model_model_init))
+
+static gboolean
+gtk_filter_list_model_run_filter (GtkFilterListModel *self,
+ guint position)
+{
+ gpointer item;
+ gboolean visible;
+
+ item = g_list_model_get_item (self->model, position);
+ visible = self->filter_func (item, self->user_data);
+ g_object_unref (item);
+
+ return visible;
+}
+
+static guint
+gtk_filter_list_model_add_items (GtkFilterListModel *self,
+ FilterNode *after,
+ guint position,
+ guint n_items)
+{
+ FilterNode *node;
+ guint i, n_visible;
+
+ n_visible = 0;
+
+ for (i = 0; i < n_items; i++)
+ {
+ node = gtk_rb_tree_insert_before (self->items, after);
+ node->visible = gtk_filter_list_model_run_filter (self, position + i);
+ if (node->visible)
+ n_visible++;
+ }
+
+ return n_visible;
+}
+
+static void
+gtk_filter_list_model_items_changed_cb (GListModel *model,
+ guint position,
+ guint removed,
+ guint added,
+ GtkFilterListModel *self)
+{
+ FilterNode *node;
+ guint i, filter_position, filter_removed, filter_added;
+
+ if (self->items == NULL)
+ {
+ g_list_model_items_changed (G_LIST_MODEL (self), position, removed, added);
+ return;
+ }
+
+ node = gtk_filter_list_model_get_nth (self->items, position, &filter_position);
+
+ filter_removed = 0;
+ for (i = 0; i < removed; i++)
+ {
+ FilterNode *next = gtk_rb_tree_node_get_next (node);
+ if (node->visible)
+ filter_removed++;
+ gtk_rb_tree_remove (self->items, node);
+ node = next;
+ }
+
+ filter_added = gtk_filter_list_model_add_items (self, node, position, added);
+
+ if (filter_removed > 0 || filter_added > 0)
+ g_list_model_items_changed (G_LIST_MODEL (self), filter_position, filter_removed, filter_added);
+}
+
+static void
+gtk_filter_list_model_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkFilterListModel *self = GTK_FILTER_LIST_MODEL (object);
+
+ switch (prop_id)
+ {
+ case PROP_ITEM_TYPE:
+ self->item_type = g_value_get_gtype (value);
+ break;
+
+ case PROP_MODEL:
+ gtk_filter_list_model_set_model (self, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_filter_list_model_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkFilterListModel *self = GTK_FILTER_LIST_MODEL (object);
+
+ switch (prop_id)
+ {
+ case PROP_HAS_FILTER:
+ g_value_set_boolean (value, self->items != NULL);
+ break;
+
+ case PROP_ITEM_TYPE:
+ g_value_set_gtype (value, self->item_type);
+ break;
+
+ case PROP_MODEL:
+ g_value_set_object (value, self->model);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_filter_list_model_clear_model (GtkFilterListModel *self)
+{
+ if (self->model == NULL)
+ return;
+
+ g_signal_handlers_disconnect_by_func (self->model, gtk_filter_list_model_items_changed_cb, self);
+ g_clear_object (&self->model);
+ if (self->items)
+ gtk_rb_tree_remove_all (self->items);
+}
+
+static void
+gtk_filter_list_model_dispose (GObject *object)
+{
+ GtkFilterListModel *self = GTK_FILTER_LIST_MODEL (object);
+
+ gtk_filter_list_model_clear_model (self);
+ if (self->user_destroy)
+ self->user_destroy (self->user_data);
+ self->filter_func = NULL;
+ self->user_data = NULL;
+ self->user_destroy = NULL;
+ g_clear_pointer (&self->items, gtk_rb_tree_unref);
+
+ G_OBJECT_CLASS (gtk_filter_list_model_parent_class)->dispose (object);
+}
+
+static void
+gtk_filter_list_model_class_init (GtkFilterListModelClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+
+ gobject_class->set_property = gtk_filter_list_model_set_property;
+ gobject_class->get_property = gtk_filter_list_model_get_property;
+ gobject_class->dispose = gtk_filter_list_model_dispose;
+
+ /**
+ * GtkFilterListModel:has-filter:
+ *
+ * If a filter is set for this model
+ */
+ properties[PROP_HAS_FILTER] =
+ g_param_spec_boolean ("has-filter",
+ P_("has filter"),
+ P_("If a filter is set for this model"),
+ FALSE,
+ GTK_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * GtkFilterListModel:item-type:
+ *
+ * The #GType for elements of this object
+ */
+ properties[PROP_ITEM_TYPE] =
+ g_param_spec_gtype ("item-type",
+ P_("Item type"),
+ P_("The type of elements of this object"),
+ G_TYPE_OBJECT,
+ GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * GtkFilterListModel:model:
+ *
+ * The model being filtered
+ */
+ properties[PROP_MODEL] =
+ g_param_spec_object ("model",
+ P_("Model"),
+ P_("The model being filtered"),
+ G_TYPE_LIST_MODEL,
+ GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY);
+
+ g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
+}
+
+static void
+gtk_filter_list_model_init (GtkFilterListModel *self)
+{
+}
+
+
+static void
+gtk_filter_list_model_augment (GtkRbTree *filter,
+ gpointer _aug,
+ gpointer _node,
+ gpointer left,
+ gpointer right)
+{
+ FilterNode *node = _node;
+ FilterAugment *aug = _aug;
+
+ aug->n_items = 1;
+ aug->n_visible = node->visible ? 1 : 0;
+
+ if (left)
+ {
+ FilterAugment *left_aug = gtk_rb_tree_get_augment (filter, left);
+ aug->n_items += left_aug->n_items;
+ aug->n_visible += left_aug->n_visible;
+ }
+ if (right)
+ {
+ FilterAugment *right_aug = gtk_rb_tree_get_augment (filter, right);
+ aug->n_items += right_aug->n_items;
+ aug->n_visible += right_aug->n_visible;
+ }
+}
+
+/**
+ * gtk_filter_list_model_new:
+ * @model: the model to sort
+ * @filter_func: (allow-none): filter function or %NULL to not filter items
+ * @user_data: (closure): user data passed to @filter_func
+ * @user_destroy: destroy notifier for @user_data
+ *
+ * Creates a new #GtkFilterListModel that will filter @model using the given
+ * @filter_func.
+ *
+ * Returns: a new #GtkFilterListModel
+ **/
+GtkFilterListModel *
+gtk_filter_list_model_new (GListModel *model,
+ GtkFilterListModelFilterFunc filter_func,
+ gpointer user_data,
+ GDestroyNotify user_destroy)
+{
+ GtkFilterListModel *result;
+
+ g_return_val_if_fail (G_IS_LIST_MODEL (model), NULL);
+
+ result = g_object_new (GTK_TYPE_FILTER_LIST_MODEL,
+ "item-type", g_list_model_get_item_type (model),
+ "model", model,
+ NULL);
+
+ if (filter_func)
+ gtk_filter_list_model_set_filter_func (result, filter_func, user_data, user_destroy);
+
+ return result;
+}
+
+/**
+ * gtk_filter_list_model_new_for_type:
+ * @item_type: the type of the items that will be returned
+ *
+ * Creates a new empty filter list model set up to return items of type @item_type.
+ * It is up to the application to set a proper filter function and model to ensure
+ * the item type is matched.
+ *
+ * Returns: a new #GtkFilterListModel
+ **/
+GtkFilterListModel *
+gtk_filter_list_model_new_for_type (GType item_type)
+{
+ g_return_val_if_fail (g_type_is_a (item_type, G_TYPE_OBJECT), NULL);
+
+ return g_object_new (GTK_TYPE_FILTER_LIST_MODEL,
+ "item-type", item_type,
+ NULL);
+}
+
+/**
+ * gtk_filter_list_model_set_filter_func:
+ * @self: a #GtkFilterListModel
+ * @filter_func: (allow-none): filter function or %NULL to not filter items
+ * @user_data: (closure): user data passed to @filter_func
+ * @user_destroy: destroy notifier for @user_data
+ *
+ * Sets the function used to filter items. The function will be called for every
+ * item and if it returns %TRUE the item is considered visible.
+ **/
+void
+gtk_filter_list_model_set_filter_func (GtkFilterListModel *self,
+ GtkFilterListModelFilterFunc filter_func,
+ gpointer user_data,
+ GDestroyNotify user_destroy)
+{
+ gboolean was_filtered, will_be_filtered;
+
+ g_return_if_fail (GTK_IS_FILTER_LIST_MODEL (self));
+ g_return_if_fail (filter_func != NULL || (user_data == NULL && !user_destroy));
+
+ was_filtered = self->filter_func != NULL;
+ will_be_filtered = filter_func != NULL;
+
+ if (!was_filtered && !will_be_filtered)
+ return;
+
+ if (self->user_destroy)
+ self->user_destroy (self->user_data);
+
+ self->filter_func = filter_func;
+ self->user_data = user_data;
+ self->user_destroy = user_destroy;
+
+ if (!will_be_filtered)
+ {
+ g_clear_pointer (&self->items, gtk_rb_tree_unref);
+ }
+ else if (!was_filtered)
+ {
+ guint i, n_items;
+
+ self->items = gtk_rb_tree_new (FilterNode,
+ FilterAugment,
+ gtk_filter_list_model_augment,
+ NULL, NULL);
+ if (self->model)
+ {
+ n_items = g_list_model_get_n_items (self->model);
+ for (i = 0; i < n_items; i++)
+ {
+ FilterNode *node = gtk_rb_tree_insert_before (self->items, NULL);
+ node->visible = TRUE;
+ }
+ }
+ }
+
+ gtk_filter_list_model_refilter (self);
+
+ if (was_filtered != will_be_filtered)
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_HAS_FILTER]);
+}
+
+/**
+ * gtk_filter_list_model_set_model:
+ * @self: a #GtkFilterListModel
+ * @model: (allow-none): The model to be filtered
+ *
+ * Sets the model to be filtered.
+ *
+ * Note that GTK makes no effort to ensure that @model conforms to
+ * the item type of @self. It assumes that the caller knows what they
+ * are doing and have set up an appropriate filter function to ensure
+ * that item types match.
+ **/
+void
+gtk_filter_list_model_set_model (GtkFilterListModel *self,
+ GListModel *model)
+{
+ guint removed, added;
+
+ g_return_if_fail (GTK_IS_FILTER_LIST_MODEL (self));
+ g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
+ /* Note: We don't check for matching item type here, we just assume the
+ * filter func takes care of filtering wrong items. */
+
+ if (self->model == model)
+ return;
+
+ removed = g_list_model_get_n_items (G_LIST_MODEL (self));
+ gtk_filter_list_model_clear_model (self);
+
+ if (model)
+ {
+ self->model = g_object_ref (model);
+ g_signal_connect (model, "items-changed", G_CALLBACK (gtk_filter_list_model_items_changed_cb), self);
+ if (self->items)
+ added = gtk_filter_list_model_add_items (self, NULL, 0, g_list_model_get_n_items (model));
+ else
+ added = g_list_model_get_n_items (model);
+ }
+ else
+ added = 0;
+
+ if (removed > 0 || added > 0)
+ g_list_model_items_changed (G_LIST_MODEL (self), 0, removed, added);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
+}
+
+/**
+ * gtk_filter_list_model_get_model:
+ * @self: a #GtkFilterListModel
+ *
+ * Gets the model currently filtered or %NULL if none.
+ *
+ * Returns: (nullable) (transfer none): The model that gets filtered
+ **/
+GListModel *
+gtk_filter_list_model_get_model (GtkFilterListModel *self)
+{
+ g_return_val_if_fail (GTK_IS_FILTER_LIST_MODEL (self), NULL);
+
+ return self->model;
+}
+
+/**
+ * gtk_filter_list_model_has_filter:
+ * @self: a #GtkFilterListModel
+ *
+ * Checks if a filter function is currently set on @self
+ *
+ * Returns: %TRUE if a filter function is set
+ **/
+gboolean
+gtk_filter_list_model_has_filter (GtkFilterListModel *self)
+{
+ g_return_val_if_fail (GTK_IS_FILTER_LIST_MODEL (self), FALSE);
+
+ return self->filter_func != NULL;
+}
+
+/**
+ * gtk_filter_list_model_refilter:
+ * @self: a #GtkFilterListModel
+ *
+ * Causes @self to refilter all items in the model.
+ *
+ * Calling this function is necessary when data used by the filter
+ * function has changed.
+ **/
+void
+gtk_filter_list_model_refilter (GtkFilterListModel *self)
+{
+ FilterNode *node;
+ guint i, first_change, last_change;
+ guint n_is_visible, n_was_visible;
+ gboolean visible;
+
+ g_return_if_fail (GTK_IS_FILTER_LIST_MODEL (self));
+
+ if (self->items == NULL || self->model == NULL)
+ return;
+
+ first_change = G_MAXUINT;
+ last_change = 0;
+ n_is_visible = 0;
+ n_was_visible = 0;
+ for (i = 0, node = gtk_rb_tree_get_first (self->items);
+ node != NULL;
+ i++, node = gtk_rb_tree_node_get_next (node))
+ {
+ visible = gtk_filter_list_model_run_filter (self, i);
+ if (visible == node->visible)
+ {
+ if (visible)
+ {
+ n_is_visible++;
+ n_was_visible++;
+ }
+ continue;
+ }
+
+ node->visible = visible;
+ gtk_rb_tree_node_mark_dirty (node);
+ first_change = MIN (n_is_visible, first_change);
+ if (visible)
+ n_is_visible++;
+ else
+ n_was_visible++;
+ last_change = MAX (n_is_visible, last_change);
+ }
+
+ if (first_change <= last_change)
+ {
+ g_list_model_items_changed (G_LIST_MODEL (self),
+ first_change,
+ last_change - first_change + n_was_visible - n_is_visible,
+ last_change - first_change);
+ }
+}
+
diff --git a/subprojects/gfm/gtkfilterlistmodel.h b/subprojects/gfm/gtkfilterlistmodel.h
new file mode 100644
index 00000000..d16bd2fc
--- /dev/null
+++ b/subprojects/gfm/gtkfilterlistmodel.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright © 2018 Benjamin Otte
+ *
+ * This library 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.1 of the License, or (at your option) any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte@gnome.org>
+ */
+
+#ifndef __GTK_FILTER_LIST_MODEL_H__
+#define __GTK_FILTER_LIST_MODEL_H__
+
+
+/* #if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+ * #error "Only <gtk/gtk.h> can be included directly."
+ * #endif
+ */
+
+#include <gio/gio.h>
+#include <gtk/gtkwidget.h>
+
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_FILTER_LIST_MODEL (gtk_filter_list_model_get_type ())
+#define GTK_PARAM_READABLE G_PARAM_READABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
+#define GTK_PARAM_READWRITE G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
+
+GDK_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (GtkFilterListModel, gtk_filter_list_model, GTK, FILTER_LIST_MODEL, GObject)
+
+/**
+ * GtkFilterListModelFilterFunc:
+ * @item: (type GObject): The item that may be filtered
+ * @user_data: user data
+ *
+ * User function that is called to determine if the @item of the original model should be visible.
+ * If it should be visible, this function must return %TRUE. If the model should filter out the
+ * @item, %FALSE must be returned.
+ *
+ * Returns: %TRUE to keep the item around
+ */
+typedef gboolean (* GtkFilterListModelFilterFunc) (gpointer item, gpointer user_data);
+
+GDK_AVAILABLE_IN_ALL
+GtkFilterListModel * gtk_filter_list_model_new (GListModel *model,
+ GtkFilterListModelFilterFunc filter_func,
+ gpointer user_data,
+ GDestroyNotify user_destroy);
+GDK_AVAILABLE_IN_ALL
+GtkFilterListModel * gtk_filter_list_model_new_for_type (GType item_type);
+
+GDK_AVAILABLE_IN_ALL
+void gtk_filter_list_model_set_filter_func (GtkFilterListModel *self,
+ GtkFilterListModelFilterFunc filter_func,
+ gpointer user_data,
+ GDestroyNotify user_destroy);
+GDK_AVAILABLE_IN_ALL
+void gtk_filter_list_model_set_model (GtkFilterListModel *self,
+ GListModel *model);
+GDK_AVAILABLE_IN_ALL
+GListModel * gtk_filter_list_model_get_model (GtkFilterListModel *self);
+GDK_AVAILABLE_IN_ALL
+gboolean gtk_filter_list_model_has_filter (GtkFilterListModel *self);
+
+GDK_AVAILABLE_IN_ALL
+void gtk_filter_list_model_refilter (GtkFilterListModel *self);
+
+G_END_DECLS
+
+#endif /* __GTK_FILTER_LIST_MODEL_H__ */
diff --git a/subprojects/gfm/gtkflattenlistmodel.c b/subprojects/gfm/gtkflattenlistmodel.c
new file mode 100644
index 00000000..0027f023
--- /dev/null
+++ b/subprojects/gfm/gtkflattenlistmodel.c
@@ -0,0 +1,541 @@
+/*
+ * Copyright © 2018 Benjamin Otte
+ *
+ * This library 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.1 of the License, or (at your option) any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte@gnome.org>
+ */
+
+// #include "config.h"
+
+#include "gtkflattenlistmodel.h"
+
+#include "gtkrbtreeprivate.h"
+#include "gtkintl.h"
+// #include "gtkprivate.h"
+
+/**
+ * SECTION:gtkflattenlistmodel
+ * @title: GtkFlattenListModel
+ * @short_description: A list model that flattens a list of lists
+ * @see_also: #GListModel
+ *
+ * #GtkFlattenListModel is a list model that takes a list model containing
+ * list models and flattens it into a single model.
+ *
+ * Another term for this is concatenation: #GtkFlattenListModel takes a
+ * list of lists and concatenates them into a single list.
+ */
+
+enum {
+ PROP_0,
+ PROP_ITEM_TYPE,
+ PROP_MODEL,
+ NUM_PROPERTIES
+};
+
+typedef struct _FlattenNode FlattenNode;
+typedef struct _FlattenAugment FlattenAugment;
+
+struct _FlattenNode
+{
+ GListModel *model;
+ GtkFlattenListModel *list;
+};
+
+struct _FlattenAugment
+{
+ guint n_items;
+ guint n_models;
+};
+
+struct _GtkFlattenListModel
+{
+ GObject parent_instance;
+
+ GType item_type;
+ GListModel *model;
+ GtkRbTree *items; /* NULL if model == NULL */
+};
+
+struct _GtkFlattenListModelClass
+{
+ GObjectClass parent_class;
+};
+
+static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
+
+static FlattenNode *
+gtk_flatten_list_model_get_nth (GtkRbTree *tree,
+ guint position,
+ guint *model_position)
+{
+ FlattenNode *node, *tmp;
+ guint model_n_items;
+
+ node = gtk_rb_tree_get_root (tree);
+
+ while (node)
+ {
+ tmp = gtk_rb_tree_node_get_left (node);
+ if (tmp)
+ {
+ FlattenAugment *aug = gtk_rb_tree_get_augment (tree, tmp);
+ if (position < aug->n_items)
+ {
+ node = tmp;
+ continue;
+ }
+ position -= aug->n_items;
+ }
+
+ model_n_items = g_list_model_get_n_items (node->model);
+ if (position < model_n_items)
+ break;
+ position -= model_n_items;
+
+ node = gtk_rb_tree_node_get_right (node);
+ }
+
+ if (model_position)
+ *model_position = node ? position : 0;
+
+ return node;
+}
+
+static FlattenNode *
+gtk_flatten_list_model_get_nth_model (GtkRbTree *tree,
+ guint position,
+ guint *items_before)
+{
+ FlattenNode *node, *tmp;
+ guint before;
+
+ node = gtk_rb_tree_get_root (tree);
+ before = 0;
+
+ while (node)
+ {
+ tmp = gtk_rb_tree_node_get_left (node);
+ if (tmp)
+ {
+ FlattenAugment *aug = gtk_rb_tree_get_augment (tree, tmp);
+ if (position < aug->n_models)
+ {
+ node = tmp;
+ continue;
+ }
+ position -= aug->n_models;
+ before += aug->n_items;
+ }
+
+ if (position == 0)
+ break;
+ position--;
+ before += g_list_model_get_n_items (node->model);
+
+ node = gtk_rb_tree_node_get_right (node);
+ }
+
+ if (items_before)
+ *items_before = before;
+
+ return node;
+}
+
+static GType
+gtk_flatten_list_model_get_item_type (GListModel *list)
+{
+ GtkFlattenListModel *self = GTK_FLATTEN_LIST_MODEL (list);
+
+ return self->item_type;
+}
+
+static guint
+gtk_flatten_list_model_get_n_items (GListModel *list)
+{
+ GtkFlattenListModel *self = GTK_FLATTEN_LIST_MODEL (list);
+ FlattenAugment *aug;
+ FlattenNode *node;
+
+ if (!self->items)
+ return 0;
+
+ node = gtk_rb_tree_get_root (self->items);
+ if (node == NULL)
+ return 0;
+
+ aug = gtk_rb_tree_get_augment (self->items, node);
+ return aug->n_items;
+}
+
+static gpointer
+gtk_flatten_list_model_get_item (GListModel *list,
+ guint position)
+{
+ GtkFlattenListModel *self = GTK_FLATTEN_LIST_MODEL (list);
+ FlattenNode *node;
+ guint model_pos;
+
+ if (!self->items)
+ return NULL;
+
+ node = gtk_flatten_list_model_get_nth (self->items, position, &model_pos);
+ if (node == NULL)
+ return NULL;
+
+ return g_list_model_get_item (node->model, model_pos);
+}
+
+static void
+gtk_flatten_list_model_model_init (GListModelInterface *iface)
+{
+ iface->get_item_type = gtk_flatten_list_model_get_item_type;
+ iface->get_n_items = gtk_flatten_list_model_get_n_items;
+ iface->get_item = gtk_flatten_list_model_get_item;
+}
+
+G_DEFINE_TYPE_WITH_CODE (GtkFlattenListModel, gtk_flatten_list_model, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_flatten_list_model_model_init))
+
+static void
+gtk_flatten_list_model_items_changed_cb (GListModel *model,
+ guint position,
+ guint removed,
+ guint added,
+ gpointer _node)
+{
+ FlattenNode *node = _node, *parent, *left;
+ GtkFlattenListModel *self = node->list;
+ guint real_position;
+
+ gtk_rb_tree_node_mark_dirty (node);
+ real_position = position;
+
+ left = gtk_rb_tree_node_get_left (node);
+ if (left)
+ {
+ FlattenAugment *aug = gtk_rb_tree_get_augment (self->items, left);
+ real_position += aug->n_items;
+ }
+
+ for (;
+ (parent = gtk_rb_tree_node_get_parent (node)) != NULL;
+ node = parent)
+ {
+ left = gtk_rb_tree_node_get_left (parent);
+ if (left != node)
+ {
+ if (left)
+ {
+ FlattenAugment *aug = gtk_rb_tree_get_augment (self->items, left);
+ real_position += aug->n_items;
+ }
+ real_position += g_list_model_get_n_items (parent->model);
+ }
+ }
+
+ g_list_model_items_changed (G_LIST_MODEL (self), real_position, removed, added);
+}
+
+static void
+gtk_flatten_list_model_clear_node (gpointer _node)
+{
+ FlattenNode *node= _node;
+
+ g_signal_handlers_disconnect_by_func (node->model, gtk_flatten_list_model_items_changed_cb, node);
+ g_object_unref (node->model);
+}
+
+static void
+gtk_flatten_list_model_augment (GtkRbTree *flatten,
+ gpointer _aug,
+ gpointer _node,
+ gpointer left,
+ gpointer right)
+{
+ FlattenNode *node = _node;
+ FlattenAugment *aug = _aug;
+
+ aug->n_items = g_list_model_get_n_items (node->model);
+ aug->n_models = 1;
+
+ if (left)
+ {
+ FlattenAugment *left_aug = gtk_rb_tree_get_augment (flatten, left);
+ aug->n_items += left_aug->n_items;
+ aug->n_models += left_aug->n_models;
+ }
+ if (right)
+ {
+ FlattenAugment *right_aug = gtk_rb_tree_get_augment (flatten, right);
+ aug->n_items += right_aug->n_items;
+ aug->n_models += right_aug->n_models;
+ }
+}
+
+static guint
+gtk_flatten_list_model_add_items (GtkFlattenListModel *self,
+ FlattenNode *after,
+ guint position,
+ guint n)
+{
+ FlattenNode *node;
+ guint added, i;
+
+ added = 0;
+ for (i = 0; i < n; i++)
+ {
+ node = gtk_rb_tree_insert_before (self->items, after);
+ node->model = g_list_model_get_item (self->model, position + i);
+ g_warn_if_fail (g_type_is_a (g_list_model_get_item_type (node->model), self->item_type));
+ g_signal_connect (node->model,
+ "items-changed",
+ G_CALLBACK (gtk_flatten_list_model_items_changed_cb),
+ node);
+ node->list = self;
+ added +=g_list_model_get_n_items (node->model);
+ }
+
+ return added;
+}
+
+static void
+gtk_flatten_list_model_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkFlattenListModel *self = GTK_FLATTEN_LIST_MODEL (object);
+
+ switch (prop_id)
+ {
+ case PROP_ITEM_TYPE:
+ self->item_type = g_value_get_gtype (value);
+ break;
+
+ case PROP_MODEL:
+ gtk_flatten_list_model_set_model (self, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_flatten_list_model_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkFlattenListModel *self = GTK_FLATTEN_LIST_MODEL (object);
+
+ switch (prop_id)
+ {
+ case PROP_ITEM_TYPE:
+ g_value_set_gtype (value, self->item_type);
+ break;
+
+ case PROP_MODEL:
+ g_value_set_object (value, self->model);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_flatten_list_model_model_items_changed_cb (GListModel *model,
+ guint position,
+ guint removed,
+ guint added,
+ GtkFlattenListModel *self)
+{
+ FlattenNode *node;
+ guint i, real_position, real_removed, real_added;
+
+ node = gtk_flatten_list_model_get_nth_model (self->items, position, &real_position);
+
+ real_removed = 0;
+ for (i = 0; i < removed; i++)
+ {
+ FlattenNode *next = gtk_rb_tree_node_get_next (node);
+ real_removed += g_list_model_get_n_items (node->model);
+ gtk_rb_tree_remove (self->items, node);
+ node = next;
+ }
+
+ real_added = gtk_flatten_list_model_add_items (self, node, position, added);
+
+ if (real_removed > 0 || real_added > 0)
+ g_list_model_items_changed (G_LIST_MODEL (self), real_position, real_removed, real_added);
+}
+
+static void
+gtk_flatten_list_clear_model (GtkFlattenListModel *self)
+{
+ if (self->model)
+ {
+ g_signal_handlers_disconnect_by_func (self->model, gtk_flatten_list_model_model_items_changed_cb, self);
+ g_clear_object (&self->model);
+ g_clear_pointer (&self->items, gtk_rb_tree_unref);
+ }
+}
+
+static void
+gtk_flatten_list_model_dispose (GObject *object)
+{
+ GtkFlattenListModel *self = GTK_FLATTEN_LIST_MODEL (object);
+
+ gtk_flatten_list_clear_model (self);
+
+ G_OBJECT_CLASS (gtk_flatten_list_model_parent_class)->dispose (object);
+}
+
+static void
+gtk_flatten_list_model_class_init (GtkFlattenListModelClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+
+ gobject_class->set_property = gtk_flatten_list_model_set_property;
+ gobject_class->get_property = gtk_flatten_list_model_get_property;
+ gobject_class->dispose = gtk_flatten_list_model_dispose;
+
+ /**
+ * GtkFlattenListModel:item-type:
+ *
+ * The #GTpe for elements of this object
+ */
+ properties[PROP_ITEM_TYPE] =
+ g_param_spec_gtype ("item-type",
+ P_("Item type"),
+ P_("The type of elements of this object"),
+ G_TYPE_OBJECT,
+ GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * GtkFlattenListModel:model:
+ *
+ * The model being flattened
+ */
+ properties[PROP_MODEL] =
+ g_param_spec_object ("model",
+ P_("Model"),
+ P_("The model being flattened"),
+ G_TYPE_LIST_MODEL,
+ GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
+}
+
+static void
+gtk_flatten_list_model_init (GtkFlattenListModel *self)
+{
+}
+
+/**
+ * gtk_flatten_list_model_new:
+ * @item_type: The type of items in the to-be-flattened models
+ * @model: (nullable) (transfer none): the item to be flattened
+ *
+ * Creates a new #GtkFlattenListModel that flattens @list. The
+ * models returned by @model must conform to the given @item_type,
+ * either by having an identical type or a subtype.
+ *
+ * Returns: a new #GtkFlattenListModel
+ **/
+GtkFlattenListModel *
+gtk_flatten_list_model_new (GType item_type,
+ GListModel *model)
+{
+ GtkFlattenListModel *result;
+
+ g_return_val_if_fail (g_type_is_a (item_type, G_TYPE_OBJECT), NULL);
+ g_return_val_if_fail (model == NULL || G_IS_LIST_MODEL (model), NULL);
+
+ result = g_object_new (GTK_TYPE_FLATTEN_LIST_MODEL,
+ "item-type", item_type,
+ "model", model,
+ NULL);
+
+ return result;
+}
+
+/**
+ * gtk_flatten_list_model_set_model:
+ * @self: a #GtkFlattenListModel
+ * @model: (nullable) (transfer none): the new model or %NULL
+ *
+ * Sets a new model to be flattened. The model must contain items of
+ * #GtkListModel that conform to the item type of @self.
+ **/
+void
+gtk_flatten_list_model_set_model (GtkFlattenListModel *self,
+ GListModel *model)
+{
+ guint removed, added = 0;
+
+ g_return_if_fail (GTK_IS_FLATTEN_LIST_MODEL (self));
+ g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
+ if (model)
+ {
+ g_return_if_fail (g_type_is_a (g_list_model_get_item_type (model), G_TYPE_LIST_MODEL));
+ }
+
+ if (self->model == model)
+ return;
+
+ removed = g_list_model_get_n_items (G_LIST_MODEL (self));
+ gtk_flatten_list_clear_model (self);
+
+ self->model = model;
+
+ if (model)
+ {
+ g_object_ref (model);
+ g_signal_connect (model, "items-changed", G_CALLBACK (gtk_flatten_list_model_model_items_changed_cb), self);
+ self->items = gtk_rb_tree_new (FlattenNode,
+ FlattenAugment,
+ gtk_flatten_list_model_augment,
+ gtk_flatten_list_model_clear_node,
+ NULL);
+
+ added = gtk_flatten_list_model_add_items (self, NULL, 0, g_list_model_get_n_items (model));
+ }
+
+ if (removed > 0 || added > 0)
+ g_list_model_items_changed (G_LIST_MODEL (self), 0, removed, added);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
+}
+
+/**
+ * gtk_flatten_list_model_get_model:
+ * @self: a #GtkFlattenListModel
+ *
+ * Gets the model set via gtk_flatten_list_model_set_model().
+ *
+ * Returns: (nullable) (transfer none): The model flattened by @self
+ **/
+GListModel *
+gtk_flatten_list_model_get_model (GtkFlattenListModel *self)
+{
+ g_return_val_if_fail (GTK_IS_FLATTEN_LIST_MODEL (self), NULL);
+
+ return self->model;
+}
diff --git a/subprojects/gfm/gtkflattenlistmodel.h b/subprojects/gfm/gtkflattenlistmodel.h
new file mode 100644
index 00000000..dec783f4
--- /dev/null
+++ b/subprojects/gfm/gtkflattenlistmodel.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright © 2018 Benjamin Otte
+ *
+ * This library 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.1 of the License, or (at your option) any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte@gnome.org>
+ */
+
+#ifndef __GTK_FLATTEN_LIST_MODEL_H__
+#define __GTK_FLATTEN_LIST_MODEL_H__
+
+
+/* #if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+ * #error "Only <gtk/gtk.h> can be included directly."
+ * #endif
+ */
+
+#include <gdk/gdk.h>
+
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_FLATTEN_LIST_MODEL (gtk_flatten_list_model_get_type ())
+#define GTK_PARAM_READWRITE G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
+
+GDK_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (GtkFlattenListModel, gtk_flatten_list_model, GTK, FLATTEN_LIST_MODEL, GObject)
+
+GDK_AVAILABLE_IN_ALL
+GtkFlattenListModel * gtk_flatten_list_model_new (GType item_type,
+ GListModel *model);
+
+GDK_AVAILABLE_IN_ALL
+void gtk_flatten_list_model_set_model (GtkFlattenListModel *self,
+ GListModel *model);
+GDK_AVAILABLE_IN_ALL
+GListModel * gtk_flatten_list_model_get_model (GtkFlattenListModel *self);
+
+G_END_DECLS
+
+#endif /* __GTK_FLATTEN_LIST_MODEL_H__ */
diff --git a/subprojects/gfm/gtkintl.h b/subprojects/gfm/gtkintl.h
new file mode 100644
index 00000000..caeb68eb
--- /dev/null
+++ b/subprojects/gfm/gtkintl.h
@@ -0,0 +1,15 @@
+#ifndef __GTKINTL_H__
+#define __GTKINTL_H__
+
+#include <glib/gi18n-lib.h>
+
+#ifdef ENABLE_NLS
+#define P_(String) g_dgettext(GETTEXT_PACKAGE "-properties",String)
+#else
+#define P_(String) (String)
+#endif
+
+/* not really I18N-related, but also a string marker macro */
+#define I_(string) g_intern_static_string (string)
+
+#endif
diff --git a/subprojects/gfm/gtkrbtree.c b/subprojects/gfm/gtkrbtree.c
new file mode 100644
index 00000000..8d706467
--- /dev/null
+++ b/subprojects/gfm/gtkrbtree.c
@@ -0,0 +1,800 @@
+/* gtkrbtree.c
+ * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+// #include "config.h"
+
+#include "gtkrbtreeprivate.h"
+
+// #include "gtkdebug.h"
+
+/* Define the following to print adds and removals to stdout.
+ * The format of the printout will be suitable for addition as a new test to
+ * testsuite/gtk/rbtree-crash.c
+ * by just grepping the printouts from the relevant rbtree.
+ *
+ * This is meant to be a trivial way to add rbtree tests to the testsuite.
+ */
+#undef DUMP_MODIFICATION
+
+typedef struct _GtkRbNode GtkRbNode;
+
+struct _GtkRbTree
+{
+ guint ref_count;
+
+ gsize element_size;
+ gsize augment_size;
+ GtkRbTreeAugmentFunc augment_func;
+ GDestroyNotify clear_func;
+ GDestroyNotify clear_augment_func;
+
+ GtkRbNode *root;
+};
+
+struct _GtkRbNode
+{
+ guint red :1;
+ guint dirty :1;
+
+ GtkRbNode *left;
+ GtkRbNode *right;
+ /* The difference between tree and parent here is that we OR the tree with 1 and because
+ * pointers are always multiples of 4, we can know if we've stored a parent or the tree here */
+ union {
+ gpointer parent_or_tree;
+ GtkRbNode *parent;
+ GtkRbTree *tree;
+ };
+};
+
+#define NODE_FROM_POINTER(ptr) ((GtkRbNode *) ((ptr) ? (((guchar *) (ptr)) - sizeof (GtkRbNode)) : NULL))
+#define NODE_TO_POINTER(node) ((gpointer) ((node) ? (((guchar *) (node)) + sizeof (GtkRbNode)) : NULL))
+#define NODE_TO_AUG_POINTER(tree, node) ((gpointer) ((node) ? (((guchar *) (node)) + sizeof (GtkRbNode) + (tree)->element_size) : NULL))
+
+static inline gboolean
+is_root (GtkRbNode *node)
+{
+ return GPOINTER_TO_SIZE (node->parent_or_tree) & 1 ? TRUE : FALSE;
+}
+
+static inline GtkRbNode *
+parent (GtkRbNode *node)
+{
+ if (is_root (node))
+ return NULL;
+ else
+ return node->parent;
+}
+
+static GtkRbTree *
+tree (GtkRbNode *node)
+{
+ while (!is_root (node))
+ node = parent (node);
+
+ return GSIZE_TO_POINTER (GPOINTER_TO_SIZE (node->tree) & ~1);
+}
+
+static void
+set_parent (GtkRbTree *tree,
+ GtkRbNode *node,
+ GtkRbNode *new_parent)
+{
+
+ if (new_parent != NULL)
+ {
+ node->parent = new_parent;
+ }
+ else
+ {
+ node->tree = GSIZE_TO_POINTER (GPOINTER_TO_SIZE (tree) | 1);
+ tree->root = node;
+ }
+}
+
+static inline gsize
+gtk_rb_node_get_size (GtkRbTree *tree)
+{
+ return sizeof (GtkRbNode) + tree->element_size + tree->augment_size;
+}
+
+static GtkRbNode *
+gtk_rb_node_new (GtkRbTree *tree)
+{
+ GtkRbNode *result;
+
+ result = g_slice_alloc0 (gtk_rb_node_get_size (tree));
+
+ result->red = TRUE;
+ result->dirty = TRUE;
+
+ return result;
+}
+
+static void
+gtk_rb_node_free (GtkRbTree *tree,
+ GtkRbNode *node)
+{
+ if (tree->clear_func)
+ tree->clear_func (NODE_TO_POINTER (node));
+ if (tree->clear_augment_func)
+ tree->clear_augment_func (NODE_TO_AUG_POINTER (tree, node));
+
+ g_slice_free1 (gtk_rb_node_get_size (tree), node);
+}
+
+static void
+gtk_rb_node_free_deep (GtkRbTree *tree,
+ GtkRbNode *node)
+{
+ GtkRbNode *right = node->right;
+
+ if (node->left)
+ gtk_rb_node_free_deep (tree, node->left);
+
+ gtk_rb_node_free (tree, node);
+
+ if (right)
+ gtk_rb_node_free_deep (tree, right);
+}
+
+static void
+gtk_rb_node_mark_dirty (GtkRbNode *node,
+ gboolean mark_parent)
+{
+ if (node->dirty)
+ return;
+
+ node->dirty = TRUE;
+
+ if (mark_parent && parent (node))
+ gtk_rb_node_mark_dirty (parent (node), TRUE);
+}
+
+static void
+gtk_rb_node_clean (GtkRbTree *tree,
+ GtkRbNode *node)
+{
+ if (!node->dirty)
+ return;
+
+ node->dirty = FALSE;
+ if (tree->augment_func)
+ tree->augment_func (tree,
+ NODE_TO_AUG_POINTER (tree, node),
+ NODE_TO_POINTER (node),
+ NODE_TO_POINTER (node->left),
+ NODE_TO_POINTER (node->right));
+}
+
+static GtkRbNode *
+gtk_rb_node_get_first (GtkRbNode *node)
+{
+ while (node->left)
+ node = node->left;
+
+ return node;
+}
+
+static GtkRbNode *
+gtk_rb_node_get_last (GtkRbNode *node)
+{
+ while (node->right)
+ node = node->right;
+
+ return node;
+}
+
+static GtkRbNode *
+gtk_rb_node_get_previous (GtkRbNode *node)
+{
+ GtkRbNode *p;
+
+ if (node->left)
+ return gtk_rb_node_get_last (node->left);
+
+ for (p = parent (node); p != NULL; p = parent (node))
+ {
+ if (p->right == node)
+ return p;
+
+ node = p;
+ }
+
+ return NULL;
+}
+
+static GtkRbNode *
+gtk_rb_node_get_next (GtkRbNode *node)
+{
+ GtkRbNode *p;
+
+ if (node->right)
+ return gtk_rb_node_get_first (node->right);
+
+ for (p = parent (node); p != NULL; p = parent (node))
+ {
+ if (p->left == node)
+ return p;
+
+ node = p;
+ }
+
+ return NULL;
+}
+
+#ifdef DUMP_MODIFICATION
+static guint
+position (GtkRbTree *tree,
+ GtkRbNode *node)
+{
+ GtkRbNode *n;
+ guint i;
+
+ i = 0;
+ for (n = gtk_rb_node_get_first (tree->root);
+ n != node;
+ n = gtk_rb_node_get_next (n))
+ i++;
+
+ return i;
+}
+#endif
+
+static void
+gtk_rb_node_rotate_left (GtkRbTree *tree,
+ GtkRbNode *node)
+{
+ GtkRbNode *right, *p;
+
+ right = node->right;
+ p = parent (node);
+
+ node->right = right->left;
+ if (right->left)
+ set_parent (tree, right->left, node);
+
+ set_parent (tree, right, p);
+ if (p)
+ {
+ if (node == p->left)
+ p->left = right;
+ else
+ p->right = right;
+ }
+
+ right->left = node;
+ set_parent (tree, node, right);
+
+ gtk_rb_node_mark_dirty (node, FALSE);
+ gtk_rb_node_mark_dirty (right, FALSE);
+}
+
+static void
+gtk_rb_node_rotate_right (GtkRbTree *tree,
+ GtkRbNode *node)
+{
+ GtkRbNode *left, *p;
+
+ left = node->left;
+ p = parent (node);
+
+ node->left = left->right;
+ if (left->right)
+ set_parent (tree, left->right, node);
+
+ set_parent (tree, left, p);
+ if (p)
+ {
+ if (node == p->right)
+ p->right = left;
+ else
+ p->left = left;
+ }
+
+ /* link node and left */
+ left->right = node;
+ set_parent (tree, node, left);
+
+ gtk_rb_node_mark_dirty (node, FALSE);
+ gtk_rb_node_mark_dirty (left, FALSE);
+}
+
+static gboolean
+is_red (GtkRbNode *node_or_null)
+{
+ if (node_or_null == NULL)
+ return FALSE;
+ else
+ return node_or_null->red;
+}
+
+static inline gboolean
+is_black (GtkRbNode *node_or_null)
+{
+ return !is_red (node_or_null);
+}
+
+static void
+set_black (GtkRbNode *node_or_null)
+{
+ if (node_or_null == NULL)
+ return;
+
+ node_or_null->red = FALSE;
+}
+
+static void
+set_red (GtkRbNode *node_or_null)
+{
+ if (node_or_null == NULL)
+ return;
+
+ node_or_null->red = TRUE;
+}
+
+static void
+gtk_rb_tree_insert_fixup (GtkRbTree *tree,
+ GtkRbNode *node)
+{
+ GtkRbNode *p;
+
+ /* check Red-Black properties */
+ for (p = parent (node);
+ p && is_red (p);
+ p = parent (node))
+ {
+ GtkRbNode *pp = parent (p);
+
+ /* we have a violation */
+ g_assert (pp);
+
+ if (p == pp->left)
+ {
+ GtkRbNode *uncle = pp->right;
+
+ if (is_red (uncle))
+ {
+ /* uncle is red */
+ set_black (p);
+ set_black (uncle);
+ set_red (pp);
+ node = pp;
+ }
+ else
+ {
+ /* uncle is black */
+ if (node == p->right)
+ {
+ /* make node a left child */
+ node = p;
+ gtk_rb_node_rotate_left (tree, node);
+ p = parent (node);
+ pp = parent (p);
+ }
+ /* recolor and rotate */
+ set_black (p);
+ set_red (pp);
+ gtk_rb_node_rotate_right (tree, pp);
+ }
+ }
+ else
+ {
+ /* mirror image of above code */
+ GtkRbNode *uncle = pp->left;
+
+ if (is_red (uncle))
+ {
+ /* uncle is red */
+ set_black (p);
+ set_black (uncle);
+ set_red (pp);
+ node = pp;
+ }
+ else
+ {
+ /* uncle is black */
+ if (node == p->left)
+ {
+ node = p;
+ gtk_rb_node_rotate_right (tree, node);
+ p = parent (node);
+ pp = parent (p);
+ }
+ set_black (p);
+ set_red (pp);
+ gtk_rb_node_rotate_left (tree, pp);
+ }
+ }
+ }
+
+ set_black (tree->root);
+}
+
+static void
+gtk_rb_tree_remove_node_fixup (GtkRbTree *tree,
+ GtkRbNode *node,
+ GtkRbNode *p)
+{
+ while (node != tree->root && is_black (node))
+ {
+ if (node == p->left)
+ {
+ GtkRbNode *w = p->right;
+
+ if (is_red (w))
+ {
+ set_black (w);
+ set_red (p);
+ gtk_rb_node_rotate_left (tree, p);
+ w = p->right;
+ }
+ if (is_black (w->left) && is_black (w->right))
+ {
+ set_red (w);
+ node = p;
+ }
+ else
+ {
+ if (is_black (w->right))
+ {
+ set_black (w->left);
+ set_red (w);
+ gtk_rb_node_rotate_right (tree, w);
+ w = p->right;
+ }
+ w->red = p->red;
+ set_black (p);
+ set_black (w->right);
+ gtk_rb_node_rotate_left (tree, p);
+ node = tree->root;
+ }
+ }
+ else
+ {
+ GtkRbNode *w = p->left;
+ if (is_red (w))
+ {
+ set_black (w);
+ set_red (p);
+ gtk_rb_node_rotate_right (tree, p);
+ w = p->left;
+ }
+ if (is_black (w->right) && is_black (w->left))
+ {
+ set_red (w);
+ node = p;
+ }
+ else
+ {
+ if (is_black (w->left))
+ {
+ set_black (w->right);
+ set_red (w);
+ gtk_rb_node_rotate_left (tree, w);
+ w = p->left;
+ }
+ w->red = p->red;
+ set_black (p);
+ set_black (w->left);
+ gtk_rb_node_rotate_right (tree, p);
+ node = tree->root;
+ }
+ }
+
+ p = parent (node);
+ }
+
+ set_black (node);
+}
+
+GtkRbTree *
+gtk_rb_tree_new_for_size (gsize element_size,
+ gsize augment_size,
+ GtkRbTreeAugmentFunc augment_func,
+ GDestroyNotify clear_func,
+ GDestroyNotify clear_augment_func)
+{
+ GtkRbTree *tree;
+
+ tree = g_slice_new0 (GtkRbTree);
+ tree->ref_count = 1;
+
+ tree->element_size = element_size;
+ tree->augment_size = augment_size;
+ tree->augment_func = augment_func;
+ tree->clear_func = clear_func;
+ tree->clear_augment_func = clear_augment_func;
+
+ return tree;
+}
+
+GtkRbTree *
+gtk_rb_tree_ref (GtkRbTree *tree)
+{
+ tree->ref_count++;
+
+ return tree;
+}
+
+void
+gtk_rb_tree_unref (GtkRbTree *tree)
+{
+ tree->ref_count--;
+ if (tree->ref_count > 0)
+ return;
+
+ if (tree->root)
+ gtk_rb_node_free_deep (tree, tree->root);
+
+ g_slice_free (GtkRbTree, tree);
+}
+
+gpointer
+gtk_rb_tree_get_first (GtkRbTree *tree)
+{
+ if (tree->root == NULL)
+ return NULL;
+
+ return NODE_TO_POINTER (gtk_rb_node_get_first (tree->root));
+}
+
+gpointer
+gtk_rb_tree_get_last (GtkRbTree *tree)
+{
+ if (tree->root == NULL)
+ return NULL;
+
+ return NODE_TO_POINTER (gtk_rb_node_get_last (tree->root));
+}
+
+gpointer
+gtk_rb_tree_node_get_previous (gpointer node)
+{
+ return NODE_TO_POINTER (gtk_rb_node_get_previous (NODE_FROM_POINTER (node)));
+}
+
+gpointer
+gtk_rb_tree_node_get_next (gpointer node)
+{
+ return NODE_TO_POINTER (gtk_rb_node_get_next (NODE_FROM_POINTER (node)));
+}
+
+gpointer
+gtk_rb_tree_get_root (GtkRbTree *tree)
+{
+ return NODE_TO_POINTER (tree->root);
+}
+
+gpointer
+gtk_rb_tree_node_get_parent (gpointer node)
+{
+ return NODE_TO_POINTER (parent (NODE_FROM_POINTER (node)));
+}
+
+gpointer
+gtk_rb_tree_node_get_left (gpointer node)
+{
+ return NODE_TO_POINTER (NODE_FROM_POINTER (node)->left);
+}
+
+gpointer
+gtk_rb_tree_node_get_right (gpointer node)
+{
+ return NODE_TO_POINTER (NODE_FROM_POINTER (node)->right);
+}
+
+gpointer
+gtk_rb_tree_get_augment (GtkRbTree *tree,
+ gpointer node)
+{
+ GtkRbNode *rbnode = NODE_FROM_POINTER (node);
+
+ gtk_rb_node_clean (tree, rbnode);
+
+ return NODE_TO_AUG_POINTER (tree, rbnode);
+}
+
+GtkRbTree *
+gtk_rb_tree_node_get_tree (gpointer node)
+{
+ return tree (NODE_FROM_POINTER (node));
+}
+
+void
+gtk_rb_tree_node_mark_dirty (gpointer node)
+{
+ gtk_rb_node_mark_dirty (NODE_FROM_POINTER (node), TRUE);
+}
+
+gpointer
+gtk_rb_tree_insert_before (GtkRbTree *tree,
+ gpointer node)
+{
+ GtkRbNode *result;
+
+
+ if (tree->root == NULL)
+ {
+#ifdef DUMP_MODIFICATION
+ g_print ("add (tree, 0); /* 0x%p */\n", tree);
+#endif /* DUMP_MODIFICATION */
+
+ g_assert (node == NULL);
+
+ result = gtk_rb_node_new (tree);
+ tree->root = result;
+ }
+ else if (node == NULL)
+ {
+ return gtk_rb_tree_insert_after (tree, gtk_rb_tree_get_last (tree));
+ }
+ else
+ {
+ GtkRbNode *current = NODE_FROM_POINTER (node);
+
+#ifdef DUMP_MODIFICATION
+ g_print ("add (tree, %u); /* 0x%p */\n", position (tree, current), tree);
+#endif /* DUMP_MODIFICATION */
+
+ /* setup new node */
+ result = gtk_rb_node_new (tree);
+
+ if (current->left)
+ {
+ current = gtk_rb_node_get_last (current->left);
+ current->right = result;
+ }
+ else
+ {
+ current->left = result;
+ }
+ set_parent (tree, result, current);
+ gtk_rb_node_mark_dirty (current, TRUE);
+ }
+
+ gtk_rb_tree_insert_fixup (tree, result);
+
+ return NODE_TO_POINTER (result);
+}
+
+gpointer
+gtk_rb_tree_insert_after (GtkRbTree *tree,
+ gpointer node)
+{
+ GtkRbNode *current, *result;
+
+ if (node == NULL)
+ return gtk_rb_tree_insert_before (tree, gtk_rb_tree_get_first (tree));
+
+ current = NODE_FROM_POINTER (node);
+
+#ifdef DUMP_MODIFICATION
+ g_print ("add (tree, %u); /* 0x%p */\n", position (tree, current) + 1, tree);
+#endif /* DUMP_MODIFICATION */
+
+ /* setup new node */
+ result = gtk_rb_node_new (tree);
+
+ if (current->right)
+ {
+ current = gtk_rb_node_get_first (current->right);
+ current->left = result;
+ }
+ else
+ {
+ current->right = result;
+ }
+ set_parent (tree, result, current);
+ gtk_rb_node_mark_dirty (current, TRUE);
+
+ gtk_rb_tree_insert_fixup (tree, result);
+
+ return NODE_TO_POINTER (result);
+}
+
+void
+gtk_rb_tree_remove (GtkRbTree *tree,
+ gpointer node)
+{
+ GtkRbNode *x, *y, *p, *real_node;
+
+ real_node = NODE_FROM_POINTER (node);
+
+#ifdef DUMP_MODIFICATION
+ g_print ("delete (tree, %u); /* 0x%p */\n", position (tree, real_node), tree);
+#endif /* DUMP_MODIFICATION */
+
+ y = real_node;
+ if (y->left && y->right)
+ {
+ y = y->right;
+
+ while (y->left)
+ y = y->left;
+ }
+
+ /* x is y's only child, or nil */
+ if (y->left)
+ x = y->left;
+ else
+ x = y->right;
+
+ /* remove y from the parent chain */
+ p = parent (y);
+ if (x != NULL)
+ set_parent (tree, x, p);
+ if (p)
+ {
+ if (y == p->left)
+ p->left = x;
+ else
+ p->right = x;
+ gtk_rb_node_mark_dirty (p, TRUE);
+ }
+ else
+ {
+ if (x == NULL)
+ tree->root = NULL;
+ }
+
+ /* We need to clean up the validity of the tree.
+ */
+ if (is_black (y))
+ gtk_rb_tree_remove_node_fixup (tree, x, p);
+
+ if (y != real_node)
+ {
+ /* Move the node over */
+ if (is_red (real_node) != is_red (y))
+ y->red = !y->red;
+
+ y->left = real_node->left;
+ if (y->left)
+ set_parent (tree, y->left, y);
+ y->right = real_node->right;
+ if (y->right)
+ set_parent (tree, y->right, y);
+ p = parent (real_node);
+ set_parent (tree, y, p);
+ if (p)
+ {
+ if (p->left == real_node)
+ p->left = y;
+ else
+ p->right = y;
+ gtk_rb_node_mark_dirty (p, TRUE);
+ }
+ gtk_rb_node_mark_dirty (y, TRUE);
+ }
+
+ gtk_rb_node_free (tree, real_node);
+}
+
+void
+gtk_rb_tree_remove_all (GtkRbTree *tree)
+{
+#ifdef DUMP_MODIFICATION
+ g_print ("delete_all (tree); /* 0x%p */\n", tree);
+#endif /* DUMP_MODIFICATION */
+
+ if (tree->root)
+ gtk_rb_node_free_deep (tree, tree->root);
+
+ tree->root = NULL;
+}
+
diff --git a/subprojects/gfm/gtkrbtreeprivate.h b/subprojects/gfm/gtkrbtreeprivate.h
new file mode 100644
index 00000000..45aba5cc
--- /dev/null
+++ b/subprojects/gfm/gtkrbtreeprivate.h
@@ -0,0 +1,75 @@
+/* gtkrbtree.h
+ * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* A Red-Black Tree implementation used specifically by GtkTreeView.
+ */
+#ifndef __GTK_RB_TREE_H__
+#define __GTK_RB_TREE_H__
+
+#include <glib.h>
+
+
+G_BEGIN_DECLS
+
+
+typedef struct _GtkRbTree GtkRbTree;
+
+typedef void (* GtkRbTreeAugmentFunc) (GtkRbTree *tree,
+ gpointer node_augment,
+ gpointer node,
+ gpointer left,
+ gpointer right);
+
+GtkRbTree * gtk_rb_tree_new_for_size (gsize element_size,
+ gsize augment_size,
+ GtkRbTreeAugmentFunc augment_func,
+ GDestroyNotify clear_func,
+ GDestroyNotify clear_augment_func);
+#define gtk_rb_tree_new(type, augment_type, augment_func, clear_func, clear_augment_func) \
+ gtk_rb_tree_new_for_size (sizeof (type), sizeof (augment_type), (augment_func), (clear_func), (clear_augment_func))
+
+GtkRbTree * gtk_rb_tree_ref (GtkRbTree *tree);
+void gtk_rb_tree_unref (GtkRbTree *tree);
+
+gpointer gtk_rb_tree_get_root (GtkRbTree *tree);
+gpointer gtk_rb_tree_get_first (GtkRbTree *tree);
+gpointer gtk_rb_tree_get_last (GtkRbTree *tree);
+
+gpointer gtk_rb_tree_node_get_previous (gpointer node);
+gpointer gtk_rb_tree_node_get_next (gpointer node);
+gpointer gtk_rb_tree_node_get_parent (gpointer node);
+gpointer gtk_rb_tree_node_get_left (gpointer node);
+gpointer gtk_rb_tree_node_get_right (gpointer node);
+GtkRbTree * gtk_rb_tree_node_get_tree (gpointer node);
+void gtk_rb_tree_node_mark_dirty (gpointer node);
+
+gpointer gtk_rb_tree_get_augment (GtkRbTree *tree,
+ gpointer node);
+
+gpointer gtk_rb_tree_insert_before (GtkRbTree *tree,
+ gpointer node);
+gpointer gtk_rb_tree_insert_after (GtkRbTree *tree,
+ gpointer node);
+void gtk_rb_tree_remove (GtkRbTree *tree,
+ gpointer node);
+void gtk_rb_tree_remove_all (GtkRbTree *tree);
+
+
+G_END_DECLS
+
+
+#endif /* __GTK_RB_TREE_H__ */
diff --git a/subprojects/gfm/gtkslicelistmodel.c b/subprojects/gfm/gtkslicelistmodel.c
new file mode 100644
index 00000000..55ea970b
--- /dev/null
+++ b/subprojects/gfm/gtkslicelistmodel.c
@@ -0,0 +1,527 @@
+/*
+ * Copyright © 2018 Benjamin Otte
+ *
+ * This library 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.1 of the License, or (at your option) any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte@gnome.org>
+ */
+
+// #include "config.h"
+
+#include "gtkslicelistmodel.h"
+
+#include "gtkintl.h"
+// #include "gtkprivate.h"
+
+/**
+ * SECTION:gtkslicelistmodel
+ * @title: GtkSliceListModel
+ * @short_description: A list model that presents a slice out of a larger list
+ * @see_also: #GListModel
+ *
+ * #GtkSliceListModel is a list model that takes a list model and presents a slice of
+ * that model.
+ *
+ * This is useful when implementing paging by setting the size to the number of elements
+ * per page and updating the offset whenever a different page is opened.
+ */
+
+#define DEFAULT_SIZE 10
+
+enum {
+ PROP_0,
+ PROP_ITEM_TYPE,
+ PROP_MODEL,
+ PROP_OFFSET,
+ PROP_SIZE,
+ NUM_PROPERTIES
+};
+
+struct _GtkSliceListModel
+{
+ GObject parent_instance;
+
+ GType item_type;
+ GListModel *model;
+ guint offset;
+ guint size;
+
+ guint n_items;
+};
+
+struct _GtkSliceListModelClass
+{
+ GObjectClass parent_class;
+};
+
+static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
+
+static GType
+gtk_slice_list_model_get_item_type (GListModel *list)
+{
+ GtkSliceListModel *self = GTK_SLICE_LIST_MODEL (list);
+
+ return self->item_type;
+}
+
+static guint
+gtk_slice_list_model_get_n_items (GListModel *list)
+{
+ GtkSliceListModel *self = GTK_SLICE_LIST_MODEL (list);
+ guint n_items;
+
+ if (self->model == NULL)
+ return 0;
+
+ /* XXX: This can be done without calling g_list_model_get_n_items() on the parent model
+ * by checking if model.get_item(offset + size) != NULL */
+ n_items = g_list_model_get_n_items (self->model);
+ if (n_items <= self->offset)
+ return 0;
+
+ n_items -= self->offset;
+ return MIN (n_items, self->size);
+}
+
+static gpointer
+gtk_slice_list_model_get_item (GListModel *list,
+ guint position)
+{
+ GtkSliceListModel *self = GTK_SLICE_LIST_MODEL (list);
+
+ if (self->model == NULL)
+ return NULL;
+
+ if (position >= self->size)
+ return NULL;
+
+ return g_list_model_get_item (self->model, position + self->offset);
+}
+
+static void
+gtk_slice_list_model_model_init (GListModelInterface *iface)
+{
+ iface->get_item_type = gtk_slice_list_model_get_item_type;
+ iface->get_n_items = gtk_slice_list_model_get_n_items;
+ iface->get_item = gtk_slice_list_model_get_item;
+}
+
+G_DEFINE_TYPE_WITH_CODE (GtkSliceListModel, gtk_slice_list_model, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_slice_list_model_model_init))
+
+static void
+gtk_slice_list_model_items_changed_cb (GListModel *model,
+ guint position,
+ guint removed,
+ guint added,
+ GtkSliceListModel *self)
+{
+ if (position >= self->offset + self->size)
+ return;
+
+ if (position < self->offset)
+ {
+ guint skip = MIN (removed, added);
+ skip = MIN (skip, position - self->offset);
+
+ position += skip;
+ removed -= skip;
+ added -= skip;
+ }
+
+ if (removed == added)
+ {
+ guint changed = removed;
+
+ if (changed == 0)
+ return;
+
+ g_assert (position >= self->offset);
+ position -= self->offset;
+ changed = MIN (changed, self->size) - position;
+
+ g_list_model_items_changed (G_LIST_MODEL (self), position, changed, changed);
+ }
+ else
+ {
+ guint n_after, n_before;
+ guint skip;
+
+ if (position > self->offset)
+ skip = position - self->offset;
+ else
+ skip = 0;
+
+ n_after = g_list_model_get_n_items (self->model);
+ n_before = n_after - added + removed;
+ n_after = CLAMP (n_after, self->offset, self->offset + self->size) - self->offset;
+ n_before = CLAMP (n_before, self->offset, self->offset + self->size) - self->offset;
+
+ g_list_model_items_changed (G_LIST_MODEL (self), skip, n_before - skip, n_after - skip);
+ }
+}
+
+static void
+gtk_slice_list_model_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkSliceListModel *self = GTK_SLICE_LIST_MODEL (object);
+
+ switch (prop_id)
+ {
+ case PROP_ITEM_TYPE:
+ self->item_type = g_value_get_gtype (value);
+ break;
+
+ case PROP_MODEL:
+ gtk_slice_list_model_set_model (self, g_value_get_object (value));
+ break;
+
+ case PROP_OFFSET:
+ gtk_slice_list_model_set_offset (self, g_value_get_uint (value));
+ break;
+
+ case PROP_SIZE:
+ gtk_slice_list_model_set_size (self, g_value_get_uint (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_slice_list_model_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkSliceListModel *self = GTK_SLICE_LIST_MODEL (object);
+
+ switch (prop_id)
+ {
+ case PROP_ITEM_TYPE:
+ g_value_set_gtype (value, self->item_type);
+ break;
+
+ case PROP_MODEL:
+ g_value_set_object (value, self->model);
+ break;
+
+ case PROP_OFFSET:
+ g_value_set_uint (value, self->offset);
+ break;
+
+ case PROP_SIZE:
+ g_value_set_uint (value, self->size);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_slice_list_model_clear_model (GtkSliceListModel *self)
+{
+ if (self->model == NULL)
+ return;
+
+ g_signal_handlers_disconnect_by_func (self->model, gtk_slice_list_model_items_changed_cb, self);
+ g_clear_object (&self->model);
+}
+
+static void
+gtk_slice_list_model_dispose (GObject *object)
+{
+ GtkSliceListModel *self = GTK_SLICE_LIST_MODEL (object);
+
+ gtk_slice_list_model_clear_model (self);
+
+ G_OBJECT_CLASS (gtk_slice_list_model_parent_class)->dispose (object);
+};
+
+static void
+gtk_slice_list_model_class_init (GtkSliceListModelClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+
+ gobject_class->set_property = gtk_slice_list_model_set_property;
+ gobject_class->get_property = gtk_slice_list_model_get_property;
+ gobject_class->dispose = gtk_slice_list_model_dispose;
+
+ /**
+ * GtkSliceListModel:item-type:
+ *
+ * The #GType for elements of this object
+ */
+ properties[PROP_ITEM_TYPE] =
+ g_param_spec_gtype ("item-type",
+ P_("Item type"),
+ P_("The type of elements of this object"),
+ G_TYPE_OBJECT,
+ GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * GtkSliceListModel:model:
+ *
+ * Child model to take slice from
+ */
+ properties[PROP_MODEL] =
+ g_param_spec_object ("model",
+ P_("Model"),
+ P_("Child model to take slice from"),
+ G_TYPE_LIST_MODEL,
+ GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * GtkSliceListModel:offset:
+ *
+ * Offset of slice
+ */
+ properties[PROP_OFFSET] =
+ g_param_spec_uint ("offset",
+ P_("Offset"),
+ P_("Offset of slice"),
+ 0, G_MAXUINT, 0,
+ GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * GtkSliceListModel:size:
+ *
+ * Maximum size of slice
+ */
+ properties[PROP_SIZE] =
+ g_param_spec_uint ("size",
+ P_("Size"),
+ P_("Maximum size of slice"),
+ 0, G_MAXUINT, DEFAULT_SIZE,
+ GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
+}
+
+static void
+gtk_slice_list_model_init (GtkSliceListModel *self)
+{
+ self->size = DEFAULT_SIZE;
+}
+
+/**
+ * gtk_slice_list_model_new:
+ * @model: (transfer none): The model to use
+ * @offset: the offset of the slice
+ * @size: maximum size of the slice
+ *
+ * Creates a new slice model that presents the slice from @offset to
+ * @offset + @size our of the given @model.
+ *
+ * Returns: A new #GtkSliceListModel
+ **/
+GtkSliceListModel *
+gtk_slice_list_model_new (GListModel *model,
+ guint offset,
+ guint size)
+{
+ g_return_val_if_fail (G_IS_LIST_MODEL (model), NULL);
+
+ return g_object_new (GTK_TYPE_SLICE_LIST_MODEL,
+ "item-type", g_list_model_get_item_type (model),
+ "model", model,
+ "offset", offset,
+ "size", size,
+ NULL);
+}
+
+/**
+ * gtk_slice_list_model_new_for_type:
+ * @item_type: the type of items
+ *
+ * Creates a new empty #GtkSliceListModel for the given @item_type that
+ * can be set up later.
+ *
+ * Returns: a new empty #GtkSliceListModel
+ **/
+GtkSliceListModel *
+gtk_slice_list_model_new_for_type (GType item_type)
+{
+ g_return_val_if_fail (g_type_is_a (item_type, G_TYPE_OBJECT), NULL);
+
+ return g_object_new (GTK_TYPE_SLICE_LIST_MODEL,
+ "item-type", item_type,
+ NULL);
+}
+
+/**
+ * gtk_slice_list_model_set_model:
+ * @self: a #GtkSliceListModel
+ * @model: (allow-none): The model to be sliced
+ *
+ * Sets the model to show a slice of. The model's item type must conform
+ * to @self's item type.
+ *
+ **/
+void
+gtk_slice_list_model_set_model (GtkSliceListModel *self,
+ GListModel *model)
+{
+ guint removed, added;
+
+ g_return_if_fail (GTK_IS_SLICE_LIST_MODEL (self));
+ g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
+
+ if (self->model == model)
+ return;
+
+ removed = g_list_model_get_n_items (G_LIST_MODEL (self));
+ gtk_slice_list_model_clear_model (self);
+
+ if (model)
+ {
+ self->model = g_object_ref (model);
+ g_signal_connect (model, "items-changed", G_CALLBACK (gtk_slice_list_model_items_changed_cb), self);
+ added = g_list_model_get_n_items (G_LIST_MODEL (self));
+ }
+ else
+ {
+ added = 0;
+ }
+
+ if (removed > 0 || added > 0)
+ g_list_model_items_changed (G_LIST_MODEL (self), 0, removed, added);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
+}
+
+/**
+ * gtk_slice_list_model_get_model:
+ * @self: a #GtkSliceListModel
+ *
+ * Gets the model that is curently being used or %NULL if none.
+ *
+ * Returns: (nullable) (transfer none): The model in use
+ **/
+GListModel *
+gtk_slice_list_model_get_model (GtkSliceListModel *self)
+{
+ g_return_val_if_fail (GTK_IS_SLICE_LIST_MODEL (self), NULL);
+
+ return self->model;
+}
+
+/**
+ * gtk_slice_list_model_set_offset:
+ * @self: a #GtkSliceListModel
+ * @offset: the new offset to use
+ *
+ * Sets the offset into the original model for this slice.
+ *
+ * If the offset is too large for the sliced model,
+ * @self will end up empty.
+ **/
+void
+gtk_slice_list_model_set_offset (GtkSliceListModel *self,
+ guint offset)
+{
+ guint before, after;
+
+ g_return_if_fail (GTK_IS_SLICE_LIST_MODEL (self));
+
+ if (self->offset == offset)
+ return;
+
+ before = g_list_model_get_n_items (G_LIST_MODEL (self));
+
+ self->offset = offset;
+
+ after = g_list_model_get_n_items (G_LIST_MODEL (self));
+
+ if (before > 0 || after > 0)
+ g_list_model_items_changed (G_LIST_MODEL (self), 0, before, after);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_OFFSET]);
+}
+
+/**
+ * gtk_slice_list_model_get_offset:
+ * @self: a #GtkSliceListModel
+ *
+ * Gets the offset set via gtk_slice_list_model_set_offset()
+ *
+ * Returns: The offset
+ **/
+guint
+gtk_slice_list_model_get_offset (GtkSliceListModel *self)
+{
+ g_return_val_if_fail (GTK_IS_SLICE_LIST_MODEL (self), 0);
+
+ return self->offset;
+}
+
+/**
+ * gtk_slice_list_model_set_size:
+ * @self: a #GtkSliceListModel
+ * @size: the maximum size
+ *
+ * Sets the maximum size. @self will never have more items
+ * than @size.
+ *
+ * It can however have fewer items if the offset is too large or
+ * the model sliced from doesn't have enough items.
+ */
+void
+gtk_slice_list_model_set_size (GtkSliceListModel *self,
+ guint size)
+{
+ guint before, after;
+
+ g_return_if_fail (GTK_IS_SLICE_LIST_MODEL (self));
+
+ if (self->size == size)
+ return;
+
+ before = g_list_model_get_n_items (G_LIST_MODEL (self));
+
+ self->size = size;
+
+ after = g_list_model_get_n_items (G_LIST_MODEL (self));
+
+ if (before > after)
+ g_list_model_items_changed (G_LIST_MODEL (self), after, before - after, 0);
+ else if (before < after)
+ g_list_model_items_changed (G_LIST_MODEL (self), before, 0, after - before);
+ /* else nothing */
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SIZE]);
+}
+
+/**
+ * gtk_slice_list_model_get_size:
+ * @self: a #GtkSliceListModel
+ *
+ * Gets the size set via gtk_slice_list_model_set_size().
+ *
+ * Returns: The size
+ **/
+guint
+gtk_slice_list_model_get_size (GtkSliceListModel *self)
+{
+ g_return_val_if_fail (GTK_IS_SLICE_LIST_MODEL (self), DEFAULT_SIZE);
+
+ return self->size;
+}
diff --git a/subprojects/gfm/gtkslicelistmodel.h b/subprojects/gfm/gtkslicelistmodel.h
new file mode 100644
index 00000000..25543f18
--- /dev/null
+++ b/subprojects/gfm/gtkslicelistmodel.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright © 2018 Benjamin Otte
+ *
+ * This library 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.1 of the License, or (at your option) any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte@gnome.org>
+ */
+
+#ifndef __GTK_SLICE_LIST_MODEL_H__
+#define __GTK_SLICE_LIST_MODEL_H__
+
+
+/* #if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+ * #error "Only <gtk/gtk.h> can be included directly."
+ * #endif
+ */
+
+#include <gio/gio.h>
+#include <gtk/gtkwidget.h>
+
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_SLICE_LIST_MODEL (gtk_slice_list_model_get_type ())
+#define GTK_PARAM_READWRITE G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
+
+GDK_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (GtkSliceListModel, gtk_slice_list_model, GTK, SLICE_LIST_MODEL, GObject)
+
+GDK_AVAILABLE_IN_ALL
+GtkSliceListModel * gtk_slice_list_model_new (GListModel *model,
+ guint offset,
+ guint size);
+GDK_AVAILABLE_IN_ALL
+GtkSliceListModel * gtk_slice_list_model_new_for_type (GType item_type);
+
+GDK_AVAILABLE_IN_ALL
+void gtk_slice_list_model_set_model (GtkSliceListModel *self,
+ GListModel *model);
+GDK_AVAILABLE_IN_ALL
+GListModel * gtk_slice_list_model_get_model (GtkSliceListModel *self);
+GDK_AVAILABLE_IN_ALL
+void gtk_slice_list_model_set_offset (GtkSliceListModel *self,
+ guint offset);
+GDK_AVAILABLE_IN_ALL
+guint gtk_slice_list_model_get_offset (GtkSliceListModel *self);
+GDK_AVAILABLE_IN_ALL
+void gtk_slice_list_model_set_size (GtkSliceListModel *self,
+ guint size);
+GDK_AVAILABLE_IN_ALL
+guint gtk_slice_list_model_get_size (GtkSliceListModel *self);
+
+G_END_DECLS
+
+#endif /* __GTK_SLICE_LIST_MODEL_H__ */
diff --git a/subprojects/gfm/gtksortlistmodel.c b/subprojects/gfm/gtksortlistmodel.c
new file mode 100644
index 00000000..a702feb6
--- /dev/null
+++ b/subprojects/gfm/gtksortlistmodel.c
@@ -0,0 +1,563 @@
+/*
+ * Copyright © 2018 Benjamin Otte
+ *
+ * This library 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.1 of the License, or (at your option) any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte@gnome.org>
+ */
+
+// #include "config.h"
+
+#include "gtksortlistmodel.h"
+
+#include "gtkintl.h"
+// #include "gtkprivate.h"
+
+/**
+ * SECTION:gtksortlistmodel
+ * @title: GtkSortListModel
+ * @short_description: A list model that sorts its items
+ * @see_also: #GListModel
+ *
+ * #GtkSortListModel is a list model that takes a list model and
+ * sorts its elements according to a compare function.
+ *
+ * #GtkSortListModel is a generic model and because of that it
+ * cannot take advantage of any external knowledge when sorting.
+ * If you run into performance issues with #GtkSortListModel, it
+ * is strongly recommended that you write your own sorting list
+ * model.
+ */
+
+enum {
+ PROP_0,
+ PROP_HAS_SORT,
+ PROP_ITEM_TYPE,
+ PROP_MODEL,
+ NUM_PROPERTIES
+};
+
+struct _GtkSortListModel
+{
+ GObject parent_instance;
+
+ GType item_type;
+ GListModel *model;
+ GCompareDataFunc sort_func;
+ gpointer user_data;
+ GDestroyNotify user_destroy;
+
+ GSequence *sorted; /* NULL if sort_func == NULL */
+ GSequence *unsorted; /* NULL if sort_func == NULL */
+};
+
+struct _GtkSortListModelClass
+{
+ GObjectClass parent_class;
+};
+
+static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
+
+static GType
+gtk_sort_list_model_get_item_type (GListModel *list)
+{
+ GtkSortListModel *self = GTK_SORT_LIST_MODEL (list);
+
+ return self->item_type;
+}
+
+static guint
+gtk_sort_list_model_get_n_items (GListModel *list)
+{
+ GtkSortListModel *self = GTK_SORT_LIST_MODEL (list);
+
+ if (self->model == NULL)
+ return 0;
+
+ if (self->sorted)
+ return g_sequence_get_length (self->sorted);
+
+ return g_list_model_get_n_items (self->model);
+}
+
+static gpointer
+gtk_sort_list_model_get_item (GListModel *list,
+ guint position)
+{
+ GtkSortListModel *self = GTK_SORT_LIST_MODEL (list);
+ GSequenceIter *iter;
+
+ if (self->model == NULL)
+ return NULL;
+
+ if (self->unsorted == NULL)
+ return g_list_model_get_item (self->model, position);
+
+ iter = g_sequence_get_iter_at_pos (self->sorted, position);
+ if (g_sequence_iter_is_end (iter))
+ return NULL;
+
+ return g_object_ref (g_sequence_get (iter));
+}
+
+static void
+gtk_sort_list_model_model_init (GListModelInterface *iface)
+{
+ iface->get_item_type = gtk_sort_list_model_get_item_type;
+ iface->get_n_items = gtk_sort_list_model_get_n_items;
+ iface->get_item = gtk_sort_list_model_get_item;
+}
+
+G_DEFINE_TYPE_WITH_CODE (GtkSortListModel, gtk_sort_list_model, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_sort_list_model_model_init))
+
+static void
+gtk_sort_list_model_remove_items (GtkSortListModel *self,
+ guint position,
+ guint n_items,
+ guint *unmodified_start,
+ guint *unmodified_end)
+{
+ GSequenceIter *unsorted_iter;
+ guint i, pos, start, end, length_before;
+
+ start = end = length_before = g_sequence_get_length (self->sorted);
+ unsorted_iter = g_sequence_get_iter_at_pos (self->unsorted, position);
+
+ for (i = 0; i < n_items ; i++)
+ {
+ GSequenceIter *sorted_iter;
+ GSequenceIter *next;
+
+ next = g_sequence_iter_next (unsorted_iter);
+
+ sorted_iter = g_sequence_get (unsorted_iter);
+ pos = g_sequence_iter_get_position (sorted_iter);
+ start = MIN (start, pos);
+ end = MIN (end, length_before - i - 1 - pos);
+
+ g_sequence_remove (sorted_iter);
+ g_sequence_remove (unsorted_iter);
+
+ unsorted_iter = next;
+ }
+
+ *unmodified_start = start;
+ *unmodified_end = end;
+}
+
+static void
+gtk_sort_list_model_add_items (GtkSortListModel *self,
+ guint position,
+ guint n_items,
+ guint *unmodified_start,
+ guint *unmodified_end)
+{
+ GSequenceIter *unsorted_iter, *sorted_iter;
+ guint i, pos, start, end, length_before;
+
+ unsorted_iter = g_sequence_get_iter_at_pos (self->unsorted, position);
+ start = end = length_before = g_sequence_get_length (self->sorted);
+
+ for (i = 0; i < n_items; i++)
+ {
+ gpointer item = g_list_model_get_item (self->model, position + i);
+ sorted_iter = g_sequence_insert_sorted (self->sorted, item, self->sort_func, self->user_data);
+ g_sequence_insert_before (unsorted_iter, sorted_iter);
+ if (unmodified_start != NULL || unmodified_end != NULL)
+ {
+ pos = g_sequence_iter_get_position (sorted_iter);
+ start = MIN (start, pos);
+ end = MIN (end, length_before + i - pos);
+ }
+ }
+
+ if (unmodified_start)
+ *unmodified_start = start;
+ if (unmodified_end)
+ *unmodified_end = end;
+}
+
+static void
+gtk_sort_list_model_items_changed_cb (GListModel *model,
+ guint position,
+ guint removed,
+ guint added,
+ GtkSortListModel *self)
+{
+ guint n_items, start, end, start2, end2;
+
+ if (removed == 0 && added == 0)
+ return;
+
+ if (self->sorted == NULL)
+ {
+ g_list_model_items_changed (G_LIST_MODEL (self), position, removed, added);
+ return;
+ }
+
+ gtk_sort_list_model_remove_items (self, position, removed, &start, &end);
+ gtk_sort_list_model_add_items (self, position, added, &start2, &end2);
+ start = MIN (start, start2);
+ end = MIN (end, end2);
+
+ n_items = g_sequence_get_length (self->sorted) - start - end;
+ g_list_model_items_changed (G_LIST_MODEL (self), start, n_items - added + removed, n_items);
+}
+
+static void
+gtk_sort_list_model_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkSortListModel *self = GTK_SORT_LIST_MODEL (object);
+
+ switch (prop_id)
+ {
+ case PROP_ITEM_TYPE:
+ self->item_type = g_value_get_gtype (value);
+ break;
+
+ case PROP_MODEL:
+ gtk_sort_list_model_set_model (self, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_sort_list_model_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkSortListModel *self = GTK_SORT_LIST_MODEL (object);
+
+ switch (prop_id)
+ {
+ case PROP_HAS_SORT:
+ g_value_set_boolean (value, self->sort_func != NULL);
+ break;
+
+ case PROP_ITEM_TYPE:
+ g_value_set_gtype (value, self->item_type);
+ break;
+
+ case PROP_MODEL:
+ g_value_set_object (value, self->model);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_sort_list_model_clear_model (GtkSortListModel *self)
+{
+ if (self->model == NULL)
+ return;
+
+ g_signal_handlers_disconnect_by_func (self->model, gtk_sort_list_model_items_changed_cb, self);
+ g_clear_object (&self->model);
+ g_clear_pointer (&self->sorted, g_sequence_free);
+ g_clear_pointer (&self->unsorted, g_sequence_free);
+}
+
+static void
+gtk_sort_list_model_dispose (GObject *object)
+{
+ GtkSortListModel *self = GTK_SORT_LIST_MODEL (object);
+
+ gtk_sort_list_model_clear_model (self);
+ if (self->user_destroy)
+ self->user_destroy (self->user_data);
+ self->sort_func = NULL;
+ self->user_data = NULL;
+ self->user_destroy = NULL;
+
+ G_OBJECT_CLASS (gtk_sort_list_model_parent_class)->dispose (object);
+};
+
+static void
+gtk_sort_list_model_class_init (GtkSortListModelClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+
+ gobject_class->set_property = gtk_sort_list_model_set_property;
+ gobject_class->get_property = gtk_sort_list_model_get_property;
+ gobject_class->dispose = gtk_sort_list_model_dispose;
+
+ /**
+ * GtkSortListModel:has-sort:
+ *
+ * If a sort function is set for this model
+ */
+ properties[PROP_HAS_SORT] =
+ g_param_spec_boolean ("has-sort",
+ P_("has sort"),
+ P_("If a sort function is set for this model"),
+ FALSE,
+ GTK_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * GtkSortListModel:item-type:
+ *
+ * The #GType for items of this model
+ */
+ properties[PROP_ITEM_TYPE] =
+ g_param_spec_gtype ("item-type",
+ P_("Item type"),
+ P_("The type of items of this list"),
+ G_TYPE_OBJECT,
+ GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * GtkSortListModel:model:
+ *
+ * The model being sorted
+ */
+ properties[PROP_MODEL] =
+ g_param_spec_object ("model",
+ P_("Model"),
+ P_("The model being sorted"),
+ G_TYPE_LIST_MODEL,
+ GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY);
+
+ g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
+}
+
+static void
+gtk_sort_list_model_init (GtkSortListModel *self)
+{
+}
+
+
+/**
+ * gtk_sort_list_model_new:
+ * @model: the model to sort
+ * @sort_func: (allow-none): sort function or %NULL to not sort items
+ * @user_data: user data passed to @sort_func
+ * @user_destroy: destroy notifier for @user_data
+ *
+ * Creates a new sort list model that uses the @sort_func to sort @model.
+ *
+ * Returns: a new #GtkSortListModel
+ **/
+GtkSortListModel *
+gtk_sort_list_model_new (GListModel *model,
+ GCompareDataFunc sort_func,
+ gpointer user_data,
+ GDestroyNotify user_destroy)
+{
+ GtkSortListModel *result;
+
+ g_return_val_if_fail (G_IS_LIST_MODEL (model), NULL);
+
+ result = g_object_new (GTK_TYPE_SORT_LIST_MODEL,
+ "item-type", g_list_model_get_item_type (model),
+ "model", model,
+ NULL);
+
+ if (sort_func)
+ gtk_sort_list_model_set_sort_func (result, sort_func, user_data, user_destroy);
+
+ return result;
+}
+
+/**
+ * gtk_sort_list_model_new_for_type:
+ * @item_type: the type of the items that will be returned
+ *
+ * Creates a new empty sort list model set up to return items of type @item_type.
+ * It is up to the application to set a proper sort function and model to ensure
+ * the item type is matched.
+ *
+ * Returns: a new #GtkSortListModel
+ **/
+GtkSortListModel *
+gtk_sort_list_model_new_for_type (GType item_type)
+{
+ g_return_val_if_fail (g_type_is_a (item_type, G_TYPE_OBJECT), NULL);
+
+ return g_object_new (GTK_TYPE_SORT_LIST_MODEL,
+ "item-type", item_type,
+ NULL);
+}
+
+static void
+gtk_sort_list_model_create_sequences (GtkSortListModel *self)
+{
+ if (!self->sort_func || self->model == NULL)
+ return;
+
+ self->sorted = g_sequence_new (g_object_unref);
+ self->unsorted = g_sequence_new (NULL);
+
+ gtk_sort_list_model_add_items (self, 0, g_list_model_get_n_items (self->model), NULL, NULL);
+}
+
+/**
+ * gtk_sort_list_model_set_sort_func:
+ * @self: a #GtkSortListModel
+ * @sort_func: (allow-none): sort function or %NULL to not sort items
+ * @user_data: user data passed to @sort_func
+ * @user_destroy: destroy notifier for @user_data
+ *
+ * Sets the function used to sort items. The function will be called for every
+ * item and must return an integer less than, equal to, or greater than zero if
+ * for two items from the model if the first item is considered to be respectively
+ * less than, equal to, or greater than the second.
+ **/
+void
+gtk_sort_list_model_set_sort_func (GtkSortListModel *self,
+ GCompareDataFunc sort_func,
+ gpointer user_data,
+ GDestroyNotify user_destroy)
+{
+ guint n_items;
+
+ g_return_if_fail (GTK_IS_SORT_LIST_MODEL (self));
+ g_return_if_fail (sort_func != NULL || (user_data == NULL && !user_destroy));
+
+ if (!sort_func && !self->sort_func)
+ return;
+
+ if (self->user_destroy)
+ self->user_destroy (self->user_data);
+
+ g_clear_pointer (&self->unsorted, g_sequence_free);
+ g_clear_pointer (&self->sorted, g_sequence_free);
+ self->sort_func = sort_func;
+ self->user_data = user_data;
+ self->user_destroy = user_destroy;
+
+ gtk_sort_list_model_create_sequences (self);
+
+ n_items = g_list_model_get_n_items (G_LIST_MODEL (self));
+ if (n_items > 1)
+ g_list_model_items_changed (G_LIST_MODEL (self), 0, n_items, n_items);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_HAS_SORT]);
+}
+
+/**
+ * gtk_sort_list_model_set_model:
+ * @self: a #GtkSortListModel
+ * @model: (allow-none): The model to be sorted
+ *
+ * Sets the model to be sorted. The @model's item type must conform to
+ * the item type of @self.
+ **/
+void
+gtk_sort_list_model_set_model (GtkSortListModel *self,
+ GListModel *model)
+{
+ guint removed, added;
+
+ g_return_if_fail (GTK_IS_SORT_LIST_MODEL (self));
+ g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
+ if (model)
+ {
+ g_return_if_fail (g_type_is_a (self->item_type, g_list_model_get_item_type (model)));
+ }
+
+ if (self->model == model)
+ return;
+
+ removed = g_list_model_get_n_items (G_LIST_MODEL (self));
+ gtk_sort_list_model_clear_model (self);
+
+ if (model)
+ {
+ self->model = g_object_ref (model);
+ g_signal_connect (model, "items-changed", G_CALLBACK (gtk_sort_list_model_items_changed_cb), self);
+ added = g_list_model_get_n_items (model);
+
+ gtk_sort_list_model_create_sequences (self);
+ }
+ else
+ added = 0;
+
+ if (removed > 0 || added > 0)
+ g_list_model_items_changed (G_LIST_MODEL (self), 0, removed, added);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
+}
+
+/**
+ * gtk_sort_list_model_get_model:
+ * @self: a #GtkSortListModel
+ *
+ * Gets the model currently sorted or %NULL if none.
+ *
+ * Returns: (nullable) (transfer none): The model that gets sorted
+ **/
+GListModel *
+gtk_sort_list_model_get_model (GtkSortListModel *self)
+{
+ g_return_val_if_fail (GTK_IS_SORT_LIST_MODEL (self), NULL);
+
+ return self->model;
+}
+
+/**
+ * gtk_sort_list_model_has_sort:
+ * @self: a #GtkSortListModel
+ *
+ * Checks if a sort function is currently set on @self
+ *
+ * Returns: %TRUE if a sort function is set
+ **/
+gboolean
+gtk_sort_list_model_has_sort (GtkSortListModel *self)
+{
+ g_return_val_if_fail (GTK_IS_SORT_LIST_MODEL (self), FALSE);
+
+ return self->sort_func != NULL;
+}
+
+/**
+ * gtk_sort_list_model_resort:
+ * @self: a #GtkSortListModel
+ *
+ * Causes @self to resort all items in the model.
+ *
+ * Calling this function is necessary when data used by the sort
+ * function has changed.
+ **/
+void
+gtk_sort_list_model_resort (GtkSortListModel *self)
+{
+ guint n_items;
+
+ g_return_if_fail (GTK_IS_SORT_LIST_MODEL (self));
+
+ if (self->sorted == NULL)
+ return;
+
+ n_items = g_list_model_get_n_items (self->model);
+ if (n_items <= 1)
+ return;
+
+ g_sequence_sort (self->sorted, self->sort_func, self->user_data);
+
+ g_list_model_items_changed (G_LIST_MODEL (self), 0, n_items, n_items);
+}
+
diff --git a/subprojects/gfm/gtksortlistmodel.h b/subprojects/gfm/gtksortlistmodel.h
new file mode 100644
index 00000000..460491b2
--- /dev/null
+++ b/subprojects/gfm/gtksortlistmodel.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright © 2018 Benjamin Otte
+ *
+ * This library 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.1 of the License, or (at your option) any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte@gnome.org>
+ */
+
+#ifndef __GTK_SORT_LIST_MODEL_H__
+#define __GTK_SORT_LIST_MODEL_H__
+
+
+/*#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)*/
+/*#error "Only <gtk/gtk.h> can be included directly."*/
+/*#endif*/
+
+#include <gio/gio.h>
+#include <gtk/gtkwidget.h>
+
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_SORT_LIST_MODEL (gtk_sort_list_model_get_type ())
+#define GTK_PARAM_READABLE G_PARAM_READABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
+#define GTK_PARAM_WRITABLE G_PARAM_WRITABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
+#define GTK_PARAM_READWRITE G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
+
+GDK_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (GtkSortListModel, gtk_sort_list_model, GTK, SORT_LIST_MODEL, GObject)
+
+GDK_AVAILABLE_IN_ALL
+GtkSortListModel * gtk_sort_list_model_new (GListModel *model,
+ GCompareDataFunc sort_func,
+ gpointer user_data,
+ GDestroyNotify user_destroy);
+GDK_AVAILABLE_IN_ALL
+GtkSortListModel * gtk_sort_list_model_new_for_type (GType item_type);
+
+GDK_AVAILABLE_IN_ALL
+void gtk_sort_list_model_set_sort_func (GtkSortListModel *self,
+ GCompareDataFunc sort_func,
+ gpointer user_data,
+ GDestroyNotify user_destroy);
+GDK_AVAILABLE_IN_ALL
+gboolean gtk_sort_list_model_has_sort (GtkSortListModel *self);
+GDK_AVAILABLE_IN_ALL
+void gtk_sort_list_model_set_model (GtkSortListModel *self,
+ GListModel *model);
+GDK_AVAILABLE_IN_ALL
+GListModel * gtk_sort_list_model_get_model (GtkSortListModel *self);
+
+GDK_AVAILABLE_IN_ALL
+void gtk_sort_list_model_resort (GtkSortListModel *self);
+
+G_END_DECLS
+
+#endif /* __GTK_SORT_LIST_MODEL_H__ */
diff --git a/subprojects/gfm/meson.build b/subprojects/gfm/meson.build
new file mode 100644
index 00000000..ab93f9cf
--- /dev/null
+++ b/subprojects/gfm/meson.build
@@ -0,0 +1,85 @@
+project('gfm', 'c',
+ meson_version: '>= 0.38.0',
+)
+
+c_args = []
+gnome = import('gnome')
+
+if not meson.is_subproject()
+ message('WARNING: This project is only intended to be used as a subproject!')
+endif
+
+api_version = '0.1'
+
+gfm_sources = [
+ 'gtkfilterlistmodel.c',
+ 'gtkflattenlistmodel.c',
+ 'gtkslicelistmodel.c',
+ 'gtksortlistmodel.c',
+ 'gtkrbtree.c',
+]
+
+gfm_headers = [
+ 'gtkfilterlistmodel.h',
+ 'gtkflattenlistmodel.h',
+ 'gtkslicelistmodel.h',
+ 'gtksortlistmodel.h',
+ 'gtkrbtreeprivate.h',
+ 'gtkintl.h',
+]
+
+gfm_introspection_headers = [
+ 'gtkfilterlistmodel.h',
+ 'gtkflattenlistmodel.h',
+ 'gtkslicelistmodel.h',
+ 'gtksortlistmodel.h',
+]
+
+pkglibdir = get_option('pkglibdir')
+pkgdatadir = get_option('pkgdatadir')
+pkggirdir = join_paths(pkgdatadir, 'gir-1.0')
+pkgtypelibdir = join_paths(pkglibdir, 'girepository-1.0')
+
+gfm_header_subdir = '.'
+gfm_header_dir = join_paths(get_option('includedir'), gfm_header_subdir)
+
+cc = meson.get_compiler('c')
+
+gfm_deps = [
+ dependency('gio-2.0', version: '>= 2.50'),
+ dependency('gtk+-3.0'),
+]
+
+gfm_lib = shared_library('gfm-' + api_version,
+ gfm_sources,
+ dependencies: gfm_deps,
+ c_args: ['-DGETTEXT_PACKAGE', '-DGTK_COMPILATION '],
+ install: true,
+ install_dir: pkglibdir
+)
+
+gfm_gir = gnome.generate_gir(gfm_lib,
+ sources: gfm_sources + gfm_introspection_headers,
+ nsversion: api_version,
+ namespace: 'Gfm',
+ symbol_prefix: 'gtk',
+ extra_args: ['-DGETTEXT_PACKAGE', '-DGTK_COMPILATION '],
+ identifier_prefix: 'Gtk',
+ link_with: gfm_lib,
+ includes: ['Gio-2.0', 'Gtk-3.0'],
+ install: true,
+ install_dir_gir: pkggirdir,
+ install_dir_typelib: pkgtypelibdir,
+)
+
+pkg = import('pkgconfig')
+
+pkg.generate(
+ description: 'A shared library backporting a few GTK4 listmodels to GTK3',
+ libraries: gfm_lib,
+ name: 'gfm',
+ filebase: 'gfm-' + api_version,
+ version: meson.project_version(),
+ requires: 'glib-2.0',
+ install_dir: join_paths(pkglibdir, 'pkgconfig')
+)
diff --git a/subprojects/gfm/meson_options.txt b/subprojects/gfm/meson_options.txt
new file mode 100644
index 00000000..7abaf8b9
--- /dev/null
+++ b/subprojects/gfm/meson_options.txt
@@ -0,0 +1,6 @@
+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.'
+)
diff --git a/subprojects/libgd b/subprojects/libgd
deleted file mode 160000
-Subproject c7c7ff4e05d3fe82854219091cf116cce6b19de
diff --git a/subprojects/libgd/Makefile.am b/subprojects/libgd/Makefile.am
new file mode 100644
index 00000000..5a071446
--- /dev/null
+++ b/subprojects/libgd/Makefile.am
@@ -0,0 +1,230 @@
+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 += Gd-1.0.gir
+CLEANFILES += 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 = gd-1.0.vapi
+
+gd-1.0.vapi: 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)
+CLEANFILES += $(VAPIS)
+endif
diff --git a/subprojects/libgd/README b/subprojects/libgd/README
new file mode 100644
index 00000000..fb8c88d2
--- /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 00000000..6fd9c8ba
--- /dev/null
+++ b/subprojects/libgd/libgd.doap
@@ -0,0 +1,35 @@
+<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="https://gitlab.gnome.org/GNOME/libgd/issues/" />
+
+<!--
+ <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 00000000..b71e3440
--- /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 00000000..6c9dd401
--- /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 00000000..12f4f068
--- /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 00000000..9a1118d2
--- /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 00000000..b2600a61
--- /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 00000000..0d187643
--- /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 00000000..2c9c805a
--- /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 00000000..042b0e43
--- /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 00000000..e37cb072
--- /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 00000000..be87b1df
--- /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 00000000..1ff80387
--- /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 00000000..3e246946
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-main-icon-box-child.c
@@ -0,0 +1,439 @@
+/*
+ * 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 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);
+
+ if (gd_main_box_item_get_pulse (priv->item))
+ {
+ GtkWidget *spinner;
+
+ spinner = gtk_spinner_new ();
+ gtk_widget_set_halign (spinner, GTK_ALIGN_CENTER);
+ gtk_widget_set_size_request (spinner, 32, 32);
+ gtk_widget_set_valign (spinner, GTK_ALIGN_CENTER);
+ gtk_spinner_start (GTK_SPINNER (spinner));
+ gtk_overlay_add_overlay (GTK_OVERLAY (overlay), spinner);
+ }
+
+ 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_notify_pulse (GdMainIconBoxChild *self)
+{
+ gd_main_icon_box_child_update_layout (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 (priv->item != NULL)
+ g_signal_handlers_disconnect_by_func (priv->item, gd_main_icon_box_child_notify_pulse, self);
+
+ if (!g_set_object (&priv->item, item))
+ return;
+
+ if (priv->item != NULL)
+ {
+ g_signal_connect_object (priv->item,
+ "notify::pulse",
+ G_CALLBACK (gd_main_icon_box_child_notify_pulse),
+ self,
+ G_CONNECT_SWAPPED);
+ }
+
+ 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_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 00000000..40eb2716
--- /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 00000000..54fa139f
--- /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, 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, 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, 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, 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 00000000..7189e0e8
--- /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 00000000..1d6cdf43
--- /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, 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, 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, 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, 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, gdk_event_free);
+ g_clear_pointer (&event, 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, gdk_event_free);
+ g_clear_pointer (&event, 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 00000000..5dc60fe9
--- /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 00000000..95bdb558
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-main-icon-view.c
@@ -0,0 +1,440 @@
+/*
+ * 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
+
+typedef struct _GdMainIconViewPrivate GdMainIconViewPrivate;
+
+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,
+ "yalign", 0.0,
+ "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);
+
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+ gtk_style_context_get_border_color (context,
+ state,
+ &border_color);
+ G_GNUC_END_IGNORE_DEPRECATIONS;
+
+ 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 00000000..c93e279f
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-main-icon-view.h
@@ -0,0 +1,42 @@
+/*
+ * 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()
+G_DECLARE_DERIVABLE_TYPE (GdMainIconView, gd_main_icon_view, GD, MAIN_ICON_VIEW, GtkIconView)
+
+struct _GdMainIconViewClass
+{
+ GtkIconViewClass parent_class;
+};
+
+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 00000000..ae985a14
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-main-list-view.c
@@ -0,0 +1,345 @@
+/*
+ * 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_ADD_PRIVATE (GdMainListView)
+ 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;
+
+ 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 = gd_main_list_view_get_instance_private (self);
+
+ 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 00000000..317e9c4b
--- /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 00000000..51347e05
--- /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 00000000..dd53e0ee
--- /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 00000000..008162b6
--- /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 00000000..4f8afe79
--- /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 00000000..20f2046e
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-margin-container.c
@@ -0,0 +1,377 @@
+/*
+ * 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_ADD_PRIVATE (GdMarginContainer)
+ 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 = gd_margin_container_get_instance_private (self);
+
+ 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");
+}
+
+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 00000000..3937ea75
--- /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 00000000..ba006faa
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-notification.c
@@ -0,0 +1,870 @@
+/* -*- 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_WITH_PRIVATE (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 = gd_notification_get_instance_private (notification);
+
+ 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);
+}
+
+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 00000000..8efa191a
--- /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 00000000..bdf72d2a
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-styled-text-renderer.c
@@ -0,0 +1,134 @@
+/*
+ * 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"
+
+typedef struct _GdStyledTextRendererPrivate GdStyledTextRendererPrivate;
+
+struct _GdStyledTextRendererPrivate {
+ GList *style_classes;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GdStyledTextRenderer, gd_styled_text_renderer, GTK_TYPE_CELL_RENDERER_TEXT)
+
+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);
+ GdStyledTextRendererPrivate *priv;
+ GtkStyleContext *context;
+ const gchar *style_class;
+ GList *l;
+
+ priv = gd_styled_text_renderer_get_instance_private (self);
+
+ context = gtk_widget_get_style_context (widget);
+ gtk_style_context_save (context);
+
+ for (l = 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);
+ GdStyledTextRendererPrivate *priv;
+
+ priv = gd_styled_text_renderer_get_instance_private (self);
+
+ if (priv->style_classes != NULL)
+ {
+ g_list_free_full (priv->style_classes, g_free);
+ 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;
+}
+
+static void
+gd_styled_text_renderer_init (GdStyledTextRenderer *self)
+{
+}
+
+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)
+{
+ GdStyledTextRendererPrivate *priv;
+
+ priv = gd_styled_text_renderer_get_instance_private (self);
+
+ if (g_list_find_custom (priv->style_classes, class, (GCompareFunc) g_strcmp0))
+ return;
+
+ priv->style_classes = g_list_append (priv->style_classes, g_strdup (class));
+}
+
+void
+gd_styled_text_renderer_remove_class (GdStyledTextRenderer *self,
+ const gchar *class)
+{
+ GdStyledTextRendererPrivate *priv;
+ GList *class_element;
+
+ priv = gd_styled_text_renderer_get_instance_private (self);
+
+ class_element = g_list_find_custom (priv->style_classes, class, (GCompareFunc) g_strcmp0);
+
+ if (class_element == NULL)
+ return;
+
+ priv->style_classes = g_list_remove_link (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 00000000..42f606b0
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-styled-text-renderer.h
@@ -0,0 +1,51 @@
+/*
+ * 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()
+G_DECLARE_DERIVABLE_TYPE (GdStyledTextRenderer,
+ gd_styled_text_renderer,
+ GD,
+ STYLED_TEXT_RENDERER,
+ GtkCellRendererText)
+
+struct _GdStyledTextRendererClass
+{
+ GtkCellRendererTextClass parent_class;
+};
+
+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 00000000..5db139e8
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-tagged-entry.c
@@ -0,0 +1,1240 @@
+/*
+ * 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_WITH_PRIVATE (GdTaggedEntry, gd_tagged_entry, GTK_TYPE_SEARCH_ENTRY)
+G_DEFINE_TYPE_WITH_PRIVATE (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 = gd_tagged_entry_get_instance_private (self);
+ 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_object_class_install_properties (oclass, NUM_PROPERTIES, properties);
+}
+
+static void
+gd_tagged_entry_tag_init (GdTaggedEntryTag *self)
+{
+ GdTaggedEntryTagPrivate *priv;
+
+ self->priv = gd_tagged_entry_tag_get_instance_private (self);
+ 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_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 00000000..ba9f6731
--- /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 00000000..069050b5
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-toggle-pixbuf-renderer.c
@@ -0,0 +1,280 @@
+/*
+ * 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"
+
+enum {
+ PROP_ACTIVE = 1,
+ PROP_TOGGLE_VISIBLE,
+ PROP_PULSE,
+ NUM_PROPERTIES
+};
+
+static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
+
+typedef struct _GdTogglePixbufRendererPrivate GdTogglePixbufRendererPrivate;
+
+struct _GdTogglePixbufRendererPrivate {
+ gboolean active;
+ gboolean toggle_visible;
+
+ guint pulse;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GdTogglePixbufRenderer, gd_toggle_pixbuf_renderer, GTK_TYPE_CELL_RENDERER_PIXBUF)
+
+static void
+render_check (GdTogglePixbufRenderer *self,
+ cairo_t *cr,
+ GtkWidget *widget,
+ const GdkRectangle *cell_area,
+ gint icon_size,
+ gint xpad,
+ gint ypad)
+{
+ GdTogglePixbufRendererPrivate *priv;
+ GtkStyleContext *context;
+ gint check_x, check_y, x_offset;
+ GtkTextDirection direction;
+
+ priv = gd_toggle_pixbuf_renderer_get_instance_private (self);
+ context = gtk_widget_get_style_context (widget);
+
+ if (!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 (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)
+{
+ GdTogglePixbufRendererPrivate *priv;
+ gint x, y, width, height;
+
+ priv = gd_toggle_pixbuf_renderer_get_instance_private (self);
+
+ if (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) 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);
+ GdTogglePixbufRendererPrivate *priv;
+
+ priv = gd_toggle_pixbuf_renderer_get_instance_private (self);
+
+ switch (property_id)
+ {
+ case PROP_ACTIVE:
+ g_value_set_boolean (value, priv->active);
+ break;
+ case PROP_TOGGLE_VISIBLE:
+ g_value_set_boolean (value, priv->toggle_visible);
+ break;
+ case PROP_PULSE:
+ g_value_set_uint (value, 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);
+ GdTogglePixbufRendererPrivate *priv;
+
+ priv = gd_toggle_pixbuf_renderer_get_instance_private (self);
+
+ switch (property_id)
+ {
+ case PROP_ACTIVE:
+ priv->active = g_value_get_boolean (value);
+ break;
+ case PROP_TOGGLE_VISIBLE:
+ priv->toggle_visible = g_value_get_boolean (value);
+ break;
+ case PROP_PULSE:
+ 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_object_class_install_properties (oclass, NUM_PROPERTIES, properties);
+}
+
+static void
+gd_toggle_pixbuf_renderer_init (GdTogglePixbufRenderer *self)
+{
+}
+
+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 00000000..93f4429c
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-toggle-pixbuf-renderer.h
@@ -0,0 +1,47 @@
+/*
+ * 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()
+G_DECLARE_DERIVABLE_TYPE (GdTogglePixbufRenderer,
+ gd_toggle_pixbuf_renderer,
+ GD,
+ TOGGLE_PIXBUF_RENDERER,
+ GtkCellRendererPixbuf)
+
+struct _GdTogglePixbufRendererClass
+{
+ GtkCellRendererPixbufClass parent_class;
+};
+
+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 00000000..f5e1ce53
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-two-lines-renderer.c
@@ -0,0 +1,632 @@
+/*
+ * 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
+
+typedef struct _GdTwoLinesRendererPrivate GdTwoLinesRendererPrivate;
+
+struct _GdTwoLinesRendererPrivate {
+ gchar *line_two;
+ gint text_lines;
+};
+
+enum {
+ PROP_TEXT_LINES = 1,
+ PROP_LINE_TWO,
+ NUM_PROPERTIES
+};
+
+static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
+
+G_DEFINE_TYPE_WITH_PRIVATE (GdTwoLinesRenderer, gd_two_lines_renderer, GTK_TYPE_CELL_RENDERER_TEXT)
+
+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)
+{
+ GdTwoLinesRendererPrivate *priv;
+ PangoLayout *line_one;
+ PangoLayout *line_two = NULL;
+ gchar *text = NULL;
+
+ priv = gd_two_lines_renderer_get_instance_private (self);
+
+ g_object_get (self,
+ "text", &text,
+ NULL);
+
+ line_one = create_layout_with_attrs (widget, cell_area,
+ self, PANGO_ELLIPSIZE_MIDDLE);
+
+ if (priv->line_two == NULL ||
+ g_strcmp0 (priv->line_two, "") == 0)
+ {
+ pango_layout_set_height (line_one, - (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, - (priv->text_lines - 1));
+ pango_layout_set_height (line_two, -1);
+ pango_layout_set_text (line_two, 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;
+ render_area.y += y_offset;
+
+ 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 += y_offset + 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)
+{
+ gint x_offset, x_offset_1, x_offset_2, y_offset;
+
+ /* fetch common information */
+ gd_two_lines_renderer_get_size (cell, widget,
+ NULL, NULL,
+ &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 + y_offset;
+}
+
+static void
+gd_two_lines_renderer_set_line_two (GdTwoLinesRenderer *self,
+ const gchar *line_two)
+{
+ GdTwoLinesRendererPrivate *priv;
+
+ priv = gd_two_lines_renderer_get_instance_private (self);
+
+ if (g_strcmp0 (priv->line_two, line_two) == 0)
+ return;
+
+ g_free (priv->line_two);
+ 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)
+{
+ GdTwoLinesRendererPrivate *priv;
+
+ priv = gd_two_lines_renderer_get_instance_private (self);
+
+ if (priv->text_lines == text_lines)
+ return;
+
+ 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);
+ GdTwoLinesRendererPrivate *priv;
+
+ priv = gd_two_lines_renderer_get_instance_private (self);
+
+ switch (property_id)
+ {
+ case PROP_TEXT_LINES:
+ g_value_set_int (value, priv->text_lines);
+ break;
+ case PROP_LINE_TWO:
+ g_value_set_string (value, 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);
+ GdTwoLinesRendererPrivate *priv;
+
+ priv = gd_two_lines_renderer_get_instance_private (self);
+
+ g_free (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_object_class_install_properties (oclass, NUM_PROPERTIES, properties);
+}
+
+static void
+gd_two_lines_renderer_init (GdTwoLinesRenderer *self)
+{
+}
+
+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 00000000..113191b6
--- /dev/null
+++ b/subprojects/libgd/libgd/gd-two-lines-renderer.h
@@ -0,0 +1,43 @@
+/*
+ * 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()
+G_DECLARE_DERIVABLE_TYPE (GdTwoLinesRenderer, gd_two_lines_renderer, GD, TWO_LINES_RENDERER, GtkCellRendererText)
+
+struct _GdTwoLinesRendererClass
+{
+ GtkCellRendererTextClass parent_class;
+};
+
+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 00000000..75f7d577
--- /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 00000000..fc99416b
--- /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 00000000..69685c97
--- /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 00000000..6ba7871f
--- /dev/null
+++ b/subprojects/libgd/libgd/meson.build
@@ -0,0 +1,204 @@
+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') or
+ get_option('with-main-box') or
+ get_option('with-main-icon-box') or
+ get_option('with-main-view'))
+ 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-box') or
+ 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 00000000..6d1242c0
--- /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 00000000..fcab3a07
--- /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 00000000..c3e7f26e
--- /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 00000000..465ab83d
--- /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 00000000..0f583d9b
--- /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;
+}
diff --git a/subprojects/shared-modules b/subprojects/shared-modules
deleted file mode 160000
-Subproject 722c257817b83e4cce12bc7b8361ced75e09c90
diff --git a/subprojects/shared-modules/CODEOWNERS b/subprojects/shared-modules/CODEOWNERS
new file mode 100644
index 00000000..d8841d17
--- /dev/null
+++ b/subprojects/shared-modules/CODEOWNERS
@@ -0,0 +1,11 @@
+# Fallback on Flathub admins for unowned shared modules
+* @flathub/reviewers
+
+/dbus-glib/ @TingPing
+/gtk2/ @TingPing
+/gudev/ @Erick555
+/intltool/ @TingPing
+/libappindicator/ @TingPing
+/libsecret/ @Lctrs
+/openjpeg/ @bochecha
+/python2.7/ @bilelmoussaoui
diff --git a/subprojects/shared-modules/README.md b/subprojects/shared-modules/README.md
new file mode 100644
index 00000000..5a0ec035
--- /dev/null
+++ b/subprojects/shared-modules/README.md
@@ -0,0 +1,29 @@
+This repository contains commonly shared modules and is intended to be used as a git submodule.
+
+To use shared modules for packaging an application, add the submodule:
+
+```
+git submodule add https://github.com/flathub/shared-modules.git
+```
+
+Then modules from this repository can be specified in a manifest JSON file like this:
+
+```json
+"modules": [
+ "shared-modules/SDL/SDL-1.2.15.json",
+ {
+ "name": "foo"
+ }
+]
+```
+
+To update the submodule:
+
+```
+git submodule update --remote --merge
+```
+
+
+[See the description in the Flathub wiki](https://github.com/flathub/flathub/wiki/App-Requirements#shared-modules) for more information.
+
+Please do not request adding modules unless they have many users in the Flathub repository.
diff --git a/subprojects/shared-modules/SDL/SDL-1.2.15.json b/subprojects/shared-modules/SDL/SDL-1.2.15.json
new file mode 100644
index 00000000..4d00288b
--- /dev/null
+++ b/subprojects/shared-modules/SDL/SDL-1.2.15.json
@@ -0,0 +1,40 @@
+{
+ "name": "SDL1",
+ "rm-configure": true,
+ "config-opts": ["--disable-static"],
+ "cleanup": [
+ "/bin",
+ "/share/man",
+ "/share/aclocal",
+ "/include",
+ "/lib/pkgconfig",
+ "/lib/*.la",
+ "/lib/*.a"
+ ],
+ "sources": [
+ {
+ "type": "archive",
+ "url": "https://www.libsdl.org/release/SDL-1.2.15.tar.gz",
+ "sha256": "d6d316a793e5e348155f0dd93b979798933fb98aa1edebcc108829d6474aad00"
+ },
+ {
+ "type": "patch",
+ "path": "sdl-libx11-build.patch"
+ },
+ {
+ "type": "patch",
+ "path": "sdl-check-for-SDL_VIDEO_X11_BACKINGSTORE.patch"
+ },
+ {
+ "type": "script",
+ "dest-filename": "autogen.sh",
+ "commands": [
+ "sed -i -e 's/.*AM_PATH_ESD.*//' configure.in",
+ "cp -p /usr/share/automake-*/config.{sub,guess} build-scripts",
+ "aclocal",
+ "libtoolize",
+ "autoconf"
+ ]
+ }
+ ]
+}
diff --git a/subprojects/shared-modules/SDL/SDL_Pango-0.1.2-API-adds.patch b/subprojects/shared-modules/SDL/SDL_Pango-0.1.2-API-adds.patch
new file mode 100644
index 00000000..6b1f2d05
--- /dev/null
+++ b/subprojects/shared-modules/SDL/SDL_Pango-0.1.2-API-adds.patch
@@ -0,0 +1,118 @@
+diff -Naupr SDL_Pango-0.1.2.orig/src/SDL_Pango.c SDL_Pango-0.1.2/src/SDL_Pango.c
+--- SDL_Pango-0.1.2.orig/src/SDL_Pango.c 2004-12-10 10:06:33.000000000 +0100
++++ SDL_Pango-0.1.2/src/SDL_Pango.c 2006-09-29 17:42:09.000000000 +0200
+@@ -723,13 +723,8 @@ SDLPango_CopyFTBitmapToSurface(
+ SDL_UnlockSurface(surface);
+ }
+
+-/*!
+- Create a context which contains Pango objects.
+-
+- @return A pointer to the context as a SDLPango_Context*.
+-*/
+ SDLPango_Context*
+-SDLPango_CreateContext()
++SDLPango_CreateContext_GivenFontDesc(const char* font_desc)
+ {
+ SDLPango_Context *context = g_malloc(sizeof(SDLPango_Context));
+ G_CONST_RETURN char *charset;
+@@ -743,8 +738,7 @@ SDLPango_CreateContext()
+ pango_context_set_language (context->context, pango_language_from_string (charset));
+ pango_context_set_base_dir (context->context, PANGO_DIRECTION_LTR);
+
+- context->font_desc = pango_font_description_from_string(
+- MAKE_FONT_NAME (DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE));
++ context->font_desc = pango_font_description_from_string(font_desc);
+
+ context->layout = pango_layout_new (context->context);
+
+@@ -762,6 +756,17 @@ SDLPango_CreateContext()
+ }
+
+ /*!
++ Create a context which contains Pango objects.
++
++ @return A pointer to the context as a SDLPango_Context*.
++*/
++SDLPango_Context*
++SDLPango_CreateContext()
++{
++ SDLPango_CreateContext_GivenFontDesc(MAKE_FONT_NAME(DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE));
++}
++
++/*!
+ Free a context.
+
+ @param *context [i/o] Context to be free
+@@ -1053,6 +1058,20 @@ SDLPango_SetMarkup(
+ pango_layout_set_font_description (context->layout, context->font_desc);
+ }
+
++void
++SDLPango_SetText_GivenAlignment(
++ SDLPango_Context *context,
++ const char *text,
++ int length,
++ SDLPango_Alignment alignment)
++{
++ pango_layout_set_attributes(context->layout, NULL);
++ pango_layout_set_text (context->layout, text, length);
++ pango_layout_set_auto_dir (context->layout, TRUE);
++ pango_layout_set_alignment (context->layout, alignment);
++ pango_layout_set_font_description (context->layout, context->font_desc);
++}
++
+ /*!
+ Set plain text to context.
+ Text must be utf-8.
+@@ -1067,11 +1086,7 @@ SDLPango_SetText(
+ const char *text,
+ int length)
+ {
+- pango_layout_set_attributes(context->layout, NULL);
+- pango_layout_set_text (context->layout, text, length);
+- pango_layout_set_auto_dir (context->layout, TRUE);
+- pango_layout_set_alignment (context->layout, PANGO_ALIGN_LEFT);
+- pango_layout_set_font_description (context->layout, context->font_desc);
++ SDLPango_SetText_GivenAlignment(context, text, length, SDLPANGO_ALIGN_LEFT);
+ }
+
+ /*!
+diff -Naupr SDL_Pango-0.1.2.orig/src/SDL_Pango.h SDL_Pango-0.1.2/src/SDL_Pango.h
+--- SDL_Pango-0.1.2.orig/src/SDL_Pango.h 2004-12-10 10:06:33.000000000 +0100
++++ SDL_Pango-0.1.2/src/SDL_Pango.h 2006-09-29 17:42:09.000000000 +0200
+@@ -109,12 +109,20 @@ typedef enum {
+ SDLPANGO_DIRECTION_NEUTRAL /*! Neutral */
+ } SDLPango_Direction;
+
+-
++/*!
++ Specifies alignment of text. See Pango reference for detail
++*/
++typedef enum {
++ SDLPANGO_ALIGN_LEFT,
++ SDLPANGO_ALIGN_CENTER,
++ SDLPANGO_ALIGN_RIGHT
++} SDLPango_Alignment;
+
+ extern DECLSPEC int SDLCALL SDLPango_Init();
+
+ extern DECLSPEC int SDLCALL SDLPango_WasInit();
+
++extern DECLSPEC SDLPango_Context* SDLCALL SDLPango_CreateContext_GivenFontDesc(const char* font_desc);
+ extern DECLSPEC SDLPango_Context* SDLCALL SDLPango_CreateContext();
+
+ extern DECLSPEC void SDLCALL SDLPango_FreeContext(
+@@ -157,6 +165,12 @@ extern DECLSPEC void SDLCALL SDLPango_Se
+ const char *markup,
+ int length);
+
++extern DECLSPEC void SDLCALL SDLPango_SetText_GivenAlignment(
++ SDLPango_Context *context,
++ const char *text,
++ int length,
++ SDLPango_Alignment alignment);
++
+ extern DECLSPEC void SDLCALL SDLPango_SetText(
+ SDLPango_Context *context,
+ const char *markup,
diff --git a/subprojects/shared-modules/SDL/SDL_image-1.2.12.json b/subprojects/shared-modules/SDL/SDL_image-1.2.12.json
new file mode 100644
index 00000000..5e1692b6
--- /dev/null
+++ b/subprojects/shared-modules/SDL/SDL_image-1.2.12.json
@@ -0,0 +1,19 @@
+{
+ "name": "SDL_image",
+ "config-opts": ["--disable-static"],
+ "rm-configure": true,
+ "sources": [
+ {
+ "type": "archive",
+ "url": "https://www.libsdl.org/projects/SDL_image/release/SDL_image-1.2.12.tar.gz",
+ "sha256": "0b90722984561004de84847744d566809dbb9daf732a9e503b91a1b5a84e5699"
+ },
+ {
+ "type": "script",
+ "dest-filename": "autogen.sh",
+ "commands": [
+ "AUTOMAKE=\"automake --foreign\" autoreconf -vfi"
+ ]
+ }
+ ]
+}
diff --git a/subprojects/shared-modules/SDL/SDL_mixer-1.2.12.json b/subprojects/shared-modules/SDL/SDL_mixer-1.2.12.json
new file mode 100644
index 00000000..ba5a6276
--- /dev/null
+++ b/subprojects/shared-modules/SDL/SDL_mixer-1.2.12.json
@@ -0,0 +1,22 @@
+{
+ "name": "SDL_mixer",
+ "config-opts": ["--disable-static"],
+ "rm-configure": true,
+ "sources": [
+ {
+ "type": "archive",
+ "url": "https://www.libsdl.org/projects/SDL_mixer/release/SDL_mixer-1.2.12.tar.gz",
+ "sha256": "1644308279a975799049e4826af2cfc787cad2abb11aa14562e402521f86992a"
+ },
+ {
+ "type": "script",
+ "dest-filename": "autogen.sh",
+ "commands": [
+ "rm acinclude/libtool.m4",
+ "rm acinclude/lt*",
+ "AUTOMAKE=\"automake --foreign\" autoreconf -vfi -I acinclude",
+ "cp -p /usr/share/automake-*/config.{sub,guess} build-scripts"
+ ]
+ }
+ ]
+}
diff --git a/subprojects/shared-modules/SDL/SDL_net-1.2.8.json b/subprojects/shared-modules/SDL/SDL_net-1.2.8.json
new file mode 100644
index 00000000..9d3e896c
--- /dev/null
+++ b/subprojects/shared-modules/SDL/SDL_net-1.2.8.json
@@ -0,0 +1,19 @@
+{
+ "name": "SDL_net",
+ "config-opts": ["--disable-static"],
+ "rm-configure": true,
+ "sources": [
+ {
+ "type": "archive",
+ "url": "https://www.libsdl.org/projects/SDL_net/release/SDL_net-1.2.8.tar.gz",
+ "sha256": "5f4a7a8bb884f793c278ac3f3713be41980c5eedccecff0260411347714facb4"
+ },
+ {
+ "type": "script",
+ "dest-filename": "autogen.sh",
+ "commands": [
+ "AUTOMAKE=\"automake --foreign\" autoreconf -vfi"
+ ]
+ }
+ ]
+}
diff --git a/subprojects/shared-modules/SDL/SDL_pango-0.1.2.json b/subprojects/shared-modules/SDL/SDL_pango-0.1.2.json
new file mode 100644
index 00000000..fdbef4d1
--- /dev/null
+++ b/subprojects/shared-modules/SDL/SDL_pango-0.1.2.json
@@ -0,0 +1,23 @@
+{
+ "name": "SDL_pango",
+ "config-opts": ["--disable-static"],
+ "rm-configure": true,
+ "sources": [
+ {
+ "type": "archive",
+ "url": "https://downloads.sourceforge.net/project/sdlpango/SDL_Pango/0.1.2/SDL_Pango-0.1.2.tar.gz",
+ "sha256": "7f75d3b97acf707c696ea126424906204ebfa07660162de925173cdd0257eba4"
+ },
+ {
+ "type": "patch",
+ "path": "SDL_Pango-0.1.2-API-adds.patch"
+ },
+ {
+ "type": "script",
+ "dest-filename": "autogen.sh",
+ "commands": [
+ "autoreconf -vfi"
+ ]
+ }
+ ]
+}
diff --git a/subprojects/shared-modules/SDL/SDL_ttf-2.0.11.json b/subprojects/shared-modules/SDL/SDL_ttf-2.0.11.json
new file mode 100644
index 00000000..5e928e46
--- /dev/null
+++ b/subprojects/shared-modules/SDL/SDL_ttf-2.0.11.json
@@ -0,0 +1,19 @@
+{
+ "name": "SDL_ttf",
+ "config-opts": ["--disable-static"],
+ "rm-configure": true,
+ "sources": [
+ {
+ "type": "archive",
+ "url": "https://www.libsdl.org/projects/SDL_ttf/release/SDL_ttf-2.0.11.tar.gz",
+ "sha256": "724cd895ecf4da319a3ef164892b72078bd92632a5d812111261cde248ebcdb7"
+ },
+ {
+ "type": "script",
+ "dest-filename": "autogen.sh",
+ "commands": [
+ "AUTOMAKE=\"automake --foreign\" autoreconf -vfi"
+ ]
+ }
+ ]
+}
diff --git a/subprojects/shared-modules/SDL/sdl-check-for-SDL_VIDEO_X11_BACKINGSTORE.patch b/subprojects/shared-modules/SDL/sdl-check-for-SDL_VIDEO_X11_BACKINGSTORE.patch
new file mode 100644
index 00000000..c29811d7
--- /dev/null
+++ b/subprojects/shared-modules/SDL/sdl-check-for-SDL_VIDEO_X11_BACKINGSTORE.patch
@@ -0,0 +1,24 @@
+Description: Do not harness backing store by default
+ xorg-server 1.15 enables backing store if composite extension is enabled
+ (default settings). Harnessing backing store through compositor leads to
+ tearing effect.
+ This patch reverts default harnessing backing store to conditional use if
+ SDL_VIDEO_X11_BACKINGSTORE environment variable exists.
+Origin: https://bugs.launchpad.net/ubuntu/+source/libsdl1.2/+bug/1280665/comments/1
+Bug: https://bugzilla.libsdl.org/show_bug.cgi?id=2383
+Bug-Debian: https://bugs.debian.org/747168
+
+--- a/src/video/x11/SDL_x11video.c
++++ b/src/video/x11/SDL_x11video.c
+@@ -1088,10 +1088,8 @@
+ }
+ }
+
+-#if 0 /* This is an experiment - are the graphics faster now? - nope. */
+ if ( SDL_getenv("SDL_VIDEO_X11_BACKINGSTORE") )
+-#endif
+- /* Cache the window in the server, when possible */
++ /* Cache the window in the server when possible, on request */
+ {
+ Screen *xscreen;
+ XSetWindowAttributes a;
diff --git a/subprojects/shared-modules/SDL/sdl-libx11-build.patch b/subprojects/shared-modules/SDL/sdl-libx11-build.patch
new file mode 100644
index 00000000..5bb14d4b
--- /dev/null
+++ b/subprojects/shared-modules/SDL/sdl-libx11-build.patch
@@ -0,0 +1,59 @@
+
+# HG changeset patch
+# User Azamat H. Hackimov <azamat.hackimov@gmail.com>
+# Date 1370184533 -21600
+# Node ID 91ad7b43317a6387e115ecdf63a49137f47e42c8
+# Parent f7fd5c3951b9ed922fdf696f7182e71b58a13268
+Fix compilation with libX11 >= 1.5.99.902.
+
+These changes fixes bug #1769 for SDL 1.2
+(http://bugzilla.libsdl.org/show_bug.cgi?id=1769).
+
+diff -r f7fd5c3951b9 -r 91ad7b43317a configure.in
+--- a/configure.in Wed Apr 17 00:56:53 2013 -0700
++++ b/configure.in Sun Jun 02 20:48:53 2013 +0600
+@@ -1169,6 +1169,17 @@
+ if test x$definitely_enable_video_x11_xrandr = xyes; then
+ AC_DEFINE(SDL_VIDEO_DRIVER_X11_XRANDR)
+ fi
++ AC_MSG_CHECKING(for const parameter to _XData32)
++ have_const_param_xdata32=no
++ AC_TRY_COMPILE([
++ #include <X11/Xlibint.h>
++ extern int _XData32(Display *dpy,register _Xconst long *data,unsigned len);
++ ],[
++ ],[
++ have_const_param_xdata32=yes
++ AC_DEFINE(SDL_VIDEO_DRIVER_X11_CONST_PARAM_XDATA32)
++ ])
++ AC_MSG_RESULT($have_const_param_xdata32)
+ fi
+ fi
+ }
+diff -r f7fd5c3951b9 -r 91ad7b43317a include/SDL_config.h.in
+--- a/include/SDL_config.h.in Wed Apr 17 00:56:53 2013 -0700
++++ b/include/SDL_config.h.in Sun Jun 02 20:48:53 2013 +0600
+@@ -283,6 +283,7 @@
+ #undef SDL_VIDEO_DRIVER_WINDIB
+ #undef SDL_VIDEO_DRIVER_WSCONS
+ #undef SDL_VIDEO_DRIVER_X11
++#undef SDL_VIDEO_DRIVER_X11_CONST_PARAM_XDATA32
+ #undef SDL_VIDEO_DRIVER_X11_DGAMOUSE
+ #undef SDL_VIDEO_DRIVER_X11_DYNAMIC
+ #undef SDL_VIDEO_DRIVER_X11_DYNAMIC_XEXT
+diff -r f7fd5c3951b9 -r 91ad7b43317a src/video/x11/SDL_x11sym.h
+--- a/src/video/x11/SDL_x11sym.h Wed Apr 17 00:56:53 2013 -0700
++++ b/src/video/x11/SDL_x11sym.h Sun Jun 02 20:48:53 2013 +0600
+@@ -165,7 +165,11 @@
+ */
+ #ifdef LONG64
+ SDL_X11_MODULE(IO_32BIT)
++#if SDL_VIDEO_DRIVER_X11_CONST_PARAM_XDATA32
++SDL_X11_SYM(int,_XData32,(Display *dpy,register _Xconst long *data,unsigned len),(dpy,data,len),return)
++#else
+ SDL_X11_SYM(int,_XData32,(Display *dpy,register long *data,unsigned len),(dpy,data,len),return)
++#endif
+ SDL_X11_SYM(void,_XRead32,(Display *dpy,register long *data,long len),(dpy,data,len),)
+ #endif
+
+
diff --git a/subprojects/shared-modules/cld2/CMakeLists.txt b/subprojects/shared-modules/cld2/CMakeLists.txt
new file mode 100644
index 00000000..d25f857f
--- /dev/null
+++ b/subprojects/shared-modules/cld2/CMakeLists.txt
@@ -0,0 +1,155 @@
+cmake_minimum_required(VERSION 2.8)
+project (cld2)
+enable_language(CXX)
+
+set (VERSION "0.0.197")
+set (common_SOURCE_FILES
+ internal/cldutil.cc
+ internal/cldutil_shared.cc
+ internal/compact_lang_det.cc
+ internal/compact_lang_det_hint_code.cc
+ internal/compact_lang_det_impl.cc
+ internal/debug.cc
+ internal/fixunicodevalue.cc
+ internal/generated_entities.cc
+ internal/generated_language.cc
+ internal/generated_ulscript.cc
+ internal/getonescriptspan.cc
+ internal/lang_script.cc
+ internal/offsetmap.cc
+ internal/scoreonescriptspan.cc
+ internal/tote.cc
+ internal/utf8statetable.cc
+ )
+
+set (cld2_SOURCE_FILES
+ internal/generated_distinct_bi_0.cc
+ internal/cld_generated_cjk_uni_prop_80.cc
+ internal/cld2_generated_cjk_compatible.cc
+ internal/cld_generated_cjk_delta_bi_4.cc
+ internal/cld2_generated_quadchrome_2.cc
+ internal/cld2_generated_deltaoctachrome.cc
+ internal/cld2_generated_distinctoctachrome.cc
+ internal/cld_generated_score_quad_octa_2.cc
+ )
+
+set (cld2_full_SOURCE_FILES
+ internal/generated_distinct_bi_0.cc
+ internal/cld_generated_cjk_uni_prop_80.cc
+ internal/cld2_generated_cjk_compatible.cc
+ internal/cld_generated_cjk_delta_bi_32.cc
+ internal/cld2_generated_quad0122.cc
+ internal/cld2_generated_deltaocta0122.cc
+ internal/cld2_generated_distinctocta0122.cc
+ internal/cld_generated_score_quad_octa_0122.cc
+ )
+
+set (cld2_dynamic_SOURCE_FILES
+ internal/cld2_dynamic_data.cc
+ internal/cld2_dynamic_data_loader.cc
+ )
+
+add_library(cld2 SHARED ${common_SOURCE_FILES} ${cld2_SOURCE_FILES})
+set_target_properties(cld2 PROPERTIES
+ ENABLE_EXPORTS On
+ OUTPUT_NAME cld2
+ VERSION ${VERSION}
+ SOVERSION 0
+ )
+add_library(cld2_full SHARED ${cld2_full_SOURCE_FILES})
+set_target_properties(cld2_full PROPERTIES
+ ENABLE_EXPORTS On
+ OUTPUT_NAME cld2_full
+ VERSION ${VERSION}
+ SOVERSION 0
+ )
+
+add_library(cld2_dynamic SHARED ${cld2_dynamic_SOURCE_FILES})
+set_target_properties(cld2_dynamic PROPERTIES
+ ENABLE_EXPORTS On
+ OUTPUT_NAME cld2_dynamic
+ VERSION ${VERSION}
+ SOVERSION 0
+ COMPILE_FLAGS "-DCLD2_DYNAMIC_MODE"
+ )
+install(TARGETS cld2 DESTINATION lib/${CMAKE_LIBRARY_ARCHITECTURE})
+install(TARGETS cld2_full DESTINATION lib/${CMAKE_LIBRARY_ARCHITECTURE})
+install(TARGETS cld2_dynamic DESTINATION lib/${CMAKE_LIBRARY_ARCHITECTURE})
+
+set (cld2_internal_HEADERS
+ internal/cld2_dynamic_compat.h
+ internal/cld2_dynamic_data_extractor.h
+ internal/cld2_dynamic_data.h
+ internal/cld2_dynamic_data_loader.h
+ internal/cld2tablesummary.h
+ internal/cldutil.h
+ internal/cldutil_offline.h
+ internal/cldutil_shared.h
+ internal/compact_lang_det_hint_code.h
+ internal/compact_lang_det_impl.h
+ internal/debug.h
+ internal/fixunicodevalue.h
+ internal/generated_language.h
+ internal/generated_ulscript.h
+ internal/getonescriptspan.h
+ internal/integral_types.h
+ internal/lang_script.h
+ internal/langspan.h
+ internal/offsetmap.h
+ internal/port.h
+ internal/scoreonescriptspan.h
+ internal/stringpiece.h
+ internal/tote.h
+ internal/unittest_data.h
+ internal/utf8acceptinterchange.h
+ internal/utf8prop_lettermarkscriptnum.h
+ internal/utf8repl_lettermarklower.h
+ internal/utf8scannot_lettermarkspecial.h
+ internal/utf8statetable.h
+ )
+
+install(FILES ${cld2_internal_HEADERS} DESTINATION include/cld2/internal)
+set (cld2_public_HEADERS
+ public/compact_lang_det.h
+ public/encodings.h
+ )
+install(FILES ${cld2_public_HEADERS} DESTINATION include/cld2/public)
+
+set (full_SOURCE_FILES
+ internal/cld_generated_cjk_uni_prop_80.cc
+ internal/cld2_generated_cjk_compatible.cc
+ internal/cld_generated_cjk_delta_bi_32.cc
+ internal/generated_distinct_bi_0.cc
+ internal/cld2_generated_quad0122.cc
+ internal/cld2_generated_deltaocta0122.cc
+ internal/cld2_generated_distinctocta0122.cc
+ internal/cld_generated_score_quad_octa_0122.cc
+ )
+
+add_executable(compact_lang_det_test_full ${full_SOURCE_FILES} internal/compact_lang_det_test.cc)
+add_executable(cld2_unittest_full ${full_SOURCE_FILES} internal/cld2_unittest_full.cc)
+add_executable(cld2_unittest_full_avoid ${full_SOURCE_FILES} internal/cld2_unittest_full.cc)
+set_target_properties(cld2_unittest_full_avoid PROPERTIES COMPILE_FLAGS "-Davoid_utf8_string_constants")
+
+add_executable(cld2_dynamic_data_tool internal/cld2_dynamic_data_extractor.cc internal/cld2_dynamic_data_tool.cc)
+add_executable(compact_lang_det_dynamic_test_chrome ${common_SOURCE_FILES} internal/cld2_dynamic_data_extractor.cc internal/compact_lang_det_test.cc)
+add_executable(cld2_dynamic_unittest ${common_SOURCE_FILES} internal/cld2_unittest.cc)
+set_target_properties(compact_lang_det_dynamic_test_chrome PROPERTIES COMPILE_FLAGS "-DCLD2_DYNAMIC_MODE")
+set_target_properties(cld2_dynamic_unittest PROPERTIES COMPILE_FLAGS "-DCLD2_DYNAMIC_MODE")
+
+add_executable(compact_lang_det_test_chrome_2 internal/compact_lang_det_test.cc)
+add_executable(compact_lang_det_test_chrome_16 internal/compact_lang_det_test.cc)
+add_executable(cld2_unittest_chrome_2 internal/cld2_unittest.cc)
+add_executable(cld2_unittest_avoid_chrome_2 internal/cld2_unittest.cc)
+set_target_properties(cld2_unittest_avoid_chrome_2 PROPERTIES COMPILE_FLAGS "-Davoid_utf8_string_constants")
+
+target_link_libraries(compact_lang_det_test_full cld2)
+target_link_libraries(cld2_unittest_full cld2)
+target_link_libraries(cld2_unittest_full_avoid cld2)
+target_link_libraries(cld2_dynamic_data_tool cld2 cld2_dynamic)
+target_link_libraries(compact_lang_det_dynamic_test_chrome cld2_dynamic)
+target_link_libraries(cld2_dynamic_unittest cld2_dynamic)
+target_link_libraries(compact_lang_det_test_chrome_2 cld2)
+target_link_libraries(compact_lang_det_test_chrome_16 cld2)
+target_link_libraries(cld2_unittest_chrome_2 cld2)
+target_link_libraries(cld2_unittest_avoid_chrome_2 cld2)
diff --git a/subprojects/shared-modules/cld2/cld2.json b/subprojects/shared-modules/cld2/cld2.json
new file mode 100644
index 00000000..8487e31e
--- /dev/null
+++ b/subprojects/shared-modules/cld2/cld2.json
@@ -0,0 +1,24 @@
+{
+ "name": "cld2",
+ "buildsystem": "simple",
+ "build-options": {
+ "cxxflags": "-std=c++98"
+ },
+ "build-commands": [
+ "cp CMakeLists.txt ./cld2",
+ "cd cld2 && mkdir build && cd build && cmake .. -DCMAKE_INSTALL_PREFIX=/app -DCMAKE_BUILD_TYPE=Release",
+ "cd cld2/build && make && make install"
+ ],
+ "sources":[
+ {
+ "type": "git",
+ "url": "https://github.com/CLD2Owners/cld2.git",
+ "commit": "84b58a5d7690ebf05a91406f371ce00c3daf31c0",
+ "dest": "cld2"
+ },
+ {
+ "type": "file",
+ "path": "CMakeLists.txt"
+ }
+ ]
+}
diff --git a/subprojects/shared-modules/dbus-glib/dbus-glib-0.110.json b/subprojects/shared-modules/dbus-glib/dbus-glib-0.110.json
new file mode 100644
index 00000000..73ffd084
--- /dev/null
+++ b/subprojects/shared-modules/dbus-glib/dbus-glib-0.110.json
@@ -0,0 +1,23 @@
+{
+ "name": "dbus-glib",
+ "cleanup": [
+ "*.la",
+ "/bin",
+ "/etc",
+ "/include",
+ "/libexec",
+ "/share/gtk-doc",
+ "/share/man"
+ ],
+ "config-opts": [
+ "--disable-static",
+ "--disable-gtk-doc"
+ ],
+ "sources": [
+ {
+ "type": "archive",
+ "url": "https://dbus.freedesktop.org/releases/dbus-glib/dbus-glib-0.110.tar.gz",
+ "sha256": "7ce4760cf66c69148f6bd6c92feaabb8812dee30846b24cd0f7395c436d7e825"
+ }
+ ]
+}
diff --git a/subprojects/shared-modules/glew/glew.json b/subprojects/shared-modules/glew/glew.json
new file mode 100644
index 00000000..6ec15bf7
--- /dev/null
+++ b/subprojects/shared-modules/glew/glew.json
@@ -0,0 +1,26 @@
+{
+ "name": "glew",
+ "no-autogen": true,
+ "make-args": [
+ "GLEW_PREFIX=/app",
+ "GLEW_DEST=/app",
+ "LIBDIR=/app/lib"
+ ],
+ "make-install-args": [
+ "GLEW_PREFIX=/app",
+ "GLEW_DEST=/app",
+ "LIBDIR=/app/lib"
+ ],
+ "sources": [
+ {
+ "type": "archive",
+ "url": "https://downloads.sourceforge.net/project/glew/glew/2.1.0/glew-2.1.0.tgz",
+ "sha256": "04de91e7e6763039bc11940095cd9c7f880baba82196a7765f727ac05a993c95"
+ }
+ ],
+ "cleanup": [
+ "/include",
+ "/lib/pkgconfig",
+ "/lib/*.a"
+ ]
+}
diff --git a/subprojects/shared-modules/glu/glu-9.json b/subprojects/shared-modules/glu/glu-9.json
new file mode 100644
index 00000000..d14e2219
--- /dev/null
+++ b/subprojects/shared-modules/glu/glu-9.json
@@ -0,0 +1,12 @@
+{
+ "name": "glu",
+ "config-opts": ["--disable-static"],
+ "sources": [
+ {
+ "type": "archive",
+ "url": "https://mesa.freedesktop.org/archive/glu/glu-9.0.1.tar.xz",
+ "sha256": "fb5a4c2dd6ba6d1c21ab7c05129b0769544e1d68e1e3b0ffecb18e73c93055bc"
+ }
+ ],
+ "cleanup": [ "/include", "/lib/*.a", "/lib/*.la", "/lib/pkgconfig" ]
+}
diff --git a/subprojects/shared-modules/gtk2/gtk2-use-adwaita-theme.patch b/subprojects/shared-modules/gtk2/gtk2-use-adwaita-theme.patch
new file mode 100644
index 00000000..916b3491
--- /dev/null
+++ b/subprojects/shared-modules/gtk2/gtk2-use-adwaita-theme.patch
@@ -0,0 +1,80 @@
+diff --git a/gdk/x11/gdkevents-x11.c b/gdk/x11/gdkevents-x11.c
+index 186a8f5cb2..f5c39b5afe 100644
+--- a/gdk/x11/gdkevents-x11.c
++++ b/gdk/x11/gdkevents-x11.c
+@@ -3000,6 +3000,50 @@ check_transform (const gchar *xsettings_name,
+ return TRUE;
+ }
+
++static gchar *
++gtk_rc_get_theme_dir (void)
++{
++ const gchar *var;
++ gchar *path;
++
++ var = g_getenv ("GTK_DATA_PREFIX");
++
++ if (var)
++ path = g_build_filename (var, "share", "themes", NULL);
++ else
++ path = g_build_filename ("/usr", "share", "themes", NULL);
++
++ return path;
++}
++
++static gboolean
++theme_name_valid (XSettingsSetting *setting)
++{
++ gboolean res = FALSE;
++
++ if (setting->type == XSETTINGS_TYPE_STRING)
++ {
++ char *theme_name = setting->data.v_string;
++ gchar *theme_dir = gtk_rc_get_theme_dir ();
++ gchar *path = g_build_filename (theme_dir, theme_name, "gtk-2.0", "gtkrc", NULL);
++
++ if (g_file_test (path, G_FILE_TEST_EXISTS))
++ res = TRUE;
++ else if (g_str_has_suffix (theme_name, "-Dark") ||
++ g_str_has_suffix (theme_name, "-dark"))
++ {
++ setting->data.v_string = g_strdup ("Adwaita-dark");
++ g_free (theme_name);
++ res = TRUE;
++ }
++
++ g_free (theme_dir);
++ g_free (path);
++ }
++
++ return res;
++}
++
+ /**
+ * gdk_screen_get_setting:
+ * @screen: the #GdkScreen where the setting is located
+@@ -3050,6 +3094,11 @@ gdk_screen_get_setting (GdkScreen *screen,
+ if (result != XSETTINGS_SUCCESS)
+ goto out;
+
++ if (strcmp (name, "gtk-theme-name") == 0 &&
++ (setting->type != XSETTINGS_TYPE_STRING ||
++ !theme_name_valid (setting)))
++ goto out;
++
+ switch (setting->type)
+ {
+ case XSETTINGS_TYPE_INT:
+diff --git a/gtk/gtksettings.c b/gtk/gtksettings.c
+index 3fbbf00548..5c0a4b33d2 100644
+--- a/gtk/gtksettings.c
++++ b/gtk/gtksettings.c
+@@ -312,7 +312,7 @@ gtk_settings_class_init (GtkSettingsClass *class)
+ #ifdef G_OS_WIN32
+ "MS-Windows",
+ #else
+- "Raleigh",
++ "Adwaita",
+ #endif
+ GTK_PARAM_READWRITE),
+ NULL);
diff --git a/subprojects/shared-modules/gtk2/gtk2.json b/subprojects/shared-modules/gtk2/gtk2.json
new file mode 100644
index 00000000..cb39de7d
--- /dev/null
+++ b/subprojects/shared-modules/gtk2/gtk2.json
@@ -0,0 +1,109 @@
+{
+ "name": "gnome-themes-extra",
+ "rm-configure": true,
+ "config-opts": [
+ "--disable-dependency-tracking",
+ "--disable-gtk3-engine"
+ ],
+ "cleanup": [
+ "/share/themes/Adwaita/gtk-3.0",
+ "/share/themes/Adwaita-dark/gtk-3.0",
+ "/share/themes/HighContrast/gtk-3.0",
+ "*.la"
+ ],
+ "sources": [
+ {
+ "type": "archive",
+ "url": "https://download.gnome.org/sources/gnome-themes-extra/3.28/gnome-themes-extra-3.28.tar.xz",
+ "sha256": "7c4ba0bff001f06d8983cfc105adaac42df1d1267a2591798a780bac557a5819"
+ },
+ {
+ "type": "shell",
+ "//": "We want to avoid generating icons as its 99% of the build time and gnome runtime has it",
+ "commands": [
+ "sed -i 's/icons//' themes/HighContrast/Makefile.am"
+ ]
+ },
+ {
+ "type": "script",
+ "commands": [
+ "autoreconf -fsi"
+ ]
+ }
+ ],
+ "modules": [
+ "../intltool/intltool-0.51.json",
+ {
+ "name": "gtk2",
+ "cleanup": [
+ "/bin",
+ "/share/gtk-2.0",
+ "/share/aclocal",
+ "/share/gtk-doc",
+ "/lib/pkgconfig",
+ "/lib/gtk-2.0/include",
+ "/include",
+ "*.la"
+ ],
+ "x-cpe": {
+ "product": "gtk+"
+ },
+ "config-opts": [
+ "--disable-dependency-tracking",
+ "--disable-gtk-doc-html",
+ "--disable-introspection",
+ "--with-xinput=xfree"
+ ],
+ "sources": [
+ {
+ "type": "archive",
+ "url": "https://download.gnome.org/sources/gtk+/2.24/gtk+-2.24.32.tar.xz",
+ "sha256": "b6c8a93ddda5eabe3bfee1eb39636c9a03d2a56c7b62828b359bf197943c582e"
+ },
+ {
+ "type": "patch",
+ "path": "gtk2-use-adwaita-theme.patch"
+ }
+ ]
+ },
+ {
+ "name": "ibus-gtk2",
+ "no-make-install": true,
+ "config-opts": [
+ "--disable-xim",
+ "--disable-dconf",
+ "--disable-gconf",
+ "--disable-memconf",
+ "--disable-schemas-compile",
+ "--disable-schemas-install",
+ "--disable-gtk3",
+ "--disable-setup",
+ "--disable-ui",
+ "--disable-engine",
+ "--disable-python-library",
+ "--disable-libnotify",
+ "--disable-emoji-dict",
+ "--disable-appindicator",
+ "--disable-glibtest",
+ "--disable-tests",
+ "--disable-unicode-dict",
+ "--disable-introspection",
+ "--disable-python2"
+ ],
+ "ensure-writable": [
+ "/lib/gtk-2.0/2.10.0/immodules.cache"
+ ],
+ "post-install": [
+ "install -m644 --target-directory=${FLATPAK_DEST}/lib/gtk-2.0/2.10.0/immodules client/gtk2/.libs/im-ibus.so",
+ "gtk-query-immodules-2.0 > ${FLATPAK_DEST}/lib/gtk-2.0/2.10.0/immodules.cache"
+ ],
+ "sources": [
+ {
+ "type": "archive",
+ "url": "https://github.com/ibus/ibus/releases/download/1.5.21/ibus-1.5.21.tar.gz",
+ "sha256": "adc1e5e620d6a89a49306f022b50561f2c05218dec13d0c9b136dc8b6568a2b9"
+ }
+ ]
+ }
+ ]
+}
diff --git a/subprojects/shared-modules/gudev/gudev.json b/subprojects/shared-modules/gudev/gudev.json
new file mode 100644
index 00000000..c86a3f91
--- /dev/null
+++ b/subprojects/shared-modules/gudev/gudev.json
@@ -0,0 +1,27 @@
+{
+ "name": "gudev",
+ "config-opts": [ "--disable-umockdev"],
+ "cleanup": [
+ "/include",
+ "/etc",
+ "/libexec",
+ "/sbin",
+ "/lib/pkgconfig",
+ "/lib/systemd",
+ "/man",
+ "/share/aclocal",
+ "/share/doc",
+ "/share/gtk-doc",
+ "/share/man",
+ "/share/pkgconfig",
+ "*.la",
+ "*.a"
+ ],
+ "sources": [
+ {
+ "type": "archive",
+ "url": "https://download.gnome.org/sources/libgudev/233/libgudev-233.tar.xz",
+ "sha256": "587c4970eb23f4e2deee2cb1fb7838c94a78c578f41ce12cac0a3f4a80dabb03"
+ }
+ ]
+}
diff --git a/subprojects/shared-modules/intltool/intltool-0.51.json b/subprojects/shared-modules/intltool/intltool-0.51.json
new file mode 100644
index 00000000..3a092241
--- /dev/null
+++ b/subprojects/shared-modules/intltool/intltool-0.51.json
@@ -0,0 +1,11 @@
+{
+ "name": "intltool",
+ "cleanup": [ "*" ],
+ "sources": [
+ {
+ "type": "archive",
+ "url": "https://launchpad.net/intltool/trunk/0.51.0/+download/intltool-0.51.0.tar.gz",
+ "sha256": "67c74d94196b153b774ab9f89b2fa6c6ba79352407037c8c14d5aeb334e959cd"
+ }
+ ]
+}
diff --git a/subprojects/shared-modules/lame/lame-3.99.5.json b/subprojects/shared-modules/lame/lame-3.99.5.json
new file mode 100644
index 00000000..3b431dca
--- /dev/null
+++ b/subprojects/shared-modules/lame/lame-3.99.5.json
@@ -0,0 +1,37 @@
+{
+ "name": "lame",
+ "rm-configure": true,
+ "config-opts": ["--disable-static"],
+ "sources": [
+ {
+ "type": "archive",
+ "url": "https://downloads.sourceforge.net/lame/lame-3.99.5.tar.gz",
+ "sha256": "24346b4158e4af3bd9f2e194bb23eb473c75fb7377011523353196b19b9a23ff"
+ },
+ {
+ "type": "patch",
+ "path": "lame-msse.patch"
+ },
+ {
+ "type": "patch",
+ "path": "lame-gtk1-ac-directives.patch"
+ },
+ {
+ "type": "patch",
+ "path": "lame-ansi2knr2devnull.patch"
+ },
+ {
+ "type": "patch",
+ "path": "lame-tinfo.patch",
+ "strip-components": 0
+ },
+ {
+ "type": "script",
+ "dest-filename": "autogen.sh",
+ "commands": [
+ "autoreconf -vfi"
+ ]
+ }
+ ],
+ "cleanup": ["/bin", "/include", "/share/doc", "/share/man", "*.la"]
+}
diff --git a/subprojects/shared-modules/lame/lame-ansi2knr2devnull.patch b/subprojects/shared-modules/lame/lame-ansi2knr2devnull.patch
new file mode 100644
index 00000000..c427345e
--- /dev/null
+++ b/subprojects/shared-modules/lame/lame-ansi2knr2devnull.patch
@@ -0,0 +1,43 @@
+Description: Patch out remaining ansi2knr.
+Author: Dimitri John Ledkov <xnox@ubuntu.com>
+Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=755111
+--- a/configure.in
++++ b/configure.in
+@@ -78,7 +78,6 @@
+ fi
+
+ dnl more automake stuff
+-AM_C_PROTOTYPES
+
+ AC_CHECK_HEADER(dmalloc.h)
+ if test "${ac_cv_header_dmalloc_h}" = "yes"; then
+--- a/doc/man/Makefile.am
++++ b/doc/man/Makefile.am
+@@ -1,6 +1,6 @@
+ ## $Id: Makefile.am,v 1.1 2000/10/22 11:39:44 aleidinger Exp $
+
+-AUTOMAKE_OPTIONS = foreign ansi2knr
++AUTOMAKE_OPTIONS = foreign
+
+ man_MANS = lame.1
+ EXTRA_DIST = ${man_MANS}
+--- a/libmp3lame/i386/Makefile.am
++++ b/libmp3lame/i386/Makefile.am
+@@ -1,6 +1,6 @@
+ ## $Id: Makefile.am,v 1.26 2011/04/04 09:42:34 aleidinger Exp $
+
+-AUTOMAKE_OPTIONS = foreign $(top_srcdir)/ansi2knr
++AUTOMAKE_OPTIONS = foreign
+
+ DEFS = @DEFS@ @CONFIG_DEFS@
+
+--- a/doc/html/Makefile.am
++++ b/doc/html/Makefile.am
+@@ -1,6 +1,6 @@
+ ## $Id: Makefile.am,v 1.7 2010/09/30 20:58:40 jaz001 Exp $
+
+-AUTOMAKE_OPTIONS = foreign ansi2knr
++AUTOMAKE_OPTIONS = foreign
+
+ docdir = $(datadir)/doc
+ pkgdocdir = $(docdir)/$(PACKAGE)
diff --git a/subprojects/shared-modules/lame/lame-gtk1-ac-directives.patch b/subprojects/shared-modules/lame/lame-gtk1-ac-directives.patch
new file mode 100644
index 00000000..7e0d3fe4
--- /dev/null
+++ b/subprojects/shared-modules/lame/lame-gtk1-ac-directives.patch
@@ -0,0 +1,205 @@
+Description: Include GTK-1 autoconf directives in build system.
+Origin: http://anonscm.debian.org/gitweb/?p=pkg-multimedia/lame.git;a=tree;f=debian/patches
+Forwarded: yes
+Applied-Upstream: http://lame.cvs.sf.net/viewvc/lame/lame/acinclude.m4?revision=1.6
+
+--- a/acinclude.m4
++++ b/acinclude.m4
+@@ -85,4 +85,197 @@
+ [AC_MSG_WARN(can't check for IEEE854 compliant 80 bit floats)]
+ )])]) # alex_IEEE854_FLOAT80
+
++# Configure paths for GTK+
++# Owen Taylor 97-11-3
+
++dnl AM_PATH_GTK([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND [, MODULES]]]])
++dnl Test for GTK, and define GTK_CFLAGS and GTK_LIBS
++dnl
++AC_DEFUN([AM_PATH_GTK],
++[dnl
++dnl Get the cflags and libraries from the gtk-config script
++dnl
++AC_ARG_WITH(gtk-prefix,[ --with-gtk-prefix=PFX Prefix where GTK is installed (optional)],
++ gtk_config_prefix="$withval", gtk_config_prefix="")
++AC_ARG_WITH(gtk-exec-prefix,[ --with-gtk-exec-prefix=PFX Exec prefix where GTK is installed (optional)],
++ gtk_config_exec_prefix="$withval", gtk_config_exec_prefix="")
++AC_ARG_ENABLE(gtktest, [ --disable-gtktest Do not try to compile and run a test GTK program],
++ , enable_gtktest=yes)
++
++ for module in . $4
++ do
++ case "$module" in
++ gthread)
++ gtk_config_args="$gtk_config_args gthread"
++ ;;
++ esac
++ done
++
++ if test x$gtk_config_exec_prefix != x ; then
++ gtk_config_args="$gtk_config_args --exec-prefix=$gtk_config_exec_prefix"
++ if test x${GTK_CONFIG+set} != xset ; then
++ GTK_CONFIG=$gtk_config_exec_prefix/bin/gtk-config
++ fi
++ fi
++ if test x$gtk_config_prefix != x ; then
++ gtk_config_args="$gtk_config_args --prefix=$gtk_config_prefix"
++ if test x${GTK_CONFIG+set} != xset ; then
++ GTK_CONFIG=$gtk_config_prefix/bin/gtk-config
++ fi
++ fi
++
++ AC_PATH_PROG(GTK_CONFIG, gtk-config, no)
++ min_gtk_version=ifelse([$1], ,0.99.7,$1)
++ AC_MSG_CHECKING(for GTK - version >= $min_gtk_version)
++ no_gtk=""
++ if test "$GTK_CONFIG" = "no" ; then
++ no_gtk=yes
++ else
++ GTK_CFLAGS=`$GTK_CONFIG $gtk_config_args --cflags`
++ GTK_LIBS=`$GTK_CONFIG $gtk_config_args --libs`
++ gtk_config_major_version=`$GTK_CONFIG $gtk_config_args --version | \
++ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
++ gtk_config_minor_version=`$GTK_CONFIG $gtk_config_args --version | \
++ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
++ gtk_config_micro_version=`$GTK_CONFIG $gtk_config_args --version | \
++ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
++ if test "x$enable_gtktest" = "xyes" ; then
++ ac_save_CFLAGS="$CFLAGS"
++ ac_save_LIBS="$LIBS"
++ CFLAGS="$CFLAGS $GTK_CFLAGS"
++ LIBS="$GTK_LIBS $LIBS"
++dnl
++dnl Now check if the installed GTK is sufficiently new. (Also sanity
++dnl checks the results of gtk-config to some extent
++dnl
++ rm -f conf.gtktest
++ AC_TRY_RUN([
++#include <gtk/gtk.h>
++#include <stdio.h>
++#include <stdlib.h>
++
++int
++main ()
++{
++ int major, minor, micro;
++ char *tmp_version;
++
++ system ("touch conf.gtktest");
++
++ /* HP/UX 9 (%@#!) writes to sscanf strings */
++ tmp_version = g_strdup("$min_gtk_version");
++ if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, &micro) != 3) {
++ printf("%s, bad version string\n", "$min_gtk_version");
++ exit(1);
++ }
++
++ if ((gtk_major_version != $gtk_config_major_version) ||
++ (gtk_minor_version != $gtk_config_minor_version) ||
++ (gtk_micro_version != $gtk_config_micro_version))
++ {
++ printf("\n*** 'gtk-config --version' returned %d.%d.%d, but GTK+ (%d.%d.%d)\n",
++ $gtk_config_major_version, $gtk_config_minor_version, $gtk_config_micro_version,
++ gtk_major_version, gtk_minor_version, gtk_micro_version);
++ printf ("*** was found! If gtk-config was correct, then it is best\n");
++ printf ("*** to remove the old version of GTK+. You may also be able to fix the error\n");
++ printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n");
++ printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n");
++ printf("*** required on your system.\n");
++ printf("*** If gtk-config was wrong, set the environment variable GTK_CONFIG\n");
++ printf("*** to point to the correct copy of gtk-config, and remove the file config.cache\n");
++ printf("*** before re-running configure\n");
++ }
++#if defined (GTK_MAJOR_VERSION) && defined (GTK_MINOR_VERSION) && defined (GTK_MICRO_VERSION)
++ else if ((gtk_major_version != GTK_MAJOR_VERSION) ||
++ (gtk_minor_version != GTK_MINOR_VERSION) ||
++ (gtk_micro_version != GTK_MICRO_VERSION))
++ {
++ printf("*** GTK+ header files (version %d.%d.%d) do not match\n",
++ GTK_MAJOR_VERSION, GTK_MINOR_VERSION, GTK_MICRO_VERSION);
++ printf("*** library (version %d.%d.%d)\n",
++ gtk_major_version, gtk_minor_version, gtk_micro_version);
++ }
++#endif /* defined (GTK_MAJOR_VERSION) ... */
++ else
++ {
++ if ((gtk_major_version > major) ||
++ ((gtk_major_version == major) && (gtk_minor_version > minor)) ||
++ ((gtk_major_version == major) && (gtk_minor_version == minor) && (gtk_micro_version >= micro)))
++ {
++ return 0;
++ }
++ else
++ {
++ printf("\n*** An old version of GTK+ (%d.%d.%d) was found.\n",
++ gtk_major_version, gtk_minor_version, gtk_micro_version);
++ printf("*** You need a version of GTK+ newer than %d.%d.%d. The latest version of\n",
++ major, minor, micro);
++ printf("*** GTK+ is always available from ftp://ftp.gtk.org.\n");
++ printf("***\n");
++ printf("*** If you have already installed a sufficiently new version, this error\n");
++ printf("*** probably means that the wrong copy of the gtk-config shell script is\n");
++ printf("*** being found. The easiest way to fix this is to remove the old version\n");
++ printf("*** of GTK+, but you can also set the GTK_CONFIG environment to point to the\n");
++ printf("*** correct copy of gtk-config. (In this case, you will have to\n");
++ printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n");
++ printf("*** so that the correct libraries are found at run-time))\n");
++ }
++ }
++ return 1;
++}
++],, no_gtk=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
++ CFLAGS="$ac_save_CFLAGS"
++ LIBS="$ac_save_LIBS"
++ fi
++ fi
++ if test "x$no_gtk" = x ; then
++ AC_MSG_RESULT(yes)
++ ifelse([$2], , :, [$2])
++ else
++ AC_MSG_RESULT(no)
++ if test "$GTK_CONFIG" = "no" ; then
++ echo "*** The gtk-config script installed by GTK could not be found"
++ echo "*** If GTK was installed in PREFIX, make sure PREFIX/bin is in"
++ echo "*** your path, or set the GTK_CONFIG environment variable to the"
++ echo "*** full path to gtk-config."
++ else
++ if test -f conf.gtktest ; then
++ :
++ else
++ echo "*** Could not run GTK test program, checking why..."
++ CFLAGS="$CFLAGS $GTK_CFLAGS"
++ LIBS="$LIBS $GTK_LIBS"
++ AC_TRY_LINK([
++#include <gtk/gtk.h>
++#include <stdio.h>
++], [ return ((gtk_major_version) || (gtk_minor_version) || (gtk_micro_version)); ],
++ [ echo "*** The test program compiled, but did not run. This usually means"
++ echo "*** that the run-time linker is not finding GTK or finding the wrong"
++ echo "*** version of GTK. If it is not finding GTK, you'll need to set your"
++ echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
++ echo "*** to the installed location Also, make sure you have run ldconfig if that"
++ echo "*** is required on your system"
++ echo "***"
++ echo "*** If you have an old version installed, it is best to remove it, although"
++ echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"
++ echo "***"
++ echo "*** If you have a RedHat 5.0 system, you should remove the GTK package that"
++ echo "*** came with the system with the command"
++ echo "***"
++ echo "*** rpm --erase --nodeps gtk gtk-devel" ],
++ [ echo "*** The test program failed to compile or link. See the file config.log for the"
++ echo "*** exact error that occured. This usually means GTK was incorrectly installed"
++ echo "*** or that you have moved GTK since it was installed. In the latter case, you"
++ echo "*** may want to edit the gtk-config script: $GTK_CONFIG" ])
++ CFLAGS="$ac_save_CFLAGS"
++ LIBS="$ac_save_LIBS"
++ fi
++ fi
++ GTK_CFLAGS=""
++ GTK_LIBS=""
++ ifelse([$3], , :, [$3])
++ fi
++ AC_SUBST(GTK_CFLAGS)
++ AC_SUBST(GTK_LIBS)
++ rm -f conf.gtktest
++])
diff --git a/subprojects/shared-modules/lame/lame-msse.patch b/subprojects/shared-modules/lame/lame-msse.patch
new file mode 100644
index 00000000..4b71a2ed
--- /dev/null
+++ b/subprojects/shared-modules/lame/lame-msse.patch
@@ -0,0 +1,17 @@
+Description: Build xmm_quantize_sub.c with -msse
+Author: Sebastian Ramacher <sramacher@debian.org>
+Bug: http://sourceforge.net/p/lame/bugs/443/
+Bug-Debian: https://bugs.debian.org/760047
+Forwarded: http://sourceforge.net/p/lame/bugs/443/
+Last-Update: 2014-08-31
+
+--- lame-3.99.5+repack1.orig/libmp3lame/vector/Makefile.am
++++ lame-3.99.5+repack1/libmp3lame/vector/Makefile.am
+@@ -20,6 +20,7 @@ xmm_sources = xmm_quantize_sub.c
+
+ if WITH_XMM
+ liblamevectorroutines_la_SOURCES = $(xmm_sources)
++liblamevectorroutines_la_CFLAGS = -msse
+ endif
+
+ noinst_HEADERS = lame_intrin.h
diff --git a/subprojects/shared-modules/lame/lame-tinfo.patch b/subprojects/shared-modules/lame/lame-tinfo.patch
new file mode 100644
index 00000000..cb570e78
--- /dev/null
+++ b/subprojects/shared-modules/lame/lame-tinfo.patch
@@ -0,0 +1,23 @@
+initscr is not used anywhere in lame sourcetree, check for used tgetent instead
+check for separate tinfo library optionally built out from libncurses source tree,
+like used in debian and gentoo
+- ssuominen@g.o
+
+http://bugs.gentoo.org/454322
+
+--- configure.in
++++ configure.in
+@@ -372,9 +372,10 @@
+
+ AC_CHECK_HEADERS(termcap.h)
+ AC_CHECK_HEADERS(ncurses/termcap.h)
+-AC_CHECK_LIB(termcap, initscr, HAVE_TERMCAP="termcap")
+-AC_CHECK_LIB(curses, initscr, HAVE_TERMCAP="curses")
+-AC_CHECK_LIB(ncurses, initscr, HAVE_TERMCAP="ncurses")
++AC_CHECK_LIB(termcap, tgetent, HAVE_TERMCAP="termcap")
++AC_CHECK_LIB(curses, tgetent, HAVE_TERMCAP="curses")
++AC_CHECK_LIB(ncurses, tgetent, HAVE_TERMCAP="ncurses")
++AC_CHECK_LIB(tinfo, tgetent, HAVE_TERMCAP="tinfo")
+
+ AM_ICONV
+
diff --git a/subprojects/shared-modules/libappindicator/Makefile b/subprojects/shared-modules/libappindicator/Makefile
new file mode 100644
index 00000000..58192455
--- /dev/null
+++ b/subprojects/shared-modules/libappindicator/Makefile
@@ -0,0 +1,10 @@
+all: libappindicator-gtk3-introspection-12.10.json libappindicator-gtk3-12.10.json libappindicator-gtk2-12.10.json
+
+libappindicator-gtk3-introspection-12.10.json: libappindicator.json.in
+ sed -e 's/@GTK_VER@/3/g' -e 's/@INTROSPECTION@/yes/g' $< > $@
+
+libappindicator-gtk3-12.10.json: libappindicator.json.in
+ sed -e 's/@GTK_VER@/3/g' -e 's/@INTROSPECTION@/no/g' $< > $@
+
+libappindicator-gtk2-12.10.json: libappindicator.json.in
+ sed -e 's/@GTK_VER@/2/g' -e 's/@INTROSPECTION@/no/g' $< > $@ \ No newline at end of file
diff --git a/subprojects/shared-modules/libappindicator/libappindicator-ftbfs.patch b/subprojects/shared-modules/libappindicator/libappindicator-ftbfs.patch
new file mode 100644
index 00000000..93d31af6
--- /dev/null
+++ b/subprojects/shared-modules/libappindicator/libappindicator-ftbfs.patch
@@ -0,0 +1,20 @@
+From: Olivier Tilloy <olivier.tilloy@canonical.com>
+Date: Tue 2018-03-20 12:47:56 +0000
+Subject: [PATCH] libappindicator FTBFS on bionic
+
+Fix build failures on bionic,
+and update Vcs-* fields in debian/control. (LP: #1757121)
+
+Approved by: Marco Trevisan (Treviño)
+---
+--- libappindicator-12.10.0/src/app-indicator.c 2017-02-15 14:10:41 +0000
++++ libappindicator-12.10.0/src/app-indicator.c 2018-03-20 12:38:59 +0000
+@@ -2196,7 +2196,7 @@ app_indicator_set_secondary_activate_tar
+
+ g_return_if_fail (GTK_IS_WIDGET (menuitem));
+
+- priv->sec_activate_target = g_object_ref(G_OBJECT(menuitem));
++ priv->sec_activate_target = g_object_ref(menuitem);
+ priv->sec_activate_enabled = widget_is_menu_child(self, menuitem);
+ g_signal_connect(menuitem, "parent-set", G_CALLBACK(sec_activate_target_parent_changed), self);
+ }
diff --git a/subprojects/shared-modules/libappindicator/libappindicator-gtk2-12.10.json b/subprojects/shared-modules/libappindicator/libappindicator-gtk2-12.10.json
new file mode 100644
index 00000000..b65cfa3f
--- /dev/null
+++ b/subprojects/shared-modules/libappindicator/libappindicator-gtk2-12.10.json
@@ -0,0 +1,121 @@
+{
+ "name": "libappindicator",
+ "build-options": {
+ "cflags": "-Wno-error"
+ },
+ "rm-configure": true,
+ "config-opts": [
+ "--disable-static",
+ "--disable-gtk-doc",
+ "--disable-tests",
+ "--disable-mono-tests",
+ "--enable-introspection=no",
+ "--with-gtk=2"
+ ],
+ "cleanup": [
+ "/include",
+ "/lib/pkgconfig",
+ "/lib/*.la",
+ "/share/gtk-doc",
+ "/share/gir-1.0"
+ ],
+ "sources": [
+ {
+ "type": "archive",
+ "url": "https://launchpad.net/libappindicator/12.10/12.10.0/+download/libappindicator-12.10.0.tar.gz",
+ "sha256": "d5907c1f98084acf28fd19593cb70672caa0ca1cf82d747ba6f4830d4cc3b49f"
+ },
+ {
+ "type": "patch",
+ "path": "libappindicator-ftbfs.patch"
+ },
+ {
+ "type": "patch",
+ "path": "libappindicator-no-python.patch"
+ },
+ {
+ "type": "script",
+ "commands": ["autoreconf -sfi"],
+ "dest-filename": "autogen.sh"
+ }
+ ],
+ "modules": [
+ "../intltool/intltool-0.51.json",
+ "../dbus-glib/dbus-glib-0.110.json",
+ {
+ "name": "libdbusmenu",
+ "build-options": {
+ "cflags": "-Wno-error",
+ "env": {
+ "HAVE_VALGRIND_FALSE": "#",
+ "HAVE_VALGRIND_TRUE": ""
+ }
+ },
+ "cleanup": [
+ "/include",
+ "/libexec",
+ "/lib/pkgconfig",
+ "/lib/*.la",
+ "/share/doc",
+ "/share/libdbusmenu",
+ "/share/gtk-doc",
+ "/share/gir-1.0"
+ ],
+ "config-opts": [
+ "--disable-static",
+ "--disable-gtk-doc",
+ "--enable-introspection=no",
+ "--disable-vala",
+ "--disable-dumper",
+ "--disable-tests",
+ "--with-gtk=2"
+ ],
+ "sources": [
+ {
+ "type": "archive",
+ "url": "https://launchpad.net/libdbusmenu/16.04/16.04.0/+download/libdbusmenu-16.04.0.tar.gz",
+ "sha256": "b9cc4a2acd74509435892823607d966d424bd9ad5d0b00938f27240a1bfa878a"
+ }
+ ]
+ },
+ {
+ "name": "libindicator",
+ "build-options": {
+ "cflags": "-Wno-error"
+ },
+ "cleanup": [
+ "/include",
+ "/libexec",
+ "/lib/pkgconfig",
+ "/lib/*.la",
+ "/share/libindicator",
+ "/share/gtk-doc",
+ "/share/gir-1.0"
+ ],
+ "config-opts": [
+ "--disable-static",
+ "--disable-tests",
+ "--with-gtk=2"
+ ],
+ "sources": [
+ {
+ "type": "archive",
+ "url": "https://launchpad.net/libindicator/12.10/12.10.1/+download/libindicator-12.10.1.tar.gz",
+ "sha256": "b2d2e44c10313d5c9cd60db455d520f80b36dc39562df079a3f29495e8f9447f"
+ },
+ {
+ "type": "shell",
+ "commands": [
+ "# FTBFS fix",
+ "sed -e '/LIBINDICATOR_LIBS/ s/\\$LIBM/ $LIBM/' -i configure.ac"
+ ]
+ },
+ {
+ "type": "script",
+ "commands": ["autoreconf -sfi"],
+ "dest-filename": "autogen.sh"
+ }
+ ]
+ }
+ ]
+}
diff --git a/subprojects/shared-modules/libappindicator/libappindicator-gtk3-12.10.json b/subprojects/shared-modules/libappindicator/libappindicator-gtk3-12.10.json
new file mode 100644
index 00000000..46e6f438
--- /dev/null
+++ b/subprojects/shared-modules/libappindicator/libappindicator-gtk3-12.10.json
@@ -0,0 +1,121 @@
+{
+ "name": "libappindicator",
+ "build-options": {
+ "cflags": "-Wno-error"
+ },
+ "rm-configure": true,
+ "config-opts": [
+ "--disable-static",
+ "--disable-gtk-doc",
+ "--disable-tests",
+ "--disable-mono-tests",
+ "--enable-introspection=no",
+ "--with-gtk=3"
+ ],
+ "cleanup": [
+ "/include",
+ "/lib/pkgconfig",
+ "/lib/*.la",
+ "/share/gtk-doc",
+ "/share/gir-1.0"
+ ],
+ "sources": [
+ {
+ "type": "archive",
+ "url": "https://launchpad.net/libappindicator/12.10/12.10.0/+download/libappindicator-12.10.0.tar.gz",
+ "sha256": "d5907c1f98084acf28fd19593cb70672caa0ca1cf82d747ba6f4830d4cc3b49f"
+ },
+ {
+ "type": "patch",
+ "path": "libappindicator-ftbfs.patch"
+ },
+ {
+ "type": "patch",
+ "path": "libappindicator-no-python.patch"
+ },
+ {
+ "type": "script",
+ "commands": ["autoreconf -sfi"],
+ "dest-filename": "autogen.sh"
+ }
+ ],
+ "modules": [
+ "../intltool/intltool-0.51.json",
+ "../dbus-glib/dbus-glib-0.110.json",
+ {
+ "name": "libdbusmenu",
+ "build-options": {
+ "cflags": "-Wno-error",
+ "env": {
+ "HAVE_VALGRIND_FALSE": "#",
+ "HAVE_VALGRIND_TRUE": ""
+ }
+ },
+ "cleanup": [
+ "/include",
+ "/libexec",
+ "/lib/pkgconfig",
+ "/lib/*.la",
+ "/share/doc",
+ "/share/libdbusmenu",
+ "/share/gtk-doc",
+ "/share/gir-1.0"
+ ],
+ "config-opts": [
+ "--disable-static",
+ "--disable-gtk-doc",
+ "--enable-introspection=no",
+ "--disable-vala",
+ "--disable-dumper",
+ "--disable-tests",
+ "--with-gtk=3"
+ ],
+ "sources": [
+ {
+ "type": "archive",
+ "url": "https://launchpad.net/libdbusmenu/16.04/16.04.0/+download/libdbusmenu-16.04.0.tar.gz",
+ "sha256": "b9cc4a2acd74509435892823607d966d424bd9ad5d0b00938f27240a1bfa878a"
+ }
+ ]
+ },
+ {
+ "name": "libindicator",
+ "build-options": {
+ "cflags": "-Wno-error"
+ },
+ "cleanup": [
+ "/include",
+ "/libexec",
+ "/lib/pkgconfig",
+ "/lib/*.la",
+ "/share/libindicator",
+ "/share/gtk-doc",
+ "/share/gir-1.0"
+ ],
+ "config-opts": [
+ "--disable-static",
+ "--disable-tests",
+ "--with-gtk=3"
+ ],
+ "sources": [
+ {
+ "type": "archive",
+ "url": "https://launchpad.net/libindicator/12.10/12.10.1/+download/libindicator-12.10.1.tar.gz",
+ "sha256": "b2d2e44c10313d5c9cd60db455d520f80b36dc39562df079a3f29495e8f9447f"
+ },
+ {
+ "type": "shell",
+ "commands": [
+ "# FTBFS fix",
+ "sed -e '/LIBINDICATOR_LIBS/ s/\\$LIBM/ $LIBM/' -i configure.ac"
+ ]
+ },
+ {
+ "type": "script",
+ "commands": ["autoreconf -sfi"],
+ "dest-filename": "autogen.sh"
+ }
+ ]
+ }
+ ]
+}
diff --git a/subprojects/shared-modules/libappindicator/libappindicator-gtk3-introspection-12.10.json b/subprojects/shared-modules/libappindicator/libappindicator-gtk3-introspection-12.10.json
new file mode 100644
index 00000000..5df82527
--- /dev/null
+++ b/subprojects/shared-modules/libappindicator/libappindicator-gtk3-introspection-12.10.json
@@ -0,0 +1,121 @@
+{
+ "name": "libappindicator",
+ "build-options": {
+ "cflags": "-Wno-error"
+ },
+ "rm-configure": true,
+ "config-opts": [
+ "--disable-static",
+ "--disable-gtk-doc",
+ "--disable-tests",
+ "--disable-mono-tests",
+ "--enable-introspection=yes",
+ "--with-gtk=3"
+ ],
+ "cleanup": [
+ "/include",
+ "/lib/pkgconfig",
+ "/lib/*.la",
+ "/share/gtk-doc",
+ "/share/gir-1.0"
+ ],
+ "sources": [
+ {
+ "type": "archive",
+ "url": "https://launchpad.net/libappindicator/12.10/12.10.0/+download/libappindicator-12.10.0.tar.gz",
+ "sha256": "d5907c1f98084acf28fd19593cb70672caa0ca1cf82d747ba6f4830d4cc3b49f"
+ },
+ {
+ "type": "patch",
+ "path": "libappindicator-ftbfs.patch"
+ },
+ {
+ "type": "patch",
+ "path": "libappindicator-no-python.patch"
+ },
+ {
+ "type": "script",
+ "commands": ["autoreconf -sfi"],
+ "dest-filename": "autogen.sh"
+ }
+ ],
+ "modules": [
+ "../intltool/intltool-0.51.json",
+ "../dbus-glib/dbus-glib-0.110.json",
+ {
+ "name": "libdbusmenu",
+ "build-options": {
+ "cflags": "-Wno-error",
+ "env": {
+ "HAVE_VALGRIND_FALSE": "#",
+ "HAVE_VALGRIND_TRUE": ""
+ }
+ },
+ "cleanup": [
+ "/include",
+ "/libexec",
+ "/lib/pkgconfig",
+ "/lib/*.la",
+ "/share/doc",
+ "/share/libdbusmenu",
+ "/share/gtk-doc",
+ "/share/gir-1.0"
+ ],
+ "config-opts": [
+ "--disable-static",
+ "--disable-gtk-doc",
+ "--enable-introspection=yes",
+ "--disable-vala",
+ "--disable-dumper",
+ "--disable-tests",
+ "--with-gtk=3"
+ ],
+ "sources": [
+ {
+ "type": "archive",
+ "url": "https://launchpad.net/libdbusmenu/16.04/16.04.0/+download/libdbusmenu-16.04.0.tar.gz",
+ "sha256": "b9cc4a2acd74509435892823607d966d424bd9ad5d0b00938f27240a1bfa878a"
+ }
+ ]
+ },
+ {
+ "name": "libindicator",
+ "build-options": {
+ "cflags": "-Wno-error"
+ },
+ "cleanup": [
+ "/include",
+ "/libexec",
+ "/lib/pkgconfig",
+ "/lib/*.la",
+ "/share/libindicator",
+ "/share/gtk-doc",
+ "/share/gir-1.0"
+ ],
+ "config-opts": [
+ "--disable-static",
+ "--disable-tests",
+ "--with-gtk=3"
+ ],
+ "sources": [
+ {
+ "type": "archive",
+ "url": "https://launchpad.net/libindicator/12.10/12.10.1/+download/libindicator-12.10.1.tar.gz",
+ "sha256": "b2d2e44c10313d5c9cd60db455d520f80b36dc39562df079a3f29495e8f9447f"
+ },
+ {
+ "type": "shell",
+ "commands": [
+ "# FTBFS fix",
+ "sed -e '/LIBINDICATOR_LIBS/ s/\\$LIBM/ $LIBM/' -i configure.ac"
+ ]
+ },
+ {
+ "type": "script",
+ "commands": ["autoreconf -sfi"],
+ "dest-filename": "autogen.sh"
+ }
+ ]
+ }
+ ]
+}
diff --git a/subprojects/shared-modules/libappindicator/libappindicator-no-python.patch b/subprojects/shared-modules/libappindicator/libappindicator-no-python.patch
new file mode 100644
index 00000000..587cddec
--- /dev/null
+++ b/subprojects/shared-modules/libappindicator/libappindicator-no-python.patch
@@ -0,0 +1,888 @@
+diff --git a/bindings/Makefile.am b/bindings/Makefile.am
+index d1f6d73..dccca6c 100644
+--- a/bindings/Makefile.am
++++ b/bindings/Makefile.am
+@@ -3,7 +3,6 @@ SUBDIRS = \
+ vala
+ else
+ SUBDIRS = \
+- python \
+ vala
+ endif
+
+diff --git a/bindings/python/Makefile.am b/bindings/python/Makefile.am
+deleted file mode 100644
+index fe95c02..0000000
+--- a/bindings/python/Makefile.am
++++ /dev/null
+@@ -1,39 +0,0 @@
+-defsdir = $(datadir)/pygtk/2.0/defs
+-defs_DATA = appindicator.defs
+-
+-#CFLAGS = -Wall -Werror
+-INCLUDES = \
+- -I$(top_srcdir)/src \
+- -DG_LOG_DOMAIN=\"appindicator-python\" \
+- -DDATADIR=\"$(datadir)\" \
+- -DLIBDIR=\"$(libdir)\" \
+- $(APPINDICATOR_PYTHON_CFLAGS) \
+- $(PYTHON_INCLUDES)
+-
+-pkgpythondir = $(pyexecdir)
+-pkgpyexecdir = $(pyexecdir)
+-
+-pkgappindicatordir = $(pkgpythondir)/appindicator
+-pkgappindicator_PYTHON = __init__.py
+-
+-appindicatordir = $(pkgpyexecdir)/appindicator
+-appindicator_LTLIBRARIES = _appindicator.la
+-
+-_appindicator_la_LDFLAGS = -module -avoid-version -export-symbols-regex init_appindicator
+-_appindicator_la_LIBADD = $(APPINDICATOR_PYTHON_LIBS) -L$(top_builddir)/src/.libs -lappindicator
+-_appindicator_la_SOURCES = appindicatormodule.c
+-nodist__appindicator_la_SOURCES = appindicator.c
+-
+-CLEANFILES = appindicator.c
+-EXTRA_DIST = appindicator.override.in appindicator-arg-types.py $(defs_DATA)
+-appindicator.c: $(defs_DATA) appindicator.override
+-
+-%.c: %.defs
+- ($(PYGTK_CODEGEN) \
+- --register $(PYGTK_DEFSDIR)/gtk-types.defs \
+- --register $(PYGTK_DEFSDIR)/gdk-types.defs \
+- --load-types $(srcdir)/appindicator-arg-types.py \
+- --override $*.override \
+- --prefix py$* $(srcdir)/$*.defs) > gen-$*.c \
+- && cp gen-$*.c $*.c \
+- && rm -f gen-$*.c
+diff --git a/bindings/python/__init__.py b/bindings/python/__init__.py
+deleted file mode 100644
+index 20e2140..0000000
+--- a/bindings/python/__init__.py
++++ /dev/null
+@@ -1,27 +0,0 @@
+-# Python bindings for libappindicator.
+-#
+-# Copyright 2009 Canonical Ltd.
+-#
+-# Authors:
+-# Eitan Isaacson <eitan@ascender.com>
+-# Neil Jagdish Patel <neil.patel@canonical.com>
+-#
+-# This program is free software: you can redistribute it and/or modify it
+-# under the terms of either or both of the following licenses:
+-#
+-# 1) the GNU Lesser General Public License version 3, as published by the
+-# Free Software Foundation; and/or
+-# 2) the GNU Lesser General Public License version 2.1, as published by
+-# the Free Software Foundation.
+-#
+-# This program is distributed in the hope that it will be useful, but
+-# WITHOUT ANY WARRANTY; without even the implied warranties of
+-# MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
+-# PURPOSE. See the applicable version of the GNU Lesser General Public
+-# License for more details.
+-#
+-# You should have received a copy of both the GNU Lesser General Public
+-# License version 3 and version 2.1 along with this program. If not, see
+-# <http://www.gnu.org/licenses/>
+-
+-from _appindicator import *
+diff --git a/bindings/python/appindicator-arg-types.py b/bindings/python/appindicator-arg-types.py
+deleted file mode 100644
+index 9d74aa0..0000000
+--- a/bindings/python/appindicator-arg-types.py
++++ /dev/null
+@@ -1,27 +0,0 @@
+-# Python bindings for libappindicator.
+-#
+-# Copyright 2009 Canonical Ltd.
+-#
+-# Authors:
+-# Eitan Isaacson <eitan@ascender.com>
+-#
+-# This program is free software: you can redistribute it and/or modify it
+-# under the terms of either or both of the following licenses:
+-#
+-# 1) the GNU Lesser General Public License version 3, as published by the
+-# Free Software Foundation; and/or
+-# 2) the GNU Lesser General Public License version 2.1, as published by
+-# the Free Software Foundation.
+-#
+-# This program is distributed in the hope that it will be useful, but
+-# WITHOUT ANY WARRANTY; without even the implied warranties of
+-# MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
+-# PURPOSE. See the applicable version of the GNU Lesser General Public
+-# License for more details.
+-#
+-# You should have received a copy of both the GNU Lesser General Public
+-# License version 3 and version 2.1 along with this program. If not, see
+-# <http://www.gnu.org/licenses/>
+-
+-import argtypes
+-
+diff --git a/bindings/python/appindicator.defs b/bindings/python/appindicator.defs
+deleted file mode 100644
+index 4fcc2d5..0000000
+--- a/bindings/python/appindicator.defs
++++ /dev/null
+@@ -1,200 +0,0 @@
+-;; -*- scheme -*-
+-; object definitions ...
+-(define-object Indicator
+- (in-module "App")
+- (parent "GObject")
+- (c-name "AppIndicator")
+- (gtype-id "APP_TYPE_INDICATOR")
+-)
+-
+-;; Enumerations and flags ...
+-
+-(define-enum IndicatorCategory
+- (in-module "App")
+- (c-name "AppIndicatorCategory")
+- (gtype-id "APP_INDICATOR_TYPE_INDICATOR_CATEGORY")
+- (values
+- '("ApplicationStatus" "APP_INDICATOR_CATEGORY_APPLICATION_STATUS")
+- '("Communications" "APP_INDICATOR_CATEGORY_COMMUNICATIONS")
+- '("SystemServices" "APP_INDICATOR_CATEGORY_SYSTEM_SERVICES")
+- '("Hardware" "APP_INDICATOR_CATEGORY_HARDWARE")
+- '("Other" "APP_INDICATOR_CATEGORY_OTHER")
+- )
+-)
+-
+-(define-enum IndicatorStatus
+- (in-module "App")
+- (c-name "AppIndicatorStatus")
+- (gtype-id "APP_INDICATOR_TYPE_INDICATOR_STATUS")
+- (values
+- '("Passive" "APP_INDICATOR_STATUS_PASSIVE")
+- '("Active" "APP_INDICATOR_STATUS_ACTIVE")
+- '("NeedsAttention" "APP_INDICATOR_STATUS_ATTENTION")
+- )
+-)
+-
+-;; From app-indicator.h
+-
+-(define-function app_indicator_get_type
+- (c-name "app_indicator_get_type")
+- (return-type "GType")
+-)
+-
+-(define-function app_indicator_new_with_path
+- (c-name "app_indicator_new_with_path")
+- (is-constructor-of "AppIndicator")
+- (return-type "AppIndicator*")
+- (parameters
+- '("const-gchar*" "id")
+- '("const-gchar*" "icon_name")
+- '("AppIndicatorCategory" "category")
+- '("const-gchar*" "icon_theme_path" (null-ok) (default "NULL"))
+- )
+-)
+-
+-(define-method set_status
+- (of-object "AppIndicator")
+- (c-name "app_indicator_set_status")
+- (return-type "none")
+- (parameters
+- '("AppIndicatorStatus" "status")
+- )
+-)
+-
+-(define-method set_attention_icon
+- (of-object "AppIndicator")
+- (c-name "app_indicator_set_attention_icon_full")
+- (return-type "none")
+- (parameters
+- '("const-gchar*" "icon_name")
+- '("const-gchar*" "icon_desc" (null-ok) (default "NULL"))
+- )
+-)
+-
+-(define-method set_menu
+- (of-object "AppIndicator")
+- (c-name "app_indicator_set_menu")
+- (return-type "none")
+- (parameters
+- '("GtkMenu*" "menu")
+- )
+-)
+-
+-(define-method set_icon
+- (of-object "AppIndicator")
+- (c-name "app_indicator_set_icon_full")
+- (return-type "none")
+- (parameters
+- '("const-gchar*" "icon_name")
+- '("const-gchar*" "icon_desc" (null-ok) (default "NULL"))
+- )
+-)
+-
+-(define-method set_label
+- (of-object "AppIndicator")
+- (c-name "app_indicator_set_label")
+- (return-type "none")
+- (parameters
+- '("const-gchar*" "label" (null-ok))
+- '("const-gchar*" "guide" (null-ok) (default "NULL"))
+- )
+-)
+-
+-(define-method set_ordering_index
+- (of-object "AppIndicator")
+- (c-name "app_indicator_set_ordering_index")
+- (parameters
+- '("guint32" "ordering_index")
+- )
+-)
+-
+-(define-method set_icon_theme_path
+- (of-object "AppIndicator")
+- (c-name "app_indicator_set_icon_theme_path")
+- (return-type "none")
+- (parameters
+- '("const-gchar*" "icon_theme_path" (null-ok))
+- )
+-)
+-
+-(define-method get_id
+- (of-object "AppIndicator")
+- (c-name "app_indicator_get_id")
+- (return-type "const-gchar*")
+-)
+-
+-(define-method get_category
+- (of-object "AppIndicator")
+- (c-name "app_indicator_get_category")
+- (return-type "AppIndicatorCategory")
+-)
+-
+-(define-method get_status
+- (of-object "AppIndicator")
+- (c-name "app_indicator_get_status")
+- (return-type "AppIndicatorStatus")
+-)
+-
+-(define-method get_icon
+- (of-object "AppIndicator")
+- (c-name "app_indicator_get_icon")
+- (return-type "const-gchar*")
+-)
+-
+-(define-method get_icon_desc
+- (of-object "AppIndicator")
+- (c-name "app_indicator_get_icon_desc")
+- (return-type "const-gchar*")
+-)
+-
+-(define-method get_icon_theme_path
+- (of-object "AppIndicator")
+- (c-name "app_indicator_get_icon_theme_path")
+- (return-type "const-gchar*")
+-)
+-
+-(define-method get_attention_icon
+- (of-object "AppIndicator")
+- (c-name "app_indicator_get_attention_icon")
+- (return-type "const-gchar*")
+-)
+-
+-(define-method get_attention_icon_desc
+- (of-object "AppIndicator")
+- (c-name "app_indicator_get_attention_icon_desc")
+- (return-type "const-gchar*")
+-)
+-
+-(define-method get_menu
+- (of-object "AppIndicator")
+- (c-name "app_indicator_get_menu")
+- (return-type "GtkMenu*")
+-)
+-
+-(define-method get_label
+- (of-object "AppIndicator")
+- (c-name "app_indicator_get_label")
+- (return-type "const-gchar*")
+-)
+-
+-(define-method get_label_guide
+- (of-object "AppIndicator")
+- (c-name "app_indicator_get_label_guide")
+- (return-type "const-gchar*")
+-)
+-
+-(define-method get_ordering_index
+- (of-object "AppIndicator")
+- (c-name "app_indicator_get_ordering_index")
+- (return-type "guint32")
+-)
+-
+-(define-method build_menu_from_desktop
+- (of-object "AppIndicator")
+- (c-name "app_indicator_build_menu_from_desktop")
+- (return-type "none")
+- (parameters
+- '("const-gchar*" "desktop_file")
+- '("const-gchar*" "desktop_profile")
+- )
+-)
+diff --git a/bindings/python/appindicator.override.in b/bindings/python/appindicator.override.in
+deleted file mode 100644
+index 84d3159..0000000
+--- a/bindings/python/appindicator.override.in
++++ /dev/null
+@@ -1,65 +0,0 @@
+-/*
+-Python bindings for libappindicator.
+-
+-Copyright 2009 Canonical Ltd.
+-
+-Authors:
+- Eitan Isaacson <eitan@ascender.com> (original)
+- Neil Jagdish Patel <neil.patel@canonical.com>
+-
+-This program is free software: you can redistribute it and/or modify it
+-under the terms of either or both of the following licenses:
+-
+-1) the GNU Lesser General Public License version 3, as published by the
+-Free Software Foundation; and/or
+-2) the GNU Lesser General Public License version 2.1, as published by
+-the Free Software Foundation.
+-
+-This program is distributed in the hope that it will be useful, but
+-WITHOUT ANY WARRANTY; without even the implied warranties of
+-MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
+-PURPOSE. See the applicable version of the GNU Lesser General Public
+-License for more details.
+-
+-You should have received a copy of both the GNU Lesser General Public
+-License version 3 and version 2.1 along with this program. If not, see
+-<http://www.gnu.org/licenses/>
+-*/
+-%%
+-headers
+-#include <Python.h>
+-#include "@top_srcdir@/src/app-indicator.h"
+-#include "@top_builddir@/src/app-indicator-enum-types.h"
+-#include <glib.h>
+-#include "pygobject.h"
+-#include "pyglib.h"
+-#include <pygtk/pygtk.h>
+-
+-typedef PyObject* (*to_pyobject_func) (gpointer data);
+-
+-#define APP_TYPE_INDICATOR APP_INDICATOR_TYPE
+-
+-void
+-_appindicator_add_constants(PyObject *module, const gchar *strip_prefix)
+-{
+-#ifdef VERSION
+- PyModule_AddStringConstant(module, "__version__", VERSION);
+-#endif
+- pyg_enum_add(module,
+- "IndicatorCategory",
+- strip_prefix,
+- APP_INDICATOR_TYPE_INDICATOR_CATEGORY);
+-
+- pyg_enum_add(module,
+- "IndicatorStatus",
+- strip_prefix,
+- APP_INDICATOR_TYPE_INDICATOR_STATUS);
+-
+- if (PyErr_Occurred())
+- PyErr_Print();
+-}
+-%%
+-modulename appindicator
+-%%
+-import gobject.GObject as PyGObject_Type
+-import gtk.Menu as PyGtkMenu_Type
+diff --git a/bindings/python/appindicatormodule.c b/bindings/python/appindicatormodule.c
+deleted file mode 100644
+index b66639c..0000000
+--- a/bindings/python/appindicatormodule.c
++++ /dev/null
+@@ -1,49 +0,0 @@
+-/*
+-Python bindings for libappindicator.
+-
+-Copyright 2009 Canonical Ltd.
+-
+-Authors:
+- Eitan Isaacson <eitan@ascender.com>
+- Neil Jagdish Patel <neil.patel@canonical.com>
+-
+-This program is free software: you can redistribute it and/or modify it
+-under the terms of either or both of the following licenses:
+-
+-1) the GNU Lesser General Public License version 3, as published by the
+-Free Software Foundation; and/or
+-2) the GNU Lesser General Public License version 2.1, as published by
+-the Free Software Foundation.
+-
+-This program is distributed in the hope that it will be useful, but
+-WITHOUT ANY WARRANTY; without even the implied warranties of
+-MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
+-PURPOSE. See the applicable version of the GNU Lesser General Public
+-License for more details.
+-
+-You should have received a copy of both the GNU Lesser General Public
+-License version 3 and version 2.1 along with this program. If not, see
+-<http://www.gnu.org/licenses/>
+-*/
+-#include <pygobject.h>
+-
+-void pyappindicator_register_classes (PyObject *d);
+-extern PyMethodDef pyappindicator_functions[];
+-
+-DL_EXPORT(void)
+-init_appindicator(void)
+-{
+- PyObject *m, *d;
+-
+- init_pygobject ();
+-
+- m = Py_InitModule ("_appindicator", pyappindicator_functions);
+- d = PyModule_GetDict (m);
+-
+- pyappindicator_register_classes (d);
+-
+- _appindicator_add_constants (m, "APP_INDICATOR_");
+- if (PyErr_Occurred ()) {
+- Py_FatalError ("can't initialise module appindicator");
+- }
+-}
+diff --git a/configure b/configure
+index 5174b3f..8ff732c 100755
+--- a/configure
++++ b/configure
+@@ -13703,363 +13703,6 @@ else
+ fi
+
+
+-###########################
+-# Python
+-###########################
+-
+-PYGTK_REQUIRED=2.14.0
+-PYGOBJECT_REQUIRED=0.22
+-
+-
+-
+-
+-
+- if test -n "$PYTHON"; then
+- # If the user set $PYTHON, use it and don't search something else.
+- { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $PYTHON version >= 2.3.5" >&5
+-$as_echo_n "checking whether $PYTHON version >= 2.3.5... " >&6; }
+- prog="import sys, string
+-# split strings by '.' and convert to numeric. Append some zeros
+-# because we need at least 4 digits for the hex conversion.
+-minver = map(int, string.split('2.3.5', '.')) + [0, 0, 0]
+-minverhex = 0
+-for i in xrange(0, 4): minverhex = (minverhex << 8) + minver[i]
+-sys.exit(sys.hexversion < minverhex)"
+- if { echo "$as_me:$LINENO: $PYTHON -c "$prog"" >&5
+- ($PYTHON -c "$prog") >&5 2>&5
+- ac_status=$?
+- echo "$as_me:$LINENO: \$? = $ac_status" >&5
+- (exit $ac_status); }; then :
+- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+-$as_echo "yes" >&6; }
+-else
+- as_fn_error $? "too old" "$LINENO" 5
+-fi
+- am_display_PYTHON=$PYTHON
+- else
+- # Otherwise, try each interpreter until we find one that satisfies
+- # VERSION.
+- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a Python interpreter with version >= 2.3.5" >&5
+-$as_echo_n "checking for a Python interpreter with version >= 2.3.5... " >&6; }
+-if ${am_cv_pathless_PYTHON+:} false; then :
+- $as_echo_n "(cached) " >&6
+-else
+-
+- for am_cv_pathless_PYTHON in python python2 python2.4 python2.3 python2.2 python2.1 python2.0 python1.6 python1.5 none; do
+- test "$am_cv_pathless_PYTHON" = none && break
+- prog="import sys, string
+-# split strings by '.' and convert to numeric. Append some zeros
+-# because we need at least 4 digits for the hex conversion.
+-minver = map(int, string.split('2.3.5', '.')) + [0, 0, 0]
+-minverhex = 0
+-for i in xrange(0, 4): minverhex = (minverhex << 8) + minver[i]
+-sys.exit(sys.hexversion < minverhex)"
+- if { echo "$as_me:$LINENO: $am_cv_pathless_PYTHON -c "$prog"" >&5
+- ($am_cv_pathless_PYTHON -c "$prog") >&5 2>&5
+- ac_status=$?
+- echo "$as_me:$LINENO: \$? = $ac_status" >&5
+- (exit $ac_status); }; then :
+- break
+-fi
+- done
+-fi
+-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_pathless_PYTHON" >&5
+-$as_echo "$am_cv_pathless_PYTHON" >&6; }
+- # Set $PYTHON to the absolute path of $am_cv_pathless_PYTHON.
+- if test "$am_cv_pathless_PYTHON" = none; then
+- PYTHON=:
+- else
+- # Extract the first word of "$am_cv_pathless_PYTHON", so it can be a program name with args.
+-set dummy $am_cv_pathless_PYTHON; ac_word=$2
+-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+-$as_echo_n "checking for $ac_word... " >&6; }
+-if ${ac_cv_path_PYTHON+:} false; then :
+- $as_echo_n "(cached) " >&6
+-else
+- case $PYTHON in
+- [\\/]* | ?:[\\/]*)
+- ac_cv_path_PYTHON="$PYTHON" # Let the user override the test with a path.
+- ;;
+- *)
+- as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+-for as_dir in $PATH
+-do
+- IFS=$as_save_IFS
+- test -z "$as_dir" && as_dir=.
+- for ac_exec_ext in '' $ac_executable_extensions; do
+- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+- ac_cv_path_PYTHON="$as_dir/$ac_word$ac_exec_ext"
+- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+- break 2
+- fi
+-done
+- done
+-IFS=$as_save_IFS
+-
+- ;;
+-esac
+-fi
+-PYTHON=$ac_cv_path_PYTHON
+-if test -n "$PYTHON"; then
+- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON" >&5
+-$as_echo "$PYTHON" >&6; }
+-else
+- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+-$as_echo "no" >&6; }
+-fi
+-
+-
+- fi
+- am_display_PYTHON=$am_cv_pathless_PYTHON
+- fi
+-
+-
+- if test "$PYTHON" = :; then
+- as_fn_error $? "no suitable Python interpreter found" "$LINENO" 5
+- else
+-
+-
+- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $am_display_PYTHON version" >&5
+-$as_echo_n "checking for $am_display_PYTHON version... " >&6; }
+-if ${am_cv_python_version+:} false; then :
+- $as_echo_n "(cached) " >&6
+-else
+- am_cv_python_version=`$PYTHON -c "import sys; print sys.version[:3]"`
+-fi
+-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_version" >&5
+-$as_echo "$am_cv_python_version" >&6; }
+- PYTHON_VERSION=$am_cv_python_version
+-
+-
+-
+- PYTHON_PREFIX='${prefix}'
+-
+- PYTHON_EXEC_PREFIX='${exec_prefix}'
+-
+-
+-
+- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $am_display_PYTHON platform" >&5
+-$as_echo_n "checking for $am_display_PYTHON platform... " >&6; }
+-if ${am_cv_python_platform+:} false; then :
+- $as_echo_n "(cached) " >&6
+-else
+- am_cv_python_platform=`$PYTHON -c "import sys; print sys.platform"`
+-fi
+-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_platform" >&5
+-$as_echo "$am_cv_python_platform" >&6; }
+- PYTHON_PLATFORM=$am_cv_python_platform
+-
+-
+-
+-
+- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $am_display_PYTHON script directory" >&5
+-$as_echo_n "checking for $am_display_PYTHON script directory... " >&6; }
+-if ${am_cv_python_pythondir+:} false; then :
+- $as_echo_n "(cached) " >&6
+-else
+- am_cv_python_pythondir=`$PYTHON -c "from distutils import sysconfig; print sysconfig.get_python_lib(0,0,prefix='$PYTHON_PREFIX')" 2>/dev/null ||
+- echo "$PYTHON_PREFIX/lib/python$PYTHON_VERSION/site-packages"`
+-fi
+-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_pythondir" >&5
+-$as_echo "$am_cv_python_pythondir" >&6; }
+- pythondir=$am_cv_python_pythondir
+-
+-
+-
+- pkgpythondir=\${pythondir}/$PACKAGE
+-
+-
+- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $am_display_PYTHON extension module directory" >&5
+-$as_echo_n "checking for $am_display_PYTHON extension module directory... " >&6; }
+-if ${am_cv_python_pyexecdir+:} false; then :
+- $as_echo_n "(cached) " >&6
+-else
+- am_cv_python_pyexecdir=`$PYTHON -c "from distutils import sysconfig; print sysconfig.get_python_lib(1,0,prefix='$PYTHON_EXEC_PREFIX')" 2>/dev/null ||
+- echo "${PYTHON_EXEC_PREFIX}/lib/python${PYTHON_VERSION}/site-packages"`
+-fi
+-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_pyexecdir" >&5
+-$as_echo "$am_cv_python_pyexecdir" >&6; }
+- pyexecdir=$am_cv_python_pyexecdir
+-
+-
+-
+- pkgpyexecdir=\${pyexecdir}/$PACKAGE
+-
+-
+-
+- fi
+-
+-
+-
+-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for headers required to compile python extensions" >&5
+-$as_echo_n "checking for headers required to compile python extensions... " >&6; }
+-py_prefix=`$PYTHON -c "import sys; print sys.prefix"`
+-py_exec_prefix=`$PYTHON -c "import sys; print sys.exec_prefix"`
+-PYTHON_INCLUDES="-I${py_prefix}/include/python${PYTHON_VERSION}"
+-if test "$py_prefix" != "$py_exec_prefix"; then
+- PYTHON_INCLUDES="$PYTHON_INCLUDES -I${py_exec_prefix}/include/python${PYTHON_VERSION}"
+-fi
+-
+-save_CPPFLAGS="$CPPFLAGS"
+-CPPFLAGS="$CPPFLAGS $PYTHON_INCLUDES"
+-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+-/* end confdefs.h. */
+-#include <Python.h>
+-_ACEOF
+-if ac_fn_c_try_cpp "$LINENO"; then :
+- { $as_echo "$as_me:${as_lineno-$LINENO}: result: found" >&5
+-$as_echo "found" >&6; }
+-
+-else
+- { $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5
+-$as_echo "not found" >&6; }
+-as_fn_error $? "could not find Python headers" "$LINENO" 5
+-fi
+-rm -f conftest.err conftest.i conftest.$ac_ext
+-CPPFLAGS="$save_CPPFLAGS"
+-
+-
+-
+-pkg_failed=no
+-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for APPINDICATOR_PYTHON" >&5
+-$as_echo_n "checking for APPINDICATOR_PYTHON... " >&6; }
+-
+-if test -n "$APPINDICATOR_PYTHON_CFLAGS"; then
+- pkg_cv_APPINDICATOR_PYTHON_CFLAGS="$APPINDICATOR_PYTHON_CFLAGS"
+- elif test -n "$PKG_CONFIG"; then
+- if test -n "$PKG_CONFIG" && \
+- { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"
+- pygtk-2.0 >= \$PYGTK_REQUIRED
+- gtk+-2.0 >= \$GTK_REQUIRED_VERSION
+- pygobject-2.0 >= \$PYGOBJECT_REQUIRED
+- \""; } >&5
+- ($PKG_CONFIG --exists --print-errors "
+- pygtk-2.0 >= $PYGTK_REQUIRED
+- gtk+-2.0 >= $GTK_REQUIRED_VERSION
+- pygobject-2.0 >= $PYGOBJECT_REQUIRED
+- ") 2>&5
+- ac_status=$?
+- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+- test $ac_status = 0; }; then
+- pkg_cv_APPINDICATOR_PYTHON_CFLAGS=`$PKG_CONFIG --cflags "
+- pygtk-2.0 >= $PYGTK_REQUIRED
+- gtk+-2.0 >= $GTK_REQUIRED_VERSION
+- pygobject-2.0 >= $PYGOBJECT_REQUIRED
+- " 2>/dev/null`
+- test "x$?" != "x0" && pkg_failed=yes
+-else
+- pkg_failed=yes
+-fi
+- else
+- pkg_failed=untried
+-fi
+-if test -n "$APPINDICATOR_PYTHON_LIBS"; then
+- pkg_cv_APPINDICATOR_PYTHON_LIBS="$APPINDICATOR_PYTHON_LIBS"
+- elif test -n "$PKG_CONFIG"; then
+- if test -n "$PKG_CONFIG" && \
+- { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"
+- pygtk-2.0 >= \$PYGTK_REQUIRED
+- gtk+-2.0 >= \$GTK_REQUIRED_VERSION
+- pygobject-2.0 >= \$PYGOBJECT_REQUIRED
+- \""; } >&5
+- ($PKG_CONFIG --exists --print-errors "
+- pygtk-2.0 >= $PYGTK_REQUIRED
+- gtk+-2.0 >= $GTK_REQUIRED_VERSION
+- pygobject-2.0 >= $PYGOBJECT_REQUIRED
+- ") 2>&5
+- ac_status=$?
+- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+- test $ac_status = 0; }; then
+- pkg_cv_APPINDICATOR_PYTHON_LIBS=`$PKG_CONFIG --libs "
+- pygtk-2.0 >= $PYGTK_REQUIRED
+- gtk+-2.0 >= $GTK_REQUIRED_VERSION
+- pygobject-2.0 >= $PYGOBJECT_REQUIRED
+- " 2>/dev/null`
+- test "x$?" != "x0" && pkg_failed=yes
+-else
+- pkg_failed=yes
+-fi
+- else
+- pkg_failed=untried
+-fi
+-
+-
+-
+-if test $pkg_failed = yes; then
+- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+-$as_echo "no" >&6; }
+-
+-if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+- _pkg_short_errors_supported=yes
+-else
+- _pkg_short_errors_supported=no
+-fi
+- if test $_pkg_short_errors_supported = yes; then
+- APPINDICATOR_PYTHON_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "
+- pygtk-2.0 >= $PYGTK_REQUIRED
+- gtk+-2.0 >= $GTK_REQUIRED_VERSION
+- pygobject-2.0 >= $PYGOBJECT_REQUIRED
+- " 2>&1`
+- else
+- APPINDICATOR_PYTHON_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "
+- pygtk-2.0 >= $PYGTK_REQUIRED
+- gtk+-2.0 >= $GTK_REQUIRED_VERSION
+- pygobject-2.0 >= $PYGOBJECT_REQUIRED
+- " 2>&1`
+- fi
+- # Put the nasty error message in config.log where it belongs
+- echo "$APPINDICATOR_PYTHON_PKG_ERRORS" >&5
+-
+- as_fn_error $? "Package requirements (
+- pygtk-2.0 >= $PYGTK_REQUIRED
+- gtk+-2.0 >= $GTK_REQUIRED_VERSION
+- pygobject-2.0 >= $PYGOBJECT_REQUIRED
+- ) were not met:
+-
+-$APPINDICATOR_PYTHON_PKG_ERRORS
+-
+-Consider adjusting the PKG_CONFIG_PATH environment variable if you
+-installed software in a non-standard prefix.
+-
+-Alternatively, you may set the environment variables APPINDICATOR_PYTHON_CFLAGS
+-and APPINDICATOR_PYTHON_LIBS to avoid the need to call pkg-config.
+-See the pkg-config man page for more details." "$LINENO" 5
+-elif test $pkg_failed = untried; then
+- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+-$as_echo "no" >&6; }
+- { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+-as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it
+-is in your PATH or set the PKG_CONFIG environment variable to the full
+-path to pkg-config.
+-
+-Alternatively, you may set the environment variables APPINDICATOR_PYTHON_CFLAGS
+-and APPINDICATOR_PYTHON_LIBS to avoid the need to call pkg-config.
+-See the pkg-config man page for more details.
+-
+-To get pkg-config, see <http://pkg-config.freedesktop.org/>.
+-See \`config.log' for more details" "$LINENO" 5; }
+-else
+- APPINDICATOR_PYTHON_CFLAGS=$pkg_cv_APPINDICATOR_PYTHON_CFLAGS
+- APPINDICATOR_PYTHON_LIBS=$pkg_cv_APPINDICATOR_PYTHON_LIBS
+- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+-$as_echo "yes" >&6; }
+-
+-fi
+-
+-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for pygtk defs" >&5
+-$as_echo_n "checking for pygtk defs... " >&6; }
+-PYGTK_DEFSDIR=`$PKG_CONFIG --variable=defsdir pygtk-2.0`
+-
+-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYGTK_DEFSDIR" >&5
+-$as_echo "$PYGTK_DEFSDIR" >&6; }
+-
+-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for pygtk codegen" >&5
+-$as_echo_n "checking for pygtk codegen... " >&6; }
+-PYGTK_CODEGEN="$PYTHON `$PKG_CONFIG --variable=codegendir pygtk-2.0`/codegen.py"
+-
+-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYGTK_CODEGEN" >&5
+-$as_echo "$PYGTK_CODEGEN" >&6; }
+
+ #########################
+ # Check if build tests
+@@ -14358,7 +14001,7 @@ fi
+ # Files
+ ###########################
+
+-ac_config_files="$ac_config_files Makefile src/Makefile src/appindicator-0.1.pc src/appindicator3-0.1.pc bindings/Makefile bindings/python/Makefile bindings/python/appindicator.override bindings/vala/Makefile bindings/vala/examples/Makefile tests/Makefile example/Makefile docs/Makefile docs/reference/Makefile docs/reference/version.xml docs/reference/libappindicator-docs.sgml"
++ac_config_files="$ac_config_files Makefile src/Makefile src/appindicator-0.1.pc src/appindicator3-0.1.pc bindings/Makefile bindings/vala/Makefile bindings/vala/examples/Makefile tests/Makefile example/Makefile docs/Makefile docs/reference/Makefile docs/reference/version.xml docs/reference/libappindicator-docs.sgml"
+
+
+ if test "x$has_mono" = "xtrue" ; then
+@@ -15437,8 +15080,6 @@ do
+ "src/appindicator-0.1.pc") CONFIG_FILES="$CONFIG_FILES src/appindicator-0.1.pc" ;;
+ "src/appindicator3-0.1.pc") CONFIG_FILES="$CONFIG_FILES src/appindicator3-0.1.pc" ;;
+ "bindings/Makefile") CONFIG_FILES="$CONFIG_FILES bindings/Makefile" ;;
+- "bindings/python/Makefile") CONFIG_FILES="$CONFIG_FILES bindings/python/Makefile" ;;
+- "bindings/python/appindicator.override") CONFIG_FILES="$CONFIG_FILES bindings/python/appindicator.override" ;;
+ "bindings/vala/Makefile") CONFIG_FILES="$CONFIG_FILES bindings/vala/Makefile" ;;
+ "bindings/vala/examples/Makefile") CONFIG_FILES="$CONFIG_FILES bindings/vala/examples/Makefile" ;;
+ "tests/Makefile") CONFIG_FILES="$CONFIG_FILES tests/Makefile" ;;
+diff --git a/configure.ac b/configure.ac
+index ee03390..4713b22 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -181,33 +181,6 @@ AM_CONDITIONAL(BUILD_MONO_TEST, test x${have_nunit} = xyes)
+ with_localinstall="no"
+ AC_ARG_ENABLE(localinstall, AS_HELP_STRING([--enable-localinstall], [install all of the files localy instead of system directories (for distcheck)]), with_localinstall=$enableval, with_localinstall=no)
+
+-###########################
+-# Python
+-###########################
+-
+-PYGTK_REQUIRED=2.14.0
+-PYGOBJECT_REQUIRED=0.22
+-
+-AM_PATH_PYTHON(2.3.5)
+-AM_CHECK_PYTHON_HEADERS(,[AC_MSG_ERROR(could not find Python headers)])
+-
+-PKG_CHECK_MODULES(APPINDICATOR_PYTHON,
+- [
+- pygtk-2.0 >= $PYGTK_REQUIRED
+- gtk+-2.0 >= $GTK_REQUIRED_VERSION
+- pygobject-2.0 >= $PYGOBJECT_REQUIRED
+- ])
+-
+-AC_MSG_CHECKING(for pygtk defs)
+-PYGTK_DEFSDIR=`$PKG_CONFIG --variable=defsdir pygtk-2.0`
+-AC_SUBST(PYGTK_DEFSDIR)
+-AC_MSG_RESULT($PYGTK_DEFSDIR)
+-
+-AC_MSG_CHECKING(for pygtk codegen)
+-PYGTK_CODEGEN="$PYTHON `$PKG_CONFIG --variable=codegendir pygtk-2.0`/codegen.py"
+-AC_SUBST(PYGTK_CODEGEN)
+-AC_MSG_RESULT($PYGTK_CODEGEN)
+-
+ #########################
+ # Check if build tests
+ #########################
+@@ -239,8 +212,6 @@ src/Makefile
+ src/appindicator-0.1.pc
+ src/appindicator3-0.1.pc
+ bindings/Makefile
+-bindings/python/Makefile
+-bindings/python/appindicator.override
+ bindings/vala/Makefile
+ bindings/vala/examples/Makefile
+ tests/Makefile \ No newline at end of file
diff --git a/subprojects/shared-modules/libappindicator/libappindicator.json.in b/subprojects/shared-modules/libappindicator/libappindicator.json.in
new file mode 100644
index 00000000..2b383af7
--- /dev/null
+++ b/subprojects/shared-modules/libappindicator/libappindicator.json.in
@@ -0,0 +1,121 @@
+{
+ "name": "libappindicator",
+ "build-options": {
+ "cflags": "-Wno-error"
+ },
+ "rm-configure": true,
+ "config-opts": [
+ "--disable-static",
+ "--disable-gtk-doc",
+ "--disable-tests",
+ "--disable-mono-tests",
+ "--enable-introspection=@INTROSPECTION@",
+ "--with-gtk=@GTK_VER@"
+ ],
+ "cleanup": [
+ "/include",
+ "/lib/pkgconfig",
+ "/lib/*.la",
+ "/share/gtk-doc",
+ "/share/gir-1.0"
+ ],
+ "sources": [
+ {
+ "type": "archive",
+ "url": "https://launchpad.net/libappindicator/12.10/12.10.0/+download/libappindicator-12.10.0.tar.gz",
+ "sha256": "d5907c1f98084acf28fd19593cb70672caa0ca1cf82d747ba6f4830d4cc3b49f"
+ },
+ {
+ "type": "patch",
+ "path": "libappindicator-ftbfs.patch"
+ },
+ {
+ "type": "patch",
+ "path": "libappindicator-no-python.patch"
+ },
+ {
+ "type": "script",
+ "commands": ["autoreconf -sfi"],
+ "dest-filename": "autogen.sh"
+ }
+ ],
+ "modules": [
+ "../intltool/intltool-0.51.json",
+ "../dbus-glib/dbus-glib-0.110.json",
+ {
+ "name": "libdbusmenu",
+ "build-options": {
+ "cflags": "-Wno-error",
+ "env": {
+ "HAVE_VALGRIND_FALSE": "#",
+ "HAVE_VALGRIND_TRUE": ""
+ }
+ },
+ "cleanup": [
+ "/include",
+ "/libexec",
+ "/lib/pkgconfig",
+ "/lib/*.la",
+ "/share/doc",
+ "/share/libdbusmenu",
+ "/share/gtk-doc",
+ "/share/gir-1.0"
+ ],
+ "config-opts": [
+ "--disable-static",
+ "--disable-gtk-doc",
+ "--enable-introspection=@INTROSPECTION@",
+ "--disable-vala",
+ "--disable-dumper",
+ "--disable-tests",
+ "--with-gtk=@GTK_VER@"
+ ],
+ "sources": [
+ {
+ "type": "archive",
+ "url": "https://launchpad.net/libdbusmenu/16.04/16.04.0/+download/libdbusmenu-16.04.0.tar.gz",
+ "sha256": "b9cc4a2acd74509435892823607d966d424bd9ad5d0b00938f27240a1bfa878a"
+ }
+ ]
+ },
+ {
+ "name": "libindicator",
+ "build-options": {
+ "cflags": "-Wno-error"
+ },
+ "cleanup": [
+ "/include",
+ "/libexec",
+ "/lib/pkgconfig",
+ "/lib/*.la",
+ "/share/libindicator",
+ "/share/gtk-doc",
+ "/share/gir-1.0"
+ ],
+ "config-opts": [
+ "--disable-static",
+ "--disable-tests",
+ "--with-gtk=@GTK_VER@"
+ ],
+ "sources": [
+ {
+ "type": "archive",
+ "url": "https://launchpad.net/libindicator/12.10/12.10.1/+download/libindicator-12.10.1.tar.gz",
+ "sha256": "b2d2e44c10313d5c9cd60db455d520f80b36dc39562df079a3f29495e8f9447f"
+ },
+ {
+ "type": "shell",
+ "commands": [
+ "# FTBFS fix",
+ "sed -e '/LIBINDICATOR_LIBS/ s/\\$LIBM/ $LIBM/' -i configure.ac"
+ ]
+ },
+ {
+ "type": "script",
+ "commands": ["autoreconf -sfi"],
+ "dest-filename": "autogen.sh"
+ }
+ ]
+ }
+ ]
+}
diff --git a/subprojects/shared-modules/libmad/Provide-Thumb-2-alternative-code-for-MAD_F_MLN.diff b/subprojects/shared-modules/libmad/Provide-Thumb-2-alternative-code-for-MAD_F_MLN.diff
new file mode 100644
index 00000000..46415107
--- /dev/null
+++ b/subprojects/shared-modules/libmad/Provide-Thumb-2-alternative-code-for-MAD_F_MLN.diff
@@ -0,0 +1,34 @@
+From: Dave Martin
+Subject: "rsc" doesnt exist anymore in thumb2
+
+diff --git a/fixed.h b/fixed.h
+index 4b58abf..ba4bc26 100644
+--- a/fixed.h
++++ b/fixed.h
+@@ -275,12 +275,25 @@ mad_fixed_t mad_f_mul_inline(mad_fixed_t x, mad_fixed_t y)
+ : "+r" (lo), "+r" (hi) \
+ : "%r" (x), "r" (y))
+
++#ifdef __thumb__
++/* In Thumb-2, the RSB-immediate instruction is only allowed with a zero
++ operand. If needed this code can also support Thumb-1
++ (simply append "s" to the end of the second two instructions). */
++# define MAD_F_MLN(hi, lo) \
++ asm ("rsbs %0, %0, #0\n\t" \
++ "sbc %1, %1, %1\n\t" \
++ "sub %1, %1, %2" \
++ : "+&r" (lo), "=&r" (hi) \
++ : "r" (hi) \
++ : "cc")
++#else /* ! __thumb__ */
+ # define MAD_F_MLN(hi, lo) \
+ asm ("rsbs %0, %2, #0\n\t" \
+ "rsc %1, %3, #0" \
+- : "=r" (lo), "=r" (hi) \
++ : "=&r" (lo), "=r" (hi) \
+ : "0" (lo), "1" (hi) \
+ : "cc")
++#endif /* __thumb__ */
+
+ # define mad_f_scale64(hi, lo) \
+ ({ mad_fixed_t __result; \
diff --git a/subprojects/shared-modules/libmad/libmad-0.15.1b-cflags-O2.patch b/subprojects/shared-modules/libmad/libmad-0.15.1b-cflags-O2.patch
new file mode 100644
index 00000000..61b4b13b
--- /dev/null
+++ b/subprojects/shared-modules/libmad/libmad-0.15.1b-cflags-O2.patch
@@ -0,0 +1,12 @@
+diff -Naur libmad-0.15.1b-orig/configure.ac libmad-0.15.1b/configure.ac
+--- libmad-0.15.1b-orig/configure.ac 2007-07-01 12:58:13.000000000 -0600
++++ libmad-0.15.1b/configure.ac 2007-07-01 12:59:13.000000000 -0600
+@@ -105,7 +105,7 @@
+ shift
+ ;;
+ -O2)
+- optimize="-O"
++ optimize="-O2"
+ shift
+ ;;
+ -fomit-frame-pointer)
diff --git a/subprojects/shared-modules/libmad/libmad-0.15.1b-cflags.patch b/subprojects/shared-modules/libmad/libmad-0.15.1b-cflags.patch
new file mode 100644
index 00000000..2ec44e34
--- /dev/null
+++ b/subprojects/shared-modules/libmad/libmad-0.15.1b-cflags.patch
@@ -0,0 +1,146 @@
+diff -Naur libmad-0.15.1b-orig/configure.ac libmad-0.15.1b/configure.ac
+--- libmad-0.15.1b-orig/configure.ac 2007-06-30 20:22:31.000000000 -0600
++++ libmad-0.15.1b/configure.ac 2007-06-30 20:25:31.000000000 -0600
+@@ -122,74 +122,74 @@
+ esac
+ done
+
+-if test "$GCC" = yes
+-then
+- if test -z "$arch"
+- then
+- case "$host" in
+- i386-*) ;;
+- i?86-*) arch="-march=i486" ;;
+- arm*-empeg-*) arch="-march=armv4 -mtune=strongarm1100" ;;
+- armv4*-*) arch="-march=armv4 -mtune=strongarm" ;;
+- powerpc-*) ;;
+- mips*-agenda-*) arch="-mcpu=vr4100" ;;
+- mips*-luxsonor-*) arch="-mips1 -mcpu=r3000 -Wa,-m4010" ;;
+- esac
+- fi
+-
+- case "$optimize" in
+- -O|"-O "*)
+- optimize="-O"
+- optimize="$optimize -fforce-mem"
+- optimize="$optimize -fforce-addr"
+- : #x optimize="$optimize -finline-functions"
+- : #- optimize="$optimize -fstrength-reduce"
+- optimize="$optimize -fthread-jumps"
+- optimize="$optimize -fcse-follow-jumps"
+- optimize="$optimize -fcse-skip-blocks"
+- : #x optimize="$optimize -frerun-cse-after-loop"
+- : #x optimize="$optimize -frerun-loop-opt"
+- : #x optimize="$optimize -fgcse"
+- optimize="$optimize -fexpensive-optimizations"
+- optimize="$optimize -fregmove"
+- : #* optimize="$optimize -fdelayed-branch"
+- : #x optimize="$optimize -fschedule-insns"
+- optimize="$optimize -fschedule-insns2"
+- : #? optimize="$optimize -ffunction-sections"
+- : #? optimize="$optimize -fcaller-saves"
+- : #> optimize="$optimize -funroll-loops"
+- : #> optimize="$optimize -funroll-all-loops"
+- : #x optimize="$optimize -fmove-all-movables"
+- : #x optimize="$optimize -freduce-all-givs"
+- : #? optimize="$optimize -fstrict-aliasing"
+- : #* optimize="$optimize -fstructure-noalias"
+-
+- case "$host" in
+- arm*-*)
+- optimize="$optimize -fstrength-reduce"
+- ;;
+- mips*-*)
+- optimize="$optimize -fstrength-reduce"
+- optimize="$optimize -finline-functions"
+- ;;
+- i?86-*)
+- optimize="$optimize -fstrength-reduce"
+- ;;
+- powerpc-apple-*)
+- # this triggers an internal compiler error with gcc2
+- : #optimize="$optimize -fstrength-reduce"
+-
+- # this is really only beneficial with gcc3
+- : #optimize="$optimize -finline-functions"
+- ;;
+- *)
+- # this sometimes provokes bugs in gcc 2.95.2
+- : #optimize="$optimize -fstrength-reduce"
+- ;;
+- esac
+- ;;
+- esac
+-fi
++#if test "$GCC" = yes
++#then
++# if test -z "$arch"
++# then
++# case "$host" in
++# i386-*) ;;
++# i?86-*) arch="-march=i486" ;;
++# arm*-empeg-*) arch="-march=armv4 -mtune=strongarm1100" ;;
++# armv4*-*) arch="-march=armv4 -mtune=strongarm" ;;
++# powerpc-*) ;;
++# mips*-agenda-*) arch="-mcpu=vr4100" ;;
++# mips*-luxsonor-*) arch="-mips1 -mcpu=r3000 -Wa,-m4010" ;;
++# esac
++# fi
++#
++# case "$optimize" in
++# -O|"-O "*)
++# optimize="-O"
++# optimize="$optimize -fforce-mem"
++# optimize="$optimize -fforce-addr"
++# : #x optimize="$optimize -finline-functions"
++# : #- optimize="$optimize -fstrength-reduce"
++# optimize="$optimize -fthread-jumps"
++# optimize="$optimize -fcse-follow-jumps"
++# optimize="$optimize -fcse-skip-blocks"
++# : #x optimize="$optimize -frerun-cse-after-loop"
++# : #x optimize="$optimize -frerun-loop-opt"
++# : #x optimize="$optimize -fgcse"
++# optimize="$optimize -fexpensive-optimizations"
++# optimize="$optimize -fregmove"
++# : #* optimize="$optimize -fdelayed-branch"
++# : #x optimize="$optimize -fschedule-insns"
++# optimize="$optimize -fschedule-insns2"
++# : #? optimize="$optimize -ffunction-sections"
++# : #? optimize="$optimize -fcaller-saves"
++# : #> optimize="$optimize -funroll-loops"
++# : #> optimize="$optimize -funroll-all-loops"
++# : #x optimize="$optimize -fmove-all-movables"
++# : #x optimize="$optimize -freduce-all-givs"
++# : #? optimize="$optimize -fstrict-aliasing"
++# : #* optimize="$optimize -fstructure-noalias"
++#
++# case "$host" in
++# arm*-*)
++# optimize="$optimize -fstrength-reduce"
++# ;;
++# mips*-*)
++# optimize="$optimize -fstrength-reduce"
++# optimize="$optimize -finline-functions"
++# ;;
++# i?86-*)
++# optimize="$optimize -fstrength-reduce"
++# ;;
++# powerpc-apple-*)
++# # this triggers an internal compiler error with gcc2
++# : #optimize="$optimize -fstrength-reduce"
++#
++# # this is really only beneficial with gcc3
++# : #optimize="$optimize -finline-functions"
++# ;;
++# *)
++# # this sometimes provokes bugs in gcc 2.95.2
++# : #optimize="$optimize -fstrength-reduce"
++# ;;
++# esac
++# ;;
++# esac
++#fi
+
+ case "$host" in
+ mips*-agenda-*)
diff --git a/subprojects/shared-modules/libmad/libmad-0.15.1b-multiarch.patch b/subprojects/shared-modules/libmad/libmad-0.15.1b-multiarch.patch
new file mode 100644
index 00000000..51bf663b
--- /dev/null
+++ b/subprojects/shared-modules/libmad/libmad-0.15.1b-multiarch.patch
@@ -0,0 +1,37 @@
+diff -up libmad-0.15.1b/Makefile.am.orig libmad-0.15.1b/Makefile.am
+--- libmad-0.15.1b/Makefile.am.orig 2009-01-25 14:35:56.000000000 +0200
++++ libmad-0.15.1b/Makefile.am 2009-01-25 18:35:07.000000000 +0200
+@@ -110,15 +110,28 @@ mad.h: config.status config.h Makefile.a
+ echo "# ifdef __cplusplus"; \
+ echo 'extern "C" {'; \
+ echo "# endif"; echo; \
+- if [ ".$(FPM)" != "." ]; then \
+- echo ".$(FPM)" | sed -e 's|^\.-D|# define |'; echo; \
+- fi; \
++ echo "# ifdef __i386__"; \
++ echo "# define FPM_INTEL"; \
++ echo "# define SIZEOF_LONG 4"; \
++ echo "# endif"; \
++ echo "#ifdef __x86_64__";\
++ echo "# define FPM_64BIT"; \
++ echo "# define SIZEOF_LONG 8"; \
++ echo "# endif"; \
++ echo "#ifdef __powerpc__"; \
++ echo "#define FPM_PPC"; \
++ echo "#define SIZEOF_LONG 4"; \
++ echo "#endif"; \
++ echo "#ifdef __powerpc64__"; \
++ echo "#define FPM_PPC"; \
++ echo "#define SIZEOF_LONG 8"; \
++ echo "#endif"; echo; \
+ sed -ne 's/^# *define *\(HAVE_.*_ASM\).*/# define \1/p' \
+ config.h; echo; \
+ sed -ne 's/^# *define *OPT_\(SPEED\|ACCURACY\).*/# define OPT_\1/p' \
+ config.h; echo; \
+- sed -ne 's/^# *define *\(SIZEOF_.*\)/# define \1/p' \
+- config.h; echo; \
++ echo "# define SIZEOF_INT 4"; \
++ echo "# define SIZEOF_LONG_LONG 8"; echo; \
+ for header in $(exported_headers); do \
+ echo; \
+ sed -n -f $(srcdir)/mad.h.sed $(srcdir)/$$header; \
diff --git a/subprojects/shared-modules/libmad/libmad-0.15.1b-ppc.patch b/subprojects/shared-modules/libmad/libmad-0.15.1b-ppc.patch
new file mode 100644
index 00000000..fa6ee383
--- /dev/null
+++ b/subprojects/shared-modules/libmad/libmad-0.15.1b-ppc.patch
@@ -0,0 +1,13 @@
+--- libmad-0.15.1b/fixed.h~ 2004-02-17 02:02:03.000000000 +0000
++++ libmad-0.15.1b/fixed.h 2009-07-19 13:03:08.000000000 +0100
+@@ -379,8 +379,8 @@ mad_fixed_t mad_f_mul_inline(mad_fixed_t
+ asm ("addc %0,%2,%3\n\t" \
+ "adde %1,%4,%5" \
+ : "=r" (lo), "=r" (hi) \
+- : "%r" (lo), "r" (__lo), \
+- "%r" (hi), "r" (__hi) \
++ : "0" (lo), "r" (__lo), \
++ "1" (hi), "r" (__hi) \
+ : "xer"); \
+ })
+ # endif
diff --git a/subprojects/shared-modules/libmad/libmad.json b/subprojects/shared-modules/libmad/libmad.json
new file mode 100644
index 00000000..7ee6cc78
--- /dev/null
+++ b/subprojects/shared-modules/libmad/libmad.json
@@ -0,0 +1,41 @@
+{
+ "name" : "libmad",
+ "sources" : [
+ {
+ "type" : "archive",
+ "url" : "https://downloads.sourceforge.net/project/mad/libmad/0.15.1b/libmad-0.15.1b.tar.gz",
+ "sha256" : "bbfac3ed6bfbc2823d3775ebb931087371e142bb0e9bb1bee51a76a6e0078690"
+ },
+ {
+ "type": "patch",
+ "path": "libmad-0.15.1b-multiarch.patch"
+ },
+ {
+ "type": "patch",
+ "path": "libmad-0.15.1b-ppc.patch"
+ },
+ {
+ "type": "patch",
+ "path": "Provide-Thumb-2-alternative-code-for-MAD_F_MLN.diff"
+ },
+ {
+ "type": "patch",
+ "path": "libmad.thumb.diff"
+ },
+ {
+ "type": "patch",
+ "path": "libmad-0.15.1b-cflags.patch"
+ },
+ {
+ "type": "patch",
+ "path": "libmad-0.15.1b-cflags-O2.patch"
+ },
+ {
+ "type": "shell",
+ "commands": [
+ "cp /usr/share/gnu-config/config.sub .",
+ "cp /usr/share/gnu-config/config.guess ."
+ ]
+ }
+ ]
+}
diff --git a/subprojects/shared-modules/libmad/libmad.thumb.diff b/subprojects/shared-modules/libmad/libmad.thumb.diff
new file mode 100644
index 00000000..13edde21
--- /dev/null
+++ b/subprojects/shared-modules/libmad/libmad.thumb.diff
@@ -0,0 +1,12 @@
+--- ./imdct_l_arm.S.orig 2010-02-25 13:25:23.000000000 +0100
++++ ./imdct_l_arm.S 2010-02-25 13:27:26.000000000 +0100
+@@ -468,7 +468,7 @@
+
+ @----
+
+- add r2, pc, #(imdct36_long_karray-.-8) @ r2 = base address of Knn array (PIC safe ?)
++ adr r2, imdct36_long_karray
+
+
+ loop:
+
diff --git a/subprojects/shared-modules/libsecret/libsecret.json b/subprojects/shared-modules/libsecret/libsecret.json
new file mode 100644
index 00000000..a2d34f58
--- /dev/null
+++ b/subprojects/shared-modules/libsecret/libsecret.json
@@ -0,0 +1,23 @@
+{
+ "name": "libsecret",
+ "buildsystem": "meson",
+ "config-opts": [
+ "-Dmanpage=false",
+ "-Dvapi=false",
+ "-Dgtk_doc=false"
+ ],
+ "cleanup": [
+ "/bin",
+ "/include",
+ "/lib/pkgconfig",
+ "/share/gir-1.0",
+ "/share/man"
+ ],
+ "sources": [
+ {
+ "type": "archive",
+ "url": "https://ftp.gnome.org/pub/GNOME/sources/libsecret/0.20/libsecret-0.20.1.tar.xz",
+ "sha256": "57f73e94ec6263a17a077fb809cf8cf424637a897a7f15b4eec42ce4aef52447"
+ }
+ ]
+}
diff --git a/subprojects/shared-modules/lua5.3/lua-5.3.0-autotoolize.patch b/subprojects/shared-modules/lua5.3/lua-5.3.0-autotoolize.patch
new file mode 100644
index 00000000..3e4723a5
--- /dev/null
+++ b/subprojects/shared-modules/lua5.3/lua-5.3.0-autotoolize.patch
@@ -0,0 +1,192 @@
+diff -up lua-5.3.0/configure.ac.autoxxx lua-5.3.0/configure.ac
+--- lua-5.3.0/configure.ac.autoxxx 2015-01-15 10:20:03.826889574 -0500
++++ lua-5.3.0/configure.ac 2015-01-15 10:20:03.826889574 -0500
+@@ -0,0 +1,69 @@
++AC_PREREQ(2.59)
++AC_INIT([lua], [5.3.0], [https://bugzilla.redhat.com/], [lua-at], [http://www.lua.org])
++AC_SUBST([MAJOR_VERSION], [5.3])
++
++AC_CONFIG_HEADERS([config.h])
++AC_CONFIG_SRCDIR([src/lapi.c])
++
++AM_INIT_AUTOMAKE([1.9 foreign])
++
++AC_PROG_CC
++AC_PROG_LIBTOOL
++
++AC_ARG_WITH(
++ [readline],
++ [AC_HELP_STRING([--with-readline], [Use readline for interpreter input [default=yes]])],
++ [use_readline=$withval],
++ [use_readline=yes]
++)
++
++LUA_LIBS="-lm"
++
++# Check for readline
++READLINE_DEFS="#undef LUA_USE_READLINE"
++if test "x$use_readline" == "xyes"; then
++ AC_CHECK_LIB([readline], [readline], [:], [use_readline=no], [-lncurses])
++ AC_CHECK_HEADERS([readline/readline.h readline/history.h], [], [use_readline=no])
++ if test "x$use_readline" == "xno"; then
++ AC_MSG_WARN([readline headers could not be found, disabling readline support])
++ else
++ READLINE_DEFS="#define LUA_USE_READLINE"
++ READLINE_LIBS="-lreadline -lncurses"
++ fi
++fi
++AC_SUBST(READLINE_DEFS)
++AC_SUBST(READLINE_LIBS)
++
++case "$host" in
++ *-mingw*) use_os=win32 ;;
++ *-darwin*) use_os=macosx ;;
++ *) use_os=posix ;;
++esac
++
++POSIX_DEFS="#undef LUA_USE_POSIX"
++LUA_DL_DEFS="#undef LUA_USE_DLOPEN"
++LUA_BUILD_AS_DLL_DEFS="#undef LUA_BUILD_AS_DLL"
++
++if test "x$use_os" == "xwin32"; then
++ LUA_BUILD_AS_DLL_DEFS="#define LUA_BUILD_AS_DLL"
++elif test "x$use_os" == "xmacosx"; then
++ POSIX_DEFS="#define LUA_USE_POSIX"
++ LUA_DL_DEFS="#define LUA_DL_DYLD"
++elif test "x$use_os" == "xposix"; then
++ POSIX_DEFS="#define LUA_USE_POSIX"
++ LUA_DL_DEFS="#define LUA_DL_DLOPEN"
++ LUA_LIBS="$LUA_LIBS -ldl"
++fi
++AC_SUBST(POSIX_DEFS)
++AC_SUBST(LUA_DL_DEFS)
++AC_SUBST(LUA_BUILD_AS_DLL_DEFS)
++
++AC_SUBST(LUA_LIBS)
++
++AC_CONFIG_FILES([Makefile
++ src/Makefile
++ src/lua.pc
++ src/luaconf.h.template
++ doc/Makefile
++])
++AC_OUTPUT
+diff -up lua-5.3.0/doc/Makefile.am.autoxxx lua-5.3.0/doc/Makefile.am
+--- lua-5.3.0/doc/Makefile.am.autoxxx 2015-01-15 10:20:03.826889574 -0500
++++ lua-5.3.0/doc/Makefile.am 2015-01-15 10:20:03.826889574 -0500
+@@ -0,0 +1,4 @@
++man1_MANS = lua.1 luac.1
++
++EXTRA_DIST = \
++ contents.html logo.gif lua.1 luac.1 lua.css manual.css manual.html osi-certified-72x60.png readme.html
+diff -up lua-5.3.0/Makefile.am.autoxxx lua-5.3.0/Makefile.am
+--- lua-5.3.0/Makefile.am.autoxxx 2015-01-15 10:20:03.826889574 -0500
++++ lua-5.3.0/Makefile.am 2015-01-15 10:20:03.826889574 -0500
+@@ -0,0 +1,3 @@
++SUBDIRS = src doc
++
++EXTRA_DIST = README
+diff -up lua-5.3.0/src/.gitignore.autoxxx lua-5.3.0/src/.gitignore
+--- lua-5.3.0/src/.gitignore.autoxxx 2015-01-15 10:20:03.826889574 -0500
++++ lua-5.3.0/src/.gitignore 2015-01-15 10:20:03.826889574 -0500
+@@ -0,0 +1,5 @@
++lua
++lua.pc
++luac
++luaconf.h
++luaconf.h.template
+diff -up lua-5.3.0/src/luaconf.h.template.in.autoxxx lua-5.3.0/src/luaconf.h.template.in
+--- lua-5.3.0/src/luaconf.h.template.in.autoxxx 2015-01-15 10:20:03.828889562 -0500
++++ lua-5.3.0/src/luaconf.h.template.in 2015-01-15 10:22:37.420027778 -0500
+@@ -11,6 +11,11 @@
+ #include <limits.h>
+ #include <stddef.h>
+
++@POSIX_DEFS@
++@LUA_DL_DEFS@
++@LUA_BUILD_AS_DLL_DEFS@
++@READLINE_DEFS@
++
+
+ /*
+ ** ===================================================================
+@@ -175,9 +180,9 @@
+
+ #else /* }{ */
+
+-#define LUA_ROOT "/usr/local/"
+-#define LUA_LDIR LUA_ROOT "share/lua/" LUA_VDIR "/"
+-#define LUA_CDIR LUA_ROOT "lib/lua/" LUA_VDIR "/"
++#define LUA_ROOT "@prefix@/"
++#define LUA_LDIR "@pkgdatadir@/lua/" LUA_VDIR "/"
++#define LUA_CDIR "@libdir@/lua/" LUA_VDIR "/"
+ #define LUA_PATH_DEFAULT \
+ LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \
+ LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua;" \
+diff -up lua-5.3.0/src/lua.pc.in.autoxxx lua-5.3.0/src/lua.pc.in
+--- lua-5.3.0/src/lua.pc.in.autoxxx 2015-01-15 10:20:03.827889568 -0500
++++ lua-5.3.0/src/lua.pc.in 2015-01-15 10:20:03.827889568 -0500
+@@ -0,0 +1,13 @@
++V= @MAJOR_VERSION@
++R= @VERSION@
++prefix= @prefix@
++exec_prefix=${prefix}
++libdir= @libdir@
++includedir=${prefix}/include
++
++Name: Lua
++Description: An Extensible Extension Language
++Version: ${R}
++Requires:
++Libs: -llua @LUA_LIBS@
++Cflags: -I${includedir}
+diff -up lua-5.3.0/src/Makefile.am.autoxxx lua-5.3.0/src/Makefile.am
+--- lua-5.3.0/src/Makefile.am.autoxxx 2015-01-15 10:20:03.826889574 -0500
++++ lua-5.3.0/src/Makefile.am 2015-01-15 10:20:03.826889574 -0500
+@@ -0,0 +1,46 @@
++AM_CFLAGS = -Wall
++
++include_HEADERS = lua.h lualib.h lauxlib.h lua.hpp
++
++nodist_include_HEADERS = luaconf.h
++
++lib_LTLIBRARIES = liblua.la
++liblua_la_LDFLAGS = -release @MAJOR_VERSION@
++liblua_la_SOURCES = \
++ lapi.c lauxlib.c lbaselib.c lbitlib.c lcode.c lcorolib.c lctype.c ldblib.c \
++ ldebug.c ldo.c ldump.c lfunc.c lgc.c linit.c liolib.c llex.c lmathlib.c lmem.c \
++ loadlib.c lobject.c lopcodes.c loslib.c lparser.c lstate.c lstring.c lstrlib.c \
++ ltable.c ltablib.c ltm.c lundump.c lutf8lib.c lvm.c lzio.c \
++ lapi.h lcode.h lctype.h ldebug.h ldo.h lfunc.h lgc.h llex.h llimits.h \
++ lmem.h lobject.h lopcodes.h lparser.h lstate.h lstring.h ltable.h ltm.h \
++ lundump.h lvm.h lzio.h
++
++pkgconfigdir = $(libdir)/pkgconfig
++pkgconfig_DATA = lua.pc
++
++bin_PROGRAMS = lua luac
++
++lua_SOURCES = lua.c
++lua_LDADD = liblua.la @LUA_LIBS@ @READLINE_LIBS@
++lua_DEPENDENCIES = liblua.la
++
++luac_SOURCES = luac.c
++# Statically link liblua against luac since luac uses symbols not exported in liblua
++luac_LDADD = .libs/liblua.a @LUA_LIBS@
++luac_DEPENDENCIES = liblua.la
++
++EXTRA_DIST = luaconf.h.template
++BUILT_SOURCES = luaconf.h
++CLEANFILES = luaconf.h luaconf.h.template
++
++readline_defs = @READLINE_DEFS@
++
++edit = sed \
++ -e 's,%prefix%,$(prefix),g' \
++ -e 's,%lua_datadir%,$(datadir),g' \
++ -e 's,%lua_libdir%,$(libdir),g'
++
++luaconf.h : luaconf.h.template
++ rm -f $@ $@.tmp
++ $(edit) $< >$@.tmp
++ mv $@.tmp $@
diff --git a/subprojects/shared-modules/lua5.3/lua-5.3.0-configure-compat-module.patch b/subprojects/shared-modules/lua5.3/lua-5.3.0-configure-compat-module.patch
new file mode 100644
index 00000000..bd5d41d4
--- /dev/null
+++ b/subprojects/shared-modules/lua5.3/lua-5.3.0-configure-compat-module.patch
@@ -0,0 +1,35 @@
+diff -up lua-5.2.2/configure.ac.compat-module lua-5.2.2/configure.ac
+--- lua-5.2.2/configure.ac.compat-module 2013-05-10 10:16:05.344137597 -0400
++++ lua-5.2.2/configure.ac 2013-05-10 10:16:05.357137596 -0400
+@@ -11,6 +11,20 @@ AC_PROG_CC
+ AC_PROG_LIBTOOL
+
+ AC_ARG_WITH(
++ [compat-module],
++ [AC_HELP_STRING([--with-compat-module], [Enable LUA_COMPAT_MODULE functions [default=no]])],
++ [use_compat_module=$withval],
++ [use_compat_module=no]
++)
++
++COMPAT_DEFS="#undef LUA_COMPAT_ALL"
++if test "x$use_compat_module" == "xyes"; then
++ COMPAT_DEFS="#define LUA_COMPAT_5_1
++#define LUA_COMPAT_5_2"
++fi
++AC_SUBST(COMPAT_DEFS)
++
++AC_ARG_WITH(
+ [readline],
+ [AC_HELP_STRING([--with-readline], [Use readline for interpreter input [default=yes]])],
+ [use_readline=$withval],
+diff -up lua-5.2.2/src/luaconf.h.template.in.compat-module lua-5.2.2/src/luaconf.h.template.in
+--- lua-5.2.2/src/luaconf.h.template.in.compat-module 2013-05-10 10:25:42.586116963 -0400
++++ lua-5.2.2/src/luaconf.h.template.in 2013-05-10 10:26:29.957115269 -0400
+@@ -15,6 +15,7 @@
+ @LUA_DL_DEFS@
+ @LUA_BUILD_AS_DLL_DEFS@
+ @READLINE_DEFS@
++@COMPAT_DEFS@
+
+
+ /*
diff --git a/subprojects/shared-modules/lua5.3/lua-5.3.0-idsize.patch b/subprojects/shared-modules/lua5.3/lua-5.3.0-idsize.patch
new file mode 100644
index 00000000..16107fe2
--- /dev/null
+++ b/subprojects/shared-modules/lua5.3/lua-5.3.0-idsize.patch
@@ -0,0 +1,12 @@
+diff -up lua-5.3.0/src/luaconf.h.template.in.idsize lua-5.3.0/src/luaconf.h.template.in
+--- lua-5.3.0/src/luaconf.h.template.in.idsize 2015-01-15 10:23:20.515801344 -0500
++++ lua-5.3.0/src/luaconf.h.template.in 2015-01-15 10:23:48.955651916 -0500
+@@ -693,7 +693,7 @@
+ @@ of a function in debug information.
+ ** CHANGE it if you want a different size.
+ */
+-#define LUA_IDSIZE 60
++#define LUA_IDSIZE 512
+
+
+ /*
diff --git a/subprojects/shared-modules/lua5.3/lua-5.3.5.json b/subprojects/shared-modules/lua5.3/lua-5.3.5.json
new file mode 100644
index 00000000..fd5f2682
--- /dev/null
+++ b/subprojects/shared-modules/lua5.3/lua-5.3.5.json
@@ -0,0 +1,34 @@
+{
+ "name": "lua-5.3",
+ "config-opts": [
+ "--with-compat-module"
+ ],
+ "sources": [
+ {
+ "type": "archive",
+ "url": "https://www.lua.org/ftp/lua-5.3.5.tar.gz",
+ "sha256": "0c2eed3f960446e1a3e4b9a1ca2f3ff893b6ce41942cf54d5dd59ab4b3b058ac"
+ },
+ {
+ "type": "shell",
+ "commands": [ "mv src/luaconf.h src/luaconf.h.template.in" ]
+ },
+ {
+ "type": "patch",
+ "path": "lua-5.3.0-autotoolize.patch"
+ },
+ {
+ "type": "patch",
+ "path": "lua-5.3.0-idsize.patch"
+ },
+ {
+ "type": "patch",
+ "path": "lua-5.3.0-configure-compat-module.patch"
+ },
+ {
+ "type": "shell",
+ "commands": [ "autoreconf -i" ]
+ }
+ ],
+ "cleanup": [ "/bin", "/include", "/lib/pkgconfig", "/lib/*.a", "/lib/*.la", "/share/man" ]
+}
diff --git a/subprojects/shared-modules/openjpeg/openjpeg.json b/subprojects/shared-modules/openjpeg/openjpeg.json
new file mode 100644
index 00000000..5eefa4c8
--- /dev/null
+++ b/subprojects/shared-modules/openjpeg/openjpeg.json
@@ -0,0 +1,25 @@
+{
+ "name": "openjpeg",
+ "buildsystem": "cmake-ninja",
+ "builddir": true,
+ "config-opts": [
+ "-DCMAKE_BUILD_TYPE=RelWithDebInfo",
+ "-DBUILD_CODEC:BOOL=OFF",
+ "-DBUILD_DOC:BOOL=OFF",
+ "-DBUILD_STATIC_LIBS:BOOL=OFF",
+ "-DBUILD_TESTING:BOOL=OFF"
+ ],
+ "sources": [
+ {
+ "type": "archive",
+ "url": "https://github.com/uclouvain/openjpeg/archive/v2.3.1.tar.gz",
+ "sha256": "63f5a4713ecafc86de51bfad89cc07bb788e9bba24ebbf0c4ca637621aadb6a9"
+ }
+ ],
+ "cleanup": [
+ "/include",
+ "/lib/openjpeg-*",
+ "/lib/pkgconfig",
+ "/share"
+ ]
+}
diff --git a/subprojects/shared-modules/pygame/audiofile-gcc6.patch b/subprojects/shared-modules/pygame/audiofile-gcc6.patch
new file mode 100644
index 00000000..89afb6d8
--- /dev/null
+++ b/subprojects/shared-modules/pygame/audiofile-gcc6.patch
@@ -0,0 +1,21 @@
+From b62c902dd258125cac86cd2df21fc898035a43d3 Mon Sep 17 00:00:00 2001
+From: Michael Pruett <michael@68k.org>
+Date: Mon, 29 Aug 2016 23:08:26 -0500
+Subject: [PATCH] Fix undefined behavior in sign conversion.
+Origin: https://github.com/mpruett/audiofile/commit/b62c902dd258125cac86cd2df21fc898035a43d3
+
+---
+diff --git a/libaudiofile/modules/SimpleModule.h b/libaudiofile/modules/SimpleModule.h
+index 03c6c69..bad85ad 100644
+--- a/libaudiofile/modules/SimpleModule.h
++++ b/libaudiofile/modules/SimpleModule.h
+@@ -123,7 +123,8 @@ struct signConverter
+ typedef typename IntTypes<Format>::UnsignedType UnsignedType;
+
+ static const int kScaleBits = (Format + 1) * CHAR_BIT - 1;
+- static const int kMinSignedValue = -1 << kScaleBits;
++ static const int kMaxSignedValue = (((1 << (kScaleBits - 1)) - 1) << 1) + 1;
++ static const int kMinSignedValue = -kMaxSignedValue - 1;
+
+ struct signedToUnsigned : public std::unary_function<SignedType, UnsignedType>
+ {
diff --git a/subprojects/shared-modules/pygame/fluidsynth-no-rawmidi.patch b/subprojects/shared-modules/pygame/fluidsynth-no-rawmidi.patch
new file mode 100644
index 00000000..88a89742
--- /dev/null
+++ b/subprojects/shared-modules/pygame/fluidsynth-no-rawmidi.patch
@@ -0,0 +1,69 @@
+diff -rupN fluidsynth-1.1.6.orig/src/drivers/fluid_alsa.c fluidsynth-1.1.6/src/drivers/fluid_alsa.c
+--- fluidsynth-1.1.6.orig/src/drivers/fluid_alsa.c 2012-08-16 05:01:13.000000000 +0100
++++ fluidsynth-1.1.6/src/drivers/fluid_alsa.c 2017-02-28 21:26:57.033244239 +0000
+@@ -96,7 +96,7 @@ struct fluid_alsa_formats_t fluid_alsa_f
+ };
+
+
+-
++#if 0
+ /*
+ * fluid_alsa_rawmidi_driver_t
+ *
+@@ -119,7 +119,7 @@ fluid_midi_driver_t* new_fluid_alsa_rawm
+
+ int delete_fluid_alsa_rawmidi_driver(fluid_midi_driver_t* p);
+ static void fluid_alsa_midi_run(void* d);
+-
++#endif
+
+ /*
+ * fluid_alsa_seq_driver_t
+@@ -535,7 +535,7 @@ static void fluid_alsa_audio_run_s16 (vo
+ *
+ */
+
+-
++#if 0
+ void fluid_alsa_rawmidi_driver_settings(fluid_settings_t* settings)
+ {
+ fluid_settings_register_str(settings, "midi.alsa.device", "default", 0, NULL, NULL);
+@@ -698,7 +698,7 @@ fluid_alsa_midi_run(void* d)
+ }
+ }
+ }
+-
++#endif
+ /**************************************************************
+ *
+ * Alsa sequencer
+diff -rupN fluidsynth-1.1.6.orig/src/drivers/fluid_mdriver.c fluidsynth-1.1.6/src/drivers/fluid_mdriver.c
+--- fluidsynth-1.1.6.orig/src/drivers/fluid_mdriver.c 2012-08-16 05:01:13.000000000 +0100
++++ fluidsynth-1.1.6/src/drivers/fluid_mdriver.c 2017-02-28 21:24:43.887833321 +0000
+@@ -24,11 +24,13 @@
+
+ /* ALSA */
+ #if ALSA_SUPPORT
++#if 0
+ fluid_midi_driver_t* new_fluid_alsa_rawmidi_driver(fluid_settings_t* settings,
+ handle_midi_event_func_t handler,
+ void* event_handler_data);
+ int delete_fluid_alsa_rawmidi_driver(fluid_midi_driver_t* p);
+ void fluid_alsa_rawmidi_driver_settings(fluid_settings_t* settings);
++#endif
+
+ fluid_midi_driver_t* new_fluid_alsa_seq_driver(fluid_settings_t* settings,
+ handle_midi_event_func_t handler,
+@@ -109,10 +111,12 @@ struct fluid_mdriver_definition_t fluid_
+ fluid_oss_midi_driver_settings },
+ #endif
+ #if ALSA_SUPPORT
++#if 0
+ { "alsa_raw",
+ new_fluid_alsa_rawmidi_driver,
+ delete_fluid_alsa_rawmidi_driver,
+ fluid_alsa_rawmidi_driver_settings },
++#endif
+ { "alsa_seq",
+ new_fluid_alsa_seq_driver,
+ delete_fluid_alsa_seq_driver,
diff --git a/subprojects/shared-modules/pygame/portmidi-no-java.patch b/subprojects/shared-modules/pygame/portmidi-no-java.patch
new file mode 100644
index 00000000..00ef8eaa
--- /dev/null
+++ b/subprojects/shared-modules/pygame/portmidi-no-java.patch
@@ -0,0 +1,105 @@
+diff -rupN portmidi.orig/CMakeLists.txt portmidi/CMakeLists.txt
+--- portmidi.orig/CMakeLists.txt 2010-09-20 15:57:48.000000000 +0100
++++ portmidi/CMakeLists.txt 2017-03-03 13:50:58.494561245 +0000
+@@ -71,7 +71,3 @@ add_subdirectory(pm_common)
+ add_subdirectory(pm_test)
+
+ add_subdirectory(pm_dylib)
+-
+-# Cannot figure out how to make an xcode Java application with CMake
+-add_subdirectory(pm_java)
+-
+diff -rupN portmidi.orig/pm_common/CMakeLists.txt portmidi/pm_common/CMakeLists.txt
+--- portmidi.orig/pm_common/CMakeLists.txt 2010-09-20 15:57:48.000000000 +0100
++++ portmidi/pm_common/CMakeLists.txt 2017-03-03 14:02:32.851938051 +0000
+@@ -67,14 +67,6 @@ if(UNIX)
+ message(STATUS "SYSROOT: " ${CMAKE_OSX_SYSROOT})
+ else(APPLE)
+ # LINUX settings...
+- include(FindJNI)
+- message(STATUS "JAVA_JVM_LIB_PATH is " ${JAVA_JVM_LIB_PATH})
+- message(STATUS "JAVA_INCLUDE_PATH is " ${JAVA_INCLUDE_PATH})
+- message(STATUS "JAVA_INCLUDE_PATH2 is " ${JAVA_INCLUDE_PATH2})
+- message(STATUS "JAVA_JVM_LIBRARY is " ${JAVA_JVM_LIBRARY})
+- set(JAVA_INCLUDE_PATHS ${JAVA_INCLUDE_PATH} ${JAVA_INCLUDE_PATH2})
+- # libjvm.so is found relative to JAVA_INCLUDE_PATH:
+- set(JAVAVM_LIB ${JAVA_JVM_LIBRARY}/libjvm.so)
+
+ set(LINUXSRC pmlinuxalsa pmlinux finddefault)
+ prepend_path(LIBSRC ../pm_linux/ ${LINUXSRC})
+@@ -99,7 +91,6 @@ else(UNIX)
+ set(PM_NEEDED_LIBS winmm.lib)
+ endif(WIN32)
+ endif(UNIX)
+-set(JNI_EXTRA_LIBS ${PM_NEEDED_LIBS} ${JAVA_JVM_LIBRARY})
+
+ # this completes the list of library sources by adding shared code
+ list(APPEND LIBSRC pmutil portmidi)
+@@ -109,19 +100,11 @@ add_library(portmidi-static ${LIBSRC})
+ set_target_properties(portmidi-static PROPERTIES OUTPUT_NAME "portmidi_s")
+ target_link_libraries(portmidi-static ${PM_NEEDED_LIBS})
+
+-# define the jni library
+-include_directories(${JAVA_INCLUDE_PATHS})
+-
+-set(JNISRC ${LIBSRC} ../pm_java/pmjni/pmjni.c)
+-add_library(pmjni SHARED ${JNISRC})
+-target_link_libraries(pmjni ${JNI_EXTRA_LIBS})
+-set_target_properties(pmjni PROPERTIES EXECUTABLE_EXTENSION "jnilib")
+-
+ # install the libraries (Linux and Mac OS X command line)
+ if(UNIX)
+- INSTALL(TARGETS portmidi-static pmjni
+- LIBRARY DESTINATION /usr/local/lib
+- ARCHIVE DESTINATION /usr/local/lib)
++ INSTALL(TARGETS portmidi-static
++ LIBRARY DESTINATION /app/lib
++ ARCHIVE DESTINATION /app/lib)
+ # .h files installed by pm_dylib/CMakeLists.txt, so don't need them here
+ # INSTALL(FILES portmidi.h ../porttime/porttime.h
+ # DESTINATION /usr/local/include)
+diff -rupN portmidi.orig/pm_dylib/CMakeLists.txt portmidi/pm_dylib/CMakeLists.txt
+--- portmidi.orig/pm_dylib/CMakeLists.txt 2009-11-20 00:41:10.000000000 +0000
++++ portmidi/pm_dylib/CMakeLists.txt 2017-03-03 14:03:56.807104521 +0000
+@@ -63,7 +63,6 @@ if(UNIX)
+ message(STATUS "SYSROOT: " ${CMAKE_OSX_SYSROOT})
+ else(APPLE)
+ # LINUX settings...
+- include(FindJNI)
+ # message(STATUS "JAVA_JVM_LIB_PATH is " ${JAVA_JVM_LIB_PATH})
+ # message(STATUS "JAVA_INCLUDE_PATH is " ${JAVA_INCLUDE_PATH})
+ # note: should use JAVA_JVM_LIB_PATH, but it is not set properly
+@@ -75,12 +74,7 @@ if(UNIX)
+ # JAVA_INCLUDE_PATH2; if no, then we need to make both JAVA_INCLUDE_PATH
+ # and JAVA_INCLUDE_PATH2 set by user (will need clear documentation
+ # because JAVA_INCLUDE_PATH2 is pretty obscure)
+- set(JAVA_INCLUDE_PATH ${JAVA_INCLUDE_PATH-UNKNOWN}
+- CACHE STRING "where to find Java SDK include directory")
+- set(JAVA_INCLUDE_PATHS ${JAVA_INCLUDE_PATH} ${JAVA_INCLUDE_PATH}/linux)
+- # libjvm.so is found relative to JAVA_INCLUDE_PATH:
+- set(JAVAVM_LIB ${JAVA_INCLUDE_PATH}/../jre/lib/i386/client/libjvm.so)
+-
++
+ set(LINUXSRC pmlinuxalsa pmlinux finddefault)
+ prepend_path(LIBSRC ../pm_linux/ ${LINUXSRC})
+ list(APPEND LIBSRC ../porttime/ptlinux)
+@@ -106,7 +100,6 @@ else(UNIX)
+ # message(STATUS "JAVAVM_LIB: " ${JAVAVM_LIB})
+ endif(WIN32)
+ endif(UNIX)
+-set(JNI_EXTRA_LIBS ${PM_NEEDED_LIBS} ${JAVAVM_LIB})
+
+ # this completes the list of library sources by adding shared code
+ set(SHARED_FILES pmutil portmidi)
+@@ -120,8 +113,8 @@ target_link_libraries(portmidi-dynamic $
+ # install the libraries (Linux and Mac OS X command line)
+ if(UNIX)
+ INSTALL(TARGETS portmidi-dynamic
+- LIBRARY DESTINATION /usr/local/lib
+- ARCHIVE DESTINATION /usr/local/lib)
++ LIBRARY DESTINATION /app/lib
++ ARCHIVE DESTINATION /app/lib)
+ INSTALL(FILES ../pm_common/portmidi.h ../porttime/porttime.h
+- DESTINATION /usr/local/include)
++ DESTINATION /app/include)
+ endif(UNIX)
diff --git a/subprojects/shared-modules/pygame/pygame-1.9.4.json b/subprojects/shared-modules/pygame/pygame-1.9.4.json
new file mode 100644
index 00000000..b80e892d
--- /dev/null
+++ b/subprojects/shared-modules/pygame/pygame-1.9.4.json
@@ -0,0 +1,112 @@
+{
+ "name": "pygame",
+ "sources": [
+ {
+ "type": "archive",
+ "url": "https://files.pythonhosted.org/packages/b2/6b/c510f0853765eb2219ca5aa3d416d65bb0dea7cd9bb2984aea0a0e04c24d/pygame-1.9.4.tar.gz",
+ "sha256": "700d1781c999af25d11bfd1f3e158ebb660f72ebccb2040ecafe5069d0b2c0b6"
+ }
+ ],
+ "buildsystem": "simple",
+ "build-commands": [
+ "pip3 install --ignore-installed --no-deps --prefix=/app ."
+ ],
+ "build-options": {
+ "env": {
+ "PORTMIDI_INC_PORTTIME": "1",
+ "PYGAME_EXTRA_BASE": "/app"
+ },
+ "arch": {
+ "aarch64": {
+ "env": {
+ "ORIGLIBDIRS": "/lib:/lib64:/lib/aarch64-linux-gnu"
+ }
+ },
+ "arm": {
+ "env": {
+ "ORIGLIBDIRS": "/lib:/lib/arm-linux-gnueabihf"
+ }
+ },
+ "i386": {
+ "env": {
+ "ORIGLIBDIRS": "/lib:/lib/i386-linux-gnu"
+ }
+ },
+ "x86_64": {
+ "env": {
+ "ORIGLIBDIRS": "/lib:/lib64:/lib/x86_64-linux-gnu"
+ }
+ }
+ }
+ },
+ "modules": [
+ "../SDL/SDL-1.2.15.json",
+ "../SDL/SDL_image-1.2.12.json",
+ "../SDL/SDL_ttf-2.0.11.json",
+ "../smpeg/smpeg-0.4.5.json",
+ "../SDL/SDL_mixer-1.2.12.json",
+ {
+ "name": "audiofile",
+ "sources": [
+ {
+ "type": "archive",
+ "url": "http://audiofile.68k.org/audiofile-0.3.6.tar.gz",
+ "sha256": "cdc60df19ab08bfe55344395739bb08f50fc15c92da3962fac334d3bff116965"
+ },
+ {
+ "type": "patch",
+ "path": "audiofile-gcc6.patch"
+ }
+ ]
+ },
+ {
+ "name": "libmikmod",
+ "sources": [
+ {
+ "type": "archive",
+ "url": "https://sourceforge.net/projects/mikmod/files/libmikmod/3.3.11.1/libmikmod-3.3.11.1.tar.gz",
+ "sha256": "ad9d64dfc8f83684876419ea7cd4ff4a41d8bcd8c23ef37ecb3a200a16b46d19"
+ }
+ ]
+ },
+ {
+ "name": "fluidsynth",
+ "buildsystem": "cmake-ninja",
+ "config-opts": [
+ "-DCMAKE_BUILD_TYPE=Release"
+ ],
+ "sources": [
+ {
+ "type": "archive",
+ "url": "https://downloads.sourceforge.net/project/fluidsynth/fluidsynth-1.1.6/fluidsynth-1.1.6.tar.bz2",
+ "sha256": "d28b47dfbf7f8e426902ae7fa2981d821fbf84f41da9e1b85be933d2d748f601"
+ },
+ {
+ "type": "patch",
+ "path": "fluidsynth-no-rawmidi.patch"
+ }
+ ]
+ },
+ {
+ "name": "portmidi",
+ "buildsystem": "cmake-ninja",
+ "config-opts": [
+ "-DCMAKE_BUILD_TYPE=Release",
+ "-DCMAKE_LIBRARY_OUTPUT_DIRECTORY:STRING=/app/lib",
+ "-DCMAKE_ARCHIVE_OUTPUT_DIRECTORY:STRING=/app/lib",
+ "-DCMAKE_RUNTIME_OUTPUT_DIRECTORY:STRING=/app/bin"
+ ],
+ "sources": [
+ {
+ "type": "archive",
+ "url": "http://downloads.sourceforge.net/project/portmedia/portmidi/217/portmidi-src-217.zip",
+ "sha256": "08e9a892bd80bdb1115213fb72dc29a7bf2ff108b378180586aa65f3cfd42e0f"
+ },
+ {
+ "type": "patch",
+ "path": "portmidi-no-java.patch"
+ }
+ ]
+ }
+ ]
+}
diff --git a/subprojects/shared-modules/python2.7/python-2.7.json b/subprojects/shared-modules/python2.7/python-2.7.json
new file mode 100644
index 00000000..14253764
--- /dev/null
+++ b/subprojects/shared-modules/python2.7/python-2.7.json
@@ -0,0 +1,52 @@
+{
+ "name": "python-2.7",
+ "sources": [
+ {
+ "type": "archive",
+ "url": "https://www.python.org/ftp/python/2.7.17/Python-2.7.17.tar.xz",
+ "sha256": "4d43f033cdbd0aa7b7023c81b0e986fd11e653b5248dac9144d508f11812ba41"
+ }
+ ],
+ "config-opts": [
+ "--enable-shared",
+ "--with-ensurepip=yes",
+ "--with-system-expat",
+ "--with-system-ffi",
+ "--enable-loadable-sqlite-extensions",
+ "--with-dbmliborder=gdbm",
+ "--enable-unicode=ucs4"
+ ],
+ "post-install": [
+ /* Theres seem to be a permissions missmatch that causes the debug stripping to fail */
+ "chmod 644 $FLATPAK_DEST/lib/libpython2.7.so.1.0"
+ ],
+ "cleanup": [
+ "/bin/2to3*",
+ "/bin/easy_install*",
+ "/bin/idle*",
+ "/bin/pydoc*",
+ "/bin/python*-config",
+ "/bin/pyvenv*",
+ "/include",
+ "/lib/pkgconfig",
+ "/lib/python*/config",
+ "/share",
+
+ /* Test scripts */
+ "/lib/python*/test",
+ "/lib/python*/*/test",
+ "/lib/python*/*/tests",
+ "/lib/python*/lib-tk/test",
+ "/lib/python*/lib-dynload/_*_test.*.so",
+ "/lib/python*/lib-dynload/_test*.*.so",
+
+ /* Unused modules */
+ "/lib/python*/idlelib",
+ "/lib/python*/tkinter*",
+ "/lib/python*/turtle*",
+ "/lib/python*/lib2to3*",
+
+ /* Static library */
+ "/lib/python2.7/config/libpython2.7.a"
+ ]
+}
diff --git a/subprojects/shared-modules/qt4/disable-sslv3.patch b/subprojects/shared-modules/qt4/disable-sslv3.patch
new file mode 100644
index 00000000..5ebd9741
--- /dev/null
+++ b/subprojects/shared-modules/qt4/disable-sslv3.patch
@@ -0,0 +1,54 @@
+diff -u -r qt-everywhere-opensource-src-4.8.7/src/network/ssl/qsslsocket_openssl.cpp qt-everywhere-opensource-src-4.8.7-nossl3/src/network/ssl/qsslsocket_openssl.cpp
+--- qt-everywhere-opensource-src-4.8.7/src/network/ssl/qsslsocket_openssl.cpp 2015-05-07 16:14:44.000000000 +0200
++++ qt-everywhere-opensource-src-4.8.7-nossl3/src/network/ssl/qsslsocket_openssl.cpp 2016-03-04 11:29:17.119300898 +0100
+@@ -267,7 +267,11 @@
+ #endif
+ break;
+ case QSsl::SslV3:
++#ifndef OPENSSL_NO_SSL3
+ ctx = q_SSL_CTX_new(client ? q_SSLv3_client_method() : q_SSLv3_server_method());
++#else
++ ctx = 0; // SSL 3 not supported by the system, but chosen deliberately -> error
++#endif
+ break;
+ case QSsl::SecureProtocols: // SslV2 will be disabled below
+ case QSsl::TlsV1SslV3: // SslV2 will be disabled below
+diff -u -r qt-everywhere-opensource-src-4.8.7/src/network/ssl/qsslsocket_openssl_symbols.cpp qt-everywhere-opensource-src-4.8.7-nossl3/src/network/ssl/qsslsocket_openssl_symbols.cpp
+--- qt-everywhere-opensource-src-4.8.7/src/network/ssl/qsslsocket_openssl_symbols.cpp 2015-05-07 16:14:44.000000000 +0200
++++ qt-everywhere-opensource-src-4.8.7-nossl3/src/network/ssl/qsslsocket_openssl_symbols.cpp 2016-03-04 11:28:52.806050135 +0100
+@@ -228,13 +228,17 @@
+ #ifndef OPENSSL_NO_SSL2
+ DEFINEFUNC(const SSL_METHOD *, SSLv2_client_method, DUMMYARG, DUMMYARG, return 0, return)
+ #endif
++#ifndef OPENSSL_NO_SSL3
+ DEFINEFUNC(const SSL_METHOD *, SSLv3_client_method, DUMMYARG, DUMMYARG, return 0, return)
++#endif
+ DEFINEFUNC(const SSL_METHOD *, SSLv23_client_method, DUMMYARG, DUMMYARG, return 0, return)
+ DEFINEFUNC(const SSL_METHOD *, TLSv1_client_method, DUMMYARG, DUMMYARG, return 0, return)
+ #ifndef OPENSSL_NO_SSL2
+ DEFINEFUNC(const SSL_METHOD *, SSLv2_server_method, DUMMYARG, DUMMYARG, return 0, return)
+ #endif
++#ifndef OPENSSL_NO_SSL3
+ DEFINEFUNC(const SSL_METHOD *, SSLv3_server_method, DUMMYARG, DUMMYARG, return 0, return)
++#endif
+ DEFINEFUNC(const SSL_METHOD *, SSLv23_server_method, DUMMYARG, DUMMYARG, return 0, return)
+ DEFINEFUNC(const SSL_METHOD *, TLSv1_server_method, DUMMYARG, DUMMYARG, return 0, return)
+ #else
+@@ -822,13 +826,17 @@
+ #ifndef OPENSSL_NO_SSL2
+ RESOLVEFUNC(SSLv2_client_method)
+ #endif
++#ifndef OPENSSL_NO_SSL3
+ RESOLVEFUNC(SSLv3_client_method)
++#endif
+ RESOLVEFUNC(SSLv23_client_method)
+ RESOLVEFUNC(TLSv1_client_method)
+ #ifndef OPENSSL_NO_SSL2
+ RESOLVEFUNC(SSLv2_server_method)
+ #endif
++#ifndef OPENSSL_NO_SSL3
+ RESOLVEFUNC(SSLv3_server_method)
++#endif
+ RESOLVEFUNC(SSLv23_server_method)
+ RESOLVEFUNC(TLSv1_server_method)
+ RESOLVEFUNC(X509_NAME_entry_count)
diff --git a/subprojects/shared-modules/qt4/qt4-4.8.7-minimal.json b/subprojects/shared-modules/qt4/qt4-4.8.7-minimal.json
new file mode 100644
index 00000000..fe7b74a4
--- /dev/null
+++ b/subprojects/shared-modules/qt4/qt4-4.8.7-minimal.json
@@ -0,0 +1,110 @@
+{
+ "name": "qt4",
+ "config-opts": [
+ "-verbose",
+ "-confirm-license",
+ "-opensource",
+ "-release",
+ "-shared",
+ "-no-static",
+ "-fast",
+ "-datadir", "/app/lib/qt4",
+ "-importdir", "/app/lib/qt4/imports",
+ "-plugindir", "/app/lib/qt4/plugins",
+ "-translationdir", "/app/share/qt4/translations",
+ "-accessibility",
+ "-exceptions",
+ "-fontconfig",
+ "-glib",
+ "-dbus-linked",
+ "-openssl-linked",
+ "-optimized-qmake",
+ "-system-libjpeg",
+ "-system-libpng",
+ "-system-libtiff",
+ "-system-proxies",
+ "-system-zlib",
+ "-no-cups",
+ "-no-declarative",
+ "-no-gtkstyle",
+ "-no-javascript-jit",
+ "-no-libmng",
+ "-no-multimedia",
+ "-no-nis",
+ "-no-opengl",
+ "-no-phonon",
+ "-no-phonon-backend",
+ "-no-qt3support",
+ "-no-rpath",
+ "-no-script",
+ "-no-scripttools",
+ "-no-sql-db2",
+ "-no-sql-ibase",
+ "-no-sql-mysql",
+ "-no-sql-oci",
+ "-no-sql-odbc",
+ "-no-sql-psql",
+ "-no-sql-sqlite",
+ "-no-sql-sqlite2",
+ "-no-sql-sqlite_symbian",
+ "-no-sql-symsql",
+ "-no-sql-tds",
+ "-no-svg",
+ "-no-webkit",
+ "-no-xmlpatterns",
+ "-nomake", "demos",
+ "-nomake", "docs",
+ "-nomake", "examples"
+ ],
+ "build-options": {
+ "env": {
+ "CXXFLAGS": "-std=gnu++98 -O2",
+ "LD_LIBRARY_PATH": "/run/build/qt4/lib/"
+ }
+ },
+ "sources": [
+ {
+ "type": "archive",
+ "url": "https://download.qt.io/archive/qt/4.8/4.8.7/qt-everywhere-opensource-src-4.8.7.tar.gz",
+ "sha256": "e2882295097e47fe089f8ac741a95fef47e0a73a3f3cdf21b56990638f626ea0"
+ },
+ {
+ "type": "patch",
+ "path": "qt4-aarch64.patch"
+ },
+ {
+ "type": "patch",
+ "path": "disable-sslv3.patch"
+ },
+ {
+ "type": "shell",
+ "commands": [
+ "sed -i \"s|-O2|${CXXFLAGS}|\" mkspecs/common/{g++,gcc}-base.conf",
+ "sed -i \"/^QMAKE_LFLAGS_RPATH/s| -Wl,-rpath,||g\" mkspecs/common/gcc-base-unix.conf",
+ "sed -i \"/^QMAKE_LFLAGS\s/s|+=|+= ${LDFLAGS}|g\" mkspecs/common/gcc-base.conf"
+ ]
+ },
+ {
+ "type": "file",
+ "path": "qt4-openssl-1.1.patch"
+ },
+ {
+ "type": "shell",
+ "commands": [
+ "echo -e '#include<openssl/opensslv.h>\nint main(){return OPENSSL_VERSION_NUMBER>=0x010100000L?0:1;}' >has_openssl_1_1.c",
+ "gcc ${CFLAGS} has_openssl_1_1.c -o has_openssl_1_1",
+ "if ./has_openssl_1_1; then patch -p1 -i qt4-openssl-1.1.patch; fi"
+ ]
+ }
+ ],
+ "cleanup": [
+ "/bin",
+ "/include",
+ "/lib/*.a",
+ "/lib/*.la",
+ "/lib/*.prl",
+ "/lib/pkgconfig",
+ "/lib/qt4/mkspecs",
+ "/lib/qt4/q3porting.xml"
+ ]
+}
diff --git a/subprojects/shared-modules/qt4/qt4-aarch64.patch b/subprojects/shared-modules/qt4/qt4-aarch64.patch
new file mode 100644
index 00000000..cbe815de
--- /dev/null
+++ b/subprojects/shared-modules/qt4/qt4-aarch64.patch
@@ -0,0 +1,514 @@
+diff -up qt-everywhere-opensource-src-4.8.7/include/QtCore/headers.pri.aarch64 qt-everywhere-opensource-src-4.8.7/include/QtCore/headers.pri
+--- qt-everywhere-opensource-src-4.8.7/include/QtCore/headers.pri.aarch64 2015-05-08 21:48:32.714057739 -0500
++++ qt-everywhere-opensource-src-4.8.7/include/QtCore/headers.pri 2015-05-08 21:53:21.088761971 -0500
+@@ -1,3 +1,3 @@
+-SYNCQT.HEADER_FILES = ../corelib/kernel/qabstracteventdispatcher.h ../corelib/kernel/qabstractitemmodel.h ../corelib/kernel/qbasictimer.h ../corelib/kernel/qcoreapplication.h ../corelib/kernel/qcoreevent.h ../corelib/kernel/qeventloop.h ../corelib/kernel/qfunctions_nacl.h ../corelib/kernel/qfunctions_vxworks.h ../corelib/kernel/qfunctions_wince.h ../corelib/kernel/qmath.h ../corelib/kernel/qmetaobject.h ../corelib/kernel/qmetatype.h ../corelib/kernel/qmimedata.h ../corelib/kernel/qobject.h ../corelib/kernel/qobjectcleanuphandler.h ../corelib/kernel/qobjectdefs.h ../corelib/kernel/qpointer.h ../corelib/kernel/qsharedmemory.h ../corelib/kernel/qsignalmapper.h ../corelib/kernel/qsocketnotifier.h ../corelib/kernel/qsystemsemaphore.h ../corelib/kernel/qtimer.h ../corelib/kernel/qtranslator.h ../corelib/kernel/qvariant.h ../corelib/animation/qabstractanimation.h ../corelib/animation/qanimationgroup.h ../corelib/animation/qparallelanimationgroup.h ../corelib/animation/qpauseanimation.h ../corelib/animation/qpropertyanimation.h ../corelib/animation/qsequentialanimationgroup.h ../corelib/animation/qvariantanimation.h ../corelib/arch/qatomic_alpha.h ../corelib/arch/qatomic_arch.h ../corelib/arch/qatomic_arm.h ../corelib/arch/qatomic_armv5.h ../corelib/arch/qatomic_armv6.h ../corelib/arch/qatomic_armv7.h ../corelib/arch/qatomic_avr32.h ../corelib/arch/qatomic_bfin.h ../corelib/arch/qatomic_bootstrap.h ../corelib/arch/qatomic_generic.h ../corelib/arch/qatomic_i386.h ../corelib/arch/qatomic_ia64.h ../corelib/arch/qatomic_integrity.h ../corelib/arch/qatomic_m68k.h ../corelib/arch/qatomic_macosx.h ../corelib/arch/qatomic_mips.h ../corelib/arch/qatomic_parisc.h ../corelib/arch/qatomic_powerpc.h ../corelib/arch/qatomic_s390.h ../corelib/arch/qatomic_sh.h ../corelib/arch/qatomic_sh4a.h ../corelib/arch/qatomic_sparc.h ../corelib/arch/qatomic_symbian.h ../corelib/arch/qatomic_vxworks.h ../corelib/arch/qatomic_windows.h ../corelib/arch/qatomic_windowsce.h ../corelib/arch/qatomic_x86_64.h ../corelib/tools/qalgorithms.h ../corelib/tools/qbitarray.h ../corelib/tools/qbytearray.h ../corelib/tools/qbytearraymatcher.h ../corelib/tools/qcache.h ../corelib/tools/qchar.h ../corelib/tools/qcontainerfwd.h ../corelib/tools/qcontiguouscache.h ../corelib/tools/qcryptographichash.h ../corelib/tools/qdatetime.h ../corelib/tools/qeasingcurve.h ../corelib/tools/qelapsedtimer.h ../corelib/tools/qhash.h ../corelib/tools/qiterator.h ../corelib/tools/qline.h ../corelib/tools/qlinkedlist.h ../corelib/tools/qlist.h ../corelib/tools/qlocale.h ../corelib/tools/qlocale_blackberry.h ../corelib/tools/qmap.h ../corelib/tools/qmargins.h ../corelib/tools/qpair.h ../corelib/tools/qpoint.h ../corelib/tools/qqueue.h ../corelib/tools/qrect.h ../corelib/tools/qregexp.h ../corelib/tools/qscopedpointer.h ../corelib/tools/qscopedvaluerollback.h ../corelib/tools/qset.h ../corelib/tools/qshareddata.h ../corelib/tools/qsharedpointer.h ../corelib/tools/qsharedpointer_impl.h ../corelib/tools/qsize.h ../corelib/tools/qstack.h ../corelib/tools/qstring.h ../corelib/tools/qstringbuilder.h ../corelib/tools/qstringlist.h ../corelib/tools/qstringmatcher.h ../corelib/tools/qtextboundaryfinder.h ../corelib/tools/qtimeline.h ../corelib/tools/qvarlengtharray.h ../corelib/tools/qvector.h ../corelib/plugin/qfactoryinterface.h ../corelib/plugin/qlibrary.h ../corelib/plugin/qplugin.h ../corelib/plugin/qpluginloader.h ../corelib/plugin/quuid.h ../corelib/xml/qxmlstream.h ../corelib/thread/qatomic.h ../corelib/thread/qbasicatomic.h ../corelib/thread/qmutex.h ../corelib/thread/qreadwritelock.h ../corelib/thread/qsemaphore.h ../corelib/thread/qthread.h ../corelib/thread/qthreadstorage.h ../corelib/thread/qwaitcondition.h ../corelib/statemachine/qabstractstate.h ../corelib/statemachine/qabstracttransition.h ../corelib/statemachine/qeventtransition.h ../corelib/statemachine/qfinalstate.h ../corelib/statemachine/qhistorystate.h ../corelib/statemachine/qsignaltransition.h ../corelib/statemachine/qstate.h ../corelib/statemachine/qstatemachine.h ../corelib/concurrent/qfuture.h ../corelib/concurrent/qfutureinterface.h ../corelib/concurrent/qfuturesynchronizer.h ../corelib/concurrent/qfuturewatcher.h ../corelib/concurrent/qrunnable.h ../corelib/concurrent/qtconcurrentcompilertest.h ../corelib/concurrent/qtconcurrentexception.h ../corelib/concurrent/qtconcurrentfilter.h ../corelib/concurrent/qtconcurrentfilterkernel.h ../corelib/concurrent/qtconcurrentfunctionwrappers.h ../corelib/concurrent/qtconcurrentiteratekernel.h ../corelib/concurrent/qtconcurrentmap.h ../corelib/concurrent/qtconcurrentmapkernel.h ../corelib/concurrent/qtconcurrentmedian.h ../corelib/concurrent/qtconcurrentreducekernel.h ../corelib/concurrent/qtconcurrentresultstore.h ../corelib/concurrent/qtconcurrentrun.h ../corelib/concurrent/qtconcurrentrunbase.h ../corelib/concurrent/qtconcurrentstoredfunctioncall.h ../corelib/concurrent/qtconcurrentthreadengine.h ../corelib/concurrent/qthreadpool.h ../corelib/io/qabstractfileengine.h ../corelib/io/qbuffer.h ../corelib/io/qdatastream.h ../corelib/io/qdebug.h ../corelib/io/qdir.h ../corelib/io/qdiriterator.h ../corelib/io/qfile.h ../corelib/io/qfileinfo.h ../corelib/io/qfilesystemwatcher.h ../corelib/io/qfsfileengine.h ../corelib/io/qiodevice.h ../corelib/io/qprocess.h ../corelib/io/qresource.h ../corelib/io/qsettings.h ../corelib/io/qtemporaryfile.h ../corelib/io/qtextstream.h ../corelib/io/qurl.h ../corelib/global/qconfig-dist.h ../corelib/global/qconfig-large.h ../corelib/global/qconfig-medium.h ../corelib/global/qconfig-minimal.h ../corelib/global/qconfig-nacl.h ../corelib/global/qconfig-small.h ../corelib/global/qendian.h ../corelib/global/qfeatures.h ../corelib/global/qglobal.h ../corelib/global/qlibraryinfo.h ../corelib/global/qnamespace.h ../corelib/global/qnumeric.h ../corelib/global/qt_windows.h ../corelib/global/qconfig.h ../corelib/codecs/qtextcodec.h ../corelib/codecs/qtextcodecplugin.h ../../include/QtCore/QtCore
++SYNCQT.HEADER_FILES = ../corelib/kernel/qabstracteventdispatcher.h ../corelib/kernel/qabstractitemmodel.h ../corelib/kernel/qbasictimer.h ../corelib/kernel/qcoreapplication.h ../corelib/kernel/qcoreevent.h ../corelib/kernel/qeventloop.h ../corelib/kernel/qfunctions_nacl.h ../corelib/kernel/qfunctions_vxworks.h ../corelib/kernel/qfunctions_wince.h ../corelib/kernel/qmath.h ../corelib/kernel/qmetaobject.h ../corelib/kernel/qmetatype.h ../corelib/kernel/qmimedata.h ../corelib/kernel/qobject.h ../corelib/kernel/qobjectcleanuphandler.h ../corelib/kernel/qobjectdefs.h ../corelib/kernel/qpointer.h ../corelib/kernel/qsharedmemory.h ../corelib/kernel/qsignalmapper.h ../corelib/kernel/qsocketnotifier.h ../corelib/kernel/qsystemsemaphore.h ../corelib/kernel/qtimer.h ../corelib/kernel/qtranslator.h ../corelib/kernel/qvariant.h ../corelib/animation/qabstractanimation.h ../corelib/animation/qanimationgroup.h ../corelib/animation/qparallelanimationgroup.h ../corelib/animation/qpauseanimation.h ../corelib/animation/qpropertyanimation.h ../corelib/animation/qsequentialanimationgroup.h ../corelib/animation/qvariantanimation.h ../corelib/arch/qatomic_aarch64.h ../corelib/arch/qatomic_alpha.h ../corelib/arch/qatomic_arch.h ../corelib/arch/qatomic_arm.h ../corelib/arch/qatomic_armv5.h ../corelib/arch/qatomic_armv6.h ../corelib/arch/qatomic_armv7.h ../corelib/arch/qatomic_avr32.h ../corelib/arch/qatomic_bfin.h ../corelib/arch/qatomic_bootstrap.h ../corelib/arch/qatomic_generic.h ../corelib/arch/qatomic_i386.h ../corelib/arch/qatomic_ia64.h ../corelib/arch/qatomic_integrity.h ../corelib/arch/qatomic_m68k.h ../corelib/arch/qatomic_macosx.h ../corelib/arch/qatomic_mips.h ../corelib/arch/qatomic_parisc.h ../corelib/arch/qatomic_powerpc.h ../corelib/arch/qatomic_s390.h ../corelib/arch/qatomic_sh.h ../corelib/arch/qatomic_sh4a.h ../corelib/arch/qatomic_sparc.h ../corelib/arch/qatomic_symbian.h ../corelib/arch/qatomic_vxworks.h ../corelib/arch/qatomic_windows.h ../corelib/arch/qatomic_windowsce.h ../corelib/arch/qatomic_x86_64.h ../corelib/tools/qalgorithms.h ../corelib/tools/qbitarray.h ../corelib/tools/qbytearray.h ../corelib/tools/qbytearraymatcher.h ../corelib/tools/qcache.h ../corelib/tools/qchar.h ../corelib/tools/qcontainerfwd.h ../corelib/tools/qcontiguouscache.h ../corelib/tools/qcryptographichash.h ../corelib/tools/qdatetime.h ../corelib/tools/qeasingcurve.h ../corelib/tools/qelapsedtimer.h ../corelib/tools/qhash.h ../corelib/tools/qiterator.h ../corelib/tools/qline.h ../corelib/tools/qlinkedlist.h ../corelib/tools/qlist.h ../corelib/tools/qlocale.h ../corelib/tools/qlocale_blackberry.h ../corelib/tools/qmap.h ../corelib/tools/qmargins.h ../corelib/tools/qpair.h ../corelib/tools/qpoint.h ../corelib/tools/qqueue.h ../corelib/tools/qrect.h ../corelib/tools/qregexp.h ../corelib/tools/qscopedpointer.h ../corelib/tools/qscopedvaluerollback.h ../corelib/tools/qset.h ../corelib/tools/qshareddata.h ../corelib/tools/qsharedpointer.h ../corelib/tools/qsharedpointer_impl.h ../corelib/tools/qsize.h ../corelib/tools/qstack.h ../corelib/tools/qstring.h ../corelib/tools/qstringbuilder.h ../corelib/tools/qstringlist.h ../corelib/tools/qstringmatcher.h ../corelib/tools/qtextboundaryfinder.h ../corelib/tools/qtimeline.h ../corelib/tools/qvarlengtharray.h ../corelib/tools/qvector.h ../corelib/plugin/qfactoryinterface.h ../corelib/plugin/qlibrary.h ../corelib/plugin/qplugin.h ../corelib/plugin/qpluginloader.h ../corelib/plugin/quuid.h ../corelib/xml/qxmlstream.h ../corelib/thread/qatomic.h ../corelib/thread/qbasicatomic.h ../corelib/thread/qmutex.h ../corelib/thread/qreadwritelock.h ../corelib/thread/qsemaphore.h ../corelib/thread/qthread.h ../corelib/thread/qthreadstorage.h ../corelib/thread/qwaitcondition.h ../corelib/statemachine/qabstractstate.h ../corelib/statemachine/qabstracttransition.h ../corelib/statemachine/qeventtransition.h ../corelib/statemachine/qfinalstate.h ../corelib/statemachine/qhistorystate.h ../corelib/statemachine/qsignaltransition.h ../corelib/statemachine/qstate.h ../corelib/statemachine/qstatemachine.h ../corelib/concurrent/qfuture.h ../corelib/concurrent/qfutureinterface.h ../corelib/concurrent/qfuturesynchronizer.h ../corelib/concurrent/qfuturewatcher.h ../corelib/concurrent/qrunnable.h ../corelib/concurrent/qtconcurrentcompilertest.h ../corelib/concurrent/qtconcurrentexception.h ../corelib/concurrent/qtconcurrentfilter.h ../corelib/concurrent/qtconcurrentfilterkernel.h ../corelib/concurrent/qtconcurrentfunctionwrappers.h ../corelib/concurrent/qtconcurrentiteratekernel.h ../corelib/concurrent/qtconcurrentmap.h ../corelib/concurrent/qtconcurrentmapkernel.h ../corelib/concurrent/qtconcurrentmedian.h ../corelib/concurrent/qtconcurrentreducekernel.h ../corelib/concurrent/qtconcurrentresultstore.h ../corelib/concurrent/qtconcurrentrun.h ../corelib/concurrent/qtconcurrentrunbase.h ../corelib/concurrent/qtconcurrentstoredfunctioncall.h ../corelib/concurrent/qtconcurrentthreadengine.h ../corelib/concurrent/qthreadpool.h ../corelib/io/qabstractfileengine.h ../corelib/io/qbuffer.h ../corelib/io/qdatastream.h ../corelib/io/qdebug.h ../corelib/io/qdir.h ../corelib/io/qdiriterator.h ../corelib/io/qfile.h ../corelib/io/qfileinfo.h ../corelib/io/qfilesystemwatcher.h ../corelib/io/qfsfileengine.h ../corelib/io/qiodevice.h ../corelib/io/qprocess.h ../corelib/io/qresource.h ../corelib/io/qsettings.h ../corelib/io/qtemporaryfile.h ../corelib/io/qtextstream.h ../corelib/io/qurl.h ../corelib/global/qconfig-dist.h ../corelib/global/qconfig-large.h ../corelib/global/qconfig-medium.h ../corelib/global/qconfig-minimal.h ../corelib/global/qconfig-nacl.h ../corelib/global/qconfig-small.h ../corelib/global/qendian.h ../corelib/global/qfeatures.h ../corelib/global/qglobal.h ../corelib/global/qlibraryinfo.h ../corelib/global/qnamespace.h ../corelib/global/qnumeric.h ../corelib/global/qt_windows.h ../corelib/global/qconfig.h ../corelib/codecs/qtextcodec.h ../corelib/codecs/qtextcodecplugin.h ../../include/QtCore/QtCore
+ SYNCQT.HEADER_CLASSES = ../../include/QtCore/QAbstractEventDispatcher ../../include/QtCore/QModelIndex ../../include/QtCore/QPersistentModelIndex ../../include/QtCore/QModelIndexList ../../include/QtCore/QAbstractItemModel ../../include/QtCore/QAbstractTableModel ../../include/QtCore/QAbstractListModel ../../include/QtCore/QBasicTimer ../../include/QtCore/QCoreApplication ../../include/QtCore/QtCleanUpFunction ../../include/QtCore/QEvent ../../include/QtCore/QTimerEvent ../../include/QtCore/QChildEvent ../../include/QtCore/QCustomEvent ../../include/QtCore/QDynamicPropertyChangeEvent ../../include/QtCore/QEventLoop ../../include/QtCore/QMetaMethod ../../include/QtCore/QMetaEnum ../../include/QtCore/QMetaProperty ../../include/QtCore/QMetaClassInfo ../../include/QtCore/QMetaType ../../include/QtCore/QMetaTypeId ../../include/QtCore/QMetaTypeId2 ../../include/QtCore/QMimeData ../../include/QtCore/QObjectList ../../include/QtCore/QObjectData ../../include/QtCore/QObject ../../include/QtCore/QObjectUserData ../../include/QtCore/QObjectCleanupHandler ../../include/QtCore/QGenericArgument ../../include/QtCore/QGenericReturnArgument ../../include/QtCore/QArgument ../../include/QtCore/QReturnArgument ../../include/QtCore/QMetaObject ../../include/QtCore/QMetaObjectAccessor ../../include/QtCore/QMetaObjectExtraData ../../include/QtCore/QPointer ../../include/QtCore/QSharedMemory ../../include/QtCore/QSignalMapper ../../include/QtCore/QSocketNotifier ../../include/QtCore/QSystemSemaphore ../../include/QtCore/QTimer ../../include/QtCore/QTranslator ../../include/QtCore/QVariant ../../include/QtCore/QVariantList ../../include/QtCore/QVariantMap ../../include/QtCore/QVariantHash ../../include/QtCore/QVariantComparisonHelper ../../include/QtCore/QAbstractAnimation ../../include/QtCore/QAnimationDriver ../../include/QtCore/QAnimationGroup ../../include/QtCore/QParallelAnimationGroup ../../include/QtCore/QPauseAnimation ../../include/QtCore/QPropertyAnimation ../../include/QtCore/QSequentialAnimationGroup ../../include/QtCore/QVariantAnimation ../../include/QtCore/QtAlgorithms ../../include/QtCore/QBitArray ../../include/QtCore/QBitRef ../../include/QtCore/QByteArray ../../include/QtCore/QByteRef ../../include/QtCore/QByteArrayMatcher ../../include/QtCore/QCache ../../include/QtCore/QLatin1Char ../../include/QtCore/QChar ../../include/QtCore/QtContainerFwd ../../include/QtCore/QContiguousCacheData ../../include/QtCore/QContiguousCacheTypedData ../../include/QtCore/QContiguousCache ../../include/QtCore/QCryptographicHash ../../include/QtCore/QDate ../../include/QtCore/QTime ../../include/QtCore/QDateTime ../../include/QtCore/QEasingCurve ../../include/QtCore/QElapsedTimer ../../include/QtCore/QHashData ../../include/QtCore/QHashDummyValue ../../include/QtCore/QHashDummyNode ../../include/QtCore/QHashNode ../../include/QtCore/QHash ../../include/QtCore/QMultiHash ../../include/QtCore/QHashIterator ../../include/QtCore/QMutableHashIterator ../../include/QtCore/QLine ../../include/QtCore/QLineF ../../include/QtCore/QLinkedListData ../../include/QtCore/QLinkedListNode ../../include/QtCore/QLinkedList ../../include/QtCore/QLinkedListIterator ../../include/QtCore/QMutableLinkedListIterator ../../include/QtCore/QListData ../../include/QtCore/QList ../../include/QtCore/QListIterator ../../include/QtCore/QMutableListIterator ../../include/QtCore/QSystemLocale ../../include/QtCore/QLocale ../../include/QtCore/QBBSystemLocaleData ../../include/QtCore/QMapData ../../include/QtCore/QMapNode ../../include/QtCore/QMapPayloadNode ../../include/QtCore/QMap ../../include/QtCore/QMultiMap ../../include/QtCore/QMapIterator ../../include/QtCore/QMutableMapIterator ../../include/QtCore/QMargins ../../include/QtCore/QPair ../../include/QtCore/QPoint ../../include/QtCore/QPointF ../../include/QtCore/QQueue ../../include/QtCore/QRect ../../include/QtCore/QRectF ../../include/QtCore/QRegExp ../../include/QtCore/QScopedPointerDeleter ../../include/QtCore/QScopedPointerArrayDeleter ../../include/QtCore/QScopedPointerPodDeleter ../../include/QtCore/QScopedPointer ../../include/QtCore/QScopedArrayPointer ../../include/QtCore/QScopedValueRollback ../../include/QtCore/QSet ../../include/QtCore/QSetIterator ../../include/QtCore/QMutableSetIterator ../../include/QtCore/QSharedData ../../include/QtCore/QSharedDataPointer ../../include/QtCore/QExplicitlySharedDataPointer ../../include/QtCore/QSharedPointer ../../include/QtCore/QWeakPointer ../../include/QtCore/QSize ../../include/QtCore/QSizeF ../../include/QtCore/QStack ../../include/QtCore/QStdWString ../../include/QtCore/QString ../../include/QtCore/QLatin1String ../../include/QtCore/QCharRef ../../include/QtCore/QConstString ../../include/QtCore/QStringRef ../../include/QtCore/QLatin1Literal ../../include/QtCore/QAbstractConcatenable ../../include/QtCore/QConcatenable ../../include/QtCore/QStringBuilder ../../include/QtCore/QStringListIterator ../../include/QtCore/QMutableStringListIterator ../../include/QtCore/QStringList ../../include/QtCore/QStringMatcher ../../include/QtCore/QTextBoundaryFinder ../../include/QtCore/QTimeLine ../../include/QtCore/QVarLengthArray ../../include/QtCore/QVectorData ../../include/QtCore/QVectorTypedData ../../include/QtCore/QVector ../../include/QtCore/QVectorIterator ../../include/QtCore/QMutableVectorIterator ../../include/QtCore/QFactoryInterface ../../include/QtCore/QLibrary ../../include/QtCore/QtPlugin ../../include/QtCore/QtPluginInstanceFunction ../../include/QtCore/QPluginLoader ../../include/QtCore/QUuid ../../include/QtCore/QXmlStreamStringRef ../../include/QtCore/QXmlStreamAttribute ../../include/QtCore/QXmlStreamAttributes ../../include/QtCore/QXmlStreamNamespaceDeclaration ../../include/QtCore/QXmlStreamNamespaceDeclarations ../../include/QtCore/QXmlStreamNotationDeclaration ../../include/QtCore/QXmlStreamNotationDeclarations ../../include/QtCore/QXmlStreamEntityDeclaration ../../include/QtCore/QXmlStreamEntityDeclarations ../../include/QtCore/QXmlStreamEntityResolver ../../include/QtCore/QXmlStreamReader ../../include/QtCore/QXmlStreamWriter ../../include/QtCore/QAtomicInt ../../include/QtCore/QAtomicPointer ../../include/QtCore/QBasicAtomicInt ../../include/QtCore/QBasicAtomicPointer ../../include/QtCore/QMutex ../../include/QtCore/QMutexLocker ../../include/QtCore/QMutexData ../../include/QtCore/QReadWriteLock ../../include/QtCore/QReadLocker ../../include/QtCore/QWriteLocker ../../include/QtCore/QSemaphore ../../include/QtCore/QThread ../../include/QtCore/QThreadStorageData ../../include/QtCore/QThreadStorage ../../include/QtCore/QWaitCondition ../../include/QtCore/QAbstractState ../../include/QtCore/QAbstractTransition ../../include/QtCore/QEventTransition ../../include/QtCore/QFinalState ../../include/QtCore/QHistoryState ../../include/QtCore/QSignalTransition ../../include/QtCore/QState ../../include/QtCore/QStateMachine ../../include/QtCore/QFuture ../../include/QtCore/QFutureIterator ../../include/QtCore/QMutableFutureIterator ../../include/QtCore/QFutureInterfaceBase ../../include/QtCore/QFutureInterface ../../include/QtCore/QFutureSynchronizer ../../include/QtCore/QFutureWatcherBase ../../include/QtCore/QFutureWatcher ../../include/QtCore/QRunnable ../../include/QtCore/QtConcurrentFilter ../../include/QtCore/QtConcurrentMap ../../include/QtCore/QtConcurrentRun ../../include/QtCore/QThreadPool ../../include/QtCore/QAbstractFileEngine ../../include/QtCore/QAbstractFileEngineHandler ../../include/QtCore/QAbstractFileEngineIterator ../../include/QtCore/QBuffer ../../include/QtCore/QDataStream ../../include/QtCore/QtDebug ../../include/QtCore/QDebug ../../include/QtCore/QNoDebug ../../include/QtCore/QDir ../../include/QtCore/QDirIterator ../../include/QtCore/QFile ../../include/QtCore/QFileInfo ../../include/QtCore/QFileInfoList ../../include/QtCore/QFileInfoListIterator ../../include/QtCore/QFileSystemWatcher ../../include/QtCore/QFSFileEngine ../../include/QtCore/QIODevice ../../include/QtCore/Q_PID ../../include/QtCore/QProcessEnvironment ../../include/QtCore/QProcess ../../include/QtCore/QResource ../../include/QtCore/QSettings ../../include/QtCore/QTemporaryFile ../../include/QtCore/QTextStream ../../include/QtCore/QTextStreamFunction ../../include/QtCore/QTextStreamManipulator ../../include/QtCore/QTS ../../include/QtCore/QTextIStream ../../include/QtCore/QTextOStream ../../include/QtCore/QUrl ../../include/QtCore/QtEndian ../../include/QtCore/QtGlobal ../../include/QtCore/QIntegerForSize ../../include/QtCore/QNoImplicitBoolCast ../../include/QtCore/Q_INT8 ../../include/QtCore/Q_UINT8 ../../include/QtCore/Q_INT16 ../../include/QtCore/Q_UINT16 ../../include/QtCore/Q_INT32 ../../include/QtCore/Q_UINT32 ../../include/QtCore/Q_INT64 ../../include/QtCore/Q_UINT64 ../../include/QtCore/Q_LLONG ../../include/QtCore/Q_ULLONG ../../include/QtCore/Q_LONG ../../include/QtCore/Q_ULONG ../../include/QtCore/QSysInfo ../../include/QtCore/QtMsgHandler ../../include/QtCore/QGlobalStatic ../../include/QtCore/QGlobalStaticDeleter ../../include/QtCore/QBool ../../include/QtCore/QTypeInfo ../../include/QtCore/QFlag ../../include/QtCore/QIncompatibleFlag ../../include/QtCore/QFlags ../../include/QtCore/QForeachContainer ../../include/QtCore/QForeachContainerBase ../../include/QtCore/QLibraryInfo ../../include/QtCore/Qt ../../include/QtCore/QInternal ../../include/QtCore/QCOORD ../../include/QtCore/QtConfig ../../include/QtCore/QTextCodec ../../include/QtCore/QTextEncoder ../../include/QtCore/QTextDecoder ../../include/QtCore/QTextCodecFactoryInterface ../../include/QtCore/QTextCodecPlugin
+ SYNCQT.PRIVATE_HEADER_FILES = ../corelib/kernel/qabstracteventdispatcher_p.h ../corelib/kernel/qabstractitemmodel_p.h ../corelib/kernel/qcore_mac_p.h ../corelib/kernel/qcore_symbian_p.h ../corelib/kernel/qcore_unix_p.h ../corelib/kernel/qcoreapplication_p.h ../corelib/kernel/qcorecmdlineargs_p.h ../corelib/kernel/qcoreglobaldata_p.h ../corelib/kernel/qcrashhandler_p.h ../corelib/kernel/qeventdispatcher_blackberry_p.h ../corelib/kernel/qeventdispatcher_glib_p.h ../corelib/kernel/qeventdispatcher_symbian_p.h ../corelib/kernel/qeventdispatcher_unix_p.h ../corelib/kernel/qeventdispatcher_win_p.h ../corelib/kernel/qfunctions_p.h ../corelib/kernel/qmetaobject_p.h ../corelib/kernel/qobject_p.h ../corelib/kernel/qsharedmemory_p.h ../corelib/kernel/qsystemerror_p.h ../corelib/kernel/qsystemsemaphore_p.h ../corelib/kernel/qtranslator_p.h ../corelib/kernel/qvariant_p.h ../corelib/kernel/qwineventnotifier_p.h ../corelib/animation/qabstractanimation_p.h ../corelib/animation/qanimationgroup_p.h ../corelib/animation/qparallelanimationgroup_p.h ../corelib/animation/qpropertyanimation_p.h ../corelib/animation/qsequentialanimationgroup_p.h ../corelib/animation/qvariantanimation_p.h ../corelib/tools/qbytedata_p.h ../corelib/tools/qdatetime_p.h ../corelib/tools/qharfbuzz_p.h ../corelib/tools/qlocale_data_p.h ../corelib/tools/qlocale_p.h ../corelib/tools/qlocale_tools_p.h ../corelib/tools/qpodlist_p.h ../corelib/tools/qringbuffer_p.h ../corelib/tools/qscopedpointer_p.h ../corelib/tools/qsimd_p.h ../corelib/tools/qtools_p.h ../corelib/tools/qunicodetables_p.h ../corelib/plugin/qelfparser_p.h ../corelib/plugin/qfactoryloader_p.h ../corelib/plugin/qlibrary_p.h ../corelib/plugin/qsystemlibrary_p.h ../corelib/xml/qxmlstream_p.h ../corelib/xml/qxmlutils_p.h ../corelib/thread/qmutex_p.h ../corelib/thread/qmutexpool_p.h ../corelib/thread/qorderedmutexlocker_p.h ../corelib/thread/qreadwritelock_p.h ../corelib/thread/qthread_p.h ../corelib/statemachine/qabstractstate_p.h ../corelib/statemachine/qabstracttransition_p.h ../corelib/statemachine/qeventtransition_p.h ../corelib/statemachine/qhistorystate_p.h ../corelib/statemachine/qsignaleventgenerator_p.h ../corelib/statemachine/qsignaltransition_p.h ../corelib/statemachine/qstate_p.h ../corelib/statemachine/qstatemachine_p.h ../corelib/concurrent/qfutureinterface_p.h ../corelib/concurrent/qfuturewatcher_p.h ../corelib/concurrent/qthreadpool_p.h ../corelib/io/qabstractfileengine_p.h ../corelib/io/qdatastream_p.h ../corelib/io/qdataurl_p.h ../corelib/io/qdir_p.h ../corelib/io/qfile_p.h ../corelib/io/qfileinfo_p.h ../corelib/io/qfilesystemengine_p.h ../corelib/io/qfilesystementry_p.h ../corelib/io/qfilesystemiterator_p.h ../corelib/io/qfilesystemmetadata_p.h ../corelib/io/qfilesystemwatcher_dnotify_p.h ../corelib/io/qfilesystemwatcher_fsevents_p.h ../corelib/io/qfilesystemwatcher_inotify_p.h ../corelib/io/qfilesystemwatcher_kqueue_p.h ../corelib/io/qfilesystemwatcher_p.h ../corelib/io/qfilesystemwatcher_symbian_p.h ../corelib/io/qfilesystemwatcher_win_p.h ../corelib/io/qfsfileengine_iterator_p.h ../corelib/io/qfsfileengine_p.h ../corelib/io/qiodevice_p.h ../corelib/io/qnoncontiguousbytedevice_p.h ../corelib/io/qprocess_p.h ../corelib/io/qresource_iterator_p.h ../corelib/io/qresource_p.h ../corelib/io/qsettings_p.h ../corelib/io/qtldurl_p.h ../corelib/io/qurltlds_p.h ../corelib/io/qwindowspipewriter_p.h ../corelib/global/qnumeric_p.h ../corelib/global/qt_pch.h ../corelib/codecs/qfontlaocodec_p.h ../corelib/codecs/qiconvcodec_p.h ../corelib/codecs/qisciicodec_p.h ../corelib/codecs/qlatincodec_p.h ../corelib/codecs/qsimplecodec_p.h ../corelib/codecs/qtextcodec_p.h ../corelib/codecs/qtsciicodec_p.h ../corelib/codecs/qutfcodec_p.h
+diff -up qt-everywhere-opensource-src-4.8.7/include/QtCore/qatomic_aarch64.h.aarch64 qt-everywhere-opensource-src-4.8.7/include/QtCore/qatomic_aarch64.h
+--- qt-everywhere-opensource-src-4.8.7/include/QtCore/qatomic_aarch64.h.aarch64 2015-05-08 21:48:32.715057744 -0500
++++ qt-everywhere-opensource-src-4.8.7/include/QtCore/qatomic_aarch64.h 2015-05-08 21:48:32.715057744 -0500
+@@ -0,0 +1 @@
++#include "../../src/corelib/arch/qatomic_aarch64.h"
+diff -up qt-everywhere-opensource-src-4.8.7/src/3rdparty/javascriptcore/JavaScriptCore/JavaScriptCore.pri.aarch64 qt-everywhere-opensource-src-4.8.7/src/3rdparty/javascriptcore/JavaScriptCore/JavaScriptCore.pri
+--- qt-everywhere-opensource-src-4.8.7/src/3rdparty/javascriptcore/JavaScriptCore/JavaScriptCore.pri.aarch64 2015-05-07 09:14:47.000000000 -0500
++++ qt-everywhere-opensource-src-4.8.7/src/3rdparty/javascriptcore/JavaScriptCore/JavaScriptCore.pri 2015-05-08 21:48:33.268061013 -0500
+@@ -66,6 +66,12 @@ contains(JAVASCRIPTCORE_JIT,no) {
+ }
+ }
+
++# Hack around AARCH64 fail wrt JSValue.h
++equals(QT_ARCH, aarch64) {
++ message("JavaScriptCore aarch64 hack: -fpermissive")
++ QMAKE_CXXFLAGS += -fpermissive
++}
++
+ wince* {
+ INCLUDEPATH += $$QT_SOURCE_TREE/src/3rdparty/ce-compat
+ SOURCES += $$QT_SOURCE_TREE/src/3rdparty/ce-compat/ce_time.c
+diff -up qt-everywhere-opensource-src-4.8.7/src/3rdparty/webkit/Source/JavaScriptCore/JavaScriptCore.pri.aarch64 qt-everywhere-opensource-src-4.8.7/src/3rdparty/webkit/Source/JavaScriptCore/JavaScriptCore.pri
+--- qt-everywhere-opensource-src-4.8.7/src/3rdparty/webkit/Source/JavaScriptCore/JavaScriptCore.pri.aarch64 2015-05-07 09:14:45.000000000 -0500
++++ qt-everywhere-opensource-src-4.8.7/src/3rdparty/webkit/Source/JavaScriptCore/JavaScriptCore.pri 2015-05-08 21:48:33.268061013 -0500
+@@ -63,6 +63,12 @@ contains (CONFIG, text_breaking_with_icu
+ DEFINES += WTF_USE_QT_ICU_TEXT_BREAKING=1
+ }
+
++# Hack around AARCH64 fail wrt JSValue.h
++equals(QT_ARCH, aarch64) {
++ message("JavaScriptCore aarch64 hack: -fpermissive")
++ QMAKE_CXXFLAGS += -fpermissive
++}
++
+ wince* {
+ INCLUDEPATH += $$QT_SOURCE_TREE/src/3rdparty/ce-compat
+ INCLUDEPATH += $$PWD/../JavaScriptCore/os-win32
+diff -up qt-everywhere-opensource-src-4.8.7/src/3rdparty/webkit/Source/JavaScriptCore/wtf/Platform.h.aarch64 qt-everywhere-opensource-src-4.8.7/src/3rdparty/webkit/Source/JavaScriptCore/wtf/Platform.h
+--- qt-everywhere-opensource-src-4.8.7/src/3rdparty/webkit/Source/JavaScriptCore/wtf/Platform.h.aarch64 2015-05-07 09:14:45.000000000 -0500
++++ qt-everywhere-opensource-src-4.8.7/src/3rdparty/webkit/Source/JavaScriptCore/wtf/Platform.h 2015-05-08 21:48:33.269061019 -0500
+@@ -369,7 +369,16 @@
+
+ #endif /* ARM */
+
+-#if CPU(ARM) || CPU(MIPS) || CPU(SH4)
++/* CPU(AARCH64) - AArch64 */
++#if defined(__aarch64__)
++#define WTF_CPU_AARCH64 1
++#if defined(__AARCH64EB__)
++#define WTF_CPU_BIG_ENDIAN 1
++#endif
++#endif
++
++/* Not sure about this one, qt5-qtwebkit doesn't include it -- rex */
++#if CPU(ARM) || CPU(MIPS) || CPU(SH4) || CPU(AARCH64)
+ #define WTF_CPU_NEEDS_ALIGNED_ACCESS 1
+ #endif
+
+@@ -1003,7 +1012,7 @@
+ || CPU(SPARC64) \
+ || CPU(S390X) \
+ || CPU(PPC64) \
+- || CPU(MIPS64)
++ || CPU(MIPS64) || CPU(AARCH64)
+ #define WTF_USE_JSVALUE64 1
+ #else
+ #define WTF_USE_JSVALUE32_64 1
+diff -up qt-everywhere-opensource-src-4.8.7/src/corelib/arch/aarch64/qatomic_aarch64.cpp.aarch64 qt-everywhere-opensource-src-4.8.7/src/corelib/arch/aarch64/qatomic_aarch64.cpp
+--- qt-everywhere-opensource-src-4.8.7/src/corelib/arch/aarch64/qatomic_aarch64.cpp.aarch64 2015-05-08 21:48:33.269061019 -0500
++++ qt-everywhere-opensource-src-4.8.7/src/corelib/arch/aarch64/qatomic_aarch64.cpp 2015-05-08 21:48:33.269061019 -0500
+@@ -0,0 +1,70 @@
++/****************************************************************************
++**
++** Copyright (C) 2012, 2013 Digia Plc and/or its subsidiary(-ies).
++** Contact: http://www.qt-project.org/legal
++**
++** This file is part of the QtCore module of the Qt Toolkit.
++**
++** $QT_BEGIN_LICENSE:LGPL$
++** Commercial License Usage
++** Licensees holding valid commercial Qt licenses may use this file in
++** accordance with the commercial license agreement provided with the
++** Software or, alternatively, in accordance with the terms contained in
++** a written agreement between you and Digia. For licensing terms and
++** conditions see http://qt.digia.com/licensing. For further information
++** use the contact form at http://qt.digia.com/contact-us.
++**
++** GNU Lesser General Public License Usage
++** Alternatively, this file may be used under the terms of the GNU Lesser
++** General Public License version 2.1 as published by the Free Software
++** Foundation and appearing in the file LICENSE.LGPL included in the
++** packaging of this file. Please review the following information to
++** ensure the GNU Lesser General Public License version 2.1 requirements
++** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
++**
++** In addition, as a special exception, Digia gives you certain additional
++** rights. These rights are described in the Digia Qt LGPL Exception
++** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
++**
++** GNU General Public License Usage
++** Alternatively, this file may be used under the terms of the GNU
++** General Public License version 3.0 as published by the Free Software
++** Foundation and appearing in the file LICENSE.GPL included in the
++** packaging of this file. Please review the following information to
++** ensure the GNU General Public License version 3.0 requirements will be
++** met: http://www.gnu.org/copyleft/gpl.html.
++**
++**
++** $QT_END_LICENSE$
++**
++****************************************************************************/
++
++#include <QtCore/qglobal.h>
++
++#include <unistd.h>
++#ifdef _POSIX_PRIORITY_SCHEDULING
++# include <sched.h>
++#endif
++#include <time.h>
++
++QT_BEGIN_NAMESPACE
++
++QT_USE_NAMESPACE
++
++Q_CORE_EXPORT void qt_atomic_yield(int *count)
++{
++#ifdef _POSIX_PRIORITY_SCHEDULING
++ if((*count)++ < 50) {
++ sched_yield();
++ } else
++#endif
++ {
++ struct timespec tm;
++ tm.tv_sec = 0;
++ tm.tv_nsec = 2000001;
++ nanosleep(&tm, NULL);
++ *count = 0;
++ }
++}
++
++QT_END_NAMESPACE
+diff -up qt-everywhere-opensource-src-4.8.7/src/corelib/arch/arch.pri.aarch64 qt-everywhere-opensource-src-4.8.7/src/corelib/arch/arch.pri
+--- qt-everywhere-opensource-src-4.8.7/src/corelib/arch/arch.pri.aarch64 2015-05-07 09:14:48.000000000 -0500
++++ qt-everywhere-opensource-src-4.8.7/src/corelib/arch/arch.pri 2015-05-08 21:48:33.270061025 -0500
+@@ -31,7 +31,9 @@ integrity:HEADERS += arch/qatomic_integr
+ arch/qatomic_s390.h \
+ arch/qatomic_x86_64.h \
+ arch/qatomic_sh.h \
+- arch/qatomic_sh4a.h
++ arch/qatomic_sh4a.h \
++ arch/qatomic_aarch64.h \
++
+
+ QT_ARCH_CPP = $$QT_SOURCE_TREE/src/corelib/arch/$$QT_ARCH
+ DEPENDPATH += $$QT_ARCH_CPP
+diff -up qt-everywhere-opensource-src-4.8.7/src/corelib/arch/qatomic_aarch64.h.aarch64 qt-everywhere-opensource-src-4.8.7/src/corelib/arch/qatomic_aarch64.h
+--- qt-everywhere-opensource-src-4.8.7/src/corelib/arch/qatomic_aarch64.h.aarch64 2015-05-08 21:48:33.270061025 -0500
++++ qt-everywhere-opensource-src-4.8.7/src/corelib/arch/qatomic_aarch64.h 2015-05-08 21:48:33.270061025 -0500
+@@ -0,0 +1,335 @@
++/****************************************************************************
++**
++** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
++** Contact: http://www.qt-project.org/legal
++**
++** This file is part of the QtCore module of the Qt Toolkit.
++**
++** $QT_BEGIN_LICENSE:LGPL$
++** Commercial License Usage
++** Licensees holding valid commercial Qt licenses may use this file in
++** accordance with the commercial license agreement provided with the
++** Software or, alternatively, in accordance with the terms contained in
++** a written agreement between you and Digia. For licensing terms and
++** conditions see http://qt.digia.com/licensing. For further information
++** use the contact form at http://qt.digia.com/contact-us.
++**
++** GNU Lesser General Public License Usage
++** Alternatively, this file may be used under the terms of the GNU Lesser
++** General Public License version 2.1 as published by the Free Software
++** Foundation and appearing in the file LICENSE.LGPL included in the
++** packaging of this file. Please review the following information to
++** ensure the GNU Lesser General Public License version 2.1 requirements
++** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
++**
++** In addition, as a special exception, Digia gives you certain additional
++** rights. These rights are described in the Digia Qt LGPL Exception
++** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
++**
++** GNU General Public License Usage
++** Alternatively, this file may be used under the terms of the GNU
++** General Public License version 3.0 as published by the Free Software
++** Foundation and appearing in the file LICENSE.GPL included in the
++** packaging of this file. Please review the following information to
++** ensure the GNU General Public License version 3.0 requirements will be
++** met: http://www.gnu.org/copyleft/gpl.html.
++**
++**
++** $QT_END_LICENSE$
++**
++****************************************************************************/
++
++#ifndef QATOMIC_AARCH64_H
++#define QATOMIC_AARCH64_H
++
++QT_BEGIN_HEADER
++
++QT_BEGIN_NAMESPACE
++
++#define Q_ATOMIC_INT_REFERENCE_COUNTING_IS_ALWAYS_NATIVE
++
++inline bool QBasicAtomicInt::isReferenceCountingNative()
++{ return true; }
++inline bool QBasicAtomicInt::isReferenceCountingWaitFree()
++{ return false; }
++
++#define Q_ATOMIC_INT_TEST_AND_SET_IS_ALWAYS_NATIVE
++
++inline bool QBasicAtomicInt::isTestAndSetNative()
++{ return true; }
++inline bool QBasicAtomicInt::isTestAndSetWaitFree()
++{ return false; }
++
++#define Q_ATOMIC_INT_FETCH_AND_STORE_IS_ALWAYS_NATIVE
++
++inline bool QBasicAtomicInt::isFetchAndStoreNative()
++{ return true; }
++inline bool QBasicAtomicInt::isFetchAndStoreWaitFree()
++{ return false; }
++
++#define Q_ATOMIC_INT_FETCH_AND_ADD_IS_ALWAYS_NATIVE
++
++inline bool QBasicAtomicInt::isFetchAndAddNative()
++{ return true; }
++inline bool QBasicAtomicInt::isFetchAndAddWaitFree()
++{ return false; }
++
++#define Q_ATOMIC_POINTER_TEST_AND_SET_IS_ALWAYS_NATIVE
++
++template <typename T>
++Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::isTestAndSetNative()
++{ return true; }
++template <typename T>
++Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::isTestAndSetWaitFree()
++{ return false; }
++
++#define Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_ALWAYS_NATIVE
++
++template <typename T>
++Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::isFetchAndStoreNative()
++{ return true; }
++template <typename T>
++Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::isFetchAndStoreWaitFree()
++{ return false; }
++
++#define Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_ALWAYS_NATIVE
++
++template <typename T>
++Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::isFetchAndAddNative()
++{ return true; }
++template <typename T>
++Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::isFetchAndAddWaitFree()
++{ return false; }
++
++#ifndef Q_DATA_MEMORY_BARRIER
++# define Q_DATA_MEMORY_BARRIER asm volatile("dmb sy\n":::"memory")
++#endif
++#ifndef Q_COMPILER_MEMORY_BARRIER
++# define Q_COMPILER_MEMORY_BARRIER asm volatile("":::"memory")
++#endif
++
++inline bool QBasicAtomicInt::ref()
++{
++ int newValue;
++
++ Q_COMPILER_MEMORY_BARRIER;
++ newValue = __atomic_add_fetch(&_q_value, 1, __ATOMIC_ACQ_REL);
++ Q_COMPILER_MEMORY_BARRIER;
++
++ return newValue != 0;
++}
++
++inline bool QBasicAtomicInt::deref()
++{
++ int newValue;
++
++ Q_COMPILER_MEMORY_BARRIER;
++ newValue = __atomic_sub_fetch(&_q_value, 1, __ATOMIC_ACQ_REL);
++ Q_COMPILER_MEMORY_BARRIER;
++
++ return newValue != 0;
++}
++
++inline bool QBasicAtomicInt::testAndSetRelaxed(int expectedValue, int newValue)
++{
++ bool val;
++
++ Q_COMPILER_MEMORY_BARRIER;
++ val = __atomic_compare_exchange_n (&_q_value, &expectedValue, newValue,
++ false, __ATOMIC_RELAXED, __ATOMIC_RELAXED);
++ Q_COMPILER_MEMORY_BARRIER;
++ return val;
++}
++
++inline int QBasicAtomicInt::fetchAndStoreRelaxed(int newValue)
++{
++ int val;
++ Q_COMPILER_MEMORY_BARRIER;
++ val = __atomic_exchange_n(&_q_value, newValue, __ATOMIC_RELAXED);
++ Q_COMPILER_MEMORY_BARRIER;
++ return val;
++}
++
++inline int QBasicAtomicInt::fetchAndAddRelaxed(int valueToAdd)
++{
++ int val;
++ Q_COMPILER_MEMORY_BARRIER;
++ val = __atomic_fetch_add(&_q_value, valueToAdd, __ATOMIC_RELAXED);
++ Q_COMPILER_MEMORY_BARRIER;
++ return val;
++}
++
++template <typename T>
++Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::testAndSetRelaxed(T *expectedValue, T *newValue)
++{
++ bool val;
++ Q_COMPILER_MEMORY_BARRIER;
++ val = __atomic_compare_exchange_n (&_q_value, &expectedValue, newValue,
++ false, __ATOMIC_RELAXED, __ATOMIC_RELAXED);
++ Q_COMPILER_MEMORY_BARRIER;
++ return val;
++}
++
++template <typename T>
++Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndStoreRelaxed(T *newValue)
++{
++ T *val;
++ Q_COMPILER_MEMORY_BARRIER;
++ val = __atomic_exchange_n(&_q_value, newValue, __ATOMIC_RELAXED);
++ Q_COMPILER_MEMORY_BARRIER;
++ return val;
++}
++
++template <typename T>
++Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndAddRelaxed(qptrdiff valueToAdd)
++{
++ T *val;
++ Q_COMPILER_MEMORY_BARRIER;
++ val = __atomic_fetch_add(&_q_value, valueToAdd, __ATOMIC_RELAXED);
++ Q_COMPILER_MEMORY_BARRIER;
++ return val;
++}
++
++inline bool QBasicAtomicInt::testAndSetAcquire(int expectedValue, int newValue)
++{
++ bool returnValue = testAndSetRelaxed(expectedValue, newValue);
++ Q_DATA_MEMORY_BARRIER;
++ return returnValue;
++}
++
++inline bool QBasicAtomicInt::testAndSetRelease(int expectedValue, int newValue)
++{
++ Q_DATA_MEMORY_BARRIER;
++ return testAndSetRelaxed(expectedValue, newValue);
++}
++
++inline bool QBasicAtomicInt::testAndSetOrdered(int expectedValue, int newValue)
++{
++ Q_DATA_MEMORY_BARRIER;
++ bool returnValue = testAndSetRelaxed(expectedValue, newValue);
++ Q_COMPILER_MEMORY_BARRIER;
++ return returnValue;
++}
++
++inline int QBasicAtomicInt::fetchAndStoreAcquire(int newValue)
++{
++ int returnValue = fetchAndStoreRelaxed(newValue);
++ Q_DATA_MEMORY_BARRIER;
++ return returnValue;
++}
++
++inline int QBasicAtomicInt::fetchAndStoreRelease(int newValue)
++{
++ Q_DATA_MEMORY_BARRIER;
++ return fetchAndStoreRelaxed(newValue);
++}
++
++inline int QBasicAtomicInt::fetchAndStoreOrdered(int newValue)
++{
++ Q_DATA_MEMORY_BARRIER;
++ int returnValue = fetchAndStoreRelaxed(newValue);
++ Q_COMPILER_MEMORY_BARRIER;
++ return returnValue;
++}
++
++inline int QBasicAtomicInt::fetchAndAddAcquire(int valueToAdd)
++{
++ int returnValue = fetchAndAddRelaxed(valueToAdd);
++ Q_DATA_MEMORY_BARRIER;
++ return returnValue;
++}
++
++inline int QBasicAtomicInt::fetchAndAddRelease(int valueToAdd)
++{
++ Q_DATA_MEMORY_BARRIER;
++ return fetchAndAddRelaxed(valueToAdd);
++}
++
++inline int QBasicAtomicInt::fetchAndAddOrdered(int valueToAdd)
++{
++ Q_DATA_MEMORY_BARRIER;
++ int returnValue = fetchAndAddRelaxed(valueToAdd);
++ Q_COMPILER_MEMORY_BARRIER;
++ return returnValue;
++}
++
++template <typename T>
++Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::testAndSetAcquire(T *expectedValue, T *newValue)
++{
++ bool returnValue = testAndSetRelaxed(expectedValue, newValue);
++ Q_DATA_MEMORY_BARRIER;
++ return returnValue;
++}
++
++template <typename T>
++Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::testAndSetRelease(T *expectedValue, T *newValue)
++{
++ Q_DATA_MEMORY_BARRIER;
++ return testAndSetRelaxed(expectedValue, newValue);
++}
++
++template <typename T>
++Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::testAndSetOrdered(T *expectedValue, T *newValue)
++{
++ Q_DATA_MEMORY_BARRIER;
++ bool returnValue = testAndSetAcquire(expectedValue, newValue);
++ Q_COMPILER_MEMORY_BARRIER;
++ return returnValue;
++}
++
++template <typename T>
++Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndStoreAcquire(T *newValue)
++{
++ T *returnValue = fetchAndStoreRelaxed(newValue);
++ Q_DATA_MEMORY_BARRIER;
++ return returnValue;
++}
++
++template <typename T>
++Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndStoreRelease(T *newValue)
++{
++ Q_DATA_MEMORY_BARRIER;
++ return fetchAndStoreRelaxed(newValue);
++}
++
++template <typename T>
++Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndStoreOrdered(T *newValue)
++{
++ Q_DATA_MEMORY_BARRIER;
++ T *returnValue = fetchAndStoreRelaxed(newValue);
++ Q_COMPILER_MEMORY_BARRIER;
++ return returnValue;
++}
++
++template <typename T>
++Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndAddAcquire(qptrdiff valueToAdd)
++{
++ T *returnValue = fetchAndAddRelaxed(valueToAdd);
++ Q_DATA_MEMORY_BARRIER;
++ return returnValue;
++}
++
++template <typename T>
++Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndAddRelease(qptrdiff valueToAdd)
++{
++ Q_DATA_MEMORY_BARRIER;
++ return fetchAndAddRelaxed(valueToAdd);
++}
++
++template <typename T>
++Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndAddOrdered(qptrdiff valueToAdd)
++{
++ Q_DATA_MEMORY_BARRIER;
++ T *returnValue = fetchAndAddRelaxed(valueToAdd);
++ Q_COMPILER_MEMORY_BARRIER;
++ return returnValue;
++}
++
++#undef Q_DATA_MEMORY_BARRIER
++#undef Q_COMPILER_MEMORY_BARRIER
++
++QT_END_NAMESPACE
++
++QT_END_HEADER
++
++#endif // QATOMIC_AARCH64_H
+diff -up qt-everywhere-opensource-src-4.8.7/src/corelib/arch/qatomic_arch.h.aarch64 qt-everywhere-opensource-src-4.8.7/src/corelib/arch/qatomic_arch.h
+--- qt-everywhere-opensource-src-4.8.7/src/corelib/arch/qatomic_arch.h.aarch64 2015-05-07 09:14:48.000000000 -0500
++++ qt-everywhere-opensource-src-4.8.7/src/corelib/arch/qatomic_arch.h 2015-05-08 21:48:33.271061031 -0500
+@@ -94,6 +94,8 @@ QT_BEGIN_HEADER
+ # include "QtCore/qatomic_sh4a.h"
+ #elif defined(QT_ARCH_NACL)
+ # include "QtCore/qatomic_generic.h"
++#elif defined(QT_ARCH_AARCH64)
++# include "QtCore/qatomic_aarch64.h"
+ #else
+ # error "Qt has not been ported to this architecture"
+ #endif
diff --git a/subprojects/shared-modules/qt4/qt4-openssl-1.1.patch b/subprojects/shared-modules/qt4/qt4-openssl-1.1.patch
new file mode 100644
index 00000000..87baef87
--- /dev/null
+++ b/subprojects/shared-modules/qt4/qt4-openssl-1.1.patch
@@ -0,0 +1,409 @@
+Description: Compile with openssl-1.1.0
+ * Most changes are related to openssl structures are now opaque.
+ * The network/ssl threading setup has been disabled because the
+ old openssl threading model has been removed and is apparently
+ no longer needed.
+ * A number of new functions had to be imported (see changes to
+ src/network/ssl/qsslsocket_openssl_symbols.cpp)
+Author: Gert Wollny <gw.fossdev@gmail.com>
+Last-Update: 2016-06-28
+Bug-Debian: http://bugs.debian.org/828522
+
+--- a/src/network/ssl/qsslcertificate.cpp
++++ b/src/network/ssl/qsslcertificate.cpp
+@@ -259,10 +259,10 @@
+ QByteArray QSslCertificate::version() const
+ {
+ QMutexLocker lock(QMutexPool::globalInstanceGet(d.data()));
+- if (d->versionString.isEmpty() && d->x509)
++ if (d->versionString.isEmpty() && d->x509) {
+ d->versionString =
+- QByteArray::number(qlonglong(q_ASN1_INTEGER_get(d->x509->cert_info->version)) + 1);
+-
++ QByteArray::number(qlonglong(q_X509_get_version(d->x509)) + 1);
++ }
+ return d->versionString;
+ }
+
+@@ -276,7 +276,7 @@
+ {
+ QMutexLocker lock(QMutexPool::globalInstanceGet(d.data()));
+ if (d->serialNumberString.isEmpty() && d->x509) {
+- ASN1_INTEGER *serialNumber = d->x509->cert_info->serialNumber;
++ ASN1_INTEGER *serialNumber = q_X509_get_serialNumber(d->x509);
+ // if we cannot convert to a long, just output the hexadecimal number
+ if (serialNumber->length > 4) {
+ QByteArray hexString;
+@@ -489,24 +489,33 @@
+ QSslKey key;
+
+ key.d->type = QSsl::PublicKey;
++#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ X509_PUBKEY *xkey = d->x509->cert_info->key;
++#else
++ X509_PUBKEY *xkey = q_X509_get_X509_PUBKEY(d->x509);
++#endif
+ EVP_PKEY *pkey = q_X509_PUBKEY_get(xkey);
+ Q_ASSERT(pkey);
+
+- if (q_EVP_PKEY_type(pkey->type) == EVP_PKEY_RSA) {
++ int key_id;
++#if OPENSSL_VERSION_NUMBER < 0x10100000L
++ key_id = q_EVP_PKEY_type(pkey->type);
++#else
++ key_id = q_EVP_PKEY_base_id(pkey);
++#endif
++ if (key_id == EVP_PKEY_RSA) {
+ key.d->rsa = q_EVP_PKEY_get1_RSA(pkey);
+ key.d->algorithm = QSsl::Rsa;
+ key.d->isNull = false;
+- } else if (q_EVP_PKEY_type(pkey->type) == EVP_PKEY_DSA) {
++ } else if (key_id == EVP_PKEY_DSA) {
+ key.d->dsa = q_EVP_PKEY_get1_DSA(pkey);
+ key.d->algorithm = QSsl::Dsa;
+ key.d->isNull = false;
+- } else if (q_EVP_PKEY_type(pkey->type) == EVP_PKEY_DH) {
++ } else if (key_id == EVP_PKEY_DH) {
+ // DH unsupported
+ } else {
+ // error?
+ }
+-
+ q_EVP_PKEY_free(pkey);
+ return key;
+ }
+--- a/src/network/ssl/qsslkey.cpp
++++ b/src/network/ssl/qsslkey.cpp
+@@ -321,8 +321,19 @@
+ {
+ if (d->isNull)
+ return -1;
++#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ return (d->algorithm == QSsl::Rsa)
+ ? q_BN_num_bits(d->rsa->n) : q_BN_num_bits(d->dsa->p);
++#else
++ if (d->algorithm == QSsl::Rsa) {
++ return q_RSA_bits(d->rsa);
++ }else{
++ BIGNUM *p = NULL;
++ q_DSA_get0_pqg(d->dsa, &p, NULL, NULL);
++ return q_BN_num_bits(p);
++ }
++#endif
++
+ }
+
+ /*!
+--- a/src/network/ssl/qsslsocket_openssl.cpp
++++ b/src/network/ssl/qsslsocket_openssl.cpp
+@@ -93,6 +93,7 @@
+ bool QSslSocketPrivate::s_loadedCiphersAndCerts = false;
+ bool QSslSocketPrivate::s_loadRootCertsOnDemand = false;
+
++#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ /* \internal
+
+ From OpenSSL's thread(3) manual page:
+@@ -174,6 +175,8 @@
+ }
+ } // extern "C"
+
++#endif //OPENSSL_VERSION_NUMBER >= 0x10100000L
++
+ QSslSocketBackendPrivate::QSslSocketBackendPrivate()
+ : ssl(0),
+ ctx(0),
+@@ -222,9 +225,12 @@
+ ciph.d->encryptionMethod = descriptionList.at(4).mid(4);
+ ciph.d->exportable = (descriptionList.size() > 6 && descriptionList.at(6) == QLatin1String("export"));
+
++#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ ciph.d->bits = cipher->strength_bits;
+ ciph.d->supportedBits = cipher->alg_bits;
+-
++#else
++ ciph.d->bits = q_SSL_CIPHER_get_bits(cipher, &ciph.d->supportedBits);
++#endif
+ }
+ return ciph;
+ }
+@@ -367,7 +373,7 @@
+ //
+ // See also: QSslContext::fromConfiguration()
+ if (caCertificate.expiryDate() >= QDateTime::currentDateTime()) {
+- q_X509_STORE_add_cert(ctx->cert_store, (X509 *)caCertificate.handle());
++ q_X509_STORE_add_cert(q_SSL_CTX_get_cert_store(ctx), (X509 *)caCertificate.handle());
+ }
+ }
+
+@@ -504,8 +510,10 @@
+ */
+ void QSslSocketPrivate::deinitialize()
+ {
++#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ q_CRYPTO_set_id_callback(0);
+ q_CRYPTO_set_locking_callback(0);
++#endif
+ }
+
+ /*!
+@@ -526,13 +534,17 @@
+ return false;
+
+ // Check if the library itself needs to be initialized.
++#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ QMutexLocker locker(openssl_locks()->initLock());
++#endif
+ if (!s_libraryLoaded) {
+ s_libraryLoaded = true;
+
+ // Initialize OpenSSL.
++#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ q_CRYPTO_set_id_callback(id_function);
+ q_CRYPTO_set_locking_callback(locking_function);
++#endif
+ if (q_SSL_library_init() != 1)
+ return false;
+ q_SSL_load_error_strings();
+@@ -571,7 +583,9 @@
+
+ void QSslSocketPrivate::ensureCiphersAndCertsLoaded()
+ {
+- QMutexLocker locker(openssl_locks()->initLock());
++#if OPENSSL_VERSION_NUMBER < 0x10100000L
++ QMutexLocker locker(openssl_locks()->initLock());
++#endif
+ if (s_loadedCiphersAndCerts)
+ return;
+ s_loadedCiphersAndCerts = true;
+@@ -663,13 +677,18 @@
+ STACK_OF(SSL_CIPHER) *supportedCiphers = q_SSL_get_ciphers(mySsl);
+ for (int i = 0; i < q_sk_SSL_CIPHER_num(supportedCiphers); ++i) {
+ if (SSL_CIPHER *cipher = q_sk_SSL_CIPHER_value(supportedCiphers, i)) {
+- if (cipher->valid) {
++
++#if OPENSSL_VERSION_NUMBER < 0x10100000L
++ if (cipher->valid) {
++#endif
+ QSslCipher ciph = QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(cipher);
+ if (!ciph.isNull()) {
+ if (!ciph.name().toLower().startsWith(QLatin1String("adh")))
+ ciphers << ciph;
+ }
++#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ }
++#endif
+ }
+ }
+
+--- a/src/network/ssl/qsslsocket_openssl_symbols_p.h
++++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h
+@@ -399,7 +399,25 @@
+ PEM_ASN1_write_bio((int (*)(void*, unsigned char**))q_i2d_DSAPrivateKey,PEM_STRING_DSA,\
+ bp,(char *)x,enc,kstr,klen,cb,u)
+ #endif
++
++X509_STORE * q_SSL_CTX_get_cert_store(const SSL_CTX *ctx);
++ASN1_INTEGER * q_X509_get_serialNumber(X509 *x);
++
++#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ #define q_SSL_CTX_set_options(ctx,op) q_SSL_CTX_ctrl((ctx),SSL_CTRL_OPTIONS,(op),NULL)
++#define q_X509_get_version(x) X509_get_version(x)
++#else
++int q_EVP_PKEY_id(const EVP_PKEY *pkey);
++int q_EVP_PKEY_base_id(const EVP_PKEY *pkey);
++int q_SSL_CIPHER_get_bits(const SSL_CIPHER *cipher, int *alg_bits);
++long q_SSL_CTX_set_options(SSL_CTX *ctx, long options);
++long q_X509_get_version(X509 *x);
++X509_PUBKEY * q_X509_get_X509_PUBKEY(X509 *x);
++int q_RSA_bits(const RSA *rsa);
++int q_DSA_security_bits(const DSA *dsa);
++void q_DSA_get0_pqg(const DSA *d, BIGNUM **p, BIGNUM **q, BIGNUM **g);
++#endif
++
+ #define q_SKM_sk_num(type, st) ((int (*)(const STACK_OF(type) *))q_sk_num)(st)
+ #define q_SKM_sk_value(type, st,i) ((type * (*)(const STACK_OF(type) *, int))q_sk_value)(st, i)
+ #define q_sk_GENERAL_NAME_num(st) q_SKM_sk_num(GENERAL_NAME, (st))
+@@ -410,8 +428,15 @@
+ #define q_sk_SSL_CIPHER_value(st, i) q_SKM_sk_value(SSL_CIPHER, (st), (i))
+ #define q_SSL_CTX_add_extra_chain_cert(ctx,x509) \
+ q_SSL_CTX_ctrl(ctx,SSL_CTRL_EXTRA_CHAIN_CERT,0,(char *)x509)
++
++#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ #define q_X509_get_notAfter(x) X509_get_notAfter(x)
+ #define q_X509_get_notBefore(x) X509_get_notBefore(x)
++#else
++ASN1_TIME *q_X509_get_notAfter(X509 *x);
++ASN1_TIME *q_X509_get_notBefore(X509 *x);
++#endif
++
+ #define q_EVP_PKEY_assign_RSA(pkey,rsa) q_EVP_PKEY_assign((pkey),EVP_PKEY_RSA,\
+ (char *)(rsa))
+ #define q_EVP_PKEY_assign_DSA(pkey,dsa) q_EVP_PKEY_assign((pkey),EVP_PKEY_DSA,\
+--- a/src/network/ssl/qsslsocket_openssl_symbols.cpp
++++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp
+@@ -290,6 +290,22 @@
+ DEFINEFUNC(void, OPENSSL_add_all_algorithms_conf, void, DUMMYARG, return, DUMMYARG)
+ DEFINEFUNC3(int, SSL_CTX_load_verify_locations, SSL_CTX *ctx, ctx, const char *CAfile, CAfile, const char *CApath, CApath, return 0, return)
+ DEFINEFUNC(long, SSLeay, void, DUMMYARG, return 0, return)
++DEFINEFUNC(X509_STORE *, SSL_CTX_get_cert_store, const SSL_CTX *ctx, ctx, return 0, return)
++
++DEFINEFUNC(ASN1_INTEGER *, X509_get_serialNumber, X509 *x, x, return 0, return)
++#if OPENSSL_VERSION_NUMBER >= 0x10100000L
++DEFINEFUNC(int, EVP_PKEY_id, const EVP_PKEY *pkey, pkey, return 0, return)
++DEFINEFUNC(int, EVP_PKEY_base_id, const EVP_PKEY *pkey, pkey, return 0, return)
++DEFINEFUNC2(int, SSL_CIPHER_get_bits, const SSL_CIPHER *cipher, cipher, int *alg_bits, alg_bits, return 0, return)
++DEFINEFUNC2(long, SSL_CTX_set_options, SSL_CTX *ctx, ctx, long options, options, return 0, return)
++DEFINEFUNC(long, X509_get_version, X509 *x, x, return 0, return)
++DEFINEFUNC(X509_PUBKEY *, X509_get_X509_PUBKEY, X509 *x, x, return 0, return)
++DEFINEFUNC(int, RSA_bits, const RSA *rsa, rsa, return 0, return)
++DEFINEFUNC(int, DSA_security_bits, const DSA *dsa, dsa, return 0, return)
++DEFINEFUNC(ASN1_TIME *, X509_get_notAfter, X509 *x, x, return 0, return)
++DEFINEFUNC(ASN1_TIME *, X509_get_notBefore, X509 *x, x, return 0, return)
++DEFINEFUNC4(void, DSA_get0_pqg, const DSA *d, d, BIGNUM **p, p, BIGNUM **q, q, BIGNUM **g, g, return, return)
++#endif
+
+ #ifdef Q_OS_SYMBIAN
+ #define RESOLVEFUNC(func, ordinal, lib) \
+@@ -801,6 +817,7 @@
+ RESOLVEFUNC(SSL_CTX_use_PrivateKey)
+ RESOLVEFUNC(SSL_CTX_use_RSAPrivateKey)
+ RESOLVEFUNC(SSL_CTX_use_PrivateKey_file)
++ RESOLVEFUNC(SSL_CTX_get_cert_store)
+ RESOLVEFUNC(SSL_accept)
+ RESOLVEFUNC(SSL_clear)
+ RESOLVEFUNC(SSL_connect)
+@@ -823,6 +840,23 @@
+ RESOLVEFUNC(SSL_set_connect_state)
+ RESOLVEFUNC(SSL_shutdown)
+ RESOLVEFUNC(SSL_write)
++
++ RESOLVEFUNC(X509_get_serialNumber)
++#if OPENSSL_VERSION_NUMBER >= 0x10100000L
++ RESOLVEFUNC(SSL_CTX_ctrl)
++ RESOLVEFUNC(EVP_PKEY_id)
++ RESOLVEFUNC(EVP_PKEY_base_id)
++ RESOLVEFUNC(SSL_CIPHER_get_bits)
++ RESOLVEFUNC(SSL_CTX_set_options)
++ RESOLVEFUNC(X509_get_version)
++ RESOLVEFUNC(X509_get_X509_PUBKEY)
++ RESOLVEFUNC(RSA_bits)
++ RESOLVEFUNC(DSA_security_bits)
++ RESOLVEFUNC(DSA_get0_pqg)
++ RESOLVEFUNC(X509_get_notAfter)
++ RESOLVEFUNC(X509_get_notBefore)
++#endif
++
+ #ifndef OPENSSL_NO_SSL2
+ RESOLVEFUNC(SSLv2_client_method)
+ #endif
+--- qt-everywhere-opensource-src-4.8.7/src/network/ssl/qsslkey.cpp.0131~ 2017-03-15 02:22:37.053244125 +0100
++++ qt-everywhere-opensource-src-4.8.7/src/network/ssl/qsslkey.cpp 2017-03-15 02:22:37.055244057 +0100
+@@ -328,7 +328,7 @@ int QSslKey::length() const
+ if (d->algorithm == QSsl::Rsa) {
+ return q_RSA_bits(d->rsa);
+ }else{
+- BIGNUM *p = NULL;
++ const BIGNUM *p = NULL;
+ q_DSA_get0_pqg(d->dsa, &p, NULL, NULL);
+ return q_BN_num_bits(p);
+ }
+--- qt-everywhere-opensource-src-4.8.7/src/network/ssl/qsslsocket_openssl_symbols.cpp.0131~ 2017-03-15 02:22:37.054244091 +0100
++++ qt-everywhere-opensource-src-4.8.7/src/network/ssl/qsslsocket_openssl_symbols.cpp 2017-03-15 02:29:41.155236836 +0100
+@@ -111,16 +111,16 @@ DEFINEFUNC(int, ASN1_STRING_length, ASN1
+ DEFINEFUNC2(int, ASN1_STRING_to_UTF8, unsigned char **a, a, ASN1_STRING *b, b, return 0, return);
+ DEFINEFUNC4(long, BIO_ctrl, BIO *a, a, int b, b, long c, c, void *d, d, return -1, return)
+ DEFINEFUNC(int, BIO_free, BIO *a, a, return 0, return)
+-DEFINEFUNC(BIO *, BIO_new, BIO_METHOD *a, a, return 0, return)
++DEFINEFUNC(BIO *, BIO_new, const BIO_METHOD *a, a, return 0, return)
+ DEFINEFUNC2(BIO *, BIO_new_mem_buf, void *a, a, int b, b, return 0, return)
+ DEFINEFUNC3(int, BIO_read, BIO *a, a, void *b, b, int c, c, return -1, return)
+-DEFINEFUNC(BIO_METHOD *, BIO_s_mem, void, DUMMYARG, return 0, return)
++DEFINEFUNC(const BIO_METHOD *, BIO_s_mem, void, DUMMYARG, return 0, return)
+ DEFINEFUNC3(int, BIO_write, BIO *a, a, const void *b, b, int c, c, return -1, return)
+ DEFINEFUNC(int, BN_num_bits, const BIGNUM *a, a, return 0, return)
+ DEFINEFUNC(int, CRYPTO_num_locks, DUMMYARG, DUMMYARG, return 0, return)
+ DEFINEFUNC(void, CRYPTO_set_locking_callback, void (*a)(int, int, const char *, int), a, return, DUMMYARG)
+ DEFINEFUNC(void, CRYPTO_set_id_callback, unsigned long (*a)(), a, return, DUMMYARG)
+-DEFINEFUNC(void, CRYPTO_free, void *a, a, return, DUMMYARG)
++DEFINEFUNC(void, OPENSSL_free, void *a, a, return, DUMMYARG)
+ DEFINEFUNC(void, DSA_free, DSA *a, a, return, DUMMYARG)
+ #if OPENSSL_VERSION_NUMBER < 0x00908000L
+ DEFINEFUNC3(X509 *, d2i_X509, X509 **a, a, unsigned char **b, b, long c, c, return 0, return)
+@@ -300,7 +300,7 @@ DEFINEFUNC(int, RSA_bits, const RSA *rs
+ DEFINEFUNC(int, DSA_security_bits, const DSA *dsa, dsa, return 0, return)
+ DEFINEFUNC(ASN1_TIME *, X509_get_notAfter, X509 *x, x, return 0, return)
+ DEFINEFUNC(ASN1_TIME *, X509_get_notBefore, X509 *x, x, return 0, return)
+-DEFINEFUNC4(void, DSA_get0_pqg, const DSA *d, d, BIGNUM **p, p, BIGNUM **q, q, BIGNUM **g, g, return, return)
++DEFINEFUNC4(void, DSA_get0_pqg, const DSA *d, d, const BIGNUM **p, p, const BIGNUM **q, q, const BIGNUM **g, g, return, return)
+ #endif
+
+ #ifdef Q_OS_SYMBIAN
+--- qt-everywhere-opensource-src-4.8.7/src/network/ssl/qsslsocket_openssl_symbols_p.h.0131~ 2017-03-15 02:22:37.054244091 +0100
++++ qt-everywhere-opensource-src-4.8.7/src/network/ssl/qsslsocket_openssl_symbols_p.h 2017-03-15 02:29:50.192986268 +0100
+@@ -59,6 +59,9 @@
+ QT_BEGIN_NAMESPACE
+
+ #define DUMMYARG
++#ifndef OPENSSL_NO_SSL2
++#define OPENSSL_NO_SSL2 1
++#endif
+
+ #if !defined QT_LINKED_OPENSSL
+ // **************** Shared declarations ******************
+@@ -207,16 +210,16 @@ int q_ASN1_STRING_length(ASN1_STRING *a)
+ int q_ASN1_STRING_to_UTF8(unsigned char **a, ASN1_STRING *b);
+ long q_BIO_ctrl(BIO *a, int b, long c, void *d);
+ int q_BIO_free(BIO *a);
+-BIO *q_BIO_new(BIO_METHOD *a);
++BIO *q_BIO_new(const BIO_METHOD *a);
+ BIO *q_BIO_new_mem_buf(void *a, int b);
+ int q_BIO_read(BIO *a, void *b, int c);
+-BIO_METHOD *q_BIO_s_mem();
++const BIO_METHOD *q_BIO_s_mem();
+ int q_BIO_write(BIO *a, const void *b, int c);
+ int q_BN_num_bits(const BIGNUM *a);
+ int q_CRYPTO_num_locks();
+ void q_CRYPTO_set_locking_callback(void (*a)(int, int, const char *, int));
+ void q_CRYPTO_set_id_callback(unsigned long (*a)());
+-void q_CRYPTO_free(void *a);
++void q_OPENSSL_free(void *a);
+ void q_DSA_free(DSA *a);
+ #if OPENSSL_VERSION_NUMBER >= 0x00908000L
+ // 0.9.8 broke SC and BC by changing this function's signature.
+@@ -326,7 +329,6 @@ void q_SSL_set_accept_state(SSL *a);
+ void q_SSL_set_connect_state(SSL *a);
+ int q_SSL_shutdown(SSL *a);
+ #if OPENSSL_VERSION_NUMBER >= 0x10000000L
+-const SSL_METHOD *q_SSLv2_client_method();
+ const SSL_METHOD *q_SSLv3_client_method();
+ const SSL_METHOD *q_SSLv23_client_method();
+ const SSL_METHOD *q_TLSv1_client_method();
+@@ -335,7 +337,6 @@ const SSL_METHOD *q_SSLv3_server_method(
+ const SSL_METHOD *q_SSLv23_server_method();
+ const SSL_METHOD *q_TLSv1_server_method();
+ #else
+-SSL_METHOD *q_SSLv2_client_method();
+ SSL_METHOD *q_SSLv3_client_method();
+ SSL_METHOD *q_SSLv23_client_method();
+ SSL_METHOD *q_TLSv1_client_method();
+@@ -415,7 +416,7 @@ long q_X509_get_version(X509 *x);
+ X509_PUBKEY * q_X509_get_X509_PUBKEY(X509 *x);
+ int q_RSA_bits(const RSA *rsa);
+ int q_DSA_security_bits(const DSA *dsa);
+-void q_DSA_get0_pqg(const DSA *d, BIGNUM **p, BIGNUM **q, BIGNUM **g);
++void q_DSA_get0_pqg(const DSA *d, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g);
+ #endif
+
+ #define q_SKM_sk_num(type, st) ((int (*)(const STACK_OF(type) *))q_sk_num)(st)
+--- qt-everywhere-opensource-src-4.8.7/src/network/ssl/qsslcertificate.cpp.omv~ 2017-03-15 02:27:18.143322736 +0100
++++ qt-everywhere-opensource-src-4.8.7/src/network/ssl/qsslcertificate.cpp 2017-03-15 02:29:56.215819741 +0100
+@@ -696,7 +696,7 @@
+ unsigned char *data = 0;
+ int size = q_ASN1_STRING_to_UTF8(&data, q_X509_NAME_ENTRY_get_data(e));
+ info[QString::fromUtf8(obj)] = QString::fromUtf8((char*)data, size);
+- q_CRYPTO_free(data);
++ q_OPENSSL_free(data);
+ }
+ return info;
+ }
diff --git a/subprojects/shared-modules/smpeg/smpeg-0.4.5.json b/subprojects/shared-modules/smpeg/smpeg-0.4.5.json
new file mode 100644
index 00000000..a4d82aab
--- /dev/null
+++ b/subprojects/shared-modules/smpeg/smpeg-0.4.5.json
@@ -0,0 +1,39 @@
+{
+ "name": "smpeg",
+ "config-opts": ["--disable-static"],
+ "cleanup": [
+ "/bin",
+ "/man"
+ ],
+ "rm-configure": true,
+ "sources": [
+ {
+ "type": "archive",
+ "url": "http://http.debian.net/debian/pool/main/s/smpeg/smpeg_0.4.5+cvs20030824.orig.tar.gz",
+ "sha256": "1276ea797dd9fde8a12dd3f33f180153922544c28ca9fc7b477c018876be1916"
+ },
+ {
+ "type": "script",
+ "dest-filename": "autogen.sh",
+ "commands": [
+ "AUTOMAKE=\"automake --foreign --add-missing --force-missing\" autoreconf -vfi"
+ ]
+ },
+ {
+ "type": "patch",
+ "path": "smpeg-am-prog-as.patch"
+ },
+ {
+ "type": "patch",
+ "path": "smpeg-export-mpegaudio-class.patch"
+ },
+ {
+ "type": "patch",
+ "path": "smpeg-no-gtk.patch"
+ },
+ {
+ "type": "patch",
+ "path": "smpeg-gcc6.patch"
+ }
+ ]
+}
diff --git a/subprojects/shared-modules/smpeg/smpeg-am-prog-as.patch b/subprojects/shared-modules/smpeg/smpeg-am-prog-as.patch
new file mode 100644
index 00000000..36197d71
--- /dev/null
+++ b/subprojects/shared-modules/smpeg/smpeg-am-prog-as.patch
@@ -0,0 +1,12 @@
+Index: smpeg-0.4.5+cvs20030824/configure.in
+===================================================================
+--- smpeg-0.4.5+cvs20030824.orig/configure.in
++++ smpeg-0.4.5+cvs20030824/configure.in
+@@ -57,6 +57,7 @@
+ AC_LIBTOOL_WIN32_DLL
+ AM_PROG_LIBTOOL
+ AC_PROG_INSTALL
++AM_PROG_AS
+
+ dnl Ugh.
+ AS="$CC"
diff --git a/subprojects/shared-modules/smpeg/smpeg-export-mpegaudio-class.patch b/subprojects/shared-modules/smpeg/smpeg-export-mpegaudio-class.patch
new file mode 100644
index 00000000..9ecb9b58
--- /dev/null
+++ b/subprojects/shared-modules/smpeg/smpeg-export-mpegaudio-class.patch
@@ -0,0 +1,17 @@
+--- smpeg-0.4.5+cvs20030824.orig/MPEGaudio.h
++++ smpeg-0.4.5+cvs20030824/MPEGaudio.h
+@@ -149,6 +149,14 @@
+ };
+
+ /* The actual MPEG audio class */
++
++class MPEGaudio;
++void Play_MPEGaudioSDL(void *udata, Uint8 *stream, int len);
++int Play_MPEGaudio(MPEGaudio *audio, Uint8 *stream, int len);
++#ifdef THREADED_AUDIO
++int Decode_MPEGaudio(void *udata);
++#endif
++
+ class MPEGaudio : public MPEGerror, public MPEGaudioaction {
+
+ friend void Play_MPEGaudioSDL(void *udata, Uint8 *stream, int len);
diff --git a/subprojects/shared-modules/smpeg/smpeg-gcc6.patch b/subprojects/shared-modules/smpeg/smpeg-gcc6.patch
new file mode 100644
index 00000000..21b30da5
--- /dev/null
+++ b/subprojects/shared-modules/smpeg/smpeg-gcc6.patch
@@ -0,0 +1,40 @@
+Description: Fix compiling with GCC 6.
+Author: Johannes Brandstätter <jbrandst@2ds.eu>
+Bug-Debian: https://bugs.debian.org/811742
+Last-Update: <2016-09-24>
+
+Index: smpeg-0.4.5+cvs20030824/audio/huffmantable.cpp
+===================================================================
+--- smpeg-0.4.5+cvs20030824.orig/audio/huffmantable.cpp
++++ smpeg-0.4.5+cvs20030824/audio/huffmantable.cpp
+@@ -9,6 +9,7 @@
+ #include "config.h"
+ #endif
+
++#include <climits>
+ #include "MPEGaudio.h"
+
+ static const unsigned int
+@@ -550,11 +551,11 @@ htd33[ 31][2]={{ 16, 1},{ 8, 1},{ 4,
+
+ const HUFFMANCODETABLE MPEGaudio::ht[HTN]=
+ {
+- { 0, 0-1, 0-1, 0, 0, htd33},
++ { 0, UINT_MAX, UINT_MAX, 0, 0, htd33},
+ { 1, 2-1, 2-1, 0, 7,htd01},
+ { 2, 3-1, 3-1, 0, 17,htd02},
+ { 3, 3-1, 3-1, 0, 17,htd03},
+- { 4, 0-1, 0-1, 0, 0, htd33},
++ { 4, UINT_MAX, UINT_MAX, 0, 0, htd33},
+ { 5, 4-1, 4-1, 0, 31,htd05},
+ { 6, 4-1, 4-1, 0, 31,htd06},
+ { 7, 6-1, 6-1, 0, 71,htd07},
+@@ -564,7 +565,7 @@ const HUFFMANCODETABLE MPEGaudio::ht[HTN
+ {11, 8-1, 8-1, 0,127,htd11},
+ {12, 8-1, 8-1, 0,127,htd12},
+ {13,16-1,16-1, 0,511,htd13},
+- {14, 0-1, 0-1, 0, 0, htd33},
++ {14, UINT_MAX, UINT_MAX, 0, 0, htd33},
+ {15,16-1,16-1, 0,511,htd15},
+ {16,16-1,16-1, 1,511,htd16},
+ {17,16-1,16-1, 2,511,htd16},
diff --git a/subprojects/shared-modules/smpeg/smpeg-no-gtk.patch b/subprojects/shared-modules/smpeg/smpeg-no-gtk.patch
new file mode 100644
index 00000000..792596cf
--- /dev/null
+++ b/subprojects/shared-modules/smpeg/smpeg-no-gtk.patch
@@ -0,0 +1,37 @@
+From 62cda978596a323cd7042722f906df906007e5b5 Mon Sep 17 00:00:00 2001
+From: Cosimo Cecchi <cosimoc@gnome.org>
+Date: Sat, 15 Apr 2017 13:24:39 -0700
+Subject: [PATCH] Comment out GTK detection
+
+---
+ configure.in | 14 +++++++-------
+ 1 file changed, 7 insertions(+), 7 deletions(-)
+
+diff --git a/configure.in b/configure.in
+index 20b1355..2cb4753 100644
+--- a/configure.in
++++ b/configure.in
+@@ -208,13 +208,13 @@ AC_ARG_ENABLE(gtk_player,
+ [ --enable-gtk-player build a GTk sample SMPEG player [default=yes]],
+ , enable_gtk_player=yes)
+ have_gtk=no
+-if test x$enable_gtk_player = xyes; then
+- AM_PATH_GTK(1.2.1, have_gtk=yes)
+- if test x$have_gtk = xyes; then
+- CFLAGS="$CFLAGS $GTK_CFLAGS"
+- fi
+- AC_SUBST(GTK_LIBS)
+-fi
++dnl if test x$enable_gtk_player = xyes; then
++dnl AM_PATH_GTK(1.2.1, have_gtk=yes)
++dnl if test x$have_gtk = xyes; then
++dnl CFLAGS="$CFLAGS $GTK_CFLAGS"
++dnl fi
++dnl AC_SUBST(GTK_LIBS)
++dnl fi
+ AM_CONDITIONAL(HAVE_GTK, test x$have_gtk = xyes)
+
+ dnl See if we can build the Mesa player
+--
+2.12.2
+