/*
* Copyright (C) 2011 Jason von Nieda <jason@vonnieda.org>
*
* This file is part of OpenPnP.
*
* OpenPnP 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.
*
* OpenPnP 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 OpenPnP. If not, see
* <http://www.gnu.org/licenses/>.
*
* For more information about OpenPnP visit http://openpnp.org
*/
package org.openpnp.gui;
import java.awt.BorderLayout;
import java.awt.Component;
import java.beans.IndexedPropertyChangeEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.prefs.Preferences;
import javax.swing.Action;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.JToolBar;
import javax.swing.JTree;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import org.openpnp.ConfigurationListener;
import org.openpnp.gui.support.Wizard;
import org.openpnp.gui.support.WizardContainer;
import org.openpnp.model.Configuration;
import org.openpnp.spi.PropertySheetHolder;
import org.openpnp.spi.PropertySheetHolder.PropertySheet;
import org.openpnp.util.BeanUtils;
@SuppressWarnings("serial")
public class MachineSetupPanel extends JPanel implements WizardContainer {
private static final String PREF_DIVIDER_POSITION = "MachineSetupPanel.dividerPosition";
private static final int PREF_DIVIDER_POSITION_DEF = -1;
private JTextField searchTextField;
private Preferences prefs = Preferences.userNodeForPackage(MachineSetupPanel.class);
private JTree tree;
private DefaultTreeModel treeModel;
private JTabbedPane tabbedPane;
private JToolBar toolBar;
public MachineSetupPanel() {
setLayout(new BorderLayout(0, 0));
JPanel panel = new JPanel();
add(panel, BorderLayout.NORTH);
panel.setLayout(new BorderLayout(0, 0));
toolBar = new JToolBar();
toolBar.setFloatable(false);
panel.add(toolBar, BorderLayout.CENTER);
JPanel panel_1 = new JPanel();
panel.add(panel_1, BorderLayout.EAST);
JLabel lblSearch = new JLabel("Search");
panel_1.add(lblSearch);
searchTextField = new JTextField();
searchTextField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void removeUpdate(DocumentEvent e) {
search();
}
@Override
public void insertUpdate(DocumentEvent e) {
search();
}
@Override
public void changedUpdate(DocumentEvent e) {
search();
}
});
panel_1.add(searchTextField);
searchTextField.setColumns(15);
final JSplitPane splitPane = new JSplitPane();
splitPane.setContinuousLayout(true);
splitPane
.setDividerLocation(prefs.getInt(PREF_DIVIDER_POSITION, PREF_DIVIDER_POSITION_DEF));
splitPane.addPropertyChangeListener("dividerLocation", new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
prefs.putInt(PREF_DIVIDER_POSITION, splitPane.getDividerLocation());
}
});
add(splitPane, BorderLayout.CENTER);
JScrollPane scrollPane = new JScrollPane();
splitPane.setLeftComponent(scrollPane);
tree = new JTree();
tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
tree.setCellRenderer(treeCellRenderer);
scrollPane.setViewportView(tree);
tabbedPane = new JTabbedPane(JTabbedPane.TOP);
splitPane.setRightComponent(tabbedPane);
tree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() {
@Override
public void valueChanged(TreeSelectionEvent e) {
tabbedPane.removeAll();
toolBar.removeAll();
TreePath path = tree.getSelectionPath();
if (path != null) {
List<Object> pathsReverse = Arrays.asList(path.getPath());
Collections.reverse(pathsReverse);
for (Object o : pathsReverse) {
PropertySheetHolderTreeNode node = (PropertySheetHolderTreeNode) o;
Action[] actions = node.obj.getPropertySheetHolderActions();
if (actions != null) {
for (Action action : actions) {
toolBar.add(action);
}
}
}
PropertySheetHolderTreeNode node =
(PropertySheetHolderTreeNode) path.getLastPathComponent();
if (node != null) {
PropertySheet[] propertySheets = node.obj.getPropertySheets();
if (propertySheets != null) {
for (PropertySheet propertySheet : propertySheets) {
String title = propertySheet.getPropertySheetTitle();
JPanel panel = propertySheet.getPropertySheetPanel();
if (title == null) {
title = "Configuration";
}
if (panel != null) {
tabbedPane.add(title, panel);
}
}
}
}
}
revalidate();
repaint();
}
});
Configuration.get().addListener(new ConfigurationListener() {
@Override
public void configurationLoaded(Configuration configuration) throws Exception {}
@Override
public void configurationComplete(Configuration configuration) throws Exception {
tree.setModel(treeModel = new DefaultTreeModel(
new PropertySheetHolderTreeNode(Configuration.get().getMachine(), null)));
for (int i = 0; i < tree.getRowCount(); i++) {
tree.expandRow(i);
}
}
});
}
private void search() {}
@Override
public void wizardCompleted(Wizard wizard) {}
@Override
public void wizardCancelled(Wizard wizard) {}
public class PropertySheetHolderTreeNode implements TreeNode {
private final PropertySheetHolder obj;
private final TreeNode parent;
private final ArrayList<PropertySheetHolderTreeNode> children = new ArrayList<>();
public PropertySheetHolderTreeNode(PropertySheetHolder obj, TreeNode parent) {
this.obj = obj;
this.parent = parent;
loadChildren();
/**
* If the object we're creating a node for supports property change then we add
* a listener. When we get an indexed changed we refresh our children and
* when we get a non-indexed change we refresh ourself.
*
* TODO: Note: Since we don't know which child got refreshed, we refresh them
* all and this causes the JTree to collapse all the other children. This sucks
* but there isn't a clean way to know which child changed without including the
* property name somewhere.
*/
BeanUtils.addPropertyChangeListener(obj, (PropertyChangeListener) (e) -> {
if (e instanceof IndexedPropertyChangeEvent) {
for (PropertySheetHolderTreeNode node : children) {
node.loadChildren();
treeModel.nodeStructureChanged(node);
}
}
else {
treeModel.nodeChanged(this);
}
});
}
private void loadChildren() {
this.children.clear();
PropertySheetHolder[] children = obj.getChildPropertySheetHolders();
if (children != null) {
for (PropertySheetHolder child : children) {
this.children.add(new PropertySheetHolderTreeNode(child, this));
}
}
}
public PropertySheetHolder getPropertySheetHolder() {
return obj;
}
@Override
public TreeNode getChildAt(int childIndex) {
return children.get(childIndex);
}
@Override
public int getChildCount() {
return children.size();
}
@Override
public TreeNode getParent() {
return parent;
}
@Override
public int getIndex(TreeNode node) {
return children.indexOf(node);
}
@Override
public boolean getAllowsChildren() {
return children.size() > 0;
}
@Override
public boolean isLeaf() {
return children.size() < 1;
}
@Override
public Enumeration children() {
return Collections.enumeration(children);
}
@Override
public String toString() {
return obj.getPropertySheetHolderTitle();
}
}
private TreeCellRenderer treeCellRenderer = new DefaultTreeCellRenderer() {
// http://stackoverflow.com/questions/20691946/set-icon-to-each-node-in-jtree
@Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected,
boolean expanded, boolean leaf, int row, boolean hasFocus) {
super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row,
hasFocus);
if (value instanceof PropertySheetHolderTreeNode) {
PropertySheetHolderTreeNode node = (PropertySheetHolderTreeNode) value;
PropertySheetHolder psh = node.getPropertySheetHolder();
setIcon(psh.getPropertySheetHolderIcon());
}
return this;
}
};
}