/*
* JAME 6.2.1
* http://jame.sourceforge.net
*
* Copyright 2001, 2016 Andrea Medeghini
*
* This file is part of JAME.
*
* JAME is an application for creating fractals and other graphics artifacts.
*
* JAME is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* JAME is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with JAME. If not, see <http://www.gnu.org/licenses/>.
*
*/
package net.sf.jame.core.swing;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.util.ArrayList;
import java.util.EventObject;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import net.sf.jame.core.extension.Extension;
import net.sf.jame.core.extension.ExtensionNotFoundException;
import net.sf.jame.core.swing.editor.extension.EditorExtensionRuntime;
import net.sf.jame.core.tree.Node;
import net.sf.jame.core.tree.NodeEvent;
import net.sf.jame.core.tree.NodeListener;
import net.sf.jame.core.tree.RootNode;
/**
* @author Andrea Medeghini
*/
public class NavigatorPanel extends JPanel {
private static final Logger logger = Logger.getLogger(NavigatorPanel.class.getName());
private static final long serialVersionUID = 1L;
private final JPanel iconsPanel = new JPanel(new NavigatorLayout(130, 80));
private final JPanel editorPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
private final List<ChangeListener> listeners = new ArrayList<ChangeListener>(1);
private final ChangeEvent event = new ChangeEvent(this);
private final ViewContext viewContext;
private NodeEditorComponent editor;
// private RootNode rootNode;
private Node editorNode;
private Node viewNode;
private final RootNode rootNode;
private final NavigatorTreeListener navigatorTreeListener;
/**
* @param viewContext
* @param rootNode
*/
public NavigatorPanel(final ViewContext viewContext, final RootNode rootNode) {
this.viewContext = viewContext;
this.rootNode = rootNode;
setLayout(new BorderLayout());
// this.rootNode = rootNode;
// iconsPanel.setPreferredSize(new Dimension(200, 120));
// iconsPanel.setMinimumSize(new Dimension(200, 120));
// iconsPanel.setMinimumSize(new Dimension(200, 120));
iconsPanel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
final JScrollPane scrollPane = new JScrollPane(iconsPanel);
scrollPane.setBorder(BorderFactory.createEmptyBorder());
final Box editorContainer = Box.createVerticalBox();
editorContainer.add(scrollPane);
editorContainer.add(Box.createGlue());
editorContainer.add(editorPanel);
editorContainer.add(Box.createGlue());
add(editorContainer, BorderLayout.CENTER);
loadNode(rootNode);
navigatorTreeListener = new NavigatorTreeListener();
rootNode.addNodeListener(navigatorTreeListener);
}
public void dispose() {
rootNode.removeTreeListener(navigatorTreeListener);
listeners.clear();
if (editor != null) {
editor.dispose();
editor = null;
}
}
/**
* @param listener
* @see javax.swing.SingleSelectionModel#addChangeListener(javax.swing.event.ChangeListener)
*/
public void addChangeListener(final ChangeListener listener) {
listeners.add(listener);
}
/**
* @param listener
* @see javax.swing.SingleSelectionModel#removeChangeListener(javax.swing.event.ChangeListener)
*/
public void removeChangeListener(final ChangeListener listener) {
listeners.remove(listener);
}
/**
*
*/
protected void fireChangeEvent() {
for (final ChangeListener listener : listeners) {
listener.stateChanged(event);
}
}
/**
* @return
*/
public Node getViewNode() {
return viewNode;
}
/**
* @return
*/
public Node getEditorNode() {
return editorNode;
}
public void reloadNode(final Node node) {
iconsPanel.removeAll();
if (node != null) {
if (node.getChildNodeCount() == 0) {
loadIcons(node.getParentNode());
}
else {
loadIcons(node);
}
}
if (viewContext != null) {
viewContext.resize();
}
}
public void loadNode(final Node node) {
if (viewNode != node) {
iconsPanel.removeAll();
viewNode = node;
if (node != null) {
if (node.getChildNodeCount() == 0) {
loadIcons(node.getParentNode());
viewNode = node.getParentNode();
}
else {
loadIcons(node);
}
}
updateEditor(node);
}
else if (editorNode != node) {
updateEditor(node);
}
else {
if (editor != null) {
editor.reloadValue();
}
}
if (viewContext != null) {
viewContext.resize();
}
}
private void loadIcons(final Node node) {
// if (node != rootNode) {
// NavigatorIcon icon = new NavigatorIcon("Root", rootNode);
// icon.addIconListener(new NavigatorIconListener() {
// public void actionPerformed(NavigatorIconEvent e) {
// if (e.getEventId() == NavigatorIconEvent.OPEN_EVENT) {
// Node childNode = ((NavigatorIcon) e.getSource()).getNode();
// if (e.getClickCount() == 2 && childNode.getChildNodeCount() > 0) {
// loadNode(childNode);
// }
// else {
// updateEditor(childNode);
// revalidate();
// repaint();
// }
// }
// }
// });
// iconsPanel.add(icon);
// }
if (node != null) {
// if (node.getParentNode() != null) {
// final NavigatorIcon icon = new NavigatorIcon(node.getParentNode());
// icon.addIconListener(new NavigatorIconListener() {
// public void actionPerformed(final NavigatorIconEvent e) {
// if (e.getEventId() == NavigatorIconEvent.OPEN_EVENT) {
// final Node childNode = ((NavigatorIcon) e.getSource()).getNode();
// if ((e.getClickCount() == 2) && (childNode.getChildNodeCount() > 0)) {
// loadNode(childNode);
// }
// else {
// updateEditor(childNode);
// }
// }
// }
// });
// iconsPanel.add(icon);
// }
for (int i = 0; i < node.getChildNodeCount(); i++) {
final NavigatorIcon icon = new NavigatorIcon(node.getChildNode(i));
icon.addIconListener(new NavigatorIconListener() {
public void actionPerformed(final NavigatorIconEvent e) {
if (e.getEventId() == NavigatorIconEvent.OPEN_EVENT) {
final Node childNode = ((NavigatorIcon) e.getSource()).getNode();
if ((e.getClickCount() == 2) && (childNode.getChildNodeCount() > 0)) {
loadNode(childNode);
}
else {
updateEditor(childNode);
if (viewContext != null) {
viewContext.resize();
}
}
}
}
});
iconsPanel.add(icon);
}
// NavigatorPanel.this.revalidate();
// NavigatorPanel.this.repaint();
// if (viewContext != null) {
// viewContext.resize();
// }
}
}
private void updateEditor(final Node node) {
editorNode = node;
if (editor != null) {
editorPanel.remove(editor.getComponent());
editor.dispose();
editor = null;
}
if (node != null) {
if (node.getNodeEditor() != null) {
try {
final Extension<EditorExtensionRuntime> extension = CoreSwingRegistry.getInstance().getEditorExtension(node.getNodeId());
final EditorExtensionRuntime runtime = extension.createExtensionRuntime();
if (NavigatorPanel.logger.isLoggable(Level.INFO)) {
NavigatorPanel.logger.info("Found editor for node = " + node.getNodeId());
}
editor = runtime.createEditor(node.getNodeEditor());
}
catch (final ExtensionNotFoundException x) {
if (NavigatorPanel.logger.isLoggable(Level.INFO)) {
NavigatorPanel.logger.info("Can't find editor for node = " + node.getNodeId());
}
}
catch (final Exception x) {
x.printStackTrace();
}
if (editor == null) {
try {
final Extension<EditorExtensionRuntime> extension = CoreSwingRegistry.getInstance().getEditorExtension(node.getNodeClass());
final EditorExtensionRuntime runtime = extension.createExtensionRuntime();
NavigatorPanel.logger.info("Found editor for node class = " + node.getNodeClass());
editor = runtime.createEditor(node.getNodeEditor());
}
catch (final ExtensionNotFoundException x) {
NavigatorPanel.logger.info("Can't find editor for node class = " + node.getNodeClass());
}
catch (final Exception x) {
x.printStackTrace();
}
}
}
else {
if (NavigatorPanel.logger.isLoggable(Level.INFO)) {
NavigatorPanel.logger.info("Undefined editor for node = " + node.getNodeId());
}
}
}
else {
if (NavigatorPanel.logger.isLoggable(Level.INFO)) {
NavigatorPanel.logger.info("Remove editor");
}
}
if (editor != null) {
editorPanel.add(editor.getComponent());
}
// NavigatorPanel.this.revalidate();
// NavigatorPanel.this.repaint();
fireChangeEvent();
}
private final class NavigatorTreeListener implements NodeListener {
/**
* @see net.sf.jame.core.tree.NodeListener#nodeChanged(net.sf.jame.core.tree.NodeEvent)
*/
public void nodeChanged(final NodeEvent e) {
if ((e.getNode() == viewNode) || ((viewNode != null) && viewNode.isChildNode(e.getNode())) || ((viewNode != null) && (e.getNode() == viewNode.getParentNode()))) {
if (!e.getNode().isHighFrequency()) {
reloadNode(viewNode);
if ((editor != null) && (editorNode == e.getNode())) {
// updateEditor(editorNode);
editor.reloadValue();
}
}
}
}
/**
* @see net.sf.jame.core.tree.NodeListener#nodeAdded(net.sf.jame.core.tree.NodeEvent)
*/
public void nodeAdded(final NodeEvent e) {
if (((viewNode != null) && viewNode.isChildNode(e.getNode())) || ((viewNode != null) && (e.getNode() == viewNode.getParentNode()))) {
reloadNode(viewNode);
if ((editor != null) && (editorNode == e.getNode())) {
// updateEditor(editorNode);
// editor.reloadValue();
}
}
}
/**
* @see net.sf.jame.core.tree.NodeListener#nodeRemoved(net.sf.jame.core.tree.NodeEvent)
*/
public void nodeRemoved(final NodeEvent e) {
if (e.getNode() == viewNode) {
reloadNode(null);
updateEditor(null);
}
else if (((viewNode != null) && viewNode.isChildNode(e.getNode())) || ((viewNode != null) && (e.getNode() == viewNode.getParentNode()))) {
reloadNode(viewNode);
if ((editor != null) && (editorNode == e.getNode())) {
// updateEditor(null);
// editor.reloadValue();
}
}
}
/**
* @see net.sf.jame.core.tree.NodeListener#nodeAccepted(net.sf.jame.core.tree.NodeEvent)
*/
public void nodeAccepted(final NodeEvent e) {
}
/**
* @see net.sf.jame.core.tree.NodeListener#nodeCancelled(net.sf.jame.core.tree.NodeEvent)
*/
public void nodeCancelled(final NodeEvent e) {
}
}
private class NavigatorIcon extends JComponent {
private static final long serialVersionUID = 1L;
private final List<NavigatorIconListener> listeners = new ArrayList<NavigatorIconListener>();
private ImageIcon nodeIcon;
private ImageIcon leafIcon;
private ImageIcon icon;
private final Node node;
private int textH1 = 0;
private int textW1 = 0;
private int textH2 = 0;
private int textW2 = 0;
// private int textH = 0;
// private int textW = 0;
private String label1;
private String label2;
private boolean over;
private final Font font1 = new Font("arial", Font.PLAIN, 10);
private final Font font2 = new Font("arial", Font.PLAIN, 9);
/**
* @param node
*/
public NavigatorIcon(final Node node) {
try {
nodeIcon = new ImageIcon(ImageIO.read(NavigatorPanel.class.getResourceAsStream("/icons/treeNode-icon.png")));
}
catch (final IOException e) {
e.printStackTrace();
}
try {
leafIcon = new ImageIcon(ImageIO.read(NavigatorPanel.class.getResourceAsStream("/icons/treeLeaf-icon.png")));
}
catch (final IOException e) {
e.printStackTrace();
}
this.node = node;
setOpaque(false);
if (node.isMutable()) {
icon = nodeIcon;
label1 = node.getNodeLabel();
label2 = (node.hasPendingCommands() ? "*" : "") + node.getValueAsString();
if (label2.length() > 30) {
label2 = label2.substring(0, 30) + "...";
}
}
else if (node.getChildNodeCount() > 0) {
icon = nodeIcon;
label1 = node.getNodeLabel();
label2 = (node.hasPendingCommands() ? "*" : "") + node.getChildNodeCount() + " " + CoreSwingResources.getInstance().getString("label.elements");
}
else {
icon = leafIcon;
label1 = node.getNodeLabel();
label2 = (node.hasPendingCommands() ? "*" : "") + node.getValueAsString();
if (label2.length() > 30) {
label2 = label2.substring(0, 30) + "...";
}
}
label1 = label1 != null ? label1 : "";
label2 = label2 != null ? label2 : "";
final FontMetrics fm1 = getFontMetrics(font1);
final FontMetrics fm2 = getFontMetrics(font2);
textH1 = fm1.getHeight();
textW1 = SwingUtilities.computeStringWidth(fm1, label1);
textH2 = fm2.getHeight();
textW2 = SwingUtilities.computeStringWidth(fm2, label2);
// textW = Math.max(textW1, textW2);
// textH = Math.max(textH1, textH2);
// Dimension size = null;
// if (icon == null) {
// size = new Dimension(textW + 8, textH1 + textH2 + 16);
// }
// else {
// size = new Dimension(Math.max(icon.getIconWidth(), textW) + 8, icon.getIconHeight() + textH1 + textH2 + 16);
// }
// setPreferredSize(size);
// setMinimumSize(size);
// setMaximumSize(size);
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(final MouseEvent e) {
fireIconEvent(new NavigatorIconEvent(NavigatorIcon.this, NavigatorIconEvent.OPEN_EVENT, e.getClickCount()));
}
@Override
public void mouseEntered(final MouseEvent e) {
over = true;
repaint();
}
@Override
public void mouseExited(final MouseEvent e) {
over = false;
repaint();
}
});
}
/**
* @see javax.swing.JComponent#paintComponent(java.awt.Graphics)
*/
@Override
protected void paintComponent(final Graphics g) {
paintComponent((Graphics2D) g);
}
private void paintComponent(final Graphics2D g) {
g.setPaintMode();
g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
final Rectangle r = SwingUtilities.calculateInnerArea(this, null);
if (icon != null) {
g.drawImage(icon.getImage(), (r.width - icon.getIconWidth()) / 2, 0, null);
if (over) {
g.setColor(Color.GRAY);
g.fillRoundRect((r.width - textW1) / 2 - 4, icon.getIconHeight() + 4, textW1 + 8, textH1 + /* textH2 */+6, 16, 16);
g.setColor(Color.WHITE);
g.setFont(font1);
g.drawString(label1, (r.width - textW1) / 2, icon.getIconHeight() + textH1 + 4);
// g.setFont(font2);
// g.drawString(label2, (r.width - textW2) / 2, icon.getIconHeight() + textH1 + textH2 + 4);
}
else {
g.setColor(Color.BLACK);
g.setFont(font1);
g.drawString(label1, (r.width - textW1) / 2, icon.getIconHeight() + textH1 + 4);
// g.setFont(font2);
// g.drawString(label2, (r.width - textW2) / 2, icon.getIconHeight() + textH1 + textH2 + 4);
}
g.setColor(Color.DARK_GRAY);
g.setFont(font2);
g.drawString(label2, (r.width - textW2) / 2, icon.getIconHeight() + textH1 + textH2 + 7);
}
else {
if (over) {
g.setColor(Color.GRAY);
g.fillRoundRect((r.width - textW1) / 2 - 4, 4, textW1 + 8, textH1 + /* textH2 */+6, 16, 16);
g.setColor(Color.WHITE);
g.setFont(font1);
g.drawString(label1, (r.width - textW1) / 2, textH1 + 4);
// g.setFont(font2);
// g.drawString(label2, (r.width - textW2) / 2, textH1 + textH2 + 4);
}
else {
g.setColor(Color.BLACK);
g.setFont(font1);
g.drawString(label1, (r.width - textW1) / 2, textH1 + 4);
// g.setFont(font2);
// g.drawString(label2, (r.width - textW2) / 2, textH1 + textH2 + 4);
}
g.setColor(Color.DARK_GRAY);
g.setFont(font2);
g.drawString(label2, (r.width - textW2) / 2, textH1 + textH2 + 7);
}
}
/**
* @return the node
*/
public Node getNode() {
return node;
}
/**
* @param listener
*/
public void addIconListener(final NavigatorIconListener listener) {
listeners.add(listener);
}
/**
* @param listener
*/
public void removeIconListener(final NavigatorIconListener listener) {
listeners.remove(listener);
}
/**
* @param e
*/
protected void fireIconEvent(final NavigatorIconEvent e) {
for (final NavigatorIconListener listener : listeners) {
listener.actionPerformed(e);
}
}
}
private class NavigatorIconEvent extends EventObject {
private static final long serialVersionUID = 1L;
public static final int OPEN_EVENT = 0;
private final int eventId;
private final int clickCount;
/**
* @param icon
* @param eventId
* @param clickCount
*/
public NavigatorIconEvent(final NavigatorIcon icon, final int eventId, final int clickCount) {
super(icon);
this.eventId = eventId;
this.clickCount = clickCount;
}
/**
* @return the clickCount
*/
public int getClickCount() {
return clickCount;
}
/**
* @return the eventId
*/
public int getEventId() {
return eventId;
}
/**
* @see java.util.EventObject#toString()
*/
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append(((NavigatorIcon) getSource()).getNode());
builder.append(", ");
builder.append(eventId);
builder.append(", ");
builder.append(clickCount);
return builder.toString();
}
}
private interface NavigatorIconListener {
/**
* @param e
*/
public void actionPerformed(NavigatorIconEvent e);
}
}