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 <gtk/gtk.h>
16 #include <glibmm/i18n.h>
17 #include "helper/window.h"
18 #include "macros.h"
19 #include "../inkscape.h"
20 #include "../document.h"
21 #include "../desktop-handles.h"
22 #include "desktop.h"
23 #include "../selection.h"
24 #include "../sp-string.h"
25 #include "../sp-tspan.h"
26 #include "../sp-root.h"
27 #include "../event-context.h"
28 #include "in-dt-coordsys.h"
31 #include "../widgets/sp-xmlview-tree.h"
32 #include "../widgets/sp-xmlview-content.h"
33 #include "../widgets/sp-xmlview-attr-list.h"
35 #include "../inkscape-stock.h"
36 #include "widgets/icon.h"
38 #include "dialog-events.h"
39 #include "../preferences.h"
40 #include "../verbs.h"
41 #include "../interface.h"
43 #include "shortcuts.h"
44 #include <gdk/gdkkeysyms.h>
46 #include "message-stack.h"
47 #include "message-context.h"
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);
151 /*
152 * \brief Sets the XML status bar when the tree is selected.
153 */
154 void tree_reset_context()
155 {
156 _message_context->set(Inkscape::NORMAL_MESSAGE,
157 _("<b>Click</b> to select nodes, <b>drag</b> to rearrange."));
158 }
161 /*
162 * \brief Sets the XML status bar, depending on which attr is selected.
163 */
164 void attr_reset_context(gint attr)
165 {
166 if (attr == 0) {
167 _message_context->set(Inkscape::NORMAL_MESSAGE,
168 _("<b>Click</b> attribute to edit."));
169 }
170 else {
171 const gchar *name = g_quark_to_string(attr);
172 gchar *message = g_strdup_printf(_("Attribute <b>%s</b> selected. Press <b>Ctrl+Enter</b> when done editing to commit changes."), name);
173 _message_context->set(Inkscape::NORMAL_MESSAGE, message);
174 g_free(message);
175 }
176 }
179 void sp_xml_tree_dialog()
180 {
181 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
183 if (!desktop) {
184 return;
185 }
187 if (dlg == NULL)
188 { // very long block
190 GtkWidget *box, *sw, *paned, *toolbar, *button;
191 GtkWidget *text_container, *attr_container, *attr_subpaned_container, *box2;
192 GtkWidget *set_attr;
194 tooltips = gtk_tooltips_new();
195 gtk_tooltips_enable(tooltips);
197 dlg = sp_window_new("", TRUE);
198 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
199 if (x == -1000 || y == -1000) {
200 x = prefs->getInt(prefs_path + "x", -1000);
201 y = prefs->getInt(prefs_path + "y", -1000);
202 }
203 if (w ==0 || h == 0) {
204 w = prefs->getInt(prefs_path + "w", 0);
205 h = prefs->getInt(prefs_path + "h", 0);
206 }
208 // if (x<0) x=0;
209 // if (y<0) y=0;
211 if (w && h) {
212 gtk_window_resize((GtkWindow *) dlg, w, h);
213 }
214 if (x >= 0 && y >= 0 && (x < (gdk_screen_width()-MIN_ONSCREEN_DISTANCE)) && (y < (gdk_screen_height()-MIN_ONSCREEN_DISTANCE))) {
215 gtk_window_move((GtkWindow *) dlg, x, y);
216 } else {
217 gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
218 }
220 sp_transientize(dlg);
221 wd.win = dlg;
222 wd.stop = 0;
223 g_signal_connect ( G_OBJECT(INKSCAPE), "activate_desktop", G_CALLBACK(sp_transientize_callback), &wd );
225 gtk_signal_connect( GTK_OBJECT(dlg), "event", GTK_SIGNAL_FUNC(sp_dialog_event_handler), dlg );
227 gtk_signal_connect( GTK_OBJECT(dlg), "destroy", G_CALLBACK(on_destroy), dlg);
228 gtk_signal_connect( GTK_OBJECT(dlg), "delete_event", G_CALLBACK(on_delete), dlg);
229 g_signal_connect ( G_OBJECT(INKSCAPE), "shut_down", G_CALLBACK(on_delete), dlg);
231 g_signal_connect ( G_OBJECT(INKSCAPE), "dialogs_hide", G_CALLBACK(sp_dialog_hide), dlg);
232 g_signal_connect ( G_OBJECT(INKSCAPE), "dialogs_unhide", G_CALLBACK(sp_dialog_unhide), dlg);
235 gtk_container_set_border_width(GTK_CONTAINER(dlg), 0);
236 gtk_window_set_default_size(GTK_WINDOW(dlg), 640, 384);
238 GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
239 gtk_container_add(GTK_CONTAINER(dlg), vbox);
241 GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
242 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
244 status = gtk_label_new(NULL);
245 gtk_misc_set_alignment(GTK_MISC(status), 0.0, 0.5);
246 gtk_widget_set_size_request(status, 1, -1);
247 gtk_label_set_markup(GTK_LABEL(status), "");
248 gtk_box_pack_start(GTK_BOX(hbox), gtk_hbox_new(FALSE, 0), FALSE, FALSE, 4);
249 gtk_box_pack_start(GTK_BOX(hbox), status, TRUE, TRUE, 0);
251 paned = gtk_hpaned_new();
252 gtk_paned_set_position(GTK_PANED(paned), 256);
253 gtk_box_pack_start(GTK_BOX(vbox), paned, TRUE, TRUE, 0);
255 _message_stack = new Inkscape::MessageStack();
256 _message_context = new Inkscape::MessageContext(_message_stack);
257 _message_changed_connection = _message_stack->connectChanged(
258 sigc::bind(sigc::ptr_fun(_set_status_message), dlg)
259 );
261 /* tree view */
263 box = gtk_vbox_new(FALSE, 0);
264 gtk_paned_pack1(GTK_PANED(paned), box, FALSE, FALSE);
266 tree = SP_XMLVIEW_TREE(sp_xmlview_tree_new(NULL, NULL, NULL));
267 gtk_tooltips_set_tip( tooltips, GTK_WIDGET(tree),
268 _("Drag to reorder nodes"), NULL );
270 g_signal_connect( G_OBJECT(tree), "tree_select_row",
271 G_CALLBACK(on_tree_select_row), NULL );
273 g_signal_connect( G_OBJECT(tree), "tree_unselect_row",
274 G_CALLBACK(on_tree_unselect_row), NULL );
276 g_signal_connect_after( G_OBJECT(tree), "tree_move",
277 G_CALLBACK(after_tree_move), NULL);
279 /* TODO: replace gtk_signal_connect_while_alive() with something
280 * else...
281 */
282 toolbar = gtk_toolbar_new();
283 gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
284 gtk_container_set_border_width(GTK_CONTAINER(toolbar), 0);
286 button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
287 NULL,
288 _("New element node"),
289 NULL,
290 sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
291 INKSCAPE_STOCK_ADD_XML_ELEMENT_NODE ),
292 G_CALLBACK(cmd_new_element_node),
293 NULL);
295 gtk_signal_connect_while_alive( GTK_OBJECT(tree),
296 "tree_select_row",
297 G_CALLBACK(on_tree_select_row_enable_if_element),
298 button,
299 GTK_OBJECT(button));
301 gtk_signal_connect_while_alive(GTK_OBJECT(tree),
302 "tree_unselect_row",
303 G_CALLBACK(on_tree_unselect_row_disable),
304 button,
305 GTK_OBJECT(button));
307 gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
309 button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
310 NULL, _("New text node"), NULL,
311 sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
312 INKSCAPE_STOCK_ADD_XML_TEXT_NODE ),
313 G_CALLBACK(cmd_new_text_node),
314 NULL);
316 gtk_signal_connect_while_alive(GTK_OBJECT(tree),
317 "tree_select_row",
318 G_CALLBACK(on_tree_select_row_enable_if_element),
319 button,
320 GTK_OBJECT(button));
322 gtk_signal_connect_while_alive(GTK_OBJECT(tree),
323 "tree_unselect_row",
324 G_CALLBACK(on_tree_unselect_row_disable),
325 button,
326 GTK_OBJECT(button));
328 gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
330 button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
331 NULL, _("Duplicate node"), NULL,
332 sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
333 INKSCAPE_STOCK_DUPLICATE_XML_NODE ),
334 G_CALLBACK(cmd_duplicate_node),
335 NULL);
337 gtk_signal_connect_while_alive(GTK_OBJECT(tree),
338 "tree_select_row",
339 G_CALLBACK(on_tree_select_row_enable_if_mutable),
340 button,
341 GTK_OBJECT(button));
343 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
344 G_CALLBACK(on_tree_unselect_row_disable),
345 button, GTK_OBJECT(button));
347 gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
349 gtk_toolbar_append_space(GTK_TOOLBAR(toolbar));
351 button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
352 NULL, _("Delete node"), NULL,
353 sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
354 INKSCAPE_STOCK_DELETE_XML_NODE ),
355 G_CALLBACK(cmd_delete_node), NULL );
357 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
358 G_CALLBACK(on_tree_select_row_enable_if_mutable),
359 button, GTK_OBJECT(button));
360 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
361 G_CALLBACK(on_tree_unselect_row_disable),
362 button, GTK_OBJECT(button));
363 gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
365 gtk_toolbar_append_space(GTK_TOOLBAR(toolbar));
367 button = gtk_toolbar_append_item( GTK_TOOLBAR(toolbar), "<",
368 _("Unindent node"), NULL,
369 gtk_arrow_new(GTK_ARROW_LEFT, GTK_SHADOW_IN),
370 G_CALLBACK(cmd_unindent_node), NULL);
372 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
373 G_CALLBACK(on_tree_select_row_enable_if_has_grandparent),
374 button, GTK_OBJECT(button));
376 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
377 G_CALLBACK(on_tree_unselect_row_disable),
378 button, GTK_OBJECT(button));
380 gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
382 button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), ">",
383 _("Indent node"), NULL,
384 gtk_arrow_new(GTK_ARROW_RIGHT, GTK_SHADOW_IN),
385 G_CALLBACK(cmd_indent_node), NULL);
386 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
387 G_CALLBACK(on_tree_select_row_enable_if_indentable),
388 button, GTK_OBJECT(button));
389 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
390 (GCallback) on_tree_unselect_row_disable,
391 button, GTK_OBJECT(button));
392 gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
394 button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), "^",
395 _("Raise node"), NULL,
396 gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_IN),
397 G_CALLBACK(cmd_raise_node), NULL);
398 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
399 G_CALLBACK(on_tree_select_row_enable_if_not_first_child),
400 button, GTK_OBJECT(button));
401 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
402 G_CALLBACK(on_tree_unselect_row_disable),
403 button, GTK_OBJECT(button));
404 gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
406 button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), "v",
407 _("Lower node"), NULL,
408 gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_IN),
409 G_CALLBACK(cmd_lower_node), NULL);
410 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
411 G_CALLBACK(on_tree_select_row_enable_if_not_last_child),
412 button, GTK_OBJECT(button));
413 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
414 G_CALLBACK(on_tree_unselect_row_disable),
415 button, GTK_OBJECT(button));
416 gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
418 gtk_box_pack_start(GTK_BOX(box), toolbar, FALSE, TRUE, 0);
420 sw = gtk_scrolled_window_new(NULL, NULL);
421 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(sw),
422 GTK_POLICY_AUTOMATIC,
423 GTK_POLICY_AUTOMATIC );
424 gtk_box_pack_start(GTK_BOX(box), sw, TRUE, TRUE, 0);
426 gtk_container_add(GTK_CONTAINER(sw), GTK_WIDGET(tree));
428 /* node view */
430 box = gtk_vbox_new(FALSE, 0);
431 gtk_paned_pack2(GTK_PANED(paned), box, TRUE, TRUE);
433 /* attributes */
435 attr_container = gtk_vbox_new(FALSE, 0);
436 gtk_box_pack_start( GTK_BOX(box), GTK_WIDGET(attr_container),
437 TRUE, TRUE, 0 );
439 attributes = SP_XMLVIEW_ATTR_LIST(sp_xmlview_attr_list_new(NULL));
440 g_signal_connect( G_OBJECT(attributes), "select_row",
441 G_CALLBACK(on_attr_select_row), NULL);
442 g_signal_connect( G_OBJECT(attributes), "unselect_row",
443 G_CALLBACK(on_attr_unselect_row), NULL);
444 g_signal_connect( G_OBJECT(attributes), "row-value-changed",
445 G_CALLBACK(on_attr_row_changed), NULL);
447 toolbar = gtk_toolbar_new();
448 gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
449 gtk_container_set_border_width(GTK_CONTAINER(toolbar), 0);
451 button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
452 NULL, _("Delete attribute"), NULL,
453 sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
454 INKSCAPE_STOCK_DELETE_XML_ATTRIBUTE ),
455 (GCallback) cmd_delete_attr, NULL);
457 gtk_signal_connect_while_alive(GTK_OBJECT(attributes), "select_row",
458 (GCallback) on_attr_select_row_enable, button,
459 GTK_OBJECT(button));
461 gtk_signal_connect_while_alive(GTK_OBJECT(attributes), "unselect_row",
462 (GCallback) on_attr_unselect_row_disable, button,
463 GTK_OBJECT(button));
465 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
466 (GCallback) on_tree_unselect_row_disable, button,
467 GTK_OBJECT(button));
469 gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
471 gtk_box_pack_start( GTK_BOX(attr_container),
472 GTK_WIDGET(toolbar), FALSE, TRUE, 0 );
474 attr_subpaned_container = gtk_vpaned_new();
475 gtk_box_pack_start( GTK_BOX(attr_container),
476 GTK_WIDGET(attr_subpaned_container),
477 TRUE, TRUE, 0 );
478 gtk_widget_show(attr_subpaned_container);
480 sw = gtk_scrolled_window_new(NULL, NULL);
481 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
482 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
483 gtk_paned_pack1( GTK_PANED(attr_subpaned_container),
484 GTK_WIDGET(sw), TRUE, TRUE );
485 gtk_container_add(GTK_CONTAINER(sw), GTK_WIDGET(attributes));
487 toolbar = gtk_vbox_new(FALSE, 4);
488 gtk_container_set_border_width(GTK_CONTAINER(toolbar), 4);
490 box2 = gtk_hbox_new(FALSE, 4);
491 gtk_box_pack_start( GTK_BOX(toolbar), GTK_WIDGET(box2),
492 FALSE, TRUE, 0);
494 attr_name = GTK_EDITABLE(gtk_entry_new());
495 gtk_tooltips_set_tip( tooltips, GTK_WIDGET(attr_name),
496 // TRANSLATORS: "Attribute" is a noun here
497 _("Attribute name"), NULL );
499 gtk_signal_connect( GTK_OBJECT(attributes), "select_row",
500 (GCallback) on_attr_select_row_set_name_content,
501 attr_name);
503 gtk_signal_connect( GTK_OBJECT(attributes), "unselect_row",
504 (GCallback) on_attr_unselect_row_clear_text,
505 attr_name);
507 gtk_signal_connect( GTK_OBJECT(tree), "tree_unselect_row",
508 (GCallback) on_tree_unselect_row_clear_text,
509 attr_name);
511 gtk_box_pack_start( GTK_BOX(box2), GTK_WIDGET(attr_name),
512 TRUE, TRUE, 0);
514 set_attr = gtk_button_new();
515 gtk_tooltips_set_tip( tooltips, GTK_WIDGET(set_attr),
516 // TRANSLATORS: "Set" is a verb here
517 _("Set attribute"), NULL );
518 // TRANSLATORS: "Set" is a verb here
519 GtkWidget *set_label = gtk_label_new(_("Set"));
520 gtk_container_add(GTK_CONTAINER(set_attr), set_label);
522 gtk_signal_connect( GTK_OBJECT(set_attr), "clicked",
523 (GCallback) cmd_set_attr, NULL);
524 gtk_signal_connect( GTK_OBJECT(attr_name), "changed",
525 (GCallback) on_editable_changed_enable_if_valid_xml_name,
526 set_attr );
527 gtk_widget_set_sensitive(GTK_WIDGET(set_attr), FALSE);
529 gtk_box_pack_start(GTK_BOX(box2), set_attr, FALSE, FALSE, 0);
531 sw = gtk_scrolled_window_new(NULL, NULL);
532 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(sw),
533 GTK_POLICY_AUTOMATIC,
534 GTK_POLICY_AUTOMATIC );
535 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN );
536 gtk_box_pack_start(GTK_BOX(toolbar), sw, TRUE, TRUE, 0);
538 attr_value =(GtkTextView *) gtk_text_view_new();
539 gtk_text_view_set_wrap_mode((GtkTextView *) attr_value, GTK_WRAP_CHAR);
540 gtk_tooltips_set_tip( tooltips, GTK_WIDGET(attr_value),
541 // TRANSLATORS: "Attribute" is a noun here
542 _("Attribute value"), NULL );
543 gtk_signal_connect( GTK_OBJECT(attributes), "select_row",
544 (GCallback) on_attr_select_row_set_value_content,
545 attr_value );
546 gtk_signal_connect( GTK_OBJECT(attributes), "unselect_row",
547 (GCallback) on_attr_unselect_row_clear_text,
548 attr_value );
549 gtk_signal_connect( GTK_OBJECT(tree), "tree_unselect_row",
550 (GCallback) on_tree_unselect_row_clear_text,
551 attr_value );
552 gtk_text_view_set_editable(attr_value, TRUE);
553 gtk_container_add( GTK_CONTAINER(sw),
554 GTK_WIDGET(attr_value) );
556 gtk_paned_pack2( GTK_PANED(attr_subpaned_container),
557 GTK_WIDGET(toolbar), FALSE, TRUE );
559 /* text */
561 sw = gtk_scrolled_window_new(NULL, NULL);
562 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(sw),
563 GTK_POLICY_AUTOMATIC,
564 GTK_POLICY_AUTOMATIC );
565 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN );
566 gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(sw), TRUE, TRUE, 0);
568 content = SP_XMLVIEW_CONTENT(sp_xmlview_content_new(NULL));
569 gtk_container_add(GTK_CONTAINER(sw), GTK_WIDGET(content));
571 text_container = sw;
573 /* initial show/hide */
575 gtk_widget_show_all(GTK_WIDGET(dlg));
577 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
578 (GCallback) on_tree_select_row_show_if_element,
579 attr_container, GTK_OBJECT(attr_container));
581 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
582 (GCallback) on_tree_unselect_row_hide,
583 attr_container, GTK_OBJECT(attr_container));
585 gtk_widget_hide(attr_container);
587 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row",
588 (GCallback) on_tree_select_row_show_if_text,
589 text_container, GTK_OBJECT(text_container));
591 gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row",
592 (GCallback) on_tree_unselect_row_hide,
593 text_container, GTK_OBJECT(text_container));
595 gtk_widget_hide(text_container);
597 g_signal_connect( G_OBJECT(INKSCAPE), "activate_desktop",
598 G_CALLBACK(sp_xmltree_desktop_activate), dlg);
600 g_signal_connect( G_OBJECT(INKSCAPE), "deactivate_desktop",
601 G_CALLBACK(sp_xmltree_desktop_deactivate), dlg);
603 g_signal_connect((GObject *) dlg, "key_press_event", (GCallback) sp_xml_tree_key_press, NULL);
605 tree_reset_context();
606 } // end of if (dlg == NULL)
608 gtk_window_present((GtkWindow *) dlg);
610 g_assert(desktop != NULL);
611 set_tree_desktop(desktop);
613 } // end of sp_xml_tree_dialog()
615 static gboolean sp_xml_tree_key_press(GtkWidget */*widget*/, GdkEventKey *event)
616 {
618 unsigned int shortcut = get_group0_keyval(event) |
619 ( event->state & GDK_SHIFT_MASK ?
620 SP_SHORTCUT_SHIFT_MASK : 0 ) |
621 ( event->state & GDK_CONTROL_MASK ?
622 SP_SHORTCUT_CONTROL_MASK : 0 ) |
623 ( event->state & GDK_MOD1_MASK ?
624 SP_SHORTCUT_ALT_MASK : 0 );
626 /* fixme: if you need to add more xml-tree-specific callbacks, you should probably upgrade
627 * the sp_shortcut mechanism to take into account windows. */
628 if (shortcut == (SP_SHORTCUT_CONTROL_MASK | GDK_Return)) {
629 cmd_set_attr(NULL, NULL);
630 return true;
631 }
632 return false;
633 }
636 static void sp_xmltree_desktop_activate(Inkscape::Application */*inkscape*/,
637 SPDesktop *desktop,
638 GtkWidget */*dialog*/ )
639 {
640 set_tree_desktop(desktop);
641 }
643 static void sp_xmltree_desktop_deactivate(Inkscape::Application */*inkscape*/,
644 SPDesktop */*desktop*/,
645 GtkWidget */*dialog*/ )
646 {
647 set_tree_desktop(NULL);
648 }
651 void set_tree_desktop(SPDesktop *desktop)
652 {
653 if ( desktop == current_desktop ) {
654 return;
655 }
657 if (current_desktop) {
658 sel_changed_connection.disconnect();
659 document_replaced_connection.disconnect();
660 }
661 current_desktop = desktop;
662 if (desktop) {
663 sel_changed_connection = sp_desktop_selection(desktop)->connectChanged(&on_desktop_selection_changed);
664 document_replaced_connection = desktop->connectDocumentReplaced(&on_document_replaced);
665 set_tree_document(sp_desktop_document(desktop));
666 } else {
667 set_tree_document(NULL);
668 }
670 } // end of set_tree_desktop()
674 void set_tree_document(SPDocument *document)
675 {
676 if (document == current_document) {
677 return;
678 }
680 if (current_document) {
681 document_uri_set_connection.disconnect();
682 }
683 current_document = document;
684 if (current_document) {
686 document_uri_set_connection = current_document->connectURISet(sigc::bind(sigc::ptr_fun(&on_document_uri_set), current_document));
687 on_document_uri_set(SP_DOCUMENT_URI(current_document), current_document);
688 set_tree_repr(sp_document_repr_root(current_document));
690 } else {
691 set_tree_repr(NULL);
692 }
693 }
697 void set_tree_repr(Inkscape::XML::Node *repr)
698 {
699 if (repr == selected_repr) {
700 return;
701 }
703 gtk_clist_freeze(GTK_CLIST(tree));
705 sp_xmlview_tree_set_repr(tree, repr);
707 if (repr) {
708 set_tree_select(get_dt_select());
709 } else {
710 set_tree_select(NULL);
711 }
713 gtk_clist_thaw(GTK_CLIST(tree));
715 propagate_tree_select(selected_repr);
717 }
721 void set_tree_select(Inkscape::XML::Node *repr)
722 {
723 if (selected_repr) {
724 Inkscape::GC::release(selected_repr);
725 }
727 selected_repr = repr;
728 if (repr) {
729 GtkCTreeNode *node;
731 Inkscape::GC::anchor(selected_repr);
733 node = sp_xmlview_tree_get_repr_node(SP_XMLVIEW_TREE(tree), repr);
734 if (node) {
735 GtkCTreeNode *parent;
737 gtk_ctree_select(GTK_CTREE(tree), node);
739 parent = GTK_CTREE_ROW(node)->parent;
740 while (parent) {
741 gtk_ctree_expand(GTK_CTREE(tree), parent);
742 parent = GTK_CTREE_ROW(parent)->parent;
743 }
745 gtk_ctree_node_moveto(GTK_CTREE(tree), node, 0, 0.66, 0.0);
746 }
747 } else {
748 gtk_clist_unselect_all(GTK_CLIST(tree));
749 }
750 propagate_tree_select(repr);
751 }
755 void propagate_tree_select(Inkscape::XML::Node *repr)
756 {
757 if (repr && repr->type() == Inkscape::XML::ELEMENT_NODE) {
758 sp_xmlview_attr_list_set_repr(attributes, repr);
759 } else {
760 sp_xmlview_attr_list_set_repr(attributes, NULL);
761 }
763 if (repr && ( repr->type() == Inkscape::XML::TEXT_NODE || repr->type() == Inkscape::XML::COMMENT_NODE || repr->type() == Inkscape::XML::PI_NODE ) ) {
764 sp_xmlview_content_set_repr(content, repr);
765 } else {
766 sp_xmlview_content_set_repr(content, NULL);
767 }
768 }
771 Inkscape::XML::Node *get_dt_select()
772 {
773 if (!current_desktop) {
774 return NULL;
775 }
777 return sp_desktop_selection(current_desktop)->singleRepr();
778 }
782 void set_dt_select(Inkscape::XML::Node *repr)
783 {
784 if (!current_desktop) {
785 return;
786 }
788 Inkscape::Selection *selection = sp_desktop_selection(current_desktop);
790 SPObject *object;
791 if (repr) {
792 while ( ( repr->type() != Inkscape::XML::ELEMENT_NODE )
793 && sp_repr_parent(repr) )
794 {
795 repr = sp_repr_parent(repr);
796 } // end of while loop
798 object = sp_desktop_document(current_desktop)->getObjectByRepr(repr);
799 } else {
800 object = NULL;
801 }
803 blocked++;
804 if ( object && in_dt_coordsys(*object)
805 && !(SP_IS_STRING(object) ||
806 SP_IS_ROOT(object) ) )
807 {
808 /* We cannot set selection to root or string - they are not items and selection is not
809 * equipped to deal with them */
810 selection->set(SP_ITEM(object));
811 }
812 blocked--;
814 } // end of set_dt_select()
817 void on_tree_select_row(GtkCTree *tree,
818 GtkCTreeNode *node,
819 gint /*column*/,
820 gpointer /*data*/)
821 {
822 if (blocked) {
823 return;
824 }
826 Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
827 g_assert(repr != NULL);
829 if (selected_repr == repr) {
830 return;
831 }
833 if (selected_repr) {
834 Inkscape::GC::release(selected_repr);
835 selected_repr = NULL;
836 }
837 selected_repr = repr;
838 Inkscape::GC::anchor(selected_repr);
840 propagate_tree_select(selected_repr);
842 set_dt_select(selected_repr);
844 tree_reset_context();
845 }
847 void on_tree_unselect_row(GtkCTree *tree,
848 GtkCTreeNode *node,
849 gint /*column*/,
850 gpointer /*data*/)
851 {
852 if (blocked) {
853 return;
854 }
856 Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
857 propagate_tree_select(NULL);
858 set_dt_select(NULL);
860 if (selected_repr && (selected_repr == repr)) {
861 Inkscape::GC::release(selected_repr);
862 selected_repr = NULL;
863 selected_attr = 0;
864 }
865 }
869 void after_tree_move(GtkCTree */*tree*/,
870 GtkCTreeNode *node,
871 GtkCTreeNode *new_parent,
872 GtkCTreeNode *new_sibling,
873 gpointer /*data*/)
874 {
875 if (GTK_CTREE_ROW(node)->parent == new_parent &&
876 GTK_CTREE_ROW(node)->sibling == new_sibling)
877 {
878 sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
879 _("Drag XML subtree"));
880 } else {
881 sp_document_cancel(current_document);
882 }
883 }
886 static void on_destroy(GtkObject */*object*/, gpointer /*data*/)
887 {
888 set_tree_desktop(NULL);
889 gtk_object_destroy(GTK_OBJECT(tooltips));
890 tooltips = NULL;
891 sp_signal_disconnect_by_data(INKSCAPE, dlg);
892 wd.win = dlg = NULL;
893 wd.stop = 0;
895 _message_changed_connection.disconnect();
896 delete _message_context;
897 _message_context = NULL;
898 Inkscape::GC::release(_message_stack);
899 _message_stack = NULL;
900 _message_changed_connection.~connection();
902 status = NULL;
903 }
907 static gboolean on_delete(GtkObject */*object*/, GdkEvent */*event*/, gpointer /*data*/)
908 {
909 gtk_window_get_position((GtkWindow *) dlg, &x, &y);
910 gtk_window_get_size((GtkWindow *) dlg, &w, &h);
912 if (x<0) x=0;
913 if (y<0) y=0;
915 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
916 prefs->setInt(prefs_path + "x", x);
917 prefs->setInt(prefs_path + "y", y);
918 prefs->setInt(prefs_path + "w", w);
919 prefs->setInt(prefs_path + "h", h);
921 return FALSE; // which means, go ahead and destroy it
922 }
925 static void _set_status_message(Inkscape::MessageType /*type*/, const gchar *message, GtkWidget */*dialog*/)
926 {
927 if (status) {
928 gtk_label_set_markup(GTK_LABEL(status), message ? message : "");
929 }
930 }
933 void on_tree_select_row_enable(GtkCTree */*tree*/,
934 GtkCTreeNode */*node*/,
935 gint /*column*/,
936 gpointer data)
937 {
938 gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
939 }
943 void on_tree_select_row_enable_if_element(GtkCTree *tree,
944 GtkCTreeNode *node,
945 gint /*column*/,
946 gpointer data )
947 {
948 Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
950 if (repr->type() == Inkscape::XML::ELEMENT_NODE) {
951 gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
952 } else {
953 gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
954 }
955 }
959 void on_tree_select_row_show_if_element(GtkCTree *tree, GtkCTreeNode *node,
960 gint /*column*/, gpointer data)
961 {
962 Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
964 if (repr->type() == Inkscape::XML::ELEMENT_NODE) {
965 gtk_widget_show(GTK_WIDGET(data));
966 } else {
967 gtk_widget_hide(GTK_WIDGET(data));
968 }
969 }
973 void on_tree_select_row_show_if_text(GtkCTree *tree, GtkCTreeNode *node,
974 gint /*column*/, gpointer data)
975 {
976 Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
978 if ( repr->type() == Inkscape::XML::TEXT_NODE || repr->type() == Inkscape::XML::COMMENT_NODE || repr->type() == Inkscape::XML::PI_NODE ) {
979 gtk_widget_show(GTK_WIDGET(data));
980 } else {
981 gtk_widget_hide(GTK_WIDGET(data));
982 }
983 }
986 gboolean xml_tree_node_mutable(GtkCTreeNode *node)
987 {
988 // top-level is immutable, obviously
989 if (!GTK_CTREE_ROW(node)->parent) {
990 return false;
991 }
993 // if not in base level (where namedview, defs, etc go), we're mutable
994 if (GTK_CTREE_ROW(GTK_CTREE_ROW(node)->parent)->parent) {
995 return true;
996 }
998 Inkscape::XML::Node *repr;
999 repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
1000 g_assert(repr);
1002 // don't let "defs" or "namedview" disappear
1003 if ( !strcmp(repr->name(),"svg:defs") ||
1004 !strcmp(repr->name(),"sodipodi:namedview") ) {
1005 return false;
1006 }
1008 // everyone else is okay, I guess. :)
1009 return true;
1010 }
1013 void on_tree_select_row_enable_if_mutable(GtkCTree */*tree*/, GtkCTreeNode *node,
1014 gint /*column*/, gpointer data)
1015 {
1016 gtk_widget_set_sensitive(GTK_WIDGET(data), xml_tree_node_mutable(node));
1017 }
1021 void on_tree_unselect_row_disable(GtkCTree */*tree*/, GtkCTreeNode */*node*/,
1022 gint /*column*/, gpointer data)
1023 {
1024 gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1025 }
1029 void on_tree_unselect_row_hide(GtkCTree */*tree*/, GtkCTreeNode */*node*/,
1030 gint /*column*/, gpointer data)
1031 {
1032 gtk_widget_hide(GTK_WIDGET(data));
1033 }
1037 void on_tree_unselect_row_clear_text(GtkCTree */*tree*/, GtkCTreeNode */*node*/,
1038 gint /*column*/, gpointer data)
1039 {
1040 if (GTK_IS_EDITABLE(data)) {
1041 gtk_editable_delete_text(GTK_EDITABLE(data), 0, -1);
1042 } else if (GTK_IS_TEXT_VIEW(data)) {
1043 GtkTextBuffer *tb;
1044 tb = gtk_text_view_get_buffer(GTK_TEXT_VIEW(data));
1045 gtk_text_buffer_set_text(tb, "", 0);
1046 }
1047 }
1050 void on_attr_select_row(GtkCList *list, gint row, gint /*column*/,
1051 GdkEventButton */*event*/, gpointer /*data*/)
1052 {
1053 selected_attr = sp_xmlview_attr_list_get_row_key(list, row);
1054 gtk_window_set_focus(GTK_WINDOW(dlg), GTK_WIDGET(attr_value));
1056 attr_reset_context(selected_attr);
1057 }
1060 void on_attr_unselect_row(GtkCList */*list*/, gint /*row*/, gint /*column*/,
1061 GdkEventButton */*event*/, gpointer /*data*/)
1062 {
1063 selected_attr = 0;
1064 attr_reset_context(selected_attr);
1065 }
1068 void on_attr_row_changed(GtkCList *list, gint row, gpointer /*data*/)
1069 {
1070 gint attr = sp_xmlview_attr_list_get_row_key(list, row);
1072 if (attr == selected_attr) {
1073 /* if the attr changed, reselect the row in the list to sync
1074 the edit box */
1076 /*
1077 // get current attr values
1078 const gchar * name = g_quark_to_string (sp_xmlview_attr_list_get_row_key (list, row));
1079 const gchar * value = selected_repr->attribute(name);
1081 g_warning("value: '%s'",value);
1083 // get the edit box value
1084 GtkTextIter start, end;
1085 gtk_text_buffer_get_bounds ( gtk_text_view_get_buffer (attr_value),
1086 &start, &end );
1087 gchar * text = gtk_text_buffer_get_text ( gtk_text_view_get_buffer (attr_value),
1088 &start, &end, TRUE );
1089 g_warning("text: '%s'",text);
1091 // compare to edit box
1092 if (strcmp(text,value)) {
1093 // issue warning if they're different
1094 _message_stack->flash(Inkscape::WARNING_MESSAGE,
1095 _("Attribute changed in GUI while editing values!"));
1096 }
1097 g_free (text);
1099 */
1100 gtk_clist_unselect_row( GTK_CLIST(list), row, 0 );
1101 gtk_clist_select_row( GTK_CLIST(list), row, 0 );
1102 }
1103 }
1106 void on_attr_select_row_set_name_content(GtkCList *list, gint row,
1107 gint /*column*/, GdkEventButton */*event*/,
1108 gpointer data)
1109 {
1110 GtkEditable *editable = GTK_EDITABLE(data);
1111 const gchar *name = g_quark_to_string(sp_xmlview_attr_list_get_row_key(list, row));
1112 gtk_editable_delete_text(editable, 0, -1);
1113 gint pos = 0;
1114 gtk_editable_insert_text(editable, name, strlen(name), &pos);
1115 }
1119 void on_attr_select_row_set_value_content(GtkCList *list, gint row, gint /*column*/,
1120 GdkEventButton */*event*/,
1121 gpointer data)
1122 {
1123 GtkTextBuffer *tb = gtk_text_view_get_buffer(GTK_TEXT_VIEW(data));
1124 const gchar *name = g_quark_to_string(sp_xmlview_attr_list_get_row_key(list, row));
1125 const gchar *value = selected_repr->attribute(name);
1126 if (!value) {
1127 value = "";
1128 }
1129 gtk_text_buffer_set_text(tb, value, strlen(value));
1130 }
1133 void on_tree_select_row_enable_if_indentable(GtkCTree *tree, GtkCTreeNode *node,
1134 gint /*column*/, gpointer data)
1135 {
1136 gboolean indentable = FALSE;
1138 if (xml_tree_node_mutable(node)) {
1139 Inkscape::XML::Node *repr, *prev;
1140 repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
1142 Inkscape::XML::Node *parent=repr->parent();
1143 if ( parent && repr != parent->firstChild() ) {
1144 g_assert(parent->firstChild());
1146 // skip to the child just before the current repr
1147 for ( prev = parent->firstChild() ;
1148 prev && prev->next() != repr ;
1149 prev = prev->next() ){};
1151 if (prev && prev->type() == Inkscape::XML::ELEMENT_NODE) {
1152 indentable = TRUE;
1153 }
1154 }
1155 }
1157 gtk_widget_set_sensitive(GTK_WIDGET(data), indentable);
1158 }
1162 void on_tree_select_row_enable_if_not_first_child(GtkCTree *tree,
1163 GtkCTreeNode *node,
1164 gint /*column*/,
1165 gpointer data)
1166 {
1167 Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
1169 Inkscape::XML::Node *parent=repr->parent();
1170 if ( parent && repr != parent->firstChild() ) {
1171 gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
1172 } else {
1173 gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1174 }
1175 }
1179 void on_tree_select_row_enable_if_not_last_child(GtkCTree *tree,
1180 GtkCTreeNode *node,
1181 gint /*column*/, gpointer data)
1182 {
1183 Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
1185 Inkscape::XML::Node *parent=repr->parent();
1186 if ( parent && parent->parent() && repr->next() ) {
1187 gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
1188 } else {
1189 gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1190 }
1191 }
1195 void on_tree_select_row_enable_if_has_grandparent(GtkCTree */*tree*/,
1196 GtkCTreeNode *node,
1197 gint /*column*/, gpointer data)
1198 {
1199 GtkCTreeNode *parent = GTK_CTREE_ROW(node)->parent;
1201 if (parent) {
1202 GtkCTreeNode *grandparent = GTK_CTREE_ROW(parent)->parent;
1203 if (grandparent) {
1204 gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
1205 } else {
1206 gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1207 }
1208 } else {
1209 gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1210 }
1211 }
1215 void on_attr_select_row_enable(GtkCList */*list*/, gint /*row*/, gint /*column*/,
1216 GdkEventButton */*event*/, gpointer data)
1217 {
1218 gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
1219 }
1223 void on_attr_unselect_row_disable(GtkCList */*list*/, gint /*row*/, gint /*column*/,
1224 GdkEventButton */*event*/, gpointer data)
1225 {
1226 gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1227 }
1231 void on_attr_unselect_row_clear_text(GtkCList */*list*/, gint /*row*/, gint /*column*/,
1232 GdkEventButton */*event*/, gpointer data)
1233 {
1234 if (GTK_IS_EDITABLE(data)) {
1235 gtk_editable_delete_text(GTK_EDITABLE(data), 0, -1);
1236 } else if (GTK_IS_TEXT_VIEW(data)) {
1237 GtkTextBuffer *tb;
1238 tb = gtk_text_view_get_buffer(GTK_TEXT_VIEW(data));
1239 gtk_text_buffer_set_text(tb, "", 0);
1240 }
1241 }
1245 void on_editable_changed_enable_if_valid_xml_name(GtkEditable *editable,
1246 gpointer data)
1247 {
1248 gchar *text = gtk_editable_get_chars(editable, 0, -1);
1250 /* TODO: need to do checking a little more rigorous than this */
1252 if (strlen(text)) {
1253 gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
1254 } else {
1255 gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1256 }
1257 g_free(text);
1258 }
1262 void on_desktop_selection_changed(Inkscape::Selection */*selection*/)
1263 {
1264 if (!blocked++) {
1265 set_tree_select(get_dt_select());
1266 }
1267 blocked--;
1268 }
1270 static void on_document_replaced(SPDesktop *dt, SPDocument *doc)
1271 {
1272 if (current_desktop)
1273 sel_changed_connection.disconnect();
1275 sel_changed_connection = sp_desktop_selection(dt)->connectChanged(&on_desktop_selection_changed);
1276 set_tree_document(doc);
1277 }
1279 void on_document_uri_set(gchar const */*uri*/, SPDocument *document)
1280 {
1281 gchar title[500];
1282 sp_ui_dialog_title_string(Inkscape::Verb::get(SP_VERB_DIALOG_XML_EDITOR), title);
1283 gchar *t = g_strdup_printf("%s: %s", SP_DOCUMENT_NAME(document), title);
1284 gtk_window_set_title(GTK_WINDOW(dlg), t);
1285 g_free(t);
1286 }
1290 void on_clicked_get_editable_text(GtkWidget */*widget*/, gpointer data)
1291 {
1292 EditableDest *dest = (EditableDest *) data;
1293 dest->text = gtk_editable_get_chars(dest->editable, 0, -1);
1294 }
1296 gboolean
1297 quit_on_esc (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
1298 {
1299 switch (get_group0_keyval (event)) {
1300 case GDK_Escape: // defocus
1301 gtk_widget_destroy(w);
1302 return TRUE;
1303 }
1304 return FALSE;
1305 }
1307 void cmd_new_element_node(GtkObject */*object*/, gpointer /*data*/)
1308 {
1309 EditableDest name;
1310 GtkWidget *window, *create, *cancel, *vbox, *entry, *bbox, *sep;
1312 g_assert(selected_repr != NULL);
1314 window = sp_window_new(NULL, TRUE);
1315 gtk_container_set_border_width(GTK_CONTAINER(window), 4);
1316 gtk_window_set_title(GTK_WINDOW(window), _("New element node..."));
1317 gtk_window_set_policy(GTK_WINDOW(window), FALSE, FALSE, TRUE);
1318 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
1319 gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(dlg));
1320 gtk_window_set_modal(GTK_WINDOW(window), TRUE);
1321 gtk_signal_connect(GTK_OBJECT(window), "destroy", gtk_main_quit, NULL);
1322 gtk_signal_connect(GTK_OBJECT(window), "key-press-event", G_CALLBACK(quit_on_esc), window);
1324 vbox = gtk_vbox_new(FALSE, 4);
1325 gtk_container_add(GTK_CONTAINER(window), vbox);
1327 entry = gtk_entry_new();
1328 gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, TRUE, 0);
1330 sep = gtk_hseparator_new();
1331 gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, TRUE, 0);
1333 bbox = gtk_hbutton_box_new();
1334 gtk_container_set_border_width(GTK_CONTAINER(bbox), 4);
1335 gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
1336 gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, TRUE, 0);
1338 cancel = gtk_button_new_with_label(_("Cancel"));
1339 GTK_WIDGET_SET_FLAGS( GTK_WIDGET(cancel),
1340 GTK_CAN_DEFAULT );
1341 gtk_signal_connect_object( GTK_OBJECT(cancel), "clicked",
1342 G_CALLBACK(gtk_widget_destroy),
1343 GTK_OBJECT(window) );
1344 gtk_container_add(GTK_CONTAINER(bbox), cancel);
1346 create = gtk_button_new_with_label(_("Create"));
1347 gtk_widget_set_sensitive(GTK_WIDGET(create), FALSE);
1348 gtk_signal_connect( GTK_OBJECT(entry), "changed",
1349 G_CALLBACK(on_editable_changed_enable_if_valid_xml_name),
1350 create );
1351 gtk_signal_connect( GTK_OBJECT(create), "clicked",
1352 G_CALLBACK(on_clicked_get_editable_text), &name );
1353 gtk_signal_connect_object( GTK_OBJECT(create), "clicked",
1354 G_CALLBACK(gtk_widget_destroy),
1355 GTK_OBJECT(window) );
1356 GTK_WIDGET_SET_FLAGS( GTK_WIDGET(create),
1357 GTK_CAN_DEFAULT | GTK_RECEIVES_DEFAULT );
1358 gtk_container_add(GTK_CONTAINER(bbox), create);
1360 gtk_widget_show_all(GTK_WIDGET(window));
1361 gtk_window_set_default(GTK_WINDOW(window), GTK_WIDGET(create));
1362 gtk_window_set_focus(GTK_WINDOW(window), GTK_WIDGET(entry));
1364 name.editable = GTK_EDITABLE(entry);
1365 name.text = NULL;
1367 gtk_main();
1369 if (selected_repr != NULL && name.text) {
1370 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(current_document);
1371 Inkscape::XML::Node *new_repr;
1372 new_repr = xml_doc->createElement(name.text);
1373 Inkscape::GC::release(new_repr);
1374 g_free(name.text);
1375 selected_repr->appendChild(new_repr);
1376 set_tree_select(new_repr);
1377 set_dt_select(new_repr);
1379 sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1380 _("Create new element node"));
1381 }
1383 } // end of cmd_new_element_node()
1387 void cmd_new_text_node(GtkObject */*object*/, gpointer /*data*/)
1388 {
1389 g_assert(selected_repr != NULL);
1391 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(current_document);
1392 Inkscape::XML::Node *text = xml_doc->createTextNode("");
1393 selected_repr->appendChild(text);
1395 sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1396 _("Create new text node"));
1398 set_tree_select(text);
1399 set_dt_select(text);
1401 gtk_window_set_focus(GTK_WINDOW(dlg), GTK_WIDGET(content));
1403 }
1405 void cmd_duplicate_node(GtkObject */*object*/, gpointer /*data*/)
1406 {
1407 g_assert(selected_repr != NULL);
1409 Inkscape::XML::Node *parent = sp_repr_parent(selected_repr);
1410 Inkscape::XML::Node *dup = selected_repr->duplicate(parent->document());
1411 parent->addChild(dup, selected_repr);
1413 sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1414 _("Duplicate node"));
1416 GtkCTreeNode *node = sp_xmlview_tree_get_repr_node(SP_XMLVIEW_TREE(tree), dup);
1418 if (node) {
1419 gtk_ctree_select(GTK_CTREE(tree), node);
1420 }
1421 }
1425 void cmd_delete_node(GtkObject */*object*/, gpointer /*data*/)
1426 {
1427 g_assert(selected_repr != NULL);
1428 sp_repr_unparent(selected_repr);
1430 sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1431 _("Delete node"));
1432 }
1436 void cmd_delete_attr(GtkObject */*object*/, gpointer /*data*/)
1437 {
1438 g_assert(selected_repr != NULL);
1439 g_assert(selected_attr != 0);
1440 selected_repr->setAttribute(g_quark_to_string(selected_attr), NULL);
1442 SPObject *updated=current_document->getObjectByRepr(selected_repr);
1443 if (updated) {
1444 // force immediate update of dependant attributes
1445 updated->updateRepr();
1446 }
1448 sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1449 _("Delete attribute"));
1450 }
1454 void cmd_set_attr(GtkObject */*object*/, gpointer /*data*/)
1455 {
1456 g_assert(selected_repr != NULL);
1458 gchar *name = gtk_editable_get_chars(attr_name, 0, -1);
1459 GtkTextIter start;
1460 GtkTextIter end;
1461 gtk_text_buffer_get_bounds( gtk_text_view_get_buffer(attr_value),
1462 &start, &end );
1463 gchar *value = gtk_text_buffer_get_text( gtk_text_view_get_buffer(attr_value),
1464 &start, &end, TRUE );
1466 selected_repr->setAttribute(name, value, false);
1468 g_free(name);
1469 g_free(value);
1471 SPObject *updated = current_document->getObjectByRepr(selected_repr);
1472 if (updated) {
1473 // force immediate update of dependant attributes
1474 updated->updateRepr();
1475 }
1477 sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1478 _("Change attribute"));
1480 /* TODO: actually, the row won't have been created yet. why? */
1481 gint row = sp_xmlview_attr_list_find_row_from_key(GTK_CLIST(attributes),
1482 g_quark_from_string(name));
1483 if (row != -1) {
1484 gtk_clist_select_row(GTK_CLIST(attributes), row, 0);
1485 }
1486 }
1490 void cmd_raise_node(GtkObject */*object*/, gpointer /*data*/)
1491 {
1492 g_assert(selected_repr != NULL);
1494 Inkscape::XML::Node *parent = sp_repr_parent(selected_repr);
1495 g_return_if_fail(parent != NULL);
1496 g_return_if_fail(parent->firstChild() != selected_repr);
1498 Inkscape::XML::Node *ref = NULL;
1499 Inkscape::XML::Node *before = parent->firstChild();
1500 while (before && before->next() != selected_repr) {
1501 ref = before;
1502 before = before->next();
1503 }
1505 parent->changeOrder(selected_repr, ref);
1507 sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1508 _("Raise node"));
1510 set_tree_select(selected_repr);
1511 set_dt_select(selected_repr);
1512 }
1516 void cmd_lower_node(GtkObject */*object*/, gpointer /*data*/)
1517 {
1518 g_assert(selected_repr != NULL);
1519 g_return_if_fail(selected_repr->next() != NULL);
1520 Inkscape::XML::Node *parent = sp_repr_parent(selected_repr);
1522 parent->changeOrder(selected_repr, selected_repr->next());
1524 sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1525 _("Lower node"));
1527 set_tree_select(selected_repr);
1528 set_dt_select(selected_repr);
1529 }
1531 void cmd_indent_node(GtkObject */*object*/, gpointer /*data*/)
1532 {
1533 Inkscape::XML::Node *repr = selected_repr;
1534 g_assert(repr != NULL);
1535 Inkscape::XML::Node *parent = sp_repr_parent(repr);
1536 g_return_if_fail(parent != NULL);
1537 g_return_if_fail(parent->firstChild() != repr);
1539 Inkscape::XML::Node* prev = parent->firstChild();
1540 while (prev && prev->next() != repr) {
1541 prev = prev->next();
1542 }
1543 g_return_if_fail(prev != NULL);
1544 g_return_if_fail(prev->type() == Inkscape::XML::ELEMENT_NODE);
1546 Inkscape::XML::Node* ref = NULL;
1547 if (prev->firstChild()) {
1548 for( ref = prev->firstChild() ; ref->next() ; ref = ref->next() ){};
1549 }
1551 parent->removeChild(repr);
1552 prev->addChild(repr, ref);
1554 sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1555 _("Indent node"));
1556 set_tree_select(repr);
1557 set_dt_select(repr);
1559 } // end of cmd_indent_node()
1563 void cmd_unindent_node(GtkObject */*object*/, gpointer /*data*/)
1564 {
1565 Inkscape::XML::Node *repr = selected_repr;
1566 g_assert(repr != NULL);
1567 Inkscape::XML::Node *parent = sp_repr_parent(repr);
1568 g_return_if_fail(parent);
1569 Inkscape::XML::Node *grandparent = sp_repr_parent(parent);
1570 g_return_if_fail(grandparent);
1572 parent->removeChild(repr);
1573 grandparent->addChild(repr, parent);
1575 sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
1576 _("Unindent node"));
1577 set_tree_select(repr);
1578 set_dt_select(repr);
1580 } // end of cmd_unindent_node()
1583 /*
1584 Local Variables:
1585 mode:c++
1586 c-file-style:"stroustrup"
1587 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1588 indent-tabs-mode:nil
1589 fill-column:99
1590 End:
1591 */
1592 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :