1 /*
2  * theme-animation: A theme used for building animations by XML files
3  *
4  * Copyright 2012-2020 Stephan Haller <nomad@froevel.de>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  * MA 02110-1301, USA.
20  *
21  *
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 
28 #include <libxfdashboard/theme-animation.h>
29 
30 #include <glib/gi18n-lib.h>
31 #include <glib.h>
32 #include <gio/gio.h>
33 #include <errno.h>
34 
35 #include <libxfdashboard/transition-group.h>
36 #include <libxfdashboard/stylable.h>
37 #include <libxfdashboard/application.h>
38 #include <libxfdashboard/enums.h>
39 #include <libxfdashboard/utils.h>
40 #include <libxfdashboard/compat.h>
41 #include <libxfdashboard/debug.h>
42 
43 
44 /* Define this class in GObject system */
45 struct _XfdashboardThemeAnimationPrivate
46 {
47 	/* Instance related */
48 	GSList			*specs;
49 };
50 
51 G_DEFINE_TYPE_WITH_PRIVATE(XfdashboardThemeAnimation,
52 							xfdashboard_theme_animation,
53 							G_TYPE_OBJECT)
54 
55 
56 /* IMPLEMENTATION: Private variables and methods */
57 #define ENABLE_ANIMATIONS_XFCONF_PROP		"/enable-animations"
58 #define DEFAULT_ENABLE_ANIMATIONS			TRUE
59 
60 enum
61 {
62 	TAG_DOCUMENT,
63 	TAG_ANIMATIONS,
64 	TAG_TRIGGER,
65 	TAG_TIMELINE,
66 	TAG_APPLY,
67 	TAG_PROPERTY
68 };
69 
70 enum
71 {
72 	XFDASHBOARD_THEME_ANIMATION_APPLY_TO_ORIGIN_SENDER=0,
73 	XFDASHBOARD_THEME_ANIMATION_APPLY_TO_ORIGIN_STAGE=1
74 };
75 
76 typedef struct _XfdashboardThemeAnimationSpec				XfdashboardThemeAnimationSpec;
77 struct _XfdashboardThemeAnimationSpec
78 {
79 	gint					refCount;
80 	gchar					*id;
81 	XfdashboardCssSelector	*senderSelector;
82 	gchar					*signal;
83 	GSList					*targets;
84 };
85 
86 typedef struct _XfdashboardThemeAnimationTargets			XfdashboardThemeAnimationTargets;
87 struct _XfdashboardThemeAnimationTargets
88 {
89 	gint					refCount;
90 	XfdashboardCssSelector	*targetSelector;
91 	guint					origin;
92 	ClutterTimeline			*timeline;
93 	GSList					*properties;
94 };
95 
96 typedef struct _XfdashboardThemeAnimationTargetsProperty	XfdashboardThemeAnimationTargetsProperty;
97 struct _XfdashboardThemeAnimationTargetsProperty
98 {
99 	gint					refCount;
100 	gchar					*name;
101 	GValue					from;
102 	GValue					to;
103 };
104 
105 typedef struct _XfdashboardThemeAnimationParserData			XfdashboardThemeAnimationParserData;
106 struct _XfdashboardThemeAnimationParserData
107 {
108 	XfdashboardThemeAnimation			*self;
109 
110 	GSList								*specs;
111 
112 	XfdashboardThemeAnimationSpec		*currentSpec;
113 	ClutterTimeline						*currentTimeline;
114 	XfdashboardThemeAnimationTargets	*currentTargets;
115 
116 	gint								lastLine;
117 	gint								lastPosition;
118 	gint								currentLine;
119 	gint								currentPostition;
120 };
121 
122 
123 /* Create, destroy, ref and unref animation targets data */
_xfdashboard_theme_animation_targets_property_new(const gchar * inPropertyName,const gchar * inPropertyFrom,const gchar * inPropertyTo)124 static XfdashboardThemeAnimationTargetsProperty* _xfdashboard_theme_animation_targets_property_new(const gchar *inPropertyName,
125 																									const gchar *inPropertyFrom,
126 																									const gchar *inPropertyTo)
127 {
128 	XfdashboardThemeAnimationTargetsProperty		*data;
129 
130 	g_return_val_if_fail(inPropertyName && *inPropertyName, NULL);
131 	g_return_val_if_fail(!inPropertyFrom || *inPropertyFrom, NULL);
132 	g_return_val_if_fail(!inPropertyTo || *inPropertyTo, NULL);
133 
134 	/* Create animation targets property data */
135 	data=g_new0(XfdashboardThemeAnimationTargetsProperty, 1);
136 	if(!data)
137 	{
138 		g_critical("Cannot allocate memory for animation targets property '%s'", inPropertyName);
139 		return(NULL);
140 	}
141 
142 	data->refCount=1;
143 	data->name=g_strdup(inPropertyName);
144 
145 	if(inPropertyFrom)
146 	{
147 		g_value_init(&data->from, G_TYPE_STRING);
148 		g_value_set_string(&data->from, inPropertyFrom);
149 	}
150 
151 	if(inPropertyTo)
152 	{
153 		g_value_init(&data->to, G_TYPE_STRING);
154 		g_value_set_string(&data->to, inPropertyTo);
155 	}
156 
157 	return(data);
158 }
159 
_xfdashboard_theme_animation_targets_property_free(XfdashboardThemeAnimationTargetsProperty * inData)160 static void _xfdashboard_theme_animation_targets_property_free(XfdashboardThemeAnimationTargetsProperty *inData)
161 {
162 	g_return_if_fail(inData);
163 
164 	/* Free property data */
165 	if(inData->name) g_free(inData->name);
166 	g_value_unset(&inData->from);
167 	g_value_unset(&inData->to);
168 	g_free(inData);
169 }
170 
_xfdashboard_theme_animation_targets_property_ref(XfdashboardThemeAnimationTargetsProperty * inData)171 static XfdashboardThemeAnimationTargetsProperty* _xfdashboard_theme_animation_targets_property_ref(XfdashboardThemeAnimationTargetsProperty *inData)
172 {
173 	g_return_val_if_fail(inData, NULL);
174 
175 	inData->refCount++;
176 	return(inData);
177 }
178 
_xfdashboard_theme_animation_targets_property_unref(XfdashboardThemeAnimationTargetsProperty * inData)179 static void _xfdashboard_theme_animation_targets_property_unref(XfdashboardThemeAnimationTargetsProperty *inData)
180 {
181 	g_return_if_fail(inData);
182 
183 	if(inData->refCount==1) _xfdashboard_theme_animation_targets_property_free(inData);
184 		else inData->refCount--;
185 }
186 
187 /* Create, destroy, ref and unref animation targets data */
_xfdashboard_theme_animation_targets_new(XfdashboardCssSelector * inTargetSelector,gint inOrigin,ClutterTimeline * inTimelineSource)188 static XfdashboardThemeAnimationTargets* _xfdashboard_theme_animation_targets_new(XfdashboardCssSelector *inTargetSelector,
189 																					gint inOrigin,
190 																					ClutterTimeline *inTimelineSource)
191 {
192 	XfdashboardThemeAnimationTargets		*data;
193 
194 	g_return_val_if_fail(!inTargetSelector || XFDASHBOARD_IS_CSS_SELECTOR(inTargetSelector), NULL);
195 	g_return_val_if_fail(inOrigin>=XFDASHBOARD_THEME_ANIMATION_APPLY_TO_ORIGIN_SENDER && inOrigin<=XFDASHBOARD_THEME_ANIMATION_APPLY_TO_ORIGIN_STAGE, NULL);
196 	g_return_val_if_fail(CLUTTER_IS_TIMELINE(inTimelineSource), NULL);
197 
198 	/* Create animation targets data */
199 	data=g_new0(XfdashboardThemeAnimationTargets, 1);
200 	if(!data)
201 	{
202 		gchar							*selector;
203 
204 		selector=xfdashboard_css_selector_to_string(inTargetSelector);
205 		g_critical("Cannot allocate memory for animation targets data with selector '%s'", selector);
206 		g_free(selector);
207 
208 		return(NULL);
209 	}
210 
211 	data->refCount=1;
212 	data->targetSelector=(inTargetSelector ? g_object_ref(inTargetSelector) : NULL);
213 	data->origin=inOrigin;
214 	data->timeline=g_object_ref(inTimelineSource);
215 	data->properties=NULL;
216 
217 	return(data);
218 }
219 
_xfdashboard_theme_animation_targets_free(XfdashboardThemeAnimationTargets * inData)220 static void _xfdashboard_theme_animation_targets_free(XfdashboardThemeAnimationTargets *inData)
221 {
222 	g_return_if_fail(inData);
223 
224 #ifdef DEBUG
225 	if(inData->refCount>1)
226 	{
227 		g_critical("Freeing animation targets data at %p with a reference counter of %d greater than one",
228 					inData,
229 					inData->refCount);
230 	}
231 #endif
232 
233 	/* Release allocated resources */
234 	if(inData->targetSelector) g_object_unref(inData->targetSelector);
235 	if(inData->timeline) g_object_unref(inData->timeline);
236 	if(inData->properties) g_slist_free_full(inData->properties, (GDestroyNotify)_xfdashboard_theme_animation_targets_property_unref);
237 	g_free(inData);
238 }
239 
_xfdashboard_theme_animation_targets_ref(XfdashboardThemeAnimationTargets * inData)240 static XfdashboardThemeAnimationTargets* _xfdashboard_theme_animation_targets_ref(XfdashboardThemeAnimationTargets *inData)
241 {
242 	g_return_val_if_fail(inData, NULL);
243 
244 	inData->refCount++;
245 	return(inData);
246 }
247 
_xfdashboard_theme_animation_targets_unref(XfdashboardThemeAnimationTargets * inData)248 static void _xfdashboard_theme_animation_targets_unref(XfdashboardThemeAnimationTargets *inData)
249 {
250 	g_return_if_fail(inData);
251 
252 	if(inData->refCount==1) _xfdashboard_theme_animation_targets_free(inData);
253 		else inData->refCount--;
254 }
255 
256 /* Add property name and from-to values to animation targets data */
_xfdashboard_theme_animation_targets_add_property(XfdashboardThemeAnimationTargets * inData,XfdashboardThemeAnimationTargetsProperty * inProperty)257 static void _xfdashboard_theme_animation_targets_add_property(XfdashboardThemeAnimationTargets *inData,
258 																XfdashboardThemeAnimationTargetsProperty *inProperty)
259 {
260 	g_return_if_fail(inData);
261 	g_return_if_fail(inProperty);
262 
263 	/* Add property to animation targets data */
264 	inData->properties=g_slist_prepend(inData->properties, _xfdashboard_theme_animation_targets_property_ref(inProperty));
265 }
266 
267 
268 /* Create, destroy, ref and unref animation specification data */
_xfdashboard_theme_animation_spec_new(const gchar * inID,XfdashboardCssSelector * inSenderSelector,const gchar * inSignal)269 static XfdashboardThemeAnimationSpec* _xfdashboard_theme_animation_spec_new(const gchar *inID,
270 																			XfdashboardCssSelector *inSenderSelector,
271 																			const gchar *inSignal)
272 {
273 	XfdashboardThemeAnimationSpec		*data;
274 
275 	g_return_val_if_fail(inID && *inID, NULL);
276 	g_return_val_if_fail(XFDASHBOARD_IS_CSS_SELECTOR(inSenderSelector), NULL);
277 	g_return_val_if_fail(inSignal && *inSignal, NULL);
278 
279 	/* Create animation specification data */
280 	data=g_new0(XfdashboardThemeAnimationSpec, 1);
281 	if(!data)
282 	{
283 		gchar							*selector;
284 
285 		selector=xfdashboard_css_selector_to_string(inSenderSelector);
286 		g_critical("Cannot allocate memory for animation specification data with sender '%s' and signal '%s'",
287 					selector,
288 					inSignal);
289 		g_free(selector);
290 
291 		return(NULL);
292 	}
293 
294 	data->refCount=1;
295 	data->id=g_strdup(inID);
296 	data->senderSelector=g_object_ref(inSenderSelector);
297 	data->signal=g_strdup(inSignal);
298 	data->targets=NULL;
299 
300 	return(data);
301 }
302 
_xfdashboard_theme_animation_spec_free(XfdashboardThemeAnimationSpec * inData)303 static void _xfdashboard_theme_animation_spec_free(XfdashboardThemeAnimationSpec *inData)
304 {
305 	g_return_if_fail(inData);
306 
307 #ifdef DEBUG
308 	if(inData->refCount>1)
309 	{
310 		g_critical("Freeing animation specification data at %p with a reference counter of %d greater than one",
311 					inData,
312 					inData->refCount);
313 	}
314 #endif
315 
316 	/* Release allocated resources */
317 	if(inData->id) g_free(inData->id);
318 	if(inData->senderSelector) g_object_unref(inData->senderSelector);
319 	if(inData->signal) g_free(inData->signal);
320 	if(inData->targets) g_slist_free_full(inData->targets, (GDestroyNotify)_xfdashboard_theme_animation_targets_unref);
321 	g_free(inData);
322 }
323 
_xfdashboard_theme_animation_spec_ref(XfdashboardThemeAnimationSpec * inData)324 static XfdashboardThemeAnimationSpec* _xfdashboard_theme_animation_spec_ref(XfdashboardThemeAnimationSpec *inData)
325 {
326 	g_return_val_if_fail(inData, NULL);
327 
328 	inData->refCount++;
329 	return(inData);
330 }
331 
_xfdashboard_theme_animation_spec_unref(XfdashboardThemeAnimationSpec * inData)332 static void _xfdashboard_theme_animation_spec_unref(XfdashboardThemeAnimationSpec *inData)
333 {
334 	g_return_if_fail(inData);
335 
336 	if(inData->refCount==1) _xfdashboard_theme_animation_spec_free(inData);
337 		else inData->refCount--;
338 }
339 
340 /* Add a animation target to animation specification */
_xfdashboard_theme_animation_spec_add_targets(XfdashboardThemeAnimationSpec * inData,XfdashboardThemeAnimationTargets * inTargets)341 static void _xfdashboard_theme_animation_spec_add_targets(XfdashboardThemeAnimationSpec *inData,
342 															XfdashboardThemeAnimationTargets *inTargets)
343 {
344 	g_return_if_fail(inData);
345 	g_return_if_fail(inTargets);
346 
347 	/* Add target to specification */
348 	inData->targets=g_slist_prepend(inData->targets, _xfdashboard_theme_animation_targets_ref(inTargets));
349 }
350 
351 /* Find best matching animation specification for sender and signal */
_xfdashboard_theme_animation_find_matching_animation_spec(XfdashboardThemeAnimation * self,XfdashboardStylable * inSender,const gchar * inSignal)352 static XfdashboardThemeAnimationSpec* _xfdashboard_theme_animation_find_matching_animation_spec(XfdashboardThemeAnimation *self,
353 																										XfdashboardStylable *inSender,
354 																										const gchar *inSignal)
355 {
356 	XfdashboardThemeAnimationPrivate			*priv;
357 	GSList										*iter;
358 	gint										score;
359 	XfdashboardThemeAnimationSpec				*spec;
360 	gint										bestScore;
361 	XfdashboardThemeAnimationSpec				*bestAnimation;
362 
363 	g_return_val_if_fail(XFDASHBOARD_IS_THEME_ANIMATION(self), NULL);
364 	g_return_val_if_fail(XFDASHBOARD_IS_STYLABLE(inSender), NULL);
365 	g_return_val_if_fail(inSignal && *inSignal, NULL);
366 
367 	priv=self->priv;
368 	bestScore=0;
369 	bestAnimation=NULL;
370 
371 	/* Iterate through all animation specification and get its score against sender.
372 	 * If the iterated specification gets a higher score than the previous one,
373 	 * remember this specification for return value.
374 	 */
375 	for(iter=priv->specs; iter; iter=g_slist_next(iter))
376 	{
377 		/* Get currently iterated specification */
378 		spec=(XfdashboardThemeAnimationSpec*)iter->data;
379 		if(!spec) continue;
380 
381 		/* Skip animation specification if its signal definition does not match
382 		 * the emitted signal.
383 		 */
384 		if(g_strcmp0(spec->signal, inSignal)!=0) continue;
385 
386 		/* Get score of currently iterated specification against sender.
387 		 * Skip this iterated specification if score is zero or lower.
388 		 */
389 		score=xfdashboard_css_selector_score(spec->senderSelector, inSender);
390 		if(score<=0) continue;
391 
392 		/* If score is higher than the previous best matching one, then release
393 		 * old specificationr remembered and ref new one.
394 		 */
395 		if(score>bestScore)
396 		{
397 			/* Release old remembered specification */
398 			if(bestAnimation) _xfdashboard_theme_animation_spec_unref(bestAnimation);
399 
400 			/* Remember new one and take a reference */
401 			bestScore=score;
402 			bestAnimation=_xfdashboard_theme_animation_spec_ref(spec);
403 		}
404 	}
405 
406 	/* Return best matching animation specification found */
407 	return(bestAnimation);
408 }
409 
410 /* Lookup animation specification for requested ID */
_xfdashboard_theme_animation_find_animation_spec_by_id(XfdashboardThemeAnimation * self,const gchar * inID)411 static XfdashboardThemeAnimationSpec* _xfdashboard_theme_animation_find_animation_spec_by_id(XfdashboardThemeAnimation *self,
412 																									const gchar *inID)
413 {
414 	XfdashboardThemeAnimationPrivate			*priv;
415 	GSList										*iter;
416 	XfdashboardThemeAnimationSpec				*spec;
417 
418 	g_return_val_if_fail(XFDASHBOARD_IS_THEME_ANIMATION(self), NULL);
419 	g_return_val_if_fail(inID && *inID, NULL);
420 
421 	priv=self->priv;
422 
423 	/* Iterate through all animation specification and check if it matches
424 	 * the requested ID.
425 	 */
426 	for(iter=priv->specs; iter; iter=g_slist_next(iter))
427 	{
428 		/* Get currently iterated specification */
429 		spec=(XfdashboardThemeAnimationSpec*)iter->data;
430 		if(!spec) continue;
431 
432 		/* Return animation specification if ID matches requested one */
433 		if(g_strcmp0(spec->id, inID)==0)
434 		{
435 			_xfdashboard_theme_animation_spec_ref(spec);
436 			return(spec);
437 		}
438 	}
439 
440 	/* If we get here no animation specification with requested ID was found */
441 	return(NULL);
442 }
443 
444 /* Find actors matching an animation target data */
_xfdashboard_theme_animation_find_actors_for_animation_targets_traverse_callback(ClutterActor * inActor,gpointer inUserData)445 static gboolean _xfdashboard_theme_animation_find_actors_for_animation_targets_traverse_callback(ClutterActor *inActor, gpointer inUserData)
446 {
447 	GSList		**actors=(GSList**)inUserData;
448 
449 	*actors=g_slist_prepend(*actors, inActor);
450 
451 	return(XFDASHBOARD_TRAVERSAL_CONTINUE);
452 }
453 
_xfdashboard_theme_animation_find_actors_for_animation_targets(XfdashboardThemeAnimation * self,XfdashboardThemeAnimationTargets * inTargetSpec,ClutterActor * inSender)454 static GSList* _xfdashboard_theme_animation_find_actors_for_animation_targets(XfdashboardThemeAnimation *self,
455 																				XfdashboardThemeAnimationTargets *inTargetSpec,
456 																				ClutterActor *inSender)
457 {
458 	GSList									*actors;
459 
460 	g_return_val_if_fail(XFDASHBOARD_IS_THEME_ANIMATION(self), NULL);
461 	g_return_val_if_fail(inTargetSpec, NULL);
462 	g_return_val_if_fail(CLUTTER_IS_ACTOR(inSender), NULL);
463 
464 	/* Traverse through actors beginning at the root node and collect each actor
465 	 * matching the target selector but only if a target selector is set. If
466 	 * target selector is NULL then set up a single-item list containing only
467 	 * the sender actor as list of actors found.
468 	 */
469 	actors=NULL;
470 	if(inTargetSpec->targetSelector)
471 	{
472 		ClutterActor						*rootNode;
473 
474 		/* Depending on origin at animation target data select root node to start
475 		 * traversal and collecting matching actors.
476 		 */
477 		rootNode=NULL;
478 		if(inTargetSpec->origin==XFDASHBOARD_THEME_ANIMATION_APPLY_TO_ORIGIN_SENDER)
479 		{
480 			rootNode=inSender;
481 		}
482 
483 		xfdashboard_traverse_actor(rootNode,
484 									inTargetSpec->targetSelector,
485 									_xfdashboard_theme_animation_find_actors_for_animation_targets_traverse_callback,
486 									&actors);
487 	}
488 		else
489 		{
490 			actors=g_slist_prepend(actors, inSender);
491 		}
492 
493 	/* Correct order in list */
494 	actors=g_slist_reverse(actors);
495 
496 	/* Return list of actors found */
497 	return(actors);
498 }
499 
500 /* Callback to add each successfully parsed animation specifications to list of
501  * known animations of this theme.
502  */
_xfdashboard_theme_animation_ref_and_add_to_theme(gpointer inData,gpointer inUserData)503 static void _xfdashboard_theme_animation_ref_and_add_to_theme(gpointer inData, gpointer inUserData)
504 {
505 	XfdashboardThemeAnimation				*self;
506 	XfdashboardThemeAnimationPrivate		*priv;
507 	XfdashboardThemeAnimationSpec			*spec;
508 
509 	g_return_if_fail(XFDASHBOARD_IS_THEME_ANIMATION(inUserData));
510 	g_return_if_fail(inData);
511 
512 	self=XFDASHBOARD_THEME_ANIMATION(inUserData);
513 	priv=self->priv;
514 	spec=(XfdashboardThemeAnimationSpec*)inData;
515 
516 	/* Increase reference of specified animation specification and add to list
517 	 * of known ones.
518 	 */
519 	priv->specs=g_slist_prepend(priv->specs, _xfdashboard_theme_animation_spec_ref(spec));
520 }
521 
522 /* Check if an animation specification with requested ID exists */
_xfdashboard_theme_animation_has_id(XfdashboardThemeAnimation * self,XfdashboardThemeAnimationParserData * inParserData,const gchar * inID)523 static gboolean _xfdashboard_theme_animation_has_id(XfdashboardThemeAnimation *self,
524 													XfdashboardThemeAnimationParserData *inParserData,
525 													const gchar *inID)
526 {
527 	XfdashboardThemeAnimationPrivate		*priv;
528 	GSList									*ids;
529 	gboolean								hasID;
530 	XfdashboardThemeAnimationSpec			*spec;
531 
532 	g_return_val_if_fail(XFDASHBOARD_IS_THEME_ANIMATION(self), TRUE);
533 
534 	priv=self->priv;
535 	hasID=FALSE;
536 
537 	/* Check that ID to lookup is specified */
538 	g_assert(inID && *inID);
539 
540 	/* Lookup ID first in currently parsed file if specified */
541 	if(inParserData)
542 	{
543 		for(ids=inParserData->specs; !hasID && ids; ids=g_slist_next(ids))
544 		{
545 			spec=(XfdashboardThemeAnimationSpec*)ids->data;
546 			if(spec && g_strcmp0(spec->id, inID)==0) hasID=TRUE;
547 		}
548 	}
549 
550 	/* If ID was not found in currently parsed effects xml file (if specified)
551 	 * continue search in already parsed and known effects.
552 	 */
553 	if(!hasID)
554 	{
555 		for(ids=priv->specs; !hasID && ids; ids=g_slist_next(ids))
556 		{
557 			spec=(XfdashboardThemeAnimationSpec*)ids->data;
558 			if(spec && g_strcmp0(spec->id, inID)==0) hasID=TRUE;
559 		}
560 	}
561 
562 	/* Return lookup result */
563 	return(hasID);
564 }
565 
566 /* Convert string to integer and throw error if conversion failed */
_xfdashboard_theme_animation_string_to_gint(const gchar * inNumberString,gint * outNumber,GError ** outError)567 static gboolean _xfdashboard_theme_animation_string_to_gint(const gchar *inNumberString, gint *outNumber, GError **outError)
568 {
569 	gint64			convertedNumber;
570 	gchar			*outNumberStringEnd;
571 
572 	g_return_val_if_fail(inNumberString && *inNumberString, FALSE);
573 	g_return_val_if_fail(outNumber, FALSE);
574 	g_return_val_if_fail(outError==NULL || *outError==NULL, FALSE);
575 
576 	/* Try to convert string to number */
577 	convertedNumber=g_ascii_strtoll(inNumberString, &outNumberStringEnd, 10);
578 
579 	/* Check if invalid base was specified */
580 	if(errno==EINVAL)
581 	{
582 		/* Set error */
583 		g_set_error(outError,
584 					XFDASHBOARD_THEME_ANIMATION_ERROR,
585 					XFDASHBOARD_THEME_ANIMATION_ERROR_ERROR,
586 					"Invalid base for conversion");
587 		return(FALSE);
588 	}
589 
590 	/* Check if integer is out of range */
591 	if(errno==ERANGE)
592 	{
593 		/* Set error */
594 		g_set_error(outError,
595 					XFDASHBOARD_THEME_ANIMATION_ERROR,
596 					XFDASHBOARD_THEME_ANIMATION_ERROR_ERROR,
597 					"Integer out of range");
598 		return(FALSE);
599 	}
600 
601 	/* If converted integer resulted in zero check if end pointer
602 	 * has moved and does not match start pointer and points to a
603 	 * NULL byte (as NULL-terminated strings must be provided).
604 	 */
605 	if(convertedNumber==0 &&
606 		(outNumberStringEnd==inNumberString || *outNumberStringEnd!=0))
607 	{
608 		/* Set error */
609 		g_set_error(outError,
610 					XFDASHBOARD_THEME_ANIMATION_ERROR,
611 					XFDASHBOARD_THEME_ANIMATION_ERROR_ERROR,
612 					"Cannot convert string '%s' to integer",
613 					inNumberString);
614 		return(FALSE);
615 	}
616 
617 	/* Set converted number - the integer */
618 	*outNumber=((gint)convertedNumber);
619 
620 	/* Return TRUE for successful conversion */
621 	return(TRUE);
622 }
623 
624 /* Helper function to set up GError object in this parser */
_xfdashboard_theme_animation_parse_set_error(XfdashboardThemeAnimationParserData * inParserData,GMarkupParseContext * inContext,GError ** outError,XfdashboardThemeAnimationErrorEnum inCode,const gchar * inFormat,...)625 static void _xfdashboard_theme_animation_parse_set_error(XfdashboardThemeAnimationParserData *inParserData,
626 															GMarkupParseContext *inContext,
627 															GError **outError,
628 															XfdashboardThemeAnimationErrorEnum inCode,
629 															const gchar *inFormat,
630 															...)
631 {
632 	GError		*tempError;
633 	gchar		*message;
634 	va_list		args;
635 
636 	/* Get error message */
637 	va_start(args, inFormat);
638 	message=g_strdup_vprintf(inFormat, args);
639 	va_end(args);
640 
641 	/* Create error object */
642 	tempError=g_error_new_literal(XFDASHBOARD_THEME_ANIMATION_ERROR, inCode, message);
643 	if(inParserData)
644 	{
645 		g_prefix_error(&tempError,
646 						"Error on line %d char %d: ",
647 						inParserData->lastLine,
648 						inParserData->lastPosition);
649 	}
650 
651 	/* Set error */
652 	g_propagate_error(outError, tempError);
653 
654 	/* Release allocated resources */
655 	g_free(message);
656 }
657 
658 /* General callbacks which can be used for any tag */
_xfdashboard_theme_animation_parse_general_no_text_nodes(GMarkupParseContext * inContext,const gchar * inText,gsize inTextLength,gpointer inUserData,GError ** outError)659 static void _xfdashboard_theme_animation_parse_general_no_text_nodes(GMarkupParseContext *inContext,
660 																		const gchar *inText,
661 																		gsize inTextLength,
662 																		gpointer inUserData,
663 																		GError **outError)
664 {
665 	XfdashboardThemeAnimationParserData			*data=(XfdashboardThemeAnimationParserData*)inUserData;
666 	gchar										*realText;
667 
668 	/* Check if text contains only whitespace. If we find any non-whitespace
669 	 * in text then set error.
670 	 */
671 	realText=g_strstrip(g_strdup(inText));
672 	if(*realText)
673 	{
674 		const GSList	*parents;
675 
676 		parents=g_markup_parse_context_get_element_stack(inContext);
677 		if(parents) parents=g_slist_next(parents);
678 
679 		_xfdashboard_theme_animation_parse_set_error(data,
680 														inContext,
681 														outError,
682 														XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
683 														"Unexpected text node '%s' at tag <%s>",
684 														realText,
685 														parents ? (gchar*)parents->data : "document");
686 	}
687 	g_free(realText);
688 }
689 
690 /* Determine tag name and ID */
_xfdashboard_theme_animation_get_tag_by_name(const gchar * inTag)691 static gint _xfdashboard_theme_animation_get_tag_by_name(const gchar *inTag)
692 {
693 	g_return_val_if_fail(inTag && *inTag, -1);
694 
695 	/* Compare string and return type ID */
696 	if(g_strcmp0(inTag, "animations")==0) return(TAG_ANIMATIONS);
697 	if(g_strcmp0(inTag, "trigger")==0) return(TAG_TRIGGER);
698 	if(g_strcmp0(inTag, "timeline")==0) return(TAG_TIMELINE);
699 	if(g_strcmp0(inTag, "apply")==0) return(TAG_APPLY);
700 	if(g_strcmp0(inTag, "property")==0) return(TAG_PROPERTY);
701 
702 	/* If we get here we do not know tag name and return invalid ID */
703 	return(-1);
704 }
705 
_xfdashboard_theme_animation_get_tag_by_id(guint inTagType)706 static const gchar* _xfdashboard_theme_animation_get_tag_by_id(guint inTagType)
707 {
708 	/* Compare ID and return string */
709 	switch(inTagType)
710 	{
711 		case TAG_DOCUMENT:
712 			return("document");
713 
714 		case TAG_ANIMATIONS:
715 			return("animations");
716 
717 		case TAG_TRIGGER:
718 			return("trigger");
719 
720 		case TAG_TIMELINE:
721 			return("timeline");
722 
723 		case TAG_APPLY:
724 			return("apply");
725 
726 		case TAG_PROPERTY:
727 			return("property");
728 
729 		default:
730 			break;
731 	}
732 
733 	/* If we get here we do not know tag name and return NULL */
734 	return(NULL);
735 }
736 
737 /* Parser callbacks for <property> node */
_xfdashboard_theme_animation_parse_property_start(GMarkupParseContext * inContext,const gchar * inElementName,const gchar ** inAttributeNames,const gchar ** inAttributeValues,gpointer inUserData,GError ** outError)738 static void _xfdashboard_theme_animation_parse_property_start(GMarkupParseContext *inContext,
739 																const gchar *inElementName,
740 																const gchar **inAttributeNames,
741 																const gchar **inAttributeValues,
742 																gpointer inUserData,
743 																GError **outError)
744 {
745 	XfdashboardThemeAnimationParserData			*data=(XfdashboardThemeAnimationParserData*)inUserData;
746 	gint										currentTag=TAG_PROPERTY;
747 	gint										nextTag;
748 
749 	/* Update last position for more accurate line and position in error messages */
750 	data->lastLine=data->currentLine;
751 	data->lastPosition=data->currentPostition;
752 	g_markup_parse_context_get_position(inContext, &data->currentLine, &data->currentPostition);
753 
754 	/* Get tag of next element */
755 	nextTag=_xfdashboard_theme_animation_get_tag_by_name(inElementName);
756 	if(nextTag==-1)
757 	{
758 		_xfdashboard_theme_animation_parse_set_error(data,
759 													inContext,
760 													outError,
761 													XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
762 													"Unknown tag <%s>",
763 													inElementName);
764 		return;
765 	}
766 
767 	/* If we get here the given element name cannot follow this tag */
768 	_xfdashboard_theme_animation_parse_set_error(data,
769 													inContext,
770 													outError,
771 													XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
772 													"Tag <%s> cannot contain tag <%s>",
773 													_xfdashboard_theme_animation_get_tag_by_id(currentTag),
774 													inElementName);
775 }
776 
777 /* Parser callbacks for <apply> node */
_xfdashboard_theme_animation_parse_apply_start(GMarkupParseContext * inContext,const gchar * inElementName,const gchar ** inAttributeNames,const gchar ** inAttributeValues,gpointer inUserData,GError ** outError)778 static void _xfdashboard_theme_animation_parse_apply_start(GMarkupParseContext *inContext,
779 																const gchar *inElementName,
780 																const gchar **inAttributeNames,
781 																const gchar **inAttributeValues,
782 																gpointer inUserData,
783 																GError **outError)
784 {
785 	XfdashboardThemeAnimationParserData			*data=(XfdashboardThemeAnimationParserData*)inUserData;
786 	gint										currentTag=TAG_APPLY;
787 	gint										nextTag;
788 	GError										*error=NULL;
789 
790 	/* Update last position for more accurate line and position in error messages */
791 	data->lastLine=data->currentLine;
792 	data->lastPosition=data->currentPostition;
793 	g_markup_parse_context_get_position(inContext, &data->currentLine, &data->currentPostition);
794 
795 	/* Get tag of next element */
796 	nextTag=_xfdashboard_theme_animation_get_tag_by_name(inElementName);
797 	if(nextTag==-1)
798 	{
799 		_xfdashboard_theme_animation_parse_set_error(data,
800 													inContext,
801 													outError,
802 													XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
803 													"Unknown tag <%s>",
804 													inElementName);
805 		return;
806 	}
807 
808 	/* Check if element name is <property> and follows expected parent tags:
809 	 * <apply>
810 	 */
811 	if(nextTag==TAG_PROPERTY)
812 	{
813 		static GMarkupParser						propertyParser=
814 													{
815 														_xfdashboard_theme_animation_parse_property_start,
816 														NULL,
817 														_xfdashboard_theme_animation_parse_general_no_text_nodes,
818 														NULL,
819 														NULL,
820 													};
821 
822 		const gchar									*propertyName=NULL;
823 		const gchar									*propertyFrom=NULL;
824 		const gchar									*propertyTo=NULL;
825 		XfdashboardThemeAnimationTargetsProperty	*property=NULL;
826 
827 		g_assert(data->currentTargets!=NULL);
828 
829 		/* Get tag's attributes */
830 		if(!g_markup_collect_attributes(inElementName,
831 											inAttributeNames,
832 											inAttributeValues,
833 											&error,
834 											G_MARKUP_COLLECT_STRING,
835 											"name",
836 											&propertyName,
837 											G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL,
838 											"from",
839 											&propertyFrom,
840 											G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL,
841 											"to",
842 											&propertyTo,
843 											G_MARKUP_COLLECT_INVALID))
844 		{
845 			g_propagate_error(outError, error);
846 			return;
847 		}
848 
849 		/* Check tag's attributes */
850 		if(strlen(propertyName)==0)
851 		{
852 			_xfdashboard_theme_animation_parse_set_error(data,
853 															inContext,
854 															outError,
855 															XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
856 															"Empty 'name' at tag '%s'",
857 															inElementName);
858 			return;
859 		}
860 
861 		if(propertyFrom && strlen(propertyFrom)==0)
862 		{
863 			_xfdashboard_theme_animation_parse_set_error(data,
864 															inContext,
865 															outError,
866 															XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
867 															"Empty 'from' at tag '%s'",
868 															inElementName);
869 			return;
870 		}
871 
872 		if(propertyTo && strlen(propertyTo)==0)
873 		{
874 			_xfdashboard_theme_animation_parse_set_error(data,
875 															inContext,
876 															outError,
877 															XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
878 															"Empty 'to' at tag '%s'",
879 															inElementName);
880 			return;
881 		}
882 
883 		/* Create new animation property and add to current targets */
884 		property=_xfdashboard_theme_animation_targets_property_new(propertyName, propertyFrom, propertyTo);
885 		_xfdashboard_theme_animation_targets_add_property(data->currentTargets, property);
886 		_xfdashboard_theme_animation_targets_property_unref(property);
887 
888 		/* Set up context for tag <apply> */
889 		g_markup_parse_context_push(inContext, &propertyParser, inUserData);
890 		return;
891 	}
892 
893 	/* If we get here the given element name cannot follow this tag */
894 	_xfdashboard_theme_animation_parse_set_error(data,
895 													inContext,
896 													outError,
897 													XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
898 													"Tag <%s> cannot contain tag <%s>",
899 													_xfdashboard_theme_animation_get_tag_by_id(currentTag),
900 													inElementName);
901 }
902 
_xfdashboard_theme_animation_parse_apply_end(GMarkupParseContext * inContext,const gchar * inElementName,gpointer inUserData,GError ** outError)903 static void _xfdashboard_theme_animation_parse_apply_end(GMarkupParseContext *inContext,
904 															const gchar *inElementName,
905 															gpointer inUserData,
906 															GError **outError)
907 {
908 	/* Restore previous parser context */
909 	g_markup_parse_context_pop(inContext);
910 }
911 
912 /* Parser callbacks for <timeline> node */
_xfdashboard_theme_animation_parse_timeline_start(GMarkupParseContext * inContext,const gchar * inElementName,const gchar ** inAttributeNames,const gchar ** inAttributeValues,gpointer inUserData,GError ** outError)913 static void _xfdashboard_theme_animation_parse_timeline_start(GMarkupParseContext *inContext,
914 																const gchar *inElementName,
915 																const gchar **inAttributeNames,
916 																const gchar **inAttributeValues,
917 																gpointer inUserData,
918 																GError **outError)
919 {
920 	XfdashboardThemeAnimationParserData			*data=(XfdashboardThemeAnimationParserData*)inUserData;
921 	gint										currentTag=TAG_TIMELINE;
922 	gint										nextTag;
923 	GError										*error=NULL;
924 
925 	/* Update last position for more accurate line and position in error messages */
926 	data->lastLine=data->currentLine;
927 	data->lastPosition=data->currentPostition;
928 	g_markup_parse_context_get_position(inContext, &data->currentLine, &data->currentPostition);
929 
930 	/* Get tag of next element */
931 	nextTag=_xfdashboard_theme_animation_get_tag_by_name(inElementName);
932 	if(nextTag==-1)
933 	{
934 		_xfdashboard_theme_animation_parse_set_error(data,
935 													inContext,
936 													outError,
937 													XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
938 													"Unknown tag <%s>",
939 													inElementName);
940 		return;
941 	}
942 
943 	/* Check if element name is <apply> and follows expected parent tags:
944 	 * <timeline>
945 	 */
946 	if(nextTag==TAG_APPLY)
947 	{
948 		static GMarkupParser					propertyParser=
949 												{
950 													_xfdashboard_theme_animation_parse_apply_start,
951 													_xfdashboard_theme_animation_parse_apply_end,
952 													_xfdashboard_theme_animation_parse_general_no_text_nodes,
953 													NULL,
954 													NULL,
955 												};
956 
957 		const gchar								*applyToText=NULL;
958 		XfdashboardCssSelector					*applyTo=NULL;
959 		const gchar								*applyOriginText=NULL;
960 		gint									applyOrigin;
961 
962 		g_assert(data->currentTargets==NULL);
963 
964 		/* Get tag's attributes */
965 		if(!g_markup_collect_attributes(inElementName,
966 											inAttributeNames,
967 											inAttributeValues,
968 											&error,
969 											G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL,
970 											"to",
971 											&applyToText,
972 											G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL,
973 											"origin",
974 											&applyOriginText,
975 											G_MARKUP_COLLECT_INVALID))
976 		{
977 			g_propagate_error(outError, error);
978 			return;
979 		}
980 
981 		/* Check tag's attributes */
982 		if(applyToText && strlen(applyToText)==0)
983 		{
984 			_xfdashboard_theme_animation_parse_set_error(data,
985 															inContext,
986 															outError,
987 															XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
988 															"Empty 'to' at tag '%s'",
989 															inElementName);
990 			return;
991 		}
992 
993 		if(applyOriginText && strlen(applyOriginText)==0)
994 		{
995 			_xfdashboard_theme_animation_parse_set_error(data,
996 															inContext,
997 															outError,
998 															XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
999 															"Empty 'origin' at tag '%s'",
1000 															inElementName);
1001 			return;
1002 		}
1003 
1004 		/* Convert tag's attributes' value to usable values */
1005 		applyOrigin=XFDASHBOARD_THEME_ANIMATION_APPLY_TO_ORIGIN_SENDER;
1006 		if(applyOriginText)
1007 		{
1008 			if(g_strcmp0(applyOriginText, "sender")==0) applyOrigin=XFDASHBOARD_THEME_ANIMATION_APPLY_TO_ORIGIN_SENDER;
1009 				else if(g_strcmp0(applyOriginText, "stage")==0) applyOrigin=XFDASHBOARD_THEME_ANIMATION_APPLY_TO_ORIGIN_STAGE;
1010 				else
1011 				{
1012 					_xfdashboard_theme_animation_parse_set_error(data,
1013 																	inContext,
1014 																	outError,
1015 																	XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
1016 																	"Invalid value '%s' for 'origin' at tag '%s'",
1017 																	applyOriginText,
1018 																	inElementName);
1019 					return;
1020 				}
1021 		}
1022 
1023 		/* Create new animation timeline with timeline data */
1024 		applyTo=NULL;
1025 		if(applyToText)
1026 		{
1027 			applyTo=xfdashboard_css_selector_new_from_string(applyToText);
1028 		}
1029 
1030 		data->currentTargets=_xfdashboard_theme_animation_targets_new(applyTo, applyOrigin, data->currentTimeline);
1031 
1032 		if(applyTo) g_object_unref(applyTo);
1033 
1034 		/* Set up context for tag <apply> */
1035 		g_markup_parse_context_push(inContext, &propertyParser, inUserData);
1036 		return;
1037 	}
1038 
1039 	/* If we get here the given element name cannot follow this tag */
1040 	_xfdashboard_theme_animation_parse_set_error(data,
1041 													inContext,
1042 													outError,
1043 													XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
1044 													"Tag <%s> cannot contain tag <%s>",
1045 													_xfdashboard_theme_animation_get_tag_by_id(currentTag),
1046 													inElementName);
1047 }
1048 
_xfdashboard_theme_animation_parse_timeline_end(GMarkupParseContext * inContext,const gchar * inElementName,gpointer inUserData,GError ** outError)1049 static void _xfdashboard_theme_animation_parse_timeline_end(GMarkupParseContext *inContext,
1050 															const gchar *inElementName,
1051 															gpointer inUserData,
1052 															GError **outError)
1053 {
1054 	XfdashboardThemeAnimationParserData			*data=(XfdashboardThemeAnimationParserData*)inUserData;
1055 
1056 	g_assert(data->currentSpec);
1057 	g_assert(data->currentTargets);
1058 
1059 	/* Add targets to animation specification */
1060 	_xfdashboard_theme_animation_spec_add_targets(data->currentSpec, data->currentTargets);
1061 	_xfdashboard_theme_animation_targets_unref(data->currentTargets);
1062 	data->currentTargets=NULL;
1063 
1064 	/* Restore previous parser context */
1065 	g_markup_parse_context_pop(inContext);
1066 }
1067 
1068 /* Parser callbacks for <trigger> node */
_xfdashboard_theme_animation_parse_trigger_start(GMarkupParseContext * inContext,const gchar * inElementName,const gchar ** inAttributeNames,const gchar ** inAttributeValues,gpointer inUserData,GError ** outError)1069 static void _xfdashboard_theme_animation_parse_trigger_start(GMarkupParseContext *inContext,
1070 																const gchar *inElementName,
1071 																const gchar **inAttributeNames,
1072 																const gchar **inAttributeValues,
1073 																gpointer inUserData,
1074 																GError **outError)
1075 {
1076 	XfdashboardThemeAnimationParserData			*data=(XfdashboardThemeAnimationParserData*)inUserData;
1077 	gint										currentTag=TAG_TRIGGER;
1078 	gint										nextTag;
1079 	GError										*error=NULL;
1080 
1081 	/* Update last position for more accurate line and position in error messages */
1082 	data->lastLine=data->currentLine;
1083 	data->lastPosition=data->currentPostition;
1084 	g_markup_parse_context_get_position(inContext, &data->currentLine, &data->currentPostition);
1085 
1086 	/* Get tag of next element */
1087 	nextTag=_xfdashboard_theme_animation_get_tag_by_name(inElementName);
1088 	if(nextTag==-1)
1089 	{
1090 		_xfdashboard_theme_animation_parse_set_error(data,
1091 													inContext,
1092 													outError,
1093 													XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
1094 													"Unknown tag <%s>",
1095 													inElementName);
1096 		return;
1097 	}
1098 
1099 	/* Check if element name is <timeline> and follows expected parent tags:
1100 	 * <trigger>
1101 	 */
1102 	if(nextTag==TAG_TIMELINE)
1103 	{
1104 		static GMarkupParser					propertyParser=
1105 												{
1106 													_xfdashboard_theme_animation_parse_timeline_start,
1107 													_xfdashboard_theme_animation_parse_timeline_end,
1108 													_xfdashboard_theme_animation_parse_general_no_text_nodes,
1109 													NULL,
1110 													NULL,
1111 												};
1112 
1113 		const gchar								*timelineDelayText=NULL;
1114 		const gchar								*timelineDurationText=NULL;
1115 		const gchar								*timelineModeText=NULL;
1116 		const gchar								*timelineRepeatText=NULL;
1117 		gint									timelineDelay;
1118 		gint									timelineDuration;
1119 		gint									timelineMode;
1120 		gint									timelineRepeat;
1121 
1122 		g_assert(data->currentTimeline==NULL);
1123 
1124 		/* Get tag's attributes */
1125 		if(!g_markup_collect_attributes(inElementName,
1126 											inAttributeNames,
1127 											inAttributeValues,
1128 											&error,
1129 											G_MARKUP_COLLECT_STRING,
1130 											"delay",
1131 											&timelineDelayText,
1132 											G_MARKUP_COLLECT_STRING,
1133 											"duration",
1134 											&timelineDurationText,
1135 											G_MARKUP_COLLECT_STRING,
1136 											"mode",
1137 											&timelineModeText,
1138 											G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL,
1139 											"repeat",
1140 											&timelineRepeatText,
1141 											G_MARKUP_COLLECT_INVALID))
1142 		{
1143 			g_propagate_error(outError, error);
1144 			return;
1145 		}
1146 
1147 		/* Check tag's attributes */
1148 		if(!timelineDelayText || strlen(timelineDelayText)==0)
1149 		{
1150 			_xfdashboard_theme_animation_parse_set_error(data,
1151 															inContext,
1152 															outError,
1153 															XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
1154 															"Missing or empty delay at tag '%s'",
1155 															inElementName);
1156 			return;
1157 		}
1158 
1159 		if(!timelineDurationText || strlen(timelineDurationText)==0)
1160 		{
1161 			_xfdashboard_theme_animation_parse_set_error(data,
1162 															inContext,
1163 															outError,
1164 															XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
1165 															"Missing or empty duration at tag '%s'",
1166 															inElementName);
1167 			return;
1168 		}
1169 
1170 		if(!timelineModeText || strlen(timelineModeText)==0)
1171 		{
1172 			_xfdashboard_theme_animation_parse_set_error(data,
1173 															inContext,
1174 															outError,
1175 															XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
1176 															"Missing or empty mode at tag '%s'",
1177 															inElementName);
1178 			return;
1179 		}
1180 
1181 		if(timelineRepeatText && strlen(timelineRepeatText)==0)
1182 		{
1183 			_xfdashboard_theme_animation_parse_set_error(data,
1184 															inContext,
1185 															outError,
1186 															XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
1187 															"Empty repeat at tag '%s'",
1188 															inElementName);
1189 			return;
1190 		}
1191 
1192 		/* Convert tag's attributes' value to usable values */
1193 		if(!_xfdashboard_theme_animation_string_to_gint(timelineDelayText, &timelineDelay, &error))
1194 		{
1195 			g_propagate_error(outError, error);
1196 			return;
1197 		}
1198 
1199 		if(!_xfdashboard_theme_animation_string_to_gint(timelineDurationText, &timelineDuration, &error))
1200 		{
1201 			g_propagate_error(outError, error);
1202 			return;
1203 		}
1204 
1205 		timelineMode=xfdashboard_get_enum_value_from_nickname(CLUTTER_TYPE_ANIMATION_MODE, timelineModeText);
1206 		if(timelineMode==G_MININT)
1207 		{
1208 			/* Set error */
1209 			g_set_error(outError,
1210 						XFDASHBOARD_THEME_ANIMATION_ERROR,
1211 						XFDASHBOARD_THEME_ANIMATION_ERROR_ERROR,
1212 						"Invalid mode '%s'",
1213 						timelineModeText);
1214 			return;
1215 		}
1216 
1217 		timelineRepeat=0;
1218 		if(timelineRepeatText &&
1219 			!_xfdashboard_theme_animation_string_to_gint(timelineRepeatText, &timelineRepeat, &error))
1220 		{
1221 			g_propagate_error(outError, error);
1222 			return;
1223 		}
1224 
1225 		/* Create new animation timeline with timeline data */
1226 		data->currentTimeline=clutter_timeline_new(timelineDuration);
1227 		clutter_timeline_set_delay(data->currentTimeline, timelineDelay);
1228 		clutter_timeline_set_progress_mode(data->currentTimeline, timelineMode);
1229 		clutter_timeline_set_repeat_count(data->currentTimeline, timelineRepeat);
1230 
1231 		/* Set up context for tag <timeline> */
1232 		g_markup_parse_context_push(inContext, &propertyParser, inUserData);
1233 		return;
1234 	}
1235 
1236 	/* If we get here the given element name cannot follow this tag */
1237 	_xfdashboard_theme_animation_parse_set_error(data,
1238 													inContext,
1239 													outError,
1240 													XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
1241 													"Tag <%s> cannot contain tag <%s>",
1242 													_xfdashboard_theme_animation_get_tag_by_id(currentTag),
1243 													inElementName);
1244 }
1245 
_xfdashboard_theme_animation_parse_trigger_end(GMarkupParseContext * inContext,const gchar * inElementName,gpointer inUserData,GError ** outError)1246 static void _xfdashboard_theme_animation_parse_trigger_end(GMarkupParseContext *inContext,
1247 															const gchar *inElementName,
1248 															gpointer inUserData,
1249 															GError **outError)
1250 {
1251 	XfdashboardThemeAnimationParserData			*data=(XfdashboardThemeAnimationParserData*)inUserData;
1252 
1253 	g_assert(data->currentTimeline);
1254 
1255 	/* Add targets to animation specification */
1256 	g_object_unref(data->currentTimeline);
1257 	data->currentTimeline=NULL;
1258 
1259 	/* Restore previous parser context */
1260 	g_markup_parse_context_pop(inContext);
1261 }
1262 
1263 /* Parser callbacks for <animations> node */
_xfdashboard_theme_animation_parse_animations_start(GMarkupParseContext * inContext,const gchar * inElementName,const gchar ** inAttributeNames,const gchar ** inAttributeValues,gpointer inUserData,GError ** outError)1264 static void _xfdashboard_theme_animation_parse_animations_start(GMarkupParseContext *inContext,
1265 																const gchar *inElementName,
1266 																const gchar **inAttributeNames,
1267 																const gchar **inAttributeValues,
1268 																gpointer inUserData,
1269 																GError **outError)
1270 {
1271 	XfdashboardThemeAnimationParserData			*data=(XfdashboardThemeAnimationParserData*)inUserData;
1272 	gint										currentTag=TAG_ANIMATIONS;
1273 	gint										nextTag;
1274 	GError										*error=NULL;
1275 
1276 	/* Update last position for more accurate line and position in error messages */
1277 	data->lastLine=data->currentLine;
1278 	data->lastPosition=data->currentPostition;
1279 	g_markup_parse_context_get_position(inContext, &data->currentLine, &data->currentPostition);
1280 
1281 	/* Get tag of next element */
1282 	nextTag=_xfdashboard_theme_animation_get_tag_by_name(inElementName);
1283 	if(nextTag==-1)
1284 	{
1285 		_xfdashboard_theme_animation_parse_set_error(data,
1286 													inContext,
1287 													outError,
1288 													XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
1289 													"Unknown tag <%s>",
1290 													inElementName);
1291 		return;
1292 	}
1293 
1294 	/* Check if element name is <trigger> and follows expected parent tags:
1295 	 * <animations>
1296 	 */
1297 	if(nextTag==TAG_TRIGGER)
1298 	{
1299 		static GMarkupParser					propertyParser=
1300 												{
1301 													_xfdashboard_theme_animation_parse_trigger_start,
1302 													_xfdashboard_theme_animation_parse_trigger_end,
1303 													_xfdashboard_theme_animation_parse_general_no_text_nodes,
1304 													NULL,
1305 													NULL,
1306 												};
1307 
1308 		const gchar								*triggerID=NULL;
1309 		const gchar								*triggerSender=NULL;
1310 		const gchar								*triggerSignal=NULL;
1311 		XfdashboardCssSelector					*selector=NULL;
1312 
1313 		g_assert(data->currentSpec==NULL);
1314 
1315 		/* Get tag's attributes */
1316 		if(!g_markup_collect_attributes(inElementName,
1317 											inAttributeNames,
1318 											inAttributeValues,
1319 											&error,
1320 											G_MARKUP_COLLECT_STRING,
1321 											"id",
1322 											&triggerID,
1323 											G_MARKUP_COLLECT_STRING,
1324 											"sender",
1325 											&triggerSender,
1326 											G_MARKUP_COLLECT_STRING,
1327 											"signal",
1328 											&triggerSignal,
1329 											G_MARKUP_COLLECT_INVALID))
1330 		{
1331 			g_propagate_error(outError, error);
1332 			return;
1333 		}
1334 
1335 		/* Check tag's attributes */
1336 		if(!triggerID || strlen(triggerID)==0)
1337 		{
1338 			_xfdashboard_theme_animation_parse_set_error(data,
1339 															inContext,
1340 															outError,
1341 															XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
1342 															"Missing or empty ID at tag '%s'",
1343 															inElementName);
1344 			return;
1345 		}
1346 
1347 		if(!triggerSender || strlen(triggerSender)==0)
1348 		{
1349 			_xfdashboard_theme_animation_parse_set_error(data,
1350 															inContext,
1351 															outError,
1352 															XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
1353 															"Missing or empty sender at tag '%s'",
1354 															inElementName);
1355 			return;
1356 		}
1357 
1358 		if(!triggerSignal || strlen(triggerSignal)==0)
1359 		{
1360 			_xfdashboard_theme_animation_parse_set_error(data,
1361 															inContext,
1362 															outError,
1363 															XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
1364 															"Missing or empty signal at tag '%s'",
1365 															inElementName);
1366 			return;
1367 		}
1368 
1369 		if(!xfdashboard_is_valid_id(triggerID))
1370 		{
1371 			_xfdashboard_theme_animation_parse_set_error(data,
1372 															inContext,
1373 															outError,
1374 															XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
1375 															"Invalid ID '%s' at tag '%s'",
1376 															triggerID,
1377 															inElementName);
1378 			return;
1379 		}
1380 
1381 		if(_xfdashboard_theme_animation_has_id(data->self, data, triggerID))
1382 		{
1383 			_xfdashboard_theme_animation_parse_set_error(data,
1384 															inContext,
1385 															outError,
1386 															XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
1387 															"Multiple definition of trigger with ID '%s'",
1388 															triggerID);
1389 			return;
1390 		}
1391 
1392 		/* Create new animation specification with trigger data */
1393 		selector=xfdashboard_css_selector_new_from_string(triggerSender);
1394 		data->currentSpec=_xfdashboard_theme_animation_spec_new(triggerID, selector, triggerSignal);
1395 		g_object_unref(selector);
1396 
1397 		/* Set up context for tag <trigger> */
1398 		g_markup_parse_context_push(inContext, &propertyParser, inUserData);
1399 		return;
1400 	}
1401 
1402 	/* If we get here the given element name cannot follow this tag */
1403 	_xfdashboard_theme_animation_parse_set_error(data,
1404 													inContext,
1405 													outError,
1406 													XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
1407 													"Tag <%s> cannot contain tag <%s>",
1408 													_xfdashboard_theme_animation_get_tag_by_id(currentTag),
1409 													inElementName);
1410 }
1411 
_xfdashboard_theme_animation_parse_animations_end(GMarkupParseContext * inContext,const gchar * inElementName,gpointer inUserData,GError ** outError)1412 static void _xfdashboard_theme_animation_parse_animations_end(GMarkupParseContext *inContext,
1413 																const gchar *inElementName,
1414 																gpointer inUserData,
1415 																GError **outError)
1416 {
1417 	XfdashboardThemeAnimationParserData			*data=(XfdashboardThemeAnimationParserData*)inUserData;
1418 
1419 	g_assert(data->currentSpec);
1420 
1421 	/* Add animation specification to list of animations */
1422 	data->specs=g_slist_prepend(data->specs, data->currentSpec);
1423 	data->currentSpec=NULL;
1424 
1425 	/* Restore previous parser context */
1426 	g_markup_parse_context_pop(inContext);
1427 }
1428 
1429 /* Parser callbacks for document root node */
_xfdashboard_theme_animation_parse_document_start(GMarkupParseContext * inContext,const gchar * inElementName,const gchar ** inAttributeNames,const gchar ** inAttributeValues,gpointer inUserData,GError ** outError)1430 static void _xfdashboard_theme_animation_parse_document_start(GMarkupParseContext *inContext,
1431 																const gchar *inElementName,
1432 																const gchar **inAttributeNames,
1433 																const gchar **inAttributeValues,
1434 																gpointer inUserData,
1435 																GError **outError)
1436 {
1437 	XfdashboardThemeAnimationParserData			*data=(XfdashboardThemeAnimationParserData*)inUserData;
1438 	gint										currentTag=TAG_DOCUMENT;
1439 	gint										nextTag;
1440 	GError										*error=NULL;
1441 
1442 	/* Update last position for more accurate line and position in error messages */
1443 	data->lastLine=data->currentLine;
1444 	data->lastPosition=data->currentPostition;
1445 	g_markup_parse_context_get_position(inContext, &data->currentLine, &data->currentPostition);
1446 
1447 	/* Get tag of next element */
1448 	nextTag=_xfdashboard_theme_animation_get_tag_by_name(inElementName);
1449 	if(nextTag==-1)
1450 	{
1451 		_xfdashboard_theme_animation_parse_set_error(data,
1452 													inContext,
1453 													outError,
1454 													XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
1455 													"Unknown tag <%s>",
1456 													inElementName);
1457 		return;
1458 	}
1459 
1460 	/* Check if element name is <animations> and follows expected parent tags:
1461 	 * <document>
1462 	 */
1463 	if(nextTag==TAG_ANIMATIONS)
1464 	{
1465 		static GMarkupParser					propertyParser=
1466 												{
1467 													_xfdashboard_theme_animation_parse_animations_start,
1468 													_xfdashboard_theme_animation_parse_animations_end,
1469 													_xfdashboard_theme_animation_parse_general_no_text_nodes,
1470 													NULL,
1471 													NULL,
1472 												};
1473 
1474 		/* Get tag's attributes */
1475 		if(!g_markup_collect_attributes(inElementName,
1476 											inAttributeNames,
1477 											inAttributeValues,
1478 											&error,
1479 											G_MARKUP_COLLECT_INVALID,
1480 											NULL))
1481 		{
1482 			g_propagate_error(outError, error);
1483 		}
1484 
1485 		/* Set up context for tag <animations> */
1486 		g_markup_parse_context_push(inContext, &propertyParser, inUserData);
1487 		return;
1488 	}
1489 
1490 	/* If we get here the given element name cannot follow this tag */
1491 	_xfdashboard_theme_animation_parse_set_error(data,
1492 													inContext,
1493 													outError,
1494 													XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
1495 													"Tag <%s> cannot contain tag <%s>",
1496 													_xfdashboard_theme_animation_get_tag_by_id(currentTag),
1497 													inElementName);
1498 }
1499 
_xfdashboard_theme_animation_parse_document_end(GMarkupParseContext * inContext,const gchar * inElementName,gpointer inUserData,GError ** outError)1500 static void _xfdashboard_theme_animation_parse_document_end(GMarkupParseContext *inContext,
1501 															const gchar *inElementName,
1502 															gpointer inUserData,
1503 															GError **outError)
1504 {
1505 	/* Restore previous parser context */
1506 	g_markup_parse_context_pop(inContext);
1507 }
1508 
1509 /* Parse XML from string */
_xfdashboard_theme_animation_parse_xml(XfdashboardThemeAnimation * self,const gchar * inPath,const gchar * inContents,GError ** outError)1510 static gboolean _xfdashboard_theme_animation_parse_xml(XfdashboardThemeAnimation *self,
1511 														const gchar *inPath,
1512 														const gchar *inContents,
1513 														GError **outError)
1514 {
1515 	static GMarkupParser					parser=
1516 											{
1517 												_xfdashboard_theme_animation_parse_document_start,
1518 												_xfdashboard_theme_animation_parse_document_end,
1519 												_xfdashboard_theme_animation_parse_general_no_text_nodes,
1520 												NULL,
1521 												NULL,
1522 											};
1523 
1524 	XfdashboardThemeAnimationParserData		*data;
1525 	GMarkupParseContext						*context;
1526 	GError									*error;
1527 	gboolean								success;
1528 
1529 	g_return_val_if_fail(XFDASHBOARD_IS_THEME_ANIMATION(self), FALSE);
1530 	g_return_val_if_fail(inPath && *inPath, FALSE);
1531 	g_return_val_if_fail(inContents && *inContents, FALSE);
1532 	g_return_val_if_fail(outError==NULL || *outError==NULL, FALSE);
1533 
1534 	error=NULL;
1535 	success=TRUE;
1536 
1537 	/* Create and set up parser instance */
1538 	data=g_new0(XfdashboardThemeAnimationParserData, 1);
1539 	if(!data)
1540 	{
1541 		/* Set error */
1542 		g_set_error(outError,
1543 					XFDASHBOARD_THEME_ANIMATION_ERROR,
1544 					XFDASHBOARD_THEME_ANIMATION_ERROR_ERROR,
1545 					"Could not set up parser data for file %s",
1546 					inPath);
1547 		return(FALSE);
1548 	}
1549 
1550 	context=g_markup_parse_context_new(&parser, 0, data, NULL);
1551 	if(!context)
1552 	{
1553 		/* Set error */
1554 		g_set_error(outError,
1555 					XFDASHBOARD_THEME_ANIMATION_ERROR,
1556 					XFDASHBOARD_THEME_ANIMATION_ERROR_ERROR,
1557 					"Could not create parser for file %s",
1558 					inPath);
1559 
1560 		g_free(data);
1561 		return(FALSE);
1562 	}
1563 
1564 	/* Now the parser and its context is set up and we can now
1565 	 * safely initialize data.
1566 	 */
1567 	data->self=self;
1568 	data->specs=NULL;
1569 	data->currentSpec=NULL;
1570 	data->currentTimeline=NULL;
1571 	data->currentTargets=NULL;
1572 	data->lastLine=1;
1573 	data->lastPosition=1;
1574 	data->currentLine=1;
1575 	data->currentPostition=1;
1576 
1577 	/* Parse XML string */
1578 	if(success && !g_markup_parse_context_parse(context, inContents, -1, &error))
1579 	{
1580 		g_propagate_error(outError, error);
1581 		success=FALSE;
1582 	}
1583 
1584 	if(success && !g_markup_parse_context_end_parse(context, &error))
1585 	{
1586 		g_propagate_error(outError, error);
1587 		success=FALSE;
1588 	}
1589 
1590 	/* Handle collected data if parsing was successful */
1591 	if(success)
1592 	{
1593 		g_slist_foreach(data->specs, (GFunc)_xfdashboard_theme_animation_ref_and_add_to_theme, self);
1594 	}
1595 
1596 	/* Clean up resources */
1597 #ifdef DEBUG
1598 	if(!success)
1599 	{
1600 		// TODO: g_slist_foreach(data->specs, (GFunc)_xfdashboard_theme_animation_print_parsed_objects, "Animation specs (this file):");
1601 		// TODO: g_slist_foreach(self->priv->specs, (GFunc)_xfdashboard_theme_animation_print_parsed_objects, "Animation specs (parsed before):");
1602 		XFDASHBOARD_DEBUG(self, THEME,
1603 							"PARSER ERROR: %s",
1604 							(outError && *outError) ? (*outError)->message : "unknown error");
1605 	}
1606 #endif
1607 
1608 	g_markup_parse_context_free(context);
1609 
1610 	g_slist_free_full(data->specs, (GDestroyNotify)_xfdashboard_theme_animation_spec_unref);
1611 	g_free(data);
1612 
1613 	return(success);
1614 }
1615 
1616 /* Find matching entry in list of provided default value and set up value if found */
_xfdashboard_theme_animation_find_default_property_value(XfdashboardThemeAnimation * self,XfdashboardAnimationValue ** inDefaultValues,XfdashboardActor * inSender,const gchar * inProperty,ClutterActor * inActor,GValue * ioValue)1617 static gboolean _xfdashboard_theme_animation_find_default_property_value(XfdashboardThemeAnimation *self,
1618 																			XfdashboardAnimationValue **inDefaultValues,
1619 																			XfdashboardActor *inSender,
1620 																			const gchar *inProperty,
1621 																			ClutterActor *inActor,
1622 																			GValue *ioValue)
1623 {
1624 	XfdashboardAnimationValue			*foundValue;
1625 
1626 	g_return_val_if_fail(XFDASHBOARD_IS_THEME_ANIMATION(self), FALSE);
1627 	g_return_val_if_fail(XFDASHBOARD_IS_ACTOR(inSender), FALSE);
1628 	g_return_val_if_fail(inProperty && *inProperty, FALSE);
1629 	g_return_val_if_fail(CLUTTER_IS_ACTOR(inActor), FALSE);
1630 	g_return_val_if_fail(ioValue && G_VALUE_TYPE(ioValue)!=G_TYPE_INVALID, FALSE);
1631 
1632 	foundValue=NULL;
1633 
1634 	/* Skip if actor is not stylable and cannot be matched againt the selectors in values list */
1635 	if(!XFDASHBOARD_IS_STYLABLE(inActor))
1636 	{
1637 		XFDASHBOARD_DEBUG(self, ANIMATION,
1638 							"Actor %s@%p is not stylable and cannot match any selector in list of default values so fail",
1639 							G_OBJECT_TYPE_NAME(inActor),
1640 							inActor);
1641 
1642 		/* Unset value to prevent using it as it could not be converted */
1643 		g_value_unset(ioValue);
1644 
1645 		return(FALSE);
1646 	}
1647 
1648 	/* Iterate through list of values and find best matching entry for given actor */
1649 	if(inDefaultValues)
1650 	{
1651 		XfdashboardAnimationValue		**iter;
1652 		gfloat							iterScore;
1653 		gfloat							foundScore;
1654 
1655 		for(iter=inDefaultValues; *iter; iter++)
1656 		{
1657 			/* Check if currently iterated entry in list of value matches the actor */
1658 			if((*iter)->selector)
1659 			{
1660 				iterScore=xfdashboard_css_selector_score((*iter)->selector, XFDASHBOARD_STYLABLE(inActor));
1661 				if(iterScore<0) continue;
1662 
1663 				/* Check match is the best one with a higher sore as the previous one (if available) */
1664 				if(foundValue && iterScore<=foundScore) continue;
1665 			}
1666 				else if((gpointer)inActor!=(gpointer)inSender) continue;
1667 
1668 			/* Check if entry matches the requested property */
1669 			if(g_strcmp0((*iter)->property, inProperty)!=0) continue;
1670 
1671 			/* Remember this match as best one */
1672 			foundValue=*iter;
1673 			foundScore=iterScore;
1674 		}
1675 	}
1676 
1677 	/* If no match was found, return FALSE here */
1678 	if(!foundValue) return(FALSE);
1679 
1680 	/* Set up value as a match was found */
1681 	if(!g_value_transform(foundValue->value, ioValue))
1682 	{
1683 		g_warning("Could not transform default value of for property '%s' of %s from type %s to %s of class %s",
1684 					foundValue->property,
1685 					G_OBJECT_TYPE_NAME(inActor),
1686 					G_VALUE_TYPE_NAME(foundValue->value),
1687 					G_VALUE_TYPE_NAME(ioValue),
1688 					G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(inActor)));
1689 
1690 		/* Unset value to prevent using it as it could not be converted */
1691 		g_value_unset(ioValue);
1692 
1693 		return(FALSE);
1694 	}
1695 
1696 	/* If we get here, we found a match and could convert the value */
1697 	return(TRUE);
1698 }
1699 
1700 /* Create animation by specification */
_xfdashboard_theme_animation_free_animation_actor_map_hashtable(gpointer inKey,gpointer inValue,gpointer inUserData)1701 static void _xfdashboard_theme_animation_free_animation_actor_map_hashtable(gpointer inKey, gpointer inValue, gpointer inUserData)
1702 {
1703 	g_return_if_fail(inKey);
1704 
1705 	g_slist_free(inKey);
1706 }
1707 
_xfdashboard_theme_animation_create_by_spec(XfdashboardThemeAnimation * self,XfdashboardThemeAnimationSpec * inSpec,XfdashboardActor * inSender,XfdashboardAnimationValue ** inDefaultInitialValues,XfdashboardAnimationValue ** inDefaultFinalValues)1708 static XfdashboardAnimation* _xfdashboard_theme_animation_create_by_spec(XfdashboardThemeAnimation *self,
1709 																			XfdashboardThemeAnimationSpec *inSpec,
1710 																			XfdashboardActor *inSender,
1711 																			XfdashboardAnimationValue **inDefaultInitialValues,
1712 																			XfdashboardAnimationValue **inDefaultFinalValues)
1713 {
1714 	XfdashboardAnimation									*animation;
1715 	XfdashboardAnimationClass								*animationClass;
1716 	GSList													*iterTargets;
1717 	gint													counterTargets;
1718 	GHashTable												*animationActorMap;
1719 #ifdef DEBUG
1720 	gboolean												doDebug=FALSE;
1721 #endif
1722 
1723 	g_return_val_if_fail(XFDASHBOARD_IS_THEME_ANIMATION(self), NULL);
1724 	g_return_val_if_fail(XFDASHBOARD_IS_ACTOR(inSender), NULL);
1725 	g_return_val_if_fail(inSpec, NULL);
1726 
1727 	animation=NULL;
1728 	animationActorMap=NULL;
1729 
1730 	/* Create animation for animation specification */
1731 	animation=g_object_new(XFDASHBOARD_TYPE_ANIMATION,
1732 							"id", inSpec->id,
1733 							NULL);
1734 	if(!animation)
1735 	{
1736 		g_critical("Cannot allocate memory for animation '%s'",
1737 					inSpec->id);
1738 		return(NULL);
1739 	}
1740 
1741 	animationClass=XFDASHBOARD_ANIMATION_GET_CLASS(animation);
1742 	if(!animationClass->add_animation)
1743 	{
1744 		g_warning("Will not be able to add animations to actors as object of type %s does not implement required virtual function XfdashboardAnimation::%s",
1745 					G_OBJECT_TYPE_NAME(self),
1746 					"add_animation");
1747 	}
1748 
1749 	/* Create actor-animation-list-mapping via a hash-table */
1750 	animationActorMap=g_hash_table_new(g_direct_hash, g_direct_equal);
1751 
1752 	/* Iterate through animation targets of animation specification and create
1753 	 * property transition for each target and property found.
1754 	 */
1755 	for(counterTargets=0, iterTargets=inSpec->targets; iterTargets; iterTargets=g_slist_next(iterTargets), counterTargets++)
1756 	{
1757 		XfdashboardThemeAnimationTargets					*targets;
1758 		GSList												*actors;
1759 		GSList												*iterActors;
1760 		gint												counterActors;
1761 
1762 		/* Get currently iterate animation targets */
1763 		targets=(XfdashboardThemeAnimationTargets*)iterTargets->data;
1764 		if(!targets) continue;
1765 
1766 		/* Find targets to apply property transitions to */
1767 		actors=_xfdashboard_theme_animation_find_actors_for_animation_targets(self, targets, CLUTTER_ACTOR(inSender));
1768 		if(!actors) continue;
1769 		XFDASHBOARD_DEBUG(self, ANIMATION,
1770 							"Target #%d of animation specification '%s' applies to %d actors",
1771 							counterTargets,
1772 							inSpec->id,
1773 							g_slist_length(actors));
1774 
1775 		/* Iterate through actor, create a transition group to collect
1776 		 * property transitions at, create a property transition for each
1777 		 * property specified in animation target, determine "from" value
1778 		 * where missing, add property transition to transition group and
1779 		 * finally add transition group with currently iterated actor to
1780 		 * animation object.
1781 		 */
1782 		for(counterActors=0, iterActors=actors; iterActors; iterActors=g_slist_next(iterActors), counterActors++)
1783 		{
1784 			GSList											*iterProperties;
1785 			ClutterActor									*actor;
1786 			int												counterProperties;
1787 
1788 			/* Get actor */
1789 			actor=(ClutterActor*)iterActors->data;
1790 			if(!actor) continue;
1791 
1792 			/* Iterate through properties and create a property transition
1793 			 * with cloned timeline from animation target. Determine "from"
1794 			 * value if missing in animation targets property specification
1795 			 * and add the property transition to transition group.
1796 			 */
1797 			for(counterProperties=0, iterProperties=targets->properties; iterProperties; iterProperties=g_slist_next(iterProperties), counterProperties++)
1798 			{
1799 				XfdashboardThemeAnimationTargetsProperty	*propertyTargetSpec;
1800 				GParamSpec									*propertySpec;
1801 				GValue										fromValue=G_VALUE_INIT;
1802 				GValue										toValue=G_VALUE_INIT;
1803 
1804 				/* Get target's property data to animate */
1805 				propertyTargetSpec=(XfdashboardThemeAnimationTargetsProperty*)iterProperties->data;
1806 				if(!propertyTargetSpec) continue;
1807 
1808 				/* Check if actor has property to animate */
1809 				propertySpec=g_object_class_find_property(G_OBJECT_GET_CLASS(actor), propertyTargetSpec->name);
1810 				if(!propertySpec && CLUTTER_IS_ANIMATABLE(actor))
1811 				{
1812 					propertySpec=clutter_animatable_find_property(CLUTTER_ANIMATABLE(actor), propertyTargetSpec->name);
1813 				}
1814 
1815 				if(!propertySpec)
1816 				{
1817 					g_warning("Cannot create animation '%s' for non-existing property '%s' at actor of type '%s'",
1818 								inSpec->id,
1819 								propertyTargetSpec->name,
1820 								G_OBJECT_TYPE_NAME(actor));
1821 
1822 					/* Skip this property as it does not exist at actor */
1823 					continue;
1824 				}
1825 
1826 				/* If no "from" value is set at target's property data, get
1827 				 * current value. Otherwise convert "from" value from target's
1828 				 * property data to expected type of property.
1829 				 */
1830 				if(G_VALUE_TYPE(&propertyTargetSpec->from)!=G_TYPE_INVALID)
1831 				{
1832 					g_value_init(&fromValue, G_PARAM_SPEC_VALUE_TYPE(propertySpec));
1833 					if(!g_value_transform(&propertyTargetSpec->from, &fromValue))
1834 					{
1835 						g_warning("Could not transform 'from'-value of '%s' for property '%s' to type %s of class %s",
1836 									g_value_get_string(&propertyTargetSpec->from),
1837 									propertyTargetSpec->name,
1838 									g_type_name(G_PARAM_SPEC_VALUE_TYPE(propertySpec)),
1839 									G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(actor)));
1840 
1841 						/* Unset "from" value to skip it, means it will no transition will be
1842 						 * create and it will not be added to transition group.
1843 						 */
1844 						g_value_unset(&fromValue);
1845 					}
1846 #ifdef DEBUG
1847 					if(doDebug)
1848 					{
1849 						gchar	*valueString;
1850 
1851 						valueString=g_strdup_value_contents(&propertyTargetSpec->from);
1852 						XFDASHBOARD_DEBUG(self, ANIMATION,
1853 											"%s 'from'-value %s of type %s for property '%s' to type %s of class %s for target #%d and actor #%d (%s@%p) of animation specification '%s'",
1854 											(G_VALUE_TYPE(&fromValue)!=G_TYPE_INVALID) ? "Converted" : "Could not convert",
1855 											valueString,
1856 											G_VALUE_TYPE_NAME(&propertyTargetSpec->from),
1857 											propertyTargetSpec->name,
1858 											g_type_name(G_PARAM_SPEC_VALUE_TYPE(propertySpec)),
1859 											G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(actor)),
1860 											counterTargets,
1861 											counterActors,
1862 											G_OBJECT_TYPE_NAME(actor),
1863 											actor,
1864 											inSpec->id);
1865 						g_free(valueString);
1866 					}
1867 #endif
1868 				}
1869 					else
1870 					{
1871 						g_value_init(&fromValue, G_PARAM_SPEC_VALUE_TYPE(propertySpec));
1872 						if(inDefaultInitialValues && _xfdashboard_theme_animation_find_default_property_value(self, inDefaultInitialValues, inSender, propertyTargetSpec->name, actor, &fromValue))
1873 						{
1874 #ifdef DEBUG
1875 							if(doDebug)
1876 							{
1877 								gchar	*valueString;
1878 
1879 								valueString=g_strdup_value_contents(&fromValue);
1880 								XFDASHBOARD_DEBUG(self, ANIMATION,
1881 													"Using provided default 'from'-value %s for property '%s' from target #%d and actor #%d (%s@%p) of animation specification '%s' as no 'from' value was specified",
1882 													valueString,
1883 													propertyTargetSpec->name,
1884 													counterTargets,
1885 													counterActors,
1886 													G_OBJECT_TYPE_NAME(actor),
1887 													actor,
1888 													inSpec->id);
1889 								g_free(valueString);
1890 							}
1891 #endif
1892 						}
1893 							else
1894 							{
1895 								g_object_get_property(G_OBJECT(actor),
1896 														propertyTargetSpec->name,
1897 														&fromValue);
1898 #ifdef DEBUG
1899 								if(doDebug)
1900 								{
1901 									gchar	*valueString;
1902 
1903 									valueString=g_strdup_value_contents(&fromValue);
1904 									XFDASHBOARD_DEBUG(self, ANIMATION,
1905 														"Fetching current 'from'-value %s for property '%s' from target #%d and actor #%d (%s@%p) of animation specification '%s' as no 'from' value was specified",
1906 														valueString,
1907 														propertyTargetSpec->name,
1908 														counterTargets,
1909 														counterActors,
1910 														G_OBJECT_TYPE_NAME(actor),
1911 														actor,
1912 														inSpec->id);
1913 									g_free(valueString);
1914 								}
1915 #endif
1916 							}
1917 					}
1918 
1919 				/* If "to" value is set at target's property data, convert it
1920 				 * from target's property data to expected type of property.
1921 				 */
1922 				if(G_VALUE_TYPE(&propertyTargetSpec->to)!=G_TYPE_INVALID)
1923 				{
1924 					g_value_init(&toValue, G_PARAM_SPEC_VALUE_TYPE(propertySpec));
1925 					if(!g_value_transform(&propertyTargetSpec->to, &toValue))
1926 					{
1927 						g_warning("Could not transform 'to'-value of '%s' for property '%s' to type %s of class %s",
1928 									g_value_get_string(&propertyTargetSpec->to),
1929 									propertyTargetSpec->name,
1930 									g_type_name(G_PARAM_SPEC_VALUE_TYPE(propertySpec)),
1931 									G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(actor)));
1932 
1933 						/* Unset "to" value to prevent setting it at transition.
1934 						 * The animation will set a value when starting the
1935 						 * animation.
1936 						 */
1937 						g_value_unset(&toValue);
1938 					}
1939 #ifdef DEBUG
1940 					if(doDebug)
1941 					{
1942 						gchar	*valueString;
1943 
1944 						valueString=g_strdup_value_contents(&propertyTargetSpec->to);
1945 						XFDASHBOARD_DEBUG(self, ANIMATION,
1946 											"%s 'to'-value %s of type %s for property '%s' to type %s of class %s for target #%d and actor #%d (%s@%p) of animation specification '%s'",
1947 											(G_VALUE_TYPE(&toValue)!=G_TYPE_INVALID) ? "Converted" : "Could not convert",
1948 											valueString,
1949 											G_VALUE_TYPE_NAME(&propertyTargetSpec->to),
1950 											propertyTargetSpec->name,
1951 											g_type_name(G_PARAM_SPEC_VALUE_TYPE(propertySpec)),
1952 											G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(actor)),
1953 											counterTargets,
1954 											counterActors,
1955 											G_OBJECT_TYPE_NAME(actor),
1956 											actor,
1957 											inSpec->id);
1958 						g_free(valueString);
1959 					}
1960 #endif
1961 				}
1962 					else
1963 					{
1964 						g_value_init(&toValue, G_PARAM_SPEC_VALUE_TYPE(propertySpec));
1965 						if(inDefaultFinalValues && _xfdashboard_theme_animation_find_default_property_value(self, inDefaultFinalValues, inSender, propertyTargetSpec->name, actor, &toValue))
1966 						{
1967 #ifdef DEBUG
1968 							if(doDebug)
1969 							{
1970 								gchar	*valueString;
1971 
1972 								valueString=g_strdup_value_contents(&toValue);
1973 								XFDASHBOARD_DEBUG(self, ANIMATION,
1974 													"Using provided default 'to'-value %s for property '%s' from target #%d and actor #%d (%s@%p) of animation specification '%s' as no 'to' value was specified",
1975 													valueString,
1976 													propertyTargetSpec->name,
1977 													counterTargets,
1978 													counterActors,
1979 													G_OBJECT_TYPE_NAME(actor),
1980 													actor,
1981 													inSpec->id);
1982 								g_free(valueString);
1983 							}
1984 #endif
1985 						}
1986 					}
1987 
1988 				/* Create property transition for property with cloned timeline
1989 				 * and add this new transition to transition group if from value
1990 				 * is not invalid.
1991 				 */
1992 				if(G_VALUE_TYPE(&fromValue)!=G_TYPE_INVALID)
1993 				{
1994 					ClutterTransition						*propertyTransition;
1995 					GSList									*animationList;
1996 
1997 					/* Create property transition */
1998 					propertyTransition=clutter_property_transition_new(propertyTargetSpec->name);
1999 					if(!propertyTransition)
2000 					{
2001 						g_critical("Cannot allocate memory for transition of property '%s' of animation specification '%s'",
2002 									propertyTargetSpec->name,
2003 									inSpec->id);
2004 
2005 						/* Release allocated resources */
2006 						g_value_unset(&fromValue);
2007 						g_value_unset(&toValue);
2008 
2009 						/* Skip this property transition */
2010 						continue;
2011 					}
2012 
2013 					/* Clone timeline configuration from animation target */
2014 					clutter_timeline_set_duration(CLUTTER_TIMELINE(propertyTransition), clutter_timeline_get_duration(targets->timeline));
2015 					clutter_timeline_set_delay(CLUTTER_TIMELINE(propertyTransition), clutter_timeline_get_delay(targets->timeline));
2016 					clutter_timeline_set_progress_mode(CLUTTER_TIMELINE(propertyTransition), clutter_timeline_get_progress_mode(targets->timeline));
2017 					clutter_timeline_set_repeat_count(CLUTTER_TIMELINE(propertyTransition), clutter_timeline_get_repeat_count(targets->timeline));
2018 
2019 					/* Set "from" value */
2020 					clutter_transition_set_from_value(propertyTransition, &fromValue);
2021 
2022 					/* Set "to" value if valid */
2023 					if(G_VALUE_TYPE(&toValue)!=G_TYPE_INVALID)
2024 					{
2025 						clutter_transition_set_to_value(propertyTransition, &toValue);
2026 					}
2027 
2028 					/* Add animation to list of animations of target actor */
2029 					animationList=g_hash_table_lookup(animationActorMap, actor);
2030 					animationList=g_slist_prepend(animationList, propertyTransition);
2031 					g_hash_table_insert(animationActorMap, actor, animationList);
2032 
2033 					XFDASHBOARD_DEBUG(self, ANIMATION,
2034 										"Created transition for property '%s' at target #%d and actor #%d (%s@%p) of animation specification '%s'",
2035 										propertyTargetSpec->name,
2036 										counterTargets,
2037 										counterActors,
2038 										G_OBJECT_TYPE_NAME(actor),
2039 										actor,
2040 										inSpec->id);
2041 				}
2042 
2043 				/* Release allocated resources */
2044 				g_value_unset(&fromValue);
2045 				g_value_unset(&toValue);
2046 			}
2047 		}
2048 
2049 		/* Release allocated resources */
2050 		g_slist_free(actors);
2051 	}
2052 
2053 	/* Now iterate through actor-animation-list-mapping, create a transition group for each actor
2054 	 * and add its animation to this newly created group.
2055 	 */
2056 	if(animationActorMap)
2057 	{
2058 		GHashTableIter										hashIter;
2059 		ClutterActor										*hashIterActor;
2060 		GSList												*hashIterList;
2061 
2062 		g_hash_table_iter_init(&hashIter, animationActorMap);
2063 		while(g_hash_table_iter_next(&hashIter, (gpointer*)&hashIterActor, (gpointer*)&hashIterList))
2064 		{
2065 			ClutterTransition								*transitionGroup;
2066 			GSList											*listIter;
2067 			guint											groupDuration;
2068 			gint											groupLoop;
2069 
2070 			/* Skip empty but warn */
2071 			if(!hashIterList)
2072 			{
2073 				g_critical("Empty animation list when creating animation for animation specification '%s'",
2074 							inSpec->id);
2075 				continue;
2076 			}
2077 
2078 			/* Create transition group to collect property transitions at */
2079 			transitionGroup=xfdashboard_transition_group_new();
2080 			if(!transitionGroup)
2081 			{
2082 				g_critical("Cannot allocate memory for transition group of animation specification '%s'",
2083 							inSpec->id);
2084 
2085 				/* Release allocated resources */
2086 				g_hash_table_foreach(animationActorMap, _xfdashboard_theme_animation_free_animation_actor_map_hashtable, NULL);
2087 				g_hash_table_destroy(animationActorMap);
2088 				g_object_unref(animation);
2089 
2090 				return(NULL);
2091 			}
2092 
2093 			XFDASHBOARD_DEBUG(self, ANIMATION,
2094 								"Created transition group at %p for %d properties for actor %s@%p of animation specification '%s'",
2095 								transitionGroup,
2096 								g_slist_length(hashIterList),
2097 								G_OBJECT_TYPE_NAME(hashIterActor),
2098 								hashIterActor,
2099 								inSpec->id);
2100 
2101 			/* Add animations to transition group */
2102 			groupDuration=0;
2103 			groupLoop=0;
2104 			for(listIter=hashIterList; listIter; listIter=g_slist_next(listIter))
2105 			{
2106 				ClutterTransition							*transition;
2107 				gint										transitionLoop;
2108 				guint										transitionDuration;
2109 
2110 				/* Get transition to add */
2111 				transition=(ClutterTransition*)listIter->data;
2112 				if(!transition) continue;
2113 
2114 				/* Adjust timeline values for transition group to play animation fully */
2115 				transitionDuration=clutter_timeline_get_delay(CLUTTER_TIMELINE(transition))+clutter_timeline_get_duration(CLUTTER_TIMELINE(transition));
2116 				if(transitionDuration>groupDuration) groupDuration=transitionDuration;
2117 
2118 				transitionLoop=0;
2119 				if(groupLoop>=0)
2120 				{
2121 					transitionLoop=clutter_timeline_get_repeat_count(CLUTTER_TIMELINE(transition));
2122 					if(transitionLoop>groupLoop) groupLoop=transitionLoop;
2123 				}
2124 
2125 				/* Add property transition to transition group */
2126 				xfdashboard_transition_group_add_transition(XFDASHBOARD_TRANSITION_GROUP(transitionGroup), transition);
2127 
2128 				XFDASHBOARD_DEBUG(self, ANIMATION,
2129 									"Added transition %s@%p (duration %u ms in %d loops) for actor %s@%p of animation specification '%s' to group %s@%p",
2130 									G_OBJECT_TYPE_NAME(transition),
2131 									transition,
2132 									transitionDuration,
2133 									transitionLoop,
2134 									G_OBJECT_TYPE_NAME(hashIterActor),
2135 									hashIterActor,
2136 									inSpec->id,
2137 									G_OBJECT_TYPE_NAME(transitionGroup),
2138 									transitionGroup);
2139 			}
2140 
2141 			/* Set up timeline configuration for transition group of target actor */
2142 			clutter_timeline_set_duration(CLUTTER_TIMELINE(transitionGroup), groupDuration);
2143 			clutter_timeline_set_delay(CLUTTER_TIMELINE(transitionGroup), 0);
2144 			clutter_timeline_set_progress_mode(CLUTTER_TIMELINE(transitionGroup), CLUTTER_LINEAR);
2145 			clutter_timeline_set_repeat_count(CLUTTER_TIMELINE(transitionGroup), groupLoop);
2146 
2147 			XFDASHBOARD_DEBUG(self, ANIMATION,
2148 								"Set up timeline of group %s@%p with duration of %u ms and %d loops for actor %s@%p",
2149 								G_OBJECT_TYPE_NAME(transitionGroup),
2150 								transitionGroup,
2151 								groupDuration,
2152 								groupLoop,
2153 								G_OBJECT_TYPE_NAME(hashIterActor),
2154 								hashIterActor);
2155 
2156 			/* Add transition group with collected property transitions
2157 			 * to actor.
2158 			 */
2159 			if(animationClass->add_animation)
2160 			{
2161 				animationClass->add_animation(animation, hashIterActor, transitionGroup);
2162 				XFDASHBOARD_DEBUG(self, ANIMATION,
2163 									"Added transition group %s@%p to actor %s@%p of animation specification '%s'",
2164 									G_OBJECT_TYPE_NAME(transitionGroup),
2165 									transitionGroup,
2166 									G_OBJECT_TYPE_NAME(hashIterActor),
2167 									hashIterActor,
2168 									inSpec->id);
2169 			}
2170 
2171 			/* Release allocated resources */
2172 			g_object_unref(transitionGroup);
2173 			g_slist_free_full(hashIterList, g_object_unref);
2174 		}
2175 
2176 		/* Destroy hash table */
2177 		g_hash_table_destroy(animationActorMap);
2178 	}
2179 
2180 	return(animation);
2181 }
2182 
2183 
2184 /* IMPLEMENTATION: GObject */
2185 
2186 /* Dispose this object */
_xfdashboard_theme_animation_dispose(GObject * inObject)2187 static void _xfdashboard_theme_animation_dispose(GObject *inObject)
2188 {
2189 	XfdashboardThemeAnimation				*self=XFDASHBOARD_THEME_ANIMATION(inObject);
2190 	XfdashboardThemeAnimationPrivate		*priv=self->priv;
2191 
2192 	/* Release allocated resources */
2193 	if(priv->specs)
2194 	{
2195 		g_slist_free_full(priv->specs, (GDestroyNotify)_xfdashboard_theme_animation_spec_unref);
2196 		priv->specs=NULL;
2197 	}
2198 
2199 	/* Call parent's class dispose method */
2200 	G_OBJECT_CLASS(xfdashboard_theme_animation_parent_class)->dispose(inObject);
2201 }
2202 
2203 /* Class initialization
2204  * Override functions in parent classes and define properties
2205  * and signals
2206  */
xfdashboard_theme_animation_class_init(XfdashboardThemeAnimationClass * klass)2207 void xfdashboard_theme_animation_class_init(XfdashboardThemeAnimationClass *klass)
2208 {
2209 	GObjectClass		*gobjectClass=G_OBJECT_CLASS(klass);
2210 
2211 	/* Override functions */
2212 	gobjectClass->dispose=_xfdashboard_theme_animation_dispose;
2213 }
2214 
2215 /* Object initialization
2216  * Create private structure and set up default values
2217  */
xfdashboard_theme_animation_init(XfdashboardThemeAnimation * self)2218 void xfdashboard_theme_animation_init(XfdashboardThemeAnimation *self)
2219 {
2220 	XfdashboardThemeAnimationPrivate		*priv;
2221 
2222 	priv=self->priv=xfdashboard_theme_animation_get_instance_private(self);
2223 
2224 	/* Set default values */
2225 	priv->specs=NULL;
2226 }
2227 
2228 /* IMPLEMENTATION: Errors */
2229 
xfdashboard_theme_animation_error_quark(void)2230 GQuark xfdashboard_theme_animation_error_quark(void)
2231 {
2232 	return(g_quark_from_static_string("xfdashboard-theme-animation-error-quark"));
2233 }
2234 
2235 /* IMPLEMENTATION: Public API */
2236 
2237 /* Create new instance */
xfdashboard_theme_animation_new(void)2238 XfdashboardThemeAnimation* xfdashboard_theme_animation_new(void)
2239 {
2240 	return(XFDASHBOARD_THEME_ANIMATION(g_object_new(XFDASHBOARD_TYPE_THEME_ANIMATION, NULL)));
2241 }
2242 
2243 /* Load a XML file into theme */
xfdashboard_theme_animation_add_file(XfdashboardThemeAnimation * self,const gchar * inPath,GError ** outError)2244 gboolean xfdashboard_theme_animation_add_file(XfdashboardThemeAnimation *self,
2245 												const gchar *inPath,
2246 												GError **outError)
2247 {
2248 	gchar								*contents;
2249 	gsize								contentsLength;
2250 	GError								*error;
2251 
2252 	g_return_val_if_fail(XFDASHBOARD_IS_THEME_ANIMATION(self), FALSE);
2253 	g_return_val_if_fail(inPath!=NULL && *inPath!=0, FALSE);
2254 	g_return_val_if_fail(outError==NULL || *outError==NULL, FALSE);
2255 
2256 	/* Load XML file, parse it and build objects from file */
2257 	error=NULL;
2258 	if(!g_file_get_contents(inPath, &contents, &contentsLength, &error))
2259 	{
2260 		g_propagate_error(outError, error);
2261 		return(FALSE);
2262 	}
2263 
2264 	_xfdashboard_theme_animation_parse_xml(self, inPath, contents, &error);
2265 	if(error)
2266 	{
2267 		g_propagate_error(outError, error);
2268 		g_free(contents);
2269 		return(FALSE);
2270 	}
2271 	XFDASHBOARD_DEBUG(self, THEME, "Loaded animation file '%s'", inPath);
2272 
2273 	/* Release allocated resources */
2274 	g_free(contents);
2275 
2276 	/* If we get here loading and parsing XML file was successful
2277 	 * so return TRUE here
2278 	 */
2279 	return(TRUE);
2280 }
2281 
2282 /* Build requested animation for sender and its signal */
xfdashboard_theme_animation_create(XfdashboardThemeAnimation * self,XfdashboardActor * inSender,const gchar * inSignal,XfdashboardAnimationValue ** inDefaultInitialValues,XfdashboardAnimationValue ** inDefaultFinalValues)2283 XfdashboardAnimation* xfdashboard_theme_animation_create(XfdashboardThemeAnimation *self,
2284 															XfdashboardActor *inSender,
2285 															const gchar *inSignal,
2286 															XfdashboardAnimationValue **inDefaultInitialValues,
2287 															XfdashboardAnimationValue **inDefaultFinalValues)
2288 {
2289 	XfdashboardThemeAnimationSpec							*spec;
2290 	XfdashboardAnimation									*animation;
2291 	gboolean												animationEnabled;
2292 
2293 	g_return_val_if_fail(XFDASHBOARD_IS_THEME_ANIMATION(self), NULL);
2294 	g_return_val_if_fail(XFDASHBOARD_IS_ACTOR(inSender), NULL);
2295 	g_return_val_if_fail(inSignal && *inSignal, NULL);
2296 
2297 	animation=NULL;
2298 
2299 	/* Check if user wants animation at all. If user does not want any animation,
2300 	 * return NULL.
2301 	 */
2302 	animationEnabled=xfconf_channel_get_bool(xfdashboard_application_get_xfconf_channel(NULL),
2303 												ENABLE_ANIMATIONS_XFCONF_PROP,
2304 												DEFAULT_ENABLE_ANIMATIONS);
2305 	if(!animationEnabled)
2306 	{
2307 		XFDASHBOARD_DEBUG(self, ANIMATION,
2308 							"User disabled animations so do not lookup animation for sender '%s' and signal '%s'",
2309 							G_OBJECT_TYPE_NAME(inSender),
2310 							inSignal);
2311 
2312 		/* Return NULL as user does not want any animation */
2313 		return(NULL);
2314 	}
2315 
2316 	/* Get best matching animation specification for sender and signal.
2317 	 * If no matching animation specification is found, return the empty one.
2318 	 */
2319 	spec=_xfdashboard_theme_animation_find_matching_animation_spec(self, XFDASHBOARD_STYLABLE(inSender), inSignal);
2320 	if(!spec)
2321 	{
2322 		XFDASHBOARD_DEBUG(self, ANIMATION,
2323 							"Could not find an animation specification for sender '%s' and signal '%s'",
2324 							G_OBJECT_TYPE_NAME(inSender),
2325 							inSignal);
2326 
2327 		/* Return NULL as no matching animation specification was found */
2328 		return(NULL);
2329 	}
2330 
2331 	XFDASHBOARD_DEBUG(self, ANIMATION,
2332 						"Found animation specification '%s' for sender '%s' and signal '%s' with %d targets",
2333 						spec->id,
2334 						G_OBJECT_TYPE_NAME(inSender),
2335 						inSignal,
2336 						g_slist_length(spec->targets));
2337 
2338 	/* Create animation for found animation specification */
2339 	animation=_xfdashboard_theme_animation_create_by_spec(self, spec, inSender, inDefaultInitialValues, inDefaultFinalValues);
2340 
2341 	/* Release allocated resources */
2342 	if(spec) _xfdashboard_theme_animation_spec_unref(spec);
2343 
2344 	return(animation);
2345 }
2346 
2347 /* Build requested animation by animation ID */
xfdashboard_theme_animation_create_by_id(XfdashboardThemeAnimation * self,XfdashboardActor * inSender,const gchar * inID,XfdashboardAnimationValue ** inDefaultInitialValues,XfdashboardAnimationValue ** inDefaultFinalValues)2348 XfdashboardAnimation* xfdashboard_theme_animation_create_by_id(XfdashboardThemeAnimation *self,
2349 																XfdashboardActor *inSender,
2350 																const gchar *inID,
2351 																XfdashboardAnimationValue **inDefaultInitialValues,
2352 																XfdashboardAnimationValue **inDefaultFinalValues)
2353 {
2354 	XfdashboardThemeAnimationSpec							*spec;
2355 	XfdashboardAnimation									*animation;
2356 	gboolean												animationEnabled;
2357 
2358 	g_return_val_if_fail(XFDASHBOARD_IS_THEME_ANIMATION(self), NULL);
2359 	g_return_val_if_fail(XFDASHBOARD_IS_ACTOR(inSender), NULL);
2360 	g_return_val_if_fail(inID && *inID, NULL);
2361 
2362 	animation=NULL;
2363 
2364 	/* Check if user wants animation at all. If user does not want any animation,
2365 	 * return NULL.
2366 	 */
2367 	animationEnabled=xfconf_channel_get_bool(xfdashboard_application_get_xfconf_channel(NULL),
2368 												ENABLE_ANIMATIONS_XFCONF_PROP,
2369 												DEFAULT_ENABLE_ANIMATIONS);
2370 	if(!animationEnabled)
2371 	{
2372 		XFDASHBOARD_DEBUG(self, ANIMATION,
2373 							"User disabled animations so do not lookup animation with ID '%s'",
2374 							inID);
2375 
2376 		/* Return NULL as user does not want any animation */
2377 		return(NULL);
2378 	}
2379 
2380 	/* Find animation specification by looking up requested ID.
2381 	 * If no matching animation specification is found, return the empty one.
2382 	 */
2383 	spec=_xfdashboard_theme_animation_find_animation_spec_by_id(self, inID);
2384 	if(!spec)
2385 	{
2386 		XFDASHBOARD_DEBUG(self, ANIMATION,
2387 							"Could not find an animation specification with ID '%s'",
2388 							inID);
2389 
2390 		/* Return NULL as no matching animation specification was found */
2391 		return(NULL);
2392 	}
2393 
2394 	XFDASHBOARD_DEBUG(self, ANIMATION,
2395 						"Found animation specification '%s'  with %d targets",
2396 						spec->id,
2397 						g_slist_length(spec->targets));
2398 
2399 	/* Create animation for found animation specification */
2400 	animation=_xfdashboard_theme_animation_create_by_spec(self, spec, inSender, inDefaultInitialValues, inDefaultFinalValues);
2401 
2402 	/* Release allocated resources */
2403 	if(spec) _xfdashboard_theme_animation_spec_unref(spec);
2404 
2405 	return(animation);
2406 }
2407 
2408 /* Look up ID of animation specification for sender and its signal*/
xfdashboard_theme_animation_lookup_id(XfdashboardThemeAnimation * self,XfdashboardActor * inSender,const gchar * inSignal)2409 gchar* xfdashboard_theme_animation_lookup_id(XfdashboardThemeAnimation *self,
2410 												XfdashboardActor *inSender,
2411 												const gchar *inSignal)
2412 {
2413 	XfdashboardThemeAnimationSpec							*spec;
2414 	gchar													*id;
2415 
2416 	g_return_val_if_fail(XFDASHBOARD_IS_THEME_ANIMATION(self), NULL);
2417 	g_return_val_if_fail(XFDASHBOARD_IS_ACTOR(inSender), NULL);
2418 	g_return_val_if_fail(inSignal && *inSignal, NULL);
2419 
2420 	id=NULL;
2421 
2422 	/* Get best matching animation specification for sender and signal.
2423 	 * If no matching animation specification is found, return the empty one.
2424 	 */
2425 	spec=_xfdashboard_theme_animation_find_matching_animation_spec(self, XFDASHBOARD_STYLABLE(inSender), inSignal);
2426 	if(spec)
2427 	{
2428 		/* Get ID of animation specification to return */
2429 		id=g_strdup(spec->id);
2430 
2431 		/* Release allocated resources */
2432 		_xfdashboard_theme_animation_spec_unref(spec);
2433 	}
2434 
2435 	/* Return found ID or NULL */
2436 	return(id);
2437 }
2438