Code

Indent support for XSLT extensions output.
[inkscape.git] / src / widgets / sp-xmlview-tree.cpp
1 #define __SP_XMLVIEW_TREE_C__
3 /*
4  * Specialization of GtkCTree for the XML tree view
5  *
6  * Authors:
7  *   MenTaLguY <mental@rydia.net>
8  *
9  * Copyright (C) 2002 MenTaLguY
10  *
11  * Released under the GNU GPL; see COPYING for details
12  */
14 #include <cstring>
15 #include <string>
17 #include "../xml/node-event-vector.h"
18 #include "sp-xmlview-tree.h"
20 struct NodeData {
21         SPXMLViewTree * tree;
22         GtkCTreeNode * node;
23         Inkscape::XML::Node * repr;
24 };
26 #define NODE_DATA(node) ((NodeData *)(GTK_CTREE_ROW ((node))->row.data))
28 static void sp_xmlview_tree_class_init (SPXMLViewTreeClass * klass);
29 static void sp_xmlview_tree_init (SPXMLViewTree * tree);
30 static void sp_xmlview_tree_destroy (GtkObject * object);
32 static NodeData * node_data_new (SPXMLViewTree * tree, GtkCTreeNode * node, Inkscape::XML::Node * repr);
33 static void node_data_free (gpointer data);
35 static GtkCTreeNode * add_node (SPXMLViewTree * tree, GtkCTreeNode * parent, GtkCTreeNode * before, Inkscape::XML::Node * repr);
37 static void element_child_added (Inkscape::XML::Node * repr, Inkscape::XML::Node * child, Inkscape::XML::Node * ref, gpointer data);
38 static void element_attr_changed (Inkscape::XML::Node * repr, const gchar * key, const gchar * old_value, const gchar * new_value, bool is_interactive, gpointer data);
39 static void element_child_removed (Inkscape::XML::Node * repr, Inkscape::XML::Node * child, Inkscape::XML::Node * ref, gpointer data);
40 static void element_order_changed (Inkscape::XML::Node * repr, Inkscape::XML::Node * child, Inkscape::XML::Node * oldref, Inkscape::XML::Node * newref, gpointer data);
42 static void text_content_changed (Inkscape::XML::Node * repr, const gchar * old_content, const gchar * new_content, gpointer data);
43 static void comment_content_changed (Inkscape::XML::Node * repr, const gchar * old_content, const gchar * new_content, gpointer data);
44 static void pi_content_changed (Inkscape::XML::Node * repr, const gchar * old_content, const gchar * new_content, gpointer data);
46 static void tree_move (GtkCTree * tree, GtkCTreeNode * node, GtkCTreeNode * new_parent, GtkCTreeNode * new_sibling);
48 static gboolean check_drag (GtkCTree * tree, GtkCTreeNode * node, GtkCTreeNode * new_parent, GtkCTreeNode * new_sibling);
50 static GtkCTreeNode * ref_to_sibling (GtkCTreeNode * parent, Inkscape::XML::Node * ref);
51 static GtkCTreeNode * repr_to_child (GtkCTreeNode * parent, Inkscape::XML::Node * repr);
52 static Inkscape::XML::Node * sibling_to_ref (GtkCTreeNode * parent, GtkCTreeNode * sibling);
54 static gint match_node_data_by_repr(gconstpointer data_p, gconstpointer repr);
56 static const Inkscape::XML::NodeEventVector element_repr_events = {
57         element_child_added,
58         element_child_removed,
59         element_attr_changed,
60         NULL, /* content_changed */
61         element_order_changed
62 };
64 static const Inkscape::XML::NodeEventVector text_repr_events = {
65         NULL, /* child_added */
66         NULL, /* child_removed */
67         NULL, /* attr_changed */
68         text_content_changed,
69         NULL  /* order_changed */
70 };
72 static const Inkscape::XML::NodeEventVector comment_repr_events = {
73         NULL, /* child_added */
74         NULL, /* child_removed */
75         NULL, /* attr_changed */
76         comment_content_changed,
77         NULL  /* order_changed */
78 };
80 static const Inkscape::XML::NodeEventVector pi_repr_events = {
81         NULL, /* child_added */
82         NULL, /* child_removed */
83         NULL, /* attr_changed */
84         pi_content_changed,
85         NULL  /* order_changed */
86 };
88 static GtkCTreeClass * parent_class = NULL;
90 GtkWidget *
91 sp_xmlview_tree_new (Inkscape::XML::Node * repr, void * /*factory*/, void * /*data*/)
92 {
93         SPXMLViewTree * tree;
95         tree = (SPXMLViewTree*)g_object_new (SP_TYPE_XMLVIEW_TREE, "n_columns", 1, "tree_column", 0, NULL);
97         gtk_clist_column_titles_hide (GTK_CLIST (tree));
98         gtk_ctree_set_line_style (GTK_CTREE (tree), GTK_CTREE_LINES_NONE);
99         gtk_ctree_set_expander_style (GTK_CTREE (tree), GTK_CTREE_EXPANDER_TRIANGLE);
100         gtk_clist_set_column_auto_resize (GTK_CLIST (tree), 0, TRUE);
101         gtk_clist_set_reorderable (GTK_CLIST (tree), TRUE);
102         gtk_ctree_set_drag_compare_func (GTK_CTREE (tree), check_drag);
104         sp_xmlview_tree_set_repr (tree, repr);
106         return (GtkWidget *) tree;
109 void
110 sp_xmlview_tree_set_repr (SPXMLViewTree * tree, Inkscape::XML::Node * repr)
112         if ( tree->repr == repr ) return;
113         gtk_clist_freeze (GTK_CLIST (tree));
114         if (tree->repr) {
115                 gtk_clist_clear (GTK_CLIST (tree));
116                 Inkscape::GC::release(tree->repr);
117         }
118         tree->repr = repr;
119         if (repr) {
120                 GtkCTreeNode * node;
121                 Inkscape::GC::anchor(repr);
122                 node = add_node (tree, NULL, NULL, repr);
123                 gtk_ctree_expand (GTK_CTREE (tree), node);
124         }
125         gtk_clist_thaw (GTK_CLIST (tree));
128 GtkType
129 sp_xmlview_tree_get_type (void)
131     //TODO: switch to GObject
132     // GtkType and such calls were deprecated a while back with the
133     // introduction of GObject as a separate layer, with GType instead. --JonCruz
135         static GtkType type = 0;
137         if (!type) {
138                 static const GtkTypeInfo info = {
139                         (gchar*) "SPXMLViewTree",
140                         sizeof (SPXMLViewTree),
141                         sizeof (SPXMLViewTreeClass),
142                         (GtkClassInitFunc) sp_xmlview_tree_class_init,
143                         (GtkObjectInitFunc) sp_xmlview_tree_init,
144                         NULL, NULL, NULL
145                 };
146                 type = gtk_type_unique (GTK_TYPE_CTREE, &info);
147         }
149         return type;
152 void
153 sp_xmlview_tree_class_init (SPXMLViewTreeClass * klass)
155         GtkObjectClass * object_class;
157         object_class = (GtkObjectClass *) klass;
158         parent_class = (GtkCTreeClass *) gtk_type_class (GTK_TYPE_CTREE);
160         GTK_CTREE_CLASS (object_class)->tree_move = tree_move;
162         object_class->destroy = sp_xmlview_tree_destroy;
165 void
166 sp_xmlview_tree_init (SPXMLViewTree * tree)
168         tree->repr = NULL;
169         tree->blocked = 0;
172 void
173 sp_xmlview_tree_destroy (GtkObject * object)
175         SPXMLViewTree * tree;
177         tree = SP_XMLVIEW_TREE (object);
179         sp_xmlview_tree_set_repr (tree, NULL);
181         GTK_OBJECT_CLASS (parent_class)->destroy (object);
184 GtkCTreeNode *
185 add_node (SPXMLViewTree * tree, GtkCTreeNode * parent, GtkCTreeNode * before, Inkscape::XML::Node * repr)
187         NodeData * data;
188         GtkCTreeNode * node;
189         const Inkscape::XML::NodeEventVector * vec;
190         static const gchar *default_text[] = { "???" };
192         g_assert (tree != NULL);
193         g_assert (repr != NULL);
195         node = gtk_ctree_insert_node (GTK_CTREE (tree), parent, before, (gchar **)default_text, 2, NULL, NULL, NULL, NULL, ( repr->type() != Inkscape::XML::ELEMENT_NODE ), FALSE);
196         g_assert (node != NULL);
198         data = node_data_new (tree, node, repr);
199         g_assert (data != NULL);
201         gtk_ctree_node_set_row_data_full (GTK_CTREE (tree), data->node, data, node_data_free);
203         if ( repr->type() == Inkscape::XML::TEXT_NODE ) {
204                 vec = &text_repr_events;
205         } else if ( repr->type() == Inkscape::XML::COMMENT_NODE ) {
206                 vec = &comment_repr_events;
207         } else if ( repr->type() == Inkscape::XML::PI_NODE ) {
208                 vec = &pi_repr_events;
209         } else if ( repr->type() == Inkscape::XML::ELEMENT_NODE ) {
210                 vec = &element_repr_events;
211         } else {
212                 vec = NULL;
213         }
215         if (vec) {
216                 gtk_clist_freeze (GTK_CLIST (tree));
217                 /* cheat a little to get the id upated properly */
218                 if (repr->type() == Inkscape::XML::ELEMENT_NODE) {
219                         element_attr_changed (repr, "id", NULL, NULL, false, data);
220                 }
221                 sp_repr_add_listener (repr, vec, data);
222                 sp_repr_synthesize_events (repr, vec, data);
223                 gtk_clist_thaw (GTK_CLIST (tree));
224         }
226         return node;
229 NodeData *
230 node_data_new (SPXMLViewTree * tree, GtkCTreeNode * node, Inkscape::XML::Node * repr)
232         NodeData * data;
233         data = g_new (NodeData, 1);
234         data->tree = tree;
235         data->node = node;
236         data->repr = repr;
237         Inkscape::GC::anchor(repr);
238         return data;
241 void
242 node_data_free (gpointer ptr) {
243         NodeData * data;
244         data = (NodeData *) ptr;
245         sp_repr_remove_listener_by_data (data->repr, data);
246         g_assert (data->repr != NULL);
247         Inkscape::GC::release(data->repr);
248         g_free (data);
251 void
252 element_child_added (Inkscape::XML::Node * /*repr*/, Inkscape::XML::Node * child, Inkscape::XML::Node * ref, gpointer ptr)
254         NodeData * data;
255         GtkCTreeNode * before;
257         data = (NodeData *) ptr;
259         if (data->tree->blocked) return;
261         before = ref_to_sibling (data->node, ref);
263         add_node (data->tree, data->node, before, child);
266 void
267 element_attr_changed (Inkscape::XML::Node * repr, const gchar * key, const gchar * /*old_value*/, const gchar * new_value, bool /*is_interactive*/, gpointer ptr)
269         NodeData * data;
270         gchar *label;
271         const gchar *layer;
273         data = (NodeData *) ptr;
275         if (data->tree->blocked) return;
277         if (0 != strcmp (key, "id") && 0 != strcmp (key, "inkscape:label"))
278                 return;
280         new_value = repr->attribute("id");
281         layer = repr->attribute("inkscape:label");
283         if (new_value && layer) {
284                 label = g_strdup_printf ("<%s id=\"%s\" inkscape:label=\"%s\">", repr->name(), new_value, layer);
285         } else if (new_value) {
286                 label = g_strdup_printf ("<%s id=\"%s\">", repr->name(), new_value);
287         } else {
288                 label = g_strdup_printf ("<%s>", repr->name());
289         }
290         gtk_ctree_node_set_text (GTK_CTREE (data->tree), data->node, 0, label);
291         g_free (label);
294 void
295 element_child_removed (Inkscape::XML::Node * /*repr*/, Inkscape::XML::Node * child, Inkscape::XML::Node * /*ref*/, gpointer ptr)
297         NodeData * data;
299         data = (NodeData *) ptr;
301         if (data->tree->blocked) return;
303         gtk_ctree_remove_node (GTK_CTREE (data->tree), repr_to_child (data->node, child));
306 void
307 element_order_changed (Inkscape::XML::Node * /*repr*/, Inkscape::XML::Node * child, Inkscape::XML::Node * /*oldref*/, Inkscape::XML::Node * newref, gpointer ptr)
309         NodeData * data;
310         GtkCTreeNode * before, * node;
312         data = (NodeData *) ptr;
314         if (data->tree->blocked) return;
316         before = ref_to_sibling (data->node, newref);
317         node = repr_to_child (data->node, child);
319         if ( before == node ) before = GTK_CTREE_ROW (before)->sibling;
321         parent_class->tree_move (GTK_CTREE (data->tree), node, data->node, before);
324 void
325 text_content_changed (Inkscape::XML::Node * /*repr*/, const gchar * /*old_content*/, const gchar * new_content, gpointer ptr)
327         NodeData *data;
328         gchar *label;
330         data = (NodeData *) ptr;
332         if (data->tree->blocked) return;
334         label = g_strdup_printf ("\"%s\"", new_content);
335         gtk_ctree_node_set_text (GTK_CTREE (data->tree), data->node, 0, label);
336         g_free (label);
339 void
340 comment_content_changed (Inkscape::XML::Node */*repr*/, const gchar * /*old_content*/, const gchar *new_content, gpointer ptr)
342         NodeData *data;
343         gchar *label;
345         data = (NodeData *) ptr;
347         if (data->tree->blocked) return;
349         label = g_strdup_printf ("<!--%s-->", new_content);
350         gtk_ctree_node_set_text (GTK_CTREE (data->tree), data->node, 0, label);
351         g_free (label);
354 void
355 pi_content_changed(Inkscape::XML::Node *repr, const gchar * /*old_content*/, const gchar *new_content, gpointer ptr)
357         NodeData *data;
358         gchar *label;
360         data = (NodeData *) ptr;
362         if (data->tree->blocked) return;
364         label = g_strdup_printf ("<?%s %s?>", repr->name(), new_content);
365         gtk_ctree_node_set_text (GTK_CTREE (data->tree), data->node, 0, label);
366         g_free (label);
368 void
369 tree_move (GtkCTree * tree, GtkCTreeNode * node, GtkCTreeNode * new_parent, GtkCTreeNode * new_sibling)
371         GtkCTreeNode * old_parent;
372         Inkscape::XML::Node * ref;
374         old_parent = GTK_CTREE_ROW (node)->parent;
375         if ( !old_parent || !new_parent ) return;
377         ref = sibling_to_ref (new_parent, new_sibling);
379         gtk_clist_freeze (GTK_CLIST (tree));
381         SP_XMLVIEW_TREE (tree)->blocked++;
382         if (new_parent == old_parent) {
383                 NODE_DATA (old_parent)->repr->changeOrder(NODE_DATA (node)->repr, ref);
384         } else {
385                 NODE_DATA (old_parent)->repr->removeChild(NODE_DATA (node)->repr);
386                 NODE_DATA (new_parent)->repr->addChild(NODE_DATA (node)->repr, ref);
387         }
388         SP_XMLVIEW_TREE (tree)->blocked--;
390         parent_class->tree_move (tree, node, new_parent, new_sibling);
392         gtk_clist_thaw (GTK_CLIST (tree));
395 GtkCTreeNode *
396 ref_to_sibling (GtkCTreeNode * parent, Inkscape::XML::Node * ref)
398         if (ref) {
399                 GtkCTreeNode * before;
400                 before = repr_to_child (parent, ref);
401                 g_assert (before != NULL);
402                 before = GTK_CTREE_ROW (before)->sibling;
403                 return before;
404         } else {
405                 return GTK_CTREE_ROW (parent)->children;
406         }
409 GtkCTreeNode *
410 repr_to_child (GtkCTreeNode * parent, Inkscape::XML::Node * repr)
412         GtkCTreeNode * child;
413         child = GTK_CTREE_ROW (parent)->children;
414         while ( child && NODE_DATA (child)->repr != repr ) {
415                 child = GTK_CTREE_ROW (child)->sibling;
416         }
417         return child;
420 Inkscape::XML::Node *
421 sibling_to_ref (GtkCTreeNode * parent, GtkCTreeNode * sibling)
423         GtkCTreeNode * child;
424         child = GTK_CTREE_ROW (parent)->children;
425         if ( child == sibling ) return NULL;
426         while ( child && GTK_CTREE_ROW (child)->sibling != sibling ) {
427                 child = GTK_CTREE_ROW (child)->sibling;
428         }
429         return NODE_DATA (child)->repr;
432 gboolean
433 check_drag (GtkCTree * /*tree*/, GtkCTreeNode * node, GtkCTreeNode * new_parent, GtkCTreeNode * /*new_sibling*/)
435         GtkCTreeNode * old_parent;
437         old_parent = GTK_CTREE_ROW (node)->parent;
439         if (!old_parent || !new_parent) return FALSE;
440         if (NODE_DATA (new_parent)->repr->type() != Inkscape::XML::ELEMENT_NODE) return FALSE;
442         /* fixme: we need add_child/remove_child/etc repr events without side-effects, so we can check here and give better visual feedback */
444         return TRUE;
447 Inkscape::XML::Node *
448 sp_xmlview_tree_node_get_repr (SPXMLViewTree * /*tree*/, GtkCTreeNode * node)
450         return NODE_DATA (node)->repr;
453 GtkCTreeNode *
454 sp_xmlview_tree_get_repr_node (SPXMLViewTree * tree, Inkscape::XML::Node * repr)
456         return gtk_ctree_find_by_row_data_custom (GTK_CTREE (tree), NULL, repr, match_node_data_by_repr);
459 gint
460 match_node_data_by_repr(gconstpointer data_p, gconstpointer repr)
462         return ((const NodeData *)data_p)->repr != (const Inkscape::XML::Node *)repr;