[gimp/wip/Jehan/layers-dockable-refresh: 50/126] app: add layer selection by regular expression.




commit 58e5f2b0924905ae54cad5bc5b0990267246e5ec
Author: Jehan <jehan girinstud io>
Date:   Sat Feb 6 16:10:22 2021 +0100

    app: add layer selection by regular expression.
    
    I am unsure if regular expression is the right choice as it may
    obviously be a bit too technical. I hesitated with glob-type match for a
    while. We'll see!

 app/core/gimpimage.c            | 54 ++++++++++++++++++++++++
 app/core/gimpimage.h            |  3 ++
 app/widgets/gimplayertreeview.c | 91 +++++++++++++++++++++++++++++++++++++++--
 3 files changed, 145 insertions(+), 3 deletions(-)
---
diff --git a/app/core/gimpimage.c b/app/core/gimpimage.c
index 07cca851a1..d2795c21f6 100644
--- a/app/core/gimpimage.c
+++ b/app/core/gimpimage.c
@@ -5464,6 +5464,60 @@ gimp_image_select_linked_layers (GimpImage   *image,
   gimp_image_set_selected_layers (image, linked);
 }
 
+/*
+ * @gimp_image_select_layers_by_regexp:
+ * @image:
+ * @pattern:
+ * @error:
+ *
+ * Replace currently selected layers in @image with the layers whose
+ * names match with the @pattern regular expression.
+ *
+ * Returns: %TRUE if some layers matched @pattern (even if it turned out
+ *          selected layers stay the same), %FALSE otherwise or if
+ *          @pattern is an invalid regular expression (in which case,
+ *          @error will be filled with the appropriate error).
+ */
+gboolean
+gimp_image_select_layers_by_regexp (GimpImage    *image,
+                                    const gchar  *pattern,
+                                    GError      **error)
+{
+  GList    *layers;
+  GList    *match = NULL;
+  GList    *iter;
+  GRegex   *regex;
+  gboolean  matched;
+
+  g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
+  g_return_val_if_fail (pattern != NULL, FALSE);
+
+  regex = g_regex_new (pattern, 0, 0, error);
+
+  if (regex == NULL)
+    {
+      return FALSE;
+    }
+
+  layers = gimp_image_get_layer_list (image);
+
+  for (iter = layers; iter; iter = iter->next)
+    {
+      if (g_regex_match (regex,
+                         gimp_object_get_name (iter->data),
+                         0, NULL))
+        match = g_list_prepend (match, iter->data);
+    }
+
+  gimp_image_set_selected_layers (image, match);
+  matched = (match != NULL);
+
+  g_list_free (match);
+  g_regex_unref (regex);
+
+  return matched;
+}
+
 /*
  * @gimp_image_add_linked_layers:
  * @image:
diff --git a/app/core/gimpimage.h b/app/core/gimpimage.h
index 07eabcdcd3..d45e9bfeb5 100644
--- a/app/core/gimpimage.h
+++ b/app/core/gimpimage.h
@@ -459,6 +459,9 @@ gboolean        gimp_image_unlink_layers         (GimpImage          *image,
                                                   const gchar        *link_name);
 void            gimp_image_select_linked_layers  (GimpImage          *image,
                                                   const gchar        *link_name);
+gboolean      gimp_image_select_layers_by_regexp (GimpImage          *image,
+                                                  const gchar        *pattern,
+                                                  GError            **error);
 void            gimp_image_add_linked_layers     (GimpImage          *image,
                                                   const gchar        *link_name);
 void            gimp_image_remove_linked_layers  (GimpImage          *image,
diff --git a/app/widgets/gimplayertreeview.c b/app/widgets/gimplayertreeview.c
index 5ccbb65b59..650ad50bfd 100644
--- a/app/widgets/gimplayertreeview.c
+++ b/app/widgets/gimplayertreeview.c
@@ -78,6 +78,7 @@ struct _GimpLayerTreeViewPrivate
   GtkWidget       *link_button;
   GtkWidget       *link_list;
   GtkWidget       *link_entry;
+  GtkWidget       *link_regexp_entry;
 
   gint             model_column_mask;
   gint             model_column_mask_visible;
@@ -151,6 +152,9 @@ static void       gimp_layer_tree_view_layer_links_changed        (GimpImage
 static gboolean   gimp_layer_tree_view_link_clicked               (GtkWidget                  *box,
                                                                    GdkEvent                   *event,
                                                                    GimpLayerTreeView          *view);
+static void       gimp_layer_tree_view_regexp_modified            (GtkEntry                   *entry,
+                                                                   const GParamSpec           *pspec,
+                                                                   GimpLayerTreeView          *view);
 static void       gimp_layer_tree_view_new_link_clicked           (GtkButton                  *button,
                                                                    GimpLayerTreeView          *view);
 static gboolean   gimp_layer_tree_view_unlink_clicked             (GtkWidget                  *widget,
@@ -474,11 +478,36 @@ gimp_layer_tree_view_constructed (GObject *object)
 
   grid = gtk_grid_new ();
 
+  /* Link popover: regexp search. */
+  layer_view->priv->link_regexp_entry = gtk_entry_new ();
+  gtk_entry_set_icon_from_icon_name (GTK_ENTRY (layer_view->priv->link_regexp_entry),
+                                     GTK_ENTRY_ICON_SECONDARY,
+                                     "system-search");
+  gtk_entry_set_placeholder_text (GTK_ENTRY (layer_view->priv->link_regexp_entry),
+                                  _("Regular Expression search"));
+  gtk_grid_attach (GTK_GRID (grid),
+                   layer_view->priv->link_regexp_entry,
+                   0, 0, 2, 1);
+  gtk_widget_show (layer_view->priv->link_regexp_entry);
+  g_signal_connect (layer_view->priv->link_regexp_entry,
+                    "notify::text",
+                    G_CALLBACK (gimp_layer_tree_view_regexp_modified),
+                    layer_view);
+
+  /* Enter on regexp entry closes the popover dialog. This allows to use
+   * the feature for quick name-based layer selection, not for actually
+   * storing the selected items.
+   */
+  g_signal_connect_swapped (layer_view->priv->link_regexp_entry,
+                            "activate",
+                            G_CALLBACK (gtk_popover_popdown),
+                            popover);
+
   /* Link popover: existing links. */
   layer_view->priv->link_list = gtk_list_box_new ();
   gtk_grid_attach (GTK_GRID (grid),
                    layer_view->priv->link_list,
-                   0, 0, 2, 1);
+                   0, 1, 2, 1);
   gtk_widget_show (layer_view->priv->link_list);
 
   gtk_list_box_set_activate_on_single_click (GTK_LIST_BOX (layer_view->priv->link_list),
@@ -491,13 +520,13 @@ gimp_layer_tree_view_constructed (GObject *object)
                                   _("Named Selection"));
   gtk_grid_attach (GTK_GRID (grid),
                    layer_view->priv->link_entry,
-                   0, 1, 1, 1);
+                   0, 2, 1, 1);
   gtk_widget_show (layer_view->priv->link_entry);
 
   button = gtk_button_new_from_icon_name (GIMP_ICON_DOCUMENT_SAVE, button_size);
   gtk_grid_attach (GTK_GRID (grid),
                    button,
-                   1, 1, 1, 1);
+                   1, 2, 1, 1);
   g_signal_connect (button,
                     "clicked",
                     G_CALLBACK (gimp_layer_tree_view_new_link_clicked),
@@ -1221,6 +1250,62 @@ gimp_layer_tree_view_link_clicked (GtkWidget         *box,
   return FALSE;
 }
 
+static void
+gimp_layer_tree_view_regexp_modified (GtkEntry         *entry,
+                                      const GParamSpec *pspec,
+                                      GimpLayerTreeView *view)
+{
+  GimpImage   *image;
+  const gchar *pattern;
+
+  gtk_entry_set_attributes (GTK_ENTRY (view->priv->link_regexp_entry),
+                            NULL);
+  gtk_widget_set_tooltip_text (view->priv->link_regexp_entry,
+                               _("Select layers by regular expressions"));
+  image = gimp_item_tree_view_get_image (GIMP_ITEM_TREE_VIEW (view));
+
+  if (! image)
+    return;
+
+  pattern = gtk_entry_get_text (GTK_ENTRY (view->priv->link_regexp_entry));
+  if (pattern && strlen (pattern) > 0)
+    {
+      GError *error = NULL;
+
+      gtk_entry_set_text (GTK_ENTRY (view->priv->link_entry), "");
+      gtk_widget_set_sensitive (view->priv->link_entry, FALSE);
+      if (! gimp_image_select_layers_by_regexp (image, pattern, &error))
+        {
+          if (error)
+            {
+              /* Invalid regular expression. */
+              PangoAttrList *attrs = pango_attr_list_new ();
+              gchar         *tooltip;
+
+              pango_attr_list_insert (attrs, pango_attr_strikethrough_new (TRUE));
+              tooltip = g_strdup_printf (_("Invalid regular expression: %s\n"),
+                                         error->message);
+              gtk_widget_set_tooltip_text (view->priv->link_regexp_entry,
+                                           tooltip);
+              gtk_entry_set_attributes (GTK_ENTRY (view->priv->link_regexp_entry),
+                                        attrs);
+              g_free (tooltip);
+              g_error_free (error);
+              pango_attr_list_unref (attrs);
+            }
+          else
+            {
+              /* Pattern does not match any results. */
+              gimp_widget_blink (view->priv->link_regexp_entry);
+            }
+        }
+    }
+  else
+    {
+      gtk_widget_set_sensitive (view->priv->link_entry, TRUE);
+    }
+}
+
 static void
 gimp_layer_tree_view_new_link_clicked (GtkButton         *button,
                                        GimpLayerTreeView *view)


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