You are viewing a plain text version of this content. The canonical link for it is here.
Posted to batik-commits@xmlgraphics.apache.org by ca...@apache.org on 2007/11/13 01:40:58 UTC

svn commit: r594367 [5/9] - in /xmlgraphics/batik/trunk: ./ resources/org/apache/batik/apps/svgbrowser/resources/ resources/org/apache/batik/util/gui/resources/ sources-1.3/org/apache/batik/util/ sources-1.3/org/apache/batik/util/gui/ sources-1.4/org/a...

Added: xmlgraphics/batik/trunk/sources/org/apache/batik/apps/svgbrowser/NodePickerPanel.java
URL: http://svn.apache.org/viewvc/xmlgraphics/batik/trunk/sources/org/apache/batik/apps/svgbrowser/NodePickerPanel.java?rev=594367&view=auto
==============================================================================
--- xmlgraphics/batik/trunk/sources/org/apache/batik/apps/svgbrowser/NodePickerPanel.java (added)
+++ xmlgraphics/batik/trunk/sources/org/apache/batik/apps/svgbrowser/NodePickerPanel.java Mon Nov 12 16:40:53 2007
@@ -0,0 +1,1636 @@
+/*
+
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+ */
+package org.apache.batik.apps.svgbrowser;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Frame;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.EventListener;
+import java.util.EventObject;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.Vector;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+import javax.swing.SwingUtilities;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.event.EventListenerList;
+import javax.swing.event.TableModelEvent;
+import javax.swing.event.TableModelListener;
+import javax.swing.table.DefaultTableModel;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.batik.dom.util.DOMUtilities;
+import org.apache.batik.util.SVGConstants;
+import org.apache.batik.util.gui.JEnhEditTextArea;
+import org.apache.batik.util.gui.resource.ActionMap;
+import org.apache.batik.util.gui.resource.ButtonFactory;
+import org.apache.batik.util.gui.resource.MissingListenerException;
+import org.apache.batik.util.resources.ResourceManager;
+
+import org.gjt.sp.jedit.textarea.TextAreaDefaults;
+import org.gjt.sp.jedit.textarea.TextAreaPainter;
+import org.gjt.sp.jedit.textarea.XMLTokenMarker;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+/**
+ * Used to preview and edit nodes.
+ */
+public class NodePickerPanel extends JPanel implements ActionMap {
+
+
+     // The NodePickerPanel modes of work
+    /**
+     * View only. Inspects the associated node.
+     */
+    private static final int VIEW_MODE = 1;
+
+    /**
+     * Edit mode. Used for editing the associated node.
+     */
+    private static final int EDIT_MODE = 2;
+
+    /**
+     * Creates new element while in this mode.
+     */
+    private static final int ADD_NEW_ELEMENT = 3;
+
+    /**
+     * The resource file name.
+     */
+    private static final String RESOURCES =
+        "org.apache.batik.apps.svgbrowser.resources.NodePickerPanelMessages";
+
+    /**
+     * The resource bundle.
+     */
+    private static ResourceBundle bundle;
+
+    /**
+     * The resource manager.
+     */
+    private static ResourceManager resources;
+    static {
+        bundle = ResourceBundle.getBundle(RESOURCES, Locale.getDefault());
+        resources = new ResourceManager(bundle);
+    }
+
+    /**
+     * The attributes table - the table that consists of attribute name and
+     * attribute value columns. Shows the element's attributes.
+     */
+    private JTable attributesTable;
+
+    /**
+     * The Attribute table model listener.
+     */
+    private TableModelListener tableModelListener;
+
+    /**
+     * The Attributes table ScrollPane.
+     */
+    private JScrollPane attributePane;
+
+    /**
+     * The Attributes table and buttons Panel.
+     */
+    private JPanel attributesPanel;
+
+    /**
+     * The Button factory.
+     */
+    private ButtonFactory buttonFactory;
+
+    /**
+     * The Add button.
+     */
+    private JButton addButton;
+
+    /**
+     * The Remove button.
+     */
+    private JButton removeButton;
+
+    /**
+     * The Attributes table label.
+     */
+    private JLabel attributesLabel;
+
+    /**
+     * The Apply button.
+     */
+    private JButton applyButton;
+
+    /**
+     * The Reset button.
+     */
+    private JButton resetButton;
+
+    /**
+     * The OK and Cancel button Panel.
+     */
+    private JPanel choosePanel;
+
+    /**
+     * The svg input panel.
+     */
+    private SVGInputPanel svgInputPanel;
+
+    /**
+     * The isWellFormed label.
+     */
+    private JLabel isWellFormedLabel;
+
+    /**
+     * The svgInputPanel name label.
+     */
+    private JLabel svgInputPanelNameLabel;
+
+    /**
+     * If the attribute table listener should process the update event and
+     * update node picker after an update on the table had triggered. Used
+     * instead of removing and adding the table listener.
+     */
+    private boolean shouldProcessUpdate = true;
+
+    /**
+     * The element that is being previewed or edited it's content (xml
+     * representation).
+     */
+    private Element previewElement;
+
+    /**
+     * The copy of the original (preview) element. Used to synchronize svginput
+     * area and the attributes table, since the original elements attributes
+     * shouldn't be changed while previewing or editing it.
+     */
+    private Element clonedElement;
+
+    /**
+     * The parent Element for the element to be added. It is used when adding
+     * the new element, to get the information on where to be appended.
+     */
+    private Node parentElement;
+
+    /**
+     * The panel mode.
+     */
+    private int mode;
+
+    /**
+     * If the element being edited is actually changed.
+     */
+    private boolean isDirty;
+
+    /**
+     * Listeners list.
+     */
+    private EventListenerList eventListeners =
+        new EventListenerList();
+
+    /**
+     * The controller for this panel.
+     */
+    private NodePickerController controller;
+
+    /**
+     * The map that contains the listeners
+     */
+    private Map listeners = new HashMap(10);
+
+    /**
+     * Constructor.
+     *
+     * @param controller
+     *            The node picker panel controller
+     */
+    public NodePickerPanel(NodePickerController controller) {
+        super(new GridBagLayout());
+        this.controller = controller;
+        initialize();
+    }
+
+    /**
+     * Initalizes this panel.
+     */
+    private void initialize() {
+        // Associate buttons with the actions
+        addButtonActions();
+
+        // Add components
+        GridBagConstraints grid = new GridBagConstraints();
+        grid.gridx = 1;
+        grid.gridy = 1;
+        grid.anchor = GridBagConstraints.NORTHWEST;
+        grid.fill = GridBagConstraints.NONE;
+        grid.insets = new Insets(5, 5, 0, 5);
+        attributesLabel = new JLabel();
+        String attributesLabelValue = resources
+                .getString("AttributesTable.name");
+        attributesLabel.setText(attributesLabelValue);
+        this.add(attributesLabel, grid);
+
+        grid.gridx = 1;
+        grid.gridy = 2;
+        grid.gridwidth = 2;
+        grid.weightx = 1.0;
+        grid.weighty = 0.3;
+        grid.fill = GridBagConstraints.BOTH;
+        grid.anchor = GridBagConstraints.CENTER;
+        grid.insets = new Insets(0, 0, 0, 5);
+        this.add(getAttributesPanel(), grid);
+
+        grid.weightx = 0;
+        grid.weighty = 0;
+        grid.gridwidth = 1;
+        grid.gridx = 1;
+        grid.gridy = 3;
+        grid.anchor = GridBagConstraints.NORTHWEST;
+        grid.fill = GridBagConstraints.NONE;
+        grid.insets = new Insets(0, 5, 0, 5);
+        svgInputPanelNameLabel = new JLabel();
+        String svgInputLabelValue = resources.getString("InputPanelLabel.name");
+        svgInputPanelNameLabel.setText(svgInputLabelValue);
+        this.add(svgInputPanelNameLabel, grid);
+
+        grid.gridx = 1;
+        grid.gridy = 4;
+        grid.gridwidth = 2;
+        grid.weightx = 1.0;
+        grid.weighty = 1.0;
+        grid.fill = GridBagConstraints.BOTH;
+        grid.anchor = GridBagConstraints.CENTER;
+        grid.insets = new Insets(0, 5, 0, 10);
+        this.add(getSvgInputPanel(), grid);
+
+        grid.weightx = 0;
+        grid.weighty = 0;
+        grid.gridwidth = 1;
+        grid.gridx = 1;
+        grid.gridy = 5;
+        grid.anchor = GridBagConstraints.NORTHWEST;
+        grid.fill = GridBagConstraints.NONE;
+        grid.insets = new Insets(5, 5, 0, 5);
+        isWellFormedLabel = new JLabel();
+        String isWellFormedLabelVal =
+            resources.getString("IsWellFormedLabel.wellFormed");
+        isWellFormedLabel.setText(isWellFormedLabelVal);
+        this.add(isWellFormedLabel, grid);
+
+        grid.weightx = 0;
+        grid.weighty = 0;
+        grid.gridwidth = 1;
+        grid.gridx = 2;
+        grid.gridy = 5;
+        grid.anchor = GridBagConstraints.EAST;
+        grid.insets = new Insets(0, 0, 0, 5);
+        this.add(getChoosePanel(), grid);
+
+        // Set the default mode
+        enterViewMode();
+    }
+
+    /**
+     * Gets buttonFactory.
+     */
+    private ButtonFactory getButtonFactory() {
+        if (buttonFactory == null) {
+            buttonFactory = new ButtonFactory(bundle, this);
+        }
+        return buttonFactory;
+    }
+
+    /**
+     * Adds button actions.
+     */
+    private void addButtonActions() {
+        listeners.put("ApplyButtonAction", new ApplyButtonAction());
+        listeners.put("ResetButtonAction", new ResetButtonAction());
+        listeners.put("AddButtonAction", new AddButtonAction());
+        listeners.put("RemoveButtonAction", new RemoveButtonAction());
+    }
+
+    /**
+     * Gets the Add button.
+     */
+    private JButton getAddButton() {
+        if (addButton == null) {
+            addButton = getButtonFactory().createJButton("AddButton");
+            addButton.addFocusListener(new NodePickerEditListener());
+        }
+        return addButton;
+    }
+
+    /**
+     * Gets the Remove button.
+     */
+    private JButton getRemoveButton() {
+        if (removeButton == null) {
+            removeButton = getButtonFactory().createJButton("RemoveButton");
+            removeButton.addFocusListener(new NodePickerEditListener());
+        }
+        return removeButton;
+    }
+
+    /**
+     * Gets the Apply button.
+     */
+    private JButton getApplyButton() {
+        if (applyButton == null) {
+            applyButton = getButtonFactory().createJButton("ApplyButton");
+        }
+        return applyButton;
+    }
+
+    /**
+     * Gets the Reset sbutton.
+     */
+    private JButton getResetButton() {
+        if (resetButton == null) {
+            resetButton = getButtonFactory().createJButton("ResetButton");
+        }
+        return resetButton;
+    }
+
+    /**
+     * Gets the attributesPanel.
+     */
+    private JPanel getAttributesPanel() {
+        if (attributesPanel == null) {
+            attributesPanel = new JPanel(new GridBagLayout());
+
+            GridBagConstraints g11 = new GridBagConstraints();
+            g11.gridx = 1;
+            g11.gridy = 1;
+            g11.fill = GridBagConstraints.BOTH;
+            g11.anchor = GridBagConstraints.CENTER;
+            g11.weightx = 4.0;
+            g11.weighty = 1.0;
+            g11.gridheight = 5;
+            g11.gridwidth = 2;
+            g11.insets = new Insets(5, 5, 5, 0);
+
+            GridBagConstraints g12 = new GridBagConstraints();
+            g12.gridx = 3;
+            g12.gridy = 1;
+            g12.fill = GridBagConstraints.HORIZONTAL;
+            g12.anchor = GridBagConstraints.NORTH;
+            g12.insets = new Insets(5, 20, 0, 5);
+            g12.weightx = 1.0;
+
+            GridBagConstraints g32 = new GridBagConstraints();
+            g32.gridx = 3;
+            g32.gridy = 3;
+            g32.fill = GridBagConstraints.HORIZONTAL;
+            g32.anchor = GridBagConstraints.NORTH;
+            g32.insets = new Insets(5, 20, 0, 5);
+            g32.weightx = 1.0;
+
+            attributesTable = new JTable();
+            attributesTable.setModel(new AttributesTableModel(10, 2));
+            tableModelListener = new AttributesTableModelListener();
+            attributesTable.getModel()
+                    .addTableModelListener(tableModelListener);
+            attributesTable.addFocusListener(new NodePickerEditListener());
+            attributePane = new JScrollPane();
+            attributePane.getViewport().add(attributesTable);
+
+            attributesPanel.add(attributePane, g11);
+            attributesPanel.add(getAddButton(), g12);
+            attributesPanel.add(getRemoveButton(), g32);
+        }
+        return attributesPanel;
+    }
+
+    /**
+     * Gets the svgInputPanel.
+     */
+    private SVGInputPanel getSvgInputPanel() {
+        if (svgInputPanel == null) {
+            svgInputPanel = new SVGInputPanel();
+            svgInputPanel.getNodeXmlArea().getDocument().addDocumentListener
+                (new XMLAreaListener());
+            svgInputPanel.getNodeXmlArea().addFocusListener
+                (new NodePickerEditListener());
+        }
+        return svgInputPanel;
+    }
+
+    /**
+     * Gets the choosePanel.
+     */
+    private JPanel getChoosePanel() {
+        if (choosePanel == null) {
+            choosePanel = new JPanel(new GridBagLayout());
+
+            GridBagConstraints g11 = new GridBagConstraints();
+            g11.gridx = 1;
+            g11.gridy = 1;
+            g11.weightx = 0.5;
+            g11.anchor = GridBagConstraints.WEST;
+            g11.fill = GridBagConstraints.HORIZONTAL;
+            g11.insets = new Insets(5, 5, 5, 5);
+
+            GridBagConstraints g12 = new GridBagConstraints();
+            g12.gridx = 2;
+            g12.gridy = 1;
+            g12.weightx = 0.5;
+            g12.anchor = GridBagConstraints.EAST;
+            g12.fill = GridBagConstraints.HORIZONTAL;
+            g12.insets = new Insets(5, 5, 5, 5);
+
+            choosePanel.add(getApplyButton(), g11);
+            choosePanel.add(getResetButton(), g12);
+        }
+        return choosePanel;
+    }
+
+    /**
+     * Gets the results of this node picker panel - gets the contents of the xml
+     * text area.
+     */
+    public String getResults() {
+        return getSvgInputPanel().getNodeXmlArea().getText();
+    }
+
+    /**
+     * Update the components and the element after text is being inputted in the
+     * xml text area.
+     *
+     * @param referentElement
+     *            The updated element, referent element
+     * @param elementToUpdate
+     *            The element to update.
+     */
+    private void updateViewAfterSvgInput(Element referentElement,
+            Element elementToUpdate) {
+        if (referentElement != null) {
+            String isWellFormedLabelVal =
+                resources.getString("IsWellFormedLabel.wellFormed");
+            isWellFormedLabel.setText(isWellFormedLabelVal);
+            getApplyButton().setEnabled(true);
+            attributesTable.setEnabled(true);
+            updateElementAttributes(elementToUpdate, referentElement);
+            shouldProcessUpdate = false;
+            updateAttributesTable(elementToUpdate);
+            shouldProcessUpdate = true;
+        } else {
+            String isWellFormedLabelVal =
+                resources.getString("IsWellFormedLabel.notWellFormed");
+            isWellFormedLabel.setText(isWellFormedLabelVal);
+            getApplyButton().setEnabled(false);
+            attributesTable.setEnabled(false);
+        }
+    }
+
+    /**
+     * Replaces all of the attributes of the given element with the referent
+     * element's attributes.
+     *
+     * @param elem
+     *            The element whose attributes should be replaced
+     * @param referentElement
+     *            The referentElement to copy the attributes from
+     */
+    private void updateElementAttributes(Element elem, Element referentElement) {
+        // Remove all element attributes
+        removeAttributes(elem);
+
+        // Copy all attributes from the referent element to the given element
+        NamedNodeMap newNodeMap = referentElement.getAttributes();
+        for (int i = newNodeMap.getLength() - 1; i >= 0; i--) {
+            Node newAttr = newNodeMap.item(i);
+            String qualifiedName = newAttr.getNodeName();
+            String attributeValue = newAttr.getNodeValue();
+            String prefix = DOMUtilities.getPrefix(qualifiedName);
+            String namespaceURI = getNamespaceURI(prefix);
+            elem.setAttributeNS(namespaceURI, qualifiedName, attributeValue);
+        }
+    }
+
+    /**
+     * Replaces all of the atributes of a given element with the values from the
+     * given table model.
+     *
+     * @param element
+     *            The node whose attributes should update
+     * @param tableModel
+     *            The tableModel from which to get attributes
+     */
+    private void updateElementAttributes
+            (Element element, AttributesTableModel tableModel) {
+
+        // Remove all element attributes
+        removeAttributes(element);
+
+        // Copy all the attribute name - value pairs from the table model to
+        // the given element
+        for (int i = 0; i < tableModel.getRowCount(); i++) {
+            String newAttrName = (String) tableModel.getAttrNameAt(i);
+            String newAttrValue = (String) tableModel.getAttrValueAt(i);
+            if (newAttrName != null && newAttrName.length() > 0) {
+                String prefix = DOMUtilities.getPrefix(newAttrName);
+                String namespaceURI = getNamespaceURI(prefix);
+                if (newAttrValue != null) {
+                    element.setAttributeNS
+                        (namespaceURI, newAttrName, newAttrValue);
+                } else {
+                    element.setAttributeNS(namespaceURI, newAttrName, "");
+                }
+            }
+        }
+    }
+
+    /**
+     * Removes all the attributes from an element.
+     *
+     * @param element
+     *            The given element
+     */
+    private void removeAttributes(Element element) {
+        NamedNodeMap oldNodeMap = element.getAttributes();
+        int n = oldNodeMap.getLength();
+        for (int i = n - 1; i >= 0; i--) {
+            element.removeAttributeNode((Attr) oldNodeMap.item(i));
+        }
+    }
+
+    /**
+     * Looks up for the namespaceURI based on the given prefix. Uses the
+     * Node.lookupNamespaceURI method, starting from the parent element of
+     * the element being edited / created.
+     *
+     * @param prefix
+     *            The given prefix
+     * @return namespaceURI or null
+     */
+    private String getNamespaceURI(String prefix) {
+        String namespaceURI = null;
+        if (prefix != null) {
+            if (prefix.equals(SVGConstants.XMLNS_PREFIX)) {
+                namespaceURI = SVGConstants.XMLNS_NAMESPACE_URI;
+            } else {
+                if (mode == EDIT_MODE) {
+                    namespaceURI = previewElement.lookupNamespaceURI(prefix);
+                } else if (mode == ADD_NEW_ELEMENT) {
+                    namespaceURI = parentElement.lookupNamespaceURI(prefix);
+                }
+
+            }
+        }
+        return namespaceURI;
+    }
+
+    /**
+     * Fills the attributesTable with the given element attribute name - value
+     * pairs.
+     *
+     * @param elem
+     *            The given element
+     */
+    private void updateAttributesTable(Element elem) {
+        NamedNodeMap map = elem.getAttributes();
+        AttributesTableModel tableModel =
+            (AttributesTableModel) attributesTable.getModel();
+        // Remove and update rows from the table if needed...
+        for (int i = tableModel.getRowCount() - 1; i >= 0; i--) {
+            String attrName = (String) tableModel.getValueAt(i, 0);
+            String newAttrValue = "";
+            if (attrName != null) {
+                newAttrValue = elem.getAttributeNS(null, attrName);
+            }
+            if (attrName == null || newAttrValue.length() == 0) {
+                tableModel.removeRow(i);
+            }
+            if (newAttrValue.length() > 0) {
+                tableModel.setValueAt(newAttrValue, i, 1);
+            }
+        }
+
+        // Add rows
+        for (int i = 0; i < map.getLength(); i++) {
+            Node attr = map.item(i);
+            String attrName = attr.getNodeName();
+            String attrValue = attr.getNodeValue();
+            if (tableModel.getValueForName(attrName) == null) {
+                Vector rowData = new Vector();
+                rowData.add(attrName);
+                rowData.add(attrValue);
+                tableModel.addRow(rowData);
+            }
+        }
+    }
+
+    /**
+     * Shows node's String representation in svgInputPanel
+     *
+     * @param node
+     *            The given node
+     */
+    private void updateNodeXmlArea(Node node) {
+        getSvgInputPanel().getNodeXmlArea().setText(DOMUtilities.getXML(node));
+    }
+
+    /**
+     * Getter for the preivewElement.
+     *
+     * @return the preivewElement
+     */
+    private Element getPreviewElement() {
+        return previewElement;
+    }
+
+    /**
+     * Sets the preview element. Enters the view mode and updates the associated
+     * components.
+     *
+     * @param elem
+     *            the element to set
+     */
+    public void setPreviewElement(Element elem) {
+        this.previewElement = elem;
+        enterViewMode();
+
+        updateNodeXmlArea(elem);
+        updateAttributesTable(elem);
+    }
+
+    /**
+     * Gets the current working mode.
+     *
+     * @return the mode
+     */
+    private int getMode() {
+        return mode;
+    }
+
+    /**
+     * Enters the view mode.
+     */
+    public void enterViewMode() {
+        if (mode != VIEW_MODE) {
+            mode = VIEW_MODE;
+            // Disable appropriate buttons
+            getApplyButton().setEnabled(false);
+            getResetButton().setEnabled(false);
+            // Enable the remove and add buttons
+            getRemoveButton().setEnabled(true);
+            getAddButton().setEnabled(true);
+            // Update the isWellFormed label
+            String isWellFormedLabelVal =
+                resources.getString("IsWellFormedLabel.wellFormed");
+            isWellFormedLabel.setText(isWellFormedLabelVal);
+        }
+    }
+
+    /**
+     * Enters the edit mode.
+     */
+    public void enterEditMode() {
+        if (mode != EDIT_MODE) {
+            mode = EDIT_MODE;
+            clonedElement = (Element) previewElement.cloneNode(true);
+
+            // Enable appropriate buttons
+            getApplyButton().setEnabled(true);
+            getResetButton().setEnabled(true);
+        }
+    }
+
+    /**
+     * Enters the add new element mode.
+     *
+     * @param newElement
+     *            The element to be added
+     * @param parent
+     *            The parent node of the element to be added
+     */
+    public void enterAddNewElementMode(Element newElement, Node parent) {
+        if (mode != ADD_NEW_ELEMENT) {
+            mode = ADD_NEW_ELEMENT;
+            previewElement = newElement;
+            clonedElement = (Element) newElement.cloneNode(true);
+            parentElement = parent;
+            // Update the appropriate areas
+            updateNodeXmlArea(newElement);
+            // Enable appropriate buttons
+            getApplyButton().setEnabled(true);
+            getResetButton().setEnabled(true);
+//          // Request focus
+//          getSvgInputPanel().getNodeXmlArea().requestFocusInWindow();
+        }
+    }
+
+    /**
+     * Updates the panel when DOM Mutation event occures.
+     */
+    public void updateOnDocumentChange(String mutationEventType, Node targetNode) {
+        if (mode == VIEW_MODE) {
+            if (this.isShowing() &&
+                    shouldUpdate(mutationEventType,
+                                 targetNode,
+                                 getPreviewElement())) {
+                setPreviewElement(getPreviewElement());
+            }
+        }
+    }
+
+    /**
+     * If the panel should update its components after dom mutation event.
+     * Checks whether any node that is the child node of the node currently
+     * being previewed has changed. If true, updates the xml text area of this
+     * NodePicker. In case of DOMAttrModiefied mutation event, the additional
+     * condition is added - to check whether the attributes of an element that
+     * is being previewed are changed. If true, the xml text area is refreshed.
+     *
+     * @return True if should update
+     */
+    private boolean shouldUpdate(String mutationEventType, Node affectedNode,
+            Node currentNode) {
+        if (mutationEventType.equals("DOMNodeInserted")) {
+            if (DOMUtilities.isAncestorOf(currentNode, affectedNode)) {
+                return true;
+            }
+        } else if (mutationEventType.equals("DOMNodeRemoved")) {
+            if (DOMUtilities.isAncestorOf(currentNode, affectedNode)) {
+                return true;
+            }
+        } else if (mutationEventType.equals("DOMAttrModified")) {
+            if (DOMUtilities.isAncestorOf(currentNode, affectedNode)
+                    || currentNode == affectedNode) {
+                return true;
+            }
+        } else if (mutationEventType.equals("DOMCharDataModified")) {
+            if (DOMUtilities.isAncestorOf(currentNode, affectedNode)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Parses the given xml and return parsed document's root element.
+     * Used to check whether the given xml is well formed.
+     *
+     * @param xmlString
+     *            Xml as a String
+     * @return Element
+     */
+    private Element parseXml(String xmlString) {
+        Document doc = null;
+        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        try {
+            javax.xml.parsers.DocumentBuilder parser = factory
+                    .newDocumentBuilder();
+            parser.setErrorHandler(new ErrorHandler() {
+                public void error(SAXParseException exception)
+                        throws SAXException {
+                }
+
+                public void fatalError(SAXParseException exception)
+                        throws SAXException {
+                }
+
+                public void warning(SAXParseException exception)
+                        throws SAXException {
+                }
+            });
+            doc = parser.parse(new InputSource(new StringReader(xmlString)));
+        } catch (ParserConfigurationException e1) {
+        } catch (SAXException e1) {
+        } catch (IOException e1) {
+        }
+        if (doc != null) {
+            return doc.getDocumentElement();
+        }
+        return null;
+    }
+
+    /**
+     * Sets the node picker components to be editable / uneditable.
+     *
+     * @param editable
+     *            Whether to enable or disable edit
+     */
+    public void setEditable(boolean editable) {
+        getSvgInputPanel().getNodeXmlArea().setEditable(editable);
+        getResetButton().setEnabled(editable);
+        getApplyButton().setEnabled(editable);
+        getAddButton().setEnabled(editable);
+        getRemoveButton().setEnabled(editable);
+        attributesTable.setEnabled(editable);
+    }
+
+    /**
+     * Checks whether the given component is a part component of the this node
+     * picker.
+     *
+     * @param component
+     *            The given component
+     * @return True if the given component is a part of the this NodePicker
+     */
+    private boolean isANodePickerComponent(Component component) {
+        return SwingUtilities.getAncestorOfClass(NodePickerPanel.class,
+                                                 component) != null;
+    }
+
+    /**
+     * Shows a dialog to save changes.
+     */
+    public void promptForChanges() {
+        isDirty = false;
+        // If the xml is well formed
+        if (getApplyButton().isEnabled() && isElementModified()) {
+            String confirmString = resources.getString("ConfirmDialog.message");
+            int option = JOptionPane.showConfirmDialog(getSvgInputPanel(),
+                    confirmString);
+            if (option == JOptionPane.YES_OPTION) {
+                getApplyButton().doClick();
+            } else {
+                getResetButton().doClick();
+            }
+        } else {
+            getResetButton().doClick();
+        }
+    }
+
+    /**
+     * Whether the element being edit is changed.
+     *
+     * @return True if the element being edit is changed
+     */
+    private boolean isElementModified() {
+        if (getMode() == EDIT_MODE) {
+            return !DOMUtilities.getXML(previewElement).equals
+                (getSvgInputPanel().getNodeXmlArea().getText());
+        } else if (getMode() == ADD_NEW_ELEMENT) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Manages the edits on focus events.
+     */
+    protected class NodePickerEditListener extends FocusAdapter {
+
+        public void focusGained(FocusEvent e) {
+            if (getMode() == VIEW_MODE) {
+                enterEditMode();
+            }
+            setEditable(controller.isEditable()
+                    && controller.canEdit(previewElement));
+            isDirty = isElementModified();
+        }
+
+        public void focusLost(FocusEvent e) {
+            // Prompts the user to save changes that he made for an element,
+            // when the NodePicker looses focus
+            if (!isANodePickerComponent(e.getOppositeComponent())
+                    && !e.isTemporary() && isDirty) {
+                promptForChanges();
+            }
+        }
+    }
+
+    /**
+     * Listens for the changes in the xml text area and updates this node picker
+     * panel if needed.
+     */
+    protected class XMLAreaListener implements DocumentListener {
+        public void changedUpdate(DocumentEvent e) {
+            isDirty = isElementModified();
+        }
+
+        public void insertUpdate(DocumentEvent e) {
+            updateNodePicker(e);
+            isDirty = isElementModified();
+        }
+
+        public void removeUpdate(DocumentEvent e) {
+            updateNodePicker(e);
+            isDirty = isElementModified();
+        }
+
+        /**
+         * Updates the node picker panel after document changes.
+         *
+         * @param e
+         *            The document event
+         */
+        private void updateNodePicker(DocumentEvent e) {
+            if (getMode() == EDIT_MODE) {
+                updateViewAfterSvgInput
+                    (parseXml(svgInputPanel.getNodeXmlArea().getText()),
+                     clonedElement);
+            } else if (getMode() == ADD_NEW_ELEMENT) {
+                updateViewAfterSvgInput
+                    (parseXml(svgInputPanel.getNodeXmlArea().getText()),
+                     previewElement);
+            }
+        }
+    }
+
+    /**
+     * Listens for the changes in the table and updates this node picker panel
+     * if needed.
+     */
+    protected class AttributesTableModelListener implements TableModelListener {
+        public void tableChanged(TableModelEvent e) {
+            if (e.getType() == TableModelEvent.UPDATE && shouldProcessUpdate) {
+                updateNodePicker(e);
+            }
+        }
+
+        /**
+         * Updates the node picker panel after document changes.
+         *
+         * @param e
+         *            The document event
+         */
+        private void updateNodePicker(TableModelEvent e) {
+            if (getMode() == EDIT_MODE) {
+                updateElementAttributes
+                    (clonedElement, (AttributesTableModel) (e.getSource()));
+                updateNodeXmlArea(clonedElement);
+            } else if (getMode() == ADD_NEW_ELEMENT) {
+                updateElementAttributes
+                    (previewElement, (AttributesTableModel) (e.getSource()));
+                updateNodeXmlArea(previewElement);
+            }
+        }
+    }
+
+    /**
+     * The action associated with the 'Apply' button.
+     */
+    protected class ApplyButtonAction extends AbstractAction {
+        public void actionPerformed(ActionEvent e) {
+            isDirty = false;
+            String xmlAreaText = getResults();
+            if (getMode() == EDIT_MODE) {
+                fireUpdateElement
+                    (new NodePickerEvent
+                        (NodePickerPanel.this,
+                         xmlAreaText,
+                         previewElement,
+                         NodePickerEvent.EDIT_ELEMENT));
+            } else if (getMode() == ADD_NEW_ELEMENT) {
+                fireAddNewElement
+                    (new NodePickerEvent
+                        (NodePickerPanel.this,
+                         xmlAreaText,
+                         parentElement,
+                         NodePickerEvent.ADD_NEW_ELEMENT));
+            }
+            enterViewMode();
+        }
+    }
+
+    /**
+     * The action associated with the 'Reset' button.
+     */
+    protected class ResetButtonAction extends AbstractAction {
+        public void actionPerformed(ActionEvent e) {
+            isDirty = false;
+            setPreviewElement(getPreviewElement());
+        }
+    }
+
+    /**
+     * The action associated with the 'Add' button.
+     */
+    protected class AddButtonAction extends AbstractAction {
+        public void actionPerformed(ActionEvent e) {
+            if (getMode() == VIEW_MODE) {
+                enterEditMode();
+            }
+            DefaultTableModel model =
+                (DefaultTableModel) attributesTable.getModel();
+            shouldProcessUpdate = false;
+            model.addRow((Vector) null);
+            shouldProcessUpdate = true;
+        }
+    }
+
+    /**
+     * The action associated with the 'Remove' button.
+     */
+    protected class RemoveButtonAction extends AbstractAction {
+        public void actionPerformed(ActionEvent e) {
+            if (getMode() == VIEW_MODE) {
+                enterEditMode();
+            }
+            // Find the contextElement
+            Element contextElement = clonedElement;
+            if (getMode() == ADD_NEW_ELEMENT) {
+                contextElement = previewElement;
+            }
+            DefaultTableModel model =
+                (DefaultTableModel) attributesTable.getModel();
+            int[] selectedRows = attributesTable.getSelectedRows();
+            for (int i = 0; i < selectedRows.length; i++) {
+                String attrName = (String) model.getValueAt(selectedRows[i], 0);
+                if (attrName != null) {
+                    String prefix = DOMUtilities.getPrefix(attrName);
+                    String localName = DOMUtilities.getLocalName(attrName);
+                    String namespaceURI = getNamespaceURI(prefix);
+                    contextElement.removeAttributeNS(namespaceURI, localName);
+                }
+            }
+            shouldProcessUpdate = false;
+            updateAttributesTable(contextElement);
+            shouldProcessUpdate = true;
+            updateNodeXmlArea(contextElement);
+        }
+    }
+
+    /**
+     * Returns the action associated with the given string or null on error
+     *
+     * @param key
+     *            the key mapped with the action to get
+     * @throws MissingListenerException
+     *             if the action is not found
+     */
+    public Action getAction(String key) throws MissingListenerException {
+        return (Action) listeners.get(key);
+    }
+
+    /**
+     * The attributesTable model.
+     */
+    public static class AttributesTableModel extends DefaultTableModel {
+        public AttributesTableModel(int rowCount, int columnCount) {
+            super(rowCount, columnCount);
+        }
+
+        public String getColumnName(int column) {
+            if (column == 0) {
+                return resources.getString("AttributesTable.column1");
+            } else {
+                return resources.getString("AttributesTable.column2");
+            }
+        }
+
+        /**
+         * Gets the value of the attribute with the given attribute name.
+         *
+         * @param attrName
+         *            The given attribute name
+         */
+        public Object getValueForName(Object attrName) {
+            for (int i = 0; i < getRowCount(); i++) {
+                if (getValueAt(i, 0) != null
+                        && getValueAt(i, 0).equals(attrName)) {
+                    return getValueAt(i, 1);
+                }
+            }
+            return null;
+        }
+
+        /**
+         * Gets the name of the attribute with the table row.
+         */
+        public Object getAttrNameAt(int i) {
+            return getValueAt(i, 0);
+        }
+
+        /**
+         * Gets the value of the attribute with the table row.
+         */
+        public Object getAttrValueAt(int i) {
+            return getValueAt(i, 1);
+        }
+
+        /**
+         * Gets the first row where the given attribute name appears.
+         * @param attrName    The given attribute name
+         */
+        public int getRow(Object attrName) {
+            for (int i = 0; i < getRowCount(); i++) {
+                if (getValueAt(i, 0) != null
+                        && getValueAt(i, 0).equals(attrName)) {
+                    return i;
+                }
+            }
+            return -1;
+        }
+    }
+
+    // Custom events support
+    /**
+     * Fires the updateElement event.
+     *
+     * @param event
+     *            The associated NodePickerEvent event
+     */
+    public void fireUpdateElement(NodePickerEvent event) {
+        Object[] listeners = eventListeners.getListenerList();
+
+        int length = listeners.length;
+        for (int i = 0; i < length; i += 2) {
+            if (listeners[i] == NodePickerListener.class) {
+                ((NodePickerListener) listeners[i + 1])
+                        .updateElement(event);
+            }
+        }
+    }
+
+    /**
+     * Fires the AddNewElement event.
+     *
+     * @param event
+     *            The associated NodePickerEvent event
+     */
+    public void fireAddNewElement(NodePickerEvent event) {
+        Object[] listeners = eventListeners.getListenerList();
+        int length = listeners.length;
+        for (int i = 0; i < length; i += 2) {
+            if (listeners[i] == NodePickerListener.class) {
+                ((NodePickerListener) listeners[i + 1])
+                        .addNewElement(event);
+            }
+        }
+    }
+
+    /**
+     * Adds the listener to the listener list.
+     *
+     * @param l
+     *            The listener to add
+     */
+    public void addListener(NodePickerListener listener) {
+        eventListeners.add(NodePickerListener.class, listener);
+    }
+
+    /**
+     * Event to pass to listener.
+     */
+    public static class NodePickerEvent extends EventObject {
+
+        // The event types
+        public static final int EDIT_ELEMENT = 1;
+
+        public static final int ADD_NEW_ELEMENT = 2;
+
+        /**
+         * The type of this event.
+         */
+        private int type;
+
+        /**
+         * The string that is to be parsed.
+         */
+        private String result;
+
+        /**
+         * The context node associated with this event.
+         */
+        private Node contextNode;
+
+        /**
+         * Creates the NodePickerEvent.
+         *
+         * @param source
+         *            The NodePicker that initiated the event
+         * @param result
+         *            the NodePicker result
+         * @param contextNode
+         *            the associated context node
+         */
+        public NodePickerEvent(Object source, String result, Node contextNode,
+                               int type) {
+            super(source);
+            this.result = result;
+            this.contextNode = contextNode;
+        }
+
+        /**
+         * Gets the NodePickerPanel result.
+         *
+         * @return the result
+         */
+        public String getResult() {
+            return result;
+        }
+
+        /**
+         * Gets the context node.
+         * 'EDIT_ELEMENT' event type - the context node is the original element
+         * being previewed.
+         * 'ADD_NEW_ELEMENT' event type - the context node is the parent node of
+         * the element being added
+         *
+         * @return the context node
+         */
+        public Node getContextNode() {
+            return contextNode;
+        }
+
+        /**
+         * Gets the type of this event.
+         *
+         * @return the type
+         */
+        public int getType() {
+            return type;
+        }
+    }
+
+    /**
+     * Node picker listener.
+     */
+    public static interface NodePickerListener extends EventListener {
+        /**
+         * Updates the element from the data contained in the NodePickerEvent.
+         */
+        void updateElement(NodePickerEvent event);
+
+        /**
+         * Adds the element from the data contained in the NodePickerEvent.
+         */
+        void addNewElement(NodePickerEvent event);
+    }
+
+    /**
+     * The adapter for the NodePicker listener.
+     */
+    public static class NodePickerAdapter implements NodePickerListener {
+
+        public void addNewElement(NodePickerEvent event) {
+        }
+
+        public void updateElement(NodePickerEvent event) {
+        }
+    }
+
+    /**
+     * The panel to view and edit the elements xml representation.
+     */
+    protected class SVGInputPanel extends JPanel {
+
+        /**
+         * The text area.
+         */
+        protected JEnhEditTextArea nodeXmlArea;
+
+        /**
+         * Constructor.
+         */
+        public SVGInputPanel() {
+            super(new BorderLayout());
+            add(getNodeXmlArea());
+            // Workaround ont JEditTextArea's overridden getMinimumSize()
+            setMinimumSize(new Dimension(100, 80));
+        }
+
+        /**
+         * Gets the nodeXmlArea.
+         *
+         * @return    the nodeXmlArea
+         */
+        protected JEnhEditTextArea getNodeXmlArea() {
+            if (nodeXmlArea == null) {
+                // Create syntax-highlighted text area
+                nodeXmlArea = new JEnhEditTextArea(getTextAreaDefaults());
+                nodeXmlArea.setTokenMarker(new XMLTokenMarker());
+                TextAreaPainter painter = nodeXmlArea.getPainter();
+                painter.setFont(getDefaultTextAreaFont());
+                nodeXmlArea.setEditable(true);
+            }
+            return nodeXmlArea;
+        }
+
+        /**
+         * The defaults for the JEditTextArea.
+         *
+         * @return TextAreaDefaults
+         */
+        protected TextAreaDefaults getTextAreaDefaults() {
+            TextAreaDefaults defaults = TextAreaDefaults.getDefaults();
+            defaults.cols = 30;
+            defaults.rows = 10;
+            defaults.electricScroll = 2;
+            return defaults;
+        }
+
+        /**
+         * The default Font for the JEditTextArea.
+         *
+         * @return Font
+         */
+        protected Font getDefaultTextAreaFont() {
+            return new Font("Monospaced", Font.PLAIN, 12);
+        }
+    }
+
+    /**
+     * Dialog for choosing element name.
+     */
+    public static class NameEditorDialog extends JDialog implements ActionMap {
+
+        /**
+         * The return value if 'OK' is chosen.
+         */
+        public static final int OK_OPTION = 0;
+
+        /**
+         * The return value if 'Cancel' is chosen.
+         */
+        public static final int CANCEL_OPTION = 1;
+
+        /**
+         * The resource file name.
+         */
+        protected static final String RESOURCES =
+            "org.apache.batik.apps.svgbrowser.resources.NameEditorDialogMessages";
+
+        /**
+         * The resource bundle.
+         */
+        protected static ResourceBundle bundle;
+
+        /**
+         * The resource manager.
+         */
+        protected static ResourceManager resources;
+        static {
+            bundle = ResourceBundle.getBundle(RESOURCES, Locale.getDefault());
+            resources = new ResourceManager(bundle);
+        }
+
+        /**
+         * The Dialog results.
+         */
+        protected int returnCode;
+
+        /**
+         * The Dialog main panel.
+         */
+        protected JPanel mainPanel;
+
+        /**
+         * The Button factory.
+         */
+        protected ButtonFactory buttonFactory;
+
+        /**
+         * The node name label.
+         */
+        protected JLabel nodeNameLabel;
+
+        /**
+         * The node name field.
+         */
+        protected JTextField nodeNameField;
+
+        /**
+         * The OK button.
+         */
+        protected JButton okButton;
+
+        /**
+         * The Cancel button.
+         */
+        protected JButton cancelButton;
+
+        /**
+         * The map that contains the listeners
+         */
+        protected Map listeners = new HashMap(10);
+
+        /**
+         * Constructor.
+         *
+         * @param frame
+         *            Parent frame
+         */
+        public NameEditorDialog(Frame frame) {
+            super(frame, true);
+            this.setResizable(false);
+            this.setModal(true);
+            initialize();
+        }
+
+        /**
+         * Initializes the dialog.
+         */
+        protected void initialize() {
+            this.setSize(resources.getInteger("Dialog.width"),
+                         resources.getInteger("Dialog.height"));
+            this.setTitle(resources.getString("Dialog.title"));
+            addButtonActions();
+            this.setContentPane(getMainPanel());
+        }
+
+        /**
+         * Gets buttonFactory.
+         */
+        protected ButtonFactory getButtonFactory() {
+            if (buttonFactory == null) {
+                buttonFactory = new ButtonFactory(bundle, this);
+            }
+            return buttonFactory;
+        }
+
+        /**
+         * Adds button actions.
+         */
+        protected void addButtonActions() {
+            listeners.put("OKButtonAction", new OKButtonAction());
+            listeners.put("CancelButtonAction", new CancelButtonAction());
+        }
+
+        /**
+         * Shows the dialog.
+         *
+         * @return OK_OPTION or CANCEL_OPTION.
+         */
+        public int showDialog() {
+            setVisible(true);
+            return returnCode;
+        }
+
+        /**
+         * Gets the Ok button.
+         *
+         * @return the okButton
+         */
+        protected JButton getOkButton() {
+            if (okButton == null) {
+                okButton = getButtonFactory().createJButton("OKButton");
+                this.getRootPane().setDefaultButton(okButton);
+            }
+            return okButton;
+        }
+
+        /**
+         * Gets the Cancel button.
+         *
+         * @return the cancelButton
+         */
+        protected JButton getCancelButton() {
+            if (cancelButton == null) {
+                cancelButton = getButtonFactory().createJButton("CancelButton");
+            }
+            return cancelButton;
+        }
+
+        /**
+         * Gets dialog's main panel.
+         *
+         * @return the mainPanel
+         */
+        protected JPanel getMainPanel() {
+            if (mainPanel == null) {
+                mainPanel = new JPanel(new GridBagLayout());
+
+                GridBagConstraints gridBag = new GridBagConstraints();
+                gridBag.gridx = 1;
+                gridBag.gridy = 1;
+                gridBag.fill = GridBagConstraints.NONE;
+                gridBag.insets = new Insets(5, 5, 5, 5);
+                mainPanel.add(getNodeNameLabel(), gridBag);
+
+                gridBag.gridx = 2;
+                gridBag.weightx = 1.0;
+                gridBag.weighty = 1.0;
+                gridBag.fill = GridBagConstraints.HORIZONTAL;
+                gridBag.anchor = GridBagConstraints.CENTER;
+                mainPanel.add(getNodeNameField(), gridBag);
+
+                gridBag.gridx = 1;
+                gridBag.gridy = 2;
+                gridBag.weightx = 0;
+                gridBag.weighty = 0;
+                gridBag.anchor = GridBagConstraints.EAST;
+                gridBag.fill = GridBagConstraints.HORIZONTAL;
+                mainPanel.add(getOkButton(), gridBag);
+
+                gridBag.gridx = 2;
+                gridBag.gridy = 2;
+                gridBag.anchor = GridBagConstraints.EAST;
+                mainPanel.add(getCancelButton(), gridBag);
+            }
+            return mainPanel;
+        }
+
+        /**
+         * Gets the node name label.
+         *
+         * @return the nodeNameLabel
+         */
+        public JLabel getNodeNameLabel() {
+            if (nodeNameLabel == null) {
+                nodeNameLabel = new JLabel();
+                nodeNameLabel.setText(resources.getString("Dialog.label"));
+            }
+            return nodeNameLabel;
+        }
+
+        /**
+         * Gets the text field for node name.
+         *
+         * @return the nodeNameField
+         */
+        protected JTextField getNodeNameField() {
+            if (nodeNameField == null) {
+                nodeNameField = new JTextField();
+            }
+            return nodeNameField;
+        }
+
+        /**
+         * Gets the dialog results.
+         *
+         * @return the element name
+         */
+        public String getResults() {
+            return nodeNameField.getText();
+        }
+
+        /**
+         * The action associated with the 'OK' button of Attribute Adder Dialog
+         */
+        protected class OKButtonAction extends AbstractAction {
+            public void actionPerformed(ActionEvent e) {
+                returnCode = OK_OPTION;
+                dispose();
+            }
+        }
+
+        /**
+         * The action associated with the 'Cancel' button of Attribute Adder
+         * Dialog
+         */
+        protected class CancelButtonAction extends AbstractAction {
+            public void actionPerformed(ActionEvent e) {
+                returnCode = CANCEL_OPTION;
+                dispose();
+            }
+        }
+
+        /**
+         * Returns the action associated with the given string or null on error
+         *
+         * @param key
+         *            the key mapped with the action to get
+         * @throws MissingListenerException
+         *             if the action is not found
+         */
+        public Action getAction(String key) throws MissingListenerException {
+            return (Action) listeners.get(key);
+        }
+    }
+}
+