[gitg/vala] Initial porting to vala



commit a74c5661a672712ddb96bd70d51fadea23dda933
Author: Jesse van den Kieboom <jesse vandenkieboom epfl ch>
Date:   Sat Apr 14 13:46:39 2012 +0200

    Initial porting to vala

 .gitmodules                                    |    6 +
 Makefile.am                                    |    6 +-
 configure.ac                                   |  172 +-
 data/gitg-glade.xml                            |    7 -
 gitg/Makefile.am                               |  116 +-
 gitg/gitg-activatable.c                        |   70 -
 gitg/gitg-activatable.h                        |   55 -
 gitg/gitg-application.vala                     |  343 +++
 gitg/gitg-avatar-cache.c                       |  416 ---
 gitg/gitg-avatar-cache.h                       |   73 -
 gitg/gitg-blame-renderer.c                     |  454 ---
 gitg/gitg-blame-renderer.h                     |   61 -
 gitg/gitg-branch-actions.c                     | 2108 --------------
 gitg/gitg-branch-actions.h                     |   53 -
 gitg/gitg-cell-renderer-path.c                 |  639 -----
 gitg/gitg-cell-renderer-path.h                 |   69 -
 gitg/gitg-commit-menu.ui                       |   54 -
 gitg/gitg-commit-view.c                        | 3216 ---------------------
 gitg/gitg-commit-view.h                        |   61 -
 gitg/gitg-diff-line-renderer.c                 |  379 ---
 gitg/gitg-diff-line-renderer.h                 |   60 -
 gitg/gitg-diff-view.c                          | 1633 -----------
 gitg/gitg-diff-view.h                          |  106 -
 gitg/gitg-dirs.c                               |   78 -
 gitg/gitg-dirs.h                               |   32 -
 gitg/gitg-dirs.vala                            |   64 +
 gitg/gitg-dnd.c                                | 1090 -------
 gitg/gitg-dnd.h                                |   45 -
 gitg/gitg-label-renderer.c                     |  349 ---
 gitg/gitg-label-renderer.h                     |   52 -
 gitg/gitg-menus.xml                            |  168 --
 gitg/gitg-new-branch.ui                        |  106 -
 gitg/gitg-plugins-engine.vala                  |   67 +
 gitg/gitg-preferences-dialog.c                 |  431 ---
 gitg/gitg-preferences-dialog.h                 |   57 -
 gitg/gitg-preferences.ui                       |  810 ------
 gitg/gitg-repository-dialog.c                  |  906 ------
 gitg/gitg-repository-dialog.h                  |   62 -
 gitg/gitg-repository.ui                        |  320 ---
 gitg/gitg-resource.vala                        |   62 +
 gitg/gitg-revision-changes-panel.c             | 1048 -------
 gitg/gitg-revision-changes-panel.h             |   34 -
 gitg/gitg-revision-changes-panel.ui            |   79 -
 gitg/gitg-revision-details-panel.c             |  936 -------
 gitg/gitg-revision-details-panel.h             |   58 -
 gitg/gitg-revision-details-panel.ui            |  270 --
 gitg/gitg-revision-files-panel.c               | 1524 ----------
 gitg/gitg-revision-files-panel.h               |   61 -
 gitg/gitg-revision-files-panel.ui              |   96 -
 gitg/gitg-revision-panel.c                     |  124 -
 gitg/gitg-revision-panel.h                     |   70 -
 gitg/gitg-stat-view.c                          |  500 ----
 gitg/gitg-stat-view.h                          |   37 -
 gitg/gitg-tag.ui                               |  158 --
 gitg/gitg-ui.xml                               |  136 -
 gitg/gitg-uri.c                                |  103 -
 gitg/gitg-uri.h                                |   17 -
 gitg/gitg-utils.c                              |  466 ---
 gitg/gitg-utils.h                              |   61 -
 gitg/gitg-window.c                             | 3584 ------------------------
 gitg/gitg-window.h                             |   87 -
 gitg/gitg-window.ui                            |  576 ----
 gitg/gitg-window.vala                          |  457 +++
 gitg/gitg.c                                    |  207 --
 gitg/gitg.vala                                 |   25 +
 gitg/resources/gitg-resources.xml              |   10 +
 gitg/resources/ui/gitg-menus.ui                |   33 +
 gitg/resources/ui/gitg-window.ui               |  138 +
 gitg/resources/ui/style.css                    |   56 +
 libgit2                                        |    1 +
 libgit2-glib                                   |    1 +
 libgitg-ext/GitgExt.py                         |   38 +
 libgitg-ext/Makefile.am                        |  100 +
 libgitg-ext/gitg-ext-application.vala          |   19 +
 libgitg-ext/gitg-ext-message-bus.vala          |  294 ++
 libgitg-ext/gitg-ext-message-id.vala           |   75 +
 libgitg-ext/gitg-ext-message.vala              |   40 +
 libgitg-ext/gitg-ext-navigation-tree-view.vala |  476 ++++
 libgitg-ext/gitg-ext-navigation.vala           |   22 +
 libgitg-ext/gitg-ext-panel.vala                |   18 +
 libgitg-ext/gitg-ext-view.vala                 |  112 +
 libgitg-ext/libgitg-ext-1.0.pc.in              |   11 +
 libgitg-ext/resources/resources.xml            |    7 +
 libgitg/Makefile.am                            |  131 +-
 libgitg/gitg-changed-file.c                    |  343 ---
 libgitg/gitg-changed-file.h                    |   90 -
 libgitg/gitg-color.c                           |  139 -
 libgitg/gitg-color.h                           |   51 -
 libgitg/gitg-color.vala                        |   85 +
 libgitg/gitg-command.c                         |  513 ----
 libgitg/gitg-command.h                         |   98 -
 libgitg/gitg-commit-model.vala                 |  246 ++
 libgitg/gitg-commit.c                          | 1484 ----------
 libgitg/gitg-commit.h                          |  108 -
 libgitg/gitg-commit.vala                       |   90 +
 libgitg/gitg-config.c                          |  529 ----
 libgitg/gitg-config.h                          |   75 -
 libgitg/gitg-convert.c                         |  118 -
 libgitg/gitg-convert.h                         |   34 -
 libgitg/gitg-debug.c                           |   74 -
 libgitg/gitg-debug.h                           |   59 -
 libgitg/gitg-encodings.c                       |  570 ----
 libgitg/gitg-encodings.h                       |   72 -
 libgitg/gitg-enum-types.c.template             |   40 -
 libgitg/gitg-enum-types.h.template             |   28 -
 libgitg/gitg-hash.c                            |  147 -
 libgitg/gitg-hash.h                            |   50 -
 libgitg/gitg-i18n.c                            |   52 -
 libgitg/gitg-i18n.h                            |   71 -
 libgitg/gitg-init.vala                         |   20 +
 libgitg/gitg-io.c                              |  507 ----
 libgitg/gitg-io.h                              |  106 -
 libgitg/gitg-lane.c                            |   86 -
 libgitg/gitg-lane.h                            |   69 -
 libgitg/gitg-lane.vala                         |   63 +
 libgitg/gitg-lanes.c                           |  750 -----
 libgitg/gitg-lanes.h                           |   64 -
 libgitg/gitg-lanes.vala                        |  466 +++
 libgitg/gitg-line-parser.c                     |  479 ----
 libgitg/gitg-line-parser.h                     |   67 -
 libgitg/gitg-ref.c                             |  248 --
 libgitg/gitg-ref.h                             |   86 -
 libgitg/gitg-ref.vala                          |  275 ++
 libgitg/gitg-repository.c                      | 2081 --------------
 libgitg/gitg-repository.h                      |  113 -
 libgitg/gitg-repository.vala                   |   38 +
 libgitg/gitg-revision.c                        |  413 ---
 libgitg/gitg-revision.h                        |   88 -
 libgitg/gitg-runner.c                          |  646 -----
 libgitg/gitg-runner.h                          |   76 -
 libgitg/gitg-shell.c                           | 1045 -------
 libgitg/gitg-shell.h                           |  159 --
 libgitg/gitg-smart-charset-converter.c         |  438 ---
 libgitg/gitg-smart-charset-converter.h         |   76 -
 libgitg/libgitg-1.0.pc.in                      |   11 +
 plugins/Makefile.am                            |    3 +
 plugins/dash/Makefile.am                       |   52 +
 plugins/dash/dash.plugin                       |   11 +
 plugins/dash/gitg-dash-navigation.vala         |   93 +
 plugins/dash/gitg-dash.vala                    |  285 ++
 plugins/dash/resources/resources.xml           |   10 +
 plugins/dash/resources/view-create.ui          |   91 +
 plugins/dash/resources/view-open.ui            |   91 +
 plugins/dash/resources/view-recent.ui          |  173 ++
 plugins/history/Makefile.am                    |   52 +
 plugins/history/gitg-history-navigation.vala   |  123 +
 plugins/history/gitg-history.vala              |  103 +
 plugins/history/history.plugin                 |   11 +
 plugins/history/resources/resources.xml        |    8 +
 plugins/history/resources/view-history.ui      |   13 +
 vapi/config.vapi                               |   13 +
 vapi/gobject-introspection-1.0.vapi            |   31 +
 152 files changed, 5169 insertions(+), 36782 deletions(-)
---
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..4798a69
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,6 @@
+[submodule "libgit2"]
+	path = libgit2
+	url = https://github.com/libgit2/libgit2.git
+[submodule "libgit2-glib"]
+	path = libgit2-glib
+	url = git github com:nacho/libgit2-glib.git
diff --git a/Makefile.am b/Makefile.am
index 8a1de3d..9ef7d1b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4,9 +4,6 @@ ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
 
 SUBDIRS = libgitg gitg data po tests tools
 
-pkgconfigdir = $(libdir)/pkgconfig
-pkgconfig_DATA = libgitg-1.0.pc
-
 DISTCLEANFILES = \
 	intltool-extract \
 	intltool-merge \
@@ -17,8 +14,7 @@ EXTRA_DIST = \
 	MAINTAINERS \
 	intltool-extract.in \
 	intltool-merge.in \
-	intltool-update.in \
-	libgitg-1.0.pc.in
+	intltool-update.in
 
 MAINTAINERCLEANFILES = 		\
 	aclocal.m4		\
diff --git a/configure.ac b/configure.ac
index 2235010..7762563 100644
--- a/configure.ac
+++ b/configure.ac
@@ -14,7 +14,7 @@ AC_INIT([gitg],
         [http://live.gnome.org/Gitg])
 
 AC_CONFIG_HEADERS([config.h])
-AC_CONFIG_SRCDIR([gitg/gitg.c])
+AC_CONFIG_SRCDIR([gitg/gitg.vala])
 AC_CONFIG_MACRO_DIR([m4])
 AC_CONFIG_AUX_DIR([build-aux])
 
@@ -36,11 +36,15 @@ AC_PROG_INSTALL
 AC_PROG_MAKE_SET
 AC_PATH_PROG(GZIP, gzip)
 
+AM_PATH_PYTHON
+AM_PROG_VALAC
+
 AC_PATH_PROG(GLIB_MKENUMS, glib-mkenums)
+AC_PATH_PROG(GLIB_COMPILE_RESOURCES, glib-compile-resources)
 
 # Initialize libtool
 LT_PREREQ([2.2])
-LT_INIT
+LT_INIT(disable-static)
 
 # i18n stuff
 IT_PROG_INTLTOOL([0.40.0])
@@ -49,47 +53,64 @@ AM_GNU_GETTEXT_VERSION([0.17])
 AM_GNU_GETTEXT([external])
 
 GETTEXT_PACKAGE=gitg
+
+dnl adl_RECURSIVE_EVAL(VALUE, RESULT)
+dnl =================================
+dnl Interpolate the VALUE in loop until it doesn't change,
+dnl and set the result to $RESULT.
+dnl WARNING: It's easy to get an infinite loop with some unsane input.
+AC_DEFUN([adl_RECURSIVE_EVAL],
+[_lcl_receval="$1"
+$2=`(test "x$prefix" = xNONE && prefix="$ac_default_prefix"
+     test "x$exec_prefix" = xNONE && exec_prefix="${prefix}"
+     _lcl_receval_old=''
+     while test "[$]_lcl_receval_old" != "[$]_lcl_receval"; do
+       _lcl_receval_old="[$]_lcl_receval"
+       eval _lcl_receval="\"[$]_lcl_receval\""
+     done
+     echo "[$]_lcl_receval")`])
+
+adl_RECURSIVE_EVAL("$datadir/gitg", [GITG_DATADIR])
+adl_RECURSIVE_EVAL("$datadir/locale", [GITG_LOCALEDIR])
+adl_RECURSIVE_EVAL("$libdir/gitg", [GITG_LIBDIR])
+
 AC_SUBST(GETTEXT_PACKAGE)
+
 AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE,"$GETTEXT_PACKAGE", [Gettext package])
+AC_DEFINE_UNQUOTED(GITG_DATADIR,"$GITG_DATADIR", [data dir])
+AC_DEFINE_UNQUOTED(GITG_LOCALEDIR,"$GITG_LOCALEDIR", [locale dir])
+AC_DEFINE_UNQUOTED(GITG_LIBDIR,"$GITG_LIBDIR", [lib dir])
 
 AC_CHECK_LIB([m], [sinf])
 
 GLIB_REQUIRED_VERSION=2.26
 GTK_REQUIRED_VERSION=3.0.0
 GTKSOURCEVIEW_REQUIRED_VERSION=3.1.3
+INTROSPECTION_REQUIRED=0.10.1
 
-PKG_CHECK_MODULES(GITG, [
-	gtk+-3.0 >= $GTK_REQUIRED_VERSION
+PKG_CHECK_MODULES(LIBGITG, [
 	gthread-2.0 >= $GLIB_REQUIRED_VERSION
 	glib-2.0 >= $GLIB_REQUIRED_VERSION
 	gobject-2.0 >= $GLIB_REQUIRED_VERSION
 	gmodule-2.0 >= $GLIB_REQUIRED_VERSION
 	gio-2.0 >= $GLIB_REQUIRED_VERSION
 	gio-unix-2.0 >= $GLIB_REQUIRED_VERSION
+	gobject-introspection-1.0 >= $INTROSPECTION_REQUIRED
+	libgit2-glib-1.0
 ])
 
-PKG_CHECK_MODULES(PACKAGE, [
-	gtksourceview-3.0 >= $GTKSOURCEVIEW_REQUIRED_VERSION
-	gsettings-desktop-schemas
-])
-
-AC_PATH_PROG(GLIB_GENMARSHAL, glib-genmarshal)
-
-GNOME_COMPILE_WARNINGS([maximum])
-
-if test "x$USE_MAINTAINER_MODE" = "xyes"; then
-PACKAGE_CFLAGS="$PACKAGE_CFLAGS -Wall -Werror -Wuninitialized -Wmissing-declarations"
-enable_deprecations="yes"
-fi
-
-
 AC_ARG_ENABLE(deprecations,
               [AS_HELP_STRING([--enable-deprecations],
                               [warn about deprecated usages [default=no]])],
               [enable_deprecations=$enableval])
 
+if test "x$USE_MAINTAINER_MODE" = "xyes"; then
+LIBGITG_CFLAGS="$LIBGITG_CFLAGS -Wall -Werror -Wuninitialized -Wmissing-declarations"
+enable_deprecations="yes"
+fi
+
 if test "$enable_deprecations" = "yes"; then
-   DISABLE_DEPRECATED_CFLAGS="\
+   LIBGITG_CFLAGS="$LIBGITG_CFLAGS \
 -DG_DISABLE_DEPRECATED \
 -DGDK_DISABLE_DEPRECATED \
 -DGTK_DISABLE_DEPRECATED \
@@ -98,36 +119,56 @@ if test "$enable_deprecations" = "yes"; then
 -DGSEAL_ENABLE"
 fi
 
-PACKAGE_LIBS="$PACKAGE_LIBS -lm"
-
-
 AC_ARG_ENABLE(debug,
               [AS_HELP_STRING([--disable-debug],
                               [disable debug information [default=yes]])],
               [enable_debug=$enableval], [enable_debug=yes])
 
 if test "x$enable_debug" = "xyes"; then
-	PACKAGE_CFLAGS="$PACKAGE_CFLAGS -g"
+	LIBGITG_CFLAGS="$LIBGITG_CFLAGS -g -O0"
 	AC_DEFINE([ENABLE_DEBUG],[1],[Whether debugging support is enabled])
 fi
 
-AM_CONDITIONAL([ENABLE_DEBUG], [test "$enable_debug" = "yes"])
+AC_SUBST(LIBGITG_CFLAGS)
+AC_SUBST(LIBGITG_LIBS)
 
+PKG_CHECK_MODULES(LIBGITG_GTK, [
+	gtk+-3.0 >= $GTK_REQUIRED_VERSION
+])
 
-AC_ARG_ENABLE(debug,
-              [AS_HELP_STRING([--disable-debug],
-                              [disable debug information [default=yes]])],
-              [enable_debug=$enableval], [enable_debug=yes])
+LIBGITG_GTK_CFLAGS="$LIBGITG_CFLAGS $LIBGITG_GTK_CFLAGS"
+LIBGITG_GTK_LIBS="$LIBGITG_LIBS $LIBGITG_GTK_LIBS"
 
-if test "x$enable_debug" = "xyes"; then
-	PACKAGE_CFLAGS="$PACKAGE_CFLAGS -g"
-	AC_DEFINE([ENABLE_DEBUG],[1],[Whether debugging support is enabled])
-fi
+AC_SUBST(LIBGITG_GTK_CFLAGS)
+AC_SUBST(LIBGITG_GTK_LIBS)
+
+LIBGITG_EXT_CFLAGS="$LIBGITG_GTK_CFLAGS"
+LIBGITG_EXT_LIBS="$LIBGITG_GTK_LIBS"
 
-AM_CONDITIONAL([ENABLE_DEBUG], [test "$enable_debug" = "yes"])
+AC_SUBST(LIBGITG_EXT_CFLAGS)
+AC_SUBST(LIBGITG_EXT_LIBS)
 
-AC_SUBST(PACKAGE_CFLAGS)
-AC_SUBST(PACKAGE_LIBS)
+LIBPEAS_REQUIRED_VERSION=1.2.0
+LIBPEAS_GTK_REQUIRED_VERSION=1.2.0
+
+PKG_CHECK_MODULES(GITG, [
+	gtksourceview-3.0 >= $GTKSOURCEVIEW_REQUIRED_VERSION
+	gsettings-desktop-schemas
+	libpeas-1.0 >= $LIBPEAS_REQUIRED_VERSION
+	libpeas-gtk-1.0 >= $LIBPEAS_GTK_REQUIRED_VERSION
+	gee-1.0
+])
+
+GITG_CFLAGS="$LIBGITG_GTK_CFLAGS $GITG_CFLAGS"
+GITG_LIBS="$LIBGITG_GTK_LIBS $GITG_LIBS -lm"
+
+AC_SUBST(GITG_CFLAGS)
+AC_SUBST(GITG_LIBS)
+
+GOBJECT_INTROSPECTION_REQUIRE($INTROSPECTION_REQUIRED)
+AC_SUBST(INTROSPECTION_REQUIRED)
+
+GNOME_COMPILE_WARNINGS([maximum])
 
 GLIB_GSETTINGS
 
@@ -148,10 +189,63 @@ AS_IF([ test "$glade_catalog" = "yes" ],
 	 AC_MSG_RESULT([$GLADE_CATALOG_DIR])
 	 AC_SUBST(GLADE_CATALOG_DIR)])
 
+GITG_PLUGIN_DATADIR="$datadir/gitg/plugins"
+AC_SUBST(GITG_PLUGIN_DATADIR)
+
+GITG_PLUGIN_LIBDIR="$libdir/gitg/plugins"
+AC_SUBST(GITG_PLUGIN_LIBDIR)
+
+GITG_PLUGIN_CFLAGS="$GITG_CFLAGS"
+GITG_PLUGIN_LIBS="$GITG_LIBS"
+
+AC_SUBST(GITG_PLUGIN_CFLAGS)
+AC_SUBST(GITG_PLUGIN_LIBS)
+
+GITG_PLUGIN_LIBTOOL_FLAGS="-module -avoid-version"
+AC_SUBST(GITG_PLUGIN_LIBTOOL_FLAGS)
+
+GITG_PLUGIN_VALAFLAGS="--pkg GitgExt-1.0		\
+	--pkg Ggit-1.0 			\
+	--pkg Gitg-1.0			\
+	--pkg gio-2.0			\
+	--pkg gtk+-3.0			\
+	--pkg libpeas-1.0		\
+	--pkg gee-1.0			\
+	--vapidir \$(top_srcdir)/vapi	\
+	--pkg config"
+
+AC_SUBST(GITG_PLUGIN_VALAFLAGS)
+
+dnl ===========================================================================
+dnl Check for python
+dnl ===========================================================================
+PYGOBJECT_REQUIRED=3.0.0
+
+AC_ARG_ENABLE([python],
+              AS_HELP_STRING([--enable-python[=@<:@no/auto/yes@:>@]],[Build with python support]),
+              [enable_python=$enableval],
+              [enable_python="auto"])
+
+if test "x$enable_python" = "xauto"; then
+        PKG_CHECK_EXISTS([pygobject-3.0 >= $PYGOBJECT_REQUIRED],
+                         [enable_python=yes],[enable_python=no])
+fi
+
+if test "x$enable_python" = "xyes"; then
+        PKG_CHECK_MODULES(PYTHON, [pygobject-3.0 >= $PYGOBJECT_REQUIRED])
+
+        pyoverridesdir=`$PYTHON -c "import gi; print(gi._overridesdir)"`
+        AC_SUBST(pyoverridesdir)
+fi
+
+AM_CONDITIONAL(ENABLE_PYTHON, test x"$enable_python" = "xyes")
+
 AC_CONFIG_FILES([
 Makefile
-libgitg-1.0.pc
 libgitg/Makefile
+libgitg/libgitg-1.0.pc
+libgitg-ext/Makefile
+libgitg-ext/libgitg-ext-1.0.pc
 gitg/Makefile
 data/Makefile
 data/gitg.desktop.in
@@ -160,6 +254,9 @@ data/org.gnome.gitg.gschema.xml.in
 po/Makefile.in
 tests/Makefile
 tools/Makefile
+plugins/Makefile
+plugins/dash/Makefile
+plugins/history/Makefile
 ])
 
 AC_OUTPUT
@@ -172,4 +269,5 @@ Configuration:
 	Compiler:		${CC}
 	Glade catalog:          ${glade_catalog}
 	Debug enabled:          ${enable_debug}
+	Python support:         ${enable_python}
 "
diff --git a/data/gitg-glade.xml b/data/gitg-glade.xml
index 0b22393..66664ce 100644
--- a/data/gitg-glade.xml
+++ b/data/gitg-glade.xml
@@ -3,15 +3,8 @@
 <glade-catalog name="gitg">
   <glade-widget-classes>
     <glade-widget-class name="GitgWindow" title="GitgWindow" generic-name="gitgwindow" parent="GtkWindow"/>
-    <glade-widget-class name="GitgCommitView" title="GitgCommitView" generic-name="gitgcommitview" parent="GtkVPaned"/>
-    <glade-widget-class name="GitgCellRendererPath" title="GitgCellRendererPath" generic-name="gitgcellrendererpath" parent="GtkCellRendererText"/>
-    <glade-widget-class name="GitgDiffView" title="GitgDiffView" generic-name="gitgdiffview" parent="GtkSourceView"/>
   </glade-widget-classes>
 
   <glade-widget-group name="gitg" title="gitg">
-    <glade-widget-class-ref name="GitgWindow" />
-    <glade-widget-class-ref name="GitgCommitView" />
-    <glade-widget-class-ref name="GitgCellRendererPath" />
-    <glade-widget-class-ref name="GitgDiffView" />
   </glade-widget-group>
 </glade-catalog>
diff --git a/gitg/Makefile.am b/gitg/Makefile.am
index e74617f..1b06d97 100644
--- a/gitg/Makefile.am
+++ b/gitg/Makefile.am
@@ -4,85 +4,67 @@ AM_CPPFLAGS =							\
 	-I$(top_srcdir)						\
 	-I$(srcdir)						\
 	$(GITG_CFLAGS)						\
-	$(PACKAGE_CFLAGS)					\
 	$(WARN_CFLAGS)						\
-	$(DISABLE_DEPRECATED_CFLAGS)				\
 	-DDATADIR=\""$(datadir)"\"				\
 	-DGITG_DATADIR=\""$(datadir)/gitg"\"			\
 	-DGITG_LOCALEDIR=\""$(datadir)/locale"\"
 
-NOINST_H_FILES =			\
-	gitg-activatable.h		\
-	gitg-avatar-cache.h		\
-	gitg-blame-renderer.h		\
-	gitg-branch-actions.h		\
-	gitg-cell-renderer-path.h	\
-	gitg-commit-view.h		\
-	gitg-diff-line-renderer.h	\
-	gitg-diff-view.h		\
-	gitg-dirs.h			\
-	gitg-dnd.h			\
-	gitg-label-renderer.h		\
-	gitg-preferences-dialog.h	\
-	gitg-repository-dialog.h	\
-	gitg-revision-panel.h		\
-	gitg-revision-details-panel.h	\
-	gitg-revision-files-panel.h	\
-	gitg-revision-changes-panel.h	\
-	gitg-stat-view.h		\
-	gitg-uri.h			\
-	gitg-utils.h			\
-	gitg-window.h
+VALAFLAGS = \
+	--vapidir $(top_srcdir)/vapi				\
+	--pkg config						\
+	--pkg Ggit-1.0						\
+	--pkg Gitg-1.0						\
+	--pkg GitgExt-1.0					\
+	--pkg gtk+-3.0						\
+	--pkg gio-2.0						\
+	--pkg libpeas-1.0					\
+	--pkg gobject-introspection-1.0
 
-gitg_SOURCES =				\
-	$(BUILT_SOURCES)		\
-	gitg.c				\
-	gitg-activatable.c		\
-	gitg-avatar-cache.c		\
-	gitg-blame-renderer.c		\
-	gitg-branch-actions.c		\
-	gitg-cell-renderer-path.c	\
-	gitg-commit-view.c		\
-	gitg-diff-line-renderer.c	\
-	gitg-diff-view.c		\
-	gitg-dirs.c			\
-	gitg-dnd.c			\
-	gitg-label-renderer.c		\
-	gitg-preferences-dialog.c	\
-	gitg-repository-dialog.c	\
-	gitg-revision-panel.c		\
-	gitg-revision-details-panel.c	\
-	gitg-revision-files-panel.c	\
-	gitg-revision-changes-panel.c	\
-	gitg-stat-view.c		\
-	gitg-uri.c			\
-	gitg-utils.c			\
-	gitg-window.c			\
-	$(NOINST_H_FILES)
+VALASOURCES =							\
+	gitg.vala						\
+	gitg-dirs.vala						\
+	gitg-window.vala					\
+	gitg-resource.vala					\
+	gitg-application.vala					\
+	gitg-plugins-engine.vala
 
-gitg_LDADD =		\
-	$(GITG_LIBS)	\
-	$(PACKAGE_LIBS) \
-	$(top_builddir)/libgitg/libgitg-1.0.la
+BUILT_SOURCES = 						\
+	gitg-resources.c					\
+	gitg-resources.h
+
+gitg_SOURCES =							\
+	$(VALASOURCES)						\
+	gitg-resources.c
+
+gitg_LDADD =							\
+	$(GITG_LIBS)						\
+	$(PACKAGE_LIBS) 					\
+	$(top_builddir)/libgitg/libgitg-1.0.la			\
+	$(top_builddir)/libgitg-ext/libgitg-ext-1.0.la
 
 gitg_LDFLAGS = -export-dynamic -no-undefined -export-symbols-regex "^[[^_]].*"
 
-uidir = $(datadir)/gitg/ui/
-ui_DATA = \
-	gitg-window.ui			\
-	gitg-commit-menu.ui		\
-	gitg-ui.xml 			\
-	gitg-menus.xml			\
-	gitg-preferences.ui		\
-	gitg-new-branch.ui		\
-	gitg-tag.ui			\
-	gitg-repository.ui		\
-	gitg-revision-details-panel.ui	\
-	gitg-revision-changes-panel.ui	\
-	gitg-revision-files-panel.ui
+gitg-resources.c: resources/gitg-resources.xml $(shell $(GLIB_COMPILE_RESOURCES) --generate-dependencies --sourcedir $(srcdir)/resources resources/gitg-resources.xml)
+	$(GLIB_COMPILE_RESOURCES) --generate-source 	\
+		--sourcedir $(srcdir)/resources 	\
+		--target "$@" "$<"
+
+gitg-resources.h: resources/gitg-resources.xml $(shell $(GLIB_COMPILE_RESOURCES) --generate-dependencies --sourcedir $(srcdir)/resources resources/gitg-resources.xml)
+	$(GLIB_COMPILE_RESOURCES) --generate-header 	\
+		--sourcedir $(srcdir)/resources 	\
+		--target "$@" "$<"
+
+# Ignore all warnings for vala code...
+gitg_CFLAGS = \
+	-w
 
 EXTRA_DIST = 				\
-	$(ui_DATA)
+	resources/gitg-resources.xml		\
+	$(shell $(GLIB_COMPILE_RESOURCES) --generate-dependencies --sourcedir $(srcdir)/resources resources/gitg-resources.xml)
+
+CLEANFILES = 				\
+	$(VALASOURCES:.vala=.c)		\
+	$(BUILT_SOURCES)
 
 -include $(top_srcdir)/git.mk
 
diff --git a/gitg/gitg-application.vala b/gitg/gitg-application.vala
new file mode 100644
index 0000000..d5a57ef
--- /dev/null
+++ b/gitg/gitg-application.vala
@@ -0,0 +1,343 @@
+namespace Gitg
+{
+
+public class Application : Gtk.Application
+{
+	public Application()
+	{
+		Object(application_id: "org.gnome.gitg",
+		       flags: ApplicationFlags.HANDLES_OPEN |
+		              ApplicationFlags.HANDLES_COMMAND_LINE |
+		              ApplicationFlags.SEND_ENVIRONMENT);
+	}
+
+	private struct Options
+	{
+		public static bool quit = false;
+		public static bool commit = false;
+		public static string? select = null;
+		public static bool startup = false;
+		public static bool no_wd = false;
+		public static ApplicationCommandLine command_line;
+
+		public static const OptionEntry[] entries = {
+			{"version", 'v', OptionFlags.NO_ARG, OptionArg.CALLBACK,
+			 (void *)show_version_and_quit, N_("Show the application's version"), null},
+			{"commit", 'c', 0, OptionArg.NONE,
+			 ref commit, N_("Start gitg in commit mode"), null},
+			 {"select", 's', 0, OptionArg.STRING, ref select, N_("Select commit after loading the repository"), null},
+			 {"no-wd", 0, 0, OptionArg.NONE,
+			 ref no_wd, N_("Do not try to load a repository from the current working directory"), null},
+			{null}
+		};
+	}
+
+	private static Options options;
+
+	private static void show_version_and_quit()
+	{
+		stdout.printf("%s %s\n",
+		              Environment.get_application_name(),
+		              Config.VERSION);
+
+		options.quit = true;
+	}
+
+	private void parse_command_line(ref unowned string[] argv) throws OptionError
+	{
+		var ctx = new OptionContext(_("- git repository viewer"));
+
+		ctx.add_main_entries(options.entries, Config.GETTEXT_PACKAGE);
+		ctx.add_group(Gtk.get_option_group(true));
+
+		ctx.parse(ref argv);
+	}
+
+	protected override bool local_command_line ([CCode (array_length = false, array_null_terminated = true)] ref unowned string[] arguments, out int exit_status)
+	{
+		// Parse command line just for -v and -h
+		string[] cp = arguments;
+		unowned string[] argv = cp;
+
+		try
+		{
+			parse_command_line(ref argv);
+		}
+		catch (Error e)
+		{
+			exit_status = 1;
+			return true;
+		}
+
+		if (options.quit)
+		{
+			exit_status = 0;
+			return true;
+		}
+
+		return base.local_command_line(ref arguments, out exit_status);
+	}
+
+	protected override int command_line(ApplicationCommandLine cmd)
+	{
+		string[] arguments = cmd.get_arguments();
+		unowned string[] argv = arguments;
+
+		try
+		{
+			parse_command_line(ref argv);
+		}
+		catch (Error e)
+		{
+			cmd.printerr("option parsing failed: %s\n", e.message);
+			return 1;
+		}
+
+		if (options.quit)
+		{
+			return 0;
+		}
+
+		options.command_line = cmd;
+
+		if (options.startup)
+		{
+			app_init();
+			options.startup = false;
+		}
+
+		if (argv.length > 1)
+		{
+			File[] files = new File[argv.length - 1];
+			files.length = 0;
+
+			foreach (string arg in argv[1:argv.length])
+			{
+				files += File.new_for_commandline_arg(arg);
+			}
+
+			open(files, open_hint_from_options);
+		}
+		else
+		{
+			activate();
+		}
+
+		return 1;
+	}
+
+	private string open_hint_from_options
+	{
+		get { return options.commit ? "commit" : "history"; }
+	}
+
+	private void on_app_new_window_activated()
+	{
+	}
+
+	private void on_app_help_activated()
+	{
+	
+	}
+
+	private void on_app_about_activated()
+	{
+	
+	}
+
+	private void on_app_quit_activated()
+	{
+		foreach (var window in get_windows())
+		{
+			window.destroy();
+		}
+	}
+
+	private static const ActionEntry[] app_entries = {
+		{"new", on_app_new_window_activated},
+		{"help", on_app_help_activated},
+		{"about", on_app_about_activated},
+		{"quit", on_app_quit_activated}
+	};
+
+	private void setup_menus()
+	{
+		add_action_entries(app_entries, this);
+
+		MenuModel[] menus = Resource.load_objects<MenuModel>("ui/gitg-menus.ui", {"app-menu", "win-menu"});
+
+		set_app_menu(menus[0]);
+		set_menubar(menus[1]);
+	}
+
+	protected override void startup()
+	{
+		options.startup = true;
+		base.startup();
+
+		setup_menus();
+	}
+
+	protected override void activate()
+	{
+		/* Application gets activated when no command line arguments have
+		 * been provided. However, gitg does something special in the case
+		 * that it has been launched from the terminal. It will try to open
+		 * the cwd as a repository. However, when not launched from the terminal
+		 * this is undesired, and a --no-wd allows gitg to be launched without
+		 * the implicit working directory opening of the repository. In the
+		 * end, the following happens:
+		 *
+		 * 1) --no-wd: present the window
+		 * 2) Get cwd from the commandline: open
+		 */
+		if (options.no_wd)
+		{
+			present_window();
+		}
+		else
+		{
+			// Otherwise open repository from current dir
+			string? wd = options.command_line.get_cwd();
+
+			open(new File[] { File.new_for_path(wd) },
+			     open_hint_from_options);
+		}
+
+		base.activate();
+	}
+
+	private Window? find_window_for_file(File file)
+	{
+		foreach (Gtk.Window window in get_windows())
+		{
+			Window wnd = window as Window;
+
+			if (wnd.repository == null)
+			{
+				continue;
+			}
+
+			if (wnd.repository.get_location().equal(file))
+			{
+				return wnd;
+			}
+		}
+
+		return null;
+	}
+
+	protected override void open(File[] files, string hint)
+	{
+		if (files.length == 0)
+		{
+			return;
+		}
+
+		bool opened = false;
+
+		// Set of files are potential git repositories
+		foreach (File f in files)
+		{
+			// See if the repository is already open somewhere
+			Window? window = find_window_for_file(f);
+
+			if (window != null)
+			{
+				// Present the window with this repository open
+				window.present_with_time(Gdk.CURRENT_TIME);
+				continue;
+			}
+
+			File? resolved;
+
+			// Try to open a repository at this location
+			try
+			{
+				resolved = Ggit.Repository.discover(f);
+			}
+			catch { continue; }
+
+			// Open the repository
+			Repository? repo;
+
+			try
+			{
+				repo = new Repository(resolved, null);
+			}
+			catch { continue; }
+
+			// Finally, create a window for the repository
+			new_window(repo, hint);
+			opened = true;
+		}
+
+		if (!opened)
+		{
+			// still open a window
+			present_window();
+		}
+	}
+
+	private void app_init()
+	{
+		PluginsEngine.initialize();
+
+		Gtk.CssProvider? provider = Resource.load_css("style.css");
+
+		if (provider != null)
+		{
+			Gtk.StyleContext.add_provider_for_screen(Gdk.Screen.get_default(),
+			                                         provider,
+			                                         600);
+		}
+	}
+
+	private GitgExt.ViewAction get_action_from_hint(string? hint)
+	{
+		if (hint == null)
+		{
+			return GitgExt.ViewAction.DEFAULT;
+		}
+
+		EnumClass klass = (EnumClass)typeof(GitgExt.ViewAction).class_ref();
+		EnumValue? val = klass.get_value_by_nick(hint);
+
+		if (val == null)
+		{
+			return GitgExt.ViewAction.DEFAULT;
+		}
+		else
+		{
+			return (GitgExt.ViewAction)val.value;
+		}
+	}
+
+	private void new_window(Repository? repo = null, string? hint = null)
+	{
+		
+		add_window(Window.create_new(this, repo, get_action_from_hint(hint)));
+		present_window();
+	}
+
+	private void present_window()
+	{
+		/* Present the first window in the windows registered on the
+		 * application. If there are no windows, then create a new empty
+		 * window.
+		 */
+		unowned List<Gtk.Window> windows = get_windows();
+
+		if (windows == null)
+		{
+			new_window();
+			return;
+		}
+
+		windows.data.present_with_time(Gdk.CURRENT_TIME);
+	}
+}
+
+}
+
+// ex:set ts=4 noet
diff --git a/gitg/gitg-dirs.vala b/gitg/gitg-dirs.vala
new file mode 100644
index 0000000..bc345e2
--- /dev/null
+++ b/gitg/gitg-dirs.vala
@@ -0,0 +1,64 @@
+namespace Gitg
+{
+
+public class Dirs
+{
+	public static string data_dir
+	{
+		get { return Config.GITG_DATADIR; }
+	}
+
+	public static string locale_dir
+	{
+		get { return Config.GITG_LOCALEDIR; }
+	}
+
+	public static string lib_dir
+	{
+		get { return Config.GITG_LIBDIR; }
+	}
+
+	public static string plugins_dir
+	{
+		owned get { return Path.build_filename(lib_dir, "plugins"); }
+	}
+
+	public static string plugins_data_dir
+	{
+		owned get { return Path.build_filename(data_dir, "plugins"); }
+	}
+
+	public static string user_plugins_dir
+	{
+		owned get { return Path.build_filename(Environment.get_user_data_dir(), "gitg", "plugins"); }
+	}
+
+	public static string user_plugins_data_dir
+	{
+		owned get { return user_plugins_dir; }
+	}
+
+	public static string build_data_file(string part, ...)
+	{
+		var l = va_list();
+		var ret = Path.build_filename(data_dir, part, null);
+
+		while (true)
+		{
+			string? s = l.arg();
+
+			if (s == null)
+			{
+				break;
+			}
+
+			ret = Path.build_filename(ret, s);
+		}
+
+		return ret;
+	}
+}
+
+}
+
+// ex: ts=4 noet
diff --git a/gitg/gitg-plugins-engine.vala b/gitg/gitg-plugins-engine.vala
new file mode 100644
index 0000000..925686e
--- /dev/null
+++ b/gitg/gitg-plugins-engine.vala
@@ -0,0 +1,67 @@
+namespace Gitg
+{
+
+public class PluginsEngine : Peas.Engine
+{
+	private static PluginsEngine s_instance;
+
+	construct
+	{
+		enable_loader("python");
+
+		var repo = Introspection.Repository.get_default();
+
+		try
+		{
+			repo.require("Peas", "1.0", 0);
+			repo.require("PeasGtk", "1.0", 0);
+		}
+		catch (Error e)
+		{
+			warning("Could not load repository: %s", e.message);
+			return;
+		}
+
+		add_search_path(Dirs.user_plugins_dir,
+		                Dirs.user_plugins_data_dir);
+
+		add_search_path(Dirs.plugins_dir,
+		                Dirs.plugins_data_dir);
+
+		Peas.PluginInfo[] builtins = new Peas.PluginInfo[20];
+		builtins.length = 0;
+
+		foreach (var info in get_plugin_list())
+		{
+			if (info.is_builtin())
+			{
+				builtins += info;
+			}
+		}
+
+		foreach (var info in builtins)
+		{
+			load_plugin(info);
+		}
+	}
+
+	public new static PluginsEngine get_default()
+	{
+		if (s_instance == null)
+		{
+			s_instance = new PluginsEngine();
+			s_instance.add_weak_pointer(&s_instance);
+		}
+
+		return s_instance;
+	}
+
+	public static void initialize()
+	{
+		get_default();
+	}
+}
+
+}
+
+// ex: ts=4 noet
diff --git a/gitg/gitg-resource.vala b/gitg/gitg-resource.vala
new file mode 100644
index 0000000..4af3c42
--- /dev/null
+++ b/gitg/gitg-resource.vala
@@ -0,0 +1,62 @@
+namespace Gitg
+{
+	class Resource
+	{
+		public static T[]? load_objects<T>(string id, string[] objects)
+		{
+			var builder = new Gtk.Builder();
+
+			try
+			{
+				builder.add_from_resource("/org/gnome/gitg/" + id);
+			}
+			catch (Error e)
+			{
+				warning("Error while loading resource: %s", e.message);
+				return null;
+			}
+
+			T[] ret = new T[objects.length];
+			ret.length = 0;
+
+			foreach (string obj in objects)
+			{
+				ret += (T)builder.get_object(obj);
+			}
+
+			return ret;
+		}
+
+		public static T? load_object<T>(string id, string object)
+		{
+			T[]? ret = load_objects<T>(id, new string[] {object});
+
+			if (ret == null)
+			{
+				return null;
+			}
+
+			return ret[0];
+		}
+
+		public static Gtk.CssProvider? load_css(string id)
+		{
+			var provider = new Gtk.CssProvider();
+			var f = File.new_for_uri("resource:///org/gnome/gitg/ui/" + id);
+
+			try
+			{
+				provider.load_from_file(f);
+			}
+			catch (Error e)
+			{
+				warning("Error while loading resource: %s", e.message);
+				return null;
+			}
+
+			return provider;
+		}
+	}
+}
+
+// ex: ts=4 noet
diff --git a/gitg/gitg-window.vala b/gitg/gitg-window.vala
new file mode 100644
index 0000000..80d176b
--- /dev/null
+++ b/gitg/gitg-window.vala
@@ -0,0 +1,457 @@
+namespace Gitg
+{
+
+public class Window : Gtk.ApplicationWindow, GitgExt.Application, Initable, Gtk.Buildable
+{
+	private class ActiveView
+	{
+		public GitgExt.View view;
+		public Gtk.RadioToolButton? navigation_button;
+
+		public ActiveView(GitgExt.View v)
+		{
+			view = v;
+		}
+	}
+
+	private Repository? d_repository;
+	private GitgExt.MessageBus d_message_bus;
+	private Peas.ExtensionSet d_extensions_view;
+	private Peas.ExtensionSet d_extensions_navigation;
+	private GitgExt.View? d_current_view;
+	private HashTable<string, GitgExt.View> d_view_map;
+	private GitgExt.ViewAction d_action;
+
+	private HashTable<string, ActiveView> d_active_views;
+
+	// Widgets
+	private Gtk.Toolbar d_topnav;
+	private Gtk.Toolbar d_subnav;
+	private Gtk.Paned d_paned;
+	private Gtk.Paned d_panels;
+	private Gtk.Notebook d_notebook_panels;
+	private GitgExt.NavigationTreeView d_navigation;
+	private Gtk.Frame d_main_frame;
+
+	public GitgExt.View? current_view
+	{
+		owned get { return d_current_view; }
+	}
+
+	public GitgExt.MessageBus message_bus
+	{
+		owned get { return d_message_bus; }
+	}
+
+	public Repository? repository
+	{
+		owned get { return d_repository; }
+	}
+
+	private Gtk.RadioToolButton? create_topnav_button(GitgExt.View v)
+	{
+		Icon? icon = v.icon;
+
+		if (icon == null)
+		{
+			return null;
+		}
+
+		var img = new Gtk.Image.from_gicon(icon, d_topnav.get_icon_size());
+		img.show();
+
+		Gtk.RadioToolButton button;
+
+		if (d_topnav.get_n_items() != 0)
+		{
+			var ic = d_topnav.get_nth_item(0);
+			button = new Gtk.RadioToolButton.from_widget(ic as Gtk.RadioToolButton);
+		}
+		else
+		{
+			button = new Gtk.RadioToolButton(null);
+		}
+
+		button.set_icon_widget(img);
+		button.set_label(v.display_name);
+
+		button.show();
+
+		return button;
+	}
+
+	private void add_view(GitgExt.View v)
+	{
+		// Add a navigation button if needed
+		Gtk.RadioToolButton? button = create_topnav_button(v);
+		ActiveView av = new ActiveView(v);
+
+		av.navigation_button = button;
+
+		if (button != null)
+		{
+			d_topnav.add(button);
+		}
+
+		button.toggled.connect((b) => {
+			if (b.active)
+			{
+				set_view(v);
+			}
+		});
+
+		d_active_views.insert(v.id, av);
+	}
+
+	private void set_view(GitgExt.View v)
+	{
+		/* This function updates the view to @v. The following steps are
+		 * involved:
+		 *
+		 * 1) Clear navigation tree
+		 * 2) Remove all panels and panel navigation widgets
+		 * 3) Remove main view widget
+		 * 3) Hide panels and panel navigation
+		 * 4) Set the current view to @v
+		 * 5) Fill the navigation model with navigation from @v (if needed)
+		 * 6) Query nagivation extensions to fill the navigation model
+		 */
+
+		if (d_current_view == v)
+		{
+			return;
+		}
+
+		d_navigation.model.clear();
+
+		// Remove panel widgets
+		while (d_notebook_panels.get_n_pages() > 0)
+		{
+			d_notebook_panels.remove_page(0);
+		}
+
+		// Remove panel navigation buttons
+		while (d_subnav.get_n_items() > 0)
+		{
+			d_subnav.remove(d_subnav.get_nth_item(0));
+		}
+
+		var child = d_main_frame.get_child();
+		if (child != null)
+		{
+			d_main_frame.remove(child);
+		}
+
+		// Hide panel note book and panel navigation toolbar
+		d_notebook_panels.hide();
+		d_subnav.hide();
+
+		if (d_current_view != null)
+		{
+			var av = d_active_views.lookup(d_current_view.id);
+
+			if (av != null)
+			{
+				av.navigation_button.set_active(false);
+			}
+		}
+
+		// Set the current view
+		d_current_view = v;
+
+		// Populate navigation from the view first
+		GitgExt.Navigation? nav = v.navigation;
+
+		if (nav != null)
+		{
+			d_navigation.model.populate(nav);
+		}
+
+		// Populate navigation from the extensions
+		d_extensions_navigation.foreach((s, info, obj) => {
+			nav = obj as GitgExt.Navigation;
+
+			if (nav.available)
+			{
+				d_navigation.model.populate(nav);
+			}
+		});
+
+		// Expand all the navigation by default
+		d_navigation.expand_all();
+
+		// Select the first item of the navigation list
+		d_navigation.select_first();
+
+		// Set the main widget
+		var widget = v.widget;
+
+		if (widget != null)
+		{
+			widget.show();
+			d_main_frame.add(widget);
+		}
+
+		var av = d_active_views.lookup(v.id);
+
+		if (av.navigation_button != null)
+		{
+			
+			av.navigation_button.set_active(true);
+		}
+	}
+
+	private void remove_view(GitgExt.View v, bool update_current)
+	{
+		ActiveView av;
+
+		if (d_active_views.lookup_extended(v.id, null, out av))
+		{
+			av.navigation_button.destroy();
+
+			d_view_map.remove(v.id);
+			d_active_views.remove(v.id);
+
+			if (av.view == d_current_view)
+			{
+				d_current_view = null;
+
+				if (update_current)
+				{
+					activate_default_view();
+				}
+			}
+		}
+	}
+
+	private void extension_view_added(Peas.ExtensionSet s,
+	                                  Peas.PluginInfo info,
+	                                  Object obj)
+	{
+		GitgExt.View v = obj as GitgExt.View;
+
+		d_view_map.insert(v.id, v);
+
+		if (v.is_available())
+		{
+			add_view(v);
+		}
+	}
+
+	private void extension_view_removed(Peas.ExtensionSet s,
+	                                    Peas.PluginInfo info,
+	                                    Object obj)
+	{
+		remove_view(obj as GitgExt.View, true);
+	}
+
+	private void update_nav_visibility(Gtk.Toolbar tb)
+	{
+		tb.visible = (tb.get_n_items() > 1);
+	}
+
+	private void parser_finished(Gtk.Builder builder)
+	{
+		d_topnav = builder.get_object("toolbar_topnav") as Gtk.Toolbar;
+		d_subnav = builder.get_object("toolbar_subnav") as Gtk.Toolbar;
+		d_paned = builder.get_object("paned_main") as Gtk.Paned;
+		d_panels = builder.get_object("paned_panel") as Gtk.Paned;
+		d_main_frame = builder.get_object("frame_main") as Gtk.Frame;
+
+		d_navigation = builder.get_object("tree_view_navigation") as GitgExt.NavigationTreeView;
+		d_notebook_panels = builder.get_object("notebook_panels") as Gtk.Notebook;
+
+		d_topnav.add.connect((t, widget) => {
+			update_nav_visibility(d_topnav);
+		});
+
+		d_topnav.remove.connect((t, widget) => {
+			update_nav_visibility(d_topnav);
+		});
+
+		d_subnav.add.connect((t, widget) => {
+			update_nav_visibility(d_subnav);
+		});
+
+		d_subnav.remove.connect((t, widget) => {
+			update_nav_visibility(d_subnav);
+		});
+
+		base.parser_finished(builder);
+	}
+
+	private bool init(Cancellable? cancellable)
+	{
+		// Setup message bus
+		d_message_bus = new GitgExt.MessageBus();
+
+		// Initialize peas extensions set for views
+		var engine = PluginsEngine.get_default();
+
+		d_extensions_view = new Peas.ExtensionSet(engine,
+		                                          typeof(GitgExt.View),
+		                                          "application",
+		                                          this);
+
+		d_extensions_navigation = new Peas.ExtensionSet(engine,
+		                                                typeof(GitgExt.Navigation),
+		                                                "application",
+		                                                this);
+
+		d_view_map = new HashTable<string, GitgExt.View>(str_hash, str_equal);
+		d_active_views = new HashTable<string, ActiveView>(str_hash, str_equal);
+
+		// Add all the extensions
+		d_extensions_view.foreach(extension_view_added);
+
+		d_extensions_view.extension_added.connect(extension_view_added);
+		d_extensions_view.extension_removed.connect(extension_view_removed);
+
+		activate_default_view();
+
+		return true;
+	}
+
+	public static Window? create_new(Gtk.Application app,
+	                                 Repository repository,
+	                                 GitgExt.ViewAction action)
+	{
+		Window? ret = Resource.load_object<Window>("ui/gitg-window.ui", "window");
+
+		if (ret != null)
+		{
+			ret.d_repository = repository;
+			ret.d_action = action;
+		}
+
+		try
+		{
+			((Initable)ret).init(null);
+		} catch {}
+
+		return ret;
+	}
+
+	private void activate_default_view()
+	{
+		GitgExt.View? def = null;
+
+		// Activate the default view
+		d_extensions_view.foreach((s, info, obj) => {
+			GitgExt.View v = obj as GitgExt.View;
+
+			if (d_active_views.lookup_extended(v.id, null, null))
+			{
+				if (v.is_default_for(d_action))
+				{
+					set_view(v);
+					def = null;
+					return;
+				}
+				else if (def == null)
+				{
+					def = v;
+				}
+			}
+		});
+
+		if (def != null)
+		{
+			set_view(def);
+		}
+	}
+
+	private void update_views()
+	{
+		/* This method is called after some state has changed and thus a new
+		 * set of views needs to be computed. Currently the only state change
+		 * is opening or closing a repository.
+		 */
+
+		// Now see if new views became available
+		d_extensions_view.foreach((s, info, obj) => {
+			GitgExt.View v = obj as GitgExt.View;
+
+			bool isavail = v.is_available();
+			bool isactive = d_active_views.lookup_extended(v.id, null, null);
+
+			if (isavail == isactive)
+			{
+				return;
+			}
+
+			if (isactive)
+			{
+				// should be inactive
+				remove_view(v, false);
+			}
+			else
+			{
+				add_view(v);
+			}
+		});
+
+		activate_default_view();
+	}
+
+	/* public API implementation of GitgExt.Application */
+	public GitgExt.View? view(string id)
+	{
+		GitgExt.View v;
+
+		if (d_view_map.lookup_extended(id, null, out v))
+		{
+			set_view(v);
+		}
+
+		return null;
+	}
+
+	public void open(File path)
+	{
+		File repo;
+
+		if (d_repository != null &&
+		    d_repository.get_location().equal(path))
+		{
+			return;
+		}
+
+		try
+		{
+			repo = Ggit.Repository.discover(path);
+		}
+		catch
+		{
+			// TODO
+			return;
+		}
+
+		if (d_repository != null)
+		{
+			close();
+		}
+
+		try
+		{
+			d_repository = new Gitg.Repository(repo, null);
+		}
+		catch {}
+
+		update_views();
+	}
+
+	public void create(File path)
+	{
+		// TODO
+	}
+
+	public void close()
+	{
+		// TODO
+	}
+}
+
+}
+
+// ex:ts=4 noet
diff --git a/gitg/gitg.vala b/gitg/gitg.vala
new file mode 100644
index 0000000..19d2428
--- /dev/null
+++ b/gitg/gitg.vala
@@ -0,0 +1,25 @@
+namespace Gitg
+{
+
+private const string version = Config.VERSION;
+
+public class Main
+{
+	public static int main(string[] args)
+	{
+		Intl.bindtextdomain(Config.GETTEXT_PACKAGE, Config.GITG_LOCALEDIR);
+		Intl.textdomain(Config.GETTEXT_PACKAGE);
+
+		Environment.set_prgname("gitg");
+		Environment.set_application_name(_("gitg"));
+
+		Gitg.init();
+
+		Application app = new Application();
+		return app.run(args);
+	}
+}
+
+}
+
+// ex:set ts=4 noet
diff --git a/gitg/resources/gitg-resources.xml b/gitg/resources/gitg-resources.xml
new file mode 100644
index 0000000..4700c6b
--- /dev/null
+++ b/gitg/resources/gitg-resources.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+  <gresource prefix="/org/gnome/gitg">
+    <file>ui/gitg-window.ui</file>
+    <file>ui/gitg-menus.ui</file>
+    <file>ui/style.css</file>
+  </gresource>
+</gresources>
+
+<!-- ex: et ts=2 -->
diff --git a/gitg/resources/ui/gitg-menus.ui b/gitg/resources/ui/gitg-menus.ui
new file mode 100644
index 0000000..cab1805
--- /dev/null
+++ b/gitg/resources/ui/gitg-menus.ui
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+<interface>
+  <!-- interface-requires gtk+ 3.0 -->
+  <menu id="app-menu">
+    <section>
+      <item>
+        <attribute name="label" translatable="yes">_New Window</attribute>
+        <attribute name="action">app.new</attribute>
+        <attribute name="accel">&lt;Primary&gt;n</attribute>
+      </item>
+    </section>
+    <section>
+      <item>
+        <attribute name="label" translatable="yes">_Help</attribute>
+        <attribute name="action">app.help</attribute>
+        <attribute name="accel">F1</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">_About</attribute>
+        <attribute name="action">app.about</attribute>
+      </item>
+    </section>
+    <section>
+      <item>
+        <attribute name="label" translatable="yes">_Quit</attribute>
+        <attribute name="action">app.quit</attribute>
+        <attribute name="accel">&lt;Primary&gt;q</attribute>
+      </item>
+    </section>
+  </menu>
+  <menu id="win-menu">
+  </menu>
+</interface>
diff --git a/gitg/resources/ui/gitg-window.ui b/gitg/resources/ui/gitg-window.ui
new file mode 100644
index 0000000..cddb1dc
--- /dev/null
+++ b/gitg/resources/ui/gitg-window.ui
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.3 -->
+  <!-- interface-requires gitg 0.2 -->
+  <object class="GitgWindow" id="window">
+    <property name="can_focus">False</property>
+    <property name="title" translatable="yes">gitg</property>
+    <property name="default_width">800</property>
+    <property name="default_height">600</property>
+    <child>
+      <object class="GtkPaned" id="paned_main">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="position">200</property>
+        <property name="vexpand">True</property>
+        <property name="name">paned_main</property>
+        <child>
+          <object class="GtkEventBox" id="navigation_background">
+            <property name="visible">True</property>
+            <property name="name">navigation_background</property>
+            <child>
+              <object class="GtkGrid" id="grid_navigation">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="name">grid_navigation</property>
+                <property name="hexpand">True</property>
+                <property name="halign">fill</property>
+                <child>
+                  <object class="GtkToolbar" id="toolbar_topnav">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="toolbar_style">icons</property>
+                    <property name="icon-size">1</property>
+                    <property name="hexpand">True</property>
+                    <property name="halign">fill</property>
+                    <property name="name">toolbar_topnav</property>
+                    <property name="show-arrow">False</property>
+                    <style>
+                      <class name="inline-toolbar"/>
+                    </style>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">0</property>
+                    <property name="width">1</property>
+                    <property name="height">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkScrolledWindow" id="scrolled_window_navigation">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="hscrollbar_policy">never</property>
+                    <property name="vexpand">true</property>
+                    <property name="hexpand">true</property>
+                    <property name="shadow-type">none</property>
+                    <property name="name">scrolled_window_navigation</property>
+                    <child>
+                      <object class="GitgExtNavigationTreeView" id="tree_view_navigation">
+                        <property name="visible">True</property>
+                        <property name="headers-visible">False</property>
+                        <property name="name">tree_view_navigation</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">1</property>
+                    <property name="width">1</property>
+                    <property name="height">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkToolbar" id="toolbar_subnav">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="toolbar_style">icons</property>
+                    <property name="hexpand">True</property>
+                    <property name="icon-size">2</property>
+                    <property name="name">toolbar_subnav</property>
+                    <style>
+                      <class name="inline-toolbar"/>
+                    </style>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">2</property>
+                    <property name="width">1</property>
+                    <property name="height">1</property>
+                  </packing>
+                </child>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="resize">False</property>
+            <property name="shrink">True</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkPaned" id="paned_panels">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="orientation">vertical</property>
+            <child>
+              <object class="GtkEventBox" id="frame_background">
+                <property name="visible">True</property>
+                <property name="name">frame_background</property>
+                <child>
+                  <object class="GtkFrame" id="frame_main">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="shadow_type">in</property>
+                    <property name="name">frame_main</property>
+                    <child>
+                      <placeholder/>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="GtkNotebook" id="notebook_panels">
+                <property name="visible">True</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="resize">True</property>
+            <property name="shrink">True</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
+
+<!-- ex:set ts=2 et: -->
diff --git a/gitg/resources/ui/style.css b/gitg/resources/ui/style.css
new file mode 100644
index 0000000..5d934b1
--- /dev/null
+++ b/gitg/resources/ui/style.css
@@ -0,0 +1,56 @@
+GtkToolbar#toolbar_topnav, GtkToolbar#toolbar_subnav {
+	border-left: 0px;
+	border-right: 0px;
+}
+
+GtkToolbar#toolbar_subnav {
+	border-bottom: 0px;
+}
+
+#navigation_background {
+	background-color: shade(@theme_bg_color, 0.85);
+	background-image: none;
+}
+
+#tree_view_navigation {
+	background-color: inherit;
+	background-image: none;
+	color: inherit;
+	background-color: shade(@theme_bg_color, 0.9);
+}
+
+#paned_main {
+	border-style: solid;
+	border-color: @borders;
+	border-width: 0px 0px 0px 1px;
+	padding: 0px;
+	border-radius: 0px;
+	background-color: inherit;
+}
+
+#tree_view_navigation.view:selected {
+	background-color: @theme_selected_bg_color;
+	color: @theme_selected_fg_color;
+}
+
+#frame_main {
+	padding: 0px 6px 6px 6px;
+}
+
+GtkLabel.title {
+	font-weight: bold;
+	font-size: 16;
+	color: @insensitive_fg_color;
+	text-shadow: 1 1 @theme_base_color;
+}
+
+GtkLabel.description {
+	padding-bottom: 12px;
+	color: @insensitive_fg_color;
+}
+
+GtkLabel.grid_title {
+	font-weight: bold;
+
+	text-shadow: 1 1 @theme_base_color;
+}
diff --git a/libgit2 b/libgit2
new file mode 160000
index 0000000..242a1ce
--- /dev/null
+++ b/libgit2
@@ -0,0 +1 @@
+Subproject commit 242a1cea8d66d9ec185044f345b22fec1940178f
diff --git a/libgit2-glib b/libgit2-glib
new file mode 160000
index 0000000..1a17238
--- /dev/null
+++ b/libgit2-glib
@@ -0,0 +1 @@
+Subproject commit 1a17238b82e5d4ab65be78d90f356d2bf4aba912
diff --git a/libgitg-ext/GitgExt.py b/libgitg-ext/GitgExt.py
new file mode 100644
index 0000000..976a445
--- /dev/null
+++ b/libgitg-ext/GitgExt.py
@@ -0,0 +1,38 @@
+from gi.repository import GObject
+from ..overrides import override
+from ..importer import modules
+
+GitgExt = modules['GitgExt']._introspection_module
+__all__ = []
+
+class MessageBus(GitgExt.MessageBus):
+    def create(self, msgid, **kwargs):
+        tp = self.lookup(msgid)
+
+        if not tp.is_a(GitgExt.Message.__gtype__):
+            return None
+
+        kwargs['id'] = msgid
+
+        return GObject.new(tp, **kwargs)
+
+    def send(self, msgid, **kwargs):
+        msg = self.create(msgid, **kwargs)
+        self.send_message(msg)
+
+        return msg
+
+MessageBus = override(MessageBus)
+__all__.append('MessageBus')
+
+class Message(Gedit.Message):
+    def __getattribute__(self, name):
+        try:
+            return Gedit.Message.__getattribute__(self, name)
+        except:
+            return getattr(self.props, name)
+
+Message = override(Message)
+__all__.append('Message')
+
+# vi:ex:ts=4:et
diff --git a/libgitg-ext/Makefile.am b/libgitg-ext/Makefile.am
new file mode 100644
index 0000000..37cad6c
--- /dev/null
+++ b/libgitg-ext/Makefile.am
@@ -0,0 +1,100 @@
+lib_LTLIBRARIES = libgitg-ext-1.0.la
+
+INCLUDES =								\
+	-I$(top_srcdir)							\
+	-I$(srcdir)							\
+	$(LIBGITG_EXT_CFLAGS)						\
+	$(WARN_CFLAGS)							\
+	-DDATADIR=\""$(datadir)"\"					\
+	-DLIBDIR=\""$(libdir)"\"
+
+VALAFLAGS = 				\
+	--pkg Ggit-1.0 			\
+	--pkg Gitg-1.0			\
+	--pkg gio-2.0			\
+	--pkg gtk+-3.0			\
+	--header libgitg-ext.h		\
+	--includedir libgitg-ext	\
+	--basedir $(top_srcdir)		\
+	--gir GitgExt-1.0.gir		\
+	--library libgitg-ext-1.0
+
+libgitg_ext_1_0_la_LDFLAGS = \
+	-export-dynamic -no-undefined -export-symbols-regex "^[^_].*"
+
+libgitg_ext_1_0_la_LIBADD = $(LIBGITG_EXT_LIBS)
+
+INST_H_FILES =				\
+	libgitg-ext.h
+
+VALA_FILES =					\
+	gitg-ext-application.vala		\
+	gitg-ext-panel.vala			\
+	gitg-ext-view.vala			\
+	gitg-ext-navigation.vala		\
+	gitg-ext-navigation-tree-view.vala	\
+	gitg-ext-message-id.vala		\
+	gitg-ext-message.vala			\
+	gitg-ext-message-bus.vala
+
+libgitg_ext_1_0_la_SOURCES =		\
+	$(VALA_FILES)			\
+	gitg-ext-resources.c
+
+headerdir = $(prefix)/include/libgitg-ext-1.0/libgitg-ext
+header_DATA = $(INST_H_FILES)
+
+GitgExt-1.0.gir: libgitg-ext-1.0.la
+
+# Ignore all warnings for vala code...
+libgitg_ext_1_0_la_CFLAGS = \
+	-w
+
+girdir = $(INTROSPECTION_GIRDIR)
+gir_DATA = GitgExt-1.0.gir
+
+typelibdir = $(INTROSPECTION_TYPELIBDIR)
+typelib_DATA = GitgExt-1.0.typelib
+
+%.typelib: %.gir
+	$(INTROSPECTION_COMPILER) $(INTROSPECTION_COMPILER_ARGS) --includedir=. -o $@ $<
+
+if ENABLE_PYTHON
+overridesdir = $(pyoverridesdir)
+overrides_PYTHON =                      \
+	GitgExt.py
+endif
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libgitg-ext-1.0.pc
+
+gitg-ext-resources.c: resources/resources.xml $(shell $(GLIB_COMPILE_RESOURCES) --generate-dependencies --sourcedir $(srcdir)/resources resources/resources.xml)
+	$(GLIB_COMPILE_RESOURCES) --generate-source 	\
+		--sourcedir $(srcdir)/resources 	\
+		--target "$@" "$<"
+
+gitg-ext-resources.h: resources/resources.xml $(shell $(GLIB_COMPILE_RESOURCES) --generate-dependencies --sourcedir $(srcdir)/resources resources/resources.xml)
+	$(GLIB_COMPILE_RESOURCES) --generate-header 	\
+		--sourcedir $(srcdir)/resources 	\
+		--target "$@" "$<"
+
+BUILT_SOURCES =				\
+	$(gir_DATA)			\
+	libgitg-ext.h			\
+	gitg-ext-resources.c		\
+	gitg-ext-resources.h
+
+EXTRA_DIST =				\
+	$(pkgconfig_DATA)		\
+	resources/resources.xml		\
+	$(shell $(GLIB_COMPILE_RESOURCES) --generate-dependencies --sourcedir $(srcdir)/resources resources/resources.xml)
+
+CLEANFILES = 					\
+	$(VALA_FILES:.vala=.c)			\
+	GitgExt-1.0.gir				\
+	GitgExt-1.0.typelib
+
+dist-hook:
+	cd $(distdir); rm -f $(BUILT_SOURCES)
+
+-include $(top_srcdir)/git.mk
diff --git a/libgitg-ext/gitg-ext-application.vala b/libgitg-ext/gitg-ext-application.vala
new file mode 100644
index 0000000..8f0671e
--- /dev/null
+++ b/libgitg-ext/gitg-ext-application.vala
@@ -0,0 +1,19 @@
+namespace GitgExt
+{
+
+public interface Application : Object
+{
+	public abstract Gitg.Repository? repository { owned get; }
+	public abstract GitgExt.MessageBus message_bus { owned get; }
+	public abstract GitgExt.View? current_view { owned get; }
+
+	public abstract GitgExt.View? view(string id);
+
+	public abstract void open(File repository);
+	public abstract void create(File repository);
+	public abstract void close();
+}
+
+}
+
+// ex:set ts=4 noet:
diff --git a/libgitg-ext/gitg-ext-message-bus.vala b/libgitg-ext/gitg-ext-message-bus.vala
new file mode 100644
index 0000000..a15daa9
--- /dev/null
+++ b/libgitg-ext/gitg-ext-message-bus.vala
@@ -0,0 +1,294 @@
+namespace GitgExt
+{
+
+public delegate void MessageCallback(GitgExt.Message message);
+
+public class MessageBus : Object
+{
+	class Listener
+	{
+		public uint id;
+		public bool blocked;
+
+		public MessageCallback callback;
+
+		public Listener(uint id, owned MessageCallback callback)
+		{
+			this.id = id;
+
+			// TODO: destroy notify is lost...
+			this.callback = (owned)callback;
+			this.blocked = false;
+		}
+	}
+
+	class Message
+	{
+		public MessageId id;
+		public List<Listener> listeners;
+
+		public Message(MessageId id)
+		{
+			this.id = id.copy();
+			this.listeners = new List<Listener>();
+		}
+	}
+
+	class IdMap
+	{
+		public Message message;
+		public unowned List<Listener> listener;
+
+		public IdMap(Message message)
+		{
+			this.message = message;
+		}
+	}
+
+	private HashTable<MessageId, Message> d_messages;
+	private HashTable<uint, IdMap> d_idmap;
+	private HashTable<MessageId, Type> d_types;
+	private static MessageBus? s_instance;
+	private static uint s_next_id;
+
+	public signal void registered(MessageId id);
+	public signal void unregistered(MessageId id);
+
+	public virtual signal void dispatch(GitgExt.Message message)
+	{
+		Message? msg = lookup_message(message.id, false);
+
+		if (msg != null)
+		{
+			dispatch_message_real(msg, message);
+		}
+	}
+
+	public MessageBus()
+	{
+		d_messages = new HashTable<MessageId, Message>(MessageId.hash, MessageId.equal);
+		d_idmap = new HashTable<uint, IdMap>(direct_hash, direct_equal);
+		d_types = new HashTable<MessageId, Type>(MessageId.hash, MessageId.equal);
+	}
+
+	public static MessageBus get_default()
+	{
+		if (s_instance == null)
+		{
+			s_instance = new MessageBus();
+			s_instance.add_weak_pointer(&s_instance);
+		}
+
+		return s_instance;
+	}
+
+	private void dispatch_message_real(Message msg, GitgExt.Message message)
+	{
+		foreach (Listener l in msg.listeners)
+		{
+			if (!l.blocked)
+			{
+				l.callback(message);
+			}
+		}
+	}
+
+	public Type lookup(MessageId id)
+	{
+		Type ret;
+
+		if (!d_types.lookup_extended(id, null, out ret))
+		{
+			return Type.INVALID;
+		}
+		else
+		{
+			return ret;
+		}
+	}
+
+	public void register(Type message_type, MessageId id)
+	{
+		if (is_registered(id))
+		{
+			warning("Message type for `%s' is already registered", id.id);
+			return;
+		}
+
+		var cp = id.copy();
+
+		d_types.insert(cp, message_type);
+
+		registered(cp);
+	}
+
+	private void unregister_real(MessageId id, bool remove_from_store)
+	{
+		var cp = id;
+
+		if (!remove_from_store || d_types.remove(cp))
+		{
+			unregistered(cp);
+		}
+	}
+
+	public void unregister(MessageId id)
+	{
+		unregister_real(id, true);
+	}
+
+	public void unregister_all(string object_path)
+	{
+		d_types.foreach_remove((key, val) => {
+			if (key.object_path == object_path)
+			{
+				unregister_real(key, true);
+				return true;
+			}
+			else
+			{
+				return false;
+			}
+		});
+	}
+
+	public bool is_registered(MessageId id)
+	{
+		return d_types.lookup_extended(id, null, null);
+	}
+
+	private Message new_message(MessageId id)
+	{
+		var ret = new Message(id);
+
+		d_messages.insert(id, ret);
+		return ret;
+	}
+
+	private Message? lookup_message(MessageId id, bool create)
+	{
+		var message = d_messages.lookup(id);
+
+		if (message == null && !create)
+		{
+			return null;
+		}
+
+		if (message == null)
+		{
+			message = new_message(id);
+		}
+
+		return message;
+	}
+
+	private uint add_listener(Message message, owned MessageCallback callback)
+	{
+		var listener = new Listener(++s_next_id, (owned)callback);
+
+		message.listeners.append(listener);
+
+		var idmap = new IdMap(message);
+		idmap.listener = message.listeners.last();
+
+		d_idmap.insert(listener.id, idmap);
+		return listener.id;
+	}
+
+	private void remove_listener(Message message, List<Listener> listener)
+	{
+		unowned Listener lst = listener.data;
+
+		d_idmap.remove(lst.id);
+
+		message.listeners.delete_link(listener);
+
+		if (message.listeners == null)
+		{
+			d_messages.remove(message.id);
+		}
+	}
+
+	private void block_listener(Message message, List<Listener> listener)
+	{
+		listener.data.blocked = true;
+	}
+
+	private void unblock_listener(Message message, List<Listener> listener)
+	{
+		listener.data.blocked = false;
+	}
+
+	public new uint connect(MessageId id, owned MessageCallback callback)
+	{
+		var message = lookup_message(id, true);
+
+		return add_listener(message, (owned)callback);
+	}
+
+	private delegate void MatchCallback(Message message, List<Listener> listeners);
+
+	private void process_by_id(uint id, MatchCallback processor)
+	{
+		IdMap? idmap = d_idmap.lookup(id);
+
+		if (idmap == null)
+		{
+			return;
+		}
+
+		processor(idmap.message, idmap.listener);
+	}
+
+	public new void disconnect(uint id)
+	{
+		process_by_id(id, remove_listener);
+	}
+
+	public void block(uint id)
+	{
+		process_by_id(id, block_listener);
+	}
+
+	public void unblock(uint id)
+	{
+		process_by_id(id, unblock_listener);
+	}
+
+	private void dispatch_message(GitgExt.Message message)
+	{
+		dispatch(message);
+	}
+
+	public GitgExt.Message send_message(GitgExt.Message message)
+	{
+		dispatch_message(message);
+		return message;
+	}
+
+	public GitgExt.Message? send(MessageId id, string? firstprop, ...)
+	{
+		Type type = lookup(id);
+
+		if (type == Type.INVALID)
+		{
+			warning("Could not find message type for `%s'", id.id);
+			return null;
+		}
+
+		GitgExt.Message? msg = (GitgExt.Message?)Object.new_valist(type, firstprop, va_list());
+
+		if (msg != null)
+		{
+			msg.id = id;
+		}
+
+		dispatch_message(msg);
+
+		return msg;
+	}
+}
+
+}
+
+// ex:set ts=4 noet:
diff --git a/libgitg-ext/gitg-ext-message-id.vala b/libgitg-ext/gitg-ext-message-id.vala
new file mode 100644
index 0000000..584da75
--- /dev/null
+++ b/libgitg-ext/gitg-ext-message-id.vala
@@ -0,0 +1,75 @@
+namespace GitgExt
+{
+
+public class MessageId : Object
+{
+	public string object_path { construct set; get; }
+	public string method { construct set; get; }
+
+	public string id
+	{
+		owned get { return object_path + "." + method; }
+	}
+
+	public uint hash()
+	{
+		return id.hash();
+	}
+
+	public bool equal(MessageId other)
+	{
+		return id == other.id;
+	}
+
+	public MessageId(string object_path, string method)
+	{
+		Object(object_path: object_path, method: method);
+	}
+
+	public MessageId copy()
+	{
+		return new MessageId(object_path, method);
+	}
+
+	public static bool valid_object_path(string path)
+	{
+		if (path == null)
+		{
+			return false;
+		}
+
+		if (path[0] != '/')
+		{
+			return false;
+		}
+
+		int i = 0;
+
+		while (i < path.length)
+		{
+			var c = path[i];
+
+			if (c == '/')
+			{
+				++i;
+
+				if (i == path.length || !(c.isalpha() || c == '_'))
+				{
+					return false;
+				}
+			}
+			else if (!(c.isalnum() || c == '_'))
+			{
+				return false;
+			}
+
+			++i;
+		}
+
+		return true;
+	}
+}
+
+}
+
+// ex:set ts=4 noet:
diff --git a/libgitg-ext/gitg-ext-message.vala b/libgitg-ext/gitg-ext-message.vala
new file mode 100644
index 0000000..a7f216d
--- /dev/null
+++ b/libgitg-ext/gitg-ext-message.vala
@@ -0,0 +1,40 @@
+namespace GitgExt
+{
+
+public abstract class Message : Object
+{
+	private MessageId d_id;
+
+	public MessageId id
+	{
+		construct set
+		{
+			d_id = value.copy();
+		}
+		get
+		{
+			return d_id;
+		}
+	}
+
+	public bool has(string propname)
+	{
+		return get_class().find_property(propname) != null;
+	}
+
+	public static bool type_has(Type type, string propname)
+	{
+		return ((ObjectClass)type.class_ref()).find_property(propname) != null;
+	}
+
+	public static bool type_check(Type type, string propname, Type value_type)
+	{
+		ParamSpec? spec = ((ObjectClass)type.class_ref()).find_property(propname);
+
+		return (spec != null && spec.value_type == value_type);
+	}
+}
+
+}
+
+// ex:set ts=4 noet:
diff --git a/libgitg-ext/gitg-ext-navigation-tree-view.vala b/libgitg-ext/gitg-ext-navigation-tree-view.vala
new file mode 100644
index 0000000..84b59cb
--- /dev/null
+++ b/libgitg-ext/gitg-ext-navigation-tree-view.vala
@@ -0,0 +1,476 @@
+namespace GitgExt
+{
+
+private enum Column
+{
+	ICON_NAME,
+	TEXT,
+	HINT,
+	SECTION,
+	OID
+}
+
+private enum Hint
+{
+	NONE,
+	HEADER,
+	DEFAULT
+}
+
+public delegate void NavigationActivated(int numclick);
+
+public class NavigationTreeModel : Gtk.TreeStore
+{
+	private class Activated : Object
+	{
+		private NavigationActivated d_activated;
+
+		public Activated(owned NavigationActivated? activated)
+		{
+			d_activated = (owned)activated;
+		}
+
+		public void activate(int numclick)
+		{
+			if (d_activated != null)
+			{
+				d_activated(numclick);
+			}
+		}
+	}
+	private SList<Gtk.TreeIter?> d_parents;
+	private uint d_sections;
+	private uint d_oid;
+	private Activated[] d_callbacks;
+
+	construct
+	{
+		set_column_types({typeof(string), typeof(string), typeof(uint), typeof(uint), typeof(uint)});
+
+		d_callbacks = new Activated[100];
+		d_callbacks.length = 0;
+	}
+
+	public uint begin_section()
+	{
+		d_parents = null;
+		return d_sections;
+	}
+
+	public void end_section()
+	{
+		++d_sections;
+	}
+
+	private void append_one(string text,
+	                        string? icon_name,
+	                        uint hint,
+	                        owned NavigationActivated? callback,
+	                        out Gtk.TreeIter iter)
+	{
+		if (d_parents != null)
+		{
+			base.append(out iter, d_parents.data);
+		}
+		else
+		{
+			base.append(out iter, null);
+		}
+
+		@set(iter,
+		     Column.ICON_NAME, icon_name,
+		     Column.TEXT, text,
+		     Column.HINT, hint,
+		     Column.SECTION, d_sections,
+		     Column.OID, d_oid);
+
+		d_callbacks += new Activated((owned)callback);
+		++d_oid;
+	}
+
+	public NavigationTreeModel begin_header(string text,
+	                                        string? icon_name)
+	{
+		Gtk.TreeIter iter;
+
+		append_one(text, icon_name, Hint.HEADER, null, out iter);
+		d_parents.prepend(iter);
+
+		return this;
+	}
+
+	public NavigationTreeModel end_header()
+	{
+		if (d_parents != null)
+		{
+			d_parents.remove_link(d_parents);
+		}
+
+		return this;
+	}
+
+	public new NavigationTreeModel append_default(string text,
+	                                              string? icon_name,
+	                                              owned NavigationActivated? callback)
+	{
+		Gtk.TreeIter iter;
+		append_one(text, icon_name, Hint.DEFAULT, (owned)callback, out iter);
+
+		return this;
+	}
+
+	public new NavigationTreeModel append(string text,
+	                                      string? icon_name,
+	                                      owned NavigationActivated? callback)
+	{
+		Gtk.TreeIter iter;
+		append_one(text, icon_name, Hint.NONE, (owned)callback, out iter);
+
+		return this;
+	}
+
+	public uint populate(GitgExt.Navigation nav)
+	{
+		uint ret = begin_section();
+
+		nav.populate(this);
+
+		end_section();
+		return ret;
+	}
+
+	public void remove_section(uint section)
+	{
+		Gtk.TreeIter iter;
+
+		if (!get_iter_first(out iter))
+		{
+			return;
+		}
+
+		while (true)
+		{
+			uint s;
+
+			@get(iter, Column.SECTION, out s);
+
+			if (s == section)
+			{
+				if (!base.remove(ref iter))
+				{
+					break;
+				}
+			}
+			else
+			{
+				if (!iter_next(ref iter))
+				{
+					break;
+				}
+			}
+		}
+	}
+
+	public new void clear()
+	{
+		base.clear();
+
+		d_sections = 0;
+		d_oid = 0;
+
+		d_callbacks.length = 0;
+	}
+
+	public void activate(Gtk.TreeIter iter, int numclick)
+	{
+		uint oid;
+
+		@get(iter, Column.OID, out oid);
+
+		if (d_callbacks[oid] != null)
+		{
+			d_callbacks[oid].activate(numclick);
+		}
+	}
+}
+
+public class NavigationRendererText : Gtk.CellRendererText
+{
+	private string d_icon_name;
+	private Gdk.Pixbuf d_pixbuf;
+	private Gtk.StateFlags d_state;
+
+	public string? icon_name
+	{
+		get { return d_icon_name;}
+		set
+		{
+			if (d_icon_name != value)
+			{
+				d_icon_name = value;
+				reset_pixbuf();
+			}
+		}
+	}
+
+	public uint hint { get; set; }
+
+	construct
+	{
+		ellipsize = Pango.EllipsizeMode.MIDDLE;
+	}
+
+	private void reset_pixbuf()
+	{
+		d_pixbuf = null;
+	}
+
+	private void ensure_pixbuf(Gtk.StyleContext ctx)
+	{
+		if (d_icon_name == null || (d_pixbuf != null && d_state == ctx.get_state()))
+		{
+			return;
+		}
+
+		d_pixbuf = null;
+
+		d_state = ctx.get_state();
+		var screen = ctx.get_screen();
+		var settings = Gtk.Settings.get_for_screen(screen);
+
+		int w = 16;
+		int h = 16;
+
+		Gtk.icon_size_lookup_for_settings(settings, Gtk.IconSize.MENU, out w, out h);
+
+		Gtk.IconInfo? info = Gtk.IconTheme.get_default().lookup_icon(d_icon_name,
+		                                                   int.min(w, h),
+		                                                   Gtk.IconLookupFlags.USE_BUILTIN);
+
+		if (info == null)
+		{
+			return;
+		}
+
+		bool symbolic = false;
+
+		try
+		{
+			d_pixbuf = info.load_symbolic_for_context(ctx, out symbolic);
+		} catch {};
+
+		if (d_pixbuf != null)
+		{
+			var source = new Gtk.IconSource();
+			source.set_pixbuf(d_pixbuf);
+
+			source.set_size(Gtk.IconSize.SMALL_TOOLBAR);
+			source.set_size_wildcarded(false);
+
+			d_pixbuf = ctx.render_icon_pixbuf(source, Gtk.IconSize.SMALL_TOOLBAR);
+		}
+	}
+
+	protected override void get_preferred_width(Gtk.Widget widget,
+	                                            out int minimum_width,
+	                                            out int minimum_height)
+	{
+		ensure_pixbuf(widget.get_style_context());
+
+		// Size of text
+		base.get_preferred_width(widget, out minimum_width, out minimum_height);
+
+		if (d_pixbuf != null)
+		{
+			minimum_width += d_pixbuf.get_width() + 3;
+			minimum_height += d_pixbuf.get_height();
+		}
+	}
+
+	protected override void get_preferred_height_for_width(Gtk.Widget widget,
+	                                                       int width,
+	                                                       out int minimum_height,
+	                                                       out int natural_height)
+	{
+		base.get_preferred_height_for_width(widget, width,
+		                                    out minimum_height,
+		                                    out natural_height);
+
+		ensure_pixbuf(widget.get_style_context());
+
+		if (d_pixbuf != null)
+		{
+			minimum_height = int.max(minimum_height, d_pixbuf.height);
+			natural_height = int.max(natural_height, d_pixbuf.height);
+		}
+	}
+
+	protected override void render(Cairo.Context ctx,
+	                               Gtk.Widget widget,
+	                               Gdk.Rectangle background_area,
+	                               Gdk.Rectangle cell_area,
+	                               Gtk.CellRendererState state)
+	{
+		var stx = widget.get_style_context();
+		ensure_pixbuf(stx);
+
+		int xpad = 3;
+
+		if (hint != Hint.HEADER)
+		{
+			cell_area.x -= 15;
+		}
+
+		if (d_pixbuf == null)
+		{
+			base.render(ctx, widget, background_area, cell_area, state);
+		}
+		else
+		{
+			// render the text with an additional padding
+			Gdk.Rectangle area = cell_area;
+			area.x += d_pixbuf.width + xpad;
+
+			base.render(ctx, widget, background_area, area, state);
+
+			// render the pixbuf
+			int ypad = (cell_area.height - d_pixbuf.height) / 2;
+
+			stx.render_icon(ctx, d_pixbuf, cell_area.x, cell_area.y + ypad);
+		}
+	}
+}
+
+public class NavigationTreeView : Gtk.TreeView
+{
+	private Gdk.RGBA d_header_bg;
+	private Gdk.RGBA d_header_fg;
+
+	construct
+	{
+		var model = new NavigationTreeModel();
+		set_model(model);
+
+		var cell = new NavigationRendererText();
+		var col = new Gtk.TreeViewColumn.with_attributes("text",
+		                                                  cell,
+		                                                  "icon_name", Column.ICON_NAME,
+		                                                  "text", Column.TEXT,
+		                                                  "hint", Column.HINT);
+
+		col.set_cell_data_func(cell, (col, cell, model, iter) => {
+			uint hint;
+
+			model.get(iter, Column.HINT, out hint);
+
+			Gtk.CellRendererText t = cell as Gtk.CellRendererText;
+
+			if (hint == Hint.HEADER)
+			{
+				t.weight = Pango.Weight.BOLD;
+				t.background_rgba = d_header_bg;
+				t.foreground_rgba = d_header_fg;
+			}
+			else
+			{
+				t.weight = Pango.Weight.NORMAL;
+				t.background_set = false;
+				t.foreground_set = false;
+			}
+		});
+
+		append_column(col);
+
+		get_selection().set_select_function((sel, model, path, cursel) => {
+			Gtk.TreeIter iter;
+			model.get_iter(out iter, path);
+
+			uint hint;
+
+			model.get(iter, Column.HINT, out hint);
+
+			return hint != Hint.HEADER;
+		});
+
+		update_header_colors();
+
+		get_selection().changed.connect((sel) => {
+			Gtk.TreeIter iter;
+
+			if (sel.get_selected(null, out iter))
+			{
+				model.activate(iter, 1);
+			}
+		});
+	}
+
+	protected override void style_updated()
+	{
+		base.style_updated();
+		update_header_colors();
+	}
+
+	private void update_header_colors()
+	{
+		get_style_context().lookup_color("insensitive_bg_color", out d_header_bg);
+		get_style_context().lookup_color("insensitive_fg_color", out d_header_fg);
+	}
+
+	public new NavigationTreeModel model
+	{
+		get { return base.get_model() as NavigationTreeModel; }
+	}
+
+	private bool select_first_in(Gtk.TreeIter? parent, bool seldef)
+	{
+		Gtk.TreeIter iter;
+
+		if (!model.iter_children(out iter, parent))
+		{
+			return false;
+		}
+
+		while (true)
+		{
+			uint hint;
+			model.get(iter, Column.HINT, out hint);
+
+			if (hint == Hint.HEADER)
+			{
+				if (select_first_in(iter, seldef))
+				{
+					return true;
+				}
+			}
+
+			if (!seldef || hint == Hint.DEFAULT)
+			{
+				get_selection().select_iter(iter);
+
+				return true;
+			}
+
+			if (!model.iter_next(ref iter))
+			{
+				return false;
+			}
+		}
+	}
+
+	public void select_first()
+	{
+		select_first_in(null, true) || select_first_in(null, false);
+	}
+
+	protected override void row_activated(Gtk.TreePath path, Gtk.TreeViewColumn col)
+	{
+		Gtk.TreeIter iter;
+
+		model.get_iter(out iter, path);
+		model.activate(iter, 2);
+	}
+}
+
+}
+
+// ex:set ts=4 noet:
diff --git a/libgitg-ext/gitg-ext-navigation.vala b/libgitg-ext/gitg-ext-navigation.vala
new file mode 100644
index 0000000..cee9941
--- /dev/null
+++ b/libgitg-ext/gitg-ext-navigation.vala
@@ -0,0 +1,22 @@
+namespace GitgExt
+{
+
+public enum NavigationSide
+{
+	LEFT = 0,
+	TOP = 1
+}
+
+public interface Navigation : Object
+{
+	public abstract Application? application { owned get; construct; }
+
+	public abstract void populate(GitgExt.NavigationTreeModel model);
+	public abstract bool available { get; }
+
+	public abstract NavigationSide navigation_side { get; }
+}
+
+}
+
+// ex:set ts=4 noet:
diff --git a/libgitg-ext/gitg-ext-panel.vala b/libgitg-ext/gitg-ext-panel.vala
new file mode 100644
index 0000000..1d6d105
--- /dev/null
+++ b/libgitg-ext/gitg-ext-panel.vala
@@ -0,0 +1,18 @@
+namespace GitgExt
+{
+
+public interface Panel : Object
+{
+	public abstract Application? application { owned get; construct; }
+
+	public abstract string id { owned get; }
+	public abstract string display_name { owned get; }
+	public abstract Icon? icon { owned get; }
+
+	public abstract bool supported { get; }
+	public abstract Gtk.Widget? widget { owned get; }
+}
+
+}
+
+// ex: ts=4 noet
diff --git a/libgitg-ext/gitg-ext-view.vala b/libgitg-ext/gitg-ext-view.vala
new file mode 100644
index 0000000..b362597
--- /dev/null
+++ b/libgitg-ext/gitg-ext-view.vala
@@ -0,0 +1,112 @@
+namespace GitgExt
+{
+
+/**
+ * A view action.
+ *
+ * A view action indicates a user preference to open gitg in a particular view.
+ */
+public enum ViewAction
+{
+	/**
+	 * Open gitg in the History view.
+	 */
+	HISTORY,
+
+	/**
+	 * Open gitg in the Commit view.
+	 */
+	COMMIT,
+
+	/**
+	 * Open gitg in the default view.
+	 */
+	DEFAULT = HISTORY
+}
+
+/**
+ * gitg View interface.
+ *
+ * The GitgExtView interface can be implemented to provide a main view in
+ * gitg. An example of such views are the builtin Dashboard, History and
+ * Commit views.
+ *
+ * Implementations of the GitgExtView interface will be integrated
+ * automatically in the gitg interface according to the various interface
+ * methods and properties that need to be implemented.
+ *
+ * == Default View Navigation ==
+ * To provide a default navigation when the view is active, the
+ * #GitgExtView::navigation property should be implemented and should return a
+ * non-null #GitgExtNavigation. This navigation section will always be present
+ * at the top of the navigation menu. Note that you should normally ''not''
+ * export this type to Peas because you will end up having the navigation
+ * shown twice in the UI.
+ */
+public interface View : Object
+{
+	/**
+	 * The main gitg application interface. This property is a "construct"
+	 * property and will be automatically set when an instance of the view
+	 * object is created.
+	 */
+	public abstract GitgExt.Application? application { owned get; construct; }
+
+	/**
+	 * A unique id for the view. Ids in gitg are normally of the form
+	 * /org/gnome/gitg/...
+	 */
+	public abstract string id { owned get; }
+
+	/**
+	 * The display name of the view. This should result in a string which can
+	 * be displayed in the gitg UI to identify the view.
+	 */
+	public abstract string display_name { owned get; }
+
+	/**
+	 * The view icon. If provided, the icon will be used in the top navigation
+	 * toolbar so that users can easily switch to the view. If not provider,
+	 * the only way to activate the view will be through the menu.
+	 */
+	public abstract Icon? icon { owned get; }
+
+	/**
+	 * The view widget. This widget will be embedded in the main gitg UI when
+	 * the view is activated.
+	 */
+	public abstract Gtk.Widget? widget { owned get; }
+
+	/**
+	 * Main navigation for the view. When provided, the corresponding navigation
+	 * section will be added in the navigation panel when the view is activated.
+	 */
+	public abstract Navigation? navigation { owned get; }
+
+	/**
+	 * This method is used by gitg to verify whether or not a particular view
+	 * is available in the current state of #GitgExtView::application.
+	 * Implementations usually at least verify whether there is a repository
+	 * currently open, but other constraints for when a view should be
+	 * available can also be implemented.
+	 *
+	 * @return %TRUE if the view is available, %FALSE otherwise.
+	 */
+	public abstract bool is_available();
+
+	/**
+	 * @param action the action
+	 *
+	 * Implement this method when a view should be the preferred default view
+	 * for a particular action. The first available view indicating to be
+	 * a default view will be used as the default activated view when launching
+	 * gitg (or when opening a repository).
+	 *
+	 * @return %TRUE if the view is a default for @action, %FALSE otherwise.
+	 */
+	public abstract bool is_default_for(ViewAction action);
+}
+
+}
+
+// ex: ts=4 noet
diff --git a/libgitg-ext/libgitg-ext-1.0.pc.in b/libgitg-ext/libgitg-ext-1.0.pc.in
new file mode 100644
index 0000000..d851288
--- /dev/null
+++ b/libgitg-ext/libgitg-ext-1.0.pc.in
@@ -0,0 +1,11 @@
+prefix= prefix@
+exec_prefix= exec_prefix@
+libdir= libdir@
+includedir= includedir@
+
+Name: libgitg-ext
+Description: gitg extensions library
+Version: @PACKAGE_VERSION@
+Requires: libgit2-glib-1.0 libgitg-1.0 gtk+-3.0 glib-2.0 gobject-2.0 gmodule-2.0 gio-2.0 gio-unix-2.0 gthread-2.0
+Libs: -L${libdir} -lgitg-ext-1.0
+Cflags: -I${includedir}/libgitg-ext-1.0
diff --git a/libgitg-ext/resources/resources.xml b/libgitg-ext/resources/resources.xml
new file mode 100644
index 0000000..a2e738f
--- /dev/null
+++ b/libgitg-ext/resources/resources.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+  <gresource prefix="/org/gnome/gitg/ext">
+  </gresource>
+</gresources>
+
+<!-- ex: et ts=2 -->
diff --git a/libgitg/Makefile.am b/libgitg/Makefile.am
index e6e6973..7dec0e8 100644
--- a/libgitg/Makefile.am
+++ b/libgitg/Makefile.am
@@ -3,97 +3,70 @@ lib_LTLIBRARIES = libgitg-1.0.la
 INCLUDES =								\
 	-I$(top_srcdir)							\
 	-I$(srcdir)							\
-	$(GITG_CFLAGS)							\
+	$(LIBGITG_CFLAGS)						\
 	$(WARN_CFLAGS)							\
-	$(DISABLE_DEPRECATED_CFLAGS)					\
 	-DDATADIR=\""$(datadir)"\"					\
-	-DLIBDIR=\""$(libdir)"\"					\
-	-DGITG_LOCALEDIR=\""$(datadir)/locale"\"
+	-DLIBDIR=\""$(libdir)"\"
 
-libgitg_1_0_la_LDFLAGS = \
+libgitg_1_0_la_LDFLAGS = 		\
 	-export-dynamic -no-undefined -export-symbols-regex "^[^_].*"
 
-libgitg_1_0_la_LIBADD = $(GITG_LIBS)
-
-BUILT_SOURCES =			\
-	gitg-enum-types.h	\
-	gitg-enum-types.c
-
-INST_H_FILES =			\
-	$(BUILT_H_FILES)	\
-	gitg-changed-file.h	\
-	gitg-color.h		\
-	gitg-commit.h		\
-	gitg-config.h		\
-	gitg-hash.h		\
-	gitg-lane.h		\
-	gitg-ref.h		\
-	gitg-repository.h	\
-	gitg-revision.h		\
-	gitg-runner.h		\
-	gitg-command.h		\
-	gitg-shell.h		\
-	gitg-io.h		\
-	gitg-line-parser.h
-
-NOINST_H_FILES =			\
-	gitg-convert.h			\
-	gitg-debug.h			\
-	gitg-i18n.h			\
-	gitg-lanes.h			\
-	gitg-smart-charset-converter.h	\
-	gitg-encodings.h
-
-C_FILES =				\
-	$(BUILT_SOURCES)		\
-	gitg-changed-file.c		\
-	gitg-color.c			\
-	gitg-commit.c			\
-	gitg-config.c			\
-	gitg-convert.c			\
-	gitg-hash.c			\
-	gitg-i18n.c			\
-	gitg-lane.c			\
-	gitg-lanes.c			\
-	gitg-ref.c			\
-	gitg-repository.c		\
-	gitg-revision.c			\
-	gitg-runner.c			\
-	gitg-smart-charset-converter.c	\
-	gitg-encodings.c		\
-	gitg-command.c			\
-	gitg-io.c			\
-	gitg-shell.c			\
-	gitg-line-parser.c
-
-if ENABLE_DEBUG
-C_FILES += gitg-debug.c
-endif
-
-ENUM_H_FILES =			\
-	gitg-changed-file.h
-
-libgitg_1_0_la_SOURCES = 	\
-	$(INST_H_FILES)		\
-	$(NOINST_H_FILES)	\
-	$(C_FILES)
+libgitg_1_0_la_LIBADD = $(LIBGITG_LIBS)
+
+INST_H_FILES =				\
+	libgitg.h
+
+VALAFLAGS = 				\
+	--pkg Ggit-1.0 			\
+	--pkg gio-2.0			\
+	--header libgitg.h		\
+	--includedir libgitg		\
+	--basedir $(top_srcdir)		\
+	--gir Gitg-1.0.gir		\
+	--library libgitg-1.0
+
+Gitg-1.0.gir: libgitg-1.0.la
+
+VALA_FILES =				\
+	gitg-repository.vala		\
+	gitg-ref.vala			\
+	gitg-lane.vala			\
+	gitg-lanes.vala			\
+	gitg-color.vala			\
+	gitg-init.vala			\
+	gitg-commit.vala		\
+	gitg-commit-model.vala
+
+# Ignore all warnings for vala code...
+libgitg_1_0_la_CFLAGS =		\
+	-w
+
+libgitg_1_0_la_SOURCES = 		\
+	$(VALA_FILES)
 
 headerdir = $(prefix)/include/libgitg-1.0/libgitg
 header_DATA = $(INST_H_FILES)
 
-EXTRA_DIST = 				\
-	gitg-enum-types.h.template	\
-	gitg-enum-types.c.template
+girdir = $(INTROSPECTION_GIRDIR)
+gir_DATA = Gitg-1.0.gir
 
-CLEANFILES = $(BUILT_SOURCES)
+typelibdir = $(INTROSPECTION_TYPELIBDIR)
+typelib_DATA = Gitg-1.0.typelib
 
-dist-hook:
-	cd $(distdir); rm -f $(BUILT_SOURCES)
+%.typelib: %.gir
+	$(INTROSPECTION_COMPILER) $(INTROSPECTION_COMPILER_ARGS) --includedir=. -o $@ $<
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libgitg-1.0.pc
 
-gitg-enum-types.h: gitg-enum-types.h.template $(ENUM_H_FILES) $(GLIB_MKENUMS)
-	$(AM_V_GEN) (cd $(srcdir) && $(GLIB_MKENUMS) --template gitg-enum-types.h.template $(ENUM_H_FILES)) > $@
+BUILT_SOURCES = \
+	$(gir_DATA) \
+	libgitg.h
 
-gitg-enum-types.c: gitg-enum-types.c.template $(ENUM_H_FILES) $(GLIB_MKENUMS)
-	$(AM_V_GEN) (cd $(srcdir) && $(GLIB_MKENUMS) --template gitg-enum-types.c.template $(ENUM_H_FILES)) > $@
+EXTRA_DIST = $(pkgconfig_DATA)
+CLEANFILES = $(VALA_FILES:.vala=.c)
+
+dist-hook:
+	cd $(distdir); rm -f $(BUILT_SOURCES)
 
 -include $(top_srcdir)/git.mk
diff --git a/libgitg/gitg-color.vala b/libgitg/gitg-color.vala
new file mode 100644
index 0000000..5dc4077
--- /dev/null
+++ b/libgitg/gitg-color.vala
@@ -0,0 +1,85 @@
+namespace Gitg
+{
+
+public class Color : Object
+{
+	private struct Rgb
+	{
+		ushort r;
+		ushort g;
+		ushort b;
+	}
+
+	private static const Rgb[] palette = {
+		{196, 160, 0},
+		{78, 154, 6},
+		{206, 92, 0},
+		{32, 74, 135},
+		{46, 52, 54},
+		{108, 53, 102},
+		{164, 0, 0},
+
+		{138, 226, 52},
+		{252, 175, 62},
+		{114, 159, 207},
+		{252, 233, 79},
+		{136, 138, 133},
+		{173, 127, 168},
+		{233, 185, 110},
+		{239, 41, 41}
+	};
+
+	private static uint current_index;
+
+	public uint idx = 0;
+
+	public static void reset()
+	{
+		current_index = 0;
+	}
+
+	public void components(out double r, out double g, out double b)
+	{
+		r = palette[idx].r / 255.0;
+		g = palette[idx].g / 255.0;
+		b = palette[idx].b / 255.0;
+	}
+
+	private static uint inc_index()
+	{
+		uint next = current_index++;
+
+		if (current_index == palette.length)
+		{
+			current_index = 0;
+		}
+
+		return next;
+	}
+
+	public static Color next()
+	{
+		Color ret = new Color();
+		ret.idx = inc_index();
+
+		return ret;
+	}
+
+	public Color next_index()
+	{
+		this.idx = inc_index();
+		return this;
+	}
+
+	public Color copy()
+	{
+		Color ret = new Color();
+		ret.idx = idx;
+
+		return ret;
+	}
+}
+
+}
+
+// ex:set ts=4 noet
diff --git a/libgitg/gitg-commit-model.vala b/libgitg/gitg-commit-model.vala
new file mode 100644
index 0000000..87aca7b
--- /dev/null
+++ b/libgitg/gitg-commit-model.vala
@@ -0,0 +1,246 @@
+namespace Gitg
+{
+
+public class CommitModel : Object
+{
+	private Repository d_repository;
+	private Cancellable? d_cancellable;
+	private Gitg.Commit[] d_ids;
+	private unowned Thread<void*>? d_thread;
+	private Ggit.RevisionWalker? d_walker;
+	private uint d_advertized_size;
+	private uint d_idleid;
+
+	public uint limit { get; set; }
+
+	private Ggit.OId[] include { get; set; }
+	private Ggit.OId[] exclude { get; set; }
+	
+	public signal void started();
+	public signal void update(uint added);
+	public signal void finished();
+
+	public CommitModel(Repository repository)
+	{
+		d_repository = repository;
+	}
+
+	~CommitModel()
+	{
+		cancel();
+	}
+
+	private void cancel()
+	{
+		if (d_cancellable == null)
+		{
+			return;
+		}
+
+		d_cancellable.cancel();
+		d_thread.join();
+
+		if (d_idleid != 0)
+		{
+			Source.remove(d_idleid);
+			d_idleid = 0;
+		}
+
+		d_thread = null;
+		d_cancellable = null;
+
+		d_ids = new Gitg.Commit[0];
+		d_advertized_size = 0;
+
+		started();
+		finished();
+	}
+
+	public void reload()
+	{
+		cancel();
+		walk();
+	}
+
+	public uint size()
+	{
+		return d_advertized_size;
+	}
+
+	public new Gitg.Commit? @get(uint idx)
+	{
+		Gitg.Commit? ret;
+
+		if (idx >= d_advertized_size)
+		{
+			return null;
+		}
+
+		lock(d_ids)
+		{
+			ret = d_ids[idx];
+		}
+
+		return ret;
+	}
+
+	private void notify_batch(bool isend)
+	{
+		lock(d_idleid)
+		{
+			if (d_idleid != 0)
+			{
+				Source.remove(d_idleid);
+				d_idleid = 0;
+			}
+		}
+
+		uint newsize = d_ids.length;
+
+		d_idleid = Idle.add(() => {
+			lock(d_idleid)
+			{
+				if (d_idleid == 0)
+				{
+					return false;
+				}
+
+				d_idleid = 0;
+
+				uint added = newsize - d_advertized_size;
+				d_advertized_size = newsize;
+
+				update(added);
+
+				if (isend)
+				{
+					d_thread.join();
+					d_thread = null;
+					d_cancellable = null;
+
+					finished();
+				}
+			}
+
+			return false;
+		});
+	}
+
+	private void walk()
+	{
+		Ggit.OId[] included = include;
+		Ggit.OId[] excluded = exclude;
+
+		d_cancellable = new Cancellable();
+		uint limit = this.limit;
+
+		ThreadFunc<void*> run = () => {
+			if (d_walker == null)
+			{
+				try
+				{
+					d_walker = new Ggit.RevisionWalker(d_repository);
+				}
+				catch
+				{
+					notify_batch(true);
+					return null;
+				}
+			}
+
+			d_walker.reset();
+
+			foreach (Ggit.OId oid in included)
+			{
+				try
+				{
+					d_walker.push(oid);
+				} catch {};
+			}
+
+			foreach (Ggit.OId oid in excluded)
+			{
+				try
+				{
+					d_walker.hide(oid);
+				} catch {};
+			}
+
+			d_walker.set_sort_mode(Ggit.SortMode.TOPOLOGICAL |
+			                       Ggit.SortMode.TIME);
+
+			uint size;
+
+			// Pre-allocate array to store commits
+			lock(d_ids)
+			{
+				d_ids = new Gitg.Commit[1000];
+
+				size = d_ids.length;
+
+				d_ids.length = 0;
+				d_advertized_size = 0;
+			}
+
+			Timer timer = new Timer();
+
+			while (true)
+			{
+				Ggit.OId? id;
+				Gitg.Commit? commit;
+
+				if (d_cancellable.is_cancelled())
+				{
+					break;
+				}
+
+				try
+				{
+					id = d_walker.next();
+					commit = d_repository.lookup(id, typeof(Gitg.Commit)) as Gitg.Commit;
+				} catch { break; }
+
+				// Add the id
+				if (d_ids.length == size)
+				{
+					lock(d_ids)
+					{
+						size *= 2;
+
+						d_ids.resize((int)size);
+					}
+				}
+
+				d_ids += commit;
+
+				if (timer.elapsed() >= 200)
+				{
+					notify_batch(false);
+					timer.start();
+				}
+
+				if (limit > 0 && d_ids.length == limit)
+				{
+					break;
+				}
+			}
+
+			notify_batch(true);
+			return null;
+		};
+
+		try
+		{
+			d_thread = Thread.create<void*>(run, true);
+		}
+		catch
+		{
+			finished();
+			d_cancellable = null;
+		}
+	}
+}
+
+}
+
+// ex:set ts=4 noet
diff --git a/libgitg/gitg-commit.vala b/libgitg/gitg-commit.vala
new file mode 100644
index 0000000..ff67a22
--- /dev/null
+++ b/libgitg/gitg-commit.vala
@@ -0,0 +1,90 @@
+namespace Gitg
+{
+
+public class Commit : Ggit.Commit
+{
+	public Lane.Tag tag { get; set; }
+	public unowned SList<Lane> lanes { get; set; }
+
+	private ushort d_mylane;
+
+	public ushort mylane
+	{
+		get	{ return d_mylane; }
+		set
+		{
+			d_mylane = value;
+			update_lane_tag();
+		}
+	}
+
+	public unowned Lane lane
+	{
+		get { return lanes.nth_data(d_mylane); }
+	}
+
+	public unowned SList<Lane> remove_lane(Lane lane)
+	{
+		lanes.remove(lane);
+		return lanes;
+	}
+
+	private void update_lane_tag()
+	{
+		unowned Lane? lane = lanes.nth_data(d_mylane);
+
+		if (lane == null)
+		{
+			return;
+		}
+
+		lane.tag &= ~(Lane.Tag.SIGN_STASH |
+		              Lane.Tag.SIGN_STAGED |
+		              Lane.Tag.SIGN_UNSTAGED) | tag;
+	}
+
+	public void update_lanes(SList<Lane> lanes, int mylane)
+	{
+		this.lanes = lanes;
+
+		if (mylane >= 0)
+		{
+			d_mylane = (ushort)mylane;
+		}
+
+		update_lane_tag();
+	}
+
+	public string format_patch_name
+	{
+		owned get
+		{
+			return get_subject().replace(" ", "-").replace("/", "-");;
+		}
+	}
+
+	private string date_for_display(DateTime dt)
+	{
+		return dt.format("%c");
+	}
+
+	public string committer_date_for_display
+	{
+		owned get
+		{
+			return date_for_display(get_committer().get_time());
+		}
+	}
+
+	public string author_date_for_display
+	{
+		owned get
+		{
+			return date_for_display(get_author().get_time());
+		}
+	}
+}
+
+}
+
+// ex:set ts=4 noet
diff --git a/libgitg/gitg-init.vala b/libgitg/gitg-init.vala
new file mode 100644
index 0000000..841a3fe
--- /dev/null
+++ b/libgitg/gitg-init.vala
@@ -0,0 +1,20 @@
+namespace Gitg
+{
+
+public void init()
+{
+	var factory = Ggit.ObjectFactory.get_default();
+
+	factory.register(typeof(Ggit.Repository),
+	                 typeof(Gitg.Repository));
+
+	factory.register(typeof(Ggit.Ref),
+	                 typeof(Gitg.Ref));
+
+	factory.register(typeof(Ggit.Commit),
+	                 typeof(Gitg.Commit));
+}
+
+}
+
+// ex:set ts=4 noet
diff --git a/libgitg/gitg-lane.vala b/libgitg/gitg-lane.vala
new file mode 100644
index 0000000..e62dbdb
--- /dev/null
+++ b/libgitg/gitg-lane.vala
@@ -0,0 +1,63 @@
+namespace Gitg
+{
+
+[Compact]
+public class Lane
+{
+	[Flags]
+	public enum Tag
+	{
+		NONE = 0,
+		START = 1 << 0,
+		END = 1 << 1,
+		SIGN_STASH = 1 << 2,
+		SIGN_STAGED = 1 << 3,
+		SIGN_UNSTAGED = 1 << 4
+	}
+
+	public Color color;
+	public SList<int> from;
+	public Tag tag;
+	public Ggit.OId? boundary_id;
+
+	public Lane()
+	{
+		this.with_color(null);
+	}
+
+	public Lane.with_color(Color? color)
+	{
+		if (color != null)
+		{
+			this.color = color;
+		}
+		else
+		{
+			this.color = Color.next();
+		}
+	}
+
+	public Lane copy()
+	{
+		Lane ret = new Lane.with_color(color);
+		ret.from = from.copy();
+		ret.tag = tag;
+		ret.boundary_id = boundary_id;
+
+		return ret;
+	}
+
+	public Lane dup()
+	{
+		Lane ret = new Lane.with_color(color.copy());
+		ret.from = from.copy();
+		ret.tag = tag;
+		ret.boundary_id = boundary_id;
+
+		return ret;
+	}
+}
+
+}
+
+// ex:set ts=4 noet
diff --git a/libgitg/gitg-lanes.vala b/libgitg/gitg-lanes.vala
new file mode 100644
index 0000000..24e6fbc
--- /dev/null
+++ b/libgitg/gitg-lanes.vala
@@ -0,0 +1,466 @@
+namespace Gitg
+{
+
+public class Lanes : Object
+{
+	public int inactive_max { get; set; }
+	public int inactive_collapse { get; set; }
+	public int inactive_gap { get; set; }
+	public bool inactive_enabled { get; set; }
+
+	private SList<Commit> d_previous;
+	private SList<LaneContainer> d_lanes;
+	private HashTable<Ggit.OId, CollapsedLane> d_collapsed;
+
+	[Compact]
+	class LaneContainer
+	{
+		public Lane lane;
+		public uint inactive;
+		public Ggit.OId? from;
+		public Ggit.OId? to;
+
+		public LaneContainer.with_color(Ggit.OId from,
+		                                Ggit.OId to,
+		                                Color?   color)
+		{
+			this.from = from;
+			this.to = to;
+			this.lane = new Lane.with_color(color);
+			this.inactive = 0;
+		}
+
+		public LaneContainer(Ggit.OId? from,
+		                     Ggit.OId? to)
+		{
+			this.with_color(from, to, null);
+		}
+
+		public void next(int index)
+		{
+			lane = lane.copy();
+
+			lane.tag = Lane.Tag.NONE;
+			lane.from = new SList<int>();
+			lane.from.prepend(index);
+
+			if (to != null)
+			{
+				++inactive;
+			}
+		}
+	}
+
+	[Compact]
+	class CollapsedLane
+	{
+		public Color color;
+		public uint index;
+		public Ggit.OId? from;
+		public Ggit.OId? to;
+
+		public CollapsedLane(LaneContainer container)
+		{
+			color = container.lane.color;
+			from = container.from;
+			to = container.to;
+		}
+	}
+
+	public Lanes()
+	{
+		d_collapsed = new HashTable<Ggit.OId, CollapsedLane>(Ggit.OId.hash,
+		                                                     Ggit.OId.equal);
+
+		reset();
+	}
+
+	public void reset()
+	{
+		d_previous = new SList<Commit>();
+		d_lanes = new SList<LaneContainer>();
+
+		Color.reset();
+
+		d_collapsed.remove_all();
+	}
+
+	public SList<Lane> next(Commit  next,
+	                        out int nextpos)
+	{
+		var myoid = next.get_id();
+
+		if (inactive_enabled)
+		{
+			collapse_lanes();
+			expand_lanes(next);
+		}
+
+		unowned LaneContainer? mylane = find_lane_by_oid(myoid, out nextpos);
+
+		if (mylane == null)
+		{
+			// there is no lane reserver for this comit, add a new lane
+			d_lanes.append(new LaneContainer(myoid, null));
+			nextpos = (int)d_lanes.length() - 1;
+		}
+		else
+		{
+			// copy the color here because the commit is a new stop
+			mylane.lane.color = mylane.lane.color.copy();
+
+			mylane.to = null;
+			mylane.from = next.get_id();
+			mylane.inactive = 0;
+		}
+
+		var res = lanes_list();
+		prepare_lanes(next, nextpos);
+
+		return res;
+	}
+
+	private void prepare_lanes(Commit next, int pos)
+	{
+		var parents = next.get_parents();
+		var myoid = next.get_id();
+
+		init_next_layer();
+		unowned LaneContainer mylane = d_lanes.nth_data(pos);
+
+		for (uint i = 0; i < parents.size(); ++i)
+		{
+			int lnpos;
+			var poid = parents.get_id(i);
+
+			unowned LaneContainer? container = find_lane_by_oid(poid, out lnpos);
+
+			if (container != null)
+			{
+				// there is already a lane for this parent. This means that
+				// we add pos as a merge for the lane, also this means the
+				// color of this lane incluis the merge should change to
+				// one color
+				container.lane.from.append(pos);
+				container.lane.color.next_index();
+
+				container.inactive = 0;
+				container.from = myoid;
+
+				continue;
+			}
+			else if (mylane != null && mylane.to == null)
+			{
+				// there is no parent yet which can proceed on the current
+				// commit lane, so set it now
+				mylane.to = poid;
+
+				if (parents.size() > 1)
+				{
+					mylane.lane.color = Color.next();
+				}
+				else
+				{
+					mylane.lane.color = mylane.lane.color.copy();
+				}
+			}
+			else
+			{
+				// generate a new lane for this parent
+				LaneContainer newlane = new LaneContainer(myoid, poid);
+
+				newlane.lane.from.prepend(pos);
+				d_lanes.append((owned)newlane);
+			}
+		}
+
+		if (mylane != null && mylane.to == null)
+		{
+			// remove current lane if no longer needed (i.e. merged)
+			d_lanes.remove(mylane);
+		}
+
+		// store new commit in track list
+		if (d_previous.length() == inactive_collapse + inactive_gap + 1)
+		{
+			d_previous.delete_link(d_previous.last());
+		}
+
+		d_previous.prepend(next);
+	}
+
+	private void add_collapsed(LaneContainer container,
+	                           int           index)
+	{
+		var collapsed = new CollapsedLane(container);
+		collapsed.index = index;
+
+		d_collapsed.insert(container.to, (owned)collapsed);
+	}
+
+	private void collapse_lane(LaneContainer container,
+	                           int           index)
+	{
+		add_collapsed(container, index);
+
+		unowned SList<Commit> item = d_previous;
+
+		while (item != null)
+		{
+			var commit = item.data;
+			unowned SList<Lane> lns = commit.lanes;
+			unowned SList<Lane> lstlane = lns.nth(index);
+			unowned Lane lane = lstlane.data;
+
+			if (item.next != null)
+			{
+				var newindex = lane.from.data;
+
+				lns = commit.remove_lane(lane);
+
+				if (item.next.next != null)
+				{
+					update_merge_indices(lns, newindex, -1);
+				}
+
+				var mylane = commit.mylane;
+
+				if (mylane > index)
+				{
+					--commit.mylane;
+				}
+
+				index = newindex;
+			}
+			else
+			{
+				lane.tag |= Lane.Tag.END;
+				lane.boundary_id = container.to;
+			}
+
+			item = item.next;
+		}
+	}
+
+	private void collapse_lanes()
+	{
+		int index = 0;
+		unowned SList<LaneContainer> item = d_lanes;
+
+		while (item != null)
+		{
+			unowned LaneContainer container = item.data;
+
+			if (container.inactive != inactive_max + inactive_gap)
+			{
+				item = item.next;
+				++index;
+				continue;
+			}
+
+			collapse_lane(container, container.lane.from.data);
+			update_current_lane_merge_indices(index, -1);
+
+			unowned SList<LaneContainer> next = item.next;
+			d_lanes.remove_link(item);
+			item = next;
+		}
+	}
+
+	private int ensure_correct_index(Commit commit,
+	                                 int    index)
+	{
+		var len = commit.lanes.length();
+
+		if (index > len)
+		{
+			return (int)len;
+		}
+		else
+		{
+			return index;
+		}
+	}
+
+	private void update_lane_merge_indices(SList<int> from,
+	                                       int        index,
+	                                       int        direction)
+	{
+		while (from != null)
+		{
+			int idx = from.data;
+
+			if (idx > index || (direction > 0 && idx == index))
+			{
+				from.data = idx + direction;
+			}
+
+			from = from.next;
+		}
+	}
+
+	private void update_merge_indices(SList<Lane> lanes,
+	                                  int         index,
+	                                  int         direction)
+	{
+		foreach (unowned Lane lane in lanes)
+		{
+			update_lane_merge_indices(lane.from, index, direction);
+		}
+	}
+	private void update_current_lane_merge_indices(int index,
+	                                               int direction)
+	{
+		foreach (unowned LaneContainer container in d_lanes)
+		{
+			update_lane_merge_indices(container.lane.from,
+			                          index,
+			                          direction);
+		}
+	}
+
+	private void expand_lane(CollapsedLane lane)
+	{
+		var index = lane.index;
+		var ln = new Lane.with_color(lane.color);
+		var len = d_lanes.length();
+
+		if (index > len)
+		{
+			index = len;
+		}
+
+		var next = ensure_correct_index(d_previous.data, (int)index);
+
+		var container = new LaneContainer.with_color(lane.from,
+		                                             lane.to,
+		                                             lane.color);
+
+		update_current_lane_merge_indices((int)index, 1);
+
+		container.lane.from.prepend(next);
+		d_lanes.insert((owned)container, (int)index);
+
+		index = next;
+		uint cnt = 0;
+
+		unowned SList<Commit> ptr = d_previous;
+
+		while (ptr != null)
+		{
+			var commit = ptr.data;
+
+			if (cnt == inactive_collapse)
+			{
+				break;
+			}
+
+			// Insert new lane at the index
+			Lane copy = ln.copy();
+			unowned SList<Lane> lns = commit.lanes;
+
+			if (ptr.next == null || cnt + 1 == inactive_collapse)
+			{
+				copy.boundary_id = lane.from;
+				copy.tag |= Lane.Tag.START;
+			}
+			else
+			{
+				next = ensure_correct_index(ptr.next.data, (int)index);
+				copy.from.prepend(next);
+
+				update_merge_indices(lns, (int)index, 1);
+			}
+
+			commit.lanes.insert((owned)copy, (int)index);
+			lns = commit.lanes;
+
+			var mylane = commit.mylane;
+
+			if (mylane >= index)
+			{
+				++commit.mylane;
+			}
+
+			index = next;
+			++cnt;
+
+			ptr = ptr.next;
+		}
+	}
+
+	private void expand_lane_from_oid(Ggit.OId id)
+	{
+		unowned CollapsedLane? collapsed = d_collapsed.lookup(id);
+
+		if (collapsed != null)
+		{
+			expand_lane(collapsed);
+			d_collapsed.remove(id);
+		}
+	}
+
+	private void expand_lanes(Commit commit)
+	{
+		expand_lane_from_oid(commit.get_id());
+
+		var parents = commit.get_parents();
+
+		for (uint i = 0; i < parents.size(); ++i)
+		{
+			expand_lane_from_oid(parents.get_id(i));
+		}
+	}
+
+	private void init_next_layer()
+	{
+		int index = 0;
+
+		foreach (unowned LaneContainer container in d_lanes)
+		{
+			container.next(index++);
+		}
+	}
+
+	private unowned LaneContainer? find_lane_by_oid(Ggit.OId id,
+	                                                out int  pos)
+	{
+		int p = 0;
+		unowned SList<LaneContainer> ptr = d_lanes;
+
+		while (ptr != null)
+		{
+			unowned LaneContainer? container = ptr.data;
+
+			if (container != null &&
+			    id.equal(container.to))
+			{
+				pos = p;
+				return container;
+			}
+
+			++p;
+			ptr = ptr.next;
+		}
+
+		pos = -1;
+		return null;
+	}
+
+	private SList<Lane> lanes_list()
+	{
+		var ret = new SList<Lane>();
+
+		foreach (unowned LaneContainer container in d_lanes)
+		{
+			ret.prepend(container.lane.copy());
+		}
+
+		ret.reverse();
+		return ret;
+	}
+}
+
+}
+
+// ex:set ts=4 noet
diff --git a/libgitg/gitg-ref.vala b/libgitg/gitg-ref.vala
new file mode 100644
index 0000000..3914b07
--- /dev/null
+++ b/libgitg/gitg-ref.vala
@@ -0,0 +1,275 @@
+namespace Gitg
+{
+
+public enum RefType
+{
+	BRANCH,
+	REMOTE,
+	TAG,
+	STASH
+}
+
+/**
+ * Parse ref name into components.
+ *
+ * This class parses a refname and splits it into several components.
+ *
+ */
+public class ParsedRefName : Object
+{
+	private string d_shortname;
+	private string d_name;
+	private string d_remote_name;
+	private string d_remote_branch;
+
+	/**
+	 * The type of ref.
+	 */
+	public RefType rtype { get; private set; }
+
+	/**
+	 * The full name of the ref.
+	 */
+	public string name
+	{
+		owned get { return d_name; }
+	}
+
+	/**
+	 * The short name of the ref. This represents the name of the ref
+	 * without the information of the type of ref.
+	 */
+	public string shortname
+	{
+		owned get { return d_shortname; }
+	}
+
+	/**
+	 * The remote name of the ref (only for remote refs)
+	 */
+	public string? remote_name
+	{
+		owned get { return d_remote_name; }
+	}
+
+	/**
+	 * The remote branch name of the ref (only for remote refs)
+	 */
+	public string? remote_branch
+	{
+		owned get { return d_remote_branch; }
+	}
+
+	public ParsedRefName(string name)
+	{
+		parse_name(name);
+	}
+
+	private void parse_name(string name)
+	{
+		d_name = name;
+
+		string[] prefixes = {
+			"refs/heads/",
+			"refs/remotes/",
+			"refs/tags/",
+			"refs/stash"
+		};
+
+		d_shortname = name;
+
+		for (var i = 0; i < prefixes.length; ++i)
+		{
+			if (!d_name.has_prefix(prefixes[i]))
+			{
+				continue;
+			}
+
+			rtype = (RefType)i;
+
+			if (rtype == RefType.STASH)
+			{
+				d_shortname = "stash";
+			}
+			else
+			{
+				d_shortname = d_name[prefixes[i].length:d_name.length];
+			}
+
+			if (rtype == RefType.REMOTE)
+			{
+				var pos = d_shortname.index_of_char('/');
+
+				if (pos != -1)
+				{
+					d_remote_name = d_shortname.substring(0, pos);
+					d_remote_branch = d_shortname.substring(pos + 1);
+				}
+				else
+				{
+					d_remote_name = d_shortname;
+				}
+			}
+		}
+	}
+}
+
+public class Ref : Ggit.Ref
+{
+	private List<Ref>? d_pushes;
+
+	private static Regex? s_remote_key_regex;
+	private ParsedRefName d_parsed_name;
+
+	public ParsedRefName parsed_name
+	{
+		owned get
+		{
+			if (d_parsed_name == null)
+			{
+				d_parsed_name = new ParsedRefName(get_name());
+			}
+
+			return d_parsed_name;
+		}
+	}
+
+	public new Gitg.Repository get_owner()
+	{
+		return (Gitg.Repository)base.get_owner();
+	}
+
+	private void add_push_ref(string spec)
+	{
+		Gitg.Ref rf;
+
+		try
+		{
+			rf = get_owner().lookup_reference(spec);
+		} catch { return; }
+
+		if (d_pushes.find_custom(rf, (a, b) => {
+			return a.get_name().ascii_casecmp(b.get_name());
+		}) == null)
+		{
+			d_pushes.append(rf);
+		}
+	}
+
+	private void add_branch_configured_push(Ggit.Config cfg)
+	{
+		string remote;
+		string merge;
+
+		try
+		{
+			remote = cfg.get_string(@"branch.$(parsed_name.shortname).remote");
+			merge = cfg.get_string(@"branch.$(parsed_name.shortname).merge");
+		} catch { return; }
+
+		var nm = new ParsedRefName(merge);
+
+		add_push_ref(@"refs/remotes/$remote/$(nm.shortname)");
+	}
+
+	private void add_remote_configured_push(Ggit.Config cfg)
+	{
+		Regex valregex;
+
+		try
+		{
+			valregex = new Regex("^%s:(.*)".printf(Regex.escape_string(get_name())));
+
+			if (s_remote_key_regex == null)
+			{
+				s_remote_key_regex = new Regex("remote\\.(.*)\\.push");
+			}
+
+			cfg.match_foreach(s_remote_key_regex, (info, val) => {
+				MatchInfo vinfo;
+
+				if (!valregex.match(val, 0, out vinfo))
+				{
+					return 0;
+				}
+
+				var rname = info.fetch(1);
+				var pref = vinfo.fetch(1);
+
+				add_push_ref(@"refs/remotes/$rname/$pref");
+				return 0;
+			});
+
+		} catch { return; }
+	}
+
+	private void add_branch_same_name_push(Ggit.Config cfg)
+	{
+		string remote;
+
+		try
+		{
+			remote = cfg.get_string(@"branch.$(parsed_name.shortname).remote");
+		} catch { return; }
+
+		add_push_ref(@"refs/remotes/$remote/$(parsed_name.shortname)");
+	}
+
+	private void compose_pushes()
+	{
+		d_pushes = new List<Ref>();
+
+		Ggit.Config cfg;
+
+		try
+		{
+			cfg = get_owner().get_config();
+		} catch { return; }
+
+		/* The possible refspecs of a local $ref (branch) are resolved in the
+		 * following order (duplicates are removed automatically):
+		 *
+		 * 1) Branch configured remote and merge (git push):
+		 *
+		 *    Remote: branch.<name>.remote
+		 *    Spec:   branch.<name>.merge
+		 *
+		 * 2) Remote configured matching push refspec:
+		 *    For each remote.<name>.push matching ${ref.name}:<spec>
+		 *
+		 *    Remote: <name>
+		 *    Spec:   <spec>
+		 *
+		 * 3) Remote branch with the same name
+		 *
+		 *    Remote: branch.<name>.remote
+		 *    Spec:   ${ref.name}
+		 */
+
+		// Branch configured remote and merge
+		add_branch_configured_push(cfg);
+
+		// Remote configured push spec
+		add_remote_configured_push(cfg);
+
+		// Same name push
+		add_branch_same_name_push(cfg);
+	}
+
+	public List<Ref> pushes
+	{
+		get
+		{
+			if (d_pushes == null)
+			{
+				compose_pushes();
+			}
+
+			return d_pushes;
+		}
+	}
+}
+
+}
+
+// ex:set ts=4 noet
diff --git a/libgitg/gitg-repository.vala b/libgitg/gitg-repository.vala
new file mode 100644
index 0000000..927f878
--- /dev/null
+++ b/libgitg/gitg-repository.vala
@@ -0,0 +1,38 @@
+namespace Gitg
+{
+
+public class Repository : Ggit.Repository
+{
+	public Repository(File location, File? workdir) throws Error
+	{
+		Object(location: location,
+		       workdir: workdir);
+
+		((Initable)this).init(null);
+	}
+
+	// Wrappers for Gitg.Ref
+	public new Ref lookup_reference(string name) throws Error
+	{
+		return base.lookup_reference(name) as Ref;
+	}
+
+	public new Ref create_reference(string name, Ggit.OId oid) throws Error
+	{
+		return base.create_reference(name, oid) as Ref;
+	}
+
+	public new Ref create_symbolic_reference(string name, string target) throws Error
+	{
+		return base.create_symbolic_reference(name, target) as Ref;
+	}
+
+	public new Gitg.Ref get_head() throws Error
+	{
+		return base.get_head() as Ref;
+	}
+}
+
+}
+
+// ex:set ts=4 noet
diff --git a/libgitg/libgitg-1.0.pc.in b/libgitg/libgitg-1.0.pc.in
new file mode 100644
index 0000000..59ff1fe
--- /dev/null
+++ b/libgitg/libgitg-1.0.pc.in
@@ -0,0 +1,11 @@
+prefix= prefix@
+exec_prefix= exec_prefix@
+libdir= libdir@
+includedir= includedir@
+
+Name: @PACKAGE_NAME@
+Description: gitg library
+Version: @PACKAGE_VERSION@
+Requires: libgit2-glib-1.0 glib-2.0 gobject-2.0 gmodule-2.0 gio-2.0 gio-unix-2.0 gthread-2.0
+Libs: -L${libdir} -lgitg-1.0
+Cflags: -I${includedir}/libgitg-1.0
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
new file mode 100644
index 0000000..32e0a3a
--- /dev/null
+++ b/plugins/Makefile.am
@@ -0,0 +1,3 @@
+SUBDIRS = dash history
+
+-include $(top_srcdir)/git.mk
diff --git a/plugins/dash/Makefile.am b/plugins/dash/Makefile.am
new file mode 100644
index 0000000..3351847
--- /dev/null
+++ b/plugins/dash/Makefile.am
@@ -0,0 +1,52 @@
+INCLUDES =								\
+	-I$(top_srcdir)							\
+	-I$(srcdir)							\
+	$(GITG_PLUGIN_CFLAGS)						\
+	$(WARN_CFLAGS)							\
+	-DDATADIR=\""$(datadir)"\"					\
+	-DLIBDIR=\""$(libdir)"\"
+
+plugindir = $(GITG_PLUGIN_LIBDIR)
+plugin_LTLIBRARIES = libdash.la
+plugin_DATA = dash.plugin
+
+VALAFLAGS = $(GITG_PLUGIN_VALAFLAGS)
+VALA_SOURCES =								\
+	gitg-dash.vala							\
+	gitg-dash-navigation.vala
+
+libdash_la_LDFLAGS = $(GITG_PLUGIN_LIBTOOL_FLAGS)
+
+libdash_la_LIBADD = $(GITG_PLUGIN_LIBS)
+libdash_la_CFLAGS = -w
+
+libdash_la_SOURCES = 				\
+	$(VALA_SOURCES)				\
+	gitg-dash-resources.c
+
+BUILT_SOURCES = 						\
+	gitg-dash-resources.c					\
+	gitg-dash-resources.h
+
+gitg-dash-resources.c: resources/resources.xml $(shell $(GLIB_COMPILE_RESOURCES) --generate-dependencies --sourcedir $(srcdir)/resources resources/resources.xml)
+	$(GLIB_COMPILE_RESOURCES) --generate-source 	\
+		--sourcedir $(srcdir)/resources 	\
+		--target "$@" "$<"
+
+gitg-dash-resources.h: resources/resources.xml $(shell $(GLIB_COMPILE_RESOURCES) --generate-dependencies --sourcedir $(srcdir)/resources resources/resources.xml)
+	$(GLIB_COMPILE_RESOURCES) --generate-header 	\
+		--sourcedir $(srcdir)/resources 	\
+		--target "$@" "$<"
+
+EXTRA_DIST = $(plugin_DATA)			\
+	resources/resources.xml			\
+	$(shell $(GLIB_COMPILE_RESOURCES) --generate-dependencies --sourcedir $(srcdir)/resources resources/resources.xml)
+
+CLEANFILES = 					\
+	$(VALA_SOURCES:.vala=.c)		\
+	$(BUILT_SOURCES)
+
+install-data-hook:
+	rm -f $(GITG_PLUGIN_LIBDIR)/libdash.la
+
+-include $(top_srcdir)/git.mk
diff --git a/plugins/dash/dash.plugin b/plugins/dash/dash.plugin
new file mode 100644
index 0000000..dad39a6
--- /dev/null
+++ b/plugins/dash/dash.plugin
@@ -0,0 +1,11 @@
+[Plugin]
+Loader=C
+Module=dash
+Name=Dash
+Description=gitg Dash
+Authors=Jesse van den Kieboom <jessevdk gnome org>
+Copyright=Copyright  2012 Jesse van den Kieboom
+Website=
+Hidden=1
+Builtin=1
+Version=1.0
diff --git a/plugins/dash/gitg-dash-navigation.vala b/plugins/dash/gitg-dash-navigation.vala
new file mode 100644
index 0000000..e74049d
--- /dev/null
+++ b/plugins/dash/gitg-dash-navigation.vala
@@ -0,0 +1,93 @@
+namespace GitgDash
+{
+	private class Navigation : Object, GitgExt.Navigation
+	{
+		public GitgExt.Application? application { owned get; construct; }
+
+		public signal void show_open();
+		public signal void show_create();
+		public signal void show_recent(string uri);
+		public signal void activate_recent(string uri);
+
+		public Navigation(GitgExt.Application app)
+		{
+			Object(application: app);
+		}
+
+		public void populate(GitgExt.NavigationTreeModel model)
+		{
+			model.begin_header("Repository", null)
+			     .append_default("Open", "document-open-symbolic", (c) => { show_open(); })
+			     .append("Create", "list-add-symbolic", (c) => { show_create(); })
+			     .end_header();
+
+			model.begin_header("Recent", null);
+
+			var manager = Gtk.RecentManager.get_default();
+			var list = new List<Gtk.RecentInfo>();
+
+			Gee.HashSet<string> uris = new Gee.HashSet<string>();
+
+			foreach (var item in manager.get_items())
+			{
+				if (!item.has_application("gitg") ||
+				    !item.exists())
+				{
+					continue;
+				}
+
+				if (uris.add(item.get_uri()))
+				{
+					list.prepend(item);
+				}
+			}
+
+			list.sort((a, b) => {
+				if (a.get_visited() < b.get_visited())
+				{
+					return 1;
+				}
+				else if (a.get_visited() > b.get_visited())
+				{
+					return -1;
+				}
+				else
+				{
+					return 0;
+				}
+			});
+
+			foreach (var item in list)
+			{
+				string uri = item.get_uri();
+
+				model.append(item.get_display_name(),
+				             null,
+				             (c) => {
+					if (c == 1)
+					{
+						show_recent(uri);
+					}
+					else
+					{
+						activate_recent(uri);
+					}
+				});
+			}
+
+			model.end_header();
+		}
+
+		public GitgExt.NavigationSide navigation_side
+		{
+			get { return GitgExt.NavigationSide.LEFT; }
+		}
+
+		public bool available
+		{
+			get { return true; }
+		}
+	}
+}
+
+// ex: ts=4 noet
diff --git a/plugins/dash/gitg-dash.vala b/plugins/dash/gitg-dash.vala
new file mode 100644
index 0000000..668a776
--- /dev/null
+++ b/plugins/dash/gitg-dash.vala
@@ -0,0 +1,285 @@
+namespace GitgDash
+{
+	// Do this to pull in config.h before glib.h (for gettext...)
+	private const string version = Gitg.Config.VERSION;
+
+	public class View : Object, GitgExt.View
+	{
+		public GitgExt.Application? application { owned get; construct; }
+		private Gtk.Notebook d_main;
+
+		private Gtk.Widget? d_open;
+		private int d_openidx;
+
+		private Gtk.Widget? d_create;
+		private int d_createidx;
+
+		private Gtk.Widget? d_recent;
+		private Gtk.Label? d_recent_path;
+		private Gtk.Label? d_recent_last_used;
+		private Gtk.Label? d_recent_current_branch;
+		private File? d_recent_current_file;
+		private int d_recentidx;
+
+		private HashTable<File, Gitg.Repository> d_repos;
+
+		private File? d_open_folder;
+
+		construct
+		{
+			d_main = new Gtk.Notebook();
+			d_main.set_show_tabs(false);
+			d_main.show();
+
+			d_repos = new HashTable<File, Gitg.Repository>(File.hash, File.equal);
+		}
+
+		public string id
+		{
+			owned get { return "/org/gnome/gitg/Views/Dash"; }
+		}
+
+		public bool is_available()
+		{
+			// The dash is always available
+			return true;
+		}
+
+		public string display_name
+		{
+			owned get { return "Dashboard"; }
+		}
+
+		public Icon? icon
+		{
+			owned get
+			{
+				return new ThemedIcon("document-open-recent-symbolic");
+			}
+		}
+
+		public Gtk.Widget? widget
+		{
+			owned get
+			{
+				return d_main;
+			}
+		}
+
+		public GitgExt.Navigation? navigation
+		{
+			owned get
+			{
+				var ret = new Navigation(application);
+
+				ret.show_open.connect(show_open);
+				ret.show_create.connect(show_create);
+				ret.show_recent.connect(show_recent);
+				ret.activate_recent.connect(activate_recent);
+
+				return ret;
+			}
+		}
+
+		public bool is_default_for(GitgExt.ViewAction action)
+		{
+			return application.repository == null;
+		}
+
+		private Gee.HashMap<string, Object>? from_builder(string path, string[] ids)
+		{
+			var builder = new Gtk.Builder();
+
+			try
+			{
+				builder.add_from_resource("/org/gnome/gitg/dash/" + path);
+			}
+			catch (Error e)
+			{
+				warning("Failed to load ui: %s", e.message);
+				return null;
+			}
+
+			Gee.HashMap<string, Object> ret = new Gee.HashMap<string, Object>();
+
+			foreach (string id in ids)
+			{
+				ret[id] = builder.get_object(id);
+			}
+
+			return ret;
+		}
+
+		private void connect_chooser_folder(Gtk.FileChooser ch)
+		{
+			if (d_open_folder == null)
+			{
+				var path = Environment.get_home_dir();
+				d_open_folder = File.new_for_path(path);
+			}
+
+			ch.unmap.connect((w) => {
+				d_open_folder = ch.get_current_folder_file();
+			});
+
+			ch.map.connect((w) => {
+				if (d_open_folder != null)
+				{
+					try
+					{
+						ch.set_current_folder_file(d_open_folder);
+					} catch {};
+				}
+			});
+		}
+
+		public void show_open()
+		{
+			if (d_open == null)
+			{
+				var ret = from_builder("view-open.ui", {"view",
+				                                        "file_chooser",
+				                                        "button_open"});
+
+				d_open = ret["view"] as Gtk.Widget;
+
+				var ch = ret["file_chooser"] as Gtk.FileChooser;
+				connect_chooser_folder(ch);
+
+				(ret["button_open"] as Gtk.Button).clicked.connect((b) => {
+					application.open(ch.get_current_folder_file());
+				});
+
+				d_openidx = d_main.append_page(d_open, null);
+			}
+
+			d_main.set_current_page(d_openidx);
+		}
+
+		public void show_create()
+		{
+			if (d_create == null)
+			{
+				var ret = from_builder("view-create.ui", {"view",
+				                                          "file_chooser",
+				                                          "button_create"});
+
+				d_create = ret["view"] as Gtk.Widget;
+
+				var ch = ret["file_chooser"] as Gtk.FileChooser;
+				connect_chooser_folder(ch);
+
+				(ret["button_create"] as Gtk.Button).clicked.connect((b) => {
+					application.create(ch.get_current_folder_file());
+				});
+
+				d_createidx = d_main.append_page(d_create, null);
+			}
+
+			d_main.set_current_page(d_createidx);
+		}
+
+		public void show_recent(string uri)
+		{
+			var manager = Gtk.RecentManager.get_default();
+			Gtk.RecentInfo? info = null;
+
+			foreach (var item in manager.get_items())
+			{
+				if (item.get_uri() == uri &&
+				    item.has_application("gitg") && item.exists())
+				{
+					info = item;
+					break;
+				}
+			}
+
+			if (info == null)
+			{
+				return;
+			}
+
+			File f = File.new_for_uri(info.get_uri());
+			Gitg.Repository? repo;
+
+			if (!d_repos.lookup_extended(f, null, out repo))
+			{
+				// Try to open the repo
+				try
+				{
+					repo = new Gitg.Repository(f, null);
+
+					d_repos.insert(f, repo);
+				}
+				catch
+				{
+					return;
+				}
+			}
+
+			if (repo == null)
+			{
+				return;
+			}
+
+			if (d_recent == null)
+			{
+				var ret = from_builder("view-recent.ui", {"view",
+				                                          "label_path_i",
+				                                          "label_last_used_i",
+				                                          "label_current_branch_i",
+				                                          "button_open"});
+
+				d_recent = ret["view"] as Gtk.Widget;
+				d_recent_path = ret["label_path_i"] as Gtk.Label;
+				d_recent_last_used = ret["label_last_used_i"] as Gtk.Label;
+				d_recent_current_branch = ret["label_current_branch_i"] as Gtk.Label;
+
+				(ret["button_open"] as Gtk.Button).clicked.connect((b) => {
+					application.open(d_recent_current_file);
+				});
+
+				d_recentidx = d_main.append_page(d_recent, null);
+			}
+
+			d_recent_path.label = Filename.display_name(f.get_path());
+			d_recent_current_file = f;
+
+			var dt = new DateTime.from_unix_utc(info.get_visited());
+			d_recent_last_used.label = dt.format("%c");
+
+			d_recent_current_branch.label = _("(no branch)");
+
+			try
+			{
+				var r = repo.get_head();
+
+				if (r != null)
+				{
+					d_recent_current_branch.label = r.parsed_name.shortname;
+				}
+			}
+			catch {}
+
+			d_main.set_current_page(d_recentidx);
+		}
+
+		public void activate_recent(string uri)
+		{
+			File f = File.new_for_uri(uri);
+
+			application.open(f);
+		}
+	}
+}
+
+[ModuleInit]
+public void peas_register_types(TypeModule module)
+{
+	Peas.ObjectModule mod = module as Peas.ObjectModule;
+
+	mod.register_extension_type(typeof(GitgExt.View),
+	                            typeof(GitgDash.View));
+}
+
+// ex: ts=4 noet
diff --git a/plugins/dash/resources/resources.xml b/plugins/dash/resources/resources.xml
new file mode 100644
index 0000000..6139db8
--- /dev/null
+++ b/plugins/dash/resources/resources.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+  <gresource prefix="/org/gnome/gitg/dash">
+    <file>view-open.ui</file>
+    <file>view-create.ui</file>
+    <file>view-recent.ui</file>
+  </gresource>
+</gresources>
+
+<!-- ex: et ts=2 -->
diff --git a/plugins/dash/resources/view-create.ui b/plugins/dash/resources/view-create.ui
new file mode 100644
index 0000000..c68addb
--- /dev/null
+++ b/plugins/dash/resources/view-create.ui
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.3 -->
+  <!-- interface-requires gitg 0.2 -->
+  <object class="GtkGrid" id="view">
+    <property name="visible">True</property>
+    <property name="row-spacing">6</property>
+    <property name="vexpand">True</property>
+    <property name="hexpand">True</property>
+    <property name="orientation">vertical</property>
+    <child>
+      <object class="GtkLabel" id="label_title">
+        <property name="visible">True</property>
+        <property name="hexpand">True</property>
+        <property name="label" translatable="yes">Create repository</property>
+        <property name="xalign">0</property>
+        <style>
+          <class name="title"/>
+        </style>
+      </object>
+      <packing>
+        <property name="left_attach">0</property>
+        <property name="top_attach">0</property>
+        <property name="width">1</property>
+        <property name="height">1</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkLabel" id="label_description">
+        <property name="visible">True</property>
+        <property name="hexpand">True</property>
+        <property name="label" translatable="yes">Select a folder in which you want to create a new git repository.</property>
+        <property name="xalign">0</property>
+        <style>
+          <class name="description"/>
+        </style>
+      </object>
+      <packing>
+        <property name="left_attach">0</property>
+        <property name="top_attach">1</property>
+        <property name="width">1</property>
+        <property name="height">1</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkFileChooserWidget" id="file_chooser">
+        <property name="visible">True</property>
+        <property name="local-only">True</property>
+        <property name="vexpand">True</property>
+        <property name="hexpand">True</property>
+        <property name="action">select-folder</property>
+        <property name="create-folders">False</property>
+      </object>
+      <packing>
+        <property name="left_attach">0</property>
+        <property name="top_attach">2</property>
+        <property name="width">1</property>
+        <property name="height">1</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkGrid" id="buttons">
+        <property name="visible">True</property>
+        <property name="hexpand">True</property>
+        <property name="halign">end</property>
+        <property name="orientation">horizontal</property>
+        <child>
+          <object class="GtkButton" id="button_create">
+            <property name="visible">True</property>
+            <property name="label" translatable="yes">Create</property>
+            <property name="use-stock">True</property>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">0</property>
+            <property name="width">1</property>
+            <property name="height">1</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="left_attach">0</property>
+        <property name="top_attach">3</property>
+        <property name="width">1</property>
+        <property name="height">1</property>
+      </packing>
+    </child>
+  </object>
+</interface>
+
+<!-- ex:set ts=2 et: -->
diff --git a/plugins/dash/resources/view-open.ui b/plugins/dash/resources/view-open.ui
new file mode 100644
index 0000000..199b9c3
--- /dev/null
+++ b/plugins/dash/resources/view-open.ui
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.3 -->
+  <!-- interface-requires gitg 0.2 -->
+  <object class="GtkGrid" id="view">
+    <property name="visible">True</property>
+    <property name="row-spacing">6</property>
+    <property name="vexpand">True</property>
+    <property name="hexpand">True</property>
+    <property name="orientation">vertical</property>
+    <child>
+      <object class="GtkLabel" id="label_title">
+        <property name="visible">True</property>
+        <property name="hexpand">True</property>
+        <property name="label" translatable="yes">Open repository</property>
+        <property name="xalign">0</property>
+        <style>
+          <class name="title"/>
+        </style>
+      </object>
+      <packing>
+        <property name="left_attach">0</property>
+        <property name="top_attach">0</property>
+        <property name="width">1</property>
+        <property name="height">1</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkLabel" id="label_description">
+        <property name="visible">True</property>
+        <property name="hexpand">True</property>
+        <property name="label" translatable="yes">Select a folder to open the corresponding git repository.</property>
+        <property name="xalign">0</property>
+        <style>
+          <class name="description"/>
+        </style>
+      </object>
+      <packing>
+        <property name="left_attach">0</property>
+        <property name="top_attach">1</property>
+        <property name="width">1</property>
+        <property name="height">1</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkFileChooserWidget" id="file_chooser">
+        <property name="visible">True</property>
+        <property name="local-only">True</property>
+        <property name="vexpand">True</property>
+        <property name="hexpand">True</property>
+        <property name="action">select-folder</property>
+        <property name="create-folders">False</property>
+      </object>
+      <packing>
+        <property name="left_attach">0</property>
+        <property name="top_attach">2</property>
+        <property name="width">1</property>
+        <property name="height">1</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkGrid" id="buttons">
+        <property name="visible">True</property>
+        <property name="hexpand">True</property>
+        <property name="halign">end</property>
+        <property name="orientation">horizontal</property>
+        <child>
+          <object class="GtkButton" id="button_open">
+            <property name="visible">True</property>
+            <property name="label">gtk-open</property>
+            <property name="use-stock">True</property>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">0</property>
+            <property name="width">1</property>
+            <property name="height">1</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="left_attach">0</property>
+        <property name="top_attach">3</property>
+        <property name="width">1</property>
+        <property name="height">1</property>
+      </packing>
+    </child>
+  </object>
+</interface>
+
+<!-- ex:set ts=2 et: -->
diff --git a/plugins/dash/resources/view-recent.ui b/plugins/dash/resources/view-recent.ui
new file mode 100644
index 0000000..80e824b
--- /dev/null
+++ b/plugins/dash/resources/view-recent.ui
@@ -0,0 +1,173 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.3 -->
+  <object class="GtkGrid" id="view">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="hexpand">True</property>
+    <property name="vexpand">True</property>
+    <property name="row_spacing">6</property>
+    <property name="column_spacing">12</property>
+    <child>
+      <object class="GtkLabel" id="label_title">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="hexpand">True</property>
+        <property name="xalign">0</property>
+        <property name="label" translatable="yes">Recent repository</property>
+        <style>
+          <class name="title"/>
+        </style>
+      </object>
+      <packing>
+        <property name="left_attach">0</property>
+        <property name="top_attach">0</property>
+        <property name="width">2</property>
+        <property name="height">1</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkLabel" id="label_description">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="hexpand">True</property>
+        <property name="xalign">0</property>
+        <property name="label" translatable="yes">Open a recently used repository.</property>
+        <style>
+          <class name="description"/>
+        </style>
+      </object>
+      <packing>
+        <property name="left_attach">0</property>
+        <property name="top_attach">1</property>
+        <property name="width">2</property>
+        <property name="height">1</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkLabel" id="label_path">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="xalign">0</property>
+        <property name="label" translatable="yes">Path:</property>
+        <style>
+          <class name="grid_title"/>
+        </style>
+      </object>
+      <packing>
+        <property name="left_attach">0</property>
+        <property name="top_attach">2</property>
+        <property name="width">1</property>
+        <property name="height">1</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkLabel" id="label_path_i">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="hexpand">True</property>
+        <property name="xalign">0</property>
+        <property name="label"></property>
+      </object>
+      <packing>
+        <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="GtkLabel" id="label_last_used">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="xalign">0</property>
+        <property name="label" translatable="yes">Last used:</property>
+        <style>
+          <class name="grid_title"/>
+        </style>
+      </object>
+      <packing>
+        <property name="left_attach">0</property>
+        <property name="top_attach">3</property>
+        <property name="width">1</property>
+        <property name="height">1</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkLabel" id="label_last_used_i">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="hexpand">True</property>
+        <property name="xalign">0</property>
+        <property name="label"></property>
+      </object>
+      <packing>
+        <property name="left_attach">1</property>
+        <property name="top_attach">3</property>
+        <property name="width">1</property>
+        <property name="height">1</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkLabel" id="label_current_branch">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="xalign">0</property>
+        <property name="label" translatable="yes">Current branch:</property>
+        <style>
+          <class name="grid_title"/>
+        </style>
+      </object>
+      <packing>
+        <property name="left_attach">0</property>
+        <property name="top_attach">4</property>
+        <property name="width">1</property>
+        <property name="height">1</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkLabel" id="label_current_branch_i">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="hexpand">True</property>
+        <property name="xalign">0</property>
+        <property name="label"></property>
+      </object>
+      <packing>
+        <property name="left_attach">1</property>
+        <property name="top_attach">4</property>
+        <property name="width">1</property>
+        <property name="height">1</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkGrid" id="buttons">
+        <property name="visible">True</property>
+        <property name="hexpand">True</property>
+        <property name="vexpand">True</property>
+        <property name="halign">end</property>
+        <property name="valign">end</property>
+        <property name="orientation">horizontal</property>
+        <child>
+          <object class="GtkButton" id="button_open">
+            <property name="visible">True</property>
+            <property name="label">gtk-open</property>
+            <property name="use-stock">True</property>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">0</property>
+            <property name="width">1</property>
+            <property name="height">1</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="left_attach">0</property>
+        <property name="top_attach">5</property>
+        <property name="width">2</property>
+        <property name="height">1</property>
+      </packing>
+    </child>
+  </object>
+</interface>
diff --git a/plugins/history/Makefile.am b/plugins/history/Makefile.am
new file mode 100644
index 0000000..099c165
--- /dev/null
+++ b/plugins/history/Makefile.am
@@ -0,0 +1,52 @@
+INCLUDES =								\
+	-I$(top_srcdir)							\
+	-I$(srcdir)							\
+	$(GITG_PLUGIN_CFLAGS)						\
+	$(WARN_CFLAGS)							\
+	-DDATADIR=\""$(datadir)"\"					\
+	-DLIBDIR=\""$(libdir)"\"
+
+plugindir = $(GITG_PLUGIN_LIBDIR)
+plugin_LTLIBRARIES = libhistory.la
+plugin_DATA = history.plugin
+
+VALAFLAGS = $(GITG_PLUGIN_VALAFLAGS)
+VALA_SOURCES =								\
+	gitg-history.vala							\
+	gitg-history-navigation.vala
+
+libhistory_la_LDFLAGS = $(GITG_PLUGIN_LIBTOOL_FLAGS)
+
+libhistory_la_LIBADD = $(GITG_PLUGIN_LIBS)
+libhistory_la_CFLAGS = -w
+
+libhistory_la_SOURCES = 				\
+	$(VALA_SOURCES)				\
+	gitg-history-resources.c
+
+BUILT_SOURCES = 						\
+	gitg-history-resources.c					\
+	gitg-history-resources.h
+
+gitg-history-resources.c: resources/resources.xml $(shell $(GLIB_COMPILE_RESOURCES) --generate-dependencies --sourcedir $(srcdir)/resources resources/resources.xml)
+	$(GLIB_COMPILE_RESOURCES) --generate-source 	\
+		--sourcedir $(srcdir)/resources 	\
+		--target "$@" "$<"
+
+gitg-history-resources.h: resources/resources.xml $(shell $(GLIB_COMPILE_RESOURCES) --generate-dependencies --sourcedir $(srcdir)/resources resources/resources.xml)
+	$(GLIB_COMPILE_RESOURCES) --generate-header 	\
+		--sourcedir $(srcdir)/resources 	\
+		--target "$@" "$<"
+
+EXTRA_DIST = $(plugin_DATA)			\
+	resources/resources.xml			\
+	$(shell $(GLIB_COMPILE_RESOURCES) --generate-dependencies --sourcedir $(srcdir)/resources resources/resources.xml)
+
+CLEANFILES = 					\
+	$(VALA_SOURCES:.vala=.c)		\
+	$(BUILT_SOURCES)
+
+install-data-hook:
+	rm -f $(GITG_PLUGIN_LIBDIR)/libhistory.la
+
+-include $(top_srcdir)/git.mk
diff --git a/plugins/history/gitg-history-navigation.vala b/plugins/history/gitg-history-navigation.vala
new file mode 100644
index 0000000..b18753c
--- /dev/null
+++ b/plugins/history/gitg-history-navigation.vala
@@ -0,0 +1,123 @@
+namespace GitgHistory
+{
+	private class Navigation : Object, GitgExt.Navigation
+	{
+		public GitgExt.Application? application { owned get; construct; }
+
+		public Navigation(GitgExt.Application app)
+		{
+			Object(application: app);
+		}
+
+		private static int sort_refs(Gitg.Ref a, Gitg.Ref b)
+		{
+			return a.parsed_name.shortname.ascii_casecmp(b.parsed_name.shortname);
+		}
+
+		public void populate(GitgExt.NavigationTreeModel model)
+		{
+			var repo = application.repository;
+
+			List<Gitg.Ref> branches = new List<Gitg.Ref>();
+			HashTable<string, List<Gitg.Ref>> remotes;
+			List<string> remotenames = new List<string>();
+
+			remotes = new HashTable<string, List<Gitg.Ref>>(str_hash, str_equal);
+
+			try
+			{
+				repo.references_foreach(Ggit.RefType.LISTALL, (nm) => {
+					Gitg.Ref? r;
+
+					try
+					{
+						r = repo.lookup_reference(nm);
+					} catch { return 0; }
+
+					if (r.parsed_name.rtype == Gitg.RefType.BRANCH)
+					{
+						branches.insert_sorted(r, sort_refs);
+					}
+					else if (r.parsed_name.rtype == Gitg.RefType.REMOTE)
+					{
+						unowned List<Gitg.Ref> lst;
+
+						string rname = r.parsed_name.remote_name;
+
+						if (!remotes.lookup_extended(rname, null, out lst))
+						{
+							List<Gitg.Ref> nlst = new List<Gitg.Ref>();
+							nlst.prepend(r);
+
+							remotes.insert(rname, (owned)nlst);
+							remotenames.insert_sorted(rname, (a, b) => a.ascii_casecmp(b));
+						}
+						else
+						{
+							/*unowned List<Gitg.Ref> start = lst;
+
+							lst.insert_sorted(r, sort_refs);
+
+							if (lst != start)
+							{
+								remotes.insert(rname, lst.copy());
+							}*/
+						}
+					}
+
+					return 0;
+				});
+			} catch {}
+
+			Gitg.Ref? head = null;
+
+			try
+			{
+				head = repo.get_head();
+			} catch {}
+
+			// Branches
+			model.begin_header("Branches", null);
+
+			foreach (var item in branches)
+			{
+				if (head != null && item.get_id().equal(head.get_id()))
+				{
+					model.append_default(item.parsed_name.shortname,
+					                     "object-select-symbolic",
+					                     null);
+				}
+				else
+				{
+					model.append(item.parsed_name.shortname, null, null);
+				}
+			}
+
+			model.end_header();
+
+			// Remotes
+			model.begin_header("Remotes", "network-server-symbolic");
+
+			foreach (var rname in remotenames)
+			{
+				//model.append(item.parsed_name.remote_branch, null, null);
+			}
+
+			model.end_header();
+
+			// Tags
+		}
+
+		public GitgExt.NavigationSide navigation_side
+		{
+			get { return GitgExt.NavigationSide.LEFT; }
+		}
+
+		public bool available
+		{
+			get { return true; }
+		}
+	}
+}
+
+// ex: ts=4 noet
diff --git a/plugins/history/gitg-history.vala b/plugins/history/gitg-history.vala
new file mode 100644
index 0000000..8a03455
--- /dev/null
+++ b/plugins/history/gitg-history.vala
@@ -0,0 +1,103 @@
+namespace GitgHistory
+{
+	// Do this to pull in config.h before glib.h (for gettext...)
+	private const string version = Gitg.Config.VERSION;
+
+	public class View : Object, GitgExt.View
+	{
+		public GitgExt.Application? application { owned get; construct; }
+
+		private Gtk.Widget d_main;
+
+		public string id
+		{
+			owned get { return "/org/gnome/gitg/Views/History"; }
+		}
+
+		public bool is_available()
+		{
+			// The history view is available only when there is a repository
+			return application.repository != null;
+		}
+
+		public string display_name
+		{
+			owned get { return "History"; }
+		}
+
+		public Icon? icon
+		{
+			owned get { return new ThemedIcon("view-list-symbolic"); }
+		}
+
+		public Gtk.Widget? widget
+		{
+			owned get
+			{
+				if (d_main == null)
+				{
+					build_ui();
+				}
+
+				return d_main;
+			}
+		}
+
+		public GitgExt.Navigation? navigation
+		{
+			owned get
+			{
+				var ret = new Navigation(application);
+
+				return ret;
+			}
+		}
+
+		public bool is_default_for(GitgExt.ViewAction action)
+		{
+			return application.repository != null && action == GitgExt.ViewAction.HISTORY;
+		}
+
+		private void build_ui()
+		{
+			var ret = from_builder("view-history.ui", {"view"});
+
+			d_main = ret["view"] as Gtk.Widget;
+		}
+
+		private Gee.HashMap<string, Object>? from_builder(string path, string[] ids)
+		{
+			var builder = new Gtk.Builder();
+
+			try
+			{
+				builder.add_from_resource("/org/gnome/gitg/history/" + path);
+			}
+			catch (Error e)
+			{
+				warning("Failed to load ui: %s", e.message);
+				return null;
+			}
+
+			Gee.HashMap<string, Object> ret = new Gee.HashMap<string, Object>();
+
+			foreach (string id in ids)
+			{
+				ret[id] = builder.get_object(id);
+			}
+
+			return ret;
+		}
+	}
+}
+
+[ModuleInit]
+public void peas_register_types(TypeModule module)
+{
+	Peas.ObjectModule mod = module as Peas.ObjectModule;
+
+	mod.register_extension_type(typeof(GitgExt.View),
+	                            typeof(GitgHistory.View));
+}
+
+// ex: ts=4 noet
diff --git a/plugins/history/history.plugin b/plugins/history/history.plugin
new file mode 100644
index 0000000..33d800a
--- /dev/null
+++ b/plugins/history/history.plugin
@@ -0,0 +1,11 @@
+[Plugin]
+Loader=C
+Module=history
+Name=History
+Description=gitg History
+Authors=Jesse van den Kieboom <jessevdk gnome org>
+Copyright=Copyright  2012 Jesse van den Kieboom
+Website=
+Hidden=1
+Builtin=1
+Version=1.0
diff --git a/plugins/history/resources/resources.xml b/plugins/history/resources/resources.xml
new file mode 100644
index 0000000..802c861
--- /dev/null
+++ b/plugins/history/resources/resources.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+  <gresource prefix="/org/gnome/gitg/history">
+    <file>view-history.ui</file>
+  </gresource>
+</gresources>
+
+<!-- ex: et ts=2 -->
diff --git a/plugins/history/resources/view-history.ui b/plugins/history/resources/view-history.ui
new file mode 100644
index 0000000..c725a0d
--- /dev/null
+++ b/plugins/history/resources/view-history.ui
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.3 -->
+  <object class="GtkGrid" id="view">
+    <property name="visible">True</property>
+    <property name="row-spacing">6</property>
+    <property name="vexpand">True</property>
+    <property name="hexpand">True</property>
+    <property name="orientation">vertical</property>
+  </object>
+</interface>
+
+<!-- ex:set ts=2 et: -->
diff --git a/vapi/config.vapi b/vapi/config.vapi
new file mode 100644
index 0000000..cee9f22
--- /dev/null
+++ b/vapi/config.vapi
@@ -0,0 +1,13 @@
+[CCode(cprefix = "", lower_case_cprefix = "", cheader_filename = "config.h")]
+namespace Gitg.Config
+{
+	public const string GETTEXT_PACKAGE;
+	public const string PACKAGE_NAME;
+	public const string PACKAGE_VERSION;
+	public const string GITG_DATADIR;
+	public const string GITG_LOCALEDIR;
+	public const string GITG_LIBDIR;
+	public const string VERSION;
+}
+
+// ex:ts=4 noet
diff --git a/vapi/gobject-introspection-1.0.vapi b/vapi/gobject-introspection-1.0.vapi
new file mode 100644
index 0000000..28f7c49
--- /dev/null
+++ b/vapi/gobject-introspection-1.0.vapi
@@ -0,0 +1,31 @@
+[CCode (cprefix = "GI", lower_case_cprefix = "g_i", cheader_filename = "girepository.h")]
+namespace Introspection
+{
+	[CCode (cprefix = "G_IREPOSITORY_ERROR_")]
+	public errordomain RepositoryError {
+		TYPELIB_NOT_FOUND,
+		NAMESPACE_MISMATCH,
+		NAMESPACE_VERSION_CONFLICT,
+		LIBRARY_NOT_FOUND
+	}
+
+	[CCode (cname="int", cprefix = "G_IREPOSITORY_LOAD_FLAG_")]
+	public enum RepositoryLoadFlags {
+		LAZY = 1
+	}
+
+	[CCode (ref_function = "", unref_function = "")]
+	public class Repository {
+		public static unowned Repository get_default();
+		public static void prepend_search_path(string directory);
+		public static unowned GLib.SList<string> get_search_path();
+
+		public unowned Typelib? require(string namespace_, string? version = null, RepositoryLoadFlags flags = 0) throws RepositoryError;
+	}
+
+	[Compact]
+	[CCode (cname = "GTypelib", cprefix = "g_typelib_", free_function = "g_typelib_free")]
+	public class Typelib {
+		public unowned string get_namespace();
+	}
+}



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