1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
4 * Copyright (C) 2003 Ximian, Inc.
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of version 2 of the GNU Lesser General Public
8 * License as published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this program; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
20 * Author: Ettore Perazzoli <ettore@ximian.com>
31 #define ES_CLASS(obj) E_SOURCE_CLASS (G_OBJECT_GET_CLASS (obj))
34 /* Private members. */
36 struct _ESourcePrivate {
48 GHashTable *properties;
58 static unsigned int signals[LAST_SIGNAL] = { 0 };
64 group_weak_notify (ESource *source,
65 GObject **where_the_object_was)
67 source->priv->group = NULL;
69 g_signal_emit (source, signals[CHANGED], 0);
73 /* GObject methods. */
75 G_DEFINE_TYPE (ESource, e_source, G_TYPE_OBJECT);
78 impl_finalize (GObject *object)
80 ESourcePrivate *priv = E_SOURCE (object)->priv;
84 g_free (priv->relative_uri);
85 g_free (priv->absolute_uri);
86 g_free (priv->color_spec);
88 g_hash_table_destroy (priv->properties);
92 (* G_OBJECT_CLASS (e_source_parent_class)->finalize) (object);
96 impl_dispose (GObject *object)
98 ESourcePrivate *priv = E_SOURCE (object)->priv;
100 if (priv->group != NULL) {
101 g_object_weak_unref (G_OBJECT (priv->group), (GWeakNotify) group_weak_notify, object);
105 (* G_OBJECT_CLASS (e_source_parent_class)->dispose) (object);
109 /* Initialization. */
112 e_source_class_init (ESourceClass *class)
114 GObjectClass *object_class = G_OBJECT_CLASS (class);
116 object_class->dispose = impl_dispose;
117 object_class->finalize = impl_finalize;
120 g_signal_new ("changed",
121 G_OBJECT_CLASS_TYPE (object_class),
123 G_STRUCT_OFFSET (ESourceClass, changed),
125 g_cclosure_marshal_VOID__VOID,
130 e_source_init (ESource *source)
132 ESourcePrivate *priv;
134 priv = g_new0 (ESourcePrivate, 1);
137 priv->properties = g_hash_table_new_full (g_str_hash, g_str_equal,
141 /* Private methods. */
144 set_color_spec (ESource *source,
145 const gchar *color_spec)
147 ESourcePrivate *priv = source->priv;
150 if (color_spec == priv->color_spec)
153 do_cmp = (color_spec != NULL && priv->color_spec != NULL);
154 if (do_cmp && g_ascii_strcasecmp (color_spec, priv->color_spec) == 0)
157 g_free (priv->color_spec);
158 priv->color_spec = g_strdup (color_spec);
163 /* Public methods. */
166 e_source_new (const char *name,
167 const char *relative_uri)
171 g_return_val_if_fail (name != NULL, NULL);
172 g_return_val_if_fail (relative_uri != NULL, NULL);
174 source = g_object_new (e_source_get_type (), NULL);
175 source->priv->uid = e_uid_new ();
177 e_source_set_name (source, name);
178 e_source_set_relative_uri (source, relative_uri);
183 e_source_new_with_absolute_uri (const char *name,
184 const char *absolute_uri)
188 g_return_val_if_fail (name != NULL, NULL);
189 g_return_val_if_fail (absolute_uri != NULL, NULL);
191 source = g_object_new (e_source_get_type (), NULL);
192 source->priv->uid = e_uid_new ();
194 e_source_set_name (source, name);
195 e_source_set_absolute_uri (source, absolute_uri);
200 e_source_new_from_xml_node (xmlNodePtr node)
205 uid = xmlGetProp (node, (xmlChar*)"uid");
209 source = g_object_new (e_source_get_type (), NULL);
211 source->priv->uid = g_strdup ((char*)uid);
214 if (e_source_update_from_xml_node (source, node, NULL))
217 g_object_unref (source);
222 e_source_equal (ESource *source_1, ESource *source_2)
224 gboolean equal = FALSE;
226 g_return_val_if_fail (E_IS_SOURCE (source_1), FALSE);
227 g_return_val_if_fail (E_IS_SOURCE (source_2), FALSE);
229 if (source_1->priv->uid && source_2->priv->uid &&
230 !strcmp (source_1->priv->uid, source_2->priv->uid)) {
233 gchar *uri_1, *uri_2;
235 uri_1 = e_source_get_uri (source_1);
236 uri_2 = e_source_get_uri (source_2);
238 if (uri_1 && uri_2 && !strcmp (uri_1, uri_2))
249 import_properties (ESource *source,
250 xmlNodePtr prop_root)
252 ESourcePrivate *priv = source->priv;
253 xmlNodePtr prop_node;
255 for (prop_node = prop_root->children; prop_node; prop_node = prop_node->next) {
256 xmlChar *name, *value;
258 if (!prop_node->name || strcmp ((char*)prop_node->name, "property"))
261 name = xmlGetProp (prop_node, (xmlChar*)"name");
262 value = xmlGetProp (prop_node, (xmlChar*)"value");
265 g_hash_table_insert (priv->properties, g_strdup ((char*)name), g_strdup ((char*)value));
281 compare_str_hash (gpointer key, gpointer value, hash_compare_data *cd)
283 gpointer value2 = g_hash_table_lookup (cd->table2, key);
284 if (value2 == NULL || g_str_equal (value, value2) == FALSE)
289 compare_str_hashes (GHashTable *table1, GHashTable *table2)
291 hash_compare_data cd;
293 if (g_hash_table_size (table1) != g_hash_table_size (table2))
298 g_hash_table_foreach (table1, (GHFunc) compare_str_hash, &cd);
303 * e_source_update_from_xml_node:
304 * @source: An ESource.
305 * @node: A pointer to the node to parse.
307 * Update the ESource properties from @node.
309 * Return value: %TRUE if the data in @node was recognized and parsed into
310 * acceptable values for @source, %FALSE otherwise.
313 e_source_update_from_xml_node (ESource *source,
315 gboolean *changed_return)
318 xmlChar *relative_uri;
319 xmlChar *absolute_uri;
322 gboolean retval = FALSE;
323 gboolean changed = FALSE;
325 name = xmlGetProp (node, (xmlChar*)"name");
326 relative_uri = xmlGetProp (node, (xmlChar*)"relative_uri");
327 absolute_uri = xmlGetProp (node, (xmlChar*)"uri");
328 color_spec = xmlGetProp (node, (xmlChar*)"color_spec");
329 color = xmlGetProp (node, (xmlChar*)"color"); /* obsolete */
331 if (name == NULL || (relative_uri == NULL && absolute_uri == NULL))
334 if (color_spec != NULL && color != NULL)
337 if (source->priv->name == NULL
338 || strcmp ((char*)name, source->priv->name) != 0
339 || source->priv->relative_uri == NULL
340 || relative_uri != NULL
341 || strcmp ((char*)relative_uri, source->priv->relative_uri) != 0) {
342 g_free (source->priv->name);
343 source->priv->name = g_strdup ((char*)name);
345 g_free (source->priv->relative_uri);
346 source->priv->relative_uri = g_strdup ((char*)relative_uri);
351 if (absolute_uri != NULL) {
352 g_free (source->priv->absolute_uri);
353 source->priv->absolute_uri = g_strdup ((char*)absolute_uri);
358 /* It is okay for color_spec to be NULL. */
359 changed |= set_color_spec (source, (char*)color_spec);
362 g_snprintf (buffer, sizeof (buffer), "#%s", color);
363 changed |= set_color_spec (source, buffer);
366 if (g_hash_table_size (source->priv->properties) && !node->children) {
367 g_hash_table_destroy (source->priv->properties);
368 source->priv->properties = g_hash_table_new_full (g_str_hash, g_str_equal,
373 for (node = node->children; node; node = node->next) {
377 if (!strcmp ((char*)node->name, "properties")) {
378 GHashTable *temp = source->priv->properties;
379 source->priv->properties = g_hash_table_new_full (g_str_hash, g_str_equal,
381 import_properties (source, node);
382 if (!compare_str_hashes (temp, source->priv->properties))
384 g_hash_table_destroy (temp);
393 g_signal_emit (source, signals[CHANGED], 0);
395 if (changed_return != NULL)
396 *changed_return = changed;
400 if (relative_uri != NULL)
401 xmlFree (relative_uri);
402 if (absolute_uri != NULL)
403 xmlFree (absolute_uri);
404 if (color_spec != NULL)
405 xmlFree (color_spec);
413 * e_source_name_from_xml_node:
414 * @node: A pointer to an XML node.
416 * Assuming that @node is a valid ESource specification, retrieve the name of
417 * the source from it.
419 * Return value: Name of the source in the specified @node. The caller must
423 e_source_uid_from_xml_node (xmlNodePtr node)
425 xmlChar *uid = xmlGetProp (node, (xmlChar*)"uid");
431 retval = g_strdup ((char*)uid);
437 e_source_build_absolute_uri (ESource *source)
439 const gchar *base_uri_str;
442 g_return_val_if_fail (source->priv->group != NULL, NULL);
444 base_uri_str = e_source_group_peek_base_uri (source->priv->group);
446 /* If last character in base URI is a slash, just concat the
447 * strings. We don't want to compress e.g. the trailing ://
448 * in a protocol specification Note: Do not use
449 * G_DIR_SEPARATOR or g_build_filename() when manipulating
450 * URIs. URIs use normal ("forward") slashes also on Windows.
452 if (*base_uri_str && *(base_uri_str + strlen (base_uri_str) - 1) == '/')
453 uri_str = g_strconcat (base_uri_str, source->priv->relative_uri, NULL);
455 if (source->priv->relative_uri != NULL)
456 uri_str = g_strconcat (base_uri_str, "/", source->priv->relative_uri,
459 uri_str = g_strdup (base_uri_str);
466 e_source_set_group (ESource *source,
469 g_return_if_fail (E_IS_SOURCE (source));
470 g_return_if_fail (group == NULL || E_IS_SOURCE_GROUP (group));
472 if (source->priv->readonly)
475 if (source->priv->group == group)
478 if (source->priv->group != NULL)
479 g_object_weak_unref (G_OBJECT (source->priv->group), (GWeakNotify) group_weak_notify, source);
481 source->priv->group = group;
483 g_object_weak_ref (G_OBJECT (group), (GWeakNotify) group_weak_notify, source);
486 g_signal_emit (source, signals[CHANGED], 0);
490 e_source_set_name (ESource *source,
493 g_return_if_fail (E_IS_SOURCE (source));
494 g_return_if_fail (name != NULL);
496 if (source->priv->readonly)
499 if (source->priv->name != NULL &&
500 strcmp (source->priv->name, name) == 0)
503 g_free (source->priv->name);
504 source->priv->name = g_strdup (name);
506 g_signal_emit (source, signals[CHANGED], 0);
510 e_source_set_relative_uri (ESource *source,
511 const char *relative_uri)
515 g_return_if_fail (E_IS_SOURCE (source));
517 if (source->priv->readonly)
520 if (source->priv->relative_uri == relative_uri)
523 g_free (source->priv->relative_uri);
524 source->priv->relative_uri = g_strdup (relative_uri);
526 /* reset the absolute uri */
527 if (source->priv->absolute_uri &&
528 (absolute_uri = e_source_build_absolute_uri (source))) {
529 g_free (source->priv->absolute_uri);
530 source->priv->absolute_uri = absolute_uri;
533 g_signal_emit (source, signals[CHANGED], 0);
537 e_source_set_absolute_uri (ESource *source,
538 const char *absolute_uri)
540 g_return_if_fail (E_IS_SOURCE (source));
542 if (!!absolute_uri == !!source->priv->absolute_uri
543 || (absolute_uri && source->priv->absolute_uri && !strcmp (source->priv->absolute_uri, absolute_uri)))
546 g_free (source->priv->absolute_uri);
547 source->priv->absolute_uri = g_strdup (absolute_uri);
549 g_signal_emit (source, signals[CHANGED], 0);
553 e_source_set_readonly (ESource *source,
556 g_return_if_fail (E_IS_SOURCE (source));
558 if (source->priv->readonly == readonly)
561 source->priv->readonly = readonly;
563 g_signal_emit (source, signals[CHANGED], 0);
568 e_source_set_color (ESource *source,
573 g_return_if_fail (E_IS_SOURCE (source));
575 g_snprintf (color_spec, sizeof (color_spec), "#%06x", color);
576 e_source_set_color_spec (source, color_spec);
580 e_source_unset_color (ESource *source)
582 g_return_if_fail (E_IS_SOURCE (source));
584 e_source_set_color_spec (source, NULL);
588 * e_source_set_color_spec:
589 * @source: an ESource
590 * @color_spec: a string specifying the color
592 * Store a textual representation of a color in @source. The @color_spec
593 * string should be parsable by #gdk_color_parse(), or %NULL to unset the
599 e_source_set_color_spec (ESource *source,
600 const gchar *color_spec)
602 g_return_if_fail (E_IS_SOURCE (source));
604 if (!source->priv->readonly && set_color_spec (source, color_spec))
605 g_signal_emit (source, signals[CHANGED], 0);
609 e_source_peek_group (ESource *source)
611 g_return_val_if_fail (E_IS_SOURCE (source), NULL);
613 return source->priv->group;
617 e_source_peek_uid (ESource *source)
619 g_return_val_if_fail (E_IS_SOURCE (source), NULL);
621 return source->priv->uid;
625 e_source_peek_name (ESource *source)
627 g_return_val_if_fail (E_IS_SOURCE (source), NULL);
629 return source->priv->name;
633 e_source_peek_relative_uri (ESource *source)
635 g_return_val_if_fail (E_IS_SOURCE (source), NULL);
637 return source->priv->relative_uri;
641 e_source_peek_absolute_uri (ESource *source)
643 g_return_val_if_fail (E_IS_SOURCE (source), NULL);
645 return source->priv->absolute_uri;
649 * e_source_peek_color_spec:
650 * @source: an ESource
652 * Return the textual representation of the color for @source, or %NULL if it
653 * has none. The returned string should be parsable by #gdk_color_parse().
655 * Return value: a string specifying the color
660 e_source_peek_color_spec (ESource *source)
662 g_return_val_if_fail (E_IS_SOURCE (source), NULL);
664 return source->priv->color_spec;
668 e_source_get_readonly (ESource *source)
670 g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
672 return source->priv->readonly;
677 * e_source_get_color:
678 * @source: An ESource
679 * @color_return: Pointer to a variable where the returned color will be
682 * If @source has an associated color, return it in *@color_return.
684 * Return value: %TRUE if the @source has a defined color (and hence
685 * *@color_return was set), %FALSE otherwise.
688 e_source_get_color (ESource *source,
689 guint32 *color_return)
691 const gchar *color_spec;
694 g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
696 color_spec = e_source_peek_color_spec (source);
698 if (color_spec == NULL)
701 if (sscanf (color_spec, "#%06x", &color) != 1)
704 if (color_return != NULL)
705 *color_return = color;
711 e_source_get_uri (ESource *source)
713 g_return_val_if_fail (E_IS_SOURCE (source), NULL);
715 if (source->priv->group == NULL) {
716 if (source->priv->absolute_uri != NULL)
717 return g_strdup (source->priv->absolute_uri);
719 g_warning ("e_source_get_uri () called on source with no absolute URI!");
722 else if (source->priv->absolute_uri != NULL) /* source->priv->group != NULL */
723 return g_strdup (source->priv->absolute_uri);
725 return e_source_build_absolute_uri (source);
730 property_dump_cb (const xmlChar *key, const xmlChar *value, xmlNodePtr root)
734 node = xmlNewChild (root, NULL, (xmlChar*)"property", NULL);
735 xmlSetProp (node, (xmlChar*)"name", key);
736 xmlSetProp (node, (xmlChar*)"value", value);
741 dump_common_to_xml_node (ESource *source,
742 xmlNodePtr parent_node)
744 ESourcePrivate *priv;
746 const char *abs_uri = NULL, *relative_uri = NULL;
751 node = xmlNewChild (parent_node, NULL, (xmlChar*)"source", NULL);
753 node = xmlNewNode (NULL, (xmlChar*)"source");
755 xmlSetProp (node, (xmlChar*)"uid", (xmlChar*)e_source_peek_uid (source));
756 xmlSetProp (node, (xmlChar*)"name", (xmlChar*)e_source_peek_name (source));
757 abs_uri = e_source_peek_absolute_uri (source);
758 relative_uri = e_source_peek_relative_uri (source);
760 xmlSetProp (node, (xmlChar*)"uri", (xmlChar*)abs_uri);
762 xmlSetProp (node, (xmlChar*)"relative_uri", (xmlChar*)relative_uri);
764 if (priv->color_spec != NULL)
765 xmlSetProp (node, (xmlChar*)"color_spec", (xmlChar*)priv->color_spec);
767 if (g_hash_table_size (priv->properties) != 0) {
768 xmlNodePtr properties_node;
770 properties_node = xmlNewChild (node, NULL, (xmlChar*)"properties", NULL);
771 g_hash_table_foreach (priv->properties, (GHFunc) property_dump_cb, properties_node);
779 e_source_dump_to_xml_node (ESource *source,
780 xmlNodePtr parent_node)
782 g_return_if_fail (E_IS_SOURCE (source));
784 dump_common_to_xml_node (source, parent_node);
789 e_source_to_standalone_xml (ESource *source)
794 char *returned_buffer;
798 g_return_val_if_fail (E_IS_SOURCE (source), NULL);
800 doc = xmlNewDoc ((xmlChar*)"1.0");
801 node = dump_common_to_xml_node (source, NULL);
803 xmlDocSetRootElement (doc, node);
805 uri = e_source_get_uri (source);
806 xmlSetProp (node, (xmlChar*)"uri", (xmlChar*)uri);
809 xmlDocDumpMemory (doc, &xml_buffer, &xml_buffer_size);
812 returned_buffer = g_malloc (xml_buffer_size + 1);
813 memcpy (returned_buffer, xml_buffer, xml_buffer_size);
814 returned_buffer [xml_buffer_size] = '\0';
815 xmlFree (xml_buffer);
817 return returned_buffer;
822 e_source_new_from_standalone_xml (const char *xml)
828 doc = xmlParseDoc ((xmlChar*)xml);
832 root = doc->children;
833 if (strcmp ((char*)root->name, "source") != 0)
836 source = e_source_new_from_xml_node (root);
844 e_source_get_property (ESource *source,
845 const gchar *property)
847 ESourcePrivate *priv;
849 g_return_val_if_fail (E_IS_SOURCE (source), NULL);
852 return g_hash_table_lookup (priv->properties, property);
856 e_source_get_duped_property (ESource *source, const char *property)
858 ESourcePrivate *priv;
860 g_return_val_if_fail (E_IS_SOURCE (source), NULL);
863 return g_strdup (g_hash_table_lookup (priv->properties, property));
867 e_source_set_property (ESource *source,
868 const gchar *property,
871 ESourcePrivate *priv;
873 g_return_if_fail (E_IS_SOURCE (source));
877 g_hash_table_replace (priv->properties, g_strdup (property), g_strdup (value));
879 g_hash_table_remove (priv->properties, property);
881 g_signal_emit (source, signals[CHANGED], 0);
886 e_source_foreach_property (ESource *source, GHFunc func, gpointer data)
888 ESourcePrivate *priv;
890 g_return_if_fail (E_IS_SOURCE (source));
893 g_hash_table_foreach (priv->properties, func, data);
898 copy_property (const gchar *key, const gchar *value, ESource *new_source)
900 e_source_set_property (new_source, key, value);
905 e_source_copy (ESource *source)
909 g_return_val_if_fail (E_IS_SOURCE (source), NULL);
911 new_source = g_object_new (e_source_get_type (), NULL);
912 new_source->priv->uid = g_strdup (e_source_peek_uid (source));
914 e_source_set_name (new_source, e_source_peek_name (source));
916 new_source->priv->color_spec = g_strdup (source->priv->color_spec);
918 new_source->priv->absolute_uri = g_strdup (e_source_peek_absolute_uri (source));
920 e_source_foreach_property (source, (GHFunc) copy_property, new_source);