/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2012 Neil C Smith.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 only, as
* published by the Free Software Foundation.
*
* This code 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
* version 3 for more details.
*
* You should have received a copy of the GNU General Public License version 3
* along with this work; if not, see http://www.gnu.org/licenses/
*
*
* Please visit http://neilcsmith.net if you need additional information or
* have any questions.
*/
package net.neilcsmith.praxis.live.pxr.editors;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyVetoException;
import java.beans.VetoableChangeListener;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.Action;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JRootPane;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.tree.TreeSelectionModel;
import net.neilcsmith.praxis.core.ComponentAddress;
import net.neilcsmith.praxis.core.ControlAddress;
import net.neilcsmith.praxis.core.interfaces.ComponentInterface;
import net.neilcsmith.praxis.core.interfaces.ContainerInterface;
import net.neilcsmith.praxis.core.types.PString;
import net.neilcsmith.praxis.live.model.ComponentProxy;
import net.neilcsmith.praxis.live.model.RootProxy;
import net.neilcsmith.praxis.live.pxr.api.RootRegistry;
import org.openide.explorer.ExplorerManager;
import org.openide.explorer.propertysheet.PropertyEnv;
import org.openide.explorer.view.BeanTreeView;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children;
import org.openide.nodes.FilterNode;
import org.openide.nodes.Node;
import org.openide.nodes.NodeNotFoundException;
import org.openide.nodes.NodeOp;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;
/**
*
* @author Neil C Smith (http://neilcsmith.net)
*/
class ControlAddressCustomEditor extends javax.swing.JPanel
implements ExplorerManager.Provider {
private final static Set<String> SYS_CTRLS = new HashSet<String>();
static {
SYS_CTRLS.add(ComponentInterface.INFO);
SYS_CTRLS.add(ContainerInterface.ADD_CHILD);
SYS_CTRLS.add(ContainerInterface.CHILDREN);
SYS_CTRLS.add(ContainerInterface.CONNECT);
SYS_CTRLS.add(ContainerInterface.CONNECTIONS);
SYS_CTRLS.add(ContainerInterface.DISCONNECT);
SYS_CTRLS.add(ContainerInterface.REMOVE_CHILD);
}
private static final Logger LOG = Logger.getLogger(ControlAddressCustomEditor.class.getName());
private ControlAddress current;
private ComponentProxy exploredComponent;
private final ExplorerManager em;
private final PropertyEnv env;
private final ControlAddressEditor editor;
private final Listener listener;
private final DefaultListModel listModel;
ControlAddressCustomEditor(ControlAddressEditor editor, ControlAddress current, PropertyEnv env) {
initComponents();
if (current != null) {
addressField.setText(current.toString());
}
this.current = current;
this.env = env;
this.editor = editor;
// env.setState(PropertyEnv.STATE_NEEDS_VALIDATION);
em = new ExplorerManager();
listener = new Listener();
listModel = new DefaultListModel();
controlList.setModel(listModel);
em.setRootContext(new AbstractNode(new RootsChildren(), Lookup.EMPTY));
if (current != null) {
Node node = findNodeFromAddress(current);
if (node != null) {
try {
em.setSelectedNodes(new Node[]{node});
refreshComponent(node.getLookup().lookup(ComponentProxy.class));
controlList.setSelectedValue(current.getID(), true);
} catch (PropertyVetoException ex) {
Exceptions.printStackTrace(ex);
}
}
}
env.addVetoableChangeListener(listener);
addressField.getDocument().addDocumentListener(listener);
em.addPropertyChangeListener(listener);
controlList.addListSelectionListener(listener);
controlList.addMouseListener(listener);
initNodeBrowser();
}
private void initNodeBrowser() {
BeanTreeView view = (BeanTreeView) nodeBrowser;
view.setRootVisible(false);
view.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
view.setPopupAllowed(false);
// view.setQuickSearchAllowed(false);
}
private Node findNodeFromAddress(ControlAddress address) {
LOG.log(Level.FINEST, "Searching for node that matches {0}", address);
ComponentAddress cmp = address.getComponentAddress();
String[] parts = new String[cmp.getDepth()];
for (int i = 0; i < parts.length; i++) {
parts[i] = cmp.getComponentID(i);
}
try {
return NodeOp.findPath(em.getRootContext(), parts);
} catch (NodeNotFoundException ex) {
LOG.log(Level.FINEST, "Node not found", ex);
return null;
}
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
addressField = new javax.swing.JTextField();
nodeBrowser = new org.openide.explorer.view.BeanTreeView();
javax.swing.JScrollPane listScroll = new javax.swing.JScrollPane();
controlList = new javax.swing.JList();
toolBar = new javax.swing.JToolBar();
clearButton = new javax.swing.JButton();
systemToggleButton = new javax.swing.JToggleButton();
addressField.setText(org.openide.util.NbBundle.getMessage(ControlAddressCustomEditor.class, "ControlAddressCustomEditor.addressField.text")); // NOI18N
nodeBrowser.setPreferredSize(new java.awt.Dimension(150, 200));
controlList.setModel(new javax.swing.AbstractListModel() {
String[] strings = { "Item 1", "Item 2", "Item 3", "Item 4", "Item 5" };
public int getSize() { return strings.length; }
public Object getElementAt(int i) { return strings[i]; }
});
listScroll.setViewportView(controlList);
toolBar.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1));
toolBar.setFloatable(false);
toolBar.setRollover(true);
clearButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/net/neilcsmith/praxis/live/pxr/resources/clear.png"))); // NOI18N
clearButton.setText(org.openide.util.NbBundle.getMessage(ControlAddressCustomEditor.class, "ControlAddressCustomEditor.clearButton.text")); // NOI18N
clearButton.setFocusable(false);
clearButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
clearButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
clearButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
clearButtonActionPerformed(evt);
}
});
toolBar.add(clearButton);
systemToggleButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/net/neilcsmith/praxis/live/pxr/resources/system.png"))); // NOI18N
systemToggleButton.setText(org.openide.util.NbBundle.getMessage(ControlAddressCustomEditor.class, "ControlAddressCustomEditor.systemToggleButton.text")); // NOI18N
systemToggleButton.setFocusable(false);
systemToggleButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
systemToggleButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
systemToggleButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
systemToggleButtonActionPerformed(evt);
}
});
toolBar.add(systemToggleButton);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(nodeBrowser, javax.swing.GroupLayout.DEFAULT_SIZE, 233, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(listScroll, javax.swing.GroupLayout.DEFAULT_SIZE, 137, Short.MAX_VALUE)
.addContainerGap())
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(addressField)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(toolBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap())))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(addressField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(toolBar, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addComponent(nodeBrowser, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(listScroll, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE))
.addContainerGap())
);
}// </editor-fold>//GEN-END:initComponents
private void clearButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_clearButtonActionPerformed
addressField.setText("");
}//GEN-LAST:event_clearButtonActionPerformed
private void systemToggleButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_systemToggleButtonActionPerformed
refreshList();
}//GEN-LAST:event_systemToggleButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JTextField addressField;
private javax.swing.JButton clearButton;
private javax.swing.JList controlList;
private javax.swing.JScrollPane nodeBrowser;
private javax.swing.JToggleButton systemToggleButton;
private javax.swing.JToolBar toolBar;
// End of variables declaration//GEN-END:variables
@Override
public ExplorerManager getExplorerManager() {
return em;
}
private void refreshComponent(ComponentProxy cmp) {
if (this.exploredComponent == cmp) {
return;
}
this.exploredComponent = cmp;
refreshList();
}
private void refreshList() {
ComponentProxy cmp = exploredComponent;
listModel.clear();
if (cmp != null) {
boolean sys = systemToggleButton.isSelected();
for (String id : cmp.getInfo().getControls()) {
if (sys || !SYS_CTRLS.contains(id)) {
listModel.addElement(id);
}
}
}
}
private class Listener extends MouseAdapter implements DocumentListener, VetoableChangeListener,
PropertyChangeListener, ListSelectionListener {
private boolean ignore;
@Override
public void insertUpdate(DocumentEvent e) {
update(e);
}
@Override
public void removeUpdate(DocumentEvent e) {
update(e);
}
@Override
public void changedUpdate(DocumentEvent e) {
update(e);
}
private void update(DocumentEvent e) {
if (ignore) {
return;
}
String txt = addressField.getText();
if (txt.isEmpty()) {
current = null;
try {
em.setSelectedNodes(new Node[]{});
} catch (PropertyVetoException ex) {}
env.setState(PropertyEnv.STATE_NEEDS_VALIDATION);
} else {
try {
current = ControlAddress.valueOf(addressField.getText());
env.setState(PropertyEnv.STATE_NEEDS_VALIDATION);
} catch (Exception ex) {
env.setState(PropertyEnv.STATE_INVALID);
}
}
}
// called when editing completed
@Override
public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException {
if (evt.getNewValue().equals(PropertyEnv.STATE_VALID)) {
if (current == null) {
editor.setValue(PString.EMPTY);
} else {
editor.setValue(current);
}
}
}
// called from node browser
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (ExplorerManager.PROP_SELECTED_NODES.equals(evt.getPropertyName())) {
LOG.finest("Node selection changed");
Node[] nodes = em.getSelectedNodes();
ComponentProxy cmp = null;
if (nodes.length == 1) {
cmp = nodes[0].getLookup().lookup(ComponentProxy.class);
}
refreshComponent(cmp);
}
}
// called from control list
@Override
public void valueChanged(ListSelectionEvent e) {
Object sel = controlList.getSelectedValue();
if (sel == null) {
return;
}
String id = sel.toString();
if (exploredComponent != null) {
current = ControlAddress.create(exploredComponent.getAddress(), id);
ignore = true;
addressField.setText(current.toString());
env.setState(PropertyEnv.STATE_NEEDS_VALIDATION);
ignore = false;
}
}
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
// LOG.finest("Double click on list");
int index = controlList.locationToIndex(e.getPoint());
if (index < 0) {
// LOG.finest("No item found at location");
return;
}
Object id = controlList.getModel().getElementAt(index);
// LOG.finest("ID found in list : " + id);
JRootPane rpane = SwingUtilities.getRootPane(controlList);
// LOG.finest(rpane == null ? "No rootpane found" : "Rootpane found");
JButton button = rpane.getDefaultButton();
// LOG.finest(button == null ? "No default button found" : "Default button found");
if (id instanceof String && button != null) {
// LOG.finest("Creating current address");
current = ControlAddress.create(exploredComponent.getAddress(), id.toString());
// LOG.finest("Clicking default button");
button.doClick();
e.consume();
}
}
}
}
private static class RootsChildren extends Children.Keys<RootProxy> {
private RootsChildren() {
setKeys(RootRegistry.getDefault().getRoots());
}
@Override
protected Node[] createNodes(RootProxy key) {
return new Node[]{new ComponentNode(key.getNodeDelegate())};
}
}
private static class ComponentNode extends FilterNode {
private ComponentNode(Node node) {
super(node,
node.getChildren() == Children.LEAF
? Children.LEAF : new ComponentChildren(node));
}
@Override
public Action[] getActions(boolean context) {
return new Action[0];
}
@Override
public Action getPreferredAction() {
return null;
}
}
private static class ComponentChildren extends FilterNode.Children {
private ComponentChildren(Node node) {
super(node);
}
@Override
protected Node copyNode(Node node) {
return new ComponentNode(node);
}
}
}