/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2013 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.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyVetoException;
import java.beans.VetoableChangeListener;
import java.io.File;
import java.net.URI;
import java.util.logging.Logger;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import net.neilcsmith.praxis.core.types.PResource;
import net.neilcsmith.praxis.core.types.PString;
import org.openide.explorer.ExplorerManager;
import org.openide.explorer.propertysheet.PropertyEnv;
import org.openide.explorer.view.ContextTreeView;
import org.openide.explorer.view.ListView;
import org.openide.filesystems.FileChooserBuilder;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.loaders.DataObject;
import org.openide.loaders.DataObjectNotFoundException;
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.Utilities;
/**
*
* @author Neil C Smith (http://neilcsmith.net)
*/
class ResourceCustomEditor extends javax.swing.JPanel
implements ExplorerManager.Provider {
private final static Logger LOG = Logger.getLogger(ResourceCustomEditor.class.getName());
private final Listener listener;
private final ExplorerManager em;
private FileObject rootDir;
private URI current;
private final PropertyEnv env;
private boolean ignoreChanges;
private final ResourceEditor editor;
/**
* Creates new form ResourceCustomEditor
*/
ResourceCustomEditor(ResourceEditor editor, FileObject workingDir, URI current, PropertyEnv env) {
initComponents();
this.current = current;
this.env = env;
this.editor = editor;
// env.setState(PropertyEnv.STATE_NEEDS_VALIDATION);
em = new ExplorerManager();
if (workingDir != null) {
FileObject root = workingDir.getFileObject("resources");
if (root == null) {
root = workingDir;
}
try {
em.setRootContext(new FileNode(DataObject.find(root).getNodeDelegate()));
rootDir = root;
} catch (DataObjectNotFoundException ex) {
Exceptions.printStackTrace(ex);
}
}
if (current != null) {
uriField.setText(current.toString());
syncEM();
}
listener = new Listener();
env.addVetoableChangeListener(listener);
uriField.getDocument().addDocumentListener(listener);
em.addPropertyChangeListener(listener);
initContextView((ContextTreeView) contextView);
initFileListView((ListView) fileView);
}
private void initContextView(ContextTreeView view) {
view.setDefaultActionAllowed(false);
}
private void initFileListView(ListView view) {
view.setPopupAllowed(false);
view.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
// view.setShowParentNode(true);
}
private void syncEM() {
try {
if (current != null && "file".equals(current.getScheme())
&& rootDir != null) {
FileObject fob = FileUtil.toFileObject(Utilities.toFile(current));
if (fob != null && FileUtil.isParentOf(rootDir, fob)) {
Node sel = null;
Node root = em.getRootContext();
String relPath = FileUtil.getRelativePath(rootDir, fob);
String[] path = relPath.split("/");
path[path.length-1] = fob.getName();
sel = findNode(root, path);
if (sel == null) {
path[path.length-1] = fob.getNameExt();
sel = findNode(root, path);
}
if (sel != null) {
em.setExploredContextAndSelection(sel.getParentNode(),new Node[]{sel});
}
}
} else {
em.setExploredContext(em.getRootContext(),new Node[0]);
}
} catch (Exception ex) {
Exceptions.printStackTrace(ex);
}
}
private Node findNode(Node start, String[] path) {
Node node = null;
try {
node = NodeOp.findPath(start, path);
} catch (NodeNotFoundException ex) {
}
return node;
}
/**
* 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() {
uriField = new javax.swing.JTextField();
contextView = new org.openide.explorer.view.ContextTreeView();
fileView = new org.openide.explorer.view.ListView();
jToolBar1 = new javax.swing.JToolBar();
clearButton = new javax.swing.JButton();
browseButton = new javax.swing.JButton();
setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1));
uriField.setText(org.openide.util.NbBundle.getMessage(ResourceCustomEditor.class, "ResourceCustomEditor.uriField.text")); // NOI18N
jToolBar1.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1));
jToolBar1.setFloatable(false);
jToolBar1.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(ResourceCustomEditor.class, "ResourceCustomEditor.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);
}
});
jToolBar1.add(clearButton);
browseButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/net/neilcsmith/praxis/live/pxr/resources/open.png"))); // NOI18N
browseButton.setText(org.openide.util.NbBundle.getMessage(ResourceCustomEditor.class, "ResourceCustomEditor.browseButton.text")); // NOI18N
browseButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
browseButtonActionPerformed(evt);
}
});
jToolBar1.add(browseButton);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(contextView, javax.swing.GroupLayout.DEFAULT_SIZE, 195, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(fileView, javax.swing.GroupLayout.DEFAULT_SIZE, 192, Short.MAX_VALUE))
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(uriField)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jToolBar1, 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.LEADING)
.addComponent(uriField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(jToolBar1, 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)
.addComponent(contextView, javax.swing.GroupLayout.DEFAULT_SIZE, 241, Short.MAX_VALUE)
.addComponent(fileView))
.addContainerGap())
);
}// </editor-fold>//GEN-END:initComponents
private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseButtonActionPerformed
File loc = null;
if (current != null && "file".equals(current.getScheme())) {
try {
loc = Utilities.toFile(current);
} catch (Exception e) {
}
}
if (loc == null && rootDir != null) {
try {
loc = FileUtil.toFile(rootDir);
} catch (Exception e) {
}
}
FileChooserBuilder dlgBld = new FileChooserBuilder(ResourceCustomEditor.class);
if (loc != null) {
dlgBld.setDefaultWorkingDirectory(loc).forceUseOfDefaultWorkingDirectory(true);
}
dlgBld.setTitle("Choose File").setApproveText("OK").setFileHiding(true);
File file = dlgBld.showOpenDialog();
if (file != null) {
current = file.toURI();
ignoreChanges = true;
uriField.setText(current.toString());
env.setState(PropertyEnv.STATE_NEEDS_VALIDATION);
syncEM();
// env.setState(PropertyEnv.STATE_VALID);
// editor.setValue(PResource.valueOf(current));
ignoreChanges = false;
}
}//GEN-LAST:event_browseButtonActionPerformed
private void clearButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_clearButtonActionPerformed
uriField.setText("");
}//GEN-LAST:event_clearButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton browseButton;
private javax.swing.JButton clearButton;
private javax.swing.JScrollPane contextView;
private javax.swing.JScrollPane fileView;
private javax.swing.JToolBar jToolBar1;
private javax.swing.JTextField uriField;
// End of variables declaration//GEN-END:variables
@Override
public ExplorerManager getExplorerManager() {
return em;
}
private class Listener implements VetoableChangeListener, DocumentListener,
PropertyChangeListener {
@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 (ignoreChanges) {
return;
}
ignoreChanges = true;
String txt = uriField.getText();
if (txt.isEmpty()) {
current = null;
env.setState(PropertyEnv.STATE_NEEDS_VALIDATION);
} else {
try {
current = PResource.valueOf(uriField.getText()).value();
env.setState(PropertyEnv.STATE_NEEDS_VALIDATION);
} catch (Exception ex) {
env.setState(PropertyEnv.STATE_INVALID);
}
}
syncEM();
ignoreChanges = false;
}
// called when OK button is pressed
@Override
public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException {
if (PropertyEnv.PROP_STATE.equals(evt.getPropertyName())
&& PropertyEnv.STATE_VALID.equals(evt.getNewValue())) {
if (current == null) {
editor.setValue(PString.EMPTY);
} else {
editor.setValue(PResource.valueOf(current));
}
}
}
// called from EM
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (ignoreChanges) {
return;
}
if (ExplorerManager.PROP_SELECTED_NODES.equals(evt.getPropertyName())) {
Node[] nodes = em.getSelectedNodes();
if (nodes.length == 1) {
FileObject fob = nodes[0].getLookup().lookup(FileObject.class);
current = fob.toURI();
env.setState(PropertyEnv.STATE_NEEDS_VALIDATION);
ignoreChanges = true;
uriField.setText(current.toString());
ignoreChanges = false;
}
}
}
}
private class FileNode extends FilterNode {
FileNode(Node node) {
super(node, node.isLeaf() ? Children.LEAF : new FileChildren(node));
}
@Override
public Action getPreferredAction() {
if (getOriginal().isLeaf()) {
return new FileAction(this);
} else {
return super.getPreferredAction();
}
}
@Override
public Action[] getActions(boolean context) {
return new Action[0];
}
}
private class FileChildren extends FilterNode.Children {
public FileChildren(Node node) {
super(node);
}
@Override
protected Node copyNode(Node node) {
return new FileNode(node);
}
}
private class FileAction extends AbstractAction {
private FileNode node;
private FileAction(FileNode node) {
super("OK");
this.node = node;
}
@Override
public void actionPerformed(ActionEvent e) {
try {
FileObject fob = node.getLookup().lookup(FileObject.class);
current = fob.toURI();
env.setState(PropertyEnv.STATE_NEEDS_VALIDATION);
SwingUtilities.getRootPane(ResourceCustomEditor.this).getDefaultButton().doClick();
} catch (Exception ex) {
Exceptions.printStackTrace(ex);
}
}
}
}