Code

06103e9b32958be78fde5c6afb5bd31300fa7dbf
[inkscape.git] / src / dialogs / xml-tree.cpp
1 #define __SP_XMLVIEW_TREE_C__
3 /**
4  * \brief  XML View
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   MenTaLguY <mental@rydia.net>
9  *   bulia byak <buliabyak@users.sf.net>
10  *   Johan Engelen <goejendaagh@zonnet.nl>
11  *
12  * Copyright (C) 1999-2006 Authors
13  * Copyright (C) 2004 David Turner
14  *
15  * Released under GNU GPL, read the file 'COPYING' for more information
16  */
18 #ifdef HAVE_CONFIG_H
19 # include "config.h"
20 #endif
22 #include <gtk/gtkmain.h>
23 #include <gtk/gtkhbox.h>
24 #include <gtk/gtkvbox.h>
25 #include <gtk/gtkhpaned.h>
26 #include <gtk/gtkvpaned.h>
27 #include <gtk/gtkhseparator.h>
28 #include <gtk/gtkhbbox.h>
29 #include <gtk/gtktoolbar.h>
30 #include <gtk/gtkscrolledwindow.h>
31 #include <gtk/gtkentry.h>
32 #include <gtk/gtkarrow.h>
34 #include <glibmm/i18n.h>
35 #include "helper/window.h"
36 #include "macros.h"
37 #include "../inkscape.h"
38 #include "../document.h"
39 #include "../desktop-handles.h"
40 #include "desktop.h"
41 #include "../selection.h"
42 #include "../sp-string.h"
43 #include "../sp-tspan.h"
44 #include "../sp-root.h"
45 #include "../event-context.h"
46 #include "in-dt-coordsys.h"
49 #include "../widgets/sp-xmlview-tree.h"
50 #include "../widgets/sp-xmlview-content.h"
51 #include "../widgets/sp-xmlview-attr-list.h"
53 #include "../inkscape-stock.h"
54 #include "widgets/icon.h"
56 #include "dialog-events.h"
57 #include "../preferences.h"
58 #include "../verbs.h"
59 #include "../interface.h"
61 #include "shortcuts.h"
62 #include <gdk/gdkkeysyms.h>
64 #include "message-stack.h"
65 #include "message-context.h"
67 #define MIN_ONSCREEN_DISTANCE 50
69 struct EditableDest {
70     GtkEditable *editable;
71     gchar *text;
72 };
74 static GtkWidget *dlg = NULL;
75 static sigc::connection sel_changed_connection;
76 static sigc::connection document_uri_set_connection;
77 static sigc::connection document_replaced_connection;
78 static win_data wd;
79 // impossible original values to make sure they are read from prefs
80 static gint x = -1000, y = -1000, w = 0, h = 0;
81 static Glib::ustring const prefs_path = "/dialogs/xml/";
82 static GtkWidget *status = NULL;
83 static Inkscape::MessageStack *_message_stack = NULL;
84 static Inkscape::MessageContext *_message_context = NULL;
85 static sigc::connection _message_changed_connection;
87 static GtkTooltips *tooltips = NULL;
88 static GtkEditable *attr_name = NULL;
89 static GtkTextView *attr_value = NULL;
90 static SPXMLViewTree *tree = NULL;
91 static SPXMLViewAttrList *attributes = NULL;
92 static SPXMLViewContent *content = NULL;
94 static gint blocked = 0;
95 static SPDesktop *current_desktop = NULL;
96 static SPDocument *current_document = NULL;
97 static gint selected_attr = 0;
98 static Inkscape::XML::Node *selected_repr = NULL;
100 static void sp_xmltree_desktop_change( Inkscape::Application *inkscape,  SPDesktop *desktop, GtkWidget *dialog );
102 static void set_tree_desktop(SPDesktop *desktop);
103 static void set_tree_document(SPDocument *document);
104 static void set_tree_repr(Inkscape::XML::Node *repr);
106 static void set_tree_select(Inkscape::XML::Node *repr);
107 static void propagate_tree_select(Inkscape::XML::Node *repr);
109 static Inkscape::XML::Node *get_dt_select();
110 static void set_dt_select(Inkscape::XML::Node *repr);
112 static void on_tree_select_row(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
113 static void on_tree_unselect_row(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
114 static void after_tree_move(GtkCTree *tree, GtkCTreeNode *node, GtkCTreeNode *new_parent, GtkCTreeNode *new_sibling, gpointer data);
115 static void on_destroy(GtkObject *object, gpointer data);
116 static gboolean on_delete(GtkObject *object, GdkEvent *event, gpointer data);
118 static void on_tree_select_row_enable_if_element(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
119 static void on_tree_select_row_enable_if_mutable(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
120 static void on_tree_select_row_show_if_element(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
121 static void on_tree_select_row_show_if_text(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
122 static void on_tree_select_row_enable_if_indentable(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
123 static void on_tree_select_row_enable_if_not_first_child(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
124 static void on_tree_select_row_enable_if_not_last_child(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
125 static void on_tree_select_row_enable_if_has_grandparent(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
127 static void on_tree_unselect_row_clear_text(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
128 static void on_tree_unselect_row_disable(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
129 static void on_tree_unselect_row_hide(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
131 static void on_attr_select_row(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
132 static void on_attr_unselect_row(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
133 static void on_attr_row_changed( GtkCList *list, gint row, gpointer data );
135 static void on_attr_select_row_enable(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
136 static void on_attr_unselect_row_disable(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
138 static void on_attr_select_row_set_name_content(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
139 static void on_attr_select_row_set_value_content(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
140 static void on_attr_unselect_row_clear_text(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
142 static void on_editable_changed_enable_if_valid_xml_name(GtkEditable *editable, gpointer data);
144 static void on_desktop_selection_changed(Inkscape::Selection *selection);
145 static void on_document_replaced(SPDesktop *dt, SPDocument *document);
146 static void on_document_uri_set(gchar const *uri, SPDocument *document);
148 static void on_clicked_get_editable_text(GtkWidget *widget, gpointer data);
150 static void _set_status_message(Inkscape::MessageType type, const gchar *message, GtkWidget *dialog);
152 static void cmd_new_element_node(GtkObject *object, gpointer data);
153 static void cmd_new_text_node(GtkObject *object, gpointer data);
154 static void cmd_duplicate_node(GtkObject *object, gpointer data);
155 static void cmd_delete_node(GtkObject *object, gpointer data);
157 static void cmd_raise_node(GtkObject *object, gpointer data);
158 static void cmd_lower_node(GtkObject *object, gpointer data);
159 static void cmd_indent_node(GtkObject *object, gpointer data);
160 static void cmd_unindent_node(GtkObject *object, gpointer data);
162 static void cmd_delete_attr(GtkObject *object, gpointer data);
163 static void cmd_set_attr(GtkObject *object, gpointer data);
165 static gboolean sp_xml_tree_key_press(GtkWidget *widget, GdkEventKey *event);
168 /*
169  * \brief Sets the XML status bar when the tree is selected.
170  */
171 void tree_reset_context()
173     _message_context->set(Inkscape::NORMAL_MESSAGE,
174                           _("<b>Click</b> to select nodes, <b>drag</b> to rearrange."));
178 /*
179  * \brief Sets the XML status bar, depending on which attr is selected.
180  */
181 void attr_reset_context(gint attr)
183     if (attr == 0) {
184         _message_context->set(Inkscape::NORMAL_MESSAGE,
185                               _("<b>Click</b> attribute to edit."));
186     }
187     else {
188         const gchar *name = g_quark_to_string(attr);
189         gchar *message = g_strdup_printf(_("Attribute <b>%s</b> selected. Press <b>Ctrl+Enter</b> when done editing to commit changes."), name);
190         _message_context->set(Inkscape::NORMAL_MESSAGE, message);
191         g_free(message);
192     }
196 void sp_xml_tree_dialog()
198     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
200     if (!desktop) {
201         return;
202     }
204     if (dlg == NULL)
205     { // very long block
207         GtkWidget *box, *sw, *paned, *toolbar, *button;
208         GtkWidget *text_container, *attr_container, *attr_subpaned_container, *box2;
209         GtkWidget *set_attr;
211         tooltips = gtk_tooltips_new();
212         gtk_tooltips_enable(tooltips);
214         dlg = sp_window_new("", TRUE);
215         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
216         if (x == -1000 || y == -1000) {
217             x = prefs->getInt(prefs_path + "x", -1000);
218             y = prefs->getInt(prefs_path + "y", -1000);
219         }
220         if (w ==0 || h == 0) {
221             w = prefs->getInt(prefs_path + "w", 0);
222             h = prefs->getInt(prefs_path + "h", 0);
223         }
225 //        if (x<0) x=0;
226 //        if (y<0) y=0;
228         if (w && h) {
229             gtk_window_resize((GtkWindow *) dlg, w, h);
230         }
231         if (x >= 0 && y >= 0 && (x < (gdk_screen_width()-MIN_ONSCREEN_DISTANCE)) && (y < (gdk_screen_height()-MIN_ONSCREEN_DISTANCE))) {
232             gtk_window_move((GtkWindow *) dlg, x, y);
233         } else {
234             gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
235         }
237         sp_transientize(dlg);
238         wd.win = dlg;
239         wd.stop = 0;
240         g_signal_connect  ( G_OBJECT(INKSCAPE), "activate_desktop", G_CALLBACK(sp_transientize_callback), &wd );
242         gtk_signal_connect( GTK_OBJECT(dlg), "event", GTK_SIGNAL_FUNC(sp_dialog_event_handler), dlg );
244         gtk_signal_connect( GTK_OBJECT(dlg), "destroy", G_CALLBACK(on_destroy), dlg);
245         gtk_signal_connect( GTK_OBJECT(dlg), "delete_event", G_CALLBACK(on_delete), dlg);
246         g_signal_connect  ( G_OBJECT(INKSCAPE), "shut_down", G_CALLBACK(on_delete), dlg);
248         g_signal_connect  ( G_OBJECT(INKSCAPE), "dialogs_hide", G_CALLBACK(sp_dialog_hide), dlg);
249         g_signal_connect  ( G_OBJECT(INKSCAPE), "dialogs_unhide", G_CALLBACK(sp_dialog_unhide), dlg);
252         gtk_container_set_border_width(GTK_CONTAINER(dlg), 0);
253         gtk_window_set_default_size(GTK_WINDOW(dlg), 640, 384);
255         GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
256         gtk_container_add(GTK_CONTAINER(dlg), vbox);
258         GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
259         gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
261         status = gtk_label_new(NULL);
262         gtk_misc_set_alignment(GTK_MISC(status), 0.0, 0.5);
263         gtk_widget_set_size_request(status, 1, -1);
264         gtk_label_set_markup(GTK_LABEL(status), "");
265         gtk_box_pack_start(GTK_BOX(hbox), gtk_hbox_new(FALSE, 0), FALSE, FALSE, 4);
266         gtk_box_pack_start(GTK_BOX(hbox), status, TRUE, TRUE, 0);
268         paned = gtk_hpaned_new();
269         gtk_paned_set_position(GTK_PANED(paned), 256);
270         gtk_box_pack_start(GTK_BOX(vbox), paned, TRUE, TRUE, 0);
272         _message_stack = new Inkscape::MessageStack();
273         _message_context = new Inkscape::MessageContext(_message_stack);
274         _message_changed_connection = _message_stack->connectChanged(
275             sigc::bind(sigc::ptr_fun(_set_status_message), dlg)
276         );
278         /* tree view */
280         box = gtk_vbox_new(FALSE, 0);
281         gtk_paned_pack1(GTK_PANED(paned), box, FALSE, FALSE);
283         tree = SP_XMLVIEW_TREE(sp_xmlview_tree_new(NULL, NULL, NULL));
284         gtk_tooltips_set_tip( tooltips, GTK_WIDGET(tree),
285                                _("Drag to reorder nodes"), NULL );
287         g_signal_connect( G_OBJECT(tree), "tree_select_row",
288                            G_CALLBACK(on_tree_select_row), NULL );
290         g_signal_connect( G_OBJECT(tree), "tree_unselect_row",
291                            G_CALLBACK(on_tree_unselect_row), NULL );
293         g_signal_connect_after( G_OBJECT(tree), "tree_move",
294                                  G_CALLBACK(after_tree_move), NULL);
296         /* TODO: replace gtk_signal_connect_while_alive() with something
297          * else...
298          */
299         toolbar = gtk_toolbar_new();
300         gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
301         gtk_container_set_border_width(GTK_CONTAINER(toolbar), 0);
303         button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
304                 NULL,
305                 _("New element node"),
306                 NULL,
307                 sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
308                                     INKSCAPE_STOCK_ADD_XML_ELEMENT_NODE ),
309                 G_CALLBACK(cmd_new_element_node),
310                 NULL);
312         gtk_signal_connect_while_alive( GTK_OBJECT(tree),
313                         "tree_select_row",
314                         G_CALLBACK(on_tree_select_row_enable_if_element),
315                         button,
316                         GTK_OBJECT(button));
318         gtk_signal_connect_while_alive(GTK_OBJECT(tree),
319                         "tree_unselect_row",
320                         G_CALLBACK(on_tree_unselect_row_disable),
321                         button,
322                         GTK_OBJECT(button));
324         gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
326         button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
327                 NULL, _("New text node"), NULL,
328                 sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
329                              INKSCAPE_STOCK_ADD_XML_TEXT_NODE ),
330                 G_CALLBACK(cmd_new_text_node),
331                 NULL);
333         gtk_signal_connect_while_alive(GTK_OBJECT(tree),
334                         "tree_select_row",
335                         G_CALLBACK(on_tree_select_row_enable_if_element),
336                         button,
337                         GTK_OBJECT(button));
339         gtk_signal_connect_while_alive(GTK_OBJECT(tree),
340                         "tree_unselect_row",
341                         G_CALLBACK(on_tree_unselect_row_disable),
342                         button,
343                         GTK_OBJECT(button));
345         gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
347         button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
348                 NULL, _("Duplicate node"), NULL,
349                 sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
350                              INKSCAPE_STOCK_DUPLICATE_XML_NODE ),
351                 G_CALLBACK(cmd_duplicate_node),
352                 NULL);
354         gtk_signal_connect_while_alive(GTK_OBJECT(tree),
355                         "tree_select_row",
356                         G_CALLBACK(on_tree_select_row_enable_if_mutable),
357                         button,
358                         GTK_OBJECT(button));
360         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
361                         G_CALLBACK(on_tree_unselect_row_disable),
362                         button, GTK_OBJECT(button));
364         gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
366         gtk_toolbar_append_space(GTK_TOOLBAR(toolbar));
368         button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
369                 NULL, _("Delete node"), NULL,
370                 sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
371                              INKSCAPE_STOCK_DELETE_XML_NODE ),
372                                            G_CALLBACK(cmd_delete_node), NULL );
374         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
375                         G_CALLBACK(on_tree_select_row_enable_if_mutable),
376                         button, GTK_OBJECT(button));
377         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
378                         G_CALLBACK(on_tree_unselect_row_disable),
379                         button, GTK_OBJECT(button));
380         gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
382         gtk_toolbar_append_space(GTK_TOOLBAR(toolbar));
384         button = gtk_toolbar_append_item( GTK_TOOLBAR(toolbar), "<",
385                         _("Unindent node"), NULL,
386                         gtk_arrow_new(GTK_ARROW_LEFT, GTK_SHADOW_IN),
387                         G_CALLBACK(cmd_unindent_node), NULL);
389         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
390                     G_CALLBACK(on_tree_select_row_enable_if_has_grandparent),
391                     button, GTK_OBJECT(button));
393         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
394                         G_CALLBACK(on_tree_unselect_row_disable),
395                         button, GTK_OBJECT(button));
397         gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
399         button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), ">",
400                         _("Indent node"), NULL,
401                         gtk_arrow_new(GTK_ARROW_RIGHT, GTK_SHADOW_IN),
402                         G_CALLBACK(cmd_indent_node), NULL);
403         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
404                         G_CALLBACK(on_tree_select_row_enable_if_indentable),
405                         button, GTK_OBJECT(button));
406         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
407                        (GCallback) on_tree_unselect_row_disable,
408                         button, GTK_OBJECT(button));
409         gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
411         button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), "^",
412                         _("Raise node"), NULL,
413                         gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_IN),
414                         G_CALLBACK(cmd_raise_node), NULL);
415         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
416                     G_CALLBACK(on_tree_select_row_enable_if_not_first_child),
417                     button, GTK_OBJECT(button));
418         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
419                         G_CALLBACK(on_tree_unselect_row_disable),
420                         button, GTK_OBJECT(button));
421         gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
423         button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), "v",
424                         _("Lower node"), NULL,
425                         gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_IN),
426                         G_CALLBACK(cmd_lower_node), NULL);
427         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
428                         G_CALLBACK(on_tree_select_row_enable_if_not_last_child),
429                         button, GTK_OBJECT(button));
430         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
431                         G_CALLBACK(on_tree_unselect_row_disable),
432                         button, GTK_OBJECT(button));
433         gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
435         gtk_box_pack_start(GTK_BOX(box), toolbar, FALSE, TRUE, 0);
437         sw = gtk_scrolled_window_new(NULL, NULL);
438         gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(sw),
439                                          GTK_POLICY_AUTOMATIC,
440                                          GTK_POLICY_AUTOMATIC );
441         gtk_box_pack_start(GTK_BOX(box), sw, TRUE, TRUE, 0);
443         gtk_container_add(GTK_CONTAINER(sw), GTK_WIDGET(tree));
445         /* node view */
447         box = gtk_vbox_new(FALSE, 0);
448         gtk_paned_pack2(GTK_PANED(paned), box, TRUE, TRUE);
450         /* attributes */
452         attr_container = gtk_vbox_new(FALSE, 0);
453         gtk_box_pack_start( GTK_BOX(box), GTK_WIDGET(attr_container),
454                              TRUE, TRUE, 0 );
456         attributes = SP_XMLVIEW_ATTR_LIST(sp_xmlview_attr_list_new(NULL));
457         g_signal_connect( G_OBJECT(attributes), "select_row",
458                            G_CALLBACK(on_attr_select_row), NULL);
459         g_signal_connect( G_OBJECT(attributes), "unselect_row",
460                            G_CALLBACK(on_attr_unselect_row), NULL);
461         g_signal_connect( G_OBJECT(attributes), "row-value-changed",
462                            G_CALLBACK(on_attr_row_changed), NULL);
464         toolbar = gtk_toolbar_new();
465         gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
466         gtk_container_set_border_width(GTK_CONTAINER(toolbar), 0);
468         button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
469                 NULL, _("Delete attribute"), NULL,
470                 sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
471                              INKSCAPE_STOCK_DELETE_XML_ATTRIBUTE ),
472                (GCallback) cmd_delete_attr, NULL);
474         gtk_signal_connect_while_alive(GTK_OBJECT(attributes), "select_row",
475                        (GCallback) on_attr_select_row_enable, button,
476                         GTK_OBJECT(button));
478         gtk_signal_connect_while_alive(GTK_OBJECT(attributes), "unselect_row",
479                        (GCallback) on_attr_unselect_row_disable, button,
480                         GTK_OBJECT(button));
482         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
483                        (GCallback) on_tree_unselect_row_disable, button,
484                         GTK_OBJECT(button));
486         gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
488         gtk_box_pack_start( GTK_BOX(attr_container),
489                              GTK_WIDGET(toolbar), FALSE, TRUE, 0 );
491         attr_subpaned_container = gtk_vpaned_new();
492         gtk_box_pack_start( GTK_BOX(attr_container),
493                              GTK_WIDGET(attr_subpaned_container),
494                              TRUE, TRUE, 0 );
495         gtk_widget_show(attr_subpaned_container);
497         sw = gtk_scrolled_window_new(NULL, NULL);
498         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
499                 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
500         gtk_paned_pack1( GTK_PANED(attr_subpaned_container),
501                           GTK_WIDGET(sw), TRUE, TRUE );
502         gtk_container_add(GTK_CONTAINER(sw), GTK_WIDGET(attributes));
504         toolbar = gtk_vbox_new(FALSE, 4);
505         gtk_container_set_border_width(GTK_CONTAINER(toolbar), 4);
507         box2 = gtk_hbox_new(FALSE, 4);
508         gtk_box_pack_start( GTK_BOX(toolbar), GTK_WIDGET(box2),
509                              FALSE, TRUE, 0);
511         attr_name = GTK_EDITABLE(gtk_entry_new());
512         gtk_tooltips_set_tip( tooltips, GTK_WIDGET(attr_name),
513                                // TRANSLATORS: "Attribute" is a noun here
514                                _("Attribute name"), NULL );
516         gtk_signal_connect( GTK_OBJECT(attributes), "select_row",
517                             (GCallback) on_attr_select_row_set_name_content,
518                              attr_name);
520         gtk_signal_connect( GTK_OBJECT(attributes), "unselect_row",
521                             (GCallback) on_attr_unselect_row_clear_text,
522                              attr_name);
524         gtk_signal_connect( GTK_OBJECT(tree), "tree_unselect_row",
525                             (GCallback) on_tree_unselect_row_clear_text,
526                              attr_name);
528         gtk_box_pack_start( GTK_BOX(box2), GTK_WIDGET(attr_name),
529                              TRUE, TRUE, 0);
531         set_attr = gtk_button_new();
532         gtk_tooltips_set_tip( tooltips, GTK_WIDGET(set_attr),
533                                // TRANSLATORS: "Set" is a verb here
534                                _("Set attribute"), NULL );
535         // TRANSLATORS: "Set" is a verb here
536         GtkWidget *set_label = gtk_label_new(_("Set"));
537         gtk_container_add(GTK_CONTAINER(set_attr), set_label);
539         gtk_signal_connect( GTK_OBJECT(set_attr), "clicked",
540                             (GCallback) cmd_set_attr, NULL);
541         gtk_signal_connect( GTK_OBJECT(attr_name), "changed",
542                    (GCallback) on_editable_changed_enable_if_valid_xml_name,
543                     set_attr );
544         gtk_widget_set_sensitive(GTK_WIDGET(set_attr), FALSE);
546         gtk_box_pack_start(GTK_BOX(box2), set_attr, FALSE, FALSE, 0);
548         sw = gtk_scrolled_window_new(NULL, NULL);
549         gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(sw),
550                                          GTK_POLICY_AUTOMATIC,
551                                          GTK_POLICY_AUTOMATIC );
552         gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN );
553         gtk_box_pack_start(GTK_BOX(toolbar), sw, TRUE, TRUE, 0);
555         attr_value =(GtkTextView *) gtk_text_view_new();
556         gtk_text_view_set_wrap_mode((GtkTextView *) attr_value, GTK_WRAP_CHAR);
557         gtk_tooltips_set_tip( tooltips, GTK_WIDGET(attr_value),
558                                // TRANSLATORS: "Attribute" is a noun here
559                                _("Attribute value"), NULL );
560         gtk_signal_connect( GTK_OBJECT(attributes), "select_row",
561                             (GCallback) on_attr_select_row_set_value_content,
562                              attr_value );
563         gtk_signal_connect( GTK_OBJECT(attributes), "unselect_row",
564                             (GCallback) on_attr_unselect_row_clear_text,
565                              attr_value );
566         gtk_signal_connect( GTK_OBJECT(tree), "tree_unselect_row",
567                             (GCallback) on_tree_unselect_row_clear_text,
568                              attr_value );
569         gtk_text_view_set_editable(attr_value, TRUE);
570         gtk_container_add( GTK_CONTAINER(sw),
571                             GTK_WIDGET(attr_value) );
573         gtk_paned_pack2( GTK_PANED(attr_subpaned_container),
574                           GTK_WIDGET(toolbar), FALSE, TRUE );
576         /* text */
578         sw = gtk_scrolled_window_new(NULL, NULL);
579         gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(sw),
580                                          GTK_POLICY_AUTOMATIC,
581                                          GTK_POLICY_AUTOMATIC );
582         gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN );
583         gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(sw), TRUE, TRUE, 0);
585         content = SP_XMLVIEW_CONTENT(sp_xmlview_content_new(NULL));
586         gtk_container_add(GTK_CONTAINER(sw), GTK_WIDGET(content));
588         text_container = sw;
590         /* initial show/hide */
592         gtk_widget_show_all(GTK_WIDGET(dlg));
594         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
595                        (GCallback) on_tree_select_row_show_if_element,
596                         attr_container, GTK_OBJECT(attr_container));
598         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
599                        (GCallback) on_tree_unselect_row_hide,
600                         attr_container, GTK_OBJECT(attr_container));
602         gtk_widget_hide(attr_container);
604         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
605                        (GCallback) on_tree_select_row_show_if_text,
606                         text_container, GTK_OBJECT(text_container));
608         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
609                        (GCallback) on_tree_unselect_row_hide,
610                         text_container, GTK_OBJECT(text_container));
612         gtk_widget_hide(text_container);
614         g_signal_connect( G_OBJECT(INKSCAPE), "activate_desktop",
615                            G_CALLBACK(sp_xmltree_desktop_change), dlg);
617         g_signal_connect( G_OBJECT(INKSCAPE), "deactivate_desktop",
618                            G_CALLBACK(sp_xmltree_desktop_change), dlg);
620         g_signal_connect((GObject *) dlg, "key_press_event", (GCallback) sp_xml_tree_key_press, NULL);
622         tree_reset_context();
623     } // end of if (dlg == NULL)
625     gtk_window_present((GtkWindow *) dlg);
627     g_assert(desktop != NULL);
628     set_tree_desktop(desktop);
630 } // end of sp_xml_tree_dialog()
632 static gboolean sp_xml_tree_key_press(GtkWidget */*widget*/, GdkEventKey *event)
635     unsigned int shortcut = get_group0_keyval(event) |
636         ( event->state & GDK_SHIFT_MASK ?
637           SP_SHORTCUT_SHIFT_MASK : 0 ) |
638         ( event->state & GDK_CONTROL_MASK ?
639           SP_SHORTCUT_CONTROL_MASK : 0 ) |
640         ( event->state & GDK_MOD1_MASK ?
641           SP_SHORTCUT_ALT_MASK : 0 );
643     /* fixme: if you need to add more xml-tree-specific callbacks, you should probably upgrade
644      * the sp_shortcut mechanism to take into account windows. */
645     if (shortcut == (SP_SHORTCUT_CONTROL_MASK | GDK_Return)) {
646         cmd_set_attr(NULL, NULL);
647         return true;
648     }
649     return false;
653 static void sp_xmltree_desktop_change(Inkscape::Application */*inkscape*/,
654                                       SPDesktop *desktop,
655                                       GtkWidget */*dialog*/ )
657     if (!desktop) {
658         return;
659     }
660     set_tree_desktop(desktop);
664 void set_tree_desktop(SPDesktop *desktop)
666     if ( desktop == current_desktop ) {
667         return;
668     }
670     if (current_desktop) {
671         sel_changed_connection.disconnect();
672         document_replaced_connection.disconnect();
673     }
674     current_desktop = desktop;
675     if (desktop) {
676         sel_changed_connection = sp_desktop_selection(desktop)->connectChanged(&on_desktop_selection_changed);
677         document_replaced_connection = desktop->connectDocumentReplaced(&on_document_replaced);
678         set_tree_document(sp_desktop_document(desktop));
679     } else {
680         set_tree_document(NULL);
681     }
683 } // end of set_tree_desktop()
687 void set_tree_document(SPDocument *document)
689     if (document == current_document) {
690         return;
691     }
693     if (current_document) {
694         document_uri_set_connection.disconnect();
695     }
696     current_document = document;
697     if (current_document) {
699         document_uri_set_connection = current_document->connectURISet(sigc::bind(sigc::ptr_fun(&on_document_uri_set), current_document));
700         on_document_uri_set(SP_DOCUMENT_URI(current_document), current_document);
701         set_tree_repr(sp_document_repr_root(current_document));
703     } else {
704         set_tree_repr(NULL);
705     }
710 void set_tree_repr(Inkscape::XML::Node *repr)
712     if (repr == selected_repr) {
713         return;
714     }
716     gtk_clist_freeze(GTK_CLIST(tree));
718     sp_xmlview_tree_set_repr(tree, repr);
720     if (repr) {
721         set_tree_select(get_dt_select());
722     } else {
723         set_tree_select(NULL);
724     }
726     gtk_clist_thaw(GTK_CLIST(tree));
728     propagate_tree_select(selected_repr);
734 void set_tree_select(Inkscape::XML::Node *repr)
736     if (selected_repr) {
737         Inkscape::GC::release(selected_repr);
738     }
740     selected_repr = repr;
741     if (repr) {
742         GtkCTreeNode *node;
744         Inkscape::GC::anchor(selected_repr);
746         node = sp_xmlview_tree_get_repr_node(SP_XMLVIEW_TREE(tree), repr);
747         if (node) {
748             GtkCTreeNode *parent;
750             gtk_ctree_select(GTK_CTREE(tree), node);
752             parent = GTK_CTREE_ROW(node)->parent;
753             while (parent) {
754                 gtk_ctree_expand(GTK_CTREE(tree), parent);
755                 parent = GTK_CTREE_ROW(parent)->parent;
756             }
758             gtk_ctree_node_moveto(GTK_CTREE(tree), node, 0, 0.66, 0.0);
759         }
760     } else {
761         gtk_clist_unselect_all(GTK_CLIST(tree));
762     }
763     propagate_tree_select(repr);
768 void propagate_tree_select(Inkscape::XML::Node *repr)
770     if (repr && repr->type() == Inkscape::XML::ELEMENT_NODE) {
771         sp_xmlview_attr_list_set_repr(attributes, repr);
772     } else {
773         sp_xmlview_attr_list_set_repr(attributes, NULL);
774     }
776     if (repr && ( repr->type() == Inkscape::XML::TEXT_NODE || repr->type() == Inkscape::XML::COMMENT_NODE || repr->type() == Inkscape::XML::PI_NODE ) ) {
777         sp_xmlview_content_set_repr(content, repr);
778     } else {
779         sp_xmlview_content_set_repr(content, NULL);
780     }
784 Inkscape::XML::Node *get_dt_select()
786     if (!current_desktop) {
787         return NULL;
788     }
790     return sp_desktop_selection(current_desktop)->singleRepr();
795 void set_dt_select(Inkscape::XML::Node *repr)
797     if (!current_desktop) {
798         return;
799     }
801     Inkscape::Selection *selection = sp_desktop_selection(current_desktop);
803     SPObject *object;
804     if (repr) {
805         while ( ( repr->type() != Inkscape::XML::ELEMENT_NODE )
806                 && sp_repr_parent(repr) )
807         {
808             repr = sp_repr_parent(repr);
809         } // end of while loop
811         object = sp_desktop_document(current_desktop)->getObjectByRepr(repr);
812     } else {
813         object = NULL;
814     }
816     blocked++;
817     if ( object && in_dt_coordsys(*object)
818          && !(SP_IS_STRING(object) ||
819                 SP_IS_ROOT(object)     ) )
820     {
821             /* We cannot set selection to root or string - they are not items and selection is not
822              * equipped to deal with them */
823             selection->set(SP_ITEM(object));
824     }
825     blocked--;
827 } // end of set_dt_select()
830 void on_tree_select_row(GtkCTree *tree,
831                         GtkCTreeNode *node,
832                         gint /*column*/,
833                         gpointer /*data*/)
835     if (blocked) {
836         return;
837     }
839     Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
840     g_assert(repr != NULL);
842     if (selected_repr == repr) {
843         return;
844     }
846     if (selected_repr) {
847         Inkscape::GC::release(selected_repr);
848         selected_repr = NULL;
849     }
850     selected_repr = repr;
851     Inkscape::GC::anchor(selected_repr);
853     propagate_tree_select(selected_repr);
855     set_dt_select(selected_repr);
857     tree_reset_context();
860 void on_tree_unselect_row(GtkCTree *tree,
861                           GtkCTreeNode *node,
862                           gint /*column*/,
863                           gpointer /*data*/)
865     if (blocked) {
866         return;
867     }
869     Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
870     propagate_tree_select(NULL);
871     set_dt_select(NULL);
873     if (selected_repr && (selected_repr == repr)) {
874         Inkscape::GC::release(selected_repr);
875         selected_repr = NULL;
876         selected_attr = 0;
877     }
882 void after_tree_move(GtkCTree */*tree*/,
883                      GtkCTreeNode *node,
884                      GtkCTreeNode *new_parent,
885                      GtkCTreeNode *new_sibling,
886                      gpointer /*data*/)
888     if (GTK_CTREE_ROW(node)->parent  == new_parent &&
889         GTK_CTREE_ROW(node)->sibling == new_sibling)
890     {
891         sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
892                          _("Drag XML subtree"));
893     } else {
894         sp_document_cancel(current_document);
895     }
899 static void on_destroy(GtkObject */*object*/, gpointer /*data*/)
901     set_tree_desktop(NULL);
902     gtk_object_destroy(GTK_OBJECT(tooltips));
903     tooltips = NULL;
904     sp_signal_disconnect_by_data(INKSCAPE, dlg);
905     wd.win = dlg = NULL;
906     wd.stop = 0;
908     _message_changed_connection.disconnect();
909     delete _message_context;
910     _message_context = NULL;
911     Inkscape::GC::release(_message_stack);
912     _message_stack = NULL;
913     _message_changed_connection.~connection();
915     status = NULL;
920 static gboolean on_delete(GtkObject */*object*/, GdkEvent */*event*/, gpointer /*data*/)
922     gtk_window_get_position((GtkWindow *) dlg, &x, &y);
923     gtk_window_get_size((GtkWindow *) dlg, &w, &h);
925     if (x<0) x=0;
926     if (y<0) y=0;
928     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
929     prefs->setInt(prefs_path + "x", x);
930     prefs->setInt(prefs_path + "y", y);
931     prefs->setInt(prefs_path + "w", w);
932     prefs->setInt(prefs_path + "h", h);
934     return FALSE; // which means, go ahead and destroy it
938 static void _set_status_message(Inkscape::MessageType /*type*/, const gchar *message, GtkWidget */*dialog*/)
940     if (status) {
941         gtk_label_set_markup(GTK_LABEL(status), message ? message : "");
942     }
946 void on_tree_select_row_enable(GtkCTree */*tree*/,
947                                GtkCTreeNode */*node*/,
948                                gint /*column*/,
949                                gpointer data)
951     gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
956 void on_tree_select_row_enable_if_element(GtkCTree *tree,
957                                           GtkCTreeNode *node,
958                                           gint /*column*/,
959                                           gpointer data )
961     Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
963     if (repr->type() == Inkscape::XML::ELEMENT_NODE) {
964         gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
965     } else {
966         gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
967     }
972 void on_tree_select_row_show_if_element(GtkCTree *tree, GtkCTreeNode *node,
973                                         gint /*column*/, gpointer data)
975     Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
977     if (repr->type() == Inkscape::XML::ELEMENT_NODE) {
978         gtk_widget_show(GTK_WIDGET(data));
979     } else {
980         gtk_widget_hide(GTK_WIDGET(data));
981     }
986 void on_tree_select_row_show_if_text(GtkCTree *tree, GtkCTreeNode *node,
987                                      gint /*column*/, gpointer data)
989     Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
991     if ( repr->type() == Inkscape::XML::TEXT_NODE || repr->type() == Inkscape::XML::COMMENT_NODE || repr->type() == Inkscape::XML::PI_NODE ) {
992         gtk_widget_show(GTK_WIDGET(data));
993     } else {
994         gtk_widget_hide(GTK_WIDGET(data));
995     }
999 gboolean xml_tree_node_mutable(GtkCTreeNode *node)
1001     // top-level is immutable, obviously
1002     if (!GTK_CTREE_ROW(node)->parent) {
1003         return false;
1004     }
1006     // if not in base level (where namedview, defs, etc go), we're mutable
1007     if (GTK_CTREE_ROW(GTK_CTREE_ROW(node)->parent)->parent) {
1008         return true;
1009     }
1011     Inkscape::XML::Node *repr;
1012     repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
1013     g_assert(repr);
1015     // don't let "defs" or "namedview" disappear
1016     if ( !strcmp(repr->name(),"svg:defs") ||
1017          !strcmp(repr->name(),"sodipodi:namedview") ) {
1018         return false;
1019     }
1021     // everyone else is okay, I guess.  :)
1022     return true;
1026 void on_tree_select_row_enable_if_mutable(GtkCTree */*tree*/, GtkCTreeNode *node,
1027                                           gint /*column*/, gpointer data)
1029     gtk_widget_set_sensitive(GTK_WIDGET(data), xml_tree_node_mutable(node));
1034 void on_tree_unselect_row_disable(GtkCTree */*tree*/, GtkCTreeNode */*node*/,
1035                                   gint /*column*/, gpointer data)
1037     gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1042 void on_tree_unselect_row_hide(GtkCTree */*tree*/, GtkCTreeNode */*node*/,
1043                                gint /*column*/, gpointer data)
1045     gtk_widget_hide(GTK_WIDGET(data));
1050 void on_tree_unselect_row_clear_text(GtkCTree */*tree*/, GtkCTreeNode */*node*/,
1051                                      gint /*column*/, gpointer data)
1053     if (GTK_IS_EDITABLE(data)) {
1054         gtk_editable_delete_text(GTK_EDITABLE(data), 0, -1);
1055     } else if (GTK_IS_TEXT_VIEW(data)) {
1056         GtkTextBuffer *tb;
1057         tb = gtk_text_view_get_buffer(GTK_TEXT_VIEW(data));
1058         gtk_text_buffer_set_text(tb, "", 0);
1059     }
1063 void on_attr_select_row(GtkCList *list, gint row, gint /*column*/,
1064                         GdkEventButton */*event*/, gpointer /*data*/)
1066     selected_attr = sp_xmlview_attr_list_get_row_key(list, row);
1067     gtk_window_set_focus(GTK_WINDOW(dlg), GTK_WIDGET(attr_value));
1069     attr_reset_context(selected_attr);
1073 void on_attr_unselect_row(GtkCList */*list*/, gint /*row*/, gint /*column*/,
1074                           GdkEventButton */*event*/, gpointer /*data*/)
1076     selected_attr = 0;
1077     attr_reset_context(selected_attr);
1081 void on_attr_row_changed(GtkCList *list, gint row, gpointer /*data*/)
1083     gint attr = sp_xmlview_attr_list_get_row_key(list, row);
1085     if (attr == selected_attr) {
1086         /* if the attr changed, reselect the row in the list to sync
1087            the edit box */
1089         /*
1090         // get current attr values
1091         const gchar * name = g_quark_to_string (sp_xmlview_attr_list_get_row_key (list, row));
1092         const gchar * value = selected_repr->attribute(name);
1094         g_warning("value: '%s'",value);
1096         // get the edit box value
1097         GtkTextIter start, end;
1098         gtk_text_buffer_get_bounds ( gtk_text_view_get_buffer (attr_value),
1099                                      &start, &end );
1100         gchar * text = gtk_text_buffer_get_text ( gtk_text_view_get_buffer (attr_value),
1101                                        &start, &end, TRUE );
1102         g_warning("text: '%s'",text);
1104         // compare to edit box
1105         if (strcmp(text,value)) {
1106             // issue warning if they're different
1107             _message_stack->flash(Inkscape::WARNING_MESSAGE,
1108                                   _("Attribute changed in GUI while editing values!"));
1109         }
1110         g_free (text);
1112         */
1113         gtk_clist_unselect_row( GTK_CLIST(list), row, 0 );
1114         gtk_clist_select_row( GTK_CLIST(list), row, 0 );
1115     }
1119 void on_attr_select_row_set_name_content(GtkCList *list, gint row,
1120                                          gint /*column*/, GdkEventButton */*event*/,
1121                                          gpointer data)
1123     GtkEditable *editable = GTK_EDITABLE(data);
1124     const gchar *name = g_quark_to_string(sp_xmlview_attr_list_get_row_key(list, row));
1125     gtk_editable_delete_text(editable, 0, -1);
1126     gint pos = 0;
1127     gtk_editable_insert_text(editable, name, strlen(name), &pos);
1132 void on_attr_select_row_set_value_content(GtkCList *list, gint row, gint /*column*/,
1133                                           GdkEventButton */*event*/,
1134                                           gpointer data)
1136     GtkTextBuffer *tb = gtk_text_view_get_buffer(GTK_TEXT_VIEW(data));
1137     const gchar *name = g_quark_to_string(sp_xmlview_attr_list_get_row_key(list, row));
1138     const gchar *value = selected_repr->attribute(name);
1139     if (!value) {
1140         value = "";
1141     }
1142     gtk_text_buffer_set_text(tb, value, strlen(value));
1146 void on_tree_select_row_enable_if_indentable(GtkCTree *tree, GtkCTreeNode *node,
1147                                              gint /*column*/, gpointer data)
1149     gboolean indentable = FALSE;
1151     if (xml_tree_node_mutable(node)) {
1152         Inkscape::XML::Node *repr, *prev;
1153         repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
1155         Inkscape::XML::Node *parent=repr->parent();
1156         if ( parent && repr != parent->firstChild() ) {
1157             g_assert(parent->firstChild());
1159             // skip to the child just before the current repr
1160             for ( prev = parent->firstChild() ;
1161                   prev && prev->next() != repr ;
1162                   prev = prev->next() );
1164             if (prev && prev->type() == Inkscape::XML::ELEMENT_NODE) {
1165                 indentable = TRUE;
1166             }
1167         }
1168     }
1170     gtk_widget_set_sensitive(GTK_WIDGET(data), indentable);
1175 void on_tree_select_row_enable_if_not_first_child(GtkCTree *tree,
1176                                                   GtkCTreeNode *node,
1177                                                   gint /*column*/,
1178                                                   gpointer data)
1180     Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
1182     Inkscape::XML::Node *parent=repr->parent();
1183     if ( parent && repr != parent->firstChild() ) {
1184         gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
1185     } else {
1186         gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1187     }
1192 void on_tree_select_row_enable_if_not_last_child(GtkCTree *tree,
1193                                                  GtkCTreeNode *node,
1194                                                  gint /*column*/, gpointer data)
1196     Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
1198     Inkscape::XML::Node *parent=repr->parent();
1199     if ( parent && parent->parent() && repr->next() ) {
1200         gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
1201     } else {
1202         gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1203     }
1208 void on_tree_select_row_enable_if_has_grandparent(GtkCTree */*tree*/,
1209                                                   GtkCTreeNode *node,
1210                                                   gint /*column*/, gpointer data)
1212     GtkCTreeNode *parent = GTK_CTREE_ROW(node)->parent;
1214     if (parent) {
1215         GtkCTreeNode *grandparent = GTK_CTREE_ROW(parent)->parent;
1216         if (grandparent) {
1217             gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
1218         } else {
1219             gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1220         }
1221     } else {
1222         gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1223     }
1228 void on_attr_select_row_enable(GtkCList */*list*/, gint /*row*/, gint /*column*/,
1229                                GdkEventButton */*event*/, gpointer data)
1231     gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
1236 void on_attr_unselect_row_disable(GtkCList */*list*/, gint /*row*/, gint /*column*/,
1237                                   GdkEventButton */*event*/, gpointer data)
1239     gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1244 void on_attr_unselect_row_clear_text(GtkCList */*list*/, gint /*row*/, gint /*column*/,
1245                                      GdkEventButton */*event*/, gpointer data)
1247     if (GTK_IS_EDITABLE(data)) {
1248         gtk_editable_delete_text(GTK_EDITABLE(data), 0, -1);
1249     } else if (GTK_IS_TEXT_VIEW(data)) {
1250         GtkTextBuffer *tb;
1251         tb = gtk_text_view_get_buffer(GTK_TEXT_VIEW(data));
1252         gtk_text_buffer_set_text(tb, "", 0);
1253     }
1258 void on_editable_changed_enable_if_valid_xml_name(GtkEditable *editable,
1259                                                   gpointer data)
1261     gchar *text = gtk_editable_get_chars(editable, 0, -1);
1263     /* TODO: need to do checking a little more rigorous than this */
1265     if (strlen(text)) {
1266         gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
1267     } else {
1268         gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1269     }
1270     g_free(text);
1275 void on_desktop_selection_changed(Inkscape::Selection */*selection*/)
1277     if (!blocked++) {
1278         set_tree_select(get_dt_select());
1279     }
1280     blocked--;
1283 static void on_document_replaced(SPDesktop *dt, SPDocument *doc)
1285     if (current_desktop)
1286         sel_changed_connection.disconnect();
1288     sel_changed_connection = sp_desktop_selection(dt)->connectChanged(&on_desktop_selection_changed);
1289     set_tree_document(doc);
1292 void on_document_uri_set(gchar const */*uri*/, SPDocument *document)
1294     gchar title[500];
1295     sp_ui_dialog_title_string(Inkscape::Verb::get(SP_VERB_DIALOG_XML_EDITOR), title);
1296     gchar *t = g_strdup_printf("%s: %s", SP_DOCUMENT_NAME(document), title);
1297     gtk_window_set_title(GTK_WINDOW(dlg), t);
1298     g_free(t);
1303 void on_clicked_get_editable_text(GtkWidget */*widget*/, gpointer data)
1305     EditableDest *dest = (EditableDest *) data;
1306     dest->text = gtk_editable_get_chars(dest->editable, 0, -1);
1309 gboolean
1310 quit_on_esc (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
1312     switch (get_group0_keyval (event)) {
1313         case GDK_Escape: // defocus
1314             gtk_widget_destroy(w);
1315             return TRUE;
1316     }
1317     return FALSE;
1320 void cmd_new_element_node(GtkObject */*object*/, gpointer /*data*/)
1322     EditableDest name;
1323     GtkWidget *window, *create, *cancel, *vbox, *entry, *bbox, *sep;
1325     g_assert(selected_repr != NULL);
1327     window = sp_window_new(NULL, TRUE);
1328     gtk_container_set_border_width(GTK_CONTAINER(window), 4);
1329     gtk_window_set_title(GTK_WINDOW(window), _("New element node..."));
1330     gtk_window_set_policy(GTK_WINDOW(window), FALSE, FALSE, TRUE);
1331     gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
1332     gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(dlg));
1333     gtk_window_set_modal(GTK_WINDOW(window), TRUE);
1334     gtk_signal_connect(GTK_OBJECT(window), "destroy", gtk_main_quit, NULL);
1335     gtk_signal_connect(GTK_OBJECT(window), "key-press-event", G_CALLBACK(quit_on_esc), window);
1337     vbox = gtk_vbox_new(FALSE, 4);
1338     gtk_container_add(GTK_CONTAINER(window), vbox);
1340     entry = gtk_entry_new();
1341     gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, TRUE, 0);
1343     sep = gtk_hseparator_new();
1344     gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, TRUE, 0);
1346     bbox = gtk_hbutton_box_new();
1347     gtk_container_set_border_width(GTK_CONTAINER(bbox), 4);
1348     gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
1349     gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, TRUE, 0);
1351     cancel = gtk_button_new_with_label(_("Cancel"));
1352     GTK_WIDGET_SET_FLAGS( GTK_WIDGET(cancel),
1353                            GTK_CAN_DEFAULT );
1354     gtk_signal_connect_object( GTK_OBJECT(cancel), "clicked",
1355                                 G_CALLBACK(gtk_widget_destroy),
1356                                 GTK_OBJECT(window) );
1357     gtk_container_add(GTK_CONTAINER(bbox), cancel);
1359     create = gtk_button_new_with_label(_("Create"));
1360     gtk_widget_set_sensitive(GTK_WIDGET(create), FALSE);
1361     gtk_signal_connect( GTK_OBJECT(entry), "changed",
1362                     G_CALLBACK(on_editable_changed_enable_if_valid_xml_name),
1363                     create );
1364     gtk_signal_connect( GTK_OBJECT(create), "clicked",
1365                          G_CALLBACK(on_clicked_get_editable_text), &name );
1366     gtk_signal_connect_object( GTK_OBJECT(create), "clicked",
1367                                 G_CALLBACK(gtk_widget_destroy),
1368                                 GTK_OBJECT(window) );
1369     GTK_WIDGET_SET_FLAGS( GTK_WIDGET(create),
1370                            GTK_CAN_DEFAULT | GTK_RECEIVES_DEFAULT );
1371     gtk_container_add(GTK_CONTAINER(bbox), create);
1373     gtk_widget_show_all(GTK_WIDGET(window));
1374     gtk_window_set_default(GTK_WINDOW(window), GTK_WIDGET(create));
1375     gtk_window_set_focus(GTK_WINDOW(window), GTK_WIDGET(entry));
1377     name.editable = GTK_EDITABLE(entry);
1378     name.text = NULL;
1380     gtk_main();
1382     if (selected_repr != NULL && name.text) {
1383         Inkscape::XML::Document *xml_doc = sp_document_repr_doc(current_document);
1384         Inkscape::XML::Node *new_repr;
1385         new_repr = xml_doc->createElement(name.text);
1386         Inkscape::GC::release(new_repr);
1387         g_free(name.text);
1388         selected_repr->appendChild(new_repr);
1389         set_tree_select(new_repr);
1390         set_dt_select(new_repr);
1392         sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1393                      _("Create new element node"));
1394     }
1396 } // end of cmd_new_element_node()
1400 void cmd_new_text_node(GtkObject */*object*/, gpointer /*data*/)
1402     g_assert(selected_repr != NULL);
1404     Inkscape::XML::Document *xml_doc = sp_document_repr_doc(current_document);
1405     Inkscape::XML::Node *text = xml_doc->createTextNode("");
1406     selected_repr->appendChild(text);
1408     sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1409                      _("Create new text node"));
1411     set_tree_select(text);
1412     set_dt_select(text);
1414     gtk_window_set_focus(GTK_WINDOW(dlg), GTK_WIDGET(content));
1418 void cmd_duplicate_node(GtkObject */*object*/, gpointer /*data*/)
1420     g_assert(selected_repr != NULL);
1422     Inkscape::XML::Node *parent = sp_repr_parent(selected_repr);
1423     Inkscape::XML::Node *dup = selected_repr->duplicate(parent->document());
1424     parent->addChild(dup, selected_repr);
1426     sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1427                      _("Duplicate node"));
1429     GtkCTreeNode *node = sp_xmlview_tree_get_repr_node(SP_XMLVIEW_TREE(tree), dup);
1431     if (node) {
1432         gtk_ctree_select(GTK_CTREE(tree), node);
1433     }
1438 void cmd_delete_node(GtkObject */*object*/, gpointer /*data*/)
1440     g_assert(selected_repr != NULL);
1441     sp_repr_unparent(selected_repr);
1443     sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1444                      _("Delete node"));
1449 void cmd_delete_attr(GtkObject */*object*/, gpointer /*data*/)
1451     g_assert(selected_repr != NULL);
1452     g_assert(selected_attr != 0);
1453     selected_repr->setAttribute(g_quark_to_string(selected_attr), NULL);
1455     SPObject *updated=current_document->getObjectByRepr(selected_repr);
1456     if (updated) {
1457         // force immediate update of dependant attributes
1458         updated->updateRepr();
1459     }
1461     sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1462                      _("Delete attribute"));
1467 void cmd_set_attr(GtkObject */*object*/, gpointer /*data*/)
1469     g_assert(selected_repr != NULL);
1471     gchar *name = gtk_editable_get_chars(attr_name, 0, -1);
1472     GtkTextIter start;
1473     GtkTextIter end;
1474     gtk_text_buffer_get_bounds( gtk_text_view_get_buffer(attr_value),
1475                                  &start, &end );
1476     gchar *value = gtk_text_buffer_get_text( gtk_text_view_get_buffer(attr_value),
1477                                        &start, &end, TRUE );
1479     selected_repr->setAttribute(name, value, false);
1481     g_free(name);
1482     g_free(value);
1484     SPObject *updated = current_document->getObjectByRepr(selected_repr);
1485     if (updated) {
1486         // force immediate update of dependant attributes
1487         updated->updateRepr();
1488     }
1490     sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1491                      _("Change attribute"));
1493     /* TODO: actually, the row won't have been created yet.  why? */
1494     gint row = sp_xmlview_attr_list_find_row_from_key(GTK_CLIST(attributes),
1495                                                       g_quark_from_string(name));
1496     if (row != -1) {
1497         gtk_clist_select_row(GTK_CLIST(attributes), row, 0);
1498     }
1503 void cmd_raise_node(GtkObject */*object*/, gpointer /*data*/)
1505     g_assert(selected_repr != NULL);
1507     Inkscape::XML::Node *parent = sp_repr_parent(selected_repr);
1508     g_return_if_fail(parent != NULL);
1509     g_return_if_fail(parent->firstChild() != selected_repr);
1511     Inkscape::XML::Node *ref = NULL;
1512     Inkscape::XML::Node *before = parent->firstChild();
1513     while (before && before->next() != selected_repr) {
1514         ref = before;
1515         before = before->next();
1516     }
1518     parent->changeOrder(selected_repr, ref);
1520     sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1521                      _("Raise node"));
1523     set_tree_select(selected_repr);
1524     set_dt_select(selected_repr);
1529 void cmd_lower_node(GtkObject */*object*/, gpointer /*data*/)
1531     g_assert(selected_repr != NULL);
1532     g_return_if_fail(selected_repr->next() != NULL);
1533     Inkscape::XML::Node *parent = sp_repr_parent(selected_repr);
1535     parent->changeOrder(selected_repr, selected_repr->next());
1537     sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1538                      _("Lower node"));
1540     set_tree_select(selected_repr);
1541     set_dt_select(selected_repr);
1544 void cmd_indent_node(GtkObject */*object*/, gpointer /*data*/)
1546     Inkscape::XML::Node *repr = selected_repr;
1547     g_assert(repr != NULL);
1548     Inkscape::XML::Node *parent = sp_repr_parent(repr);
1549     g_return_if_fail(parent != NULL);
1550     g_return_if_fail(parent->firstChild() != repr);
1552     Inkscape::XML::Node* prev = parent->firstChild();
1553     while (prev && prev->next() != repr) {
1554         prev = prev->next();
1555     }
1556     g_return_if_fail(prev != NULL);
1557     g_return_if_fail(prev->type() == Inkscape::XML::ELEMENT_NODE);
1559     Inkscape::XML::Node* ref = NULL;
1560     if (prev->firstChild()) {
1561         for( ref = prev->firstChild() ; ref->next() ; ref = ref->next() );
1562     }
1564     parent->removeChild(repr);
1565     prev->addChild(repr, ref);
1567     sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1568                      _("Indent node"));
1569     set_tree_select(repr);
1570     set_dt_select(repr);
1572 } // end of cmd_indent_node()
1576 void cmd_unindent_node(GtkObject */*object*/, gpointer /*data*/)
1578     Inkscape::XML::Node *repr = selected_repr;
1579     g_assert(repr != NULL);
1580     Inkscape::XML::Node *parent = sp_repr_parent(repr);
1581     g_return_if_fail(parent);
1582     Inkscape::XML::Node *grandparent = sp_repr_parent(parent);
1583     g_return_if_fail(grandparent);
1585     parent->removeChild(repr);
1586     grandparent->addChild(repr, parent);
1588     sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1589                      _("Unindent node"));
1590     set_tree_select(repr);
1591     set_dt_select(repr);
1593 } // end of cmd_unindent_node()
1596 /*
1597   Local Variables:
1598   mode:c++
1599   c-file-style:"stroustrup"
1600   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1601   indent-tabs-mode:nil
1602   fill-column:99
1603   End:
1604 */
1605 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :