/*
* 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.Component;
import java.awt.Dimension;
import java.awt.GraphicsEnvironment;
import java.awt.HeadlessException;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.net.URL;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.WindowConstants;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreePath;
import net.sf.jame.core.swing.util.GUIFactory;
import net.sf.jame.core.swing.util.GUIUtil;
import net.sf.jame.core.tree.Node;
import net.sf.jame.core.tree.NodeEvent;
import net.sf.jame.core.tree.NodeSession;
import net.sf.jame.core.tree.Tree;
import net.sf.jame.core.tree.NodeListener;
import net.sf.jame.core.util.RenderContext;
/**
* @author Andrea Medeghini
*/
public class NavigatorFrame extends JFrame {
// private static final Logger logger = Logger.getLogger(NavigatorFrame.class);
private static final long serialVersionUID = 1L;
private static final String NAVIGATOR_FRAME_TITLE = "navigatorFrame.title";
private static final String NAVIGATOR_FRAME_WIDTH = "navigatorFrame.width";
private static final String NAVIGATOR_FRAME_HEIGHT = "navigatorFrame.height";
private static final String NAVIGATOR_FRAME_ICON = "navigatorFrame.icon";
private NavigationPanel navigationPanel;
/**
* @param config
* @param context
* @param session
* @throws HeadlessException
*/
public NavigatorFrame(final Tree twisterTree, final RenderContext context, final NodeSession session) throws HeadlessException {
navigationPanel = new NavigationPanel(new NavigatorViewContext(), twisterTree, context, session);
getContentPane().add(navigationPanel);
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
final int defaultWidth = Integer.parseInt(CoreSwingResources.getInstance().getString(NavigatorFrame.NAVIGATOR_FRAME_WIDTH));
final int defaultHeight = Integer.parseInt(CoreSwingResources.getInstance().getString(NavigatorFrame.NAVIGATOR_FRAME_HEIGHT));
final int width = Integer.getInteger(NavigatorFrame.NAVIGATOR_FRAME_WIDTH, defaultWidth);
final int height = Integer.getInteger(NavigatorFrame.NAVIGATOR_FRAME_HEIGHT, defaultHeight);
setTitle(CoreSwingResources.getInstance().getString(NavigatorFrame.NAVIGATOR_FRAME_TITLE));
final URL resource = NavigatorFrame.class.getClassLoader().getResource(CoreSwingResources.getInstance().getString(NavigatorFrame.NAVIGATOR_FRAME_ICON));
if (resource != null) {
setIconImage(getToolkit().createImage(resource));
}
this.setSize(new Dimension(width, height));
final Point p = GraphicsEnvironment.getLocalGraphicsEnvironment().getCenterPoint();
p.x -= getWidth() / 2;
p.y -= getHeight() / 2;
this.setLocation(p);
}
/**
* @see java.awt.Window#dispose()
*/
@Override
public void dispose() {
super.dispose();
if (navigationPanel != null) {
navigationPanel.dispose();
navigationPanel = null;
}
}
public class NavigationPanel extends JPanel {
private static final long serialVersionUID = 1L;
private final NavigatorPanel navigatorPanel;
private final NavigatorTree navigatorTree;
private final Tree twisterTree;
private final RenderContext context;
private final NavigatorPanelSelectionListener panelSelectionListener;
private final NavigatorTreeSelectionListener treeSelectionListener;
private final NavigatorTreeModelListener treeModelListener;
private final NavigatorTreeListener navigatorTreeListener;
private final NavigatorChangeListener navigatorChangeListener;
/**
* @param viewContext
* @param twisterTree
* @param context
* @param session
*/
public NavigationPanel(final ViewContext viewContext, final Tree twisterTree, final RenderContext context, final NodeSession session) {
this.context = context;
// this.session = session;
this.twisterTree = twisterTree;
setLayout(new BorderLayout());
navigatorTree = new NavigatorTree(twisterTree.getRootNode());
navigatorTree.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createMatteBorder(1, 1, 1, 1, Color.DARK_GRAY), BorderFactory.createEmptyBorder(2, 2, 2, 2)));
navigatorPanel = new NavigatorPanel(viewContext, twisterTree.getRootNode());
navigatorPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createMatteBorder(1, 1, 1, 1, Color.DARK_GRAY), BorderFactory.createEmptyBorder(2, 2, 2, 2)));
final JScrollPane scrollPane = new JScrollPane(navigatorTree);
scrollPane.setPreferredSize(new Dimension(300, 500));
navigatorPanel.setPreferredSize(new Dimension(400, 500));
scrollPane.setBorder(BorderFactory.createEmptyBorder());
final JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, scrollPane, navigatorPanel);
splitPane.setOneTouchExpandable(true);
splitPane.setDividerSize(10);
final Box buttonsPanel = Box.createHorizontalBox();
final JButton acceptButton = GUIFactory.createButton(new AcceptAction(), CoreSwingResources.getInstance().getString("tooltip.treeAccept"));
final JButton cancelButton = GUIFactory.createButton(new CancelAction(), CoreSwingResources.getInstance().getString("tooltip.treeCancel"));
final JCheckBox acceptImmediatlyCheckBox = GUIFactory.createCheckBox(CoreSwingResources.getInstance().getString("label.acceptImmediatly"), CoreSwingResources.getInstance().getString("tooltip.acceptImmediatly"));
buttonsPanel.add(acceptImmediatlyCheckBox);
buttonsPanel.add(Box.createHorizontalGlue());
buttonsPanel.add(cancelButton);
buttonsPanel.add(Box.createHorizontalStrut(8));
buttonsPanel.add(acceptButton);
buttonsPanel.setBorder(BorderFactory.createEmptyBorder(0, 8, 0, 8));
this.add(splitPane, BorderLayout.CENTER);
this.add(buttonsPanel, BorderLayout.SOUTH);
buttonsPanel.setBorder(BorderFactory.createEmptyBorder(8, 0, 0, 0));
setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
acceptImmediatlyCheckBox.setSelected(session.isAcceptImmediatly());
acceptButton.setEnabled(!session.isAcceptImmediatly());
cancelButton.setEnabled(!session.isAcceptImmediatly());
panelSelectionListener = new NavigatorPanelSelectionListener();
treeSelectionListener = new NavigatorTreeSelectionListener();
treeModelListener = new NavigatorTreeModelListener();
navigatorTreeListener = new NavigatorTreeListener(context, session);
navigatorChangeListener = new NavigatorChangeListener(acceptImmediatlyCheckBox, acceptButton, cancelButton, session);
navigatorPanel.addChangeListener(panelSelectionListener);
navigatorTree.getModel().addTreeModelListener(treeModelListener);
navigatorTree.getSelectionModel().addTreeSelectionListener(treeSelectionListener);
twisterTree.getRootNode().addNodeListener(navigatorTreeListener);
acceptImmediatlyCheckBox.addChangeListener(navigatorChangeListener);
}
public void dispose() {
navigatorPanel.removeChangeListener(panelSelectionListener);
navigatorTree.getModel().removeTreeModelListener(treeModelListener);
navigatorTree.getSelectionModel().removeTreeSelectionListener(treeSelectionListener);
twisterTree.getRootNode().removeTreeListener(navigatorTreeListener);
}
private final class NavigatorChangeListener implements ChangeListener {
private final JCheckBox acceptImmediatlyCheckBox;
private final JButton acceptButton;
private final JButton cancelButton;
private final NodeSession session;
private NavigatorChangeListener(final JCheckBox acceptImmediatlyCheckBox, final JButton acceptButton, final JButton cancelButton, final NodeSession session) {
this.acceptImmediatlyCheckBox = acceptImmediatlyCheckBox;
this.acceptButton = acceptButton;
this.cancelButton = cancelButton;
this.session = session;
}
public void stateChanged(final ChangeEvent e) {
GUIUtil.executeTask(new Runnable() {
public void run() {
final boolean isAcceptImmediatly = acceptImmediatlyCheckBox.isSelected();
acceptButton.setEnabled(!isAcceptImmediatly);
cancelButton.setEnabled(!isAcceptImmediatly);
session.setAcceptImmediatly(isAcceptImmediatly);
if (isAcceptImmediatly) {
doAccept();
}
}
}, true);
}
}
private final class NavigatorTreeListener implements NodeListener {
private final RenderContext context;
private final NodeSession session;
private NavigatorTreeListener(final RenderContext context, final NodeSession session) {
this.context = context;
this.session = session;
}
/**
* @see net.sf.jame.core.tree.NodeListener#nodeChanged(net.sf.jame.core.tree.NodeEvent)
*/
public void nodeChanged(final NodeEvent e) {
twisterTree.getRootNode().getSession().fireSessionChanged();
}
/**
* @see net.sf.jame.core.tree.NodeListener#nodeAdded(net.sf.jame.core.tree.NodeEvent)
*/
public void nodeAdded(final NodeEvent e) {
twisterTree.getRootNode().getSession().fireSessionChanged();
}
/**
* @see net.sf.jame.core.tree.NodeListener#nodeRemoved(net.sf.jame.core.tree.NodeEvent)
*/
public void nodeRemoved(final NodeEvent e) {
twisterTree.getRootNode().getSession().fireSessionChanged();
}
/**
* @see net.sf.jame.core.tree.NodeListener#nodeAccepted(net.sf.jame.core.tree.NodeEvent)
*/
public void nodeAccepted(final NodeEvent e) {
GUIUtil.executeTask(new Runnable() {
public void run() {
if (session.isAcceptImmediatly()) {
context.refresh();
}
}
}, true);
}
/**
* @see net.sf.jame.core.tree.NodeListener#nodeCancelled(net.sf.jame.core.tree.NodeEvent)
*/
public void nodeCancelled(final NodeEvent e) {
}
}
private class NavigatorPanelSelectionListener implements ChangeListener {
/**
* @see javax.swing.event.ChangeListener#stateChanged(javax.swing.event.ChangeEvent)
*/
public void stateChanged(final ChangeEvent e) {
GUIUtil.executeTask(new Runnable() {
public void run() {
if (navigatorPanel.getEditorNode() != null) {
navigatorTree.getSelectionModel().removeTreeSelectionListener(treeSelectionListener);
final TreePath path = navigatorTree.creareTreePath(navigatorPanel.getEditorNode().getNodePath());
navigatorTree.expandPath(path.getParentPath());
navigatorTree.setSelectionPath(path);
navigatorTree.getSelectionModel().addTreeSelectionListener(treeSelectionListener);
}
}
}, true);
}
}
private class NavigatorTreeSelectionListener implements TreeSelectionListener {
/**
* @see javax.swing.event.TreeSelectionListener#valueChanged(javax.swing.event.TreeSelectionEvent)
*/
public void valueChanged(final TreeSelectionEvent e) {
GUIUtil.executeTask(new Runnable() {
public void run() {
navigatorPanel.removeChangeListener(panelSelectionListener);
if (navigatorTree.getSelectionPath() != null) {
final Node node = (Node) ((DefaultMutableTreeNode) navigatorTree.getSelectionPath().getLastPathComponent()).getUserObject();
navigatorPanel.loadNode(node);
}
else {
navigatorPanel.loadNode(null);
}
navigatorPanel.addChangeListener(panelSelectionListener);
}
}, true);
}
}
private class NavigatorTreeModelListener implements TreeModelListener {
/**
* @see javax.swing.event.TreeModelListener#treeNodesChanged(javax.swing.event.TreeModelEvent)
*/
public void treeNodesChanged(final TreeModelEvent e) {
GUIUtil.executeTask(new Runnable() {
public void run() {
navigatorTree.expandPath(e.getTreePath());
}
}, true);
}
/**
* @see javax.swing.event.TreeModelListener#treeNodesInserted(javax.swing.event.TreeModelEvent)
*/
public void treeNodesInserted(final TreeModelEvent e) {
GUIUtil.executeTask(new Runnable() {
public void run() {
navigatorTree.expandPath(e.getTreePath());
}
}, true);
}
/**
* @see javax.swing.event.TreeModelListener#treeNodesRemoved(javax.swing.event.TreeModelEvent)
*/
public void treeNodesRemoved(final TreeModelEvent e) {
}
/**
* @see javax.swing.event.TreeModelListener#treeStructureChanged(javax.swing.event.TreeModelEvent)
*/
public void treeStructureChanged(final TreeModelEvent e) {
}
}
private class AcceptAction extends AbstractAction {
private static final long serialVersionUID = 1L;
/**
*
*/
public AcceptAction() {
super(CoreSwingResources.getInstance().getString("action.accept"));
}
/**
* @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*/
public void actionPerformed(final ActionEvent e) {
GUIUtil.executeTask(new Runnable() {
public void run() {
doAccept();
}
}, true);
}
}
private class CancelAction extends AbstractAction {
private static final long serialVersionUID = 1L;
/**
*
*/
public CancelAction() {
super(CoreSwingResources.getInstance().getString("action.cancel"));
}
/**
* @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*/
public void actionPerformed(final ActionEvent e) {
GUIUtil.executeTask(new Runnable() {
public void run() {
doCancel();
}
}, true);
}
}
/**
* @param renderer
*/
public void setCellRenderer(final TreeCellRenderer renderer) {
navigatorTree.setCellRenderer(renderer);
}
/**
* @param session
*/
public void doAccept() {
try {
context.acquire();
context.stopRenderers();
twisterTree.getRootNode().getContext().updateTimestamp();
twisterTree.getRootNode().getSession().fireSessionAccepted();
twisterTree.getRootNode().accept();
context.startRenderers();
context.release();
context.refresh();
}
catch (InterruptedException x) {
Thread.currentThread().interrupt();
}
}
/**
*
*/
public void doCancel() {
twisterTree.getRootNode().cancel();
twisterTree.getRootNode().getSession().fireSessionCancelled();
}
}
private class NavigatorViewContext implements ViewContext {
public void removeComponent(final Component c) {
}
public void resize() {
validate();
NavigatorFrame.this.repaint();
}
public void resize(final int amount) {
validate();
NavigatorFrame.this.repaint();
}
public void setComponent(final Component c) {
validate();
NavigatorFrame.this.repaint();
}
public void restoreComponent(final Component c) {
validate();
NavigatorFrame.this.repaint();
}
}
}