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"
44 #include "util/ege-appear-time-tracker.h"
46 using Inkscape::DocumentUndo;
47 using ege::AppearTimeTracker;
49 #define MIN_ONSCREEN_DISTANCE 50
51 struct EditableDest {
52 GtkEditable *editable;
53 gchar *text;
54 };
56 static GtkWidget *dlg = NULL;
57 static sigc::connection sel_changed_connection;
58 static sigc::connection document_uri_set_connection;
59 static sigc::connection document_replaced_connection;
60 static win_data wd;
61 // impossible original values to make sure they are read from prefs
62 static gint x = -1000, y = -1000, w = 0, h = 0;
63 static Glib::ustring const prefs_path = "/dialogs/xml/";
64 static GtkWidget *status = NULL;
65 static Inkscape::MessageStack *_message_stack = NULL;
66 static Inkscape::MessageContext *_message_context = NULL;
67 static sigc::connection _message_changed_connection;
69 static GtkTooltips *tooltips = NULL;
70 static GtkEditable *attr_name = NULL;
71 static GtkTextView *attr_value = NULL;
72 static SPXMLViewTree *tree = NULL;
73 static SPXMLViewAttrList *attributes = NULL;
74 static SPXMLViewContent *content = NULL;
76 static gint blocked = 0;
77 static SPDesktop *current_desktop = NULL;
78 static SPDocument *current_document = NULL;
79 static gint selected_attr = 0;
80 static Inkscape::XML::Node *selected_repr = NULL;
82 static void sp_xmltree_desktop_activate( Inkscape::Application *inkscape, SPDesktop *desktop, GtkWidget *dialog );
83 static void sp_xmltree_desktop_deactivate( Inkscape::Application *inkscape, SPDesktop *desktop, GtkWidget *dialog );
85 static void set_tree_desktop(SPDesktop *desktop);
86 static void set_tree_document(SPDocument *document);
87 static void set_tree_repr(Inkscape::XML::Node *repr);
89 static void set_tree_select(Inkscape::XML::Node *repr);
90 static void propagate_tree_select(Inkscape::XML::Node *repr);
92 static Inkscape::XML::Node *get_dt_select();
93 static void set_dt_select(Inkscape::XML::Node *repr);
95 static void on_tree_select_row(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
96 static void on_tree_unselect_row(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
97 static void after_tree_move(GtkCTree *tree, GtkCTreeNode *node, GtkCTreeNode *new_parent, GtkCTreeNode *new_sibling, gpointer data);
98 static void on_destroy(GtkObject *object, gpointer data);
99 static gboolean on_delete(GtkObject *object, GdkEvent *event, gpointer data);
101 static void on_tree_select_row_enable_if_element(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
102 static void on_tree_select_row_enable_if_mutable(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
103 static void on_tree_select_row_show_if_element(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
104 static void on_tree_select_row_show_if_text(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
105 static void on_tree_select_row_enable_if_indentable(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
106 static void on_tree_select_row_enable_if_not_first_child(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
107 static void on_tree_select_row_enable_if_not_last_child(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
108 static void on_tree_select_row_enable_if_has_grandparent(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
110 static void on_tree_unselect_row_clear_text(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
111 static void on_tree_unselect_row_disable(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
112 static void on_tree_unselect_row_hide(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data);
114 static void on_attr_select_row(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
115 static void on_attr_unselect_row(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
116 static void on_attr_row_changed( GtkCList *list, gint row, gpointer data );
118 static void on_attr_select_row_enable(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
119 static void on_attr_unselect_row_disable(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
121 static void on_attr_select_row_set_name_content(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
122 static void on_attr_select_row_set_value_content(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
123 static void on_attr_unselect_row_clear_text(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
125 static void on_editable_changed_enable_if_valid_xml_name(GtkEditable *editable, gpointer data);
127 static void on_desktop_selection_changed(Inkscape::Selection *selection);
128 static void on_document_replaced(SPDesktop *dt, SPDocument *document);
129 static void on_document_uri_set(gchar const *uri, SPDocument *document);
131 static void on_clicked_get_editable_text(GtkWidget *widget, gpointer data);
133 static void _set_status_message(Inkscape::MessageType type, const gchar *message, GtkWidget *dialog);
135 static void cmd_new_element_node(GtkObject *object, gpointer data);
136 static void cmd_new_text_node(GtkObject *object, gpointer data);
137 static void cmd_duplicate_node(GtkObject *object, gpointer data);
138 static void cmd_delete_node(GtkObject *object, gpointer data);
140 static void cmd_raise_node(GtkObject *object, gpointer data);
141 static void cmd_lower_node(GtkObject *object, gpointer data);
142 static void cmd_indent_node(GtkObject *object, gpointer data);
143 static void cmd_unindent_node(GtkObject *object, gpointer data);
145 static void cmd_delete_attr(GtkObject *object, gpointer data);
146 static void cmd_set_attr(GtkObject *object, gpointer data);
148 static gboolean sp_xml_tree_key_press(GtkWidget *widget, GdkEventKey *event);
150 static bool in_dt_coordsys(SPObject const &item);
152 /*
153 * \brief Sets the XML status bar when the tree is selected.
154 */
155 void tree_reset_context()
156 {
157 _message_context->set(Inkscape::NORMAL_MESSAGE,
158 _("<b>Click</b> to select nodes, <b>drag</b> to rearrange."));
159 }
162 /*
163 * \brief Sets the XML status bar, depending on which attr is selected.
164 */
165 void attr_reset_context(gint attr)
166 {
167 if (attr == 0) {
168 _message_context->set(Inkscape::NORMAL_MESSAGE,
169 _("<b>Click</b> attribute to edit."));
170 }
171 else {
172 const gchar *name = g_quark_to_string(attr);
173 gchar *message = g_strdup_printf(_("Attribute <b>%s</b> selected. Press <b>Ctrl+Enter</b> when done editing to commit changes."), name);
174 _message_context->set(Inkscape::NORMAL_MESSAGE, message);
175 g_free(message);
176 }
177 }
180 void sp_xml_tree_dialog()
181 {
182 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
183 if (!desktop) {
184 return;
185 }
187 bool wantTiming = Inkscape::Preferences::get()->getBool("/dialogs/debug/trackAppear", false);
188 GTimer *timer = wantTiming ? g_timer_new() : 0;
190 if (dlg == NULL)
191 { // very long block
193 GtkWidget *box, *sw, *paned, *toolbar, *button;
194 GtkWidget *text_container, *attr_container, *attr_subpaned_container, *box2;
195 GtkWidget *set_attr;
197 tooltips = gtk_tooltips_new();
198 gtk_tooltips_enable(tooltips);
200 dlg = sp_window_new("", TRUE);
201 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
202 if (x == -1000 || y == -1000) {
203 x = prefs->getInt(prefs_path + "x", -1000);
204 y = prefs->getInt(prefs_path + "y", -1000);
205 }
206 if (w ==0 || h == 0) {
207 w = prefs->getInt(prefs_path + "w", 0);
208 h = prefs->getInt(prefs_path + "h", 0);
209 }
211 // if (x<0) x=0;
212 // if (y<0) y=0;
214 if (w && h) {
215 gtk_window_resize((GtkWindow *) dlg, w, h);
216 }
217 if (x >= 0 && y >= 0 && (x < (gdk_screen_width()-MIN_ONSCREEN_DISTANCE)) && (y < (gdk_screen_height()-MIN_ONSCREEN_DISTANCE))) {
218 gtk_window_move((GtkWindow *) dlg, x, y);
219 } else {
220 gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
221 }
223 sp_transientize(dlg);
224 wd.win = dlg;
225 wd.stop = 0;
226 g_signal_connect ( G_OBJECT(INKSCAPE), "activate_desktop", G_CALLBACK(sp_transientize_callback), &wd );
228 gtk_signal_connect( GTK_OBJECT(dlg), "event", GTK_SIGNAL_FUNC(sp_dialog_event_handler), dlg );
230 gtk_signal_connect( GTK_OBJECT(dlg), "destroy", G_CALLBACK(on_destroy), dlg);
231 gtk_signal_connect( GTK_OBJECT(dlg), "delete_event", G_CALLBACK(on_delete), dlg);
232 g_signal_connect ( G_OBJECT(INKSCAPE), "shut_down", G_CALLBACK(on_delete), dlg);
234 g_signal_connect ( G_OBJECT(INKSCAPE), "dialogs_hide", G_CALLBACK(sp_dialog_hide), dlg);
235 g_signal_connect ( G_OBJECT(INKSCAPE), "dialogs_unhide", G_CALLBACK(sp_dialog_unhide), dlg);
238 gtk_container_set_border_width(GTK_CONTAINER(dlg), 0);
239 gtk_window_set_default_size(GTK_WINDOW(dlg), 640, 384);
241 GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
242 gtk_container_add(GTK_CONTAINER(dlg), vbox);
244 GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
245 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
247 status = gtk_label_new(NULL);
248 gtk_misc_set_alignment(GTK_MISC(status), 0.0, 0.5);
249 gtk_widget_set_size_request(status, 1, -1);
250 gtk_label_set_markup(GTK_LABEL(status), "");
251 gtk_box_pack_start(GTK_BOX(hbox), gtk_hbox_new(FALSE, 0), FALSE, FALSE, 4);
252 gtk_box_pack_start(GTK_BOX(hbox), status, TRUE, TRUE, 0);
254 paned = gtk_hpaned_new();
255 gtk_paned_set_position(GTK_PANED(paned), 256);
256 gtk_box_pack_start(GTK_BOX(vbox), paned, TRUE, TRUE, 0);
258 _message_stack = new Inkscape::MessageStack();
259 _message_context = new Inkscape::MessageContext(_message_stack);
260 _message_changed_connection = _message_stack->connectChanged(
261 sigc::bind(sigc::ptr_fun(_set_status_message), dlg)
262 );
264 /* tree view */
266 box = gtk_vbox_new(FALSE, 0);
267 gtk_paned_pack1(GTK_PANED(paned), box, FALSE, FALSE);
269 tree = SP_XMLVIEW_TREE(sp_xmlview_tree_new(NULL, NULL, NULL));
270 gtk_tooltips_set_tip( tooltips, GTK_WIDGET(tree),
271 _("Drag to reorder nodes"), NULL );
273 g_signal_connect( G_OBJECT(tree), "tree_select_row",
274 G_CALLBACK(on_tree_select_row), NULL );
276 g_signal_connect( G_OBJECT(tree), "tree_unselect_row",
277 G_CALLBACK(on_tree_unselect_row), NULL );
279 g_signal_connect_after( G_OBJECT(tree), "tree_move",
280 G_CALLBACK(after_tree_move), NULL);
282 /* TODO: replace gtk_signal_connect_while_alive() with something
283 * else...
284 */
285 toolbar = gtk_toolbar_new();
286 gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
287 gtk_container_set_border_width(GTK_CONTAINER(toolbar), 0);
289 button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
290 NULL,
291 _("New element node"),
292 NULL,
293 sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
294 INKSCAPE_ICON_XML_ELEMENT_NEW ),
295 G_CALLBACK(cmd_new_element_node),
296 NULL);
298 gtk_signal_connect_while_alive( GTK_OBJECT(tree),
299 "tree_select_row",
300 G_CALLBACK(on_tree_select_row_enable_if_element),
301 button,
302 GTK_OBJECT(button));
304 gtk_signal_connect_while_alive(GTK_OBJECT(tree),
305 "tree_unselect_row",
306 G_CALLBACK(on_tree_unselect_row_disable),
307 button,
308 GTK_OBJECT(button));
310 gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
312 button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
313 NULL, _("New text node"), NULL,
314 sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
315 INKSCAPE_ICON_XML_TEXT_NEW ),
316 G_CALLBACK(cmd_new_text_node),
317 NULL);
319 gtk_signal_connect_while_alive(GTK_OBJECT(tree),
320 "tree_select_row",
321 G_CALLBACK(on_tree_select_row_enable_if_element),
322 button,
323 GTK_OBJECT(button));
325 gtk_signal_connect_while_alive(GTK_OBJECT(tree),
326 "tree_unselect_row",
327 G_CALLBACK(on_tree_unselect_row_disable),
328 button,
329 GTK_OBJECT(button));
331 gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
333 button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
334 NULL, _("Duplicate node"), NULL,
335 sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
336 INKSCAPE_ICON_XML_NODE_DUPLICATE ),
337 G_CALLBACK(cmd_duplicate_node),
338 NULL);
340 gtk_signal_connect_while_alive(GTK_OBJECT(tree),
341 "tree_select_row",
342 G_CALLBACK(on_tree_select_row_enable_if_mutable),
343 button,
344 GTK_OBJECT(button));
346 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
347 G_CALLBACK(on_tree_unselect_row_disable),
348 button, GTK_OBJECT(button));
350 gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
352 gtk_toolbar_append_space(GTK_TOOLBAR(toolbar));
354 button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
355 NULL, Q_("nodeAsInXMLdialogTooltip|Delete node"), NULL,
356 sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
357 INKSCAPE_ICON_XML_NODE_DELETE ),
358 G_CALLBACK(cmd_delete_node), NULL );
360 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
361 G_CALLBACK(on_tree_select_row_enable_if_mutable),
362 button, GTK_OBJECT(button));
363 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
364 G_CALLBACK(on_tree_unselect_row_disable),
365 button, GTK_OBJECT(button));
366 gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
368 gtk_toolbar_append_space(GTK_TOOLBAR(toolbar));
370 button = gtk_toolbar_append_item( GTK_TOOLBAR(toolbar), "<",
371 _("Unindent node"), NULL,
372 gtk_arrow_new(GTK_ARROW_LEFT, GTK_SHADOW_IN),
373 G_CALLBACK(cmd_unindent_node), NULL);
375 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
376 G_CALLBACK(on_tree_select_row_enable_if_has_grandparent),
377 button, GTK_OBJECT(button));
379 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
380 G_CALLBACK(on_tree_unselect_row_disable),
381 button, GTK_OBJECT(button));
383 gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
385 button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), ">",
386 _("Indent node"), NULL,
387 gtk_arrow_new(GTK_ARROW_RIGHT, GTK_SHADOW_IN),
388 G_CALLBACK(cmd_indent_node), NULL);
389 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
390 G_CALLBACK(on_tree_select_row_enable_if_indentable),
391 button, GTK_OBJECT(button));
392 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
393 (GCallback) on_tree_unselect_row_disable,
394 button, GTK_OBJECT(button));
395 gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
397 button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), "^",
398 _("Raise node"), NULL,
399 gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_IN),
400 G_CALLBACK(cmd_raise_node), NULL);
401 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
402 G_CALLBACK(on_tree_select_row_enable_if_not_first_child),
403 button, GTK_OBJECT(button));
404 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
405 G_CALLBACK(on_tree_unselect_row_disable),
406 button, GTK_OBJECT(button));
407 gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
409 button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), "v",
410 _("Lower node"), NULL,
411 gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_IN),
412 G_CALLBACK(cmd_lower_node), NULL);
413 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
414 G_CALLBACK(on_tree_select_row_enable_if_not_last_child),
415 button, GTK_OBJECT(button));
416 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
417 G_CALLBACK(on_tree_unselect_row_disable),
418 button, GTK_OBJECT(button));
419 gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
421 gtk_box_pack_start(GTK_BOX(box), toolbar, FALSE, TRUE, 0);
423 sw = gtk_scrolled_window_new(NULL, NULL);
424 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(sw),
425 GTK_POLICY_AUTOMATIC,
426 GTK_POLICY_AUTOMATIC );
427 gtk_box_pack_start(GTK_BOX(box), sw, TRUE, TRUE, 0);
429 gtk_container_add(GTK_CONTAINER(sw), GTK_WIDGET(tree));
431 /* node view */
433 box = gtk_vbox_new(FALSE, 0);
434 gtk_paned_pack2(GTK_PANED(paned), box, TRUE, TRUE);
436 /* attributes */
438 attr_container = gtk_vbox_new(FALSE, 0);
439 gtk_box_pack_start( GTK_BOX(box), GTK_WIDGET(attr_container),
440 TRUE, TRUE, 0 );
442 attributes = SP_XMLVIEW_ATTR_LIST(sp_xmlview_attr_list_new(NULL));
443 g_signal_connect( G_OBJECT(attributes), "select_row",
444 G_CALLBACK(on_attr_select_row), NULL);
445 g_signal_connect( G_OBJECT(attributes), "unselect_row",
446 G_CALLBACK(on_attr_unselect_row), NULL);
447 g_signal_connect( G_OBJECT(attributes), "row-value-changed",
448 G_CALLBACK(on_attr_row_changed), NULL);
450 toolbar = gtk_toolbar_new();
451 gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
452 gtk_container_set_border_width(GTK_CONTAINER(toolbar), 0);
454 button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
455 NULL, _("Delete attribute"), NULL,
456 sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
457 INKSCAPE_ICON_XML_ATTRIBUTE_DELETE ),
458 (GCallback) cmd_delete_attr, NULL);
460 gtk_signal_connect_while_alive(GTK_OBJECT(attributes), "select_row",
461 (GCallback) on_attr_select_row_enable, button,
462 GTK_OBJECT(button));
464 gtk_signal_connect_while_alive(GTK_OBJECT(attributes), "unselect_row",
465 (GCallback) on_attr_unselect_row_disable, button,
466 GTK_OBJECT(button));
468 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
469 (GCallback) on_tree_unselect_row_disable, button,
470 GTK_OBJECT(button));
472 gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
474 gtk_box_pack_start( GTK_BOX(attr_container),
475 GTK_WIDGET(toolbar), FALSE, TRUE, 0 );
477 attr_subpaned_container = gtk_vpaned_new();
478 gtk_box_pack_start( GTK_BOX(attr_container),
479 GTK_WIDGET(attr_subpaned_container),
480 TRUE, TRUE, 0 );
481 gtk_widget_show(attr_subpaned_container);
483 sw = gtk_scrolled_window_new(NULL, NULL);
484 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
485 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
486 gtk_paned_pack1( GTK_PANED(attr_subpaned_container),
487 GTK_WIDGET(sw), TRUE, TRUE );
488 gtk_container_add(GTK_CONTAINER(sw), GTK_WIDGET(attributes));
490 toolbar = gtk_vbox_new(FALSE, 4);
491 gtk_container_set_border_width(GTK_CONTAINER(toolbar), 4);
493 box2 = gtk_hbox_new(FALSE, 4);
494 gtk_box_pack_start( GTK_BOX(toolbar), GTK_WIDGET(box2),
495 FALSE, TRUE, 0);
497 attr_name = GTK_EDITABLE(gtk_entry_new());
498 gtk_tooltips_set_tip( tooltips, GTK_WIDGET(attr_name),
499 // TRANSLATORS: "Attribute" is a noun here
500 _("Attribute name"), NULL );
502 gtk_signal_connect( GTK_OBJECT(attributes), "select_row",
503 (GCallback) on_attr_select_row_set_name_content,
504 attr_name);
506 gtk_signal_connect( GTK_OBJECT(attributes), "unselect_row",
507 (GCallback) on_attr_unselect_row_clear_text,
508 attr_name);
510 gtk_signal_connect( GTK_OBJECT(tree), "tree_unselect_row",
511 (GCallback) on_tree_unselect_row_clear_text,
512 attr_name);
514 gtk_box_pack_start( GTK_BOX(box2), GTK_WIDGET(attr_name),
515 TRUE, TRUE, 0);
517 set_attr = gtk_button_new();
518 gtk_tooltips_set_tip( tooltips, GTK_WIDGET(set_attr),
519 // TRANSLATORS: "Set" is a verb here
520 _("Set attribute"), NULL );
521 // TRANSLATORS: "Set" is a verb here
522 GtkWidget *set_label = gtk_label_new(_("Set"));
523 gtk_container_add(GTK_CONTAINER(set_attr), set_label);
525 gtk_signal_connect( GTK_OBJECT(set_attr), "clicked",
526 (GCallback) cmd_set_attr, NULL);
527 gtk_signal_connect( GTK_OBJECT(attr_name), "changed",
528 (GCallback) on_editable_changed_enable_if_valid_xml_name,
529 set_attr );
530 gtk_widget_set_sensitive(GTK_WIDGET(set_attr), FALSE);
532 gtk_box_pack_start(GTK_BOX(box2), set_attr, FALSE, FALSE, 0);
534 sw = gtk_scrolled_window_new(NULL, NULL);
535 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(sw),
536 GTK_POLICY_AUTOMATIC,
537 GTK_POLICY_AUTOMATIC );
538 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN );
539 gtk_box_pack_start(GTK_BOX(toolbar), sw, TRUE, TRUE, 0);
541 attr_value =(GtkTextView *) gtk_text_view_new();
542 gtk_text_view_set_wrap_mode((GtkTextView *) attr_value, GTK_WRAP_CHAR);
543 gtk_tooltips_set_tip( tooltips, GTK_WIDGET(attr_value),
544 // TRANSLATORS: "Attribute" is a noun here
545 _("Attribute value"), NULL );
546 gtk_signal_connect( GTK_OBJECT(attributes), "select_row",
547 (GCallback) on_attr_select_row_set_value_content,
548 attr_value );
549 gtk_signal_connect( GTK_OBJECT(attributes), "unselect_row",
550 (GCallback) on_attr_unselect_row_clear_text,
551 attr_value );
552 gtk_signal_connect( GTK_OBJECT(tree), "tree_unselect_row",
553 (GCallback) on_tree_unselect_row_clear_text,
554 attr_value );
555 gtk_text_view_set_editable(attr_value, TRUE);
556 gtk_container_add( GTK_CONTAINER(sw),
557 GTK_WIDGET(attr_value) );
559 gtk_paned_pack2( GTK_PANED(attr_subpaned_container),
560 GTK_WIDGET(toolbar), FALSE, TRUE );
562 /* text */
564 sw = gtk_scrolled_window_new(NULL, NULL);
565 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(sw),
566 GTK_POLICY_AUTOMATIC,
567 GTK_POLICY_AUTOMATIC );
568 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN );
569 gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(sw), TRUE, TRUE, 0);
571 content = SP_XMLVIEW_CONTENT(sp_xmlview_content_new(NULL));
572 gtk_container_add(GTK_CONTAINER(sw), GTK_WIDGET(content));
574 text_container = sw;
576 /* initial show/hide */
578 gtk_widget_show_all(GTK_WIDGET(dlg));
580 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
581 (GCallback) on_tree_select_row_show_if_element,
582 attr_container, GTK_OBJECT(attr_container));
584 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
585 (GCallback) on_tree_unselect_row_hide,
586 attr_container, GTK_OBJECT(attr_container));
588 gtk_widget_hide(attr_container);
590 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
591 (GCallback) on_tree_select_row_show_if_text,
592 text_container, GTK_OBJECT(text_container));
594 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
595 (GCallback) on_tree_unselect_row_hide,
596 text_container, GTK_OBJECT(text_container));
598 gtk_widget_hide(text_container);
600 g_signal_connect( G_OBJECT(INKSCAPE), "activate_desktop",
601 G_CALLBACK(sp_xmltree_desktop_activate), dlg);
603 g_signal_connect( G_OBJECT(INKSCAPE), "deactivate_desktop",
604 G_CALLBACK(sp_xmltree_desktop_deactivate), dlg);
606 g_signal_connect((GObject *) dlg, "key_press_event", (GCallback) sp_xml_tree_key_press, NULL);
608 tree_reset_context();
609 } // end of if (dlg == NULL)
611 if ( wantTiming ) {
612 // Time tracker takes ownership of the timer.
613 AppearTimeTracker *tracker = new AppearTimeTracker(timer, GTK_WIDGET(dlg), "DialogXMLEditor");
614 tracker->setAutodelete(true);
615 timer = 0;
616 }
618 gtk_window_present((GtkWindow *) dlg);
620 g_assert(desktop != NULL);
621 set_tree_desktop(desktop);
623 } // end of sp_xml_tree_dialog()
625 static gboolean sp_xml_tree_key_press(GtkWidget */*widget*/, GdkEventKey *event)
626 {
628 unsigned int shortcut = get_group0_keyval(event) |
629 ( event->state & GDK_SHIFT_MASK ?
630 SP_SHORTCUT_SHIFT_MASK : 0 ) |
631 ( event->state & GDK_CONTROL_MASK ?
632 SP_SHORTCUT_CONTROL_MASK : 0 ) |
633 ( event->state & GDK_MOD1_MASK ?
634 SP_SHORTCUT_ALT_MASK : 0 );
636 /* fixme: if you need to add more xml-tree-specific callbacks, you should probably upgrade
637 * the sp_shortcut mechanism to take into account windows. */
638 if (shortcut == (SP_SHORTCUT_CONTROL_MASK | GDK_Return)) {
639 cmd_set_attr(NULL, NULL);
640 return true;
641 }
642 return false;
643 }
646 static void sp_xmltree_desktop_activate(Inkscape::Application */*inkscape*/,
647 SPDesktop *desktop,
648 GtkWidget */*dialog*/ )
649 {
650 set_tree_desktop(desktop);
651 }
653 static void sp_xmltree_desktop_deactivate(Inkscape::Application */*inkscape*/,
654 SPDesktop */*desktop*/,
655 GtkWidget */*dialog*/ )
656 {
657 set_tree_desktop(NULL);
658 }
661 void set_tree_desktop(SPDesktop *desktop)
662 {
663 if ( desktop == current_desktop ) {
664 return;
665 }
667 if (current_desktop) {
668 sel_changed_connection.disconnect();
669 document_replaced_connection.disconnect();
670 }
671 current_desktop = desktop;
672 if (desktop) {
673 sel_changed_connection = sp_desktop_selection(desktop)->connectChanged(&on_desktop_selection_changed);
674 document_replaced_connection = desktop->connectDocumentReplaced(&on_document_replaced);
675 set_tree_document(sp_desktop_document(desktop));
676 } else {
677 set_tree_document(NULL);
678 }
680 } // end of set_tree_desktop()
684 void set_tree_document(SPDocument *document)
685 {
686 if (document == current_document) {
687 return;
688 }
690 if (current_document) {
691 document_uri_set_connection.disconnect();
692 }
693 current_document = document;
694 if (current_document) {
696 document_uri_set_connection = current_document->connectURISet(sigc::bind(sigc::ptr_fun(&on_document_uri_set), current_document));
697 on_document_uri_set( current_document->getURI(), current_document );
698 set_tree_repr(current_document->getReprRoot());
699 } else {
700 set_tree_repr(NULL);
701 }
702 }
706 void set_tree_repr(Inkscape::XML::Node *repr)
707 {
708 if (repr == selected_repr) {
709 return;
710 }
712 gtk_clist_freeze(GTK_CLIST(tree));
714 sp_xmlview_tree_set_repr(tree, repr);
716 if (repr) {
717 set_tree_select(get_dt_select());
718 } else {
719 set_tree_select(NULL);
720 }
722 gtk_clist_thaw(GTK_CLIST(tree));
724 propagate_tree_select(selected_repr);
726 }
730 void set_tree_select(Inkscape::XML::Node *repr)
731 {
732 if (selected_repr) {
733 Inkscape::GC::release(selected_repr);
734 }
736 selected_repr = repr;
737 if (repr) {
738 GtkCTreeNode *node;
740 Inkscape::GC::anchor(selected_repr);
742 node = sp_xmlview_tree_get_repr_node(SP_XMLVIEW_TREE(tree), repr);
743 if (node) {
744 GtkCTreeNode *parent;
746 gtk_ctree_select(GTK_CTREE(tree), node);
748 parent = GTK_CTREE_ROW(node)->parent;
749 while (parent) {
750 gtk_ctree_expand(GTK_CTREE(tree), parent);
751 parent = GTK_CTREE_ROW(parent)->parent;
752 }
754 gtk_ctree_node_moveto(GTK_CTREE(tree), node, 0, 0.66, 0.0);
755 }
756 } else {
757 gtk_clist_unselect_all(GTK_CLIST(tree));
758 }
759 propagate_tree_select(repr);
760 }
764 void propagate_tree_select(Inkscape::XML::Node *repr)
765 {
766 if (repr && repr->type() == Inkscape::XML::ELEMENT_NODE) {
767 sp_xmlview_attr_list_set_repr(attributes, repr);
768 } else {
769 sp_xmlview_attr_list_set_repr(attributes, NULL);
770 }
772 if (repr && ( repr->type() == Inkscape::XML::TEXT_NODE || repr->type() == Inkscape::XML::COMMENT_NODE || repr->type() == Inkscape::XML::PI_NODE ) ) {
773 sp_xmlview_content_set_repr(content, repr);
774 } else {
775 sp_xmlview_content_set_repr(content, NULL);
776 }
777 }
780 Inkscape::XML::Node *get_dt_select()
781 {
782 if (!current_desktop) {
783 return NULL;
784 }
786 return sp_desktop_selection(current_desktop)->singleRepr();
787 }
791 void set_dt_select(Inkscape::XML::Node *repr)
792 {
793 if (!current_desktop) {
794 return;
795 }
797 Inkscape::Selection *selection = sp_desktop_selection(current_desktop);
799 SPObject *object;
800 if (repr) {
801 while ( ( repr->type() != Inkscape::XML::ELEMENT_NODE )
802 && sp_repr_parent(repr) )
803 {
804 repr = sp_repr_parent(repr);
805 } // end of while loop
807 object = sp_desktop_document(current_desktop)->getObjectByRepr(repr);
808 } else {
809 object = NULL;
810 }
812 blocked++;
813 if ( object && in_dt_coordsys(*object)
814 && !(SP_IS_STRING(object) ||
815 SP_IS_ROOT(object) ) )
816 {
817 /* We cannot set selection to root or string - they are not items and selection is not
818 * equipped to deal with them */
819 selection->set(SP_ITEM(object));
820 }
821 blocked--;
823 } // end of set_dt_select()
826 void on_tree_select_row(GtkCTree *tree,
827 GtkCTreeNode *node,
828 gint /*column*/,
829 gpointer /*data*/)
830 {
831 if (blocked) {
832 return;
833 }
835 Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
836 g_assert(repr != NULL);
838 if (selected_repr == repr) {
839 return;
840 }
842 if (selected_repr) {
843 Inkscape::GC::release(selected_repr);
844 selected_repr = NULL;
845 }
846 selected_repr = repr;
847 Inkscape::GC::anchor(selected_repr);
849 propagate_tree_select(selected_repr);
851 set_dt_select(selected_repr);
853 tree_reset_context();
854 }
856 void on_tree_unselect_row(GtkCTree *tree,
857 GtkCTreeNode *node,
858 gint /*column*/,
859 gpointer /*data*/)
860 {
861 if (blocked) {
862 return;
863 }
865 Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
866 propagate_tree_select(NULL);
867 set_dt_select(NULL);
869 if (selected_repr && (selected_repr == repr)) {
870 Inkscape::GC::release(selected_repr);
871 selected_repr = NULL;
872 selected_attr = 0;
873 }
874 }
878 void after_tree_move(GtkCTree */*tree*/,
879 GtkCTreeNode *node,
880 GtkCTreeNode *new_parent,
881 GtkCTreeNode *new_sibling,
882 gpointer /*data*/)
883 {
884 if (GTK_CTREE_ROW(node)->parent == new_parent &&
885 GTK_CTREE_ROW(node)->sibling == new_sibling)
886 {
887 DocumentUndo::done(current_document, SP_VERB_DIALOG_XML_EDITOR,
888 _("Drag XML subtree"));
889 } else {
890 DocumentUndo::cancel(current_document);
891 }
892 }
895 static void on_destroy(GtkObject */*object*/, gpointer /*data*/)
896 {
897 set_tree_desktop(NULL);
898 gtk_object_destroy(GTK_OBJECT(tooltips));
899 tooltips = NULL;
900 sp_signal_disconnect_by_data(INKSCAPE, dlg);
901 wd.win = dlg = NULL;
902 wd.stop = 0;
904 _message_changed_connection.disconnect();
905 delete _message_context;
906 _message_context = NULL;
907 Inkscape::GC::release(_message_stack);
908 _message_stack = NULL;
909 _message_changed_connection.~connection();
911 status = NULL;
912 }
916 static gboolean on_delete(GtkObject */*object*/, GdkEvent */*event*/, gpointer /*data*/)
917 {
918 gtk_window_get_position((GtkWindow *) dlg, &x, &y);
919 gtk_window_get_size((GtkWindow *) dlg, &w, &h);
921 if (x<0) x=0;
922 if (y<0) y=0;
924 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
925 prefs->setInt(prefs_path + "x", x);
926 prefs->setInt(prefs_path + "y", y);
927 prefs->setInt(prefs_path + "w", w);
928 prefs->setInt(prefs_path + "h", h);
930 return FALSE; // which means, go ahead and destroy it
931 }
934 static void _set_status_message(Inkscape::MessageType /*type*/, const gchar *message, GtkWidget */*dialog*/)
935 {
936 if (status) {
937 gtk_label_set_markup(GTK_LABEL(status), message ? message : "");
938 }
939 }
942 void on_tree_select_row_enable(GtkCTree */*tree*/,
943 GtkCTreeNode */*node*/,
944 gint /*column*/,
945 gpointer data)
946 {
947 gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
948 }
952 void on_tree_select_row_enable_if_element(GtkCTree *tree,
953 GtkCTreeNode *node,
954 gint /*column*/,
955 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_set_sensitive(GTK_WIDGET(data), TRUE);
961 } else {
962 gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
963 }
964 }
968 void on_tree_select_row_show_if_element(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::ELEMENT_NODE) {
974 gtk_widget_show(GTK_WIDGET(data));
975 } else {
976 gtk_widget_hide(GTK_WIDGET(data));
977 }
978 }
982 void on_tree_select_row_show_if_text(GtkCTree *tree, GtkCTreeNode *node,
983 gint /*column*/, gpointer data)
984 {
985 Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
987 if ( repr->type() == Inkscape::XML::TEXT_NODE || repr->type() == Inkscape::XML::COMMENT_NODE || repr->type() == Inkscape::XML::PI_NODE ) {
988 gtk_widget_show(GTK_WIDGET(data));
989 } else {
990 gtk_widget_hide(GTK_WIDGET(data));
991 }
992 }
995 gboolean xml_tree_node_mutable(GtkCTreeNode *node)
996 {
997 // top-level is immutable, obviously
998 if (!GTK_CTREE_ROW(node)->parent) {
999 return false;
1000 }
1002 // if not in base level (where namedview, defs, etc go), we're mutable
1003 if (GTK_CTREE_ROW(GTK_CTREE_ROW(node)->parent)->parent) {
1004 return true;
1005 }
1007 Inkscape::XML::Node *repr;
1008 repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
1009 g_assert(repr);
1011 // don't let "defs" or "namedview" disappear
1012 if ( !strcmp(repr->name(),"svg:defs") ||
1013 !strcmp(repr->name(),"sodipodi:namedview") ) {
1014 return false;
1015 }
1017 // everyone else is okay, I guess. :)
1018 return true;
1019 }
1022 void on_tree_select_row_enable_if_mutable(GtkCTree */*tree*/, GtkCTreeNode *node,
1023 gint /*column*/, gpointer data)
1024 {
1025 gtk_widget_set_sensitive(GTK_WIDGET(data), xml_tree_node_mutable(node));
1026 }
1030 void on_tree_unselect_row_disable(GtkCTree */*tree*/, GtkCTreeNode */*node*/,
1031 gint /*column*/, gpointer data)
1032 {
1033 gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1034 }
1038 void on_tree_unselect_row_hide(GtkCTree */*tree*/, GtkCTreeNode */*node*/,
1039 gint /*column*/, gpointer data)
1040 {
1041 gtk_widget_hide(GTK_WIDGET(data));
1042 }
1046 void on_tree_unselect_row_clear_text(GtkCTree */*tree*/, GtkCTreeNode */*node*/,
1047 gint /*column*/, gpointer data)
1048 {
1049 if (GTK_IS_EDITABLE(data)) {
1050 gtk_editable_delete_text(GTK_EDITABLE(data), 0, -1);
1051 } else if (GTK_IS_TEXT_VIEW(data)) {
1052 GtkTextBuffer *tb;
1053 tb = gtk_text_view_get_buffer(GTK_TEXT_VIEW(data));
1054 gtk_text_buffer_set_text(tb, "", 0);
1055 }
1056 }
1059 void on_attr_select_row(GtkCList *list, gint row, gint /*column*/,
1060 GdkEventButton */*event*/, gpointer /*data*/)
1061 {
1062 selected_attr = sp_xmlview_attr_list_get_row_key(list, row);
1063 gtk_window_set_focus(GTK_WINDOW(dlg), GTK_WIDGET(attr_value));
1065 attr_reset_context(selected_attr);
1066 }
1069 void on_attr_unselect_row(GtkCList */*list*/, gint /*row*/, gint /*column*/,
1070 GdkEventButton */*event*/, gpointer /*data*/)
1071 {
1072 selected_attr = 0;
1073 attr_reset_context(selected_attr);
1074 }
1077 void on_attr_row_changed(GtkCList *list, gint row, gpointer /*data*/)
1078 {
1079 gint attr = sp_xmlview_attr_list_get_row_key(list, row);
1081 if (attr == selected_attr) {
1082 /* if the attr changed, reselect the row in the list to sync
1083 the edit box */
1085 /*
1086 // get current attr values
1087 const gchar * name = g_quark_to_string (sp_xmlview_attr_list_get_row_key (list, row));
1088 const gchar * value = selected_repr->attribute(name);
1090 g_warning("value: '%s'",value);
1092 // get the edit box value
1093 GtkTextIter start, end;
1094 gtk_text_buffer_get_bounds ( gtk_text_view_get_buffer (attr_value),
1095 &start, &end );
1096 gchar * text = gtk_text_buffer_get_text ( gtk_text_view_get_buffer (attr_value),
1097 &start, &end, TRUE );
1098 g_warning("text: '%s'",text);
1100 // compare to edit box
1101 if (strcmp(text,value)) {
1102 // issue warning if they're different
1103 _message_stack->flash(Inkscape::WARNING_MESSAGE,
1104 _("Attribute changed in GUI while editing values!"));
1105 }
1106 g_free (text);
1108 */
1109 gtk_clist_unselect_row( GTK_CLIST(list), row, 0 );
1110 gtk_clist_select_row( GTK_CLIST(list), row, 0 );
1111 }
1112 }
1115 void on_attr_select_row_set_name_content(GtkCList *list, gint row,
1116 gint /*column*/, GdkEventButton */*event*/,
1117 gpointer data)
1118 {
1119 GtkEditable *editable = GTK_EDITABLE(data);
1120 const gchar *name = g_quark_to_string(sp_xmlview_attr_list_get_row_key(list, row));
1121 gtk_editable_delete_text(editable, 0, -1);
1122 gint pos = 0;
1123 gtk_editable_insert_text(editable, name, strlen(name), &pos);
1124 }
1128 void on_attr_select_row_set_value_content(GtkCList *list, gint row, gint /*column*/,
1129 GdkEventButton */*event*/,
1130 gpointer data)
1131 {
1132 GtkTextBuffer *tb = gtk_text_view_get_buffer(GTK_TEXT_VIEW(data));
1133 const gchar *name = g_quark_to_string(sp_xmlview_attr_list_get_row_key(list, row));
1134 const gchar *value = selected_repr->attribute(name);
1135 if (!value) {
1136 value = "";
1137 }
1138 gtk_text_buffer_set_text(tb, value, strlen(value));
1139 }
1142 void on_tree_select_row_enable_if_indentable(GtkCTree *tree, GtkCTreeNode *node,
1143 gint /*column*/, gpointer data)
1144 {
1145 gboolean indentable = FALSE;
1147 if (xml_tree_node_mutable(node)) {
1148 Inkscape::XML::Node *repr, *prev;
1149 repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
1151 Inkscape::XML::Node *parent=repr->parent();
1152 if ( parent && repr != parent->firstChild() ) {
1153 g_assert(parent->firstChild());
1155 // skip to the child just before the current repr
1156 for ( prev = parent->firstChild() ;
1157 prev && prev->next() != repr ;
1158 prev = prev->next() ){};
1160 if (prev && prev->type() == Inkscape::XML::ELEMENT_NODE) {
1161 indentable = TRUE;
1162 }
1163 }
1164 }
1166 gtk_widget_set_sensitive(GTK_WIDGET(data), indentable);
1167 }
1171 void on_tree_select_row_enable_if_not_first_child(GtkCTree *tree,
1172 GtkCTreeNode *node,
1173 gint /*column*/,
1174 gpointer data)
1175 {
1176 Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
1178 Inkscape::XML::Node *parent=repr->parent();
1179 if ( parent && repr != parent->firstChild() ) {
1180 gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
1181 } else {
1182 gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1183 }
1184 }
1188 void on_tree_select_row_enable_if_not_last_child(GtkCTree *tree,
1189 GtkCTreeNode *node,
1190 gint /*column*/, gpointer data)
1191 {
1192 Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
1194 Inkscape::XML::Node *parent=repr->parent();
1195 if ( parent && parent->parent() && repr->next() ) {
1196 gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
1197 } else {
1198 gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1199 }
1200 }
1204 void on_tree_select_row_enable_if_has_grandparent(GtkCTree */*tree*/,
1205 GtkCTreeNode *node,
1206 gint /*column*/, gpointer data)
1207 {
1208 GtkCTreeNode *parent = GTK_CTREE_ROW(node)->parent;
1210 if (parent) {
1211 GtkCTreeNode *grandparent = GTK_CTREE_ROW(parent)->parent;
1212 if (grandparent) {
1213 gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
1214 } else {
1215 gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1216 }
1217 } else {
1218 gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1219 }
1220 }
1224 void on_attr_select_row_enable(GtkCList */*list*/, gint /*row*/, gint /*column*/,
1225 GdkEventButton */*event*/, gpointer data)
1226 {
1227 gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
1228 }
1232 void on_attr_unselect_row_disable(GtkCList */*list*/, gint /*row*/, gint /*column*/,
1233 GdkEventButton */*event*/, gpointer data)
1234 {
1235 gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1236 }
1240 void on_attr_unselect_row_clear_text(GtkCList */*list*/, gint /*row*/, gint /*column*/,
1241 GdkEventButton */*event*/, gpointer data)
1242 {
1243 if (GTK_IS_EDITABLE(data)) {
1244 gtk_editable_delete_text(GTK_EDITABLE(data), 0, -1);
1245 } else if (GTK_IS_TEXT_VIEW(data)) {
1246 GtkTextBuffer *tb;
1247 tb = gtk_text_view_get_buffer(GTK_TEXT_VIEW(data));
1248 gtk_text_buffer_set_text(tb, "", 0);
1249 }
1250 }
1254 void on_editable_changed_enable_if_valid_xml_name(GtkEditable *editable,
1255 gpointer data)
1256 {
1257 gchar *text = gtk_editable_get_chars(editable, 0, -1);
1259 /* TODO: need to do checking a little more rigorous than this */
1261 if (strlen(text)) {
1262 gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
1263 } else {
1264 gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1265 }
1266 g_free(text);
1267 }
1271 void on_desktop_selection_changed(Inkscape::Selection */*selection*/)
1272 {
1273 if (!blocked++) {
1274 set_tree_select(get_dt_select());
1275 }
1276 blocked--;
1277 }
1279 static void on_document_replaced(SPDesktop *dt, SPDocument *doc)
1280 {
1281 if (current_desktop)
1282 sel_changed_connection.disconnect();
1284 sel_changed_connection = sp_desktop_selection(dt)->connectChanged(&on_desktop_selection_changed);
1285 set_tree_document(doc);
1286 }
1288 void on_document_uri_set(gchar const */*uri*/, SPDocument *document)
1289 {
1290 gchar title[500];
1291 sp_ui_dialog_title_string(Inkscape::Verb::get(SP_VERB_DIALOG_XML_EDITOR), title);
1292 gchar *t = g_strdup_printf("%s: %s", document->getName(), title);
1293 gtk_window_set_title(GTK_WINDOW(dlg), t);
1294 g_free(t);
1295 }
1299 void on_clicked_get_editable_text(GtkWidget */*widget*/, gpointer data)
1300 {
1301 EditableDest *dest = (EditableDest *) data;
1302 dest->text = gtk_editable_get_chars(dest->editable, 0, -1);
1303 }
1305 gboolean
1306 quit_on_esc (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
1307 {
1308 switch (get_group0_keyval (event)) {
1309 case GDK_Escape: // defocus
1310 gtk_widget_destroy(w);
1311 return TRUE;
1312 }
1313 return FALSE;
1314 }
1316 void cmd_new_element_node(GtkObject */*object*/, gpointer /*data*/)
1317 {
1318 EditableDest name;
1319 GtkWidget *window, *create, *cancel, *vbox, *entry, *bbox, *sep;
1321 g_assert(selected_repr != NULL);
1323 window = sp_window_new(NULL, TRUE);
1324 gtk_container_set_border_width(GTK_CONTAINER(window), 4);
1325 gtk_window_set_title(GTK_WINDOW(window), _("New element node..."));
1326 gtk_window_set_policy(GTK_WINDOW(window), FALSE, FALSE, TRUE);
1327 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
1328 gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(dlg));
1329 gtk_window_set_modal(GTK_WINDOW(window), TRUE);
1330 gtk_signal_connect(GTK_OBJECT(window), "destroy", gtk_main_quit, NULL);
1331 gtk_signal_connect(GTK_OBJECT(window), "key-press-event", G_CALLBACK(quit_on_esc), window);
1333 vbox = gtk_vbox_new(FALSE, 4);
1334 gtk_container_add(GTK_CONTAINER(window), vbox);
1336 entry = gtk_entry_new();
1337 gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, TRUE, 0);
1339 sep = gtk_hseparator_new();
1340 gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, TRUE, 0);
1342 bbox = gtk_hbutton_box_new();
1343 gtk_container_set_border_width(GTK_CONTAINER(bbox), 4);
1344 gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
1345 gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, TRUE, 0);
1347 cancel = gtk_button_new_with_label(_("Cancel"));
1348 GTK_WIDGET_SET_FLAGS( GTK_WIDGET(cancel),
1349 GTK_CAN_DEFAULT );
1350 gtk_signal_connect_object( GTK_OBJECT(cancel), "clicked",
1351 G_CALLBACK(gtk_widget_destroy),
1352 GTK_OBJECT(window) );
1353 gtk_container_add(GTK_CONTAINER(bbox), cancel);
1355 create = gtk_button_new_with_label(_("Create"));
1356 gtk_widget_set_sensitive(GTK_WIDGET(create), FALSE);
1357 gtk_signal_connect( GTK_OBJECT(entry), "changed",
1358 G_CALLBACK(on_editable_changed_enable_if_valid_xml_name),
1359 create );
1360 gtk_signal_connect( GTK_OBJECT(create), "clicked",
1361 G_CALLBACK(on_clicked_get_editable_text), &name );
1362 gtk_signal_connect_object( GTK_OBJECT(create), "clicked",
1363 G_CALLBACK(gtk_widget_destroy),
1364 GTK_OBJECT(window) );
1365 GTK_WIDGET_SET_FLAGS( GTK_WIDGET(create),
1366 GTK_CAN_DEFAULT | GTK_RECEIVES_DEFAULT );
1367 gtk_container_add(GTK_CONTAINER(bbox), create);
1369 gtk_widget_show_all(GTK_WIDGET(window));
1370 gtk_window_set_default(GTK_WINDOW(window), GTK_WIDGET(create));
1371 gtk_window_set_focus(GTK_WINDOW(window), GTK_WIDGET(entry));
1373 name.editable = GTK_EDITABLE(entry);
1374 name.text = NULL;
1376 gtk_main();
1378 if (selected_repr != NULL && name.text) {
1379 Inkscape::XML::Document *xml_doc = current_document->getReprDoc();
1380 Inkscape::XML::Node *new_repr;
1381 new_repr = xml_doc->createElement(name.text);
1382 Inkscape::GC::release(new_repr);
1383 g_free(name.text);
1384 selected_repr->appendChild(new_repr);
1385 set_tree_select(new_repr);
1386 set_dt_select(new_repr);
1388 DocumentUndo::done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1389 _("Create new element node"));
1390 }
1392 } // end of cmd_new_element_node()
1396 void cmd_new_text_node(GtkObject */*object*/, gpointer /*data*/)
1397 {
1398 g_assert(selected_repr != NULL);
1400 Inkscape::XML::Document *xml_doc = current_document->getReprDoc();
1401 Inkscape::XML::Node *text = xml_doc->createTextNode("");
1402 selected_repr->appendChild(text);
1404 DocumentUndo::done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1405 _("Create new text node"));
1407 set_tree_select(text);
1408 set_dt_select(text);
1410 gtk_window_set_focus(GTK_WINDOW(dlg), GTK_WIDGET(content));
1412 }
1414 void cmd_duplicate_node(GtkObject */*object*/, gpointer /*data*/)
1415 {
1416 g_assert(selected_repr != NULL);
1418 Inkscape::XML::Node *parent = sp_repr_parent(selected_repr);
1419 Inkscape::XML::Node *dup = selected_repr->duplicate(parent->document());
1420 parent->addChild(dup, selected_repr);
1422 DocumentUndo::done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1423 _("Duplicate node"));
1425 GtkCTreeNode *node = sp_xmlview_tree_get_repr_node(SP_XMLVIEW_TREE(tree), dup);
1427 if (node) {
1428 gtk_ctree_select(GTK_CTREE(tree), node);
1429 }
1430 }
1434 void cmd_delete_node(GtkObject */*object*/, gpointer /*data*/)
1435 {
1436 g_assert(selected_repr != NULL);
1437 sp_repr_unparent(selected_repr);
1439 DocumentUndo::done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1440 Q_("nodeAsInXMLinHistoryDialog|Delete node"));
1441 }
1445 void cmd_delete_attr(GtkObject */*object*/, gpointer /*data*/)
1446 {
1447 g_assert(selected_repr != NULL);
1448 g_assert(selected_attr != 0);
1449 selected_repr->setAttribute(g_quark_to_string(selected_attr), NULL);
1451 SPObject *updated=current_document->getObjectByRepr(selected_repr);
1452 if (updated) {
1453 // force immediate update of dependant attributes
1454 updated->updateRepr();
1455 }
1457 DocumentUndo::done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1458 _("Delete attribute"));
1459 }
1463 void cmd_set_attr(GtkObject */*object*/, gpointer /*data*/)
1464 {
1465 g_assert(selected_repr != NULL);
1467 gchar *name = gtk_editable_get_chars(attr_name, 0, -1);
1468 GtkTextIter start;
1469 GtkTextIter end;
1470 gtk_text_buffer_get_bounds( gtk_text_view_get_buffer(attr_value),
1471 &start, &end );
1472 gchar *value = gtk_text_buffer_get_text( gtk_text_view_get_buffer(attr_value),
1473 &start, &end, TRUE );
1475 selected_repr->setAttribute(name, value, false);
1477 g_free(name);
1478 g_free(value);
1480 SPObject *updated = current_document->getObjectByRepr(selected_repr);
1481 if (updated) {
1482 // force immediate update of dependant attributes
1483 updated->updateRepr();
1484 }
1486 DocumentUndo::done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1487 _("Change attribute"));
1489 /* TODO: actually, the row won't have been created yet. why? */
1490 gint row = sp_xmlview_attr_list_find_row_from_key(GTK_CLIST(attributes),
1491 g_quark_from_string(name));
1492 if (row != -1) {
1493 gtk_clist_select_row(GTK_CLIST(attributes), row, 0);
1494 }
1495 }
1499 void cmd_raise_node(GtkObject */*object*/, gpointer /*data*/)
1500 {
1501 g_assert(selected_repr != NULL);
1503 Inkscape::XML::Node *parent = sp_repr_parent(selected_repr);
1504 g_return_if_fail(parent != NULL);
1505 g_return_if_fail(parent->firstChild() != selected_repr);
1507 Inkscape::XML::Node *ref = NULL;
1508 Inkscape::XML::Node *before = parent->firstChild();
1509 while (before && before->next() != selected_repr) {
1510 ref = before;
1511 before = before->next();
1512 }
1514 parent->changeOrder(selected_repr, ref);
1516 DocumentUndo::done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1517 _("Raise node"));
1519 set_tree_select(selected_repr);
1520 set_dt_select(selected_repr);
1521 }
1525 void cmd_lower_node(GtkObject */*object*/, gpointer /*data*/)
1526 {
1527 g_assert(selected_repr != NULL);
1528 g_return_if_fail(selected_repr->next() != NULL);
1529 Inkscape::XML::Node *parent = sp_repr_parent(selected_repr);
1531 parent->changeOrder(selected_repr, selected_repr->next());
1533 DocumentUndo::done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1534 _("Lower node"));
1536 set_tree_select(selected_repr);
1537 set_dt_select(selected_repr);
1538 }
1540 void cmd_indent_node(GtkObject */*object*/, gpointer /*data*/)
1541 {
1542 Inkscape::XML::Node *repr = selected_repr;
1543 g_assert(repr != NULL);
1544 Inkscape::XML::Node *parent = sp_repr_parent(repr);
1545 g_return_if_fail(parent != NULL);
1546 g_return_if_fail(parent->firstChild() != repr);
1548 Inkscape::XML::Node* prev = parent->firstChild();
1549 while (prev && prev->next() != repr) {
1550 prev = prev->next();
1551 }
1552 g_return_if_fail(prev != NULL);
1553 g_return_if_fail(prev->type() == Inkscape::XML::ELEMENT_NODE);
1555 Inkscape::XML::Node* ref = NULL;
1556 if (prev->firstChild()) {
1557 for( ref = prev->firstChild() ; ref->next() ; ref = ref->next() ){};
1558 }
1560 parent->removeChild(repr);
1561 prev->addChild(repr, ref);
1563 DocumentUndo::done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1564 _("Indent node"));
1565 set_tree_select(repr);
1566 set_dt_select(repr);
1568 } // end of cmd_indent_node()
1572 void cmd_unindent_node(GtkObject */*object*/, gpointer /*data*/)
1573 {
1574 Inkscape::XML::Node *repr = selected_repr;
1575 g_assert(repr != NULL);
1576 Inkscape::XML::Node *parent = sp_repr_parent(repr);
1577 g_return_if_fail(parent);
1578 Inkscape::XML::Node *grandparent = sp_repr_parent(parent);
1579 g_return_if_fail(grandparent);
1581 parent->removeChild(repr);
1582 grandparent->addChild(repr, parent);
1584 DocumentUndo::done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1585 _("Unindent node"));
1586 set_tree_select(repr);
1587 set_dt_select(repr);
1589 } // end of cmd_unindent_node()
1591 /** Returns true iff \a item is suitable to be included in the selection, in particular
1592 whether it has a bounding box in the desktop coordinate system for rendering resize handles.
1594 Descendents of <defs> nodes (markers etc.) return false, for example.
1595 */
1596 bool in_dt_coordsys(SPObject const &item)
1597 {
1598 /* Definition based on sp_item_i2doc_affine. */
1599 SPObject const *child = &item;
1600 g_return_val_if_fail(child != NULL, false);
1601 for(;;) {
1602 if (!SP_IS_ITEM(child)) {
1603 return false;
1604 }
1605 SPObject const * const parent = SP_OBJECT_PARENT(child);
1606 if (parent == NULL) {
1607 break;
1608 }
1609 child = parent;
1610 }
1611 g_assert(SP_IS_ROOT(child));
1612 /* Relevance: Otherwise, I'm not sure whether to return true or false. */
1613 return true;
1614 }
1617 /*
1618 Local Variables:
1619 mode:c++
1620 c-file-style:"stroustrup"
1621 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1622 indent-tabs-mode:nil
1623 fill-column:99
1624 End:
1625 */
1626 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :