/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2012-2013, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library 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
* Lesser General Public License for more details.
*/
package org.geotoolkit.gui.swing.etl;
import java.awt.BorderLayout;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import javax.xml.bind.JAXBException;
import org.geotoolkit.gui.swing.resource.MessageBundle;
import org.geotoolkit.process.ProcessingRegistry;
import org.geotoolkit.processing.chain.ChainProcessDescriptor;
import org.geotoolkit.processing.chain.model.Chain;
import org.geotoolkit.processing.chain.model.event.EventChain;
import org.jdesktop.swingx.JXMultiSplitPane;
import org.jdesktop.swingx.MultiSplitLayout.Divider;
import org.jdesktop.swingx.MultiSplitLayout.Leaf;
import org.jdesktop.swingx.MultiSplitLayout.Split;
/**
* Chain visual editor.
*
* @author Johann Sorel (Geomatys)
*/
public class JChainEditor extends JPanel{
// views flags.
private static final int BASIC_VIEW = 0;
private static final int INTERMEDIATE_VIEW = 1;
private static final int ADVANCED_VIEW = 2;
private EventChain chain = null;
//swing elements
private final JXMultiSplitPane guiSplitPane = new JXMultiSplitPane();
private final JPanel guiCenterPanel = new JPanel(new BorderLayout());
private final JSplitPane guiRightPanel = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
private final JTree guiProcessTree = new JTree();
private final JTree guiOtherTree = new JTree();
private final JTree guiDataTree = new JTree();
private final JToolBar guiToolbar = new JToolBar(JToolBar.HORIZONTAL);
private final boolean editable;
private int usedView = BASIC_VIEW;
private ChainScene scene;
public JChainEditor(final boolean editable) {
super(new BorderLayout());
this.editable = editable;
//configure the data tree and actions
guiDataTree.setModel(new DefaultTreeModel(new DefaultMutableTreeNode("root")));
//guiDataTree.setCellRenderer(new JDataTreeCellRenderer());
guiDataTree.setDragEnabled(true);
guiDataTree.setRootVisible(false);
guiDataTree.setTransferHandler(new TransferHandler(){
@Override
public void exportAsDrag(JComponent comp, InputEvent e, int action) {
super.exportAsDrag(comp, e, action);
}
@Override
protected Transferable createTransferable(JComponent c) {
final Collection candidates = new ArrayList();
final TreePath[] paths = guiDataTree.getSelectionPaths();
if(paths != null){
for(TreePath tp : paths){
Object candidate = tp.getLastPathComponent();
if(candidate instanceof DefaultMutableTreeNode){
candidate = ((DefaultMutableTreeNode)candidate).getUserObject();
}
candidates.add(candidate);
}
}
final Transferable tr = new Transferable() {
@Override
public DataFlavor[] getTransferDataFlavors() {
return new DataFlavor[]{new DataFlavor(Object.class, "java/object")};
}
@Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
return true;
}
@Override
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
return candidates;
}
};
return tr;
}
/**
* The list handles both copy and move actions.
*/
@Override
public int getSourceActions(JComponent c) {
return COPY_OR_MOVE;
}
});
//configure tree renderer and actions
guiProcessTree.setModel(new DefaultTreeModel(new DefaultMutableTreeNode("root")));
guiProcessTree.setCellRenderer(new JProcessTreeCellRenderer());
guiProcessTree.setDragEnabled(true);
guiProcessTree.setRootVisible(false);
ToolTipManager.sharedInstance().registerComponent(guiProcessTree);
guiProcessTree.setTransferHandler(new TransferHandler(){
@Override
public void exportAsDrag(JComponent comp, InputEvent e, int action) {
super.exportAsDrag(comp, e, action);
}
@Override
protected Transferable createTransferable(JComponent c) {
final Collection candidates = new ArrayList();
final TreePath[] paths = guiProcessTree.getSelectionPaths();
if(paths != null){
for(TreePath tp : paths){
Object candidate = tp.getLastPathComponent();
if(candidate instanceof DefaultMutableTreeNode){
candidate = ((DefaultMutableTreeNode)candidate).getUserObject();
}
candidates.add(candidate);
}
}
final Transferable tr = new Transferable() {
@Override
public DataFlavor[] getTransferDataFlavors() {
return new DataFlavor[]{new DataFlavor(Object.class, "java/object")};
}
@Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
return true;
}
@Override
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
return candidates;
}
};
return tr;
}
/**
* The list handles both copy and move actions.
*/
@Override
public int getSourceActions(JComponent c) {
return COPY_OR_MOVE;
}
});
//configure tree renderer and actions
guiOtherTree.setModel(new DefaultTreeModel(new DefaultMutableTreeNode("root")));
guiOtherTree.setCellRenderer(new JOtherTreeCellRenderer());
guiOtherTree.setDragEnabled(true);
guiOtherTree.setRootVisible(false);
ToolTipManager.sharedInstance().registerComponent(guiOtherTree);
guiOtherTree.setTransferHandler(new TransferHandler(){
@Override
public void exportAsDrag(JComponent comp, InputEvent e, int action) {
super.exportAsDrag(comp, e, action);
}
@Override
protected Transferable createTransferable(JComponent c) {
final Collection candidates = new ArrayList();
final TreePath[] paths = guiOtherTree.getSelectionPaths();
if(paths != null){
for(TreePath tp : paths){
Object candidate = tp.getLastPathComponent();
if(candidate instanceof DefaultMutableTreeNode){
candidate = ((DefaultMutableTreeNode)candidate).getUserObject();
}
candidates.add(candidate);
}
}
final Transferable tr = new Transferable() {
@Override
public DataFlavor[] getTransferDataFlavors() {
return new DataFlavor[]{new DataFlavor(Object.class, "java/object")};
}
@Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
return true;
}
@Override
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
return candidates;
}
};
return tr;
}
/**
* The list handles both copy and move actions.
*/
@Override
public int getSourceActions(JComponent c) {
return COPY_OR_MOVE;
}
});
final JLabel guiLblData = new JLabel(MessageBundle.format("datas"));
guiLblData.setHorizontalTextPosition(SwingConstants.CENTER);
guiLblData.setHorizontalAlignment(SwingConstants.CENTER);
final JLabel guiLblProcess = new JLabel(MessageBundle.format("processes"));
guiLblProcess.setHorizontalTextPosition(SwingConstants.CENTER);
guiLblProcess.setHorizontalAlignment(SwingConstants.CENTER);
final JLabel guiLblOther = new JLabel(MessageBundle.format("other"));
guiLblOther.setHorizontalTextPosition(SwingConstants.CENTER);
guiLblOther.setHorizontalAlignment(SwingConstants.CENTER);
final JPanel rtpane = new JPanel(new BorderLayout());
final JSplitPane rlpane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
final JPanel rppane = new JPanel(new BorderLayout());
final JPanel ropane = new JPanel(new BorderLayout());
rtpane.add(BorderLayout.NORTH,guiLblData);
rtpane.add(BorderLayout.CENTER,new JScrollPane(guiDataTree));
rppane.add(BorderLayout.NORTH,guiLblProcess);
rppane.add(BorderLayout.CENTER,new JScrollPane(guiProcessTree));
ropane.add(BorderLayout.NORTH,guiLblOther);
ropane.add(BorderLayout.CENTER,new JScrollPane(guiOtherTree));
rlpane.setTopComponent(rppane);
rlpane.setBottomComponent(ropane);
rlpane.setDividerLocation(325);
guiRightPanel.setTopComponent(rtpane);
guiRightPanel.setBottomComponent(rlpane);
guiRightPanel.setDividerLocation(150);
final Leaf left = new Leaf("left"); left.setWeight(0.15);
final Leaf center = new Leaf("center"); center.setWeight(0.70);
final Leaf right = new Leaf("right"); right.setWeight(0.15);
final Split splitModel = new Split(
center,
new Divider(),
right);
guiSplitPane.setModel(splitModel);
guiSplitPane.add(guiCenterPanel, "center");
// hide process/data panel for non-editor user
if(editable) {
guiSplitPane.add(guiRightPanel, "right");
}
/*
* Button to save/load/execute chain.
*/
final Action actNew = new AbstractAction("New") {
@Override
public void actionPerformed(ActionEvent e) {
final EventChain chain = new EventChain();
setChain(chain);
}
};
final Action actSave = new AbstractAction("Save") {
@Override
public void actionPerformed(ActionEvent e) {
final EventChain chain = getChain();
if(chain == null) return;
final JFileChooser jfc = new JFileChooser();
final int action = jfc.showSaveDialog(null);
if(action == JFileChooser.APPROVE_OPTION && jfc.getSelectedFile() != null){
try {
final File file = jfc.getSelectedFile();
chain.write(file);
} catch (JAXBException ex) {
ex.printStackTrace();
}
}
}
};
final Action actLoad = new AbstractAction("Load") {
@Override
public void actionPerformed(ActionEvent e) {
setChain(new EventChain());
final JFileChooser jfc = new JFileChooser();
final int action = jfc.showOpenDialog(null);
if(action == JFileChooser.APPROVE_OPTION && jfc.getSelectedFile() != null){
try {
final File file = jfc.getSelectedFile();
EventChain chain = new EventChain(Chain.read(file));
setChain(chain);
} catch (JAXBException ex) {
ex.printStackTrace();
}
}
}
};
final Action actExecute = new AbstractAction("Execute") {
@Override
public void actionPerformed(ActionEvent e) {
final EventChain chain = getChain();
if(chain == null) return;
final ChainProcessDescriptor desc = new ChainProcessDescriptor(chain, null);
// final Process process = desc.createProcess(input);
// ChainProcess process = new ChainProcess(null, null)
}
};
/*
* Button to change view mode.
*/
final ButtonGroup guiToggleGroup = new ButtonGroup();
final ActionListener toggleListener = new ToggleViewActionListener();
final JToggleButton guiToggleBasicView = new JToggleButton();
guiToggleBasicView.setText(MessageBundle.format("guiToogleBasicView"));
guiToggleBasicView.setName("tglBasic");
if (usedView == BASIC_VIEW) {
guiToggleBasicView.getModel().setSelected(true);
}
guiToggleBasicView.addActionListener(toggleListener);
guiToggleGroup.add(guiToggleBasicView);
final JToggleButton guiToggleNoConstantView = new JToggleButton();
guiToggleNoConstantView.setText(MessageBundle.format("guiToogleInterView"));
guiToggleNoConstantView.setName("tglCst");
if (usedView == INTERMEDIATE_VIEW) {
guiToggleNoConstantView.getModel().setSelected(true);
}
guiToggleNoConstantView.addActionListener(toggleListener);
guiToggleGroup.add(guiToggleNoConstantView);
final JToggleButton guiToggleFullView = new JToggleButton();
guiToggleFullView.setText(MessageBundle.format("guiToogleAdvancedView"));
guiToggleFullView.setName("tglFull");
if (usedView == ADVANCED_VIEW) {
guiToggleFullView.getModel().setSelected(true);
}
guiToggleFullView.addActionListener(toggleListener);
guiToggleGroup.add(guiToggleFullView);
guiToolbar.setFloatable(false);
//save/load/execute actions
guiToolbar.add(actNew);
guiToolbar.add(actSave);
guiToolbar.add(actLoad);
guiToolbar.add(actExecute);
guiToolbar.add(new JSeparator());
//view actions
guiToolbar.add(guiToggleBasicView);
guiToolbar.add(guiToggleNoConstantView);
guiToolbar.add(guiToggleFullView);
add(BorderLayout.NORTH, guiToolbar);
add(BorderLayout.CENTER, guiSplitPane);
}
public void setChain(final EventChain chain){
this.chain = chain;
//center
guiCenterPanel.removeAll();
//scene
createView();
final JComponent view = scene.createView();
guiCenterPanel.add(BorderLayout.CENTER,new JScrollPane(view));
guiCenterPanel.revalidate();
guiCenterPanel.repaint();
if(chain != null){
final JProcessTreeModel processTreeModel = new JProcessTreeModel();
for(ProcessingRegistry factory : chain.getFactories()){
processTreeModel.addRegistry(factory);
}
this.guiProcessTree.setModel(processTreeModel);
//this.guiDataTree.setModel(new JDataTreeModel(session));
this.guiOtherTree.setModel(new JOtherTreeModel());
}
}
public EventChain getChain() {
return chain;
}
/**
* Create the ChainView depending of usedView attribute.
*/
private void createView() {
if (scene != null) {
scene.dispose();
}
if (usedView == BASIC_VIEW) {
scene = new ChainSceneBasic(chain, editable);
} else if (usedView == INTERMEDIATE_VIEW) {
scene = new ChainSceneIntermediate(chain, editable);
} else if (usedView == ADVANCED_VIEW) {
scene = new ChainSceneAdvanced(chain, editable);
}
}
/**
* ActionListener used by toggle view buttons.
*/
private class ToggleViewActionListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
final JToggleButton btn = (JToggleButton) e.getSource();
if ("tglBasic".equals(btn.getName())) {
usedView = BASIC_VIEW;
} else if ("tglCst".equals(btn.getName())) {
usedView = INTERMEDIATE_VIEW;
} else if ("tglFull".equals(btn.getName())) {
usedView = ADVANCED_VIEW;
}
guiCenterPanel.removeAll();
//update scene.
createView();
final JComponent sceneView = scene.createView();
guiCenterPanel.add(BorderLayout.CENTER, new JScrollPane(sceneView));
guiCenterPanel.revalidate();
guiCenterPanel.repaint();
}
}
}