Code

Merge from trunk
[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_activate( Inkscape::Application *inkscape,  SPDesktop *desktop, GtkWidget *dialog );
101 static void sp_xmltree_desktop_deactivate( Inkscape::Application *inkscape,  SPDesktop *desktop, GtkWidget *dialog );
103 static void set_tree_desktop(SPDesktop *desktop);
104 static void set_tree_document(SPDocument *document);
105 static void set_tree_repr(Inkscape::XML::Node *repr);
107 static void set_tree_select(Inkscape::XML::Node *repr);
108 static void propagate_tree_select(Inkscape::XML::Node *repr);
110 static Inkscape::XML::Node *get_dt_select();
111 static void set_dt_select(Inkscape::XML::Node *repr);
113 static void on_tree_select_row(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
114 static void on_tree_unselect_row(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
115 static void after_tree_move(GtkCTree *tree, GtkCTreeNode *node, GtkCTreeNode *new_parent, GtkCTreeNode *new_sibling, gpointer data);
116 static void on_destroy(GtkObject *object, gpointer data);
117 static gboolean on_delete(GtkObject *object, GdkEvent *event, gpointer data);
119 static void on_tree_select_row_enable_if_element(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
120 static void on_tree_select_row_enable_if_mutable(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
121 static void on_tree_select_row_show_if_element(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
122 static void on_tree_select_row_show_if_text(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
123 static void on_tree_select_row_enable_if_indentable(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
124 static void on_tree_select_row_enable_if_not_first_child(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
125 static void on_tree_select_row_enable_if_not_last_child(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
126 static void on_tree_select_row_enable_if_has_grandparent(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
128 static void on_tree_unselect_row_clear_text(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
129 static void on_tree_unselect_row_disable(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
130 static void on_tree_unselect_row_hide(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
132 static void on_attr_select_row(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
133 static void on_attr_unselect_row(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
134 static void on_attr_row_changed( GtkCList *list, gint row, gpointer data );
136 static void on_attr_select_row_enable(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
137 static void on_attr_unselect_row_disable(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
139 static void on_attr_select_row_set_name_content(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
140 static void on_attr_select_row_set_value_content(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
141 static void on_attr_unselect_row_clear_text(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
143 static void on_editable_changed_enable_if_valid_xml_name(GtkEditable *editable, gpointer data);
145 static void on_desktop_selection_changed(Inkscape::Selection *selection);
146 static void on_document_replaced(SPDesktop *dt, SPDocument *document);
147 static void on_document_uri_set(gchar const *uri, SPDocument *document);
149 static void on_clicked_get_editable_text(GtkWidget *widget, gpointer data);
151 static void _set_status_message(Inkscape::MessageType type, const gchar *message, GtkWidget *dialog);
153 static void cmd_new_element_node(GtkObject *object, gpointer data);
154 static void cmd_new_text_node(GtkObject *object, gpointer data);
155 static void cmd_duplicate_node(GtkObject *object, gpointer data);
156 static void cmd_delete_node(GtkObject *object, gpointer data);
158 static void cmd_raise_node(GtkObject *object, gpointer data);
159 static void cmd_lower_node(GtkObject *object, gpointer data);
160 static void cmd_indent_node(GtkObject *object, gpointer data);
161 static void cmd_unindent_node(GtkObject *object, gpointer data);
163 static void cmd_delete_attr(GtkObject *object, gpointer data);
164 static void cmd_set_attr(GtkObject *object, gpointer data);
166 static gboolean sp_xml_tree_key_press(GtkWidget *widget, GdkEventKey *event);
169 /*
170  * \brief Sets the XML status bar when the tree is selected.
171  */
172 void tree_reset_context()
174     _message_context->set(Inkscape::NORMAL_MESSAGE,
175                           _("<b>Click</b> to select nodes, <b>drag</b> to rearrange."));
179 /*
180  * \brief Sets the XML status bar, depending on which attr is selected.
181  */
182 void attr_reset_context(gint attr)
184     if (attr == 0) {
185         _message_context->set(Inkscape::NORMAL_MESSAGE,
186                               _("<b>Click</b> attribute to edit."));
187     }
188     else {
189         const gchar *name = g_quark_to_string(attr);
190         gchar *message = g_strdup_printf(_("Attribute <b>%s</b> selected. Press <b>Ctrl+Enter</b> when done editing to commit changes."), name);
191         _message_context->set(Inkscape::NORMAL_MESSAGE, message);
192         g_free(message);
193     }
197 void sp_xml_tree_dialog()
199     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
201     if (!desktop) {
202         return;
203     }
205     if (dlg == NULL)
206     { // very long block
208         GtkWidget *box, *sw, *paned, *toolbar, *button;
209         GtkWidget *text_container, *attr_container, *attr_subpaned_container, *box2;
210         GtkWidget *set_attr;
212         tooltips = gtk_tooltips_new();
213         gtk_tooltips_enable(tooltips);
215         dlg = sp_window_new("", TRUE);
216         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
217         if (x == -1000 || y == -1000) {
218             x = prefs->getInt(prefs_path + "x", -1000);
219             y = prefs->getInt(prefs_path + "y", -1000);
220         }
221         if (w ==0 || h == 0) {
222             w = prefs->getInt(prefs_path + "w", 0);
223             h = prefs->getInt(prefs_path + "h", 0);
224         }
226 //        if (x<0) x=0;
227 //        if (y<0) y=0;
229         if (w && h) {
230             gtk_window_resize((GtkWindow *) dlg, w, h);
231         }
232         if (x >= 0 && y >= 0 && (x < (gdk_screen_width()-MIN_ONSCREEN_DISTANCE)) && (y < (gdk_screen_height()-MIN_ONSCREEN_DISTANCE))) {
233             gtk_window_move((GtkWindow *) dlg, x, y);
234         } else {
235             gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
236         }
238         sp_transientize(dlg);
239         wd.win = dlg;
240         wd.stop = 0;
241         g_signal_connect  ( G_OBJECT(INKSCAPE), "activate_desktop", G_CALLBACK(sp_transientize_callback), &wd );
243         gtk_signal_connect( GTK_OBJECT(dlg), "event", GTK_SIGNAL_FUNC(sp_dialog_event_handler), dlg );
245         gtk_signal_connect( GTK_OBJECT(dlg), "destroy", G_CALLBACK(on_destroy), dlg);
246         gtk_signal_connect( GTK_OBJECT(dlg), "delete_event", G_CALLBACK(on_delete), dlg);
247         g_signal_connect  ( G_OBJECT(INKSCAPE), "shut_down", G_CALLBACK(on_delete), dlg);
249         g_signal_connect  ( G_OBJECT(INKSCAPE), "dialogs_hide", G_CALLBACK(sp_dialog_hide), dlg);
250         g_signal_connect  ( G_OBJECT(INKSCAPE), "dialogs_unhide", G_CALLBACK(sp_dialog_unhide), dlg);
253         gtk_container_set_border_width(GTK_CONTAINER(dlg), 0);
254         gtk_window_set_default_size(GTK_WINDOW(dlg), 640, 384);
256         GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
257         gtk_container_add(GTK_CONTAINER(dlg), vbox);
259         GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
260         gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
262         status = gtk_label_new(NULL);
263         gtk_misc_set_alignment(GTK_MISC(status), 0.0, 0.5);
264         gtk_widget_set_size_request(status, 1, -1);
265         gtk_label_set_markup(GTK_LABEL(status), "");
266         gtk_box_pack_start(GTK_BOX(hbox), gtk_hbox_new(FALSE, 0), FALSE, FALSE, 4);
267         gtk_box_pack_start(GTK_BOX(hbox), status, TRUE, TRUE, 0);
269         paned = gtk_hpaned_new();
270         gtk_paned_set_position(GTK_PANED(paned), 256);
271         gtk_box_pack_start(GTK_BOX(vbox), paned, TRUE, TRUE, 0);
273         _message_stack = new Inkscape::MessageStack();
274         _message_context = new Inkscape::MessageContext(_message_stack);
275         _message_changed_connection = _message_stack->connectChanged(
276             sigc::bind(sigc::ptr_fun(_set_status_message), dlg)
277         );
279         /* tree view */
281         box = gtk_vbox_new(FALSE, 0);
282         gtk_paned_pack1(GTK_PANED(paned), box, FALSE, FALSE);
284         tree = SP_XMLVIEW_TREE(sp_xmlview_tree_new(NULL, NULL, NULL));
285         gtk_tooltips_set_tip( tooltips, GTK_WIDGET(tree),
286                                _("Drag to reorder nodes"), NULL );
288         g_signal_connect( G_OBJECT(tree), "tree_select_row",
289                            G_CALLBACK(on_tree_select_row), NULL );
291         g_signal_connect( G_OBJECT(tree), "tree_unselect_row",
292                            G_CALLBACK(on_tree_unselect_row), NULL );
294         g_signal_connect_after( G_OBJECT(tree), "tree_move",
295                                  G_CALLBACK(after_tree_move), NULL);
297         /* TODO: replace gtk_signal_connect_while_alive() with something
298          * else...
299          */
300         toolbar = gtk_toolbar_new();
301         gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
302         gtk_container_set_border_width(GTK_CONTAINER(toolbar), 0);
304         button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
305                 NULL,
306                 _("New element node"),
307                 NULL,
308                 sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
309                                     INKSCAPE_STOCK_ADD_XML_ELEMENT_NODE ),
310                 G_CALLBACK(cmd_new_element_node),
311                 NULL);
313         gtk_signal_connect_while_alive( GTK_OBJECT(tree),
314                         "tree_select_row",
315                         G_CALLBACK(on_tree_select_row_enable_if_element),
316                         button,
317                         GTK_OBJECT(button));
319         gtk_signal_connect_while_alive(GTK_OBJECT(tree),
320                         "tree_unselect_row",
321                         G_CALLBACK(on_tree_unselect_row_disable),
322                         button,
323                         GTK_OBJECT(button));
325         gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
327         button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
328                 NULL, _("New text node"), NULL,
329                 sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
330                              INKSCAPE_STOCK_ADD_XML_TEXT_NODE ),
331                 G_CALLBACK(cmd_new_text_node),
332                 NULL);
334         gtk_signal_connect_while_alive(GTK_OBJECT(tree),
335                         "tree_select_row",
336                         G_CALLBACK(on_tree_select_row_enable_if_element),
337                         button,
338                         GTK_OBJECT(button));
340         gtk_signal_connect_while_alive(GTK_OBJECT(tree),
341                         "tree_unselect_row",
342                         G_CALLBACK(on_tree_unselect_row_disable),
343                         button,
344                         GTK_OBJECT(button));
346         gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
348         button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
349                 NULL, _("Duplicate node"), NULL,
350                 sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
351                              INKSCAPE_STOCK_DUPLICATE_XML_NODE ),
352                 G_CALLBACK(cmd_duplicate_node),
353                 NULL);
355         gtk_signal_connect_while_alive(GTK_OBJECT(tree),
356                         "tree_select_row",
357                         G_CALLBACK(on_tree_select_row_enable_if_mutable),
358                         button,
359                         GTK_OBJECT(button));
361         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
362                         G_CALLBACK(on_tree_unselect_row_disable),
363                         button, GTK_OBJECT(button));
365         gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
367         gtk_toolbar_append_space(GTK_TOOLBAR(toolbar));
369         button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
370                 NULL, _("Delete node"), NULL,
371                 sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
372                              INKSCAPE_STOCK_DELETE_XML_NODE ),
373                                            G_CALLBACK(cmd_delete_node), NULL );
375         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
376                         G_CALLBACK(on_tree_select_row_enable_if_mutable),
377                         button, GTK_OBJECT(button));
378         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
379                         G_CALLBACK(on_tree_unselect_row_disable),
380                         button, GTK_OBJECT(button));
381         gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
383         gtk_toolbar_append_space(GTK_TOOLBAR(toolbar));
385         button = gtk_toolbar_append_item( GTK_TOOLBAR(toolbar), "<",
386                         _("Unindent node"), NULL,
387                         gtk_arrow_new(GTK_ARROW_LEFT, GTK_SHADOW_IN),
388                         G_CALLBACK(cmd_unindent_node), NULL);
390         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
391                     G_CALLBACK(on_tree_select_row_enable_if_has_grandparent),
392                     button, GTK_OBJECT(button));
394         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
395                         G_CALLBACK(on_tree_unselect_row_disable),
396                         button, GTK_OBJECT(button));
398         gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
400         button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), ">",
401                         _("Indent node"), NULL,
402                         gtk_arrow_new(GTK_ARROW_RIGHT, GTK_SHADOW_IN),
403                         G_CALLBACK(cmd_indent_node), NULL);
404         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
405                         G_CALLBACK(on_tree_select_row_enable_if_indentable),
406                         button, GTK_OBJECT(button));
407         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
408                        (GCallback) on_tree_unselect_row_disable,
409                         button, GTK_OBJECT(button));
410         gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
412         button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), "^",
413                         _("Raise node"), NULL,
414                         gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_IN),
415                         G_CALLBACK(cmd_raise_node), NULL);
416         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
417                     G_CALLBACK(on_tree_select_row_enable_if_not_first_child),
418                     button, GTK_OBJECT(button));
419         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
420                         G_CALLBACK(on_tree_unselect_row_disable),
421                         button, GTK_OBJECT(button));
422         gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
424         button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), "v",
425                         _("Lower node"), NULL,
426                         gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_IN),
427                         G_CALLBACK(cmd_lower_node), NULL);
428         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
429                         G_CALLBACK(on_tree_select_row_enable_if_not_last_child),
430                         button, GTK_OBJECT(button));
431         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
432                         G_CALLBACK(on_tree_unselect_row_disable),
433                         button, GTK_OBJECT(button));
434         gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
436         gtk_box_pack_start(GTK_BOX(box), toolbar, FALSE, TRUE, 0);
438         sw = gtk_scrolled_window_new(NULL, NULL);
439         gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(sw),
440                                          GTK_POLICY_AUTOMATIC,
441                                          GTK_POLICY_AUTOMATIC );
442         gtk_box_pack_start(GTK_BOX(box), sw, TRUE, TRUE, 0);
444         gtk_container_add(GTK_CONTAINER(sw), GTK_WIDGET(tree));
446         /* node view */
448         box = gtk_vbox_new(FALSE, 0);
449         gtk_paned_pack2(GTK_PANED(paned), box, TRUE, TRUE);
451         /* attributes */
453         attr_container = gtk_vbox_new(FALSE, 0);
454         gtk_box_pack_start( GTK_BOX(box), GTK_WIDGET(attr_container),
455                              TRUE, TRUE, 0 );
457         attributes = SP_XMLVIEW_ATTR_LIST(sp_xmlview_attr_list_new(NULL));
458         g_signal_connect( G_OBJECT(attributes), "select_row",
459                            G_CALLBACK(on_attr_select_row), NULL);
460         g_signal_connect( G_OBJECT(attributes), "unselect_row",
461                            G_CALLBACK(on_attr_unselect_row), NULL);
462         g_signal_connect( G_OBJECT(attributes), "row-value-changed",
463                            G_CALLBACK(on_attr_row_changed), NULL);
465         toolbar = gtk_toolbar_new();
466         gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
467         gtk_container_set_border_width(GTK_CONTAINER(toolbar), 0);
469         button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
470                 NULL, _("Delete attribute"), NULL,
471                 sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
472                              INKSCAPE_STOCK_DELETE_XML_ATTRIBUTE ),
473                (GCallback) cmd_delete_attr, NULL);
475         gtk_signal_connect_while_alive(GTK_OBJECT(attributes), "select_row",
476                        (GCallback) on_attr_select_row_enable, button,
477                         GTK_OBJECT(button));
479         gtk_signal_connect_while_alive(GTK_OBJECT(attributes), "unselect_row",
480                        (GCallback) on_attr_unselect_row_disable, button,
481                         GTK_OBJECT(button));
483         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
484                        (GCallback) on_tree_unselect_row_disable, button,
485                         GTK_OBJECT(button));
487         gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
489         gtk_box_pack_start( GTK_BOX(attr_container),
490                              GTK_WIDGET(toolbar), FALSE, TRUE, 0 );
492         attr_subpaned_container = gtk_vpaned_new();
493         gtk_box_pack_start( GTK_BOX(attr_container),
494                              GTK_WIDGET(attr_subpaned_container),
495                              TRUE, TRUE, 0 );
496         gtk_widget_show(attr_subpaned_container);
498         sw = gtk_scrolled_window_new(NULL, NULL);
499         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
500                 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
501         gtk_paned_pack1( GTK_PANED(attr_subpaned_container),
502                           GTK_WIDGET(sw), TRUE, TRUE );
503         gtk_container_add(GTK_CONTAINER(sw), GTK_WIDGET(attributes));
505         toolbar = gtk_vbox_new(FALSE, 4);
506         gtk_container_set_border_width(GTK_CONTAINER(toolbar), 4);
508         box2 = gtk_hbox_new(FALSE, 4);
509         gtk_box_pack_start( GTK_BOX(toolbar), GTK_WIDGET(box2),
510                              FALSE, TRUE, 0);
512         attr_name = GTK_EDITABLE(gtk_entry_new());
513         gtk_tooltips_set_tip( tooltips, GTK_WIDGET(attr_name),
514                                // TRANSLATORS: "Attribute" is a noun here
515                                _("Attribute name"), NULL );
517         gtk_signal_connect( GTK_OBJECT(attributes), "select_row",
518                             (GCallback) on_attr_select_row_set_name_content,
519                              attr_name);
521         gtk_signal_connect( GTK_OBJECT(attributes), "unselect_row",
522                             (GCallback) on_attr_unselect_row_clear_text,
523                              attr_name);
525         gtk_signal_connect( GTK_OBJECT(tree), "tree_unselect_row",
526                             (GCallback) on_tree_unselect_row_clear_text,
527                              attr_name);
529         gtk_box_pack_start( GTK_BOX(box2), GTK_WIDGET(attr_name),
530                              TRUE, TRUE, 0);
532         set_attr = gtk_button_new();
533         gtk_tooltips_set_tip( tooltips, GTK_WIDGET(set_attr),
534                                // TRANSLATORS: "Set" is a verb here
535                                _("Set attribute"), NULL );
536         // TRANSLATORS: "Set" is a verb here
537         GtkWidget *set_label = gtk_label_new(_("Set"));
538         gtk_container_add(GTK_CONTAINER(set_attr), set_label);
540         gtk_signal_connect( GTK_OBJECT(set_attr), "clicked",
541                             (GCallback) cmd_set_attr, NULL);
542         gtk_signal_connect( GTK_OBJECT(attr_name), "changed",
543                    (GCallback) on_editable_changed_enable_if_valid_xml_name,
544                     set_attr );
545         gtk_widget_set_sensitive(GTK_WIDGET(set_attr), FALSE);
547         gtk_box_pack_start(GTK_BOX(box2), set_attr, FALSE, FALSE, 0);
549         sw = gtk_scrolled_window_new(NULL, NULL);
550         gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(sw),
551                                          GTK_POLICY_AUTOMATIC,
552                                          GTK_POLICY_AUTOMATIC );
553         gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN );
554         gtk_box_pack_start(GTK_BOX(toolbar), sw, TRUE, TRUE, 0);
556         attr_value =(GtkTextView *) gtk_text_view_new();
557         gtk_text_view_set_wrap_mode((GtkTextView *) attr_value, GTK_WRAP_CHAR);
558         gtk_tooltips_set_tip( tooltips, GTK_WIDGET(attr_value),
559                                // TRANSLATORS: "Attribute" is a noun here
560                                _("Attribute value"), NULL );
561         gtk_signal_connect( GTK_OBJECT(attributes), "select_row",
562                             (GCallback) on_attr_select_row_set_value_content,
563                              attr_value );
564         gtk_signal_connect( GTK_OBJECT(attributes), "unselect_row",
565                             (GCallback) on_attr_unselect_row_clear_text,
566                              attr_value );
567         gtk_signal_connect( GTK_OBJECT(tree), "tree_unselect_row",
568                             (GCallback) on_tree_unselect_row_clear_text,
569                              attr_value );
570         gtk_text_view_set_editable(attr_value, TRUE);
571         gtk_container_add( GTK_CONTAINER(sw),
572                             GTK_WIDGET(attr_value) );
574         gtk_paned_pack2( GTK_PANED(attr_subpaned_container),
575                           GTK_WIDGET(toolbar), FALSE, TRUE );
577         /* text */
579         sw = gtk_scrolled_window_new(NULL, NULL);
580         gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(sw),
581                                          GTK_POLICY_AUTOMATIC,
582                                          GTK_POLICY_AUTOMATIC );
583         gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN );
584         gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(sw), TRUE, TRUE, 0);
586         content = SP_XMLVIEW_CONTENT(sp_xmlview_content_new(NULL));
587         gtk_container_add(GTK_CONTAINER(sw), GTK_WIDGET(content));
589         text_container = sw;
591         /* initial show/hide */
593         gtk_widget_show_all(GTK_WIDGET(dlg));
595         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
596                        (GCallback) on_tree_select_row_show_if_element,
597                         attr_container, GTK_OBJECT(attr_container));
599         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
600                        (GCallback) on_tree_unselect_row_hide,
601                         attr_container, GTK_OBJECT(attr_container));
603         gtk_widget_hide(attr_container);
605         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
606                        (GCallback) on_tree_select_row_show_if_text,
607                         text_container, GTK_OBJECT(text_container));
609         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
610                        (GCallback) on_tree_unselect_row_hide,
611                         text_container, GTK_OBJECT(text_container));
613         gtk_widget_hide(text_container);
615         g_signal_connect( G_OBJECT(INKSCAPE), "activate_desktop",
616                            G_CALLBACK(sp_xmltree_desktop_activate), dlg);
618         g_signal_connect( G_OBJECT(INKSCAPE), "deactivate_desktop",
619                            G_CALLBACK(sp_xmltree_desktop_deactivate), dlg);
621         g_signal_connect((GObject *) dlg, "key_press_event", (GCallback) sp_xml_tree_key_press, NULL);
623         tree_reset_context();
624     } // end of if (dlg == NULL)
626     gtk_window_present((GtkWindow *) dlg);
628     g_assert(desktop != NULL);
629     set_tree_desktop(desktop);
631 } // end of sp_xml_tree_dialog()
633 static gboolean sp_xml_tree_key_press(GtkWidget */*widget*/, GdkEventKey *event)
636     unsigned int shortcut = get_group0_keyval(event) |
637         ( event->state & GDK_SHIFT_MASK ?
638           SP_SHORTCUT_SHIFT_MASK : 0 ) |
639         ( event->state & GDK_CONTROL_MASK ?
640           SP_SHORTCUT_CONTROL_MASK : 0 ) |
641         ( event->state & GDK_MOD1_MASK ?
642           SP_SHORTCUT_ALT_MASK : 0 );
644     /* fixme: if you need to add more xml-tree-specific callbacks, you should probably upgrade
645      * the sp_shortcut mechanism to take into account windows. */
646     if (shortcut == (SP_SHORTCUT_CONTROL_MASK | GDK_Return)) {
647         cmd_set_attr(NULL, NULL);
648         return true;
649     }
650     return false;
654 static void sp_xmltree_desktop_activate(Inkscape::Application */*inkscape*/,
655                                       SPDesktop *desktop,
656                                       GtkWidget */*dialog*/ )
658     set_tree_desktop(desktop);
661 static void sp_xmltree_desktop_deactivate(Inkscape::Application */*inkscape*/,
662                                       SPDesktop *desktop,
663                                       GtkWidget */*dialog*/ )
665     set_tree_desktop(NULL);
669 void set_tree_desktop(SPDesktop *desktop)
671     if ( desktop == current_desktop ) {
672         return;
673     }
675     if (current_desktop) {
676         sel_changed_connection.disconnect();
677         document_replaced_connection.disconnect();
678     }
679     current_desktop = desktop;
680     if (desktop) {
681         sel_changed_connection = sp_desktop_selection(desktop)->connectChanged(&on_desktop_selection_changed);
682         document_replaced_connection = desktop->connectDocumentReplaced(&on_document_replaced);
683         set_tree_document(sp_desktop_document(desktop));
684     } else {
685         set_tree_document(NULL);
686     }
688 } // end of set_tree_desktop()
692 void set_tree_document(SPDocument *document)
694     if (document == current_document) {
695         return;
696     }
698     if (current_document) {
699         document_uri_set_connection.disconnect();
700     }
701     current_document = document;
702     if (current_document) {
704         document_uri_set_connection = current_document->connectURISet(sigc::bind(sigc::ptr_fun(&on_document_uri_set), current_document));
705         on_document_uri_set(SP_DOCUMENT_URI(current_document), current_document);
706         set_tree_repr(sp_document_repr_root(current_document));
708     } else {
709         set_tree_repr(NULL);
710     }
715 void set_tree_repr(Inkscape::XML::Node *repr)
717     if (repr == selected_repr) {
718         return;
719     }
721     gtk_clist_freeze(GTK_CLIST(tree));
723     sp_xmlview_tree_set_repr(tree, repr);
725     if (repr) {
726         set_tree_select(get_dt_select());
727     } else {
728         set_tree_select(NULL);
729     }
731     gtk_clist_thaw(GTK_CLIST(tree));
733     propagate_tree_select(selected_repr);
739 void set_tree_select(Inkscape::XML::Node *repr)
741     if (selected_repr) {
742         Inkscape::GC::release(selected_repr);
743     }
745     selected_repr = repr;
746     if (repr) {
747         GtkCTreeNode *node;
749         Inkscape::GC::anchor(selected_repr);
751         node = sp_xmlview_tree_get_repr_node(SP_XMLVIEW_TREE(tree), repr);
752         if (node) {
753             GtkCTreeNode *parent;
755             gtk_ctree_select(GTK_CTREE(tree), node);
757             parent = GTK_CTREE_ROW(node)->parent;
758             while (parent) {
759                 gtk_ctree_expand(GTK_CTREE(tree), parent);
760                 parent = GTK_CTREE_ROW(parent)->parent;
761             }
763             gtk_ctree_node_moveto(GTK_CTREE(tree), node, 0, 0.66, 0.0);
764         }
765     } else {
766         gtk_clist_unselect_all(GTK_CLIST(tree));
767     }
768     propagate_tree_select(repr);
773 void propagate_tree_select(Inkscape::XML::Node *repr)
775     if (repr && repr->type() == Inkscape::XML::ELEMENT_NODE) {
776         sp_xmlview_attr_list_set_repr(attributes, repr);
777     } else {
778         sp_xmlview_attr_list_set_repr(attributes, NULL);
779     }
781     if (repr && ( repr->type() == Inkscape::XML::TEXT_NODE || repr->type() == Inkscape::XML::COMMENT_NODE || repr->type() == Inkscape::XML::PI_NODE ) ) {
782         sp_xmlview_content_set_repr(content, repr);
783     } else {
784         sp_xmlview_content_set_repr(content, NULL);
785     }
789 Inkscape::XML::Node *get_dt_select()
791     if (!current_desktop) {
792         return NULL;
793     }
795     return sp_desktop_selection(current_desktop)->singleRepr();
800 void set_dt_select(Inkscape::XML::Node *repr)
802     if (!current_desktop) {
803         return;
804     }
806     Inkscape::Selection *selection = sp_desktop_selection(current_desktop);
808     SPObject *object;
809     if (repr) {
810         while ( ( repr->type() != Inkscape::XML::ELEMENT_NODE )
811                 && sp_repr_parent(repr) )
812         {
813             repr = sp_repr_parent(repr);
814         } // end of while loop
816         object = sp_desktop_document(current_desktop)->getObjectByRepr(repr);
817     } else {
818         object = NULL;
819     }
821     blocked++;
822     if ( object && in_dt_coordsys(*object)
823          && !(SP_IS_STRING(object) ||
824                 SP_IS_ROOT(object)     ) )
825     {
826             /* We cannot set selection to root or string - they are not items and selection is not
827              * equipped to deal with them */
828             selection->set(SP_ITEM(object));
829     }
830     blocked--;
832 } // end of set_dt_select()
835 void on_tree_select_row(GtkCTree *tree,
836                         GtkCTreeNode *node,
837                         gint /*column*/,
838                         gpointer /*data*/)
840     if (blocked) {
841         return;
842     }
844     Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
845     g_assert(repr != NULL);
847     if (selected_repr == repr) {
848         return;
849     }
851     if (selected_repr) {
852         Inkscape::GC::release(selected_repr);
853         selected_repr = NULL;
854     }
855     selected_repr = repr;
856     Inkscape::GC::anchor(selected_repr);
858     propagate_tree_select(selected_repr);
860     set_dt_select(selected_repr);
862     tree_reset_context();
865 void on_tree_unselect_row(GtkCTree *tree,
866                           GtkCTreeNode *node,
867                           gint /*column*/,
868                           gpointer /*data*/)
870     if (blocked) {
871         return;
872     }
874     Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
875     propagate_tree_select(NULL);
876     set_dt_select(NULL);
878     if (selected_repr && (selected_repr == repr)) {
879         Inkscape::GC::release(selected_repr);
880         selected_repr = NULL;
881         selected_attr = 0;
882     }
887 void after_tree_move(GtkCTree */*tree*/,
888                      GtkCTreeNode *node,
889                      GtkCTreeNode *new_parent,
890                      GtkCTreeNode *new_sibling,
891                      gpointer /*data*/)
893     if (GTK_CTREE_ROW(node)->parent  == new_parent &&
894         GTK_CTREE_ROW(node)->sibling == new_sibling)
895     {
896         sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
897                          _("Drag XML subtree"));
898     } else {
899         sp_document_cancel(current_document);
900     }
904 static void on_destroy(GtkObject */*object*/, gpointer /*data*/)
906     set_tree_desktop(NULL);
907     gtk_object_destroy(GTK_OBJECT(tooltips));
908     tooltips = NULL;
909     sp_signal_disconnect_by_data(INKSCAPE, dlg);
910     wd.win = dlg = NULL;
911     wd.stop = 0;
913     _message_changed_connection.disconnect();
914     delete _message_context;
915     _message_context = NULL;
916     Inkscape::GC::release(_message_stack);
917     _message_stack = NULL;
918     _message_changed_connection.~connection();
920     status = NULL;
925 static gboolean on_delete(GtkObject */*object*/, GdkEvent */*event*/, gpointer /*data*/)
927     gtk_window_get_position((GtkWindow *) dlg, &x, &y);
928     gtk_window_get_size((GtkWindow *) dlg, &w, &h);
930     if (x<0) x=0;
931     if (y<0) y=0;
933     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
934     prefs->setInt(prefs_path + "x", x);
935     prefs->setInt(prefs_path + "y", y);
936     prefs->setInt(prefs_path + "w", w);
937     prefs->setInt(prefs_path + "h", h);
939     return FALSE; // which means, go ahead and destroy it
943 static void _set_status_message(Inkscape::MessageType /*type*/, const gchar *message, GtkWidget */*dialog*/)
945     if (status) {
946         gtk_label_set_markup(GTK_LABEL(status), message ? message : "");
947     }
951 void on_tree_select_row_enable(GtkCTree */*tree*/,
952                                GtkCTreeNode */*node*/,
953                                gint /*column*/,
954                                gpointer data)
956     gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
961 void on_tree_select_row_enable_if_element(GtkCTree *tree,
962                                           GtkCTreeNode *node,
963                                           gint /*column*/,
964                                           gpointer data )
966     Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
968     if (repr->type() == Inkscape::XML::ELEMENT_NODE) {
969         gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
970     } else {
971         gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
972     }
977 void on_tree_select_row_show_if_element(GtkCTree *tree, GtkCTreeNode *node,
978                                         gint /*column*/, gpointer data)
980     Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
982     if (repr->type() == Inkscape::XML::ELEMENT_NODE) {
983         gtk_widget_show(GTK_WIDGET(data));
984     } else {
985         gtk_widget_hide(GTK_WIDGET(data));
986     }
991 void on_tree_select_row_show_if_text(GtkCTree *tree, GtkCTreeNode *node,
992                                      gint /*column*/, gpointer data)
994     Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
996     if ( repr->type() == Inkscape::XML::TEXT_NODE || repr->type() == Inkscape::XML::COMMENT_NODE || repr->type() == Inkscape::XML::PI_NODE ) {
997         gtk_widget_show(GTK_WIDGET(data));
998     } else {
999         gtk_widget_hide(GTK_WIDGET(data));
1000     }
1004 gboolean xml_tree_node_mutable(GtkCTreeNode *node)
1006     // top-level is immutable, obviously
1007     if (!GTK_CTREE_ROW(node)->parent) {
1008         return false;
1009     }
1011     // if not in base level (where namedview, defs, etc go), we're mutable
1012     if (GTK_CTREE_ROW(GTK_CTREE_ROW(node)->parent)->parent) {
1013         return true;
1014     }
1016     Inkscape::XML::Node *repr;
1017     repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
1018     g_assert(repr);
1020     // don't let "defs" or "namedview" disappear
1021     if ( !strcmp(repr->name(),"svg:defs") ||
1022          !strcmp(repr->name(),"sodipodi:namedview") ) {
1023         return false;
1024     }
1026     // everyone else is okay, I guess.  :)
1027     return true;
1031 void on_tree_select_row_enable_if_mutable(GtkCTree */*tree*/, GtkCTreeNode *node,
1032                                           gint /*column*/, gpointer data)
1034     gtk_widget_set_sensitive(GTK_WIDGET(data), xml_tree_node_mutable(node));
1039 void on_tree_unselect_row_disable(GtkCTree */*tree*/, GtkCTreeNode */*node*/,
1040                                   gint /*column*/, gpointer data)
1042     gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1047 void on_tree_unselect_row_hide(GtkCTree */*tree*/, GtkCTreeNode */*node*/,
1048                                gint /*column*/, gpointer data)
1050     gtk_widget_hide(GTK_WIDGET(data));
1055 void on_tree_unselect_row_clear_text(GtkCTree */*tree*/, GtkCTreeNode */*node*/,
1056                                      gint /*column*/, gpointer data)
1058     if (GTK_IS_EDITABLE(data)) {
1059         gtk_editable_delete_text(GTK_EDITABLE(data), 0, -1);
1060     } else if (GTK_IS_TEXT_VIEW(data)) {
1061         GtkTextBuffer *tb;
1062         tb = gtk_text_view_get_buffer(GTK_TEXT_VIEW(data));
1063         gtk_text_buffer_set_text(tb, "", 0);
1064     }
1068 void on_attr_select_row(GtkCList *list, gint row, gint /*column*/,
1069                         GdkEventButton */*event*/, gpointer /*data*/)
1071     selected_attr = sp_xmlview_attr_list_get_row_key(list, row);
1072     gtk_window_set_focus(GTK_WINDOW(dlg), GTK_WIDGET(attr_value));
1074     attr_reset_context(selected_attr);
1078 void on_attr_unselect_row(GtkCList */*list*/, gint /*row*/, gint /*column*/,
1079                           GdkEventButton */*event*/, gpointer /*data*/)
1081     selected_attr = 0;
1082     attr_reset_context(selected_attr);
1086 void on_attr_row_changed(GtkCList *list, gint row, gpointer /*data*/)
1088     gint attr = sp_xmlview_attr_list_get_row_key(list, row);
1090     if (attr == selected_attr) {
1091         /* if the attr changed, reselect the row in the list to sync
1092            the edit box */
1094         /*
1095         // get current attr values
1096         const gchar * name = g_quark_to_string (sp_xmlview_attr_list_get_row_key (list, row));
1097         const gchar * value = selected_repr->attribute(name);
1099         g_warning("value: '%s'",value);
1101         // get the edit box value
1102         GtkTextIter start, end;
1103         gtk_text_buffer_get_bounds ( gtk_text_view_get_buffer (attr_value),
1104                                      &start, &end );
1105         gchar * text = gtk_text_buffer_get_text ( gtk_text_view_get_buffer (attr_value),
1106                                        &start, &end, TRUE );
1107         g_warning("text: '%s'",text);
1109         // compare to edit box
1110         if (strcmp(text,value)) {
1111             // issue warning if they're different
1112             _message_stack->flash(Inkscape::WARNING_MESSAGE,
1113                                   _("Attribute changed in GUI while editing values!"));
1114         }
1115         g_free (text);
1117         */
1118         gtk_clist_unselect_row( GTK_CLIST(list), row, 0 );
1119         gtk_clist_select_row( GTK_CLIST(list), row, 0 );
1120     }
1124 void on_attr_select_row_set_name_content(GtkCList *list, gint row,
1125                                          gint /*column*/, GdkEventButton */*event*/,
1126                                          gpointer data)
1128     GtkEditable *editable = GTK_EDITABLE(data);
1129     const gchar *name = g_quark_to_string(sp_xmlview_attr_list_get_row_key(list, row));
1130     gtk_editable_delete_text(editable, 0, -1);
1131     gint pos = 0;
1132     gtk_editable_insert_text(editable, name, strlen(name), &pos);
1137 void on_attr_select_row_set_value_content(GtkCList *list, gint row, gint /*column*/,
1138                                           GdkEventButton */*event*/,
1139                                           gpointer data)
1141     GtkTextBuffer *tb = gtk_text_view_get_buffer(GTK_TEXT_VIEW(data));
1142     const gchar *name = g_quark_to_string(sp_xmlview_attr_list_get_row_key(list, row));
1143     const gchar *value = selected_repr->attribute(name);
1144     if (!value) {
1145         value = "";
1146     }
1147     gtk_text_buffer_set_text(tb, value, strlen(value));
1151 void on_tree_select_row_enable_if_indentable(GtkCTree *tree, GtkCTreeNode *node,
1152                                              gint /*column*/, gpointer data)
1154     gboolean indentable = FALSE;
1156     if (xml_tree_node_mutable(node)) {
1157         Inkscape::XML::Node *repr, *prev;
1158         repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
1160         Inkscape::XML::Node *parent=repr->parent();
1161         if ( parent && repr != parent->firstChild() ) {
1162             g_assert(parent->firstChild());
1164             // skip to the child just before the current repr
1165             for ( prev = parent->firstChild() ;
1166                   prev && prev->next() != repr ;
1167                   prev = prev->next() );
1169             if (prev && prev->type() == Inkscape::XML::ELEMENT_NODE) {
1170                 indentable = TRUE;
1171             }
1172         }
1173     }
1175     gtk_widget_set_sensitive(GTK_WIDGET(data), indentable);
1180 void on_tree_select_row_enable_if_not_first_child(GtkCTree *tree,
1181                                                   GtkCTreeNode *node,
1182                                                   gint /*column*/,
1183                                                   gpointer data)
1185     Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
1187     Inkscape::XML::Node *parent=repr->parent();
1188     if ( parent && repr != parent->firstChild() ) {
1189         gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
1190     } else {
1191         gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1192     }
1197 void on_tree_select_row_enable_if_not_last_child(GtkCTree *tree,
1198                                                  GtkCTreeNode *node,
1199                                                  gint /*column*/, gpointer data)
1201     Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
1203     Inkscape::XML::Node *parent=repr->parent();
1204     if ( parent && parent->parent() && repr->next() ) {
1205         gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
1206     } else {
1207         gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1208     }
1213 void on_tree_select_row_enable_if_has_grandparent(GtkCTree */*tree*/,
1214                                                   GtkCTreeNode *node,
1215                                                   gint /*column*/, gpointer data)
1217     GtkCTreeNode *parent = GTK_CTREE_ROW(node)->parent;
1219     if (parent) {
1220         GtkCTreeNode *grandparent = GTK_CTREE_ROW(parent)->parent;
1221         if (grandparent) {
1222             gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
1223         } else {
1224             gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1225         }
1226     } else {
1227         gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1228     }
1233 void on_attr_select_row_enable(GtkCList */*list*/, gint /*row*/, gint /*column*/,
1234                                GdkEventButton */*event*/, gpointer data)
1236     gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
1241 void on_attr_unselect_row_disable(GtkCList */*list*/, gint /*row*/, gint /*column*/,
1242                                   GdkEventButton */*event*/, gpointer data)
1244     gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1249 void on_attr_unselect_row_clear_text(GtkCList */*list*/, gint /*row*/, gint /*column*/,
1250                                      GdkEventButton */*event*/, gpointer data)
1252     if (GTK_IS_EDITABLE(data)) {
1253         gtk_editable_delete_text(GTK_EDITABLE(data), 0, -1);
1254     } else if (GTK_IS_TEXT_VIEW(data)) {
1255         GtkTextBuffer *tb;
1256         tb = gtk_text_view_get_buffer(GTK_TEXT_VIEW(data));
1257         gtk_text_buffer_set_text(tb, "", 0);
1258     }
1263 void on_editable_changed_enable_if_valid_xml_name(GtkEditable *editable,
1264                                                   gpointer data)
1266     gchar *text = gtk_editable_get_chars(editable, 0, -1);
1268     /* TODO: need to do checking a little more rigorous than this */
1270     if (strlen(text)) {
1271         gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
1272     } else {
1273         gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1274     }
1275     g_free(text);
1280 void on_desktop_selection_changed(Inkscape::Selection */*selection*/)
1282     if (!blocked++) {
1283         set_tree_select(get_dt_select());
1284     }
1285     blocked--;
1288 static void on_document_replaced(SPDesktop *dt, SPDocument *doc)
1290     if (current_desktop)
1291         sel_changed_connection.disconnect();
1293     sel_changed_connection = sp_desktop_selection(dt)->connectChanged(&on_desktop_selection_changed);
1294     set_tree_document(doc);
1297 void on_document_uri_set(gchar const */*uri*/, SPDocument *document)
1299     gchar title[500];
1300     sp_ui_dialog_title_string(Inkscape::Verb::get(SP_VERB_DIALOG_XML_EDITOR), title);
1301     gchar *t = g_strdup_printf("%s: %s", SP_DOCUMENT_NAME(document), title);
1302     gtk_window_set_title(GTK_WINDOW(dlg), t);
1303     g_free(t);
1308 void on_clicked_get_editable_text(GtkWidget */*widget*/, gpointer data)
1310     EditableDest *dest = (EditableDest *) data;
1311     dest->text = gtk_editable_get_chars(dest->editable, 0, -1);
1314 gboolean
1315 quit_on_esc (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
1317     switch (get_group0_keyval (event)) {
1318         case GDK_Escape: // defocus
1319             gtk_widget_destroy(w);
1320             return TRUE;
1321     }
1322     return FALSE;
1325 void cmd_new_element_node(GtkObject */*object*/, gpointer /*data*/)
1327     EditableDest name;
1328     GtkWidget *window, *create, *cancel, *vbox, *entry, *bbox, *sep;
1330     g_assert(selected_repr != NULL);
1332     window = sp_window_new(NULL, TRUE);
1333     gtk_container_set_border_width(GTK_CONTAINER(window), 4);
1334     gtk_window_set_title(GTK_WINDOW(window), _("New element node..."));
1335     gtk_window_set_policy(GTK_WINDOW(window), FALSE, FALSE, TRUE);
1336     gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
1337     gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(dlg));
1338     gtk_window_set_modal(GTK_WINDOW(window), TRUE);
1339     gtk_signal_connect(GTK_OBJECT(window), "destroy", gtk_main_quit, NULL);
1340     gtk_signal_connect(GTK_OBJECT(window), "key-press-event", G_CALLBACK(quit_on_esc), window);
1342     vbox = gtk_vbox_new(FALSE, 4);
1343     gtk_container_add(GTK_CONTAINER(window), vbox);
1345     entry = gtk_entry_new();
1346     gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, TRUE, 0);
1348     sep = gtk_hseparator_new();
1349     gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, TRUE, 0);
1351     bbox = gtk_hbutton_box_new();
1352     gtk_container_set_border_width(GTK_CONTAINER(bbox), 4);
1353     gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
1354     gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, TRUE, 0);
1356     cancel = gtk_button_new_with_label(_("Cancel"));
1357     GTK_WIDGET_SET_FLAGS( GTK_WIDGET(cancel),
1358                            GTK_CAN_DEFAULT );
1359     gtk_signal_connect_object( GTK_OBJECT(cancel), "clicked",
1360                                 G_CALLBACK(gtk_widget_destroy),
1361                                 GTK_OBJECT(window) );
1362     gtk_container_add(GTK_CONTAINER(bbox), cancel);
1364     create = gtk_button_new_with_label(_("Create"));
1365     gtk_widget_set_sensitive(GTK_WIDGET(create), FALSE);
1366     gtk_signal_connect( GTK_OBJECT(entry), "changed",
1367                     G_CALLBACK(on_editable_changed_enable_if_valid_xml_name),
1368                     create );
1369     gtk_signal_connect( GTK_OBJECT(create), "clicked",
1370                          G_CALLBACK(on_clicked_get_editable_text), &name );
1371     gtk_signal_connect_object( GTK_OBJECT(create), "clicked",
1372                                 G_CALLBACK(gtk_widget_destroy),
1373                                 GTK_OBJECT(window) );
1374     GTK_WIDGET_SET_FLAGS( GTK_WIDGET(create),
1375                            GTK_CAN_DEFAULT | GTK_RECEIVES_DEFAULT );
1376     gtk_container_add(GTK_CONTAINER(bbox), create);
1378     gtk_widget_show_all(GTK_WIDGET(window));
1379     gtk_window_set_default(GTK_WINDOW(window), GTK_WIDGET(create));
1380     gtk_window_set_focus(GTK_WINDOW(window), GTK_WIDGET(entry));
1382     name.editable = GTK_EDITABLE(entry);
1383     name.text = NULL;
1385     gtk_main();
1387     if (selected_repr != NULL && name.text) {
1388         Inkscape::XML::Document *xml_doc = sp_document_repr_doc(current_document);
1389         Inkscape::XML::Node *new_repr;
1390         new_repr = xml_doc->createElement(name.text);
1391         Inkscape::GC::release(new_repr);
1392         g_free(name.text);
1393         selected_repr->appendChild(new_repr);
1394         set_tree_select(new_repr);
1395         set_dt_select(new_repr);
1397         sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1398                      _("Create new element node"));
1399     }
1401 } // end of cmd_new_element_node()
1405 void cmd_new_text_node(GtkObject */*object*/, gpointer /*data*/)
1407     g_assert(selected_repr != NULL);
1409     Inkscape::XML::Document *xml_doc = sp_document_repr_doc(current_document);
1410     Inkscape::XML::Node *text = xml_doc->createTextNode("");
1411     selected_repr->appendChild(text);
1413     sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1414                      _("Create new text node"));
1416     set_tree_select(text);
1417     set_dt_select(text);
1419     gtk_window_set_focus(GTK_WINDOW(dlg), GTK_WIDGET(content));
1423 void cmd_duplicate_node(GtkObject */*object*/, gpointer /*data*/)
1425     g_assert(selected_repr != NULL);
1427     Inkscape::XML::Node *parent = sp_repr_parent(selected_repr);
1428     Inkscape::XML::Node *dup = selected_repr->duplicate(parent->document());
1429     parent->addChild(dup, selected_repr);
1431     sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1432                      _("Duplicate node"));
1434     GtkCTreeNode *node = sp_xmlview_tree_get_repr_node(SP_XMLVIEW_TREE(tree), dup);
1436     if (node) {
1437         gtk_ctree_select(GTK_CTREE(tree), node);
1438     }
1443 void cmd_delete_node(GtkObject */*object*/, gpointer /*data*/)
1445     g_assert(selected_repr != NULL);
1446     sp_repr_unparent(selected_repr);
1448     sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1449                      _("Delete node"));
1454 void cmd_delete_attr(GtkObject */*object*/, gpointer /*data*/)
1456     g_assert(selected_repr != NULL);
1457     g_assert(selected_attr != 0);
1458     selected_repr->setAttribute(g_quark_to_string(selected_attr), NULL);
1460     SPObject *updated=current_document->getObjectByRepr(selected_repr);
1461     if (updated) {
1462         // force immediate update of dependant attributes
1463         updated->updateRepr();
1464     }
1466     sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1467                      _("Delete attribute"));
1472 void cmd_set_attr(GtkObject */*object*/, gpointer /*data*/)
1474     g_assert(selected_repr != NULL);
1476     gchar *name = gtk_editable_get_chars(attr_name, 0, -1);
1477     GtkTextIter start;
1478     GtkTextIter end;
1479     gtk_text_buffer_get_bounds( gtk_text_view_get_buffer(attr_value),
1480                                  &start, &end );
1481     gchar *value = gtk_text_buffer_get_text( gtk_text_view_get_buffer(attr_value),
1482                                        &start, &end, TRUE );
1484     selected_repr->setAttribute(name, value, false);
1486     g_free(name);
1487     g_free(value);
1489     SPObject *updated = current_document->getObjectByRepr(selected_repr);
1490     if (updated) {
1491         // force immediate update of dependant attributes
1492         updated->updateRepr();
1493     }
1495     sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1496                      _("Change attribute"));
1498     /* TODO: actually, the row won't have been created yet.  why? */
1499     gint row = sp_xmlview_attr_list_find_row_from_key(GTK_CLIST(attributes),
1500                                                       g_quark_from_string(name));
1501     if (row != -1) {
1502         gtk_clist_select_row(GTK_CLIST(attributes), row, 0);
1503     }
1508 void cmd_raise_node(GtkObject */*object*/, gpointer /*data*/)
1510     g_assert(selected_repr != NULL);
1512     Inkscape::XML::Node *parent = sp_repr_parent(selected_repr);
1513     g_return_if_fail(parent != NULL);
1514     g_return_if_fail(parent->firstChild() != selected_repr);
1516     Inkscape::XML::Node *ref = NULL;
1517     Inkscape::XML::Node *before = parent->firstChild();
1518     while (before && before->next() != selected_repr) {
1519         ref = before;
1520         before = before->next();
1521     }
1523     parent->changeOrder(selected_repr, ref);
1525     sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1526                      _("Raise node"));
1528     set_tree_select(selected_repr);
1529     set_dt_select(selected_repr);
1534 void cmd_lower_node(GtkObject */*object*/, gpointer /*data*/)
1536     g_assert(selected_repr != NULL);
1537     g_return_if_fail(selected_repr->next() != NULL);
1538     Inkscape::XML::Node *parent = sp_repr_parent(selected_repr);
1540     parent->changeOrder(selected_repr, selected_repr->next());
1542     sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1543                      _("Lower node"));
1545     set_tree_select(selected_repr);
1546     set_dt_select(selected_repr);
1549 void cmd_indent_node(GtkObject */*object*/, gpointer /*data*/)
1551     Inkscape::XML::Node *repr = selected_repr;
1552     g_assert(repr != NULL);
1553     Inkscape::XML::Node *parent = sp_repr_parent(repr);
1554     g_return_if_fail(parent != NULL);
1555     g_return_if_fail(parent->firstChild() != repr);
1557     Inkscape::XML::Node* prev = parent->firstChild();
1558     while (prev && prev->next() != repr) {
1559         prev = prev->next();
1560     }
1561     g_return_if_fail(prev != NULL);
1562     g_return_if_fail(prev->type() == Inkscape::XML::ELEMENT_NODE);
1564     Inkscape::XML::Node* ref = NULL;
1565     if (prev->firstChild()) {
1566         for( ref = prev->firstChild() ; ref->next() ; ref = ref->next() );
1567     }
1569     parent->removeChild(repr);
1570     prev->addChild(repr, ref);
1572     sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1573                      _("Indent node"));
1574     set_tree_select(repr);
1575     set_dt_select(repr);
1577 } // end of cmd_indent_node()
1581 void cmd_unindent_node(GtkObject */*object*/, gpointer /*data*/)
1583     Inkscape::XML::Node *repr = selected_repr;
1584     g_assert(repr != NULL);
1585     Inkscape::XML::Node *parent = sp_repr_parent(repr);
1586     g_return_if_fail(parent);
1587     Inkscape::XML::Node *grandparent = sp_repr_parent(parent);
1588     g_return_if_fail(grandparent);
1590     parent->removeChild(repr);
1591     grandparent->addChild(repr, parent);
1593     sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1594                      _("Unindent node"));
1595     set_tree_select(repr);
1596     set_dt_select(repr);
1598 } // end of cmd_unindent_node()
1601 /*
1602   Local Variables:
1603   mode:c++
1604   c-file-style:"stroustrup"
1605   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1606   indent-tabs-mode:nil
1607   fill-column:99
1608   End:
1609 */
1610 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :