+
+static guint
+gtk_rc_parse_logical_color (GScanner *scanner,
+ GtkRcStyle *rc_style,
+ GHashTable *hash)
+{
+ gchar *color_id = NULL;
+ guint token;
+ GdkColor color;
+
+ token = g_scanner_get_next_token (scanner);
+ if (token != GTK_RC_TOKEN_COLOR)
+ return GTK_RC_TOKEN_COLOR;
+
+ token = gtk_rc_parse_hash_key (scanner, &color_id);
+ if (token != G_TOKEN_NONE)
+ return token;
+
+ token = g_scanner_get_next_token (scanner);
+ if (token != G_TOKEN_EQUAL_SIGN)
+ {
+ g_free (color_id);
+ return G_TOKEN_EQUAL_SIGN;
+ }
+
+ token = gtk_rc_parse_color_full (scanner, rc_style, &color);
+ if (token != G_TOKEN_NONE)
+ {
+ g_free (color_id);
+ return token;
+ }
+
+ /* Because the hash is created with destroy functions,
+ * g_hash_table_insert will free any old values for us,
+ * if a mapping with the specified key already exists.
+ */
+ g_hash_table_insert (hash, color_id, gdk_color_copy (&color));
+
+ 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);
+}