[gitg] Sepearate out diff renderer types



commit f291802654629729d3ec5905c174055ff24df091
Author: Jesse van den Kieboom <jessevdk gnome org>
Date:   Mon Jan 4 15:04:31 2016 +0100

    Sepearate out diff renderer types

 libgitg/Makefile.am                                |    3 +
 libgitg/gitg-diff-selectable.vala                  |   28 +
 libgitg/gitg-diff-view-file-renderer-text.vala     |  674 ++++++++++++++++++++
 libgitg/gitg-diff-view-file-renderer.vala          |   25 +
 libgitg/gitg-diff-view-file.vala                   |  663 ++------------------
 libgitg/gitg-diff-view.vala                        |   30 +-
 libgitg/resources/resources.xml                    |    1 +
 .../ui/gitg-diff-view-file-renderer-text.ui        |   23 +
 libgitg/resources/ui/gitg-diff-view-file.ui        |   22 +-
 libgitg/resources/ui/libgitg-style.css             |    4 +-
 10 files changed, 821 insertions(+), 652 deletions(-)
---
diff --git a/libgitg/Makefile.am b/libgitg/Makefile.am
index fa1d475..7456787 100644
--- a/libgitg/Makefile.am
+++ b/libgitg/Makefile.am
@@ -66,7 +66,10 @@ libgitg_libgitg_1_0_la_VALASOURCES =                 \
        libgitg/gitg-diff-view.vala                     \
        libgitg/gitg-diff-view-file.vala                \
        libgitg/gitg-diff-view-file-selectable.vala     \
+       libgitg/gitg-diff-view-file-renderer.vala       \
+       libgitg/gitg-diff-view-file-renderer-text.vala  \
        libgitg/gitg-diff-view-lines-renderer.vala      \
+       libgitg/gitg-diff-selectable.vala               \
        libgitg/gitg-diff-view-commit-details.vala      \
        libgitg/gitg-diff-view-options.vala             \
        libgitg/gitg-hook.vala                          \
diff --git a/libgitg/gitg-diff-selectable.vala b/libgitg/gitg-diff-selectable.vala
new file mode 100644
index 0000000..e0938c3
--- /dev/null
+++ b/libgitg/gitg-diff-selectable.vala
@@ -0,0 +1,28 @@
+/*
+ * This file is part of gitg
+ *
+ * Copyright (C) 2016 - 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/>.
+ */
+
+interface Gitg.DiffSelectable : Object
+{
+       public abstract bool has_selection { get; }
+       public abstract bool can_select { get; construct set; }
+
+       public abstract PatchSet selection { owned get; }
+}
+
+// ex:ts=4 noet
diff --git a/libgitg/gitg-diff-view-file-renderer-text.vala b/libgitg/gitg-diff-view-file-renderer-text.vala
new file mode 100644
index 0000000..5800104
--- /dev/null
+++ b/libgitg/gitg-diff-view-file-renderer-text.vala
@@ -0,0 +1,674 @@
+/*
+ * This file is part of gitg
+ *
+ * Copyright (C) 2016 - 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/>.
+ */
+
+[GtkTemplate (ui = "/org/gnome/gitg/ui/gitg-diff-view-file-renderer-text.ui")]
+class Gitg.DiffViewFileRendererText : Gtk.SourceView, DiffSelectable, DiffViewFileRenderer
+{
+       private enum RegionType
+       {
+               ADDED,
+               REMOVED,
+               CONTEXT
+       }
+
+       private struct Region
+       {
+               public RegionType type;
+               public int buffer_line_start;
+               public int source_line_start;
+               public int length;
+       }
+
+       public uint added { get; set; }
+       public uint removed { get; set; }
+
+       private int64 d_doffset;
+
+       private Gee.HashMap<int, PatchSet.Patch?> d_lines;
+
+       private DiffViewFileSelectable d_selectable;
+       private DiffViewLinesRenderer d_old_lines;
+       private DiffViewLinesRenderer d_new_lines;
+       private DiffViewLinesRenderer d_sym_lines;
+
+       private bool d_highlight;
+
+       private Cancellable? d_higlight_cancellable;
+       private Gtk.SourceBuffer? d_old_highlight_buffer;
+       private Gtk.SourceBuffer? d_new_highlight_buffer;
+       private bool d_old_highlight_ready;
+       private bool d_new_highlight_ready;
+
+       private Region[] d_regions;
+       private bool d_constructed;
+
+       public bool new_is_workdir { get; construct set; }
+
+       public bool wrap_lines
+       {
+               get { return this.wrap_mode != Gtk.WrapMode.NONE; }
+               set
+               {
+                       if (value)
+                       {
+                               this.wrap_mode = Gtk.WrapMode.WORD_CHAR;
+                       }
+                       else
+                       {
+                               this.wrap_mode = Gtk.WrapMode.NONE;
+                       }
+               }
+       }
+
+       public new int tab_width
+       { 
+               get { return (int)get_tab_width(); }
+               set { set_tab_width((uint)value); }
+       }
+
+       public int maxlines { get; set; }
+       public Ggit.DiffDelta? delta { get; construct set; }
+       public Repository repository { get; construct set; }
+
+       public bool highlight
+       {
+               get { return d_highlight; }
+
+               construct set
+               {
+                       d_highlight = value;
+                       update_highlight();
+               }
+
+               default = true;
+       }
+       
+       private bool d_has_selection;
+
+       public bool has_selection
+       {
+               get { return d_has_selection; }
+       }
+       
+       public bool can_select { get; construct set; }
+
+       public PatchSet selection
+       {
+               owned get
+               {
+                       var ret = new PatchSet();
+
+                       ret.filename = delta.get_new_file().get_path();
+
+                       var patches = new PatchSet.Patch[0];
+
+                       if (!can_select)
+                       {
+                               return ret;
+                       }
+
+                       var selected = d_selectable.selected_lines;
+
+                       for (var i = 0; i < selected.length; i++)
+                       {
+                               var line = selected[i];
+                               var pset = d_lines[line];
+
+                               if (i == 0)
+                               {
+                                       patches += pset;
+                                       continue;
+                               }
+                               
+                               var last = patches[patches.length - 1];
+
+                               if (last.new_offset + last.length == pset.new_offset &&
+                                   last.type == pset.type)
+                               {
+                                       last.length += pset.length;
+                                       patches[patches.length - 1] = last;
+                               }
+                               else
+                               {
+                                       patches += pset;
+                               }
+                       }
+
+                       ret.patches = patches;
+                       return ret;
+               }
+       }
+
+       public DiffViewFileRendererText(Repository repository, Ggit.DiffDelta delta, bool new_is_workdir, 
bool can_select)
+       {
+               Object(repository: repository, new_is_workdir: new_is_workdir, delta: delta, can_select: 
can_select);
+       }
+
+       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);
+
+               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;
+
+               gutter.insert(d_old_lines, 0);
+               gutter.insert(d_new_lines, 1);
+               gutter.insert(d_sym_lines, 2);
+
+               this.set_border_window_size(Gtk.TextWindowType.TOP, 1);
+
+               var settings = Gtk.Settings.get_default();
+               settings.notify["gtk-application-prefer-dark-theme"].connect(update_theme);
+
+               update_theme();
+
+               if (can_select)
+               {
+                       d_selectable = new DiffViewFileSelectable(this);
+
+                       d_selectable.notify["has-selection"].connect(() => {
+                               d_has_selection = d_selectable.has_selection;
+                               notify_property("has-selection");
+                       });
+               }
+
+               d_lines = new Gee.HashMap<int, PatchSet.Patch?>();
+       }
+
+       protected override void dispose()
+       {
+               base.dispose();
+
+               if (d_higlight_cancellable != null)
+               {
+                       d_higlight_cancellable.cancel();
+                       d_higlight_cancellable = null;
+               }
+       }
+
+       private void update_highlight()
+       {
+               if (!d_constructed)
+               {
+                       return;
+               }
+
+               if (d_higlight_cancellable != null)
+               {
+                       d_higlight_cancellable.cancel();
+                       d_higlight_cancellable = null;
+               }
+
+               d_old_highlight_buffer = null;
+               d_new_highlight_buffer = null;
+
+               d_old_highlight_ready = false;
+               d_new_highlight_ready = false;
+
+               if (highlight && repository != null && delta != null)
+               {
+                       var cancellable = new Cancellable();
+                       d_higlight_cancellable = cancellable;
+
+                       init_highlighting_buffer.begin(delta.get_old_file(), false, cancellable, (obj, res) 
=> {
+                               var buffer = init_highlighting_buffer.end(res);
+
+                               if (!cancellable.is_cancelled())
+                               {
+                                       d_old_highlight_buffer = buffer;
+                                       d_old_highlight_ready = true;
+
+                                       update_highlighting_ready();
+                               }
+                       });
+
+                       init_highlighting_buffer.begin(delta.get_new_file(), new_is_workdir, cancellable, 
(obj, res) => {
+                               var buffer = init_highlighting_buffer.end(res);
+
+                               if (!cancellable.is_cancelled())
+                               {
+                                       d_new_highlight_buffer = buffer;
+                                       d_new_highlight_ready = true;
+
+                                       update_highlighting_ready();
+                               }
+                       });
+               }
+               else
+               {
+                       update_highlighting_ready();
+               }
+       }
+
+       private async Gtk.SourceBuffer? init_highlighting_buffer(Ggit.DiffFile file, bool from_workdir, 
Cancellable cancellable)
+       {
+               var id = file.get_oid();
+               var path = file.get_path();
+
+               if ((id.is_zero() && !from_workdir) || (path == null && from_workdir))
+               {
+                       return null;
+               }
+
+               var sfile = new Gtk.SourceFile();
+               sfile.location = repository.get_workdir().get_child(path);
+
+               var basename = sfile.location.get_basename();
+               uint8[] content;
+
+               if (!from_workdir)
+               {
+                       Ggit.Blob blob;
+
+                       try
+                       {
+                               blob = repository.lookup<Ggit.Blob>(id);
+                       }
+                       catch
+                       {
+                               return null;
+                       }
+
+                       content = blob.get_raw_content();
+               }
+               else
+               {
+                       // Try to read from disk
+                       try
+                       {
+                               string etag;
+
+                               // Read it all into a buffer so we can guess the content type from
+                               // it. This isn't really nice, but it's simple.
+                               yield sfile.location.load_contents_async(cancellable, out content, out etag);
+                       }
+                       catch
+                       {
+                               return null;
+                       }
+               }
+
+               bool uncertain;
+               var content_type = GLib.ContentType.guess(basename, content, out uncertain);
+
+               var bytes = new Bytes(content);
+               var stream = new GLib.MemoryInputStream.from_bytes(bytes);
+
+               var manager = Gtk.SourceLanguageManager.get_default();
+               var language = manager.guess_language(basename, content_type);
+
+               if (language == null)
+               {
+                       return null;
+               }
+
+               var buffer = new Gtk.SourceBuffer(this.buffer.tag_table);
+
+               var style_scheme_manager = Gtk.SourceStyleSchemeManager.get_default();
+
+               buffer.language = language;
+               buffer.highlight_syntax = true;
+               buffer.style_scheme = style_scheme_manager.get_scheme("classic");
+
+               var loader = new Gtk.SourceFileLoader.from_stream(buffer, sfile, stream);
+
+               try
+               {
+                       yield loader.load_async(GLib.Priority.LOW, cancellable, null);
+               }
+               catch (Error e)
+               {
+                       if (!cancellable.is_cancelled())
+                       {
+                               stderr.printf(@"ERROR: failed to load $(file.get_path()) for highlighting: 
$(e.message)\n");
+                       }
+
+                       return null;
+               }
+
+               return buffer;
+       }
+
+       private void update_highlighting_ready()
+       {
+               if (!d_old_highlight_ready && !d_new_highlight_ready)
+               {
+                       // Remove highlights
+                       return;
+               }
+               else if (!d_old_highlight_ready || !d_new_highlight_ready)
+               {
+                       // Both need to be loaded
+                       return;
+               }
+
+               var buffer = this.buffer;
+
+               // Go over all the source chunks and match up to old/new buffer. Then,
+               // apply the tags that are applied to the highlighted source buffers.
+               foreach (var region in d_regions)
+               {
+                       Gtk.SourceBuffer? source;
+
+                       if (region.type == RegionType.REMOVED)
+                       {
+                               source = d_old_highlight_buffer;
+                       }
+                       else
+                       {
+                               source = d_new_highlight_buffer;
+                       }
+
+                       if (source == null)
+                       {
+                               continue;
+                       }
+
+                       Gtk.TextIter buffer_iter, source_iter;
+
+                       buffer.get_iter_at_line(out buffer_iter, region.buffer_line_start);
+                       source.get_iter_at_line(out source_iter, region.source_line_start);
+
+                       var source_end_iter = source_iter;
+                       source_end_iter.forward_lines(region.length);
+
+                       source.ensure_highlight(source_iter, source_end_iter);
+
+                       var buffer_end_iter = buffer_iter;
+                       buffer_end_iter.forward_lines(region.length);
+
+                       var source_next_iter = source_iter;
+                       var tags = source_iter.get_tags();
+
+                       while (source_next_iter.forward_to_tag_toggle(null) && 
source_next_iter.compare(source_end_iter) < 0)
+                       {
+                               var buffer_next_iter = buffer_iter;
+                               buffer_next_iter.forward_chars(source_next_iter.get_offset() - 
source_iter.get_offset());
+
+                               foreach (var tag in tags)
+                               {
+                                       buffer.apply_tag(tag, buffer_iter, buffer_next_iter);
+                               }
+
+                               source_iter = source_next_iter;
+                               buffer_iter = buffer_next_iter;
+
+                               tags = source_iter.get_tags();
+                       }
+
+                       foreach (var tag in tags)
+                       {
+                               buffer.apply_tag(tag, buffer_iter, buffer_end_iter);
+                       }
+               }
+       }
+
+       protected override bool draw(Cairo.Context cr)
+       {
+               base.draw(cr);
+
+               var win = this.get_window(Gtk.TextWindowType.LEFT);
+
+               if (!Gtk.cairo_should_draw_window(cr, win))
+               {
+                       return false;
+               }
+
+               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 sym_lines_width = d_sym_lines.size + d_sym_lines.xpad * 2;
+
+               ctx.save();
+               ctx.add_class("diff-lines-separator");
+               ctx.render_frame(cr, 0, 0, old_lines_width, win.get_height());
+               ctx.restore();
+
+               ctx.save();
+               ctx.add_class("diff-lines-gutter-border");
+               ctx.render_frame(cr, old_lines_width + new_lines_width, 0, sym_lines_width, win.get_height());
+               ctx.restore();
+               
+               return false;
+       }
+
+       private void update_theme()
+       {
+               var header_attributes = new Gtk.SourceMarkAttributes();
+               var added_attributes = new Gtk.SourceMarkAttributes();
+               var removed_attributes = new Gtk.SourceMarkAttributes();
+
+               var settings = Gtk.Settings.get_default();
+               var theme = Environment.get_variable("GTK_THEME");
+
+               var dark = settings.gtk_application_prefer_dark_theme || (theme != null && 
theme.has_suffix(":dark"));
+
+               if (dark)
+               {
+                       header_attributes.background = Gdk.RGBA() { red = 136.0 / 255.0, green = 138.0 / 
255.0, blue = 133.0 / 255.0, alpha = 1.0 };
+                       added_attributes.background = Gdk.RGBA() { red = 78.0 / 255.0, green = 154.0 / 255.0, 
blue = 6.0 / 255.0, alpha = 1.0 };
+                       removed_attributes.background = Gdk.RGBA() { red = 164.0 / 255.0, green = 0.0, blue = 
0.0, alpha = 1.0 };
+               }
+               else
+               {
+                       header_attributes.background = Gdk.RGBA() { red = 244.0 / 255.0, green = 247.0 / 
255.0, blue = 251.0 / 255.0, alpha = 1.0 };
+                       added_attributes.background = Gdk.RGBA() { red = 220.0 / 255.0, green = 1.0, blue = 
220.0 / 255.0, alpha = 1.0 };
+                       removed_attributes.background = Gdk.RGBA() { red = 1.0, green = 220.0 / 255.0, blue = 
220.0 / 255.0, alpha = 1.0 };
+               }
+
+               this.set_mark_attributes("header", header_attributes, 0);
+               this.set_mark_attributes("added", added_attributes, 0);
+               this.set_mark_attributes("removed", removed_attributes, 0);
+       }
+
+       protected override void constructed()
+       {
+               base.constructed();
+
+               d_constructed = true;
+               update_highlight();
+       }
+
+       public void add_hunk(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();
+
+               /* Diff Content */
+               var content = new StringBuilder();
+
+               var region = Region() {
+                       type = RegionType.CONTEXT,
+                       buffer_line_start = 0,
+                       source_line_start = 0,
+                       length = 0
+               };
+
+               this.freeze_notify();
+
+               for (var i = 0; i < lines.size; i++)
+               {
+                       var line = lines[i];
+                       var text = line.get_text();
+                       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
+                               };
+                       }
+
+                       region.length++;
+
+                       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);
+                       }
+
+                       content.append(text);
+                       buffer_line++;
+               }
+
+               if (lines.size != 0)
+               {
+                       d_regions += region;
+               }
+
+               int line_hunk_start = iter.get_line();
+
+               buffer.insert(ref iter, (string)content.data, -1);
+
+               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);
+
+               for (var i = 0; i < lines.size; i++)
+               {
+                       var line = lines[i];
+                       string? category = null;
+
+                       switch (line.get_origin())
+                       {
+                               case Ggit.DiffLineType.ADDITION:
+                                       category = "added";
+                                       break;
+                               case Ggit.DiffLineType.DELETION:
+                                       category = "removed";
+                                       break;
+                       }
+
+                       if (category != null)
+                       {
+                               buffer.get_iter_at_line(out iter, line_hunk_start + i);
+                               buffer.create_source_mark(null, category, iter);
+                       }
+               }
+
+               this.thaw_notify();
+
+               sensitive = true;
+       }
+}
+
+// ex:ts=4 noet
diff --git a/libgitg/gitg-diff-view-file-renderer.vala b/libgitg/gitg-diff-view-file-renderer.vala
new file mode 100644
index 0000000..1b03671
--- /dev/null
+++ b/libgitg/gitg-diff-view-file-renderer.vala
@@ -0,0 +1,25 @@
+/*
+ * This file is part of gitg
+ *
+ * Copyright (C) 2016 - 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/>.
+ */
+
+interface Gitg.DiffViewFileRenderer : Gtk.Widget
+{
+       public abstract void add_hunk(Ggit.DiffHunk hunk, Gee.ArrayList<Ggit.DiffLine> lines);
+}
+
+// ex:ts=4 noet
diff --git a/libgitg/gitg-diff-view-file.vala b/libgitg/gitg-diff-view-file.vala
index 5805cb7..1ed0986 100644
--- a/libgitg/gitg-diff-view-file.vala
+++ b/libgitg/gitg-diff-view-file.vala
@@ -20,21 +20,6 @@
 [GtkTemplate (ui = "/org/gnome/gitg/ui/gitg-diff-view-file.ui")]
 class Gitg.DiffViewFile : Gtk.Grid
 {
-       private enum RegionType
-       {
-               ADDED,
-               REMOVED,
-               CONTEXT
-       }
-
-       private struct Region
-       {
-               public RegionType type;
-               public int buffer_line_start;
-               public int source_line_start;
-               public int length;
-       }
-
        [GtkChild( name = "expander" )]
        private Gtk.Expander d_expander;
 
@@ -44,485 +29,100 @@ class Gitg.DiffViewFile : Gtk.Grid
        [GtkChild( name = "diff_stat_file" )]
        private DiffStat d_diff_stat_file;
 
-       [GtkChild( name = "revealer_hunks" )]
-       private Gtk.Revealer d_revealer_hunks;
-
-       [GtkChild( name = "sourceview_hunks" )]
-       private Gtk.SourceView d_sourceview_hunks;
+       [GtkChild( name = "revealer_content" )]
+       private Gtk.Revealer d_revealer_content;
 
-       private uint d_added;
-       private uint d_removed;
        private bool d_expanded;
-       private int64 d_doffset;
-       private Gee.HashMap<int, PatchSet.Patch?> d_lines;
-       private DiffViewFileSelectable d_selectable;
-       private DiffViewLinesRenderer d_old_lines;
-       private DiffViewLinesRenderer d_new_lines;
-       private DiffViewLinesRenderer d_sym_lines;
-       private bool d_highlight;
-       private Cancellable? d_higlight_cancellable;
-       private Gtk.SourceBuffer? d_old_highlight_buffer;
-       private Gtk.SourceBuffer? d_new_highlight_buffer;
-       private bool d_old_highlight_ready;
-       private bool d_new_highlight_ready;
-       private Region[] d_regions;
-       private bool d_constructed;
 
-       public bool new_is_workdir { get; construct set; }
+       private Binding? d_vexpand_binding;
 
-       public bool expanded
+       public DiffViewFileRenderer? renderer
        {
-               get
+               owned get
                {
-                       return d_expanded;
+                       return d_revealer_content.get_child() as DiffViewFileRenderer;
                }
 
-               set
+               construct set
                {
-                       if (d_expanded != value)
-                       {
-                               d_expanded = value;
-                               d_revealer_hunks.reveal_child = d_expanded;
-
-                               var ctx = get_style_context();
+                       var current = this.renderer;
 
-                               if (d_expanded)
+                       if (current != value)
+                       {
+                               if (d_vexpand_binding != null)
                                {
-                                       ctx.add_class("expanded");
+                                       d_vexpand_binding.unbind();
+                                       d_vexpand_binding = null;
                                }
-                               else
+
+                               if (current != null)
                                {
-                                       ctx.remove_class("expanded");
+                                       d_revealer_content.remove(current);
                                }
-                       }
-               }
-       }
 
-       public bool wrap_lines
-       {
-               get { return d_sourceview_hunks.wrap_mode != Gtk.WrapMode.NONE; }
-               set
-               {
-                       if (value)
-                       {
-                               d_sourceview_hunks.wrap_mode = Gtk.WrapMode.WORD_CHAR;
-                       }
-                       else
-                       {
-                               d_sourceview_hunks.wrap_mode = Gtk.WrapMode.NONE;
+                               d_revealer_content.add(value);
+                               d_vexpand_binding = this.bind_property("vexpand", value, "vexpand", 
BindingFlags.SYNC_CREATE);
                        }
                }
        }
 
-       public int tab_width
-       {
-               get { return (int)d_sourceview_hunks.tab_width; }
-               set
-               {
-                       if (value > 0)
-                       {
-                               d_sourceview_hunks.tab_width = (uint)value;
-                       }
-               }
-       }
-
-       public int maxlines { get; set; }
-       public bool has_selection { get; private set; }
-       public bool handle_selection { get; construct set; }
-       public Ggit.DiffDelta? delta { get; construct set; }
-       public Repository repository { get; construct set; }
+       public bool new_is_workdir { get; construct set; }
 
-       public bool highlight
+       public bool expanded
        {
-               get { return d_highlight; }
-
-               construct set
+               get
                {
-                       d_highlight = value;
-                       update_highlight();
+                       return d_expanded;
                }
 
-               default = true;
-       }
-       
-
-       public DiffViewFile(Repository repository, Ggit.DiffDelta delta, bool new_is_workdir, bool 
handle_selection)
-       {
-               Object(repository: repository, new_is_workdir: new_is_workdir, delta: delta, 
handle_selection: handle_selection);
-       }
-
-       public PatchSet selection
-       {
-               owned get
+               set
                {
-                       var ret = new PatchSet();
-
-                       ret.filename = delta.get_new_file().get_path();
-
-                       var patches = new PatchSet.Patch[0];
-
-                       if (!handle_selection)
-                       {
-                               return ret;
-                       }
-
-                       var selected = d_selectable.selected_lines;
-
-                       for (var i = 0; i < selected.length; i++)
+                       if (d_expanded != value)
                        {
-                               var line = selected[i];
-                               var pset = d_lines[line];
+                               d_expanded = value;
+                               d_revealer_content.reveal_child = d_expanded;
 
-                               if (i == 0)
-                               {
-                                       patches += pset;
-                                       continue;
-                               }
-                               
-                               var last = patches[patches.length - 1];
+                               var ctx = get_style_context();
 
-                               if (last.new_offset + last.length == pset.new_offset &&
-                                   last.type == pset.type)
+                               if (d_expanded)
                                {
-                                       last.length += pset.length;
-                                       patches[patches.length - 1] = last;
+                                       ctx.add_class("expanded");
                                }
                                else
                                {
-                                       patches += pset;
+                                       ctx.remove_class("expanded");
                                }
                        }
-
-                       ret.patches = patches;
-                       return ret;
                }
        }
 
-       construct
-       {
-               var gutter = d_sourceview_hunks.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);
-
-               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;
-
-               gutter.insert(d_old_lines, 0);
-               gutter.insert(d_new_lines, 1);
-               gutter.insert(d_sym_lines, 2);
-
-               d_sourceview_hunks.set_border_window_size(Gtk.TextWindowType.TOP, 1);
-
-               var settings = Gtk.Settings.get_default();
-               settings.notify["gtk-application-prefer-dark-theme"].connect(update_theme);
-
-               update_theme();
-
-               if (handle_selection)
-               {
-                       d_selectable = new DiffViewFileSelectable(d_sourceview_hunks);
-
-                       d_selectable.notify["has-selection"].connect(() => {
-                               this.has_selection = d_selectable.has_selection;
-                       });
-               }
-
-               d_lines = new Gee.HashMap<int, PatchSet.Patch?>();
-
-               this.bind_property("vexpand", d_sourceview_hunks, "vexpand", BindingFlags.SYNC_CREATE);
-
-               d_sourceview_hunks.draw.connect_after(sourceview_hunks_on_draw);
-       }
-
-       protected override void dispose()
-       {
-               base.dispose();
-
-               if (d_higlight_cancellable != null)
-               {
-                       d_higlight_cancellable.cancel();
-                       d_higlight_cancellable = null;
-               }
-       }
-
-       private void update_highlight()
-       {
-               if (!d_constructed)
-               {
-                       return;
-               }
-
-               if (d_higlight_cancellable != null)
-               {
-                       d_higlight_cancellable.cancel();
-                       d_higlight_cancellable = null;
-               }
-
-               d_old_highlight_buffer = null;
-               d_new_highlight_buffer = null;
-
-               d_old_highlight_ready = false;
-               d_new_highlight_ready = false;
-
-               if (highlight && repository != null && delta != null)
-               {
-                       var cancellable = new Cancellable();
-                       d_higlight_cancellable = cancellable;
-
-                       init_highlighting_buffer.begin(delta.get_old_file(), false, cancellable, (obj, res) 
=> {
-                               var buffer = init_highlighting_buffer.end(res);
-
-                               if (!cancellable.is_cancelled())
-                               {
-                                       d_old_highlight_buffer = buffer;
-                                       d_old_highlight_ready = true;
-
-                                       update_highlighting_ready();
-                               }
-                       });
-
-                       init_highlighting_buffer.begin(delta.get_new_file(), new_is_workdir, cancellable, 
(obj, res) => {
-                               var buffer = init_highlighting_buffer.end(res);
-
-                               if (!cancellable.is_cancelled())
-                               {
-                                       d_new_highlight_buffer = buffer;
-                                       d_new_highlight_ready = true;
-
-                                       update_highlighting_ready();
-                               }
-                       });
-               }
-               else
-               {
-                       update_highlighting_ready();
-               }
-       }
+       public Ggit.DiffDelta? delta { get; construct set; }
+       public Repository repository { get; construct set; }
 
-       private async Gtk.SourceBuffer? init_highlighting_buffer(Ggit.DiffFile file, bool from_workdir, 
Cancellable cancellable)
+       public DiffViewFile(Repository repository, Ggit.DiffDelta delta)
        {
-               var id = file.get_oid();
-               var path = file.get_path();
-
-               if ((id.is_zero() && !from_workdir) || (path == null && from_workdir))
-               {
-                       return null;
-               }
-
-               var sfile = new Gtk.SourceFile();
-               sfile.location = repository.get_workdir().get_child(path);
-
-               var basename = sfile.location.get_basename();
-               uint8[] content;
-
-               if (!from_workdir)
-               {
-                       Ggit.Blob blob;
-
-                       try
-                       {
-                               blob = repository.lookup<Ggit.Blob>(id);
-                       }
-                       catch
-                       {
-                               return null;
-                       }
-
-                       content = blob.get_raw_content();
-               }
-               else
-               {
-                       // Try to read from disk
-                       try
-                       {
-                               string etag;
-
-                               // Read it all into a buffer so we can guess the content type from
-                               // it. This isn't really nice, but it's simple.
-                               yield sfile.location.load_contents_async(cancellable, out content, out etag);
-                       }
-                       catch
-                       {
-                               return null;
-                       }
-               }
-
-               bool uncertain;
-               var content_type = GLib.ContentType.guess(basename, content, out uncertain);
-
-               var bytes = new Bytes(content);
-               var stream = new GLib.MemoryInputStream.from_bytes(bytes);
-
-               var manager = Gtk.SourceLanguageManager.get_default();
-               var language = manager.guess_language(basename, content_type);
-
-               if (language == null)
-               {
-                       return null;
-               }
-
-               var buffer = new Gtk.SourceBuffer(d_sourceview_hunks.buffer.tag_table);
-
-               var style_scheme_manager = Gtk.SourceStyleSchemeManager.get_default();
-
-               buffer.language = language;
-               buffer.highlight_syntax = true;
-               buffer.style_scheme = style_scheme_manager.get_scheme("classic");
-
-               var loader = new Gtk.SourceFileLoader.from_stream(buffer, sfile, stream);
-
-               try
-               {
-                       yield loader.load_async(GLib.Priority.LOW, cancellable, null);
-               }
-               catch (Error e)
-               {
-                       if (!cancellable.is_cancelled())
-                       {
-                               stderr.printf(@"ERROR: failed to load $(file.get_path()) for highlighting: 
$(e.message)\n");
-                       }
-
-                       return null;
-               }
-
-               return buffer;
+               Object(repository: repository, delta: delta);
        }
 
-       private void update_highlighting_ready()
+       public DiffViewFile.text(Repository repository, Ggit.DiffDelta delta, bool new_is_workdir, bool 
handle_selection)
        {
-               if (!d_old_highlight_ready && !d_new_highlight_ready)
-               {
-                       // Remove highlights
-                       return;
-               }
-               else if (!d_old_highlight_ready || !d_new_highlight_ready)
-               {
-                       // Both need to be loaded
-                       return;
-               }
-
-               var buffer = d_sourceview_hunks.buffer;
+               this(repository, delta);
 
-               // Go over all the source chunks and match up to old/new buffer. Then,
-               // apply the tags that are applied to the highlighted source buffers.
-               foreach (var region in d_regions)
-               {
-                       Gtk.SourceBuffer? source;
+               this.renderer = new DiffViewFileRendererText(repository, delta, new_is_workdir, 
handle_selection);
+               this.renderer.show();
 
-                       if (region.type == RegionType.REMOVED)
-                       {
-                               source = d_old_highlight_buffer;
-                       }
-                       else
-                       {
-                               source = d_new_highlight_buffer;
-                       }
-
-                       if (source == null)
-                       {
-                               continue;
-                       }
-
-                       Gtk.TextIter buffer_iter, source_iter;
-
-                       buffer.get_iter_at_line(out buffer_iter, region.buffer_line_start);
-                       source.get_iter_at_line(out source_iter, region.source_line_start);
-
-                       var source_end_iter = source_iter;
-                       source_end_iter.forward_lines(region.length);
-
-                       source.ensure_highlight(source_iter, source_end_iter);
-
-                       var buffer_end_iter = buffer_iter;
-                       buffer_end_iter.forward_lines(region.length);
-
-                       var source_next_iter = source_iter;
-                       var tags = source_iter.get_tags();
-
-                       while (source_next_iter.forward_to_tag_toggle(null) && 
source_next_iter.compare(source_end_iter) < 0)
-                       {
-                               var buffer_next_iter = buffer_iter;
-                               buffer_next_iter.forward_chars(source_next_iter.get_offset() - 
source_iter.get_offset());
-
-                               foreach (var tag in tags)
-                               {
-                                       buffer.apply_tag(tag, buffer_iter, buffer_next_iter);
-                               }
-
-                               source_iter = source_next_iter;
-                               buffer_iter = buffer_next_iter;
-
-                               tags = source_iter.get_tags();
-                       }
-
-                       foreach (var tag in tags)
-                       {
-                               buffer.apply_tag(tag, buffer_iter, buffer_end_iter);
-                       }
-               }
+               this.renderer.bind_property("added", d_diff_stat_file, "added");
+               this.renderer.bind_property("removed", d_diff_stat_file, "removed");
        }
 
-       private bool sourceview_hunks_on_draw(Cairo.Context cr)
+       public DiffViewFile.binary(Repository repository, Ggit.DiffDelta delta)
        {
-               var win = d_sourceview_hunks.get_window(Gtk.TextWindowType.LEFT);
-
-               if (!Gtk.cairo_should_draw_window(cr, win))
-               {
-                       return false;
-               }
-
-               var ctx = d_sourceview_hunks.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 sym_lines_width = d_sym_lines.size + d_sym_lines.xpad * 2;
-
-               ctx.save();
-               ctx.add_class("diff-lines-separator");
-               ctx.render_frame(cr, 0, 0, old_lines_width, win.get_height());
-               ctx.restore();
-
-               ctx.save();
-               ctx.add_class("diff-lines-gutter-border");
-               ctx.render_frame(cr, old_lines_width + new_lines_width, 0, sym_lines_width, win.get_height());
-               ctx.restore();
-               
-               return false;
+               this(repository, delta);
        }
 
-       private void update_theme()
+       public DiffViewFile.image(Repository repository, Ggit.DiffDelta delta)
        {
-               var header_attributes = new Gtk.SourceMarkAttributes();
-               var added_attributes = new Gtk.SourceMarkAttributes();
-               var removed_attributes = new Gtk.SourceMarkAttributes();
-
-               var settings = Gtk.Settings.get_default();
-               var theme = Environment.get_variable("GTK_THEME");
-
-               var dark = settings.gtk_application_prefer_dark_theme || (theme != null && 
theme.has_suffix(":dark"));
-
-               if (dark)
-               {
-                       header_attributes.background = Gdk.RGBA() { red = 136.0 / 255.0, green = 138.0 / 
255.0, blue = 133.0 / 255.0, alpha = 1.0 };
-                       added_attributes.background = Gdk.RGBA() { red = 78.0 / 255.0, green = 154.0 / 255.0, 
blue = 6.0 / 255.0, alpha = 1.0 };
-                       removed_attributes.background = Gdk.RGBA() { red = 164.0 / 255.0, green = 0.0, blue = 
0.0, alpha = 1.0 };
-               }
-               else
-               {
-                       header_attributes.background = Gdk.RGBA() { red = 244.0 / 255.0, green = 247.0 / 
255.0, blue = 251.0 / 255.0, alpha = 1.0 };
-                       added_attributes.background = Gdk.RGBA() { red = 220.0 / 255.0, green = 1.0, blue = 
220.0 / 255.0, alpha = 1.0 };
-                       removed_attributes.background = Gdk.RGBA() { red = 1.0, green = 220.0 / 255.0, blue = 
220.0 / 255.0, alpha = 1.0 };
-               }
-
-               d_sourceview_hunks.set_mark_attributes("header", header_attributes, 0);
-               d_sourceview_hunks.set_mark_attributes("added", added_attributes, 0);
-               d_sourceview_hunks.set_mark_attributes("removed", removed_attributes, 0);
+               this(repository, delta);
        }
 
        protected override void constructed()
@@ -549,184 +149,11 @@ class Gitg.DiffViewFile : Gtk.Grid
                }
 
                d_expander.bind_property("expanded", this, "expanded", BindingFlags.BIDIRECTIONAL);
-
-               d_constructed = true;
-               update_highlight();
        }
 
        public void add_hunk(Ggit.DiffHunk hunk, Gee.ArrayList<Ggit.DiffLine> lines)
        {
-               var buffer = d_sourceview_hunks.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();
-
-               /* Diff Content */
-               var content = new StringBuilder();
-
-               var region = Region() {
-                       type = RegionType.CONTEXT,
-                       buffer_line_start = 0,
-                       source_line_start = 0,
-                       length = 0
-               };
-
-               for (var i = 0; i < lines.size; i++)
-               {
-                       var line = lines[i];
-                       var text = line.get_text();
-                       var added = false;
-                       var removed = false;
-                       var origin = line.get_origin();
-
-                       var rtype = RegionType.CONTEXT;
-
-                       switch (origin)
-                       {
-                               case Ggit.DiffLineType.ADDITION:
-                                       added = true;
-                                       d_added++;
-
-                                       rtype = RegionType.ADDED;
-                                       break;
-                               case Ggit.DiffLineType.DELETION:
-                                       removed = true;
-                                       d_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
-                               };
-                       }
-
-                       region.length++;
-
-                       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);
-                       }
-
-                       content.append(text);
-                       buffer_line++;
-               }
-
-               if (lines.size != 0)
-               {
-                       d_regions += region;
-               }
-
-               int line_hunk_start = iter.get_line();
-
-               buffer.insert(ref iter, (string)content.data, -1);
-
-               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);
-
-               for (var i = 0; i < lines.size; i++)
-               {
-                       var line = lines[i];
-                       string? category = null;
-
-                       switch (line.get_origin())
-                       {
-                               case Ggit.DiffLineType.ADDITION:
-                                       category = "added";
-                                       break;
-                               case Ggit.DiffLineType.DELETION:
-                                       category = "removed";
-                                       break;
-                       }
-
-                       if (category != null)
-                       {
-                               buffer.get_iter_at_line(out iter, line_hunk_start + i);
-                               buffer.create_source_mark(null, category, iter);
-                       }
-               }
-
-               d_diff_stat_file.added = d_added;
-               d_diff_stat_file.removed = d_removed;
-
-               sensitive = true;
+               this.renderer.add_hunk(hunk, lines);
        }
 }
 
diff --git a/libgitg/gitg-diff-view.vala b/libgitg/gitg-diff-view.vala
index 0584e5f..ab66767 100644
--- a/libgitg/gitg-diff-view.vala
+++ b/libgitg/gitg-diff-view.vala
@@ -280,7 +280,9 @@ public class Gitg.DiffView : Gtk.Grid
 
                foreach (var file in d_grid_files.get_children())
                {
-                       if ((file as Gitg.DiffViewFile).has_selection)
+                       var selectable = (file as Gitg.DiffViewFile).renderer as DiffSelectable;
+
+                       if (selectable.has_selection)
                        {
                                something_selected = true;
                                break;
@@ -350,8 +352,8 @@ public class Gitg.DiffView : Gtk.Grid
 
                                        add_file();
 
-                                       current_file = new Gitg.DiffViewFile(repository, delta, 
new_is_workdir, handle_selection);
-                                       this.bind_property("highlight", current_file, "highlight", 
BindingFlags.SYNC_CREATE);
+                                       current_file = new Gitg.DiffViewFile.text(repository, delta, 
new_is_workdir, handle_selection);
+                                       this.bind_property("highlight", current_file.renderer, "highlight", 
BindingFlags.SYNC_CREATE);
 
                                        return 0;
                                },
@@ -431,19 +433,25 @@ public class Gitg.DiffView : Gtk.Grid
                        var path = primary_path(file);
 
                        file.expanded = d_commit_details.expanded || (path != null && 
was_expanded.contains(path));
-                       file.maxlines = maxlines;
 
-                       d_grid_files.add(file);
+                       var renderer_text = file.renderer as DiffViewFileRendererText;
 
-                       file.notify["expanded"].connect(auto_update_expanded);
+                       if (renderer_text != null)
+                       {
+                               renderer_text.maxlines = maxlines;
 
-                       this.bind_property("wrap-lines", file, "wrap-lines", BindingFlags.DEFAULT | 
BindingFlags.SYNC_CREATE);
-                       this.bind_property("tab-width", file, "tab-width", BindingFlags.DEFAULT | 
BindingFlags.SYNC_CREATE);
+                               this.bind_property("wrap-lines", renderer_text, "wrap-lines", 
BindingFlags.DEFAULT | BindingFlags.SYNC_CREATE);
+                               this.bind_property("tab-width", renderer_text, "tab-width", 
BindingFlags.DEFAULT | BindingFlags.SYNC_CREATE);
+                       }
 
                        if (i == files.size - 1)
                        {
                                file.vexpand = true;
                        }
+
+                       d_grid_files.add(file);
+
+                       file.notify["expanded"].connect(auto_update_expanded);
                }
        }
 
@@ -469,11 +477,11 @@ public class Gitg.DiffView : Gtk.Grid
 
                        foreach (var file in d_grid_files.get_children())
                        {
-                               var sel = (file as Gitg.DiffViewFile).selection;
+                               var sel = (file as Gitg.DiffViewFile).renderer as DiffSelectable;
 
-                               if (sel.patches.length != 0)
+                               if (sel != null && sel.has_selection && sel.selection.patches.length != 0)
                                {
-                                       ret += sel;
+                                       ret += sel.selection;
                                }
                        }
 
diff --git a/libgitg/resources/resources.xml b/libgitg/resources/resources.xml
index 60e63be..f197fce 100644
--- a/libgitg/resources/resources.xml
+++ b/libgitg/resources/resources.xml
@@ -5,6 +5,7 @@
     <file compressed="true" preprocess="xml-stripblanks">ui/gitg-authentication-dialog.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">ui/gitg-diff-view.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">ui/gitg-diff-view-file.ui</file>
+    <file compressed="true" preprocess="xml-stripblanks">ui/gitg-diff-view-file-renderer-text.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">ui/gitg-diff-view-options.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">ui/gitg-diff-view-options-spacing.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">ui/gitg-diff-view-commit-details.ui</file>
diff --git a/libgitg/resources/ui/gitg-diff-view-file-renderer-text.ui 
b/libgitg/resources/ui/gitg-diff-view-file-renderer-text.ui
new file mode 100644
index 0000000..26c13f9
--- /dev/null
+++ b/libgitg/resources/ui/gitg-diff-view-file-renderer-text.ui
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.19.0 -->
+<interface>
+  <requires lib="gtk+" version="3.16"/>
+  <template class="GitgDiffViewFileRendererText" parent="GtkSourceView">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="show_line_numbers">False</property>
+    <property name="editable">False</property>
+    <property name="cursor_visible">False</property>
+    <property name="monospace">True</property>
+    <property name="hexpand">True</property>
+    <property name="pixels_above_lines">1</property>
+    <property name="pixels_below_lines">1</property>
+    <property name="left_margin">6</property>
+    <property name="right_margin">6</property>
+    <property name="buffer">buffer</property>
+  </template>
+  <object class="GtkSourceBuffer" id="buffer">
+    <property name="highlight_matching_brackets">False</property>
+    <property name="implicit_trailing_newline">False</property>
+  </object>
+</interface>
diff --git a/libgitg/resources/ui/gitg-diff-view-file.ui b/libgitg/resources/ui/gitg-diff-view-file.ui
index 0e379eb..b3b6284 100644
--- a/libgitg/resources/ui/gitg-diff-view-file.ui
+++ b/libgitg/resources/ui/gitg-diff-view-file.ui
@@ -51,31 +51,11 @@
       </object>
     </child>
     <child>
-      <object class="GtkRevealer" id="revealer_hunks">
+      <object class="GtkRevealer" id="revealer_content">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
         <property name="transition_type">slide-down</property>
-        <child>
-          <object class="GtkSourceView" id="sourceview_hunks">
-            <property name="visible">True</property>
-            <property name="can_focus">False</property>
-            <property name="show_line_numbers">False</property>
-            <property name="editable">False</property>
-            <property name="cursor_visible">False</property>
-            <property name="monospace">True</property>
-            <property name="hexpand">True</property>
-            <property name="pixels_above_lines">1</property>
-            <property name="pixels_below_lines">1</property>
-            <property name="left_margin">6</property>
-            <property name="right_margin">6</property>
-            <property name="buffer">buffer</property>
-          </object>
-        </child>
       </object>
     </child>
   </template>
-  <object class="GtkSourceBuffer" id="buffer">
-    <property name="highlight_matching_brackets">False</property>
-    <property name="implicit_trailing_newline">False</property>
-  </object>
 </interface>
diff --git a/libgitg/resources/ui/libgitg-style.css b/libgitg/resources/ui/libgitg-style.css
index 37eb778..19628e6 100644
--- a/libgitg/resources/ui/libgitg-style.css
+++ b/libgitg/resources/ui/libgitg-style.css
@@ -20,11 +20,11 @@
        border-right: 2px solid shade(@theme_bg_color, 0.95);
 }
 
-GitgDiffViewFile GtkSourceView {
+GitgDiffViewFileRendererText {
        color: @insensitive_fg_color;
 }
 
-GitgDiffViewFile GtkSourceView.view {
+GitgDiffViewFileRendererText.view {
        color: @theme_text_color;
 }
 



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