Commits

Anonymous committed ba238a9

Finally updated jgraph,and other fixes to designer

Comments (0)

Files changed (26)

lib/designer/jgraph.jar

Binary file modified.

src/designer/com/opensymphony/workflow/designer/ResultEdge.java

  */
 public class ResultEdge extends WorkflowEdge
 {
-  private static final EdgeRouter EDGE_ROUTER = new EdgeRouter();
-
-  private ResultDescriptor descriptor;
+  //private static final EdgeRouter EDGE_ROUTER = new EdgeRouter();
 
   public ResultEdge(ResultDescriptor descriptor, Point2D labelPos)
   {
     super(descriptor);
     setAttributes(new WorkflowAttributeMap(getAttributes()));
-    this.descriptor = descriptor;
+    getAttributes().put("descriptor", descriptor);
     int arrow = GraphConstants.ARROW_CLASSIC;
     //GraphConstants.setLabelAlongEdge(attributes, true);
     GraphConstants.setLineEnd(attributes, arrow);
 
   public ResultDescriptor getDescriptor()
   {
-    return descriptor;
+    return (ResultDescriptor)getAttributes().get("descriptor");
   }
 
   public void setAutoroute()
   {
-    GraphConstants.setRouting(attributes, EDGE_ROUTER);
+    GraphConstants.setRouting(attributes, new DefaultRouting());
   }
 
   public String toString()
   {
+    ResultDescriptor descriptor = getDescriptor();
     if(descriptor == null) return null;
     if(descriptor.getDisplayName() != null)
     {

src/designer/com/opensymphony/workflow/designer/StepCell.java

   {
     super(userObject);
     setAttributes(new WorkflowAttributeMap(getAttributes()));
+    getAttributes().put("descriptor", userObject);
     GraphConstants.setEditable(attributes, true);
     descriptor = userObject;
     id = descriptor.getId();

src/designer/com/opensymphony/workflow/designer/WorkflowAttributeMap.java

     super(map);
   }
 
-  public Object valueChanged(Object newValue)
+  public AttributeMap applyMap(Map change)
   {
-    Object userObject = get(GraphConstants.VALUE);
+    if(!change.containsKey(GraphConstants.VALUE)) return super.applyMap(change);
+    Object userObject = get("descriptor");
+    Object newValue = change.get(GraphConstants.VALUE);
     if(userObject instanceof StepDescriptor)
     {
       StepDescriptor descriptor = (StepDescriptor)userObject;
       //StepDescriptor user = (StepDescriptor)((StepDescriptor)userObject).clone();
       descriptor.setName(newValue.toString());
-      return descriptor;
     }
     else if(userObject instanceof ResultDescriptor)
     {
           if(((ActionDescriptor)result.getParent()).getConditionalResults().isEmpty())
           {
             ((ActionDescriptor)result.getParent()).setName(newValue.toString());
-            return result;
           }
         }
       }
       result.setDisplayName(newValue.toString());
-      return result;
     }
     else if(userObject instanceof ActionDescriptor)
     {
       ActionDescriptor descriptor = (ActionDescriptor)userObject;
       //StepDescriptor user = (StepDescriptor)((StepDescriptor)userObject).clone();
       descriptor.setName(newValue.toString());
-      return descriptor;
     }
 
-    return super.valueChanged(newValue);
+    return super.applyMap(change);
   }
-
-
 }

src/designer/com/opensymphony/workflow/designer/WorkflowCellViewFactory.java

       WorkflowPort port = (WorkflowPort)cell;
       if(port.getIndex() == 0)
         return new CustomPortView(cell);
-      return new PortView(cell);
+      return new OutsidePortView(cell);
     }
     else if(model.isEdge(cell))
     {

src/designer/com/opensymphony/workflow/designer/WorkflowDesigner.java

 import java.awt.*;
 import java.awt.event.WindowAdapter;
 import java.awt.event.WindowEvent;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.InputStream;
-import java.io.PrintWriter;
+import java.io.*;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.Locale;
 /**
  * @author Hani Suleiman (hani@formicary.net) Date: May 15, 2003 Time: 8:36:20 PM
  */
-public class WorkflowDesigner extends JFrame implements GraphSelectionListener, GraphModelListener
+public class WorkflowDesigner extends JFrame implements GraphSelectionListener, GraphModelListener, FileChangeListener
 {
   public static final String WORKSPACE_SUFFIX = ".wsf";
 
   public static WorkflowDesigner INSTANCE = null;
   private PaletteDescriptor palette = null;
   public StatusBar statusBar;
-
+  private FileMonitor monitor = new FileMonitor();
+  
   public WorkflowDesigner(Splash splash)
   {
     super(ResourceManager.getString("app.name"));
     //graphTabs.removeGraph(graphTabs.getCurrentGraph());
   }
 
+  public void fileChanged(Object key, String fileName)
+  {
+    //TODO disabled now because clearing the graph is a bit of a fucker
+//    int result = JOptionPane.showConfirmDialog(this, "The workflow " + key + " has been modified externally. Reload?", "Reload Workflow", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
+//    if(result == JOptionPane.NO_OPTION) return;
+//    try
+//    {
+//      graphTabs.getGraph((String)key).setDescriptor(manager.getCurrentWorkspace().getWorkflow((String)key, false));
+//    }
+//    catch(FactoryException e)
+//    {
+//      e.printStackTrace();
+//    }
+  }
+
   public void createGraph(String workflowName)
   {
     //Workspace currentWorkspace = manager.getCurrentWorkspace();
     try
     {
       descriptor = currentWorkspace.getWorkflow(workflowName, false);
+      if(currentWorkspace instanceof Workspace)
+      {
+        try
+        {
+          String workflowFile = ((Workspace)currentWorkspace).getWorkflowFile(workflowName);
+          if(workflowFile != null)
+            monitor.addFileChangeListener(this, workflowName, workflowFile, 5000);
+        }
+        catch(FileNotFoundException e)
+        {
+          //can't really happen
+          e.printStackTrace();
+        }
+      }
     }
     catch(FactoryException e)
     {

src/designer/com/opensymphony/workflow/designer/WorkflowGraph.java

 import java.awt.dnd.*;
 import java.util.List;
 import java.util.Properties;
+import java.util.ArrayList;
 import javax.swing.*;
 
 import com.opensymphony.workflow.designer.actions.*;
   private JPopupMenu cellMenu;
 
   private UndoManager undoManager = new UndoManager();
+  private List actions = new ArrayList();
+  
   private static final Color GRID_COLOR = new Color(240, 240, 240);
 
   public WorkflowGraph(GraphModel model, WorkflowDescriptor descriptor, Layout layout, boolean doAutoLayout)
   {
     super(model);
+    setMarqueeHandler(new WorkflowMarqueeHandler(this));
     getGraphLayoutCache().setFactory(new WorkflowCellViewFactory());
     getGraphLayoutCache().setSelectsAllInsertedCells(false);
     ToolTipManager.sharedInstance().registerComponent(this);
     setGridMode(JGraph.LINE_GRID_MODE);
 
     setBendable(true);
-    setMarqueeHandler(new WorkflowMarqueeHandler(this));
     setCloneable(false);
     setPortsVisible(true);
 
     genericMenu = new JPopupMenu();
     JMenu n = new JMenu(ResourceManager.getString("create.new"));
     genericMenu.add(n);
-    n.add(new CreateStep(descriptor, getWorkflowGraphModel(), menuLocation));
+    CreateStep createStep = new CreateStep(descriptor, getWorkflowGraphModel(), menuLocation);
+    n.add(createStep);
+    actions.add(createStep);
     //	n.add(new CreateInitialAction(descriptor, getWorkflowGraphModel(), menuLocation));
-    n.add(new CreateJoin(descriptor, getWorkflowGraphModel(), menuLocation));
-    n.add(new CreateSplit(descriptor, getWorkflowGraphModel(), menuLocation));
+    CreateJoin createJoin = new CreateJoin(descriptor, getWorkflowGraphModel(), menuLocation);
+    n.add(createJoin);
+    actions.add(createJoin);
+    CreateSplit createSplit = new CreateSplit(descriptor, getWorkflowGraphModel(), menuLocation);
+    n.add(createSplit);
+    actions.add(createSplit);
     JMenu g = new JMenu(ResourceManager.getString("grid.size"));
     genericMenu.add(g);
     g.add(new SetGridSize(this, menuLocation, 1));
     g.add(new SetGridSize(this, menuLocation, 16));
 
     cellMenu = new JPopupMenu();
-    cellMenu.add(new Delete(descriptor, this, menuLocation));
+    Delete delete = new Delete(descriptor, this, menuLocation);
+    actions.add(delete);
+    cellMenu.add(delete);
 
     edgeMenu = new JPopupMenu();
     n = new JMenu(ResourceManager.getString("edge.color"));
     {
       n.add(new ResultEdgeLineWidth(this, menuLocation, i));
     }
-    edgeMenu.add(new Delete(descriptor, this, menuLocation));
+    edgeMenu.add(delete);
     model.addUndoableEditListener(undoManager);
     new DropTarget(this, this);
   }
         addJoinDescriptor(join);
       }
       getWorkflowGraphModel().insertResultConnections();
+      for(int i = 0; i < actions.size(); i++)
+      {
+        if(actions.get(i) instanceof DescriptorAware)
+        {
+          ((DescriptorAware)actions.get(i)).setDescriptor(descriptor);
+        }
+      }
     }
   }
 

src/designer/com/opensymphony/workflow/designer/WorkflowGraphModel.java

         Iterator k = edges(port);
         while(k.hasNext())
         {
-          WorkflowEdge edge = (WorkflowEdge)k.next();
-          ResultDescriptor descriptor = (ResultDescriptor)edge.getUserObject();
+          ResultEdge edge = (ResultEdge)k.next();
+          ResultDescriptor descriptor = edge.getDescriptor();
           edges.put(new Integer(descriptor.getId()), edge);
         }
       }

src/designer/com/opensymphony/workflow/designer/WorkflowMarqueeHandler.java

 
 import org.jgraph.graph.BasicMarqueeHandler;
 import org.jgraph.graph.PortView;
+import org.jgraph.graph.CellView;
 
 /**
  * @author Hani Suleiman (hani@formicary.net) Date: Oct 25 2003 Time: 6:56:10 PM
     else if(port != null && !e.isConsumed() && graph.isPortsVisible())
     {
       // Remember Start Location
-      start = graph.toScreen(port.getLocation(null));
+      start = graph.toScreen(port.getLocation());
       // Remember First Port
       firstPort = port;
       // Consume Event
       readyToConnect = false;
     }
     else
+    {
     // Call Superclass
       super.mousePressed(e);
+    }
   }
 
   // Find Port under Mouse and Repaint Connector
     // If remembered Start Point is Valid
     if(start != null && !e.isConsumed())
     {
+//      CellView nearestCell = graph.getTopmostViewAt(e.getX(), e.getY(), false, true);
+//      if(nearestCell != null && nearestCell instanceof StepView)
+//      {
+//        System.out.println("in cell " + nearestCell.getClass() + " children " + nearestCell.getChildViews().length);
+//      }
       // ready to connect
       readyToConnect = true;
 
       port = newPort;
       // If Port was found then Point to Port Location
       if(port != null)
-        current = graph.toScreen(port.getLocation(null));
+        current = graph.toScreen(port.getLocation());
       // Else If no Port was found then Point to Mouse Location
       else
         current = graph.snap(e.getPoint());
     // Call Superclass
     super.mouseReleased(e);
   }
+
+  public CellView getCurrentCell()
+  {
+    return null;
+  }
 }

src/designer/com/opensymphony/workflow/designer/actions/CreateJoin.java

  * @author jackflit
  * Date: 2003-11-18
  */
-public class CreateJoin extends AbstractAction
+public class CreateJoin extends AbstractAction implements DescriptorAware
 {
 
   private WorkflowDescriptor workflow;
     CellFactory.createJoin(workflow, model, location);
   }
 
+  public void setDescriptor(WorkflowDescriptor descriptor)
+  {
+    this.workflow = descriptor;
+  }
 }

src/designer/com/opensymphony/workflow/designer/actions/CreateSplit.java

  * @author jackflit
  * Date: 2003-11-18
  */
-public class CreateSplit extends AbstractAction 
+public class CreateSplit extends AbstractAction implements DescriptorAware
 {
 
 	private WorkflowGraphModel model;
 	{
 		CellFactory.createSplit(workflow, model, location);
 	}
+  
+  public void setDescriptor(WorkflowDescriptor descriptor)
+  {
+    this.workflow = descriptor;
+  }
 }

src/designer/com/opensymphony/workflow/designer/actions/CreateStep.java

  * Date: Oct 24, 2003
  * Time: 2:29:02 PM
  */
-public class CreateStep extends AbstractAction
+public class CreateStep extends AbstractAction implements DescriptorAware
 {
-  private WorkflowDescriptor workflow;
+  private WorkflowDescriptor descriptor;
   private WorkflowGraphModel model;
   private Point location;
 
   public CreateStep(WorkflowDescriptor workflow, WorkflowGraphModel model, Point location)
   {
     super(ResourceManager.getString("step"));
-    this.workflow = workflow;
+    this.descriptor = workflow;
     this.model = model;
     this.location = location;
   }
 
   public void actionPerformed(ActionEvent e)
   {
-    CellFactory.createStep(workflow, model, location);
+    CellFactory.createStep(descriptor, model, location);
+  }
+
+  public void setDescriptor(WorkflowDescriptor descriptor)
+  {
+    this.descriptor = descriptor;
   }
 }

src/designer/com/opensymphony/workflow/designer/actions/Delete.java

  * Date: Nov 24, 2003
  * Time: 1:10:28 PM
  */
-public class Delete extends AbstractAction
+public class Delete extends AbstractAction implements DescriptorAware
 {
   private WorkflowDescriptor workflow;
   private WorkflowGraph graph;
 		WorkflowDesigner.INSTANCE.navigator().selectTreeNode(workflow, workflow);
   }
 
+  public void setDescriptor(WorkflowDescriptor descriptor)
+  {
+    this.workflow = descriptor;
+  }
 }

src/designer/com/opensymphony/workflow/designer/actions/DescriptorAware.java

+package com.opensymphony.workflow.designer.actions;
+
+import com.opensymphony.workflow.loader.WorkflowDescriptor;
+
+/**
+ * @author Hani Suleiman
+ *         Date: Mar 22, 2006
+ *         Time: 8:59:05 PM
+ */
+public interface DescriptorAware
+{
+  public void setDescriptor(WorkflowDescriptor descriptor);
+}

src/designer/com/opensymphony/workflow/designer/views/CustomPortView.java

    */
   public Rectangle2D getBounds()
   {
-    Point2D location = getLocation(null);
-    Rectangle2D bounds = new Rectangle2D.Double(location.getX() - WIDTH / 2, location.getY(), WIDTH, HEIGHT);
-    //bounds.x = bounds.x - WIDTH / 2;
-    //bounds.width = WIDTH;
-    //bounds.height = HEIGHT;
-    return bounds;
+    Point2D location = getLocation();
+    return new Rectangle2D.Double(location.getX() - WIDTH / 2, location.getY(), WIDTH, HEIGHT);
   }
 
-  //public Point2D getLocation(EdgeView edge)
-  //{
-  //  Point p = super.getLocation(edge);
-//    if(edge!=null && edge.getSource()==edge.getTarget())
-//    {
-//      int index = ((WorkflowPort)getCell()).getEdgeIndex((Edge)edge.getCell());
-//      p.y = p.y - (5 * index);
-//    }
-//    return p;
-//  }
-
   public CellViewRenderer getRenderer()
   {
     return RENDERER;
   {
     public void paint(Graphics g)
     {
-      g.setColor(graph.getBackground());
+      g.setColor(getBackground());
       //g.setXORMode(graph.getBackground());
       if(preview)
       {
         g.drawRect(2, 2, d.width - 5, d.height - 5);
       }
       else
-        ICON.paintIcon(graph, g, 0, 0);
+        ICON.paintIcon(this, g, 0, 0);
     }
 
   }

src/designer/com/opensymphony/workflow/designer/views/EdgeRouter.java

 import java.awt.Point;
 import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
+import java.util.List;
 
 import org.jgraph.graph.Edge;
 import org.jgraph.graph.EdgeView;
  */
 public class EdgeRouter implements Edge.Routing
 {
+  public List route(EdgeView edgeView)
+  {
+    return null;
+  }
+
+  public int getPreferredLineStyle(EdgeView edgeView)
+  {
+    return 0;
+  }
+
   public void route(EdgeView edge, java.util.List points)
   {
     PortView sourceView = (PortView)edge.getSource();
 
     WorkflowPort sourcePort = (WorkflowPort)sourceView.getCell();
     WorkflowPort targetPort = (WorkflowPort)targetView.getCell();
-    Point2D from = sourceView.getLocation(null);
-    Point2D to = targetView.getLocation(null);
+    Point2D from = sourceView.getLocation();
+    Point2D to = targetView.getLocation();
     //check if this is a dup route
 //    Collection duplicates = new HashSet();
 //    if(sourcePort!=targetPort)

src/designer/com/opensymphony/workflow/designer/views/InitialActionRenderer.java

     if(selected)
     {
       g2.setStroke(GraphConstants.SELECTION_STROKE);
-      g.setColor(graph.getHighlightColor());
+      g.setColor(highlightColor);
       g.drawOval(b - 1, b - 1, d.width - b, d.height - b);
     }
   }

src/designer/com/opensymphony/workflow/designer/views/JoinRenderer.java

     if(selected)
     {
       g2.setStroke(GraphConstants.SELECTION_STROKE);
-      g.setColor(graph.getHighlightColor());
+      g.setColor(highlightColor);
       g.drawRect(b - 1, b - 1, d.width - b, d.height - b);
     }
   }

src/designer/com/opensymphony/workflow/designer/views/OutsidePortView.java

+package com.opensymphony.workflow.designer.views;
+
+import java.awt.*;
+
+import org.jgraph.graph.PortView;
+import org.jgraph.graph.CellViewRenderer;
+import org.jgraph.graph.PortRenderer;
+import org.jgraph.graph.CellView;
+
+/**
+ * User: Hani Suleiman
+ * Date: Oct 16, 2003
+ * Time: 10:23:47 AM
+ */
+public class OutsidePortView extends PortView
+{
+  private final CellViewRenderer RENDERER = new OutsidePortRenderer();
+
+  public OutsidePortView(Object object)
+  {
+    super(object);
+  }
+
+  public CellViewRenderer getRenderer()
+  {
+    return RENDERER;
+  }
+
+  class OutsidePortRenderer extends PortRenderer
+  {
+    public void paint(Graphics g)
+    {
+      
+      //TODO need to figure out how we can only paint these if we have focus,
+//      CellView parent = view.getParentView();
+      //or if a mouse is dragged into the containing cell
+//      WorkflowPort port = (WorkflowPort)getCell();
+//      if(port.getEdges().size() > 0)
+        super.paint(g);
+    }
+  }
+}

src/designer/com/opensymphony/workflow/designer/views/SplitRenderer.java

     if(selected)
     {
       g2.setStroke(GraphConstants.SELECTION_STROKE);
-      g.setColor(graph.getHighlightColor());
+      g.setColor(highlightColor);
       g.drawRect(b - 1, b - 1, d.width - b, d.height - b);
     }
   }

src/designer/com/opensymphony/workflow/designer/views/StepRenderer.java

     if(selected)
     {
       g2.setStroke(GraphConstants.SELECTION_STROKE);
-      g.setColor(graph.getHighlightColor());
+      g.setColor(highlightColor);
       g.drawRoundRect(b - 1,
                       b - 1,
                       d.width - b,

src/designer/com/opensymphony/workflow/loader/FileChangeListener.java

+package com.opensymphony.workflow.loader;
+
+/**
+ * @author Hani Suleiman
+ */
+public interface FileChangeListener
+{
+  /** Invoked when a file changes.
+   * @param fileName name of changed file.
+   */
+  public void fileChanged(Object key, String fileName);
+}

src/designer/com/opensymphony/workflow/loader/FileMonitor.java

+package com.opensymphony.workflow.loader;
+
+import java.util.*;
+import java.io.*;
+import java.net.URL;
+
+/**
+ * @author Hani Suleiman
+ * Date: May 18, 2002
+ * Time: 9:30:01 PM
+ */
+
+public class FileMonitor
+{
+  private Timer timer;
+  private Map timerEntries;
+
+  public FileMonitor()
+  {
+    // Create timer, run timer thread as daemon.
+    timer = new Timer(true);
+    timerEntries = new Hashtable();
+  }
+
+  /** Add a monitored file with a FileChangeListener.
+   * @param listener listener to notify when the file changed.
+   * @param fileName name of the file to monitor.
+   * @param period polling period in milliseconds.
+   */
+  public void addFileChangeListener(FileChangeListener listener, Object key, String fileName, long period) throws FileNotFoundException
+  {
+    removeFileChangeListener(key);
+    File file = getFile(listener, fileName);
+    TimerTask task;
+    if(file.isDirectory())
+    {
+      task = new DirectoryMonitorTask(listener, fileName, file, key);
+    }
+    else
+    {
+      task = new FileMonitorTask(listener, fileName, key, file);
+    }
+    timerEntries.put(key, task);
+    timer.schedule(task, period, period);
+  }
+
+  /** Add a monitored file with a FileChangeListener.
+   * @param listener listener to notify when the file changed.
+   * @param dir name of the directory to monitor.
+   * @param period polling period in milliseconds.
+   * @param filter The file filter to apply when checking for whether a file within the specified dir should be monitored or not
+   */
+  public void addFileChangeListener(FileChangeListener listener, Object key, String dir, long period, FileFilter filter) throws FileNotFoundException
+  {
+    removeFileChangeListener(key);
+    File file = getFile(listener, dir);
+    TimerTask task;
+    if(file.isDirectory())
+    {
+      task = new DirectoryMonitorTask(listener, dir, file, filter);
+    }
+    else
+    {
+      task = new FileMonitorTask(listener, dir, key, file);
+    }
+    timerEntries.put(key, task);
+    timer.schedule(task, period, period);
+  }
+
+  /** Remove the listener from the notification list.
+   */
+  public void removeFileChangeListener(Object key)
+  {
+    TimerTask task = (TimerTask)timerEntries.remove(key);
+    if(task != null)
+    {
+      task.cancel();
+    }
+  }
+
+  protected void fireFileChangeEvent(FileChangeListener listener, Object key, String fileName)
+  {
+    listener.fileChanged(key, fileName);
+  }
+
+  private File getFile(FileChangeListener listener, String name) throws FileNotFoundException
+  {
+    File file = new File(name);
+    if(!file.exists())
+    {  // but is it on CLASSPATH?
+      URL fileURL = listener.getClass().getClassLoader().getResource(name);
+      if(fileURL != null)
+      {
+        file = new File(fileURL.getFile());
+      }
+      else
+      {
+        throw new FileNotFoundException("File Not Found: " + name);
+      }
+    }
+    return file;
+  }
+
+  class DirectoryMonitorTask extends TimerTask
+  {
+    FileChangeListener listener;
+    File monitoredDirectory;
+    private Map modifiedMap;
+    private String dirName;
+    private Object key;
+    private FileFilter filter;
+
+    public DirectoryMonitorTask(FileChangeListener listener, String name, File dir, Object key)
+    {
+      this.listener = listener;
+      this.dirName = name;
+      modifiedMap = new HashMap();
+      monitoredDirectory = dir;
+      this.key = key;
+    }
+
+    public DirectoryMonitorTask(FileChangeListener listener, String name, File dir, FileFilter filter, Object key)
+    {
+      this.listener = listener;
+      this.dirName = name;
+      modifiedMap = new HashMap();
+      monitoredDirectory = dir;
+      this.filter = filter;
+      this.key = key;
+    }
+
+    public void run()
+    {
+      //check if we have new entries/entries deleted, if we do, fire change
+      File[] files;
+      if(filter==null)
+        files = monitoredDirectory.listFiles();
+      else
+        files = monitoredDirectory.listFiles(filter);
+      Set allFiles = new HashSet();
+      for (int i = 0; i < files.length; i++)
+      {
+        allFiles.add(files[i].toString());
+      }
+      if(files.length != modifiedMap.size() || !allFiles.containsAll(modifiedMap.keySet()))
+      {
+        updateMap();
+        fireFileChangeEvent(this.listener, this.key, this.dirName);
+      }
+      //same number of files, so check their modified timestamps
+      boolean isModified = false;
+      for (int i = 0; i < files.length; i++)
+      {
+        File file = files[i];
+        long lastModified = file.lastModified();
+        Long oldModified = (Long)modifiedMap.get(file.toString());
+        if(oldModified==null || lastModified != oldModified.longValue())
+        {
+          isModified = true;
+          modifiedMap.put(file.toString(), new Long(lastModified));
+        }
+      }
+      if(isModified)
+      {
+        fireFileChangeEvent(this.listener, this.key, this.dirName);
+      }
+    }
+
+    private void updateMap()
+    {
+      File[] files;
+      if(filter==null)
+        files = monitoredDirectory.listFiles();
+      else
+        files = monitoredDirectory.listFiles(filter);
+      Map map = new HashMap(files.length);
+      for (int i = 0; i < files.length; i++)
+      {
+        File file = files[i];
+        long lastModified = file.lastModified();
+        Long oldModified = (Long)map.get(file.toString());
+        if(oldModified==null || lastModified != oldModified.longValue())
+        {
+          map.put(file.toString(), new Long(lastModified));
+        }
+      }
+      modifiedMap = map;
+    }
+  }
+
+  class FileMonitorTask extends TimerTask
+  {
+    FileChangeListener listener;
+    File monitoredFile;
+    long lastModified;
+    String fileName;
+    Object key;
+    
+    public FileMonitorTask(FileChangeListener listener, String name, Object key, File file)
+    {
+      this.listener = listener;
+      this.fileName = name;
+      this.lastModified = 0;
+      this.key = key;
+      monitoredFile = file;
+      this.lastModified = monitoredFile.lastModified();
+    }
+
+    public void run()
+    {
+      long lastModified = monitoredFile.lastModified();
+      if(lastModified != this.lastModified)
+      {
+        this.lastModified = lastModified;
+        fireFileChangeEvent(this.listener, this.key, this.fileName);
+      }
+    }
+  }
+
+/*  public static void main(String[] args) throws Exception
+  {
+    //create some crap
+    File file = new File("blah.txt");
+    File dir = new File("testdir");
+    file.deleteOnExit();
+    dir.deleteOnExit();
+    dir.mkdir();
+    file.createNewFile();
+    FileChangeListener listener = new FileChangeListener()
+    {
+      public void fileChanged(String fileName)
+      {
+        System.out.println(System.currentTimeMillis() + " fileChanged " + fileName);
+      }
+    };
+    FileMonitor.getInstance().addFileChangeListener(listener, file.toString(), 1000L);
+    FileMonitor.getInstance().addFileChangeListener(listener, dir.toString(), 1000L);
+    Thread.currentThread().sleep(60000L);
+   }*/
+}

src/designer/com/opensymphony/workflow/loader/Workspace.java

           File file = new File(dir, config.location);
           config.url = file.toURL();
           if(file.exists())
+          {
             config.lastModified = file.lastModified();
+          }
         }
         catch(MalformedURLException e)
         {
     }
   }
 
+  public String getWorkflowFile(String workflowName)
+  {
+    WorkflowConfig config = (WorkflowConfig)workflows.get(workflowName);
+    if(config != null && config.url != null)
+    {
+      return config.url.getFile();
+    }
+    return null;
+  }
+  
   public Object getLayout(String workflowName)
   {
     Object obj = layouts.get(workflowName);

src/designer/designer.iml

           <root url="jar://$MODULE_DIR$/../../lib/designer/jgraph.jar!/" />
         </CLASSES>
         <JAVADOC />
-        <SOURCES />
+        <SOURCES>
+          <root url="file://$MODULE_DIR$/../../../jgraph5/src" />
+        </SOURCES>
       </library>
     </orderEntry>
     <orderEntry type="module-library">

src/java/com/opensymphony/workflow/AbstractWorkflow.java

         // Check global actions first
         boolean isCompleted = wf.getGlobalActions().size() == 0;
 
-        for (Iterator iterator = currentSteps.iterator(); isCompleted && iterator.hasNext();) {
+        for (Iterator iterator = currentSteps.iterator();
+                isCompleted && iterator.hasNext();) {
             Step step = (Step) iterator.next();
             StepDescriptor stepDes = wf.getStep(step.getStepId());