/** * xfdashboard_traverse_actor: * @inRootActor: The root #ClutterActor where to begin traversing * @inSelector: A #XfdashboardCssSelector to filter actors while traversing or * %NULL to disable filterting * @inCallback: Function to call on matching children * @inUserData: Data to pass to callback function * * Iterates through all children of @inRootActor recursively beginning at * @inRootActor and for each child matching the selector @inSelector it calls the * callback function @inCallback with the matching child and the user-data at * @inUserData. * * If @inRootActor is %NULL it begins at the global stage. * * If the selector @inSelector is %NULL all children will match and the callback * function @inCallback is called for all children. */ void xfdashboard_traverse_actor(ClutterActor *inRootActor, XfdashboardCssSelector *inSelector, XfdashboardTraversalCallback inCallback, gpointer inUserData) { g_return_if_fail(!inRootActor || CLUTTER_IS_ACTOR(inRootActor)); g_return_if_fail(!inSelector || XFDASHBOARD_IS_CSS_SELECTOR(inSelector)); g_return_if_fail(inCallback); /* If root actor where begin traversal is NULL then begin at stage */ if(!inRootActor) { inRootActor=CLUTTER_ACTOR(xfdashboard_application_get_stage(NULL)); } /* If no selector is provider create a seletor matching all actors. * Otherwise take an extra ref on provided selector to prevent * destruction when we unref it later. */ if(!inSelector) inSelector=xfdashboard_css_selector_new_from_string("*"); else g_object_ref(inSelector); /* Do traversal */ _xfdashboard_traverse_actor_internal(inRootActor, inSelector, inCallback, inUserData); /* Release reference on selector */ g_object_unref(inSelector); }
static gboolean _xfdashboard_css_selector_parse(XfdashboardCssSelector *self, GScanner *ioScanner) { GScannerConfig *oldScannerConfig; GScannerConfig *scannerConfig; gboolean success; GTokenType token; g_return_val_if_fail(XFDASHBOARD_IS_CSS_SELECTOR(self), FALSE); g_return_val_if_fail(ioScanner, FALSE); success=TRUE; /* Set up scanner configuration for parsing css selectors: * - Identifiers are allowed to contain '-' (minus sign) as non-first characters * - Disallow scanning float values as we need '.' for identifiers * - Set up single comment line not to include '#' as this character is need for identifiers * - Disable parsing HEX values * - Identifiers cannot be single quoted * - Identifiers cannot be double quoted */ scannerConfig=(GScannerConfig*)g_memdup(ioScanner->config, sizeof(GScannerConfig)); scannerConfig->cset_skip_characters=" \n\r\t"; scannerConfig->cset_identifier_nth=G_CSET_a_2_z "-_0123456789" G_CSET_A_2_Z G_CSET_LATINS G_CSET_LATINC; scannerConfig->scan_float=FALSE; scannerConfig->cpair_comment_single="\1\n"; scannerConfig->scan_hex=FALSE; scannerConfig->scan_string_sq=FALSE; scannerConfig->scan_string_dq=FALSE; /* Set new scanner configuration but remember old one to restore it later */ oldScannerConfig=ioScanner->config; ioScanner->config=scannerConfig; /* Parse input stream */ token=g_scanner_peek_next_token(ioScanner); if(token!=G_TOKEN_EOF) { token=_xfdashboard_css_selector_parse_css_rule(self, ioScanner); if(token==G_TOKEN_ERROR) { g_warning(_("Failed to parse css selector.")); success=FALSE; } } else { g_warning(_("Failed to parse css selector because stream is empty.")); success=FALSE; } /* Restore old scanner configuration */ ioScanner->config=oldScannerConfig; /* Release allocated resources */ g_free(scannerConfig); /* Return success result */ return(success); }
/* Check and score this selector against a stylable node. * A score below 0 means that they did not match. */ gint xfdashboard_css_selector_score_matching_stylable_node(XfdashboardCssSelector *self, XfdashboardStylable *inStylable) { g_return_val_if_fail(XFDASHBOARD_IS_CSS_SELECTOR(self), -1); g_return_val_if_fail(XFDASHBOARD_IS_STYLABLE(inStylable), -1); /* Check and score rules */ return(_xfdashboard_css_selector_score_matching_node(self->priv->rule, inStylable)); }
/* Get string for selector. * Free string returned with g_free(). */ gchar* xfdashboard_css_selector_to_string(XfdashboardCssSelector *self) { XfdashboardCssSelectorPrivate *priv; gchar *selector; g_return_val_if_fail(XFDASHBOARD_IS_CSS_SELECTOR(self), NULL); priv=self->priv; selector=NULL; /* Get string for selector */ if(priv->rule) { selector=_xfdashboard_css_selector_rule_to_string(priv->rule); } /* Return newly created string for selector */ return(selector); }
/* Adjust source line and position of this selector to an offset */ void xfdashboard_css_selector_adjust_to_offset(XfdashboardCssSelector *self, gint inLine, gint inPosition) { XfdashboardCssSelectorPrivate *priv; gint newLine; gint newPosition; g_return_if_fail(XFDASHBOARD_IS_CSS_SELECTOR(self)); priv=self->priv; /* Adjust to offset */ if(priv->rule) { newLine=inLine+priv->rule->origLine; priv->rule->line=MAX(0, newLine); newPosition=inPosition+priv->rule->origPosition; priv->rule->position=MAX(0, newPosition); } }
/* Internal function to traverse an actor which can be call recursively */ static gboolean _xfdashboard_traverse_actor_internal(ClutterActor *inActor, XfdashboardCssSelector *inSelector, XfdashboardTraversalCallback inCallback, gpointer inUserData) { ClutterActorIter iter; ClutterActor *child; gint score; gboolean doContinueTraversal; g_return_val_if_fail(CLUTTER_IS_ACTOR(inActor), XFDASHBOARD_TRAVERSAL_CONTINUE); g_return_val_if_fail(XFDASHBOARD_IS_CSS_SELECTOR(inSelector), XFDASHBOARD_TRAVERSAL_CONTINUE); g_return_val_if_fail(inCallback, XFDASHBOARD_TRAVERSAL_CONTINUE); /* Check if given actor matches selector if a selector is provided * otherwise each child will match. Call callback for matching children. */ if(XFDASHBOARD_IS_STYLABLE(inActor)) { score=xfdashboard_css_selector_score_matching_stylable_node(inSelector, XFDASHBOARD_STYLABLE(inActor)); if(score>=0) { doContinueTraversal=(inCallback)(inActor, inUserData); if(!doContinueTraversal) return(doContinueTraversal); } } /* For each child of actor call ourselve recursive */ clutter_actor_iter_init(&iter, inActor); while(clutter_actor_iter_next(&iter, &child)) { doContinueTraversal=_xfdashboard_traverse_actor_internal(child, inSelector, inCallback, inUserData); if(!doContinueTraversal) return(doContinueTraversal); } /* If we get here return and continue traversal */ return(XFDASHBOARD_TRAVERSAL_CONTINUE); }
static GTokenType _xfdashboard_css_selector_parse_css_rule(XfdashboardCssSelector *self, GScanner *inScanner) { XfdashboardCssSelectorPrivate *priv; GTokenType token; XfdashboardCssSelectorRule *rule, *parentRule; g_return_val_if_fail(XFDASHBOARD_IS_CSS_SELECTOR(self), G_TOKEN_ERROR); g_return_val_if_fail(inScanner, G_TOKEN_ERROR); priv=self->priv; /* Parse comma-seperated selectors until a left curly bracket is found */ parentRule=NULL; rule=NULL; token=g_scanner_peek_next_token(inScanner); while(token!=G_TOKEN_EOF) { switch((guint)token) { case G_TOKEN_IDENTIFIER: case '*': case '#': case '.': case ':': /* Set last selector as parent if available */ if(rule) parentRule=rule; else parentRule=NULL; /* Create new selector */ rule=_xfdashboard_css_selector_rule_new(inScanner->input_name, priv->priority, g_scanner_cur_line(inScanner), g_scanner_cur_position(inScanner)); priv->rule=rule; /* Check if there was a previous selector and if so, the new one * should use the previous selector to match an ancestor */ if(parentRule) { rule->parentRule=parentRule; rule->parentRuleMode=XFDASHBOARD_CSS_SELECTOR_RULE_MODE_ANCESTOR; } /* Parse selector */ token=_xfdashboard_css_selector_parse_css_simple_selector(self, inScanner, rule); if(token!=G_TOKEN_NONE) return(token); break; case '>': g_scanner_get_next_token(inScanner); /* Set last selector as parent selector */ if(!rule) { g_scanner_unexp_token(inScanner, G_TOKEN_IDENTIFIER, NULL, NULL, NULL, _("No parent when parsing '>'"), TRUE); return(G_TOKEN_ERROR); } parentRule=rule; /* Create new selector */ rule=_xfdashboard_css_selector_rule_new(inScanner->input_name, priv->priority, g_scanner_cur_line(inScanner), g_scanner_cur_position(inScanner)); priv->rule=rule; /* Link parent to the new selector as parent selector */ rule->parentRule=parentRule; rule->parentRuleMode=XFDASHBOARD_CSS_SELECTOR_RULE_MODE_PARENT; /* Parse selector */ token=_xfdashboard_css_selector_parse_css_simple_selector(self, inScanner, rule); if(token!=G_TOKEN_NONE) return(token); break; default: /* Stop at first invalid character in stream and * return with success result. */ return(G_TOKEN_NONE); } /* Continue parsing with next token */ token=g_scanner_peek_next_token(inScanner); } /* Eat "eof" token */ if(token==G_TOKEN_EOF) token=g_scanner_get_next_token(inScanner); /* Successfully parsed */ return(G_TOKEN_EOF); }
/* Parse selector */ static GTokenType _xfdashboard_css_selector_parse_css_simple_selector(XfdashboardCssSelector *self, GScanner *inScanner, XfdashboardCssSelectorRule *ioRule) { GTokenType token; g_return_val_if_fail(XFDASHBOARD_IS_CSS_SELECTOR(self), G_TOKEN_ERROR); g_return_val_if_fail(inScanner, G_TOKEN_ERROR); g_return_val_if_fail(ioRule, G_TOKEN_ERROR); /* Parse type of selector. It is optional as '*' can be used as wildcard */ token=g_scanner_peek_next_token(inScanner); switch((guint)token) { case '*': g_scanner_get_next_token(inScanner); ioRule->type=g_strdup("*"); /* Check if next token follows directly after this identifier. * It is determined by checking if scanner needs to move more than * one (the next) character. If there is a gap then either a new * selector follows or it is a new typeless selector. */ token=g_scanner_peek_next_token(inScanner); if(inScanner->next_line==g_scanner_cur_line(inScanner) && (inScanner->next_position-g_scanner_cur_position(inScanner))>1) { return(G_TOKEN_NONE); } break; case G_TOKEN_IDENTIFIER: g_scanner_get_next_token(inScanner); ioRule->type=g_strdup(inScanner->value.v_identifier); /* Check if next token follows directly after this identifier. * It is determined by checking if scanner needs to move more than * one (the next) character. If there is a gap then either a new * selector follows or it is a new typeless selector. */ token=g_scanner_peek_next_token(inScanner); if(inScanner->next_line==g_scanner_cur_line(inScanner) && (inScanner->next_position-g_scanner_cur_position(inScanner))>1) { return(G_TOKEN_NONE); } break; default: break; } /* Here we look for '#', '.' or ':' and return if we find anything else */ token=g_scanner_peek_next_token(inScanner); while(token!=G_TOKEN_NONE) { switch((guint)token) { /* Parse ID */ case '#': g_scanner_get_next_token(inScanner); token=g_scanner_get_next_token(inScanner); if(token!=G_TOKEN_IDENTIFIER) { g_scanner_unexp_token(inScanner, G_TOKEN_IDENTIFIER, NULL, NULL, NULL, _("Invalid name identifier"), TRUE); return(G_TOKEN_ERROR); } ioRule->id=g_strdup(inScanner->value.v_identifier); break; /* Parse class */ case '.': g_scanner_get_next_token(inScanner); token=g_scanner_get_next_token(inScanner); if(token!=G_TOKEN_IDENTIFIER) { g_scanner_unexp_token(inScanner, G_TOKEN_IDENTIFIER, NULL, NULL, NULL, _("Invalid class identifier"), TRUE); return(G_TOKEN_ERROR); } if(ioRule->classes) { /* Remember old classes as it can only be freed afterwards */ gchar *oldClasses=ioRule->classes; /* Create new list of classes */ ioRule->classes=g_strconcat(ioRule->classes, ".", inScanner->value.v_identifier, NULL); /* Now free old classes */ g_free(oldClasses); } else { ioRule->classes=g_strdup(inScanner->value.v_identifier); } break; /* Parse pseudo-class */ case ':': g_scanner_get_next_token(inScanner); token=g_scanner_get_next_token(inScanner); if(token!=G_TOKEN_IDENTIFIER) { g_scanner_unexp_token(inScanner, G_TOKEN_IDENTIFIER, NULL, NULL, NULL, _("Invalid pseudo-class identifier"), TRUE); return(G_TOKEN_ERROR); } if(ioRule->pseudoClasses) { /* Remember old pseudo-classes as it can only be freed afterwards */ gchar *oldPseudoClasses=ioRule->pseudoClasses; /* Create new list of pseudo-classes */ ioRule->pseudoClasses=g_strconcat(ioRule->pseudoClasses, ":", inScanner->value.v_identifier, NULL); /* Now free old pseudo-classes */ g_free(oldPseudoClasses); } else { ioRule->pseudoClasses=g_strdup(inScanner->value.v_identifier); } break; default: return(G_TOKEN_NONE); } /* Get next token */ token=g_scanner_peek_next_token(inScanner); } /* Successfully parsed */ return(G_TOKEN_NONE); }
/* Get rule parsed */ XfdashboardCssSelectorRule* xfdashboard_css_selector_get_rule(XfdashboardCssSelector *self) { g_return_val_if_fail(XFDASHBOARD_IS_CSS_SELECTOR(self), NULL); return(self->priv->rule); }