[gitg] Implemented multiple selection for commit
- From: Jesse van den Kieboom <jessevdk src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gitg] Implemented multiple selection for commit
- Date: Fri, 11 Jul 2014 17:04:27 +0000 (UTC)
commit 8d78cd62679e3328e7d71cda367958e4924f66cb
Author: Jesse van den Kieboom <jessevdk gnome org>
Date: Fri Jul 11 19:03:48 2014 +0200
Implemented multiple selection for commit
gitg/commit/gitg-commit-sidebar.vala | 191 +++++++++++++
gitg/commit/gitg-commit.vala | 509 +++++++++++++++++++---------------
libgitg/gitg-sidebar.vala | 132 +++++++--
libgitg/gitg-stage.vala | 36 +++-
4 files changed, 614 insertions(+), 254 deletions(-)
---
diff --git a/gitg/commit/gitg-commit-sidebar.vala b/gitg/commit/gitg-commit-sidebar.vala
index d07b13e..517dbda 100644
--- a/gitg/commit/gitg-commit-sidebar.vala
+++ b/gitg/commit/gitg-commit-sidebar.vala
@@ -31,6 +31,72 @@ class Sidebar : Gitg.Sidebar
[Signal(action = true)]
public signal void discard_selection();
+ public signal void selected_items_changed(Gitg.SidebarItem[] items);
+
+ public class File : Object, Gitg.SidebarItem
+ {
+ public enum Type
+ {
+ NONE,
+ STAGED,
+ UNSTAGED,
+ UNTRACKED
+ }
+
+ Gitg.StageStatusFile d_file;
+ Type d_type;
+
+ public File(Gitg.StageStatusFile f, Type type)
+ {
+ d_file = f;
+ d_type = type;
+ }
+
+ public Gitg.StageStatusFile file
+ {
+ get { return d_file; }
+ }
+
+ public string text
+ {
+ owned get { return d_file.path; }
+ }
+
+ public Type stage_type
+ {
+ get { return d_type; }
+ }
+
+ private string? icon_for_status(Ggit.StatusFlags status)
+ {
+ if ((status & (Ggit.StatusFlags.INDEX_NEW |
+ Ggit.StatusFlags.WORKING_TREE_NEW)) != 0)
+ {
+ return "list-add-symbolic";
+ }
+ else if ((status & (Ggit.StatusFlags.INDEX_MODIFIED |
+ Ggit.StatusFlags.INDEX_RENAMED |
+ Ggit.StatusFlags.INDEX_TYPECHANGE |
+ Ggit.StatusFlags.WORKING_TREE_MODIFIED |
+ Ggit.StatusFlags.WORKING_TREE_TYPECHANGE)) != 0)
+ {
+ return "text-editor-symbolic";
+ }
+ else if ((status & (Ggit.StatusFlags.INDEX_DELETED |
+ Ggit.StatusFlags.WORKING_TREE_DELETED)) != 0)
+ {
+ return "edit-delete-symbolic";
+ }
+
+ return null;
+ }
+
+ public string? icon_name
+ {
+ owned get { return icon_for_status(d_file.flags); }
+ }
+ }
+
construct
{
unowned Gtk.BindingSet binding_set = Gtk.BindingSet.by_class(get_class());
@@ -52,6 +118,131 @@ class Sidebar : Gitg.Sidebar
Gdk.ModifierType.CONTROL_MASK,
"discard-selection",
0);
+
+ var sel = get_selection();
+ sel.mode = Gtk.SelectionMode.MULTIPLE;
+ }
+
+ private File.Type get_item_type(Gitg.SidebarItem item)
+ {
+ var header = item as Gitg.SidebarStore.SidebarHeader;
+
+ if (header != null)
+ {
+ return (File.Type)header.id;
+ }
+
+ var file = item as File;
+
+ if (file != null)
+ {
+ return file.stage_type;
+ }
+
+ return File.Type.NONE;
+ }
+
+ private File.Type selected_type()
+ {
+ foreach (var item in get_selected_items<Gitg.SidebarItem>())
+ {
+ var tp = get_item_type(item);
+
+ if (tp != File.Type.NONE)
+ {
+ return tp;
+ }
+ }
+
+ return File.Type.NONE;
+ }
+
+ protected override bool select_function(Gtk.TreeSelection sel,
+ Gtk.TreeModel model,
+ Gtk.TreePath path,
+ bool cursel)
+ {
+ if (cursel)
+ {
+ return true;
+ }
+
+ Gtk.TreeIter iter;
+ model.get_iter(out iter, path);
+
+ Gitg.SidebarHint hint;
+
+ var m = model as Gitg.SidebarStore;
+ m.get(iter, Gitg.SidebarColumn.HINT, out hint);
+
+ if (hint == Gitg.SidebarHint.DUMMY)
+ {
+ return false;
+ }
+
+ var item = m.item_for_iter(iter);
+
+ // Prevent selection of the untracked header
+ var header = item as Gitg.SidebarStore.SidebarHeader;
+
+ if (header != null && (File.Type)header.id == File.Type.UNTRACKED)
+ {
+ return false;
+ }
+
+ var seltp = selected_type();
+
+ if (seltp == File.Type.NONE)
+ {
+ return true;
+ }
+
+ var tp = get_item_type(item);
+ return tp == seltp;
+ }
+
+ protected override void selection_changed(Gtk.TreeSelection sel)
+ {
+ if (model.clearing)
+ {
+ return;
+ }
+
+ var items = get_selected_items<Gitg.SidebarItem>();
+
+ if (items.length == 0)
+ {
+ deselected();
+ }
+ else
+ {
+ selected_items_changed(items);
+ }
+ }
+
+ public File[] items_of_type(File.Type type)
+ {
+ var ret = new File[0];
+
+ model.foreach((m, path, iter) => {
+ var item = model.item_for_iter(iter);
+
+ if (item == null)
+ {
+ return false;
+ }
+
+ var file = item as File;
+
+ if (file != null && file.stage_type == type)
+ {
+ ret += file;
+ }
+
+ return false;
+ });
+
+ return ret;
}
}
diff --git a/gitg/commit/gitg-commit.vala b/gitg/commit/gitg-commit.vala
index 9809fb8..324b92e 100644
--- a/gitg/commit/gitg-commit.vala
+++ b/gitg/commit/gitg-commit.vala
@@ -26,74 +26,9 @@ namespace GitgCommit
private Paned? d_main;
private bool d_reloading;
private bool d_has_staged;
- private Gitg.StageStatusFile? d_current_file;
- private bool d_current_staged;
public GitgExt.Application? application { owned get; construct set; }
- private class SidebarFile : Object, Gitg.SidebarItem
- {
- public enum Type
- {
- STAGED,
- UNSTAGED,
- UNTRACKED
- }
-
- Gitg.StageStatusFile d_file;
- Type d_type;
-
- public SidebarFile(Gitg.StageStatusFile f, Type type)
- {
- d_file = f;
- d_type = type;
- }
-
- public Gitg.StageStatusFile file
- {
- get { return d_file; }
- }
-
- public string text
- {
- owned get { return d_file.path; }
- }
-
- public Type stage_type
- {
- get { return d_type; }
- }
-
- private string? icon_for_status(Ggit.StatusFlags status)
- {
- if ((status & (Ggit.StatusFlags.INDEX_NEW |
- Ggit.StatusFlags.WORKING_TREE_NEW)) != 0)
- {
- return "list-add-symbolic";
- }
- else if ((status & (Ggit.StatusFlags.INDEX_MODIFIED |
- Ggit.StatusFlags.INDEX_RENAMED |
- Ggit.StatusFlags.INDEX_TYPECHANGE |
- Ggit.StatusFlags.WORKING_TREE_MODIFIED |
- Ggit.StatusFlags.WORKING_TREE_TYPECHANGE)) != 0)
- {
- return "text-editor-symbolic";
- }
- else if ((status & (Ggit.StatusFlags.INDEX_DELETED |
- Ggit.StatusFlags.WORKING_TREE_DELETED)) != 0)
- {
- return "edit-delete-symbolic";
- }
-
- return null;
- }
-
- public string? icon_name
- {
- owned get { return icon_for_status(d_file.flags); }
- }
- }
-
public Activity(GitgExt.Application application)
{
Object(application: application);
@@ -152,19 +87,19 @@ namespace GitgCommit
return action == "commit";
}
- private delegate void StageUnstageCallback(Gitg.StageStatusFile f, int numclick);
+ private delegate void StageUnstageCallback(Sidebar.File f);
private delegate void UpdateDiffCallback();
private UpdateDiffCallback? d_update_diff_callback;
- private void show_unstaged_diff(Gitg.StageStatusFile f)
+ private void show_unstaged_diff(Gitg.StageStatusFile[] files)
{
var stage = application.repository.stage;
- stage.diff_workdir.begin(f, d_main.diff_view.options, (obj, res) => {
+ stage.diff_workdir_all.begin(files, d_main.diff_view.options, (obj, res) => {
try
{
- var d = stage.diff_workdir.end(res);
+ var d = stage.diff_workdir_all.end(res);
d_main.diff_view.unstaged = true;
d_main.diff_view.staged = false;
@@ -182,78 +117,64 @@ namespace GitgCommit
});
d_update_diff_callback = () => {
- show_unstaged_diff(f);
+ show_unstaged_diff(files);
};
}
- private void stage_file(Gitg.StageStatusFile f)
+ private async void stage_files(owned Gitg.StageStatusFile[] files)
{
var stage = application.repository.stage;
- stage.stage_path.begin(f.path, (obj, res) => {
- try
- {
- stage.stage_path.end(res);
- }
- catch (Error e)
+ foreach (var f in files)
+ {
+ if ((f.flags & Ggit.StatusFlags.WORKING_TREE_DELETED) != 0)
{
- var msg = _("Failed to stage the file `%s'").printf(f.path);
- application.show_infobar(msg, e.message, Gtk.MessageType.ERROR);
- }
-
- reload();
- });
- }
-
- private void delete_file(Gitg.StageStatusFile f)
- {
- var stage = application.repository.stage;
+ try
+ {
+ yield stage.delete_path(f.path);
+ }
+ catch (Error e)
+ {
+ var msg = _("Failed to stage the removal of file
`%s'").printf(f.path);
+ application.show_infobar(msg, e.message,
Gtk.MessageType.ERROR);
- stage.delete_path.begin(f.path, (obj, res) => {
- try
- {
- stage.delete_path.end(res);
+ break;
+ }
}
- catch (Error e)
+ else
{
- var msg = _("Failed to stage the removal of file
`%s'").printf(f.path);
- application.show_infobar(msg, e.message, Gtk.MessageType.ERROR);
+ try
+ {
+ yield stage.stage_path(f.path);
+ }
+ catch (Error e)
+ {
+ var msg = _("Failed to stage the file `%s'").printf(f.path);
+ application.show_infobar(msg, e.message,
Gtk.MessageType.ERROR);
+
+ break;
+ }
}
+ }
- reload();
- });
+ reload();
}
- private void on_unstaged_activated(Gitg.StageStatusFile f, int numclick)
+ private void on_unstaged_activated(Gitg.StageStatusFile[] files)
{
- d_current_file = f;
- d_current_staged = false;
-
- if (numclick == 1)
- {
- show_unstaged_diff(f);
- }
- else
- {
- if ((f.flags & Ggit.StatusFlags.WORKING_TREE_DELETED) != 0)
- {
- delete_file(f);
- }
- else
- {
- stage_file(f);
- }
- }
+ stage_files.begin(files, (obj, res) => {
+ stage_files.end(res);
+ });
}
- private void show_staged_diff(Gitg.StageStatusFile f)
+ private void show_staged_diff(Gitg.StageStatusFile[] files)
{
var stage = application.repository.stage;
- stage.diff_index.begin(f, d_main.diff_view.options, (obj, res) => {
+ stage.diff_index_all.begin(files, d_main.diff_view.options, (obj, res) => {
try
{
- var d = stage.diff_index.end(res);
+ var d = stage.diff_index_all.end(res);
d_main.diff_view.unstaged = false;
d_main.diff_view.staged = true;
@@ -271,95 +192,81 @@ namespace GitgCommit
});
d_update_diff_callback = () => {
- show_staged_diff(f);
+ show_staged_diff(files);
};
}
- private void delete_index_file(Gitg.StageStatusFile f)
+ private async void unstage_files(owned Gitg.StageStatusFile[] files)
{
var stage = application.repository.stage;
- stage.delete_path.begin(f.path, (obj, res) => {
- try
- {
- stage.delete_path.end(res);
- }
- catch (Error e)
+ foreach (var f in files)
+ {
+ if ((f.flags & Ggit.StatusFlags.INDEX_NEW) != 0)
{
- var msg = _("Failed to unstage the removal of file
`%s'").printf(f.path);
- application.show_infobar(msg, e.message, Gtk.MessageType.ERROR);
- }
-
- reload();
- });
- }
-
- private void unstage_file(Gitg.StageStatusFile f)
- {
- var stage = application.repository.stage;
+ try
+ {
+ yield stage.delete_path(f.path);
+ }
+ catch (Error e)
+ {
+ var msg = _("Failed to unstage the removal of file
`%s'").printf(f.path);
+ application.show_infobar(msg, e.message,
Gtk.MessageType.ERROR);
- stage.unstage_path.begin(f.path, (obj, res) => {
- try
- {
- stage.unstage_path.end(res);
+ break;
+ }
}
- catch (Error e)
+ else
{
- var msg = _("Failed to unstage the file `%s'").printf(f.path);
- application.show_infobar(msg, e.message, Gtk.MessageType.ERROR);
+ try
+ {
+ yield stage.unstage_path(f.path);
+ }
+ catch (Error e)
+ {
+ var msg = _("Failed to unstage the file `%s'").printf(f.path);
+ application.show_infobar(msg, e.message,
Gtk.MessageType.ERROR);
+
+ break;
+ }
}
+ }
- reload();
- });
+ reload();
}
- private void on_staged_activated(Gitg.StageStatusFile f, int numclick)
+ private void on_staged_activated(Gitg.StageStatusFile[] files)
{
- d_current_file = f;
- d_current_staged = true;
-
- if (numclick == 1)
- {
- show_staged_diff(f);
- }
- else
- {
- if ((f.flags & Ggit.StatusFlags.INDEX_NEW) != 0)
- {
- delete_index_file(f);
- }
- else
- {
- unstage_file(f);
- }
- }
+ unstage_files.begin(files, (obj, res) => {
+ unstage_files.end(res);
+ });
}
- private SidebarFile? append_files(Gitg.SidebarStore model,
- Gitg.StageStatusFile[] files,
- SidebarFile.Type type,
- Gitg.StageStatusFile? current,
- StageUnstageCallback? callback)
+ private Sidebar.File[] append_files(Gitg.SidebarStore model,
+ Gitg.StageStatusFile[] files,
+ Sidebar.File.Type type,
+ Gee.HashSet<string>? selected_paths,
+ StageUnstageCallback? callback)
{
- SidebarFile? citem = null;
+ var ret = new Sidebar.File[0];
foreach (var f in files)
{
- var item = new SidebarFile(f, type);
+ var item = new Sidebar.File(f, type);
- if (current != null && f.path == current.path)
+ if (selected_paths != null && selected_paths.contains(f.path))
{
- citem = item;
+ ret += item;
}
item.activated.connect((numclick) => {
- callback(f, numclick);
+ callback(item);
});
model.append(item);
}
- return citem;
+ return ret;
}
private void reload()
@@ -373,8 +280,21 @@ namespace GitgCommit
d_reloading = true;
- var currentfile = d_current_file;
- d_current_file = null;
+ var sb = d_main.sidebar;
+ var model = sb.model;
+
+ Sidebar.File.Type selected_type;
+ Gitg.StageStatusFile[] selected_files;
+
+ selected_files = files_for_items(sb.get_selected_items<Gitg.SidebarItem>(),
+ out selected_type);
+
+ var selected_paths = new Gee.HashSet<string>();
+
+ foreach (var f in selected_files)
+ {
+ selected_paths.add(f.path);
+ }
// Preload author avatar
try
@@ -387,8 +307,6 @@ namespace GitgCommit
});
} catch {}
- var model = d_main.sidebar.model;
-
var stage = repository.stage;
var opts = Ggit.StatusOption.INCLUDE_UNTRACKED |
@@ -447,10 +365,10 @@ namespace GitgCommit
model.clear();
d_main.diff_view.diff = null;
- model.begin_header(_("Staged"));
+ var staged_header = model.begin_header(_("Staged"),
(uint)Sidebar.File.Type.STAGED);
- SidebarFile? current_staged = null;
- SidebarFile? current_unstaged = null;
+ var current_staged = new Sidebar.File[0];
+ var current_unstaged = new Sidebar.File[0];
if (staged.length == 0)
{
@@ -460,14 +378,16 @@ namespace GitgCommit
{
current_staged = append_files(model,
staged,
- SidebarFile.Type.STAGED,
- currentfile,
- on_staged_activated);
+ Sidebar.File.Type.STAGED,
+ selected_paths,
+ (f) => {
+ on_staged_activated(new
Gitg.StageStatusFile[] {f.file});
+ });
}
model.end_header();
- model.begin_header(_("Unstaged"));
+ var unstaged_header = model.begin_header(_("Unstaged"),
(uint)Sidebar.File.Type.UNSTAGED);
if (unstaged.length == 0)
{
@@ -477,14 +397,16 @@ namespace GitgCommit
{
current_unstaged = append_files(model,
unstaged,
- SidebarFile.Type.UNSTAGED,
- currentfile,
- on_unstaged_activated);
+ Sidebar.File.Type.UNSTAGED,
+ selected_paths,
+ (f) => {
+ on_unstaged_activated(new
Gitg.StageStatusFile[] {f.file});
+ });
}
model.end_header();
- model.begin_header(_("Untracked"));
+ model.begin_header(_("Untracked"), (uint)Sidebar.File.Type.UNTRACKED);
if (untracked.length == 0)
{
@@ -494,9 +416,11 @@ namespace GitgCommit
{
append_files(model,
untracked,
- SidebarFile.Type.UNTRACKED,
+ Sidebar.File.Type.UNTRACKED,
null,
- on_unstaged_activated);
+ (f) => {
+ on_unstaged_activated(new Gitg.StageStatusFile[]
{f.file});
+ });
}
model.end_header();
@@ -506,24 +430,40 @@ namespace GitgCommit
d_reloading = false;
- if (currentfile != null)
+ if (selected_paths.size != 0)
{
- SidebarFile? sel = null;
+ Sidebar.File[] sel;
- if (d_current_staged)
+ if (selected_type == Sidebar.File.Type.STAGED)
{
- sel = (current_staged != null) ? current_staged :
current_unstaged;
+ sel = (current_staged.length != 0) ? current_staged :
current_unstaged;
}
else
{
- sel = (current_unstaged != null) ? current_unstaged :
current_staged;
+ sel = (current_unstaged.length != 0) ? current_unstaged :
current_staged;
}
- if (sel != null)
+ if (sel.length != 0)
+ {
+ foreach (var item in sel)
+ {
+ d_main.sidebar.select(item);
+ }
+ }
+ else if (selected_type == Sidebar.File.Type.STAGED)
+ {
+ d_main.sidebar.select(staged_header);
+ }
+ else
{
- d_main.sidebar.select(sel);
+ d_main.sidebar.select(unstaged_header);
}
}
+ else
+ {
+ // Select unstaged header
+ d_main.sidebar.select(unstaged_header);
+ }
});
}
@@ -879,7 +819,7 @@ namespace GitgCommit
private void on_discard_clicked()
{
var primary = _("Discard changes");
- var secondary = _("Are you sure you want to permanently discard the selected changes
in the file `%s'?").printf(d_current_file.path);
+ var secondary = _("Are you sure you want to permanently discard the selected
changes?").printf();
var q = new GitgExt.UserQuery();
@@ -961,16 +901,31 @@ namespace GitgCommit
});
}
- private bool do_discard_file(GitgExt.UserQuery q, Gitg.StageStatusFile f)
+ private async void revert_paths(string[] paths) throws Error
{
var stage = application.repository.stage;
+ foreach (var path in paths)
+ {
+ yield stage.revert_path(path);
+ }
+ }
+
+ private bool do_discard_files(GitgExt.UserQuery q, Gitg.StageStatusFile[] files)
+ {
application.busy = true;
- stage.revert_path.begin(f.path, (o, ret) => {
+ var paths = new string[files.length];
+
+ for (var i = 0; i < files.length; i++)
+ {
+ paths[i] = files[i].path;
+ }
+
+ revert_paths.begin(paths, (o, ret) => {
try
{
- stage.revert_path.end(ret);
+ revert_paths.end(ret);
}
catch (Error e)
{
@@ -988,10 +943,26 @@ namespace GitgCommit
return false;
}
- private void on_discard_menu_activated(SidebarFile f)
+ private void on_discard_menu_activated(Gitg.StageStatusFile[] files)
{
var primary = _("Discard changes");
- var secondary = _("Are you sure you want to permanently discard all changes made to
the file `%s'?").printf(f.file.path);
+ string secondary;
+
+ if (files.length == 1)
+ {
+ secondary = _("Are you sure you want to permanently discard all changes made
to the file `%s'?").printf(files[0].path);
+ }
+ else
+ {
+ var paths = new string[files.length - 1];
+
+ for (var i = 0; i < files.length - 1; i++)
+ {
+ paths[i] = @"`$(files[i].path)'";
+ }
+
+ secondary = _("Are you sure you want to permanently discard all changes made
to the files %s and `%s'?").printf(string.joinv(", ", paths), files[files.length - 1].path);
+ }
var q = new GitgExt.UserQuery();
@@ -1009,7 +980,7 @@ namespace GitgCommit
q.response.connect((w, r) => {
if (r == Gtk.ResponseType.OK)
{
- return do_discard_file(q, f.file);
+ return do_discard_files(q, files);
}
return true;
@@ -1020,45 +991,117 @@ namespace GitgCommit
private void do_populate_menu(Gtk.Menu menu)
{
- var f = d_main.sidebar.get_selected_item<SidebarFile>();
+ var items = d_main.sidebar.get_selected_items<Gitg.SidebarItem>();
- if (f == null)
+ if (items.length == 0)
{
return;
}
- if (f.stage_type == SidebarFile.Type.UNSTAGED ||
- f.stage_type == SidebarFile.Type.UNTRACKED)
+ Sidebar.File.Type type;
+
+ var files = files_for_items(items, out type);
+
+ if (type == Sidebar.File.Type.UNSTAGED ||
+ type == Sidebar.File.Type.UNTRACKED)
{
var stage = new Gtk.MenuItem.with_mnemonic(_("_Stage changes"));
menu.append(stage);
stage.activate.connect(() => {
- on_unstaged_activated(f.file, 2);
+ on_unstaged_activated(files);
});
}
- if (f.stage_type == SidebarFile.Type.STAGED)
+ if (type == Sidebar.File.Type.STAGED)
{
var stage = new Gtk.MenuItem.with_mnemonic(_("_Unstage changes"));
menu.append(stage);
stage.activate.connect(() => {
- on_staged_activated(f.file, 2);
+ on_staged_activated(files);
});
}
- if (f.stage_type == SidebarFile.Type.UNSTAGED)
+ if (type == Sidebar.File.Type.UNSTAGED)
{
var discard = new Gtk.MenuItem.with_mnemonic(_("_Discard changes"));
menu.append(discard);
discard.activate.connect(() => {
- on_discard_menu_activated(f);
+ on_discard_menu_activated(files);
});
}
}
+ private Gitg.StageStatusFile[] files_to_stage_files(Sidebar.File[] files)
+ {
+ var ret = new Gitg.StageStatusFile[files.length];
+
+ for (var i = 0; i < ret.length; i++)
+ {
+ ret[i] = files[i].file;
+ }
+
+ return ret;
+ }
+
+ private Gitg.StageStatusFile[] stage_status_files_of_type(Sidebar.File.Type type)
+ {
+ return files_to_stage_files(d_main.sidebar.items_of_type(type));
+ }
+
+ private Gitg.StageStatusFile[] files_for_items(Gitg.SidebarItem[] items, out
Sidebar.File.Type type)
+ {
+ var files = new Gitg.StageStatusFile[items.length];
+ files.length = 0;
+
+ type = Sidebar.File.Type.NONE;
+
+ foreach (var item in items)
+ {
+ var header = item as Gitg.SidebarStore.SidebarHeader;
+
+ if (header != null)
+ {
+ type = (Sidebar.File.Type)header.id;
+ return stage_status_files_of_type(type);
+ }
+
+ var file = item as Sidebar.File;
+
+ if (file != null)
+ {
+ files += file.file;
+ type = file.stage_type;
+ }
+ }
+
+ return files;
+ }
+
+ private void sidebar_selection_changed(Gitg.SidebarItem[] items)
+ {
+ Sidebar.File.Type type;
+
+ var files = files_for_items(items, out type);
+
+ if (files.length == 0)
+ {
+ d_main.diff_view.diff = null;
+ return;
+ }
+
+ if (type == Sidebar.File.Type.STAGED)
+ {
+ show_staged_diff(files);
+ }
+ else
+ {
+ show_unstaged_diff(files);
+ }
+ }
+
private void build_ui()
{
d_main = new Paned();
@@ -1075,28 +1118,44 @@ namespace GitgCommit
});
d_main.sidebar.stage_selection.connect(() => {
- var sel = d_main.sidebar.get_selected_item<SidebarFile>();
+ var sel = d_main.sidebar.get_selected_items<Gitg.SidebarItem>();
+ Sidebar.File.Type type;
+
+ var files = files_for_items(sel, out type);
- if (sel != null && (sel.stage_type == SidebarFile.Type.UNSTAGED ||
- sel.stage_type == SidebarFile.Type.UNTRACKED))
+ if (files.length != 0 && (type == Sidebar.File.Type.UNSTAGED ||
+ type == Sidebar.File.Type.UNTRACKED))
{
- on_unstaged_activated(sel.file, 2);
+ on_unstaged_activated(files);
}
});
d_main.sidebar.unstage_selection.connect(() => {
- var sel = d_main.sidebar.get_selected_item<SidebarFile>();
+ var sel = d_main.sidebar.get_selected_items<Gitg.SidebarItem>();
+ Sidebar.File.Type type;
- if (sel != null && sel.stage_type == SidebarFile.Type.STAGED)
+ var files = files_for_items(sel, out type);
+
+ if (files.length != 0 && type == Sidebar.File.Type.STAGED)
{
- on_staged_activated(sel.file, 2);
+ on_staged_activated(files);
}
});
d_main.sidebar.discard_selection.connect(() => {
-
+ var sel = d_main.sidebar.get_selected_items<Gitg.SidebarItem>();
+ Sidebar.File.Type type;
+
+ var files = files_for_items(sel, out type);
+
+ if (files.length != 0 && type == Sidebar.File.Type.UNSTAGED)
+ {
+ on_discard_menu_activated(files);
+ }
});
+ d_main.sidebar.selected_items_changed.connect(sidebar_selection_changed);
+
d_main.button_commit.clicked.connect(() => {
on_commit_clicked();
});
diff --git a/libgitg/gitg-sidebar.vala b/libgitg/gitg-sidebar.vala
index 5bee494..11df833 100644
--- a/libgitg/gitg-sidebar.vala
+++ b/libgitg/gitg-sidebar.vala
@@ -54,7 +54,7 @@ public class SidebarStore : Gtk.TreeStore
private SList<Gtk.TreeIter?> d_parents;
private bool d_clearing;
- private class SidebarText : Object, SidebarItem
+ protected class SidebarText : Object, SidebarItem
{
private string d_text;
@@ -74,6 +74,23 @@ public class SidebarStore : Gtk.TreeStore
}
}
+ public class SidebarHeader : SidebarText
+ {
+ private uint d_id;
+
+ public uint id
+ {
+ get { return d_id; }
+ }
+
+ public SidebarHeader(string text, uint id)
+ {
+ base(text);
+
+ d_id = id;
+ }
+ }
+
private void append_real(SidebarItem item,
uint hint,
out Gtk.TreeIter iter)
@@ -109,14 +126,16 @@ public class SidebarStore : Gtk.TreeStore
return this;
}
- public SidebarStore begin_header(string text)
+ public SidebarHeader begin_header(string text, uint id = 0)
{
Gtk.TreeIter iter;
- append_real(new SidebarText(text), SidebarHint.HEADER, out iter);
+ var item = new SidebarHeader(text, id);
+
+ append_real(item, SidebarHint.HEADER, out iter);
d_parents.prepend(iter);
- return this;
+ return item;
}
public SidebarStore end_header()
@@ -250,26 +269,41 @@ public class Sidebar : Gtk.TreeView
var sel = get_selection();
- sel.set_select_function((sel, model, path, cursel) => {
- Gtk.TreeIter iter;
- model.get_iter(out iter, path);
+ sel.set_select_function(select_function);
- uint hint;
+ sel.changed.connect(selection_changed);
+ }
- model.get(iter, SidebarColumn.HINT, out hint);
+ protected virtual bool select_function(Gtk.TreeSelection sel,
+ Gtk.TreeModel model,
+ Gtk.TreePath path,
+ bool cursel)
+ {
+ Gtk.TreeIter iter;
+ model.get_iter(out iter, path);
- return hint != SidebarHint.HEADER && hint != SidebarHint.DUMMY;
- });
+ uint hint;
- sel.changed.connect((sel) => {
- Gtk.TreeIter iter;
+ model.get(iter, SidebarColumn.HINT, out hint);
- if (model.clearing)
- {
- return;
- }
+ return hint != SidebarHint.HEADER && hint != SidebarHint.DUMMY;
+ }
+
+ protected virtual void selection_changed(Gtk.TreeSelection sel)
+ {
+ Gtk.TreeIter iter;
+
+ if (model.clearing)
+ {
+ return;
+ }
+
+ if (get_selected_iter(out iter))
+ {
+ SidebarHint hint;
+ model.get(iter, SidebarColumn.HINT, out hint);
- if (sel.get_selected(null, out iter))
+ if (hint != SidebarHint.HEADER && hint != SidebarHint.DUMMY)
{
model.activate(iter, 1);
}
@@ -277,15 +311,39 @@ public class Sidebar : Gtk.TreeView
{
deselected();
}
- });
+ }
+ else
+ {
+ deselected();
+ }
}
- public T? get_selected_item<T>()
+ protected bool get_selected_iter(out Gtk.TreeIter iter)
{
var sel = get_selection();
+
+ if (sel.count_selected_rows() == 1)
+ {
+ Gtk.TreeModel m;
+
+ var rows = sel.get_selected_rows(out m);
+ m.get_iter(out iter, rows.data);
+
+ return true;
+ }
+ else
+ {
+ iter = Gtk.TreeIter();
+ }
+
+ return false;
+ }
+
+ public T? get_selected_item<T>()
+ {
Gtk.TreeIter iter;
- if (sel.get_selected(null, out iter))
+ if (get_selected_iter(out iter))
{
return (T)model.item_for_iter(iter);
}
@@ -293,6 +351,25 @@ public class Sidebar : Gtk.TreeView
return null;
}
+ public T[] get_selected_items<T>()
+ {
+ var sel = get_selection();
+
+ Gtk.TreeModel m;
+ Gtk.TreeIter iter;
+
+ var rows = sel.get_selected_rows(out m);
+ var ret = new T[0];
+
+ foreach (var row in rows)
+ {
+ m.get_iter(out iter, row);
+ ret += (T)model.item_for_iter(iter);
+ }
+
+ return ret;
+ }
+
public void select(SidebarItem item)
{
model.foreach((m, path, iter) => {
@@ -356,16 +433,21 @@ public class Sidebar : Gtk.TreeView
protected override bool button_press_event(Gdk.EventButton event)
{
- var ret = base.button_press_event(event);
-
Gdk.Event *ev = (Gdk.Event *)event;
if (ev->triggers_context_menu())
{
+ if (get_selection().count_selected_rows() <= 1)
+ {
+ base.button_press_event(event);
+ }
+
return do_populate_popup(event);
}
-
- return ret;
+ else
+ {
+ return base.button_press_event(event);
+ }
}
protected override bool popup_menu()
diff --git a/libgitg/gitg-stage.vala b/libgitg/gitg-stage.vala
index 75b84e8..fa4128d 100644
--- a/libgitg/gitg-stage.vala
+++ b/libgitg/gitg-stage.vala
@@ -904,7 +904,8 @@ public class Stage : Object
});
}
- public async Ggit.Diff? diff_index(StageStatusFile f, Ggit.DiffOptions? defopts = null) throws Error
+ public async Ggit.Diff? diff_index_all(StageStatusFile[] files,
+ Ggit.DiffOptions? defopts = null) throws Error
{
var opts = new Ggit.DiffOptions();
@@ -912,7 +913,14 @@ public class Stage : Object
Ggit.DiffOption.DISABLE_PATHSPEC_MATCH |
Ggit.DiffOption.RECURSE_UNTRACKED_DIRS;
- opts.pathspec = new string[] {f.path};
+ var pspec = new string[files.length];
+
+ for (var i = 0; i < files.length; i++)
+ {
+ pspec[i] = files[i].path;
+ }
+
+ opts.pathspec = pspec;
if (defopts != null)
{
@@ -933,7 +941,14 @@ public class Stage : Object
opts);
}
- public async Ggit.Diff? diff_workdir(StageStatusFile f, Ggit.DiffOptions? defopts = null) throws Error
+ public async Ggit.Diff? diff_index(StageStatusFile f,
+ Ggit.DiffOptions? defopts = null) throws Error
+ {
+ return yield diff_index_all(new StageStatusFile[] {f}, defopts);
+ }
+
+ public async Ggit.Diff? diff_workdir_all(StageStatusFile[] files,
+ Ggit.DiffOptions? defopts = null) throws Error
{
var opts = new Ggit.DiffOptions();
@@ -941,7 +956,14 @@ public class Stage : Object
Ggit.DiffOption.DISABLE_PATHSPEC_MATCH |
Ggit.DiffOption.RECURSE_UNTRACKED_DIRS;
- opts.pathspec = new string[] {f.path};
+ var pspec = new string[files.length];
+
+ for (var i = 0; i < files.length; i++)
+ {
+ pspec[i] = files[i].path;
+ }
+
+ opts.pathspec = pspec;
if (defopts != null)
{
@@ -958,6 +980,12 @@ public class Stage : Object
d_repository.get_index(),
opts);
}
+
+ public async Ggit.Diff? diff_workdir(StageStatusFile f,
+ Ggit.DiffOptions? defopts = null) throws Error
+ {
+ return yield diff_workdir_all(new StageStatusFile[] {f}, defopts);
+ }
}
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]