[gitg/wip/actions: 45/61] Implement history navigation as ListBox



commit 980dd2e7decd38132499de9a1ba60ac00939975a
Author: Jesse van den Kieboom <jessevdk gmail com>
Date:   Wed Jan 15 00:45:04 2014 +0100

    Implement history navigation as ListBox

 gitg/Makefile.am                          |    2 +-
 gitg/history/gitg-history-navigation.vala |  581 -----------------------------
 gitg/history/gitg-history-paned.vala      |    6 +-
 gitg/history/gitg-history-refs-list.vala  |  418 +++++++++++++++++++++
 gitg/history/gitg-history.vala            |  105 +++---
 gitg/resources/gitg-resources.xml         |    1 +
 gitg/resources/ui/gitg-history-paned.ui   |    4 +-
 gitg/resources/ui/gitg-history-ref-row.ui |   24 ++
 8 files changed, 491 insertions(+), 650 deletions(-)
---
diff --git a/gitg/Makefile.am b/gitg/Makefile.am
index 8cbc485..35eeaa7 100644
--- a/gitg/Makefile.am
+++ b/gitg/Makefile.am
@@ -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                      \
diff --git a/gitg/history/gitg-history-paned.vala b/gitg/history/gitg-history-paned.vala
index 79a1d52..81951a8 100644
--- a/gitg/history/gitg-history-paned.vala
+++ b/gitg/history/gitg-history-paned.vala
@@ -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
diff --git a/gitg/history/gitg-history-refs-list.vala b/gitg/history/gitg-history-refs-list.vala
new file mode 100644
index 0000000..95c2e11
--- /dev/null
+++ b/gitg/history/gitg-history-refs-list.vala
@@ -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
diff --git a/gitg/history/gitg-history.vala b/gitg/history/gitg-history.vala
index 57bf6b8..3d29258 100644
--- a/gitg/history/gitg-history.vala
+++ b/gitg/history/gitg-history.vala
@@ -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 ulong d_insertsig;
                private Settings d_settings;
@@ -49,18 +49,6 @@ namespace GitgHistory
                        owned get { return "/org/gnome/gitg/Activities/History"; }
                }
 
-               [Notify]
-               public Gitg.Repository repository
-               {
-                       set
-                       {
-                               if (value != null)
-                               {
-                                       reload();
-                               }
-                       }
-               }
-
                public void foreach_selected(GitgExt.ForeachCommitSelectionFunc func)
                {
                        bool breakit = false;
@@ -88,25 +76,14 @@ 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);
 
                        update_sort_mode();
 
-                       application.bind_property("repository", d_navigation_model,
-                                                 "repository", BindingFlags.DEFAULT);
-
                        application.bind_property("repository", d_commit_list_model,
                                                  "repository", BindingFlags.DEFAULT);
-
-                       application.bind_property("repository", this,
-                                                 "repository", BindingFlags.DEFAULT);
                }
 
                private void update_sort_mode()
@@ -197,35 +174,10 @@ 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();
-               }
-
-               public void reload()
-               {
-                       double vadj = d_main.navigation_view.get_vadjustment().get_value();
-
-                       d_navigation_model.reload();
-                       d_main.navigation_view.expand_all();
-                       d_main.navigation_view.select();
-
-                       d_main.navigation_view.size_allocate.connect((a) => {
-                               d_main.navigation_view.get_vadjustment().set_value(vadj);
-                       });
-               }
-
                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) => {
@@ -243,6 +195,15 @@ 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)
@@ -275,25 +236,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);
                        }
diff --git a/gitg/resources/gitg-resources.xml b/gitg/resources/gitg-resources.xml
index 6c4b21c..f913934 100644
--- a/gitg/resources/gitg-resources.xml
+++ b/gitg/resources/gitg-resources.xml
@@ -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>
diff --git a/gitg/resources/ui/gitg-history-paned.ui b/gitg/resources/ui/gitg-history-paned.ui
index 761d032..53cd0c4 100644
--- a/gitg/resources/ui/gitg-history-paned.ui
+++ b/gitg/resources/ui/gitg-history-paned.ui
@@ -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>
diff --git a/gitg/resources/ui/gitg-history-ref-row.ui b/gitg/resources/ui/gitg-history-ref-row.ui
new file mode 100644
index 0000000..46b0e2f
--- /dev/null
+++ b/gitg/resources/ui/gitg-history-ref-row.ui
@@ -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 -->


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]