[gnome-terminal/wip/text-objects: 2/4] Text-Objects: on profile change callback load text-objects vte match.



commit fbb25fee07fc7d74146171455e9ab8d39475acd6
Author: Rodolfo Granata <warlock cc gmail com>
Date:   Fri May 10 16:42:43 2019 -0400

    Text-Objects: on profile change callback load text-objects vte match.

 src/profile-editor.c       |   4 +-
 src/profile-text-objects.c | 101 +++++++++++++++++++++++++++++++++++++++++++--
 src/profile-text-objects.h |  18 +++++++-
 src/terminal-debug.h       |   3 +-
 src/terminal-screen.c      |  94 ++++++++++++++++++++++++++++++++---------
 src/terminal-screen.h      |   1 +
 src/terminal-util.c        |  17 ++++----
 7 files changed, 202 insertions(+), 36 deletions(-)
---
diff --git a/src/profile-editor.c b/src/profile-editor.c
index d9f0926f..0609559c 100644
--- a/src/profile-editor.c
+++ b/src/profile-editor.c
@@ -837,7 +837,7 @@ profile_prefs_init (void)
   g_free (text);
 
   /* Text-Objects: setup list */
-  profile_text_objects_init();
+  profile_text_objects_editor_init ();
 }
 
 /* Called each time the user switches away from a profile, so it's no longer being edited */
@@ -1260,7 +1260,7 @@ profile_prefs_load (const char *uuid, GSettings *profile)
                                G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET);
 
   /* Text-Object options */
-  profile_text_objects_load(profile);
+  profile_text_objects_editor_load (profile);
 }
 
 /* Called once per Preferences window, to destroy stuff that doesn't depend on the profile being edited */
diff --git a/src/profile-text-objects.c b/src/profile-text-objects.c
index 23e42f4d..2faafce7 100644
--- a/src/profile-text-objects.c
+++ b/src/profile-text-objects.c
@@ -17,13 +17,16 @@
 
 #include "config.h"
 
-#include "profile-editor.h"
-#include "profile-text-objects.h"
+#include "terminal-debug.h"
 #include "terminal-libgsystem.h"
+#include "terminal-pcre2.h"
 #include "terminal-prefs.h"
+#include "profile-editor.h"
+#include "profile-text-objects.h"
 
 #include <glib/gi18n.h>
 
+
 enum {
   TEXT_OBJ_NAME = 0,
   TEXT_OBJ_MATCH,
@@ -34,7 +37,7 @@ enum {
 
 /* setup the the profile editor's text-object tab */
 void
-profile_text_objects_init(void)
+profile_text_objects_editor_init(void)
 {
   GtkBuilder *builder = the_pref_data->builder;
   GtkTreeView *tree_view =
@@ -263,6 +266,17 @@ validate_text_object_cb (GtkEntry *entry, gpointer user_data)
     valid &= (end_ptr == text + strlen(text));
   }
 
+  /* Check that the 'match' field contains a valid regex */
+  GtkEntry *regex = GTK_ENTRY (gtk_builder_get_object (builder, "txt-obj-match"));
+  if (entry == regex) {
+    VteRegex *ok = vte_regex_new_for_match (
+        text, -1, PCRE2_UTF | PCRE2_NO_UTF_CHECK | PCRE2_MULTILINE, NULL);
+    valid &= (ok != NULL);
+    if (ok) {
+      vte_regex_unref (ok);
+    }
+  }
+
   /* react to input being valid: set warning icon and toggle Save button */
   gtk_entry_set_icon_from_icon_name (
       entry,
@@ -321,7 +335,7 @@ profile_text_objects_bind(GSettings *profile)
 }
 
 void
-profile_text_objects_load(GSettings *profile)
+profile_text_objects_editor_load(GSettings *profile)
 {
   /* Create the model */
   GtkListStore *store = gtk_list_store_new (
@@ -357,3 +371,82 @@ profile_text_objects_load(GSettings *profile)
   /* bind buttons and actions */
   profile_text_objects_bind (profile);
 }
+
+static gint
+_handler_prio_compare(const UrlHandler *a, const UrlHandler *b) {
+  /* Higher priority objects first */
+  return (a->prio < b->prio) ? 1 : ((a->prio > b->prio) ? -1 : 0);
+}
+
+static void
+_free_url_handler(UrlHandler *uh) {
+  if (uh) {
+    g_free(uh->name);
+    g_free(uh->match);
+    g_free(uh->rewrite);
+    g_free(uh);
+  }
+}
+
+void profile_text_objects_free (GSList *handlers) {
+  g_slist_free_full(handlers, (GDestroyNotify)_free_url_handler);
+}
+
+/**
+ * terminal_util_load_url_handlers:
+ *
+ * Load text-object conf from GSettings
+ */
+GSList *
+profile_text_objects_load (GSettings *profile)
+{
+  GError *error = NULL;
+  GSList *handlers = NULL;
+  GVariantIter viter;
+  gchar *name, *match, *rewrite;
+  gint prio;
+
+  /* Load text-objects config from profile gsettings */
+  gs_unref_variant GVariant *text_objects =
+    g_settings_get_value(profile, "text-objects");
+  /* populate the text-object table */
+  g_variant_iter_init (&viter, text_objects);
+  while (g_variant_iter_next (&viter, "{s(ssi)}", &name, &match, &rewrite, &prio)) {
+    UrlHandler *uh = g_new0(UrlHandler, 1);
+    /* adopted: uh members take ownership of strings */
+    uh->name = name;
+    uh->match = match;
+    uh->rewrite = rewrite;
+    uh->prio = prio;
+    uh->regex = vte_regex_new_for_match (uh->match, -1,
+                                         PCRE2_UTF | PCRE2_NO_UTF_CHECK |
+                                         PCRE2_MULTILINE, &error);
+    if (error != NULL) {
+      _terminal_debug_print (
+          TERMINAL_DEBUG_TEXT_OBJECTS,
+          "Text-Objects: error compiling regex [%s]\n", name);
+      g_clear_error(&error);
+      _free_url_handler(uh);
+      continue;
+    }
+    if (!vte_regex_jit (uh->regex, PCRE2_JIT_COMPLETE, &error) ||
+        !vte_regex_jit (uh->regex, PCRE2_JIT_PARTIAL_SOFT, &error)) {
+      _terminal_debug_print (
+          TERMINAL_DEBUG_TEXT_OBJECTS,
+          "Text-Objects: failed to JIT regex for [%s]: '%s' %s\n",
+          name, (char*)uh->match, error->message);
+      g_clear_error (&error);
+      _free_url_handler(uh);
+      continue;
+    }
+    _terminal_debug_print (
+        TERMINAL_DEBUG_TEXT_OBJECTS,
+        "Text-Objects: loaded [%s]: %s -> %s\n", name, uh->match, uh->rewrite);
+    /* when vte checks for matches (vte_terminal_match_check_event)
+     * they're returned in order of calling vte_terminal_match_add_regex */
+    handlers =
+      g_slist_insert_sorted(handlers, uh, (GCompareFunc)_handler_prio_compare);
+  }
+
+  return handlers;
+}
diff --git a/src/profile-text-objects.h b/src/profile-text-objects.h
index ae4f328b..9c06877c 100644
--- a/src/profile-text-objects.h
+++ b/src/profile-text-objects.h
@@ -20,11 +20,25 @@
 
 #include <gio/gio.h>
 #include <gtk/gtk.h>
+#include <vte/vte.h>
 
 G_BEGIN_DECLS
 
-void profile_text_objects_init(void);
-void profile_text_objects_load(GSettings *profile);
+typedef struct
+{
+  char *name;
+  char *match;
+  char *rewrite;
+  gint prio;
+  VteRegex *regex;
+  int tag;
+} UrlHandler;
+
+void profile_text_objects_editor_init (void);
+void profile_text_objects_editor_load (GSettings *profile);
+
+GSList *profile_text_objects_load (GSettings *profile);
+void profile_text_objects_free (GSList*);
 
 G_END_DECLS
 
diff --git a/src/terminal-debug.h b/src/terminal-debug.h
index 0fafcc3a..5795d3b7 100644
--- a/src/terminal-debug.h
+++ b/src/terminal-debug.h
@@ -34,7 +34,8 @@ typedef enum {
   TERMINAL_DEBUG_PROCESSES     = 1 << 6,
   TERMINAL_DEBUG_PROFILE       = 1 << 7,
   TERMINAL_DEBUG_SETTINGS_LIST = 1 << 8,
-  TERMINAL_DEBUG_SEARCH        = 1 << 9
+  TERMINAL_DEBUG_SEARCH        = 1 << 9,
+  TERMINAL_DEBUG_TEXT_OBJECTS  = 1 << 10,
 } TerminalDebugFlags;
 
 void _terminal_debug_init(void);
diff --git a/src/terminal-screen.c b/src/terminal-screen.c
index 23134e80..ea01444a 100644
--- a/src/terminal-screen.c
+++ b/src/terminal-screen.c
@@ -59,6 +59,7 @@
 #include "terminal-window.h"
 #include "terminal-info-bar.h"
 #include "terminal-libgsystem.h"
+#include "profile-text-objects.h"
 
 #include "eggshell.h"
 
@@ -77,6 +78,7 @@ typedef struct
 {
   int tag;
   TerminalURLFlavor flavor;
+  UrlHandler *url_handler;
 } TagData;
 
 struct _TerminalScreenPrivate
@@ -94,6 +96,7 @@ struct _TerminalScreenPrivate
   int child_pid;
   GSList *match_tags;
   guint launch_child_source_id;
+  GSList *custom_url_handlers;
 };
 
 enum
@@ -155,9 +158,10 @@ static char* terminal_screen_check_hyperlink   (TerminalScreen            *scree
 static void terminal_screen_check_extra (TerminalScreen *screen,
                                          GdkEvent       *event,
                                          char           **number_info);
-static char* terminal_screen_check_match       (TerminalScreen            *screen,
-                                                GdkEvent                  *event,
-                                                int                  *flavor);
+static char* terminal_screen_check_match (TerminalScreen *screen,
+                                          GdkEvent       *event,
+                                          int            *flavor,
+                                          UrlHandler     **uhandler);
 
 static void terminal_screen_set_override_command (TerminalScreen  *screen,
                                                   char           **argv,
@@ -487,7 +491,7 @@ terminal_screen_class_init (TerminalScreenClass *klass)
                   g_cclosure_marshal_VOID__OBJECT,
                   G_TYPE_NONE,
                   1, G_TYPE_SETTINGS);
-  
+
   signals[SHOW_POPUP_MENU] =
     g_signal_new (I_("show-popup-menu"),
                   G_OBJECT_CLASS_TYPE (object_class),
@@ -508,7 +512,7 @@ terminal_screen_class_init (TerminalScreenClass *klass)
                   _terminal_marshal_BOOLEAN__STRING_INT_UINT,
                   G_TYPE_BOOLEAN,
                   3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_UINT);
-  
+
   signals[CLOSE_SCREEN] =
     g_signal_new (I_("close-screen"),
                   G_OBJECT_CLASS_TYPE (object_class),
@@ -699,7 +703,7 @@ terminal_screen_new (GSettings       *profile,
   return screen;
 }
 
-gboolean 
+gboolean
 terminal_screen_exec (TerminalScreen *screen,
                       char          **argv,
                       char          **envv,
@@ -824,7 +828,7 @@ terminal_screen_profile_changed_cb (GSettings     *profile,
   if (!prop_name || prop_name == I_(TERMINAL_PROFILE_BACKSPACE_BINDING_KEY))
   vte_terminal_set_backspace_binding (vte_terminal,
                                       g_settings_get_enum (profile, TERMINAL_PROFILE_BACKSPACE_BINDING_KEY));
-  
+
   if (!prop_name || prop_name == I_(TERMINAL_PROFILE_DELETE_BINDING_KEY))
   vte_terminal_set_delete_binding (vte_terminal,
                                    g_settings_get_enum (profile, TERMINAL_PROFILE_DELETE_BINDING_KEY));
@@ -859,6 +863,40 @@ terminal_screen_profile_changed_cb (GSettings     *profile,
       vte_terminal_set_word_char_exceptions (vte_terminal, word_char_exceptions);
     }
 
+  VteTerminal *terminal = VTE_TERMINAL (screen);
+
+  /* remove old text-object handlers if any */
+  if (priv->custom_url_handlers) {
+    GSList *next = priv->custom_url_handlers;
+    while (next != NULL)
+      {
+        UrlHandler *uh = (UrlHandler*)next->data;
+        next = g_slist_next(next);
+        vte_terminal_match_remove (terminal, uh->tag);
+      }
+    profile_text_objects_free (priv->custom_url_handlers);
+  }
+
+  /* connect new text-object handlers */
+  priv->custom_url_handlers = profile_text_objects_load (profile);
+  if (priv->custom_url_handlers) {
+    GSList *next = priv->custom_url_handlers;
+    while (next != NULL)
+      {
+        UrlHandler *uh = (UrlHandler*)next->data;
+        next = g_slist_next(next);
+        TagData *tag_data;
+        tag_data = g_slice_new (TagData);
+        tag_data->flavor = FLAVOR_CUSTOM_URI;
+        tag_data->tag = vte_terminal_match_add_regex (terminal, uh->regex, 0);
+        uh->tag = tag_data->tag;
+        tag_data->url_handler = uh;
+        vte_terminal_match_set_cursor_type (terminal,
+                                            tag_data->tag, URL_MATCH_CURSOR);
+        priv->match_tags = g_slist_prepend (priv->match_tags, tag_data);
+      }
+  }
+
   g_object_thaw_notify (object);
 }
 
@@ -1538,6 +1576,7 @@ terminal_screen_button_press (GtkWidget      *widget,
     GTK_WIDGET_CLASS (terminal_screen_parent_class)->button_press_event;
   gs_free char *hyperlink = NULL;
   gs_free char *url = NULL;
+  UrlHandler *uhandler = NULL;
   int url_flavor = 0;
   gs_free char *number_info = NULL;
   guint state;
@@ -1545,9 +1584,6 @@ terminal_screen_button_press (GtkWidget      *widget,
   state = event->state & gtk_accelerator_get_default_mod_mask ();
 
   hyperlink = terminal_screen_check_hyperlink (screen, (GdkEvent*)event);
-  url = terminal_screen_check_match (screen, (GdkEvent*)event, &url_flavor);
-  terminal_screen_check_extra (screen, (GdkEvent*)event, &number_info);
-
   if (hyperlink != NULL &&
       (event->button == 1 || event->button == 2) &&
       (state & GDK_CONTROL_MASK))
@@ -1563,6 +1599,22 @@ terminal_screen_button_press (GtkWidget      *widget,
         return TRUE; /* don't do anything else such as select with the click */
     }
 
+  url = terminal_screen_check_match (screen, (GdkEvent*)event,
+                                     &url_flavor, &uhandler);
+  /* try re-writing the URL if its a custom flavor */
+  if (url_flavor == FLAVOR_CUSTOM_URI && uhandler != NULL) {
+    gchar *urltmp = vte_regex_substitute (
+        uhandler->regex,
+        url,
+        uhandler->rewrite,
+        PCRE2_SUBSTITUTE_EXTENDED,
+        NULL);
+    if (urltmp != NULL) {
+      g_free(url);
+      url = urltmp;
+    }
+  }
+
   if (url != NULL &&
       (event->button == 1 || event->button == 2) &&
       (state & GDK_CONTROL_MASK))
@@ -1578,6 +1630,7 @@ terminal_screen_button_press (GtkWidget      *widget,
         return TRUE; /* don't do anything else such as select with the click */
     }
 
+  terminal_screen_check_extra (screen, (GdkEvent*)event, &number_info);
   if (event->type == GDK_BUTTON_PRESS && event->button == 3)
     {
       if (!(event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK)))
@@ -1618,7 +1671,7 @@ terminal_screen_button_press (GtkWidget      *widget,
  * Tries to determine the current working directory of the foreground process
  * in @screen's PTY, falling back to the current working directory of the
  * primary child.
- * 
+ *
  * Returns: a newly allocated string containing the current working directory,
  *   or %NULL on failure
  */
@@ -1663,9 +1716,9 @@ terminal_screen_child_exited (VteTerminal *terminal,
                          screen);
 
   priv->child_pid = -1;
-  
+
   action = g_settings_get_enum (priv->profile, TERMINAL_PROFILE_EXIT_ACTION_KEY);
-  
+
   switch (action)
     {
     case TERMINAL_EXIT_CLOSE:
@@ -1737,8 +1790,8 @@ terminal_screen_drag_data_received (GtkWidget        *widget,
       {
         GdkAtom atom = GDK_POINTER_TO_ATOM (tmp->data);
 
-        g_print ("Target: %s\n", gdk_atom_name (atom));        
-        
+        g_print ("Target: %s\n", gdk_atom_name (atom));
+
         tmp = tmp->next;
       }
 
@@ -1801,7 +1854,7 @@ terminal_screen_drag_data_received (GtkWidget        *widget,
         char *utf8_data, *newline, *text;
         char *uris[2];
         gsize len;
-        
+
         /* MOZ_URL is in UCS-2 but in format 8. BROKEN!
          *
          * The data contains the URL, a \n, then the
@@ -1838,7 +1891,7 @@ terminal_screen_drag_data_received (GtkWidget        *widget,
         char *utf8_data, *newline, *text;
         char *uris[2];
         gsize len;
-        
+
         /* The data contains the URL, a \n, then the
          * title of the web page.
          */
@@ -1942,7 +1995,8 @@ terminal_screen_check_hyperlink (TerminalScreen *screen,
 static char*
 terminal_screen_check_match (TerminalScreen *screen,
                              GdkEvent       *event,
-                             int       *flavor)
+                             int            *flavor,
+                             UrlHandler     **uhandler)
 {
   TerminalScreenPrivate *priv = screen->priv;
   GSList *tags;
@@ -1957,6 +2011,8 @@ terminal_screen_check_match (TerminalScreen *screen,
        {
          if (flavor)
            *flavor = tag_data->flavor;
+         if (uhandler != NULL)
+           *uhandler = tag_data->url_handler;
          return match;
        }
     }
@@ -2016,7 +2072,7 @@ terminal_screen_check_extra (TerminalScreen *screen,
  *
  * Checks whether there's a foreground process running in
  * this terminal.
- * 
+ *
  * Returns: %TRUE iff there's a foreground process running in @screen
  */
 gboolean
diff --git a/src/terminal-screen.h b/src/terminal-screen.h
index ff77fcf7..11349deb 100644
--- a/src/terminal-screen.h
+++ b/src/terminal-screen.h
@@ -32,6 +32,7 @@ typedef enum {
   FLAVOR_VOIP_CALL,
   FLAVOR_EMAIL,
   FLAVOR_NUMBER,
+  FLAVOR_CUSTOM_URI,
 } TerminalURLFlavor;
 
 /* Forward decls */
diff --git a/src/terminal-util.c b/src/terminal-util.c
index 72a0d401..3ea70b7c 100644
--- a/src/terminal-util.c
+++ b/src/terminal-util.c
@@ -51,17 +51,17 @@
  * @message_format: printf() style format string
  *
  * Create a #GtkMessageDialog window with the message, and present it, handling its buttons.
- * If @weap_ptr is not #NULL, only create the dialog if <literal>*weap_ptr</literal> is #NULL 
- * (and in that * case, set @weap_ptr to be a weak pointer to the new dialog), otherwise just 
+ * If @weap_ptr is not #NULL, only create the dialog if <literal>*weap_ptr</literal> is #NULL
+ * (and in that * case, set @weap_ptr to be a weak pointer to the new dialog), otherwise just
  * present <literal>*weak_ptr</literal>. Note that in this last case, the message <emph>will</emph>
  * be changed.
  */
 void
-terminal_util_show_error_dialog (GtkWindow *transient_parent, 
+terminal_util_show_error_dialog (GtkWindow *transient_parent,
                                  GtkWidget **weak_ptr,
                                  GError *error,
-                                 const char *message_format, 
-                                 ...) 
+                                 const char *message_format,
+                                 ...)
 {
   gs_free char *message;
   va_list args;
@@ -97,10 +97,10 @@ terminal_util_show_error_dialog (GtkWindow *transient_parent,
         }
 
       gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
-      
+
       gtk_widget_show_all (dialog);
     }
-  else 
+  else
     {
       g_return_if_fail (GTK_IS_MESSAGE_DIALOG (*weak_ptr));
 
@@ -264,7 +264,7 @@ terminal_util_set_atk_name_description (GtkWidget  *widget,
                                         const char *desc)
 {
   AtkObject *obj;
-  
+
   obj = gtk_widget_get_accessible (widget);
 
   if (obj == NULL)
@@ -306,6 +306,7 @@ terminal_util_open_url (GtkWidget *parent,
       else
        uri = g_strdup (orig_url);
       break;
+    case FLAVOR_CUSTOM_URI:
     case FLAVOR_VOIP_CALL:
     case FLAVOR_AS_IS:
       uri = g_strdup (orig_url);


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