[gitg/wip/patch-stage: 10/11] Make stage selection behavior part of diff view widget



commit e5091246142c6c006b161d553ddec0980b223c04
Author: Jesse van den Kieboom <jessevdk gmail com>
Date:   Tue Dec 31 15:17:20 2013 +0100

    Make stage selection behavior part of diff view widget

 libgitg/Makefile.am                         |    6 +-
 libgitg/gitg-diff-view.vala                 |  159 ++++++++++++++++++++++++++-
 libgitg/gitg-js-utils.c                     |   39 +++++++
 libgitg/gitg-js-utils.h                     |   10 ++
 libgitg/resources/diff-view-html-builder.js |   26 +----
 libgitg/resources/diff-view.css             |   34 ------
 libgitg/resources/diff-view.html            |    1 -
 libgitg/resources/diff-view.js              |  147 +++++++++++++++++++++++--
 vapi/gitg-js-utils.vapi                     |    6 +
 9 files changed, 358 insertions(+), 70 deletions(-)
---
diff --git a/libgitg/Makefile.am b/libgitg/Makefile.am
index 6036b72..8a767b7 100644
--- a/libgitg/Makefile.am
+++ b/libgitg/Makefile.am
@@ -25,6 +25,7 @@ AM_VALAFLAGS =                                \
        --pkg gee-0.8                   \
        --pkg json-glib-1.0             \
        --pkg gio-unix-2.0              \
+       --pkg gitg-js-utils             \
        $(GITG_VALAFLAGS)               \
        --vapidir $(top_srcdir)/vapi    \
        --header libgitg.h              \
@@ -60,10 +61,8 @@ VALA_FILES =                                 \
        gitg-diff-view-request-resource.vala    \
        gitg-diff-view-request-patch.vala       \
        gitg-diff-view-request-diff.vala        \
-       gitg-diff-view-request-stage.vala       \
        gitg-repository-list-box.vala           \
        gitg-when-mapped.vala                   \
-       gitg-patch-set.vala                     \
        gitg-progress-bin.vala                  \
        gitg-stage.vala                         \
        gitg-stage-status-enumerator.vala       \
@@ -79,10 +78,11 @@ libgitg_1_0_la_CFLAGS =             \
 
 libgitg_1_0_la_SOURCES =               \
        gitg-resources.c                \
+       gitg-js-utils.c                 \
        $(VALA_FILES)
 
 headerdir = $(prefix)/include/libgitg-1.0/libgitg
-header_HEADERS = libgitg.h
+header_HEADERS = libgitg.h gitg-js-utils.h
 
 vapidir = $(prefix)/share/vala/vapi
 vapi_DATA = libgitg-1.0.vapi
diff --git a/libgitg/gitg-diff-view.vala b/libgitg/gitg-diff-view.vala
index f08aace..d270083 100644
--- a/libgitg/gitg-diff-view.vala
+++ b/libgitg/gitg-diff-view.vala
@@ -21,9 +21,73 @@ namespace Gitg
 {
        public class DiffView : WebKit.WebView
        {
+               public class PatchSet
+               {
+                       public enum Type
+                       {
+                               ADD    = 'a',
+                               REMOVE = 'r'
+                       }
+
+                       public struct Patch
+                       {
+                               Type   type;
+                               size_t old_offset;
+                               size_t new_offset;
+                               size_t length;
+
+                               public string to_string()
+                               {
+                                       var tp = type == Type.ADD ? "add" : "remove";
+                                       return @"$tp[old_offset: $old_offset, new_offset: $new_offset, 
length: $length]";
+                               }
+                       }
+
+                       public string  filename;
+                       public Patch[] patches;
+
+                       public string to_string()
+                       {
+                               var ps = new string[patches.length];
+
+                               for (var i = 0; i < patches.length; i++)
+                               {
+                                       ps[i] = patches[i].to_string();
+                               }
+
+                               var p = string.joinv(", ", ps);
+                               return @"$filename: $p";
+                       }
+               }
+
+               private class DiffViewRequestInternal : DiffViewRequest
+               {
+                       public DiffViewRequestInternal(DiffView? view, WebKit.URISchemeRequest request, 
Soup.URI uri)
+                       {
+                               base(view, request, uri);
+                       }
+
+                       protected override InputStream? run_async(Cancellable? cancellable) throws Error
+                       {
+                               Idle.add(() => {
+                                       switch (parameter("action"))
+                                       {
+                                               case "selection-changed":
+                                                       d_view.update_has_selection(parameter("value") == 
"yes");
+                                                       break;
+                                       }
+
+                                       return false;
+                               });
+
+                               return null;
+                       }
+               }
+
                private Ggit.Diff? d_diff;
                private Commit? d_commit;
                private Settings d_fontsettings;
+               private bool d_has_selection;
 
                private static Gee.HashMap<string, DiffView> s_diff_map;
                private static uint64 s_diff_id;
@@ -32,6 +96,12 @@ namespace Gitg
                public File? custom_js { get; construct; }
                public Ggit.DiffOptions? options { get; construct set; }
 
+               [Notify]
+               public bool has_selection
+               {
+                       get { return d_has_selection; }
+               }
+
                private Cancellable d_cancellable;
                private bool d_loaded;
                private ulong d_diffid;
@@ -141,6 +211,8 @@ namespace Gitg
                                        return new DiffViewRequestDiff(view, request, uri);
                                case "patch":
                                        return new DiffViewRequestPatch(view, request, uri);
+                               case "internal":
+                                       return new DiffViewRequestInternal(view, request, uri);
                        }
 
                        return null;
@@ -150,6 +222,11 @@ namespace Gitg
                {
                        var req = parse_request(request);
 
+                       if (req == null)
+                       {
+                               return;
+                       }
+
                        if (req.view != null)
                        {
                                req.view.request(req);
@@ -262,6 +339,8 @@ namespace Gitg
 
                        d_cancellable = new Cancellable();
 
+                       d_loaded = false;
+
                        load_changed.connect((v, ev) => {
                                if (ev == WebKit.LoadEvent.FINISHED)
                                {
@@ -275,8 +354,6 @@ namespace Gitg
 
                        uri += "&settings=" + Soup.URI.encode(json_settings(), null);
 
-                       d_loaded = false;
-
                        load_uri(uri);
                }
 
@@ -327,6 +404,84 @@ namespace Gitg
                                });
                        }
                }
+
+               public void update_has_selection(bool hs)
+               {
+                       if (d_has_selection != hs)
+                       {
+                               d_has_selection = hs;
+                               notify_property("has-selection");
+                       }
+               }
+
+               private PatchSet parse_patchset(Json.Node node)
+               {
+                       PatchSet ret = new PatchSet();
+
+                       var elems = node.get_array();
+                       ret.filename = elems.get_element(0).get_string();
+
+                       var ps = elems.get_element(1).get_array();
+
+                       var l = ps.get_length();
+                       ret.patches = new PatchSet.Patch[l];
+
+                       for (uint i = 0; i < l; i++)
+                       {
+                               var p = ps.get_element(i).get_array();
+
+                               ret.patches[i] = PatchSet.Patch() {
+                                       type = (PatchSet.Type)p.get_element(0).get_int(),
+                                       old_offset = (size_t)p.get_element(1).get_int(),
+                                       new_offset = (size_t)p.get_element(2).get_int(),
+                                       length = (size_t)p.get_element(3).get_int()
+                               };
+                       }
+
+                       return ret;
+               }
+
+               public async PatchSet[] get_selection()
+               {
+                       WebKit.JavascriptResult jsret;
+
+                       try
+                       {
+                               jsret = yield run_javascript("get_selection();", d_cancellable);
+                       }
+                       catch (Error e)
+                       {
+                               stderr.printf("Error running get_selection(): %s\n", e.message);
+                               return new PatchSet[] {};
+                       }
+
+                       var json = GitgJsUtils.get_json(jsret);
+                       var parser = new Json.Parser();
+
+                       try
+                       {
+                               parser.load_from_data(json, -1);
+                       }
+                       catch (Error e)
+                       {
+                               stderr.printf("Error parsing json: %s\n", e.message);
+                               return new PatchSet[] {};
+                       }
+
+                       var root = parser.get_root();
+
+                       var elems = root.get_array();
+                       var l = elems.get_length();
+
+                       var ret = new PatchSet[l];
+
+                       for (uint i = 0; i < l; i++)
+                       {
+                               ret[i] = parse_patchset(elems.get_element(i));
+                       }
+
+                       return ret;
+               }
        }
 }
 
diff --git a/libgitg/gitg-js-utils.c b/libgitg/gitg-js-utils.c
new file mode 100644
index 0000000..8e6123a
--- /dev/null
+++ b/libgitg/gitg-js-utils.c
@@ -0,0 +1,39 @@
+#include "gitg-js-utils.h"
+
+#include <JavaScriptCore/JavaScript.h>
+
+gchar *
+gitg_js_utils_get_json (WebKitJavascriptResult *js_result)
+{
+       JSValueRef value;
+       JSStringRef json;
+       size_t size;
+       gchar *ret;
+
+       value = webkit_javascript_result_get_value (js_result);
+
+       json = JSValueCreateJSONString(webkit_javascript_result_get_global_context (js_result),
+                                      value,
+                                      0,
+                                      NULL);
+
+       size = JSStringGetMaximumUTF8CStringSize (json);
+       ret = g_new0 (gchar, size);
+
+       JSStringGetUTF8CString (json, ret, size);
+       JSStringRelease (json);
+
+       return ret;
+}
+
+gboolean
+gitg_js_utils_check (WebKitJavascriptResult *js_result)
+{
+       JSValueRef value;
+
+       value = webkit_javascript_result_get_value (js_result);
+
+       return JSValueToBoolean (webkit_javascript_result_get_global_context (js_result),
+                                value);
+}
+
diff --git a/libgitg/gitg-js-utils.h b/libgitg/gitg-js-utils.h
new file mode 100644
index 0000000..4e5e65f
--- /dev/null
+++ b/libgitg/gitg-js-utils.h
@@ -0,0 +1,10 @@
+#ifndef __GITG_JS_UTILS_H__
+#define __GITG_JS_UTILS_H__
+
+#include <webkit2/webkit2.h>
+
+gchar    *gitg_js_utils_get_json (WebKitJavascriptResult *js_result);
+gboolean  gitg_js_utils_check    (WebKitJavascriptResult *js_result);
+
+#endif /* __GITG_JS_UTILS_H__ */
+
diff --git a/libgitg/resources/diff-view-html-builder.js b/libgitg/resources/diff-view-html-builder.js
index f4f3158..83f1a87 100644
--- a/libgitg/resources/diff-view-html-builder.js
+++ b/libgitg/resources/diff-view-html-builder.js
@@ -138,7 +138,7 @@ function diff_file(file, lnstate, data)
                'FILE_PATH': file_path,
                'FILE_BODY': file_body,
                'FILE_STATS': file_stats,
-               'FILE_STAGE': lnstate.stagebutton,
+               'FILE_FILENAME': file_path,
                'FILE_CLASSES': file_classes
        };
 
@@ -164,7 +164,7 @@ function diff_files(files, lines, maxlines, data)
                'FILE_PATH',
                'FILE_BODY',
                'FILE_STATS',
-               'FILE_STAGE'
+               'FILE_FILENAME',
                'FILE_CLASSES'
        ];
 
@@ -172,7 +172,8 @@ function diff_files(files, lines, maxlines, data)
 
        for (var r in repl)
        {
-               replacements[repl[r]] = new RegExp('<!-- \\$\\{' + repl[r] + '\\} -->', 'g');
+               var varspec = '\\$\\{' + repl[r] + '\\}';
+               replacements[repl[r]] = new RegExp('<!-- ' + varspec + ' -->|' + varspec, 'g');
        }
 
        var lnstate = {
@@ -182,28 +183,9 @@ function diff_files(files, lines, maxlines, data)
                processed: 0,
                nexttick: 0,
                tickfreq: 0.01,
-               stagebutton: '',
                replacements: replacements,
        };
 
-       if (data.settings.staged || data.settings.unstaged)
-       {
-               var cls;
-               var nm;
-
-               if (data.settings.staged)
-               {
-                       cls = 'unstage';
-                       nm = data.settings.strings.unstage;
-               }
-               else
-               {
-                       cls = 'stage';
-                       nm = data.settings.strings.stage;
-               }
-
-               lnstate.stagebutton = '<span class="' + cls + '">' + nm + '</span>';
-       }
        // special empty background filler
        f += diff_file({hunks: [null]}, lnstate, data);
 
diff --git a/libgitg/resources/diff-view.css b/libgitg/resources/diff-view.css
index 04e2d09..2104742 100644
--- a/libgitg/resources/diff-view.css
+++ b/libgitg/resources/diff-view.css
@@ -235,40 +235,6 @@ div.loading {
   font-family: sans-serif;
 }
 
-span.stage, span.unstage {
-  border-radius: 3px;
-  padding: 2px 5px 2px 5px;
-  cursor: pointer;
-  display: inline-block;
-  vertical-align: middle;
-  text-align: center;
-  border: 1px solid #999;
-  box-shadow: inset 1px 1px 1px 0px #fff;
-  color: #333;
-}
-
-span.stage {
-  background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #ddffdd), color-stop(1, 
#99dd99));
-  text-shadow: 1px 1px #99ff99;
-  border: 1px solid #6a6;
-}
-
-span.stage:hover {
-  background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #ddffdd), color-stop(1, 
#bbddbb));
-  border: 1px solid #6c6;
-}
-
-span.unstage {
-  background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #ffdddd), color-stop(1, 
#dd9999));
-  text-shadow: 1px 1px #ff9999;
-  border: 1px solid #a66;
-}
-
-span.unstage:hover {
-  background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #ffdddd), color-stop(1, 
#ddbbbb));
-  border: 1px solid #c66;
-}
-
 span.hunk_stats, span.file_path {
   display: inline-block;
   vertical-align: middle;
diff --git a/libgitg/resources/diff-view.html b/libgitg/resources/diff-view.html
index 91cb5cd..d8bd96f 100644
--- a/libgitg/resources/diff-view.html
+++ b/libgitg/resources/diff-view.html
@@ -31,7 +31,6 @@
             <tr class="file_header">
               <td colspan="3">
                 <span class="expander">-</span>
-                <!-- ${FILE_STAGE} -->
                 <!-- ${FILE_STATS} -->
                 <span class="file_path"><!-- ${FILE_PATH} --></span>
               </td>
diff --git a/libgitg/resources/diff-view.js b/libgitg/resources/diff-view.js
index 7b5b2e1..364e986 100644
--- a/libgitg/resources/diff-view.js
+++ b/libgitg/resources/diff-view.js
@@ -144,23 +144,150 @@ function expand_collapse()
        expander.closest('tbody').toggleClass("collapsed");
 }
 
+function next_element(elem)
+{
+       elem = elem.nextSibling;
+
+       while (elem != null && elem.nodeType != Node.ELEMENT_NODE)
+       {
+               elem = elem.nextSibling;
+       }
+
+       return elem;
+}
+
+function has_class(e, cls)
+{
+       return e.classList.contains(cls);
+}
+
+var has_selection = false;
+
+function update_has_selection()
+{
+       var selection = document.querySelectorAll('tr.added.selected, tr.removed.selected');
+       var hs = (selection.length != 0);
+
+       if (hs != has_selection)
+       {
+               has_selection = hs;
+
+               var v = has_selection ? "yes" : "no";
+
+               xhr_get('internal', {action: 'selection-changed', value: v});
+       }
+}
+
+function prepare_patchset(filediv)
+{
+       var elem = filediv.querySelector('tr.file_header');
+
+       elem = next_element(elem);
+
+       var patches = [];
+
+       var doffset = 0;
+
+       var a = "a".charCodeAt(0);
+       var r = "r".charCodeAt(0);
+
+       while (elem != null)
+       {
+               var e = elem;
+               elem = next_element(elem);
+
+               var added = has_class(e, 'added');
+               var removed = has_class(e, 'removed');
+
+               if (!added && !removed)
+               {
+                       continue;
+               }
+
+               var selected = has_class(e, 'selected');
+               var offset = parseInt(e.getAttribute('data-offset'));
+               var length = parseInt(e.getAttribute('data-length'));
+
+               if (selected)
+               {
+                       // [sign, old_offset, new_offset, length]
+                       patches.push([added ? a : r, offset + doffset, offset, length]);
+
+                       doffset += added ? -length : length;
+               }
+               else
+               {
+                       doffset += added ? -length : length;
+               }
+       }
+
+       var filename = filediv.getAttribute('data-filename');
+       return [filename, patches];
+}
+
+function get_selection()
+{
+       var files = document.querySelectorAll('#diff_content div.file');
+       var ret = [];
+
+       for (var i = 0; i < files.length; i++)
+       {
+               ret.push(prepare_patchset(files[i]));
+       }
+
+       return ret;
+}
 
 function stage_unstage_hunk()
 {
-       var addrm = $(this).nextUntil('tr.file_header, tr.hunk_header').filter('tr.added, tr.removed');
+       var elem = next_element(this);
 
-       var unsel = $.grep(addrm, function(e) { return !$(e).hasClass('selected'); });
-       addrm.removeClass('selected');
+       var hasunsel = false;
+       var lines = [];
 
-       if (unsel.length != 0)
+       while (elem != null && !(has_class(elem, 'file_header') || has_class(elem, 'hunk_header')))
        {
-               addrm.addClass('selected');
+               if ((has_class(elem, 'added') || has_class(elem, 'removed')))
+               {
+                       lines.push(elem);
+
+                       if (!has_class(elem, 'selected'))
+                       {
+                               hasunsel = true;
+                       }
+               }
+
+               elem = next_element(elem);
        }
+
+       for (var i = 0; i < lines.length; i++)
+       {
+               if (hasunsel)
+               {
+                       lines[i].classList.add('selected');
+               }
+               else
+               {
+                       lines[i].classList.remove('selected');
+               }
+       }
+
+       update_has_selection();
 }
 
 function stage_unstage_line()
 {
-       $(this).toggleClass("selected");
+       if (has_class(this, 'selected'))
+       {
+               this.classList.remove('selected');
+       }
+       else
+       {
+               this.classList.add('selected');
+       }
+
+       update_has_selection();
+}
 
 function xhr_get(action, data, onload)
 {
@@ -255,8 +382,12 @@ function update_diff(id, lsettings)
                        content.html(event.data.diff_html);
 
                        $(".expander").click(expand_collapse);
-                       $("tr.hunk_header").click(stage_unstage_hunk);
-                       $("tr.added, tr.removed").click(stage_unstage_line);
+
+                       if (settings.staged || settings.unstaged)
+                       {
+                               $("tr.hunk_header").click(stage_unstage_hunk);
+                               $("tr.added, tr.removed").click(stage_unstage_line);
+                       }
                }
        }
 
diff --git a/vapi/gitg-js-utils.vapi b/vapi/gitg-js-utils.vapi
new file mode 100644
index 0000000..d97d563
--- /dev/null
+++ b/vapi/gitg-js-utils.vapi
@@ -0,0 +1,6 @@
+[CCode(cprefix = "GitgJsUtils", lower_case_cprefix = "gitg_js_utils_", cheader_filename = 
"libgitg/gitg-js-utils.h")]
+namespace GitgJsUtils
+{
+       public string get_json(WebKit.JavascriptResult result);
+       public bool check(WebKit.JavascriptResult result);
+}


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