[Xfce4-commits] [apps/xfdashboard] 01/02: Complete animation XML parser and create animation XML file for default theme (xfdashboard)

noreply at xfce.org noreply at xfce.org
Fri Nov 15 20:57:59 CET 2019


This is an automated email from the git hooks/post-receive script.

n   o   m   a   d       p   u   s   h   e   d       a       c   o   m   m   i   t       t   o       b   r   a   n   c   h       m   a   s   t   e   r   
   in repository apps/xfdashboard.

commit 6b649f55f995c9ef3fff56f9ff26b490a9c120af
Author: Stephan Haller <nomad at froevel.de>
Date:   Fri Nov 15 14:12:38 2019 +0100

    Complete animation XML parser and create animation XML file for default theme (xfdashboard)
---
 data/themes/xfdashboard/animations.xml          |   17 +
 data/themes/xfdashboard/xfdashboard.theme.in.in |    1 +
 libxfdashboard/theme-animation.c                | 1297 ++++++++++++++++++++---
 libxfdashboard/theme.c                          |   29 +-
 libxfdashboard/utils.c                          |   43 +
 libxfdashboard/utils.h                          |    1 +
 6 files changed, 1250 insertions(+), 138 deletions(-)

diff --git a/data/themes/xfdashboard/animations.xml b/data/themes/xfdashboard/animations.xml
new file mode 100644
index 0000000..afd976f
--- /dev/null
+++ b/data/themes/xfdashboard/animations.xml
@@ -0,0 +1,17 @@
+<animations>
+	<trigger id="expand-workspace" sender="#workspace-selector-collapse-box" signal="expand">
+		<timeline delay="0" duration="500" mode="ease-out-cubic">
+			<apply>
+				<property name="width" />
+			</apply>
+		</timeline>
+	</trigger>
+
+	<trigger id="collapse-workspace" sender="#workspace-selector-collapse-box" signal="collapse">
+		<timeline delay="0" duration="500" mode="ease-in-cubic">
+			<apply>
+				<property name="width" />
+			</apply>
+		</timeline>
+	</trigger>
+</animations>
diff --git a/data/themes/xfdashboard/xfdashboard.theme.in.in b/data/themes/xfdashboard/xfdashboard.theme.in.in
index 3443987..a4494a4 100644
--- a/data/themes/xfdashboard/xfdashboard.theme.in.in
+++ b/data/themes/xfdashboard/xfdashboard.theme.in.in
@@ -4,6 +4,7 @@ _Comment=Default theme for xfdashboard
 Style=xfdashboard.css
 Layout=xfdashboard.xml;xfdashboard-secondary.xml
 Effects=effects.xml
+Animations=animations.xml
 Author=Stephan Haller
 Version=@PACKAGE_VERSION@
 Screenshot=screenshot-xfdashboard.png
diff --git a/libxfdashboard/theme-animation.c b/libxfdashboard/theme-animation.c
index b90537b..2d83426 100644
--- a/libxfdashboard/theme-animation.c
+++ b/libxfdashboard/theme-animation.c
@@ -30,6 +30,7 @@
 #include <glib/gi18n-lib.h>
 #include <glib.h>
 #include <gio/gio.h>
+#include <errno.h>
 
 #include <libxfdashboard/transition-group.h>
 #include <libxfdashboard/stylable.h>
@@ -54,6 +55,16 @@ G_DEFINE_TYPE_WITH_PRIVATE(XfdashboardThemeAnimation,
 /* IMPLEMENTATION: Private variables and methods */
 enum
 {
+	TAG_DOCUMENT,
+	TAG_ANIMATIONS,
+	TAG_TRIGGER,
+	TAG_TIMELINE,
+	TAG_APPLY,
+	TAG_PROPERTY
+};
+
+enum
+{
 	XFDASHBOARD_THEME_ANIMATION_APPLY_TO_ORIGIN_SENDER=0,
 	XFDASHBOARD_THEME_ANIMATION_APPLY_TO_ORIGIN_STAGE=1
 };
@@ -87,6 +98,24 @@ struct _XfdashboardThemeAnimationTargetsProperty
 	GValue					to;
 };
 
+typedef struct _XfdashboardThemeAnimationParserData			XfdashboardThemeAnimationParserData;
+struct _XfdashboardThemeAnimationParserData
+{
+	XfdashboardThemeAnimation			*self;
+
+	GSList								*specs;
+
+	XfdashboardThemeAnimationSpec		*currentSpec;
+	ClutterTimeline						*currentTimeline;
+	XfdashboardThemeAnimationTargets	*currentTargets;
+
+	gint								lastLine;
+	gint								lastPosition;
+	gint								currentLine;
+	gint								currentPostition;
+};
+
+
 /* Create, destroy, ref and unref animation targets data */
 static XfdashboardThemeAnimationTargetsProperty* _xfdashboard_theme_animation_targets_property_new(const gchar *inPropertyName,
 																									const gchar *inPropertyFrom,
@@ -430,6 +459,1128 @@ static GSList* _xfdashboard_theme_animation_find_actors_for_animation_targets(Xf
 	return(actors);
 }
 
+/* Callback to add each successfully parsed animation specifications to list of
+ * known animations of this theme.
+ */
+static void _xfdashboard_theme_animation_ref_and_add_to_theme(gpointer inData, gpointer inUserData)
+{
+	XfdashboardThemeAnimation				*self;
+	XfdashboardThemeAnimationPrivate		*priv;
+	XfdashboardThemeAnimationSpec			*spec;
+
+	g_return_if_fail(XFDASHBOARD_IS_THEME_ANIMATION(inUserData));
+	g_return_if_fail(inData);
+
+	self=XFDASHBOARD_THEME_ANIMATION(inUserData);
+	priv=self->priv;
+	spec=(XfdashboardThemeAnimationSpec*)inData;
+
+	/* Increase reference of specified animation specification and add to list
+	 * of known ones.
+	 */
+	priv->specs=g_slist_prepend(priv->specs, _xfdashboard_theme_animation_spec_ref(spec));
+}
+
+/* Check if an animation specification with requested ID exists */
+static gboolean _xfdashboard_theme_animation_has_id(XfdashboardThemeAnimation *self,
+													XfdashboardThemeAnimationParserData *inParserData,
+													const gchar *inID)
+{
+	XfdashboardThemeAnimationPrivate		*priv;
+	GSList									*ids;
+	gboolean								hasID;
+	XfdashboardThemeAnimationSpec			*spec;
+
+	g_return_val_if_fail(XFDASHBOARD_IS_THEME_ANIMATION(self), TRUE);
+
+	priv=self->priv;
+	hasID=FALSE;
+
+	/* Check that ID to lookup is specified */
+	g_assert(inID && *inID);
+
+	/* Lookup ID first in currently parsed file if specified */
+	if(inParserData)
+	{
+		for(ids=inParserData->specs; !hasID && ids; ids=g_slist_next(ids))
+		{
+			spec=(XfdashboardThemeAnimationSpec*)ids->data;
+			if(spec && g_strcmp0(spec->id, inID)==0) hasID=TRUE;
+		}
+	}
+
+	/* If ID was not found in currently parsed effects xml file (if specified)
+	 * continue search in already parsed and known effects.
+	 */
+	if(!hasID)
+	{
+		for(ids=priv->specs; !hasID && ids; ids=g_slist_next(ids))
+		{
+			spec=(XfdashboardThemeAnimationSpec*)ids->data;
+			if(spec && g_strcmp0(spec->id, inID)==0) hasID=TRUE;
+		}
+	}
+
+	/* Return lookup result */
+	return(hasID);
+}
+
+/* Convert string to integer and throw error if conversion failed */
+static gboolean _xfdashboard_theme_animation_string_to_gint(const gchar *inNumberString, gint *outNumber, GError **outError)
+{
+	gint64			convertedNumber;
+	gchar			*outNumberStringEnd;
+
+	g_return_val_if_fail(inNumberString && *inNumberString, FALSE);
+	g_return_val_if_fail(outNumber, FALSE);
+	g_return_val_if_fail(outError==NULL || *outError==NULL, FALSE);
+
+	/* Try to convert string to number */
+	convertedNumber=g_ascii_strtoll(inNumberString, &outNumberStringEnd, 10);
+
+	/* Check if invalid base was specified */
+	if(errno==EINVAL)
+	{
+		/* Set error */
+		g_set_error(outError,
+					XFDASHBOARD_THEME_ANIMATION_ERROR,
+					XFDASHBOARD_THEME_ANIMATION_ERROR_ERROR,
+					_("Invalid base for conversion"));
+		return(FALSE);
+	}
+
+	/* Check if integer is out of range */
+	if(errno==ERANGE)
+	{
+		/* Set error */
+		g_set_error(outError,
+					XFDASHBOARD_THEME_ANIMATION_ERROR,
+					XFDASHBOARD_THEME_ANIMATION_ERROR_ERROR,
+					_("Integer out of range"));
+		return(FALSE);
+	}
+
+	/* If converted integer resulted in zero check if end pointer
+	 * has moved and does not match start pointer and points to a
+	 * NULL byte (as NULL-terminated strings must be provided).
+	 */
+	if(convertedNumber==0 &&
+		(outNumberStringEnd==inNumberString || *outNumberStringEnd!=0))
+	{
+		/* Set error */
+		g_set_error(outError,
+					XFDASHBOARD_THEME_ANIMATION_ERROR,
+					XFDASHBOARD_THEME_ANIMATION_ERROR_ERROR,
+					_("Cannot convert string '%s' to integer"),
+					inNumberString);
+		return(FALSE);
+	}
+
+	/* Set converted number - the integer */
+	*outNumber=((gint)convertedNumber);
+
+	/* Return TRUE for successful conversion */
+	return(TRUE);
+}
+
+/* Helper function to set up GError object in this parser */
+static void _xfdashboard_theme_animation_parse_set_error(XfdashboardThemeAnimationParserData *inParserData,
+															GMarkupParseContext *inContext,
+															GError **outError,
+															XfdashboardThemeAnimationErrorEnum inCode,
+															const gchar *inFormat,
+															...)
+{
+	GError		*tempError;
+	gchar		*message;
+	va_list		args;
+
+	/* Get error message */
+	va_start(args, inFormat);
+	message=g_strdup_vprintf(inFormat, args);
+	va_end(args);
+
+	/* Create error object */
+	tempError=g_error_new_literal(XFDASHBOARD_THEME_ANIMATION_ERROR, inCode, message);
+	if(inParserData)
+	{
+		g_prefix_error(&tempError,
+						_("Error on line %d char %d: "),
+						inParserData->lastLine,
+						inParserData->lastPosition);
+	}
+
+	/* Set error */
+	g_propagate_error(outError, tempError);
+
+	/* Release allocated resources */
+	g_free(message);
+}
+
+/* General callbacks which can be used for any tag */
+static void _xfdashboard_theme_animation_parse_general_no_text_nodes(GMarkupParseContext *inContext,
+																		const gchar *inText,
+																		gsize inTextLength,
+																		gpointer inUserData,
+																		GError **outError)
+{
+	XfdashboardThemeAnimationParserData			*data=(XfdashboardThemeAnimationParserData*)inUserData;
+	gchar										*realText;
+
+	/* Check if text contains only whitespace. If we find any non-whitespace
+	 * in text then set error.
+	 */
+	realText=g_strstrip(g_strdup(inText));
+	if(*realText)
+	{
+		const GSList	*parents;
+
+		parents=g_markup_parse_context_get_element_stack(inContext);
+		if(parents) parents=g_slist_next(parents);
+
+		_xfdashboard_theme_animation_parse_set_error(data,
+														inContext,
+														outError,
+														XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
+														_("Unexpected text node '%s' at tag <%s>"),
+														realText,
+														parents ? (gchar*)parents->data : "document");
+	}
+	g_free(realText);
+}
+
+/* Determine tag name and ID */
+static gint _xfdashboard_theme_animation_get_tag_by_name(const gchar *inTag)
+{
+	g_return_val_if_fail(inTag && *inTag, -1);
+
+	/* Compare string and return type ID */
+	if(g_strcmp0(inTag, "animations")==0) return(TAG_ANIMATIONS);
+	if(g_strcmp0(inTag, "trigger")==0) return(TAG_TRIGGER);
+	if(g_strcmp0(inTag, "timeline")==0) return(TAG_TIMELINE);
+	if(g_strcmp0(inTag, "apply")==0) return(TAG_APPLY);
+	if(g_strcmp0(inTag, "property")==0) return(TAG_PROPERTY);
+
+	/* If we get here we do not know tag name and return invalid ID */
+	return(-1);
+}
+
+static const gchar* _xfdashboard_theme_animation_get_tag_by_id(guint inTagType)
+{
+	/* Compare ID and return string */
+	switch(inTagType)
+	{
+		case TAG_DOCUMENT:
+			return("document");
+
+		case TAG_ANIMATIONS:
+			return("animations");
+
+		case TAG_TRIGGER:
+			return("trigger");
+
+		case TAG_TIMELINE:
+			return("timeline");
+
+		case TAG_APPLY:
+			return("apply");
+
+		case TAG_PROPERTY:
+			return("property");
+
+		default:
+			break;
+	}
+
+	/* If we get here we do not know tag name and return NULL */
+	return(NULL);
+}
+
+/* Parser callbacks for <property> node */
+static void _xfdashboard_theme_animation_parse_property_start(GMarkupParseContext *inContext,
+																const gchar *inElementName,
+																const gchar **inAttributeNames,
+																const gchar **inAttributeValues,
+																gpointer inUserData,
+																GError **outError)
+{
+	XfdashboardThemeAnimationParserData			*data=(XfdashboardThemeAnimationParserData*)inUserData;
+	gint										currentTag=TAG_PROPERTY;
+	gint										nextTag;
+
+	/* Update last position for more accurate line and position in error messages */
+	data->lastLine=data->currentLine;
+	data->lastPosition=data->currentPostition;
+	g_markup_parse_context_get_position(inContext, &data->currentLine, &data->currentPostition);
+
+	/* Get tag of next element */
+	nextTag=_xfdashboard_theme_animation_get_tag_by_name(inElementName);
+	if(nextTag==-1)
+	{
+		_xfdashboard_theme_animation_parse_set_error(data,
+													inContext,
+													outError,
+													XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
+													_("Unknown tag <%s>"),
+													inElementName);
+		return;
+	}
+
+	/* If we get here the given element name cannot follow this tag */
+	_xfdashboard_theme_animation_parse_set_error(data,
+													inContext,
+													outError,
+													XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
+													_("Tag <%s> cannot contain tag <%s>"),
+													_xfdashboard_theme_animation_get_tag_by_id(currentTag),
+													inElementName);
+}
+
+/* Parser callbacks for <apply> node */
+static void _xfdashboard_theme_animation_parse_apply_start(GMarkupParseContext *inContext,
+																const gchar *inElementName,
+																const gchar **inAttributeNames,
+																const gchar **inAttributeValues,
+																gpointer inUserData,
+																GError **outError)
+{
+	XfdashboardThemeAnimationParserData			*data=(XfdashboardThemeAnimationParserData*)inUserData;
+	gint										currentTag=TAG_APPLY;
+	gint										nextTag;
+	GError										*error=NULL;
+
+	/* Update last position for more accurate line and position in error messages */
+	data->lastLine=data->currentLine;
+	data->lastPosition=data->currentPostition;
+	g_markup_parse_context_get_position(inContext, &data->currentLine, &data->currentPostition);
+
+	/* Get tag of next element */
+	nextTag=_xfdashboard_theme_animation_get_tag_by_name(inElementName);
+	if(nextTag==-1)
+	{
+		_xfdashboard_theme_animation_parse_set_error(data,
+													inContext,
+													outError,
+													XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
+													_("Unknown tag <%s>"),
+													inElementName);
+		return;
+	}
+
+	/* Check if element name is <property> and follows expected parent tags:
+	 * <apply>
+	 */
+	if(nextTag==TAG_PROPERTY)
+	{
+		static GMarkupParser						propertyParser=
+													{
+														_xfdashboard_theme_animation_parse_property_start,
+														NULL,
+														_xfdashboard_theme_animation_parse_general_no_text_nodes,
+														NULL,
+														NULL,
+													};
+
+		const gchar									*propertyName=NULL;
+		const gchar									*propertyFrom=NULL;
+		const gchar									*propertyTo=NULL;
+		XfdashboardThemeAnimationTargetsProperty	*property=NULL;
+
+		g_assert(data->currentTargets!=NULL);
+
+		/* Get tag's attributes */
+		if(!g_markup_collect_attributes(inElementName,
+											inAttributeNames,
+											inAttributeValues,
+											&error,
+											G_MARKUP_COLLECT_STRING,
+											"name",
+											&propertyName,
+											G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL,
+											"from",
+											&propertyFrom,
+											G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL,
+											"to",
+											&propertyTo,
+											G_MARKUP_COLLECT_INVALID))
+		{
+			g_propagate_error(outError, error);
+			return;
+		}
+
+		/* Check tag's attributes */
+		if(strlen(propertyName)==0)
+		{
+			_xfdashboard_theme_animation_parse_set_error(data,
+															inContext,
+															outError,
+															XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
+															_("Empty 'name' at tag '%s'"),
+															inElementName);
+			return;
+		}
+
+		if(propertyFrom && strlen(propertyFrom)==0)
+		{
+			_xfdashboard_theme_animation_parse_set_error(data,
+															inContext,
+															outError,
+															XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
+															_("Empty 'from' at tag '%s'"),
+															inElementName);
+			return;
+		}
+
+		if(propertyTo && strlen(propertyTo)==0)
+		{
+			_xfdashboard_theme_animation_parse_set_error(data,
+															inContext,
+															outError,
+															XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
+															_("Empty 'to' at tag '%s'"),
+															inElementName);
+			return;
+		}
+
+		/* Create new animation property and add to current targets */
+		property=_xfdashboard_theme_animation_targets_property_new(propertyName, propertyFrom, propertyTo);
+		_xfdashboard_theme_animation_targets_add_property(data->currentTargets, property);
+		_xfdashboard_theme_animation_targets_property_unref(property);
+
+		/* Set up context for tag <apply> */
+		g_markup_parse_context_push(inContext, &propertyParser, inUserData);
+		return;
+	}
+
+	/* If we get here the given element name cannot follow this tag */
+	_xfdashboard_theme_animation_parse_set_error(data,
+													inContext,
+													outError,
+													XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
+													_("Tag <%s> cannot contain tag <%s>"),
+													_xfdashboard_theme_animation_get_tag_by_id(currentTag),
+													inElementName);
+}
+
+static void _xfdashboard_theme_animation_parse_apply_end(GMarkupParseContext *inContext,
+															const gchar *inElementName,
+															gpointer inUserData,
+															GError **outError)
+{
+	/* Restore previous parser context */
+	g_markup_parse_context_pop(inContext);
+}
+
+/* Parser callbacks for <timeline> node */
+static void _xfdashboard_theme_animation_parse_timeline_start(GMarkupParseContext *inContext,
+																const gchar *inElementName,
+																const gchar **inAttributeNames,
+																const gchar **inAttributeValues,
+																gpointer inUserData,
+																GError **outError)
+{
+	XfdashboardThemeAnimationParserData			*data=(XfdashboardThemeAnimationParserData*)inUserData;
+	gint										currentTag=TAG_TIMELINE;
+	gint										nextTag;
+	GError										*error=NULL;
+
+	/* Update last position for more accurate line and position in error messages */
+	data->lastLine=data->currentLine;
+	data->lastPosition=data->currentPostition;
+	g_markup_parse_context_get_position(inContext, &data->currentLine, &data->currentPostition);
+
+	/* Get tag of next element */
+	nextTag=_xfdashboard_theme_animation_get_tag_by_name(inElementName);
+	if(nextTag==-1)
+	{
+		_xfdashboard_theme_animation_parse_set_error(data,
+													inContext,
+													outError,
+													XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
+													_("Unknown tag <%s>"),
+													inElementName);
+		return;
+	}
+
+	/* Check if element name is <apply> and follows expected parent tags:
+	 * <timeline>
+	 */
+	if(nextTag==TAG_APPLY)
+	{
+		static GMarkupParser					propertyParser=
+												{
+													_xfdashboard_theme_animation_parse_apply_start,
+													_xfdashboard_theme_animation_parse_apply_end,
+													_xfdashboard_theme_animation_parse_general_no_text_nodes,
+													NULL,
+													NULL,
+												};
+
+		const gchar								*applyToText=NULL;
+		XfdashboardCssSelector					*applyTo=NULL;
+		const gchar								*applyOriginText=NULL;
+		gint									applyOrigin;
+
+		g_assert(data->currentTargets==NULL);
+
+		/* Get tag's attributes */
+		if(!g_markup_collect_attributes(inElementName,
+											inAttributeNames,
+											inAttributeValues,
+											&error,
+											G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL,
+											"to",
+											&applyToText,
+											G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL,
+											"origin",
+											&applyOriginText,
+											G_MARKUP_COLLECT_INVALID))
+		{
+			g_propagate_error(outError, error);
+			return;
+		}
+
+		/* Check tag's attributes */
+		if(applyToText && strlen(applyToText)==0)
+		{
+			_xfdashboard_theme_animation_parse_set_error(data,
+															inContext,
+															outError,
+															XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
+															_("Empty 'to' at tag '%s'"),
+															inElementName);
+			return;
+		}
+
+		if(applyOriginText && strlen(applyOriginText)==0)
+		{
+			_xfdashboard_theme_animation_parse_set_error(data,
+															inContext,
+															outError,
+															XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
+															_("Empty 'origin' at tag '%s'"),
+															inElementName);
+			return;
+		}
+
+		/* Convert tag's attributes' value to usable values */
+		applyOrigin=XFDASHBOARD_THEME_ANIMATION_APPLY_TO_ORIGIN_SENDER;
+		if(applyOriginText)
+		{
+			if(g_strcmp0(applyOriginText, "sender")==0) applyOrigin=XFDASHBOARD_THEME_ANIMATION_APPLY_TO_ORIGIN_SENDER;
+				else if(g_strcmp0(applyOriginText, "stage")==0) applyOrigin=XFDASHBOARD_THEME_ANIMATION_APPLY_TO_ORIGIN_STAGE;
+				else
+				{
+					_xfdashboard_theme_animation_parse_set_error(data,
+																	inContext,
+																	outError,
+																	XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
+																	_("Invalid value '%s' for 'origin' at tag '%s'"),
+																	applyOriginText,
+																	inElementName);
+					return;
+				}
+		}
+
+		/* Create new animation timeline with timeline data */
+		applyTo=NULL;
+		if(applyToText)
+		{
+			applyTo=xfdashboard_css_selector_new_from_string(applyToText);
+		}
+
+		data->currentTargets=_xfdashboard_theme_animation_targets_new(applyTo, applyOrigin, data->currentTimeline);
+
+		if(applyTo) g_object_unref(applyTo);
+
+		/* Set up context for tag <apply> */
+		g_markup_parse_context_push(inContext, &propertyParser, inUserData);
+		return;
+	}
+
+	/* If we get here the given element name cannot follow this tag */
+	_xfdashboard_theme_animation_parse_set_error(data,
+													inContext,
+													outError,
+													XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
+													_("Tag <%s> cannot contain tag <%s>"),
+													_xfdashboard_theme_animation_get_tag_by_id(currentTag),
+													inElementName);
+}
+
+static void _xfdashboard_theme_animation_parse_timeline_end(GMarkupParseContext *inContext,
+															const gchar *inElementName,
+															gpointer inUserData,
+															GError **outError)
+{
+	XfdashboardThemeAnimationParserData			*data=(XfdashboardThemeAnimationParserData*)inUserData;
+
+	g_assert(data->currentSpec);
+	g_assert(data->currentTargets);
+
+	/* Add targets to animation specification */
+	_xfdashboard_theme_animation_spec_add_targets(data->currentSpec, data->currentTargets);
+	_xfdashboard_theme_animation_targets_unref(data->currentTargets);
+	data->currentTargets=NULL;
+
+	/* Restore previous parser context */
+	g_markup_parse_context_pop(inContext);
+}
+
+/* Parser callbacks for <trigger> node */
+static void _xfdashboard_theme_animation_parse_trigger_start(GMarkupParseContext *inContext,
+																const gchar *inElementName,
+																const gchar **inAttributeNames,
+																const gchar **inAttributeValues,
+																gpointer inUserData,
+																GError **outError)
+{
+	XfdashboardThemeAnimationParserData			*data=(XfdashboardThemeAnimationParserData*)inUserData;
+	gint										currentTag=TAG_TRIGGER;
+	gint										nextTag;
+	GError										*error=NULL;
+
+	/* Update last position for more accurate line and position in error messages */
+	data->lastLine=data->currentLine;
+	data->lastPosition=data->currentPostition;
+	g_markup_parse_context_get_position(inContext, &data->currentLine, &data->currentPostition);
+
+	/* Get tag of next element */
+	nextTag=_xfdashboard_theme_animation_get_tag_by_name(inElementName);
+	if(nextTag==-1)
+	{
+		_xfdashboard_theme_animation_parse_set_error(data,
+													inContext,
+													outError,
+													XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
+													_("Unknown tag <%s>"),
+													inElementName);
+		return;
+	}
+
+	/* Check if element name is <timeline> and follows expected parent tags:
+	 * <trigger>
+	 */
+	if(nextTag==TAG_TIMELINE)
+	{
+		static GMarkupParser					propertyParser=
+												{
+													_xfdashboard_theme_animation_parse_timeline_start,
+													_xfdashboard_theme_animation_parse_timeline_end,
+													_xfdashboard_theme_animation_parse_general_no_text_nodes,
+													NULL,
+													NULL,
+												};
+
+		const gchar								*timelineDelayText=NULL;
+		const gchar								*timelineDurationText=NULL;
+		const gchar								*timelineModeText=NULL;
+		const gchar								*timelineRepeatText=NULL;
+		gint									timelineDelay;
+		gint									timelineDuration;
+		gint									timelineMode;
+		gint									timelineRepeat;
+
+		g_assert(data->currentTimeline==NULL);
+
+		/* Get tag's attributes */
+		if(!g_markup_collect_attributes(inElementName,
+											inAttributeNames,
+											inAttributeValues,
+											&error,
+											G_MARKUP_COLLECT_STRING,
+											"delay",
+											&timelineDelayText,
+											G_MARKUP_COLLECT_STRING,
+											"duration",
+											&timelineDurationText,
+											G_MARKUP_COLLECT_STRING,
+											"mode",
+											&timelineModeText,
+											G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL,
+											"repeat",
+											&timelineRepeatText,
+											G_MARKUP_COLLECT_INVALID))
+		{
+			g_propagate_error(outError, error);
+			return;
+		}
+
+		/* Check tag's attributes */
+		if(!timelineDelayText || strlen(timelineDelayText)==0)
+		{
+			_xfdashboard_theme_animation_parse_set_error(data,
+															inContext,
+															outError,
+															XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
+															_("Missing or empty delay at tag '%s'"),
+															inElementName);
+			return;
+		}
+
+		if(!timelineDurationText || strlen(timelineDurationText)==0)
+		{
+			_xfdashboard_theme_animation_parse_set_error(data,
+															inContext,
+															outError,
+															XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
+															_("Missing or empty duration at tag '%s'"),
+															inElementName);
+			return;
+		}
+
+		if(!timelineModeText || strlen(timelineModeText)==0)
+		{
+			_xfdashboard_theme_animation_parse_set_error(data,
+															inContext,
+															outError,
+															XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
+															_("Missing or empty mode at tag '%s'"),
+															inElementName);
+			return;
+		}
+
+		if(timelineRepeatText && strlen(timelineRepeatText)==0)
+		{
+			_xfdashboard_theme_animation_parse_set_error(data,
+															inContext,
+															outError,
+															XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
+															_("Empty repeat at tag '%s'"),
+															inElementName);
+			return;
+		}
+
+		/* Convert tag's attributes' value to usable values */
+		if(!_xfdashboard_theme_animation_string_to_gint(timelineDelayText, &timelineDelay, &error))
+		{
+			g_propagate_error(outError, error);
+			return;
+		}
+
+		if(!_xfdashboard_theme_animation_string_to_gint(timelineDurationText, &timelineDuration, &error))
+		{
+			g_propagate_error(outError, error);
+			return;
+		}
+
+		if(!_xfdashboard_theme_animation_string_to_gint(timelineDelayText, &timelineDelay, &error))
+		{
+			g_propagate_error(outError, error);
+			return;
+		}
+
+		timelineMode=xfdashboard_get_enum_value_from_nickname(CLUTTER_TYPE_ANIMATION_MODE, timelineModeText);
+		if(timelineMode==G_MININT)
+		{
+			/* Set error */
+			g_set_error(outError,
+						XFDASHBOARD_THEME_ANIMATION_ERROR,
+						XFDASHBOARD_THEME_ANIMATION_ERROR_ERROR,
+						_("Invalid mode '%s'"),
+						timelineModeText);
+			return;
+		}
+
+		timelineRepeat=0;
+		if(timelineRepeatText &&
+			!_xfdashboard_theme_animation_string_to_gint(timelineRepeatText, &timelineRepeat, &error))
+		{
+			g_propagate_error(outError, error);
+			return;
+		}
+
+		/* Create new animation timeline with timeline data */
+		data->currentTimeline=clutter_timeline_new(timelineDuration);
+		clutter_timeline_set_delay(data->currentTimeline, timelineDelay);
+		clutter_timeline_set_progress_mode(data->currentTimeline, timelineMode);
+		clutter_timeline_set_repeat_count(data->currentTimeline, timelineRepeat);
+
+		/* Set up context for tag <timeline> */
+		g_markup_parse_context_push(inContext, &propertyParser, inUserData);
+		return;
+	}
+
+	/* If we get here the given element name cannot follow this tag */
+	_xfdashboard_theme_animation_parse_set_error(data,
+													inContext,
+													outError,
+													XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
+													_("Tag <%s> cannot contain tag <%s>"),
+													_xfdashboard_theme_animation_get_tag_by_id(currentTag),
+													inElementName);
+}
+
+static void _xfdashboard_theme_animation_parse_trigger_end(GMarkupParseContext *inContext,
+															const gchar *inElementName,
+															gpointer inUserData,
+															GError **outError)
+{
+	XfdashboardThemeAnimationParserData			*data=(XfdashboardThemeAnimationParserData*)inUserData;
+
+	g_assert(data->currentTimeline);
+
+	/* Add targets to animation specification */
+	g_object_unref(data->currentTimeline);
+	data->currentTimeline=NULL;
+
+	/* Restore previous parser context */
+	g_markup_parse_context_pop(inContext);
+}
+
+/* Parser callbacks for <animations> node */
+static void _xfdashboard_theme_animation_parse_animations_start(GMarkupParseContext *inContext,
+																const gchar *inElementName,
+																const gchar **inAttributeNames,
+																const gchar **inAttributeValues,
+																gpointer inUserData,
+																GError **outError)
+{
+	XfdashboardThemeAnimationParserData			*data=(XfdashboardThemeAnimationParserData*)inUserData;
+	gint										currentTag=TAG_ANIMATIONS;
+	gint										nextTag;
+	GError										*error=NULL;
+
+	/* Update last position for more accurate line and position in error messages */
+	data->lastLine=data->currentLine;
+	data->lastPosition=data->currentPostition;
+	g_markup_parse_context_get_position(inContext, &data->currentLine, &data->currentPostition);
+
+	/* Get tag of next element */
+	nextTag=_xfdashboard_theme_animation_get_tag_by_name(inElementName);
+	if(nextTag==-1)
+	{
+		_xfdashboard_theme_animation_parse_set_error(data,
+													inContext,
+													outError,
+													XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
+													_("Unknown tag <%s>"),
+													inElementName);
+		return;
+	}
+
+	/* Check if element name is <trigger> and follows expected parent tags:
+	 * <animations>
+	 */
+	if(nextTag==TAG_TRIGGER)
+	{
+		static GMarkupParser					propertyParser=
+												{
+													_xfdashboard_theme_animation_parse_trigger_start,
+													_xfdashboard_theme_animation_parse_trigger_end,
+													_xfdashboard_theme_animation_parse_general_no_text_nodes,
+													NULL,
+													NULL,
+												};
+
+		const gchar								*triggerID=NULL;
+		const gchar								*triggerSender=NULL;
+		const gchar								*triggerSignal=NULL;
+		XfdashboardCssSelector					*selector=NULL;
+
+		g_assert(data->currentSpec==NULL);
+
+		/* Get tag's attributes */
+		if(!g_markup_collect_attributes(inElementName,
+											inAttributeNames,
+											inAttributeValues,
+											&error,
+											G_MARKUP_COLLECT_STRING,
+											"id",
+											&triggerID,
+											G_MARKUP_COLLECT_STRING,
+											"sender",
+											&triggerSender,
+											G_MARKUP_COLLECT_STRING,
+											"signal",
+											&triggerSignal,
+											G_MARKUP_COLLECT_INVALID))
+		{
+			g_propagate_error(outError, error);
+			return;
+		}
+
+		/* Check tag's attributes */
+		if(!triggerID || strlen(triggerID)==0)
+		{
+			_xfdashboard_theme_animation_parse_set_error(data,
+															inContext,
+															outError,
+															XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
+															_("Missing or empty ID at tag '%s'"),
+															inElementName);
+			return;
+		}
+
+		if(!triggerSender || strlen(triggerSender)==0)
+		{
+			_xfdashboard_theme_animation_parse_set_error(data,
+															inContext,
+															outError,
+															XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
+															_("Missing or empty sender at tag '%s'"),
+															inElementName);
+			return;
+		}
+
+		if(!triggerSignal || strlen(triggerSignal)==0)
+		{
+			_xfdashboard_theme_animation_parse_set_error(data,
+															inContext,
+															outError,
+															XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
+															_("Missing or empty signal at tag '%s'"),
+															inElementName);
+			return;
+		}
+
+		if(!xfdashboard_is_valid_id(triggerID))
+		{
+			_xfdashboard_theme_animation_parse_set_error(data,
+															inContext,
+															outError,
+															XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
+															_("Invalid ID '%s' at tag '%s'"),
+															triggerID,
+															inElementName);
+			return;
+		}
+
+		if(_xfdashboard_theme_animation_has_id(data->self, data, triggerID))
+		{
+			_xfdashboard_theme_animation_parse_set_error(data,
+															inContext,
+															outError,
+															XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
+															_("Multiple definition of trigger with ID '%s'"),
+															triggerID);
+			return;
+		}
+
+		/* Create new animation specification with trigger data */
+		selector=xfdashboard_css_selector_new_from_string(triggerSender);
+		data->currentSpec=_xfdashboard_theme_animation_spec_new(triggerID, selector, triggerSignal);
+		g_object_unref(selector);
+
+		/* Set up context for tag <trigger> */
+		g_markup_parse_context_push(inContext, &propertyParser, inUserData);
+		return;
+	}
+
+	/* If we get here the given element name cannot follow this tag */
+	_xfdashboard_theme_animation_parse_set_error(data,
+													inContext,
+													outError,
+													XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
+													_("Tag <%s> cannot contain tag <%s>"),
+													_xfdashboard_theme_animation_get_tag_by_id(currentTag),
+													inElementName);
+}
+
+static void _xfdashboard_theme_animation_parse_animations_end(GMarkupParseContext *inContext,
+																const gchar *inElementName,
+																gpointer inUserData,
+																GError **outError)
+{
+	XfdashboardThemeAnimationParserData			*data=(XfdashboardThemeAnimationParserData*)inUserData;
+
+	g_assert(data->currentSpec);
+
+	/* Add animation specification to list of animations */
+	data->specs=g_slist_prepend(data->specs, data->currentSpec);
+	data->currentSpec=NULL;
+
+	/* Restore previous parser context */
+	g_markup_parse_context_pop(inContext);
+}
+
+/* Parser callbacks for document root node */
+static void _xfdashboard_theme_animation_parse_document_start(GMarkupParseContext *inContext,
+																const gchar *inElementName,
+																const gchar **inAttributeNames,
+																const gchar **inAttributeValues,
+																gpointer inUserData,
+																GError **outError)
+{
+	XfdashboardThemeAnimationParserData			*data=(XfdashboardThemeAnimationParserData*)inUserData;
+	gint										currentTag=TAG_DOCUMENT;
+	gint										nextTag;
+	GError										*error=NULL;
+
+	/* Update last position for more accurate line and position in error messages */
+	data->lastLine=data->currentLine;
+	data->lastPosition=data->currentPostition;
+	g_markup_parse_context_get_position(inContext, &data->currentLine, &data->currentPostition);
+
+	/* Get tag of next element */
+	nextTag=_xfdashboard_theme_animation_get_tag_by_name(inElementName);
+	if(nextTag==-1)
+	{
+		_xfdashboard_theme_animation_parse_set_error(data,
+													inContext,
+													outError,
+													XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
+													_("Unknown tag <%s>"),
+													inElementName);
+		return;
+	}
+
+	/* Check if element name is <animations> and follows expected parent tags:
+	 * <document>
+	 */
+	if(nextTag==TAG_ANIMATIONS)
+	{
+		static GMarkupParser					propertyParser=
+												{
+													_xfdashboard_theme_animation_parse_animations_start,
+													_xfdashboard_theme_animation_parse_animations_end,
+													_xfdashboard_theme_animation_parse_general_no_text_nodes,
+													NULL,
+													NULL,
+												};
+
+		/* Get tag's attributes */
+		if(!g_markup_collect_attributes(inElementName,
+											inAttributeNames,
+											inAttributeValues,
+											&error,
+											G_MARKUP_COLLECT_INVALID,
+											NULL))
+		{
+			g_propagate_error(outError, error);
+		}
+
+		/* Set up context for tag <animations> */
+		g_markup_parse_context_push(inContext, &propertyParser, inUserData);
+		return;
+	}
+
+	/* If we get here the given element name cannot follow this tag */
+	_xfdashboard_theme_animation_parse_set_error(data,
+													inContext,
+													outError,
+													XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
+													_("Tag <%s> cannot contain tag <%s>"),
+													_xfdashboard_theme_animation_get_tag_by_id(currentTag),
+													inElementName);
+}
+
+static void _xfdashboard_theme_animation_parse_document_end(GMarkupParseContext *inContext,
+															const gchar *inElementName,
+															gpointer inUserData,
+															GError **outError)
+{
+	/* Restore previous parser context */
+	g_markup_parse_context_pop(inContext);
+}
+
+/* Parse XML from string */
+static gboolean _xfdashboard_theme_animation_parse_xml(XfdashboardThemeAnimation *self,
+														const gchar *inPath,
+														const gchar *inContents,
+														GError **outError)
+{
+	static GMarkupParser					parser=
+											{
+												_xfdashboard_theme_animation_parse_document_start,
+												_xfdashboard_theme_animation_parse_document_end,
+												_xfdashboard_theme_animation_parse_general_no_text_nodes,
+												NULL,
+												NULL,
+											};
+
+	XfdashboardThemeAnimationParserData		*data;
+	GMarkupParseContext						*context;
+	GError									*error;
+	gboolean								success;
+
+	g_return_val_if_fail(XFDASHBOARD_IS_THEME_ANIMATION(self), FALSE);
+	g_return_val_if_fail(inPath && *inPath, FALSE);
+	g_return_val_if_fail(inContents && *inContents, FALSE);
+	g_return_val_if_fail(outError==NULL || *outError==NULL, FALSE);
+
+	error=NULL;
+	success=TRUE;
+
+	/* Create and set up parser instance */
+	data=g_new0(XfdashboardThemeAnimationParserData, 1);
+	if(!data)
+	{
+		/* Set error */
+		g_set_error(outError,
+					XFDASHBOARD_THEME_ANIMATION_ERROR,
+					XFDASHBOARD_THEME_ANIMATION_ERROR_ERROR,
+					_("Could not set up parser data for file %s"),
+					inPath);
+		return(FALSE);
+	}
+
+	context=g_markup_parse_context_new(&parser, 0, data, NULL);
+	if(!context)
+	{
+		/* Set error */
+		g_set_error(outError,
+					XFDASHBOARD_THEME_ANIMATION_ERROR,
+					XFDASHBOARD_THEME_ANIMATION_ERROR_ERROR,
+					_("Could not create parser for file %s"),
+					inPath);
+
+		g_free(data);
+		return(FALSE);
+	}
+
+	/* Now the parser and its context is set up and we can now
+	 * safely initialize data.
+	 */
+	data->self=self;
+	data->specs=NULL;
+	data->currentSpec=NULL;
+	data->currentTimeline=NULL;
+	data->currentTargets=NULL;
+	data->lastLine=1;
+	data->lastPosition=1;
+	data->currentLine=1;
+	data->currentPostition=1;
+
+	/* Parse XML string */
+	if(success && !g_markup_parse_context_parse(context, inContents, -1, &error))
+	{
+		g_propagate_error(outError, error);
+		success=FALSE;
+	}
+
+	if(success && !g_markup_parse_context_end_parse(context, &error))
+	{
+		g_propagate_error(outError, error);
+		success=FALSE;
+	}
+
+	/* Handle collected data if parsing was successful */
+	if(success)
+	{
+		g_slist_foreach(data->specs, (GFunc)_xfdashboard_theme_animation_ref_and_add_to_theme, self);
+	}
+
+	/* Clean up resources */
+#ifdef DEBUG
+	if(!success)
+	{
+		// TODO: g_slist_foreach(data->specs, (GFunc)_xfdashboard_theme_animation_print_parsed_objects, "Animation specs (this file):");
+		// TODO: g_slist_foreach(self->priv->specs, (GFunc)_xfdashboard_theme_animation_print_parsed_objects, "Animation specs (parsed before):");
+		XFDASHBOARD_DEBUG(self, THEME,
+							"PARSER ERROR: %s",
+							(outError && *outError) ? (*outError)->message : "unknown error");
+	}
+#endif
+
+	g_markup_parse_context_free(context);
+
+	g_slist_free_full(data->specs, (GDestroyNotify)_xfdashboard_theme_animation_spec_unref);
+	g_free(data);
+
+	return(success);
+}
+
 
 /* IMPLEMENTATION: GObject */
 
@@ -490,144 +1641,38 @@ XfdashboardThemeAnimation* xfdashboard_theme_animation_new(void)
 	return(XFDASHBOARD_THEME_ANIMATION(g_object_new(XFDASHBOARD_TYPE_THEME_ANIMATION, NULL)));
 }
 
-
 /* Load a XML file into theme */
 gboolean xfdashboard_theme_animation_add_file(XfdashboardThemeAnimation *self,
 												const gchar *inPath,
 												GError **outError)
 {
-	XfdashboardThemeAnimationPrivate			*priv;
-	XfdashboardThemeAnimationSpec				*spec;
-	gchar										*id;
-	gchar										*sender;
-	gchar										*signal;
-	XfdashboardCssSelector						*senderSelector;
-	ClutterTimeline								*timeline;
-	gint										timelineDelay;
-	gint										timelineDuration;
-	guint										timelineMode;
-	gint										timelineRepeat;
-	XfdashboardThemeAnimationTargets			*targets;
-	XfdashboardCssSelector						*targetsSelector;
-	gint										targetsOrigin;
-	XfdashboardThemeAnimationTargetsProperty	*property;
-	gchar										*propertyName;
-	gchar										*propertyFrom;
-	gchar										*propertyTo;
+	gchar								*contents;
+	gsize								contentsLength;
+	GError								*error;
 
 	g_return_val_if_fail(XFDASHBOARD_IS_THEME_ANIMATION(self), FALSE);
 	g_return_val_if_fail(inPath!=NULL && *inPath!=0, FALSE);
 	g_return_val_if_fail(outError==NULL || *outError==NULL, FALSE);
 
-	priv=self->priv;
-
-	/* TODO: Replace with real loading code ;) */
-
-	/* <trigger id="expand-workspace" sender="#workspace-selector-collapse-box" signal="expand"> */
-	id="expand-workspace";
-	sender="XfdashboardCollapseBox";
-	signal="expand";
-
-	senderSelector=xfdashboard_css_selector_new_from_string(sender);
-	spec=_xfdashboard_theme_animation_spec_new(id, senderSelector, signal);
-	g_object_unref(senderSelector);
-
-	/*   <timeline delay="0" duration="500" mode=ease-out-cubic" repeat="0" (optional, default=0) > */
-	timelineDelay=0;
-	timelineDuration=500;
-	timelineMode=CLUTTER_EASE_OUT_CUBIC;
-	timelineRepeat=0;
-
-	timeline=clutter_timeline_new(timelineDuration);
-	clutter_timeline_set_delay(timeline, timelineDelay);
-	clutter_timeline_set_progress_mode(timeline, timelineMode);
-	clutter_timeline_set_repeat_count(timeline, timelineRepeat);
-
-	/*     <apply to="" (optional, default="" -> sender) origin="sender" (optional, default="sender") > */
-	targetsSelector=NULL;
-	targetsOrigin=XFDASHBOARD_THEME_ANIMATION_APPLY_TO_ORIGIN_SENDER;
-
-	targets=_xfdashboard_theme_animation_targets_new(targetsSelector, targetsOrigin, timeline);
-
-	/*       <property name="collapse-fraction" from="" (optional, default="") to="" (optional, default="")> */
-	propertyName="width";
-	propertyFrom=NULL;
-	propertyTo=NULL;
-
-	property=_xfdashboard_theme_animation_targets_property_new(propertyName, propertyFrom, propertyTo);
-
-	/*       </property> */
-	_xfdashboard_theme_animation_targets_add_property(targets, property);
-	_xfdashboard_theme_animation_targets_property_unref(property);
-	property=NULL;
-
-	/*     </apply> */
-	_xfdashboard_theme_animation_spec_add_targets(spec, targets);
-	_xfdashboard_theme_animation_targets_unref(targets);
-	targets=NULL;
-
-	/*   </timeline> */
-	g_object_unref(timeline);
-	timeline=NULL;
-
-	/* </trigger> */
-	priv->specs=g_slist_prepend(priv->specs, spec);
-
-
-
-	/* <trigger id="collapse-workspace" sender="#workspace-selector-collapse-box" signal="collapse"> */
-	id="collapse-workspace";
-	sender="XfdashboardCollapseBox";
-	signal="collapse";
-
-	senderSelector=xfdashboard_css_selector_new_from_string(sender);
-	spec=_xfdashboard_theme_animation_spec_new(id, senderSelector, signal);
-	g_object_unref(senderSelector);
-
-	/*   <timeline delay="0" duration="500" mode=ease-in-cubic" repeat="0" (optional, default=0) > */
-	timelineDelay=0;
-	timelineDuration=500;
-	timelineMode=CLUTTER_EASE_OUT_CUBIC;
-	timelineRepeat=0;
-
-	timeline=clutter_timeline_new(timelineDuration);
-	clutter_timeline_set_delay(timeline, timelineDelay);
-	clutter_timeline_set_progress_mode(timeline, timelineMode);
-	clutter_timeline_set_repeat_count(timeline, timelineRepeat);
-
-	/*     <apply to="" (optional, default="" -> sender) origin="sender" (optional, default="sender") > */
-	targetsSelector=NULL;
-	targetsOrigin=XFDASHBOARD_THEME_ANIMATION_APPLY_TO_ORIGIN_SENDER;
-
-	targets=_xfdashboard_theme_animation_targets_new(targetsSelector, targetsOrigin, timeline);
-
-	/*       <property name="collapse-fraction" from="" (optional, default="") to="" (optional, default="")> */
-	propertyName="width";
-	propertyFrom=NULL;
-	propertyTo=NULL;
-
-	property=_xfdashboard_theme_animation_targets_property_new(propertyName, propertyFrom, propertyTo);
-
-	/*       </property> */
-	_xfdashboard_theme_animation_targets_add_property(targets, property);
-	_xfdashboard_theme_animation_targets_property_unref(property);
-	property=NULL;
-
-	/*     </apply> */
-	_xfdashboard_theme_animation_spec_add_targets(spec, targets);
-	_xfdashboard_theme_animation_targets_unref(targets);
-	targets=NULL;
-
-	/*   </timeline> */
-	g_object_unref(timeline);
-	timeline=NULL;
-
-	/* </trigger> */
-	priv->specs=g_slist_prepend(priv->specs, spec);
+	/* Load XML file, parse it and build objects from file */
+	error=NULL;
+	if(!g_file_get_contents(inPath, &contents, &contentsLength, &error))
+	{
+		g_propagate_error(outError, error);
+		return(FALSE);
+	}
 
+	_xfdashboard_theme_animation_parse_xml(self, inPath, contents, &error);
+	if(error)
+	{
+		g_propagate_error(outError, error);
+		g_free(contents);
+		return(FALSE);
+	}
+	XFDASHBOARD_DEBUG(self, THEME, "Loaded animation file '%s'", inPath);
 
-	/* TODO: Just for testing animations */
-	g_message("Loading animations done!");
+	/* Release allocated resources */
+	g_free(contents);
 
 	/* If we get here loading and parsing XML file was successful
 	 * so return TRUE here
diff --git a/libxfdashboard/theme.c b/libxfdashboard/theme.c
index 69cb1fb..478d997 100644
--- a/libxfdashboard/theme.c
+++ b/libxfdashboard/theme.c
@@ -81,9 +81,17 @@ static GParamSpec* XfdashboardThemeProperties[PROP_LAST]={ 0, };
 /* IMPLEMENTATION: Private variables and methods */
 #define XFDASHBOARD_THEME_SUBPATH						"xfdashboard-1.0"
 #define XFDASHBOARD_THEME_FILE							"xfdashboard.theme"
-#define XFDASHBOARD_THEME_GROUP							"Xfdashboard Theme"
 #define XFDASHBOARD_USER_GLOBAL_CSS_FILE				"global.css"
 
+#define XFDASHBOARD_THEME_GROUP							"Xfdashboard Theme"
+#define XFDASHBOARD_THEME_GROUP_KEY_NAME				"Name"
+#define XFDASHBOARD_THEME_GROUP_KEY_COMMENT				"Comment"
+#define XFDASHBOARD_THEME_GROUP_KEY_STYLE				"Style"
+#define XFDASHBOARD_THEME_GROUP_KEY_LAYOUT				"Layout"
+#define XFDASHBOARD_THEME_GROUP_KEY_EFFECTS				"Effects"
+#define XFDASHBOARD_THEME_GROUP_KEY_ANIMATIONS			"Animations"
+
+
 /* Load theme file and all listed resources in this file */
 static gboolean _xfdashboard_theme_load_resources(XfdashboardTheme *self,
 													GError **outError)
@@ -141,7 +149,7 @@ static gboolean _xfdashboard_theme_load_resources(XfdashboardTheme *self,
 	/* Get display name and notify about property change (regardless of success result) */
 	priv->themeDisplayName=g_key_file_get_locale_string(themeKeyFile,
 														XFDASHBOARD_THEME_GROUP,
-														"Name",
+														XFDASHBOARD_THEME_GROUP_KEY_NAME,
 														NULL,
 														&error);
 	g_object_notify_by_pspec(G_OBJECT(self), XfdashboardThemeProperties[PROP_DISPLAY_NAME]);
@@ -161,7 +169,7 @@ static gboolean _xfdashboard_theme_load_resources(XfdashboardTheme *self,
 	/* Get comment and notify about property change (regardless of success result) */
 	priv->themeComment=g_key_file_get_locale_string(themeKeyFile,
 														XFDASHBOARD_THEME_GROUP,
-														"Comment",
+														XFDASHBOARD_THEME_GROUP_KEY_COMMENT,
 														NULL,
 														&error);
 	g_object_notify_by_pspec(G_OBJECT(self), XfdashboardThemeProperties[PROP_COMMENT]);
@@ -184,7 +192,7 @@ static gboolean _xfdashboard_theme_load_resources(XfdashboardTheme *self,
 	 */
 	resources=g_key_file_get_string_list(themeKeyFile,
 											XFDASHBOARD_THEME_GROUP,
-											"Style",
+											XFDASHBOARD_THEME_GROUP_KEY_STYLE,
 											NULL,
 											&error);
 	if(!resources)
@@ -289,7 +297,7 @@ static gboolean _xfdashboard_theme_load_resources(XfdashboardTheme *self,
 	/* Create XML parser and load layout resources */
 	resources=g_key_file_get_string_list(themeKeyFile,
 											XFDASHBOARD_THEME_GROUP,
-											"Layout",
+											XFDASHBOARD_THEME_GROUP_KEY_LAYOUT,
 											NULL,
 											&error);
 	if(!resources)
@@ -342,12 +350,12 @@ static gboolean _xfdashboard_theme_load_resources(XfdashboardTheme *self,
 	/* Create XML parser and load effect resources which are optional */
 	if(g_key_file_has_key(themeKeyFile,
 							XFDASHBOARD_THEME_GROUP,
-							"Effects",
+							XFDASHBOARD_THEME_GROUP_KEY_EFFECTS,
 							NULL))
 	{
 		resources=g_key_file_get_string_list(themeKeyFile,
 												XFDASHBOARD_THEME_GROUP,
-												"Effects",
+												XFDASHBOARD_THEME_GROUP_KEY_EFFECTS,
 												NULL,
 												&error);
 		if(!resources)
@@ -401,12 +409,12 @@ static gboolean _xfdashboard_theme_load_resources(XfdashboardTheme *self,
 	/* Create XML parser and load animation resources which are optional */
 	if(g_key_file_has_key(themeKeyFile,
 							XFDASHBOARD_THEME_GROUP,
-							"Animation",
+							XFDASHBOARD_THEME_GROUP_KEY_ANIMATIONS,
 							NULL))
 	{
 		resources=g_key_file_get_string_list(themeKeyFile,
 												XFDASHBOARD_THEME_GROUP,
-												"Animation",
+												XFDASHBOARD_THEME_GROUP_KEY_ANIMATIONS,
 												NULL,
 												&error);
 		if(!resources)
@@ -460,9 +468,6 @@ static gboolean _xfdashboard_theme_load_resources(XfdashboardTheme *self,
 	/* Release allocated resources */
 	if(themeKeyFile) g_key_file_free(themeKeyFile);
 
-	/* TODO: Just for testing animations  */
-	xfdashboard_theme_animation_add_file(priv->animation, "dummy", &error);
-
 	/* Return TRUE to indicate success */
 	return(TRUE);
 }
diff --git a/libxfdashboard/utils.c b/libxfdashboard/utils.c
index 2065416..5e75436 100644
--- a/libxfdashboard/utils.c
+++ b/libxfdashboard/utils.c
@@ -750,6 +750,49 @@ gchar* xfdashboard_get_enum_value_name(GType inEnumClass, gint inValue)
 	return(valueName);
 }
 
+/**
+ * xfdashboard_get_enum_value_from_nickname:
+ * @inEnumClass: The #GType of enum class
+ * @inNickname: The nickname for value of enumeration at @inEnumClass
+ *
+ * Returns integer value for nickname @inNickname of
+ * enumeration class @inEnumClass.
+ *
+ * NOTE: %G_MININT will be returned even if nickname was not found but it can
+ *       match the value of enumeration. So do not use this function if
+ *       %G_INTMIN could be a valid value for enumeration
+ *
+ * Return value: An integer value for nickname of enumeration or %G_MININT
+ *               if nickname was not found.
+ */
+gint xfdashboard_get_enum_value_from_nickname(GType inEnumClass, const gchar *inNickname)
+{
+	GEnumClass		*enumClass;
+	GEnumValue		*enumValue;
+	gint			value;
+
+	enumClass=NULL;
+	enumValue=NULL;
+	value=G_MININT;
+
+	/* Reference enum class to keep it alive for transformation */
+	enumClass=g_type_class_ref(inEnumClass);
+
+	/* Get enum value */
+	if(enumClass) enumValue=g_enum_get_value_by_nick(enumClass, inNickname);
+
+	/* Get a copy of value's name if it could be found */
+	if(enumValue)
+	{
+		value=enumValue->value;
+	}
+
+	/* Release allocated resources */
+	if(enumClass) g_type_class_unref(enumClass);
+
+	/* Return integer value */
+	return(value);
+}
 /* Dump actors */
 static void _xfdashboard_dump_actor_print(ClutterActor *inActor, gint inLevel)
 {
diff --git a/libxfdashboard/utils.h b/libxfdashboard/utils.h
index 5064af8..6de0022 100644
--- a/libxfdashboard/utils.h
+++ b/libxfdashboard/utils.h
@@ -133,6 +133,7 @@ gchar** xfdashboard_split_string(const gchar *inString, const gchar *inDelimiter
 gboolean xfdashboard_is_valid_id(const gchar *inString);
 
 gchar* xfdashboard_get_enum_value_name(GType inEnumClass, gint inValue);
+gint xfdashboard_get_enum_value_from_nickname(GType inEnumClass, const gchar *inNickname);
 
 void xfdashboard_dump_actor(ClutterActor *inActor);
 

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the Xfce4-commits mailing list