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);
+ }
+ }
+}
+