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 *
11 * Copyright (C) 1999-2006 Authors
12 * Released under GNU GPL, read the file 'COPYING' for more information
13 */
15 #include <gdk/gdkkeysyms.h>
16 #include <glibmm/i18n.h>
17 #include <gtk/gtk.h>
19 #include "desktop.h"
20 #include "../desktop-handles.h"
21 #include "dialog-events.h"
22 #include "../document.h"
23 #include "../event-context.h"
24 #include "helper/window.h"
25 #include "../inkscape.h"
26 #include "../interface.h"
27 #include "macros.h"
28 #include "message-context.h"
29 #include "message-stack.h"
30 #include "../preferences.h"
31 #include "../selection.h"
32 #include "shortcuts.h"
33 #include "../sp-root.h"
34 #include "../sp-string.h"
35 #include "../sp-tspan.h"
36 #include "ui/icon-names.h"
37 #include "../verbs.h"
38 #include "widgets/icon.h"
39 #include "../widgets/sp-xmlview-attr-list.h"
40 #include "../widgets/sp-xmlview-content.h"
41 #include "../widgets/sp-xmlview-tree.h"
43 #define MIN_ONSCREEN_DISTANCE 50
45 struct EditableDest {
46 GtkEditable *editable;
47 gchar *text;
48 };
50 static GtkWidget *dlg = NULL;
51 static sigc::connection sel_changed_connection;
52 static sigc::connection document_uri_set_connection;
53 static sigc::connection document_replaced_connection;
54 static win_data wd;
55 // impossible original values to make sure they are read from prefs
56 static gint x = -1000, y = -1000, w = 0, h = 0;
57 static Glib::ustring const prefs_path = "/dialogs/xml/";
58 static GtkWidget *status = NULL;
59 static Inkscape::MessageStack *_message_stack = NULL;
60 static Inkscape::MessageContext *_message_context = NULL;
61 static sigc::connection _message_changed_connection;
63 static GtkTooltips *tooltips = NULL;
64 static GtkEditable *attr_name = NULL;
65 static GtkTextView *attr_value = NULL;
66 static SPXMLViewTree *tree = NULL;
67 static SPXMLViewAttrList *attributes = NULL;
68 static SPXMLViewContent *content = NULL;
70 static gint blocked = 0;
71 static SPDesktop *current_desktop = NULL;
72 static SPDocument *current_document = NULL;
73 static gint selected_attr = 0;
74 static Inkscape::XML::Node *selected_repr = NULL;
76 static void sp_xmltree_desktop_activate( Inkscape::Application *inkscape, SPDesktop *desktop, GtkWidget *dialog );
77 static void sp_xmltree_desktop_deactivate( Inkscape::Application *inkscape, SPDesktop *desktop, GtkWidget *dialog );
79 static void set_tree_desktop(SPDesktop *desktop);
80 static void set_tree_document(SPDocument *document);
81 static void set_tree_repr(Inkscape::XML::Node *repr);
83 static void set_tree_select(Inkscape::XML::Node *repr);
84 static void propagate_tree_select(Inkscape::XML::Node *repr);
86 static Inkscape::XML::Node *get_dt_select();
87 static void set_dt_select(Inkscape::XML::Node *repr);
89 static void on_tree_select_row(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
90 static void on_tree_unselect_row(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
91 static void after_tree_move(GtkCTree *tree, GtkCTreeNode *node, GtkCTreeNode *new_parent, GtkCTreeNode *new_sibling, gpointer data);
92 static void on_destroy(GtkObject *object, gpointer data);
93 static gboolean on_delete(GtkObject *object, GdkEvent *event, gpointer data);
95 static void on_tree_select_row_enable_if_element(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
96 static void on_tree_select_row_enable_if_mutable(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
97 static void on_tree_select_row_show_if_element(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
98 static void on_tree_select_row_show_if_text(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
99 static void on_tree_select_row_enable_if_indentable(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
100 static void on_tree_select_row_enable_if_not_first_child(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
101 static void on_tree_select_row_enable_if_not_last_child(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
102 static void on_tree_select_row_enable_if_has_grandparent(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
104 static void on_tree_unselect_row_clear_text(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
105 static void on_tree_unselect_row_disable(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
106 static void on_tree_unselect_row_hide(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
108 static void on_attr_select_row(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
109 static void on_attr_unselect_row(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
110 static void on_attr_row_changed( GtkCList *list, gint row, gpointer data );
112 static void on_attr_select_row_enable(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
113 static void on_attr_unselect_row_disable(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
115 static void on_attr_select_row_set_name_content(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
116 static void on_attr_select_row_set_value_content(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
117 static void on_attr_unselect_row_clear_text(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
119 static void on_editable_changed_enable_if_valid_xml_name(GtkEditable *editable, gpointer data);
121 static void on_desktop_selection_changed(Inkscape::Selection *selection);
122 static void on_document_replaced(SPDesktop *dt, SPDocument *document);
123 static void on_document_uri_set(gchar const *uri, SPDocument *document);
125 static void on_clicked_get_editable_text(GtkWidget *widget, gpointer data);
127 static void _set_status_message(Inkscape::MessageType type, const gchar *message, GtkWidget *dialog);
129 static void cmd_new_element_node(GtkObject *object, gpointer data);
130 static void cmd_new_text_node(GtkObject *object, gpointer data);
131 static void cmd_duplicate_node(GtkObject *object, gpointer data);
132 static void cmd_delete_node(GtkObject *object, gpointer data);
134 static void cmd_raise_node(GtkObject *object, gpointer data);
135 static void cmd_lower_node(GtkObject *object, gpointer data);
136 static void cmd_indent_node(GtkObject *object, gpointer data);
137 static void cmd_unindent_node(GtkObject *object, gpointer data);
139 static void cmd_delete_attr(GtkObject *object, gpointer data);
140 static void cmd_set_attr(GtkObject *object, gpointer data);
142 static gboolean sp_xml_tree_key_press(GtkWidget *widget, GdkEventKey *event);
144 static bool in_dt_coordsys(SPObject const &item);
146 /*
147 * \brief Sets the XML status bar when the tree is selected.
148 */
149 void tree_reset_context()
150 {
151 _message_context->set(Inkscape::NORMAL_MESSAGE,
152 _("<b>Click</b> to select nodes, <b>drag</b> to rearrange."));
153 }
156 /*
157 * \brief Sets the XML status bar, depending on which attr is selected.
158 */
159 void attr_reset_context(gint attr)
160 {
161 if (attr == 0) {
162 _message_context->set(Inkscape::NORMAL_MESSAGE,
163 _("<b>Click</b> attribute to edit."));
164 }
165 else {
166 const gchar *name = g_quark_to_string(attr);
167 gchar *message = g_strdup_printf(_("Attribute <b>%s</b> selected. Press <b>Ctrl+Enter</b> when done editing to commit changes."), name);
168 _message_context->set(Inkscape::NORMAL_MESSAGE, message);
169 g_free(message);
170 }
171 }
174 void sp_xml_tree_dialog()
175 {
176 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
178 if (!desktop) {
179 return;
180 }
182 if (dlg == NULL)
183 { // very long block
185 GtkWidget *box, *sw, *paned, *toolbar, *button;
186 GtkWidget *text_container, *attr_container, *attr_subpaned_container, *box2;
187 GtkWidget *set_attr;
189 tooltips = gtk_tooltips_new();
190 gtk_tooltips_enable(tooltips);
192 dlg = sp_window_new("", TRUE);
193 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
194 if (x == -1000 || y == -1000) {
195 x = prefs->getInt(prefs_path + "x", -1000);
196 y = prefs->getInt(prefs_path + "y", -1000);
197 }
198 if (w ==0 || h == 0) {
199 w = prefs->getInt(prefs_path + "w", 0);
200 h = prefs->getInt(prefs_path + "h", 0);
201 }
203 // if (x<0) x=0;
204 // if (y<0) y=0;
206 if (w && h) {
207 gtk_window_resize((GtkWindow *) dlg, w, h);
208 }
209 if (x >= 0 && y >= 0 && (x < (gdk_screen_width()-MIN_ONSCREEN_DISTANCE)) && (y < (gdk_screen_height()-MIN_ONSCREEN_DISTANCE))) {
210 gtk_window_move((GtkWindow *) dlg, x, y);
211 } else {
212 gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
213 }
215 sp_transientize(dlg);
216 wd.win = dlg;
217 wd.stop = 0;
218 g_signal_connect ( G_OBJECT(INKSCAPE), "activate_desktop", G_CALLBACK(sp_transientize_callback), &wd );
220 gtk_signal_connect( GTK_OBJECT(dlg), "event", GTK_SIGNAL_FUNC(sp_dialog_event_handler), dlg );
222 gtk_signal_connect( GTK_OBJECT(dlg), "destroy", G_CALLBACK(on_destroy), dlg);
223 gtk_signal_connect( GTK_OBJECT(dlg), "delete_event", G_CALLBACK(on_delete), dlg);
224 g_signal_connect ( G_OBJECT(INKSCAPE), "shut_down", G_CALLBACK(on_delete), dlg);
226 g_signal_connect ( G_OBJECT(INKSCAPE), "dialogs_hide", G_CALLBACK(sp_dialog_hide), dlg);
227 g_signal_connect ( G_OBJECT(INKSCAPE), "dialogs_unhide", G_CALLBACK(sp_dialog_unhide), dlg);
230 gtk_container_set_border_width(GTK_CONTAINER(dlg), 0);
231 gtk_window_set_default_size(GTK_WINDOW(dlg), 640, 384);
233 GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
234 gtk_container_add(GTK_CONTAINER(dlg), vbox);
236 GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
237 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
239 status = gtk_label_new(NULL);
240 gtk_misc_set_alignment(GTK_MISC(status), 0.0, 0.5);
241 gtk_widget_set_size_request(status, 1, -1);
242 gtk_label_set_markup(GTK_LABEL(status), "");
243 gtk_box_pack_start(GTK_BOX(hbox), gtk_hbox_new(FALSE, 0), FALSE, FALSE, 4);
244 gtk_box_pack_start(GTK_BOX(hbox), status, TRUE, TRUE, 0);
246 paned = gtk_hpaned_new();
247 gtk_paned_set_position(GTK_PANED(paned), 256);
248 gtk_box_pack_start(GTK_BOX(vbox), paned, TRUE, TRUE, 0);
250 _message_stack = new Inkscape::MessageStack();
251 _message_context = new Inkscape::MessageContext(_message_stack);
252 _message_changed_connection = _message_stack->connectChanged(
253 sigc::bind(sigc::ptr_fun(_set_status_message), dlg)
254 );
256 /* tree view */
258 box = gtk_vbox_new(FALSE, 0);
259 gtk_paned_pack1(GTK_PANED(paned), box, FALSE, FALSE);
261 tree = SP_XMLVIEW_TREE(sp_xmlview_tree_new(NULL, NULL, NULL));
262 gtk_tooltips_set_tip( tooltips, GTK_WIDGET(tree),
263 _("Drag to reorder nodes"), NULL );
265 g_signal_connect( G_OBJECT(tree), "tree_select_row",
266 G_CALLBACK(on_tree_select_row), NULL );
268 g_signal_connect( G_OBJECT(tree), "tree_unselect_row",
269 G_CALLBACK(on_tree_unselect_row), NULL );
271 g_signal_connect_after( G_OBJECT(tree), "tree_move",
272 G_CALLBACK(after_tree_move), NULL);
274 /* TODO: replace gtk_signal_connect_while_alive() with something
275 * else...
276 */
277 toolbar = gtk_toolbar_new();
278 gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
279 gtk_container_set_border_width(GTK_CONTAINER(toolbar), 0);
281 button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
282 NULL,
283 _("New element node"),
284 NULL,
285 sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
286 INKSCAPE_ICON_XML_ELEMENT_NEW ),
287 G_CALLBACK(cmd_new_element_node),
288 NULL);
290 gtk_signal_connect_while_alive( GTK_OBJECT(tree),
291 "tree_select_row",
292 G_CALLBACK(on_tree_select_row_enable_if_element),
293 button,
294 GTK_OBJECT(button));
296 gtk_signal_connect_while_alive(GTK_OBJECT(tree),
297 "tree_unselect_row",
298 G_CALLBACK(on_tree_unselect_row_disable),
299 button,
300 GTK_OBJECT(button));
302 gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
304 button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
305 NULL, _("New text node"), NULL,
306 sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
307 INKSCAPE_ICON_XML_TEXT_NEW ),
308 G_CALLBACK(cmd_new_text_node),
309 NULL);
311 gtk_signal_connect_while_alive(GTK_OBJECT(tree),
312 "tree_select_row",
313 G_CALLBACK(on_tree_select_row_enable_if_element),
314 button,
315 GTK_OBJECT(button));
317 gtk_signal_connect_while_alive(GTK_OBJECT(tree),
318 "tree_unselect_row",
319 G_CALLBACK(on_tree_unselect_row_disable),
320 button,
321 GTK_OBJECT(button));
323 gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
325 button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
326 NULL, _("Duplicate node"), NULL,
327 sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
328 INKSCAPE_ICON_XML_NODE_DUPLICATE ),
329 G_CALLBACK(cmd_duplicate_node),
330 NULL);
332 gtk_signal_connect_while_alive(GTK_OBJECT(tree),
333 "tree_select_row",
334 G_CALLBACK(on_tree_select_row_enable_if_mutable),
335 button,
336 GTK_OBJECT(button));
338 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
339 G_CALLBACK(on_tree_unselect_row_disable),
340 button, GTK_OBJECT(button));
342 gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
344 gtk_toolbar_append_space(GTK_TOOLBAR(toolbar));
346 button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
347 NULL, Q_("nodeAsInXMLdialogTooltip|Delete node"), NULL,
348 sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
349 INKSCAPE_ICON_XML_NODE_DELETE ),
350 G_CALLBACK(cmd_delete_node), NULL );
352 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
353 G_CALLBACK(on_tree_select_row_enable_if_mutable),
354 button, GTK_OBJECT(button));
355 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
356 G_CALLBACK(on_tree_unselect_row_disable),
357 button, GTK_OBJECT(button));
358 gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
360 gtk_toolbar_append_space(GTK_TOOLBAR(toolbar));
362 button = gtk_toolbar_append_item( GTK_TOOLBAR(toolbar), "<",
363 _("Unindent node"), NULL,
364 gtk_arrow_new(GTK_ARROW_LEFT, GTK_SHADOW_IN),
365 G_CALLBACK(cmd_unindent_node), NULL);
367 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
368 G_CALLBACK(on_tree_select_row_enable_if_has_grandparent),
369 button, GTK_OBJECT(button));
371 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
372 G_CALLBACK(on_tree_unselect_row_disable),
373 button, GTK_OBJECT(button));
375 gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
377 button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), ">",
378 _("Indent node"), NULL,
379 gtk_arrow_new(GTK_ARROW_RIGHT, GTK_SHADOW_IN),
380 G_CALLBACK(cmd_indent_node), NULL);
381 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
382 G_CALLBACK(on_tree_select_row_enable_if_indentable),
383 button, GTK_OBJECT(button));
384 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
385 (GCallback) on_tree_unselect_row_disable,
386 button, GTK_OBJECT(button));
387 gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
389 button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), "^",
390 _("Raise node"), NULL,
391 gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_IN),
392 G_CALLBACK(cmd_raise_node), NULL);
393 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
394 G_CALLBACK(on_tree_select_row_enable_if_not_first_child),
395 button, GTK_OBJECT(button));
396 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
397 G_CALLBACK(on_tree_unselect_row_disable),
398 button, GTK_OBJECT(button));
399 gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
401 button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), "v",
402 _("Lower node"), NULL,
403 gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_IN),
404 G_CALLBACK(cmd_lower_node), NULL);
405 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
406 G_CALLBACK(on_tree_select_row_enable_if_not_last_child),
407 button, GTK_OBJECT(button));
408 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
409 G_CALLBACK(on_tree_unselect_row_disable),
410 button, GTK_OBJECT(button));
411 gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
413 gtk_box_pack_start(GTK_BOX(box), toolbar, FALSE, TRUE, 0);
415 sw = gtk_scrolled_window_new(NULL, NULL);
416 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(sw),
417 GTK_POLICY_AUTOMATIC,
418 GTK_POLICY_AUTOMATIC );
419 gtk_box_pack_start(GTK_BOX(box), sw, TRUE, TRUE, 0);
421 gtk_container_add(GTK_CONTAINER(sw), GTK_WIDGET(tree));
423 /* node view */
425 box = gtk_vbox_new(FALSE, 0);
426 gtk_paned_pack2(GTK_PANED(paned), box, TRUE, TRUE);
428 /* attributes */
430 attr_container = gtk_vbox_new(FALSE, 0);
431 gtk_box_pack_start( GTK_BOX(box), GTK_WIDGET(attr_container),
432 TRUE, TRUE, 0 );
434 attributes = SP_XMLVIEW_ATTR_LIST(sp_xmlview_attr_list_new(NULL));
435 g_signal_connect( G_OBJECT(attributes), "select_row",
436 G_CALLBACK(on_attr_select_row), NULL);
437 g_signal_connect( G_OBJECT(attributes), "unselect_row",
438 G_CALLBACK(on_attr_unselect_row), NULL);
439 g_signal_connect( G_OBJECT(attributes), "row-value-changed",
440 G_CALLBACK(on_attr_row_changed), NULL);
442 toolbar = gtk_toolbar_new();
443 gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
444 gtk_container_set_border_width(GTK_CONTAINER(toolbar), 0);
446 button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
447 NULL, _("Delete attribute"), NULL,
448 sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
449 INKSCAPE_ICON_XML_ATTRIBUTE_DELETE ),
450 (GCallback) cmd_delete_attr, NULL);
452 gtk_signal_connect_while_alive(GTK_OBJECT(attributes), "select_row",
453 (GCallback) on_attr_select_row_enable, button,
454 GTK_OBJECT(button));
456 gtk_signal_connect_while_alive(GTK_OBJECT(attributes), "unselect_row",
457 (GCallback) on_attr_unselect_row_disable, button,
458 GTK_OBJECT(button));
460 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
461 (GCallback) on_tree_unselect_row_disable, button,
462 GTK_OBJECT(button));
464 gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
466 gtk_box_pack_start( GTK_BOX(attr_container),
467 GTK_WIDGET(toolbar), FALSE, TRUE, 0 );
469 attr_subpaned_container = gtk_vpaned_new();
470 gtk_box_pack_start( GTK_BOX(attr_container),
471 GTK_WIDGET(attr_subpaned_container),
472 TRUE, TRUE, 0 );
473 gtk_widget_show(attr_subpaned_container);
475 sw = gtk_scrolled_window_new(NULL, NULL);
476 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
477 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
478 gtk_paned_pack1( GTK_PANED(attr_subpaned_container),
479 GTK_WIDGET(sw), TRUE, TRUE );
480 gtk_container_add(GTK_CONTAINER(sw), GTK_WIDGET(attributes));
482 toolbar = gtk_vbox_new(FALSE, 4);
483 gtk_container_set_border_width(GTK_CONTAINER(toolbar), 4);
485 box2 = gtk_hbox_new(FALSE, 4);
486 gtk_box_pack_start( GTK_BOX(toolbar), GTK_WIDGET(box2),
487 FALSE, TRUE, 0);
489 attr_name = GTK_EDITABLE(gtk_entry_new());
490 gtk_tooltips_set_tip( tooltips, GTK_WIDGET(attr_name),
491 // TRANSLATORS: "Attribute" is a noun here
492 _("Attribute name"), NULL );
494 gtk_signal_connect( GTK_OBJECT(attributes), "select_row",
495 (GCallback) on_attr_select_row_set_name_content,
496 attr_name);
498 gtk_signal_connect( GTK_OBJECT(attributes), "unselect_row",
499 (GCallback) on_attr_unselect_row_clear_text,
500 attr_name);
502 gtk_signal_connect( GTK_OBJECT(tree), "tree_unselect_row",
503 (GCallback) on_tree_unselect_row_clear_text,
504 attr_name);
506 gtk_box_pack_start( GTK_BOX(box2), GTK_WIDGET(attr_name),
507 TRUE, TRUE, 0);
509 set_attr = gtk_button_new();
510 gtk_tooltips_set_tip( tooltips, GTK_WIDGET(set_attr),
511 // TRANSLATORS: "Set" is a verb here
512 _("Set attribute"), NULL );
513 // TRANSLATORS: "Set" is a verb here
514 GtkWidget *set_label = gtk_label_new(_("Set"));
515 gtk_container_add(GTK_CONTAINER(set_attr), set_label);
517 gtk_signal_connect( GTK_OBJECT(set_attr), "clicked",
518 (GCallback) cmd_set_attr, NULL);
519 gtk_signal_connect( GTK_OBJECT(attr_name), "changed",
520 (GCallback) on_editable_changed_enable_if_valid_xml_name,
521 set_attr );
522 gtk_widget_set_sensitive(GTK_WIDGET(set_attr), FALSE);
524 gtk_box_pack_start(GTK_BOX(box2), set_attr, FALSE, FALSE, 0);
526 sw = gtk_scrolled_window_new(NULL, NULL);
527 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(sw),
528 GTK_POLICY_AUTOMATIC,
529 GTK_POLICY_AUTOMATIC );
530 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN );
531 gtk_box_pack_start(GTK_BOX(toolbar), sw, TRUE, TRUE, 0);
533 attr_value =(GtkTextView *) gtk_text_view_new();
534 gtk_text_view_set_wrap_mode((GtkTextView *) attr_value, GTK_WRAP_CHAR);
535 gtk_tooltips_set_tip( tooltips, GTK_WIDGET(attr_value),
536 // TRANSLATORS: "Attribute" is a noun here
537 _("Attribute value"), NULL );
538 gtk_signal_connect( GTK_OBJECT(attributes), "select_row",
539 (GCallback) on_attr_select_row_set_value_content,
540 attr_value );
541 gtk_signal_connect( GTK_OBJECT(attributes), "unselect_row",
542 (GCallback) on_attr_unselect_row_clear_text,
543 attr_value );
544 gtk_signal_connect( GTK_OBJECT(tree), "tree_unselect_row",
545 (GCallback) on_tree_unselect_row_clear_text,
546 attr_value );
547 gtk_text_view_set_editable(attr_value, TRUE);
548 gtk_container_add( GTK_CONTAINER(sw),
549 GTK_WIDGET(attr_value) );
551 gtk_paned_pack2( GTK_PANED(attr_subpaned_container),
552 GTK_WIDGET(toolbar), FALSE, TRUE );
554 /* text */
556 sw = gtk_scrolled_window_new(NULL, NULL);
557 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(sw),
558 GTK_POLICY_AUTOMATIC,
559 GTK_POLICY_AUTOMATIC );
560 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN );
561 gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(sw), TRUE, TRUE, 0);
563 content = SP_XMLVIEW_CONTENT(sp_xmlview_content_new(NULL));
564 gtk_container_add(GTK_CONTAINER(sw), GTK_WIDGET(content));
566 text_container = sw;
568 /* initial show/hide */
570 gtk_widget_show_all(GTK_WIDGET(dlg));
572 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
573 (GCallback) on_tree_select_row_show_if_element,
574 attr_container, GTK_OBJECT(attr_container));
576 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
577 (GCallback) on_tree_unselect_row_hide,
578 attr_container, GTK_OBJECT(attr_container));
580 gtk_widget_hide(attr_container);
582 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
583 (GCallback) on_tree_select_row_show_if_text,
584 text_container, GTK_OBJECT(text_container));
586 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
587 (GCallback) on_tree_unselect_row_hide,
588 text_container, GTK_OBJECT(text_container));
590 gtk_widget_hide(text_container);
592 g_signal_connect( G_OBJECT(INKSCAPE), "activate_desktop",
593 G_CALLBACK(sp_xmltree_desktop_activate), dlg);
595 g_signal_connect( G_OBJECT(INKSCAPE), "deactivate_desktop",
596 G_CALLBACK(sp_xmltree_desktop_deactivate), dlg);
598 g_signal_connect((GObject *) dlg, "key_press_event", (GCallback) sp_xml_tree_key_press, NULL);
600 tree_reset_context();
601 } // end of if (dlg == NULL)
603 gtk_window_present((GtkWindow *) dlg);
605 g_assert(desktop != NULL);
606 set_tree_desktop(desktop);
608 } // end of sp_xml_tree_dialog()
610 static gboolean sp_xml_tree_key_press(GtkWidget */*widget*/, GdkEventKey *event)
611 {
613 unsigned int shortcut = get_group0_keyval(event) |
614 ( event->state & GDK_SHIFT_MASK ?
615 SP_SHORTCUT_SHIFT_MASK : 0 ) |
616 ( event->state & GDK_CONTROL_MASK ?
617 SP_SHORTCUT_CONTROL_MASK : 0 ) |
618 ( event->state & GDK_MOD1_MASK ?
619 SP_SHORTCUT_ALT_MASK : 0 );
621 /* fixme: if you need to add more xml-tree-specific callbacks, you should probably upgrade
622 * the sp_shortcut mechanism to take into account windows. */
623 if (shortcut == (SP_SHORTCUT_CONTROL_MASK | GDK_Return)) {
624 cmd_set_attr(NULL, NULL);
625 return true;
626 }
627 return false;
628 }
631 static void sp_xmltree_desktop_activate(Inkscape::Application */*inkscape*/,
632 SPDesktop *desktop,
633 GtkWidget */*dialog*/ )
634 {
635 set_tree_desktop(desktop);
636 }
638 static void sp_xmltree_desktop_deactivate(Inkscape::Application */*inkscape*/,
639 SPDesktop */*desktop*/,
640 GtkWidget */*dialog*/ )
641 {
642 set_tree_desktop(NULL);
643 }
646 void set_tree_desktop(SPDesktop *desktop)
647 {
648 if ( desktop == current_desktop ) {
649 return;
650 }
652 if (current_desktop) {
653 sel_changed_connection.disconnect();
654 document_replaced_connection.disconnect();
655 }
656 current_desktop = desktop;
657 if (desktop) {
658 sel_changed_connection = sp_desktop_selection(desktop)->connectChanged(&on_desktop_selection_changed);
659 document_replaced_connection = desktop->connectDocumentReplaced(&on_document_replaced);
660 set_tree_document(sp_desktop_document(desktop));
661 } else {
662 set_tree_document(NULL);
663 }
665 } // end of set_tree_desktop()
669 void set_tree_document(SPDocument *document)
670 {
671 if (document == current_document) {
672 return;
673 }
675 if (current_document) {
676 document_uri_set_connection.disconnect();
677 }
678 current_document = document;
679 if (current_document) {
681 document_uri_set_connection = current_document->connectURISet(sigc::bind(sigc::ptr_fun(&on_document_uri_set), current_document));
682 on_document_uri_set(SP_DOCUMENT_URI(current_document), current_document);
683 set_tree_repr(sp_document_repr_root(current_document));
685 } else {
686 set_tree_repr(NULL);
687 }
688 }
692 void set_tree_repr(Inkscape::XML::Node *repr)
693 {
694 if (repr == selected_repr) {
695 return;
696 }
698 gtk_clist_freeze(GTK_CLIST(tree));
700 sp_xmlview_tree_set_repr(tree, repr);
702 if (repr) {
703 set_tree_select(get_dt_select());
704 } else {
705 set_tree_select(NULL);
706 }
708 gtk_clist_thaw(GTK_CLIST(tree));
710 propagate_tree_select(selected_repr);
712 }
716 void set_tree_select(Inkscape::XML::Node *repr)
717 {
718 if (selected_repr) {
719 Inkscape::GC::release(selected_repr);
720 }
722 selected_repr = repr;
723 if (repr) {
724 GtkCTreeNode *node;
726 Inkscape::GC::anchor(selected_repr);
728 node = sp_xmlview_tree_get_repr_node(SP_XMLVIEW_TREE(tree), repr);
729 if (node) {
730 GtkCTreeNode *parent;
732 gtk_ctree_select(GTK_CTREE(tree), node);
734 parent = GTK_CTREE_ROW(node)->parent;
735 while (parent) {
736 gtk_ctree_expand(GTK_CTREE(tree), parent);
737 parent = GTK_CTREE_ROW(parent)->parent;
738 }
740 gtk_ctree_node_moveto(GTK_CTREE(tree), node, 0, 0.66, 0.0);
741 }
742 } else {
743 gtk_clist_unselect_all(GTK_CLIST(tree));
744 }
745 propagate_tree_select(repr);
746 }
750 void propagate_tree_select(Inkscape::XML::Node *repr)
751 {
752 if (repr && repr->type() == Inkscape::XML::ELEMENT_NODE) {
753 sp_xmlview_attr_list_set_repr(attributes, repr);
754 } else {
755 sp_xmlview_attr_list_set_repr(attributes, NULL);
756 }
758 if (repr && ( repr->type() == Inkscape::XML::TEXT_NODE || repr->type() == Inkscape::XML::COMMENT_NODE || repr->type() == Inkscape::XML::PI_NODE ) ) {
759 sp_xmlview_content_set_repr(content, repr);
760 } else {
761 sp_xmlview_content_set_repr(content, NULL);
762 }
763 }
766 Inkscape::XML::Node *get_dt_select()
767 {
768 if (!current_desktop) {
769 return NULL;
770 }
772 return sp_desktop_selection(current_desktop)->singleRepr();
773 }
777 void set_dt_select(Inkscape::XML::Node *repr)
778 {
779 if (!current_desktop) {
780 return;
781 }
783 Inkscape::Selection *selection = sp_desktop_selection(current_desktop);
785 SPObject *object;
786 if (repr) {
787 while ( ( repr->type() != Inkscape::XML::ELEMENT_NODE )
788 && sp_repr_parent(repr) )
789 {
790 repr = sp_repr_parent(repr);
791 } // end of while loop
793 object = sp_desktop_document(current_desktop)->getObjectByRepr(repr);
794 } else {
795 object = NULL;
796 }
798 blocked++;
799 if ( object && in_dt_coordsys(*object)
800 && !(SP_IS_STRING(object) ||
801 SP_IS_ROOT(object) ) )
802 {
803 /* We cannot set selection to root or string - they are not items and selection is not
804 * equipped to deal with them */
805 selection->set(SP_ITEM(object));
806 }
807 blocked--;
809 } // end of set_dt_select()
812 void on_tree_select_row(GtkCTree *tree,
813 GtkCTreeNode *node,
814 gint /*column*/,
815 gpointer /*data*/)
816 {
817 if (blocked) {
818 return;
819 }
821 Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
822 g_assert(repr != NULL);
824 if (selected_repr == repr) {
825 return;
826 }
828 if (selected_repr) {
829 Inkscape::GC::release(selected_repr);
830 selected_repr = NULL;
831 }
832 selected_repr = repr;
833 Inkscape::GC::anchor(selected_repr);
835 propagate_tree_select(selected_repr);
837 set_dt_select(selected_repr);
839 tree_reset_context();
840 }
842 void on_tree_unselect_row(GtkCTree *tree,
843 GtkCTreeNode *node,
844 gint /*column*/,
845 gpointer /*data*/)
846 {
847 if (blocked) {
848 return;
849 }
851 Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
852 propagate_tree_select(NULL);
853 set_dt_select(NULL);
855 if (selected_repr && (selected_repr == repr)) {
856 Inkscape::GC::release(selected_repr);
857 selected_repr = NULL;
858 selected_attr = 0;
859 }
860 }
864 void after_tree_move(GtkCTree */*tree*/,
865 GtkCTreeNode *node,
866 GtkCTreeNode *new_parent,
867 GtkCTreeNode *new_sibling,
868 gpointer /*data*/)
869 {
870 if (GTK_CTREE_ROW(node)->parent == new_parent &&
871 GTK_CTREE_ROW(node)->sibling == new_sibling)
872 {
873 sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
874 _("Drag XML subtree"));
875 } else {
876 sp_document_cancel(current_document);
877 }
878 }
881 static void on_destroy(GtkObject */*object*/, gpointer /*data*/)
882 {
883 set_tree_desktop(NULL);
884 gtk_object_destroy(GTK_OBJECT(tooltips));
885 tooltips = NULL;
886 sp_signal_disconnect_by_data(INKSCAPE, dlg);
887 wd.win = dlg = NULL;
888 wd.stop = 0;
890 _message_changed_connection.disconnect();
891 delete _message_context;
892 _message_context = NULL;
893 Inkscape::GC::release(_message_stack);
894 _message_stack = NULL;
895 _message_changed_connection.~connection();
897 status = NULL;
898 }
902 static gboolean on_delete(GtkObject */*object*/, GdkEvent */*event*/, gpointer /*data*/)
903 {
904 gtk_window_get_position((GtkWindow *) dlg, &x, &y);
905 gtk_window_get_size((GtkWindow *) dlg, &w, &h);
907 if (x<0) x=0;
908 if (y<0) y=0;
910 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
911 prefs->setInt(prefs_path + "x", x);
912 prefs->setInt(prefs_path + "y", y);
913 prefs->setInt(prefs_path + "w", w);
914 prefs->setInt(prefs_path + "h", h);
916 return FALSE; // which means, go ahead and destroy it
917 }
920 static void _set_status_message(Inkscape::MessageType /*type*/, const gchar *message, GtkWidget */*dialog*/)
921 {
922 if (status) {
923 gtk_label_set_markup(GTK_LABEL(status), message ? message : "");
924 }
925 }
928 void on_tree_select_row_enable(GtkCTree */*tree*/,
929 GtkCTreeNode */*node*/,
930 gint /*column*/,
931 gpointer data)
932 {
933 gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
934 }
938 void on_tree_select_row_enable_if_element(GtkCTree *tree,
939 GtkCTreeNode *node,
940 gint /*column*/,
941 gpointer data )
942 {
943 Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
945 if (repr->type() == Inkscape::XML::ELEMENT_NODE) {
946 gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
947 } else {
948 gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
949 }
950 }
954 void on_tree_select_row_show_if_element(GtkCTree *tree, GtkCTreeNode *node,
955 gint /*column*/, gpointer data)
956 {
957 Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
959 if (repr->type() == Inkscape::XML::ELEMENT_NODE) {
960 gtk_widget_show(GTK_WIDGET(data));
961 } else {
962 gtk_widget_hide(GTK_WIDGET(data));
963 }
964 }
968 void on_tree_select_row_show_if_text(GtkCTree *tree, GtkCTreeNode *node,
969 gint /*column*/, gpointer data)
970 {
971 Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
973 if ( repr->type() == Inkscape::XML::TEXT_NODE || repr->type() == Inkscape::XML::COMMENT_NODE || repr->type() == Inkscape::XML::PI_NODE ) {
974 gtk_widget_show(GTK_WIDGET(data));
975 } else {
976 gtk_widget_hide(GTK_WIDGET(data));
977 }
978 }
981 gboolean xml_tree_node_mutable(GtkCTreeNode *node)
982 {
983 // top-level is immutable, obviously
984 if (!GTK_CTREE_ROW(node)->parent) {
985 return false;
986 }
988 // if not in base level (where namedview, defs, etc go), we're mutable
989 if (GTK_CTREE_ROW(GTK_CTREE_ROW(node)->parent)->parent) {
990 return true;
991 }
993 Inkscape::XML::Node *repr;
994 repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
995 g_assert(repr);
997 // don't let "defs" or "namedview" disappear
998 if ( !strcmp(repr->name(),"svg:defs") ||
999 !strcmp(repr->name(),"sodipodi:namedview") ) {
1000 return false;
1001 }
1003 // everyone else is okay, I guess. :)
1004 return true;
1005 }
1008 void on_tree_select_row_enable_if_mutable(GtkCTree */*tree*/, GtkCTreeNode *node,
1009 gint /*column*/, gpointer data)
1010 {
1011 gtk_widget_set_sensitive(GTK_WIDGET(data), xml_tree_node_mutable(node));
1012 }
1016 void on_tree_unselect_row_disable(GtkCTree */*tree*/, GtkCTreeNode */*node*/,
1017 gint /*column*/, gpointer data)
1018 {
1019 gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1020 }
1024 void on_tree_unselect_row_hide(GtkCTree */*tree*/, GtkCTreeNode */*node*/,
1025 gint /*column*/, gpointer data)
1026 {
1027 gtk_widget_hide(GTK_WIDGET(data));
1028 }
1032 void on_tree_unselect_row_clear_text(GtkCTree */*tree*/, GtkCTreeNode */*node*/,
1033 gint /*column*/, gpointer data)
1034 {
1035 if (GTK_IS_EDITABLE(data)) {
1036 gtk_editable_delete_text(GTK_EDITABLE(data), 0, -1);
1037 } else if (GTK_IS_TEXT_VIEW(data)) {
1038 GtkTextBuffer *tb;
1039 tb = gtk_text_view_get_buffer(GTK_TEXT_VIEW(data));
1040 gtk_text_buffer_set_text(tb, "", 0);
1041 }
1042 }
1045 void on_attr_select_row(GtkCList *list, gint row, gint /*column*/,
1046 GdkEventButton */*event*/, gpointer /*data*/)
1047 {
1048 selected_attr = sp_xmlview_attr_list_get_row_key(list, row);
1049 gtk_window_set_focus(GTK_WINDOW(dlg), GTK_WIDGET(attr_value));
1051 attr_reset_context(selected_attr);
1052 }
1055 void on_attr_unselect_row(GtkCList */*list*/, gint /*row*/, gint /*column*/,
1056 GdkEventButton */*event*/, gpointer /*data*/)
1057 {
1058 selected_attr = 0;
1059 attr_reset_context(selected_attr);
1060 }
1063 void on_attr_row_changed(GtkCList *list, gint row, gpointer /*data*/)
1064 {
1065 gint attr = sp_xmlview_attr_list_get_row_key(list, row);
1067 if (attr == selected_attr) {
1068 /* if the attr changed, reselect the row in the list to sync
1069 the edit box */
1071 /*
1072 // get current attr values
1073 const gchar * name = g_quark_to_string (sp_xmlview_attr_list_get_row_key (list, row));
1074 const gchar * value = selected_repr->attribute(name);
1076 g_warning("value: '%s'",value);
1078 // get the edit box value
1079 GtkTextIter start, end;
1080 gtk_text_buffer_get_bounds ( gtk_text_view_get_buffer (attr_value),
1081 &start, &end );
1082 gchar * text = gtk_text_buffer_get_text ( gtk_text_view_get_buffer (attr_value),
1083 &start, &end, TRUE );
1084 g_warning("text: '%s'",text);
1086 // compare to edit box
1087 if (strcmp(text,value)) {
1088 // issue warning if they're different
1089 _message_stack->flash(Inkscape::WARNING_MESSAGE,
1090 _("Attribute changed in GUI while editing values!"));
1091 }
1092 g_free (text);
1094 */
1095 gtk_clist_unselect_row( GTK_CLIST(list), row, 0 );
1096 gtk_clist_select_row( GTK_CLIST(list), row, 0 );
1097 }
1098 }
1101 void on_attr_select_row_set_name_content(GtkCList *list, gint row,
1102 gint /*column*/, GdkEventButton */*event*/,
1103 gpointer data)
1104 {
1105 GtkEditable *editable = GTK_EDITABLE(data);
1106 const gchar *name = g_quark_to_string(sp_xmlview_attr_list_get_row_key(list, row));
1107 gtk_editable_delete_text(editable, 0, -1);
1108 gint pos = 0;
1109 gtk_editable_insert_text(editable, name, strlen(name), &pos);
1110 }
1114 void on_attr_select_row_set_value_content(GtkCList *list, gint row, gint /*column*/,
1115 GdkEventButton */*event*/,
1116 gpointer data)
1117 {
1118 GtkTextBuffer *tb = gtk_text_view_get_buffer(GTK_TEXT_VIEW(data));
1119 const gchar *name = g_quark_to_string(sp_xmlview_attr_list_get_row_key(list, row));
1120 const gchar *value = selected_repr->attribute(name);
1121 if (!value) {
1122 value = "";
1123 }
1124 gtk_text_buffer_set_text(tb, value, strlen(value));
1125 }
1128 void on_tree_select_row_enable_if_indentable(GtkCTree *tree, GtkCTreeNode *node,
1129 gint /*column*/, gpointer data)
1130 {
1131 gboolean indentable = FALSE;
1133 if (xml_tree_node_mutable(node)) {
1134 Inkscape::XML::Node *repr, *prev;
1135 repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
1137 Inkscape::XML::Node *parent=repr->parent();
1138 if ( parent && repr != parent->firstChild() ) {
1139 g_assert(parent->firstChild());
1141 // skip to the child just before the current repr
1142 for ( prev = parent->firstChild() ;
1143 prev && prev->next() != repr ;
1144 prev = prev->next() ){};
1146 if (prev && prev->type() == Inkscape::XML::ELEMENT_NODE) {
1147 indentable = TRUE;
1148 }
1149 }
1150 }
1152 gtk_widget_set_sensitive(GTK_WIDGET(data), indentable);
1153 }
1157 void on_tree_select_row_enable_if_not_first_child(GtkCTree *tree,
1158 GtkCTreeNode *node,
1159 gint /*column*/,
1160 gpointer data)
1161 {
1162 Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
1164 Inkscape::XML::Node *parent=repr->parent();
1165 if ( parent && repr != parent->firstChild() ) {
1166 gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
1167 } else {
1168 gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1169 }
1170 }
1174 void on_tree_select_row_enable_if_not_last_child(GtkCTree *tree,
1175 GtkCTreeNode *node,
1176 gint /*column*/, gpointer data)
1177 {
1178 Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
1180 Inkscape::XML::Node *parent=repr->parent();
1181 if ( parent && parent->parent() && repr->next() ) {
1182 gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
1183 } else {
1184 gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1185 }
1186 }
1190 void on_tree_select_row_enable_if_has_grandparent(GtkCTree */*tree*/,
1191 GtkCTreeNode *node,
1192 gint /*column*/, gpointer data)
1193 {
1194 GtkCTreeNode *parent = GTK_CTREE_ROW(node)->parent;
1196 if (parent) {
1197 GtkCTreeNode *grandparent = GTK_CTREE_ROW(parent)->parent;
1198 if (grandparent) {
1199 gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
1200 } else {
1201 gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1202 }
1203 } else {
1204 gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1205 }
1206 }
1210 void on_attr_select_row_enable(GtkCList */*list*/, gint /*row*/, gint /*column*/,
1211 GdkEventButton */*event*/, gpointer data)
1212 {
1213 gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
1214 }
1218 void on_attr_unselect_row_disable(GtkCList */*list*/, gint /*row*/, gint /*column*/,
1219 GdkEventButton */*event*/, gpointer data)
1220 {
1221 gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1222 }
1226 void on_attr_unselect_row_clear_text(GtkCList */*list*/, gint /*row*/, gint /*column*/,
1227 GdkEventButton */*event*/, gpointer data)
1228 {
1229 if (GTK_IS_EDITABLE(data)) {
1230 gtk_editable_delete_text(GTK_EDITABLE(data), 0, -1);
1231 } else if (GTK_IS_TEXT_VIEW(data)) {
1232 GtkTextBuffer *tb;
1233 tb = gtk_text_view_get_buffer(GTK_TEXT_VIEW(data));
1234 gtk_text_buffer_set_text(tb, "", 0);
1235 }
1236 }
1240 void on_editable_changed_enable_if_valid_xml_name(GtkEditable *editable,
1241 gpointer data)
1242 {
1243 gchar *text = gtk_editable_get_chars(editable, 0, -1);
1245 /* TODO: need to do checking a little more rigorous than this */
1247 if (strlen(text)) {
1248 gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
1249 } else {
1250 gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1251 }
1252 g_free(text);
1253 }
1257 void on_desktop_selection_changed(Inkscape::Selection */*selection*/)
1258 {
1259 if (!blocked++) {
1260 set_tree_select(get_dt_select());
1261 }
1262 blocked--;
1263 }
1265 static void on_document_replaced(SPDesktop *dt, SPDocument *doc)
1266 {
1267 if (current_desktop)
1268 sel_changed_connection.disconnect();
1270 sel_changed_connection = sp_desktop_selection(dt)->connectChanged(&on_desktop_selection_changed);
1271 set_tree_document(doc);
1272 }
1274 void on_document_uri_set(gchar const */*uri*/, SPDocument *document)
1275 {
1276 gchar title[500];
1277 sp_ui_dialog_title_string(Inkscape::Verb::get(SP_VERB_DIALOG_XML_EDITOR), title);
1278 gchar *t = g_strdup_printf("%s: %s", SP_DOCUMENT_NAME(document), title);
1279 gtk_window_set_title(GTK_WINDOW(dlg), t);
1280 g_free(t);
1281 }
1285 void on_clicked_get_editable_text(GtkWidget */*widget*/, gpointer data)
1286 {
1287 EditableDest *dest = (EditableDest *) data;
1288 dest->text = gtk_editable_get_chars(dest->editable, 0, -1);
1289 }
1291 gboolean
1292 quit_on_esc (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
1293 {
1294 switch (get_group0_keyval (event)) {
1295 case GDK_Escape: // defocus
1296 gtk_widget_destroy(w);
1297 return TRUE;
1298 }
1299 return FALSE;
1300 }
1302 void cmd_new_element_node(GtkObject */*object*/, gpointer /*data*/)
1303 {
1304 EditableDest name;
1305 GtkWidget *window, *create, *cancel, *vbox, *entry, *bbox, *sep;
1307 g_assert(selected_repr != NULL);
1309 window = sp_window_new(NULL, TRUE);
1310 gtk_container_set_border_width(GTK_CONTAINER(window), 4);
1311 gtk_window_set_title(GTK_WINDOW(window), _("New element node..."));
1312 gtk_window_set_policy(GTK_WINDOW(window), FALSE, FALSE, TRUE);
1313 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
1314 gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(dlg));
1315 gtk_window_set_modal(GTK_WINDOW(window), TRUE);
1316 gtk_signal_connect(GTK_OBJECT(window), "destroy", gtk_main_quit, NULL);
1317 gtk_signal_connect(GTK_OBJECT(window), "key-press-event", G_CALLBACK(quit_on_esc), window);
1319 vbox = gtk_vbox_new(FALSE, 4);
1320 gtk_container_add(GTK_CONTAINER(window), vbox);
1322 entry = gtk_entry_new();
1323 gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, TRUE, 0);
1325 sep = gtk_hseparator_new();
1326 gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, TRUE, 0);
1328 bbox = gtk_hbutton_box_new();
1329 gtk_container_set_border_width(GTK_CONTAINER(bbox), 4);
1330 gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
1331 gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, TRUE, 0);
1333 cancel = gtk_button_new_with_label(_("Cancel"));
1334 GTK_WIDGET_SET_FLAGS( GTK_WIDGET(cancel),
1335 GTK_CAN_DEFAULT );
1336 gtk_signal_connect_object( GTK_OBJECT(cancel), "clicked",
1337 G_CALLBACK(gtk_widget_destroy),
1338 GTK_OBJECT(window) );
1339 gtk_container_add(GTK_CONTAINER(bbox), cancel);
1341 create = gtk_button_new_with_label(_("Create"));
1342 gtk_widget_set_sensitive(GTK_WIDGET(create), FALSE);
1343 gtk_signal_connect( GTK_OBJECT(entry), "changed",
1344 G_CALLBACK(on_editable_changed_enable_if_valid_xml_name),
1345 create );
1346 gtk_signal_connect( GTK_OBJECT(create), "clicked",
1347 G_CALLBACK(on_clicked_get_editable_text), &name );
1348 gtk_signal_connect_object( GTK_OBJECT(create), "clicked",
1349 G_CALLBACK(gtk_widget_destroy),
1350 GTK_OBJECT(window) );
1351 GTK_WIDGET_SET_FLAGS( GTK_WIDGET(create),
1352 GTK_CAN_DEFAULT | GTK_RECEIVES_DEFAULT );
1353 gtk_container_add(GTK_CONTAINER(bbox), create);
1355 gtk_widget_show_all(GTK_WIDGET(window));
1356 gtk_window_set_default(GTK_WINDOW(window), GTK_WIDGET(create));
1357 gtk_window_set_focus(GTK_WINDOW(window), GTK_WIDGET(entry));
1359 name.editable = GTK_EDITABLE(entry);
1360 name.text = NULL;
1362 gtk_main();
1364 if (selected_repr != NULL && name.text) {
1365 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(current_document);
1366 Inkscape::XML::Node *new_repr;
1367 new_repr = xml_doc->createElement(name.text);
1368 Inkscape::GC::release(new_repr);
1369 g_free(name.text);
1370 selected_repr->appendChild(new_repr);
1371 set_tree_select(new_repr);
1372 set_dt_select(new_repr);
1374 sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1375 _("Create new element node"));
1376 }
1378 } // end of cmd_new_element_node()
1382 void cmd_new_text_node(GtkObject */*object*/, gpointer /*data*/)
1383 {
1384 g_assert(selected_repr != NULL);
1386 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(current_document);
1387 Inkscape::XML::Node *text = xml_doc->createTextNode("");
1388 selected_repr->appendChild(text);
1390 sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1391 _("Create new text node"));
1393 set_tree_select(text);
1394 set_dt_select(text);
1396 gtk_window_set_focus(GTK_WINDOW(dlg), GTK_WIDGET(content));
1398 }
1400 void cmd_duplicate_node(GtkObject */*object*/, gpointer /*data*/)
1401 {
1402 g_assert(selected_repr != NULL);
1404 Inkscape::XML::Node *parent = sp_repr_parent(selected_repr);
1405 Inkscape::XML::Node *dup = selected_repr->duplicate(parent->document());
1406 parent->addChild(dup, selected_repr);
1408 sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1409 _("Duplicate node"));
1411 GtkCTreeNode *node = sp_xmlview_tree_get_repr_node(SP_XMLVIEW_TREE(tree), dup);
1413 if (node) {
1414 gtk_ctree_select(GTK_CTREE(tree), node);
1415 }
1416 }
1420 void cmd_delete_node(GtkObject */*object*/, gpointer /*data*/)
1421 {
1422 g_assert(selected_repr != NULL);
1423 sp_repr_unparent(selected_repr);
1425 sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1426 Q_("nodeAsInXMLinHistoryDialog|Delete node"));
1427 }
1431 void cmd_delete_attr(GtkObject */*object*/, gpointer /*data*/)
1432 {
1433 g_assert(selected_repr != NULL);
1434 g_assert(selected_attr != 0);
1435 selected_repr->setAttribute(g_quark_to_string(selected_attr), NULL);
1437 SPObject *updated=current_document->getObjectByRepr(selected_repr);
1438 if (updated) {
1439 // force immediate update of dependant attributes
1440 updated->updateRepr();
1441 }
1443 sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1444 _("Delete attribute"));
1445 }
1449 void cmd_set_attr(GtkObject */*object*/, gpointer /*data*/)
1450 {
1451 g_assert(selected_repr != NULL);
1453 gchar *name = gtk_editable_get_chars(attr_name, 0, -1);
1454 GtkTextIter start;
1455 GtkTextIter end;
1456 gtk_text_buffer_get_bounds( gtk_text_view_get_buffer(attr_value),
1457 &start, &end );
1458 gchar *value = gtk_text_buffer_get_text( gtk_text_view_get_buffer(attr_value),
1459 &start, &end, TRUE );
1461 selected_repr->setAttribute(name, value, false);
1463 g_free(name);
1464 g_free(value);
1466 SPObject *updated = current_document->getObjectByRepr(selected_repr);
1467 if (updated) {
1468 // force immediate update of dependant attributes
1469 updated->updateRepr();
1470 }
1472 sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1473 _("Change attribute"));
1475 /* TODO: actually, the row won't have been created yet. why? */
1476 gint row = sp_xmlview_attr_list_find_row_from_key(GTK_CLIST(attributes),
1477 g_quark_from_string(name));
1478 if (row != -1) {
1479 gtk_clist_select_row(GTK_CLIST(attributes), row, 0);
1480 }
1481 }
1485 void cmd_raise_node(GtkObject */*object*/, gpointer /*data*/)
1486 {
1487 g_assert(selected_repr != NULL);
1489 Inkscape::XML::Node *parent = sp_repr_parent(selected_repr);
1490 g_return_if_fail(parent != NULL);
1491 g_return_if_fail(parent->firstChild() != selected_repr);
1493 Inkscape::XML::Node *ref = NULL;
1494 Inkscape::XML::Node *before = parent->firstChild();
1495 while (before && before->next() != selected_repr) {
1496 ref = before;
1497 before = before->next();
1498 }
1500 parent->changeOrder(selected_repr, ref);
1502 sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1503 _("Raise node"));
1505 set_tree_select(selected_repr);
1506 set_dt_select(selected_repr);
1507 }
1511 void cmd_lower_node(GtkObject */*object*/, gpointer /*data*/)
1512 {
1513 g_assert(selected_repr != NULL);
1514 g_return_if_fail(selected_repr->next() != NULL);
1515 Inkscape::XML::Node *parent = sp_repr_parent(selected_repr);
1517 parent->changeOrder(selected_repr, selected_repr->next());
1519 sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1520 _("Lower node"));
1522 set_tree_select(selected_repr);
1523 set_dt_select(selected_repr);
1524 }
1526 void cmd_indent_node(GtkObject */*object*/, gpointer /*data*/)
1527 {
1528 Inkscape::XML::Node *repr = selected_repr;
1529 g_assert(repr != NULL);
1530 Inkscape::XML::Node *parent = sp_repr_parent(repr);
1531 g_return_if_fail(parent != NULL);
1532 g_return_if_fail(parent->firstChild() != repr);
1534 Inkscape::XML::Node* prev = parent->firstChild();
1535 while (prev && prev->next() != repr) {
1536 prev = prev->next();
1537 }
1538 g_return_if_fail(prev != NULL);
1539 g_return_if_fail(prev->type() == Inkscape::XML::ELEMENT_NODE);
1541 Inkscape::XML::Node* ref = NULL;
1542 if (prev->firstChild()) {
1543 for( ref = prev->firstChild() ; ref->next() ; ref = ref->next() ){};
1544 }
1546 parent->removeChild(repr);
1547 prev->addChild(repr, ref);
1549 sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1550 _("Indent node"));
1551 set_tree_select(repr);
1552 set_dt_select(repr);
1554 } // end of cmd_indent_node()
1558 void cmd_unindent_node(GtkObject */*object*/, gpointer /*data*/)
1559 {
1560 Inkscape::XML::Node *repr = selected_repr;
1561 g_assert(repr != NULL);
1562 Inkscape::XML::Node *parent = sp_repr_parent(repr);
1563 g_return_if_fail(parent);
1564 Inkscape::XML::Node *grandparent = sp_repr_parent(parent);
1565 g_return_if_fail(grandparent);
1567 parent->removeChild(repr);
1568 grandparent->addChild(repr, parent);
1570 sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1571 _("Unindent node"));
1572 set_tree_select(repr);
1573 set_dt_select(repr);
1575 } // end of cmd_unindent_node()
1577 /** Returns true iff \a item is suitable to be included in the selection, in particular
1578 whether it has a bounding box in the desktop coordinate system for rendering resize handles.
1580 Descendents of <defs> nodes (markers etc.) return false, for example.
1581 */
1582 bool in_dt_coordsys(SPObject const &item)
1583 {
1584 /* Definition based on sp_item_i2doc_affine. */
1585 SPObject const *child = &item;
1586 g_return_val_if_fail(child != NULL, false);
1587 for(;;) {
1588 if (!SP_IS_ITEM(child)) {
1589 return false;
1590 }
1591 SPObject const * const parent = SP_OBJECT_PARENT(child);
1592 if (parent == NULL) {
1593 break;
1594 }
1595 child = parent;
1596 }
1597 g_assert(SP_IS_ROOT(child));
1598 /* Relevance: Otherwise, I'm not sure whether to return true or false. */
1599 return true;
1600 }
1603 /*
1604 Local Variables:
1605 mode:c++
1606 c-file-style:"stroustrup"
1607 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1608 indent-tabs-mode:nil
1609 fill-column:99
1610 End:
1611 */
1612 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :