]> Pileus Git - ~andy/gtk/commitdiff
Support subclasses in RC files. (#142417, Todd Berman, patch based on a
authorMatthias Clasen <mclasen@redhat.com>
Thu, 23 Mar 2006 23:21:30 +0000 (23:21 +0000)
committerMatthias Clasen <matthiasc@src.gnome.org>
Thu, 23 Mar 2006 23:21:30 +0000 (23:21 +0000)
2006-03-23  Matthias Clasen  <mclasen@redhat.com>

Support subclasses in RC files.  (#142417, Todd Berman, patch
based on a patch by Benjamin Berg)

* gtk/gtkrc.h:
* gtk/gtkrc.c: Support <classname> elements in widget_class paths
in rc files which match any classes derived from named class.

(_gtk_rc_init): Use the new syntax in the default rc string.

* gtk/gtkbindings.c: Support the new syntax for bindings too.

* tests/testrc.c: Tests for widget_class path matching

ChangeLog
ChangeLog.pre-2-10
gtk/gtkbindings.c
gtk/gtkrc.c
gtk/gtkrc.h
tests/testrc.c [new file with mode: 0644]

index 7768fa11b491b224f0f067740e9a9688709ace66..ee7b54ed23d73e39f1e4c1d018beda4a9ee97843 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+2006-03-23  Matthias Clasen  <mclasen@redhat.com>
+
+       Support subclasses in RC files.  (#142417, Todd Berman, patch
+       based on a patch by Benjamin Berg)
+       
+       * gtk/gtkrc.h: 
+       * gtk/gtkrc.c: Support <classname> elements in widget_class paths 
+       in rc files which match any classes derived from named class.
+
+       (_gtk_rc_init): Use the new syntax in the default rc string.
+
+       * gtk/gtkbindings.c: Support the new syntax for bindings too.
+       
+       * tests/testrc.c: Tests for widget_class path matching
+
 2006-03-23  Carlos Garnacho  <carlosg@gnome.org>
 
        * gtk/gtkdnd.c (gtk_drag_dest_motion): make sure that gdk_drag_event()
index 7768fa11b491b224f0f067740e9a9688709ace66..ee7b54ed23d73e39f1e4c1d018beda4a9ee97843 100644 (file)
@@ -1,3 +1,18 @@
+2006-03-23  Matthias Clasen  <mclasen@redhat.com>
+
+       Support subclasses in RC files.  (#142417, Todd Berman, patch
+       based on a patch by Benjamin Berg)
+       
+       * gtk/gtkrc.h: 
+       * gtk/gtkrc.c: Support <classname> elements in widget_class paths 
+       in rc files which match any classes derived from named class.
+
+       (_gtk_rc_init): Use the new syntax in the default rc string.
+
+       * gtk/gtkbindings.c: Support the new syntax for bindings too.
+       
+       * tests/testrc.c: Tests for widget_class path matching
+
 2006-03-23  Carlos Garnacho  <carlosg@gnome.org>
 
        * gtk/gtkdnd.c (gtk_drag_dest_motion): make sure that gdk_drag_event()
index 9e2c46294756346f89c0f13631b03d9fed742e04..fcba628870bb15d3075b694a19f563529577e74f 100644 (file)
 
 /* --- structures --- */
 typedef struct {
+  GtkPathType   type;
   GPatternSpec *pspec;
-  gpointer user_data;
-  guint seq_id;
+  GSList       *path;
+  gpointer      user_data;
+  guint         seq_id;
 } PatternSpec;
 
 
@@ -59,6 +61,15 @@ static GQuark                 key_id_class_binding_set = 0;
 
 
 /* --- functions --- */
+static void
+pattern_spec_free (PatternSpec *pspec)
+{
+  _gtk_rc_free_widget_class_path (pspec->path);
+  if (pspec->pspec)
+    g_pattern_spec_free (pspec->pspec);
+  g_free (pspec);
+}
+
 static GtkBindingSignal*
 binding_signal_new (const gchar *signal_name,
                    guint        n_args)
@@ -869,7 +880,18 @@ gtk_binding_set_add_path (GtkBindingSet         *binding_set,
     }
   
   pspec = g_new (PatternSpec, 1);
-  pspec->pspec = g_pattern_spec_new (path_pattern);
+  pspec->type = path_type;
+  if (path_type == GTK_PATH_WIDGET_CLASS)
+    {
+      pspec->pspec = NULL;
+      pspec->path = _gtk_rc_parse_widget_class_path (path_pattern);
+    }
+  else
+    {
+      pspec->pspec = g_pattern_spec_new (path_pattern);
+      pspec->path = NULL;
+    }
+    
   pspec->seq_id = priority << 28;
   pspec->user_data = binding_set;
   
@@ -885,8 +907,7 @@ gtk_binding_set_add_path (GtkBindingSet          *binding_set,
        {
          GtkPathPriorityType lprio = tmp_pspec->seq_id >> 28;
 
-         g_pattern_spec_free (pspec->pspec);
-         g_free (pspec);
+         pattern_spec_free (pspec);
          pspec = NULL;
          if (lprio < priority)
            {
@@ -907,25 +928,32 @@ static gboolean
 binding_match_activate (GSList          *pspec_list,
                        GtkObject       *object,
                        guint            path_length,
-                       const gchar     *path,
-                       const gchar     *path_reversed)
+                       gchar           *path,
+                       gchar           *path_reversed)
 {
   GSList *slist;
 
   for (slist = pspec_list; slist; slist = slist->next)
     {
       PatternSpec *pspec;
+      GtkBindingSet *binding_set;
 
+      binding_set = NULL;
       pspec = slist->data;
-      if (g_pattern_match (pspec->pspec, path_length, path, path_reversed))
-       {
-         GtkBindingSet *binding_set;
-
-         binding_set = pspec->user_data;
+      
+      if (pspec->type == GTK_PATH_WIDGET_CLASS)
+        {
+          if (_gtk_rc_match_widget_class (pspec->path, path_length, path, path_reversed))
+           binding_set = pspec->user_data;
+        }
+      else
+        {
+          if (g_pattern_match (pspec->pspec, path_length, path, path_reversed))
+           binding_set = pspec->user_data;
+        }
 
-         if (gtk_binding_entry_activate (binding_set->current, object))
-           return TRUE;
-       }
+      if (binding_set && gtk_binding_entry_activate (binding_set->current, object))
+        return TRUE;
     }
 
   return FALSE;
@@ -1052,14 +1080,15 @@ gtk_bindings_activate_list (GtkObject *object,
       while (class_type && !handled)
        {
          guint path_length;
-         const gchar *path;
+         gchar *path;
          gchar *path_reversed;
          
-         path = g_type_name (class_type);
+         path = g_strdup (g_type_name (class_type));
          path_reversed = g_strdup (path);
          g_strreverse (path_reversed);
          path_length = strlen (path);
          handled = binding_match_activate (patterns, object, path_length, path, path_reversed);
+         g_free (path);
          g_free (path_reversed);
 
          class_type = g_type_parent (class_type);
@@ -1417,8 +1446,7 @@ free_pattern_specs (GSList *pattern_specs)
 
       pspec = slist->data;
 
-      g_pattern_spec_free (pspec->pspec);
-      g_free (pspec);
+      pattern_spec_free (pspec);
     }
 
   g_slist_free (pattern_specs);
index 0047dc5e0d79d4bbe2e65072740e9af66b5578c5..933192a4403de2f0758349dbb10c0a9e281f68a9 100644 (file)
@@ -66,21 +66,43 @@ typedef struct _GtkRcSet    GtkRcSet;
 typedef struct _GtkRcNode   GtkRcNode;
 typedef struct _GtkRcFile   GtkRcFile;
 
+enum 
+{
+  PATH_ELT_PSPEC,
+  PATH_ELT_UNRESOLVED,
+  PATH_ELT_TYPE
+};
+
+typedef struct
+{
+  gint type;
+  union 
+  {
+    GType         class_type;
+    gchar        *class_name;
+    GPatternSpec *pspec;
+  } elt;
+} PathElt;
+
 struct _GtkRcSet
 {
+  GtkPathType   type;
+
   GPatternSpec *pspec;
-  GtkRcStyle *rc_style;
-  gint priority;
+  GSList       *path;
+
+  GtkRcStyle   *rc_style;
+  gint          priority;
 };
 
 struct _GtkRcFile
 {
-  gboolean is_string;          /* If TRUE, name is a string to parse with gtk_rc_parse_string() */
   time_t mtime;
   gchar *name;
   gchar *canonical_name;
   gchar *directory;
-  guint reload;
+  guint  reload    : 1;
+  guint  is_string : 1;        /* If TRUE, name is a string to parse with gtk_rc_parse_string() */
 };
 
 #define GTK_RC_MAX_PIXMAP_PATHS 128
@@ -130,8 +152,8 @@ static GtkRcStyle* gtk_rc_style_find                 (GtkRcContext    *context,
 static GSList *    gtk_rc_styles_match               (GSList          *rc_styles,
                                                       GSList          *sets,
                                                       guint            path_length,
-                                                      const gchar     *path,
-                                                      const gchar     *path_reversed);
+                                                      gchar           *path,
+                                                      gchar           *path_reversed);
 static GtkStyle *  gtk_rc_style_to_style             (GtkRcContext    *context,
                                                      GtkRcStyle      *rc_style);
 static GtkStyle*   gtk_rc_init_style                 (GtkRcContext    *context,
@@ -213,6 +235,7 @@ static GtkRcStyle* gtk_rc_style_real_create_rc_style (GtkRcStyle      *rc_style)
 static GtkStyle*   gtk_rc_style_real_create_style    (GtkRcStyle      *rc_style);
 static gint       gtk_rc_properties_cmp             (gconstpointer    bsearch_node1,
                                                      gconstpointer    bsearch_node2);
+static void        gtk_rc_set_free                   (GtkRcSet        *rc_set);
 
 static gpointer parent_class = NULL;
 
@@ -737,19 +760,8 @@ _gtk_rc_init (void)
                       "\n"
                       "class \"GtkProgressBar\" style : gtk \"gtk-default-progress-bar-style\"\n"
                       "widget \"gtk-tooltips*\" style : gtk \"gtk-default-tooltips-style\"\n"
-                      "class \"GtkMenuItem\" style : gtk \"gtk-default-menu-item-style\"\n"
-                      "widget_class \"*.GtkMenuItem.*\" style : gtk \"gtk-default-menu-item-style\"\n"
-                      "widget_class \"*.GtkAccelMenuItem.*\" style : gtk \"gtk-default-menu-item-style\"\n"
-                      "widget_class \"*.GtkRadioMenuItem.*\" style : gtk \"gtk-default-menu-item-style\"\n"
-                      "widget_class \"*.GtkCheckMenuItem.*\" style : gtk \"gtk-default-menu-item-style\"\n"
-                      "widget_class \"*.GtkImageMenuItem.*\" style : gtk \"gtk-default-menu-item-style\"\n"
-                      "widget_class \"*.GtkSeparatorMenuItem.*\" style : gtk \"gtk-default-menu-item-style\"\n"
-                      "widget_class \"*.GtkCellViewMenuItem.*\" style : gtk \"gtk-default-menu-item-style\"\n"
-                      "widget_class \"*GtkMenuBar*GtkMenuItem\" style : gtk \"gtk-default-menu-bar-item-style\"\n"
-                      "widget_class \"*GtkMenuBar*GtkAccelMenuItem\" style : gtk \"gtk-default-menu-bar-item-style\"\n"
-                      "widget_class \"*GtkMenuBar*GtkRadioMenuItem\" style : gtk \"gtk-default-menu-bar-item-style\"\n"
-                      "widget_class \"*GtkMenuBar*GtkCheckMenuItem\" style : gtk \"gtk-default-menu-bar-item-style\"\n"
-                      "widget_class \"*GtkMenuBar*GtkImageMenuItem\" style : gtk \"gtk-default-menu-bar-item-style\"\n"
+                      "widget_class \"*<GtkMenuItem>*\" style : gtk \"gtk-default-menu-item-style\"\n"
+                      "widget_class \"*<GtkMenuBar>*<GtkMenuItem>\" style : gtk \"gtk-default-menu-bar-item-style\"\n"
       );
 }
   
@@ -1329,8 +1341,7 @@ gtk_rc_free_rc_sets (GSList *slist)
       GtkRcSet *rc_set;
 
       rc_set = slist->data;
-      g_pattern_spec_free (rc_set->pspec);
-      g_free (rc_set);
+      gtk_rc_set_free (rc_set);
 
       slist = slist->next;
     }
@@ -1623,8 +1634,8 @@ static GSList *
 gtk_rc_styles_match (GSList       *rc_styles,
                     GSList       *sets,
                     guint         path_length,
-                    const gchar  *path,
-                    const gchar  *path_reversed)
+                    gchar        *path,
+                    gchar        *path_reversed)
                     
 {
   GtkRcSet *rc_set;
@@ -1634,10 +1645,18 @@ gtk_rc_styles_match (GSList       *rc_styles,
       rc_set = sets->data;
       sets = sets->next;
 
-      if (g_pattern_match (rc_set->pspec, path_length, path, path_reversed))
-       rc_styles = g_slist_append (rc_styles, rc_set);
+      if (rc_set->type == GTK_PATH_WIDGET_CLASS)
+        {
+          if (_gtk_rc_match_widget_class (rc_set->path, path_length, path, path_reversed))
+           rc_styles = g_slist_append (rc_styles, rc_set);
+        }
+      else
+        {
+          if (g_pattern_match (rc_set->pspec, path_length, path, path_reversed))
+           rc_styles = g_slist_append (rc_styles, rc_set);
+       }
     }
-  
+
   return rc_styles;
 }
 
@@ -1746,16 +1765,17 @@ gtk_rc_get_style (GtkWidget *widget)
       type = G_TYPE_FROM_INSTANCE (widget);
       while (type)
        {
-         const gchar *path;
+         gchar *path;
           gchar *path_reversed;
          guint path_length;
 
-         path = g_type_name (type);
+         path = g_strdup (g_type_name (type));
          path_length = strlen (path);
          path_reversed = g_strdup (path);
          g_strreverse (path_reversed);
          
          rc_styles = gtk_rc_styles_match (rc_styles, context->rc_sets_class, path_length, path, path_reversed);
+         g_free (path);
          g_free (path_reversed);
       
          type = g_type_parent (type);
@@ -1832,27 +1852,33 @@ gtk_rc_get_style_by_paths (GtkSettings *settings,
 
   if (widget_path && context->rc_sets_widget)
     {
+      gchar *path;
       gchar *path_reversed;
       guint path_length;
 
       path_length = strlen (widget_path);
+      path = g_strdup (widget_path);
       path_reversed = g_strdup (widget_path);
       g_strreverse (path_reversed);
 
-      rc_styles = gtk_rc_styles_match (rc_styles, context->rc_sets_widget, path_length, widget_path, path_reversed);
+      rc_styles = gtk_rc_styles_match (rc_styles, context->rc_sets_widget, path_length, path, path_reversed);
+      g_free (path);
       g_free (path_reversed);
     }
   
   if (class_path && context->rc_sets_widget_class)
     {
+      gchar *path;
       gchar *path_reversed;
       guint path_length;
 
+      path = g_strdup (class_path);
       path_length = strlen (class_path);
       path_reversed = g_strdup (class_path);
       g_strreverse (path_reversed);
 
-      rc_styles = gtk_rc_styles_match (rc_styles, context->rc_sets_widget_class, path_length, class_path, path_reversed);
+      rc_styles = gtk_rc_styles_match (rc_styles, context->rc_sets_widget_class, path_length, path, path_reversed);
+      g_free (path);
       g_free (path_reversed);
     }
 
@@ -1860,16 +1886,17 @@ gtk_rc_get_style_by_paths (GtkSettings *settings,
     {
       while (type)
        {
-         const gchar *path;
+         gchar *path;
           gchar *path_reversed;
          guint path_length;
 
-         path = g_type_name (type);
+         path = g_strdup (g_type_name (type));
          path_length = strlen (path);
          path_reversed = g_strdup (path);
          g_strreverse (path_reversed);
          
          rc_styles = gtk_rc_styles_match (rc_styles, context->rc_sets_class, path_length, path, path_reversed);
+         g_free (path);
          g_free (path_reversed);
       
          type = g_type_parent (type);
@@ -1887,7 +1914,8 @@ gtk_rc_get_style_by_paths (GtkSettings *settings,
 static GSList *
 gtk_rc_add_rc_sets (GSList      *slist,
                    GtkRcStyle  *rc_style,
-                   const gchar *pattern)
+                   const gchar *pattern,
+                   GtkPathType  path_type)
 {
   GtkRcStyle *new_style;
   GtkRcSet *rc_set;
@@ -1903,7 +1931,19 @@ gtk_rc_add_rc_sets (GSList      *slist,
     new_style->bg_pixmap_name[i] = g_strdup (rc_style->bg_pixmap_name[i]);
   
   rc_set = g_new (GtkRcSet, 1);
-  rc_set->pspec = g_pattern_spec_new (pattern);
+  rc_set->type = path_type;
+  
+  if (path_type == GTK_PATH_WIDGET_CLASS)
+    {
+      rc_set->pspec = NULL;
+      rc_set->path = _gtk_rc_parse_widget_class_path (pattern);
+    }
+  else
+    {
+      rc_set->pspec = g_pattern_spec_new (pattern);
+      rc_set->path = NULL;
+    }
+  
   rc_set->rc_style = rc_style;
   
   return g_slist_prepend (slist, rc_set);
@@ -1920,7 +1960,7 @@ gtk_rc_add_widget_name_style (GtkRcStyle  *rc_style,
 
   context = gtk_rc_context_get (gtk_settings_get_default ());
   
-  context->rc_sets_widget = gtk_rc_add_rc_sets (context->rc_sets_widget, rc_style, pattern);
+  context->rc_sets_widget = gtk_rc_add_rc_sets (context->rc_sets_widget, rc_style, pattern, GTK_PATH_WIDGET);
 }
 
 void
@@ -1934,7 +1974,7 @@ gtk_rc_add_widget_class_style (GtkRcStyle  *rc_style,
 
   context = gtk_rc_context_get (gtk_settings_get_default ());
   
-  context->rc_sets_widget_class = gtk_rc_add_rc_sets (context->rc_sets_widget_class, rc_style, pattern);
+  context->rc_sets_widget_class = gtk_rc_add_rc_sets (context->rc_sets_widget_class, rc_style, pattern, GTK_PATH_WIDGET_CLASS);
 }
 
 void
@@ -1948,7 +1988,7 @@ gtk_rc_add_class_style (GtkRcStyle  *rc_style,
 
   context = gtk_rc_context_get (gtk_settings_get_default ());
   
-  context->rc_sets_class = gtk_rc_add_rc_sets (context->rc_sets_class, rc_style, pattern);
+  context->rc_sets_class = gtk_rc_add_rc_sets (context->rc_sets_class, rc_style, pattern, GTK_PATH_CLASS);
 }
 
 GScanner*
@@ -3870,7 +3910,19 @@ gtk_rc_parse_path_pattern (GtkRcContext *context,
        }
 
       rc_set = g_new (GtkRcSet, 1);
-      rc_set->pspec = g_pattern_spec_new (pattern);
+      rc_set->type = path_type;
+      
+      if (path_type == GTK_PATH_WIDGET_CLASS)
+        {
+          rc_set->pspec = NULL;
+          rc_set->path = _gtk_rc_parse_widget_class_path (pattern);
+        }
+      else
+        {
+          rc_set->pspec = g_pattern_spec_new (pattern);
+          rc_set->path = NULL;
+        }
+      
       rc_set->rc_style = rc_style;
       rc_set->priority = priority;
 
@@ -4215,6 +4267,286 @@ gtk_rc_parse_logical_color (GScanner   *scanner,
   return G_TOKEN_NONE;
 }
 
+
+GSList *
+_gtk_rc_parse_widget_class_path (const gchar *pattern)
+{
+  GSList *result;
+  PathElt *path_elt;
+  const gchar *current;
+  const gchar *class_start;
+  const gchar *class_end;
+  const gchar *pattern_end;
+  const gchar *pattern_start;
+  gchar *sub_pattern;
+
+  result = NULL;
+  current = pattern;
+  while ((class_start = strchr (current, '<')) && 
+        (class_end = strchr (class_start, '>')))
+    {
+      /* Add patterns, but ignore single dots */
+      if (!(class_start == current || 
+           (class_start == current + 1 && current[0] == '.')))
+        {
+          pattern_end = class_start - 1;
+          pattern_start = current;
+          
+          path_elt = g_new (PathElt, 1);
+          
+          sub_pattern = g_strndup (pattern_start, pattern_end - pattern_start + 1);
+         path_elt->type = PATH_ELT_PSPEC;
+          path_elt->elt.pspec = g_pattern_spec_new (sub_pattern);
+          g_free (sub_pattern);
+          
+          result = g_slist_prepend (result, path_elt);
+        }
+      
+      path_elt = g_new (PathElt, 1);
+      
+      /* The < > need to be removed from the string. */
+      sub_pattern = g_strndup (class_start + 1, class_end - class_start - 1);
+      
+      path_elt->type = PATH_ELT_UNRESOLVED;
+      path_elt->elt.class_name = sub_pattern;
+      
+      result = g_slist_prepend (result, path_elt);
+      
+      current = class_end + 1;
+    }
+  
+  /* Add the rest, if anything is left */
+  if (strlen (current) > 0)
+    {
+      path_elt = g_new (PathElt, 1);
+      path_elt->type = PATH_ELT_PSPEC;
+      path_elt->elt.pspec = g_pattern_spec_new (current);
+      
+      result = g_slist_prepend (result, path_elt);
+    }
+  
+  return g_slist_reverse (result);
+}
+
+static void
+free_path_elt (gpointer data, 
+              gpointer user_data)
+{
+  PathElt *path_elt = data;
+
+  switch (path_elt->type)
+    {
+    case PATH_ELT_PSPEC:
+      g_pattern_spec_free (path_elt->elt.pspec);
+      break;
+    case PATH_ELT_UNRESOLVED:
+      g_free (path_elt->elt.class_name);
+      break;
+    case PATH_ELT_TYPE:
+      break;
+    default:
+      g_assert_not_reached ();
+    }
+
+  g_free (path_elt);
+}
+
+void
+_gtk_rc_free_widget_class_path (GSList *list)
+{
+  g_slist_foreach (list, free_path_elt, NULL);
+  g_slist_free (list);
+}
+
+static void
+gtk_rc_set_free (GtkRcSet *rc_set)
+{
+  if (rc_set->pspec)
+    g_pattern_spec_free (rc_set->pspec);
+
+  _gtk_rc_free_widget_class_path (rc_set->path);
+  
+  g_free (rc_set);
+}
+
+static gboolean
+match_class (PathElt *path_elt, 
+            gchar   *type_name)
+{
+  GType type;
+  
+  if (path_elt->type == PATH_ELT_UNRESOLVED)
+    {
+      type = g_type_from_name (path_elt->elt.class_name);
+      if (type != G_TYPE_INVALID)
+        {
+          g_free (path_elt->elt.class_name);
+          path_elt->elt.class_type = type;
+         path_elt->type = PATH_ELT_TYPE;
+        }
+      else
+       return g_str_equal (type_name, path_elt->elt.class_name);
+    }
+  
+  return g_type_is_a (g_type_from_name (type_name), path_elt->elt.class_type);
+}
+
+static gboolean
+match_widget_class_recursive (GSList *list, 
+                             guint   length, 
+                             gchar  *path, 
+                             gchar  *path_reversed)
+{
+  PathElt *path_elt;
+  
+  /* break out if we cannot match anymore. */
+  if (list == NULL)
+    {
+      if (length > 0)
+        return FALSE;
+      else
+        return TRUE;
+    }
+
+  /* there are two possibilities:
+   *  1. The next pattern should match the class.
+   *  2. First normal matching, and then maybe a class */
+  
+  path_elt = list->data;
+
+  if (path_elt->type != PATH_ELT_PSPEC)
+    {
+      gchar *class_start = path;
+      gchar *class_end;
+      
+      /* ignore leading dot */
+      if (class_start[0] == '.')
+        class_start++;
+      class_end = strchr (class_start, '.');
+
+      if (class_end == NULL)
+        {
+          if (!match_class (path_elt, class_start))
+            return FALSE;
+         else
+           return match_widget_class_recursive (list->next, 0, "", "");
+        }
+      else
+        {
+          class_end[0] = '\0';
+          if (!match_class (path_elt, class_start))
+            {
+              class_end[0] = '.';
+              return FALSE;
+            }
+          else
+            {
+              gboolean result;
+              gint new_length = length - (class_end - path);
+              gchar old_char = path_reversed[new_length];
+              
+              class_end[0] = '.';
+              
+              path_reversed[new_length] = '\0';
+              result = match_widget_class_recursive (list->next, new_length, class_end, path_reversed);
+              path_reversed[new_length] = old_char;
+              
+              return result;
+            }
+        }
+    }
+  else
+    {
+      PathElt *class_elt;
+      gchar *class_start;
+      gchar *class_end;
+      gboolean result = FALSE;
+      
+      /* If there is nothing after this (ie. no class match), 
+       * just compare the pspec. 
+       */
+      if (list->next == NULL)
+        return g_pattern_match (path_elt->elt.pspec, length, path, path_reversed);
+      
+      class_elt = (PathElt *)list->next->data;
+      g_assert (class_elt->type != PATH_ELT_PSPEC);
+      
+      class_start = path;
+      if (class_start[0] == '.')
+        class_start++;
+      
+      while (TRUE)
+        {
+         class_end = strchr (class_start, '.');
+          
+          /* It should be cheaper to match the class first. (either the pattern
+           * is simple, and will match most of the times, or it may be complex
+           * and matching is slow) 
+          */
+          if (class_end == NULL)
+           {
+             result = match_class (class_elt, class_start);
+           }
+          else
+            {
+              class_end[0] = '\0';
+              result = match_class (class_elt, class_start);
+              class_end[0] = '.';
+            }
+          
+          if (result)
+            {
+              gchar old_char;
+              result = FALSE;
+              
+              /* terminate the string in front of the class. It does not matter
+               * that the class becomes unusable, because it is not needed 
+              * inside the recursion 
+              */
+              old_char = class_start[0];
+              class_start[0] = '\0';
+              
+              if (g_pattern_match (path_elt->elt.pspec, class_start - path, path, path_reversed + length - (class_start - path)))
+                {
+                  if (class_end != NULL)
+                    {
+                      gint new_length = length - (class_end - path);
+                      gchar path_reversed_char = path_reversed[new_length];
+                      
+                      path_reversed[new_length] = '\0';
+                      
+                      result = match_widget_class_recursive (list->next->next, new_length, class_end, path_reversed);
+                      
+                      path_reversed[new_length] = path_reversed_char;
+                    }
+                  else
+                    result = match_widget_class_recursive (list->next->next, 0, "", "");
+                }
+                
+              class_start[0] = old_char;
+            }
+          
+          if (result)
+            return TRUE;
+          
+          /* get next class in path, or break out */
+          if (class_end != NULL)
+            class_start = class_end + 1;
+          else
+            return FALSE;
+        }
+    }
+}
+
+gboolean
+_gtk_rc_match_widget_class (GSList  *list,
+                            gint     length,
+                            gchar   *path,
+                            gchar   *path_reversed)
+{
+  return match_widget_class_recursive (list, length, path, path_reversed);
+}
+
 #ifdef G_OS_WIN32
 
 /* DLL ABI stability backward compatibility versions */
index ea6ff22235fa3105ab8b542b1bd36a6a0a5e9c95..3b8e67e6c0a670a4f81b9a1d49959b0afa7364e3 100644 (file)
@@ -126,7 +126,14 @@ struct _GtkRcStyleClass
 #define gtk_rc_parse gtk_rc_parse_utf8
 #endif
 
-void     _gtk_rc_init                  (void);
+void     _gtk_rc_init                   (void);
+GSList*   _gtk_rc_parse_widget_class_path (const gchar *pattern);
+void      _gtk_rc_free_widget_class_path (GSList       *list);
+gboolean  _gtk_rc_match_widget_class     (GSList       *list,
+                                          gint          length,
+                                          gchar        *path,
+                                          gchar        *path_reversed);
+
 void      gtk_rc_add_default_file      (const gchar *filename);
 void      gtk_rc_set_default_files      (gchar **filenames);
 gchar**   gtk_rc_get_default_files      (void);
diff --git a/tests/testrc.c b/tests/testrc.c
new file mode 100644 (file)
index 0000000..a019ae3
--- /dev/null
@@ -0,0 +1,77 @@
+/* GTK - The GIMP Toolkit
+
+   Copyright (C) 2006 Red Hat, Inc.
+   Author: Matthias Clasen <mclasen@redhat.com>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+*/
+
+#include <string.h>
+#include "gtk/gtk.h"
+
+/* NOTE to compile this test, GTK+ needs to be build without
+ * the _-prefix stripping.
+ */
+struct {
+  gchar    *pattern;
+  gchar    *test;
+  gboolean  match;
+} tests[] = {
+  { "", "", TRUE },
+  { "<GtkCheckButton>", "GtkToggleButton", FALSE },
+  { "<GtkCheckButton>", "GtkCheckButton", TRUE },
+  { "<GtkCheckButton>", "GtkRadioButton", TRUE },
+  { "abc*.<GtkButton>.<GtkLabel>.*foo", "abcx.GtkToggleButton.GtkLabel.foo", TRUE },
+  { "*abc.<GtkButton>.foo*", "abc.GtkToggleButton.bar", FALSE },
+  { "*abc.<GtkButton>.foo*", "xabc.GtkToggleButton.fox", FALSE },
+  { NULL, NULL, FALSE }
+};
+
+static void
+load_types (void)
+{
+  volatile GType type;
+
+  type = gtk_radio_button_get_type ();
+  type = gtk_label_get_type ();
+}
+
+int
+main (int argc, char *argv[])
+{
+  gint i;
+
+  gtk_init (&argc, &argv);
+  load_types ();
+
+  for (i = 0; tests[i].test; i++)
+    {
+      GSList *list;
+      gchar *path, *rpath;
+      gboolean result;
+      
+      list = _gtk_rc_parse_widget_class_path (tests[i].pattern);
+      path = g_strdup (tests[i].test);
+      rpath = g_utf8_strreverse (path, -1);
+      result = _gtk_rc_match_widget_class (list, strlen (path), path, rpath);
+      g_print ("%d. \"%s\" \"%s\", expected %d, got %d\n",
+              i, tests[i].pattern, tests[i].test, tests[i].match, result);
+      g_assert (result == tests[i].match);
+      g_free (path);
+    }
+
+  return 0;
+}