1 /*
2  *  Authors: Rodney Dawes <dobey@ximian.com>
3  *  Bastien Nocera <hadess@hadess.net>
4  *
5  *  Copyright 2003-2006 Novell, Inc. (www.novell.com)
6  *  Copyright 2011 Red Hat Inc.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include <gio/gio.h>
23 #include <string.h>
24 #include <libxml/parser.h>
25 #include <gdesktop-enums.h>
26 
27 #include "gdesktop-enums-types.h"
28 #include "cc-background-item.h"
29 #include "cc-background-xml.h"
30 
31 /* The number of items we signal as "added" before
32  * returning to the main loop */
33 #define NUM_ITEMS_PER_BATCH 1
34 
35 struct _CcBackgroundXml
36 {
37   GObject      parent_instance;
38 
39   GHashTable  *wp_hash;
40   GAsyncQueue *item_added_queue;
41   guint        item_added_id;
42   GSList      *monitors; /* GSList of GFileMonitor */
43 };
44 
45 enum {
46 	ADDED,
47 	LAST_SIGNAL
48 };
49 
50 static guint signals[LAST_SIGNAL] = { 0 };
51 
G_DEFINE_TYPE(CcBackgroundXml,cc_background_xml,G_TYPE_OBJECT)52 G_DEFINE_TYPE (CcBackgroundXml, cc_background_xml, G_TYPE_OBJECT)
53 
54 static gboolean
55 cc_background_xml_get_bool (const xmlNode *parent,
56 			    const gchar   *prop_name)
57 {
58   xmlChar *prop;
59   gboolean ret_val = FALSE;
60 
61   g_return_val_if_fail (parent != NULL, FALSE);
62   g_return_val_if_fail (prop_name != NULL, FALSE);
63 
64   prop = xmlGetProp ((xmlNode *) parent, (xmlChar*)prop_name);
65   if (prop != NULL) {
66     if (!g_ascii_strcasecmp ((gchar *)prop, "true") || !g_ascii_strcasecmp ((gchar *)prop, "1")) {
67       ret_val = TRUE;
68     } else {
69       ret_val = FALSE;
70     }
71     xmlFree (prop);
72   }
73 
74   return ret_val;
75 }
76 
77 static struct {
78 	int value;
79 	const char *string;
80 } lookups[] = {
81 	{ G_DESKTOP_BACKGROUND_SHADING_HORIZONTAL, "horizontal-gradient" },
82 	{ G_DESKTOP_BACKGROUND_SHADING_VERTICAL, "vertical-gradient" },
83 };
84 
85 static int
enum_string_to_value(GType type,const char * string)86 enum_string_to_value (GType type,
87 		      const char *string)
88 {
89 	GEnumClass *eclass;
90 	GEnumValue *value;
91 
92 	eclass = G_ENUM_CLASS (g_type_class_peek (type));
93 	value = g_enum_get_value_by_nick (eclass, string);
94 
95 	/* Here's a bit of hand-made parsing, bad bad */
96 	if (value == NULL) {
97 		guint i;
98 		for (i = 0; i < G_N_ELEMENTS (lookups); i++) {
99 			if (g_str_equal (lookups[i].string, string))
100 				return lookups[i].value;
101 		}
102 		g_warning ("Unhandled value '%s' for enum '%s'",
103 			   string, G_FLAGS_CLASS_TYPE_NAME (eclass));
104 		return 0;
105 	}
106 
107 	return value->value;
108 }
109 
110 static gboolean
idle_emit(CcBackgroundXml * xml)111 idle_emit (CcBackgroundXml *xml)
112 {
113 	gint i;
114 
115 	g_async_queue_lock (xml->item_added_queue);
116 
117 	for (i = 0; i < NUM_ITEMS_PER_BATCH; i++) {
118 		g_autoptr(GObject) item = NULL;
119 
120 		item = g_async_queue_try_pop_unlocked (xml->item_added_queue);
121 		if (item == NULL)
122 			break;
123 		g_signal_emit (G_OBJECT (xml), signals[ADDED], 0, item);
124 	}
125 
126 	g_async_queue_unlock (xml->item_added_queue);
127 
128         if (g_async_queue_length (xml->item_added_queue) > 0) {
129                 return TRUE;
130         } else {
131                 xml->item_added_id = 0;
132                 return FALSE;
133         }
134 }
135 
136 static void
emit_added_in_idle(CcBackgroundXml * xml,GObject * object)137 emit_added_in_idle (CcBackgroundXml *xml,
138 		    GObject         *object)
139 {
140 	g_async_queue_lock (xml->item_added_queue);
141 	g_async_queue_push_unlocked (xml->item_added_queue, object);
142 	if (xml->item_added_id == 0)
143 		xml->item_added_id = g_idle_add ((GSourceFunc) idle_emit, xml);
144 	g_async_queue_unlock (xml->item_added_queue);
145 }
146 
147 #define NONE "(none)"
148 #define UNSET_FLAG(flag) G_STMT_START{ (flags&=~(flag)); }G_STMT_END
149 #define SET_FLAG(flag) G_STMT_START{ (flags|=flag); }G_STMT_END
150 
151 static gboolean
cc_background_xml_load_xml_internal(CcBackgroundXml * xml,const gchar * filename,gboolean in_thread)152 cc_background_xml_load_xml_internal (CcBackgroundXml *xml,
153 				     const gchar     *filename,
154 				     gboolean         in_thread)
155 {
156   xmlDoc * wplist;
157   xmlNode * root, * list, * wpa;
158   xmlChar * nodelang;
159   const gchar * const * syslangs;
160   gint i;
161   gboolean retval;
162 
163   wplist = xmlParseFile (filename);
164   retval = FALSE;
165 
166   if (!wplist)
167     return retval;
168 
169   syslangs = g_get_language_names ();
170 
171   root = xmlDocGetRootElement (wplist);
172 
173   for (list = root->children; list != NULL; list = list->next) {
174     if (!strcmp ((gchar *)list->name, "wallpaper")) {
175       g_autoptr(CcBackgroundItem) item = NULL;
176       CcBackgroundItemFlags flags;
177       g_autofree gchar *uri = NULL;
178       g_autofree gchar *cname = NULL;
179       g_autofree gchar *id = NULL;
180 
181       flags = 0;
182       item = cc_background_item_new (NULL);
183 
184       g_object_set (G_OBJECT (item),
185 		    "is-deleted", cc_background_xml_get_bool (list, "deleted"),
186 		    "source-xml", filename,
187 		    NULL);
188 
189       for (wpa = list->children; wpa != NULL; wpa = wpa->next) {
190 	if (wpa->type == XML_COMMENT_NODE) {
191 	  continue;
192 	} else if (!strcmp ((gchar *)wpa->name, "filename")) {
193 	  if (wpa->last != NULL && wpa->last->content != NULL) {
194 	    gchar *content = g_strstrip ((gchar *)wpa->last->content);
195 	    g_autofree gchar *bg_uri = NULL;
196 
197 	    /* FIXME same rubbish as in other parts of the code */
198 	    if (strcmp (content, NONE) == 0) {
199 	      bg_uri = NULL;
200 	    } else {
201 	      g_autoptr(GFile) file = NULL;
202 	      g_autofree gchar *dirname = NULL;
203 
204 	      dirname = g_path_get_dirname (filename);
205 	      file = g_file_new_for_commandline_arg_and_cwd (content, dirname);
206 	      bg_uri = g_file_get_uri (file);
207 	    }
208 	    SET_FLAG(CC_BACKGROUND_ITEM_HAS_URI);
209 	    g_object_set (G_OBJECT (item), "uri", bg_uri, NULL);
210 	  } else {
211 	    break;
212 	  }
213 	} else if (!strcmp ((gchar *)wpa->name, "name")) {
214 	  if (wpa->last != NULL && wpa->last->content != NULL) {
215 	    g_autofree gchar *name = NULL;
216 	    nodelang = xmlNodeGetLang (wpa->last);
217 
218 	    g_object_get (G_OBJECT (item), "name", &name, NULL);
219 
220 	    if (name == NULL && nodelang == NULL) {
221 	       g_free (cname);
222 	       cname = g_strdup (g_strstrip ((gchar *)wpa->last->content));
223 	       g_object_set (G_OBJECT (item), "name", cname, NULL);
224             } else {
225 	       for (i = 0; syslangs[i] != NULL; i++) {
226 	         if (!strcmp (syslangs[i], (gchar *)nodelang)) {
227 		   g_object_set (G_OBJECT (item), "name",
228 				 g_strstrip ((gchar *)wpa->last->content), NULL);
229 	           break;
230 	         }
231 	       }
232 	    }
233 
234 	    xmlFree (nodelang);
235 	  } else {
236 	    break;
237 	  }
238 	} else if (!strcmp ((gchar *)wpa->name, "options")) {
239 	  if (wpa->last != NULL) {
240 	    g_object_set (G_OBJECT (item), "placement",
241 			  enum_string_to_value (G_DESKTOP_TYPE_DESKTOP_BACKGROUND_STYLE,
242 						g_strstrip ((gchar *)wpa->last->content)), NULL);
243 	    SET_FLAG(CC_BACKGROUND_ITEM_HAS_PLACEMENT);
244 	  }
245 	} else if (!strcmp ((gchar *)wpa->name, "shade_type")) {
246 	  if (wpa->last != NULL) {
247 	    g_object_set (G_OBJECT (item), "shading",
248 			  enum_string_to_value (G_DESKTOP_TYPE_DESKTOP_BACKGROUND_SHADING,
249 						g_strstrip ((gchar *)wpa->last->content)), NULL);
250 	    SET_FLAG(CC_BACKGROUND_ITEM_HAS_SHADING);
251 	  }
252 	} else if (!strcmp ((gchar *)wpa->name, "pcolor")) {
253 	  if (wpa->last != NULL) {
254 	    g_object_set (G_OBJECT (item), "primary-color",
255 			  g_strstrip ((gchar *)wpa->last->content), NULL);
256 	    SET_FLAG(CC_BACKGROUND_ITEM_HAS_PCOLOR);
257 	  }
258 	} else if (!strcmp ((gchar *)wpa->name, "scolor")) {
259 	  if (wpa->last != NULL) {
260 	    g_object_set (G_OBJECT (item), "secondary-color",
261 			  g_strstrip ((gchar *)wpa->last->content), NULL);
262 	    SET_FLAG(CC_BACKGROUND_ITEM_HAS_SCOLOR);
263 	  }
264 	} else if (!strcmp ((gchar *)wpa->name, "source_url")) {
265 	   if (wpa->last != NULL) {
266              g_object_set (G_OBJECT (item),
267 			   "source-url", g_strstrip ((gchar *)wpa->last->content),
268 			   "needs-download", FALSE,
269 			   NULL);
270 	   }
271 	} else if (!strcmp ((gchar *)wpa->name, "text")) {
272 	  /* Do nothing here, libxml2 is being weird */
273 	} else {
274 	  g_debug ("Unknown Tag in %s: %s", filename, wpa->name);
275 	}
276       }
277 
278       /* Check whether the target file exists */
279       {
280         const char *uri;
281 
282 	uri = cc_background_item_get_uri (item);
283 	if (uri != NULL)
284 	  {
285             g_autoptr(GFile) file = NULL;
286 
287             file = g_file_new_for_uri (uri);
288 	    if (g_file_query_exists (file, NULL) == FALSE)
289 	      {
290 	        g_clear_pointer (&cname, g_free);
291 	        g_clear_object (&item);
292 	        continue;
293 	      }
294 	  }
295       }
296 
297       /* FIXME, this is a broken way of doing,
298        * need to use proper code here */
299       uri = g_filename_to_uri (filename, NULL, NULL);
300       id = g_strdup_printf ("%s#%s", uri, cname);
301 
302       /* Make sure we don't already have this one and that filename exists */
303       if (g_hash_table_lookup (xml->wp_hash, id) != NULL) {
304 	continue;
305       }
306 
307       g_object_set (G_OBJECT (item), "flags", flags, NULL);
308       g_hash_table_insert (xml->wp_hash,
309                            g_strdup (id),
310                            g_object_ref (item));
311       if (in_thread)
312         emit_added_in_idle (xml, g_object_ref (G_OBJECT (item)));
313       else
314         g_signal_emit (G_OBJECT (xml), signals[ADDED], 0, item);
315       retval = TRUE;
316     }
317   }
318   xmlFreeDoc (wplist);
319 
320   return retval;
321 }
322 
323 static void
gnome_wp_file_changed(GFileMonitor * monitor,GFile * file,GFile * other_file,GFileMonitorEvent event_type,CcBackgroundXml * data)324 gnome_wp_file_changed (GFileMonitor *monitor,
325 		       GFile *file,
326 		       GFile *other_file,
327 		       GFileMonitorEvent event_type,
328 		       CcBackgroundXml *data)
329 {
330   g_autofree gchar *filename = NULL;
331 
332   switch (event_type) {
333   case G_FILE_MONITOR_EVENT_CHANGED:
334   case G_FILE_MONITOR_EVENT_CREATED:
335     filename = g_file_get_path (file);
336     cc_background_xml_load_xml_internal (data, filename, FALSE);
337     break;
338   default:
339     break;
340   }
341 }
342 
343 static void
cc_background_xml_add_monitor(GFile * directory,CcBackgroundXml * data)344 cc_background_xml_add_monitor (GFile      *directory,
345 			       CcBackgroundXml *data)
346 {
347   GFileMonitor *monitor;
348   g_autoptr(GError) error = NULL;
349 
350   monitor = g_file_monitor_directory (directory,
351                                       G_FILE_MONITOR_NONE,
352                                       NULL,
353                                       &error);
354   if (error != NULL) {
355     g_autofree gchar *path = NULL;
356 
357     path = g_file_get_parse_name (directory);
358     g_warning ("Unable to monitor directory %s: %s",
359                path, error->message);
360     return;
361   }
362 
363   g_signal_connect (monitor, "changed",
364                     G_CALLBACK (gnome_wp_file_changed),
365                     data);
366 
367   data->monitors = g_slist_prepend (data->monitors, monitor);
368 }
369 
370 static void
cc_background_xml_load_from_dir(const gchar * path,CcBackgroundXml * data,gboolean in_thread)371 cc_background_xml_load_from_dir (const gchar      *path,
372 				 CcBackgroundXml  *data,
373 				 gboolean          in_thread)
374 {
375   g_autoptr(GFile) directory = NULL;
376   g_autoptr(GFileEnumerator) enumerator = NULL;
377   g_autoptr(GError) error = NULL;
378 
379   if (!g_file_test (path, G_FILE_TEST_IS_DIR)) {
380     return;
381   }
382 
383   directory = g_file_new_for_path (path);
384   enumerator = g_file_enumerate_children (directory,
385                                           G_FILE_ATTRIBUTE_STANDARD_NAME,
386                                           G_FILE_QUERY_INFO_NONE,
387                                           NULL,
388                                           &error);
389   if (error != NULL) {
390     g_warning ("Unable to check directory %s: %s", path, error->message);
391     return;
392   }
393 
394   while (TRUE) {
395     g_autoptr(GFileInfo) info = NULL;
396     const gchar *filename;
397     g_autofree gchar *fullpath = NULL;
398 
399     info = g_file_enumerator_next_file (enumerator, NULL, NULL);
400     if (info == NULL) {
401         g_file_enumerator_close (enumerator, NULL, NULL);
402         cc_background_xml_add_monitor (directory, data);
403         return;
404     }
405 
406     filename = g_file_info_get_name (info);
407     fullpath = g_build_filename (path, filename, NULL);
408 
409     cc_background_xml_load_xml_internal (data, fullpath, in_thread);
410   }
411 }
412 
413 static void
cc_background_xml_load_list(CcBackgroundXml * data,gboolean in_thread)414 cc_background_xml_load_list (CcBackgroundXml *data,
415 			     gboolean         in_thread)
416 {
417   const char * const *system_data_dirs;
418   g_autofree gchar *datadir = NULL;
419   gint i;
420 
421   datadir = g_build_filename (g_get_user_data_dir (),
422                               "gnome-background-properties",
423                               NULL);
424   cc_background_xml_load_from_dir (datadir, data, in_thread);
425 
426   system_data_dirs = g_get_system_data_dirs ();
427   for (i = 0; system_data_dirs[i]; i++) {
428     g_autofree gchar *sdatadir = NULL;
429     sdatadir = g_build_filename (system_data_dirs[i],
430                                 "gnome-background-properties",
431 				NULL);
432     cc_background_xml_load_from_dir (sdatadir, data, in_thread);
433   }
434 }
435 
436 gboolean
cc_background_xml_load_list_finish(CcBackgroundXml * xml,GAsyncResult * result,GError ** error)437 cc_background_xml_load_list_finish (CcBackgroundXml *xml,
438 				    GAsyncResult    *result,
439 				    GError         **error)
440 {
441 	g_return_val_if_fail (g_task_is_valid (result, xml), FALSE);
442 	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
443 	return g_task_propagate_boolean (G_TASK (result), error);
444 }
445 
446 static void
load_list_thread(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)447 load_list_thread (GTask *task,
448 		  gpointer source_object,
449 		  gpointer task_data,
450 		  GCancellable *cancellable)
451 {
452 	CcBackgroundXml *xml = CC_BACKGROUND_XML (source_object);
453 	cc_background_xml_load_list (xml, TRUE);
454 	g_task_return_boolean (task, TRUE);
455 }
456 
457 void
cc_background_xml_load_list_async(CcBackgroundXml * xml,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)458 cc_background_xml_load_list_async (CcBackgroundXml *xml,
459 				   GCancellable *cancellable,
460 				   GAsyncReadyCallback callback,
461 				   gpointer user_data)
462 {
463 	g_autoptr(GTask) task = NULL;
464 
465 	g_return_if_fail (CC_IS_BACKGROUND_XML (xml));
466 
467 	task = g_task_new (xml, cancellable, callback, user_data);
468 	g_task_run_in_thread (task, load_list_thread);
469 }
470 
471 gboolean
cc_background_xml_load_xml(CcBackgroundXml * xml,const gchar * filename)472 cc_background_xml_load_xml (CcBackgroundXml *xml,
473 			    const gchar     *filename)
474 {
475 	g_return_val_if_fail (CC_IS_BACKGROUND_XML (xml), FALSE);
476 
477 	if (g_file_test (filename, G_FILE_TEST_IS_REGULAR) == FALSE)
478 		return FALSE;
479 
480 	return cc_background_xml_load_xml_internal (xml, filename, FALSE);
481 }
482 
483 static void
single_xml_added(CcBackgroundXml * xml,CcBackgroundItem * item,CcBackgroundItem ** ret)484 single_xml_added (CcBackgroundXml   *xml,
485 		  CcBackgroundItem  *item,
486 		  CcBackgroundItem **ret)
487 {
488 	g_assert (*ret == NULL);
489 	*ret = g_object_ref (item);
490 }
491 
492 CcBackgroundItem *
cc_background_xml_get_item(const char * filename)493 cc_background_xml_get_item (const char *filename)
494 {
495 	g_autoptr(CcBackgroundXml) xml = NULL;
496 	CcBackgroundItem *item = NULL;
497 
498 	if (g_file_test (filename, G_FILE_TEST_IS_REGULAR) == FALSE)
499 		return NULL;
500 
501 	xml = cc_background_xml_new ();
502 	g_signal_connect (G_OBJECT (xml), "added",
503 			  G_CALLBACK (single_xml_added), &item);
504 	if (cc_background_xml_load_xml (xml, filename) == FALSE)
505 		return NULL;
506 
507 	return item;
508 }
509 
510 static const char *
enum_to_str(GType type,int v)511 enum_to_str (GType type,
512 	     int   v)
513 {
514 	GEnumClass *eclass;
515 	GEnumValue *value;
516 
517 	eclass = G_ENUM_CLASS (g_type_class_peek (type));
518 	value = g_enum_get_value (eclass, v);
519 
520 	g_assert (value);
521 
522 	return value->value_nick;
523 }
524 
525 void
cc_background_xml_save(CcBackgroundItem * item,const char * filename)526 cc_background_xml_save (CcBackgroundItem *item,
527 			const char       *filename)
528 {
529   xmlDoc *wp;
530   xmlNode *root, *wallpaper;
531   xmlNode *xml_item G_GNUC_UNUSED;
532   const char * none = "(none)";
533   const char *placement_str, *shading_str;
534   g_autofree gchar *name = NULL;
535   g_autofree gchar *pcolor = NULL;
536   g_autofree gchar *scolor = NULL;
537   g_autofree gchar *uri = NULL;
538   g_autofree gchar *source_url = NULL;
539   CcBackgroundItemFlags flags;
540   GDesktopBackgroundStyle placement;
541   GDesktopBackgroundShading shading;
542 
543   xmlKeepBlanksDefault (0);
544 
545   wp = xmlNewDoc ((xmlChar *)"1.0");
546   xmlCreateIntSubset (wp, (xmlChar *)"wallpapers", NULL, (xmlChar *)"gnome-wp-list.dtd");
547   root = xmlNewNode (NULL, (xmlChar *)"wallpapers");
548   xmlDocSetRootElement (wp, root);
549 
550   g_object_get (G_OBJECT (item),
551 		"name", &name,
552 		"uri", &uri,
553 		"shading", &shading,
554 		"placement", &placement,
555 		"primary-color", &pcolor,
556 		"secondary-color", &scolor,
557 		"source-url", &source_url,
558 		"flags", &flags,
559 		NULL);
560 
561   placement_str = enum_to_str (G_DESKTOP_TYPE_DESKTOP_BACKGROUND_STYLE, placement);
562   shading_str = enum_to_str (G_DESKTOP_TYPE_DESKTOP_BACKGROUND_SHADING, shading);
563 
564   wallpaper = xmlNewChild (root, NULL, (xmlChar *)"wallpaper", NULL);
565   xml_item = xmlNewTextChild (wallpaper, NULL, (xmlChar *)"name", (xmlChar *)name);
566   if (flags & CC_BACKGROUND_ITEM_HAS_URI &&
567       uri != NULL)
568     {
569       g_autoptr(GFile) file = NULL;
570       g_autofree gchar *fname = NULL;
571 
572       file = g_file_new_for_commandline_arg (uri);
573       fname = g_file_get_path (file);
574       xml_item = xmlNewTextChild (wallpaper, NULL, (xmlChar *)"filename", (xmlChar *)fname);
575     }
576   else if (flags & CC_BACKGROUND_ITEM_HAS_URI)
577     {
578       xml_item = xmlNewTextChild (wallpaper, NULL, (xmlChar *)"filename", (xmlChar *)none);
579     }
580 
581   if (flags & CC_BACKGROUND_ITEM_HAS_PLACEMENT)
582     xml_item = xmlNewTextChild (wallpaper, NULL, (xmlChar *)"options", (xmlChar *)placement_str);
583   if (flags & CC_BACKGROUND_ITEM_HAS_SHADING)
584     xml_item = xmlNewTextChild (wallpaper, NULL, (xmlChar *)"shade_type", (xmlChar *)shading_str);
585   if (flags & CC_BACKGROUND_ITEM_HAS_PCOLOR)
586     xml_item = xmlNewTextChild (wallpaper, NULL, (xmlChar *)"pcolor", (xmlChar *)pcolor);
587   if (flags & CC_BACKGROUND_ITEM_HAS_SCOLOR)
588     xml_item = xmlNewTextChild (wallpaper, NULL, (xmlChar *)"scolor", (xmlChar *)scolor);
589   if (source_url != NULL)
590     xml_item = xmlNewTextChild (wallpaper, NULL, (xmlChar *)"source_url", (xmlChar *)source_url);
591 
592   xmlSaveFormatFile (filename, wp, 1);
593   xmlFreeDoc (wp);
594 }
595 
596 static void
cc_background_xml_finalize(GObject * object)597 cc_background_xml_finalize (GObject *object)
598 {
599         CcBackgroundXml *xml;
600 
601         g_return_if_fail (object != NULL);
602         g_return_if_fail (CC_IS_BACKGROUND_XML (object));
603 
604         xml = CC_BACKGROUND_XML (object);
605 
606         g_slist_free_full (xml->monitors, g_object_unref);
607 
608 	g_clear_pointer (&xml->wp_hash, g_hash_table_destroy);
609 	if (xml->item_added_id != 0) {
610 		g_source_remove (xml->item_added_id);
611 		xml->item_added_id = 0;
612 	}
613 	g_clear_pointer (&xml->item_added_queue, g_async_queue_unref);
614 
615         G_OBJECT_CLASS (cc_background_xml_parent_class)->finalize (object);
616 }
617 
618 static void
cc_background_xml_class_init(CcBackgroundXmlClass * klass)619 cc_background_xml_class_init (CcBackgroundXmlClass *klass)
620 {
621         GObjectClass  *object_class = G_OBJECT_CLASS (klass);
622 
623         object_class->finalize = cc_background_xml_finalize;
624 
625 	signals[ADDED] = g_signal_new ("added",
626 				       G_OBJECT_CLASS_TYPE (object_class),
627 				       G_SIGNAL_RUN_LAST,
628 				       0,
629 				       NULL, NULL,
630 				       g_cclosure_marshal_VOID__OBJECT,
631 				       G_TYPE_NONE, 1, CC_TYPE_BACKGROUND_ITEM);
632 }
633 
634 static void
cc_background_xml_init(CcBackgroundXml * xml)635 cc_background_xml_init (CcBackgroundXml *xml)
636 {
637         xml->wp_hash = g_hash_table_new_full (g_str_hash,
638                                               g_str_equal,
639                                               (GDestroyNotify) g_free,
640                                               (GDestroyNotify) g_object_unref);
641 	xml->item_added_queue = g_async_queue_new_full ((GDestroyNotify) g_object_unref);
642 }
643 
644 CcBackgroundXml *
cc_background_xml_new(void)645 cc_background_xml_new (void)
646 {
647 	return CC_BACKGROUND_XML (g_object_new (CC_TYPE_BACKGROUND_XML, NULL));
648 }
649