[geany/geany-plugins] 28c5c5: git-ui: Add tooltips for removed and modified lines

Colomban Wendling git-noreply at xxxxx
Wed Feb 18 08:38:05 UTC 2015


Branch:      refs/heads/master
Author:      Colomban Wendling <ban at herbesfolles.org>
Committer:   Colomban Wendling <ban at herbesfolles.org>
Date:        Tue, 07 Oct 2014 15:46:29 UTC
Commit:      28c5c5230d0928920cde17bd4db1c4e50ecefd55
             https://github.com/geany/geany-plugins/commit/28c5c5230d0928920cde17bd4db1c4e50ecefd55

Log Message:
-----------
git-ui: Add tooltips for removed and modified lines


Modified Paths:
--------------
    build/git-ui.m4
    git-ui/src/ggu-plugin.c

Modified: build/git-ui.m4
2 lines changed, 1 insertions(+), 1 deletions(-)
===================================================================
@@ -3,7 +3,7 @@ AC_DEFUN([GP_CHECK_GITUI],
     GP_ARG_DISABLE([GitUI], [auto])
 
     GP_CHECK_PLUGIN_DEPS([GitUI], [GITUI],
-                         [$GP_GTK_PACKAGE
+                         [$GP_GTK_PACKAGE >= 2.18
                           glib-2.0
                           libgit2])
 


Modified: git-ui/src/ggu-plugin.c
207 lines changed, 179 insertions(+), 28 deletions(-)
===================================================================
@@ -53,7 +53,8 @@ PLUGIN_SET_TRANSLATABLE_INFO (
  * data that we know cannot ever be a valid job */
 #define QUIT_THREAD_JOB ((AsyncBlobJob *) (&G_queue))
 
-#define MARKER_ALLOCATED_QTAG (g_quark_from_static_string ("ggu-git-marker-allocated"))
+#define RESOURCES_ALLOCATED_QTAG \
+  (g_quark_from_static_string ("ggu-git-resources-allocated"))
 
 
 enum {
@@ -75,6 +76,18 @@ struct AsyncBlobJob {
   gpointer      user_data;
 };
 
+typedef struct TooltipHunkData TooltipHunkData;
+struct TooltipHunkData {
+  gint            line;
+  gboolean        found;
+  GeanyDocument  *doc;
+  const git_blob *file_blob;
+  GtkTooltip     *tooltip;
+};
+
+#define TOOLTIP_HUNK_DATA_INIT(line, doc, blob, tooltip) \
+  { line, FALSE, doc, blob, tooltip }
+
 
 /* cache */
 static git_blob        *G_file_blob = NULL;
@@ -93,11 +106,17 @@ static struct {
 };
 
 
-static void on_git_dir_changed (GFileMonitor     *monitor,
-                                GFile            *file,
-                                GFile            *other_file,
-                                GFileMonitorEvent event_type,
-                                gpointer          user_data);
+static void         on_git_dir_changed          (GFileMonitor     *monitor,
+                                                 GFile            *file,
+                                                 GFile            *other_file,
+                                                 GFileMonitorEvent event_type,
+                                                 gpointer          user_data);
+static gboolean     on_sci_query_tooltip        (GtkWidget   *widget,
+                                                 gint         x,
+                                                 gint         y,
+                                                 gboolean     keyboard_mode,
+                                                 GtkTooltip  *tooltip,
+                                                 gpointer     user_data);
 
 
 static void
@@ -286,11 +305,11 @@ allocate_marker (ScintillaObject *sci,
 }
 
 static gboolean
-allocate_markers (ScintillaObject *sci)
+allocate_resources (ScintillaObject *sci)
 {
   guint i;
   
-  if (g_object_get_qdata (G_OBJECT (sci), MARKER_ALLOCATED_QTAG)) {
+  if (g_object_get_qdata (G_OBJECT (sci), RESOURCES_ALLOCATED_QTAG)) {
     return TRUE;
   }
   
@@ -312,16 +331,21 @@ allocate_markers (ScintillaObject *sci)
                             ((G_markers[i].color & 0x0000ff) << 16));
   }
   
-  g_object_set_qdata (G_OBJECT (sci), MARKER_ALLOCATED_QTAG,
+  /* setup tooltips */
+  gtk_widget_set_has_tooltip (GTK_WIDGET (sci), TRUE);
+  g_signal_connect (sci, "query-tooltip",
+                    G_CALLBACK (on_sci_query_tooltip), NULL);
+  
+  g_object_set_qdata (G_OBJECT (sci), RESOURCES_ALLOCATED_QTAG,
                       sci /* anything non-NULL */);
   
   return TRUE;
 }
 
 static void
-release_markers (ScintillaObject *sci)
+release_resources (ScintillaObject *sci)
 {
-  if (g_object_get_qdata (G_OBJECT (sci), MARKER_ALLOCATED_QTAG)) {
+  if (g_object_get_qdata (G_OBJECT (sci), RESOURCES_ALLOCATED_QTAG)) {
     guint j;
     
     for (j = 0; j < MARKER_COUNT; j++) {
@@ -330,11 +354,35 @@ release_markers (ScintillaObject *sci)
                                 G_markers[j].num, SC_MARK_AVAILABLE);
       }
     }
-    g_object_set_qdata (G_OBJECT (sci), MARKER_ALLOCATED_QTAG, NULL);
+    g_signal_handlers_disconnect_by_func (sci, on_sci_query_tooltip, NULL);
+    g_object_set_qdata (G_OBJECT (sci), RESOURCES_ALLOCATED_QTAG, NULL);
   }
 }
 
 static int
+diff_blob_to_sci (const git_blob   *old_blob,
+                  ScintillaObject  *sci,
+                  git_diff_hunk_cb  hunk_cb,
+                  void             *payload)
+{
+  git_diff_options  opts;
+  const gchar      *buf;
+  size_t            len;
+  
+  buf = (const gchar *) scintilla_send_message (sci, SCI_GETCHARACTERPOINTER,
+                                                0, 0);
+  len = sci_get_length (sci);
+  
+  /* no context lines, and no need to bother about binary checks */
+  git_diff_init_options (&opts, GIT_DIFF_OPTIONS_VERSION);
+  opts.context_lines = 0;
+  opts.flags = GIT_DIFF_FORCE_TEXT;
+  
+  return git_diff_blob_to_buffer (old_blob, NULL, buf, len, NULL, &opts,
+                                  NULL, hunk_cb, NULL, payload);
+}
+
+static int
 diff_hunk_cb (const git_diff_delta *delta,
               const git_diff_hunk  *hunk,
               void                 *data)
@@ -356,6 +404,122 @@ diff_hunk_cb (const git_diff_delta *delta,
   return 0;
 }
 
+static GtkWidget *
+get_widget_for_blob_range (GeanyDocument   *doc,
+                           const git_blob  *blob,
+                           gint             line_start,
+                           gint             n_lines)
+{
+  ScintillaObject        *sci     = editor_create_widget (doc->editor);
+  const GeanyIndentPrefs *iprefs  = editor_get_indent_prefs (doc->editor);
+  gint                    width   = 0;
+  gint                    height  = 0;
+  gint                    i;
+  GtkAllocation           alloc;
+  
+  gtk_widget_get_allocation (GTK_WIDGET (doc->editor->sci), &alloc);
+  
+  highlighting_set_styles (sci, doc->file_type);
+  if (iprefs->type == GEANY_INDENT_TYPE_BOTH) {
+    scintilla_send_message (sci, SCI_SETTABWIDTH, iprefs->hard_tab_width, 0);
+  } else {
+    scintilla_send_message (sci, SCI_SETTABWIDTH, iprefs->width, 0);
+  }
+  scintilla_send_message (sci, SCI_SETINDENT, iprefs->width, 0);
+  
+  /* hide stuff we don't wanna see */
+  scintilla_send_message (sci, SCI_SETHSCROLLBAR, 0, 0);
+  scintilla_send_message (sci, SCI_SETVSCROLLBAR, 0, 0);
+  for (i = 0; i < SC_MAX_MARGIN; i++) {
+    scintilla_send_message (sci, SCI_SETMARGINWIDTHN, i, 0);
+  }
+  
+  scintilla_send_message (sci, SCI_ADDTEXT,
+                          (gulong) git_blob_rawsize (blob),
+                          (glong) git_blob_rawcontent (blob));
+  scintilla_send_message (sci, SCI_SETFIRSTVISIBLELINE, line_start, 0);
+  
+  /* compute the size of the area we want to see */
+  for (i = line_start; i < line_start + n_lines; i++) {
+    gint pos    = sci_get_line_end_position (sci, i);
+    gint end_x  = scintilla_send_message (sci, SCI_POINTXFROMPOSITION, 0, pos);
+    
+    height += scintilla_send_message (sci, SCI_TEXTHEIGHT, i, 0);
+    width = MAX (width, end_x);
+    
+    if (height > alloc.height) {
+      break;
+    }
+  }
+  gtk_widget_set_size_request (GTK_WIDGET (sci),
+                               MIN (width, alloc.width),
+                               MIN (height + 1, alloc.height));
+  
+  return GTK_WIDGET (sci);
+}
+
+static int
+tooltip_diff_hunk_cb (const git_diff_delta *delta,
+                      const git_diff_hunk  *hunk,
+                      void                 *data)
+{
+  TooltipHunkData *thd = data;
+  
+  if (thd->found) {
+    return 1;
+  }
+  
+  if (hunk->old_lines > 0 &&
+      thd->line >= hunk->new_start &&
+      thd->line < hunk->new_start + MAX (1, hunk->new_lines)) {
+    GtkWidget *old = get_widget_for_blob_range (thd->doc, thd->file_blob,
+                                                hunk->old_start - 1,
+                                                hunk->old_lines);
+    
+    gtk_tooltip_set_custom (thd->tooltip, old);
+    thd->found = old != NULL;
+  }
+  
+  return thd->found;
+}
+
+static gboolean
+on_sci_query_tooltip (GtkWidget  *widget,
+                      gint        x,
+                      gint        y,
+                      gboolean    keyboard_mode,
+                      GtkTooltip *tooltip,
+                      gpointer    user_data)
+{
+  gint              min_x;
+  gint              max_x;
+  ScintillaObject  *sci         = (ScintillaObject *) widget;
+  GeanyDocument    *doc         = document_get_current ();
+  gboolean          has_tooltip = FALSE;
+  
+  g_return_val_if_fail (doc && doc->editor->sci == sci, FALSE);
+  
+  min_x = scintilla_send_message (sci, SCI_GETMARGINWIDTHN, 0, 0);
+  max_x = min_x + scintilla_send_message (sci, SCI_GETMARGINWIDTHN, 1, 0);
+  
+  if (x >= min_x && x <= max_x && G_file_blob) {
+    gint pos  = scintilla_send_message (sci, SCI_POSITIONFROMPOINT, x, y);
+    gint line = sci_get_line_from_position (sci, pos);
+    gint mask = scintilla_send_message (sci, SCI_MARKERGET, line, 0);
+    
+    if (mask & ((1 << G_markers[MARKER_LINE_CHANGED].num) |
+                (1 << G_markers[MARKER_LINE_REMOVED].num))) {
+      TooltipHunkData thd = TOOLTIP_HUNK_DATA_INIT (line + 1, doc, G_file_blob,
+                                                    tooltip);
+      
+      diff_blob_to_sci (G_file_blob, sci, tooltip_diff_hunk_cb, &thd);
+      has_tooltip = thd.found;
+    }
+  }
+  
+  return has_tooltip;
+}
+
 static void
 update_diff (const gchar *path,
              git_blob    *blob,
@@ -364,11 +528,8 @@ update_diff (const gchar *path,
   GeanyDocument *doc = document_get_current ();
   
   if (doc && doc->id == GPOINTER_TO_UINT (data) &&
-      blob && allocate_markers (doc->editor->sci)) {
+      blob && allocate_resources (doc->editor->sci)) {
     ScintillaObject  *sci = doc->editor->sci;
-    git_diff_options  opts;
-    const gchar      *buf;
-    size_t            len;
     guint             i;
     
     /* clear previous markers */
@@ -376,17 +537,7 @@ update_diff (const gchar *path,
       scintilla_send_message (sci, SCI_MARKERDELETEALL, G_markers[i].num, 0);
     }
     
-    buf = (const gchar *) scintilla_send_message (sci, SCI_GETCHARACTERPOINTER,
-                                                  0, 0);
-    len = sci_get_length (sci);
-    
-    /* no context lines, and no need to bother about binary checks */
-    git_diff_init_options (&opts, GIT_DIFF_OPTIONS_VERSION);
-    opts.context_lines = 0;
-    opts.flags = GIT_DIFF_FORCE_TEXT;
-    
-    git_diff_blob_to_buffer (blob, NULL, buf, len, NULL, &opts,
-                             NULL, diff_hunk_cb, NULL, sci);
+    diff_blob_to_sci (blob, sci, diff_hunk_cb, sci);
   }
 }
 
@@ -519,7 +670,7 @@ plugin_cleanup (void)
   }
   
   foreach_document (i) {
-    release_markers (documents[i]->editor->sci);
+    release_resources (documents[i]->editor->sci);
   }
   
   git_threads_shutdown ();



--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).


More information about the Plugins-Commits mailing list