diff --git a/gitg/Makefile.am b/gitg/Makefile.am
index b8ed78b9..28ff9b9e 100644
--- a/gitg/Makefile.am
+++ b/gitg/Makefile.am
@@ -58,9 +58,12 @@ gitg_gitg_VALASOURCES = \
gitg/gitg-application.vala \
gitg/gitg-plugins-engine.vala \
gitg/gitg-popup-menu.vala \
+ gitg/gitg-animated-paned.vala \
gitg/gitg-ui-elements.vala \
gitg/gitg-ref-action-rename.vala \
gitg/gitg-ref-action-delete.vala \
+ gitg/gitg-commit-action-create-branch.vala \
+ gitg/gitg-create-branch-dialog.vala \
gitg/preferences/gitg-preferences-commit.vala \
gitg/preferences/gitg-preferences-dialog.vala \
gitg/preferences/gitg-preferences-interface.vala \
diff --git a/gitg/gitg-animated-paned.vala b/gitg/gitg-animated-paned.vala
new file mode 100644
index 00000000..716e0792
--- /dev/null
+++ b/gitg/gitg-animated-paned.vala
@@ -0,0 +1,191 @@
+/*
+ * This file is part of gitg
+ *
+ * Copyright (C) 2014 - Jesse van den Kieboom
+ *
+ * gitg is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * gitg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with gitg. If not, see .
+ */
+
+namespace Gitg
+{
+
+public enum SlideDirection
+{
+ IN,
+ OUT
+}
+
+public enum SlidePanedChild
+{
+ FIRST,
+ SECOND
+}
+
+public class AnimatedPaned : Gtk.Paned
+{
+ private int d_target_pos;
+ private int d_original_pos;
+ private int d_start_pos;
+ private int64 d_slide_start;
+ private int64 d_slide_duration;
+ private uint d_tick_id;
+ private SourceFunc? d_async_callback;
+ private SlideDirection d_direction;
+ private SlidePanedChild d_child;
+
+ public uint transition_duration
+ {
+ get;
+ construct set;
+ default = 250;
+ }
+
+ private bool on_animate_step(Gtk.Widget widget, Gdk.FrameClock clock)
+ {
+ var elapsed = (clock.get_frame_time() - d_slide_start);
+ var factor = (double)elapsed / (double)d_slide_duration;
+
+ if (factor > 1)
+ {
+ factor = 1;
+ }
+
+ var pos = (int)Math.round((d_target_pos - d_start_pos) * factor) + d_start_pos;
+ set_position(pos);
+
+ if (pos == d_target_pos)
+ {
+ d_tick_id = 0;
+
+ if (d_async_callback != null)
+ {
+ d_async_callback();
+ }
+
+ if (d_direction == SlideDirection.OUT)
+ {
+ if (d_child == SlidePanedChild.FIRST)
+ {
+ get_child1().hide();
+ }
+ else
+ {
+ get_child2().hide();
+ }
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+
+ public override void dispose()
+ {
+ if (d_tick_id != 0)
+ {
+ remove_tick_callback(d_tick_id);
+ d_tick_id = 0;
+ }
+
+ base.dispose();
+ }
+
+ public bool is_animating
+ {
+ get { return d_tick_id != 0; }
+ }
+
+ public void slide(SlidePanedChild child,
+ SlideDirection direction)
+ {
+ slide_async.begin(child, direction, (obj, res) => {
+ slide_async.end(res);
+ });
+ }
+
+ public async void slide_async(SlidePanedChild child,
+ SlideDirection direction)
+ {
+ if (d_tick_id == 0)
+ {
+ if (direction == SlideDirection.OUT)
+ {
+ d_original_pos = get_position();
+ }
+
+ d_tick_id = add_tick_callback(on_animate_step);
+ }
+
+ d_slide_start = get_frame_clock().get_frame_time();
+ d_start_pos = get_position();
+ d_direction = direction;
+ d_child = child;
+
+ double factor;
+ int w;
+
+ if (orientation == Gtk.Orientation.VERTICAL)
+ {
+ w = get_allocated_height();
+ }
+ else
+ {
+ w = get_allocated_width();
+ }
+
+ if (direction == SlideDirection.OUT)
+ {
+ if (child == SlidePanedChild.FIRST)
+ {
+ d_target_pos = 0;
+ }
+ else
+ {
+ d_target_pos = w;
+ }
+
+ factor = ((double)d_target_pos - (double)d_start_pos) /
+ ((double)d_target_pos - (double)d_original_pos);
+ }
+ else
+ {
+ d_target_pos = d_original_pos;
+
+ double div;
+
+ if (child == SlidePanedChild.FIRST)
+ {
+ div = d_original_pos;
+ get_child1().show();
+ }
+ else
+ {
+ div = d_original_pos - w;
+ get_child2().show();
+ }
+
+ factor = ((double)d_target_pos - (double)d_start_pos) / div;
+ }
+
+ d_async_callback = slide_async.callback;
+ d_slide_duration = (int64)(factor * transition_duration) * 1000;
+
+ yield;
+ }
+}
+
+}
+
+// vi: ts=4 noet
diff --git a/gitg/gitg-commit-action-create-branch.vala b/gitg/gitg-commit-action-create-branch.vala
new file mode 100644
index 00000000..018db6ec
--- /dev/null
+++ b/gitg/gitg-commit-action-create-branch.vala
@@ -0,0 +1,106 @@
+/*
+ * This file is part of gitg
+ *
+ * Copyright (C) 2014 - Jesse van den Kieboom
+ *
+ * gitg is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * gitg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with gitg. If not, see .
+ */
+
+namespace Gitg
+{
+
+class CommitActionCreateBranch : GitgExt.UIElement, GitgExt.Action, GitgExt.CommitAction, Object
+{
+ // Do this to pull in config.h before glib.h (for gettext...)
+ private const string version = Gitg.Config.VERSION;
+
+ public GitgExt.Application? application { owned get; construct set; }
+ public GitgExt.RefActionInterface action_interface { get; construct set; }
+ public Gitg.Commit commit { get; construct set; }
+
+ public CommitActionCreateBranch(GitgExt.Application application,
+ GitgExt.RefActionInterface action_interface,
+ Gitg.Commit commit)
+ {
+ Object(application: application,
+ action_interface: action_interface,
+ commit: commit);
+ }
+
+ public string id
+ {
+ owned get { return "/org/gnome/gitg/commit-actions/create-branch"; }
+ }
+
+ public string display_name
+ {
+ owned get { return _("Create Branch"); }
+ }
+
+ public string description
+ {
+ owned get { return _("Create a new branch at the selected commit"); }
+ }
+
+ public void activate()
+ {
+ var dlg = new CreateBranchDialog((Gtk.Window)application);
+
+ dlg.response.connect((d, resp) => {
+ if (resp == Gtk.ResponseType.OK)
+ {
+ Ggit.Branch? branch = null;
+ Ggit.Signature? author = null;
+
+ var repo = application.repository;
+
+ try
+ {
+ author = repo.get_signature_with_environment(application.environment);
+ } catch {}
+
+ var id = commit.get_id().to_string();
+
+ try
+ {
+ branch = repo.create_branch(dlg.new_branch_name,
+ commit,
+ Ggit.CreateFlags.NONE,
+ author,
+ @"branch: Created from $(id)");
+ }
+ catch (Error e)
+ {
+ application.show_infobar(_("Failed to create branch"),
+ e.message,
+ Gtk.MessageType.ERROR);
+ }
+
+ if (branch != null)
+ {
+ action_interface.add_ref((Gitg.Ref)branch);
+ }
+ }
+
+ dlg.destroy();
+ finished();
+ });
+
+ dlg.show();
+ }
+}
+
+}
+
+// ex:set ts=4 noet
diff --git a/gitg/gitg-create-branch-dialog.vala b/gitg/gitg-create-branch-dialog.vala
new file mode 100644
index 00000000..edd3017b
--- /dev/null
+++ b/gitg/gitg-create-branch-dialog.vala
@@ -0,0 +1,60 @@
+/*
+ * This file is part of gitg
+ *
+ * Copyright (C) 2012 - Jesse van den Kieboom
+ *
+ * gitg is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * gitg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with gitg. If not, see .
+ */
+
+namespace Gitg
+{
+
+[GtkTemplate (ui = "/org/gnome/gitg/ui/gitg-create-branch-dialog.ui")]
+class CreateBranchDialog : Gtk.Dialog
+{
+ [GtkChild]
+ private Gtk.Button d_button_create;
+
+ [GtkChild]
+ private Gtk.Entry d_entry_branch_name;
+
+ construct
+ {
+ d_entry_branch_name.changed.connect(() => {
+ d_button_create.sensitive = (new_branch_name.length != 0);
+ });
+ }
+
+ public CreateBranchDialog(Gtk.Window? parent)
+ {
+ Object(use_header_bar : 1);
+
+ if (parent != null)
+ {
+ set_transient_for(parent);
+ }
+ }
+
+ public string new_branch_name
+ {
+ owned get
+ {
+ return d_entry_branch_name.text.strip();
+ }
+ }
+}
+
+}
+
+// ex: ts=4 noet
diff --git a/gitg/history/gitg-history-paned.vala b/gitg/history/gitg-history-paned.vala
index 81951a82..276a9494 100644
--- a/gitg/history/gitg-history-paned.vala
+++ b/gitg/history/gitg-history-paned.vala
@@ -21,13 +21,13 @@ namespace GitgHistory
{
[GtkTemplate (ui = "/org/gnome/gitg/ui/gitg-history-paned.ui")]
-class Paned : Gtk.Paned
+class Paned : Gitg.AnimatedPaned
{
[GtkChild]
private Gtk.Box d_box_sidebar;
[GtkChild]
- private Gtk.Paned d_paned_panels;
+ private Gitg.AnimatedPaned d_paned_panels;
[GtkChild]
private Gtk.StackSwitcher d_stack_switcher_panels;
@@ -85,19 +85,95 @@ class Paned : Gtk.Paned
}
}
+ private void slide_in()
+ {
+ slide(Gitg.SlidePanedChild.FIRST, Gitg.SlideDirection.IN);
+
+ Gitg.SlidePanedChild child;
+
+ if (inner_orientation == Gtk.Orientation.HORIZONTAL)
+ {
+ child = Gitg.SlidePanedChild.FIRST;
+ }
+ else
+ {
+ child = Gitg.SlidePanedChild.SECOND;
+ }
+
+ d_paned_panels.slide(child, Gitg.SlideDirection.IN);
+ }
+
+ private void slide_out()
+ {
+ slide(Gitg.SlidePanedChild.FIRST, Gitg.SlideDirection.OUT);
+
+ Gitg.SlidePanedChild child;
+
+ if (inner_orientation == Gtk.Orientation.HORIZONTAL)
+ {
+ child = Gitg.SlidePanedChild.FIRST;
+ }
+ else
+ {
+ child = Gitg.SlidePanedChild.SECOND;
+ }
+
+ d_paned_panels.slide(child, Gitg.SlideDirection.OUT);
+ }
+
+ private GitgExt.SelectionMode d_selectable_mode;
+
+ [Notify]
+ public GitgExt.SelectionMode selectable_mode
+ {
+ get { return d_selectable_mode; }
+ set
+ {
+ if (d_selectable_mode != value)
+ {
+ d_selectable_mode = value;
+
+ if (d_selectable_mode == GitgExt.SelectionMode.NORMAL)
+ {
+ slide_in();
+ }
+ else
+ {
+ slide_out();
+ }
+ }
+ }
+ }
+
+ private void store_paned_position(Gitg.AnimatedPaned paned, Settings settings, string key)
+ {
+ if (paned.is_animating)
+ {
+ return;
+ }
+
+ if (!paned.get_child1().visible || !paned.get_child2().visible)
+ {
+ return;
+ }
+
+ settings.set_int(key, paned.get_position());
+ }
+
construct
{
var state_settings = new Settings("org.gnome.gitg.state.history");
- state_settings.bind("paned-sidebar-position",
- this,
- "position",
- SettingsBindFlags.GET | SettingsBindFlags.SET);
+ position = state_settings.get_int("paned-sidebar-position");
+ d_paned_panels.position = state_settings.get_int("paned-panels-position");
- state_settings.bind("paned-panels-position",
- d_paned_panels,
- "position",
- SettingsBindFlags.GET | SettingsBindFlags.SET);
+ notify["position"].connect(() => {
+ store_paned_position(this, state_settings, "paned-sidebar-position");
+ });
+
+ d_paned_panels.notify["position"].connect(() => {
+ store_paned_position(d_paned_panels, state_settings, "paned-panels-position");
+ });
var interface_settings = new Settings("org.gnome.gitg.preferences.interface");
@@ -141,6 +217,11 @@ class Paned : Gtk.Paned
{
var ret = base.draw(context);
+ if (!get_child1().visible || !get_child2().visible)
+ {
+ return ret;
+ }
+
var window = d_box_sidebar.get_window();
var handlewin = get_handle_window();
diff --git a/gitg/history/gitg-history.vala b/gitg/history/gitg-history.vala
index 6639c8b5..8b5cd944 100644
--- a/gitg/history/gitg-history.vala
+++ b/gitg/history/gitg-history.vala
@@ -22,7 +22,7 @@ namespace GitgHistory
/* The main history view. This view shows the equivalent of git log, but
* in a nice way with lanes, merges, ref labels etc.
*/
- public class Activity : Object, GitgExt.UIElement, GitgExt.Activity, GitgExt.History
+ public class Activity : Object, GitgExt.UIElement, GitgExt.Activity, GitgExt.Selectable, GitgExt.History
{
// Do this to pull in config.h before glib.h (for gettext...)
private const string version = Gitg.Config.VERSION;
@@ -39,6 +39,7 @@ namespace GitgHistory
private Paned d_main;
private Gitg.PopupMenu d_refs_list_popup;
+ private Gitg.PopupMenu d_commit_list_popup;
private Gitg.UIElements d_panels;
@@ -357,15 +358,30 @@ namespace GitgHistory
update_walker();
});
+ d_commit_list_popup = new Gitg.PopupMenu(d_main.commit_list_view);
+ d_commit_list_popup.populate_menu.connect(on_commit_list_populate_menu);
+
application.bind_property("repository", d_main.refs_list,
"repository",
BindingFlags.DEFAULT |
BindingFlags.SYNC_CREATE);
+
+ bind_property("selectable-mode",
+ d_main,
+ "selectable-mode",
+ BindingFlags.BIDIRECTIONAL);
}
- private void add_ref_action(Gee.LinkedList actions, GitgExt.RefAction? action)
+ private Gtk.Menu? on_commit_list_populate_menu(Gdk.EventButton? event)
{
- if (action.available)
+ selectable_mode = GitgExt.SelectionMode.SELECTION;
+ return null;
+ }
+
+ private void add_ref_action(Gee.LinkedList actions,
+ GitgExt.RefAction? action)
+ {
+ if (action != null && action.available)
{
actions.add(action);
}
@@ -462,6 +478,91 @@ namespace GitgHistory
d_commit_list_model.set_include(include);
d_commit_list_model.reload();
}
+
+ [Notify]
+ public GitgExt.SelectionMode selectable_mode
+ {
+ get; set;
+ }
+
+ private void add_commit_action(Gee.LinkedList actions,
+ GitgExt.CommitAction? action)
+ {
+ if (action != null && action.available)
+ {
+ actions.add(action);
+ action.finished.connect(() => {
+ selectable_mode = GitgExt.SelectionMode.NORMAL;
+ });
+ }
+ }
+
+ public Gtk.Widget? action_widget
+ {
+ owned get
+ {
+ Gitg.Commit? commit = null;
+
+ foreach_selected((c) => {
+ commit = (Gitg.Commit)c;
+ return false;
+ });
+
+ var af = new ActionInterface(application, d_main.refs_list);
+
+ var actions = new Gee.LinkedList();
+
+ add_commit_action(actions,
+ new Gitg.CommitActionCreateBranch(application,
+ af,
+ commit));
+
+ var exts = new Peas.ExtensionSet(Gitg.PluginsEngine.get_default(),
+ typeof(GitgExt.CommitAction),
+ "application",
+ application,
+ "action_interface",
+ af,
+ "commit",
+ commit);
+
+ exts.foreach((extset, info, extension) => {
+ add_commit_action(actions, extension as GitgExt.CommitAction);
+ });
+
+ var ab = new Gtk.ActionBar();
+ ab.show();
+
+ var box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 6);
+ box.margin = 6;
+ box.homogeneous = true;
+ box.show();
+
+ foreach (var action in actions)
+ {
+ var widget = action.widget;
+
+ if (widget == null)
+ {
+ var button = new Gtk.Button.with_label(action.display_name);
+ button.tooltip_text = action.description;
+
+ button.clicked.connect(() => {
+ action.activate();
+ });
+
+ widget = button;
+ }
+
+ widget.show();
+ box.add(widget);
+ }
+
+ ab.set_center_widget(box);
+
+ return ab;
+ }
+ }
}
}
diff --git a/gitg/resources/gitg-resources.xml b/gitg/resources/gitg-resources.xml
index 2eb37f51..a5bae93f 100644
--- a/gitg/resources/gitg-resources.xml
+++ b/gitg/resources/gitg-resources.xml
@@ -15,6 +15,7 @@
ui/gitg-history-ref-header.ui
ui/gitg-commit-paned.ui
ui/gitg-commit-dialog.ui
+ ui/gitg-create-branch-dialog.ui
ui/style.css
diff --git a/gitg/resources/ui/gitg-create-branch-dialog.ui b/gitg/resources/ui/gitg-create-branch-dialog.ui
new file mode 100644
index 00000000..7219e3c4
--- /dev/null
+++ b/gitg/resources/ui/gitg-create-branch-dialog.ui
@@ -0,0 +1,108 @@
+
+
+
+
+ False
+ 5
+ Create Branch
+ False
+ True
+ dialog
+
+
+
+
+
+
+
+ button_cancel
+ d_button_create
+
+
+
diff --git a/gitg/resources/ui/gitg-history-paned.ui b/gitg/resources/ui/gitg-history-paned.ui
index 05a022ff..ff709aff 100644
--- a/gitg/resources/ui/gitg-history-paned.ui
+++ b/gitg/resources/ui/gitg-history-paned.ui
@@ -3,7 +3,7 @@
-
+
True
True
True
@@ -64,7 +64,7 @@
-
+
True
True
True
diff --git a/libgitg-ext/gitg-ext-commit-action.vala b/libgitg-ext/gitg-ext-commit-action.vala
index b348749e..7f0addc8 100644
--- a/libgitg-ext/gitg-ext-commit-action.vala
+++ b/libgitg-ext/gitg-ext-commit-action.vala
@@ -22,7 +22,9 @@ namespace GitgExt
public interface CommitAction : Action
{
- public abstract Ggit.Commit commit { get; construct set; }
+ public abstract RefActionInterface action_interface { get; construct set; }
+ public abstract Gitg.Commit commit { get; construct set; }
+ public signal void finished();
}
}
diff --git a/po/POTFILES.in b/po/POTFILES.in
index cb5d2378..9736c070 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -11,6 +11,7 @@ gitg/gitg-clone-dialog.vala
gitg/gitg-dash-view.vala
gitg/gitg-ref-action-delete.vala
gitg/gitg-ref-action-rename.vala
+gitg/gitg-commit-action-create-branch.vala
gitg/gitg.vala
gitg/gitg-window.vala
gitg/history/gitg-history-refs-list.vala
@@ -28,6 +29,7 @@ plugins/files/gitg-files.vala
[type: gettext/glade]gitg/resources/ui/gitg-clone-dialog.ui
[type: gettext/glade]gitg/resources/ui/gitg-commit-dialog.ui
[type: gettext/glade]gitg/resources/ui/gitg-commit-paned.ui
+[type: gettext/glade]gitg/resources/ui/gitg-create-branch-dialog.ui
[type: gettext/glade]gitg/resources/ui/gitg-history-paned.ui
[type: gettext/glade]gitg/resources/ui/gitg-menus.ui
[type: gettext/glade]gitg/resources/ui/gitg-preferences-commit.ui
diff --git a/po/POTFILES.skip b/po/POTFILES.skip
index 310a5b86..c0da5e9d 100644
--- a/po/POTFILES.skip
+++ b/po/POTFILES.skip
@@ -9,6 +9,7 @@ gitg/gitg-clone-dialog.c
gitg/gitg-dash-view.c
gitg/gitg-ref-action-delete.c
gitg/gitg-ref-action-rename.c
+gitg/gitg-commit-action-create-branch.c
gitg/gitg-window.c
gitg/history/gitg-history.c
gitg/history/gitg-history-refs-list.c