[gitg/xiaoguang/split-view: 3/4] diff-view: Add split view function



commit 9b97e098b83d9d3e0e17d621337a4da618c3f2a9
Author: Xiaoguang Wang <xwang suse com>
Date:   Mon Jan 22 10:15:06 2018 +0800

    diff-view: Add split view function
    
    https://bugzilla.gnome.org/show_bug.cgi?id=792706

 libgitg/gitg-diff-view-file-renderer-text.vala | 530 +++++++++++++++++++++++--
 libgitg/gitg-diff-view-file.vala               | 161 +++++++-
 libgitg/gitg-diff-view-lines-renderer.vala     |  78 +++-
 libgitg/gitg-diff-view.vala                    |  22 +-
 libgitg/resources/ui/gitg-diff-view-file.ui    | 104 +++--
 5 files changed, 814 insertions(+), 81 deletions(-)
---
diff --git a/libgitg/gitg-diff-view-file-renderer-text.vala b/libgitg/gitg-diff-view-file-renderer-text.vala
index 6b68bb6b..f5639a4f 100644
--- a/libgitg/gitg-diff-view-file-renderer-text.vala
+++ b/libgitg/gitg-diff-view-file-renderer-text.vala
@@ -27,6 +27,13 @@ class Gitg.DiffViewFileRendererText : Gtk.SourceView, DiffSelectable, DiffViewFi
                CONTEXT
        }
 
+       public enum Style
+       {
+               ONE,
+               OLD,
+               NEW
+       }
+
        private struct Region
        {
                public RegionType type;
@@ -60,6 +67,8 @@ class Gitg.DiffViewFileRendererText : Gtk.SourceView, DiffSelectable, DiffViewFi
 
        private Settings? d_stylesettings;
 
+       public Style d_style { get; construct set; }
+
        public bool new_is_workdir { get; construct set; }
 
        public bool wrap_lines
@@ -168,29 +177,58 @@ class Gitg.DiffViewFileRendererText : Gtk.SourceView, DiffSelectable, DiffViewFi
                }
        }
 
-       public DiffViewFileRendererText(DiffViewFileInfo info, bool can_select)
+       public DiffViewFileRendererText(DiffViewFileInfo info, bool can_select, Style d_style)
        {
-               Object(info: info, can_select: can_select);
+               Object(info: info, can_select: can_select, d_style: d_style);
        }
 
        construct
        {
                var gutter = this.get_gutter(Gtk.TextWindowType.LEFT);
 
-               d_old_lines = new DiffViewLinesRenderer(DiffViewLinesRenderer.Style.OLD);
-               d_new_lines = new DiffViewLinesRenderer(DiffViewLinesRenderer.Style.NEW);
-               d_sym_lines = new DiffViewLinesRenderer(DiffViewLinesRenderer.Style.SYMBOL);
+               if (this.d_style == Style.ONE)
+               {
+                       d_old_lines = new DiffViewLinesRenderer(DiffViewLinesRenderer.Style.OLD);
+                       d_new_lines = new DiffViewLinesRenderer(DiffViewLinesRenderer.Style.NEW);
+                       d_sym_lines = new DiffViewLinesRenderer(DiffViewLinesRenderer.Style.SYMBOL);
 
-               this.bind_property("maxlines", d_old_lines, "maxlines", BindingFlags.DEFAULT | 
BindingFlags.SYNC_CREATE);
-               this.bind_property("maxlines", d_new_lines, "maxlines", BindingFlags.DEFAULT | 
BindingFlags.SYNC_CREATE);
+                       this.bind_property("maxlines", d_old_lines, "maxlines", BindingFlags.DEFAULT | 
BindingFlags.SYNC_CREATE);
+                       this.bind_property("maxlines", d_new_lines, "maxlines", BindingFlags.DEFAULT | 
BindingFlags.SYNC_CREATE);
 
-               d_old_lines.xpad = 8;
-               d_new_lines.xpad = 8;
-               d_sym_lines.xpad = 6;
+                       d_old_lines.xpad = 8;
+                       d_new_lines.xpad = 8;
+                       d_sym_lines.xpad = 6;
 
-               gutter.insert(d_old_lines, 0);
-               gutter.insert(d_new_lines, 1);
-               gutter.insert(d_sym_lines, 2);
+                       gutter.insert(d_old_lines, 0);
+                       gutter.insert(d_new_lines, 1);
+                       gutter.insert(d_sym_lines, 2);
+               }
+               else if (this.d_style == Style.OLD)
+               {
+                       d_old_lines = new DiffViewLinesRenderer(DiffViewLinesRenderer.Style.OLD);
+                       d_sym_lines = new DiffViewLinesRenderer(DiffViewLinesRenderer.Style.SYMBOL_OLD);
+
+                       this.bind_property("maxlines", d_old_lines, "maxlines", BindingFlags.DEFAULT | 
BindingFlags.SYNC_CREATE);
+
+                       d_old_lines.xpad = 8;
+                       d_sym_lines.xpad = 6;
+
+                       gutter.insert(d_old_lines, 0);
+                       gutter.insert(d_sym_lines, 1);
+               }
+               else if (this.d_style == Style.NEW)
+               {
+                       d_new_lines = new DiffViewLinesRenderer(DiffViewLinesRenderer.Style.NEW);
+                       d_sym_lines = new DiffViewLinesRenderer(DiffViewLinesRenderer.Style.SYMBOL_NEW);
+
+                       this.bind_property("maxlines", d_new_lines, "maxlines", BindingFlags.DEFAULT | 
BindingFlags.SYNC_CREATE);
+
+                       d_new_lines.xpad = 8;
+                       d_sym_lines.xpad = 6;
+
+                       gutter.insert(d_new_lines, 0);
+                       gutter.insert(d_sym_lines, 1);
+               }
 
                this.set_border_window_size(Gtk.TextWindowType.TOP, 1);
 
@@ -562,15 +600,35 @@ class Gitg.DiffViewFileRendererText : Gtk.SourceView, DiffSelectable, DiffViewFi
 
                var ctx = this.get_style_context();
 
-               var old_lines_width = d_old_lines.size + d_old_lines.xpad * 2;
-               var new_lines_width = d_new_lines.size + d_new_lines.xpad * 2;
+               var old_lines_width = 0;
+               var new_lines_width = 0;
+
+               switch (this.d_style)
+               {
+               case Style.ONE:
+                       old_lines_width = d_old_lines.size + d_old_lines.xpad * 2;
+                       new_lines_width = d_new_lines.size + d_new_lines.xpad * 2;
+                       break;
+
+               case Style.OLD:
+                       old_lines_width = d_old_lines.size + d_old_lines.xpad * 2;
+                       break;
+
+               case Style.NEW:
+                       new_lines_width = d_new_lines.size + d_new_lines.xpad * 2;
+                       break;
+               }
+
                var sym_lines_width = d_sym_lines.size + d_sym_lines.xpad * 2;
 
-               ctx.save();
-               Gtk.cairo_transform_to_window(cr, this, win);
-               ctx.add_class("diff-lines-separator");
-               ctx.render_frame(cr, 0, 0, old_lines_width, win.get_height());
-               ctx.restore();
+               if (this.d_style == Style.ONE)
+               {
+                       ctx.save();
+                       Gtk.cairo_transform_to_window(cr, this, win);
+                       ctx.add_class("diff-lines-separator");
+                       ctx.render_frame(cr, 0, 0, old_lines_width, win.get_height());
+                       ctx.restore();
+               }
 
                ctx.save();
                Gtk.cairo_transform_to_window(cr, this, win);
@@ -616,6 +674,22 @@ class Gitg.DiffViewFileRendererText : Gtk.SourceView, DiffSelectable, DiffViewFi
        }
 
        public void add_hunk(Ggit.DiffHunk hunk, Gee.ArrayList<Ggit.DiffLine> lines)
+       {
+               switch (this.d_style)
+               {
+               case Style.ONE:
+                       add_hunk_one(hunk, lines);
+                       break;
+               case Style.OLD:
+                       add_hunk_left(hunk, lines);
+                       break;
+               case Style.NEW:
+                       add_hunk_right(hunk, lines);
+                       break;
+               }
+       }
+
+       private void add_hunk_one(Ggit.DiffHunk hunk, Gee.ArrayList<Ggit.DiffLine> lines)
        {
                var buffer = this.buffer as Gtk.SourceBuffer;
 
@@ -646,8 +720,7 @@ class Gitg.DiffViewFileRendererText : Gtk.SourceView, DiffSelectable, DiffViewFi
 
                int buffer_line = iter.get_line();
 
-               /* Diff Content */
-               var content = new StringBuilder();
+               int line_hunk_start = iter.get_line();
 
                var region = Region() {
                        type = RegionType.CONTEXT,
@@ -747,8 +820,31 @@ class Gitg.DiffViewFileRendererText : Gtk.SourceView, DiffSelectable, DiffViewFi
                                text = text.slice(0, text.length - 1);
                        }
 
-                       content.append(text);
-                       buffer_line++;
+                       if (rtype == RegionType.CONTEXT)
+                       {
+                               buffer.insert(ref iter, text, -1);
+                               buffer_line++;
+                       }
+
+                       if (rtype == RegionType.REMOVED)
+                       {
+                               Gtk.TextIter t_iter;
+                               buffer.get_end_iter(out t_iter);
+                               buffer.create_source_mark(null, "removed", t_iter);
+
+                               buffer.insert(ref iter, text, -1);
+                               buffer_line++;
+                       }
+
+                       if (rtype == RegionType.ADDED)
+                       {
+                               Gtk.TextIter t_iter;
+                               buffer.get_end_iter(out t_iter);
+                               buffer.create_source_mark(null, "added", t_iter);
+
+                               buffer.insert(ref iter, text, -1);
+                               buffer_line++;
+                       }
                }
 
                if (lines.size != 0)
@@ -756,36 +852,400 @@ class Gitg.DiffViewFileRendererText : Gtk.SourceView, DiffSelectable, DiffViewFi
                        d_regions += region;
                }
 
+               d_old_lines.add_hunk(line_hunk_start, iter.get_line(), hunk, buffer);
+               d_new_lines.add_hunk(line_hunk_start, iter.get_line(), hunk, buffer);
+               d_sym_lines.add_hunk(line_hunk_start, iter.get_line(), hunk, buffer);
+
+               this.thaw_notify();
+
+               sensitive = true;
+       }
+
+       private void add_hunk_left(Ggit.DiffHunk hunk, Gee.ArrayList<Ggit.DiffLine> lines)
+       {
+               var buffer = this.buffer as Gtk.SourceBuffer;
+
+               /* Diff hunk */
+               var h = hunk.get_header();
+               var pos = h.last_index_of("@@");
+
+               if (pos >= 0)
+               {
+                       h = h.substring(pos + 2).chug();
+               }
+
+               h = h.chomp();
+
+               Gtk.TextIter iter;
+               buffer.get_end_iter(out iter);
+
+               if (!iter.is_start())
+               {
+                       buffer.insert(ref iter, "\n", 1);
+               }
+
+               iter.set_line_offset(0);
+               buffer.create_source_mark(null, "header", iter);
+
+               var header = @"@@ -$(hunk.get_old_start()),$(hunk.get_old_lines()) 
+$(hunk.get_new_start()),$(hunk.get_new_lines()) @@ $h\n";
+               buffer.insert(ref iter, header, -1);
+
+               int buffer_line = iter.get_line();
+
                int line_hunk_start = iter.get_line();
 
-               buffer.insert(ref iter, (string)content.data, -1);
+               var region = Region() {
+                       type = RegionType.CONTEXT,
+                       buffer_line_start = 0,
+                       source_line_start = 0,
+                       length = 0
+               };
 
-               d_old_lines.add_hunk(line_hunk_start, iter.get_line(), hunk, lines);
-               d_new_lines.add_hunk(line_hunk_start, iter.get_line(), hunk, lines);
-               d_sym_lines.add_hunk(line_hunk_start, iter.get_line(), hunk, lines);
+               this.freeze_notify();
 
+               var add_line_num = 0;
+               var remove_line_num = 0;
+               var in_change_line = false;
                for (var i = 0; i < lines.size; i++)
                {
                        var line = lines[i];
-                       string? category = null;
+                       var text = line.get_text().replace("\r", "");
+                       var added = false;
+                       var removed = false;
+                       var origin = line.get_origin();
+
+                       var rtype = RegionType.CONTEXT;
 
-                       switch (line.get_origin())
+                       switch (origin)
                        {
                                case Ggit.DiffLineType.ADDITION:
-                                       category = "added";
+                                       added = true;
+                                       this.added++;
+
+                                       rtype = RegionType.ADDED;
                                        break;
                                case Ggit.DiffLineType.DELETION:
-                                       category = "removed";
+                                       removed = true;
+                                       this.removed++;
+
+                                       rtype = RegionType.REMOVED;
+                                       break;
+                               case Ggit.DiffLineType.CONTEXT_EOFNL:
+                               case Ggit.DiffLineType.ADD_EOFNL:
+                               case Ggit.DiffLineType.DEL_EOFNL:
+                                       text = text.substring(1);
                                        break;
                        }
 
-                       if (category != null)
+                       if (i == 0 || rtype != region.type)
                        {
-                               buffer.get_iter_at_line(out iter, line_hunk_start + i);
-                               buffer.create_source_mark(null, category, iter);
+                               if (i != 0)
+                               {
+                                       d_regions += region;
+                               }
+
+                               int source_line_start;
+
+                               if (rtype == RegionType.REMOVED)
+                               {
+                                       source_line_start = line.get_old_lineno() - 1;
+                               }
+                               else
+                               {
+                                       source_line_start = line.get_new_lineno() - 1;
+                               }
+
+                               region = Region() {
+                                       type = rtype,
+                                       buffer_line_start = buffer_line,
+                                       source_line_start = source_line_start,
+                                       length = 0
+                               };
                        }
+
+                       if (added || removed)
+                       {
+                               var offset = (size_t)line.get_content_offset();
+                               var bytes = line.get_content();
+
+                               var pset = PatchSet.Patch() {
+                                       type = added ? PatchSet.Type.ADD : PatchSet.Type.REMOVE,
+                                       old_offset = offset,
+                                       new_offset = offset,
+                                       length = bytes.length
+                               };
+
+                               if (added)
+                               {
+                                       pset.old_offset = (size_t)((int64)pset.old_offset - d_doffset);
+                               }
+                               else
+                               {
+                                       pset.new_offset = (size_t)((int64)pset.new_offset + d_doffset);
+                               }
+
+                               d_lines[buffer_line] = pset;
+                               d_doffset += added ? (int64)bytes.length : -(int64)bytes.length;
+                       }
+
+                       if (i == lines.size - 1 && text.length > 0 && text[text.length - 1] == '\n')
+                       {
+                               text = text.slice(0, text.length - 1);
+                       }
+
+                       if (rtype == RegionType.CONTEXT)
+                       {
+                               if (in_change_line == true)
+                               {
+                                       if (add_line_num > remove_line_num)
+                                       {
+                                               for (var l = 0; l < (add_line_num - remove_line_num); l++)
+                                               {
+                                                       Gtk.TextIter t_iter;
+                                                       buffer.get_end_iter(out t_iter);
+                                                       buffer.create_source_mark(null, "empty", t_iter);
+
+                                                       buffer.insert(ref iter, "\n", -1);
+                                                       buffer_line++;
+                                                       region.buffer_line_start = buffer_line;
+                                               }
+                                       }
+
+                                       add_line_num = 0;
+                                       remove_line_num = 0;
+                               }
+
+                               in_change_line = false;
+
+                               buffer.insert(ref iter, text, -1);
+                               buffer_line++;
+                               region.length++;
+                       }
+
+                       if (rtype == RegionType.REMOVED)
+                       {
+                               Gtk.TextIter t_iter;
+                               buffer.get_end_iter(out t_iter);
+                               buffer.create_source_mark(null, "removed", t_iter);
+
+                               buffer.insert(ref iter, text, -1);
+                               buffer_line++;
+                               region.length++;
+
+                               remove_line_num++;
+                               in_change_line = true;
+                       }
+
+                       if (rtype == RegionType.ADDED)
+                       {
+                               add_line_num++;
+                               in_change_line = true;
+                       }
+               }
+
+               if (lines.size != 0)
+               {
+                       d_regions += region;
+               }
+
+               d_old_lines.add_hunk(line_hunk_start, iter.get_line(), hunk, buffer);
+               d_sym_lines.add_hunk(line_hunk_start, iter.get_line(), hunk, buffer);
+
+               this.thaw_notify();
+
+               sensitive = true;
+       }
+
+       private void add_hunk_right(Ggit.DiffHunk hunk, Gee.ArrayList<Ggit.DiffLine> lines)
+       {
+               var buffer = this.buffer as Gtk.SourceBuffer;
+
+               /* Diff hunk */
+               var h = hunk.get_header();
+               var pos = h.last_index_of("@@");
+
+               if (pos >= 0)
+               {
+                       h = h.substring(pos + 2).chug();
+               }
+
+               h = h.chomp();
+
+               Gtk.TextIter iter;
+               buffer.get_end_iter(out iter);
+
+               if (!iter.is_start())
+               {
+                       buffer.insert(ref iter, "\n", 1);
+               }
+
+               iter.set_line_offset(0);
+               buffer.create_source_mark(null, "header", iter);
+
+               var header = @"@@ -$(hunk.get_old_start()),$(hunk.get_old_lines()) 
+$(hunk.get_new_start()),$(hunk.get_new_lines()) @@ $h\n";
+               buffer.insert(ref iter, header, -1);
+
+               int buffer_line = iter.get_line();
+
+               int line_hunk_start = iter.get_line();
+
+               var region = Region() {
+                       type = RegionType.CONTEXT,
+                       buffer_line_start = 0,
+                       source_line_start = 0,
+                       length = 0
+               };
+
+               this.freeze_notify();
+
+               var add_line_num = 0;
+               var remove_line_num = 0;
+               var in_change_line = false;
+               for (var i = 0; i < lines.size; i++)
+               {
+                       var line = lines[i];
+                       var text = line.get_text().replace("\r", "");
+                       var added = false;
+                       var removed = false;
+                       var origin = line.get_origin();
+
+                       var rtype = RegionType.CONTEXT;
+
+                       switch (origin)
+                       {
+                               case Ggit.DiffLineType.ADDITION:
+                                       added = true;
+                                       this.added++;
+
+                                       rtype = RegionType.ADDED;
+                                       break;
+                               case Ggit.DiffLineType.DELETION:
+                                       removed = true;
+                                       this.removed++;
+
+                                       rtype = RegionType.REMOVED;
+                                       break;
+                               case Ggit.DiffLineType.CONTEXT_EOFNL:
+                               case Ggit.DiffLineType.ADD_EOFNL:
+                               case Ggit.DiffLineType.DEL_EOFNL:
+                                       text = text.substring(1);
+                                       break;
+                       }
+
+                       if (i == 0 || rtype != region.type)
+                       {
+                               if (i != 0)
+                               {
+                                       d_regions += region;
+                               }
+
+                               int source_line_start;
+
+                               if (rtype == RegionType.REMOVED)
+                               {
+                                       source_line_start = line.get_old_lineno() - 1;
+                               }
+                               else
+                               {
+                                       source_line_start = line.get_new_lineno() - 1;
+                               }
+
+                               region = Region() {
+                                       type = rtype,
+                                       buffer_line_start = buffer_line,
+                                       source_line_start = source_line_start,
+                                       length = 0
+                               };
+                       }
+
+                       if (added || removed)
+                       {
+                               var offset = (size_t)line.get_content_offset();
+                               var bytes = line.get_content();
+
+                               var pset = PatchSet.Patch() {
+                                       type = added ? PatchSet.Type.ADD : PatchSet.Type.REMOVE,
+                                       old_offset = offset,
+                                       new_offset = offset,
+                                       length = bytes.length
+                               };
+
+                               if (added)
+                               {
+                                       pset.old_offset = (size_t)((int64)pset.old_offset - d_doffset);
+                               }
+                               else
+                               {
+                                       pset.new_offset = (size_t)((int64)pset.new_offset + d_doffset);
+                               }
+
+                               d_lines[buffer_line] = pset;
+                               d_doffset += added ? (int64)bytes.length : -(int64)bytes.length;
+                       }
+
+                       if (i == lines.size - 1 && text.length > 0 && text[text.length - 1] == '\n')
+                       {
+                               text = text.slice(0, text.length - 1);
+                       }
+
+                       if (rtype == RegionType.CONTEXT)
+                       {
+                               if (in_change_line == true)
+                               {
+                                       if (remove_line_num > add_line_num)
+                                       {
+                                               for (var l = 0; l < (remove_line_num - add_line_num); l++)
+                                               {
+                                                       Gtk.TextIter t_iter;
+                                                       buffer.get_end_iter(out t_iter);
+                                                       buffer.create_source_mark(null, "empty", t_iter);
+
+                                                       buffer.insert(ref iter, "\n", -1);
+                                                       buffer_line++;
+                                                       region.buffer_line_start = buffer_line;
+                                               }
+                                       }
+
+                                       add_line_num = 0;
+                                       remove_line_num = 0;
+                               }
+
+                               in_change_line = false;
+
+                               buffer.insert(ref iter, text, -1);
+                               buffer_line++;
+                               region.length++;
+                       }
+
+                       if (rtype == RegionType.ADDED)
+                       {
+                               Gtk.TextIter t_iter;
+                               buffer.get_end_iter(out t_iter);
+                               buffer.create_source_mark(null, "added", t_iter);
+
+                               buffer.insert(ref iter, text, -1);
+                               buffer_line++;
+                               region.length++;
+
+                               add_line_num++;
+                               in_change_line = true;
+                       }
+
+                       if (rtype == RegionType.REMOVED)
+                       {
+                               remove_line_num++;
+                               in_change_line = true;
+                       }
+               }
+
+               if (lines.size != 0)
+               {
+                       d_regions += region;
                }
 
+               d_new_lines.add_hunk(line_hunk_start, iter.get_line(), hunk, buffer);
+               d_sym_lines.add_hunk(line_hunk_start, iter.get_line(), hunk, buffer);
+
                this.thaw_notify();
 
                sensitive = true;
diff --git a/libgitg/gitg-diff-view-file.vala b/libgitg/gitg-diff-view-file.vala
index afec0608..917fd267 100644
--- a/libgitg/gitg-diff-view-file.vala
+++ b/libgitg/gitg-diff-view-file.vala
@@ -32,15 +32,33 @@ class Gitg.DiffViewFile : Gtk.Grid
        [GtkChild( name = "revealer_content" )]
        private Gtk.Revealer d_revealer_content;
 
+       [GtkChild( name = "box_file_renderer" )]
+       private Gtk.Box d_box_file_renderer;
+
+       [GtkChild( name = "d_split_button" )]
+       private Gtk.ToggleButton d_split_button;
+
+       private Gtk.ScrolledWindow d_scrolledwindow;
+       private Gtk.ScrolledWindow d_scrolledwindow_left;
+       private Gtk.ScrolledWindow d_scrolledwindow_right;
+
        private bool d_expanded;
 
        private Binding? d_vexpand_binding;
+       private Binding? d_vexpand_binding_l;
+       private Binding? d_vexpand_binding_r;
+
+       private bool d_split { get; set; default = false; }
+
+       private DiffViewFileRenderer? d_renderer;
+       private DiffViewFileRenderer? d_renderer_left;
+       private DiffViewFileRenderer? d_renderer_right;
 
        public DiffViewFileRenderer? renderer
        {
                owned get
                {
-                       return d_revealer_content.get_child() as DiffViewFileRenderer;
+                       return d_renderer;
                }
 
                construct set
@@ -57,15 +75,120 @@ class Gitg.DiffViewFile : Gtk.Grid
 
                                if (current != null)
                                {
-                                       d_revealer_content.remove(current);
+                                       d_scrolledwindow.remove(current);
+                                       d_box_file_renderer.remove(d_scrolledwindow);
+                               }
+
+                               d_renderer = value;
+                               d_scrolledwindow.add(value);
+                               d_scrolledwindow.show();
+
+                               if (!d_split)
+                               {
+                                       d_box_file_renderer.pack_start(d_scrolledwindow, true, true, 0);
                                }
 
-                               d_revealer_content.add(value);
                                d_vexpand_binding = this.bind_property("vexpand", value, "vexpand", 
BindingFlags.SYNC_CREATE);
                        }
                }
        }
 
+       public DiffViewFileRenderer? renderer_left
+       {
+               owned get
+               {
+                       return d_renderer_left;
+               }
+
+               construct set
+               {
+                       var current = this.renderer_left;
+
+                       if (current != value)
+                       {
+                               if (d_vexpand_binding_l != null)
+                               {
+                                       d_vexpand_binding_l.unbind();
+                                       d_vexpand_binding_l = null;
+                               }
+
+                               if (current != null)
+                               {
+                                       d_scrolledwindow_left.remove(current);
+                                       d_box_file_renderer.remove(d_scrolledwindow_left);
+                               }
+
+                               d_renderer_left = value;
+                               d_scrolledwindow_left.add(value);
+                               d_scrolledwindow_left.show();
+
+                               if (d_split)
+                               {
+                                       d_box_file_renderer.pack_start(d_scrolledwindow_left, true, true, 0);
+                               }
+
+                               d_vexpand_binding_l = this.bind_property("vexpand", value, "vexpand", 
BindingFlags.SYNC_CREATE);
+                       }
+               }
+       }
+
+       public DiffViewFileRenderer? renderer_right
+       {
+               owned get
+               {
+                       return d_renderer_right;
+               }
+
+               construct set
+               {
+                       var current = this.renderer_right;
+
+                       if (current != value)
+                       {
+                               if (d_vexpand_binding_r != null)
+                               {
+                                       d_vexpand_binding_r.unbind();
+                                       d_vexpand_binding_r = null;
+                               }
+
+                               if (current != null)
+                               {
+                                       d_scrolledwindow_right.remove(current);
+                                       d_box_file_renderer.remove(d_scrolledwindow_right);
+                               }
+
+                               d_renderer_right = value;
+                               d_scrolledwindow_right.add(value);
+                               d_scrolledwindow_right.show();
+
+                               if (d_split)
+                               {
+                                       d_box_file_renderer.pack_start(d_scrolledwindow_right, true, true, 0);
+                               }
+
+                               d_vexpand_binding_r = this.bind_property("vexpand", value, "vexpand", 
BindingFlags.SYNC_CREATE);
+                       }
+               }
+       }
+
+       [GtkCallback]
+       private void split_button_toggled(Gtk.ToggleButton button)
+       {
+               d_split = !d_split;
+               if (d_split)
+               {
+                       d_box_file_renderer.remove(d_scrolledwindow);
+                       d_box_file_renderer.pack_start(d_scrolledwindow_left, true, true, 0);
+                       d_box_file_renderer.pack_end(d_scrolledwindow_right, true, true, 0);
+               }
+               else
+               {
+                       d_box_file_renderer.remove(d_scrolledwindow_left);
+                       d_box_file_renderer.remove(d_scrolledwindow_right);
+                       d_box_file_renderer.pack_start(d_scrolledwindow, true, true, 0);
+               }
+       }
+
        public bool new_is_workdir { get; construct set; }
 
        public bool expanded
@@ -104,15 +227,40 @@ class Gitg.DiffViewFile : Gtk.Grid
                Object(repository: repository, delta: delta);
        }
 
+       construct
+       {
+               d_scrolledwindow = new Gtk.ScrolledWindow(null, null);
+               d_scrolledwindow_left = new Gtk.ScrolledWindow(null, null);
+               d_scrolledwindow_right = new Gtk.ScrolledWindow(null, null);
+
+               d_scrolledwindow.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.NEVER);
+               d_scrolledwindow_left.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.NEVER);
+               d_scrolledwindow_right.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.NEVER);
+       }
+
        public DiffViewFile.text(DiffViewFileInfo info, bool handle_selection)
        {
                this(info.repository, info.delta);
 
-               this.renderer = new DiffViewFileRendererText(info, handle_selection);
+               this.renderer = new DiffViewFileRendererText(info, handle_selection, 
DiffViewFileRendererText.Style.ONE);
                this.renderer.show();
 
                this.renderer.bind_property("added", d_diff_stat_file, "added");
                this.renderer.bind_property("removed", d_diff_stat_file, "removed");
+
+               this.renderer_left = new DiffViewFileRendererText(info, handle_selection, 
DiffViewFileRendererText.Style.OLD);
+               this.renderer_left.show();
+
+               this.renderer_left.bind_property("added", d_diff_stat_file, "added");
+               this.renderer_left.bind_property("removed", d_diff_stat_file, "removed");
+
+               this.renderer_right = new DiffViewFileRendererText(info, handle_selection, 
DiffViewFileRendererText.Style.NEW);
+               this.renderer_right.show();
+
+               this.renderer_right.bind_property("added", d_diff_stat_file, "added");
+               this.renderer_right.bind_property("removed", d_diff_stat_file, "removed");
+
+               d_split_button.visible = true;
        }
 
        public DiffViewFile.binary(Repository? repository, Ggit.DiffDelta delta)
@@ -269,6 +417,11 @@ class Gitg.DiffViewFile : Gtk.Grid
        public void add_hunk(Ggit.DiffHunk hunk, Gee.ArrayList<Ggit.DiffLine> lines)
        {
                this.renderer.add_hunk(hunk, lines);
+               if (this.renderer_left != null && this.renderer_right !=null)
+               {
+                       this.renderer_left.add_hunk(hunk, lines);
+                       this.renderer_right.add_hunk(hunk, lines);
+               }
        }
 }
 
diff --git a/libgitg/gitg-diff-view-lines-renderer.vala b/libgitg/gitg-diff-view-lines-renderer.vala
index 51d8444a..51d50f0e 100644
--- a/libgitg/gitg-diff-view-lines-renderer.vala
+++ b/libgitg/gitg-diff-view-lines-renderer.vala
@@ -23,7 +23,17 @@ class Gitg.DiffViewLinesRenderer : Gtk.SourceGutterRendererText
        {
                OLD,
                NEW,
-               SYMBOL
+               SYMBOL,
+               SYMBOL_OLD,
+               SYMBOL_NEW
+       }
+
+       private enum Line_Style
+       {
+               CONTEXT,
+               ADDED,
+               REMOVED,
+               EMPTY
        }
 
        private int d_num_digits;
@@ -104,7 +114,7 @@ class Gitg.DiffViewLinesRenderer : Gtk.SourceGutterRendererText
 
                if (info == null || (line - info.start) >= info.line_infos.length)
                {
-                       if (is_hunk && style != Style.SYMBOL)
+                       if (is_hunk && style != Style.SYMBOL && style != Style.SYMBOL_OLD && style != 
Style.SYMBOL_NEW)
                        {
                                set_text("...", -1);
                        }
@@ -186,48 +196,87 @@ class Gitg.DiffViewLinesRenderer : Gtk.SourceGutterRendererText
                d_num_digits_fill = string.nfill(num_digits, ' ');
        }
 
-       private string[] precalculate_line_strings(Ggit.DiffHunk hunk, Gee.ArrayList<Ggit.DiffLine> lines)
+       private Line_Style get_origin(int buffer_line, Gtk.SourceBuffer buffer)
+       {
+               var origin = Line_Style.CONTEXT;
+
+               var mark = buffer.get_source_marks_at_line(buffer_line, null);
+               if (mark != null)
+               {
+                       mark.@foreach ((item) => {
+                               switch (item.get_category())
+                               {
+                               case "added":
+                                       origin = Line_Style.ADDED;
+                                       break;
+                               case "removed":
+                                       origin = Line_Style.REMOVED;
+                                       break;
+                               case "empty":
+                                       origin = Line_Style.EMPTY;
+                                       break;
+                               }
+                       });
+               }
+
+               return origin;
+       }
+
+       private string[] precalculate_line_strings(Ggit.DiffHunk hunk, Gtk.SourceBuffer buffer, int 
buffer_line_start)
        {
                var oldn = hunk.get_old_start();
                var newn = hunk.get_new_start();
 
-               var lns = lines;
+               Gtk.TextIter iter;
+               buffer.get_end_iter(out iter);
+               int buffer_line_end = iter.get_line();
 
-               var line_infos = new string[lns.size];
+               var line_infos = new string[buffer_line_end - buffer_line_start + 1];
 
-               for (var i = 0; i < lns.size; i++)
+               for (var i = 0; i <= (buffer_line_end - buffer_line_start); i++)
                {
-                       var line = lns[i];
-                       var origin = line.get_origin();
+                       var origin = get_origin(buffer_line_start + i, buffer);
 
                        string ltext = "";
 
                        switch (style)
                        {
                        case Style.NEW:
-                               if (origin == Ggit.DiffLineType.CONTEXT || origin == 
Ggit.DiffLineType.ADDITION)
+                               if (origin == Line_Style.CONTEXT || origin == Line_Style.ADDED)
                                {
                                        ltext = "%*d".printf(d_num_digits, newn);
                                        newn++;
                                }
                                break;
                        case Style.OLD:
-                               if (origin == Ggit.DiffLineType.CONTEXT || origin == 
Ggit.DiffLineType.DELETION)
+                               if (origin == Line_Style.CONTEXT || origin == Line_Style.REMOVED)
                                {
                                        ltext = "%*d".printf(d_num_digits, oldn);
                                        oldn++;
                                }
                                break;
                        case Style.SYMBOL:
-                               if (origin == Ggit.DiffLineType.ADDITION)
+                               if (origin == Line_Style.ADDED)
                                {
                                        ltext = "+";
                                }
-                               else if (origin == Ggit.DiffLineType.DELETION)
+                               else if (origin == Line_Style.REMOVED)
+                               {
+                                       ltext = "-";
+                               }
+                               break;
+                       case Style.SYMBOL_OLD:
+                               if (origin == Line_Style.REMOVED)
                                {
                                        ltext = "-";
                                }
                                break;
+                       case Style.SYMBOL_NEW:
+                               if (origin == Line_Style.ADDED)
+                               {
+                                       ltext = "+";
+                               }
+                               break;
                        }
 
                        line_infos[i] = ltext;
@@ -235,8 +284,7 @@ class Gitg.DiffViewLinesRenderer : Gtk.SourceGutterRendererText
 
                return line_infos;
        }
-
-       public void add_hunk(int buffer_line_start, int buffer_line_end, Ggit.DiffHunk hunk, 
Gee.ArrayList<Ggit.DiffLine> lines)
+       public void add_hunk(int buffer_line_start, int buffer_line_end, Ggit.DiffHunk hunk, Gtk.SourceBuffer 
buffer)
        {
                HunkInfo info = HunkInfo();
 
@@ -246,7 +294,7 @@ class Gitg.DiffViewLinesRenderer : Gtk.SourceGutterRendererText
                info.end = buffer_line_end;
                info.hunk_line = buffer_line_start - 1;
                info.hunk = hunk;
-               info.line_infos = precalculate_line_strings(hunk, lines);
+               info.line_infos = precalculate_line_strings(hunk, buffer, buffer_line_start);
 
                d_hunks_list.add(info);
 
diff --git a/libgitg/gitg-diff-view.vala b/libgitg/gitg-diff-view.vala
index f4076f5b..acd2b3c2 100644
--- a/libgitg/gitg-diff-view.vala
+++ b/libgitg/gitg-diff-view.vala
@@ -450,7 +450,7 @@ public class Gitg.DiffView : Gtk.Grid
                        if (current_file != null)
                        {
                                current_file.show();
-                               current_file.renderer.notify["has-selection"].connect(on_selection_changed);  
  
+                               current_file.renderer.notify["has-selection"].connect(on_selection_changed);
 
                                files.add(current_file);
 
@@ -529,6 +529,8 @@ public class Gitg.DiffView : Gtk.Grid
                                        {
                                                current_file = new Gitg.DiffViewFile.text(info, 
handle_selection);
                                                this.bind_property("highlight", current_file.renderer, 
"highlight", BindingFlags.SYNC_CREATE);
+                                               this.bind_property("highlight", current_file.renderer_left, 
"highlight", BindingFlags.SYNC_CREATE);
+                                               this.bind_property("highlight", current_file.renderer_left, 
"highlight", BindingFlags.SYNC_CREATE);
                                        }
 
                                        return 0;
@@ -623,6 +625,24 @@ public class Gitg.DiffView : Gtk.Grid
                                this.bind_property("tab-width", renderer_text, "tab-width", 
BindingFlags.DEFAULT | BindingFlags.SYNC_CREATE);
                        }
 
+                       var renderer_left = file.renderer_left as DiffViewFileRendererText;
+                       if (renderer_left != null)
+                       {
+                               renderer_left.maxlines = maxlines;
+
+                               this.bind_property("wrap-lines", renderer_left, "wrap-lines", 
BindingFlags.DEFAULT | BindingFlags.SYNC_CREATE);
+                               this.bind_property("tab-width", renderer_left, "tab-width", 
BindingFlags.DEFAULT | BindingFlags.SYNC_CREATE);
+                       }
+
+                       var renderer_right = file.renderer_right as DiffViewFileRendererText;
+                       if (renderer_right != null)
+                       {
+                               renderer_right.maxlines = maxlines;
+
+                               this.bind_property("wrap-lines", renderer_right, "wrap-lines", 
BindingFlags.DEFAULT | BindingFlags.SYNC_CREATE);
+                               this.bind_property("tab-width", renderer_right, "tab-width", 
BindingFlags.DEFAULT | BindingFlags.SYNC_CREATE);
+                       }
+
                        if (i == files.size - 1)
                        {
                                file.vexpand = true;
diff --git a/libgitg/resources/ui/gitg-diff-view-file.ui b/libgitg/resources/ui/gitg-diff-view-file.ui
index a55a7c5f..e37a35cd 100644
--- a/libgitg/resources/ui/gitg-diff-view-file.ui
+++ b/libgitg/resources/ui/gitg-diff-view-file.ui
@@ -11,44 +11,86 @@
       <class name="gitg-file-header"/>
     </style>
     <child>
-      <object class="GtkExpander" id="expander">
+      <object class="GtkBox" id="header_grid">
         <property name="visible">True</property>
-        <property name="can_focus">True</property>
-        <property name="hexpand">True</property>
-        <property name="margin_start">3</property>
-        <child type="label">
-          <object class="GtkGrid" id="grid_file_header">
+        <property name="can_focus">False</property>
+        <property name="homogeneous">False</property>
+        <property name="orientation">horizontal</property>
+        <child>
+          <object class="GtkExpander" id="expander">
             <property name="visible">True</property>
-            <property name="can_focus">False</property>
+            <property name="can_focus">True</property>
             <property name="hexpand">True</property>
-            <property name="row_spacing">6</property>
-            <property name="column_spacing">12</property>
-            <property name="orientation">horizontal</property>
-            <child>
-              <object class="GitgDiffStat" id="diff_stat_file">
+            <property name="margin_start">3</property>
+            <child type="label">
+              <object class="GtkGrid" id="grid_file_header">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
-                <property name="vexpand">False</property>
-                <property name="valign">baseline</property>
-                <property name="margin_top">6</property>
-                <property name="margin_bottom">6</property>
-                <style>
-                  <class name="no-frame"/>
-                </style>
+                <property name="hexpand">True</property>
+                <property name="row_spacing">6</property>
+                <property name="column_spacing">12</property>
+                <property name="orientation">horizontal</property>
+                <child>
+                  <object class="GitgDiffStat" id="diff_stat_file">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="vexpand">False</property>
+                    <property name="valign">baseline</property>
+                    <property name="margin_top">6</property>
+                    <property name="margin_bottom">6</property>
+                    <style>
+                      <class name="no-frame"/>
+                    </style>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="label_file_header">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="hexpand">True</property>
+                    <property name="label">the/file/header</property>
+                    <property name="halign">start</property>
+                    <property name="margin_top">6</property>
+                    <property name="margin_bottom">6</property>
+                  </object>
+                </child>
               </object>
             </child>
+          </object>
+          <packing>
+            <property name="pack-type">start</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkToggleButton" id="d_split_button">
+            <property name="visible">False</property>
+            <property name="valign">center</property>
+            <property name="can_focus">False</property>
+            <property name="tooltip-text" translatable="yes">Split view</property>
+            <property name="margin_end">6</property>
+            <signal name="toggled" handler="split_button_toggled" swapped="no"/>
+            <style>
+              <class name="text-button"/>
+            </style>
             <child>
-              <object class="GtkLabel" id="label_file_header">
+              <object class="GtkLabel" id="label_split_view">
                 <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="hexpand">True</property>
-                <property name="label">the/file/header</property>
-                <property name="halign">start</property>
-                <property name="margin_top">6</property>
-                <property name="margin_bottom">6</property>
+                <property name="can_focus">True</property>
+                <property name="halign">center</property>
+                <property name="valign">baseline</property>
+                <property name="label" translatable="yes">Split View</property>
+                <property name="selectable">True</property>
+                <property name="ellipsize">end</property>
+                <property name="max-width-chars">12</property>
+                <style>
+                  <class name="dim-label"/>
+                </style>
               </object>
             </child>
           </object>
+          <packing>
+            <property name="pack-type">end</property>
+          </packing>
         </child>
       </object>
     </child>
@@ -57,6 +99,16 @@
         <property name="visible">True</property>
         <property name="can_focus">False</property>
         <property name="transition_type">slide-down</property>
+        <child>
+          <object class="GtkBox" id="box_file_renderer">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="hexpand">True</property>
+            <property name="homogeneous">True</property>
+            <property name="orientation">horizontal</property>
+           <property name="margin_bottom">6</property>
+          </object>
+        </child>
       </object>
     </child>
   </template>


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