gitg/libgitg/gitg-repository-list-box.vala

556 lines
11 KiB
Vala
Raw Normal View History

2012-07-10 14:32:48 +00:00
/*
* This file is part of gitg
*
* Copyright (C) 2012 - Ignacio Casal Quinteiro
*
* gitg is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* 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 <http://www.gnu.org/licenses/>.
*/
2013-06-23 12:40:04 +00:00
namespace Gitg
2012-07-10 14:32:48 +00:00
{
2014-07-16 07:57:26 +00:00
public enum SelectionMode
{
NORMAL,
SELECTION
}
2013-07-08 19:00:37 +00:00
public class RepositoryListBox : Gtk.ListBox
2012-07-10 14:32:48 +00:00
{
2013-03-13 09:26:15 +00:00
private string? d_filter_text;
2013-06-22 11:09:05 +00:00
2014-07-16 07:57:26 +00:00
public signal void repository_activated(Repository repository);
public signal void show_error(string primary_message, string secondary_message);
2013-07-08 19:00:37 +00:00
[GtkTemplate (ui = "/org/gnome/gitg/gtk/gitg-repository-list-box-row.ui")]
2014-07-15 08:04:25 +00:00
public class Row : Gtk.ListBoxRow
2012-07-10 14:32:48 +00:00
{
2013-06-25 16:43:56 +00:00
private Repository? d_repository;
private DateTime d_time;
private bool d_loading;
private bool d_has_remote;
2013-06-25 16:43:56 +00:00
[GtkChild]
private ProgressBin d_progress_bin;
[GtkChild]
private Gtk.Image d_image;
[GtkChild]
private Gtk.Label d_repository_label;
[GtkChild]
private Gtk.Label d_branch_label;
[GtkChild]
private Gtk.Arrow d_arrow;
[GtkChild]
private Gtk.Spinner d_spinner;
[GtkChild]
2014-07-15 08:04:25 +00:00
private Gtk.CheckButton d_remove_check_button;
[GtkChild]
private Gtk.Revealer d_remove_revealer;
public signal void request_remove();
2013-06-25 16:43:56 +00:00
2014-07-16 07:57:26 +00:00
private SelectionMode d_mode;
2014-07-15 08:04:25 +00:00
2014-07-16 07:57:26 +00:00
private static Gtk.IconSize s_icon_size;
static construct
2014-07-15 08:04:25 +00:00
{
2014-07-16 07:57:26 +00:00
s_icon_size = Gtk.icon_size_register("gitg", 64, 64);
}
public SelectionMode mode
{
get { return d_mode; }
2014-07-15 08:04:25 +00:00
set
{
2014-07-16 07:57:26 +00:00
if (d_mode != value)
2014-07-15 08:04:25 +00:00
{
2014-07-16 07:57:26 +00:00
d_mode = value;
2014-07-15 08:04:25 +00:00
2014-07-16 07:57:26 +00:00
d_remove_revealer.reveal_child = (d_mode == SelectionMode.SELECTION);
2014-07-15 08:04:25 +00:00
d_remove_check_button.active = false;
}
}
}
[Notify]
2014-07-16 07:57:26 +00:00
public new bool selected
2014-07-15 08:04:25 +00:00
{
get; set;
}
construct
{
d_remove_check_button.bind_property("active",
this,
2014-07-16 07:57:26 +00:00
"selected",
2014-07-15 08:04:25 +00:00
BindingFlags.BIDIRECTIONAL |
BindingFlags.SYNC_CREATE);
}
2013-06-25 16:43:56 +00:00
public Repository? repository
{
get { return d_repository; }
set
{
d_repository = value;
branch_name = "";
2014-07-16 07:57:26 +00:00
2013-06-25 16:43:56 +00:00
if (d_repository != null)
{
try
{
var head = d_repository.get_head();
branch_name = head.parsed_name.shortname;
}
catch {}
}
}
}
public bool can_remove
{
2014-07-15 08:04:25 +00:00
get { return d_remove_check_button.sensitive; }
set { d_remove_check_button.sensitive = value; }
}
2013-06-25 16:43:56 +00:00
public DateTime time
{
get { return d_time; }
set { d_time = value; }
default = new DateTime.now_local();
2013-06-25 16:43:56 +00:00
}
public double fraction
{
set { d_progress_bin.fraction = value; }
}
public string? repository_name
{
get { return d_repository_label.get_text(); }
set { d_repository_label.set_markup("<b>%s</b>".printf(value)); }
}
public string? branch_name
{
get { return d_branch_label.get_text(); }
set { d_branch_label.set_markup("<small>%s</small>".printf(value)); }
}
public bool loading
{
get { return d_loading; }
set
{
d_loading = value;
if (d_loading)
{
d_spinner.stop();
d_spinner.hide();
d_arrow.show();
d_progress_bin.fraction = 0;
}
else
{
d_arrow.hide();
d_spinner.show();
d_spinner.start();
}
}
}
public bool has_remote
2013-06-25 16:43:56 +00:00
{
get { return d_has_remote; }
set
{
d_has_remote = value;
var folder_icon_name = d_has_remote ? "folder-remote" : "folder";
2014-07-16 07:57:26 +00:00
d_image.set_from_icon_name(folder_icon_name, s_icon_size);
}
}
2013-06-25 16:43:56 +00:00
2013-07-08 19:00:37 +00:00
public Row(string name, string branch_name, bool has_remote)
{
Object(repository_name: name, branch_name: branch_name, has_remote: has_remote);
2013-06-25 16:43:56 +00:00
}
2012-07-10 14:32:48 +00:00
}
2014-07-16 07:57:26 +00:00
[Notify]
public SelectionMode mode { get; set; }
2014-07-15 08:04:25 +00:00
2014-07-16 07:57:26 +00:00
protected override bool button_press_event(Gdk.EventButton event)
2013-06-23 13:46:22 +00:00
{
2014-07-16 07:57:26 +00:00
Gdk.Event *ev = (Gdk.Event *)event;
if (ev->triggers_context_menu() && mode == SelectionMode.NORMAL)
2014-07-15 08:04:25 +00:00
{
2014-07-16 07:57:26 +00:00
mode = SelectionMode.SELECTION;
return true;
}
2014-07-15 08:04:25 +00:00
2014-07-16 07:57:26 +00:00
return false;
}
protected override void row_activated(Gtk.ListBoxRow row)
{
var r = (Row)row;
2014-07-15 08:04:25 +00:00
2014-07-16 07:57:26 +00:00
if (mode == SelectionMode.SELECTION)
{
r.selected = !r.selected;
2014-07-15 08:04:25 +00:00
return;
}
2013-06-23 13:46:22 +00:00
if (r.repository != null)
{
repository_activated(r.repository);
}
}
2012-07-10 14:32:48 +00:00
construct
{
2013-06-23 13:46:22 +00:00
set_header_func(update_header);
2013-07-08 18:51:38 +00:00
set_filter_func(filter);
2013-06-23 13:46:22 +00:00
set_sort_func(compare_widgets);
show();
2012-10-03 07:00:20 +00:00
2014-07-16 07:57:26 +00:00
set_selection_mode(Gtk.SelectionMode.NONE);
2012-07-10 14:32:48 +00:00
add_recent_info();
2012-07-10 14:32:48 +00:00
}
private void update_header(Gtk.ListBoxRow row, Gtk.ListBoxRow? before)
2012-10-20 13:35:18 +00:00
{
row.set_header(before != null ? new Gtk.Separator(Gtk.Orientation.HORIZONTAL) : null);
2012-10-20 13:35:18 +00:00
}
private bool filter(Gtk.ListBoxRow row)
2013-03-13 09:26:15 +00:00
{
2013-07-08 19:00:37 +00:00
return d_filter_text != null ? ((Row)row).repository_name.contains(d_filter_text) : true;
2013-03-13 09:26:15 +00:00
}
private int compare_widgets(Gtk.ListBoxRow a, Gtk.ListBoxRow b)
2013-03-13 14:14:14 +00:00
{
2013-07-08 19:00:37 +00:00
return - ((Row)a).time.compare(((Row)b).time);
2013-03-13 14:14:14 +00:00
}
private void add_recent_info()
2012-07-10 14:32:48 +00:00
{
var recent_manager = Gtk.RecentManager.get_default();
var reversed_items = recent_manager.get_items();
reversed_items.reverse();
2012-07-10 14:32:48 +00:00
foreach (var item in reversed_items)
2012-07-10 14:32:48 +00:00
{
if (item.has_group("gitg"))
{
File info_file = File.new_for_uri(item.get_uri());
File repo_file;
try
{
repo_file = Ggit.Repository.discover(info_file);
}
catch
{
2013-03-13 14:42:25 +00:00
try
{
recent_manager.remove_item(item.get_uri());
}
catch {}
return;
}
Repository repo;
try
{
repo = new Repository(repo_file, null);
}
catch
{
2013-03-13 14:42:25 +00:00
try
{
recent_manager.remove_item(item.get_uri());
}
catch {}
return;
}
add_repository(repo);
}
2012-07-10 14:32:48 +00:00
}
2013-03-13 14:14:14 +00:00
}
2013-07-08 19:00:37 +00:00
private Row get_row_for_repository(Repository repository)
2013-03-13 14:14:14 +00:00
{
2013-07-08 19:00:37 +00:00
Row? row = null;
2013-06-23 13:46:22 +00:00
foreach (var child in get_children())
2012-07-10 14:32:48 +00:00
{
2013-07-08 19:00:37 +00:00
var d = (Row)child;
2014-07-16 07:57:26 +00:00
if (d.repository.get_location().equal(repository.get_location()))
{
2013-06-22 11:09:05 +00:00
row = d;
break;
}
2012-07-10 14:32:48 +00:00
}
2013-06-22 11:09:05 +00:00
return row;
}
private void add_repository_to_recent_manager(string uri)
{
var recent_manager = Gtk.RecentManager.get_default();
var item = Gtk.RecentData();
item.app_name = Environment.get_application_name();
item.mime_type = "inode/directory";
item.app_exec = string.join(" ", Environment.get_prgname(), "%f");
item.groups = { "gitg", null };
recent_manager.add_full(uri, item);
}
public void add_repository(Repository repository)
{
2013-07-08 19:00:37 +00:00
Row? row = get_row_for_repository(repository);
var f = repository.workdir != null ? repository.workdir : repository.location;
2013-06-22 11:09:05 +00:00
if (row == null)
2012-07-10 14:32:48 +00:00
{
string head_name = "";
bool has_remote = true;
2012-07-10 14:32:48 +00:00
try
{
var head = repository.get_head();
head_name = head.parsed_name.shortname;
var remotes = repository.list_remotes();
if (remotes.length == 0)
{
has_remote = false;
}
}
catch {}
2012-07-10 14:32:48 +00:00
2013-07-08 19:00:37 +00:00
row = new Row(repository.name, head_name, has_remote);
2013-06-22 11:09:05 +00:00
row.repository = repository;
2013-06-25 16:43:56 +00:00
row.show();
if (f != null)
{
2014-07-16 07:57:26 +00:00
bind_property("mode",
2014-07-15 08:04:25 +00:00
row,
2014-07-16 07:57:26 +00:00
"mode");
2014-07-15 08:04:25 +00:00
}
if (f != null)
{
2014-07-16 07:57:26 +00:00
row.notify["selected"].connect(() => {
2014-07-15 08:04:25 +00:00
notify_property("has-selection");
});
row.request_remove.connect(() => {
try
{
var recent_manager = Gtk.RecentManager.get_default();
recent_manager.remove_item(f.get_uri());
} catch {}
remove(row);
});
row.can_remove = true;
}
else
{
row.can_remove = false;
}
2013-06-25 16:43:56 +00:00
add(row);
}
else
{
// to get the item sorted to the beginning of the list
2013-06-22 11:09:05 +00:00
row.time = new DateTime.now_local();
2013-07-08 18:34:19 +00:00
invalidate_sort();
}
2013-03-13 14:42:25 +00:00
if (f != null)
{
add_repository_to_recent_manager(f.get_uri());
}
2012-07-10 14:32:48 +00:00
}
2013-03-13 09:26:15 +00:00
2014-07-15 08:04:25 +00:00
public Row[] selection
{
owned get
{
var ret = new Row[0];
foreach (var row in get_children())
{
2014-07-16 07:57:26 +00:00
var r = (Row)row;
2014-07-15 08:04:25 +00:00
2014-07-16 07:57:26 +00:00
if (r.selected)
2014-07-15 08:04:25 +00:00
{
ret += r;
}
}
return ret;
}
}
public bool has_selection
{
get
{
foreach (var row in get_children())
{
2014-07-16 07:57:26 +00:00
var r = (Row)row;
2014-07-15 08:04:25 +00:00
2014-07-16 07:57:26 +00:00
if (r.selected)
2014-07-15 08:04:25 +00:00
{
return true;
}
}
return false;
}
}
2013-11-23 13:54:47 +00:00
class CloneProgress : Ggit.RemoteCallbacks
{
private Row d_row;
public CloneProgress(Row row)
{
d_row = row;
}
protected override bool transfer_progress(Ggit.TransferProgress stats) throws Error
{
var recvobj = stats.get_received_objects();
var indxobj = stats.get_indexed_objects();
var totaobj = stats.get_total_objects();
d_row.fraction = (recvobj + indxobj) / (double)(2 * totaobj);
return true;
}
}
2013-07-08 19:00:37 +00:00
private async Repository? clone(Row row, string url, File location, bool is_bare)
2013-03-21 16:32:15 +00:00
{
SourceFunc callback = clone.callback;
Repository? repository = null;
2013-03-21 16:32:15 +00:00
ThreadFunc<void*> run = () => {
try
{
2013-03-26 12:03:54 +00:00
var options = new Ggit.CloneOptions();
2013-11-23 13:54:47 +00:00
2013-03-26 12:03:54 +00:00
options.set_is_bare(is_bare);
2013-11-23 13:54:47 +00:00
options.set_remote_callbacks(new CloneProgress(row));
2013-03-26 12:03:54 +00:00
repository = (Repository)Ggit.Repository.clone(url, location, options);
}
catch (Ggit.Error e)
{
show_error("Gitg could not clone the git repository.", e.message);
}
catch (GLib.Error e)
{
show_error("Gitg could not clone the git repository.", e.message);
}
2013-03-21 16:32:15 +00:00
Idle.add((owned) callback);
return null;
};
2013-03-21 16:32:15 +00:00
try
{
2013-06-10 17:20:08 +00:00
new Thread<void*>.try("gitg-clone-thread", (owned)run);
yield;
2013-03-21 16:32:15 +00:00
}
catch {}
return repository;
}
2013-03-26 12:03:54 +00:00
public void clone_repository(string url, File location, bool is_bare)
{
2013-03-26 11:48:56 +00:00
// create subfolder
var subfolder_name = url.substring(url.last_index_of_char('/') + 1);
2013-03-28 10:22:52 +00:00
if (subfolder_name.has_suffix(".git") && !is_bare)
2013-03-26 11:48:56 +00:00
{
2013-03-28 10:22:52 +00:00
subfolder_name = subfolder_name.slice(0, - ".git".length);
2013-03-26 12:03:54 +00:00
}
2013-03-28 10:22:52 +00:00
else if (is_bare)
2013-03-26 12:03:54 +00:00
{
subfolder_name += ".git";
2013-03-26 11:48:56 +00:00
}
var subfolder = location.resolve_relative_path(subfolder_name);
2013-03-26 11:48:56 +00:00
try
{
subfolder.make_directory_with_parents(null);
}
catch (GLib.Error e)
{
show_error("Gitg could not clone the git repository.", e.message);
2013-03-26 11:48:56 +00:00
return;
}
// Clone
2014-07-16 07:57:26 +00:00
var row = new Row(subfolder_name, "Cloning...", true);
2013-06-25 16:43:56 +00:00
row.loading = true;
row.show();
add(row);
2013-03-26 11:48:56 +00:00
2013-06-22 11:09:05 +00:00
clone.begin(row, url, subfolder, is_bare, (obj, res) => {
Gitg.Repository? repository = clone.end(res);
// FIXME: show an error
if (repository != null)
{
File? workdir = repository.get_workdir();
File? repo_file = repository.get_location();
var uri = (workdir != null) ? workdir.get_uri() : repo_file.get_uri();
add_repository_to_recent_manager(uri);
}
2013-06-22 11:09:05 +00:00
row.repository = repository;
2013-06-25 16:43:56 +00:00
row.loading = false;
});
2013-03-21 16:32:15 +00:00
}
2013-03-13 09:26:15 +00:00
public void filter_text(string? text)
{
d_filter_text = text;
2013-07-08 18:51:38 +00:00
invalidate_filter();
2013-03-13 09:26:15 +00:00
}
2012-07-10 14:32:48 +00:00
}
}
// ex:ts=4 noet