summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2006-03-23 23:21:30 +0000
committerMatthias Clasen <matthiasc@src.gnome.org>2006-03-23 23:21:30 +0000
commitac879843d0796bea474272842a4e129a2feab2f7 (patch)
treec759d8091ab89874ebce25e1ae9e144be1d997be
parentefbac0906142738366f4264f65987a180f8869be (diff)
downloadgdk-pixbuf-ac879843d0796bea474272842a4e129a2feab2f7.tar.gz
Support subclasses in RC files. (#142417, Todd Berman, patch based on a
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
-rw-r--r--ChangeLog15
-rw-r--r--ChangeLog.pre-2-1015
-rw-r--r--gtk/gtkbindings.c66
-rw-r--r--gtk/gtkrc.c408
-rw-r--r--gtk/gtkrc.h9
-rw-r--r--tests/testrc.c77
6 files changed, 532 insertions, 58 deletions
diff --git a/ChangeLog b/ChangeLog
index 7768fa11b..ee7b54ed2 100644
--- 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()
diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10
index 7768fa11b..ee7b54ed2 100644
--- a/ChangeLog.pre-2-10
+++ b/ChangeLog.pre-2-10
@@ -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()
diff --git a/gtk/gtkbindings.c b/gtk/gtkbindings.c
index 9e2c46294..fcba62887 100644
--- a/gtk/gtkbindings.c
+++ b/gtk/gtkbindings.c
@@ -44,9 +44,11 @@
/* --- 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);
diff --git a/gtk/gtkrc.c b/gtk/gtkrc.c
index 0047dc5e0..933192a44 100644
--- a/gtk/gtkrc.c
+++ b/gtk/gtkrc.c
@@ -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 */
diff --git a/gtk/gtkrc.h b/gtk/gtkrc.h
index ea6ff2223..3b8e67e6c 100644
--- a/gtk/gtkrc.h
+++ b/gtk/gtkrc.h
@@ -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
index 000000000..a019ae394
--- /dev/null
+++ b/tests/testrc.c
@@ -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;
+}