package de.unisiegen.gtitool.ui.jgraph;
import java.awt.Color;
import java.awt.geom.Rectangle2D;
import javax.swing.event.EventListenerList;
import org.jgraph.event.GraphModelEvent;
import org.jgraph.event.GraphModelListener;
import org.jgraph.event.GraphModelEvent.GraphModelChange;
import org.jgraph.graph.DefaultGraphCell;
import org.jgraph.graph.DefaultGraphModel;
import org.jgraph.graph.GraphConstants;
import de.unisiegen.gtitool.core.entities.State;
import de.unisiegen.gtitool.core.entities.listener.ModifyStatusChangedListener;
import de.unisiegen.gtitool.core.entities.listener.StateChangedListener;
import de.unisiegen.gtitool.core.storage.Attribute;
import de.unisiegen.gtitool.core.storage.Element;
import de.unisiegen.gtitool.core.storage.Modifyable;
import de.unisiegen.gtitool.core.storage.Storable;
import de.unisiegen.gtitool.ui.model.DefaultMachineModel;
import de.unisiegen.gtitool.ui.redoundo.StatePositionChangedListener;
/**
* This class represents the {@link State} in the gui.
*
* @author Benjamin Mies
* @author Christian Fehler
* @version $Id:DefaultStateView.java 910 2008-05-16 00:31:21Z fehler $
*/
public final class DefaultStateView extends DefaultGraphCell implements
Storable, Modifyable, Comparable < DefaultStateView >
{
/**
* The height is not defined value.
*/
public static final double HEIGHT_NOT_DEFINED = Double.MIN_VALUE;
/**
* The position is not defined value.
*/
public static final double POSITION_NOT_DEFINED = Double.MIN_VALUE;
/**
* The serial version uid.
*/
private static final long serialVersionUID = -2780335257122860579L;
/**
* The width is not defined value.
*/
public static final double WIDTH_NOT_DEFINED = Double.MIN_VALUE;
/**
* The {@link DefaultGraphModel} for this model.
*/
private DefaultGraphModel graphModel;
/**
* Flag signals that nex state move should be ignored.
*/
private boolean ignoreStateMove = false;
/**
* The initial x position.
*/
private double initialXPosition = POSITION_NOT_DEFINED;
/**
* The initial y position.
*/
private double initialYPosition = POSITION_NOT_DEFINED;
/**
* The {@link EventListenerList}.
*/
private EventListenerList listenerList = new EventListenerList ();
/**
* The {@link DefaultMachineModel}
*/
private DefaultMachineModel machineModel;
/**
* The overwritten {@link Color} of this {@link StateView}.
*/
private Color overwrittenColor;
/**
* The puplished x position.
*/
private double puplishedXPosition = POSITION_NOT_DEFINED;
/**
* The puplished y position.
*/
private double puplishedYPosition = POSITION_NOT_DEFINED;
/**
* The {@link State} represented by this view.
*/
protected State state;
/**
* The actual x value.
*/
private double xValue = POSITION_NOT_DEFINED;
/**
* The actual y value.
*/
private double yValue = POSITION_NOT_DEFINED;
/**
* Creates a new {@link DefaultStateView}.
*
* @param machineModel The {@link DefaultMachineModel}.
* @param graphModel The {@link DefaultGraphModel}.
* @param state The {@link State} represented by this view.
*/
public DefaultStateView ( DefaultMachineModel machineModel,
DefaultGraphModel graphModel, State state )
{
super ( state );
this.machineModel = machineModel;
this.graphModel = graphModel;
this.state = state;
this.state.addStateChangedListener ( new StateChangedListener ()
{
public void stateChanged (
@SuppressWarnings ( "unused" ) boolean nameChanged,
boolean startChanged, boolean loopTransitionChanged )
{
if ( startChanged )
{
if ( DefaultStateView.this.state.isStartState () )
{
moveRelative ( -StateView.START_OFFSET, 0 );
}
else
{
moveRelative ( StateView.START_OFFSET, 0 );
}
}
if ( loopTransitionChanged )
{
if ( DefaultStateView.this.state.isLoopTransition () )
{
moveRelative ( 0, -StateView.LOOP_TRANSITION_OFFSET );
}
else
{
moveRelative ( 0, StateView.LOOP_TRANSITION_OFFSET );
}
}
}
} );
this.graphModel.addGraphModelListener ( new GraphModelListener ()
{
public void graphChanged ( GraphModelEvent event )
{
GraphModelChange graphModelChange = event.getChange ();
Object [] changed = graphModelChange.getChanged ();
for ( Object current : changed )
{
if ( current instanceof DefaultStateView )
{
DefaultStateView defaultStateView = ( DefaultStateView ) current;
if ( defaultStateView.getState ().equals ( getState () ) )
{
fireModifyStatusChanged ();
fireStatePositionChanged ();
return;
}
}
}
}
} );
// Reset modify
resetModify ();
}
/**
* {@inheritDoc}
*
* @see Modifyable#addModifyStatusChangedListener(ModifyStatusChangedListener)
*/
public final void addModifyStatusChangedListener (
ModifyStatusChangedListener listener )
{
this.listenerList.add ( ModifyStatusChangedListener.class, listener );
}
/**
* /** Adds the given {@link StatePositionChangedListener}.
*
* @param listener The {@link StatePositionChangedListener}.
*/
public final void addStatePositionChangedListener (
StatePositionChangedListener listener )
{
this.listenerList.add ( StatePositionChangedListener.class, listener );
}
/**
* {@inheritDoc}
*
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
public int compareTo ( DefaultStateView o )
{
return ( int ) ( getPositionX () - o.getPositionX () );
}
/**
* {@inheritDoc}
*
* @see Object#equals(Object)
*/
@Override
public final boolean equals ( Object other )
{
if ( other instanceof DefaultStateView )
{
DefaultStateView stateView = ( DefaultStateView ) other;
return this.state.equals ( stateView.getState () );
}
return false;
}
/**
* Let the listeners know that the modify status has changed.
*/
protected final void fireModifyStatusChanged ()
{
ModifyStatusChangedListener [] listeners = this.listenerList
.getListeners ( ModifyStatusChangedListener.class );
double x = getPositionX ();
double y = getPositionY ();
if ( ( this.puplishedXPosition != x ) || ( this.puplishedYPosition != y ) )
{
this.puplishedXPosition = x;
this.puplishedYPosition = y;
boolean newModifyStatus = isModified ();
for ( ModifyStatusChangedListener current : listeners )
{
current.modifyStatusChanged ( newModifyStatus );
}
}
}
/**
* Let the listeners know that the state position has changed.
*/
protected final void fireStatePositionChanged ()
{
double x = getPositionX ();
double y = getPositionY ();
StatePositionChangedListener [] listeners = this.listenerList
.getListeners ( StatePositionChangedListener.class );
if ( ( this.xValue != x ) || ( this.yValue != y ) )
{
if ( this.ignoreStateMove )
{
this.ignoreStateMove = false;
this.xValue = x;
this.yValue = y;
return;
}
for ( StatePositionChangedListener current : listeners )
{
current.statePositionChanged ( this, this.xValue, this.yValue, x, y );
}
this.xValue = x;
this.yValue = y;
}
}
/**
* {@inheritDoc}
*
* @see Storable#getElement()
*/
public final Element getElement ()
{
Rectangle2D bounds = GraphConstants.getBounds ( getAttributes () );
Element newElement = new Element ( "StateView" ); //$NON-NLS-1$
newElement.addAttribute ( new Attribute ( "x", bounds.getX () ) ); //$NON-NLS-1$
newElement.addAttribute ( new Attribute ( "y", bounds.getY () ) ); //$NON-NLS-1$
newElement.addElement ( this.state.getElement () );
return newElement;
}
/**
* Returns the height.
*
* @return The height.
*/
public final double getHeight ()
{
Rectangle2D bounds = GraphConstants.getBounds ( getAttributes () );
if ( bounds == null )
{
return HEIGHT_NOT_DEFINED;
}
return bounds.getHeight ();
}
/**
* Returns the {@link DefaultMachineModel}.
*
* @return The {@link DefaultMachineModel}.
* @see #machineModel
*/
public DefaultMachineModel getMachineModel ()
{
return this.machineModel;
}
/**
* Returns the overwritten {@link Color}.
*
* @return The overwritten {@link Color}.
* @see #overwrittenColor
*/
public final Color getOverwrittenColor ()
{
return this.overwrittenColor;
}
/**
* Returns the x position.
*
* @return The x position.
*/
public final double getPositionX ()
{
Rectangle2D bounds = GraphConstants.getBounds ( getAttributes () );
if ( bounds == null )
{
return POSITION_NOT_DEFINED;
}
return bounds.getX ();
}
/**
* Returns the y position.
*
* @return The y position.
*/
public final double getPositionY ()
{
Rectangle2D bounds = GraphConstants.getBounds ( getAttributes () );
if ( bounds == null )
{
return POSITION_NOT_DEFINED;
}
return bounds.getY ();
}
/**
* Returns the {@link State} of this view.
*
* @return The {@link State} of this view.
*/
public final State getState ()
{
return this.state;
}
/**
* Returns the width.
*
* @return The width.
*/
public final double getWidth ()
{
Rectangle2D bounds = GraphConstants.getBounds ( getAttributes () );
if ( bounds == null )
{
return WIDTH_NOT_DEFINED;
}
return bounds.getWidth ();
}
/**
* {@inheritDoc}
*
* @see Object#hashCode()
*/
@Override
public final int hashCode ()
{
return this.state.hashCode ();
}
/**
* {@inheritDoc}
*
* @see Modifyable#isModified()
*/
public final boolean isModified ()
{
return ( this.initialXPosition != getPositionX () )
|| ( this.initialYPosition != getPositionY () );
}
/**
* Moves this {@link DefaultStateView}.
*
* @param x The new x value.
* @param y The new y value.
*/
public final void move ( double x, double y )
{
this.ignoreStateMove = true;
Rectangle2D bounds = GraphConstants.getBounds ( getAttributes () );
bounds.setRect ( x, y, bounds.getWidth (), bounds.getHeight () );
GraphConstants.setBounds ( getAttributes (), bounds );
}
/**
* Moves this {@link DefaultStateView} relative to the current position.
*
* @param x The x offset value.
* @param y The y offset value.
*/
public final void moveRelative ( double x, double y )
{
this.ignoreStateMove = true;
Rectangle2D bounds = GraphConstants.getBounds ( getAttributes () );
double newX = bounds.getX () + x < 0 ? 0 : bounds.getX () + x;
double newY = bounds.getY () + y < 0 ? 0 : bounds.getY () + y;
bounds.setRect ( newX, newY, bounds.getWidth (), bounds.getHeight () );
GraphConstants.setBounds ( getAttributes (), bounds );
}
/**
* {@inheritDoc}
*
* @see Modifyable#removeModifyStatusChangedListener(ModifyStatusChangedListener)
*/
public final void removeModifyStatusChangedListener (
ModifyStatusChangedListener listener )
{
this.listenerList.remove ( ModifyStatusChangedListener.class, listener );
}
/**
* {@inheritDoc}
*
* @see Modifyable#resetModify()
*/
public final void resetModify ()
{
this.initialXPosition = getPositionX ();
this.initialYPosition = getPositionY ();
}
/**
* Sets the overwritten {@link Color}.
*
* @param overwrittenColor The overwritten {@link Color} to set.
* @see #overwrittenColor
*/
public final void setOverwrittenColor ( Color overwrittenColor )
{
this.overwrittenColor = overwrittenColor;
}
}