Logo Search packages:      
Sourcecode: inkscape version File versions  Download package

xml-tree.cpp

#define __SP_XMLVIEW_TREE_C__

/**
 * \brief  XML View
 *
 * Authors:
 *   Lauris Kaplinski <lauris@kaplinski.com>
 *   MenTaLguY <mental@rydia.net>
 *   bulia byak <buliabyak@users.sf.net>
 *
 * Copyright (C) 1999-2005 Authors
 * Copyright (C) 2004 David Turner
 *
 * Released under GNU GPL, read the file 'COPYING' for more information
 */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <sigc++/sigc++.h>
#include <string.h>
#include <glib.h>
#include <gtk/gtkmain.h>
#include <gtk/gtkhbox.h>
#include <gtk/gtkvbox.h>
#include <gtk/gtkhpaned.h>
#include <gtk/gtkvpaned.h>
#include <gtk/gtkhseparator.h>
#include <gtk/gtkhbbox.h>
#include <gtk/gtktoolbar.h>
#include <gtk/gtkscrolledwindow.h>
#include <gtk/gtkentry.h>
#include <gtk/gtktooltips.h>
#include <gtk/gtkimage.h>
#include <gtk/gtkarrow.h>

#include <glibmm/i18n.h>
#include "helper/window.h"
#include "macros.h"
#include "../inkscape.h"
#include "../document.h"
#include "../desktop-handles.h"
#include "../selection.h"
#include "../sp-item.h"
#include "../sp-text.h"
#include "../sp-tspan.h"
#include "../sp-root.h"
#include "../sp-defs.h"
#include "../event-context.h"
#include "in-dt-coordsys.h"


#include "../widgets/sp-xmlview-tree.h"
#include "../widgets/sp-xmlview-content.h"
#include "../widgets/sp-xmlview-attr-list.h"

#include "../inkscape-stock.h"
#include "widgets/icon.h"

#include "dialog-events.h"
#include "../prefs-utils.h"
#include "../verbs.h"
#include "../interface.h"

#include "shortcuts.h"
#include <gdk/gdkkeysyms.h>

#include "message-stack.h"
#include "message-context.h"

00072 struct EditableDest {
    GtkEditable * editable;
    gchar * text;
};

static GtkWidget * dlg = NULL;
static sigc::connection sel_changed_connection;
static sigc::connection document_uri_set_connection;
static win_data wd;
// impossible original values to make sure they are read from prefs
static gint x = -1000, y = -1000, w = 0, h = 0;
static gchar *prefs_path = "dialogs.xml";
static GtkWidget *status = NULL;
static Inkscape::MessageStack * _message_stack = NULL;
static Inkscape::MessageContext * _message_context = NULL;
static sigc::connection _message_changed_connection;

static GtkTooltips * tooltips = NULL;
static GtkEditable * attr_name = NULL;
static GtkTextView *attr_value = NULL;
static SPXMLViewTree * tree = NULL;
static SPXMLViewAttrList * attributes = NULL;
static SPXMLViewContent * content = NULL;

static gint blocked = 0;
static SPDesktop * current_desktop = NULL;
static SPDocument * current_document = NULL;
static gint selected_attr = 0;
static Inkscape::XML::Node * selected_repr = NULL;

static void sp_xmltree_desktop_change ( Inkscape::Application *inkscape,  SPDesktop *desktop, GtkWidget *dialog );

static void set_tree_desktop (SPDesktop * desktop);
static void set_tree_document (SPDocument * document);
static void set_tree_repr (Inkscape::XML::Node * repr);

static void set_tree_select (Inkscape::XML::Node * repr);
static void propagate_tree_select (Inkscape::XML::Node * repr);

static Inkscape::XML::Node * get_dt_select (void);
static void set_dt_select (Inkscape::XML::Node * repr);

static void on_tree_select_row (GtkCTree * tree, GtkCTreeNode * node, gint column, gpointer data);
static void on_tree_unselect_row (GtkCTree * tree, GtkCTreeNode * node, gint column, gpointer data);
static void after_tree_move (GtkCTree * tree, GtkCTreeNode * node, GtkCTreeNode * new_parent, GtkCTreeNode * new_sibling, gpointer data);
static void on_destroy (GtkObject * object, gpointer data);
static gboolean on_delete (GtkObject *object, GdkEvent *event, gpointer data);

static void on_tree_select_row_enable_if_element (GtkCTree * tree, GtkCTreeNode * node, gint column, gpointer data);
static void on_tree_select_row_enable_if_mutable (GtkCTree * tree, GtkCTreeNode * node, gint column, gpointer data);
static void on_tree_select_row_show_if_element (GtkCTree * tree, GtkCTreeNode * node, gint column, gpointer data);
static void on_tree_select_row_show_if_text (GtkCTree * tree, GtkCTreeNode * node, gint column, gpointer data);
static void on_tree_select_row_enable_if_indentable (GtkCTree * tree, GtkCTreeNode * node, gint column, gpointer data);
static void on_tree_select_row_enable_if_not_first_child (GtkCTree * tree, GtkCTreeNode * node, gint column, gpointer data);
static void on_tree_select_row_enable_if_not_last_child (GtkCTree * tree, GtkCTreeNode * node, gint column, gpointer data);
static void on_tree_select_row_enable_if_has_grandparent (GtkCTree * tree, GtkCTreeNode * node, gint column, gpointer data);

static void on_tree_unselect_row_clear_text (GtkCTree * tree, GtkCTreeNode * node, gint column, gpointer data);
static void on_tree_unselect_row_disable (GtkCTree * tree, GtkCTreeNode * node, gint column, gpointer data);
static void on_tree_unselect_row_hide (GtkCTree * tree, GtkCTreeNode * node, gint column, gpointer data);

static void on_attr_select_row (GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
static void on_attr_unselect_row (GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
static void on_attr_row_changed ( GtkCList *list, gint row, gpointer data );

static void on_attr_select_row_enable (GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
static void on_attr_unselect_row_disable (GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);

static void on_attr_select_row_set_name_content (GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
static void on_attr_select_row_set_value_content (GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);
static void on_attr_unselect_row_clear_text (GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data);

static void on_editable_changed_enable_if_valid_xml_name (GtkEditable * editable, gpointer data);

static void on_desktop_selection_changed (Inkscape::Selection * selection);
static void on_document_uri_set (gchar const *uri, SPDocument * document);

static void on_clicked_get_editable_text (GtkWidget * widget, gpointer data);

static void _set_status_message (Inkscape::MessageType type, const gchar *message, GtkWidget * dialog);

static void cmd_new_element_node (GtkObject * object, gpointer data);
static void cmd_new_text_node (GtkObject * object, gpointer data);
static void cmd_duplicate_node (GtkObject * object, gpointer data);
static void cmd_delete_node (GtkObject * object, gpointer data);

static void cmd_raise_node (GtkObject * object, gpointer data);
static void cmd_lower_node (GtkObject * object, gpointer data);
static void cmd_indent_node (GtkObject * object, gpointer data);
static void cmd_unindent_node (GtkObject * object, gpointer data);

static void cmd_delete_attr (GtkObject * object, gpointer data);
static void cmd_set_attr (GtkObject * object, gpointer data);

static gboolean sp_xml_tree_key_press (GtkWidget *widget, GdkEventKey *event);


/*
 * \brief Sets the XML status bar when the tree is selected.
 */
void
tree_reset_context ( void )
{
    _message_context->set(Inkscape::NORMAL_MESSAGE,
                          _("<b>Click</b> to select nodes, <b>drag</b> to rearrange."));
}


/*
 * \brief Sets the XML status bar, depending on which attr is selected.
 */
void
attr_reset_context ( gint attr )
{
    if (attr == 0) {
        _message_context->set(Inkscape::NORMAL_MESSAGE,
                              _("<b>Click</b> attribute to edit."));
    }
    else {
        const gchar * name = g_quark_to_string (attr);
        gchar * message = g_strdup_printf(_("Attribute <b>%s</b> selected. Press <b>Ctrl+Enter</b> when done editing to commit changes."), name);
        _message_context->set(Inkscape::NORMAL_MESSAGE, message);
        g_free(message);
    }
}


void
sp_xml_tree_dialog (void)
{
    SPDesktop *desktop = SP_ACTIVE_DESKTOP;

    if (!desktop)
        return;

    if (dlg == NULL)
    { // very long block

        GtkWidget *box, *sw, *paned, *toolbar, *button;
        GtkWidget *text_container, *attr_container, *attr_subpaned_container, *box2;
        GtkWidget *set_attr;

        tooltips = gtk_tooltips_new ();
        gtk_tooltips_enable (tooltips);

        dlg = sp_window_new ("", TRUE);
        if (x == -1000 || y == -1000) {
            x = prefs_get_int_attribute (prefs_path, "x", 0);
            y = prefs_get_int_attribute (prefs_path, "y", 0);
        }
        if (w ==0 || h == 0) {
            w = prefs_get_int_attribute (prefs_path, "w", 0);
            h = prefs_get_int_attribute (prefs_path, "h", 0);
        }
        if (x != 0 || y != 0) {
            gtk_window_move ((GtkWindow *) dlg, x, y);
        } else {
            gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
        }

        if (w && h) {
            gtk_window_resize ((GtkWindow *) dlg, w, h);
        }
        sp_transientize (dlg);
        wd.win = dlg;
        wd.stop = 0;
        g_signal_connect   ( G_OBJECT (INKSCAPE), "activate_desktop", G_CALLBACK (sp_transientize_callback), &wd );

        gtk_signal_connect ( GTK_OBJECT (dlg), "event", GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg );

        gtk_signal_connect ( GTK_OBJECT (dlg), "destroy", G_CALLBACK (on_destroy), dlg);
        gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event", G_CALLBACK (on_delete), dlg);
        g_signal_connect   ( G_OBJECT (INKSCAPE), "shut_down", G_CALLBACK (on_delete), dlg);

        g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_hide", G_CALLBACK (sp_dialog_hide), dlg);
        g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_unhide", G_CALLBACK (sp_dialog_unhide), dlg);


        gtk_container_set_border_width (GTK_CONTAINER (dlg), 0);
        gtk_window_set_default_size (GTK_WINDOW (dlg), 640, 384);

        GtkWidget * vbox = gtk_vbox_new (FALSE, 0);
        gtk_container_add (GTK_CONTAINER (dlg), vbox);

        GtkWidget * hbox = gtk_hbox_new (FALSE, 0);
        gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 2);

        status = gtk_label_new (NULL);
        gtk_misc_set_alignment (GTK_MISC (status), 0.0, 0.5);
        gtk_widget_set_size_request (status, 1, -1);
        gtk_label_set_markup (GTK_LABEL (status), "");
        gtk_box_pack_start (GTK_BOX (hbox), gtk_hbox_new(FALSE, 0), FALSE, FALSE, 4);
        gtk_box_pack_start (GTK_BOX (hbox), status, TRUE, TRUE, 0);

        paned = gtk_hpaned_new ();
        gtk_paned_set_position (GTK_PANED (paned), 256);
        gtk_box_pack_start (GTK_BOX (vbox), paned, TRUE, TRUE, 0);

        _message_stack = new Inkscape::MessageStack();
        _message_context = new Inkscape::MessageContext(_message_stack);
        _message_changed_connection = _message_stack->connectChanged(
            sigc::bind(sigc::ptr_fun(_set_status_message), dlg)
        );

        /* tree view */

        box = gtk_vbox_new (FALSE, 0);
        gtk_paned_pack1 (GTK_PANED (paned), box, FALSE, FALSE);

        tree = SP_XMLVIEW_TREE (sp_xmlview_tree_new (NULL, NULL, NULL));
        gtk_tooltips_set_tip ( tooltips, GTK_WIDGET (tree),
                               _("Drag to reorder nodes"), NULL );

        g_signal_connect ( G_OBJECT (tree), "tree_select_row",
                           G_CALLBACK (on_tree_select_row), NULL );

        g_signal_connect ( G_OBJECT (tree), "tree_unselect_row",
                           G_CALLBACK (on_tree_unselect_row), NULL );

        g_signal_connect_after ( G_OBJECT (tree), "tree_move",
                                 G_CALLBACK (after_tree_move), NULL);

        /* TODO: replace gtk_signal_connect_while_alive() with something
         * else...
         */
        toolbar = gtk_toolbar_new ();
        gtk_toolbar_set_style (GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
        gtk_container_set_border_width (GTK_CONTAINER (toolbar), 0);

        button = gtk_toolbar_append_item (GTK_TOOLBAR (toolbar),
                NULL,
                _("New element node"),
                NULL,
                sp_icon_new( GTK_ICON_SIZE_LARGE_TOOLBAR,
                                    INKSCAPE_STOCK_ADD_XML_ELEMENT_NODE ),
                G_CALLBACK (cmd_new_element_node),
                NULL);

        gtk_signal_connect_while_alive ( GTK_OBJECT (tree),
                        "tree_select_row",
                        G_CALLBACK (on_tree_select_row_enable_if_element),
                        button,
                        GTK_OBJECT (button));

        gtk_signal_connect_while_alive (GTK_OBJECT (tree),
                        "tree_unselect_row",
                        G_CALLBACK (on_tree_unselect_row_disable),
                        button,
                        GTK_OBJECT (button));

        gtk_widget_set_sensitive (GTK_WIDGET (button), FALSE);

        button = gtk_toolbar_append_item (GTK_TOOLBAR (toolbar),
                NULL, _("New text node"), NULL,
                sp_icon_new( GTK_ICON_SIZE_LARGE_TOOLBAR,
                             INKSCAPE_STOCK_ADD_XML_TEXT_NODE ),
                G_CALLBACK (cmd_new_text_node),
                NULL);

        gtk_signal_connect_while_alive (GTK_OBJECT (tree),
                        "tree_select_row",
                        G_CALLBACK (on_tree_select_row_enable_if_element),
                        button,
                        GTK_OBJECT (button));

        gtk_signal_connect_while_alive (GTK_OBJECT (tree),
                        "tree_unselect_row",
                        G_CALLBACK (on_tree_unselect_row_disable),
                        button,
                        GTK_OBJECT (button));

        gtk_widget_set_sensitive (GTK_WIDGET (button), FALSE);

        button = gtk_toolbar_append_item (GTK_TOOLBAR (toolbar),
                NULL, _("Duplicate node"), NULL,
                sp_icon_new( GTK_ICON_SIZE_LARGE_TOOLBAR,
                             INKSCAPE_STOCK_DUPLICATE_XML_NODE ),
                G_CALLBACK (cmd_duplicate_node),
                NULL);

        gtk_signal_connect_while_alive (GTK_OBJECT (tree),
                        "tree_select_row",
                        G_CALLBACK (on_tree_select_row_enable_if_mutable),
                        button,
                        GTK_OBJECT (button));

        gtk_signal_connect_while_alive (GTK_OBJECT (tree), "tree_unselect_row",
                        G_CALLBACK (on_tree_unselect_row_disable),
                        button, GTK_OBJECT (button));

        gtk_widget_set_sensitive (GTK_WIDGET (button), FALSE);

        gtk_toolbar_append_space (GTK_TOOLBAR (toolbar));

        button = gtk_toolbar_append_item (GTK_TOOLBAR (toolbar),
                NULL, _("Delete node"), NULL,
                sp_icon_new( GTK_ICON_SIZE_LARGE_TOOLBAR,
                             INKSCAPE_STOCK_DELETE_XML_NODE ),
                                           G_CALLBACK (cmd_delete_node), NULL );

        gtk_signal_connect_while_alive (GTK_OBJECT (tree), "tree_select_row",
                        G_CALLBACK (on_tree_select_row_enable_if_mutable),
                        button, GTK_OBJECT (button));
        gtk_signal_connect_while_alive (GTK_OBJECT (tree), "tree_unselect_row",
                        G_CALLBACK (on_tree_unselect_row_disable),
                        button, GTK_OBJECT (button));
        gtk_widget_set_sensitive (GTK_WIDGET (button), FALSE);

        gtk_toolbar_append_space (GTK_TOOLBAR (toolbar));

        button = gtk_toolbar_append_item ( GTK_TOOLBAR (toolbar), "<",
                        _("Unindent node"), NULL,
                        gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_IN),
                        G_CALLBACK (cmd_unindent_node), NULL);

        gtk_signal_connect_while_alive (GTK_OBJECT (tree), "tree_select_row",
                    G_CALLBACK (on_tree_select_row_enable_if_has_grandparent),
                    button, GTK_OBJECT (button));

        gtk_signal_connect_while_alive (GTK_OBJECT (tree), "tree_unselect_row",
                        G_CALLBACK (on_tree_unselect_row_disable),
                        button, GTK_OBJECT (button));

        gtk_widget_set_sensitive (GTK_WIDGET (button), FALSE);

        button = gtk_toolbar_append_item (GTK_TOOLBAR (toolbar), ">",
                        _("Indent node"), NULL,
                        gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_IN),
                        G_CALLBACK (cmd_indent_node), NULL);
        gtk_signal_connect_while_alive (GTK_OBJECT (tree), "tree_select_row",
                        G_CALLBACK (on_tree_select_row_enable_if_indentable),
                        button, GTK_OBJECT (button));
        gtk_signal_connect_while_alive (GTK_OBJECT (tree), "tree_unselect_row",
                        (GCallback) on_tree_unselect_row_disable,
                        button, GTK_OBJECT (button));
        gtk_widget_set_sensitive (GTK_WIDGET (button), FALSE);

        button = gtk_toolbar_append_item (GTK_TOOLBAR (toolbar), "^",
                        _("Raise node"), NULL,
                        gtk_arrow_new (GTK_ARROW_UP, GTK_SHADOW_IN),
                        G_CALLBACK (cmd_raise_node), NULL);
        gtk_signal_connect_while_alive (GTK_OBJECT (tree), "tree_select_row",
                    G_CALLBACK (on_tree_select_row_enable_if_not_first_child),
                    button, GTK_OBJECT (button));
        gtk_signal_connect_while_alive (GTK_OBJECT (tree), "tree_unselect_row",
                        G_CALLBACK (on_tree_unselect_row_disable),
                        button, GTK_OBJECT (button));
        gtk_widget_set_sensitive (GTK_WIDGET (button), FALSE);

        button = gtk_toolbar_append_item (GTK_TOOLBAR (toolbar), "v",
                        _("Lower node"), NULL,
                        gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_IN),
                        G_CALLBACK (cmd_lower_node), NULL);
        gtk_signal_connect_while_alive (GTK_OBJECT (tree), "tree_select_row",
                        G_CALLBACK (on_tree_select_row_enable_if_not_last_child),
                        button, GTK_OBJECT (button));
        gtk_signal_connect_while_alive (GTK_OBJECT (tree), "tree_unselect_row",
                        G_CALLBACK (on_tree_unselect_row_disable),
                        button, GTK_OBJECT (button));
        gtk_widget_set_sensitive (GTK_WIDGET (button), FALSE);

        gtk_box_pack_start (GTK_BOX (box), toolbar, FALSE, TRUE, 0);

        sw = gtk_scrolled_window_new (NULL, NULL);
        gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW (sw),
                                         GTK_POLICY_AUTOMATIC,
                                         GTK_POLICY_AUTOMATIC );
        gtk_box_pack_start (GTK_BOX (box), sw, TRUE, TRUE, 0);

        gtk_container_add (GTK_CONTAINER (sw), GTK_WIDGET (tree));

        /* node view */

        box = gtk_vbox_new (FALSE, 0);
        gtk_paned_pack2 (GTK_PANED (paned), box, TRUE, TRUE);

        /* attributes */

        attr_container = gtk_vbox_new (FALSE, 0);
        gtk_box_pack_start ( GTK_BOX (box), GTK_WIDGET (attr_container),
                             TRUE, TRUE, 0 );

        attributes = SP_XMLVIEW_ATTR_LIST (sp_xmlview_attr_list_new (NULL));
        g_signal_connect ( G_OBJECT (attributes), "select_row",
                           G_CALLBACK (on_attr_select_row), NULL);
        g_signal_connect ( G_OBJECT (attributes), "unselect_row",
                           G_CALLBACK (on_attr_unselect_row), NULL);
        g_signal_connect ( G_OBJECT (attributes), "row-value-changed",
                           G_CALLBACK (on_attr_row_changed), NULL);

        toolbar = gtk_toolbar_new ();
        gtk_toolbar_set_style (GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
        gtk_container_set_border_width (GTK_CONTAINER (toolbar), 0);

        button = gtk_toolbar_append_item (GTK_TOOLBAR (toolbar),
                NULL, _("Delete attribute"), NULL,
                sp_icon_new( GTK_ICON_SIZE_LARGE_TOOLBAR,
                             INKSCAPE_STOCK_DELETE_XML_ATTRIBUTE ),
                (GCallback) cmd_delete_attr, NULL);

        gtk_signal_connect_while_alive (GTK_OBJECT (attributes), "select_row",
                        (GCallback) on_attr_select_row_enable, button,
                        GTK_OBJECT (button));

        gtk_signal_connect_while_alive (GTK_OBJECT (attributes), "unselect_row",
                        (GCallback) on_attr_unselect_row_disable, button,
                        GTK_OBJECT (button));

        gtk_signal_connect_while_alive (GTK_OBJECT (tree), "tree_unselect_row",
                        (GCallback) on_tree_unselect_row_disable, button,
                        GTK_OBJECT (button));

        gtk_widget_set_sensitive (GTK_WIDGET (button), FALSE);

        gtk_box_pack_start ( GTK_BOX (attr_container),
                             GTK_WIDGET (toolbar), FALSE, TRUE, 0 );

        attr_subpaned_container = gtk_vpaned_new();
        gtk_box_pack_start ( GTK_BOX (attr_container),
                             GTK_WIDGET (attr_subpaned_container),
                             TRUE, TRUE, 0 );
        gtk_widget_show(attr_subpaned_container);

        sw = gtk_scrolled_window_new (NULL, NULL);
        gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
                GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
        gtk_paned_pack1 ( GTK_PANED(attr_subpaned_container),
                          GTK_WIDGET (sw), TRUE, TRUE );
        gtk_container_add (GTK_CONTAINER (sw), GTK_WIDGET (attributes));

        toolbar = gtk_vbox_new (FALSE, 4);
        gtk_container_set_border_width (GTK_CONTAINER (toolbar), 4);

        box2 = gtk_hbox_new (FALSE, 4);
        gtk_box_pack_start ( GTK_BOX (toolbar), GTK_WIDGET (box2),
                             FALSE, TRUE, 0);

        attr_name = GTK_EDITABLE (gtk_entry_new ());
        gtk_tooltips_set_tip ( tooltips, GTK_WIDGET (attr_name),
                               // TRANSLATORS: "Attribute" is a noun here
                               _("Attribute name"), NULL );

        gtk_signal_connect ( GTK_OBJECT (attributes), "select_row",
                             (GCallback) on_attr_select_row_set_name_content,
                             attr_name);

        gtk_signal_connect ( GTK_OBJECT (attributes), "unselect_row",
                             (GCallback) on_attr_unselect_row_clear_text,
                             attr_name);

        gtk_signal_connect ( GTK_OBJECT (tree), "tree_unselect_row",
                             (GCallback) on_tree_unselect_row_clear_text,
                             attr_name);

        gtk_box_pack_start ( GTK_BOX (box2), GTK_WIDGET (attr_name),
                             TRUE, TRUE, 0);

        set_attr = gtk_button_new ();
        gtk_tooltips_set_tip ( tooltips, GTK_WIDGET (set_attr),
                               // TRANSLATORS: "Set" is a verb here
                               _("Set attribute"), NULL );
        // TRANSLATORS: "Set" is a verb here
        GtkWidget *set_label = gtk_label_new (_("Set"));
        gtk_container_add (GTK_CONTAINER (set_attr), set_label);

        gtk_signal_connect ( GTK_OBJECT (set_attr), "clicked",
                             (GCallback) cmd_set_attr, NULL);
        gtk_signal_connect ( GTK_OBJECT (attr_name), "changed",
                    (GCallback) on_editable_changed_enable_if_valid_xml_name,
                    set_attr );
        gtk_widget_set_sensitive (GTK_WIDGET (set_attr), FALSE);

        gtk_box_pack_start (GTK_BOX (box2), set_attr, FALSE, FALSE, 0);

        sw = gtk_scrolled_window_new (NULL, NULL);
        gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW (sw),
                                         GTK_POLICY_AUTOMATIC,
                                         GTK_POLICY_AUTOMATIC );
        gtk_scrolled_window_set_shadow_type ( GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN );
        gtk_box_pack_start (GTK_BOX (toolbar), sw, TRUE, TRUE, 0);

        attr_value = (GtkTextView *) gtk_text_view_new ();
        gtk_text_view_set_wrap_mode ((GtkTextView *) attr_value, GTK_WRAP_CHAR);
        gtk_tooltips_set_tip ( tooltips, GTK_WIDGET (attr_value),
                               // TRANSLATORS: "Attribute" is a noun here
                               _("Attribute value"), NULL );
        gtk_signal_connect ( GTK_OBJECT (attributes), "select_row",
                             (GCallback) on_attr_select_row_set_value_content,
                             attr_value );
        gtk_signal_connect ( GTK_OBJECT (attributes), "unselect_row",
                             (GCallback) on_attr_unselect_row_clear_text,
                             attr_value );
        gtk_signal_connect ( GTK_OBJECT (tree), "tree_unselect_row",
                             (GCallback) on_tree_unselect_row_clear_text,
                             attr_value );
        gtk_text_view_set_editable (attr_value, TRUE);
        gtk_container_add ( GTK_CONTAINER (sw),
                            GTK_WIDGET (attr_value) );

        gtk_paned_pack2 ( GTK_PANED (attr_subpaned_container),
                          GTK_WIDGET (toolbar), FALSE, TRUE );

        /* text */

        sw = gtk_scrolled_window_new (NULL, NULL);
        gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW (sw),
                                         GTK_POLICY_AUTOMATIC,
                                         GTK_POLICY_AUTOMATIC );
        gtk_scrolled_window_set_shadow_type ( GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN );
        gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (sw), TRUE, TRUE, 0);

        content = SP_XMLVIEW_CONTENT (sp_xmlview_content_new (NULL));
        gtk_container_add (GTK_CONTAINER (sw), GTK_WIDGET (content));

        text_container = sw;

        /* initial show/hide */

        gtk_widget_show_all (GTK_WIDGET (dlg));

        gtk_signal_connect_while_alive (GTK_OBJECT (tree), "tree_select_row",
                        (GCallback) on_tree_select_row_show_if_element,
                        attr_container, GTK_OBJECT (attr_container));

        gtk_signal_connect_while_alive (GTK_OBJECT (tree), "tree_unselect_row",
                        (GCallback) on_tree_unselect_row_hide,
                        attr_container, GTK_OBJECT (attr_container));

        gtk_widget_hide (attr_container);

        gtk_signal_connect_while_alive (GTK_OBJECT (tree), "tree_select_row",
                        (GCallback) on_tree_select_row_show_if_text,
                        text_container, GTK_OBJECT (text_container));

        gtk_signal_connect_while_alive (GTK_OBJECT (tree), "tree_unselect_row",
                        (GCallback) on_tree_unselect_row_hide,
                        text_container, GTK_OBJECT (text_container));

        gtk_widget_hide (text_container);

        g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop",
                           G_CALLBACK (sp_xmltree_desktop_change), dlg);

        g_signal_connect ( G_OBJECT (INKSCAPE), "deactivate_desktop",
                           G_CALLBACK (sp_xmltree_desktop_change), dlg);

        g_signal_connect ((GObject *) dlg, "key_press_event", (GCallback) sp_xml_tree_key_press, NULL);

        tree_reset_context();
    } // end of if (dlg == NULL)

    gtk_window_present ((GtkWindow *) dlg);

    g_assert (desktop != NULL);
    set_tree_desktop (desktop);

} // end of sp_xml_tree_dialog()

static gboolean
sp_xml_tree_key_press (GtkWidget *widget, GdkEventKey *event)
{

    unsigned int shortcut = get_group0_keyval (event) |
        ( event->state & GDK_SHIFT_MASK ?
          SP_SHORTCUT_SHIFT_MASK : 0 ) |
        ( event->state & GDK_CONTROL_MASK ?
          SP_SHORTCUT_CONTROL_MASK : 0 ) |
        ( event->state & GDK_MOD1_MASK ?
          SP_SHORTCUT_ALT_MASK : 0 );

    /* fixme: if you need to add more xml-tree-specific callbacks, you should probably upgrade
     * the sp_shortcut mechanism to take into account windows. */
    if (shortcut == (SP_SHORTCUT_CONTROL_MASK | GDK_Return)) {
        cmd_set_attr (NULL, NULL);
        return true;
    }
    return false;
}


static void
sp_xmltree_desktop_change ( Inkscape::Application *inkscape,
                          SPDesktop *desktop,
                          GtkWidget *dialog )
{
    if (!desktop) return;
    set_tree_desktop (desktop);
}


void
set_tree_desktop (SPDesktop * desktop)
{
    if ( desktop == current_desktop )
        return;

    if (current_desktop) {
        sel_changed_connection.disconnect();
//        sp_signal_disconnect_by_data (current_desktop, dlg);
    }
    current_desktop = desktop;
    if (desktop) {
        sel_changed_connection = SP_DT_SELECTION(desktop)->connectChanged(&on_desktop_selection_changed);
        set_tree_document (SP_DT_DOCUMENT (desktop));
    } else {
        set_tree_document (NULL);
    }

} // end of set_tree_desktop()



void
set_tree_document (SPDocument * document)
{
    if ( document == current_document )
        return;

    if (current_document) {
        document_uri_set_connection.disconnect();
    }
    current_document = document;
    if (current_document) {

        document_uri_set_connection = current_document->connectURISet(sigc::bind(sigc::ptr_fun(&on_document_uri_set), current_document));
        on_document_uri_set(SP_DOCUMENT_URI (current_document), current_document);
        set_tree_repr (sp_document_repr_root (current_document));

    } else {
        set_tree_repr (NULL);
    }
}



void
set_tree_repr (Inkscape::XML::Node * repr)
{
    if ( repr == selected_repr )
        return;

    gtk_clist_freeze (GTK_CLIST (tree));

    sp_xmlview_tree_set_repr (tree, repr);

    if (repr) {
        set_tree_select (get_dt_select ());
    } else {
        set_tree_select (NULL);
    }

    gtk_clist_thaw (GTK_CLIST (tree));

    propagate_tree_select (selected_repr);

}



void
set_tree_select (Inkscape::XML::Node * repr)
{
    if (selected_repr) {
        sp_repr_unref (selected_repr);
    }

    selected_repr = repr;
    if (repr) {
        GtkCTreeNode * node;

        sp_repr_ref (selected_repr);

        node = sp_xmlview_tree_get_repr_node (SP_XMLVIEW_TREE (tree), repr);
        if (node) {
            GtkCTreeNode * parent;

            gtk_ctree_select (GTK_CTREE (tree), node);

            parent = GTK_CTREE_ROW (node)->parent;
            while (parent) {
                gtk_ctree_expand (GTK_CTREE (tree), parent);
                parent = GTK_CTREE_ROW (parent)->parent;
            }

            gtk_ctree_node_moveto (GTK_CTREE (tree), node, 0, 0.66, 0.0);
        }
    } else {
        gtk_clist_unselect_all (GTK_CLIST (tree));
    }
    propagate_tree_select (repr);
}



void
propagate_tree_select (Inkscape::XML::Node * repr)
{
    if (repr && repr->type() == Inkscape::XML::ELEMENT_NODE) {
        sp_xmlview_attr_list_set_repr (attributes, repr);
    } else {
        sp_xmlview_attr_list_set_repr (attributes, NULL);
    }

    if (repr && ( repr->type() == Inkscape::XML::TEXT_NODE || repr->type() == Inkscape::XML::COMMENT_NODE ) ) {
        sp_xmlview_content_set_repr (content, repr);
    } else {
        sp_xmlview_content_set_repr (content, NULL);
    }
}


Inkscape::XML::Node *
get_dt_select (void)
{
    if (!current_desktop)
        return NULL;

    return SP_DT_SELECTION(current_desktop)->singleRepr();
}



void
set_dt_select (Inkscape::XML::Node *repr)
{
    if (!current_desktop)
        return;

    Inkscape::Selection *selection = SP_DT_SELECTION(current_desktop);

    SPObject *object;
    if (repr) {
        while ( ( repr->type() != Inkscape::XML::ELEMENT_NODE )
                && sp_repr_parent(repr) )
        {
            repr = sp_repr_parent(repr);
        } // end of while loop

        object = SP_DT_DOCUMENT(current_desktop)->getObjectByRepr(repr);
    } else {
        object = NULL;
    }

    blocked++;
    if ( object && in_dt_coordsys(*object)
         && !( SP_IS_TSPAN (object)  ||
               SP_IS_STRING (object) ||
               SP_IS_ROOT (object)     ) )
    {
            /* We cannot set selection to tspan, string, or root; failures and
             * crashes will occur. */
            /* TODO: when a tspan is highlighted, set selection to its parent
             * text
             */
            selection->set(SP_ITEM(object));
    }
    blocked--;

} // end of set_dt_select()


void
on_tree_select_row ( GtkCTree * tree,
                     GtkCTreeNode * node,
                     gint column,
                     gpointer data )
{
    if (blocked)
        return;

    Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node);
    g_assert( repr != NULL );

    if ( selected_repr == repr )
        return;

    if (selected_repr) {
        sp_repr_unref(selected_repr);
        selected_repr = NULL;
    }
    selected_repr = repr;
    sp_repr_ref(selected_repr);

    propagate_tree_select(selected_repr);

    set_dt_select(selected_repr);

    tree_reset_context();
}

void
on_tree_unselect_row ( GtkCTree * tree,
                       GtkCTreeNode * node,
                       gint column,
                       gpointer data )
{
    Inkscape::XML::Node * repr;

    if (blocked)
        return;

    repr = sp_xmlview_tree_node_get_repr (SP_XMLVIEW_TREE (tree), node);
    propagate_tree_select (NULL);
    set_dt_select (NULL);

    if ( selected_repr && ( selected_repr == repr ) ) {
        sp_repr_unref (selected_repr);
        selected_repr = NULL;
        selected_attr = 0;
    }
}



void
after_tree_move ( GtkCTree * tree,
                  GtkCTreeNode * node,
                  GtkCTreeNode * new_parent,
                  GtkCTreeNode * new_sibling,
                  gpointer data )
{
    if (GTK_CTREE_ROW (node)->parent  == new_parent &&
        GTK_CTREE_ROW (node)->sibling == new_sibling)
    {
        sp_document_done (current_document);
    } else {
        sp_document_cancel (current_document);
    }
}


static void
on_destroy (GtkObject * object, gpointer data)
{
    set_tree_desktop (NULL);
    gtk_object_destroy (GTK_OBJECT (tooltips));
    tooltips = NULL;
    sp_signal_disconnect_by_data (INKSCAPE, dlg);
    wd.win = dlg = NULL;
    wd.stop = 0;

    _message_changed_connection.disconnect();
    delete _message_context;
    _message_context = NULL;
    Inkscape::GC::release(_message_stack);
    _message_stack = NULL;
    _message_changed_connection.~connection();

    status = NULL;
}



static gboolean
on_delete (GtkObject *object, GdkEvent *event, gpointer data)
{
    gtk_window_get_position ((GtkWindow *) dlg, &x, &y);
    gtk_window_get_size ((GtkWindow *) dlg, &w, &h);

    prefs_set_int_attribute (prefs_path, "x", x);
    prefs_set_int_attribute (prefs_path, "y", y);
    prefs_set_int_attribute (prefs_path, "w", w);
    prefs_set_int_attribute (prefs_path, "h", h);

    return FALSE; // which means, go ahead and destroy it
}


static void
_set_status_message (Inkscape::MessageType type, const gchar *message, GtkWidget * dialog)
{
    if (status) {
        gtk_label_set_markup (GTK_LABEL(status), message ? message : "");
    }
}


void
on_tree_select_row_enable ( GtkCTree * tree,
                            GtkCTreeNode * node,
                            gint column,
                            gpointer data )
{
    gtk_widget_set_sensitive (GTK_WIDGET (data), TRUE);
}



void
on_tree_select_row_enable_if_element ( GtkCTree * tree,
                                       GtkCTreeNode * node,
                                       gint column,
                                       gpointer data )
{
    Inkscape::XML::Node * repr;
    repr = sp_xmlview_tree_node_get_repr (SP_XMLVIEW_TREE (tree), node);

    if (repr->type() == Inkscape::XML::ELEMENT_NODE) {
        gtk_widget_set_sensitive (GTK_WIDGET (data), TRUE);
    } else {
        gtk_widget_set_sensitive (GTK_WIDGET (data), FALSE);
    }
}



void
on_tree_select_row_show_if_element ( GtkCTree * tree, GtkCTreeNode * node,
                                     gint column, gpointer data)
{
    Inkscape::XML::Node * repr;
    repr = sp_xmlview_tree_node_get_repr (SP_XMLVIEW_TREE (tree), node);

    if (repr->type() == Inkscape::XML::ELEMENT_NODE) {
        gtk_widget_show (GTK_WIDGET (data));
    } else {
        gtk_widget_hide (GTK_WIDGET (data));
    }
}



void
on_tree_select_row_show_if_text ( GtkCTree * tree, GtkCTreeNode * node,
                                  gint column, gpointer data )
{
    Inkscape::XML::Node * repr;
    repr = sp_xmlview_tree_node_get_repr (SP_XMLVIEW_TREE (tree), node);

    if ( repr->type() == Inkscape::XML::TEXT_NODE || repr->type() == Inkscape::XML::COMMENT_NODE ) {
        gtk_widget_show (GTK_WIDGET (data));
    } else {
        gtk_widget_hide (GTK_WIDGET (data));
    }
}


gboolean
xml_tree_node_mutable ( GtkCTreeNode * node )
{
    // top-level is immutable, obviously
    if (!GTK_CTREE_ROW (node)->parent) {
        return false;
    }

    // if not in base level (where namedview, defs, etc go), we're mutable
    if (GTK_CTREE_ROW(GTK_CTREE_ROW (node)->parent)->parent) {
        return true;
    }

    Inkscape::XML::Node * repr;
    repr = sp_xmlview_tree_node_get_repr (SP_XMLVIEW_TREE (tree), node);
    g_assert (repr);

    // don't let "defs" or "namedview" disappear
    if ( !strcmp(repr->name(),"svg:defs") ||
         !strcmp(repr->name(),"sodipodi:namedview") ) {
        return false;
    }

    // everyone else is okay, I guess.  :)
    return true;
}


void
on_tree_select_row_enable_if_mutable ( GtkCTree * tree, GtkCTreeNode * node,
                                        gint column, gpointer data )
{

    gtk_widget_set_sensitive (GTK_WIDGET (data), xml_tree_node_mutable(node));
}



void
on_tree_unselect_row_disable ( GtkCTree * tree, GtkCTreeNode * node,
                               gint column, gpointer data )
{
    gtk_widget_set_sensitive (GTK_WIDGET (data), FALSE);
}



void
on_tree_unselect_row_hide ( GtkCTree * tree, GtkCTreeNode * node,
                            gint column, gpointer data )
{
    gtk_widget_hide (GTK_WIDGET (data));
}



void
on_tree_unselect_row_clear_text ( GtkCTree * tree, GtkCTreeNode * node,
                                  gint column, gpointer data )
{
    if (GTK_IS_EDITABLE (data)) {
        gtk_editable_delete_text (GTK_EDITABLE (data), 0, -1);
    } else if (GTK_IS_TEXT_VIEW (data)) {
        GtkTextBuffer *tb;
        tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (data));
        gtk_text_buffer_set_text (tb, "", 0);
    }
}


void
on_attr_select_row ( GtkCList *list, gint row, gint column,
                     GdkEventButton *event, gpointer data )
{
    selected_attr = sp_xmlview_attr_list_get_row_key (list, row);
    gtk_window_set_focus (GTK_WINDOW (dlg), GTK_WIDGET (attr_value));

    attr_reset_context ( selected_attr );
}


void
on_attr_unselect_row ( GtkCList *list, gint row, gint column,
                       GdkEventButton *event, gpointer data )
{
    selected_attr = 0;
    attr_reset_context ( selected_attr );
}


void
on_attr_row_changed ( GtkCList *list, gint row, gpointer data )
{
    gint attr = sp_xmlview_attr_list_get_row_key (list, row);

    if (attr == selected_attr) {
        /* if the attr changed, reselect the row in the list to sync
           the edit box */

        /*
        // get current attr values
        const gchar * name = g_quark_to_string (sp_xmlview_attr_list_get_row_key (list, row));
        const gchar * value = selected_repr->attribute(name);

        g_warning("value: '%s'",value);

        // get the edit box value
        GtkTextIter start, end;
        gtk_text_buffer_get_bounds ( gtk_text_view_get_buffer (attr_value),
                                     &start, &end );
        gchar * text = gtk_text_buffer_get_text ( gtk_text_view_get_buffer (attr_value),
                                       &start, &end, TRUE );
        g_warning("text: '%s'",text);

        // compare to edit box
        if (strcmp(text,value)) {
            // issue warning if they're different
            _message_stack->flash(Inkscape::WARNING_MESSAGE,
                                  _("Attribute changed in GUI while editing values!"));
        }
        g_free (text);

        */
        gtk_clist_unselect_row ( GTK_CLIST(list), row, 0 );
        gtk_clist_select_row ( GTK_CLIST(list), row, 0 );
    }
}


void
on_attr_select_row_set_name_content ( GtkCList *list, gint row,
                                      gint column, GdkEventButton *event,
                                      gpointer data )
{
    GtkEditable * editable;
    const gchar * name;
    gint pos;
    editable = GTK_EDITABLE (data);
    name = g_quark_to_string (sp_xmlview_attr_list_get_row_key (list, row));
    gtk_editable_delete_text (editable, 0, -1);
    pos = 0;
    gtk_editable_insert_text (editable, name, strlen(name), &pos);

}



void
on_attr_select_row_set_value_content ( GtkCList *list, gint row, gint column,
                                       GdkEventButton *event,
                                       gpointer data )
{
    GtkTextBuffer *tb;
    const gchar *name, *value;
    tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (data));
    name = g_quark_to_string (sp_xmlview_attr_list_get_row_key (list, row));
    value = selected_repr->attribute(name);
    if (!value) {
        value = "";
    }
    gtk_text_buffer_set_text (tb, value, strlen (value));
}


void
on_tree_select_row_enable_if_indentable ( GtkCTree * tree, GtkCTreeNode * node,
                                          gint column, gpointer data )
{
    gboolean indentable = FALSE;

    if ( xml_tree_node_mutable( node ) ) {
        Inkscape::XML::Node * repr, * prev;
        repr = sp_xmlview_tree_node_get_repr (SP_XMLVIEW_TREE (tree), node);

        Inkscape::XML::Node *parent=repr->parent();
        if ( parent && repr != parent->firstChild() ) {
            g_assert (parent->firstChild());

            // skip to the child just before the current repr
            for ( prev = parent->firstChild() ;
                  prev && prev->next() != repr ;
                  prev = prev->next() );

            if (prev && prev->type() == Inkscape::XML::ELEMENT_NODE) {
                indentable = TRUE;
            }
        }
    }

    gtk_widget_set_sensitive (GTK_WIDGET (data), indentable);
}



void
on_tree_select_row_enable_if_not_first_child ( GtkCTree * tree,
                                               GtkCTreeNode * node,
                                               gint column,
                                               gpointer data )
{
    Inkscape::XML::Node * repr;
    repr = sp_xmlview_tree_node_get_repr (SP_XMLVIEW_TREE (tree), node);

    Inkscape::XML::Node *parent=repr->parent();
    if ( parent && repr != parent->firstChild() ) {
        gtk_widget_set_sensitive (GTK_WIDGET (data), TRUE);
    } else {
        gtk_widget_set_sensitive (GTK_WIDGET (data), FALSE);
    }
}



void
on_tree_select_row_enable_if_not_last_child ( GtkCTree * tree,
                                              GtkCTreeNode * node,
                                              gint column, gpointer data )
{
    Inkscape::XML::Node * repr;
    repr = sp_xmlview_tree_node_get_repr (SP_XMLVIEW_TREE (tree), node);

    Inkscape::XML::Node *parent=repr->parent();
    if ( parent && parent->parent() && repr->next() ) {
        gtk_widget_set_sensitive (GTK_WIDGET (data), TRUE);
    } else {
        gtk_widget_set_sensitive (GTK_WIDGET (data), FALSE);
    }
}



void
on_tree_select_row_enable_if_has_grandparent ( GtkCTree * tree,
                                               GtkCTreeNode * node,
                                               gint column, gpointer data )
{
    GtkCTreeNode * parent, * grandparent;
    parent = GTK_CTREE_ROW (node)->parent;

    if (parent) {
        grandparent = GTK_CTREE_ROW (parent)->parent;
        if (grandparent) {
            gtk_widget_set_sensitive (GTK_WIDGET (data), TRUE);
        } else {
            gtk_widget_set_sensitive (GTK_WIDGET (data), FALSE);
        }
    } else {
        gtk_widget_set_sensitive (GTK_WIDGET (data), FALSE);
    }
}



void
on_attr_select_row_enable ( GtkCList *list, gint row, gint column,
                            GdkEventButton *event, gpointer data )
{
    gtk_widget_set_sensitive ( GTK_WIDGET (data), TRUE );
}



void
on_attr_unselect_row_disable ( GtkCList *list, gint row, gint column,
                               GdkEventButton *event, gpointer data )
{
    gtk_widget_set_sensitive ( GTK_WIDGET (data), FALSE );
}



void
on_attr_unselect_row_clear_text ( GtkCList *list, gint row, gint column,
                                  GdkEventButton *event, gpointer data )
{
    if (GTK_IS_EDITABLE (data)) {
        gtk_editable_delete_text (GTK_EDITABLE (data), 0, -1);
    } else if (GTK_IS_TEXT_VIEW (data)) {
        GtkTextBuffer *tb;
        tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (data));
        gtk_text_buffer_set_text (tb, "", 0);
    }
}



void
on_editable_changed_enable_if_valid_xml_name ( GtkEditable * editable,
                                               gpointer data )
{
    gchar * text;
    text = gtk_editable_get_chars (editable, 0, -1);

    /* TODO: need to do checking a little more rigorous than this */

    if (strlen(text)) {
        gtk_widget_set_sensitive (GTK_WIDGET (data), TRUE);
    } else {
        gtk_widget_set_sensitive (GTK_WIDGET (data), FALSE);
    }
    g_free (text);
}



void
on_desktop_selection_changed (Inkscape::Selection * selection)
{
    if (!blocked++) {
        set_tree_select (get_dt_select ());
    }
    blocked--;
}


void on_document_uri_set(gchar const *uri, SPDocument *document) {
    gchar *t;
    gchar title[500];

    sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_DIALOG_XML_EDITOR), title);
    t = g_strdup_printf ("%s: %s", SP_DOCUMENT_NAME (document), title);
    gtk_window_set_title (GTK_WINDOW (dlg), t);
    g_free (t);
}



void
on_clicked_get_editable_text (GtkWidget * widget, gpointer data)
{
    EditableDest * dest;
    dest = (EditableDest *) data;
    dest->text = gtk_editable_get_chars (dest->editable, 0, -1);
}



void
cmd_new_element_node (GtkObject * object, gpointer data)
{

    EditableDest name;
    GtkWidget * window, * create, * cancel, * vbox, * entry, * bbox, * sep;

    g_assert (selected_repr != NULL);

    window = sp_window_new (NULL, TRUE);
    gtk_container_set_border_width (GTK_CONTAINER (window), 4);
    gtk_window_set_title (GTK_WINDOW (window), _("New element node..."));
    gtk_window_set_policy (GTK_WINDOW (window), FALSE, FALSE, TRUE);
    gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
    gtk_window_set_transient_for (GTK_WINDOW (window), GTK_WINDOW (dlg));
    gtk_window_set_modal (GTK_WINDOW (window), TRUE);
    gtk_signal_connect (GTK_OBJECT (window), "destroy", gtk_main_quit, NULL);

    vbox = gtk_vbox_new (FALSE, 4);
    gtk_container_add (GTK_CONTAINER (window), vbox);

    entry = gtk_entry_new ();
    gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, TRUE, 0);

    sep = gtk_hseparator_new ();
    gtk_box_pack_start (GTK_BOX (vbox), sep, FALSE, TRUE, 0);

    bbox = gtk_hbutton_box_new ();
    gtk_container_set_border_width (GTK_CONTAINER (bbox), 4);
    gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END);
    gtk_box_pack_start (GTK_BOX (vbox), bbox, FALSE, TRUE, 0);

    cancel = gtk_button_new_with_label (_("Cancel"));
    gtk_signal_connect_object ( GTK_OBJECT (cancel), "clicked",
                                G_CALLBACK (gtk_widget_destroy),
                                GTK_OBJECT (window) );
    gtk_container_add (GTK_CONTAINER (bbox), cancel);

    create = gtk_button_new_with_label (_("Create"));
    gtk_widget_set_sensitive (GTK_WIDGET (create), FALSE);
    gtk_signal_connect ( GTK_OBJECT (entry), "changed",
                    G_CALLBACK (on_editable_changed_enable_if_valid_xml_name),
                    create );
    gtk_signal_connect ( GTK_OBJECT (create), "clicked",
                         G_CALLBACK (on_clicked_get_editable_text), &name );
    gtk_signal_connect_object ( GTK_OBJECT (create), "clicked",
                                G_CALLBACK (gtk_widget_destroy),
                                GTK_OBJECT (window) );
    GTK_WIDGET_SET_FLAGS ( GTK_WIDGET (create),
                           GTK_CAN_DEFAULT | GTK_RECEIVES_DEFAULT );
    gtk_container_add (GTK_CONTAINER (bbox), create);

    gtk_widget_show_all (GTK_WIDGET (window));
    gtk_window_set_default (GTK_WINDOW (window), GTK_WIDGET (create));
    gtk_window_set_focus (GTK_WINDOW (window), GTK_WIDGET (entry));

    name.editable = GTK_EDITABLE (entry);
    name.text = NULL;

    gtk_main();

    g_assert (selected_repr != NULL);

    if (name.text) {
        Inkscape::XML::Node * new_repr;
        new_repr = sp_repr_new (name.text);
        g_free (name.text);
        selected_repr->appendChild(new_repr);
        set_tree_select (new_repr);
        set_dt_select (new_repr);
    }

} // end of cmd_new_element_node()



void
cmd_new_text_node (GtkObject * object, gpointer data)
{
    Inkscape::XML::Node * text;

    g_assert (selected_repr != NULL);

    text = sp_repr_new_text ("");
    selected_repr->appendChild(text);

    sp_document_done (current_document);

    set_tree_select (text);
    set_dt_select (text);

    gtk_window_set_focus (GTK_WINDOW (dlg), GTK_WIDGET (content));

}

void
cmd_duplicate_node (GtkObject * object, gpointer data)
{
    Inkscape::XML::Node * parent;
    Inkscape::XML::Node * dup;
    GtkCTreeNode * node;

    g_assert (selected_repr != NULL);

    parent = sp_repr_parent (selected_repr);
    dup = selected_repr->duplicate();
    sp_repr_add_child (parent, dup, selected_repr);

    sp_document_done (current_document);

    node = sp_xmlview_tree_get_repr_node (SP_XMLVIEW_TREE (tree), dup);

    if (node)
        gtk_ctree_select (GTK_CTREE (tree), node);
}



void
cmd_delete_node (GtkObject * object, gpointer data)
{
    g_assert (selected_repr != NULL);
    sp_repr_unparent (selected_repr);

    sp_document_done (current_document);
}



void
cmd_delete_attr (GtkObject * object, gpointer data)
{
    g_assert (selected_repr != NULL);
    g_assert (selected_attr != 0);
    sp_repr_set_attr (selected_repr, g_quark_to_string (selected_attr), NULL);

    SPObject *updated=current_document->getObjectByRepr(selected_repr);
    if (updated) {
        // force immediate update of dependant attributes
        updated->updateRepr();
    }

    sp_document_done (current_document);
}



void
cmd_set_attr (GtkObject * object, gpointer data)
{
    GtkTextIter start, end;
    gchar * name;
    gchar * value;
    gint row;

    g_assert (selected_repr != NULL);

    name = gtk_editable_get_chars (attr_name, 0, -1);
    gtk_text_buffer_get_bounds ( gtk_text_view_get_buffer (attr_value),
                                 &start, &end );
    value = gtk_text_buffer_get_text ( gtk_text_view_get_buffer (attr_value),
                                       &start, &end, TRUE );

    if (!sp_repr_set_attr (selected_repr, name, value)) {
        gchar * message = g_strdup_printf(_("Cannot set <b>%s</b>: Another element with value <b>%s</b> already exists!"), name, value);
        _message_stack->flash(Inkscape::WARNING_MESSAGE, message);
        g_free(message);
    }

    g_free (name);
    g_free (value);

    SPObject *updated=current_document->getObjectByRepr(selected_repr);
    if (updated) {
        // force immediate update of dependant attributes
        updated->updateRepr();
    }

    sp_document_done (current_document);

    /* TODO: actually, the row won't have been created yet.  why? */
    row = sp_xmlview_attr_list_find_row_from_key ( GTK_CLIST (attributes),
                                                   g_quark_from_string (name) );
    if (row != -1) {
        gtk_clist_select_row (GTK_CLIST (attributes), row, 0);
    }
}



void
cmd_raise_node (GtkObject * object, gpointer data)
{
    Inkscape::XML::Node * before, * parent, * ref;
    g_assert (selected_repr != NULL);

    parent = sp_repr_parent (selected_repr);
    g_return_if_fail (parent != NULL);
    g_return_if_fail (parent->firstChild() != selected_repr);

    ref = NULL;
    before = parent->firstChild() ;
    while (before && before->next() != selected_repr) {
        ref = before;
        before = before->next();
    }

    sp_repr_change_order (parent, selected_repr, ref);

    sp_document_done (current_document);

    set_tree_select (selected_repr);
    set_dt_select (selected_repr);
}



void
cmd_lower_node (GtkObject * object, gpointer data)
{
    Inkscape::XML::Node * parent;
    g_assert (selected_repr != NULL);
    g_return_if_fail (selected_repr->next() != NULL);
    parent = sp_repr_parent (selected_repr);

    sp_repr_change_order (parent, selected_repr, selected_repr->next());

    sp_document_done (current_document);

    set_tree_select (selected_repr);
    set_dt_select (selected_repr);


}

void
cmd_indent_node (GtkObject * object, gpointer data)
{
    int ok;
    Inkscape::XML::Node * prev, * parent, * repr, * ref;

    repr = selected_repr;
    g_assert (repr != NULL);
    parent = sp_repr_parent (repr);
    g_return_if_fail (parent != NULL);
    g_return_if_fail (parent->firstChild() != repr);

    prev = parent->firstChild();
    while (prev && prev->next() != repr) {
        prev = prev->next();
    }
    g_return_if_fail (prev != NULL);
    g_return_if_fail (prev->type() == Inkscape::XML::ELEMENT_NODE);

    if (prev->firstChild()) {
        for ( ref = prev->firstChild() ; ref->next() ; ref = ref->next() );
    } else {
        ref = NULL;
    }

    ok = sp_repr_remove_child (parent, repr) &&
        sp_repr_add_child (prev, repr, ref);

    if (ok) {
        sp_document_done (current_document);
        set_tree_select (repr);
        set_dt_select (repr);
    } else {
        sp_document_cancel (current_document);
    }

} // end of cmd_indent_node()



void
cmd_unindent_node (GtkObject * object, gpointer data)
{
    Inkscape::XML::Node * grandparent, * parent, * repr;
    int ok;

    repr = selected_repr;
    g_assert (repr != NULL);
    parent = sp_repr_parent (repr);
    g_return_if_fail (parent);
    grandparent = sp_repr_parent (parent);
    g_return_if_fail (grandparent);

    ok = sp_repr_remove_child (parent, repr) &&
        sp_repr_add_child (grandparent, repr, parent);

    if (ok) {
        sp_document_done (current_document);
        set_tree_select (repr);
        set_dt_select (repr);
    } else {
        sp_document_cancel (current_document);
    }

} // end of cmd_unindent_node()


/*
  Local Variables:
  mode:c++
  c-file-style:"stroustrup"
  c-file-offsets:((innamespace . 0)(inline-open . 0))
  indent-tabs-mode:nil
  fill-column:99
  End:
*/
// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :

Generated by  Doxygen 1.6.0   Back to index