Code

Merge and cleanup of GSoC C++-ification project.
[inkscape.git] / src / dialogs / xml-tree.cpp
1 /** @file
2  * @brief XML editor
3  */
4 /* Authors:
5  *   Lauris Kaplinski <lauris@kaplinski.com>
6  *   MenTaLguY <mental@rydia.net>
7  *   bulia byak <buliabyak@users.sf.net>
8  *   Johan Engelen <goejendaagh@zonnet.nl>
9  *   David Turner
10  *   Jon A. Cruz <jon@joncruz.org>
11  *   Abhishek Sharma
12  *
13  * Copyright (C) 1999-2006 Authors
14  * Released under GNU GPL, read the file 'COPYING' for more information
15  */
17 #include <gdk/gdkkeysyms.h>
18 #include <glibmm/i18n.h>
19 #include <gtk/gtk.h>
21 #include "desktop.h"
22 #include "../desktop-handles.h"
23 #include "dialog-events.h"
24 #include "../document.h"
25 #include "../event-context.h"
26 #include "helper/window.h"
27 #include "../inkscape.h"
28 #include "../interface.h"
29 #include "macros.h"
30 #include "message-context.h"
31 #include "message-stack.h"
32 #include "../preferences.h"
33 #include "../selection.h"
34 #include "shortcuts.h"
35 #include "../sp-root.h"
36 #include "../sp-string.h"
37 #include "../sp-tspan.h"
38 #include "ui/icon-names.h"
39 #include "../verbs.h"
40 #include "widgets/icon.h"
41 #include "../widgets/sp-xmlview-attr-list.h"
42 #include "../widgets/sp-xmlview-content.h"
43 #include "../widgets/sp-xmlview-tree.h"
45 using Inkscape::DocumentUndo;
47 #define MIN_ONSCREEN_DISTANCE 50
49 struct EditableDest {
50     GtkEditable *editable;
51     gchar *text;
52 };
54 static GtkWidget *dlg = NULL;
55 static sigc::connection sel_changed_connection;
56 static sigc::connection document_uri_set_connection;
57 static sigc::connection document_replaced_connection;
58 static win_data wd;
59 // impossible original values to make sure they are read from prefs
60 static gint x = -1000, y = -1000, w = 0, h = 0;
61 static Glib::ustring const prefs_path = "/dialogs/xml/";
62 static GtkWidget *status = NULL;
63 static Inkscape::MessageStack *_message_stack = NULL;
64 static Inkscape::MessageContext *_message_context = NULL;
65 static sigc::connection _message_changed_connection;
67 static GtkTooltips *tooltips = NULL;
68 static GtkEditable *attr_name = NULL;
69 static GtkTextView *attr_value = NULL;
70 static SPXMLViewTree *tree = NULL;
71 static SPXMLViewAttrList *attributes = NULL;
72 static SPXMLViewContent *content = NULL;
74 static gint blocked = 0;
75 static SPDesktop *current_desktop = NULL;
76 static SPDocument *current_document = NULL;
77 static gint selected_attr = 0;
78 static Inkscape::XML::Node *selected_repr = NULL;
80 static void sp_xmltree_desktop_activate( Inkscape::Application *inkscape,  SPDesktop *desktop, GtkWidget *dialog );
81 static void sp_xmltree_desktop_deactivate( Inkscape::Application *inkscape,  SPDesktop *desktop, GtkWidget *dialog );
83 static void set_tree_desktop(SPDesktop *desktop);
84 static void set_tree_document(SPDocument *document);
85 static void set_tree_repr(Inkscape::XML::Node *repr);
87 static void set_tree_select(Inkscape::XML::Node *repr);
88 static void propagate_tree_select(Inkscape::XML::Node *repr);
90 static Inkscape::XML::Node *get_dt_select();
91 static void set_dt_select(Inkscape::XML::Node *repr);
93 static void on_tree_select_row(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
94 static void on_tree_unselect_row(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
95 static void after_tree_move(GtkCTree *tree, GtkCTreeNode *node, GtkCTreeNode *new_parent, GtkCTreeNode *new_sibling, gpointer data);
96 static void on_destroy(GtkObject *object, gpointer data);
97 static gboolean on_delete(GtkObject *object, GdkEvent *event, gpointer data);
99 static void on_tree_select_row_enable_if_element(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
100 static void on_tree_select_row_enable_if_mutable(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
101 static void on_tree_select_row_show_if_element(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
102 static void on_tree_select_row_show_if_text(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
103 static void on_tree_select_row_enable_if_indentable(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
104 static void on_tree_select_row_enable_if_not_first_child(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
105 static void on_tree_select_row_enable_if_not_last_child(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
106 static void on_tree_select_row_enable_if_has_grandparent(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
108 static void on_tree_unselect_row_clear_text(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
109 static void on_tree_unselect_row_disable(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
110 static void on_tree_unselect_row_hide(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
112 static void on_attr_select_row(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
113 static void on_attr_unselect_row(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
114 static void on_attr_row_changed( GtkCList *list, gint row, gpointer data );
116 static void on_attr_select_row_enable(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
117 static void on_attr_unselect_row_disable(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
119 static void on_attr_select_row_set_name_content(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
120 static void on_attr_select_row_set_value_content(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
121 static void on_attr_unselect_row_clear_text(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
123 static void on_editable_changed_enable_if_valid_xml_name(GtkEditable *editable, gpointer data);
125 static void on_desktop_selection_changed(Inkscape::Selection *selection);
126 static void on_document_replaced(SPDesktop *dt, SPDocument *document);
127 static void on_document_uri_set(gchar const *uri, SPDocument *document);
129 static void on_clicked_get_editable_text(GtkWidget *widget, gpointer data);
131 static void _set_status_message(Inkscape::MessageType type, const gchar *message, GtkWidget *dialog);
133 static void cmd_new_element_node(GtkObject *object, gpointer data);
134 static void cmd_new_text_node(GtkObject *object, gpointer data);
135 static void cmd_duplicate_node(GtkObject *object, gpointer data);
136 static void cmd_delete_node(GtkObject *object, gpointer data);
138 static void cmd_raise_node(GtkObject *object, gpointer data);
139 static void cmd_lower_node(GtkObject *object, gpointer data);
140 static void cmd_indent_node(GtkObject *object, gpointer data);
141 static void cmd_unindent_node(GtkObject *object, gpointer data);
143 static void cmd_delete_attr(GtkObject *object, gpointer data);
144 static void cmd_set_attr(GtkObject *object, gpointer data);
146 static gboolean sp_xml_tree_key_press(GtkWidget *widget, GdkEventKey *event);
148 static bool in_dt_coordsys(SPObject const &item);
150 /*
151  * \brief Sets the XML status bar when the tree is selected.
152  */
153 void tree_reset_context()
155     _message_context->set(Inkscape::NORMAL_MESSAGE,
156                           _("<b>Click</b> to select nodes, <b>drag</b> to rearrange."));
160 /*
161  * \brief Sets the XML status bar, depending on which attr is selected.
162  */
163 void attr_reset_context(gint attr)
165     if (attr == 0) {
166         _message_context->set(Inkscape::NORMAL_MESSAGE,
167                               _("<b>Click</b> attribute to edit."));
168     }
169     else {
170         const gchar *name = g_quark_to_string(attr);
171         gchar *message = g_strdup_printf(_("Attribute <b>%s</b> selected. Press <b>Ctrl+Enter</b> when done editing to commit changes."), name);
172         _message_context->set(Inkscape::NORMAL_MESSAGE, message);
173         g_free(message);
174     }
178 void sp_xml_tree_dialog()
180     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
182     if (!desktop) {
183         return;
184     }
186     if (dlg == NULL)
187     { // very long block
189         GtkWidget *box, *sw, *paned, *toolbar, *button;
190         GtkWidget *text_container, *attr_container, *attr_subpaned_container, *box2;
191         GtkWidget *set_attr;
193         tooltips = gtk_tooltips_new();
194         gtk_tooltips_enable(tooltips);
196         dlg = sp_window_new("", TRUE);
197         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
198         if (x == -1000 || y == -1000) {
199             x = prefs->getInt(prefs_path + "x", -1000);
200             y = prefs->getInt(prefs_path + "y", -1000);
201         }
202         if (w ==0 || h == 0) {
203             w = prefs->getInt(prefs_path + "w", 0);
204             h = prefs->getInt(prefs_path + "h", 0);
205         }
207 //        if (x<0) x=0;
208 //        if (y<0) y=0;
210         if (w && h) {
211             gtk_window_resize((GtkWindow *) dlg, w, h);
212         }
213         if (x >= 0 && y >= 0 && (x < (gdk_screen_width()-MIN_ONSCREEN_DISTANCE)) && (y < (gdk_screen_height()-MIN_ONSCREEN_DISTANCE))) {
214             gtk_window_move((GtkWindow *) dlg, x, y);
215         } else {
216             gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
217         }
219         sp_transientize(dlg);
220         wd.win = dlg;
221         wd.stop = 0;
222         g_signal_connect  ( G_OBJECT(INKSCAPE), "activate_desktop", G_CALLBACK(sp_transientize_callback), &wd );
224         gtk_signal_connect( GTK_OBJECT(dlg), "event", GTK_SIGNAL_FUNC(sp_dialog_event_handler), dlg );
226         gtk_signal_connect( GTK_OBJECT(dlg), "destroy", G_CALLBACK(on_destroy), dlg);
227         gtk_signal_connect( GTK_OBJECT(dlg), "delete_event", G_CALLBACK(on_delete), dlg);
228         g_signal_connect  ( G_OBJECT(INKSCAPE), "shut_down", G_CALLBACK(on_delete), dlg);
230         g_signal_connect  ( G_OBJECT(INKSCAPE), "dialogs_hide", G_CALLBACK(sp_dialog_hide), dlg);
231         g_signal_connect  ( G_OBJECT(INKSCAPE), "dialogs_unhide", G_CALLBACK(sp_dialog_unhide), dlg);
234         gtk_container_set_border_width(GTK_CONTAINER(dlg), 0);
235         gtk_window_set_default_size(GTK_WINDOW(dlg), 640, 384);
237         GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
238         gtk_container_add(GTK_CONTAINER(dlg), vbox);
240         GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
241         gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
243         status = gtk_label_new(NULL);
244         gtk_misc_set_alignment(GTK_MISC(status), 0.0, 0.5);
245         gtk_widget_set_size_request(status, 1, -1);
246         gtk_label_set_markup(GTK_LABEL(status), "");
247         gtk_box_pack_start(GTK_BOX(hbox), gtk_hbox_new(FALSE, 0), FALSE, FALSE, 4);
248         gtk_box_pack_start(GTK_BOX(hbox), status, TRUE, TRUE, 0);
250         paned = gtk_hpaned_new();
251         gtk_paned_set_position(GTK_PANED(paned), 256);
252         gtk_box_pack_start(GTK_BOX(vbox), paned, TRUE, TRUE, 0);
254         _message_stack = new Inkscape::MessageStack();
255         _message_context = new Inkscape::MessageContext(_message_stack);
256         _message_changed_connection = _message_stack->connectChanged(
257             sigc::bind(sigc::ptr_fun(_set_status_message), dlg)
258         );
260         /* tree view */
262         box = gtk_vbox_new(FALSE, 0);
263         gtk_paned_pack1(GTK_PANED(paned), box, FALSE, FALSE);
265         tree = SP_XMLVIEW_TREE(sp_xmlview_tree_new(NULL, NULL, NULL));
266         gtk_tooltips_set_tip( tooltips, GTK_WIDGET(tree),
267                                _("Drag to reorder nodes"), NULL );
269         g_signal_connect( G_OBJECT(tree), "tree_select_row",
270                            G_CALLBACK(on_tree_select_row), NULL );
272         g_signal_connect( G_OBJECT(tree), "tree_unselect_row",
273                            G_CALLBACK(on_tree_unselect_row), NULL );
275         g_signal_connect_after( G_OBJECT(tree), "tree_move",
276                                  G_CALLBACK(after_tree_move), NULL);
278         /* TODO: replace gtk_signal_connect_while_alive() with something
279          * else...
280          */
281         toolbar = gtk_toolbar_new();
282         gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
283         gtk_container_set_border_width(GTK_CONTAINER(toolbar), 0);
285         button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
286                 NULL,
287                 _("New element node"),
288                 NULL,
289                 sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
290                                     INKSCAPE_ICON_XML_ELEMENT_NEW ),
291                 G_CALLBACK(cmd_new_element_node),
292                 NULL);
294         gtk_signal_connect_while_alive( GTK_OBJECT(tree),
295                         "tree_select_row",
296                         G_CALLBACK(on_tree_select_row_enable_if_element),
297                         button,
298                         GTK_OBJECT(button));
300         gtk_signal_connect_while_alive(GTK_OBJECT(tree),
301                         "tree_unselect_row",
302                         G_CALLBACK(on_tree_unselect_row_disable),
303                         button,
304                         GTK_OBJECT(button));
306         gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
308         button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
309                 NULL, _("New text node"), NULL,
310                 sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
311                              INKSCAPE_ICON_XML_TEXT_NEW ),
312                 G_CALLBACK(cmd_new_text_node),
313                 NULL);
315         gtk_signal_connect_while_alive(GTK_OBJECT(tree),
316                         "tree_select_row",
317                         G_CALLBACK(on_tree_select_row_enable_if_element),
318                         button,
319                         GTK_OBJECT(button));
321         gtk_signal_connect_while_alive(GTK_OBJECT(tree),
322                         "tree_unselect_row",
323                         G_CALLBACK(on_tree_unselect_row_disable),
324                         button,
325                         GTK_OBJECT(button));
327         gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
329         button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
330                 NULL, _("Duplicate node"), NULL,
331                 sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
332                              INKSCAPE_ICON_XML_NODE_DUPLICATE ),
333                 G_CALLBACK(cmd_duplicate_node),
334                 NULL);
336         gtk_signal_connect_while_alive(GTK_OBJECT(tree),
337                         "tree_select_row",
338                         G_CALLBACK(on_tree_select_row_enable_if_mutable),
339                         button,
340                         GTK_OBJECT(button));
342         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
343                         G_CALLBACK(on_tree_unselect_row_disable),
344                         button, GTK_OBJECT(button));
346         gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
348         gtk_toolbar_append_space(GTK_TOOLBAR(toolbar));
350         button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
351                 NULL, Q_("nodeAsInXMLdialogTooltip|Delete node"), NULL,
352                 sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
353                              INKSCAPE_ICON_XML_NODE_DELETE ),
354                                            G_CALLBACK(cmd_delete_node), NULL );
356         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
357                         G_CALLBACK(on_tree_select_row_enable_if_mutable),
358                         button, GTK_OBJECT(button));
359         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
360                         G_CALLBACK(on_tree_unselect_row_disable),
361                         button, GTK_OBJECT(button));
362         gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
364         gtk_toolbar_append_space(GTK_TOOLBAR(toolbar));
366         button = gtk_toolbar_append_item( GTK_TOOLBAR(toolbar), "<",
367                         _("Unindent node"), NULL,
368                         gtk_arrow_new(GTK_ARROW_LEFT, GTK_SHADOW_IN),
369                         G_CALLBACK(cmd_unindent_node), NULL);
371         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
372                     G_CALLBACK(on_tree_select_row_enable_if_has_grandparent),
373                     button, GTK_OBJECT(button));
375         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
376                         G_CALLBACK(on_tree_unselect_row_disable),
377                         button, GTK_OBJECT(button));
379         gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
381         button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), ">",
382                         _("Indent node"), NULL,
383                         gtk_arrow_new(GTK_ARROW_RIGHT, GTK_SHADOW_IN),
384                         G_CALLBACK(cmd_indent_node), NULL);
385         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
386                         G_CALLBACK(on_tree_select_row_enable_if_indentable),
387                         button, GTK_OBJECT(button));
388         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
389                        (GCallback) on_tree_unselect_row_disable,
390                         button, GTK_OBJECT(button));
391         gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
393         button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), "^",
394                         _("Raise node"), NULL,
395                         gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_IN),
396                         G_CALLBACK(cmd_raise_node), NULL);
397         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
398                     G_CALLBACK(on_tree_select_row_enable_if_not_first_child),
399                     button, GTK_OBJECT(button));
400         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
401                         G_CALLBACK(on_tree_unselect_row_disable),
402                         button, GTK_OBJECT(button));
403         gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
405         button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), "v",
406                         _("Lower node"), NULL,
407                         gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_IN),
408                         G_CALLBACK(cmd_lower_node), NULL);
409         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
410                         G_CALLBACK(on_tree_select_row_enable_if_not_last_child),
411                         button, GTK_OBJECT(button));
412         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
413                         G_CALLBACK(on_tree_unselect_row_disable),
414                         button, GTK_OBJECT(button));
415         gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
417         gtk_box_pack_start(GTK_BOX(box), toolbar, FALSE, TRUE, 0);
419         sw = gtk_scrolled_window_new(NULL, NULL);
420         gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(sw),
421                                          GTK_POLICY_AUTOMATIC,
422                                          GTK_POLICY_AUTOMATIC );
423         gtk_box_pack_start(GTK_BOX(box), sw, TRUE, TRUE, 0);
425         gtk_container_add(GTK_CONTAINER(sw), GTK_WIDGET(tree));
427         /* node view */
429         box = gtk_vbox_new(FALSE, 0);
430         gtk_paned_pack2(GTK_PANED(paned), box, TRUE, TRUE);
432         /* attributes */
434         attr_container = gtk_vbox_new(FALSE, 0);
435         gtk_box_pack_start( GTK_BOX(box), GTK_WIDGET(attr_container),
436                              TRUE, TRUE, 0 );
438         attributes = SP_XMLVIEW_ATTR_LIST(sp_xmlview_attr_list_new(NULL));
439         g_signal_connect( G_OBJECT(attributes), "select_row",
440                            G_CALLBACK(on_attr_select_row), NULL);
441         g_signal_connect( G_OBJECT(attributes), "unselect_row",
442                            G_CALLBACK(on_attr_unselect_row), NULL);
443         g_signal_connect( G_OBJECT(attributes), "row-value-changed",
444                            G_CALLBACK(on_attr_row_changed), NULL);
446         toolbar = gtk_toolbar_new();
447         gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
448         gtk_container_set_border_width(GTK_CONTAINER(toolbar), 0);
450         button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
451                 NULL, _("Delete attribute"), NULL,
452                 sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
453                              INKSCAPE_ICON_XML_ATTRIBUTE_DELETE ),
454                (GCallback) cmd_delete_attr, NULL);
456         gtk_signal_connect_while_alive(GTK_OBJECT(attributes), "select_row",
457                        (GCallback) on_attr_select_row_enable, button,
458                         GTK_OBJECT(button));
460         gtk_signal_connect_while_alive(GTK_OBJECT(attributes), "unselect_row",
461                        (GCallback) on_attr_unselect_row_disable, button,
462                         GTK_OBJECT(button));
464         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
465                        (GCallback) on_tree_unselect_row_disable, button,
466                         GTK_OBJECT(button));
468         gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
470         gtk_box_pack_start( GTK_BOX(attr_container),
471                              GTK_WIDGET(toolbar), FALSE, TRUE, 0 );
473         attr_subpaned_container = gtk_vpaned_new();
474         gtk_box_pack_start( GTK_BOX(attr_container),
475                              GTK_WIDGET(attr_subpaned_container),
476                              TRUE, TRUE, 0 );
477         gtk_widget_show(attr_subpaned_container);
479         sw = gtk_scrolled_window_new(NULL, NULL);
480         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
481                 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
482         gtk_paned_pack1( GTK_PANED(attr_subpaned_container),
483                           GTK_WIDGET(sw), TRUE, TRUE );
484         gtk_container_add(GTK_CONTAINER(sw), GTK_WIDGET(attributes));
486         toolbar = gtk_vbox_new(FALSE, 4);
487         gtk_container_set_border_width(GTK_CONTAINER(toolbar), 4);
489         box2 = gtk_hbox_new(FALSE, 4);
490         gtk_box_pack_start( GTK_BOX(toolbar), GTK_WIDGET(box2),
491                              FALSE, TRUE, 0);
493         attr_name = GTK_EDITABLE(gtk_entry_new());
494         gtk_tooltips_set_tip( tooltips, GTK_WIDGET(attr_name),
495                                // TRANSLATORS: "Attribute" is a noun here
496                                _("Attribute name"), NULL );
498         gtk_signal_connect( GTK_OBJECT(attributes), "select_row",
499                             (GCallback) on_attr_select_row_set_name_content,
500                              attr_name);
502         gtk_signal_connect( GTK_OBJECT(attributes), "unselect_row",
503                             (GCallback) on_attr_unselect_row_clear_text,
504                              attr_name);
506         gtk_signal_connect( GTK_OBJECT(tree), "tree_unselect_row",
507                             (GCallback) on_tree_unselect_row_clear_text,
508                              attr_name);
510         gtk_box_pack_start( GTK_BOX(box2), GTK_WIDGET(attr_name),
511                              TRUE, TRUE, 0);
513         set_attr = gtk_button_new();
514         gtk_tooltips_set_tip( tooltips, GTK_WIDGET(set_attr),
515                                // TRANSLATORS: "Set" is a verb here
516                                _("Set attribute"), NULL );
517         // TRANSLATORS: "Set" is a verb here
518         GtkWidget *set_label = gtk_label_new(_("Set"));
519         gtk_container_add(GTK_CONTAINER(set_attr), set_label);
521         gtk_signal_connect( GTK_OBJECT(set_attr), "clicked",
522                             (GCallback) cmd_set_attr, NULL);
523         gtk_signal_connect( GTK_OBJECT(attr_name), "changed",
524                    (GCallback) on_editable_changed_enable_if_valid_xml_name,
525                     set_attr );
526         gtk_widget_set_sensitive(GTK_WIDGET(set_attr), FALSE);
528         gtk_box_pack_start(GTK_BOX(box2), set_attr, FALSE, FALSE, 0);
530         sw = gtk_scrolled_window_new(NULL, NULL);
531         gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(sw),
532                                          GTK_POLICY_AUTOMATIC,
533                                          GTK_POLICY_AUTOMATIC );
534         gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN );
535         gtk_box_pack_start(GTK_BOX(toolbar), sw, TRUE, TRUE, 0);
537         attr_value =(GtkTextView *) gtk_text_view_new();
538         gtk_text_view_set_wrap_mode((GtkTextView *) attr_value, GTK_WRAP_CHAR);
539         gtk_tooltips_set_tip( tooltips, GTK_WIDGET(attr_value),
540                                // TRANSLATORS: "Attribute" is a noun here
541                                _("Attribute value"), NULL );
542         gtk_signal_connect( GTK_OBJECT(attributes), "select_row",
543                             (GCallback) on_attr_select_row_set_value_content,
544                              attr_value );
545         gtk_signal_connect( GTK_OBJECT(attributes), "unselect_row",
546                             (GCallback) on_attr_unselect_row_clear_text,
547                              attr_value );
548         gtk_signal_connect( GTK_OBJECT(tree), "tree_unselect_row",
549                             (GCallback) on_tree_unselect_row_clear_text,
550                              attr_value );
551         gtk_text_view_set_editable(attr_value, TRUE);
552         gtk_container_add( GTK_CONTAINER(sw),
553                             GTK_WIDGET(attr_value) );
555         gtk_paned_pack2( GTK_PANED(attr_subpaned_container),
556                           GTK_WIDGET(toolbar), FALSE, TRUE );
558         /* text */
560         sw = gtk_scrolled_window_new(NULL, NULL);
561         gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(sw),
562                                          GTK_POLICY_AUTOMATIC,
563                                          GTK_POLICY_AUTOMATIC );
564         gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN );
565         gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(sw), TRUE, TRUE, 0);
567         content = SP_XMLVIEW_CONTENT(sp_xmlview_content_new(NULL));
568         gtk_container_add(GTK_CONTAINER(sw), GTK_WIDGET(content));
570         text_container = sw;
572         /* initial show/hide */
574         gtk_widget_show_all(GTK_WIDGET(dlg));
576         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
577                        (GCallback) on_tree_select_row_show_if_element,
578                         attr_container, GTK_OBJECT(attr_container));
580         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
581                        (GCallback) on_tree_unselect_row_hide,
582                         attr_container, GTK_OBJECT(attr_container));
584         gtk_widget_hide(attr_container);
586         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
587                        (GCallback) on_tree_select_row_show_if_text,
588                         text_container, GTK_OBJECT(text_container));
590         gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
591                        (GCallback) on_tree_unselect_row_hide,
592                         text_container, GTK_OBJECT(text_container));
594         gtk_widget_hide(text_container);
596         g_signal_connect( G_OBJECT(INKSCAPE), "activate_desktop",
597                            G_CALLBACK(sp_xmltree_desktop_activate), dlg);
599         g_signal_connect( G_OBJECT(INKSCAPE), "deactivate_desktop",
600                            G_CALLBACK(sp_xmltree_desktop_deactivate), dlg);
602         g_signal_connect((GObject *) dlg, "key_press_event", (GCallback) sp_xml_tree_key_press, NULL);
604         tree_reset_context();
605     } // end of if (dlg == NULL)
607     gtk_window_present((GtkWindow *) dlg);
609     g_assert(desktop != NULL);
610     set_tree_desktop(desktop);
612 } // end of sp_xml_tree_dialog()
614 static gboolean sp_xml_tree_key_press(GtkWidget */*widget*/, GdkEventKey *event)
617     unsigned int shortcut = get_group0_keyval(event) |
618         ( event->state & GDK_SHIFT_MASK ?
619           SP_SHORTCUT_SHIFT_MASK : 0 ) |
620         ( event->state & GDK_CONTROL_MASK ?
621           SP_SHORTCUT_CONTROL_MASK : 0 ) |
622         ( event->state & GDK_MOD1_MASK ?
623           SP_SHORTCUT_ALT_MASK : 0 );
625     /* fixme: if you need to add more xml-tree-specific callbacks, you should probably upgrade
626      * the sp_shortcut mechanism to take into account windows. */
627     if (shortcut == (SP_SHORTCUT_CONTROL_MASK | GDK_Return)) {
628         cmd_set_attr(NULL, NULL);
629         return true;
630     }
631     return false;
635 static void sp_xmltree_desktop_activate(Inkscape::Application */*inkscape*/,
636                                       SPDesktop *desktop,
637                                       GtkWidget */*dialog*/ )
639     set_tree_desktop(desktop);
642 static void sp_xmltree_desktop_deactivate(Inkscape::Application */*inkscape*/,
643                                       SPDesktop */*desktop*/,
644                                       GtkWidget */*dialog*/ )
646     set_tree_desktop(NULL);
650 void set_tree_desktop(SPDesktop *desktop)
652     if ( desktop == current_desktop ) {
653         return;
654     }
656     if (current_desktop) {
657         sel_changed_connection.disconnect();
658         document_replaced_connection.disconnect();
659     }
660     current_desktop = desktop;
661     if (desktop) {
662         sel_changed_connection = sp_desktop_selection(desktop)->connectChanged(&on_desktop_selection_changed);
663         document_replaced_connection = desktop->connectDocumentReplaced(&on_document_replaced);
664         set_tree_document(sp_desktop_document(desktop));
665     } else {
666         set_tree_document(NULL);
667     }
669 } // end of set_tree_desktop()
673 void set_tree_document(SPDocument *document)
675     if (document == current_document) {
676         return;
677     }
679     if (current_document) {
680         document_uri_set_connection.disconnect();
681     }
682     current_document = document;
683     if (current_document) {
685         document_uri_set_connection = current_document->connectURISet(sigc::bind(sigc::ptr_fun(&on_document_uri_set), current_document));
686         on_document_uri_set( current_document->getURI(), current_document );
687         set_tree_repr(current_document->getReprRoot());
688     } else {
689         set_tree_repr(NULL);
690     }
695 void set_tree_repr(Inkscape::XML::Node *repr)
697     if (repr == selected_repr) {
698         return;
699     }
701     gtk_clist_freeze(GTK_CLIST(tree));
703     sp_xmlview_tree_set_repr(tree, repr);
705     if (repr) {
706         set_tree_select(get_dt_select());
707     } else {
708         set_tree_select(NULL);
709     }
711     gtk_clist_thaw(GTK_CLIST(tree));
713     propagate_tree_select(selected_repr);
719 void set_tree_select(Inkscape::XML::Node *repr)
721     if (selected_repr) {
722         Inkscape::GC::release(selected_repr);
723     }
725     selected_repr = repr;
726     if (repr) {
727         GtkCTreeNode *node;
729         Inkscape::GC::anchor(selected_repr);
731         node = sp_xmlview_tree_get_repr_node(SP_XMLVIEW_TREE(tree), repr);
732         if (node) {
733             GtkCTreeNode *parent;
735             gtk_ctree_select(GTK_CTREE(tree), node);
737             parent = GTK_CTREE_ROW(node)->parent;
738             while (parent) {
739                 gtk_ctree_expand(GTK_CTREE(tree), parent);
740                 parent = GTK_CTREE_ROW(parent)->parent;
741             }
743             gtk_ctree_node_moveto(GTK_CTREE(tree), node, 0, 0.66, 0.0);
744         }
745     } else {
746         gtk_clist_unselect_all(GTK_CLIST(tree));
747     }
748     propagate_tree_select(repr);
753 void propagate_tree_select(Inkscape::XML::Node *repr)
755     if (repr && repr->type() == Inkscape::XML::ELEMENT_NODE) {
756         sp_xmlview_attr_list_set_repr(attributes, repr);
757     } else {
758         sp_xmlview_attr_list_set_repr(attributes, NULL);
759     }
761     if (repr && ( repr->type() == Inkscape::XML::TEXT_NODE || repr->type() == Inkscape::XML::COMMENT_NODE || repr->type() == Inkscape::XML::PI_NODE ) ) {
762         sp_xmlview_content_set_repr(content, repr);
763     } else {
764         sp_xmlview_content_set_repr(content, NULL);
765     }
769 Inkscape::XML::Node *get_dt_select()
771     if (!current_desktop) {
772         return NULL;
773     }
775     return sp_desktop_selection(current_desktop)->singleRepr();
780 void set_dt_select(Inkscape::XML::Node *repr)
782     if (!current_desktop) {
783         return;
784     }
786     Inkscape::Selection *selection = sp_desktop_selection(current_desktop);
788     SPObject *object;
789     if (repr) {
790         while ( ( repr->type() != Inkscape::XML::ELEMENT_NODE )
791                 && sp_repr_parent(repr) )
792         {
793             repr = sp_repr_parent(repr);
794         } // end of while loop
796         object = sp_desktop_document(current_desktop)->getObjectByRepr(repr);
797     } else {
798         object = NULL;
799     }
801     blocked++;
802     if ( object && in_dt_coordsys(*object)
803          && !(SP_IS_STRING(object) ||
804                 SP_IS_ROOT(object)     ) )
805     {
806             /* We cannot set selection to root or string - they are not items and selection is not
807              * equipped to deal with them */
808             selection->set(SP_ITEM(object));
809     }
810     blocked--;
812 } // end of set_dt_select()
815 void on_tree_select_row(GtkCTree *tree,
816                         GtkCTreeNode *node,
817                         gint /*column*/,
818                         gpointer /*data*/)
820     if (blocked) {
821         return;
822     }
824     Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
825     g_assert(repr != NULL);
827     if (selected_repr == repr) {
828         return;
829     }
831     if (selected_repr) {
832         Inkscape::GC::release(selected_repr);
833         selected_repr = NULL;
834     }
835     selected_repr = repr;
836     Inkscape::GC::anchor(selected_repr);
838     propagate_tree_select(selected_repr);
840     set_dt_select(selected_repr);
842     tree_reset_context();
845 void on_tree_unselect_row(GtkCTree *tree,
846                           GtkCTreeNode *node,
847                           gint /*column*/,
848                           gpointer /*data*/)
850     if (blocked) {
851         return;
852     }
854     Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
855     propagate_tree_select(NULL);
856     set_dt_select(NULL);
858     if (selected_repr && (selected_repr == repr)) {
859         Inkscape::GC::release(selected_repr);
860         selected_repr = NULL;
861         selected_attr = 0;
862     }
867 void after_tree_move(GtkCTree */*tree*/,
868                      GtkCTreeNode *node,
869                      GtkCTreeNode *new_parent,
870                      GtkCTreeNode *new_sibling,
871                      gpointer /*data*/)
873     if (GTK_CTREE_ROW(node)->parent  == new_parent &&
874         GTK_CTREE_ROW(node)->sibling == new_sibling)
875     {
876         DocumentUndo::done(current_document, SP_VERB_DIALOG_XML_EDITOR,
877                            _("Drag XML subtree"));
878     } else {
879         DocumentUndo::cancel(current_document);
880     }
884 static void on_destroy(GtkObject */*object*/, gpointer /*data*/)
886     set_tree_desktop(NULL);
887     gtk_object_destroy(GTK_OBJECT(tooltips));
888     tooltips = NULL;
889     sp_signal_disconnect_by_data(INKSCAPE, dlg);
890     wd.win = dlg = NULL;
891     wd.stop = 0;
893     _message_changed_connection.disconnect();
894     delete _message_context;
895     _message_context = NULL;
896     Inkscape::GC::release(_message_stack);
897     _message_stack = NULL;
898     _message_changed_connection.~connection();
900     status = NULL;
905 static gboolean on_delete(GtkObject */*object*/, GdkEvent */*event*/, gpointer /*data*/)
907     gtk_window_get_position((GtkWindow *) dlg, &x, &y);
908     gtk_window_get_size((GtkWindow *) dlg, &w, &h);
910     if (x<0) x=0;
911     if (y<0) y=0;
913     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
914     prefs->setInt(prefs_path + "x", x);
915     prefs->setInt(prefs_path + "y", y);
916     prefs->setInt(prefs_path + "w", w);
917     prefs->setInt(prefs_path + "h", h);
919     return FALSE; // which means, go ahead and destroy it
923 static void _set_status_message(Inkscape::MessageType /*type*/, const gchar *message, GtkWidget */*dialog*/)
925     if (status) {
926         gtk_label_set_markup(GTK_LABEL(status), message ? message : "");
927     }
931 void on_tree_select_row_enable(GtkCTree */*tree*/,
932                                GtkCTreeNode */*node*/,
933                                gint /*column*/,
934                                gpointer data)
936     gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
941 void on_tree_select_row_enable_if_element(GtkCTree *tree,
942                                           GtkCTreeNode *node,
943                                           gint /*column*/,
944                                           gpointer data )
946     Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
948     if (repr->type() == Inkscape::XML::ELEMENT_NODE) {
949         gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
950     } else {
951         gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
952     }
957 void on_tree_select_row_show_if_element(GtkCTree *tree, GtkCTreeNode *node,
958                                         gint /*column*/, gpointer data)
960     Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
962     if (repr->type() == Inkscape::XML::ELEMENT_NODE) {
963         gtk_widget_show(GTK_WIDGET(data));
964     } else {
965         gtk_widget_hide(GTK_WIDGET(data));
966     }
971 void on_tree_select_row_show_if_text(GtkCTree *tree, GtkCTreeNode *node,
972                                      gint /*column*/, gpointer data)
974     Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
976     if ( repr->type() == Inkscape::XML::TEXT_NODE || repr->type() == Inkscape::XML::COMMENT_NODE || repr->type() == Inkscape::XML::PI_NODE ) {
977         gtk_widget_show(GTK_WIDGET(data));
978     } else {
979         gtk_widget_hide(GTK_WIDGET(data));
980     }
984 gboolean xml_tree_node_mutable(GtkCTreeNode *node)
986     // top-level is immutable, obviously
987     if (!GTK_CTREE_ROW(node)->parent) {
988         return false;
989     }
991     // if not in base level (where namedview, defs, etc go), we're mutable
992     if (GTK_CTREE_ROW(GTK_CTREE_ROW(node)->parent)->parent) {
993         return true;
994     }
996     Inkscape::XML::Node *repr;
997     repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
998     g_assert(repr);
1000     // don't let "defs" or "namedview" disappear
1001     if ( !strcmp(repr->name(),"svg:defs") ||
1002          !strcmp(repr->name(),"sodipodi:namedview") ) {
1003         return false;
1004     }
1006     // everyone else is okay, I guess.  :)
1007     return true;
1011 void on_tree_select_row_enable_if_mutable(GtkCTree */*tree*/, GtkCTreeNode *node,
1012                                           gint /*column*/, gpointer data)
1014     gtk_widget_set_sensitive(GTK_WIDGET(data), xml_tree_node_mutable(node));
1019 void on_tree_unselect_row_disable(GtkCTree */*tree*/, GtkCTreeNode */*node*/,
1020                                   gint /*column*/, gpointer data)
1022     gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1027 void on_tree_unselect_row_hide(GtkCTree */*tree*/, GtkCTreeNode */*node*/,
1028                                gint /*column*/, gpointer data)
1030     gtk_widget_hide(GTK_WIDGET(data));
1035 void on_tree_unselect_row_clear_text(GtkCTree */*tree*/, GtkCTreeNode */*node*/,
1036                                      gint /*column*/, gpointer data)
1038     if (GTK_IS_EDITABLE(data)) {
1039         gtk_editable_delete_text(GTK_EDITABLE(data), 0, -1);
1040     } else if (GTK_IS_TEXT_VIEW(data)) {
1041         GtkTextBuffer *tb;
1042         tb = gtk_text_view_get_buffer(GTK_TEXT_VIEW(data));
1043         gtk_text_buffer_set_text(tb, "", 0);
1044     }
1048 void on_attr_select_row(GtkCList *list, gint row, gint /*column*/,
1049                         GdkEventButton */*event*/, gpointer /*data*/)
1051     selected_attr = sp_xmlview_attr_list_get_row_key(list, row);
1052     gtk_window_set_focus(GTK_WINDOW(dlg), GTK_WIDGET(attr_value));
1054     attr_reset_context(selected_attr);
1058 void on_attr_unselect_row(GtkCList */*list*/, gint /*row*/, gint /*column*/,
1059                           GdkEventButton */*event*/, gpointer /*data*/)
1061     selected_attr = 0;
1062     attr_reset_context(selected_attr);
1066 void on_attr_row_changed(GtkCList *list, gint row, gpointer /*data*/)
1068     gint attr = sp_xmlview_attr_list_get_row_key(list, row);
1070     if (attr == selected_attr) {
1071         /* if the attr changed, reselect the row in the list to sync
1072            the edit box */
1074         /*
1075         // get current attr values
1076         const gchar * name = g_quark_to_string (sp_xmlview_attr_list_get_row_key (list, row));
1077         const gchar * value = selected_repr->attribute(name);
1079         g_warning("value: '%s'",value);
1081         // get the edit box value
1082         GtkTextIter start, end;
1083         gtk_text_buffer_get_bounds ( gtk_text_view_get_buffer (attr_value),
1084                                      &start, &end );
1085         gchar * text = gtk_text_buffer_get_text ( gtk_text_view_get_buffer (attr_value),
1086                                        &start, &end, TRUE );
1087         g_warning("text: '%s'",text);
1089         // compare to edit box
1090         if (strcmp(text,value)) {
1091             // issue warning if they're different
1092             _message_stack->flash(Inkscape::WARNING_MESSAGE,
1093                                   _("Attribute changed in GUI while editing values!"));
1094         }
1095         g_free (text);
1097         */
1098         gtk_clist_unselect_row( GTK_CLIST(list), row, 0 );
1099         gtk_clist_select_row( GTK_CLIST(list), row, 0 );
1100     }
1104 void on_attr_select_row_set_name_content(GtkCList *list, gint row,
1105                                          gint /*column*/, GdkEventButton */*event*/,
1106                                          gpointer data)
1108     GtkEditable *editable = GTK_EDITABLE(data);
1109     const gchar *name = g_quark_to_string(sp_xmlview_attr_list_get_row_key(list, row));
1110     gtk_editable_delete_text(editable, 0, -1);
1111     gint pos = 0;
1112     gtk_editable_insert_text(editable, name, strlen(name), &pos);
1117 void on_attr_select_row_set_value_content(GtkCList *list, gint row, gint /*column*/,
1118                                           GdkEventButton */*event*/,
1119                                           gpointer data)
1121     GtkTextBuffer *tb = gtk_text_view_get_buffer(GTK_TEXT_VIEW(data));
1122     const gchar *name = g_quark_to_string(sp_xmlview_attr_list_get_row_key(list, row));
1123     const gchar *value = selected_repr->attribute(name);
1124     if (!value) {
1125         value = "";
1126     }
1127     gtk_text_buffer_set_text(tb, value, strlen(value));
1131 void on_tree_select_row_enable_if_indentable(GtkCTree *tree, GtkCTreeNode *node,
1132                                              gint /*column*/, gpointer data)
1134     gboolean indentable = FALSE;
1136     if (xml_tree_node_mutable(node)) {
1137         Inkscape::XML::Node *repr, *prev;
1138         repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
1140         Inkscape::XML::Node *parent=repr->parent();
1141         if ( parent && repr != parent->firstChild() ) {
1142             g_assert(parent->firstChild());
1144             // skip to the child just before the current repr
1145             for ( prev = parent->firstChild() ;
1146                   prev && prev->next() != repr ;
1147                   prev = prev->next() ){};
1149             if (prev && prev->type() == Inkscape::XML::ELEMENT_NODE) {
1150                 indentable = TRUE;
1151             }
1152         }
1153     }
1155     gtk_widget_set_sensitive(GTK_WIDGET(data), indentable);
1160 void on_tree_select_row_enable_if_not_first_child(GtkCTree *tree,
1161                                                   GtkCTreeNode *node,
1162                                                   gint /*column*/,
1163                                                   gpointer data)
1165     Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
1167     Inkscape::XML::Node *parent=repr->parent();
1168     if ( parent && repr != parent->firstChild() ) {
1169         gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
1170     } else {
1171         gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1172     }
1177 void on_tree_select_row_enable_if_not_last_child(GtkCTree *tree,
1178                                                  GtkCTreeNode *node,
1179                                                  gint /*column*/, gpointer data)
1181     Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
1183     Inkscape::XML::Node *parent=repr->parent();
1184     if ( parent && parent->parent() && repr->next() ) {
1185         gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
1186     } else {
1187         gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1188     }
1193 void on_tree_select_row_enable_if_has_grandparent(GtkCTree */*tree*/,
1194                                                   GtkCTreeNode *node,
1195                                                   gint /*column*/, gpointer data)
1197     GtkCTreeNode *parent = GTK_CTREE_ROW(node)->parent;
1199     if (parent) {
1200         GtkCTreeNode *grandparent = GTK_CTREE_ROW(parent)->parent;
1201         if (grandparent) {
1202             gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
1203         } else {
1204             gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1205         }
1206     } else {
1207         gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1208     }
1213 void on_attr_select_row_enable(GtkCList */*list*/, gint /*row*/, gint /*column*/,
1214                                GdkEventButton */*event*/, gpointer data)
1216     gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
1221 void on_attr_unselect_row_disable(GtkCList */*list*/, gint /*row*/, gint /*column*/,
1222                                   GdkEventButton */*event*/, gpointer data)
1224     gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1229 void on_attr_unselect_row_clear_text(GtkCList */*list*/, gint /*row*/, gint /*column*/,
1230                                      GdkEventButton */*event*/, gpointer data)
1232     if (GTK_IS_EDITABLE(data)) {
1233         gtk_editable_delete_text(GTK_EDITABLE(data), 0, -1);
1234     } else if (GTK_IS_TEXT_VIEW(data)) {
1235         GtkTextBuffer *tb;
1236         tb = gtk_text_view_get_buffer(GTK_TEXT_VIEW(data));
1237         gtk_text_buffer_set_text(tb, "", 0);
1238     }
1243 void on_editable_changed_enable_if_valid_xml_name(GtkEditable *editable,
1244                                                   gpointer data)
1246     gchar *text = gtk_editable_get_chars(editable, 0, -1);
1248     /* TODO: need to do checking a little more rigorous than this */
1250     if (strlen(text)) {
1251         gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
1252     } else {
1253         gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1254     }
1255     g_free(text);
1260 void on_desktop_selection_changed(Inkscape::Selection */*selection*/)
1262     if (!blocked++) {
1263         set_tree_select(get_dt_select());
1264     }
1265     blocked--;
1268 static void on_document_replaced(SPDesktop *dt, SPDocument *doc)
1270     if (current_desktop)
1271         sel_changed_connection.disconnect();
1273     sel_changed_connection = sp_desktop_selection(dt)->connectChanged(&on_desktop_selection_changed);
1274     set_tree_document(doc);
1277 void on_document_uri_set(gchar const */*uri*/, SPDocument *document)
1279     gchar title[500];
1280     sp_ui_dialog_title_string(Inkscape::Verb::get(SP_VERB_DIALOG_XML_EDITOR), title);
1281     gchar *t = g_strdup_printf("%s: %s", document->getName(), title);
1282     gtk_window_set_title(GTK_WINDOW(dlg), t);
1283     g_free(t);
1288 void on_clicked_get_editable_text(GtkWidget */*widget*/, gpointer data)
1290     EditableDest *dest = (EditableDest *) data;
1291     dest->text = gtk_editable_get_chars(dest->editable, 0, -1);
1294 gboolean
1295 quit_on_esc (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
1297     switch (get_group0_keyval (event)) {
1298         case GDK_Escape: // defocus
1299             gtk_widget_destroy(w);
1300             return TRUE;
1301     }
1302     return FALSE;
1305 void cmd_new_element_node(GtkObject */*object*/, gpointer /*data*/)
1307     EditableDest name;
1308     GtkWidget *window, *create, *cancel, *vbox, *entry, *bbox, *sep;
1310     g_assert(selected_repr != NULL);
1312     window = sp_window_new(NULL, TRUE);
1313     gtk_container_set_border_width(GTK_CONTAINER(window), 4);
1314     gtk_window_set_title(GTK_WINDOW(window), _("New element node..."));
1315     gtk_window_set_policy(GTK_WINDOW(window), FALSE, FALSE, TRUE);
1316     gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
1317     gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(dlg));
1318     gtk_window_set_modal(GTK_WINDOW(window), TRUE);
1319     gtk_signal_connect(GTK_OBJECT(window), "destroy", gtk_main_quit, NULL);
1320     gtk_signal_connect(GTK_OBJECT(window), "key-press-event", G_CALLBACK(quit_on_esc), window);
1322     vbox = gtk_vbox_new(FALSE, 4);
1323     gtk_container_add(GTK_CONTAINER(window), vbox);
1325     entry = gtk_entry_new();
1326     gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, TRUE, 0);
1328     sep = gtk_hseparator_new();
1329     gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, TRUE, 0);
1331     bbox = gtk_hbutton_box_new();
1332     gtk_container_set_border_width(GTK_CONTAINER(bbox), 4);
1333     gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
1334     gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, TRUE, 0);
1336     cancel = gtk_button_new_with_label(_("Cancel"));
1337     GTK_WIDGET_SET_FLAGS( GTK_WIDGET(cancel),
1338                            GTK_CAN_DEFAULT );
1339     gtk_signal_connect_object( GTK_OBJECT(cancel), "clicked",
1340                                 G_CALLBACK(gtk_widget_destroy),
1341                                 GTK_OBJECT(window) );
1342     gtk_container_add(GTK_CONTAINER(bbox), cancel);
1344     create = gtk_button_new_with_label(_("Create"));
1345     gtk_widget_set_sensitive(GTK_WIDGET(create), FALSE);
1346     gtk_signal_connect( GTK_OBJECT(entry), "changed",
1347                     G_CALLBACK(on_editable_changed_enable_if_valid_xml_name),
1348                     create );
1349     gtk_signal_connect( GTK_OBJECT(create), "clicked",
1350                          G_CALLBACK(on_clicked_get_editable_text), &name );
1351     gtk_signal_connect_object( GTK_OBJECT(create), "clicked",
1352                                 G_CALLBACK(gtk_widget_destroy),
1353                                 GTK_OBJECT(window) );
1354     GTK_WIDGET_SET_FLAGS( GTK_WIDGET(create),
1355                            GTK_CAN_DEFAULT | GTK_RECEIVES_DEFAULT );
1356     gtk_container_add(GTK_CONTAINER(bbox), create);
1358     gtk_widget_show_all(GTK_WIDGET(window));
1359     gtk_window_set_default(GTK_WINDOW(window), GTK_WIDGET(create));
1360     gtk_window_set_focus(GTK_WINDOW(window), GTK_WIDGET(entry));
1362     name.editable = GTK_EDITABLE(entry);
1363     name.text = NULL;
1365     gtk_main();
1367     if (selected_repr != NULL && name.text) {
1368         Inkscape::XML::Document *xml_doc = current_document->getReprDoc();
1369         Inkscape::XML::Node *new_repr;
1370         new_repr = xml_doc->createElement(name.text);
1371         Inkscape::GC::release(new_repr);
1372         g_free(name.text);
1373         selected_repr->appendChild(new_repr);
1374         set_tree_select(new_repr);
1375         set_dt_select(new_repr);
1377         DocumentUndo::done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1378                            _("Create new element node"));
1379     }
1381 } // end of cmd_new_element_node()
1385 void cmd_new_text_node(GtkObject */*object*/, gpointer /*data*/)
1387     g_assert(selected_repr != NULL);
1389     Inkscape::XML::Document *xml_doc = current_document->getReprDoc();
1390     Inkscape::XML::Node *text = xml_doc->createTextNode("");
1391     selected_repr->appendChild(text);
1393     DocumentUndo::done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1394                        _("Create new text node"));
1396     set_tree_select(text);
1397     set_dt_select(text);
1399     gtk_window_set_focus(GTK_WINDOW(dlg), GTK_WIDGET(content));
1403 void cmd_duplicate_node(GtkObject */*object*/, gpointer /*data*/)
1405     g_assert(selected_repr != NULL);
1407     Inkscape::XML::Node *parent = sp_repr_parent(selected_repr);
1408     Inkscape::XML::Node *dup = selected_repr->duplicate(parent->document());
1409     parent->addChild(dup, selected_repr);
1411     DocumentUndo::done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1412                        _("Duplicate node"));
1414     GtkCTreeNode *node = sp_xmlview_tree_get_repr_node(SP_XMLVIEW_TREE(tree), dup);
1416     if (node) {
1417         gtk_ctree_select(GTK_CTREE(tree), node);
1418     }
1423 void cmd_delete_node(GtkObject */*object*/, gpointer /*data*/)
1425     g_assert(selected_repr != NULL);
1426     sp_repr_unparent(selected_repr);
1428     DocumentUndo::done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1429                        Q_("nodeAsInXMLinHistoryDialog|Delete node"));
1434 void cmd_delete_attr(GtkObject */*object*/, gpointer /*data*/)
1436     g_assert(selected_repr != NULL);
1437     g_assert(selected_attr != 0);
1438     selected_repr->setAttribute(g_quark_to_string(selected_attr), NULL);
1440     SPObject *updated=current_document->getObjectByRepr(selected_repr);
1441     if (updated) {
1442         // force immediate update of dependant attributes
1443         updated->updateRepr();
1444     }
1446     DocumentUndo::done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1447                        _("Delete attribute"));
1452 void cmd_set_attr(GtkObject */*object*/, gpointer /*data*/)
1454     g_assert(selected_repr != NULL);
1456     gchar *name = gtk_editable_get_chars(attr_name, 0, -1);
1457     GtkTextIter start;
1458     GtkTextIter end;
1459     gtk_text_buffer_get_bounds( gtk_text_view_get_buffer(attr_value),
1460                                  &start, &end );
1461     gchar *value = gtk_text_buffer_get_text( gtk_text_view_get_buffer(attr_value),
1462                                        &start, &end, TRUE );
1464     selected_repr->setAttribute(name, value, false);
1466     g_free(name);
1467     g_free(value);
1469     SPObject *updated = current_document->getObjectByRepr(selected_repr);
1470     if (updated) {
1471         // force immediate update of dependant attributes
1472         updated->updateRepr();
1473     }
1475     DocumentUndo::done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1476                        _("Change attribute"));
1478     /* TODO: actually, the row won't have been created yet.  why? */
1479     gint row = sp_xmlview_attr_list_find_row_from_key(GTK_CLIST(attributes),
1480                                                       g_quark_from_string(name));
1481     if (row != -1) {
1482         gtk_clist_select_row(GTK_CLIST(attributes), row, 0);
1483     }
1488 void cmd_raise_node(GtkObject */*object*/, gpointer /*data*/)
1490     g_assert(selected_repr != NULL);
1492     Inkscape::XML::Node *parent = sp_repr_parent(selected_repr);
1493     g_return_if_fail(parent != NULL);
1494     g_return_if_fail(parent->firstChild() != selected_repr);
1496     Inkscape::XML::Node *ref = NULL;
1497     Inkscape::XML::Node *before = parent->firstChild();
1498     while (before && before->next() != selected_repr) {
1499         ref = before;
1500         before = before->next();
1501     }
1503     parent->changeOrder(selected_repr, ref);
1505     DocumentUndo::done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1506                        _("Raise node"));
1508     set_tree_select(selected_repr);
1509     set_dt_select(selected_repr);
1514 void cmd_lower_node(GtkObject */*object*/, gpointer /*data*/)
1516     g_assert(selected_repr != NULL);
1517     g_return_if_fail(selected_repr->next() != NULL);
1518     Inkscape::XML::Node *parent = sp_repr_parent(selected_repr);
1520     parent->changeOrder(selected_repr, selected_repr->next());
1522     DocumentUndo::done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1523                        _("Lower node"));
1525     set_tree_select(selected_repr);
1526     set_dt_select(selected_repr);
1529 void cmd_indent_node(GtkObject */*object*/, gpointer /*data*/)
1531     Inkscape::XML::Node *repr = selected_repr;
1532     g_assert(repr != NULL);
1533     Inkscape::XML::Node *parent = sp_repr_parent(repr);
1534     g_return_if_fail(parent != NULL);
1535     g_return_if_fail(parent->firstChild() != repr);
1537     Inkscape::XML::Node* prev = parent->firstChild();
1538     while (prev && prev->next() != repr) {
1539         prev = prev->next();
1540     }
1541     g_return_if_fail(prev != NULL);
1542     g_return_if_fail(prev->type() == Inkscape::XML::ELEMENT_NODE);
1544     Inkscape::XML::Node* ref = NULL;
1545     if (prev->firstChild()) {
1546         for( ref = prev->firstChild() ; ref->next() ; ref = ref->next() ){};
1547     }
1549     parent->removeChild(repr);
1550     prev->addChild(repr, ref);
1552     DocumentUndo::done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1553                        _("Indent node"));
1554     set_tree_select(repr);
1555     set_dt_select(repr);
1557 } // end of cmd_indent_node()
1561 void cmd_unindent_node(GtkObject */*object*/, gpointer /*data*/)
1563     Inkscape::XML::Node *repr = selected_repr;
1564     g_assert(repr != NULL);
1565     Inkscape::XML::Node *parent = sp_repr_parent(repr);
1566     g_return_if_fail(parent);
1567     Inkscape::XML::Node *grandparent = sp_repr_parent(parent);
1568     g_return_if_fail(grandparent);
1570     parent->removeChild(repr);
1571     grandparent->addChild(repr, parent);
1573     DocumentUndo::done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1574                        _("Unindent node"));
1575     set_tree_select(repr);
1576     set_dt_select(repr);
1578 } // end of cmd_unindent_node()
1580 /** Returns true iff \a item is suitable to be included in the selection, in particular
1581     whether it has a bounding box in the desktop coordinate system for rendering resize handles.
1583     Descendents of <defs> nodes (markers etc.) return false, for example.
1584 */
1585 bool in_dt_coordsys(SPObject const &item)
1587     /* Definition based on sp_item_i2doc_affine. */
1588     SPObject const *child = &item;
1589     g_return_val_if_fail(child != NULL, false);
1590     for(;;) {
1591         if (!SP_IS_ITEM(child)) {
1592             return false;
1593         }
1594         SPObject const * const parent = SP_OBJECT_PARENT(child);
1595         if (parent == NULL) {
1596             break;
1597         }
1598         child = parent;
1599     }
1600     g_assert(SP_IS_ROOT(child));
1601     /* Relevance: Otherwise, I'm not sure whether to return true or false. */
1602     return true;
1606 /*
1607   Local Variables:
1608   mode:c++
1609   c-file-style:"stroustrup"
1610   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1611   indent-tabs-mode:nil
1612   fill-column:99
1613   End:
1614 */
1615 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :