[gnome-terminal] app: Show useful information about numbers on right click



commit b2fd6a0a25ac65e3ee835bfd531443b20a19e901
Author: Egmont Koblinger <egmont gmail com>
Date:   Sat Oct 24 00:43:23 2015 +0200

    app: Show useful information about numbers on right click
    
    Add infrastructure for matching extra regexes on right click.
    
    When clicked on a number, add a new entry in the popup menu: group the
    digits by 3, perform dec/hex conversion, and convert to kibi/mebi/etc.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=741728

 src/terminal-screen.c |   75 +++++++++++++++++++++++++++++++-
 src/terminal-screen.h |    4 +-
 src/terminal-util.c   |  112 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/terminal-util.h   |    2 +
 src/terminal-window.c |   10 ++++-
 src/terminal.xml      |    1 +
 6 files changed, 199 insertions(+), 5 deletions(-)
---
diff --git a/src/terminal-screen.c b/src/terminal-screen.c
index a30726e..6eae7ab 100644
--- a/src/terminal-screen.c
+++ b/src/terminal-screen.c
@@ -152,6 +152,9 @@ static void terminal_screen_icon_title_changed        (VteTerminal *vte_terminal
 
 static void update_color_scheme                      (TerminalScreen *screen);
 
+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);
@@ -188,13 +191,21 @@ static const TerminalRegexPattern url_regex_patterns[] = {
   { "(?:news:|man:|info:)[-[:alnum:]\\Q^_{|}~!\"#$%&'()*+,./;:=?`\\E]+", FLAVOR_AS_IS, TRUE },
 };
 
+static const TerminalRegexPattern extra_regex_patterns[] = {
+  { "(0[Xx][[:xdigit:]]+|[[:digit:]]+)", FLAVOR_NUMBER, FALSE },
+};
+
 #ifdef WITH_PCRE2
 static VteRegex **url_regexes;
+static VteRegex **extra_regexes;
 #else
 static GRegex **url_regexes;
+static GRegex **extra_regexes;
 #endif
 static TerminalURLFlavor *url_regex_flavors;
+static TerminalURLFlavor *extra_regex_flavors;
 static guint n_url_regexes;
+static guint n_extra_regexes;
 
 /* See bug #697024 */
 #ifndef __linux__
@@ -594,6 +605,8 @@ terminal_screen_class_init (TerminalScreenClass *klass)
 
   n_url_regexes = G_N_ELEMENTS (url_regex_patterns);
   precompile_regexes (url_regex_patterns, n_url_regexes, &url_regexes, &url_regex_flavors);
+  n_extra_regexes = G_N_ELEMENTS (extra_regex_patterns);
+  precompile_regexes (extra_regex_patterns, n_extra_regexes, &extra_regexes, &extra_regex_flavors);
 
   /* This fixes bug #329827 */
   settings = terminal_app_get_global_settings (terminal_app_get ());
@@ -1445,6 +1458,7 @@ terminal_screen_popup_info_unref (TerminalScreenPopupInfo *info)
   g_object_unref (info->screen);
   g_weak_ref_clear (&info->window_weak_ref);
   g_free (info->url);
+  g_free (info->number_info);
   g_slice_free (TerminalScreenPopupInfo, info);
 }
 
@@ -1482,7 +1496,8 @@ static void
 terminal_screen_do_popup (TerminalScreen *screen,
                           GdkEventButton *event,
                           char *url,
-                          int url_flavor)
+                          int url_flavor,
+                          char *number_info)
 {
   TerminalScreenPopupInfo *info;
 
@@ -1492,6 +1507,7 @@ terminal_screen_do_popup (TerminalScreen *screen,
   info->timestamp = event->time;
   info->url = url; /* adopted */
   info->url_flavor = url_flavor;
+  info->number_info = number_info; /* adopted */
 
   g_signal_emit (screen, signals[SHOW_POPUP_MENU], 0, info);
   terminal_screen_popup_info_unref (info);
@@ -1506,11 +1522,13 @@ terminal_screen_button_press (GtkWidget      *widget,
     GTK_WIDGET_CLASS (terminal_screen_parent_class)->button_press_event;
   gs_free char *url = NULL;
   int url_flavor = 0;
+  gs_free char *number_info = NULL;
   guint state;
 
   state = event->state & gtk_accelerator_get_default_mod_mask ();
 
   url = terminal_screen_check_match (screen, (GdkEvent*)event, &url_flavor);
+  terminal_screen_check_extra (screen, (GdkEvent*)event, &number_info);
 
   if (url != NULL &&
       (event->button == 1 || event->button == 2) &&
@@ -1536,15 +1554,17 @@ terminal_screen_button_press (GtkWidget      *widget,
           if (button_press_event && button_press_event (widget, event))
             return TRUE;
 
-          terminal_screen_do_popup (screen, event, url, url_flavor);
+          terminal_screen_do_popup (screen, event, url, url_flavor, number_info);
           url = NULL; /* adopted to the popup info */
+          number_info = NULL; /* detto */
           return TRUE;
         }
       else if (!(event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)))
         {
           /* do popup on shift+right-click */
-          terminal_screen_do_popup (screen, event, url, url_flavor);
+          terminal_screen_do_popup (screen, event, url, url_flavor, number_info);
           url = NULL; /* adopted to the popup info */
+          number_info = NULL; /* detto */
           return TRUE;
         }
     }
@@ -1907,6 +1927,55 @@ terminal_screen_check_match (TerminalScreen *screen,
   return NULL;
 }
 
+static void
+terminal_screen_check_extra (TerminalScreen *screen,
+                             GdkEvent       *event,
+                             char           **number_info)
+{
+  guint i;
+  char **matches;
+  gboolean flavor_number_found = FALSE;
+
+  matches = g_newa (char *, n_extra_regexes);
+
+  if (
+#ifdef WITH_PCRE2
+      vte_terminal_event_check_regex_simple(
+#else
+      vte_terminal_event_check_gregex_simple(
+#endif
+                                             VTE_TERMINAL (screen),
+                                             event,
+                                             extra_regexes,
+                                             n_extra_regexes,
+                                             0,
+                                             matches))
+    {
+      for (i = 0; i < n_extra_regexes; i++)
+        {
+          if (matches[i] != NULL)
+            {
+              /* Store the first match for each flavor, free all the others */
+              switch (extra_regex_flavors[i])
+                {
+                case FLAVOR_NUMBER:
+                  if (!flavor_number_found)
+                    {
+                      *number_info = terminal_util_number_info (matches[i]);
+                      flavor_number_found = TRUE;
+                    }
+                  g_free (matches[i]);
+                  break;
+                default:
+                  g_free (matches[i]);
+                }
+            }
+        }
+    }
+
+  return NULL;
+}
+
 /**
  * terminal_screen_has_foreground_process:
  * @screen:
diff --git a/src/terminal-screen.h b/src/terminal-screen.h
index 5ac7f4c..be1e765 100644
--- a/src/terminal-screen.h
+++ b/src/terminal-screen.h
@@ -30,7 +30,8 @@ typedef enum {
   FLAVOR_AS_IS,
   FLAVOR_DEFAULT_TO_HTTP,
   FLAVOR_VOIP_CALL,
-  FLAVOR_EMAIL
+  FLAVOR_EMAIL,
+  FLAVOR_NUMBER,
 } TerminalURLFlavor;
 
 /* Forward decls */
@@ -139,6 +140,7 @@ struct _TerminalScreenPopupInfo {
   TerminalScreen *screen;
   char *url;
   TerminalURLFlavor url_flavor;
+  char *number_info;
   guint button;
   guint state;
   guint32 timestamp;
diff --git a/src/terminal-util.c b/src/terminal-util.c
index 973bd19..ce354a1 100644
--- a/src/terminal-util.c
+++ b/src/terminal-util.c
@@ -26,6 +26,7 @@
 #include <time.h>
 #include <unistd.h>
 #include <sys/types.h>
+#include <langinfo.h>
 #include <errno.h>
 
 #include <glib.h>
@@ -951,3 +952,114 @@ terminal_util_bind_mnemonic_label_sensitivity (GtkWidget *widget)
                            (GtkCallback) terminal_util_bind_mnemonic_label_sensitivity,
                            NULL);
 }
+
+/*
+ * "1234567", "'", 3 -> "1'234'567"
+ */
+static char *
+add_separators (const char *in, const char *sep, int groupby)
+{
+  int inlen, outlen, seplen, firstgrouplen;
+  char *out, *ret;
+
+  if (in[0] == '\0')
+    return g_strdup("");
+
+  inlen = strlen(in);
+  seplen = strlen(sep);
+  outlen = inlen + (inlen - 1) / groupby * seplen;
+  ret = out = g_malloc(outlen + 1);
+
+  firstgrouplen = (inlen - 1) % groupby + 1;
+  strncpy(out, in, firstgrouplen);
+  in += firstgrouplen;
+  out += firstgrouplen;
+
+  while (*in != '\0') {
+    strncpy(out, sep, seplen);
+    out += seplen;
+    strncpy(out, in, groupby);
+    in += groupby;
+    out += groupby;
+  }
+
+  g_assert(out - ret == outlen);
+  *out = '\0';
+  return ret;
+}
+
+/**
+ * terminal_util_number_info:
+ * @str: a dec or hex number as string
+ *
+ * Returns: (transfer full): Useful info about @str, or %NULL if it's too large
+ */
+char *
+terminal_util_number_info (const char *str)
+{
+  gs_free char *decstr = NULL;
+  gs_free char *hextmp = NULL;
+  gs_free char *hexstr = NULL;
+  gs_free char *magnitudestr = NULL;
+  unsigned long long num;
+  gboolean exact = TRUE;
+  gboolean hex = FALSE;
+  const char *thousep;
+
+  errno = 0;
+  /* Deliberately not handle octal */
+  if (str[1] == 'x' || str[1] == 'X') {
+    num = strtoull(str + 2, NULL, 16);
+    hex = TRUE;
+  } else {
+    num = strtoull(str, NULL, 10);
+  }
+  if (errno) {
+    return NULL;
+  }
+
+  /* No use in dec-hex conversion for so small numbers */
+  if (num < 10) {
+    return NULL;
+  }
+
+  /* Group the decimal digits */
+  thousep = nl_langinfo(THOUSEP);
+  if (thousep[0] != '\0') {
+    /* If thousep is nonempty, use printf's magic which can handle
+       more complex separating logics, e.g. 2+2+2+3 for some locales */
+    decstr = g_strdup_printf("%'llu", num);
+  } else {
+    /* If, however, thousep is empty, override it with a space so that we
+       do always group the digits (that's the whole point of this feature;
+       the choice of space guarantees not conflicting with the decimal separator) */
+    gs_free char *tmp = g_strdup_printf("%llu", num);
+    thousep = " ";
+    decstr = add_separators(tmp, thousep, 3);
+  }
+
+  /* Group the hex digits by 4 using the same nonempty separator */
+  hextmp = g_strdup_printf("%llx", num);
+  hexstr = add_separators(hextmp, thousep, 4);
+
+  /* Find out the human-readable magnitude, e.g. 15.99 Mi */
+  if (num >= 1024) {
+    int power = 0;
+    while (num >= 1024 * 1024) {
+      power++;
+      if (num % 1024 != 0)
+        exact = FALSE;
+      num /= 1024;
+    }
+    /* Show 2 fraction digits, always rounding downwards. Printf rounds floats to the nearest representable 
value,
+       so do the calculation with integers until we get 100-fold the desired value, and then switch to 
float. */
+    if (100 * num % 1024 != 0)
+      exact = FALSE;
+    num = 100 * num / 1024;
+    magnitudestr = g_strdup_printf(" %s %.2f %ci", exact ? "=" : "≈", (double) num / 100, "KMGTPE"[power]);
+  } else {
+    magnitudestr = g_strdup("");
+  }
+
+  return g_strdup_printf(hex ? "0x%2$s = %1$s%3$s" : "%s = 0x%s%s", decstr, hexstr, magnitudestr);
+}
diff --git a/src/terminal-util.h b/src/terminal-util.h
index b3f9df1..b8447f7 100644
--- a/src/terminal-util.h
+++ b/src/terminal-util.h
@@ -89,6 +89,8 @@ void terminal_g_settings_set_rgba_palette (GSettings      *settings,
 
 void terminal_util_bind_mnemonic_label_sensitivity (GtkWidget *widget);
 
+char *terminal_util_number_info (const char *str);
+
 G_END_DECLS
 
 #endif /* TERMINAL_UTIL_H */
diff --git a/src/terminal-window.c b/src/terminal-window.c
index e7e7e9c..b3c2649 100644
--- a/src/terminal-window.c
+++ b/src/terminal-window.c
@@ -2051,7 +2051,7 @@ popup_clipboard_targets_received_cb (GtkClipboard *clipboard,
   TerminalScreen *screen = info->screen;
   GtkWidget *popup_menu;
   GtkAction *action;
-  gboolean can_paste, can_paste_uris, show_link, show_email_link, show_call_link;
+  gboolean can_paste, can_paste_uris, show_link, show_email_link, show_call_link, show_number_info;
 
   window = terminal_screen_popup_info_ref_window (info);
   if (window == NULL ||
@@ -2072,6 +2072,7 @@ popup_clipboard_targets_received_cb (GtkClipboard *clipboard,
   show_link = info->url != NULL && (info->url_flavor == FLAVOR_AS_IS || info->url_flavor == 
FLAVOR_DEFAULT_TO_HTTP);
   show_email_link = info->url != NULL && info->url_flavor == FLAVOR_EMAIL;
   show_call_link = info->url != NULL && info->url_flavor == FLAVOR_VOIP_CALL;
+  show_number_info = info->number_info != NULL;
 
   action = gtk_action_group_get_action (priv->action_group, "PopupSendEmail");
   gtk_action_set_visible (action, show_email_link);
@@ -2085,6 +2086,10 @@ popup_clipboard_targets_received_cb (GtkClipboard *clipboard,
   gtk_action_set_visible (action, show_link);
   action = gtk_action_group_get_action (priv->action_group, "PopupCopyLinkAddress");
   gtk_action_set_visible (action, show_link);
+  action = gtk_action_group_get_action (priv->action_group, "PopupNumberInfo");
+  gtk_action_set_label (action, info->number_info);
+  gtk_action_set_sensitive (action, FALSE);
+  gtk_action_set_visible (action, show_number_info);
 
   action = gtk_action_group_get_action (priv->action_group, "PopupCopy");
   gtk_action_set_sensitive (action, vte_terminal_get_has_selection (VTE_TERMINAL (screen)));
@@ -2604,6 +2609,9 @@ terminal_window_init (TerminalWindow *window)
       { "PopupCopyLinkAddress", NULL, N_("_Copy Link Address"), NULL,
         NULL,
         G_CALLBACK (popup_copy_url_callback) },
+      { "PopupNumberInfo", NULL, "", NULL,
+        NULL,
+        NULL },
       { "PopupTerminalProfiles", NULL, N_("P_rofiles") },
       { "PopupCopy", "edit-copy", N_("Copy"), "",
         NULL,
diff --git a/src/terminal.xml b/src/terminal.xml
index d2065b3..cbf21cc 100644
--- a/src/terminal.xml
+++ b/src/terminal.xml
@@ -82,6 +82,7 @@
     <menuitem action="PopupCopyCallAddress" />
     <menuitem action="PopupOpenLink" />
     <menuitem action="PopupCopyLinkAddress" />
+    <menuitem action="PopupNumberInfo" />
     <separator />
     <menuitem action="PopupNewTerminal" />
     <separator />


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