Project: BioMAV
/*
    This file is part of the BioMAV project. 
 
    The BioMAV project 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. 
 
    The BioMAV project 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 The BioMAV project. If not, see <http://www.gnu.org/licenses/>. 
*/
package nl.ru.ai.projects.parrot.biomav.editor; 
 
import java.awt.BorderLayout; 
import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.Font; 
import java.awt.GridLayout; 
import java.awt.Paint; 
import java.awt.Shape; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.awt.event.ComponentEvent; 
import java.awt.event.ComponentListener; 
import java.awt.event.InputEvent; 
import java.awt.event.ItemEvent; 
import java.awt.event.ItemListener; 
import java.awt.event.KeyEvent; 
import java.awt.event.KeyListener; 
import java.awt.event.MouseEvent; 
import java.util.HashSet; 
import java.util.Set; 
 
import javax.swing.JComboBox; 
import javax.swing.JFrame; 
import javax.swing.JLabel; 
import javax.swing.JOptionPane; 
import javax.swing.JPanel; 
import javax.swing.SwingUtilities; 
import javax.swing.event.ChangeEvent; 
import javax.swing.event.ChangeListener; 
 
import org.apache.commons.collections15.Factory; 
import org.apache.commons.collections15.Predicate; 
import org.apache.commons.collections15.Transformer; 
 
import edu.uci.ics.jung.algorithms.layout.FRLayout2; 
import edu.uci.ics.jung.graph.DelegateForest; 
import edu.uci.ics.jung.graph.DirectedGraph; 
import edu.uci.ics.jung.graph.DirectedOrderedSparseMultigraph; 
import edu.uci.ics.jung.graph.DirectedSparseGraph; 
import edu.uci.ics.jung.graph.Graph; 
import edu.uci.ics.jung.graph.SparseMultigraph; 
import edu.uci.ics.jung.visualization.VisualizationViewer; 
import edu.uci.ics.jung.visualization.control.AbstractGraphMousePlugin; 
import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse; 
import edu.uci.ics.jung.visualization.control.EditingGraphMousePlugin; 
import edu.uci.ics.jung.visualization.control.GraphMouseListener; 
import edu.uci.ics.jung.visualization.control.ModalGraphMouse; 
import edu.uci.ics.jung.visualization.control.PickingGraphMousePlugin; 
import edu.uci.ics.jung.visualization.control.PluggableGraphMouse; 
import edu.uci.ics.jung.visualization.control.ViewTranslatingGraphMousePlugin; 
import edu.uci.ics.jung.visualization.renderers.DefaultVertexLabelRenderer; 
import edu.uci.ics.jung.visualization.renderers.GradientVertexRenderer; 
import edu.uci.ics.jung.visualization.renderers.VertexLabelAsShapeRenderer; 
import edu.uci.ics.jung.visualization.util.PredicatedParallelEdgeIndexFunction; 
 
/**
 * Editor component that allows editing a finite state machine. The 
 * component is a JUNG-based graph editor that can directly manipulate 
 * behavior graphs of the finite state machine that controls the drone.   
 *  
 * @author Paul Konstantin Gerke 
 * 
 */
 
public class BehaviorEditor extends JPanel { 
  private static final long serialVersionUID = 690046335487721939L
 
  private DirectedOrderedSparseMultigraph<BehaviorVertex, TransitionEdge> graph = new DirectedOrderedSparseMultigraph<BehaviorVertex, TransitionEdge>(); 
   
  private VisualizationViewer<BehaviorVertex, TransitionEdge> graphViewer = new VisualizationViewer<BehaviorVertex, TransitionEdge>(new FRLayout2<BehaviorVertex, TransitionEdge>(graph)); 
 
  private PluggableGraphMouse graphMouse = new PluggableGraphMouse(); 
  private EditingGraphMousePlugin<BehaviorVertex, TransitionEdge> graphMouseEditPlugin = new EditingGraphMousePlugin<BehaviorVertex, TransitionEdge>(InputEvent.CTRL_MASK, new Factory<BehaviorVertex>() { 
      @Override 
      public BehaviorVertex create() { 
        BehaviorVertex vertex = new BehaviorVertex(); 
        vertex.addBehaviorVertexListener(new BehaviorVertexListener() { 
          @Override 
          public void behaviorVertexActivityChanged(BehaviorVertex vertex) { 
            SwingUtilities.invokeLater(new Runnable() { 
              @Override 
              public void run() { 
                graphViewer.repaint(); 
              } 
            }); 
          } 
        }); 
        return vertex; 
      } 
    }, new Factory<TransitionEdge>() { 
      @Override 
      public TransitionEdge create() { 
        return new TransitionEdge(); 
      } 
    }); 
  private ViewTranslatingGraphMousePlugin graphMouseTranslatePlugin = new ViewTranslatingGraphMousePlugin(MouseEvent.BUTTON3_MASK); 
  private PickingGraphMousePlugin<BehaviorVertex, TransitionEdge> graphMousePickPlugin = new PickingGraphMousePlugin<BehaviorVertex, TransitionEdge>(); 
 
  private JPanel pcInterfacePanel = new JPanel(); 
  private JPanel pcInterfacePanelContents = new JPanel(); 
  private ParameterControlInterface selectedPCInterface = null
   
  private void setNewSelectedParameterControlInterface(ParameterControlInterface pcInterface) { 
    pcInterfacePanelContents.removeAll(); 
    selectedPCInterface = null
     
    if (pcInterface != null) { 
      selectedPCInterface = pcInterface; 
      final ParameterControlInterface fpcInterface = pcInterface; 
       
      String[] parameterNames = pcInterface.getParameterNames(); 
      ParameterControlInterface.ParameterTypes[] parameterTypes = pcInterface.getParameterTypes(); 
       
      for (int i = 0; i < parameterTypes.length; i++) { 
        JLabel label = new JLabel(parameterNames[i]); 
        pcInterfacePanelContents.add(label); 
         
        final int index = i; 
        switch (parameterTypes[i]) { 
          case OPTIONS: 
            final JComboBox optionComboBox = new JComboBox(pcInterface.getParameterOptions(i)); 
            optionComboBox.setEditable(false); 
            optionComboBox.setSelectedItem(pcInterface.getParameterValue(i)); 
             
            optionComboBox.addActionListener(new ActionListener() { 
              @Override 
              public void actionPerformed(ActionEvent e) { 
                fpcInterface.setParameterValue(index, optionComboBox.getSelectedItem()); 
                repaint(); 
              } 
            }); 
             
            pcInterfacePanelContents.add(optionComboBox); 
             
            break
        } 
      } 
    } 
 
    pcInterfacePanel.validate(); 
  } 
   
  public BehaviorEditor() { 
    ///////// Graph stuff 
    graphViewer.addKeyListener(new KeyListener() { 
      @Override 
      public void keyTyped(KeyEvent e) { 
      } 
       
      @Override 
      public void keyReleased(KeyEvent e) { 
      } 
       
      @Override 
      public void keyPressed(KeyEvent e) { 
        if (e.getKeyCode() == KeyEvent.VK_DELETE) { 
          for (TransitionEdge edge : graphViewer.getPickedEdgeState().getPicked()) { 
            graph.removeEdge(edge); 
          } 
          for (BehaviorVertex vertex : graphViewer.getPickedVertexState().getPicked()) { 
            graph.removeVertex(vertex); 
          } 
          e.consume(); 
          repaint(); 
        } 
      } 
    }); 
     
    graphViewer.setBackground(Color.GRAY); 
     
    graphViewer.setGraphMouse(graphMouse); 
    graphMouse.add(graphMouseEditPlugin);  
    graphMouse.add(graphMouseTranslatePlugin); 
    graphMouse.add(graphMousePickPlugin); 
 
    // Set label renderer 
    VertexLabelAsShapeRenderer<BehaviorVertex, TransitionEdge> renderer = new VertexLabelAsShapeRenderer<BehaviorVertex, TransitionEdge>(graphViewer.getRenderContext()); 
    graphViewer.getRenderContext().setVertexShapeTransformer(renderer); 
    graphViewer.getRenderer().setVertexLabelRenderer(renderer); 
 
    // Set label contents 
    graphViewer.getRenderContext().setVertexLabelTransformer(new Transformer<BehaviorVertex, String>() { 
      @Override 
      public String transform(BehaviorVertex v) { 
        return v.getSelectedBehavior(); 
      } 
    }); 
    graphViewer.getRenderContext().setEdgeLabelTransformer(new Transformer<TransitionEdge, String>() { 
      @Override 
      public String transform(TransitionEdge e) { 
        return e.getTransition(); 
      } 
    }); 
 
    // Set fonts 
    graphViewer.getRenderContext().setVertexFontTransformer(new Transformer<BehaviorVertex, Font>() { 
      @Override 
      public Font transform(BehaviorVertex v) { 
        return new Font(graphViewer.getFont().getName(), graphViewer.getFont().getStyle(), 20); 
      } 
    }); 
    graphViewer.getRenderContext().setEdgeFontTransformer(new Transformer<TransitionEdge, Font>() { 
      @Override 
      public Font transform(TransitionEdge arg0) { 
        return new Font(graphViewer.getFont().getName(), graphViewer.getFont().getStyle(), 16); 
      } 
    }); 
     
    // Fill color dependent on vertex activity 
    graphViewer.getRenderContext().setVertexFillPaintTransformer(new Transformer<BehaviorVertex, Paint>() { 
      @Override 
      public Paint transform(BehaviorVertex vertex) { 
        return vertex.isActive() ? Color.ORANGE : Color.RED; 
      } 
    }); 
     
    graphViewer.getPickedVertexState().addItemListener(new ItemListener() { 
      @Override 
      public void itemStateChanged(ItemEvent e) { 
        BehaviorVertex vertex = (BehaviorVertex) e.getItem(); 
        if (e.getStateChange() == ItemEvent.SELECTED) { 
          setNewSelectedParameterControlInterface(vertex); 
        } 
        if (e.getStateChange() == ItemEvent.DESELECTED) { 
          if (vertex == selectedPCInterface) { 
            setNewSelectedParameterControlInterface(null); 
          } 
        } 
      } 
    }); 
    graphViewer.getPickedEdgeState().addItemListener(new ItemListener() { 
      @Override 
      public void itemStateChanged(ItemEvent e) { 
        TransitionEdge edge = (TransitionEdge) e.getItem(); 
        if (e.getStateChange() == ItemEvent.SELECTED) { 
          setNewSelectedParameterControlInterface(edge); 
        } 
        if (e.getStateChange() == ItemEvent.DESELECTED) { 
          if (edge == selectedPCInterface) { 
            setNewSelectedParameterControlInterface(null); 
          } 
        } 
      } 
    }); 
     
    this.addComponentListener(new ComponentListener() { 
      @Override 
      public void componentShown(ComponentEvent e) { 
      } 
       
      @Override 
      public void componentResized(ComponentEvent e) { 
        graphViewer.getGraphLayout().setSize(graphViewer.getSize()); 
      } 
       
      @Override 
      public void componentMoved(ComponentEvent e) { 
      } 
       
      @Override 
      public void componentHidden(ComponentEvent e) { 
      } 
    }); 
     
    ///////////// Side panel (parameter control) 
    pcInterfacePanel.setPreferredSize(new Dimension(2000)); 
    pcInterfacePanel.setLayout(new BorderLayout()); 
     
    pcInterfacePanelContents.setLayout(new GridLayout(0205)); 
    pcInterfacePanel.add(pcInterfacePanelContents, BorderLayout.NORTH); 
     
    ///////////// [this] 
     
    setLayout(new BorderLayout(1010)); 
     
    add(graphViewer, BorderLayout.CENTER); 
    add(pcInterfacePanel, BorderLayout.EAST); 
  } 
   
  public DirectedGraph<BehaviorVertex, TransitionEdge> getGraph() { 
    return graph; 
  } 
 
  public static void main(String[] args) { 
    SwingUtilities.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
        JFrame mainFrame = new JFrame("Behavior Editor Test"); 
         
        BehaviorEditor be = new BehaviorEditor(); 
        mainFrame.add(be); 
         
        mainFrame.pack(); 
        mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
        mainFrame.setVisible(true); 
      } 
    }); 
  } 
}