//Copyright (c)2005 Holobloc Inc.
//Contributed to the OOONEIDA FBench project under the Common Public License.
//Copyright (c) 2007 University of Auckland
//Contributed to the FBench extension of the OOONEIDA Workbench project under the Common Public License
package fbench.graph;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import javax.swing.BorderFactory;
import javax.swing.DefaultCellEditor;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.border.Border;
import fbench.IconFactory;
import fbench.graph.model.ActionsModel;
import fbench.graph.popup.ContextMenu;
import fbench.graph.popup.ECCDialog;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
/**
* A graphic element representing an ECC state, which encapsulates an
* ElementModel for an IEC 61499 <TT>ECState</TT> Element.
*
* @author JHC, JP
*
* @version 20070301/JP - Linked with ContextMenu and ECCDialog for adding, modifying
* and removing the selected ECState through GUI
* @version 20070130/JP - Drag function added
* @version 20051129/JHC - Made FocusListener for Actions, added under-painting
* of mainLabel when selected.
* @version 20051028/JHC
*/
public class ECState extends GraphNode implements FocusListener {
/**
*
*/
private static final long serialVersionUID = -44825563959444428L;
private static final Border border1 = BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(Color.black), BorderFactory
.createEmptyBorder(2, 2, 2, 2));
private static final Border border3 = BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(Color.black), BorderFactory
.createEmptyBorder(0, 0, 0, 0));
private static final Border border2 = BorderFactory.createCompoundBorder(
border1, border3);
public static final Border thickBorder = BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(Color.black), BorderFactory
.createEmptyBorder(5, 5, 5, 5));
public static final Border thickInitBorder = BorderFactory.createCompoundBorder(
border1, border3);
private JTable tbl = new JTable();
private boolean isInitialState = false;
private Element elem;
private Float xPos;
private Float yPos;
private Float scaleFactor = Float.parseFloat(
""+((getFontMetrics(getFont()).getHeight() + 4.0) / 100.0));
private Point currentMousePoint = new Point();
private int currentMouseButton;
private boolean isBeingRightClickDragged = false;
private boolean isMouseOver = false;
/**
* Initializes the element as an initial state if <TT>istate</TT> is
* <B>true </B>, as an ordinary state otherwise.
*/
public ECState(Element el, boolean istate) {
super();
setLayout(new GridBagLayout());
isInitialState = istate;
setElement(el);
setName(model.getName());
addMouseMotionListener(new MouseMotionAdapter() {
public void mouseDragged(MouseEvent evt) {
processMouseDrag(evt);
}
});
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent evt) {
processMousePressed(evt);
}
public void mouseClicked(MouseEvent evt){
processMouseClicked(evt);
}
public void mouseReleased(MouseEvent evt){
processMouseReleased(evt);
}
});
}
public void initComponents() {
super.initComponents();
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 0;
gbc.weighty = 0;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.anchor = GridBagConstraints.CENTER;
mainLabel.setBorder(isInitialState ? border2 : border1);
mainLabel.setOpaque(false);
add(mainLabel, gbc);
if (getElement().getFirstChild() == null)
return;
gbc.gridx = 1;
JLabel hline = new JLabel(IconFactory.getIcon("hline"));
add(hline, gbc);
if(isInitialState){
hline.setVisible(false);
}
gbc.gridx = 2;
gbc.weightx = 1;
gbc.weighty = 1;
gbc.fill = GridBagConstraints.BOTH;
tbl.setFont(getFont());
add(tbl, gbc);
if(isInitialState){
tbl.setVisible(false);
}
tbl.setModel(new ActionsModel(getElement()));
tbl.setBorder(border1);
tbl.setGridColor(Color.black);
tbl.setCellSelectionEnabled(true);
Element owner = getElement().getOwnerDocument().getDocumentElement();
setColumnEditor(tbl, 0, owner.getElementsByTagName("Algorithm"));
NodeList intfcs = owner.getElementsByTagName("InterfaceList");
NodeList eos = ((Element) intfcs.item(0)).getElementsByTagName("EventOutputs");
if (eos.getLength() == 0)
return;
setColumnEditor(tbl, 1, eos.item(0).getChildNodes());
}
public void refresh()
{
initComponents();
}
private void setColumnEditor(JTable tbl, int col, NodeList nodes) {
JComboBox box = new JComboBox();
box.setFont(getFont());
box.setEditable(false);
box.addItem("");
box.addFocusListener(this);
for (int i = 0; i < nodes.getLength(); i++)
box.addItem(((Element) nodes.item(i)).getAttribute("Name"));
tbl.getColumnModel().getColumn(col).setCellEditor(
new DefaultCellEditor(box));
}
public Rectangle getPreferredBounds() {
int x = Math.round(getScaledAtt("x"));
int y = Math.round(getScaledAtt("y"));
Dimension sz = getPreferredSize();
return new Rectangle(x - mainLabel.getPreferredSize().width / 2, y
- sz.height / 2, sz.width, sz.height);
}
public void paintBorder(Graphics g) {
super.paintBorder(g);
Rectangle r = mainLabel.getBounds();
Color oldcolor = g.getColor();
g.setColor(isSelected() ? getSelectionColor() : getDeselectedColor());
g.fillRect(r.x, r.y, r.width, r.height);
g.setColor(oldcolor);
}
protected Color getDeselectedColor() {
return Color.white;
}
private void processMouseDrag(MouseEvent mevent) {
if(currentMouseButton == MouseEvent.BUTTON1){
// TODO get initial state draggable
//if(!isInitialState){
//System.out.println("[ECState] " + mevent.getX() + "," + currentMousePoint.x);
//System.out.print("[ECState] " + xPos + "," + yPos + " --> ");
//String oldxPos = xPos.toString();
//String oldyPos = yPos.toString();
xPos = xPos + ((mevent.getX() - currentMousePoint.x) / scaleFactor);
yPos = yPos + ((mevent.getY() - currentMousePoint.y) / scaleFactor);
// Ensure NOT negative
if( xPos < 0 ) xPos = 0.0f;
if( yPos < 0 ) yPos = 0.0f;
//System.out.println(xPos + "," + yPos);
getElement().setAttribute("x", xPos.toString());
getElement().setAttribute("y", yPos.toString());
// Refresh (calls re-draw) - is this the best way?
//System.out.println("[ECState] " + this.getParent() + " " + this.getParent().getParent());
setElement(getElement());
//System.out.println("[RS] " + getElement().toString() + " " + String.valueOf(xPos) + " " + String.valueOf(yPos));
//if(isInitialState)
//{
// TODO: This is what makes the initial state Re-draw!!! :D
//reset viewport (ensure new state is in view @ all times
if( this.getParent() instanceof GraphView )
{
((GraphView)this.getParent()).redraw();//centerViewPort(new Point(xPos.intValue(),yPos.intValue()));
}
else if( this.getParent().getParent() instanceof GraphView )
{
/*this.getParent().repaint();
if(this.isInitialState)
{
this.refresh();
((GraphView)this.getParent().getParent()).invalidate();
}*/
((GraphView)this.getParent().getParent()).redraw();//centerViewPort(new Point(xPos.intValue(),yPos.intValue()));
}
// Dispatch Events
//Document doc = (Document)getElement().getOwnerDocument();
//MutationEventImpl mEvt = new MutationEventImpl();
//if( !oldxPos.equals(xPos.toString()) )
//mEvt.initMutationEvent("Modification", true, true, (Node)getElement(), oldxPos, xPos.toString(), "x", MutationEvent.MODIFICATION);
// Reselect
//ElementSelectionEvent eEvt = new ElementSelectionEvent();
//eEvt.initSelectionEvent(getElement(), this, ElementSelectionEvent.ACTIVATE);
//((DocumentImpl)doc).dispatchEvent(eEvt);
//((DocumentImpl)doc).dispatchEvent(mEvt);
//System.out.println("[ECState] Event thrown");
//}
//}
}
else if(currentMouseButton == MouseEvent.BUTTON3){
isBeingRightClickDragged = true;
Point rightClickPoint = new Point();
rightClickPoint.setLocation(xPos*scaleFactor, yPos*scaleFactor);
}
}
private void processMousePressed(MouseEvent evt){
elem = getElement();
xPos = (Float.parseFloat(elem.getAttribute("x")));
yPos = (Float.parseFloat(elem.getAttribute("y")));
currentMousePoint = evt.getPoint();
currentMouseButton = evt.getButton();
}
private void processMouseReleased(MouseEvent evt){
System.out.println("[ECSTATE] processMouseReleased");
if(isBeingRightClickDragged){
isBeingRightClickDragged = false;
Point p = evt.getPoint();
p.translate(this.getX(), this.getY());
Component c = getParent().getComponentAt(p);
if(c != null && c instanceof ECState && c != this){
ECState other = (ECState)c;
ECCDialog eccdialog = new ECCDialog((Element)elem.getParentNode(), evt);
eccdialog.setSourceStateName(getName());
eccdialog.setDestinationStateName(other.getName());
eccdialog.create("Add Transition");
}
}
}
private void processMouseClicked(MouseEvent evt){
System.out.println("[ECSTATE] mouseClicked");
if(isInFBench()){
if(evt.getButton() == MouseEvent.BUTTON1 && evt.getClickCount() == 2){
ECCDialog eccDialog = new ECCDialog(elem, evt);
eccDialog.create("Edit State");
}
else if(evt.getButton() == MouseEvent.BUTTON3){
ContextMenu contextMenu = new ContextMenu(getElement(), evt);
contextMenu.create(getElement().getNodeName());
}
}
}
/**
* Update <tt>xmlArea</tt> of <tt>LibraryElementView</tt> when the mouse is released
*/
/*
private void updateXML() {
if(getParent().getParent().getParent().getParent().getParent().getParent()
.getClass().toString().equals("fbench.LibraryElementView")){
LibraryElementView mainContainer =
(LibraryElementView)getParent().getParent().getParent().getParent().getParent().getParent();
JTextArea xmlArea = mainContainer.getXmlArea();
Document document = mainContainer.getDocument();
NodeList BasicFBItems;
if(!document.getLastChild().getLastChild().getNodeName().equals("BasicFB")){
System.out.println("Cannot update XML: cannot find BasicFB element from document");
return;
}
else
BasicFBItems = document.getLastChild().getLastChild().getChildNodes();
for(int i = 0; i < BasicFBItems.getLength(); i++){
Element tmp = (Element) BasicFBItems.item(i);
if(elem.getAttribute("Name").equals(tmp.getAttribute("Name"))){
tmp.setAttribute("x", elem.getAttribute("x"));
tmp.setAttribute("y", elem.getAttribute("y"));
}
}
try {
int caretPosition = xmlArea.getCaretPosition();
String documentText = xmlArea.getText(0, caretPosition);
String[] textInArray = documentText.split("\n");
int documentTextLineCounter = textInArray.length;
int caretPositionInLine = caretPosition - documentText.lastIndexOf("\n");
new DOMTextModel(xmlArea, document);
documentText = xmlArea.getText();
int lineIndex = 0;
for(int i = 0; i < documentTextLineCounter-1;){
lineIndex = documentText.indexOf("\n");
documentText = documentText.replaceFirst("\n"," ");
i++;
}
if(caretPositionInLine == 1){
lineIndex = documentText.indexOf("\n");
}
xmlArea.setCaretPosition(lineIndex + caretPositionInLine);
} catch (BadLocationException e) {
e.printStackTrace();
}
}
}
*/
public boolean isMouseOver(){
return isMouseOver;
}
/**
* Generates an ElementSelectionEvent when a JComboBox in an Action gets the
* focus.
*/
public void focusGained(FocusEvent e) {
super.processMousePress(null);
}
public void focusLost(FocusEvent e) {
super.processMousePress(null);
}
}