mirror of
https://gitlab.gnome.org/GNOME/gitg
synced 2024-09-13 21:21:17 +00:00
Implement history navigation as ListBox
This commit is contained in:
parent
7cfa45167b
commit
d0aa7692d7
|
@ -64,7 +64,7 @@ gitg_gitg_VALASOURCES = \
|
|||
gitg/preferences/gitg-preferences-interface.vala \
|
||||
gitg/preferences/gitg-preferences-history.vala \
|
||||
gitg/history/gitg-history.vala \
|
||||
gitg/history/gitg-history-navigation.vala \
|
||||
gitg/history/gitg-history-refs-list.vala \
|
||||
gitg/history/gitg-history-paned.vala \
|
||||
gitg/commit/gitg-commit.vala \
|
||||
gitg/commit/gitg-commit-paned.vala \
|
||||
|
|
|
@ -1,581 +0,0 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace GitgHistory
|
||||
{
|
||||
private enum Hint
|
||||
{
|
||||
NONE,
|
||||
HEADER,
|
||||
DEFAULT,
|
||||
SEPARATOR
|
||||
}
|
||||
|
||||
private enum Column
|
||||
{
|
||||
ICON_NAME,
|
||||
NAME,
|
||||
TEXT,
|
||||
HEADER,
|
||||
HINT,
|
||||
SECTION,
|
||||
OID
|
||||
}
|
||||
|
||||
public delegate void NavigationActivated(int numclick);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Navigation : Gtk.TreeStore
|
||||
{
|
||||
// Do this to pull in config.h before glib.h (for gettext...)
|
||||
private const string version = Gitg.Config.VERSION;
|
||||
|
||||
private List<Gitg.Ref> d_all;
|
||||
private uint d_oid;
|
||||
private SList<Gtk.TreeIter?> d_parents;
|
||||
private uint d_sections;
|
||||
private Activated[] d_callbacks;
|
||||
private bool d_reloading;
|
||||
private Gitg.Repository? d_repository;
|
||||
private string? d_selected_head;
|
||||
private Gtk.TreeIter? d_selected_iter;
|
||||
|
||||
public signal void ref_activated(Gitg.Ref? r);
|
||||
|
||||
public Navigation(Gitg.Repository? repo)
|
||||
{
|
||||
Object(repository: repo);
|
||||
}
|
||||
|
||||
construct
|
||||
{
|
||||
set_column_types({typeof(string),
|
||||
typeof(string),
|
||||
typeof(string),
|
||||
typeof(string),
|
||||
typeof(uint),
|
||||
typeof(uint),
|
||||
typeof(uint)
|
||||
});
|
||||
|
||||
d_callbacks = new Activated[100];
|
||||
d_callbacks.length = 0;
|
||||
}
|
||||
|
||||
public List<Gitg.Ref> all
|
||||
{
|
||||
get { return d_all; }
|
||||
}
|
||||
|
||||
[Notify]
|
||||
public Gitg.Repository repository
|
||||
{
|
||||
get { return d_repository; }
|
||||
set
|
||||
{
|
||||
d_repository = value;
|
||||
reload();
|
||||
}
|
||||
}
|
||||
|
||||
public Gtk.TreeIter? selected_iter
|
||||
{
|
||||
get { return d_selected_iter; }
|
||||
set { d_selected_iter = value; }
|
||||
}
|
||||
|
||||
public bool show_expanders
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
private static int sort_refs(Gitg.Ref a, Gitg.Ref b)
|
||||
{
|
||||
return a.parsed_name.shortname.ascii_casecmp(b.parsed_name.shortname);
|
||||
}
|
||||
|
||||
private static int sort_remote_refs(Gitg.Ref a, Gitg.Ref b)
|
||||
{
|
||||
return a.parsed_name.remote_branch.ascii_casecmp(b.parsed_name.remote_branch);
|
||||
}
|
||||
|
||||
private void populate(Gitg.Repository repo)
|
||||
{
|
||||
List<Gitg.Ref> branches = new List<Gitg.Ref>();
|
||||
List<Gitg.Ref> tags = new List<Gitg.Ref>();
|
||||
|
||||
HashTable<string, Gee.LinkedList<Gitg.Ref>> remotes;
|
||||
List<string> remotenames = new List<string>();
|
||||
|
||||
remotes = new HashTable<string, Gee.LinkedList<Gitg.Ref>>(str_hash, str_equal);
|
||||
d_all = new List<Gitg.Ref>();
|
||||
|
||||
try
|
||||
{
|
||||
repo.references_foreach_name((nm) => {
|
||||
Gitg.Ref? r;
|
||||
|
||||
try
|
||||
{
|
||||
r = repo.lookup_reference(nm);
|
||||
} catch { return 0; }
|
||||
|
||||
d_all.prepend(r);
|
||||
|
||||
if (r.parsed_name.rtype == Gitg.RefType.BRANCH)
|
||||
{
|
||||
branches.insert_sorted(r, sort_refs);
|
||||
}
|
||||
else if (r.parsed_name.rtype == Gitg.RefType.TAG)
|
||||
{
|
||||
tags.insert_sorted(r, sort_refs);
|
||||
}
|
||||
else if (r.parsed_name.rtype == Gitg.RefType.REMOTE)
|
||||
{
|
||||
Gee.LinkedList<Gitg.Ref> lst;
|
||||
|
||||
string rname = r.parsed_name.remote_name;
|
||||
|
||||
if (!remotes.lookup_extended(rname, null, out lst))
|
||||
{
|
||||
Gee.LinkedList<Gitg.Ref> nlst = new Gee.LinkedList<Gitg.Ref>();
|
||||
nlst.insert(0, r);
|
||||
|
||||
remotes.insert(rname, nlst);
|
||||
remotenames.insert_sorted(rname, (a, b) => a.ascii_casecmp(b));
|
||||
}
|
||||
else
|
||||
{
|
||||
lst.insert(0, r);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
if (repo.is_head_detached())
|
||||
{
|
||||
d_all.prepend(repo.get_head());
|
||||
}
|
||||
} catch {}
|
||||
|
||||
d_all.reverse();
|
||||
|
||||
begin_section();
|
||||
|
||||
append_normal(_("All commits"), null, null, (nc) => activate_ref(null));
|
||||
|
||||
// Branches
|
||||
begin_header(_("Branches"), null);
|
||||
|
||||
foreach (var item in branches)
|
||||
{
|
||||
var branch = item as Ggit.Branch;
|
||||
string? icon = null;
|
||||
bool isdef = false;
|
||||
|
||||
try
|
||||
{
|
||||
if (branch.is_head())
|
||||
{
|
||||
icon = "object-select-symbolic";
|
||||
isdef = true;
|
||||
}
|
||||
}
|
||||
catch {}
|
||||
|
||||
if (isdef)
|
||||
{
|
||||
append_default(item.parsed_name.shortname,
|
||||
item.parsed_name.name,
|
||||
icon,
|
||||
(nc) => activate_ref(item));
|
||||
}
|
||||
else
|
||||
{
|
||||
append_normal(item.parsed_name.shortname,
|
||||
item.parsed_name.name,
|
||||
icon,
|
||||
(nc) => activate_ref(item));
|
||||
}
|
||||
}
|
||||
|
||||
end_header();
|
||||
|
||||
// Remotes
|
||||
begin_header(_("Remotes"), null);
|
||||
|
||||
foreach (var rname in remotenames)
|
||||
{
|
||||
begin_header(rname, null);
|
||||
|
||||
var rrefs = remotes.lookup(rname);
|
||||
|
||||
rrefs.sort((CompareDataFunc)sort_remote_refs);
|
||||
|
||||
foreach (var rref in remotes.lookup(rname))
|
||||
{
|
||||
var it = rref;
|
||||
|
||||
append_normal(rref.parsed_name.remote_branch,
|
||||
rref.parsed_name.name,
|
||||
null,
|
||||
(nc) => activate_ref(it));
|
||||
}
|
||||
|
||||
end_header();
|
||||
}
|
||||
|
||||
end_header();
|
||||
|
||||
// Tags
|
||||
begin_header(_("Tags"), null);
|
||||
|
||||
foreach (var item in tags)
|
||||
{
|
||||
var it = item;
|
||||
|
||||
append_normal(item.parsed_name.shortname,
|
||||
item.parsed_name.name,
|
||||
null,
|
||||
(nc) => activate_ref(it));
|
||||
}
|
||||
|
||||
end_header();
|
||||
|
||||
end_section();
|
||||
}
|
||||
|
||||
private new void append(string text,
|
||||
string? name,
|
||||
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.NAME, name,
|
||||
hint == Hint.HEADER ? Column.HEADER : Column.TEXT, text,
|
||||
Column.HINT, hint,
|
||||
Column.SECTION, d_sections,
|
||||
Column.OID, d_oid);
|
||||
|
||||
if (d_selected_head == name && name != null ||
|
||||
d_selected_head == "--ALL REFS--" && text == _("All commits"))
|
||||
{
|
||||
d_selected_iter = iter;
|
||||
}
|
||||
|
||||
d_callbacks += new Activated((owned)callback);
|
||||
++d_oid;
|
||||
}
|
||||
|
||||
private Navigation append_normal(string text,
|
||||
string? name,
|
||||
string? icon_name,
|
||||
owned NavigationActivated? callback)
|
||||
{
|
||||
Gtk.TreeIter iter;
|
||||
append(text, name, icon_name, Hint.NONE, (owned)callback, out iter);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private Navigation append_default(string text,
|
||||
string? name,
|
||||
string? icon_name,
|
||||
owned NavigationActivated? callback)
|
||||
{
|
||||
Gtk.TreeIter iter;
|
||||
append(text, name, icon_name, Hint.DEFAULT, (owned)callback, out iter);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private Navigation begin_header(string text,
|
||||
string? icon_name)
|
||||
{
|
||||
Gtk.TreeIter iter;
|
||||
|
||||
append(text, null, icon_name, Hint.HEADER, null, out iter);
|
||||
d_parents.prepend(iter);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private Navigation end_header()
|
||||
{
|
||||
if (d_parents != null)
|
||||
{
|
||||
d_parents.remove_link(d_parents);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private uint begin_section()
|
||||
{
|
||||
d_parents = null;
|
||||
return d_sections;
|
||||
}
|
||||
|
||||
private void end_section()
|
||||
{
|
||||
++d_sections;
|
||||
}
|
||||
|
||||
public new void clear()
|
||||
{
|
||||
base.clear();
|
||||
|
||||
d_oid = 0;
|
||||
d_sections = 0;
|
||||
d_callbacks.length = 0;
|
||||
}
|
||||
|
||||
public void reload()
|
||||
{
|
||||
if (d_repository != null)
|
||||
{
|
||||
d_reloading = true;
|
||||
clear();
|
||||
populate(d_repository);
|
||||
d_reloading = false;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private void activate_ref(Gitg.Ref? r)
|
||||
{
|
||||
if (d_reloading)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (r != null)
|
||||
{
|
||||
d_selected_head = r.parsed_name.name;
|
||||
}
|
||||
else
|
||||
{
|
||||
d_selected_head = "--ALL REFS--";
|
||||
}
|
||||
ref_activated(r);
|
||||
}
|
||||
}
|
||||
|
||||
public class NavigationView : Gtk.TreeView
|
||||
{
|
||||
private void build_ui()
|
||||
{
|
||||
var col = new Gtk.TreeViewColumn();
|
||||
|
||||
var padcell = new Gtk.CellRendererText();
|
||||
var iconcell = new Gtk.CellRendererPixbuf();
|
||||
|
||||
var headercell = new Gtk.CellRendererText();
|
||||
var cell = new Gtk.CellRendererText();
|
||||
|
||||
iconcell.follow_state = true;
|
||||
headercell.ellipsize = Pango.EllipsizeMode.MIDDLE;
|
||||
cell.ellipsize = Pango.EllipsizeMode.MIDDLE;
|
||||
|
||||
padcell.xpad = 6;
|
||||
headercell.ypad = 6;
|
||||
|
||||
headercell.weight = Pango.Weight.BOLD;
|
||||
|
||||
col.pack_start(padcell, false);
|
||||
col.pack_start(iconcell, false);
|
||||
col.pack_start(headercell, true);
|
||||
col.pack_start(cell, true);
|
||||
|
||||
col.set_attributes(iconcell, "icon-name", Column.ICON_NAME);
|
||||
col.set_attributes(headercell, "text", Column.HEADER);
|
||||
col.set_attributes(cell, "text", Column.TEXT);
|
||||
|
||||
col.set_cell_data_func(iconcell, (layout, cell, model, iter) => {
|
||||
string? icon_name;
|
||||
model.get(iter, Column.ICON_NAME, out icon_name);
|
||||
|
||||
cell.visible = (icon_name != null);
|
||||
});
|
||||
|
||||
col.set_cell_data_func(headercell, (layout, cell, model, iter) => {
|
||||
Hint hint;
|
||||
model.get(iter, Column.HINT, out hint);
|
||||
|
||||
cell.visible = (hint == Hint.HEADER);
|
||||
});
|
||||
|
||||
col.set_cell_data_func(cell, (layout, cell, model, iter) => {
|
||||
Hint hint;
|
||||
model.get(iter, Column.HINT, out hint);
|
||||
|
||||
cell.visible = (hint != Hint.HEADER);
|
||||
});
|
||||
|
||||
set_row_separator_func((model, iter) => {
|
||||
Hint hint;
|
||||
model.get(iter, Column.HINT, out hint);
|
||||
|
||||
return hint == Hint.SEPARATOR;
|
||||
});
|
||||
|
||||
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;
|
||||
});
|
||||
|
||||
get_selection().changed.connect((sel) => {
|
||||
Gtk.TreeIter iter;
|
||||
|
||||
if (sel.get_selected(null, out iter))
|
||||
{
|
||||
model.activate(iter, 1);
|
||||
}
|
||||
});
|
||||
|
||||
set_show_expanders(model.show_expanders);
|
||||
|
||||
if (model.show_expanders)
|
||||
{
|
||||
set_level_indentation(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
set_level_indentation(12);
|
||||
}
|
||||
}
|
||||
|
||||
public new Navigation model
|
||||
{
|
||||
get { return base.get_model() as Navigation; }
|
||||
set { base.set_model(value); build_ui(); }
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public void select()
|
||||
{
|
||||
if (model.selected_iter != null)
|
||||
{
|
||||
get_selection().select_iter(model.selected_iter);
|
||||
model.selected_iter = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
select_first();
|
||||
}
|
||||
}
|
||||
|
||||
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: ts=4 noet
|
|
@ -33,7 +33,7 @@ class Paned : Gtk.Paned
|
|||
private Gtk.StackSwitcher d_stack_switcher_panels;
|
||||
|
||||
[GtkChild]
|
||||
private NavigationView d_navigation_view;
|
||||
private RefsList d_refs_list;
|
||||
|
||||
[GtkChild]
|
||||
private Gtk.TreeView d_commit_list_view;
|
||||
|
@ -117,9 +117,9 @@ class Paned : Gtk.Paned
|
|||
Object(orientation: Gtk.Orientation.HORIZONTAL);
|
||||
}
|
||||
|
||||
public NavigationView navigation_view
|
||||
public RefsList refs_list
|
||||
{
|
||||
get { return d_navigation_view; }
|
||||
get { return d_refs_list; }
|
||||
}
|
||||
|
||||
public Gtk.TreeView commit_list_view
|
||||
|
|
418
gitg/history/gitg-history-refs-list.vala
Normal file
418
gitg/history/gitg-history-refs-list.vala
Normal file
|
@ -0,0 +1,418 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace GitgHistory
|
||||
{
|
||||
|
||||
private int ref_type_sort_order(Gitg.RefType ref_type)
|
||||
{
|
||||
switch (ref_type)
|
||||
{
|
||||
case Gitg.RefType.NONE:
|
||||
return 0;
|
||||
case Gitg.RefType.BRANCH:
|
||||
return 1;
|
||||
case Gitg.RefType.REMOTE:
|
||||
return 2;
|
||||
case Gitg.RefType.TAG:
|
||||
return 3;
|
||||
}
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
private interface RefTyped : Object
|
||||
{
|
||||
public abstract Gitg.RefType ref_type { get; }
|
||||
}
|
||||
|
||||
[GtkTemplate (ui = "/org/gnome/gitg/ui/gitg-history-ref-row.ui")]
|
||||
private class RefRow : RefTyped, Gtk.Box
|
||||
{
|
||||
private const string version = Gitg.Config.VERSION;
|
||||
|
||||
[GtkChild]
|
||||
private Gtk.Image d_icon;
|
||||
|
||||
[GtkChild]
|
||||
private Gtk.Label d_label;
|
||||
|
||||
public Gitg.Ref? reference { get; set; }
|
||||
|
||||
public Gitg.RefType ref_type
|
||||
{
|
||||
get { return reference != null ? reference.parsed_name.rtype : Gitg.RefType.NONE; }
|
||||
}
|
||||
|
||||
public RefRow(Gitg.Ref? reference)
|
||||
{
|
||||
Object(orientation: Gtk.Orientation.HORIZONTAL, spacing: 6);
|
||||
|
||||
this.reference = reference;
|
||||
|
||||
d_label.label = label_text();
|
||||
|
||||
if (is_head)
|
||||
{
|
||||
d_icon.icon_name = "object-select-symbolic";
|
||||
d_icon.show();
|
||||
}
|
||||
|
||||
if (reference != null)
|
||||
{
|
||||
margin_left += 12;
|
||||
}
|
||||
|
||||
if (ref_type == Gitg.RefType.REMOTE)
|
||||
{
|
||||
margin_left += 12;
|
||||
}
|
||||
}
|
||||
|
||||
private string label_text()
|
||||
{
|
||||
if (reference == null)
|
||||
{
|
||||
return _("All commits");
|
||||
}
|
||||
|
||||
var pn = reference.parsed_name;
|
||||
|
||||
if (pn.rtype == Gitg.RefType.REMOTE)
|
||||
{
|
||||
return pn.remote_branch;
|
||||
}
|
||||
|
||||
return pn.shortname;
|
||||
}
|
||||
|
||||
private bool is_head
|
||||
{
|
||||
get
|
||||
{
|
||||
if (reference == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var branch = reference as Ggit.Branch;
|
||||
|
||||
if (branch != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
return branch.is_head();
|
||||
} catch { return false; }
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private int compare_type(RefRow other)
|
||||
{
|
||||
var pnme = reference.parsed_name;
|
||||
var pnot = other.reference.parsed_name;
|
||||
|
||||
if (pnme.rtype != pnot.rtype)
|
||||
{
|
||||
var i1 = ref_type_sort_order(pnme.rtype);
|
||||
var i2 = ref_type_sort_order(pnot.rtype);
|
||||
|
||||
return i1 < i2 ? -1 : (i1 > i2 ? 1 : 0);
|
||||
}
|
||||
|
||||
if (pnme.rtype == Gitg.RefType.REMOTE)
|
||||
{
|
||||
return pnme.remote_name.casefold().collate(pnot.remote_name.casefold());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int compare_to(RefRow other)
|
||||
{
|
||||
if (reference == null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (other.reference == null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
var ct = compare_type(other);
|
||||
|
||||
if (ct != 0)
|
||||
{
|
||||
return ct;
|
||||
}
|
||||
|
||||
var t1 = label_text();
|
||||
var t2 = other.label_text();
|
||||
|
||||
var hassep1 = t1.index_of_char('/');
|
||||
var hassep2 = t2.index_of_char('/');
|
||||
|
||||
if ((hassep1 >= 0) != (hassep2 >= 0))
|
||||
{
|
||||
return hassep1 >= 0 ? 1 : -1;
|
||||
}
|
||||
|
||||
return t1.casefold().collate(t2.casefold());
|
||||
}
|
||||
}
|
||||
|
||||
private class RefHeader : RefTyped, Gtk.Label
|
||||
{
|
||||
private Gitg.RefType d_rtype;
|
||||
private bool d_is_sub_header_remote;
|
||||
private string d_name;
|
||||
|
||||
public Gitg.RefType ref_type
|
||||
{
|
||||
get { return d_rtype; }
|
||||
}
|
||||
|
||||
public RefHeader(Gitg.RefType rtype, string name)
|
||||
{
|
||||
var escaped = Markup.escape_text(name);
|
||||
|
||||
set_markup(@"<b>$escaped</b>");
|
||||
xalign = 0;
|
||||
|
||||
d_name = name;
|
||||
d_rtype = rtype;
|
||||
|
||||
margin_top = 3;
|
||||
margin_bottom = 3;
|
||||
margin_left = 16;
|
||||
}
|
||||
|
||||
public RefHeader.remote(string name)
|
||||
{
|
||||
this(Gitg.RefType.REMOTE, name);
|
||||
d_is_sub_header_remote = true;
|
||||
margin_left += 12;
|
||||
}
|
||||
|
||||
public int compare_to(RefHeader other)
|
||||
{
|
||||
// Both are headers of remote type
|
||||
if (d_is_sub_header_remote != other.d_is_sub_header_remote)
|
||||
{
|
||||
return d_is_sub_header_remote ? 1 : -1;
|
||||
}
|
||||
|
||||
return d_name.casefold().collate(other.d_name.casefold());
|
||||
}
|
||||
}
|
||||
|
||||
public class RefsList : Gtk.ListBox
|
||||
{
|
||||
public signal void edited(Gitg.Ref reference, Gtk.TreePath path, string text);
|
||||
public signal void ref_activated(Gitg.Ref? r);
|
||||
|
||||
private Gitg.Repository? d_repository;
|
||||
private Gee.HashMap<string, RefHeader> d_remote_headers;
|
||||
|
||||
public Gitg.Repository? repository
|
||||
{
|
||||
get { return d_repository; }
|
||||
set
|
||||
{
|
||||
if (d_repository != value)
|
||||
{
|
||||
d_repository = value;
|
||||
}
|
||||
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
construct
|
||||
{
|
||||
d_remote_headers = new Gee.HashMap<string, RefHeader>();
|
||||
set_sort_func(sort_rows);
|
||||
}
|
||||
|
||||
private int sort_rows(Gtk.ListBoxRow row1, Gtk.ListBoxRow row2)
|
||||
{
|
||||
var c1 = row1.get_child();
|
||||
var c2 = row2.get_child();
|
||||
|
||||
var r1 = ((RefTyped)c1).ref_type;
|
||||
var r2 = ((RefTyped)c2).ref_type;
|
||||
|
||||
// Compare types first
|
||||
var rs1 = ref_type_sort_order(r1);
|
||||
var rs2 = ref_type_sort_order(r2);
|
||||
|
||||
if (rs1 != rs2)
|
||||
{
|
||||
return rs1 < rs2 ? -1 : 1;
|
||||
}
|
||||
|
||||
var head1 = c1 as RefHeader;
|
||||
var ref1 = c1 as RefRow;
|
||||
|
||||
var head2 = c2 as RefHeader;
|
||||
var ref2 = c2 as RefRow;
|
||||
|
||||
if ((head1 == null) != (head2 == null))
|
||||
{
|
||||
// Only one is a header
|
||||
return head1 != null ? -1 : 1;
|
||||
}
|
||||
else if (head1 != null && head2 != null)
|
||||
{
|
||||
return head1.compare_to(head2);
|
||||
}
|
||||
else
|
||||
{
|
||||
return ref1.compare_to(ref2);
|
||||
}
|
||||
}
|
||||
|
||||
private void clear()
|
||||
{
|
||||
d_remote_headers = new Gee.HashMap<string, RefHeader>();
|
||||
|
||||
foreach (var child in get_children())
|
||||
{
|
||||
child.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
private void add_header(Gitg.RefType ref_type, string name)
|
||||
{
|
||||
var header = new RefHeader(ref_type, name);
|
||||
header.show();
|
||||
|
||||
add(header);
|
||||
}
|
||||
|
||||
private RefHeader add_remote_header(string name)
|
||||
{
|
||||
var header = new RefHeader.remote(name);
|
||||
header.show();
|
||||
|
||||
add(header);
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
private void add_ref(Gitg.Ref? reference)
|
||||
{
|
||||
var row = new RefRow(reference);
|
||||
row.show();
|
||||
|
||||
add(row);
|
||||
}
|
||||
|
||||
// Checks if the provided reference is a symbolic ref with the name HEAD.
|
||||
private bool ref_is_a_symbolic_head(Gitg.Ref reference)
|
||||
{
|
||||
if (reference.get_reference_type() != Ggit.RefType.SYMBOLIC)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string name;
|
||||
|
||||
if (reference.parsed_name.rtype == Gitg.RefType.REMOTE)
|
||||
{
|
||||
name = reference.parsed_name.remote_branch;
|
||||
}
|
||||
else
|
||||
{
|
||||
name = reference.parsed_name.shortname;
|
||||
}
|
||||
|
||||
return name == "HEAD";
|
||||
}
|
||||
|
||||
public void refresh()
|
||||
{
|
||||
clear();
|
||||
|
||||
if (d_repository == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
add_ref(null);
|
||||
|
||||
add_header(Gitg.RefType.BRANCH, _("Branches"));
|
||||
add_header(Gitg.RefType.REMOTE, _("Remotes"));
|
||||
add_header(Gitg.RefType.TAG, _("Tags"));
|
||||
|
||||
try
|
||||
{
|
||||
d_repository.references_foreach_name((nm) => {
|
||||
Gitg.Ref? r;
|
||||
|
||||
try
|
||||
{
|
||||
r = d_repository.lookup_reference(nm);
|
||||
} catch { return 0; }
|
||||
|
||||
// Skip symbolic refs named HEAD since they aren't really
|
||||
// useful to show (we get these for remotes for example)
|
||||
if (ref_is_a_symbolic_head(r))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (r.parsed_name.rtype == Gitg.RefType.REMOTE)
|
||||
{
|
||||
var remote = r.parsed_name.remote_name;
|
||||
|
||||
if (!d_remote_headers.has_key(remote))
|
||||
{
|
||||
d_remote_headers[remote] = add_remote_header(remote);
|
||||
}
|
||||
}
|
||||
|
||||
add_ref(r);
|
||||
return 0;
|
||||
});
|
||||
} catch {}
|
||||
}
|
||||
|
||||
private RefRow? get_ref_row(Gtk.ListBoxRow row)
|
||||
{
|
||||
return row.get_child() as RefRow;
|
||||
}
|
||||
|
||||
protected override void row_activated(Gtk.ListBoxRow row)
|
||||
{
|
||||
var r = get_ref_row(row);
|
||||
|
||||
if (r != null)
|
||||
{
|
||||
ref_activated(r.reference);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ex: ts=4 noet
|
|
@ -29,8 +29,8 @@ namespace GitgHistory
|
|||
|
||||
public GitgExt.Application? application { owned get; construct set; }
|
||||
|
||||
private Navigation? d_navigation_model;
|
||||
private Gitg.CommitModel? d_commit_list_model;
|
||||
|
||||
private Gee.HashSet<Ggit.OId> d_selected;
|
||||
private Ggit.OId? d_scroll_to;
|
||||
private float d_scroll_y;
|
||||
|
@ -102,11 +102,6 @@ namespace GitgHistory
|
|||
d_selected = new Gee.HashSet<Ggit.OId>((Gee.HashDataFunc<Ggit.OId>)Ggit.OId.hash,
|
||||
(Gee.EqualDataFunc<Ggit.OId>)Ggit.OId.equal);
|
||||
|
||||
d_navigation_model = new Navigation(application.repository);
|
||||
d_navigation_model.ref_activated.connect((r) => {
|
||||
on_ref_activated(d_navigation_model, r);
|
||||
});
|
||||
|
||||
d_commit_list_model = new Gitg.CommitModel(application.repository);
|
||||
d_commit_list_model.started.connect(on_commit_model_started);
|
||||
d_commit_list_model.finished.connect(on_commit_model_finished);
|
||||
|
@ -221,22 +216,11 @@ namespace GitgHistory
|
|||
return -1;
|
||||
}
|
||||
|
||||
private void on_ref_activated(Navigation n, Gitg.Ref? r)
|
||||
{
|
||||
update_walker(n, r);
|
||||
}
|
||||
|
||||
public void activate()
|
||||
{
|
||||
d_main.navigation_view.expand_all();
|
||||
d_main.navigation_view.select_first();
|
||||
}
|
||||
|
||||
private void reload()
|
||||
{
|
||||
var view = d_main.commit_list_view;
|
||||
|
||||
double vadj = d_main.navigation_view.get_vadjustment().get_value();
|
||||
double vadj = d_main.refs_list.get_adjustment().get_value();
|
||||
|
||||
d_selected.clear();
|
||||
|
||||
|
@ -290,28 +274,24 @@ namespace GitgHistory
|
|||
d_commit_list_model.repository = repository;
|
||||
|
||||
// Reloads branches, tags, etc.
|
||||
d_navigation_model.repository = repository;
|
||||
d_main.refs_list.repository = repository;
|
||||
|
||||
ulong sid = 0;
|
||||
|
||||
sid = d_main.navigation_view.size_allocate.connect((a) => {
|
||||
d_main.navigation_view.get_vadjustment().set_value(vadj);
|
||||
sid = d_main.refs_list.size_allocate.connect((a) => {
|
||||
d_main.refs_list.get_adjustment().set_value(vadj);
|
||||
|
||||
if (sid != 0)
|
||||
{
|
||||
d_main.navigation_view.disconnect(sid);
|
||||
d_main.refs_list.disconnect(sid);
|
||||
}
|
||||
});
|
||||
|
||||
d_main.navigation_view.expand_all();
|
||||
d_main.navigation_view.select();
|
||||
}
|
||||
|
||||
private void build_ui()
|
||||
{
|
||||
d_main = new Paned();
|
||||
|
||||
d_main.navigation_view.model = d_navigation_model;
|
||||
d_main.commit_list_view.model = d_commit_list_model;
|
||||
|
||||
d_main.commit_list_view.get_selection().changed.connect((sel) => {
|
||||
|
@ -329,9 +309,18 @@ namespace GitgHistory
|
|||
|
||||
d_panels = new Gitg.UIElements<GitgExt.HistoryPanel>(extset,
|
||||
d_main.stack_panel);
|
||||
|
||||
d_main.refs_list.ref_activated.connect((r) => {
|
||||
update_walker(r);
|
||||
});
|
||||
|
||||
application.bind_property("repository", d_main.refs_list,
|
||||
"repository",
|
||||
BindingFlags.DEFAULT |
|
||||
BindingFlags.SYNC_CREATE);
|
||||
}
|
||||
|
||||
private void update_walker(Navigation n, Gitg.Ref? head)
|
||||
private void update_walker(Gitg.Ref? head)
|
||||
{
|
||||
Ggit.OId? id = null;
|
||||
|
||||
|
@ -358,25 +347,45 @@ namespace GitgHistory
|
|||
else
|
||||
{
|
||||
var included = new Ggit.OId[] {};
|
||||
var repo = application.repository;
|
||||
|
||||
// Simply push all the refs
|
||||
foreach (Gitg.Ref r in n.all)
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
var resolved = r.resolve();
|
||||
repo.references_foreach_name((nm) => {
|
||||
Gitg.Ref? r;
|
||||
|
||||
try
|
||||
{
|
||||
var t = application.repository.lookup<Ggit.Tag>(resolved.get_target());
|
||||
included += t.get_target_id();
|
||||
}
|
||||
catch
|
||||
r = repo.lookup_reference(nm);
|
||||
} catch { return 0; }
|
||||
|
||||
try
|
||||
{
|
||||
included += resolved.get_target();
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
var resolved = r.resolve();
|
||||
|
||||
try
|
||||
{
|
||||
var t = repo.lookup<Ggit.Tag>(resolved.get_target());
|
||||
included += t.get_target_id();
|
||||
}
|
||||
catch
|
||||
{
|
||||
included += resolved.get_target();
|
||||
}
|
||||
} catch {}
|
||||
|
||||
return 0;
|
||||
});
|
||||
} catch {}
|
||||
|
||||
try
|
||||
{
|
||||
if (repo.is_head_detached())
|
||||
{
|
||||
var resolved = repo.get_head().resolve();
|
||||
included += resolved.get_target();
|
||||
}
|
||||
} catch {}
|
||||
|
||||
d_commit_list_model.set_include(included);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
<file compressed="true" preprocess="xml-stripblanks">ui/gitg-clone-dialog.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks">ui/gitg-author-details-dialog.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks">ui/gitg-history-paned.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks">ui/gitg-history-ref-row.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks">ui/gitg-commit-paned.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks">ui/gitg-commit-dialog.ui</file>
|
||||
<file compressed="true">ui/style.css</file>
|
||||
|
|
|
@ -32,11 +32,9 @@
|
|||
<class name="sidebar"/>
|
||||
</style>
|
||||
<child>
|
||||
<object class="GitgHistoryNavigationView" id="d_navigation_view">
|
||||
<object class="GitgHistoryRefsList" id="d_refs_list">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="headers_visible">False</property>
|
||||
<property name="name">tree_view_navigation</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
|
24
gitg/resources/ui/gitg-history-ref-row.ui
Normal file
24
gitg/resources/ui/gitg-history-ref-row.ui
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<!-- interface-requires gtk+ 3.3 -->
|
||||
<!-- interface-requires gitg 0.0 -->
|
||||
<!-- interface-requires gd 1.0 -->
|
||||
<template class="GitgHistoryRefRow">
|
||||
<property name="orientation">horizontal</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="margin_left">16</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="d_icon">
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="d_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="ellipsize">middle</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
</interface>
|
||||
<!-- vi:ts=2:et -->
|
Loading…
Reference in a new issue