Initial import of ephy (rev# 7126) from svn
[ephy-soc.git] / lib / ephy-node.c
blob736dbdc95ecc6b04888e55310ed9210c79516103
1 /*
2 * Copyright © 2002 Jorn Baayen <jorn@nl.linux.org>
3 * Copyright © 2003 Marco Pesenti Gritti
4 * Copyright © 2003 Christian Persch
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.
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.
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, MA 02110-1301, USA.
20 * $Id: ephy-node.c 6952 2007-03-11 19:42:02Z chpe $
23 #include "config.h"
25 #include <glib.h>
26 #include <glib/gi18n.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <stdarg.h>
30 #include <gdk/gdk.h>
31 #include <time.h>
33 #include "ephy-node.h"
35 typedef struct
37 EphyNode *node;
38 int id;
39 EphyNodeCallback callback;
40 EphyNodeSignalType type;
41 gpointer data;
42 gboolean invalidated;
43 } EphyNodeSignalData;
45 typedef struct
47 EphyNode *node;
48 guint index;
49 } EphyNodeParent;
51 typedef struct
53 EphyNode *node;
54 guint property_id;
55 } EphyNodeChange;
57 struct _EphyNode
59 int ref_count;
61 guint id;
63 GPtrArray *properties;
65 GHashTable *parents;
66 GPtrArray *children;
68 GHashTable *signals;
69 int signal_id;
70 guint emissions;
71 guint invalidated_signals;
72 guint is_drag_source : 1;
73 guint is_drag_dest : 1;
75 EphyNodeDb *db;
78 typedef struct
80 EphyNodeSignalType type;
81 va_list valist;
82 } ENESCData;
84 static gboolean
85 int_equal (gconstpointer a,
86 gconstpointer b)
88 return GPOINTER_TO_INT (a) == GPOINTER_TO_INT (b);
91 static guint
92 int_hash (gconstpointer a)
94 return GPOINTER_TO_INT (a);
97 static void
98 callback (long id, EphyNodeSignalData *data, gpointer *dummy)
100 ENESCData *user_data;
101 va_list valist;
103 if (data->invalidated) return;
105 user_data = (ENESCData *) dummy;
107 G_VA_COPY(valist, user_data->valist);
109 if (data->type != user_data->type) return;
111 switch (data->type)
113 case EPHY_NODE_DESTROY:
114 case EPHY_NODE_RESTORED:
115 data->callback (data->node, data->data);
116 break;
118 case EPHY_NODE_CHANGED:
120 guint property_id;
122 property_id = va_arg (valist, guint);
124 data->callback (data->node, property_id, data->data);
126 break;
128 case EPHY_NODE_CHILD_ADDED:
130 EphyNode *node;
132 node = va_arg (valist, EphyNode *);
134 data->callback (data->node, node, data->data);
136 break;
138 case EPHY_NODE_CHILD_CHANGED:
140 EphyNode *node;
141 guint property_id;
143 node = va_arg (valist, EphyNode *);
144 property_id = va_arg (valist, guint);
146 data->callback (data->node, node, property_id, data->data);
148 break;
150 case EPHY_NODE_CHILD_REMOVED:
152 EphyNode *node;
153 guint last_index;
155 node = va_arg (valist, EphyNode *);
156 last_index = va_arg (valist, guint);
158 data->callback (data->node, node, last_index, data->data);
160 break;
162 case EPHY_NODE_CHILDREN_REORDERED:
163 data->callback (data->node, va_arg (valist, int *), data->data);
164 break;
167 va_end(valist);
170 static gboolean
171 remove_invalidated_signals (long id,
172 EphyNodeSignalData *data,
173 gpointer user_data)
175 return data->invalidated;
178 static void
179 ephy_node_emit_signal (EphyNode *node, EphyNodeSignalType type, ...)
181 ENESCData data;
183 ++node->emissions;
185 va_start (data.valist, type);
187 data.type = type;
189 g_hash_table_foreach (node->signals,
190 (GHFunc) callback,
191 &data);
193 va_end (data.valist);
195 if (G_UNLIKELY (--node->emissions == 0 && node->invalidated_signals))
197 int removed;
199 removed = g_hash_table_foreach_remove
200 (node->signals,
201 (GHRFunc) remove_invalidated_signals,
202 NULL);
203 g_assert (removed == node->invalidated_signals);
205 node->invalidated_signals = 0;
209 static inline void
210 real_remove_child (EphyNode *node,
211 EphyNode *child,
212 gboolean remove_from_parent,
213 gboolean remove_from_child)
215 EphyNodeParent *node_info;
217 node_info = g_hash_table_lookup (child->parents,
218 GINT_TO_POINTER (node->id));
220 if (remove_from_parent) {
221 guint i;
222 guint old_index;
224 old_index = node_info->index;
226 g_ptr_array_remove_index (node->children,
227 node_info->index);
229 /* correct indices on kids */
230 for (i = node_info->index; i < node->children->len; i++) {
231 EphyNode *borked_node;
232 EphyNodeParent *borked_node_info;
234 borked_node = g_ptr_array_index (node->children, i);
237 borked_node_info = g_hash_table_lookup (borked_node->parents,
238 GINT_TO_POINTER (node->id));
239 borked_node_info->index--;
242 ephy_node_emit_signal (node, EPHY_NODE_CHILD_REMOVED, child, old_index);
245 if (remove_from_child) {
246 g_hash_table_remove (child->parents,
247 GINT_TO_POINTER (node->id));
251 static void
252 remove_child (long id,
253 EphyNodeParent *node_info,
254 EphyNode *node)
256 real_remove_child (node_info->node, node, TRUE, FALSE);
259 static void
260 signal_object_weak_notify (EphyNodeSignalData *signal_data,
261 GObject *where_the_object_was)
263 signal_data->data = NULL;
264 ephy_node_signal_disconnect (signal_data->node, signal_data->id);
267 static void
268 destroy_signal_data (EphyNodeSignalData *signal_data)
270 if (signal_data->data)
272 g_object_weak_unref (G_OBJECT (signal_data->data),
273 (GWeakNotify)signal_object_weak_notify,
274 signal_data);
277 g_slice_free (EphyNodeSignalData, signal_data);
280 static void
281 node_parent_free (EphyNodeParent *parent)
283 g_slice_free (EphyNodeParent, parent);
286 static void
287 ephy_node_destroy (EphyNode *node)
289 guint i;
291 ephy_node_emit_signal (node, EPHY_NODE_DESTROY);
293 /* Remove from parents. */
294 g_hash_table_foreach (node->parents,
295 (GHFunc) remove_child,
296 node);
297 g_hash_table_destroy (node->parents);
299 /* Remove children. */
300 for (i = 0; i < node->children->len; i++) {
301 EphyNode *child;
303 child = g_ptr_array_index (node->children, i);
305 real_remove_child (node, child, FALSE, TRUE);
307 g_ptr_array_free (node->children, TRUE);
309 /* Remove signals. */
310 g_hash_table_destroy (node->signals);
312 /* Remove id. */
313 _ephy_node_db_remove_id (node->db, node->id);
315 /* Remove properties. */
316 for (i = 0; i < node->properties->len; i++) {
317 GValue *val;
319 val = g_ptr_array_index (node->properties, i);
321 if (val != NULL) {
322 g_value_unset (val);
323 g_slice_free (GValue, val);
326 g_ptr_array_free (node->properties, TRUE);
328 g_slice_free (EphyNode, node);
331 EphyNode *
332 ephy_node_new (EphyNodeDb *db)
334 long id;
336 g_return_val_if_fail (EPHY_IS_NODE_DB (db), NULL);
338 if (ephy_node_db_is_immutable (db)) return NULL;
340 id = _ephy_node_db_new_id (db);
342 return ephy_node_new_with_id (db, id);
345 EphyNode *
346 ephy_node_new_with_id (EphyNodeDb *db, guint reserved_id)
348 EphyNode *node;
350 g_return_val_if_fail (EPHY_IS_NODE_DB (db), NULL);
352 if (ephy_node_db_is_immutable (db)) return NULL;
354 node = g_slice_new0 (EphyNode);
356 node->ref_count = 1;
358 node->id = reserved_id;
360 node->db = db;
362 node->properties = g_ptr_array_new ();
364 node->children = g_ptr_array_new ();
366 node->parents = g_hash_table_new_full
367 (int_hash, int_equal, NULL, (GDestroyNotify) node_parent_free);
369 node->signals = g_hash_table_new_full
370 (int_hash, int_equal, NULL,
371 (GDestroyNotify)destroy_signal_data);
373 node->signal_id = 0;
374 node->emissions = 0;
375 node->invalidated_signals = 0;
376 node->is_drag_source = TRUE;
377 node->is_drag_dest = TRUE;
379 _ephy_node_db_add_id (db, reserved_id, node);
381 return node;
384 EphyNodeDb *
385 ephy_node_get_db (EphyNode *node)
387 g_return_val_if_fail (EPHY_IS_NODE (node), NULL);
389 return node->db;
392 guint
393 ephy_node_get_id (EphyNode *node)
395 long ret;
397 g_return_val_if_fail (EPHY_IS_NODE (node), G_MAXUINT);
399 ret = node->id;
401 return ret;
404 void
405 ephy_node_ref (EphyNode *node)
407 g_return_if_fail (EPHY_IS_NODE (node));
409 node->ref_count++;
412 void
413 ephy_node_unref (EphyNode *node)
415 g_return_if_fail (EPHY_IS_NODE (node));
417 node->ref_count--;
419 if (node->ref_count <= 0) {
420 ephy_node_destroy (node);
424 static void
425 child_changed (guint id,
426 EphyNodeParent *node_info,
427 EphyNodeChange *change)
429 ephy_node_emit_signal (node_info->node, EPHY_NODE_CHILD_CHANGED,
430 change->node, change->property_id);
433 static inline void
434 real_set_property (EphyNode *node,
435 guint property_id,
436 GValue *value)
438 GValue *old;
440 if (property_id >= node->properties->len) {
441 g_ptr_array_set_size (node->properties, property_id + 1);
444 old = g_ptr_array_index (node->properties, property_id);
445 if (old != NULL) {
446 g_value_unset (old);
447 g_slice_free (GValue, old);
450 g_ptr_array_index (node->properties, property_id) = value;
453 static inline void
454 ephy_node_set_property_internal (EphyNode *node,
455 guint property_id,
456 GValue *value)
458 EphyNodeChange change;
460 real_set_property (node, property_id, value);
462 change.node = node;
463 change.property_id = property_id;
464 g_hash_table_foreach (node->parents,
465 (GHFunc) child_changed,
466 &change);
468 ephy_node_emit_signal (node, EPHY_NODE_CHANGED, property_id);
472 void
473 ephy_node_set_property (EphyNode *node,
474 guint property_id,
475 const GValue *value)
477 GValue *new;
479 g_return_if_fail (EPHY_IS_NODE (node));
480 g_return_if_fail (value != NULL);
482 if (ephy_node_db_is_immutable (node->db)) return;
484 new = g_slice_new0 (GValue);
485 g_value_init (new, G_VALUE_TYPE (value));
486 g_value_copy (value, new);
488 ephy_node_set_property_internal (node, property_id, new);
491 gboolean
492 ephy_node_get_property (EphyNode *node,
493 guint property_id,
494 GValue *value)
496 GValue *ret;
498 g_return_val_if_fail (EPHY_IS_NODE (node), FALSE);
499 g_return_val_if_fail (value != NULL, FALSE);
501 if (property_id >= node->properties->len) {
502 return FALSE;
505 ret = g_ptr_array_index (node->properties, property_id);
506 if (ret == NULL) {
507 return FALSE;
510 g_value_init (value, G_VALUE_TYPE (ret));
511 g_value_copy (ret, value);
513 return TRUE;
516 void
517 ephy_node_set_property_string (EphyNode *node,
518 guint property_id,
519 const char *value)
521 GValue *new;
523 g_return_if_fail (EPHY_IS_NODE (node));
525 if (ephy_node_db_is_immutable (node->db)) return;
527 new = g_slice_new0 (GValue);
528 g_value_init (new, G_TYPE_STRING);
529 g_value_set_string (new, value);
531 ephy_node_set_property_internal (node, property_id, new);
534 const char *
535 ephy_node_get_property_string (EphyNode *node,
536 guint property_id)
538 GValue *ret;
539 const char *retval;
541 g_return_val_if_fail (EPHY_IS_NODE (node), NULL);
543 if (property_id >= node->properties->len) {
544 return NULL;
547 ret = g_ptr_array_index (node->properties, property_id);
548 if (ret == NULL) {
549 return NULL;
552 retval = g_value_get_string (ret);
554 return retval;
557 void
558 ephy_node_set_property_boolean (EphyNode *node,
559 guint property_id,
560 gboolean value)
562 GValue *new;
564 g_return_if_fail (EPHY_IS_NODE (node));
566 if (ephy_node_db_is_immutable (node->db)) return;
568 new = g_slice_new0 (GValue);
569 g_value_init (new, G_TYPE_BOOLEAN);
570 g_value_set_boolean (new, value);
572 ephy_node_set_property_internal (node, property_id, new);
575 gboolean
576 ephy_node_get_property_boolean (EphyNode *node,
577 guint property_id)
579 GValue *ret;
580 gboolean retval;
582 g_return_val_if_fail (EPHY_IS_NODE (node), FALSE);
584 if (property_id >= node->properties->len) {
585 return FALSE;
588 ret = g_ptr_array_index (node->properties, property_id);
589 if (ret == NULL) {
590 return FALSE;
593 retval = g_value_get_boolean (ret);
595 return retval;
598 void
599 ephy_node_set_property_long (EphyNode *node,
600 guint property_id,
601 long value)
603 GValue *new;
605 g_return_if_fail (EPHY_IS_NODE (node));
607 if (ephy_node_db_is_immutable (node->db)) return;
609 new = g_slice_new0 (GValue);
610 g_value_init (new, G_TYPE_LONG);
611 g_value_set_long (new, value);
613 ephy_node_set_property_internal (node, property_id, new);
616 long
617 ephy_node_get_property_long (EphyNode *node,
618 guint property_id)
620 GValue *ret;
621 long retval;
623 g_return_val_if_fail (EPHY_IS_NODE (node), -1);
625 if (property_id >= node->properties->len) {
626 return -1;
629 ret = g_ptr_array_index (node->properties, property_id);
630 if (ret == NULL) {
631 return -1;
634 retval = g_value_get_long (ret);
636 return retval;
639 void
640 ephy_node_set_property_int (EphyNode *node,
641 guint property_id,
642 int value)
644 GValue *new;
646 g_return_if_fail (EPHY_IS_NODE (node));
648 if (ephy_node_db_is_immutable (node->db)) return;
650 new = g_slice_new0 (GValue);
651 g_value_init (new, G_TYPE_INT);
652 g_value_set_int (new, value);
654 ephy_node_set_property_internal (node, property_id, new);
658 ephy_node_get_property_int (EphyNode *node,
659 guint property_id)
661 GValue *ret;
662 int retval;
664 g_return_val_if_fail (EPHY_IS_NODE (node), -1);
666 if (property_id >= node->properties->len) {
667 return -1;
670 ret = g_ptr_array_index (node->properties, property_id);
671 if (ret == NULL) {
672 return -1;
675 retval = g_value_get_int (ret);
677 return retval;
680 void
681 ephy_node_set_property_double (EphyNode *node,
682 guint property_id,
683 double value)
685 GValue *new;
687 g_return_if_fail (EPHY_IS_NODE (node));
689 if (ephy_node_db_is_immutable (node->db)) return;
691 new = g_slice_new0 (GValue);
692 g_value_init (new, G_TYPE_DOUBLE);
693 g_value_set_double (new, value);
695 ephy_node_set_property_internal (node, property_id, new);
698 double
699 ephy_node_get_property_double (EphyNode *node,
700 guint property_id)
702 GValue *ret;
703 double retval;
705 g_return_val_if_fail (EPHY_IS_NODE (node), -1);
707 if (property_id >= node->properties->len) {
708 return -1;
711 ret = g_ptr_array_index (node->properties, property_id);
712 if (ret == NULL) {
713 return -1;
716 retval = g_value_get_double (ret);
718 return retval;
721 void
722 ephy_node_set_property_float (EphyNode *node,
723 guint property_id,
724 float value)
726 GValue *new;
728 g_return_if_fail (EPHY_IS_NODE (node));
730 if (ephy_node_db_is_immutable (node->db)) return;
732 new = g_slice_new0 (GValue);
733 g_value_init (new, G_TYPE_FLOAT);
734 g_value_set_float (new, value);
736 ephy_node_set_property_internal (node, property_id, new);
739 float
740 ephy_node_get_property_float (EphyNode *node,
741 guint property_id)
743 GValue *ret;
744 float retval;
746 g_return_val_if_fail (EPHY_IS_NODE (node), -1);
748 if (property_id >= node->properties->len) {
749 return -1;
752 ret = g_ptr_array_index (node->properties, property_id);
753 if (ret == NULL) {
754 return -1;
757 retval = g_value_get_float (ret);
759 return retval;
762 EphyNode *
763 ephy_node_get_property_node (EphyNode *node,
764 guint property_id)
766 GValue *ret;
767 EphyNode *retval;
769 g_return_val_if_fail (EPHY_IS_NODE (node), NULL);
770 g_return_val_if_fail (property_id >= 0, NULL);
772 if (property_id >= node->properties->len) {
773 return NULL;
776 ret = g_ptr_array_index (node->properties, property_id);
777 if (ret == NULL) {
778 return NULL;
781 retval = g_value_get_pointer (ret);
783 return retval;
786 typedef struct
788 xmlTextWriterPtr writer;
789 int ret;
790 } ForEachData;
792 static void
793 write_parent (guint id,
794 EphyNodeParent *node_info,
795 ForEachData* data)
797 xmlTextWriterPtr writer = data->writer;
799 /* there already was an error, do nothing. this works around
800 * the fact that g_hash_table_foreach cannot be cancelled.
802 if (data->ret < 0) return;
804 data->ret = xmlTextWriterStartElement (writer, (const xmlChar *)"parent");
805 if (data->ret < 0) return;
807 data->ret = xmlTextWriterWriteFormatAttribute
808 (writer, (const xmlChar *)"id", "%d", node_info->node->id);
809 if (data->ret < 0) return;
811 data->ret = xmlTextWriterEndElement (writer); /* parent */
812 if (data->ret < 0) return;
815 static inline int
816 safe_write_string (xmlTextWriterPtr writer,
817 const xmlChar *string)
819 int ret;
820 xmlChar *copy, *p;
822 if (!string)
823 return 0;
825 /* http://www.w3.org/TR/REC-xml/#sec-well-formed :
826 Character Range
827 [2] Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] |
828 [#xE000-#xFFFD] | [#x10000-#x10FFFF]
829 any Unicode character, excluding the surrogate blocks, FFFE, and FFFF.
832 copy = xmlStrdup (string);
833 for (p = copy; *p; p++)
835 xmlChar c = *p;
836 if (G_UNLIKELY (c < 0x20 && c != 0xd && c != 0xa && c != 0x9)) {
837 *p = 0x20;
841 ret = xmlTextWriterWriteString (writer, copy);
842 xmlFree (copy);
844 return ret;
848 ephy_node_write_to_xml(EphyNode *node,
849 xmlTextWriterPtr writer)
851 xmlChar xml_buf[G_ASCII_DTOSTR_BUF_SIZE];
852 guint i;
853 int ret;
854 ForEachData data;
856 g_return_val_if_fail (EPHY_IS_NODE (node), -1);
857 g_return_val_if_fail (writer != NULL, -1);
859 /* start writing the node */
860 ret = xmlTextWriterStartElement (writer, (const xmlChar *)"node");
861 if (ret < 0) goto out;
863 /* write node id */
864 ret = xmlTextWriterWriteFormatAttribute (writer, (const xmlChar *)"id", "%d", node->id);
865 if (ret < 0) goto out;
867 /* write node properties */
868 for (i = 0; i < node->properties->len; i++)
870 GValue *value;
872 value = g_ptr_array_index (node->properties, i);
874 if (value == NULL) continue;
875 if (G_VALUE_TYPE (value) == G_TYPE_STRING &&
876 g_value_get_string (value) == NULL) continue;
878 ret = xmlTextWriterStartElement (writer, (const xmlChar *)"property");
879 if (ret < 0) break;
881 ret = xmlTextWriterWriteFormatAttribute (writer, (const xmlChar *)"id", "%d", i);
882 if (ret < 0) break;
884 ret = xmlTextWriterWriteAttribute
885 (writer, (const xmlChar *)"value_type",
886 (const xmlChar *)g_type_name (G_VALUE_TYPE (value)));
887 if (ret < 0) break;
889 switch (G_VALUE_TYPE (value))
891 case G_TYPE_STRING:
892 ret = safe_write_string
893 (writer, (const xmlChar *)g_value_get_string (value));
894 break;
895 case G_TYPE_BOOLEAN:
896 ret = xmlTextWriterWriteFormatString
897 (writer, "%d", g_value_get_boolean (value));
898 break;
899 case G_TYPE_INT:
900 ret = xmlTextWriterWriteFormatString
901 (writer, "%d", g_value_get_int (value));
902 break;
903 case G_TYPE_LONG:
904 ret = xmlTextWriterWriteFormatString
905 (writer, "%ld", g_value_get_long (value));
906 break;
907 case G_TYPE_FLOAT:
908 g_ascii_dtostr ((gchar *)xml_buf, sizeof (xml_buf),
909 g_value_get_float (value));
910 ret = xmlTextWriterWriteString (writer, xml_buf);
911 break;
912 case G_TYPE_DOUBLE:
913 g_ascii_dtostr ((gchar *)xml_buf, sizeof (xml_buf),
914 g_value_get_double (value));
915 ret = xmlTextWriterWriteString (writer, xml_buf);
916 break;
917 default:
918 g_assert_not_reached ();
919 break;
921 if (ret < 0) break;
923 ret = xmlTextWriterEndElement (writer); /* property */
924 if (ret < 0) break;
926 if (ret < 0) goto out;
928 /* now write parent node ids */
929 data.writer = writer;
930 data.ret = 0;
932 g_hash_table_foreach (node->parents,
933 (GHFunc) write_parent,
934 &data);
935 ret = data.ret;
936 if (ret < 0) goto out;
938 ret = xmlTextWriterEndElement (writer); /* node */
939 if (ret < 0) goto out;
941 out:
942 return ret >= 0 ? 0 : -1;
945 static inline void
946 real_add_child (EphyNode *node,
947 EphyNode *child)
949 EphyNodeParent *node_info;
951 if (g_hash_table_lookup (child->parents,
952 GINT_TO_POINTER (node->id)) != NULL) {
953 return;
956 g_ptr_array_add (node->children, child);
958 node_info = g_slice_new0 (EphyNodeParent);
959 node_info->node = node;
960 node_info->index = node->children->len - 1;
962 g_hash_table_insert (child->parents,
963 GINT_TO_POINTER (node->id),
964 node_info);
967 EphyNode *
968 ephy_node_new_from_xml (EphyNodeDb *db, xmlNodePtr xml_node)
970 EphyNode *node;
971 xmlNodePtr xml_child;
972 xmlChar *xml;
973 long id;
975 g_return_val_if_fail (EPHY_IS_NODE_DB (db), NULL);
976 g_return_val_if_fail (xml_node != NULL, NULL);
978 if (ephy_node_db_is_immutable (db)) return NULL;
980 xml = xmlGetProp (xml_node, (const xmlChar *)"id");
981 if (xml == NULL)
982 return NULL;
983 id = atol ((const char *)xml);
984 xmlFree (xml);
986 node = ephy_node_new_with_id (db, id);
988 for (xml_child = xml_node->children; xml_child != NULL; xml_child = xml_child->next) {
989 if (strcmp ((const char *)xml_child->name, "parent") == 0) {
990 EphyNode *parent;
991 long parent_id;
993 xml = xmlGetProp (xml_child, (const xmlChar *)"id");
994 g_assert (xml != NULL);
995 parent_id = atol ((const char *)xml);
996 xmlFree (xml);
998 parent = ephy_node_db_get_node_from_id (db, parent_id);
1000 if (parent != NULL)
1002 real_add_child (parent, node);
1004 ephy_node_emit_signal (parent, EPHY_NODE_CHILD_ADDED, node);
1006 } else if (strcmp ((const char *)xml_child->name, "property") == 0) {
1007 GValue *value;
1008 xmlChar *xmlType, *xmlValue;
1009 int property_id;
1011 xml = xmlGetProp (xml_child, (const xmlChar *)"id");
1012 property_id = atoi ((const char *)xml);
1013 xmlFree (xml);
1015 xmlType = xmlGetProp (xml_child, (const xmlChar *)"value_type");
1016 xmlValue = xmlNodeGetContent (xml_child);
1018 value = g_slice_new0 (GValue);
1020 if (xmlStrEqual (xmlType, (const xmlChar *) "gchararray"))
1022 g_value_init (value, G_TYPE_STRING);
1023 g_value_set_string (value, (const gchar *)xmlValue);
1025 else if (xmlStrEqual (xmlType, (const xmlChar *) "gint"))
1027 g_value_init (value, G_TYPE_INT);
1028 g_value_set_int (value, atoi ((const char *)xmlValue));
1030 else if (xmlStrEqual (xmlType, (const xmlChar *) "gboolean"))
1032 g_value_init (value, G_TYPE_BOOLEAN);
1033 g_value_set_boolean (value, atoi ((const char *)xmlValue));
1035 else if (xmlStrEqual (xmlType, (const xmlChar *) "glong"))
1037 g_value_init (value, G_TYPE_LONG);
1038 g_value_set_long (value, atol ((const char *)xmlValue));
1040 else if (xmlStrEqual (xmlType, (const xmlChar *) "gfloat"))
1042 g_value_init (value, G_TYPE_FLOAT);
1043 g_value_set_float (value, g_ascii_strtod ((const gchar *)xmlValue, NULL));
1045 else if (xmlStrEqual (xmlType, (const xmlChar *) "gdouble"))
1047 g_value_init (value, G_TYPE_DOUBLE);
1048 g_value_set_double (value, g_ascii_strtod ((const gchar *)xmlValue, NULL));
1050 else if (xmlStrEqual (xmlType, (const xmlChar *) "gpointer"))
1052 EphyNode *property_node;
1054 property_node = ephy_node_db_get_node_from_id (db, atol ((const char *)xmlValue));
1056 g_value_set_pointer (value, property_node);
1057 break;
1059 else
1061 g_assert_not_reached ();
1064 real_set_property (node, property_id, value);
1066 xmlFree (xmlValue);
1067 xmlFree (xmlType);
1071 ephy_node_emit_signal (node, EPHY_NODE_RESTORED);
1073 return node;
1076 void
1077 ephy_node_add_child (EphyNode *node,
1078 EphyNode *child)
1080 g_return_if_fail (EPHY_IS_NODE (node));
1082 if (ephy_node_db_is_immutable (node->db)) return;
1084 real_add_child (node, child);
1086 ephy_node_emit_signal (node, EPHY_NODE_CHILD_ADDED, child);
1089 void
1090 ephy_node_remove_child (EphyNode *node,
1091 EphyNode *child)
1093 g_return_if_fail (EPHY_IS_NODE (node));
1095 if (ephy_node_db_is_immutable (node->db)) return;
1097 real_remove_child (node, child, TRUE, TRUE);
1100 gboolean
1101 ephy_node_has_child (EphyNode *node,
1102 EphyNode *child)
1104 gboolean ret;
1106 g_return_val_if_fail (EPHY_IS_NODE (node), FALSE);
1108 ret = (g_hash_table_lookup (child->parents,
1109 GINT_TO_POINTER (node->id)) != NULL);
1111 return ret;
1114 static int
1115 ephy_node_real_get_child_index (EphyNode *node,
1116 EphyNode *child)
1118 EphyNodeParent *node_info;
1119 int ret;
1121 node_info = g_hash_table_lookup (child->parents,
1122 GINT_TO_POINTER (node->id));
1124 if (node_info == NULL)
1125 return -1;
1127 ret = node_info->index;
1129 return ret;
1132 void
1133 ephy_node_sort_children (EphyNode *node,
1134 GCompareFunc compare_func)
1136 GPtrArray *newkids;
1137 int i, *new_order;
1139 if (ephy_node_db_is_immutable (node->db)) return;
1141 g_return_if_fail (EPHY_IS_NODE (node));
1142 g_return_if_fail (compare_func != NULL);
1144 newkids = g_ptr_array_new ();
1145 g_ptr_array_set_size (newkids, node->children->len);
1147 /* dup the array */
1148 for (i = 0; i < node->children->len; i++)
1150 g_ptr_array_index (newkids, i) = g_ptr_array_index (node->children, i);
1153 g_ptr_array_sort (newkids, compare_func);
1155 new_order = g_new (int, newkids->len);
1156 memset (new_order, -1, sizeof (int) * newkids->len);
1158 for (i = 0; i < newkids->len; i++)
1160 EphyNodeParent *node_info;
1161 EphyNode *child;
1163 child = g_ptr_array_index (newkids, i);
1164 new_order[ephy_node_real_get_child_index (node, child)] = i;
1165 node_info = g_hash_table_lookup (child->parents,
1166 GINT_TO_POINTER (node->id));
1167 node_info->index = i;
1170 g_ptr_array_free (node->children, FALSE);
1171 node->children = newkids;
1173 ephy_node_emit_signal (node, EPHY_NODE_CHILDREN_REORDERED, new_order);
1175 g_free (new_order);
1178 void
1179 ephy_node_reorder_children (EphyNode *node,
1180 int *new_order)
1182 GPtrArray *newkids;
1183 int i;
1185 g_return_if_fail (EPHY_IS_NODE (node));
1186 g_return_if_fail (new_order != NULL);
1188 if (ephy_node_db_is_immutable (node->db)) return;
1190 newkids = g_ptr_array_new ();
1191 g_ptr_array_set_size (newkids, node->children->len);
1193 for (i = 0; i < node->children->len; i++) {
1194 EphyNode *child;
1195 EphyNodeParent *node_info;
1197 child = g_ptr_array_index (node->children, i);
1199 g_ptr_array_index (newkids, new_order[i]) = child;
1201 node_info = g_hash_table_lookup (child->parents,
1202 GINT_TO_POINTER (node->id));
1203 node_info->index = new_order[i];
1206 g_ptr_array_free (node->children, FALSE);
1207 node->children = newkids;
1209 ephy_node_emit_signal (node, EPHY_NODE_CHILDREN_REORDERED, new_order);
1212 GPtrArray *
1213 ephy_node_get_children (EphyNode *node)
1215 g_return_val_if_fail (EPHY_IS_NODE (node), NULL);
1217 return node->children;
1221 ephy_node_get_n_children (EphyNode *node)
1223 int ret;
1225 g_return_val_if_fail (EPHY_IS_NODE (node), -1);
1227 ret = node->children->len;
1229 return ret;
1232 EphyNode *
1233 ephy_node_get_nth_child (EphyNode *node,
1234 guint n)
1236 EphyNode *ret;
1238 g_return_val_if_fail (EPHY_IS_NODE (node), NULL);
1239 g_return_val_if_fail (n >= 0, NULL);
1241 if (n < node->children->len) {
1242 ret = g_ptr_array_index (node->children, n);
1243 } else {
1244 ret = NULL;
1247 return ret;
1250 static inline int
1251 get_child_index_real (EphyNode *node,
1252 EphyNode *child)
1254 EphyNodeParent *node_info;
1256 node_info = g_hash_table_lookup (child->parents,
1257 GINT_TO_POINTER (node->id));
1259 if (node_info == NULL)
1260 return -1;
1262 return node_info->index;
1267 ephy_node_get_child_index (EphyNode *node,
1268 EphyNode *child)
1270 int ret;
1272 g_return_val_if_fail (EPHY_IS_NODE (node), -1);
1273 g_return_val_if_fail (EPHY_IS_NODE (child), -1);
1275 ret = ephy_node_real_get_child_index (node, child);
1277 return ret;
1280 EphyNode *
1281 ephy_node_get_next_child (EphyNode *node,
1282 EphyNode *child)
1284 EphyNode *ret;
1285 guint idx;
1287 g_return_val_if_fail (EPHY_IS_NODE (node), NULL);
1288 g_return_val_if_fail (EPHY_IS_NODE (child), NULL);
1290 idx = get_child_index_real (node, child);
1292 if ((idx + 1) < node->children->len) {
1293 ret = g_ptr_array_index (node->children, idx + 1);
1294 } else {
1295 ret = NULL;
1298 return ret;
1301 EphyNode *
1302 ephy_node_get_previous_child (EphyNode *node,
1303 EphyNode *child)
1305 EphyNode *ret;
1306 int idx;
1308 g_return_val_if_fail (EPHY_IS_NODE (node), NULL);
1309 g_return_val_if_fail (EPHY_IS_NODE (child), NULL);
1311 idx = get_child_index_real (node, child);
1313 if ((idx - 1) >= 0) {
1314 ret = g_ptr_array_index (node->children, idx - 1);
1315 } else {
1316 ret = NULL;
1319 return ret;
1323 ephy_node_signal_connect_object (EphyNode *node,
1324 EphyNodeSignalType type,
1325 EphyNodeCallback callback,
1326 GObject *object)
1328 EphyNodeSignalData *signal_data;
1329 int ret;
1331 g_return_val_if_fail (EPHY_IS_NODE (node), -1);
1332 /* FIXME: */
1333 g_return_val_if_fail (node->emissions == 0, -1);
1335 signal_data = g_slice_new0 (EphyNodeSignalData);
1336 signal_data->node = node;
1337 signal_data->id = node->signal_id;
1338 signal_data->callback = callback;
1339 signal_data->type = type;
1340 signal_data->data = object;
1342 g_hash_table_insert (node->signals,
1343 GINT_TO_POINTER (node->signal_id),
1344 signal_data);
1345 if (object)
1347 g_object_weak_ref (object,
1348 (GWeakNotify)signal_object_weak_notify,
1349 signal_data);
1352 ret = node->signal_id;
1353 node->signal_id++;
1355 return ret;
1358 static gboolean
1359 remove_matching_signal_data (gpointer key,
1360 EphyNodeSignalData *signal_data,
1361 EphyNodeSignalData *user_data)
1363 return (user_data->data == signal_data->data &&
1364 user_data->type == signal_data->type &&
1365 user_data->callback == signal_data->callback);
1368 static void
1369 invalidate_matching_signal_data (gpointer key,
1370 EphyNodeSignalData *signal_data,
1371 EphyNodeSignalData *user_data)
1373 if (user_data->data == signal_data->data &&
1374 user_data->type == signal_data->type &&
1375 user_data->callback == signal_data->callback &&
1376 !signal_data->invalidated)
1378 signal_data->invalidated = TRUE;
1379 ++signal_data->node->invalidated_signals;
1383 guint
1384 ephy_node_signal_disconnect_object (EphyNode *node,
1385 EphyNodeSignalType type,
1386 EphyNodeCallback callback,
1387 GObject *object)
1389 EphyNodeSignalData user_data;
1391 g_return_val_if_fail (EPHY_IS_NODE (node), 0);
1393 user_data.callback = callback;
1394 user_data.type = type;
1395 user_data.data = object;
1397 if (G_LIKELY (node->emissions == 0))
1399 return g_hash_table_foreach_remove (node->signals,
1400 (GHRFunc) remove_matching_signal_data,
1401 &user_data);
1403 else
1405 g_hash_table_foreach (node->signals,
1406 (GHFunc) invalidate_matching_signal_data,
1407 &user_data);
1408 return 0;
1412 void
1413 ephy_node_signal_disconnect (EphyNode *node,
1414 int signal_id)
1416 g_return_if_fail (EPHY_IS_NODE (node));
1417 g_return_if_fail (signal_id != -1);
1419 if (G_LIKELY (node->emissions == 0))
1421 g_hash_table_remove (node->signals,
1422 GINT_TO_POINTER (signal_id));
1424 else
1426 EphyNodeSignalData *data;
1428 data = g_hash_table_lookup (node->signals,
1429 GINT_TO_POINTER (signal_id));
1430 g_return_if_fail (data != NULL);
1431 g_return_if_fail (!data->invalidated);
1433 data->invalidated = TRUE;
1434 node->invalidated_signals++;
1438 void
1439 ephy_node_set_is_drag_source (EphyNode *node,
1440 gboolean allow)
1442 node->is_drag_source = allow != FALSE;
1445 gboolean
1446 ephy_node_get_is_drag_source (EphyNode *node)
1448 return node->is_drag_source;
1451 void
1452 ephy_node_set_is_drag_dest (EphyNode *node,
1453 gboolean allow)
1455 node->is_drag_dest = allow != FALSE;
1458 gboolean
1459 ephy_node_get_is_drag_dest (EphyNode *node)
1461 return node->is_drag_dest;
1464 GType
1465 ephy_node_get_type (void)
1467 static GType type = 0;
1469 if (G_UNLIKELY (type == 0))
1471 type = g_boxed_type_register_static ("EphyNode",
1472 (GBoxedCopyFunc) ephy_node_ref,
1473 (GBoxedFreeFunc) ephy_node_unref);
1476 return type;