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 [2/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...
Copied: xmlgraphics/batik/trunk/sources/org/apache/batik/apps/svgbrowser/DOMViewer.java (from r594022, xmlgraphics/batik/trunk/sources/org/apache/batik/util/gui/DOMViewer.java)
URL: http://svn.apache.org/viewvc/xmlgraphics/batik/trunk/sources/org/apache/batik/apps/svgbrowser/DOMViewer.java?p2=xmlgraphics/batik/trunk/sources/org/apache/batik/apps/svgbrowser/DOMViewer.java&p1=xmlgraphics/batik/trunk/sources/org/apache/batik/util/gui/DOMViewer.java&r1=594022&r2=594367&rev=594367&view=diff
==============================================================================
--- xmlgraphics/batik/trunk/sources/org/apache/batik/util/gui/DOMViewer.java (original)
+++ xmlgraphics/batik/trunk/sources/org/apache/batik/apps/svgbrowser/DOMViewer.java Mon Nov 12 16:40:53 2007
@@ -16,18 +16,31 @@
limitations under the License.
*/
-package org.apache.batik.util.gui;
+package org.apache.batik.apps.svgbrowser;
import java.awt.BorderLayout;
import java.awt.Component;
-import java.awt.GridLayout;
+import java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
-
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.lang.reflect.InvocationTargetException;
+import java.net.URL;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
+import java.util.Enumeration;
import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
@@ -36,14 +49,21 @@
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
+import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
import javax.swing.JPanel;
+import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
+import javax.swing.JToggleButton;
+import javax.swing.JToolBar;
import javax.swing.JTree;
+import javax.swing.SwingUtilities;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.table.AbstractTableModel;
@@ -52,24 +72,48 @@
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeNode;
+import javax.swing.tree.TreePath;
+import org.apache.batik.apps.svgbrowser.DOMDocumentTree.DOMDocumentTreeAdapter;
+import org.apache.batik.apps.svgbrowser.DOMDocumentTree.DOMDocumentTreeEvent;
+import org.apache.batik.apps.svgbrowser.DOMDocumentTree.DropCompletedInfo;
+import org.apache.batik.apps.svgbrowser.DropDownHistoryModel.RedoPopUpMenuModel;
+import org.apache.batik.apps.svgbrowser.DropDownHistoryModel.UndoPopUpMenuModel;
+import org.apache.batik.apps.svgbrowser.HistoryBrowser.DocumentCommandController;
+import org.apache.batik.apps.svgbrowser.NodePickerPanel.NameEditorDialog;
+import org.apache.batik.apps.svgbrowser.NodePickerPanel.NodePickerAdapter;
+import org.apache.batik.apps.svgbrowser.NodePickerPanel.NodePickerEvent;
+import org.apache.batik.apps.svgbrowser.NodeTemplates.NodeTemplateDescriptor;
import org.apache.batik.bridge.svg12.ContentManager;
import org.apache.batik.bridge.svg12.DefaultXBLManager;
import org.apache.batik.dom.AbstractDocument;
+import org.apache.batik.dom.svg.SVGOMDocument;
import org.apache.batik.dom.svg12.XBLOMContentElement;
+import org.apache.batik.dom.util.DOMUtilities;
+import org.apache.batik.dom.util.SAXDocumentFactory;
import org.apache.batik.dom.xbl.NodeXBL;
import org.apache.batik.dom.xbl.XBLManager;
+import org.apache.batik.util.SVGConstants;
+import org.apache.batik.util.XMLResourceDescriptor;
+import org.apache.batik.util.gui.DropDownComponent;
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.w3c.dom.Attr;
import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.css.CSSStyleDeclaration;
import org.w3c.dom.css.ViewCSS;
+import org.w3c.dom.events.Event;
+import org.w3c.dom.events.EventListener;
+import org.w3c.dom.events.EventTarget;
+import org.w3c.dom.events.MutationEvent;
/**
* The components of this class are used to view a DOM tree.
@@ -78,11 +122,12 @@
* @version $Id$
*/
public class DOMViewer extends JFrame implements ActionMap {
+
/**
* The resource file name
*/
protected static final String RESOURCE =
- "org.apache.batik.util.gui.resources.DOMViewerMessages";
+ "org.apache.batik.apps.svgbrowser.resources.DOMViewerMessages";
/**
* The resource bundle
@@ -105,9 +150,14 @@
protected Map listeners = new HashMap();
/**
+ * The button factory.
+ */
+ protected ButtonFactory buttonFactory;
+
+ /**
* The panel.
*/
- protected Panel panel = new Panel();
+ protected Panel panel;
/**
* Whether to show text nodes that contain only whitespace in the tree.
@@ -115,39 +165,97 @@
protected boolean showWhitespace = true;
/**
+ * Whether "capturing click" tool is enabled. If enabled, the element being
+ * clicked on is found and selected in the DOMViewer's document tree
+ */
+ protected boolean isCapturingClickEnabled;
+
+ /**
+ * The DOMViewer controller.
+ */
+ protected DOMViewerController domViewerController;
+
+ /**
+ * Manages the element selection on the canvas.
+ */
+ protected ElementOverlayManager elementOverlayManager;
+
+ /**
+ * Whether painting the overlay on the canvas is enabled.
+ */
+ protected boolean isElementOverlayEnabled = true;
+
+ /**
+ * The history browsing manager. Manages undo / redo.
+ */
+ protected HistoryBrowserInterface historyBrowserInterface;
+
+ /**
+ * Whether the DOMViewer can be used for editing the document.
+ */
+ protected boolean canEdit = true;
+
+ /**
+ * The button for enabling and disabling the overlay.
+ */
+ protected JToggleButton overlayButton;
+
+ /**
* Creates a new DOMViewer panel.
*/
- public DOMViewer() {
+ public DOMViewer(DOMViewerController controller) {
super(resources.getString("Frame.title"));
setSize(resources.getInteger("Frame.width"),
resources.getInteger("Frame.height"));
+ domViewerController = controller;
+
+ elementOverlayManager = domViewerController.createSelectionManager();
+ if (elementOverlayManager != null) {
+ elementOverlayManager
+ .setController(new DOMViewerElementOverlayController());
+ }
+ historyBrowserInterface =
+ new HistoryBrowserInterface
+ (new DocumentCommandController(controller));
+
listeners.put("CloseButtonAction", new CloseButtonAction());
+ listeners.put("UndoButtonAction", new UndoButtonAction());
+ listeners.put("RedoButtonAction", new RedoButtonAction());
+ listeners.put("CapturingClickButtonAction",
+ new CapturingClickButtonAction());
+ listeners.put("OverlayButtonAction", new OverlayButtonAction());
+ // Create the main panel
+ panel = new Panel();
getContentPane().add(panel);
JPanel p = new JPanel(new BorderLayout());
-
- JCheckBox cb = new JCheckBox("Show Whitespace Text Nodes");
+ JCheckBox cb =
+ new JCheckBox(resources.getString("ShowWhitespaceCheckbox.text"));
cb.setSelected(showWhitespace);
cb.addItemListener(new ItemListener() {
- public void itemStateChanged(ItemEvent ie) {
- setShowWhitespace
- (ie.getStateChange() == ItemEvent.SELECTED);
- }
- });
-
+ public void itemStateChanged(ItemEvent ie) {
+ setShowWhitespace(ie.getStateChange() == ItemEvent.SELECTED);
+ }
+ });
p.add(cb, BorderLayout.WEST);
-
-
- ButtonFactory bf = new ButtonFactory(bundle, this);
- p.add(bf.createJButton("CloseButton"), BorderLayout.EAST);
- getContentPane().add( p, BorderLayout.SOUTH );
+ p.add(getButtonFactory().createJButton("CloseButton"),
+ BorderLayout.EAST);
+ getContentPane().add(p, BorderLayout.SOUTH);
+
+ // Set the document
+ Document document = domViewerController.getDocument();
+ if (document != null) {
+// panel
+// .setDocument(document, (ViewCSS) document
+// .getDocumentElement());
+ panel.setDocument(document, null);
+ }
}
/**
- * Sets whether to show text nodes that contain only whitespace
- * in the tree.
+ * Sets whether to show text nodes that contain only whitespace in the tree.
*/
public void setShowWhitespace(boolean state) {
showWhitespace = state;
@@ -170,34 +278,234 @@
}
/**
- * 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
+ * Whether the document can be edited using the DOMViewer.
+ *
+ * @return True if the document can be edited throught the DOMViewer
+ */
+ public boolean canEdit() {
+ return domViewerController.canEdit() && canEdit;
+ }
+
+ /**
+ * Enables / disables the DOMViewer to be used to edit the documents.
+ *
+ * @param canEdit
+ * True - The DOMViewer can be used to edit the documents
+ */
+ public void setEditable(boolean canEdit) {
+ this.canEdit = canEdit;
+ }
+
+ /**
+ * Selects and scrolls to the given node in the document tree.
+ *
+ * @param node
+ * The node to be selected
+ */
+ public void selectNode(Node node) {
+ panel.selectNode(node);
+ }
+
+ /**
+ * Resets the history.
+ */
+ public void resetHistory() {
+ historyBrowserInterface.getHistoryBrowser().resetHistory();
+ }
+
+ /**
+ * Gets buttonFactory.
+ */
+ private ButtonFactory getButtonFactory() {
+ if (buttonFactory == null) {
+ buttonFactory = new ButtonFactory(bundle, this);
+ }
+ return buttonFactory;
+ }
+
+ // ActionMap
+
+ /**
+ * 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);
}
/**
+ * Groups the document changes that were made out of the document into a
+ * single change and adds this change to the history browser.
+ */
+ private void addChangesToHistory() {
+ historyBrowserInterface.performCurrentCompoundCommand();
+ }
+
+ /**
* The action associated with the 'Close' button of the viewer panel
*/
protected class CloseButtonAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
+ panel.tree.setSelectionRow(0);
dispose();
}
}
/**
+ * The action associated with the 'Undo' dropdown button of the viewer panel
+ */
+ protected class UndoButtonAction extends AbstractAction {
+ public void actionPerformed(ActionEvent e) {
+ addChangesToHistory();
+ historyBrowserInterface.getHistoryBrowser().undo();
+ }
+ }
+
+ /**
+ * The action associated with the 'Redo' dropdown button of the viewer panel
+ */
+ protected class RedoButtonAction extends AbstractAction {
+ public void actionPerformed(ActionEvent e) {
+ addChangesToHistory();
+ historyBrowserInterface.getHistoryBrowser().redo();
+ }
+ }
+
+ /**
+ * The action associated with the 'Capturing-click' toggle button of the
+ * viewer panel.
+ */
+ protected class CapturingClickButtonAction extends AbstractAction {
+ public void actionPerformed(ActionEvent e) {
+ JToggleButton btn = (JToggleButton) e.getSource();
+ isCapturingClickEnabled = btn.isSelected();
+ if (!isCapturingClickEnabled) {
+ btn.setToolTipText
+ (resources.getString("CapturingClickButton.tooltip"));
+ } else {
+ btn.setToolTipText
+ (resources.getString("CapturingClickButton.disableText"));
+ }
+ }
+ }
+
+ /**
+ * Toggles the element highlighting overlay.
+ */
+ protected void toggleOverlay() {
+ isElementOverlayEnabled = overlayButton.isSelected();
+ if (!isElementOverlayEnabled) {
+ overlayButton.setToolTipText
+ (resources.getString("OverlayButton.tooltip"));
+ } else {
+ overlayButton.setToolTipText
+ (resources.getString("OverlayButton.disableText"));
+ }
+ // Refresh overlay
+ if (elementOverlayManager != null) {
+ elementOverlayManager.setOverlayEnabled(isElementOverlayEnabled);
+ elementOverlayManager.repaint();
+ }
+ }
+
+ /**
+ * The action associated with the 'overlay' toggle button of the viewer
+ * panel.
+ */
+ protected class OverlayButtonAction extends AbstractAction {
+ public void actionPerformed(ActionEvent e) {
+ toggleOverlay();
+ }
+ }
+
+ /**
+ * NodePickerController implementation.
+ */
+ protected class DOMViewerNodePickerController
+ implements NodePickerController {
+
+ public boolean isEditable() {
+ return DOMViewer.this.canEdit();
+ }
+
+ public boolean canEdit(Element el) {
+ if (panel == null || panel.document == null
+ || panel.document.getDocumentElement() != el) {
+ return true;
+ }
+ return false;
+ }
+ }
+
+ /**
+ * DOMDocumentTreeController implementation.
+ */
+ protected class DOMViewerDOMDocumentTreeController
+ implements DOMDocumentTreeController {
+
+ public boolean isDNDSupported() {
+ return canEdit();
+ }
+ }
+
+ /**
+ * ElementOverlayController implementation.
+ */
+ protected class DOMViewerElementOverlayController
+ implements ElementOverlayController {
+
+ public boolean isOverlayEnabled() {
+ return canEdit() && isElementOverlayEnabled;
+ }
+ }
+
+ /**
* The panel that contains the viewer.
*/
public class Panel extends JPanel {
+
+ // DOM Mutation event names.
+ public static final String NODE_INSERTED = "DOMNodeInserted";
+ public static final String NODE_REMOVED = "DOMNodeRemoved";
+ public static final String ATTRIBUTE_MODIFIED = "DOMAttrModified";
+ public static final String CHAR_DATA_MODIFIED = "DOMCharacterDataModified";
+
/**
* The DOM document.
*/
protected Document document;
/**
+ * "Node inserted" DOM Mutation Listener.
+ */
+ protected EventListener nodeInsertion;
+
+ /**
+ * "Node removed" DOM Mutation Listener.
+ */
+ protected EventListener nodeRemoval;
+
+ /**
+ * "Attribute modified" DOM Mutation Listener.
+ */
+ protected EventListener attrModification;
+
+ /**
+ * "Character data modified" DOM Mutation Listener.
+ */
+ protected EventListener charDataModification;
+
+ /**
+ * Capturing "click" event type listener. Allows the "capturing click"
+ * option
+ */
+ protected EventListener capturingListener;
+
+ /**
* The ViewCSS object associated with the document.
*/
protected ViewCSS viewCSS;
@@ -205,7 +513,7 @@
/**
* The tree.
*/
- protected JTree tree;
+ protected DOMDocumentTree tree;
/**
* The split pane.
@@ -218,53 +526,246 @@
protected JPanel rightPanel = new JPanel(new BorderLayout());
/**
- * The attributes table.
+ * The properties table.
*/
- protected JTable attributesTable = new JTable();
+ protected JTable propertiesTable = new JTable();
/**
- * The properties table.
+ * The panel to show the nodes attributes.
*/
- protected JTable propertiesTable = new JTable();
+ protected NodePickerPanel attributePanel =
+ new NodePickerPanel(new DOMViewerNodePickerController());
+ {
+ attributePanel.addListener(new NodePickerAdapter() {
+
+ public void updateElement(NodePickerEvent event) {
+ String result = event.getResult();
+ Element targetElement = (Element) event.getContextNode();
+ Element newElem = wrapAndParse(result, targetElement);
+
+ addChangesToHistory();
+
+ AbstractCompoundCommand cmd = historyBrowserInterface
+ .createNodeChangedCommand(newElem);
+ Node parent = targetElement.getParentNode();
+ Node nextSibling = targetElement.getNextSibling();
+ cmd.addCommand(historyBrowserInterface
+ .createRemoveChildCommand(parent, targetElement));
+ cmd.addCommand(historyBrowserInterface
+ .createInsertChildCommand(parent, nextSibling,
+ newElem));
+ historyBrowserInterface.performCompoundUpdateCommand(cmd);
+
+ attributePanel.setPreviewElement(newElem);
+ //selectNewNode(newElem);
+ }
+
+ public void addNewElement(NodePickerEvent event) {
+ String result = event.getResult();
+ Element targetElement = (Element) event.getContextNode();
+ Element newElem = wrapAndParse(result, targetElement);
+
+ addChangesToHistory();
+
+ historyBrowserInterface.appendChild(targetElement,
+ newElem);
+
+ attributePanel.setPreviewElement(newElem);
+ //selectNewNode(newElem);
+ }
+
+ /**
+ * Parses the given string into an element after editing, or
+ * adding new element. The element that should be parsed has to
+ * have all the active prefixes set, so it has to be wrapped
+ * with the wrapper element that has all the mentioned prefixes
+ * bound to the appopriate namespaces. Wrapper element string
+ * has all the active prefixes set using the parentNode.
+ *
+ * @param toParse
+ * The string that should be parsed into svg element
+ * @param startingNode
+ * The node from where to start looking the prefixes
+ * @return The parsed element
+ */
+ private Element wrapAndParse(String toParse, Node startingNode) {
+ // Fill the prefix map
+ Map prefixMap = new HashMap();
+ int j = 0;
+ for (Node currentNode = startingNode;
+ currentNode != null;
+ currentNode = currentNode.getParentNode()) {
+ NamedNodeMap nMap = currentNode.getAttributes();
+ for (int i = 0; nMap != null && i < nMap.getLength(); i++) {
+ Attr atr = (Attr) nMap.item(i);
+ String prefix = atr.getPrefix();
+ String localName = atr.getLocalName();
+ String namespaceURI = atr.getValue();
+ if (prefix != null
+ && prefix.equals(SVGConstants.XMLNS_PREFIX)) {
+ String attrName = SVGConstants.XMLNS_PREFIX
+ + ":" + localName;
+ if (!prefixMap.containsKey(attrName)) {
+ prefixMap.put(attrName, namespaceURI);
+ }
+ }
+ // Start from parentNode searching for the xmlns
+ if ((j != 0 || currentNode == document
+ .getDocumentElement())
+ && atr.getNodeName().equals(
+ SVGConstants.XMLNS_PREFIX)
+ && !prefixMap
+ .containsKey(SVGConstants.XMLNS_PREFIX)) {
+ prefixMap.put(SVGConstants.XMLNS_PREFIX, atr
+ .getNodeValue());
+ }
+ }
+ j++;
+ }
+ Document doc = panel.document;
+ SAXDocumentFactory df = new SAXDocumentFactory(
+ doc.getImplementation(),
+ XMLResourceDescriptor.getXMLParserClassName());
+ URL urlObj = null;
+ if (doc instanceof SVGOMDocument) {
+ urlObj = ((SVGOMDocument) doc).getURLObject();
+ }
+ String uri = (urlObj == null) ? "" : urlObj.toString();
+ Node node = DOMUtilities.parseXML(toParse, doc, uri,
+ prefixMap, SVGConstants.SVG_SVG_TAG, df);
+ return (Element) node.getFirstChild();
+ }
+
+ /**
+ * Selects the given element. Needs to be wrapped with the
+ * UpdateManager to wait for the previous command to be executed
+ * by HistoryBrowser
+ *
+ * @param elem
+ * The element to select
+ */
+ private void selectNewNode(final Element elem) {
+ domViewerController.performUpdate(new Runnable() {
+ public void run() {
+ selectNode(elem);
+ };
+ });
+ }
+ });
+ }
/**
- * The element panel.
+ * The layout for the attribute panel.
*/
- protected JPanel elementPanel = new JPanel(new GridLayout(2, 1));
+ protected GridBagConstraints attributePanelLayout =
+ new GridBagConstraints();
{
- JScrollPane pane = new JScrollPane();
- pane.setBorder(BorderFactory.createCompoundBorder
- (BorderFactory.createEmptyBorder(2, 0, 2, 2),
- BorderFactory.createCompoundBorder
- (BorderFactory.createTitledBorder
- (BorderFactory.createEmptyBorder(),
- resources.getString("AttributesPanel.title")),
- BorderFactory.createLoweredBevelBorder())));
- pane.getViewport().add(attributesTable);
+ attributePanelLayout.gridx = 1;
+ attributePanelLayout.gridy = 1;
+ attributePanelLayout.gridheight = 2;
+ attributePanelLayout.weightx = 1.0;
+ attributePanelLayout.weighty = 1.0;
+ attributePanelLayout.fill = GridBagConstraints.BOTH;
+ }
+
+ /**
+ * The layout for the properties table.
+ */
+ protected GridBagConstraints propertiesTableLayout =
+ new GridBagConstraints();
+ {
+ propertiesTableLayout.gridx = 1;
+ propertiesTableLayout.gridy = 3;
+ propertiesTableLayout.weightx = 1.0;
+ propertiesTableLayout.weighty = 1.0;
+ propertiesTableLayout.fill = GridBagConstraints.BOTH;
+ }
+ /**
+ * The element panel.
+ */
+ protected JPanel elementPanel = new JPanel(new GridBagLayout());
+ {
JScrollPane pane2 = new JScrollPane();
- pane2.setBorder(BorderFactory.createCompoundBorder
- (BorderFactory.createEmptyBorder(2, 0, 2, 2),
- BorderFactory.createCompoundBorder
- (BorderFactory.createTitledBorder
- (BorderFactory.createEmptyBorder(),
- resources.getString("CSSValuesPanel.title")),
- BorderFactory.createLoweredBevelBorder())));
+ pane2.setBorder(BorderFactory.createCompoundBorder(BorderFactory
+ .createEmptyBorder(2, 0, 2, 2), BorderFactory
+ .createCompoundBorder(BorderFactory.createTitledBorder(
+ BorderFactory.createEmptyBorder(), resources
+ .getString("CSSValuesPanel.title")),
+ BorderFactory.createLoweredBevelBorder())));
pane2.getViewport().add(propertiesTable);
- elementPanel.add(pane);
- elementPanel.add(pane2);
+ // 2/3 attributePanel, 1/3 propertiesTable
+ elementPanel.add(attributePanel, attributePanelLayout);
+ elementPanel.add(pane2, propertiesTableLayout);
}
/**
* The CharacterData panel text area.
*/
- protected JTextArea characterData = new JTextArea();
+ protected class CharacterPanel extends JPanel {
+
+ /**
+ * Associated node.
+ */
+ protected Node node;
+
+ /**
+ * The text area.
+ */
+ protected JTextArea textArea = new JTextArea();
+
+ /**
+ * Constructor.
+ * @param layout The LayoutManager
+ */
+ public CharacterPanel(BorderLayout layout) {
+ super(layout);
+ }
+
+ /**
+ * Gets the textArea.
+ *
+ * @return textArea
+ */
+ public JTextArea getTextArea() {
+ return textArea;
+ }
+
+ /**
+ * Sets the textArea.
+ *
+ * @param textArea
+ * the text area to set
+ */
+ public void setTextArea(JTextArea textArea) {
+ this.textArea = textArea;
+ }
+
+ /**
+ * Gets the node.
+ *
+ * @return node
+ */
+ public Node getNode() {
+ return node;
+ }
+
+ /**
+ * Sets the node.
+ *
+ * @param node
+ * the node to set
+ */
+ public void setNode(Node node) {
+ this.node = node;
+ }
+ }
/**
* The CharacterData node panel.
*/
- protected JPanel characterDataPanel = new JPanel(new BorderLayout());
+ protected CharacterPanel characterDataPanel = new CharacterPanel(new BorderLayout());
{
characterDataPanel.setBorder
(BorderFactory.createCompoundBorder
@@ -275,9 +776,30 @@
resources.getString("CDataPanel.title")),
BorderFactory.createLoweredBevelBorder())));
JScrollPane pane = new JScrollPane();
- pane.getViewport().add(characterData);
+ JTextArea textArea = new JTextArea();
+ characterDataPanel.setTextArea(textArea);
+ pane.getViewport().add(textArea);
characterDataPanel.add(pane);
- characterData.setEditable(false);
+
+ textArea.setEditable(true);
+ textArea.addFocusListener(new FocusAdapter() {
+ public void focusLost(FocusEvent e) {
+ if (canEdit()) {
+ Node contextNode = characterDataPanel.getNode();
+ String newValue = characterDataPanel.getTextArea()
+ .getText();
+ switch (contextNode.getNodeType()) {
+ case Node.COMMENT_NODE:
+ case Node.TEXT_NODE:
+ case Node.CDATA_SECTION_NODE:
+ addChangesToHistory();
+ historyBrowserInterface.setNodeValue(contextNode,
+ newValue);
+ break;
+ }
+ }
+ }
+ });
}
/**
@@ -313,13 +835,92 @@
(BorderFactory.createEmptyBorder(),
resources.getString("DOMViewerPanel.title")));
+ JToolBar tb =
+ new JToolBar(resources.getString("DOMViewerToolbar.name"));
+
+ // Undo
+ JButton undoButton = getButtonFactory().createJButton("UndoButton");
+ undoButton.setDisabledIcon
+ (new ImageIcon
+ (getClass().getResource(resources.getString("UndoButton.disabledIcon"))));
+ DropDownComponent undoDD = new DropDownComponent(undoButton);
+ undoDD.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 2));
+ undoDD.setMaximumSize(new Dimension(44, 25));
+ undoDD.setPreferredSize(new Dimension(44, 25));
+ tb.add(undoDD);
+ UndoPopUpMenuModel undoModel = new UndoPopUpMenuModel(undoDD
+ .getPopupMenu(), historyBrowserInterface);
+ undoDD.getPopupMenu().setModel(undoModel);
+
+ // Redo
+ JButton redoButton = getButtonFactory().createJButton("RedoButton");
+ redoButton.setDisabledIcon
+ (new ImageIcon
+ (getClass().getResource(resources.getString("RedoButton.disabledIcon"))));
+ DropDownComponent redoDD = new DropDownComponent(redoButton);
+ redoDD.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 2));
+ redoDD.setMaximumSize(new Dimension(44, 25));
+ redoDD.setPreferredSize(new Dimension(44, 25));
+ tb.add(redoDD);
+ RedoPopUpMenuModel redoModel = new RedoPopUpMenuModel(redoDD
+ .getPopupMenu(), historyBrowserInterface);
+ redoDD.getPopupMenu().setModel(redoModel);
+
+ // Capturing click toggle button
+ JToggleButton capturingClickButton = getButtonFactory()
+ .createJToolbarToggleButton("CapturingClickButton");
+ capturingClickButton.setEnabled(true);
+ capturingClickButton.setPreferredSize(new Dimension(32, 25));
+ tb.add(capturingClickButton);
+
+ // Overlay toggle button
+ overlayButton =
+ getButtonFactory().createJToolbarToggleButton("OverlayButton");
+ overlayButton.setEnabled(true);
+ overlayButton.setPreferredSize(new Dimension(32, 25));
+ tb.add(overlayButton);
+ overlayButton.doClick();
+
+ // Add toolbar
+ add(tb, BorderLayout.NORTH);
+
+ // DOMDocumentTree
TreeNode root;
root = new DefaultMutableTreeNode
(resources.getString("EmptyDocument.text"));
- tree = new JTree(root);
+ tree = new DOMDocumentTree
+ (root, new DOMViewerDOMDocumentTreeController());
tree.setCellRenderer(new NodeRenderer());
tree.putClientProperty("JTree.lineStyle", "Angled");
+ // Add the listeners to DOMDocumentTree
+ tree.addListener(new DOMDocumentTreeAdapter() {
+ public void dropCompleted(DOMDocumentTreeEvent event) {
+ DropCompletedInfo info = (DropCompletedInfo) event
+ .getSource();
+
+ addChangesToHistory();
+
+ AbstractCompoundCommand cmd = historyBrowserInterface
+ .createNodesDroppedCommand(info.getChildren());
+
+ int n = info.getChildren().size();
+ for (int i = 0; i < n; i++) {
+ Node node = (Node) info.getChildren().get(i);
+ // If the node has its ancestor in selected nodes,
+ // it should stay as it's child
+ if (!DOMUtilities.isAnyNodeAncestorOf(info
+ .getChildren(), node)) {
+ cmd.addCommand(historyBrowserInterface
+ .createInsertChildCommand(info.getParent(),
+ info.getSibling(), node));
+ }
+ }
+ historyBrowserInterface.performCompoundUpdateCommand(cmd);
+ }
+ });
+ tree.addTreeSelectionListener(new DOMTreeSelectionListener());
+ tree.addMouseListener(new TreePopUpListener());
JScrollPane treePane = new JScrollPane();
treePane.setBorder(BorderFactory.createCompoundBorder
(BorderFactory.createEmptyBorder(2, 2, 2, 0),
@@ -336,8 +937,6 @@
int loc = resources.getInteger("SplitPane.dividerLocation");
splitPane.setDividerLocation(loc);
add(splitPane);
-
- tree.addTreeSelectionListener(new DOMTreeSelectionListener());
}
/**
@@ -351,10 +950,23 @@
* Sets the document to display and its ViewCSS.
*/
public void setDocument(Document doc, ViewCSS view) {
+ if (document != null) {
+ if (document != doc) {
+ removeDomMutationListeners(document);
+ addDomMutationListeners(doc);
+ removeCapturingListener(document);
+ addCapturingListener(doc);
+ }
+ }
+ else {
+ addDomMutationListeners(doc);
+ addCapturingListener(doc);
+ }
+ resetHistory();
document = doc;
- viewCSS = view;
+ viewCSS = view;
TreeNode root = createTree(doc, showWhitespace);
- ((DefaultTreeModel)tree.getModel()).setRoot(root);
+ ((DefaultTreeModel) tree.getModel()).setRoot(root);
if (rightPanel.getComponentCount() != 0) {
rightPanel.remove(0);
splitPane.revalidate();
@@ -363,6 +975,426 @@
}
/**
+ * Registers DOM Mutation Listener on the given document.
+ *
+ * @param doc
+ * The given document
+ */
+ protected void addDomMutationListeners(Document doc) {
+ EventTarget target = (EventTarget) doc;
+ nodeInsertion = new NodeInsertionHandler();
+ target.addEventListener(NODE_INSERTED, nodeInsertion, true);
+ nodeRemoval = new NodeRemovalHandler();
+ target.addEventListener(NODE_REMOVED, nodeRemoval, true);
+ attrModification = new AttributeModificationHandler();
+ target.addEventListener(ATTRIBUTE_MODIFIED, attrModification,
+ true);
+ charDataModification = new CharDataModificationHandler();
+ target.addEventListener(CHAR_DATA_MODIFIED,
+ charDataModification, true);
+ }
+
+ /**
+ * Removes previously registered mutation listeners from the document.
+ *
+ * @param doc
+ * The document
+ */
+ protected void removeDomMutationListeners(Document doc) {
+ if (doc != null) {
+ EventTarget target = (EventTarget) doc;
+ target.removeEventListener(NODE_INSERTED, nodeInsertion, true);
+ target.removeEventListener(NODE_REMOVED, nodeRemoval, true);
+ target.removeEventListener(ATTRIBUTE_MODIFIED,
+ attrModification, true);
+ target.removeEventListener(CHAR_DATA_MODIFIED,
+ charDataModification, true);
+ }
+ }
+
+ /**
+ * Registers capturing "click" listener on the document element of the
+ * given document.
+ *
+ * @param doc
+ * The given document
+ */
+ protected void addCapturingListener(Document doc) {
+ EventTarget target = (EventTarget) doc.getDocumentElement();
+ capturingListener = new CapturingClickHandler();
+ target.addEventListener(SVGConstants.SVG_CLICK_EVENT_TYPE,
+ capturingListener, true);
+ }
+
+ /**
+ * Removes previously registered capturing "click" event listener from
+ * the document element of the given document.
+ *
+ * @param doc
+ * The given document
+ */
+ protected void removeCapturingListener(Document doc) {
+ if (doc != null) {
+ EventTarget target = (EventTarget) doc.getDocumentElement();
+ target.removeEventListener(SVGConstants.SVG_CLICK_EVENT_TYPE,
+ capturingListener, true);
+ }
+ }
+
+ /**
+ * Handles "DOMNodeInserted" event.
+ */
+ protected class NodeInsertionHandler implements EventListener {
+
+ public void handleEvent(final Event evt) {
+ Runnable runnable = new Runnable() {
+ public void run() {
+ MutationEvent mevt = (MutationEvent) evt;
+ Node targetNode = (Node) mevt.getTarget();
+ DefaultMutableTreeNode parentNode = findNode(tree,
+ targetNode.getParentNode());
+ DefaultMutableTreeNode insertedNode =
+ (DefaultMutableTreeNode)
+ createTree(targetNode, showWhitespace);
+ DefaultTreeModel model =
+ (DefaultTreeModel) tree.getModel();
+ DefaultMutableTreeNode newParentNode =
+ (DefaultMutableTreeNode)
+ createTree(targetNode.getParentNode(),
+ showWhitespace);
+ // Finds where to insert the node in the tree
+ int index = findIndexToInsert(parentNode, newParentNode);
+ if (index != -1) {
+ model.insertNodeInto
+ (insertedNode, parentNode, index);
+ }
+ attributePanel.updateOnDocumentChange(mevt.getType(),
+ targetNode);
+ }
+ };
+ refreshGUI(runnable);
+ registerDocumentChange((MutationEvent)evt);
+ }
+
+ /**
+ * Compares the children of the two tree nodes and returns the index
+ * of the first difference.
+ *
+ * @param parentNode
+ * The old tree node
+ * @param newParentNode
+ * The new tree node
+ * @return first child that differs
+ */
+ protected int findIndexToInsert
+ (DefaultMutableTreeNode parentNode,
+ DefaultMutableTreeNode newParentNode) {
+ int index = -1;
+ if (parentNode == null || newParentNode == null) {
+ return index;
+ }
+ // Finds the index where to insert new node
+ Enumeration oldChildren = parentNode.children();
+ Enumeration newChildren = newParentNode.children();
+ int count = 0;
+ while (oldChildren.hasMoreElements()) {
+ // Current DefaultMutableTreeNode node being processed
+ DefaultMutableTreeNode currentOldChild =
+ (DefaultMutableTreeNode) oldChildren.nextElement();
+ DefaultMutableTreeNode currentNewChild =
+ (DefaultMutableTreeNode) newChildren.nextElement();
+ Node oldChild =
+ ((NodeInfo) currentOldChild.getUserObject()).getNode();
+ Node newChild =
+ ((NodeInfo) currentNewChild.getUserObject()).getNode();
+ if (oldChild != newChild) {
+ return count;
+ }
+ count++;
+ }
+ return count;
+ }
+ }
+
+ /**
+ * Handles "DOMNodeRemoved" event.
+ */
+ protected class NodeRemovalHandler implements EventListener {
+
+ public void handleEvent(final Event evt) {
+ Runnable runnable = new Runnable() {
+ public void run() {
+ MutationEvent mevt = (MutationEvent) evt;
+ Node targetNode = (Node) mevt.getTarget();
+ DefaultMutableTreeNode treeNode = findNode(tree,
+ targetNode);
+ DefaultTreeModel model = (DefaultTreeModel) tree
+ .getModel();
+ if (treeNode != null) {
+ model.removeNodeFromParent(treeNode);
+ }
+ attributePanel.updateOnDocumentChange(mevt.getType(),
+ targetNode);
+ }
+ };
+ refreshGUI(runnable);
+ registerDocumentChange((MutationEvent)evt);
+ }
+ }
+
+ /**
+ * Handles "DOMAttrModified" event.
+ */
+ protected class AttributeModificationHandler implements EventListener {
+
+ public void handleEvent(final Event evt) {
+ Runnable runnable = new Runnable() {
+ public void run() {
+ MutationEvent mevt = (MutationEvent) evt;
+ Element targetElement = (Element) mevt.getTarget();
+ // Repaint the target node
+ DefaultTreeModel model = (DefaultTreeModel) tree
+ .getModel();
+
+ model.nodeChanged(findNode(tree, targetElement));
+ attributePanel.updateOnDocumentChange(mevt.getType(),
+ targetElement);
+ }
+ };
+ refreshGUI(runnable);
+ registerDocumentChange((MutationEvent) evt);
+ }
+ }
+
+ /**
+ * Handles "DOMCharacterDataModified" event.
+ */
+ protected class CharDataModificationHandler implements EventListener {
+ public void handleEvent(final Event evt) {
+ Runnable runnable = new Runnable() {
+ public void run() {
+ MutationEvent mevt = (MutationEvent) evt;
+ Node targetNode = (Node) mevt.getTarget();
+ if (characterDataPanel.getNode() == targetNode) {
+ characterDataPanel.getTextArea().setText(
+ targetNode.getNodeValue());
+ attributePanel.updateOnDocumentChange(mevt
+ .getType(), targetNode);
+ }
+ }
+ };
+ refreshGUI(runnable);
+ if (characterDataPanel.getNode() == evt.getTarget()) {
+ registerDocumentChange((MutationEvent) evt);
+ }
+ }
+ }
+
+ /**
+ * Checks whether the DOMViewer can be used to edit the document and if
+ * true refreshes the DOMViewer after the DOM Mutation event occured.
+ *
+ * @param runnable
+ * The runnable to invoke for refresh
+ */
+ protected void refreshGUI(Runnable runnable) {
+ if (canEdit()) {
+ try {
+ SwingUtilities.invokeAndWait(runnable);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Adds the "DOMNodeInserted" Mutation event to current history
+ * browser's interface compound command
+ *
+ * @param mevt
+ * The Mutation Event
+ */
+ protected void registerNodeInserted(MutationEvent mevt) {
+ Node targetNode = (Node) mevt.getTarget();
+ historyBrowserInterface.addToCurrentCompoundCommand(
+ historyBrowserInterface.createNodeInsertedCommand(
+ targetNode.getParentNode(),
+ targetNode.getNextSibling(),targetNode));
+ }
+
+ /**
+ * Adds the "DOMNodeRemoved" Mutation event to current history browser's
+ * interface compound command
+ *
+ * @param mevt
+ * The Mutation Event
+ */
+ protected void registerNodeRemoved(MutationEvent mevt) {
+ Node targetNode = (Node) mevt.getTarget();
+ historyBrowserInterface.addToCurrentCompoundCommand
+ (historyBrowserInterface.createNodeRemovedCommand
+ (mevt.getRelatedNode(),
+ targetNode.getNextSibling(),
+ targetNode));
+ }
+
+ /**
+ * Adds the "DOMAttrModified" Mutation event, of the
+ * MutationEvent.ADDITION type to current history browser's
+ * interface compound command
+ *
+ * @param mevt
+ * The Mutation Event
+ */
+
+ protected void registerAttributeAdded(MutationEvent mevt) {
+ Element targetElement = (Element) mevt.getTarget();
+ historyBrowserInterface.addToCurrentCompoundCommand
+ (historyBrowserInterface.createAttributeAddedCommand
+ (targetElement,
+ mevt.getAttrName(),
+ mevt.getNewValue(),
+ null));
+ }
+
+ /**
+ * Adds the "DOMAttrModified" Mutation event, of the
+ * MutationEvent.REMOVAL type to current history browser's
+ * interface compound command
+ *
+ * @param mevt
+ * The Mutation Event
+ */
+
+ protected void registerAttributeRemoved(MutationEvent mevt) {
+ Element targetElement = (Element) mevt.getTarget();
+ historyBrowserInterface.addToCurrentCompoundCommand
+ (historyBrowserInterface.createAttributeRemovedCommand
+ (targetElement,
+ mevt.getAttrName(),
+ mevt.getPrevValue(),
+ null));
+ }
+
+ /**
+ * Adds the "DOMAttrModified" Mutation event, of the
+ * MutationEvent.MODIFICATION type to current history browser's
+ * interface compound command
+ *
+ * @param mevt
+ * The Mutation Event
+ */
+ protected void registerAttributeModified(MutationEvent mevt) {
+ Element targetElement = (Element) mevt.getTarget();
+ historyBrowserInterface.addToCurrentCompoundCommand
+ (historyBrowserInterface.createAttributeModifiedCommand
+ (targetElement,
+ mevt.getAttrName(),
+ mevt.getPrevValue(),
+ mevt.getNewValue(),
+ null));
+ }
+
+ /**
+ * Checks what type of the "DOMAttrModified" mutation event occured, and
+ * invokes the appropriate method to register the change.
+ *
+ * @param mevt
+ * The Mutation Event
+ */
+ protected void registerAttributeChanged(MutationEvent mevt) {
+ switch (mevt.getAttrChange()) {
+ case MutationEvent.ADDITION:
+ registerAttributeAdded(mevt);
+ break;
+ case MutationEvent.REMOVAL:
+ registerAttributeRemoved(mevt);
+ break;
+ case MutationEvent.MODIFICATION:
+ registerAttributeModified(mevt);
+ break;
+ default:
+ registerAttributeModified(mevt);
+ break;
+ }
+ }
+
+ /**
+ * Adds the "DOMCharDataModified" Mutation event to current history
+ * browser's interface compound command
+ *
+ * @param mevt
+ * The Mutation Event
+ */
+ protected void registerCharDataModified(MutationEvent mevt) {
+ Node targetNode = (Node) mevt.getTarget();
+ historyBrowserInterface.addToCurrentCompoundCommand
+ (historyBrowserInterface.createCharDataModifiedCommand
+ (targetNode,
+ mevt.getPrevValue(),
+ mevt.getNewValue()));
+ }
+
+ /**
+ * Checks if the document change that occured should be registered. If
+ * the document change has occured out of the DOMViewer, the state of
+ * the history browser should be HistoryBrowserState.IDLE. Otherwise, if
+ * the DOMViewer caused the change, one of the following states is
+ * active: HistoryBrowserState.EXECUTING, HistoryBrowserState.UNDOING,
+ * HistoryBrowserState.REDOING. This method should be invoked while the
+ * document change is occuring (in mutation event handlers), otherwise,
+ * if put in another thread, the state flag becomes invalid. Also checks
+ * if the DOMViewer can be used to edit the document. If not, then the
+ * change shouldn't be registered by the HistoryBrowser
+ *
+ * @return True if the DOMViewer can edit the document and the history
+ * browser state is IDLE at the moment
+ */
+ protected boolean shouldRegisterDocumentChange() {
+ return canEdit() &&
+ historyBrowserInterface.getHistoryBrowser().getState()
+ == HistoryBrowser.IDLE;
+ }
+
+ /**
+ * Puts the document change in the current history browser's interface
+ * compound command if the document change occured outside of the
+ * DOMViewer.
+ *
+ * @param mevt
+ * The info on the event. Use to describe the document change
+ * to the history browser
+ */
+ protected void registerDocumentChange(MutationEvent mevt) {
+ if (shouldRegisterDocumentChange()) {
+ String type = mevt.getType();
+ if (type.equals(NODE_INSERTED)) {
+ registerNodeInserted(mevt);
+ } else if (type.equals(NODE_REMOVED)) {
+ registerNodeRemoved(mevt);
+ } else if (type.equals(ATTRIBUTE_MODIFIED)) {
+ registerAttributeChanged(mevt);
+ } else if (type.equals(CHAR_DATA_MODIFIED)) {
+ registerCharDataModified(mevt);
+ }
+ }
+ }
+
+ /**
+ * Handles the capturing "mouseclick" event.
+ */
+ protected class CapturingClickHandler implements EventListener {
+ public void handleEvent(Event evt) {
+ if (isCapturingClickEnabled) {
+ Element targetElement = (Element) evt.getTarget();
+ selectNode(targetElement);
+ }
+ }
+ }
+
+ /**
* Creates a swing tree from a DOM document.
*/
protected MutableTreeNode createTree(Node node,
@@ -413,17 +1445,332 @@
}
/**
+ * Finds and returns the node in the tree that represents the given node
+ * in the document.
+ *
+ * @param theTree
+ * The given JTree
+ * @param node
+ * The given org.w3c.dom.Node
+ * @return Node or null if not found
+ */
+ protected DefaultMutableTreeNode findNode(JTree theTree, Node node) {
+ DefaultMutableTreeNode root = (DefaultMutableTreeNode) theTree
+ .getModel().getRoot();
+ Enumeration treeNodes = root.breadthFirstEnumeration();
+ while (treeNodes.hasMoreElements()) {
+ DefaultMutableTreeNode currentNode = (DefaultMutableTreeNode) treeNodes
+ .nextElement();
+ NodeInfo userObject = (NodeInfo) currentNode.getUserObject();
+ if (userObject.getNode() == node) {
+ return currentNode;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Finds and selects the given node in the document tree.
+ *
+ * @param targetNode
+ * The node to be selected
+ */
+ public void selectNode(final Node targetNode) {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ DefaultMutableTreeNode node = findNode(tree, targetNode);
+ if (node != null) {
+ TreeNode[] path = node.getPath();
+ TreePath tp = new TreePath(path);
+ // Changes the selection
+ tree.setSelectionPath(tp);
+ // Expands and scrolls the TreePath to visible if
+ // needed
+ tree.scrollPathToVisible(tp);
+ }
+ }
+ });
+ }
+
+ /**
+ * Tree popup listener.
+ */
+ protected class TreePopUpListener extends MouseAdapter {
+
+ /**
+ * The actual pop-up menu.
+ */
+ protected JPopupMenu treePopupMenu;
+
+ /**
+ * Creates new pop up listener.
+ */
+ public TreePopUpListener() {
+ treePopupMenu = new JPopupMenu();
+
+ // "Insert new node" menu
+ treePopupMenu.add(createTemplatesMenu(resources
+ .getString("ContextMenuItem.insertNewNode")));
+
+ // "Create new element..." item
+ JMenuItem item = new JMenuItem(resources
+ .getString("ContextMenuItem.createNewElement"));
+ treePopupMenu.add(item);
+ item.addActionListener(new TreeNodeAdder());
+
+ // "Remove selection" item
+ item = new JMenuItem(resources
+ .getString("ContextMenuItem.removeSelection"));
+ item.addActionListener(new TreeNodeRemover());
+ treePopupMenu.add(item);
+ }
+
+ public void mouseReleased(MouseEvent e) {
+ // Show the pop-up component
+ if (e.isPopupTrigger() && e.getClickCount() == 1) {
+ if (tree.getSelectionPaths() != null) {
+ showPopUp(e);
+ }
+ }
+ }
+
+ // Handles selection on the tree
+ public void mousePressed(MouseEvent e) {
+ JTree sourceTree = (JTree) e.getSource();
+ TreePath targetPath = sourceTree.getPathForLocation(e.getX(), e
+ .getY());
+ if (!e.isControlDown() && !e.isShiftDown()) {
+ sourceTree.setSelectionPath(targetPath);
+ } else {
+ sourceTree.addSelectionPath(targetPath);
+ }
+ // Show the pop-up component
+ if (e.isPopupTrigger() && e.getClickCount() == 1) {
+ showPopUp(e);
+ }
+ }
+
+ /**
+ * Shows this popup menu if the DOMViewer can be used to edti the
+ * document.
+ */
+ private void showPopUp(MouseEvent e) {
+ if (canEdit()) {
+ TreePath path = tree.getPathForLocation(e.getX(), e.getY());
+ // Don't show the pop up menu for the root element
+ if (path != null && path.getPathCount() > 1) {
+ treePopupMenu.show((Component) e.getSource(), e.getX(),
+ e.getY());
+ }
+ }
+ }
+ }
+
+ /**
+ * Handles tree pop-up menu action for adding new node.
+ */
+ protected class TreeNodeAdder implements ActionListener {
+
+ public void actionPerformed(ActionEvent e) {
+ // Prompt for the node name
+ NameEditorDialog nameEditorDialog = new NameEditorDialog(
+ DOMViewer.this);
+ nameEditorDialog.setLocationRelativeTo(DOMViewer.this);
+ int results = nameEditorDialog.showDialog();
+ if (results == NameEditorDialog.OK_OPTION) {
+ Element elementToAdd = document.createElementNS(
+ SVGConstants.SVG_NAMESPACE_URI, nameEditorDialog
+ .getResults());
+ if (rightPanel.getComponentCount() != 0) {
+ rightPanel.remove(0);
+ }
+ rightPanel.add(elementPanel);
+
+ // Pass the parent node as the selected one
+ TreePath[] treePaths = tree.getSelectionPaths();
+ if (treePaths != null) {
+ TreePath treePath = treePaths[treePaths.length - 1];
+ DefaultMutableTreeNode node = (DefaultMutableTreeNode) treePath
+ .getLastPathComponent();
+ NodeInfo nodeInfo = (NodeInfo) node.getUserObject();
+ // Enter the attributePanel 'add new element' mode
+ attributePanel.enterAddNewElementMode(elementToAdd,
+ nodeInfo.getNode());
+ }
+ }
+ }
+ }
+
+ /**
+ * Parser for the Element template.
+ */
+ protected class NodeTemplateParser implements ActionListener {
+
+ /**
+ * The string to parse.
+ */
+ protected String toParse;
+
+ /**
+ * The node type.
+ */
+ protected short nodeType;
+
+ /**
+ * Constructor.
+ *
+ * @param toParse
+ * The string to parse
+ */
+ public NodeTemplateParser(String toParse, short nodeType) {
+ this.toParse = toParse;
+ this.nodeType = nodeType;
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ Node nodeToAdd = null;
+ switch (nodeType) {
+ case Node.ELEMENT_NODE:
+ URL urlObj = null;
+ if (document instanceof SVGOMDocument) {
+ urlObj = ((SVGOMDocument) document).getURLObject();
+ }
+ String uri = (urlObj == null) ? "" : urlObj.toString();
+ Map prefixes = new HashMap();
+ prefixes.put(SVGConstants.XMLNS_PREFIX,
+ SVGConstants.SVG_NAMESPACE_URI);
+ prefixes.put(SVGConstants.XMLNS_PREFIX + ":"
+ + SVGConstants.XLINK_PREFIX,
+ SVGConstants.XLINK_NAMESPACE_URI);
+ SAXDocumentFactory df = new SAXDocumentFactory(document
+ .getImplementation(), XMLResourceDescriptor
+ .getXMLParserClassName());
+ DocumentFragment documentFragment = (DocumentFragment) DOMUtilities
+ .parseXML(toParse, document, uri, prefixes,
+ SVGConstants.SVG_SVG_TAG, df);
+ nodeToAdd = documentFragment.getFirstChild();
+ break;
+ case Node.TEXT_NODE:
+ nodeToAdd = document.createTextNode(toParse);
+ break;
+ case Node.COMMENT_NODE:
+ nodeToAdd = document.createComment(toParse);
+ break;
+ case Node.CDATA_SECTION_NODE:
+ nodeToAdd = document.createCDATASection(toParse);
+ }
+
+ // Append the new node to the parentNode
+ TreePath[] treePaths = tree.getSelectionPaths();
+ if (treePaths != null) {
+ TreePath treePath = treePaths[treePaths.length - 1];
+ DefaultMutableTreeNode node = (DefaultMutableTreeNode) treePath
+ .getLastPathComponent();
+ NodeInfo nodeInfo = (NodeInfo) node.getUserObject();
+
+ addChangesToHistory();
+
+ historyBrowserInterface.appendChild(nodeInfo.getNode(),
+ nodeToAdd);
+ }
+ }
+ }
+
+ /**
+ * Creates JMenu menu using {@link NodeTemplates}.
+ *
+ * @param name
+ * The name of the submenu
+ * @return The JMenu submenu
+ */
+ protected JMenu createTemplatesMenu(String name) {
+ NodeTemplates templates = new NodeTemplates();
+ JMenu submenu = new JMenu(name);
+
+ // Create submenus
+ HashMap menuMap = new HashMap();
+ ArrayList categoriesList = templates.getCategories();
+ int n = categoriesList.size();
+ for (int i = 0; i < n; i++) {
+ String category = categoriesList.get(i).toString();
+ JMenu currentMenu = new JMenu(category);
+ submenu.add(currentMenu);
+ // Add submenus to the map
+ menuMap.put(category, currentMenu);
+ }
+
+ // Sort the value list and then iterate through node templates
+ Collection values = templates.getNodeTemplatesMap().values();
+ List valuesAsList = Collections.list(Collections.enumeration(values));
+ Collections.sort(valuesAsList, new Comparator() {
+ public int compare(Object o1, Object o2) {
+ NodeTemplateDescriptor n1 = (NodeTemplateDescriptor) o1;
+ NodeTemplateDescriptor n2 = (NodeTemplateDescriptor) o2;
+ return n1.getName().compareTo(n2.getName());
+ }
+ });
+ Iterator iter = valuesAsList.iterator();
+ while (iter.hasNext()) {
+ NodeTemplateDescriptor desc = (NodeTemplateDescriptor) iter
+ .next();
+ String toParse = desc.getXmlValue();
+ short nodeType = desc.getType();
+ String nodeCategory = desc.getCategory();
+ JMenuItem currentItem = new JMenuItem(desc.getName());
+ currentItem.addActionListener(new NodeTemplateParser(toParse,
+ nodeType));
+ JMenu currentSubmenu = (JMenu)menuMap.get(nodeCategory);
+ currentSubmenu.add(currentItem);
+ }
+ return submenu;
+ }
+
+ /**
+ * Handles tree pop-up menu action for removing nodes.
+ */
+ protected class TreeNodeRemover implements ActionListener {
+
+ public void actionPerformed(ActionEvent e) {
+ addChangesToHistory();
+
+ AbstractCompoundCommand cmd = historyBrowserInterface
+ .createRemoveSelectedTreeNodesCommand(null);
+ TreePath[] treePaths = tree.getSelectionPaths();
+ for (int i = 0; treePaths != null && i < treePaths.length; i++) {
+ TreePath treePath = treePaths[i];
+ DefaultMutableTreeNode node = (DefaultMutableTreeNode) treePath
+ .getLastPathComponent();
+ NodeInfo nodeInfo = (NodeInfo) node.getUserObject();
+ if (DOMUtilities.isParentOf(nodeInfo.getNode(),
+ nodeInfo.getNode().getParentNode())) {
+ cmd.addCommand(historyBrowserInterface
+ .createRemoveChildCommand(nodeInfo.getNode()
+ .getParentNode(), nodeInfo.getNode()));
+ }
+ }
+ historyBrowserInterface.performCompoundUpdateCommand(cmd);
+ }
+ }
+
+ /**
* To listen to the tree selection.
*/
protected class DOMTreeSelectionListener
- implements TreeSelectionListener {
+ implements TreeSelectionListener {
+
/**
* Called when the selection changes.
*/
public void valueChanged(TreeSelectionEvent ev) {
+ // Manages the selection overlay
+ if (elementOverlayManager != null) {
+ handleElementSelection(ev);
+ }
+
DefaultMutableTreeNode mtn;
- mtn =
- (DefaultMutableTreeNode)tree.getLastSelectedPathComponent();
+ mtn = (DefaultMutableTreeNode)
+ tree.getLastSelectedPathComponent();
+
if (mtn == null) {
return;
}
@@ -434,25 +1781,29 @@
Object nodeInfo = mtn.getUserObject();
if (nodeInfo instanceof NodeInfo) {
- Node node = ((NodeInfo)nodeInfo).getNode();
+ Node node = ((NodeInfo) nodeInfo).getNode();
switch (node.getNodeType()) {
case Node.DOCUMENT_NODE:
documentInfo.setText
- (createDocumentText((Document)node));
+ (createDocumentText((Document) node));
rightPanel.add(documentInfoPanel);
break;
case Node.ELEMENT_NODE:
- attributesTable.setModel(new NodeAttributesModel(node));
propertiesTable.setModel(new NodeCSSValuesModel(node));
+ attributePanel.promptForChanges();
+ attributePanel.setPreviewElement((Element) node);
rightPanel.add(elementPanel);
break;
case Node.COMMENT_NODE:
case Node.TEXT_NODE:
case Node.CDATA_SECTION_NODE:
- characterData.setText(node.getNodeValue());
+ characterDataPanel.setNode(node);
+ characterDataPanel.getTextArea().setText
+ (node.getNodeValue());
rightPanel.add(characterDataPanel);
}
}
+
splitPane.revalidate();
splitPane.repaint();
}
@@ -473,31 +1824,61 @@
}
return result;
}
+
+ /**
+ * Processes element selection overlay.
+ *
+ * @param ev
+ * Tree selection event
+ */
+ protected void handleElementSelection(TreeSelectionEvent ev) {
+ TreePath[] paths = ev.getPaths();
+ for (int i = 0; i < paths.length; i++) {
+ TreePath path = paths[i];
+ DefaultMutableTreeNode mtn =
+ (DefaultMutableTreeNode) path.getLastPathComponent();
+ Object nodeInfo = mtn.getUserObject();
+ if (nodeInfo instanceof NodeInfo) {
+ Node node = ((NodeInfo) nodeInfo).getNode();
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ if (ev.isAddedPath(path)) {
+ elementOverlayManager.addElement
+ ((Element) node);
+ } else {
+ elementOverlayManager.removeElement
+ ((Element) node);
+ }
+ }
+ }
+ }
+ elementOverlayManager.repaint();
+ }
}
/**
* To render the tree nodes.
*/
protected class NodeRenderer extends DefaultTreeCellRenderer {
+
/**
* The icon used to represent elements.
*/
- ImageIcon elementIcon;
+ protected ImageIcon elementIcon;
/**
* The icon used to represent comments.
*/
- ImageIcon commentIcon;
+ protected ImageIcon commentIcon;
/**
* The icon used to represent processing instructions.
*/
- ImageIcon piIcon;
+ protected ImageIcon piIcon;
/**
* The icon used to represent text.
*/
- ImageIcon textIcon;
+ protected ImageIcon textIcon;
/**
* Creates a new NodeRenderer object.
@@ -560,71 +1941,10 @@
}
/**
- * To display the attributes of a DOM node attributes in a table.
- */
- protected class NodeAttributesModel extends AbstractTableModel {
- /**
- * The node.
- */
- protected Node node;
-
- /**
- * Creates a new NodeAttributesModel object.
- */
- public NodeAttributesModel(Node n) {
- node = n;
- }
-
- /**
- * Returns the name to give to a column.
- */
- public String getColumnName(int col) {
- if (col == 0) {
- return resources.getString("AttributesTable.column1");
- } else {
- return resources.getString("AttributesTable.column2");
- }
- }
-
- /**
- * Returns the number of columns in the table.
- */
- public int getColumnCount() {
- return 2;
- }
-
- /**
- * Returns the number of rows in the table.
- */
- public int getRowCount() {
- return node.getAttributes().getLength();
- }
-
- /**
- * Whether the given cell is editable.
- */
- public boolean isCellEditable(int row, int col) {
- return false;
- }
-
- /**
- * Returns the value of the given cell.
- */
- public Object getValueAt(int row, int col) {
- NamedNodeMap map = node.getAttributes();
- Node n = map.item(row);
- if (col == 0) {
- return n.getNodeName();
- } else {
- return n.getNodeValue();
- }
- }
- }
-
- /**
* To display the CSS properties of a DOM node in a table.
*/
protected class NodeCSSValuesModel extends AbstractTableModel {
+
/**
* The node.
*/
@@ -735,7 +2055,8 @@
*/
public String toString() {
if (node instanceof Element) {
- String id = ((Element)node).getAttribute("id");
+ Element e = (Element) node;
+ String id = e.getAttribute(SVGConstants.SVG_ID_ATTRIBUTE);
if (id.length() != 0) {
return node.getNodeName() + " \"" + id + "\"";
}
@@ -748,6 +2069,7 @@
* To store the node information for a shadow tree.
*/
protected static class ShadowNodeInfo extends NodeInfo {
+
/**
* Creates a new ShadowNodeInfo object.
*/
@@ -768,6 +2090,7 @@
* selected content.
*/
protected static class ContentNodeInfo extends NodeInfo {
+
/**
* Creates a new ContentNodeInfo object.
*/
Added: xmlgraphics/batik/trunk/sources/org/apache/batik/apps/svgbrowser/DOMViewerController.java
URL: http://svn.apache.org/viewvc/xmlgraphics/batik/trunk/sources/org/apache/batik/apps/svgbrowser/DOMViewerController.java?rev=594367&view=auto
==============================================================================
--- xmlgraphics/batik/trunk/sources/org/apache/batik/apps/svgbrowser/DOMViewerController.java (added)
+++ xmlgraphics/batik/trunk/sources/org/apache/batik/apps/svgbrowser/DOMViewerController.java Mon Nov 12 16:40:53 2007
@@ -0,0 +1,77 @@
+/*
+
+ 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 org.apache.batik.swing.gvt.Overlay;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+/**
+ * Provides the information needed for the DOMViewer to show and edit the
+ * document.
+ */
+public interface DOMViewerController {
+
+ /**
+ * Performs the document update.
+ *
+ * @param runnable
+ * The runnable that contains the update
+ */
+ void performUpdate(Runnable r);
+
+ /**
+ * Creates the ElementSelectionManager to manage the selection overlay on
+ * the canvas.
+ *
+ * @return ElementSelectionManager
+ */
+ ElementOverlayManager createSelectionManager();
+
+ /**
+ * Removes the given selection overlay from the canvas.
+ *
+ * @param selectionOverlay
+ * The given selection overlay
+ */
+ void removeSelectionOverlay(Overlay selectionOverlay);
+
+ /**
+ * Gets the document for the DOMViewer to show.
+ *
+ * @return the document
+ */
+ Document getDocument();
+
+ /**
+ * Selects the given node in the DOMViewer's document tree.
+ *
+ * @param node
+ * The node to select
+ */
+ void selectNode(Node node);
+
+ /**
+ * Checks whether the DOMViewer should be allowed to edit the document.
+ *
+ * @return True for non static documents, when UpdateManager is available
+ */
+ boolean canEdit();
+}