[gitg/wip/new-dash] wip



commit 1d933f76991dff6ad611157b9d1338308a5fb9a4
Author: Ignacio Casal Quinteiro <icq gnome org>
Date:   Sat Jan 23 12:46:27 2016 +0100

    wip

 Makefile.am                                        |    3 +
 configure.ac                                       |    6 +
 contrib/ide/Makefile.am                            |   26 +
 contrib/ide/ide-doap-person.c                      |  178 ++++++
 contrib/ide/ide-doap-person.h                      |   40 ++
 contrib/ide/ide-doap.c                             |  630 ++++++++++++++++++++
 contrib/ide/ide-doap.h                             |   60 ++
 contrib/ide/ide.h                                  |   31 +
 contrib/ide/ide.vapi                               |   48 ++
 contrib/xml/Makefile.am                            |   11 +
 contrib/xml/xml-reader.c                           |  597 +++++++++++++++++++
 contrib/xml/xml-reader.h                           |  100 +++
 gitg/resources/ui/gitg-dash-view.ui                |   27 +-
 libgitg/Makefile.am                                |    6 +-
 libgitg/gitg-repository-list-box.vala              |  187 +++----
 .../resources/ui/gitg-repository-list-box-row.ui   |   47 +-
 libgitg/resources/ui/libgitg-style.css             |    6 +
 17 files changed, 1844 insertions(+), 159 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 61f85be..9267a3c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -83,6 +83,7 @@ GITIGNOREFILES    =
 CLEANFILES        =
 bin_PROGRAMS      =
 noinst_PROGRAMS   =
+noinst_LTLIBRARIES =
 SCALABLE_ICONS    =
 gsettings_SCHEMAS =
 TESTS             =
@@ -91,6 +92,8 @@ GITG_PLUGIN_VAPISOURCES =                     \
        libgitg-ext/libgitg-ext-1.0.vapi        \
        libgitg/libgitg-1.0.vapi
 
+include contrib/xml/Makefile.am
+include contrib/ide/Makefile.am
 include libgitg/Makefile.am
 include libgitg-ext/Makefile.am
 include plugins/Makefile.am
diff --git a/configure.ac b/configure.ac
index c950490..b6ed060 100644
--- a/configure.ac
+++ b/configure.ac
@@ -92,6 +92,7 @@ GTKSOURCEVIEW_REQUIRED_VERSION=3.10
 INTROSPECTION_REQUIRED=0.10.1
 LIBGIT2_GLIB_REQUIRED_VERSION=0.23.5
 LIBGIT2_GLIB_REQUIRED_MAX_VERSION=0.24.0
+LIBXML_REQUIRED_VERSION=2.9.0
 
 gdk_targets=`$PKG_CONFIG --variable=targets gdk-3.0`
 
@@ -154,6 +155,11 @@ PKG_CHECK_MODULES(LIBGITG, [
        libsecret-1
 ])
 
+PKG_CHECK_MODULES(XML, [
+       gio-2.0 >= $GLIB_REQUIRED_VERSION
+       libxml-2.0 >= $LIBXML_REQUIRED_VERSION
+])
+
 AC_SUBST(GIO_SYSTEM_PKG)
 AM_CONDITIONAL(GDK_WINDOWING_X11, test "$gdk_windowing_x11" = "yes")
 AM_CONDITIONAL(GDK_WINDOWING_QUARTZ, test "$gdk_windowing_quartz" = "yes")
diff --git a/contrib/ide/Makefile.am b/contrib/ide/Makefile.am
new file mode 100644
index 0000000..c1f83ab
--- /dev/null
+++ b/contrib/ide/Makefile.am
@@ -0,0 +1,26 @@
+noinst_LTLIBRARIES += contrib/ide/libide.la
+
+contrib_ide_libide_la_CPPFLAGS = \
+       -I$(top_srcdir)/contrib/xml
+
+contrib_ide_libide_la_SOURCES = \
+       contrib/ide/ide-doap.c \
+       contrib/ide/ide-doap.h \
+       contrib/ide/ide-doap-person.c \
+       contrib/ide/ide-doap-person.h \
+       contrib/ide/ide.h \
+       $(NULL)
+
+contrib_ide_libide_la_CFLAGS = \
+       $(XML_CFLAGS) \
+       $(NULL)
+
+contrib_ide_libide_la_LIBADD = \
+       $(XML_LIBS) \
+       contrib/xml/libxml.la \
+       $(NULL)
+
+EXTRA_DIST += \
+       contrib/ide/ide.vapi
+
+-include $(top_srcdir)/git.mk
diff --git a/contrib/ide/ide-doap-person.c b/contrib/ide/ide-doap-person.c
new file mode 100644
index 0000000..ca1d84d
--- /dev/null
+++ b/contrib/ide/ide-doap-person.c
@@ -0,0 +1,178 @@
+/* ide-doap-person.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This program 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 3 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n.h>
+
+#include "ide-doap-person.h"
+
+struct _IdeDoapPerson
+{
+  GObject parent_instance;
+
+  gchar *email;
+  gchar *name;
+};
+
+G_DEFINE_TYPE (IdeDoapPerson, ide_doap_person, G_TYPE_OBJECT)
+
+enum {
+  PROP_0,
+  PROP_EMAIL,
+  PROP_NAME,
+  LAST_PROP
+};
+
+static GParamSpec *properties [LAST_PROP];
+
+IdeDoapPerson *
+ide_doap_person_new (void)
+{
+  return g_object_new (IDE_TYPE_DOAP_PERSON, NULL);
+}
+
+const gchar *
+ide_doap_person_get_name (IdeDoapPerson *self)
+{
+  g_return_val_if_fail (IDE_IS_DOAP_PERSON (self), NULL);
+
+  return self->name;
+}
+
+void
+ide_doap_person_set_name (IdeDoapPerson *self,
+                          const gchar   *name)
+{
+  g_return_if_fail (IDE_IS_DOAP_PERSON (self));
+
+  if (g_strcmp0 (self->name, name) != 0)
+    {
+      g_free (self->name);
+      self->name = g_strdup (name);
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_NAME]);
+    }
+}
+
+const gchar *
+ide_doap_person_get_email (IdeDoapPerson *self)
+{
+  g_return_val_if_fail (IDE_IS_DOAP_PERSON (self), NULL);
+
+  return self->email;
+}
+
+void
+ide_doap_person_set_email (IdeDoapPerson *self,
+                           const gchar   *email)
+{
+  g_return_if_fail (IDE_IS_DOAP_PERSON (self));
+
+  if (g_strcmp0 (self->email, email) != 0)
+    {
+      g_free (self->email);
+      self->email = g_strdup (email);
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_EMAIL]);
+    }
+}
+
+static void
+ide_doap_person_finalize (GObject *object)
+{
+  IdeDoapPerson *self = (IdeDoapPerson *)object;
+
+  g_clear_pointer (&self->email, g_free);
+  g_clear_pointer (&self->name, g_free);
+
+  G_OBJECT_CLASS (ide_doap_person_parent_class)->finalize (object);
+}
+
+static void
+ide_doap_person_get_property (GObject    *object,
+                              guint       prop_id,
+                              GValue     *value,
+                              GParamSpec *pspec)
+{
+  IdeDoapPerson *self = IDE_DOAP_PERSON (object);
+
+  switch (prop_id)
+    {
+    case PROP_EMAIL:
+      g_value_set_string (value, ide_doap_person_get_email (self));
+      break;
+
+    case PROP_NAME:
+      g_value_set_string (value, ide_doap_person_get_name (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_doap_person_set_property (GObject      *object,
+                              guint         prop_id,
+                              const GValue *value,
+                              GParamSpec   *pspec)
+{
+  IdeDoapPerson *self = IDE_DOAP_PERSON (object);
+
+  switch (prop_id)
+    {
+    case PROP_EMAIL:
+      ide_doap_person_set_email (self, g_value_get_string (value));
+      break;
+
+    case PROP_NAME:
+      ide_doap_person_set_name (self, g_value_get_string (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_doap_person_class_init (IdeDoapPersonClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ide_doap_person_finalize;
+  object_class->get_property = ide_doap_person_get_property;
+  object_class->set_property = ide_doap_person_set_property;
+
+  properties [PROP_EMAIL] =
+    g_param_spec_string ("email",
+                         "Email",
+                         "The email of the person.",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_NAME] =
+    g_param_spec_string ("name",
+                         "Name",
+                         "The name of the person.",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, LAST_PROP, properties);
+}
+
+static void
+ide_doap_person_init (IdeDoapPerson *self)
+{
+}
diff --git a/contrib/ide/ide-doap-person.h b/contrib/ide/ide-doap-person.h
new file mode 100644
index 0000000..c5f6439
--- /dev/null
+++ b/contrib/ide/ide-doap-person.h
@@ -0,0 +1,40 @@
+/* ide-doap-person.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This program 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 3 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_DOAP_PERSON_H
+#define IDE_DOAP_PERSON_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_DOAP_PERSON (ide_doap_person_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeDoapPerson, ide_doap_person, IDE, DOAP_PERSON, GObject)
+
+IdeDoapPerson *ide_doap_person_new       (void);
+const gchar   *ide_doap_person_get_name  (IdeDoapPerson *self);
+void           ide_doap_person_set_name  (IdeDoapPerson *self,
+                                          const gchar   *name);
+const gchar   *ide_doap_person_get_email (IdeDoapPerson *self);
+void           ide_doap_person_set_email (IdeDoapPerson *self,
+                                          const gchar   *email);
+
+G_END_DECLS
+
+#endif /* IDE_DOAP_PERSON_H */
diff --git a/contrib/ide/ide-doap.c b/contrib/ide/ide-doap.c
new file mode 100644
index 0000000..1bf0542
--- /dev/null
+++ b/contrib/ide/ide-doap.c
@@ -0,0 +1,630 @@
+/* ide-doap.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This program 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 3 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-doap"
+
+#include <glib/gi18n.h>
+
+#include "ide-doap.h"
+
+#include "xml-reader.h"
+
+/*
+ * TODO: We don't do any XMLNS checking or anything here.
+ */
+
+struct _IdeDoap
+{
+  GObject parent_instance;
+
+  gchar     *bug_database;
+  gchar     *category;
+  gchar     *description;
+  gchar     *download_page;
+  gchar     *homepage;;
+  gchar     *name;
+  gchar     *shortdesc;
+
+  GPtrArray *languages;
+  GList     *maintainers;
+};
+
+G_DEFINE_QUARK (ide_doap_error, ide_doap_error)
+G_DEFINE_TYPE (IdeDoap, ide_doap, G_TYPE_OBJECT)
+
+enum {
+  PROP_0,
+  PROP_BUG_DATABASE,
+  PROP_CATEGORY,
+  PROP_DESCRIPTION,
+  PROP_DOWNLOAD_PAGE,
+  PROP_HOMEPAGE,
+  PROP_LANGUAGES,
+  PROP_NAME,
+  PROP_SHORTDESC,
+  LAST_PROP
+};
+
+static GParamSpec *properties [LAST_PROP];
+
+IdeDoap *
+ide_doap_new (void)
+{
+  return g_object_new (IDE_TYPE_DOAP, NULL);
+}
+
+const gchar *
+ide_doap_get_name (IdeDoap *self)
+{
+  g_return_val_if_fail (IDE_IS_DOAP (self), NULL);
+
+  return self->name;
+}
+
+const gchar *
+ide_doap_get_shortdesc (IdeDoap *self)
+{
+  g_return_val_if_fail (IDE_IS_DOAP (self), NULL);
+
+  return self->shortdesc;
+}
+
+const gchar *
+ide_doap_get_description (IdeDoap *self)
+{
+  g_return_val_if_fail (IDE_IS_DOAP (self), NULL);
+
+  return self->description;
+}
+
+const gchar *
+ide_doap_get_bug_database (IdeDoap *self)
+{
+  g_return_val_if_fail (IDE_IS_DOAP (self), NULL);
+
+  return self->bug_database;
+}
+
+const gchar *
+ide_doap_get_download_page (IdeDoap *self)
+{
+  g_return_val_if_fail (IDE_IS_DOAP (self), NULL);
+
+  return self->download_page;
+}
+
+const gchar *
+ide_doap_get_homepage (IdeDoap *self)
+{
+  g_return_val_if_fail (IDE_IS_DOAP (self), NULL);
+
+  return self->homepage;
+}
+
+const gchar *
+ide_doap_get_category (IdeDoap *self)
+{
+  g_return_val_if_fail (IDE_IS_DOAP (self), NULL);
+
+  return self->category;
+}
+
+/**
+ * ide_doap_get_languages:
+ *
+ * Returns: (transfer none): A #GStrv.
+ */
+gchar **
+ide_doap_get_languages (IdeDoap *self)
+{
+  g_return_val_if_fail (IDE_IS_DOAP (self), NULL);
+
+  if (self->languages != NULL)
+    return (gchar **)self->languages->pdata;
+
+  return NULL;
+}
+
+static void
+ide_doap_set_bug_database (IdeDoap     *self,
+                           const gchar *bug_database)
+{
+  g_return_if_fail (IDE_IS_DOAP (self));
+
+  if (g_strcmp0 (self->bug_database, bug_database) != 0)
+    {
+      g_free (self->bug_database);
+      self->bug_database = g_strdup (bug_database);
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BUG_DATABASE]);
+    }
+}
+
+static void
+ide_doap_set_category (IdeDoap     *self,
+                       const gchar *category)
+{
+  g_return_if_fail (IDE_IS_DOAP (self));
+
+  if (g_strcmp0 (self->category, category) != 0)
+    {
+      g_free (self->category);
+      self->category = g_strdup (category);
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CATEGORY]);
+    }
+}
+
+static void
+ide_doap_set_description (IdeDoap     *self,
+                          const gchar *description)
+{
+  g_return_if_fail (IDE_IS_DOAP (self));
+
+  if (g_strcmp0 (self->description, description) != 0)
+    {
+      g_free (self->description);
+      self->description = g_strdup (description);
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_DESCRIPTION]);
+    }
+}
+
+static void
+ide_doap_set_download_page (IdeDoap     *self,
+                            const gchar *download_page)
+{
+  g_return_if_fail (IDE_IS_DOAP (self));
+
+  if (g_strcmp0 (self->download_page, download_page) != 0)
+    {
+      g_free (self->download_page);
+      self->download_page = g_strdup (download_page);
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_DOWNLOAD_PAGE]);
+    }
+}
+
+static void
+ide_doap_set_homepage (IdeDoap     *self,
+                       const gchar *homepage)
+{
+  g_return_if_fail (IDE_IS_DOAP (self));
+
+  if (g_strcmp0 (self->homepage, homepage) != 0)
+    {
+      g_free (self->homepage);
+      self->homepage = g_strdup (homepage);
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_HOMEPAGE]);
+    }
+}
+
+static void
+ide_doap_set_name (IdeDoap     *self,
+                   const gchar *name)
+{
+  g_return_if_fail (IDE_IS_DOAP (self));
+
+  if (g_strcmp0 (self->name, name) != 0)
+    {
+      g_free (self->name);
+      self->name = g_strdup (name);
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_NAME]);
+    }
+}
+
+static void
+ide_doap_set_shortdesc (IdeDoap     *self,
+                        const gchar *shortdesc)
+{
+  g_return_if_fail (IDE_IS_DOAP (self));
+
+  if (g_strcmp0 (self->shortdesc, shortdesc) != 0)
+    {
+      g_free (self->shortdesc);
+      self->shortdesc = g_strdelimit (g_strdup (shortdesc), "\n", ' ');
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SHORTDESC]);
+    }
+}
+
+/**
+ * ide_doap_get_maintainers:
+ *
+ *
+ *
+ * Returns: (transfer none) (element-type IdeDoapPerson*): A #GList of #IdeDoapPerson.
+ */
+GList *
+ide_doap_get_maintainers (IdeDoap *self)
+{
+  g_return_val_if_fail (IDE_IS_DOAP (self), NULL);
+
+  return self->maintainers;
+}
+
+static void
+ide_doap_add_language (IdeDoap     *self,
+                       const gchar *language)
+{
+  g_return_if_fail (IDE_IS_DOAP (self));
+  g_return_if_fail (language != NULL);
+
+  if (self->languages == NULL)
+    {
+      self->languages = g_ptr_array_new_with_free_func (g_free);
+      g_ptr_array_add (self->languages, NULL);
+    }
+
+  g_assert (self->languages->len > 0);
+
+  g_ptr_array_index (self->languages, self->languages->len - 1) = g_strdup (language);
+  g_ptr_array_add (self->languages, NULL);
+
+  g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_LANGUAGES]);
+}
+
+static void
+ide_doap_set_languages (IdeDoap  *self,
+                        gchar   **languages)
+{
+  gsize i;
+
+  g_return_if_fail (IDE_IS_DOAP (self));
+
+  if ((self->languages != NULL) && (self->languages->len > 0))
+    g_ptr_array_remove_range (self->languages, 0, self->languages->len);
+
+  g_object_freeze_notify (G_OBJECT (self));
+  for (i = 0; languages [i]; i++)
+    ide_doap_add_language (self, languages [i]);
+  g_object_thaw_notify (G_OBJECT (self));
+}
+
+static void
+ide_doap_finalize (GObject *object)
+{
+  IdeDoap *self = (IdeDoap *)object;
+
+  g_clear_pointer (&self->bug_database, g_free);
+  g_clear_pointer (&self->category, g_free);
+  g_clear_pointer (&self->description, g_free);
+  g_clear_pointer (&self->download_page, g_free);
+  g_clear_pointer (&self->homepage, g_free);
+  g_clear_pointer (&self->languages, g_ptr_array_unref);
+  g_clear_pointer (&self->name, g_free);
+  g_clear_pointer (&self->shortdesc, g_free);
+
+  g_list_free_full (self->maintainers, g_object_unref);
+  self->maintainers = NULL;
+
+  G_OBJECT_CLASS (ide_doap_parent_class)->finalize (object);
+}
+
+static void
+ide_doap_get_property (GObject    *object,
+                       guint       prop_id,
+                       GValue     *value,
+                       GParamSpec *pspec)
+{
+  IdeDoap *self = IDE_DOAP (object);
+
+  switch (prop_id)
+    {
+    case PROP_BUG_DATABASE:
+      g_value_set_string (value, ide_doap_get_bug_database (self));
+      break;
+
+    case PROP_CATEGORY:
+      g_value_set_string (value, ide_doap_get_category (self));
+      break;
+
+    case PROP_DESCRIPTION:
+      g_value_set_string (value, ide_doap_get_description (self));
+      break;
+
+    case PROP_DOWNLOAD_PAGE:
+      g_value_set_string (value, ide_doap_get_download_page (self));
+      break;
+
+    case PROP_HOMEPAGE:
+      g_value_set_string (value, ide_doap_get_homepage (self));
+      break;
+
+    case PROP_LANGUAGES:
+      g_value_set_boxed (value, ide_doap_get_languages (self));
+      break;
+
+    case PROP_NAME:
+      g_value_set_string (value, ide_doap_get_name (self));
+      break;
+
+    case PROP_SHORTDESC:
+      g_value_set_string (value, ide_doap_get_shortdesc (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_doap_set_property (GObject      *object,
+                       guint         prop_id,
+                       const GValue *value,
+                       GParamSpec   *pspec)
+{
+  IdeDoap *self = IDE_DOAP (object);
+
+  switch (prop_id)
+    {
+    case PROP_BUG_DATABASE:
+      ide_doap_set_bug_database (self, g_value_get_string (value));
+      break;
+
+    case PROP_CATEGORY:
+      ide_doap_set_category (self, g_value_get_string (value));
+      break;
+
+    case PROP_DESCRIPTION:
+      ide_doap_set_description (self, g_value_get_string (value));
+      break;
+
+    case PROP_DOWNLOAD_PAGE:
+      ide_doap_set_download_page (self, g_value_get_string (value));
+      break;
+
+    case PROP_HOMEPAGE:
+      ide_doap_set_homepage (self, g_value_get_string (value));
+      break;
+
+    case PROP_LANGUAGES:
+      ide_doap_set_languages (self, g_value_get_boxed (value));
+      break;
+
+    case PROP_NAME:
+      ide_doap_set_name (self, g_value_get_string (value));
+      break;
+
+    case PROP_SHORTDESC:
+      ide_doap_set_shortdesc (self, g_value_get_string (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_doap_class_init (IdeDoapClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ide_doap_finalize;
+  object_class->get_property = ide_doap_get_property;
+  object_class->set_property = ide_doap_set_property;
+
+  properties [PROP_BUG_DATABASE] =
+    g_param_spec_string ("bug-database",
+                         "Bug Database",
+                         "Bug Database",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_CATEGORY] =
+    g_param_spec_string ("category",
+                         "Category",
+                         "Category",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_DESCRIPTION] =
+    g_param_spec_string ("description",
+                         "Description",
+                         "Description",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_DOWNLOAD_PAGE] =
+    g_param_spec_string ("download-page",
+                         "Download Page",
+                         "Download Page",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_HOMEPAGE] =
+    g_param_spec_string ("homepage",
+                         "Homepage",
+                         "Homepage",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_LANGUAGES] =
+    g_param_spec_string ("languages",
+                         "Languages",
+                         "Languages",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_NAME] =
+    g_param_spec_string ("name",
+                         "Name",
+                         "Name",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_SHORTDESC] =
+    g_param_spec_string ("shortdesc",
+                         "Shortdesc",
+                         "Shortdesc",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, LAST_PROP, properties);
+}
+
+static void
+ide_doap_init (IdeDoap *self)
+{
+}
+
+static gboolean
+ide_doap_parse_maintainer (IdeDoap   *self,
+                           XmlReader *reader)
+{
+  g_assert (IDE_IS_DOAP (self));
+  g_assert (XML_IS_READER (reader));
+
+  if (!xml_reader_read (reader))
+    return FALSE;
+
+  do
+    {
+      if (xml_reader_is_a_local (reader, "Person") && xml_reader_read (reader))
+        {
+          g_autoptr(IdeDoapPerson) person = ide_doap_person_new ();
+
+          do
+            {
+              if (xml_reader_is_a_local (reader, "name"))
+                {
+                  ide_doap_person_set_name (person, xml_reader_read_string (reader));
+                }
+              else if (xml_reader_is_a_local (reader, "mbox"))
+                {
+                  gchar *str;
+
+                  str = xml_reader_get_attribute (reader, "rdf:resource");
+                  if (str != NULL && str[0] != '\0' && g_str_has_prefix (str, "mailto:";))
+                    ide_doap_person_set_email (person, str + strlen ("mailto:";));
+                  g_free (str);
+                }
+            }
+          while (xml_reader_read_to_next (reader));
+
+          if (ide_doap_person_get_name (person) || ide_doap_person_get_email (person))
+            self->maintainers = g_list_append (self->maintainers, g_object_ref (person));
+        }
+    }
+  while (xml_reader_read_to_next (reader));
+
+  return TRUE;
+}
+
+static gboolean
+load_doap (IdeDoap       *self,
+           XmlReader     *reader,
+           GError       **error)
+{
+  if (!xml_reader_read_start_element (reader, "Project"))
+    {
+      g_set_error (error,
+                   IDE_DOAP_ERROR,
+                   IDE_DOAP_ERROR_INVALID_FORMAT,
+                   "Project element is missing from doap.");
+      return FALSE;
+    }
+
+  g_object_freeze_notify (G_OBJECT (self));
+
+  xml_reader_read (reader);
+
+  do
+    {
+      const gchar *element_name;
+
+      element_name = xml_reader_get_local_name (reader);
+
+      if (g_strcmp0 (element_name, "name") == 0 ||
+          g_strcmp0 (element_name, "shortdesc") == 0 ||
+          g_strcmp0 (element_name, "description") == 0)
+        {
+          gchar *str;
+
+          str = xml_reader_read_string (reader);
+          if (str != NULL)
+            g_object_set (self, element_name, g_strstrip (str), NULL);
+          g_free (str);
+        }
+      else if (g_strcmp0 (element_name, "category") == 0 ||
+               g_strcmp0 (element_name, "homepage") == 0 ||
+               g_strcmp0 (element_name, "download-page") == 0 ||
+               g_strcmp0 (element_name, "bug-database") == 0)
+        {
+          gchar *str;
+
+          str = xml_reader_get_attribute (reader, "rdf:resource");
+          if (str != NULL)
+            g_object_set (self, element_name, g_strstrip (str), NULL);
+          g_free (str);
+        }
+      else if (g_strcmp0 (element_name, "programming-language") == 0)
+        {
+          gchar *str;
+
+          str = xml_reader_read_string (reader);
+          if (str != NULL && str[0] != '\0')
+            ide_doap_add_language (self, g_strstrip (str));
+          g_free (str);
+        }
+      else if (g_strcmp0 (element_name, "maintainer") == 0)
+        {
+          if (!ide_doap_parse_maintainer (self, reader))
+            break;
+        }
+    }
+  while (xml_reader_read_to_next (reader));
+
+  g_object_thaw_notify (G_OBJECT (self));
+
+  return TRUE;
+}
+
+gboolean
+ide_doap_load_from_file (IdeDoap       *self,
+                         GFile         *file,
+                         GCancellable  *cancellable,
+                         GError       **error)
+{
+  g_autoptr(XmlReader) reader = NULL;
+
+  g_return_val_if_fail (IDE_IS_DOAP (self), FALSE);
+  g_return_val_if_fail (G_IS_FILE (file), FALSE);
+  g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE);
+
+  reader = xml_reader_new ();
+
+  if (!xml_reader_load_from_file (reader, file, cancellable, error))
+    return FALSE;
+
+  return load_doap (self, reader, error);
+}
+
+gboolean
+ide_doap_load_from_data (IdeDoap       *self,
+                         const gchar   *data,
+                         gsize          length,
+                         GError       **error)
+{
+  g_autoptr(XmlReader) reader = NULL;
+
+  g_return_val_if_fail (IDE_IS_DOAP (self), FALSE);
+  g_return_val_if_fail (data != NULL, FALSE);
+
+  reader = xml_reader_new ();
+
+  if (!xml_reader_load_from_data (reader, (const gchar *)data, length, NULL, NULL))
+    return FALSE;
+
+  return load_doap (self, reader, error);
+}
diff --git a/contrib/ide/ide-doap.h b/contrib/ide/ide-doap.h
new file mode 100644
index 0000000..0751313
--- /dev/null
+++ b/contrib/ide/ide-doap.h
@@ -0,0 +1,60 @@
+/* ide-doap.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This program 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 3 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_DOAP_H
+#define IDE_DOAP_H
+
+#include <gio/gio.h>
+
+#include "ide-doap-person.h"
+
+G_BEGIN_DECLS
+
+#define IDE_DOAP_ERROR (ide_doap_error_quark())
+#define IDE_TYPE_DOAP  (ide_doap_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeDoap, ide_doap, IDE, DOAP, GObject)
+
+typedef enum
+{
+  IDE_DOAP_ERROR_INVALID_FORMAT = 1,
+} IdeDoapError;
+
+IdeDoap       *ide_doap_new               (void);
+GQuark         ide_doap_error_quark       (void);
+gboolean       ide_doap_load_from_file    (IdeDoap        *self,
+                                           GFile          *file,
+                                           GCancellable   *cancellable,
+                                           GError        **error);
+gboolean       ide_doap_load_from_data    (IdeDoap        *self,
+                                           const gchar    *data,
+                                           gsize           length,
+                                           GError        **error);
+const gchar   *ide_doap_get_name          (IdeDoap        *self);
+const gchar   *ide_doap_get_shortdesc     (IdeDoap        *self);
+const gchar   *ide_doap_get_description   (IdeDoap        *self);
+const gchar   *ide_doap_get_bug_database  (IdeDoap        *self);
+const gchar   *ide_doap_get_download_page (IdeDoap        *self);
+const gchar   *ide_doap_get_homepage      (IdeDoap        *self);
+const gchar   *ide_doap_get_category      (IdeDoap        *self);
+gchar        **ide_doap_get_languages     (IdeDoap        *self);
+GList         *ide_doap_get_maintainers   (IdeDoap        *self);
+
+G_END_DECLS
+
+#endif /* IDE_DOAP_H */
diff --git a/contrib/ide/ide.h b/contrib/ide/ide.h
new file mode 100644
index 0000000..be0ee0c
--- /dev/null
+++ b/contrib/ide/ide.h
@@ -0,0 +1,31 @@
+/* ide.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This program 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 3 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_H
+#define IDE_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#include "ide-doap.h"
+#include "ide-doap-person.h"
+
+G_END_DECLS
+
+#endif /* IDE_H */
diff --git a/contrib/ide/ide.vapi b/contrib/ide/ide.vapi
new file mode 100644
index 0000000..c14fc63
--- /dev/null
+++ b/contrib/ide/ide.vapi
@@ -0,0 +1,48 @@
+// FIXME: in the future we might want to automatically generate this
+[CCode (cprefix = "Ide", gir_namespace = "Ide", gir_version = "1.0", lower_case_cprefix = "ide_")]
+namespace Ide {
+       [CCode (cheader_filename = "ide.h", type_id = "ide_doap_get_type ()")]
+       public class Doap : GLib.Object {
+               [CCode (has_construct_function = false)]
+               public Doap ();
+               public unowned string get_bug_database ();
+               public unowned string get_category ();
+               public unowned string get_description ();
+               public unowned string get_download_page ();
+               public unowned string get_homepage ();
+               [CCode (array_length = false, array_null_terminated = true)]
+               public unowned string[] get_languages ();
+               public unowned GLib.List<Ide.DoapPerson> get_maintainers ();
+               public unowned string get_name ();
+               public unowned string get_shortdesc ();
+               public bool load_from_file (GLib.File file, GLib.Cancellable? cancellable = null) throws 
GLib.Error;
+               public bool load_from_data (string data, size_t length) throws GLib.Error;
+               [NoAccessorMethod]
+               public string bug_database { owned get; set; }
+               [NoAccessorMethod]
+               public string category { owned get; set; }
+               [NoAccessorMethod]
+               public string description { owned get; set; }
+               [NoAccessorMethod]
+               public string download_page { owned get; set; }
+               [NoAccessorMethod]
+               public string homepage { owned get; set; }
+               [NoAccessorMethod]
+               public string languages { owned get; set; }
+               [NoAccessorMethod]
+               public string name { owned get; set; }
+               [NoAccessorMethod]
+               public string shortdesc { owned get; set; }
+       }
+       [CCode (cheader_filename = "ide.h", type_id = "ide_doap_person_get_type ()")]
+       public class DoapPerson : GLib.Object {
+               [CCode (has_construct_function = false)]
+               public DoapPerson ();
+               public unowned string get_email ();
+               public unowned string get_name ();
+               public void set_email (string email);
+               public void set_name (string name);
+               public string email { get; set; }
+               public string name { get; set; }
+       }
+}
diff --git a/contrib/xml/Makefile.am b/contrib/xml/Makefile.am
new file mode 100644
index 0000000..487fa52
--- /dev/null
+++ b/contrib/xml/Makefile.am
@@ -0,0 +1,11 @@
+noinst_LTLIBRARIES += contrib/xml/libxml.la
+
+contrib_xml_libxml_la_SOURCES = \
+       contrib/xml/xml-reader.c \
+       contrib/xml/xml-reader.h \
+       $(NULL)
+
+contrib_xml_libxml_la_CFLAGS = $(XML_CFLAGS)
+contrib_xml_libxml_la_LIBADD = $(XML_LIBS)
+
+-include $(top_srcdir)/git.mk
diff --git a/contrib/xml/xml-reader.c b/contrib/xml/xml-reader.c
new file mode 100644
index 0000000..3e783a7
--- /dev/null
+++ b/contrib/xml/xml-reader.c
@@ -0,0 +1,597 @@
+/* xml-reader.c
+ *
+ * Copyright (C) 2009  Christian Hergert  <chris dronelabs com>
+ *
+ * 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 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.
+ *
+ * Author:
+ *   Christian Hergert  <chris dronelabs com>
+ */
+
+#include <glib/gi18n.h>
+#include <string.h>
+#include <libxml/xmlreader.h>
+
+#include "xml-reader.h"
+
+#define XML_TO_CHAR(s)  ((char *) (s))
+#define CHAR_TO_XML(s)  ((unsigned char *) (s))
+#define RETURN_STRDUP_AND_XMLFREE(stmt) \
+  G_STMT_START {                        \
+    guchar *x;                          \
+    gchar *y;                           \
+    x = stmt;                           \
+    y = g_strdup((char *)x);            \
+    xmlFree(x);                         \
+    return y;                           \
+  } G_STMT_END
+
+struct _XmlReader
+{
+  GObject           parent_instance;
+  xmlTextReaderPtr  xml;
+  GInputStream     *stream;
+  gchar            *cur_name;
+  gchar            *encoding;
+  gchar            *uri;
+};
+
+enum {
+  PROP_0,
+  PROP_ENCODING,
+  PROP_URI,
+  LAST_PROP
+};
+
+enum {
+  ERROR,
+  LAST_SIGNAL
+};
+
+G_DEFINE_QUARK (xml_reader_error, xml_reader_error)
+G_DEFINE_TYPE (XmlReader, xml_reader, G_TYPE_OBJECT)
+
+static GParamSpec *properties [LAST_PROP];
+static guint signals [LAST_SIGNAL];
+
+#define XML_NODE_TYPE_ELEMENT      1
+#define XML_NODE_TYPE_END_ELEMENT 15
+#define XML_NODE_TYPE_ATTRIBUTE    2
+
+static void
+xml_reader_set_encoding (XmlReader   *reader,
+                         const gchar *encoding)
+{
+   g_return_if_fail (XML_IS_READER (reader));
+   g_free (reader->encoding);
+   reader->encoding = g_strdup (encoding);
+}
+
+static void
+xml_reader_set_uri (XmlReader   *reader,
+                    const gchar *uri)
+{
+   g_return_if_fail (XML_IS_READER (reader));
+   g_free (reader->uri);
+   reader->uri = g_strdup (uri);
+}
+
+static void
+xml_reader_clear (XmlReader *reader)
+{
+   g_return_if_fail(XML_IS_READER(reader));
+
+   g_free (reader->cur_name);
+   reader->cur_name = NULL;
+
+   if (reader->xml) {
+      xmlTextReaderClose(reader->xml);
+      xmlFreeTextReader(reader->xml);
+      reader->xml = NULL;
+   }
+
+   if (reader->stream) {
+      g_object_unref(reader->stream);
+      reader->stream = NULL;
+   }
+}
+
+static void
+xml_reader_finalize (GObject *object)
+{
+   XmlReader *reader = (XmlReader *)object;
+
+   xml_reader_clear (reader);
+
+   g_free (reader->encoding);
+   reader->encoding = NULL;
+
+   g_free (reader->uri);
+   reader->uri = NULL;
+
+   G_OBJECT_CLASS (xml_reader_parent_class)->finalize (object);
+}
+
+static void
+xml_reader_get_property (GObject    *object,
+                         guint       prop_id,
+                         GValue     *value,
+                         GParamSpec *pspec)
+{
+   XmlReader *reader = (XmlReader *)object;
+
+   switch (prop_id)
+     {
+     case PROP_ENCODING:
+        g_value_set_string (value, reader->encoding);
+        break;
+
+     case PROP_URI:
+        g_value_set_string (value, reader->uri);
+        break;
+
+     default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+     }
+}
+
+static void
+xml_reader_set_property (GObject      *object,
+                         guint         prop_id,
+                         const GValue *value,
+                         GParamSpec   *pspec)
+{
+   XmlReader *reader = (XmlReader *)object;
+
+   switch (prop_id)
+     {
+     case PROP_ENCODING:
+        xml_reader_set_encoding (reader, g_value_get_string (value));
+        break;
+
+     case PROP_URI:
+        xml_reader_set_uri (reader, g_value_get_string (value));
+        break;
+
+     default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+     }
+}
+
+static void
+xml_reader_class_init (XmlReaderClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = xml_reader_finalize;
+  object_class->get_property = xml_reader_get_property;
+  object_class->set_property = xml_reader_set_property;
+
+  properties [PROP_ENCODING] =
+    g_param_spec_string ("encoding",
+                         "Encoding",
+                         "Encoding",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_URI] =
+    g_param_spec_string ("uri",
+                         "URI",
+                         "URI",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, LAST_PROP, properties);
+
+  signals [ERROR] =
+    g_signal_new ("error",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  0,
+                  NULL, NULL, NULL,
+                  G_TYPE_NONE,
+                  1,
+                  G_TYPE_STRING);
+}
+
+static void
+xml_reader_init (XmlReader *reader)
+{
+}
+
+XmlReader*
+xml_reader_new (void)
+{
+  return g_object_new (XML_TYPE_READER, NULL);
+}
+
+static void
+xml_reader_error_cb (void                    *arg,
+                     const char              *msg,
+                     xmlParserSeverities      severity,
+                     xmlTextReaderLocatorPtr  locator)
+{
+  XmlReader *reader = arg;
+
+  g_assert (XML_IS_READER (reader));
+
+  g_signal_emit (reader, signals [ERROR], 0, msg);
+}
+
+gboolean
+xml_reader_load_from_path (XmlReader   *reader,
+                           const gchar *path)
+{
+  g_return_val_if_fail (XML_IS_READER (reader), FALSE);
+
+  xml_reader_clear (reader);
+
+  if ((reader->xml = xmlNewTextReaderFilename (path)))
+    xmlTextReaderSetErrorHandler (reader->xml, xml_reader_error_cb, reader);
+
+  return (reader->xml != NULL);
+}
+
+gboolean
+xml_reader_load_from_file (XmlReader     *reader,
+                           GFile         *file,
+                           GCancellable  *cancellable,
+                           GError       **error)
+{
+  GFileInputStream *stream;
+  gboolean ret;
+
+  g_return_val_if_fail (XML_IS_READER (reader), FALSE);
+  g_return_val_if_fail (G_IS_FILE (file), FALSE);
+  g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE);
+
+  if (!(stream = g_file_read (file, cancellable, error)))
+    return FALSE;
+
+  ret = xml_reader_load_from_stream (reader, G_INPUT_STREAM (stream), error);
+
+  g_clear_object (&stream);
+
+  return ret;
+}
+
+gboolean
+xml_reader_load_from_data (XmlReader   *reader,
+                           const gchar *data,
+                           gsize        length,
+                           const gchar *uri,
+                           const gchar *encoding)
+{
+  g_return_val_if_fail (XML_IS_READER (reader), FALSE);
+
+  xml_reader_clear (reader);
+
+  if (length == -1)
+    length = strlen (data);
+
+  reader->xml = xmlReaderForMemory (data, length, uri, encoding, 0);
+  xmlTextReaderSetErrorHandler (reader->xml, xml_reader_error_cb, reader);
+
+  return (reader->xml != NULL);
+}
+
+static int
+xml_reader_io_read_cb (void *context,
+                       char *buffer,
+                       int   len)
+{
+  GInputStream *stream = (GInputStream *)context;
+  g_return_val_if_fail (G_IS_INPUT_STREAM(stream), -1);
+  return g_input_stream_read (stream, buffer, len, NULL, NULL);
+}
+
+static int
+xml_reader_io_close_cb (void *context)
+{
+  GInputStream *stream = (GInputStream *)context;
+
+  g_return_val_if_fail (G_IS_INPUT_STREAM(stream), -1);
+
+  return g_input_stream_close (stream, NULL, NULL) ? 0 : -1;
+}
+
+gboolean
+xml_reader_load_from_stream (XmlReader     *reader,
+                             GInputStream  *stream,
+                             GError       **error)
+{
+  g_return_val_if_fail (XML_IS_READER(reader), FALSE);
+
+  xml_reader_clear (reader);
+
+  reader->xml = xmlReaderForIO (xml_reader_io_read_cb,
+                                xml_reader_io_close_cb,
+                                stream,
+                                reader->uri,
+                                reader->encoding,
+                                XML_PARSE_RECOVER | XML_PARSE_NOBLANKS | XML_PARSE_COMPACT);
+
+  if (!reader->xml)
+    {
+      g_set_error (error,
+                   XML_READER_ERROR,
+                   XML_READER_ERROR_INVALID,
+                   _("Could not parse XML from stream"));
+      return FALSE;
+    }
+
+   reader->stream = g_object_ref (stream);
+
+   xmlTextReaderSetErrorHandler (reader->xml, xml_reader_error_cb, reader);
+
+   return TRUE;
+}
+
+G_CONST_RETURN gchar *
+xml_reader_get_value (XmlReader *reader)
+{
+  g_return_val_if_fail (XML_IS_READER (reader), NULL);
+
+  g_return_val_if_fail (reader->xml != NULL, NULL);
+
+  return XML_TO_CHAR (xmlTextReaderConstValue (reader->xml));
+}
+
+G_CONST_RETURN gchar *
+xml_reader_get_name (XmlReader *reader)
+{
+  g_return_val_if_fail (XML_IS_READER (reader), NULL);
+  g_return_val_if_fail (reader->xml != NULL, NULL);
+
+  return XML_TO_CHAR (xmlTextReaderConstName (reader->xml));
+}
+
+gchar *
+xml_reader_read_string (XmlReader *reader)
+{
+  g_return_val_if_fail (XML_IS_READER (reader), NULL);
+  g_return_val_if_fail (reader->xml != NULL, NULL);
+
+  RETURN_STRDUP_AND_XMLFREE (xmlTextReaderReadString (reader->xml));
+}
+
+gchar *
+xml_reader_get_attribute (XmlReader   *reader,
+                          const gchar *name)
+{
+  g_return_val_if_fail (XML_IS_READER (reader), NULL);
+  g_return_val_if_fail (reader->xml != NULL, NULL);
+
+  RETURN_STRDUP_AND_XMLFREE (xmlTextReaderGetAttribute (reader->xml, CHAR_TO_XML (name)));
+}
+
+static gboolean
+read_to_type_and_name (XmlReader   *reader,
+                       gint         type,
+                       const gchar *name)
+{
+  gboolean success = FALSE;
+
+  g_return_val_if_fail (XML_IS_READER (reader), FALSE);
+
+  g_return_val_if_fail (reader->xml != NULL, FALSE);
+
+  while (xmlTextReaderRead (reader->xml) == 1)
+    {
+      if (xmlTextReaderNodeType (reader->xml) == type)
+        {
+          if (g_strcmp0 (XML_TO_CHAR (xmlTextReaderConstName (reader->xml)), name) == 0)
+            {
+              success = TRUE;
+              break;
+            }
+        }
+    }
+
+  return success;
+}
+
+gboolean
+xml_reader_read_start_element (XmlReader   *reader,
+                               const gchar *name)
+{
+  g_return_val_if_fail (XML_IS_READER (reader), FALSE);
+
+  if (read_to_type_and_name (reader, XML_NODE_TYPE_ELEMENT, name))
+    {
+      g_free (reader->cur_name);
+      reader->cur_name = g_strdup (name);
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+gboolean
+xml_reader_read_end_element (XmlReader *reader)
+{
+  gboolean success = FALSE;
+
+  g_return_val_if_fail (XML_IS_READER (reader), FALSE);
+
+  if (reader->cur_name)
+    success = read_to_type_and_name (reader, XML_NODE_TYPE_END_ELEMENT, reader->cur_name);
+
+  return success;
+}
+
+gchar *
+xml_reader_read_inner_xml (XmlReader *reader)
+{
+  g_return_val_if_fail (XML_IS_READER (reader), FALSE);
+
+  RETURN_STRDUP_AND_XMLFREE (xmlTextReaderReadInnerXml (reader->xml));
+}
+
+gchar*
+xml_reader_read_outer_xml (XmlReader *reader)
+{
+  g_return_val_if_fail (XML_IS_READER (reader), FALSE);
+
+  RETURN_STRDUP_AND_XMLFREE (xmlTextReaderReadOuterXml (reader->xml));
+}
+
+gboolean
+xml_reader_read_to_next (XmlReader *reader)
+{
+  g_return_val_if_fail (XML_IS_READER (reader), FALSE);
+
+  return (xmlTextReaderNext (reader->xml) == 1);
+}
+
+gboolean
+xml_reader_read (XmlReader *reader)
+{
+  g_return_val_if_fail (XML_IS_READER (reader), FALSE);
+
+  return (xmlTextReaderRead (reader->xml) == 1);
+}
+
+gboolean
+xml_reader_read_to_next_sibling (XmlReader *reader)
+{
+  g_return_val_if_fail (XML_IS_READER (reader), FALSE);
+
+  xmlTextReaderMoveToElement (reader->xml);
+
+  return (xmlTextReaderNextSibling (reader->xml) == 1);
+}
+
+gint
+xml_reader_get_depth (XmlReader *reader)
+{
+  g_return_val_if_fail (XML_IS_READER(reader), -1);
+
+  return xmlTextReaderDepth (reader->xml);
+}
+
+void
+xml_reader_move_up_to_depth (XmlReader *reader,
+                             gint       depth)
+{
+  g_return_if_fail(XML_IS_READER(reader));
+
+  while (xml_reader_get_depth(reader) > depth)
+    xml_reader_read_end_element(reader);
+}
+
+xmlReaderTypes
+xml_reader_get_node_type (XmlReader *reader)
+{
+  g_return_val_if_fail (XML_IS_READER (reader), 0);
+
+  return xmlTextReaderNodeType (reader->xml);
+}
+
+gboolean
+xml_reader_is_empty_element (XmlReader *reader)
+{
+  g_return_val_if_fail (XML_IS_READER (reader), FALSE);
+
+  return xmlTextReaderIsEmptyElement (reader->xml);
+}
+
+gboolean
+xml_reader_is_a (XmlReader   *reader,
+                 const gchar *name)
+{
+   return (g_strcmp0 (xml_reader_get_name (reader), name) == 0);
+}
+
+gboolean
+xml_reader_is_a_local (XmlReader   *reader,
+                       const gchar *local_name)
+{
+   return (g_strcmp0 (xml_reader_get_local_name (reader), local_name) == 0);
+}
+
+gboolean
+xml_reader_is_namespace (XmlReader   *reader,
+                         const gchar *ns)
+{
+   g_return_val_if_fail (XML_IS_READER (reader), FALSE);
+
+   return (g_strcmp0 (XML_TO_CHAR(xmlTextReaderConstNamespaceUri (reader->xml)), ns) == 0);
+}
+
+G_CONST_RETURN gchar *
+xml_reader_get_local_name (XmlReader *reader)
+{
+   g_return_val_if_fail(XML_IS_READER (reader), NULL);
+
+   return XML_TO_CHAR (xmlTextReaderConstLocalName (reader->xml));
+}
+
+gboolean
+xml_reader_move_to_element (XmlReader *reader)
+{
+  g_return_val_if_fail (XML_IS_READER (reader), FALSE);
+
+  return (xmlTextReaderMoveToElement (reader->xml) == 1);
+}
+
+gboolean
+xml_reader_move_to_attribute (XmlReader   *reader,
+                              const gchar *name)
+{
+  g_return_val_if_fail (XML_IS_READER (reader), FALSE);
+
+  return (xmlTextReaderMoveToAttribute (reader->xml, CHAR_TO_XML (name)) == 1);
+}
+
+gboolean
+xml_reader_move_to_first_attribute (XmlReader *reader)
+{
+  g_return_val_if_fail (XML_IS_READER (reader), FALSE);
+
+  return (xmlTextReaderMoveToFirstAttribute (reader->xml) == 1);
+}
+
+gboolean
+xml_reader_move_to_next_attribute (XmlReader *reader)
+{
+  g_return_val_if_fail (XML_IS_READER (reader), FALSE);
+
+  return (xmlTextReaderMoveToNextAttribute (reader->xml) == 1);
+}
+
+gboolean
+xml_reader_move_to_nth_attribute (XmlReader *reader,
+                                  gint       nth)
+{
+  g_return_val_if_fail (XML_IS_READER (reader), FALSE);
+
+  return (xmlTextReaderMoveToAttributeNo (reader->xml, nth) == 1);
+}
+
+gint
+xml_reader_count_attributes (XmlReader *reader)
+{
+  g_return_val_if_fail (XML_IS_READER (reader), FALSE);
+
+  return xmlTextReaderAttributeCount (reader->xml);
+}
+
+gint
+xml_reader_get_line_number (XmlReader *reader)
+{
+  g_return_val_if_fail(XML_IS_READER(reader), -1);
+
+  if (reader->xml)
+    return xmlTextReaderGetParserLineNumber(reader->xml);
+
+  return -1;
+}
diff --git a/contrib/xml/xml-reader.h b/contrib/xml/xml-reader.h
new file mode 100644
index 0000000..cfdfa53
--- /dev/null
+++ b/contrib/xml/xml-reader.h
@@ -0,0 +1,100 @@
+/* xml-reader.h
+ *
+ * Copyright (C) 2009 Christian Hergert  <chris dronelabs com>
+ *
+ * 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 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.
+ *
+ * Author:
+ *   Christian Hergert <chris dronelabs com>
+ *
+ * Based upon work by:
+ *   Emmanuele Bassi
+ */
+
+#ifndef XML_READER_H
+#define XML_READER_H
+
+#include <gio/gio.h>
+#include <libxml/xmlreader.h>
+
+G_BEGIN_DECLS
+
+#define XML_TYPE_READER (xml_reader_get_type ())
+
+#define XML_READER_ERROR (xml_reader_error_quark())
+
+G_DECLARE_FINAL_TYPE (XmlReader, xml_reader, XML, READER, GObject)
+
+typedef enum
+{
+   XML_READER_ERROR_INVALID,
+} XmlReaderError;
+
+GQuark                xml_reader_error_quark             (void);
+XmlReader            *xml_reader_new                     (void);
+gboolean              xml_reader_load_from_path          (XmlReader     *reader,
+                                                          const gchar   *path);
+gboolean              xml_reader_load_from_file          (XmlReader     *reader,
+                                                          GFile         *file,
+                                                          GCancellable  *cancellable,
+                                                          GError       **error);
+gboolean              xml_reader_load_from_data          (XmlReader     *reader,
+                                                          const gchar   *data,
+                                                          gsize          length,
+                                                          const gchar   *uri,
+                                                          const gchar   *encoding);
+gboolean              xml_reader_load_from_stream        (XmlReader     *reader,
+                                                          GInputStream  *stream,
+                                                          GError       **error);
+
+gint                  xml_reader_get_depth               (XmlReader   *reader);
+xmlReaderTypes        xml_reader_get_node_type           (XmlReader   *reader);
+const gchar          *xml_reader_get_value               (XmlReader   *reader);
+const gchar          *xml_reader_get_name                (XmlReader   *reader);
+const gchar          *xml_reader_get_local_name          (XmlReader   *reader);
+gchar                *xml_reader_read_string             (XmlReader   *reader);
+gchar                *xml_reader_get_attribute           (XmlReader   *reader,
+                                                          const gchar *name);
+gboolean              xml_reader_is_a                    (XmlReader   *reader,
+                                                          const gchar *name);
+gboolean              xml_reader_is_a_local              (XmlReader   *reader,
+                                                          const gchar *local_name);
+gboolean              xml_reader_is_namespace            (XmlReader   *reader,
+                                                          const gchar *ns);
+gboolean              xml_reader_is_empty_element        (XmlReader   *reader);
+
+gboolean              xml_reader_read_start_element      (XmlReader   *reader,
+                                                          const gchar *name);
+gboolean              xml_reader_read_end_element        (XmlReader   *reader);
+
+gchar                *xml_reader_read_inner_xml          (XmlReader   *reader);
+gchar                *xml_reader_read_outer_xml          (XmlReader   *reader);
+
+gboolean              xml_reader_read                    (XmlReader   *reader);
+gboolean              xml_reader_read_to_next            (XmlReader   *reader);
+gboolean              xml_reader_read_to_next_sibling    (XmlReader   *reader);
+
+gboolean              xml_reader_move_to_element         (XmlReader   *reader);
+gboolean              xml_reader_move_to_attribute       (XmlReader   *reader,
+                                                          const gchar *name);
+void                  xml_reader_move_up_to_depth        (XmlReader   *reader,
+                                                          gint         depth);
+
+gboolean              xml_reader_move_to_first_attribute (XmlReader   *reader);
+gboolean              xml_reader_move_to_next_attribute  (XmlReader   *reader);
+gint                  xml_reader_count_attributes        (XmlReader   *reader);
+gboolean              xml_reader_move_to_nth_attribute   (XmlReader   *reader,
+                                                          gint         nth);
+gint                  xml_reader_get_line_number         (XmlReader   *reader);
+
+G_END_DECLS
+
+#endif /* XML_READER_H */
diff --git a/gitg/resources/ui/gitg-dash-view.ui b/gitg/resources/ui/gitg-dash-view.ui
index f74b43e..a1959f4 100644
--- a/gitg/resources/ui/gitg-dash-view.ui
+++ b/gitg/resources/ui/gitg-dash-view.ui
@@ -11,9 +11,6 @@
         <property name="halign">center</property>
         <property name="hexpand">True</property>
         <property name="can_focus">False</property>
-        <style>
-          <class name="dim-label"/>
-        </style>
         <child>
           <object class="GtkImage" id="gitg_icon">
             <property name="visible">True</property>
@@ -104,16 +101,22 @@
         <property name="visible">False</property>
         <property name="vexpand">True</property>
         <property name="hexpand">True</property>
-        <style>
-          <class name="view"/>
-        </style>
         <child>
-          <object class="GitgRepositoryListBox" id="repository_list_box">
-            <property name="visible">True</property>
-            <property name="can_focus">True</property>
-            <style>
-              <class name="view"/>
-            </style>
+          <object class="GtkFrame" id="repository_list_frame">
+            <property name="halign">center</property>
+            <property name="visible">true</property>
+            <property name="margin-bottom">32</property>
+            <property name="margin-top">32</property>
+            <property name="width-request">550</property>
+            <child>
+              <object class="GitgRepositoryListBox" id="repository_list_box">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <style>
+                  <class name="view"/>
+                </style>
+              </object>
+            </child>
           </object>
         </child>
       </object>
diff --git a/libgitg/Makefile.am b/libgitg/Makefile.am
index 7456787..8262f0e 100644
--- a/libgitg/Makefile.am
+++ b/libgitg/Makefile.am
@@ -4,6 +4,7 @@ libgitgexec_LTLIBRARIES = libgitg/libgitg-1.0.la
 libgitg_libgitg_1_0_la_CPPFLAGS =                      \
        -I$(top_srcdir)                                 \
        -I$(srcdir)                                     \
+       -I$(top_srcdir)/contrib/ide                     \
        -DDATADIR=\""$(datadir)"\"                      \
        -DLIBDIR=\""$(libdir)"\"                        \
        -DGETTEXT_PACKAGE=\"$(GETTEXT_PACKAGE)\"
@@ -19,7 +20,8 @@ libgitg_libgitg_1_0_la_LDFLAGS =      \
        -export-symbols-regex "^[^_].*"
 
 libgitg_libgitg_1_0_la_LIBADD =        \
-       $(LIBGITG_LIBS)
+       $(LIBGITG_LIBS)                 \
+       contrib/ide/libide.la
 
 if GDK_WINDOWING_QUARTZ
 libgitg_libgitg_1_0_la_LIBADD += -lobjc
@@ -38,8 +40,10 @@ libgitg_libgitg_1_0_la_VALAFLAGS =   \
        --pkg libsoup-2.4               \
        --pkg gtksourceview-3.0         \
        --pkg gitg-platform-support     \
+       --pkg ide                       \
        $(GITG_VALAFLAGS)               \
        --vapidir $(top_srcdir)/vapi    \
+       --vapidir $(top_srcdir)/contrib/ide \
        --includedir libgitg            \
        --basedir $(top_srcdir)         \
        --gir Gitg-1.0.gir              \
diff --git a/libgitg/gitg-repository-list-box.vala b/libgitg/gitg-repository-list-box.vala
index 30b0fdc..d37dc01 100644
--- a/libgitg/gitg-repository-list-box.vala
+++ b/libgitg/gitg-repository-list-box.vala
@@ -1,7 +1,7 @@
 /*
  * This file is part of gitg
  *
- * Copyright (C) 2012 - Ignacio Casal Quinteiro
+ * Copyright (C) 2012-2016 - Ignacio Casal Quinteiro
  *
  * gitg is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -38,17 +38,14 @@ namespace Gitg
                        private Repository? d_repository;
                        private DateTime d_time;
                        private bool d_loading;
-                       private bool d_has_remote;
                        [GtkChild]
                        private ProgressBin d_progress_bin;
                        [GtkChild]
-                       private Gtk.Image d_image;
-                       [GtkChild]
                        private Gtk.Label d_repository_label;
                        [GtkChild]
-                       private Gtk.Label d_branch_label;
+                       private Gtk.Label d_description_label;
                        [GtkChild]
-                       private Gtk.Arrow d_arrow;
+                       private Gtk.Label d_branch_label;
                        [GtkChild]
                        private Gtk.Spinner d_spinner;
                        [GtkChild]
@@ -56,7 +53,7 @@ namespace Gitg
                        [GtkChild]
                        private Gtk.Revealer d_remove_revealer;
                        [GtkChild]
-                       private Gtk.Box d_submodule_box;
+                       private Gtk.Box d_languages_box;
 
                        public signal void request_remove();
 
@@ -64,13 +61,6 @@ namespace Gitg
                        private string? d_dirname;
                        private string? d_branch_name;
 
-                       private static Gtk.IconSize s_icon_size;
-
-                       static construct
-                       {
-                               s_icon_size = Gtk.icon_size_register("gitg", 64, 64);
-                       }
-
                        public SelectionMode mode
                        {
                                get { return d_mode; }
@@ -108,18 +98,7 @@ namespace Gitg
                                set
                                {
                                        d_repository = value;
-
-                                       branch_name = "";
-
-                                       if (d_repository != null)
-                                       {
-                                               try
-                                               {
-                                                       var head = d_repository.get_head();
-                                                       branch_name = head.parsed_name.shortname;
-                                               }
-                                               catch {}
-                                       }
+                                       update_repository_data();
                                }
                        }
 
@@ -185,6 +164,64 @@ namespace Gitg
                                }
                        }
 
+                       private void update_repository_data()
+                       {
+                               string head_name = "";
+                               string head_description = "";
+
+                               if (d_repository != null)
+                               {
+                                       try
+                                       {
+                                               var head = d_repository.get_head();
+                                               head_name = head.parsed_name.shortname;
+
+                                               var commit = (Ggit.Commit)head.lookup();
+                                               var tree = commit.get_tree();
+
+                                               Ggit.OId? entry_id = null;
+                                               tree.walk(Ggit.TreeWalkMode.PRE, (root, entry) => {
+                                                       if (root == "" && entry.get_name() != null && 
entry.get_name().has_suffix(".doap"))
+                                                       {
+                                                               entry_id = entry.get_id();
+                                                               return 1;
+                                                       }
+                                                       return 0;
+                                               });
+
+                                               if (entry_id != null)
+                                               {
+                                                       var blob = d_repository.lookup<Ggit.Blob>(entry_id);
+
+                                                       unowned uint8[] content = blob.get_raw_content();
+                                                       var doap = new Ide.Doap();
+                                                       doap.load_from_data((string)content, -1);
+
+                                                       head_description = doap.get_shortdesc();
+
+                                                       foreach (var lang in doap.get_languages())
+                                                       {
+                                                               var frame = new Gtk.Frame(null);
+                                                               frame.shadow_type = Gtk.ShadowType.NONE;
+                                                               
frame.get_style_context().add_class("language-frame");
+                                                               frame.show();
+
+                                                               var label = new Gtk.Label(lang);
+                                                               
label.get_attributes().insert_before(Pango.attr_scale_new(Pango.Scale.SMALL));
+                                                               label.show();
+
+                                                               frame.add(label);
+                                                               d_languages_box.add(frame);
+                                                       }
+                                               }
+                                       } catch {}
+                               }
+
+                               repository_name = d_repository != null ? d_repository.name : "";
+                               d_description_label.label = head_description;
+                               branch_name = head_name;
+                       }
+
                        public bool loading
                        {
                                get { return d_loading; }
@@ -196,77 +233,19 @@ namespace Gitg
                                        {
                                                d_spinner.stop();
                                                d_spinner.hide();
-                                               d_arrow.show();
                                                d_progress_bin.fraction = 0;
                                        }
                                        else
                                        {
-                                               d_arrow.hide();
                                                d_spinner.show();
                                                d_spinner.start();
                                        }
                                }
                        }
 
-                       public bool has_remote
+                       public Row(Repository? repository, string dirname)
                        {
-                               get { return d_has_remote; }
-                               set
-                               {
-                                       d_has_remote = value;
-
-                                       var folder_icon_name = d_has_remote ? "folder-remote" : "folder";
-                                       d_image.set_from_icon_name(folder_icon_name, s_icon_size);
-                               }
-                       }
-
-                       public Row(string name, string dirname, string branch_name, bool has_remote)
-                       {
-                               Object(repository_name: name, dirname: dirname, branch_name: branch_name, 
has_remote: has_remote);
-                       }
-
-                       public void add_submodule(Ggit.Submodule module)
-                       {
-                               var submodule_url = module.get_url();
-                               if (submodule_url == null)
-                               {
-                                       return;
-                               }
-
-                               var box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 3);
-                               var tip = @"$(module.get_path())/ ($(submodule_url))";
-
-                               box.set_tooltip_text(tip);
-                               box.show();
-
-                               var icon = new Gtk.Image.from_icon_name("folder-remote-symbolic",
-                                                                       Gtk.IconSize.MENU);
-                               icon.show();
-
-                               var name = Path.get_basename(submodule_url);
-
-                               if (name.has_suffix(".git"))
-                               {
-                                       name = name[0:-4];
-                               }
-
-                               var labelName = new Gtk.Label(name);
-                               labelName.show();
-
-                               var arrow = new Gtk.Arrow(Gtk.ArrowType.RIGHT, Gtk.ShadowType.NONE);
-                               arrow.show();
-
-                               var path = module.get_path();
-                               var labelPath = new Gtk.Label(@"$path/");
-                               labelPath.set_ellipsize(Pango.EllipsizeMode.MIDDLE);
-                               labelPath.show();
-
-                               box.add(icon);
-                               box.add(labelName);
-                               box.add(arrow);
-                               box.add(labelPath);
-
-                               d_submodule_box.add(box);
+                               Object(repository: repository, dirname: dirname);
                        }
                }
 
@@ -423,7 +402,9 @@ namespace Gitg
 
                public Row? begin_cloning(File location)
                {
-                       var row = new Row(location.get_basename(), 
Utils.replace_home_dir_with_tilde(location.get_parent()), _("Cloning…"), true);
+                       var row = new Row(null, Utils.replace_home_dir_with_tilde(location.get_parent()));
+                       row.repository_name = location.get_basename();
+                       row.branch_name = _("Cloning…");
 
                        row.loading = true;
                        row.show();
@@ -440,41 +421,13 @@ namespace Gitg
 
                        if (row == null)
                        {
-                               string head_name = "";
-                               bool has_remote = true;
-
-                               try
-                               {
-                                       var head = repository.get_head();
-                                       head_name = head.parsed_name.shortname;
-
-                                       var remotes = repository.list_remotes();
-
-                                       if (remotes.length == 0)
-                                       {
-                                               has_remote = false;
-                                       }
-                               } catch {}
-
                                var dirname = Utils.replace_home_dir_with_tilde((repository.workdir != null ? 
repository.workdir : repository.location).get_parent());
-                               row = new Row(repository.name, dirname, head_name, has_remote);
-                               row.repository = repository;
+                               row = new Row(repository, dirname);
                                row.show();
 
-                               try
-                               {
-                                       repository.submodule_foreach((module) => {
-                                               row.add_submodule(module);
-                                               return 0;
-                                       });
-                               }
-                               catch {}
-
                                if (f != null)
                                {
-                                       bind_property("mode",
-                                                     row,
-                                                     "mode");
+                                       bind_property("mode", row, "mode");
                                }
 
                                if (f != null)
diff --git a/libgitg/resources/ui/gitg-repository-list-box-row.ui 
b/libgitg/resources/ui/gitg-repository-list-box-row.ui
index 635194f..aeb19c9 100644
--- a/libgitg/resources/ui/gitg-repository-list-box-row.ui
+++ b/libgitg/resources/ui/gitg-repository-list-box-row.ui
@@ -45,22 +45,29 @@
               </packing>
             </child>
             <child>
-              <object class="GtkImage" id="d_image">
+              <object class="GtkLabel" id="d_repository_label">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
                 <property name="has_focus">False</property>
                 <property name="is_focus">False</property>
-                <property name="icon_name">image-missing</property>
+                <property name="halign">start</property>
+                <property name="valign">end</property>
+                <property name="hexpand">True</property>
+                <property name="ellipsize">end</property>
+                <attributes>
+                  <attribute name="scale" value="1.2"/>
+                  <attribute name="weight" value="bold"/>
+                </attributes>
               </object>
               <packing>
                 <property name="left_attach">1</property>
                 <property name="top_attach">0</property>
                 <property name="width">1</property>
-                <property name="height">3</property>
+                <property name="height">1</property>
               </packing>
             </child>
             <child>
-              <object class="GtkLabel" id="d_repository_label">
+              <object class="GtkLabel" id="d_description_label">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
                 <property name="has_focus">False</property>
@@ -69,13 +76,10 @@
                 <property name="valign">end</property>
                 <property name="hexpand">True</property>
                 <property name="ellipsize">end</property>
-                <attributes>
-                  <attribute name="weight" value="bold"/>
-                </attributes>
               </object>
               <packing>
-                <property name="left_attach">2</property>
-                <property name="top_attach">0</property>
+                <property name="left_attach">1</property>
+                <property name="top_attach">1</property>
                 <property name="width">1</property>
                 <property name="height">1</property>
               </packing>
@@ -97,14 +101,14 @@
                 </style>
               </object>
               <packing>
-                <property name="left_attach">2</property>
-                <property name="top_attach">1</property>
+                <property name="left_attach">1</property>
+                <property name="top_attach">2</property>
                 <property name="width">1</property>
                 <property name="height">1</property>
               </packing>
             </child>
             <child>
-              <object class="GtkBox" id="d_submodule_box">
+              <object class="GtkBox" id="d_languages_box">
                 <property name="orientation">horizontal</property>
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
@@ -112,7 +116,7 @@
                 <property name="is_focus">False</property>
                 <property name="halign">start</property>
                 <property name="valign">start</property>
-                <property name="spacing">24</property>
+                <property name="spacing">3</property>
               </object>
               <packing>
                 <property name="left_attach">2</property>
@@ -122,28 +126,13 @@
               </packing>
             </child>
             <child>
-              <object class="GtkArrow" id="d_arrow">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="has_focus">False</property>
-                <property name="is_focus">False</property>
-                <property name="shadow_type">none</property>
-              </object>
-              <packing>
-                <property name="left_attach">3</property>
-                <property name="top_attach">0</property>
-                <property name="width">1</property>
-                <property name="height">3</property>
-              </packing>
-            </child>
-            <child>
               <object class="GtkSpinner" id="d_spinner">
                 <property name="can_focus">False</property>
                 <property name="has_focus">False</property>
                 <property name="is_focus">False</property>
               </object>
               <packing>
-                <property name="left_attach">4</property>
+                <property name="left_attach">3</property>
                 <property name="top_attach">0</property>
                 <property name="width">1</property>
                 <property name="height">3</property>
diff --git a/libgitg/resources/ui/libgitg-style.css b/libgitg/resources/ui/libgitg-style.css
index 19628e6..3e2c325 100644
--- a/libgitg/resources/ui/libgitg-style.css
+++ b/libgitg/resources/ui/libgitg-style.css
@@ -102,3 +102,9 @@ GitgDiffViewOptions GtkButton {
 GitgDiffViewOptions {
        border-top: 1px solid @borders;
 }
+
+.language-frame {
+       background-color: #e1e1e1;
+       border-radius: 3px;
+       padding: 3px;
+}


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