c90cde490ba02dfd0812b1c57a19ddadfbe2934f
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()
154 {
155 _message_context->set(Inkscape::NORMAL_MESSAGE,
156 _("<b>Click</b> to select nodes, <b>drag</b> to rearrange."));
157 }
160 /*
161 * \brief Sets the XML status bar, depending on which attr is selected.
162 */
163 void attr_reset_context(gint attr)
164 {
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 }
175 }
178 void sp_xml_tree_dialog()
179 {
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)
615 {
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;
632 }
635 static void sp_xmltree_desktop_activate(Inkscape::Application */*inkscape*/,
636 SPDesktop *desktop,
637 GtkWidget */*dialog*/ )
638 {
639 set_tree_desktop(desktop);
640 }
642 static void sp_xmltree_desktop_deactivate(Inkscape::Application */*inkscape*/,
643 SPDesktop */*desktop*/,
644 GtkWidget */*dialog*/ )
645 {
646 set_tree_desktop(NULL);
647 }
650 void set_tree_desktop(SPDesktop *desktop)
651 {
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)
674 {
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 }
691 }
695 void set_tree_repr(Inkscape::XML::Node *repr)
696 {
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);
715 }
719 void set_tree_select(Inkscape::XML::Node *repr)
720 {
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);
749 }
753 void propagate_tree_select(Inkscape::XML::Node *repr)
754 {
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 }
766 }
769 Inkscape::XML::Node *get_dt_select()
770 {
771 if (!current_desktop) {
772 return NULL;
773 }
775 return sp_desktop_selection(current_desktop)->singleRepr();
776 }
780 void set_dt_select(Inkscape::XML::Node *repr)
781 {
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*/)
819 {
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();
843 }
845 void on_tree_unselect_row(GtkCTree *tree,
846 GtkCTreeNode *node,
847 gint /*column*/,
848 gpointer /*data*/)
849 {
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 }
863 }
867 void after_tree_move(GtkCTree */*tree*/,
868 GtkCTreeNode *node,
869 GtkCTreeNode *new_parent,
870 GtkCTreeNode *new_sibling,
871 gpointer /*data*/)
872 {
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 }
881 }
884 static void on_destroy(GtkObject */*object*/, gpointer /*data*/)
885 {
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;
901 }
905 static gboolean on_delete(GtkObject */*object*/, GdkEvent */*event*/, gpointer /*data*/)
906 {
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
920 }
923 static void _set_status_message(Inkscape::MessageType /*type*/, const gchar *message, GtkWidget */*dialog*/)
924 {
925 if (status) {
926 gtk_label_set_markup(GTK_LABEL(status), message ? message : "");
927 }
928 }
931 void on_tree_select_row_enable(GtkCTree */*tree*/,
932 GtkCTreeNode */*node*/,
933 gint /*column*/,
934 gpointer data)
935 {
936 gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
937 }
941 void on_tree_select_row_enable_if_element(GtkCTree *tree,
942 GtkCTreeNode *node,
943 gint /*column*/,
944 gpointer data )
945 {
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 }
953 }
957 void on_tree_select_row_show_if_element(GtkCTree *tree, GtkCTreeNode *node,
958 gint /*column*/, gpointer data)
959 {
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 }
967 }
971 void on_tree_select_row_show_if_text(GtkCTree *tree, GtkCTreeNode *node,
972 gint /*column*/, gpointer data)
973 {
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 }
981 }
984 gboolean xml_tree_node_mutable(GtkCTreeNode *node)
985 {
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;
1008 }
1011 void on_tree_select_row_enable_if_mutable(GtkCTree */*tree*/, GtkCTreeNode *node,
1012 gint /*column*/, gpointer data)
1013 {
1014 gtk_widget_set_sensitive(GTK_WIDGET(data), xml_tree_node_mutable(node));
1015 }
1019 void on_tree_unselect_row_disable(GtkCTree */*tree*/, GtkCTreeNode */*node*/,
1020 gint /*column*/, gpointer data)
1021 {
1022 gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1023 }
1027 void on_tree_unselect_row_hide(GtkCTree */*tree*/, GtkCTreeNode */*node*/,
1028 gint /*column*/, gpointer data)
1029 {
1030 gtk_widget_hide(GTK_WIDGET(data));
1031 }
1035 void on_tree_unselect_row_clear_text(GtkCTree */*tree*/, GtkCTreeNode */*node*/,
1036 gint /*column*/, gpointer data)
1037 {
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 }
1045 }
1048 void on_attr_select_row(GtkCList *list, gint row, gint /*column*/,
1049 GdkEventButton */*event*/, gpointer /*data*/)
1050 {
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);
1055 }
1058 void on_attr_unselect_row(GtkCList */*list*/, gint /*row*/, gint /*column*/,
1059 GdkEventButton */*event*/, gpointer /*data*/)
1060 {
1061 selected_attr = 0;
1062 attr_reset_context(selected_attr);
1063 }
1066 void on_attr_row_changed(GtkCList *list, gint row, gpointer /*data*/)
1067 {
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 }
1101 }
1104 void on_attr_select_row_set_name_content(GtkCList *list, gint row,
1105 gint /*column*/, GdkEventButton */*event*/,
1106 gpointer data)
1107 {
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);
1113 }
1117 void on_attr_select_row_set_value_content(GtkCList *list, gint row, gint /*column*/,
1118 GdkEventButton */*event*/,
1119 gpointer data)
1120 {
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));
1128 }
1131 void on_tree_select_row_enable_if_indentable(GtkCTree *tree, GtkCTreeNode *node,
1132 gint /*column*/, gpointer data)
1133 {
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);
1156 }
1160 void on_tree_select_row_enable_if_not_first_child(GtkCTree *tree,
1161 GtkCTreeNode *node,
1162 gint /*column*/,
1163 gpointer data)
1164 {
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 }
1173 }
1177 void on_tree_select_row_enable_if_not_last_child(GtkCTree *tree,
1178 GtkCTreeNode *node,
1179 gint /*column*/, gpointer data)
1180 {
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 }
1189 }
1193 void on_tree_select_row_enable_if_has_grandparent(GtkCTree */*tree*/,
1194 GtkCTreeNode *node,
1195 gint /*column*/, gpointer data)
1196 {
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 }
1209 }
1213 void on_attr_select_row_enable(GtkCList */*list*/, gint /*row*/, gint /*column*/,
1214 GdkEventButton */*event*/, gpointer data)
1215 {
1216 gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
1217 }
1221 void on_attr_unselect_row_disable(GtkCList */*list*/, gint /*row*/, gint /*column*/,
1222 GdkEventButton */*event*/, gpointer data)
1223 {
1224 gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1225 }
1229 void on_attr_unselect_row_clear_text(GtkCList */*list*/, gint /*row*/, gint /*column*/,
1230 GdkEventButton */*event*/, gpointer data)
1231 {
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 }
1239 }
1243 void on_editable_changed_enable_if_valid_xml_name(GtkEditable *editable,
1244 gpointer data)
1245 {
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);
1256 }
1260 void on_desktop_selection_changed(Inkscape::Selection */*selection*/)
1261 {
1262 if (!blocked++) {
1263 set_tree_select(get_dt_select());
1264 }
1265 blocked--;
1266 }
1268 static void on_document_replaced(SPDesktop *dt, SPDocument *doc)
1269 {
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);
1275 }
1277 void on_document_uri_set(gchar const */*uri*/, SPDocument *document)
1278 {
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);
1284 }
1288 void on_clicked_get_editable_text(GtkWidget */*widget*/, gpointer data)
1289 {
1290 EditableDest *dest = (EditableDest *) data;
1291 dest->text = gtk_editable_get_chars(dest->editable, 0, -1);
1292 }
1294 gboolean
1295 quit_on_esc (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
1296 {
1297 switch (get_group0_keyval (event)) {
1298 case GDK_Escape: // defocus
1299 gtk_widget_destroy(w);
1300 return TRUE;
1301 }
1302 return FALSE;
1303 }
1305 void cmd_new_element_node(GtkObject */*object*/, gpointer /*data*/)
1306 {
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*/)
1386 {
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));
1401 }
1403 void cmd_duplicate_node(GtkObject */*object*/, gpointer /*data*/)
1404 {
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 }
1419 }
1423 void cmd_delete_node(GtkObject */*object*/, gpointer /*data*/)
1424 {
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"));
1430 }
1434 void cmd_delete_attr(GtkObject */*object*/, gpointer /*data*/)
1435 {
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"));
1448 }
1452 void cmd_set_attr(GtkObject */*object*/, gpointer /*data*/)
1453 {
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 }
1484 }
1488 void cmd_raise_node(GtkObject */*object*/, gpointer /*data*/)
1489 {
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);
1510 }
1514 void cmd_lower_node(GtkObject */*object*/, gpointer /*data*/)
1515 {
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);
1527 }
1529 void cmd_indent_node(GtkObject */*object*/, gpointer /*data*/)
1530 {
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*/)
1562 {
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)
1586 {
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;
1603 }
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 :