[gtk/ci-file-filters] GtkFileFilter: Add suffix matches




commit 91daf657ec7526e8825c9cfd2855351fbfed2386
Author: Matthias Clasen <mclasen redhat com>
Date:   Thu Jun 3 23:55:48 2021 -0400

    GtkFileFilter: Add suffix matches
    
    This is less flexible than a glob pattern, but it is
    explicitly case-insensitive, to match the behavior
    on Windows.
    
    Fixes: #3705

 gtk/gtkfilefilter.c | 112 +++++++++++++++++++++++++++++++++++++++++++---------
 gtk/gtkfilefilter.h |   6 +++
 tests/testgtk.c     |   4 +-
 3 files changed, 102 insertions(+), 20 deletions(-)
---
diff --git a/gtk/gtkfilefilter.c b/gtk/gtkfilefilter.c
index 99a82d6845..74aa227d75 100644
--- a/gtk/gtkfilefilter.c
+++ b/gtk/gtkfilefilter.c
@@ -23,8 +23,8 @@
  *
  * `GtkFileFilter` can be used to restrict the files being shown in a
  * `GtkFileChooser`. Files can be filtered based on their name (with
- * [method@Gtk.FileFilter.add_pattern]) or on their mime type (with
- * [method@Gtk.FileFilter.add_mime_type]).
+ * [method@Gtk.FileFilter.add_pattern] or [method@Gtk.FileFilter.add_suffix])
+ * or on their mime type (with [method@Gtk.FileFilter.add_mime_type]).
  *
  * Filtering by mime types handles aliasing and subclassing of mime
  * types; e.g. a filter for text/plain also matches a file with mime
@@ -40,11 +40,13 @@
  * # GtkFileFilter as GtkBuildable
  *
  * The `GtkFileFilter` implementation of the `GtkBuildable` interface
- * supports adding rules using the <mime-types> and <patterns>
- * elements and listing the rules within. Specifying a <mime-type>
- * or <pattern> has the same effect as as calling
+ * supports adding rules using the `<mime-types>` and `<patterns>` and
+ * `<suffixes>` elements and listing the rules within. Specifying a
+ * `<mime-type>` or `<pattern>` or `<suffix>` has the same effect as
+ * as calling
  * [method@Gtk.FileFilter.add_mime_type] or
- * [method@Gtk.FileFilter.add_pattern].
+ * [method@Gtk.FileFilter.add_pattern] or
+ * [method@Gtk.FileFilter.add_suffix].
  *
  * An example of a UI definition fragment specifying `GtkFileFilter`
  * rules:
@@ -57,8 +59,10 @@
  *   </mime-types>
  *   <patterns>
  *     <pattern>*.txt</pattern>
- *     <pattern>*.png</pattern>
  *   </patterns>
+ *   <suffixes>
+ *     <suffix>png</suffix>
+ *   </suffixes>
  * </object>
  * ```
  */
@@ -85,6 +89,7 @@ typedef struct _FilterRule FilterRule;
 typedef enum {
   FILTER_RULE_PATTERN,
   FILTER_RULE_MIME_TYPE,
+  FILTER_RULE_SUFFIX,
   FILTER_RULE_PIXBUF_FORMATS
 } FilterRuleType;
 
@@ -182,6 +187,7 @@ filter_rule_free (FilterRule *rule)
   switch (rule->type)
     {
     case FILTER_RULE_PATTERN:
+    case FILTER_RULE_SUFFIX:
       g_free (rule->u.pattern);
       break;
     case FILTER_RULE_MIME_TYPE:
@@ -245,7 +251,8 @@ gtk_file_filter_class_init (GtkFileFilterClass *class)
 typedef enum
 {
   PARSE_MIME_TYPES,
-  PARSE_PATTERNS
+  PARSE_PATTERNS,
+  PARSE_SUFFIXES
 } ParserType;
 
 typedef struct
@@ -267,14 +274,6 @@ parser_start_element (GtkBuildableParseContext  *context,
 {
   SubParserData *data = (SubParserData*)user_data;
 
-  if (!g_markup_collect_attributes (element_name, names, values, error,
-                                    G_MARKUP_COLLECT_INVALID, NULL, NULL,
-                                    G_MARKUP_COLLECT_INVALID))
-    {
-      _gtk_builder_prefix_error (data->builder, context, error);
-      return;
-    }
-
   if (strcmp (element_name, "mime-types") == 0 ||
       strcmp (element_name, "patterns") == 0)
     {
@@ -293,6 +292,13 @@ parser_start_element (GtkBuildableParseContext  *context,
       if (!_gtk_builder_check_parent (data->builder, context, "patterns", error))
         return;
 
+      data->parsing = TRUE;
+    }
+  else if (strcmp (element_name, "suffix") == 0)
+    {
+      if (!_gtk_builder_check_parent (data->builder, context, "suffixes", error))
+        return;
+
       data->parsing = TRUE;
     }
   else
@@ -334,6 +340,9 @@ parser_end_element (GtkBuildableParseContext  *context,
         case PARSE_PATTERNS:
           gtk_file_filter_add_pattern (data->filter, data->string->str);
           break;
+        case PARSE_SUFFIXES:
+          gtk_file_filter_add_suffix (data->filter, data->string->str);
+          break;
         default:
           break;
         }
@@ -379,6 +388,17 @@ gtk_file_filter_buildable_custom_tag_start (GtkBuildable       *buildable,
       data->filter = GTK_FILE_FILTER (buildable);
       data->builder = builder;
 
+      *parser = sub_parser;
+      *parser_data = data;
+    }
+  else if (strcmp (tagname, "suffixes") == 0)
+    {
+      data = g_slice_new0 (SubParserData);
+      data->string = g_string_new ("");
+      data->type = PARSE_SUFFIXES;
+      data->filter = GTK_FILE_FILTER (buildable);
+      data->builder = builder;
+
       *parser = sub_parser;
       *parser_data = data;
     }
@@ -422,7 +442,8 @@ gtk_file_filter_buildable_init (GtkBuildableIface *iface)
  * Such a filter doesn’t accept any files, so is not
  * particularly useful until you add rules with
  * [method@Gtk.FileFilter.add_mime_type],
- * [method@Gtk.FileFilter.add_pattern], or
+ * [method@Gtk.FileFilter.add_pattern],
+ * [method@Gtk.FileFilter.add_suffix] or
  * [method@Gtk.FileFilter.add_pixbuf_formats].
  *
  * To create a filter that accepts any file, use:
@@ -543,6 +564,10 @@ gtk_file_filter_add_mime_type (GtkFileFilter *filter,
  * @pattern: a shell style glob
  *
  * Adds a rule allowing a shell style glob to a filter.
+ *
+ * Note that it depends on the platform whether pattern
+ * matching ignores case or not. On Windows, it does, on
+ * other platforms, it doesn't.
  */
 void
 gtk_file_filter_add_pattern (GtkFileFilter *filter,
@@ -561,6 +586,36 @@ gtk_file_filter_add_pattern (GtkFileFilter *filter,
   file_filter_add_rule (filter, rule);
 }
 
+/**
+ * gtk_file_filter_add_suffix:
+ * @filter: a `GtkFileFilter`
+ * @suffix: filename suffix to match
+ *
+ * Adds a suffix match rule to a filter.
+ *
+ * This is similar to adding a match for the pattern
+ * "*.@suffix".
+ *
+ * In contrast to pattern matches, suffix matches
+ * are *always* case-insensitive.
+ */
+void
+gtk_file_filter_add_suffix (GtkFileFilter *filter,
+                            const char    *suffix)
+{
+  FilterRule *rule;
+
+  g_return_if_fail (GTK_IS_FILE_FILTER (filter));
+  g_return_if_fail (suffix != NULL);
+
+  rule = g_slice_new (FilterRule);
+  rule->type = FILTER_RULE_SUFFIX;
+  rule->u.pattern = g_strconcat ("*.", suffix, NULL);
+
+  file_filter_add_attribute (filter, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME);
+  file_filter_add_rule (filter, rule);
+}
+
 /**
  * gtk_file_filter_add_pixbuf_formats:
  * @filter: a `GtkFileFilter`
@@ -657,6 +712,7 @@ NSArray * _gtk_file_filter_get_as_pattern_nsstrings (GtkFileFilter *filter)
           break;
 
         case FILTER_RULE_PATTERN:
+        case FILTER_RULE_SUFFIX:
           {
             // patterns will need to be stripped of their leading *.
             GString *pattern = g_string_new (rule->u.pattern);
@@ -725,6 +781,10 @@ _gtk_file_filter_get_as_patterns (GtkFileFilter *filter)
           break;
 
         case FILTER_RULE_PATTERN:
+        case FILTER_RULE_SUFFIX:
+          /* Note: we don't make the suffix pattern explicitly
+           * case-insensitive, since this is only used on Windows
+           */
           g_ptr_array_add (array, g_strdup (rule->u.pattern));
           break;
 
@@ -794,9 +854,14 @@ gtk_file_filter_match (GtkFilter *filter,
   for (tmp_list = file_filter->rules; tmp_list; tmp_list = tmp_list->next)
     {
       FilterRule *rule = tmp_list->data;
+      gboolean ignore_case = FALSE;
 
       switch (rule->type)
         {
+        case FILTER_RULE_SUFFIX:
+          ignore_case = TRUE;
+          G_GNUC_FALLTHROUGH;
+
         case FILTER_RULE_PATTERN:
           {
             const char *display_name;
@@ -804,7 +869,7 @@ gtk_file_filter_match (GtkFilter *filter,
             display_name = g_file_info_get_display_name (info);
             if (display_name)
               {
-                if (_gtk_fnmatch (rule->u.pattern, display_name, FALSE, FALSE))
+                if (_gtk_fnmatch (rule->u.pattern, display_name, FALSE, ignore_case))
                   return TRUE;
               }
           }
@@ -863,6 +928,17 @@ gtk_file_filter_to_gvariant (GtkFileFilter *filter)
           g_variant_builder_add (&builder, "(us)", 0, rule->u.pattern);
           break;
 
+        case FILTER_RULE_SUFFIX:
+          {
+            /* Tweak the glob, since the filechooser portal has no api
+             * for case-insensitive globs
+             */
+            char *pattern = _gtk_make_ci_glob_pattern (rule->u.pattern);
+            g_variant_builder_add (&builder, "(us)", 0, pattern);
+            g_free (pattern);
+          }
+          break;
+
         case FILTER_RULE_MIME_TYPE:
         case FILTER_RULE_PIXBUF_FORMATS:
           for (i = 0; rule->u.content_types[i]; i++)
diff --git a/gtk/gtkfilefilter.h b/gtk/gtkfilefilter.h
index 5232dfb0ae..e14d70d823 100644
--- a/gtk/gtkfilefilter.h
+++ b/gtk/gtkfilefilter.h
@@ -48,9 +48,15 @@ const char *    gtk_file_filter_get_name           (GtkFileFilter *filter);
 GDK_AVAILABLE_IN_ALL
 void            gtk_file_filter_add_mime_type      (GtkFileFilter *filter,
                                                     const char    *mime_type);
+
 GDK_AVAILABLE_IN_ALL
 void            gtk_file_filter_add_pattern        (GtkFileFilter *filter,
                                                     const char    *pattern);
+
+GDK_AVAILABLE_IN_4_4
+void            gtk_file_filter_add_suffix         (GtkFileFilter *filter,
+                                                    const char    *suffix);
+
 GDK_AVAILABLE_IN_ALL
 void            gtk_file_filter_add_pixbuf_formats (GtkFileFilter *filter);
 
diff --git a/tests/testgtk.c b/tests/testgtk.c
index 077d16136b..1b8cbf4cbe 100644
--- a/tests/testgtk.c
+++ b/tests/testgtk.c
@@ -5576,8 +5576,8 @@ native_filter_changed (GtkWidget *combo,
     case 1:   /* pattern */
       filter = gtk_file_filter_new ();
       gtk_file_filter_set_name (filter, "Text");
-      gtk_file_filter_add_pattern (filter, "*.doc");
-      gtk_file_filter_add_pattern (filter, "*.txt");
+      gtk_file_filter_add_suffix (filter, "doc");
+      gtk_file_filter_add_suffix (filter, "txt");
       gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (native), filter);
       g_object_unref (filter);
 


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