[gitg/vala] Initial porting to vala
- From: Jesse van den Kieboom <jessevdk src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gitg/vala] Initial porting to vala
- Date: Sat, 14 Apr 2012 11:47:23 +0000 (UTC)
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"><Primary>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"><Primary>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]